from django.core.exceptions import ObjectDoesNotExist # NOTE: node.prev a node.succ jsou implementovány přímo v models.TreeNode # TODO: Všechny tyto funkce se naivně spoléhají na to, že jako parametr dostanou nějaký TreeNode (některé možná zvládnou i None) # TODO: Chceme, aby všechno nějak zvládlo None jako parametr. # Slouží k debugování pro rychlé získání představy o podobě podstromu pod tímto TreeNode. def print_tree(node,indent=0): # FIXME: Tady se spoléháme na to, že nedeklarovaný primární klíč se jmenuje by default 'id', což není úplně správně print("{}{} (id: {})".format(" "*indent,node, node.id)) if node.first_child: print_tree(node.first_child, indent=indent+2) if node.succ: print_tree(node.succ, indent=indent) # Django je trošku hloupé, takže node.prev nevrací None, ale hází django.core.exceptions.ObjectDoesNotExist def safe_pred(node): try: return node.prev except ObjectDoesNotExist: return None def first_brother(node): if node is None: return None brother = node while safe_pred(brother) is not None: brother = safe_pred(brother) return brother # A to samé pro .father_of_first def safe_father_of_first(node): first = first_brother(node) try: return first.father_of_first except ObjectDoesNotExist: return None ## Rodinné vztahy def get_parent(node): # Nejdřív získáme prvního potomka... while safe_pred(node) is not None: node = safe_pred(node) # ... a z prvního potomka umíme najít rodiče return safe_father_of_first(node) # Obecný next: další Node v "the-right-order" pořadí (já, pak potomci, pak sousedé) def general_next(node): # Máme potomka? if node.first_child is not None: return node.first_child # Nemáme potomka. # Chceme najít následníka sebe, nebo některého (toho nejblíž příbuzného) z našich předků (tatínka, dědečka, ...) while node.succ is None: node = get_parent(node) if node is None: return None # žádný z předků nemá následníka, takže žádny vrchol nenásleduje. return node.succ def last_brother(node): while node.succ is not None: node = node.succ return node def general_prev(node): # Předchůdce je buď rekurzivně poslední potomek předchůdce, nebo náš otec. # Otce vyřešíme nejdřív: if safe_pred(node) is None: return safe_father_of_first(node) pred = safe_pred(node) while pred.first_child is not None: pred = last_brother(pred.first_child) # pred nyní nemá žádné potomky, takže je to poslední rekurzivní potomek původního předchůdce return pred # Generátor pravých bratrů (konkrétně sebe a následujících potomků) # Generátor potomků níže spoléhá na to, že se tohle dá volat i s parametrem None. def me_and_right_brothers(node): current = node while current is not None: yield current current = current.succ def right_brothers(node): generator = me_and_right_brothers(node.succ) for item in generator: yield item # Generátor všech sourozenců (vč. sám sebe) def all_brothers(node): # Najdeme prvního bratra fb = first_brother(node) marb = me_and_right_brothers(fb) for cur in marb: yield cur def all_proper_brothers(node): all = all_brothers(node) for br in all: if br is node: continue yield br # Generátor potomků def all_children(node): brothers = all_brothers(node.first_child) for br in brothers: yield br # Generátor následníků v "the-right-order" # Bez tohoto vrcholu def all_following(node): current = general_next(node) while current is not None: yield current current = general_next(current) ## Filtrační hledání # Najdi dalšího bratra nějakého typu, nebo None. # hledá i podtřídy, i.e. get_next_brother_of_type(neco, TreeNode) je prostě succ. def get_next_brother_of_type(node, type): for current in right_brothers(node): if isinstance(current, type): return current return None def get_prev_brother_of_type(node, type): # Na tohle není rozumný generátor, ani ho asi nechceme, prostě to implementujeme cyklem. current = node while safe_pred(current) is not None: current = safe_pred(current) if isinstance(current, type): return current return None # Totéž pro "the-right-order" pořadí def get_next_node_of_type(node, type): for cur in all_folowing(node): if isinstance(cur, type): return cur return None def get_prev_node_of_type(node, type): # Na tohle není rozumný generátor, ani ho asi nechceme, prostě to implementujeme cyklem. current = node while general_prev(current) is not None: current = general_prev(current) if isinstance(current, type): return current return None # Editace stromu: def create_node_after(predecessor, type, **kwargs): new_node = type.objects.create(**kwargs) new_node.save() succ = predecessor.succ predecessor.succ = new_node predecessor.save() new_node.succ = succ new_node.save() # Vyrábí prvního syna, ostatní nalepí za (existují-li) def create_child(parent, type, **kwargs): new_node = type.objects.create(**kwargs) new_node.save() orig_child = parent.first_child parent.first_child = new_node parent.save() if orig_child is not None: # Přidáme původního prvního syna jako potomka nového vrcholu new_node.succ = orig_child new_node.save() def create_node_before(successor, type, **kwargs): if safe_pred(successor) is not None: # Easy: přidáme za předchůdce create_node_after(successor.prev, type, **kwargs) # Nemáme předchůdce, jsme tedy první z bratrů. Máme otce? if safe_father_of_first(successor) is not None: # Ano -> Easy: vyrobíme nového potomka # NOTE: Tohle je možná trošku abuse implementace výše, ale to nevadí moc... create_child(successor.father_of_first, type, **kwargs) # Teď už easy: Jsme sirotci, takže se vyrobíme a našeho následníka si přidáme jako succ new = type.objects.create(**kwargs) new.succ = successor new.save() # ValueError, pokud je (aspoň) jeden parametr None def swap(node, other): raise NotImplementedError("YAGNI (You aren't gonna need it).") # Exception, kterou některé metody při špatném použití mohou házet # Hlavní důvod je možnost informovat o selhání, aby se příslušný problém dal zobrazit na frontendu, class TreeLibError(RuntimeError): pass def swap_pred(node): if node is None: raise TreeLibError("Nelze přesunout None. Tohle by se nemělo stát.") pred = safe_pred(node) if pred is None: raise TreeLibError("Nelze posunout vlevo, není tam žádný další uzel.") pre_pred = safe_pred(pred) succ = node.succ if pre_pred is not None: pre_pred.succ = node pre_pred.save() node.succ = pred node.save() pred.succ = succ pred.save() def swap_succ(node): if node is None: raise TreeLibError("Nelze přesunout None. Tohle by se nemělo stát.") succ = node.succ if succ is None: raise TreeLibError("Nelze posunout vpravo, není tam žádný další uzel") pred = safe_pred(node) post_succ = succ.succ if pred is not None: pred.succ = succ pred.save() succ.succ = node succ.save() node.succ = post_succ node.save() # Rotace stromu # Dokumentace viz wiki: # (lower bude jednoduchá rotace, ne mega, existence jednoduché rotace mi došla až po nakreslení obrázku) def raise_node(node): pass def lower_node(node): pass