1111import static com .google .common .base .Preconditions .checkArgument ;
1212import static org .sosy_lab .java_smt .api .QuantifiedFormulaManager .Quantifier .EXISTS ;
1313import static org .sosy_lab .java_smt .api .QuantifiedFormulaManager .Quantifier .FORALL ;
14+ import static org .sosy_lab .java_smt .solvers .princess .PrincessEnvironment .toITermSeq ;
1415import static org .sosy_lab .java_smt .solvers .princess .PrincessEnvironment .toSeq ;
1516import static scala .collection .JavaConverters .asJava ;
1617import static scala .collection .JavaConverters .asJavaCollection ;
17- import static scala .collection .JavaConverters .asScala ;
1818
1919import ap .basetypes .IdealInt ;
2020import ap .parser .IAtom ;
5454import com .google .common .collect .Table ;
5555import java .lang .reflect .InvocationTargetException ;
5656import java .util .HashMap ;
57+ import java .util .LinkedHashMap ;
5758import java .util .List ;
5859import java .util .Map ;
5960import java .util .Set ;
60- import org .sosy_lab .common .UniqueIdGenerator ;
6161import org .sosy_lab .common .rationals .Rational ;
6262import org .sosy_lab .java_smt .api .ArrayFormula ;
6363import org .sosy_lab .java_smt .api .BitvectorFormula ;
@@ -185,8 +185,10 @@ class PrincessFormulaCreator
185185 */
186186 private final Table <Sort , Sort , Sort > arraySortCache = HashBasedTable .create ();
187187
188- // Keeps track of the bound/free variables created when visiting quantified terms.
189- private final UniqueIdGenerator boundVariablesId = new UniqueIdGenerator ();
188+ /** This map keeps track of the bound/free variables created when visiting quantified terms. */
189+ // TODO should we use a WeakHashMap?
190+ // We do not cleanup in other places, too, and the number of quantified formulas is small.
191+ private final Map <IFormula , String > boundVariableNames = new LinkedHashMap <>();
190192
191193 PrincessFormulaCreator (PrincessEnvironment pEnv ) {
192194 super (
@@ -538,14 +540,14 @@ private <R> R visitQuantifier(FormulaVisitor<R> visitor, BooleanFormula f, IQuan
538540
539541 // Princess uses de-Bruijn indices, so we have index 0 here for the most outer quantified scope
540542 IVariable boundVariable = input .sort ().boundVariable (0 );
541- String boundVariableName = "__JAVASMT__BOUND_VARIABLE_" + boundVariablesId .getFreshId ();
543+ String boundVariableName = getFreshVariableNameForBody (body );
544+
542545 // Currently, Princess supports only non-boolean bound variables, so we can cast to ITerm.
543546 ITerm substitutionVariable = (ITerm ) makeVariable (boundVariable .sort (), boundVariableName );
544547
545- // substitute the bound variable with a new variable,
546- // and un-shift the remaining de-Bruijn indices.
547- IFormula substitutedBody =
548- IFormula .subst (body , asScala (List .of (substitutionVariable )).toList (), -1 );
548+ // substitute the bound variable with index 0 with a new variable, and un-shift the remaining
549+ // de-Bruijn indices, such that the next nested bound variable has index 0.
550+ IFormula substitutedBody = IFormula .subst (body , toITermSeq (substitutionVariable ).toList (), -1 );
549551
550552 return visitor .visitQuantifier (
551553 f ,
@@ -554,6 +556,16 @@ private <R> R visitQuantifier(FormulaVisitor<R> visitor, BooleanFormula f, IQuan
554556 encapsulateBoolean (substitutedBody ));
555557 }
556558
559+ /**
560+ * Get a fresh variable name for the given formula. We compute the same variable name for the same
561+ * body, such that a user gets the same substituted body when visiting a quantified formulas
562+ * several times.
563+ */
564+ private String getFreshVariableNameForBody (IFormula body ) {
565+ return boundVariableNames .computeIfAbsent (
566+ body , k -> "__JAVASMT__BOUND_VARIABLE_" + boundVariableNames .size ());
567+ }
568+
557569 private boolean isBitvectorOperationWithAdditionalArgument (FunctionDeclarationKind kind ) {
558570 switch (kind ) {
559571 case BV_NOT :
0 commit comments