Feature: Polynomials and rational functions #469
@ -1,92 +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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@file:OptIn(UnstableKMathAPI::class)
|
|
||||||
|
|
||||||
package space.kscience.kmath.interpolation
|
|
||||||
|
|
||||||
import space.kscience.kmath.data.XYColumnarData
|
|
||||||
import space.kscience.kmath.functions.PiecewisePolynomial
|
|
||||||
import space.kscience.kmath.functions.asFunction
|
|
||||||
import space.kscience.kmath.functions.substitute
|
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
|
||||||
import space.kscience.kmath.operations.Ring
|
|
||||||
import space.kscience.kmath.structures.Buffer
|
|
||||||
import space.kscience.kmath.structures.asBuffer
|
|
||||||
|
|
||||||
/**
|
|
||||||
* And interpolator for data with x column type [X], y column type [Y].
|
|
||||||
*/
|
|
||||||
public fun interface Interpolator<T, in X : T, Y : T> {
|
|
||||||
public fun interpolate(points: XYColumnarData<T, X, Y>): (X) -> Y
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* And interpolator returning [PiecewisePolynomial] function
|
|
||||||
*/
|
|
||||||
public interface PolynomialInterpolator<T : Comparable<T>> : Interpolator<T, T, T> {
|
|
||||||
public val algebra: Ring<T>
|
|
||||||
|
|
||||||
public fun getDefaultValue(): T = error("Out of bounds")
|
|
||||||
|
|
||||||
public fun interpolatePolynomials(points: XYColumnarData<T, T, T>): PiecewisePolynomial<T>
|
|
||||||
|
|
||||||
override fun interpolate(points: XYColumnarData<T, T, T>): (T) -> T = { x ->
|
|
||||||
interpolatePolynomials(points).substitute(algebra, x) ?: getDefaultValue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolatePolynomials(
|
|
||||||
x: Buffer<T>,
|
|
||||||
y: Buffer<T>,
|
|
||||||
): PiecewisePolynomial<T> {
|
|
||||||
val pointSet = XYColumnarData.of(x, y)
|
|
||||||
return interpolatePolynomials(pointSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolatePolynomials(
|
|
||||||
data: Map<T, T>,
|
|
||||||
): PiecewisePolynomial<T> {
|
|
||||||
val pointSet = XYColumnarData.of(data.keys.toList().asBuffer(), data.values.toList().asBuffer())
|
|
||||||
return interpolatePolynomials(pointSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolatePolynomials(
|
|
||||||
data: List<Pair<T, T>>,
|
|
||||||
): PiecewisePolynomial<T> {
|
|
||||||
val pointSet = XYColumnarData.of(data.map { it.first }.asBuffer(), data.map { it.second }.asBuffer())
|
|
||||||
return interpolatePolynomials(pointSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolate(
|
|
||||||
x: Buffer<T>,
|
|
||||||
y: Buffer<T>,
|
|
||||||
): (T) -> T? = interpolatePolynomials(x, y).asFunction(algebra)
|
|
||||||
|
|
||||||
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolate(
|
|
||||||
data: Map<T, T>,
|
|
||||||
): (T) -> T? = interpolatePolynomials(data).asFunction(algebra)
|
|
||||||
|
|
||||||
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolate(
|
|
||||||
data: List<Pair<T, T>>,
|
|
||||||
): (T) -> T? = interpolatePolynomials(data).asFunction(algebra)
|
|
||||||
|
|
||||||
|
|
||||||
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolate(
|
|
||||||
x: Buffer<T>,
|
|
||||||
y: Buffer<T>,
|
|
||||||
defaultValue: T,
|
|
||||||
): (T) -> T = interpolatePolynomials(x, y).asFunction(algebra, defaultValue)
|
|
||||||
|
|
||||||
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolate(
|
|
||||||
data: Map<T, T>,
|
|
||||||
defaultValue: T,
|
|
||||||
): (T) -> T = interpolatePolynomials(data).asFunction(algebra, defaultValue)
|
|
||||||
|
|
||||||
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolate(
|
|
||||||
data: List<Pair<T, T>>,
|
|
||||||
defaultValue: T,
|
|
||||||
): (T) -> T = interpolatePolynomials(data).asFunction(algebra, defaultValue)
|
|
@ -1,43 +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.interpolation
|
|
||||||
|
|
||||||
import space.kscience.kmath.data.XYColumnarData
|
|
||||||
import space.kscience.kmath.functions.PiecewisePolynomial
|
|
||||||
import space.kscience.kmath.functions.ListPolynomial
|
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
|
||||||
import space.kscience.kmath.operations.Field
|
|
||||||
import space.kscience.kmath.operations.invoke
|
|
||||||
|
|
||||||
@OptIn(UnstableKMathAPI::class)
|
|
||||||
internal fun <T : Comparable<T>> insureSorted(points: XYColumnarData<*, T, *>) {
|
|
||||||
for (i in 0 until points.size - 1)
|
|
||||||
require(points.x[i + 1] > points.x[i]) { "Input data is not sorted at index $i" }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reference JVM implementation: https://github.com/apache/commons-math/blob/master/src/main/java/org/apache/commons/math4/analysis/interpolation/LinearInterpolator.java
|
|
||||||
*/
|
|
||||||
public class LinearInterpolator<T : Comparable<T>>(override val algebra: Field<T>) : PolynomialInterpolator<T> {
|
|
||||||
|
|
||||||
@OptIn(UnstableKMathAPI::class)
|
|
||||||
override fun interpolatePolynomials(points: XYColumnarData<T, T, T>): PiecewisePolynomial<T> = algebra {
|
|
||||||
require(points.size > 0) { "Point array should not be empty" }
|
|
||||||
insureSorted(points)
|
|
||||||
|
|
||||||
PiecewisePolynomial(points.x[0]) {
|
|
||||||
for (i in 0 until points.size - 1) {
|
|
||||||
val slope = (points.y[i + 1] - points.y[i]) / (points.x[i + 1] - points.x[i])
|
|
||||||
val const = points.y[i] - slope * points.x[i]
|
|
||||||
val polynomial = ListPolynomial(const, slope)
|
|
||||||
putRight(points.x[i + 1], polynomial)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public val <T : Comparable<T>> Field<T>.linearInterpolator: LinearInterpolator<T>
|
|
||||||
get() = LinearInterpolator(this)
|
|
@ -1,83 +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.interpolation
|
|
||||||
|
|
||||||
import space.kscience.kmath.data.XYColumnarData
|
|
||||||
import space.kscience.kmath.functions.PiecewisePolynomial
|
|
||||||
import space.kscience.kmath.functions.ListPolynomial
|
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
|
||||||
import space.kscience.kmath.operations.DoubleField
|
|
||||||
import space.kscience.kmath.operations.Field
|
|
||||||
import space.kscience.kmath.operations.invoke
|
|
||||||
import space.kscience.kmath.structures.DoubleBuffer
|
|
||||||
import space.kscience.kmath.structures.MutableBufferFactory
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generic spline interpolator. Not recommended for performance critical places, use platform-specific and type
|
|
||||||
* specific ones.
|
|
||||||
*
|
|
||||||
* Based on
|
|
||||||
* https://github.com/apache/commons-math/blob/eb57d6d457002a0bb5336d789a3381a24599affe/src/main/java/org/apache/commons/math4/analysis/interpolation/SplineInterpolator.java
|
|
||||||
*/
|
|
||||||
public class SplineInterpolator<T : Comparable<T>>(
|
|
||||||
override val algebra: Field<T>,
|
|
||||||
public val bufferFactory: MutableBufferFactory<T>,
|
|
||||||
) : PolynomialInterpolator<T> {
|
|
||||||
//TODO possibly optimize zeroed buffers
|
|
||||||
|
|
||||||
@OptIn(UnstableKMathAPI::class)
|
|
||||||
override fun interpolatePolynomials(points: XYColumnarData<T, T, T>): PiecewisePolynomial<T> = algebra {
|
|
||||||
require(points.size >= 3) { "Can't use spline interpolator with less than 3 points" }
|
|
||||||
insureSorted(points)
|
|
||||||
// Number of intervals. The number of data points is n + 1.
|
|
||||||
val n = points.size - 1
|
|
||||||
// Differences between knot points
|
|
||||||
val h = bufferFactory(n) { i -> points.x[i + 1] - points.x[i] }
|
|
||||||
val mu = bufferFactory(n) { zero }
|
|
||||||
val z = bufferFactory(n + 1) { zero }
|
|
||||||
|
|
||||||
for (i in 1 until n) {
|
|
||||||
val g = 2.0 * (points.x[i + 1] - points.x[i - 1]) - h[i - 1] * mu[i - 1]
|
|
||||||
mu[i] = h[i] / g
|
|
||||||
z[i] =
|
|
||||||
((points.y[i + 1] * h[i - 1] - points.y[i] * (points.x[i + 1] - points.x[i - 1]) + points.y[i - 1] * h[i]) * 3.0 /
|
|
||||||
(h[i - 1] * h[i]) - h[i - 1] * z[i - 1]) / g
|
|
||||||
}
|
|
||||||
|
|
||||||
// cubic spline coefficients -- b is linear, c quadratic, d is cubic (original y's are constants)
|
|
||||||
|
|
||||||
PiecewisePolynomial(points.x[points.size - 1]) {
|
|
||||||
var cOld = zero
|
|
||||||
|
|
||||||
for (j in n - 1 downTo 0) {
|
|
||||||
val c = z[j] - mu[j] * cOld
|
|
||||||
val a = points.y[j]
|
|
||||||
val b = (points.y[j + 1] - points.y[j]) / h[j] - h[j] * (cOld + 2.0 * c) / 3.0
|
|
||||||
val d = (cOld - c) / (3.0 * h[j])
|
|
||||||
val x0 = points.x[j]
|
|
||||||
val x02 = x0 * x0
|
|
||||||
val x03 = x02 * x0
|
|
||||||
//Shift coefficients to represent absolute polynomial instead of one with an offset
|
|
||||||
val polynomial = ListPolynomial(
|
|
||||||
a - b * x0 + c * x02 - d * x03,
|
|
||||||
b - 2 * c * x0 + 3 * d * x02,
|
|
||||||
c - 3 * d * x0,
|
|
||||||
d
|
|
||||||
)
|
|
||||||
cOld = c
|
|
||||||
putLeft(x0, polynomial)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public fun <T : Comparable<T>> Field<T>.splineInterpolator(
|
|
||||||
bufferFactory: MutableBufferFactory<T>,
|
|
||||||
): SplineInterpolator<T> = SplineInterpolator(this, bufferFactory)
|
|
||||||
|
|
||||||
public val DoubleField.splineInterpolator: SplineInterpolator<Double>
|
|
||||||
get() = SplineInterpolator(this, ::DoubleBuffer)
|
|
@ -1,48 +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.integration
|
|
||||||
|
|
||||||
import space.kscience.kmath.functions.ListPolynomial
|
|
||||||
import space.kscience.kmath.functions.integrate
|
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
|
||||||
import space.kscience.kmath.operations.DoubleField
|
|
||||||
import kotlin.math.PI
|
|
||||||
import kotlin.math.sin
|
|
||||||
import kotlin.test.Test
|
|
||||||
import kotlin.test.assertEquals
|
|
||||||
|
|
||||||
@OptIn(UnstableKMathAPI::class)
|
|
||||||
class SplineIntegralTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun integratePolynomial(){
|
|
||||||
val polynomial = ListPolynomial(1.0, 2.0, 3.0)
|
|
||||||
val integral = polynomial.integrate(DoubleField,1.0..2.0)
|
|
||||||
assertEquals(11.0, integral, 0.001)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun gaussSin() {
|
|
||||||
val res = DoubleField.splineIntegrator.integrate(0.0..2 * PI, IntegrandMaxCalls(5)) { x ->
|
|
||||||
sin(x)
|
|
||||||
}
|
|
||||||
assertEquals(0.0, res.value, 1e-2)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun gaussUniform() {
|
|
||||||
val res = DoubleField.splineIntegrator.integrate(35.0..100.0, IntegrandMaxCalls(20)) { x ->
|
|
||||||
if(x in 30.0..50.0){
|
|
||||||
1.0
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assertEquals(15.0, res.value, 0.5)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,29 +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.interpolation
|
|
||||||
|
|
||||||
import space.kscience.kmath.operations.DoubleField
|
|
||||||
import kotlin.test.Test
|
|
||||||
import kotlin.test.assertEquals
|
|
||||||
|
|
||||||
internal class LinearInterpolatorTest {
|
|
||||||
@Test
|
|
||||||
fun testInterpolation() {
|
|
||||||
val data = listOf(
|
|
||||||
0.0 to 0.0,
|
|
||||||
1.0 to 1.0,
|
|
||||||
2.0 to 3.0,
|
|
||||||
3.0 to 4.0
|
|
||||||
)
|
|
||||||
|
|
||||||
//val polynomial: PiecewisePolynomial<Double> = DoubleField.linearInterpolator.interpolatePolynomials(data)
|
|
||||||
val function = DoubleField.linearInterpolator.interpolate(data)
|
|
||||||
assertEquals(null, function(-1.0))
|
|
||||||
assertEquals(0.5, function(0.5))
|
|
||||||
assertEquals(2.0, function(1.5))
|
|
||||||
assertEquals(3.0, function(2.0))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +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.interpolation
|
|
||||||
|
|
||||||
import space.kscience.kmath.operations.DoubleField
|
|
||||||
import kotlin.math.PI
|
|
||||||
import kotlin.math.sin
|
|
||||||
import kotlin.test.Test
|
|
||||||
import kotlin.test.assertEquals
|
|
||||||
|
|
||||||
internal class SplineInterpolatorTest {
|
|
||||||
@Test
|
|
||||||
fun testInterpolation() {
|
|
||||||
val data = (0..10).map {
|
|
||||||
val x = it.toDouble() / 5 * PI
|
|
||||||
x to sin(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
//val polynomial: PiecewisePolynomial<Double> = DoubleField.splineInterpolator.interpolatePolynomials(data)
|
|
||||||
|
|
||||||
val function = DoubleField.splineInterpolator.interpolate(data, Double.NaN)
|
|
||||||
|
|
||||||
assertEquals(Double.NaN, function(-1.0))
|
|
||||||
assertEquals(sin(0.5), function(0.5), 0.1)
|
|
||||||
assertEquals(sin(1.5), function(1.5), 0.1)
|
|
||||||
assertEquals(sin(2.0), function(2.0), 0.1)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,142 +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.test.misc
|
|
||||||
|
|
||||||
import space.kscience.kmath.functions.ListPolynomial
|
|
||||||
import space.kscience.kmath.functions.ListPolynomialSpace
|
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
|
||||||
import space.kscience.kmath.operations.Ring
|
|
||||||
|
|
||||||
|
|
||||||
class IntModulo {
|
|
||||||
val residue: Int
|
|
||||||
val modulus: Int
|
|
||||||
|
|
||||||
@PublishedApi
|
|
||||||
internal constructor(residue: Int, modulus: Int, toCheckInput: Boolean = true) {
|
|
||||||
if (toCheckInput) {
|
|
||||||
require(modulus != 0) { "modulus can not be zero" }
|
|
||||||
this.modulus = if (modulus < 0) -modulus else modulus
|
|
||||||
this.residue = residue.mod(modulus)
|
|
||||||
} else {
|
|
||||||
this.residue = residue
|
|
||||||
this.modulus = modulus
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(residue: Int, modulus: Int) : this(residue, modulus, true)
|
|
||||||
|
|
||||||
operator fun unaryPlus(): IntModulo = this
|
|
||||||
operator fun unaryMinus(): IntModulo =
|
|
||||||
IntModulo(
|
|
||||||
if (residue == 0) 0 else modulus - residue,
|
|
||||||
modulus,
|
|
||||||
toCheckInput = false
|
|
||||||
)
|
|
||||||
operator fun plus(other: IntModulo): IntModulo {
|
|
||||||
require(modulus == other.modulus) { "can not add two residue different modulo" }
|
|
||||||
return IntModulo(
|
|
||||||
(residue + other.residue) % modulus,
|
|
||||||
modulus,
|
|
||||||
toCheckInput = false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
operator fun plus(other: Int): IntModulo =
|
|
||||||
IntModulo(
|
|
||||||
(residue + other) % modulus,
|
|
||||||
modulus,
|
|
||||||
toCheckInput = false
|
|
||||||
)
|
|
||||||
operator fun minus(other: IntModulo): IntModulo {
|
|
||||||
require(modulus == other.modulus) { "can not subtract two residue different modulo" }
|
|
||||||
return IntModulo(
|
|
||||||
(residue - other.residue) % modulus,
|
|
||||||
modulus,
|
|
||||||
toCheckInput = false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
operator fun minus(other: Int): IntModulo =
|
|
||||||
IntModulo(
|
|
||||||
(residue - other) % modulus,
|
|
||||||
modulus,
|
|
||||||
toCheckInput = false
|
|
||||||
)
|
|
||||||
operator fun times(other: IntModulo): IntModulo {
|
|
||||||
require(modulus == other.modulus) { "can not multiply two residue different modulo" }
|
|
||||||
return IntModulo(
|
|
||||||
(residue * other.residue) % modulus,
|
|
||||||
modulus,
|
|
||||||
toCheckInput = false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
operator fun times(other: Int): IntModulo =
|
|
||||||
IntModulo(
|
|
||||||
(residue * other) % modulus,
|
|
||||||
modulus,
|
|
||||||
toCheckInput = false
|
|
||||||
)
|
|
||||||
operator fun div(other: IntModulo): IntModulo {
|
|
||||||
require(modulus == other.modulus) { "can not divide two residue different modulo" }
|
|
||||||
val (reciprocalCandidate, gcdOfOtherResidueAndModulus) = bezoutIdentityWithGCD(other.residue, modulus)
|
|
||||||
require(gcdOfOtherResidueAndModulus == 1) { "can not divide to residue that has non-trivial GCD with modulo" }
|
|
||||||
return IntModulo(
|
|
||||||
(residue * reciprocalCandidate) % modulus,
|
|
||||||
modulus,
|
|
||||||
toCheckInput = false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
operator fun div(other: Int): IntModulo {
|
|
||||||
val (reciprocalCandidate, gcdOfOtherResidueAndModulus) = bezoutIdentityWithGCD(other, modulus)
|
|
||||||
require(gcdOfOtherResidueAndModulus == 1) { "can not divide to residue that has non-trivial GCD with modulo" }
|
|
||||||
return IntModulo(
|
|
||||||
(residue * reciprocalCandidate) % modulus,
|
|
||||||
modulus,
|
|
||||||
toCheckInput = false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
override fun equals(other: Any?): Boolean =
|
|
||||||
when (other) {
|
|
||||||
is IntModulo -> residue == other.residue && modulus == other.modulus
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int = residue.hashCode()
|
|
||||||
|
|
||||||
override fun toString(): String = "$residue mod $modulus"
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
|
|
||||||
@OptIn(UnstableKMathAPI::class)
|
|
||||||
class IntModuloRing : Ring<IntModulo> {
|
|
||||||
|
|
||||||
val modulus: Int
|
|
||||||
|
|
||||||
constructor(modulus: Int) {
|
|
||||||
require(modulus != 0) { "modulus can not be zero" }
|
|
||||||
this.modulus = if (modulus < 0) -modulus else modulus
|
|
||||||
}
|
|
||||||
|
|
||||||
override inline val zero: IntModulo get() = IntModulo(0, modulus, toCheckInput = false)
|
|
||||||
override inline val one: IntModulo get() = IntModulo(1, modulus, toCheckInput = false)
|
|
||||||
|
|
||||||
fun number(arg: Int) = IntModulo(arg, modulus, toCheckInput = false)
|
|
||||||
|
|
||||||
override inline fun add(left: IntModulo, right: IntModulo): IntModulo = left + right
|
|
||||||
override inline fun multiply(left: IntModulo, right: IntModulo): IntModulo = left * right
|
|
||||||
|
|
||||||
override inline fun IntModulo.unaryMinus(): IntModulo = -this
|
|
||||||
override inline fun IntModulo.plus(arg: IntModulo): IntModulo = this + arg
|
|
||||||
override inline fun IntModulo.minus(arg: IntModulo): IntModulo = this - arg
|
|
||||||
override inline fun IntModulo.times(arg: IntModulo): IntModulo = this * arg
|
|
||||||
inline fun IntModulo.div(arg: IntModulo): IntModulo = this / arg
|
|
||||||
}
|
|
||||||
|
|
||||||
fun ListPolynomialSpace<IntModulo, IntModuloRing>.number(arg: Int) = IntModulo(arg, ring.modulus, toCheckInput = false)
|
|
||||||
|
|
||||||
fun ListPolynomialSpace<IntModulo, IntModuloRing>.ListPolynomial(vararg coefs: Int): ListPolynomial<IntModulo> =
|
|
||||||
ListPolynomial(coefs.map { IntModulo(it, ring.modulus) })
|
|
||||||
fun IntModuloRing.ListPolynomial(vararg coefs: Int): ListPolynomial<IntModulo> =
|
|
||||||
ListPolynomial(coefs.map { IntModulo(it, modulus) })
|
|
@ -1,135 +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.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<Rational>, NumbersAddOps<Rational> {
|
|
||||||
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
|
|
||||||
}
|
|
@ -1,107 +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.test.misc
|
|
||||||
|
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
|
||||||
import space.kscience.kmath.operations.*
|
|
||||||
|
|
||||||
class RationalWithMemorization private constructor(
|
|
||||||
val value: Rational,
|
|
||||||
override val memory : OperationsMemory
|
|
||||||
): WithMemorization {
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* Constant containing the zero (the additive identity) of the [Rational] field.
|
|
||||||
*/
|
|
||||||
public val ZERO: RationalWithMemorization = RationalWithMemorization(Rational.ZERO, object : Endpoint {})
|
|
||||||
/**
|
|
||||||
* Constant containing the one (the multiplicative identity) of the [Rational] field.
|
|
||||||
*/
|
|
||||||
public val ONE: RationalWithMemorization = RationalWithMemorization(Rational.ONE, object : Endpoint {})
|
|
||||||
}
|
|
||||||
constructor(numerator: Int, denominator: Int) : this(Rational(numerator, denominator), object : Endpoint {})
|
|
||||||
constructor(numerator: Int, denominator: Long) : this(Rational(numerator, denominator), object : Endpoint {})
|
|
||||||
constructor(numerator: Long, denominator: Int) : this(Rational(numerator, denominator), object : Endpoint {})
|
|
||||||
constructor(numerator: Long, denominator: Long) : this(Rational(numerator, denominator), object : Endpoint {})
|
|
||||||
constructor(numerator: Int) : this(Rational(numerator), object : Endpoint {})
|
|
||||||
constructor(numerator: Long) : this(Rational(numerator), object : Endpoint {})
|
|
||||||
|
|
||||||
operator fun unaryPlus(): RationalWithMemorization = this
|
|
||||||
operator fun unaryMinus(): RationalWithMemorization = RationalWithMemorization(
|
|
||||||
-value,
|
|
||||||
object : Negation {
|
|
||||||
override val negated: OperationsMemory = memory
|
|
||||||
}
|
|
||||||
)
|
|
||||||
operator fun plus(other: RationalWithMemorization): RationalWithMemorization = RationalWithMemorization(
|
|
||||||
value + other.value,
|
|
||||||
object : Sum {
|
|
||||||
override val augend: OperationsMemory = memory
|
|
||||||
override val addend: OperationsMemory = other.memory
|
|
||||||
}
|
|
||||||
)
|
|
||||||
operator fun minus(other: RationalWithMemorization): RationalWithMemorization = RationalWithMemorization(
|
|
||||||
value - other.value,
|
|
||||||
object : Difference {
|
|
||||||
override val minuend: OperationsMemory = memory
|
|
||||||
override val subtrahend: OperationsMemory = other.memory
|
|
||||||
}
|
|
||||||
)
|
|
||||||
operator fun times(other: RationalWithMemorization): RationalWithMemorization = RationalWithMemorization(
|
|
||||||
value * other.value,
|
|
||||||
object : Product {
|
|
||||||
override val multiplicand: OperationsMemory = memory
|
|
||||||
override val multiplier: OperationsMemory = other.memory
|
|
||||||
}
|
|
||||||
)
|
|
||||||
operator fun div(other: RationalWithMemorization): RationalWithMemorization = RationalWithMemorization(
|
|
||||||
value / other.value,
|
|
||||||
object : Quotient {
|
|
||||||
override val dividend: OperationsMemory = memory
|
|
||||||
override val divisor: OperationsMemory = other.memory
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean =
|
|
||||||
other is RationalWithMemorization && value == other.value
|
|
||||||
|
|
||||||
override fun hashCode(): Int = value.hashCode()
|
|
||||||
|
|
||||||
override fun toString(): String = value.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
|
|
||||||
object RationalWithMemorizationRing : Ring<RationalWithMemorization> {
|
|
||||||
override inline val zero: RationalWithMemorization get() = RationalWithMemorization.ZERO
|
|
||||||
override inline val one: RationalWithMemorization get() = RationalWithMemorization.ONE
|
|
||||||
|
|
||||||
override inline fun add(left: RationalWithMemorization, right: RationalWithMemorization): RationalWithMemorization = left + right
|
|
||||||
override inline fun multiply(left: RationalWithMemorization, right: RationalWithMemorization): RationalWithMemorization = left * right
|
|
||||||
|
|
||||||
override inline fun RationalWithMemorization.unaryMinus(): RationalWithMemorization = -this
|
|
||||||
override inline fun RationalWithMemorization.plus(arg: RationalWithMemorization): RationalWithMemorization = this + arg
|
|
||||||
override inline fun RationalWithMemorization.minus(arg: RationalWithMemorization): RationalWithMemorization = this - arg
|
|
||||||
override inline fun RationalWithMemorization.times(arg: RationalWithMemorization): RationalWithMemorization = this * arg
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
|
|
||||||
object RationalWithMemorizationField : Field<RationalWithMemorization> {
|
|
||||||
override inline val zero: RationalWithMemorization get() = RationalWithMemorization.ZERO
|
|
||||||
override inline val one: RationalWithMemorization get() = RationalWithMemorization.ONE
|
|
||||||
|
|
||||||
override inline fun number(value: Number): RationalWithMemorization = RationalWithMemorization(value.toLong())
|
|
||||||
|
|
||||||
override inline fun add(left: RationalWithMemorization, right: RationalWithMemorization): RationalWithMemorization = left + right
|
|
||||||
override inline fun multiply(left: RationalWithMemorization, right: RationalWithMemorization): RationalWithMemorization = left * right
|
|
||||||
override inline fun divide(left: RationalWithMemorization, right: RationalWithMemorization): RationalWithMemorization = left / right
|
|
||||||
override inline fun scale(a: RationalWithMemorization, value: Double): RationalWithMemorization = a * number(value)
|
|
||||||
|
|
||||||
override inline fun RationalWithMemorization.unaryMinus(): RationalWithMemorization = -this
|
|
||||||
override inline fun RationalWithMemorization.plus(arg: RationalWithMemorization): RationalWithMemorization = this + arg
|
|
||||||
override inline fun RationalWithMemorization.minus(arg: RationalWithMemorization): RationalWithMemorization = this - arg
|
|
||||||
override inline fun RationalWithMemorization.times(arg: RationalWithMemorization): RationalWithMemorization = this * arg
|
|
||||||
override inline fun RationalWithMemorization.div(arg: RationalWithMemorization): RationalWithMemorization = this / arg
|
|
||||||
}
|
|
@ -1,51 +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.test.misc
|
|
||||||
|
|
||||||
sealed interface OperationsMemory
|
|
||||||
|
|
||||||
interface Endpoint: OperationsMemory
|
|
||||||
|
|
||||||
interface Negation: OperationsMemory {
|
|
||||||
val negated: OperationsMemory
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Sum: OperationsMemory {
|
|
||||||
val augend: OperationsMemory
|
|
||||||
val addend: OperationsMemory
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Difference: OperationsMemory {
|
|
||||||
val minuend: OperationsMemory
|
|
||||||
val subtrahend: OperationsMemory
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Product: OperationsMemory {
|
|
||||||
val multiplicand: OperationsMemory
|
|
||||||
val multiplier: OperationsMemory
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Quotient: OperationsMemory {
|
|
||||||
val dividend: OperationsMemory
|
|
||||||
val divisor: OperationsMemory
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun equalMemories(one: OperationsMemory, other: OperationsMemory) : Boolean =
|
|
||||||
when(one) {
|
|
||||||
is Negation -> other is Negation && equalMemories(one.negated, other.negated)
|
|
||||||
is Sum -> other is Sum && equalMemories(one.augend, other.augend) && equalMemories(one.addend, other.addend)
|
|
||||||
is Difference -> other is Difference && equalMemories(one.minuend, other.minuend) && equalMemories(one.subtrahend, other.subtrahend)
|
|
||||||
is Product -> other is Product && equalMemories(one.multiplicand, other.multiplicand) && equalMemories(one.multiplier, other.multiplier)
|
|
||||||
is Quotient -> other is Quotient && equalMemories(one.dividend, other.dividend) && equalMemories(one.divisor, other.divisor)
|
|
||||||
is Endpoint -> one === other
|
|
||||||
}
|
|
||||||
|
|
||||||
interface WithMemorization {
|
|
||||||
val memory: OperationsMemory
|
|
||||||
}
|
|
||||||
|
|
||||||
fun equalMemories(one: WithMemorization, other: WithMemorization) : Boolean = equalMemories(one.memory, other.memory)
|
|
@ -1,31 +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.test.misc
|
|
||||||
|
|
||||||
// TODO: Move to corresponding module "kmath-number-theory"
|
|
||||||
|
|
||||||
import kotlin.math.abs
|
|
||||||
|
|
||||||
|
|
||||||
data class BezoutIdentityWithGCD<T>(val first: T, val second: T, val gcd: T)
|
|
||||||
|
|
||||||
tailrec fun gcd(a: Long, b: Long): Long = if (a == 0L) abs(b) else gcd(b % a, a)
|
|
||||||
|
|
||||||
fun bezoutIdentityWithGCD(a: Int, b: Int): BezoutIdentityWithGCD<Int> =
|
|
||||||
when {
|
|
||||||
a < 0 && b < 0 -> with(bezoutIdentityWithGCDInternalLogic(-a, -b, 1, 0, 0, 1)) { BezoutIdentityWithGCD(-first, -second, gcd) }
|
|
||||||
a < 0 -> with(bezoutIdentityWithGCDInternalLogic(-a, b, 1, 0, 0, 1)) { BezoutIdentityWithGCD(-first, second, gcd) }
|
|
||||||
b < 0 -> with(bezoutIdentityWithGCDInternalLogic(a, -b, 1, 0, 0, 1)) { BezoutIdentityWithGCD(first, -second, gcd) }
|
|
||||||
else -> bezoutIdentityWithGCDInternalLogic(a, b, 1, 0, 0, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal tailrec fun bezoutIdentityWithGCDInternalLogic(a: Int, b: Int, m1: Int, m2: Int, m3: Int, m4: Int): BezoutIdentityWithGCD<Int> =
|
|
||||||
if (b == 0) BezoutIdentityWithGCD(m1, m3, a)
|
|
||||||
else {
|
|
||||||
val quotient = a / b
|
|
||||||
val reminder = a % b
|
|
||||||
bezoutIdentityWithGCDInternalLogic(b, reminder, m2, m1 - quotient * m2, m4, m3 - quotient * m4)
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user