All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.com.darkrockstudios.symspellkt.common.SpellHelper.kt Maven / Gradle / Ivy

package com.darkrockstudios.symspellkt.common

import kotlin.math.abs
import kotlin.math.min
import kotlin.math.roundToLong

object SpellHelper {
	/**
	 * Generate delete string set  for the Key.
	 */
	fun getEditDeletes(
		key: String,
		maxEditDistance: Double,
		prefixLength: Int,
		editFactor: Double,
	): Set {
		val deletedWords: MutableSet = mutableSetOf()
		var newKey = key
		if (key.length <= maxEditDistance) {
			deletedWords.add("")
		}
		if (key.length > maxEditDistance) {
			newKey = key.substring(0, if (prefixLength < key.length) prefixLength else key.length)
			deletedWords.add(newKey)
		}
		return edits(newKey, 0.0, deletedWords, getEdistance(maxEditDistance, newKey.length, editFactor))
	}

	private fun getEdistance(maxEditDistance: Double, length: Int, factor: Double): Double {
		val computedEd: Double = (factor * length).roundToLong().toDouble()
		if (min(maxEditDistance, computedEd) == maxEditDistance) {
			return maxEditDistance
		}
		return computedEd
	}

	/**
	 * Inexpensive and language independent: only deletes, no transposes + replaces + inserts replaces
	 * and inserts are expensive and language dependent
	 */
	fun edits(
		word: String,
		editDistance: Double,
		deletedWords: MutableSet,
		maxEd: Double,
	): Set {
		var runningEditDistance = editDistance
		runningEditDistance++
		if (word.isEmpty()) {
			return deletedWords
		}

		for (i in word.indices) {
			val delete = word.substring(0, i) + word.substring(i + 1, word.length)
			if (deletedWords.add(delete) && runningEditDistance < maxEd) {
				edits(delete, runningEditDistance, deletedWords, maxEd)
			}
		}
		deletedWords.add(word)
		return deletedWords
	}

	/**
	 * Early exit method
	 */
	fun earlyExit(
		suggestionItems: MutableList,
		phrase: String?,
		maxEditDistance: Double,
		topK: Int,
		ignoreUnknown: Boolean,
	): MutableList {
		if (suggestionItems.isEmpty() && !ignoreUnknown && phrase != null) {
			val si = SuggestionItem(phrase, maxEditDistance + 1.0, 0.0)
			suggestionItems.addItemSorted(si, topK)
		}
		return suggestionItems
	}

	fun tokenizeOnWhiteSpace(word: String): Array {
		return word.split("\\s+".toRegex()).toTypedArray()
	}

	fun isLessOrEqualDouble(d1: Double, d2: Double, threshold: Double = EPSILON): Boolean {
		return abs(d1 - d2) < threshold || d1 < d2
	}

	fun isLessDouble(d1: Double, d2: Double, threshold: Double = EPSILON): Boolean {
		return !isEqualDouble(d1, d2, threshold) && d1 < d2
	}

	fun isEqualDouble(d1: Double, d2: Double, threshold: Double = EPSILON): Boolean {
		return abs(d1 - d2) < threshold
	}

	/**
	 * Check if heads  are same
	 * @param suggestions
	 * @param suggestions1
	 * @return boolean
	 */
	fun continueConditionIfHeadIsSame(
		suggestions: List,
		suggestions1: List
	): Boolean {
		return (
				suggestions1.isEmpty()
						|| (suggestions.isNotEmpty()
						&& suggestions1.isNotEmpty()
						&& suggestions[0] == suggestions1[0])
				)
	}

	const val EPSILON = 0.011
}

/**
 * Add $item to the proper position in the list, such that the list
 * remains sorted. Removing an item from the list if necessary in order
 * to stay within the $maxSize.
 * 	 * @param item The item to add to the list
 * 	 * @param maxSize The maximum size of the list to not exceed
 * 	 * @return this list for chaining
 */
fun > MutableList.addItemSorted(item: T, maxSize: Int): MutableList {
	if (size >= maxSize) {
		val lastItem = this[maxSize - 1]
		if (lastItem <= item) {
			return this
		} else {
			removeAt(maxSize - 1)
		}
	}
	var index = binarySearch(item)
	if (index < 0) index = -index - 1
	add(index, item)
	return this
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy