package com.nickperov.stud.combinatorics.experiment

import com.nickperov.stud.combinatorics.CombinatorialUtils
import java.util.*

/**
 * Test sample to try different approaches to calculate permutations
 */
object PermutationsCalculatorExperiment {
    /**
     * Provides all permutations of the elements in the given array.
     * recursive implementation, uses array invariant of pre-calculated size
     * returns array of arrays
     */
    @JvmStatic
    fun <T> getAllPermutationsV1(elements: Array<T>): Array<List<T>> {
        if (elements.isEmpty()) {
            return arrayOf()
        }

        val result = CombinatorialUtils.CombinationsInvariant(Array(CombinatorialUtils.calculateNumberOfPermutationsInt(elements.size)) { listOf<T>() }, 0)
        val used = BooleanArray(elements.size)

        collectAllPermutations(result, used, elements, arrayListOf())

        return result.value
    }

    private fun <T> collectAllPermutations(result: CombinatorialUtils.CombinationsInvariant<T>, used: BooleanArray, elements: Array<T>, selection: MutableList<T>) {
        if (selection.size == elements.size) {
            result.value[result.index] = selection.toList()
            result.index += 1
        } else {
            for (i in used.indices) {
                if (!used[i]) {
                    val usedCopy = used.clone()
                    usedCopy[i] = true
                    val selectionCopy = selection.toMutableList()
                    selectionCopy.add(elements[i])
                    collectAllPermutations(result, usedCopy, elements, selectionCopy)
                }
            }
        }
    }

    /**
     * Provides all permutations of the elements in the given array.
     * recursive implementation, uses list invariant
     * returns array of arrays
     *
     * Default one - look into utils class
     */
    @JvmStatic
    fun <T> getAllPermutationsV2(elements: Array<T>): Array<List<T>> {
        return CombinatorialUtils.getAllPermutations(elements)
    }

    /**
     * Provides all permutations of the elements in the given array.
     * recursive implementation, optimized (reduced number of array copy operations), uses array invariant of pre-calculated size
     * returns array of arrays
     */
    @JvmStatic
    fun <T> getAllPermutationsV3(elements: Array<T>): Array<List<T>> {
        if (elements.isEmpty()) {
            return arrayOf()
        }

        val result = CombinatorialUtils.CombinationsInvariant(Array(CombinatorialUtils.calculateNumberOfPermutationsInt(elements.size)) { listOf<T>() }, 0)
        val used = BooleanArray(elements.size)

        collectAllPermutations(result, used, elements, arrayListOf(), used.size)

        return result.value
    }

    private fun <T> collectAllPermutations(result: CombinatorialUtils.CombinationsInvariant<T>, used: BooleanArray, elements: Array<T>, selection: MutableList<T>, count: Int) {
        if (selection.size == elements.size) {
            result.value[result.index] = selection.toList()
            result.index += 1
        } else {
            var k = 1
            for (i in used.indices) {
                if (!used[i]) {
                    if (k++ == count) {
                        // Do not copy arrays
                        used[i] = true
                        selection.add(elements[i])
                        collectAllPermutations(result, used, elements, selection, count - 1)
                    } else {
                        val usedCopy = used.clone()
                        usedCopy[i] = true
                        val selectionCopy = selection.toMutableList()
                        selectionCopy.add(elements[i])
                        collectAllPermutations(result, usedCopy, elements, selectionCopy, count - 1)
                    }
                }
            }
        }
    }

