package com.marcdenning.adventofcode.day17

import java.io.File

const val ACTIVE = '#'
const val INACTIVE = '.'

fun main(args: Array<String>) {
    val numberOfBootCycles = args[1].toInt()
    var pocketDimension = arrayOf(File(args[0]).readLines().map { it.toCharArray() }.toTypedArray())

    for (i in 1..numberOfBootCycles) {
        pocketDimension = conductBootCycle(pocketDimension)
    }
    println(
        "Number of active cubes after $numberOfBootCycles boot cycles: ${
            countCubesWithState(
                pocketDimension,
                ACTIVE
            )
        }"
    )
}

/**
 * Execute a single boot cycle. Each cube simultaneously changes state according to rules:
 *
 * 1. If a cube is active and exactly 2 or 3 of its neighbors are also active, the cube remains active. Otherwise, the cube becomes inactive.
 * 2. If a cube is inactive but exactly 3 of its neighbors are active, the cube becomes active. Otherwise, the cube remains inactive.
 *
 * @return new matrix of cube states, will grow continuously
 */
fun conductBootCycle(pocketDimension: Array<Array<CharArray>>): Array<Array<CharArray>> {
    val expandedPocketDimension = Array(pocketDimension.size + 2) { Array(pocketDimension[0].size + 2) { CharArray(pocketDimension[0][0].size + 2) { INACTIVE } } }

    // Copy existing dimension map into expanded map
    pocketDimension.forEachIndexed { planeIndex, plane ->
        plane.forEachIndexed { rowIndex, row ->
            row.forEachIndexed { charIndex, char ->
                expandedPocketDimension[planeIndex + 1][rowIndex + 1][charIndex + 1] = char
            }
        }
    }

    // Apply active/inactive rules
    expandedPocketDimension.forEachIndexed { planeIndex, plane ->
        plane.forEachIndexed { rowIndex, row ->
            row.forEachIndexed { charIndex, char ->
                val countOfActiveAdjacentCubes = countCubesWithState(
                    getAdjacentCubes(expandedPocketDimension, Coordinates(charIndex, rowIndex, planeIndex)),
                    ACTIVE
                )

                if (char == ACTIVE && countOfActiveAdjacentCubes !in 2..3) {
                    expandedPocketDimension[planeIndex][rowIndex][charIndex] = INACTIVE
                } else if (char == INACTIVE && countOfActiveAdjacentCubes == 3) {
                    expandedPocketDimension[planeIndex][rowIndex][charIndex] = ACTIVE
                }
            }
        }
    }

    // Trim completely inactive edges

    return expandedPocketDimension
}

/**
 * Get all cubes adjacent to given coordinates. This is 26 cubes, with each coordinate value offset by 1.
 *
 * @return matrix of adjascent cubes, including target coordinates as space character
 */
fun getAdjacentCubes(pocketDimension: Array<Array<CharArray>>, coordinates: Coordinates): Array<Array<CharArray>> {
    val adjacentCubes = Array(3) { Array(3) { CharArray(3) { INACTIVE } } }

    for (z in -1..1) {
        for (y in -1..1) {
            for (x in -1..1) {
                if (z == 0 && y == 0 && x == 0) {
                    adjacentCubes[1][1][1] = ' '
                } else if (coordinates.z + z !in pocketDimension.indices ||
                    coordinates.y + y !in pocketDimension[coordinates.z + z].indices ||
                    coordinates.x + x !in pocketDimension[coordinates.z + z][coordinates.y + y].indices
                ) {
                    adjacentCubes[z + 1][y + 1][x + 1] = INACTIVE
                } else {
                    adjacentCubes[z + 1][y + 1][x + 1] =
                        pocketDimension[coordinates.z + z][coordinates.y + y][coordinates.x + x]
                }
            }
        }
    }

    return adjacentCubes
}

fun countCubesWithState(pocketDimension: Array<Array<CharArray>>, cubeState: Char) =
    pocketDimension.map { plane ->
        plane.map { row -> row.map { cube -> if (cube == cubeState) 1 else 0 }.sum() }.sum()
    }.sum()

data class Coordinates(
    val x: Int,
    val y: Int,
    val z: Int
)

Compiled from "Day17a.kt"
public final class com.marcdenning.adventofcode.day17.Day17aKt {
  public static final char ACTIVE;

  public static final char INACTIVE;

  public static final void main(java.lang.String[]);
    Code:
       0: aload_0
       1: ldc           #9                  // String args
       3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: iconst_1
       8: aaload
       9: invokestatic  #21                 // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I
      12: istore_1
      13: iconst_1
      14: anewarray     #23                 // class "[[C"
      17: astore_3
      18: aload_3
      19: iconst_0
      20: new           #25                 // class java/io/File
      23: dup
      24: aload_0
      25: iconst_0
      26: aaload
      27: invokespecial #29                 // Method java/io/File."<init>":(Ljava/lang/String;)V
      30: aconst_null
      31: iconst_1
      32: aconst_null
      33: invokestatic  #35                 // Method kotlin/io/FilesKt.readLines$default:(Ljava/io/File;Ljava/nio/charset/Charset;ILjava/lang/Object;)Ljava/util/List;
      36: checkcast     #37                 // class java/lang/Iterable
      39: astore        4
      41: istore        14
      43: astore        13
      45: iconst_0
      46: istore        5
      48: aload         4
      50: astore        6
      52: new           #39                 // class java/util/ArrayList
      55: dup
      56: aload         4
      58: bipush        10
      60: invokestatic  #45                 // Method kotlin/collections/CollectionsKt.collectionSizeOrDefault:(Ljava/lang/Iterable;I)I
      63: invokespecial #48                 // Method java/util/ArrayList."<init>":(I)V
      66: checkcast     #50                 // class java/util/Collection
      69: astore        7
      71: iconst_0
      72: istore        8
      74: aload         6
      76: invokeinterface #54,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
      81: astore        9
      83: aload         9
      85: invokeinterface #60,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
      90: ifeq          140
      93: aload         9
      95: invokeinterface #64,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     100: astore        10
     102: aload         7
     104: aload         10
     106: checkcast     #66                 // class java/lang/String
     109: astore        11
     111: astore        15
     113: iconst_0
     114: istore        12
     116: aload         11
     118: invokevirtual #70                 // Method java/lang/String.toCharArray:()[C
     121: dup
     122: ldc           #72                 // String toCharArray(...)
     124: invokestatic  #75                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullExpressionValue:(Ljava/lang/Object;Ljava/lang/String;)V
     127: nop
     128: aload         15
     130: swap
     131: invokeinterface #79,  2           // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     136: pop
     137: goto          83
     140: aload         7
     142: checkcast     #81                 // class java/util/List
     145: nop
     146: astore        15
     148: aload         13
     150: iload         14
     152: aload         15
     154: checkcast     #50                 // class java/util/Collection
     157: astore        4
     159: nop
     160: iconst_0
     161: istore        5
     163: aload         4
     165: astore        6
     167: aload         6
     169: iconst_0
     170: anewarray     #83                 // class "[C"
     173: invokeinterface #87,  2           // InterfaceMethod java/util/Collection.toArray:([Ljava/lang/Object;)[Ljava/lang/Object;
     178: aastore
     179: aload_3
     180: astore_2
     181: iconst_1
     182: istore_3
     183: iload_3
     184: iload_1
     185: if_icmpgt     204
     188: aload_2
     189: invokestatic  #91                 // Method conductBootCycle:([[[C)[[[C
     192: astore_2
     193: iload_3
     194: iload_1
     195: if_icmpeq     204
     198: iinc          3, 1
     201: goto          188
     204: new           #93                 // class java/lang/StringBuilder
     207: dup
     208: invokespecial #96                 // Method java/lang/StringBuilder."<init>":()V
     211: ldc           #98                 // String Number of active cubes after
     213: invokevirtual #102                // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     216: iload_1
     217: invokevirtual #105                // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
     220: ldc           #107                // String  boot cycles:
     222: invokevirtual #102                // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     225: aload_2
     226: bipush        35
     228: invokestatic  #111                // Method countCubesWithState:([[[CC)I
     231: invokevirtual #105                // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
     234: invokevirtual #115                // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     237: astore_3
     238: getstatic     #121                // Field java/lang/System.out:Ljava/io/PrintStream;
     241: aload_3
     242: invokevirtual #127                // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
     245: return

