class_docstr: ai - prover of logic rules
  
  given a set of initial rules, Prover tries to prove all possible rules
  which follow from given premises.
  
  As a result proved_rules are always either in one of two forms: alpha or
  beta:
  
  Alpha rules
  -----------
  
  This are rules of the form::
  
    a -> b & c & d & ...
  
  
  Beta rules
  ----------
  
  This are rules of the form::
  
    &(a,b,...) -> c & d & ...
  
  
  i.e. beta rules are join conditions that say that something follows when
  *several* facts are true at the same time.
code: def _process_rule(self, a, b):
      # right part first
  
      # a -> b & c    -->  a -> b  ;  a -> c
      # (?) FIXME this is only correct when b & c != null !
  
      if isinstance(b, And):
          sorted_bargs = sorted(b.args, key=str)
          for barg in sorted_bargs:
              self.process_rule(a, barg)
  
      # a -> b | c    -->  !b & !c -> !a
      #               -->   a & !b -> c
      #               -->   a & !c -> b
      elif isinstance(b, Or):
          sorted_bargs = sorted(b.args, key=str)
          # detect tautology first
          if not isinstance(a, Logic):  # Atom
              # tautology:  a -> a|c|...
              if a in sorted_bargs:
                  raise TautologyDetected(a, b, "a -> a|c|...")
          self.process_rule(
              And(*[Not(barg) for barg in b.args]), Not(a)
          )
  
          for bidx in range(len(sorted_bargs)):
              barg = sorted_bargs[bidx]
              brest = sorted_bargs[:bidx] + sorted_bargs[bidx + 1 :]
              self.process_rule(And(a, Not(barg)), Or(*brest))
  
      # left part
  
      # a & b -> c    -->  IRREDUCIBLE CASE -- WE STORE IT AS IS
      #                    (this will be the basis of beta-network)
      elif isinstance(a, And):
          sorted_aargs = sorted(a.args, key=str)
          if b in sorted_aargs:
              raise TautologyDetected(a, b, "a & b -> a")
          self.proved_rules.append((a, b))
          # XXX NOTE at present we ignore  !c -> !a | !b
  
      elif isinstance(a, Or):
          sorted_aargs = sorted(a.args, key=str)
          if b in sorted_aargs:
              raise TautologyDetected(a, b, "a | b -> a")
          for aarg in sorted_aargs:
              self.process_rule(aarg, b)
  
      else:
          # both `a` and `b` are atoms
          self.proved_rules.append((a, b))  # a  -> b
          self.proved_rules.append((Not(b), Not(a)))
  
imports: from collections import defaultdict
  from typing import Iterator
  from .logic import Logic, And, Or, Not
label_desc: Use image node_id 6 for calling the Prover obj's underlying member method code with example usage: obj._process_rule(a, b) without return types