001// ***** This file is automatically generated from Bound.java.jpp
002
003package daikon.inv.unary.sequence;
004
005import daikon.*;
006import daikon.inv.*;
007
008  import daikon.inv.binary.twoSequence.*;
009  import daikon.inv.unary.scalar.*;
010
011import daikon.derive.unary.*;
012import daikon.inv.unary.*;
013import java.util.*;
014import org.checkerframework.checker.interning.qual.Interned;
015import org.checkerframework.checker.lock.qual.GuardSatisfied;
016import org.checkerframework.checker.nullness.qual.Nullable;
017import org.checkerframework.dataflow.qual.Pure;
018import org.checkerframework.dataflow.qual.SideEffectFree;
019import org.checkerframework.framework.qual.Unused;
020import org.plumelib.util.Intern;
021import typequals.prototype.qual.NonPrototype;
022import typequals.prototype.qual.Prototype;
023
024  /**
025   * Represents the invariant that each element of a sequence of long values is greater than or
026   * equal to a constant. Prints as {@code x[] elements >= c}.
027   */
028
029// One reason not to combine LowerBound and UpperBound into a single range
030// invariant is that they have separate justifications:  one may be
031// justified when the other is not.
032@SuppressWarnings("UnnecessaryParentheses")  // code generated by macros
033public class EltLowerBound extends SingleScalarSequence {
034  static final long serialVersionUID = 20030822L;
035
036  // Variables starting with dkconfig_ should only be set via the
037  // daikon.config.Configuration interface.
038  /** Boolean. True iff EltLowerBound invariants should be considered. */
039  public static boolean dkconfig_enabled = Invariant.invariantEnabledDefault;
040  /**
041   * Long integer. Together with the corresponding {@code maximal_interesting} parameter,
042   * specifies the range of the computed constant that is ``interesting'' --- the range that should
043   * be reported. For instance, setting {@code minimal_interesting} to -1 and
044   * {@code maximal_interesting} to 2 would only permit output of EltLowerBound invariants whose
045   * cutoff was one of (-1,0,1,2).
046   */
047  public static long dkconfig_minimal_interesting = -1;
048  /**
049   * Long integer. Together with the corresponding {@code minimal_interesting} parameter,
050   * specifies the range of the computed constant that is ``interesting'' --- the range that should
051   * be reported. For instance, setting {@code minimal_interesting} to -1 and
052   * {@code maximal_interesting} to 2 would only permit output of EltLowerBound invariants whose
053   * cutoff was one of (-1,0,1,2).
054   */
055  public static long dkconfig_maximal_interesting = 2;
056
057  @Unused(when=Prototype.class)
058  private LowerBoundCore core;
059
060  @SuppressWarnings("nullness") // circular initialization
061  private EltLowerBound(PptSlice slice) {
062    super(slice);
063    assert slice != null;
064    core = new LowerBoundCore(this);
065  }
066
067  private @Prototype EltLowerBound() {
068    super();
069    // do we need a core?
070  }
071
072  private static @Prototype EltLowerBound proto = new @Prototype EltLowerBound();
073
074  /** Returns the prototype invariant for EltLowerBound */
075  public static @Prototype EltLowerBound get_proto() {
076    return proto;
077  }
078
079  @Override
080  public boolean enabled() {
081    return dkconfig_enabled;
082  }
083
084  @Override
085  public boolean instantiate_ok(VarInfo[] vis) {
086
087    if (!valid_types(vis)) {
088      return false;
089    }
090
091    return vis[0].file_rep_type.baseIsIntegral();
092    }
093
094  @Override
095  public EltLowerBound instantiate_dyn(@Prototype EltLowerBound this, PptSlice slice) {
096    return new EltLowerBound(slice);
097  }
098
099  @SideEffectFree
100  @Override
101  public EltLowerBound clone(@GuardSatisfied EltLowerBound this) {
102    EltLowerBound result = (EltLowerBound) super.clone();
103    result.core = core.clone();
104    result.core.wrapper = result;
105    return result;
106  }
107
108  public long min() {
109    return core.min();          // i.e., core.min1
110  }
111
112  @Override
113  public String repr(@GuardSatisfied EltLowerBound this) {
114    return "EltLowerBound" + varNames() + ": "
115      + core.repr();
116  }
117
118  @SideEffectFree
119  @Override
120  public String format_using(@GuardSatisfied EltLowerBound this, OutputFormat format) {
121
122    if (format.isJavaFamily()) {
123      return format_java_family(format);
124    }
125
126    if (format == OutputFormat.DAIKON) {
127      return format_daikon();
128    } else if (format == OutputFormat.SIMPLIFY) {
129      return format_simplify();
130    } else if (format == OutputFormat.ESCJAVA) {
131      return format_esc();
132    } else if (format == OutputFormat.CSHARPCONTRACT) {
133      return format_csharp_contract();
134    }
135
136    return format_unimplemented(format);
137  }
138  // ELTLOWEr || ELTUPPEr
139  public String format_daikon(@GuardSatisfied EltLowerBound this) {
140    PptTopLevel pptt = ppt.parent;
141    String name = var().name();
142
143    if (PrintInvariants.dkconfig_static_const_infer) {
144      for (VarInfo vi : pptt.var_infos) {
145        if (vi.isStaticConstant() && VarComparability.comparable(vi, var())) {
146          // If variable is a double, then use fuzzy comparison
147          if (vi.rep_type == ProglangType.DOUBLE) {
148            Double constantVal = (Double)vi.constantValue();
149            if (Global.fuzzy.eq(constantVal, (double)(core.min1)) || false) {
150              return name + " >= " + vi.name();
151            }
152          }
153          // Otherwise just use the equals method
154          else {
155            Object constantVal = vi.constantValue();
156            if (constantVal.equals(core.min1)) {
157              return name + " >= " + vi.name();
158            }
159          }
160        }
161      }
162    }
163
164    return var().name() + " elements >= " + core.min1;
165  }
166
167  public String format_esc(@GuardSatisfied EltLowerBound this) {
168    PptTopLevel pptt = ppt.parent;
169
170    if (PrintInvariants.dkconfig_static_const_infer) {
171      for (VarInfo vi : pptt.var_infos) {
172        if (vi.isStaticConstant() && VarComparability.comparable(vi, var())) {
173          // If variable is a double, then use fuzzy comparison
174          if (vi.rep_type == ProglangType.DOUBLE) {
175            Double constantVal = (Double)vi.constantValue();
176            if (Global.fuzzy.eq(constantVal, (double)(core.min1)) || false) {
177
178              String[] form = VarInfo.esc_quantify(var());
179              return form[0] + "(" + form[1] + " >= " + vi.name() + ")" + form[2];
180            }
181          }
182          // Otherwise just use the equals method
183            else {
184            Object constantVal = vi.constantValue();
185            if (constantVal.equals(core.min1)) {
186
187              String[] form = VarInfo.esc_quantify(var());
188              return form[0] + "(" + form[1] + " >= " + vi.name() + ")" + form[2];
189            }
190          }
191        }
192      }
193    }
194
195    String[] form = VarInfo.esc_quantify(var());
196    return form[0] + "(" + form[1] + " >= " + core.min1 + ")" + form[2];
197  }
198
199  public String format_csharp_contract(@GuardSatisfied EltLowerBound this) {
200    PptTopLevel pptt = ppt.parent;
201
202    if (PrintInvariants.dkconfig_static_const_infer) {
203      for (VarInfo vi : pptt.var_infos) {
204        if (vi.isStaticConstant() && VarComparability.comparable(vi, var())) {
205          // If variable is a double, then use fuzzy comparison
206          if (vi.rep_type == ProglangType.DOUBLE) {
207            Double constantVal = (Double)vi.constantValue();
208            if (Global.fuzzy.eq(constantVal, (double)(core.min1)) || false) {
209
210              String[] split = var().csharp_array_split();
211              return "Contract.ForAll(" + split[0] + ", x => x" + split[1] + " >= " + vi.name() + ")";
212            }
213          }
214          // Otherwise just use the equals method
215          else {
216            Object constantVal = vi.constantValue();
217            if (constantVal.equals(core.min1)) {
218
219              String[] split = var().csharp_array_split();
220              return "Contract.ForAll(" + split[0] + ", x => x" + split[1] + " >= " + vi.name() + ")";
221            }
222          }
223        }
224      }
225    }
226
227    String[] split = var().csharp_array_split();
228    return "Contract.ForAll(" + split[0] + ", x => x" + split[1] + " >= " + core.min1+ ")";
229  }
230
231  public String format_java_family(@GuardSatisfied EltLowerBound this, OutputFormat format) {
232    PptTopLevel pptt = ppt.parent;
233
234    if (PrintInvariants.dkconfig_static_const_infer) {
235      for (VarInfo vi : pptt.var_infos) {
236        if (vi.isStaticConstant() && VarComparability.comparable(vi, var())) {
237          // If variable is a double, then use fuzzy comparison
238          if (vi.rep_type == ProglangType.DOUBLE) {
239            Double constantVal = (Double)vi.constantValue();
240            if (Global.fuzzy.eq(constantVal, (double)(core.min1)) || false) {
241
242              return "daikon.Quant.eltsGTE(" + var().name_using(format) + ", " + vi.name() + ")";
243            }
244          }
245          // Otherwise just use the equals method
246          else {
247            Object constantVal = vi.constantValue();
248            if (constantVal.equals(core.min1)) {
249
250              return "daikon.Quant.eltsGTE(" + var().name_using(format) + ", " + vi.name() + ")";
251            }
252          }
253        }
254      }
255    }
256
257    return
258      "daikon.Quant.eltsGTE(" + var().name_using(format) + ", " + core.min1 + ")";
259
260  }
261
262  public String format_simplify(@GuardSatisfied EltLowerBound this) {
263
264    String value = simplify_format_long(core.min1);
265
266    String[] form = VarInfo.simplify_quantify(var());
267    return form[0] + "(>= " + form[1] + " " + value + ")"
268      + form[2];
269
270  }
271
272  @Override
273  public InvariantStatus add_modified(long @Interned [] value, int count) {
274    // System.out.println("EltLowerBound" + varNames() + ": "
275    //              + "add(" + value + ", " + modified + ", " + count + ")");
276
277    boolean changed = false;
278    InvariantStatus status = InvariantStatus.NO_CHANGE;
279    for (int i = 0; i < value.length; i++) {
280      if (!changed && core.wouldChange(value[i])) {
281        changed = true;
282        status = InvariantStatus.WEAKENED;
283      }
284      if (core.add_modified(value[i], count) == InvariantStatus.FALSIFIED) {
285        return InvariantStatus.FALSIFIED;
286      }
287    }
288    return status;
289  }
290
291  @Override
292  public InvariantStatus check_modified(long @Interned [] value, int count) {
293
294    for (int i = 0; i < value.length; i++) {
295      if (core.check(value[i]) == InvariantStatus.WEAKENED) {
296        return InvariantStatus.WEAKENED;
297      }
298    }
299    return InvariantStatus.NO_CHANGE;
300  }
301
302  @Override
303  public boolean enoughSamples(@GuardSatisfied EltLowerBound this) {
304    return core.enoughSamples();
305  }
306
307  @Override
308  protected double computeConfidence() {
309    return core.computeConfidence();
310  }
311
312  @Pure
313  @Override
314  public boolean isExact() {
315    return core.isExact();
316  }
317
318  @Pure
319  @Override
320  public boolean isSameFormula(Invariant other) {
321    return core.isSameFormula(((EltLowerBound) other).core);
322  }
323
324  @Override
325  public boolean hasUninterestingConstant() {
326    // If the constant bound is not in some small range of interesting
327    // values (by default {-1, 0, 1, 2}), call it uninteresting.
328    if ((core.min1 < dkconfig_minimal_interesting)
329        || (core.min1 > dkconfig_maximal_interesting)) {
330      return true;
331    }
332
333    return false;
334  }
335
336  @Pure
337  @Override
338  public @Nullable DiscardInfo isObviousStatically(VarInfo[] vis) {
339    VarInfo var = vis[0];
340    if ((var.derived instanceof SequenceLength)
341         && (((SequenceLength) var.derived).shift != 0)) {
342      return new DiscardInfo(this, DiscardCode.obvious, "Bounds are preferrable over sequence lengths with no shift");
343    }
344
345    return super.isObviousStatically(vis);
346  }
347
348  @Pure
349  @Override
350  public @Nullable DiscardInfo isObviousDynamically(VarInfo[] vis) {
351    DiscardInfo super_result = super.isObviousDynamically(vis);
352    if (super_result != null) {
353      return super_result;
354    }
355
356    PptTopLevel pptt = ppt.parent;
357
358    // This check always lets invariants pass through (even if it is not within
359    // the default range of (-1 to 2) if it matches a static constant
360    // As noted below, this check really doesn't belong here, but should be
361    // moved to hasUninterestingConstant() whenever that is implemented
362    if (PrintInvariants.dkconfig_static_const_infer) {
363      if (core.matchConstant()) {
364        return null;
365      }
366    }
367
368    // if the value is not in some range (like -1,0,1,2) then say that it is obvious
369    if ((core.min1 < dkconfig_minimal_interesting)
370        || (core.min1 > dkconfig_maximal_interesting)) {
371      // XXX This check doesn't really belong here. However It
372      // shouldn't get removed until hasUninterestingConstant() is
373      // suitable to be turned on everywhere by default. -SMcC
374      // if the value is not in some range (like -1,0,1,2) then say that
375      // it is obvious
376      String discardString = "";
377      if (core.min1 < dkconfig_minimal_interesting) {
378        discardString = "MIN1="+core.min1+" is less than dkconfig_minimal_interesting=="
379          + dkconfig_minimal_interesting;
380      } else {
381        discardString = "MIN1="+core.min1+" is greater than dkconfig_maximal_interesting=="+
382          dkconfig_maximal_interesting;
383      }
384      return new DiscardInfo(this, DiscardCode.obvious, discardString);
385    }
386    EltOneOf oo = EltOneOf.find(ppt);
387    if ((oo != null) && oo.enoughSamples() && oo.num_elts() > 0) {
388      assert oo.var().isCanonical();
389      // We could also use core.min1 == oo.min_elt(), since the LowerBound
390      // will never have a core.min1 that does not appear in the OneOf.
391      if (core.min1 <= oo.min_elt()) {
392        String varName = vis[0].name();
393        String discardString = varName + "<=" + core.min1 + " is implied by " + varName + "<=" + oo.min_elt();
394        log("%s", discardString);
395        return new DiscardInfo(this, DiscardCode.obvious, discardString);
396      }
397    }
398
399    // NOT: "VarInfo v = var();" because we want to operate not on this
400    // object's own variables, but on the variables that were passed in.
401    VarInfo v = vis[0];
402
403    // Look for the same property over a supersequence of this one.
404    for (Iterator<Invariant> inv_itor = pptt.invariants_iterator(); inv_itor.hasNext(); ) {
405      Invariant inv = inv_itor.next();
406      if (inv == this) {
407        continue;
408      }
409      if (inv instanceof EltLowerBound) {
410        EltLowerBound other = (EltLowerBound) inv;
411        if (isSameFormula(other)
412            && SubSequence.isObviousSubSequenceDynamically(this, v, other.var())) {
413          String varName = v.name();
414          String otherName = other.var().name();
415          String discardString = varName + " is a subsequence of " + otherName + " for which the invariant holds.";
416          log("%s", discardString);
417          return new DiscardInfo(this, DiscardCode.obvious, discardString);
418        }
419      }
420    }
421
422    // For each sequence variable, if this is an obvious member/subsequence, and
423    // it has the same invariant, then this one is obvious.
424    for (int i = 0; i < pptt.var_infos.length; i++) {
425      VarInfo vi = pptt.var_infos[i];
426
427      if (SubSequence.isObviousSubSequenceDynamically(this, v, vi))
428      {
429        PptSlice1 other_slice = pptt.findSlice(vi);
430        if (other_slice != null) {
431          EltLowerBound eb = EltLowerBound.find(other_slice);
432          if ((eb != null)
433              && eb.enoughSamples()
434              && eb.min() == min()) {
435            String otherName = other_slice.var_infos[0].name();
436            String varName = v.name();
437            String discardString = varName+" is a subsequence of "+otherName+" for which the invariant holds.";
438            log("%s", discardString);
439            return new DiscardInfo(this, DiscardCode.obvious, discardString);
440          }
441        }
442      }
443    }
444
445    return null;
446  }
447
448  @Pure
449  @Override
450  public boolean isExclusiveFormula(Invariant other) {
451
452    // N.B. "x[] elements >= 200" is not mutually exclusive with "x[]
453    // elements <= 100"; they could both be true if x[] were always empty.
454
455    if (other instanceof OneOfScalar) {
456      return other.isExclusiveFormula(this);
457    }
458    return false;
459  }
460
461  // Look up a previously instantiated invariant.
462  public static @Nullable EltLowerBound find(PptSlice ppt) {
463    assert ppt.arity() == 1;
464    for (Invariant inv : ppt.invs) {
465      if (inv instanceof EltLowerBound) {
466        return (EltLowerBound) inv;
467      }
468    }
469    return null;
470  }
471
472  /**
473   * Bound can merge different formulas from lower points to create a single formula at an upper
474   * point. See merge() below.
475   */
476  @Override
477  public boolean mergeFormulasOk() {
478    return true;
479  }
480
481  /**
482   * Merge the invariants in invs to form a new invariant. Each must be a EltLowerBound invariant. This
483   * code finds all of the min/max values in each invariant, applies them to a new parent invariant
484   * and returns the merged invariant (if any).
485   *
486   * @param invs list of invariants to merge. The invariants must all be of the same type and should
487   *     come from the children of parent_ppt. They should also all be permuted to match the
488   *     variable order in parent_ppt.
489   * @param parent_ppt slice that will contain the new invariant
490   */
491  @Override
492  public @Nullable EltLowerBound merge(List<Invariant> invs, PptSlice parent_ppt) {
493
494    // Create the initial parent invariant from the first child
495    EltLowerBound first = (EltLowerBound) invs.get(0);
496    EltLowerBound result= first.clone();
497    result.ppt = parent_ppt;
498
499    // Loop through the rest of the child invariants
500    for (int i = 1; i < invs.size(); i++ ) {
501      EltLowerBound lb = (EltLowerBound) invs.get(i);
502      result.core.add(lb.core);
503    }
504
505    result.log("Merged '%s' from %s child invariants", result.format(),invs.size());
506    return result;
507  }
508}