From fbe7363cde853b82d51448b7193a62409643b066 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 26 Apr 2020 21:47:34 +0300 Subject: [PATCH] BigInt refactoring --- build.gradle.kts | 2 +- .../operations/{KBigInteger.kt => BigInt.kt} | 275 +++++---- .../kmath/operations/BigIntAlgebraTest.kt | 50 ++ .../kmath/operations/BigIntConstructorTest.kt | 26 + .../kmath/operations/BigIntConversionsTest.kt | 43 ++ .../kmath/operations/BigIntOperationsTest.kt | 381 ++++++++++++ .../kmath/operations/KBigIntegerTest.kt | 543 ------------------ 7 files changed, 651 insertions(+), 669 deletions(-) rename kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/{KBigInteger.kt => BigInt.kt} (60%) create mode 100644 kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntAlgebraTest.kt create mode 100644 kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConstructorTest.kt create mode 100644 kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConversionsTest.kt create mode 100644 kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntOperationsTest.kt delete mode 100644 kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/KBigIntegerTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index c807636e6..6ab33d31c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("scientifik.publish") version "0.4.2" apply false } -val kmathVersion by extra("0.1.4-dev-3") +val kmathVersion by extra("0.1.4-dev-4") val bintrayRepo by extra("scientifik") val githubProject by extra("kmath") diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/KBigInteger.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt similarity index 60% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/KBigInteger.kt rename to kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt index b9b2bbb81..1661170d3 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/KBigInteger.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/BigInt.kt @@ -1,49 +1,45 @@ package scientifik.kmath.operations +import scientifik.kmath.operations.BigInt.Companion.BASE +import scientifik.kmath.operations.BigInt.Companion.BASE_SIZE import kotlin.math.log2 import kotlin.math.max import kotlin.math.min import kotlin.math.sign -/* - * Kotlin Multiplatform implementation of Big Integer numbers (KBigInteger). - * Initial version from https://github.com/robdrynkin/kotlin-big-integer - */ -@kotlin.ExperimentalUnsignedTypes typealias Magnitude = UIntArray - -@kotlin.ExperimentalUnsignedTypes typealias TBase = ULong -object KBigIntegerRing: Ring { - override val zero: KBigInteger = KBigInteger.ZERO - override val one: KBigInteger = KBigInteger.ONE +/** + * Kotlin Multiplatform implementation of Big Integer numbers (KBigInteger). + * + * @author Robert Drynkin (https://github.com/robdrynkin) and Peter Klimai (https://github.com/pklimai) + */ +object BigIntField : Field { + override val zero: BigInt = BigInt.ZERO + override val one: BigInt = BigInt.ONE - override fun add(a: KBigInteger, b: KBigInteger): KBigInteger = a.plus(b) + override fun add(a: BigInt, b: BigInt): BigInt = a.plus(b) - override fun multiply(a: KBigInteger, k: Number): KBigInteger = a.times(k.toLong()) + override fun multiply(a: BigInt, k: Number): BigInt = a.times(k.toLong()) - override fun multiply(a: KBigInteger, b: KBigInteger): KBigInteger = a.times(b) + override fun multiply(a: BigInt, b: BigInt): BigInt = a.times(b) - operator fun String.unaryPlus(): KBigInteger = this.toKBigInteger()!! + operator fun String.unaryPlus(): BigInt = this.parseBigInteger() ?: error("Can't parse $this as big integer") - operator fun String.unaryMinus(): KBigInteger = -this.toKBigInteger()!! + operator fun String.unaryMinus(): BigInt = + -(this.parseBigInteger() ?: error("Can't parse $this as big integer")) + + override fun divide(a: BigInt, b: BigInt): BigInt = a.div(b) } -@kotlin.ExperimentalUnsignedTypes -class KBigInteger internal constructor( - private val sign: Byte = 0, - private val magnitude: Magnitude = Magnitude(0) -): Comparable { +class BigInt internal constructor( + private val sign: Byte, + private val magnitude: Magnitude +) : Comparable { - constructor(x: Int) : this(x.sign.toByte(), uintArrayOf(kotlin.math.abs(x).toUInt())) - - constructor(x: Long) : this(x.sign.toByte(), stripLeadingZeros(uintArrayOf( - (kotlin.math.abs(x).toULong() and BASE).toUInt(), - ((kotlin.math.abs(x).toULong() shr BASE_SIZE) and BASE).toUInt()))) - - override fun compareTo(other: KBigInteger): Int { + override fun compareTo(other: BigInt): Int { return when { (this.sign == 0.toByte()) and (other.sign == 0.toByte()) -> 0 this.sign < other.sign -> -1 @@ -53,85 +49,87 @@ class KBigInteger internal constructor( } override fun equals(other: Any?): Boolean { - if (other is KBigInteger) { + if (other is BigInt) { return this.compareTo(other) == 0 - } - else error("Can't compare KBigInteger to a different type") + } else error("Can't compare KBigInteger to a different type") } override fun hashCode(): Int { return magnitude.hashCode() + this.sign } - fun abs(): KBigInteger = if (sign == 0.toByte()) this else KBigInteger(1, magnitude) + fun abs(): BigInt = if (sign == 0.toByte()) this else BigInt(1, magnitude) - operator fun unaryMinus(): KBigInteger { - return if (this.sign == 0.toByte()) this else KBigInteger((-this.sign).toByte(), this.magnitude) + operator fun unaryMinus(): BigInt { + return if (this.sign == 0.toByte()) this else BigInt((-this.sign).toByte(), this.magnitude) } - operator fun plus(b: KBigInteger): KBigInteger { + operator fun plus(b: BigInt): BigInt { return when { b.sign == 0.toByte() -> this this.sign == 0.toByte() -> b this == -b -> ZERO - this.sign == b.sign -> KBigInteger(this.sign, addMagnitudes(this.magnitude, b.magnitude)) + this.sign == b.sign -> BigInt(this.sign, addMagnitudes(this.magnitude, b.magnitude)) else -> { val comp: Int = compareMagnitudes(this.magnitude, b.magnitude) if (comp == 1) { - KBigInteger(this.sign, subtractMagnitudes(this.magnitude, b.magnitude)) + BigInt(this.sign, subtractMagnitudes(this.magnitude, b.magnitude)) } else { - KBigInteger((-this.sign).toByte(), subtractMagnitudes(b.magnitude, this.magnitude)) + BigInt((-this.sign).toByte(), subtractMagnitudes(b.magnitude, this.magnitude)) } } } } - operator fun minus(b: KBigInteger): KBigInteger { + operator fun minus(b: BigInt): BigInt { return this + (-b) } - operator fun times(b: KBigInteger): KBigInteger { + operator fun times(b: BigInt): BigInt { return when { this.sign == 0.toByte() -> ZERO b.sign == 0.toByte() -> ZERO // TODO: Karatsuba - else -> KBigInteger((this.sign * b.sign).toByte(), multiplyMagnitudes(this.magnitude, b.magnitude)) + else -> BigInt((this.sign * b.sign).toByte(), multiplyMagnitudes(this.magnitude, b.magnitude)) } } - operator fun times(other: UInt): KBigInteger { + operator fun times(other: UInt): BigInt { return when { this.sign == 0.toByte() -> ZERO other == 0U -> ZERO - else -> KBigInteger(this.sign, multiplyMagnitudeByUInt(this.magnitude, other)) + else -> BigInt(this.sign, multiplyMagnitudeByUInt(this.magnitude, other)) } } - operator fun times(other: Int): KBigInteger { + operator fun times(other: Int): BigInt { return if (other > 0) this * kotlin.math.abs(other).toUInt() else -this * kotlin.math.abs(other).toUInt() } - operator fun div(other: UInt): KBigInteger { - return KBigInteger(this.sign, divideMagnitudeByUInt(this.magnitude, other)) + operator fun div(other: UInt): BigInt { + return BigInt(this.sign, divideMagnitudeByUInt(this.magnitude, other)) } - operator fun div(other: Int): KBigInteger { - return KBigInteger((this.sign * other.sign).toByte(), - divideMagnitudeByUInt(this.magnitude, kotlin.math.abs(other).toUInt())) + operator fun div(other: Int): BigInt { + return BigInt( + (this.sign * other.sign).toByte(), + divideMagnitudeByUInt(this.magnitude, kotlin.math.abs(other).toUInt()) + ) } - private fun division(other: KBigInteger): Pair { + private fun division(other: BigInt): Pair { // Long division algorithm: // https://en.wikipedia.org/wiki/Division_algorithm#Integer_division_(unsigned)_with_remainder // TODO: Implement more effective algorithm - var q: KBigInteger = ZERO - var r: KBigInteger = ZERO + var q: BigInt = ZERO + var r: BigInt = ZERO - val bitSize = (BASE_SIZE * (this.magnitude.size - 1) + log2(this.magnitude.last().toFloat() + 1)).toInt() + val bitSize = + (BASE_SIZE * (this.magnitude.size - 1) + log2(this.magnitude.lastOrNull()?.toFloat() ?: 0f + 1)).toInt() for (i in bitSize downTo 0) { r = r shl 1 r = r or ((abs(this) shr i) and ONE) @@ -141,21 +139,21 @@ class KBigInteger internal constructor( } } - return Pair(KBigInteger((this.sign * other.sign).toByte(), q.magnitude), r) + return Pair(BigInt((this.sign * other.sign).toByte(), q.magnitude), r) } - operator fun div(other: KBigInteger): KBigInteger { + operator fun div(other: BigInt): BigInt { return this.division(other).first } - infix fun shl(i: Int): KBigInteger { + infix fun shl(i: Int): BigInt { if (this == ZERO) return ZERO if (i == 0) return this val fullShifts = i / BASE_SIZE + 1 val relShift = i % BASE_SIZE - val shiftLeft = {x: UInt -> if (relShift >= 32) 0U else x shl relShift} - val shiftRight = {x: UInt -> if (BASE_SIZE - relShift >= 32) 0U else x shr (BASE_SIZE - relShift)} + val shiftLeft = { x: UInt -> if (relShift >= 32) 0U else x shl relShift } + val shiftRight = { x: UInt -> if (BASE_SIZE - relShift >= 32) 0U else x shr (BASE_SIZE - relShift) } val newMagnitude: Magnitude = Magnitude(this.magnitude.size + fullShifts) @@ -168,17 +166,17 @@ class KBigInteger internal constructor( newMagnitude[this.magnitude.size + fullShifts - 1] = shiftRight(this.magnitude.last()) - return KBigInteger(this.sign, stripLeadingZeros(newMagnitude)) + return BigInt(this.sign, stripLeadingZeros(newMagnitude)) } - infix fun shr(i: Int): KBigInteger { + infix fun shr(i: Int): BigInt { if (this == ZERO) return ZERO if (i == 0) return this val fullShifts = i / BASE_SIZE val relShift = i % BASE_SIZE - val shiftRight = {x: UInt -> if (relShift >= 32) 0U else x shr relShift} - val shiftLeft = {x: UInt -> if (BASE_SIZE - relShift >= 32) 0U else x shl (BASE_SIZE - relShift)} + val shiftRight = { x: UInt -> if (relShift >= 32) 0U else x shr relShift } + val shiftLeft = { x: UInt -> if (BASE_SIZE - relShift >= 32) 0U else x shl (BASE_SIZE - relShift) } if (this.magnitude.size - fullShifts <= 0) { return ZERO } @@ -191,10 +189,10 @@ class KBigInteger internal constructor( } } - return KBigInteger(this.sign, stripLeadingZeros(newMagnitude)) + return BigInt(this.sign, stripLeadingZeros(newMagnitude)) } - infix fun or(other: KBigInteger): KBigInteger { + infix fun or(other: BigInt): BigInt { if (this == ZERO) return other; if (other == ZERO) return this; val resSize = max(this.magnitude.size, other.magnitude.size) @@ -207,17 +205,17 @@ class KBigInteger internal constructor( newMagnitude[i] = newMagnitude[i] or other.magnitude[i] } } - return KBigInteger(1, stripLeadingZeros(newMagnitude)) + return BigInt(1, stripLeadingZeros(newMagnitude)) } - infix fun and(other: KBigInteger): KBigInteger { + infix fun and(other: BigInt): BigInt { if ((this == ZERO) or (other == ZERO)) return ZERO; val resSize = min(this.magnitude.size, other.magnitude.size) val newMagnitude: Magnitude = Magnitude(resSize) for (i in 0 until resSize) { newMagnitude[i] = this.magnitude[i] and other.magnitude[i] } - return KBigInteger(1, stripLeadingZeros(newMagnitude)) + return BigInt(1, stripLeadingZeros(newMagnitude)) } operator fun rem(other: Int): Int { @@ -225,11 +223,11 @@ class KBigInteger internal constructor( return if (res == ZERO) 0 else res.sign * res.magnitude[0].toInt() } - operator fun rem(other: KBigInteger): KBigInteger { + operator fun rem(other: BigInt): BigInt { return this - (this / other) * other } - fun modPow(exponent: KBigInteger, m: KBigInteger): KBigInteger { + fun modPow(exponent: BigInt, m: BigInt): BigInt { return when { exponent == ZERO -> ONE exponent % 2 == 1 -> (this * modPow(exponent - ONE, m)) % m @@ -263,29 +261,15 @@ class KBigInteger internal constructor( companion object { const val BASE = 0xffffffffUL const val BASE_SIZE: Int = 32 - val ZERO: KBigInteger = KBigInteger() - val ONE: KBigInteger = KBigInteger(1) + val ZERO: BigInt = BigInt(0, uintArrayOf()) + val ONE: BigInt = BigInt(1, uintArrayOf(1u)) - private val hexMapping: HashMap = - hashMapOf( - 0U to "0", 1U to "1", 2U to "2", 3U to "3", - 4U to "4", 5U to "5", 6U to "6", 7U to "7", - 8U to "8", 9U to "9", 10U to "a", 11U to "b", - 12U to "c", 13U to "d", 14U to "e", 15U to "f" - ) - - internal fun stripLeadingZeros(mag: Magnitude): Magnitude { - if (mag.isEmpty() || mag.last() != 0U) { - return mag - } - var resSize: Int = mag.size - 1 - while (mag[resSize] == 0U) { - if (resSize == 0) - break - resSize -= 1 - } - return mag.sliceArray(IntRange(0, resSize)) - } + private val hexMapping: HashMap = hashMapOf( + 0U to "0", 1U to "1", 2U to "2", 3U to "3", + 4U to "4", 5U to "5", 6U to "6", 7U to "7", + 8U to "8", 9U to "9", 10U to "a", 11U to "b", + 12U to "c", 13U to "d", 14U to "e", 15U to "f" + ) private fun compareMagnitudes(mag1: Magnitude, mag2: Magnitude): Int { when { @@ -329,8 +313,8 @@ class KBigInteger internal constructor( for (i in 0 until resultLength) { var res: Long = - if (i < mag2.size) mag1[i].toLong() - mag2[i].toLong() - carry - else mag1[i].toLong() - carry + if (i < mag2.size) mag1[i].toLong() - mag2[i].toLong() - carry + else mag1[i].toLong() - carry carry = if (res < 0) 1 else 0 res += carry * (BASE + 1UL).toLong() @@ -390,33 +374,76 @@ class KBigInteger internal constructor( } -@kotlin.ExperimentalUnsignedTypes -fun abs(x: KBigInteger): KBigInteger = x.abs() -@kotlin.ExperimentalUnsignedTypes -// Can't put it as constructor in class due to platform declaration clash with KBigInteger(Int) -fun KBigInteger(x: UInt): KBigInteger - = KBigInteger(1, uintArrayOf(x)) +private fun stripLeadingZeros(mag: Magnitude): Magnitude { + if (mag.isEmpty() || mag.last() != 0U) { + return mag + } + var resSize: Int = mag.size - 1 + while (mag[resSize] == 0U) { + if (resSize == 0) + break + resSize -= 1 + } + return mag.sliceArray(IntRange(0, resSize)) +} -@kotlin.ExperimentalUnsignedTypes -// Can't put it as constructor in class due to platform declaration clash with KBigInteger(Long) -fun KBigInteger(x: ULong): KBigInteger - = KBigInteger(1, - KBigInteger.stripLeadingZeros(uintArrayOf( - (x and KBigInteger.BASE).toUInt(), - ((x shr KBigInteger.BASE_SIZE) and KBigInteger.BASE).toUInt()) - ) +fun abs(x: BigInt): BigInt = x.abs() + +/** + * Convert this [Int] to [BigInt] + */ +fun Int.toBigInt() = BigInt(sign.toByte(), uintArrayOf(kotlin.math.abs(this).toUInt())) + +/** + * Convert this [Long] to [BigInt] + */ +fun Long.toBigInt() = BigInt( + sign.toByte(), stripLeadingZeros( + uintArrayOf( + (kotlin.math.abs(this).toULong() and BASE).toUInt(), + ((kotlin.math.abs(this).toULong() shr BASE_SIZE) and BASE).toUInt() ) - -val hexChToInt = hashMapOf( - '0' to 0, '1' to 1, '2' to 2, '3' to 3, - '4' to 4, '5' to 5, '6' to 6, '7' to 7, - '8' to 8, '9' to 9, 'A' to 10, 'B' to 11, - 'C' to 12, 'D' to 13, 'E' to 14, 'F' to 15 + ) ) -// Returns None if a valid number can not be read from a string -fun String.toKBigInteger(): KBigInteger? { +/** + * Convert UInt to [BigInt] + */ +fun UInt.toBigInt() = BigInt(1, uintArrayOf(this)) + +/** + * Convert ULong to [BigInt] + */ +fun ULong.toBigInt() = BigInt( + 1, + stripLeadingZeros( + uintArrayOf( + (this and BigInt.BASE).toUInt(), + ((this shr BigInt.BASE_SIZE) and BigInt.BASE).toUInt() + ) + ) +) + +/** + * Create a [BigInt] with this array of magnitudes with protective copy + */ +fun UIntArray.toBigInt(sign: Byte): BigInt { + if (sign == 0.toByte() && isNotEmpty()) error("") + return BigInt(sign, this.copyOf()) +} + +val hexChToInt = hashMapOf( + '0' to 0, '1' to 1, '2' to 2, '3' to 3, + '4' to 4, '5' to 5, '6' to 6, '7' to 7, + '8' to 8, '9' to 9, 'A' to 10, 'B' to 11, + 'C' to 12, 'D' to 13, 'E' to 14, 'F' to 15 +) + +/** + * Returns null if a valid number can not be read from a string + */ +fun String.parseBigInteger(): BigInt? { val sign: Int val sPositive: String when { @@ -433,27 +460,25 @@ fun String.toKBigInteger(): KBigInteger? { sign = +1 } } - var res = KBigInteger.ZERO - var digitValue = KBigInteger.ONE + var res = BigInt.ZERO + var digitValue = BigInt.ONE val sPositiveUpper = sPositive.toUpperCase() if (sPositiveUpper.startsWith("0X")) { // hex representation val sHex = sPositiveUpper.substring(2) for (ch in sHex.reversed()) { if (ch == '_') continue - res += digitValue * (hexChToInt[ch] ?: return null) - digitValue *= KBigInteger(16) + res += digitValue * (hexChToInt[ch] ?: return null) + digitValue *= 16.toBigInt() } - } - else { // decimal representation - val sDecimal = sPositiveUpper - for (ch in sDecimal.reversed()) { + } else { // decimal representation + for (ch in sPositiveUpper.reversed()) { if (ch == '_') continue if (ch !in '0'..'9') { return null } - res += digitValue * (ch.toInt() - '0'.toInt()) - digitValue *= KBigInteger(10) + res += digitValue * (ch.toInt() - '0'.toInt()) + digitValue *= 10.toBigInt() } } - return res * sign + return res * sign } diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntAlgebraTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntAlgebraTest.kt new file mode 100644 index 000000000..5ae977196 --- /dev/null +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntAlgebraTest.kt @@ -0,0 +1,50 @@ +package scientifik.kmath.operations + +import kotlin.test.Test +import kotlin.test.assertEquals + +class BigIntAlgebraTest { + @Test + fun testKBigIntegerRingSum() { + val res = BigIntField { + 1_000L.toBigInt() * 1_000L.toBigInt() + } + assertEquals(res, 1_000_000.toBigInt()) + } + + @Test + fun testKBigIntegerRingSum_100_000_000__100_000_000() { + BigIntField { + val sum = +"100_000_000" + +"100_000_000" + assertEquals(sum, "200_000_000".parseBigInteger()) + } + } + + @Test + fun test_mul_3__4() { + BigIntField { + val prod = +"0x3000_0000_0000" * +"0x4000_0000_0000_0000_0000" + assertEquals(prod, "0xc00_0000_0000_0000_0000_0000_0000_0000".parseBigInteger()) + } + } + + @Test + fun test_div_big_1() { + BigIntField { + val res = +"1_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000" / + +"555_000_444_000_333_000_222_000_111_000_999_001" + assertEquals(res, +"1801800360360432432518919022699") + } + } + + @Test + fun test_rem_big_1() { + BigIntField { + val res = +"1_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000" % + +"555_000_444_000_333_000_222_000_111_000_999_001" + assertEquals(res, +"324121220440768000291647788404676301") + } + } + +} + diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConstructorTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConstructorTest.kt new file mode 100644 index 000000000..2af1b7e50 --- /dev/null +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConstructorTest.kt @@ -0,0 +1,26 @@ +package scientifik.kmath.operations + +import kotlin.test.Test +import kotlin.test.assertEquals + +class BigIntConstructorTest { + @Test + fun testConstructorZero() { + assertEquals(0.toBigInt(), uintArrayOf().toBigInt(0)) + } + + @Test + fun testConstructor8() { + assertEquals( + 8.toBigInt(), + uintArrayOf(8U).toBigInt(1) + ) + } + + @Test + fun testConstructor_0xffffffffaL() { + val x = -0xffffffffaL.toBigInt() + val y = uintArrayOf(0xfffffffaU, 0xfU).toBigInt(-1) + assertEquals(x, y) + } +} \ No newline at end of file diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConversionsTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConversionsTest.kt new file mode 100644 index 000000000..51b9509e0 --- /dev/null +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntConversionsTest.kt @@ -0,0 +1,43 @@ +package scientifik.kmath.operations + +import kotlin.test.Test +import kotlin.test.assertEquals + +@kotlin.ExperimentalUnsignedTypes +class BigIntConversionsTest { + @Test + fun testToString0x10() { + val x = 0x10.toBigInt() + assertEquals("0x10", x.toString()) + } + + @Test + fun testToString0x17ffffffd() { + val x = 0x17ffffffdL.toBigInt() + assertEquals("0x17ffffffd", x.toString()) + } + + @Test + fun testToString_0x17ead2ffffd() { + val x = -0x17ead2ffffdL.toBigInt() + assertEquals("-0x17ead2ffffd", x.toString()) + } + + @Test + fun testToString_0x17ead2ffffd11223344() { + val x = uintArrayOf(0x11223344U, 0xad2ffffdU, 0x17eU).toBigInt(-1) + assertEquals("-0x17ead2ffffd11223344", x.toString()) + } + + @Test + fun testFromString_0x17ead2ffffd11223344() { + val x = "0x17ead2ffffd11223344".parseBigInteger() + assertEquals("0x17ead2ffffd11223344", x.toString()) + } + + @Test + fun testFromString_7059135710711894913860() { + val x = "-7059135710711894913860".parseBigInteger() + assertEquals("-0x17ead2ffffd11223344", x.toString()) + } +} \ No newline at end of file diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntOperationsTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntOperationsTest.kt new file mode 100644 index 000000000..72ac9f229 --- /dev/null +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/BigIntOperationsTest.kt @@ -0,0 +1,381 @@ +package scientifik.kmath.operations + +import kotlin.test.Test +import kotlin.test.assertEquals + +@kotlin.ExperimentalUnsignedTypes +class BigIntOperationsTest { + @Test + fun testPlus_1_1() { + val x = 1.toBigInt() + val y = 1.toBigInt() + + val res = x + y + val sum = 2.toBigInt() + + assertEquals(sum, res) + } + + @Test + fun testPlusBigNumbers() { + val x = 0x7fffffff.toBigInt() + val y = 0x7fffffff.toBigInt() + val z = 0x7fffffff.toBigInt() + + val res = x + y + z + val sum = uintArrayOf(0x7ffffffdU, 0x1U).toBigInt(1) + + assertEquals(sum, res) + } + + @Test + fun testUnaryMinus() { + val x = 1234.toBigInt() + val y = -1234.toBigInt() + assertEquals(-x, y) + } + + @Test + fun testMinus_2_1() { + val x = 2.toBigInt() + val y = 1.toBigInt() + + val res = x - y + val sum = 1.toBigInt() + + assertEquals(sum, res) + } + + @Test + fun testMinus__2_1() { + val x = -2.toBigInt() + val y = 1.toBigInt() + + val res = x - y + val sum = -3.toBigInt() + + assertEquals(sum, res) + } + + @Test + fun testMinus___2_1() { + val x = -2.toBigInt() + val y = 1.toBigInt() + + val res = -x - y + val sum = 1.toBigInt() + + assertEquals(sum, res) + } + + @Test + fun testMinusBigNumbers() { + val x = 12345.toBigInt() + val y = 0xffffffffaL.toBigInt() + + val res = x - y + val sum = -0xfffffcfc1L.toBigInt() + + assertEquals(sum, res) + } + + @Test + fun testMultiply_2_3() { + val x = 2.toBigInt() + val y = 3.toBigInt() + + val res = x * y + val prod = 6.toBigInt() + + assertEquals(prod, res) + } + + @Test + fun testMultiply__2_3() { + val x = -2.toBigInt() + val y = 3.toBigInt() + + val res = x * y + val prod = -6.toBigInt() + + assertEquals(prod, res) + } + + @Test + fun testMultiply_0xfff123_0xfff456() { + val x = 0xfff123.toBigInt() + val y = 0xfff456.toBigInt() + + val res = x * y + val prod = 0xffe579ad5dc2L.toBigInt() + + assertEquals(prod, res) + } + + @Test + fun testMultiplyUInt_0xfff123_0xfff456() { + val x = 0xfff123.toBigInt() + val y = 0xfff456U + + val res = x * y + val prod = 0xffe579ad5dc2L.toBigInt() + + assertEquals(prod, res) + } + + @Test + fun testMultiplyInt_0xfff123__0xfff456() { + val x = 0xfff123.toBigInt() + val y = -0xfff456 + + val res = x * y + val prod = -0xffe579ad5dc2L.toBigInt() + + assertEquals(prod, res) + } + + @Test + fun testMultiply_0xffffffff_0xffffffff() { + val x = 0xffffffffL.toBigInt() + val y = 0xffffffffL.toBigInt() + + val res = x * y + val prod = 0xfffffffe00000001UL.toBigInt() + + assertEquals(prod, res) + } + + @Test + fun test_shr_20() { + val x = 20.toBigInt() + assertEquals(10.toBigInt(), x shr 1) + } + + @Test + fun test_shl_20() { + val x = 20.toBigInt() + assertEquals(40.toBigInt(), x shl 1) + } + + @Test + fun test_shl_1_0() { + assertEquals( + BigInt.ONE, + BigInt.ONE shl 0 + ) + } + + @Test + fun test_shl_1_32() { + assertEquals( + 0x100000000UL.toBigInt(), + BigInt.ONE shl 32 + ) + } + + @Test + fun test_shl_1_33() { + assertEquals( + 0x200000000UL.toBigInt(), + BigInt.ONE shl 33 + ) + } + + @Test + fun test_shr_1_33_33() { + assertEquals( + BigInt.ONE, + (BigInt.ONE shl 33) shr 33 + ) + } + + @Test + fun test_shr_1_32() { + assertEquals( + BigInt.ZERO, + BigInt.ONE shr 32 + ) + } + + @Test + fun test_and_123_456() { + val x = 123.toBigInt() + val y = 456.toBigInt() + assertEquals(72.toBigInt(), x and y) + } + + @Test + fun test_or_123_456() { + val x = 123.toBigInt() + val y = 456.toBigInt() + assertEquals(507.toBigInt(), x or y) + } + + @Test + fun test_asd() { + assertEquals( + BigInt.ONE, + BigInt.ZERO or ((20.toBigInt() shr 4) and BigInt.ONE) + ) + } + + @Test + fun test_square_0x11223344U_0xad2ffffdU_0x17eU() { + val num = + uintArrayOf(0x11223344U, 0xad2ffffdU, 0x17eU).toBigInt(-1) + println(num) + val res = num * num + assertEquals( + res, + uintArrayOf(0xb0542a10U, 0xbbd85bc8U, 0x2a1fa515U, 0x5069e03bU, 0x23c09U).toBigInt(1) + ) + } + + @Test + fun testDivision_6_3() { + val x = 6.toBigInt() + val y = 3U + + val res = x / y + val div = 2.toBigInt() + + assertEquals(div, res) + } + + @Test + fun testBigDivision_6_3() { + val x = 6.toBigInt() + val y = 3.toBigInt() + + val res = x / y + val div = 2.toBigInt() + + assertEquals(div, res) + } + + @Test + fun testDivision_20__3() { + val x = 20.toBigInt() + val y = -3 + + val res = x / y + val div = -6.toBigInt() + + assertEquals(div, res) + } + + @Test + fun testBigDivision_20__3() { + val x = 20.toBigInt() + val y = -3.toBigInt() + + val res = x / y + val div = -6.toBigInt() + + assertEquals(div, res) + } + + @Test + fun testDivision_0xfffffffe00000001_0xffffffff() { + val x = 0xfffffffe00000001UL.toBigInt() + val y = 0xffffffffU + + val res = x / y + val div = 0xffffffffL.toBigInt() + + assertEquals(div, res) + } + + @Test + fun testBigDivision_0xfffffffeabcdef01UL_0xfffffffeabc() { + val res = 0xfffffffeabcdef01UL.toBigInt() / 0xfffffffeabc.toBigInt() + assertEquals(res, 0x100000.toBigInt()) + } + + @Test + fun testBigDivision_0xfffffffe00000001_0xffffffff() { + val x = 0xfffffffe00000001UL.toBigInt() + val y = 0xffffffffU.toBigInt() + + val res = x / y + val div = 0xffffffffL.toBigInt() + + assertEquals(div, res) + } + + @Test + fun testMod_20_3() { + val x = 20.toBigInt() + val y = 3 + + val res = x % y + val mod = 2 + + assertEquals(mod, res) + } + + @Test + fun testBigMod_20_3() { + val x = 20.toBigInt() + val y = 3.toBigInt() + + val res = x % y + val mod = 2.toBigInt() + + assertEquals(mod, res) + } + + @Test + fun testMod_0xfffffffe00000001_12345() { + val x = 0xfffffffe00000001UL.toBigInt() + val y = 12345 + + val res = x % y + val mod = 1980 + + assertEquals(mod, res) + } + + @Test + fun testBigMod_0xfffffffe00000001_12345() { + val x = 0xfffffffe00000001UL.toBigInt() + val y = 12345.toBigInt() + + val res = x % y + val mod = 1980.toBigInt() + + assertEquals(mod, res) + } + + @Test + fun testModPow_3_10_17() { + val x = 3.toBigInt() + val exp = 10.toBigInt() + val mod = 17.toBigInt() + + val res = 8.toBigInt() + + return assertEquals(res, x.modPow(exp, mod)) + } + + @Test + fun testModPowBigNumbers() { + val x = 0xfffffffeabcdef01UL.toBigInt() + val exp = 2.toBigInt() + val mod = 0xfffffffeabcUL.toBigInt() + + val res = 0xc2253cde01.toBigInt() + + return assertEquals(res, x.modPow(exp, mod)) + } + + @Test + fun testModBigNumbers() { + val x = 0xfffffffeabcdef01UL.toBigInt() + val mod = 0xfffffffeabcUL.toBigInt() + + val res = 0xdef01.toBigInt() + + return assertEquals(res, x % mod) + } +} \ No newline at end of file diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/KBigIntegerTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/KBigIntegerTest.kt deleted file mode 100644 index 96e2dee9a..000000000 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/KBigIntegerTest.kt +++ /dev/null @@ -1,543 +0,0 @@ -package scientifik.kmath.operations - -import kotlin.test.Test -import kotlin.test.assertTrue -import kotlin.test.assertEquals - -@kotlin.ExperimentalUnsignedTypes -class KBigIntegerConstructorTest { - @Test - fun testConstructorZero() { - assertEquals(KBigInteger(0), KBigInteger(0, uintArrayOf())) - } - - @Test - fun testConstructor8() { - assertEquals(KBigInteger(8), KBigInteger(1, uintArrayOf(8U))) - } - - @Test - fun testConstructor_0xffffffffaL() { - val x = KBigInteger(-0xffffffffaL) - val y = KBigInteger(-1, uintArrayOf(0xfffffffaU, 0xfU)) - assertEquals(x, y) - } -} - -@kotlin.ExperimentalUnsignedTypes -class KBigIntegerCompareTest { - @Test - fun testCompare1_2() { - val x = KBigInteger(1) - val y = KBigInteger(2) - assertTrue { x < y } - } - - @Test - fun testCompare0_0() { - val x = KBigInteger(0) - val y = KBigInteger(0) - assertEquals(x, y) - } - - @Test - fun testCompare1__2() { - val x = KBigInteger(1) - val y = KBigInteger(-2) - assertTrue { x > y } - } - - @Test - fun testCompare_1__2() { - val x = KBigInteger(-1) - val y = KBigInteger(-2) - assertTrue { x > y } - } - - @Test - fun testCompare_2__1() { - val x = KBigInteger(-2) - val y = KBigInteger(-1) - assertTrue { x < y } - } - - @Test - fun testCompare12345_12345() { - val x = KBigInteger(12345) - val y = KBigInteger(12345) - assertEquals(x, y) - } - - @Test - fun testEqualsWithLong() { - val x = KBigInteger(12345) - assertTrue { x == KBigInteger(12345L) } - } - - @Test - fun testEqualsWithULong() { - val x = KBigInteger(12345) - assertTrue { x == KBigInteger(12345UL) } - } - - @Test - fun testCompareBigNumbersGreater() { - val x = KBigInteger(0xfffffffffL) - val y = KBigInteger(0xffffffffaL) - assertTrue { x > y } - } - - @Test - fun testCompareBigNumbersEqual() { - val x = KBigInteger(0xffffffffaL) - val y = KBigInteger(0xffffffffaL) - assertEquals(x, y) - } - - @Test - fun testCompareBigNumbersLess() { - val x = KBigInteger(-0xffffffffaL) - val y = KBigInteger(0xffffffffaL) - assertTrue { x < y } - } -} - -@kotlin.ExperimentalUnsignedTypes -class KBigIntegerOperationsTest { - @Test - fun testPlus_1_1() { - val x = KBigInteger(1) - val y = KBigInteger(1) - - val res = x + y - val sum = KBigInteger(2) - - assertEquals(sum, res) - } - - @Test - fun testPlusBigNumbers() { - val x = KBigInteger(0x7fffffff) - val y = KBigInteger(0x7fffffff) - val z = KBigInteger(0x7fffffff) - - val res = x + y + z - val sum = KBigInteger(1, uintArrayOf(0x7ffffffdU, 0x1U)) - - assertEquals(sum, res) - } - - @Test - fun testUnaryMinus() { - val x = KBigInteger(1234) - val y = KBigInteger(-1234) - assertEquals(-x, y) - } - - @Test - fun testMinus_2_1() { - val x = KBigInteger(2) - val y = KBigInteger(1) - - val res = x - y - val sum = KBigInteger(1) - - assertEquals(sum, res) - } - - @Test - fun testMinus__2_1() { - val x = KBigInteger(-2) - val y = KBigInteger(1) - - val res = x - y - val sum = KBigInteger(-3) - - assertEquals(sum, res) - } - - @Test - fun testMinus___2_1() { - val x = KBigInteger(-2) - val y = KBigInteger(1) - - val res = -x - y - val sum = KBigInteger(1) - - assertEquals(sum, res) - } - - @Test - fun testMinusBigNumbers() { - val x = KBigInteger(12345) - val y = KBigInteger(0xffffffffaL) - - val res = x - y - val sum = KBigInteger(-0xfffffcfc1L) - - assertEquals(sum, res) - } - - @Test - fun testMultiply_2_3() { - val x = KBigInteger(2) - val y = KBigInteger(3) - - val res = x * y - val prod = KBigInteger(6) - - assertEquals(prod, res) - } - - @Test - fun testMultiply__2_3() { - val x = KBigInteger(-2) - val y = KBigInteger(3) - - val res = x * y - val prod = KBigInteger(-6) - - assertEquals(prod, res) - } - - @Test - fun testMultiply_0xfff123_0xfff456() { - val x = KBigInteger(0xfff123) - val y = KBigInteger(0xfff456) - - val res = x * y - val prod = KBigInteger(0xffe579ad5dc2L) - - assertEquals(prod, res) - } - - @Test - fun testMultiplyUInt_0xfff123_0xfff456() { - val x = KBigInteger(0xfff123) - val y = 0xfff456U - - val res = x * y - val prod = KBigInteger(0xffe579ad5dc2L) - - assertEquals(prod, res) - } - - @Test - fun testMultiplyInt_0xfff123__0xfff456() { - val x = KBigInteger(0xfff123) - val y = -0xfff456 - - val res = x * y - val prod = KBigInteger(-0xffe579ad5dc2L) - - assertEquals(prod, res) - } - - @Test - fun testMultiply_0xffffffff_0xffffffff() { - val x = KBigInteger(0xffffffffL) - val y = KBigInteger(0xffffffffL) - - val res = x * y - val prod = KBigInteger(0xfffffffe00000001UL) - - assertEquals(prod, res) - } - - @Test - fun test_shr_20() { - val x = KBigInteger(20) - assertEquals(KBigInteger(10), x shr 1) - } - - @Test - fun test_shl_20() { - val x = KBigInteger(20) - assertEquals(KBigInteger(40), x shl 1) - } - - @Test - fun test_shl_1_0() { - assertEquals(KBigInteger.ONE, KBigInteger.ONE shl 0) - } - - @Test - fun test_shl_1_32() { - assertEquals(KBigInteger(0x100000000UL), KBigInteger.ONE shl 32) - } - - @Test - fun test_shl_1_33() { - assertEquals(KBigInteger(0x200000000UL), KBigInteger.ONE shl 33) - } - - @Test - fun test_shr_1_33_33() { - assertEquals(KBigInteger.ONE, (KBigInteger.ONE shl 33) shr 33) - } - - @Test - fun test_shr_1_32() { - assertEquals(KBigInteger.ZERO, KBigInteger.ONE shr 32) - } - - @Test - fun test_and_123_456() { - val x = KBigInteger(123) - val y = KBigInteger(456) - assertEquals(KBigInteger(72), x and y) - } - - @Test - fun test_or_123_456() { - val x = KBigInteger(123) - val y = KBigInteger(456) - assertEquals(KBigInteger(507), x or y) - } - - @Test - fun test_asd() { - assertEquals(KBigInteger.ONE, KBigInteger.ZERO or ((KBigInteger(20) shr 4) and KBigInteger.ONE)) - } - - @Test - fun test_square_0x11223344U_0xad2ffffdU_0x17eU() { - val num = KBigInteger(-1, uintArrayOf(0x11223344U, 0xad2ffffdU, 0x17eU )) - println(num) - val res = num * num - assertEquals(res, KBigInteger(1, uintArrayOf(0xb0542a10U, 0xbbd85bc8U, 0x2a1fa515U, 0x5069e03bU, 0x23c09U))) - } - - @Test - fun testDivision_6_3() { - val x = KBigInteger(6) - val y = 3U - - val res = x / y - val div = KBigInteger(2) - - assertEquals(div, res) - } - - @Test - fun testBigDivision_6_3() { - val x = KBigInteger(6) - val y = KBigInteger(3) - - val res = x / y - val div = KBigInteger(2) - - assertEquals(div, res) - } - - @Test - fun testDivision_20__3() { - val x = KBigInteger(20) - val y = -3 - - val res = x / y - val div = KBigInteger(-6) - - assertEquals(div, res) - } - - @Test - fun testBigDivision_20__3() { - val x = KBigInteger(20) - val y = KBigInteger(-3) - - val res = x / y - val div = KBigInteger(-6) - - assertEquals(div, res) - } - - @Test - fun testDivision_0xfffffffe00000001_0xffffffff() { - val x = KBigInteger(0xfffffffe00000001UL) - val y = 0xffffffffU - - val res = x / y - val div = KBigInteger(0xffffffffL) - - assertEquals(div, res) - } - - @Test - fun testBigDivision_0xfffffffeabcdef01UL_0xfffffffeabc() { - val res = KBigInteger(0xfffffffeabcdef01UL) / KBigInteger(0xfffffffeabc) - assertEquals(res, KBigInteger(0x100000)) - } - - @Test - fun testBigDivision_0xfffffffe00000001_0xffffffff() { - val x = KBigInteger(0xfffffffe00000001UL) - val y = KBigInteger(0xffffffffU) - - val res = x / y - val div = KBigInteger(0xffffffffL) - - assertEquals(div, res) - } - - @Test - fun testMod_20_3() { - val x = KBigInteger(20) - val y = 3 - - val res = x % y - val mod = 2 - - assertEquals(mod, res) - } - - @Test - fun testBigMod_20_3() { - val x = KBigInteger(20) - val y = KBigInteger(3) - - val res = x % y - val mod = KBigInteger(2) - - assertEquals(mod, res) - } - - @Test - fun testMod_0xfffffffe00000001_12345() { - val x = KBigInteger(0xfffffffe00000001UL) - val y = 12345 - - val res = x % y - val mod = 1980 - - assertEquals(mod, res) - } - - @Test - fun testBigMod_0xfffffffe00000001_12345() { - val x = KBigInteger(0xfffffffe00000001UL) - val y = KBigInteger(12345) - - val res = x % y - val mod = KBigInteger(1980) - - assertEquals(mod, res) - } - - @Test - fun testModPow_3_10_17() { - val x = KBigInteger(3) - val exp = KBigInteger(10) - val mod = KBigInteger(17) - - val res = KBigInteger(8) - - return assertEquals(res, x.modPow(exp, mod)) - } - - @Test - fun testModPowBigNumbers() { - val x = KBigInteger(0xfffffffeabcdef01UL) - val exp = KBigInteger(2) - val mod = KBigInteger(0xfffffffeabcUL) - - val res = KBigInteger(0xc2253cde01) - - return assertEquals(res, x.modPow(exp, mod)) - } - - @Test - fun testModBigNumbers() { - val x = KBigInteger(0xfffffffeabcdef01UL) - val mod = KBigInteger(0xfffffffeabcUL) - - val res = KBigInteger(0xdef01) - - return assertEquals(res, x % mod) - } -} - -@kotlin.ExperimentalUnsignedTypes -class KBigIntegerConversionsTest { - @Test - fun testToString0x10() { - val x = KBigInteger(0x10) - assertEquals("0x10", x.toString()) - } - - @Test - fun testToString0x17ffffffd() { - val x = KBigInteger(0x17ffffffdL) - assertEquals("0x17ffffffd", x.toString()) - } - - @Test - fun testToString_0x17ead2ffffd() { - val x = KBigInteger(-0x17ead2ffffdL) - assertEquals("-0x17ead2ffffd", x.toString()) - } - - @Test - fun testToString_0x17ead2ffffd11223344() { - val x = KBigInteger(-1, uintArrayOf(0x11223344U, 0xad2ffffdU, 0x17eU )) - assertEquals("-0x17ead2ffffd11223344", x.toString()) - } - - @Test - fun testFromString_0x17ead2ffffd11223344() { - val x = "0x17ead2ffffd11223344".toKBigInteger() - assertEquals( "0x17ead2ffffd11223344", x.toString()) - } - - @Test - fun testFromString_7059135710711894913860() { - val x = "-7059135710711894913860".toKBigInteger() - assertEquals("-0x17ead2ffffd11223344", x.toString()) - } -} - -class KBigIntegerRingTest { - @Test - fun testKBigIntegerRingSum() { - val res = KBigIntegerRing { - KBigInteger(1_000L) * KBigInteger(1_000L) - } - assertEquals(res, KBigInteger(1_000_000) ) - } - - @Test - fun testKBigIntegerRingSum_100_000_000__100_000_000() { - KBigIntegerRing { - val sum = +"100_000_000" + +"100_000_000" - assertEquals(sum, "200_000_000".toKBigInteger()) - } - } - - @Test - fun test_mul_3__4() { - KBigIntegerRing { - val prod = +"0x3000_0000_0000" * +"0x4000_0000_0000_0000_0000" - assertEquals(prod, "0xc00_0000_0000_0000_0000_0000_0000_0000".toKBigInteger()) - } - } - - @Test - fun test_div_big_1() { - KBigIntegerRing { - val res = +"1_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000" / - +"555_000_444_000_333_000_222_000_111_000_999_001" - assertEquals(res, +"1801800360360432432518919022699") - } - } - - @Test - fun test_rem_big_1() { - KBigIntegerRing { - val res = +"1_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000" % - +"555_000_444_000_333_000_222_000_111_000_999_001" - assertEquals(res, +"324121220440768000291647788404676301") - } - } - -} -