    /**
     * Provides all permutations of the elements in the given array.
     * non-recursive implementation, optimized (reduced number of array copy operations), uses array of pre-calculated size
     * returns array of arrays
     */
    @JvmStatic
    fun <T> getAllPermutationsV4(elements: Array<T>): Array<List<T>> {
        if (elements.isEmpty()) {
            return arrayOf()
        }

        val result = CombinatorialUtils.CombinationsInvariant(Array(CombinatorialUtils.calculateNumberOfPermutationsInt(elements.size)) { listOf<T>() }, 0)

        val stack: Deque<RecursiveState<T>> = ArrayDeque(elements.size)
        var state: RecursiveState<T>? = RecursiveState(BooleanArray(elements.size), arrayListOf(), elements.size, 0, 1)
        stack.push(state)

        MAIN_LOOP@
        while (true) {
            if (state == null) {
                break
            } else {
                val used = state.used
                val selection = state.selection
                val count = state.count

                var k = state.currCount
                var i = state.index

                while (i < elements.size) {
                    if (!used[i]) {
                        if (k++ == count) {
                            // Do not copy arrays
                            used[i] = true
                            selection.add(elements[i])

                            // Update state index
                            state?.index = i + 1
                            state?.currCount = k

                            if (selection.size == elements.size) {
                                // Save the result
                                result.value[result.index] = selection.toList()
                                result.index += 1
                                break
                            }

                            state = RecursiveState(used, selection, count - 1, 0, 1)
                            stack.push(state)
                            continue@MAIN_LOOP
                        } else {
                            val usedCopy = used.clone()
                            usedCopy[i] = true
                            val selectionCopy = selection.toMutableList()
                            selectionCopy.add(elements[i])

                            // Update state index and count
                            state?.index = i + 1
                            state?.currCount = k

                            state = RecursiveState(usedCopy, selectionCopy, count - 1, 0, 1)
                            stack.push(state)
                            continue@MAIN_LOOP
                        }
                    }

                    i++
                    // Update state index and count
                    state?.index = i
                    state?.currCount = k
                }
                stack.remove()
                state = stack.peek()
            }
        }

        return result.value
    }

    data class RecursiveState<T>(val used: BooleanArray, val selection: MutableList<T>, val count: Int, var index: Int, var currCount: Int) {

        override fun equals(other: Any?): Boolean {
            if (this === other) return true
            if (javaClass != other?.javaClass) return false

            other as RecursiveState<*>

            if (!used.contentEquals(other.used)) return false
            if (selection != other.selection) return false
            if (count != other.count) return false
            if (index != other.index) return false
            if (currCount != other.currCount) return false

            return true
        }

        override fun hashCode(): Int {
            var result = used.contentHashCode()
            result = 31 * result + selection.hashCode()
            result = 31 * result + count
            result = 31 * result + index
            result = 31 * result + currCount
            return result
        }
    }
}

Compiled from "PermutationsCalculatorExperiment.kt"
public final class com.nickperov.stud.combinatorics.experiment.PermutationsCalculatorExperiment {
  public static final com.nickperov.stud.combinatorics.experiment.PermutationsCalculatorExperiment INSTANCE;

  private com.nickperov.stud.combinatorics.experiment.PermutationsCalculatorExperiment();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public static final <T> java.util.List<T>[] getAllPermutationsV1(T[]);
    Code:
       0: aload_0
       1: ldc           #17                 // String elements
       3: invokestatic  #23                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: arraylength
       8: ifne          15
      11: iconst_1
      12: goto          16
      15: iconst_0
      16: ifeq          24
      19: iconst_0
      20: anewarray     #25                 // class java/util/List
      23: areturn
      24: iconst_0
      25: istore_2
      26: getstatic     #31                 // Field com/nickperov/stud/combinatorics/CombinatorialUtils.INSTANCE:Lcom/nickperov/stud/combinatorics/CombinatorialUtils;
      29: aload_0
      30: arraylength
      31: invokevirtual #35                 // Method com/nickperov/stud/combinatorics/CombinatorialUtils.calculateNumberOfPermutationsInt:(I)I
      34: istore_3
      35: iload_3
      36: anewarray     #25                 // class java/util/List
      39: astore        4
      41: iload_2
      42: iload_3
      43: if_icmpge     63
      46: iload_2
      47: istore        5
      49: aload         4
      51: iload         5
      53: invokestatic  #41                 // Method kotlin/collections/CollectionsKt.emptyList:()Ljava/util/List;
      56: aastore
      57: iinc          2, 1
      60: goto          41
      63: aload         4
      65: iconst_0
      66: istore        6
      68: astore        7
      70: new           #43                 // class com/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant
      73: dup
      74: aload         7
      76: iload         6
      78: invokespecial #46                 // Method com/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant."<init>":([Ljava/util/List;I)V
      81: astore_1
      82: aload_0
      83: arraylength
      84: newarray       boolean
      86: astore_2
      87: getstatic     #48                 // Field INSTANCE:Lcom/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment;
      90: aload_1
      91: aload_2
      92: aload_0
      93: new           #50                 // class java/util/ArrayList
      96: dup
      97: invokespecial #51                 // Method java/util/ArrayList."<init>":()V
     100: checkcast     #25                 // class java/util/List
     103: invokespecial #55                 // Method collectAllPermutations:(Lcom/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant;[Z[Ljava/lang/Object;Ljava/util/List;)V
     106: aload_1
     107: invokevirtual #59                 // Method com/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant.getValue:()[Ljava/util/List;
     110: areturn

  private final <T> void collectAllPermutations(com.nickperov.stud.combinatorics.CombinatorialUtils$CombinationsInvariant<T>, boolean[], T[], java.util.List<T>);
    Code:
       0: aload         4
       2: invokeinterface #72,  1           // InterfaceMethod java/util/List.size:()I
       7: aload_3
       8: arraylength
       9: if_icmpne     42
      12: aload_1
      13: invokevirtual #59                 // Method com/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant.getValue:()[Ljava/util/List;
      16: aload_1
      17: invokevirtual #75                 // Method com/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant.getIndex:()I
      20: aload         4
      22: checkcast     #77                 // class java/lang/Iterable
      25: invokestatic  #81                 // Method kotlin/collections/CollectionsKt.toList:(Ljava/lang/Iterable;)Ljava/util/List;
      28: aastore
      29: aload_1
      30: aload_1
      31: invokevirtual #75                 // Method com/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant.getIndex:()I
      34: iconst_1
      35: iadd
      36: invokevirtual #85                 // Method com/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant.setIndex:(I)V
      39: goto          116
      42: iconst_0
      43: istore        5
      45: aload_2
      46: arraylength
      47: istore        6
      49: iload         5
      51: iload         6
      53: if_icmpge     116
      56: aload_2
      57: iload         5
      59: baload
      60: ifne          110
      63: aload_2
      64: invokevirtual #89                 // Method java/lang/Object.clone:()Ljava/lang/Object;
      67: checkcast     #90                 // class "[Z"
      70: astore        7
      72: aload         7
      74: iload         5
      76: iconst_1
      77: bastore
      78: aload         4
      80: checkcast     #92                 // class java/util/Collection
      83: invokestatic  #96                 // Method kotlin/collections/CollectionsKt.toMutableList:(Ljava/util/Collection;)Ljava/util/List;
      86: astore        8
      88: aload         8
      90: aload_3
      91: iload         5
      93: aaload
      94: invokeinterface #100,  2          // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      99: pop
     100: aload_0
     101: aload_1
     102: aload         7
     104: aload_3
     105: aload         8
     107: invokespecial #55                 // Method collectAllPermutations:(Lcom/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant;[Z[Ljava/lang/Object;Ljava/util/List;)V
     110: iinc          5, 1
     113: goto          49
     116: return

  public static final <T> java.util.List<T>[] getAllPermutationsV2(T[]);
    Code:
       0: aload_0
       1: ldc           #17                 // String elements
       3: invokestatic  #23                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: getstatic     #31                 // Field com/nickperov/stud/combinatorics/CombinatorialUtils.INSTANCE:Lcom/nickperov/stud/combinatorics/CombinatorialUtils;
       9: aload_0
      10: invokevirtual #110                // Method com/nickperov/stud/combinatorics/CombinatorialUtils.getAllPermutations:([Ljava/lang/Object;)[Ljava/util/List;
      13: areturn

  public static final <T> java.util.List<T>[] getAllPermutationsV3(T[]);
    Code:
       0: aload_0
       1: ldc           #17                 // String elements
       3: invokestatic  #23                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: arraylength
       8: ifne          15
      11: iconst_1
      12: goto          16
      15: iconst_0
      16: ifeq          24
      19: iconst_0
      20: anewarray     #25                 // class java/util/List
      23: areturn
      24: iconst_0
      25: istore_2
      26: getstatic     #31                 // Field com/nickperov/stud/combinatorics/CombinatorialUtils.INSTANCE:Lcom/nickperov/stud/combinatorics/CombinatorialUtils;
      29: aload_0
      30: arraylength
      31: invokevirtual #35                 // Method com/nickperov/stud/combinatorics/CombinatorialUtils.calculateNumberOfPermutationsInt:(I)I
      34: istore_3
      35: iload_3
      36: anewarray     #25                 // class java/util/List
      39: astore        4
      41: iload_2
      42: iload_3
      43: if_icmpge     63
      46: iload_2
      47: istore        5
      49: aload         4
      51: iload         5
      53: invokestatic  #41                 // Method kotlin/collections/CollectionsKt.emptyList:()Ljava/util/List;
      56: aastore
      57: iinc          2, 1
      60: goto          41
      63: aload         4
      65: iconst_0
      66: istore        6
      68: astore        7
      70: new           #43                 // class com/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant
      73: dup
      74: aload         7
      76: iload         6
      78: invokespecial #46                 // Method com/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant."<init>":([Ljava/util/List;I)V
      81: astore_1
      82: aload_0
      83: arraylength
      84: newarray       boolean
      86: astore_2
      87: getstatic     #48                 // Field INSTANCE:Lcom/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment;
      90: aload_1
      91: aload_2
      92: aload_0
      93: new           #50                 // class java/util/ArrayList
      96: dup
      97: invokespecial #51                 // Method java/util/ArrayList."<init>":()V
     100: checkcast     #25                 // class java/util/List
     103: aload_2
     104: arraylength
     105: invokespecial #114                // Method collectAllPermutations:(Lcom/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant;[Z[Ljava/lang/Object;Ljava/util/List;I)V
     108: aload_1
     109: invokevirtual #59                 // Method com/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant.getValue:()[Ljava/util/List;
     112: areturn

