package com.exemple.scrabble

import java.lang.StringBuilder
import java.util.ArrayList

/**
 * Title: Trie
 *
 * Author: <NAME>
 *
 * Brief:
 * This object represents the implementation of a Trie tree for a 26 letter
 * alphabet.
 *
 * Descrição:
 * A Trie tree, or prefix tree, is an ordered tree that can be used to store an
 * array where the keys are usually strings. All descendants of any node have a
 * common prefix with the chain associated with that node, and the root is
 * associated with the empty chain. Normally, values are not associated with
 * all nodes, only with the leaves and some internal nodes that correspond to
 * keys of interest.
 */
object Trie {
    const val alphabet = 26
    private var root: TrieNode = TrieNode()

    class TrieNode {
        var child = arrayOfNulls<TrieNode>(alphabet)
        var leaf = false
    }

    /**
     * Brief:
     * Function responsible for inserting the dictionary words in the [Trie]
     * tree.
     *
     * Description:
     * This function inserts all the words in a dictionary into a tree [Trie].
     * For the sake of simplicity, the dictionary is created in a 'hardcoded'
     * way, that is, its data is directly in the code. Each word in this
     * dictionary is added individually to the tree, the length of the string
     * is measured to find the index of each of its letters. The insertion
     * begins at the root of the tree, checking its descendants, if there is
     * already a node for that letter, then the root becomes that node,
     * otherwise a new node is created and defined as the root. Thus, with each
     * letter entered, a level is lowered in the tree. After the word has been
     * completely inserted, the last node is marked as a leaf node, indicating
     * the end of the word. This process is repeated for all words in the
     * dictionary.
     */
    fun insert() {
        // dictionary containing all available words
        val dict = arrayOf(
            "pineapple", "herd", "order", "door", "table", "die", "mangoes",
            "go", "things", "radiography", "math", "drugs", "buildings",
            "implementation", "computer", "balloon", "cup", "boredom", "banner",
            "book", "leave", "superior", "profession", "meeting", "buildings",
            "mountain", "botany", "bathroom", "boxes", "cursing", "infestation",
            "termite", "winning", "breaded", "rats", "noise", "antecedent",
            "company", "emissary", "slack", "break", "guava", "free",
            "hydraulic", "man", "dinner", "games", "assembly", "manual",
            "cloud", "snow", "operation", "yesterday", "duck", "foot", "trip",
            "cheese", "room", "backyard", "loose", "route", "jungle", "tattoo",
            "tiger", "grape", "last", "reproach", "voltage", "angry", "mockery",
            "pain"
        )

        // performs the individual insertion of each word in the tree
        for (key in dict) {
            // saves the word size and the root node
            val length = key.length
            var crawl = root

            // inserts each letter of the word in the tree
            for (i in 0 until length) {
                val index = key[i] - 'a'
                if (crawl.child[index] == null) crawl.child[index] = TrieNode()
                crawl = crawl.child[index]!!
            }

            // marks the current node as a leaf node
            crawl.leaf = true
        }
    }

    /**
     * Brief:
     * Function responsible for calculating the word score.
     *
     * Description:
     * Calculates the value of each valid word obtained from the input
     * characters. For each string, its characters are traversed and the value
     * assigned to them is added to an array of points, initialized with zeros,
     * in the index corresponding to the word in question. The set of words and
     * points are combined to form a set of pairs of (word, points). This set
     * is ordered by the size of the words, from smallest to largest, and then
     * the word with the highest score is selected; if there is a tie, the first
     * occurrence of that value is returned, that is, the smallest word.
     *
     * Params:
     *
     *      arr (CharArray): Array of characters received as input for word
     *          formation.
     *
     * Returns:
     *
     *      (Pair<String, Int>?): Pair composed of the word with the highest
     *          score and its value.
     */
    fun highestScore(arr: CharArray): Pair<String, Int>? {
        // auxiliary variables for calculating the value of each letter
        val one = arrayOf('e', 'a', 'i', 'o', 'n', 'r', 't', 'l', 's', 'u')
        val two = arrayOf('w', 'd', 'g')
        val tree = arrayOf('b', 'c', 'm', 'p')
        val four = arrayOf('f', 'h', 'v')
        val eight = arrayOf('j', 'x')
        val ten = arrayOf('q', 'z')

        // searches for all words that can be formed from the entry
        val words = searchAllWords(arr)

        // filters the words to respect the frequency of characters
        wordFilter(arr, words)

        // calculates the value of each word and saves it in an array of points
        val points = IntArray(words.size)
        for (i in words.indices) for (letter in words[i])
            when (letter) {
                in one -> points[i] += 1
                in two -> points[i] += 2
                in tree -> points[i] += 3
                in four -> points[i] += 4
                in eight -> points[i] += 8
                in ten -> points[i] += 10
            }

        // associates each word with its respective score
        var pair = words zip points.toTypedArray()

        // sorts pairs of (word, dots) by the length of each word
        pair = pair.sortedBy { it.first.length }

        // returns the first occurrence of the pair with the highest score
        return pair.maxBy { it.second }
    }

    /**
     * Brief:
     * Function responsible for finding the characters that were not used for
     * the composition of the final word.
     *
     * Description:
     * This function finds and saves all the characters that were not used for
     * the formation of the word with the highest score. From the input
     * characters and the chosen word, each letter of the word is traversed and
     * removes its first occurrence from the input set, so at the end of this
     * execution, there is a set with only the letters that were not used.
     *
     * Params:
     *
     *      word (String): Highest scored word.
     *
     *      arr (CharArray): Array of characters received as input for word
     *          formation.
     *
     * Returns:
     *
     *      (String): Set with all unused characters.
     */
    fun getRest(word: String, arr: CharArray) : String {
        // auxiliary variable to store the input characters
        val rest = arr.toMutableList()

        // removes each character in the word from the input characters
        for (letter in word) rest.remove(letter)

        // returns all unused characters
        return String(rest.toCharArray())
    }

    /**
     * Brief:
     * Function responsible for searching all words in the dictionary that can
     * be formed with the input characters.
     *
     * Description:
     * Searches for all words contained in the trees that can be formed with
     * the characters provided. Initially an array is created where the words
     * found will be saved and also an array of Booleans for each letter of the
     * alphabet. For each character received in the entry, it is marked as
     * 'true' in the array of Booleans in the index corresponding to that letter
     * in the alphabet. In sequence, this array is traversed and if any position
     * is marked as 'true' and there is a descendant in the tree for that
     * letter, then that letter is added to a string, which is initially empty,
     * so that a deep search can be performed for the descendants of that
     * letter. After all letters that have been received have been searched, the
     * words that were found are returned.
     *
     * Params:
     *
     *      arr (CharArray): Array of characters received as input for word
     *          formation.
     *
     * Returns:
     *
     *      (ArrayList<String>): Set with all the words found by the search.
     */
    private fun searchAllWords(arr: CharArray): ArrayList<String> {
        // initially empty word set and boolean array
        val words = arrayListOf("")
        val hash = BooleanArray(alphabet)

        // marks each letter of the entry as 'true' in the Boolean array
        for (element in arr) hash[element - 'a'] = true
        var str = StringBuilder()

        // scrolls through the alphabet to search each letter in the entry
        for (i in 0 until alphabet) if (hash[i] && root.child[i] != null) {
            // saves the letter that has descendants and is in the entry
            str.append((i + 'a'.toInt()).toChar())

            // performs in-depth search for each letter
            searchWord(root.child[i], words, hash, str.toString())
            str = StringBuilder()
        }

        // returns all words found in the tree
        return words
    }

