From 3a6aa1432094c6803ac026593e3035c14a51111d Mon Sep 17 00:00:00 2001 From: Gleb Minaev <43728100+lounres@users.noreply.github.com> Date: Sun, 12 Jun 2022 22:52:08 +0300 Subject: [PATCH] Cleaned up ListPolynomials and ListRationalFunctions: - Added/updated docs. - Fully (but in a simple way) implemented invocation, substitution, functional representation, derivatives and antiderivatives. Optimized reimplementation is in progress. - Upgraded `PolynomialSpaceOfFractions` by adding a bit of laziness. - Other little things... --- .../kmath/functions/ListPolynomial.kt | 80 ++++-- .../kmath/functions/ListRationalFunction.kt | 159 +++++++---- .../kscience/kmath/functions/Polynomial.kt | 2 + .../kmath/functions/RationalFunction.kt | 10 +- .../kmath/functions/listPolynomialUtil.kt | 233 --------------- .../kscience/kmath/functions/listUtil.kt | 268 ++++++++++++++++++ ...alFunctionUtil.kt => listUtilOptimized.kt} | 129 +++++---- .../space/kscience/kmath/functions/misc.kt | 13 + .../kmath/functions/ListPolynomialUtilTest.kt | 2 + .../kscience/kmath/test/misc/Rational.kt | 135 +++++++++ .../space/kscience/kmath/test/misc/misc.kt | 11 + 11 files changed, 667 insertions(+), 375 deletions(-) delete mode 100644 kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/listPolynomialUtil.kt create mode 100644 kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/listUtil.kt rename kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/{listRationalFunctionUtil.kt => listUtilOptimized.kt} (72%) create mode 100644 kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/misc.kt create mode 100644 kmath-functions/src/commonTest/kotlin/space/kscience/kmath/test/misc/Rational.kt create mode 100644 kmath-functions/src/commonTest/kotlin/space/kscience/kmath/test/misc/misc.kt diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/ListPolynomial.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/ListPolynomial.kt index 585da95ea..fce179fc8 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/ListPolynomial.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/ListPolynomial.kt @@ -8,23 +8,19 @@ 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.contracts.InvocationKind -import kotlin.contracts.contract -import kotlin.experimental.ExperimentalTypeInference -import kotlin.jvm.JvmName import kotlin.math.max import kotlin.math.min /** - * Polynomial model without fixation on specific context they are applied to. + * Represents univariate polynomial that stores its coefficients in a [List]. * * @param coefficients constant is the leftmost coefficient. */ public data class ListPolynomial( /** - * List that collects coefficients of the polynomial. Every monomial `a x^d` is represented as a coefficients - * `a` placed into the list with index `d`. For example coefficients of polynomial `5 x^2 - 6` can be represented as + * 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 + @@ -42,26 +38,28 @@ public data class ListPolynomial( * 0, // 0 x^4 * ) * ``` - * It is recommended not to put extra zeros at end of the list (as for `0x^3` and `0x^4` in the example), but is not - * prohibited. + * 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 = "Polynomial$coefficients" + override fun toString(): String = "ListPolynomial$coefficients" } /** - * Space of univariate polynomials constructed over ring. + * Arithmetic context for univariate polynomials with coefficients stored as a [List] constructed with the given [ring] + * of constants. * - * @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 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 polynomial. + * 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]. */ @@ -79,7 +77,7 @@ public open class ListPolynomialSpace>( } ) /** - * Returns difference between the polynomial and the integer represented as polynomial. + * 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]. */ @@ -97,7 +95,7 @@ public open class ListPolynomialSpace>( } ) /** - * Returns product of the polynomial and the integer represented as polynomial. + * Returns product of the polynomial and the integer represented as a polynomial. * * The operation is equivalent to sum of [other] copies of [this]. */ @@ -112,7 +110,7 @@ public open class ListPolynomialSpace>( ) /** - * Returns sum of the integer represented as polynomial and the polynomial. + * 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]. */ @@ -130,7 +128,7 @@ public open class ListPolynomialSpace>( } ) /** - * Returns difference between the integer represented as polynomial and the polynomial. + * 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]. */ @@ -150,7 +148,7 @@ public open class ListPolynomialSpace>( } ) /** - * Returns product of the integer represented as polynomial and the polynomial. + * Returns product of the integer represented as a polynomial and the polynomial. * * The operation is equivalent to sum of [this] copies of [other]. */ @@ -170,7 +168,7 @@ public open class ListPolynomialSpace>( public override fun number(value: Int): ListPolynomial = number(constantNumber(value)) /** - * Returns sum of the constant represented as polynomial and the polynomial. + * Returns sum of the constant represented as a polynomial and the polynomial. */ public override operator fun C.plus(other: ListPolynomial): ListPolynomial = with(other.coefficients) { @@ -186,7 +184,7 @@ public open class ListPolynomialSpace>( ) } /** - * Returns difference between the constant represented as polynomial and the polynomial. + * Returns difference between the constant represented as a polynomial and the polynomial. */ public override operator fun C.minus(other: ListPolynomial): ListPolynomial = with(other.coefficients) { @@ -204,7 +202,7 @@ public open class ListPolynomialSpace>( ) } /** - * Returns product of the constant represented as polynomial and the polynomial. + * Returns product of the constant represented as a polynomial and the polynomial. */ public override operator fun C.times(other: ListPolynomial): ListPolynomial = ListPolynomial( @@ -216,7 +214,7 @@ public open class ListPolynomialSpace>( ) /** - * Returns sum of the constant represented as polynomial and the polynomial. + * Returns sum of the constant represented as a polynomial and the polynomial. */ public override operator fun ListPolynomial.plus(other: C): ListPolynomial = with(coefficients) { @@ -232,7 +230,7 @@ public open class ListPolynomialSpace>( ) } /** - * Returns difference between the constant represented as polynomial and the polynomial. + * Returns difference between the constant represented as a polynomial and the polynomial. */ public override operator fun ListPolynomial.minus(other: C): ListPolynomial = with(coefficients) { @@ -248,7 +246,7 @@ public open class ListPolynomialSpace>( ) } /** - * Returns product of the constant represented as polynomial and the polynomial. + * Returns product of the constant represented as a polynomial and the polynomial. */ public override operator fun ListPolynomial.times(other: C): ListPolynomial = ListPolynomial( @@ -262,7 +260,7 @@ public open class ListPolynomialSpace>( /** * Converts the constant [value] to polynomial. */ - public override fun number(value: C): ListPolynomial = ListPolynomial(value) + public override fun number(value: C): ListPolynomial = ListPolynomial(listOf(value)) /** * Returns negation of the polynomial. @@ -321,9 +319,9 @@ public open class ListPolynomialSpace>( */ override val zero: ListPolynomial = ListPolynomial(emptyList()) /** - * Instance of unit constant (unit of the underlying ring). + * Instance of unit polynomial (unit of the polynomial ring). */ - override val one: ListPolynomial = ListPolynomial(listOf(constantOne)) + 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 @@ -331,23 +329,43 @@ public open class ListPolynomialSpace>( */ 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. + */ @Suppress("NOTHING_TO_INLINE") public inline fun ListPolynomial.substitute(argument: C): C = this.substitute(ring, argument) + /** + * Substitutes provided polynomial [argument] into [this] polynomial. + */ @Suppress("NOTHING_TO_INLINE") public inline fun ListPolynomial.substitute(argument: ListPolynomial): ListPolynomial = this.substitute(ring, argument) + /** + * Represent [this] polynomial as a regular context-less function. + */ @Suppress("NOTHING_TO_INLINE") public inline fun ListPolynomial.asFunction(): (C) -> C = { this.substitute(ring, it) } + /** + * Represent [this] polynomial as a regular context-less function. + */ @Suppress("NOTHING_TO_INLINE") - public inline fun ListPolynomial.asFunctionOnConstants(): (C) -> C = { this.substitute(ring, it) } + public inline fun ListPolynomial.asFunctionOfConstant(): (C) -> C = { this.substitute(ring, it) } + /** + * Represent [this] polynomial as a regular context-less function. + */ @Suppress("NOTHING_TO_INLINE") - public inline fun ListPolynomial.asFunctionOnPolynomials(): (ListPolynomial) -> ListPolynomial = { this.substitute(ring, it) } + public inline fun ListPolynomial.asFunctionOfPolynomial(): (ListPolynomial) -> ListPolynomial = { this.substitute(ring, it) } /** - * Evaluates the polynomial for the given value [argument]. + * Evaluates value of [this] polynomial on provided [argument]. */ @Suppress("NOTHING_TO_INLINE") public inline operator fun ListPolynomial.invoke(argument: C): C = this.substitute(ring, argument) + /** + * Evaluates value of [this] polynomial on provided [argument]. + */ @Suppress("NOTHING_TO_INLINE") public inline operator fun ListPolynomial.invoke(argument: ListPolynomial): ListPolynomial = this.substitute(ring, argument) } diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/ListRationalFunction.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/ListRationalFunction.kt index 7b6c23ac3..45ea99fb0 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/ListRationalFunction.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/ListRationalFunction.kt @@ -8,13 +8,23 @@ package space.kscience.kmath.functions import space.kscience.kmath.operations.Ring +/** + * Represents rational function that stores its numerator and denominator as [ListPolynomial]s. + */ public data class ListRationalFunction( public override val numerator: ListPolynomial, public override val denominator: ListPolynomial ) : RationalFunction> { - override fun toString(): String = "RationalFunction${numerator.coefficients}/${denominator.coefficients}" + override fun toString(): String = "ListRationalFunction${numerator.coefficients}/${denominator.coefficients}" } +/** + * Arithmetic context for univariate rational functions with numerator and denominator represented as [ListPolynomial]s. + * + * @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 class ListRationalFunctionSpace> ( public val ring: A, ) : @@ -30,7 +40,13 @@ public class ListRationalFunctionSpace> ( ListRationalFunction, >() { + /** + * Underlying polynomial ring. Its polynomial operations are inherited by local polynomial operations. + */ override val polynomialRing : ListPolynomialSpace = ListPolynomialSpace(ring) + /** + * Constructor of [ListRationalFunction] from numerator and denominator [ListPolynomial]. + */ override fun constructRationalFunction(numerator: ListPolynomial, denominator: ListPolynomial): ListRationalFunction = ListRationalFunction(numerator, denominator) @@ -43,63 +59,88 @@ public class ListRationalFunctionSpace> ( */ public override val one: ListRationalFunction = ListRationalFunction(polynomialOne, polynomialOne) - // TODO: Разобрать + // 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. + */ + @Suppress("NOTHING_TO_INLINE") + public inline fun ListPolynomial.substitute(argument: C): C = this.substitute(ring, argument) + /** + * Substitutes provided polynomial [argument] into [this] polynomial. + */ + @Suppress("NOTHING_TO_INLINE") + public inline fun ListPolynomial.substitute(argument: ListPolynomial): ListPolynomial = this.substitute(ring, argument) + /** + * Substitutes provided rational function [argument] into [this] polynomial. + */ + @Suppress("NOTHING_TO_INLINE") + public inline fun ListPolynomial.substitute(argument: ListRationalFunction): ListRationalFunction = this.substitute(ring, argument) + /** + * Substitutes provided polynomial [argument] into [this] rational function. + */ + @Suppress("NOTHING_TO_INLINE") + public inline fun ListRationalFunction.substitute(argument: ListPolynomial): ListRationalFunction = this.substitute(ring, argument) + /** + * Substitutes provided rational function [argument] into [this] rational function. + */ + @Suppress("NOTHING_TO_INLINE") + public inline fun ListRationalFunction.substitute(argument: ListRationalFunction): ListRationalFunction = this.substitute(ring, argument) -// operator fun invoke(arg: UnivariatePolynomial): RationalFunction = -// RationalFunction( -// numerator(arg), -// denominator(arg) -// ) -// -// operator fun invoke(arg: RationalFunction): RationalFunction { -// val num = numerator invokeRFTakeNumerator arg -// val den = denominator invokeRFTakeNumerator arg -// val degreeDif = numeratorDegree - denominatorDegree -// return if (degreeDif > 0) -// RationalFunction( -// num, -// multiplyByPower(den, arg.denominator, degreeDif) -// ) -// else -// RationalFunction( -// multiplyByPower(num, arg.denominator, -degreeDif), -// den -// ) -// } -// -// override fun toString(): String = toString(UnivariatePolynomial.variableName) -// -// fun toString(withVariableName: String = UnivariatePolynomial.variableName): String = -// when(true) { -// numerator.isZero() -> "0" -// denominator.isOne() -> numerator.toString(withVariableName) -// else -> "${numerator.toStringWithBrackets(withVariableName)}/${denominator.toStringWithBrackets(withVariableName)}" -// } -// -// fun toStringWithBrackets(withVariableName: String = UnivariatePolynomial.variableName): String = -// when(true) { -// numerator.isZero() -> "0" -// denominator.isOne() -> numerator.toStringWithBrackets(withVariableName) -// else -> "(${numerator.toStringWithBrackets(withVariableName)}/${denominator.toStringWithBrackets(withVariableName)})" -// } -// -// fun toReversedString(withVariableName: String = UnivariatePolynomial.variableName): String = -// when(true) { -// numerator.isZero() -> "0" -// denominator.isOne() -> numerator.toReversedString(withVariableName) -// else -> "${numerator.toReversedStringWithBrackets(withVariableName)}/${denominator.toReversedStringWithBrackets(withVariableName)}" -// } -// -// fun toReversedStringWithBrackets(withVariableName: String = UnivariatePolynomial.variableName): String = -// when(true) { -// numerator.isZero() -> "0" -// denominator.isOne() -> numerator.toReversedStringWithBrackets(withVariableName) -// else -> "(${numerator.toReversedStringWithBrackets(withVariableName)}/${denominator.toReversedStringWithBrackets(withVariableName)})" -// } -// -// fun removeZeros() = -// RationalFunction( -// numerator.removeZeros(), -// denominator.removeZeros() -// ) + /** + * Represent [this] polynomial as a regular context-less function. + */ + @Suppress("NOTHING_TO_INLINE") + public inline fun ListPolynomial.asFunction(): (C) -> C = { this.substitute(ring, it) } + /** + * Represent [this] polynomial as a regular context-less function. + */ + @Suppress("NOTHING_TO_INLINE") + public inline fun ListPolynomial.asFunctionOfConstant(): (C) -> C = { this.substitute(ring, it) } + /** + * Represent [this] polynomial as a regular context-less function. + */ + @Suppress("NOTHING_TO_INLINE") + public inline fun ListPolynomial.asFunctionOfPolynomial(): (ListPolynomial) -> ListPolynomial = { this.substitute(ring, it) } + /** + * Represent [this] polynomial as a regular context-less function. + */ + @Suppress("NOTHING_TO_INLINE") + public inline fun ListPolynomial.asFunctionOfRationalFunction(): (ListRationalFunction) -> ListRationalFunction = { this.substitute(ring, it) } + /** + * Represent [this] rational function as a regular context-less function. + */ + @Suppress("NOTHING_TO_INLINE") + public inline fun ListRationalFunction.asFunctionOfPolynomial(): (ListPolynomial) -> ListRationalFunction = { this.substitute(ring, it) } + /** + * Represent [this] rational function as a regular context-less function. + */ + @Suppress("NOTHING_TO_INLINE") + public inline fun ListRationalFunction.asFunctionOfRationalFunction(): (ListRationalFunction) -> ListRationalFunction = { this.substitute(ring, it) } + + /** + * Evaluates value of [this] polynomial on provided argument. + */ + @Suppress("NOTHING_TO_INLINE") + public inline operator fun ListPolynomial.invoke(argument: C): C = this.substitute(ring, argument) + /** + * Evaluates value of [this] polynomial on provided argument. + */ + @Suppress("NOTHING_TO_INLINE") + public inline operator fun ListPolynomial.invoke(argument: ListPolynomial): ListPolynomial = this.substitute(ring, argument) + /** + * Evaluates value of [this] polynomial on provided argument. + */ + @Suppress("NOTHING_TO_INLINE") + public inline operator fun ListPolynomial.invoke(argument: ListRationalFunction): ListRationalFunction = this.substitute(ring, argument) + /** + * Evaluates value of [this] rational function on provided argument. + */ + @Suppress("NOTHING_TO_INLINE") + public inline operator fun ListRationalFunction.invoke(argument: ListPolynomial): ListRationalFunction = this.substitute(ring, argument) + /** + * Evaluates value of [this] rational function on provided argument. + */ + @Suppress("NOTHING_TO_INLINE") + public inline operator fun ListRationalFunction.invoke(argument: ListRationalFunction): ListRationalFunction = this.substitute(ring, argument) } \ No newline at end of file diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt index f2eba10d5..12490d133 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt @@ -435,10 +435,12 @@ public interface MultivariatePolynomialSpace>: Polynomial /** * 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) /** diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/RationalFunction.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/RationalFunction.kt index edc9dfa5c..4ce6c7c26 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/RationalFunction.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/RationalFunction.kt @@ -1060,12 +1060,12 @@ public abstract class PolynomialSpaceOfFractions< /** * Instance of zero rational function (zero of the rational functions ring). */ - public override val zero: R get() = constructRationalFunction(polynomialZero) + public override val zero: R by lazy { constructRationalFunction(polynomialZero) } /** * Instance of unit polynomial (unit of the rational functions ring). */ - public override val one: R get() = constructRationalFunction(polynomialOne) + public override val one: R by lazy { constructRationalFunction(polynomialOne) } } /** @@ -1177,19 +1177,23 @@ public interface MultivariateRationalFunctionalSpace< /** * Represents the [variable] as a monic monomial. */ + @JvmName("polynomialNumberVariable") public fun polynomialNumber(variable: V): P = +variable /** * Represents the variable as a monic monomial. */ + @JvmName("asPolynomialVariable") public fun V.asPolynomial(): P = polynomialNumber(this) /** * Represents the [variable] as a rational function. */ + @JvmName("numberVariable") public fun number(variable: V): R = number(polynomialNumber(variable)) /** * Represents the variable as a rational function. */ + @JvmName("asRationalFunctionVariable") public fun V.asRationalFunction(): R = number(this) /** @@ -1403,10 +1407,12 @@ public interface MultivariateRationalFunctionalSpaceOverMultivariatePolynomialSp /** * Represents the [variable] as a monic monomial. */ + @JvmName("polynomialNumberVariable") public override fun polynomialNumber(variable: V): P = polynomialRing { number(variable) } /** * Represents the variable as a monic monomial. */ + @JvmName("asPolynomialVariable") public override fun V.asPolynomial(): P = polynomialRing { this@asPolynomial.asPolynomial() } /** diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/listPolynomialUtil.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/listPolynomialUtil.kt deleted file mode 100644 index 50313cab9..000000000 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/listPolynomialUtil.kt +++ /dev/null @@ -1,233 +0,0 @@ -/* - * 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.min -import kotlin.math.pow - - -/** - * Removes zeros on the end of the coefficient list of polynomial. - */ -//context(PolynomialSpace) -//fun > Polynomial.removeZeros() : Polynomial = -// if (degree > -1) Polynomial(coefficients.subList(0, degree + 1)) else zero - -/** - * Creates a [ListPolynomialSpace] over a received ring. - */ -public fun > A.listPolynomial(): ListPolynomialSpace = - ListPolynomialSpace(this) - -/** - * Creates a [ListPolynomialSpace]'s scope over a received ring. - */ -public inline fun , R> A.listPolynomial(block: ListPolynomialSpace.() -> R): R { - contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } - return ListPolynomialSpace(this).block() -} - -/** - * Creates a [ScalableListPolynomialSpace] over a received scalable ring. - */ -public fun A.scalableListPolynomial(): ScalableListPolynomialSpace where A : Ring, A : ScaleOperations = - ScalableListPolynomialSpace(this) - -/** - * Creates a [ScalableListPolynomialSpace]'s scope over a received scalable ring. - */ -public inline fun A.scalableListPolynomial(block: ScalableListPolynomialSpace.() -> R): R where A : Ring, A : ScaleOperations { - contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } - return ScalableListPolynomialSpace(this).block() -} - -@Suppress("NOTHING_TO_INLINE") -internal inline fun copyTo( - origin: List, - originDegree: Int, - target: MutableList, -) { - for (deg in 0 .. originDegree) target[deg] = origin[deg] -} - -@Suppress("NOTHING_TO_INLINE") -internal inline fun multiplyAddingToUpdater( - ring: Ring, - multiplicand: MutableList, - multiplicandDegree: Int, - multiplier: List, - multiplierDegree: Int, - updater: MutableList, - zero: C, -) { - multiplyAddingTo( - ring = ring, - multiplicand = multiplicand, - multiplicandDegree = multiplicandDegree, - multiplier = multiplier, - multiplierDegree = multiplierDegree, - target = updater - ) - for (updateDeg in 0 .. multiplicandDegree + multiplierDegree) { - multiplicand[updateDeg] = updater[updateDeg] - updater[updateDeg] = zero - } -} - -@Suppress("NOTHING_TO_INLINE") -internal inline fun multiplyAddingTo( - ring: Ring, - multiplicand: List, - multiplicandDegree: Int, - multiplier: List, - multiplierDegree: Int, - target: MutableList -) = ring { - for (d in 0 .. multiplicandDegree + multiplierDegree) - for (k in max(0, d - multiplierDegree)..min(multiplicandDegree, d)) - target[d] += multiplicand[k] * multiplier[d - k] -} - -/** - * Evaluates the value of the given double polynomial for given double argument. - */ -public fun ListPolynomial.substitute(arg: Double): Double = - coefficients.reduceIndexedOrNull { index, acc, c -> - acc + c * arg.pow(index) - } ?: .0 - -/** - * Evaluates the value of the given polynomial for given 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@ring zero - var result: C = coefficients.last() - for (j in coefficients.size - 2 downTo 0) { - result = (arg * result) + coefficients[j] - } - return result -} - -public fun ListPolynomial.substitute(ring: Ring, arg: ListPolynomial) : ListPolynomial = ring { - if (coefficients.isEmpty()) return ListPolynomial(emptyList()) - - val thisDegree = coefficients.lastIndex - if (thisDegree == -1) return ListPolynomial(emptyList()) - val argDegree = arg.coefficients.lastIndex - if (argDegree == -1) return coefficients[0].asListPolynomial() - val constantZero = zero - val resultCoefs: MutableList = MutableList(thisDegree * argDegree + 1) { constantZero } - resultCoefs[0] = coefficients[thisDegree] - val resultCoefsUpdate: MutableList = MutableList(thisDegree * argDegree + 1) { constantZero } - var resultDegree = 0 - for (deg in thisDegree - 1 downTo 0) { - resultCoefsUpdate[0] = coefficients[deg] - multiplyAddingToUpdater( - ring = ring, - multiplicand = resultCoefs, - multiplicandDegree = resultDegree, - multiplier = arg.coefficients, - multiplierDegree = argDegree, - updater = resultCoefsUpdate, - zero = constantZero - ) - resultDegree += argDegree - } - - return ListPolynomial(resultCoefs) -} - -/** - * Represent the polynomial as a regular context-less function. - */ -public fun > ListPolynomial.asFunction(ring: A): (C) -> C = { substitute(ring, it) } - -/** - * Represent the polynomial as a regular context-less function. - */ -public fun > ListPolynomial.asPolynomialFunctionOver(ring: A): (ListPolynomial) -> ListPolynomial = { substitute(ring, it) } - -/** - * Returns algebraic derivative of received polynomial. - */ -@UnstableKMathAPI -public fun ListPolynomial.derivative( - algebra: A, -): ListPolynomial where A : Ring, A : NumericAlgebra = algebra { - ListPolynomial( - buildList(max(0, coefficients.size - 1)) { - for (deg in 1 .. coefficients.lastIndex) add(number(deg) * coefficients[deg]) - } - ) -} - -/** - * Returns algebraic derivative of received polynomial. - */ -@UnstableKMathAPI -public fun ListPolynomial.nthDerivative( - algebra: A, - order: Int, -): ListPolynomial where A : Ring, A : NumericAlgebra = algebra { - 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( - algebra: A, -): ListPolynomial where A : Field, A : NumericAlgebra = algebra { - ListPolynomial( - buildList(coefficients.size + 1) { - add(zero) - coefficients.mapIndexedTo(this) { index, t -> t / number(index + 1) } - } - ) -} - -/** - * Returns algebraic antiderivative of received polynomial. - */ -@UnstableKMathAPI -public fun ListPolynomial.nthAntiderivative( - algebra: A, - order: Int, -): ListPolynomial where A : Field, A : NumericAlgebra = algebra { - 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) } } - } - ) -} - -/** - * Compute a definite integral of a given polynomial in a [range] - */ -@UnstableKMathAPI -public fun > ListPolynomial.integrate( - algebra: Field, - range: ClosedRange, -): C = algebra { - val integral = antiderivative(algebra) - integral.substitute(algebra, range.endInclusive) - integral.substitute(algebra, range.start) -} \ No newline at end of file diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/listUtil.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/listUtil.kt new file mode 100644 index 000000000..127dd8c7a --- /dev/null +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/listUtil.kt @@ -0,0 +1,268 @@ +/* + * 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 fun > A.listPolynomialSpace(): ListPolynomialSpace = + 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 fun A.scalableListPolynomialSpace(): ScalableListPolynomialSpace where A : Ring, A : ScaleOperations = + 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 fun > A.listRationalFunctionSpace(): ListRationalFunctionSpace = + 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.asPolynomialFunctionOver(ring: A): (ListPolynomial) -> ListPolynomial = { substitute(ring, it) } + +/** + * Represent [this] polynomial as a regular context-less function. + */ +public fun > ListPolynomial.asFunctionOfRationalFunctionOver(ring: A): (ListPolynomial) -> ListPolynomial = { 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.asPolynomialFunctionOver(ring: A): (ListPolynomial) -> ListRationalFunction = { substitute(ring, it) } + +/** + * Represent [this] rational function as a regular context-less function. + */ +public fun > ListRationalFunction.asFunctionOfRationalFunctionOver(ring: A): (ListPolynomial) -> 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) +} + +/** + * Returns algebraic derivative of received rational function. + */ +@UnstableKMathAPI +public fun ListRationalFunction.derivative( + ring: A, +): ListRationalFunction where A : Ring, A : NumericAlgebra = ring.listRationalFunctionSpace { + ListRationalFunction( + numerator.derivative(ring) * denominator - numerator * denominator.derivative(ring), + denominator * denominator + ) +} + +/** + * Returns algebraic derivative of received rational function of specified [order]. The [order] should be non-negative integer. + */ +@UnstableKMathAPI +public tailrec fun ListRationalFunction.nthDerivative( + ring: A, + order: Int, +): ListRationalFunction where A : Ring, A : NumericAlgebra = + if (order == 0) this else derivative(ring).nthDerivative(ring, order - 1) \ No newline at end of file diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/listRationalFunctionUtil.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/listUtilOptimized.kt similarity index 72% rename from kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/listRationalFunctionUtil.kt rename to kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/listUtilOptimized.kt index 367212588..6eb3a1dc7 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/listRationalFunctionUtil.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/listUtilOptimized.kt @@ -5,41 +5,91 @@ package space.kscience.kmath.functions -import space.kscience.kmath.operations.Field import space.kscience.kmath.operations.Ring import space.kscience.kmath.operations.invoke -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract import kotlin.math.max +import kotlin.math.min -/** - * Creates a [ListRationalFunctionSpace] over a received ring. - */ -public fun > A.listRationalFunction(): ListRationalFunctionSpace = - ListRationalFunctionSpace(this) - -/** - * Creates a [ListRationalFunctionSpace]'s scope over a received ring. - */ -public inline fun , R> A.listRationalFunction(block: ListRationalFunctionSpace.() -> R): R { - contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } - return ListRationalFunctionSpace(this).block() +// TODO: Optimized copies of substitution and invocation +@UnstablePolynomialBoxingOptimization +@Suppress("NOTHING_TO_INLINE") +internal inline fun copyTo( + origin: List, + originDegree: Int, + target: MutableList, +) { + for (deg in 0 .. originDegree) target[deg] = origin[deg] } -/** - * Evaluates the value of the given double polynomial for given double argument. - */ -public fun ListRationalFunction.substitute(arg: Double): Double = - numerator.substitute(arg) / denominator.substitute(arg) +@UnstablePolynomialBoxingOptimization +@Suppress("NOTHING_TO_INLINE") +internal inline fun multiplyAddingToUpdater( + ring: Ring, + multiplicand: MutableList, + multiplicandDegree: Int, + multiplier: List, + multiplierDegree: Int, + updater: MutableList, + zero: C, +) { + multiplyAddingTo( + ring = ring, + multiplicand = multiplicand, + multiplicandDegree = multiplicandDegree, + multiplier = multiplier, + multiplierDegree = multiplierDegree, + target = updater + ) + for (updateDeg in 0 .. multiplicandDegree + multiplierDegree) { + multiplicand[updateDeg] = updater[updateDeg] + updater[updateDeg] = zero + } +} -/** - * Evaluates the value of the given polynomial for given 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) +@UnstablePolynomialBoxingOptimization +@Suppress("NOTHING_TO_INLINE") +internal inline fun multiplyAddingTo( + ring: Ring, + multiplicand: List, + multiplicandDegree: Int, + multiplier: List, + multiplierDegree: Int, + target: MutableList +) = ring { + for (d in 0 .. multiplicandDegree + multiplierDegree) + for (k in max(0, d - multiplierDegree)..min(multiplicandDegree, d)) + target[d] += multiplicand[k] * multiplier[d - k] +} + +@UnstablePolynomialBoxingOptimization +public fun ListPolynomial.substitute2(ring: Ring, arg: ListPolynomial) : ListPolynomial = ring { + if (coefficients.isEmpty()) return ListPolynomial(emptyList()) + + val thisDegree = coefficients.lastIndex + if (thisDegree == -1) return ListPolynomial(emptyList()) + val argDegree = arg.coefficients.lastIndex + if (argDegree == -1) return coefficients[0].asListPolynomial() + val constantZero = zero + val resultCoefs: MutableList = MutableList(thisDegree * argDegree + 1) { constantZero } + resultCoefs[0] = coefficients[thisDegree] + val resultCoefsUpdate: MutableList = MutableList(thisDegree * argDegree + 1) { constantZero } + var resultDegree = 0 + for (deg in thisDegree - 1 downTo 0) { + resultCoefsUpdate[0] = coefficients[deg] + multiplyAddingToUpdater( + ring = ring, + multiplicand = resultCoefs, + multiplicandDegree = resultDegree, + multiplier = arg.coefficients, + multiplierDegree = argDegree, + updater = resultCoefsUpdate, + zero = constantZero + ) + resultDegree += argDegree + } + + return ListPolynomial(resultCoefs) } /** @@ -52,6 +102,7 @@ public fun ListRationalFunction.substitute(ring: Field, arg: C): C = r * * Used in [ListPolynomial.substitute] and [ListRationalFunction.substitute] for performance optimisation. */ // TODO: Дописать +@UnstablePolynomialBoxingOptimization internal fun ListPolynomial.substituteRationalFunctionTakeNumerator(ring: Ring, arg: ListRationalFunction): ListPolynomial = ring { if (coefficients.isEmpty()) return ListPolynomial(emptyList()) @@ -196,26 +247,4 @@ internal fun ListPolynomial.substituteRationalFunctionTakeNumerator(ring: end = thisDegree + 1 ) ) -} - -//operator fun > RationalFunction.invoke(arg: T): T = numerator(arg) / denominator(arg) -// -//fun > RationalFunction.reduced(): RationalFunction = -// polynomialGCD(numerator, denominator).let { -// RationalFunction( -// numerator / it, -// denominator / it -// ) -// } - -///** -// * Returns result of applying formal derivative to the polynomial. -// * -// * @param T Field where we are working now. -// * @return Result of the operator. -// */ -//fun > RationalFunction.derivative() = -// RationalFunction( -// numerator.derivative() * denominator - denominator.derivative() * numerator, -// denominator * denominator -// ) \ No newline at end of file +} \ No newline at end of file diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/misc.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/misc.kt new file mode 100644 index 000000000..8b6fac39e --- /dev/null +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/misc.kt @@ -0,0 +1,13 @@ +/* + * 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 + + +@RequiresOptIn( + message = "It's copy of operation with optimized boxing. It's currently unstable.", + level = RequiresOptIn.Level.ERROR +) +internal annotation class UnstablePolynomialBoxingOptimization \ No newline at end of file diff --git a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/functions/ListPolynomialUtilTest.kt b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/functions/ListPolynomialUtilTest.kt index c5eb8fb81..69c1611f3 100644 --- a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/functions/ListPolynomialUtilTest.kt +++ b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/functions/ListPolynomialUtilTest.kt @@ -5,6 +5,7 @@ package space.kscience.kmath.functions +import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.test.misc.Rational import space.kscience.kmath.test.misc.RationalField import kotlin.test.Test @@ -12,6 +13,7 @@ import kotlin.test.assertEquals import kotlin.test.assertFailsWith +@OptIn(UnstableKMathAPI::class) class ListPolynomialUtilTest { @Test fun test_substitute_Double() { diff --git a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/test/misc/Rational.kt b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/test/misc/Rational.kt new file mode 100644 index 000000000..72bb5942c --- /dev/null +++ b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/test/misc/Rational.kt @@ -0,0 +1,135 @@ +/* + * 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.test.misc + +import space.kscience.kmath.misc.UnstableKMathAPI +import space.kscience.kmath.operations.Field +import space.kscience.kmath.operations.NumbersAddOps + +class Rational { + companion object { + val ZERO: Rational = Rational(0L) + val ONE: Rational = Rational(1L) + } + + val numerator: Long + val denominator: Long + + internal constructor(numerator: Long, denominator: Long, toCheckInput: Boolean = true) { + if (toCheckInput) { + if (denominator == 0L) throw ArithmeticException("/ by zero") + + val greatestCommonDivider = gcd(numerator, denominator).let { if (denominator < 0L) -it else it } + + this.numerator = numerator / greatestCommonDivider + this.denominator = denominator / greatestCommonDivider + } else { + this.numerator = numerator + this.denominator = denominator + } + } + + constructor(numerator: Int, denominator: Int) : this(numerator.toLong(), denominator.toLong(), true) + constructor(numerator: Int, denominator: Long) : this(numerator.toLong(), denominator, true) + constructor(numerator: Long, denominator: Int) : this(numerator, denominator.toLong(), true) + constructor(numerator: Long, denominator: Long) : this(numerator, denominator, true) + constructor(numerator: Int) : this(numerator.toLong(), 1L, false) + constructor(numerator: Long) : this(numerator, 1L, false) + + operator fun unaryPlus(): Rational = this + operator fun unaryMinus(): Rational = Rational(-this.numerator, this.denominator) + operator fun plus(other: Rational): Rational = + Rational( + numerator * other.denominator + denominator * other.numerator, + denominator * other.denominator + ) + operator fun plus(other: Int): Rational = + Rational( + numerator + denominator * other.toLong(), + denominator + ) + operator fun plus(other: Long): Rational = + Rational( + numerator + denominator * other, + denominator + ) + operator fun minus(other: Rational): Rational = + Rational( + numerator * other.denominator - denominator * other.numerator, + denominator * other.denominator + ) + operator fun minus(other: Int): Rational = + Rational( + numerator - denominator * other.toLong(), + denominator + ) + operator fun minus(other: Long): Rational = + Rational( + numerator - denominator * other, + denominator + ) + operator fun times(other: Rational): Rational = + Rational( + numerator * other.numerator, + denominator * other.denominator + ) + operator fun times(other: Int): Rational = + Rational( + numerator * other.toLong(), + denominator + ) + operator fun times(other: Long): Rational = + Rational( + numerator * other, + denominator + ) + operator fun div(other: Rational): Rational = + Rational( + numerator * other.denominator, + denominator * other.numerator + ) + operator fun div(other: Int): Rational = + Rational( + numerator, + denominator * other.toLong() + ) + operator fun div(other: Long): Rational = + Rational( + numerator, + denominator * other + ) + override fun equals(other: Any?): Boolean = + when (other) { + is Rational -> numerator == other.numerator && denominator == other.denominator + is Int -> numerator == other && denominator == 1L + is Long -> numerator == other && denominator == 1L + else -> false + } + + override fun hashCode(): Int = 31 * numerator.hashCode() + denominator.hashCode() + + override fun toString(): String = if (denominator == 1L) "$numerator" else "$numerator/$denominator" +} + +@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") +@OptIn(UnstableKMathAPI::class) +object RationalField : Field, NumbersAddOps { + override inline val zero: Rational get() = Rational.ZERO + override inline val one: Rational get() = Rational.ONE + + override inline fun number(value: Number): Rational = Rational(value.toLong()) + + override inline fun add(left: Rational, right: Rational): Rational = left + right + override inline fun multiply(left: Rational, right: Rational): Rational = left * right + override inline fun divide(left: Rational, right: Rational): Rational = left / right + override inline fun scale(a: Rational, value: Double): Rational = a * number(value) + + override inline fun Rational.unaryMinus(): Rational = -this + override inline fun Rational.plus(arg: Rational): Rational = this + arg + override inline fun Rational.minus(arg: Rational): Rational = this - arg + override inline fun Rational.times(arg: Rational): Rational = this * arg + override inline fun Rational.div(arg: Rational): Rational = this / arg +} \ No newline at end of file diff --git a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/test/misc/misc.kt b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/test/misc/misc.kt new file mode 100644 index 000000000..873162a1b --- /dev/null +++ b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/test/misc/misc.kt @@ -0,0 +1,11 @@ +/* + * 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.test.misc + +import kotlin.math.abs + + +tailrec fun gcd(a: Long, b: Long): Long = if (a == 0L) abs(b) else gcd(b % a, a) \ No newline at end of file