From 74d7f05dcab1f9f225fd1e441bebe070b6d73d64 Mon Sep 17 00:00:00 2001
From: codeboss <2422523675@qq.com>
Date: Thu, 1 Aug 2024 00:07:08 +0800
Subject: [PATCH] =?UTF-8?q?=E7=82=B9=E5=87=BB=E9=80=89=E4=B8=AD=E6=95=85?=
=?UTF-8?q?=E4=BA=8B=E7=BA=BF=E7=89=B9=E6=80=A7=E5=BC=80=E5=8F=91=E5=AE=8C?=
=?UTF-8?q?=E6=88=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.idea/workspace.xml | 4 +-
frame/ContentView.py | 81 +++++++-
graph/DataType.py | 5 +-
graph/__pycache__/DataType.cpython-312.pyc | Bin 3134 -> 3033 bytes
graph/directed_acyclic_graph/DAGLayout.py | 25 ++-
graph/directed_acyclic_graph/DAGPresent.py | 192 ++++++++++++++++--
.../__pycache__/DAGLayout.cpython-312.pyc | Bin 13274 -> 13378 bytes
.../__pycache__/DAGPresent.cpython-312.pyc | Bin 16549 -> 23959 bytes
8 files changed, 262 insertions(+), 45 deletions(-)
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 61f4fb4..38adec9 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -7,6 +7,8 @@
+
+
@@ -190,7 +192,7 @@
-
+
diff --git a/frame/ContentView.py b/frame/ContentView.py
index 6070e3f..ece389c 100644
--- a/frame/ContentView.py
+++ b/frame/ContentView.py
@@ -1,7 +1,9 @@
import sys
from typing import Dict, List
-from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout
+from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QMainWindow
+from PyQt5.QtWidgets import QMenu,QAction
+from PyQt5.QtCore import QPoint
from graph.DataType import Arrow, Point
from graph.directed_acyclic_graph.DAGPresent import DAGActiveView, Direction
@@ -10,21 +12,24 @@ from parse.ast_load import global_ast_path
class FragmentPoint(Point):
- def __init__(self, name: str, start_mark: bool):
- Point.__init__(self, name, start_mark)
+ def __init__(self, name: str):
+ Point.__init__(self, name)
pass
-class ContentWindow(QWidget):
+class ContentWindow(QMainWindow):
def __init__(self, parent):
- QWidget.__init__(self, parent)
- layout = QVBoxLayout(self)
- self.fragment_view = DAGActiveView(Direction.RankLR, self)
- layout.addWidget(self.fragment_view)
+ QMainWindow.__init__(self, parent)
+ self.fragment_view = DAGActiveView(self)
+ self.setCentralWidget(self.fragment_view)
+ self.fragment_view.nodes_clicked.connect(self.print_node_list)
+ self.present_graph: Dict[str, StoryMap] = None
pass
pass
def present_stories_graph(self, graph: Dict[str, StoryMap]) -> None:
+ self.present_graph = graph
+
arrows: List[Arrow] = []
for story_name in graph:
fragments = graph[story_name].slice_list
@@ -42,8 +47,8 @@ class ContentWindow(QWidget):
end_node_label = f"{end_frag.story_refer}&{end_frag.fragm_refer}"
arrows.append(Arrow(
- FragmentPoint(start_node_label, fragi == 1),
- FragmentPoint(end_node_label, False))
+ FragmentPoint(start_node_label),
+ FragmentPoint(end_node_label))
)
pass
pass
@@ -51,6 +56,60 @@ class ContentWindow(QWidget):
self.fragment_view.update_with_edges(arrows)
pass
+ def print_node_list(self, xpos, ypos, list):
+ if len(list) == 0:
+ return
+
+ if len(list) > 1:
+ pass
+ else:
+ node_x = list[0]
+ if node_x[0].startswith("node"):
+ node_sections = [s for s in node_x[1].split("&") if s != ""]
+ if len(node_sections) == 1:
+ story = self.present_graph[node_sections[0]]
+ node_names = [node_sections[0]]
+ for slice in story.slice_list[1:]:
+ if slice.is_define_node[0]:
+ node_names.append(f"{slice.parent_story}&{slice.is_define_node[1]}")
+ else:
+ node_names.append(f"{slice.story_refer}&{slice.fragm_refer}")
+ pass
+ pass
+ self.fragment_view.highlight_graph_link(node_names)
+ pass
+ elif len(node_sections) > 1:
+ story_node = self.present_graph[node_sections[0]]
+ fragm_node = story_node.get_fragment_defined(node_sections[1])
+
+ story_list = [node_sections[0]]
+ for vsname in fragm_node.refers_slice:
+ if vsname not in story_list:
+ story_list.append(vsname)
+ pass
+ pass
+
+ if len(story_list) == 1:
+ self.print_node_list(xpos, ypos, [("node", story_list[0])])
+ elif len(story_list) > 1:
+ menu = QMenu()
+ for story in story_list:
+ def trigger(story_name:str):
+ return lambda : self.print_node_list(xpos, ypos, [("node", story_name)])
+
+ menu.addAction(f"story/{story}", trigger(story))
+ pass
+
+ menu.exec(self.fragment_view.mapToGlobal(QPoint(xpos, ypos)))
+ pass
+ else:
+ print(story_list)
+ pass
+ pass
+ pass
+ pass
+ pass
+
if __name__ == "__main__":
app = QApplication(sys.argv)
@@ -60,4 +119,6 @@ if __name__ == "__main__":
tool = XAST_ParseTool(global_ast_path)
view.present_stories_graph(tool.get_story_graph())
+# view.fragment_view.highlight_graph_link(["血脉的源头", "血脉的源头&待续"])
+
app.exec()
\ No newline at end of file
diff --git a/graph/DataType.py b/graph/DataType.py
index e98f09d..100a964 100644
--- a/graph/DataType.py
+++ b/graph/DataType.py
@@ -12,16 +12,15 @@ class Pos:
class Point:
- def __init__(self, name:str, start_mark: bool):
+ def __init__(self, name:str):
self.point_name = name
- self.is_start = start_mark
pass
def name(self) -> str:
return self.point_name
def make_copy(self) -> 'Point':
- return Point(self.point_name, self.is_start)
+ return Point(self.point_name)
class Line:
diff --git a/graph/__pycache__/DataType.cpython-312.pyc b/graph/__pycache__/DataType.cpython-312.pyc
index 43b744ffda6da18b0120391b575d1a07daee1d94..20f65d0867f2a5073277b56d3a8908ed51eded70 100644
GIT binary patch
delta 646
zcmXw0&ubG=5Pq}2vOnCWO;@`~Xi`l!vbCmSMHD<~@uU)|r%+f*b_+$C)J+ct3igmg
z1i{K2q9;!tiXaGrxBLTM3SMj!Jk@h074aZAv%!J==9_u*ee-xba1UqfPqu9eioxIg
z_3V2)BR`S2q!=v_^hG4<5Q@4KLL{$Cq109CN+>5>A+Cn0P^GMOG+!U8wbn*+d#E(m
zHlxOWBQ|3$(>KZx^u-SBh=ABQIwCfR?ZeRAYGuhFV7)KJr>Gm}3EHg3Bl)b&tzGdY
zSLi9v(wI|jOYtmDsSZ9?CSe+%DixFWB)b;y06h0CP))Hi-a@uHs^S8v{F`Tpwx%=*KNUO7PcyJ^D7Uv`?5P(^MXI{uSdvux<*kvR
zbgY9~+-r;kr4tR*3Vv5VXl0gq_)0579tYYgoX1C|gNrE#inyFQv`RcM&yb<}WhW=@
zY&4_l=Gwz3XtcIE=kUIM7fSeAFT*@OS6wp^Fy!!uew~iyjCV6hbAp<_JbuGPcD#f~
zhDRGMGfMU@!3BI{u4??Bu^Q@7T7`
z)RTDrx^exDeZYVn)4F_I-E3=~Rwb@h)m`EluI|wWb&XA0WiIuMhVC+zbJujaim~;W
zV}uu{4h;zsd06;sr7Y(*^d|dnhLL$_A&GYVTG-geNBa5oUk(7Wp6p!<%If}A-KQ?$S_>v
z%fx|qe3Fd7cfQAqXqbjxtVBlPN9-ag!h$(QreT(4L?j||uwkB7(gr&OU(848NElGU
zzopG$n>jV7NG9Qh<*6A7aYD5@Czkm!6diabX7~)IC*i9oj+QY!g~%Xq71BoF|Bo;M
zh4|be9xDzHd9c3LY~7Lu$_x>$J9?F< int:
"""
- 节点分层处理,返回路径最大长度
+ 节点分层处理,返回本次执行路径最大长度
:param inst: 当前节点
:param layer_current: 节点等级
:return: 最长路径长度
"""
- inst.layer_v = max(inst.layer_v, layer_current)
+ max_remains = layer_current
+ if layer_current == 0 or inst.layer_v < layer_current:
+ inst.layer_v = layer_current
- max_remains = inst.layer_v
- values = inst.next_nodes()
- for fork in values:
- max_remains = max(self.__node_layering(fork, inst.layer_v + 1), max_remains)
- pass
+ values = inst.next_nodes()
+ for fork in values:
+ max_remains = max(self.__node_layering(fork, inst.layer_v + 1), max_remains)
+ pass
return max_remains + 1
@@ -181,7 +183,7 @@ class DAGGraph:
# 注册所有数据图实节点
for node in self.graph_inst.values():
- nodes_temp[node.bind_point().point_name] = DAGOrderHelper(bind=node, relate=node)
+ nodes_temp[node.bind_point().point_name] = DAGOrderHelper(bind=node, relate=None, towards=None)
pass
temp_array: List[DAGOrderHelper] = []
@@ -192,7 +194,8 @@ class DAGGraph:
for next in node.next_nodes():
node_links = [nodes_temp[node.bind_point().point_name]]
for layer_index in range(node.layer_v + 1, next.layer_v):
- node_links.append(DAGOrderHelper(layer_index, relate=node))
+ node_links.append(DAGOrderHelper(relate=node, towards=next, bind=None))
+ node_links[-1].layer_number = layer_index
pass
node_links.append(nodes_temp[next.bind_point().point_name])
diff --git a/graph/directed_acyclic_graph/DAGPresent.py b/graph/directed_acyclic_graph/DAGPresent.py
index f29df4d..aad6521 100644
--- a/graph/directed_acyclic_graph/DAGPresent.py
+++ b/graph/directed_acyclic_graph/DAGPresent.py
@@ -2,14 +2,32 @@ from graph.directed_acyclic_graph.DAGLayout import DAGGraph, DAGOrderHelper
from graph.DataType import Arrow, Point
from typing import List, Dict
from PyQt5.QtWidgets import QWidget, QApplication, QGraphicsView, QGraphicsScene, QGraphicsItem
-from PyQt5.QtCore import QRectF, Qt, QPointF
-from PyQt5.QtGui import QFont, QBrush, QPen, QPainter, QPainterPath
+from PyQt5.QtCore import QRectF, Qt, QPointF, pyqtSignal
+from PyQt5.QtGui import QFont, QBrush, QPen, QPainter, QPainterPath, QMouseEvent
import sys
from enum import Enum
from abc import abstractmethod
+class GraphNodeType(Enum):
+ ActivePresentNode = 0,
+ PenetrateNode = 1,
+ TransitionCurve = 2,
+
+
class GraphNode:
+ @abstractmethod
+ def node_type(self) -> GraphNodeType:
+ raise NotImplementedError("node_type")
+
+ @abstractmethod
+ def highlight(self, flag: bool):
+ raise NotImplementedError("highlight")
+
+ @abstractmethod
+ def is_highlighted(self) -> bool:
+ raise NotImplementedError("is_highlighted")
+
@abstractmethod
def boundingRect(self) -> QRectF:
raise NotImplementedError("boundingRect")
@@ -35,12 +53,23 @@ class ActivePresentNode(QGraphicsItem, GraphNode):
def __init__(self, name: str, type: StoryNodeType, font: QFont, parent):
QGraphicsItem.__init__(self, parent)
+ self.__highlight_mark = False
self.node_type_within_story = type
self.fragment_name = name
self.font_bind = font
self.data_bind: object = None
pass
+ def node_type(self) -> GraphNodeType:
+ return GraphNodeType.ActivePresentNode
+
+ def highlight(self, flag: bool):
+ self.__highlight_mark = flag
+ pass
+
+ def is_highlighted(self) -> bool:
+ return self.__highlight_mark
+
def boundingRect(self) -> QRectF:
size = self.font_bind.pixelSize()
if self.node_type_within_story == StoryNodeType.FragmentNode:
@@ -53,10 +82,16 @@ class ActivePresentNode(QGraphicsItem, GraphNode):
text_rect = QRectF(outline.x() + 5, outline.y() + 5, outline.width() - 10, outline.height() - 10)
painter.save()
+
if self.node_type_within_story == StoryNodeType.FragmentNode:
- painter.fillRect(outline, QBrush(Qt.gray))
+ painter.fillRect(outline, Qt.gray)
else:
painter.fillRect(outline, Qt.green)
+ pass
+
+ if self.__highlight_mark:
+ painter.setPen(Qt.red)
+
painter.drawRect(outline)
painter.setFont(self.font_bind)
painter.drawText(text_rect, self.fragment_name)
@@ -83,9 +118,20 @@ class PenetrateNode(QGraphicsItem, GraphNode):
def __init__(self, width: float, parent):
QGraphicsItem.__init__(self, parent)
self.width_store = width
+ self.__highlight_mark = False
self.data_bind: object = None
pass
+ def node_type(self) -> GraphNodeType:
+ return GraphNodeType.PenetrateNode
+
+ def highlight(self, flag: bool):
+ self.__highlight_mark = flag
+ pass
+
+ def is_highlighted(self) -> bool:
+ return self.__highlight_mark
+
def resize_width(self, width: float):
self.width_store = width
self.update()
@@ -98,7 +144,13 @@ class PenetrateNode(QGraphicsItem, GraphNode):
outline = self.boundingRect()
painter.save()
- painter.fillRect(QRectF(0, 2, outline.width(), 4), Qt.black)
+
+ if self.__highlight_mark:
+ painter.fillRect(QRectF(0, 2, outline.width(), 4), Qt.red)
+ else:
+ painter.fillRect(QRectF(0, 2, outline.width(), 4), Qt.black)
+ pass
+
painter.restore()
pass
@@ -111,9 +163,10 @@ class PenetrateNode(QGraphicsItem, GraphNode):
return self.data_bind
-class TransitionCurve(QGraphicsItem):
+class TransitionCurve(QGraphicsItem, GraphNode):
def __init__(self, start: GraphNode, end: GraphNode, prev_layrer_width:float, parent):
QGraphicsItem.__init__(self, parent)
+ self.__highlight_mark = False
self.start_node = start
self.end_node = end
self.prev_layer_w = prev_layrer_width
@@ -121,6 +174,16 @@ class TransitionCurve(QGraphicsItem):
self.data_bind: object = None
pass
+ def node_type(self) -> GraphNodeType:
+ return GraphNodeType.TransitionCurve
+
+ def highlight(self, flag: bool):
+ self.__highlight_mark = flag
+ pass
+
+ def is_highlighted(self) -> bool:
+ return self.__highlight_mark
+
def layout_refresh(self):
orect = self.start_node.boundingRect()
erect = self.end_node.boundingRect()
@@ -160,6 +223,10 @@ class TransitionCurve(QGraphicsItem):
control_pos1 = end_pos - QPointF((outline.width() - line_span)/3, 0)
npen = QPen(Qt.black)
+ if self.__highlight_mark:
+ npen = QPen(Qt.red)
+ pass
+
npen.setWidthF(4)
painter.setPen(npen)
@@ -188,21 +255,24 @@ class Direction(Enum):
class DAGActiveView(QGraphicsView):
- def __init__(self, orie: Direction, parent):
+ nodes_clicked = pyqtSignal(int,int,list)
+
+ def __init__(self, parent):
QGraphicsView.__init__(self, parent)
self.setViewportUpdateMode(QGraphicsView.ViewportUpdateMode.FullViewportUpdate)
- self.layer_span = 200
- self.node_span = 20
-
- self.orientation = orie
- self.scene_bind = QGraphicsScene(self)
- self.setScene(self.scene_bind)
-
font = QFont()
font.setPixelSize(20)
self.setFont(font)
+ self.layer_span = 200
+ self.node_span = 20
+
+ self.scene_bind = QGraphicsScene(self)
+ self.setScene(self.scene_bind)
+
+ self.__highlight_nodelist: List[GraphNode] = []
+ self.__total_graph_nodes: Dict[str, GraphNode] = {}
pass
def update_with_edges(self, arrows: List[Arrow]) -> None:
@@ -229,18 +299,30 @@ class DAGActiveView(QGraphicsView):
ypos_acc += curr_gnode.boundingRect().height()
current_graphics_nodes.append(curr_gnode)
self.scene_bind.addItem(curr_gnode)
+
+ fragm_start = node.relate_bind.bind_point().point_name
+ fragm_end = node.towards_to.bind_node.point_name
+ node_key = (f"plac${fragm_start}::{fragm_end}-{node.layer_number}")
+ curr_gnode.node_key_bind = "plac", fragm_start, fragm_end, node.layer_number
+ self.__total_graph_nodes[node_key] = curr_gnode
+ pass
else:
- if node.layer_bind.bind_point().is_start:
- curr_gnode = ActivePresentNode(node.layer_bind.bind_point().point_name, StoryNodeType.StoryStartNode,
- self.font(), None)
- else:
- curr_gnode = ActivePresentNode(node.layer_bind.bind_point().point_name, StoryNodeType.FragmentNode,
- self.font(), None)
+ node_type_vx = StoryNodeType.FragmentNode
+ if node.layer_bind.input_count == 0:
+ node_type_vx = StoryNodeType.StoryStartNode
+
+ curr_gnode = ActivePresentNode(node.layer_bind.bind_point().point_name,
+ node_type_vx, self.font(), None)
+
curr_gnode.attach_data(node)
curr_gnode.setPos(previous_node_end, ypos_acc)
ypos_acc += curr_gnode.boundingRect().height()
current_graphics_nodes.append(curr_gnode)
self.scene_bind.addItem(curr_gnode)
+
+ node_key = f"node@{node.layer_bind.bind_point().point_name}"
+ curr_gnode.node_key_bind = "node", node.layer_bind.bind_point().point_name
+ self.__total_graph_nodes[node_key] = curr_gnode
pass
ypos_acc += self.node_span
@@ -271,6 +353,24 @@ class DAGActiveView(QGraphicsView):
line_cmbn = TransitionCurve(prev_gnode, curr_gnode, prev_layer_width,None)
self.scene_bind.addItem(line_cmbn)
line_cmbn.layout_refresh()
+
+ relate_node_name = ""
+ if prev_gnode.node_type() == GraphNodeType.ActivePresentNode:
+ relate_node_name = prev_gnode.get_data().layer_bind.bind_point().point_name
+ elif prev_gnode.node_type() == GraphNodeType.PenetrateNode:
+ relate_node_name = prev_gnode.get_data().relate_bind.bind_point().point_name
+
+ towards_node_name = ""
+ if curr_gnode.node_type() == GraphNodeType.ActivePresentNode:
+ towards_node_name = sort_helper.layer_bind.bind_point().point_name
+ elif curr_gnode.node_type() == GraphNodeType.PenetrateNode:
+ towards_node_name = sort_helper.towards_to.bind_point().point_name
+
+ fragm_start = relate_node_name
+ fragm_end = towards_node_name
+ node_key = f"curv&{relate_node_name}::{towards_node_name}-{sort_helper.layer_number}"
+ line_cmbn.node_key_bind = "curv", fragm_start, fragm_end, sort_helper.layer_number
+ self.__total_graph_nodes[node_key] = line_cmbn
pass
pass
pass
@@ -281,10 +381,62 @@ class DAGActiveView(QGraphicsView):
pass
+ def highlight_graph_link(self, highlight_path: List[str]):
+ for n in self.__highlight_nodelist:
+ n.highlight(False)
+ pass
+ self.__highlight_nodelist.clear()
+
+ start_node = self.__total_graph_nodes[f"node@{highlight_path[0]}"]
+ start_node.highlight(True)
+ self.__highlight_nodelist.append(start_node)
+
+ for idx in range(1, len(highlight_path)):
+ start_name = highlight_path[idx-1]
+ end_name = highlight_path[idx]
+
+ end_node = self.__total_graph_nodes[f"node@{end_name}"]
+ end_node.highlight(True)
+ self.__highlight_nodelist.append(end_node)
+
+ plac_key = f"plac${start_name}::{end_name}"
+ curv_key = f"curv&{start_name}::{end_name}"
+ for key in self.__total_graph_nodes:
+ if key.startswith(plac_key):
+ placex = self.__total_graph_nodes[key]
+ placex.highlight(True)
+ self.__highlight_nodelist.append(placex)
+ pass
+ if key.startswith(curv_key):
+ curvx = self.__total_graph_nodes[key]
+ curvx.highlight(True)
+ self.__highlight_nodelist.append(curvx)
+ pass
+ pass
+ pass
+
+ self.scene_bind.update()
+ self.update()
+ pass
+
+ def mousePressEvent(self, event: QMouseEvent):
+ QGraphicsView.mousePressEvent(self, event)
+
+ gitems = self.items(event.pos())
+ noderef_names = []
+ for gnode in gitems:
+ if gnode.node_key_bind[0].startswith("node"):
+ noderef_names.append(gnode.node_key_bind)
+ pass
+ pass
+
+ self.nodes_clicked.emit(event.pos().x(), event.pos().y(), noderef_names[0:1])
+
+ pass
if __name__ == "__main__":
app = QApplication(sys.argv)
- view = DAGActiveView(Direction.RankLR, None)
+ view = DAGActiveView(None)
view.show()
arrows = [
diff --git a/graph/directed_acyclic_graph/__pycache__/DAGLayout.cpython-312.pyc b/graph/directed_acyclic_graph/__pycache__/DAGLayout.cpython-312.pyc
index a1055ec1aa3c3c1f1e78b82aca196abe7e443260..5ea70b7d43b8b84143ba8f0f9e968fa3d97ebcad 100644
GIT binary patch
delta 1522
zcmYk6ZETZO6vyvU>5nZvL!Ut2pARa{Zh
zGc{^Z(7@YCytFUHNi|>xa&TNC-Bq+Y(@MPP9vs?CTb*#>R;0+dUVATz$-ZeD(_qgF-Fjn
zSEh*XtkT4T7P#%LSWTilaM4Q13BoKW!zD0acA8C36N)A$V88j?n$0+Ln&h0Glr&YA
ztMR3zC1X|2iS1O`1D6|2mT%}rxNRAt8^EP(q3tlH97BCy8K#}^ytS@fU>z8PHSj%3
z*;=maI6@qy92JPXw0B}+oV9Vkoud=Ju=-kGLODrv!6GX&N3IQKJ}3!?|KyABrki0;ZfvCdK2leuE+5NTiLw~C-lM>10N1(BYGi>^h-PZ$%=Q#dJV
zc)N!%k3^XUy3?U(!XZ3$)~5*?jl$2~I{3w{P#GS%Yhl^B8vGuQS$Gqd?;v6Eja#tQ
zQ)hYj@%4w-FF!nc^^eO_z&z)j{N7j%A}lc**Iq}%dWbp^dd9C4cHR#F*A
znRDi$2;Ha@W_Ds|lqdQ`;Ums;8qDqKl5Tsoq?bXPF3!BQ5>v@y!PA
z`P;Jowj0TtgWnH-H$2~y?Rt69-@mYZ_o8PwZO+;3x{Q-`BeB)YymI!`g3-h5xamHy
zcr7+w`o+?!M^2LiA2wBkF5BV3I-@H>_TW{W6q5gyj-V2iT&Hs)9P_?F8{u2;aVbsr
zltQyFC>L4-~-qmiR{+T#B@`1qs0
z(ZULBuWdKC@L4-JI^q4=O4ek
z%04PvtO_raa<~=P)GC%&r?=+po_qFS)*k%q$j!)nU}5KLOKXRg?7MR_Y#N
zy(wp}(o1l*Zi32S%3WKoI35RxQm)rwWWAo)oD0V4;&rg`%&p*2fwn?xXpsI1snDR2
zVOC+vlJI+ICtZe)a41+z-?j1|#<*%lBgf1p&8?PMrj+o)igl8aHr`^?-{=CT^>?Aa3slI=PrM-Y?4Fc
zILC3GBh108iLK)3euftTo{KlwF|L)?zcCC~!{?_+;m7ydge;DJ1eVUi=#5Po6S
VCWt2+#zg9POgQzy47Bvoe*hWjg_HmQ
delta 1453
zcmYk6ZA@EL7{|}KZG(t&e~*tj{CqG6f^Wej#N8?1xwmf;v>
z3~XYzpcy$vnESA~_yL0-FwM;TqRSE!qF+XeS#vdJ#xIM0SOF7FCed>)FX1Hpo#+4D
zbDwkX|30UW&M9Y&KN<{LfX8nif6?Do`IRxGozxJG$Hq~NIUs>9D1t6Z)QXfui?qau
z@JayyBC`|qb0V8!yI7HvxPD43NHJXnq9z5pG@?!{#Hw%%3VOvDX*<#(i;{d)ip3>4
zs<1I>REjB_EX8_9B$+(N83v&5wHCjuZk?o)pa(}=BS~QecNIh)#%VtbaFW?g(o@Ar
zdQgj|Y?l3?huX;(f-xZLu@X2%8|xjDlxa%ptaeGYANr-ONSW(=oaZWHvCIY(gLr<`K%7VcH
zQkG?t5O46N%>p5+;oUlT1sCZA0(EH#z0Fsm>->kdQk)VPg}%^xSvKKV_F$ko_tkUv
zaF95UaSi-s1dh`A#ybaZ9b72O?hE4&rhjU8&c$Y9XGqu7lxOi;RO;a^6Ks61jv7kqvl^@_cNt!ei?m{!36z$NkC?$GEqziGm8U0&6&1G;pkwPhdHN6_{
zM)&>4t?Qtec$IUw?iAfB$~uH~U_!P)*iV^DZno#F{1auN=G)!LD>
zITybT5vcFbPofZp!o&s=Bcw6nau_`q4soOxIY`^lhlpZij|7fbn-oqM>aK5a!>=ya*kE
F{{sePXHNhC
diff --git a/graph/directed_acyclic_graph/__pycache__/DAGPresent.cpython-312.pyc b/graph/directed_acyclic_graph/__pycache__/DAGPresent.cpython-312.pyc
index a6faba88da781717ecccdbfc9c265d39fbf78fc8..c960f8570e0c6176ac3aea41c65a7d8a438319d6 100644
GIT binary patch
literal 23959
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
delta 6231
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
ZPWfm3p=p2UM&pdVjxWCOT?zfi{{YuWh*|&u