Repository: ionspin/kotlin-multiplatform-bignum (kotlin)

Issue:
Convert BigInteger to Int
**Is your feature request related to a problem? Please describe.**
At some point my code likes to scale down the BigInteger to a normal Int, instead of scaling up all Int's to BigInteger's. This is not possible

**Describe the solution you'd like**
Add an extra method like `.toInt()` to convert a BigInteger to an Int (which may lose some precision)

**Describe alternatives you've considered**
Rewriting all related Int's to BigInteger's.

**Additional context**
JVM provides `.intValue()`

Pull request:
for primitive signed and unsigned types. This fixes #17 and fixes #13.
Also simple range test.

Patch:
diff --git a/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/BigNumber.kt b/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/BigNumber.kt
index 1408fd70..98363242 100644
--- a/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/BigNumber.kt
+++ b/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/BigNumber.kt
@@ -134,6 +134,19 @@ interface BigNumber<BigType> where BigType : BigNumber<BigType> {
 
 
 
+
+
+}
+
+internal interface NarrowingOperations<BigType> where BigType : BigNumber<BigType> {
+    fun intValue(exactRequired : Boolean = false) : Int
+    fun longValue(exactRequired : Boolean = false) : Long
+    fun byteValue(exactRequired : Boolean = false) : Byte
+    fun shortValue(exactRequired : Boolean = false) : Short
+    fun uintValue(exactRequired : Boolean = false) : UInt
+    fun ulongValue(exactRequired : Boolean = false) : ULong
+    fun ubyteValue(exactRequired : Boolean = false) : UByte
+    fun ushortValue(exactRequired : Boolean = false) : UShort
 }
 
 @ExperimentalUnsignedTypes
diff --git a/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/decimal/BigDecimal.kt b/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/decimal/BigDecimal.kt
index a42343ff..f2c199c6 100644
--- a/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/decimal/BigDecimal.kt
+++ b/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/decimal/BigDecimal.kt
@@ -1184,6 +1184,8 @@ class BigDecimal private constructor(
         return comparison == 0
     }
 
+
+
     override fun hashCode(): Int {
         return significand.hashCode() + exponent.hashCode() + decimalMode.hashCode()
     }
@@ -1284,4 +1286,6 @@ class BigDecimal private constructor(
 
     }
 
+
+
 }
\ No newline at end of file
diff --git a/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/integer/BigInteger.kt b/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/integer/BigInteger.kt
index 418c2118..f53dbb33 100644
--- a/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/integer/BigInteger.kt
+++ b/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/integer/BigInteger.kt
@@ -20,9 +20,9 @@ package com.ionspin.kotlin.bignum.integer
 import com.ionspin.kotlin.bignum.BigNumber
 import com.ionspin.kotlin.bignum.BitwiseCapable
 import com.ionspin.kotlin.bignum.CommonBigNumberOperations
+import com.ionspin.kotlin.bignum.NarrowingOperations
 import com.ionspin.kotlin.bignum.modular.ModularBigInteger
 import kotlin.math.ceil
-import kotlin.math.floor
 import kotlin.math.log10
 
 
@@ -60,6 +60,7 @@ enum class Sign {
 @ExperimentalUnsignedTypes
 class BigInteger internal constructor(wordArray: WordArray, val sign: Sign) : BigNumber<BigInteger>,
     CommonBigNumberOperations<BigInteger>,
+    NarrowingOperations<BigInteger>,
     BitwiseCapable<BigInteger>, Comparable<Any> {
 
 
@@ -536,6 +537,10 @@ class BigInteger internal constructor(wordArray: WordArray, val sign: Sign) : Bi
             is Int -> compare(fromInt(other))
             is Short -> compare(fromShort(other))
             is Byte -> compare(fromByte(other))
+            is ULong -> compare(fromULong(other))
+            is UInt -> compare(fromUInt(other))
+            is UShort -> compare(fromUShort(other))
+            is UByte -> compare(fromUByte(other))
             else -> throw RuntimeException("Invalid comparison type for BigInteger: ${other::class.simpleName}")
         }
 
@@ -548,6 +553,10 @@ class BigInteger internal constructor(wordArray: WordArray, val sign: Sign) : Bi
             is Int -> compare(fromInt(other))
             is Short -> compare(fromShort(other))
             is Byte -> compare(fromByte(other))
+            is ULong -> compare(fromULong(other))
+            is UInt -> compare(fromUInt(other))
+            is UShort -> compare(fromUShort(other))
+            is UByte -> compare(fromUByte(other))
             else -> -1
         }
         return comparison == 0
