forked from kscience/kmath
Reusing of existing power function
This commit is contained in:
parent
1e94538931
commit
c204747401
@ -21,7 +21,7 @@ internal class BigIntBenchmark {
|
|||||||
|
|
||||||
val kmNumber = BigIntField.number(Int.MAX_VALUE)
|
val kmNumber = BigIntField.number(Int.MAX_VALUE)
|
||||||
val jvmNumber = JBigIntegerField.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 largeJvmNumber: BigInteger = JBigIntegerField { number(11).pow(100_000) }
|
||||||
val bigExponent = 50_000
|
val bigExponent = 50_000
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ internal class BigIntBenchmark {
|
|||||||
|
|
||||||
@Benchmark
|
@Benchmark
|
||||||
fun kmPower(blackhole: Blackhole) = BigIntField {
|
fun kmPower(blackhole: Blackhole) = BigIntField {
|
||||||
blackhole.consume(kmNumber.pow(bigExponent.toULong()))
|
blackhole.consume(kmNumber.pow(bigExponent.toUInt()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Benchmark
|
@Benchmark
|
||||||
|
@ -253,26 +253,6 @@ public interface Ring<T> : Group<T>, RingOperations<T> {
|
|||||||
public val one: T
|
public val one: T
|
||||||
}
|
}
|
||||||
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public fun <T> Ring<T>.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 <T> Ring<T>.pow(base: T, exponent: UInt): T = pow(base, exponent.toULong())
|
|
||||||
|
|
||||||
private fun <T> Ring<T>.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
|
* 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.
|
* [add] and [multiply]; binary operation [divide] as multiplication of left operand by reciprocal of right one.
|
||||||
|
@ -98,11 +98,7 @@ public class BigInt internal constructor(
|
|||||||
else -> BigInt(sign, multiplyMagnitudeByUInt(magnitude, other))
|
else -> BigInt(sign, multiplyMagnitudeByUInt(magnitude, other))
|
||||||
}
|
}
|
||||||
|
|
||||||
@UnstableKMathAPI
|
public fun pow(exponent: UInt): BigInt = BigIntField.power(this@BigInt, exponent)
|
||||||
public fun pow(other: ULong): BigInt = BigIntField { pow(this@BigInt, other) }
|
|
||||||
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public fun pow(other: UInt): BigInt = BigIntField { pow(this@BigInt, other) }
|
|
||||||
|
|
||||||
public operator fun times(other: Int): BigInt = when {
|
public operator fun times(other: Int): BigInt = when {
|
||||||
other > 0 -> this * kotlin.math.abs(other).toUInt()
|
other > 0 -> this * kotlin.math.abs(other).toUInt()
|
||||||
|
@ -97,34 +97,45 @@ public fun <T, S> Sequence<T>.averageWith(space: S): T where S : Ring<T>, S : Sc
|
|||||||
//TODO optimized power operation
|
//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.
|
* @receiver the algebra to provide multiplication.
|
||||||
* @param arg the base.
|
* @param arg the base.
|
||||||
* @param power the exponent.
|
* @param power the exponent.
|
||||||
* @return the base raised to the power.
|
* @return the base raised to the power.
|
||||||
|
* @author Evgeniy Zhelenskiy
|
||||||
*/
|
*/
|
||||||
public fun <T> Ring<T>.power(arg: T, power: Int): T {
|
public fun <T> Ring<T>.power(arg: T, power: UInt): T = when {
|
||||||
require(power >= 0) { "The power can't be negative." }
|
this == zero && power > 0U -> zero
|
||||||
require(power != 0 || arg != zero) { "The $zero raised to $power is not defined." }
|
this == one -> arg
|
||||||
if (power == 0) return one
|
this == -one -> powWithoutOptimization(arg, power % 2U)
|
||||||
var res = arg
|
else -> powWithoutOptimization(arg, power)
|
||||||
repeat(power - 1) { res *= arg }
|
|
||||||
return res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun <T> Ring<T>.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].
|
* Raises [arg] to the integer power [power].
|
||||||
*
|
*
|
||||||
|
* Special case: 0 ^ 0 is 1.
|
||||||
|
*
|
||||||
* @receiver the algebra to provide multiplication and division.
|
* @receiver the algebra to provide multiplication and division.
|
||||||
* @param arg the base.
|
* @param arg the base.
|
||||||
* @param power the exponent.
|
* @param power the exponent.
|
||||||
* @return the base raised to the power.
|
* @return the base raised to the power.
|
||||||
* @author Iaroslav Postovalov
|
* @author Iaroslav Postovalov, Evgeniy Zhelenskiy
|
||||||
*/
|
*/
|
||||||
public fun <T> Field<T>.power(arg: T, power: Int): T {
|
public fun <T> Field<T>.power(arg: T, power: Int): T = when {
|
||||||
require(power != 0 || arg != zero) { "The $zero raised to $power is not defined." }
|
power < 0 -> one / (this as Ring<T>).power(arg, if (power == Int.MIN_VALUE) Int.MAX_VALUE.toUInt().inc() else (-power).toUInt())
|
||||||
if (power == 0) return one
|
else -> (this as Ring<T>).power(arg, power.toUInt())
|
||||||
if (power < 0) return one / (this as Ring<T>).power(arg, -power)
|
|
||||||
return (this as Ring<T>).power(arg, power)
|
|
||||||
}
|
}
|
||||||
|
@ -27,18 +27,12 @@ internal class BigIntAlgebraTest {
|
|||||||
@Test
|
@Test
|
||||||
fun testKBigIntegerRingPow() {
|
fun testKBigIntegerRingPow() {
|
||||||
for (num in -5..5)
|
for (num in -5..5)
|
||||||
for (exponent in 0U..10U) {
|
for (exponent in 0U..10U)
|
||||||
assertEquals(
|
|
||||||
num.toDouble().pow(exponent.toInt()).toLong().toBigInt(),
|
|
||||||
num.toBigInt().pow(exponent.toULong()),
|
|
||||||
"$num ^ $exponent"
|
|
||||||
)
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
num.toDouble().pow(exponent.toInt()).toLong().toBigInt(),
|
num.toDouble().pow(exponent.toInt()).toLong().toBigInt(),
|
||||||
num.toBigInt().pow(exponent),
|
num.toBigInt().pow(exponent),
|
||||||
"$num ^ $exponent"
|
"$num ^ $exponent"
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -18,4 +18,16 @@ internal class DoubleFieldTest {
|
|||||||
val sqrt = DoubleField { sqrt(25 * one) }
|
val sqrt = DoubleField { sqrt(25 * one) }
|
||||||
assertEquals(5.0, sqrt)
|
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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user