Package groupthink :: Module aatree
[hide private]
[frames] | no frames]

Source Code for Module groupthink.aatree

  1  """ 
  2  a helper library providing a number of fundamental tree data structures, 
  3  ultimately intended to improve performance in L{SimpleStringTree}.  These data 
  4  structures are sufficiently generic that they might also be of use for other 
  5  purposes. 
  6  """ 
7 8 -class Node:
9 """ 10 Node represents a node in a binary tree. It is entirely passive, acting 11 as a container much a like a C struct. Its unusual features include a 12 distinction between annotation and value, and references not only to its children 13 but also to its parent. 14 """ 15 parent = None 16 """ 17 @ivar: The parent of the node, or None if the node has no parent 18 @type: L{Node} 19 """ 20 leftchild = None 21 """ 22 @ivar: The left child of the node, or None if the node has no left child 23 @type: L{Node} 24 """ 25 rightchild = None 26 """ 27 @ivar: The right child of the node, or None if the node has no right child 28 @type: L{Node} 29 """ 30 annotation = None 31 """ 32 @ivar: A piece of data associated with a node, assigned by a 33 tree-specific function and satisfying a related constraint. 34 @type: tree-specific type 35 """ 36 value = None 37 """ 38 @ivar: A value assigned by the user of the tree. In monoid-annotated 39 trees, only the leaves typically have values, and internal nodes will 40 therefore have a value of None. 41 The purpose of 42 the annotation is to permit the construction of monoid-annotated trees, 43 which often have both highly constrained annotations and completely 44 unconstrained values. 45 See, for example, 46 U{http://scienceblogs.com/goodmath/2009/05/finally_finger_trees.php}. 47 Non-monoid-annotated trees may ignore this variable. 48 @type: typically unconstrained 49 """
50
51 -class AANode(Node):
52 """ 53 An AANode is a L{Node} that can be used in an AA tree. AA trees require 54 that each node have an associated level. 55 """ 56 level = 1 57 """ 58 @ivar: The level index for the node. For more information on the level 59 in AA trees, see U{http://en.wikipedia.org/wiki/AA_tree}. 60 @type: int 61 """
62
63 -class Walker:
64 """ 65 A Walker walks up and down trees according to a specific objective. Its 66 goal is typically to find some particular element, such as the element 67 at a specific index, or with a specific value. 68 69 Walker is an abstract base class. 70 """
71 - def descend(self, node):
72 """ 73 @type node: Node 74 @param node: A node from which to descend. 75 @rtype: int 76 @return: descend must return 0 if the node in question is the one desired, 77 -1 if the left child would be better, or 1 if the right child would be 78 better. 79 """ 80 raise
81 - def prepare_descend(self, *args):
82 """An optional method to prepare for descent""" 83 raise
84 - def ascend(self, node):
85 """ 86 @type node: Node 87 @param node: A node from which to ascend 88 @rtype: bool 89 @return: ascend should return True iff it should run again on the parent 90 of node. It should set the walker's internal state such that a 91 subsequent descent would retrace these steps.""" 92 raise
93 - def prepare_ascend(self, *args):
94 """ An optional method to prepare for ascent""" 95 raise
96
97 -class SearchWalker(Walker):
98 """ 99 A L{Walker} that can be used to search a classic binary tree. This class 100 exists solely to expose the concept of the Walker. As a convention, the 101 binary tree being search must satisfy:: 102 leftchild.annotation < annotation < rightchild.annotation 103 """ 104 val = 0 105 compare = cmp
106 - def prepare_descend(self, val, comparator=cmp):
107 self.val = val 108 self.compare = comparator
109 - def descend(self, node):
110 x = self.compare(node.annotation, self.val) 111 return x
112 - def ascend(self, node):
113 self.val = node.annotation 114 return False
115
116 -class RandomWalker(Walker):
117 """ 118 An experimental Walker that could be used to select a random 119 sub-leaf position in a binary tree. ascent is not implemented. 120 """
121 - def prepare_descend(self):
122 from random import choice as choice 123 self.choice = choice
124 - def descend(self, node):
125 return self.choice((-1,1))
126
127 -def descend(node, walker): #move down from a root node
128 """ 129 @type node: L{Node} 130 @param node: A node from which to start descent 131 @type walker: L{Walker} 132 @param walker: A L{Walker} to apply recursively from node. 133 @rtype: tuple(L{Node}, int) 134 @return: If the L{Walker} accepted a L{Node} n, the function 135 returns (n, 0). Otherwise, the function returns (L, 1), or (L, -1), 136 where L is a node that has no right or left child, respectively. 137 These return values typically indicate that the walker is looking for a 138 node that is not present in the tree, but, if it were present, would be 139 in the (currently empty) subtree right (or left) of L. 140 """ 141 x = walker.descend(node) 142 while x != 0: 143 if x == 1: 144 if node.rightchild is None: 145 return (node, 1) 146 else: 147 node = node.rightchild 148 else: #x == -1 149 if node.leftchild is None: 150 return (node, -1) 151 else: 152 node = node.leftchild 153 x = walker.descend(node) 154 return (node, 0) 155
156 -def ascend(node, walker):
157 """ 158 @type node: L{Node} 159 @type walker: L{Walker} 160 @param node: the L{Node} from which to ascend. 161 @param walker: The L{walker} with which to ascend. 162 @rtype: None 163 ascend causes walker to ascend until walker asks to stop or the tree's 164 root node is reached. Nothing is returned; instead, state is accumulated 165 inside the walker 166 """ 167 while node is not None and walker.ascend(node): 168 node = node.parent
169
170 -def search(root, val):
171 """ 172 @type root: L{Node} 173 @type val: comparable 174 Searches a correctly sorted binary tree, starting with the root Node, for 175 val. Returns a node that contains val if val is present, otherwise a node 176 for which val would be an acceptable child value. 177 178 Provided solely as an example.""" 179 w = SearchWalker 180 w.prepared_descend(val) 181 return descend(root, w)
182
183 -def findmin(root):
184 """ 185 Simple search function for the leftmost element in a tree, or subtree. 186 @type root: L{Node} 187 @param root: The node from which to start walking. 188 @rtype: L{Node} 189 @return: the leftmost element in the subtree of root 190 """ 191 while root.leftchild is not None: 192 root = root.leftchild 193 return root
194
195 -def findmax(root):
196 """ 197 Simple search function for the rightmost element in a tree, or subtree. 198 @type root: L{Node} 199 @param root: The node from which to start walking. 200 @rtype: L{Node} 201 @return: the rightmost element in the subtree of root 202 """ 203 while root.rightchild is not None: 204 root = root.rightchild 205 return root
206
207 -class MonoidTree:
208 """ 209 A Monoid Annotation Tree is a binary tree whose nodes are each annotated 210 by values from some monoid. The annotation of an internal node is computed 211 by applying the operation to the annotations of its children. The annotation of a leaf 212 node is specified by the user. Every node must either have two children or 213 be a leaf node. 214 215 Each leaf node may also be associated with an arbitrary opaque value of the user's 216 choosing. This node and value will remain associated.""" 217 makenode = Node 218 """ 219 @cvar: a factory function that returns nodes of the type required 220 for this tree. It is provided so that subclasses can easily override it 221 to use subtypes of L{Node}. 222 """
223 - def __init__(self, operation, rootnode):
224 """ 225 Let M be the monoid type. 226 @type operation: f(M,M) -> M 227 @param operation: a function taking two arguments from the monoid set 228 and returning another element from the monoid set. The monoid 229 operation must obey an associativity criterion; see U{http://en.wikipedia.org/wiki/Monoid#Definition}. 230 @type rootnode: L{makenode} 231 @param rootnode: The root node for the tree. rootnode must have a valid annotation, and its parent and children must be None.""" 232 self.op = operation 233 self.root = rootnode
234 - def _update(self, node, sentinel=None):
235 """node must be an internal node""" 236 while node is not sentinel: 237 #oldval = node.annotation 238 node.annotation = self.op(node.leftchild.annotation, node.rightchild.annotation) 239 #if oldval == node.annotation: 240 # #this node has not changed, so nodes above it will also not have changed 241 # break 242 #else: 243 node = node.parent
244 _update_add = _update 245 _update_del = _update
260 - def addleft(self, new, old):
261 """ 262 Add a new leaf node to the left of an old leaf node. The new node's 263 parent will be assigned to be a new internal node. 264 @type new: makenode 265 @param new: a new node, not yet present in the tree 266 @type old: makenode 267 @param old: A leaf node currently in the tree. 268 """ 269 newparent = self._split_link(old) 270 newparent.rightchild = old 271 newparent.leftchild = new 272 new.parent = newparent 273 self._update_add(newparent)
274 - def addright(self, new, old):
275 """ 276 Add a new leaf node to the right of an old leaf node. The new node's 277 parent will be assigned to be a new internal node. 278 @type new: makenode 279 @param new: a new node, not yet present in the tree 280 @type old: makenode 281 @param old: A leaf node currently in the tree. 282 """ 283 newparent = self._split_link(old) 284 newparent.rightchild = new 285 newparent.leftchild = old 286 new.parent = newparent 287 self._update_add(newparent)
288 - def add(self, new, walker):
289 """ 290 Add a new leaf node at a position determined by walker. Acts as a 291 thin wrapper around L{aatree.descend} and addleft or addright. 292 If the walker descent process returns a position of zero, the new node 293 will be added left of the returned node. 294 @type new: makenode 295 @param new: a new node to add to the tree. 296 @type walker: L{Walker} 297 @param walker: A walker that will determine the insertion position 298 """ 299 leaf, position = descend(self.root, walker) 300 assert leaf.leftchild is None 301 assert leaf.rightchild is None 302 if position == 1: 303 self.addright(new, leaf) 304 else: #Makes left the default for duplicate values 305 self.addleft(new, leaf)
306 - def remove(self, leaf):
307 """ 308 Remove a leaf node. The leaf will be removed from the tree, and the 309 tree will retain no reference to it. 310 @type leaf: makenode 311 @param leaf: A leaf node to remove. 312 """ 313 p = leaf.parent 314 if p.leftchild is leaf: 315 sibling = p.rightchild 316 else: 317 assert p.rightchild is leaf 318 sibling = p.leftchild 319 gp = p.parent 320 if gp.leftchild is p: 321 gp.leftchild = sibling 322 elif gp.rightchild is p: 323 gp.rightchild = sibling 324 sibling.parent = gp 325 # The only remaining reference to p is now in leaf itself, and the only 326 # remaining reference to leaf is in the user's hands 327 self._update_del(gp)
328 - def change_annotation(self, leaf, newann):
329 """ 330 When changing the annotation of a leaf, it is required to use this method, 331 so that the change may be propagated up through the tree according to the 332 operation. Note that no such method is needed for the value of a leaf, 333 because the value is opaque to the tree. 334 @type leaf: makenode 335 @param leaf: the node whose annotation should be altered 336 @type newann: element of the monoid set 337 @param newann: the new annotation which should be associated with leaf 338 """ 339 assert leaf.leftchild is None 340 assert leaf.rightchild is None 341 leaf.annotation = newann 342 self._update(leaf.parent)
343 - def getnext(self, leaf, skip=None):
344 """ 345 Get the next rightward leaf node from leaf. Optionally, skip any nodes 346 meeting some criterion. 347 @type leaf: makenode 348 @param leaf: The leaf node whose next rightward neighbor will be returned 349 @type skip: f(makenode)->bool 350 @param skip: An optional function returning True if a given node should 351 be skipped. This function will be called on each internal node during 352 the search, not only on leaf nodes. It will typically implement a 353 criterion on the annotation such that a node will be skipped only 354 if every descendant of that node would also be skipped if considered 355 individually. 356 """ 357 assert leaf.leftchild is None 358 assert leaf.rightchild is None 359 node = leaf 360 while ((node.parent is not None) and 361 ((node.parent.rightchild is node) or 362 ((skip is not None) and skip(node.parent.rightchild)))): 363 # Move up until you can move right 364 node = node.parent 365 if (node.parent is not None) and (node.parent.leftchild is node): 366 node = node.parent.rightchild 367 while node.leftchild is not None: 368 # Move down, staying as far left as possible. 369 assert node.rightchild is not None 370 if (skip is not None) and skip(node.leftchild): 371 node = node.rightchild 372 else: 373 node = node.leftchild 374 return node 375 else: 376 raise StopIteration("No next node")
377
378 - def _build_subtree(self, nodes):
379 #FIXME: This cannot be helpful because insertion of a subtree requires 380 #rebalancing the main tree by more than one level, which is not possible 381 #with a single invocation of skew and split 382 L = len(nodes) 383 if L == 1: 384 return nodes[0] 385 else: 386 next = [] 387 sentinel = 'g' #must not be None, since None is the root sentinel 388 if L % 2: 389 n2 = nodes.pop() 390 n1 = nodes.pop() 391 newnode = self.makenode() 392 newnode.parent=sentinel #totally arbitrary constant 393 newnode.leftchild = n1 394 n1.parent = newnode 395 newnode.rightchild = n2 396 n2.parent = newnode 397 self._update_add(newnode, sentinel) 398 nodes.append(newnode) 399 for i in xrange(0,L,2): 400 n1,n2 = nodes[i:(i+2)] 401 newnode = self.makenode() 402 newnode.parent=sentinel #totally arbitrary constant 403 newnode.leftchild = n1 404 n1.parent = newnode 405 newnode.rightchild = n2 406 n2.parent = newnode 407 self._update_add(newnode, sentinel)
408
409 410 411 -class SumWalker(Walker):
412 """ 413 SumWalker is designed to walk over full trees where each leaf has annotation 1 414 and the monoid is +. Target is the zero-indexed position of the target node. 415 416 There is one exception: the last node in every tree has annotation 0.""" 417 target = None 418 offset = None
419 - def prepare_descend(self, target):
420 self.target = target 421 self.offset = 0
422 - def descend(self, node):
423 if node.annotation == 0: #empty leaf at the last position 424 assert self.target == self.offset 425 return -1 426 elif node.leftchild is None: #leaf node case 427 assert node.rightchild is None 428 assert self.target == self.offset 429 return 0 430 else: #internal node case 431 p = self.offset + node.leftchild.annotation 432 if p <= self.target: 433 self.offset = p 434 return 1 435 else: 436 return -1
437 - def prepare_ascend(self):
438 self.target = 0
439 - def ascend(self, node):
440 if node.parent is not None: 441 if node.parent.rightchild is node: 442 self.target += node.parent.leftchild.annotation 443 else: 444 assert node.parent.leftchild is node 445 return True 446 else: 447 return False
448
449 -class TreeList:
450 """Implements a list-like interface, backed by a MonoidTree""" 451 _treetype = MonoidTree
452 - def __init__(self):
453 self._makenode = self._treetype.makenode 454 r = self._makenode() 455 r.annotation = 0 456 from operator import add 457 self._tree = self._treetype(add, r) 458 self._walker = SumWalker() 459 # We regard the fields of this walker as public API, and manipulate 460 # them directly 461 self._index = {}
462 - def __len__(self):
463 return self._tree.root.annotation
464 - def _getnode(self, i):
465 self._walker.prepare_descend(i) 466 node, pos = descend(self._tree.root, self._walker) 467 assert pos == 0 468 return node
469 - def __getitem__(self, s):
470 if isinstance(s, int): 471 node = self._getnode(s) 472 return node.value 473 else: 474 raise UnimplementedError
475 - def __setitem__(self, s, v):
476 if isinstance(s, int): 477 if s < len(self): 478 node = self._getnode(s) 479 oldv = node.value 480 self._index[oldv].remove(node) 481 if not self._index[oldv]: 482 del self._index[oldv] 483 node.value = v 484 if v not in self._index: 485 self._index[v] = set() 486 self._index[v].add(node) 487 else: 488 self.insert(s, v) 489 else: 490 raise UnimplementedError
491 - def __delitem__(self, s):
492 if isinstance(s, int): 493 if s < len(self): 494 node = self._getnode(s) 495 oldv = node.value 496 self._index[oldv].remove(node) 497 if not self._index[oldv]: 498 del self._index[oldv] 499 self._tree.remove(node) 500 else: 501 raise UnimplementedError
502 - def insert(self, p, v):
503 if p > len(self): 504 raise IndexError("Index out of range") 505 self._walker.prepare_descend(p) 506 newnode = self._makenode() 507 newnode.annotation = 1 508 newnode.value = v 509 self._tree.add(newnode, self._walker) 510 if v not in self._index: 511 self._index[v] = set() 512 self._index[v].add(newnode)
513 - def index(self, v):
514 """index returns some index such that self[i] == v. No promises about ordering.""" 515 self._walker.prepare_ascend() 516 for node in self._index[v]: #Pull one arbitrary node out of the set 517 assert node.value == v 518 ascend(node, self._walker) 519 break 520 return self._walker.target
521
522 -class TreeHideList:
523 """Implements the EagerHideList interface, backed by a MonoidTree""" 524 _treetype = MonoidTree
525 - class MultiSumWalker(Walker):
526 index = 0 527 target = 0 528 offset = 0
529 - def prepare_descend(self, target, index):
530 self.index = index 531 self.target = target 532 self.offset = 0
533 - def descend(self, node):
534 if node.annotation == (0,0): #empty leaf at the last position 535 assert self.target == self.offset 536 return -1 537 elif node.leftchild is None: #leaf node case 538 assert node.rightchild is None 539 assert self.target == self.offset 540 return 0 541 else: #internal node case 542 p = self.offset + node.leftchild.annotation[self.index] 543 if p <= self.target: 544 self.offset = p 545 return 1 546 else: 547 return -1
548 - def prepare_ascend(self, index):
549 self.target = 0 550 self.index = index
551 - def ascend(self, node):
552 if node.parent is not None: 553 if node.parent.rightchild is node: 554 self.target += node.parent.leftchild.annotation[self.index] 555 else: 556 assert node.parent.leftchild is node 557 return True 558 else: 559 return False
560 561 @staticmethod
562 - def op(a,b):
563 # Convention: a[0] is visible elements. a[1] is all elements. 564 return (a[0] + b[0], a[1] + b[1])
565 566 @staticmethod
567 - def skip(node):
568 return node.annotation[0] == 0
569
570 - def __init__(self):
571 self._makenode = self._treetype.makenode 572 r = self._makenode() 573 r.annotation = (0, 0) 574 self._tree = self._treetype(self.op, r) 575 self._walker = self.MultiSumWalker() 576 # We regard the fields of this walker as public API, and manipulate 577 # them directly 578 self._index = {} 579 unique = True 580 if unique: 581 self._index_lookup = self._index.__getitem__ 582 self._index_assign = self._index.__setitem__ 583 else: 584 self._index_lookup = self._index_lookup_set 585 self._index_assign = self._index_assign_set
586 - def _index_lookup_set(self, item):
587 for v in self._index[item]: 588 return v
589 - def _index_assign_set(self, key, value):
590 if key not in self._index: 591 self._index[key] = set() 592 self._index[key].add(value)
593 - def __len__(self):
594 return self._tree.root.annotation[0]
595 - def _getnode(self, i, a):
596 self._walker.prepare_descend(i, a) 597 node, pos = descend(self._tree.root, self._walker) 598 assert (pos == 0) or ((pos == -1) and (i == len(self))) 599 return node
600 - def __getitem__(self, s):
601 if isinstance(s, int): 602 if s < len(self): #FIXME: negative indices 603 node = self._getnode(s, 0) 604 return node.value 605 else: 606 raise IndexError("Index out of range") 607 else: 608 start, stop, stride = s.indices(len(self)) 609 if start == stop: 610 return [] 611 elif stride == 1: 612 # runs in k + log(N) (amortized) 613 nodes = [self._getnode(start,0)] 614 k = stop - start 615 while len(nodes) < k: 616 nodes.append(self._tree.getnext(nodes[-1],self.skip)) 617 return [n.value for n in nodes] 618 else: 619 #FIXME: runs in k*log(N), could be reduced to k*log(step) + log(N) 620 return [self[i] for i in xrange(start,stop,stride)]
621 - def index(self, v, visible=True):
622 """index returns some index such that self[i] == v. No promises about ordering.""" 623 self._walker.prepare_ascend(0 if visible else 1) 624 node = self._index_lookup(v) #Pull one arbitrary node out of the set 625 assert node.value == v 626 ascend(node, self._walker) 627 return self._walker.target
628 - def hide(self, position, length):
629 #self.__getitem__ is eager, so we acquire the list of nodes before 630 #acting on them 631 node = self._getnode(position,0) 632 for i in xrange(position+1,position+length): 633 self._tree.change_annotation(node,(0,1)) 634 node = self._tree.getnext(node, self.skip) 635 self._tree.change_annotation(node,(0,1))
636 #FIXME: runs in length*log(N). Could be reduced using a priority queue, 637 #possibly to length + log(N)
638 - def getitem_all(self, s):
639 if isinstance(s, int): 640 node = self._getnode(s, 1) 641 return node.value 642 else: 643 #FIXME: runs in k*log(N), could be reduced to k + log(N) by linked list 644 return [self.getitem_all(i) for i in xrange(*s.indices())]
645 - def index_all(self, item):
646 return self.index(item, False)
647 - def is_visible(self, i):
648 node = self._getnode(i, 1) 649 return node.annotation[0] == 1
650 - def is_visible_item(self, item):
651 node = self._index_lookup(item) 652 return node.annotation[0] == 1
653 - def insert_sequence_all(self, position, sequence, visibility):
654 node = self._getnode(position,1) 655 self._insert_sequence_leftofnode(node, sequence, visibility)
656 - def insert_sequence_leftof(self, target, sequence, visibility):
657 node = self._index_lookup(target) 658 self._insert_sequence_leftofnode(node, sequence, visibility)
659 - def _insert_sequence_leftofnode(self, node, sequence, visibility):
660 for i in xrange(len(sequence)): 661 v = sequence[i] 662 viz = visibility[i] 663 newnode = self._makenode() 664 newnode.annotation = (1 if viz else 0, 1) 665 newnode.value = v 666 self._tree.addleft(newnode, node) 667 self._index_assign(v, newnode)
668
669 # Skew, split, and decrease_level are the AA balancing functions, as described 670 # at http://en.wikipedia.org/wiki/AA_tree . They have been modified 671 # substantially here to (1) maintain bidirectional linking and (2) maintain 672 # monoid annotations. 673 -def skew(node, op=None):
674 L = node.leftchild 675 if (L is not None) and node.level == L.level: 676 node.leftchild = L.rightchild 677 if node.leftchild is not None: 678 node.leftchild.parent = node 679 L.rightchild = node 680 L.parent = node.parent 681 node.parent = L 682 if L.parent is not None: 683 if L.parent.leftchild is node: 684 L.parent.leftchild = L 685 else: 686 assert L.parent.rightchild is node 687 L.parent.rightchild = L 688 if op is not None: 689 L.annotation = node.annotation 690 node.annotation = op(node.leftchild.annotation, node.rightchild.annotation) 691 assert L.annotation == op(L.leftchild.annotation, L.rightchild.annotation) 692 # This assertion is the condition of associativity, guaranteed for any 693 # valid monoid operation. 694 return L 695 else: 696 return node
697
698 -def split(node, op=None):
699 R = node.rightchild 700 if ((R is not None) and 701 (R.rightchild is not None) and 702 (node.level == R.rightchild.level)): 703 node.rightchild = R.leftchild 704 node.rightchild.parent = node 705 706 R.leftchild = node 707 R.parent = node.parent 708 node.parent = R 709 710 R.level += 1 711 712 if R.parent is not None: 713 if R.parent.leftchild is node: 714 R.parent.leftchild = R 715 else: 716 assert R.parent.rightchild is node 717 R.parent.rightchild = R 718 719 if op is not None: 720 R.annotation = node.annotation 721 node.annotation = op(node.leftchild.annotation, node.rightchild.annotation) 722 assert R.annotation == op(R.leftchild.annotation, R.rightchild.annotation) 723 # This assertion is the condition of associativity, guaranteed for any 724 # valid monoid operation. 725 726 return R 727 else: 728 return node
729
730 -def decrease_level(node):
731 # Decrease the level of node if necessary. Returns true if a modification 732 # was made. 733 target = min(node.leftchild.level, node.rightchild.level) + 1 734 if target < node.level: 735 node.level = target 736 if target < node.rightchild.level: 737 node.rightchild.level = target 738 return True 739 return False
740
741 -class AAMonoidTree(MonoidTree):
742 makenode = AANode
743 - def _update_add(self, node, sentinel=None):
744 """node must be an internal node one level above the leaves, with 745 two leaves itself.""" 746 node.level = 2 747 while node is not sentinel: 748 #oldval = node.annotation 749 node.annotation = self.op(node.leftchild.annotation, node.rightchild.annotation) 750 node = skew(node, self.op) 751 node = split(node, self.op) 752 if node.parent is None: 753 self.root = node 754 node = node.parent
755 - def _update_del(self, node, sentinel=None):
756 while node is not sentinel: 757 #oldval = node.annotation 758 #oldlevel = node.level 759 node.annotation = self.op(node.leftchild.annotation, node.rightchild.annotation) 760 761 decrease_level(node) 762 763 node = skew(node, self.op) 764 node.rightchild = skew(node.rightchild, self.op) 765 if node.rightchild.rightchild is not None: 766 node.rightchild.rightchild = skew(node.rightchild.rightchild, self.op) 767 node = split(node, self.op) 768 node.rightchild = split(node.rightchild, self.op) 769 770 #if (oldval == node.annotation) and (oldlevel == node.level): 771 # #Nodes above this point will not have changed 772 # break 773 774 if node.parent is None: 775 self.root = node 776 node = node.parent
777
778 -class AATreeList(TreeList):
779 _treetype = AAMonoidTree
780
781 -class AATreeHideList(TreeHideList):
782 _treetype = AAMonoidTree
783