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 { 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.divCeil(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) } } }