Checkpoint
This commit is contained in:
parent
e3c84e1761
commit
e73e5cbfc8
32 changed files with 1354 additions and 92 deletions
357
tests/prolog/logic/ArithmeticTests.kt
Normal file
357
tests/prolog/logic/ArithmeticTests.kt
Normal file
|
@ -0,0 +1,357 @@
|
|||
package prolog.logic
|
||||
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import prolog.ast.terms.Integer
|
||||
import prolog.ast.terms.Term
|
||||
import prolog.ast.terms.Variable
|
||||
|
||||
class ArithmeticTests {
|
||||
@Test
|
||||
fun `1_between_0_and_2`() {
|
||||
val result = between(Integer(0), Integer(2), Integer(1))
|
||||
|
||||
assertTrue(result.any(), "Expected 1 to be between 0 and 2")
|
||||
assertEquals(emptyMap<Variable, Term>(), result.first(), "Expected no substitutions")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `3_not_between_0_and_2`() {
|
||||
val result = between(Integer(0), Integer(2), Integer(3))
|
||||
|
||||
assertTrue(result.none(), "Expected 3 not to be between 0 and 2")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun numbers_between_0_and_2() {
|
||||
val result = between(Integer(0), Integer(2), Variable("X"))
|
||||
|
||||
val expectedResults = listOf(
|
||||
mapOf(Variable("X") to Integer(0)),
|
||||
mapOf(Variable("X") to Integer(1)),
|
||||
mapOf(Variable("X") to Integer(2))
|
||||
)
|
||||
|
||||
val actualResults = result.toList()
|
||||
assertEquals(expectedResults.size, actualResults.size, "Expected 3 results")
|
||||
for ((expected, actual) in expectedResults.zip(actualResults)) {
|
||||
for ((key, value) in expected) {
|
||||
assertTrue(actual.containsKey(key), "Expected key $key to be present")
|
||||
assertTrue(
|
||||
equivalent(value, actual[key]!!),
|
||||
"Expected value $value for key $key, but got ${actual[key]}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `succ(0) is 1`() {
|
||||
val t1 = Integer(0)
|
||||
|
||||
val expected = Integer(1)
|
||||
|
||||
val result = succ(t1, expected, emptyMap())
|
||||
|
||||
assertTrue(result.any(), "Expected 0 + 1 to be equal to 1")
|
||||
assertTrue(result.first().isEmpty(), "Expected no substitutions")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `succ(1) is 2`() {
|
||||
val t1 = Integer(1)
|
||||
|
||||
val expected = Integer(2)
|
||||
|
||||
val result = succ(t1, expected, emptyMap())
|
||||
|
||||
assertTrue(result.any(), "Expected 1 + 1 to be equal to 2")
|
||||
assertTrue(result.first().isEmpty(), "Expected no substitutions")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `succ(1) is var`() {
|
||||
val t1 = Integer(1)
|
||||
val t2 = Variable("X")
|
||||
|
||||
val expected = Integer(2)
|
||||
|
||||
val result = succ(t1, t2, emptyMap()).toList()
|
||||
|
||||
assertTrue(result.isNotEmpty(), "Expected 1 + 1 to be equal to X")
|
||||
assertTrue(result[0][t2] == expected, "Expected X to be equal to 2")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `succ(bound-to-1-var) is 2`() {
|
||||
val t1 = Variable("X")
|
||||
val t2 = Integer(2)
|
||||
|
||||
t1.bind(Integer(1))
|
||||
|
||||
val result = succ(t1, t2, emptyMap())
|
||||
|
||||
assertTrue(result.any(), "Expected X + 1 to be equal to 2")
|
||||
assertTrue(result.first().isEmpty(), "Expected no substitutions")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `succ(bound-to-1-var) is 2 by map`() {
|
||||
val t1 = Variable("X")
|
||||
val t2 = Integer(2)
|
||||
|
||||
val result = succ(t1, t2, mapOf(t1 to Integer(1)))
|
||||
|
||||
assertTrue(result.any(), "Expected X + 1 to be equal to 2")
|
||||
assertTrue(result.first().isEmpty(), "Expected no substitutions")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `succ(bound-to-1-var) is var`() {
|
||||
val t1 = Variable("X")
|
||||
val t2 = Variable("Y")
|
||||
|
||||
t1.bind(Integer(1))
|
||||
|
||||
val result = succ(t1, t2, emptyMap()).toList()
|
||||
|
||||
assertTrue(result.isNotEmpty(), "Expected X + 1 to be equal to Y")
|
||||
assertTrue(result[0][t2] == Integer(2), "Expected Y to be equal to 2")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `succ(bound-to-1-var) is var by map`() {
|
||||
val t1 = Variable("X")
|
||||
val t2 = Variable("Y")
|
||||
|
||||
val result = succ(t1, t2, mapOf(t1 to Integer(1))).toList()
|
||||
|
||||
assertTrue(result.isNotEmpty(), "Expected X + 1 to be equal to Y")
|
||||
assertTrue(result[0][t2] == Integer(2), "Expected Y to be equal to 2")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `succ(var, 0) fails silently`() {
|
||||
val t1 = Variable("X")
|
||||
val t2 = Integer(0)
|
||||
|
||||
val result = succ(t1, t2, emptyMap()).toList()
|
||||
|
||||
assertTrue(result.isEmpty(), "Expected X + 1 to fail")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `1_plus_2_is_3`() {
|
||||
val t1 = Integer(1)
|
||||
val t2 = Integer(2)
|
||||
val t3 = Integer(3)
|
||||
|
||||
val result = plus(t1, t2, t3, emptyMap())
|
||||
|
||||
assertTrue(result.any(), "1 + 2 should be equal to 3")
|
||||
assertTrue(result.first().isEmpty(), "1 + 2 should be equal to 3")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `1_plus_2_is_not_4`() {
|
||||
val t1 = Integer(1)
|
||||
val t2 = Integer(2)
|
||||
val t3 = Integer(4)
|
||||
|
||||
val result = plus(t1, t2, t3, emptyMap())
|
||||
|
||||
assertTrue(result.none(), "1 + 2 should not be equal to 4")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `1_plus_2_is_variable`() {
|
||||
val t1 = Integer(1)
|
||||
val t2 = Integer(2)
|
||||
val t3 = Variable("X")
|
||||
|
||||
val result = plus(t1, t2, t3, emptyMap()).toList()
|
||||
|
||||
assertTrue(result.isNotEmpty(), "1 + 2 should be equal to X")
|
||||
assertTrue(equivalent(result[0][t3]!!, Integer(3)), "X should be equal to 3")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `1_plus_variable_is_3`() {
|
||||
val t1 = Integer(1)
|
||||
val t2 = Variable("X")
|
||||
val t3 = Integer(3)
|
||||
|
||||
val result = plus(t1, t2, t3, emptyMap()).toList()
|
||||
|
||||
assertTrue(result.isNotEmpty(), "1 + X should be equal to 3")
|
||||
assertTrue(equivalent(result[0][t2]!!, Integer(2)), "X should be equal to 2")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun variable_plus_2_is_3() {
|
||||
val t1 = Variable("X")
|
||||
val t2 = Integer(2)
|
||||
val t3 = Integer(3)
|
||||
|
||||
val result = plus(t1, t2, t3, emptyMap()).toList()
|
||||
|
||||
assertTrue(result.isNotEmpty(), "X + 2 should be equal to 3")
|
||||
assertTrue(equivalent(result[0][t1]!!, Integer(1)), "X should be equal to 1")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `1 plus 2 is bound-to-3-var`() {
|
||||
val t1 = Integer(1)
|
||||
val t2 = Integer(2)
|
||||
val t3 = Variable("X")
|
||||
|
||||
t3.bind(Integer(3))
|
||||
|
||||
val result = plus(t1, t2, t3, emptyMap())
|
||||
|
||||
assertTrue(result.any(), "1 + 2 should be equal to X")
|
||||
assertTrue(result.first().isEmpty(), "t3 should not be rebound")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `1 plus 2 is bound-to-3-var by map`() {
|
||||
val t1 = Integer(1)
|
||||
val t2 = Integer(2)
|
||||
val t3 = Variable("X")
|
||||
|
||||
val result = plus(t1, t2, t3, mapOf(Variable("X") to Integer(3)))
|
||||
|
||||
assertTrue(result.any(), "1 + 2 should be equal to X")
|
||||
assertTrue(result.first().isEmpty(), "t3 should not be rebound")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `1 plus 2 is bound-to-4-var`() {
|
||||
val t1 = Integer(1)
|
||||
val t2 = Integer(2)
|
||||
val t3 = Variable("X")
|
||||
|
||||
t3.bind(Integer(4))
|
||||
|
||||
val result = plus(t1, t2, t3, emptyMap())
|
||||
|
||||
assertTrue(result.none(), "1 + 2 should not be equal to X")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `1 plus bound-to-2-var is 3`() {
|
||||
val t1 = Integer(1)
|
||||
val t2 = Variable("X")
|
||||
val t3 = Integer(3)
|
||||
|
||||
t2.bind(Integer(2))
|
||||
|
||||
val result = plus(t1, t2, t3, emptyMap())
|
||||
|
||||
assertTrue(result.any(), "1 + X should be equal to 3")
|
||||
assertTrue(result.first().isEmpty(), "t2 should not be rebound")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `1 plus bound-to-2-var is not 4`() {
|
||||
val t1 = Integer(1)
|
||||
val t2 = Variable("X")
|
||||
val t3 = Integer(4)
|
||||
|
||||
t2.bind(Integer(2))
|
||||
|
||||
val result = plus(t1, t2, t3, emptyMap())
|
||||
|
||||
assertTrue(result.none(), "1 + X should not be equal to 4")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `bound-to-1-var plus 2 is 3`() {
|
||||
val t1 = Variable("X")
|
||||
val t2 = Integer(2)
|
||||
val t3 = Integer(3)
|
||||
|
||||
t1.bind(Integer(1))
|
||||
|
||||
val result = plus(t1, t2, t3, emptyMap())
|
||||
|
||||
assertTrue(result.any(), "X + 2 should be equal to 3")
|
||||
assertTrue(result.first().none(), "t1 should not be rebound")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `bound-to-1-var plus 2 is not 4`() {
|
||||
val t1 = Variable("X")
|
||||
val t2 = Integer(2)
|
||||
val t3 = Integer(4)
|
||||
|
||||
t1.bind(Integer(1))
|
||||
|
||||
val result = plus(t1, t2, t3, emptyMap())
|
||||
|
||||
assertTrue(result.none(), "X + 2 should not be equal to 4")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `two unbound vars plus should throw`() {
|
||||
val t1 = Variable("X")
|
||||
val t2 = Variable("Y")
|
||||
val t3 = Integer(3)
|
||||
|
||||
assertThrows<IllegalArgumentException> {
|
||||
plus(t1, t2, t3, emptyMap())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `bound-to-1-var plus bound-to-2-var is variable`() {
|
||||
val t1 = Variable("X")
|
||||
val t2 = Variable("Y")
|
||||
val t3 = Variable("Z")
|
||||
|
||||
t1.bind(Integer(1))
|
||||
t2.bind(Integer(2))
|
||||
|
||||
val result = plus(t1, t2, t3, emptyMap()).toList()
|
||||
|
||||
assertTrue(result.isNotEmpty(), "X + Y should be equal to Z")
|
||||
assertTrue(equivalent(result[0][t3]!!, Integer(3)), "Z should be equal to 3")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `1 times 1 is 1`() {
|
||||
val t1 = Integer(1)
|
||||
val t2 = Integer(1)
|
||||
val t3 = Integer(1)
|
||||
|
||||
val result = mul(t1, t2, t3, emptyMap()).toList()
|
||||
|
||||
assertEquals(1, result.size, "There should be one solution")
|
||||
assertTrue(result[0].isEmpty(), "1 * 1 should already be equal to 1")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `2 times 3 is 6`() {
|
||||
val t1 = Integer(2)
|
||||
val t2 = Integer(3)
|
||||
val t3 = Integer(6)
|
||||
|
||||
val result = mul(t1, t2, t3, emptyMap()).toList()
|
||||
|
||||
assertEquals(1, result.size, "There should be one solution")
|
||||
assertTrue(result[0].isEmpty(), "2 * 3 should already be equal to 6")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `2 times 3 is not 4`() {
|
||||
val t1 = Integer(2)
|
||||
val t2 = Integer(3)
|
||||
val t3 = Integer(4)
|
||||
|
||||
val result = mul(t1, t2, t3, emptyMap())
|
||||
|
||||
assertTrue(result.none(), "2 * 3 should not be equal to 4")
|
||||
}
|
||||
}
|
|
@ -3,10 +3,10 @@ package prolog.logic
|
|||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.junit.jupiter.api.Test
|
||||
import prolog.ast.terms.Integer
|
||||
import prolog.ast.terms.Atom
|
||||
import prolog.ast.terms.Structure
|
||||
import prolog.ast.terms.Variable
|
||||
import prolog.builtins.equivalent
|
||||
|
||||
/*
|
||||
* Based on: https://en.wikipedia.org/wiki/Unification_%28computer_science%29#Examples_of_syntactic_unification_of_first-order_terms
|
||||
|
@ -260,4 +260,25 @@ class UnifyTest {
|
|||
|
||||
assertFalse(result.isPresent, "Atom with different arity should not unify")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun identical_integers_unify() {
|
||||
val int1 = Integer(1)
|
||||
val int2 = Integer(1)
|
||||
|
||||
val result = unify(int1, int2)
|
||||
|
||||
assertTrue(result.isPresent, "Identical integers should unify")
|
||||
assertEquals(0, result.get().size, "No substitutions should be made")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun different_integers_do_not_unify() {
|
||||
val int1 = Integer(1)
|
||||
val int2 = Integer(2)
|
||||
|
||||
val result = unify(int1, int2)
|
||||
|
||||
assertFalse(result.isPresent, "Different integers should not unify")
|
||||
}
|
||||
}
|
Reference in a new issue