From ce437d4e140fe61bbc29ddf62e5eb6dd7ac4f6d2 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sat, 14 Jan 2023 10:05:42 +0700 Subject: [PATCH] Remove Fraction class --- .../ru/dbotthepony/mc/otm/core/Decimal.kt | 12 - .../kotlin/ru/dbotthepony/mc/otm/core/Ext.kt | 55 +- .../ru/dbotthepony/mc/otm/core/Formatting.kt | 1 - .../ru/dbotthepony/mc/otm/core/Fraction.kt | 931 ------------------ .../dbotthepony/mc/otm/tests/FractionTests.kt | 345 ------- 5 files changed, 1 insertion(+), 1343 deletions(-) delete mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/core/Fraction.kt delete mode 100644 src/test/kotlin/ru/dbotthepony/mc/otm/tests/FractionTests.kt diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Decimal.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Decimal.kt index 68d8ab1d7..ddd2f602b 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Decimal.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Decimal.kt @@ -729,18 +729,6 @@ class Decimal @JvmOverloads constructor(whole: BigInteger, decimal: Double = 0.0 return toInt().toShort() } - fun toFraction(): Fraction { - if (isZero) { - return Fraction.ZERO - } - - if (weakEqualDoubles(decimal, 0.0)) { - return Fraction(whole) - } - - return Fraction(toBigDecmial()) - } - fun percentage(divisor: Decimal, zeroing: Boolean = true): Float { if ((isZero || divisor.isZero) && zeroing) return 0f if (isNaN || divisor.isNaN || divisor.isZero) return Float.NaN diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt index 5466189c0..dbb2e120e 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt @@ -59,19 +59,6 @@ fun Number.dynPlus(other: Number): Number { is Int, is Byte, is Short -> this + other.toInt() is Long -> this + other.toLong() is Decimal -> this + other - is Fraction -> this + other.toImpreciseFraction() - else -> throw IllegalStateException("I don't know what to do with ${other::class.qualifiedName} (right hand operand)") - } - } - - is Fraction -> { - when (other) { - is Float -> this + other.toFloat() - is Double -> this + other.toDouble() - is Int, is Byte, is Short -> this + other.toInt() - is Long -> this + other.toLong() - is Decimal -> this + other.toFraction() - is Fraction -> this + other else -> throw IllegalStateException("I don't know what to do with ${other::class.qualifiedName} (right hand operand)") } } @@ -101,19 +88,6 @@ fun Number.dynMinus(other: Number): Number { is Int, is Byte, is Short -> this - other.toInt() is Long -> this - other.toLong() is Decimal -> this - other - is Fraction -> this - other.toImpreciseFraction() - else -> throw IllegalStateException("I don't know what to do with ${other::class.qualifiedName} (right hand operand)") - } - } - - is Fraction -> { - when (other) { - is Float -> this - other.toFloat() - is Double -> this - other.toDouble() - is Int, is Byte, is Short -> this - other.toInt() - is Long -> this - other.toLong() - is Decimal -> this - other.toFraction() - is Fraction -> this - other else -> throw IllegalStateException("I don't know what to do with ${other::class.qualifiedName} (right hand operand)") } } @@ -143,19 +117,6 @@ fun Number.dynTimes(other: Number): Number { is Int, is Byte, is Short -> this * other.toInt() is Long -> this * other.toLong() is Decimal -> this * other - is Fraction -> this * other.toImpreciseFraction() - else -> throw IllegalStateException("I don't know what to do with ${other::class.qualifiedName} (right hand operand)") - } - } - - is Fraction -> { - when (other) { - is Float -> this * other.toFloat() - is Double -> this * other.toDouble() - is Int, is Byte, is Short -> this * other.toInt() - is Long -> this * other.toLong() - is Decimal -> this * other.toFraction() - is Fraction -> this * other else -> throw IllegalStateException("I don't know what to do with ${other::class.qualifiedName} (right hand operand)") } } @@ -185,19 +146,6 @@ fun Number.dynDiv(other: Number): Number { is Int, is Byte, is Short -> this / other.toInt() is Long -> this / other.toLong() is Decimal -> this / other - is Fraction -> this / other.toImpreciseFraction() - else -> throw IllegalStateException("I don't know what to do with ${other::class.qualifiedName} (right hand operand)") - } - } - - is Fraction -> { - when (other) { - is Float -> this / other.toFloat() - is Double -> this / other.toDouble() - is Int, is Byte, is Short -> this / other.toInt() - is Long -> this / other.toLong() - is Decimal -> this / other.toFraction() - is Fraction -> this / other else -> throw IllegalStateException("I don't know what to do with ${other::class.qualifiedName} (right hand operand)") } } @@ -206,7 +154,7 @@ fun Number.dynDiv(other: Number): Number { } } -val Number.isFractional get() = this is Float || this is Double || this is Decimal || this is Fraction +val Number.isFractional get() = this is Float || this is Double || this is Decimal val Number.isWhole get() = !isFractional fun Number.toDecimal(): Decimal { @@ -218,7 +166,6 @@ fun Number.toDecimal(): Decimal { is Byte -> Decimal(this) is Short -> Decimal(this) is Long -> Decimal(this) - is Fraction -> this.toImpreciseFraction() else -> throw ClassCastException("Can not turn $this into ImpreciseFraction") } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Formatting.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Formatting.kt index 70dbdba68..e67b33a37 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Formatting.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Formatting.kt @@ -87,7 +87,6 @@ enum class SiPrefix( } val decimal = BigDecimal(string) - val fraction = Fraction(decimal) val impreciseFraction = Decimal(string) val integer = if (!fractional) BigInteger(string) else null diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Fraction.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Fraction.kt deleted file mode 100644 index 4733891a5..000000000 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Fraction.kt +++ /dev/null @@ -1,931 +0,0 @@ -package ru.dbotthepony.mc.otm.core - -import net.minecraft.nbt.ByteArrayTag -import net.minecraft.nbt.StringTag -import net.minecraft.nbt.Tag -import net.minecraft.network.FriendlyByteBuf -import org.apache.logging.log4j.LogManager -import java.math.BigDecimal -import java.math.BigInteger -import java.math.MathContext -import java.math.RoundingMode - -private fun powScale(int: Int): BigInteger { - if (int <= 0) - return BigInteger.ONE - - var result = BigInteger.TEN - - for (i in 2 .. int) - result *= BigInteger.TEN - - return result -} - -private fun powUnscaled(unscaled: BigInteger, scale: Int): BigInteger { - if (scale >= 0) - return unscaled - - var result = unscaled - - for (i in 2 .. -scale) - result *= BigInteger.TEN - - return result -} - -val DEFAULT_MATH_CONTEXT = MathContext(64, RoundingMode.HALF_UP) - -@Suppress("NOTHING_TO_INLINE") -private inline fun invertCompare(int: Int): Int { - if (int == 0) - return 0 - - return if (int < 0) 1 else -1 -} - -private val BI_MINUS_ZERO = BigInteger("-0") -private val BI_MINUS_ONE = BigInteger("-1") -private val BI_DIV = BigInteger.valueOf(1_000_000_000) -private val BI_DIV2 = BigInteger.valueOf(10) -private val BI_MINUS_DIV = BigInteger.valueOf(-1_000_000_000) - -private fun isZero(value: BigInteger): Boolean { - return when (value.signum()) { - 0 -> true - 1 -> value == BigInteger.ZERO - -1 -> value == BI_MINUS_ZERO - else -> false - } -} - -private fun isOne(value: BigInteger): Boolean { - return when (value.signum()) { - 0 -> false - 1 -> value == BigInteger.ONE - -1 -> value == BI_MINUS_ONE - else -> false - } -} - -private fun compactTwoMod(value1: BigInteger, value2: BigInteger, compact: Boolean = true): Fraction { - val mod = value1.divideAndRemainder(value2) - - if (isZero(mod[1])) - return Fraction(mod[0], compact = compact) - - return Fraction(value1, value2, compact = compact) -} - -private fun positive(value: BigInteger): BigInteger { - return when (value.signum()) { - 0 -> value - 1 -> value - -1 -> -value - else -> throw InternalError() - } -} - -private val GCDs = arrayOf( - BigInteger.valueOf(2), BigInteger.valueOf(3), BigInteger.valueOf(5), BigInteger.valueOf(7), BigInteger.valueOf(11), BigInteger.valueOf(13), BigInteger.valueOf(17), BigInteger.valueOf(19), BigInteger.valueOf(23), BigInteger.valueOf(29), BigInteger.valueOf(31), BigInteger.valueOf(37), BigInteger.valueOf(41), BigInteger.valueOf(43), BigInteger.valueOf(47), BigInteger.valueOf(53), BigInteger.valueOf(59), BigInteger.valueOf(61), BigInteger.valueOf(67), BigInteger.valueOf(71), - BigInteger.valueOf(73), BigInteger.valueOf(79), BigInteger.valueOf(83), BigInteger.valueOf(89), BigInteger.valueOf(97), BigInteger.valueOf(101), BigInteger.valueOf(103), BigInteger.valueOf(107), BigInteger.valueOf(109), BigInteger.valueOf(113), BigInteger.valueOf(127), BigInteger.valueOf(131), BigInteger.valueOf(137), BigInteger.valueOf(139), BigInteger.valueOf(149), BigInteger.valueOf(151), BigInteger.valueOf(157), BigInteger.valueOf(163), BigInteger.valueOf(167), BigInteger.valueOf(173), - BigInteger.valueOf(179), BigInteger.valueOf(181), BigInteger.valueOf(191), BigInteger.valueOf(193), BigInteger.valueOf(197), BigInteger.valueOf(199), BigInteger.valueOf(211), BigInteger.valueOf(223), BigInteger.valueOf(227), BigInteger.valueOf(229), BigInteger.valueOf(233), BigInteger.valueOf(239), BigInteger.valueOf(241), BigInteger.valueOf(251), BigInteger.valueOf(257), BigInteger.valueOf(263), BigInteger.valueOf(269), BigInteger.valueOf(271), BigInteger.valueOf(277), BigInteger.valueOf(281), - BigInteger.valueOf(283), BigInteger.valueOf(293), BigInteger.valueOf(307), BigInteger.valueOf(311), BigInteger.valueOf(313), BigInteger.valueOf(317), BigInteger.valueOf(331), BigInteger.valueOf(337), BigInteger.valueOf(347), BigInteger.valueOf(349), BigInteger.valueOf(353), BigInteger.valueOf(359), BigInteger.valueOf(367), BigInteger.valueOf(373), BigInteger.valueOf(379), BigInteger.valueOf(383), BigInteger.valueOf(389), BigInteger.valueOf(397), BigInteger.valueOf(401), BigInteger.valueOf(409), - BigInteger.valueOf(419), BigInteger.valueOf(421), BigInteger.valueOf(431), BigInteger.valueOf(433), BigInteger.valueOf(439), BigInteger.valueOf(443), BigInteger.valueOf(449), BigInteger.valueOf(457), BigInteger.valueOf(461), BigInteger.valueOf(463), BigInteger.valueOf(467), BigInteger.valueOf(479), BigInteger.valueOf(487), BigInteger.valueOf(491), BigInteger.valueOf(499), BigInteger.valueOf(503), BigInteger.valueOf(509), BigInteger.valueOf(521), BigInteger.valueOf(523), BigInteger.valueOf(541), - BigInteger.valueOf(547), BigInteger.valueOf(557), BigInteger.valueOf(563), BigInteger.valueOf(569), BigInteger.valueOf(571), BigInteger.valueOf(577), BigInteger.valueOf(587), BigInteger.valueOf(593), BigInteger.valueOf(599), BigInteger.valueOf(601), BigInteger.valueOf(607), BigInteger.valueOf(613), BigInteger.valueOf(617), BigInteger.valueOf(619), BigInteger.valueOf(631), BigInteger.valueOf(641), BigInteger.valueOf(643), BigInteger.valueOf(647), BigInteger.valueOf(653), BigInteger.valueOf(659), - BigInteger.valueOf(661), BigInteger.valueOf(673), BigInteger.valueOf(677), BigInteger.valueOf(683), BigInteger.valueOf(691), BigInteger.valueOf(701), BigInteger.valueOf(709), BigInteger.valueOf(719), BigInteger.valueOf(727), BigInteger.valueOf(733), BigInteger.valueOf(739), BigInteger.valueOf(743), BigInteger.valueOf(751), BigInteger.valueOf(757), BigInteger.valueOf(761), BigInteger.valueOf(769), BigInteger.valueOf(773), BigInteger.valueOf(787), BigInteger.valueOf(797), BigInteger.valueOf(809), - BigInteger.valueOf(811), BigInteger.valueOf(821), BigInteger.valueOf(823), BigInteger.valueOf(827), BigInteger.valueOf(829), BigInteger.valueOf(839), BigInteger.valueOf(853), BigInteger.valueOf(857), BigInteger.valueOf(859), BigInteger.valueOf(863), BigInteger.valueOf(877), BigInteger.valueOf(881), BigInteger.valueOf(883), BigInteger.valueOf(887), BigInteger.valueOf(907), BigInteger.valueOf(911), BigInteger.valueOf(919), BigInteger.valueOf(929), BigInteger.valueOf(937), BigInteger.valueOf(941), - BigInteger.valueOf(947), BigInteger.valueOf(953), BigInteger.valueOf(967), BigInteger.valueOf(971), BigInteger.valueOf(977), BigInteger.valueOf(983), BigInteger.valueOf(991), BigInteger.valueOf(997), BigInteger.valueOf(1009), BigInteger.valueOf(1013), BigInteger.valueOf(1019), BigInteger.valueOf(1021), BigInteger.valueOf(1031), BigInteger.valueOf(1033), BigInteger.valueOf(1039), BigInteger.valueOf(1049), BigInteger.valueOf(1051), BigInteger.valueOf(1061), BigInteger.valueOf(1063), BigInteger.valueOf(1069), - BigInteger.valueOf(1087), BigInteger.valueOf(1091), BigInteger.valueOf(1093), BigInteger.valueOf(1097), BigInteger.valueOf(1103), BigInteger.valueOf(1109), BigInteger.valueOf(1117), BigInteger.valueOf(1123), BigInteger.valueOf(1129), BigInteger.valueOf(1151), BigInteger.valueOf(1153), BigInteger.valueOf(1163), BigInteger.valueOf(1171), BigInteger.valueOf(1181), BigInteger.valueOf(1187), BigInteger.valueOf(1193), BigInteger.valueOf(1201), BigInteger.valueOf(1213), BigInteger.valueOf(1217), BigInteger.valueOf(1223), - BigInteger.valueOf(1229), BigInteger.valueOf(1231), BigInteger.valueOf(1237), BigInteger.valueOf(1249), BigInteger.valueOf(1259), BigInteger.valueOf(1277), BigInteger.valueOf(1279), BigInteger.valueOf(1283), BigInteger.valueOf(1289), BigInteger.valueOf(1291), BigInteger.valueOf(1297), BigInteger.valueOf(1301), BigInteger.valueOf(1303), BigInteger.valueOf(1307), BigInteger.valueOf(1319), BigInteger.valueOf(1321), BigInteger.valueOf(1327), BigInteger.valueOf(1361), BigInteger.valueOf(1367), BigInteger.valueOf(1373), - BigInteger.valueOf(1381), BigInteger.valueOf(1399), BigInteger.valueOf(1409), BigInteger.valueOf(1423), BigInteger.valueOf(1427), BigInteger.valueOf(1429), BigInteger.valueOf(1433), BigInteger.valueOf(1439), BigInteger.valueOf(1447), BigInteger.valueOf(1451), BigInteger.valueOf(1453), BigInteger.valueOf(1459), BigInteger.valueOf(1471), BigInteger.valueOf(1481), BigInteger.valueOf(1483), BigInteger.valueOf(1487), BigInteger.valueOf(1489), BigInteger.valueOf(1493), BigInteger.valueOf(1499), BigInteger.valueOf(1511), - BigInteger.valueOf(1523), BigInteger.valueOf(1531), BigInteger.valueOf(1543), BigInteger.valueOf(1549), BigInteger.valueOf(1553), BigInteger.valueOf(1559), BigInteger.valueOf(1567), BigInteger.valueOf(1571), BigInteger.valueOf(1579), BigInteger.valueOf(1583), BigInteger.valueOf(1597), BigInteger.valueOf(1601), BigInteger.valueOf(1607), BigInteger.valueOf(1609), BigInteger.valueOf(1613), BigInteger.valueOf(1619), BigInteger.valueOf(1621), BigInteger.valueOf(1627), BigInteger.valueOf(1637), BigInteger.valueOf(1657), - BigInteger.valueOf(1663), BigInteger.valueOf(1667), BigInteger.valueOf(1669), BigInteger.valueOf(1693), BigInteger.valueOf(1697), BigInteger.valueOf(1699), BigInteger.valueOf(1709), BigInteger.valueOf(1721), BigInteger.valueOf(1723), BigInteger.valueOf(1733), BigInteger.valueOf(1741), BigInteger.valueOf(1747), BigInteger.valueOf(1753), BigInteger.valueOf(1759), BigInteger.valueOf(1777), BigInteger.valueOf(1783), BigInteger.valueOf(1787), BigInteger.valueOf(1789), BigInteger.valueOf(1801), BigInteger.valueOf(1811), - BigInteger.valueOf(1823), BigInteger.valueOf(1831), BigInteger.valueOf(1847), BigInteger.valueOf(1861), BigInteger.valueOf(1867), BigInteger.valueOf(1871), BigInteger.valueOf(1873), BigInteger.valueOf(1877), BigInteger.valueOf(1879), BigInteger.valueOf(1889), BigInteger.valueOf(1901), BigInteger.valueOf(1907), BigInteger.valueOf(1913), BigInteger.valueOf(1931), BigInteger.valueOf(1933), BigInteger.valueOf(1949), BigInteger.valueOf(1951), BigInteger.valueOf(1973), BigInteger.valueOf(1979), BigInteger.valueOf(1987), - BigInteger.valueOf(1993), BigInteger.valueOf(1997), BigInteger.valueOf(1999), BigInteger.valueOf(2003), BigInteger.valueOf(2011), BigInteger.valueOf(2017), BigInteger.valueOf(2027), BigInteger.valueOf(2029), BigInteger.valueOf(2039), BigInteger.valueOf(2053), BigInteger.valueOf(2063), BigInteger.valueOf(2069), BigInteger.valueOf(2081), BigInteger.valueOf(2083), BigInteger.valueOf(2087), BigInteger.valueOf(2089), BigInteger.valueOf(2099), BigInteger.valueOf(2111), BigInteger.valueOf(2113), BigInteger.valueOf(2129), - BigInteger.valueOf(2131), BigInteger.valueOf(2137), BigInteger.valueOf(2141), BigInteger.valueOf(2143), BigInteger.valueOf(2153), BigInteger.valueOf(2161), BigInteger.valueOf(2179), BigInteger.valueOf(2203), BigInteger.valueOf(2207), BigInteger.valueOf(2213), BigInteger.valueOf(2221), BigInteger.valueOf(2237), BigInteger.valueOf(2239), BigInteger.valueOf(2243), BigInteger.valueOf(2251), BigInteger.valueOf(2267), BigInteger.valueOf(2269), BigInteger.valueOf(2273), BigInteger.valueOf(2281), BigInteger.valueOf(2287), - BigInteger.valueOf(2293), BigInteger.valueOf(2297), BigInteger.valueOf(2309), BigInteger.valueOf(2311), BigInteger.valueOf(2333), BigInteger.valueOf(2339), BigInteger.valueOf(2341), BigInteger.valueOf(2347), BigInteger.valueOf(2351), BigInteger.valueOf(2357), BigInteger.valueOf(2371), BigInteger.valueOf(2377), BigInteger.valueOf(2381), BigInteger.valueOf(2383), BigInteger.valueOf(2389), BigInteger.valueOf(2393), BigInteger.valueOf(2399), BigInteger.valueOf(2411), BigInteger.valueOf(2417), BigInteger.valueOf(2423), - BigInteger.valueOf(2437), BigInteger.valueOf(2441), BigInteger.valueOf(2447), BigInteger.valueOf(2459), BigInteger.valueOf(2467), BigInteger.valueOf(2473), BigInteger.valueOf(2477), BigInteger.valueOf(2503), BigInteger.valueOf(2521), BigInteger.valueOf(2531), BigInteger.valueOf(2539), BigInteger.valueOf(2543), BigInteger.valueOf(2549), BigInteger.valueOf(2551), BigInteger.valueOf(2557), BigInteger.valueOf(2579), BigInteger.valueOf(2591), BigInteger.valueOf(2593), BigInteger.valueOf(2609), BigInteger.valueOf(2617), - BigInteger.valueOf(2621), BigInteger.valueOf(2633), BigInteger.valueOf(2647), BigInteger.valueOf(2657), BigInteger.valueOf(2659), BigInteger.valueOf(2663), BigInteger.valueOf(2671), BigInteger.valueOf(2677), BigInteger.valueOf(2683), BigInteger.valueOf(2687), BigInteger.valueOf(2689), BigInteger.valueOf(2693), BigInteger.valueOf(2699), BigInteger.valueOf(2707), BigInteger.valueOf(2711), BigInteger.valueOf(2713), BigInteger.valueOf(2719), BigInteger.valueOf(2729), BigInteger.valueOf(2731), BigInteger.valueOf(2741), - BigInteger.valueOf(2749), BigInteger.valueOf(2753), BigInteger.valueOf(2767), BigInteger.valueOf(2777), BigInteger.valueOf(2789), BigInteger.valueOf(2791), BigInteger.valueOf(2797), BigInteger.valueOf(2801), BigInteger.valueOf(2803), BigInteger.valueOf(2819), BigInteger.valueOf(2833), BigInteger.valueOf(2837), BigInteger.valueOf(2843), BigInteger.valueOf(2851), BigInteger.valueOf(2857), BigInteger.valueOf(2861), BigInteger.valueOf(2879), BigInteger.valueOf(2887), BigInteger.valueOf(2897), BigInteger.valueOf(2903), - BigInteger.valueOf(2909), BigInteger.valueOf(2917), BigInteger.valueOf(2927), BigInteger.valueOf(2939), BigInteger.valueOf(2953), BigInteger.valueOf(2957), BigInteger.valueOf(2963), BigInteger.valueOf(2969), BigInteger.valueOf(2971), BigInteger.valueOf(2999), BigInteger.valueOf(3001), BigInteger.valueOf(3011), BigInteger.valueOf(3019), BigInteger.valueOf(3023), BigInteger.valueOf(3037), BigInteger.valueOf(3041), BigInteger.valueOf(3049), BigInteger.valueOf(3061), BigInteger.valueOf(3067), BigInteger.valueOf(3079), - BigInteger.valueOf(3083), BigInteger.valueOf(3089), BigInteger.valueOf(3109), BigInteger.valueOf(3119), BigInteger.valueOf(3121), BigInteger.valueOf(3137), BigInteger.valueOf(3163), BigInteger.valueOf(3167), BigInteger.valueOf(3169), BigInteger.valueOf(3181), BigInteger.valueOf(3187), BigInteger.valueOf(3191), BigInteger.valueOf(3203), BigInteger.valueOf(3209), BigInteger.valueOf(3217), BigInteger.valueOf(3221), BigInteger.valueOf(3229), BigInteger.valueOf(3251), BigInteger.valueOf(3253), BigInteger.valueOf(3257), - BigInteger.valueOf(3259), BigInteger.valueOf(3271), BigInteger.valueOf(3299), BigInteger.valueOf(3301), BigInteger.valueOf(3307), BigInteger.valueOf(3313), BigInteger.valueOf(3319), BigInteger.valueOf(3323), BigInteger.valueOf(3329), BigInteger.valueOf(3331), BigInteger.valueOf(3343), BigInteger.valueOf(3347), BigInteger.valueOf(3359), BigInteger.valueOf(3361), BigInteger.valueOf(3371), BigInteger.valueOf(3373), BigInteger.valueOf(3389), BigInteger.valueOf(3391), BigInteger.valueOf(3407), BigInteger.valueOf(3413), - BigInteger.valueOf(3433), BigInteger.valueOf(3449), BigInteger.valueOf(3457), BigInteger.valueOf(3461), BigInteger.valueOf(3463), BigInteger.valueOf(3467), BigInteger.valueOf(3469), BigInteger.valueOf(3491), BigInteger.valueOf(3499), BigInteger.valueOf(3511), BigInteger.valueOf(3517), BigInteger.valueOf(3527), BigInteger.valueOf(3529), BigInteger.valueOf(3533), BigInteger.valueOf(3539), BigInteger.valueOf(3541), BigInteger.valueOf(3547), BigInteger.valueOf(3557), BigInteger.valueOf(3559), BigInteger.valueOf(3571), -) - -private val COMPACT_THRESHOLD = BigInteger("1000000000000000000000") - -@Suppress("NAME_SHADOWING") -private fun compactTwo(value1: BigInteger, value2: BigInteger, compact: Boolean = true): Fraction { - when (value1.signum()) { - 0 -> return Fraction(value1, value2, compact = compact) - 1 -> if (value1 < BI_DIV) return compactTwoMod(value1, value2, compact) - -1 -> if (value1 > BI_MINUS_DIV) return compactTwoMod(value1, value2, compact) - } - - when (value2.signum()) { - 0 -> return Fraction(value1, value2, compact = compact) - 1 -> if (value2 < BI_DIV) return compactTwoMod(value1, value2, compact) - -1 -> if (value2 > BI_MINUS_DIV) return compactTwoMod(value1, value2, compact) - } - - var value1 = value1 - var value2 = value2 - - while (true) { - val _a = value1.divideAndRemainder(BI_DIV) - val _b = value2.divideAndRemainder(BI_DIV) - - if (!isZero(_a[1]) || !isZero(_b[1])) - break - - value1 = _a[0] - value2 = _b[0] - } - - while (true) { - val _a = value1.divideAndRemainder(BI_DIV2) - val _b = value2.divideAndRemainder(BI_DIV2) - - if (!isZero(_a[1]) || !isZero(_b[1])) - break - - value1 = _a[0] - value2 = _b[0] - } - - val p1 = positive(value1) - val p2 = positive(value2) - - if (p1 > COMPACT_THRESHOLD || p2 > COMPACT_THRESHOLD) { - var hit = true - - while (hit) { - hit = false - - for (i in GCDs.size - 1 downTo 0) { - val div = GCDs[i] - - while (true) { - val _a = value1.divideAndRemainder(div) - val _b = value2.divideAndRemainder(div) - - if (!isZero(_a[1]) || !isZero(_b[1])) - break - - value1 = _a[0] - value2 = _b[0] - hit = true - } - } - } - } else if (p1 > GCDs[0] && p2 > GCDs[0]) { - var hit = true - - while (hit) { - hit = false - - for (i in 4 downTo 0) { - val div = GCDs[i] - - while (true) { - val _a = value1.divideAndRemainder(div) - val _b = value2.divideAndRemainder(div) - - if (!isZero(_a[1]) || !isZero(_b[1])) - break - - value1 = _a[0] - value2 = _b[0] - hit = true - } - } - } - } - - return compactTwoMod(value1, value2, compact) -} - -private fun unsignedInt(value: Byte): Int { - if (value < 0) { - return 256 + value - } - - return value.toInt() -} - -private data class MagnitudeCrunchResult(val value: BigInteger, val mag: Int) - -@Suppress("NAME_SHADOWING") -private fun magnitude(value: BigInteger): MagnitudeCrunchResult { - if (isZero(value)) - return MagnitudeCrunchResult(value, 0) - - var mag = 0 - var value = value - - while (true) { - val c = value.divideAndRemainder(BI_DIV2) - - if (!isZero(c[1])) - break - - value = c[0] - mag++ - } - - return MagnitudeCrunchResult(value, mag) -} - -@Suppress("unused") -class Fraction @JvmOverloads constructor( - val value: BigInteger, - val divisor: BigInteger = BigInteger.ONE, - val compact: Boolean = true -) : Number(), Comparable { - @JvmOverloads constructor(value: Long, compact: Boolean = true) : this(BigInteger.valueOf(value), compact = compact) - @JvmOverloads constructor(value: Int, compact: Boolean = true) : this(BigInteger.valueOf(value.toLong()), compact = compact) - @JvmOverloads constructor(value: Float, compact: Boolean = true) : this(BigDecimal(value.toString()), compact = compact) - @JvmOverloads constructor(value: Double, compact: Boolean = true) : this(BigDecimal(value.toString()), compact = compact) - @JvmOverloads constructor(value: String, compact: Boolean = true) : this(BigDecimal(value), compact = compact) - - @JvmOverloads constructor(value: Long, div: Long, compact: Boolean = true) : this(BigInteger.valueOf(value), BigInteger.valueOf(div), compact = compact) - @JvmOverloads constructor(value: Int, div: Int, compact: Boolean = true) : this(BigInteger.valueOf(value.toLong()), BigInteger.valueOf(div.toLong()), compact = compact) - @JvmOverloads constructor(value: Float, div: Float, compact: Boolean = true) : this(BigDecimal(value.toString()), BigDecimal(div.toString()), compact = compact) - @JvmOverloads constructor(value: Double, div: Double, compact: Boolean = true) : this(BigDecimal(value.toString()), BigDecimal(div.toString()), compact = compact) - @JvmOverloads constructor(value: String, div: String, compact: Boolean = true) : this(BigDecimal(value), BigDecimal(div), compact = compact) - - @JvmOverloads constructor(value: BigDecimal, compact: Boolean = true) : this(powUnscaled(value.unscaledValue(), value.scale()), powScale(value.scale()), compact = compact) - @JvmOverloads constructor(value: BigDecimal, div: BigDecimal, compact: Boolean = true) : this(powUnscaled(value.unscaledValue(), value.scale()).multiply(powScale(div.scale())), powScale(value.scale()).multiply(powUnscaled(div.unscaledValue(), div.scale())), compact = compact) - - override fun equals(other: Any?): Boolean { - if (other is Fraction) { - if (other.divisor == divisor) - return other.value == value - - val a = value * other.divisor - val b = other.value * divisor - - return a == b - } - - return false - } - - override fun hashCode(): Int { - return 31 * value.hashCode() + divisor.hashCode() - } - - fun compactAndCanonize(): Fraction { - if (value == BigInteger.ZERO || value == BigInteger.ONE || divisor == BigInteger.ONE) - return this - - val a = signnum() - val b = value.signum() - val c = divisor.signum() - - if (a != b && a != c) { - if (isZero(divisor)) - return Fraction(-value, -divisor) - - val mod = value.divideAndRemainder(divisor) - - if (isZero(mod[1])) - return Fraction(mod[0], compact = compact) - - return Fraction(-value, -divisor) - } - - if (isZero(divisor)) - return this - - return compactTwo(value, divisor, compact) - } - - fun isZero(): Boolean { - if (isNaN()) return false - return value == BigInteger.ZERO - } - - fun isOne(): Boolean { - if (isNaN()) return false - return value != BigInteger.ZERO && value == divisor - } - - fun compact(): Fraction { - if (isNaN()) return this - - if (value == BigInteger.ZERO || value == BigInteger.ONE || divisor == BigInteger.ONE) - return this - - if (divisor == BigInteger.ZERO || divisor == BI_MINUS_ZERO) - return this - - return compactTwo(value, divisor, compact) - } - - fun canonize(): Fraction { - if (isNaN()) return this - - if (value == BigInteger.ZERO || value == BigInteger.ONE || divisor == BigInteger.ONE || divisor == BigInteger.ZERO) - return this - - val a = signnum() - val b = value.signum() - val c = divisor.signum() - - if (a != b && a != c) - return Fraction(-value, -divisor) - - return this - } - - // Операторы - fun equalsCompact(other: Fraction?): Boolean { - if (other == null) - return false - - if (isNaN() || other.isNaN()) return false - - val a = compact() - val b = other.compact() - - return a.value == b.value && a.divisor == b.divisor - } - - override operator fun compareTo(other: Fraction): Int { - if (isNaN() || other.isNaN()) return 0 - - if (divisor == other.divisor) - return value.compareTo(other.value) - - val a = signnum() - val b = other.signnum() - - if (a == b && a == 0) - return 0 - - if (a < b) - return -1 - else if (a > b) - return 1 - - val cmp = (value * other.divisor).compareTo(other.value * divisor) - - if (a != value.signum() && a != divisor.signum()) { - if (b != other.value.signum() && b != other.divisor.signum()) { - return cmp - } - - return invertCompare(cmp) - } else if (b != other.value.signum() && b != other.divisor.signum()) { - return invertCompare(cmp) - } - - return cmp - } - - private fun plusCompact(other: Fraction): Fraction { - if (isNaN()) return this - - if (divisor == other.divisor) { - if (isOne(divisor)) - return Fraction(value + other.value, divisor) - - val new = value + other.value - - if (new.compareTo(BigInteger.ZERO) == 0) - return ZERO - - if (divisor == BigInteger.ZERO || divisor == BI_MINUS_ZERO) - return Fraction(new, divisor) - - return compactTwo(new, divisor) - } - - val new = value * other.divisor + other.value * divisor - val div = divisor * other.divisor - - if (div == BigInteger.ZERO || div == BI_MINUS_ZERO) - return Fraction(new, div) - - return compactTwo(new, div) - } - - operator fun plus(other: Fraction): Fraction { - if (isNaN()) return this - if (other.isNaN()) return other - - if (compact) - return plusCompact(other) - - if (divisor == other.divisor) { - return Fraction(value + other.value, divisor, compact = false) - } - - return Fraction(value * other.divisor + other.value * divisor, divisor * other.divisor, compact = false) - } - - private fun minusCompact(other: Fraction): Fraction { - if (divisor == other.divisor) { - if (isOne(divisor)) - return Fraction(value - other.value, divisor) - - val new = value - other.value - - if (new.compareTo(BigInteger.ZERO) == 0) - return ZERO - - if (divisor == BigInteger.ZERO || divisor == BI_MINUS_ZERO) - return Fraction(new, divisor) - - return compactTwo(new, divisor) - } - - val new = value * other.divisor - other.value * divisor - val div = divisor * other.divisor - - if (div == BigInteger.ZERO || div == BI_MINUS_ZERO) - return Fraction(new, div) - - return compactTwo(new, div) - } - - operator fun minus(other: Fraction): Fraction { - if (isNaN()) return this - if (other.isNaN()) return other - - if (compact) - return minusCompact(other) - - if (divisor == other.divisor) { - return Fraction(value - other.value, divisor, compact = false) - } - - return Fraction(value * other.divisor - other.value * divisor, divisor * other.divisor, compact = false) - } - - private fun timesCompact(other: Fraction): Fraction { - val new = value * other.value - - if (new.compareTo(BigInteger.ZERO) == 0) - return ZERO - - val div = divisor * other.divisor - - if (div == BigInteger.ZERO || div == BI_MINUS_ZERO) - return Fraction(new, div) - - return compactTwo(new, div) - } - - operator fun times(other: Fraction): Fraction { - if (isNaN()) return this - if (other.isNaN()) return other - if (other.isOne()) return this - - if (compact) - return timesCompact(other) - - return Fraction(value * other.value, divisor * other.divisor, compact = false) - } - - private fun divCompact(other: Fraction): Fraction { - val new = value * other.divisor - - if (new.compareTo(BigInteger.ZERO) == 0) - return ZERO - - val div = divisor * other.value - - if (div == BigInteger.ZERO || div == BI_MINUS_ZERO) - return Fraction(new, div) - - return compactTwo(new, div) - } - - operator fun div(other: Fraction): Fraction { - if (isNaN()) return this - if (other.isNaN()) return other - if (other.isOne()) return this - - if (compact) - return divCompact(other) - - return Fraction(value * other.divisor, divisor * other.value, compact = false) - } - - operator fun unaryMinus(): Fraction { - if (isNaN()) return this - return Fraction(-value, divisor) - } - - operator fun unaryPlus(): Fraction { - return this - } - - operator fun plus(other: Float): Fraction = plus(Fraction(other)) - operator fun minus(other: Float): Fraction = minus(Fraction(other)) - operator fun times(other: Float): Fraction = times(Fraction(other)) - operator fun div(other: Float): Fraction = div(Fraction(other)) - - operator fun plus(other: Double): Fraction = plus(Fraction(other)) - operator fun minus(other: Double): Fraction = minus(Fraction(other)) - operator fun times(other: Double): Fraction = times(Fraction(other)) - operator fun div(other: Double): Fraction = div(Fraction(other)) - - // может вызвать путаницу - /* - operator fun plus(other: BigDecimal): Fraction = plus(Fraction(other)) - operator fun minus(other: BigDecimal): Fraction = minus(Fraction(other)) - operator fun times(other: BigDecimal): Fraction = times(Fraction(other)) - operator fun div(other: BigDecimal): Fraction = div(Fraction(other)) - - operator fun plus(other: BigInteger): Fraction = plus(Fraction(other)) - operator fun minus(other: BigInteger): Fraction = minus(Fraction(other)) - operator fun times(other: BigInteger): Fraction = times(Fraction(other)) - operator fun div(other: BigInteger): Fraction = div(Fraction(other)) - */ - - operator fun plus(other: Int): Fraction = plus(Fraction(other)) - operator fun minus(other: Int): Fraction = minus(Fraction(other)) - operator fun times(other: Int): Fraction = times(Fraction(other)) - operator fun div(other: Int): Fraction = div(Fraction(other)) - - operator fun plus(other: Long): Fraction = plus(Fraction(other)) - operator fun minus(other: Long): Fraction = minus(Fraction(other)) - operator fun times(other: Long): Fraction = times(Fraction(other)) - operator fun div(other: Long): Fraction = div(Fraction(other)) - - /* - fun add(other: Float): Fraction = plus(Fraction(other)) - fun subtract(other: Float): Fraction = minus(Fraction(other)) - fun multiply(other: Float): Fraction = times(Fraction(other)) - fun divide(other: Float): Fraction = div(Fraction(other)) - - fun add(other: Double): Fraction = plus(Fraction(other)) - fun subtract(other: Double): Fraction = minus(Fraction(other)) - fun multiply(other: Double): Fraction = times(Fraction(other)) - fun divide(other: Double): Fraction = div(Fraction(other)) - - fun add(other: Int): Fraction = plus(Fraction(other)) - fun subtract(other: Int): Fraction = minus(Fraction(other)) - fun multiply(other: Int): Fraction = times(Fraction(other)) - fun divide(other: Int): Fraction = div(Fraction(other)) - - fun add(other: Long): Fraction = plus(Fraction(other)) - fun subtract(other: Long): Fraction = minus(Fraction(other)) - fun multiply(other: Long): Fraction = times(Fraction(other)) - fun divide(other: Long): Fraction = div(Fraction(other)) - - fun add(other: Fraction): Fraction = plus(other) - fun subtract(other: Fraction): Fraction = minus(other) - fun multiply(other: Fraction): Fraction = times(other) - fun divide(other: Fraction): Fraction = div(other) - */ - - operator fun rem(other: Fraction): Fraction { - if (isNaN()) return this - if (other.isNaN()) return other - return Fraction((this / other).wholePart()) - } - - // Преобразования - override fun toFloat(): Float { - if (isNaN()) return Float.NaN - return (value / divisor).toFloat() + ((value % divisor).toFloat() / divisor.toFloat()) - } - - override fun toDouble(): Double { - if (isNaN()) return Double.NaN - return (value / divisor).toDouble() + ((value % divisor).toDouble() / divisor.toDouble()) - } - - fun toImpreciseFraction(): Decimal { - if (isNaN()) - return Decimal.NaN - - val div = value.divideAndRemainder(divisor) - val a = divisor.toDouble() - val b = div[1].toDouble() - - if (b == 0.0 || !a.isFinite() || !b.isFinite()) { - return Decimal(div[0]) - } - - val div2 = a / b - - if (div2.isFinite() && !div2.isNaN()) { - return Decimal(div[0], div2) - } - - return Decimal(div[0]) - } - - fun toByteArray(): ByteArray { - if (isNaN()) { - return byteArrayOf(1, 0, 0, 0, 0, 1, 0, 0, 0, 0) - } - - val magValue = magnitude(value) - val magDiv = magnitude(divisor) - - val bytesA = magValue.value.toByteArray() - val bytesB = magDiv.value.toByteArray() - - return byteArrayOf( - (bytesA.size and 0xFF).toByte(), - (bytesA.size ushr 8).toByte(), - - (magValue.mag and 0xFF).toByte(), - (magValue.mag ushr 8).toByte(), - - *bytesA, - - (bytesB.size and 0xFF).toByte(), - (bytesB.size ushr 8).toByte(), - - (magDiv.mag and 0xFF).toByte(), - (magDiv.mag ushr 8).toByte(), - - *bytesB - ) - } - - override fun toInt(): Int { - if (isNaN()) throw ArithmeticException("Fraction is not a number") - return (value / divisor).toInt() - } - - override fun toLong(): Long { - if (isNaN()) throw ArithmeticException("Fraction is not a number") - return (value / divisor).toLong() - } - - override fun toByte(): Byte { - return toInt().toByte() - } - - override fun toChar(): Char { - return toInt().toChar() - } - - override fun toShort(): Short { - return toInt().toShort() - } - - @JvmOverloads - fun toBigDecimal(context: MathContext = DEFAULT_MATH_CONTEXT): BigDecimal { - if (isNaN()) throw ArithmeticException("Fraction is not a number") - return BigDecimal(value).divide(BigDecimal(divisor), context) - } - - // Утилиты - fun wholePart(): BigInteger { - if (isNaN()) throw ArithmeticException("Fraction is not a number") - return value / divisor - } - - fun fractionPart(): Fraction { - if (isNaN()) return this - return Fraction(value % divisor, divisor) - } - - fun modPart(): BigInteger { - if (isNaN()) throw ArithmeticException("Fraction is not a number") - return value % divisor - } - - override fun toString(): String { - if (isNaN()) return "NaN" - - return "$value/$divisor" - } - - fun formattedString(): String { - if (isNaN()) return "NaN" - return "${wholePart()} ${modPart()}/$divisor" - } - - @JvmOverloads - fun decimalString(nums: Int = 2, strict: Boolean = false): String { - if (isNaN()) return "NaN" - - val whole = wholePart() - val fraction = modPart() - - if (fraction == BigInteger.ZERO) { - if (!strict) - return whole.toString() - - return "${whole}.${"0".repeat(nums)}" - } - - var strdec = (fraction.toDouble() / divisor.toDouble()).toString() - - if (strdec.length == 1) { - strdec = "" - - if (strict) { - strdec = "0".repeat(nums) - } - } else { - strdec = strdec.substring(2) - - if (strict && strdec.length < nums) { - strdec += "0".repeat(nums - strdec.length) - } else if (strdec.length > nums) { - strdec = strdec.substring(0, nums) - } - } - - if (strdec == "") { - return whole.toString() - } - - return "${whole}.$strdec" - } - - fun signnum(): Int { - if (isNaN()) return 0 - - val a = value.signum() - val b = divisor.signum() - - if (a == b) { - if (a == 0) - return 0 - - return 1 - } - - return -1 - } - - fun serializeNBT(): ByteArrayTag { - return ByteArrayTag(toByteArray()) - } - - fun isNaN(): Boolean { - return divisor == BigInteger.ZERO - } - - fun write(buff: FriendlyByteBuf) { - buff.writeByteArray(toByteArray()) - } - - fun max(vararg others: Fraction): Fraction { - var max = this - - for (other in others) { - if (max < other) { - max = other - } - } - - return max - } - - fun min(vararg others: Fraction): Fraction { - var min = this - - for (other in others) { - if (min > other) { - min = other - } - } - - return min - } - - fun moreThanZero(): Fraction { - if (signnum() >= 0) - return this - - return ZERO - } - - fun lessOrZero(): Fraction { - if (signnum() <= 0) - return this - - return ZERO - } - - // Позволяет получить процент от деления данного на divisor с точностью до 5 цифр - fun percentage(divisor: Fraction, zeroing: Boolean = true): Float { - if (divisor.isZero() && zeroing) return 0f - if (isNaN() || divisor.isNaN()) return Float.NaN - - val mul = (this * TEN_THOUSAND) / divisor - if (mul.isNaN()) return Float.NaN - - return mul.wholePart().toFloat() / 10_000f - } - - companion object { - private val LOGGER = LogManager.getLogger() - - @JvmField - val ZERO = Fraction(BigInteger.ZERO) - - @JvmField - val NaN = Fraction(BigInteger.ZERO, BigInteger.ZERO) - - @JvmField - val ONE_HUNDRED = Fraction(BigInteger.valueOf(100)) - - @JvmField - val TEN_THOUSAND = Fraction(BigInteger.valueOf(10_000)) - - @JvmField - val HALF = Fraction(BigInteger.ONE, BigInteger.TWO) - - @JvmField - val THIRD = Fraction(BigInteger.ONE, BigInteger.valueOf(3)) - - @JvmField - val ONE = Fraction(BigInteger.ONE) - - @JvmField - val TWO = Fraction(BigInteger.TWO) - - @JvmField - val MINUS_ONE = Fraction(-1) - - @JvmField - val TEN = Fraction(BigInteger.TEN) - - @JvmField - val INT_MAX_VALUE = Fraction(Int.MAX_VALUE) - - @JvmField - val LONG_MAX_VALUE = Fraction(Long.MAX_VALUE) - - @JvmStatic - fun read(buff: FriendlyByteBuf): Fraction { - return fromByteArray(buff.readByteArray()) - } - - @JvmStatic - fun fromByteArray(bytes: ByteArray): Fraction { - try { - val rangeA = unsignedInt(bytes[0]) or (unsignedInt(bytes[1]) shl 8) - val magA = unsignedInt(bytes[2]) or (unsignedInt(bytes[3]) shl 8) - - val bytesA = bytes.copyOfRange(4, 4 + rangeA) - - val offsetB = 4 + rangeA - - val rangeB = unsignedInt(bytes[offsetB]) or (unsignedInt(bytes[offsetB + 1]) shl 8) - val magB = unsignedInt(bytes[offsetB + 2]) or (unsignedInt(bytes[offsetB + 3]) shl 8) - - val bytesB = bytes.copyOfRange(offsetB + 4, offsetB + 4 + rangeB) - - if (bytesB.isEmpty()) - return NaN - - return Fraction(if (bytesA.isNotEmpty()) (BigInteger(bytesA) * BigInteger("1" + "0".repeat(magA))) else BigInteger.ZERO, BigInteger(bytesB) * BigInteger("1" + "0".repeat(magB))) - } catch(err: Throwable) { - LOGGER.error("Unable to load Fraction from byte array", err) - return NaN - } - } - - @JvmStatic - fun deserializeNBT(bytesTag: ByteArrayTag): Fraction { - val bytes = bytesTag.asByteArray - return fromByteArray(bytes) - } - - // Преобразование строки в дробь с подавлением ошибок форматирования - @JvmStatic - fun fromString(str: String): Fraction { - try { - return Fraction(BigDecimal(str)) - } catch(err: Throwable) { - return ZERO - } - } - - @JvmStatic - fun deserializeNBT(tag: Tag?): Fraction { - if (tag == null) - return ZERO - - if (tag is ByteArrayTag) - return deserializeNBT(tag) - - if (tag is StringTag) - return try { - fromString(tag.asString) - } catch (anything: Throwable) { - ZERO - } - - return ZERO - } - } -} - diff --git a/src/test/kotlin/ru/dbotthepony/mc/otm/tests/FractionTests.kt b/src/test/kotlin/ru/dbotthepony/mc/otm/tests/FractionTests.kt deleted file mode 100644 index e51fa3924..000000000 --- a/src/test/kotlin/ru/dbotthepony/mc/otm/tests/FractionTests.kt +++ /dev/null @@ -1,345 +0,0 @@ -package ru.dbotthepony.mc.otm.tests - -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import ru.dbotthepony.mc.otm.core.Fraction -import ru.dbotthepony.mc.otm.core.Decimal -import java.math.BigDecimal -import java.math.BigInteger - -object FractionTests { - @Test - @DisplayName("Fraction declaration") - fun declaration() { - println("BigInteger 1/1 == ${Fraction(BigInteger.valueOf(1), BigInteger.valueOf(1))}") - println("BigDecimal 1 == ${Fraction(BigDecimal("1"))}") - - var one = BigDecimal("1.00") - - println("Unscaled ${one.unscaledValue()} with scale ${one.scale()} of $one") - println("BigDecimal $one == ${Fraction(one)}") - assert(Fraction(one).compareTo(Fraction.ONE) == 0) - - one = BigDecimal("1.0000") - - println("Unscaled ${one.unscaledValue()} with scale ${one.scale()} of $one") - println("BigDecimal $one == ${Fraction(one)}") - assert(Fraction(one).compareTo(Fraction.ONE) == 0) - - println("1/2 == ${Fraction(1, 2)}") - println("-1/2 == ${Fraction(-1, 2)}") - println("1/-2 == ${Fraction(1, -2)}") - println("-1/-2 == ${Fraction(-1, -2)}") - - println("canonical 1/2 == ${Fraction(1, 2).canonize()}") - println("canonical -1/2 == ${Fraction(-1, 2).canonize()}") - println("canonical 1/-2 == ${Fraction(1, -2).canonize()}") - println("canonical -1/-2 == ${Fraction(-1, -2).canonize()}") - } - - @Test - @DisplayName("Fraction NaN behavior") - fun nan() { - assert((Fraction.NaN + Fraction.ONE).isNaN()) - assert((Fraction.NaN - Fraction.ONE).isNaN()) - assert((Fraction.NaN / Fraction.ONE).isNaN()) - assert((Fraction.NaN * Fraction.ONE).isNaN()) - - assert((Fraction.ONE + Fraction.NaN).isNaN()) - assert((Fraction.ONE - Fraction.NaN).isNaN()) - assert((Fraction.ONE / Fraction.NaN).isNaN()) - assert((Fraction.ONE * Fraction.NaN).isNaN()) - - assert((Fraction.ONE / Fraction.ZERO).isNaN()) - assert((Fraction.ONE * Fraction(0, 0)).isNaN()) - assert((Fraction.ONE * Fraction(1, 0)).isNaN()) - - assert(Fraction.NaN.toFloat().isNaN()) - assert(Fraction.NaN.toDouble().isNaN()) - - assertThrows {Fraction.NaN.toInt()} - assertThrows {Fraction.NaN.toLong()} - assertThrows {Fraction.NaN.wholePart()} - - assert(Fraction.fromByteArray(Fraction.NaN.toByteArray()).isNaN()) - assert(Fraction.NaN.percentage(Fraction.ONE).isNaN()) - } - - @Test - @DisplayName("Fraction comparison") - fun equality() { - assert(Fraction(1).compareTo(Fraction.ONE) == 0) - assert(Fraction(1.0).compareTo(Fraction.ONE) == 0) - assert(Fraction(1.0F).compareTo(Fraction.ONE) == 0) - assert(Fraction(1, 1).compareTo(Fraction.ONE) == 0) - assert(Fraction(1.0, 1.0).compareTo(Fraction.ONE) == 0) - assert(Fraction(1.0F, 1.0F).compareTo(Fraction.ONE) == 0) - - assert(Fraction(1, 2).compareTo(Fraction.HALF) == 0) - assert(Fraction(1.0, 2.0).compareTo(Fraction.HALF) == 0) - assert(Fraction(1.0F, 2.0F).compareTo(Fraction.HALF) == 0) - - assert(Fraction(-1, -2).compareTo(Fraction.HALF) == 0) - assert(Fraction(-1.0, -2.0).compareTo(Fraction.HALF) == 0) - assert(Fraction(-1.0F, -2.0F).compareTo(Fraction.HALF) == 0) - - assert(Fraction(-1, 2).compareTo(Fraction.HALF) != 0) - assert(Fraction(-1.0, 2.0).compareTo(Fraction.HALF) != 0) - assert(Fraction(-1.0F, 2.0F).compareTo(Fraction.HALF) != 0) - - assert(Fraction(1, 2).compareTo(Fraction.ONE) != 0) - assert(Fraction(1.0, 2.0).compareTo(Fraction.ONE) != 0) - assert(Fraction(1.0F, 2.0F).compareTo(Fraction.ONE) != 0) - - assert(Fraction(2).compareTo(Fraction.TWO) == 0) - assert(Fraction(2.0).compareTo(Fraction.TWO) == 0) - assert(Fraction(2.0F).compareTo(Fraction.TWO) == 0) - assert(Fraction(2, 1).compareTo(Fraction.TWO) == 0) - assert(Fraction(2.0, 1.0).compareTo(Fraction.TWO) == 0) - assert(Fraction(2.0F, 1.0F).compareTo(Fraction.TWO) == 0) - - assert(Fraction(4, 3) > Fraction(5, 4)) - assert(Fraction(4, 4) < Fraction(5, 4)) - - assert(Fraction(-15, 2) < Fraction(2, 4)) - assert(Fraction(-1, 2) < Fraction(2, 4)) - assert(Fraction(-15, -2) > Fraction(2, 4)) - assert(Fraction(-15, -2) > Fraction(-2, -4)) - assert(Fraction(-15, 2) < Fraction(-2, -4)) - } - - @Test - @DisplayName("Fraction math") - fun math() { - assert((Fraction(1) / Fraction(1)) == Fraction(1)) - assert((Fraction(2) / Fraction(1)) == Fraction(2, 1)) - assert((Fraction(2) / Fraction(2)) == Fraction(1)) { Fraction(2) / Fraction(2) } - - assert((Fraction(4, 3) / Fraction(5, 4)) == (Fraction(4, 3) * Fraction(4, 5))) - assert((Fraction(4, 3) + Fraction(5, 4)) == Fraction(31, 12)) - } - - @Test - @DisplayName("Fraction compacting") - fun compacting() { - val frac = Fraction("2721280000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "25600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") - - println("Compacting $frac => ${frac + Fraction.ZERO}") - } - - private val samples = arrayOf( - Fraction(9475, 4729), - Fraction(23535, 58723), - Fraction(-4852, 6859), - Fraction(-45623, -76849), - Fraction(38494, -76849), - Fraction(1043, -648), - ) - - private val samplesIM = arrayOf( - Decimal(9475.0 / 4729), - Decimal(23535.0 / 58723), - Decimal(-4852.0 / 6859), - Decimal(-45623.0 / -76849), - Decimal(38494.0 / -76849), - Decimal(1043.0 / -648), - ) - - private val samples2 = arrayOf( - (9475.0 / 4729), - (23535.0 / 58723), - (-4852.0 / 6859), - (-45623.0 / -76849), - (38494.0 / -76849), - (1043.0 / -648), - ) - - private val samples3 = arrayOf( - BigDecimal((9475.0 / 4729).toString()), - BigDecimal((23535.0 / 58723).toString()), - BigDecimal((-4852.0 / 6859).toString()), - BigDecimal((-45623.0 / -76849).toString()), - BigDecimal((38494.0 / -76849).toString()), - BigDecimal((1043.0 / -648).toString()), - ) - - @Test - @DisplayName("Fraction performance measurement") - fun performance() { - val rand = java.util.Random() - val blackHole = arrayOfNulls(100_000) - val blackHoleIM = arrayOfNulls(100_000) - val size = samples.size - var time = System.currentTimeMillis() - - for (i in 0 until 100_000) { - when (rand.nextInt(3)) { - 0 -> blackHole[i] = samples[rand.nextInt(size)] + samples[rand.nextInt(size)] - 1 -> blackHole[i] = samples[rand.nextInt(size)] - samples[rand.nextInt(size)] - 2 -> blackHole[i] = samples[rand.nextInt(size)] * samples[rand.nextInt(size)] - // 3 -> blackHole[i] = samples[rand.nextInt(size)] / samples[rand.nextInt(size)] - } - } - - println("Mean time for Fraction operation is ~${System.currentTimeMillis() - time} ms per 100,000 ops") - - time = System.currentTimeMillis() - - for (i in 0 until 100_000) { - when (rand.nextInt(3)) { - 0 -> blackHoleIM[i] = samplesIM[rand.nextInt(size)] + samplesIM[rand.nextInt(size)] - 1 -> blackHoleIM[i] = samplesIM[rand.nextInt(size)] - samplesIM[rand.nextInt(size)] - 2 -> blackHoleIM[i] = samplesIM[rand.nextInt(size)] * samplesIM[rand.nextInt(size)] - // 3 -> blackHole[i] = samples[rand.nextInt(size)] / samples[rand.nextInt(size)] - } - } - - println("Mean time for ImpreciseFraction operation is ~${System.currentTimeMillis() - time} ms per 100,000 ops") - - var sum = Fraction.ZERO - - // перемешаем чтоб оптимизатор не отбросил - for (i in 0 until size) { - sum += blackHole[i]!! - blackHole[i] = blackHole[rand.nextInt(size)] - } - - val blackHole2 = DoubleArray(100_000) - time = System.currentTimeMillis() - - for (i in 0 until 100_000) { - when (rand.nextInt(3)) { - 0 -> blackHole2[i] = samples2[rand.nextInt(size)] + samples2[rand.nextInt(size)] - 1 -> blackHole2[i] = samples2[rand.nextInt(size)] - samples2[rand.nextInt(size)] - 2 -> blackHole2[i] = samples2[rand.nextInt(size)] * samples2[rand.nextInt(size)] - // 3 -> blackHole2[i] = samples2[rand.nextInt(size)] / samples2[rand.nextInt(size)] - } - } - - println("Mean time for Double operation is ~${System.currentTimeMillis() - time} ms per 100,000 ops") - - val blackHole3 = arrayOfNulls(100_000) - time = System.currentTimeMillis() - - for (i in 0 until 100_000) { - when (rand.nextInt(3)) { - 0 -> blackHole3[i] = samples3[rand.nextInt(size)] + samples3[rand.nextInt(size)] - 1 -> blackHole3[i] = samples3[rand.nextInt(size)] - samples3[rand.nextInt(size)] - 2 -> blackHole3[i] = samples3[rand.nextInt(size)] * samples3[rand.nextInt(size)] - // 3 -> blackHole2[i] = samples2[rand.nextInt(size)] / samples2[rand.nextInt(size)] - } - } - - println("Mean time for BigDecimal operation is ~${System.currentTimeMillis() - time} ms per 100,000 ops") - - var sum2 = 0.0 - - // перемешаем чтоб оптимизатор не отбросил - for (i in 0 until size) { - sum2 += blackHole2[i] - blackHole2[i] = blackHole2[rand.nextInt(size)] - } - - var sum3 = 0.0 - - // перемешаем чтоб оптимизатор не отбросил - for (i in 0 until size) { - sum3 += blackHole2[i] - blackHole3[i] = blackHole3[rand.nextInt(size)] - } - - var sum4 = 0.0 - - // перемешаем чтоб оптимизатор не отбросил - for (i in 0 until size) { - sum4 += blackHoleIM[i]!!.toDouble() - blackHoleIM[i] = blackHoleIM[rand.nextInt(size)] - } - - println("$sum $sum2 $sum3") - } - - @Test - @DisplayName("Fraction serialization test") - fun serialization() { - var value = Fraction(1) - var serialized = value.serializeNBT() - - assert(value.compareTo(Fraction.deserializeNBT(serialized)) == 0) - - value = Fraction(4, 2) - serialized = value.serializeNBT() - - assert(value.compareTo(Fraction.deserializeNBT(serialized)) == 0) - - value = Fraction(-4, 2) - serialized = value.serializeNBT() - - assert(value.compareTo(Fraction.deserializeNBT(serialized)) == 0) - - value = Fraction(-4, -18) - serialized = value.serializeNBT() - - assert(value.compareTo(Fraction.deserializeNBT(serialized)) == 0) - - value = Fraction("3407203485237459085739045724837543569234750927348902374590872345", "-57777772398450982374590230984532984") - serialized = value.serializeNBT() - - assert(value.compareTo(Fraction.deserializeNBT(serialized)) == 0) - - value = Fraction("320", "100") - serialized = value.serializeNBT() - assert(value.compareTo(Fraction.deserializeNBT(serialized)) == 0) - - value = Fraction("324", "100") - serialized = value.serializeNBT() - assert(value.compareTo(Fraction.deserializeNBT(serialized)) == 0) - - value = Fraction("328", "100") - serialized = value.serializeNBT() - assert(value.compareTo(Fraction.deserializeNBT(serialized)) == 0) - - value = Fraction("332", "100") - serialized = value.serializeNBT() - assert(value.compareTo(Fraction.deserializeNBT(serialized)) == 0) - - value = Fraction("336", "100") - serialized = value.serializeNBT() - assert(value.compareTo(Fraction.deserializeNBT(serialized)) == 0) - } - - @Test - @DisplayName("Fraction inaccurate representation") - fun inaccurate() { - var value = 1.1 - var frac = Fraction(value) - - assert(frac.toDouble() == value) { "$value != $frac as (${frac.toDouble()})" } - - value = 1.45 - frac = Fraction(value) - - assert(frac.toDouble() == value) { "$value != $frac as (${frac.toDouble()})" } - - value = -1.0 - frac = Fraction(value) - - assert(frac.toDouble() == value) { "$value != $frac as (${frac.toDouble()})" } - - value = -254.66 - frac = Fraction(value) - - assert(frac.toDouble() == value) { "$value != $frac as (${frac.toDouble()})" } - - value = 94.949494 - frac = Fraction(value) - - assert(frac.toDouble() == value) { "$value != $frac as (${frac.toDouble()})" } - - value = 1.0 / 3 - frac = Fraction(value) - - assert(frac.toDouble() == value) { "$value != $frac as (${frac.toDouble()})" } - } -}