    /**
     * Brief:
     * Recursive function responsible for searching in depth for each letter of
     * the entry.
     *
     * Description:
     * Searches each node for the existence of descendants and adds the
     * characters found to the string passed by parameter, which initially has
     * only the first letter of the word. If the node in question is a leaf
     * node, it means that a complete word has been found, then that word is
     * added to the set. The search continues until you scroll through all the
     * letters available at the entrance.
     *
     * Params:
     *
     *      root (TrieNode?): Node through which the search will start.
     *
     *      words (ArrayList<String>): Set with all the words found by the
     *          search.
     *
     *      hash (BooleanArray): Set of Booleans that indicate which letters of
     *          the alphabets are present at the entrance.
     *
     *      str (String): Word that will be constructed during the execution of
     *          the search.
     */
    private fun searchWord(
            root: TrieNode?,
            words: ArrayList<String>,
            hash: BooleanArray,
            str: String
    ) {
        // if the node in question is a leaf the word is saved in the set
        if (root!!.leaf) words.add(str)

        // performs the in-depth search for each valid descendant
        for (i in 0 until alphabet) if (hash[i] && root.child[i] != null) {
            val c = (i + 'a'.toInt()).toChar()
            searchWord(root.child[i], words, hash, str + c)
        }
    }

    /**
     * Brief:
     * Function responsible for filtering words that disregard the character
     * frequency of the entry.
     *
     * Description:
     * This function removes from the set of words those that violate the
     * acceptance criteria, in this case the frequency of each character.
     * Initially an array of integers saves how many occurrences there were of
     * each character in the entry, where the index corresponds to its position
     * in the alphabet. This array is then traversed and for each letter whose
     * frequency is not zero, it is verified how many occurrences of it exist
     * in each of the words in the set. If any word has more characters of that
     * type than are available, that word is removed from the set.
     *
     * Params:
     *
     *      arr (CharArray): Array of characters received as input for word
     *          formation.
     *
     *      words (ArrayList<String>): Set with all the words found by the
     *          search.
     */
    private fun wordFilter(arr: CharArray, words: ArrayList<String>) {
        // array that stores the frequency of each letter in the alphabet
        val frequency = IntArray(alphabet)

        // auxiliary variable to store the words that must be removed
        val aux = arrayListOf<String>()

        // the frequency of each letter is calculated and the search begins
        for (element in arr) frequency[element - 'a']++
        for (i in 0 until alphabet) if (frequency[i] > 0)
            for (word in words) {
                // the occurrences of the letter in question are counted in the word
                val n = word.count { c -> c == (i + 'a'.toInt()).toChar() }

                // save the word that must be removed from the set
                if (n > frequency[i]) aux.add(word)
            }

        // remove the invalid words from the solution set
        for (element in aux) words.remove(element)
    }
}

Compiled from "Trie.kt"
public final class com.exemple.scrabble.Trie {
  public static final com.exemple.scrabble.Trie INSTANCE;

  public static final int alphabet;

  private static com.exemple.scrabble.Trie$TrieNode root;