  public static final char[][][] conductBootCycle(char[][][]);
    Code:
       0: aload_0
       1: ldc           #151                // String pocketDimension
       3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: iconst_0
       7: istore_2
       8: aload_0
       9: checkcast     #153                // class "[Ljava/lang/Object;"
      12: arraylength
      13: iconst_2
      14: iadd
      15: istore_3
      16: iload_3
      17: anewarray     #23                 // class "[[C"
      20: astore        4
      22: iload_2
      23: iload_3
      24: if_icmpge     147
      27: iload_2
      28: istore        5
      30: aload         4
      32: iload         5
      34: iconst_0
      35: istore        6
      37: aload_0
      38: iconst_0
      39: aaload
      40: checkcast     #153                // class "[Ljava/lang/Object;"
      43: arraylength
      44: iconst_2
      45: iadd
      46: istore        7
      48: iload         7
      50: anewarray     #83                 // class "[C"
      53: astore        8
      55: istore        31
      57: astore        30
      59: iload         6
      61: iload         7
      63: if_icmpge     134
      66: iload         6
      68: istore        9
      70: aload         8
      72: iload         9
      74: iconst_0
      75: istore        10
      77: aload_0
      78: iconst_0
      79: aaload
      80: iconst_0
      81: aaload
      82: arraylength
      83: iconst_2
      84: iadd
      85: istore        11
      87: iload         11
      89: newarray       char
      91: astore        12
      93: istore        33
      95: astore        32
      97: iload         10
      99: iload         11
     101: if_icmpge     121
     104: iload         10
     106: istore        13
     108: aload         12
     110: iload         13
     112: bipush        46
     114: castore
     115: iinc          10, 1
     118: goto          97
     121: aload         32
     123: iload         33
     125: aload         12
     127: aastore
     128: iinc          6, 1
     131: goto          59
     134: aload         30
     136: iload         31
     138: aload         8
     140: aastore
     141: iinc          2, 1
     144: goto          22
     147: aload         4
     149: astore_1
     150: aload_0
     151: checkcast     #153                // class "[Ljava/lang/Object;"
     154: astore_2
     155: iconst_0
     156: istore_3
     157: iconst_0
     158: istore        4
     160: iconst_0
     161: istore        5
     163: aload_2
     164: arraylength
     165: istore        6
     167: iload         5
     169: iload         6
     171: if_icmpge     339
     174: aload_2
     175: iload         5
     177: aaload
     178: astore        7
     180: iload         4
     182: iinc          4, 1
     185: aload         7
     187: checkcast     #23                 // class "[[C"
     190: astore        8
     192: istore        9
     194: iconst_0
     195: istore        10
     197: aload         8
     199: checkcast     #153                // class "[Ljava/lang/Object;"
     202: astore        11
     204: iconst_0
     205: istore        12
     207: iconst_0
     208: istore        13
     210: iconst_0
     211: istore        14
     213: aload         11
     215: arraylength
     216: istore        15
     218: iload         14
     220: iload         15
     222: if_icmpge     330
     225: aload         11
     227: iload         14
     229: aaload
     230: astore        16
     232: iload         13
     234: iinc          13, 1
     237: aload         16
     239: checkcast     #83                 // class "[C"
     242: astore        17
     244: istore        18
     246: iconst_0
     247: istore        19
     249: aload         17
     251: astore        20
     253: iconst_0
     254: istore        21
     256: iconst_0
     257: istore        22
     259: iconst_0
     260: istore        23
     262: aload         20
     264: arraylength
     265: istore        24
     267: iload         23
     269: iload         24
     271: if_icmpge     321
     274: aload         20
     276: iload         23
     278: caload
     279: istore        25
     281: iload         22
     283: iinc          22, 1
     286: iload         25
     288: istore        26
     290: istore        27
     292: iconst_0
     293: istore        28
     295: aload_1
     296: iload         9
     298: iconst_1
     299: iadd
     300: aaload
     301: iload         18
     303: iconst_1
     304: iadd
     305: aaload
     306: iload         27
     308: iconst_1
     309: iadd
     310: iload         26
     312: castore
     313: nop
     314: nop
     315: iinc          23, 1
     318: goto          267
     321: nop
     322: nop
     323: nop
     324: iinc          14, 1
     327: goto          218
     330: nop
     331: nop
     332: nop
     333: iinc          5, 1
     336: goto          167
     339: nop
     340: aload_1
     341: checkcast     #153                // class "[Ljava/lang/Object;"
     344: astore_2
     345: iconst_0
     346: istore_3
     347: iconst_0
     348: istore        4
     350: iconst_0
     351: istore        5
     353: aload_2
     354: arraylength
     355: istore        6
     357: iload         5
     359: iload         6
     361: if_icmpge     606
     364: aload_2
     365: iload         5
     367: aaload
     368: astore        7
     370: iload         4
     372: iinc          4, 1
     375: aload         7
     377: checkcast     #23                 // class "[[C"
     380: astore        8
     382: istore        9
     384: iconst_0
     385: istore        10
     387: aload         8
     389: checkcast     #153                // class "[Ljava/lang/Object;"
     392: astore        11
     394: iconst_0
     395: istore        12
     397: iconst_0
     398: istore        13
     400: iconst_0
     401: istore        14
     403: aload         11
     405: arraylength
     406: istore        15
     408: iload         14
     410: iload         15
     412: if_icmpge     597
     415: aload         11
     417: iload         14
     419: aaload
     420: astore        16
     422: iload         13
     424: iinc          13, 1
     427: aload         16
     429: checkcast     #83                 // class "[C"
     432: astore        17
     434: istore        18
     436: iconst_0
     437: istore        19
     439: aload         17
     441: astore        20
     443: iconst_0
     444: istore        21
     446: iconst_0
     447: istore        22
     449: iconst_0
     450: istore        23
     452: aload         20
     454: arraylength
     455: istore        24
     457: iload         23
     459: iload         24
     461: if_icmpge     588
     464: aload         20
     466: iload         23
     468: caload
     469: istore        25
     471: iload         22
     473: iinc          22, 1
     476: iload         25
     478: istore        26
     480: istore        27
     482: iconst_0
     483: istore        28
     485: aload_1
     486: new           #155                // class com/marcdenning/adventofcode/day17/Coordinates
     489: dup
     490: iload         27
     492: iload         18
     494: iload         9
     496: invokespecial #158                // Method com/marcdenning/adventofcode/day17/Coordinates."<init>":(III)V
     499: invokestatic  #162                // Method getAdjacentCubes:([[[CLcom/marcdenning/adventofcode/day17/Coordinates;)[[[C
     502: bipush        35
     504: invokestatic  #111                // Method countCubesWithState:([[[CC)I
     507: istore        29
     509: iload         26
     511: bipush        35
     513: if_icmpne     555
     516: iconst_2
     517: iload         29
     519: if_icmpgt     536
     522: iload         29
     524: iconst_4
     525: if_icmpge     532
     528: iconst_1
     529: goto          537
     532: iconst_0
     533: goto          537
     536: iconst_0
     537: ifne          555
     540: aload_1
     541: iload         9
     543: aaload
     544: iload         18
     546: aaload
     547: iload         27
     549: bipush        46
     551: castore
     552: goto          580
     555: iload         26
     557: bipush        46
     559: if_icmpne     580
     562: iload         29
     564: iconst_3
     565: if_icmpne     580
     568: aload_1
     569: iload         9
     571: aaload
     572: iload         18
     574: aaload
     575: iload         27
     577: bipush        35
     579: castore
     580: nop
     581: nop
     582: iinc          23, 1
     585: goto          457
     588: nop
     589: nop
     590: nop
     591: iinc          14, 1
     594: goto          408
     597: nop
     598: nop
     599: nop
     600: iinc          5, 1
     603: goto          357
     606: nop
     607: aload_1
     608: areturn

