Skip to content

Commit a055735

Browse files
authored
To and from byte array (#39)
* Invalid 2 complement implementation, this needs to be on big intere level not arithmetic, as arithmetic is always unsigned. Sigh. * Working positive toByte * Working toByteArray * Renamed to/from bytearray methods * Working from byte array * Fixed 63bit parsing for ULong.MAX_VALUE, connected BigInteger to toByteArray and fromByteArray * Updated changelog and readme * Changelog version * Disable byte test as it fails only on travis
1 parent 014c317 commit a055735

File tree

13 files changed

+584
-53
lines changed

13 files changed

+584
-53
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
## Descriptive changelog
22
(All dates are DD.MM.YYYY)
33

4-
##### 0.1.0-SNAPSHOT - 28.7.2019
4+
##### 0.1.0 - 31.7.2019
5+
- Added toByteArray and fromByteArray
56
- Added toFloat and toDouble to BigInteger and ModularBigInteger classes
67
- Added BigInteger creation from Float and Double by using `tryFromFloat` and `tryFromDouble`, with optional exact
78
parameter to preserve precision.

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,18 @@ bigDecimal.toStringExpanded() == "123.456"
309309
BigDecimal: 123.456
310310
```
311311

312+
## toByteArray and fromByteArray
313+
314+
Converts the BigInteger to and from two's complement big endian array of bytes
315+
```kotlin
316+
val bigIntOriginal = BigInteger.fromULong(ULong.MAX_VALUE)
317+
val byteArray = bigIntOriginal.toByteArray()
318+
val reconstructed = BigInteger.fromByteArray(byteArray)
319+
println("${bigIntOriginal == reconstructed}")
320+
----- Output -----
321+
true
322+
```
323+
312324
### Arithmetic operations
313325

314326
Standard arithmetic operations that are present:

bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/BigNumber.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,4 +257,15 @@ interface BitwiseCapable<BigType> {
257257
*/
258258
fun not(): BigType
259259

260+
}
261+
262+
interface ByteArraySerializable {
263+
264+
fun toByteArray() : Array<Byte>
265+
266+
}
267+
268+
@ExperimentalUnsignedTypes
269+
interface ByteArrayDeserializable<BigType : BigNumber<BigType>> {
270+
fun fromByteArray(byteArray : Array<Byte>) : BigType
260271
}

bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/integer/BigInteger.kt

Lines changed: 36 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@
1717

1818
package com.ionspin.kotlin.bignum.integer
1919

20-
import com.ionspin.kotlin.bignum.BigNumber
21-
import com.ionspin.kotlin.bignum.BitwiseCapable
20+
import com.ionspin.kotlin.bignum.*
2221
import com.ionspin.kotlin.bignum.CommonBigNumberOperations
2322
import com.ionspin.kotlin.bignum.NarrowingOperations
2423
import com.ionspin.kotlin.bignum.decimal.BigDecimal
@@ -28,31 +27,6 @@ import kotlin.math.floor
2827
import kotlin.math.log10
2928

3029

31-
/**
32-
* Created by Ugljesa Jovanovic
33-
34-
* on 10-Mar-2019
35-
*/
36-
37-
enum class Sign {
38-
POSITIVE, NEGATIVE, ZERO;
39-
40-
operator fun not(): Sign {
41-
return when (this) {
42-
POSITIVE -> NEGATIVE
43-
NEGATIVE -> POSITIVE
44-
ZERO -> ZERO
45-
}
46-
}
47-
48-
fun toInt(): Int {
49-
return when (this) {
50-
POSITIVE -> 1
51-
NEGATIVE -> -1
52-
ZERO -> 0
53-
}
54-
}
55-
}
5630

5731

5832

@@ -65,7 +39,8 @@ enum class Sign {
6539
class BigInteger internal constructor(wordArray: WordArray, val sign: Sign) : BigNumber<BigInteger>,
6640
CommonBigNumberOperations<BigInteger>,
6741
NarrowingOperations<BigInteger>,
68-
BitwiseCapable<BigInteger>, Comparable<Any> {
42+
BitwiseCapable<BigInteger>, Comparable<Any>,
43+
ByteArraySerializable {
6944

7045

7146
constructor(long: Long) : this(arithmetic.fromLong(long), determinSignFromNumber(long))
@@ -82,9 +57,8 @@ class BigInteger internal constructor(wordArray: WordArray, val sign: Sign) : Bi
8257
}
8358

8459

85-
8660
@ExperimentalUnsignedTypes
87-
companion object : BigNumber.Creator<BigInteger>, BigNumber.Util<BigInteger> {
61+
companion object : BigNumber.Creator<BigInteger>, BigNumber.Util<BigInteger>, ByteArrayDeserializable<BigInteger> {
8862
private val arithmetic: BigIntegerArithmetic<WordArray, Word> = chosenArithmetic
8963

9064
override val ZERO = BigInteger(arithmetic.ZERO, Sign.ZERO)
@@ -172,8 +146,12 @@ class BigInteger internal constructor(wordArray: WordArray, val sign: Sign) : Bi
172146
}
173147

174148
}
149+
175150
//BigIntegers are immutable so this is pointless, but the rest of creator implementations use this.
176-
override fun fromBigInteger(bigInteger: BigInteger): BigInteger { return bigInteger }
151+
override fun fromBigInteger(bigInteger: BigInteger): BigInteger {
152+
return bigInteger
153+
}
154+
177155
override fun fromULong(uLong: ULong) = BigInteger(arithmetic.fromULong(uLong), Sign.POSITIVE)
178156
override fun fromUInt(uInt: UInt) = BigInteger(arithmetic.fromUInt(uInt), Sign.POSITIVE)
179157
override fun fromUShort(uShort: UShort) = BigInteger(arithmetic.fromUShort(uShort), Sign.POSITIVE)
@@ -222,6 +200,11 @@ class BigInteger internal constructor(wordArray: WordArray, val sign: Sign) : Bi
222200
second
223201
}
224202
}
203+
204+
override fun fromByteArray(byteArray: Array<Byte>): BigInteger {
205+
val result = arithmetic.fromByteArray(byteArray)
206+
return BigInteger(result.first, result.second)
207+
}
225208
}
226209

227210
internal val magnitude: WordArray = wordArray
@@ -373,22 +356,22 @@ class BigInteger internal constructor(wordArray: WordArray, val sign: Sign) : Bi
373356

374357
}
375358

376-
fun sqrt() : BigInteger {
359+
fun sqrt(): BigInteger {
377360
return BigInteger(arithmetic.sqrt(magnitude).first, this.sign)
378361
}
379362

380-
fun sqrtAndRemainder() : SqareRootAndRemainder {
363+
fun sqrtAndRemainder(): SqareRootAndRemainder {
381364
return SqareRootAndRemainder(
382365
BigInteger(arithmetic.sqrt(magnitude).first, this.sign),
383366
BigInteger(arithmetic.sqrt(magnitude).second, this.sign)
384367
)
385368
}
386369

387-
fun gcd(other: BigInteger) : BigInteger {
370+
fun gcd(other: BigInteger): BigInteger {
388371
return BigInteger(arithmetic.gcd(this.magnitude, other.magnitude), Sign.POSITIVE)
389372
}
390373

391-
private fun naiveGcd(other : BigInteger) : BigInteger {
374+
private fun naiveGcd(other: BigInteger): BigInteger {
392375
var u = this
393376
var v = other
394377
while (v != ZERO) {
@@ -399,7 +382,7 @@ class BigInteger internal constructor(wordArray: WordArray, val sign: Sign) : Bi
399382
return u
400383
}
401384

402-
fun modInverse(modulo: BigInteger) : BigInteger {
385+
fun modInverse(modulo: BigInteger): BigInteger {
403386
if (gcd(modulo) != ONE) {
404387
throw ArithmeticException("BigInteger is not invertible. This and modulus are not relatively prime (coprime)")
405388
}
@@ -408,20 +391,20 @@ class BigInteger internal constructor(wordArray: WordArray, val sign: Sign) : Bi
408391
var b = this
409392
var c = modulo
410393
while (c != ZERO) {
411-
val (q,r) = b divrem c
394+
val (q, r) = b divrem c
412395
b = c
413396
c = r
414397
val tmpU = u
415398
u = w
416-
w = tmpU - q*w
399+
w = tmpU - q * w
417400
}
418401
return u
419402
}
420403

421404
/**
422405
* Returns an always positive remainder of division operation
423406
*/
424-
infix fun mod(modulo : BigInteger) : BigInteger {
407+
infix fun mod(modulo: BigInteger): BigInteger {
425408
val result = this % modulo
426409
return if (result < 0) {
427410
result + modulo
@@ -431,7 +414,6 @@ class BigInteger internal constructor(wordArray: WordArray, val sign: Sign) : Bi
431414
}
432415

433416

434-
435417
fun compare(other: BigInteger): Int {
436418
if (isZero() && other.isZero()) return 0
437419
if (other.isZero() && this.sign == Sign.POSITIVE) return 1
@@ -598,7 +580,7 @@ class BigInteger internal constructor(wordArray: WordArray, val sign: Sign) : Bi
598580
is UShort -> compare(fromUShort(other))
599581
is UByte -> compare(fromUByte(other))
600582
is Float -> compareFloatAndBigInt(other) { compare(it) }
601-
is Double -> compareDoubleAndBigInt(other) {compare(it) }
583+
is Double -> compareDoubleAndBigInt(other) { compare(it) }
602584
else -> throw RuntimeException("Invalid comparison type for BigInteger: ${other::class.simpleName}")
603585
}
604586

@@ -608,16 +590,16 @@ class BigInteger internal constructor(wordArray: WordArray, val sign: Sign) : Bi
608590
* Javascrpt doesn't have different types for float, integer, long, it's all just "number", so we need
609591
* to check if it's a decimal or integer number before comparing.
610592
*/
611-
private fun javascriptNumberComparison(number : Number) : Int {
593+
private fun javascriptNumberComparison(number: Number): Int {
612594
val float = number.toFloat()
613-
return when {
595+
return when {
614596
float % 1 == 0f -> compare(fromLong(number.toLong()))
615597
else -> compareFloatAndBigInt(number.toFloat()) { compare(it) }
616598

617599
}
618600
}
619601

620-
fun compareFloatAndBigInt(float : Float, comparisonBlock : (BigInteger) -> Int) : Int {
602+
fun compareFloatAndBigInt(float: Float, comparisonBlock: (BigInteger) -> Int): Int {
621603
val withoutDecimalPart = floor(float)
622604
val hasDecimalPart = (float % 1 != 0f)
623605
return if (hasDecimalPart) {
@@ -633,7 +615,7 @@ class BigInteger internal constructor(wordArray: WordArray, val sign: Sign) : Bi
633615
}
634616
}
635617

636-
fun compareDoubleAndBigInt(double : Double, comparisonBlock : (BigInteger) -> Int) : Int {
618+
fun compareDoubleAndBigInt(double: Double, comparisonBlock: (BigInteger) -> Int): Int {
637619
val withoutDecimalPart = floor(double)
638620
val hasDecimalPart = (double % 1 != 0.0)
639621
return if (hasDecimalPart) {
@@ -707,7 +689,7 @@ class BigInteger internal constructor(wordArray: WordArray, val sign: Sign) : Bi
707689
return stringBuilder.toString()
708690
}
709691

710-
fun toModularBigInteger(modulo : BigInteger) : ModularBigInteger {
692+
fun toModularBigInteger(modulo: BigInteger): ModularBigInteger {
711693
val creator = ModularBigInteger.creatorForModulo(modulo)
712694
return creator.fromBigInteger(this)
713695
}
@@ -782,17 +764,22 @@ class BigInteger internal constructor(wordArray: WordArray, val sign: Sign) : Bi
782764
return this.toString().toDouble()
783765
}
784766

785-
operator fun rangeTo(other : BigInteger) = BigIntegerRange(this, other)
767+
override fun toByteArray(): Array<Byte> {
768+
return arithmetic.toByteArray(magnitude, sign)
769+
}
770+
771+
operator fun rangeTo(other: BigInteger) = BigIntegerRange(this, other)
786772

787-
class BigIntegerRange(override val start : BigInteger, override val endInclusive : BigInteger) : ClosedRange<BigInteger>, Iterable<BigInteger> {
773+
class BigIntegerRange(override val start: BigInteger, override val endInclusive: BigInteger) :
774+
ClosedRange<BigInteger>, Iterable<BigInteger> {
788775

789776
override fun iterator(): Iterator<BigInteger> {
790777
return BigIntegerIterator(start, endInclusive)
791778
}
792779

793780
}
794781

795-
class BigIntegerIterator(start : BigInteger, private val endInclusive : BigInteger) : Iterator<BigInteger> {
782+
class BigIntegerIterator(start: BigInteger, private val endInclusive: BigInteger) : Iterator<BigInteger> {
796783

797784
private var current = start
798785

bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/integer/BigIntegerArithmetic.kt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,35 @@ interface BigIntegerArithmetic<BackingCollectionType, BackingWordType> {
132132
fun bitAt(operand : BackingCollectionType, position : Long) : Boolean
133133
fun setBitAt(operand : BackingCollectionType, position : Long, bit : Boolean) : BackingCollectionType
134134

135+
fun toByteArray(operand : BackingCollectionType, sign : Sign) : Array<Byte>
136+
fun fromByteArray(byteArray: Array<Byte>) : Pair<BackingCollectionType, Sign>
135137

138+
139+
}
140+
141+
/**
142+
* Created by Ugljesa Jovanovic
143+
144+
* on 10-Mar-2019
145+
*/
146+
147+
enum class Sign {
148+
POSITIVE, NEGATIVE, ZERO;
149+
150+
operator fun not(): Sign {
151+
return when (this) {
152+
POSITIVE -> NEGATIVE
153+
NEGATIVE -> POSITIVE
154+
ZERO -> ZERO
155+
}
156+
}
157+
158+
fun toInt(): Int {
159+
return when (this) {
160+
POSITIVE -> 1
161+
NEGATIVE -> -1
162+
ZERO -> 0
163+
}
164+
}
136165
}
137166

0 commit comments

Comments
 (0)