001// ***** This file is automatically generated from CommonSequence.java.jpp
002
003package daikon.inv.unary.sequence;
004
005import daikon.*;
006import daikon.inv.*;
007import java.util.Arrays;
008import java.util.List;
009import java.util.StringJoiner;
010import org.checkerframework.checker.interning.qual.Interned;
011import org.checkerframework.checker.lock.qual.GuardSatisfied;
012import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
013import org.checkerframework.checker.nullness.qual.NonNull;
014import org.checkerframework.checker.nullness.qual.Nullable;
015import org.checkerframework.dataflow.qual.Pure;
016import org.checkerframework.dataflow.qual.SideEffectFree;
017import org.plumelib.util.ArraysPlume;
018import org.plumelib.util.Intern;
019import typequals.prototype.qual.NonPrototype;
020import typequals.prototype.qual.Prototype;
021
022/**
023 * Represents sequences of long values that contain a common subset. Prints as
024 * {@code {e1, e2, e3, ...} subset of x[]}.
025 */
026
027public class CommonSequence extends SingleScalarSequence {
028  static final long serialVersionUID = 20030822L;
029
030  // Variables starting with dkconfig_ should only be set via the
031  // daikon.config.Configuration interface.
032  /** Boolean. True iff CommonSequence invariants should be considered. */
033  public static boolean dkconfig_enabled = false;
034
035  /** Boolean. Set to true to consider common sequences over hashcodes (pointers). */
036  public static boolean dkconfig_hashcode_seqs = false;
037
038  /** The number of samples observed. */
039  private int count = 0;
040
041  /** Null means no samples have been seen yet. Empty array means intersection is empty. */
042  private long @MonotonicNonNull @Interned [] intersect = null;
043
044  protected CommonSequence(PptSlice ppt) {
045    super(ppt);
046  }
047
048  protected @Prototype CommonSequence() {
049    super();
050  }
051
052  private static @Prototype CommonSequence proto = new @Prototype CommonSequence();
053
054  /** Returns the prototype invariant for CommonSequence */
055  public static @Prototype CommonSequence get_proto() {
056    return proto;
057  }
058
059  @Override
060  public boolean enabled() {
061    return dkconfig_enabled;
062  }
063
064  /** Sequences of hashcode values won't be consistent and are thus not printed by default. */
065  @Override
066  public boolean instantiate_ok(VarInfo[] vis) {
067
068    if (!valid_types(vis)) {
069      return false;
070    }
071
072    return dkconfig_hashcode_seqs || vis[0].file_rep_type.baseIsIntegral();
073  }
074
075  @Override
076  protected CommonSequence instantiate_dyn(@Prototype CommonSequence this, PptSlice slice) {
077    return new CommonSequence(slice);
078  }
079
080  // Don't define clone (use the Object version instead), because this.intersect is read-only.
081  // public Object clone();
082
083  @Override
084  public String repr(@GuardSatisfied CommonSequence this) {
085    return "CommonSequence " + varNames() + ": count=\"" + count;
086  }
087
088  private String printIntersect(@GuardSatisfied CommonSequence this) {
089    if (intersect == null) {
090      return "{}";
091    }
092
093    StringJoiner result = new StringJoiner(", ", "{", "}");
094    for (int i = 0; i < intersect.length; i++) {
095      result.add("" + intersect[i]);
096    }
097    return result.toString();
098  }
099
100  @SideEffectFree
101  @Override
102  public String format_using(@GuardSatisfied CommonSequence this, OutputFormat format) {
103    if (format == OutputFormat.DAIKON) {
104      return format_daikon();
105    }
106    if (format == OutputFormat.CSHARPCONTRACT) {
107      return format_csharp_contract();
108    }
109    if (format == OutputFormat.SIMPLIFY) {
110      return format_simplify();
111    }
112
113    return format_unimplemented(format);
114  }
115
116  public String format_daikon(@GuardSatisfied CommonSequence this) {
117    return printIntersect() + " subset of " + var().name();
118  }
119
120  public String format_csharp_contract(@GuardSatisfied CommonSequence this) {
121    if (intersect == null) {
122      return "()";
123    }
124
125    if (intersect.length == 1) {
126      String collection = var().csharp_collection_string();
127      return collection + ".Contains(" + intersect[0] + ")";
128    }
129
130    StringJoiner exp = new StringJoiner(", ", "(", ")");
131    for (long i : intersect) {
132      exp.add("" + i);
133    }
134
135    String[] split = var().csharp_array_split();
136    return "Contract.ForAll(" + split[0] + ", x => x" + split[1] + ".OneOf" + exp + ")";
137  }
138
139  private String format_simplify(@GuardSatisfied CommonSequence this) {
140    if (intersect == null || intersect.length == 0) {
141      return "(AND)";
142    }
143    String[] name = var().simplifyNameAndBounds();
144    if (name == null) {
145      return format_unimplemented(OutputFormat.SIMPLIFY);
146    }
147    String idx;
148    if (!name[0].equals("|i|")) {
149      idx = "i";
150    } else {
151      idx = "j";
152    }
153    StringBuilder pre_buf = new StringBuilder("");
154    StringBuilder end_buf = new StringBuilder("");
155    for (int i = 0; i < intersect.length; i++) {
156      pre_buf.append("(EXISTS ("+idx + i + ") (AND ");
157      pre_buf.append("(>= "+idx + i + " " + name[1] + ") ");
158      pre_buf.append("(<= "+idx + i + " " + name[2] + ") ");
159
160      // Based on the class name, I originally wrote this method as if
161      // the invariant represented a common subsequence between two
162      // sequences (i.e. where the match was required to be in
163      // order). In case an invariant like that is added in the
164      // future, use the following:
165//       if (i == 0)
166//         pre_buf.append("(>= "+idx + i + " 0) ");
167//       else if (i > 0)
168//          pre_buf.append("(> "+idx + i + " "+idx+(i-1) +") ");
169//       if (i == intersect.length - 1)
170//         pre_buf.append("(< "+idx + i + " (select arrayLength " + name[0] + ")) ");
171      pre_buf.append("(EQ (select (select elems " + name[0] + ") "+idx + i + ") "
172                     + simplify_format_long(intersect[i]) + ")");
173      if (i == intersect.length - 1) {
174        pre_buf.append(" ");
175      }
176      end_buf.append("))");
177    }
178    pre_buf.append(end_buf);
179    return pre_buf.toString();
180  }
181
182  @Override
183  public InvariantStatus check_modified(long @Interned [] a, int count) {
184    if (a == null) {
185      return InvariantStatus.FALSIFIED;
186    } else if (intersect == null) {
187      return InvariantStatus.NO_CHANGE;
188    } else {
189      for (int i = 0; i < a.length; i++) {
190        if (ArraysPlume.indexOf(intersect, a[i]) != -1) {
191          return InvariantStatus.NO_CHANGE;
192        }
193      }
194      return InvariantStatus.FALSIFIED;
195    }
196  }
197
198  @Override
199  public InvariantStatus add_modified(long @Interned [] a, int count) {
200    // System.out.println ("common: " + Arrays.toString (a));
201    if (a == null) {
202      return InvariantStatus.FALSIFIED;
203    } else if (intersect == null) {
204      intersect = a;
205      return InvariantStatus.NO_CHANGE;
206    }
207
208    long[] tmp = new long[intersect.length];
209    int size = 0;
210    for (int i = 0; i < a.length; i++) {
211      // if (a[i] in intersect) && !(a[i] in tmp), add a[i] to tmp
212      int ii = ArraysPlume.indexOf(intersect, a[i]);
213      if ((ii != -1)
214        && (ArraysPlume.indexOf(ArraysPlume.subarray(tmp,0,size), a[i]) == -1)) {
215        // System.out.println ("adding " + intersect[ii] + " at " + size);
216
217        // Carefully add the existing intersect value and not a[i].  These
218        // are not necessarily the same when fuzzy floating point
219        // comparisons are active.
220        tmp[size++] = intersect[ii];
221      }
222    }
223    if (size == 0) {
224      return InvariantStatus.FALSIFIED;
225    }
226
227    intersect = Intern.intern(ArraysPlume.subarray(tmp, 0, size));
228
229    /// Use a lesser count, because this invariant is frequently a false positive.
230    // this.count += count;
231    this.count++;
232    return InvariantStatus.NO_CHANGE;
233  }
234
235  @Override
236  protected double computeConfidence() {
237    return 1 - Math.pow(.9, count);
238  }
239
240  @Pure
241  @Override
242  public boolean isSameFormula(Invariant other) {
243    assert other instanceof CommonSequence;
244    return true;
245  }
246
247  @Override
248  public @Nullable @NonPrototype CommonSequence merge(
249    @Prototype CommonSequence this, List<@NonPrototype Invariant> invs, PptSlice parent_ppt) {
250    @SuppressWarnings("nullness") // super.merge does not return null
251    @NonNull CommonSequence result = (CommonSequence) super.merge(invs, parent_ppt);
252    for (int i=1; i<invs.size(); i++) {
253      CommonSequence inv = (CommonSequence) invs.get(i);
254      long @MonotonicNonNull @Interned [] invIntersect = inv.intersect;
255      if (invIntersect != null) {
256        InvariantStatus status = result.add_modified(invIntersect, inv.count);
257        if (status == InvariantStatus.FALSIFIED) {
258          return null;
259        }
260      }
261    }
262    return result;
263  }
264}