  private com.exemple.scrabble.Trie();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public final void insert();
    Code:
       0: bipush        71
       2: anewarray     #13                 // class java/lang/String
       5: astore_2
       6: aload_2
       7: iconst_0
       8: ldc           #15                 // String pineapple
      10: aastore
      11: aload_2
      12: iconst_1
      13: ldc           #17                 // String herd
      15: aastore
      16: aload_2
      17: iconst_2
      18: ldc           #19                 // String order
      20: aastore
      21: aload_2
      22: iconst_3
      23: ldc           #21                 // String door
      25: aastore
      26: aload_2
      27: iconst_4
      28: ldc           #23                 // String table
      30: aastore
      31: aload_2
      32: iconst_5
      33: ldc           #25                 // String die
      35: aastore
      36: aload_2
      37: bipush        6
      39: ldc           #27                 // String mangoes
      41: aastore
      42: aload_2
      43: bipush        7
      45: ldc           #29                 // String go
      47: aastore
      48: aload_2
      49: bipush        8
      51: ldc           #31                 // String things
      53: aastore
      54: aload_2
      55: bipush        9
      57: ldc           #33                 // String radiography
      59: aastore
      60: aload_2
      61: bipush        10
      63: ldc           #35                 // String math
      65: aastore
      66: aload_2
      67: bipush        11
      69: ldc           #37                 // String drugs
      71: aastore
      72: aload_2
      73: bipush        12
      75: ldc           #39                 // String buildings
      77: aastore
      78: aload_2
      79: bipush        13
      81: ldc           #41                 // String implementation
      83: aastore
      84: aload_2
      85: bipush        14
      87: ldc           #43                 // String computer
      89: aastore
      90: aload_2
      91: bipush        15
      93: ldc           #45                 // String balloon
      95: aastore
      96: aload_2
      97: bipush        16
      99: ldc           #47                 // String cup
     101: aastore
     102: aload_2
     103: bipush        17
     105: ldc           #49                 // String boredom
     107: aastore
     108: aload_2
     109: bipush        18
     111: ldc           #51                 // String banner
     113: aastore
     114: aload_2
     115: bipush        19
     117: ldc           #53                 // String book
     119: aastore
     120: aload_2
     121: bipush        20
     123: ldc           #55                 // String leave
     125: aastore
     126: aload_2
     127: bipush        21
     129: ldc           #57                 // String superior
     131: aastore
     132: aload_2
     133: bipush        22
     135: ldc           #59                 // String profession
     137: aastore
     138: aload_2
     139: bipush        23
     141: ldc           #61                 // String meeting
     143: aastore
     144: aload_2
     145: bipush        24
     147: ldc           #39                 // String buildings
     149: aastore
     150: aload_2
     151: bipush        25
     153: ldc           #63                 // String mountain
     155: aastore
     156: aload_2
     157: bipush        26
     159: ldc           #65                 // String botany
     161: aastore
     162: aload_2
     163: bipush        27
     165: ldc           #67                 // String bathroom
     167: aastore
     168: aload_2
     169: bipush        28
     171: ldc           #69                 // String boxes
     173: aastore
     174: aload_2
     175: bipush        29
     177: ldc           #71                 // String cursing
     179: aastore
     180: aload_2
     181: bipush        30
     183: ldc           #73                 // String infestation
     185: aastore
     186: aload_2
     187: bipush        31
     189: ldc           #75                 // String termite
     191: aastore
     192: aload_2
     193: bipush        32
     195: ldc           #77                 // String winning
     197: aastore
     198: aload_2
     199: bipush        33
     201: ldc           #79                 // String breaded
     203: aastore
     204: aload_2
     205: bipush        34
     207: ldc           #81                 // String rats
     209: aastore
     210: aload_2
     211: bipush        35
     213: ldc           #83                 // String noise
     215: aastore
     216: aload_2
     217: bipush        36
     219: ldc           #85                 // String antecedent
     221: aastore
     222: aload_2
     223: bipush        37
     225: ldc           #87                 // String company
     227: aastore
     228: aload_2
     229: bipush        38
     231: ldc           #89                 // String emissary
     233: aastore
     234: aload_2
     235: bipush        39
     237: ldc           #91                 // String slack
     239: aastore
     240: aload_2
     241: bipush        40
     243: ldc           #93                 // String break
     245: aastore
     246: aload_2
     247: bipush        41
     249: ldc           #95                 // String guava
     251: aastore
     252: aload_2
     253: bipush        42
     255: ldc           #97                 // String free
     257: aastore
     258: aload_2
     259: bipush        43
     261: ldc           #99                 // String hydraulic
     263: aastore
     264: aload_2
     265: bipush        44
     267: ldc           #101                // String man
     269: aastore
     270: aload_2
     271: bipush        45
     273: ldc           #103                // String dinner
     275: aastore
     276: aload_2
     277: bipush        46
     279: ldc           #105                // String games
     281: aastore
     282: aload_2
     283: bipush        47
     285: ldc           #107                // String assembly
     287: aastore
     288: aload_2
     289: bipush        48
     291: ldc           #109                // String manual
     293: aastore
     294: aload_2
     295: bipush        49
     297: ldc           #111                // String cloud
     299: aastore
     300: aload_2
     301: bipush        50
     303: ldc           #113                // String snow
     305: aastore
     306: aload_2
     307: bipush        51
     309: ldc           #115                // String operation
     311: aastore
     312: aload_2
     313: bipush        52
     315: ldc           #117                // String yesterday
     317: aastore
     318: aload_2
     319: bipush        53
     321: ldc           #119                // String duck
     323: aastore
     324: aload_2
     325: bipush        54
     327: ldc           #121                // String foot
     329: aastore
     330: aload_2
     331: bipush        55
     333: ldc           #123                // String trip
     335: aastore
     336: aload_2
     337: bipush        56
     339: ldc           #125                // String cheese
     341: aastore
     342: aload_2
     343: bipush        57
     345: ldc           #127                // String room
     347: aastore
     348: aload_2
     349: bipush        58
     351: ldc           #129                // String backyard
     353: aastore
     354: aload_2
     355: bipush        59
     357: ldc           #131                // String loose
     359: aastore
     360: aload_2
     361: bipush        60
     363: ldc           #133                // String route
     365: aastore
     366: aload_2
     367: bipush        61
     369: ldc           #135                // String jungle
     371: aastore
     372: aload_2
     373: bipush        62
     375: ldc           #137                // String tattoo
     377: aastore
     378: aload_2
     379: bipush        63
     381: ldc           #139                // String tiger
     383: aastore
     384: aload_2
     385: bipush        64
     387: ldc           #141                // String grape
     389: aastore
     390: aload_2
     391: bipush        65
     393: ldc           #143                // String last
     395: aastore
     396: aload_2
     397: bipush        66
     399: ldc           #145                // String reproach
     401: aastore
     402: aload_2
     403: bipush        67
     405: ldc           #147                // String voltage
     407: aastore
     408: aload_2
     409: bipush        68
     411: ldc           #149                // String angry
     413: aastore
     414: aload_2
     415: bipush        69
     417: ldc           #151                // String mockery
     419: aastore
     420: aload_2
     421: bipush        70
     423: ldc           #153                // String pain
     425: aastore
     426: aload_2
     427: astore_1
     428: iconst_0
     429: istore_2
     430: aload_1
     431: arraylength
     432: istore_3
     433: iload_2
     434: iload_3
     435: if_icmpge     535
     438: aload_1
     439: iload_2
     440: aaload
     441: astore        4
     443: aload         4
     445: invokevirtual #157                // Method java/lang/String.length:()I
     448: istore        5
     450: getstatic     #161                // Field root:Lcom/exemple/scrabble/Trie$TrieNode;
     453: astore        6
     455: iconst_0
     456: istore        7
     458: iload         7
     460: iload         5
     462: if_icmpge     523
     465: aload         4
     467: iload         7
     469: invokevirtual #165                // Method java/lang/String.charAt:(I)C
     472: bipush        97
     474: isub
     475: istore        8
     477: aload         6
     479: invokevirtual #171                // Method com/exemple/scrabble/Trie$TrieNode.getChild:()[Lcom/exemple/scrabble/Trie$TrieNode;
     482: iload         8
     484: aaload
     485: ifnonnull     503
     488: aload         6
     490: invokevirtual #171                // Method com/exemple/scrabble/Trie$TrieNode.getChild:()[Lcom/exemple/scrabble/Trie$TrieNode;
     493: iload         8
     495: new           #167                // class com/exemple/scrabble/Trie$TrieNode
     498: dup
     499: invokespecial #172                // Method com/exemple/scrabble/Trie$TrieNode."<init>":()V
     502: aastore
     503: aload         6
     505: invokevirtual #171                // Method com/exemple/scrabble/Trie$TrieNode.getChild:()[Lcom/exemple/scrabble/Trie$TrieNode;
     508: iload         8
     510: aaload
     511: dup
     512: invokestatic  #178                // Method kotlin/jvm/internal/Intrinsics.checkNotNull:(Ljava/lang/Object;)V
     515: astore        6
     517: iinc          7, 1
     520: goto          458
     523: aload         6
     525: iconst_1
     526: invokevirtual #182                // Method com/exemple/scrabble/Trie$TrieNode.setLeaf:(Z)V
     529: iinc          2, 1
     532: goto          433
     535: return

