Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restored kbignum and luak #517

Merged
merged 1 commit into from
Jul 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions kbignum/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[*]
charset=utf-8
end_of_line=lf
insert_final_newline=true
indent_style=space
indent_size=4

[*.json]
indent_size=2

[*.yml]
indent_size = 2


1 change: 1 addition & 0 deletions kbignum/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto eol=lf
10 changes: 10 additions & 0 deletions kbignum/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/.idea
/.gradle
/build
/out
/classes
/web
*.ipr
*.iws
*.iml
/local.properties
21 changes: 21 additions & 0 deletions kbignum/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2017 Carlos Ballesteros Velasco

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
11 changes: 11 additions & 0 deletions kbignum/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<h2 align="center">KBigNum</h2>

<!-- BADGES -->
<p align="center">
<a href="https://github.com/korlibs/kbignum/actions"><img alt="Build Status" src="https://github.com/korlibs/kbignum/workflows/CI/badge.svg" /></a>
<a href="https://search.maven.org/artifact/com.soywiz.korlibs.kbignum/kbignum"><img alt="Maven Central" src="https://img.shields.io/maven-central/v/com.soywiz.korlibs.kbignum/kbignum"></a>
<a href="https://discord.korge.org/"><img alt="Discord" src="https://img.shields.io/discord/728582275884908604?logo=discord" /></a>
</p>
<!-- /BADGES -->

### Full Documentation: <https://korlibs.soywiz.com/kbignum/>
13 changes: 13 additions & 0 deletions kbignum/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
description = "Big Integers and decimals in Kotlin Common"

project.ext.props = [
"project.scm.url" : "https://github.com/korlibs/korge/main/kbignum",
"project.license.name" : "MIT License",
"project.license.url" : "https://raw.githubusercontent.com/korlibs/korge/main/kbignum/LICENSE",
"project.author.id" : "soywiz",
"project.author.name" : "Carlos Ballesteros Velasco",
"project.author.email" : "soywiz@gmail.com",
]

dependencies {
}
13 changes: 13 additions & 0 deletions kbignum/src/commonMain/kotlin/com/soywiz/kbignum/BigExt.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.soywiz.kbignum

// Big Integer
val Long.bi get() = BigInt(this)
val Int.bi get() = BigInt(this)
val String.bi get() = BigInt(this)
fun String.bi(radix: Int) = BigInt(this, radix)

// Big Number
val Double.bn get() = BigNum("$this")
val Long.bn get() = BigNum(this.bi, 0)
val Int.bn get() = BigNum(this.bi, 0)
val String.bn get() = BigNum(this)
139 changes: 139 additions & 0 deletions kbignum/src/commonMain/kotlin/com/soywiz/kbignum/BigInt.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package com.soywiz.kbignum

import com.soywiz.kbignum.ranges.*

interface BigInt : Comparable<BigInt>, BigIntConstructor {
companion object {
val usesNativeImplementation get() = BigInt(0) !is CommonBigInt

val ZERO = BigInt("0")
val MINUS_ONE = BigInt("-1")
val ONE = BigInt("1")
val TWO = BigInt("2")
val TEN = BigInt("10")
val SMALL = BigInt(UINT16_MASK)
}

// Checks
val signum: Int
val isZero get() = signum == 0
val isNotZero get() = signum != 0
val isNegative get() = signum < 0
val isPositive get() = signum > 0
val isNegativeOrZero get() = signum <= 0
val isPositiveOrZero get() = signum >= 0

// Unary
operator fun unaryMinus(): BigInt
fun inv(): BigInt

// Binary
infix fun pow(exponent: BigInt): BigInt
infix fun pow(exponent: Int): BigInt

infix fun and(other: BigInt): BigInt
infix fun or(other: BigInt): BigInt
infix fun xor(other: BigInt): BigInt

infix fun shl(count: Int): BigInt
infix fun shr(count: Int): BigInt

operator fun plus(other: BigInt): BigInt
operator fun minus(other: BigInt): BigInt
operator fun times(other: BigInt): BigInt
operator fun div(other: BigInt): BigInt
operator fun rem(other: BigInt): BigInt

// Conversion
fun toInt(): Int
fun toString(radix: Int): String

// Extra
fun square(): BigInt = this * this
operator fun unaryPlus(): BigInt = this
fun abs(): BigInt = if (isNegative) -this else this
operator fun plus(other: Int): BigInt = this + create(other)
operator fun minus(other: Int): BigInt = this - create(other)
operator fun times(other: Int): BigInt = this * create(other)
operator fun times(other: Long): BigInt = this * create(other)
operator fun div(other: Int): BigInt = this / create(other)
operator fun rem(other: Int): BigInt = this % create(other)
operator fun rangeTo(that: BigInt): BigIntRange = BigIntRange(start = this, endInclusive = that)
}

