package services.utilities

import java.util.concurrent.ConcurrentHashMap

class RequirementsParser {
    // requirementsData example:
    //    ["1:CS 115,CS 135,CS 145;1:CS 136,CS 146;1:MATH 127,MATH 137,MATH 147;1:MATH 128,MATH 138,MATH 148;1:MATH 135,MATH 145;1:MATH 136,MATH 146;1:MATH 239,MATH 249;1:STAT 230,STAT 240;1:STAT 231,STAT 241;1:CS 136L;1:CS 240,CS 240E;1:CS 241,CS 241E;1:CS 245,CS 245E;1:CS 246,CS 246E;1:CS 251,CS 251E;1:CS 341;1:CS 350;3:CS 340-CS 398,CS 440-CS 489;2:CS 440-CS 489;1:CO 487,CS 440-CS 498,CS 499T,STAT 440,CS 6xx,CS 7xx"]
    fun parseRequirements(requirementsData: List<String>): Requirements {
        return finalizeRequirements(initialReqParse(requirementsData))
    }

    private fun parseBoundedRangeCourses(course: String, courses: MutableSet<Course>): Unit {
        val courseRange = course.split("-")
        val courseLowerBound = courseRange[0].split(" ")
        val courseUpperBound = courseRange[1].split(" ")
        val courseSubject = courseLowerBound[0]
        val courseLowerCode = courseLowerBound[1].toInt()
        val courseUpperCode = courseUpperBound[1].toInt()
        // Add all possible course codes now. Invalid ones will be filter out when querying DB.
        for (i in courseLowerCode..courseUpperCode) {
            courses.add(
                Course(
                    subject = courseSubject,
                    code = i.toString(),
                )
            )
        }
    }

    // Parse a single requirement clause. Eg. requirement = "1:CS 115,CS 135,CS 145"
    private fun parseSingleReqClause(requirement: String, optionalCourses: MutableSet<OptionalCourses>,
                                     mandatoryCourses: MutableSet<Course>): Unit {
        val courses = mutableSetOf<Course>()
        val temp = requirement.split(":")
        val nOf = temp[0]
        val courseList = temp[1].split(",")
        for (course in courseList) {
            // Eg. CS 440-CS 489
            if (course.contains("-")) {
                parseBoundedRangeCourses(course, courses)
            } else {
                val newCourse = course.split(" ")
                // Eg. CS 7xx
                if (newCourse[1].contains("xx")) {
                    // Add all possible values of xx as course. These courses will be filtered when querying DB.
                    for (i in 0..9) {
                        courses.add(
                            Course(
                                subject = newCourse[0],
                                code = newCourse[1][0] + "0" + i.toString(),
                            )
                        )
                    }
                    for (i in 10..99) {
                        courses.add(
                            Course(
                                subject = newCourse[0],
                                code = newCourse[1][0] + i.toString(),
                            )
                        )
                    }
                } else {
                    courses.add(
                        Course(
                            subject = newCourse[0],
                            code = newCourse[1],
                        )
                    )
                }
            }
        }
        if (nOf.toInt() == courses.size) {
            mandatoryCourses.addAll(courses)
        } else {
            optionalCourses.add(
                OptionalCourses(
                    nOf = nOf.toInt(),
                    courses = courses,
                )
            )
        }
    }

    // Extract all courses needed based on requirement.
    fun initialReqParse(requirementsData: List<String>): Requirements {
        val optionalCourses = mutableSetOf<OptionalCourses>()
        val mandatoryCourses = mutableSetOf<Course>()
        for (requirements in requirementsData) {
            val requirement = requirements.split(";")
            for (r in requirement) {
                parseSingleReqClause(r, optionalCourses, mandatoryCourses)
            }
        }

        return Requirements(
            optionalCourses = optionalCourses,
            mandatoryCourses = mandatoryCourses,
        )
    }

    // Finalize requirements by restraining some optional choices of courses as mandatory
    private fun finalizeRequirements(requirements: Requirements): Requirements {
        val optionalCoursesList = requirements.optionalCourses.toMutableList()
        val mandatoryCourses = requirements.mandatoryCourses.toMutableList()
        val commonTable: ConcurrentHashMap<Course, Int> = ConcurrentHashMap()

        for (i in 0 until optionalCoursesList.size) {
            for (j in i + 1 until optionalCoursesList.size) {
                val coursesI = optionalCoursesList[i]
                val coursesJ = optionalCoursesList[j]
                val commonCourses = coursesJ.courses.filter { coursesI.courses.contains(it) }.toSet()
                for (commonCourse in commonCourses) {
                    commonTable.compute(commonCourse) { _, count -> count?.plus(1) ?: 1 }
                }
            }
        }

        val remainingOptionalCourses = mutableListOf<OptionalCourses>()
        for (optionalCourses in optionalCoursesList) {
            var commonCourses = optionalCourses.courses.filter { it in commonTable.keys }.toSet()
            if (commonCourses.size < optionalCourses.nOf) {
                optionalCourses.nOf -= commonCourses.size
                optionalCourses.courses.removeAll(commonCourses)
                remainingOptionalCourses.add(optionalCourses)
            } else {
                commonCourses = commonTable.filterKeys { it in commonCourses }
                    .toList()
                    .sortedBy { (_, value) -> value }
                    .map { it.first }
                    .take(optionalCourses.nOf)
                    .toSet()
                mandatoryCourses.addAll(commonCourses)
            }
        }

        remainingOptionalCourses.removeIf { optionalCourses ->
            mandatoryCourses.containsAll(optionalCourses.courses)
        }

        // add breadth and depth
        mandatoryCourses.addAll(
            listOf(
                Course("ECON", "101"),
                Course("ECON", "102"),
                Course("ECON", "371"),
            )
        )

        mandatoryCourses.addAll(
            listOf(
                Course("MUSIC", "116"),
                Course("PHYS", "111"),
                Course("CHEM", "102"),
            )
        )

        return Requirements(
            optionalCourses = remainingOptionalCourses.toMutableSet(),
            mandatoryCourses = mandatoryCourses.toMutableSet(),
        )
    }

    fun combineOptionalRequirements(
        requirements: Requirements,
        checkCourses: List<String>,
    ): CommonRequirementsData {
        var commonSize = 0
        if (checkCourses.isNotEmpty()) {
            val iterator = requirements.optionalCourses.iterator()

            while (iterator.hasNext()) {
                val optionalReq = iterator.next()
                val commonCourses = optionalReq.courses.filter { "${it.subject} ${it.code}" in checkCourses }
                commonSize = commonCourses.size
                if (commonSize >= optionalReq.nOf) {
                    iterator.remove()
                } else {
                    optionalReq.nOf -= commonSize
                    optionalReq.courses.removeAll(commonCourses.toSet())
                }
            }
        }
        return CommonRequirementsData(
            requirements = requirements,
            commonSize = commonSize,
        )
    }
}

data class Course (
    val subject: String,
    val code: String,
)

data class OptionalCourses (
    var nOf: Int = 1,
    val courses: MutableSet<Course> = mutableSetOf(),
)

data class Requirements (
    val optionalCourses: MutableSet<OptionalCourses> = mutableSetOf(),
    val mandatoryCourses: MutableSet<Course> = mutableSetOf(),
)

data class CommonRequirementsData (
    val requirements: Requirements,
    val commonSize: Int,
)

Compiled from "Comparisons.kt"
public final class services.utilities.RequirementsParser$finalizeRequirements$$inlined$sortedBy$1<T> implements java.util.Comparator {
  public services.utilities.RequirementsParser$finalizeRequirements$$inlined$sortedBy$1();
    Code:
       0: aload_0
       1: invokespecial #16                 // Method java/lang/Object."<init>":()V
       4: return

  public final int compare(T, T);
    Code:
       0: aload_1
       1: checkcast     #23                 // class kotlin/Pair
       4: astore_3
       5: iconst_0
       6: istore        4
       8: aload_3
       9: invokevirtual #27                 // Method kotlin/Pair.component2:()Ljava/lang/Object;
      12: checkcast     #29                 // class java/lang/Number
      15: invokevirtual #33                 // Method java/lang/Number.intValue:()I
      18: istore        5
      20: iload         5
      22: invokestatic  #39                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      25: checkcast     #41                 // class java/lang/Comparable
      28: aload_2
      29: checkcast     #23                 // class kotlin/Pair
      32: astore_3
      33: astore        6
      35: iconst_0
      36: istore        4
      38: aload_3
      39: invokevirtual #27                 // Method kotlin/Pair.component2:()Ljava/lang/Object;
      42: checkcast     #29                 // class java/lang/Number
      45: invokevirtual #33                 // Method java/lang/Number.intValue:()I
      48: istore        5
      50: iload         5
      52: invokestatic  #39                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      55: aload         6
      57: swap
      58: checkcast     #41                 // class java/lang/Comparable
      61: invokestatic  #47                 // Method kotlin/comparisons/ComparisonsKt.compareValues:(Ljava/lang/Comparable;Ljava/lang/Comparable;)I
      64: ireturn
}

