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