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