From 0be734c10d14d2e692ec0b473871733c927ceb1b Mon Sep 17 00:00:00 2001 From: lbressler13 Date: Tue, 30 Jan 2024 23:25:51 -0500 Subject: [PATCH] EF helper functions (#40) --- .../exactfraction/ExactFraction.kt | 157 ++++-------------- .../exactfraction/exactFractionHelpers.kt | 32 ++++ .../exactfraction/exactFractionOperators.kt | 103 ++++++++++++ .../exactfraction/exactFractionParsing.kt | 139 +++++++++------- .../exactfraction/ExactFractionHelpersTest.kt | 7 + .../ExactFractionOperatorsTest.kt | 10 ++ .../exactfraction/commonPowTests.kt | 3 +- .../{compareToTests.kt => compareTests.kt} | 39 +++-- 8 files changed, 282 insertions(+), 208 deletions(-) create mode 100644 exact-numbers/src/main/kotlin/xyz/lbres/exactnumbers/exactfraction/exactFractionHelpers.kt create mode 100644 exact-numbers/src/main/kotlin/xyz/lbres/exactnumbers/exactfraction/exactFractionOperators.kt create mode 100644 exact-numbers/src/test/kotlin/xyz/lbres/exactnumbers/exactfraction/ExactFractionHelpersTest.kt create mode 100644 exact-numbers/src/test/kotlin/xyz/lbres/exactnumbers/exactfraction/ExactFractionOperatorsTest.kt rename exact-numbers/src/test/kotlin/xyz/lbres/exactnumbers/exactfraction/{compareToTests.kt => compareTests.kt} (64%) diff --git a/exact-numbers/src/main/kotlin/xyz/lbres/exactnumbers/exactfraction/ExactFraction.kt b/exact-numbers/src/main/kotlin/xyz/lbres/exactnumbers/exactfraction/ExactFraction.kt index e73fa5a6..21770607 100644 --- a/exact-numbers/src/main/kotlin/xyz/lbres/exactnumbers/exactfraction/ExactFraction.kt +++ b/exact-numbers/src/main/kotlin/xyz/lbres/exactnumbers/exactfraction/ExactFraction.kt @@ -2,11 +2,8 @@ package xyz.lbres.exactnumbers.exactfraction import xyz.lbres.common.divideByZero import xyz.lbres.exactnumbers.ext.eq -import xyz.lbres.exactnumbers.ext.toExactFraction -import xyz.lbres.kotlinutils.biginteger.ext.ifZero import xyz.lbres.kotlinutils.biginteger.ext.isNegative import xyz.lbres.kotlinutils.biginteger.ext.isZero -import xyz.lbres.kotlinutils.biginteger.getGCD import java.math.BigDecimal import java.math.BigInteger import java.math.MathContext @@ -43,9 +40,13 @@ class ExactFraction private constructor() : Comparable, Number() * @throws ArithmeticException if denominator is 0 */ constructor (numerator: BigInteger, denominator: BigInteger) : this() { - this.numerator = numerator - this.denominator = denominator.ifZero { throw divideByZero } - simplify() + if (denominator.isZero()) { + throw divideByZero + } + + val simplified = simplifyFraction(numerator, denominator) + this.numerator = simplified.first + this.denominator = simplified.second } /** @@ -91,44 +92,25 @@ class ExactFraction private constructor() : Comparable, Number() // BINARY OPERATORS - operator fun plus(other: ExactFraction): ExactFraction { - if (denominator == other.denominator) { - val newNumerator = numerator + other.numerator - return ExactFraction(newNumerator, denominator) - } - - val scaled1 = numerator * other.denominator - val scaled2 = other.numerator * denominator - - val newNumerator = scaled1 + scaled2 - val newDenominator = denominator * other.denominator - return ExactFraction(newNumerator, newDenominator) - } - - operator fun plus(other: BigInteger): ExactFraction = plus(other.toExactFraction()) - operator fun plus(other: Long): ExactFraction = plus(other.toExactFraction()) - operator fun plus(other: Int): ExactFraction = plus(other.toExactFraction()) + operator fun plus(other: ExactFraction): ExactFraction = efAdd(this, other) + operator fun plus(other: BigInteger): ExactFraction = plus(ExactFraction(other)) + operator fun plus(other: Long): ExactFraction = plus(ExactFraction(other)) + operator fun plus(other: Int): ExactFraction = plus(ExactFraction(other)) operator fun minus(other: ExactFraction): ExactFraction = plus(-other) - operator fun minus(other: BigInteger): ExactFraction = plus(-other) - operator fun minus(other: Long): ExactFraction = plus(-other) - operator fun minus(other: Int): ExactFraction = plus(-other) - - operator fun times(other: ExactFraction): ExactFraction { - val newNumerator = numerator * other.numerator - val newDenominator = denominator * other.denominator - return ExactFraction(newNumerator, newDenominator) - } + operator fun minus(other: BigInteger): ExactFraction = minus(ExactFraction(other)) + operator fun minus(other: Long): ExactFraction = minus(ExactFraction(other)) + operator fun minus(other: Int): ExactFraction = minus(ExactFraction(other)) - operator fun times(other: BigInteger): ExactFraction = times(other.toExactFraction()) - operator fun times(other: Long): ExactFraction = times(other.toExactFraction()) - operator fun times(other: Int): ExactFraction = times(other.toExactFraction()) + operator fun times(other: ExactFraction): ExactFraction = efTimes(this, other) + operator fun times(other: BigInteger): ExactFraction = times(ExactFraction(other)) + operator fun times(other: Long): ExactFraction = times(ExactFraction(other)) + operator fun times(other: Int): ExactFraction = times(ExactFraction(other)) operator fun div(other: ExactFraction): ExactFraction = times(other.inverse()) - - operator fun div(other: BigInteger): ExactFraction = div(other.toExactFraction()) - operator fun div(other: Long): ExactFraction = div(other.toExactFraction()) - operator fun div(other: Int): ExactFraction = div(other.toExactFraction()) + operator fun div(other: BigInteger): ExactFraction = div(ExactFraction(other)) + operator fun div(other: Long): ExactFraction = div(ExactFraction(other)) + operator fun div(other: Int): ExactFraction = div(ExactFraction(other)) override fun equals(other: Any?): Boolean { if (other == null || other !is ExactFraction) { @@ -140,53 +122,17 @@ class ExactFraction private constructor() : Comparable, Number() return scaled1 == scaled2 } - fun eq(other: Int): Boolean = numerator.eq(other) && denominator.eq(1) - fun eq(other: Long): Boolean = numerator.eq(other) && denominator.eq(1) + fun eq(other: Int): Boolean = eq(other.toBigInteger()) + fun eq(other: Long): Boolean = eq(other.toBigInteger()) fun eq(other: BigInteger): Boolean = numerator == other && denominator.eq(1) - override fun compareTo(other: ExactFraction): Int { - val difference = minus(other) - return when { - difference.isNegative() -> -1 - difference.isZero() -> 0 - else -> 1 - } - } + override fun compareTo(other: ExactFraction): Int = efCompare(this, other) - operator fun compareTo(other: Int): Int = compareTo(other.toExactFraction()) - operator fun compareTo(other: Long): Int = compareTo(other.toExactFraction()) - operator fun compareTo(other: BigInteger): Int = compareTo(other.toExactFraction()) + operator fun compareTo(other: Int): Int = compareTo(ExactFraction(other)) + operator fun compareTo(other: Long): Int = compareTo(ExactFraction(other)) + operator fun compareTo(other: BigInteger): Int = compareTo(ExactFraction(other)) - fun pow(other: ExactFraction): ExactFraction { - if (other.isZero()) { - return ONE - } - - if (other.denominator != BigInteger.ONE) { - throw ArithmeticException("Exponents must be whole numbers") - } - - var numeratorMult = BigInteger.ONE - var denominatorMult = BigInteger.ONE - var remaining = other.absoluteValue().numerator.abs() - val intMax = Int.MAX_VALUE - - while (remaining > BigInteger.ZERO) { - if (remaining > intMax.toBigInteger()) { - numeratorMult *= numerator.pow(intMax) - denominatorMult *= denominator.pow(intMax) - remaining -= intMax.toBigInteger() - } else { - val exp = remaining.toInt() - numeratorMult = numerator.pow(exp) - denominatorMult = denominator.pow(exp) - remaining = BigInteger.ZERO - } - } - - val result = ExactFraction(numeratorMult, denominatorMult) - return if (other < 0) result.inverse() else result - } + fun pow(other: ExactFraction): ExactFraction = efPow(this, other) // UNARY NON-OPERATORS @@ -202,53 +148,6 @@ class ExactFraction private constructor() : Comparable, Number() fun isNegative(): Boolean = numerator.isNegative() fun isZero(): Boolean = numerator.isZero() - // SIMPLIFICATION - - private fun simplify() { - simplifyZero() - simplifyGCD() - simplifySign() - } - - /** - * Set denominator to 1 when numerator is 0 - */ - private fun simplifyZero() { - if (numerator.eq(0)) { - denominator = BigInteger.ONE - } - } - - /** - * Move negatives to numerator - */ - private fun simplifySign() { - val numNegative = numerator.isNegative() - val denomNegative = denominator.isNegative() - - when { - numNegative && denomNegative -> { - numerator = numerator.abs() - denominator = denominator.abs() - } - !numNegative && denomNegative -> { - numerator = -numerator - denominator = denominator.abs() - } - } - } - - /** - * Simplify using greatest common divisor - */ - private fun simplifyGCD() { - if (!numerator.isZero()) { - val gcd = getGCD(numerator, denominator) - numerator /= gcd - denominator /= gcd - } - } - // STRING METHODS /** diff --git a/exact-numbers/src/main/kotlin/xyz/lbres/exactnumbers/exactfraction/exactFractionHelpers.kt b/exact-numbers/src/main/kotlin/xyz/lbres/exactnumbers/exactfraction/exactFractionHelpers.kt new file mode 100644 index 00000000..64de1ba1 --- /dev/null +++ b/exact-numbers/src/main/kotlin/xyz/lbres/exactnumbers/exactfraction/exactFractionHelpers.kt @@ -0,0 +1,32 @@ +package xyz.lbres.exactnumbers.exactfraction + +import xyz.lbres.kotlinutils.biginteger.ext.isNegative +import xyz.lbres.kotlinutils.biginteger.ext.isZero +import xyz.lbres.kotlinutils.biginteger.getGCD +import java.math.BigInteger + +/** + * Simplify numerator and denominator values to smallest values with same ratio, and move all negatives into numerator + * + * @param numerator [BigInteger]: numerator of fraction to simplify + * @param denominator [BigInteger]: denominator of fraction to simplify + * @return Pair: pair where first value represents simplified numerator, and second value represents simplified denominator + */ +internal fun simplifyFraction(numerator: BigInteger, denominator: BigInteger): Pair { + // set denominator to 1 when numerator is 0 + if (numerator.isZero()) { + return Pair(BigInteger.ZERO, BigInteger.ONE) + } + + // simplify using greatest common divisor + val gcd = getGCD(numerator, denominator) + val simplifiedNumerator = numerator / gcd + val simplifiedDenominator = denominator / gcd + + // move negatives to numerator + return if (denominator.isNegative()) { + Pair(-simplifiedNumerator, -simplifiedDenominator) + } else { + Pair(simplifiedNumerator, simplifiedDenominator) + } +} diff --git a/exact-numbers/src/main/kotlin/xyz/lbres/exactnumbers/exactfraction/exactFractionOperators.kt b/exact-numbers/src/main/kotlin/xyz/lbres/exactnumbers/exactfraction/exactFractionOperators.kt new file mode 100644 index 00000000..f9c60b6a --- /dev/null +++ b/exact-numbers/src/main/kotlin/xyz/lbres/exactnumbers/exactfraction/exactFractionOperators.kt @@ -0,0 +1,103 @@ +package xyz.lbres.exactnumbers.exactfraction + +import java.math.BigInteger + +/** + * Add two ExactFractions + * + * @param ef1 [ExactFraction] + * @param ef2 [ExactFraction] + * @return [ExactFraction]: sum of [ef1] and [ef2] + */ +internal fun efAdd(ef1: ExactFraction, ef2: ExactFraction): ExactFraction { + if (ef1.denominator == ef2.denominator) { + val newNumerator = ef1.numerator + ef2.numerator + return ExactFraction(newNumerator, ef1.denominator) + } + + val newNumerator = ef1.numerator * ef2.denominator + ef2.numerator * ef1.denominator + val newDenominator = ef1.denominator * ef2.denominator + return ExactFraction(newNumerator, newDenominator) +} + +/** + * Multiply two ExactFractions + * + * @param ef1 [ExactFraction] + * @param ef2 [ExactFraction] + * @return [ExactFraction]: product of [ef1] and [ef2] + */ +internal fun efTimes(ef1: ExactFraction, ef2: ExactFraction): ExactFraction { + val newNumerator = ef1.numerator * ef2.numerator + val newDenominator = ef1.denominator * ef2.denominator + return ExactFraction(newNumerator, newDenominator) +} + +/** + * Apply exponent to an ExactFraction + * + * @param base [ExactFraction]: base of exponentiation + * @param exponent [ExactFraction]: exponent, must be a whole number + * @return [ExactFraction] + */ +internal fun efPow(base: ExactFraction, exponent: ExactFraction): ExactFraction { + // TODO + // if (!exponent.isWholeNumber()) { + if (exponent.denominator != BigInteger.ONE) { + throw ArithmeticException("Exponents must be whole numbers") + } + + when { + base == ExactFraction.ONE || exponent.isZero() -> return ExactFraction.ONE + base.isZero() -> return ExactFraction.ZERO + exponent == ExactFraction.ONE -> return base + } + + var powNumerator = BigInteger.ONE + var powDenominator = BigInteger.ONE + var remaining = exponent.numerator.abs() + val intMax = Int.MAX_VALUE + + try { + while (remaining > BigInteger.ZERO) { + if (remaining > intMax.toBigInteger()) { + powNumerator *= base.numerator.pow(intMax) + powDenominator *= base.denominator.pow(intMax) + remaining -= intMax.toBigInteger() + } else { + val exp = remaining.toInt() + powNumerator = base.numerator.pow(exp) + powDenominator = base.denominator.pow(exp) + remaining = BigInteger.ZERO + } + } + } catch (e: ArithmeticException) { + if (e.message == "BigInteger would overflow supported range") { + throw ExactFractionOverflowException() + } + + throw e + } + + return if (exponent.isNegative()) { + ExactFraction(powDenominator, powNumerator) + } else { + ExactFraction(powNumerator, powDenominator) + } +} + +/** + * Compare two ExactFractions + * + * @param ef1 [ExactFraction] + * @param ef2 [ExactFraction] + * @return [Int]: comparison of [ef1] to [ef2] + */ +internal fun efCompare(ef1: ExactFraction, ef2: ExactFraction): Int { + val difference = ef1 - ef2 + return when { + difference.isNegative() -> -1 + difference.isZero() -> 0 + else -> 1 + } +} diff --git a/exact-numbers/src/main/kotlin/xyz/lbres/exactnumbers/exactfraction/exactFractionParsing.kt b/exact-numbers/src/main/kotlin/xyz/lbres/exactnumbers/exactfraction/exactFractionParsing.kt index 7ad3d2a7..b4956a6f 100644 --- a/exact-numbers/src/main/kotlin/xyz/lbres/exactnumbers/exactfraction/exactFractionParsing.kt +++ b/exact-numbers/src/main/kotlin/xyz/lbres/exactnumbers/exactfraction/exactFractionParsing.kt @@ -1,65 +1,65 @@ package xyz.lbres.exactnumbers.exactfraction -import xyz.lbres.kotlinutils.biginteger.ext.isZero -import xyz.lbres.kotlinutils.string.ext.substringTo +// TODO +// import xyz.lbres.kotlinutils.general.simpleIf +// import xyz.lbres.kotlinutils.general.succeeds +// import xyz.lbres.kotlinutils.general.tryOrDefault +import java.math.BigDecimal import java.math.BigInteger +private const val efPrefix = "EF[" +private const val efSuffix = "]" + /** * Parse a string from standard number format into a ExactFraction. * Standard format is a string which may start with "-", but otherwise consists of at least one digit and up to 1 "." * - * @param unparsed [String]: string to parse - * @return parsed ExactFraction - * @throws NumberFormatException in case of improperly formatted number string + * @param s [String]: string to parse + * @return [ExactFraction]: parsed value */ -internal fun parseDecimal(unparsed: String): ExactFraction { - var currentState: String = unparsed.trim() +internal fun parseDecimal(s: String): ExactFraction { + var currentState: String = s.trim() - val isNegative = unparsed.startsWith("-") - val timesNeg = if (isNegative) -BigInteger.ONE else BigInteger.ONE - if (isNegative) { - currentState = currentState.substring(1) + // validate string + // TODO + // if (!succeeds { BigDecimal(currentState) } || currentState.last() == '.') { + // throw NumberFormatException("Error parsing $currentState") + // } + try { + BigDecimal(currentState) + } catch (_: Exception) { + throw NumberFormatException("Error parsing $currentState") } - val decimalIndex: Int = currentState.indexOf('.') - - return when (decimalIndex) { - -1 -> { - val numerator = BigInteger(currentState) - ExactFraction(numerator * timesNeg) - } - 0 -> { - currentState = currentState.substring(1) - val numerator = BigInteger(currentState) + if (currentState.last() == '.') { + throw NumberFormatException("Error parsing $currentState") + } - if (numerator.isZero()) { - return ExactFraction(0) - } + // remove negative sign + val isNegative = currentState.startsWith("-") + if (isNegative) { + currentState = currentState.substring(1) + } - val zeros = "0".repeat(currentState.length) - val denomString = "1$zeros" - val denominator = BigInteger(denomString) + val divResult = BigDecimal(currentState).divideAndRemainder(BigDecimal.ONE) + val whole = divResult[0].toBigInteger() - ExactFraction(numerator * timesNeg, denominator) - } - else -> { - val wholeString = currentState.substringTo(decimalIndex) - val decimalString = currentState.substring(decimalIndex + 1) - val whole = BigInteger(wholeString) - val decimal = BigInteger(decimalString) - - if (decimal.isZero()) { - return ExactFraction(whole * timesNeg) // also covers the case where number is 0 - } + val rawDecimalString = divResult[1].stripTrailingZeros().toPlainString() + val decimalIndex = rawDecimalString.indexOf('.') + val decimalString = rawDecimalString.substring(decimalIndex + 1) // starts from 0 if decimalIndex == -1 - val zeros = "0".repeat(decimalString.length) - val denomString = "1$zeros" + val denomZeroes = "0".repeat(decimalString.length) + val denomString = "1$denomZeroes" - val denominator = BigInteger(denomString) - val numerator = whole * denominator + decimal + val denominator = BigInteger(denomString) + val numerator = whole * denominator + BigInteger(decimalString) - ExactFraction(numerator * timesNeg, denominator) - } + // TODO + // return simpleIf(isNegative, { ExactFraction(-numerator, denominator) }, { ExactFraction(numerator, denominator) }) + return if (isNegative) { + ExactFraction(-numerator, denominator) + } else { + ExactFraction(numerator, denominator) } } @@ -67,26 +67,28 @@ internal fun parseDecimal(unparsed: String): ExactFraction { * Parse a string from a EF string format into a ExactFraction. * EF string format is "EF[num denom]" * - * @param unparsed [String]: string to parse - * @return parsed ExactFraction - * @throws NumberFormatException in case of improperly formatted number string + * @param s [String]: string to parse + * @return [ExactFraction]: parsed value */ -internal fun parseEFString(unparsed: String): ExactFraction { - if (!checkIsEFString(unparsed)) { +internal fun parseEFString(s: String): ExactFraction { + if (!checkIsEFString(s)) { + // TODO + // throw NumberFormatException("Invalid EF string format: $s") throw NumberFormatException("Invalid EF string format") } try { - val numbers = unparsed.substring(3, unparsed.lastIndex) - val split = numbers.split(' ') - val numString = split[0].trim() - val denomString = split[1].trim() + val numbers = s.trim().substring(efPrefix.length, s.length - efSuffix.length).split(' ') + val numString = numbers[0].trim() + val denomString = numbers[1].trim() val numerator = BigInteger(numString) val denominator = BigInteger(denomString) return ExactFraction(numerator, denominator) } catch (e: ArithmeticException) { throw e - } catch (e: Exception) { + } catch (_: Exception) { + // TODO + // throw NumberFormatException("Invalid EF string format: $s") throw NumberFormatException("Invalid EF string format") } } @@ -96,16 +98,29 @@ internal fun parseEFString(unparsed: String): ExactFraction { * EF string format is "EF[num denom]" * * @param s [String]: string to check - * @return true if s is in EF string format, false otherwise + * @return [Boolean]: `true` if s is in EF string format, `false` otherwise */ -fun checkIsEFString(s: String): Boolean { +internal fun checkIsEFString(s: String): Boolean { + val trimmed = s.trim() + + if (!trimmed.startsWith(efPrefix) || !trimmed.endsWith(efSuffix)) { + return false + } + + // TODO + // return tryOrDefault(false) { return try { - val startEnd = s.trim().startsWith("EF[") && s.trim().endsWith("]") - val split = s.substring(3, s.lastIndex).split(" ") - BigInteger(split[0]) - BigInteger(split[1]) - startEnd && split.size == 2 - } catch (e: Exception) { + val numbers = trimmed.substring(efPrefix.length, s.length - efSuffix.length).split(' ') + val validNumber: (String) -> Boolean = { + when { + it.isEmpty() -> false + it.length == 1 -> it[0].isDigit() + else -> (it[0] == '-' || it[0].isDigit()) && it.substring(1).all(Char::isDigit) + } + } + + numbers.size == 2 && validNumber(numbers[0]) && validNumber(numbers[1]) + } catch (_: Exception) { false } } diff --git a/exact-numbers/src/test/kotlin/xyz/lbres/exactnumbers/exactfraction/ExactFractionHelpersTest.kt b/exact-numbers/src/test/kotlin/xyz/lbres/exactnumbers/exactfraction/ExactFractionHelpersTest.kt new file mode 100644 index 00000000..831ba199 --- /dev/null +++ b/exact-numbers/src/test/kotlin/xyz/lbres/exactnumbers/exactfraction/ExactFractionHelpersTest.kt @@ -0,0 +1,7 @@ +package xyz.lbres.exactnumbers.exactfraction + +import kotlin.test.Test + +class ExactFractionHelpersTest { + @Test fun testSimplifyFraction() = runCommonSimplifyTests(::simplifyFraction) +} diff --git a/exact-numbers/src/test/kotlin/xyz/lbres/exactnumbers/exactfraction/ExactFractionOperatorsTest.kt b/exact-numbers/src/test/kotlin/xyz/lbres/exactnumbers/exactfraction/ExactFractionOperatorsTest.kt new file mode 100644 index 00000000..fddfd0c1 --- /dev/null +++ b/exact-numbers/src/test/kotlin/xyz/lbres/exactnumbers/exactfraction/ExactFractionOperatorsTest.kt @@ -0,0 +1,10 @@ +package xyz.lbres.exactnumbers.exactfraction + +import kotlin.test.Test + +class ExactFractionOperatorsTest { + @Test fun testEfAdd() = runCommonPlusTests(::efAdd) + @Test fun testEfTimes() = runCommonTimesTests(::efTimes) + @Test fun testEfPow() = runCommonPowTests(::efPow) + @Test fun testEfCompare() = runCommonCompareTests(::efCompare) +} diff --git a/exact-numbers/src/test/kotlin/xyz/lbres/exactnumbers/exactfraction/commonPowTests.kt b/exact-numbers/src/test/kotlin/xyz/lbres/exactnumbers/exactfraction/commonPowTests.kt index 4af36546..5e4dd0ec 100644 --- a/exact-numbers/src/test/kotlin/xyz/lbres/exactnumbers/exactfraction/commonPowTests.kt +++ b/exact-numbers/src/test/kotlin/xyz/lbres/exactnumbers/exactfraction/commonPowTests.kt @@ -16,7 +16,6 @@ fun runCommonPowTests(powFn: (ExactFraction, ExactFraction) -> ExactFraction) { base = ExactFraction.ZERO exp = ExactFraction.ZERO - // expected = ExactFraction.ZERO // TODO expected = ExactFraction.ONE assertEquals(expected, powFn(base, exp)) @@ -108,7 +107,7 @@ fun runCommonPowTests(powFn: (ExactFraction, ExactFraction) -> ExactFraction) { assertEquals(expected, powFn(base, exp)) // large exponent - // runLargeExponentTests(powFn) // TODO + runLargeExponentTests(powFn) // fractional exponent val expectedError = "Exponents must be whole numbers" diff --git a/exact-numbers/src/test/kotlin/xyz/lbres/exactnumbers/exactfraction/compareToTests.kt b/exact-numbers/src/test/kotlin/xyz/lbres/exactnumbers/exactfraction/compareTests.kt similarity index 64% rename from exact-numbers/src/test/kotlin/xyz/lbres/exactnumbers/exactfraction/compareToTests.kt rename to exact-numbers/src/test/kotlin/xyz/lbres/exactnumbers/exactfraction/compareTests.kt index 34922866..0b91aff0 100644 --- a/exact-numbers/src/test/kotlin/xyz/lbres/exactnumbers/exactfraction/compareToTests.kt +++ b/exact-numbers/src/test/kotlin/xyz/lbres/exactnumbers/exactfraction/compareTests.kt @@ -4,53 +4,62 @@ import kotlin.test.assertEquals import kotlin.test.assertTrue @Suppress("KotlinConstantConditions") -fun runCompareToTests() { +fun runCommonCompareTests(compareFn: (ExactFraction, ExactFraction) -> Int) { // equal values var first = ExactFraction(0) var second = ExactFraction(0) - assertEquals(0, first.compareTo(second)) + assertEquals(0, compareFn(first, second)) first = ExactFraction(100, 3) second = ExactFraction(100, 3) - assertEquals(0, first.compareTo(second)) + assertEquals(0, compareFn(first, second)) // positive greater than zero first = ExactFraction(3) second = ExactFraction(0) - assertTrue(first > second) - assertTrue(second < first) + assertEquals(1, compareFn(first, second)) + assertEquals(-1, compareFn(second, first)) // negative less than zero first = ExactFraction(-3) second = ExactFraction(0) - assertTrue(first < second) - assertTrue(second > first) + assertEquals(-1, compareFn(first, second)) + assertEquals(1, compareFn(second, first)) // negative less than positive first = ExactFraction(-1) second = ExactFraction(1) - assertTrue(first < second) - assertTrue(second > first) + assertEquals(-1, compareFn(first, second)) + assertEquals(1, compareFn(second, first)) // negative order first = ExactFraction(-3, 4) second = ExactFraction(-2) - assertTrue(first > second) - assertTrue(second < first) + assertEquals(1, compareFn(first, second)) + assertEquals(-1, compareFn(second, first)) first = ExactFraction(-3, 4) second = ExactFraction(-4, 3) - assertTrue(first > second) - assertTrue(second < first) + assertEquals(1, compareFn(first, second)) + assertEquals(-1, compareFn(second, first)) // positive order first = ExactFraction(3) second = ExactFraction(2) - assertTrue(first > second) - assertTrue(second < first) + assertEquals(1, compareFn(first, second)) + assertEquals(-1, compareFn(second, first)) first = ExactFraction(3, 4) second = ExactFraction(4, 3) + assertEquals(-1, compareFn(first, second)) + assertEquals(1, compareFn(second, first)) +} + +fun runCompareToTests() { + runCommonCompareTests(ExactFraction::compareTo) + + val first = ExactFraction(3, 4) + val second = ExactFraction(4, 3) assertTrue(first < second) assertTrue(second > first)