  public static final char[][][] getAdjacentCubes(char[][][], com.marcdenning.adventofcode.day17.Coordinates);
    Code:
       0: aload_0
       1: ldc           #151                // String pocketDimension
       3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_1
       7: ldc           #183                // String coordinates
       9: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
      12: iconst_0
      13: istore_3
      14: iconst_3
      15: anewarray     #23                 // class "[[C"
      18: astore        4
      20: iload_3
      21: iconst_3
      22: if_icmpge     120
      25: iload_3
      26: istore        5
      28: aload         4
      30: iload         5
      32: iconst_0
      33: istore        6
      35: iconst_3
      36: anewarray     #83                 // class "[C"
      39: astore        7
      41: istore        13
      43: astore        12
      45: iload         6
      47: iconst_3
      48: if_icmpge     107
      51: iload         6
      53: istore        8
      55: aload         7
      57: iload         8
      59: iconst_0
      60: istore        9
      62: iconst_3
      63: newarray       char
      65: astore        10
      67: istore        15
      69: astore        14
      71: iload         9
      73: iconst_3
      74: if_icmpge     94
      77: iload         9
      79: istore        11
      81: aload         10
      83: iload         11
      85: bipush        46
      87: castore
      88: iinc          9, 1
      91: goto          71
      94: aload         14
      96: iload         15
      98: aload         10
     100: aastore
     101: iinc          6, 1
     104: goto          45
     107: aload         12
     109: iload         13
     111: aload         7
     113: aastore
     114: iinc          3, 1
     117: goto          20
     120: aload         4
     122: astore_2
     123: iconst_m1
     124: istore_3
     125: iload_3
     126: iconst_2
     127: if_icmpge     392
     130: iconst_m1
     131: istore        4
     133: iload         4
     135: iconst_2
     136: if_icmpge     386
     139: iconst_m1
     140: istore        5
     142: iload         5
     144: iconst_2
     145: if_icmpge     380
     148: iload_3
     149: ifne          174
     152: iload         4
     154: ifne          174
     157: iload         5
     159: ifne          174
     162: aload_2
     163: iconst_1
     164: aaload
     165: iconst_1
     166: aaload
     167: iconst_1
     168: bipush        32
     170: castore
     171: goto          374
     174: aload_0
     175: checkcast     #153                // class "[Ljava/lang/Object;"
     178: arraylength
     179: istore        6
     181: aload_1
     182: invokevirtual #187                // Method com/marcdenning/adventofcode/day17/Coordinates.getZ:()I
     185: iload_3
     186: iadd
     187: istore        7
     189: iconst_0
     190: iload         7
     192: if_icmpgt     210
     195: iload         7
     197: iload         6
     199: if_icmpge     206
     202: iconst_1
     203: goto          211
     206: iconst_0
     207: goto          211
     210: iconst_0
     211: ifeq          315
     214: aload_0
     215: aload_1
     216: invokevirtual #187                // Method com/marcdenning/adventofcode/day17/Coordinates.getZ:()I
     219: iload_3
     220: iadd
     221: aaload
     222: checkcast     #153                // class "[Ljava/lang/Object;"
     225: arraylength
     226: istore        6
     228: aload_1
     229: invokevirtual #190                // Method com/marcdenning/adventofcode/day17/Coordinates.getY:()I
     232: iload         4
     234: iadd
     235: istore        7
     237: iconst_0
     238: iload         7
     240: if_icmpgt     258
     243: iload         7
     245: iload         6
     247: if_icmpge     254
     250: iconst_1
     251: goto          259
     254: iconst_0
     255: goto          259
     258: iconst_0
     259: ifeq          315
     262: aload_0
     263: aload_1
     264: invokevirtual #187                // Method com/marcdenning/adventofcode/day17/Coordinates.getZ:()I
     267: iload_3
     268: iadd
     269: aaload
     270: aload_1
     271: invokevirtual #190                // Method com/marcdenning/adventofcode/day17/Coordinates.getY:()I
     274: iload         4
     276: iadd
     277: aaload
     278: arraylength
     279: istore        6
     281: aload_1
     282: invokevirtual #193                // Method com/marcdenning/adventofcode/day17/Coordinates.getX:()I
     285: iload         5
     287: iadd
     288: istore        7
     290: iconst_0
     291: iload         7
     293: if_icmpgt     311
     296: iload         7
     298: iload         6
     300: if_icmpge     307
     303: iconst_1
     304: goto          312
     307: iconst_0
     308: goto          312
     311: iconst_0
     312: ifne          335
     315: aload_2
     316: iload_3
     317: iconst_1
     318: iadd
     319: aaload
     320: iload         4
     322: iconst_1
     323: iadd
     324: aaload
     325: iload         5
     327: iconst_1
     328: iadd
     329: bipush        46
     331: castore
     332: goto          374
     335: aload_2
     336: iload_3
     337: iconst_1
     338: iadd
     339: aaload
     340: iload         4
     342: iconst_1
     343: iadd
     344: aaload
     345: iload         5
     347: iconst_1
     348: iadd
     349: aload_0
     350: aload_1
     351: invokevirtual #187                // Method com/marcdenning/adventofcode/day17/Coordinates.getZ:()I
     354: iload_3
     355: iadd
     356: aaload
     357: aload_1
     358: invokevirtual #190                // Method com/marcdenning/adventofcode/day17/Coordinates.getY:()I
     361: iload         4
     363: iadd
     364: aaload
     365: aload_1
     366: invokevirtual #193                // Method com/marcdenning/adventofcode/day17/Coordinates.getX:()I
     369: iload         5
     371: iadd
     372: caload
     373: castore
     374: iinc          5, 1
     377: goto          142
     380: iinc          4, 1
     383: goto          133
     386: iinc          3, 1
     389: goto          125
     392: aload_2
     393: areturn

  public static final int countCubesWithState(char[][][], char);
    Code:
       0: aload_0
       1: ldc           #151                // String pocketDimension
       3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: checkcast     #153                // class "[Ljava/lang/Object;"
      10: astore_2
      11: iconst_0
      12: istore_3
      13: aload_2
      14: astore        4
      16: new           #39                 // class java/util/ArrayList
      19: dup
      20: aload_2
      21: arraylength
      22: invokespecial #48                 // Method java/util/ArrayList."<init>":(I)V
      25: checkcast     #50                 // class java/util/Collection
      28: astore        5
      30: iconst_0
      31: istore        6
      33: iconst_0
      34: istore        7
      36: aload         4
      38: arraylength
      39: istore        8
      41: iload         7
      43: iload         8
      45: if_icmpge     288
      48: aload         4
      50: iload         7
      52: aaload
      53: astore        9
      55: aload         5
      57: aload         9
      59: checkcast     #23                 // class "[[C"
      62: astore        10
      64: astore        34
      66: iconst_0
      67: istore        11
      69: aload         10
      71: checkcast     #153                // class "[Ljava/lang/Object;"
      74: astore        12
      76: iconst_0
      77: istore        13
      79: aload         12
      81: astore        14
      83: new           #39                 // class java/util/ArrayList
      86: dup
      87: aload         12
      89: arraylength
      90: invokespecial #48                 // Method java/util/ArrayList."<init>":(I)V
      93: checkcast     #50                 // class java/util/Collection
      96: astore        15
      98: iconst_0
      99: istore        16
     101: iconst_0
     102: istore        17
     104: aload         14
     106: arraylength
     107: istore        18
     109: iload         17
     111: iload         18
     113: if_icmpge     258
     116: aload         14
     118: iload         17
     120: aaload
     121: astore        19
     123: aload         15
     125: aload         19
     127: checkcast     #83                 // class "[C"
     130: astore        20
     132: astore        21
     134: iconst_0
     135: istore        22
     137: aload         20
     139: astore        23
     141: iconst_0
     142: istore        24
     144: aload         23
     146: astore        25
     148: new           #39                 // class java/util/ArrayList
     151: dup
     152: aload         23
     154: arraylength
     155: invokespecial #48                 // Method java/util/ArrayList."<init>":(I)V
     158: checkcast     #50                 // class java/util/Collection
     161: astore        26
     163: iconst_0
     164: istore        27
     166: iconst_0
     167: istore        28
     169: aload         25
     171: arraylength
     172: istore        29
     174: iload         28
     176: iload         29
     178: if_icmpge     228
     181: aload         25
     183: iload         28
     185: caload
     186: istore        30
     188: aload         26
     190: iload         30
     192: istore        31
     194: astore        32
     196: iconst_0
     197: istore        33
     199: iload         31
     201: iload_1
     202: if_icmpne     209
     205: iconst_1
     206: goto          210
     209: iconst_0
     210: invokestatic  #202                // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
     213: aload         32
     215: swap
     216: invokeinterface #79,  2           // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     221: pop
     222: iinc          28, 1
     225: goto          174
     228: aload         26
     230: checkcast     #81                 // class java/util/List
     233: nop
     234: checkcast     #37                 // class java/lang/Iterable
     237: invokestatic  #206                // Method kotlin/collections/CollectionsKt.sumOfInt:(Ljava/lang/Iterable;)I
     240: invokestatic  #202                // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
     243: aload         21
     245: swap
     246: invokeinterface #79,  2           // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     251: pop
     252: iinc          17, 1
     255: goto          109
     258: aload         15
     260: checkcast     #81                 // class java/util/List
     263: nop
     264: checkcast     #37                 // class java/lang/Iterable
     267: invokestatic  #206                // Method kotlin/collections/CollectionsKt.sumOfInt:(Ljava/lang/Iterable;)I
     270: invokestatic  #202                // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
     273: aload         34
     275: swap
     276: invokeinterface #79,  2           // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     281: pop
     282: iinc          7, 1
     285: goto          41
     288: aload         5
     290: checkcast     #81                 // class java/util/List
     293: nop
     294: checkcast     #37                 // class java/lang/Iterable
     297: invokestatic  #206                // Method kotlin/collections/CollectionsKt.sumOfInt:(Ljava/lang/Iterable;)I
     300: ireturn
}


package com.marcdenning.adventofcode.day21

import java.io.File

fun main(args: Array<String>) {
    val foods = File(args[0]).readLines().map { Food(getIngredients(it), getIdentifiedAllergens(it)) }
    val allIngredientsList = foods.flatMap { it.ingredients }
    val safeIngredients = getSafeIngredients(foods)
    val countOfInstancesOfSafeIngredients = allIngredientsList.count { safeIngredients.contains(it) }

    println("Number of instances of ingredients with no known allergens: $countOfInstancesOfSafeIngredients")
}

