ionspin / kotlin-multiplatform-bignum

A Kotlin multiplatform library for arbitrary precision arithmetics
Apache License 2.0
338 stars 40 forks source link

Get direct access to magnitude #286

Open hgourvest opened 2 months ago

hgourvest commented 2 months ago

Is your feature request related to a problem? Please describe. I need to convert a biginteger value to another format to be stored in a database that stores a 128 integer in two 64-bit integers. I've written two conversion functions, but as this code will be run intensively, I don't like having to create unnecessary objects using the "getBackingArrayCopy" function.

Describe the solution you'd like I would like to have direct access to the "magnitude" value.

Describe alternatives you've considered If you think that these conversion functions can be integrated into your library, I can also provide you with the conversion functions. The database is called Firebird SQL and uses "libtommath" to manipulate 128bit signed integers without needing to manage the sign separately.

hgourvest commented 2 months ago

I've run extensive tests on these functions, and it's reliable.

inline fun fromBigInteger(value: BigInteger, block: (a:Long, b:Long) -> Unit) {
    val words = value.getBackingArrayCopy().toLongArray()
    val v0: Long = words[0]
    val v1: Long = if (words.size > 1) words[1] else 0L
    when (value.getSign()) {
        Sign.POSITIVE -> {
            if (v1 == 0L) {
                block(v0, 0L)
            } else {
                if (v1 % 2 == 0L) {
                    block(v0, v1 / 2)
                } else {
                    block(Long.MIN_VALUE + v0, (v1 - 1) / 2)
                }
            }
        }
        Sign.NEGATIVE -> {
            if (v1 == 0L) {
                block(-v0, -1L)
            } else {
                if (v0 == 0L) {
                    if (v1 % 2 == 0L)
                        block(0L, -(v1 / 2))
                    else
                        block(Long.MIN_VALUE, -(v1 + 1) / 2)
                } else {
                    if (v1 % 2 == 0L)
                        block(-v0, -(v1 / 2) - 1)
                    else
                        block(Long.MAX_VALUE - v0 + 1, -(v1 + 1) / 2)
                }
            }
        }
        Sign.ZERO -> ZERO
    }
}

fun toBigInteger(a: Long, b: Long): BigInteger {
    val sign = when {
        b == 0L -> if (a == 0L) Sign.ZERO else Sign.POSITIVE
        b > 0L -> Sign.POSITIVE
        else -> Sign.NEGATIVE
    }

    return when (sign) {
        Sign.POSITIVE ->
            createFromWordArray((if (a < 0)
                longArrayOf(a - Long.MIN_VALUE, b * 2 + 1)
            else if (b == 0L)
                longArrayOf(a)
            else
                longArrayOf(a, b * 2)).toULongArray(), sign)
        Sign.NEGATIVE ->
            createFromWordArray((when {
                a == 0L -> longArrayOf(0L, -b * 2)
                a == Long.MIN_VALUE -> longArrayOf(0L, (-b - 1) * 2 + 1)
                a < 0L -> {
                    val v2 = (-b - 1)
                    if (v2 == 0L)
                        longArrayOf(-a)
                    else
                        longArrayOf(-a, v2 * 2)
                }
                else ->
                    longArrayOf(Long.MAX_VALUE - a + 1, (-b - 1) * 2 + 1)
            }).toULongArray(), sign)
        Sign.ZERO -> BigInteger.ZERO
    }
}
ionspin commented 2 months ago

Hi @hgourvest ,

I'll be traveling next two weeks so I probably won't have time to tackle this until I get back, but I'm sure we can figure something out, I feel like both options are possible, it's just deciding which approach is better.

hgourvest commented 2 months ago

Thanks, nothing urge.

The "inline" function saves an Object because of the Lambda function, but does not allow access to the class's private elements. So if you integrate this function in "inline" mode, you'll still need to make the "magnitude" variable public.

hgourvest commented 1 month ago

I just released the FirebirdClient library, feel free to get what you need. https://github.com/hgourvest/Firebird-Client-KMP/blob/main/library-ext/src/commonMain/kotlin/com/progdigy/fbclient/ext/BigInteger.kt

ionspin commented 1 month ago

Thanks and congrats on the release!