  public final kotlin.Pair<java.lang.String, java.lang.Integer> highestScore(char[]);
    Code:
       0: aload_1
       1: ldc           #198                // String arr
       3: invokestatic  #202                // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: bipush        10
       8: anewarray     #204                // class java/lang/Character
      11: astore_3
      12: aload_3
      13: iconst_0
      14: bipush        101
      16: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
      19: aastore
      20: aload_3
      21: iconst_1
      22: bipush        97
      24: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
      27: aastore
      28: aload_3
      29: iconst_2
      30: bipush        105
      32: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
      35: aastore
      36: aload_3
      37: iconst_3
      38: bipush        111
      40: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
      43: aastore
      44: aload_3
      45: iconst_4
      46: bipush        110
      48: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
      51: aastore
      52: aload_3
      53: iconst_5
      54: bipush        114
      56: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
      59: aastore
      60: aload_3
      61: bipush        6
      63: bipush        116
      65: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
      68: aastore
      69: aload_3
      70: bipush        7
      72: bipush        108
      74: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
      77: aastore
      78: aload_3
      79: bipush        8
      81: bipush        115
      83: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
      86: aastore
      87: aload_3
      88: bipush        9
      90: bipush        117
      92: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
      95: aastore
      96: aload_3
      97: astore_2
      98: iconst_3
      99: anewarray     #204                // class java/lang/Character
     102: astore        4
     104: aload         4
     106: iconst_0
     107: bipush        119
     109: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
     112: aastore
     113: aload         4
     115: iconst_1
     116: bipush        100
     118: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
     121: aastore
     122: aload         4
     124: iconst_2
     125: bipush        103
     127: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
     130: aastore
     131: aload         4
     133: astore_3
     134: iconst_4
     135: anewarray     #204                // class java/lang/Character
     138: astore        5
     140: aload         5
     142: iconst_0
     143: bipush        98
     145: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
     148: aastore
     149: aload         5
     151: iconst_1
     152: bipush        99
     154: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
     157: aastore
     158: aload         5
     160: iconst_2
     161: bipush        109
     163: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
     166: aastore
     167: aload         5
     169: iconst_3
     170: bipush        112
     172: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
     175: aastore
     176: aload         5
     178: astore        4
     180: iconst_3
     181: anewarray     #204                // class java/lang/Character
     184: astore        6
     186: aload         6
     188: iconst_0
     189: bipush        102
     191: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
     194: aastore
     195: aload         6
     197: iconst_1
     198: bipush        104
     200: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
     203: aastore
     204: aload         6
     206: iconst_2
     207: bipush        118
     209: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
     212: aastore
     213: aload         6
     215: astore        5
     217: iconst_2
     218: anewarray     #204                // class java/lang/Character
     221: astore        7
     223: aload         7
     225: iconst_0
     226: bipush        106
     228: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
     231: aastore
     232: aload         7
     234: iconst_1
     235: bipush        120
     237: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
     240: aastore
     241: aload         7
     243: astore        6
     245: iconst_2
     246: anewarray     #204                // class java/lang/Character
     249: astore        8
     251: aload         8
     253: iconst_0
     254: bipush        113
     256: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
     259: aastore
     260: aload         8
     262: iconst_1
     263: bipush        122
     265: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
     268: aastore
     269: aload         8
     271: astore        7
     273: aload_0
     274: aload_1
     275: invokespecial #212                // Method searchAllWords:([C)Ljava/util/ArrayList;
     278: astore        8
     280: aload_0
     281: aload_1
     282: aload         8
     284: invokespecial #216                // Method wordFilter:([CLjava/util/ArrayList;)V
     287: aload         8
     289: invokevirtual #221                // Method java/util/ArrayList.size:()I
     292: newarray       int
     294: astore        9
     296: iconst_0
     297: istore        10
     299: aload         8
     301: checkcast     #223                // class java/util/Collection
     304: invokeinterface #224,  1          // InterfaceMethod java/util/Collection.size:()I
     309: istore        11
     311: iload         10
     313: iload         11
     315: if_icmpge     567
     318: aload         8
     320: iload         10
     322: invokevirtual #228                // Method java/util/ArrayList.get:(I)Ljava/lang/Object;
     325: dup
     326: ldc           #230                // String get(...)
     328: invokestatic  #233                // Method kotlin/jvm/internal/Intrinsics.checkNotNullExpressionValue:(Ljava/lang/Object;Ljava/lang/String;)V
     331: checkcast     #13                 // class java/lang/String
     334: astore        12
     336: iconst_0
     337: istore        13
     339: aload         12
     341: invokevirtual #157                // Method java/lang/String.length:()I
     344: istore        14
     346: iload         13
     348: iload         14
     350: if_icmpge     561
     353: aload         12
     355: iload         13
     357: invokevirtual #165                // Method java/lang/String.charAt:(I)C
     360: istore        15
     362: iload         15
     364: istore        16
     366: aload_2
     367: iload         16
     369: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
     372: invokestatic  #239                // Method kotlin/collections/ArraysKt.contains:([Ljava/lang/Object;Ljava/lang/Object;)Z
     375: ifeq          397
     378: iload         10
     380: istore        17
     382: aload         9
     384: iload         17
     386: aload         9
     388: iload         17
     390: iaload
     391: iconst_1
     392: iadd
     393: iastore
     394: goto          555
     397: aload_3
     398: iload         16
     400: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
     403: invokestatic  #239                // Method kotlin/collections/ArraysKt.contains:([Ljava/lang/Object;Ljava/lang/Object;)Z
     406: ifeq          428
     409: iload         10
     411: istore        17
     413: aload         9
     415: iload         17
     417: aload         9
     419: iload         17
     421: iaload
     422: iconst_2
     423: iadd
     424: iastore
     425: goto          555
     428: aload         4
     430: iload         16
     432: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
     435: invokestatic  #239                // Method kotlin/collections/ArraysKt.contains:([Ljava/lang/Object;Ljava/lang/Object;)Z
     438: ifeq          460
     441: iload         10
     443: istore        17
     445: aload         9
     447: iload         17
     449: aload         9
     451: iload         17
     453: iaload
     454: iconst_3
     455: iadd
     456: iastore
     457: goto          555
     460: aload         5
     462: iload         16
     464: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
     467: invokestatic  #239                // Method kotlin/collections/ArraysKt.contains:([Ljava/lang/Object;Ljava/lang/Object;)Z
     470: ifeq          492
     473: iload         10
     475: istore        17
     477: aload         9
     479: iload         17
     481: aload         9
     483: iload         17
     485: iaload
     486: iconst_4
     487: iadd
     488: iastore
     489: goto          555
     492: aload         6
     494: iload         16
     496: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
     499: invokestatic  #239                // Method kotlin/collections/ArraysKt.contains:([Ljava/lang/Object;Ljava/lang/Object;)Z
     502: ifeq          525
     505: iload         10
     507: istore        17
     509: aload         9
     511: iload         17
     513: aload         9
     515: iload         17
     517: iaload
     518: bipush        8
     520: iadd
     521: iastore
     522: goto          555
     525: aload         7
     527: iload         16
     529: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
     532: invokestatic  #239                // Method kotlin/collections/ArraysKt.contains:([Ljava/lang/Object;Ljava/lang/Object;)Z
     535: ifeq          555
     538: iload         10
     540: istore        17
     542: aload         9
     544: iload         17
     546: aload         9
     548: iload         17
     550: iaload
     551: bipush        10
     553: iadd
     554: iastore
     555: iinc          13, 1
     558: goto          346
     561: iinc          10, 1
     564: goto          311
     567: aload         8
     569: checkcast     #241                // class java/lang/Iterable
     572: aload         9
     574: invokestatic  #245                // Method kotlin/collections/ArraysKt.toTypedArray:([I)[Ljava/lang/Integer;
     577: invokestatic  #251                // Method kotlin/collections/CollectionsKt.zip:(Ljava/lang/Iterable;[Ljava/lang/Object;)Ljava/util/List;
     580: astore        10
     582: aload         10
     584: checkcast     #241                // class java/lang/Iterable
     587: astore        11
     589: iconst_0
     590: istore        12
     592: aload         11
     594: new           #253                // class com/exemple/scrabble/Trie$highestScore$$inlined$sortedBy$1
     597: dup
     598: invokespecial #254                // Method com/exemple/scrabble/Trie$highestScore$$inlined$sortedBy$1."<init>":()V
     601: checkcast     #256                // class java/util/Comparator
     604: invokestatic  #260                // Method kotlin/collections/CollectionsKt.sortedWith:(Ljava/lang/Iterable;Ljava/util/Comparator;)Ljava/util/List;
     607: astore        10
     609: aload         10
     611: checkcast     #241                // class java/lang/Iterable
     614: astore        11
     616: iconst_0
     617: istore        12
     619: aload         11
     621: invokeinterface #264,  1          // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
     626: astore        13
     628: aload         13
     630: invokeinterface #270,  1          // InterfaceMethod java/util/Iterator.hasNext:()Z
     635: ifne          646
     638: new           #272                // class java/util/NoSuchElementException
     641: dup
     642: invokespecial #273                // Method java/util/NoSuchElementException."<init>":()V
     645: athrow
     646: aload         13
     648: invokeinterface #277,  1          // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     653: astore        14
     655: aload         13
     657: invokeinterface #270,  1          // InterfaceMethod java/util/Iterator.hasNext:()Z
     662: ifne          670
     665: aload         14
     667: goto          752
     670: aload         14
     672: checkcast     #279                // class kotlin/Pair
     675: astore        15
     677: iconst_0
     678: istore        16
     680: aload         15
     682: invokevirtual #282                // Method kotlin/Pair.getSecond:()Ljava/lang/Object;
     685: checkcast     #284                // class java/lang/Number
     688: invokevirtual #287                // Method java/lang/Number.intValue:()I
     691: istore        15
     693: aload         13
     695: invokeinterface #277,  1          // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     700: astore        16
     702: aload         16
     704: checkcast     #279                // class kotlin/Pair
     707: astore        17
     709: iconst_0
     710: istore        18
     712: aload         17
     714: invokevirtual #282                // Method kotlin/Pair.getSecond:()Ljava/lang/Object;
     717: checkcast     #284                // class java/lang/Number
     720: invokevirtual #287                // Method java/lang/Number.intValue:()I
     723: istore        17
     725: iload         15
     727: iload         17
     729: if_icmpge     740
     732: aload         16
     734: astore        14
     736: iload         17
     738: istore        15
     740: aload         13
     742: invokeinterface #270,  1          // InterfaceMethod java/util/Iterator.hasNext:()Z
     747: ifne          693
     750: aload         14
     752: checkcast     #279                // class kotlin/Pair
     755: areturn

