diff --git a/CHANGELOG.md b/CHANGELOG.md index 6af9dbd7769..d23d1cf5cfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 0.17.3 + +* `Common`: + * Add `fixed` extensions for `Float` and `Double` + * New function `emptyDiff` + * Now you may pass custom `comparisonFun` to all `diff` functions + ## 0.17.2 * `FSM`: diff --git a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/DiffUtils.kt b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/DiffUtils.kt index 367c267ac77..167c94a21fd 100644 --- a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/DiffUtils.kt +++ b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/DiffUtils.kt @@ -34,7 +34,11 @@ data class Diff internal constructor( */ val replaced: List, @Serializable(IndexedValueSerializer::class) IndexedValue>>, val added: List<@Serializable(IndexedValueSerializer::class) IndexedValue> -) +) { + fun isEmpty(): Boolean = removed.isEmpty() && replaced.isEmpty() && added.isEmpty() +} + +fun emptyDiff(): Diff = Diff(emptyList(), emptyList(), emptyList()) private inline fun performChanges( potentialChanges: MutableList?, IndexedValue?>>, @@ -43,14 +47,14 @@ private inline fun performChanges( changedList: MutableList, IndexedValue>>, removedList: MutableList>, addedList: MutableList>, - strictComparison: Boolean + comparisonFun: (T?, T?) -> Boolean ) { var i = -1 val (oldObject, newObject) = potentialChanges.lastOrNull() ?: return for ((old, new) in potentialChanges.take(potentialChanges.size - 1)) { i++ - val oldOneEqualToNewObject = old ?.value === newObject ?.value || (old ?.value == newObject ?.value && !strictComparison) - val newOneEqualToOldObject = new ?.value === oldObject ?.value || (new ?.value == oldObject ?.value && !strictComparison) + val oldOneEqualToNewObject = comparisonFun(old ?.value, newObject ?.value) + val newOneEqualToOldObject = comparisonFun(new ?.value, oldObject ?.value) if (oldOneEqualToNewObject || newOneEqualToOldObject) { changedList.addAll( potentialChanges.take(i).mapNotNull { @@ -104,7 +108,7 @@ private inline fun performChanges( */ fun Iterable.calculateDiff( other: Iterable, - strictComparison: Boolean = false + comparisonFun: (T?, T?) -> Boolean ): Diff { var i = -1 var j = -1 @@ -132,7 +136,7 @@ fun Iterable.calculateDiff( } when { - oldObject === newObject || (oldObject == newObject && !strictComparison) -> { + comparisonFun(oldObject, newObject) -> { changedObjects.addAll(potentiallyChangedObjects.map { @Suppress("UNCHECKED_CAST") it as Pair, IndexedValue> @@ -143,23 +147,49 @@ fun Iterable.calculateDiff( potentiallyChangedObjects.add(oldObject ?.let { IndexedValue(i, oldObject) } to newObject ?.let { IndexedValue(j, newObject) }) val previousOldsAdditionsSize = additionalInOld.size val previousNewsAdditionsSize = additionalInNew.size - performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, strictComparison) + performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, comparisonFun) i -= (additionalInOld.size - previousOldsAdditionsSize) j -= (additionalInNew.size - previousNewsAdditionsSize) } } } potentiallyChangedObjects.add(null to null) - performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, strictComparison) + performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, comparisonFun) return Diff(removedObjects.toList(), changedObjects.toList(), addedObjects.toList()) } + +/** + * Calculating [Diff] object + * + * @param strictComparison If this parameter set to true, objects which are not equal by links will be used as different + * objects. For example, in case of two "Example" string they will be equal by value, but CAN be different by links + */ +fun Iterable.calculateDiff( + other: Iterable, + strictComparison: Boolean = false +): Diff = calculateDiff( + other, + comparisonFun = if (strictComparison) { + { t1, t2 -> + t1 === t2 + } + } else { + { t1, t2 -> + t1 === t2 || t1 == t2 // small optimization for cases when t1 and t2 are the same - comparison will be faster potentially + } + } +) inline fun Iterable.diff( other: Iterable, strictComparison: Boolean = false ): Diff = calculateDiff(other, strictComparison) +inline fun Iterable.diff( + other: Iterable, + noinline comparisonFun: (T?, T?) -> Boolean +): Diff = calculateDiff(other, comparisonFun) -inline fun Diff(old: Iterable, new: Iterable) = old.calculateDiff(new) +inline fun Diff(old: Iterable, new: Iterable) = old.calculateDiff(new, strictComparison = false) inline fun StrictDiff(old: Iterable, new: Iterable) = old.calculateDiff(new, true) /** @@ -187,3 +217,22 @@ fun MutableList.applyDiff( set(new.index, new.value) } } + +/** + * This method call [calculateDiff] with strict mode [strictComparison] and then apply differences to [this] + * mutable list + */ +fun MutableList.applyDiff( + source: Iterable, + comparisonFun: (T?, T?) -> Boolean +): Diff = calculateDiff(source, comparisonFun).also { + for (i in it.removed.indices.sortedDescending()) { + removeAt(it.removed[i].index) + } + it.added.forEach { (i, t) -> + add(i, t) + } + it.replaced.forEach { (_, new) -> + set(new.index, new.value) + } +} diff --git a/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/ToFixed.kt b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/ToFixed.kt new file mode 100644 index 00000000000..61797b470e2 --- /dev/null +++ b/common/src/commonMain/kotlin/dev/inmo/micro_utils/common/ToFixed.kt @@ -0,0 +1,6 @@ +package dev.inmo.micro_utils.common + +val FixedSignsRange = 0 .. 100 + +expect fun Float.fixed(signs: Int): Float +expect fun Double.fixed(signs: Int): Double diff --git a/common/src/jsMain/kotlin/dev/inmo/micro_utils/common/toFixed.kt b/common/src/jsMain/kotlin/dev/inmo/micro_utils/common/toFixed.kt new file mode 100644 index 00000000000..44a6d18fd34 --- /dev/null +++ b/common/src/jsMain/kotlin/dev/inmo/micro_utils/common/toFixed.kt @@ -0,0 +1,4 @@ +package dev.inmo.micro_utils.common + +actual fun Float.fixed(signs: Int): Float = this.asDynamic().toFixed(signs.coerceIn(FixedSignsRange)).unsafeCast().toFloat() +actual fun Double.fixed(signs: Int): Double = this.asDynamic().toFixed(signs.coerceIn(FixedSignsRange)).unsafeCast().toDouble() diff --git a/common/src/jvmMain/kotlin/dev/inmo/micro_utils/common/ToFixedActual.kt b/common/src/jvmMain/kotlin/dev/inmo/micro_utils/common/ToFixedActual.kt new file mode 100644 index 00000000000..9ef32711fce --- /dev/null +++ b/common/src/jvmMain/kotlin/dev/inmo/micro_utils/common/ToFixedActual.kt @@ -0,0 +1,12 @@ +package dev.inmo.micro_utils.common + +import java.math.BigDecimal +import java.math.RoundingMode + +actual fun Float.fixed(signs: Int): Float = BigDecimal.valueOf(this.toDouble()) + .setScale(signs.coerceIn(FixedSignsRange), RoundingMode.HALF_UP) + .toFloat(); + +actual fun Double.fixed(signs: Int): Double = BigDecimal.valueOf(this) + .setScale(signs.coerceIn(FixedSignsRange), RoundingMode.HALF_UP) + .toDouble(); diff --git a/gradle.properties b/gradle.properties index 9e210cbe693..7d16fe68871 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,5 +14,5 @@ crypto_js_version=4.1.1 # Project data group=dev.inmo -version=0.17.2 -android_code_version=184 +version=0.17.3 +android_code_version=185