  private final <T> void collectAllPermutations(com.nickperov.stud.combinatorics.CombinatorialUtils$CombinationsInvariant<T>, boolean[], T[], java.util.List<T>, int);
    Code:
       0: aload         4
       2: invokeinterface #72,  1           // InterfaceMethod java/util/List.size:()I
       7: aload_3
       8: arraylength
       9: if_icmpne     42
      12: aload_1
      13: invokevirtual #59                 // Method com/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant.getValue:()[Ljava/util/List;
      16: aload_1
      17: invokevirtual #75                 // Method com/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant.getIndex:()I
      20: aload         4
      22: checkcast     #77                 // class java/lang/Iterable
      25: invokestatic  #81                 // Method kotlin/collections/CollectionsKt.toList:(Ljava/lang/Iterable;)Ljava/util/List;
      28: aastore
      29: aload_1
      30: aload_1
      31: invokevirtual #75                 // Method com/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant.getIndex:()I
      34: iconst_1
      35: iadd
      36: invokevirtual #85                 // Method com/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant.setIndex:(I)V
      39: goto          166
      42: iconst_1
      43: istore        6
      45: iconst_0
      46: istore        7
      48: aload_2
      49: arraylength
      50: istore        8
      52: iload         7
      54: iload         8
      56: if_icmpge     166
      59: aload_2
      60: iload         7
      62: baload
      63: ifne          160
      66: iload         6
      68: iinc          6, 1
      71: iload         5
      73: if_icmpne     109
      76: aload_2
      77: iload         7
      79: iconst_1
      80: bastore
      81: aload         4
      83: aload_3
      84: iload         7
      86: aaload
      87: invokeinterface #100,  2          // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      92: pop
      93: aload_0
      94: aload_1
      95: aload_2
      96: aload_3
      97: aload         4
      99: iload         5
     101: iconst_1
     102: isub
     103: invokespecial #114                // Method collectAllPermutations:(Lcom/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant;[Z[Ljava/lang/Object;Ljava/util/List;I)V
     106: goto          160
     109: aload_2
     110: invokevirtual #89                 // Method java/lang/Object.clone:()Ljava/lang/Object;
     113: checkcast     #90                 // class "[Z"
     116: astore        9
     118: aload         9
     120: iload         7
     122: iconst_1
     123: bastore
     124: aload         4
     126: checkcast     #92                 // class java/util/Collection
     129: invokestatic  #96                 // Method kotlin/collections/CollectionsKt.toMutableList:(Ljava/util/Collection;)Ljava/util/List;
     132: astore        10
     134: aload         10
     136: aload_3
     137: iload         7
     139: aaload
     140: invokeinterface #100,  2          // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
     145: pop
     146: aload_0
     147: aload_1
     148: aload         9
     150: aload_3
     151: aload         10
     153: iload         5
     155: iconst_1
     156: isub
     157: invokespecial #114                // Method collectAllPermutations:(Lcom/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant;[Z[Ljava/lang/Object;Ljava/util/List;I)V
     160: iinc          7, 1
     163: goto          52
     166: return