  public final java.lang.String getRest(java.lang.String, char[]);
    Code:
       0: aload_1
       1: ldc_w         #327                // String word
       4: invokestatic  #202                // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       7: aload_2
       8: ldc           #198                // String arr
      10: invokestatic  #202                // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
      13: aload_2
      14: invokestatic  #331                // Method kotlin/collections/ArraysKt.toMutableList:([C)Ljava/util/List;
      17: astore_3
      18: iconst_0
      19: istore        4
      21: aload_1
      22: invokevirtual #157                // Method java/lang/String.length:()I
      25: istore        5
      27: iload         4
      29: iload         5
      31: if_icmpge     60
      34: aload_1
      35: iload         4
      37: invokevirtual #165                // Method java/lang/String.charAt:(I)C
      40: istore        6
      42: aload_3
      43: iload         6
      45: invokestatic  #208                // Method java/lang/Character.valueOf:(C)Ljava/lang/Character;
      48: invokeinterface #335,  2          // InterfaceMethod java/util/List.remove:(Ljava/lang/Object;)Z
      53: pop
      54: iinc          4, 1
      57: goto          27
      60: new           #13                 // class java/lang/String
      63: dup
      64: aload_3
      65: checkcast     #223                // class java/util/Collection
      68: invokestatic  #339                // Method kotlin/collections/CollectionsKt.toCharArray:(Ljava/util/Collection;)[C
      71: invokespecial #342                // Method java/lang/String."<init>":([C)V
      74: areturn

  private final java.util.ArrayList<java.lang.String> searchAllWords(char[]);
    Code:
       0: iconst_1
       1: anewarray     #13                 // class java/lang/String
       4: astore_3
       5: aload_3
       6: iconst_0
       7: ldc_w         #346                // String
      10: aastore
      11: aload_3
      12: invokestatic  #350                // Method kotlin/collections/CollectionsKt.arrayListOf:([Ljava/lang/Object;)Ljava/util/ArrayList;
      15: astore_2
      16: bipush        26
      18: newarray       boolean
      20: astore_3
      21: iconst_0
      22: istore        4
      24: aload_1
      25: arraylength
      26: istore        5
      28: iload         4
      30: iload         5
      32: if_icmpge     55
      35: aload_1
      36: iload         4
      38: caload
      39: istore        6
      41: aload_3
      42: iload         6
      44: bipush        97
      46: isub
      47: iconst_1
      48: bastore
      49: iinc          4, 1
      52: goto          28
      55: new           #352                // class java/lang/StringBuilder
      58: dup
      59: invokespecial #353                // Method java/lang/StringBuilder."<init>":()V
      62: astore        4
      64: iconst_0
      65: istore        5
      67: iload         5
      69: bipush        26
      71: if_icmpge     147
      74: aload_3
      75: iload         5
      77: baload
      78: ifeq          141
      81: getstatic     #161                // Field root:Lcom/exemple/scrabble/Trie$TrieNode;
      84: invokevirtual #171                // Method com/exemple/scrabble/Trie$TrieNode.getChild:()[Lcom/exemple/scrabble/Trie$TrieNode;
      87: iload         5
      89: aaload
      90: ifnull        141
      93: aload         4
      95: iload         5
      97: bipush        97
      99: iadd
     100: i2c
     101: invokevirtual #357                // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
     104: pop
     105: aload_0
     106: getstatic     #161                // Field root:Lcom/exemple/scrabble/Trie$TrieNode;
     109: invokevirtual #171                // Method com/exemple/scrabble/Trie$TrieNode.getChild:()[Lcom/exemple/scrabble/Trie$TrieNode;
     112: iload         5
     114: aaload
     115: aload_2
     116: aload_3
     117: aload         4
     119: invokevirtual #361                // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     122: dup
     123: ldc_w         #363                // String toString(...)
     126: invokestatic  #233                // Method kotlin/jvm/internal/Intrinsics.checkNotNullExpressionValue:(Ljava/lang/Object;Ljava/lang/String;)V
     129: invokespecial #367                // Method searchWord:(Lcom/exemple/scrabble/Trie$TrieNode;Ljava/util/ArrayList;[ZLjava/lang/String;)V
     132: new           #352                // class java/lang/StringBuilder
     135: dup
     136: invokespecial #353                // Method java/lang/StringBuilder."<init>":()V
     139: astore        4
     141: iinc          5, 1
     144: goto          67
     147: aload_2
     148: areturn

