104 lines
3.6 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.Ring
/**
* Represents piecewise-defined function.
*
* @param T the piece key type.
* @param R the sub-function type.
*/
public fun interface Piecewise<T, R> {
/**
* Returns the appropriate sub-function for given piece key.
*/
public fun findPiece(arg: T): R?
}
/**
* Represents piecewise-defined function where all the sub-functions are polynomials.
* @param pieces An ordered list of range-polynomial pairs. The list does not in general guarantee that there are no "holes" in it.
*/
public class PiecewisePolynomial<T : Comparable<T>>(
public val pieces: List<Pair<ClosedRange<T>, Polynomial<T>>>,
) : Piecewise<T, Polynomial<T>> {
public override fun findPiece(arg: T): Polynomial<T>? {
return if (arg < pieces.first().first.start || arg >= pieces.last().first.endInclusive)
null
else {
pieces.firstOrNull { arg in it.first }?.second
}
}
}
/**
* A [Piecewise] builder where all the pieces are ordered by the [Comparable] type instances.
*
* @param T the comparable piece key type.
* @param delimiter the initial piecewise separator
*/
public class PiecewiseBuilder<T : Comparable<T>>(delimiter: T) {
private val delimiters: MutableList<T> = arrayListOf(delimiter)
private val pieces: MutableList<Polynomial<T>> = arrayListOf()
/**
* Dynamically adds a piece to the right side (beyond maximum argument value of previous piece)
*
* @param right new rightmost position. If is less then current rightmost position, an error is thrown.
* @param piece the sub-function.
*/
public fun putRight(right: T, piece: Polynomial<T>) {
require(right > delimiters.last()) { "New delimiter should be to the right of old one" }
delimiters += right
pieces += piece
}
/**
* Dynamically adds a piece to the left side (beyond maximum argument value of previous piece)
*
* @param left the new leftmost position. If is less then current rightmost position, an error is thrown.
* @param piece the sub-function.
*/
public fun putLeft(left: T, piece: Polynomial<T>) {
require(left < delimiters.first()) { "New delimiter should be to the left of old one" }
delimiters.add(0, left)
pieces.add(0, piece)
}
public fun build(): PiecewisePolynomial<T> {
return PiecewisePolynomial(delimiters.zipWithNext { l, r -> l..r }.zip(pieces))
}
}
/**
* A builder for [PiecewisePolynomial]
*/
public fun <T : Comparable<T>> PiecewisePolynomial(
startingPoint: T,
builder: PiecewiseBuilder<T>.() -> Unit,
): PiecewisePolynomial<T> = PiecewiseBuilder(startingPoint).apply(builder).build()
/**
* Return a value of polynomial function with given [ring] an given [arg] or null if argument is outside of piecewise
* definition.
*/
public fun <T : Comparable<T>, C : Ring<T>> PiecewisePolynomial<T>.value(ring: C, arg: T): T? =
findPiece(arg)?.value(ring, arg)
/**
* Convert this polynomial to a function returning nullable value (null if argument is outside piecewise range).
*/
public fun <T : Comparable<T>, C : Ring<T>> PiecewisePolynomial<T>.asFunction(ring: C): (T) -> T? = { value(ring, it) }
/**
* Convert this polynomial to a function using [defaultValue] for arguments outside the piecewise range.
*/
public fun <T : Comparable<T>, C : Ring<T>> PiecewisePolynomial<T>.asFunction(ring: C, defaultValue: T): (T) -> T =
{ value(ring, it) ?: defaultValue }