fun getSafeIngredients(foods: List<Food>): Set<String> {
    val allIngredientsList = foods.flatMap { it.ingredients }
    val allIngredientsSet = allIngredientsList.toSet()
    val allAllergensSet = foods.flatMap { it.allergens }.toSet()
    val ingredientsContainingAllergens = mutableSetOf<String>()

    for (allergen in allAllergensSet) {
        ingredientsContainingAllergens.addAll(getCommonIngredientsForAllergen(foods, allergen))
    }
    return allIngredientsSet.subtract(ingredientsContainingAllergens)
}

fun getCommonIngredientsForAllergen(foods: List<Food>, allergen: String): List<String> {
    return foods
        .filter { it.allergens.contains(allergen) }
        .map { it.ingredients }
        .reduce { acc, list -> acc.intersect(list).toList() }
}

fun getIngredients(description: String): List<String> =
    description.split('(')[0].split(' ').filter { it.isNotBlank() }.toList()

fun getIdentifiedAllergens(description: String) =
    description.split('(')[1].removePrefix("contains").split(',').map { it.removeSuffix(")").trim() }.toList()

data class Food(
    val ingredients: List<String>,
    val allergens: List<String>
)

Compiled from "Day21a.kt"
public final class com.marcdenning.adventofcode.day21.Day21aKt {
  public static final void main(java.lang.String[]);
    Code:
       0: aload_0
       1: ldc           #9                  // String args
       3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: new           #17                 // class java/io/File
       9: dup
      10: aload_0
      11: iconst_0
      12: aaload
      13: invokespecial #21                 // Method java/io/File."<init>":(Ljava/lang/String;)V
      16: aconst_null
      17: iconst_1
      18: aconst_null
      19: invokestatic  #27                 // Method kotlin/io/FilesKt.readLines$default:(Ljava/io/File;Ljava/nio/charset/Charset;ILjava/lang/Object;)Ljava/util/List;
      22: checkcast     #29                 // class java/lang/Iterable
      25: astore_2
      26: iconst_0
      27: istore_3
      28: aload_2
      29: astore        4
      31: new           #31                 // class java/util/ArrayList
      34: dup
      35: aload_2
      36: bipush        10
      38: invokestatic  #37                 // Method kotlin/collections/CollectionsKt.collectionSizeOrDefault:(Ljava/lang/Iterable;I)I
      41: invokespecial #40                 // Method java/util/ArrayList."<init>":(I)V
      44: checkcast     #42                 // class java/util/Collection
      47: astore        5
      49: iconst_0
      50: istore        6
      52: aload         4
      54: invokeinterface #46,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
      59: astore        7
      61: aload         7
      63: invokeinterface #52,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
      68: ifeq          123
      71: aload         7
      73: invokeinterface #56,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      78: astore        8
      80: aload         5
      82: aload         8
      84: checkcast     #58                 // class java/lang/String
      87: astore        9
      89: astore        12
      91: iconst_0
      92: istore        10
      94: new           #60                 // class com/marcdenning/adventofcode/day21/Food
      97: dup
      98: aload         9
     100: invokestatic  #64                 // Method getIngredients:(Ljava/lang/String;)Ljava/util/List;
     103: aload         9
     105: invokestatic  #67                 // Method getIdentifiedAllergens:(Ljava/lang/String;)Ljava/util/List;
     108: invokespecial #70                 // Method com/marcdenning/adventofcode/day21/Food."<init>":(Ljava/util/List;Ljava/util/List;)V
     111: aload         12
     113: swap
     114: invokeinterface #74,  2           // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     119: pop
     120: goto          61
     123: aload         5
     125: checkcast     #76                 // class java/util/List
     128: nop
     129: astore_1
     130: aload_1
     131: checkcast     #29                 // class java/lang/Iterable
     134: astore_3
     135: iconst_0
     136: istore        4
     138: aload_3
     139: astore        5
     141: new           #31                 // class java/util/ArrayList
     144: dup
     145: invokespecial #79                 // Method java/util/ArrayList."<init>":()V
     148: checkcast     #42                 // class java/util/Collection
     151: astore        6
     153: iconst_0
     154: istore        7
     156: aload         5
     158: invokeinterface #46,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
     163: astore        8
     165: aload         8
     167: invokeinterface #52,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
     172: ifeq          215
     175: aload         8
     177: invokeinterface #56,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     182: astore        9
     184: aload         9
     186: checkcast     #60                 // class com/marcdenning/adventofcode/day21/Food
     189: astore        10
     191: iconst_0
     192: istore        11
     194: aload         10
     196: invokevirtual #82                 // Method com/marcdenning/adventofcode/day21/Food.getIngredients:()Ljava/util/List;
     199: checkcast     #29                 // class java/lang/Iterable
     202: astore        10
     204: aload         6
     206: aload         10
     208: invokestatic  #86                 // Method kotlin/collections/CollectionsKt.addAll:(Ljava/util/Collection;Ljava/lang/Iterable;)Z
     211: pop
     212: goto          165
     215: aload         6
     217: checkcast     #76                 // class java/util/List
     220: nop
     221: astore_2
     222: aload_1
     223: invokestatic  #90                 // Method getSafeIngredients:(Ljava/util/List;)Ljava/util/Set;
     226: astore_3
     227: aload_2
     228: checkcast     #29                 // class java/lang/Iterable
     231: astore        5
     233: iconst_0
     234: istore        6
     236: aload         5
     238: instanceof    #42                 // class java/util/Collection
     241: ifeq          261
     244: aload         5
     246: checkcast     #42                 // class java/util/Collection
     249: invokeinterface #93,  1           // InterfaceMethod java/util/Collection.isEmpty:()Z
     254: ifeq          261
     257: iconst_0
     258: goto          329
     261: iconst_0
     262: istore        7
     264: aload         5
     266: invokeinterface #46,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
     271: astore        8
     273: aload         8
     275: invokeinterface #52,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
     280: ifeq          327
     283: aload         8
     285: invokeinterface #56,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     290: astore        9
     292: aload         9
     294: checkcast     #58                 // class java/lang/String
     297: astore        10
     299: iconst_0
     300: istore        11
     302: aload_3
     303: aload         10
     305: invokeinterface #98,  2           // InterfaceMethod java/util/Set.contains:(Ljava/lang/Object;)Z
     310: ifeq          273
     313: iinc          7, 1
     316: iload         7
     318: ifge          273
     321: invokestatic  #101                // Method kotlin/collections/CollectionsKt.throwCountOverflow:()V
     324: goto          273
     327: iload         7
     329: istore        4
     331: new           #103                // class java/lang/StringBuilder
     334: dup
     335: invokespecial #104                // Method java/lang/StringBuilder."<init>":()V
     338: ldc           #106                // String Number of instances of ingredients with no known allergens:
     340: invokevirtual #110                // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     343: iload         4
     345: invokevirtual #113                // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
     348: invokevirtual #117                // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     351: getstatic     #123                // Field java/lang/System.out:Ljava/io/PrintStream;
     354: swap
     355: invokevirtual #129                // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
     358: return

