diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/Decimal.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/Decimal.kt index 3c1c12f09..1df81a457 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/Decimal.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/Decimal.kt @@ -26,44 +26,28 @@ fun Decimal(value: Byte) = Decimal.valueOf(value) fun Decimal(value: Short) = Decimal.valueOf(value) fun Decimal(value: Int) = Decimal.valueOf(value) fun Decimal(value: Long) = Decimal.valueOf(value) +fun Decimal(value: Float) = Decimal.valueOf(value) +fun Decimal(value: Double) = Decimal.valueOf(value) +fun Decimal(value: BigDecimal) = Decimal.valueOf(value) +fun Decimal(value: BigInteger) = Decimal.valueOf(value) +fun Decimal(value: String) = Decimal.valueOf(value) /** * Fixed point arbitrary precision Decimal value. Values of this class embed [BigInteger] unscaled value, scale * is defined at compile time by [PRECISION]. */ -class Decimal private constructor(val mag: BigInteger, marker: Nothing?) : Number(), Comparable { - constructor(value: BigInteger) : this(value * PRECISION_POW_BI, null) - constructor(value: BigDecimal) : this(value.setScale(PRECISION, RoundingMode.HALF_UP).unscaledValue(), null) - constructor(value: Float) : this(BigDecimal.valueOf(value.toDouble())) - constructor(value: Double) : this(BigDecimal(value)) - constructor(value: String) : this(BigDecimal(value)) - - private var _whole: BigInteger? = null - private var _fractional: BigInteger? = null - - private fun computeWholeFractional(): BigInteger { - val (a, b) = mag.divideAndRemainder(PRECISION_POW_BI) - _whole = a - _fractional = b - return a - } - +sealed class Decimal : Number(), Comparable { /** * Whole part of this Decimal */ - val whole: BigInteger get() { - return _whole ?: computeWholeFractional() - } + abstract val whole: BigInteger /** * Arbitrary fractional part of this Decimal, as [BigInteger] * * Makes sense only when utilized along [PRECISION], [PRECISION_POW], [PRECISION_POW_BI] */ - val fractional: BigInteger get() { - if (_fractional == null) computeWholeFractional() - return _fractional!! - } + abstract val fractional: BigInteger /** * *Signed* normalized (-1,1) fractional part of this Decimal, as [Float] @@ -79,349 +63,50 @@ class Decimal private constructor(val mag: BigInteger, marker: Nothing?) : Numbe return fractional.toDouble() / PRECISION_DOUBLE } - override fun compareTo(other: Decimal): Int { - return mag.compareTo(other.mag) - } + abstract operator fun plus(other: Decimal): Decimal + abstract operator fun minus(other: Decimal): Decimal + abstract operator fun times(other: Decimal): Decimal + abstract operator fun div(other: Decimal): Decimal - override fun toByte(): Byte { - return whole.toByte() - } - - override fun toChar(): Char { - return whole.toChar() - } - - override fun toDouble(): Double { - return mag.toDouble() / PRECISION_DOUBLE - } - - override fun toFloat(): Float { - return mag.toFloat() / PRECISION_FLOAT - } - - override fun toInt(): Int { - if (whole > BI_INT_MAX) { - return Int.MAX_VALUE - } else if (whole < BI_INT_MIN) { - return Int.MIN_VALUE - } else { - return whole.toInt() - } - } - - override fun toLong(): Long { - if (whole > BI_LONG_MAX) { - return Long.MAX_VALUE - } else if (whole < BI_LONG_MIN) { - return Long.MIN_VALUE - } else { - return whole.toLong() - } - } - - override fun toShort(): Short { - return whole.toShort() - } - - operator fun plus(other: Decimal): Decimal { - if (other.mag.signum() == 0) { - return this - } - - return Decimal(mag + other.mag, null) - } - - operator fun minus(other: Decimal): Decimal { - if (other.mag.signum() == 0) { - return this - } - - return Decimal(mag - other.mag, null) - } - - operator fun times(other: Decimal): Decimal { - if (other.mag.signum() == 0) { - return ZERO - } else if (other == ONE) { - return this - } else if (other == MINUS_ONE) { - return Decimal(-mag, null) - } - - val result = mag * other.mag - val (a, b) = result.divideAndRemainder(PRECISION_POW_BI) - - if (b >= PRECISION_POW_BI_HIGH) { - return Decimal(a + BigInteger.ONE, null) - } else { - return Decimal(a, null) - } - } - - operator fun div(other: Decimal): Decimal { - if (other.mag.signum() == 0) { - throw ArithmeticException("Attempt to divide $this by zero") - } else if (other == ONE) { - return this - } else if (other == MINUS_ONE) { - return Decimal(-mag, null) - } - - return Decimal((mag * PRECISION_POW_BI) / other.mag, null) - } - - operator fun rem(other: Decimal): Decimal { - return Decimal(mag % other.mag, null) - } + abstract operator fun rem(other: Decimal): Decimal // Primitive operators - operator fun plus(other: Float): Decimal { - if (other == 0f) { - return this - } else if (other.isNaN()) { - throw ArithmeticException("Attempt to add NaN to $this") - } else if (other.isInfinite()) { - throw ArithmeticException("Attempt to add infinity to $this") - } + abstract operator fun plus(other: Float): Decimal + abstract operator fun minus(other: Float): Decimal + abstract operator fun times(other: Float): Decimal + abstract operator fun div(other: Float): Decimal - return plus(Decimal(other)) - } + abstract operator fun plus(other: Double): Decimal + abstract operator fun minus(other: Double): Decimal + abstract operator fun times(other: Double): Decimal + abstract operator fun div(other: Double): Decimal - operator fun minus(other: Float): Decimal { - if (other == 0f) { - return this - } else if (other.isNaN()) { - throw ArithmeticException("Attempt to subtract NaN from $this") - } else if (other.isInfinite()) { - throw ArithmeticException("Attempt to subtract infinity from $this") - } + abstract operator fun plus(other: Int): Decimal + abstract operator fun minus(other: Int): Decimal + abstract operator fun times(other: Int): Decimal + abstract operator fun div(other: Int): Decimal - return minus(Decimal(other)) - } + abstract operator fun plus(other: Long): Decimal + abstract operator fun minus(other: Long): Decimal + abstract operator fun times(other: Long): Decimal + abstract operator fun div(other: Long): Decimal - operator fun times(other: Float): Decimal { - if (other == 1f) { - return this - } else if (other == 0f || isZero) { - return ZERO - } else if (other == -1f) { - return -this - } else if (other.isNaN()) { - throw ArithmeticException("Attempt to multiply $this by NaN") - } else if (other.isInfinite()) { - throw ArithmeticException("Attempt to multiply $this by infinity") - } - - return times(Decimal(other)) - } - - operator fun div(other: Float): Decimal { - if (other == 0f) { - throw ArithmeticException("Attempt to divide $this by zero") - } else if (other == 1f || isZero) { - return this - } else if (other == -1f) { - return -this - } else if (other.isNaN()) { - throw ArithmeticException("Attempt to divide $this by NaN") - } else if (other.isInfinite()) { - throw ArithmeticException("Attempt to divide $this by infinity") - } - - return div(Decimal(other)) - } - - operator fun plus(other: Double): Decimal { - if (other == 0.0) { - return this - } else if (other.isNaN()) { - throw ArithmeticException("Attempt to add NaN to $this") - } else if (other.isInfinite()) { - throw ArithmeticException("Attempt to add infinity to $this") - } - - return plus(Decimal(other)) - } - - operator fun minus(other: Double): Decimal { - if (other == 0.0) { - return this - } else if (other.isNaN()) { - throw ArithmeticException("Attempt to subtract NaN from $this") - } else if (other.isInfinite()) { - throw ArithmeticException("Attempt to subtract infinity from $this") - } - - return minus(Decimal(other)) - } - - operator fun times(other: Double): Decimal { - if (other == 1.0) { - return this - } else if (other == 0.0 || isZero) { - return ZERO - } else if (other == -1.0) { - return -this - } else if (other.isNaN()) { - throw ArithmeticException("Attempt to multiply $this by NaN") - } else if (other.isInfinite()) { - throw ArithmeticException("Attempt to multiply $this by infinity") - } - - return times(Decimal(other)) - } - - operator fun div(other: Double): Decimal { - if (other == 0.0) { - throw ArithmeticException("Attempt to divide $this by zero") - } else if (other == 1.0 || isZero) { - return this - } else if (other == -1.0) { - return -this - } else if (other.isNaN()) { - throw ArithmeticException("Attempt to divide $this by NaN") - } else if (other.isInfinite()) { - throw ArithmeticException("Attempt to divide $this by infinity") - } - - return div(Decimal(other)) - } - - operator fun plus(other: Int): Decimal { - if (other == 0) { - return this - } - - return plus(valueOf(other)) - } - - operator fun minus(other: Int): Decimal { - if (other == 0) { - return this - } - - return minus(valueOf(other)) - } - - operator fun times(other: Int): Decimal { - if (other == 1 || isZero) { - return this - } else if (other == 0) { - return ZERO - } else if (other == -1) { - return -this - } - - return times(valueOf(other)) - } - - operator fun div(other: Int): Decimal { - if (other == 0) { - throw ArithmeticException("Attempt to divide $this by zero") - } else if (other == 1 || signum() == 0) { - return this - } else if (other == -1) { - return -this - } - - return div(valueOf(other)) - } - - operator fun plus(other: Long): Decimal { - if (other == 0L) { - return this - } - - return plus(valueOf(other)) - } - - operator fun minus(other: Long): Decimal { - if (other == 0L) { - return this - } - - return minus(valueOf(other)) - } - - operator fun times(other: Long): Decimal { - if (other == 1L || isZero) { - return this - } else if (other == 0L) { - return ZERO - } else if (other == -1L) { - return -this - } - - return times(valueOf(other)) - } - - operator fun div(other: Long): Decimal { - if (other == 0L) { - throw ArithmeticException("Attempt to divide $this by zero") - } else if (other == 1L || isZero) { - return this - } else if (other == -1L) { - return -this - } - - return div(valueOf(other)) - } - - operator fun plus(other: BigInteger): Decimal { - if (other == BigInteger.ZERO) { - return this - } - - return plus(Decimal(other)) - } - - operator fun minus(other: BigInteger): Decimal { - if (other == BigInteger.ZERO) { - return this - } - - return minus(Decimal(other)) - } - - operator fun times(other: BigInteger): Decimal { - if (other == BigInteger.ONE) { - return this - } else if (other.signum() == 0 || isZero) { - return ZERO - } else if (other == BI_MINUS_ONE) { - return -this - } - - return times(Decimal(other)) - } - - operator fun div(other: BigInteger): Decimal { - if (other == BigInteger.ZERO) { - throw ArithmeticException("Attempt to divide $this by zero") - } else if (other == BigInteger.ONE || isZero) { - return this - } else if (other == BI_MINUS_ONE) { - return -this - } - - return div(Decimal(other)) - } + abstract operator fun plus(other: BigInteger): Decimal + abstract operator fun minus(other: BigInteger): Decimal + abstract operator fun times(other: BigInteger): Decimal + abstract operator fun div(other: BigInteger): Decimal // /Primitive operators - operator fun unaryMinus(): Decimal { - if (mag == BigInteger.ZERO) { - return this - } - - return Decimal(-mag, null) - } + // "de-virtualize" generic method + abstract override fun compareTo(other: Decimal): Int + abstract operator fun unaryMinus(): Decimal operator fun unaryPlus() = this /** * Sign number of this Decimal, as defined by [BigInteger.signum] */ - fun signum() = mag.signum() + abstract fun signum(): Int /** * Whenever this Decimal is negative (less than zero) @@ -438,6 +123,9 @@ class Decimal private constructor(val mag: BigInteger, marker: Nothing?) : Numbe */ inline val isZero get() = signum() == 0 + abstract val isInfinite: Boolean + abstract val isFinite: Boolean + /** * Alias for [coerceAtLeast] with [ZERO] constant, except this method might * perform faster. @@ -446,7 +134,7 @@ class Decimal private constructor(val mag: BigInteger, marker: Nothing?) : Numbe if (signum() >= 0) return this - return ZERO + return Zero } /** @@ -457,12 +145,10 @@ class Decimal private constructor(val mag: BigInteger, marker: Nothing?) : Numbe if (signum() <= 0) return this - return ZERO + return Zero } - fun toByteArray(): ByteArray { - return mag.toByteArray() - } + abstract fun toByteArray(): ByteArray fun serializeNBT(): Tag { return ByteArrayTag(toByteArray()) @@ -474,110 +160,1201 @@ class Decimal private constructor(val mag: BigInteger, marker: Nothing?) : Numbe val absoluteValue: Decimal get() { - if (isNegative) { - return -this + return if (isNegative) { + -this } else { - return this + this } } /** * Truncates fractional part of this Decimal */ - fun floor(): Decimal { - return Decimal(whole) - } - - override fun equals(other: Any?): Boolean { - return this === other || other is Decimal && mag == other.mag - } - - override fun hashCode(): Int { - return mag.hashCode() - } - - fun toString(decimals: Int): String { - if (decimals == 0) { - return whole.toString() - } - - if (mag.signum() == 0) { - if (decimals < 0) { - return "0.0" - } else { - return "0." + "0".repeat(decimals) - } - } - - var original = mag.toString() - - if (mag.signum() < 0) { - // если у нас отрицательное число - убираем знак минуса - original = original.substring(1) - } - - if (original.length <= PRECISION) { - // если у нас чисто дробное число - дописываем нули в начало - original = "0".repeat(PRECISION - original.length + 1) + original - } - - // теперь у нас беззнаковая строка с нужной длиной - - if (decimals < 0) { - // нам неважно количество знаков после запятой - var result = original.substring(0, original.length - PRECISION) + "." + original.substring(original.length - PRECISION) - - if (mag.signum() < 0) { - result = "-$result" - } - - var pos = result.length - 1 - - while (result[pos] == '0') { - if (result[pos - 1] == '0' || result[pos - 1] != '.') - pos-- - else - break - } - - return result.substring(0, pos + 1) - } else { - // нужно некоторое количество знаков после запятой - val result = original.substring(0, original.length - PRECISION) + "." - var decimalPart = original.substring(original.length - PRECISION) - - if (decimalPart.length < decimals) { - decimalPart += "0".repeat(decimals - decimalPart.length) - } else if (decimalPart.length > decimals) { - decimalPart = decimalPart.substring(0, decimals) - } - - if (mag.signum() < 0) { - return "-$result$decimalPart" - } else { - return "$result$decimalPart" - } - } - } + abstract fun floor(): Decimal + abstract fun toString(decimals: Int): String override fun toString(): String = toString(-1) - fun toBigDecmial(): BigDecimal { - return BigDecimal(mag, PRECISION) - } + abstract fun toBigDecmial(): BigDecimal - fun percentage(divisor: Decimal, zeroing: Boolean = true): Float { - if ((isZero || divisor.isZero) && zeroing) return 0f + fun percentage(divisor: Decimal): Float { + if (isZero || divisor.isZero) return 0f if (this >= divisor) return 1f - - if (this <= FLOAT_MAX_VALUE && divisor <= FLOAT_MAX_VALUE) + else if (this <= FLOAT_MAX_VALUE && divisor <= FLOAT_MAX_VALUE) return (toDouble() / divisor.toDouble()).toFloat() + else if (divisor === PositiveInfinity || divisor === NegativeInfinity) + return 0f return toBigDecmial().divide(divisor.toBigDecmial(), PERCENTAGE_CONTEXT).toFloat() } + private class Regular(val mag: BigInteger, marker: Nothing?) : Decimal() { + constructor(value: BigInteger) : this(value * PRECISION_POW_BI, null) + constructor(value: BigDecimal) : this(value.setScale(PRECISION, RoundingMode.HALF_UP).unscaledValue(), null) + constructor(value: Float) : this(BigDecimal.valueOf(value.toDouble())) + constructor(value: Double) : this(BigDecimal(value)) + constructor(value: String) : this(BigDecimal(value)) + + override val isInfinite: Boolean + get() = false + override val isFinite: Boolean + get() = true + + private var _whole: BigInteger? = null + private var _fractional: BigInteger? = null + + private fun computeWholeFractional(): BigInteger { + val (a, b) = mag.divideAndRemainder(PRECISION_POW_BI) + _whole = a + _fractional = b + return a + } + + override val whole: BigInteger get() { + return _whole ?: computeWholeFractional() + } + + override val fractional: BigInteger get() { + if (_fractional == null) computeWholeFractional() + return _fractional!! + } + + override fun toDouble(): Double { + return mag.toDouble() / PRECISION_DOUBLE + } + + override fun toFloat(): Float { + return mag.toFloat() / PRECISION_FLOAT + } + + override fun toByte(): Byte { + return whole.toByte() + } + + override fun toShort(): Short { + return whole.toShort() + } + + override fun signum(): Int { + return mag.signum() + } + + override fun toInt(): Int { + return if (whole > BI_INT_MAX) { + Int.MAX_VALUE + } else if (whole < BI_INT_MIN) { + Int.MIN_VALUE + } else { + whole.toInt() + } + } + + override fun toLong(): Long { + return if (whole > BI_LONG_MAX) { + Long.MAX_VALUE + } else if (whole < BI_LONG_MIN) { + Long.MIN_VALUE + } else { + whole.toLong() + } + } + + override fun compareTo(other: Decimal): Int { + return if (other is Regular) + mag.compareTo(other.mag) + else if (other === PositiveInfinity) + -1 + else if (other === NegativeInfinity) + 1 + else if (other === Zero) + signum() + else + throw RuntimeException("unreachable code") + } + + + override fun plus(other: Decimal): Decimal { + return if (other is Regular) { + raw(mag + other.mag) + } else if (other === Zero) { + this + } else if (other === PositiveInfinity) { + PositiveInfinity + } else if (other === NegativeInfinity) { + NegativeInfinity + } else { + throw RuntimeException("Unreachable code") + } + } + + override fun minus(other: Decimal): Decimal { + return if (other is Regular) { + raw(mag - other.mag) + } else if (other === Zero) { + this + } else if (other === PositiveInfinity) { + NegativeInfinity + } else if (other === NegativeInfinity) { + PositiveInfinity + } else { + throw RuntimeException("Unreachable code") + } + } + + override fun times(other: Decimal): Decimal { + if (other is Regular) { + if (other == ONE) { + return this + } else if (other == MINUS_ONE) { + return raw(-mag) + } + + val result = mag * other.mag + val (a, b) = result.divideAndRemainder(PRECISION_POW_BI) + + return if (b >= PRECISION_POW_BI_HIGH) { + raw(a + BigInteger.ONE) + } else { + raw(a) + } + } else if (other === Zero) { + return Zero + } else if (other === PositiveInfinity) { + return PositiveInfinity + } else if (other === NegativeInfinity) { + return NegativeInfinity + } else { + throw RuntimeException("Unreachable code") + } + } + + override fun div(other: Decimal): Decimal { + if (other is Regular) { + if (other == ONE) { + return this + } else if (other == MINUS_ONE) { + return raw(-mag) + } + + return raw((mag * PRECISION_POW_BI) / other.mag) + } else if (other === Zero) { + throw ArithmeticException("$this / 0") + } else if (other === PositiveInfinity || other === NegativeInfinity) { + return Zero + } else { + throw RuntimeException("Unreachable code") + } + } + + override fun rem(other: Decimal): Decimal { + return if (other is Regular) { + raw(mag % other.mag) + } else if (other === Zero) { + throw ArithmeticException("$this % 0") + } else if (other === PositiveInfinity) { + this + } else if (other === NegativeInfinity) { + -this + } else { + throw RuntimeException("Unreachable code") + } + } + + // Primitive operators + override fun plus(other: Float): Decimal { + if (other == 0f) { + return this + } else if (other.isNaN()) { + throw ArithmeticException("$this + NaN") + } else if (other == Float.POSITIVE_INFINITY) { + return PositiveInfinity + } else if (other == Float.NEGATIVE_INFINITY) { + return NegativeInfinity + } + + return plus(valueOf(other)) + } + + override fun minus(other: Float): Decimal { + if (other == 0f) { + return this + } else if (other.isNaN()) { + throw ArithmeticException("$this - NaN") + } else if (other == Float.POSITIVE_INFINITY) { + return NegativeInfinity + } else if (other == Float.NEGATIVE_INFINITY) { + return PositiveInfinity + } + + return minus(valueOf(other)) + } + + override fun times(other: Float): Decimal { + if (other == 1f) { + return this + } else if (other == 0f) { + return Zero + } else if (other == -1f) { + return -this + } else if (other.isNaN()) { + throw ArithmeticException("$this * NaN") + } else if (other == Float.POSITIVE_INFINITY) { + return if (signum() < 0) NegativeInfinity else PositiveInfinity + } else if (other == Float.NEGATIVE_INFINITY) { + return if (signum() < 0) PositiveInfinity else NegativeInfinity + } + + return times(valueOf(other)) + } + + override fun div(other: Float): Decimal { + if (other == 0f) { + throw ArithmeticException("$this / 0") + } else if (other == 1f) { + return this + } else if (other == -1f) { + return -this + } else if (other.isNaN()) { + throw ArithmeticException("$this / NaN") + } else if (other.isInfinite()) { + return Zero + } + + return div(valueOf(other)) + } + + override fun plus(other: Double): Decimal { + if (other == 0.0) { + return this + } else if (other.isNaN()) { + throw ArithmeticException("$this + NaN") + } else if (other == Double.POSITIVE_INFINITY) { + return PositiveInfinity + } else if (other == Double.NEGATIVE_INFINITY) { + return NegativeInfinity + } + + return plus(valueOf(other)) + } + + override fun minus(other: Double): Decimal { + if (other == 0.0) { + return this + } else if (other.isNaN()) { + throw ArithmeticException("$this - NaN") + } else if (other == Double.POSITIVE_INFINITY) { + return NegativeInfinity + } else if (other == Double.NEGATIVE_INFINITY) { + return PositiveInfinity + } + + return minus(valueOf(other)) + } + + override fun times(other: Double): Decimal { + if (other == 1.0) { + return this + } else if (other == 0.0) { + return Zero + } else if (other == -1.0) { + return -this + } else if (other.isNaN()) { + throw ArithmeticException("$this * NaN") + } else if (other == Double.POSITIVE_INFINITY) { + return if (signum() < 0) NegativeInfinity else PositiveInfinity + } else if (other == Double.NEGATIVE_INFINITY) { + return if (signum() < 0) PositiveInfinity else NegativeInfinity + } + + return times(valueOf(other)) + } + + override fun div(other: Double): Decimal { + if (other == 0.0) { + throw ArithmeticException("$this / zero") + } else if (other == 1.0) { + return this + } else if (other == -1.0) { + return -this + } else if (other.isNaN()) { + throw ArithmeticException("$this / NaN") + } else if (other.isInfinite()) { + return Zero + } + + return div(valueOf(other)) + } + + override fun plus(other: Int): Decimal { + if (other == 0) + return this + + return plus(valueOf(other)) + } + + override fun minus(other: Int): Decimal { + if (other == 0) + return this + + return minus(valueOf(other)) + } + + override fun times(other: Int): Decimal { + if (other == 1) { + return this + } else if (other == 0) { + return Zero + } else if (other == -1) { + return -this + } + + return times(valueOf(other)) + } + + override fun div(other: Int): Decimal { + if (other == 0) { + throw ArithmeticException("$this / 0") + } else if (other == 1) { + return this + } else if (other == -1) { + return -this + } + + return div(valueOf(other)) + } + + override fun plus(other: Long): Decimal { + if (other == 0L) + return this + + return plus(valueOf(other)) + } + + override fun minus(other: Long): Decimal { + if (other == 0L) + return this + + return minus(valueOf(other)) + } + + override fun times(other: Long): Decimal { + if (other == 1L) { + return this + } else if (other == 0L) { + return Zero + } else if (other == -1L) { + return -this + } + + return times(valueOf(other)) + } + + override fun div(other: Long): Decimal { + if (other == 0L) { + throw ArithmeticException("$this / 0") + } else if (other == 1L || isZero) { + return this + } else if (other == -1L) { + return -this + } + + return div(valueOf(other)) + } + + override fun plus(other: BigInteger): Decimal { + if (other == BigInteger.ZERO) + return this + + return plus(valueOf(other)) + } + + override fun minus(other: BigInteger): Decimal { + if (other == BigInteger.ZERO) + return this + + return minus(valueOf(other)) + } + + override fun times(other: BigInteger): Decimal { + if (other == BigInteger.ONE) { + return this + } else if (other.signum() == 0) { + return Zero + } else if (other == BI_MINUS_ONE) { + return -this + } + + return times(valueOf(other)) + } + + override fun div(other: BigInteger): Decimal { + if (other == BigInteger.ZERO) { + throw ArithmeticException("$this / 0") + } else if (other == BigInteger.ONE) { + return this + } else if (other == BI_MINUS_ONE) { + return -this + } + + return div(valueOf(other)) + } + // /Primitive operators + + override fun unaryMinus(): Decimal { + return Regular(-mag, null) + } + + override fun toByteArray(): ByteArray { + val result = mag.toByteArray() + val copy = ByteArray(result.size + 1) + copy[0] = TYPE_NORMAL + result.copyInto(copy, 1) + return copy + } + + override fun floor(): Decimal { + return Regular(whole) + } + + override fun toString(decimals: Int): String { + if (decimals == 0) { + return whole.toString() + } + + if (mag.signum() == 0) { + return if (decimals < 0) { + "0.0" + } else { + "0." + "0".repeat(decimals) + } + } + + var original = mag.toString() + + if (mag.signum() < 0) { + // если у нас отрицательное число - убираем знак минуса + original = original.substring(1) + } + + if (original.length <= PRECISION) { + // если у нас чисто дробное число - дописываем нули в начало + original = "0".repeat(PRECISION - original.length + 1) + original + } + + // теперь у нас беззнаковая строка с нужной длиной + + if (decimals < 0) { + // нам неважно количество знаков после запятой + var result = original.substring(0, original.length - PRECISION) + "." + original.substring(original.length - PRECISION) + + if (mag.signum() < 0) { + result = "-$result" + } + + var pos = result.length - 1 + + while (result[pos] == '0') { + if (result[pos - 1] == '0' || result[pos - 1] != '.') + pos-- + else + break + } + + return result.substring(0, pos + 1) + } else { + // нужно некоторое количество знаков после запятой + val result = original.substring(0, original.length - PRECISION) + "." + var decimalPart = original.substring(original.length - PRECISION) + + if (decimalPart.length < decimals) { + decimalPart += "0".repeat(decimals - decimalPart.length) + } else if (decimalPart.length > decimals) { + decimalPart = decimalPart.substring(0, decimals) + } + + return if (mag.signum() < 0) { + "-$result$decimalPart" + } else { + "$result$decimalPart" + } + } + } + + override fun toBigDecmial(): BigDecimal { + return BigDecimal(mag, PRECISION) + } + + override fun equals(other: Any?): Boolean { + return this === other || other is Regular && mag == other.mag + } + + override fun hashCode(): Int { + return mag.hashCode() + } + } + + private object PositiveInfinity : Decimal() { + private fun readResolve(): Any = PositiveInfinity + + override val isInfinite: Boolean + get() = true + override val isFinite: Boolean + get() = false + + override fun compareTo(other: Decimal): Int { + return if (other === this) 0 else 1 + } + + override fun toByte(): Byte { + return Byte.MAX_VALUE + } + + override fun toDouble(): Double { + return Double.POSITIVE_INFINITY + } + + override fun toFloat(): Float { + return Float.POSITIVE_INFINITY + } + + override fun toInt(): Int { + return Int.MAX_VALUE + } + + override fun toLong(): Long { + return Long.MAX_VALUE + } + + override fun toShort(): Short { + return Short.MAX_VALUE + } + + override val whole: BigInteger + get() = throw UnsupportedOperationException("Attempt to get whole part of positive infinity") + override val fractional: BigInteger + get() = throw UnsupportedOperationException("Attempt to get fractional part of positive infinity") + + override fun plus(other: Decimal): Decimal { + return this + } + + override fun minus(other: Decimal): Decimal { + return this + } + + override fun times(other: Decimal): Decimal { + return if (other.signum() == 0) + Zero + else if (other.signum() < 0) + NegativeInfinity + else + this + } + + override fun div(other: Decimal): Decimal { + return if (other.signum() == 0) + throw ArithmeticException("Attempt to divide positive infinity by zero") + else if (other === this) + throw ArithmeticException("Dividing positive infinity by itself is undefined") + else if (other === NegativeInfinity) + throw ArithmeticException("Dividing positive infinity by negative infinity is undefined") + else if (other.signum() < 0) + NegativeInfinity + else + this + } + + override fun rem(other: Decimal): Decimal { + return if (other.signum() == 0) + throw ArithmeticException("Attempt to remainder divide positive infinity by zero") + else if (other.signum() < 0) + NegativeInfinity + else + this + } + + override fun plus(other: Float): Decimal { + return this + } + + override fun minus(other: Float): Decimal { + return this + } + + override fun times(other: Float): Decimal { + return if (other == 0f) + Zero + else if (other < 0f) + NegativeInfinity + else + PositiveInfinity + } + + override fun div(other: Float): Decimal { + return if (other == 0f) + throw ArithmeticException("Attempt to divide positive infinity by zero") + else if (other < 0f) + NegativeInfinity + else + PositiveInfinity + } + + override fun plus(other: Double): Decimal { + return this + } + + override fun minus(other: Double): Decimal { + return this + } + + override fun times(other: Double): Decimal { + return if (other == 0.0) + Zero + else if (other < 0.0) + NegativeInfinity + else + PositiveInfinity + } + + override fun div(other: Double): Decimal { + return if (other == 0.0) + throw ArithmeticException("Attempt to divide positive infinity by zero") + else if (other < 0.0) + NegativeInfinity + else + PositiveInfinity + } + + override fun plus(other: Int): Decimal { + return this + } + + override fun minus(other: Int): Decimal { + return this + } + + override fun times(other: Int): Decimal { + return if (other == 0) + Zero + else if (other < 0) + NegativeInfinity + else + PositiveInfinity + } + + override fun div(other: Int): Decimal { + return if (other == 0) + throw ArithmeticException("Attempt to divide positive infinity by zero") + else if (other < 0) + NegativeInfinity + else + PositiveInfinity + } + + override fun plus(other: Long): Decimal { + return this + } + + override fun minus(other: Long): Decimal { + return this + } + + override fun times(other: Long): Decimal { + return if (other == 0L) + Zero + else if (other < 0L) + NegativeInfinity + else + PositiveInfinity + } + + override fun div(other: Long): Decimal { + return if (other == 0L) + throw ArithmeticException("Attempt to divide positive infinity by zero") + else if (other < 0L) + NegativeInfinity + else + PositiveInfinity + } + + override fun plus(other: BigInteger): Decimal { + return this + } + + override fun minus(other: BigInteger): Decimal { + return this + } + + override fun times(other: BigInteger): Decimal { + return if (other.signum() == 0) + Zero + else if (other.signum() < 0) + NegativeInfinity + else + PositiveInfinity + } + + override fun div(other: BigInteger): Decimal { + return if (other.signum() == 0) + throw ArithmeticException("Attempt to divide positive infinity by zero") + else if (other.signum() < 0) + NegativeInfinity + else + PositiveInfinity + } + + override fun unaryMinus(): Decimal { + return NegativeInfinity + } + + override fun signum(): Int { + return 1 + } + + override fun toByteArray(): ByteArray { + return byteArrayOf(TYPE_POSITIVE_INFINITY) + } + + override fun floor(): Decimal { + return this + } + + override fun toString(decimals: Int): String { + return "+Infinity" + } + + override fun toBigDecmial(): BigDecimal { + throw UnsupportedOperationException("Unable to construct BigDecimal of positive infinity") + } + } + + private object NegativeInfinity : Decimal() { + private fun readResolve(): Any = NegativeInfinity + + override val isInfinite: Boolean + get() = true + override val isFinite: Boolean + get() = false + + override fun compareTo(other: Decimal): Int { + return if (other === this) 0 else -1 + } + + override fun toByte(): Byte { + return Byte.MIN_VALUE + } + + override fun toDouble(): Double { + return Double.NEGATIVE_INFINITY + } + + override fun toFloat(): Float { + return Float.NEGATIVE_INFINITY + } + + override fun toInt(): Int { + return Int.MIN_VALUE + } + + override fun toLong(): Long { + return Long.MIN_VALUE + } + + override fun toShort(): Short { + return Short.MIN_VALUE + } + + override val whole: BigInteger + get() = throw UnsupportedOperationException("Attempt to get whole part of negative infinity") + override val fractional: BigInteger + get() = throw UnsupportedOperationException("Attempt to get fractional part of negative infinity") + + override fun plus(other: Decimal): Decimal { + return this + } + + override fun minus(other: Decimal): Decimal { + return this + } + + override fun times(other: Decimal): Decimal { + return if (other.signum() == 0) + Zero + else if (other.signum() < 0) + PositiveInfinity + else + this + } + + override fun div(other: Decimal): Decimal { + return if (other.signum() == 0) + throw ArithmeticException("Attempt to divide negative infinity by zero") + else if (other === this) + throw ArithmeticException("Dividing negative infinity by itself is undefined") + else if (other === PositiveInfinity) + throw ArithmeticException("Dividing negative infinity by positive infinity is undefined") + else if (other.signum() < 0) + PositiveInfinity + else + this + } + + override fun rem(other: Decimal): Decimal { + return if (other.signum() == 0) + throw ArithmeticException("Attempt to remainder divide negative infinity by zero") + else if (other.signum() < 0) + PositiveInfinity + else + this + } + + override fun plus(other: Float): Decimal { + return this + } + + override fun minus(other: Float): Decimal { + return this + } + + override fun times(other: Float): Decimal { + return if (other == 0f) + Zero + else if (other < 0f) + PositiveInfinity + else + this + } + + override fun div(other: Float): Decimal { + return if (other == 0f) + throw ArithmeticException("Attempt to divide negative negative by zero") + else if (other < 0f) + PositiveInfinity + else + this + } + + override fun plus(other: Double): Decimal { + return this + } + + override fun minus(other: Double): Decimal { + return this + } + + override fun times(other: Double): Decimal { + return if (other == 0.0) + Zero + else if (other < 0.0) + PositiveInfinity + else + this + } + + override fun div(other: Double): Decimal { + return if (other == 0.0) + throw ArithmeticException("Attempt to divide negative infinity by zero") + else if (other < 0.0) + PositiveInfinity + else + this + } + + override fun plus(other: Int): Decimal { + return this + } + + override fun minus(other: Int): Decimal { + return this + } + + override fun times(other: Int): Decimal { + return if (other == 0) + Zero + else if (other < 0) + PositiveInfinity + else + this + } + + override fun div(other: Int): Decimal { + return if (other == 0) + throw ArithmeticException("Attempt to divide negative infinity by zero") + else if (other < 0) + PositiveInfinity + else + this + } + + override fun plus(other: Long): Decimal { + return this + } + + override fun minus(other: Long): Decimal { + return this + } + + override fun times(other: Long): Decimal { + return if (other == 0L) + Zero + else if (other < 0L) + PositiveInfinity + else + this + } + + override fun div(other: Long): Decimal { + return if (other == 0L) + throw ArithmeticException("Attempt to divide negative infinity by zero") + else if (other < 0L) + PositiveInfinity + else + this + } + + override fun plus(other: BigInteger): Decimal { + return this + } + + override fun minus(other: BigInteger): Decimal { + return this + } + + override fun times(other: BigInteger): Decimal { + return if (other.signum() == 0) + Zero + else if (other.signum() < 0) + PositiveInfinity + else + this + } + + override fun div(other: BigInteger): Decimal { + return if (other.signum() == 0) + throw ArithmeticException("Attempt to divide negative infinity by zero") + else if (other.signum() < 0) + PositiveInfinity + else + this + } + + override fun unaryMinus(): Decimal { + return PositiveInfinity + } + + override fun signum(): Int { + return -1 + } + + override fun toByteArray(): ByteArray { + return byteArrayOf(TYPE_NEGATIVE_INFINITY) + } + + override fun floor(): Decimal { + return this + } + + override fun toString(decimals: Int): String { + return "-Infinity" + } + + override fun toBigDecmial(): BigDecimal { + throw UnsupportedOperationException("Unable to construct BigDecimal of negative infinity") + } + } + + private object Zero : Decimal() { + private fun readResolve(): Any = Zero + + override val isInfinite: Boolean + get() = false + override val isFinite: Boolean + get() = true + + override fun compareTo(other: Decimal): Int { + return -other.signum() + } + + override fun toByte(): Byte { + return 0 + } + + override fun toDouble(): Double { + return 0.0 + } + + override fun toFloat(): Float { + return 0f + } + + override fun toInt(): Int { + return 0 + } + + override fun toLong(): Long { + return 0L + } + + override fun toShort(): Short { + return 0 + } + + override val whole: BigInteger + get() = BigInteger.ZERO + override val fractional: BigInteger + get() = BigInteger.ZERO + + override fun plus(other: Decimal): Decimal { + return other + } + + override fun minus(other: Decimal): Decimal { + return -other + } + + override fun times(other: Decimal): Decimal { + return this + } + + override fun div(other: Decimal): Decimal { + if (other === this) + throw ArithmeticException("0 / 0") + else + return this + } + + override fun rem(other: Decimal): Decimal { + if (other === this) + throw ArithmeticException("0 % 0") + else + return this + } + + override fun plus(other: Float): Decimal { + return valueOf(other) + } + + override fun minus(other: Float): Decimal { + return valueOf(-other) + } + + override fun times(other: Float): Decimal { + return this + } + + override fun div(other: Float): Decimal { + if (other == 0f) + throw ArithmeticException("0 / 0") + + return this + } + + override fun plus(other: Double): Decimal { + return valueOf(other) + } + + override fun minus(other: Double): Decimal { + return valueOf(-other) + } + + override fun times(other: Double): Decimal { + return this + } + + override fun div(other: Double): Decimal { + if (other == 0.0) + throw ArithmeticException("0 / 0") + + return this + } + + override fun plus(other: Int): Decimal { + return valueOf(other) + } + + override fun minus(other: Int): Decimal { + return valueOf(-other) + } + + override fun times(other: Int): Decimal { + return this + } + + override fun div(other: Int): Decimal { + if (other == 0) + throw ArithmeticException("0 / 0") + + return this + } + + override fun plus(other: Long): Decimal { + return valueOf(other) + } + + override fun minus(other: Long): Decimal { + return valueOf(-other) + } + + override fun times(other: Long): Decimal { + return this + } + + override fun div(other: Long): Decimal { + if (other == 0L) + throw ArithmeticException("0 / 0") + + return this + } + + override fun plus(other: BigInteger): Decimal { + return valueOf(other) + } + + override fun minus(other: BigInteger): Decimal { + return valueOf(-other) + } + + override fun times(other: BigInteger): Decimal { + return this + } + + override fun div(other: BigInteger): Decimal { + if (other == BigInteger.ZERO) + throw ArithmeticException("0 / 0") + + return this + } + + override fun unaryMinus(): Decimal { + return this + } + + override fun signum(): Int { + return 0 + } + + override fun toByteArray(): ByteArray { + return byteArrayOf(TYPE_ZERO) + } + + override fun floor(): Decimal { + return this + } + + override fun toString(decimals: Int): String { + if (decimals <= 0) + return "0" + else + return "0." + "0".repeat(decimals) + } + + override fun toBigDecmial(): BigDecimal { + return BigDecimal.ZERO + } + } + @Suppress("unused") companion object { /** @@ -585,6 +1362,11 @@ class Decimal private constructor(val mag: BigInteger, marker: Nothing?) : Numbe */ const val PRECISION = 11 + const val TYPE_ZERO: Byte = 0 + const val TYPE_NORMAL: Byte = 1 + const val TYPE_POSITIVE_INFINITY: Byte = 2 + const val TYPE_NEGATIVE_INFINITY: Byte = 3 + @JvmField val PRECISION_POW_BI: BigInteger = BigInteger("1" + "0".repeat(PRECISION)) @JvmField @@ -599,7 +1381,12 @@ class Decimal private constructor(val mag: BigInteger, marker: Nothing?) : Numbe private const val cacheSize = 16384 private const val cacheSizeL = cacheSize.toLong() - private val cache = Array(cacheSize) { Decimal(BigInteger.valueOf(it.toLong() - cacheSize / 2)) } + private val cache = Array(cacheSize) { Regular(BigInteger.valueOf(it.toLong() - cacheSize / 2)) } + + init { + check(cache[cacheSize / 2].signum() == 0) + cache[cacheSize / 2] = Zero + } /** * Returns pooled value if present, otherwise constructs new object @@ -610,7 +1397,7 @@ class Decimal private constructor(val mag: BigInteger, marker: Nothing?) : Numbe return cache[value + cacheSize / 2] } - return Decimal(BigInteger.valueOf(value.toLong())) + return Regular(BigInteger.valueOf(value.toLong())) } /** @@ -622,7 +1409,7 @@ class Decimal private constructor(val mag: BigInteger, marker: Nothing?) : Numbe return cache[value.toInt() + cacheSize / 2] } - return Decimal(BigInteger.valueOf(value)) + return Regular(BigInteger.valueOf(value)) } /** @@ -637,18 +1424,77 @@ class Decimal private constructor(val mag: BigInteger, marker: Nothing?) : Numbe @JvmStatic fun valueOf(value: Byte) = valueOf(value.toInt()) - @JvmStatic fun valueOf(value: BigInteger) = Decimal(value) - @JvmStatic fun valueOf(value: BigDecimal) = Decimal(value) - @JvmStatic fun valueOf(value: String) = Decimal(value) - @JvmStatic fun valueOf(value: Float) = Decimal(value) - @JvmStatic fun valueOf(value: Double) = Decimal(value) + @JvmStatic fun valueOf(value: BigInteger): Decimal { + return if (value.signum() == 0) { + Zero + } else { + Regular(value) + } + } + + @JvmStatic fun valueOf(value: BigDecimal): Decimal { + return if (value.signum() == 0) { + Zero + } else { + Regular(value) + } + } + + @JvmStatic fun valueOf(value: String): Decimal { + if (value == "0") { + return Zero + } else { + val lower = value.lowercase() + + // why not + if (lower.startsWith("+inf") || lower.startsWith("inf") || lower == "∞" || lower == "+∞") { + return PositiveInfinity + } else if (lower.startsWith("-inf") || lower == "-∞") { + return NegativeInfinity + } else { + val result = Regular(value) + if (result.signum() == 0) return Zero + return result + } + } + } + + @JvmStatic fun valueOf(value: Float): Decimal { + return if (value == 0f) { + Zero + } else if (value == Float.POSITIVE_INFINITY) { + PositiveInfinity + } else if (value == Float.NEGATIVE_INFINITY) { + NegativeInfinity + } else { + Regular(value) + } + } + + @JvmStatic fun valueOf(value: Double): Decimal { + return if (value == 0.0) { + Zero + } else if (value == Double.POSITIVE_INFINITY) { + PositiveInfinity + } else if (value == Double.NEGATIVE_INFINITY) { + NegativeInfinity + } else { + Regular(value) + } + } @JvmStatic fun fromByteArray(input: ByteArray): Decimal { - if (input.isEmpty()) { - return ZERO + return if (input.isEmpty()) { + Zero } else { - return Decimal(BigInteger(input), null) + return when (input[0]) { + TYPE_NORMAL -> raw(BigInteger(input.copyOfRange(1, input.size))) + TYPE_NEGATIVE_INFINITY -> NegativeInfinity + TYPE_POSITIVE_INFINITY -> PositiveInfinity + // TYPE_ZERO -> Zero + else -> Zero + } } } @@ -657,24 +1503,32 @@ class Decimal private constructor(val mag: BigInteger, marker: Nothing?) : Numbe if (input is ByteArrayTag) { return fromByteArray(input.asByteArray) } else if (input is StringTag) { - return Decimal(input.asString) + return valueOf(input.asString) } - return ZERO + return Zero } @JvmStatic fun read(buff: FriendlyByteBuf): Decimal { - return Decimal(BigInteger(buff.readByteArray()), null) + return fromByteArray(buff.readByteArray()) } /** * Takes in [value] as-is and constructs [Decimal] out of it ([value] is treated as already scaled by [PRECISION]) */ @JvmStatic - fun raw(value: BigInteger): Decimal = Decimal(value, null) + fun raw(value: BigInteger): Decimal { + return if (value.signum() == 0) { + Zero + } else { + Regular(value, null) + } + } - @JvmField val ZERO = valueOf(0) + @JvmField val POSITIVE_INFINITY: Decimal = PositiveInfinity + @JvmField val NEGATIVE_INFINITY: Decimal = NegativeInfinity + @JvmField val ZERO: Decimal = Zero @JvmField val ONE = valueOf(1) @JvmField val TWO = valueOf(2) @JvmField val TEN = valueOf(10) @@ -690,15 +1544,15 @@ class Decimal private constructor(val mag: BigInteger, marker: Nothing?) : Numbe @JvmField val MINUS_ONE_QUARTER = valueOf("-0.25") @JvmField val MINUS_ONE_HALF = valueOf("-0.5") - @JvmField val INT_MAX_VALUE = Decimal(BI_INT_MAX) - @JvmField val INT_MIN_VALUE = Decimal(BI_INT_MIN) - @JvmField val LONG_MAX_VALUE = Decimal(BI_LONG_MAX) - @JvmField val LONG_MIN_VALUE = Decimal(BI_LONG_MIN) + @JvmField val INT_MAX_VALUE: Decimal = Regular(BI_INT_MAX) + @JvmField val INT_MIN_VALUE: Decimal = Regular(BI_INT_MIN) + @JvmField val LONG_MAX_VALUE: Decimal = Regular(BI_LONG_MAX) + @JvmField val LONG_MIN_VALUE: Decimal = Regular(BI_LONG_MIN) - @JvmField val FLOAT_MAX_VALUE = Decimal(BI_FLOAT_MAX) - @JvmField val FLOAT_MIN_VALUE = Decimal(BI_FLOAT_MIN) - @JvmField val DOUBLE_MAX_VALUE = Decimal(BI_DOUBLE_MAX) - @JvmField val DOUBLE_MIN_VALUE = Decimal(BI_DOUBLE_MIN) + @JvmField val FLOAT_MAX_VALUE: Decimal = Regular(BI_FLOAT_MAX) + @JvmField val FLOAT_MIN_VALUE: Decimal = Regular(BI_FLOAT_MIN) + @JvmField val DOUBLE_MAX_VALUE: Decimal = Regular(BI_DOUBLE_MAX) + @JvmField val DOUBLE_MIN_VALUE: Decimal = Regular(BI_DOUBLE_MIN) } } @@ -789,10 +1643,10 @@ fun ForgeConfigSpec.Builder.defineDecimal(path: List, defaultValue: Deci fun RandomSource.nextDecimal(min: Decimal, max: Decimal, round: Boolean = false): Decimal { val value = nextDouble() - if (round) { - return Decimal((min + (max - min) * value).whole) + return if (round) { + Decimal((min + (max - min) * value).whole) } else { - return min + (max - min) * value + min + (max - min) * value } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/Formatting.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/Formatting.kt index a773fb4d1..56339f947 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/Formatting.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/Formatting.kt @@ -167,6 +167,8 @@ fun Double.formatSiComponent(suffix: Any = "", decimalPlaces: Int = 3, formatAsR fun Decimal.formatSiComponent(suffix: Any = "", decimalPlaces: Int = 3, formatAsReadable: BooleanSupplier = never, bias: Int = 0): Component { require(decimalPlaces >= 0) { "Invalid amount of decimal places required: $decimalPlaces" } + if (this == Decimal.POSITIVE_INFINITY) return concat("∞", suffix) + if (this == Decimal.NEGATIVE_INFINITY) return concat("-∞", suffix) if (formatAsReadable.asBoolean && this.absoluteValue >= Decimal.ONE) return concat(reformat(toString(decimalPlaces)), suffix) val prefix = SiPrefix.determine(this) return TranslatableComponent(prefix.neighbour(bias).formatLocaleKey, (this / prefix.decimal).toString(decimalPlaces), suffix) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/SiPrefix.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/SiPrefix.kt index 2789e4ebf..4255840f6 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/SiPrefix.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/SiPrefix.kt @@ -121,7 +121,7 @@ enum class SiPrefix(val power: Int, val symbol: String) { } fun determine(value: Decimal, bias: Int = 0): SiPrefix { - if (value.isZero) { + if (value.isZero || value.isInfinite) { return NONE }