Compiled from "RequirementsParser.kt"
public final class services.utilities.RequirementsParser {
  public services.utilities.RequirementsParser();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public final services.utilities.Requirements parseRequirements(java.util.List<java.lang.String>);
    Code:
       0: aload_1
       1: ldc           #16                 // String requirementsData
       3: invokestatic  #22                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: aload_0
       8: aload_1
       9: invokevirtual #25                 // Method initialReqParse:(Ljava/util/List;)Lservices/utilities/Requirements;
      12: invokespecial #29                 // Method finalizeRequirements:(Lservices/utilities/Requirements;)Lservices/utilities/Requirements;
      15: areturn

  private final void parseBoundedRangeCourses(java.lang.String, java.util.Set<services.utilities.Course>);
    Code:
       0: aload_1
       1: checkcast     #35                 // class java/lang/CharSequence
       4: iconst_1
       5: anewarray     #37                 // class java/lang/String
       8: astore        4
      10: aload         4
      12: iconst_0
      13: ldc           #39                 // String -
      15: aastore
      16: aload         4
      18: iconst_0
      19: iconst_0
      20: bipush        6
      22: aconst_null
      23: invokestatic  #45                 // Method kotlin/text/StringsKt.split$default:(Ljava/lang/CharSequence;[Ljava/lang/String;ZIILjava/lang/Object;)Ljava/util/List;
      26: astore_3
      27: aload_3
      28: iconst_0
      29: invokeinterface #51,  2           // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
      34: checkcast     #35                 // class java/lang/CharSequence
      37: iconst_1
      38: anewarray     #37                 // class java/lang/String
      41: astore        5
      43: aload         5
      45: iconst_0
      46: ldc           #53                 // String
      48: aastore
      49: aload         5
      51: iconst_0
      52: iconst_0
      53: bipush        6
      55: aconst_null
      56: invokestatic  #45                 // Method kotlin/text/StringsKt.split$default:(Ljava/lang/CharSequence;[Ljava/lang/String;ZIILjava/lang/Object;)Ljava/util/List;
      59: astore        4
      61: aload_3
      62: iconst_1
      63: invokeinterface #51,  2           // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
      68: checkcast     #35                 // class java/lang/CharSequence
      71: iconst_1
      72: anewarray     #37                 // class java/lang/String
      75: astore        6
      77: aload         6
      79: iconst_0
      80: ldc           #53                 // String
      82: aastore
      83: aload         6
      85: iconst_0
      86: iconst_0
      87: bipush        6
      89: aconst_null
      90: invokestatic  #45                 // Method kotlin/text/StringsKt.split$default:(Ljava/lang/CharSequence;[Ljava/lang/String;ZIILjava/lang/Object;)Ljava/util/List;
      93: astore        5
      95: aload         4
      97: iconst_0
      98: invokeinterface #51,  2           // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
     103: checkcast     #37                 // class java/lang/String
     106: astore        6
     108: aload         4
     110: iconst_1
     111: invokeinterface #51,  2           // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
     116: checkcast     #37                 // class java/lang/String
     119: invokestatic  #59                 // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I
     122: istore        7
     124: aload         5
     126: iconst_1
     127: invokeinterface #51,  2           // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
     132: checkcast     #37                 // class java/lang/String
     135: invokestatic  #59                 // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I
     138: istore        8
     140: iload         7
     142: istore        9
     144: iload         9
     146: iload         8
     148: if_icmpgt     185
     151: aload_2
     152: new           #61                 // class services/utilities/Course
     155: dup
     156: aload         6
     158: iload         9
     160: invokestatic  #65                 // Method java/lang/String.valueOf:(I)Ljava/lang/String;
     163: invokespecial #68                 // Method services/utilities/Course."<init>":(Ljava/lang/String;Ljava/lang/String;)V
     166: invokeinterface #74,  2           // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
     171: pop
     172: iload         9
     174: iload         8
     176: if_icmpeq     185
     179: iinc          9, 1
     182: goto          151
     185: return

  private final void parseSingleReqClause(java.lang.String, java.util.Set<services.utilities.OptionalCourses>, java.util.Set<services.utilities.Course>);
    Code:
       0: new           #91                 // class java/util/LinkedHashSet
       3: dup
       4: invokespecial #92                 // Method java/util/LinkedHashSet."<init>":()V
       7: checkcast     #70                 // class java/util/Set
      10: astore        4
      12: aload_1
      13: checkcast     #35                 // class java/lang/CharSequence
      16: iconst_1
      17: anewarray     #37                 // class java/lang/String
      20: astore        6
      22: aload         6
      24: iconst_0
      25: ldc           #94                 // String :
      27: aastore
      28: aload         6
      30: iconst_0
      31: iconst_0
      32: bipush        6
      34: aconst_null
      35: invokestatic  #45                 // Method kotlin/text/StringsKt.split$default:(Ljava/lang/CharSequence;[Ljava/lang/String;ZIILjava/lang/Object;)Ljava/util/List;
      38: astore        5
      40: aload         5
      42: iconst_0
      43: invokeinterface #51,  2           // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
      48: checkcast     #37                 // class java/lang/String
      51: astore        6
      53: aload         5
      55: iconst_1
      56: invokeinterface #51,  2           // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
      61: checkcast     #35                 // class java/lang/CharSequence
      64: iconst_1
      65: anewarray     #37                 // class java/lang/String
      68: astore        8
      70: aload         8
      72: iconst_0
      73: ldc           #96                 // String ,
      75: aastore
      76: aload         8
      78: iconst_0
      79: iconst_0
      80: bipush        6
      82: aconst_null
      83: invokestatic  #45                 // Method kotlin/text/StringsKt.split$default:(Ljava/lang/CharSequence;[Ljava/lang/String;ZIILjava/lang/Object;)Ljava/util/List;
      86: astore        7
      88: aload         7
      90: invokeinterface #100,  1          // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
      95: astore        8
      97: aload         8
      99: invokeinterface #106,  1          // InterfaceMethod java/util/Iterator.hasNext:()Z
     104: ifeq          431
     107: aload         8
     109: invokeinterface #110,  1          // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     114: checkcast     #37                 // class java/lang/String
     117: astore        9
     119: aload         9
     121: checkcast     #35                 // class java/lang/CharSequence
     124: ldc           #39                 // String -
     126: checkcast     #35                 // class java/lang/CharSequence
     129: iconst_0
     130: iconst_2
     131: aconst_null
     132: invokestatic  #114                // Method kotlin/text/StringsKt.contains$default:(Ljava/lang/CharSequence;Ljava/lang/CharSequence;ZILjava/lang/Object;)Z
     135: ifeq          149
     138: aload_0
     139: aload         9
     141: aload         4
     143: invokespecial #116                // Method parseBoundedRangeCourses:(Ljava/lang/String;Ljava/util/Set;)V
     146: goto          97
     149: aload         9
     151: checkcast     #35                 // class java/lang/CharSequence
     154: iconst_1
     155: anewarray     #37                 // class java/lang/String
     158: astore        11
     160: aload         11
     162: iconst_0
     163: ldc           #53                 // String
     165: aastore
     166: aload         11
     168: iconst_0
     169: iconst_0
     170: bipush        6
     172: aconst_null
     173: invokestatic  #45                 // Method kotlin/text/StringsKt.split$default:(Ljava/lang/CharSequence;[Ljava/lang/String;ZIILjava/lang/Object;)Ljava/util/List;
     176: astore        10
     178: aload         10
     180: iconst_1
     181: invokeinterface #51,  2           // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
     186: checkcast     #35                 // class java/lang/CharSequence
     189: ldc           #118                // String xx
     191: checkcast     #35                 // class java/lang/CharSequence
     194: iconst_0
     195: iconst_2
     196: aconst_null
     197: invokestatic  #114                // Method kotlin/text/StringsKt.contains$default:(Ljava/lang/CharSequence;Ljava/lang/CharSequence;ZILjava/lang/Object;)Z
     200: ifeq          391
     203: iconst_0
     204: istore        11
     206: iload         11
     208: bipush        10
     210: if_icmpge     304
     213: aload         4
     215: new           #61                 // class services/utilities/Course
     218: dup
     219: aload         10
     221: iconst_0
     222: invokeinterface #51,  2           // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
     227: checkcast     #37                 // class java/lang/String
     230: new           #120                // class java/lang/StringBuilder
     233: dup
     234: invokespecial #121                // Method java/lang/StringBuilder."<init>":()V
     237: aload         10
     239: iconst_1
     240: invokeinterface #51,  2           // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
     245: checkcast     #37                 // class java/lang/String
     248: iconst_0
     249: invokevirtual #125                // Method java/lang/String.charAt:(I)C
     252: istore        12
     254: ldc           #127                // String 0
     256: astore        13
     258: new           #120                // class java/lang/StringBuilder
     261: dup
     262: invokespecial #121                // Method java/lang/StringBuilder."<init>":()V
     265: iload         12
     267: invokevirtual #131                // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
     270: aload         13
     272: invokevirtual #134                // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     275: invokevirtual #138                // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     278: invokevirtual #134                // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     281: iload         11
     283: invokevirtual #141                // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
     286: invokevirtual #138                // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     289: invokespecial #68                 // Method services/utilities/Course."<init>":(Ljava/lang/String;Ljava/lang/String;)V
     292: invokeinterface #74,  2           // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
     297: pop
     298: iinc          11, 1
     301: goto          206
     304: bipush        10
     306: istore        11
     308: iload         11
     310: bipush        100
     312: if_icmpge     97
     315: aload         4
     317: new           #61                 // class services/utilities/Course
     320: dup
     321: aload         10
     323: iconst_0
     324: invokeinterface #51,  2           // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
     329: checkcast     #37                 // class java/lang/String
     332: aload         10
     334: iconst_1
     335: invokeinterface #51,  2           // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
     340: checkcast     #37                 // class java/lang/String
     343: iconst_0
     344: invokevirtual #125                // Method java/lang/String.charAt:(I)C
     347: istore        12
     349: iload         11
     351: invokestatic  #65                 // Method java/lang/String.valueOf:(I)Ljava/lang/String;
     354: astore        13
     356: new           #120                // class java/lang/StringBuilder
     359: dup
     360: invokespecial #121                // Method java/lang/StringBuilder."<init>":()V
     363: iload         12
     365: invokevirtual #131                // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
     368: aload         13
     370: invokevirtual #134                // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     373: invokevirtual #138                // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     376: invokespecial #68                 // Method services/utilities/Course."<init>":(Ljava/lang/String;Ljava/lang/String;)V
     379: invokeinterface #74,  2           // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
     384: pop
     385: iinc          11, 1
     388: goto          308
     391: aload         4
     393: new           #61                 // class services/utilities/Course
     396: dup
     397: aload         10
     399: iconst_0
     400: invokeinterface #51,  2           // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
     405: checkcast     #37                 // class java/lang/String
     408: aload         10
     410: iconst_1
     411: invokeinterface #51,  2           // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
     416: checkcast     #37                 // class java/lang/String
     419: invokespecial #68                 // Method services/utilities/Course."<init>":(Ljava/lang/String;Ljava/lang/String;)V
     422: invokeinterface #74,  2           // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
     427: pop
     428: goto          97
     431: aload         6
     433: invokestatic  #59                 // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I
     436: aload         4
     438: invokeinterface #145,  1          // InterfaceMethod java/util/Set.size:()I
     443: if_icmpne     460
     446: aload_3
     447: aload         4
     449: checkcast     #147                // class java/util/Collection
     452: invokeinterface #151,  2          // InterfaceMethod java/util/Set.addAll:(Ljava/util/Collection;)Z
     457: goto          480
     460: aload_2
     461: new           #153                // class services/utilities/OptionalCourses
     464: dup
     465: aload         6
     467: invokestatic  #59                 // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I
     470: aload         4
     472: invokespecial #156                // Method services/utilities/OptionalCourses."<init>":(ILjava/util/Set;)V
     475: invokeinterface #74,  2           // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
     480: pop
     481: return

