Skip to content

Commit f1e5113

Browse files
committed
Reproducible solutions
1 parent c544b33 commit f1e5113

31 files changed

Lines changed: 323 additions & 272 deletions

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
__pycache__
22
.venv
33
build
4-
submission.tar.gz
4+
submission.zip

build.py

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1-
from pathlib import Path
2-
import re
31
import sys
4-
import compress
2+
from pathlib import Path
53

4+
import compress
65

76
input_dir = Path("solutions")
87
output_dir = Path("build")
98

109

11-
def main():
10+
def main() -> None:
1211
if len(sys.argv) > 1:
1312
task_num = int(sys.argv[1])
14-
tasks = [task_num]
13+
tasks: list[int] = [task_num]
1514
else:
1615
tasks = range(1, 401)
1716

@@ -20,22 +19,38 @@ def main():
2019

2120
for i in tasks:
2221
filename = f"task{i:03d}.py"
23-
with open(input_dir / filename, "r", encoding="latin-1") as f:
22+
with (input_dir / filename).open(encoding="latin-1") as f:
2423
content = f.read()
2524

26-
match = re.search(r"# compression: auto", content)
25+
source_content = content.split("\n#")[0]
26+
source_bytes = source_content.encode("latin-1")
2727

28-
content = content.split("\n#")[0]
28+
directives: dict[str, str] = {}
29+
for line in content.split("\n"):
30+
if line.startswith("# ") and ":" in line:
31+
key, _, value = line[2:].partition(":")
32+
directives[key.strip()] = value.strip()
2933

30-
if match:
31-
source_bytes = content.encode("latin-1")
32-
compressed, _ = compress.compress(source_bytes)
34+
compression = directives.get("compression")
35+
36+
if compression == "frozen":
37+
huffman_hex = directives.get("huffman", "")
38+
if not huffman_hex:
39+
print(f"Warning: {filename} has frozen compression but no huffman")
40+
output = source_bytes
41+
else:
42+
output = compress.compress_frozen(source_bytes, huffman_hex)
43+
with (output_dir / filename).open("wb") as f:
44+
f.write(output)
3345

34-
with open(output_dir / filename, "wb") as f:
46+
elif compression == "auto":
47+
compressed, _ = compress.compress(source_bytes)
48+
with (output_dir / filename).open("wb") as f:
3549
f.write(compressed)
50+
3651
else:
37-
with open(output_dir / filename, "w", encoding="latin-1") as f:
38-
f.write(content)
52+
with (output_dir / filename).open("w", encoding="latin-1") as f:
53+
f.write(source_content)
3954

4055

4156
if __name__ == "__main__":

common.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
from __future__ import annotations
2-
3-
from dataclasses import dataclass
41
import importlib.util
52
import json
3+
import warnings
4+
from dataclasses import dataclass
65
from pathlib import Path
76
from types import ModuleType
8-
import warnings
97

108

119
@dataclass(frozen=True, slots=True)
@@ -14,7 +12,7 @@ class TestCase:
1412
output: list[list[int]]
1513

1614
@staticmethod
17-
def from_dict(data: dict) -> TestCase:
15+
def from_dict(data: dict) -> "TestCase":
1816
return TestCase(data["input"], data["output"])
1917

2018

@@ -38,17 +36,17 @@ def all_testcases(self) -> list[TestCase]:
3836
return self.train + self.test + self.arc_gen
3937

4038
@staticmethod
41-
def from_dict(data: dict) -> Task:
39+
def from_dict(data: dict) -> "Task":
4240
train = [TestCase.from_dict(tc) for tc in data["train"]]
4341
test = [TestCase.from_dict(tc) for tc in data["test"]]
4442
arc_gen = [TestCase.from_dict(tc) for tc in data["arc-gen"]]
4543
return Task(train, test, arc_gen)
4644

4745
@staticmethod
48-
def load(task_num: int, tasks_dir: Path = Path("tasks")) -> Task:
46+
def load(task_num: int, tasks_dir: Path = Path("tasks")) -> "Task":
4947
task_filename = f"task{task_num:03d}.json"
5048
task_path = tasks_dir / task_filename
51-
with open(task_path) as f:
49+
with task_path.open() as f:
5250
task_data = json.load(f)
5351
return Task.from_dict(task_data)
5452

compress.py

Lines changed: 92 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,35 @@
11
import zlib
2-
3-
from reencode import reencode
2+
from dataclasses import dataclass
43

54
import deflate
65
import zopfli.zlib
76

7+
from reencode import Huffman, lz77, reencode
8+
9+
10+
@dataclass(frozen=True)
11+
class CompressionInfo:
12+
method: str
13+
window: int
14+
delimiter: str
15+
reencode: bool
816

9-
ZOPFLI_ITERS = [15, 128]
10-
LIBDEFLATE_LEVELS = [11, 12]
11-
ZLIB_LEVELS = [9]
12-
DELIMS = [b"'", b'"']
13-
WINDOWS = [-9, -10]
1417

18+
ZOPFLI_ITERS: list[int] = [15, 128]
19+
LIBDEFLATE_LEVELS: list[int] = [11, 12]
20+
ZLIB_LEVELS: list[int] = [9]
21+
DELIMS: list[bytes] = [b"'", b'"']
22+
WINDOWS: list[int] = [-9, -10]
1523

