forked from kscience/kmath
451 lines
17 KiB
Kotlin
451 lines
17 KiB
Kotlin
/*
|
|
* 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.*
|
|
import kotlin.math.max
|
|
import kotlin.math.min
|
|
|
|
/**
|
|
* Polynomial model without fixation on specific context they are applied to.
|
|
*
|
|
* @param coefficients constant is the leftmost coefficient.
|
|
*/
|
|
public data class Polynomial<C>(
|
|
/**
|
|
* 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
|
|
* ```
|
|
* 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 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.
|
|
*/
|
|
public val coefficients: List<C>
|
|
) : AbstractPolynomial<C> {
|
|
override fun toString(): String = "Polynomial$coefficients"
|
|
}
|
|
|
|
/**
|
|
* Returns a [Polynomial] instance with given [coefficients]. The collection of coefficients will be reversed if
|
|
* [reverse] parameter is true.
|
|
*/
|
|
@Suppress("FunctionName")
|
|
public fun <C> Polynomial(coefficients: List<C>, reverse: Boolean = false): Polynomial<C> =
|
|
Polynomial(with(coefficients) { if (reverse) reversed() else this })
|
|
|
|
/**
|
|
* Returns a [Polynomial] instance with given [coefficients]. The collection of coefficients will be reversed if
|
|
* [reverse] parameter is true.
|
|
*/
|
|
@Suppress("FunctionName")
|
|
public fun <C> Polynomial(vararg coefficients: C, reverse: Boolean = false): Polynomial<C> =
|
|
Polynomial(with(coefficients) { if (reverse) reversed() else toList() })
|
|
|
|
public fun <C> C.asPolynomial() : Polynomial<C> = Polynomial(listOf(this))
|
|
|
|
/**
|
|
* Space of univariate 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 open class PolynomialSpace<C, A : Ring<C>>(
|
|
public override val ring: A,
|
|
) : AbstractPolynomialSpaceOverRing<C, Polynomial<C>, A> {
|
|
/**
|
|
* Returns sum of the polynomial and the integer represented as polynomial.
|
|
*
|
|
* The operation is equivalent to adding [other] copies of unit polynomial to [this].
|
|
*/
|
|
public override operator fun Polynomial<C>.plus(other: Int): Polynomial<C> =
|
|
if (other == 0) this
|
|
else
|
|
Polynomial(
|
|
coefficients
|
|
.toMutableList()
|
|
.apply {
|
|
val result = getOrElse(0) { constantZero } + other
|
|
val isResultZero = result.isZero()
|
|
|
|
when {
|
|
size == 0 && !isResultZero -> add(result)
|
|
size > 1 || !isResultZero -> this[0] = result
|
|
else -> clear()
|
|
}
|
|
}
|
|
)
|
|
/**
|
|
* Returns difference between the polynomial and the integer represented as polynomial.
|
|
*
|
|
* The operation is equivalent to subtraction [other] copies of unit polynomial from [this].
|
|
*/
|
|
public override operator fun Polynomial<C>.minus(other: Int): Polynomial<C> =
|
|
if (other == 0) this
|
|
else
|
|
Polynomial(
|
|
coefficients
|
|
.toMutableList()
|
|
.apply {
|
|
val result = getOrElse(0) { constantZero } - other
|
|
val isResultZero = result.isZero()
|
|
|
|
when {
|
|
size == 0 && !isResultZero -> add(result)
|
|
size > 1 || !isResultZero -> this[0] = result
|
|
else -> clear()
|
|
}
|
|
}
|
|
)
|
|
/**
|
|
* Returns product of the polynomial and the integer represented as polynomial.
|
|
*
|
|
* The operation is equivalent to sum of [other] copies of [this].
|
|
*/
|
|
public override operator fun Polynomial<C>.times(other: Int): Polynomial<C> =
|
|
if (other == 0) zero
|
|
else Polynomial(
|
|
coefficients
|
|
.subList(0, degree + 1)
|
|
.map { it * other }
|
|
)
|
|
|
|
/**
|
|
* Returns sum of the integer represented as polynomial and the polynomial.
|
|
*
|
|
* The operation is equivalent to adding [this] copies of unit polynomial to [other].
|
|
*/
|
|
public override operator fun Int.plus(other: Polynomial<C>): Polynomial<C> =
|
|
if (this == 0) other
|
|
else
|
|
Polynomial(
|
|
other.coefficients
|
|
.toMutableList()
|
|
.apply {
|
|
val result = this@plus + getOrElse(0) { constantZero }
|
|
val isResultZero = result.isZero()
|
|
|
|
when {
|
|
size == 0 && !isResultZero -> add(result)
|
|
size > 1 || !isResultZero -> this[0] = result
|
|
else -> clear()
|
|
}
|
|
}
|
|
)
|
|
/**
|
|
* Returns difference between the integer represented as polynomial and the polynomial.
|
|
*
|
|
* The operation is equivalent to subtraction [this] copies of unit polynomial from [other].
|
|
*/
|
|
public override operator fun Int.minus(other: Polynomial<C>): Polynomial<C> =
|
|
if (this == 0) other
|
|
else
|
|
Polynomial(
|
|
other.coefficients
|
|
.toMutableList()
|
|
.apply {
|
|
forEachIndexed { index, c -> if (index != 0) this[index] = -c }
|
|
|
|
val result = this@minus - getOrElse(0) { constantZero }
|
|
val isResultZero = result.isZero()
|
|
|
|
when {
|
|
size == 0 && !isResultZero -> add(result)
|
|
size > 1 || !isResultZero -> this[0] = result
|
|
else -> clear()
|
|
}
|
|
}
|
|
)
|
|
/**
|
|
* Returns product of the integer represented as polynomial and the polynomial.
|
|
*
|
|
* The operation is equivalent to sum of [this] copies of [other].
|
|
*/
|
|
public override operator fun Int.times(other: Polynomial<C>): Polynomial<C> =
|
|
if (this == 0) zero
|
|
else Polynomial(
|
|
other.coefficients
|
|
.subList(0, other.degree + 1)
|
|
.map { it * this }
|
|
)
|
|
|
|
/**
|
|
* Returns sum of the constant represented as polynomial and the polynomial.
|
|
*/
|
|
public override operator fun C.plus(other: Polynomial<C>): Polynomial<C> =
|
|
if (this.isZero()) other
|
|
else with(other.coefficients) {
|
|
if (isEmpty()) Polynomial(listOf(this@plus))
|
|
else Polynomial(
|
|
toMutableList()
|
|
.apply {
|
|
val result = if (size == 0) this@plus else this@plus + get(0)
|
|
val isResultZero = result.isZero()
|
|
|
|
when {
|
|
size == 0 && !isResultZero -> add(result)
|
|
size > 1 || !isResultZero -> this[0] = result
|
|
else -> clear()
|
|
}
|
|
}
|
|
)
|
|
}
|
|
/**
|
|
* Returns difference between the constant represented as polynomial and the polynomial.
|
|
*/
|
|
public override operator fun C.minus(other: Polynomial<C>): Polynomial<C> =
|
|
if (this.isZero()) other
|
|
else with(other.coefficients) {
|
|
if (isEmpty()) Polynomial(listOf(-this@minus))
|
|
else Polynomial(
|
|
toMutableList()
|
|
.apply {
|
|
forEachIndexed { index, c -> if (index != 0) this[index] = -c }
|
|
|
|
val result = if (size == 0) this@minus else this@minus - get(0)
|
|
val isResultZero = result.isZero()
|
|
|
|
when {
|
|
size == 0 && !isResultZero -> add(result)
|
|
size > 1 || !isResultZero -> this[0] = result
|
|
else -> clear()
|
|
}
|
|
}
|
|
)
|
|
}
|
|
/**
|
|
* Returns product of the constant represented as polynomial and the polynomial.
|
|
*/
|
|
public override operator fun C.times(other: Polynomial<C>): Polynomial<C> =
|
|
if (this.isZero()) other
|
|
else Polynomial(
|
|
other.coefficients
|
|
.subList(0, other.degree + 1)
|
|
.map { it * this }
|
|
)
|
|
|
|
/**
|
|
* Returns sum of the constant represented as polynomial and the polynomial.
|
|
*/
|
|
public override operator fun Polynomial<C>.plus(other: C): Polynomial<C> =
|
|
if (other.isZero()) this
|
|
else with(coefficients) {
|
|
if (isEmpty()) Polynomial(listOf(other))
|
|
else Polynomial(
|
|
toMutableList()
|
|
.apply {
|
|
val result = if (size == 0) other else get(0) + other
|
|
val isResultZero = result.isZero()
|
|
|
|
when {
|
|
size == 0 && !isResultZero -> add(result)
|
|
size > 1 || !isResultZero -> this[0] = result
|
|
else -> clear()
|
|
}
|
|
}
|
|
)
|
|
}
|
|
/**
|
|
* Returns difference between the constant represented as polynomial and the polynomial.
|
|
*/
|
|
public override operator fun Polynomial<C>.minus(other: C): Polynomial<C> =
|
|
if (other.isZero()) this
|
|
else with(coefficients) {
|
|
if (isEmpty()) Polynomial(listOf(other))
|
|
else Polynomial(
|
|
toMutableList()
|
|
.apply {
|
|
val result = if (size == 0) other else get(0) - other
|
|
val isResultZero = result.isZero()
|
|
|
|
when {
|
|
size == 0 && !isResultZero -> add(result)
|
|
size > 1 || !isResultZero -> this[0] = result
|
|
else -> clear()
|
|
}
|
|
}
|
|
)
|
|
}
|
|
/**
|
|
* Returns product of the constant represented as polynomial and the polynomial.
|
|
*/
|
|
public override operator fun Polynomial<C>.times(other: C): Polynomial<C> =
|
|
if (other.isZero()) this
|
|
else Polynomial(
|
|
coefficients
|
|
.subList(0, degree + 1)
|
|
.map { it * other }
|
|
)
|
|
|
|
/**
|
|
* Returns negation of the polynomial.
|
|
*/
|
|
public override operator fun Polynomial<C>.unaryMinus(): Polynomial<C> =
|
|
Polynomial(coefficients.map { -it })
|
|
/**
|
|
* Returns sum of the polynomials.
|
|
*/
|
|
public override operator fun Polynomial<C>.plus(other: Polynomial<C>): Polynomial<C> =
|
|
Polynomial(
|
|
(0..max(degree, other.degree))
|
|
.map {
|
|
when {
|
|
it > degree -> other.coefficients[it]
|
|
it > other.degree -> coefficients[it]
|
|
else -> coefficients[it] + other.coefficients[it]
|
|
}
|
|
}
|
|
.ifEmpty { listOf(constantZero) }
|
|
)
|
|
/**
|
|
* Returns difference of the polynomials.
|
|
*/
|
|
public override operator fun Polynomial<C>.minus(other: Polynomial<C>): Polynomial<C> =
|
|
Polynomial(
|
|
(0..max(degree, other.degree))
|
|
.map {
|
|
when {
|
|
it > degree -> -other.coefficients[it]
|
|
it > other.degree -> coefficients[it]
|
|
else -> coefficients[it] - other.coefficients[it]
|
|
}
|
|
}
|
|
.ifEmpty { listOf(constantZero) }
|
|
)
|
|
/**
|
|
* Returns product of the polynomials.
|
|
*/
|
|
public override operator fun Polynomial<C>.times(other: Polynomial<C>): Polynomial<C> {
|
|
val thisDegree = degree
|
|
val otherDegree = other.degree
|
|
return when {
|
|
thisDegree == -1 -> zero
|
|
otherDegree == -1 -> zero
|
|
else ->
|
|
Polynomial(
|
|
(0..(thisDegree + otherDegree))
|
|
.map { d ->
|
|
(max(0, d - otherDegree)..min(thisDegree, d))
|
|
.map { coefficients[it] * other.coefficients[d - it] }
|
|
.reduce { acc, rational -> acc + rational }
|
|
}
|
|
.run { subList(0, indexOfLast { it.isNotZero() } + 1) }
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if the instant is zero polynomial.
|
|
*/
|
|
public override fun Polynomial<C>.isZero(): Boolean = coefficients.all { it.isZero() }
|
|
/**
|
|
* Check if the instant is unit polynomial.
|
|
*/
|
|
public override fun Polynomial<C>.isOne(): Boolean =
|
|
with(coefficients) {
|
|
isNotEmpty() && withIndex().any { (index, c) -> if (index == 0) c.isOne() else c.isZero() }
|
|
}
|
|
/**
|
|
* Check if the instant is minus unit polynomial.
|
|
*/
|
|
public override fun Polynomial<C>.isMinusOne(): Boolean =
|
|
with(coefficients) {
|
|
isNotEmpty() && withIndex().any { (index, c) -> if (index == 0) c.isMinusOne() else c.isZero() }
|
|
}
|
|
|
|
/**
|
|
* Instance of zero polynomial (zero of the polynomial ring).
|
|
*/
|
|
override val zero: Polynomial<C> = Polynomial(emptyList())
|
|
/**
|
|
* Instance of unit constant (unit of the underlying ring).
|
|
*/
|
|
override val one: Polynomial<C> = Polynomial(listOf(constantZero))
|
|
|
|
/**
|
|
* Checks equality of the polynomials.
|
|
*/
|
|
public override infix fun Polynomial<C>.equalsTo(other: Polynomial<C>): Boolean =
|
|
when {
|
|
this === other -> true
|
|
else -> {
|
|
if (this.degree == other.degree)
|
|
(0..degree).all { coefficients[it] == other.coefficients[it] }
|
|
else false
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 Polynomial<C>.degree: Int get() = coefficients.indexOfLast { it != constantZero }
|
|
|
|
/**
|
|
* If polynomial is a constant polynomial represents and returns it as constant.
|
|
* Otherwise, (when the polynomial is not constant polynomial) returns `null`.
|
|
*/
|
|
public override fun Polynomial<C>.asConstantOrNull(): C? =
|
|
with(coefficients) {
|
|
when {
|
|
isEmpty() -> constantZero
|
|
degree > 0 -> null
|
|
else -> first()
|
|
}
|
|
}
|
|
|
|
@Suppress("NOTHING_TO_INLINE")
|
|
public inline fun Polynomial<C>.substitute(argument: C): C = this.substitute(ring, argument)
|
|
@Suppress("NOTHING_TO_INLINE")
|
|
public inline fun Polynomial<C>.substitute(argument: Polynomial<C>): Polynomial<C> = this.substitute(ring, argument)
|
|
|
|
@Suppress("NOTHING_TO_INLINE")
|
|
public inline fun Polynomial<C>.asFunction(): (C) -> C = { this.substitute(ring, it) }
|
|
@Suppress("NOTHING_TO_INLINE")
|
|
public inline fun Polynomial<C>.asFunctionOnConstants(): (C) -> C = { this.substitute(ring, it) }
|
|
@Suppress("NOTHING_TO_INLINE")
|
|
public inline fun Polynomial<C>.asFunctionOnPolynomials(): (Polynomial<C>) -> Polynomial<C> = { this.substitute(ring, it) }
|
|
|
|
/**
|
|
* Evaluates the polynomial for the given value [arg].
|
|
*/
|
|
@Suppress("NOTHING_TO_INLINE")
|
|
public inline operator fun Polynomial<C>.invoke(argument: C): C = this.substitute(ring, argument)
|
|
@Suppress("NOTHING_TO_INLINE")
|
|
public inline operator fun Polynomial<C>.invoke(argument: Polynomial<C>): Polynomial<C> = this.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 ScalablePolynomialSpace<C, A>(
|
|
ring: A,
|
|
) : PolynomialSpace<C, A>(ring), ScaleOperations<Polynomial<C>> where A : Ring<C>, A : ScaleOperations<C> {
|
|
|
|
override fun scale(a: Polynomial<C>, value: Double): Polynomial<C> =
|
|
ring { Polynomial(List(a.coefficients.size) { index -> a.coefficients[index] * value }) }
|
|
|
|
}
|