  public final services.utilities.Requirements initialReqParse(java.util.List<java.lang.String>);
    Code:
       0: aload_1
       1: ldc           #16                 // String requirementsData
       3: invokestatic  #22                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: new           #91                 // class java/util/LinkedHashSet
       9: dup
      10: invokespecial #92                 // Method java/util/LinkedHashSet."<init>":()V
      13: checkcast     #70                 // class java/util/Set
      16: astore_2
      17: new           #91                 // class java/util/LinkedHashSet
      20: dup
      21: invokespecial #92                 // Method java/util/LinkedHashSet."<init>":()V
      24: checkcast     #70                 // class java/util/Set
      27: astore_3
      28: aload_1
      29: invokeinterface #100,  1          // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
      34: astore        4
      36: aload         4
      38: invokeinterface #106,  1          // InterfaceMethod java/util/Iterator.hasNext:()Z
      43: ifeq          129
      46: aload         4
      48: invokeinterface #110,  1          // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      53: checkcast     #37                 // class java/lang/String
      56: astore        5
      58: aload         5
      60: checkcast     #35                 // class java/lang/CharSequence
      63: iconst_1
      64: anewarray     #37                 // class java/lang/String
      67: astore        7
      69: aload         7
      71: iconst_0
      72: ldc           #167                // String ;
      74: aastore
      75: aload         7
      77: iconst_0
      78: iconst_0
      79: bipush        6
      81: aconst_null
      82: invokestatic  #45                 // Method kotlin/text/StringsKt.split$default:(Ljava/lang/CharSequence;[Ljava/lang/String;ZIILjava/lang/Object;)Ljava/util/List;
      85: astore        6
      87: aload         6
      89: invokeinterface #100,  1          // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
      94: astore        7
      96: aload         7
      98: invokeinterface #106,  1          // InterfaceMethod java/util/Iterator.hasNext:()Z
     103: ifeq          36
     106: aload         7
     108: invokeinterface #110,  1          // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     113: checkcast     #37                 // class java/lang/String
     116: astore        8
     118: aload_0
     119: aload         8
     121: aload_2
     122: aload_3
     123: invokespecial #169                // Method parseSingleReqClause:(Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;)V
     126: goto          96
     129: new           #171                // class services/utilities/Requirements
     132: dup
     133: aload_2
     134: aload_3
     135: invokespecial #174                // Method services/utilities/Requirements."<init>":(Ljava/util/Set;Ljava/util/Set;)V
     138: areturn

