diff --git a/kbignum/src/commonMain/kotlin/korlibs/bignumber/BigIntException.kt b/kbignum/src/commonMain/kotlin/korlibs/bignumber/BigIntException.kt index b194ac4eb2..1a4c58408a 100644 --- a/kbignum/src/commonMain/kotlin/korlibs/bignumber/BigIntException.kt +++ b/kbignum/src/commonMain/kotlin/korlibs/bignumber/BigIntException.kt @@ -1,7 +1,12 @@ package korlibs.bignumber +/** A generic [BigInt] exception */ open class BigIntException(message: String) : Throwable(message) +/** A [BigInt] exception thrown when an invalid String value is provided while parsing */ open class BigIntInvalidFormatException(message: String) : BigIntException(message) +/** A [BigInt] exception thrown when trying to divide by zero */ open class BigIntDivisionByZeroException() : BigIntException("Division by zero") +/** A [BigInt] exception thrown when an overflow operation occurs, like for example when trying to convert a too big [BigInt] into an [Int] */ open class BigIntOverflowException(message: String) : BigIntException(message) +/** A [BigInt] exception thrown when doing a `pow` operation with a negative exponent */ open class BigIntNegativeExponentException() : BigIntOverflowException("Negative exponent") diff --git a/kbignum/src/commonMain/kotlin/korlibs/bignumber/BigNum.kt b/kbignum/src/commonMain/kotlin/korlibs/bignumber/BigNum.kt index 0ac1c8c79b..bae9131ce0 100644 --- a/kbignum/src/commonMain/kotlin/korlibs/bignumber/BigNum.kt +++ b/kbignum/src/commonMain/kotlin/korlibs/bignumber/BigNum.kt @@ -54,14 +54,19 @@ class BigNum(val int: BigInt, val scale: Int) : Comparable { else -> BigNum(int / (10.bi pow (this.scale - otherScale)), otherScale) } + /** Performs this + [other] returning a [BigNum], if the scale is different for both numbers, it finds a common one */ operator fun plus(other: BigNum): BigNum = binary(other, BigInt::plus) + /** Performs this - [other] returning a [BigNum], if the scale is different for both numbers, it finds a common one */ operator fun minus(other: BigNum): BigNum = binary(other, BigInt::minus) + /** Performs this * [other] returning a [BigNum], the scale ends being the sum of both scales */ operator fun times(other: BigNum): BigNum = BigNum(this.int * other.int, this.scale + other.scale) + /** Performs this / [other] returning a [BigNum] */ //operator fun div(other: BigNum): BigNum = div(other, other.int.significantBits / 2) operator fun div(other: BigNum): BigNum = div(other, 0) + /** Performs this / [other] returning a [BigNum] using a specific [precision] */ fun div(other: BigNum, precision: Int): BigNum { val scale = (10.bi pow (other.scale + precision)) val li = this.int * scale @@ -70,8 +75,10 @@ class BigNum(val int: BigInt, val scale: Int) : Comparable { return BigNum(res, this.scale) * BigNum(1.bi, precision) } - infix fun pow(other: Int) = pow(other, 32) + /** Performs this ** [exponent] */ + infix fun pow(exponent: Int) = pow(exponent, 32) + /** Performs this ** [exponent] with a specific [precision] */ fun pow(exponent: Int, precision: Int): BigNum { //if (exponent < 0) return ONE.div(this.pow(-exponent, precision), precision) if (exponent < 0) return ONE.div(this.pow(-exponent, precision), 0) @@ -91,6 +98,7 @@ class BigNum(val int: BigInt, val scale: Int) : Comparable { return this.convertToScale(commonScale).int.compareTo(other.convertToScale(commonScale).int) } + /** Creates a [ClosedBigNumRange] between this and [that] */ operator fun rangeTo(that: BigNum): ClosedBigNumRange = ClosedBigNumRange( start = this, endInclusive = that @@ -122,18 +130,23 @@ class BigNum(val int: BigInt, val scale: Int) : Comparable { } } + /** Converts this [BigInt] effectively losing the decimal places */ fun toBigInt(): BigInt = convertToScale(0).int + /** Converts this [BigInt] doing flooring when there are decimals */ fun toBigIntFloor(): BigInt = toBigInt() + /** Converts this [BigInt] doing ceiling when there are decimals */ fun toBigIntCeil(): BigInt { val it = this.toBigInt() val decimal = decimalPart return if (decimal.isZero) it else (it + 1.bi) } + /** Converts this [BigInt] doing rounding when there are decimals */ fun toBigIntRound(): BigInt { val firstDigit = decimalPart / 10.bi.pow(scale - 1) return if (firstDigit.toInt() >= 5) toBigIntCeil() else toBigIntFloor() } + /** Returns the decimal part as a [BigInt] of this BigNum so for `1.9123.bn` it will return `9123.bi` */ val decimalPart: BigInt get() = int % 10.bi.pow(scale) } diff --git a/kbignum/src/jvmAndroidMain/kotlin/korlibs/bignumber/BigIntJvm.kt b/kbignum/src/jvmAndroidMain/kotlin/korlibs/bignumber/BigIntJvm.kt index acf7c24289..b9fc1fea44 100644 --- a/kbignum/src/jvmAndroidMain/kotlin/korlibs/bignumber/BigIntJvm.kt +++ b/kbignum/src/jvmAndroidMain/kotlin/korlibs/bignumber/BigIntJvm.kt @@ -2,7 +2,14 @@ package korlibs.bignumber import java.math.* -val BigInteger.bi get() = JvmBigInt(this) +/** Converts a [BigInteger] into a [BigInt] ([JvmBigInt]) */ +val BigInteger.bi: JvmBigInt get() = JvmBigInt(this) + +/** Converts a [BigInt] into a [BigInteger] */ +fun BigInt.toBigInteger(): BigInteger = when (this) { + is JvmBigInt -> this.value + else -> BigInteger(this.toString()) +} actual val BigIntNativeFactory: BigIntConstructor = JvmBigInt diff --git a/kbignum/src/jvmTest/kotlin/korlibs/bignumber/BigIntCompareWithJVMTest.kt b/kbignum/src/jvmTest/kotlin/korlibs/bignumber/BigIntCompareWithJVMTest.kt index d341f10c66..d88a4ba4ab 100644 --- a/kbignum/src/jvmTest/kotlin/korlibs/bignumber/BigIntCompareWithJVMTest.kt +++ b/kbignum/src/jvmTest/kotlin/korlibs/bignumber/BigIntCompareWithJVMTest.kt @@ -230,6 +230,13 @@ class BigIntCompareWithJVMTestCommon : AbstractBigIntCompareWithJVMTest() { override fun testRightShift2() = testBinary { jvmL, jvmR, kL, kR -> Result(">>", "${jvmL / (1 shl 27).toBigInteger()}", "${kL shr 27}") } + + @Test + fun testBidirectionalJVMBigIntegerConversion() { + val bigInt = BigInteger("0123456789".repeat(10)) + assertEquals(bigInt, bigInt.bi.toBigInteger()) + assertEquals(bigInt, CommonBigInt(bigInt.toString()).toBigInteger()) + } } class BigIntCompareWithJVMTestJVM : AbstractBigIntCompareWithJVMTest() {