001package daikon.inv.unary.scalar;
002
003import daikon.PptSlice;
004import daikon.VarInfo;
005import daikon.inv.Invariant;
006import daikon.inv.InvariantStatus;
007import daikon.inv.OutputFormat;
008import java.util.List;
009import java.util.NavigableSet;
010import java.util.TreeSet;
011import org.checkerframework.checker.lock.qual.GuardSatisfied;
012import org.checkerframework.checker.nullness.qual.NonNull;
013import org.checkerframework.checker.nullness.qual.Nullable;
014import org.checkerframework.dataflow.qual.Pure;
015import org.checkerframework.dataflow.qual.SideEffectFree;
016import org.plumelib.util.Intern;
017import org.plumelib.util.MathPlume;
018import typequals.prototype.qual.NonPrototype;
019import typequals.prototype.qual.Prototype;
020
021/**
022 * Represents long scalars that are never equal to {@code r (mod m)} where all other numbers in the
023 * same range (i.e., all the values that {@code x} doesn't take from {@code min(x)} to {@code
024 * max(x)}) are equal to {@code r (mod m)}. Prints as {@code x != r (mod m)}, where {@code r} is the
025 * remainder and {@code m} is the modulus.
026 */
027public class NonModulus extends SingleScalar {
028  static final long serialVersionUID = 20020122L;
029
030  // Variables starting with dkconfig_ should only be set via the
031  // daikon.config.Configuration interface.
032  /** Boolean. True iff NonModulus invariants should be considered. */
033  public static boolean dkconfig_enabled = false;
034
035  /** The values to be tested for the non-modulus property. */
036  @SuppressWarnings("serial")
037  NavigableSet<Long> elements = new TreeSet<>();
038
039  private long modulus = 0;
040  private long remainder = 0;
041  // The next two variables indicate whether the "modulus" and "result"
042  // fields are up to date.
043  // Indicates that no nonmodulus has been found; maybe with more
044  // samples, one will appear.
045  private boolean no_result_yet = false;
046  // We don't continuously keep the modulus and remainder field up to date.
047  // This indicates whether it is.
048  private boolean results_accurate = false;
049
050  private NonModulus(PptSlice ppt) {
051    super(ppt);
052  }
053
054  private @Prototype NonModulus() {
055    super();
056  }
057
058  private static @Prototype NonModulus proto = new @Prototype NonModulus();
059
060  /** Returns the prototype invariant for NonModulus. */
061  public static @Prototype NonModulus get_proto() {
062    return proto;
063  }
064
065  @Override
066  public boolean instantiate_ok(VarInfo[] vis) {
067
068    if (!valid_types(vis)) {
069      return false;
070    }
071
072    return vis[0].file_rep_type.baseIsIntegral();
073  }
074
075  @Override
076  public boolean enabled() {
077    return dkconfig_enabled;
078  }
079
080  @Override
081  protected NonModulus instantiate_dyn(@Prototype NonModulus this, PptSlice slice) {
082    return new NonModulus(slice);
083  }
084
085  @SideEffectFree
086  @Override
087  public NonModulus clone(@GuardSatisfied NonModulus this) {
088    NonModulus result = (NonModulus) super.clone();
089    result.elements = new TreeSet<Long>(this.elements);
090    return result;
091  }
092
093  @Override
094  public String repr(@GuardSatisfied NonModulus this) {
095    return "NonModulus" + varNames() + ": m=" + modulus + ",r=" + remainder;
096  }
097
098  @SideEffectFree
099  @Override
100  public String format_using(@GuardSatisfied NonModulus this, OutputFormat format) {
101    updateResults();
102    String name = var().name_using(format);
103
104    if (format == OutputFormat.DAIKON) {
105      if (no_result_yet) {
106        return name + " != ? (mod ?) ***";
107      }
108      return name + " != " + remainder + "  (mod " + modulus + ")";
109    }
110
111    if (no_result_yet) {
112      return format_too_few_samples(format, null);
113    }
114
115    if (format.isJavaFamily()) {
116
117      if (var().type.isFloat()) {
118        return "daikon.Quant.fuzzy.ne(" + name + " % " + modulus + ", " + remainder + ")";
119      } else {
120        return name + " % " + modulus + " != " + remainder;
121      }
122    }
123
124    if (format == OutputFormat.CSHARPCONTRACT) {
125      return name + " % " + modulus + " != " + remainder;
126    }
127
128    if (format == OutputFormat.SIMPLIFY) {
129      return "(NEQ (MOD "
130          + var().simplify_name()
131          + " "
132          + simplify_format_long(modulus)
133          + ") "
134          + simplify_format_long(remainder)
135          + ")";
136    }
137
138    return format_unimplemented(format);
139  }
140
141  // Set either modulus and remainder, or no_result_yet.
142  void updateResults(@GuardSatisfied NonModulus this) {
143    if (results_accurate) {
144      return;
145    }
146    if (elements.size() == 0) {
147      no_result_yet = true;
148    } else {
149      // Do I want to communicate back some information about the smallest
150      // possible modulus?
151      long[] result = MathPlume.nonmodulusStrictLong(elements.iterator());
152      if (result == null) {
153        no_result_yet = true;
154      } else {
155        remainder = result[0];
156        modulus = result[1];
157        no_result_yet = false;
158      }
159    }
160    results_accurate = true;
161  }
162
163  @Override
164  public InvariantStatus check_modified(long value, int count) {
165    return InvariantStatus.NO_CHANGE;
166  }
167
168  // XXX have to deal with flowing this; maybe it should live at all ppts?
169  @Override
170  public InvariantStatus add_modified(long value, int count) {
171    if (elements.add(Intern.internedLong(value))
172        && results_accurate
173        && !no_result_yet
174        && (MathPlume.modNonnegative(value, modulus) == remainder)) results_accurate = false;
175    return InvariantStatus.NO_CHANGE;
176  }
177
178  @Override
179  protected double computeConfidence() {
180    updateResults();
181    if (no_result_yet) {
182      return Invariant.CONFIDENCE_UNJUSTIFIED;
183    }
184    double probability_one_elt_nonmodulus = 1 - 1.0 / modulus;
185    // return 1 - Math.pow(probability_one_elt_nonmodulus, ppt.num_mod_samples());
186    return 1 - Math.pow(probability_one_elt_nonmodulus, ppt.num_samples());
187  }
188
189  @Pure
190  @Override
191  public boolean isSameFormula(Invariant o) {
192    NonModulus other = (NonModulus) o;
193
194    updateResults();
195    other.updateResults();
196
197    if (no_result_yet && other.no_result_yet) {
198      return true;
199    } else if (no_result_yet || other.no_result_yet) {
200      return false;
201    } else {
202      return (modulus == other.modulus) && (remainder == other.remainder);
203    }
204  }
205
206  /** Returns true if this has the given modulus and remainder. */
207  public boolean hasModulusRemainder(long modulus, long remainder) {
208    updateResults();
209    if (no_result_yet) {
210      return false;
211    }
212
213    return (modulus == this.modulus) && (remainder == this.remainder);
214  }
215
216  @Pure
217  @Override
218  public boolean isExclusiveFormula(Invariant o) {
219    updateResults();
220    if (no_result_yet) {
221      return false;
222    }
223    if (o instanceof NonModulus) {
224      NonModulus other = (NonModulus) o;
225      other.updateResults();
226      if (other.no_result_yet) {
227        return false;
228      }
229      return (modulus == other.modulus) && (remainder != other.remainder);
230    } else if (o instanceof Modulus) {
231      Modulus other = (Modulus) o;
232      return (modulus == other.modulus) && (remainder == other.remainder);
233    }
234
235    return false;
236  }
237
238  @Override
239  public @Nullable @NonPrototype NonModulus merge(
240      @Prototype NonModulus this, List<@NonPrototype Invariant> invs, PptSlice parent_ppt) {
241    @SuppressWarnings("nullness") // super.merge does not return null
242    @NonNull NonModulus result = (NonModulus) super.merge(invs, parent_ppt);
243    for (Invariant inv : invs) {
244      NonModulus r = (NonModulus) inv;
245      if (result.modulus != r.modulus
246          || result.remainder != r.remainder
247          || result.no_result_yet != r.no_result_yet
248          || result.results_accurate != r.results_accurate) {
249        return null;
250      }
251    }
252    return result;
253  }
254}