-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: partial AdditiveExpression implementation + start of tests
- Loading branch information
1 parent
b977663
commit b09abd0
Showing
9 changed files
with
425 additions
and
26 deletions.
There are no files selected for viewing
53 changes: 53 additions & 0 deletions
53
...mbers/src/main/kotlin/xyz/lbres/exactnumbers/expressions/expression/AdditiveExpression.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package xyz.lbres.exactnumbers.expressions.expression | ||
|
||
import xyz.lbres.exactnumbers.exactfraction.ExactFraction | ||
import xyz.lbres.exactnumbers.expressions.Expression | ||
import xyz.lbres.exactnumbers.expressions.ExpressionImpl | ||
import xyz.lbres.exactnumbers.expressions.term.Term | ||
import xyz.lbres.exactnumbers.utils.createHashCode | ||
import xyz.lbres.exactnumbers.utils.getOrSet | ||
import xyz.lbres.kotlinutils.collection.ext.toConstMultiSet | ||
import xyz.lbres.kotlinutils.set.multiset.const.ConstMultiSet | ||
import xyz.lbres.kotlinutils.set.multiset.const.constMultiSetOf | ||
import xyz.lbres.kotlinutils.set.multiset.mapToSetConsistent | ||
|
||
/** | ||
* Expression which is the sum of several other expressions. | ||
* Can also represent 1/sum | ||
*/ | ||
@Suppress("EqualsOrHashCode") | ||
internal class AdditiveExpression private constructor(private val expressions: ConstMultiSet<Expression>, private val isInverted: Boolean) : ExpressionImpl() { | ||
private var term: Term? = null | ||
|
||
init { | ||
if (expressions.isEmpty()) { | ||
throw Exception("Invalid expression") | ||
} | ||
} | ||
|
||
constructor(expr1: Expression, expr2: Expression) : this(constMultiSetOf(expr1, expr2), false) | ||
|
||
override fun unaryPlus(): Expression = this | ||
override fun unaryMinus(): Expression { | ||
val newExpressions = expressions.mapToSetConsistent { -it }.toConstMultiSet() | ||
return AdditiveExpression(newExpressions, isInverted) | ||
} | ||
|
||
override fun inverse(): Expression = AdditiveExpression(expressions, !isInverted) | ||
|
||
override fun toTerm(): Term { | ||
return getOrSet({ term }, { term = it }) { | ||
val terms = expressions.map { it.toTerm() } | ||
.groupBy { it.factors }.map { | ||
val coefficient = it.value.fold(ExactFraction.ZERO) { acc, t -> acc + t.coefficient } // value is list of terms | ||
Term.fromValues(coefficient, it.key) // key is list of factors | ||
} | ||
Term.ZERO | ||
// TODO | ||
} | ||
} | ||
|
||
override fun hashCode(): Int = createHashCode(listOf(expressions, "AdditiveExpression")) | ||
|
||
override fun toString(): String = "(${expressions.joinToString("+")})" | ||
} |
23 changes: 23 additions & 0 deletions
23
...s/src/test/kotlin/xyz/lbres/exactnumbers/expressions/expression/AdditiveExpressionTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package xyz.lbres.exactnumbers.expressions.expression | ||
|
||
import xyz.lbres.exactnumbers.expressions.expression.additive.* // ktlint-disable no-wildcard-imports no-unused-imports | ||
import kotlin.test.Test | ||
|
||
class AdditiveExpressionTest { | ||
// @Test fun testEquals() = runEqualsTests() // TODO | ||
@Test fun testToString() = runToStringTests() | ||
|
||
@Test fun testUnaryMinus() = runUnaryMinusTests() | ||
@Test fun testUnaryPlus() = runUnaryPlusTests() | ||
// @Test fun testInverse() = runInverseTests() // TODO | ||
// @Test fun testGetValue() = runGetValueTests() // TODO | ||
|
||
// @Test fun testToTerm() = runToTermTests() // TODO | ||
// @Test fun testToByte() = runToByteTests() // TODO | ||
// @Test fun testToChar() = runToCharTests() // TODO | ||
// @Test fun testToShort() = runToShortTests() // TODO | ||
// @Test fun testToInt() = runToIntTests() // TODO | ||
// @Test fun testToLong() = runToLongTests() // TODO | ||
// @Test fun testToFloat() = runToFloatTests() // TODO | ||
// @Test fun testToDouble() = runToDoubleTests() // TODO | ||
} |
167 changes: 167 additions & 0 deletions
167
...rs/src/test/kotlin/xyz/lbres/exactnumbers/expressions/expression/additive/castingTests.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
package xyz.lbres.exactnumbers.expressions.expression.additive | ||
|
||
import xyz.lbres.exactnumbers.exactfraction.ExactFraction | ||
import xyz.lbres.exactnumbers.expressions.Expression | ||
import xyz.lbres.exactnumbers.expressions.expression.* // ktlint-disable no-wildcard-imports no-unused-imports | ||
import xyz.lbres.exactnumbers.expressions.term.Term | ||
import xyz.lbres.exactnumbers.irrationals.log.Log | ||
import xyz.lbres.exactnumbers.testutils.assertSucceeds | ||
import xyz.lbres.exactnumbers.testutils.getCastingOverflowAssertion | ||
import kotlin.test.assertEquals | ||
|
||
private val assertCastingOverflow = getCastingOverflowAssertion<AdditiveExpression>("Expression") | ||
|
||
fun runToTermTests() { | ||
} | ||
|
||
fun runToByteTests() { | ||
runWholeNumberCastingTests(Long::toByte, AdditiveExpression::toByte, Byte.MIN_VALUE, Byte.MAX_VALUE, "Byte") | ||
} | ||
|
||
fun runToCharTests() { | ||
var expr = AdditiveExpression(Expression.ZERO, Expression.ZERO) | ||
var expected = 0.toChar() | ||
assertEquals(expected, expr.toChar()) | ||
|
||
expr = AdditiveExpression(simpleExpr1, simpleExpr1.inverse()) | ||
expected = 1.toChar() | ||
assertEquals(expected, expr.toChar()) | ||
|
||
expr = AdditiveExpression(simpleExpr1, AdditiveExpression(simpleExpr2, simpleExpr2.inverse())) | ||
expected = 25.toChar() | ||
assertEquals(expected, expr.toChar()) | ||
|
||
expr = AdditiveExpression(partialMultExpr.inverse(), -simpleExpr2.inverse()) | ||
expected = 0.toChar() | ||
assertEquals(expected, expr.toChar()) | ||
|
||
expr = AdditiveExpression(partialMultExpr, simpleExpr2) | ||
assertCastingOverflow("Char", expr) { expr.toChar() } | ||
|
||
val maxExpr = SimpleExpression(Term.fromValues(ExactFraction(Char.MAX_VALUE.code), emptyList())) | ||
expr = AdditiveExpression(maxExpr, SimpleExpression(Term.fromValues(one, listOf(Log(11).inverse())))) | ||
assertSucceeds("Cast expected to succeed") { expr.toChar() } | ||
|
||
expr = AdditiveExpression(maxExpr, SimpleExpression(Term.fromValues(one, listOf(Log(11))))) | ||
assertCastingOverflow("Char", expr) { expr.toChar() } | ||
} | ||
|
||
fun runToShortTests() { | ||
runWholeNumberCastingTests(Long::toShort, AdditiveExpression::toShort, Short.MIN_VALUE, Short.MAX_VALUE, "Short") | ||
} | ||
|
||
fun runToIntTests() { | ||
runWholeNumberCastingTests(Long::toInt, AdditiveExpression::toInt, Int.MIN_VALUE, Int.MAX_VALUE, "Int") | ||
} | ||
|
||
fun runToLongTests() { | ||
runWholeNumberCastingTests({ it }, AdditiveExpression::toLong, Long.MIN_VALUE, Long.MAX_VALUE, "Long") | ||
} | ||
|
||
fun runToFloatTests() { | ||
runDecimalNumberCastingTests(Double::toFloat, AdditiveExpression::toFloat, Float.MAX_VALUE, "Float") | ||
} | ||
|
||
fun runToDoubleTests() { | ||
runDecimalNumberCastingTests({ it }, AdditiveExpression::toDouble, Double.MAX_VALUE, "Double") | ||
} | ||
|
||
/** | ||
* Run tests for a single type of whole number | ||
* | ||
* @param castLong (Long) -> T: cast a long value to a value of the current number type | ||
* @param castExpr (AdditiveExpression) -> T: cast a multiplicative expression to a value of the current number type | ||
* @param minValue T: minimum valid value for the current number type | ||
* @param maxValue T: maximum valid value for the current number type | ||
* @param type [String]: string representation of type, which is used in overflow exceptions | ||
*/ | ||
private fun <T : Number> runWholeNumberCastingTests(castLong: (Long) -> T, castExpr: (AdditiveExpression) -> T, minValue: T, maxValue: T, type: String) { | ||
var expr = AdditiveExpression(Expression.ZERO, Expression.ZERO) | ||
var expected = castLong(0) | ||
assertEquals(expected, castExpr(expr)) | ||
|
||
expr = AdditiveExpression(simpleExpr1, -simpleExpr1) | ||
expected = castLong(1) | ||
assertEquals(expected, castExpr(expr)) | ||
|
||
expr = AdditiveExpression(simpleExpr1, MultiplicativeExpression(simpleExpr2, simpleExpr2.inverse())) | ||
expected = castLong(25) | ||
assertEquals(expected, castExpr(expr)) | ||
|
||
expr = AdditiveExpression(simpleExpr1, AdditiveExpression(simpleExpr2, -simpleExpr1)) | ||
expected = castLong(25) | ||
assertEquals(expected, castExpr(expr)) | ||
|
||
// expr = AdditiveExpression(partialExpr, simpleExpr2) | ||
// expected = castLong(-58) | ||
// assertEquals(expected, castExpr(expr)) | ||
// | ||
// expr = AdditiveExpression(partialExpr.inverse(), -simpleExpr2.inverse()) | ||
// expected = castLong(0) | ||
// assertEquals(expected, castExpr(expr)) | ||
// | ||
// val minExpr = SimpleExpression(Term.fromValues(ExactFraction(minValue.toLong()), emptyList())) | ||
// expr = AdditiveExpression(minExpr, SimpleExpression(Term.fromValues(one, listOf(Log(11).inverse())))) | ||
// assertSucceeds("Cast expected to succeed") { castExpr(expr) } | ||
// | ||
// expr = AdditiveExpression(minExpr, SimpleExpression(Term.fromValues(one, listOf(Log(11))))) | ||
// assertCastingOverflow(type, expr) { castExpr(expr) } | ||
// | ||
// val maxExpr = SimpleExpression(Term.fromValues(ExactFraction(maxValue.toLong()), emptyList())) | ||
// expr = AdditiveExpression(maxExpr, SimpleExpression(Term.fromValues(one, listOf(Log(11).inverse())))) | ||
// assertSucceeds("Cast expected to succeed") { castExpr(expr) } | ||
// | ||
// expr = AdditiveExpression(maxExpr, SimpleExpression(Term.fromValues(one, listOf(Log(11))))) | ||
// assertCastingOverflow(type, expr) { castExpr(expr) } | ||
} | ||
|
||
/** | ||
* Run tests for a single type of decimal number | ||
* | ||
* @param castDouble (Double) -> T: cast a double value to a value of the current number type | ||
* @param castExpr (AdditiveExpression) -> T: cast a multiplicative expression to a value of the current number type | ||
* @param maxValue T: maximum valid value for the current number type | ||
* @param type [String]: string representation of type, which is used in overflow exceptions | ||
*/ | ||
private fun <T : Number> runDecimalNumberCastingTests(castDouble: (Double) -> T, castExpr: (AdditiveExpression) -> T, maxValue: T, type: String) { | ||
var expr = AdditiveExpression(Expression.ZERO, Expression.ZERO) | ||
var expected = castDouble(0.0) | ||
assertEquals(expected, castExpr(expr)) | ||
|
||
expr = AdditiveExpression(simpleExpr1, -simpleExpr1) | ||
expected = castDouble(1.0) | ||
assertEquals(expected, castExpr(expr)) | ||
|
||
expr = AdditiveExpression(simpleExpr1, MultiplicativeExpression(simpleExpr2, simpleExpr2.inverse())) | ||
expected = castDouble(25.132741228718345) | ||
assertEquals(expected, castExpr(expr)) | ||
|
||
expr = AdditiveExpression(simpleExpr1, AdditiveExpression(simpleExpr2, -simpleExpr1)) | ||
expected = castDouble(25.132741228718345) | ||
assertEquals(expected, castExpr(expr)) | ||
// | ||
// expr = MultiplicativeExpression(partialExpr, simpleExpr2) | ||
// expected = castDouble(-58.61224322251436) | ||
// assertEquals(expected, castExpr(expr)) | ||
// | ||
// expr = MultiplicativeExpression(partialExpr.inverse(), -simpleExpr2.inverse()) | ||
// expected = castDouble(0.01706128182474811) | ||
// assertEquals(expected, castExpr(expr)) | ||
// | ||
// val largeValue = maxValue.toDouble().toBigDecimal().toBigInteger() | ||
// val smallValue = (-maxValue.toDouble()).toBigDecimal().toBigInteger() | ||
// | ||
// val minExpr = SimpleExpression(Term.fromValues(ExactFraction(smallValue), emptyList())) | ||
// expr = MultiplicativeExpression(minExpr, SimpleExpression(Term.fromValues(one, listOf(Log(11).inverse())))) | ||
// assertSucceeds("Cast expected to succeed") { castExpr(expr) } | ||
// | ||
// expr = MultiplicativeExpression(minExpr, SimpleExpression(Term.fromValues(ExactFraction.TEN, listOf(Log(11))))) | ||
// assertCastingOverflow(type, expr) { castExpr(expr) } | ||
// | ||
// val maxExpr = SimpleExpression(Term.fromValues(ExactFraction(largeValue), emptyList())) | ||
// expr = MultiplicativeExpression(maxExpr, SimpleExpression(Term.fromValues(one, listOf(Log(11).inverse())))) | ||
// assertSucceeds("Cast expected to succeed") { castExpr(expr) } | ||
// | ||
// expr = MultiplicativeExpression(maxExpr, SimpleExpression(Term.fromValues(ExactFraction.TEN, listOf(Log(11))))) | ||
// assertCastingOverflow(type, expr) { castExpr(expr) } | ||
} |
82 changes: 82 additions & 0 deletions
82
...c/test/kotlin/xyz/lbres/exactnumbers/expressions/expression/additive/stringEqualsTests.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package xyz.lbres.exactnumbers.expressions.expression.additive | ||
|
||
import xyz.lbres.exactnumbers.expressions.Expression | ||
import xyz.lbres.exactnumbers.expressions.expression.* // ktlint-disable no-wildcard-imports no-unused-imports | ||
import kotlin.test.assertEquals | ||
import kotlin.test.assertNotEquals | ||
|
||
fun runEqualsTests() { | ||
// equal | ||
var expr1 = MultiplicativeExpression(simpleExpr1, Expression.ZERO) | ||
assertEquals(expr1, expr1) | ||
|
||
expr1 = MultiplicativeExpression(simpleExpr1, simpleExpr1) | ||
assertEquals(expr1, expr1) | ||
|
||
expr1 = MultiplicativeExpression(-simpleExpr2, partialMultExpr) | ||
assertEquals(expr1, expr1) | ||
|
||
expr1 = MultiplicativeExpression(simpleExpr2, simpleExpr3) | ||
var expr2 = MultiplicativeExpression(simpleExpr3, simpleExpr2) | ||
assertEquals(expr1, expr2) | ||
assertEquals(expr2, expr1) | ||
|
||
expr1 = MultiplicativeExpression(simpleExpr2, simpleExpr1) | ||
expr2 = MultiplicativeExpression(-simpleExpr1, -simpleExpr2) | ||
assertEquals(expr1, expr2) | ||
assertEquals(expr2, expr1) | ||
|
||
expr1 = MultiplicativeExpression(simpleExpr3, simpleExpr3.inverse()) | ||
expr2 = MultiplicativeExpression(Expression.ONE, Expression.ONE) | ||
assertEquals(expr1, expr2) | ||
assertEquals(expr2, expr1) | ||
|
||
expr1 = MultiplicativeExpression(Expression.ONE, MultiplicativeExpression(simpleExpr1, simpleExpr2)) | ||
expr2 = MultiplicativeExpression(simpleExpr1, simpleExpr2) | ||
assertEquals(expr1, expr2) | ||
assertEquals(expr2, expr1) | ||
|
||
expr1 = MultiplicativeExpression(Expression.ZERO, partialMultExpr) | ||
expr2 = MultiplicativeExpression(simpleExpr2, MultiplicativeExpression(simpleExpr3, Expression.ZERO)) | ||
assertEquals(expr1, expr2) | ||
assertEquals(expr2, expr1) | ||
|
||
// not equal | ||
expr1 = MultiplicativeExpression(simpleExpr1, simpleExpr1) | ||
expr2 = MultiplicativeExpression(simpleExpr1, -simpleExpr1) | ||
assertNotEquals(expr1, expr2) | ||
assertNotEquals(expr2, expr1) | ||
|
||
expr1 = MultiplicativeExpression(simpleExpr2.inverse(), MultiplicativeExpression(simpleExpr1, simpleExpr2)) | ||
expr2 = MultiplicativeExpression(simpleExpr1, simpleExpr2) | ||
assertNotEquals(expr1, expr2) | ||
assertNotEquals(expr2, expr1) | ||
|
||
expr1 = MultiplicativeExpression(simpleExpr1, partialMultExpr) | ||
expr2 = MultiplicativeExpression(simpleExpr1.inverse(), partialMultExpr) | ||
assertNotEquals(expr1, expr2) | ||
assertNotEquals(expr2, expr1) | ||
|
||
expr1 = MultiplicativeExpression(simpleExpr1, partialMultExpr) | ||
expr2 = MultiplicativeExpression(simpleExpr1.inverse(), partialMultExpr.inverse()) | ||
assertNotEquals(expr1, expr2) | ||
assertNotEquals(expr2, expr1) | ||
} | ||
|
||
fun runToStringTests() { | ||
var expr = AdditiveExpression(simpleExpr1, simpleExpr1) | ||
var expected = "($simpleExpr1+$simpleExpr1)" | ||
assertEquals(expected, expr.toString()) | ||
|
||
expr = AdditiveExpression(simpleExpr1.inverse(), simpleExpr3) | ||
expected = "(${simpleExpr1.inverse()}+$simpleExpr3)" | ||
assertEquals(expected, expr.toString()) | ||
|
||
expr = AdditiveExpression(partialMultExpr, simpleExpr2) | ||
expected = "((${simpleExpr3}x$simpleExpr1)+$simpleExpr2)" | ||
assertEquals(expected, expr.toString()) | ||
|
||
expr = AdditiveExpression(partialAddExpr, partialMultExpr) | ||
expected = "(($simpleExpr2+$simpleExpr3)+(${simpleExpr3}x$simpleExpr1))" | ||
assertEquals(expected, expr.toString()) | ||
} |
Oops, something went wrong.