diff --git a/examples/quickcheck/src/main/java/quickcheck/strategies/TrivialQCStrategy.java b/examples/quickcheck/src/main/java/quickcheck/strategies/TrivialQCStrategy.java
new file mode 100644
index 000000000..c97371a64
--- /dev/null
+++ b/examples/quickcheck/src/main/java/quickcheck/strategies/TrivialQCStrategy.java
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2023 Nick Battle.
+ *
+ * Author: Nick Battle
+ *
+ * This file is part of VDMJ.
+ *
+ * VDMJ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * VDMJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with VDMJ. If not, see .
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ ******************************************************************************/
+
+package quickcheck.strategies;
+
+import static com.fujitsu.vdmj.plugins.PluginConsole.errorln;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Stack;
+
+import com.fujitsu.vdmj.in.expressions.INExpression;
+import com.fujitsu.vdmj.in.patterns.INBindingSetter;
+import com.fujitsu.vdmj.pog.ProofObligation;
+import com.fujitsu.vdmj.runtime.Context;
+import com.fujitsu.vdmj.tc.expressions.TCExpression;
+import com.fujitsu.vdmj.values.ValueList;
+
+import quickcheck.QuickCheck;
+import quickcheck.visitors.TrivialQCVisitor;
+
+public class TrivialQCStrategy extends QCStrategy
+{
+ private int errorCount = 0;
+
+ public TrivialQCStrategy(List argv)
+ {
+ for (int i=0; i < argv.size(); i++)
+ {
+ // No plugin arguments yet?
+
+ if (argv.get(i).startsWith("-trivial:"))
+ {
+ errorln("Unknown trivial option: " + argv.get(i));
+ errorln(help());
+ errorCount ++;
+ argv.remove(i);
+ }
+ }
+ }
+
+ @Override
+ public String getName()
+ {
+ return "trivial";
+ }
+
+ @Override
+ public boolean hasErrors()
+ {
+ return errorCount > 0;
+ }
+
+ @Override
+ public boolean init(QuickCheck qc)
+ {
+ return true;
+ }
+
+ @Override
+ public Results getValues(ProofObligation po, INExpression exp, List binds, Context ctxt)
+ {
+ long before = System.currentTimeMillis();
+ boolean proved = false;
+
+ if (po.isCheckable && po.getCheckedExpression() != null)
+ {
+ TrivialQCVisitor visitor = new TrivialQCVisitor();
+ proved = po.getCheckedExpression().apply(visitor, new Stack());
+ }
+
+ return new Results(proved, new HashMap(), System.currentTimeMillis() - before);
+ }
+
+ @Override
+ public String help()
+ {
+ return getName() + " (no options)";
+ }
+
+ @Override
+ public boolean useByDefault()
+ {
+ return true; // Use if no -s given
+ }
+}
diff --git a/examples/quickcheck/src/main/java/quickcheck/visitors/TrivialQCVisitor.java b/examples/quickcheck/src/main/java/quickcheck/visitors/TrivialQCVisitor.java
new file mode 100644
index 000000000..dea6462e9
--- /dev/null
+++ b/examples/quickcheck/src/main/java/quickcheck/visitors/TrivialQCVisitor.java
@@ -0,0 +1,182 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2023 Nick Battle.
+ *
+ * Author: Nick Battle
+ *
+ * This file is part of VDMJ.
+ *
+ * VDMJ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * VDMJ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with VDMJ. If not, see .
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ ******************************************************************************/
+
+package quickcheck.visitors;
+
+import java.util.Stack;
+
+import com.fujitsu.vdmj.ast.lex.LexKeywordToken;
+import com.fujitsu.vdmj.lex.Token;
+import com.fujitsu.vdmj.tc.expressions.TCEqualsExpression;
+import com.fujitsu.vdmj.tc.expressions.TCExpression;
+import com.fujitsu.vdmj.tc.expressions.TCForAllExpression;
+import com.fujitsu.vdmj.tc.expressions.TCIfExpression;
+import com.fujitsu.vdmj.tc.expressions.TCImpliesExpression;
+import com.fujitsu.vdmj.tc.expressions.TCInSetExpression;
+import com.fujitsu.vdmj.tc.expressions.TCIntegerLiteralExpression;
+import com.fujitsu.vdmj.tc.expressions.TCMapEnumExpression;
+import com.fujitsu.vdmj.tc.expressions.TCNotEqualExpression;
+import com.fujitsu.vdmj.tc.expressions.TCNotExpression;
+import com.fujitsu.vdmj.tc.expressions.TCSeqEnumExpression;
+import com.fujitsu.vdmj.tc.expressions.TCSetEnumExpression;
+import com.fujitsu.vdmj.tc.expressions.TCVariableExpression;
+import com.fujitsu.vdmj.tc.expressions.visitors.TCExpressionVisitor;
+import com.fujitsu.vdmj.tc.patterns.TCIdentifierPattern;
+import com.fujitsu.vdmj.tc.patterns.TCMultipleBind;
+import com.fujitsu.vdmj.tc.patterns.TCMultipleSetBind;
+import com.fujitsu.vdmj.tc.patterns.TCPattern;
+
+/**
+ * Search for trivial truths in POs. Each method is passed a stack of expressions
+ * which are known to be true in the current context and they return true if they
+ * are always true, given this stack.
+ *
+ * eg. X => Y will pass "X" as a truth when evaluating Y. If Y is just X, then
+ * that will always be true and so the whole implication is always true, etc.
+ */
+public class TrivialQCVisitor extends TCExpressionVisitor>
+{
+ public TrivialQCVisitor()
+ {
+ }
+
+ @Override
+ public Boolean caseForAllExpression(TCForAllExpression node, Stack truths)
+ {
+ int pushes = 0;
+
+ for (TCMultipleBind bind: node.bindList)
+ {
+ if (bind instanceof TCMultipleSetBind)
+ {
+ TCMultipleSetBind sbind = (TCMultipleSetBind)bind;
+
+ for (TCPattern p: sbind.plist)
+ {
+ if (p instanceof TCIdentifierPattern)
+ {
+ TCIdentifierPattern id = (TCIdentifierPattern)p;
+
+ truths.push(new TCInSetExpression(
+ new TCVariableExpression(id.location, id.name, p.toString()),
+ new LexKeywordToken(Token.INSET, sbind.location),
+ sbind.set));
+
+ pushes++; // eg. "x in set S" is always true here
+ }
+ }
+ }
+ }
+
+ boolean result = node.predicate.apply(this, truths);
+
+ for (int i=0; i truths)
+ {
+ truths.push(node.ifExp);
+ boolean result1 = node.thenExp.apply(this, truths);
+ truths.pop();
+
+ truths.push(new TCNotExpression(node.location, node.ifExp));
+ boolean result2 = node.elseExp.apply(this, truths);
+ truths.pop();
+
+ return result1 && result2; // All paths always true
+ }
+
+ @Override
+ public Boolean caseImpliesExpression(TCImpliesExpression node, Stack truths)
+ {
+ truths.push(node.left);
+
+ if (node.left instanceof TCEqualsExpression)
+ {
+ TCEqualsExpression eq = (TCEqualsExpression)node.left;
+ truths.push(new TCNotEqualExpression(eq.left, new LexKeywordToken(Token.NE, eq.location), eq.right));
+ }
+
+ boolean result = node.right.apply(this, truths);
+
+ if (node.left instanceof TCEqualsExpression)
+ {
+ truths.pop();
+ }
+
+ truths.pop();
+ return result;
+ }
+
+ @Override
+ public Boolean caseNotEqualExpression(TCNotEqualExpression node, Stack truths)
+ {
+ if (node.left instanceof TCSeqEnumExpression &&
+ node.right instanceof TCSeqEnumExpression)
+ {
+ TCSeqEnumExpression sleft = (TCSeqEnumExpression)node.left;
+ TCSeqEnumExpression sright = (TCSeqEnumExpression)node.right;
+
+ return (sleft.members.isEmpty() != sright.members.isEmpty()); // eg. [1,2,3] <> []
+ }
+ else if (node.left instanceof TCSetEnumExpression &&
+ node.right instanceof TCSetEnumExpression)
+ {
+ TCSetEnumExpression sleft = (TCSetEnumExpression)node.left;
+ TCSetEnumExpression sright = (TCSetEnumExpression)node.right;
+
+ return (sleft.members.isEmpty() != sright.members.isEmpty()); // eg. {1,2,3} <> {}
+ }
+ else if (node.left instanceof TCMapEnumExpression &&
+ node.right instanceof TCMapEnumExpression)
+ {
+ TCMapEnumExpression sleft = (TCMapEnumExpression)node.left;
+ TCMapEnumExpression sright = (TCMapEnumExpression)node.right;
+
+ return (sleft.members.isEmpty() != sright.members.isEmpty()); // eg. {1|->2} <> {|->}
+ }
+ else if (node.left instanceof TCIntegerLiteralExpression &&
+ node.right instanceof TCIntegerLiteralExpression)
+ {
+ TCIntegerLiteralExpression vleft = (TCIntegerLiteralExpression)node.left;
+ TCIntegerLiteralExpression vright = (TCIntegerLiteralExpression)node.right;
+
+ return (vleft.value.value != vright.value.value); // eg. 123 <> 456
+ }
+
+ return false;
+ }
+
+ @Override
+ public Boolean caseExpression(TCExpression node, Stack truths)
+ {
+ return truths.contains(node);
+ }
+}
diff --git a/examples/quickcheck/src/main/resources/qc.strategies b/examples/quickcheck/src/main/resources/qc.strategies
index 6d235f224..357a8c546 100644
--- a/examples/quickcheck/src/main/resources/qc.strategies
+++ b/examples/quickcheck/src/main/resources/qc.strategies
@@ -1,4 +1,5 @@
quickcheck.strategies.FixedQCStrategy
quickcheck.strategies.SearchQCStrategy
quickcheck.strategies.RandomQCStrategy
-quickcheck.strategies.FiniteQCStrategy
\ No newline at end of file
+quickcheck.strategies.FiniteQCStrategy
+quickcheck.strategies.TrivialQCStrategy
\ No newline at end of file