Skip to content

Commit e954439

Browse files
authored
Merge pull request #333 from ionspin/division-reimplementation
Fix rounding issues
2 parents 86b22ce + 157e3d5 commit e954439

File tree

13 files changed

+3163
-105
lines changed

13 files changed

+3163
-105
lines changed

bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/decimal/BigDecimal.kt

Lines changed: 207 additions & 80 deletions
Large diffs are not rendered by default.

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,10 @@ class BigInteger internal constructor(wordArray: WordArray, requestedSign: Sign)
248248
return arithmetic.compare(resultMagnitude, arithmetic.ZERO) == 0
249249
}
250250

251+
fun isEven(): Boolean {
252+
return arithmetic.isEven(this.magnitude)
253+
}
254+
251255
val numberOfWords = magnitude.size
252256

253257
var stringRepresentation: String? = null

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ interface BigInteger32ArithmeticInterface {
123123

124124
fun numberOfDecimalDigits(operand: UIntArray): Long
125125

126+
fun isEven(value: UIntArray): Boolean
127+
126128
fun fromULong(uLong: ULong): UIntArray
127129
fun fromUInt(uInt: UInt): UIntArray
128130
fun fromUShort(uShort: UShort): UIntArray

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ interface BigIntegerArithmetic {
142142

143143
fun toUByteArray(operand: ULongArray): UByteArray
144144
fun toByteArray(operand: ULongArray): ByteArray
145+
fun isEven(magnitude: WordArray): Boolean
145146
}
146147

147148
/**

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ interface BigIntegerList63Arithmetic {
119119

120120
fun numberOfDecimalDigits(operand: List<ULong>): Long
121121

122+
fun isEven(value: List<ULong>): Boolean
123+
122124
fun fromULong(uLong: ULong): List<ULong>
123125
fun fromUInt(uInt: UInt): List<ULong>
124126
fun fromUShort(uShort: UShort): List<ULong>

bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/integer/base32/BigInteger32Arithmetic.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,6 +1236,14 @@ internal object BigInteger32Arithmetic : BigInteger32ArithmeticInterface {
12361236
return compare(this, uintArrayOf(other))
12371237
}
12381238

1239+
override fun isEven(value: UIntArray): Boolean {
1240+
return if (value.isEmpty()) {
1241+
true
1242+
} else {
1243+
value[0].and(1U) != 1U
1244+
}
1245+
}
1246+
12391247
fun toUnsignedIntArrayCodeFormat(array: UIntArray): String {
12401248
return array.joinToString(prefix = "uintArrayOf(", separator = ", ", postfix = ")") {
12411249
it.toString() + "U"

bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/integer/base63/BigInteger63LinkedListArithmetic.kt

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,9 @@ internal object BigInteger63LinkedListArithmetic : BigIntegerList63Arithmetic {
140140
}
141141

142142
override fun trailingZeroBits(value: List<ULong>): Int {
143-
if (value == ZERO) { return 0 }
143+
if (value == ZERO) {
144+
return 0
145+
}
144146
val zeroWordsCount = value.takeWhile { it == 0UL }.count()
145147
if (zeroWordsCount == value.size) {
146148
return 0
@@ -188,12 +190,15 @@ internal object BigInteger63LinkedListArithmetic : BigIntegerList63Arithmetic {
188190
shiftWords -> {
189191
(operand[it - shiftWords] shl shiftBits) and baseMask
190192
}
193+
191194
in (shiftWords + 1) until (originalSize + shiftWords) -> {
192195
((operand[it - shiftWords] shl shiftBits) and baseMask) or (operand[it - shiftWords - 1] shr (basePowerOfTwo - shiftBits))
193196
}
197+
194198
originalSize + wordsNeeded - 1 -> {
195199
(operand[it - wordsNeeded] shr (basePowerOfTwo - shiftBits))
196200
}
201+
197202
else -> {
198203
throw RuntimeException("Invalid case $it")
199204
}
@@ -223,11 +228,13 @@ internal object BigInteger63LinkedListArithmetic : BigIntegerList63Arithmetic {
223228
when (it) {
224229
in 0 until (operand.size - 1 - wordsToDiscard) -> {
225230
((operand[it + wordsToDiscard] shr shiftBits)) or
226-
((operand[it + wordsToDiscard + 1] shl (basePowerOfTwo - shiftBits) and baseMask))
231+
((operand[it + wordsToDiscard + 1] shl (basePowerOfTwo - shiftBits) and baseMask))
227232
}
233+
228234
operand.size - 1 - wordsToDiscard -> {
229235
(operand[it + wordsToDiscard] shr shiftBits)
230236
}
237+
231238
else -> {
232239
throw RuntimeException("Invalid case $it")
233240
}
@@ -460,10 +467,12 @@ internal object BigInteger63LinkedListArithmetic : BigIntegerList63Arithmetic {
460467
val prepared = extendULongArray(second, firstLength - secondLength, 0U)
461468
Pair(first, prepared)
462469
}
470+
463471
firstLength < secondLength -> {
464472
val prepared = extendULongArray(first, secondLength - firstLength, 0U)
465473
Pair(prepared, second)
466474
}
475+
467476
else -> Pair(first, second)
468477
}
469478

@@ -529,10 +538,10 @@ internal object BigInteger63LinkedListArithmetic : BigIntegerList63Arithmetic {
529538
val rb3 = (r3 shl (bShiftAmount * 3))
530539
val rb4 = (r4 shl (bShiftAmount * 4))
531540
val rb = rb0 +
532-
rb1 +
533-
rb2 +
534-
rb3 +
535-
rb4
541+
rb1 +
542+
rb2 +
543+
rb3 +
544+
rb4
536545

537546
return rb.unsignedValue
538547
}
@@ -554,13 +563,17 @@ internal object BigInteger63LinkedListArithmetic : BigIntegerList63Arithmetic {
554563

555564
val SIGNED_POSITIVE_TWO = SignedULongArray(TWO, true)
556565

557-
private fun signedSubtract(first: SignedULongArray, second: SignedULongArray) = signedAdd(first, second.copy(sign = !second.sign))
566+
private fun signedSubtract(first: SignedULongArray, second: SignedULongArray) =
567+
signedAdd(first, second.copy(sign = !second.sign))
558568

559-
private fun signedMultiply(first: SignedULongArray, second: SignedULongArray) = SignedULongArray(first.unsignedValue * second.unsignedValue, !(first.sign xor second.sign))
569+
private fun signedMultiply(first: SignedULongArray, second: SignedULongArray) =
570+
SignedULongArray(first.unsignedValue * second.unsignedValue, !(first.sign xor second.sign))
560571

561-
private fun signedDivide(first: SignedULongArray, second: SignedULongArray) = SignedULongArray(first.unsignedValue / second.unsignedValue, !(first.sign xor second.sign))
572+
private fun signedDivide(first: SignedULongArray, second: SignedULongArray) =
573+
SignedULongArray(first.unsignedValue / second.unsignedValue, !(first.sign xor second.sign))
562574

563-
private fun signedRemainder(first: SignedULongArray, second: SignedULongArray) = SignedULongArray(first.unsignedValue % second.unsignedValue, !(first.sign xor second.sign))
575+
private fun signedRemainder(first: SignedULongArray, second: SignedULongArray) =
576+
SignedULongArray(first.unsignedValue % second.unsignedValue, !(first.sign xor second.sign))
564577

565578
internal operator fun SignedULongArray.plus(other: SignedULongArray): SignedULongArray {
566579
return signedAdd(this, other)
@@ -859,23 +872,26 @@ internal object BigInteger63LinkedListArithmetic : BigIntegerList63Arithmetic {
859872
val position = (i * 2) - skipWordCount
860873
if (requiredLength == 2) {
861874
result[0] = operand[0].toULong() or ((operand[1].toULong() shl 32) and highMask)
862-
result[i] = (operand[1].toULong() shr 31) or (operand[2].toULong() shl 1) or (operand[3].toULong() shl 33)
875+
result[i] =
876+
(operand[1].toULong() shr 31) or (operand[2].toULong() shl 1) or (operand[3].toULong() shl 33)
863877
} else {
864878
when (i) {
865879
0 -> {
866880
result[i] = operand[0].toULong() or ((operand[1].toULong() shl 32) and highMask)
867881
}
882+
868883
in 1 until requiredLength - 1 -> {
869884
result[i] =
870885
(operand[position - 1].toULong() shr (32 - shiftAmount)) or
871-
(operand[position].toULong() shl shiftAmount) or
872-
((operand[position + 1].toULong() shl (32 + shiftAmount)) and highMask)
886+
(operand[position].toULong() shl shiftAmount) or
887+
((operand[position + 1].toULong() shl (32 + shiftAmount)) and highMask)
873888
}
889+
874890
requiredLength - 1 -> {
875891
if (position < operand.size) {
876892
result[i] =
877893
(operand[position - 1].toULong() shr (32 - shiftAmount)) or
878-
(operand[position].toULong() shl shiftAmount)
894+
(operand[position].toULong() shl shiftAmount)
879895
} else {
880896
result[i] =
881897
(operand[position - 1].toULong() shr (32 - shiftAmount))
@@ -1040,7 +1056,9 @@ internal object BigInteger63LinkedListArithmetic : BigIntegerList63Arithmetic {
10401056
}
10411057

10421058
override fun toString(operand: List<ULong>, base: Int): String {
1043-
if (operand == ZERO) { return "0" }
1059+
if (operand == ZERO) {
1060+
return "0"
1061+
}
10441062
var copy = operand
10451063
val baseArray = listOf(base.toULong())
10461064
val stringBuilder = StringBuilder()
@@ -1237,6 +1255,14 @@ internal object BigInteger63LinkedListArithmetic : BigIntegerList63Arithmetic {
12371255
return convertFrom32BitRepresentation(this)
12381256
}
12391257

1258+
override fun isEven(value: List<ULong>): Boolean {
1259+
return if (value.isEmpty()) {
1260+
true
1261+
} else {
1262+
value[0].and(1UL) != 1UL
1263+
}
1264+
}
1265+
12401266
override fun fromULong(uLong: ULong): List<ULong> {
12411267
return if ((uLong and BigInteger63Arithmetic.overflowMask) != 0UL) {
12421268
listOf(uLong and BigInteger63Arithmetic.baseMask, 1U)
@@ -1275,9 +1301,11 @@ internal object BigInteger63LinkedListArithmetic : BigIntegerList63Arithmetic {
12751301
val converted = result.toList()
12761302
return converted
12771303
}
1304+
12781305
override fun toUByteArray(operand: List<ULong>): UByteArray {
12791306
return BigInteger63Arithmetic.toUByteArray(operand.toULongArray())
12801307
}
1308+
12811309
override fun toByteArray(operand: List<ULong>): ByteArray {
12821310
return BigInteger63Arithmetic.toByteArray(operand.toULongArray())
12831311
}

bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/integer/base63/array/BigInteger63Arithmetic.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,12 +307,15 @@ internal object BigInteger63Arithmetic : BigIntegerArithmetic {
307307
shiftWords -> {
308308
(operand[it - shiftWords] shl shiftBits) and baseMask
309309
}
310+
310311
in (shiftWords + 1) until (originalSize + shiftWords) -> {
311312
((operand[it - shiftWords] shl shiftBits) and baseMask) or (operand[it - shiftWords - 1] shr (basePowerOfTwo - shiftBits))
312313
}
314+
313315
originalSize + wordsNeeded - 1 -> {
314316
(operand[it - wordsNeeded] shr (basePowerOfTwo - shiftBits))
315317
}
318+
316319
else -> {
317320
throw RuntimeException("Invalid case $it")
318321
}
@@ -354,9 +357,11 @@ internal object BigInteger63Arithmetic : BigIntegerArithmetic {
354357
((operand[it + wordsToDiscard] shr shiftBits)) or
355358
((operand[it + wordsToDiscard + 1] shl (basePowerOfTwo - shiftBits) and baseMask))
356359
}
360+
357361
realOperandSize - 1 - wordsToDiscard -> {
358362
(operand[it + wordsToDiscard] shr shiftBits)
359363
}
364+
360365
else -> {
361366
throw RuntimeException("Invalid case $it")
362367
}
@@ -859,6 +864,7 @@ internal object BigInteger63Arithmetic : BigIntegerArithmetic {
859864
)
860865
Pair(first, prepared)
861866
}
867+
862868
firstLength < secondLength -> {
863869
val prepared =
864870
extendULongArray(
@@ -868,6 +874,7 @@ internal object BigInteger63Arithmetic : BigIntegerArithmetic {
868874
)
869875
Pair(prepared, second)
870876
}
877+
871878
else -> Pair(first, second)
872879
}
873880

@@ -1534,12 +1541,14 @@ internal object BigInteger63Arithmetic : BigIntegerArithmetic {
15341541
0 -> {
15351542
result[i] = operand[0].toULong() or ((operand[1].toULong() shl 32) and highMask)
15361543
}
1544+
15371545
in 1 until requiredLength - 1 -> {
15381546
result[i] =
15391547
(operand[position - 1].toULong() shr (32 - shiftAmount)) or
15401548
(operand[position].toULong() shl shiftAmount) or
15411549
((operand[position + 1].toULong() shl (32 + shiftAmount)) and highMask)
15421550
}
1551+
15431552
requiredLength - 1 -> {
15441553
if (position < operand.size) {
15451554
result[i] =
@@ -1592,6 +1601,7 @@ internal object BigInteger63Arithmetic : BigIntegerArithmetic {
15921601
0 -> {
15931602
result[i] = operand[0] and baseMask
15941603
}
1604+
15951605
in 1 until requiredLength - 1 -> {
15961606
result[i] = if (shiftAmount > 0) {
15971607
((operand[position - 1] shr (64 - shiftAmount)) or
@@ -1600,6 +1610,7 @@ internal object BigInteger63Arithmetic : BigIntegerArithmetic {
16001610
(operand[position] shl shiftAmount) and baseMask
16011611
}
16021612
}
1613+
16031614
requiredLength - 1 -> {
16041615
if (position < operand.size) {
16051616
result[i] = if (shiftAmount > 0) {
@@ -2158,6 +2169,14 @@ internal object BigInteger63Arithmetic : BigIntegerArithmetic {
21582169
)
21592170
}
21602171

2172+
override fun isEven(value: ULongArray): Boolean {
2173+
return if (value.isEmpty()) {
2174+
true
2175+
} else {
2176+
value[0].and(1UL) != 1UL
2177+
}
2178+
}
2179+
21612180
internal fun ULongArray.to32Bit(): UIntArray {
21622181
return convertTo32BitRepresentation(
21632182
this

bignum/src/commonTest/kotlin/com/ionspin/kotlin/bignum/integer/arithmetic/BigInteger32BitWiseTest.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

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

20+
import com.ionspin.kotlin.bignum.integer.base32.BigInteger32Arithmetic
2021
import com.ionspin.kotlin.bignum.integer.base63.array.BigInteger63Arithmetic
2122
import kotlin.test.Ignore
2223
import kotlin.test.Test
@@ -51,4 +52,20 @@ class BigInteger32BitWiseTest {
5152
count == 70
5253
}
5354
}
55+
56+
@Test
57+
fun testIsEven() {
58+
59+
val firstArray = uintArrayOf(0U, 64U, 0U, 2U)
60+
val number = BigInteger32Arithmetic.add(firstArray, uintArrayOf(2U))
61+
assertTrue {
62+
BigInteger32Arithmetic.isEven(number)
63+
}
64+
65+
val secondArray = uintArrayOf(1U, 64U, 0U, 2U)
66+
val number2 = BigInteger32Arithmetic.add(secondArray, uintArrayOf(2U))
67+
assertTrue {
68+
BigInteger32Arithmetic.isEven(number2).not()
69+
}
70+
}
5471
}

bignum/src/commonTest/kotlin/com/ionspin/kotlin/bignum/integer/arithmetic/bigint63/BigInteger63BitwiseTest.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,19 @@ class BigInteger63BitwiseTest {
3737
expected.contentEquals(result)
3838
}
3939
}
40+
41+
@Test
42+
fun testIsEven() {
43+
val firstArray = ulongArrayOf(0U, 64U, 0U, 3U, 2U, 1U)
44+
val number = BigInteger63Arithmetic.add(firstArray, ulongArrayOf(2U))
45+
assertTrue {
46+
BigInteger63Arithmetic.isEven(number)
47+
}
48+
49+
val secondArray = ulongArrayOf(1U, 64U, 0U, 2U)
50+
val number2 = BigInteger63Arithmetic.add(secondArray, ulongArrayOf(2U))
51+
assertTrue {
52+
BigInteger63Arithmetic.isEven(number2).not()
53+
}
54+
}
4055
}

0 commit comments

Comments
 (0)