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 cd3c96913..59683671a 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 @@ -537,6 +537,15 @@ class Decimal @JvmOverloads constructor(whole: BigInteger, decimal: Double = 0.0 return Decimal(-whole, -decimal) } + val absoluteValue: Decimal + get() { + if (isNegative) { + return -this + } else { + return this + } + } + override fun equals(other: Any?): Boolean { if (isNaN) return false 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 a70ad48ac..ad2dad48b 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 @@ -8,6 +8,16 @@ import ru.dbotthepony.mc.otm.core.math.isNegative import ru.dbotthepony.mc.otm.core.math.isZero import java.math.BigDecimal import java.math.BigInteger +import kotlin.math.absoluteValue + +private fun concat(numbers: String, suffix: Any): Component { + if (suffix == "") + return TextComponent(numbers) + else if (suffix is Component) + return TextComponent(numbers + " " + suffix.string) + else + return TextComponent("$numbers $suffix") +} fun BigInteger.formatReadableNumber(): String { if (isZero) { @@ -49,121 +59,25 @@ fun BigInteger.formatReadableNumber(): String { } fun BigDecimal.determineSiPrefix(): SiPrefix? { - if (isZero) { - return null - } - - var num = this - - if (isNegative) { - num = -this - } - - var prev: SiPrefix? = null + if (isZero) return null + val num = this.abs() if (num >= BigDecimal.ONE) { - for (value in SiPrefix.MULTIPLIES) { - if (value.decimal <= num) { - prev = value - } else { - break - } - } + return SiPrefix.MULTIPLIES.lastOrNull { it.decimal <= num } } else { - for (value in SiPrefix.DECIMALS) { - if (value.decimal >= num) { - prev = value - } else { - break - } - } + return SiPrefix.DECIMALS.lastOrNull { it.decimal >= num } } - - return prev -} - -fun BigDecimal.formatSiTranslatable(): Component { - if (isZero) { - return TextComponent("0.00") - } else if (this == BigDecimal.ONE) { - return TextComponent("1.00") - } - - return TextComponent("1.00") } fun BigInteger.determineSiPrefix(): SiPrefix? { - if (isZero) { - return null - } - - var num = this - - if (isNegative) { - num = -this - } - - var prev: SiPrefix? = null - - if (num >= BigInteger.ONE) { - for (value in SiPrefix.MULTIPLIES) { - if (value.integer!! <= num) { - prev = value - } else { - break - } - } - } - - return prev -} - -fun BigInteger.formatSi(decimalPlaces: Int = 2): String { - require(decimalPlaces >= 0) { "Invalid amount of decimal places required: $decimalPlaces" } - val prefix = determineSiPrefix() ?: return toString() // + "." + "0".repeat(decimalPlaces) - val isNegative = isNegative - val arr = (if (isNegative) -this else this).divideAndRemainder(prefix.integer) - val divided = arr[0].toString() - val remainder = arr[1].toString() - - if (decimalPlaces == 0) { - if (isNegative) { - return "-" + divided + prefix.symbol - } else { - return divided + prefix.symbol - } - } - - @Suppress("NAME_SHADOWING") - val decimalPlaces = decimalPlaces.coerceAtMost(prefix.power) - - val add = (if (isNegative) 1 else 0) - - val buffer = CharArray(divided.length + 2 + decimalPlaces + add) - buffer[buffer.size - 1] = prefix.symbol - - if (isNegative) { - buffer[0] = '-' - } - - for (i in divided.indices) { - buffer[add + i] = divided[i] - } - - buffer[add + divided.length] = '.' - - for (i in 0 until decimalPlaces) { - buffer[add + i + divided.length + 1] = prefix.paddedIndex(remainder, i) - } - - return String(buffer) + if (isZero) return null + val num = this.abs() + return SiPrefix.MULTIPLIES.lastOrNull { it.integer!! <= num } } fun BigInteger.formatSiComponent(suffix: Any = "", decimalPlaces: Int = 2): Component { require(decimalPlaces >= 0) { "Invalid amount of decimal places required: $decimalPlaces" } - val prefix = determineSiPrefix() ?: return if (suffix == "") TextComponent(toString(decimalPlaces)) else if (suffix is Component) TextComponent( - toString(decimalPlaces) + suffix.string - ) else TextComponent(toString(decimalPlaces) + suffix) + val prefix = determineSiPrefix() ?: return concat(toString(decimalPlaces), suffix) val isNegative = isNegative val arr = (if (isNegative) -this else this).divideAndRemainder(prefix.integer) val divided = arr[0].toString() @@ -171,7 +85,7 @@ fun BigInteger.formatSiComponent(suffix: Any = "", decimalPlaces: Int = 2): Comp if (decimalPlaces == 0) { if (isNegative) { - return TranslatableComponent(prefix.formatLocaleKey, "-$divided", suffix) + return TranslatableComponent(prefix.formatLocaleKey, "-$divided${prefix.symbol}", suffix) } else { return TranslatableComponent(prefix.formatLocaleKey, divided + prefix.symbol, suffix) } @@ -179,9 +93,7 @@ fun BigInteger.formatSiComponent(suffix: Any = "", decimalPlaces: Int = 2): Comp @Suppress("NAME_SHADOWING") val decimalPlaces = decimalPlaces.coerceAtMost(prefix.power) - val add = (if (isNegative) 1 else 0) - val buffer = CharArray(divided.length + 1 + decimalPlaces + add) if (isNegative) { @@ -206,33 +118,13 @@ fun Decimal.determineSiPrefix(): SiPrefix? { return null } - var num = this - - if (isNegative) { - num = -this - } - - var prev: SiPrefix? = null + val num = this.absoluteValue if (num >= Decimal.ONE) { - for (value in SiPrefix.MULTIPLIES) { - if (value.impreciseFraction <= num) { - prev = value - } else { - break - } - } + return SiPrefix.MULTIPLIES.lastOrNull { it.impreciseFraction <= num } } else { - for (value in SiPrefix.DECIMALS_IMPRECISE) { - if (value.impreciseFraction >= num) { - prev = value - } else { - break - } - } + return SiPrefix.DECIMALS.lastOrNull { it.impreciseFraction >= num } } - - return prev } fun Int.determineSiPrefix(): SiPrefix? { @@ -240,51 +132,20 @@ fun Int.determineSiPrefix(): SiPrefix? { return null } - var num = this - - if (this < 0) { - num = -this - } - - var prev: SiPrefix? = null - - if (num >= 1) { - for (value in SiPrefix.MULTIPLIES) { - if (value.int != null && value.int <= num) { - prev = value - } else { - break - } - } - } - - return prev + val num = this.absoluteValue + if (num <= 1) return null + return SiPrefix.MULTIPLIES.lastOrNull { it.int != null && it.int <= num } } fun Double.determineSiPrefix(): SiPrefix? { - if (this == 0.0) { - return null + if (this == 0.0) return null + val num = this.absoluteValue + + if (num >= 1.0) { + return SiPrefix.MULTIPLIES.lastOrNull { it.double <= num } + } else { + return SiPrefix.DECIMALS.lastOrNull { it.double >= num } } - - var num = this - - if (this < 0) { - num = -this - } - - var prev: SiPrefix? = null - - if (num >= 1) { - for (value in SiPrefix.MULTIPLIES) { - if (value.double <= num) { - prev = value - } else { - break - } - } - } - - return prev } fun Decimal.formatSi(decimalPlaces: Int = 2): String { @@ -295,59 +156,34 @@ fun Decimal.formatSi(decimalPlaces: Int = 2): String { fun Int.formatSiComponent(suffix: Any = "", decimalPlaces: Int = 2): Component { require(decimalPlaces >= 0) { "Invalid amount of decimal places required: $decimalPlaces" } - val prefix = determineSiPrefix() ?: return if (suffix == "") TextComponent(toString()) else if (suffix is Component) TextComponent( - toString() + " " + suffix.string - ) else TextComponent(toString() + " " + suffix) - return TranslatableComponent( - prefix.formatLocaleKey, - "%.${decimalPlaces}f".format(this.toFloat() / prefix.int!!.toFloat()), - suffix - ) + val prefix = determineSiPrefix() ?: return concat(toString(), suffix) + return TranslatableComponent(prefix.formatLocaleKey, "%.${decimalPlaces}f".format(this.toFloat() / prefix.int!!.toFloat()), suffix) } fun Double.formatSiComponent(suffix: Any = "", decimalPlaces: Int = 2): Component { require(decimalPlaces >= 0) { "Invalid amount of decimal places required: $decimalPlaces" } - val prefix = determineSiPrefix() ?: return if (suffix == "") TextComponent("%.${decimalPlaces}f".format(this)) else if (suffix is Component) TextComponent( - "%.${decimalPlaces}f".format(this) + " " + suffix.string - ) else TextComponent("%.${decimalPlaces}f".format(this) + " " + suffix) + val prefix = determineSiPrefix() ?: return concat("%.${decimalPlaces}f".format(this), suffix) return TranslatableComponent(prefix.formatLocaleKey, "%.${decimalPlaces}f".format(this / prefix.double), suffix) } fun Decimal.formatSiComponent(suffix: Any = "", decimalPlaces: Int = 2): Component { require(decimalPlaces >= 0) { "Invalid amount of decimal places required: $decimalPlaces" } - val prefix = determineSiPrefix() ?: return if (suffix == "") TextComponent(toString(decimalPlaces)) else if (suffix is Component) TextComponent( - toString(decimalPlaces) + " " + suffix.string - ) else TextComponent(toString(decimalPlaces) + " " + suffix) - return TranslatableComponent( - prefix.formatLocaleKey, - (this / prefix.impreciseFraction).toString(decimalPlaces), - suffix - ) + val prefix = determineSiPrefix() ?: return concat(toString(decimalPlaces), suffix) + return TranslatableComponent(prefix.formatLocaleKey, (this / prefix.impreciseFraction).toString(decimalPlaces), suffix) } -val POWER_NAME = TranslatableComponent("otm.gui.power.name") -val MATTER_NAME = TranslatableComponent("otm.gui.matter.name") -fun Int.formatPower(decimalPlaces: Int = 2) = formatSiComponent(POWER_NAME, decimalPlaces) -fun Decimal.formatPower(decimalPlaces: Int = 2) = formatSiComponent(POWER_NAME, decimalPlaces) -fun Decimal.formatMatter(decimalPlaces: Int = 2) = formatSiComponent(MATTER_NAME, decimalPlaces) -fun Decimal.formatMatterFull(decimalPlaces: Int = 2) = TranslatableComponent( - "otm.gui.matter.format", formatSiComponent( - MATTER_NAME, decimalPlaces - ) -) +fun Int.formatPower(decimalPlaces: Int = 2) = formatSiComponent(TranslatableComponent("otm.gui.power.name"), decimalPlaces) +fun Decimal.formatPower(decimalPlaces: Int = 2) = formatSiComponent(TranslatableComponent("otm.gui.power.name"), decimalPlaces) +fun Decimal.formatMatter(decimalPlaces: Int = 2) = formatSiComponent(TranslatableComponent("otm.gui.matter.name"), decimalPlaces) +fun Decimal.formatMatterFull(decimalPlaces: Int = 2) = TranslatableComponent("otm.gui.matter.format", formatSiComponent(TranslatableComponent("otm.gui.matter.name"), decimalPlaces)) -fun BigInteger.formatPower(decimalPlaces: Int = 2) = formatSiComponent(POWER_NAME, decimalPlaces) -fun BigInteger.formatMatter(decimalPlaces: Int = 2) = formatSiComponent(MATTER_NAME, decimalPlaces) -fun BigInteger.formatMatterFull(decimalPlaces: Int = 2) = TranslatableComponent( - "otm.gui.matter.format", formatSiComponent( - MATTER_NAME, decimalPlaces - ) -) +fun BigInteger.formatPower(decimalPlaces: Int = 2) = formatSiComponent(TranslatableComponent("otm.gui.power.name"), decimalPlaces) +fun BigInteger.formatMatter(decimalPlaces: Int = 2) = formatSiComponent(TranslatableComponent("otm.gui.matter.name"), decimalPlaces) +fun BigInteger.formatMatterFull(decimalPlaces: Int = 2) = TranslatableComponent("otm.gui.matter.format", formatSiComponent(TranslatableComponent("otm.gui.matter.name"), decimalPlaces)) + +fun formatPowerLevel(a: Decimal, b: Decimal, decimalPlaces: Int = 2) = TranslatableComponent("otm.gui.level", a.formatPower(decimalPlaces), b.formatPower(decimalPlaces)) +fun formatMatterLevel(a: Decimal, b: Decimal, decimalPlaces: Int = 2) = TranslatableComponent("otm.gui.level", a.formatMatter(decimalPlaces), b.formatMatter(decimalPlaces)) -fun formatPowerLevel(a: Decimal, b: Decimal, decimalPlaces: Int = 2) = - TranslatableComponent("otm.gui.level", a.formatPower(decimalPlaces), b.formatPower(decimalPlaces)) -fun formatMatterLevel(a: Decimal, b: Decimal, decimalPlaces: Int = 2) = - TranslatableComponent("otm.gui.level", a.formatMatter(decimalPlaces), b.formatMatter(decimalPlaces)) private fun padded(num: Int): String { if (num in 0 .. 9) { return "0$num" 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 b12ac983d..7c227a279 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 @@ -56,6 +56,7 @@ enum class SiPrefix( val double = string.toDouble() companion object { + @JvmField val MULTIPLIES: List = ImmutableList.builder() .add(KILO) .add(MEGA) @@ -67,6 +68,7 @@ enum class SiPrefix( .add(YOTTA) .build() + @JvmField val DECIMALS: List = ImmutableList.builder() //.add(DECI) //.add(CENTI) @@ -80,6 +82,7 @@ enum class SiPrefix( .add(YOCTO) .build() + @JvmField val DECIMALS_IMPRECISE: List = ImmutableList.builder() //.add(DECI) //.add(CENTI) diff --git a/src/test/kotlin/ru/dbotthepony/mc/otm/tests/FormattingTests.kt b/src/test/kotlin/ru/dbotthepony/mc/otm/tests/FormattingTests.kt deleted file mode 100644 index e258d8012..000000000 --- a/src/test/kotlin/ru/dbotthepony/mc/otm/tests/FormattingTests.kt +++ /dev/null @@ -1,69 +0,0 @@ -package ru.dbotthepony.mc.otm.tests - -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import ru.dbotthepony.mc.otm.core.math.Decimal -import ru.dbotthepony.mc.otm.core.util.formatReadableNumber -import ru.dbotthepony.mc.otm.core.util.formatSi -import java.math.BigInteger - -object FormattingTests { - @Test - @DisplayName("BigInteger formatting as readable number") - fun biginteger() { - assertEquals("0", BigInteger("0").formatReadableNumber()) - assertEquals("45", BigInteger("45").formatReadableNumber()) - assertEquals("-45", BigInteger("-45").formatReadableNumber()) - assertEquals("0", BigInteger("-0").formatReadableNumber()) - - assertEquals("100", BigInteger("100").formatReadableNumber()) - assertEquals("-100", BigInteger("-100").formatReadableNumber()) - assertEquals("999", BigInteger("999").formatReadableNumber()) - assertEquals("1 999", BigInteger("1999").formatReadableNumber()) - assertEquals("8 992", BigInteger("8992").formatReadableNumber()) - assertEquals("-8 992", BigInteger("-8992").formatReadableNumber()) - assertEquals("100 200", BigInteger("100200").formatReadableNumber()) - assertEquals("-100 200", BigInteger("-100200").formatReadableNumber()) - - assertEquals("-1 100 200", BigInteger("-1100200").formatReadableNumber()) - assertEquals("1 100 200", BigInteger("1100200").formatReadableNumber()) - assertEquals("2 730 250 200", BigInteger("2730250200").formatReadableNumber()) - assertEquals("1 222 730 250 200", BigInteger("1222730250200").formatReadableNumber()) - } - - @Test - @DisplayName("BigInteger formatting as si number") - fun bigintegerSi() { - assertEquals("0", BigInteger("0").formatSi()) - assertEquals("420", BigInteger("420").formatSi()) - assertEquals("-420", BigInteger("-420").formatSi()) - assertEquals("555", BigInteger("555").formatSi()) - assertEquals("55", BigInteger("55").formatSi()) - assertEquals("1.20k", BigInteger("1205").formatSi()) - assertEquals("-1.20k", BigInteger("-1205").formatSi()) - assertEquals("1.21k", BigInteger("1215").formatSi()) - assertEquals("4.50M", BigInteger("4501204").formatSi()) - assertEquals("4.00M", BigInteger("4000111").formatSi()) - assertEquals("4.0011M", BigInteger("4001111").formatSi(4)) - assertEquals("-4.0011M", BigInteger("-4001111").formatSi(4)) - } - - @Test - @DisplayName("ImpreciseFraction formatting as si number") - fun impreciseFractionSi() { - assertEquals("0.00", Decimal("0").formatSi(2)) - assertEquals("14.62", Decimal("14.62").formatSi(2)) - assertEquals("1.00k", Decimal("1000").formatSi(2)) - assertEquals("1.00k", Decimal("1000.1").formatSi(2)) - assertEquals("1.00k", Decimal("1004.2").formatSi(2)) - assertEquals("1.01k", Decimal("1014.5").formatSi(2)) - assertEquals("1.014k", Decimal("1014.5").formatSi(3)) - assertEquals("1.01k", Decimal("1014.256").formatSi(2)) - assertEquals("12.73k", Decimal("12734.256").formatSi(2)) - assertEquals("127.34k", Decimal("127342.256").formatSi(2)) - assertEquals("1.27M", Decimal("1273421.256").formatSi(2)) - assertEquals("1.273M", Decimal("1273421.256").formatSi(3)) - assertEquals("1.2734M", Decimal("1273421.256").formatSi(4)) - } -}