Repository: ionspin/kotlin-multiplatform-bignum
Pull request #57 (2019-09-22 18:25:38)

Adding fromUByteArray and toUByteArray


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f842878..245289b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,8 @@ metadata resolution available only on Gradle >= 5.3
 - Added more BigDecimal extension functions
 - Added ModularBigInteger extension functions
 - Added Karatsuba multiplication
+- Added `copy` and `moveDecimalPoint` methods
+- Added `fromUByteArray` and `toUByteArray`
 
 
 ##### 0.1.0 - 31.7.2019
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 74f8aa9..a34cb64 100644
--- a/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/BigNumber.kt
+++ b/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/BigNumber.kt
@@ -249,12 +249,15 @@ interface BitwiseCapable<BigType> {
     fun not(): BigType
 }
 
+@ExperimentalUnsignedTypes
 interface ByteArraySerializable {
 
     fun toByteArray(): Array<Byte>
+    fun toUByteArray(endianness: Endianness): Array<UByte>
 }
 
 @ExperimentalUnsignedTypes
 interface ByteArrayDeserializable<BigType : BigNumber<BigType>> {
     fun fromByteArray(byteArray: Array<Byte>): BigType
+    fun fromUByteArray(uByteArray: Array<UByte>, endianness: Endianness): BigType
 }
\ No newline at end of file
diff --git a/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/Endianness.kt b/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/Endianness.kt
new file mode 100644
index 0000000..ce5acc5
--- /dev/null
+++ b/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/Endianness.kt
@@ -0,0 +1,27 @@
+/*
+ *    Copyright 2019 Ugljesa Jovanovic
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ *
+ */
+
+package com.ionspin.kotlin.bignum
+
+/**
+ * Created by Ugljesa Jovanovic
+ * ugljesa.jovanovic@ionspin.com
+ * on 22-Sep-2019
+ */
+enum class Endianness {
+    BIG, LITTLE
+}
\ 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 33e59ed..defb211 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
@@ -22,6 +22,7 @@ import com.ionspin.kotlin.bignum.BitwiseCapable
 import com.ionspin.kotlin.bignum.ByteArrayDeserializable
 import com.ionspin.kotlin.bignum.ByteArraySerializable
 import com.ionspin.kotlin.bignum.CommonBigNumberOperations
+import com.ionspin.kotlin.bignum.Endianness
 import com.ionspin.kotlin.bignum.NarrowingOperations
 import com.ionspin.kotlin.bignum.decimal.BigDecimal
 import com.ionspin.kotlin.bignum.modular.ModularBigInteger
@@ -200,6 +201,11 @@ class BigInteger internal constructor(wordArray: WordArray, val sign: Sign) : Bi
             val result = arithmetic.fromByteArray(byteArray)
             return BigInteger(result.first, result.second)
         }
+
+        override fun fromUByteArray(uByteArray: Array<UByte>, endianness: Endianness): BigInteger {
+            val result = arithmetic.fromUByteArray(uByteArray, endianness)
+            return BigInteger(result.first, result.second)
+        }
     }
 
     internal val magnitude: WordArray = wordArray
@@ -749,6 +755,10 @@ class BigInteger internal constructor(wordArray: WordArray, val sign: Sign) : Bi
         return arithmetic.toByteArray(magnitude, sign)
     }
 
+    override fun toUByteArray(endianness: Endianness): Array<UByte> {
+        return arithmetic.toUByteArray(magnitude, endianness)
+    }
+
     operator fun rangeTo(other: BigInteger) = BigIntegerRange(this, other)
 
     class BigIntegerRange(override val start: BigInteger, override val endInclusive: BigInteger) :
diff --git a/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/integer/BigIntegerArithmetic.kt b/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/integer/BigIntegerArithmetic.kt
index 7af27c1..3452498 100644
--- a/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/integer/BigIntegerArithmetic.kt
+++ b/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/integer/BigIntegerArithmetic.kt
@@ -17,6 +17,8 @@
 
 package com.ionspin.kotlin.bignum.integer
 
