nodes: 0, module; 1, function_definition; 2, function_name:simplify; 3, parameters; 4, block; 5, identifier:self; 6, expression_statement; 7, comment:# TODO: Refactor DualBase.simplify into different "sub-evals".; 8, comment:# If self is already canonical do nothing.; 9, if_statement; 10, comment:# Otherwise bring arguments into canonical form.; 11, expression_statement; 12, comment:# Create new instance of own class with canonical args.; 13, comment:# TODO: Only create new class if some args changed.; 14, expression_statement; 15, comment:# Literalize before doing anything, this also applies De Morgan's Law; 16, expression_statement; 17, comment:# Associativity:; 18, comment:#     (A & B) & C = A & (B & C) = A & B & C; 19, comment:#     (A | B) | C = A | (B | C) = A | B | C; 20, expression_statement; 21, comment:# Annihilation: A & 0 = 0, A | 1 = 1; 22, if_statement; 23, comment:# Idempotence: A & A = A, A | A = A; 24, comment:# this boils down to removing duplicates; 25, expression_statement; 26, for_statement; 27, if_statement; 28, comment:# Identity: A & 1 = A, A | 0 = A; 29, if_statement; 30, comment:# Complementation: A & ~A = 0, A | ~A = 1; 31, for_statement; 32, comment:# Elimination: (A & B) | (A & ~B) = A, (A | B) & (A | ~B) = A; 33, expression_statement; 34, while_statement; 35, comment:# Absorption: A & (A | B) = A, A | (A & B) = A; 36, comment:# Negative absorption: A & (~A | B) = A & B, A | (~A & B) = A | B; 37, expression_statement; 38, if_statement; 39, comment:# Commutativity: A & B = B & A, A | B = B | A; 40, expression_statement; 41, comment:# Create new (now canonical) expression.; 42, expression_statement; 43, expression_statement; 44, return_statement; 45, comment:"""
          Return a new simplified expression in canonical form from this
          expression.
  
          For simplification of AND and OR fthe ollowing rules are used
          recursively bottom up:
           - Associativity (output does not contain same operations nested)
           - Annihilation
           - Idempotence
           - Identity
           - Complementation
           - Elimination
           - Absorption
           - Commutativity (output is always sorted)
  
          Other boolean objects are also in their canonical form.
          """; 46, attribute; 47, block; 48, assignment; 49, assignment; 50, assignment; 51, assignment; 52, comparison_operator:self.annihilator in expr.args; 53, block; 54, assignment; 55, identifier:arg; 56, attribute; 57, block; 58, comparison_operator:len(args) == 1; 59, block; 60, comparison_operator:self.identity in args; 61, block; 62, identifier:arg; 63, identifier:args; 64, block; 65, assignment; 66, comparison_operator:i < len(args) - 1; 67, block; 68, assignment; 69, comparison_operator:len(args) == 1; 70, block; 71, call; 72, assignment; 73, assignment; 74, identifier:expr; 75, identifier:self; 76, identifier:iscanonical; 77, return_statement; 78, identifier:args; 79, list_comprehension; 80, identifier:expr; 81, call; 82, identifier:expr; 83, call; 84, identifier:expr; 85, call; 86, attribute; 87, attribute; 88, return_statement; 89, identifier:args; 90, list; 91, identifier:expr; 92, identifier:args; 93, if_statement; 94, call; 95, integer:1; 96, return_statement; 97, attribute; 98, identifier:args; 99, expression_statement; 100, if_statement; 101, if_statement; 102, identifier:i; 103, integer:0; 104, identifier:i; 105, binary_operator:len(args) - 1; 106, expression_statement; 107, expression_statement; 108, if_statement; 109, while_statement; 110, expression_statement; 111, identifier:args; 112, call; 113, call; 114, integer:1; 115, return_statement; 116, attribute; 117, argument_list; 118, identifier:expr; 119, call; 120, attribute; 121, True; 122, identifier:self; 123, call; 124, for_in_clause; 125, attribute; 126, argument_list; 127, attribute; 128, argument_list; 129, attribute; 130, argument_list; 131, identifier:self; 132, identifier:annihilator; 133, identifier:expr; 134, identifier:args; 135, attribute; 136, comparison_operator:arg not in args; 137, block; 138, identifier:len; 139, argument_list; 140, subscript; 141, identifier:self; 142, identifier:identity; 143, call; 144, comparison_operator:len(args) == 1; 145, block; 146, comparison_operator:self.NOT(arg) in args; 147, block; 148, call; 149, integer:1; 150, assignment; 151, assignment; 152, not_operator; 153, block; 154, comparison_operator:j < len(args); 155, block; 156, augmented_assignment; 157, attribute; 158, argument_list; 159, identifier:len; 160, argument_list; 161, subscript; 162, identifier:args; 163, identifier:sort; 164, attribute; 165, argument_list; 166, identifier:expr; 167, identifier:iscanonical; 168, attribute; 169, argument_list; 170, identifier:arg; 171, attribute; 172, identifier:self; 173, identifier:__class__; 174, list_splat; 175, identifier:expr; 176, identifier:literalize; 177, identifier:expr; 178, identifier:flatten; 179, identifier:self; 180, identifier:annihilator; 181, identifier:arg; 182, identifier:args; 183, expression_statement; 184, identifier:args; 185, identifier:args; 186, integer:0; 187, attribute; 188, argument_list; 189, call; 190, integer:1; 191, return_statement; 192, call; 193, identifier:args; 194, return_statement; 195, identifier:len; 196, argument_list; 197, identifier:j; 198, binary_operator:i + 1; 199, identifier:ai; 200, subscript; 201, call; 202, expression_statement; 203, continue_statement; 204, identifier:j; 205, call; 206, expression_statement; 207, if_statement; 208, comment:# Find terms where only one arg is different.; 209, expression_statement; 210, for_statement; 211, comment:# If the different arg is a negation simplify the expr.; 212, if_statement; 213, expression_statement; 214, identifier:i; 215, integer:1; 216, identifier:self; 217, identifier:absorb; 218, identifier:args; 219, identifier:args; 220, identifier:args; 221, integer:0; 222, identifier:self; 223, identifier:__class__; 224, list_splat; 225, identifier:arg; 226, identifier:simplify; 227, identifier:self; 228, identifier:args; 229, identifier:args; 230, call; 231, identifier:args; 232, identifier:remove; 233, attribute; 234, identifier:len; 235, argument_list; 236, subscript; 237, attribute; 238, argument_list; 239, attribute; 240, identifier:args; 241, identifier:i; 242, integer:1; 243, identifier:args; 244, identifier:i; 245, identifier:isinstance; 246, argument_list; 247, augmented_assignment; 248, identifier:len; 249, argument_list; 250, assignment; 251, boolean_operator; 252, block; 253, assignment; 254, identifier:arg; 255, attribute; 256, comment:# FIXME: what does this pass Do?; 257, block; 258, comparison_operator:negated is not None; 259, comment:# Cancel out one of the two terms.; 260, block; 261, augmented_assignment; 262, identifier:args; 263, attribute; 264, argument_list; 265, identifier:self; 266, identifier:identity; 267, identifier:args; 268, identifier:args; 269, integer:0; 270, identifier:self; 271, identifier:NOT; 272, identifier:arg; 273, identifier:self; 274, identifier:annihilator; 275, identifier:ai; 276, attribute; 277, identifier:i; 278, integer:1; 279, identifier:args; 280, identifier:aj; 281, subscript; 282, not_operator; 283, comparison_operator:len(ai.args) != len(aj.args); 284, expression_statement; 285, continue_statement; 286, identifier:negated; 287, None; 288, identifier:ai; 289, identifier:args; 290, if_statement; 291, identifier:negated; 292, None; 293, delete_statement; 294, expression_statement; 295, expression_statement; 296, if_statement; 297, if_statement; 298, identifier:j; 299, integer:1; 300, identifier:args; 301, identifier:append; 302, identifier:arg; 303, identifier:self; 304, identifier:dual; 305, identifier:args; 306, identifier:j; 307, call; 308, call; 309, call; 310, augmented_assignment; 311, comparison_operator:arg in aj.args; 312, block; 313, elif_clause; 314, else_clause; 315, subscript; 316, assignment; 317, call; 318, comparison_operator:len(aiargs) == 1; 319, block; 320, else_clause; 321, comparison_operator:len(args) == 1; 322, block; 323, else_clause; 324, identifier:isinstance; 325, argument_list; 326, identifier:len; 327, argument_list; 328, identifier:len; 329, argument_list; 330, identifier:j; 331, integer:1; 332, identifier:arg; 333, attribute; 334, pass_statement; 335, comparison_operator:self.NOT(arg).cancel() in aj.args; 336, block; 337, block; 338, identifier:args; 339, identifier:j; 340, identifier:aiargs; 341, call; 342, attribute; 343, argument_list; 344, call; 345, integer:1; 346, expression_statement; 347, block; 348, call; 349, integer:1; 350, return_statement; 351, comment:# Now the other simplifications have to be redone.; 352, block; 353, identifier:aj; 354, attribute; 355, attribute; 356, attribute; 357, identifier:aj; 358, identifier:args; 359, call; 360, attribute; 361, if_statement; 362, expression_statement; 363, break_statement; 364, identifier:list; 365, argument_list; 366, identifier:aiargs; 367, identifier:remove; 368, identifier:negated; 369, identifier:len; 370, argument_list; 371, assignment; 372, expression_statement; 373, identifier:len; 374, argument_list; 375, subscript; 376, return_statement; 377, identifier:self; 378, identifier:dual; 379, identifier:ai; 380, identifier:args; 381, identifier:aj; 382, identifier:args; 383, attribute; 384, argument_list; 385, identifier:aj; 386, identifier:args; 387, comparison_operator:negated is None; 388, block; 389, else_clause; 390, assignment; 391, attribute; 392, identifier:aiargs; 393, subscript; 394, subscript; 395, assignment; 396, identifier:args; 397, identifier:args; 398, integer:0; 399, call; 400, call; 401, identifier:cancel; 402, identifier:negated; 403, None; 404, expression_statement; 405, block; 406, identifier:negated; 407, None; 408, identifier:ai; 409, identifier:args; 410, identifier:args; 411, identifier:i; 412, identifier:aiargs; 413, integer:0; 414, subscript; 415, call; 416, attribute; 417, argument_list; 418, attribute; 419, argument_list; 420, assignment; 421, expression_statement; 422, break_statement; 423, identifier:args; 424, identifier:i; 425, attribute; 426, argument_list; 427, call; 428, identifier:simplify; 429, identifier:self; 430, identifier:NOT; 431, identifier:arg; 432, identifier:negated; 433, identifier:arg; 434, assignment; 435, identifier:self; 436, identifier:dual; 437, list_splat; 438, attribute; 439, argument_list; 440, identifier:negated; 441, None; 442, identifier:aiargs; 443, identifier:self; 444, identifier:__class__; 445, list_splat; 446, identifier:args
code: def simplify(self):
          """
          Return a new simplified expression in canonical form from this
          expression.
  
          For simplification of AND and OR fthe ollowing rules are used
          recursively bottom up:
           - Associativity (output does not contain same operations nested)
           - Annihilation
           - Idempotence
           - Identity
           - Complementation
           - Elimination
           - Absorption
           - Commutativity (output is always sorted)
  
          Other boolean objects are also in their canonical form.
          """
          # TODO: Refactor DualBase.simplify into different "sub-evals".
  
          # If self is already canonical do nothing.
          if self.iscanonical:
              return self
  
          # Otherwise bring arguments into canonical form.
          args = [arg.simplify() for arg in self.args]
  
          # Create new instance of own class with canonical args.
          # TODO: Only create new class if some args changed.
          expr = self.__class__(*args)
  
          # Literalize before doing anything, this also applies De Morgan's Law
          expr = expr.literalize()
  
          # Associativity:
          #     (A & B) & C = A & (B & C) = A & B & C
          #     (A | B) | C = A | (B | C) = A | B | C
          expr = expr.flatten()
  
          # Annihilation: A & 0 = 0, A | 1 = 1
          if self.annihilator in expr.args:
              return self.annihilator
  
          # Idempotence: A & A = A, A | A = A
          # this boils down to removing duplicates
          args = []
          for arg in expr.args:
              if arg not in args:
                  args.append(arg)
          if len(args) == 1:
              return args[0]
  
          # Identity: A & 1 = A, A | 0 = A
          if self.identity in args:
              args.remove(self.identity)
              if len(args) == 1:
                  return args[0]
  
          # Complementation: A & ~A = 0, A | ~A = 1
          for arg in args:
              if self.NOT(arg) in args:
                  return self.annihilator
  
          # Elimination: (A & B) | (A & ~B) = A, (A | B) & (A | ~B) = A
          i = 0
          while i < len(args) - 1:
              j = i + 1
              ai = args[i]
              if not isinstance(ai, self.dual):
                  i += 1
                  continue
              while j < len(args):
                  aj = args[j]
                  if not isinstance(aj, self.dual) or len(ai.args) != len(aj.args):
                      j += 1
                      continue
  
                  # Find terms where only one arg is different.
                  negated = None
                  for arg in ai.args:
                      # FIXME: what does this pass Do?
                      if arg in aj.args:
                          pass
                      elif self.NOT(arg).cancel() in aj.args:
                          if negated is None:
                              negated = arg
                          else:
                              negated = None
                              break
                      else:
                          negated = None
                          break
  
                  # If the different arg is a negation simplify the expr.
                  if negated is not None:
                      # Cancel out one of the two terms.
                      del args[j]
                      aiargs = list(ai.args)
                      aiargs.remove(negated)
                      if len(aiargs) == 1:
                          args[i] = aiargs[0]
                      else:
                          args[i] = self.dual(*aiargs)
  
                      if len(args) == 1:
                          return args[0]
                      else:
                          # Now the other simplifications have to be redone.
                          return self.__class__(*args).simplify()
                  j += 1
              i += 1
  
          # Absorption: A & (A | B) = A, A | (A & B) = A
          # Negative absorption: A & (~A | B) = A & B, A | (~A & B) = A | B
          args = self.absorb(args)
          if len(args) == 1:
              return args[0]
  
          # Commutativity: A & B = B & A, A | B = B | A
          args.sort()
  
          # Create new (now canonical) expression.
          expr = self.__class__(*args)
          expr.iscanonical = True
          return expr