|
1 | 1 | package liquidjava.rj_language.opt; |
2 | 2 |
|
3 | 3 | import static org.junit.jupiter.api.Assertions.*; |
| 4 | +import static liquidjava.utils.TestUtils.*; |
4 | 5 |
|
5 | 6 | import java.util.List; |
6 | 7 | import java.util.Map; |
|
15 | 16 | import liquidjava.rj_language.ast.UnaryExpression; |
16 | 17 | import liquidjava.rj_language.ast.Var; |
17 | 18 | import liquidjava.rj_language.opt.derivation_node.BinaryDerivationNode; |
18 | | -import liquidjava.rj_language.opt.derivation_node.DerivationNode; |
19 | 19 | import liquidjava.rj_language.opt.derivation_node.IteDerivationNode; |
20 | 20 | import liquidjava.rj_language.opt.derivation_node.UnaryDerivationNode; |
21 | 21 | import liquidjava.rj_language.opt.derivation_node.ValDerivationNode; |
@@ -1020,37 +1020,78 @@ void testTwoArgAliasWithNormalExpression() { |
1020 | 1020 | assertNull(rightNode.getOrigin()); |
1021 | 1021 | } |
1022 | 1022 |
|
1023 | | - /** |
1024 | | - * Helper method to compare two derivation nodes recursively |
1025 | | - */ |
1026 | | - private void assertDerivationEquals(DerivationNode expected, DerivationNode actual, String message) { |
1027 | | - if (expected == null && actual == null) |
1028 | | - return; |
1029 | | - |
1030 | | - assertNotNull(expected); |
1031 | | - assertEquals(expected.getClass(), actual.getClass(), message + ": node types should match"); |
1032 | | - if (expected instanceof ValDerivationNode expectedVal) { |
1033 | | - ValDerivationNode actualVal = (ValDerivationNode) actual; |
1034 | | - assertEquals(expectedVal.getValue().toString(), actualVal.getValue().toString(), |
1035 | | - message + ": values should match"); |
1036 | | - assertDerivationEquals(expectedVal.getOrigin(), actualVal.getOrigin(), message + " > origin"); |
1037 | | - } else if (expected instanceof BinaryDerivationNode expectedBin) { |
1038 | | - BinaryDerivationNode actualBin = (BinaryDerivationNode) actual; |
1039 | | - assertEquals(expectedBin.getOp(), actualBin.getOp(), message + ": operators should match"); |
1040 | | - assertDerivationEquals(expectedBin.getLeft(), actualBin.getLeft(), message + " > left"); |
1041 | | - assertDerivationEquals(expectedBin.getRight(), actualBin.getRight(), message + " > right"); |
1042 | | - } else if (expected instanceof VarDerivationNode expectedVar) { |
1043 | | - VarDerivationNode actualVar = (VarDerivationNode) actual; |
1044 | | - assertEquals(expectedVar.getVar(), actualVar.getVar(), message + ": variables should match"); |
1045 | | - } else if (expected instanceof UnaryDerivationNode expectedUnary) { |
1046 | | - UnaryDerivationNode actualUnary = (UnaryDerivationNode) actual; |
1047 | | - assertEquals(expectedUnary.getOp(), actualUnary.getOp(), message + ": operators should match"); |
1048 | | - assertDerivationEquals(expectedUnary.getOperand(), actualUnary.getOperand(), message + " > operand"); |
1049 | | - } else if (expected instanceof IteDerivationNode expectedIte) { |
1050 | | - IteDerivationNode actualIte = (IteDerivationNode) actual; |
1051 | | - assertDerivationEquals(expectedIte.getCondition(), actualIte.getCondition(), message + " > condition"); |
1052 | | - assertDerivationEquals(expectedIte.getThenBranch(), actualIte.getThenBranch(), message + " > then"); |
1053 | | - assertDerivationEquals(expectedIte.getElseBranch(), actualIte.getElseBranch(), message + " > else"); |
1054 | | - } |
| 1023 | + @Test |
| 1024 | + void testEntailedConjunctIsRemovedButOriginIsPreserved() { |
| 1025 | + // Given: b >= 100 && b > 0 |
| 1026 | + // Expected: b >= 100 (b >= 100 implies b > 0) |
| 1027 | + |
| 1028 | + addIntVariableToContext("b"); |
| 1029 | + Expression b = new Var("b"); |
| 1030 | + Expression bGe100 = new BinaryExpression(b, ">=", new LiteralInt(100)); |
| 1031 | + Expression bGt0 = new BinaryExpression(b, ">", new LiteralInt(0)); |
| 1032 | + Expression fullExpression = new BinaryExpression(bGe100, "&&", bGt0); |
| 1033 | + |
| 1034 | + ValDerivationNode result = ExpressionSimplifier.simplify(fullExpression); |
| 1035 | + |
| 1036 | + assertNotNull(result); |
| 1037 | + assertEquals("b >= 100", result.getValue().toString(), |
| 1038 | + "The weaker conjunct should be removed when implied by the stronger one"); |
| 1039 | + |
| 1040 | + ValDerivationNode expectedLeft = new ValDerivationNode(bGe100, null); |
| 1041 | + ValDerivationNode expectedRight = new ValDerivationNode(bGt0, null); |
| 1042 | + ValDerivationNode expected = new ValDerivationNode(bGe100, |
| 1043 | + new BinaryDerivationNode(expectedLeft, expectedRight, "&&")); |
| 1044 | + |
| 1045 | + assertDerivationEquals(expected, result, "Entailment simplification should preserve conjunction origin"); |
| 1046 | + } |
| 1047 | + |
| 1048 | + @Test |
| 1049 | + void testStrictComparisonImpliesNonStrictComparison() { |
| 1050 | + // Given: x > y && x >= y |
| 1051 | + // Expected: x > y (x > y implies x >= y) |
| 1052 | + |
| 1053 | + addIntVariableToContext("x"); |
| 1054 | + addIntVariableToContext("y"); |
| 1055 | + Expression x = new Var("x"); |
| 1056 | + Expression y = new Var("y"); |
| 1057 | + Expression xGtY = new BinaryExpression(x, ">", y); |
| 1058 | + Expression xGeY = new BinaryExpression(x, ">=", y); |
| 1059 | + Expression fullExpression = new BinaryExpression(xGtY, "&&", xGeY); |
| 1060 | + |
| 1061 | + ValDerivationNode result = ExpressionSimplifier.simplify(fullExpression); |
| 1062 | + |
| 1063 | + assertNotNull(result); |
| 1064 | + assertEquals("x > y", result.getValue().toString(), |
| 1065 | + "The stricter comparison should be kept when it implies the weaker one"); |
| 1066 | + |
| 1067 | + ValDerivationNode expectedLeft = new ValDerivationNode(xGtY, null); |
| 1068 | + ValDerivationNode expectedRight = new ValDerivationNode(xGeY, null); |
| 1069 | + ValDerivationNode expected = new ValDerivationNode(xGtY, |
| 1070 | + new BinaryDerivationNode(expectedLeft, expectedRight, "&&")); |
| 1071 | + |
| 1072 | + assertDerivationEquals(expected, result, "Strict comparison simplification should preserve conjunction origin"); |
| 1073 | + } |
| 1074 | + |
| 1075 | + @Test |
| 1076 | + void testEquivalentBoundsKeepOneSide() { |
| 1077 | + // Given: i >= 0 && 0 <= i |
| 1078 | + // Expected: 0 <= i (both conjuncts express the same condition) |
| 1079 | + addIntVariableToContext("i"); |
| 1080 | + Expression i = new Var("i"); |
| 1081 | + Expression zeroLeI = new BinaryExpression(new LiteralInt(0), "<=", i); |
| 1082 | + Expression iGeZero = new BinaryExpression(i, ">=", new LiteralInt(0)); |
| 1083 | + Expression fullExpression = new BinaryExpression(zeroLeI, "&&", iGeZero); |
| 1084 | + |
| 1085 | + ValDerivationNode result = ExpressionSimplifier.simplify(fullExpression); |
| 1086 | + |
| 1087 | + assertNotNull(result); |
| 1088 | + assertEquals("0 <= i", result.getValue().toString(), "Equivalent bounds should collapse to a single conjunct"); |
| 1089 | + |
| 1090 | + ValDerivationNode expectedLeft = new ValDerivationNode(zeroLeI, null); |
| 1091 | + ValDerivationNode expectedRight = new ValDerivationNode(iGeZero, null); |
| 1092 | + ValDerivationNode expected = new ValDerivationNode(zeroLeI, |
| 1093 | + new BinaryDerivationNode(expectedLeft, expectedRight, "&&")); |
| 1094 | + |
| 1095 | + assertDerivationEquals(expected, result, "Equivalent bounds simplification should preserve conjunction origin"); |
1055 | 1096 | } |
1056 | 1097 | } |
0 commit comments