  public static final java.util.Set<java.lang.String> getSafeIngredients(java.util.List<com.marcdenning.adventofcode.day21.Food>);
    Code:
       0: aload_0
       1: ldc           #165                // String foods
       3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: checkcast     #29                 // class java/lang/Iterable
      10: astore_2
      11: iconst_0
      12: istore_3
      13: aload_2
      14: astore        4
      16: new           #31                 // class java/util/ArrayList
      19: dup
      20: invokespecial #79                 // Method java/util/ArrayList."<init>":()V
      23: checkcast     #42                 // class java/util/Collection
      26: astore        5
      28: iconst_0
      29: istore        6
      31: aload         4
      33: invokeinterface #46,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
      38: astore        7
      40: aload         7
      42: invokeinterface #52,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
      47: ifeq          90
      50: aload         7
      52: invokeinterface #56,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      57: astore        8
      59: aload         8
      61: checkcast     #60                 // class com/marcdenning/adventofcode/day21/Food
      64: astore        9
      66: iconst_0
      67: istore        10
      69: aload         9
      71: invokevirtual #82                 // Method com/marcdenning/adventofcode/day21/Food.getIngredients:()Ljava/util/List;
      74: checkcast     #29                 // class java/lang/Iterable
      77: astore        9
      79: aload         5
      81: aload         9
      83: invokestatic  #86                 // Method kotlin/collections/CollectionsKt.addAll:(Ljava/util/Collection;Ljava/lang/Iterable;)Z
      86: pop
      87: goto          40
      90: aload         5
      92: checkcast     #76                 // class java/util/List
      95: nop
      96: astore_1
      97: aload_1
      98: checkcast     #29                 // class java/lang/Iterable
     101: invokestatic  #169                // Method kotlin/collections/CollectionsKt.toSet:(Ljava/lang/Iterable;)Ljava/util/Set;
     104: astore_2
     105: aload_0
     106: checkcast     #29                 // class java/lang/Iterable
     109: astore        4
     111: iconst_0
     112: istore        5
     114: aload         4
     116: astore        6
     118: new           #31                 // class java/util/ArrayList
     121: dup
     122: invokespecial #79                 // Method java/util/ArrayList."<init>":()V
     125: checkcast     #42                 // class java/util/Collection
     128: astore        7
     130: iconst_0
     131: istore        8
     133: aload         6
     135: invokeinterface #46,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
     140: astore        9
     142: aload         9
     144: invokeinterface #52,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
     149: ifeq          192
     152: aload         9
     154: invokeinterface #56,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     159: astore        10
     161: aload         10
     163: checkcast     #60                 // class com/marcdenning/adventofcode/day21/Food
     166: astore        11
     168: iconst_0
     169: istore        12
     171: aload         11
     173: invokevirtual #172                // Method com/marcdenning/adventofcode/day21/Food.getAllergens:()Ljava/util/List;
     176: checkcast     #29                 // class java/lang/Iterable
     179: astore        11
     181: aload         7
     183: aload         11
     185: invokestatic  #86                 // Method kotlin/collections/CollectionsKt.addAll:(Ljava/util/Collection;Ljava/lang/Iterable;)Z
     188: pop
     189: goto          142
     192: aload         7
     194: checkcast     #76                 // class java/util/List
     197: nop
     198: checkcast     #29                 // class java/lang/Iterable
     201: invokestatic  #169                // Method kotlin/collections/CollectionsKt.toSet:(Ljava/lang/Iterable;)Ljava/util/Set;
     204: astore_3
     205: new           #174                // class java/util/LinkedHashSet
     208: dup
     209: invokespecial #175                // Method java/util/LinkedHashSet."<init>":()V
     212: checkcast     #95                 // class java/util/Set
     215: astore        4
     217: aload_3
     218: invokeinterface #176,  1          // InterfaceMethod java/util/Set.iterator:()Ljava/util/Iterator;
     223: astore        5
     225: aload         5
     227: invokeinterface #52,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
     232: ifeq          267
     235: aload         5
     237: invokeinterface #56,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     242: checkcast     #58                 // class java/lang/String
     245: astore        6
     247: aload         4
     249: aload_0
     250: aload         6
     252: invokestatic  #180                // Method getCommonIngredientsForAllergen:(Ljava/util/List;Ljava/lang/String;)Ljava/util/List;
     255: checkcast     #42                 // class java/util/Collection
     258: invokeinterface #183,  2          // InterfaceMethod java/util/Set.addAll:(Ljava/util/Collection;)Z
     263: pop
     264: goto          225
     267: aload_2
     268: checkcast     #29                 // class java/lang/Iterable
     271: aload         4
     273: checkcast     #29                 // class java/lang/Iterable
     276: invokestatic  #187                // Method kotlin/collections/CollectionsKt.subtract:(Ljava/lang/Iterable;Ljava/lang/Iterable;)Ljava/util/Set;
     279: areturn

  public static final java.util.List<java.lang.String> getCommonIngredientsForAllergen(java.util.List<com.marcdenning.adventofcode.day21.Food>, java.lang.String);
    Code:
       0: aload_0
       1: ldc           #165                // String foods
       3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_1
       7: ldc           #195                // String allergen
       9: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
      12: aload_0
      13: checkcast     #29                 // class java/lang/Iterable
      16: astore_2
      17: nop
      18: iconst_0
      19: istore_3
      20: aload_2
      21: astore        4
      23: new           #31                 // class java/util/ArrayList
      26: dup
      27: invokespecial #79                 // Method java/util/ArrayList."<init>":()V
      30: checkcast     #42                 // class java/util/Collection
      33: astore        5
      35: iconst_0
      36: istore        6
      38: aload         4
      40: invokeinterface #46,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
      45: astore        7
      47: aload         7
      49: invokeinterface #52,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
      54: ifeq          103
      57: aload         7
      59: invokeinterface #56,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      64: astore        8
      66: aload         8
      68: checkcast     #60                 // class com/marcdenning/adventofcode/day21/Food
      71: astore        9
      73: iconst_0
      74: istore        10
      76: aload         9
      78: invokevirtual #172                // Method com/marcdenning/adventofcode/day21/Food.getAllergens:()Ljava/util/List;
      81: aload_1
      82: invokeinterface #196,  2          // InterfaceMethod java/util/List.contains:(Ljava/lang/Object;)Z
      87: ifeq          47
      90: aload         5
      92: aload         8
      94: invokeinterface #74,  2           // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
      99: pop
     100: goto          47
     103: aload         5
     105: checkcast     #76                 // class java/util/List
     108: nop
     109: checkcast     #29                 // class java/lang/Iterable
     112: astore_2
     113: nop
     114: iconst_0
     115: istore_3
     116: aload_2
     117: astore        4
     119: new           #31                 // class java/util/ArrayList
     122: dup
     123: aload_2
     124: bipush        10
     126: invokestatic  #37                 // Method kotlin/collections/CollectionsKt.collectionSizeOrDefault:(Ljava/lang/Iterable;I)I
     129: invokespecial #40                 // Method java/util/ArrayList."<init>":(I)V
     132: checkcast     #42                 // class java/util/Collection
     135: astore        5
     137: iconst_0
     138: istore        6
     140: aload         4
     142: invokeinterface #46,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
     147: astore        7
     149: aload         7
     151: invokeinterface #52,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
     156: ifeq          199
     159: aload         7
     161: invokeinterface #56,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     166: astore        8
     168: aload         5
     170: aload         8
     172: checkcast     #60                 // class com/marcdenning/adventofcode/day21/Food
     175: astore        9
     177: astore        11
     179: iconst_0
     180: istore        10
     182: aload         9
     184: invokevirtual #82                 // Method com/marcdenning/adventofcode/day21/Food.getIngredients:()Ljava/util/List;
     187: aload         11
     189: swap
     190: invokeinterface #74,  2           // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     195: pop
     196: goto          149
     199: aload         5
     201: checkcast     #76                 // class java/util/List
     204: nop
     205: checkcast     #29                 // class java/lang/Iterable
     208: astore_2
     209: nop
     210: iconst_0
     211: istore_3
     212: aload_2
     213: invokeinterface #46,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
     218: astore        4
     220: aload         4
     222: invokeinterface #52,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
     227: ifne          240
     230: new           #198                // class java/lang/UnsupportedOperationException
     233: dup
     234: ldc           #200                // String Empty collection can\'t be reduced.
     236: invokespecial #201                // Method java/lang/UnsupportedOperationException."<init>":(Ljava/lang/String;)V
     239: athrow
     240: aload         4
     242: invokeinterface #56,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     247: astore        5
     249: aload         4
     251: invokeinterface #52,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
     256: ifeq          305
     259: aload         5
     261: aload         4
     263: invokeinterface #56,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     268: checkcast     #76                 // class java/util/List
     271: astore        6
     273: checkcast     #76                 // class java/util/List
     276: astore        7
     278: iconst_0
     279: istore        8
     281: aload         7
     283: checkcast     #29                 // class java/lang/Iterable
     286: aload         6
     288: checkcast     #29                 // class java/lang/Iterable
     291: invokestatic  #204                // Method kotlin/collections/CollectionsKt.intersect:(Ljava/lang/Iterable;Ljava/lang/Iterable;)Ljava/util/Set;
     294: checkcast     #29                 // class java/lang/Iterable
     297: invokestatic  #208                // Method kotlin/collections/CollectionsKt.toList:(Ljava/lang/Iterable;)Ljava/util/List;
     300: astore        5
     302: goto          249
     305: aload         5
     307: checkcast     #76                 // class java/util/List
     310: areturn

