pd-code-connected-sum 0.0.3__tar.gz → 0.1.1__tar.gz
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.
- pd_code_connected_sum-0.1.1/PKG-INFO +67 -0
- pd_code_connected_sum-0.1.1/README.md +46 -0
- pd_code_connected_sum-0.1.1/pd_code_connected_sum/__init__.py +6 -0
- pd_code_connected_sum-0.1.1/pd_code_connected_sum/main.py +90 -0
- {pd_code_connected_sum-0.0.3 → pd_code_connected_sum-0.1.1}/pyproject.toml +3 -3
- pd_code_connected_sum-0.0.3/PKG-INFO +0 -50
- pd_code_connected_sum-0.0.3/README.md +0 -29
- pd_code_connected_sum-0.0.3/pd_code_connected_sum/__init__.py +0 -5
- pd_code_connected_sum-0.0.3/pd_code_connected_sum/main.py +0 -245
- {pd_code_connected_sum-0.0.3 → pd_code_connected_sum-0.1.1}/LICENSE +0 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pd-code-connected-sum
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: make connected sum for link pd_code
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Author: GGN_2015
|
|
8
|
+
Author-email: neko@jlulug.org
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Requires-Dist: pd-code-pre-nxt (>=0.1.0)
|
|
18
|
+
Requires-Dist: pd-code-sanity (>=0.1.0)
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# pd-code-connected-sum
|
|
22
|
+
|
|
23
|
+
Join two selected oriented PD-code components and canonically renumber the result.
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install pd-code-connected-sum
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Usage example
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from pd_code_connected_sum import connected_sum
|
|
35
|
+
|
|
36
|
+
left = [[1, 5, 2, 4], [3, 1, 4, 6], [5, 3, 6, 2]]
|
|
37
|
+
right = [[1, 5, 2, 4], [3, 1, 4, 6], [5, 3, 6, 2]]
|
|
38
|
+
result, label_map = connected_sum(left, right, 1, 1)
|
|
39
|
+
print(result)
|
|
40
|
+
print(label_map["a_1"], label_map["b_1"])
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Algorithm
|
|
44
|
+
|
|
45
|
+
Each input component is converted to a deterministic oriented cycle. The algorithm locates the incidence immediately after each selected arc, offsets the second code to avoid collisions, cuts those two oriented arcs, and glues the endpoints crosswise. It then canonically orders component cycles, assigns contiguous labels, orients every crossing consistently, and returns maps for all original labels. This avoids the incorrect local crossing replacement used by older releases.
|
|
46
|
+
|
|
47
|
+
## Input conventions
|
|
48
|
+
|
|
49
|
+
A PD code is represented as a list of four-entry crossings. Arc labels normally occur exactly twice. Public functions validate inputs and return new values rather than mutating caller-owned data unless their API explicitly says otherwise.
|
|
50
|
+
|
|
51
|
+
## External software
|
|
52
|
+
|
|
53
|
+
No external software is required.
|
|
54
|
+
|
|
55
|
+
## Development
|
|
56
|
+
|
|
57
|
+
Run examples and package checks before release. Python packages require Python 3.10 or newer. Build PyPI artifacts with:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
poetry check
|
|
61
|
+
poetry build
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## License
|
|
65
|
+
|
|
66
|
+
MIT. See `LICENSE`.
|
|
67
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# pd-code-connected-sum
|
|
2
|
+
|
|
3
|
+
Join two selected oriented PD-code components and canonically renumber the result.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install pd-code-connected-sum
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage example
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from pd_code_connected_sum import connected_sum
|
|
15
|
+
|
|
16
|
+
left = [[1, 5, 2, 4], [3, 1, 4, 6], [5, 3, 6, 2]]
|
|
17
|
+
right = [[1, 5, 2, 4], [3, 1, 4, 6], [5, 3, 6, 2]]
|
|
18
|
+
result, label_map = connected_sum(left, right, 1, 1)
|
|
19
|
+
print(result)
|
|
20
|
+
print(label_map["a_1"], label_map["b_1"])
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Algorithm
|
|
24
|
+
|
|
25
|
+
Each input component is converted to a deterministic oriented cycle. The algorithm locates the incidence immediately after each selected arc, offsets the second code to avoid collisions, cuts those two oriented arcs, and glues the endpoints crosswise. It then canonically orders component cycles, assigns contiguous labels, orients every crossing consistently, and returns maps for all original labels. This avoids the incorrect local crossing replacement used by older releases.
|
|
26
|
+
|
|
27
|
+
## Input conventions
|
|
28
|
+
|
|
29
|
+
A PD code is represented as a list of four-entry crossings. Arc labels normally occur exactly twice. Public functions validate inputs and return new values rather than mutating caller-owned data unless their API explicitly says otherwise.
|
|
30
|
+
|
|
31
|
+
## External software
|
|
32
|
+
|
|
33
|
+
No external software is required.
|
|
34
|
+
|
|
35
|
+
## Development
|
|
36
|
+
|
|
37
|
+
Run examples and package checks before release. Python packages require Python 3.10 or newer. Build PyPI artifacts with:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
poetry check
|
|
41
|
+
poetry build
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## License
|
|
45
|
+
|
|
46
|
+
MIT. See `LICENSE`.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from copy import deepcopy
|
|
2
|
+
|
|
3
|
+
import pd_code_pre_nxt
|
|
4
|
+
import pd_code_sanity
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _normalize(pd_code: list[list[int]]) -> tuple[list[list[int]], dict[int, int]]:
|
|
8
|
+
if not pd_code:
|
|
9
|
+
return [], {}
|
|
10
|
+
pre, nxt = pd_code_pre_nxt.get_pre_nxt(pd_code)
|
|
11
|
+
old_to_new: dict[int, int] = {}
|
|
12
|
+
next_label = 1
|
|
13
|
+
for start in pd_code_pre_nxt.get_num_set(pd_code):
|
|
14
|
+
if start in old_to_new:
|
|
15
|
+
continue
|
|
16
|
+
current = start
|
|
17
|
+
while current not in old_to_new:
|
|
18
|
+
old_to_new[current] = next_label
|
|
19
|
+
next_label += 1
|
|
20
|
+
current = nxt[current]
|
|
21
|
+
|
|
22
|
+
normalized = [[old_to_new[label] for label in crossing] for crossing in pd_code]
|
|
23
|
+
new_next = {old_to_new[label]: old_to_new[target] for label, target in nxt.items()}
|
|
24
|
+
for index, crossing in enumerate(normalized):
|
|
25
|
+
if new_next[crossing[0]] == crossing[2]:
|
|
26
|
+
continue
|
|
27
|
+
if new_next[crossing[2]] == crossing[0]:
|
|
28
|
+
normalized[index] = crossing[2:] + crossing[:2]
|
|
29
|
+
else:
|
|
30
|
+
raise ValueError("crossing is inconsistent with component orientation")
|
|
31
|
+
normalized.sort()
|
|
32
|
+
if not pd_code_sanity.sanity(normalized):
|
|
33
|
+
raise AssertionError("normalization produced an invalid PD code")
|
|
34
|
+
return normalized, old_to_new
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def normalize_pd_code(pd_code: list[list[int]]) -> tuple[list[list[int]], dict[int, int]]:
|
|
38
|
+
"""Return a canonically oriented, contiguously labelled copy and its map."""
|
|
39
|
+
if not pd_code_sanity.sanity(pd_code):
|
|
40
|
+
raise ValueError("invalid PD code")
|
|
41
|
+
return _normalize(deepcopy(pd_code))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _endpoint(pd_code, label, nxt, pre, want_after: bool) -> tuple[int, int]:
|
|
45
|
+
slots = [(i, j) for i, crossing in enumerate(pd_code) for j, value in enumerate(crossing) if value == label]
|
|
46
|
+
if len(slots) != 2:
|
|
47
|
+
raise ValueError("connected-sum label must occur exactly twice")
|
|
48
|
+
if nxt[label] == pre[label]:
|
|
49
|
+
return slots[1 if want_after else 0]
|
|
50
|
+
target = nxt[label] if want_after else pre[label]
|
|
51
|
+
for i, j in slots:
|
|
52
|
+
if pd_code[i][(j + 2) % 4] == target:
|
|
53
|
+
return i, j
|
|
54
|
+
raise ValueError("could not locate oriented endpoint for label")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def connected_sum(
|
|
58
|
+
pd_code1: list[list[int]], pd_code2: list[list[int]], val_1: int, val_2: int
|
|
59
|
+
) -> tuple[list[list[int]], dict[str, int]]:
|
|
60
|
+
"""Join the oriented components containing ``val_1`` and ``val_2``."""
|
|
61
|
+
for pd_code in (pd_code1, pd_code2):
|
|
62
|
+
if not pd_code_sanity.sanity(pd_code):
|
|
63
|
+
raise ValueError("invalid PD code")
|
|
64
|
+
if any(isinstance(x, bool) or not isinstance(x, int) for crossing in pd_code for x in crossing):
|
|
65
|
+
raise TypeError("connected_sum requires integer labels")
|
|
66
|
+
|
|
67
|
+
if not pd_code1 or not pd_code2:
|
|
68
|
+
normalized, mapping = _normalize(deepcopy(pd_code1 or pd_code2))
|
|
69
|
+
prefix = "a_" if pd_code1 else "b_"
|
|
70
|
+
return normalized, {prefix + str(old): new for old, new in mapping.items()}
|
|
71
|
+
if sum(label == val_1 for crossing in pd_code1 for label in crossing) != 2:
|
|
72
|
+
raise ValueError("val_1 must occur exactly twice in pd_code1")
|
|
73
|
+
if sum(label == val_2 for crossing in pd_code2 for label in crossing) != 2:
|
|
74
|
+
raise ValueError("val_2 must occur exactly twice in pd_code2")
|
|
75
|
+
|
|
76
|
+
first = deepcopy(pd_code1)
|
|
77
|
+
offset = max(label for crossing in first for label in crossing)
|
|
78
|
+
second = [[label + offset for label in crossing] for crossing in deepcopy(pd_code2)]
|
|
79
|
+
second_label = val_2 + offset
|
|
80
|
+
pre_a, nxt_a = pd_code_pre_nxt.get_pre_nxt(first)
|
|
81
|
+
pre_b, nxt_b = pd_code_pre_nxt.get_pre_nxt(second)
|
|
82
|
+
ai, aj = _endpoint(first, val_1, nxt_a, pre_a, True)
|
|
83
|
+
bi, bj = _endpoint(second, second_label, nxt_b, pre_b, True)
|
|
84
|
+
|
|
85
|
+
first[ai][aj] = second_label
|
|
86
|
+
second[bi][bj] = val_1
|
|
87
|
+
normalized, mapping = _normalize(first + second)
|
|
88
|
+
output_map = {"a_" + str(old): new for old, new in mapping.items() if old <= offset}
|
|
89
|
+
output_map.update({"b_" + str(old - offset): new for old, new in mapping.items() if old > offset})
|
|
90
|
+
return normalized, output_map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pd-code-connected-sum"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.1.1"
|
|
4
4
|
description = "make connected sum for link pd_code"
|
|
5
5
|
authors = [
|
|
6
6
|
{name = "GGN_2015",email = "neko@jlulug.org"}
|
|
@@ -9,8 +9,8 @@ license = {text = "MIT"}
|
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
11
11
|
dependencies = [
|
|
12
|
-
"pd-code-sanity",
|
|
13
|
-
"pd-code-pre-nxt"
|
|
12
|
+
"pd-code-sanity>=0.1.0",
|
|
13
|
+
"pd-code-pre-nxt>=0.1.0"
|
|
14
14
|
]
|
|
15
15
|
|
|
16
16
|
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: pd-code-connected-sum
|
|
3
|
-
Version: 0.0.3
|
|
4
|
-
Summary: make connected sum for link pd_code
|
|
5
|
-
License: MIT
|
|
6
|
-
License-File: LICENSE
|
|
7
|
-
Author: GGN_2015
|
|
8
|
-
Author-email: neko@jlulug.org
|
|
9
|
-
Requires-Python: >=3.10
|
|
10
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
-
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
-
Requires-Dist: pd-code-pre-nxt
|
|
18
|
-
Requires-Dist: pd-code-sanity
|
|
19
|
-
Description-Content-Type: text/markdown
|
|
20
|
-
|
|
21
|
-
# pd_code_connected_sum
|
|
22
|
-
caculate connected sum for link pd_code.
|
|
23
|
-
|
|
24
|
-
## Install
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
pip install pd-code-connected-sum
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## Usage
|
|
31
|
-
|
|
32
|
-
```python
|
|
33
|
-
import pd_code_connected_sum
|
|
34
|
-
|
|
35
|
-
# L2a1
|
|
36
|
-
link_pd_code_1 = [[4, 1, 3, 2], [2, 3, 1, 4]]
|
|
37
|
-
|
|
38
|
-
# L4a1
|
|
39
|
-
link_pd_code_2 = [[6, 1, 7, 2], [8, 3, 5, 4], [2, 5, 3, 6], [4, 7, 1, 8]]
|
|
40
|
-
|
|
41
|
-
connect_pos_1 = 1
|
|
42
|
-
connect_pos_2 = 1
|
|
43
|
-
|
|
44
|
-
new_pd_code, num_map = pd_code_connected_sum.connected_sum(
|
|
45
|
-
link_pd_code_1, link_pd_code_2, connect_pos_1, connect_pos_2)
|
|
46
|
-
|
|
47
|
-
print(new_pd_code)
|
|
48
|
-
print(num_map)
|
|
49
|
-
```
|
|
50
|
-
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# pd_code_connected_sum
|
|
2
|
-
caculate connected sum for link pd_code.
|
|
3
|
-
|
|
4
|
-
## Install
|
|
5
|
-
|
|
6
|
-
```bash
|
|
7
|
-
pip install pd-code-connected-sum
|
|
8
|
-
```
|
|
9
|
-
|
|
10
|
-
## Usage
|
|
11
|
-
|
|
12
|
-
```python
|
|
13
|
-
import pd_code_connected_sum
|
|
14
|
-
|
|
15
|
-
# L2a1
|
|
16
|
-
link_pd_code_1 = [[4, 1, 3, 2], [2, 3, 1, 4]]
|
|
17
|
-
|
|
18
|
-
# L4a1
|
|
19
|
-
link_pd_code_2 = [[6, 1, 7, 2], [8, 3, 5, 4], [2, 5, 3, 6], [4, 7, 1, 8]]
|
|
20
|
-
|
|
21
|
-
connect_pos_1 = 1
|
|
22
|
-
connect_pos_2 = 1
|
|
23
|
-
|
|
24
|
-
new_pd_code, num_map = pd_code_connected_sum.connected_sum(
|
|
25
|
-
link_pd_code_1, link_pd_code_2, connect_pos_1, connect_pos_2)
|
|
26
|
-
|
|
27
|
-
print(new_pd_code)
|
|
28
|
-
print(num_map)
|
|
29
|
-
```
|
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
import pd_code_sanity
|
|
2
|
-
import pd_code_pre_nxt
|
|
3
|
-
import json
|
|
4
|
-
|
|
5
|
-
# 为编码增加前缀
|
|
6
|
-
def add_prefix(pd_code:list[list], prefix:str) -> list[list]:
|
|
7
|
-
pd_code = json.loads(json.dumps(pd_code))
|
|
8
|
-
for i in range(len(pd_code)):
|
|
9
|
-
for j in range(len(pd_code[i])):
|
|
10
|
-
pd_code[i][j] = prefix + str(pd_code[i][j])
|
|
11
|
-
return json.loads(json.dumps(pd_code))
|
|
12
|
-
|
|
13
|
-
# 替换一个值恰好一次
|
|
14
|
-
def replace_val(crossing:list, val_now:str, val_new:str):
|
|
15
|
-
cnt = 0
|
|
16
|
-
for i in range(len(crossing)):
|
|
17
|
-
if crossing[i] == val_now:
|
|
18
|
-
crossing[i] = val_new
|
|
19
|
-
cnt += 1
|
|
20
|
-
if cnt != 1:
|
|
21
|
-
raise ValueError()
|
|
22
|
-
|
|
23
|
-
# 检查一个 crossing 是否包含某个值
|
|
24
|
-
def check_has(crossing:list, val_now:str) -> bool:
|
|
25
|
-
for i in range(len(crossing)):
|
|
26
|
-
if crossing[i] == val_now:
|
|
27
|
-
return True
|
|
28
|
-
return False
|
|
29
|
-
|
|
30
|
-
# 检查两个元素同时存在
|
|
31
|
-
def check_has2(crossing:list, val1:str, val2:str) -> bool:
|
|
32
|
-
return (
|
|
33
|
-
check_has(crossing, val1) and
|
|
34
|
-
check_has(crossing, val2))
|
|
35
|
-
|
|
36
|
-
get_num_set = pd_code_pre_nxt.get_num_set
|
|
37
|
-
|
|
38
|
-
# 按照顺序对循环进行遍历
|
|
39
|
-
def dfs(val, nxt:dict[str, str], vis: set[int], idx:dict[str, int]):
|
|
40
|
-
if val in vis:
|
|
41
|
-
return
|
|
42
|
-
assert nxt.get(val) is not None
|
|
43
|
-
|
|
44
|
-
# 向已经访问元素队列中添加该节点
|
|
45
|
-
vis.add(val)
|
|
46
|
-
idx[val] = len(vis) # 当前元素个数,就是当前节点新编号
|
|
47
|
-
|
|
48
|
-
if nxt[val] not in vis:
|
|
49
|
-
dfs(nxt[val], nxt, vis, idx)
|
|
50
|
-
|
|
51
|
-
# 在有向图中重新编号
|
|
52
|
-
def get_new_number_map(nxt:dict[str, str], num_set:list[int]) -> dict[str, int]:
|
|
53
|
-
vis = set()
|
|
54
|
-
idx = dict()
|
|
55
|
-
for val in num_set:
|
|
56
|
-
if val not in vis:
|
|
57
|
-
dfs(val, nxt, vis, idx)
|
|
58
|
-
return idx
|
|
59
|
-
|
|
60
|
-
# 检查有多少个 crossing 包含了 val_now
|
|
61
|
-
def count_crossing_exists(pd_code_now:list[list], val_now) -> int:
|
|
62
|
-
ans = 0
|
|
63
|
-
for crossing in pd_code_now:
|
|
64
|
-
if check_has(crossing, val_now):
|
|
65
|
-
ans += 1
|
|
66
|
-
return ans
|
|
67
|
-
|
|
68
|
-
# 找到同一个连通分量中合法的位置
|
|
69
|
-
def find_avaible_pos(pd_code:list[list], val_now):
|
|
70
|
-
for crossing in pd_code:
|
|
71
|
-
if check_has(crossing, val_now):
|
|
72
|
-
values = []
|
|
73
|
-
for i in range(len(crossing)):
|
|
74
|
-
if crossing[i] != val_now and crossing[i] not in values:
|
|
75
|
-
values.append(crossing[i])
|
|
76
|
-
if len(values) == 1:
|
|
77
|
-
return None
|
|
78
|
-
else:
|
|
79
|
-
return values[0]
|
|
80
|
-
|
|
81
|
-
# 在这里报错说明 pd_code 中没找到 val_now
|
|
82
|
-
raise AssertionError()
|
|
83
|
-
|
|
84
|
-
# 删除 pd_code1 中 val_1 所在的 crossing
|
|
85
|
-
# 然后把两个扭结直接合并
|
|
86
|
-
# 然后重新编号
|
|
87
|
-
def del_component_and_merge(pd_code1, pd_code2, val_1, val_2) -> tuple[list[list], dict]:
|
|
88
|
-
suc = False
|
|
89
|
-
for i in range(len(pd_code1)):
|
|
90
|
-
if check_has(pd_code1[i], val_1):
|
|
91
|
-
suc = True
|
|
92
|
-
pd_code1 = pd_code1[:i] + pd_code1[i+1:] # 跳过第 i 个元素
|
|
93
|
-
|
|
94
|
-
# 这里报错说明没找到 val_1 对应的 component
|
|
95
|
-
if not suc:
|
|
96
|
-
raise AssertionError()
|
|
97
|
-
|
|
98
|
-
# 获取元素集合
|
|
99
|
-
num_set_1 = add_prefix([get_num_set(pd_code1)], "a_")[0]
|
|
100
|
-
num_set_2 = add_prefix([get_num_set(pd_code2)], "b_")[0]
|
|
101
|
-
|
|
102
|
-
# 增加前缀编码
|
|
103
|
-
pd_code1 = add_prefix(pd_code1, "a_")
|
|
104
|
-
pd_code2 = add_prefix(pd_code2, "b_")
|
|
105
|
-
|
|
106
|
-
# 计算前驱后继
|
|
107
|
-
_, nxt1 = pd_code_pre_nxt.get_pre_nxt(pd_code1)
|
|
108
|
-
_, nxt2 = pd_code_pre_nxt.get_pre_nxt(pd_code2)
|
|
109
|
-
nxt = nxt1 | nxt2
|
|
110
|
-
|
|
111
|
-
# 合并得到新的 pd_code
|
|
112
|
-
new_pd_code = json.loads(json.dumps(pd_code1 + pd_code2))
|
|
113
|
-
|
|
114
|
-
# 获得新的编号方式
|
|
115
|
-
num_map = get_new_number_map(nxt, num_set_1 + num_set_2)
|
|
116
|
-
for crossing in new_pd_code:
|
|
117
|
-
for i in range(len(crossing)):
|
|
118
|
-
if num_map.get(crossing[i]) is None:
|
|
119
|
-
assert AssertionError()
|
|
120
|
-
crossing[i] = num_map[crossing[i]]
|
|
121
|
-
return sorted(new_pd_code), num_map
|
|
122
|
-
|
|
123
|
-
# 将 pd_code1 里面的 val_1 和 pd_code2 里面的 val_2 连接起来
|
|
124
|
-
def connected_sum(
|
|
125
|
-
pd_code1:list[list], pd_code2:list[list], val_1, val_2) -> tuple[list[list], dict]:
|
|
126
|
-
|
|
127
|
-
# 如果有一个扭结平凡,则不需要连接
|
|
128
|
-
if pd_code1 == [] or pd_code2 == []:
|
|
129
|
-
return json.loads(json.dumps(pd_code1 + pd_code2))
|
|
130
|
-
|
|
131
|
-
# 检查 pd_code 的弱合法性
|
|
132
|
-
for pd_code in [pd_code1, pd_code2]:
|
|
133
|
-
if not pd_code_sanity.sanity(pd_code):
|
|
134
|
-
raise TypeError()
|
|
135
|
-
|
|
136
|
-
# 检查 val_1 和 val_2 是否出现了
|
|
137
|
-
lis_wrap_1 = [0]
|
|
138
|
-
lis_wrap_2 = [0]
|
|
139
|
-
lis_wrap = [lis_wrap_1, lis_wrap_2]
|
|
140
|
-
for cnt_wrap, val_now, pd_now in [
|
|
141
|
-
(lis_wrap_1, val_1, pd_code1),
|
|
142
|
-
(lis_wrap_2, val_2, pd_code2)
|
|
143
|
-
]:
|
|
144
|
-
for crossing in pd_now:
|
|
145
|
-
for term in crossing:
|
|
146
|
-
if term == val_now:
|
|
147
|
-
cnt_wrap[0] += 1
|
|
148
|
-
|
|
149
|
-
# 检查出现次数是否正确
|
|
150
|
-
for i in range(2):
|
|
151
|
-
if lis_wrap[i][0] != 2:
|
|
152
|
-
raise ValueError(f"val_{i+1} not in pd_code{i+1}")
|
|
153
|
-
|
|
154
|
-
# 分别计算两个需要连接的地方
|
|
155
|
-
# 是不是在一个 r1 crossing 上
|
|
156
|
-
# 如果 has_val_x_cnt[0] != 2
|
|
157
|
-
# 则说明,val_x 在一个 r1 crossing
|
|
158
|
-
# 其中 x = 1 或者 2
|
|
159
|
-
has_val_1_cnt = [0]
|
|
160
|
-
has_val_2_cnt = [0]
|
|
161
|
-
for pd_code_now, has_val_cnt, val_now in [
|
|
162
|
-
(pd_code1, has_val_1_cnt, val_1),
|
|
163
|
-
(pd_code2, has_val_2_cnt, val_2)
|
|
164
|
-
]:
|
|
165
|
-
has_val_cnt[0] = count_crossing_exists(pd_code_now, val_now)
|
|
166
|
-
|
|
167
|
-
vals = [val_1, val_2]
|
|
168
|
-
for val_pos, pd_code_now, pd_code_other, has_cnt_now in [
|
|
169
|
-
(0, pd_code1, pd_code2, has_val_1_cnt),
|
|
170
|
-
(1, pd_code2, pd_code1, has_val_2_cnt),
|
|
171
|
-
]:
|
|
172
|
-
if has_cnt_now[0] != 2:
|
|
173
|
-
|
|
174
|
-
# 在同一个连通分量中
|
|
175
|
-
# 找到一个出现在多个 crossing 中的编号
|
|
176
|
-
vals_new = find_avaible_pos(pd_code_now, vals[val_pos])
|
|
177
|
-
if vals_new is None:
|
|
178
|
-
|
|
179
|
-
# 遇到了 [a, b, b, a] 或者 [a, a, b, b] 之类的
|
|
180
|
-
# 直接删掉当前 components 然后 merge 即可
|
|
181
|
-
return del_component_and_merge(
|
|
182
|
-
pd_code_now, pd_code_other, vals[val_pos], vals[1-val_pos])
|
|
183
|
-
|
|
184
|
-
vals[val_pos] = vals_new
|
|
185
|
-
|
|
186
|
-
# 覆盖旧的元素
|
|
187
|
-
val_1, val_2 = vals
|
|
188
|
-
|
|
189
|
-
# 获取元素集合
|
|
190
|
-
num_set_1 = add_prefix([get_num_set(pd_code1)], "a_")[0]
|
|
191
|
-
num_set_2 = add_prefix([get_num_set(pd_code2)], "b_")[0]
|
|
192
|
-
|
|
193
|
-
# 增加前缀编码
|
|
194
|
-
pd_code1 = add_prefix(pd_code1, "a_")
|
|
195
|
-
pd_code2 = add_prefix(pd_code2, "b_")
|
|
196
|
-
|
|
197
|
-
# 由于不存在 nugatory crossing 和 r1 crossing
|
|
198
|
-
# 所以两侧各有两个相关 crossing
|
|
199
|
-
_, nxt1 = pd_code_pre_nxt.get_pre_nxt(pd_code1)
|
|
200
|
-
_, nxt2 = pd_code_pre_nxt.get_pre_nxt(pd_code2)
|
|
201
|
-
nxt = nxt1 | nxt2
|
|
202
|
-
|
|
203
|
-
# 带有字母前缀的成分
|
|
204
|
-
a_val_1 = f"a_{val_1}"
|
|
205
|
-
b_val_2 = f"b_{val_2}"
|
|
206
|
-
|
|
207
|
-
# 将两组 pd_code 合并
|
|
208
|
-
# 需要注意 nxt[a_val_1] 和 pre[a_val_1] 可能相同
|
|
209
|
-
# 所以要保证只替换一次
|
|
210
|
-
suc_1 = False
|
|
211
|
-
suc_2 = False
|
|
212
|
-
new_pd_code = json.loads(json.dumps(pd_code1 + pd_code2))
|
|
213
|
-
for crossing in new_pd_code:
|
|
214
|
-
if not suc_1 and check_has2(crossing, a_val_1, nxt[a_val_1]):
|
|
215
|
-
replace_val(crossing, a_val_1, b_val_2)
|
|
216
|
-
suc_1 = True
|
|
217
|
-
if not suc_2 and check_has2(crossing, b_val_2, nxt[b_val_2]):
|
|
218
|
-
replace_val(crossing, b_val_2, a_val_1)
|
|
219
|
-
suc_2 = True
|
|
220
|
-
if suc_1 and suc_2:
|
|
221
|
-
break
|
|
222
|
-
|
|
223
|
-
# 调整前驱后继关系
|
|
224
|
-
nxt[b_val_2] = nxt1[a_val_1]
|
|
225
|
-
nxt[a_val_1] = nxt2[b_val_2]
|
|
226
|
-
|
|
227
|
-
# 获得新的编号方式
|
|
228
|
-
num_map = get_new_number_map(nxt, num_set_1 + num_set_2)
|
|
229
|
-
for crossing in new_pd_code:
|
|
230
|
-
for i in range(len(crossing)):
|
|
231
|
-
if num_map.get(crossing[i]) is None:
|
|
232
|
-
assert AssertionError()
|
|
233
|
-
crossing[i] = num_map[crossing[i]]
|
|
234
|
-
|
|
235
|
-
ans_pd_code = sorted(new_pd_code)
|
|
236
|
-
if not pd_code_sanity.sanity(ans_pd_code):
|
|
237
|
-
raise AssertionError()
|
|
238
|
-
|
|
239
|
-
return ans_pd_code, num_map
|
|
240
|
-
|
|
241
|
-
if __name__ == "__main__":
|
|
242
|
-
pd_code, num_map = connected_sum(
|
|
243
|
-
[[1, 5, 2, 4], [3, 1, 4, 6], [5, 3, 6, 2]],
|
|
244
|
-
[[1, 5, 2, 4], [3, 1, 4, 6], [5, 3, 6, 2]], 1, 1)
|
|
245
|
-
print(num_map)
|
|
File without changes
|