static new ThreadLocal<Bool> applyNLLogicFacts_v4_iterate_verbose;

svoid applyNLLogicFacts_v4_iterate(S input, VF2<IfThen, NLLogicChecker_v2.Matching> onRuleFired) {
  applyNLLogicFacts_v4_iterate(input, onRuleFired, mL_facts());
}

svoid applyNLLogicFacts_v4_iterate(S input, VF2<IfThen, NLLogicChecker_v2.Matching> onRuleFired, LS facts) {
  applyNLLogicFacts_v4_iterate(input, onRuleFired, facts, ai_mL_parsedLogicExamples2());
}

svoid applyNLLogicFacts_v4_iterate(S input, VF2<IfThen, NLLogicChecker_v2.Matching> onRuleFired, LS facts, L<IfThen> rules) {
  new NLLogicChecker_v2 c;
  c.matcher = new NLStringMatcher_dollarVars;
  addAll(c.facts, facts);
  c.input = input;
  
  applyNLLogicFacts_v4_iterate(c, onRuleFired, rules);
}

svoid applyNLLogicFacts_v4_iterate(final NLLogicChecker_v2 c, final VF2<IfThen, NLLogicChecker_v2.Matching> onRuleFired, L<IfThen> rules) {
  c.rules = rules;
  for (final IfThen rule : rules) pcall {
    final new NLLogicChecker_v2.Matching m;
    m.verbose = isTrue(applyNLLogicFacts_v4_iterate_verbose!);
    if (m.verbose)
      print("Checking rule (iterating): " + rule);
    c.iterate(rule.in, m, r {
      if (m.verbose) {
        print("Matched " + quote(c.input) + " with rule " + quote(rule));
        printStruct("  ", m.matches);
      }
      callF(onRuleFired, rule, m);
    });
  }
}