@@ -600,9 +609,59 @@ class BigInteger internal constructor(wordArray: WordArray, val sign: Sign) : Bi
         return creator.fromBigInteger(this)
     }
 
-//    fun modularInverse(modulus : BigInteger) : BigInteger {
-//
-//    }
+    override fun intValue(exactRequired: Boolean): Int {
+        if (exactRequired && this > Int.MAX_VALUE.toUInt()) {
+            throw ArithmeticException("Cannot convert to int and provide exact value")
+        }
+        return magnitude[0].toInt()
+    }
+
+    override fun longValue(exactRequired: Boolean): Long {
+        if (exactRequired && (this > Long.MAX_VALUE.toUInt())) {
+            throw ArithmeticException("Cannot convert to long and provide exact value")
+        }
+        return magnitude[0].toLong()
+    }
+
+    override fun byteValue(exactRequired: Boolean): Byte {
+        if (exactRequired && this > Byte.MAX_VALUE.toUInt()) {
+            throw ArithmeticException("Cannot convert to byte and provide exact value")
+        }
+        return magnitude[0].toByte()
+    }
 
+    override fun shortValue(exactRequired: Boolean): Short {
+        if (exactRequired && this > Short.MAX_VALUE.toUInt()) {
+            throw ArithmeticException("Cannot convert to short and provide exact value")
+        }
+        return magnitude[0].toShort()
+    }
 
+    override fun uintValue(exactRequired: Boolean): UInt {
+        if (exactRequired && this > UInt.MAX_VALUE.toUInt()) {
+            throw ArithmeticException("Cannot convert to unsigned int and provide exact value")
+        }
+        return magnitude[0].toUInt()
+    }
+
+    override fun ulongValue(exactRequired: Boolean): ULong {
+        if (exactRequired && (this > ULong.MAX_VALUE.toUInt())) {
+            throw ArithmeticException("Cannot convert to unsigned long and provide exact value")
+        }
+        return magnitude[0]
+    }
+
+    override fun ubyteValue(exactRequired: Boolean): UByte {
+        if (exactRequired && this > UByte.MAX_VALUE.toUInt()) {
+            throw ArithmeticException("Cannot convert to unsigned byte and provide exact value")
+        }
+        return magnitude[0].toUByte()
+    }
+
+    override fun ushortValue(exactRequired: Boolean): UShort {
+        if (exactRequired && this > UShort.MAX_VALUE.toUInt()) {
+            throw ArithmeticException("Cannot convert to unsigned short and provide exact value")
+        }
+        return magnitude[0].toUShort()
+    }
 }
\ No newline at end of file
diff --git a/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/modular/ModularBigInteger.kt b/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/modular/ModularBigInteger.kt
index ca253729..e2db3a9a 100644
--- a/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/modular/ModularBigInteger.kt
+++ b/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/modular/ModularBigInteger.kt
@@ -20,6 +20,7 @@ package com.ionspin.kotlin.bignum.modular
 import com.ionspin.kotlin.bignum.BigNumber
 import com.ionspin.kotlin.bignum.CommonBigNumberOperations
 import com.ionspin.kotlin.bignum.ModularQuotientAndRemainder
+import com.ionspin.kotlin.bignum.NarrowingOperations
 import com.ionspin.kotlin.bignum.integer.BigInteger
 import com.ionspin.kotlin.bignum.integer.BigInteger.Companion.ONE
 import com.ionspin.kotlin.bignum.integer.Sign
@@ -37,7 +38,8 @@ class ModularBigInteger @ExperimentalUnsignedTypes constructor(
     val residue : BigInteger,
     val modulus : BigInteger,
     private val creator: BigNumber.Creator<ModularBigInteger>) : BigNumber<ModularBigInteger>,
