class_docstr: Rules that describe how to deduce facts in logic space
  
  When defined, these rules allow implications to quickly be determined
  for a set of facts. For this precomputed deduction tables are used.
  see `deduce_all_facts`   (forward-chaining)
  
  Also it is possible to gather prerequisites for a fact, which is tried
  to be proven.    (backward-chaining)
  
  
  Definition Syntax
  -----------------
  
  a -> b       -- a=T -> b=T  (and automatically b=F -> a=F)
  a -> !b      -- a=T -> b=F
  a == b       -- a -> b & b -> a
  a -> b & c   -- a=T -> b=T & c=T
  # TODO b | c
  
  
  Internals
  ---------
  
  .full_implications[k, v]: all the implications of fact k=v
  .beta_triggers[k, v]: beta rules that might be triggered when k=v
  .prereq  -- {} k <- [] of k's prerequisites
  
  .defined_facts -- set of defined fact names
docstr: Compile rules into internal lookup tables
docstr_tok: ["Compile","rules","into","internal","lookup","tables"]
code: def __init__(self, rules):
      """Compile rules into internal lookup tables"""
  
      if isinstance(rules, str):
          rules = rules.splitlines()
  
      # --- parse and process rules ---
      P = Prover()
  
      for rule in rules:
          # XXX `a` is hardcoded to be always atom
          a, op, b = rule.split(None, 2)
  
          a = Logic.fromstring(a)
          b = Logic.fromstring(b)
  
          if op == "->":
              P.process_rule(a, b)
          elif op == "==":
              P.process_rule(a, b)
              P.process_rule(b, a)
          else:
              raise ValueError("unknown op %r" % op)
  
      # --- build deduction networks ---
      self.beta_rules = []
      for bcond, bimpl in P.rules_beta:
          self.beta_rules.append(
              ({_as_pair(a) for a in bcond.args}, _as_pair(bimpl))
          )
  
      # deduce alpha implications
      impl_a = deduce_alpha_implications(P.rules_alpha)
  
      # now:
      # - apply beta rules to alpha chains  (static extension), and
      # - further associate beta rules to alpha chain (for inference
      # at runtime)
      impl_ab = apply_beta_to_alpha_route(impl_a, P.rules_beta)
  
      # extract defined fact names
      self.defined_facts = {_base_fact(k) for k in impl_ab.keys()}
  
      # build rels (forward chains)
      full_implications = defaultdict(set)
      beta_triggers = defaultdict(set)
      for k, (impl, betaidxs) in impl_ab.items():
          full_implications[_as_pair(k)] = {_as_pair(i) for i in impl}
          beta_triggers[_as_pair(k)] = betaidxs
  
      self.full_implications = full_implications
      self.beta_triggers = beta_triggers
  
      # build prereq (backward chains)
      prereq = defaultdict(set)
      rel_prereq = rules_2prereq(full_implications)
      for k, pitems in rel_prereq.items():
          prereq[k] |= pitems
      self.prereq = prereq
  
imports: from collections import defaultdict
  from typing import Iterator
  from .logic import Logic, And, Or, Not
label_desc: Use image node_id 1 to create a new FactRules object  with example: obj = FactRules(rules)