  private final services.utilities.Requirements finalizeRequirements(services.utilities.Requirements);
    Code:
       0: aload_1
       1: invokevirtual #180                // Method services/utilities/Requirements.getOptionalCourses:()Ljava/util/Set;
       4: checkcast     #147                // class java/util/Collection
       7: invokestatic  #186                // Method kotlin/collections/CollectionsKt.toMutableList:(Ljava/util/Collection;)Ljava/util/List;
      10: astore_2
      11: aload_1
      12: invokevirtual #189                // Method services/utilities/Requirements.getMandatoryCourses:()Ljava/util/Set;
      15: checkcast     #147                // class java/util/Collection
      18: invokestatic  #186                // Method kotlin/collections/CollectionsKt.toMutableList:(Ljava/util/Collection;)Ljava/util/List;
      21: astore_3
      22: new           #191                // class java/util/concurrent/ConcurrentHashMap
      25: dup
      26: invokespecial #192                // Method java/util/concurrent/ConcurrentHashMap."<init>":()V
      29: astore        4
      31: iconst_0
      32: istore        5
      34: aload_2
      35: invokeinterface #193,  1          // InterfaceMethod java/util/List.size:()I
      40: istore        6
      42: iload         5
      44: iload         6
      46: if_icmpge     272
      49: iload         5
      51: iconst_1
      52: iadd
      53: istore        7
      55: aload_2
      56: invokeinterface #193,  1          // InterfaceMethod java/util/List.size:()I
      61: istore        8
      63: iload         7
      65: iload         8
      67: if_icmpge     266
      70: aload_2
      71: iload         5
      73: invokeinterface #51,  2           // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
      78: checkcast     #153                // class services/utilities/OptionalCourses
      81: astore        9
      83: aload_2
      84: iload         7
      86: invokeinterface #51,  2           // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
      91: checkcast     #153                // class services/utilities/OptionalCourses
      94: astore        10
      96: aload         10
      98: invokevirtual #196                // Method services/utilities/OptionalCourses.getCourses:()Ljava/util/Set;
     101: checkcast     #198                // class java/lang/Iterable
     104: astore        12
     106: iconst_0
     107: istore        13
     109: aload         12
     111: astore        14
     113: new           #200                // class java/util/ArrayList
     116: dup
     117: invokespecial #201                // Method java/util/ArrayList."<init>":()V
     120: checkcast     #147                // class java/util/Collection
     123: astore        15
     125: iconst_0
     126: istore        16
     128: aload         14
     130: invokeinterface #202,  1          // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
     135: astore        17
     137: aload         17
     139: invokeinterface #106,  1          // InterfaceMethod java/util/Iterator.hasNext:()Z
     144: ifeq          194
     147: aload         17
     149: invokeinterface #110,  1          // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     154: astore        18
     156: aload         18
     158: checkcast     #61                 // class services/utilities/Course
     161: astore        19
     163: iconst_0
     164: istore        20
     166: aload         9
     168: invokevirtual #196                // Method services/utilities/OptionalCourses.getCourses:()Ljava/util/Set;
     171: aload         19
     173: invokeinterface #205,  2          // InterfaceMethod java/util/Set.contains:(Ljava/lang/Object;)Z
     178: ifeq          137
     181: aload         15
     183: aload         18
     185: invokeinterface #206,  2          // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     190: pop
     191: goto          137
     194: aload         15
     196: checkcast     #47                 // class java/util/List
     199: nop
     200: checkcast     #198                // class java/lang/Iterable
     203: invokestatic  #210                // Method kotlin/collections/CollectionsKt.toSet:(Ljava/lang/Iterable;)Ljava/util/Set;
     206: astore        11
     208: aload         11
     210: invokeinterface #211,  1          // InterfaceMethod java/util/Set.iterator:()Ljava/util/Iterator;
     215: astore        12
     217: aload         12
     219: invokeinterface #106,  1          // InterfaceMethod java/util/Iterator.hasNext:()Z
     224: ifeq          260
     227: aload         12
     229: invokeinterface #110,  1          // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     234: checkcast     #61                 // class services/utilities/Course
     237: astore        13
     239: aload         4
     241: aload         13
     243: invokedynamic #230,  0            // InvokeDynamic #0:invoke:()Lkotlin/jvm/functions/Function2;
     248: invokedynamic #241,  0            // InvokeDynamic #1:apply:(Lkotlin/jvm/functions/Function2;)Ljava/util/function/BiFunction;
     253: invokevirtual #245                // Method java/util/concurrent/ConcurrentHashMap.compute:(Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object;
     256: pop
     257: goto          217
     260: iinc          7, 1
     263: goto          63
     266: iinc          5, 1
     269: goto          42
     272: new           #200                // class java/util/ArrayList
     275: dup
     276: invokespecial #201                // Method java/util/ArrayList."<init>":()V
     279: checkcast     #47                 // class java/util/List
     282: astore        5
     284: aload_2
     285: invokeinterface #100,  1          // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
     290: astore        6
     292: aload         6
     294: invokeinterface #106,  1          // InterfaceMethod java/util/Iterator.hasNext:()Z
     299: ifeq          765
     302: aload         6
     304: invokeinterface #110,  1          // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     309: checkcast     #153                // class services/utilities/OptionalCourses
     312: astore        7
     314: aconst_null
     315: astore        8
     317: aload         7
     319: invokevirtual #196                // Method services/utilities/OptionalCourses.getCourses:()Ljava/util/Set;
     322: checkcast     #198                // class java/lang/Iterable
     325: astore        9
     327: iconst_0
     328: istore        10
     330: aload         9
     332: astore        11
     334: new           #200                // class java/util/ArrayList
     337: dup
     338: invokespecial #201                // Method java/util/ArrayList."<init>":()V
     341: checkcast     #147                // class java/util/Collection
     344: astore        12
     346: iconst_0
     347: istore        13
     349: aload         11
     351: invokeinterface #202,  1          // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
     356: astore        14
     358: aload         14
     360: invokeinterface #106,  1          // InterfaceMethod java/util/Iterator.hasNext:()Z
     365: ifeq          416
     368: aload         14
     370: invokeinterface #110,  1          // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     375: astore        15
     377: aload         15
     379: checkcast     #61                 // class services/utilities/Course
     382: astore        16
     384: iconst_0
     385: istore        17
     387: aload         4
     389: invokevirtual #248                // Method java/util/concurrent/ConcurrentHashMap.keySet:()Ljava/util/Set;
     392: checkcast     #250                // class java/util/concurrent/ConcurrentHashMap$KeySetView
     395: aload         16
     397: invokevirtual #251                // Method java/util/concurrent/ConcurrentHashMap$KeySetView.contains:(Ljava/lang/Object;)Z
     400: ifeq          358
     403: aload         12
     405: aload         15
     407: invokeinterface #206,  2          // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     412: pop
     413: goto          358
     416: aload         12
     418: checkcast     #47                 // class java/util/List
     421: nop
     422: checkcast     #198                // class java/lang/Iterable
     425: invokestatic  #210                // Method kotlin/collections/CollectionsKt.toSet:(Ljava/lang/Iterable;)Ljava/util/Set;
     428: astore        8
     430: aload         8
     432: invokeinterface #145,  1          // InterfaceMethod java/util/Set.size:()I
     437: aload         7
     439: invokevirtual #254                // Method services/utilities/OptionalCourses.getNOf:()I
     442: if_icmpge     491
     445: aload         7
     447: aload         7
     449: invokevirtual #254                // Method services/utilities/OptionalCourses.getNOf:()I
     452: aload         8
     454: invokeinterface #145,  1          // InterfaceMethod java/util/Set.size:()I
     459: isub
     460: invokevirtual #258                // Method services/utilities/OptionalCourses.setNOf:(I)V
     463: aload         7
     465: invokevirtual #196                // Method services/utilities/OptionalCourses.getCourses:()Ljava/util/Set;
     468: aload         8
     470: checkcast     #147                // class java/util/Collection
     473: invokeinterface #261,  2          // InterfaceMethod java/util/Set.removeAll:(Ljava/util/Collection;)Z
     478: pop
     479: aload         5
     481: aload         7
     483: invokeinterface #262,  2          // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
     488: goto          761
     491: aload         4
     493: checkcast     #264                // class java/util/Map
     496: astore        9
     498: iconst_0
     499: istore        10
     501: new           #266                // class java/util/LinkedHashMap
     504: dup
     505: invokespecial #267                // Method java/util/LinkedHashMap."<init>":()V
     508: astore        11
     510: aload         9
     512: invokeinterface #270,  1          // InterfaceMethod java/util/Map.entrySet:()Ljava/util/Set;
     517: invokeinterface #211,  1          // InterfaceMethod java/util/Set.iterator:()Ljava/util/Iterator;
     522: astore        12
     524: aload         12
     526: invokeinterface #106,  1          // InterfaceMethod java/util/Iterator.hasNext:()Z
     531: ifeq          596
     534: aload         12
     536: invokeinterface #110,  1          // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     541: checkcast     #272                // class java/util/Map$Entry
     544: astore        13
     546: aload         13
     548: invokeinterface #275,  1          // InterfaceMethod java/util/Map$Entry.getKey:()Ljava/lang/Object;
     553: checkcast     #61                 // class services/utilities/Course
     556: astore        14
     558: iconst_0
     559: istore        15
     561: aload         8
     563: aload         14
     565: invokeinterface #205,  2          // InterfaceMethod java/util/Set.contains:(Ljava/lang/Object;)Z
     570: ifeq          524
     573: aload         11
     575: aload         13
     577: invokeinterface #275,  1          // InterfaceMethod java/util/Map$Entry.getKey:()Ljava/lang/Object;
     582: aload         13
     584: invokeinterface #278,  1          // InterfaceMethod java/util/Map$Entry.getValue:()Ljava/lang/Object;
     589: invokevirtual #281                // Method java/util/LinkedHashMap.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
     592: pop
     593: goto          524
     596: aload         11
     598: checkcast     #264                // class java/util/Map
     601: invokestatic  #287                // Method kotlin/collections/MapsKt.toList:(Ljava/util/Map;)Ljava/util/List;
     604: checkcast     #198                // class java/lang/Iterable
     607: astore        9
     609: nop
     610: iconst_0
     611: istore        10
     613: aload         9
     615: new           #289                // class services/utilities/RequirementsParser$finalizeRequirements$$inlined$sortedBy$1
     618: dup
     619: invokespecial #290                // Method services/utilities/RequirementsParser$finalizeRequirements$$inlined$sortedBy$1."<init>":()V
     622: checkcast     #292                // class java/util/Comparator
     625: invokestatic  #296                // Method kotlin/collections/CollectionsKt.sortedWith:(Ljava/lang/Iterable;Ljava/util/Comparator;)Ljava/util/List;
     628: checkcast     #198                // class java/lang/Iterable
     631: astore        9
     633: nop
     634: iconst_0
     635: istore        10
     637: aload         9
     639: astore        11
     641: new           #200                // class java/util/ArrayList
     644: dup
     645: aload         9
     647: bipush        10
     649: invokestatic  #300                // Method kotlin/collections/CollectionsKt.collectionSizeOrDefault:(Ljava/lang/Iterable;I)I
     652: invokespecial #302                // Method java/util/ArrayList."<init>":(I)V
     655: checkcast     #147                // class java/util/Collection
     658: astore        12
     660: iconst_0
     661: istore        13
     663: aload         11
     665: invokeinterface #202,  1          // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
     670: astore        14
     672: aload         14
     674: invokeinterface #106,  1          // InterfaceMethod java/util/Iterator.hasNext:()Z
     679: ifeq          725
     682: aload         14
     684: invokeinterface #110,  1          // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     689: astore        15
     691: aload         12
     693: aload         15
     695: checkcast     #304                // class kotlin/Pair
     698: astore        16
     700: astore        21
     702: iconst_0
     703: istore        17
     705: aload         16
     707: invokevirtual #307                // Method kotlin/Pair.getFirst:()Ljava/lang/Object;
     710: checkcast     #61                 // class services/utilities/Course
     713: aload         21
     715: swap
     716: invokeinterface #206,  2          // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     721: pop
     722: goto          672
     725: aload         12
     727: checkcast     #47                 // class java/util/List
     730: nop
     731: checkcast     #198                // class java/lang/Iterable
     734: aload         7
     736: invokevirtual #254                // Method services/utilities/OptionalCourses.getNOf:()I
     739: invokestatic  #311                // Method kotlin/collections/CollectionsKt.take:(Ljava/lang/Iterable;I)Ljava/util/List;
     742: checkcast     #198                // class java/lang/Iterable
     745: invokestatic  #210                // Method kotlin/collections/CollectionsKt.toSet:(Ljava/lang/Iterable;)Ljava/util/Set;
     748: astore        8
     750: aload_3
     751: aload         8
     753: checkcast     #147                // class java/util/Collection
     756: invokeinterface #312,  2          // InterfaceMethod java/util/List.addAll:(Ljava/util/Collection;)Z
     761: pop
     762: goto          292
     765: aload         5
     767: aload_3
     768: invokedynamic #324,  0            // InvokeDynamic #2:invoke:(Ljava/util/List;)Lkotlin/jvm/functions/Function1;
     773: invokedynamic #334,  0            // InvokeDynamic #3:test:(Lkotlin/jvm/functions/Function1;)Ljava/util/function/Predicate;
     778: invokeinterface #338,  2          // InterfaceMethod java/util/List.removeIf:(Ljava/util/function/Predicate;)Z
     783: pop
     784: aload_3
     785: iconst_3
     786: anewarray     #61                 // class services/utilities/Course
     789: astore        6
     791: aload         6
     793: iconst_0
     794: new           #61                 // class services/utilities/Course
     797: dup
     798: ldc_w         #340                // String ECON
     801: ldc_w         #342                // String 101
     804: invokespecial #68                 // Method services/utilities/Course."<init>":(Ljava/lang/String;Ljava/lang/String;)V
     807: aastore
     808: aload         6
     810: iconst_1
     811: new           #61                 // class services/utilities/Course
     814: dup
     815: ldc_w         #340                // String ECON
     818: ldc_w         #344                // String 102
     821: invokespecial #68                 // Method services/utilities/Course."<init>":(Ljava/lang/String;Ljava/lang/String;)V
     824: aastore
     825: aload         6
     827: iconst_2
     828: new           #61                 // class services/utilities/Course
     831: dup
     832: ldc_w         #340                // String ECON
     835: ldc_w         #346                // String 371
     838: invokespecial #68                 // Method services/utilities/Course."<init>":(Ljava/lang/String;Ljava/lang/String;)V
     841: aastore
     842: aload         6
     844: invokestatic  #350                // Method kotlin/collections/CollectionsKt.listOf:([Ljava/lang/Object;)Ljava/util/List;
     847: checkcast     #147                // class java/util/Collection
     850: invokeinterface #312,  2          // InterfaceMethod java/util/List.addAll:(Ljava/util/Collection;)Z
     855: pop
     856: aload_3
     857: iconst_3
     858: anewarray     #61                 // class services/utilities/Course
     861: astore        6
     863: aload         6
     865: iconst_0
     866: new           #61                 // class services/utilities/Course
     869: dup
     870: ldc_w         #352                // String MUSIC
     873: ldc_w         #354                // String 116
     876: invokespecial #68                 // Method services/utilities/Course."<init>":(Ljava/lang/String;Ljava/lang/String;)V
     879: aastore
     880: aload         6
     882: iconst_1
     883: new           #61                 // class services/utilities/Course
     886: dup
     887: ldc_w         #356                // String PHYS
     890: ldc_w         #358                // String 111
     893: invokespecial #68                 // Method services/utilities/Course."<init>":(Ljava/lang/String;Ljava/lang/String;)V
     896: aastore
     897: aload         6
     899: iconst_2
     900: new           #61                 // class services/utilities/Course
     903: dup
     904: ldc_w         #360                // String CHEM
     907: ldc_w         #344                // String 102
     910: invokespecial #68                 // Method services/utilities/Course."<init>":(Ljava/lang/String;Ljava/lang/String;)V
     913: aastore
     914: aload         6
     916: invokestatic  #350                // Method kotlin/collections/CollectionsKt.listOf:([Ljava/lang/Object;)Ljava/util/List;
     919: checkcast     #147                // class java/util/Collection
     922: invokeinterface #312,  2          // InterfaceMethod java/util/List.addAll:(Ljava/util/Collection;)Z
     927: pop
     928: new           #171                // class services/utilities/Requirements
     931: dup
     932: aload         5
     934: checkcast     #198                // class java/lang/Iterable
     937: invokestatic  #363                // Method kotlin/collections/CollectionsKt.toMutableSet:(Ljava/lang/Iterable;)Ljava/util/Set;
     940: aload_3
     941: checkcast     #198                // class java/lang/Iterable
     944: invokestatic  #363                // Method kotlin/collections/CollectionsKt.toMutableSet:(Ljava/lang/Iterable;)Ljava/util/Set;
     947: invokespecial #174                // Method services/utilities/Requirements."<init>":(Ljava/util/Set;Ljava/util/Set;)V
     950: areturn