+import com.ionspin.kotlin.bignum.Endianness
+
 /**
  * Interface defining big integer operations
  *
@@ -138,6 +140,8 @@ interface BigIntegerArithmetic<BackingCollectionType, BackingWordType> {
 
     fun toByteArray(operand: BackingCollectionType, sign: Sign): Array<Byte>
     fun fromByteArray(byteArray: Array<Byte>): Pair<BackingCollectionType, Sign>
+    fun fromUByteArray(uByteArray: Array<UByte>, endianness: Endianness = Endianness.BIG): Pair<BackingCollectionType, Sign>
+    fun toUByteArray(operand: BackingCollectionType, endianness: Endianness = Endianness.BIG): Array<UByte>
 }
 
 /**
diff --git a/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/integer/base32/BigInteger32Arithmetic.kt b/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/integer/base32/BigInteger32Arithmetic.kt
index f28bec0..319a96c 100644
--- a/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/integer/base32/BigInteger32Arithmetic.kt
+++ b/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/integer/base32/BigInteger32Arithmetic.kt
@@ -17,6 +17,7 @@
 
 package com.ionspin.kotlin.bignum.integer.base32
 
+import com.ionspin.kotlin.bignum.Endianness
 import com.ionspin.kotlin.bignum.integer.BigInteger
 import com.ionspin.kotlin.bignum.integer.BigIntegerArithmetic
 import com.ionspin.kotlin.bignum.integer.Quadruple
@@ -1037,6 +1038,62 @@ internal object BigInteger32Arithmetic : BigIntegerArithmetic<UIntArray, UInt> {
         }
     }
 
+    override fun fromUByteArray(uByteArray: Array<UByte>, endianness: Endianness): Pair<UIntArray, Sign> {
+        val chunked = when (endianness) {
+            Endianness.BIG -> {
+                uByteArray.toList().reversed().chunked(4)
+            }
+            Endianness.LITTLE -> {
+                uByteArray.toList().chunked(4)
+            }
+        }
+
+        val resolvedSign = Sign.POSITIVE
+
+        val collected = chunked.flatMap { chunk ->
+            val result = chunk.reversed().foldIndexed(0U) { index, acc, byte ->
+                acc + ((byte.toUInt() and 0xFFU) shl ((chunk.size - 1) * 8 - index * 8))
+            }
+            val discard = 4 - chunk.size
+            val discarded = (result shl (8 * discard)) shr (8 * discard)
+            uintArrayOf(discarded)
+        }.toUIntArray()
+        if (collected.contentEquals(ZERO)) {
+            return Pair(ZERO, Sign.ZERO)
+        }
+        val corrected = collected.dropLastWhile { it == 0U }.toUIntArray()
+
+        return Pair(removeLeadingZeroes(corrected), resolvedSign)
+    }
+
+    override fun toUByteArray(operand: UIntArray, endianness: Endianness): Array<UByte> {
+        val corrected = when (endianness) {
+            Endianness.BIG -> {
+                val collected = operand.reversed().flatMap {
+                    listOf(
+                        ((it shr 24) and 0xFFU).toUByte(),
+                        ((it shr 16) and 0xFFU).toUByte(),
+                        ((it shr 8) and 0xFFU).toUByte(),
+                        ((it) and 0xFFU).toUByte()
+                    )
+                }
+                collected
+            }
+            Endianness.LITTLE -> {
+                val collected = operand.flatMap {
+                    listOf(
+                        ((it shr 24) and 0xFFU).toUByte(),
+                        ((it shr 16) and 0xFFU).toUByte(),
+                        ((it shr 8) and 0xFFU).toUByte(),
+                        ((it) and 0xFFU).toUByte()
+                    )
+                }
+                collected
+            }
+        }.toTypedArray()
+        return corrected.dropLeadingZeroes()
+    }
+
     private fun List<Byte>.dropLeadingZeroes(): List<Byte> {
         return this.dropWhile { it == 0.toByte() }
     }
@@ -1044,4 +1101,8 @@ internal object BigInteger32Arithmetic : BigIntegerArithmetic<UIntArray, UInt> {
     private fun Array<Byte>.dropLeadingZeroes(): Array<Byte> {
         return this.dropWhile { it == 0.toByte() }.toTypedArray()
     }
+
+    private fun Array<UByte>.dropLeadingZeroes(): Array<UByte> {
+        return this.dropWhile { it == 0.toUByte() }.toTypedArray()
+    }
 }
\ No newline at end of file
diff --git a/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/integer/base63/BigInteger63Arithmetic.kt b/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/integer/base63/BigInteger63Arithmetic.kt
index e65e614..b473325 100644
--- a/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/integer/base63/BigInteger63Arithmetic.kt
+++ b/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/integer/base63/BigInteger63Arithmetic.kt
@@ -17,6 +17,7 @@
 
 package com.ionspin.kotlin.bignum.integer.base63
 
+import com.ionspin.kotlin.bignum.Endianness
 import com.ionspin.kotlin.bignum.integer.BigInteger
 import com.ionspin.kotlin.bignum.integer.BigIntegerArithmetic
 import com.ionspin.kotlin.bignum.integer.Quadruple
@@ -1176,6 +1177,16 @@ internal object BigInteger63Arithmetic : BigIntegerArithmetic<ULongArray, ULong>
         return Pair(convertFrom32BitRepresentation(result.first), result.second)
     }
 
+    override fun fromUByteArray(uByteArray: Array<UByte>, endianness: Endianness): Pair<ULongArray, Sign> {
+        val result = BigInteger32Arithmetic.fromUByteArray(uByteArray, endianness)
+        return Pair(convertFrom32BitRepresentation(result.first), result.second)
+    }
+
+    override fun toUByteArray(operand: ULongArray, endianness: Endianness): Array<UByte> {
+        val result = BigInteger32Arithmetic.toUByteArray(convertTo32BitRepresentation(operand), endianness)
+        return result
+    }
+
     // ------------- Useful constants --------------
     val powersOf10 = arrayOf(
         ulongArrayOf(1UL),
diff --git a/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/integer/base63/BigInteger63LinkedListArithmetic.kt b/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/integer/base63/BigInteger63LinkedListArithmetic.kt
index 8eea3b5..d0dead5 100644
--- a/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/integer/base63/BigInteger63LinkedListArithmetic.kt
+++ b/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/integer/base63/BigInteger63LinkedListArithmetic.kt
@@ -17,6 +17,7 @@
 
 package com.ionspin.kotlin.bignum.integer.base63
 
+import com.ionspin.kotlin.bignum.Endianness
 import com.ionspin.kotlin.bignum.integer.BigInteger
 import com.ionspin.kotlin.bignum.integer.BigIntegerArithmetic
 import com.ionspin.kotlin.bignum.integer.Quadruple
@@ -995,6 +996,16 @@ internal object BigInteger63LinkedListArithmetic : BigIntegerArithmetic<List<ULo
         return Pair(convertFrom32BitRepresentation(result.first), result.second)
     }
 
+    override fun fromUByteArray(uByteArray: Array<UByte>, endianness: Endianness): Pair<List<ULong>, Sign> {
+        val result = BigInteger32Arithmetic.fromUByteArray(uByteArray, endianness)
+        return Pair(convertFrom32BitRepresentation(result.first), result.second)
+    }
+
+    override fun toUByteArray(operand: List<ULong>, endianness: Endianness): Array<UByte> {
+        val result = BigInteger32Arithmetic.toUByteArray(convertTo32BitRepresentation(operand), endianness)
+        return result
+    }
+
     // ------------- Useful constants --------------
     val powersOf10 = arrayOf(
         listOf(1UL),
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 9975e51..bc81951 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
@@ -18,7 +18,10 @@
 package com.ionspin.kotlin.bignum.modular
 
 import com.ionspin.kotlin.bignum.BigNumber
+import com.ionspin.kotlin.bignum.ByteArrayDeserializable
+import com.ionspin.kotlin.bignum.ByteArraySerializable
 import com.ionspin.kotlin.bignum.CommonBigNumberOperations
+import com.ionspin.kotlin.bignum.Endianness
 import com.ionspin.kotlin.bignum.ModularQuotientAndRemainder
 import com.ionspin.kotlin.bignum.NarrowingOperations
 import com.ionspin.kotlin.bignum.integer.BigInteger
@@ -39,7 +42,8 @@ class ModularBigInteger @ExperimentalUnsignedTypes private constructor(
     private val creator: BigNumber.Creator<ModularBigInteger>
 ) : BigNumber<ModularBigInteger>,
     CommonBigNumberOperations<ModularBigInteger>,
-    NarrowingOperations<ModularBigInteger> {
+    NarrowingOperations<ModularBigInteger>,
+    ByteArraySerializable {
 
     val residue = when (signedResidue.sign) {
         Sign.POSITIVE -> signedResidue
@@ -383,4 +387,12 @@ class ModularBigInteger @ExperimentalUnsignedTypes private constructor(
     override fun doubleValue(exactRequired: Boolean): Double {
         return residue.doubleValue()
     }
+
+    override fun toByteArray(): Array<Byte> {
+        return residue.toByteArray()
+    }
+
+    override fun toUByteArray(endianness: Endianness): Array<UByte> {
+        return residue.toUByteArray(endianness)
+    }
 }
\ No newline at end of file
diff --git a/bignum/src/jsMain/kotlin/com/ionspin/kotlin/bignum/integer/PlatformBigIntegerArithmetic.kt b/bignum/src/jsMain/kotlin/com/ionspin/kotlin/bignum/integer/PlatformBigIntegerArithmetic.kt
index 6dcaec9..ab8cce0 100644
--- a/bignum/src/jsMain/kotlin/com/ionspin/kotlin/bignum/integer/PlatformBigIntegerArithmetic.kt
+++ b/bignum/src/jsMain/kotlin/com/ionspin/kotlin/bignum/integer/PlatformBigIntegerArithmetic.kt
@@ -17,6 +17,8 @@
 
 package com.ionspin.kotlin.bignum.integer
 
+import com.ionspin.kotlin.bignum.Endianness
+
 /**
  * Created by Ugljesa Jovanovic
  * ugljesa.jovanovic@ionspin.com
@@ -164,4 +166,12 @@ actual object PlatformBigIntegerArithmetic : BigIntegerArithmetic<IntArray, Int>
     override fun fromByteArray(byteArray: Array<Byte>): Pair<IntArray, Sign> {
         TODO("not implemented yet")
     }
+
+    override fun fromUByteArray(uByteArray: Array<UByte>, endianness: Endianness): Pair<IntArray, Sign> {
+        TODO("not implemented yet")
+    }
+
+    override fun toUByteArray(operand: IntArray, endianness: Endianness): Array<UByte> {
+        TODO("not implemented yet")
+    }
 }
\ No newline at end of file
diff --git a/bignum/src/jvmMain/kotlin/com/ionspin/kotlin/bignum/integer/PlatformBigIntegerArithmetic.kt b/bignum/src/jvmMain/kotlin/com/ionspin/kotlin/bignum/integer/PlatformBigIntegerArithmetic.kt
index 6dcaec9..ab8cce0 100644
--- a/bignum/src/jvmMain/kotlin/com/ionspin/kotlin/bignum/integer/PlatformBigIntegerArithmetic.kt
+++ b/bignum/src/jvmMain/kotlin/com/ionspin/kotlin/bignum/integer/PlatformBigIntegerArithmetic.kt
@@ -17,6 +17,8 @@
 
 package com.ionspin.kotlin.bignum.integer
 
+import com.ionspin.kotlin.bignum.Endianness
+
 /**
  * Created by Ugljesa Jovanovic
  * ugljesa.jovanovic@ionspin.com
@@ -164,4 +166,12 @@ actual object PlatformBigIntegerArithmetic : BigIntegerArithmetic<IntArray, Int>
     override fun fromByteArray(byteArray: Array<Byte>): Pair<IntArray, Sign> {
         TODO("not implemented yet")
     }
+
+    override fun fromUByteArray(uByteArray: Array<UByte>, endianness: Endianness): Pair<IntArray, Sign> {
+        TODO("not implemented yet")
+    }
+
+    override fun toUByteArray(operand: IntArray, endianness: Endianness): Array<UByte> {
+        TODO("not implemented yet")
+    }
 }
\ No newline at end of file
diff --git a/bignum/src/nativeMain/kotlin/com/ionspin/kotlin/bignum/integer/PlatformBigIntegerArithmetic.kt b/bignum/src/nativeMain/kotlin/com/ionspin/kotlin/bignum/integer/PlatformBigIntegerArithmetic.kt
index 74faaad..04df12b 100644
--- a/bignum/src/nativeMain/kotlin/com/ionspin/kotlin/bignum/integer/PlatformBigIntegerArithmetic.kt
+++ b/bignum/src/nativeMain/kotlin/com/ionspin/kotlin/bignum/integer/PlatformBigIntegerArithmetic.kt
@@ -17,6 +17,8 @@
 
 package com.ionspin.kotlin.bignum.integer
 
+import com.ionspin.kotlin.bignum.Endianness
+
 /**
  * Created by Ugljesa Jovanovic
  * ugljesa.jovanovic@ionspin.com
@@ -163,4 +165,12 @@ actual object PlatformBigIntegerArithmetic : BigIntegerArithmetic<IntArray, Int>
     override fun fromByteArray(byteArray: Array<Byte>): Pair<IntArray, Sign> {
         TODO("not implemented yet")
     }
+
+    override fun fromUByteArray(uByteArray: Array<UByte>, endianness: Endianness): Pair<IntArray, Sign> {
+        TODO("not implemented yet")
+    }
+
+    override fun toUByteArray(operand: IntArray, endianness: Endianness): Array<UByte> {
+        TODO("not implemented yet")
+    }
 }
\ No newline at end of file


Test patch:
diff --git a/bignum/src/jvmTest/kotlin/com/ionspin/kotlin/bignum/integer/base32/ByteArrayToAndFromTest.kt b/bignum/src/jvmTest/kotlin/com/ionspin/kotlin/bignum/integer/base32/ByteArrayToAndFromTest.kt
index 5570e47..bc2172a 100644
--- a/bignum/src/jvmTest/kotlin/com/ionspin/kotlin/bignum/integer/base32/ByteArrayToAndFromTest.kt
+++ b/bignum/src/jvmTest/kotlin/com/ionspin/kotlin/bignum/integer/base32/ByteArrayToAndFromTest.kt
@@ -17,6 +17,7 @@
 
 package com.ionspin.kotlin.bignum.integer.base32
 
+import com.ionspin.kotlin.bignum.Endianness
 import com.ionspin.kotlin.bignum.integer.Sign
 import kotlinx.coroutines.GlobalScope
 import kotlinx.coroutines.Job
@@ -263,6 +264,25 @@ class ByteArrayToAndFromTest {
         }
     }
 
+    @Test
+    fun testFromUByteArray() {
+        assertTrue {
+            val uByteArray = "FF01ABCD01FF".chunked(2).map { it.toUByte(16) }.toTypedArray()
+            val bigInt = BigInteger32Arithmetic.fromUByteArray(uByteArray, Endianness.BIG)
+            bigInt.first.toJavaBigInteger() == 0xFF01ABCD01FF.toBigInteger()
+        }
+    }
+
+    @Test
+    fun testToUByteArray() {
+        assertTrue {
+            val uByteArray = "FF01ABCD01FF".chunked(2).map { it.toUByte(16) }.toTypedArray()
+            val bigInt = BigInteger32Arithmetic.fromUByteArray(uByteArray, Endianness.BIG)
+            val reconstructed = BigInteger32Arithmetic.toUByteArray(bigInt.first, Endianness.BIG)
+            uByteArray.contentEquals(reconstructed)
+        }
+    }
+
     private fun Array<Byte>.dropLeadingZeroes(): Array<Byte> {
         return this.dropWhile { it == 0.toByte() }.toTypedArray()
     }