  public static final java.util.List<java.lang.String> getIngredients(java.lang.String);
    Code:
       0: aload_0
       1: ldc           #225                // String description
       3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: checkcast     #227                // class java/lang/CharSequence
      10: iconst_1
      11: newarray       char
      13: astore_1
      14: aload_1
      15: iconst_0
      16: bipush        40
      18: castore
      19: aload_1
      20: iconst_0
      21: iconst_0
      22: bipush        6
      24: aconst_null
      25: invokestatic  #233                // Method kotlin/text/StringsKt.split$default:(Ljava/lang/CharSequence;[CZIILjava/lang/Object;)Ljava/util/List;
      28: iconst_0
      29: invokeinterface #237,  2          // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
      34: checkcast     #227                // class java/lang/CharSequence
      37: iconst_1
      38: newarray       char
      40: astore_1
      41: aload_1
      42: iconst_0
      43: bipush        32
      45: castore
      46: aload_1
      47: iconst_0
      48: iconst_0
      49: bipush        6
      51: aconst_null
      52: invokestatic  #233                // Method kotlin/text/StringsKt.split$default:(Ljava/lang/CharSequence;[CZIILjava/lang/Object;)Ljava/util/List;
      55: checkcast     #29                 // class java/lang/Iterable
      58: astore_1
      59: iconst_0
      60: istore_2
      61: aload_1
      62: astore_3
      63: new           #31                 // class java/util/ArrayList
      66: dup
      67: invokespecial #79                 // Method java/util/ArrayList."<init>":()V
      70: checkcast     #42                 // class java/util/Collection
      73: astore        4
      75: iconst_0
      76: istore        5
      78: aload_3
      79: invokeinterface #46,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
      84: astore        6
      86: aload         6
      88: invokeinterface #52,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
      93: ifeq          148
      96: aload         6
      98: invokeinterface #56,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     103: astore        7
     105: aload         7
     107: checkcast     #58                 // class java/lang/String
     110: astore        8
     112: iconst_0
     113: istore        9
     115: aload         8
     117: checkcast     #227                // class java/lang/CharSequence
     120: invokestatic  #241                // Method kotlin/text/StringsKt.isBlank:(Ljava/lang/CharSequence;)Z
     123: ifne          130
     126: iconst_1
     127: goto          131
     130: iconst_0
     131: nop
     132: ifeq          86
     135: aload         4
     137: aload         7
     139: invokeinterface #74,  2           // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     144: pop
     145: goto          86
     148: aload         4
     150: checkcast     #76                 // class java/util/List
     153: nop
     154: checkcast     #29                 // class java/lang/Iterable
     157: invokestatic  #208                // Method kotlin/collections/CollectionsKt.toList:(Ljava/lang/Iterable;)Ljava/util/List;
     160: areturn

  public static final java.util.List<java.lang.String> getIdentifiedAllergens(java.lang.String);
    Code:
       0: aload_0
       1: ldc           #225                // String description
       3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: checkcast     #227                // class java/lang/CharSequence
      10: iconst_1
      11: newarray       char
      13: astore_1
      14: aload_1
      15: iconst_0
      16: bipush        40
      18: castore
      19: aload_1
      20: iconst_0
      21: iconst_0
      22: bipush        6
      24: aconst_null
      25: invokestatic  #233                // Method kotlin/text/StringsKt.split$default:(Ljava/lang/CharSequence;[CZIILjava/lang/Object;)Ljava/util/List;
      28: iconst_1
      29: invokeinterface #237,  2          // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
      34: checkcast     #58                 // class java/lang/String
      37: ldc           #243                // String contains
      39: checkcast     #227                // class java/lang/CharSequence
      42: invokestatic  #247                // Method kotlin/text/StringsKt.removePrefix:(Ljava/lang/String;Ljava/lang/CharSequence;)Ljava/lang/String;
      45: checkcast     #227                // class java/lang/CharSequence
      48: iconst_1
      49: newarray       char
      51: astore_1
      52: aload_1
      53: iconst_0
      54: bipush        44
      56: castore
      57: aload_1
      58: iconst_0
      59: iconst_0
      60: bipush        6
      62: aconst_null
      63: invokestatic  #233                // Method kotlin/text/StringsKt.split$default:(Ljava/lang/CharSequence;[CZIILjava/lang/Object;)Ljava/util/List;
      66: checkcast     #29                 // class java/lang/Iterable
      69: astore_1
      70: iconst_0
      71: istore_2
      72: aload_1
      73: astore_3
      74: new           #31                 // class java/util/ArrayList
      77: dup
      78: aload_1
      79: bipush        10
      81: invokestatic  #37                 // Method kotlin/collections/CollectionsKt.collectionSizeOrDefault:(Ljava/lang/Iterable;I)I
      84: invokespecial #40                 // Method java/util/ArrayList."<init>":(I)V
      87: checkcast     #42                 // class java/util/Collection
      90: astore        4
      92: iconst_0
      93: istore        5
      95: aload_3
      96: invokeinterface #46,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
     101: astore        6
     103: aload         6
     105: invokeinterface #52,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
     110: ifeq          168
     113: aload         6
     115: invokeinterface #56,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     120: astore        7
     122: aload         4
     124: aload         7
     126: checkcast     #58                 // class java/lang/String
     129: astore        8
     131: astore        10
     133: iconst_0
     134: istore        9
     136: aload         8
     138: ldc           #249                // String )
     140: checkcast     #227                // class java/lang/CharSequence
     143: invokestatic  #252                // Method kotlin/text/StringsKt.removeSuffix:(Ljava/lang/String;Ljava/lang/CharSequence;)Ljava/lang/String;
     146: checkcast     #227                // class java/lang/CharSequence
     149: invokestatic  #256                // Method kotlin/text/StringsKt.trim:(Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
     152: invokevirtual #257                // Method java/lang/Object.toString:()Ljava/lang/String;
     155: nop
     156: aload         10
     158: swap
     159: invokeinterface #74,  2           // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     164: pop
     165: goto          103
     168: aload         4
     170: checkcast     #76                 // class java/util/List
     173: nop
     174: checkcast     #29                 // class java/lang/Iterable
     177: invokestatic  #208                // Method kotlin/collections/CollectionsKt.toList:(Ljava/lang/Iterable;)Ljava/util/List;
     180: areturn
}


package com.tddfellow.inference

data class Relation(val name: String, val subject: String, val relative: String) {
    private fun matcherFor(getter: (Relation) -> String): (Relation) -> Boolean {
        val value = getter(this)

        return if (value == "?") {
            { getter(it) != "?" }
        } else {
            { getter(it) == value }
        }
    }

    val extractors: List<(Relation) -> String> = listOf(
            { it -> it.name },
            { it -> it.subject },
            { it -> it.relative }
    )

    val matchers = extractors.map { matcherFor(it) }

    val freeExtractors = extractors.filter { it(this) == "?" }

    fun match(other: Relation): Boolean {
        return matchers.all { it(other) }
    }
}

val relations = mutableListOf<Relation>()

fun _clear() {
    relations.clear()
}

fun query(relation: Relation): List<Relation> {
    if (!relations.contains(relation)) {
        relations.add(relation)
    }

    return relations.filter { relation.match(it) }
}

fun query(relationString: String): List<Relation> {
    return query(relationFromString(relationString))
}

fun relationFromString(relationString: String): Relation {
    val found = relations
            .map { relationString.split(it.name) + listOf(it.name) }
            .find { it.count() == 3 }

    return if (found == null) {
        newRelationFromString(relationString)
    } else {
        Relation(found[2], found[0], found[1])
    }
}

fun newRelationFromString(relationString: String): Relation {
    val components = relationString.split("?")

    if (components.count() != 3) {
        throw RuntimeException("New relation should be in format '? <relation name> ?'")
    }

    return Relation(components[1], "?", "?")
}

fun join(
        left: List<Relation>, leftValue: (Relation) -> String,
        right: List<Relation>, rightValue: (Relation) -> String): List<String> {
    return left.flatMap { relation ->
        right.map { rightValue(it) }
                .filter { it == leftValue(relation) }
    }.distinct()
}

fun join(
        left: String, leftValue: (Relation) -> String,
        right: String, rightValue: (Relation) -> String): List<String> {
    val leftRelation = relationFromString(left)
    val rightRelation = relationFromString(right)

    return join(
            query(leftRelation), leftValue,
            query(rightRelation), rightValue)
}

fun join(left: String, right: String): List<String> {
    val leftRelation = relationFromString(left)
    if (leftRelation.freeExtractors.count() == 0) {
        throw RuntimeException("join(left, right): left should have at least one '?' token")
    }

    val rightRelation = relationFromString(right)
    if (rightRelation.freeExtractors.count() == 0) {
        throw RuntimeException("join(left, right): right should have at least one '?' token")
    }

    return join(
            query(leftRelation), leftRelation.freeExtractors.first(),
            query(rightRelation), rightRelation.freeExtractors.first()
    )
}

Compiled from "inference.kt"
public final class com.tddfellow.inference.InferenceKt {
  private static final java.util.List<com.tddfellow.inference.Relation> relations;

  public static final java.util.List<com.tddfellow.inference.Relation> getRelations();
    Code:
       0: getstatic     #12                 // Field relations:Ljava/util/List;
       3: areturn

  public static final void _clear();
    Code:
       0: getstatic     #12                 // Field relations:Ljava/util/List;
       3: invokeinterface #19,  1           // InterfaceMethod java/util/List.clear:()V
       8: return

