diff --git a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/functions/ListPolynomialTest.kt b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/functions/ListPolynomialTest.kt index 5401be707..c9950fac5 100644 --- a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/functions/ListPolynomialTest.kt +++ b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/functions/ListPolynomialTest.kt @@ -12,7 +12,7 @@ import kotlin.test.* class ListPolynomialTest { @Test fun test_Polynomial_Int_plus() { - RationalField.listPolynomial { + RationalField.listPolynomialSpace { assertEquals( ListPolynomial(Rational(-22, 9), Rational(-8, 9), Rational(-8, 7)), ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)) + -3, @@ -52,7 +52,7 @@ class ListPolynomialTest { } @Test fun test_Polynomial_Int_minus() { - RationalField.listPolynomial { + RationalField.listPolynomialSpace { assertEquals( ListPolynomial(Rational(32, 9), Rational(-8, 9), Rational(-8, 7)), ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)) - -3, @@ -92,7 +92,7 @@ class ListPolynomialTest { } @Test fun test_Polynomial_Int_times() { - IntModuloRing(35).listPolynomial { + IntModuloRing(35).listPolynomialSpace { assertEquals( ListPolynomial(34, 2, 1, 20, 2), ListPolynomial(22, 26, 13, 15, 26) * 27, @@ -107,7 +107,7 @@ class ListPolynomialTest { } @Test fun test_Int_Polynomial_plus() { - RationalField.listPolynomial { + RationalField.listPolynomialSpace { assertEquals( ListPolynomial(Rational(-22, 9), Rational(-8, 9), Rational(-8, 7)), -3 + ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)), @@ -147,7 +147,7 @@ class ListPolynomialTest { } @Test fun test_Int_Polynomial_minus() { - RationalField.listPolynomial { + RationalField.listPolynomialSpace { assertEquals( ListPolynomial(Rational(32, 9), Rational(-8, 9), Rational(-8, 7)), 3 - ListPolynomial(Rational(-5, 9), Rational(8, 9), Rational(8, 7)), @@ -187,7 +187,7 @@ class ListPolynomialTest { } @Test fun test_Int_Polynomial_times() { - IntModuloRing(35).listPolynomial { + IntModuloRing(35).listPolynomialSpace { assertEquals( ListPolynomial(34, 2, 1, 20, 2), 27 * ListPolynomial(22, 26, 13, 15, 26), @@ -202,7 +202,7 @@ class ListPolynomialTest { } @Test fun test_Polynomial_Constant_plus() { - RationalField.listPolynomial { + RationalField.listPolynomialSpace { assertEquals( ListPolynomial(Rational(-22, 9), Rational(-8, 9), Rational(-8, 7)), ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)) + Rational(-3), @@ -242,7 +242,7 @@ class ListPolynomialTest { } @Test fun test_Polynomial_Constant_minus() { - RationalField.listPolynomial { + RationalField.listPolynomialSpace { assertEquals( ListPolynomial(Rational(32, 9), Rational(-8, 9), Rational(-8, 7)), ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)) - Rational(-3), @@ -282,7 +282,7 @@ class ListPolynomialTest { } @Test fun test_Polynomial_Constant_times() { - IntModuloRing(35).listPolynomial { + IntModuloRing(35).listPolynomialSpace { assertEquals( ListPolynomial(34, 2, 1, 20, 2), ListPolynomial(22, 26, 13, 15, 26) * 27.asConstant(), @@ -297,7 +297,7 @@ class ListPolynomialTest { } @Test fun test_Constant_Polynomial_plus() { - RationalField.listPolynomial { + RationalField.listPolynomialSpace { assertEquals( ListPolynomial(Rational(-22, 9), Rational(-8, 9), Rational(-8, 7)), Rational(-3) + ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)), @@ -337,7 +337,7 @@ class ListPolynomialTest { } @Test fun test_Constant_Polynomial_minus() { - RationalField.listPolynomial { + RationalField.listPolynomialSpace { assertEquals( ListPolynomial(Rational(32, 9), Rational(-8, 9), Rational(-8, 7)), Rational(3) - ListPolynomial(Rational(-5, 9), Rational(8, 9), Rational(8, 7)), @@ -377,7 +377,7 @@ class ListPolynomialTest { } @Test fun test_Constant_Polynomial_times() { - IntModuloRing(35).listPolynomial { + IntModuloRing(35).listPolynomialSpace { assertEquals( ListPolynomial(34, 2, 1, 20, 2), 27 * ListPolynomial(22, 26, 13, 15, 26), @@ -392,7 +392,7 @@ class ListPolynomialTest { } @Test fun test_Polynomial_unaryMinus() { - RationalField.listPolynomial { + RationalField.listPolynomialSpace { assertEquals( ListPolynomial(Rational(-5, 9), Rational(8, 9), Rational(8, 7)), -ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)), @@ -407,7 +407,7 @@ class ListPolynomialTest { } @Test fun test_Polynomial_Polynomial_plus() { - RationalField.listPolynomial { + RationalField.listPolynomialSpace { // (5/9 - 8/9 x - 8/7 x^2) + (-5/7 + 5/1 x + 5/8 x^2) ?= -10/63 + 37/9 x - 29/56 x^2 assertEquals( ListPolynomial(Rational(-10, 63), Rational(37, 9), Rational(-29, 56)), @@ -440,7 +440,7 @@ class ListPolynomialTest { } @Test fun test_Polynomial_Polynomial_minus() { - RationalField.listPolynomial { + RationalField.listPolynomialSpace { // (5/9 - 8/9 x - 8/7 x^2) - (-5/7 + 5/1 x + 5/8 x^2) ?= 80/63 - 53/9 x - 99/56 x^2 assertEquals( ListPolynomial(Rational(80, 63), Rational(-53, 9), Rational(-99, 56)), @@ -473,7 +473,7 @@ class ListPolynomialTest { } @Test fun test_Polynomial_Polynomial_times() { - IntModuloRing(35).listPolynomial { + IntModuloRing(35).listPolynomialSpace { // (1 + x + x^2) * (1 - x + x^2) ?= 1 + x^2 + x^4 assertEquals( ListPolynomial(1, 0, 1, 0, 1), diff --git a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/test/misc/IntModulo.kt b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/test/misc/IntModulo.kt new file mode 100644 index 000000000..36a0d8be5 --- /dev/null +++ b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/test/misc/IntModulo.kt @@ -0,0 +1,140 @@ +/* + * 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 { + + 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.ListPolynomial(vararg coefs: Int): ListPolynomial = + ListPolynomial(coefs.map { IntModulo(it, ring.modulus) }) +fun IntModuloRing.ListPolynomial(vararg coefs: Int): ListPolynomial = + ListPolynomial(coefs.map { IntModulo(it, modulus) }) \ 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 index 873162a1b..ed41b9245 100644 --- 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 @@ -8,4 +8,22 @@ 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 +data class BezoutIdentityWithGCD(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 = + 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 = + 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) + } \ No newline at end of file