  public final services.utilities.CommonRequirementsData combineOptionalRequirements(services.utilities.Requirements, java.util.List<java.lang.String>);
    Code:
       0: aload_1
       1: ldc_w         #408                // String requirements
       4: invokestatic  #22                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       7: aload_2
       8: ldc_w         #410                // String checkCourses
      11: invokestatic  #22                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
      14: iconst_0
      15: istore_3
      16: aload_2
      17: checkcast     #147                // class java/util/Collection
      20: invokeinterface #413,  1          // InterfaceMethod java/util/Collection.isEmpty:()Z
      25: ifne          32
      28: iconst_1
      29: goto          33
      32: iconst_0
      33: ifeq          264
      36: aload_1
      37: invokevirtual #180                // Method services/utilities/Requirements.getOptionalCourses:()Ljava/util/Set;
      40: invokeinterface #211,  1          // InterfaceMethod java/util/Set.iterator:()Ljava/util/Iterator;
      45: astore        4
      47: aload         4
      49: invokeinterface #106,  1          // InterfaceMethod java/util/Iterator.hasNext:()Z
      54: ifeq          264
      57: aload         4
      59: invokeinterface #110,  1          // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      64: checkcast     #153                // class services/utilities/OptionalCourses
      67: astore        5
      69: aload         5
      71: invokevirtual #196                // Method services/utilities/OptionalCourses.getCourses:()Ljava/util/Set;
      74: checkcast     #198                // class java/lang/Iterable
      77: astore        7
      79: iconst_0
      80: istore        8
      82: aload         7
      84: astore        9
      86: new           #200                // class java/util/ArrayList
      89: dup
      90: invokespecial #201                // Method java/util/ArrayList."<init>":()V
      93: checkcast     #147                // class java/util/Collection
      96: astore        10
      98: iconst_0
      99: istore        11
     101: aload         9
     103: invokeinterface #202,  1          // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
     108: astore        12
     110: aload         12
     112: invokeinterface #106,  1          // InterfaceMethod java/util/Iterator.hasNext:()Z
     117: ifeq          192
     120: aload         12
     122: invokeinterface #110,  1          // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     127: astore        13
     129: aload         13
     131: checkcast     #61                 // class services/utilities/Course
     134: astore        14
     136: iconst_0
     137: istore        15
     139: aload_2
     140: new           #120                // class java/lang/StringBuilder
     143: dup
     144: invokespecial #121                // Method java/lang/StringBuilder."<init>":()V
     147: aload         14
     149: invokevirtual #416                // Method services/utilities/Course.getSubject:()Ljava/lang/String;
     152: invokevirtual #134                // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     155: bipush        32
     157: invokevirtual #131                // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
     160: aload         14
     162: invokevirtual #419                // Method services/utilities/Course.getCode:()Ljava/lang/String;
     165: invokevirtual #134                // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     168: invokevirtual #138                // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     171: invokeinterface #420,  2          // InterfaceMethod java/util/List.contains:(Ljava/lang/Object;)Z
     176: ifeq          110
     179: aload         10
     181: aload         13
     183: invokeinterface #206,  2          // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     188: pop
     189: goto          110
     192: aload         10
     194: checkcast     #47                 // class java/util/List
     197: nop
     198: astore        6
     200: aload         6
     202: invokeinterface #193,  1          // InterfaceMethod java/util/List.size:()I
     207: istore_3
     208: iload_3
     209: aload         5
     211: invokevirtual #254                // Method services/utilities/OptionalCourses.getNOf:()I
     214: if_icmplt     227
     217: aload         4
     219: invokeinterface #423,  1          // InterfaceMethod java/util/Iterator.remove:()V
     224: goto          47
     227: aload         5
     229: aload         5
     231: invokevirtual #254                // Method services/utilities/OptionalCourses.getNOf:()I
     234: iload_3
     235: isub
     236: invokevirtual #258                // Method services/utilities/OptionalCourses.setNOf:(I)V
     239: aload         5
     241: invokevirtual #196                // Method services/utilities/OptionalCourses.getCourses:()Ljava/util/Set;
     244: aload         6
     246: checkcast     #198                // class java/lang/Iterable
     249: invokestatic  #210                // Method kotlin/collections/CollectionsKt.toSet:(Ljava/lang/Iterable;)Ljava/util/Set;
     252: checkcast     #147                // class java/util/Collection
     255: invokeinterface #261,  2          // InterfaceMethod java/util/Set.removeAll:(Ljava/util/Collection;)Z
     260: pop
     261: goto          47
     264: new           #425                // class services/utilities/CommonRequirementsData
     267: dup
     268: aload_1
     269: iload_3
     270: invokespecial #428                // Method services/utilities/CommonRequirementsData."<init>":(Lservices/utilities/Requirements;I)V
     273: areturn