  public static final java.util.List<com.tddfellow.inference.Relation> query(com.tddfellow.inference.Relation);
    Code:
       0: aload_0
       1: ldc           #24                 // String relation
       3: invokestatic  #30                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: getstatic     #12                 // Field relations:Ljava/util/List;
       9: aload_0
      10: invokeinterface #34,  2           // InterfaceMethod java/util/List.contains:(Ljava/lang/Object;)Z
      15: ifne          28
      18: getstatic     #12                 // Field relations:Ljava/util/List;
      21: aload_0
      22: invokeinterface #37,  2           // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      27: pop
      28: getstatic     #12                 // Field relations:Ljava/util/List;
      31: checkcast     #39                 // class java/lang/Iterable
      34: astore_1
      35: iconst_0
      36: istore_2
      37: aload_1
      38: astore_3
      39: new           #41                 // class java/util/ArrayList
      42: dup
      43: invokespecial #44                 // Method java/util/ArrayList."<init>":()V
      46: checkcast     #46                 // class java/util/Collection
      49: astore        4
      51: iconst_0
      52: istore        5
      54: aload_3
      55: invokeinterface #50,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
      60: astore        6
      62: aload         6
      64: invokeinterface #56,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
      69: ifeq          113
      72: aload         6
      74: invokeinterface #60,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      79: astore        7
      81: aload         7
      83: checkcast     #62                 // class com/tddfellow/inference/Relation
      86: astore        8
      88: iconst_0
      89: istore        9
      91: aload_0
      92: aload         8
      94: invokevirtual #66                 // Method com/tddfellow/inference/Relation.match:(Lcom/tddfellow/inference/Relation;)Z
      97: ifeq          62
     100: aload         4
     102: aload         7
     104: invokeinterface #67,  2           // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     109: pop
     110: goto          62
     113: aload         4
     115: checkcast     #16                 // class java/util/List
     118: nop
     119: areturn

  public static final java.util.List<com.tddfellow.inference.Relation> query(java.lang.String);
    Code:
       0: aload_0
       1: ldc           #84                 // String relationString
       3: invokestatic  #30                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: invokestatic  #88                 // Method relationFromString:(Ljava/lang/String;)Lcom/tddfellow/inference/Relation;
      10: invokestatic  #90                 // Method query:(Lcom/tddfellow/inference/Relation;)Ljava/util/List;
      13: areturn

  public static final com.tddfellow.inference.Relation relationFromString(java.lang.String);
    Code:
       0: aload_0
       1: ldc           #84                 // String relationString
       3: invokestatic  #30                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: getstatic     #12                 // Field relations:Ljava/util/List;
       9: checkcast     #39                 // class java/lang/Iterable
      12: astore_2
      13: nop
      14: iconst_0
      15: istore_3
      16: aload_2
      17: astore        4
      19: new           #41                 // class java/util/ArrayList
      22: dup
      23: aload_2
      24: bipush        10
      26: invokestatic  #97                 // Method kotlin/collections/CollectionsKt.collectionSizeOrDefault:(Ljava/lang/Iterable;I)I
      29: invokespecial #100                // Method java/util/ArrayList."<init>":(I)V
      32: checkcast     #46                 // class java/util/Collection
      35: astore        5
      37: iconst_0
      38: istore        6
      40: aload         4
      42: invokeinterface #50,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
      47: astore        7
      49: aload         7
      51: invokeinterface #56,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
      56: ifeq          140
      59: aload         7
      61: invokeinterface #60,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      66: astore        8
      68: aload         5
      70: aload         8
      72: checkcast     #62                 // class com/tddfellow/inference/Relation
      75: astore        9
      77: astore        12
      79: iconst_0
      80: istore        10
      82: aload_0
      83: checkcast     #102                // class java/lang/CharSequence
      86: iconst_1
      87: anewarray     #104                // class java/lang/String
      90: astore        11
      92: aload         11
      94: iconst_0
      95: aload         9
      97: invokevirtual #108                // Method com/tddfellow/inference/Relation.getName:()Ljava/lang/String;
     100: aastore
     101: aload         11
     103: iconst_0
     104: iconst_0
     105: bipush        6
     107: aconst_null
     108: invokestatic  #114                // Method kotlin/text/StringsKt.split$default:(Ljava/lang/CharSequence;[Ljava/lang/String;ZIILjava/lang/Object;)Ljava/util/List;
     111: checkcast     #46                 // class java/util/Collection
     114: aload         9
     116: invokevirtual #108                // Method com/tddfellow/inference/Relation.getName:()Ljava/lang/String;
     119: invokestatic  #118                // Method kotlin/collections/CollectionsKt.listOf:(Ljava/lang/Object;)Ljava/util/List;
     122: checkcast     #39                 // class java/lang/Iterable
     125: invokestatic  #122                // Method kotlin/collections/CollectionsKt.plus:(Ljava/util/Collection;Ljava/lang/Iterable;)Ljava/util/List;
     128: aload         12
     130: swap
     131: invokeinterface #67,  2           // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     136: pop
     137: goto          49
     140: aload         5
     142: checkcast     #16                 // class java/util/List
     145: nop
     146: checkcast     #39                 // class java/lang/Iterable
     149: astore_3
     150: aload_3
     151: invokeinterface #50,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
     156: astore        4
     158: aload         4
     160: invokeinterface #56,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
     165: ifeq          214
     168: aload         4
     170: invokeinterface #60,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     175: astore        5
     177: aload         5
     179: checkcast     #16                 // class java/util/List
     182: astore        6
     184: iconst_0
     185: istore        7
     187: aload         6
     189: checkcast     #46                 // class java/util/Collection
     192: invokeinterface #126,  1          // InterfaceMethod java/util/Collection.size:()I
     197: iconst_3
     198: if_icmpne     205
     201: iconst_1
     202: goto          206
     205: iconst_0
     206: ifeq          158
     209: aload         5
     211: goto          215
     214: aconst_null
     215: checkcast     #16                 // class java/util/List
     218: astore_1
     219: aload_1
     220: ifnonnull     230
     223: aload_0
     224: invokestatic  #129                // Method newRelationFromString:(Ljava/lang/String;)Lcom/tddfellow/inference/Relation;
     227: goto          267
     230: new           #62                 // class com/tddfellow/inference/Relation
     233: dup
     234: aload_1
     235: iconst_2
     236: invokeinterface #133,  2          // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
     241: checkcast     #104                // class java/lang/String
     244: aload_1
     245: iconst_0
     246: invokeinterface #133,  2          // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
     251: checkcast     #104                // class java/lang/String
     254: aload_1
     255: iconst_1
     256: invokeinterface #133,  2          // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
     261: checkcast     #104                // class java/lang/String
     264: invokespecial #136                // Method com/tddfellow/inference/Relation."<init>":(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
     267: areturn

  public static final com.tddfellow.inference.Relation newRelationFromString(java.lang.String);
    Code:
       0: aload_0
       1: ldc           #84                 // String relationString
       3: invokestatic  #30                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: checkcast     #102                // class java/lang/CharSequence
      10: iconst_1
      11: anewarray     #104                // class java/lang/String
      14: astore_2
      15: aload_2
      16: iconst_0
      17: ldc           #146                // String ?
      19: aastore
      20: aload_2
      21: iconst_0
      22: iconst_0
      23: bipush        6
      25: aconst_null
      26: invokestatic  #114                // Method kotlin/text/StringsKt.split$default:(Ljava/lang/CharSequence;[Ljava/lang/String;ZIILjava/lang/Object;)Ljava/util/List;
      29: astore_1
      30: aload_1
      31: checkcast     #46                 // class java/util/Collection
      34: invokeinterface #126,  1          // InterfaceMethod java/util/Collection.size:()I
      39: iconst_3
      40: if_icmpeq     53
      43: new           #148                // class java/lang/RuntimeException
      46: dup
      47: ldc           #150                // String New relation should be in format \'? <relation name> ?\'
      49: invokespecial #153                // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
      52: athrow
      53: new           #62                 // class com/tddfellow/inference/Relation
      56: dup
      57: aload_1
      58: iconst_1
      59: invokeinterface #133,  2          // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
      64: checkcast     #104                // class java/lang/String
      67: ldc           #146                // String ?
      69: ldc           #146                // String ?
      71: invokespecial #136                // Method com/tddfellow/inference/Relation."<init>":(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
      74: areturn

