From 51dd72e48f2043a00bc2a5da75dbf8b6c1c2039b Mon Sep 17 00:00:00 2001 From: Gleb Minaev <43728100+lounres@users.noreply.github.com> Date: Mon, 11 Jul 2022 22:39:13 +0300 Subject: [PATCH] Finish move. --- examples/build.gradle.kts | 2 +- .../build.gradle.kts | 4 +- .../LabeledPolynomial.kt | 0 .../LabeledRationalFunction.kt | 0 .../ListPolynomial.kt | 387 ++++++++++++++ .../ListRationalFunction.kt | 0 .../NumberedPolynomial.kt | 0 .../NumberedRationalFunction.kt | 0 .../Polynomial.kt | 502 ++++++++++++++++++ .../RationalFunction.kt | 3 +- .../algebraicStub.kt | 0 .../labeledConstructors.kt | 0 .../labeledUtil.kt | 2 +- .../listConstructors.kt | 92 ++++ .../listUtil.kt | 255 +++++++++ .../space.kscience.kmath.functions/misc.kt | 22 + .../numberedConstructors.kt | 0 .../numberedUtil.kt | 0 .../functions/LabeledConstructorsTest.kt | 6 +- .../kmath/functions/LabeledPolynomialTest.kt | 10 +- .../functions/LabeledPolynomialUtilTest.kt | 9 +- .../functions/NumberedConstructorsTest.kt | 0 .../kmath/functions/NumberedPolynomialTest.kt | 0 .../functions/NumberedPolynomialUtilTest.kt | 0 settings.gradle.kts | 4 +- .../build.gradle.kts | 4 +- .../kmath/functions/testUtils/BufferUtils.kt | 0 .../kmath/functions/testUtils/assertion.kt | 0 .../kmath/functions/testUtils/misc.kt | 0 29 files changed, 1278 insertions(+), 24 deletions(-) rename {kmath-polynomialX => kmath-polynomial}/build.gradle.kts (81%) rename {kmath-polynomialX => kmath-polynomial}/src/commonMain/kotlin/space.kscience.kmath.functions/LabeledPolynomial.kt (100%) rename {kmath-polynomialX => kmath-polynomial}/src/commonMain/kotlin/space.kscience.kmath.functions/LabeledRationalFunction.kt (100%) create mode 100644 kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/ListPolynomial.kt rename {kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions => kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions}/ListRationalFunction.kt (100%) rename {kmath-polynomialX => kmath-polynomial}/src/commonMain/kotlin/space.kscience.kmath.functions/NumberedPolynomial.kt (100%) rename {kmath-polynomialX => kmath-polynomial}/src/commonMain/kotlin/space.kscience.kmath.functions/NumberedRationalFunction.kt (100%) create mode 100644 kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/Polynomial.kt rename {kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions => kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions}/RationalFunction.kt (99%) rename {kmath-polynomialX => kmath-polynomial}/src/commonMain/kotlin/space.kscience.kmath.functions/algebraicStub.kt (100%) rename {kmath-polynomialX => kmath-polynomial}/src/commonMain/kotlin/space.kscience.kmath.functions/labeledConstructors.kt (100%) rename {kmath-polynomialX => kmath-polynomial}/src/commonMain/kotlin/space.kscience.kmath.functions/labeledUtil.kt (99%) create mode 100644 kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/listConstructors.kt create mode 100644 kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/listUtil.kt create mode 100644 kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/misc.kt rename {kmath-polynomialX => kmath-polynomial}/src/commonMain/kotlin/space.kscience.kmath.functions/numberedConstructors.kt (100%) rename {kmath-polynomialX => kmath-polynomial}/src/commonMain/kotlin/space.kscience.kmath.functions/numberedUtil.kt (100%) rename {kmath-polynomialX => kmath-polynomial}/src/commonTest/kotlin/space/kscience/kmath/functions/LabeledConstructorsTest.kt (99%) rename {kmath-polynomialX => kmath-polynomial}/src/commonTest/kotlin/space/kscience/kmath/functions/LabeledPolynomialTest.kt (99%) rename {kmath-polynomialX => kmath-polynomial}/src/commonTest/kotlin/space/kscience/kmath/functions/LabeledPolynomialUtilTest.kt (99%) rename {kmath-polynomialX => kmath-polynomial}/src/commonTest/kotlin/space/kscience/kmath/functions/NumberedConstructorsTest.kt (100%) rename {kmath-polynomialX => kmath-polynomial}/src/commonTest/kotlin/space/kscience/kmath/functions/NumberedPolynomialTest.kt (100%) rename {kmath-polynomialX => kmath-polynomial}/src/commonTest/kotlin/space/kscience/kmath/functions/NumberedPolynomialUtilTest.kt (100%) rename {test-utils-polynomialX => test-utils-polynomial}/build.gradle.kts (63%) rename {test-utils-polynomialX => test-utils-polynomial}/src/commonMain/kotlin/space/kscience/kmath/functions/testUtils/BufferUtils.kt (100%) rename {test-utils-polynomialX => test-utils-polynomial}/src/commonMain/kotlin/space/kscience/kmath/functions/testUtils/assertion.kt (100%) rename {test-utils-polynomialX => test-utils-polynomial}/src/commonMain/kotlin/space/kscience/kmath/functions/testUtils/misc.kt (100%) diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index 67b28cd85..aa5c1f47a 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -16,7 +16,7 @@ dependencies { implementation(project(":kmath-commons")) implementation(project(":kmath-complex")) implementation(project(":kmath-functions")) - implementation(project(":kmath-polynomialX")) + implementation(project(":kmath-polynomial")) implementation(project(":kmath-optimization")) implementation(project(":kmath-stat")) implementation(project(":kmath-viktor")) diff --git a/kmath-polynomialX/build.gradle.kts b/kmath-polynomial/build.gradle.kts similarity index 81% rename from kmath-polynomialX/build.gradle.kts rename to kmath-polynomial/build.gradle.kts index 51b3c0665..dcfcb1b46 100644 --- a/kmath-polynomialX/build.gradle.kts +++ b/kmath-polynomial/build.gradle.kts @@ -10,13 +10,11 @@ kotlin.sourceSets { commonMain { dependencies { api(projects.kmathCore) - api(projects.kmathFunctions) } } commonTest { dependencies { - api(projects.testUtilsFunctions) - api(projects.testUtilsPolynomialX) + api(projects.testUtilsPolynomial) api(kotlin("test")) } } diff --git a/kmath-polynomialX/src/commonMain/kotlin/space.kscience.kmath.functions/LabeledPolynomial.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/LabeledPolynomial.kt similarity index 100% rename from kmath-polynomialX/src/commonMain/kotlin/space.kscience.kmath.functions/LabeledPolynomial.kt rename to kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/LabeledPolynomial.kt diff --git a/kmath-polynomialX/src/commonMain/kotlin/space.kscience.kmath.functions/LabeledRationalFunction.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/LabeledRationalFunction.kt similarity index 100% rename from kmath-polynomialX/src/commonMain/kotlin/space.kscience.kmath.functions/LabeledRationalFunction.kt rename to kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/LabeledRationalFunction.kt 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 new file mode 100644 index 000000000..76e1a6bb6 --- /dev/null +++ b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/ListPolynomial.kt @@ -0,0 +1,387 @@ +/* + * 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. + */ + +@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress") + +package space.kscience.kmath.functions + +import space.kscience.kmath.operations.Ring +import space.kscience.kmath.operations.ScaleOperations +import space.kscience.kmath.operations.invoke +import kotlin.math.max +import kotlin.math.min + + +/** + * Represents univariate polynomial that stores its coefficients in a [List]. + * + * @param C the type of constants. + */ +public data class ListPolynomial( + /** + * List that contains coefficients of the polynomial. Every monomial `a x^d` is stored as a coefficient `a` placed + * into the list at index `d`. For example, coefficients of a polynomial `5 x^2 - 6` can be represented as + * ``` + * listOf( + * -6, // -6 + + * 0, // 0 x + + * 5, // 5 x^2 + * ) + * ``` + * and also as + * ``` + * listOf( + * -6, // -6 + + * 0, // 0 x + + * 5, // 5 x^2 + * 0, // 0 x^3 + * 0, // 0 x^4 + * ) + * ``` + * It is not prohibited to put extra zeros at end of the list (as for `0x^3` and `0x^4` in the example). But the + * longer the coefficients list the worse performance of arithmetical operations performed on it. Thus, it is + * recommended not to put (or even to remove) extra (or useless) coefficients at the end of the coefficients list. + */ + public val coefficients: List +) : Polynomial { + override fun toString(): String = "ListPolynomial$coefficients" +} + +/** + * Arithmetic context for univariate polynomials with coefficients stored as a [List] constructed with the provided + * [ring] of constants. + * + * @param C the type of constants. Polynomials have them a coefficients in their terms. + * @param A type of provided underlying ring of constants. It's [Ring] of [C]. + * @param ring underlying ring of constants of type [A]. + */ +public open class ListPolynomialSpace>( + public override val ring: A, +) : PolynomialSpaceOverRing, A> { + /** + * Returns sum of the polynomial and the integer represented as a polynomial. + * + * The operation is equivalent to adding [other] copies of unit polynomial to [this]. + */ + public override operator fun ListPolynomial.plus(other: Int): ListPolynomial = + if (other == 0) this + else + ListPolynomial( + coefficients + .toMutableList() + .apply { + val result = getOrElse(0) { constantZero } + other + + if(size == 0) add(result) + else this[0] = result + } + ) + /** + * Returns difference between the polynomial and the integer represented as a polynomial. + * + * The operation is equivalent to subtraction [other] copies of unit polynomial from [this]. + */ + public override operator fun ListPolynomial.minus(other: Int): ListPolynomial = + if (other == 0) this + else + ListPolynomial( + coefficients + .toMutableList() + .apply { + val result = getOrElse(0) { constantZero } - other + + if(size == 0) add(result) + else this[0] = result + } + ) + /** + * Returns product of the polynomial and the integer represented as a polynomial. + * + * The operation is equivalent to sum of [other] copies of [this]. + */ + public override operator fun ListPolynomial.times(other: Int): ListPolynomial = + when (other) { + 0 -> zero + 1 -> this + else -> ListPolynomial( + coefficients + .toMutableList() + .apply { + for (deg in indices) this[deg] = this[deg] * other + } + ) + } + + /** + * Returns sum of the integer represented as a polynomial and the polynomial. + * + * The operation is equivalent to adding [this] copies of unit polynomial to [other]. + */ + public override operator fun Int.plus(other: ListPolynomial): ListPolynomial = + if (this == 0) other + else + ListPolynomial( + other.coefficients + .toMutableList() + .apply { + val result = this@plus + getOrElse(0) { constantZero } + + if(size == 0) add(result) + else this[0] = result + } + ) + /** + * Returns difference between the integer represented as a polynomial and the polynomial. + * + * The operation is equivalent to subtraction [this] copies of unit polynomial from [other]. + */ + public override operator fun Int.minus(other: ListPolynomial): ListPolynomial = + ListPolynomial( + other.coefficients + .toMutableList() + .apply { + if (this@minus == 0) { + indices.forEach { this[it] = -this[it] } + } else { + (1..lastIndex).forEach { this[it] = -this[it] } + + val result = this@minus - getOrElse(0) { constantZero } + + if (size == 0) add(result) + else this[0] = result + } + } + ) + /** + * Returns product of the integer represented as a polynomial and the polynomial. + * + * The operation is equivalent to sum of [this] copies of [other]. + */ + public override operator fun Int.times(other: ListPolynomial): ListPolynomial = + when (this) { + 0 -> zero + 1 -> other + else -> ListPolynomial( + other.coefficients + .toMutableList() + .apply { + for (deg in indices) this[deg] = this@times * this[deg] + } + ) + } + + /** + * Returns sum of the constant represented as a polynomial and the polynomial. + */ + public override operator fun C.plus(other: ListPolynomial): ListPolynomial = + with(other.coefficients) { + if (isEmpty()) ListPolynomial(listOf(this@plus)) + else ListPolynomial( + toMutableList() + .apply { + val result = if (size == 0) this@plus else this@plus + get(0) + + if(size == 0) add(result) + else this[0] = result + } + ) + } + /** + * Returns difference between the constant represented as a polynomial and the polynomial. + */ + public override operator fun C.minus(other: ListPolynomial): ListPolynomial = + with(other.coefficients) { + if (isEmpty()) ListPolynomial(listOf(this@minus)) + else ListPolynomial( + toMutableList() + .apply { + (1 .. lastIndex).forEach { this[it] = -this[it] } + + val result = if (size == 0) this@minus else this@minus - get(0) + + if(size == 0) add(result) + else this[0] = result + } + ) + } + /** + * Returns product of the constant represented as a polynomial and the polynomial. + */ + public override operator fun C.times(other: ListPolynomial): ListPolynomial = + ListPolynomial( + other.coefficients + .toMutableList() + .apply { + for (deg in indices) this[deg] = this@times * this[deg] + } + ) + + /** + * Returns sum of the constant represented as a polynomial and the polynomial. + */ + public override operator fun ListPolynomial.plus(other: C): ListPolynomial = + with(coefficients) { + if (isEmpty()) ListPolynomial(listOf(other)) + else ListPolynomial( + toMutableList() + .apply { + val result = if (size == 0) other else get(0) + other + + if(size == 0) add(result) + else this[0] = result + } + ) + } + /** + * Returns difference between the constant represented as a polynomial and the polynomial. + */ + public override operator fun ListPolynomial.minus(other: C): ListPolynomial = + with(coefficients) { + if (isEmpty()) ListPolynomial(listOf(-other)) + else ListPolynomial( + toMutableList() + .apply { + val result = if (size == 0) other else get(0) - other + + if(size == 0) add(result) + else this[0] = result + } + ) + } + /** + * Returns product of the constant represented as a polynomial and the polynomial. + */ + public override operator fun ListPolynomial.times(other: C): ListPolynomial = + ListPolynomial( + coefficients + .toMutableList() + .apply { + for (deg in indices) this[deg] = this[deg] * other + } + ) + + /** + * Converts the constant [value] to polynomial. + */ + public override fun number(value: C): ListPolynomial = ListPolynomial(listOf(value)) + + /** + * Returns negation of the polynomial. + */ + public override operator fun ListPolynomial.unaryMinus(): ListPolynomial = + ListPolynomial(coefficients.map { -it }) + /** + * Returns sum of the polynomials. + */ + public override operator fun ListPolynomial.plus(other: ListPolynomial): ListPolynomial { + val thisDegree = degree + val otherDegree = other.degree + return ListPolynomial( + List(max(thisDegree, otherDegree) + 1) { + when { + it > thisDegree -> other.coefficients[it] + it > otherDegree -> coefficients[it] + else -> coefficients[it] + other.coefficients[it] + } + } + ) + } + /** + * Returns difference of the polynomials. + */ + public override operator fun ListPolynomial.minus(other: ListPolynomial): ListPolynomial { + val thisDegree = degree + val otherDegree = other.degree + return ListPolynomial( + List(max(thisDegree, otherDegree) + 1) { + when { + it > thisDegree -> -other.coefficients[it] + it > otherDegree -> coefficients[it] + else -> coefficients[it] - other.coefficients[it] + } + } + ) + } + /** + * Returns product of the polynomials. + */ + public override operator fun ListPolynomial.times(other: ListPolynomial): ListPolynomial { + val thisDegree = degree + val otherDegree = other.degree + return ListPolynomial( + List(thisDegree + otherDegree + 1) { d -> + (max(0, d - otherDegree)..min(thisDegree, d)) + .map { coefficients[it] * other.coefficients[d - it] } + .reduce { acc, rational -> acc + rational } + } + ) + } + /** + * Raises [arg] to the integer power [exponent]. + */ // TODO: To optimize boxing + override fun power(arg: ListPolynomial, exponent: UInt): ListPolynomial = super.power(arg, exponent) + + /** + * Instance of zero polynomial (zero of the polynomial ring). + */ + override val zero: ListPolynomial = ListPolynomial(emptyList()) + /** + * Instance of unit polynomial (unit of the polynomial ring). + */ + override val one: ListPolynomial by lazy { ListPolynomial(listOf(constantOne)) } + + /** + * Degree of the polynomial, [see also](https://en.wikipedia.org/wiki/Degree_of_a_polynomial). If the polynomial is + * zero, degree is -1. + */ + public override val ListPolynomial.degree: Int get() = coefficients.lastIndex + + // TODO: When context receivers will be ready move all of this substitutions and invocations to utilities with + // [ListPolynomialSpace] as a context receiver + /** + * Evaluates value of [this] polynomial on provided [argument]. + */ + public inline fun ListPolynomial.substitute(argument: C): C = substitute(ring, argument) + /** + * Substitutes provided polynomial [argument] into [this] polynomial. + */ + public inline fun ListPolynomial.substitute(argument: ListPolynomial): ListPolynomial = substitute(ring, argument) + + /** + * Represent [this] polynomial as a regular context-less function. + */ + public inline fun ListPolynomial.asFunction(): (C) -> C = asFunctionOver(ring) + /** + * Represent [this] polynomial as a regular context-less function. + */ + public inline fun ListPolynomial.asFunctionOfConstant(): (C) -> C = asFunctionOfConstantOver(ring) + /** + * Represent [this] polynomial as a regular context-less function. + */ + public inline fun ListPolynomial.asFunctionOfPolynomial(): (ListPolynomial) -> ListPolynomial = asFunctionOfPolynomialOver(ring) + + /** + * Evaluates value of [this] polynomial on provided [argument]. + */ + public inline operator fun ListPolynomial.invoke(argument: C): C = substitute(ring, argument) + /** + * Evaluates value of [this] polynomial on provided [argument]. + */ + public inline operator fun ListPolynomial.invoke(argument: ListPolynomial): ListPolynomial = substitute(ring, argument) +} + +/** + * Space of polynomials constructed over ring. + * + * @param C the type of constants. Polynomials have them as a coefficients in their terms. + * @param A type of underlying ring of constants. It's [Ring] of [C]. + * @param ring underlying ring of constants of type [A]. + */ +public class ScalableListPolynomialSpace( + ring: A, +) : ListPolynomialSpace(ring), ScaleOperations> where A : Ring, A : ScaleOperations { + override fun scale(a: ListPolynomial, value: Double): ListPolynomial = + ring { ListPolynomial(a.coefficients.map { scale(it, value) }) } +} diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/ListRationalFunction.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/ListRationalFunction.kt similarity index 100% rename from kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/ListRationalFunction.kt rename to kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/ListRationalFunction.kt diff --git a/kmath-polynomialX/src/commonMain/kotlin/space.kscience.kmath.functions/NumberedPolynomial.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/NumberedPolynomial.kt similarity index 100% rename from kmath-polynomialX/src/commonMain/kotlin/space.kscience.kmath.functions/NumberedPolynomial.kt rename to kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/NumberedPolynomial.kt diff --git a/kmath-polynomialX/src/commonMain/kotlin/space.kscience.kmath.functions/NumberedRationalFunction.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/NumberedRationalFunction.kt similarity index 100% rename from kmath-polynomialX/src/commonMain/kotlin/space.kscience.kmath.functions/NumberedRationalFunction.kt rename to kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/NumberedRationalFunction.kt diff --git a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/Polynomial.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/Polynomial.kt new file mode 100644 index 000000000..61ea5a342 --- /dev/null +++ b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/Polynomial.kt @@ -0,0 +1,502 @@ +/* + * 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 space.kscience.kmath.operations.Ring +import space.kscience.kmath.operations.invoke +import kotlin.js.JsName +import kotlin.jvm.JvmName + + +/** + * Abstraction of polynomials. + */ +public interface Polynomial + +/** + * Abstraction of ring of polynomials of type [P] over ring of constants of type [C]. + * + * @param C the type of constants. Polynomials have them as coefficients in their terms. + * @param P the type of polynomials. + */ +@Suppress("INAPPLICABLE_JVM_NAME", "PARAMETER_NAME_CHANGED_ON_OVERRIDE") // FIXME: Waiting for KT-31420 +public interface PolynomialSpace> : Ring

{ + /** + * Returns sum of the constant and the integer represented as a constant (member of underlying ring). + * + * The operation is equivalent to adding [other] copies of unit of underlying ring to [this]. + */ + public operator fun C.plus(other: Int): C + /** + * Returns difference between the constant and the integer represented as a constant (member of underlying ring). + * + * The operation is equivalent to subtraction [other] copies of unit of underlying ring from [this]. + */ + public operator fun C.minus(other: Int): C + /** + * Returns product of the constant and the integer represented as a constant (member of underlying ring). + * + * The operation is equivalent to sum of [other] copies of [this]. + */ + public operator fun C.times(other: Int): C + + /** + * Returns sum of the integer represented as a constant (member of underlying ring) and the constant. + * + * The operation is equivalent to adding [this] copies of unit of underlying ring to [other]. + */ + public operator fun Int.plus(other: C): C + /** + * Returns difference between the integer represented as a constant (member of underlying ring) and the constant. + * + * The operation is equivalent to subtraction [this] copies of unit of underlying ring from [other]. + */ + public operator fun Int.minus(other: C): C + /** + * Returns product of the integer represented as a constant (member of underlying ring) and the constant. + * + * The operation is equivalent to sum of [this] copies of [other]. + */ + public operator fun Int.times(other: C): C + + /** + * Converts the integer [value] to constant. + */ + public fun constantNumber(value: Int): C = constantOne * value + /** + * Converts the integer to constant. + */ + public fun Int.asConstant(): C = constantNumber(this) + + /** + * Returns sum of the polynomial and the integer represented as a polynomial. + * + * The operation is equivalent to adding [other] copies of unit polynomial to [this]. + */ + public operator fun P.plus(other: Int): P = addMultipliedByDoubling(this, one, other) + /** + * Returns difference between the polynomial and the integer represented as a polynomial. + * + * The operation is equivalent to subtraction [other] copies of unit polynomial from [this]. + */ + public operator fun P.minus(other: Int): P = addMultipliedByDoubling(this, one, -other) + /** + * Returns product of the polynomial and the integer represented as a polynomial. + * + * The operation is equivalent to sum of [other] copies of [this]. + */ + public operator fun P.times(other: Int): P = multiplyByDoubling(this, other) + + /** + * Returns sum of the integer represented as a polynomial and the polynomial. + * + * The operation is equivalent to adding [this] copies of unit polynomial to [other]. + */ + public operator fun Int.plus(other: P): P = addMultipliedByDoubling(other, one, this) + /** + * Returns difference between the integer represented as a polynomial and the polynomial. + * + * The operation is equivalent to subtraction [this] copies of unit polynomial from [other]. + */ + public operator fun Int.minus(other: P): P = addMultipliedByDoubling(-other, one, this) + /** + * Returns product of the integer represented as a polynomial and the polynomial. + * + * The operation is equivalent to sum of [this] copies of [other]. + */ + public operator fun Int.times(other: P): P = multiplyByDoubling(other, this) + + /** + * Converts the integer [value] to polynomial. + */ + public fun number(value: Int): P = number(constantNumber(value)) + /** + * Converts the integer to polynomial. + */ + public fun Int.asPolynomial(): P = number(this) + + /** + * Returns the same constant. + */ + @JvmName("unaryPlusConstant") + @JsName("unaryPlusConstant") + public operator fun C.unaryPlus(): C = this + /** + * Returns negation of the constant. + */ + @JvmName("unaryMinusConstant") + @JsName("unaryMinusConstant") + public operator fun C.unaryMinus(): C + /** + * Returns sum of the constants. + */ + @JvmName("plusConstantConstant") + @JsName("plusConstantConstant") + public operator fun C.plus(other: C): C + /** + * Returns difference of the constants. + */ + @JvmName("minusConstantConstant") + @JsName("minusConstantConstant") + public operator fun C.minus(other: C): C + /** + * Returns product of the constants. + */ + @JvmName("timesConstantConstant") + @JsName("timesConstantConstant") + public operator fun C.times(other: C): C + /** + * Raises [arg] to the integer power [exponent]. + */ + @JvmName("powerConstant") + @JsName("powerConstant") + public fun power(arg: C, exponent: UInt) : C + + /** + * Instance of zero constant (zero of the underlying ring). + */ + public val constantZero: C + /** + * Instance of unit constant (unit of the underlying ring). + */ + public val constantOne: C + + /** + * Returns sum of the constant represented as a polynomial and the polynomial. + */ + public operator fun C.plus(other: P): P + /** + * Returns difference between the constant represented as a polynomial and the polynomial. + */ + public operator fun C.minus(other: P): P + /** + * Returns product of the constant represented as a polynomial and the polynomial. + */ + public operator fun C.times(other: P): P + + /** + * Returns sum of the constant represented as a polynomial and the polynomial. + */ + public operator fun P.plus(other: C): P + /** + * Returns difference between the constant represented as a polynomial and the polynomial. + */ + public operator fun P.minus(other: C): P + /** + * Returns product of the constant represented as a polynomial and the polynomial. + */ + public operator fun P.times(other: C): P + + /** + * Converts the constant [value] to polynomial. + */ + public fun number(value: C): P = one * value + /** + * Converts the constant to polynomial. + */ + public fun C.asPolynomial(): P = number(this) + + /** + * Returns the same polynomial. + */ + public override operator fun P.unaryPlus(): P = this + /** + * Returns negation of the polynomial. + */ + public override operator fun P.unaryMinus(): P + /** + * Returns sum of the polynomials. + */ + public override operator fun P.plus(other: P): P + /** + * Returns difference of the polynomials. + */ + public override operator fun P.minus(other: P): P + /** + * Returns product of the polynomials. + */ + public override operator fun P.times(other: P): P + /** + * Raises [arg] to the integer power [exponent]. + */ + public override fun power(arg: P, exponent: UInt) : P = exponentiateBySquaring(arg, exponent) + + /** + * Instance of zero polynomial (zero of the polynomial ring). + */ + public override val zero: P + /** + * Instance of unit polynomial (unit of the polynomial ring). + */ + public override val one: P + + /** + * Degree of the polynomial, [see also](https://en.wikipedia.org/wiki/Degree_of_a_polynomial). If the polynomial is + * zero, degree is -1. + */ + public val P.degree: Int + + override fun add(left: P, right: P): P = left + right + override fun multiply(left: P, right: P): P = left * right +} + +/** + * Abstraction of ring of polynomials of type [P] over ring of constants of type [C]. It also assumes that there is + * provided [ring] (of type [A]), that provides constant-wise operations. + * + * @param C the type of constants. Polynomials have them as coefficients in their terms. + * @param P the type of polynomials. + * @param A the type of algebraic structure (precisely, of ring) provided for constants. + */ +@Suppress("INAPPLICABLE_JVM_NAME") // FIXME: Waiting for KT-31420 +public interface PolynomialSpaceOverRing, A: Ring> : PolynomialSpace { + + /** + * Underlying ring of constants. Its operations on constants are inherited by local operations on constants. + */ + public val ring: A + + /** + * Returns sum of the constant and the integer represented as a constant (member of underlying ring). + * + * The operation is equivalent to adding [other] copies of unit of underlying ring to [this]. + */ + public override operator fun C.plus(other: Int): C = ring { addMultipliedByDoubling(this@plus, one, other) } + /** + * Returns difference between the constant and the integer represented as a constant (member of underlying ring). + * + * The operation is equivalent to subtraction [other] copies of unit of underlying ring from [this]. + */ + public override operator fun C.minus(other: Int): C = ring { addMultipliedByDoubling(this@minus, one, -other) } + /** + * Returns product of the constant and the integer represented as a constant (member of underlying ring). + * + * The operation is equivalent to sum of [other] copies of [this]. + */ + public override operator fun C.times(other: Int): C = ring { multiplyByDoubling(this@times, other) } + + /** + * Returns sum of the integer represented as a constant (member of underlying ring) and the constant. + * + * The operation is equivalent to adding [this] copies of unit of underlying ring to [other]. + */ + public override operator fun Int.plus(other: C): C = ring { addMultipliedByDoubling(other, one, this@plus) } + /** + * Returns difference between the integer represented as a constant (member of underlying ring) and the constant. + * + * The operation is equivalent to subtraction [this] copies of unit of underlying ring from [other]. + */ + public override operator fun Int.minus(other: C): C = ring { addMultipliedByDoubling(-other, one, this@minus) } + /** + * Returns product of the integer represented as a constant (member of underlying ring) and the constant. + * + * The operation is equivalent to sum of [this] copies of [other]. + */ + public override operator fun Int.times(other: C): C = ring { multiplyByDoubling(other, this@times) } + + /** + * Returns negation of the constant. + */ + @JvmName("unaryMinusConstant") + public override operator fun C.unaryMinus(): C = ring { -this@unaryMinus } + /** + * Returns sum of the constants. + */ + @JvmName("plusConstantConstant") + public override operator fun C.plus(other: C): C = ring { this@plus + other } + /** + * Returns difference of the constants. + */ + @JvmName("minusConstantConstant") + public override operator fun C.minus(other: C): C = ring { this@minus - other } + /** + * Returns product of the constants. + */ + @JvmName("timesConstantConstant") + public override operator fun C.times(other: C): C = ring { this@times * other } + /** + * Raises [arg] to the integer power [exponent]. + */ + @JvmName("powerConstant") + override fun power(arg: C, exponent: UInt): C = ring { power(arg, exponent) } + + /** + * Instance of zero constant (zero of the underlying ring). + */ + public override val constantZero: C get() = ring.zero + /** + * Instance of unit constant (unit of the underlying ring). + */ + public override val constantOne: C get() = ring.one +} + +/** + * Abstraction of ring of polynomials of type [P] of variables of type [V] and over ring of constants of type [C]. + * + * @param C the type of constants. Polynomials have them as coefficients in their terms. + * @param V the type of variables. Polynomials have them in representations of terms. + * @param P the type of polynomials. + */ +@Suppress("INAPPLICABLE_JVM_NAME") // FIXME: Waiting for KT-31420 +public interface MultivariatePolynomialSpace>: PolynomialSpace { + /** + * Returns sum of the variable represented as a monic monomial and the integer represented as a constant polynomial. + */ + @JvmName("plusVariableInt") + public operator fun V.plus(other: Int): P + /** + * Returns difference between the variable represented as a monic monomial and the integer represented as a constant polynomial. + */ + @JvmName("minusVariableInt") + public operator fun V.minus(other: Int): P + /** + * Returns product of the variable represented as a monic monomial and the integer represented as a constant polynomial. + */ + @JvmName("timesVariableInt") + public operator fun V.times(other: Int): P + + /** + * Returns sum of the integer represented as a constant polynomial and the variable represented as a monic monomial. + */ + @JvmName("plusIntVariable") + public operator fun Int.plus(other: V): P + /** + * Returns difference between the integer represented as a constant polynomial and the variable represented as a monic monomial. + */ + @JvmName("minusIntVariable") + public operator fun Int.minus(other: V): P + /** + * Returns product of the integer represented as a constant polynomial and the variable represented as a monic monomial. + */ + @JvmName("timesIntVariable") + public operator fun Int.times(other: V): P + + /** + * Returns sum of the variable represented as a monic monomial and the constant represented as a constant polynomial. + */ + @JvmName("plusVariableConstant") + public operator fun V.plus(other: C): P + /** + * Returns difference between the variable represented as a monic monomial and the constant represented as a constant polynomial. + */ + @JvmName("minusVariableConstant") + public operator fun V.minus(other: C): P + /** + * Returns product of the variable represented as a monic monomial and the constant represented as a constant polynomial. + */ + @JvmName("timesVariableConstant") + public operator fun V.times(other: C): P + + /** + * Returns sum of the constant represented as a constant polynomial and the variable represented as a monic monomial. + */ + @JvmName("plusConstantVariable") + public operator fun C.plus(other: V): P + /** + * Returns difference between the constant represented as a constant polynomial and the variable represented as a monic monomial. + */ + @JvmName("minusConstantVariable") + public operator fun C.minus(other: V): P + /** + * Returns product of the constant represented as a constant polynomial and the variable represented as a monic monomial. + */ + @JvmName("timesConstantVariable") + public operator fun C.times(other: V): P + + /** + * Represents the variable as a monic monomial. + */ + @JvmName("unaryPlusVariable") + public operator fun V.unaryPlus(): P + /** + * Returns negation of representation of the variable as a monic monomial. + */ + @JvmName("unaryMinusVariable") + public operator fun V.unaryMinus(): P + /** + * Returns sum of the variables represented as monic monomials. + */ + @JvmName("plusVariableVariable") + public operator fun V.plus(other: V): P + /** + * Returns difference between the variables represented as monic monomials. + */ + @JvmName("minusVariableVariable") + public operator fun V.minus(other: V): P + /** + * Returns product of the variables represented as monic monomials. + */ + @JvmName("timesVariableVariable") + public operator fun V.times(other: V): P + + /** + * Represents the [variable] as a monic monomial. + */ + @JvmName("numberVariable") + public fun number(variable: V): P = +variable + /** + * Represents the variable as a monic monomial. + */ + @JvmName("asPolynomialVariable") + public fun V.asPolynomial(): P = number(this) + + /** + * Returns sum of the variable represented as a monic monomial and the polynomial. + */ + @JvmName("plusVariablePolynomial") + public operator fun V.plus(other: P): P + /** + * Returns difference between the variable represented as a monic monomial and the polynomial. + */ + @JvmName("minusVariablePolynomial") + public operator fun V.minus(other: P): P + /** + * Returns product of the variable represented as a monic monomial and the polynomial. + */ + @JvmName("timesVariablePolynomial") + public operator fun V.times(other: P): P + + /** + * Returns sum of the polynomial and the variable represented as a monic monomial. + */ + @JvmName("plusPolynomialVariable") + public operator fun P.plus(other: V): P + /** + * Returns difference between the polynomial and the variable represented as a monic monomial. + */ + @JvmName("minusPolynomialVariable") + public operator fun P.minus(other: V): P + /** + * Returns product of the polynomial and the variable represented as a monic monomial. + */ + @JvmName("timesPolynomialVariable") + public operator fun P.times(other: V): P + + /** + * Map that associates variables (that appear in the polynomial in positive exponents) with their most exponents + * in which they are appeared in the polynomial. + * + * As consequence all values in the map are positive integers. Also, if the polynomial is constant, the map is empty. + * And keys of the map is the same as in [variables]. + */ + public val P.degrees: Map + /** + * Counts degree of the polynomial by the specified [variable]. + */ + public fun P.degreeBy(variable: V): UInt = degrees.getOrElse(variable) { 0u } + /** + * Counts degree of the polynomial by the specified [variables]. + */ + public fun P.degreeBy(variables: Collection): UInt + /** + * Set of all variables that appear in the polynomial in positive exponents. + */ + public val P.variables: Set get() = degrees.keys + /** + * Count of all variables that appear in the polynomial in positive exponents. + */ + public val P.countOfVariables: Int get() = variables.size +} \ No newline at end of file diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/RationalFunction.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/RationalFunction.kt similarity index 99% rename from kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/RationalFunction.kt rename to kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/RationalFunction.kt index 1782dba74..f664ae9db 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/RationalFunction.kt +++ b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/RationalFunction.kt @@ -5,7 +5,8 @@ package space.kscience.kmath.functions -import space.kscience.kmath.operations.* +import space.kscience.kmath.operations.Ring +import space.kscience.kmath.operations.invoke import kotlin.js.JsName import kotlin.jvm.JvmName diff --git a/kmath-polynomialX/src/commonMain/kotlin/space.kscience.kmath.functions/algebraicStub.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/algebraicStub.kt similarity index 100% rename from kmath-polynomialX/src/commonMain/kotlin/space.kscience.kmath.functions/algebraicStub.kt rename to kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/algebraicStub.kt diff --git a/kmath-polynomialX/src/commonMain/kotlin/space.kscience.kmath.functions/labeledConstructors.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/labeledConstructors.kt similarity index 100% rename from kmath-polynomialX/src/commonMain/kotlin/space.kscience.kmath.functions/labeledConstructors.kt rename to kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/labeledConstructors.kt diff --git a/kmath-polynomialX/src/commonMain/kotlin/space.kscience.kmath.functions/labeledUtil.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/labeledUtil.kt similarity index 99% rename from kmath-polynomialX/src/commonMain/kotlin/space.kscience.kmath.functions/labeledUtil.kt rename to kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/labeledUtil.kt index 4e799cb43..e3b35facc 100644 --- a/kmath-polynomialX/src/commonMain/kotlin/space.kscience.kmath.functions/labeledUtil.kt +++ b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/labeledUtil.kt @@ -8,9 +8,9 @@ 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.invoke import space.kscience.kmath.operations.Ring import space.kscience.kmath.operations.algebra +import space.kscience.kmath.operations.invoke import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.jvm.JvmName diff --git a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/listConstructors.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/listConstructors.kt new file mode 100644 index 000000000..e95361724 --- /dev/null +++ b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/listConstructors.kt @@ -0,0 +1,92 @@ +/* + * 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 space.kscience.kmath.operations.Ring + + +/** + * Constructs a [ListPolynomial] instance with provided [coefficients]. The collection of coefficients will be reversed + * if [reverse] parameter is true. + */ +@Suppress("FunctionName") +public fun ListPolynomial(coefficients: List, reverse: Boolean = false): ListPolynomial = + ListPolynomial(with(coefficients) { if (reverse) reversed() else this }) + +/** + * Constructs a [ListPolynomial] instance with provided [coefficients]. The collection of coefficients will be reversed + * if [reverse] parameter is true. + */ +@Suppress("FunctionName") +public fun ListPolynomial(vararg coefficients: C, reverse: Boolean = false): ListPolynomial = + ListPolynomial(with(coefficients) { if (reverse) reversed() else toList() }) + +/** + * Represents [this] constant as a [ListPolynomial]. + */ +public fun C.asListPolynomial() : ListPolynomial = ListPolynomial(listOf(this)) + + +// Waiting for context receivers :( FIXME: Replace with context receivers when they will be available + +/** + * Constructs [ListRationalFunction] instance with numerator and denominator constructed with provided + * [numeratorCoefficients] and [denominatorCoefficients]. The both collections of coefficients will be reversed if + * [reverse] parameter is true. + */ +@Suppress("FunctionName") +public fun ListRationalFunction(numeratorCoefficients: List, denominatorCoefficients: List, reverse: Boolean = false): ListRationalFunction = + ListRationalFunction( + ListPolynomial( with(numeratorCoefficients) { if (reverse) reversed() else this } ), + ListPolynomial( with(denominatorCoefficients) { if (reverse) reversed() else this } ) + ) +/** + * Constructs [ListRationalFunction] instance with provided [numerator] and unit denominator. + */ +@Suppress("FunctionName") +public fun > A.ListRationalFunction(numerator: ListPolynomial): ListRationalFunction = + ListRationalFunction(numerator, ListPolynomial(listOf(one))) +/** + * Constructs [ListRationalFunction] instance with provided [numerator] and unit denominator. + */ +@Suppress("FunctionName") +public fun > ListRationalFunctionSpace.ListRationalFunction(numerator: ListPolynomial): ListRationalFunction = + ListRationalFunction(numerator, polynomialOne) +/** + * Constructs [ListRationalFunction] instance with numerator constructed with provided [numeratorCoefficients] and unit + * denominator. The collection of numerator coefficients will be reversed if [reverse] parameter is true. + */ +@Suppress("FunctionName") +public fun > A.ListRationalFunction(numeratorCoefficients: List, reverse: Boolean = false): ListRationalFunction = + ListRationalFunction( + ListPolynomial( with(numeratorCoefficients) { if (reverse) reversed() else this } ), + ListPolynomial(listOf(one)) + ) +/** + * Constructs [ListRationalFunction] instance with numerator constructed with provided [numeratorCoefficients] and unit + * denominator. The collection of numerator coefficients will be reversed if [reverse] parameter is true. + */ +@Suppress("FunctionName") +public fun > ListRationalFunctionSpace.ListRationalFunction(numeratorCoefficients: List, reverse: Boolean = false): ListRationalFunction = + ListRationalFunction( + ListPolynomial( with(numeratorCoefficients) { if (reverse) reversed() else this } ), + polynomialOne + ) + +/** + * Represents [this] constant as a rational function. + */ // FIXME: When context receivers will be ready, delete this function and uncomment the following two +public fun > C.asListRationalFunction(ring: A) : ListRationalFunction = ring.ListRationalFunction(asListPolynomial()) +///** +// * Represents [this] constant as a rational function. +// */ +//context(A) +//public fun > C.asListRationalFunction() : ListRationalFunction = ListRationalFunction(asListPolynomial()) +///** +// * Represents [this] constant as a rational function. +// */ +//context(ListRationalFunctionSpace) +//public fun > C.asListRationalFunction() : ListRationalFunction = ListRationalFunction(asListPolynomial()) \ No newline at end of file diff --git a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/listUtil.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/listUtil.kt new file mode 100644 index 000000000..4f3f6d88e --- /dev/null +++ b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/listUtil.kt @@ -0,0 +1,255 @@ +/* + * 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 space.kscience.kmath.misc.UnstableKMathAPI +import space.kscience.kmath.operations.* +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract +import kotlin.math.max +import kotlin.math.pow + + +/** + * Creates a [ListPolynomialSpace] over a received ring. + */ +public inline val > A.listPolynomialSpace: ListPolynomialSpace + get() = ListPolynomialSpace(this) + +/** + * Creates a [ListPolynomialSpace]'s scope over a received ring. + */ // TODO: When context will be ready move [ListPolynomialSpace] and add [A] to context receivers of [block] +public inline fun , R> A.listPolynomialSpace(block: ListPolynomialSpace.() -> R): R { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return ListPolynomialSpace(this).block() +} + +/** + * Creates a [ScalableListPolynomialSpace] over a received scalable ring. + */ +public inline val A.scalableListPolynomialSpace: ScalableListPolynomialSpace where A : Ring, A : ScaleOperations + get() = ScalableListPolynomialSpace(this) + +/** + * Creates a [ScalableListPolynomialSpace]'s scope over a received scalable ring. + */ // TODO: When context will be ready move [ListPolynomialSpace] and add [A] to context receivers of [block] +public inline fun A.scalableListPolynomialSpace(block: ScalableListPolynomialSpace.() -> R): R where A : Ring, A : ScaleOperations { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return ScalableListPolynomialSpace(this).block() +} + +/** + * Creates a [ListRationalFunctionSpace] over a received ring. + */ +public inline val > A.listRationalFunctionSpace: ListRationalFunctionSpace + get() = ListRationalFunctionSpace(this) + +/** + * Creates a [ListRationalFunctionSpace]'s scope over a received ring. + */ // TODO: When context will be ready move [ListRationalFunctionSpace] and add [A] to context receivers of [block] +public inline fun , R> A.listRationalFunctionSpace(block: ListRationalFunctionSpace.() -> R): R { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return ListRationalFunctionSpace(this).block() +} + + +/** + * Evaluates value of [this] Double polynomial on provided Double argument. + */ +public fun ListPolynomial.substitute(arg: Double): Double = + coefficients.reduceIndexedOrNull { index, acc, c -> + acc + c * arg.pow(index) + } ?: .0 + +/** + * Evaluates value of [this] polynomial on provided argument. + * + * It is an implementation of [Horner's method](https://en.wikipedia.org/wiki/Horner%27s_method). + */ +public fun ListPolynomial.substitute(ring: Ring, arg: C): C = ring { + if (coefficients.isEmpty()) return zero + var result: C = coefficients.last() + for (j in coefficients.size - 2 downTo 0) { + result = (arg * result) + coefficients[j] + } + return result +} + +/** + * Substitutes provided polynomial [arg] into [this] polynomial. + * + * It is an implementation of [Horner's method](https://en.wikipedia.org/wiki/Horner%27s_method). + */ // TODO: To optimize boxing +public fun ListPolynomial.substitute(ring: Ring, arg: ListPolynomial) : ListPolynomial = + ring.listPolynomialSpace { + if (coefficients.isEmpty()) return zero + var result: ListPolynomial = coefficients.last().asPolynomial() + for (j in coefficients.size - 2 downTo 0) { + result = (arg * result) + coefficients[j] + } + return result + } + +/** + * Substitutes provided rational function [arg] into [this] polynomial. + * + * It is an implementation of [Horner's method](https://en.wikipedia.org/wiki/Horner%27s_method). + */ // TODO: To optimize boxing +public fun ListPolynomial.substitute(ring: Ring, arg: ListRationalFunction) : ListRationalFunction = + ring.listRationalFunctionSpace { + if (coefficients.isEmpty()) return zero + var result: ListRationalFunction = coefficients.last().asRationalFunction() + for (j in coefficients.size - 2 downTo 0) { + result = (arg * result) + coefficients[j] + } + return result + } + +/** + * Evaluates value of [this] Double rational function in provided Double argument. + */ +public fun ListRationalFunction.substitute(arg: Double): Double = + numerator.substitute(arg) / denominator.substitute(arg) + +/** + * Evaluates value of [this] polynomial for provided argument. + * + * It is an implementation of [Horner's method](https://en.wikipedia.org/wiki/Horner%27s_method). + */ +public fun ListRationalFunction.substitute(ring: Field, arg: C): C = ring { + numerator.substitute(ring, arg) / denominator.substitute(ring, arg) +} + +/** + * Substitutes provided polynomial [arg] into [this] rational function. + */ // TODO: To optimize boxing +public fun ListRationalFunction.substitute(ring: Ring, arg: ListPolynomial) : ListRationalFunction = + ring.listRationalFunctionSpace { + numerator.substitute(ring, arg) / denominator.substitute(ring, arg) + } + +/** + * Substitutes provided rational function [arg] into [this] rational function. + */ // TODO: To optimize boxing +public fun ListRationalFunction.substitute(ring: Ring, arg: ListRationalFunction) : ListRationalFunction = + ring.listRationalFunctionSpace { + numerator.substitute(ring, arg) / denominator.substitute(ring, arg) + } + +/** + * Represent [this] polynomial as a regular context-less function. + */ +public fun > ListPolynomial.asFunctionOver(ring: A): (C) -> C = { substitute(ring, it) } + +/** + * Represent [this] polynomial as a regular context-less function. + */ +public fun > ListPolynomial.asFunctionOfConstantOver(ring: A): (C) -> C = { substitute(ring, it) } + +/** + * Represent [this] polynomial as a regular context-less function. + */ +public fun > ListPolynomial.asFunctionOfPolynomialOver(ring: A): (ListPolynomial) -> ListPolynomial = { substitute(ring, it) } + +/** + * Represent [this] polynomial as a regular context-less function. + */ +public fun > ListPolynomial.asFunctionOfRationalFunctionOver(ring: A): (ListRationalFunction) -> ListRationalFunction = { substitute(ring, it) } + +/** + * Represent [this] rational function as a regular context-less function. + */ +public fun > ListRationalFunction.asFunctionOver(ring: A): (C) -> C = { substitute(ring, it) } + +/** + * Represent [this] rational function as a regular context-less function. + */ +public fun > ListRationalFunction.asFunctionOfConstantOver(ring: A): (C) -> C = { substitute(ring, it) } + +/** + * Represent [this] rational function as a regular context-less function. + */ +public fun > ListRationalFunction.asFunctionOfPolynomialOver(ring: A): (ListPolynomial) -> ListRationalFunction = { substitute(ring, it) } + +/** + * Represent [this] rational function as a regular context-less function. + */ +public fun > ListRationalFunction.asFunctionOfRationalFunctionOver(ring: A): (ListRationalFunction) -> ListRationalFunction = { substitute(ring, it) } + +/** + * Returns algebraic derivative of received polynomial. + */ +@UnstableKMathAPI +public fun ListPolynomial.derivative( + ring: A, +): ListPolynomial where A : Ring, A : NumericAlgebra = ring { + ListPolynomial( + buildList(max(0, coefficients.size - 1)) { + for (deg in 1 .. coefficients.lastIndex) add(number(deg) * coefficients[deg]) + } + ) +} + +/** + * Returns algebraic derivative of received polynomial of specified [order]. The [order] should be non-negative integer. + */ +@UnstableKMathAPI +public fun ListPolynomial.nthDerivative( + ring: A, + order: Int, +): ListPolynomial where A : Ring, A : NumericAlgebra = ring { + require(order >= 0) { "Order of derivative must be non-negative" } + ListPolynomial( + buildList(max(0, coefficients.size - order)) { + for (deg in order.. coefficients.lastIndex) + add((deg - order + 1 .. deg).fold(coefficients[deg]) { acc, d -> acc * number(d) }) + } + ) +} + +/** + * Returns algebraic antiderivative of received polynomial. + */ +@UnstableKMathAPI +public fun ListPolynomial.antiderivative( + ring: A, +): ListPolynomial where A : Field, A : NumericAlgebra = ring { + ListPolynomial( + buildList(coefficients.size + 1) { + add(zero) + coefficients.mapIndexedTo(this) { index, t -> t / number(index + 1) } + } + ) +} + +/** + * Returns algebraic antiderivative of received polynomial of specified [order]. The [order] should be non-negative integer. + */ +@UnstableKMathAPI +public fun ListPolynomial.nthAntiderivative( + ring: A, + order: Int, +): ListPolynomial where A : Field, A : NumericAlgebra = ring { + require(order >= 0) { "Order of antiderivative must be non-negative" } + ListPolynomial( + buildList(coefficients.size + order) { + repeat(order) { add(zero) } + coefficients.mapIndexedTo(this) { index, c -> (1..order).fold(c) { acc, i -> acc / number(index + i) } } + } + ) +} + +/** + * Computes a definite integral of [this] polynomial in the specified [range]. + */ +@UnstableKMathAPI +public fun > ListPolynomial.integrate( + ring: Field, + range: ClosedRange, +): C = ring { + val antiderivative = antiderivative(ring) + antiderivative.substitute(ring, range.endInclusive) - antiderivative.substitute(ring, range.start) +} \ No newline at end of file diff --git a/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/misc.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/misc.kt new file mode 100644 index 000000000..76f1c294e --- /dev/null +++ b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/misc.kt @@ -0,0 +1,22 @@ +/* + * 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 + + +/** + * Marks declarations that give access to internal entities of polynomials delicate structure. Thus, it allows to + * optimize performance a bit by skipping standard steps, but such skips may cause critical errors if something is + * implemented badly. Make sure you fully read and understand documentation and don't break internal contracts. + */ +@RequiresOptIn( + message = "This declaration gives access to delicate internal structure of polynomials. " + + "It allows to optimize performance by skipping unnecessary arguments check. " + + "But at the same time makes it easy to make a mistake " + + "that will cause wrong computation result or even runtime error. " + + "Make sure you fully read and understand documentation.", + level = RequiresOptIn.Level.ERROR +) +public annotation class DelicatePolynomialAPI \ No newline at end of file diff --git a/kmath-polynomialX/src/commonMain/kotlin/space.kscience.kmath.functions/numberedConstructors.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/numberedConstructors.kt similarity index 100% rename from kmath-polynomialX/src/commonMain/kotlin/space.kscience.kmath.functions/numberedConstructors.kt rename to kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/numberedConstructors.kt diff --git a/kmath-polynomialX/src/commonMain/kotlin/space.kscience.kmath.functions/numberedUtil.kt b/kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/numberedUtil.kt similarity index 100% rename from kmath-polynomialX/src/commonMain/kotlin/space.kscience.kmath.functions/numberedUtil.kt rename to kmath-polynomial/src/commonMain/kotlin/space.kscience.kmath.functions/numberedUtil.kt diff --git a/kmath-polynomialX/src/commonTest/kotlin/space/kscience/kmath/functions/LabeledConstructorsTest.kt b/kmath-polynomial/src/commonTest/kotlin/space/kscience/kmath/functions/LabeledConstructorsTest.kt similarity index 99% rename from kmath-polynomialX/src/commonTest/kotlin/space/kscience/kmath/functions/LabeledConstructorsTest.kt rename to kmath-polynomial/src/commonTest/kotlin/space/kscience/kmath/functions/LabeledConstructorsTest.kt index 081cf06e4..80476050b 100644 --- a/kmath-polynomialX/src/commonTest/kotlin/space/kscience/kmath/functions/LabeledConstructorsTest.kt +++ b/kmath-polynomial/src/commonTest/kotlin/space/kscience/kmath/functions/LabeledConstructorsTest.kt @@ -6,13 +6,13 @@ package space.kscience.kmath.functions import space.kscience.kmath.expressions.Symbol -import space.kscience.kmath.misc.UnstableKMathAPI -import space.kscience.kmath.operations.algebra -import space.kscience.kmath.operations.invoke import space.kscience.kmath.functions.testUtils.t import space.kscience.kmath.functions.testUtils.x import space.kscience.kmath.functions.testUtils.y import space.kscience.kmath.functions.testUtils.z +import space.kscience.kmath.misc.UnstableKMathAPI +import space.kscience.kmath.operations.algebra +import space.kscience.kmath.operations.invoke import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-polynomialX/src/commonTest/kotlin/space/kscience/kmath/functions/LabeledPolynomialTest.kt b/kmath-polynomial/src/commonTest/kotlin/space/kscience/kmath/functions/LabeledPolynomialTest.kt similarity index 99% rename from kmath-polynomialX/src/commonTest/kotlin/space/kscience/kmath/functions/LabeledPolynomialTest.kt rename to kmath-polynomial/src/commonTest/kotlin/space/kscience/kmath/functions/LabeledPolynomialTest.kt index d2d417a02..bde1386da 100644 --- a/kmath-polynomialX/src/commonTest/kotlin/space/kscience/kmath/functions/LabeledPolynomialTest.kt +++ b/kmath-polynomial/src/commonTest/kotlin/space/kscience/kmath/functions/LabeledPolynomialTest.kt @@ -11,15 +11,15 @@ import space.kscience.kmath.expressions.Symbol import space.kscience.kmath.functions.testUtils.IntModuloRing import space.kscience.kmath.functions.testUtils.Rational import space.kscience.kmath.functions.testUtils.RationalField -import space.kscience.kmath.functions.testUtils.o +import space.kscience.kmath.functions.testUtils.iota import space.kscience.kmath.functions.testUtils.m -import kotlin.test.* +import space.kscience.kmath.functions.testUtils.o +import space.kscience.kmath.functions.testUtils.s +import space.kscience.kmath.functions.testUtils.t import space.kscience.kmath.functions.testUtils.x import space.kscience.kmath.functions.testUtils.y import space.kscience.kmath.functions.testUtils.z -import space.kscience.kmath.functions.testUtils.t -import space.kscience.kmath.functions.testUtils.s -import space.kscience.kmath.functions.testUtils.iota +import kotlin.test.* // TODO: Тесты на конвертацию. diff --git a/kmath-polynomialX/src/commonTest/kotlin/space/kscience/kmath/functions/LabeledPolynomialUtilTest.kt b/kmath-polynomial/src/commonTest/kotlin/space/kscience/kmath/functions/LabeledPolynomialUtilTest.kt similarity index 99% rename from kmath-polynomialX/src/commonTest/kotlin/space/kscience/kmath/functions/LabeledPolynomialUtilTest.kt rename to kmath-polynomial/src/commonTest/kotlin/space/kscience/kmath/functions/LabeledPolynomialUtilTest.kt index 37329a318..88ee1cbb8 100644 --- a/kmath-polynomialX/src/commonTest/kotlin/space/kscience/kmath/functions/LabeledPolynomialUtilTest.kt +++ b/kmath-polynomial/src/commonTest/kotlin/space/kscience/kmath/functions/LabeledPolynomialUtilTest.kt @@ -6,16 +6,15 @@ package space.kscience.kmath.functions import space.kscience.kmath.expressions.Symbol -import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.functions.testUtils.Rational import space.kscience.kmath.functions.testUtils.RationalField +import space.kscience.kmath.functions.testUtils.iota +import space.kscience.kmath.functions.testUtils.x +import space.kscience.kmath.functions.testUtils.y +import space.kscience.kmath.misc.UnstableKMathAPI import kotlin.test.Ignore import kotlin.test.Test import kotlin.test.assertEquals -import space.kscience.kmath.functions.testUtils.x -import space.kscience.kmath.functions.testUtils.y -import space.kscience.kmath.functions.testUtils.iota -import space.kscience.kmath.functions.testUtils.assertEquals class LabeledPolynomialUtilTest { @Test diff --git a/kmath-polynomialX/src/commonTest/kotlin/space/kscience/kmath/functions/NumberedConstructorsTest.kt b/kmath-polynomial/src/commonTest/kotlin/space/kscience/kmath/functions/NumberedConstructorsTest.kt similarity index 100% rename from kmath-polynomialX/src/commonTest/kotlin/space/kscience/kmath/functions/NumberedConstructorsTest.kt rename to kmath-polynomial/src/commonTest/kotlin/space/kscience/kmath/functions/NumberedConstructorsTest.kt diff --git a/kmath-polynomialX/src/commonTest/kotlin/space/kscience/kmath/functions/NumberedPolynomialTest.kt b/kmath-polynomial/src/commonTest/kotlin/space/kscience/kmath/functions/NumberedPolynomialTest.kt similarity index 100% rename from kmath-polynomialX/src/commonTest/kotlin/space/kscience/kmath/functions/NumberedPolynomialTest.kt rename to kmath-polynomial/src/commonTest/kotlin/space/kscience/kmath/functions/NumberedPolynomialTest.kt diff --git a/kmath-polynomialX/src/commonTest/kotlin/space/kscience/kmath/functions/NumberedPolynomialUtilTest.kt b/kmath-polynomial/src/commonTest/kotlin/space/kscience/kmath/functions/NumberedPolynomialUtilTest.kt similarity index 100% rename from kmath-polynomialX/src/commonTest/kotlin/space/kscience/kmath/functions/NumberedPolynomialUtilTest.kt rename to kmath-polynomial/src/commonTest/kotlin/space/kscience/kmath/functions/NumberedPolynomialUtilTest.kt diff --git a/settings.gradle.kts b/settings.gradle.kts index 543d08001..bdd83d04e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -27,8 +27,8 @@ include( ":kmath-coroutines", ":kmath-functions", ":test-utils-functions", - ":kmath-polynomialX", - ":test-utils-polynomialX", + ":kmath-polynomial", + ":test-utils-polynomial", ":kmath-histograms", ":kmath-commons", ":kmath-viktor", diff --git a/test-utils-polynomialX/build.gradle.kts b/test-utils-polynomial/build.gradle.kts similarity index 63% rename from test-utils-polynomialX/build.gradle.kts rename to test-utils-polynomial/build.gradle.kts index f20e1b8bb..e10e1f2b1 100644 --- a/test-utils-polynomialX/build.gradle.kts +++ b/test-utils-polynomial/build.gradle.kts @@ -7,9 +7,7 @@ kotlin.sourceSets { commonMain { dependencies { api(projects.kmathCore) - api(projects.kmathFunctions) - api(projects.testUtilsFunctions) - api(projects.kmathPolynomialX) + api(projects.kmathPolynomial) api(kotlin("test")) } } diff --git a/test-utils-polynomialX/src/commonMain/kotlin/space/kscience/kmath/functions/testUtils/BufferUtils.kt b/test-utils-polynomial/src/commonMain/kotlin/space/kscience/kmath/functions/testUtils/BufferUtils.kt similarity index 100% rename from test-utils-polynomialX/src/commonMain/kotlin/space/kscience/kmath/functions/testUtils/BufferUtils.kt rename to test-utils-polynomial/src/commonMain/kotlin/space/kscience/kmath/functions/testUtils/BufferUtils.kt diff --git a/test-utils-polynomialX/src/commonMain/kotlin/space/kscience/kmath/functions/testUtils/assertion.kt b/test-utils-polynomial/src/commonMain/kotlin/space/kscience/kmath/functions/testUtils/assertion.kt similarity index 100% rename from test-utils-polynomialX/src/commonMain/kotlin/space/kscience/kmath/functions/testUtils/assertion.kt rename to test-utils-polynomial/src/commonMain/kotlin/space/kscience/kmath/functions/testUtils/assertion.kt diff --git a/test-utils-polynomialX/src/commonMain/kotlin/space/kscience/kmath/functions/testUtils/misc.kt b/test-utils-polynomial/src/commonMain/kotlin/space/kscience/kmath/functions/testUtils/misc.kt similarity index 100% rename from test-utils-polynomialX/src/commonMain/kotlin/space/kscience/kmath/functions/testUtils/misc.kt rename to test-utils-polynomial/src/commonMain/kotlin/space/kscience/kmath/functions/testUtils/misc.kt