rbx.cp 0.13.7__py3-none-any.whl → 0.14.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.
- rbx/box/cli.py +74 -70
- rbx/box/code.py +3 -0
- rbx/box/contest/build_contest_statements.py +65 -23
- rbx/box/contest/contest_package.py +8 -1
- rbx/box/contest/main.py +9 -3
- rbx/box/contest/schema.py +17 -13
- rbx/box/contest/statements.py +12 -8
- rbx/box/dump_schemas.py +2 -1
- rbx/box/environment.py +1 -1
- rbx/box/fields.py +22 -4
- rbx/box/generators.py +32 -13
- rbx/box/limits_info.py +161 -0
- rbx/box/package.py +18 -1
- rbx/box/packaging/boca/boca_language_utils.py +26 -0
- rbx/box/packaging/boca/boca_outcome_utils.py +10 -0
- rbx/box/packaging/boca/packager.py +7 -5
- rbx/box/packaging/contest_main.py +20 -12
- rbx/box/packaging/packager.py +24 -14
- rbx/box/packaging/polygon/packager.py +7 -3
- rbx/box/packaging/polygon/upload.py +9 -2
- rbx/box/presets/__init__.py +64 -64
- rbx/box/remote.py +3 -3
- rbx/box/sanitizers/issue_stack.py +124 -0
- rbx/box/schema.py +87 -27
- rbx/box/solutions.py +74 -117
- rbx/box/statements/build_statements.py +12 -1
- rbx/box/statements/builders.py +5 -3
- rbx/box/statements/latex_jinja.py +73 -23
- rbx/box/statements/schema.py +7 -9
- rbx/box/stressing/generator_parser.py +3 -1
- rbx/box/tasks.py +10 -10
- rbx/box/testcase_extractors.py +8 -0
- rbx/box/testcase_utils.py +7 -7
- rbx/box/testing/testing_preset.py +129 -2
- rbx/box/testing/testing_shared.py +3 -1
- rbx/box/timing.py +305 -0
- rbx/box/tooling/boca/debug_utils.py +88 -0
- rbx/box/tooling/boca/manual_scrape.py +20 -0
- rbx/box/tooling/boca/scraper.py +660 -57
- rbx/box/unit.py +0 -2
- rbx/box/validators.py +0 -4
- rbx/grading/judge/cacher.py +36 -0
- rbx/grading/judge/program.py +12 -2
- rbx/grading/judge/sandbox.py +1 -1
- rbx/grading/judge/sandboxes/stupid_sandbox.py +2 -1
- rbx/grading/judge/storage.py +36 -3
- rbx/grading/limits.py +4 -0
- rbx/grading/steps.py +3 -2
- rbx/resources/presets/default/contest/contest.rbx.yml +11 -1
- rbx/resources/presets/default/contest/statement/info.rbx.tex +54 -0
- rbx/resources/presets/default/problem/.gitignore +1 -0
- rbx/resources/presets/default/problem/problem.rbx.yml +21 -3
- rbx/resources/presets/default/problem/rbx.h +52 -5
- rbx/resources/presets/default/problem/statement/statement.rbx.tex +6 -2
- rbx/resources/presets/default/problem/testlib.h +6299 -0
- rbx/resources/presets/default/problem/validator.cpp +4 -3
- rbx/resources/presets/default/shared/contest_template.rbx.tex +13 -3
- rbx/resources/presets/default/shared/icpc.sty +33 -5
- rbx/resources/presets/default/shared/problem_template.rbx.tex +10 -1
- rbx/testing_utils.py +17 -1
- {rbx_cp-0.13.7.dist-info → rbx_cp-0.14.0.dist-info}/METADATA +4 -2
- {rbx_cp-0.13.7.dist-info → rbx_cp-0.14.0.dist-info}/RECORD +66 -63
- {rbx_cp-0.13.7.dist-info → rbx_cp-0.14.0.dist-info}/WHEEL +1 -1
- {rbx_cp-0.13.7.dist-info → rbx_cp-0.14.0.dist-info}/entry_points.txt +0 -1
- rbx/providers/__init__.py +0 -43
- rbx/providers/codeforces.py +0 -73
- rbx/providers/provider.py +0 -26
- rbx/submitors/__init__.py +0 -18
- rbx/submitors/codeforces.py +0 -121
- rbx/submitors/submitor.py +0 -25
- /rbx/resources/presets/default/problem/sols/{wa.cpp → wa-overflow.cpp} +0 -0
- {rbx_cp-0.13.7.dist-info → rbx_cp-0.14.0.dist-info}/LICENSE +0 -0
rbx/box/unit.py
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
import pathlib
|
2
2
|
from typing import List, Optional, Set
|
3
3
|
|
4
|
-
import syncer
|
5
4
|
from pydantic import BaseModel
|
6
5
|
|
7
6
|
from rbx import console
|
@@ -232,7 +231,6 @@ async def run_checker_unit_tests(progress: StatusProgress):
|
|
232
231
|
console.console.print(f' [status]Message[/status] {result.message}')
|
233
232
|
|
234
233
|
|
235
|
-
@syncer.sync
|
236
234
|
async def run_unit_tests(progress: StatusProgress):
|
237
235
|
await run_validator_unit_tests(progress)
|
238
236
|
await run_checker_unit_tests(progress)
|
rbx/box/validators.py
CHANGED
@@ -94,10 +94,6 @@ async def _validate_testcase(
|
|
94
94
|
vars: Optional[Dict[str, Primitive]] = None,
|
95
95
|
) -> Tuple[bool, Optional[str], HitBounds]:
|
96
96
|
vars = vars or {}
|
97
|
-
for var in vars:
|
98
|
-
assert (
|
99
|
-
var.isidentifier()
|
100
|
-
), f'Variable {var} should be a valid Python identifier.'
|
101
97
|
# TODO: check if needs to do some escaping
|
102
98
|
var_args = [f'--{k}={v}' for k, v in vars.items()]
|
103
99
|
var_args.extend(['--testOverviewLogFileName', 'validator.log'])
|
rbx/grading/judge/cacher.py
CHANGED
@@ -243,6 +243,12 @@ class FileCacher:
|
|
243
243
|
return typing.cast(IO[bytes], self._load(digest, False))
|
244
244
|
|
245
245
|
def path_for_symlink(self, digest: str) -> Optional[pathlib.Path]:
|
246
|
+
"""Retrieve the symlink path that points to the backend file.
|
247
|
+
|
248
|
+
If the file is not in the cache, or it is stored in a transformed
|
249
|
+
form (such as a compressed file), this function will return None,
|
250
|
+
as creating a symlink to it would not be meaningful.
|
251
|
+
"""
|
246
252
|
if digest == storage.TOMBSTONE:
|
247
253
|
raise TombstoneError()
|
248
254
|
|
@@ -252,7 +258,37 @@ class FileCacher:
|
|
252
258
|
logger.debug('Getting symlink file path %s.', digest)
|
253
259
|
return self.backend.path_for_symlink(digest)
|
254
260
|
|
261
|
+
def transient_path_for_symlink(self, digest: str):
|
262
|
+
"""Retrieve a file from the storage and return a path to it.
|
263
|
+
|
264
|
+
Notice that this is different than `path_for_symlink', which
|
265
|
+
returns the path to the file in the backend itself, not a path
|
266
|
+
inside the cacher.
|
267
|
+
|
268
|
+
`path_for_symlink` should be used for persistent links, such as
|
269
|
+
those you create in the package folder and should live across
|
270
|
+
multiple runs of the tool.
|
271
|
+
|
272
|
+
`transient_path_for_symlink` should be used for transient links, such as
|
273
|
+
those created in the sandbox, like test inputs and compiled
|
274
|
+
binaries.
|
275
|
+
"""
|
276
|
+
if digest == storage.TOMBSTONE:
|
277
|
+
raise TombstoneError()
|
278
|
+
self._load(digest, cache_only=True)
|
279
|
+
|
280
|
+
cache_file_path = self.file_dir / digest
|
281
|
+
if not cache_file_path.exists():
|
282
|
+
raise FileNotFoundError(f'File {digest} not found in cache.')
|
283
|
+
return cache_file_path
|
284
|
+
|
255
285
|
def digest_from_symlink(self, link: pathlib.Path) -> Optional[str]:
|
286
|
+
"""Retrieve the digest of the file that the symlink points to.
|
287
|
+
|
288
|
+
This is supposed to be used as a counterpart to `path_for_symlink'.
|
289
|
+
`digest_from_symlink(path_for_symlink(digest))` should be equivalent to
|
290
|
+
`digest`.
|
291
|
+
"""
|
256
292
|
if grading_context.is_transient():
|
257
293
|
return None
|
258
294
|
|
rbx/grading/judge/program.py
CHANGED
@@ -89,6 +89,14 @@ def get_preexec_fn(params: ProgramParams):
|
|
89
89
|
if params.fs_limit is not None:
|
90
90
|
fs_limit = params.fs_limit * 1024 # in bytes
|
91
91
|
resource.setrlimit(resource.RLIMIT_FSIZE, (fs_limit + 1, fs_limit * 2))
|
92
|
+
if sys.platform != 'darwin':
|
93
|
+
try:
|
94
|
+
resource.setrlimit(
|
95
|
+
resource.RLIMIT_STACK,
|
96
|
+
(resource.RLIM_INFINITY, resource.RLIM_INFINITY),
|
97
|
+
)
|
98
|
+
except ValueError:
|
99
|
+
pass
|
92
100
|
|
93
101
|
return preexec_fn
|
94
102
|
|
@@ -236,7 +244,6 @@ class Program:
|
|
236
244
|
cwd=self.params.chdir,
|
237
245
|
env={**os.environ, **self.params.env},
|
238
246
|
preexec_fn=get_preexec_fn(self.params),
|
239
|
-
close_fds=True,
|
240
247
|
)
|
241
248
|
self.start_time = monotonic()
|
242
249
|
|
@@ -244,6 +251,8 @@ class Program:
|
|
244
251
|
threading.Thread(target=self._handle_alarm, daemon=True).start()
|
245
252
|
|
246
253
|
def process_exit(self, exitstatus, ru) -> ProgramResult:
|
254
|
+
_maybe_close_files(self._files)
|
255
|
+
|
247
256
|
wall_time = monotonic() - self.start_time
|
248
257
|
cpu_time = get_cpu_time(ru)
|
249
258
|
memory_used = get_memory_usage(ru)
|
@@ -283,7 +292,7 @@ class Program:
|
|
283
292
|
):
|
284
293
|
program_codes.append(ProgramCode.OL)
|
285
294
|
|
286
|
-
|
295
|
+
result = ProgramResult(
|
287
296
|
exitcode=exitcode,
|
288
297
|
wall_time=wall_time,
|
289
298
|
cpu_time=cpu_time,
|
@@ -293,6 +302,7 @@ class Program:
|
|
293
302
|
killing_signal=killing_signal,
|
294
303
|
alarm_msg=self._alarm_msg or None,
|
295
304
|
)
|
305
|
+
return result
|
296
306
|
|
297
307
|
def wait(self):
|
298
308
|
assert self.popen is not None
|
rbx/grading/judge/sandbox.py
CHANGED
@@ -359,7 +359,7 @@ class SandboxBase(abc.ABC):
|
|
359
359
|
|
360
360
|
"""
|
361
361
|
if try_symlink and executable:
|
362
|
-
symlink_path = self.file_cacher.
|
362
|
+
symlink_path = self.file_cacher.transient_path_for_symlink(digest)
|
363
363
|
if symlink_path is not None:
|
364
364
|
created = self.create_symlink(
|
365
365
|
path,
|
@@ -155,7 +155,7 @@ class StupidSandbox(SandboxBase):
|
|
155
155
|
) -> SandboxLog:
|
156
156
|
return SandboxLog(
|
157
157
|
params=params.model_copy(deep=True),
|
158
|
-
execution_time=result.
|
158
|
+
execution_time=result.cpu_time,
|
159
159
|
memory_used=result.memory_used,
|
160
160
|
exitcode=result.exitcode,
|
161
161
|
exitstatus=self._get_exit_status(result),
|
@@ -163,6 +163,7 @@ class StupidSandbox(SandboxBase):
|
|
163
163
|
other_logs={
|
164
164
|
'program_codes': [code.value for code in result.program_codes],
|
165
165
|
'alarm_msg': result.alarm_msg,
|
166
|
+
'wall_time': result.wall_time,
|
166
167
|
},
|
167
168
|
)
|
168
169
|
|
rbx/grading/judge/storage.py
CHANGED
@@ -415,7 +415,40 @@ class FilesystemStorage(Storage):
|
|
415
415
|
def filename_from_symlink(self, link: pathlib.Path) -> Optional[str]:
|
416
416
|
if not link.is_symlink():
|
417
417
|
return None
|
418
|
-
|
419
|
-
|
418
|
+
|
419
|
+
# Track visited symlinks to detect circular references
|
420
|
+
visited = set()
|
421
|
+
current = link
|
422
|
+
max_depth = 100 # Reasonable limit to prevent infinite loops
|
423
|
+
depth = 0
|
424
|
+
|
425
|
+
while current.is_symlink() and depth < max_depth:
|
426
|
+
# Convert to absolute path for consistent comparison
|
427
|
+
abs_current = utils.abspath(current)
|
428
|
+
|
429
|
+
# Check for circular reference
|
430
|
+
if abs_current in visited:
|
431
|
+
return None
|
432
|
+
|
433
|
+
visited.add(abs_current)
|
434
|
+
|
435
|
+
# Read the target of the symlink
|
436
|
+
target = current.readlink()
|
437
|
+
|
438
|
+
# If target is relative, resolve it relative to the symlink's parent directory
|
439
|
+
if not target.is_absolute():
|
440
|
+
current = utils.abspath(current.parent / target)
|
441
|
+
else:
|
442
|
+
current = utils.abspath(target)
|
443
|
+
|
444
|
+
depth += 1
|
445
|
+
|
446
|
+
# If we hit the depth limit, assume circular reference
|
447
|
+
if depth >= max_depth:
|
448
|
+
return None
|
449
|
+
|
450
|
+
if not current.is_file():
|
451
|
+
return None
|
452
|
+
if not current.is_relative_to(self.path):
|
420
453
|
return None
|
421
|
-
return str(
|
454
|
+
return str(current.relative_to(self.path))
|
rbx/grading/limits.py
CHANGED
@@ -18,6 +18,10 @@ class Limits(BaseModel):
|
|
18
18
|
default=False, description='Whether to use double TL for this language.'
|
19
19
|
)
|
20
20
|
|
21
|
+
profile: Optional[str] = Field(
|
22
|
+
default=None, description='The profile that was used to get these limits.'
|
23
|
+
)
|
24
|
+
|
21
25
|
def get_expanded_tl(self) -> Optional[int]:
|
22
26
|
if self.time is None:
|
23
27
|
return None
|
rbx/grading/steps.py
CHANGED
@@ -14,7 +14,7 @@ import typer
|
|
14
14
|
from pydantic import BaseModel, Field
|
15
15
|
from rich.text import Text
|
16
16
|
|
17
|
-
from rbx import utils
|
17
|
+
from rbx import testing_utils, utils
|
18
18
|
from rbx.config import get_bits_stdcpp, get_jngen, get_testlib
|
19
19
|
from rbx.console import console
|
20
20
|
from rbx.grading import grading_context
|
@@ -389,7 +389,7 @@ def jngen_grading_input() -> GradingFileInput:
|
|
389
389
|
def _expand_part(part: str, sandbox: SandboxBase) -> List[str]:
|
390
390
|
part = part.strip()
|
391
391
|
if part.startswith('@glob:'):
|
392
|
-
return [
|
392
|
+
return [str(path) for path in sandbox.glob(part[6:])]
|
393
393
|
return [part]
|
394
394
|
|
395
395
|
|
@@ -736,6 +736,7 @@ def compile(
|
|
736
736
|
)
|
737
737
|
console.print(f'[error]Summary:[/error] {logs[-1].get_summary()}')
|
738
738
|
console.print(Text.from_ansi(logs[-1].log), style='default')
|
739
|
+
testing_utils.print_directory_tree(sandbox.get_root_path())
|
739
740
|
return False
|
740
741
|
|
741
742
|
return _process_output_artifacts(artifacts, sandbox)
|
@@ -17,16 +17,26 @@ statements:
|
|
17
17
|
configure:
|
18
18
|
- type: "rbx-tex" # Convert rbxTeX to TeX
|
19
19
|
template: "statement/template.rbx.tex"
|
20
|
+
vars:
|
21
|
+
# Turn into false to hide time limits and memory limits in the problem statement.
|
22
|
+
# Useful for ICPC-style contests where you distribute a separate info sheet.
|
23
|
+
show_limits: true
|
20
24
|
- name: "editorial-en"
|
21
25
|
extends: "statement-en"
|
22
26
|
override:
|
23
27
|
vars:
|
24
28
|
# Whether to show the problem statement in the editorial.
|
25
29
|
show_problem: false
|
30
|
+
show_limits: true
|
26
31
|
editorial: true
|
27
32
|
vars:
|
28
|
-
|
33
|
+
# Whether to render an "EDITORIAL" watermark.
|
29
34
|
watermark: false
|
35
|
+
editorial: true
|
36
|
+
- name: "info-en"
|
37
|
+
extends: "statement-en"
|
38
|
+
path: "statement/info.rbx.tex"
|
39
|
+
joiner: null
|
30
40
|
vars:
|
31
41
|
year: 2025
|
32
42
|
date: "2025-06-21"
|
@@ -0,0 +1,54 @@
|
|
1
|
+
\documentclass{article}
|
2
|
+
\usepackage[a4paper, margin=1in]{geometry}
|
3
|
+
\usepackage{multirow}
|
4
|
+
\usepackage{multicol}
|
5
|
+
\usepackage{graphicx}
|
6
|
+
\usepackage{amsfonts}
|
7
|
+
\usepackage[brazil]{babel}
|
8
|
+
\usepackage[utf8]{inputenc}
|
9
|
+
\usepackage[T1]{fontenc}
|
10
|
+
|
11
|
+
\usepackage[utf8]{inputenc}
|
12
|
+
\usepackage{icpc}
|
13
|
+
|
14
|
+
%\pagestyle{myheadings}
|
15
|
+
\markright{\VAR{contest.title}}
|
16
|
+
|
17
|
+
\begin{document}
|
18
|
+
|
19
|
+
\begin{titlepage}
|
20
|
+
\vspace*{\fill}
|
21
|
+
\begin{center}
|
22
|
+
\includegraphics[width=4cm]{logo.png}
|
23
|
+
|
24
|
+
\vspace{1cm}
|
25
|
+
{\huge{\bf \VAR{vars.year}}} \\
|
26
|
+
|
27
|
+
\vspace{0.5cm}
|
28
|
+
{\huge \bf \strInfoSheet}\\[12pt]
|
29
|
+
{\Large \bf \VAR{contest.title}}
|
30
|
+
\end{center}
|
31
|
+
|
32
|
+
\vspace{2cm}
|
33
|
+
|
34
|
+
%\subsection*{\strLimits}
|
35
|
+
|
36
|
+
\begin{center}
|
37
|
+
\begin{tabular}{c|ccc|c}
|
38
|
+
& \multicolumn{3}{c|}{\strTimeLimit (ms)} & \multirow{2}{*}{\strMemoryLimit (MiB)} \\
|
39
|
+
%\cline{2-4}
|
40
|
+
{\sf \strProblem} & {\sf C/C++} &{\sf Java} & {\sf Python3} & \\
|
41
|
+
\hline
|
42
|
+
%- for problem in problems:
|
43
|
+
\VAR{problem.short_name}
|
44
|
+
& \VAR{problem.limits.timelimit_for_language('cpp')}
|
45
|
+
& \VAR{problem.limits.timelimit_for_language('java')}
|
46
|
+
& \VAR{problem.limits.timelimit_for_language('py')}
|
47
|
+
& \VAR{problem.limits.memorylimit_for_language()}
|
48
|
+
\\ \hline
|
49
|
+
%- endfor
|
50
|
+
\end{tabular}
|
51
|
+
\end{center}
|
52
|
+
\vspace*{\fill}
|
53
|
+
\end{titlepage}
|
54
|
+
\end{document}
|
@@ -17,8 +17,18 @@ testcases:
|
|
17
17
|
solutions:
|
18
18
|
- path: "sols/main.cpp"
|
19
19
|
outcome: "ACCEPTED"
|
20
|
-
- path: "sols/
|
20
|
+
- path: "sols/ac-*.cpp"
|
21
|
+
outcome: "ACCEPTED"
|
22
|
+
- path: "sols/wa-*.cpp"
|
21
23
|
outcome: "WRONG_ANSWER"
|
24
|
+
- path: "sols/tle-*.cpp"
|
25
|
+
outcome: "TIME_LIMIT_EXCEEDED"
|
26
|
+
- path: "sols/mle-*.cpp"
|
27
|
+
outcome: "MEMORY_LIMIT_EXCEEDED"
|
28
|
+
- path: "sols/re-*.cpp"
|
29
|
+
outcome: "RUNTIME_ERROR"
|
30
|
+
- path: "sols/fail-*.cpp"
|
31
|
+
outcome: "INCORRECT"
|
22
32
|
statements:
|
23
33
|
- name: "statement-en"
|
24
34
|
title: "New Problem"
|
@@ -31,11 +41,14 @@ statements:
|
|
31
41
|
configure:
|
32
42
|
- type: "rbx-tex" # Convert rbxTeX to TeX
|
33
43
|
template: "statement/template.rbx.tex"
|
44
|
+
vars:
|
45
|
+
# Set to false to hide time limits and memory limits in the problem statement.
|
46
|
+
show_limits: true
|
34
47
|
stresses:
|
35
48
|
- name: "stress"
|
36
49
|
generator:
|
37
50
|
name: "gens/gen"
|
38
|
-
args: "[1..<
|
51
|
+
args: "[1..<N.max>] @" # `@` generates a random string
|
39
52
|
finder: "[sols/wa.cpp] ~ INCORRECT"
|
40
53
|
unitTests:
|
41
54
|
validator:
|
@@ -51,4 +64,9 @@ unitTests:
|
|
51
64
|
# Can be used in the validator, checker, interactor, stress tests
|
52
65
|
# and in the statement.
|
53
66
|
vars:
|
54
|
-
|
67
|
+
# Author name to be displayed in the editorial.
|
68
|
+
author: "John Doe"
|
69
|
+
# Constraints of the problem.
|
70
|
+
N:
|
71
|
+
min: 1
|
72
|
+
max: 1000000000
|
@@ -1,17 +1,25 @@
|
|
1
1
|
#ifndef _RBX_H
|
2
2
|
#define _RBX_H
|
3
|
+
#include <cstdint>
|
4
|
+
#include <limits>
|
3
5
|
#include <optional>
|
4
6
|
#include <stdexcept>
|
5
7
|
#include <string>
|
6
8
|
|
7
9
|
std::optional<std::string> getStringVar(std::string name) {
|
8
|
-
|
10
|
+
if (name == "author") {
|
11
|
+
return "John Doe";
|
12
|
+
}
|
13
|
+
|
9
14
|
return std::nullopt;
|
10
15
|
}
|
11
16
|
|
12
|
-
std::optional<
|
13
|
-
if (name == "
|
14
|
-
return 1000000000;
|
17
|
+
std::optional<int64_t> getIntVar(std::string name) {
|
18
|
+
if (name == "N.max") {
|
19
|
+
return static_cast<int64_t>(1000000000);
|
20
|
+
}
|
21
|
+
if (name == "N.min") {
|
22
|
+
return static_cast<int64_t>(1);
|
15
23
|
}
|
16
24
|
|
17
25
|
return std::nullopt;
|
@@ -29,7 +37,46 @@ std::optional<bool> getBoolVar(std::string name) {
|
|
29
37
|
|
30
38
|
template <typename T> T getVar(std::string name);
|
31
39
|
|
32
|
-
template <>
|
40
|
+
template <> int32_t getVar<int32_t>(std::string name) {
|
41
|
+
auto opt = getIntVar(name);
|
42
|
+
if (!opt.has_value()) {
|
43
|
+
throw std::runtime_error("Variable " + name +
|
44
|
+
" is not an integer or could not be found");
|
45
|
+
}
|
46
|
+
if (opt.value() < std::numeric_limits<int32_t>::min() ||
|
47
|
+
opt.value() > std::numeric_limits<int32_t>::max()) {
|
48
|
+
throw std::runtime_error("Variable " + name + " of value " +
|
49
|
+
std::to_string(opt.value()) +
|
50
|
+
" does not fit in int32_t");
|
51
|
+
}
|
52
|
+
return opt.value();
|
53
|
+
}
|
54
|
+
|
55
|
+
template <> uint32_t getVar<uint32_t>(std::string name) {
|
56
|
+
auto opt = getIntVar(name);
|
57
|
+
if (!opt.has_value()) {
|
58
|
+
throw std::runtime_error("Variable " + name +
|
59
|
+
" is not an integer or could not be found");
|
60
|
+
}
|
61
|
+
if (opt.value() < std::numeric_limits<uint32_t>::min() ||
|
62
|
+
opt.value() > std::numeric_limits<uint32_t>::max()) {
|
63
|
+
throw std::runtime_error("Variable " + name + " of value " +
|
64
|
+
std::to_string(opt.value()) +
|
65
|
+
" does not fit in uint32_t");
|
66
|
+
}
|
67
|
+
return opt.value();
|
68
|
+
}
|
69
|
+
|
70
|
+
template <> int64_t getVar<int64_t>(std::string name) {
|
71
|
+
auto opt = getIntVar(name);
|
72
|
+
if (!opt.has_value()) {
|
73
|
+
throw std::runtime_error("Variable " + name +
|
74
|
+
" is not an integer or could not be found");
|
75
|
+
}
|
76
|
+
return opt.value();
|
77
|
+
}
|
78
|
+
|
79
|
+
template <> uint64_t getVar<uint64_t>(std::string name) {
|
33
80
|
auto opt = getIntVar(name);
|
34
81
|
if (!opt.has_value()) {
|
35
82
|
throw std::runtime_error("Variable " + name +
|
@@ -3,7 +3,7 @@ Given two integers $A$ and $B$, determine the value of $A + B$.
|
|
3
3
|
%- endblock
|
4
4
|
|
5
5
|
%- block input
|
6
|
-
The input is a single line containing two integers $A$ and $B$ (
|
6
|
+
The input is a single line containing two integers $A$ and $B$ ($\VAR{vars.N.max} \leq A, B \leq \VAR{vars.N.max | sci}$).
|
7
7
|
%- endblock
|
8
8
|
|
9
9
|
%- block output
|
@@ -12,4 +12,8 @@ The output must contain only one integer, the sum of $A$ and $B$.
|
|
12
12
|
|
13
13
|
%- block notes
|
14
14
|
No notes.
|
15
|
-
%- endblock
|
15
|
+
%- endblock
|
16
|
+
|
17
|
+
%- block editorial
|
18
|
+
This is the editorial.
|
19
|
+
%- endblock
|