001// ***** This file is automatically generated from OneOf.java.jpp
002
003package daikon.inv.unary.sequence;
004
005import daikon.*;
006import daikon.inv.*;
007import daikon.inv.unary.OneOf;
008
009  import daikon.inv.binary.twoSequence.SubSequence;
010  import daikon.inv.unary.scalar.*;
011
012import java.io.*;
013import java.util.logging.Logger;
014import java.util.logging.Level;
015import java.util.*;
016
017import org.checkerframework.checker.initialization.qual.Initialized;
018import org.checkerframework.checker.interning.qual.Interned;
019import org.checkerframework.checker.lock.qual.GuardSatisfied;
020import org.checkerframework.checker.nullness.qual.NonNull;
021import org.checkerframework.checker.nullness.qual.Nullable;
022import org.checkerframework.dataflow.qual.Pure;
023import org.checkerframework.dataflow.qual.SideEffectFree;
024import org.checkerframework.framework.qual.Unused;
025import org.plumelib.reflection.Signatures;
026import org.plumelib.util.ArraysPlume;
027import org.plumelib.util.Intern;
028import org.plumelib.util.StringsPlume;
029import org.plumelib.util.UtilPlume;
030import typequals.prototype.qual.NonPrototype;
031import typequals.prototype.qual.Prototype;
032
033// This subsumes an "exact" invariant that says the value is always exactly
034// a specific value.  Do I want to make that a separate invariant
035// nonetheless?  Probably not, as this will simplify implication and such.
036
037  /**
038   * Represents sequences of long values where the elements of the sequence take on only a
039   * few distinct values. Prints as either {@code x[] elements == c} (when there is only one value),
040   * or as {@code x[] elements one of {c1, c2, c3}} (when there are multiple values).
041
042   * May print as {@code x[] elements has only one value} when {@code x} is an array of hashcodes
043   * (pointers) -- this is because the numerical value of the hashcode (pointer) is uninteresting.
044
045   */
046
047@SuppressWarnings("UnnecessaryParentheses")  // generated code, parentheses are sometimes needed
048public final class EltOneOf extends SingleScalarSequence implements OneOf {
049  // We are Serializable, so we specify a version to allow changes to
050  // method signatures without breaking serialization.  If you add or
051  // remove fields, you should change this number to the current date.
052  static final long serialVersionUID = 20030822L;
053
054  /** Debugging logger. */
055  public static final Logger debug =
056    Logger.getLogger(EltOneOf.class.getName());
057
058  // Variables starting with dkconfig_ should only be set via the
059  // daikon.config.Configuration interface.
060  /** Boolean. True iff OneOf invariants should be considered. */
061  public static boolean dkconfig_enabled = Invariant.invariantEnabledDefault;
062
063  /**
064   * Positive integer. Specifies the maximum set size for this type of invariant (x is one of
065   * {@code size} items).
066   */
067
068  public static int dkconfig_size = 3;
069
070  /**
071   * Boolean. If true, invariants describing hashcode-typed variables as having any particular value
072   * will have an artificial value substituted for the exact hashhode values. The artificial values
073   * will stay the same from run to run even if the actual hashcode values change (as long as the
074   * OneOf invariants remain the same). If false, hashcodes will be formatted as the application of
075   * a hashcode uninterpreted function to an integer representing the bit pattern of the hashcode.
076   * One might wish to omit the exact values of the hashcodes because they are usually
077   * uninteresting; this is the same reason they print in the native Daikon format, for instance, as
078   * {@code var has only one value} rather than {@code var == 150924732}.
079   */
080  public static boolean dkconfig_omit_hashcode_values_Simplify = false;
081
082  // Probably needs to keep its own list of the values, and number of each seen.
083  // (That depends on the slice; maybe not until the slice is cleared out.
084  // But so few values is cheap, so this is quite fine for now and long-term.)
085
086  @Unused(when=Prototype.class)
087  private long[] elts;
088  @Unused(when=Prototype.class)
089  private int num_elts;
090
091  @Prototype EltOneOf() {
092    super();
093  }
094
095  EltOneOf(PptSlice slice) {
096    super(slice);
097
098    elts = new long[dkconfig_size];
099
100    num_elts = 0;
101
102    // var() is initialized by the super constructor
103    assert var().is_array() :
104      String.format("In %s constructor, var %s (type=%s, rep_type=%s) should be an array",
105                     "EltOneOf", var().name(), var().type, var().rep_type);
106
107  }
108
109  private static @Prototype EltOneOf proto = new @Prototype EltOneOf();
110
111  /** Returns the prototype invariant for EltOneOf */
112  public static @Prototype EltOneOf get_proto() {
113    return proto;
114  }
115
116  /** returns whether or not this invariant is enabled */
117  @Override
118  public boolean enabled() {
119    return dkconfig_enabled;
120  }
121
122  /** instantiate an invariant on the specified slice */
123  @Override
124  public EltOneOf instantiate_dyn(@Prototype EltOneOf this, PptSlice slice) {
125    return new EltOneOf(slice);
126  }
127
128  @Pure
129  public boolean is_boolean(@GuardSatisfied EltOneOf this) {
130    return var().file_rep_type.elementType() == ProglangType.BOOLEAN;
131  }
132  @Pure
133  public boolean is_hashcode(@GuardSatisfied EltOneOf this) {
134    return var().file_rep_type.elementType() == ProglangType.HASHCODE;
135  }
136
137  @SideEffectFree
138  @Override
139  public EltOneOf clone(@GuardSatisfied EltOneOf this) {
140    EltOneOf result = (EltOneOf) super.clone();
141    result.elts = elts.clone();
142
143    result.num_elts = this.num_elts;
144    return result;
145  }
146
147  @Override
148  public int num_elts() {
149    return num_elts;
150  }
151
152  @Override
153  public Object elt() {
154    return elt(0);
155  }
156
157  public Object elt(int index) {
158    if (num_elts <= index) {
159      throw new Error("Represents " + num_elts + " elements, index " + index + " not valid");
160    }
161
162    // Not sure whether interning is necessary (or just returning an Integer
163    // would be sufficient), but just in case...
164    return Intern.internedLong(elts[index]);
165  }
166
167  private void sort_rep(@GuardSatisfied EltOneOf this) {
168    Arrays.sort(elts, 0, num_elts );
169  }
170
171  public long min_elt() {
172    if (num_elts == 0) {
173      throw new Error("Represents no elements");
174    }
175    sort_rep();
176    return elts[0];
177  }
178
179  public long max_elt() {
180    if (num_elts == 0) {
181      throw new Error("Represents no elements");
182    }
183    sort_rep();
184    return elts[num_elts - 1];
185  }
186
187  // Assumes the other array is already sorted
188  public boolean compare_rep(int num_other_elts, long[] other_elts) {
189    if (num_elts != num_other_elts) {
190      return false;
191    }
192    sort_rep();
193    for (int i = 0; i < num_elts; i++) {
194      if (!((elts[i]) == (other_elts[i]))) { // elements are interned
195        return false;
196      }
197    }
198    return true;
199  }
200
201  private String subarray_rep(@GuardSatisfied EltOneOf this) {
202    // Not so efficient an implementation, but simple;
203    // and how often will we need to print this anyway?
204    sort_rep();
205    StringBuilder sb = new StringBuilder();
206    sb.append("{ ");
207    for (int i = 0; i < num_elts; i++) {
208      if (i != 0) {
209        sb.append(", ");
210      }
211
212      if (PrintInvariants.dkconfig_static_const_infer) {
213        boolean curVarMatch = false;
214        PptTopLevel pptt = ppt.parent;
215        for (VarInfo vi : pptt.var_infos) {
216          if (vi.isStaticConstant() && VarComparability.comparable(vi, var())) {
217            Object constantVal = vi.constantValue();
218            if (constantVal.equals(elts[i])) {
219              sb.append(vi.name());
220              curVarMatch = true;
221            }
222          }
223        }
224
225        if (curVarMatch == false) {
226          sb.append((((elts[i] == 0) && (var().file_rep_type == ProglangType.HASHCODE_ARRAY)) ? "null" : ((Integer.MIN_VALUE <= elts[i] && elts[i] <= Integer.MAX_VALUE) ? String.valueOf(elts[i]) : (String.valueOf(elts[i]) + "L"))));
227        }
228      } else {
229        sb.append((((elts[i] == 0) && (var().file_rep_type == ProglangType.HASHCODE_ARRAY)) ? "null" : ((Integer.MIN_VALUE <= elts[i] && elts[i] <= Integer.MAX_VALUE) ? String.valueOf(elts[i]) : (String.valueOf(elts[i]) + "L"))));
230      }
231
232    }
233    sb.append(" }");
234    return sb.toString();
235  }
236
237  @Override
238  public String repr(@GuardSatisfied EltOneOf this) {
239    return "EltOneOf" + varNames() + ": falsified=" + falsified
240      + ", num_elts=" + num_elts
241      + ", elts=" + subarray_rep();
242  }
243
244  @SideEffectFree
245  @Override
246  public String format_using(@GuardSatisfied EltOneOf this, OutputFormat format) {
247    sort_rep();
248
249    if (format.isJavaFamily()) {
250      return format_java_family(format);
251    }
252
253    if (format == OutputFormat.DAIKON) {
254      return format_daikon();
255    } else if (format == OutputFormat.SIMPLIFY) {
256      return format_simplify();
257    } else if (format == OutputFormat.ESCJAVA) {
258      String result = format_esc();
259      return result;
260    } else if (format == OutputFormat.CSHARPCONTRACT) {
261      return format_csharp_contract();
262    } else {
263      return format_unimplemented(format);
264    }
265  }
266
267  public String format_daikon(@GuardSatisfied EltOneOf this) {
268    String varname = var().name() + " elements";
269    if (num_elts == 1) {
270
271        if (is_boolean()) {
272          if ((elts[0] != 0) && (elts[0] != 1)) {
273              System.out.println("WARNING:: Variable "
274              + varname + " is of type boolean, but has non boolean value: "
275              + elts[0]);
276          }
277          return varname + " == " + ((elts[0] == 0) ? "false" : "true");
278        } else if (is_hashcode()) {
279          if (elts[0] == 0) {
280            return varname + " == null";
281          } else {
282            return varname + " has only one value"
283              // + " (hashcode=" + elts[0] + ")"
284              ;
285          }
286        } else {
287          if (PrintInvariants.dkconfig_static_const_infer) {
288            PptTopLevel pptt = ppt.parent;
289            for (VarInfo vi : pptt.var_infos) {
290              if (vi.isStaticConstant() && VarComparability.comparable(vi, var())) {
291                Object constantVal = vi.constantValue();
292                if (constantVal.equals(elts[0])) {
293                  return varname + " == " + vi.name();
294                }
295              }
296            }
297          }
298          return varname + " == " + (((elts[0] == 0) && (var().file_rep_type == ProglangType.HASHCODE_ARRAY)) ? "null" : ((Integer.MIN_VALUE <= elts[0] && elts[0] <= Integer.MAX_VALUE) ? String.valueOf(elts[0]) : (String.valueOf(elts[0]) + "L")));
299        }
300
301    } else {
302      return varname + " one of " + subarray_rep();
303    }
304  }
305
306  public String format_esc(@GuardSatisfied EltOneOf this) {
307    sort_rep();
308
309    String[] form = VarInfo.esc_quantify(var());
310    String varname = form[1];
311
312    String result;
313
314    if (is_boolean()) {
315      assert num_elts == 1;
316      assert (elts[0] == 0) || (elts[0] == 1);
317      result = varname + " == " + ((elts[0] == 0) ? "false" : "true");
318    } else if (is_hashcode()) {
319      if (num_elts == 1) {
320        if (elts[0] == 0) {
321          result = varname + " == null";
322        } else {
323          // This seems wrong, because there is already a "!= null" invariant.
324          // This invariant (EltOneOf) just shouldn't print for ESC format.
325          result = varname + " != null";
326          // varname + " has only one value"
327          // + " (hashcode=" + elts[0] + ")"
328        }
329      } else if (num_elts == 2) {
330        // add_modified allows two elements iff one is null
331        assert elts[0] == 0;
332        assert elts[1] != 0;
333        return format_unimplemented(OutputFormat.ESCJAVA); // "needs to be implemented"
334      } else if (num_elts == 0) {
335        // Do nothing
336        return format_unimplemented(OutputFormat.ESCJAVA); // "needs to be implemented"
337      } else {
338        throw new Error("Contains more than 2 elements");
339      }
340    } else {
341      result = "";
342      for (int i = 0; i < num_elts; i++) {
343        if (i != 0) { result += " || "; }
344        result += varname + " == " + (((elts[i] == 0) && (var().file_rep_type == ProglangType.HASHCODE_ARRAY)) ? "null" : ((Integer.MIN_VALUE <= elts[i] && elts[i] <= Integer.MAX_VALUE) ? String.valueOf(elts[i]) : (String.valueOf(elts[i]) + "L")));
345      }
346    }
347
348    result = form[0] + "(" + result + ")" + form[2];
349
350    return result;
351  }
352
353public String format_csharp_contract(@GuardSatisfied EltOneOf this) {
354
355    @NonNull @Initialized String result;
356
357    String equalsString = " == ";
358    String endString = "";
359
360    String[] split = var().csharp_array_split();
361    List<String> args_list = new ArrayList<>();
362    // Construct the array that unary value will be compared against.
363    for (int i = 0; i < num_elts; i++) {
364
365      args_list.add((((elts[i] == 0) && (var().file_rep_type == ProglangType.HASHCODE_ARRAY)) ? "null" : ((Integer.MIN_VALUE <= elts[i] && elts[i] <= Integer.MAX_VALUE) ? String.valueOf(elts[i]) : (String.valueOf(elts[i]) + "L"))));
366
367    }
368    String args = String.join(", ", args_list);
369
370    if (num_elts == 0) { // If there are no elements, length must be 0.
371      String varname = var().csharp_name();
372      return varname + ".Count() == 0";
373    } else if (num_elts == 1) {
374
375      if (is_boolean()) {
376        String bool_str = (elts[0] == 0) ? "false" : "true";
377        result = "Contract.ForAll(" + split[0] + ", x => x" + split[1] + " == " + bool_str + ")";
378      } else if (is_hashcode()) {
379        String null_str = elts[0] == 0 ? " == null" : " != null"; // interned
380        result = "Contract.ForAll(" + split[0] + ", x => x" + split[1] + null_str + ")";
381      } else {
382        result = "Contract.ForAll(" + split[0] + ", x => x" + split[1] + equalsString + args + endString + ")";
383      }
384    } else {
385      assert num_elts > 1;
386      result = "Contract.ForAll(" + split[0] + ", x => x" + split[1] + ".OneOf(" + args + "))";
387    }
388
389    return result;
390  }
391
392  public String format_java_family(@GuardSatisfied EltOneOf this, OutputFormat format) {
393
394    String result;
395
396    // Setting up the name of the unary variable
397    String varname = var().name_using(format);
398
399    // Constructing the array that unary val will be compared against
400
401    String oneOfArray = "new long[] { ";
402
403    for (int i = 0 ; i < num_elts ; i++) {
404      if (i != 0) { oneOfArray += ", "; }
405      oneOfArray = oneOfArray + (((elts[i] == 0) && (var().file_rep_type == ProglangType.HASHCODE_ARRAY)) ? "null" : ((Integer.MIN_VALUE <= elts[i] && elts[i] <= Integer.MAX_VALUE) ? String.valueOf(elts[i]) : (String.valueOf(elts[i]) + "L")));
406    }
407    oneOfArray += " }";
408
409    // Calling quantification method
410    if (num_elts == 1) {
411
412      if (is_boolean()) {
413        result = "daikon.Quant.eltsEqual(" + varname + ", "
414          + ((elts[0] == 0) ? "false" : "true") + ")";
415      } else if (is_hashcode()) {
416        result =
417          ((elts[0] == 0)
418           ? ("daikon.Quant.eltsEqual(" + varname + ", null)")
419           : ("daikon.Quant.eltsNotEqual(" + varname + ", null)"));
420      } else {
421          result = "daikon.Quant.eltsEqual(" + varname + ", "
422            + (((elts[0] == 0) && (var().file_rep_type == ProglangType.HASHCODE_ARRAY)) ? "null" : ((Integer.MIN_VALUE <= elts[0] && elts[0] <= Integer.MAX_VALUE) ? String.valueOf(elts[0]) : (String.valueOf(elts[0]) + "L"))) + ")";
423        }
424    } else {
425      assert num_elts > 1;
426      // eltsOneOf == subsetOf
427      result = "daikon.Quant.subsetOf(" + varname + ", " + oneOfArray + ")";
428    }
429
430    return result;
431  }
432
433  public String format_simplify(@GuardSatisfied EltOneOf this) {
434
435    // if (is_hashcode() && dkconfig_omit_hashcode_values_Simplify)
436    //   return "(AND)";
437
438    sort_rep();
439
440    String[] form = VarInfo.simplify_quantify(var());
441    String varname = form[1];
442
443    String result;
444
445    if (is_boolean()) {
446      assert num_elts == 1;
447      assert (elts[0] == 0) || (elts[0] == 1);
448      result = "(EQ " + varname + " " + ((elts[0] == 0) ? "|@false|" : "|@true|") + ")";
449    } else if (is_hashcode()) {
450      if (num_elts == 1) {
451        long hashcode_val = get_hashcode_val(elts[0]);
452        result = "(EQ " + varname + " "
453          + ((elts[0] == 0) ? "null" :
454           ("(hashcode " + simplify_format_long(hashcode_val) + ")")) + ")";
455      } else if (num_elts == 2) {
456        // add_modified allows two elements iff one is null
457        assert elts[0] == 0;
458        assert elts[1] != 0;
459        long hashcode_val = get_hashcode_val(elts[1]);
460        result = "(OR (EQ " + varname + " null) (EQ " + varname
461          + "(hashcode " + simplify_format_long(hashcode_val) + ")))";
462      } else if (num_elts == 0) {
463        return format_too_few_samples(OutputFormat.SIMPLIFY, null);
464      } else {
465        throw new Error("Contains more than 2 elements");
466      }
467    } else {
468      result = "";
469      for (int i = 0; i < num_elts; i++) {
470        result += " (EQ " + varname + " " + (((elts[i] == 0) && (var().file_rep_type == ProglangType.HASHCODE_ARRAY)) ? "null" : simplify_format_long(elts[i])) + ")";
471      }
472      if (num_elts > 1) {
473        result = "(OR" + result + ")";
474      } else if (num_elts == 1) {
475        // chop leading space
476        result = result.substring(1);
477      } else {
478        // Haven't actually seen any data, so we're vacuously true
479        return format_too_few_samples(OutputFormat.SIMPLIFY, null);
480      }
481    }
482
483    result = form[0] + result + form[2];
484
485    if (result.indexOf("format_simplify") == -1) {
486      daikon.simplify.SimpUtil.assert_well_formed(result);
487    }
488    return result;
489  }
490
491  @Override
492  public InvariantStatus add_modified(long[] a, int count) {
493    return runValue(a, count, true);
494  }
495
496  @Override
497  public InvariantStatus check_modified(long[] a, int count) {
498    return runValue(a, count, false);
499  }
500
501  private InvariantStatus runValue(long[] a, int count, boolean mutate) {
502    InvariantStatus finalStatus = InvariantStatus.NO_CHANGE;
503    for (int ai = 0; ai <a.length; ai++) {
504      InvariantStatus status;
505      if (mutate) {
506        status = add_mod_elem(a[ai], count);
507      } else {
508        status = check_mod_elem(a[ai], count);
509      }
510      if (status == InvariantStatus.FALSIFIED) {
511        return InvariantStatus.FALSIFIED;
512      } else if (status == InvariantStatus.WEAKENED) {
513        finalStatus = InvariantStatus.WEAKENED;
514      }
515    }
516    return finalStatus;
517  }
518
519  /**
520   * Adds a single sample to the invariant. Returns
521   * the appropriate InvariantStatus from the result
522   * of adding the sample to this.
523   */
524  public InvariantStatus add_mod_elem(long v, int count) {
525    InvariantStatus status = check_mod_elem(v, count);
526    if (status == InvariantStatus.WEAKENED) {
527      elts[num_elts] = v;
528      num_elts++;
529    }
530    return status;
531  }
532
533  /**
534   * Checks a single sample to the invariant. Returns
535   * the appropriate InvariantStatus from the result
536   * of adding the sample to this.
537   */
538  public InvariantStatus check_mod_elem(long v, int count) {
539
540    // Look for v in our list of previously seen values.  If it's
541    // found, we're all set.
542    for (int i = 0; i < num_elts; i++) {
543      // if (logDetail())
544      //  log ("add_modified (" + v + ")");
545      if (((elts[i]) == ( v))) {
546        return InvariantStatus.NO_CHANGE;
547      }
548    }
549
550    if (num_elts == dkconfig_size) {
551      return InvariantStatus.FALSIFIED;
552    }
553
554    if ((is_boolean() && (num_elts == 1)) || (is_hashcode() && (num_elts == 2))) {
555      return InvariantStatus.FALSIFIED;
556    }
557
558    if (is_hashcode() && (num_elts == 1)) {
559      // Permit two object values only if one of them is null
560      if ((elts[0] != 0) && (v != 0)) {
561        return InvariantStatus.FALSIFIED;
562      }
563    }
564
565    return InvariantStatus.WEAKENED;
566  }
567
568  // It is possible to have seen many (array) samples, but no (long)
569  // array element values.
570  @Override
571  public boolean enoughSamples(@GuardSatisfied EltOneOf this) {
572    return num_elts > 0;
573  }
574
575  @Override
576  protected double computeConfidence() {
577    // This is not ideal.
578    if (num_elts == 0) {
579      return Invariant.CONFIDENCE_UNJUSTIFIED;
580    } else if (is_hashcode() && (num_elts > 1)) {
581      // This should never happen
582      return Invariant.CONFIDENCE_UNJUSTIFIED;
583    } else {
584      return Invariant.CONFIDENCE_JUSTIFIED;
585    }
586  }
587
588  @Pure
589  @Override
590  public @Nullable DiscardInfo isObviousStatically(VarInfo[] vis) {
591    // Static constants are necessarily OneOf precisely one value.
592    // This removes static constants from the output, which might not be
593    // desirable if the user doesn't know their actual value.
594    if (vis[0].isStaticConstant()) {
595      assert num_elts <= 1;
596      return new DiscardInfo(this, DiscardCode.obvious, vis[0].name() + " is a static constant.");
597    }
598    return super.isObviousStatically(vis);
599  }
600
601  @Pure
602  @Override
603  public @Nullable DiscardInfo isObviousDynamically(VarInfo[] vis) {
604    DiscardInfo super_result = super.isObviousDynamically(vis);
605    if (super_result != null) {
606      return super_result;
607    }
608
609    VarInfo v = vis[0];
610
611    // We can check if all values in the list match with the ones we know about
612    // (useful for booleans and numeric enumerations).
613    if (v.aux.hasValue(VarInfoAux.VALID_VALUES)) {
614      String[] vsValidValues         = v.aux.getList(VarInfoAux.VALID_VALUES);
615      Set<Long> setValidValues = new TreeSet<>();
616      for (String s : vsValidValues) {
617        setValidValues.add(Long.valueOf(s));
618      }
619      Set<Long> setValuesInvariant = new TreeSet<>();
620      for (long e : elts) {
621        setValuesInvariant.add(e);
622      }
623      if (setValidValues.equals(setValuesInvariant)) {
624        return new DiscardInfo(this, DiscardCode.obvious,
625          "The value list matches the allowed value list");
626      }
627    }
628
629    // Look for the same property over a supersequence of this one.
630    PptTopLevel pptt = ppt.parent;
631    for (Iterator<Invariant> inv_itor = pptt.invariants_iterator(); inv_itor.hasNext(); ) {
632      Invariant inv = inv_itor.next();
633      if (inv == this) {
634        continue;
635      }
636      if (inv instanceof EltOneOf) {
637        EltOneOf other = (EltOneOf) inv;
638        if (isSameFormula(other)
639          && SubSequence.isObviousSubSequenceDynamically(this, v, other.var())) {
640          debug.fine("isObviousDyn: Returning true because isObviousSubSequenceDynamically");
641          return new DiscardInfo(this, DiscardCode.obvious, "The same property holds over a supersequence " + other.var().name());
642        }
643      }
644    }
645
646    return null;
647  }
648
649  /**
650   * Oneof can merge different formulas from lower points to create a single formula at an upper
651   * point.
652   */
653  @Override
654  public boolean mergeFormulasOk() {
655    return true;
656  }
657
658  @Pure
659  @Override
660  public boolean isSameFormula(Invariant o) {
661    EltOneOf other = (EltOneOf) o;
662    if (num_elts != other.num_elts) {
663      return false;
664    }
665    if (num_elts == 0 && other.num_elts == 0) {
666      return true;
667    }
668
669    sort_rep();
670    other.sort_rep();
671
672    // All nonzero hashcode values should be considered equal to each other
673    //
674    // Examples:
675    // inv1  inv2  result
676    // ----  ----  ------
677    // 19    0     false
678    // 19    22    true
679    // 0     0     true
680
681    if (is_hashcode() && other.is_hashcode()) {
682      if (num_elts == 1 && other.num_elts == 1) {
683        return ((elts[0] == 0 && other.elts[0] == 0)
684                || (elts[0] != 0 && other.elts[0] != 0));
685      } else if (num_elts == 2 && other.num_elts == 2) {
686        // add_modified allows two elements iff one is null
687        assert elts[0] == 0;
688        assert other.elts[0] == 0;
689        assert elts[1] != 0;
690        assert other.elts[1] != 0;
691
692        // Since we know the first elements of each invariant are
693        // zero, and the second elements are nonzero, we can immediately
694        // return true
695        return true;
696      } else {
697        return false;
698      }
699    }
700
701    for (int i = 0; i < num_elts; i++) {
702      if (!((elts[i]) == (other.elts[i]))) {
703        return false;
704      }
705    }
706
707    return true;
708  }
709
710  @Pure
711  @Override
712  public boolean isExclusiveFormula(Invariant o) {
713    if (o instanceof EltOneOf) {
714      EltOneOf other = (EltOneOf) o;
715
716      if (num_elts == 0 || other.num_elts == 0) {
717        return false;
718      }
719      for (int i = 0; i < num_elts; i++) {
720        for (int j = 0; j < other.num_elts; j++) {
721          if (((elts[i]) == (other.elts[j]))) // elements are interned
722            return false;
723        }
724      }
725
726      // Don't consider two instances of "non-null" as exclusive.
727      if (is_hashcode() && num_elts == 1
728          && other.is_hashcode() && other.num_elts == 1
729          && elts[0] != 0 && other.elts[0] != 0) {
730        return false;
731      }
732
733      // Be even more aggressive about rejecting these for use in
734      // implications in this case, since, we'd be printing them as
735      // "true"
736      /*
737      if (dkconfig_omit_hashcode_values_Simplify
738          && (is_hashcode() || other.is_hashcode())) {
739        return false;
740      }
741      */
742
743      return true;
744    }
745
746    // Many more checks can be added here:  against nonzero, modulus, etc.
747    if ((o instanceof EltNonZero) && (num_elts == 1) && (elts[0] == 0)) {
748      return true;
749    }
750    long elts_min = Long.MAX_VALUE;
751    long elts_max = Long.MIN_VALUE;
752    for (int i = 0; i < num_elts; i++) {
753      elts_min = Math.min(elts_min, elts[i]);
754      elts_max = Math.max(elts_max, elts[i]);
755    }
756    if ((o instanceof LowerBound) && (elts_max < ((LowerBound)o).min())) {
757      return true;
758    }
759    if ((o instanceof UpperBound) && (elts_min > ((UpperBound)o).max())) {
760      return true;
761    }
762
763    return false;
764  }
765
766  @Override
767  public boolean hasUninterestingConstant() {
768
769    for (int i = 0; i < num_elts; i++) {
770      if (elts[i] < -1 || elts[i] > 2) {
771        return true;
772      }
773    }
774
775    return false;
776  }
777
778  @Pure
779  @Override
780  public boolean isExact() {
781    return num_elts == 1;
782  }
783
784  // Look up a previously instantiated invariant.
785  public static @Nullable EltOneOf find(PptSlice ppt) {
786    assert ppt.arity() == 1;
787    for (Invariant inv : ppt.invs) {
788      if (inv instanceof EltOneOf) {
789        return (EltOneOf) inv;
790      }
791    }
792    return null;
793  }
794
795  // Interning is lost when an object is serialized and deserialized.
796  // Manually re-intern any interned fields upon deserialization.
797  private void readObject(ObjectInputStream in) throws IOException,
798    ClassNotFoundException {
799    in.defaultReadObject();
800
801    for (int i = 0; i < num_elts; i++) {
802      elts[i] = Intern.intern(elts[i]);
803    }
804  }
805
806  /**
807   * Merge the invariants in invs to form a new invariant. Each must be a EltOneOf invariant.
808   * This code finds all of the oneof values from each of the invariants and returns the merged
809   * invariant (if any).
810   *
811   * @param invs list of invariants to merge. The invariants must all be of the same type and should
812   *     come from the children of parent_ppt. They should also all be permuted to match the
813   *     variable order in parent_ppt.
814   * @param parent_ppt slice that will contain the new invariant
815   */
816  @Override
817  public @Nullable Invariant merge(List<Invariant> invs, PptSlice parent_ppt) {
818
819    // Create the initial parent invariant from the first child
820    EltOneOf  first = (EltOneOf) invs.get(0);
821    EltOneOf result = first.clone();
822    result.ppt = parent_ppt;
823
824    // Loop through the rest of the child invariants
825    for (int i = 1; i < invs.size(); i++ ) {
826
827      // Get this invariant
828      EltOneOf inv = (EltOneOf) invs.get(i);
829
830      // Loop through each distinct value found in this child and add
831      // it to the parent.  If the invariant is falsified, there is no parent
832      // invariant
833      for (int j = 0; j < inv.num_elts; j++) {
834        long val = inv.elts[j];
835
836        InvariantStatus status = result.add_mod_elem(val, 1);
837        if (status == InvariantStatus.FALSIFIED) {
838
839          result.log("%s", "child value '" + val + "' destroyed oneof");
840
841          return null;
842        }
843      }
844    }
845
846    result.log("Merged '%s' from %s child invariants", result.format(), invs.size());
847    return result;
848  }
849
850  /**
851   * Setup the invariant with the specified elements. Normally used when searching for a specified
852   * OneOf. The elements of vals are not necessarily interned; this method interns each element.
853   */
854  public void set_one_of_val(long[] vals) {
855
856    num_elts = vals.length;
857    for (int i = 0; i < num_elts; i++) {
858      elts[i] = Intern.intern(vals[i]);
859    }
860  }
861
862  /**
863   * Returns true if every element in this invariant is contained in the specified state. For
864   * example if x = 1 and the state contains 1 and 2, true will be returned.
865   */
866  @Override
867  public boolean state_match(Object state) {
868
869    if (num_elts == 0) {
870      return false;
871    }
872
873    if (!(state instanceof long[])) {
874      // Daikon is about to crash.  Produce some debugging output.
875      System.out.printf("state = %s [%s]%n", state, state.getClass());
876      System.out.printf("problem with %s%n", this);
877    }
878    long[] e = (long[]) state;
879    for (int i = 0; i < num_elts; i++) {
880      boolean match = false;
881      for (int j = 0; j < e.length; j++) {
882        if (elts[i] == e[j]) {
883          match = true;
884          break;
885        }
886      }
887      if (!match) {
888        return false;
889      }
890    }
891    return true;
892  }
893
894  /**
895   * Map that holds dummy hashcode values for hashcodes. Each hashcode seen is assigned a small
896   * integer in the order they are seen. These values will be consistent as long as new hashcodes do
897   * not appear in the output. Not a perfect fix for regressions consistency, but workable.
898   */
899  private static Map<Long,Long> dummy_hashcode_vals = new LinkedHashMap<>();
900  private static long next_dummy_hashcode = 1001;
901
902  private long get_hashcode_val(@GuardSatisfied EltOneOf this, long hashcode) {
903    if (!dkconfig_omit_hashcode_values_Simplify) {
904      return hashcode;
905    }
906
907    Long val = dummy_hashcode_vals.get(hashcode);
908    if (val != null) {
909      return val;
910    }
911    dummy_hashcode_vals.put(hashcode, next_dummy_hashcode);
912    return next_dummy_hashcode++;
913  }
914
915}