cppkh-interface 0.1.0__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.
- cppkh_interface/__init__.py +19 -0
- cppkh_interface/__main__.py +4 -0
- cppkh_interface/data/src/main.cpp +3506 -0
- cppkh_interface/main.py +352 -0
- cppkh_interface/py.typed +1 -0
- cppkh_interface-0.1.0.dist-info/METADATA +76 -0
- cppkh_interface-0.1.0.dist-info/RECORD +9 -0
- cppkh_interface-0.1.0.dist-info/WHEEL +4 -0
- cppkh_interface-0.1.0.dist-info/entry_points.txt +3 -0
cppkh_interface/main.py
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import ast
|
|
5
|
+
import hashlib
|
|
6
|
+
import os
|
|
7
|
+
import pathlib
|
|
8
|
+
import platform
|
|
9
|
+
import re
|
|
10
|
+
import shlex
|
|
11
|
+
import subprocess
|
|
12
|
+
import sys
|
|
13
|
+
import tempfile
|
|
14
|
+
from importlib import resources
|
|
15
|
+
from typing import Optional, Sequence, Union
|
|
16
|
+
|
|
17
|
+
import cpp_simple_interface
|
|
18
|
+
import pd_code_de_r1
|
|
19
|
+
import pd_code_delete_nugatory
|
|
20
|
+
import pd_code_sanity
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
PathLike = Union[str, os.PathLike]
|
|
24
|
+
PdInput = Union[str, Sequence[Sequence[int]]]
|
|
25
|
+
UNKNOT_RESULT = "q^-1*t^0*Z[0] + q^1*t^0*Z[0]"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class CppkhInterfaceError(RuntimeError):
|
|
29
|
+
"""Raised when the C++ executable cannot be built or run."""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _format_pd(crossings: Sequence[Sequence[int]]) -> str:
|
|
33
|
+
parts = []
|
|
34
|
+
for crossing in crossings:
|
|
35
|
+
values = list(crossing)
|
|
36
|
+
if len(values) != 4:
|
|
37
|
+
raise ValueError(f"PD crossing must have four entries: {crossing!r}")
|
|
38
|
+
parts.append("X[{},{},{},{}]".format(*(int(value) for value in values)))
|
|
39
|
+
return "PD[" + ",".join(parts) + "]"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _parse_x_crossings(text: str) -> Optional[list[list[int]]]:
|
|
43
|
+
pattern = r"X\s*\[\s*(-?\d+)\s*,\s*(-?\d+)\s*,\s*(-?\d+)\s*,\s*(-?\d+)\s*\]"
|
|
44
|
+
crossings = []
|
|
45
|
+
for match in re.finditer(pattern, text):
|
|
46
|
+
crossings.append([int(match.group(i)) for i in range(1, 5)])
|
|
47
|
+
return crossings if crossings else None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _as_crossings(pd_code: PdInput) -> list[list[int]]:
|
|
51
|
+
if isinstance(pd_code, str):
|
|
52
|
+
body = pd_code.strip()
|
|
53
|
+
if ":" in body:
|
|
54
|
+
body = body.split(":", 1)[1].strip()
|
|
55
|
+
if body.replace(" ", "") in ("PD[]", "[]"):
|
|
56
|
+
return []
|
|
57
|
+
|
|
58
|
+
parsed = _parse_x_crossings(body)
|
|
59
|
+
if parsed is not None:
|
|
60
|
+
return parsed
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
value = ast.literal_eval(body)
|
|
64
|
+
except (SyntaxError, ValueError) as exc:
|
|
65
|
+
raise ValueError(f"unsupported PD-code string format: {pd_code!r}") from exc
|
|
66
|
+
else:
|
|
67
|
+
value = pd_code
|
|
68
|
+
|
|
69
|
+
crossings = []
|
|
70
|
+
for crossing in value:
|
|
71
|
+
values = list(crossing)
|
|
72
|
+
if len(values) != 4:
|
|
73
|
+
raise ValueError(f"PD crossing must have four entries: {crossing!r}")
|
|
74
|
+
crossings.append([int(item) for item in values])
|
|
75
|
+
return crossings
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _check_sanity(crossings: list[list[int]]) -> None:
|
|
79
|
+
if crossings == []:
|
|
80
|
+
return
|
|
81
|
+
if not pd_code_sanity.sanity(crossings):
|
|
82
|
+
raise TypeError("pd_code does not satisfy PD-code sanity checks")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def normalize_pd_code(pd_code: PdInput) -> str:
|
|
86
|
+
"""Normalize a supported PD-code value into standard ``PD[X[...],...]`` text."""
|
|
87
|
+
|
|
88
|
+
return _format_pd(_as_crossings(pd_code))
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _resource_source_path():
|
|
92
|
+
source = resources.files("cppkh_interface") / "data" / "src" / "main.cpp"
|
|
93
|
+
return resources.as_file(source)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _cache_dir() -> pathlib.Path:
|
|
97
|
+
env_value = os.environ.get("CPPKH_INTERFACE_CACHE_DIR")
|
|
98
|
+
if env_value:
|
|
99
|
+
root = pathlib.Path(env_value)
|
|
100
|
+
elif sys.platform == "win32":
|
|
101
|
+
root = pathlib.Path(os.environ.get("LOCALAPPDATA", pathlib.Path.home())) / "cppkh-interface"
|
|
102
|
+
elif sys.platform == "darwin":
|
|
103
|
+
root = pathlib.Path.home() / "Library" / "Caches" / "cppkh-interface"
|
|
104
|
+
else:
|
|
105
|
+
root = pathlib.Path(os.environ.get("XDG_CACHE_HOME", pathlib.Path.home() / ".cache")) / "cppkh-interface"
|
|
106
|
+
root.mkdir(parents=True, exist_ok=True)
|
|
107
|
+
return root
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _default_compile_flags() -> list[str]:
|
|
111
|
+
flags = ["-std=c++14", "-O3", "-DNDEBUG"]
|
|
112
|
+
native = os.environ.get("CPPKH_INTERFACE_NATIVE", "1").strip().lower()
|
|
113
|
+
if native not in ("0", "false", "no", "off"):
|
|
114
|
+
flags.append("-march=native")
|
|
115
|
+
if platform.system() != "Windows":
|
|
116
|
+
flags.append("-pthread")
|
|
117
|
+
extra = os.environ.get("CPPKH_INTERFACE_CXXFLAGS", "").strip()
|
|
118
|
+
if extra:
|
|
119
|
+
flags.extend(shlex.split(extra))
|
|
120
|
+
return flags
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _exe_suffix() -> str:
|
|
124
|
+
return ".exe" if platform.system() == "Windows" else ""
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _compiler_runtime_path_entries() -> list[str]:
|
|
128
|
+
compiler = cpp_simple_interface.get_gpp_filepath().strip()
|
|
129
|
+
if not compiler:
|
|
130
|
+
return []
|
|
131
|
+
|
|
132
|
+
candidates = []
|
|
133
|
+
unquoted = compiler
|
|
134
|
+
if len(unquoted) >= 2 and unquoted[0] == unquoted[-1] and unquoted[0] in ("'", '"'):
|
|
135
|
+
unquoted = unquoted[1:-1]
|
|
136
|
+
candidates.append(unquoted)
|
|
137
|
+
|
|
138
|
+
try:
|
|
139
|
+
candidates.extend(shlex.split(compiler, posix=True))
|
|
140
|
+
except ValueError:
|
|
141
|
+
pass
|
|
142
|
+
|
|
143
|
+
paths = []
|
|
144
|
+
for candidate in candidates:
|
|
145
|
+
path = pathlib.Path(candidate)
|
|
146
|
+
if path.exists() and path.is_file():
|
|
147
|
+
parent = str(path.resolve().parent)
|
|
148
|
+
if parent not in paths:
|
|
149
|
+
paths.append(parent)
|
|
150
|
+
return paths
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _cache_key(source_bytes: bytes, flags: Sequence[str]) -> str:
|
|
154
|
+
digest = hashlib.sha256()
|
|
155
|
+
digest.update(source_bytes)
|
|
156
|
+
digest.update("\0".join(flags).encode("utf-8"))
|
|
157
|
+
digest.update(cpp_simple_interface.get_gpp_filepath().encode("utf-8"))
|
|
158
|
+
digest.update(platform.platform().encode("utf-8"))
|
|
159
|
+
return digest.hexdigest()[:20]
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def compile_cppkh(
|
|
163
|
+
*,
|
|
164
|
+
force: bool = False,
|
|
165
|
+
cxx: Optional[str] = None,
|
|
166
|
+
extra_flags: Optional[Sequence[str]] = None,
|
|
167
|
+
) -> pathlib.Path:
|
|
168
|
+
"""Compile the packaged C++ source with cpp-simple-interface and return the executable path."""
|
|
169
|
+
|
|
170
|
+
if cxx:
|
|
171
|
+
cpp_simple_interface.set_gpp_filepath(cxx)
|
|
172
|
+
|
|
173
|
+
with _resource_source_path() as source:
|
|
174
|
+
source_path = pathlib.Path(source)
|
|
175
|
+
source_bytes = source_path.read_bytes()
|
|
176
|
+
flags = _default_compile_flags()
|
|
177
|
+
if extra_flags:
|
|
178
|
+
flags.extend(str(flag) for flag in extra_flags)
|
|
179
|
+
|
|
180
|
+
cache = _cache_dir()
|
|
181
|
+
exe = cache / f"cppkh-{_cache_key(source_bytes, flags)}{_exe_suffix()}"
|
|
182
|
+
if exe.exists() and not force:
|
|
183
|
+
return exe
|
|
184
|
+
|
|
185
|
+
tmp_exe = cache / f"{exe.name}.tmp-{os.getpid()}{_exe_suffix()}"
|
|
186
|
+
if tmp_exe.exists():
|
|
187
|
+
tmp_exe.unlink()
|
|
188
|
+
|
|
189
|
+
success, message = cpp_simple_interface.compile_cpp_files(
|
|
190
|
+
[str(source_path)],
|
|
191
|
+
str(tmp_exe),
|
|
192
|
+
other_flags=flags,
|
|
193
|
+
)
|
|
194
|
+
if not success and "-march=native" in flags:
|
|
195
|
+
fallback_flags = [flag for flag in flags if flag != "-march=native"]
|
|
196
|
+
success, message = cpp_simple_interface.compile_cpp_files(
|
|
197
|
+
[str(source_path)],
|
|
198
|
+
str(tmp_exe),
|
|
199
|
+
other_flags=fallback_flags,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
if not success:
|
|
203
|
+
raise CppkhInterfaceError(message)
|
|
204
|
+
if not tmp_exe.exists():
|
|
205
|
+
raise CppkhInterfaceError(f"compiled executable was not created: {tmp_exe}")
|
|
206
|
+
os.replace(tmp_exe, exe)
|
|
207
|
+
try:
|
|
208
|
+
exe.chmod(exe.stat().st_mode | 0o755)
|
|
209
|
+
except OSError:
|
|
210
|
+
pass
|
|
211
|
+
return exe
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def get_cppkh_executable() -> pathlib.Path:
|
|
215
|
+
"""Return the cached executable path, compiling it first when necessary."""
|
|
216
|
+
|
|
217
|
+
return compile_cppkh()
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _run_cppkh(
|
|
221
|
+
pd_text: str,
|
|
222
|
+
*,
|
|
223
|
+
encoding: Optional[str] = None,
|
|
224
|
+
threads: Union[str, int] = "1",
|
|
225
|
+
print_simplified_pd: bool = False,
|
|
226
|
+
) -> str:
|
|
227
|
+
exe = compile_cppkh()
|
|
228
|
+
with tempfile.NamedTemporaryFile("w", suffix=".pd", encoding="utf-8", delete=False) as handle:
|
|
229
|
+
handle.write(pd_text)
|
|
230
|
+
handle.write("\n")
|
|
231
|
+
pd_file = handle.name
|
|
232
|
+
|
|
233
|
+
command = [
|
|
234
|
+
str(exe),
|
|
235
|
+
"--pd-file",
|
|
236
|
+
pd_file,
|
|
237
|
+
"--quiet",
|
|
238
|
+
"--threads",
|
|
239
|
+
str(threads),
|
|
240
|
+
"--no-simplify-pd",
|
|
241
|
+
]
|
|
242
|
+
if print_simplified_pd:
|
|
243
|
+
command.append("--print-simplified-pd")
|
|
244
|
+
|
|
245
|
+
kwargs = {
|
|
246
|
+
"stdout": subprocess.PIPE,
|
|
247
|
+
"stderr": subprocess.PIPE,
|
|
248
|
+
"text": True,
|
|
249
|
+
"encoding": encoding or "utf-8",
|
|
250
|
+
"errors": "replace",
|
|
251
|
+
}
|
|
252
|
+
env = os.environ.copy()
|
|
253
|
+
runtime_paths = _compiler_runtime_path_entries()
|
|
254
|
+
if runtime_paths:
|
|
255
|
+
env["PATH"] = os.pathsep.join(runtime_paths + [env.get("PATH", "")])
|
|
256
|
+
kwargs["env"] = env
|
|
257
|
+
if platform.system() == "Windows":
|
|
258
|
+
kwargs["creationflags"] = subprocess.CREATE_NO_WINDOW
|
|
259
|
+
|
|
260
|
+
try:
|
|
261
|
+
result = subprocess.run(command, **kwargs)
|
|
262
|
+
finally:
|
|
263
|
+
try:
|
|
264
|
+
os.unlink(pd_file)
|
|
265
|
+
except OSError:
|
|
266
|
+
pass
|
|
267
|
+
|
|
268
|
+
if result.returncode != 0:
|
|
269
|
+
detail = (result.stderr or result.stdout or "").strip()
|
|
270
|
+
raise CppkhInterfaceError(detail or f"cppkh exited with code {result.returncode}")
|
|
271
|
+
|
|
272
|
+
if print_simplified_pd:
|
|
273
|
+
return result.stdout.strip()
|
|
274
|
+
|
|
275
|
+
matches = re.findall(r'"([^"]*)"', result.stdout)
|
|
276
|
+
if not matches:
|
|
277
|
+
raise CppkhInterfaceError(f"result not found in cppkh output: {result.stdout!r}")
|
|
278
|
+
return matches[-1]
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def _prepare_crossings(pd_code: PdInput, de_r1: bool, de_k8: bool) -> list[list[int]]:
|
|
282
|
+
crossings = _as_crossings(pd_code)
|
|
283
|
+
_check_sanity(crossings)
|
|
284
|
+
if de_r1:
|
|
285
|
+
crossings = pd_code_de_r1.de_r1(crossings)
|
|
286
|
+
if de_k8:
|
|
287
|
+
crossings = pd_code_delete_nugatory.erase_all_nugatory(crossings)
|
|
288
|
+
return crossings
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def solve_khovanov(
|
|
292
|
+
pd_code: PdInput,
|
|
293
|
+
encoding: Optional[str] = None,
|
|
294
|
+
de_r1: bool = True,
|
|
295
|
+
de_k8: bool = True,
|
|
296
|
+
show_real_pdcode: bool = False,
|
|
297
|
+
) -> str:
|
|
298
|
+
"""Compute Khovanov homology with a javakh-interface compatible signature."""
|
|
299
|
+
|
|
300
|
+
crossings = _prepare_crossings(pd_code, de_r1=de_r1, de_k8=de_k8)
|
|
301
|
+
if show_real_pdcode:
|
|
302
|
+
print(f"Real PD code after de_r1 and de_k8: {crossings}")
|
|
303
|
+
if crossings == []:
|
|
304
|
+
return UNKNOT_RESULT
|
|
305
|
+
return _run_cppkh(_format_pd(crossings), encoding=encoding)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def compute_pd(
|
|
309
|
+
pd_code: PdInput,
|
|
310
|
+
*,
|
|
311
|
+
encoding: Optional[str] = None,
|
|
312
|
+
de_r1: bool = True,
|
|
313
|
+
de_k8: bool = True,
|
|
314
|
+
show_real_pdcode: bool = False,
|
|
315
|
+
threads: Union[str, int] = "1",
|
|
316
|
+
) -> str:
|
|
317
|
+
"""Compute Khovanov homology using the same defaults as solve_khovanov."""
|
|
318
|
+
|
|
319
|
+
crossings = _prepare_crossings(pd_code, de_r1=de_r1, de_k8=de_k8)
|
|
320
|
+
if show_real_pdcode:
|
|
321
|
+
print(f"Real PD code after de_r1 and de_k8: {crossings}")
|
|
322
|
+
if crossings == []:
|
|
323
|
+
return UNKNOT_RESULT
|
|
324
|
+
return _run_cppkh(_format_pd(crossings), encoding=encoding, threads=threads)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def simplify_pd(pd_code: PdInput, *, de_r1: bool = True, de_k8: bool = True) -> str:
|
|
328
|
+
"""Return the normalized PD string after optional R1 and nugatory simplification."""
|
|
329
|
+
|
|
330
|
+
return _format_pd(_prepare_crossings(pd_code, de_r1=de_r1, de_k8=de_k8))
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
334
|
+
parser = argparse.ArgumentParser(description="Compute Khovanov homology with cppkh-interface.")
|
|
335
|
+
parser.add_argument("pd_code", help="PD code as PD[...] text or a Python-style list of crossings.")
|
|
336
|
+
parser.add_argument("--no-de-r1", action="store_true", help="Disable R1-move removal.")
|
|
337
|
+
parser.add_argument("--no-de-k8", action="store_true", help="Disable nugatory-crossing removal.")
|
|
338
|
+
parser.add_argument("--threads", default="1", help="cppkh --threads value for direct compute_pd mode.")
|
|
339
|
+
args = parser.parse_args(argv)
|
|
340
|
+
print(
|
|
341
|
+
compute_pd(
|
|
342
|
+
args.pd_code,
|
|
343
|
+
de_r1=not args.no_de_r1,
|
|
344
|
+
de_k8=not args.no_de_k8,
|
|
345
|
+
threads=args.threads,
|
|
346
|
+
)
|
|
347
|
+
)
|
|
348
|
+
return 0
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
if __name__ == "__main__":
|
|
352
|
+
raise SystemExit(main())
|
cppkh_interface/py.typed
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cppkh-interface
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python interface for cppkh Khovanov homology computation with runtime C++ compilation.
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Author: GGN_2015
|
|
7
|
+
Author-email: neko@jlulug.org
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
|
+
Requires-Dist: cpp-simple-interface (>=0.1.2)
|
|
16
|
+
Requires-Dist: pd-code-de-r1
|
|
17
|
+
Requires-Dist: pd-code-delete-nugatory
|
|
18
|
+
Requires-Dist: pd-code-sanity (>=0.0.1)
|
|
19
|
+
Project-URL: Documentation, https://github.com/GGN-2015/cppkh/blob/main/docs/PYTHON_PACKAGE.md
|
|
20
|
+
Project-URL: Homepage, https://github.com/GGN-2015/cppkh
|
|
21
|
+
Project-URL: Repository, https://github.com/GGN-2015/cppkh
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# cppkh-interface
|
|
25
|
+
|
|
26
|
+
`cppkh-interface` is a Python package for computing integer Khovanov homology
|
|
27
|
+
with the C++ `cppkh` implementation.
|
|
28
|
+
|
|
29
|
+
The package is compatible with the main `javakh-interface` function:
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
import cppkh_interface
|
|
33
|
+
|
|
34
|
+
pd_code = [[1, 5, 2, 4], [3, 1, 4, 6], [5, 3, 6, 2]]
|
|
35
|
+
print(cppkh_interface.solve_khovanov(pd_code, de_r1=True, de_k8=True))
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Unlike wrappers that ship a prebuilt DLL or shared object, this package ships
|
|
39
|
+
the `cppkh` C++ source file and compiles a local executable on first use through
|
|
40
|
+
`cpp-simple-interface`. The compiled executable is cached for later calls.
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
pip install cppkh-interface
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
A `g++` compatible compiler must be available at runtime. To select a compiler,
|
|
49
|
+
set `CXX` before importing or calling the package:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
CXX=clang++ python your_script.py
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Windows PowerShell:
|
|
56
|
+
|
|
57
|
+
```powershell
|
|
58
|
+
$env:CXX = "C:\path\to\g++.exe"
|
|
59
|
+
python your_script.py
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Build And Publish
|
|
63
|
+
|
|
64
|
+
From this directory:
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
poetry build
|
|
68
|
+
poetry publish
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
For local testing:
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
poetry run python -m cppkh_interface "[[1,5,2,4], [3,1,4,6], [5,3,6,2]]"
|
|
75
|
+
```
|
|
76
|
+
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
cppkh_interface/__init__.py,sha256=WJNYCnxkp3e-ebXMgoo20S5N3eJxJGlkqbiW844M0VQ,343
|
|
2
|
+
cppkh_interface/__main__.py,sha256=fdA1QWBe5LNX0fbtaJXa1-dwEhrg_4sS_5UnjaajcMo,80
|
|
3
|
+
cppkh_interface/data/src/main.cpp,sha256=T56Ftt152CN5a23juzwSAOXnCnLhdeL-0Wgj7Cbo3dw,141886
|
|
4
|
+
cppkh_interface/main.py,sha256=WOwZd9owRN5K8wall_PW7JyRP0FYITBfOiHUrBwvsxY,11077
|
|
5
|
+
cppkh_interface/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
6
|
+
cppkh_interface-0.1.0.dist-info/entry_points.txt,sha256=CsnHBNaO_RJe4eFXmEZpSxWu-c1aF0RpjVFaEGlIm4Y,61
|
|
7
|
+
cppkh_interface-0.1.0.dist-info/METADATA,sha256=bmeNcT8rRTjyDrODAeurHKEMq6tZR-bqcEn6WBQTcAQ,2071
|
|
8
|
+
cppkh_interface-0.1.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
9
|
+
cppkh_interface-0.1.0.dist-info/RECORD,,
|