/**
   * Returns true if the two original variables are related in a way that makes subsequence or
   * subset detection not informative.
   */
  public static boolean isObviousSubSequenceDynamically(
      Invariant inv, VarInfo subvar, VarInfo supervar) {

    VarInfo[] vis = {subvar, supervar};

    ProglangType rep1 = subvar.rep_type;
    ProglangType rep2 = supervar.rep_type;
    if (!(((rep1 == ProglangType.INT_ARRAY) && (rep2 == ProglangType.INT_ARRAY))
        || ((rep1 == ProglangType.DOUBLE_ARRAY) && (rep2 == ProglangType.DOUBLE_ARRAY))
        || ((rep1 == ProglangType.STRING_ARRAY) && (rep2 == ProglangType.STRING_ARRAY))))
      return false;

    if (debug.isLoggable(Level.FINE)) {
      debug.fine(
          "Checking isObviousSubSequenceDynamically " + subvar.name() + " in " + supervar.name());
    }

    Object[] di = isObviousSubSequence(subvar, supervar);
    if (di[1] != null) {
      inv.log("ObvSubSeq- true from isObviousSubSequence: " + di[1]);
      return true;
    }
    debug.fine("  not isObviousSubSequence(statically)");

    PptTopLevel ppt_parent = subvar.ppt;

    // If the elements of supervar are always the same (EltOneOf),
    // we aren't going to learn anything new from this invariant,
    // since each sequence should have an EltOneOf over it.
    if (false) {
      PptSlice1 slice = ppt_parent.findSlice(supervar);
      if (slice == null) {
        System.out.println("No slice: parent =" + ppt_parent);
      } else {
        System.out.println("Slice var =" + slice.var_infos[0]);
        for (Invariant superinv : slice.invs) {
          System.out.println("Inv = " + superinv);
          if (superinv instanceof EltOneOf) {
            EltOneOf eltinv = (EltOneOf) superinv;
            if (eltinv.num_elts() > 0) {
              inv.log(" obvious because of " + eltinv.format());
              return true;
            }
          }
        }
      }
    }

    // Obvious if subvar is always just []
    if (true) {
      PptSlice1 slice = ppt_parent.findSlice(subvar);
      if (slice != null) {
        for (Invariant subinv : slice.invs) {
          if (subinv instanceof OneOfSequence) {
            OneOfSequence seqinv = (OneOfSequence) subinv;
            if (seqinv.num_elts() == 1) {
              Object elt = seqinv.elt();
              if (elt instanceof long[] && ((long[]) elt).length == 0) {
                Debug.log(
                    debug, inv.getClass(), inv.ppt, vis, "ObvSubSeq- True from subvar being []");
                return true;
              }
              if (elt instanceof double[] && ((double[]) elt).length == 0) {
                inv.log("ObvSubSeq- True from subvar being []");
                return true;
              }
            }
          }
        }
      }
    }

    // Check for a[0..i] subseq a[0..j] but i < j.
    VarInfo subvar_super = subvar.isDerivedSubSequenceOf();
    VarInfo supervar_super = supervar.isDerivedSubSequenceOf();

    if (subvar_super != null && subvar_super == supervar_super) {
      // both sequences are derived from the same supersequence
      if ((subvar.derived instanceof SequenceScalarSubsequence
              || subvar.derived instanceof SequenceScalarArbitrarySubsequence)
          && (supervar.derived instanceof SequenceScalarSubsequence
              || supervar.derived instanceof SequenceScalarArbitrarySubsequence)) {
        VarInfo sub_left_var = null,
            sub_right_var = null,
            super_left_var = null,
            super_right_var = null;
        // I'm careful not to access foo_shift unless foo_var has been set
        // to a non-null value, but Java is too stupid to recognize that.
        int sub_left_shift = 42,
            sub_right_shift = 69,
            super_left_shift = 1492,
            super_right_shift = 1776;
        if (subvar.derived instanceof SequenceScalarSubsequence) {
          SequenceScalarSubsequence sub = (SequenceScalarSubsequence) subvar.derived;
          if (sub.from_start) {
            sub_right_var = sub.sclvar();
            sub_right_shift = sub.index_shift;
          } else {
            sub_left_var = sub.sclvar();
            sub_left_shift = sub.index_shift;
          }
        } else if (subvar.derived instanceof SequenceScalarArbitrarySubsequence) {
          SequenceScalarArbitrarySubsequence sub =
              (SequenceScalarArbitrarySubsequence) subvar.derived;
          sub_left_var = sub.startvar();
          sub_left_shift = (sub.left_closed ? 0 : 1);
          sub_right_var = sub.endvar();
          sub_right_shift = (sub.right_closed ? 0 : -1);
        } else {
          Assert.assertTrue(false);
        }
        if (supervar.derived instanceof SequenceScalarSubsequence) {
          SequenceScalarSubsequence super_ = (SequenceScalarSubsequence) supervar.derived;
          if (super_.from_start) {
            super_right_var = super_.sclvar();
            super_right_shift = super_.index_shift;
          } else {
            super_left_var = super_.sclvar();
            super_left_shift = super_.index_shift;
          }
        } else if (supervar.derived instanceof SequenceScalarArbitrarySubsequence) {
          SequenceScalarArbitrarySubsequence super_ =
              (SequenceScalarArbitrarySubsequence) supervar.derived;
          super_left_var = super_.startvar();
          super_left_shift = (super_.left_closed ? 0 : 1);
          super_right_var = super_.endvar();
          super_right_shift = (super_.right_closed ? 0 : -1);
        } else {
          Assert.assertTrue(false);
        }
        boolean left_included, right_included;
        if (super_left_var == null) left_included = true;
        else if (sub_left_var == null) // we know super_left_var != null here
        left_included = false;
        else
          left_included =
              VarInfo.compare_vars(
                  super_left_var, super_left_shift, sub_left_var, sub_left_shift, true /* <= */);
        if (super_right_var == null) right_included = true;
        else if (sub_right_var == null) // we know super_right_var != null here
        right_included = false;
        else
          right_included =
              VarInfo.compare_vars(
                  super_right_var,
                  super_right_shift,
                  sub_right_var,
                  sub_right_shift,
                  false /* >= */);
        //         System.out.println("Is " + subvar.name() + " contained in "
        //                            + supervar.name()
        //                            + "? left: " + left_included + ", right: "
        //                            + right_included);
        if (left_included && right_included) {
          inv.log("ObvSubSeq- True a[0..i] subseq a[0..j] and i < j");
          return true;
        }
      } else if ((subvar.derived instanceof SequenceStringSubsequence)
          && (supervar.derived instanceof SequenceStringSubsequence)) {
        // Copied from just above
        SequenceStringSubsequence sss1 = (SequenceStringSubsequence) subvar.derived;
        SequenceStringSubsequence sss2 = (SequenceStringSubsequence) supervar.derived;
        VarInfo index1 = sss1.sclvar();
        int shift1 = sss1.index_shift;
        boolean start1 = sss1.from_start;
        VarInfo index2 = sss2.sclvar();
        int shift2 = sss2.index_shift;
        boolean start2 = sss2.from_start;
        if (start1 == start2)
          if (VarInfo.compare_vars(index1, shift1, index2, shift2, start1)) {
            inv.log("True from comparing indices");
            return true;
          }
      } else {
        Assert.assertTrue(
            false,
            "how can this happen? "
                + subvar.name()
                + " "
                + subvar.derived.getClass()
                + " "
                + supervar.name()
                + " "
                + supervar.derived.getClass());
      }
    }

    // Also need to check A[0..i] subseq A[0..j] via compare_vars.

    // A subseq B[0..n] => A subseq B

    List<Derivation> derivees = supervar.derivees();
    // For each variable derived from supervar ("B")
    for (Derivation der : derivees) {
      // System.out.println("  ... der = " + der.getVarInfo().name() + " " + der);
      if (der instanceof SequenceScalarSubsequence) {
        // If that variable is "B[0..n]"
        VarInfo supervar_part = der.getVarInfo();
        // Get the canonical version; being equal to it is good enough.
        if (supervar_part.get_equalitySet_leader() == subvar) {
          Debug.log(debug, inv.getClass(), inv.ppt, vis, "ObvSubSeq- True from canonical leader");
          return true;
        }

        if (supervar_part.isCanonical()) {
          if (subvar == supervar_part) {
            System.err.println(
                "Error: variables "
                    + subvar.name()
                    + " and "
                    + supervar_part.name()
                    + " are identical.  Canonical");
            System.err.println(subvar.isCanonical());
            System.err.println(supervar_part.isCanonical());
            throw new Error();
          }

          // Check to see if there is a subsequence over the supervar
          if (ppt_parent.is_subsequence(subvar, supervar_part)) {
            if (Debug.logOn())
              inv.log(
                  "ObvSubSeq- true from A subseq B[0..n] "
                      + subvar.name()
                      + "/"
                      + supervar_part.name());
            return (true);
          }
        }
      }
    }
    return false;
  }
  public DiscardInfo isObviousDynamically(VarInfo[] vis) {
    DiscardInfo super_result = super.isObviousDynamically(vis);
    if (super_result != null) {
      return super_result;
    }

    if (!hasSeenNonTrivialSample()) {
      return new DiscardInfo(
          this, DiscardCode.obvious, "No samples sequences of size >=2 were seen. Vacuously true.");
    }

    EltOneOf eoo = EltOneOf.find(ppt);
    if ((eoo != null) && eoo.enoughSamples() && (eoo.num_elts() == 1)) {
      return new DiscardInfo(this, DiscardCode.obvious, "The sequence contains all equal values.");
    }

    {
      // some relations imply others as follows: < -> <=, > -> >=
      // == -> <=, >=

      // This code lets through some implied invariants, here is how:
      // In the ESC, JML, Java modes of output, the invariants are guarded
      // before printing.  This guarding makes some of the invariants that
      // are searched for (example, NoDuplicates) unable to be found since it
      // won't find something of the form (a -> NoDuplicates).  This can
      // cause cases to exist, for example, the invariants (a -> b[] sorted
      // by !=), (a -> b[] has no duplicates) existing in the same ppt where
      // one is obviously implied by the other. I am not sure currently of
      // the best way with dealing with this, but I am going to allow these
      // other invariants to exist for now because they are not wrong, just
      // obvious.

      for (Invariant inv : ppt.invs) {}
    }

    // sorted by (any operation) for an entire sequence -> sorted by that same
    // operation for a subsequence

    // also, sorted by < for entire -> sorted by <= for subsequence
    //       sorted by > for entire -> sorted by >= for subsequence

    Derivation deriv = vis[0].derived;

    if (deriv != null
        && ((deriv instanceof SequenceScalarSubsequence)
            || (deriv instanceof SequenceFloatSubsequence))) {
      // Find the slice with the full sequence, check for an invariant of this type
      PptSlice sliceToCheck;

      if (deriv instanceof SequenceScalarSubsequence)
        sliceToCheck = ppt.parent.findSlice(((SequenceScalarSubsequence) deriv).seqvar());
      else sliceToCheck = ppt.parent.findSlice(((SequenceFloatSubsequence) deriv).seqvar());

      if (sliceToCheck != null) {
        for (Invariant inv : sliceToCheck.invs) {

          if (inv.getClass().equals(getClass())) {
            String discardString =
                "This is a subsequence of a sequence for which the same invariant holds.";
            return new DiscardInfo(this, DiscardCode.obvious, discardString);
          }
        }
      }
    }

    return null;
  }
    private void add(PptTopLevel ppt, ValueTuple vt) {
      // Add the sample to any splitters
      if (ppt.has_splitters()) {
        for (PptSplitter ppt_split : ppt.splitters) {
          PptConditional ppt_cond = ppt_split.choose_conditional(vt);
          if (ppt_cond != null) add(ppt_cond, vt);
          else debug.fine(": sample doesn't pick conditional");
        }
      }

      // if this is a numbered exit, apply to the combined exit as well
      if (!(ppt instanceof PptConditional) && ppt.ppt_name.isNumberedExitPoint()) {
        PptTopLevel parent = all_ppts.get(ppt.ppt_name.makeExit());
        if (parent != null) {
          parent.get_missingOutOfBounds(ppt, vt);
          add(parent, vt);
        }
      }

      // If the point has no variables, skip it
      if (ppt.var_infos.length == 0) return;

      // We should have received sample here before, or there is nothing
      // to check.
      // Yoav added: It can be that the different dtrace and inv files have different program points
      if (false && ppt.num_samples() <= 0)
        Assert.assertTrue(
            ppt.num_samples() > 0,
            "ppt " + ppt.name + " has 0 samples and " + ppt.var_infos.length + " variables");

      // Loop through each slice
      slice_loop:
      for (Iterator<PptSlice> i = ppt.views_iterator(); i.hasNext(); ) {
        PptSlice slice = i.next();
        if (debug_detail.isLoggable(Level.FINE))
          debug_detail.fine(
              ": processing slice " + slice + "vars: " + Debug.toString(slice.var_infos, vt));

        // If any variables are missing, skip this slice
        for (int j = 0; j < slice.var_infos.length; j++) {
          VarInfo v = slice.var_infos[j];
          int mod = vt.getModified(v);
          if (v.isMissing(vt)) {
            if (debug_detail.isLoggable(Level.FINE))
              debug_detail.fine(": : Skipping slice, " + v.name() + " missing");
            continue slice_loop;
          }
          if (v.missingOutOfBounds()) {
            if (debug_detail.isLoggable(Level.FINE))
              debug.fine(": : Skipping slice, " + v.name() + " out of bounds");
            continue slice_loop;
          }
        }

        // Loop through each invariant
        for (Invariant inv : slice.invs) {
          if (debug_detail.isLoggable(Level.FINE))
            debug_detail.fine(": : Processing invariant: " + inv);
          if (!inv.isActive()) {
            if (debug_detail.isLoggable(Level.FINE))
              debug_detail.fine(": : skipped non-active " + inv);
            continue;
          }

          // Yoav added
          if (!activeInvariants.contains(inv)) {
            // System.out.printf ("skipping invariant %s:%s\n", inv.ppt.name(),
            //                   inv.format());
            continue;
          }

          // String invRep = invariant2str(ppt, inv);
          testedInvariants.add(inv);

          InvariantStatus status = inv.add_sample(vt, 1);
          sample_cnt++;
          if (status != InvariantStatus.NO_CHANGE) {
            LineNumberReader lnr = FileIO.data_trace_state.reader;
            String line = (lnr == null) ? "?" : String.valueOf(lnr.getLineNumber());
            if (!quiet) {
              output_stream.println(
                  "At ppt "
                      + ppt.name
                      + ", Invariant '"
                      + inv.format()
                      + "' invalidated by sample "
                      + Debug.toString(slice.var_infos, vt)
                      + "at line "
                      + line
                      + " in file "
                      + FileIO.data_trace_state.filename);
            }
            failedInvariants.add(inv);
            activeInvariants.remove(inv);
            error_cnt++;
          }
        }
      }
    }
 private static String invariant2str(PptTopLevel ppt, Invariant inv) {
   return ppt.name + " == " + inv.repr() + inv.getClass() + inv.varNames() + ": " + inv.format();
 }
  private static void checkInvariants() throws IOException {
    // Read the invariant file
    PptMap ppts = FileIO.read_serialized_pptmap(inv_file, true);

    // Yoav: make sure we have unique invariants
    InvariantFilters fi = InvariantFilters.defaultFilters();
    // Set<String> allInvariantsStr = new HashSet<String>();
    Set<Invariant> allInvariants = new HashSet<Invariant>();
    for (PptTopLevel ppt : ppts.all_ppts())
      for (Iterator<PptSlice> i = ppt.views_iterator(); i.hasNext(); ) {
        PptSlice slice = i.next();
        for (Invariant inv : slice.invs) {
          if (doConf && inv.getConfidence() < Invariant.dkconfig_confidence_limit) {
            // System.out.printf ("inv ignored (conf): %s:%s\n", inv.ppt.name(),
            //                   inv.format());
            continue;
          }

          if (doFilter && fi.shouldKeep(inv) == null) {
            // System.out.printf ("inv ignored (filter): %s:%s\n",
            //                     inv.ppt.name(), inv.format());
            continue;
          }
          activeInvariants.add(inv);

          // String n = invariant2str(ppt, inv);
          // if (!allInvariants.contains(inv) && allInvariantsStr.contains(n)) throw new
          // Daikon.TerminationMessage("Two invariants have the same ppt.name+inv.rep:"+n);
          allInvariants.add(inv);
          // allInvariantsStr.add(n);
        }
      }

    // Read and process the data trace files
    FileIO.Processor processor = new InvariantCheckProcessor();

    Daikon.FileIOProgress progress = new Daikon.FileIOProgress();
    progress.start();
    progress.clear();
    FileIO.read_data_trace_files(dtrace_files, ppts, processor, false);
    progress.shouldStop = true;
    System.out.println();
    System.out.printf(
        "%s: %,d errors found in %,d samples (%s)\n",
        inv_file, error_cnt, sample_cnt, toPercentage(error_cnt, sample_cnt));
    int failedCount = failedInvariants.size();
    int testedCount = testedInvariants.size();
    String percent = toPercentage(failedCount, testedCount);
    System.out.println(
        inv_file
            + ": "
            + failedCount
            + " false positives, out of "
            + testedCount
            + ", which is "
            + percent
            + ".");
    if (false) {
      for (Invariant inv : failedInvariants) {
        System.out.printf("+%s:%s\n", inv.ppt.name(), inv.format());
      }
    }
  }