-
-
Notifications
You must be signed in to change notification settings - Fork 393
/
Copy pathShallowBinaryExponentiation.kt
87 lines (78 loc) · 2.67 KB
/
ShallowBinaryExponentiation.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package math
import java.math.BigInteger
const val ERROR_MESSAGE_FOR_NEGATIVE_INDICES = "Binary Exponentiation cannot be used for negative indices"
internal inline fun Long.toBigInteger(): BigInteger = BigInteger.valueOf(this)
internal fun throwIllegalArgumentExceptionForNegativeIndices(): Nothing =
throw IllegalArgumentException(ERROR_MESSAGE_FOR_NEGATIVE_INDICES)
/**
* Calculates a.pow(b) by using the algorithm of binary exponentiation,
* @param that allows for arbitrarily large indices.
* However, sufficiently large indices (as a rule of thumb : anything above 2 ^ 1000 as index) can cause a stack-overflow
* To prevent stack overflow and use arbitrarily large indices, use deep recursive binary exponentiation
* */
infix fun BigInteger.bpow(that: BigInteger): BigInteger {
if (that < BigInteger.ZERO) {
throwIllegalArgumentExceptionForNegativeIndices()
}
if (that == BigInteger.ZERO) {
return BigInteger.ONE
}
val toSquare = this.bpow(that / 2.toBigInteger())
return if (that % 2.toBigInteger() == BigInteger.ZERO) {
toSquare * toSquare
} else {
this * toSquare * toSquare
}
}
/**
* Calculates a.pow(b) by using the algorithm of binary exponentiation,
* where a is the base and b is the index.
* If b is odd, a.pow(b) is written as a * (a.pow(b / 2)).
* If b is even, a.pow(b) is written as (a.pow(b / 2)).
* We compute (a.pow(b / 2)) recursively.
* Time Complexity : O(log(n)).
* Space Complexity : O(1).
* @see Long.bpow
* @see BigInteger.bpow
* @receiver the base of exponentiation
* @param that : the index of exponentiation
*/
infix fun Int.bpow(that: Int): Int {
if (that < 0) {
throwIllegalArgumentExceptionForNegativeIndices()
}
// a.pow(0) = 1
if (that == 0) {
return 1
}
val toSquare = this.bpow(that / 2)
return if (that % 2 == 0) {
toSquare * toSquare
} else {
this * toSquare * toSquare
}
}
/**
* Calculates a.pow(b) by using the algorithm of binary exponentiation
* Note that neither the [Int.bpow] nor [Long.bpow] are overflow-proof,
* they use native ints (32-bit signed integers) and longs (64-bit signed integers).
* To use overflow-proof exponentiation, use [BigInteger.bpow]
* @see Int.bpow(that)
* @see BigInteger.bpow
* @receiver the base of exponentiation
* @param that : the index of exponentiation
* */
infix fun Long.bpow(that: Long): Long {
if (that < 0L) {
throwIllegalArgumentExceptionForNegativeIndices()
}
if (that == 0L) {
return 1L
}
val toSquare = this.bpow(that / 2)
return if (that % 2L == 0L) {
toSquare * toSquare
} else {
this * toSquare * toSquare
}
}