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 }) }
}