-        CommonBigNumberOperations<ModularBigInteger>
+        CommonBigNumberOperations<ModularBigInteger>,
+        NarrowingOperations<ModularBigInteger>
 {
 
     init {
@@ -277,6 +279,62 @@ class ModularBigInteger @ExperimentalUnsignedTypes constructor(
         }
     }
 
+    override fun intValue(exactRequired: Boolean): Int {
+        if (exactRequired && residue > Int.MAX_VALUE.toUInt()) {
+            throw ArithmeticException("Cannot convert to int and provide exact value")
+        }
+        return residue.magnitude[0].toInt()
+    }
+
+    override fun longValue(exactRequired: Boolean): Long {
+        if (exactRequired && (residue > Long.MAX_VALUE.toUInt())) {
+            throw ArithmeticException("Cannot convert to long and provide exact value")
+        }
+        return residue.magnitude[0].toLong()
+    }
+
+    override fun byteValue(exactRequired: Boolean): Byte {
+        if (exactRequired && residue > Byte.MAX_VALUE.toUInt()) {
+            throw ArithmeticException("Cannot convert to byte and provide exact value")
+        }
+        return residue.magnitude[0].toByte()
+    }
+
+    override fun shortValue(exactRequired: Boolean): Short {
+        if (exactRequired && residue > Short.MAX_VALUE.toUInt()) {
+            throw ArithmeticException("Cannot convert to short and provide exact value")
+        }
+        return residue.magnitude[0].toShort()
+    }
+
+    override fun uintValue(exactRequired: Boolean): UInt {
+        if (exactRequired && residue > UInt.MAX_VALUE.toUInt()) {
+            throw ArithmeticException("Cannot convert to unsigned int and provide exact value")
+        }
+        return residue.magnitude[0].toUInt()
+    }
+
+    override fun ulongValue(exactRequired: Boolean): ULong {
+        if (exactRequired && (residue > ULong.MAX_VALUE.toUInt())) {
+            throw ArithmeticException("Cannot convert to unsigned long and provide exact value")
+        }
+        return residue.magnitude[0]
+    }
+
+    override fun ubyteValue(exactRequired: Boolean): UByte {
+        if (exactRequired && residue > UByte.MAX_VALUE.toUInt()) {
+            throw ArithmeticException("Cannot convert to unsigned byte and provide exact value")
+        }
+        return residue.magnitude[0].toUByte()
+    }
+
+    override fun ushortValue(exactRequired: Boolean): UShort {
+        if (exactRequired && residue > UShort.MAX_VALUE.toUInt()) {
+            throw ArithmeticException("Cannot convert to unsigned short and provide exact value")
+        }
+        return residue.magnitude[0].toUShort()
+    }
+
 
 
 


Test patch:
diff --git a/bignum/src/commonTest/kotlin/com/ionspin/kotlin/bignum/integer/BigIntegerTest.kt b/bignum/src/commonTest/kotlin/com/ionspin/kotlin/bignum/integer/BigIntegerTest.kt
index 3c941dad..816ba91a 100644
--- a/bignum/src/commonTest/kotlin/com/ionspin/kotlin/bignum/integer/BigIntegerTest.kt
+++ b/bignum/src/commonTest/kotlin/com/ionspin/kotlin/bignum/integer/BigIntegerTest.kt
@@ -18,7 +18,6 @@
 package com.ionspin.kotlin.bignum.integer.arithmetic
 
 import com.ionspin.kotlin.bignum.integer.BigInteger
-import com.ionspin.kotlin.bignum.integer.Sign
 import com.ionspin.kotlin.bignum.integer.toBigInteger
 import kotlin.test.Test
 import kotlin.test.assertFailsWith
@@ -213,4 +212,133 @@ BigIntegerTest {
 
     }
 
+    @Test
+    fun testNarrowingFunctions() {
+        assertFailsWith(ArithmeticException::class) {
+            val a = Long.MAX_VALUE.toBigInteger()
+            a.intValue(exactRequired = true)
+        }
+        assertFailsWith(ArithmeticException::class) {
+            val a = Long.MAX_VALUE.toBigInteger()
+            a.shortValue(exactRequired = true)
+        }
+        assertFailsWith(ArithmeticException::class) {
+            val a = Long.MAX_VALUE.toBigInteger()
+            a.byteValue(exactRequired = true)
+        }
+        assertFailsWith(ArithmeticException::class) {
+            val a = Long.MAX_VALUE.toBigInteger() + 10
+            a.longValue(exactRequired = true)
+        }
+        assertFailsWith(ArithmeticException::class) {
+            val a = Long.MAX_VALUE.toBigInteger()
+            a.uintValue(exactRequired = true)
+        }
+        assertFailsWith(ArithmeticException::class) {
+            val a = Long.MAX_VALUE.toBigInteger()
+            a.ushortValue(exactRequired = true)
+        }
+        assertFailsWith(ArithmeticException::class) {
+            val a = Long.MAX_VALUE.toBigInteger()
+            a.ubyteValue(exactRequired = true)
+        }
+        assertFailsWith(ArithmeticException::class) {
+            val a = Long.MAX_VALUE.toBigInteger() + 10
+            a.ulongValue(exactRequired = true)
+        }
+
+        assertTrue {
+            val a = 2.toBigInteger()
+            a.intValue(exactRequired = true) == 2
+        }
+        assertTrue {
+            val a = 2.toBigInteger()
+            val short : Short = 2
+            a.shortValue(exactRequired = true) == short
+        }
+        assertTrue {
+            val a = 2.toBigInteger()
+            val byte : Byte = 2
+            a.byteValue(exactRequired = true) == byte
+        }
+        assertTrue {
+            val a = 2.toBigInteger()
+            a.longValue(exactRequired = true) == 2L
+        }
+        assertTrue {
+            val a = 2.toBigInteger()
+            a.uintValue(exactRequired = true) == 2U
+        }
+        assertTrue {
+            val a = 2.toBigInteger()
+            val ushort : UShort = 2U
+            a.ushortValue(exactRequired = true) == ushort
+        }
+        assertTrue {
+            val a = 2.toBigInteger()
+            val ubyte : UByte = 2U
+            a.ubyteValue(exactRequired = true) == ubyte
+        }
+        assertTrue {
+            val a = 2.toBigInteger()
+            a.ulongValue(exactRequired = true) == 2UL
+        }
+
+        assertTrue {
+            val a = Long.MAX_VALUE.toBigInteger() + 10
+            a.intValue(exactRequired = false) == 9
+        }
+        assertTrue {
+            val a = Long.MAX_VALUE.toBigInteger() + 10
+            val short : Short = 9
+            a.shortValue(exactRequired = false) == short
+        }
+        assertTrue {
+            val a = Long.MAX_VALUE.toBigInteger() + 10
+            val byte : Byte = 9
+            a.byteValue(exactRequired = false) == byte
+        }
+        assertTrue {
+            val a = Long.MAX_VALUE.toBigInteger() + 10
+            a.longValue(exactRequired = false) == 9L
+        }
+        assertTrue {
+            val a = Long.MAX_VALUE.toBigInteger() + 10
+            a.uintValue(exactRequired = false) == 9U
+        }
+        assertTrue {
+            val a = Long.MAX_VALUE.toBigInteger() + 10
+            val ushort : UShort = 9U
+            a.ushortValue(exactRequired = false) == ushort
+        }
+        assertTrue {
+            val a = Long.MAX_VALUE.toBigInteger() + 10
+            val ubyte : UByte = 9U
+            a.ubyteValue(exactRequired = false) == ubyte
+        }
+        assertTrue {
+            val a = Long.MAX_VALUE.toBigInteger() + 10
+            a.ulongValue(exactRequired = false) == 9UL
+        }
+
+
+
+    }
+
+    @Test
+    fun testRange() {
+
+
+        assertTrue {
+            val range = 1.toBigInteger() .. 10.toBigInteger()
+            range.contains(5.toBigInteger())
+        }
+
+        assertTrue {
+            val range = 1.toBigInteger() .. (Long.MAX_VALUE.toBigInteger() * Long.MAX_VALUE.toBigInteger())
+            range.contains(Long.MAX_VALUE.toBigInteger() * Int.MAX_VALUE)
+        }
+
+    }
+
 }