  private final void searchWord(com.exemple.scrabble.Trie$TrieNode, java.util.ArrayList<java.lang.String>, boolean[], java.lang.String);
    Code:
       0: aload_1
       1: dup
       2: invokestatic  #178                // Method kotlin/jvm/internal/Intrinsics.checkNotNull:(Ljava/lang/Object;)V
       5: invokevirtual #377                // Method com/exemple/scrabble/Trie$TrieNode.getLeaf:()Z
       8: ifeq          18
      11: aload_2
      12: aload         4
      14: invokevirtual #380                // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
      17: pop
      18: iconst_0
      19: istore        5
      21: iload         5
      23: bipush        26
      25: if_icmpge     92
      28: aload_3
      29: iload         5
      31: baload
      32: ifeq          86
      35: aload_1
      36: invokevirtual #171                // Method com/exemple/scrabble/Trie$TrieNode.getChild:()[Lcom/exemple/scrabble/Trie$TrieNode;
      39: iload         5
      41: aaload
      42: ifnull        86
      45: iload         5
      47: bipush        97
      49: iadd
      50: i2c
      51: istore        6
      53: aload_0
      54: aload_1
      55: invokevirtual #171                // Method com/exemple/scrabble/Trie$TrieNode.getChild:()[Lcom/exemple/scrabble/Trie$TrieNode;
      58: iload         5
      60: aaload
      61: aload_2
      62: aload_3
      63: new           #352                // class java/lang/StringBuilder
      66: dup
      67: invokespecial #353                // Method java/lang/StringBuilder."<init>":()V
      70: aload         4
      72: invokevirtual #383                // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      75: iload         6
      77: invokevirtual #357                // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
      80: invokevirtual #361                // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      83: invokespecial #367                // Method searchWord:(Lcom/exemple/scrabble/Trie$TrieNode;Ljava/util/ArrayList;[ZLjava/lang/String;)V
      86: iinc          5, 1
      89: goto          21
      92: return

  private final void wordFilter(char[], java.util.ArrayList<java.lang.String>);
    Code:
       0: bipush        26
       2: newarray       int
       4: astore_3
       5: new           #218                // class java/util/ArrayList
       8: dup
       9: invokespecial #386                // Method java/util/ArrayList."<init>":()V
      12: astore        4
      14: iconst_0
      15: istore        5
      17: aload_1
      18: arraylength
      19: istore        6
      21: iload         5
      23: iload         6
      25: if_icmpge     61
      28: aload_1
      29: iload         5
      31: caload
      32: istore        7
      34: iload         7
      36: bipush        97
      38: isub
      39: istore        8
      41: aload_3
      42: iload         8
      44: iaload
      45: istore        9
      47: aload_3
      48: iload         8
      50: iload         9
      52: iconst_1
      53: iadd
      54: iastore
      55: iinc          5, 1
      58: goto          21
      61: iconst_0
      62: istore        5
      64: iload         5
      66: bipush        26
      68: if_icmpge     224
      71: aload_3
      72: iload         5
      74: iaload
      75: ifle          218
      78: aload_2
      79: invokevirtual #387                // Method java/util/ArrayList.iterator:()Ljava/util/Iterator;
      82: dup
      83: ldc_w         #389                // String iterator(...)
      86: invokestatic  #233                // Method kotlin/jvm/internal/Intrinsics.checkNotNullExpressionValue:(Ljava/lang/Object;Ljava/lang/String;)V
      89: astore        6
      91: aload         6
      93: invokeinterface #270,  1          // InterfaceMethod java/util/Iterator.hasNext:()Z
      98: ifeq          218
     101: aload         6
     103: invokeinterface #277,  1          // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     108: dup
     109: ldc_w         #391                // String next(...)
     112: invokestatic  #233                // Method kotlin/jvm/internal/Intrinsics.checkNotNullExpressionValue:(Ljava/lang/Object;Ljava/lang/String;)V
     115: checkcast     #13                 // class java/lang/String
     118: astore        7
     120: aload         7
     122: checkcast     #393                // class java/lang/CharSequence
     125: astore        9
     127: iconst_0
     128: istore        10
     130: iconst_0
     131: istore        11
     133: iconst_0
     134: istore        12
     136: iload         12
     138: aload         9
     140: invokeinterface #394,  1          // InterfaceMethod java/lang/CharSequence.length:()I
     145: if_icmpge     194
     148: aload         9
     150: iload         12
     152: invokeinterface #395,  2          // InterfaceMethod java/lang/CharSequence.charAt:(I)C
     157: istore        13
     159: iload         13
     161: istore        14
     163: iconst_0
     164: istore        15
     166: iload         14
     168: iload         5
     170: bipush        97
     172: iadd
     173: i2c
     174: if_icmpne     181
     177: iconst_1
     178: goto          182
     181: iconst_0
     182: ifeq          188
     185: iinc          11, 1
     188: iinc          12, 1
     191: goto          136
     194: iload         11
     196: istore        8
     198: iload         8
     200: aload_3
     201: iload         5
     203: iaload
     204: if_icmple     91
     207: aload         4
     209: aload         7
     211: invokevirtual #380                // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
     214: pop
     215: goto          91
     218: iinc          5, 1
     221: goto          64
     224: aload         4
     226: invokevirtual #387                // Method java/util/ArrayList.iterator:()Ljava/util/Iterator;
     229: dup
     230: ldc_w         #389                // String iterator(...)
     233: invokestatic  #233                // Method kotlin/jvm/internal/Intrinsics.checkNotNullExpressionValue:(Ljava/lang/Object;Ljava/lang/String;)V
     236: astore        5
     238: aload         5
     240: invokeinterface #270,  1          // InterfaceMethod java/util/Iterator.hasNext:()Z
     245: ifeq          277
     248: aload         5
     250: invokeinterface #277,  1          // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     255: dup
     256: ldc_w         #391                // String next(...)
     259: invokestatic  #233                // Method kotlin/jvm/internal/Intrinsics.checkNotNullExpressionValue:(Ljava/lang/Object;Ljava/lang/String;)V
     262: checkcast     #13                 // class java/lang/String
     265: astore        6
     267: aload_2
     268: aload         6
     270: invokevirtual #396                // Method java/util/ArrayList.remove:(Ljava/lang/Object;)Z
     273: pop
     274: goto          238
     277: return

