refactor: Rework

This commit is contained in:
Tibo De Peuter 2025-04-15 12:32:59 +02:00
parent ac55ed4c64
commit 6469dd6ced
Signed by: tdpeuter
GPG key ID: 38297DE43F75FFE2
34 changed files with 593 additions and 552 deletions

View file

@ -200,7 +200,7 @@ class EvaluationTest {
Program.load(listOf(fact1, fact2, fact3))
val results = Query(Structure(Atom("a"), listOf(Variable("X")))).prove(emptyMap())
val results = Query(Structure(Atom("a"), listOf(Variable("X")))).satisfy(emptyMap())
val expectedResults = listOf(
mapOf(Variable("X") to Atom("b")),
@ -211,8 +211,8 @@ class EvaluationTest {
assertEquals(expectedResults.size, actualResults.size, "Number of results should match")
for (i in expectedResults.indices) {
assertEquals(expectedResults[i].size, actualResults[i].size, "Substitution size should match")
assertTrue(expectedResults[i].all { actualResults[i][it.key]?.let { it1 -> equivalent(it.value, it1) } ?: false }, "Substitution values should match")
assertEquals(expectedResults[i].size, actualResults[i].getOrNull()!!.size, "Substitution size should match")
assertTrue(expectedResults[i].all { actualResults[i].getOrNull()!![it.key]?.let { it1 -> equivalent(it.value, it1, emptyMap()) } ?: false }, "Substitution values should match")
}
}
}

View file

@ -3,9 +3,10 @@ package prolog.builtins
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import prolog.Substitutions
import prolog.ast.terms.Integer
import prolog.ast.terms.Term
import prolog.ast.terms.Variable
import prolog.logic.between
import prolog.logic.equivalent
class ArithmeticOperatorsTests {
@ -16,18 +17,21 @@ class ArithmeticOperatorsTests {
Integer(1)
)
var result = op1.evaluate(emptyMap()).first
var result = op1.satisfy(emptyMap()).toList()
assertEquals(True, result, "1 should be equal to 1")
assertEquals(1, result.size, "1 should be equal to 1")
assertTrue(result[0].isSuccess, "1 should be equal to 1")
val subs = result[0].getOrNull()!!
assertTrue(subs.isEmpty(), "1 should not be rebound")
val op2 = EvaluatesToDifferent(
Integer(1),
Integer(1)
)
result = op2.evaluate(emptyMap()).first
result = op2.satisfy(emptyMap()).toList()
assertEquals(False, result, "1 should not be different from 1")
assertEquals(0, result.size, "1 should not be different from 1")
}
@Test
@ -37,18 +41,21 @@ class ArithmeticOperatorsTests {
Integer(2)
)
var result = op1.evaluate(emptyMap()).first
var result = op1.satisfy(emptyMap()).toList()
assertEquals(0, result.size, "1 should not be equal to 2")
assertEquals(False, result, "1 should not be equal to 2")
val op2 = EvaluatesToDifferent(
Integer(1),
Integer(2)
)
result = op2.evaluate(emptyMap()).first
result = op2.satisfy(emptyMap()).toList()
assertEquals(True, result, "1 should be different from 2")
assertEquals(1, result.size, "1 should be different from 2")
assertTrue(result[0].isSuccess, "1 should be different from 2")
val subs = result[0].getOrNull()!!
assertTrue(subs.isEmpty(), "1 should not be rebound")
}
@Test
@ -58,18 +65,21 @@ class ArithmeticOperatorsTests {
Integer(5)
)
var result = op.evaluate(emptyMap()).first
var result = op.satisfy(emptyMap()).toList()
assertEquals(True, result, "2 + 3 should be equal to 5")
assertEquals(1, result.size, "2 + 3 should be equal to 5")
assertTrue(result[0].isSuccess, "2 + 3 should be equal to 5")
val subs = result[0].getOrNull()!!
assertTrue(subs.isEmpty(), "2 + 3 should not be rebound")
val op2 = EvaluatesToDifferent(
Add(Integer(2), Integer(3)),
Integer(5)
)
result = op2.evaluate(emptyMap()).first
result = op2.satisfy(emptyMap()).toList()
assertEquals(False, result, "2 + 3 should not be different from 5")
assertEquals(0, result.size, "2 + 3 should not be different from 5")
}
@Test
@ -79,18 +89,21 @@ class ArithmeticOperatorsTests {
Add(Integer(4), Integer(1))
)
var result = op.evaluate(emptyMap()).first
var result = op.satisfy(emptyMap()).toList()
assertEquals(True, result, "2 + 3 should be equal to 4 + 1")
assertEquals(1, result.size, "2 + 3 should be equal to 4 + 1")
assertTrue(result[0].isSuccess, "2 + 3 should be equal to 4 + 1")
val subs = result[0].getOrNull()!!
assertTrue(subs.isEmpty(), "2 + 3 should not be rebound")
val op2 = EvaluatesToDifferent(
Add(Integer(2), Integer(3)),
Add(Integer(4), Integer(1))
)
result = op2.evaluate(emptyMap()).first
result = op2.satisfy(emptyMap()).toList()
assertEquals(False, result, "2 + 3 should not be different from 4 + 1")
assertEquals(0, result.size, "2 + 3 should not be different from 4 + 1")
}
@Test
@ -100,14 +113,24 @@ class ArithmeticOperatorsTests {
Variable("X")
)
assertThrows<IllegalArgumentException> { op.evaluate(emptyMap()) }
var result = op.satisfy(emptyMap()).toList()
assertEquals(1, result.size, "One exception should be thrown")
assertTrue(result[0].isFailure, "One exception should be thrown")
var exceptions = result[0].exceptionOrNull()
assertTrue(exceptions is IllegalArgumentException, "One exception should be thrown")
val op2 = EvaluatesToDifferent(
Integer(1),
Variable("X")
)
assertThrows<IllegalArgumentException> { op2.evaluate(emptyMap()) }
result = op2.satisfy(emptyMap()).toList()
assertEquals(1, result.size, "One exception should be thrown")
assertTrue(result[0].isFailure, "One exception should be thrown")
exceptions = result[0].exceptionOrNull()
assertTrue(exceptions is IllegalArgumentException, "One exception should be thrown")
}
@Test
@ -117,14 +140,14 @@ class ArithmeticOperatorsTests {
Integer(1)
)
assertThrows<IllegalArgumentException> { op.evaluate(emptyMap()) }
assertThrows<IllegalArgumentException> { op.satisfy(emptyMap()) }
val op2 = EvaluatesToDifferent(
Add(Integer(1), Variable("X")),
Integer(1)
)
assertThrows<IllegalArgumentException> { op2.evaluate(emptyMap()) }
assertThrows<IllegalArgumentException> { op2.satisfy(emptyMap()) }
}
@Test
@ -134,7 +157,7 @@ class ArithmeticOperatorsTests {
Integer(1)
)
val result = op.prove(emptyMap())
val result = op.satisfy(emptyMap())
assertTrue(result.any(), "1 should be equal to 1")
}
@ -146,7 +169,7 @@ class ArithmeticOperatorsTests {
Integer(2)
)
val result = op.prove(emptyMap()).toList()
val result = op.satisfy(emptyMap()).toList()
assertTrue(result.isEmpty(), "1 should not be equal to 2")
}
@ -159,12 +182,11 @@ class ArithmeticOperatorsTests {
Integer(1)
)
t1.bind(Integer(1))
val result = op.satisfy(mapOf(t1 to Integer(1))).toList()
val result = op.prove(emptyMap())
assertTrue(result.any(), "X should be equal to 1")
assertTrue(result.first().isEmpty(), "X should not be rebound")
assertEquals(1, result.size, "X should be equal to 1")
assertTrue(result.first().isSuccess, "X should be equal to 1")
assertTrue(result.first().getOrNull()!!.isEmpty(), "X should not be rebound")
}
@Test
@ -175,9 +197,7 @@ class ArithmeticOperatorsTests {
Integer(1)
)
t1.bind(Integer(2))
val result = op.prove(emptyMap())
val result = op.satisfy(mapOf(t1 to Integer(2)))
assertFalse(result.any(), "X should not be equal to 1")
}
@ -189,11 +209,12 @@ class ArithmeticOperatorsTests {
Integer(1)
)
val result = op.prove(emptyMap()).toList()
val result = op.satisfy(emptyMap()).toList()
assertFalse(result.isEmpty(), "X should be equal to 1")
assertEquals(1, result[0].size, "X should be rebound")
assertTrue(equivalent(Integer(1), result[0][Variable("X")]!!), "X should be equal to 1")
assertTrue(result.first().isSuccess, "X should be equal to 1")
assertEquals(1, result[0].getOrNull()!!.size, "X should be rebound")
assertTrue(equivalent(Integer(1), result[0].getOrNull()!![Variable("X")]!!, emptyMap()), "X should be equal to 1")
}
@Test
@ -203,11 +224,18 @@ class ArithmeticOperatorsTests {
Add(Integer(1), Integer(2))
)
val result = op.prove(emptyMap()).toList()
val result = op.satisfy(emptyMap()).toList()
assertFalse(result.isEmpty(), "X should be equal to 3")
assertEquals(1, result[0].size, "X should be rebound")
assertTrue(equivalent(Integer(3), result[0][Variable("X")]!!), "X should be equal to 3")
assertTrue(result.first().isSuccess, "X should be equal to 3")
val subs = result[0].getOrNull()!!
assertEquals(1, subs.size, "X should be rebound")
assertTrue(
equivalent(Integer(3), subs[Variable("X")]!!, emptyMap()),
"X should be equal to 3"
)
}
@Test
@ -217,7 +245,12 @@ class ArithmeticOperatorsTests {
Variable("Y")
)
assertThrows<IllegalArgumentException> { op.prove(emptyMap()) }
val result = op.satisfy(emptyMap()).toList()
assertEquals(1, result.size, "One exception should be thrown")
assertTrue(result[0].isFailure, "One exception should be thrown")
val exceptions = result[0].exceptionOrNull()
assertTrue(exceptions is IllegalArgumentException, "One exception should be thrown")
}
@Test
@ -227,23 +260,29 @@ class ArithmeticOperatorsTests {
Variable("X")
)
assertThrows<IllegalArgumentException> { op.prove(emptyMap()) }
val result = op.satisfy(emptyMap()).toList()
assertEquals(1, result.size, "One exception should be thrown")
assertTrue(result[0].isFailure, "One exception should be thrown")
val exceptions = result[0].exceptionOrNull()
assertTrue(exceptions is IllegalArgumentException, "One exception should be thrown")
}
/**
* ?- X = 1, Y = 1 + 2, X is Y.
* false.
*/
@Test
fun `var is bound-to-sum-var`() {
val t1 = Variable("X")
val t2 = Variable("Y")
t2.bind(Add(Integer(1), Integer(2)))
val op = Is(t1, t2)
val map: Substitutions = mapOf(t1 to Integer(1), t2 to Add(Integer(1), Integer(2)))
val result = op.prove(emptyMap()).toList()
val result = op.satisfy(map).toList()
assertTrue(result.isNotEmpty(), "X should be equal to 3")
assertEquals(1, result[0].size, "X should be rebound, Y should not")
assertTrue(equivalent(Integer(3), result[0][t1]!!), "X should be equal to 3")
assertEquals(0, result.size, "X should not be equal to Y")
}
/**
@ -268,47 +307,38 @@ class ArithmeticOperatorsTests {
fun `negate 1 to get -1`() {
val t1 = Integer(1)
val result = Negate(t1).evaluate(emptyMap()).first
val result = Negate(t1).simplify(emptyMap())
assertEquals(Integer(-1), result, "negate(1) should be equal to -1")
assertEquals(Integer(-1), result.to, "negate(1) should be equal to -1")
}
@Test
fun `negate 0 to get 0`() {
val t1 = Integer(0)
val result = Negate(t1).evaluate(emptyMap()).first
val result = Negate(t1).simplify(emptyMap())
assertEquals(Integer(0), result, "negate(0) should be equal to 0")
assertEquals(Integer(0), result.to, "negate(0) should be equal to 0")
}
@Test
fun `negate -1 to get 1`() {
val t1 = Integer(-1)
val result = Negate(t1).evaluate(emptyMap()).first
val result = Negate(t1).simplify(emptyMap())
assertEquals(Integer(1), result, "negate(-1) should be equal to 1")
assertEquals(Integer(1), result.to, "negate(-1) should be equal to 1")
}
@Test
fun `negate bound-to-1-var to get -1`() {
val t1 = Variable("X")
t1.bind(Integer(1))
val map: Substitutions = mapOf(t1 to Integer(1))
val result = Negate(t1).evaluate(emptyMap()).first
val result = Negate(t1).simplify(map)
assertEquals(Integer(-1), result, "negate(1) should be equal to -1")
}
@Test
fun `negate bound-to-1-var to get -1 by map`() {
val t1 = Variable("X")
val result = Negate(t1).evaluate(mapOf(t1 to Integer(1))).first
assertEquals(Integer(-1), result, "negate(1) should be equal to -1")
assertEquals(Integer(-1), result.to, "negate(1) should be equal to -1")
}
@Test
@ -316,9 +346,9 @@ class ArithmeticOperatorsTests {
val t1 = Integer(1)
val t2 = Integer(3)
val result = Negate(Add(t1, t2)).evaluate(emptyMap()).first
val result = Negate(Add(t1, t2)).simplify(emptyMap())
assertEquals(Integer(-4), result, "negate(1 + 3) should be equal to -4")
assertEquals(Integer(-4), result.to, "negate(1 + 3) should be equal to -4")
}
@Test
@ -326,9 +356,9 @@ class ArithmeticOperatorsTests {
val t1 = Integer(1)
val t2 = Integer(2)
val result = Add(t1, t2).evaluate(emptyMap()).first
val result = Add(t1, t2).simplify(emptyMap())
assertEquals(Integer(3), result, "1 + 2 should be equal to 3")
assertEquals(Integer(3), result.to, "1 + 2 should be equal to 3")
}
@Test
@ -336,21 +366,10 @@ class ArithmeticOperatorsTests {
val t1 = Integer(1)
val t2 = Variable("X")
t2.bind(Integer(2))
val subs: Substitutions = mapOf(t2 to Integer(2))
val result = Add(t1, t2).simplify(subs)
val result = Add(t1, t2).evaluate(emptyMap()).first
assertEquals(Integer(3), result, "1 + 2 should be equal to 3")
}
@Test
fun `Add 1 and bound-to-2-var to get 3 by map`() {
val t1 = Integer(1)
val t2 = Variable("X")
val result = Add(t1, t2).evaluate(mapOf(t2 to Integer(2))).first
assertEquals(Integer(3), result, "1 + 2 should be equal to 3")
assertEquals(Integer(3), result.to, "1 + 2 should be equal to 3")
}
@Test
@ -358,9 +377,9 @@ class ArithmeticOperatorsTests {
val t1 = Integer(1)
val t2 = Integer(2)
val result = Subtract(t1, t2).evaluate(emptyMap()).first
val result = Subtract(t1, t2).simplify(emptyMap())
assertEquals(Integer(-1), result, "1 - 2 should be equal to -1")
assertEquals(Integer(-1), result.to, "1 - 2 should be equal to -1")
}
@Test
@ -368,9 +387,9 @@ class ArithmeticOperatorsTests {
val t1 = Integer(3)
val t2 = Integer(1)
val result = Subtract(t1, t2).evaluate(emptyMap()).first
val result = Subtract(t1, t2).simplify(emptyMap())
assertEquals(Integer(2), result, "3 - 1 should be equal to 2")
assertEquals(Integer(2), result.to, "3 - 1 should be equal to 2")
}
@Test
@ -378,9 +397,9 @@ class ArithmeticOperatorsTests {
val t1 = Integer(0)
val t2 = Integer(0)
val result = Multiply(t1, t2).evaluate(emptyMap()).first
val result = Multiply(t1, t2).simplify(emptyMap())
assertEquals(Integer(0), result, "0 * 0 should be equal to 0")
assertEquals(Integer(0), result.to, "0 * 0 should be equal to 0")
}
@Test
@ -388,9 +407,9 @@ class ArithmeticOperatorsTests {
val t1 = Integer(1)
val t2 = Integer(0)
val result = Multiply(t1, t2).evaluate(emptyMap()).first
val result = Multiply(t1, t2).simplify(emptyMap())
assertEquals(Integer(0), result, "1 * 0 should be equal to 0")
assertEquals(Integer(0), result.to, "1 * 0 should be equal to 0")
}
@Test
@ -398,9 +417,9 @@ class ArithmeticOperatorsTests {
val t1 = Integer(0)
val t2 = Integer(1)
val result = Multiply(t1, t2).evaluate(emptyMap()).first
val result = Multiply(t1, t2).simplify(emptyMap())
assertEquals(Integer(0), result, "0 * 1 should be equal to 0")
assertEquals(Integer(0), result.to, "0 * 1 should be equal to 0")
}
@Test
@ -408,9 +427,9 @@ class ArithmeticOperatorsTests {
val t1 = Integer(3)
val t2 = Integer(1)
val result = Multiply(t1, t2).evaluate(emptyMap()).first
val result = Multiply(t1, t2).simplify(emptyMap())
assertEquals(Integer(3), result, "3 * 1 should be equal to 3")
assertEquals(Integer(3), result.to, "3 * 1 should be equal to 3")
}
@Test
@ -418,8 +437,8 @@ class ArithmeticOperatorsTests {
val t1 = Integer(2)
val t2 = Integer(3)
val result = Multiply(t1, t2).evaluate(emptyMap())
val result = Multiply(t1, t2).simplify(emptyMap())
assertEquals(Integer(6), result.first, "2 * 3 should be equal to 6")
assertEquals(Integer(6), result.to, "2 * 3 should be equal to 6")
}
}

View file

@ -16,7 +16,7 @@ class UnificationTest {
val variable = Variable("X")
val atom = Atom("a")
val result = Equivalent(variable, atom).prove(emptyMap())
val result = Equivalent(variable, atom).satisfy(emptyMap())
assertFalse(result.any(), "Variable and atom should not be equivalent")
}
@ -30,10 +30,11 @@ class UnificationTest {
val atom1 = Atom("a")
val atom2 = Atom("a")
val result = Equivalent(atom1, atom2).prove(emptyMap())
val result = Equivalent(atom1, atom2).satisfy(emptyMap())
assertTrue(result.any(), "Identical atoms should be equivalent")
assertEquals(0, result.first().size, "No substitutions should be made")
assertTrue(result.first().isSuccess, "Result should be successful")
assertEquals(0, result.first().getOrNull()!!.size, "No substitutions should be made")
}
/**
@ -45,7 +46,7 @@ class UnificationTest {
val addition = Add(Integer(1), Integer(2))
val solution = Integer(3)
val result = Equivalent(addition, solution).prove(emptyMap())
val result = Equivalent(addition, solution).satisfy(emptyMap())
assertFalse(result.any(), "Addition should be equivalent")
}

View file

@ -3,20 +3,19 @@ package prolog.builtins
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import prolog.Substitutions
import prolog.ast.terms.Atom
import prolog.ast.terms.Structure
import prolog.ast.terms.Variable
import prolog.logic.compound
import prolog.logic.nonvariable
import prolog.logic.variable
import kotlin.test.assertEquals
class VerificationTest {
@Test
fun unbound_variable_is_var() {
val variable = Variable("X")
assertTrue(variable(variable))
assertTrue(variable.alias().isEmpty)
}
@Test
@ -26,10 +25,8 @@ class VerificationTest {
assertTrue(variable(variable))
val atom = Atom("a")
variable.bind(atom)
assertFalse(variable(variable))
assertEquals(atom, variable.alias().get())
assertFalse(variable(variable, mapOf(variable to atom)))
}
@Test
@ -39,10 +36,8 @@ class VerificationTest {
assertTrue(variable(variable))
val structure = Structure(Atom("a"), listOf(Atom("b")))
variable.bind(structure)
assertFalse(variable(variable))
assertEquals(structure, variable.alias().get())
assertFalse(variable(variable, mapOf(variable to structure)))
}
/**
@ -52,8 +47,7 @@ class VerificationTest {
@Test
fun variable_bound_to_itself_is_var() {
val variable = Variable("X")
variable.bind(variable)
assertTrue(variable(variable))
assertTrue(variable(variable, mapOf(variable to variable)))
}
/**
@ -64,9 +58,7 @@ class VerificationTest {
fun variable_bound_to_another_variable_is_var() {
val variable1 = Variable("X")
val variable2 = Variable("Y")
variable1.bind(variable2)
assertTrue(variable(variable1))
assertEquals(variable2, variable1.alias().get())
assertTrue(variable(variable1, mapOf(variable1 to variable2)))
}
/**
@ -77,9 +69,11 @@ class VerificationTest {
fun variable_bound_to_bound_variable_is_not_var() {
val variable1 = Variable("X")
val variable2 = Variable("Y")
variable2.bind(Atom("a"))
variable1.bind(variable2)
assertFalse(variable(variable1))
val map: Substitutions = mapOf(
variable1 to variable2,
variable2 to Atom("a")
)
assertFalse(variable(variable1, map))
}
@Test
@ -117,10 +111,8 @@ class VerificationTest {
assertFalse(nonvariable(variable))
val atom = Atom("a")
variable.bind(atom)
assertTrue(nonvariable(variable))
assertEquals(atom, variable.alias().get())
assertTrue(nonvariable(variable, mapOf(variable to atom)))
}
/**
@ -134,10 +126,8 @@ class VerificationTest {
assertFalse(nonvariable(variable))
val structure = Structure(Atom("a"), listOf(Atom("b")))
variable.bind(structure)
assertTrue(nonvariable(variable))
assertEquals(structure, variable.alias().get())
assertTrue(nonvariable(variable, mapOf(variable to structure)))
}
/**
@ -147,8 +137,7 @@ class VerificationTest {
@Test
fun variable_bound_to_itself_is_not_nonvar() {
val variable = Variable("X")
variable.bind(variable)
assertFalse(nonvariable(variable))
assertFalse(nonvariable(variable, mapOf(variable to variable)))
}
/**
@ -159,8 +148,7 @@ class VerificationTest {
fun variable_bound_to_another_variable_is_not_nonvar() {
val variable1 = Variable("X")
val variable2 = Variable("Y")
variable1.bind(variable2)
assertFalse(nonvariable(variable1))
assertFalse(nonvariable(variable1, mapOf(variable1 to variable2)))
}
@Test
@ -195,8 +183,7 @@ class VerificationTest {
fun bound_variable_to_atom_is_not_compound() {
val variable = Variable("X")
val atom = Atom("a")
variable.bind(atom)
assertFalse(compound(variable))
assertFalse(compound(variable, mapOf(variable to atom)))
}
/**
@ -207,8 +194,7 @@ class VerificationTest {
fun bound_variable_to_compound_term_is_compound() {
val variable = Variable("X")
val structure = Structure(Atom("a"), listOf(Atom("b")))
variable.bind(structure)
assertTrue(compound(variable))
assertTrue(compound(variable, mapOf(variable to structure)))
}
/**
@ -221,13 +207,12 @@ class VerificationTest {
val variable2 = Variable("Y")
val structure = Structure(Atom("a"), listOf(Atom("b")))
variable2.bind(structure)
variable1.bind(variable2)
val subs: Substitutions = mapOf(
variable1 to variable2,
variable2 to structure
)
assertTrue(compound(variable1))
assertEquals(structure, variable2.alias().get())
assertEquals(variable2, variable1.alias().get())
assertTrue(compound(variable1, subs))
}
@Test

View file

@ -4,7 +4,7 @@ import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.RepeatedTest
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import prolog.Substitutions
import prolog.ast.terms.Integer
import prolog.ast.terms.Term
import prolog.ast.terms.Variable
@ -15,7 +15,11 @@ class ArithmeticTests {
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")
assertTrue(result.first().isSuccess, "Expected success")
val subs = result.first().getOrNull()!!
assertEquals(emptyMap<Variable, Term>(), subs, "Expected no substitutions")
}
@Test
@ -39,9 +43,11 @@ class ArithmeticTests {
assertEquals(expectedResults.size, actualResults.size, "Expected 3 results")
for ((expected, actual) in expectedResults.zip(actualResults)) {
for ((key, value) in expected) {
assertTrue(actual.isSuccess, "Expected success")
val actual = actual.getOrNull()!!
assertTrue(actual.containsKey(key), "Expected key $key to be present")
assertTrue(
equivalent(value, actual[key]!!),
equivalent(value, actual[key]!!, emptyMap()),
"Expected value $value for key $key, but got ${actual[key]}"
)
}
@ -93,20 +99,6 @@ class ArithmeticTests {
val t1 = Variable("X")
val t2 = Integer(2)
t1.bind(Integer(1))
val result = succ(t1, t2, emptyMap()).toList()
assertEquals(1, result.size, "Expected X + 1 to be equal to 2")
assertTrue(result[0].isSuccess, "Expected success")
assertTrue(result[0].getOrNull()!!.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))).toList()
assertEquals(1, result.size, "Expected X + 1 to be equal to 2")
@ -119,20 +111,6 @@ class ArithmeticTests {
val t1 = Variable("X")
val t2 = Variable("Y")
t1.bind(Integer(1))
val result = succ(t1, t2, emptyMap()).toList()
assertEquals(1, result.size, "Expected X + 1 to be equal to Y")
assertTrue(result[0].isSuccess, "Expected success")
assertEquals(Integer(2), result[0].getOrNull()!![t2], "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()
assertEquals(1, result.size, "Expected X + 1 to be equal to Y")
@ -219,21 +197,6 @@ class ArithmeticTests {
val t2 = Integer(2)
val t3 = Variable("X")
t3.bind(Integer(3))
val result = plus(t1, t2, t3, emptyMap()).toList()
assertEquals(1, result.size, "1 + 2 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-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))).toList()
assertEquals(1, result.size, "1 + 2 should be equal to X")
@ -247,9 +210,7 @@ class ArithmeticTests {
val t2 = Integer(2)
val t3 = Variable("X")
t3.bind(Integer(4))
val result = plus(t1, t2, t3, emptyMap())
val result = plus(t1, t2, t3, mapOf(t3 to Integer(4)))
assertTrue(result.none(), "1 + 2 should not be equal to X")
}
@ -260,9 +221,7 @@ class ArithmeticTests {
val t2 = Variable("X")
val t3 = Integer(3)
t2.bind(Integer(2))
val result = plus(t1, t2, t3, emptyMap()).toList()
val result = plus(t1, t2, t3, mapOf(t2 to Integer(2))).toList()
assertEquals(1, result.size, "1 + X should be equal to 3")
assertTrue(result[0].isSuccess, "Expected success")
@ -275,9 +234,7 @@ class ArithmeticTests {
val t2 = Variable("X")
val t3 = Integer(4)
t2.bind(Integer(2))
val result = plus(t1, t2, t3, emptyMap())
val result = plus(t1, t2, t3, mapOf(t2 to Integer(2)))
assertTrue(result.none(), "1 + X should not be equal to 4")
}
@ -288,9 +245,7 @@ class ArithmeticTests {
val t2 = Integer(2)
val t3 = Integer(3)
t1.bind(Integer(1))
val result = plus(t1, t2, t3, emptyMap()).toList()
val result = plus(t1, t2, t3, mapOf(t1 to Integer(1))).toList()
assertEquals(1, result.size, "X + 2 should be equal to 3")
assertTrue(result[0].isSuccess, "Expected success")
@ -303,9 +258,7 @@ class ArithmeticTests {
val t2 = Integer(2)
val t3 = Integer(4)
t1.bind(Integer(1))
val result = plus(t1, t2, t3, emptyMap())
val result = plus(t1, t2, t3, mapOf(t1 to Integer(1)))
assertTrue(result.none(), "X + 2 should not be equal to 4")
}
@ -329,14 +282,16 @@ class ArithmeticTests {
val t2 = Variable("Y")
val t3 = Variable("Z")
t1.bind(Integer(1))
t2.bind(Integer(2))
val map: Substitutions = mapOf(
t1 to Integer(1),
t2 to Integer(2),
)
val result = plus(t1, t2, t3, emptyMap()).toList()
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]!!, Integer(3)), "Z should be equal to 3")
assertTrue(equivalent(result[0].getOrThrow()[t3]!!, Integer(3), result[0].getOrNull()!!), "Z should be equal to 3")
}
@Test

View file

@ -3,10 +3,12 @@ package prolog.logic
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.terms.Atom
import prolog.ast.terms.Structure
import prolog.ast.terms.Variable
import prolog.builtins.Add
/*
* Based on: https://en.wikipedia.org/wiki/Unification_%28computer_science%29#Examples_of_syntactic_unification_of_first-order_terms
@ -19,8 +21,8 @@ class UnifyTest {
val result = unify(atom1, atom2)
assertTrue(result.isPresent, "Identical atoms should unify")
assertEquals(0, result.get().size, "No substitutions should be made")
assertTrue(result.isSuccess, "Identical atoms should unify")
assertEquals(0, result.getOrNull()!!.size, "No substitutions should be made")
}
@Test
@ -30,7 +32,7 @@ class UnifyTest {
val result = unify(atom1, atom2)
assertFalse(result.isPresent, "Different atoms should not unify")
assertFalse(result.isSuccess, "Different atoms should not unify")
}
/**
@ -44,8 +46,8 @@ class UnifyTest {
val result = unify(variable1, variable2)
assertTrue(result.isPresent, "Identical variables should unify")
assertEquals(0, result.get().size, "No substitutions should be made")
assertTrue(result.isSuccess, "Identical variables should unify")
assertEquals(0, result.getOrNull()!!.size, "No substitutions should be made")
}
@Test
@ -55,9 +57,9 @@ class UnifyTest {
val result = unify(atom, variable)
assertTrue(result.isPresent, "Variable should unify with atom")
assertEquals(1, result.get().size, "There should be one substitution")
assertEquals(atom, variable.alias().get(), "Variable should be substituted with atom")
assertTrue(result.isSuccess, "Variable should unify with atom")
assertEquals(1, result.getOrNull()!!.size, "There should be one substitution")
assertEquals(atom, result.getOrNull()!![variable], "Variable should be substituted with atom")
}
@Test
@ -67,9 +69,9 @@ class UnifyTest {
val result = unify(variable1, variable2)
assertTrue(result.isPresent)
assertEquals(1, result.get().size)
assertEquals(variable2, variable1.alias().get(), "Variable 1 should alias to variable 2")
assertTrue(result.isSuccess)
assertEquals(1, result.getOrNull()!!.size)
assertEquals(variable2, result.getOrNull()!![variable1], "Variable 1 should alias to variable 2")
}
@Test
@ -79,8 +81,8 @@ class UnifyTest {
val result = unify(structure1, structure2)
assertTrue(result.isPresent, "Identical compound terms should unify")
assertEquals(0, result.get().size, "No substitutions should be made")
assertTrue(result.isSuccess, "Identical compound terms should unify")
assertEquals(0, result.getOrNull()!!.size, "No substitutions should be made")
}
@Test
@ -90,7 +92,7 @@ class UnifyTest {
val result = unify(structure1, structure2)
assertFalse(result.isPresent, "Different compound terms should not unify")
assertFalse(result.isSuccess, "Different compound terms should not unify")
}
@Test
@ -100,7 +102,7 @@ class UnifyTest {
val result = unify(structure1, structure2)
assertFalse(result.isPresent, "Compound terms with different functors should not unify")
assertFalse(result.isSuccess, "Compound terms with different functors should not unify")
}
/**
@ -114,10 +116,14 @@ class UnifyTest {
val result = unify(variable, structure)
assertTrue(result.isPresent, "Variable should unify with compound term")
assertEquals(1, result.get().size, "There should be one substitution")
assertTrue(result.isSuccess, "Variable should unify with compound term")
val subs = result.getOrNull()!!
assertEquals(1, subs.size, "There should be one substitution")
assertTrue(subs.containsKey(variable), "Variable should be in the substitution map")
assertTrue(
equivalent(Structure(Atom("f"), listOf(Atom("a"), Atom("b"))), variable.alias().get()),
equivalent(Structure(Atom("f"), listOf(Atom("a"), Atom("b"))), subs[variable]!!, subs),
"Variable should be substituted with compound term"
)
}
@ -130,9 +136,13 @@ class UnifyTest {
val result = unify(structure1, structure2)
assertTrue(result.isPresent, "Compound term with variable should unify with part")
assertEquals(1, result.get().size, "There should be one substitution")
val equivalence = equivalent(Atom("b"), variable.alias().get())
assertTrue(result.isSuccess, "Compound term with variable should unify with part")
val subs = result.getOrNull()!!
assertEquals(1, subs.size, "There should be one substitution")
assertTrue(subs.containsKey(variable), "Variable should be in the substitution map")
val equivalence = equivalent(Atom("b"), subs[variable]!!, subs)
assertTrue(equivalence, "Variable should be substituted with atom")
}
@ -146,9 +156,13 @@ class UnifyTest {
val result = unify(structure1, structure2)
assertTrue(result.isPresent, "Compound terms with variable arguments should unify")
assertEquals(1, result.get().size, "There should be one substitution")
assertEquals(variable2, variable1.alias().get(), "Variable 1 should alias to variable 2")
assertTrue(result.isSuccess, "Compound terms with variable arguments should unify")
val subs = result.getOrNull()!!
assertEquals(1, subs.size, "There should be one substitution")
assertTrue(subs.containsKey(variable1), "Variable 1 should be in the substitution map")
assertEquals(variable2, subs[variable1], "Variable 1 should alias to variable 2")
}
/**
@ -161,7 +175,7 @@ class UnifyTest {
val result = unify(structure1, structure2)
assertFalse(result.isPresent, "Compound terms with different arity should not unify")
assertFalse(result.isSuccess, "Compound terms with different arity should not unify")
}
/**
@ -177,10 +191,14 @@ class UnifyTest {
val result = unify(structure1, structure2)
assertTrue(result.isPresent, "Nested compound terms with variables should unify")
assertEquals(1, result.get().size, "There should be one substitution")
assertTrue(result.isSuccess, "Nested compound terms with variables should unify")
val subs = result.getOrNull()!!
assertEquals(1, subs.size, "There should be one substitution")
assertTrue(subs.containsKey(variable2), "Variable 2 should be in the substitution map")
assertTrue(
equivalent(Structure(Atom("g"), listOf(Variable("X"))), variable2.alias().get()),
equivalent(Structure(Atom("g"), listOf(Variable("X"))), subs[variable2]!!, subs),
"Variable should be substituted with compound term"
)
}
@ -200,15 +218,22 @@ class UnifyTest {
val result = unify(structure1, structure2)
assertTrue(result.isPresent, "Compound terms with more variables should unify")
assertEquals(2, result.get().size, "There should be two substitutions")
assertTrue(result.isSuccess, "Compound terms with more variables should unify")
val subs = result.getOrNull()!!
assertEquals(2, subs.size, "There should be two substitutions")
assertTrue(subs.containsKey(variable1), "Variable 1 should be in the substitution map")
assertTrue(
equivalent(Atom("a"), variable1.alias().get()),
equivalent(Atom("a"), subs[variable1]!!, subs),
"Variable 1 should be substituted with atom"
)
val equivalence = equivalent(Structure(Atom("g"), listOf(Atom("a"))), variable2.alias().get())
assertTrue(equivalence, "Variable 2 should be substituted with compound term")
assertTrue(subs.containsKey(variable2), "Variable 2 should be in the substitution map")
assertTrue(
equivalent(Structure(Atom("g"), listOf(Atom("a"))), subs[variable2]!!, subs),
"Variable 2 should be substituted with compound term"
)
}
/**
@ -220,11 +245,16 @@ class UnifyTest {
val variable1 = Variable("X")
val structure2 = Structure(Atom("f"), listOf(Variable("X")))
val result = unify(variable1, structure2)
val result = unifyLazy(variable1, structure2, emptyMap()).toList()
assertTrue(result.isPresent, "Recursive unification should succeed")
assertEquals(1, result.get().size, "There should be one substitution")
assertEquals(structure2, variable1.alias().get(), "Variable should be substituted with compound term")
assertEquals(1, result.size, "There should be one result")
assertTrue(result[0].isSuccess, "Recursive unification should succeed")
val subs = result[0].getOrNull()!!
assertEquals(1, subs.size, "There should be one substitution")
assertTrue(subs.containsKey(variable1), "Variable should be in the substitution map")
assertEquals(structure2, subs[variable1], "Variable should be substituted with compound term")
}
/**
@ -238,13 +268,15 @@ class UnifyTest {
val variable2 = Variable("Y")
val atom = Atom("bar")
variable1.bind(atom)
variable2.bind(atom)
val map: Substitutions = mapOf(
variable1 to atom,
variable2 to atom
)
val result = unifyLazy(variable1, variable2, map).toList()
val result = unify(variable1, variable2)
assertTrue(result.isPresent, "Multiple unification should succeed")
assertEquals(0, result.get().size, "No substitutions should be made")
assertEquals(1, result.size, "There should be one substitution")
assertTrue(result[0].isSuccess, "Multiple unification should succeed")
assertEquals(0, result[0].getOrNull()!!.size, "No (additional) substitutions should be made")
}
/**
@ -258,7 +290,7 @@ class UnifyTest {
val result = unify(atom1, structure2)
assertFalse(result.isPresent, "Atom with different arity should not unify")
assertFalse(result.isSuccess, "Atom with different arity should not unify")
}
@Test
@ -268,8 +300,8 @@ class UnifyTest {
val result = unify(int1, int2)
assertTrue(result.isPresent, "Identical integers should unify")
assertEquals(0, result.get().size, "No substitutions should be made")
assertTrue(result.isSuccess, "Identical integers should unify")
assertEquals(0, result.getOrNull()!!.size, "No substitutions should be made")
}
@Test
@ -279,6 +311,17 @@ class UnifyTest {
val result = unify(int1, int2)
assertFalse(result.isPresent, "Different integers should not unify")
assertFalse(result.isSuccess, "Different integers should not unify")
}
@Test
fun `1 + 2 does not unify with 3`() {
val expr1 = Add(Integer(1), Integer(2))
val expr2 = Integer(3)
val result = unify(expr1, expr2)
assertFalse(result.isSuccess, "1 + 2 should not unify with 3")
}
}