Skip to content

Commit 014c317

Browse files
authored
Merge pull request #38 from ionspin/modular-integer-mod-improvement
Improving ModularBigInteger exponentiation
2 parents 920647b + 3dba5df commit 014c317

File tree

7 files changed

+169
-21
lines changed

7 files changed

+169
-21
lines changed

CHANGELOG.md

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

4-
##### 0.1.0-SNAPSHOT - 2.6.2019
4+
##### 0.1.0-SNAPSHOT - 28.7.2019
55
- Added toFloat and toDouble to BigInteger and ModularBigInteger classes
66
- Added BigInteger creation from Float and Double by using `tryFromFloat` and `tryFromDouble`, with optional exact
77
parameter to preserve precision.
88
- Added BigInteger comparison with Float and Double
9+
- Added BigDecimal configuration option to switch to expanded representation instead of scientific when calling `toString()`
10+
- Improved ModularBigInteger exponentiation algorithm, based on Bruce Schneier Applied Cryptography pesudocode
911

1012
##### 0.0.9 - 11.5.2019 Adding modular integer support, changing api
1113
- Added modular integers - ModularBigInteger

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ interface BigNumber<BigType> where BigType : BigNumber<BigType> {
3131

3232
@ExperimentalUnsignedTypes
3333
interface Creator<BigType> {
34+
val ZERO : BigType
35+
val ONE : BigType
36+
val TWO : BigType
37+
val TEN : BigType
3438
fun parseString(string: String, base: Int = 10): BigType
3539
fun fromULong(uLong: ULong): BigType
3640
fun fromUInt(uInt: UInt): BigType

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

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,10 @@ package com.ionspin.kotlin.bignum.decimal
2020
import com.ionspin.kotlin.bignum.BigNumber
2121
import com.ionspin.kotlin.bignum.CommonBigNumberOperations
2222
import com.ionspin.kotlin.bignum.integer.BigInteger
23-
import com.ionspin.kotlin.bignum.integer.BigInteger.Companion.TEN
24-
import com.ionspin.kotlin.bignum.integer.BigInteger.Companion.TWO
25-
import com.ionspin.kotlin.bignum.integer.BigInteger.Companion.ZERO
2623
import com.ionspin.kotlin.bignum.integer.Sign
2724
import com.ionspin.kotlin.bignum.integer.toBigInteger
2825
import kotlin.math.absoluteValue
2926
import kotlin.math.max
30-
import kotlin.math.sqrt
3127

3228
/**
3329
* Implementation of floating-point arbitrary precision arithmetic.
@@ -55,9 +51,10 @@ class BigDecimal private constructor(
5551
val precision = significand.numberOfDecimalDigits()
5652

5753
companion object : BigNumber.Creator<BigDecimal> {
58-
val ZERO = BigDecimal(BigInteger.ZERO)
59-
val ONE = BigDecimal(BigInteger.ONE)
60-
val TWO = BigDecimal(BigInteger.TWO)
54+
override val ZERO = BigDecimal(BigInteger.ZERO)
55+
override val ONE = BigDecimal(BigInteger.ONE)
56+
override val TWO = BigDecimal(BigInteger.TWO)
57+
override val TEN = BigDecimal(BigInteger.TEN)
6158

6259
var useToStringExpanded : Boolean = false
6360

@@ -84,7 +81,7 @@ class BigDecimal private constructor(
8481

8582
val toDiscard = significand.numberOfDecimalDigits() - decimalMode.decimalPrecision
8683
var result = if (toDiscard > 0) {
87-
(significand divrem TEN.pow(toDiscard)).quotient
84+
(significand divrem BigInteger.TEN.pow(toDiscard)).quotient
8885
} else {
8986
significand
9087
}
@@ -97,7 +94,8 @@ class BigDecimal private constructor(
9794
if (discarded == BigInteger.ZERO) {
9895
BigInteger.ZERO
9996
} else {
100-
(discarded / (discarded.numberOfDecimalDigits())).abs() + (significand divrem TEN.pow(toDiscard)).remainder * TEN.pow(
97+
(discarded / (discarded.numberOfDecimalDigits())).abs() +
98+
(significand divrem BigInteger.TEN.pow(toDiscard)).remainder * BigInteger.TEN.pow(
10199
toDiscard
102100
)
103101
}
@@ -227,11 +225,11 @@ class BigDecimal private constructor(
227225
val desiredPrecision = decimalMode.decimalPrecision
228226
return when {
229227
desiredPrecision > significandDigits -> {
230-
val extendedSignificand = significand * TEN.pow(desiredPrecision - significandDigits)
228+
val extendedSignificand = significand * BigInteger.TEN.pow(desiredPrecision - significandDigits)
231229
BigDecimal(extendedSignificand, exponent, decimalMode)
232230
}
233231
desiredPrecision < significandDigits -> {
234-
val divRem = significand divrem TEN.pow(significandDigits - desiredPrecision)
232+
val divRem = significand divrem BigInteger.TEN.pow(significandDigits - desiredPrecision)
235233
val newSignificand = roundDiscarded(divRem.quotient, divRem.remainder, decimalMode)
236234
BigDecimal(newSignificand, exponent, decimalMode)
237235

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,10 @@ class BigInteger internal constructor(wordArray: WordArray, val sign: Sign) : Bi
8787
companion object : BigNumber.Creator<BigInteger>, BigNumber.Util<BigInteger> {
8888
private val arithmetic: BigIntegerArithmetic<WordArray, Word> = chosenArithmetic
8989

90-
val ZERO = BigInteger(arithmetic.ZERO, Sign.ZERO)
91-
val ONE = BigInteger(arithmetic.ONE, Sign.POSITIVE)
92-
val TWO = BigInteger(arithmetic.TWO, Sign.POSITIVE)
93-
val TEN = BigInteger(arithmetic.TEN, Sign.POSITIVE)
90+
override val ZERO = BigInteger(arithmetic.ZERO, Sign.ZERO)
91+
override val ONE = BigInteger(arithmetic.ONE, Sign.POSITIVE)
92+
override val TWO = BigInteger(arithmetic.TWO, Sign.POSITIVE)
93+
override val TEN = BigInteger(arithmetic.TEN, Sign.POSITIVE)
9494

9595
val LOG_10_OF_2 = log10(2.0)
9696

@@ -536,7 +536,11 @@ class BigInteger internal constructor(wordArray: WordArray, val sign: Sign) : Bi
536536
}
537537

538538
override infix fun shr(places: Int): BigInteger {
539-
return BigInteger(arithmetic.shiftRight(this.magnitude, places), sign)
539+
val result = BigInteger(arithmetic.shiftRight(this.magnitude, places), sign)
540+
if (result.magnitude == arithmetic.ZERO) {
541+
return ZERO
542+
}
543+
return result
540544
}
541545

542546
override operator fun unaryMinus(): BigInteger = negate()

bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/modular/ModularBigInteger.kt

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import com.ionspin.kotlin.bignum.CommonBigNumberOperations
2222
import com.ionspin.kotlin.bignum.ModularQuotientAndRemainder
2323
import com.ionspin.kotlin.bignum.NarrowingOperations
2424
import com.ionspin.kotlin.bignum.integer.BigInteger
25-
import com.ionspin.kotlin.bignum.integer.BigInteger.Companion.ONE
2625
import com.ionspin.kotlin.bignum.integer.Sign
2726

2827
/**
@@ -60,6 +59,12 @@ class ModularBigInteger @ExperimentalUnsignedTypes constructor(
6059

6160
fun creatorForModulo(modulo: BigInteger) : BigNumber.Creator<ModularBigInteger> {
6261
return object : BigNumber.Creator<ModularBigInteger> {
62+
override val ZERO = ModularBigInteger(BigInteger.ZERO, modulo, this)
63+
override val ONE = ModularBigInteger(BigInteger.ONE, modulo, this)
64+
override val TWO = ModularBigInteger(BigInteger.TWO, modulo, this)
65+
override val TEN = ModularBigInteger(BigInteger.TEN, modulo, this)
66+
67+
6368
override fun fromBigInteger(bigInteger: BigInteger): ModularBigInteger {
6469
return ModularBigInteger(bigInteger.prep(), modulo, this)
6570
}
@@ -207,11 +212,26 @@ class ModularBigInteger @ExperimentalUnsignedTypes constructor(
207212
}
208213

209214
fun pow(exponent: ModularBigInteger): ModularBigInteger {
210-
return ModularBigInteger(residue.pow(exponent.residue) % modulus, modulus, creator)
215+
return pow(exponent.residue)
211216
}
212217

213218
fun pow(exponent: BigInteger): ModularBigInteger {
214-
return ModularBigInteger(residue.pow(exponent) % modulus, modulus, creator)
219+
var e = exponent
220+
return if (this.modulus == BigInteger.ONE) {
221+
creator.ZERO
222+
} else {
223+
var residue = BigInteger.ONE
224+
var base = this.residue
225+
while (e > 0) {
226+
if (e % 2 == BigInteger.ONE) {
227+
residue = (residue * base) % modulus
228+
}
229+
e = e shr 1
230+
base = base.pow(2) % modulus
231+
}
232+
ModularBigInteger(residue, modulus, creator)
233+
}
234+
215235
}
216236

217237
override fun pow(exponent: Long): ModularBigInteger {
@@ -281,7 +301,7 @@ class ModularBigInteger @ExperimentalUnsignedTypes constructor(
281301
}
282302

283303
private fun checkIfDivisible(other : ModularBigInteger) {
284-
if (other.residue.gcd(modulus) != ONE) {
304+
if (other.residue.gcd(modulus) != BigInteger.ONE) {
285305
throw ArithmeticException("BigInteger is not invertible. This and modulus are not relatively prime (coprime)")
286306
}
287307
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2019 Ugljesa Jovanovic
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package com.ionspin.kotlin.bignum.modular
19+
20+
import kotlin.test.Test
21+
import kotlin.test.assertEquals
22+
23+
/**
24+
* Created by Ugljesa Jovanovic
25+
26+
* on 28-Jul-2019
27+
*/
28+
@ExperimentalUnsignedTypes
29+
class ModularBigIntegerExponentiationTest {
30+
@Test
31+
fun testExponentiation() {
32+
val base = 5
33+
val modulus = 100
34+
val exponent = 3
35+
val creator = ModularBigInteger.creatorForModulo(modulus)
36+
val result = creator.fromInt(base).pow(exponent)
37+
assertEquals(result, creator.fromInt(25))
38+
}
39+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright 2019 Ugljesa Jovanovic
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package com.ionspin.kotlin.bignum.modular
19+
20+
import com.ionspin.kotlin.bignum.integer.BigInteger
21+
import com.ionspin.kotlin.bignum.integer.Sign
22+
import com.ionspin.kotlin.bignum.integer.base63.toJavaBigInteger
23+
import kotlinx.coroutines.GlobalScope
24+
import kotlinx.coroutines.Job
25+
import kotlinx.coroutines.launch
26+
import kotlinx.coroutines.runBlocking
27+
import kotlin.random.Random
28+
import kotlin.random.nextULong
29+
import kotlin.test.Test
30+
import kotlin.test.assertTrue
31+
32+
/**
33+
* Created by Ugljesa Jovanovic
34+
35+
* on 28-Jul-2019
36+
*/
37+
@ExperimentalUnsignedTypes
38+
class JvmModularBigIntegerExponentiationTest {
39+
@Test
40+
fun testRandomModularExponentiation() {
41+
val seed = 1
42+
val random = Random(seed)
43+
val jobList: MutableList<Job> = mutableListOf()
44+
for (i in 1..1000) {
45+
val aLength = random.nextInt(1, 500)
46+
val a = ULongArray(aLength) {
47+
random.nextULong() shr 1
48+
}
49+
50+
val modulo = ULongArray(random.nextInt(1, 50)) {
51+
random.nextULong() shr 1
52+
}
53+
54+
val creator = ModularBigInteger.creatorForModulo(BigInteger(modulo, Sign.POSITIVE))
55+
56+
val b = creator.fromInt(random.nextInt(500))
57+
58+
val job = GlobalScope.launch {
59+
try {
60+
val aMod = creator.fromBigInteger(BigInteger(a, Sign.POSITIVE))
61+
singleDivisionTest(aMod, b)
62+
} catch (exception: Exception) {
63+
exception.printStackTrace()
64+
}
65+
}
66+
jobList.add(job)
67+
68+
}
69+
runBlocking {
70+
jobList.forEach { it.join() }
71+
}
72+
}
73+
74+
fun singleDivisionTest(a : ModularBigInteger, b : ModularBigInteger) {
75+
assertTrue {
76+
val result = a.pow(b)
77+
val javaResult = a.residue.toJavaBigInteger().modPow(b.residue.toJavaBigInteger(), b.modulus.toJavaBigInteger())
78+
result.toBigInteger().toJavaBigInteger() == javaResult
79+
}
80+
}
81+
}

0 commit comments

Comments
 (0)