  static {};
    Code:
       0: new           #2                  // class com/exemple/scrabble/Trie
       3: dup
       4: invokespecial #407                // Method "<init>":()V
       7: putstatic     #410                // Field INSTANCE:Lcom/exemple/scrabble/Trie;
      10: new           #167                // class com/exemple/scrabble/Trie$TrieNode
      13: dup
      14: invokespecial #172                // Method com/exemple/scrabble/Trie$TrieNode."<init>":()V
      17: putstatic     #161                // Field root:Lcom/exemple/scrabble/Trie$TrieNode;
      20: return
}

Compiled from "Comparisons.kt"
public final class com.exemple.scrabble.Trie$highestScore$$inlined$sortedBy$1<T> implements java.util.Comparator {
  public com.exemple.scrabble.Trie$highestScore$$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.getFirst:()Ljava/lang/Object;
      12: checkcast     #29                 // class java/lang/String
      15: invokevirtual #33                 // Method java/lang/String.length:()I
      18: invokestatic  #39                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      21: checkcast     #41                 // class java/lang/Comparable
      24: aload_2
      25: checkcast     #23                 // class kotlin/Pair
      28: astore_3
      29: astore        5
      31: iconst_0
      32: istore        4
      34: aload_3
      35: invokevirtual #27                 // Method kotlin/Pair.getFirst:()Ljava/lang/Object;
      38: checkcast     #29                 // class java/lang/String
      41: invokevirtual #33                 // Method java/lang/String.length:()I
      44: invokestatic  #39                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      47: aload         5
      49: swap
      50: checkcast     #41                 // class java/lang/Comparable
      53: invokestatic  #47                 // Method kotlin/comparisons/ComparisonsKt.compareValues:(Ljava/lang/Comparable;Ljava/lang/Comparable;)I
      56: ireturn
}

Compiled from "Trie.kt"
public final class com.exemple.scrabble.Trie$TrieNode {
  private com.exemple.scrabble.Trie$TrieNode[] child;

  private boolean leaf;

  public com.exemple.scrabble.Trie$TrieNode();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: bipush        26
       7: anewarray     #2                  // class com/exemple/scrabble/Trie$TrieNode
      10: putfield      #12                 // Field child:[Lcom/exemple/scrabble/Trie$TrieNode;
      13: return

  public final com.exemple.scrabble.Trie$TrieNode[] getChild();
    Code:
       0: aload_0
       1: getfield      #12                 // Field child:[Lcom/exemple/scrabble/Trie$TrieNode;
       4: areturn

  public final void setChild(com.exemple.scrabble.Trie$TrieNode[]);
    Code:
       0: aload_1
       1: ldc           #21                 // String <set-?>
       3: invokestatic  #27                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: aload_1
       8: putfield      #12                 // Field child:[Lcom/exemple/scrabble/Trie$TrieNode;
      11: return

  public final boolean getLeaf();
    Code:
       0: aload_0
       1: getfield      #33                 // Field leaf:Z
       4: ireturn

  public final void setLeaf(boolean);
    Code:
       0: aload_0
       1: iload_1
       2: putfield      #33                 // Field leaf:Z
       5: return
}


package ren.natsuyuk1.comet.utils.string

import java.math.BigDecimal
import java.math.RoundingMode
import kotlin.math.max

/**
 * The Levenshtein distance between two words is the minimum number of
 * single-character edits (insertions, deletions or substitutions) required to
 * change one string into the other.
 *
 * @author <NAME>
 * @see https://github.com/tdebatty/java-string-similarity#levenshtein
 */
fun levenshteinDistance(s1: String, s2: String, limit: Int = Int.MAX_VALUE): Double {
    if (s1 == s2) {
        return 0.0
    }

    if (s1.isBlank()) {
        return s2.length.toDouble()
    }

    if (s2.isBlank()) {
        return s1.length.toDouble()
    }

    // create two work vectors of integer distances
    var v0 = IntArray(s2.length + 1)
    var v1 = IntArray(s2.length + 1)
    var vtemp: IntArray

    // initialize v0 (the previous row of distances)
    // this row is A[0][i]: edit distance for an empty s
    // the distance is just the number of characters to delete from t

    // initialize v0 (the previous row of distances)
    // this row is A[0][i]: edit distance for an empty s
    // the distance is just the number of characters to delete from t
    for (i in v0.indices) {
        v0[i] = i
    }

    for (i in s1.indices) {
        // calculate v1 (current row distances) from the previous row v0
        // first element of v1 is A[i+1][0]
        //   edit distance is delete (i+1) chars from s to match empty t
        v1[0] = i + 1
        var minv1 = v1[0]

        // use formula to fill in the rest of the row
        for (j in s2.indices) {
            var cost = 1
            if (s1.toCharArray()[i] == s2.toCharArray()[j]) {
                cost = 0
            }
            v1[j + 1] = // Cost of insertion
                // Cost of remove
                (v1[j] + 1).coerceAtMost((v0[j + 1] + 1).coerceAtMost(v0[j] + cost)) // Cost of substitution
            minv1 = minv1.coerceAtMost(v1[j + 1])
        }
        if (minv1 >= limit) {
            return limit.toDouble()
        }

        // copy v1 (current row) to v0 (previous row) for next iteration
        // System.arraycopy(v1, 0, v0, 0, v0.length);

        // Flip references to current and previous row
        vtemp = v0
        v0 = v1
        v1 = vtemp
    }

    return v0[s2.length].toDouble()
}

fun ldSimilarity(s1: String, s2: String): BigDecimal {
    val ld = levenshteinDistance(s1, s2)
    return BigDecimal.ONE.minus(
        BigDecimal.valueOf(ld).divide(BigDecimal.valueOf(max(s1.length, s2.length).toLong()), 2, RoundingMode.HALF_UP),
    )
}

