Skip to content

Commit

Permalink
feat: partial AdditiveExpression implementation + start of tests
Browse files Browse the repository at this point in the history
  • Loading branch information
lbressler13 committed Feb 11, 2024
1 parent b977663 commit b09abd0
Show file tree
Hide file tree
Showing 9 changed files with 425 additions and 26 deletions.
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("+")})"
}
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
}
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) }
}
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())
}
Loading

0 comments on commit b09abd0

Please sign in to comment.