hits-to-stts 0.1.0__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.
- hits_to_stts-0.1.0/LICENSE +21 -0
- hits_to_stts-0.1.0/PKG-INFO +96 -0
- hits_to_stts-0.1.0/README.md +63 -0
- hits_to_stts-0.1.0/pyproject.toml +26 -0
- hits_to_stts-0.1.0/pyproject.toml.orig +24 -0
- hits_to_stts-0.1.0/src/hits_to_stts/__init__.py +173 -0
- hits_to_stts-0.1.0/src/hits_to_stts/__main__.py +25 -0
- hits_to_stts-0.1.0/src/hits_to_stts/data/HiTS-tagset.txt +1888 -0
- hits_to_stts-0.1.0/src/hits_to_stts/data/Tiger_v8_tagset.txt +967 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Türker Han
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: hits-to-stts
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Convert HiTS part-of-speech tags to STTS tags.
|
|
5
|
+
License: MIT License
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2024 Türker Han
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
in the Software without restriction, including without limitation the rights
|
|
12
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
SOFTWARE.
|
|
26
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
27
|
+
Classifier: Programming Language :: Python :: 3
|
|
28
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
29
|
+
Requires-Python: >=3.13
|
|
30
|
+
Project-URL: Repository, https://github.com/daanvdn/HiTS-to-STTS-Mapping
|
|
31
|
+
Project-URL: Upstream, https://github.com/tuerkerhan/HiTS-to-STTS-Mapping
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# HiTS to STTS Tagset Conversion
|
|
35
|
+
|
|
36
|
+
> [!NOTE]
|
|
37
|
+
> This repository is a fork of
|
|
38
|
+
> [tuerkerhan/HiTS-to-STTS-Mapping](https://github.com/tuerkerhan/HiTS-to-STTS-Mapping).
|
|
39
|
+
|
|
40
|
+
Convert HiTS (Historical Tagset) part-of-speech tags to STTS
|
|
41
|
+
(Stuttgart-Tuebingen Tagset) tags. The project requires Python 3.13 or newer
|
|
42
|
+
and uses [uv](https://docs.astral.sh/uv/) for project and package management.
|
|
43
|
+
|
|
44
|
+
## Development
|
|
45
|
+
|
|
46
|
+
Clone the repository and create the locked development environment:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
uv sync
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Run the tests:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
uv run python -m unittest discover -s tests
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Library usage
|
|
59
|
+
|
|
60
|
+
Install the project from a local checkout:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
uv add --editable ../HiTS-to-STTS-Mapping
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
It can then be imported like any other dependency:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from hits_to_stts import map_tag
|
|
70
|
+
|
|
71
|
+
mapped_tag = map_tag("ADJN.Masc.Nom.Sg")
|
|
72
|
+
assert mapped_tag == "ADJA.Nom.Sg.Masc"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
To build installable source and wheel distributions:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
uv build
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The artifacts are written to `dist/`.
|
|
82
|
+
|
|
83
|
+
## CLI usage
|
|
84
|
+
|
|
85
|
+
The bundled HiTS tagset can be mapped and checked against the bundled STTS
|
|
86
|
+
tagset with:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
uv run hits-to-stts
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Unknown tags and mapped tags that are absent from the STTS tagset are printed.
|
|
93
|
+
|
|
94
|
+
## License
|
|
95
|
+
|
|
96
|
+
This project is licensed under the MIT License. See `LICENSE` for details.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# HiTS to STTS Tagset Conversion
|
|
2
|
+
|
|
3
|
+
> [!NOTE]
|
|
4
|
+
> This repository is a fork of
|
|
5
|
+
> [tuerkerhan/HiTS-to-STTS-Mapping](https://github.com/tuerkerhan/HiTS-to-STTS-Mapping).
|
|
6
|
+
|
|
7
|
+
Convert HiTS (Historical Tagset) part-of-speech tags to STTS
|
|
8
|
+
(Stuttgart-Tuebingen Tagset) tags. The project requires Python 3.13 or newer
|
|
9
|
+
and uses [uv](https://docs.astral.sh/uv/) for project and package management.
|
|
10
|
+
|
|
11
|
+
## Development
|
|
12
|
+
|
|
13
|
+
Clone the repository and create the locked development environment:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
uv sync
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Run the tests:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
uv run python -m unittest discover -s tests
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Library usage
|
|
26
|
+
|
|
27
|
+
Install the project from a local checkout:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
uv add --editable ../HiTS-to-STTS-Mapping
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
It can then be imported like any other dependency:
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from hits_to_stts import map_tag
|
|
37
|
+
|
|
38
|
+
mapped_tag = map_tag("ADJN.Masc.Nom.Sg")
|
|
39
|
+
assert mapped_tag == "ADJA.Nom.Sg.Masc"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
To build installable source and wheel distributions:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
uv build
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The artifacts are written to `dist/`.
|
|
49
|
+
|
|
50
|
+
## CLI usage
|
|
51
|
+
|
|
52
|
+
The bundled HiTS tagset can be mapped and checked against the bundled STTS
|
|
53
|
+
tagset with:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
uv run hits-to-stts
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Unknown tags and mapped tags that are absent from the STTS tagset are printed.
|
|
60
|
+
|
|
61
|
+
## License
|
|
62
|
+
|
|
63
|
+
This project is licensed under the MIT License. See `LICENSE` for details.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "hits-to-stts"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Convert HiTS part-of-speech tags to STTS tags."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
|
+
classifiers = [
|
|
8
|
+
"License :: OSI Approved :: MIT License",
|
|
9
|
+
"Programming Language :: Python :: 3",
|
|
10
|
+
"Programming Language :: Python :: 3.13",
|
|
11
|
+
]
|
|
12
|
+
dependencies = []
|
|
13
|
+
|
|
14
|
+
[project.license]
|
|
15
|
+
file = "LICENSE"
|
|
16
|
+
|
|
17
|
+
[project.urls]
|
|
18
|
+
Repository = "https://github.com/daanvdn/HiTS-to-STTS-Mapping"
|
|
19
|
+
Upstream = "https://github.com/tuerkerhan/HiTS-to-STTS-Mapping"
|
|
20
|
+
|
|
21
|
+
[project.scripts]
|
|
22
|
+
hits-to-stts = "hits_to_stts.__main__:main"
|
|
23
|
+
|
|
24
|
+
[build-system]
|
|
25
|
+
requires = ["uv_build>=0.9.2,<0.10.0"]
|
|
26
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "hits-to-stts"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Convert HiTS part-of-speech tags to STTS tags."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
|
+
license = { file = "LICENSE" }
|
|
8
|
+
classifiers = [
|
|
9
|
+
"License :: OSI Approved :: MIT License",
|
|
10
|
+
"Programming Language :: Python :: 3",
|
|
11
|
+
"Programming Language :: Python :: 3.13",
|
|
12
|
+
]
|
|
13
|
+
dependencies = []
|
|
14
|
+
|
|
15
|
+
[project.urls]
|
|
16
|
+
Repository = "https://github.com/daanvdn/HiTS-to-STTS-Mapping"
|
|
17
|
+
Upstream = "https://github.com/tuerkerhan/HiTS-to-STTS-Mapping"
|
|
18
|
+
|
|
19
|
+
[project.scripts]
|
|
20
|
+
hits-to-stts = "hits_to_stts.__main__:main"
|
|
21
|
+
|
|
22
|
+
[build-system]
|
|
23
|
+
requires = ["uv_build>=0.9.2,<0.10.0"]
|
|
24
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""Convert HiTS part-of-speech tags to STTS tags."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
__all__ = ["convert", "get_pos", "hits_to_stts", "map_tag", "unknown_pos"]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
hits_to_stts = {
|
|
9
|
+
"ADJN": "ADJA",
|
|
10
|
+
"ADJS": "ADJA",
|
|
11
|
+
"AVD": "ADV",
|
|
12
|
+
"AVNEG": "ADV",
|
|
13
|
+
"AVG": "PWAV",
|
|
14
|
+
"AVW": "PWAV",
|
|
15
|
+
"APPR": "APPR",
|
|
16
|
+
"APPO": "APPO",
|
|
17
|
+
"CARDA": "CARD",
|
|
18
|
+
"CARDD": "CARD",
|
|
19
|
+
"CARDN": "CARD",
|
|
20
|
+
"CARDS": "CARD",
|
|
21
|
+
"DDART": "ART",
|
|
22
|
+
"DDA": "PDAT",
|
|
23
|
+
"DDS": "PDS",
|
|
24
|
+
"DDN": "PDAT",
|
|
25
|
+
"DDD": "PDAT",
|
|
26
|
+
"DIA": "PIAT",
|
|
27
|
+
"DIART": "ART",
|
|
28
|
+
"DID": "PDAT",
|
|
29
|
+
"DIN": "PDAT",
|
|
30
|
+
"DRELS": "PRELS",
|
|
31
|
+
"DPOSA": "PPOSAT",
|
|
32
|
+
"DPOSN": "PPOSAT",
|
|
33
|
+
"DPOSD": "PPOSS",
|
|
34
|
+
"DPOSGEN": "PPOSAT",
|
|
35
|
+
"DPOSS": "NN",
|
|
36
|
+
"DWA": "PWAT",
|
|
37
|
+
"DWN": "PWAT",
|
|
38
|
+
"DWS": "PWS",
|
|
39
|
+
"DWD": "PWS",
|
|
40
|
+
"KO*": "KOUS",
|
|
41
|
+
"NA": "NN",
|
|
42
|
+
"PAVAP": "PAV",
|
|
43
|
+
"PAVD": "PAV",
|
|
44
|
+
"PAVG": "PAV",
|
|
45
|
+
"PAVREL": "PWAV",
|
|
46
|
+
"PAVW": "PWAV",
|
|
47
|
+
"VVPS": "ADJA",
|
|
48
|
+
"VAPS": "ADJA",
|
|
49
|
+
"VMPS": "ADJA",
|
|
50
|
+
"PI": "PIS",
|
|
51
|
+
"VVFIN": "VVFIN",
|
|
52
|
+
"NE": "NE",
|
|
53
|
+
"VVIMP": "VVIMP",
|
|
54
|
+
"PPER": "PPER",
|
|
55
|
+
"VAFIN": "VAFIN",
|
|
56
|
+
"VAIMP": "VAIMP",
|
|
57
|
+
"VMFIN": "VMFIN",
|
|
58
|
+
"ADJA": "ADJA",
|
|
59
|
+
"VMINF": "VMINF",
|
|
60
|
+
"ADJD": "ADJD",
|
|
61
|
+
"PRF": "PRF",
|
|
62
|
+
"CARD": "CARD",
|
|
63
|
+
"PW": "PWS",
|
|
64
|
+
"DIS": "PIS",
|
|
65
|
+
"DGA": "PWAT",
|
|
66
|
+
"DGS": "PWS",
|
|
67
|
+
"PG": "PWS",
|
|
68
|
+
"PGA": "PWAT",
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
unknown_pos: set[str] = set()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def get_pos(tag: str) -> str:
|
|
75
|
+
"""Return the part-of-speech component of a HiTS tag."""
|
|
76
|
+
return tag.split(".", 1)[0]
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def convert(pos: str) -> str:
|
|
80
|
+
"""Convert a HiTS POS value to STTS, preserving unknown values."""
|
|
81
|
+
if pos in hits_to_stts:
|
|
82
|
+
return hits_to_stts[pos]
|
|
83
|
+
if pos in hits_to_stts.values():
|
|
84
|
+
return pos
|
|
85
|
+
|
|
86
|
+
unknown_pos.add(pos)
|
|
87
|
+
return pos
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def map_tag(tag: str) -> str:
|
|
91
|
+
"""Convert a complete HiTS tag to its STTS representation."""
|
|
92
|
+
tag = (
|
|
93
|
+
tag.replace("Masc,Neut", "*")
|
|
94
|
+
.replace("Fem,Masc", "*")
|
|
95
|
+
.replace("Fem,Neut", "*")
|
|
96
|
+
.replace("Masc,Fem", "*")
|
|
97
|
+
.replace("VVINF", "VVINF.Inf")
|
|
98
|
+
.replace("VMINF", "VMINF.Inf")
|
|
99
|
+
.replace("VAPP", "VAPP.Psp")
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
if re.match(r"(AVG)", tag):
|
|
103
|
+
return convert(tag)
|
|
104
|
+
|
|
105
|
+
match = re.match(
|
|
106
|
+
r"(ADJA|ADJD|ADJN|ADJS)\.?(Pos|Comp|Sup|\*)?"
|
|
107
|
+
r"\.?(Masc|Fem|Neut|\*)?\.?(Nom|Gen|Dat|Akk|\*)?"
|
|
108
|
+
r"\.?(Sg|Pl|\*)?\.?(st|wk|\*)?",
|
|
109
|
+
tag,
|
|
110
|
+
)
|
|
111
|
+
if match:
|
|
112
|
+
pos, degree, gender, case, number, _strength = match.groups()
|
|
113
|
+
if case == "Akk":
|
|
114
|
+
case = "Acc"
|
|
115
|
+
return ".".join(
|
|
116
|
+
value
|
|
117
|
+
for value in [convert(pos), degree, case, number, gender]
|
|
118
|
+
if value
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
match = re.match(
|
|
122
|
+
r"(DPOSD|NA|CARDD|CARDA|CARDN|CARDS|DDA|DDART|DDD|DDN|DDS|DGA|DGS|"
|
|
123
|
+
r"DIA|DIART|DID|DIN|DIS|DPOSA|DPOSN|DPOSS|DRELS|DWA|DWD|DWS|NA|NE|"
|
|
124
|
+
r"PG|PI|PW)\.?(Masc|Fem|Neut|\*)?\.?(Nom|Gen|Dat|Akk|\*)?"
|
|
125
|
+
r"\.?(Sg|Pl|\*)?\.?(st|wk|\*)?",
|
|
126
|
+
tag,
|
|
127
|
+
)
|
|
128
|
+
if match:
|
|
129
|
+
pos, gender, case, number, _strength = match.groups()
|
|
130
|
+
if case == "Akk":
|
|
131
|
+
case = "Acc"
|
|
132
|
+
return ".".join(
|
|
133
|
+
value for value in [convert(pos), case, number, gender] if value
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
match = re.match(
|
|
137
|
+
r"(CARD)\.?(Masc|Fem|Neut|\*)?\.?(Nom|Gen|Dat|Akk|\*)?"
|
|
138
|
+
r"\.?(Sg|Pl|\*)?\.?(st|wk|\*)?",
|
|
139
|
+
tag,
|
|
140
|
+
)
|
|
141
|
+
if match:
|
|
142
|
+
pos, _gender, _case, _number, _strength = match.groups()
|
|
143
|
+
return convert(pos)
|
|
144
|
+
|
|
145
|
+
match = re.match(
|
|
146
|
+
r"(PPER|PRF)\.?(Masc|Fem|Neut|\*)?\.?(Nom|Gen|Dat|Akk|\*)?"
|
|
147
|
+
r"\.?(Sg|Pl|\*)?\.?(st|wk|\*)?(1|2|3|\*)?",
|
|
148
|
+
tag,
|
|
149
|
+
)
|
|
150
|
+
if match:
|
|
151
|
+
pos, gender, case, number, _strength, person = match.groups()
|
|
152
|
+
if case == "Akk":
|
|
153
|
+
case = "Acc"
|
|
154
|
+
return ".".join(
|
|
155
|
+
value
|
|
156
|
+
for value in [convert(pos), person, case, number, gender]
|
|
157
|
+
if value
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
match = re.match(
|
|
161
|
+
r"(VAFIN|VAIMP|VMFIN|VVFIN|VVIMP)\.?(Ind|Subj|\*)?"
|
|
162
|
+
r"\.?(Past|Pres\*)?\.?(Sg|Pl|\*)?\.?(1|2|3|\*)?",
|
|
163
|
+
tag,
|
|
164
|
+
)
|
|
165
|
+
if match:
|
|
166
|
+
pos, mood, tense, number, person = match.groups()
|
|
167
|
+
return ".".join(
|
|
168
|
+
value
|
|
169
|
+
for value in [convert(pos), person, number, tense, mood]
|
|
170
|
+
if value
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
return tag
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Command-line validation of the bundled HiTS mapping."""
|
|
2
|
+
|
|
3
|
+
from importlib.resources import files
|
|
4
|
+
|
|
5
|
+
from . import map_tag, unknown_pos
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _read_tagset(filename: str) -> list[str]:
|
|
9
|
+
resource = files("hits_to_stts").joinpath("data", filename)
|
|
10
|
+
return resource.read_text(encoding="utf-8").splitlines()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def main() -> None:
|
|
14
|
+
"""Map the bundled HiTS tagset and compare it with the STTS tagset."""
|
|
15
|
+
hits = _read_tagset("HiTS-tagset.txt")
|
|
16
|
+
stts = set(_read_tagset("Tiger_v8_tagset.txt"))
|
|
17
|
+
invalid_tags = set(map(map_tag, hits)) - stts
|
|
18
|
+
|
|
19
|
+
print(invalid_tags)
|
|
20
|
+
print("fehler: ", len(invalid_tags))
|
|
21
|
+
print("Unknown POS list:", unknown_pos)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
if __name__ == "__main__":
|
|
25
|
+
main()
|