  public static final <T> java.util.List<T>[] getAllPermutationsV4(T[]);
    Code:
       0: aload_0
       1: ldc           #17                 // String elements
       3: invokestatic  #23                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: arraylength
       8: ifne          15
      11: iconst_1
      12: goto          16
      15: iconst_0
      16: ifeq          24
      19: iconst_0
      20: anewarray     #25                 // class java/util/List
      23: areturn
      24: iconst_0
      25: istore_2
      26: getstatic     #31                 // Field com/nickperov/stud/combinatorics/CombinatorialUtils.INSTANCE:Lcom/nickperov/stud/combinatorics/CombinatorialUtils;
      29: aload_0
      30: arraylength
      31: invokevirtual #35                 // Method com/nickperov/stud/combinatorics/CombinatorialUtils.calculateNumberOfPermutationsInt:(I)I
      34: istore_3
      35: iload_3
      36: anewarray     #25                 // class java/util/List
      39: astore        4
      41: iload_2
      42: iload_3
      43: if_icmpge     63
      46: iload_2
      47: istore        5
      49: aload         4
      51: iload         5
      53: invokestatic  #41                 // Method kotlin/collections/CollectionsKt.emptyList:()Ljava/util/List;
      56: aastore
      57: iinc          2, 1
      60: goto          41
      63: aload         4
      65: iconst_0
      66: istore        11
      68: astore        12
      70: new           #43                 // class com/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant
      73: dup
      74: aload         12
      76: iload         11
      78: invokespecial #46                 // Method com/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant."<init>":([Ljava/util/List;I)V
      81: astore_1
      82: new           #120                // class java/util/ArrayDeque
      85: dup
      86: aload_0
      87: arraylength
      88: invokespecial #122                // Method java/util/ArrayDeque."<init>":(I)V
      91: checkcast     #124                // class java/util/Deque
      94: astore_2
      95: new           #126                // class com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState
      98: dup
      99: aload_0
     100: arraylength
     101: newarray       boolean
     103: new           #50                 // class java/util/ArrayList
     106: dup
     107: invokespecial #51                 // Method java/util/ArrayList."<init>":()V
     110: checkcast     #25                 // class java/util/List
     113: aload_0
     114: arraylength
     115: iconst_0
     116: iconst_1
     117: invokespecial #129                // Method com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState."<init>":([ZLjava/util/List;III)V
     120: astore_3
     121: aload_2
     122: aload_3
     123: invokeinterface #133,  2          // InterfaceMethod java/util/Deque.push:(Ljava/lang/Object;)V
     128: nop
     129: aload_3
     130: ifnonnull     136
     133: goto          411
     136: aload_3
     137: invokevirtual #137                // Method com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState.getUsed:()[Z
     140: astore        4
     142: aload_3
     143: invokevirtual #140                // Method com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState.getSelection:()Ljava/util/List;
     146: astore        5
     148: aload_3
     149: invokevirtual #143                // Method com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState.getCount:()I
     152: istore        6
     154: aload_3
     155: invokevirtual #146                // Method com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState.getCurrCount:()I
     158: istore        7
     160: aload_3
     161: invokevirtual #147                // Method com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState.getIndex:()I
     164: istore        8
     166: iload         8
     168: aload_0
     169: arraylength
     170: if_icmpge     391
     173: aload         4
     175: iload         8
     177: baload
     178: ifne          373
     181: iload         7
     183: iinc          7, 1
     186: iload         6
     188: if_icmpne     293
     191: aload         4
     193: iload         8
     195: iconst_1
     196: bastore
     197: aload         5
     199: aload_0
     200: iload         8
     202: aaload
     203: invokeinterface #100,  2          // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
     208: pop
     209: aload_3
     210: iload         8
     212: iconst_1
     213: iadd
     214: invokevirtual #148                // Method com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState.setIndex:(I)V
     217: aload_3
     218: iload         7
     220: invokevirtual #151                // Method com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState.setCurrCount:(I)V
     223: aload         5
     225: invokeinterface #72,  1           // InterfaceMethod java/util/List.size:()I
     230: aload_0
     231: arraylength
     232: if_icmpne     265
     235: aload_1
     236: invokevirtual #59                 // Method com/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant.getValue:()[Ljava/util/List;
     239: aload_1
     240: invokevirtual #75                 // Method com/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant.getIndex:()I
     243: aload         5
     245: checkcast     #77                 // class java/lang/Iterable
     248: invokestatic  #81                 // Method kotlin/collections/CollectionsKt.toList:(Ljava/lang/Iterable;)Ljava/util/List;
     251: aastore
     252: aload_1
     253: aload_1
     254: invokevirtual #75                 // Method com/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant.getIndex:()I
     257: iconst_1
     258: iadd
     259: invokevirtual #85                 // Method com/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant.setIndex:(I)V
     262: goto          391
     265: new           #126                // class com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState
     268: dup
     269: aload         4
     271: aload         5
     273: iload         6
     275: iconst_1
     276: isub
     277: iconst_0
     278: iconst_1
     279: invokespecial #129                // Method com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState."<init>":([ZLjava/util/List;III)V
     282: astore_3
     283: aload_2
     284: aload_3
     285: invokeinterface #133,  2          // InterfaceMethod java/util/Deque.push:(Ljava/lang/Object;)V
     290: goto          128
     293: aload         4
     295: invokevirtual #89                 // Method java/lang/Object.clone:()Ljava/lang/Object;
     298: checkcast     #90                 // class "[Z"
     301: astore        9
     303: aload         9
     305: iload         8
     307: iconst_1
     308: bastore
     309: aload         5
     311: checkcast     #92                 // class java/util/Collection
     314: invokestatic  #96                 // Method kotlin/collections/CollectionsKt.toMutableList:(Ljava/util/Collection;)Ljava/util/List;
     317: astore        10
     319: aload         10
     321: aload_0
     322: iload         8
     324: aaload
     325: invokeinterface #100,  2          // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
     330: pop
     331: aload_3
     332: iload         8
     334: iconst_1
     335: iadd
     336: invokevirtual #148                // Method com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState.setIndex:(I)V
     339: aload_3
     340: iload         7
     342: invokevirtual #151                // Method com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState.setCurrCount:(I)V
     345: new           #126                // class com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState
     348: dup
     349: aload         9
     351: aload         10
     353: iload         6
     355: iconst_1
     356: isub
     357: iconst_0
     358: iconst_1
     359: invokespecial #129                // Method com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState."<init>":([ZLjava/util/List;III)V
     362: astore_3
     363: aload_2
     364: aload_3
     365: invokeinterface #133,  2          // InterfaceMethod java/util/Deque.push:(Ljava/lang/Object;)V
     370: goto          128
     373: iinc          8, 1
     376: aload_3
     377: iload         8
     379: invokevirtual #148                // Method com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState.setIndex:(I)V
     382: aload_3
     383: iload         7
     385: invokevirtual #151                // Method com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState.setCurrCount:(I)V
     388: goto          166
     391: aload_2
     392: invokeinterface #154,  1          // InterfaceMethod java/util/Deque.remove:()Ljava/lang/Object;
     397: pop
     398: aload_2
     399: invokeinterface #157,  1          // InterfaceMethod java/util/Deque.peek:()Ljava/lang/Object;
     404: checkcast     #126                // class com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState
     407: astore_3
     408: goto          128
     411: aload_1
     412: invokevirtual #59                 // Method com/nickperov/stud/combinatorics/CombinatorialUtils$CombinationsInvariant.getValue:()[Ljava/util/List;
     415: areturn

