text: import random
  
  
  def probability(p):
      """Return true with probability p."""
      return p > random.uniform(0.0, 1.0)
  
  
  def event_values(event, variables):
      """Return a tuple of the values of variables in event.
      >>> event_values ({'A': 10, 'B': 9, 'C': 8}, ['C', 'A'])
      (8, 10)
      >>> event_values ((1, 2), ['C', 'A'])
      (1, 2)
      """
      if isinstance(event, tuple) and len(event) == len(variables):
          return event
      else:
          return tuple([event[var] for var in variables])
  
  
  class BayesNode:
      """A conditional probability distribution for a boolean variable,
      P(X | parents). Part of a BayesNet."""
  
      def __init__(self, X, parents, cpt):
          """X is a variable name, and parents a sequence of variable
          names or a space-separated string. cpt, the conditional
          probability table, takes one of these forms:
  
          * A number, the unconditional probability P(X=true). You can
            use this form when there are no parents.
  
          * A dict {v: p, ...}, the conditional probability distribution
            P(X=true | parent=v) = p. When there's just one parent.
  
          * A dict {(v1, v2, ...): p, ...}, the distribution P(X=true |
            parent1=v1, parent2=v2, ...) = p. Each key must have as many
            values as there are parents. You can use this form always;
            the first two are just conveniences.
  
          In all cases the probability of X being false is left implicit,
          since it follows from P(X=true).
  
          >>> X = BayesNode('X', '', 0.2)
          >>> Y = BayesNode('Y', 'P', {T: 0.2, F: 0.7})
          >>> Z = BayesNode('Z', 'P Q',
          ...    {(T, T): 0.2, (T, F): 0.3, (F, T): 0.5, (F, F): 0.7})
          """
          if isinstance(parents, str):
              parents = parents.split()
  
          # We store the table always in the third form above.
          if isinstance(cpt, (float, int)):  # no parents, 0-tuple
              cpt = {(): cpt}
          elif isinstance(cpt, dict):
              # one parent, 1-tuple
              if cpt and isinstance(list(cpt.keys())[0], bool):
                  cpt = {(v,): p for v, p in cpt.items()}
  
          assert isinstance(cpt, dict)
          for vs, p in cpt.items():
              assert isinstance(vs, tuple) and len(vs) == len(parents)
              assert all(isinstance(v, bool) for v in vs)
              assert 0 <= p <= 1
  
          self.variable = X
          self.parents = parents
          self.cpt = cpt
          self.children = []
  
      def p(self, value, event):
          """Return the conditional probability
          P(X=value | parents=parent_values), where parent_values
          are the values of parents in event. (event must assign each
          parent a value.)
          >>> bn = BayesNode('X', 'Burglary', {T: 0.2, F: 0.625})
          >>> bn.p(False, {'Burglary': False, 'Earthquake': True})
          0.375"""
          assert isinstance(value, bool)
          ptrue = self.cpt[event_values(event, self.parents)]
          return ptrue if value else 1 - ptrue
  
      def sample(self, event):
          """Sample from the distribution for this variable conditioned
          on event's values for parent_variables. That is, return True/False
          at random according with the conditional probability given the
          parents."""
          return probability(self.p(True, event))
  
      def __repr__(self):
          return repr((self.variable, ' '.join(self.parents)))
  
  
  class BayesNet:
      """Bayesian network containing only boolean-variable nodes."""
  
      def __init__(self, node_specs=None):
          """Nodes must be ordered with parents before children."""
          self.nodes = []
          self.variables = []
          node_specs = node_specs or []
          for node_spec in node_specs:
              self.add(node_spec)
  
      def add(self, node_spec):
          """Add a node to the net. Its parents must already be in the
          net, and its variable must not."""
          node = BayesNode(*node_spec)
          assert node.variable not in self.variables
          assert all((parent in self.variables) for parent in node.parents)
          self.nodes.append(node)
          self.variables.append(node.variable)
          for parent in node.parents:
              self.variable_node(parent).children.append(node)
  
      def variable_node(self, var):
          """Return the node for the variable named var.
          >>> burglary.variable_node('Burglary').variable
          'Burglary'"""
          for n in self.nodes:
              if n.variable == var:
                  return n
          raise Exception("No such variable: {}".format(var))
  
      def variable_values(self, var):
          """Return the domain of var."""
          return [True, False]
  
      def __repr__(self):
          return 'BayesNet({0!r})'.format(self.nodes)
  