from django.core.exceptions import ObjectDoesNotExist from django.db import transaction # 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. # TODO: Do nějakých consistency-checků přidat hledání polo-sirotků (kteří nesplňují invarianty, třeba nejsou dosažitelní a mají root, vyrábějí DAG, ...) # 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): if node is None: return None try: return node.prev except ObjectDoesNotExist: return None # FIXME: Proč????? def safe_succ(node): try: return node.succ except ObjectDoesNotExist: return None def safe_father_of_first(node): if node is None: return None first = first_brother(node) try: return first.father_of_first 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 ## Rodinné vztahy # Tohle se teď zrovna k None chová správně, ale je potřeba na to dávat pozor 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) def get_last_child(node): if node is None: return None first = node.first_child if first is None: return None else: return last_brother(first) def is_orphan(node): """ Zjišťuje, jestli už je daný Node někde pověšený či nikoli. """ # None jsem se rozhodl, že sirotek není if node is None: return False if get_parent(node) is None: if node.succ is not None or safe_pred(node) is not None or safe_father_of_first(node) is not None or node.root is not None: import logging logger = logging.getLogger(__name__) # Error = pošle mail :-) logger.error(f"Node-sirotek s id {node.id} má rodinné vztahy (Node: {node})") return True else: return False # Obecný next: další Node v "the-right-order" pořadí (já, pak potomci, pak sousedé) def general_next(node): if node is None: return None # 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): if node is None: return None 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: # Tady se ošetří node=None samo 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): if node is None: return 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): if node is None: return # 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): if node is None: return all = all_brothers(node) for br in all: if br is node: continue yield br def all_children(node): """ Generátor všech potomků zadaného Node. """ if node is None: return brothers = all_brothers(node.first_child) for br in brothers: yield br def all_children_of_type(node, type): """ Generuje všechny potomky daného Node a daného typu. """ if node is None: return brothers = all_brothers(node.first_child) for br in brothers: if isinstance(br, type): yield br # Generátor následníků v "the-right-order" # Bez tohoto vrcholu def all_following(node): if node is None: return 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): if node is None: return for current in right_brothers(node): if isinstance(current, type): return current return None def get_prev_brother_of_type(node, type): if node is None: return # 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): if node is None: return 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. if node is None: return current = node while general_prev(current) is not None: current = general_prev(current) if isinstance(current, type): return current return None def get_upper_node_of_type(node, type): # vrací první vyšší node daného typu (ignoruje sourozence) if node is None: return current = node while get_parent(current) is not None: current = get_parent(current) if isinstance(current, type): return current return None # 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 # Editace stromu: def create_node_after(predecessor, type, **kwargs): from .models import TreeNode if predecessor is None: raise TreeLibError("Nelze vyrábět sirotky! (predecessor=None)") if not issubclass(type, TreeNode): raise TreeLibError("Nový node není node!") new_node = type.objects.create(**kwargs) new_node.root = predecessor.root new_node.save() succ = predecessor.succ predecessor.succ = new_node predecessor.save() new_node.succ = succ new_node.save() return new_node # Vyrábí prvního syna, ostatní nalepí za (existují-li) def create_child(parent, type, **kwargs): from .models import TreeNode if parent is None: raise TreeLibError("Nelze vyrábět sirotky! (parent=None)") if not issubclass(type, TreeNode): raise TreeLibError("Nový node není node!") new_node = type.objects.create(**kwargs) new_node.root = parent.root 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() return new_node def insert_last_child(parent, node): if parent is None: raise TreeLibError("Nelze vyrábět sirotky! (parent=None)") """ Zadaný Node přidá jako posledního potomka otce. """ last = get_last_child(parent) if not is_orphan(node): print(safe_pred(node)) print(safe_father_of_first(node)) if len(safe_father_of_first(node).get_real_instances()) == 0: print("Related Manager je prázdný.") print(type(safe_father_of_first(node).queryset_class)) raise TreeLibError("Snažíš se přidat do stromu Node, který už je zavěšený.") if last is None: parent.first_child = node parent.save() else: last.succ = node last.save() def create_node_before(successor, type, **kwargs): from .models import TreeNode if successor is None: raise TreeLibError("Nelze vyrábět sirotky! (successor=None)") if not issubclass(type, TreeNode): raise TreeLibError("Nový node není node!") 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.root = successor.root new.succ = successor new.save() return new # ValueError, pokud je (aspoň) jeden parametr None def swap(node, other): raise NotImplementedError("YAGNI (You aren't gonna need it).") @transaction.atomic 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 = None pred.save() # Nemame predchudce -> je potreba upravit otce father = safe_father_of_first(node) if pred is None and father is not None: # Mame otce father.first_child = succ father.save() succ.succ = node succ.save() node.succ = post_succ node.save() if pred is not None: pred.succ = succ pred.save() @transaction.atomic 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.") return swap_succ(pred) # Rotace stromu # Dokumentace viz wiki: def raise_node(node): if node is None: raise TreeLibError("Nelze přesunout None. Tohle by se nemělo stát.") # Pojmenování viz WIKI (as of 2020-03-19 01:33:44 GMT+1) # FIXME: Trochu méně naivní, nevěřím tomu, prosím otestovat D = node C = get_parent(D) if C is None: raise TreeLibError("Nelze povýšit vrchol, jenž nemá otce.") E = C.succ # Může být None a ničemu to nevadí subtree4_head = D.first_child # Může být None, ale pak se musí z 3P udělat přímo potomek D subtree4_tail = last_brother(subtree4_head) # Měl by být None právě když je sub4_head=None subtree3P_head = D.succ # Může být None a ničemu to nevadí subtree3L_tail = safe_pred(D) # Pokud je None, D je první syn C a C má tedy skončit bezdětný # Prostor pro motlitbu... pass # Amen. # Teď už nesmíme spadnout, protože jinak skončíme se stromem v nekonzistentním stavu C.succ = D # Nespadne C.save() D.succ = E # Nespadne D.save() if subtree3L_tail is not None: subtree3L_tail.succ = None subtree3L_tail.save() else: assert C.first_child is D C.first_child = None C.save() if subtree4_tail is not None: subtree4_tail.succ = subtree3P_head subtree4_tail.save() else: D.first_child = subtree3P_head D.save() # To by mělo být všechno... # (lower bude jednoduchá rotace, ne mega, existence jednoduché rotace mi došla až po nakreslení obrázku) def lower_node(node): if node is None: raise TreeLibError("Nelze přesunout None. Tohle by se nemělo stát.") # Pojmenování viz WIKI (as of 2020-03-19 01:33:44 GMT+1) # FIXME: Trochu naivní, prosím otestovat C = node D = C.succ # Může být None a ničemu to nevadí B = safe_pred(C) if B is None: raise TreeLibError("Nelze ponížit prvního syna (není pod co)") subtree2_head = B.first_child # Je-li None, pak se z C má stát první syn subtree2_tail = last_brother(subtree2_head) # None iff head=None, doufám # Prostor pro motlitbu... pass # Amen. # Teď už nesmíme spadnout, protože jinak skončíme se stromem v nekonzistentním stavu B.succ = D # Nespadne B.save() if subtree2_tail is not None: subtree2_tail.succ = C subtree2_tail.save() else: assert subtree2_head is None B.first_child = C B.save() # To by mělo být všechno... def disconnect_node(node): #FIXME: dodělat odstranění roota všem potomkům if node is None: raise TreeLibError("Nelze odpojit None. Tohle by se nemělo stát.") print(f'My:{node}, predchudce:{safe_pred(node)}, naslednik:{safe_succ(node)}, otec:{safe_father_of_first(node)}') # Jsme prvnim synem if safe_pred(node) is None: if safe_succ(node) is None: # Jsme jedinym synem - upravime otce (pokud mame) a odpojime se father = safe_father_of_first(node) if father is not None: father.first_child = None father.save() return else: # mame bratra swap_succ(node) # Staneme se neprvním synem, pokracujeme mimo if # Jsme neprvním synem prev = node.prev prev.succ = node.succ node.succ = None node.save() clear_root(node) prev.save() def clear_root(node): node.root = None node.save() if node.first_child: clear_root(node.first_child) if node.succ: clear_root(node.succ) def set_root(node,root): node.root = root node.save() if node.first_child: clear_root(node.first_child) if node.succ: clear_root(node.succ)