  static {};
    Code:
       0: new           #2                  // class com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment
       3: dup
       4: invokespecial #163                // Method "<init>":()V
       7: putstatic     #48                 // Field INSTANCE:Lcom/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment;
      10: return
}

Compiled from "PermutationsCalculatorExperiment.kt"
public final class com.nickperov.stud.combinatorics.experiment.PermutationsCalculatorExperiment$RecursiveState<T> {
  private final boolean[] used;

  private final java.util.List<T> selection;

  private final int count;

  private int index;

  private int currCount;

  public com.nickperov.stud.combinatorics.experiment.PermutationsCalculatorExperiment$RecursiveState(boolean[], java.util.List<T>, int, int, int);
    Code:
       0: aload_1
       1: ldc           #11                 // String used
       3: invokestatic  #17                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_2
       7: ldc           #19                 // String selection
       9: invokestatic  #17                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
      12: aload_0
      13: invokespecial #22                 // Method java/lang/Object."<init>":()V
      16: aload_0
      17: aload_1
      18: putfield      #25                 // Field used:[Z
      21: aload_0
      22: aload_2
      23: putfield      #28                 // Field selection:Ljava/util/List;
      26: aload_0
      27: iload_3
      28: putfield      #32                 // Field count:I
      31: aload_0
      32: iload         4
      34: putfield      #35                 // Field index:I
      37: aload_0
      38: iload         5
      40: putfield      #38                 // Field currCount:I
      43: return

