diff --git a/key.core/src/main/antlr4/JmlLexer.g4 b/key.core/src/main/antlr4/JmlLexer.g4 index a8594d8bbf4..41e42a4a715 100644 --- a/key.core/src/main/antlr4/JmlLexer.g4 +++ b/key.core/src/main/antlr4/JmlLexer.g4 @@ -266,6 +266,7 @@ MAP_UPDATE: '\\map_update'; //KeY extension, not official JML MAX: '\\max'; E_MEASURED_BY: '\\measured_by' -> type(MEASURED_BY); MIN: '\\min'; +MSET: '\\mset'; //KeY extension, not official JML NEWELEMSFRESH: '\\new_elems_fresh'; //KeY extension, not official JML NEW_OBJECTS: '\\new_objects'; //KeY extension, not official JML NONNULLELEMENTS: '\\nonnullelements'; diff --git a/key.core/src/main/antlr4/JmlParser.g4 b/key.core/src/main/antlr4/JmlParser.g4 index 40f27ec6acc..bd8579449b4 100644 --- a/key.core/src/main/antlr4/JmlParser.g4 +++ b/key.core/src/main/antlr4/JmlParser.g4 @@ -383,7 +383,7 @@ sequence mapExpression: MAP_GET | MAP_OVERRIDE | MAP_UPDATE | MAP_REMOVE | IN_DOMAIN | DOMAIN_IMPLIES_CREATED | MAP_SIZE | MAP_SINGLETON | IS_FINITE; fpOperator: FP_ABS | FP_INFINITE | FP_NAN | FP_NEGATIVE | FP_NICE | FP_NORMAL | FP_POSITIVE | FP_SUBNORMAL; -quantifier: FORALL | EXISTS | MIN | MAX | NUM_OF | PRODUCT | SUM; +quantifier: FORALL | EXISTS | MIN | MAX | NUM_OF | PRODUCT | SUM | MSET; infinite_union_expr: LPAREN UNIONINF (boundvarmodifiers)? quantifiedvardecls SEMI (predicate SEMI)* storeref RPAREN; specquantifiedexpression: LPAREN quantifier (boundvarmodifiers)? quantifiedvardecls SEMI (expression SEMI)? expression RPAREN; oldexpression: (PRE LPAREN expression RPAREN | OLD LPAREN expression (COMMA IDENT)? RPAREN); diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/Recoder2KeYConverter.java b/key.core/src/main/java/de/uka/ilkd/key/java/Recoder2KeYConverter.java index 322cc07a03a..3576c43ed15 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/Recoder2KeYConverter.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/Recoder2KeYConverter.java @@ -23,6 +23,7 @@ import de.uka.ilkd.key.java.expression.literal.*; import de.uka.ilkd.key.java.expression.operator.*; import de.uka.ilkd.key.java.expression.operator.adt.*; +import de.uka.ilkd.key.java.expression.operator.mset.*; import de.uka.ilkd.key.java.recoderext.ImplicitFieldAdder; import de.uka.ilkd.key.java.recoderext.ImplicitIdentifier; import de.uka.ilkd.key.java.reference.*; @@ -723,6 +724,47 @@ public SeqPut convert(de.uka.ilkd.key.java.recoderext.adt.SeqPut e) { return new SeqPut(children); } + + public EmptyMSetLiteral convert(de.uka.ilkd.key.java.recoderext.mset.EmptyMSetLiteral e) { + return EmptyMSetLiteral.INSTANCE; + } + + public MSetUnion convert(de.uka.ilkd.key.java.recoderext.mset.MSetUnion e) { + ExtList children = collectChildren(e); + return new MSetUnion(children); + } + + public MSetSum convert(de.uka.ilkd.key.java.recoderext.mset.MSetSum e) { + ExtList children = collectChildren(e); + return new MSetSum(children); + } + + public MSetDiff convert(de.uka.ilkd.key.java.recoderext.mset.MSetDiff e) { + ExtList children = collectChildren(e); + return new MSetDiff(children); + } + + public MSetIntersect convert(de.uka.ilkd.key.java.recoderext.mset.MSetIntersect e) { + ExtList children = collectChildren(e); + return new MSetIntersect(children); + } + + public MSetSingle convert(de.uka.ilkd.key.java.recoderext.mset.MSetSingle e) { + ExtList children = collectChildren(e); + return new MSetSingle(children); + } + + public MSetMul convert(de.uka.ilkd.key.java.recoderext.mset.MSetMul e) { + + return new MSetMul(collectChildren(e)); + } + + public MSetCard convert(de.uka.ilkd.key.java.recoderext.mset.MSetCard e) { + + return new MSetCard(collectChildren(e)); + } + + public EmptyMapLiteral convert(de.uka.ilkd.key.java.recoderext.adt.EmptyMapLiteral e) { return EmptyMapLiteral.INSTANCE; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/TypeConverter.java b/key.core/src/main/java/de/uka/ilkd/key/java/TypeConverter.java index a88985df91e..da0409fc7b5 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/TypeConverter.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/TypeConverter.java @@ -111,6 +111,10 @@ public SeqLDT getSeqLDT() { return (SeqLDT) getLDT(SeqLDT.NAME); } + public MSetLDT getMSetLDT() { + return (MSetLDT) getLDT(MSetLDT.NAME); + } + public SortLDT getSortLDT() { return (SortLDT) getLDT(SortLDT.NAME); } diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/abstraction/PrimitiveType.java b/key.core/src/main/java/de/uka/ilkd/key/java/abstraction/PrimitiveType.java index 3e4eca762ef..68e24fb1994 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/abstraction/PrimitiveType.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/abstraction/PrimitiveType.java @@ -8,6 +8,7 @@ import de.uka.ilkd.key.java.expression.Literal; import de.uka.ilkd.key.java.expression.literal.*; +import de.uka.ilkd.key.java.expression.literal.EmptyMSetLiteral; import de.uka.ilkd.key.ldt.*; import de.uka.ilkd.key.logic.ProgramElementName; @@ -57,6 +58,8 @@ public final class PrimitiveType implements Type { new PrimitiveType("\\map", EmptyMapLiteral.INSTANCE, MapLDT.NAME); public static final PrimitiveType JAVA_TYPE = new PrimitiveType("\\TYPE", NullLiteral.NULL, SortLDT.NAME); + public static final PrimitiveType JAVA_MSET = + new PrimitiveType("\\mset", EmptyMSetLiteral.INSTANCE, MSetLDT.NAME); public static final PrimitiveType PROGRAM_SV = new PrimitiveType("SV", null, null); diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/expression/literal/EmptyMSetLiteral.java b/key.core/src/main/java/de/uka/ilkd/key/java/expression/literal/EmptyMSetLiteral.java new file mode 100644 index 00000000000..0d48266bfd8 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/java/expression/literal/EmptyMSetLiteral.java @@ -0,0 +1,44 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package de.uka.ilkd.key.java.expression.literal; + +import de.uka.ilkd.key.java.Services; +import de.uka.ilkd.key.java.abstraction.KeYJavaType; +import de.uka.ilkd.key.java.abstraction.PrimitiveType; +import de.uka.ilkd.key.java.expression.Literal; +import de.uka.ilkd.key.java.visitor.Visitor; +import de.uka.ilkd.key.ldt.MSetLDT; + +import org.key_project.logic.Name; + +public class EmptyMSetLiteral extends Literal { + + public static final EmptyMSetLiteral INSTANCE = new EmptyMSetLiteral(); + + private EmptyMSetLiteral() {} + + @Override + public boolean equals(Object o) { + return o == this; + } + + @Override + protected int computeHashCode() { + return System.identityHashCode(this); + } + + public void visit(Visitor v) { + v.performActionOnEmptyMSetLiteral(this); + } + + + public KeYJavaType getKeYJavaType(Services javaServ) { + return javaServ.getJavaInfo().getKeYJavaType(PrimitiveType.JAVA_MSET); + } + + @Override + public Name getLDTName() { + return MSetLDT.NAME; + } +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/expression/literal/EmptySeqLiteral.java b/key.core/src/main/java/de/uka/ilkd/key/java/expression/literal/EmptySeqLiteral.java index 55977ab30a5..cd6ce1068d0 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/expression/literal/EmptySeqLiteral.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/expression/literal/EmptySeqLiteral.java @@ -34,7 +34,6 @@ public void visit(Visitor v) { v.performActionOnEmptySeqLiteral(this); } - public KeYJavaType getKeYJavaType(Services javaServ) { return javaServ.getJavaInfo().getKeYJavaType(PrimitiveType.JAVA_SEQ); } diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/expression/operator/mset/MSetCard.java b/key.core/src/main/java/de/uka/ilkd/key/java/expression/operator/mset/MSetCard.java new file mode 100644 index 00000000000..9bcdc7461e2 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/java/expression/operator/mset/MSetCard.java @@ -0,0 +1,49 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package de.uka.ilkd.key.java.expression.operator.mset; + +import de.uka.ilkd.key.java.Services; +import de.uka.ilkd.key.java.abstraction.KeYJavaType; +import de.uka.ilkd.key.java.abstraction.PrimitiveType; +import de.uka.ilkd.key.java.expression.Operator; +import de.uka.ilkd.key.java.reference.ExecutionContext; +import de.uka.ilkd.key.java.visitor.Visitor; + +import org.key_project.util.ExtList; + +public class MSetCard extends Operator { + + public MSetCard(ExtList children) { + super(children); + } + + + @Override + public int getPrecedence() { + return 0; + } + + @Override + public void visit(Visitor v) { + v.performActionOnMSetCard(this); + } + + + @Override + public int getArity() { + return 1; + } + + + @Override + public KeYJavaType getKeYJavaType(Services javaServ, ExecutionContext ec) { + return javaServ.getJavaInfo().getKeYJavaType(PrimitiveType.JAVA_INT); + } + + + @Override + public int getNotation() { + return Operator.PREFIX; + } +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/expression/operator/mset/MSetDiff.java b/key.core/src/main/java/de/uka/ilkd/key/java/expression/operator/mset/MSetDiff.java new file mode 100644 index 00000000000..b9693e7c5c1 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/java/expression/operator/mset/MSetDiff.java @@ -0,0 +1,37 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package de.uka.ilkd.key.java.expression.operator.mset; + +import de.uka.ilkd.key.java.Expression; +import de.uka.ilkd.key.java.expression.operator.BinaryOperator; +import de.uka.ilkd.key.java.visitor.Visitor; + +import org.key_project.util.ExtList; + +public class MSetDiff extends BinaryOperator { + public MSetDiff(ExtList children) { + super(children); + } + + public MSetDiff(Expression msetA, Expression msetB) { + super(msetA, msetB); + } + + + + @Override + public void visit(Visitor v) { + v.performActionOnMSetDiff(this); + } + + @Override + public int getPrecedence() { + return 0; + } + + @Override + public int getNotation() { + return PREFIX; + } +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/expression/operator/mset/MSetIntersect.java b/key.core/src/main/java/de/uka/ilkd/key/java/expression/operator/mset/MSetIntersect.java new file mode 100644 index 00000000000..36715363a71 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/java/expression/operator/mset/MSetIntersect.java @@ -0,0 +1,39 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package de.uka.ilkd.key.java.expression.operator.mset; + +import de.uka.ilkd.key.java.Expression; +import de.uka.ilkd.key.java.expression.operator.BinaryOperator; +import de.uka.ilkd.key.java.visitor.Visitor; + +import org.key_project.util.ExtList; + +public class MSetIntersect extends BinaryOperator { + + + public MSetIntersect(ExtList children) { + super(children); + } + + public MSetIntersect(Expression msetA, Expression msetB) { + super(msetA, msetB); + } + + + + @Override + public void visit(Visitor v) { + v.performActionOnMSetIntersect(this); + } + + @Override + public int getPrecedence() { + return 0; + } + + @Override + public int getNotation() { + return PREFIX; + } +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/expression/operator/mset/MSetMul.java b/key.core/src/main/java/de/uka/ilkd/key/java/expression/operator/mset/MSetMul.java new file mode 100644 index 00000000000..332add3e3a1 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/java/expression/operator/mset/MSetMul.java @@ -0,0 +1,49 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package de.uka.ilkd.key.java.expression.operator.mset; + +import de.uka.ilkd.key.java.Services; +import de.uka.ilkd.key.java.abstraction.KeYJavaType; +import de.uka.ilkd.key.java.abstraction.PrimitiveType; +import de.uka.ilkd.key.java.expression.Operator; +import de.uka.ilkd.key.java.reference.ExecutionContext; +import de.uka.ilkd.key.java.visitor.Visitor; + +import org.key_project.util.ExtList; + +public class MSetMul extends Operator { + + public MSetMul(ExtList children) { + super(children); + } + + + @Override + public int getPrecedence() { + return 0; + } + + @Override + public void visit(Visitor v) { + v.performActionOnMSetMul(this); + } + + @Override + public int getArity() { + return 1; + } + + + @Override + public KeYJavaType getKeYJavaType(Services javaServ, ExecutionContext ec) { + return javaServ.getJavaInfo().getKeYJavaType(PrimitiveType.JAVA_INT); + } + + + @Override + public int getNotation() { + return Operator.PREFIX; + } + +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/expression/operator/mset/MSetSingle.java b/key.core/src/main/java/de/uka/ilkd/key/java/expression/operator/mset/MSetSingle.java new file mode 100644 index 00000000000..b6f079f0eb0 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/java/expression/operator/mset/MSetSingle.java @@ -0,0 +1,43 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package de.uka.ilkd.key.java.expression.operator.mset; + +import de.uka.ilkd.key.java.Expression; +import de.uka.ilkd.key.java.Services; +import de.uka.ilkd.key.java.abstraction.KeYJavaType; +import de.uka.ilkd.key.java.abstraction.PrimitiveType; +import de.uka.ilkd.key.java.expression.Operator; +import de.uka.ilkd.key.java.reference.ExecutionContext; +import de.uka.ilkd.key.java.visitor.Visitor; + +import org.key_project.util.ExtList; + +public class MSetSingle extends Operator { + + public MSetSingle(ExtList children) { super(children); } + + public MSetSingle(Expression child) { super(child); } + + public int getPrecedence() { + return 0; + } + + + public int getNotation() { + return PREFIX; + } + + + public void visit(Visitor v) { + v.performActionOnMSetSingle(this); + } + + public int getArity() { + return 1; + } + + public KeYJavaType getKeYJavaType(Services javaServ, ExecutionContext ec) { + return javaServ.getJavaInfo().getKeYJavaType(PrimitiveType.JAVA_MSET); + } +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/expression/operator/mset/MSetSum.java b/key.core/src/main/java/de/uka/ilkd/key/java/expression/operator/mset/MSetSum.java new file mode 100644 index 00000000000..8a2bf77d996 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/java/expression/operator/mset/MSetSum.java @@ -0,0 +1,38 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package de.uka.ilkd.key.java.expression.operator.mset; + +import de.uka.ilkd.key.java.Expression; +import de.uka.ilkd.key.java.expression.operator.BinaryOperator; +import de.uka.ilkd.key.java.visitor.Visitor; + +import org.key_project.util.ExtList; + +public class MSetSum extends BinaryOperator { + + public MSetSum(ExtList children) { + super(children); + } + + public MSetSum(Expression msetA, Expression msetB) { + super(msetA, msetB); + } + + + + @Override + public void visit(Visitor v) { + v.performActionOnMSetSum(this); + } + + @Override + public int getPrecedence() { + return 0; + } + + @Override + public int getNotation() { + return PREFIX; + } +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/expression/operator/mset/MSetUnion.java b/key.core/src/main/java/de/uka/ilkd/key/java/expression/operator/mset/MSetUnion.java new file mode 100644 index 00000000000..c57f0edc7f0 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/java/expression/operator/mset/MSetUnion.java @@ -0,0 +1,36 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package de.uka.ilkd.key.java.expression.operator.mset; + +import de.uka.ilkd.key.java.Expression; +import de.uka.ilkd.key.java.expression.operator.BinaryOperator; +import de.uka.ilkd.key.java.visitor.Visitor; + +import org.key_project.util.ExtList; + +public class MSetUnion extends BinaryOperator { + public MSetUnion(ExtList children) { + super(children); + } + + public MSetUnion(Expression msetA, Expression msetB) { + super(msetA, msetB); + } + + + @Override + public void visit(Visitor v) { + v.performActionOnMSetUnion(this); + } + + @Override + public int getPrecedence() { + return 0; + } + + @Override + public int getNotation() { + return PREFIX; + } +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/RecoderModelTransformer.java b/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/RecoderModelTransformer.java index f87a70c23df..8d52db54945 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/RecoderModelTransformer.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/RecoderModelTransformer.java @@ -9,6 +9,7 @@ import de.uka.ilkd.key.java.recoderext.adt.EmptySeqLiteral; import de.uka.ilkd.key.java.recoderext.adt.EmptySetLiteral; import de.uka.ilkd.key.java.recoderext.expression.literal.RealLiteral; +import de.uka.ilkd.key.java.recoderext.mset.EmptyMSetLiteral; import de.uka.ilkd.key.util.Debug; import recoder.CrossReferenceServiceConfiguration; @@ -81,6 +82,7 @@ public Expression getDefaultValue(Type type) { case "\\TYPE" -> new DLEmbeddedExpression("any::ssort", Collections.emptyList()); case "\\free" -> new DLEmbeddedExpression("atom", Collections.emptyList()); case "\\map" -> EmptyMapLiteral.INSTANCE; + case "\\mset" -> EmptyMSetLiteral.INSTANCE; default -> { if (type.getName().startsWith("\\dl_")) { // The default value of a type is resolved later, then we know the Sort of the diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/mset/EmptyMSetLiteral.java b/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/mset/EmptyMSetLiteral.java new file mode 100644 index 00000000000..8cd81549951 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/mset/EmptyMSetLiteral.java @@ -0,0 +1,37 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package de.uka.ilkd.key.java.recoderext.mset; + +import recoder.java.Expression; +import recoder.java.SourceVisitor; +import recoder.java.expression.Literal; + +public class EmptyMSetLiteral extends Literal { + + /** + * + */ + private static final long serialVersionUID = 8572271426600775146L; + public static final EmptyMSetLiteral INSTANCE = new EmptyMSetLiteral(); + + @Override + public Expression deepClone() { + return this; + } + + @Override + public void accept(SourceVisitor v) { + } + + + @Override + public Object getEquivalentJavaType() { + return null; + } + + @Override + public String toSource() { + return "\\mset_empty"; + } +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/mset/MSetCard.java b/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/mset/MSetCard.java new file mode 100644 index 00000000000..347bda1f492 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/mset/MSetCard.java @@ -0,0 +1,61 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package de.uka.ilkd.key.java.recoderext.mset; + +import recoder.java.Expression; +import recoder.java.SourceVisitor; +import recoder.java.expression.Operator; + +public class MSetCard extends Operator { + + + private static final long serialVersionUID = 0; + + + public MSetCard(Expression msetEl) { + super(msetEl); + makeParentRoleValid(); + } + + + protected MSetCard(de.uka.ilkd.key.java.recoderext.mset.MSetCard proto) { + super(proto); + makeParentRoleValid(); + } + + + @Override + public de.uka.ilkd.key.java.recoderext.mset.MSetCard deepClone() { + return new de.uka.ilkd.key.java.recoderext.mset.MSetCard(this); + } + + + @Override + public int getArity() { + return 1; + } + + + @Override + public int getPrecedence() { + return 0; + } + + + @Override + public int getNotation() { + return POSTFIX; + } + + + @Override + public void accept(SourceVisitor v) { + + } + + public String toSource() { + return children.get(0).toSource() + ".length"; + } + +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/mset/MSetDiff.java b/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/mset/MSetDiff.java new file mode 100644 index 00000000000..956b9af9f73 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/mset/MSetDiff.java @@ -0,0 +1,48 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package de.uka.ilkd.key.java.recoderext.mset; + +import de.uka.ilkd.key.java.recoderext.adt.ADTPrefixConstruct; + +import recoder.java.Expression; + +public class MSetDiff extends ADTPrefixConstruct { + + + private static final long serialVersionUID = -6950638934821692317L; + + public MSetDiff(Expression lhs, Expression rhs) { + super(lhs, rhs); + makeParentRoleValid(); + } + + + protected MSetDiff(MSetDiff proto) { + super(proto); + makeParentRoleValid(); + } + + + + @Override + public Expression deepClone() { + return new MSetDiff(this); + } + + @Override + public int getArity() { + return 2; + } + + @Override + public int getNotation() { + return PREFIX; + } + + @Override + public String toSource() { + return "\\mset_diff(" + children.get(0).toSource() + "," + children.get(1).toSource() + + ")"; + } +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/mset/MSetIntersect.java b/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/mset/MSetIntersect.java new file mode 100644 index 00000000000..b39dfded934 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/mset/MSetIntersect.java @@ -0,0 +1,46 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package de.uka.ilkd.key.java.recoderext.mset; + +import de.uka.ilkd.key.java.recoderext.adt.ADTPrefixConstruct; + +import recoder.java.Expression; + +public class MSetIntersect extends ADTPrefixConstruct { + + /** + * + */ + private static final long serialVersionUID = 8877658515734186914L; + + + public MSetIntersect(Expression lhs, Expression rhs) { + super(lhs, rhs); + makeParentRoleValid(); + } + + + protected MSetIntersect(de.uka.ilkd.key.java.recoderext.mset.MSetIntersect proto) { + super(proto); + makeParentRoleValid(); + } + + + @Override + public de.uka.ilkd.key.java.recoderext.mset.MSetIntersect deepClone() { + return new de.uka.ilkd.key.java.recoderext.mset.MSetIntersect(this); + } + + + @Override + public int getArity() { + return 2; + } + + + @Override + public int getNotation() { + return PREFIX; + } +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/mset/MSetMul.java b/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/mset/MSetMul.java new file mode 100644 index 00000000000..5abf412dc08 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/mset/MSetMul.java @@ -0,0 +1,52 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package de.uka.ilkd.key.java.recoderext.mset; + +import de.uka.ilkd.key.java.recoderext.adt.ADTPrefixConstruct; + +import recoder.java.Expression; + +public class MSetMul extends ADTPrefixConstruct { + + /** + * + */ + private static final long serialVersionUID = -521447886220796576L; + + + public MSetMul(Expression mset, Expression msetEl) { + super(mset, msetEl); + makeParentRoleValid(); + } + + + protected MSetMul(de.uka.ilkd.key.java.recoderext.mset.MSetMul proto) { + super(proto); + makeParentRoleValid(); + } + + + @Override + public de.uka.ilkd.key.java.recoderext.mset.MSetMul deepClone() { + + return new de.uka.ilkd.key.java.recoderext.mset.MSetMul(this); + } + + + @Override + public int getArity() { + return 2; + } + + + @Override + public int getNotation() { + return POSTFIX; + } + + @Override + public String toSource() { + return children.get(0).toSource() + "[" + children.get(1).toSource() + "]"; + } +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/mset/MSetSingle.java b/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/mset/MSetSingle.java new file mode 100644 index 00000000000..bfd7495336b --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/mset/MSetSingle.java @@ -0,0 +1,51 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package de.uka.ilkd.key.java.recoderext.mset; + +import de.uka.ilkd.key.java.recoderext.adt.ADTPrefixConstruct; +import de.uka.ilkd.key.java.recoderext.adt.SeqSingleton; + +import recoder.java.Expression; + +public class MSetSingle extends ADTPrefixConstruct { + + /** + * + */ + private static final long serialVersionUID = 970102046205262465L; + + public MSetSingle(Expression lhs) { + super(lhs); + makeParentRoleValid(); + } + + + protected MSetSingle(SeqSingleton proto) { + super(proto); + makeParentRoleValid(); + } + + + @Override + public MSetSingle deepClone() { + return new MSetSingle(this); + } + + + @Override + public int getArity() { + return 1; + } + + + @Override + public int getNotation() { + return PREFIX; + } + + @Override + public String toSource() { + return "\\mset_single(" + children.get(0) + ")"; + } +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/mset/MSetSum.java b/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/mset/MSetSum.java new file mode 100644 index 00000000000..b0fbdb8a1cc --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/mset/MSetSum.java @@ -0,0 +1,47 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package de.uka.ilkd.key.java.recoderext.mset; + +import de.uka.ilkd.key.java.recoderext.adt.ADTPrefixConstruct; + +import recoder.java.Expression; + +public class MSetSum extends ADTPrefixConstruct { + + private static final long serialVersionUID = -6950638934821692317L; + + public MSetSum(Expression lhs, Expression rhs) { + super(lhs, rhs); + makeParentRoleValid(); + } + + + protected MSetSum(MSetSum proto) { + super(proto); + makeParentRoleValid(); + } + + + + @Override + public Expression deepClone() { + return new MSetSum(this); + } + + @Override + public int getArity() { + return 2; + } + + @Override + public int getNotation() { + return PREFIX; + } + + @Override + public String toSource() { + return "\\mset_sum(" + children.get(0).toSource() + "," + children.get(1).toSource() + + ")"; + } +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/mset/MSetUnion.java b/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/mset/MSetUnion.java new file mode 100644 index 00000000000..89181843f4f --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/java/recoderext/mset/MSetUnion.java @@ -0,0 +1,47 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package de.uka.ilkd.key.java.recoderext.mset; + +import de.uka.ilkd.key.java.recoderext.adt.ADTPrefixConstruct; + +import recoder.java.Expression; + +public class MSetUnion extends ADTPrefixConstruct { + + private static final long serialVersionUID = -6850638934821692317L; + + public MSetUnion(Expression lhs, Expression rhs) { + super(lhs, rhs); + makeParentRoleValid(); + } + + + protected MSetUnion(MSetUnion proto) { + super(proto); + makeParentRoleValid(); + } + + + + @Override + public Expression deepClone() { + return new MSetUnion(this); + } + + @Override + public int getArity() { + return 2; + } + + @Override + public int getNotation() { + return PREFIX; + } + + @Override + public String toSource() { + return "\\mset_union(" + children.get(0).toSource() + "," + children.get(1).toSource() + + ")"; + } +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/visitor/CreatingASTVisitor.java b/key.core/src/main/java/de/uka/ilkd/key/java/visitor/CreatingASTVisitor.java index 31bb841ed14..22153092166 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/visitor/CreatingASTVisitor.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/visitor/CreatingASTVisitor.java @@ -16,6 +16,7 @@ import de.uka.ilkd.key.java.expression.PassiveExpression; import de.uka.ilkd.key.java.expression.operator.*; import de.uka.ilkd.key.java.expression.operator.adt.*; +import de.uka.ilkd.key.java.expression.operator.mset.*; import de.uka.ilkd.key.java.reference.*; import de.uka.ilkd.key.java.statement.*; import de.uka.ilkd.key.logic.op.IProgramVariable; @@ -1473,6 +1474,92 @@ ProgramElement createNewElement(ExtList changeList) { def.doAction(x); } + public void performActionOnMSetUnion(MSetUnion x) { + + DefaultAction def = new DefaultAction(x) { + @Override + ProgramElement createNewElement(ExtList changeList) { + + return new MSetUnion(changeList); + } + + }; + + def.doAction(x); + + } + + @Override + public void performActionOnMSetIntersect(MSetIntersect x) { + DefaultAction def = new DefaultAction(x) { + @Override + ProgramElement createNewElement(ExtList changeList) { + return new MSetIntersect(changeList); + } + }; + def.doAction(x); + } + + @Override + public void performActionOnMSetSum(MSetSum x) { + DefaultAction def = new DefaultAction(x) { + @Override + ProgramElement createNewElement(ExtList changeList) { + return new MSetSum(changeList); + } + }; + def.doAction(x); + + } + + @Override + public void performActionOnMSetDiff(MSetDiff x) { + DefaultAction def = new DefaultAction(x) { + @Override + ProgramElement createNewElement(ExtList changeList) { + return new MSetDiff(changeList); + } + }; + def.doAction(x); + + + } + + @Override + public void performActionOnMSetSingle(MSetSingle x) { + DefaultAction def = new DefaultAction(x) { + @Override + ProgramElement createNewElement(ExtList changeList) { + return new MSetSingle(changeList); + } + }; + def.doAction(x); + } + + @Override + public void performActionOnMSetCard(MSetCard x) { + DefaultAction def = new DefaultAction(x) { + @Override + ProgramElement createNewElement(ExtList changeList) { + return new MSetCard(changeList); + } + }; + def.doAction(x); + } + + @Override + public void performActionOnMSetMul(MSetMul x) { + + DefaultAction def = new DefaultAction(x) { + @Override + ProgramElement createNewElement(ExtList changeList) { + return new MSetMul(changeList); + } + }; + def.doAction(x); + } + + @Override public void performActionOnDLEmbeddedExpression(final DLEmbeddedExpression x) { DefaultAction def = new DefaultAction(x) { diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/visitor/JavaASTVisitor.java b/key.core/src/main/java/de/uka/ilkd/key/java/visitor/JavaASTVisitor.java index 3486003abe5..dd2ef6fa83a 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/visitor/JavaASTVisitor.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/visitor/JavaASTVisitor.java @@ -9,8 +9,10 @@ import de.uka.ilkd.key.java.expression.ParenthesizedExpression; import de.uka.ilkd.key.java.expression.PassiveExpression; import de.uka.ilkd.key.java.expression.literal.*; +import de.uka.ilkd.key.java.expression.literal.EmptyMSetLiteral; import de.uka.ilkd.key.java.expression.operator.*; import de.uka.ilkd.key.java.expression.operator.adt.*; +import de.uka.ilkd.key.java.expression.operator.mset.*; import de.uka.ilkd.key.java.reference.*; import de.uka.ilkd.key.java.statement.*; import de.uka.ilkd.key.logic.ProgramElementName; @@ -181,6 +183,8 @@ public void performActionOnSetUnion(SetUnion x) { doDefaultAction(x); } + + @Override public void performActionOnIntersect(Intersect x) { doDefaultAction(x); @@ -231,6 +235,30 @@ public void performActionOnSeqPut(SeqPut x) { doDefaultAction(x); } + @Override + public void performActionOnEmptyMSetLiteral(EmptyMSetLiteral x) { doDefaultAction(x); } + + @Override + public void performActionOnMSetUnion(MSetUnion x) { doDefaultAction(x); } + + @Override + public void performActionOnMSetIntersect(MSetIntersect x) { doDefaultAction(x); } + + @Override + public void performActionOnMSetSum(MSetSum x) { doDefaultAction(x); } + + @Override + public void performActionOnMSetDiff(MSetDiff x) { doDefaultAction(x); } + + @Override + public void performActionOnMSetSingle(MSetSingle x) { doDefaultAction(x); } + + @Override + public void performActionOnMSetMul(MSetMul x) { doDefaultAction(x); } + + @Override + public void performActionOnMSetCard(MSetCard x) { doDefaultAction(x); } + @Override public void performActionOnDLEmbeddedExpression(DLEmbeddedExpression x) { doDefaultAction(x); diff --git a/key.core/src/main/java/de/uka/ilkd/key/java/visitor/Visitor.java b/key.core/src/main/java/de/uka/ilkd/key/java/visitor/Visitor.java index d06e16af955..15787e42e36 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/java/visitor/Visitor.java +++ b/key.core/src/main/java/de/uka/ilkd/key/java/visitor/Visitor.java @@ -9,8 +9,10 @@ import de.uka.ilkd.key.java.expression.ParenthesizedExpression; import de.uka.ilkd.key.java.expression.PassiveExpression; import de.uka.ilkd.key.java.expression.literal.*; +import de.uka.ilkd.key.java.expression.literal.EmptyMSetLiteral; import de.uka.ilkd.key.java.expression.operator.*; import de.uka.ilkd.key.java.expression.operator.adt.*; +import de.uka.ilkd.key.java.expression.operator.mset.*; import de.uka.ilkd.key.java.reference.*; import de.uka.ilkd.key.java.statement.*; import de.uka.ilkd.key.java.statement.SetStatement; @@ -60,6 +62,8 @@ public interface Visitor { void performActionOnSetUnion(SetUnion x); + + void performActionOnIntersect(Intersect x); void performActionOnSetMinus(SetMinus x); @@ -82,6 +86,22 @@ public interface Visitor { void performActionOnSeqPut(SeqPut seqPut); + void performActionOnMSetUnion(MSetUnion x); + + void performActionOnMSetIntersect(MSetIntersect x); + + void performActionOnMSetSum(MSetSum x); + + void performActionOnMSetDiff(MSetDiff x); + + void performActionOnEmptyMSetLiteral(EmptyMSetLiteral x); + + void performActionOnMSetSingle(MSetSingle x); + + void performActionOnMSetMul(MSetMul x); + + void performActionOnMSetCard(MSetCard x); + void performActionOnDLEmbeddedExpression(DLEmbeddedExpression x); void performActionOnStringLiteral(StringLiteral x); diff --git a/key.core/src/main/java/de/uka/ilkd/key/ldt/LDT.java b/key.core/src/main/java/de/uka/ilkd/key/ldt/LDT.java index f6c8e741ca6..db365e53588 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/ldt/LDT.java +++ b/key.core/src/main/java/de/uka/ilkd/key/ldt/LDT.java @@ -142,6 +142,7 @@ public static Map getNewLDTInstances(Services s) { ret.put(DoubleLDT.NAME, new DoubleLDT(s)); ret.put(RealLDT.NAME, new RealLDT(s)); ret.put(CharListLDT.NAME, new CharListLDT(s)); + ret.put(MSetLDT.NAME, new MSetLDT(s)); return ret; } diff --git a/key.core/src/main/java/de/uka/ilkd/key/ldt/MSetLDT.java b/key.core/src/main/java/de/uka/ilkd/key/ldt/MSetLDT.java new file mode 100644 index 00000000000..e4d8c295878 --- /dev/null +++ b/key.core/src/main/java/de/uka/ilkd/key/ldt/MSetLDT.java @@ -0,0 +1,149 @@ +/* This file is part of KeY - https://key-project.org + * KeY is licensed under the GNU General Public License Version 2 + * SPDX-License-Identifier: GPL-2.0-only */ +package de.uka.ilkd.key.ldt; + +import de.uka.ilkd.key.java.Expression; +import de.uka.ilkd.key.java.Services; +import de.uka.ilkd.key.java.abstraction.Type; +import de.uka.ilkd.key.java.expression.Literal; +import de.uka.ilkd.key.java.expression.Operator; +import de.uka.ilkd.key.java.expression.literal.EmptyMSetLiteral; +import de.uka.ilkd.key.java.expression.literal.EmptySeqLiteral; +import de.uka.ilkd.key.java.expression.operator.adt.*; +import de.uka.ilkd.key.java.expression.operator.mset.*; +import de.uka.ilkd.key.java.reference.ExecutionContext; +import de.uka.ilkd.key.logic.Term; +import de.uka.ilkd.key.logic.TermServices; +import de.uka.ilkd.key.logic.op.JFunction; + +import org.key_project.logic.Name; +import org.key_project.util.ExtList; + +public class MSetLDT extends LDT { + + public static final Name NAME = new Name("Mset"); + + private final JFunction msetRange; + private final JFunction msetMul; + private final JFunction msetEmpty; + private final JFunction msetSingle; + private final JFunction msetUnion; + private final JFunction msetIntersec; + private final JFunction msetSum; + private final JFunction msetDiff; + private final JFunction msetCard; + + public MSetLDT(TermServices services) { + super(NAME, services); + msetMul = addFunction(services, "msetMul"); + msetEmpty = addFunction(services, "msetEmpty"); + msetSingle = addFunction(services, "msetSingle"); + msetUnion = addFunction(services, "msetUnion"); + msetIntersec = addFunction(services, "msetIntersec"); + msetSum = addFunction(services, "msetSum"); + msetDiff = addFunction(services, "msetDiff"); + msetCard = addFunction(services, "msetCard"); + msetRange = addFunction(services, "msetRange"); + } + + + public JFunction getMsetRange() { return msetRange; } + + public JFunction getMsetMul() { + return msetMul; + } + + public JFunction getMsetEmpty() { + return msetEmpty; + } + + public JFunction getMsetSingle() { + return msetSingle; + } + + public JFunction getMsetUnion() { + return msetUnion; + } + + public JFunction getMsetIntersec() { + return msetIntersec; + } + + public JFunction getMsetAdd() { + return msetSum; + } + + public JFunction getMsetRemove() { + return msetDiff; + } + + @Override + public boolean isResponsible(Operator op, Term[] subs, Services services, ExecutionContext ec) { + return op instanceof MSetCard || op instanceof MSetMul || op instanceof MSetSingle + || op instanceof MSetIntersect || + op instanceof MSetDiff || op instanceof MSetUnion || op instanceof MSetSum; + } + + @Override + public boolean isResponsible(Operator op, Term left, Term right, Services services, + ExecutionContext ec) { + return op instanceof MSetUnion || op instanceof MSetIntersect || op instanceof MSetSum + || op instanceof MSetDiff; + } + + @Override + public boolean isResponsible(Operator op, Term sub, TermServices services, + ExecutionContext ec) { + return op instanceof MSetSingle || op instanceof MSetMul || op instanceof MSetCard; + } + + @Override + public Term translateLiteral(Literal lit, Services services) { + assert lit instanceof EmptySeqLiteral; + return services.getTermBuilder().func(msetEmpty); + } + + @Override + public JFunction getFunctionFor(Operator op, Services services, ExecutionContext ec) { + if (op instanceof MSetSingle) { + return msetSingle; + } else if (op instanceof MSetCard) { + return msetCard; + } else if (op instanceof MSetUnion) { + return msetUnion; + } else if (op instanceof MSetDiff) { + return msetDiff; + } else if (op instanceof MSetSum) { + return msetSum; + } else if (op instanceof MSetIntersect) { + return msetIntersec; + } else if (op instanceof MSetMul) { + return msetMul; + } + + assert false; + return null; + + } + + @Override + public boolean hasLiteralFunction(JFunction f) { + return f.equals(msetEmpty); + } + + @Override + public Expression translateTerm(Term t, ExtList children, Services services) { + if (t.op().equals(msetEmpty)) { + return EmptyMSetLiteral.INSTANCE; + } + assert false; + return null; + } + + @Override + public Type getType(Term t) { + assert false; + return null; + } +} diff --git a/key.core/src/main/java/de/uka/ilkd/key/logic/LexPathOrdering.java b/key.core/src/main/java/de/uka/ilkd/key/logic/LexPathOrdering.java index 24fa9417ee7..cb9cd819723 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/logic/LexPathOrdering.java +++ b/key.core/src/main/java/de/uka/ilkd/key/logic/LexPathOrdering.java @@ -420,6 +420,13 @@ protected Integer getWeight(Operator p_op) { return 7; } + + if (opStr.equals("msetSingle")) { + return 6; + } + if (opStr.equals("msetSum")) { + return 7; + } return null; } } diff --git a/key.core/src/main/java/de/uka/ilkd/key/logic/TermBuilder.java b/key.core/src/main/java/de/uka/ilkd/key/logic/TermBuilder.java index c6a697b2181..0eed2bbea67 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/logic/TermBuilder.java +++ b/key.core/src/main/java/de/uka/ilkd/key/logic/TermBuilder.java @@ -2040,6 +2040,10 @@ public Term seqEmpty() { return func(services.getTypeConverter().getSeqLDT().getSeqEmpty()); } + public Term msetEmpty() { + return func(services.getTypeConverter().getMSetLDT().getMsetEmpty()); + } + public Term seqSingleton(Term x) { return func(services.getTypeConverter().getSeqLDT().getSeqSingleton(), x); } @@ -2164,6 +2168,22 @@ public Term seqDef(QuantifiableVariable qv, Term a, Term b, Term t) { new ImmutableArray<>(qv)); } + public Term mset(QuantifiableVariable qv, Term a, Term b, Term t) { + return func(services.getTypeConverter().getMSetLDT().getMsetRange(), new Term[] { a, b, t }, + new ImmutableArray<>(qv)); + } + + public Term mset(ImmutableList qvs, Term range, Term t) { + final JFunction mset = services.getNamespaces().functions().lookup("mset"); + final Iterator it = qvs.iterator(); + Term res = func(mset, new Term[] { convertToBoolean(range), t }, + new ImmutableArray<>(it.next())); + while (it.hasNext()) { + res = func(mset, new Term[] { TRUE(), res }, new ImmutableArray<>(it.next())); + } + return res; + } + public Term values() { return func(services.getTypeConverter().getSeqLDT().getValues()); } diff --git a/key.core/src/main/java/de/uka/ilkd/key/nparser/builder/ExpressionBuilder.java b/key.core/src/main/java/de/uka/ilkd/key/nparser/builder/ExpressionBuilder.java index 1d638f33534..7408f796c38 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/nparser/builder/ExpressionBuilder.java +++ b/key.core/src/main/java/de/uka/ilkd/key/nparser/builder/ExpressionBuilder.java @@ -14,10 +14,7 @@ import de.uka.ilkd.key.java.*; import de.uka.ilkd.key.java.abstraction.KeYJavaType; import de.uka.ilkd.key.java.expression.literal.StringLiteral; -import de.uka.ilkd.key.ldt.IntegerLDT; -import de.uka.ilkd.key.ldt.JavaDLTheory; -import de.uka.ilkd.key.ldt.LDT; -import de.uka.ilkd.key.ldt.SeqLDT; +import de.uka.ilkd.key.ldt.*; import de.uka.ilkd.key.logic.*; import de.uka.ilkd.key.logic.label.TermLabel; import de.uka.ilkd.key.logic.op.*; @@ -1696,6 +1693,9 @@ private boolean isSequenceTerm(Term reference) { return reference != null && reference.sort().name().equals(SeqLDT.NAME); } + private boolean inMultisetTerm(Term reference){ + return reference != null && reference.sort().name().equals(MSetLDT.NAME); + } private boolean isIntTerm(Term reference) { return reference.sort().name().equals(IntegerLDT.NAME); } diff --git a/key.core/src/main/java/de/uka/ilkd/key/pp/PrettyPrinter.java b/key.core/src/main/java/de/uka/ilkd/key/pp/PrettyPrinter.java index aa4d5c176c9..eee068246c2 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/pp/PrettyPrinter.java +++ b/key.core/src/main/java/de/uka/ilkd/key/pp/PrettyPrinter.java @@ -17,6 +17,7 @@ import de.uka.ilkd.key.java.expression.operator.*; import de.uka.ilkd.key.java.expression.operator.adt.SeqGet; import de.uka.ilkd.key.java.expression.operator.adt.SeqLength; +import de.uka.ilkd.key.java.expression.operator.mset.*; import de.uka.ilkd.key.java.reference.*; import de.uka.ilkd.key.java.statement.*; import de.uka.ilkd.key.java.visitor.Visitor; @@ -405,6 +406,53 @@ public void performActionOnSeqPut( printDLFunctionOperator("\\seq_upd", x); } + @Override + public void performActionOnEmptyMSetLiteral(EmptyMSetLiteral x) { + layouter.print("\\mset_empty"); + } + + + + @Override + public void performActionOnMSetUnion(MSetUnion x) { + printDLFunctionOperator("\\mset_union", x); + } + + @Override + public void performActionOnMSetIntersect(MSetIntersect x) { + printDLFunctionOperator("\\mset_intersection", x); + } + + @Override + public void performActionOnMSetSum(MSetSum x) { + printDLFunctionOperator("\\mset_sum", x); + } + + @Override + public void performActionOnMSetDiff(MSetDiff x) { + printDLFunctionOperator("\\mset_diff", x); + } + + @Override + public void performActionOnMSetSingle(MSetSingle x) { + printDLFunctionOperator("\\mset_single", x); + } + + @Override + public void performActionOnMSetMul(MSetMul x) { + x.getChildAt(0).visit(this); + layouter.print("["); + x.getChildAt(1).visit(this); + layouter.print("]"); + } + + @Override + public void performActionOnMSetCard(MSetCard x) { + x.getChildAt(0).visit(this); + layouter.print(".length"); + } + + @Override public void performActionOnDLEmbeddedExpression(DLEmbeddedExpression x) { layouter.print("\\dl_" + x.getFunctionSymbol().name()); diff --git a/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/JmlTermFactory.java b/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/JmlTermFactory.java index 968913112a5..117e95b7989 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/JmlTermFactory.java +++ b/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/JmlTermFactory.java @@ -259,6 +259,17 @@ public SLExpression quantifiedMax(Term _guard, Term body, KeYJavaType declsType, return numeralQuantifier(javaType, nullable, qvs, t1, t2, resultType, unbounded, bounded); } + public @NonNull SLExpression quantifiedMset(KeYJavaType javaType, boolean nullable, + Iterable qvs, @Nullable Term t1, Term t2, KeYJavaType resultType) { + BoundedNumericalQuantifier bounded = tb::mset; + UnboundedNumericalQuantifier unbounded = (declsType, n, vars, range, body) -> { + final Term tr = typerestrict(declsType, n, vars); + return tb.mset(vars, tb.andSC(tr, range), body); + }; + return nonNumeralQuantifier(javaType, nullable, qvs, t1, t2, resultType, unbounded, + bounded); + } + public SLExpression forall(Term preTerm, Term bodyTerm, KeYJavaType declsType, ImmutableList declVars, boolean nullable, KeYJavaType resultType) { BiFunction quantify = tb::all; @@ -380,6 +391,33 @@ public Term upperBound(Term a, LogicVariable lv) { return buildBigintTruncationExpression(resultType, t); } + private @NonNull SLExpression nonNumeralQuantifier(KeYJavaType declsType, boolean nullable, + Iterable qvs, Term t1, Term t2, @Nullable KeYJavaType resultType, + UnboundedNumericalQuantifier unbounded, BoundedNumericalQuantifier bounded) { + Iterator it = qvs.iterator(); + LogicVariable lv = it.next(); + Term t; + if (it.hasNext() || !isBoundedNumerical(t1, lv)) { + // not interval range, create unbounded comprehension term + ImmutableList _qvs = + ImmutableSLList.nil().prepend(lv); + while (it.hasNext()) { + _qvs = _qvs.prepend(it.next()); + } + t = unbounded.apply(declsType, nullable, _qvs, t1, t2); + } else { + t = bounded.apply(lv, lowerBound(t1, lv), upperBound(t1, lv), t2); + } + + if (resultType == null) { + resultType = services.getTypeConverter().getKeYJavaType(t2); + } + + // cast to specific JML type (fixes bug #1347) + return new SLExpression(t, resultType); + } + + public ImmutableList infflowspeclist(ImmutableList result) { return result; } @@ -747,6 +785,19 @@ private SLExpression buildBigintTruncationExpression(KeYJavaType resultType, Ter } } + private SLExpression buildMSetTruncationExpression(KeYJavaType resultType, Term term) { + assert term.sort() == services.getTypeConverter().getIntegerLDT().targetSort(); + + SpecMathMode mode = this.overloadedFunctionHandler.getSpecMathMode(); + if (mode == SpecMathMode.JAVA) { + return buildIntCastExpression(resultType, term); + } else { + KeYJavaType mset = services.getJavaInfo().getKeYJavaType(PrimitiveType.JAVA_MSET); + return new SLExpression(term, mset); + } + } + + private SLExpression buildIntCastExpression(KeYJavaType resultType, Term term) { IntegerLDT integerLDT = services.getTypeConverter().getIntegerLDT(); try { @@ -870,6 +921,33 @@ public SLExpression createSeqDef(SLExpression a, SLExpression b, SLExpression t, return new SLExpression(resultTerm, seqtype); } + /* + * public SLExpression createMSet(SLExpression a, SLExpression b, SLExpression t, + * + * KeYJavaType declsType, ImmutableList declVars) { + * if (!(declsType.getJavaType().equals(PrimitiveType.JAVA_INT) + * || declsType.getJavaType().equals(PrimitiveType.JAVA_BIGINT))) { + * throw exc.createException0( + * "multiset definition variable must be of type int or \\bigint"); + * } else if (declVars.size() != 1) { + * throw exc.createException0("multiset definition must declare exactly one variable"); + * } + * QuantifiableVariable qv = declVars.head(); + * Term tt = t.getTerm(); + * if (tt.sort() == JavaDLTheory.FORMULA) { + * // bugfix (CS): t.getTerm() delivers a formula instead of a + * // boolean term; obviously the original boolean terms are + * // converted to formulas somewhere else; however, we need + * // boolean terms instead of formulas here + * tt = tb.convertToBoolean(t.getTerm()); + * } + * Term resultTerm = tb.mset(qv, a.getTerm(), b.getTerm(), tt); + * final KeYJavaType msettype = services.getJavaInfo().getPrimitiveKeYJavaType("\\mset"); + * return new SLExpression(resultTerm, msettype); + * } + * + */ + public SLExpression createUnionF(boolean nullable, Pair> declVars, Term expr, Term guard) { final JavaInfo javaInfo = services.getJavaInfo(); diff --git a/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/Translator.java b/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/Translator.java index 83548cb6e20..d338c7dc6e0 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/Translator.java +++ b/key.core/src/main/java/de/uka/ilkd/key/speclang/njml/Translator.java @@ -1724,8 +1724,11 @@ public SLExpression visitSpecquantifiedexpression( expr.getType()); case JmlLexer.MAX -> termFactory.quantifiedMax(guard, body, declVars.first, nullable, declVars.second); + case JmlLexer.MIN -> termFactory.quantifiedMin(guard, body, declVars.first, nullable, declVars.second); + case JmlLexer.MSET -> termFactory.quantifiedMset(declVars.first, nullable, declVars.second, guard, body, + services.getJavaInfo().getKeYJavaType(PrimitiveType.JAVA_MSET)); case JmlLexer.NUM_OF -> { KeYJavaType kjtInt = services.getTypeConverter().getKeYJavaType(PrimitiveType.JAVA_BIGINT); diff --git a/key.core/src/main/java/de/uka/ilkd/key/strategy/JavaCardDLStrategy.java b/key.core/src/main/java/de/uka/ilkd/key/strategy/JavaCardDLStrategy.java index 960cd033f50..6cb8217b7f3 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/strategy/JavaCardDLStrategy.java +++ b/key.core/src/main/java/de/uka/ilkd/key/strategy/JavaCardDLStrategy.java @@ -348,6 +348,11 @@ private RuleSetDispatchFeature setupCostComputationF() { bindRuleSet(d, "try_apply_subst", add(EqNonDuplicateAppFeature.INSTANCE, longConst(-10000))); + bindRuleSet(d, "out_of_bounds", + add(applyTF("left", tf.polynomial), + applyTF("right", tf.polynomial), + PolynomialValuesCmpFeature.lt(instOf("right"), instOf("left")))); + final TermBuffer superFor = new TermBuffer(); bindRuleSet(d, "split_if", add(sum(superFor, SuperTermGenerator.upwards(any(), getServices()), diff --git a/key.core/src/main/java/de/uka/ilkd/key/strategy/quantifierHeuristics/EqualityConstraint.java b/key.core/src/main/java/de/uka/ilkd/key/strategy/quantifierHeuristics/EqualityConstraint.java index f77ca51a50b..87110f3e2f6 100644 --- a/key.core/src/main/java/de/uka/ilkd/key/strategy/quantifierHeuristics/EqualityConstraint.java +++ b/key.core/src/main/java/de/uka/ilkd/key/strategy/quantifierHeuristics/EqualityConstraint.java @@ -549,9 +549,6 @@ private Constraint normalize(Metavariable mv, Term t, boolean modifyThis, // MV cycles are impossible if the orders of MV pairs are // correct - if (!t.isRigid()) { - return Constraint.TOP; - } // metavariable instantiations must not contain free variables if (!t.freeVars().isEmpty() || diff --git a/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/heapRules.key b/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/heapRules.key index 8303f8a0288..d6213b16151 100644 --- a/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/heapRules.key +++ b/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/heapRules.key @@ -83,7 +83,7 @@ | elementOf(o, f, freshLocs(h))) \then(beta::select(h2, o, f)) \else(beta::select(h, o, f))) - \heuristics(semantics_blasting) + \heuristics(simplify_ENLARGING) }; selectOfMemset { diff --git a/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/ldt.key b/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/ldt.key index d54f2527f00..536151c6682 100644 --- a/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/ldt.key +++ b/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/ldt.key @@ -28,4 +28,5 @@ freeADT, wellfound, charListHeader, - types; + types, + msetRules; diff --git a/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/msetRules.key b/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/msetRules.key new file mode 100644 index 00000000000..6375739a3cc --- /dev/null +++ b/key.core/src/main/resources/de/uka/ilkd/key/proof/rules/msetRules.key @@ -0,0 +1,320 @@ +\sorts { + Mset; + \generic alpha, beta; +} + +\functions { + // Getters + int msetMul(Mset, any); + int msetCard(Mset); + + // Constructors + Mset msetEmpty; + Mset msetSingle(any); + Mset msetUnion(Mset, Mset); + Mset msetIntersec(Mset, Mset); + Mset msetDiff(Mset, Mset); + Mset msetSum(Mset, Mset); + Mset msetRange {false, false, true}(int, int, any); +} + +\rules { + + mset_Empty { + \schemaVar \term int left, right; + \schemaVar \variables int uSub; + \schemaVar \term beta t; + + \find(msetRange{uSub;}(left, right, t)) + \varcond(\notFreeIn(uSub, left)) + \replacewith(\if(right < left) + \then(msetEmpty) + \else(msetRange{uSub;}(left, right, t))) + + \heuristics(out_of_bounds) + }; + + mset_Single { + \schemaVar \term int left; + \schemaVar \variables int uSub; + \schemaVar \term beta t; + + \find(msetRange{uSub;}(left, left, t)) + \sameUpdateLevel + \replacewith(msetSingle({\subst uSub; left}(t))) + \heuristics(simplify) + }; + + msetUnionWithMSetEmpty1 { + \find(msetUnion(msetEmpty, msetEmpty)) + \replacewith(msetEmpty) + \heuristics(simplify) + \displayname "msetUnionWithEmpty" + }; + + msetUnionWithMSetEmpty2 { + \schemaVar \term Mset m; + \find(msetUnion(m, msetEmpty)) + \replacewith(m) + \heuristics(simplify) + \displayname "msetUnionWithEmpty" + }; + + msetUnionWithSameMSets { + \schemaVar \term Mset m; + \find(msetUnion(m, m)) + \replacewith(m) + \heuristics(simplify) + }; + + msetUnionCommutativity { + \schemaVar \term Mset commLeft, commRight; + \find(msetUnion(commLeft, commRight)) + \replacewith(msetUnion(commRight, commLeft)) + \heuristics(polySimp_expand, polySimp_addOrder) + }; + + msetUnionAssociativity { + \schemaVar \term Mset addAssocPoly0, addAssocPoly1, addAssocMono; + \find(msetUnion(addAssocPoly0, msetUnion(addAssocPoly1, addAssocMono))) + \replacewith(msetUnion(msetUnion(addAssocPoly0, addAssocPoly1), addAssocMono)) + \heuristics(polySimp_expand, polySimp_addAssoc) + }; + + msetUnionWithMSetIntersection { + \schemaVar \term Mset m, s; + \find(msetUnion(m, msetIntersec(m, s))) + \replacewith(m) + \heuristics(simplify) + }; + + msetUnionSubset { + \schemaVar \term Mset m, s; + \schemaVar \term any msetEl; + + \find(msetUnion(m, s)) + \replacewith(\if(msetMul(m, msetEl) > 0 & msetMul(s, msetEl) > 0 & msetIntersec(m, s) = s) + \then(m) + \else(msetUnion(m, s))) + \heuristics(simplify) + }; + + msetIntersectionWithMSetEmpty1 { + \find(msetIntersec(msetEmpty, msetEmpty)) + \replacewith(msetEmpty) + \heuristics(simplify) + \displayname "msetIntersecWithEmpty" + }; + + msetIntersectionWithMSetEmpty2 { + \schemaVar \term Mset m; + \find(msetIntersec(m, msetEmpty)) + \replacewith(msetEmpty) + \heuristics(simplify) + \displayname "msetIntersecWithEmpty" + }; + + msetIntersecWithSameMSets { + \schemaVar \term Mset m; + \find(msetIntersec(m, m)) + \replacewith(m) + \heuristics(simplify) + }; + + msetIntersecCommutativity { + \schemaVar \term Mset commLeft, commRight; + \find(msetIntersec(commLeft, commRight)) + \replacewith(msetIntersec(commRight, commLeft)) + \heuristics(polySimp_expand, polySimp_addOrder) + }; + + msetIntersecDifferent { + \schemaVar \term Mset m, s; + \schemaVar \term any msetEl; + + \find(msetIntersec(m, s)) + \replacewith(\if(msetMul(m, msetEl) > 0 & msetMul(s, msetEl) = 0) + \then(msetEmpty) + \else(msetIntersec(m, s))) + \heuristics(simplify) + }; + + msetIntersecSubset { + \schemaVar \term Mset m, s; + \schemaVar \term any msetEl; + + \find(msetIntersec(m, s)) + \replacewith(\if(msetMul(m, msetEl) > 0 & msetMul(s, msetEl) > 0 & msetUnion(m, s) = m) + \then(s) + \else(msetIntersec(m, s))) + \heuristics(simplify) + }; + + msetIntersecWithMSetUnion { + \schemaVar \term Mset m, s, t; + \find(msetIntersec(m, msetUnion(s, t))) + \replacewith(msetUnion(msetIntersec(m, s), msetIntersec(m, t))) + \heuristics(simplify) + }; + + msetSumWithMSetEmpty1 { + \schemaVar \term Mset m; + \find(msetSum(msetEmpty, m)) + \replacewith(m) + \heuristics(simplify) + \displayname "msetSumWithEmpty" + }; + + msetSumWithMSetEmpty2 { + \schemaVar \term Mset m; + \find(msetSum(m, msetEmpty)) + \replacewith(m) + \heuristics(simplify) + \displayname "msetSumWithEmpty" + }; + + msetSumCommutativity { + \schemaVar \term Mset commLeft, commRight; + \find(msetSum(commLeft, commRight)) + \replacewith(msetSum(commRight, commLeft)) + \heuristics(polySimp_expand, polySimp_addOrder) + }; + + msetSumAssociativity { + \schemaVar \term Mset addAssocPoly0, addAssocPoly1, addAssocMono; + \find(msetSum(addAssocPoly0, msetSum(addAssocPoly1, addAssocMono))) + \replacewith(msetSum(msetSum(addAssocPoly0, addAssocPoly1), addAssocMono)) + \heuristics(polySimp_expand, polySimp_addAssoc) + }; + + msetDiffWithMSetEmpty1 { + \find(msetDiff(msetEmpty, msetEmpty)) + \replacewith(msetEmpty) + \heuristics(simplify) + \displayname "msetDiffWithEmpty" + }; + + msetDiffWithMSetEmpty2 { + \schemaVar \term Mset m; + \find(msetDiff(m, msetEmpty)) + \replacewith(m) + \heuristics(simplify) + \displayname "msetDiffWithEmpty" + }; + + msetDiffCommutativity { + \schemaVar \term Mset commLeft, commRight; + \find(msetDiff(commLeft, commRight)) + \replacewith(msetDiff(commRight, commLeft)) + \heuristics(polySimp_expand, polySimp_addOrder) + }; + + mset_split { + \schemaVar \term int left, right; + \schemaVar \term int middle; + \schemaVar \variables int uSub; + \schemaVar \term beta t; + + \find(msetRange{uSub;}(left, right, t)) + \varcond(\notFreeIn(uSub, left), \notFreeIn(uSub, middle), \notFreeIn(uSub, right)) + \replacewith(\if(left <= middle & middle <= right) + \then(msetSum(msetRange{uSub;}(left, middle-1, t), + msetRange{uSub;}(middle, right, t))) + \else(msetRange{uSub;}(left, right, t))) + }; + + mset_extract_triggered { + \schemaVar \term int left, right; + \schemaVar \term int middle; + \schemaVar \variables int uSub; + \schemaVar \term Heap h; + \schemaVar \term Object array; + + \find(msetRange{uSub;}(left, right, beta::select(h, array, arr(uSub)))) + \varcond(\notFreeIn(uSub, left), \notFreeIn(uSub, middle), \notFreeIn(uSub, right), \notFreeIn(uSub, h), \notFreeIn(uSub, array)) + \replacewith(\if(left <= middle & middle <= right) + \then(msetSum(msetSum(msetRange{uSub;}(left, middle-1, beta::select(h, array, arr(uSub))), + msetSingle(beta::select(h, array, arr(middle)))), + msetRange{uSub;}(middle+1, right, beta::select(h, array, arr(uSub))))) + \else(msetRange{uSub;}(left, right, beta::select(h, array, arr(uSub))))) + \heuristics(comprehension_split, triggered) + \trigger {middle} msetSingle(beta::select(h, array, arr(middle))) + \avoid middle <= -1 + left, right <= -1 + middle; + }; + + mset_extract_array { + \schemaVar \term int left, right; + \schemaVar \term int middle; + \schemaVar \term Heap h; + \schemaVar \term Object array; + \schemaVar \variables int uSub; + \schemaVar \term alpha x; + + \find(msetRange{uSub;}(left, right, beta::select(store(h, array, arr(middle), x), array, arr(uSub)))) + \varcond(\notFreeIn(uSub, left), \notFreeIn(uSub, middle), \notFreeIn(uSub, right)) + \replacewith(\if(left <= middle & middle <= right) + \then(msetSum(msetSum(msetRange{uSub;}(left, middle-1, beta::select(h, array, arr(uSub))), + msetSingle({\subst uSub; middle} x)), + msetRange{uSub;}(middle+1, right, beta::select(h, array, arr(uSub))))) + \else(msetRange{uSub;}(left, right, beta::select(h, array, arr(uSub))))) + \heuristics(simplify_ENLARGING) + }; + + mset_extract_array_front { + \schemaVar \term int left, right; + \schemaVar \variables int uSub; + \schemaVar \term Heap h; + \schemaVar \term Object array; + \schemaVar \term alpha x; + + \find(msetRange{uSub;}(left, right, beta::select(store(h, array, arr(left), x), array, arr(uSub)))) + \varcond(\notFreeIn(uSub, left), \notFreeIn(uSub, right)) + \replacewith(msetSum(msetSingle({\subst uSub; left} x), + msetRange{uSub;}(left+1, right, beta::select(h, array, arr(uSub))))) + \heuristics(simplify_enlarging) + }; + + mset_extract_array_back { + \schemaVar \term int left, right; + \schemaVar \variables int uSub; + \schemaVar \term Heap h; + \schemaVar \term Object array; + \schemaVar \term alpha x; + + \find(msetRange{uSub;}(left, right, beta::select(store(h, array, arr(right), x), array, arr(uSub)))) + \varcond(\notFreeIn(uSub, left), \notFreeIn(uSub, right)) + \replacewith(msetSum(msetRange{uSub;}(left, right-1, beta::select(h, array, arr(uSub))), + msetSingle({\subst uSub; right} x))) + \heuristics(simplify_enlarging) + }; + + mset_extract_triggered_front { + \schemaVar \term int low, high; + \schemaVar \variables int uSub; + \schemaVar \term Heap h; + \schemaVar \term Object array; + + \find(msetRange{uSub;}(low, high, beta::select(h, array, arr(uSub)))) + \varcond(\notFreeIn(uSub, low), \notFreeIn(uSub, high), \notFreeIn(uSub, h), \notFreeIn(uSub, array)) + \replacewith(msetSum(msetSingle(beta::select(h, array, arr(low))), + msetRange{uSub;}(low+1, high, beta::select(h, array, arr(uSub))))) + \heuristics(comprehension_split, triggered) + \trigger {low} msetSingle(beta::select(h, array, arr(low))) + \avoid low >= high, false; + }; + + mset_extract_triggered_back { + \schemaVar \term int low, high; + \schemaVar \variables int uSub; + \schemaVar \term Heap h; + \schemaVar \term Object array; + + \find(msetRange{uSub;}(low, high, beta::select(h, array, arr(uSub)))) + \varcond(\notFreeIn(uSub, low), \notFreeIn(uSub, high), \notFreeIn(uSub, h), \notFreeIn(uSub, array)) + \replacewith(msetSum(msetSingle(beta::select(h, array, arr(high))), + msetRange{uSub;}(low, high-1, beta::select(h, array, arr(uSub))))) + \heuristics(comprehension_split, triggered) + \trigger {high} msetSingle(beta::select(h, array, arr(high))) + \avoid high <= low, false; + }; +} diff --git a/key.ui/examples/heap/quicksort_mset/Quicksort.java b/key.ui/examples/heap/quicksort_mset/Quicksort.java new file mode 100644 index 00000000000..a4c013082ac --- /dev/null +++ b/key.ui/examples/heap/quicksort_mset/Quicksort.java @@ -0,0 +1,77 @@ +/** + * This shows how MultiSets can be used to verify the permutation property + * of a sorting algorithm. + * FIXME: The sortedness property is NOT shown. + * The source code is the same as quicksort/QuickSort.java but the present + * specification is proven automatically, without proof scripts. + * + * @author Silvia Zoraqi, Lukas Grätz, 2025 + * + * based on: + * @author Mattias Ulbrich, 2015 + */ + +class Quicksort { + + /*@ public normal_behaviour + @ ensures (\mset int k; 0 <= k < array.length; array[k]) == \old((\mset int k; 0 <= k < array.length; array[k])); + @ assignable array[*]; + @*/ + public void sort(int[] array) { + if(array.length > 0) { + sort(array, 0, array.length-1); + } + } + + /*@ public normal_behaviour + @ requires 0 <= from; + @ requires to < array.length; + @ ensures (\mset int k; 0 <= k < array.length; array[k]) == \old((\mset int k; 0 <= k < array.length; array[k])); + @ assignable array[*]; + @ measured_by to - from + 1; + @*/ + private void sort(int[] array, int from, int to) { + if(from < to) { + int splitPoint = split(array, from, to); + sort(array, from, splitPoint-1); + sort(array, splitPoint+1, to); + } + } + + /*@ public normal_behaviour + @ requires 0 <= from && from < to && to <= array.length-1; + @ ensures (\mset int k; 0 <= k < array.length; array[k]) == \old((\mset int k; 0 <= k < array.length; array[k])); + @ ensures from <= \result && \result <= to; + @ assignable array[*]; + @*/ + private int split(int[] array, int from, int to) { + + int i = from; + int pivot = array[to]; + + /*@ + @ loop_invariant from <= i && i <= j; + @ loop_invariant from <= j && j <= to; + @ loop_invariant (\mset int k; 0 <= k < array.length; array[k]) == \old((\mset int k; 0 <= k < array.length; array[k])); + @ decreases to + to - j - i + 2; + @ assignable array[*]; + @*/ + for(int j = from; j < to; j++) { + if(array[j] <= pivot) { + int t = array[i]; + array[i] = array[j]; + array[j] = t; + i++; + } + } + + // FIXME: this assignment has no effect, it should be a loop invariant + pivot = array[to]; + + array[to] = array[i]; + array[i] = pivot; + + return i; + + } +}