From 579511a5ee0b598fd0e72863ad69f33456e84996 Mon Sep 17 00:00:00 2001 From: Gleb Minaev <43728100+lounres@users.noreply.github.com> Date: Sat, 16 Jul 2022 16:07:03 +0300 Subject: [PATCH] Add utilities for maps. Fix some tests. --- .../LabeledPolynomial.kt | 171 ++---- .../ListPolynomial.kt | 24 +- .../NumberedPolynomial.kt | 80 +-- .../collectionUtils.kt | 500 ++++++++++++++++++ .../labeledConstructors.kt | 50 +- .../labeledUtil.kt | 24 +- .../numberedConstructors.kt | 47 +- .../numberedUtil.kt | 10 +- .../functions/NumberedConstructorsTest.kt | 12 +- 9 files changed, 606 insertions(+), 312 deletions(-) create mode 100644 kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/collectionUtils.kt diff --git a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/LabeledPolynomial.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/LabeledPolynomial.kt index 12bf9f839..b07674a1e 100644 --- a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/LabeledPolynomial.kt +++ b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/LabeledPolynomial.kt @@ -150,12 +150,7 @@ public class LabeledPolynomialSpace>( else with(coefficients) { if (isEmpty()) other.asPolynomial() else LabeledPolynomialAsIs( - toMutableMap() - .apply { - val degs = emptyMap() - - this[degs] = getOrElse(degs) { constantZero } + other - } + withPutOrChanged(emptyMap(), other.asConstant()) { it -> it + other } ) } /** @@ -168,12 +163,7 @@ public class LabeledPolynomialSpace>( else with(coefficients) { if (isEmpty()) (-other).asPolynomial() else LabeledPolynomialAsIs( - toMutableMap() - .apply { - val degs = emptyMap() - - this[degs] = getOrElse(degs) { constantZero } - other - } + withPutOrChanged(emptyMap(), (-other).asConstant()) { it -> it - other } ) } /** @@ -186,11 +176,7 @@ public class LabeledPolynomialSpace>( 0 -> zero 1 -> this else -> LabeledPolynomialAsIs( - coefficients - .toMutableMap() - .apply { - for (degs in keys) this[degs] = this[degs]!! * other - } + coefficients.mapValues { (_, value) -> value * other } ) } @@ -204,12 +190,7 @@ public class LabeledPolynomialSpace>( else with(other.coefficients) { if (isEmpty()) this@plus.asPolynomial() else LabeledPolynomialAsIs( - toMutableMap() - .apply { - val degs = emptyMap() - - this[degs] = this@plus + getOrElse(degs) { constantZero } - } + withPutOrChanged(emptyMap(), this@plus.asConstant()) { it -> this@plus + it } ) } /** @@ -218,18 +199,14 @@ public class LabeledPolynomialSpace>( * The operation is equivalent to subtraction [this] copies of unit polynomial from [other]. */ public override operator fun Int.minus(other: LabeledPolynomial): LabeledPolynomial = - if (this == 0) -other - else with(other.coefficients) { - if (isEmpty()) this@minus.asPolynomial() - else LabeledPolynomialAsIs( - toMutableMap() - .apply { - forEach { (key, value) -> if (key.isNotEmpty()) this[key] = -value } - - val degs = emptyMap() - - this[degs] = this@minus - getOrElse(degs) { constantZero } - } + when { + this == 0 -> -other + other.coefficients.isEmpty() -> this@minus.asPolynomial() + else -> LabeledPolynomialAsIs( + buildMap(other.coefficients.size + 1) { + put(emptyMap(), asConstant()) + other.coefficients.copyMapToBy(this, { _, c -> -c }, { currentC, newC -> currentC - newC }) + } ) } /** @@ -242,11 +219,7 @@ public class LabeledPolynomialSpace>( 0 -> zero 1 -> other else -> LabeledPolynomialAsIs( - other.coefficients - .toMutableMap() - .apply { - for (degs in keys) this[degs] = this@times * this[degs]!! - } + other.coefficients.mapValues { (_, value) -> this@times * value } ) } @@ -305,41 +278,26 @@ public class LabeledPolynomialSpace>( with(other.coefficients) { if (isEmpty()) this@plus.asLabeledPolynomial() else LabeledPolynomialAsIs( - toMutableMap() - .apply { - val degs = emptyMap() - - this[degs] = this@plus + getOrElse(degs) { constantZero } - } + withPutOrChanged(emptyMap(), this@plus) { it -> this@plus + it } ) } /** * Returns difference between the constant represented as a polynomial and the polynomial. */ override operator fun C.minus(other: LabeledPolynomial): LabeledPolynomial = - with(other.coefficients) { - if (isEmpty()) this@minus.asLabeledPolynomial() - else LabeledPolynomialAsIs( - toMutableMap() - .apply { - forEach { (degs, c) -> if(degs.isNotEmpty()) this[degs] = -c } - - val degs = emptyMap() - - this[degs] = this@minus - getOrElse(degs) { constantZero } - } - ) - } + if (other.coefficients.isEmpty()) this@minus.asPolynomial() + else LabeledPolynomialAsIs( + buildMap(other.coefficients.size + 1) { + put(emptyMap(), this@minus) + other.coefficients.copyMapToBy(this, { _, c -> -c }, { currentC, newC -> currentC - newC }) + } + ) /** * Returns product of the constant represented as a polynomial and the polynomial. */ override operator fun C.times(other: LabeledPolynomial): LabeledPolynomial = LabeledPolynomialAsIs( - other.coefficients - .toMutableMap() - .apply { - for (degs in keys) this[degs] = this@times * this[degs]!! - } + other.coefficients.mapValues { this@times * it.value } ) /** @@ -349,12 +307,7 @@ public class LabeledPolynomialSpace>( with(coefficients) { if (isEmpty()) other.asLabeledPolynomial() else LabeledPolynomialAsIs( - toMutableMap() - .apply { - val degs = emptyMap() - - this[degs] = getOrElse(degs) { constantZero } + other - } + withPutOrChanged(emptyMap(), other) { it -> it + other } ) } /** @@ -364,12 +317,7 @@ public class LabeledPolynomialSpace>( with(coefficients) { if (isEmpty()) other.asLabeledPolynomial() else LabeledPolynomialAsIs( - toMutableMap() - .apply { - val degs = emptyMap() - - this[degs] = getOrElse(degs) { constantZero } - other - } + withPutOrChanged(emptyMap(), -other) { it -> it - other } ) } /** @@ -377,11 +325,7 @@ public class LabeledPolynomialSpace>( */ override operator fun LabeledPolynomial.times(other: C): LabeledPolynomial = LabeledPolynomialAsIs( - coefficients - .toMutableMap() - .apply { - for (degs in keys) this[degs] = this[degs]!! * other - } + coefficients.mapValues { it.value * other } ) /** @@ -441,38 +385,27 @@ public class LabeledPolynomialSpace>( with(other.coefficients) { if (isEmpty()) this@plus.asPolynomial() else LabeledPolynomialAsIs( - toMutableMap() - .apply { - val degs = mapOf(this@plus to 1U) - - this[degs] = constantOne + getOrElse(degs) { constantZero } - } + withPutOrChanged(mapOf(this@plus to 1U), constantOne) { it -> constantOne + it } ) } /** * Returns difference between the variable represented as a monic monomial and the polynomial. */ public override operator fun Symbol.minus(other: LabeledPolynomial): LabeledPolynomial = - with(other.coefficients) { - if (isEmpty()) this@minus.asPolynomial() - else LabeledPolynomialAsIs( - toMutableMap() - .apply { - val theVariableDegs = mapOf(this@minus to 1U) - - forEach { (degs, c) -> if(degs != theVariableDegs) this[degs] = -c } - - this[theVariableDegs] = constantOne - getOrElse(theVariableDegs) { constantZero } - } - ) - } + if (other.coefficients.isEmpty()) this@minus.asPolynomial() + else LabeledPolynomialAsIs( + buildMap(other.coefficients.size + 1) { + put(mapOf(this@minus to 1U), constantOne) + other.coefficients.copyMapToBy(this, { _, c -> -c }) { currentC, newC -> currentC - newC } + } + ) /** * Returns product of the variable represented as a monic monomial and the polynomial. */ public override operator fun Symbol.times(other: LabeledPolynomial): LabeledPolynomial = LabeledPolynomialAsIs( other.coefficients - .mapKeys { (degs, _) -> degs.toMutableMap().also{ it[this] = if (this in it) it[this]!! + 1U else 1U } } + .mapKeys { (degs, _) -> degs.withPutOrChanged(this, 1u) { it -> it + 1u } } ) /** @@ -482,12 +415,7 @@ public class LabeledPolynomialSpace>( with(coefficients) { if (isEmpty()) other.asPolynomial() else LabeledPolynomialAsIs( - toMutableMap() - .apply { - val degs = mapOf(other to 1U) - - this[degs] = constantOne + getOrElse(degs) { constantZero } - } + withPutOrChanged(mapOf(other to 1U), constantOne) { it -> it + constantOne } ) } /** @@ -497,12 +425,7 @@ public class LabeledPolynomialSpace>( with(coefficients) { if (isEmpty()) other.asPolynomial() else LabeledPolynomialAsIs( - toMutableMap() - .apply { - val degs = mapOf(other to 1U) - - this[degs] = getOrElse(degs) { constantZero } - constantOne - } + withPutOrChanged(mapOf(other to 1U), -constantOne) { it -> it - constantOne } ) } /** @@ -511,7 +434,7 @@ public class LabeledPolynomialSpace>( public override operator fun LabeledPolynomial.times(other: Symbol): LabeledPolynomial = LabeledPolynomialAsIs( coefficients - .mapKeys { (degs, _) -> degs.toMutableMap().also{ it[other] = if (other in it) it[other]!! + 1U else 1U } } + .mapKeys { (degs, _) -> degs.withPutOrChanged(other, 1u) { it -> it + 1u } } ) /** @@ -526,10 +449,7 @@ public class LabeledPolynomialSpace>( */ override operator fun LabeledPolynomial.plus(other: LabeledPolynomial): LabeledPolynomial = LabeledPolynomialAsIs( - buildMap(coefficients.size + other.coefficients.size) { - coefficients.mapValuesTo(this) { it.value } - other.coefficients.mapValuesTo(this) { (key, value) -> if (key in this) this[key]!! + value else value } - } + mergeBy(coefficients, other.coefficients) { c1, c2 -> c1 + c2 } ) /** * Returns difference of the polynomials. @@ -537,8 +457,8 @@ public class LabeledPolynomialSpace>( override operator fun LabeledPolynomial.minus(other: LabeledPolynomial): LabeledPolynomial = LabeledPolynomialAsIs( buildMap(coefficients.size + other.coefficients.size) { - coefficients.mapValuesTo(this) { it.value } - other.coefficients.mapValuesTo(this) { (key, value) -> if (key in this) this[key]!! - value else -value } + coefficients.copyTo(this) + other.coefficients.copyMapToBy(this, { _, c -> -c }, { currentC, newC -> currentC - newC }) } ) /** @@ -548,10 +468,9 @@ public class LabeledPolynomialSpace>( LabeledPolynomialAsIs( buildMap(coefficients.size * other.coefficients.size) { for ((degs1, c1) in coefficients) for ((degs2, c2) in other.coefficients) { - val degs = degs1.toMutableMap() - degs2.mapValuesTo(degs) { (variable, deg) -> degs.getOrElse(variable) { 0u } + deg } + val degs = mergeBy(degs1, degs2) { deg1, deg2 -> deg1 + deg2 } val c = c1 * c2 - this[degs] = if (degs in this) this[degs]!! + c else c + this.putOrChange(degs, c) { it -> it + c } } } ) @@ -581,10 +500,8 @@ public class LabeledPolynomialSpace>( public override val LabeledPolynomial.degrees: Map get() = buildMap { - coefficients.entries.forEach { (degs, _) -> - degs.mapValuesTo(this) { (variable, deg) -> - max(getOrElse(variable) { 0u }, deg) - } + coefficients.keys.forEach { degs -> + degs.copyToBy(this, ::max) } } /** diff --git a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/ListPolynomial.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/ListPolynomial.kt index 91b9c7658..17c42ac8c 100644 --- a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/ListPolynomial.kt +++ b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/ListPolynomial.kt @@ -109,11 +109,7 @@ public open class ListPolynomialSpace>( 0 -> zero 1 -> this else -> ListPolynomial( - coefficients - .toMutableList() - .apply { - for (deg in indices) this[deg] = this[deg] * other - } + coefficients.map { it * other } ) } @@ -167,11 +163,7 @@ public open class ListPolynomialSpace>( 0 -> zero 1 -> other else -> ListPolynomial( - other.coefficients - .toMutableList() - .apply { - for (deg in indices) this[deg] = this@times * this[deg] - } + other.coefficients.map { this@times * it } ) } @@ -214,11 +206,7 @@ public open class ListPolynomialSpace>( */ public override operator fun C.times(other: ListPolynomial): ListPolynomial = ListPolynomial( - other.coefficients - .toMutableList() - .apply { - for (deg in indices) this[deg] = this@times * this[deg] - } + other.coefficients.map { this@times * it } ) /** @@ -258,11 +246,7 @@ public open class ListPolynomialSpace>( */ public override operator fun ListPolynomial.times(other: C): ListPolynomial = ListPolynomial( - coefficients - .toMutableList() - .apply { - for (deg in indices) this[deg] = this[deg] * other - } + coefficients.map { it * other } ) /** diff --git a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/NumberedPolynomial.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/NumberedPolynomial.kt index 96c96e555..14c03ff7c 100644 --- a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/NumberedPolynomial.kt +++ b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/NumberedPolynomial.kt @@ -71,13 +71,7 @@ public class NumberedPolynomialSpace>( if (other == 0) this else NumberedPolynomialAsIs( - coefficients - .toMutableMap() - .apply { - val degs = emptyList() - - this[degs] = getOrElse(degs) { constantZero } + other - } + coefficients.withPutOrChanged(emptyList(), other.asConstant()) { it -> it + other } ) /** * Returns difference between the polynomial and the integer represented as a polynomial. @@ -88,13 +82,7 @@ public class NumberedPolynomialSpace>( if (other == 0) this else NumberedPolynomialAsIs( - coefficients - .toMutableMap() - .apply { - val degs = emptyList() - - this[degs] = getOrElse(degs) { constantZero } - other - } + coefficients.withPutOrChanged(emptyList(), (-other).asConstant()) { it -> it - other } ) /** * Returns product of the polynomial and the integer represented as a polynomial. @@ -106,11 +94,7 @@ public class NumberedPolynomialSpace>( 0 -> zero 1 -> this else -> NumberedPolynomialAsIs( - coefficients - .toMutableMap() - .apply { - for (degs in keys) this[degs] = this[degs]!! * other - } + coefficients.mapValues { it.value * other } ) } @@ -123,13 +107,7 @@ public class NumberedPolynomialSpace>( if (this == 0) other else NumberedPolynomialAsIs( - other.coefficients - .toMutableMap() - .apply { - val degs = emptyList() - - this[degs] = this@plus + getOrElse(degs) { constantZero } - } + other.coefficients.withPutOrChanged(emptyList(), this@plus.asConstant()) { it -> this@plus + it } ) /** * Returns difference between the integer represented as a polynomial and the polynomial. @@ -162,11 +140,7 @@ public class NumberedPolynomialSpace>( 0 -> zero 1 -> other else -> NumberedPolynomialAsIs( - other.coefficients - .toMutableMap() - .apply { - for (degs in keys) this[degs] = this@times * this[degs]!! - } + other.coefficients.mapValues { this@times * it.value } ) } @@ -177,12 +151,7 @@ public class NumberedPolynomialSpace>( with(other.coefficients) { if (isEmpty()) NumberedPolynomialAsIs(mapOf(emptyList() to this@plus)) else NumberedPolynomialAsIs( - toMutableMap() - .apply { - val degs = emptyList() - - this[degs] = this@plus + getOrElse(degs) { constantZero } - } + withPutOrChanged(emptyList(), this@plus) { it -> this@plus + it } ) } /** @@ -207,11 +176,7 @@ public class NumberedPolynomialSpace>( */ override operator fun C.times(other: NumberedPolynomial): NumberedPolynomial = NumberedPolynomialAsIs( - other.coefficients - .toMutableMap() - .apply { - for (degs in keys) this[degs] = this@times * this[degs]!! - } + other.coefficients.mapValues { this@times * it.value } ) /** @@ -221,12 +186,7 @@ public class NumberedPolynomialSpace>( with(coefficients) { if (isEmpty()) NumberedPolynomialAsIs(mapOf(emptyList() to other)) else NumberedPolynomialAsIs( - toMutableMap() - .apply { - val degs = emptyList() - - this[degs] = getOrElse(degs) { constantZero } + other - } + withPutOrChanged(emptyList(), other) { it -> it + other } ) } /** @@ -236,12 +196,7 @@ public class NumberedPolynomialSpace>( with(coefficients) { if (isEmpty()) NumberedPolynomialAsIs(mapOf(emptyList() to other)) else NumberedPolynomialAsIs( - toMutableMap() - .apply { - val degs = emptyList() - - this[degs] = getOrElse(degs) { constantZero } - other - } + withPutOrChanged(emptyList(), -other) { it -> it - other } ) } /** @@ -249,11 +204,7 @@ public class NumberedPolynomialSpace>( */ override operator fun NumberedPolynomial.times(other: C): NumberedPolynomial = NumberedPolynomialAsIs( - coefficients - .toMutableMap() - .apply { - for (degs in keys) this[degs] = this[degs]!! * other - } + coefficients.mapValues { it.value * other } ) /** @@ -274,10 +225,7 @@ public class NumberedPolynomialSpace>( */ override operator fun NumberedPolynomial.plus(other: NumberedPolynomial): NumberedPolynomial = NumberedPolynomialAsIs( - buildMap(coefficients.size + other.coefficients.size) { - coefficients.mapValuesTo(this) { it.value } - other.coefficients.mapValuesTo(this) { (key, value) -> if (key in this) this[key]!! + value else value } - } + mergeBy(coefficients, other.coefficients) { c1, c2 -> c1 + c2 } ) /** * Returns difference of the polynomials. @@ -285,8 +233,8 @@ public class NumberedPolynomialSpace>( override operator fun NumberedPolynomial.minus(other: NumberedPolynomial): NumberedPolynomial = NumberedPolynomialAsIs( buildMap(coefficients.size + other.coefficients.size) { - coefficients.mapValuesTo(this) { it.value } - other.coefficients.mapValuesTo(this) { (key, value) -> if (key in this) this[key]!! - value else -value } + coefficients.copyTo(this) + other.coefficients.copyMapToBy(this, { _, c -> -c }, { currentC, newC -> currentC - newC }) } ) /** @@ -300,7 +248,7 @@ public class NumberedPolynomialSpace>( (0..max(degs1.lastIndex, degs2.lastIndex)) .map { degs1.getOrElse(it) { 0U } + degs2.getOrElse(it) { 0U } } val c = c1 * c2 - this[degs] = if (degs in this) this[degs]!! + c else c + putOrChange(degs, c) { it -> it + c } } } ) diff --git a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/collectionUtils.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/collectionUtils.kt new file mode 100644 index 000000000..1d3da4c8b --- /dev/null +++ b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/collectionUtils.kt @@ -0,0 +1,500 @@ +/* + * Copyright 2018-2021 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package space.kscience.kmath.functions + +import kotlin.contracts.InvocationKind.* +import kotlin.contracts.contract + + +/** + * Applies the [transformation][transform] to the value corresponding to the given [key] or null instead if it's not + * present. + * + * @param key key to check. + * @param transform transformation to apply. + * @return result of the transformation + */ +internal inline fun MutableMap.applyToKey(key: K, transform: (currentValue: V?) -> V): V { + contract { + callsInPlace(transform, EXACTLY_ONCE) + } + return transform(get(key)).also { this[key] = it } +} + +/** + * Depending on presence of value corresponding to the given [key] either puts new value calculated by [valueOnPut] or + * changes the present value with [transformOnChange]. + * + * @param key key to check. + * @param valueOnPut lazily calculated value to put in case of absence of the [key]. + * @param transformOnChange transform to apply to current value corresponding to the [key] in case of its presence. Uses + * current value as a parameter. + * @return result value corresponding to the [key]. + */ +internal inline fun MutableMap.putOrChange(key: K, valueOnPut: () -> V, transformOnChange: (currentValue: V) -> V): V { + contract { + callsInPlace(valueOnPut, AT_MOST_ONCE) + callsInPlace(transformOnChange, AT_MOST_ONCE) + } + @Suppress("UNCHECKED_CAST") + return (if (key !in this) valueOnPut() else transformOnChange(get(key) as V)).also { this[key] = it } +} + +/** + * Depending on presence of value corresponding to the given [key] either puts new value [valueOnPut] or + * changes the present value with [transformOnChange]. + * + * @param key key to check. + * @param valueOnPut value to put in case of absence of the [key]. + * @param transformOnChange transform to apply to current value corresponding to the [key] in case of its presence. Uses + * current value as a parameter. + * @return result value corresponding to the [key]. + */ +internal inline fun MutableMap.putOrChange(key: K, valueOnPut: V, transformOnChange: (currentValue: V) -> V): V { + contract { + callsInPlace(transformOnChange, AT_MOST_ONCE) + } + return putOrChange(key, { valueOnPut }, transformOnChange) +} + +/** + * Depending on presence of value corresponding to the given [key] either puts new value [valueOnPut] or + * changes the present value with [transformOnChange]. + * + * @param key key to check. + * @param valueOnPut value to put in case of absence of the [key]. + * @param transformOnChange transform to apply to current value corresponding to the [key] in case of its presence. Uses + * current value and new value as parameters. + * @return result value corresponding to the [key]. + */ +internal inline fun MutableMap.putOrChange(key: K, valueOnPut: V, transformOnChange: (currentValue: V, newValue: V) -> V): V { + contract { + callsInPlace(transformOnChange, AT_MOST_ONCE) + } + return putOrChange(key, { valueOnPut }, { transformOnChange(it, valueOnPut) }) +} + +/** + * Depending on presence of value corresponding to the given [key] either puts new value [valueOnPut] or + * changes the present value with [transformOnChange]. + * + * @param key key to check. + * @param valueOnPut value to put in case of absence of the [key]. + * @param transformOnChange transform to apply to current value corresponding to the [key] in case of its presence. Uses + * the [key], current value, and new value as parameters. + * @return result value corresponding to the [key]. + */ +internal inline fun MutableMap.putOrChange(key: K, valueOnPut: V, transformOnChange: (key: K, currentValue: V, newValue: V) -> V): V { + contract { + callsInPlace(transformOnChange, AT_MOST_ONCE) + } + return putOrChange(key, { valueOnPut }, { transformOnChange(key, it, valueOnPut) }) +} + +/** + * Creates copy of [the map][this] and applies the [transformation][transform] to the value corresponding to the given + * [key] in the copy or null instead if it's not present. + * + * @param key key to check. + * @param transform transformation to apply. + * @return the copy of [the map][this]. + */ +internal inline fun Map.withAppliedToKey(key: K, transform: (currentValue: V?) -> V): Map { + contract { + callsInPlace(transform, EXACTLY_ONCE) + } + return buildMap(size) { + putAll(this) + applyToKey(key, transform) + } +} + +/** + * Creates copy of [the map][this] and depending on presence of value corresponding to the given [key] either puts new + * value calculated by [valueOnPut] or changes the present value with [transformOnChange]. + * + * @param key key to check. + * @param valueOnPut lazily calculated value to put in case of absence of the [key]. + * @param transformOnChange transform to apply to current value corresponding to the [key] in case of its presence. Uses + * current value as a parameter. + * @return the copy of [the map][this]. + */ +internal inline fun Map.withPutOrChanged(key: K, valueOnPut: () -> V, transformOnChange: (currentValue: V) -> V): Map { + contract { + callsInPlace(valueOnPut, AT_MOST_ONCE) + callsInPlace(transformOnChange, AT_MOST_ONCE) + } + return buildMap(size + 1) { + putAll(this@withPutOrChanged) + putOrChange(key, valueOnPut, transformOnChange) + } +} + +/** + * Creates copy of [the map][this] and depending on presence of value corresponding to the given [key] either puts new + * value [valueOnPut] or changes the present value with [transformOnChange]. + * + * @param key key to check. + * @param valueOnPut value to put in case of absence of the [key]. + * @param transformOnChange transform to apply to current value corresponding to the [key] in case of its presence. Uses + * current value as a parameter. + * @return the copy of [the map][this]. + */ +internal inline fun Map.withPutOrChanged(key: K, valueOnPut: V, transformOnChange: (currentValue: V) -> V): Map { + contract { + callsInPlace(transformOnChange, AT_MOST_ONCE) + } + return withPutOrChanged(key, { valueOnPut }, transformOnChange) +} + +/** + * Creates copy of [the map][this] and depending on presence of value corresponding to the given [key] either puts new + * value [valueOnPut] or changes the present value with [transformOnChange]. + * + * @param key key to check. + * @param valueOnPut value to put in case of absence of the [key]. + * @param transformOnChange transform to apply to current value corresponding to the [key] in case of its presence. Uses + * current value and new value as parameters. + * @return the copy of [the map][this]. + */ +internal inline fun Map.withPutOrChanged(key: K, valueOnPut: V, transformOnChange: (currentValue: V, newValue: V) -> V): Map { + contract { + callsInPlace(transformOnChange, AT_MOST_ONCE) + } + return withPutOrChanged(key, { valueOnPut }, { transformOnChange(it, valueOnPut) }) +} + +/** + * Creates copy of [the map][this] and depending on presence of value corresponding to the given [key] either puts new + * value [valueOnPut] or changes the present value with [transformOnChange]. + * + * @param key key to check. + * @param valueOnPut value to put in case of absence of the [key]. + * @param transformOnChange transform to apply to current value corresponding to the [key] in case of its presence. Uses + * the [key], current value, and new value as parameters. + * @return the copy of [the map][this]. + */ +internal inline fun Map.withPutOrChanged(key: K, valueOnPut: V, transformOnChange: (key: K, currentValue: V, newValue: V) -> V): Map { + contract { + callsInPlace(transformOnChange, AT_MOST_ONCE) + } + return withPutOrChanged(key, { valueOnPut }, { transformOnChange(key, it, valueOnPut) }) +} + +/** + * Copies entries of [this map][this] to the [destination] map overriding present ones if needed. + * + * @receiver map to be copied. + * @param destination map to receive copies. + * @return the [destination]. + */ +internal fun > Map.copyTo(destination: D): D { + for ((key, value) in this) { + destination[key] = value + } + return destination +} + +/** + * Copies entries of [this map][this] to the [destination] map merging present entries with new ones using [resolve] + * lambda. + * + * @receiver map to be copied. + * @param destination map to receive copies. + * @param resolve lambda function that resolves overriding. It takes a key, current value corresponding to the key, and + * a new one and returns value to associate to the key. + * @return the [destination]. + */ +internal inline fun > Map.copyToBy(destination: D, resolve: (key: K, currentValue: W, newValue: V) -> W): D { + for ((key, value) in this) { + destination.putOrChange(key, value) { it -> resolve(key, it, value) } + } + return destination +} + +/** + * Copies entries of [this map][this] to the [destination] map merging present entries with new ones using [resolve] + * lambda. + * + * @receiver map to be copied. + * @param destination map to receive copies. + * @param resolve lambda function that resolves overriding. It takes current value corresponding to some key, and + * a new one and returns value to associate to the key. + * @return the [destination]. + */ +internal inline fun > Map.copyToBy(destination: D, resolve: (currentValue: W, newValue: V) -> W): D = + copyToBy(destination) { _, currentValue, newValue -> resolve(currentValue, newValue) } + +/** + * Transforms values of entries of [this map][this] with [the given transformation][transform] and copies resulting + * entries to the [destination] map overriding present ones if needed. Is equivalent to + * ```kotlin + * this.mapValues(transform).copyTo(destination) + * ``` + * + * @receiver map to be transformed and copied. + * @param destination map to receive copies. + * @param transform generates value of transformed entry using initial entry as an argument. Key of transformed entry is + * the same as initial entry. + * @return the [destination]. + */ +internal inline fun > Map.copyMapTo(destination: D, transform: (Map.Entry) -> W): D { + for (entry in this) { + destination[entry.key] = transform(entry) + } + return destination +} + +/** + * Transforms values of entries of [this map][this] with [the given transformation][transform] and copies resulting + * entries to the [destination] map overriding present ones if needed. Is equivalent to + * ```kotlin + * this.mapValues(transform).copyTo(destination) + * ``` + * + * @receiver map to be transformed and copied. + * @param destination map to receive copies. + * @param transform generates value of transformed entry using initial entry as an argument. Key of transformed entry is + * the same as initial entry. + * @return the [destination]. + */ +internal inline fun > Map.copyMapTo(destination: D, transform: (key: K, value: V) -> W): D = + copyMapTo(destination) { (key, value) -> transform(key, value) } + +/** + * Transforms values of entries of [this map][this] with [the given transformation][transform] and copies resulting + * entries to the [destination] map merging present entries with new ones using [resolve] lambda. Is equivalent to + * ```kotlin + * this.mapValues(transform).copyToBy(destination, resolve) + * ``` + * + * @receiver map to be transformed and copied. + * @param destination map to receive copies. + * @param transform generates value of transformed entry using initial entry as an argument. Key of transformed entry is + * the same as initial entry. + * @param resolve lambda function that resolves overriding. It takes a key, current value corresponding to the key, and + * a new one and returns value to associate to the key. + * @return the [destination]. + */ +internal inline fun > Map.copyMapToBy(destination: D, transform: (Map.Entry) -> W, resolve: (key: K, currentValue: W, newValue: V) -> W): D { + for (entry in this) { + val (key, value) = entry + destination.putOrChange(key, transform(entry)) { it -> resolve(key, it, value) } + } + return destination +} + +/** + * Transforms values of entries of [this map][this] with [the given transformation][transform] and copies resulting + * entries to the [destination] map merging present entries with new ones using [resolve] lambda. Is equivalent to + * ```kotlin + * this.mapValues(transform).copyToBy(destination, resolve) + * ``` + * + * @receiver map to be transformed and copied. + * @param destination map to receive copies. + * @param transform generates value of transformed entry using initial entry as an argument. Key of transformed entry is + * the same as initial entry. + * @param resolve lambda function that resolves overriding. It takes a key, current value corresponding to the key, and + * a new one and returns value to associate to the key. + * @return the [destination]. + */ +internal inline fun > Map.copyMapToBy(destination: D, transform: (key: K, value: V) -> W, resolve: (key: K, currentValue: W, newValue: V) -> W): D = + copyMapToBy(destination, { (key, value) -> transform(key, value) }, resolve) + +/** + * Transforms values of entries of [this map][this] with [the given transformation][transform] and copies resulting + * entries to the [destination] map merging present entries with new ones using [resolve] lambda. Is equivalent to + * ```kotlin + * this.mapValues(transform).copyToBy(destination, resolve) + * ``` + * + * @receiver map to be transformed and copied. + * @param destination map to receive copies. + * @param transform generates value of transformed entry using initial entry as an argument. Key of transformed entry is + * the same as initial entry. + * @param resolve lambda function that resolves overriding. It takes current value corresponding to some key, and + * a new one and returns value to associate to the key. + * @return the [destination]. + */ +internal inline fun > Map.copyMapToBy(destination: D, transform: (Map.Entry) -> W, resolve: (currentValue: W, newValue: V) -> W): D = + copyMapToBy(destination, transform, { _, currentValue, newValue -> resolve(currentValue, newValue) }) + +/** + * Transforms values of entries of [this map][this] with [the given transformation][transform] and copies resulting + * entries to the [destination] map merging present entries with new ones using [resolve] lambda. Is equivalent to + * ```kotlin + * this.mapValues(transform).copyToBy(destination, resolve) + * ``` + * + * @receiver map to be transformed and copied. + * @param destination map to receive copies. + * @param transform generates value of transformed entry using initial entry as an argument. Key of transformed entry is + * the same as initial entry. + * @param resolve lambda function that resolves overriding. It takes current value corresponding to some key, and + * a new one and returns value to associate to the key. + * @return the [destination]. + */ +internal inline fun > Map.copyMapToBy(destination: D, transform: (key: K, value: V) -> W, resolve: (currentValue: W, newValue: V) -> W): D = + copyMapToBy(destination, { (key, value) -> transform(key, value) }, { _, currentValue, newValue -> resolve(currentValue, newValue) }) + +// TODO: Docs +internal fun > mergeTo(map1: Map, map2: Map, destination: D): D { + for ((key, value) in map1) { + destination.put(key, value) + } + for ((key, value) in map2) { + destination.put(key, value) + } + return destination +} + +// TODO: Docs +internal inline fun > mergeToBy(map1: Map, map2: Map, destination: D, resolve: (key: K, value1: V1, value2: V2) -> W): D { + for (key in map2.keys) { + destination.remove(key) + } + for ((key, value) in map1) { + destination.put(key, value) + } + for ((key, value) in map2) { + @Suppress("UNCHECKED_CAST") + destination.putOrChange(key, value) { it -> resolve(key, it as V1, value) } + } + return destination +} + +// TODO: Docs +internal inline fun > mergeToBy(map1: Map, map2: Map, destination: D, resolve: (value1: V1, value2: V2) -> W): D = + mergeToBy(map1, map2, destination) { _, value1, value2 -> resolve(value1, value2) } + +// TODO: Docs +internal fun merge(map1: Map, map2: Map): Map { + val result = LinkedHashMap(map1.size + map2.size) + return mergeTo(map1, map2, result) +} + +// TODO: Docs +internal inline fun mergeBy(map1: Map, map2: Map, transform: (key: K, value1: V1, value2: V2) -> W): Map { + val result = LinkedHashMap(map1.size + map2.size) + return mergeToBy(map1, map2, result, transform) +} + +// TODO: Docs +internal inline fun mergeBy(map1: Map, map2: Map, transform: (value1: V1, value2: V2) -> W): Map = + mergeBy(map1, map2) { _, value1, value2 -> transform(value1, value2) } + +// TODO: Docs +internal inline fun > Iterable.associateTo(destination: D, transform: (T) -> Pair, resolve: (key: K, currentValue: V, newValue: V) -> V): D { + for (element in this) { + val (key, value) = transform(element) + destination.putOrChange(key, value, resolve) + } + return destination +} + +// TODO: Docs +internal inline fun > Iterable.associateByTo(destination: D, keySelector: (T) -> K, valueTransform: (T) -> V, resolve: (key: K, currentValue: V, newValue: V) -> V): D { + for (element in this) { + val key = keySelector(element) + val value = valueTransform(element) + destination.putOrChange(key, value, resolve) + } + return destination +} + +// TODO: Docs +internal inline fun > Iterable.associateByTo(destination: D, keySelector: (T) -> K, resolve: (key: K, currentValue: T, newValue: T) -> T): D { + for (element in this) { + val key = keySelector(element) + destination.putOrChange(key, element, resolve) + } + return destination +} + +// TODO: Docs +internal inline fun > Iterable.associateTo(destination: D, transform: (T) -> Pair, resolve: (currentValue: V, newValue: V) -> V): D = + associateTo(destination, transform) { _, currentValue, newValue -> resolve(currentValue, newValue) } + +// TODO: Docs +internal inline fun > Iterable.associateByTo(destination: D, keySelector: (T) -> K, valueTransform: (T) -> V, resolve: (currentValue: V, newValue: V) -> V): D = + associateByTo(destination, keySelector, valueTransform) { _, currentValue, newValue -> resolve(currentValue, newValue) } + +// TODO: Docs +internal inline fun > Iterable.associateByTo(destination: D, keySelector: (T) -> K, resolve: (currentValue: T, newValue: T) -> T): D = + associateByTo(destination, keySelector) { _, currentValue, newValue -> resolve(currentValue, newValue) } + +// TODO: Docs +internal inline fun Iterable.associate(transform: (T) -> Pair, resolve: (key: K, currentValue: V, newValue: V) -> V): Map = + associateTo(LinkedHashMap(), transform, resolve) + +// TODO: Docs +internal inline fun Iterable.associateBy(keySelector: (T) -> K, valueTransform: (T) -> V, resolve: (key: K, currentValue: V, newValue: V) -> V): Map = + associateByTo(LinkedHashMap(), keySelector, valueTransform, resolve) + +// TODO: Docs +internal inline fun Iterable.associateBy(keySelector: (T) -> K, resolve: (key: K, currentValue: T, newValue: T) -> T): Map = + associateByTo(LinkedHashMap(), keySelector, resolve) + +// TODO: Docs +internal inline fun Iterable.associate(transform: (T) -> Pair, resolve: (currentValue: V, newValue: V) -> V): Map = + associateTo(LinkedHashMap(), transform, resolve) + +// TODO: Docs +internal inline fun Iterable.associateBy(keySelector: (T) -> K, valueTransform: (T) -> V, resolve: (currentValue: V, newValue: V) -> V): Map = + associateByTo(LinkedHashMap(), keySelector, valueTransform, resolve) + +// TODO: Docs +internal inline fun Iterable.associateBy(keySelector: (T) -> K, resolve: (currentValue: T, newValue: T) -> T): Map = + associateByTo(LinkedHashMap(), keySelector, resolve) + +// TODO: Docs +internal inline fun > Map.mapValuesTo(destination: D, transform: (Map.Entry) -> W, resolve: (key: K, currentValue: W, newValue: W) -> W): D = + entries.associateByTo(destination, { it.key }, transform, resolve) + +// TODO: Docs +internal inline fun > Map.mapValuesTo(destination: D, transform: (key: K, value: V) -> W, resolve: (key: K, currentValue: W, newValue: W) -> W): D = + entries.associateByTo(destination, { it.key }, { (key, value) -> transform(key, value) }, resolve) + +// TODO: Docs +internal inline fun > Map.mapValuesTo(destination: D, transform: (Map.Entry) -> W, resolve: (currentValue: W, newValue: W) -> W): D = + entries.associateByTo(destination, { it.key }, transform, resolve) + +// TODO: Docs +internal inline fun > Map.mapValuesTo(destination: D, transform: (key: K, value: V) -> W, resolve: (currentValue: W, newValue: W) -> W): D = + entries.associateByTo(destination, { it.key }, { (key, value) -> transform(key, value) }, resolve) + +// TODO: Docs +internal inline fun > Map.mapKeysTo(destination: D, transform: (Map.Entry) -> L, resolve: (key: L, currentValue: V, newValue: V) -> V): D = + entries.associateByTo(destination, transform, { it.value }, resolve) + +// TODO: Docs +internal inline fun > Map.mapKeysTo(destination: D, transform: (key: K, value: V) -> L, resolve: (key: L, currentValue: V, newValue: V) -> V): D = + entries.associateByTo(destination, { (key, value) -> transform(key, value) }, { it.value }, resolve) + +// TODO: Docs +internal inline fun > Map.mapKeysTo(destination: D, transform: (Map.Entry) -> L, resolve: (currentValue: V, newValue: V) -> V): D = + entries.associateByTo(destination, transform, { it.value }, resolve) + +// TODO: Docs +internal inline fun > Map.mapKeysTo(destination: D, transform: (key: K, value: V) -> L, resolve: (currentValue: V, newValue: V) -> V): D = + entries.associateByTo(destination, { (key, value) -> transform(key, value) }, { it.value }, resolve) + +// TODO: Docs +internal inline fun Map.mapKeys(transform: (Map.Entry) -> L, resolve: (key: L, currentValue: V, newValue: V) -> V): Map = + mapKeysTo(LinkedHashMap(size), transform, resolve) + +// TODO: Docs +internal inline fun Map.mapKeys(transform: (key: K, value: V) -> L, resolve: (key: L, currentValue: V, newValue: V) -> V): Map = + mapKeysTo(LinkedHashMap(size), transform, resolve) + +// TODO: Docs +internal inline fun Map.mapKeys(transform: (Map.Entry) -> L, resolve: (currentValue: V, newValue: V) -> V): Map = + mapKeysTo(LinkedHashMap(size), transform, resolve) + +// TODO: Docs +internal inline fun Map.mapKeys(transform: (key: K, value: V) -> L, resolve: (currentValue: V, newValue: V) -> V): Map = + mapKeysTo(LinkedHashMap(size), transform, resolve) \ No newline at end of file diff --git a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/labeledConstructors.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/labeledConstructors.kt index aa44660c1..43048089e 100644 --- a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/labeledConstructors.kt +++ b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/labeledConstructors.kt @@ -10,6 +10,7 @@ package space.kscience.kmath.functions import space.kscience.kmath.expressions.Symbol import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.operations.Ring +import space.kscience.kmath.operations.invoke /** @@ -76,17 +77,10 @@ public inline fun LabeledPolynomialWithoutCheck(vararg pairs: Pair LabeledPolynomial(coefs: Map, C>, add: (C, C) -> C) : LabeledPolynomial { - val fixedCoefs = mutableMapOf, C>() - - for (entry in coefs) { - val key = entry.key.cleanUp() - val value = entry.value - fixedCoefs[key] = if (key in fixedCoefs) add(fixedCoefs[key]!!, value) else value - } - - return LabeledPolynomial(fixedCoefs) -} +public fun LabeledPolynomial(coefs: Map, C>, add: (C, C) -> C) : LabeledPolynomial = + LabeledPolynomialAsIs( + coefs.mapKeys({ key, _ -> key.cleanUp() }, add) + ) /** * Constructs [LabeledPolynomial] with provided collection of [pairs] of pairs "term's signature — term's coefficient". @@ -98,17 +92,10 @@ public fun LabeledPolynomial(coefs: Map, C>, add: (C, C) - * * @see LabeledPolynomialWithoutCheck */ -public fun LabeledPolynomial(pairs: Collection, C>>, add: (C, C) -> C) : LabeledPolynomial { - val fixedCoefs = mutableMapOf, C>() - - for (entry in pairs) { - val key = entry.first.cleanUp() - val value = entry.second - fixedCoefs[key] = if (key in fixedCoefs) add(fixedCoefs[key]!!, value) else value - } - - return LabeledPolynomial(fixedCoefs) -} +public fun LabeledPolynomial(pairs: Collection, C>>, add: (C, C) -> C) : LabeledPolynomial = + LabeledPolynomialAsIs( + pairs.associateBy({ it.first.cleanUp() }, { it.second }, add) + ) /** * Constructs [LabeledPolynomial] with provided array [pairs] of pairs "term's signature — term's coefficient". @@ -120,17 +107,10 @@ public fun LabeledPolynomial(pairs: Collection, C>>, * * @see LabeledPolynomialWithoutCheck */ -public fun LabeledPolynomial(vararg pairs: Pair, C>, add: (C, C) -> C) : LabeledPolynomial { - val fixedCoefs = mutableMapOf, C>() - - for (entry in pairs) { - val key = entry.first.cleanUp() - val value = entry.second - fixedCoefs[key] = if (key in fixedCoefs) add(fixedCoefs[key]!!, value) else value - } - - return LabeledPolynomial(fixedCoefs) -} +public fun LabeledPolynomial(vararg pairs: Pair, C>, add: (C, C) -> C) : LabeledPolynomial = + LabeledPolynomialAsIs( + pairs.asIterable().associateBy({ it.first.cleanUp() }, { it.second }, add) + ) // Waiting for context receivers :( FIXME: Replace with context receivers when they will be available @@ -304,7 +284,7 @@ public class DSL1LabeledPolynomialTermSignatureBuilder { */ public infix fun Symbol.inPowerOf(deg: UInt) { if (deg == 0u) return - signature[this] = signature.getOrElse(this) { 0u } + deg + signature.putOrChange(this, deg) { it -> it + deg } } /** * Declares power of [this] variable of degree [deg]. @@ -362,7 +342,7 @@ public class DSL1LabeledPolynomialBuilder( * coefficients is zero at any moment the monomial won't be removed but will be left as it is. */ public infix fun C.with(signature: Map) { - coefficients[signature] = if (signature in coefficients) add(coefficients[signature]!!, this@with) else this@with + coefficients.putOrChange(signature, this@with, add) } /** * Declares monomial with [this] coefficient and signature constructed by [block]. diff --git a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/labeledUtil.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/labeledUtil.kt index e3b35facc..a37a1fe39 100644 --- a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/labeledUtil.kt +++ b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/labeledUtil.kt @@ -7,10 +7,7 @@ package space.kscience.kmath.functions import space.kscience.kmath.expressions.Symbol import space.kscience.kmath.misc.UnstableKMathAPI -import space.kscience.kmath.operations.Field -import space.kscience.kmath.operations.Ring -import space.kscience.kmath.operations.algebra -import space.kscience.kmath.operations.invoke +import space.kscience.kmath.operations.* import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.jvm.JvmName @@ -56,7 +53,7 @@ public fun LabeledPolynomial.substitute(args: Map): Labe val deg = degs.getOrElse(variable) { 0u } if (deg == 0u) product else product * power(substitution, deg) } - this[newDegs] = if (newDegs in this) this[newDegs]!! + newC else newC + putOrChange(newDegs, newC, ::add) } } ) @@ -75,7 +72,7 @@ public fun LabeledPolynomial.substitute(ring: Ring, args: Map> LabeledPolynomial.antiderivativeWithRespectTo( buildMap(coefficients.size) { coefficients .forEach { (degs, c) -> - val newDegs = buildMap(degs.size + 1) { - put(variable, 1u) - for ((vari, deg) in degs) put(vari, deg + getOrElse(vari) { 0u }) - } + val newDegs = degs.withPutOrChanged(variable, 1u) { it -> it + 1u } put( newDegs, c / multiplyByDoubling(one, newDegs[variable]!!) @@ -284,10 +278,7 @@ public fun > LabeledPolynomial.nthAntiderivativeWithRespectTo buildMap(coefficients.size) { coefficients .forEach { (degs, c) -> - val newDegs = buildMap(degs.size + 1) { - put(variable, order) - for ((vari, deg) in degs) put(vari, deg + getOrElse(vari) { 0u }) - } + val newDegs = degs.withPutOrChanged(variable, order) { it -> it + order } put( newDegs, newDegs[variable]!!.let { deg -> @@ -314,10 +305,7 @@ public fun > LabeledPolynomial.nthAntiderivativeWithRespectTo buildMap(coefficients.size) { coefficients .forEach { (degs, c) -> - val newDegs = buildMap(degs.size + 1) { - for ((variable, order) in filteredVariablesAndOrders) put(variable, order) - for ((vari, deg) in degs) put(vari, deg + getOrElse(vari) { 0u }) - } + val newDegs = mergeBy(degs, filteredVariablesAndOrders) { deg, order -> deg + order } put( newDegs, filteredVariablesAndOrders.entries.fold(c) { acc1, (index, order) -> diff --git a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/numberedConstructors.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/numberedConstructors.kt index ce0db3d17..ce49088ae 100644 --- a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/numberedConstructors.kt +++ b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/numberedConstructors.kt @@ -75,17 +75,10 @@ public inline fun NumberedPolynomialWithoutCheck(vararg pairs: Pair NumberedPolynomial(coefs: Map, C>, add: (C, C) -> C) : NumberedPolynomial { - val fixedCoefs = mutableMapOf, C>() - - for (entry in coefs) { - val key = entry.key.cleanUp() - val value = entry.value - fixedCoefs[key] = if (key in fixedCoefs) add(fixedCoefs[key]!!, value) else value - } - - return NumberedPolynomial(fixedCoefs) -} +public fun NumberedPolynomial(coefs: Map, C>, add: (C, C) -> C) : NumberedPolynomial = + NumberedPolynomialAsIs( + coefs.mapKeys({ key, _ -> key.cleanUp() }, add) + ) /** * Constructs [NumberedPolynomial] with provided collection of [pairs] of pairs "term's signature — term's coefficient". @@ -97,17 +90,10 @@ public fun NumberedPolynomial(coefs: Map, C>, add: (C, C) -> C) : * * @see NumberedPolynomialWithoutCheck */ -public fun NumberedPolynomial(pairs: Collection, C>>, add: (C, C) -> C) : NumberedPolynomial { - val fixedCoefs = mutableMapOf, C>() - - for (entry in pairs) { - val key = entry.first.cleanUp() - val value = entry.second - fixedCoefs[key] = if (key in fixedCoefs) add(fixedCoefs[key]!!, value) else value - } - - return NumberedPolynomial(fixedCoefs) -} +public fun NumberedPolynomial(pairs: Collection, C>>, add: (C, C) -> C) : NumberedPolynomial = + NumberedPolynomialAsIs( + pairs.associateBy({ it.first.cleanUp() }, { it.second }, add) + ) /** * Constructs [NumberedPolynomial] with provided array [pairs] of pairs "term's signature — term's coefficient". @@ -119,17 +105,10 @@ public fun NumberedPolynomial(pairs: Collection, C>>, add: ( * * @see NumberedPolynomialWithoutCheck */ -public fun NumberedPolynomial(vararg pairs: Pair, C>, add: (C, C) -> C) : NumberedPolynomial { - val fixedCoefs = mutableMapOf, C>() - - for (entry in pairs) { - val key = entry.first.cleanUp() - val value = entry.second - fixedCoefs[key] = if (key in fixedCoefs) add(fixedCoefs[key]!!, value) else value - } - - return NumberedPolynomial(fixedCoefs) -} +public fun NumberedPolynomial(vararg pairs: Pair, C>, add: (C, C) -> C) : NumberedPolynomial = + NumberedPolynomialAsIs( + pairs.asIterable().associateBy({ it.first.cleanUp() }, { it.second }, add) + ) // Waiting for context receivers :( FIXME: Replace with context receivers when they will be available @@ -349,7 +328,7 @@ public class DSL1NumberedPolynomialBuilder( * coefficients is zero at any moment the monomial won't be removed but will be left as it is. */ public infix fun C.with(signature: List) { - coefficients[signature] = if (signature in coefficients) add(coefficients[signature]!!, this@with) else this@with + coefficients.putOrChange(signature, this@with, add) } /** * Declares monomial with [this] coefficient and signature constructed by [block]. diff --git a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/numberedUtil.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/numberedUtil.kt index 9d88cd648..9f29cf31a 100644 --- a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/numberedUtil.kt +++ b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/numberedUtil.kt @@ -58,7 +58,7 @@ public fun NumberedPolynomial.substitute(args: Map): Number val deg = degs.getOrElse(variable) { 0u } if (deg == 0u) product else product * substitution.pow(deg.toInt()) } - this[newDegs] = if (newDegs !in this) newC else this[newDegs]!! + newC + putOrChange(newDegs, newC) { it -> it + newC } } } ) @@ -76,7 +76,7 @@ public fun NumberedPolynomial.substitute(ring: Ring, args: Map val deg = degs.getOrElse(variable) { 0u } if (deg == 0u) product else product * power(substitution, deg) } - this[newDegs] = if (newDegs !in this) newC else this[newDegs]!! + newC + putOrChange(newDegs, newC) { it -> it + newC } } } ) @@ -158,8 +158,7 @@ public fun NumberedPolynomial.substitute(args: Buffer): Numbered val deg = degs[variable] if (deg == 0u) product else product * args[variable].pow(deg.toInt()) } - if (newDegs !in this) this[newDegs] = newC - else this[newDegs] = this[newDegs]!! + newC + putOrChange(newDegs, newC) { it -> it + newC } } } ) @@ -183,8 +182,7 @@ public fun NumberedPolynomial.substitute(ring: Ring, args: Buffer): val deg = degs[variable] if (deg == 0u) product else product * power(args[variable], deg) } - if (newDegs !in this) this[newDegs] = newC - else this[newDegs] = this[newDegs]!! + newC + putOrChange(newDegs, newC) { it -> it + newC } } } ) diff --git a/kmath-polynomial/src/commonTest/kotlin/space/kscience/kmath/functions/NumberedConstructorsTest.kt b/kmath-polynomial/src/commonTest/kotlin/space/kscience/kmath/functions/NumberedConstructorsTest.kt index f9a5a041b..1815749ce 100644 --- a/kmath-polynomial/src/commonTest/kotlin/space/kscience/kmath/functions/NumberedConstructorsTest.kt +++ b/kmath-polynomial/src/commonTest/kotlin/space/kscience/kmath/functions/NumberedConstructorsTest.kt @@ -23,8 +23,8 @@ class NumberedConstructorsTest { ), Int.algebra.numberedPolynomialSpace { NumberedPolynomialDSL1 { - 5 { 1 pow 2u; 3 pow 3u } - (-6) { 2 pow 1u } + 5 { 0 pow 2u; 2 pow 3u } + (-6) { 1 pow 1u } } }, "test 1" @@ -47,8 +47,8 @@ class NumberedConstructorsTest { ), Int.algebra.numberedPolynomialSpace { NumberedPolynomialDSL1 { - 5 { 1 pow 1u; 1 pow 1u } - (-6) { 1 pow 2u } + 5 { 0 pow 1u; 0 pow 1u } + (-6) { 0 pow 2u } } }, "test 3" @@ -59,8 +59,8 @@ class NumberedConstructorsTest { ), Int.algebra.numberedPolynomialSpace { NumberedPolynomialDSL1 { - 5 { 1 pow 1u; 1 pow 1u } - (-6) { 1 pow 2u; 3 pow 0u } + 5 { 0 pow 1u; 0 pow 1u } + (-6) { 0 pow 2u; 2 pow 0u } } }, "test 3"