16-
def sanitize(b_in: bytes, delim: bytes, use_reencode: bool = True) -> bytes:
17-
if use_reencode:
18-
b_in = reencode(b_in, delim)
24+
25+
def _hoist_import(src: bytes) -> tuple[bytes, bytes]:
26+
if src.startswith(b"import"):
27+
module = src.split()[1]
28+
return src[len(module) + 8 :], b"," + module
29+
return src, b""
30+
31+
32+
def _sanitize(b_in: bytes, delim: bytes) -> bytes:
1933
b_out = bytearray()
2034
for b, b_next in zip(b_in, [*b_in[1:], 0]):
2135
if b == 0:
@@ -33,74 +47,103 @@ def sanitize(b_in: bytes, delim: bytes, use_reencode: bool = True) -> bytes:
3347
return bytes(b_out)
3448

3549

36-
def compress(src: bytes) -> tuple[bytes, dict]:
37-
candidates: list[tuple[bytes, dict]] = []
38-
39-
# import hoisting: "import zlib,re"
40-
hoisted_import = b""
41-
if src.startswith(b"import"):
42-
module = src.split()[1]
43-
hoisted_import = b"," + module
44-
src = src[len(module) + 8 :]
45-
50+
def _wrap(deflate_data: bytes, delim: bytes, hoisted: bytes, window: int) -> bytes:
51+
sanitized = _sanitize(reencode(deflate_data, delim), delim)
52+
window_str = b",~9" if window == -10 else (b",%d" % window if window != 15 else b"")
53+
return (
54+
b"#coding:L1\nimport zlib"
55+
+ hoisted
56+
+ b"\nexec(zlib.decompress(bytes("
57+
+ delim
58+
+ sanitized
59+
+ delim
60+
+ b',"L1")'
61+
+ window_str
62+
+ b"))"
63+
)
64+
65+
66+
def compress(src: bytes) -> tuple[bytes, CompressionInfo]:
67+
src, hoisted = _hoist_import(src)
4668
compressed_data: list[tuple[bytes, str, int]] = []
4769

4870
for iters in ZOPFLI_ITERS:
49-
full_result = zopfli.zlib.compress(
50-
src, numiterations=iters, blocksplitting=False
71+
full: bytes = zopfli.zlib.compress(
72+
src,
73+
numiterations=iters,
74+
blocksplitting=False,
5175
)
52-
result = full_result[2:-4]
53-
actual_window = -(((full_result[0] >> 4) & 0x0F) + 8)
54-
76+
result = full[2:-4]
77+
actual_window = -(((full[0] >> 4) & 0x0F) + 8)
5578
compressed_data.append((result, f"zopfli(iters={iters})", -10))
5679
if actual_window != -10:
57-
output_window = -9 if actual_window < 15 else actual_window
58-
compressed_data.append((result, f"zopfli(iters={iters})", output_window))
59-
60-
for level in LIBDEFLATE_LEVELS:
61-
result = bytes(deflate.deflate_compress(src, compresslevel=level))
62-
compressed_data.append((result, f"libdeflate(level={level})", -10))
80+
compressed_data.append(
81+
(
82+
result,
83+
f"zopfli(iters={iters})",
84+
-9 if actual_window < 15 else actual_window,
85+
),
86+
)
87+
88+
compressed_data.extend(
89+
(
90+
bytes(deflate.deflate_compress(src, compresslevel=level)),
91+
f"libdeflate(level={level})",
92+
-10,
93+
)
94+
for level in LIBDEFLATE_LEVELS
95+
)
6396

6497
for level in ZLIB_LEVELS:
6598
for window in WINDOWS:
66-
if window == -10:
67-
result = zlib.compress(src, level=level, wbits=-15)[:]
68-
else:
69-
result = zlib.compress(src, level=level, wbits=window)[:]
99+
result = zlib.compress(
100+
src,
101+
level=level,
102+
wbits=-15 if window == -10 else window,
103+
)
70104
compressed_data.append((result, f"zlib(level={level})", window))
71105

106+
candidates: list[tuple[bytes, CompressionInfo]] = []
72107
for data, method, window in compressed_data:
73108
for delim in DELIMS:
74109
for use_reencode in [True, False]:
75-
sanitized = sanitize(data, delim, use_reencode=use_reencode)
76-
literal = delim + sanitized + delim
77-
110+
sanitized = _sanitize(
111+
reencode(data, delim) if use_reencode else data,
112+
delim,
113+
)
78114
window_str = (
79115
b",~9"
80116
if window == -10
81117
else (b",%d" % window if window != 15 else b"")
82118
)
83-
84119
code = (
85120
b"#coding:L1\nimport zlib"
86-
+ hoisted_import
121+
+ hoisted
87122
+ b"\nexec(zlib.decompress(bytes("
88-
+ literal
123+
+ delim
124+
+ sanitized
125+
+ delim
89126
+ b',"L1")'
90127
+ window_str
91128
+ b"))"
92129
)
93-
94130
candidates.append(
95131
(
96132
code,
97-
{
98-
"method": method,
99-
"window": window,
100-
"delimiter": delim.decode("latin-1"),
101-
"reencode": use_reencode,
102-
},
103-
)
133+
CompressionInfo(
134+
method=method,
135+
window=window,
136+
delimiter=delim.decode(),
137+
reencode=use_reencode,
138+
),
139+
),
104140
)
105141

106142
return min(candidates, key=lambda x: len(x[0]))
143+
144+
145+
def compress_frozen(src: bytes, huffman_hex: str) -> bytes:
146+
src, hoisted = _hoist_import(src)
147+
huffman = Huffman.parse(bytes.fromhex(huffman_hex))
148+
candidates = [_wrap(lz77(src, huffman, d), d, hoisted, -10) for d in DELIMS]
149+
return min(candidates, key=len)

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,6 @@ dev = ["ruff>=0.14.3", "ty>=0.0.1a25"]
1818

1919
[tool.ruff]
2020
exclude = ["solutions", "build"]
21+
22+
[tool.ty.src]
23+
exclude = ["solutions/", "build/"]

0 commit comments

Comments
 (0)