Files
encoding.kt/src/commonMain/kotlin/com/infendro/encoding/util/BigInt.kt

74 lines
2.2 KiB
Kotlin

package com.infendro.encoding.util
internal class BigInt private constructor(
val words: IntArray,
) {
init {
require(words.isNotEmpty())
}
fun isZero(): Boolean = words.all { it == 0 }
fun timesAdd(multiplier: Int, addend: Int): BigInt {
val product = IntArray(words.size)
var carry = addend
for (i in words.indices) {
val current = (words[i].toLong() and 0xFFFFFFFFL) * multiplier + carry
product[i] = current.toInt()
carry = (current ushr 32).toInt()
}
when {
carry > 0 -> {
val expanded = product.copyOf(product.size + 1)
expanded[product.size] = carry
return BigInt(expanded)
}
else -> return BigInt(product)
}
}
fun divRem(divisor: Int): Pair<BigInt, Int> {
val quotient = IntArray(words.size)
var remainder = 0
for (i in words.indices.reversed()) {
val current = (remainder.toLong() shl 32) or (words[i].toLong() and 0xFFFFFFFFL)
quotient[i] = (current / divisor.toLong()).toInt()
remainder = (current % divisor.toLong()).toInt()
}
when {
quotient.size > 1 && quotient.last() == 0 -> {
val contracted = quotient.copyOfRange(0, quotient.size - 1)
return Pair(BigInt(contracted), remainder)
}
else -> return Pair(BigInt(quotient), remainder)
}
}
fun toByteArray(): ByteArray {
val bytes = ByteArray((words.size * 4) - words.last().countLeadingZeroBytes())
for (i in bytes.indices) {
val (wi, bi) = (bytes.lastIndex - i).divRem(4)
bytes[i] = (words[wi] ushr (bi * 8)).toByte()
}
return bytes
}
companion object {
val zero: BigInt
get() = BigInt(intArrayOf(0))
fun from(bytes: ByteArray): BigInt {
val words = IntArray(bytes.size.ceilDiv(4))
for (i in bytes.indices) {
val (wi, bi) = (bytes.lastIndex - i).divRem(4)
words[wi] = words[wi] or ((bytes[i].toInt() and 0xFF) shl (bi * 8))
}
return BigInt(words)
}
}
}