memory-graph 0.3.30__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- memory_graph/__init__.py +322 -0
- memory_graph/call_stack.py +9 -0
- memory_graph/config.py +35 -0
- memory_graph/config_default.py +108 -0
- memory_graph/config_helpers.py +56 -0
- memory_graph/extension_numpy.py +30 -0
- memory_graph/extension_pandas.py +26 -0
- memory_graph/html_table.py +137 -0
- memory_graph/list_view.py +57 -0
- memory_graph/memory_to_nodes.py +200 -0
- memory_graph/node_base.py +136 -0
- memory_graph/node_key_value.py +124 -0
- memory_graph/node_leaf.py +23 -0
- memory_graph/node_linear.py +100 -0
- memory_graph/node_table.py +83 -0
- memory_graph/sequence.py +111 -0
- memory_graph/slicer.py +46 -0
- memory_graph/slices.py +163 -0
- memory_graph/slices_iterator.py +55 -0
- memory_graph/slices_table_iterator.py +79 -0
- memory_graph/test.py +245 -0
- memory_graph/test_max_graph_depth.py +27 -0
- memory_graph/test_memory_graph.py +15 -0
- memory_graph/test_memory_to_nodes.py +15 -0
- memory_graph/test_sequence.py +37 -0
- memory_graph/test_slicer.py +50 -0
- memory_graph/test_slices.py +91 -0
- memory_graph/test_slices_iterator.py +28 -0
- memory_graph/utils.py +105 -0
- memory_graph-0.3.30.dist-info/METADATA +897 -0
- memory_graph-0.3.30.dist-info/RECORD +34 -0
- memory_graph-0.3.30.dist-info/WHEEL +5 -0
- memory_graph-0.3.30.dist-info/licenses/LICENSE.txt +25 -0
- memory_graph-0.3.30.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# This file is part of memory_graph.
|
|
2
|
+
# Copyright (c) 2023, Bas Terwijn.
|
|
3
|
+
# SPDX-License-Identifier: BSD-2-Clause
|
|
4
|
+
|
|
5
|
+
from memory_graph.node_base import Node_Base
|
|
6
|
+
from memory_graph.sequence import Sequence1D
|
|
7
|
+
|
|
8
|
+
import memory_graph.config_helpers as config_helpers
|
|
9
|
+
|
|
10
|
+
class Node_Linear(Node_Base):
|
|
11
|
+
"""
|
|
12
|
+
Node_Linear (subclass of Node_Base) is a node that represents a linear sequence
|
|
13
|
+
of data used for most iterable type like list, tuple, set, etc.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, data, children=None):
|
|
17
|
+
"""
|
|
18
|
+
Create a Node_Linear object. Use a Slicer to slice the children so the
|
|
19
|
+
Node_Base will not get to big or have too many childeren in the graph.
|
|
20
|
+
"""
|
|
21
|
+
super().__init__(data, Sequence1D(children))
|
|
22
|
+
|
|
23
|
+
def has_references(self, nodes, slices):
|
|
24
|
+
"""
|
|
25
|
+
Return if the node has references to other nodes.
|
|
26
|
+
"""
|
|
27
|
+
for index in slices:
|
|
28
|
+
child_id = id(self.children[index])
|
|
29
|
+
if child_id in nodes:
|
|
30
|
+
child = nodes[child_id]
|
|
31
|
+
if not child.is_hidden_node():
|
|
32
|
+
return True
|
|
33
|
+
return False
|
|
34
|
+
|
|
35
|
+
def is_vertical(self, nodes, slices, id_to_slices):
|
|
36
|
+
"""
|
|
37
|
+
Return if the node is vertical or horizontal based on the orientation of the children.
|
|
38
|
+
"""
|
|
39
|
+
vertical = config_helpers.get_vertical_orientation(self, None)
|
|
40
|
+
if vertical is None:
|
|
41
|
+
vertical = not self.has_references(nodes, slices)
|
|
42
|
+
return vertical
|
|
43
|
+
|
|
44
|
+
def fill_html_table(self, nodes, html_table, slices, id_to_slices):
|
|
45
|
+
"""
|
|
46
|
+
Fill the html_table with the children of the Node_Base.
|
|
47
|
+
"""
|
|
48
|
+
if slices is None:
|
|
49
|
+
return
|
|
50
|
+
if self.is_vertical(nodes, slices, id_to_slices):
|
|
51
|
+
self.fill_html_table_vertical(html_table, nodes, slices, id_to_slices)
|
|
52
|
+
else:
|
|
53
|
+
self.fill_html_table_horizontal(html_table, nodes, slices, id_to_slices)
|
|
54
|
+
|
|
55
|
+
def fill_html_table_vertical(self, html_table, nodes, slices, id_to_slices):
|
|
56
|
+
"""
|
|
57
|
+
Helper function to fill the html_table with the children of the Node_Base in vertical orientation.
|
|
58
|
+
"""
|
|
59
|
+
for index in slices.table_iter(self.children.size()):
|
|
60
|
+
if index>=0:
|
|
61
|
+
html_table.add_index(index)
|
|
62
|
+
child = self.children[index]
|
|
63
|
+
html_table.add_entry(self, nodes, child, id_to_slices, dashed=slices.is_dashed(index))
|
|
64
|
+
html_table.add_new_line()
|
|
65
|
+
else:
|
|
66
|
+
html_table.add_value('', border=0)
|
|
67
|
+
html_table.add_dots()
|
|
68
|
+
html_table.add_new_line()
|
|
69
|
+
|
|
70
|
+
def fill_html_table_horizontal(self, html_table, nodes, slices, id_to_slices):
|
|
71
|
+
"""
|
|
72
|
+
Helper function to fill the html_table with the children of the Node_Base in horizontal orientation.
|
|
73
|
+
"""
|
|
74
|
+
for index in slices.table_iter(self.children.size()):
|
|
75
|
+
if index>=0:
|
|
76
|
+
html_table.add_index(index)
|
|
77
|
+
else:
|
|
78
|
+
html_table.add_value('', border=0)
|
|
79
|
+
html_table.add_new_line()
|
|
80
|
+
for index in slices.table_iter(self.children.size()):
|
|
81
|
+
if index>=0:
|
|
82
|
+
child = self.children[index]
|
|
83
|
+
html_table.add_entry(self, nodes, child, id_to_slices, dashed=slices.is_dashed(index))
|
|
84
|
+
else:
|
|
85
|
+
html_table.add_dots()
|
|
86
|
+
|
|
87
|
+
def get_label(self, slices):
|
|
88
|
+
"""
|
|
89
|
+
Return a label for the node to be shown in the graph next to the HTML table.
|
|
90
|
+
"""
|
|
91
|
+
type_name = self.get_type_name()
|
|
92
|
+
if slices is None:
|
|
93
|
+
return f'{type_name}'
|
|
94
|
+
size = self.get_children().size()
|
|
95
|
+
s = slices.get_slices()
|
|
96
|
+
if len(s) == 1:
|
|
97
|
+
if s[0][1] - s[0][0] == size:
|
|
98
|
+
return f'{type_name}'
|
|
99
|
+
return f'{type_name} {size}'
|
|
100
|
+
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# This file is part of memory_graph.
|
|
2
|
+
# Copyright (c) 2023, Bas Terwijn.
|
|
3
|
+
# SPDX-License-Identifier: BSD-2-Clause
|
|
4
|
+
|
|
5
|
+
from memory_graph.node_base import Node_Base
|
|
6
|
+
from memory_graph.sequence import Sequence2D
|
|
7
|
+
from memory_graph.list_view import List_View
|
|
8
|
+
|
|
9
|
+
class Node_Table(Node_Base):
|
|
10
|
+
"""
|
|
11
|
+
Node_Table (subclass of Node_Base) is a node that represents a 2D table of data used for
|
|
12
|
+
example for Numpy arrays and Pandas DataFrames.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, data, children, data_width=None, row_names=None, col_names=None):
|
|
16
|
+
"""
|
|
17
|
+
Create a Node_Table object. Use a Slicer to slice the children so the Node_Base
|
|
18
|
+
will not get to big or have too many childeren in the graph.
|
|
19
|
+
"""
|
|
20
|
+
self.row_names = row_names
|
|
21
|
+
self.col_names = col_names
|
|
22
|
+
if data_width is None:
|
|
23
|
+
super().__init__(data, Sequence2D(children))
|
|
24
|
+
else:
|
|
25
|
+
list_views = [List_View(children, i, i+data_width) for i in range(0,len(children),data_width)]
|
|
26
|
+
super().__init__(data, Sequence2D(list_views))
|
|
27
|
+
|
|
28
|
+
def add_index_or_name(self, html_table, index, names):
|
|
29
|
+
if not names is None and index < len(names):
|
|
30
|
+
html_table.add_value(names[index], rounded=1, border=1)
|
|
31
|
+
else:
|
|
32
|
+
html_table.add_index(index)
|
|
33
|
+
|
|
34
|
+
def fill_html_table(self, nodes, html_table, slices, id_to_slices):
|
|
35
|
+
"""
|
|
36
|
+
Fill the html_table with the children of the Node_Base.
|
|
37
|
+
"""
|
|
38
|
+
if slices is None or slices.is_empty():
|
|
39
|
+
return
|
|
40
|
+
children = self.children
|
|
41
|
+
children_size = children.size()
|
|
42
|
+
children_width = children_size[1]
|
|
43
|
+
col_slices = slices.get_col_slices()
|
|
44
|
+
|
|
45
|
+
# use column indices for header row
|
|
46
|
+
html_table.add_value('', border=0)
|
|
47
|
+
for coli in col_slices.table_iter(children_width):
|
|
48
|
+
if coli == -1:
|
|
49
|
+
html_table.add_value('', border=0)
|
|
50
|
+
else:
|
|
51
|
+
self.add_index_or_name(html_table, coli, self.col_names)
|
|
52
|
+
html_table.add_new_line()
|
|
53
|
+
|
|
54
|
+
# add remaing rows
|
|
55
|
+
first_col = True
|
|
56
|
+
for index in slices.table_iter(children_size):
|
|
57
|
+
rowi, coli = index
|
|
58
|
+
if first_col and not coli==-3:
|
|
59
|
+
first_col = False
|
|
60
|
+
self.add_index_or_name(html_table, rowi, self.row_names)
|
|
61
|
+
if coli == -1:
|
|
62
|
+
html_table.add_dots()
|
|
63
|
+
elif coli == -2:
|
|
64
|
+
html_table.add_new_line()
|
|
65
|
+
first_col = True
|
|
66
|
+
elif coli == -3:
|
|
67
|
+
html_table.add_new_line()
|
|
68
|
+
html_table.add_value('', border=0)
|
|
69
|
+
for _ in range (html_table.get_max_column()-1):
|
|
70
|
+
html_table.add_dots()
|
|
71
|
+
html_table.add_new_line()
|
|
72
|
+
first_col = True
|
|
73
|
+
else:
|
|
74
|
+
child = children[index]
|
|
75
|
+
html_table.add_entry(self, nodes, child, id_to_slices, dashed=slices.is_dashed(index))
|
|
76
|
+
|
|
77
|
+
def get_label(self, slices):
|
|
78
|
+
"""
|
|
79
|
+
Return a label for the node to be shown in the graph next to the HTML table.
|
|
80
|
+
"""
|
|
81
|
+
size = self.get_children().size()
|
|
82
|
+
return f'{self.get_type_name()} {size[0]}⨯{size[1]}'
|
|
83
|
+
|
memory_graph/sequence.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# This file is part of memory_graph.
|
|
2
|
+
# Copyright (c) 2023, Bas Terwijn.
|
|
3
|
+
# SPDX-License-Identifier: BSD-2-Clause
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
|
|
7
|
+
from memory_graph.slices import Slices1D, Slices2D
|
|
8
|
+
import memory_graph.utils as utils
|
|
9
|
+
|
|
10
|
+
class Sequence(ABC):
|
|
11
|
+
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def is_empty(self):
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
@abstractmethod
|
|
17
|
+
def size(self):
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
@abstractmethod
|
|
21
|
+
def empty_slices(self):
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
@abstractmethod
|
|
25
|
+
def slice(self, slicer):
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
@abstractmethod
|
|
29
|
+
def indices_all(self):
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
@abstractmethod
|
|
33
|
+
def __getitem__(self, index):
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
def __setitem__(self, index, value):
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
class Sequence1D(Sequence):
|
|
41
|
+
|
|
42
|
+
def __init__(self, data):
|
|
43
|
+
self.data = utils.make_sliceable(data)
|
|
44
|
+
|
|
45
|
+
def __repr__(self):
|
|
46
|
+
return f'Sequence1D: {self.data}'
|
|
47
|
+
|
|
48
|
+
def is_empty(self):
|
|
49
|
+
return len(self.data) == 0
|
|
50
|
+
|
|
51
|
+
def size(self):
|
|
52
|
+
return len(self.data)
|
|
53
|
+
|
|
54
|
+
def empty_slices(self):
|
|
55
|
+
return Slices1D()
|
|
56
|
+
|
|
57
|
+
def slice(self, slicer):
|
|
58
|
+
return slicer.get_slices( len(self.data) )
|
|
59
|
+
|
|
60
|
+
def indices_all(self):
|
|
61
|
+
for i in range(len(self.data)):
|
|
62
|
+
yield i
|
|
63
|
+
|
|
64
|
+
def __getitem__(self, index):
|
|
65
|
+
return self.data[index]
|
|
66
|
+
|
|
67
|
+
def __setitem__(self, index, value):
|
|
68
|
+
self.data[index] = value
|
|
69
|
+
|
|
70
|
+
class Sequence2D(Sequence):
|
|
71
|
+
|
|
72
|
+
def __init__(self, data):
|
|
73
|
+
self.data = utils.make_sliceable(data)
|
|
74
|
+
|
|
75
|
+
def __repr__(self):
|
|
76
|
+
return f'Sequence2D: {self.data}'
|
|
77
|
+
|
|
78
|
+
def is_empty(self):
|
|
79
|
+
return len(self.data) == 0
|
|
80
|
+
|
|
81
|
+
def size(self):
|
|
82
|
+
l1, l2 = len(self.data), 0
|
|
83
|
+
if l1 > 0:
|
|
84
|
+
l2 = len(self.data[0])
|
|
85
|
+
return l1, l2
|
|
86
|
+
|
|
87
|
+
def empty_slices(self):
|
|
88
|
+
return Slices2D()
|
|
89
|
+
|
|
90
|
+
def slice(self, slicer0):
|
|
91
|
+
if type(slicer0) is tuple:
|
|
92
|
+
slicer0, slicer1 = slicer0
|
|
93
|
+
else:
|
|
94
|
+
slicer1 = slicer0
|
|
95
|
+
slices0 = slicer0.get_slices( len(self.data) )
|
|
96
|
+
slices1 = slicer1.get_slices( len(self.data[0]) )
|
|
97
|
+
return Slices2D(slices0, slices1)
|
|
98
|
+
|
|
99
|
+
def indices_all(self):
|
|
100
|
+
len0 = len(self.data)
|
|
101
|
+
if len0 > 0:
|
|
102
|
+
len1 = len(self.data[0])
|
|
103
|
+
for y in range(len0):
|
|
104
|
+
for x in range(len1):
|
|
105
|
+
yield (y,x)
|
|
106
|
+
|
|
107
|
+
def __getitem__(self, index):
|
|
108
|
+
return self.data[index[0]][index[1]]
|
|
109
|
+
|
|
110
|
+
def __setitem__(self, index, value):
|
|
111
|
+
self.data[index[0]][index[1]] = value
|
memory_graph/slicer.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# This file is part of memory_graph.
|
|
2
|
+
# Copyright (c) 2023, Bas Terwijn.
|
|
3
|
+
# SPDX-License-Identifier: BSD-2-Clause
|
|
4
|
+
|
|
5
|
+
from memory_graph.slices import Slices1D
|
|
6
|
+
import memory_graph.utils as utils
|
|
7
|
+
|
|
8
|
+
class Slicer:
|
|
9
|
+
|
|
10
|
+
def __init__(self, begin=None, end=None, middle=None, /) -> None:
|
|
11
|
+
self.begin = begin
|
|
12
|
+
self.end = end
|
|
13
|
+
self.middle = middle
|
|
14
|
+
if not self.middle is None:
|
|
15
|
+
self.end, self.middle = self.middle, self.end
|
|
16
|
+
|
|
17
|
+
def __repr__(self) -> str:
|
|
18
|
+
return f"Slicer({self.begin},{self.middle},{self.end})"
|
|
19
|
+
|
|
20
|
+
def get_slices(self, length):
|
|
21
|
+
slices1d = Slices1D()
|
|
22
|
+
if self.begin is None:
|
|
23
|
+
slices1d.add_slice([0, length])
|
|
24
|
+
else:
|
|
25
|
+
if isinstance(self.begin, float):
|
|
26
|
+
slices1d.add_slice([0,
|
|
27
|
+
min(length,utils.my_round(length*self.begin))])
|
|
28
|
+
else:
|
|
29
|
+
slices1d.add_slice([0,
|
|
30
|
+
min(length,self.begin)])
|
|
31
|
+
if not self.middle is None:
|
|
32
|
+
mid = length/2
|
|
33
|
+
if isinstance(self.middle, float):
|
|
34
|
+
half = length*self.middle/2
|
|
35
|
+
else:
|
|
36
|
+
half = self.middle/2
|
|
37
|
+
slices1d.add_slice([max(0,utils.my_round(mid-half)),
|
|
38
|
+
min(length,utils.my_round(mid+half))])
|
|
39
|
+
if not self.end is None:
|
|
40
|
+
if isinstance(self.end, float):
|
|
41
|
+
slices1d.add_slice([max(0,utils.my_round(length-length*self.end)),
|
|
42
|
+
length])
|
|
43
|
+
else:
|
|
44
|
+
slices1d.add_slice([max(0,length-self.end),
|
|
45
|
+
length])
|
|
46
|
+
return slices1d
|
memory_graph/slices.py
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# This file is part of memory_graph.
|
|
2
|
+
# Copyright (c) 2023, Bas Terwijn.
|
|
3
|
+
# SPDX-License-Identifier: BSD-2-Clause
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
|
|
7
|
+
import bisect
|
|
8
|
+
import copy
|
|
9
|
+
|
|
10
|
+
from memory_graph.slices_iterator import Slices_Iterator1D, Slices_Iterator2D
|
|
11
|
+
from memory_graph.slices_table_iterator import Slices_Table_Iterator1D, Slices_Table_Iterator2D
|
|
12
|
+
|
|
13
|
+
class Slices(ABC):
|
|
14
|
+
|
|
15
|
+
def __init__(self):
|
|
16
|
+
self.dashed = set()
|
|
17
|
+
|
|
18
|
+
def __repr__(self) -> str:
|
|
19
|
+
return f"dashed: {self.dashed}"
|
|
20
|
+
|
|
21
|
+
def is_dashed(self, index):
|
|
22
|
+
return index in self.dashed
|
|
23
|
+
|
|
24
|
+
@abstractmethod
|
|
25
|
+
def __iter__(self):
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
@abstractmethod
|
|
29
|
+
def has_index(self, index):
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
@abstractmethod
|
|
33
|
+
def add_index(self, index):
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
def table_iter(self, size):
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
@abstractmethod
|
|
41
|
+
def is_empty(self):
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
class Slices1D(Slices):
|
|
45
|
+
|
|
46
|
+
def __init__(self, slices=None) -> None:
|
|
47
|
+
super().__init__()
|
|
48
|
+
self.slices = []
|
|
49
|
+
if not slices is None:
|
|
50
|
+
for i in slices:
|
|
51
|
+
self.add_slice(i)
|
|
52
|
+
|
|
53
|
+
def __repr__(self) -> str:
|
|
54
|
+
return f"Slices1D({self.slices}) "+super().__repr__()
|
|
55
|
+
|
|
56
|
+
def get_iter(self,length):
|
|
57
|
+
return Slices_Iterator(self.slices,length)
|
|
58
|
+
|
|
59
|
+
def copy(self):
|
|
60
|
+
s = Slices1D()
|
|
61
|
+
s.slices = copy.deepcopy(self.slices)
|
|
62
|
+
s.dashed = copy.deepcopy(self.dashed)
|
|
63
|
+
return s
|
|
64
|
+
|
|
65
|
+
def get_slices(self):
|
|
66
|
+
return self.slices
|
|
67
|
+
|
|
68
|
+
def has_index(self, index):
|
|
69
|
+
for i in self.slices:
|
|
70
|
+
if i[0] <= index and index < i[1]:
|
|
71
|
+
return True
|
|
72
|
+
return False
|
|
73
|
+
|
|
74
|
+
def add_slice(self, begin_end, remove_interposed_dots=1):
|
|
75
|
+
i0, i1 = begin_end
|
|
76
|
+
if i1 <= i0:
|
|
77
|
+
return False
|
|
78
|
+
insert0 = bisect.bisect_right(self.slices, i0, key=lambda x: x[0])
|
|
79
|
+
insert1 = bisect.bisect_left (self.slices, i1, key=lambda x: x[1])
|
|
80
|
+
merge_begin, merge_end = False, False
|
|
81
|
+
if insert0 > 0:
|
|
82
|
+
if self.slices[insert0-1][1] >= (i0 - remove_interposed_dots):
|
|
83
|
+
merge_begin = True
|
|
84
|
+
if insert1 < len(self.slices):
|
|
85
|
+
if self.slices[insert1][0] <= (i1 + remove_interposed_dots):
|
|
86
|
+
merge_end = True
|
|
87
|
+
if merge_begin and merge_end:
|
|
88
|
+
if insert0 - insert1 == 1: # no slices changed
|
|
89
|
+
return False
|
|
90
|
+
self.slices[insert0-1][1] = self.slices[insert1][1]
|
|
91
|
+
del self.slices[insert0:insert1+1]
|
|
92
|
+
elif merge_begin:
|
|
93
|
+
self.slices[insert0-1][1] = max(self.slices[insert1-1][1], begin_end[1])
|
|
94
|
+
del self.slices[insert0:insert1]
|
|
95
|
+
elif merge_end:
|
|
96
|
+
self.slices[insert1][0] = min(self.slices[insert0][0], begin_end[0])
|
|
97
|
+
del self.slices[insert0:insert1]
|
|
98
|
+
else:
|
|
99
|
+
del self.slices[insert0:insert1]
|
|
100
|
+
self.slices.insert(insert0, begin_end)
|
|
101
|
+
return True
|
|
102
|
+
|
|
103
|
+
def __iter__(self):
|
|
104
|
+
return Slices_Iterator1D(self)
|
|
105
|
+
|
|
106
|
+
def has_index(self, index):
|
|
107
|
+
insert = bisect.bisect_right(self.slices, index, key=lambda x: x[0])
|
|
108
|
+
#print('insert:',insert,'index:',index,'slices:',self.slices)
|
|
109
|
+
if insert==0:
|
|
110
|
+
return False
|
|
111
|
+
i0, i1 = self.slices[insert-1]
|
|
112
|
+
return i0 <= index and index < i1
|
|
113
|
+
|
|
114
|
+
def add_index(self, index, dashed=False):
|
|
115
|
+
self.add_slice([index,index+1], 0)
|
|
116
|
+
if dashed:
|
|
117
|
+
self.dashed.add(index)
|
|
118
|
+
|
|
119
|
+
def table_iter(self, size):
|
|
120
|
+
return Slices_Table_Iterator1D(self, size)
|
|
121
|
+
|
|
122
|
+
def is_empty(self):
|
|
123
|
+
return len(self.slices) == 0
|
|
124
|
+
|
|
125
|
+
class Slices2D(Slices):
|
|
126
|
+
|
|
127
|
+
def __init__(self, row_slices = None, col_slices= None) -> None:
|
|
128
|
+
super().__init__()
|
|
129
|
+
self.row_slices = Slices1D() if row_slices is None else row_slices
|
|
130
|
+
self.col_slices = Slices1D() if col_slices is None else col_slices
|
|
131
|
+
|
|
132
|
+
def __repr__(self):
|
|
133
|
+
s='Sices2D:\n'
|
|
134
|
+
s += 'row_slices:' + str(self.row_slices) + '\n'
|
|
135
|
+
s += 'col_slices:' + str(self.col_slices) + '\n'
|
|
136
|
+
s += super().__repr__() + '\n'
|
|
137
|
+
return s
|
|
138
|
+
|
|
139
|
+
def get_row_slices(self):
|
|
140
|
+
return self.row_slices
|
|
141
|
+
|
|
142
|
+
def get_col_slices(self):
|
|
143
|
+
return self.col_slices
|
|
144
|
+
|
|
145
|
+
def __iter__(self):
|
|
146
|
+
return Slices_Iterator2D(self)
|
|
147
|
+
|
|
148
|
+
def has_index(self, index):
|
|
149
|
+
i0,i1 = index
|
|
150
|
+
return self.row_slices.has_index(i0) and self.col_slices.has_index(i1)
|
|
151
|
+
|
|
152
|
+
def add_index(self, index, dashed=False):
|
|
153
|
+
i0,i1 = index
|
|
154
|
+
self.row_slices.add_slice([i0,i0+1], 0)
|
|
155
|
+
self.col_slices.add_slice([i1,i1+1], 0)
|
|
156
|
+
if dashed:
|
|
157
|
+
self.dashed.add((i0,i1))
|
|
158
|
+
|
|
159
|
+
def table_iter(self, size):
|
|
160
|
+
return Slices_Table_Iterator2D(self, size)
|
|
161
|
+
|
|
162
|
+
def is_empty(self):
|
|
163
|
+
return len(self.row_slices.slices) == 0
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# This file is part of memory_graph.
|
|
2
|
+
# Copyright (c) 2023, Bas Terwijn.
|
|
3
|
+
# SPDX-License-Identifier: BSD-2-Clause
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from abc import ABC, abstractmethod
|
|
7
|
+
|
|
8
|
+
class Slices_Iterator(ABC):
|
|
9
|
+
|
|
10
|
+
@abstractmethod
|
|
11
|
+
def __iter__(self):
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
@abstractmethod
|
|
15
|
+
def __next__(self):
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
class Slices_Iterator1D(Slices_Iterator):
|
|
19
|
+
|
|
20
|
+
def __init__(self, slices1d):
|
|
21
|
+
self.slices = slices1d
|
|
22
|
+
self.gen = self.generate()
|
|
23
|
+
|
|
24
|
+
def __iter__(self):
|
|
25
|
+
return self
|
|
26
|
+
|
|
27
|
+
def generate(self):
|
|
28
|
+
slices = self.slices.get_slices()
|
|
29
|
+
for si in range(len(slices)):
|
|
30
|
+
for i in range(slices[si][0], slices[si][1]):
|
|
31
|
+
yield i
|
|
32
|
+
|
|
33
|
+
def __next__(self):
|
|
34
|
+
return next(self.gen)
|
|
35
|
+
|
|
36
|
+
class Slices_Iterator2D(Slices_Iterator):
|
|
37
|
+
|
|
38
|
+
def __init__(self, slices2d):
|
|
39
|
+
self.slices = slices2d
|
|
40
|
+
self.gen = self.generate()
|
|
41
|
+
|
|
42
|
+
def __iter__(self):
|
|
43
|
+
return self
|
|
44
|
+
|
|
45
|
+
def generate(self):
|
|
46
|
+
row_slices = self.slices.get_row_slices().get_slices()
|
|
47
|
+
col_slices = self.slices.get_col_slices().get_slices()
|
|
48
|
+
for row_slice in row_slices:
|
|
49
|
+
for row_i in range(row_slice[0], row_slice[1]):
|
|
50
|
+
for col_slice in col_slices:
|
|
51
|
+
for col_i in range(col_slice[0], col_slice[1]):
|
|
52
|
+
yield (row_i, col_i)
|
|
53
|
+
|
|
54
|
+
def __next__(self):
|
|
55
|
+
return next(self.gen)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# This file is part of memory_graph.
|
|
2
|
+
# Copyright (c) 2023, Bas Terwijn.
|
|
3
|
+
# SPDX-License-Identifier: BSD-2-Clause
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
|
|
7
|
+
class Slices_Table_Iterator(ABC):
|
|
8
|
+
|
|
9
|
+
@abstractmethod
|
|
10
|
+
def __iter__(self):
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def __next__(self):
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
class Slices_Table_Iterator1D(Slices_Table_Iterator):
|
|
18
|
+
|
|
19
|
+
def __init__(self, slices1d, size):
|
|
20
|
+
self.slices = slices1d
|
|
21
|
+
self.size = size
|
|
22
|
+
self.gen = self.generate()
|
|
23
|
+
|
|
24
|
+
def __iter__(self):
|
|
25
|
+
return self
|
|
26
|
+
|
|
27
|
+
def generate(self):
|
|
28
|
+
slices = self.slices.get_slices()
|
|
29
|
+
if len(slices) > 0 and slices[0][0] > 0:
|
|
30
|
+
yield -1
|
|
31
|
+
for slice in slices:
|
|
32
|
+
for i in range(slice[0], slice[1]):
|
|
33
|
+
yield i
|
|
34
|
+
if i < self.size-1:
|
|
35
|
+
yield -1
|
|
36
|
+
|
|
37
|
+
def __next__(self):
|
|
38
|
+
return next(self.gen)
|
|
39
|
+
|
|
40
|
+
class Slices_Table_Iterator2D(Slices_Table_Iterator):
|
|
41
|
+
|
|
42
|
+
def __init__(self, slices2d, size):
|
|
43
|
+
self.slices = slices2d
|
|
44
|
+
self.size = size
|
|
45
|
+
self.gen = self.generate()
|
|
46
|
+
|
|
47
|
+
def __iter__(self):
|
|
48
|
+
return self
|
|
49
|
+
|
|
50
|
+
def generate(self):
|
|
51
|
+
row_slices = self.slices.get_row_slices().get_slices()
|
|
52
|
+
col_slices = self.slices.get_col_slices().get_slices()
|
|
53
|
+
first_row_slice = True
|
|
54
|
+
for row_slice in row_slices:
|
|
55
|
+
if first_row_slice:
|
|
56
|
+
if len(row_slices) > 0 and row_slice[0] > 0:
|
|
57
|
+
yield (-3, -3)
|
|
58
|
+
first_col_slice = False
|
|
59
|
+
else:
|
|
60
|
+
yield (-3, -3)
|
|
61
|
+
for row_i in range(row_slice[0], row_slice[1]):
|
|
62
|
+
first_col_slice = True
|
|
63
|
+
for col_slice in col_slices:
|
|
64
|
+
if first_col_slice:
|
|
65
|
+
if len(col_slices) > 0 and col_slice[0] > 0:
|
|
66
|
+
yield (row_i, -1)
|
|
67
|
+
first_col_slice = False
|
|
68
|
+
else:
|
|
69
|
+
yield (row_i, -1)
|
|
70
|
+
for col_i in range(col_slice[0], col_slice[1]):
|
|
71
|
+
yield (row_i, col_i)
|
|
72
|
+
if col_i < self.size[1]-1:
|
|
73
|
+
yield (row_i, -1)
|
|
74
|
+
yield (row_i, -2)
|
|
75
|
+
if len(row_slices)>0 and row_i < self.size[0]-1:
|
|
76
|
+
yield (row_i, -3)
|
|
77
|
+
|
|
78
|
+
def __next__(self):
|
|
79
|
+
return next(self.gen)
|