Compiled from "FuzzyMatch.kt"
public final class ren.natsuyuk1.comet.utils.string.FuzzyMatchKt {
  public static final double levenshteinDistance(java.lang.String, java.lang.String, int);
    Code:
       0: aload_0
       1: ldc           #9                  // String s1
       3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_1
       7: ldc           #17                 // String s2
       9: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
      12: aload_0
      13: aload_1
      14: invokestatic  #21                 // Method kotlin/jvm/internal/Intrinsics.areEqual:(Ljava/lang/Object;Ljava/lang/Object;)Z
      17: ifeq          22
      20: dconst_0
      21: dreturn
      22: aload_0
      23: checkcast     #23                 // class java/lang/CharSequence
      26: invokestatic  #29                 // Method kotlin/text/StringsKt.isBlank:(Ljava/lang/CharSequence;)Z
      29: ifeq          38
      32: aload_1
      33: invokevirtual #35                 // Method java/lang/String.length:()I
      36: i2d
      37: dreturn
      38: aload_1
      39: checkcast     #23                 // class java/lang/CharSequence
      42: invokestatic  #29                 // Method kotlin/text/StringsKt.isBlank:(Ljava/lang/CharSequence;)Z
      45: ifeq          54
      48: aload_0
      49: invokevirtual #35                 // Method java/lang/String.length:()I
      52: i2d
      53: dreturn
      54: aload_1
      55: invokevirtual #35                 // Method java/lang/String.length:()I
      58: iconst_1
      59: iadd
      60: newarray       int
      62: astore_3
      63: aload_1
      64: invokevirtual #35                 // Method java/lang/String.length:()I
      67: iconst_1
      68: iadd
      69: newarray       int
      71: astore        4
      73: aconst_null
      74: astore        5
      76: iconst_0
      77: istore        6
      79: aload_3
      80: arraylength
      81: istore        7
      83: iload         6
      85: iload         7
      87: if_icmpge     102
      90: aload_3
      91: iload         6
      93: iload         6
      95: iastore
      96: iinc          6, 1
      99: goto          83
     102: iconst_0
     103: istore        6
     105: aload_0
     106: checkcast     #23                 // class java/lang/CharSequence
     109: invokeinterface #36,  1           // InterfaceMethod java/lang/CharSequence.length:()I
     114: istore        7
     116: iload         6
     118: iload         7
     120: if_icmpge     273
     123: aload         4
     125: iconst_0
     126: iload         6
     128: iconst_1
     129: iadd
     130: iastore
     131: aload         4
     133: iconst_0
     134: iaload
     135: istore        8
     137: iconst_0
     138: istore        9
     140: aload_1
     141: checkcast     #23                 // class java/lang/CharSequence
     144: invokeinterface #36,  1           // InterfaceMethod java/lang/CharSequence.length:()I
     149: istore        10
     151: iload         9
     153: iload         10
     155: if_icmpge     248
     158: iconst_1
     159: istore        11
     161: aload_0
     162: invokevirtual #40                 // Method java/lang/String.toCharArray:()[C
     165: dup
     166: ldc           #42                 // String toCharArray(...)
     168: invokestatic  #45                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullExpressionValue:(Ljava/lang/Object;Ljava/lang/String;)V
     171: iload         6
     173: caload
     174: aload_1
     175: invokevirtual #40                 // Method java/lang/String.toCharArray:()[C
     178: dup
     179: ldc           #42                 // String toCharArray(...)
     181: invokestatic  #45                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullExpressionValue:(Ljava/lang/Object;Ljava/lang/String;)V
     184: iload         9
     186: caload
     187: if_icmpne     193
     190: iconst_0
     191: istore        11
     193: aload         4
     195: iload         9
     197: iconst_1
     198: iadd
     199: aload         4
     201: iload         9
     203: iaload
     204: iconst_1
     205: iadd
     206: aload_3
     207: iload         9
     209: iconst_1
     210: iadd
     211: iaload
     212: iconst_1
     213: iadd
     214: aload_3
     215: iload         9
     217: iaload
     218: iload         11
     220: iadd
     221: invokestatic  #51                 // Method kotlin/ranges/RangesKt.coerceAtMost:(II)I
     224: invokestatic  #51                 // Method kotlin/ranges/RangesKt.coerceAtMost:(II)I
     227: iastore
     228: iload         8
     230: aload         4
     232: iload         9
     234: iconst_1
     235: iadd
     236: iaload
     237: invokestatic  #51                 // Method kotlin/ranges/RangesKt.coerceAtMost:(II)I
     240: istore        8
     242: iinc          9, 1
     245: goto          151
     248: iload         8
     250: iload_2
     251: if_icmplt     257
     254: iload_2
     255: i2d
     256: dreturn
     257: aload_3
     258: astore        5
     260: aload         4
     262: astore_3
     263: aload         5
     265: astore        4
     267: iinc          6, 1
     270: goto          116
     273: aload_3
     274: aload_1
     275: invokevirtual #35                 // Method java/lang/String.length:()I
     278: iaload
     279: i2d
     280: dreturn

  public static double levenshteinDistance$default(java.lang.String, java.lang.String, int, int, java.lang.Object);
    Code:
       0: iload_3
       1: iconst_4
       2: iand
       3: ifeq          9
       6: ldc           #66                 // int 2147483647
       8: istore_2
       9: aload_0
      10: aload_1
      11: iload_2
      12: invokestatic  #68                 // Method levenshteinDistance:(Ljava/lang/String;Ljava/lang/String;I)D
      15: dreturn

  public static final java.math.BigDecimal ldSimilarity(java.lang.String, java.lang.String);
    Code:
       0: aload_0
       1: ldc           #9                  // String s1
       3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_1
       7: ldc           #17                 // String s2
       9: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
      12: aload_0
      13: aload_1
      14: iconst_0
      15: iconst_4
      16: aconst_null
      17: invokestatic  #72                 // Method levenshteinDistance$default:(Ljava/lang/String;Ljava/lang/String;IILjava/lang/Object;)D
      20: dstore_2
      21: getstatic     #78                 // Field java/math/BigDecimal.ONE:Ljava/math/BigDecimal;
      24: dup
      25: ldc           #79                 // String ONE
      27: invokestatic  #45                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullExpressionValue:(Ljava/lang/Object;Ljava/lang/String;)V
      30: astore        4
      32: dload_2
      33: invokestatic  #83                 // Method java/math/BigDecimal.valueOf:(D)Ljava/math/BigDecimal;
      36: aload_0
      37: invokevirtual #35                 // Method java/lang/String.length:()I
      40: aload_1
      41: invokevirtual #35                 // Method java/lang/String.length:()I
      44: invokestatic  #88                 // Method java/lang/Math.max:(II)I
      47: i2l
      48: invokestatic  #91                 // Method java/math/BigDecimal.valueOf:(J)Ljava/math/BigDecimal;
      51: iconst_2
      52: getstatic     #97                 // Field java/math/RoundingMode.HALF_UP:Ljava/math/RoundingMode;
      55: invokevirtual #101                // Method java/math/BigDecimal.divide:(Ljava/math/BigDecimal;ILjava/math/RoundingMode;)Ljava/math/BigDecimal;
      58: dup
      59: ldc           #103                // String divide(...)
      61: invokestatic  #45                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullExpressionValue:(Ljava/lang/Object;Ljava/lang/String;)V
      64: astore        5
      66: aload         4
      68: aload         5
      70: invokevirtual #107                // Method java/math/BigDecimal.subtract:(Ljava/math/BigDecimal;)Ljava/math/BigDecimal;
      73: dup
      74: ldc           #109                // String subtract(...)
      76: invokestatic  #45                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullExpressionValue:(Ljava/lang/Object;Ljava/lang/String;)V
      79: areturn
}