feat: Floating point arithmetic

This commit is contained in:
Tibo De Peuter 2025-04-15 18:23:04 +02:00
parent f9950a3fd3
commit 4a6850527f
Signed by: tdpeuter
GPG key ID: 38297DE43F75FFE2
13 changed files with 527 additions and 55 deletions

View file

@ -5,7 +5,8 @@ import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.RepeatedTest
import org.junit.jupiter.api.Test
import prolog.Substitutions
import prolog.ast.terms.Integer
import prolog.ast.arithmetic.Integer
import prolog.ast.arithmetic.Float
import prolog.ast.terms.Term
import prolog.ast.terms.Variable
@ -141,6 +142,19 @@ class ArithmeticTests {
assertTrue(result[0].getOrNull()!!.isEmpty(), "1 + 2 should be equal to 3")
}
@Test
fun `1,0 + 2,0 = 3,0`() {
val t1 = Float(1.0f)
val t2 = Float(2.0f)
val t3 = Float(3.0f)
val result = plus(t1, t2, t3, emptyMap()).toList()
assertEquals(1, result.size, "There should be one solution")
assertTrue(result[0].isSuccess, "Expected success")
assertTrue(result[0].getOrNull()!!.isEmpty(), "1.0 + 1.0 should already be equal to 2.0")
}
@Test
fun `1_plus_2_is_not_4`() {
val t1 = Integer(1)
@ -152,6 +166,17 @@ class ArithmeticTests {
assertTrue(result.none(), "1 + 2 should not be equal to 4")
}
@Test
fun `1,0 plus 2,0 is not 4,0`() {
val t1 = Float(1.0f)
val t2 = Float(2.0f)
val t3 = Float(4.0f)
val result = plus(t1, t2, t3, emptyMap())
assertTrue(result.none(), "1.0 + 2.0 should not be equal to 4.0")
}
@Test
fun `1_plus_2_is_variable`() {
val t1 = Integer(1)
@ -165,6 +190,22 @@ class ArithmeticTests {
assertEquals(Integer(3), result[0].getOrNull()!![t3], "X should be equal to 3")
}
@Test
fun `1,0 plus 2,0 is variable`() {
val t1 = Float(1.0f)
val t2 = Float(2.0f)
val t3 = Variable("X")
val result = plus(t1, t2, t3, emptyMap()).toList()
assertEquals(1, result.size, "1.0 + 2.0 should be equal to X")
assertTrue(result[0].isSuccess, "Expected success")
val subs = result[0].getOrNull()!!
assertTrue(equivalent(Float(3.0f), subs[t3]!!, emptyMap()), "X should be equal to 3.0")
}
@Test
fun `1_plus_variable_is_3`() {
val t1 = Integer(1)
@ -178,6 +219,22 @@ class ArithmeticTests {
assertEquals(Integer(2), result[0].getOrNull()!![t2], "X should be equal to 2")
}
@Test
fun `1,0 plus variable is 3,0`() {
val t1 = Float(1.0f)
val t2 = Variable("X")
val t3 = Float(3.0f)
val result = plus(t1, t2, t3, emptyMap()).toList()
assertEquals(1, result.size, "1.0 + X should be equal to 3.0")
assertTrue(result[0].isSuccess, "Expected success")
val subs = result[0].getOrNull()!!
assertTrue(equivalent(Float(2.0f), subs[t2]!!, emptyMap()), "X should be equal to 2.0")
}
@Test
fun variable_plus_2_is_3() {
val t1 = Variable("X")
@ -191,6 +248,22 @@ class ArithmeticTests {
assertEquals(Integer(1), result[0].getOrNull()!![t1], "X should be equal to 1")
}
@Test
fun `variable plus 2,0 is 3,0`() {
val t1 = Variable("X")
val t2 = Float(2.0f)
val t3 = Float(3.0f)
val result = plus(t1, t2, t3, emptyMap()).toList()
assertEquals(1, result.size, "X + 2.0 should be equal to 3.0")
assertTrue(result[0].isSuccess, "Expected success")
val subs = result[0].getOrNull()!!
assertTrue(equivalent(Float(1.0f), subs[t1]!!, emptyMap()), "X should be equal to 1.0")
}
@Test
fun `1 plus 2 is bound-to-3-var`() {
val t1 = Integer(1)
@ -204,6 +277,19 @@ class ArithmeticTests {
assertTrue(result[0].getOrNull()!!.isEmpty(), "t3 should not be rebound")
}
@Test
fun `1,0 plus 2,0 is bound-to-3,0-var`() {
val t1 = Float(1.0f)
val t2 = Float(2.0f)
val t3 = Variable("X")
val result = plus(t1, t2, t3, mapOf(Variable("X") to Float(3.0f))).toList()
assertEquals(1, result.size, "1.0 + 2.0 should be equal to X")
assertTrue(result[0].isSuccess, "Expected success")
assertTrue(result[0].getOrNull()!!.isEmpty(), "t3 should not be rebound")
}
@Test
fun `1 plus 2 is bound-to-4-var`() {
val t1 = Integer(1)
@ -215,6 +301,17 @@ class ArithmeticTests {
assertTrue(result.none(), "1 + 2 should not be equal to X")
}
@Test
fun `1,0 plus 2,0 is bound-to-4,0-var`() {
val t1 = Float(1.0f)
val t2 = Float(2.0f)
val t3 = Variable("X")
val result = plus(t1, t2, t3, mapOf(t3 to Float(4.0f)))
assertTrue(result.none(), "1.0 + 2.0 should not be equal to X")
}
@Test
fun `1 plus bound-to-2-var is 3`() {
val t1 = Integer(1)
@ -228,6 +325,19 @@ class ArithmeticTests {
assertTrue(result[0].getOrNull()!!.isEmpty(), "t2 should not be rebound")
}
@Test
fun `1,0 plus bound-to-2,0-var is 3,0`() {
val t1 = Float(1.0f)
val t2 = Variable("X")
val t3 = Float(3.0f)
val result = plus(t1, t2, t3, mapOf(t2 to Float(2.0f))).toList()
assertEquals(1, result.size, "1.0 + X should be equal to 3.0")
assertTrue(result[0].isSuccess, "Expected success")
assertTrue(result[0].getOrNull()!!.isEmpty(), "t2 should not be rebound")
}
@Test
fun `1 plus bound-to-2-var is not 4`() {
val t1 = Integer(1)
@ -239,6 +349,17 @@ class ArithmeticTests {
assertTrue(result.none(), "1 + X should not be equal to 4")
}
@Test
fun `1,0 plus bound-to-2,0-var is not 4,0`() {
val t1 = Float(1.0f)
val t2 = Variable("X")
val t3 = Float(4.0f)
val result = plus(t1, t2, t3, mapOf(t2 to Float(2.0f)))
assertTrue(result.none(), "1.0 + X should not be equal to 4.0")
}
@Test
fun `bound-to-1-var plus 2 is 3`() {
val t1 = Variable("X")
@ -252,6 +373,19 @@ class ArithmeticTests {
assertTrue(result[0].getOrNull()!!.none(), "t1 should not be rebound")
}
@Test
fun `bound-to-1-var plus 2,0 is 3,0`() {
val t1 = Variable("X")
val t2 = Float(2.0f)
val t3 = Float(3.0f)
val result = plus(t1, t2, t3, mapOf(t1 to Integer(1))).toList()
assertEquals(1, result.size, "X + 2.0 should be equal to 3.0")
assertTrue(result[0].isSuccess, "Expected success")
assertTrue(result[0].getOrNull()!!.none(), "t1 should not be rebound")
}
@Test
fun `bound-to-1-var plus 2 is not 4`() {
val t1 = Variable("X")
@ -263,6 +397,17 @@ class ArithmeticTests {
assertTrue(result.none(), "X + 2 should not be equal to 4")
}
@Test
fun `bound-to-1-var plus 2,0 is not 4,0`() {
val t1 = Variable("X")
val t2 = Float(2.0f)
val t3 = Float(4.0f)
val result = plus(t1, t2, t3, mapOf(t1 to Integer(1)))
assertTrue(result.none(), "X + 2.0 should not be equal to 4.0")
}
@Test
fun `two unbound vars plus should throw`() {
val t1 = Variable("X")
@ -294,6 +439,37 @@ class ArithmeticTests {
assertTrue(equivalent(result[0].getOrThrow()[t3]!!, Integer(3), result[0].getOrNull()!!), "Z should be equal to 3")
}
@Test
fun `bound-to-1,0-var plus bound-to-2,0-var is variable`() {
val t1 = Variable("X")
val t2 = Variable("Y")
val t3 = Variable("Z")
val map: Substitutions = mapOf(
t1 to Float(1.0f),
t2 to Float(2.0f),
)
val result = plus(t1, t2, t3, map).toList()
assertTrue(result.isNotEmpty(), "X + Y should be equal to Z")
assertTrue(result[0].isSuccess, "Expected success")
assertTrue(equivalent(result[0].getOrThrow()[t3]!!, Float(3.0f), result[0].getOrNull()!!), "Z should be equal to 3.0")
}
@Test
fun `int + float is float`() {
val t1 = Integer(1)
val t2 = Float(2.0f)
val t3 = Variable("X")
val result = plus(t1, t2, t3, emptyMap()).toList()
assertEquals(1, result.size, "There should be one solution")
assertTrue(result[0].isSuccess, "Expected success")
assertTrue(equivalent(result[0].getOrThrow()[t3]!!, Float(3.0f), result[0].getOrNull()!!), "X should be equal to 3.0")
}
@Test
fun `1 times 1 is 1`() {
val t1 = Integer(1)
@ -307,6 +483,19 @@ class ArithmeticTests {
assertTrue(result[0].getOrNull()!!.isEmpty(), "1 * 1 should already be equal to 1")
}
@Test
fun `1,0 times 1,0 is 1,0`() {
val t1 = Float(1.0f)
val t2 = Float(1.0f)
val t3 = Float(1.0f)
val result = mul(t1, t2, t3, emptyMap()).toList()
assertEquals(1, result.size, "There should be one solution")
assertTrue(result[0].isSuccess, "Expected success")
assertTrue(result[0].getOrNull()!!.isEmpty(), "1.0 * 1.0 should already be equal to 1.0")
}
@Test
fun `1 times 2 is 2`() {
val t1 = Integer(1)
@ -320,6 +509,19 @@ class ArithmeticTests {
assertTrue(result[0].getOrNull()!!.isEmpty(), "1 * 2 should already be equal to 2")
}
@Test
fun `1,0 times 2,0 is 2,0`() {
val t1 = Float(1.0f)
val t2 = Float(2.0f)
val t3 = Float(2.0f)
val result = mul(t1, t2, t3, emptyMap()).toList()
assertEquals(1, result.size, "There should be one solution")
assertTrue(result[0].isSuccess, "Expected success")
assertTrue(result[0].getOrNull()!!.isEmpty(), "1.0 * 2.0 should already be equal to 2.0")
}
@Test
fun `2 times 3 is 6`() {
val t1 = Integer(2)
@ -333,6 +535,19 @@ class ArithmeticTests {
assertTrue(result[0].getOrNull()!!.isEmpty(), "2 * 3 should already be equal to 6")
}
@Test
fun `2,0 times 3,0 is 6,0`() {
val t1 = Float(2.0f)
val t2 = Float(3.0f)
val t3 = Float(6.0f)
val result = mul(t1, t2, t3, emptyMap()).toList()
assertEquals(1, result.size, "There should be one solution")
assertTrue(result[0].isSuccess, "Expected success")
assertTrue(result[0].getOrNull()!!.isEmpty(), "2.0 * 3.0 should already be equal to 6.0")
}
@Test
fun `2 times 3 is not 4`() {
val t1 = Integer(2)
@ -344,6 +559,30 @@ class ArithmeticTests {
assertTrue(result.none(), "2 * 3 should not be equal to 4")
}
@Test
fun `2,0 times 3,0 is not 4,0`() {
val t1 = Float(2.0f)
val t2 = Float(3.0f)
val t3 = Float(4.0f)
val result = mul(t1, t2, t3, emptyMap())
assertTrue(result.none(), "2.0 * 3.0 should not be equal to 4.0")
}
@Test
fun `int times float is float`() {
val t1 = Integer(2)
val t2 = Float(3.0f)
val t3 = Variable("X")
val result = mul(t1, t2, t3, emptyMap()).toList()
assertEquals(1, result.size, "There should be one solution")
assertTrue(result[0].isSuccess, "Expected success")
assertTrue(equivalent(result[0].getOrThrow()[t3]!!, Float(6.0f), result[0].getOrNull()!!), "X should be equal to 6.0")
}
@RepeatedTest(100)
fun `random test for mul`() {
val t1 = Integer((0..1000).random())
@ -356,4 +595,18 @@ class ArithmeticTests {
assertTrue(result[0].isSuccess, "Expected success")
assertEquals(Integer(t1.value * t2.value), result[0].getOrNull()!![t3], "X should be equal to ${t1.value * t2.value}")
}
@RepeatedTest(100)
fun `random test for mul with floats`() {
val t1 = Float((0..1000).random().toFloat())
val t2 = Float((0..1000).random().toFloat())
val t3 = Variable("X")
val result = mul(t1, t2, t3, emptyMap()).toList()
assertEquals(1, result.size, "There should be one solution")
assertTrue(result[0].isSuccess, "Expected success")
val subs = result[0].getOrNull()!!
assertTrue(equivalent(subs[t3]!!, Float(t1.value * t2.value), subs), "X should be equal to ${t1.value * t2.value}")
}
}

View file

@ -4,7 +4,7 @@ import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import prolog.Substitutions
import prolog.ast.terms.Integer
import prolog.ast.arithmetic.Integer
import prolog.ast.terms.Atom
import prolog.ast.terms.Structure
import prolog.ast.terms.Variable