Skip to content

Commit

Permalink
working implementation of BigInteger/BigDecimal for Kotlin/Native
Browse files Browse the repository at this point in the history
  • Loading branch information
ianbotsf committed Dec 19, 2024
1 parent 66c7e83 commit ca1c1f1
Show file tree
Hide file tree
Showing 7 changed files with 270 additions and 66 deletions.
21 changes: 21 additions & 0 deletions runtime/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,27 @@ subprojects {
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile> {
compilerOptions {
freeCompilerArgs.add("-Xexpect-actual-classes")

// FIXME When building LinuxX64 on AL2 the linker inclues a bunch of dynamic links to unavailable versions
// of zlib. The below workaround forces the linker to statically link zlib but it's a hack because the
// linker will still dynamically link zlib (although the executable will no longer fail at runtime due to
// link resolution failures). The correct solution for this is probably containerized builds similar to
// what we do in aws-crt-kotlin. The following compiler args were helpful in debugging this issue:
// * Enable verbose compiler output : -verbose
// * Increase verbosity during the compiler's linker phase : -Xverbose-phases=Linker
// * Enable verbose linker output from gold : -linker-option --verbose
if (target.contains("linux", ignoreCase = true)) {
freeCompilerArgs.addAll(
listOf(
"-linker-option", // The subsequent argument is for the linker
"-Bstatic", // Enable static linking for the libraries that follow
"-linker-option", // The subsequent argument is for the linker
"-lz", // Link zlib statically (because of -Bstatic above)
"-linker-option", // The subsequent argument is for the linker
"-Bdynamic", // Restore dynamic linking, which is the default
),
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,94 @@ package aws.smithy.kotlin.runtime.content
public expect class BigDecimal(value: String) :
Number,
Comparable<BigDecimal> {

/**
* Create an instance of [BigDecimal] from a mantissa and exponent.
* @param mantissa a [BigInteger] representing the mantissa of this big decimal
* @param exponent an [Int] representing the exponent of this big decimal
* @param mantissa a [BigInteger] representing the [significant digits](https://en.wikipedia.org/wiki/Significand)
* of this decimal value
* @param exponent an [Int] representing the exponent of this decimal value
*/
public constructor(mantissa: BigInteger, exponent: Int)

/**
* The mantissa of this decimal number
* The [significant digits](https://en.wikipedia.org/wiki/Significand) of this decimal value
*/
public val mantissa: BigInteger

/**
* The exponent of this decimal number.
* If zero or positive, this represents the number of digits to the right of the decimal point.
* If negative, the mantissa is multiplied by ten to the power of the negation of the scale.
* The exponent of this decimal number. If zero or positive, this represents the number of digits to the right of
* the decimal point. If negative, the [mantissa] is multiplied by ten to the power of the negation of the scale.
*/
public val exponent: Int

/**
* Converts this value to a [Byte], which may involve rounding or truncation
*/
override fun toByte(): Byte

/**
* Converts this value to a [Double], which may involve rounding or truncation
*/
override fun toDouble(): Double

/**
* Converts this value to a [Float], which may involve rounding or truncation
*/
override fun toFloat(): Float

/**
* Converts this value to a [Short], which may involve rounding or truncation
*/
override fun toShort(): Short

/**
* Converts this value to an [Int], which may involve rounding or truncation
*/
override fun toInt(): Int

/**
* Converts this value to a [Long], which may involve rounding or truncation
*/
override fun toLong(): Long

/**
* Returns the decimal (i.e., radix-10) string representation of this value in long-form (i.e., _not_ scientific)
* notation
*/
public fun toPlainString(): String

/**
* Returns the decimal (i.e., radix-10) string representation of this value using scientific notation if an exponent
* is needed
*/
override fun toString(): String

/**
* Returns a hash code for this value
*/
override fun hashCode(): Int

/**
* Checks if this value is equal to the given object
* @param other The other value to compare against
*/
override fun equals(other: Any?): Boolean

/**
* Returns the sum of this value and the given value
* @param other The other value to add (i.e., the addend)
*/
public operator fun plus(other: BigDecimal): BigDecimal

/**
* Returns the difference of this value and the given value
* @param other The value to subtract (i.e., the subtrahend)
*/
public operator fun minus(other: BigDecimal): BigDecimal

/**
* Compare this value to the given value for in/equality
* @param other The value to compare against
*/
public override operator fun compareTo(other: BigDecimal): Int
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,86 @@ package aws.smithy.kotlin.runtime.content
public expect class BigInteger(value: String) :
Number,
Comparable<BigInteger> {

/**
* Create an instance of [BigInteger] from a [ByteArray]
* @param bytes ByteArray representing the large integer
*/
public constructor(bytes: ByteArray)

/**
* Converts this value to a [Byte], which may involve rounding or truncation
*/
override fun toByte(): Byte

/**
* Converts this value to a [Long], which may involve rounding or truncation
*/
override fun toLong(): Long

/**
* Converts this value to a [Short], which may involve rounding or truncation
*/
override fun toShort(): Short

/**
* Converts this value to an [Int], which may involve rounding or truncation
*/
override fun toInt(): Int

/**
* Converts this value to a [Float], which may involve rounding or truncation
*/
override fun toFloat(): Float

/**
* Converts this value to a [Double], which may involve rounding or truncation
*/
override fun toDouble(): Double

/**
* Returns the decimal (i.e., radix-10) string representation of this value
*/
override fun toString(): String

/**
* Returns a string representation of this value in the given radix
* @param radix The [numerical base](https://en.wikipedia.org/wiki/Radix) in which to represent the value
*/
public fun toString(radix: Int = 10): String

/**
* Returns a hash code for this value
*/
override fun hashCode(): Int

/**
* Checks if this value is equal to the given object
* @param other The other value to compare against
*/
override fun equals(other: Any?): Boolean

/**
* Returns the sum of this value and the given value
* @param other The other value to add (i.e., the addend)
*/
public operator fun plus(other: BigInteger): BigInteger

/**
* Returns the difference of this value and the given value
* @param other The value to subtract (i.e., the subtrahend)
*/
public operator fun minus(other: BigInteger): BigInteger

/**
* Returns the [two's complement](https://en.wikipedia.org/wiki/Two%27s_complement) binary representation of this
* value
*/
public fun toByteArray(): ByteArray

/**
* Compare this value to the given value for in/equality
* @param other The value to compare against
*/
public override operator fun compareTo(other: BigInteger): Int
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,52 @@
*/
package aws.smithy.kotlin.runtime.content

public actual class BigDecimal actual constructor(public val value: String) :
import java.math.BigDecimal as JvmBigDecimal

public actual class BigDecimal private constructor(private val delegate: JvmBigDecimal) :
Number(),
Comparable<BigDecimal> {
private val delegate = java.math.BigDecimal(value)

public actual constructor(mantissa: BigInteger, exponent: Int) : this(
java.math.BigDecimal(
java.math.BigInteger(mantissa.toString()),
exponent,
).toPlainString(),
)
private companion object {
/**
* Returns a new or existing [BigDecimal] wrapper for the given delegate [value]
* @param value The delegate value to wrap
* @param left A candidate wrapper which may already contain [value]
* @param right A candidate wrapper which may already contain [value]
*/
fun coalesceOrCreate(value: JvmBigDecimal, left: BigDecimal, right: BigDecimal): BigDecimal = when (value) {
left.delegate -> left
right.delegate -> right
else -> BigDecimal(value)
}
}

public actual constructor(value: String) : this(JvmBigDecimal(value))
public actual constructor(mantissa: BigInteger, exponent: Int) : this(JvmBigDecimal(mantissa.delegate, exponent))

public actual fun toPlainString(): String = delegate.toPlainString()
actual override fun toByte(): Byte = delegate.toByte()
actual override fun toDouble(): Double = delegate.toDouble()
actual override fun toFloat(): Float = delegate.toFloat()
actual override fun toInt(): Int = delegate.toInt()
actual override fun toLong(): Long = delegate.toLong()
actual override fun toShort(): Short = delegate.toShort()
public actual override fun toString(): String = delegate.toString()
public actual override fun toByte(): Byte = delegate.toByte()
public actual override fun toDouble(): Double = delegate.toDouble()
public actual override fun toFloat(): Float = delegate.toFloat()
public actual override fun toInt(): Int = delegate.toInt()
public actual override fun toLong(): Long = delegate.toLong()
public actual override fun toShort(): Short = delegate.toShort()

actual override fun equals(other: Any?): Boolean = other is BigDecimal && delegate == other.delegate
public actual override fun equals(other: Any?): Boolean = other is BigDecimal && delegate == other.delegate
public actual override fun hashCode(): Int = 31 + delegate.hashCode()

public actual val mantissa: BigInteger
get() = BigInteger(delegate.unscaledValue().toString())
get() = BigInteger(delegate.unscaledValue())

public actual val exponent: Int
get() = delegate.scale()

public actual operator fun plus(other: BigDecimal): BigDecimal =
coalesceOrCreate(delegate + other.delegate, this, other)

public actual operator fun minus(other: BigDecimal): BigDecimal =
coalesceOrCreate(delegate - other.delegate, this, other)

actual override fun compareTo(other: BigDecimal): Int = delegate.compareTo(other.delegate)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,47 @@
*/
package aws.smithy.kotlin.runtime.content

public actual class BigInteger actual constructor(public val value: String) :
import java.math.BigInteger as JvmBigInteger

public actual class BigInteger internal constructor(internal val delegate: JvmBigInteger) :
Number(),
Comparable<BigInteger> {
private val delegate = java.math.BigInteger(value)

public actual constructor(bytes: ByteArray) : this(java.math.BigInteger(bytes).toString())
private companion object {
/**
* Returns a new or existing [BigInteger] wrapper for the given delegate [value]
* @param value The delegate value to wrap
* @param left A candidate wrapper which may already contain [value]
* @param right A candidate wrapper which may already contain [value]
*/
fun coalesceOrCreate(value: JvmBigInteger, left: BigInteger, right: BigInteger): BigInteger = when (value) {
left.delegate -> left
right.delegate -> right
else -> BigInteger(value)
}
}

public actual constructor(value: String) : this(JvmBigInteger(value))
public actual constructor(bytes: ByteArray) : this(JvmBigInteger(bytes))

public actual override fun toByte(): Byte = delegate.toByte()
public actual override fun toLong(): Long = delegate.toLong()
public actual override fun toShort(): Short = delegate.toShort()
public actual override fun toInt(): Int = delegate.toInt()
public actual override fun toFloat(): Float = delegate.toFloat()
public actual override fun toDouble(): Double = delegate.toDouble()
public actual override fun toString(): String = delegate.toString()
public actual override fun hashCode(): Int = delegate.hashCode()
public actual override fun toString(): String = toString(10)
public actual fun toString(radix: Int): String = delegate.toString(radix)

public actual override fun hashCode(): Int = 17 + delegate.hashCode()
public actual override fun equals(other: Any?): Boolean = other is BigInteger && delegate == other.delegate

public actual operator fun plus(other: BigInteger): BigInteger = BigInteger((delegate + other.delegate).toString())
public actual operator fun minus(other: BigInteger): BigInteger = BigInteger((delegate - other.delegate).toString())
public actual operator fun plus(other: BigInteger): BigInteger =
coalesceOrCreate(delegate + other.delegate, this, other)

public actual operator fun minus(other: BigInteger): BigInteger =
coalesceOrCreate(delegate - other.delegate, this, other)

public actual override operator fun compareTo(other: BigInteger): Int = delegate.compareTo(other.delegate)
public actual fun toByteArray(): ByteArray = delegate.toByteArray()
}
Loading

0 comments on commit ca1c1f1

Please sign in to comment.