  public final boolean[] getUsed();
    Code:
       0: aload_0
       1: getfield      #25                 // Field used:[Z
       4: areturn

  public final java.util.List<T> getSelection();
    Code:
       0: aload_0
       1: getfield      #28                 // Field selection:Ljava/util/List;
       4: areturn

  public final int getCount();
    Code:
       0: aload_0
       1: getfield      #32                 // Field count:I
       4: ireturn

  public final int getIndex();
    Code:
       0: aload_0
       1: getfield      #35                 // Field index:I
       4: ireturn

  public final void setIndex(int);
    Code:
       0: aload_0
       1: iload_1
       2: putfield      #35                 // Field index:I
       5: return

  public final int getCurrCount();
    Code:
       0: aload_0
       1: getfield      #38                 // Field currCount:I
       4: ireturn

  public final void setCurrCount(int);
    Code:
       0: aload_0
       1: iload_1
       2: putfield      #38                 // Field currCount:I
       5: return

  public boolean equals(java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: if_acmpne     7
       5: iconst_1
       6: ireturn
       7: aload_0
       8: invokevirtual #60                 // Method java/lang/Object.getClass:()Ljava/lang/Class;
      11: aload_1
      12: dup
      13: ifnull        22
      16: invokevirtual #60                 // Method java/lang/Object.getClass:()Ljava/lang/Class;
      19: goto          24
      22: pop
      23: aconst_null
      24: invokestatic  #64                 // Method kotlin/jvm/internal/Intrinsics.areEqual:(Ljava/lang/Object;Ljava/lang/Object;)Z
      27: ifne          32
      30: iconst_0
      31: ireturn
      32: aload_1
      33: ldc           #66                 // String null cannot be cast to non-null type com.nickperov.stud.combinatorics.experiment.PermutationsCalculatorExperiment.RecursiveState<*>
      35: invokestatic  #69                 // Method kotlin/jvm/internal/Intrinsics.checkNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
      38: aload_1
      39: checkcast     #2                  // class com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState
      42: pop
      43: aload_0
      44: getfield      #25                 // Field used:[Z
      47: aload_1
      48: checkcast     #2                  // class com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState
      51: getfield      #25                 // Field used:[Z
      54: invokestatic  #74                 // Method java/util/Arrays.equals:([Z[Z)Z
      57: ifne          62
      60: iconst_0
      61: ireturn
      62: aload_0
      63: getfield      #28                 // Field selection:Ljava/util/List;
      66: aload_1
      67: checkcast     #2                  // class com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState
      70: getfield      #28                 // Field selection:Ljava/util/List;
      73: invokestatic  #64                 // Method kotlin/jvm/internal/Intrinsics.areEqual:(Ljava/lang/Object;Ljava/lang/Object;)Z
      76: ifne          81
      79: iconst_0
      80: ireturn
      81: aload_0
      82: getfield      #32                 // Field count:I
      85: aload_1
      86: checkcast     #2                  // class com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState
      89: getfield      #32                 // Field count:I
      92: if_icmpeq     97
      95: iconst_0
      96: ireturn
      97: aload_0
      98: getfield      #35                 // Field index:I
     101: aload_1
     102: checkcast     #2                  // class com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState
     105: getfield      #35                 // Field index:I
     108: if_icmpeq     113
     111: iconst_0
     112: ireturn
     113: aload_0
     114: getfield      #38                 // Field currCount:I
     117: aload_1
     118: checkcast     #2                  // class com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState
     121: getfield      #38                 // Field currCount:I
     124: if_icmpeq     129
     127: iconst_0
     128: ireturn
     129: iconst_1
     130: ireturn

  public int hashCode();
    Code:
       0: aload_0
       1: getfield      #25                 // Field used:[Z
       4: invokestatic  #82                 // Method java/util/Arrays.hashCode:([Z)I
       7: istore_1
       8: bipush        31
      10: iload_1
      11: imul
      12: aload_0
      13: getfield      #28                 // Field selection:Ljava/util/List;
      16: invokevirtual #84                 // Method java/lang/Object.hashCode:()I
      19: iadd
      20: istore_1
      21: bipush        31
      23: iload_1
      24: imul
      25: aload_0
      26: getfield      #32                 // Field count:I
      29: iadd
      30: istore_1
      31: bipush        31
      33: iload_1
      34: imul
      35: aload_0
      36: getfield      #35                 // Field index:I
      39: iadd
      40: istore_1
      41: bipush        31
      43: iload_1
      44: imul
      45: aload_0
      46: getfield      #38                 // Field currCount:I
      49: iadd
      50: istore_1
      51: iload_1
      52: ireturn

  public final boolean[] component1();
    Code:
       0: aload_0
       1: getfield      #25                 // Field used:[Z
       4: areturn

  public final java.util.List<T> component2();
    Code:
       0: aload_0
       1: getfield      #28                 // Field selection:Ljava/util/List;
       4: areturn

  public final int component3();
    Code:
       0: aload_0
       1: getfield      #32                 // Field count:I
       4: ireturn

  public final int component4();
    Code:
       0: aload_0
       1: getfield      #35                 // Field index:I
       4: ireturn

  public final int component5();
    Code:
       0: aload_0
       1: getfield      #38                 // Field currCount:I
       4: ireturn

  public final com.nickperov.stud.combinatorics.experiment.PermutationsCalculatorExperiment$RecursiveState<T> copy(boolean[], java.util.List<T>, int, int, int);
    Code:
       0: aload_1
       1: ldc           #11                 // String used
       3: invokestatic  #17                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_2
       7: ldc           #19                 // String selection
       9: invokestatic  #17                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
      12: new           #2                  // class com/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState
      15: dup
      16: aload_1
      17: aload_2
      18: iload_3
      19: iload         4
      21: iload         5
      23: invokespecial #95                 // Method "<init>":([ZLjava/util/List;III)V
      26: areturn

  public static com.nickperov.stud.combinatorics.experiment.PermutationsCalculatorExperiment$RecursiveState copy$default(com.nickperov.stud.combinatorics.experiment.PermutationsCalculatorExperiment$RecursiveState, boolean[], java.util.List, int, int, int, int, java.lang.Object);
    Code:
       0: iload         6
       2: iconst_1
       3: iand
       4: ifeq          12
       7: aload_0
       8: getfield      #25                 // Field used:[Z
      11: astore_1
      12: iload         6
      14: iconst_2
      15: iand
      16: ifeq          24
      19: aload_0
      20: getfield      #28                 // Field selection:Ljava/util/List;
      23: astore_2
      24: iload         6
      26: iconst_4
      27: iand
      28: ifeq          36
      31: aload_0
      32: getfield      #32                 // Field count:I
      35: istore_3
      36: iload         6
      38: bipush        8
      40: iand
      41: ifeq          50
      44: aload_0
      45: getfield      #35                 // Field index:I
      48: istore        4
      50: iload         6
      52: bipush        16
      54: iand
      55: ifeq          64
      58: aload_0
      59: getfield      #38                 // Field currCount:I
      62: istore        5
      64: aload_0
      65: aload_1
      66: aload_2
      67: iload_3
      68: iload         4
      70: iload         5
      72: invokevirtual #99                 // Method copy:([ZLjava/util/List;III)Lcom/nickperov/stud/combinatorics/experiment/PermutationsCalculatorExperiment$RecursiveState;
      75: areturn

  public java.lang.String toString();
    Code:
       0: new           #103                // class java/lang/StringBuilder
       3: dup
       4: invokespecial #104                // Method java/lang/StringBuilder."<init>":()V
       7: ldc           #106                // String RecursiveState(used=
       9: invokevirtual #110                // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      12: aload_0
      13: getfield      #25                 // Field used:[Z
      16: invokestatic  #113                // Method java/util/Arrays.toString:([Z)Ljava/lang/String;
      19: invokevirtual #110                // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      22: ldc           #115                // String , selection=
      24: invokevirtual #110                // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      27: aload_0
      28: getfield      #28                 // Field selection:Ljava/util/List;
      31: invokevirtual #118                // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
      34: ldc           #120                // String , count=
      36: invokevirtual #110                // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      39: aload_0
      40: getfield      #32                 // Field count:I
      43: invokevirtual #123                // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      46: ldc           #125                // String , index=
      48: invokevirtual #110                // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      51: aload_0
      52: getfield      #35                 // Field index:I
      55: invokevirtual #123                // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      58: ldc           #127                // String , currCount=
      60: invokevirtual #110                // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      63: aload_0
      64: getfield      #38                 // Field currCount:I
      67: invokevirtual #123                // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      70: bipush        41
      72: invokevirtual #130                // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
      75: invokevirtual #132                // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      78: areturn
}