interface BigIntCompanion : BigIntConstructor {
operator fun invoke(value: Int) = create(value)
operator fun invoke(value: Long) = create(value)
operator fun invoke(value: String) = create(value)
operator fun invoke(value: String, radix: Int) = create(value, radix)
}

interface BigIntConstructor {
fun create(value: Int): BigInt
//fun create(value: Long): BigInt = create("$value", 10) // @TODO: Kotlin.JS BUG
fun create(value: Long): BigInt {
if (value.toInt().toLong() == value) return create(value.toInt())
return create(value.toString(10), 10)
}
// General
fun create(value: String, radix: Int): BigInt {
if (value.isEmpty()) throw BigIntInvalidFormatException("Zero length BigInteger")
if (value.startsWith('-')) return -create(value.substring(1), radix)
if (value == "0") return create(0)
//val wordsRequired = ceil((value.length * log2(radix.toDouble())) / 16.0).toInt()
//val out = UInt16ArrayZeroPad(IntArray(wordsRequired))
var out = create(0)

//for (c in value) {
// out *= radix
// out += digit(c, radix)
//}

var sum = 0
var mul = 1

for (n in 0 until value.length) {
val last = n == value.length - 1
val c = value[n]
val d = digit(c, radix)

//UnsignedBigInt.inplaceSmallMulAdd(out, radix, d)
sum *= radix
sum += d
mul *= radix
//if (last || mul * radix > 0x7FFF) {
//if (last || mul * radix >= 0x1FFFFFFF) {
if (last || mul * radix >= 0x1FFFFFF) {
out *= mul
out += sum
sum = 0
mul = 1
}
}
return out
}

fun create(value: String): BigInt {
if (value.startsWith("-")) return -create(value.substring(1))
return parseWithNumberPrefix(value) { sub, radix -> create(sub, radix) }
}
}

internal fun validateRadix(value: String, radix: Int) {
for (c in value) digit(c, radix)
}

expect val BigIntNativeFactory: BigIntConstructor

fun BigInt(value: Int): BigInt = BigIntNativeFactory.create(value)
fun BigInt(value: Long): BigInt = BigIntNativeFactory.create(value)
fun BigInt(value: String, radix: Int): BigInt = BigIntNativeFactory.create(value, radix)
fun BigInt(value: String): BigInt = BigIntNativeFactory.create(value)

