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
009import java.io.*;
010import java.util.logging.Logger;
011import java.util.logging.Level;
012import java.util.*;
013
014import org.checkerframework.checker.initialization.qual.Initialized;
015import org.checkerframework.checker.interning.qual.Interned;
016import org.checkerframework.checker.lock.qual.GuardSatisfied;
017import org.checkerframework.checker.nullness.qual.NonNull;
018import org.checkerframework.checker.nullness.qual.Nullable;
019import org.checkerframework.dataflow.qual.Pure;
020import org.checkerframework.dataflow.qual.SideEffectFree;
021import org.checkerframework.framework.qual.Unused;
022import org.plumelib.reflection.Signatures;
023import org.plumelib.util.ArraysPlume;
024import org.plumelib.util.Intern;
025import org.plumelib.util.StringsPlume;
026import org.plumelib.util.UtilPlume;
027import typequals.prototype.qual.NonPrototype;
028import typequals.prototype.qual.Prototype;
029
030// This subsumes an "exact" invariant that says the value is always exactly
031// a specific value.  Do I want to make that a separate invariant
032// nonetheless?  Probably not, as this will simplify implication and such.
033
034  /**
035   * Represents double[] variables that take on only a few distinct values. Prints as either
036   * {@code x == c} (when there is only one value) or as {@code x one of {c1, c2, c3}}
037   * (when there are multiple values).
038
039   */
040
041@SuppressWarnings("UnnecessaryParentheses")  // generated code, parentheses are sometimes needed
042public final class OneOfFloatSequence extends SingleFloatSequence implements OneOf {
043  // We are Serializable, so we specify a version to allow changes to
044  // method signatures without breaking serialization.  If you add or
045  // remove fields, you should change this number to the current date.
046  static final long serialVersionUID = 20030822L;
047
048  /** Debugging logger. */
049  public static final Logger debug =
050    Logger.getLogger(OneOfFloatSequence.class.getName());
051
052  // Variables starting with dkconfig_ should only be set via the
053  // daikon.config.Configuration interface.
054  /** Boolean. True iff OneOf invariants should be considered. */
055  public static boolean dkconfig_enabled = Invariant.invariantEnabledDefault;
056
057  /**
058   * Positive integer. Specifies the maximum set size for this type of invariant (x is one of
059   * {@code size} items).
060   */
061
062  public static int dkconfig_size = 3;
063
064  // Probably needs to keep its own list of the values, and number of each seen.
065  // (That depends on the slice; maybe not until the slice is cleared out.
066  // But so few values is cheap, so this is quite fine for now and long-term.)
067
068  @Unused(when=Prototype.class)
069  private double[] @Interned [] elts;
070  @Unused(when=Prototype.class)
071  private int num_elts;
072
073  @Prototype OneOfFloatSequence() {
074    super();
075  }
076
077  OneOfFloatSequence(PptSlice slice) {
078    super(slice);
079
080    // Elements are interned, so can test with == (except that NaN != NaN)
081    // (in the general online case, it's not worth interning).
082    elts = new double[dkconfig_size] @Interned [];
083
084    num_elts = 0;
085
086    // var() is initialized by the super constructor
087    assert var().is_array() :
088      String.format("In %s constructor, var %s (type=%s, rep_type=%s) should be an array",
089                     "OneOfSequenceFloat", var().name(), var().type, var().rep_type);
090
091  }
092
093  private static @Prototype OneOfFloatSequence proto = new @Prototype OneOfFloatSequence();
094
095  /** Returns the prototype invariant for OneOfFloatSequence */
096  public static @Prototype OneOfFloatSequence get_proto() {
097    return proto;
098  }
099
100  /** returns whether or not this invariant is enabled */
101  @Override
102  public boolean enabled() {
103    return dkconfig_enabled;
104  }
105
106  /** instantiate an invariant on the specified slice */
107  @Override
108  public OneOfFloatSequence instantiate_dyn(@Prototype OneOfFloatSequence this, PptSlice slice) {
109    return new OneOfFloatSequence(slice);
110  }
111
112  @Pure
113  public boolean is_boolean(@GuardSatisfied OneOfFloatSequence this) {
114    return var().file_rep_type.elementType() == ProglangType.BOOLEAN;
115  }
116  @Pure
117  public boolean is_hashcode(@GuardSatisfied OneOfFloatSequence this) {
118    return var().file_rep_type.elementType() == ProglangType.HASHCODE;
119  }
120
121  @SideEffectFree
122  @Override
123  public OneOfFloatSequence clone(@GuardSatisfied OneOfFloatSequence this) {
124    OneOfFloatSequence result = (OneOfFloatSequence) super.clone();
125    result.elts = elts.clone();
126
127    for (int i = 0; i < num_elts; i++) {
128      result.elts[i] = Intern.intern(elts[i].clone());
129    }
130
131    result.num_elts = this.num_elts;
132    return result;
133  }
134
135  @Override
136  public int num_elts() {
137    return num_elts;
138  }
139
140  @Override
141  public Object elt() {
142    return elt(0);
143  }
144
145  public Object elt(int index) {
146    if (num_elts <= index) {
147      throw new Error("Represents " + num_elts + " elements, index " + index + " not valid");
148    }
149
150    return elts[index];
151  }
152
153  static Comparator<double[]> comparator = ArraysPlume.DoubleArrayComparatorLexical.it;
154
155  private void sort_rep(@GuardSatisfied OneOfFloatSequence this) {
156    Arrays.sort(elts, 0, num_elts , comparator);
157  }
158
159  public double @Interned [] min_elt() {
160    if (num_elts == 0) {
161      throw new Error("Represents no elements");
162    }
163    sort_rep();
164    return elts[0];
165  }
166
167  public double @Interned [] max_elt() {
168    if (num_elts == 0) {
169      throw new Error("Represents no elements");
170    }
171    sort_rep();
172    return elts[num_elts - 1];
173  }
174
175  // Assumes the other array is already sorted
176  public boolean compare_rep(int num_other_elts, double[] @Interned [] other_elts) {
177    if (num_elts != num_other_elts) {
178      return false;
179    }
180    sort_rep();
181    for (int i = 0; i < num_elts; i++) {
182      if (!((elts[i]) == (other_elts[i]))) { // elements are interned
183        return false;
184      }
185    }
186    return true;
187  }
188
189  private String subarray_rep(@GuardSatisfied OneOfFloatSequence this) {
190    // Not so efficient an implementation, but simple;
191    // and how often will we need to print this anyway?
192    sort_rep();
193    StringBuilder sb = new StringBuilder();
194    sb.append("{ ");
195    for (int i = 0; i < num_elts; i++) {
196      if (i != 0) {
197        sb.append(", ");
198      }
199
200      sb.append(Arrays.toString(elts[i]));
201
202    }
203    sb.append(" }");
204    return sb.toString();
205  }
206
207  @Override
208  public String repr(@GuardSatisfied OneOfFloatSequence this) {
209    return "OneOfSequenceFloat" + varNames() + ": falsified=" + falsified
210      + ", num_elts=" + num_elts
211      + ", elts=" + subarray_rep();
212  }
213
214  @SideEffectFree
215  @Override
216  public String format_using(@GuardSatisfied OneOfFloatSequence this, OutputFormat format) {
217    sort_rep();
218
219    if (format.isJavaFamily()) {
220      return format_java_family(format);
221    }
222
223    if (format == OutputFormat.DAIKON) {
224      return format_daikon();
225    } else if (format == OutputFormat.SIMPLIFY) {
226      return format_simplify();
227    } else if (format == OutputFormat.ESCJAVA) {
228      String result = format_esc();
229      return result;
230    } else if (format == OutputFormat.CSHARPCONTRACT) {
231      return format_csharp_contract();
232    } else {
233      return format_unimplemented(format);
234    }
235  }
236
237  public String format_daikon(@GuardSatisfied OneOfFloatSequence this) {
238    String varname = var().name();
239    if (num_elts == 1) {
240
241          return varname + " == " + Arrays.toString(elts[0]);
242    } else {
243      return varname + " one of " + subarray_rep();
244    }
245  }
246
247  public String format_esc(@GuardSatisfied OneOfFloatSequence this) {
248    sort_rep();
249
250    String result;
251
252    String length = null;
253    String forall = null;
254
255    if (length == null) {
256      if (forall == null) {
257        return format_unimplemented(OutputFormat.ESCJAVA); // "needs to be implemented"
258      } else {
259        result = forall;
260      }
261    } else {
262      if (forall == null) {
263        result = length;
264      } else {
265        result = "(" + length + ") && (" + forall + ")";
266      }
267    }
268
269    return result;
270  }
271
272public String format_csharp_contract(@GuardSatisfied OneOfFloatSequence this) {
273
274    @NonNull @Initialized String result;
275
276  result = "(\"oneOf.java.jpp: SEQUENCE unimplemented\" != null)"; // "interned"
277
278    return result;
279  }
280
281  public String format_java_family(@GuardSatisfied OneOfFloatSequence this, OutputFormat format) {
282
283    String result;
284
285    // Setting up the name of the unary variable
286    String varname = var().name_using(format);
287
288    result = "";
289    for (int i = 0; i < num_elts; i++) {
290      if (i != 0) { result += " || "; }
291
292      String seq = "new double[] { ";
293
294      for (int j = 0 ; j < elts[i].length ; j++) {
295        if (j != 0) { seq += ", "; }
296        seq = seq + (Double.isNaN(elts[i][j]) ? "Double.NaN" : String.valueOf(elts[i][j]));
297      }
298      seq += " }";
299
300      result += "daikon.Quant.pairwiseEqual(" + varname + ", " + seq + ")";
301    }
302
303    return result;
304  }
305
306  public String format_simplify(@GuardSatisfied OneOfFloatSequence this) {
307
308    sort_rep();
309
310    String result;
311
312    StringBuilder resultBuf = new StringBuilder();
313    for (int i = 0; i < num_elts; i++) {
314      double @Interned [] seq = elts[i];
315      String offset = null;
316      String contents = null;
317      String[] bounds_name;
318      String length = var().get_simplify_size_name();
319      // if ((length == null) && var().name.isApplySizeSafe())
320      //  System.out.printf("var %s, type %s, is_array %b%n", var().name(),
321      //                     var().type, var().type.isArray());
322      if (length != null) {
323        length = "(EQ " + length + " "+ simplify_format_long(seq.length) + ")";
324      } else if ((bounds_name = var().get_simplify_slice_bounds()) != null) {
325        String size = "(+ 1 (- " + bounds_name[1] +" " + bounds_name[0] + "))";
326        length = "(EQ " + size + " " + simplify_format_long(seq.length) + ")";
327        offset = bounds_name[0];
328      }
329
330      {
331        StringBuilder contentsBuf = new StringBuilder();
332        for (int j = 0; j < seq.length; j++) {
333          if (j + 3 < seq.length
334              && (((seq[j]) == ( seq[j + 1])) || (Double.isNaN(seq[j]) &&Double.isNaN( seq[j + 1])))
335              && (((seq[j]) == ( seq[j + 2])) || (Double.isNaN(seq[j]) &&Double.isNaN( seq[j + 2])))
336              && (((seq[j]) == ( seq[j + 3])) || (Double.isNaN(seq[j]) &&Double.isNaN( seq[j + 3])))) {
337            // Compress a sequence of adjacent values
338            int k = j + 4;
339            for (; k < seq.length; k++) {
340              if (!(((seq[j]) == ( seq[k])) || (Double.isNaN(seq[j]) &&Double.isNaN( seq[k])))) {
341                break;
342              }
343            }
344            k--;
345            String index_name = VarInfo.get_simplify_free_index(var());
346            String cond_left, cond_right;
347            if (offset == null) {
348              cond_left  = "(<= " + j + " " + index_name + ")";
349              cond_right = "(<= " + index_name + " " + k + ")";
350            } else {
351              cond_left = "(<= (+ " + offset + " " + j + ") "
352                + index_name + ")";
353              cond_right = "(>= (+ " + offset + " " + k + ") "
354                + index_name + ")";
355            }
356            String cond = "(AND " + cond_left + " " + cond_right + ")";
357            String nth = var().get_simplify_selectNth(index_name, true, 0);
358            String eq = "(EQ " + nth + " " + simplify_format_double(seq[j]) + ")";
359            String implies = "(IMPLIES " + cond + " " + eq + ")";
360            String forall = "(FORALL (" + index_name + ") " + implies + ")";
361            contentsBuf.append(" " + forall);
362            j = k;
363          } else {
364            // Output a single value
365            String nth = var().get_simplify_selectNth_lower(j);
366            if (nth == null) {
367              String classname = this.getClass().toString().substring(6);
368              result = "warning: method " + classname
369                + ".format_simplify() needs to fix selectNth(): " + format();
370              return result;
371            }
372            String value = simplify_format_double(seq[j]);
373            contentsBuf.append(" (EQ " + nth + " " + value + ")");
374            // if (nth.contains ("--orig__a"))
375            //   System.out.printf("regular orig__a%n");
376
377          }
378        }
379        if (seq.length > 1) {
380          contents = "(AND " + contentsBuf.toString() + ")";
381        } else if (seq.length == 1) {
382          contents = contentsBuf.toString().substring(1);
383        } else if (seq.length == 0) {
384          contents = null; // back from ""
385        }
386      }
387      if (length == null && contents == null) {
388        resultBuf.append(" ");
389      } else if (length == null && contents != null) {
390        resultBuf.append(" " + contents);
391      } else if (length != null && contents == null) {
392        resultBuf.append(" " + length);
393      } else {
394        assert length != null && contents != null;
395        resultBuf.append(" (AND " + length + " " + contents + ")");
396      }
397
398      }
399    if (num_elts > 1) {
400      result = "(OR" + resultBuf.toString() + ")";
401    } else if (num_elts == 1) {
402      // chop leading space
403      result = resultBuf.toString().substring(1);
404    } else if (num_elts == 0) {
405      return format_too_few_samples(OutputFormat.SIMPLIFY, null);
406    } else {
407      throw new Error("this can't happen");
408      // result = null;
409    }
410    if (result.trim().equals("")) {
411      result = "format_simplify() failed on a weird OneOf";
412    }
413
414    if (result.indexOf("format_simplify") == -1) {
415      daikon.simplify.SimpUtil.assert_well_formed(result);
416    }
417    return result;
418  }
419
420  @Override
421  public InvariantStatus add_modified(double @Interned [] a, int count) {
422    return runValue(a, count, true);
423  }
424
425  @Override
426  public InvariantStatus check_modified(double @Interned [] a, int count) {
427    return runValue(a, count, false);
428  }
429
430  private InvariantStatus runValue(double @Interned [] v, int count, boolean mutate) {
431    InvariantStatus status;
432    if (mutate) {
433      status = add_mod_elem(v, count);
434    } else {
435      status = check_mod_elem(v, count);
436    }
437    if (status == InvariantStatus.FALSIFIED) {
438      if (logOn() && mutate) {
439        StringBuilder eltString = new StringBuilder();
440        for (int i = 0; i < num_elts; i++) {
441          eltString.append(Arrays.toString(elts[i]) + " ");
442        }
443        log("destroyed on sample %s previous vals = {%s} num_elts = %s",
444             Arrays.toString(v), eltString, num_elts);
445      }
446      return InvariantStatus.FALSIFIED;
447    }
448    return status;
449  }
450
451  /**
452   * Adds a single sample to the invariant. Returns
453   * the appropriate InvariantStatus from the result
454   * of adding the sample to this.
455   */
456  public InvariantStatus add_mod_elem(double @Interned [] v, int count) {
457    InvariantStatus status = check_mod_elem(v, count);
458    if (status == InvariantStatus.WEAKENED) {
459      elts[num_elts] = v;
460      num_elts++;
461    }
462    return status;
463  }
464
465  /**
466   * Checks a single sample to the invariant. Returns
467   * the appropriate InvariantStatus from the result
468   * of adding the sample to this.
469   */
470  public InvariantStatus check_mod_elem(double @Interned [] v, int count) {
471
472    // Look for v in our list of previously seen values.  If it's
473    // found, we're all set.
474    for (int i = 0; i < num_elts; i++) {
475      // if (logDetail())
476      //  log ("add_modified (" + v + ")");
477      if (((elts[i]) == ( v))) {
478        return InvariantStatus.NO_CHANGE;
479      }
480    }
481
482    if (num_elts == dkconfig_size) {
483      return InvariantStatus.FALSIFIED;
484    }
485
486    return InvariantStatus.WEAKENED;
487  }
488
489  @Override
490  protected double computeConfidence() {
491    // This is not ideal.
492    if (num_elts == 0) {
493      return Invariant.CONFIDENCE_UNJUSTIFIED;
494    } else {
495      return Invariant.CONFIDENCE_JUSTIFIED;
496    }
497  }
498
499  @Pure
500  @Override
501  public @Nullable DiscardInfo isObviousStatically(VarInfo[] vis) {
502    // Static constants are necessarily OneOf precisely one value.
503    // This removes static constants from the output, which might not be
504    // desirable if the user doesn't know their actual value.
505    if (vis[0].isStaticConstant()) {
506      assert num_elts <= 1;
507      return new DiscardInfo(this, DiscardCode.obvious, vis[0].name() + " is a static constant.");
508    }
509    return super.isObviousStatically(vis);
510  }
511
512  /** {@inheritDoc} */
513  @Override
514  public @Nullable DiscardInfo isObviousDynamically(VarInfo[] vis) {
515    DiscardInfo super_result = super.isObviousDynamically(vis);
516    if (super_result != null) {
517      return super_result;
518    }
519
520    VarInfo v = vis[0];
521
522    // We can check if all values in the element sequence match
523    // with the ones we know about (useful for booleans and numeric
524    // enumerations).
525    if (v.aux.hasValue(VarInfoAux.VALID_VALUES)
526        && v.aux.hasValue(VarInfoAux.MAXIMUM_LENGTH)
527        && v.aux.hasValue(VarInfoAux.MINIMUM_LENGTH)
528        && v.aux.getInt(VarInfoAux.MAXIMUM_LENGTH) == 1
529        && v.aux.getInt(VarInfoAux.MINIMUM_LENGTH) == 1) {
530
531      String[] vsValidValues       = v.aux.getList(VarInfoAux.VALID_VALUES);
532      Set<Double> setValidValues = new TreeSet<>();
533      for (String s : vsValidValues) {
534        setValidValues.add(Double.valueOf(s));
535      }
536      Set<Double> setValuesInvariant = new TreeSet<>();
537      for (double @Interned [] e : elts) {
538        if (e == null) {
539  continue;
540}
541        for (Double b : e) {
542          setValuesInvariant.add(b);
543        }
544      }
545      if (setValidValues.equals(setValuesInvariant)) {
546        return new DiscardInfo(this, DiscardCode.obvious,
547          "The value list matches the allowed value list");
548      }
549    }
550
551    return null;
552  }
553
554  /**
555   * Oneof can merge different formulas from lower points to create a single formula at an upper
556   * point.
557   */
558  @Override
559  public boolean mergeFormulasOk() {
560    return true;
561  }
562
563  @Pure
564  @Override
565  public boolean isSameFormula(Invariant o) {
566    OneOfFloatSequence other = (OneOfFloatSequence) o;
567    if (num_elts != other.num_elts) {
568      return false;
569    }
570    if (num_elts == 0 && other.num_elts == 0) {
571      return true;
572    }
573
574    sort_rep();
575    other.sort_rep();
576
577    for (int i = 0; i < num_elts; i++) {
578      if (!((elts[i]) == (other.elts[i]))) {
579        return false;
580      }
581    }
582
583    return true;
584  }
585
586  @Pure
587  @Override
588  public boolean isExclusiveFormula(Invariant o) {
589    if (o instanceof OneOfFloatSequence) {
590      OneOfFloatSequence other = (OneOfFloatSequence) o;
591
592      if (num_elts == 0 || other.num_elts == 0) {
593        return false;
594      }
595      for (int i = 0; i < num_elts; i++) {
596        for (int j = 0; j < other.num_elts; j++) {
597          if (((elts[i]) == (other.elts[j]))) // elements are interned
598            return false;
599        }
600      }
601
602      return true;
603    }
604
605    return false;
606  }
607
608  @Override
609  public boolean hasUninterestingConstant() {
610
611    return false;
612  }
613
614  @Pure
615  @Override
616  public boolean isExact() {
617    return num_elts == 1;
618  }
619
620  // Look up a previously instantiated invariant.
621  public static @Nullable OneOfFloatSequence find(PptSlice ppt) {
622    assert ppt.arity() == 1;
623    for (Invariant inv : ppt.invs) {
624      if (inv instanceof OneOfFloatSequence) {
625        return (OneOfFloatSequence) inv;
626      }
627    }
628    return null;
629  }
630
631  // Interning is lost when an object is serialized and deserialized.
632  // Manually re-intern any interned fields upon deserialization.
633  private void readObject(ObjectInputStream in) throws IOException,
634    ClassNotFoundException {
635    in.defaultReadObject();
636
637    for (int i = 0; i < num_elts; i++) {
638      elts[i] = Intern.intern(elts[i]);
639    }
640  }
641
642  /**
643   * Merge the invariants in invs to form a new invariant. Each must be a OneOfFloatSequence invariant.
644   * This code finds all of the oneof values from each of the invariants and returns the merged
645   * invariant (if any).
646   *
647   * @param invs list of invariants to merge. The invariants must all be of the same type and should
648   *     come from the children of parent_ppt. They should also all be permuted to match the
649   *     variable order in parent_ppt.
650   * @param parent_ppt slice that will contain the new invariant
651   */
652  @Override
653  public @Nullable Invariant merge(List<Invariant> invs, PptSlice parent_ppt) {
654
655    // Create the initial parent invariant from the first child
656    OneOfFloatSequence  first = (OneOfFloatSequence) invs.get(0);
657    OneOfFloatSequence result = first.clone();
658    result.ppt = parent_ppt;
659
660      for (int i = 0; i < result.num_elts; i++) {
661        result.elts[i] = Intern.intern(result.elts[i]);
662      }
663
664    // Loop through the rest of the child invariants
665    for (int i = 1; i < invs.size(); i++ ) {
666
667      // Get this invariant
668      OneOfFloatSequence inv = (OneOfFloatSequence) invs.get(i);
669
670      // Loop through each distinct value found in this child and add
671      // it to the parent.  If the invariant is falsified, there is no parent
672      // invariant
673      for (int j = 0; j < inv.num_elts; j++) {
674        double @Interned [] val = inv.elts[j];
675
676        val = Intern.intern(val);
677
678        InvariantStatus status = result.add_mod_elem(val, 1);
679        if (status == InvariantStatus.FALSIFIED) {
680
681          result.log("%s", "child value '" + Arrays.toString(val) + "' destroyed oneof");
682
683          return null;
684        }
685      }
686    }
687
688    result.log("Merged '%s' from %s child invariants", result.format(), invs.size());
689    return result;
690  }
691
692  /**
693   * Setup the invariant with the specified elements. Normally used when searching for a specified
694   * OneOf. The elements of vals are not necessarily interned; this method interns each element.
695   */
696  public void set_one_of_val(double[][] vals) {
697
698    num_elts = vals.length;
699    for (int i = 0; i < num_elts; i++) {
700      elts[i] = Intern.intern(vals[i]);
701    }
702  }
703
704  /**
705   * Returns true if every element in this invariant is contained in the specified state. For
706   * example if x = 1 and the state contains 1 and 2, true will be returned.
707   */
708  @Override
709  public boolean state_match(Object state) {
710
711    if (num_elts == 0) {
712      return false;
713    }
714
715    if (!(state instanceof double[] @Interned [])) {
716      // Daikon is about to crash.  Produce some debugging output.
717      System.out.printf("state = %s [%s]%n", state, state.getClass());
718      System.out.printf("problem with %s%n", this);
719    }
720    double[] @Interned [] e = (double[] @Interned []) state;
721    for (int i = 0; i < num_elts; i++) {
722      boolean match = false;
723      for (int j = 0; j < e.length; j++) {
724        if (elts[i] == e[j]) {
725          match = true;
726          break;
727        }
728      }
729      if (!match) {
730        return false;
731      }
732    }
733    return true;
734  }
735
736}