|
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 | | -import liquidjava.processor.context.Context; |
6 | | -import liquidjava.rj_language.Predicate; |
7 | 6 | import java.util.List; |
8 | 7 | import java.util.Map; |
9 | 8 |
|
|
17 | 16 | import liquidjava.rj_language.ast.UnaryExpression; |
18 | 17 | import liquidjava.rj_language.ast.Var; |
19 | 18 | import liquidjava.rj_language.opt.derivation_node.BinaryDerivationNode; |
20 | | -import liquidjava.rj_language.opt.derivation_node.DerivationNode; |
21 | 19 | import liquidjava.rj_language.opt.derivation_node.IteDerivationNode; |
22 | 20 | import liquidjava.rj_language.opt.derivation_node.UnaryDerivationNode; |
23 | 21 | import liquidjava.rj_language.opt.derivation_node.ValDerivationNode; |
24 | 22 | import liquidjava.rj_language.opt.derivation_node.VarDerivationNode; |
25 | 23 | import org.junit.jupiter.api.Test; |
26 | | -import spoon.Launcher; |
27 | | -import spoon.reflect.factory.Factory; |
28 | 24 |
|
29 | 25 | /** |
30 | 26 | * Test suite for expression simplification using constant propagation and folding |
31 | 27 | */ |
32 | 28 | class ExpressionSimplifierTest { |
33 | 29 |
|
34 | | - private final Factory factory = new Launcher().getFactory(); |
35 | | - private final Context context = Context.getInstance(); |
36 | | - |
37 | 30 | @Test |
38 | 31 | void testNegation() { |
39 | 32 | // Given: -a && a == 7 |
@@ -891,81 +884,6 @@ void testInternalToInternalNoFurtherResolution() { |
891 | 884 | "#a_3 (lower counter) replaced by #b_7 (higher counter); equality collapses to trivial"); |
892 | 885 | } |
893 | 886 |
|
894 | | - @Test |
895 | | - void testEntailedConjunctIsRemovedButOriginIsPreserved() { |
896 | | - // Given: b >= 100 && b > 0 |
897 | | - // Expected: b >= 100 (b >= 100 implies b > 0) |
898 | | - |
899 | | - addIntVariableToContext("b"); |
900 | | - Expression b = new Var("b"); |
901 | | - Expression bGe100 = new BinaryExpression(b, ">=", new LiteralInt(100)); |
902 | | - Expression bGt0 = new BinaryExpression(b, ">", new LiteralInt(0)); |
903 | | - Expression fullExpression = new BinaryExpression(bGe100, "&&", bGt0); |
904 | | - |
905 | | - ValDerivationNode result = ExpressionSimplifier.simplify(fullExpression); |
906 | | - |
907 | | - assertNotNull(result); |
908 | | - assertEquals("b >= 100", result.getValue().toString(), |
909 | | - "The weaker conjunct should be removed when implied by the stronger one"); |
910 | | - |
911 | | - ValDerivationNode expectedLeft = new ValDerivationNode(bGe100, null); |
912 | | - ValDerivationNode expectedRight = new ValDerivationNode(bGt0, null); |
913 | | - ValDerivationNode expected = new ValDerivationNode(bGe100, |
914 | | - new BinaryDerivationNode(expectedLeft, expectedRight, "&&")); |
915 | | - |
916 | | - assertDerivationEquals(expected, result, "Entailment simplification should preserve conjunction origin"); |
917 | | - } |
918 | | - |
919 | | - @Test |
920 | | - void testStrictComparisonImpliesNonStrictComparison() { |
921 | | - // Given: x > y && x >= y |
922 | | - // Expected: x > y (x > y implies x >= y) |
923 | | - |
924 | | - addIntVariableToContext("x"); |
925 | | - addIntVariableToContext("y"); |
926 | | - Expression x = new Var("x"); |
927 | | - Expression y = new Var("y"); |
928 | | - Expression xGtY = new BinaryExpression(x, ">", y); |
929 | | - Expression xGeY = new BinaryExpression(x, ">=", y); |
930 | | - Expression fullExpression = new BinaryExpression(xGtY, "&&", xGeY); |
931 | | - |
932 | | - ValDerivationNode result = ExpressionSimplifier.simplify(fullExpression); |
933 | | - |
934 | | - assertNotNull(result); |
935 | | - assertEquals("x > y", result.getValue().toString(), |
936 | | - "The stricter comparison should be kept when it implies the weaker one"); |
937 | | - |
938 | | - ValDerivationNode expectedLeft = new ValDerivationNode(xGtY, null); |
939 | | - ValDerivationNode expectedRight = new ValDerivationNode(xGeY, null); |
940 | | - ValDerivationNode expected = new ValDerivationNode(xGtY, |
941 | | - new BinaryDerivationNode(expectedLeft, expectedRight, "&&")); |
942 | | - |
943 | | - assertDerivationEquals(expected, result, "Strict comparison simplification should preserve conjunction origin"); |
944 | | - } |
945 | | - |
946 | | - @Test |
947 | | - void testEquivalentBoundsKeepOneSide() { |
948 | | - // Given: i >= 0 && 0 <= i |
949 | | - // Expected: 0 <= i (both conjuncts express the same condition) |
950 | | - addIntVariableToContext("i"); |
951 | | - Expression i = new Var("i"); |
952 | | - Expression zeroLeI = new BinaryExpression(new LiteralInt(0), "<=", i); |
953 | | - Expression iGeZero = new BinaryExpression(i, ">=", new LiteralInt(0)); |
954 | | - Expression fullExpression = new BinaryExpression(zeroLeI, "&&", iGeZero); |
955 | | - |
956 | | - ValDerivationNode result = ExpressionSimplifier.simplify(fullExpression); |
957 | | - |
958 | | - assertNotNull(result); |
959 | | - assertEquals("0 <= i", result.getValue().toString(), "Equivalent bounds should collapse to a single conjunct"); |
960 | | - |
961 | | - ValDerivationNode expectedLeft = new ValDerivationNode(zeroLeI, null); |
962 | | - ValDerivationNode expectedRight = new ValDerivationNode(iGeZero, null); |
963 | | - ValDerivationNode expected = new ValDerivationNode(zeroLeI, |
964 | | - new BinaryDerivationNode(expectedLeft, expectedRight, "&&")); |
965 | | - |
966 | | - assertDerivationEquals(expected, result, "Equivalent bounds simplification should preserve conjunction origin"); |
967 | | - } |
968 | | - |
969 | 887 | @Test |
970 | 888 | void testIteTrueConditionSimplifiesToThenBranch() { |
971 | 889 | // Given: true ? a : b |
@@ -1102,47 +1020,78 @@ void testTwoArgAliasWithNormalExpression() { |
1102 | 1020 | assertNull(rightNode.getOrigin()); |
1103 | 1021 | } |
1104 | 1022 |
|
1105 | | - /** |
1106 | | - * Helper method to compare two derivation nodes recursively |
1107 | | - */ |
1108 | | - private void assertDerivationEquals(DerivationNode expected, DerivationNode actual, String message) { |
1109 | | - if (expected == null && actual == null) |
1110 | | - return; |
1111 | | - |
1112 | | - assertNotNull(expected); |
1113 | | - assertEquals(expected.getClass(), actual.getClass(), message + ": node types should match"); |
1114 | | - if (expected instanceof ValDerivationNode expectedVal) { |
1115 | | - ValDerivationNode actualVal = (ValDerivationNode) actual; |
1116 | | - assertEquals(expectedVal.getValue().toString(), actualVal.getValue().toString(), |
1117 | | - message + ": values should match"); |
1118 | | - assertDerivationEquals(expectedVal.getOrigin(), actualVal.getOrigin(), message + " > origin"); |
1119 | | - } else if (expected instanceof BinaryDerivationNode expectedBin) { |
1120 | | - BinaryDerivationNode actualBin = (BinaryDerivationNode) actual; |
1121 | | - assertEquals(expectedBin.getOp(), actualBin.getOp(), message + ": operators should match"); |
1122 | | - assertDerivationEquals(expectedBin.getLeft(), actualBin.getLeft(), message + " > left"); |
1123 | | - assertDerivationEquals(expectedBin.getRight(), actualBin.getRight(), message + " > right"); |
1124 | | - } else if (expected instanceof VarDerivationNode expectedVar) { |
1125 | | - VarDerivationNode actualVar = (VarDerivationNode) actual; |
1126 | | - assertEquals(expectedVar.getVar(), actualVar.getVar(), message + ": variables should match"); |
1127 | | - } else if (expected instanceof UnaryDerivationNode expectedUnary) { |
1128 | | - UnaryDerivationNode actualUnary = (UnaryDerivationNode) actual; |
1129 | | - assertEquals(expectedUnary.getOp(), actualUnary.getOp(), message + ": operators should match"); |
1130 | | - assertDerivationEquals(expectedUnary.getOperand(), actualUnary.getOperand(), message + " > operand"); |
1131 | | - } else if (expected instanceof IteDerivationNode expectedIte) { |
1132 | | - IteDerivationNode actualIte = (IteDerivationNode) actual; |
1133 | | - assertDerivationEquals(expectedIte.getCondition(), actualIte.getCondition(), message + " > condition"); |
1134 | | - assertDerivationEquals(expectedIte.getThenBranch(), actualIte.getThenBranch(), message + " > then"); |
1135 | | - assertDerivationEquals(expectedIte.getElseBranch(), actualIte.getElseBranch(), message + " > else"); |
1136 | | - } |
| 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"); |
1137 | 1046 | } |
1138 | 1047 |
|
1139 | | - /** |
1140 | | - * Helper method to add an integer variable to the context Needed for tests that rely on the SMT-based implication |
1141 | | - * checks The simplifier asks Z3 whether one conjunct implies another, so every variable in those expressions must |
1142 | | - * be in the context |
1143 | | - */ |
1144 | | - private void addIntVariableToContext(String name) { |
1145 | | - context.addVarToContext(name, factory.Type().INTEGER_PRIMITIVE, new Predicate(), |
1146 | | - factory.Code().createCodeSnippetStatement("")); |
| 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"); |
1147 | 1096 | } |
1148 | 1097 | } |
0 commit comments