001package daikon;
002
003import static daikon.tools.nullness.NullnessUtil.castNonNullDeep;
004
005import daikon.inv.Equality;
006import daikon.inv.Invariant;
007import java.util.ArrayList;
008import java.util.Arrays;
009import java.util.Collections;
010import java.util.Comparator;
011import java.util.HashMap;
012import java.util.Iterator;
013import java.util.LinkedHashMap;
014import java.util.List;
015import java.util.Map;
016import java.util.Set;
017import java.util.logging.Level;
018import java.util.logging.Logger;
019import org.checkerframework.checker.initialization.qual.UnknownInitialization;
020import org.checkerframework.checker.lock.qual.GuardSatisfied;
021import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
022import org.checkerframework.checker.nullness.qual.KeyFor;
023import org.checkerframework.checker.nullness.qual.Nullable;
024import org.checkerframework.dataflow.qual.Pure;
025import org.checkerframework.dataflow.qual.SideEffectFree;
026
027/** Holds Equality invariants. */
028public class PptSliceEquality extends PptSlice {
029  static final long serialVersionUID = 20021231L;
030
031  // Variables starting with dkconfig_ should only be set via the
032  // daikon.config.Configuration interface.
033
034  /**
035   * If true, create one equality set for each variable. This has the effect of turning the equality
036   * optimization off, without actually removing the sets themselves (which are presumed to exist in
037   * many parts of the code).
038   */
039  public static boolean dkconfig_set_per_var = false;
040
041  public static final Logger debug = Logger.getLogger("daikon.PptSliceEquality");
042
043  public static final Logger debugGlobal = Logger.getLogger("daikon.PptSliceEquality.Global");
044
045  PptSliceEquality(PptTopLevel parent) {
046    super(parent, parent.var_infos);
047  }
048
049  @Override
050  public final int arity(@UnknownInitialization(PptSlice.class) PptSliceEquality this) {
051    throw new Error("Don't call arity on PptSliceEquality");
052  }
053
054  void init_po() {
055    throw new Error("Shouldn't get called");
056  }
057
058  @Override
059  public void addInvariant(Invariant inv) {
060    assert inv instanceof Equality;
061    invs.add(inv);
062  }
063
064  // Not valid for this type of slice.  Always pretend there are enough.
065  @Override
066  public int num_samples(@UnknownInitialization @GuardSatisfied PptSliceEquality this) {
067    if (true) {
068      throw new Error();
069    }
070    return Integer.MAX_VALUE;
071  }
072
073  public int num_mod_samples() {
074    if (true) {
075      throw new Error();
076    }
077    return Integer.MAX_VALUE;
078  }
079
080  @Override
081  public int num_values() {
082    if (true) {
083      throw new Error();
084    }
085    return Integer.MAX_VALUE;
086  }
087
088  /**
089   * Encapsulates a VarInfo and its Comparability so that the two can be used to create sets of
090   * VarInfos that are initially equal. Two VarInfoAndComparability's are true iff they are
091   * VarComparability.comparable() to each other.
092   */
093  private static class VarInfoAndComparability {
094    final VarInfo vi;
095
096    @Pure
097    @Override
098    public int hashCode(@GuardSatisfied VarInfoAndComparability this) {
099      // This is very coarse but is about as good as we can do it.  Can't do hashcode of
100      // the comparability because two comparabilities may be
101      // comparable and yet be not the same.
102      // (e.g. VarComparabilityExplicit).
103      return vi.file_rep_type.hashCode();
104    }
105
106    @EnsuresNonNullIf(result = true, expression = "#1")
107    @Pure
108    @Override
109    public boolean equals(
110        @GuardSatisfied VarInfoAndComparability this, @GuardSatisfied @Nullable Object o) {
111      if (!(o instanceof VarInfoAndComparability)) {
112        return false;
113      }
114      return equalsVarInfoAndComparability((VarInfoAndComparability) o);
115    }
116
117    /**
118     * True if two VarInfos can be set to be equal to each other is whether they are comparableNWay.
119     * Since we do not yet handle inheritance, we require that the comparability go both ways.
120     */
121    @EnsuresNonNullIf(result = true, expression = "#1")
122    @Pure
123    boolean equalsVarInfoAndComparability(
124        @GuardSatisfied VarInfoAndComparability this, @GuardSatisfied VarInfoAndComparability o) {
125
126      return (vi.comparableNWay(o.vi)
127          && vi.comparability.equality_set_ok(o.vi.comparability)
128          && vi.aux.equals_for_instantiation(o.vi.aux));
129    }
130
131    VarInfoAndComparability(VarInfo vi) {
132      this.vi = vi;
133    }
134  }
135
136  /** Actually instantiate the equality sets. */
137  @Override
138  void instantiate_invariants() {
139
140    // If each variable gets its own set, create those sets and return
141    if (dkconfig_set_per_var) {
142      // Debug.debugTrack.fine ("Vars for " + parent.name());
143      for (VarInfo vi : var_infos) {
144        List<VarInfo> vi_list = Collections.singletonList(vi);
145        Equality eq = new Equality(vi_list, this);
146        invs.add(eq);
147        // System.out.println ("  eq set = " + eq.shortString());
148      }
149      return;
150    }
151
152    // Start with everything comparable being equal.
153    if (debug.isLoggable(Level.FINE)) {
154      debug.fine("PptSliceEquality.instantiate_invariants: " + parent.name() + " vars:");
155    }
156    LinkedHashMap<VarInfoAndComparability, List<VarInfo>> multiMap = new LinkedHashMap<>();
157    for (VarInfo vi : var_infos) {
158      VarInfoAndComparability viac = new VarInfoAndComparability(vi);
159      addToBindingList(multiMap, viac, vi);
160      if (debug.isLoggable(Level.FINE)) {
161        debug.fine("  " + vi.name() + ": " + vi.comparability);
162      }
163    }
164    if (debug.isLoggable(Level.FINE)) {
165      debug.fine(
166          "PptSliceEquality.instantiate_invariants "
167              + parent.name()
168              + ": "
169              + Integer.toString(multiMap.keySet().size())
170              + " VarInfoAndComparability keys");
171    }
172    Equality[] newInvs = new Equality[multiMap.keySet().size()];
173    int varCount = 0;
174    int invCount = 0;
175    for (List<VarInfo> list : multiMap.values()) {
176      varCount += list.size();
177
178      Equality eq = new Equality(list, this);
179      newInvs[invCount] = eq;
180      if (debug.isLoggable(Level.FINE)) {
181        debug.fine(" Created: " + eq);
182        for (VarInfo vi : list) {
183          debug.fine("   vi: " + vi + " aux : " + vi.aux);
184        }
185      }
186      if (Debug.logOn()) {
187        Debug.log(getClass(), parent, Debug.vis(eq.leader()), "Created");
188      }
189      invCount++;
190    }
191    // Ensure determinism
192    Arrays.sort(newInvs, EqualityComparator.theInstance);
193    invs.addAll(Arrays.<Invariant>asList(newInvs));
194    assert varCount == var_infos.length; // Check that we get all vis
195  }
196
197  /**
198   * Instantiate the full equality sets from a set of variable pairs where each member of a pair is
199   * equal to the other.
200   */
201  public void instantiate_from_pairs(Set<VarInfo.Pair> eset) {
202
203    // Build a map from each variable to all those that are equal to it
204    Map<VarInfo, List<VarInfo>> varmap = new LinkedHashMap<>();
205    Map<VarInfo, Integer> sample_cnt_map = new LinkedHashMap<>();
206    for (VarInfo.Pair cp : eset) {
207      List<VarInfo> vlist = varmap.get(cp.v1);
208      if (vlist == null) {
209        vlist = new ArrayList<VarInfo>();
210        vlist.add(cp.v1);
211        varmap.put(cp.v1, vlist);
212        sample_cnt_map.put(cp.v1, cp.samples);
213      }
214      vlist.add(cp.v2);
215      vlist = varmap.get(cp.v2);
216      if (vlist == null) {
217        vlist = new ArrayList<VarInfo>();
218        vlist.add(cp.v2);
219        varmap.put(cp.v2, vlist);
220        sample_cnt_map.put(cp.v2, cp.samples);
221      }
222      vlist.add(cp.v1);
223    }
224
225    // Loop through each variable, building the appropriate equality set
226    // for each.  Note that variables that are distinct still have an
227    // equality set (albeit with only the one variable)
228    ArrayList<Invariant> newInvs = new ArrayList<>();
229    for (VarInfo v : var_infos) {
230      if (v.equalitySet != null) {
231        continue;
232      }
233      List<VarInfo> vlist = varmap.get(v);
234      if (vlist == null) {
235        vlist = Collections.singletonList(v);
236      }
237      Equality eq = new Equality(vlist, this);
238      Integer sample_cnt = sample_cnt_map.get(v);
239      if (sample_cnt != null) {
240        eq.setSamples(sample_cnt.intValue());
241      }
242      v.equalitySet = eq;
243      newInvs.add(eq);
244    }
245    invs.addAll(newInvs);
246  }
247
248  /**
249   * Returns a List of Invariants that have been weakened/destroyed. However, this handles the
250   * creation of new Equality invariants and the instantiation of other invariants.
251   *
252   * @return a List of invariants that have been weakened
253   */
254  // The basic approach is as follows:
255  //    - Loop through each equality set
256  //        - look for any variables that are no longer equal
257  //        - Create new equality sets (call createEqualityInvs)
258  //        - Get the new leaders
259  //        - Create new slices and invariants (call CopyInvsFromLeader)
260  //
261  @Override
262  public List<Invariant> add(ValueTuple vt, int count) {
263
264    ArrayList<Equality> allNewInvs = new ArrayList<>();
265    ArrayList<Invariant> weakenedInvs = new ArrayList<>();
266
267    // Loop through each existing equality invariant
268    for (Invariant invar : invs) {
269      Equality inv = (Equality) invar;
270
271      // Add this sample to the invariant and track any vars that fall
272      // out of the set.
273      List<VarInfo> nonEqualVis = inv.add(vt, count);
274
275      // If some vars fell out
276      if (!nonEqualVis.isEmpty()) {
277
278        // Create new equality sets for all of the non-equal vars
279        List<Equality> newInvs = createEqualityInvs(nonEqualVis, vt, inv, count);
280
281        // Get a list of all of the new non-missing leaders
282        List<VarInfo> newInvsLeaders = new ArrayList<>(newInvs.size());
283        for (Equality eq : newInvs) {
284          if ((parent.constants == null) || !parent.constants.is_missing(eq.leader())) {
285            newInvsLeaders.add(eq.leader());
286          }
287        }
288
289        // Debug print the new leaders
290        if (Debug.logOn()) {
291          for (VarInfo nileader : newInvsLeaders) {
292            Debug.log(
293                getClass(),
294                parent,
295                Debug.vis(nileader),
296                "Split off from previous leader "
297                    + inv.leader().name()
298                    + ": new set = "
299                    + nileader.equalitySet
300                    + ": old set = "
301                    + inv);
302          }
303        }
304
305        // Create new slices and invariants for each new leader
306        weakenedInvs.addAll(copyInvsFromLeader(inv.leader(), newInvsLeaders));
307
308        // Keep track of all of the new invariants created.
309        allNewInvs.addAll(newInvs);
310      }
311    }
312
313    // Add all of the new equality sets to our list
314    invs.addAll(allNewInvs);
315
316    return weakenedInvs;
317  }
318
319  /**
320   * Dummy value that's incomparable to everything else to indicate missing in createEqualityInvs.
321   */
322  private static final Object dummyMissing = new Object();
323
324  /**
325   * Create a List of Equality invariants based on the values given by vt for the VarInfos in vis.
326   * Any variables that are out of bounds are forced into a separate equality set (since they no
327   * longer make sense and certainly shouldn't be equal to anything else)
328   *
329   * <p>Requires: vis.size() &gt; 0
330   *
331   * <p>Ensures: result.size() &gt; 0
332   *
333   * @param vis the VarInfos that were different from leader
334   * @param vt the ValueTuple associated with the VarInfos now
335   * @param leader the original leader of VarInfos
336   * @param count the number of samples seen (needed to set the number of samples for the new
337   *     Equality invariants)
338   * @return a List of Equality invariants bundling together same values from vis, and if needed,
339   *     another representing all the missing values
340   */
341  private List<Equality> createEqualityInvs(
342      List<VarInfo> vis, ValueTuple vt, Equality leader, int count) {
343    assert !vis.isEmpty();
344    HashMap<Object, List<VarInfo>> multiMap = new HashMap<>(); /* key is a value */
345    List<VarInfo> out_of_bounds = new ArrayList<>();
346    for (VarInfo vi : vis) {
347      if (vi.missingOutOfBounds()) {
348        out_of_bounds.add(vi);
349      } else if (vt.isMissing(vi)) {
350        addToBindingList(multiMap, dummyMissing, vi);
351      } else {
352        if (vi.getValue(vt) == null) {
353          System.out.printf(
354              "null value for variable %s, mod=%d at ppt %s%n",
355              vi.name(), vt.getModified(vi), parent.name());
356          VarInfo rv = parent.find_var_by_name("return");
357          assert rv != null : "@AssumeAssertion(nullness)";
358          System.out.println("return value = " + Debug.toString(rv.getValue(vt)));
359          System.out.println("At line number " + FileIO.get_linenum());
360        }
361        addToBindingList(multiMap, vi.getValue(vt), vi);
362      }
363    }
364    // Why use an array?  Because we'll be sorting shortly
365    /*NNC:@MonotonicNonNull*/ Equality[] resultArray =
366        new Equality[multiMap.values().size() + out_of_bounds.size()];
367    int resultCount = 0;
368    for (Map.Entry<@KeyFor("multiMap") Object, List<VarInfo>> entry : multiMap.entrySet()) {
369      Object key = entry.getKey();
370      List<VarInfo> list = entry.getValue();
371      assert !list.isEmpty();
372      Equality eq = new Equality(list, this);
373      @SuppressWarnings("interning") // special value
374      boolean isMissing = (key == dummyMissing);
375      if (isMissing) {
376        eq.setSamples(leader.numSamples() - count);
377      } else {
378        eq.setSamples(leader.numSamples());
379      }
380      if (debug.isLoggable(Level.FINE)) {
381        debug.fine("  created new inv: " + eq + " samples: " + eq.numSamples());
382      }
383      resultArray[resultCount] = eq;
384      resultCount++;
385    }
386    for (VarInfo oob : out_of_bounds) {
387      List<VarInfo> list = Collections.singletonList(oob);
388      resultArray[resultCount] = new Equality(list, this);
389      resultCount++;
390    }
391    resultArray = castNonNullDeep(resultArray); // https://tinyurl.com/cfissue/986
392
393    // Sort for determinism
394    Arrays.sort(resultArray, EqualityComparator.theInstance);
395    List<Equality> result = Arrays.<Equality>asList(resultArray);
396    assert !result.isEmpty();
397    return result;
398  }
399
400  /**
401   * Create a List of Equality invariants based on the VarInfos in vis. Assumes that the VarInfos in
402   * vis are not missing. The method is used exclusively for reversing optimizations in Daikon.
403   *
404   * <p>Requires: vis.size() &gt; 0
405   *
406   * <p>Ensures: result.size() &gt; 0
407   *
408   * @param vis the VarInfos that were different from leader
409   * @param leader the original leader of VarInfos
410   * @return a List of Equality invariants bundling together same values from vis
411   */
412  public List<Equality> createEqualityInvs(List<VarInfo> vis, Equality leader) {
413    assert !vis.isEmpty();
414
415    // Why use an array?  Because we'll be sorting shortly
416    /*NNC:@MonotonicNonNull*/ Equality[] resultArray = new Equality[vis.size()];
417    for (int i = 0; i < vis.size(); i++) {
418      VarInfo vi = vis.get(i);
419      List<VarInfo> list = new ArrayList<>();
420      list.add(vi);
421      Equality eq = new Equality(list, this);
422      eq.setSamples(leader.numSamples());
423      resultArray[i] = eq;
424    }
425    resultArray = castNonNullDeep(resultArray); // https://tinyurl.com/cfissue/986
426
427    // Sort for determinism
428    Arrays.sort(resultArray, PptSliceEquality.EqualityComparator.theInstance);
429    List<Equality> result = Arrays.<Equality>asList(resultArray);
430    assert !result.isEmpty();
431    return result;
432  }
433
434  /**
435   * Map maps keys to non-empty lists of elements. This method adds var to the list mapped by key,
436   * creating a new list for key if one doesn't already exist.
437   *
438   * <p>Requires: Each value in map is a list of size 1 or greater
439   *
440   * <p>Ensures: Each value in map is a list of size 1 or greater
441   *
442   * @param map the map to add the bindings to
443   * @param key if there is already a List associated with key, then add value to key. Otherwise
444   *     create a new List associated with key and insert value.
445   * @param value the value to insert into the List mapped to key
446   */
447  private <T> void addToBindingList(Map<T, List<VarInfo>> map, T key, VarInfo value) {
448    if (key == null) {
449      throw new IllegalArgumentException();
450    }
451    List<VarInfo> elements = map.computeIfAbsent(key, __ -> new ArrayList<VarInfo>());
452    elements.add(value);
453  }
454
455  /**
456   * Instantiate invariants from each inv's leader. This is like instantiate_invariants at the start
457   * of reading the trace file, where we create new PptSliceNs. This is called when newVis have just
458   * split off from leader, and we want the leaders of newVis to have the same invariants as leader.
459   *
460   * <p>post: Adds the newly instantiated invariants and slices to this.parent.
461   *
462   * @param leader the old leader
463   * @param newVis a List of new VarInfos that used to be equal to leader. Actually, it's the list
464   *     of canonical that were equal to leader, representing their own newly-created equality sets.
465   */
466  public List<Invariant> copyInvsFromLeader(VarInfo leader, List<VarInfo> newVis) {
467
468    List<Invariant> falsified_invs = new ArrayList<>();
469    List<PptSlice> newSlices = new ArrayList<>();
470    if (debug.isLoggable(Level.FINE)) {
471      debug.fine(
472          "copyInvsFromLeader: "
473              + parent.name()
474              + ": leader "
475              + leader.name()
476              + ": new leaders = "
477              + newVis);
478      debug.fine("  orig slices count:" + parent.numViews());
479    }
480
481    // Copy all possible combinations from the current ppt (with repetition)
482    // of replacing leader with different members of newVis.
483
484    // Loop through each slice.
485    // Uses old-style for loop and Iterator because it will be side-effected.
486    for (Iterator<PptSlice> i = parent.views_iterator(); i.hasNext(); ) {
487      PptSlice slice = i.next();
488
489      if (debug.isLoggable(Level.FINE)) {
490        debug.fine("  Slice is: " + slice.toString());
491        debug.fine("  With invs: " + slice.invs);
492      }
493
494      // If this slice contains the old leader
495      if (slice.containsVar(leader)) {
496
497        // Substitute new leader for old leader and create new slices/invs
498        VarInfo[] toFill = new VarInfo[slice.var_infos.length];
499        copyInvsFromLeaderHelper(leader, newVis, slice, newSlices, 0, -1, toFill);
500
501        // Remove any statically obvious invariants in the old slice.
502        // This is called here because breaking up the equality set may
503        // cause some invariants to become statically obvious (because
504        // they will now be the only item in their set)
505        for (Invariant inv : slice.invs) {
506          if (!Daikon.dkconfig_undo_opts) {
507            if (inv.isObviousStatically_AllInEquality()) {
508              inv.falsify();
509              falsified_invs.add(inv);
510            }
511          }
512        }
513        if (slice.invs.isEmpty()) {
514          i.remove();
515        }
516      }
517    }
518
519    // Add each new slice with invariants
520    for (PptSlice slice : newSlices) {
521      if (slice.invs.isEmpty()) {
522        continue;
523      }
524      assert (parent.findSlice(slice.var_infos) == null) : parent.findSlice(slice.var_infos);
525      slice.repCheck();
526      parent.addSlice(slice);
527    }
528
529    parent.repCheck();
530
531    if (debug.isLoggable(Level.FINE)) {
532      debug.fine("  new slices count:" + parent.numViews());
533    }
534    return falsified_invs;
535  }
536
537  /**
538   * Clones slice (zero or more times) such that instances of leader are replaced by members of
539   * newVis; places new slices in newSlices. The replacement is such that we get all combinations,
540   * with repetition of newVis and leader in every slot in slice where there used to be leader. For
541   * example, if slice contained (A1, A1, B) and A1 is leader and newVis contains A2 and A3, then
542   * the slices we produce would be: (A1, A2, B), (A1, A3, B), (A2, A2, B) (A2, A3, B), (A3, A3, B).
543   * We do not produce (A1, A1, B) because it is already there. We do not produce (A2, A1, B)
544   * because it is the same as (A1, A2, B) wrt combinations. This method does the main work of
545   * copyInvsFromLeader so that each new equality set that spawned off leader has the correct
546   * slices. It works as a nested series of for loops, whose depth is equal to the length of
547   * slice.var_infos. The position and loop arguments along with the call stack keep track of the
548   * loop nesting. When position reaches the end of slice.var_infos, this method attempts to
549   * instantiate the slice that has been produced. The standard start for position is 0, and for
550   * loop is -1.
551   *
552   * @param leader the variable to replace in slice
553   * @param newVis of VarInfos that will replace leader in combination in slice
554   * @param slice the slice to clone
555   * @param newSlices where to put the cloned slices
556   * @param position the position currently being replaced in source. Starts at 0.
557   * @param loop the iteration of the loop for this position. If -1, means the previous replacement
558   *     is leader.
559   * @param soFar buffer to which assignments temporarily go before becoming instantiated. Has to
560   *     equal slice.var_infos in length.
561   */
562  private void copyInvsFromLeaderHelper(
563      VarInfo leader,
564      List<VarInfo> newVis,
565      PptSlice slice,
566      List<PptSlice> newSlices,
567      int position,
568      int loop,
569      VarInfo[] soFar) {
570
571    // Track debug if any variables are in newVis
572    Debug dlog = null;
573    if (Debug.logOn()) {
574      dlog = new Debug(getClass(), parent, newVis);
575    }
576
577    if (position >= slice.var_infos.length) {
578      // Done with assigning positions and recursion
579      if (parent.findSlice_unordered(soFar) == null) {
580        // If slice is already there, no need to clone.
581
582        if (parent.is_slice_ok(soFar, slice.arity())) {
583          PptSlice newSlice = slice.cloneAndPivot(soFar);
584          // Debug.debugTrack.fine ("LeaderHelper: Created Slice " + newSlice);
585          if (Debug.logOn()) {
586            assert dlog != null : "@AssumeAssertion(nullness): dependent: set if Debug.logOn()";
587            dlog.log(
588                "Created slice " + newSlice + " Leader equality set = " + soFar[0].equalitySet);
589            Debug.log(getClass(), newSlice, "Created this slice");
590          }
591          List<Invariant> invs = newSlice.invs;
592          for (Iterator<Invariant> iInvs = invs.iterator(); iInvs.hasNext(); ) {
593            Invariant inv = iInvs.next();
594            if (!Daikon.dkconfig_undo_opts) {
595              if (inv.isObviousStatically_AllInEquality()) {
596                iInvs.remove();
597              }
598            }
599          }
600          if (newSlice.invs.isEmpty()) {
601            Debug.log(debug, getClass(), newSlice, soFar, "slice not added because 0 invs");
602          } else {
603            newSlices.add(newSlice);
604          }
605        }
606      } else {
607        if (Debug.logOn()) {
608          assert dlog != null : "@AssumeAssertion(nullness): dependent: set if Debug.logOn()";
609          dlog.log("Slice already existed " + parent.findSlice_unordered(soFar));
610        }
611      }
612      return;
613    } else {
614      // Not yet done with recursion, keep assigning to soFar
615      if (slice.var_infos[position] == leader) {
616        // If leader does need replacing
617        // newLoop starts at loop so that we don't have repeats
618        for (int newLoop = loop; newLoop < newVis.size(); newLoop++) {
619          VarInfo vi = newLoop == -1 ? leader : newVis.get(newLoop);
620          soFar[position] = vi;
621          // Advance position to next step, let next loop variable be
622          // this loop's counter.
623          copyInvsFromLeaderHelper(leader, newVis, slice, newSlices, position + 1, newLoop, soFar);
624        }
625      } else {
626        // Non leader position, just keep going after assigning soFar
627        soFar[position] = slice.var_infos[position];
628        copyInvsFromLeaderHelper(leader, newVis, slice, newSlices, position + 1, loop, soFar);
629      }
630    }
631  }
632
633  @Override
634  public void repCheck() {
635    for (Invariant inv : invs) {
636      inv.repCheck();
637      assert inv.ppt == this;
638    }
639  }
640
641  @SideEffectFree
642  @Override
643  public String toString(@GuardSatisfied PptSliceEquality this) {
644    StringBuilder result = new StringBuilder("PptSliceEquality: [");
645    for (Invariant inv : invs) {
646      result.append(inv.repr());
647      result.append(lineSep);
648    }
649    result.append("  ]");
650    return result.toString();
651  }
652
653  /** Order Equality invariants by the indices of leaders. */
654  public static final class EqualityComparator implements Comparator<Equality> {
655    public static final EqualityComparator theInstance = new EqualityComparator();
656
657    private EqualityComparator() {}
658
659    @Pure
660    @Override
661    public int compare(Equality eq1, Equality eq2) {
662      return VarInfo.IndexComparator.theInstance.compare(eq1.leader(), eq2.leader());
663    }
664  }
665
666  /** Returns an array of all of the leaders sorted by varinfo_index for this equality view. */
667  public VarInfo[] get_leaders_sorted() {
668    List<VarInfo> leaders = new ArrayList<>(invs.size());
669    for (Invariant inv : invs) {
670      VarInfo leader = ((Equality) inv).leader();
671      assert leader != null;
672      leaders.add(leader);
673    }
674    Collections.sort(leaders, VarInfo.IndexComparator.getInstance());
675    return leaders.toArray(new VarInfo[0]);
676  }
677}