  public static final java.util.List<java.lang.String> join(java.util.List<com.tddfellow.inference.Relation>, kotlin.jvm.functions.Function1<? super com.tddfellow.inference.Relation, java.lang.String>, java.util.List<com.tddfellow.inference.Relation>, kotlin.jvm.functions.Function1<? super com.tddfellow.inference.Relation, java.lang.String>);
    Code:
       0: aload_0
       1: ldc           #161                // String left
       3: invokestatic  #30                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_1
       7: ldc           #163                // String leftValue
       9: invokestatic  #30                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
      12: aload_2
      13: ldc           #165                // String right
      15: invokestatic  #30                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
      18: aload_3
      19: ldc           #167                // String rightValue
      21: invokestatic  #30                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
      24: aload_0
      25: checkcast     #39                 // class java/lang/Iterable
      28: astore        4
      30: iconst_0
      31: istore        5
      33: aload         4
      35: astore        6
      37: new           #41                 // class java/util/ArrayList
      40: dup
      41: invokespecial #44                 // Method java/util/ArrayList."<init>":()V
      44: checkcast     #46                 // class java/util/Collection
      47: astore        7
      49: iconst_0
      50: istore        8
      52: aload         6
      54: invokeinterface #50,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
      59: astore        9
      61: aload         9
      63: invokeinterface #56,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
      68: ifeq          314
      71: aload         9
      73: invokeinterface #60,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      78: astore        10
      80: aload         10
      82: checkcast     #62                 // class com/tddfellow/inference/Relation
      85: astore        11
      87: iconst_0
      88: istore        12
      90: aload_2
      91: checkcast     #39                 // class java/lang/Iterable
      94: astore        13
      96: iconst_0
      97: istore        14
      99: aload         13
     101: astore        15
     103: new           #41                 // class java/util/ArrayList
     106: dup
     107: aload         13
     109: bipush        10
     111: invokestatic  #97                 // Method kotlin/collections/CollectionsKt.collectionSizeOrDefault:(Ljava/lang/Iterable;I)I
     114: invokespecial #100                // Method java/util/ArrayList."<init>":(I)V
     117: checkcast     #46                 // class java/util/Collection
     120: astore        16
     122: iconst_0
     123: istore        17
     125: aload         15
     127: invokeinterface #50,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
     132: astore        18
     134: aload         18
     136: invokeinterface #56,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
     141: ifeq          190
     144: aload         18
     146: invokeinterface #60,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     151: astore        19
     153: aload         16
     155: aload         19
     157: checkcast     #62                 // class com/tddfellow/inference/Relation
     160: astore        20
     162: astore        21
     164: iconst_0
     165: istore        22
     167: aload_3
     168: aload         20
     170: invokeinterface #173,  2          // InterfaceMethod kotlin/jvm/functions/Function1.invoke:(Ljava/lang/Object;)Ljava/lang/Object;
     175: checkcast     #104                // class java/lang/String
     178: aload         21
     180: swap
     181: invokeinterface #67,  2           // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     186: pop
     187: goto          134
     190: aload         16
     192: checkcast     #16                 // class java/util/List
     195: nop
     196: checkcast     #39                 // class java/lang/Iterable
     199: astore        13
     201: nop
     202: iconst_0
     203: istore        14
     205: aload         13
     207: astore        15
     209: new           #41                 // class java/util/ArrayList
     212: dup
     213: invokespecial #44                 // Method java/util/ArrayList."<init>":()V
     216: checkcast     #46                 // class java/util/Collection
     219: astore        16
     221: iconst_0
     222: istore        17
     224: aload         15
     226: invokeinterface #50,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
     231: astore        18
     233: aload         18
     235: invokeinterface #56,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
     240: ifeq          291
     243: aload         18
     245: invokeinterface #60,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     250: astore        19
     252: aload         19
     254: checkcast     #104                // class java/lang/String
     257: astore        20
     259: iconst_0
     260: istore        22
     262: aload         20
     264: aload_1
     265: aload         11
     267: invokeinterface #173,  2          // InterfaceMethod kotlin/jvm/functions/Function1.invoke:(Ljava/lang/Object;)Ljava/lang/Object;
     272: invokestatic  #177                // Method kotlin/jvm/internal/Intrinsics.areEqual:(Ljava/lang/Object;Ljava/lang/Object;)Z
     275: ifeq          233
     278: aload         16
     280: aload         19
     282: invokeinterface #67,  2           // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     287: pop
     288: goto          233
     291: aload         16
     293: checkcast     #16                 // class java/util/List
     296: nop
     297: checkcast     #39                 // class java/lang/Iterable
     300: nop
     301: astore        23
     303: aload         7
     305: aload         23
     307: invokestatic  #181                // Method kotlin/collections/CollectionsKt.addAll:(Ljava/util/Collection;Ljava/lang/Iterable;)Z
     310: pop
     311: goto          61
     314: aload         7
     316: checkcast     #16                 // class java/util/List
     319: nop
     320: checkcast     #39                 // class java/lang/Iterable
     323: invokestatic  #185                // Method kotlin/collections/CollectionsKt.distinct:(Ljava/lang/Iterable;)Ljava/util/List;
     326: areturn

  public static final java.util.List<java.lang.String> join(java.lang.String, kotlin.jvm.functions.Function1<? super com.tddfellow.inference.Relation, java.lang.String>, java.lang.String, kotlin.jvm.functions.Function1<? super com.tddfellow.inference.Relation, java.lang.String>);
    Code:
       0: aload_0
       1: ldc           #161                // String left
       3: invokestatic  #30                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_1
       7: ldc           #163                // String leftValue
       9: invokestatic  #30                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
      12: aload_2
      13: ldc           #165                // String right
      15: invokestatic  #30                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
      18: aload_3
      19: ldc           #167                // String rightValue
      21: invokestatic  #30                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
      24: aload_0
      25: invokestatic  #88                 // Method relationFromString:(Ljava/lang/String;)Lcom/tddfellow/inference/Relation;
      28: astore        4
      30: aload_2
      31: invokestatic  #88                 // Method relationFromString:(Ljava/lang/String;)Lcom/tddfellow/inference/Relation;
      34: astore        5
      36: aload         4
      38: invokestatic  #90                 // Method query:(Lcom/tddfellow/inference/Relation;)Ljava/util/List;
      41: aload_1
      42: aload         5
      44: invokestatic  #90                 // Method query:(Lcom/tddfellow/inference/Relation;)Ljava/util/List;
      47: aload_3
      48: invokestatic  #198                // Method join:(Ljava/util/List;Lkotlin/jvm/functions/Function1;Ljava/util/List;Lkotlin/jvm/functions/Function1;)Ljava/util/List;
      51: areturn

  public static final java.util.List<java.lang.String> join(java.lang.String, java.lang.String);
    Code:
       0: aload_0
       1: ldc           #161                // String left
       3: invokestatic  #30                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_1
       7: ldc           #165                // String right
       9: invokestatic  #30                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
      12: aload_0
      13: invokestatic  #88                 // Method relationFromString:(Ljava/lang/String;)Lcom/tddfellow/inference/Relation;
      16: astore_2
      17: aload_2
      18: invokevirtual #205                // Method com/tddfellow/inference/Relation.getFreeExtractors:()Ljava/util/List;
      21: checkcast     #46                 // class java/util/Collection
      24: invokeinterface #126,  1          // InterfaceMethod java/util/Collection.size:()I
      29: ifne          42
      32: new           #148                // class java/lang/RuntimeException
      35: dup
      36: ldc           #207                // String join(left, right): left should have at least one \'?\' token
      38: invokespecial #153                // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
      41: athrow
      42: aload_1
      43: invokestatic  #88                 // Method relationFromString:(Ljava/lang/String;)Lcom/tddfellow/inference/Relation;
      46: astore_3
      47: aload_3
      48: invokevirtual #205                // Method com/tddfellow/inference/Relation.getFreeExtractors:()Ljava/util/List;
      51: checkcast     #46                 // class java/util/Collection
      54: invokeinterface #126,  1          // InterfaceMethod java/util/Collection.size:()I
      59: ifne          72
      62: new           #148                // class java/lang/RuntimeException
      65: dup
      66: ldc           #209                // String join(left, right): right should have at least one \'?\' token
      68: invokespecial #153                // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
      71: athrow
      72: aload_2
      73: invokestatic  #90                 // Method query:(Lcom/tddfellow/inference/Relation;)Ljava/util/List;
      76: aload_2
      77: invokevirtual #205                // Method com/tddfellow/inference/Relation.getFreeExtractors:()Ljava/util/List;
      80: invokestatic  #213                // Method kotlin/collections/CollectionsKt.first:(Ljava/util/List;)Ljava/lang/Object;
      83: checkcast     #169                // class kotlin/jvm/functions/Function1
      86: aload_3
      87: invokestatic  #90                 // Method query:(Lcom/tddfellow/inference/Relation;)Ljava/util/List;
      90: aload_3
      91: invokevirtual #205                // Method com/tddfellow/inference/Relation.getFreeExtractors:()Ljava/util/List;
      94: invokestatic  #213                // Method kotlin/collections/CollectionsKt.first:(Ljava/util/List;)Ljava/lang/Object;
      97: checkcast     #169                // class kotlin/jvm/functions/Function1
     100: invokestatic  #198                // Method join:(Ljava/util/List;Lkotlin/jvm/functions/Function1;Ljava/util/List;Lkotlin/jvm/functions/Function1;)Ljava/util/List;
     103: areturn

  static {};
    Code:
       0: new           #41                 // class java/util/ArrayList
       3: dup
       4: invokespecial #44                 // Method java/util/ArrayList."<init>":()V
       7: checkcast     #16                 // class java/util/List
      10: putstatic     #12                 // Field relations:Ljava/util/List;
      13: return
}