From 1716b7f5d1d871610961419fd425c49730ea1a2f Mon Sep 17 00:00:00 2001 From: zhelenskiy Date: Mon, 10 May 2021 17:57:49 +0300 Subject: [PATCH] Fast power is added --- .../kscience/kmath/benchmarks/BigIntBenchmark.kt | 6 ++---- .../space/kscience/kmath/operations/Algebra.kt | 15 +++++++++++++++ .../kmath/operations/BigIntAlgebraTest.kt | 14 ++++++++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/BigIntBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/BigIntBenchmark.kt index b9a10c774..33c659fca 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/BigIntBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/BigIntBenchmark.kt @@ -15,15 +15,13 @@ import space.kscience.kmath.operations.BigIntField import space.kscience.kmath.operations.JBigIntegerField import space.kscience.kmath.operations.invoke -private fun BigInt.pow(power: Int): BigInt = modPow(BigIntField.number(power), BigInt.ONE) - @State(Scope.Benchmark) internal class BigIntBenchmark { val kmNumber = BigIntField.number(Int.MAX_VALUE) val jvmNumber = JBigIntegerField.number(Int.MAX_VALUE) - val largeKmNumber = BigIntField { number(11).pow(100_000) } + val largeKmNumber = BigIntField { number(11).pow(100_000UL) } val largeJvmNumber = JBigIntegerField { number(11).pow(100_000) } val bigExponent = 50_000 @@ -59,7 +57,7 @@ internal class BigIntBenchmark { @Benchmark fun kmPower(blackhole: Blackhole) = BigIntField { - blackhole.consume(kmNumber.pow(bigExponent)) + blackhole.consume(kmNumber.pow(bigExponent.toULong())) } @Benchmark diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt index 0334e0466..49861b543 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt @@ -250,6 +250,21 @@ public interface Ring : Group, RingOperations { * neutral operation for multiplication */ public val one: T + + public fun T.pow(exponent: ULong): T = when { + this == zero && exponent > 0UL -> zero + this == one -> this + else -> powWithoutOptimization(exponent) + } + + private fun T.powWithoutOptimization(exponent: ULong): T = when (exponent) { + 0UL -> one + 1UL -> this + else -> { + val pre = powWithoutOptimization(exponent shr 1).let { it * it } + if (exponent and 1UL == 0UL) pre else pre * this + } + } } /** diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/operations/BigIntAlgebraTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/operations/BigIntAlgebraTest.kt index 5df89a385..05b73b49d 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/operations/BigIntAlgebraTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/operations/BigIntAlgebraTest.kt @@ -6,6 +6,7 @@ package space.kscience.kmath.operations import space.kscience.kmath.testutils.RingVerifier +import kotlin.math.pow import kotlin.test.Test import kotlin.test.assertEquals @@ -21,6 +22,19 @@ internal class BigIntAlgebraTest { assertEquals(res, 1_000_000.toBigInt()) } + @Test + fun testKBigIntegerRingPow() { + BigIntField { + for (num in 0..5) + for (exponent in 0UL..10UL) + assertEquals( + num.toDouble().pow(exponent.toInt()).toLong().toBigInt(), + num.toBigInt().pow(exponent), + "$num ^ $exponent" + ) + } + } + @Test fun testKBigIntegerRingSum_100_000_000__100_000_000() { BigIntField {