\ No newline at end of file
diff --git a/bignum/src/jvmTest/kotlin/com/ionspin/kotlin/bignum/integer/BigIntegerJvmTest.kt b/bignum/src/jvmTest/kotlin/com/ionspin/kotlin/bignum/integer/BigIntegerJvmTest.kt
index 485f24ae..c1998ed5 100644
--- a/bignum/src/jvmTest/kotlin/com/ionspin/kotlin/bignum/integer/BigIntegerJvmTest.kt
+++ b/bignum/src/jvmTest/kotlin/com/ionspin/kotlin/bignum/integer/BigIntegerJvmTest.kt
@@ -19,7 +19,6 @@ package com.ionspin.kotlin.bignum.integer
 
 import com.ionspin.kotlin.bignum.integer.BigInteger.Companion.ZERO
 import com.ionspin.kotlin.bignum.integer.base63.toJavaBigInteger
-import com.ionspin.kotlin.bignum.modular.ModularBigInteger
 import kotlinx.coroutines.GlobalScope
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.launch
@@ -27,6 +26,7 @@ import kotlinx.coroutines.runBlocking
 import org.junit.Test
 import kotlin.random.Random
 import kotlin.random.nextULong
+import com.ionspin.kotlin.bignum.modular.ModularBigInteger
 import kotlin.test.assertTrue
 
 /**
@@ -121,4 +121,9 @@ class BigIntegerJvmTest {
     }
 
 
-}
\ No newline at end of file
+
+
+
+
+
+}