  private static final java.lang.Integer finalizeRequirements$lambda$1(services.utilities.Course, java.lang.Integer);
    Code:
       0: aload_0
       1: ldc_w         #434                // String <unused var>
       4: invokestatic  #22                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       7: aload_1
       8: dup
       9: ifnull        20
      12: invokevirtual #437                // Method java/lang/Integer.intValue:()I
      15: iconst_1
      16: iadd
      17: goto          22
      20: pop
      21: iconst_1
      22: invokestatic  #440                // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      25: areturn

  private static final java.lang.Integer finalizeRequirements$lambda$2(kotlin.jvm.functions.Function2, java.lang.Object, java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: aload_2
       3: invokeinterface #446,  3          // InterfaceMethod kotlin/jvm/functions/Function2.invoke:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
       8: checkcast     #55                 // class java/lang/Integer
      11: areturn

  private static final boolean finalizeRequirements$lambda$7(java.util.List, services.utilities.OptionalCourses);
    Code:
       0: aload_1
       1: ldc_w         #451                // String optionalCourses
       4: invokestatic  #22                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       7: aload_0
       8: aload_1
       9: invokevirtual #196                // Method services/utilities/OptionalCourses.getCourses:()Ljava/util/Set;
      12: checkcast     #147                // class java/util/Collection
      15: invokeinterface #454,  2          // InterfaceMethod java/util/List.containsAll:(Ljava/util/Collection;)Z
      20: ireturn

  private static final boolean finalizeRequirements$lambda$8(kotlin.jvm.functions.Function1, java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: invokeinterface #459,  2          // InterfaceMethod kotlin/jvm/functions/Function1.invoke:(Ljava/lang/Object;)Ljava/lang/Object;
       7: checkcast     #461                // class java/lang/Boolean
      10: invokevirtual #464                // Method java/lang/Boolean.booleanValue:()Z
      13: ireturn
}


package com.github.d_costa.acacia

data class BNode<T>(
    val value: T,
    val left: BNode<T>? = null,
    val right: BNode<T>? = null
)

/**
 * A Binary Tree
 *
 * Supports insertion, deletion, and iteration
 */
class BinaryTree<T : Comparable<T>>: Iterable<T> {

    var root: BNode<T>? = null

    fun insert(value: T) {
        root = internalInsert(root, value)
    }

    fun exists(value: T): Boolean = findNode(root, value) != null

    fun find(value: T): BNode<T>? = findNode(root, value)

    fun delete(value: T) {
        root = deleteNode(root, value)
    }

    override fun iterator(): Iterator<T> = BinaryTreeIterator(root)
}

class BinaryTreeIterator<T : Comparable<T>>(private val bt: BNode<T>?) : Iterator<T> {

    private val queue = ArrayDeque<BNode<T>>()

    init {
        var current = bt
        while(current != null) {
            queue.add(current)
            current = current.left
        }
    }

    override fun hasNext(): Boolean {
        return queue.isNotEmpty()
    }

    override fun next(): T {
        if (!hasNext()) throw RuntimeException()

        val current = queue.removeLast()

        var next = current.right
        if(next != null) {
            queue.add(next)
            next = next.left
            while(next != null) {
                queue.add(next)
                next = next.left
            }
        }

        return current.value
    }

}

private fun <T : Comparable<T>> internalInsert(tree: BNode<T>?, value: T): BNode<T> {
    return if (tree == null) {
        BNode(value)
    } else if (tree.value > value) {
        BNode(tree.value, internalInsert(tree.left, value), tree.right)
    } else if (tree.value < value) {
        BNode(tree.value, tree.left, internalInsert(tree.right, value))
    } else {
        BNode(tree.value, tree.left, tree.right)
    }
}

private tailrec fun <T : Comparable<T>> findNode(tree: BNode<T>?, value: T): BNode<T>? {
    if (tree == null) {
       return null
    }

    return if (tree.value > value) {
        findNode(tree.left, value)
    } else if (tree.value < value) {
        findNode(tree.right, value)
    } else {
        tree
    }
}

private fun <T: Comparable<T>> deleteNode(tree: BNode<T>?, value: T): BNode<T>? {
    if (tree == null) {
        return tree
    }
    var newValue = tree.value
    var leftSubtree = tree.left
    var rightSubtree = tree.right

    if (tree.value > value) {
        leftSubtree = deleteNode(tree.left, value)
    } else if (tree.value < value) {
        rightSubtree = deleteNode(tree.right, value)
    } else {
        // Found it!
        if (tree.left == null && tree.right == null) return null

        if (tree.right != null) {
            newValue = findSmallest(tree.right)
            rightSubtree = deleteNode(tree.right, newValue)
        } else if (tree.left != null) {
            newValue = findLargest(tree.left)
            leftSubtree = deleteNode(tree.left, newValue)
        }
    }
    return BNode(newValue, leftSubtree, rightSubtree)
}
private tailrec fun <T: Comparable<T>> findLargest(tree: BNode<T>): T {
    return if (tree.right != null) {
        findLargest(tree.right)
    } else {
        tree.value
    }
}

private tailrec fun <T: Comparable<T>> findSmallest(tree: BNode<T>): T {
    return if (tree.left != null) {
        findSmallest(tree.left)
    } else {
        tree.value
    }
}

Compiled from "BinaryTree.kt"
public final class com.github.d_costa.acacia.BinaryTree<T extends java.lang.Comparable<? super T>> implements java.lang.Iterable<T>, kotlin.jvm.internal.markers.KMappedMarker {
  private com.github.d_costa.acacia.BNode<T> root;

  public com.github.d_costa.acacia.BinaryTree();
    Code:
       0: aload_0
       1: invokespecial #13                 // Method java/lang/Object."<init>":()V
       4: return

  public final com.github.d_costa.acacia.BNode<T> getRoot();
    Code:
       0: aload_0
       1: getfield      #23                 // Field root:Lcom/github/d_costa/acacia/BNode;
       4: areturn

  public final void setRoot(com.github.d_costa.acacia.BNode<T>);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #23                 // Field root:Lcom/github/d_costa/acacia/BNode;
       5: return

  public final void insert(T);
    Code:
       0: aload_1
       1: ldc           #33                 // String value
       3: invokestatic  #39                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: aload_0
       8: getfield      #23                 // Field root:Lcom/github/d_costa/acacia/BNode;
      11: aload_1
      12: invokestatic  #45                 // Method com/github/d_costa/acacia/BinaryTreeKt.access$internalInsert:(Lcom/github/d_costa/acacia/BNode;Ljava/lang/Comparable;)Lcom/github/d_costa/acacia/BNode;
      15: putfield      #23                 // Field root:Lcom/github/d_costa/acacia/BNode;
      18: return

  public final boolean exists(T);
    Code:
       0: aload_1
       1: ldc           #33                 // String value
       3: invokestatic  #39                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: getfield      #23                 // Field root:Lcom/github/d_costa/acacia/BNode;
      10: aload_1
      11: invokestatic  #52                 // Method com/github/d_costa/acacia/BinaryTreeKt.access$findNode:(Lcom/github/d_costa/acacia/BNode;Ljava/lang/Comparable;)Lcom/github/d_costa/acacia/BNode;
      14: ifnull        21
      17: iconst_1
      18: goto          22
      21: iconst_0
      22: ireturn

  public final com.github.d_costa.acacia.BNode<T> find(T);
    Code:
       0: aload_1
       1: ldc           #33                 // String value
       3: invokestatic  #39                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: getfield      #23                 // Field root:Lcom/github/d_costa/acacia/BNode;
      10: aload_1
      11: invokestatic  #52                 // Method com/github/d_costa/acacia/BinaryTreeKt.access$findNode:(Lcom/github/d_costa/acacia/BNode;Ljava/lang/Comparable;)Lcom/github/d_costa/acacia/BNode;
      14: areturn

  public final void delete(T);
    Code:
       0: aload_1
       1: ldc           #33                 // String value
       3: invokestatic  #39                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: aload_0
       8: getfield      #23                 // Field root:Lcom/github/d_costa/acacia/BNode;
      11: aload_1
      12: invokestatic  #59                 // Method com/github/d_costa/acacia/BinaryTreeKt.access$deleteNode:(Lcom/github/d_costa/acacia/BNode;Ljava/lang/Comparable;)Lcom/github/d_costa/acacia/BNode;
      15: putfield      #23                 // Field root:Lcom/github/d_costa/acacia/BNode;
      18: return

  public java.util.Iterator<T> iterator();
    Code:
       0: new           #64                 // class com/github/d_costa/acacia/BinaryTreeIterator
       3: dup
       4: aload_0
       5: getfield      #23                 // Field root:Lcom/github/d_costa/acacia/BNode;
       8: invokespecial #66                 // Method com/github/d_costa/acacia/BinaryTreeIterator."<init>":(Lcom/github/d_costa/acacia/BNode;)V
      11: checkcast     #68                 // class java/util/Iterator
      14: areturn
}

Compiled from "BinaryTree.kt"
public final class com.github.d_costa.acacia.BinaryTreeKt {
  private static final <T extends java.lang.Comparable<? super T>> com.github.d_costa.acacia.BNode<T> internalInsert(com.github.d_costa.acacia.BNode<T>, T);
    Code:
       0: aload_0
       1: ifnonnull     20
       4: new           #9                  // class com/github/d_costa/acacia/BNode
       7: dup
       8: aload_1
       9: aconst_null
      10: aconst_null
      11: bipush        6
      13: aconst_null
      14: invokespecial #13                 // Method com/github/d_costa/acacia/BNode."<init>":(Ljava/lang/Object;Lcom/github/d_costa/acacia/BNode;Lcom/github/d_costa/acacia/BNode;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
      17: goto          123
      20: aload_0
      21: invokevirtual #17                 // Method com/github/d_costa/acacia/BNode.getValue:()Ljava/lang/Object;
      24: checkcast     #19                 // class java/lang/Comparable
      27: aload_1
      28: invokeinterface #23,  2           // InterfaceMethod java/lang/Comparable.compareTo:(Ljava/lang/Object;)I
      33: ifle          62
      36: new           #9                  // class com/github/d_costa/acacia/BNode
      39: dup
      40: aload_0
      41: invokevirtual #17                 // Method com/github/d_costa/acacia/BNode.getValue:()Ljava/lang/Object;
      44: aload_0
      45: invokevirtual #27                 // Method com/github/d_costa/acacia/BNode.getLeft:()Lcom/github/d_costa/acacia/BNode;
      48: aload_1
      49: invokestatic  #29                 // Method internalInsert:(Lcom/github/d_costa/acacia/BNode;Ljava/lang/Comparable;)Lcom/github/d_costa/acacia/BNode;
      52: aload_0
      53: invokevirtual #32                 // Method com/github/d_costa/acacia/BNode.getRight:()Lcom/github/d_costa/acacia/BNode;
      56: invokespecial #35                 // Method com/github/d_costa/acacia/BNode."<init>":(Ljava/lang/Object;Lcom/github/d_costa/acacia/BNode;Lcom/github/d_costa/acacia/BNode;)V
      59: goto          123
      62: aload_0
      63: invokevirtual #17                 // Method com/github/d_costa/acacia/BNode.getValue:()Ljava/lang/Object;
      66: checkcast     #19                 // class java/lang/Comparable
      69: aload_1
      70: invokeinterface #23,  2           // InterfaceMethod java/lang/Comparable.compareTo:(Ljava/lang/Object;)I
      75: ifge          104
      78: new           #9                  // class com/github/d_costa/acacia/BNode
      81: dup
      82: aload_0
      83: invokevirtual #17                 // Method com/github/d_costa/acacia/BNode.getValue:()Ljava/lang/Object;
      86: aload_0
      87: invokevirtual #27                 // Method com/github/d_costa/acacia/BNode.getLeft:()Lcom/github/d_costa/acacia/BNode;
      90: aload_0
      91: invokevirtual #32                 // Method com/github/d_costa/acacia/BNode.getRight:()Lcom/github/d_costa/acacia/BNode;
      94: aload_1
      95: invokestatic  #29                 // Method internalInsert:(Lcom/github/d_costa/acacia/BNode;Ljava/lang/Comparable;)Lcom/github/d_costa/acacia/BNode;
      98: invokespecial #35                 // Method com/github/d_costa/acacia/BNode."<init>":(Ljava/lang/Object;Lcom/github/d_costa/acacia/BNode;Lcom/github/d_costa/acacia/BNode;)V
     101: goto          123
     104: new           #9                  // class com/github/d_costa/acacia/BNode
     107: dup
     108: aload_0
     109: invokevirtual #17                 // Method com/github/d_costa/acacia/BNode.getValue:()Ljava/lang/Object;
     112: aload_0
     113: invokevirtual #27                 // Method com/github/d_costa/acacia/BNode.getLeft:()Lcom/github/d_costa/acacia/BNode;
     116: aload_0
     117: invokevirtual #32                 // Method com/github/d_costa/acacia/BNode.getRight:()Lcom/github/d_costa/acacia/BNode;
     120: invokespecial #35                 // Method com/github/d_costa/acacia/BNode."<init>":(Ljava/lang/Object;Lcom/github/d_costa/acacia/BNode;Lcom/github/d_costa/acacia/BNode;)V
     123: areturn

  private static final <T extends java.lang.Comparable<? super T>> com.github.d_costa.acacia.BNode<T> findNode(com.github.d_costa.acacia.BNode<T>, T);
    Code:
       0: aload_0
       1: ifnonnull     6
       4: aconst_null
       5: areturn
       6: aload_0
       7: invokevirtual #17                 // Method com/github/d_costa/acacia/BNode.getValue:()Ljava/lang/Object;
      10: checkcast     #19                 // class java/lang/Comparable
      13: aload_1
      14: invokeinterface #23,  2           // InterfaceMethod java/lang/Comparable.compareTo:(Ljava/lang/Object;)I
      19: ifle          36
      22: aload_0
      23: invokevirtual #27                 // Method com/github/d_costa/acacia/BNode.getLeft:()Lcom/github/d_costa/acacia/BNode;
      26: astore_2
      27: aload_1
      28: astore_3
      29: aload_2
      30: astore_0
      31: aload_3
      32: astore_1
      33: goto          0
      36: aload_0
      37: invokevirtual #17                 // Method com/github/d_costa/acacia/BNode.getValue:()Ljava/lang/Object;
      40: checkcast     #19                 // class java/lang/Comparable
      43: aload_1
      44: invokeinterface #23,  2           // InterfaceMethod java/lang/Comparable.compareTo:(Ljava/lang/Object;)I
      49: ifge          66
      52: aload_0
      53: invokevirtual #32                 // Method com/github/d_costa/acacia/BNode.getRight:()Lcom/github/d_costa/acacia/BNode;
      56: astore_2
      57: aload_1
      58: astore_3
      59: aload_2
      60: astore_0
      61: aload_3
      62: astore_1
      63: goto          0
      66: aload_0
      67: areturn

  private static final <T extends java.lang.Comparable<? super T>> com.github.d_costa.acacia.BNode<T> deleteNode(com.github.d_costa.acacia.BNode<T>, T);
    Code:
       0: aload_0
       1: ifnonnull     6
       4: aload_0
       5: areturn
       6: aload_0
       7: invokevirtual #17                 // Method com/github/d_costa/acacia/BNode.getValue:()Ljava/lang/Object;
      10: checkcast     #19                 // class java/lang/Comparable
      13: astore_2
      14: aload_0
      15: invokevirtual #27                 // Method com/github/d_costa/acacia/BNode.getLeft:()Lcom/github/d_costa/acacia/BNode;
      18: astore_3
      19: aload_0
      20: invokevirtual #32                 // Method com/github/d_costa/acacia/BNode.getRight:()Lcom/github/d_costa/acacia/BNode;
      23: astore        4
      25: aload_0
      26: invokevirtual #17                 // Method com/github/d_costa/acacia/BNode.getValue:()Ljava/lang/Object;
      29: checkcast     #19                 // class java/lang/Comparable
      32: aload_1
      33: invokeinterface #23,  2           // InterfaceMethod java/lang/Comparable.compareTo:(Ljava/lang/Object;)I
      38: ifle          53
      41: aload_0
      42: invokevirtual #27                 // Method com/github/d_costa/acacia/BNode.getLeft:()Lcom/github/d_costa/acacia/BNode;
      45: aload_1
      46: invokestatic  #43                 // Method deleteNode:(Lcom/github/d_costa/acacia/BNode;Ljava/lang/Comparable;)Lcom/github/d_costa/acacia/BNode;
      49: astore_3
      50: goto          150
      53: aload_0
      54: invokevirtual #17                 // Method com/github/d_costa/acacia/BNode.getValue:()Ljava/lang/Object;
      57: checkcast     #19                 // class java/lang/Comparable
      60: aload_1
      61: invokeinterface #23,  2           // InterfaceMethod java/lang/Comparable.compareTo:(Ljava/lang/Object;)I
      66: ifge          82
      69: aload_0
      70: invokevirtual #32                 // Method com/github/d_costa/acacia/BNode.getRight:()Lcom/github/d_costa/acacia/BNode;
      73: aload_1
      74: invokestatic  #43                 // Method deleteNode:(Lcom/github/d_costa/acacia/BNode;Ljava/lang/Comparable;)Lcom/github/d_costa/acacia/BNode;
      77: astore        4
      79: goto          150
      82: aload_0
      83: invokevirtual #27                 // Method com/github/d_costa/acacia/BNode.getLeft:()Lcom/github/d_costa/acacia/BNode;
      86: ifnonnull     98
      89: aload_0
      90: invokevirtual #32                 // Method com/github/d_costa/acacia/BNode.getRight:()Lcom/github/d_costa/acacia/BNode;
      93: ifnonnull     98
      96: aconst_null
      97: areturn
      98: aload_0
      99: invokevirtual #32                 // Method com/github/d_costa/acacia/BNode.getRight:()Lcom/github/d_costa/acacia/BNode;
     102: ifnull        126
     105: aload_0
     106: invokevirtual #32                 // Method com/github/d_costa/acacia/BNode.getRight:()Lcom/github/d_costa/acacia/BNode;
     109: invokestatic  #47                 // Method findSmallest:(Lcom/github/d_costa/acacia/BNode;)Ljava/lang/Comparable;
     112: astore_2
     113: aload_0
     114: invokevirtual #32                 // Method com/github/d_costa/acacia/BNode.getRight:()Lcom/github/d_costa/acacia/BNode;
     117: aload_2
     118: invokestatic  #43                 // Method deleteNode:(Lcom/github/d_costa/acacia/BNode;Ljava/lang/Comparable;)Lcom/github/d_costa/acacia/BNode;
     121: astore        4
     123: goto          150
     126: aload_0
     127: invokevirtual #27                 // Method com/github/d_costa/acacia/BNode.getLeft:()Lcom/github/d_costa/acacia/BNode;
     130: ifnull        150
     133: aload_0
     134: invokevirtual #27                 // Method com/github/d_costa/acacia/BNode.getLeft:()Lcom/github/d_costa/acacia/BNode;
     137: invokestatic  #50                 // Method findLargest:(Lcom/github/d_costa/acacia/BNode;)Ljava/lang/Comparable;
     140: astore_2
     141: aload_0
     142: invokevirtual #27                 // Method com/github/d_costa/acacia/BNode.getLeft:()Lcom/github/d_costa/acacia/BNode;
     145: aload_2
     146: invokestatic  #43                 // Method deleteNode:(Lcom/github/d_costa/acacia/BNode;Ljava/lang/Comparable;)Lcom/github/d_costa/acacia/BNode;
     149: astore_3
     150: new           #9                  // class com/github/d_costa/acacia/BNode
     153: dup
     154: aload_2
     155: aload_3
     156: aload         4
     158: invokespecial #35                 // Method com/github/d_costa/acacia/BNode."<init>":(Ljava/lang/Object;Lcom/github/d_costa/acacia/BNode;Lcom/github/d_costa/acacia/BNode;)V
     161: areturn

  private static final <T extends java.lang.Comparable<? super T>> T findLargest(com.github.d_costa.acacia.BNode<T>);
    Code:
       0: aload_0
       1: invokevirtual #32                 // Method com/github/d_costa/acacia/BNode.getRight:()Lcom/github/d_costa/acacia/BNode;
       4: ifnull        15
       7: aload_0
       8: invokevirtual #32                 // Method com/github/d_costa/acacia/BNode.getRight:()Lcom/github/d_costa/acacia/BNode;
      11: astore_0
      12: goto          0
      15: aload_0
      16: invokevirtual #17                 // Method com/github/d_costa/acacia/BNode.getValue:()Ljava/lang/Object;
      19: checkcast     #19                 // class java/lang/Comparable
      22: areturn

  private static final <T extends java.lang.Comparable<? super T>> T findSmallest(com.github.d_costa.acacia.BNode<T>);
    Code:
       0: aload_0
       1: invokevirtual #27                 // Method com/github/d_costa/acacia/BNode.getLeft:()Lcom/github/d_costa/acacia/BNode;
       4: ifnull        15
       7: aload_0
       8: invokevirtual #27                 // Method com/github/d_costa/acacia/BNode.getLeft:()Lcom/github/d_costa/acacia/BNode;
      11: astore_0
      12: goto          0
      15: aload_0
      16: invokevirtual #17                 // Method com/github/d_costa/acacia/BNode.getValue:()Ljava/lang/Object;
      19: checkcast     #19                 // class java/lang/Comparable
      22: areturn

  public static final com.github.d_costa.acacia.BNode access$internalInsert(com.github.d_costa.acacia.BNode, java.lang.Comparable);
    Code:
       0: aload_0
       1: aload_1
       2: invokestatic  #29                 // Method internalInsert:(Lcom/github/d_costa/acacia/BNode;Ljava/lang/Comparable;)Lcom/github/d_costa/acacia/BNode;
       5: areturn

  public static final com.github.d_costa.acacia.BNode access$findNode(com.github.d_costa.acacia.BNode, java.lang.Comparable);
    Code:
       0: aload_0
       1: aload_1
       2: invokestatic  #58                 // Method findNode:(Lcom/github/d_costa/acacia/BNode;Ljava/lang/Comparable;)Lcom/github/d_costa/acacia/BNode;
       5: areturn

  public static final com.github.d_costa.acacia.BNode access$deleteNode(com.github.d_costa.acacia.BNode, java.lang.Comparable);
    Code:
       0: aload_0
       1: aload_1
       2: invokestatic  #43                 // Method deleteNode:(Lcom/github/d_costa/acacia/BNode;Ljava/lang/Comparable;)Lcom/github/d_costa/acacia/BNode;
       5: areturn
}