|
15 | 15 | import org.key_project.util.collection.ImmutableMapEntry; |
16 | 16 | import org.key_project.util.collection.ImmutableSet; |
17 | 17 |
|
| 18 | +/** |
| 19 | + * A trigger built from several uni-trigger {@link #elements} that, taken together, bind all |
| 20 | + * universal variables of a clause. A match requires matching every element and merging their |
| 21 | + * substitutions into one that is consistent and total on {@link #clauseVariables}. |
| 22 | + */ |
18 | 23 | class MultiTrigger implements Trigger { |
19 | 24 |
|
20 | | - private final ImmutableSet<Trigger> triggers; |
| 25 | + /** The uni-trigger elements that jointly have to cover {@link #clauseVariables}. */ |
| 26 | + private final ImmutableSet<Trigger> elements; |
21 | 27 |
|
22 | | - private final ImmutableSet<QuantifiableVariable> qvs; |
| 28 | + /** The universal variables of the clause this multi-trigger must bind. */ |
| 29 | + private final ImmutableSet<QuantifiableVariable> clauseVariables; |
23 | 30 |
|
24 | 31 | private final Term clause; |
25 | 32 |
|
26 | | - MultiTrigger(ImmutableSet<Trigger> triggers, ImmutableSet<QuantifiableVariable> qvs, |
| 33 | + MultiTrigger(ImmutableSet<Trigger> elements, ImmutableSet<QuantifiableVariable> clauseVariables, |
27 | 34 | Term clause) { |
28 | | - this.triggers = triggers; |
29 | | - this.qvs = qvs; |
| 35 | + this.elements = elements; |
| 36 | + this.clauseVariables = clauseVariables; |
30 | 37 | this.clause = clause; |
31 | 38 | } |
32 | 39 |
|
33 | 40 | @Override |
34 | 41 | public ImmutableSet<Substitution> getSubstitutionsFromTerms(ImmutableSet<Term> targetTerms, |
35 | 42 | Services services) { |
36 | | - ImmutableList<Substitution> res = ImmutableList.nil(); |
| 43 | + ImmutableList<Substitution> total = ImmutableList.nil(); |
37 | 44 |
|
38 | | - ImmutableSet<Substitution> mulsubs = |
39 | | - setMultiSubstitution(triggers.iterator(), targetTerms, services); |
| 45 | + final ImmutableSet<Substitution> combined = |
| 46 | + combineElementSubstitutions(elements.iterator(), targetTerms, services); |
40 | 47 |
|
41 | | - for (Substitution sub : mulsubs) { |
42 | | - if (sub.isTotalOn(qvs)) { |
43 | | - res = res.prepend(sub); |
| 48 | + for (Substitution sub : combined) { |
| 49 | + if (sub.isTotalOn(clauseVariables)) { |
| 50 | + total = total.prepend(sub); |
44 | 51 | } |
45 | 52 | } |
46 | 53 |
|
47 | | - return DefaultImmutableSet.fromImmutableList(res); |
| 54 | + return DefaultImmutableSet.fromImmutableList(total); |
48 | 55 | } |
49 | 56 |
|
50 | | - /** help function for getMultiSubstitution */ |
51 | | - private ImmutableSet<Substitution> setMultiSubstitution(Iterator<? extends Trigger> ts, |
52 | | - ImmutableSet<Term> terms, Services services) { |
53 | | - ImmutableList<Substitution> res = ImmutableList.nil(); |
54 | | - if (ts.hasNext()) { |
55 | | - ImmutableSet<Substitution> subi = ts.next().getSubstitutionsFromTerms(terms, services); |
56 | | - ImmutableSet<Substitution> nextSubs = setMultiSubstitution(ts, terms, services); |
57 | | - if (nextSubs.isEmpty()) { |
58 | | - return subi; |
59 | | - } else if (subi.isEmpty()) { |
60 | | - return nextSubs; |
| 57 | + /** |
| 58 | + * Matches every remaining element against the target terms and combines the per-element |
| 59 | + * substitution sets by taking the cross product and merging each compatible pair (incompatible |
| 60 | + * pairs are dropped). Used by {@link #getSubstitutionsFromTerms}. |
| 61 | + */ |
| 62 | + private ImmutableSet<Substitution> combineElementSubstitutions( |
| 63 | + Iterator<? extends Trigger> remainingElements, ImmutableSet<Term> terms, |
| 64 | + Services services) { |
| 65 | + ImmutableList<Substitution> result = ImmutableList.nil(); |
| 66 | + if (remainingElements.hasNext()) { |
| 67 | + ImmutableSet<Substitution> headSubs = |
| 68 | + remainingElements.next().getSubstitutionsFromTerms(terms, services); |
| 69 | + ImmutableSet<Substitution> tailSubs = |
| 70 | + combineElementSubstitutions(remainingElements, terms, services); |
| 71 | + if (tailSubs.isEmpty()) { |
| 72 | + return headSubs; |
| 73 | + } else if (headSubs.isEmpty()) { |
| 74 | + return tailSubs; |
61 | 75 | } |
62 | | - for (Substitution sub0 : nextSubs) { |
63 | | - for (Substitution subiSub : subi) { |
64 | | - final Substitution sub1 = unifySubstitution(sub0, subiSub); |
65 | | - if (sub1 != null) { |
66 | | - res = res.prepend(sub1); |
| 76 | + for (Substitution tailSub : tailSubs) { |
| 77 | + for (Substitution headSub : headSubs) { |
| 78 | + final Substitution merged = mergeIfCompatible(tailSub, headSub); |
| 79 | + if (merged != null) { |
| 80 | + result = result.prepend(merged); |
67 | 81 | } |
68 | 82 | } |
69 | 83 |
|
70 | 84 | } |
71 | 85 | } |
72 | | - return DefaultImmutableSet.fromImmutableList(res); |
| 86 | + return DefaultImmutableSet.fromImmutableList(result); |
73 | 87 | } |
74 | 88 |
|
75 | 89 | /** |
76 | | - * unify two substitution, if same variable are bound with same term return a new substitution |
77 | | - * with all universal quantifiable variables in two substituition, otherwise return null |
| 90 | + * Merges two substitutions: if they bind every shared variable to the same term, returns a new |
| 91 | + * substitution containing all bindings of both; otherwise (a conflicting binding) returns |
| 92 | + * {@code null}. |
78 | 93 | */ |
79 | | - private Substitution unifySubstitution(Substitution sub0, Substitution sub1) { |
80 | | - final ImmutableMap<QuantifiableVariable, Term> varMap1 = sub1.getVarMap(); |
81 | | - ImmutableMap<QuantifiableVariable, Term> resMap = varMap1; |
82 | | - |
83 | | - for (final ImmutableMapEntry<QuantifiableVariable, Term> en : sub0.getVarMap()) { |
84 | | - QuantifiableVariable key = en.key(); |
85 | | - Term value = en.value(); |
86 | | - if (varMap1.containsKey(key)) { |
87 | | - if (!(varMap1.get(key).equals(value))) { |
| 94 | + private Substitution mergeIfCompatible(Substitution left, Substitution right) { |
| 95 | + final ImmutableMap<QuantifiableVariable, Term> rightMap = right.getVarMap(); |
| 96 | + ImmutableMap<QuantifiableVariable, Term> mergedMap = rightMap; |
| 97 | + |
| 98 | + for (final ImmutableMapEntry<QuantifiableVariable, Term> entry : left.getVarMap()) { |
| 99 | + QuantifiableVariable key = entry.key(); |
| 100 | + Term value = entry.value(); |
| 101 | + if (rightMap.containsKey(key)) { |
| 102 | + if (!(rightMap.get(key).equals(value))) { |
88 | 103 | return null; |
89 | 104 | } |
90 | 105 | } |
91 | | - resMap = resMap.put(key, value); |
| 106 | + mergedMap = mergedMap.put(key, value); |
92 | 107 | } |
93 | | - return new Substitution(resMap); |
| 108 | + return new Substitution(mergedMap); |
94 | 109 | } |
95 | 110 |
|
96 | 111 | @Override |
97 | | - public boolean equals(Object arg0) { |
98 | | - if (!(arg0 instanceof MultiTrigger a)) { |
| 112 | + public boolean equals(Object other) { |
| 113 | + if (!(other instanceof MultiTrigger otherTrigger)) { |
99 | 114 | return false; |
100 | 115 | } |
101 | 116 |
|
102 | | - return a.triggers.equals(triggers); |
| 117 | + return otherTrigger.elements.equals(elements); |
103 | 118 | } |
104 | 119 |
|
105 | 120 | @Override |
106 | 121 | public int hashCode() { |
107 | | - return triggers.hashCode(); |
| 122 | + return elements.hashCode(); |
108 | 123 | } |
109 | 124 |
|
110 | 125 | @Override |
111 | 126 | public String toString() { |
112 | | - return String.valueOf(triggers); |
| 127 | + return String.valueOf(elements); |
113 | 128 | } |
114 | 129 |
|
115 | 130 | @Override |
|
0 commit comments