BigInt refactoring

This commit is contained in:
Alexander Nozik 2020-04-26 21:47:34 +03:00
parent 85ad44ac9b
commit fbe7363cde
7 changed files with 651 additions and 669 deletions

View File

@ -2,7 +2,7 @@ plugins {
id("scientifik.publish") version "0.4.2" apply false 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 bintrayRepo by extra("scientifik")
val githubProject by extra("kmath") val githubProject by extra("kmath")

View File

@ -1,49 +1,45 @@
package scientifik.kmath.operations 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.log2
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
import kotlin.math.sign 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 typealias Magnitude = UIntArray
@kotlin.ExperimentalUnsignedTypes
typealias TBase = ULong typealias TBase = ULong
object KBigIntegerRing: Ring<KBigInteger> { /**
override val zero: KBigInteger = KBigInteger.ZERO * Kotlin Multiplatform implementation of Big Integer numbers (KBigInteger).
override val one: KBigInteger = KBigInteger.ONE *
* @author Robert Drynkin (https://github.com/robdrynkin) and Peter Klimai (https://github.com/pklimai)
*/
object BigIntField : Field<BigInt> {
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 BigInt internal constructor(
class KBigInteger internal constructor( private val sign: Byte,
private val sign: Byte = 0, private val magnitude: Magnitude
private val magnitude: Magnitude = Magnitude(0) ) : Comparable<BigInt> {
): Comparable<KBigInteger> {
constructor(x: Int) : this(x.sign.toByte(), uintArrayOf(kotlin.math.abs(x).toUInt())) override fun compareTo(other: BigInt): Int {
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 {
return when { return when {
(this.sign == 0.toByte()) and (other.sign == 0.toByte()) -> 0 (this.sign == 0.toByte()) and (other.sign == 0.toByte()) -> 0
this.sign < other.sign -> -1 this.sign < other.sign -> -1
@ -53,85 +49,87 @@ class KBigInteger internal constructor(
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (other is KBigInteger) { if (other is BigInt) {
return this.compareTo(other) == 0 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 { override fun hashCode(): Int {
return magnitude.hashCode() + this.sign 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 { operator fun unaryMinus(): BigInt {
return if (this.sign == 0.toByte()) this else KBigInteger((-this.sign).toByte(), this.magnitude) 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 { return when {
b.sign == 0.toByte() -> this b.sign == 0.toByte() -> this
this.sign == 0.toByte() -> b this.sign == 0.toByte() -> b
this == -b -> ZERO 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 -> { else -> {
val comp: Int = compareMagnitudes(this.magnitude, b.magnitude) val comp: Int = compareMagnitudes(this.magnitude, b.magnitude)
if (comp == 1) { if (comp == 1) {
KBigInteger(this.sign, subtractMagnitudes(this.magnitude, b.magnitude)) BigInt(this.sign, subtractMagnitudes(this.magnitude, b.magnitude))
} else { } 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) return this + (-b)
} }
operator fun times(b: KBigInteger): KBigInteger { operator fun times(b: BigInt): BigInt {
return when { return when {
this.sign == 0.toByte() -> ZERO this.sign == 0.toByte() -> ZERO
b.sign == 0.toByte() -> ZERO b.sign == 0.toByte() -> ZERO
// TODO: Karatsuba // 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 { return when {
this.sign == 0.toByte() -> ZERO this.sign == 0.toByte() -> ZERO
other == 0U -> 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) return if (other > 0)
this * kotlin.math.abs(other).toUInt() this * kotlin.math.abs(other).toUInt()
else else
-this * kotlin.math.abs(other).toUInt() -this * kotlin.math.abs(other).toUInt()
} }
operator fun div(other: UInt): KBigInteger { operator fun div(other: UInt): BigInt {
return KBigInteger(this.sign, divideMagnitudeByUInt(this.magnitude, other)) return BigInt(this.sign, divideMagnitudeByUInt(this.magnitude, other))
} }
operator fun div(other: Int): KBigInteger { operator fun div(other: Int): BigInt {
return KBigInteger((this.sign * other.sign).toByte(), return BigInt(
divideMagnitudeByUInt(this.magnitude, kotlin.math.abs(other).toUInt())) (this.sign * other.sign).toByte(),
divideMagnitudeByUInt(this.magnitude, kotlin.math.abs(other).toUInt())
)
} }
private fun division(other: KBigInteger): Pair<KBigInteger, KBigInteger> { private fun division(other: BigInt): Pair<BigInt, BigInt> {
// Long division algorithm: // Long division algorithm:
// https://en.wikipedia.org/wiki/Division_algorithm#Integer_division_(unsigned)_with_remainder // https://en.wikipedia.org/wiki/Division_algorithm#Integer_division_(unsigned)_with_remainder
// TODO: Implement more effective algorithm // TODO: Implement more effective algorithm
var q: KBigInteger = ZERO var q: BigInt = ZERO
var r: KBigInteger = 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) { for (i in bitSize downTo 0) {
r = r shl 1 r = r shl 1
r = r or ((abs(this) shr i) and ONE) 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 return this.division(other).first
} }
infix fun shl(i: Int): KBigInteger { infix fun shl(i: Int): BigInt {
if (this == ZERO) return ZERO if (this == ZERO) return ZERO
if (i == 0) return this if (i == 0) return this
val fullShifts = i / BASE_SIZE + 1 val fullShifts = i / BASE_SIZE + 1
val relShift = i % BASE_SIZE val relShift = i % BASE_SIZE
val shiftLeft = {x: UInt -> if (relShift >= 32) 0U else x shl 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 shiftRight = { x: UInt -> if (BASE_SIZE - relShift >= 32) 0U else x shr (BASE_SIZE - relShift) }
val newMagnitude: Magnitude = Magnitude(this.magnitude.size + fullShifts) 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()) 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 (this == ZERO) return ZERO
if (i == 0) return this if (i == 0) return this
val fullShifts = i / BASE_SIZE val fullShifts = i / BASE_SIZE
val relShift = i % BASE_SIZE val relShift = i % BASE_SIZE
val shiftRight = {x: UInt -> if (relShift >= 32) 0U else x shr 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)} val shiftLeft = { x: UInt -> if (BASE_SIZE - relShift >= 32) 0U else x shl (BASE_SIZE - relShift) }
if (this.magnitude.size - fullShifts <= 0) { if (this.magnitude.size - fullShifts <= 0) {
return ZERO 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 (this == ZERO) return other;
if (other == ZERO) return this; if (other == ZERO) return this;
val resSize = max(this.magnitude.size, other.magnitude.size) 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] 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; if ((this == ZERO) or (other == ZERO)) return ZERO;
val resSize = min(this.magnitude.size, other.magnitude.size) val resSize = min(this.magnitude.size, other.magnitude.size)
val newMagnitude: Magnitude = Magnitude(resSize) val newMagnitude: Magnitude = Magnitude(resSize)
for (i in 0 until resSize) { for (i in 0 until resSize) {
newMagnitude[i] = this.magnitude[i] and other.magnitude[i] 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 { 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() 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 return this - (this / other) * other
} }
fun modPow(exponent: KBigInteger, m: KBigInteger): KBigInteger { fun modPow(exponent: BigInt, m: BigInt): BigInt {
return when { return when {
exponent == ZERO -> ONE exponent == ZERO -> ONE
exponent % 2 == 1 -> (this * modPow(exponent - ONE, m)) % m exponent % 2 == 1 -> (this * modPow(exponent - ONE, m)) % m
@ -263,29 +261,15 @@ class KBigInteger internal constructor(
companion object { companion object {
const val BASE = 0xffffffffUL const val BASE = 0xffffffffUL
const val BASE_SIZE: Int = 32 const val BASE_SIZE: Int = 32
val ZERO: KBigInteger = KBigInteger() val ZERO: BigInt = BigInt(0, uintArrayOf())
val ONE: KBigInteger = KBigInteger(1) val ONE: BigInt = BigInt(1, uintArrayOf(1u))
private val hexMapping: HashMap<UInt, String> = private val hexMapping: HashMap<UInt, String> = hashMapOf(
hashMapOf( 0U to "0", 1U to "1", 2U to "2", 3U to "3",
0U to "0", 1U to "1", 2U to "2", 3U to "3", 4U to "4", 5U to "5", 6U to "6", 7U to "7",
4U to "4", 5U to "5", 6U to "6", 7U to "7", 8U to "8", 9U to "9", 10U to "a", 11U to "b",
8U to "8", 9U to "9", 10U to "a", 11U to "b", 12U to "c", 13U to "d", 14U to "e", 15U to "f"
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 fun compareMagnitudes(mag1: Magnitude, mag2: Magnitude): Int { private fun compareMagnitudes(mag1: Magnitude, mag2: Magnitude): Int {
when { when {
@ -329,8 +313,8 @@ class KBigInteger internal constructor(
for (i in 0 until resultLength) { for (i in 0 until resultLength) {
var res: Long = var res: Long =
if (i < mag2.size) mag1[i].toLong() - mag2[i].toLong() - carry if (i < mag2.size) mag1[i].toLong() - mag2[i].toLong() - carry
else mag1[i].toLong() - carry else mag1[i].toLong() - carry
carry = if (res < 0) 1 else 0 carry = if (res < 0) 1 else 0
res += carry * (BASE + 1UL).toLong() res += carry * (BASE + 1UL).toLong()
@ -390,33 +374,76 @@ class KBigInteger internal constructor(
} }
@kotlin.ExperimentalUnsignedTypes
fun abs(x: KBigInteger): KBigInteger = x.abs()
@kotlin.ExperimentalUnsignedTypes private fun stripLeadingZeros(mag: Magnitude): Magnitude {
// Can't put it as constructor in class due to platform declaration clash with KBigInteger(Int) if (mag.isEmpty() || mag.last() != 0U) {
fun KBigInteger(x: UInt): KBigInteger return mag
= KBigInteger(1, uintArrayOf(x)) }
var resSize: Int = mag.size - 1
while (mag[resSize] == 0U) {
if (resSize == 0)
break
resSize -= 1
}
return mag.sliceArray(IntRange(0, resSize))
}
@kotlin.ExperimentalUnsignedTypes fun abs(x: BigInt): BigInt = x.abs()
// Can't put it as constructor in class due to platform declaration clash with KBigInteger(Long)
fun KBigInteger(x: ULong): KBigInteger /**
= KBigInteger(1, * Convert this [Int] to [BigInt]
KBigInteger.stripLeadingZeros(uintArrayOf( */
(x and KBigInteger.BASE).toUInt(), fun Int.toBigInt() = BigInt(sign.toByte(), uintArrayOf(kotlin.math.abs(this).toUInt()))
((x shr KBigInteger.BASE_SIZE) and KBigInteger.BASE).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 sign: Int
val sPositive: String val sPositive: String
when { when {
@ -433,27 +460,25 @@ fun String.toKBigInteger(): KBigInteger? {
sign = +1 sign = +1
} }
} }
var res = KBigInteger.ZERO var res = BigInt.ZERO
var digitValue = KBigInteger.ONE var digitValue = BigInt.ONE
val sPositiveUpper = sPositive.toUpperCase() val sPositiveUpper = sPositive.toUpperCase()
if (sPositiveUpper.startsWith("0X")) { // hex representation if (sPositiveUpper.startsWith("0X")) { // hex representation
val sHex = sPositiveUpper.substring(2) val sHex = sPositiveUpper.substring(2)
for (ch in sHex.reversed()) { for (ch in sHex.reversed()) {
if (ch == '_') continue if (ch == '_') continue
res += digitValue * (hexChToInt[ch] ?: return null) res += digitValue * (hexChToInt[ch] ?: return null)
digitValue *= KBigInteger(16) digitValue *= 16.toBigInt()
} }
} } else { // decimal representation
else { // decimal representation for (ch in sPositiveUpper.reversed()) {
val sDecimal = sPositiveUpper
for (ch in sDecimal.reversed()) {
if (ch == '_') continue if (ch == '_') continue
if (ch !in '0'..'9') { if (ch !in '0'..'9') {
return null return null
} }
res += digitValue * (ch.toInt() - '0'.toInt()) res += digitValue * (ch.toInt() - '0'.toInt())
digitValue *= KBigInteger(10) digitValue *= 10.toBigInt()
} }
} }
return res * sign return res * sign
} }

View File

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

View File

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

View File

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

View File

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

View File

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