001package daikon.inv.unary.scalar; 002 003import daikon.PptSlice; 004import daikon.VarInfo; 005import daikon.derive.unary.SequenceLength; 006import daikon.inv.DiscardCode; 007import daikon.inv.DiscardInfo; 008import daikon.inv.Invariant; 009import daikon.inv.InvariantStatus; 010import daikon.inv.OutputFormat; 011import java.util.ArrayList; 012import java.util.Collection; 013import java.util.List; 014import java.util.function.Predicate; 015import org.checkerframework.checker.lock.qual.GuardSatisfied; 016import org.checkerframework.checker.nullness.qual.NonNull; 017import org.checkerframework.checker.nullness.qual.Nullable; 018import org.checkerframework.dataflow.qual.Pure; 019import org.checkerframework.dataflow.qual.SideEffectFree; 020import org.plumelib.util.MathPlume; 021import typequals.prototype.qual.NonPrototype; 022import typequals.prototype.qual.Prototype; 023 024/** 025 * Represents the invariant {@code x == r (mod m)} where {@code x} is a long scalar variable, {@code 026 * r} is the (constant) remainder, and {@code m} is the (constant) modulus. 027 */ 028public class Modulus extends SingleScalar { 029 static final long serialVersionUID = 20030822L; 030 031 // Variables starting with dkconfig_ should only be set via the 032 // daikon.config.Configuration interface. 033 /** Boolean. True iff Modulus invariants should be considered. */ 034 public static boolean dkconfig_enabled = false; 035 036 long modulus = 0; 037 long remainder = 0; 038 039 // An arbitrarily-chosen value used for computing the differences among 040 // all the values. Arbitrary initial value 2222 will be replaced by the 041 // first actual value seen. Once `remainder` is set, this is of no interest. 042 long value1 = 2222; 043 // used for initializing value1 044 boolean no_samples_seen = true; 045 046 private Modulus(PptSlice ppt) { 047 super(ppt); 048 } 049 050 private @Prototype Modulus() { 051 super(); 052 } 053 054 private static @Prototype Modulus proto = new @Prototype Modulus(); 055 056 /** Returns the prototype invariant for Modulus. */ 057 public static @Prototype Modulus get_proto() { 058 return proto; 059 } 060 061 @Override 062 public boolean enabled() { 063 return dkconfig_enabled; 064 } 065 066 @Override 067 public boolean instantiate_ok(VarInfo[] vis) { 068 069 if (!valid_types(vis)) { 070 return false; 071 } 072 073 return vis[0].file_rep_type.baseIsIntegral(); 074 } 075 076 @Override 077 protected Modulus instantiate_dyn(@Prototype Modulus this, PptSlice slice) { 078 return new Modulus(slice); 079 } 080 081 @Override 082 public String repr(@GuardSatisfied Modulus this) { 083 return "Modulus" + varNames() + ": modulus=" + modulus + ",remainder=" + remainder; 084 } 085 086 @SideEffectFree 087 @Override 088 public String format_using(@GuardSatisfied Modulus this, OutputFormat format) { 089 String name = var().name_using(format); 090 091 if (format == OutputFormat.DAIKON) { 092 return var().name() + " == " + remainder + " (mod " + modulus + ")"; 093 } 094 095 if (format.isJavaFamily()) { 096 name = var().name_using(format); 097 if (var().type.isFloat()) { 098 return "daikon.Quant.fuzzy.eq(" + name + " % " + modulus + ", " + remainder + ")"; 099 } else { 100 return name + " % " + modulus + " == " + remainder; 101 } 102 } 103 104 if (format == OutputFormat.CSHARPCONTRACT) { 105 return var().csharp_name() + " % " + modulus + " == " + remainder; 106 } 107 108 // if (format == OutputFormat.JAVA 109 // || format == OutputFormat.JML) { 110 // return var().name.name() + " % " + modulus + " == " + remainder; 111 // } 112 113 if (format == OutputFormat.SIMPLIFY) { 114 if (modulus > 0) { 115 return "(EQ (MOD " 116 + var().simplify_name() 117 + " " 118 + simplify_format_long(modulus) 119 + ") " 120 + simplify_format_long(remainder) 121 + ")"; 122 } else { 123 return format_too_few_samples(format, null); 124 } 125 } 126 127 return format_unimplemented(format); 128 } 129 130 @Override 131 public InvariantStatus check_modified(long value, int count) { 132 if (modulus == 1) { 133 // We shouldn't ever get to this case; the invariant should have been 134 // destroyed instead. 135 throw new Error("Modulus = 1"); 136 } else if (no_samples_seen) { 137 return InvariantStatus.NO_CHANGE; 138 } else if (value == value1) { 139 // no new information, so nothing to do 140 return InvariantStatus.NO_CHANGE; 141 } else if (modulus == 0) { 142 // only one value seen so far 143 // REACHABLE? 144 if (modulus == 1) { 145 return InvariantStatus.FALSIFIED; 146 } 147 } else { 148 long new_modulus_long = Math.abs(MathPlume.gcd(modulus, value1 - value)); 149 int new_modulus; 150 if (new_modulus_long > Integer.MAX_VALUE || (new_modulus_long < Integer.MIN_VALUE)) { 151 new_modulus = 1; 152 } else { 153 new_modulus = (int) new_modulus_long; 154 assert new_modulus > 0; 155 } 156 if (new_modulus != modulus) { 157 if (new_modulus == 1) { 158 return InvariantStatus.FALSIFIED; 159 } 160 } 161 } 162 assert modulus != 1; 163 return InvariantStatus.NO_CHANGE; 164 } 165 166 @Override 167 public InvariantStatus add_modified(long value, int count) { 168 if (modulus == 1) { 169 // We shouldn't ever get to this case; the invariant should have been 170 // destroyed instead. 171 throw new Error( 172 String.format("Modulus = 1 for %s in add_modified(%d, %d)", this, value, count)); 173 // assert falsified; 174 // // We already know this confidence fails 175 // return; 176 } else if (no_samples_seen) { 177 value1 = value; 178 no_samples_seen = false; 179 return InvariantStatus.NO_CHANGE; 180 } else if (value == value1) { 181 // no new information, so nothing to do 182 return InvariantStatus.NO_CHANGE; 183 } else if (modulus == 0) { 184 // only one value seen so far 185 long new_modulus = Math.abs(value1 - value); 186 187 if (new_modulus == 1) { 188 return InvariantStatus.FALSIFIED; 189 } 190 modulus = new_modulus; 191 remainder = MathPlume.modNonnegative(value, modulus); 192 } else { 193 long new_modulus_long = Math.abs(MathPlume.gcd(modulus, value1 - value)); 194 int new_modulus; 195 if (new_modulus_long > Integer.MAX_VALUE || (new_modulus_long < Integer.MIN_VALUE)) { 196 new_modulus = 1; 197 } else { 198 new_modulus = (int) new_modulus_long; 199 assert new_modulus > 0; 200 } 201 if (new_modulus != modulus) { 202 modulus = new_modulus; 203 if (new_modulus == 1) { 204 return InvariantStatus.FALSIFIED; 205 } else { 206 remainder = remainder % new_modulus; 207 } 208 } 209 } 210 assert modulus != 1; 211 return InvariantStatus.NO_CHANGE; 212 } 213 214 @Override 215 protected double computeConfidence() { 216 if (modulus == 1) { 217 return Invariant.CONFIDENCE_NEVER; 218 } 219 if (modulus == 0) { 220 return Invariant.CONFIDENCE_UNJUSTIFIED; 221 } 222 double probability_one_elt_modulus = 1 - 1.0 / modulus; 223 // return 1 - Math.pow(probability_one_elt_modulus, ppt.num_mod_samples()); 224 return 1 - Math.pow(probability_one_elt_modulus, ppt.num_samples()); 225 } 226 227 @Pure 228 @Override 229 public boolean isSameFormula(Invariant other) { 230 Modulus otherModulus = (Modulus) other; 231 232 boolean thisMeaningless = (modulus == 0 || modulus == 1); 233 boolean otherMeaningless = (otherModulus.modulus == 0 || otherModulus.modulus == 1); 234 235 if (thisMeaningless && otherMeaningless) { 236 return true; 237 } else { 238 return (modulus != 1) 239 && (modulus != 0) 240 && (modulus == otherModulus.modulus) 241 && (remainder == otherModulus.remainder); 242 } 243 } 244 245 @Pure 246 @Override 247 public boolean isExclusiveFormula(Invariant other) { 248 if ((modulus == 0) || (modulus == 1)) { 249 return false; 250 } 251 252 // Weak test, can be strengthened. 253 // * x = 1 mod 4 is exclusive with x = 6 mod 8 254 // * x = 1 mod 4 is exclusive with x = 0 mod 2 255 // * x = 0 mod 4 is exclusive with 1 <= x <= 3 256 if (other instanceof Modulus) { 257 return (modulus == ((Modulus) other).modulus) && (remainder != ((Modulus) other).remainder); 258 } else if (other instanceof NonModulus) { 259 return ((NonModulus) other).hasModulusRemainder(modulus, remainder); 260 } 261 262 return false; 263 } 264 265 // Look up a previously instantiated invariant. 266 public static @Nullable Modulus find(PptSlice ppt) { 267 assert ppt.arity() == 1; 268 for (Invariant inv : ppt.invs) { 269 if (inv instanceof Modulus) { 270 return (Modulus) inv; 271 } 272 } 273 return null; 274 } 275 276 /** 277 * Checks to see if this is obvious over the specified variables. Implements the following checks: 278 * 279 * <pre> 280 * size(x[]) = r (mod m) ⇒ size(x[])-1 = (r-1) (mod m) 281 * </pre> 282 */ 283 @Pure 284 @Override 285 public @Nullable DiscardInfo isObviousDynamically(VarInfo[] vis) { 286 287 // Do not show x-1 = a (mod b). There must be a different mod 288 // invariant over x. JHP: This should really find the invariant rather 289 // than presuming it is true. 290 VarInfo x = vis[0]; 291 if ((x.derived instanceof SequenceLength) && (((SequenceLength) x.derived).shift != 0)) { 292 return new DiscardInfo( 293 this, 294 DiscardCode.obvious, 295 "The invariant " 296 + format() 297 + " is implied by a mod invariant " 298 + "over " 299 + x.name() 300 + " without the offset"); 301 } 302 return null; 303 } 304 305 @Override 306 public @Nullable @NonPrototype Modulus merge( 307 @Prototype Modulus this, List<@NonPrototype Invariant> invs, PptSlice parent_ppt) { 308 309 long new_modulus = 0; 310 boolean some_value_set = false; 311 long some_value = 0; 312 for (Invariant inv : invs) { 313 Modulus m = (Modulus) inv; 314 315 if (!some_value_set && !m.no_samples_seen) { 316 some_value = m.value1; 317 } 318 319 if (m.modulus == 0) { 320 continue; 321 } else if (new_modulus == 0) { 322 new_modulus = m.modulus; 323 } else { 324 new_modulus = MathPlume.gcd(new_modulus, m.modulus); 325 } 326 } 327 if (new_modulus == 0 || new_modulus == 1) { 328 return null; 329 } 330 331 @SuppressWarnings("nullness") // super.merge does not return null 332 @NonNull Modulus result = (Modulus) super.merge(invs, parent_ppt); 333 result.modulus = new_modulus; 334 result.remainder = some_value % result.modulus; 335 336 for (Invariant inv : invs) { 337 Modulus m = (Modulus) inv; 338 if (!m.no_samples_seen) { 339 InvariantStatus status = result.add_modified(m.remainder, 1); 340 if (status == InvariantStatus.FALSIFIED) { 341 return null; 342 } 343 } 344 } 345 346 return result; 347 } 348 349 // TODO: Move this to plume-util. 350 /** 351 * Returns a new list containing only the elements for which the filter returns true. To modify 352 * the collection in place, use {@code Collection#removeIf}. 353 * 354 * <p>Using streams gives an equivalent list but is less efficient and more verbose: 355 * 356 * <pre>{@code 357 * coll.stream().filter(filter).collect(Collectors.toList()); 358 * }</pre> 359 * 360 * @param <T> the type of elements 361 * @param coll a collection 362 * @param filter a predicate 363 * @return a new list with the elements for which the filter returns true 364 */ 365 public static <T> List<T> listFilter(Collection<T> coll, Predicate<? super T> filter) { 366 List<T> result = new ArrayList<>(); 367 for (T elt : coll) { 368 if (filter.test(elt)) { 369 result.add(elt); 370 } 371 } 372 return result; 373 } 374}