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 21b097609..749cd5e75 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/BigIntBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/BigIntBenchmark.kt @@ -21,7 +21,7 @@ internal class BigIntBenchmark { val kmNumber = BigIntField.number(Int.MAX_VALUE) val jvmNumber = JBigIntegerField.number(Int.MAX_VALUE) - val largeKmNumber = BigIntField { number(11).pow(100_000UL) } + val largeKmNumber = BigIntField { number(11).pow(100_000U) } val largeJvmNumber: BigInteger = JBigIntegerField { number(11).pow(100_000) } val bigExponent = 50_000 @@ -67,7 +67,7 @@ internal class BigIntBenchmark { @Benchmark fun kmPower(blackhole: Blackhole) = BigIntField { - blackhole.consume(kmNumber.pow(bigExponent.toULong())) + blackhole.consume(kmNumber.pow(bigExponent.toUInt())) } @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 8881ae93c..b444ed964 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 @@ -253,26 +253,6 @@ public interface Ring : Group, RingOperations { public val one: T } -@UnstableKMathAPI -public fun Ring.pow(base: T, exponent: ULong): T = when { - this == zero && exponent > 0UL -> zero - this == one -> base - this == -one -> powWithoutOptimization(base, exponent % 2UL) - else -> powWithoutOptimization(base, exponent) -} - -@UnstableKMathAPI -public fun Ring.pow(base: T, exponent: UInt): T = pow(base, exponent.toULong()) - -private fun Ring.powWithoutOptimization(base: T, exponent: ULong): T = when (exponent) { - 0UL -> one - 1UL -> base - else -> { - val pre = powWithoutOptimization(base, exponent shr 1).let { it * it } - if (exponent and 1UL == 0UL) pre else pre * base - } -} - /** * Represents field without without multiplicative and additive identities, i.e. algebraic structure with associative, binary, commutative operations * [add] and [multiply]; binary operation [divide] as multiplication of left operand by reciprocal of right one. diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BigInt.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BigInt.kt index 0dcbbe567..924ef07f4 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BigInt.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BigInt.kt @@ -98,11 +98,7 @@ public class BigInt internal constructor( else -> BigInt(sign, multiplyMagnitudeByUInt(magnitude, other)) } - @UnstableKMathAPI - public fun pow(other: ULong): BigInt = BigIntField { pow(this@BigInt, other) } - - @UnstableKMathAPI - public fun pow(other: UInt): BigInt = BigIntField { pow(this@BigInt, other) } + public fun pow(exponent: UInt): BigInt = BigIntField.power(this@BigInt, exponent) public operator fun times(other: Int): BigInt = when { other > 0 -> this * kotlin.math.abs(other).toUInt() diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/algebraExtensions.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/algebraExtensions.kt index b8670553d..9691c9d17 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/algebraExtensions.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/algebraExtensions.kt @@ -97,34 +97,45 @@ public fun Sequence.averageWith(space: S): T where S : Ring, S : Sc //TODO optimized power operation /** - * Raises [arg] to the natural power [power]. + * Raises [arg] to the non-negative integer power [power]. + * + * Special case: 0 ^ 0 is 1. * * @receiver the algebra to provide multiplication. * @param arg the base. * @param power the exponent. * @return the base raised to the power. + * @author Evgeniy Zhelenskiy */ -public fun Ring.power(arg: T, power: Int): T { - require(power >= 0) { "The power can't be negative." } - require(power != 0 || arg != zero) { "The $zero raised to $power is not defined." } - if (power == 0) return one - var res = arg - repeat(power - 1) { res *= arg } - return res +public fun Ring.power(arg: T, power: UInt): T = when { + this == zero && power > 0U -> zero + this == one -> arg + this == -one -> powWithoutOptimization(arg, power % 2U) + else -> powWithoutOptimization(arg, power) } +private fun Ring.powWithoutOptimization(base: T, exponent: UInt): T = when (exponent) { + 0U -> one + 1U -> base + else -> { + val pre = powWithoutOptimization(base, exponent shr 1).let { it * it } + if (exponent and 1U == 0U) pre else pre * base + } +} + + /** * Raises [arg] to the integer power [power]. * + * Special case: 0 ^ 0 is 1. + * * @receiver the algebra to provide multiplication and division. * @param arg the base. * @param power the exponent. * @return the base raised to the power. - * @author Iaroslav Postovalov + * @author Iaroslav Postovalov, Evgeniy Zhelenskiy */ -public fun Field.power(arg: T, power: Int): T { - require(power != 0 || arg != zero) { "The $zero raised to $power is not defined." } - if (power == 0) return one - if (power < 0) return one / (this as Ring).power(arg, -power) - return (this as Ring).power(arg, power) +public fun Field.power(arg: T, power: Int): T = when { + power < 0 -> one / (this as Ring).power(arg, if (power == Int.MIN_VALUE) Int.MAX_VALUE.toUInt().inc() else (-power).toUInt()) + else -> (this as Ring).power(arg, power.toUInt()) } 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 d3fca4351..0527f5252 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 @@ -27,18 +27,12 @@ internal class BigIntAlgebraTest { @Test fun testKBigIntegerRingPow() { for (num in -5..5) - for (exponent in 0U..10U) { - assertEquals( - num.toDouble().pow(exponent.toInt()).toLong().toBigInt(), - num.toBigInt().pow(exponent.toULong()), - "$num ^ $exponent" - ) + for (exponent in 0U..10U) assertEquals( num.toDouble().pow(exponent.toInt()).toLong().toBigInt(), num.toBigInt().pow(exponent), "$num ^ $exponent" ) - } } @Test diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/operations/DoubleFieldTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/operations/DoubleFieldTest.kt index c482dc978..7e689d079 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/operations/DoubleFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/operations/DoubleFieldTest.kt @@ -18,4 +18,16 @@ internal class DoubleFieldTest { val sqrt = DoubleField { sqrt(25 * one) } assertEquals(5.0, sqrt) } + + @Test + fun testPow() = DoubleField { + val num = 5 * one + assertEquals(5.0, power(num, 1)) + assertEquals(25.0, power(num, 2)) + assertEquals(1.0, power(num, 0)) + assertEquals(0.2, power(num, -1)) + assertEquals(0.04, power(num, -2)) + assertEquals(0.0, power(num, Int.MIN_VALUE)) + assertEquals(1.0, power(zero, 0)) + } }