docs: private List[] reduceResidual(List[] equatedIds) {
      List[] ans = null;
      for (int i = 0; i < tmpResidual.size(); ) {
        Operator oper = substitute((Operator) tmpResidual.get(i), equatedIds);
        if (oper.getNumIds() > 0 && !Matching.isSimple(oper))
          // Even after substitution, must remain as a residual
          tmpResidual.set(i++, oper);
        else if (oper.getNumIds() == 1) {
          // Eligible as a simple test.  DNF transform the test first to exploit new type
          // information that may have been introduced by the substitution.
          Selector trans = Matching.getTransformer().DNF(oper);
          if (trans instanceof Operator && ((Operator) trans).getOp() == Selector.OR)
            // DNF transformation of the result has revealed a new OR connector.  This can
            // happen iff the process of substitution changed a <> operator of type
            // UNKNOWN into type NUMERIC, which in turn became a disjunction of
            // inequalities.  If this happens we choose to punt since otherwise we would
            // have to redo the entire division of the selector into Conjunctions.  We
            // don't have logic for the case where the transformation reveals a new AND
            // because there is no substitution into a DNF-normalized conjunct that can
            // produce a naked AND.
            tmpResidual.set(i++, oper);
          else {
            // Otherwise, remove the residual and make a new SimpleTest and enter it.  If
            // the new SimpleTest is non-conflicting, proceed, otherwise abandon the
            // Conjunction.
            SimpleTest newTest = new SimpleTestImpl(trans);
            if (!and(newTest))
              return new List[0];
            tmpResidual.remove(i);
            // See if the new test is capable of further reducing the residual
            if (newTest.getKind() == SimpleTest.NULL) {
              if (ans == null)
                ans = new List[] { new ArrayList(), new ArrayList() };
              ans[0].add(newTest.getIdentifier().getName());
              ans[1].add(null);
            }
            else {
              Object newTestValue = newTest.getValue();
              if (newTestValue != null) {
                if (ans == null)
                  ans = new List[] { new ArrayList(), new ArrayList() };
                ans[0].add(newTest.getIdentifier().getName());
                ans[1].add(newTestValue);
              }
            }
          }
        }
        else { // oper.numIds == 0
          // Bail if always false
          Boolean theEval = (Boolean) Matching.getEvaluator().eval(oper);
          if (theEval == null || !(theEval).booleanValue())
            return new List[0];
          // Always true, so just forget this residual
          tmpResidual.remove(i);
        }
      }
      return ans;
    }