internal fun <T> parseWithNumberPrefix(str: String, gen: (sub: String, radix: Int) -> T): T = when {
str.startsWith("0x") -> gen(str.substring(2), 16)
str.startsWith("0o") -> gen(str.substring(2), 8)
str.startsWith("0b") -> gen(str.substring(2), 2)
else -> gen(str, 10)
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.soywiz.kbignum

open class BigIntException(message: String) : Throwable(message)
open class BigIntInvalidFormatException(message: String) : BigIntException(message)
open class BigIntDivisionByZeroException(message: String) : BigIntException(message)
open class BigIntOverflowException(message: String) : BigIntException(message)
open class BigIntInvalidOperationException(message: String) : BigIntException(message)
121 changes: 121 additions & 0 deletions kbignum/src/commonMain/kotlin/com/soywiz/kbignum/BigNum.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package com.soywiz.kbignum

import com.soywiz.kbignum.ranges.*
import kotlin.math.*

class BigNum(val int: BigInt, val scale: Int) : Comparable<BigNum> {
init {
//println("BigNum($int, $scale) == $this")
}

companion object {
val ZERO = BigNum(BigInt(0), 0)
val ONE = BigNum(BigInt(1), 0)
val TWO = BigNum(BigInt(2), 0)

operator fun invoke(str: String): BigNum {
val str = str.lowercase()
//val ss = if (str.contains('.')) str.trimEnd('0') else str
val exponentPartStr = str.substringAfter('e', "").takeIf { it.isNotEmpty() }
val ss = str.substringBefore('e')
val point = ss.indexOf('.')
val strBase = ss.replace(".", "")
val exponent = exponentPartStr?.toInt() ?: 0
val int = BigInt(strBase)
return if (point < 0) {
BigNum(int, -exponent)
} else {
BigNum(int, ss.length - point - 1 - exponent)
}
}
}

fun convertToScale(otherScale: Int): BigNum = when {
this.scale == otherScale -> this
otherScale > this.scale -> BigNum(int * (10.bi pow (otherScale - this.scale)), otherScale)
else -> BigNum(int / (10.bi pow (this.scale - otherScale)), otherScale)
}

operator fun plus(other: BigNum): BigNum = binary(other, BigInt::plus)
operator fun minus(other: BigNum): BigNum = binary(other, BigInt::minus)
operator fun times(other: BigNum): BigNum =
BigNum(this.int * other.int, this.scale + other.scale)

//operator fun div(other: BigNum): BigNum = div(other, other.int.significantBits / 2)
operator fun div(other: BigNum): BigNum = div(other, 0)

fun div(other: BigNum, precision: Int): BigNum {
val scale = (10.bi pow (other.scale + precision))
val li = this.int * scale
val ri = other.int
val res = li / ri
return BigNum(res, this.scale) * BigNum(1.bi, precision)
}

infix fun pow(other: Int) = pow(other, 32)

fun pow(exponent: Int, precision: Int): BigNum {
//if (exponent < 0) return ONE.div(this.pow(-exponent, precision), precision)
if (exponent < 0) return ONE.div(this.pow(-exponent, precision), 0)
var result = ONE
var base = this
var exp = exponent
while (exp != 0) {
if ((exp and 1) != 0) result *= base
exp = exp shr 1
base *= base
}
return result
}

override operator fun compareTo(other: BigNum): Int {
val commonScale = this.commonScale(other)
return this.convertToScale(commonScale).int.compareTo(other.convertToScale(commonScale).int)
}

operator fun rangeTo(that: BigNum): ClosedBigNumRange = ClosedBigNumRange(
start = this,
endInclusive = that
)

override fun hashCode(): Int = int.hashCode() + 3 * scale.hashCode()
override fun equals(other: Any?): Boolean = (other is BigNum) && this.compareTo(other) == 0

private fun commonScale(other: BigNum) = max(this.scale, other.scale)

private inline fun binary(other: BigNum, callback: (l: BigInt, r: BigInt) -> BigInt): BigNum {
val commonScale = this.commonScale(other)
val l = this.convertToScale(commonScale)
val r = other.convertToScale(commonScale)
val li = l.int
val ri = r.int
return BigNum(callback(li, ri), commonScale)
}

override fun toString(): String {
val isNegative = int.isNegative
val out = "${int.abs()}"
val pos = out.length - scale
val negativePart = if (isNegative) "-" else ""
return negativePart + when {
pos <= 0 -> "0." + "0".repeat(-pos) + out
pos >= out.length -> out + "0".repeat(pos - out.length)
else -> (out.substring(0, pos) + "." + out.substring(pos)).trimEnd('.')
}
}

fun toBigInt(): BigInt = convertToScale(0).int
fun toBigIntFloor(): BigInt = toBigInt()
fun toBigIntCeil(): BigInt {
val it = this.toBigInt()
val decimal = decimalPart
return if (decimal.isZero) it else (it + 1.bi)
}
fun toBigIntRound(): BigInt {
val firstDigit = decimalPart / 10.bi.pow(scale - 1)
return if (firstDigit.toInt() >= 5) toBigIntCeil() else toBigIntFloor()
}

val decimalPart: BigInt
get() = int % 10.bi.pow(scale)
}
Loading