window.ENGINE_PY = "import io\nimport json\nimport sys\nimport threading\nimport time\nimport traceback\n\nDEFAULT_TIMEOUT = 8.0\nEXERCISE_TEST_TIMEOUT = 2.0\nMAX_VARS = 40\nMAX_REPR = 200\n# W3: cap captured stdout/stderr so a runaway print loop cannot blow up the\n# JSON payload or exhaust memory. 1 MiB is plenty for terminal output.\nMAX_OUTPUT = 1_048_576\n# W2: coarse byte-size guard applied *before* calling repr() on a user value,\n# so a gigantic object (or a repr that would allocate massively) is skipped.\nMAX_VAR_BYTES = 1_000_000\n\n\nclass SandboxTimeout(Exception):\n    pass\n\n\nclass InputExhausted(Exception):\n    pass\n\n\ndef _make_input(stdin_lines):\n    queue = list(stdin_lines)\n\n    def _input(prompt=\"\"):\n        if prompt:\n            sys.stdout.write(str(prompt))\n            sys.stdout.flush()\n        if not queue:\n            raise InputExhausted(\n                \"运行台不支持交互式输入:请把测试数据直接写在代码里,或到「神经接口」终端中使用 input()\"\n            )\n        return queue.pop(0)\n\n    return _input\n\n\nclass _Watchdog:\n    def __init__(self, deadline):\n        self.deadline = deadline\n\n    def __call__(self, frame, event, arg):\n        if time.perf_counter() > self.deadline:\n            raise SandboxTimeout()\n        return self\n\n\ndef _error_payload(etype, evalue, tb):\n    trimmed_tb = tb.tb_next if tb is not None else None\n    text = \"\".join(traceback.format_exception(etype, evalue, trimmed_tb)).strip()\n    return {\n        \"type\": getattr(etype, \"__name__\", \"Error\"),\n        \"message\": str(evalue),\n        \"traceback\": text,\n    }\n\n\ndef _truncate(text):\n    \"\"\"W3: keep stdout/stderr bounded. When too long, drop the head and keep the\n    tail so the most recent output (and the error) stays visible.\"\"\"\n    if text and len(text) > MAX_OUTPUT:\n        return \"[输出过长已截断]\\n\" + text[-MAX_OUTPUT:]\n    return text\n\n\ndef _run_protected(code, namespace, stdin_lines, timeout):\n    out_buf = io.StringIO()\n    err_buf = io.StringIO()\n    state = {\"finished\": False, \"error\": None}\n    import builtins\n\n    original_input = builtins.input\n    old_stdout = sys.stdout\n    old_stderr = sys.stderr\n\n    def target():\n        try:\n            sys.stdout = out_buf\n            sys.stderr = err_buf\n            builtins.input = _make_input(stdin_lines)\n            sys.settrace(_Watchdog(time.perf_counter() + timeout))\n            try:\n                exec(compile(code, \"<pyneon>\", \"exec\"), namespace)\n            finally:\n                sys.settrace(None)\n        except SandboxTimeout:\n            state[\"error\"] = {\n                \"type\": \"Timeout\",\n                \"message\": \"代码运行超过 %.1f 秒,已被强制中断(可能是死循环)\"\n                % timeout,\n                \"traceback\": \"\",\n            }\n        except InputExhausted as exc:\n            state[\"error\"] = {\n                \"type\": \"InputError\",\n                \"message\": str(exc),\n                \"traceback\": \"\",\n            }\n        except BaseException:\n            etype, evalue, tb = sys.exc_info()\n            state[\"error\"] = _error_payload(etype, evalue, tb)\n        finally:\n            builtins.input = original_input\n            sys.stdout = old_stdout\n            sys.stderr = old_stderr\n            state[\"finished\"] = True\n\n    started_at = time.perf_counter()\n    worker = threading.Thread(target=target, daemon=True)\n    worker.start()\n    worker.join(timeout + 1.5)\n    duration_ms = int((time.perf_counter() - started_at) * 1000)\n\n    error = (\n        None\n        if state[\"finished\"] and state[\"error\"] is None\n        else (\n            state[\"error\"]\n            or {\n                \"type\": \"Timeout\",\n                \"message\": \"代码运行超过 %.1f 秒,已被强制中断(可能是死循环)\"\n                % timeout,\n                \"traceback\": \"\",\n            }\n        )\n    )\n    return {\n        \"stdout\": _truncate(out_buf.getvalue()),\n        \"stderr\": _truncate(err_buf.getvalue()),\n        \"error\": error,\n        \"duration_ms\": duration_ms,\n    }\n\n\ndef _snapshot(namespace):\n    items = []\n    for key, value in namespace.items():\n        if key.startswith(\"__\"):\n            continue\n        if callable(value) and getattr(value, \"__module__\", None) == \"builtins\":\n            continue\n        try:\n            if sys.getsizeof(value, 0) > MAX_VAR_BYTES:\n                rendered = \"<对象过大>\"\n            else:\n                rendered = repr(value)\n        except BaseException:\n            # W2: a custom __repr__ that raises (or recurses into RecursionError)\n            # must never crash the snapshot of the caller's session.\n            rendered = \"<unrepresentable>\"\n        if len(rendered) > MAX_REPR:\n            rendered = rendered[:MAX_REPR] + \"…\"\n        items.append({\"name\": key, \"type\": type(value).__name__, \"value\": rendered})\n        if len(items) >= MAX_VARS:\n            break\n    return items\n\n\ndef run_code(code, stdin_lines=None, timeout=DEFAULT_TIMEOUT):\n    namespace = {\"__name__\": \"__main__\", \"__doc__\": None}\n    core = _run_protected(code, namespace, stdin_lines or [], float(timeout))\n    result = dict(core)\n    result[\"ok\"] = core[\"error\"] is None\n    result[\"variables\"] = _snapshot(namespace)\n    return result\n\n\ndef check_exercise(code, tests, stdin_lines=None):\n    namespace = {\"__name__\": \"__main__\", \"__doc__\": None}\n    core = _run_protected(code, namespace, stdin_lines or [], DEFAULT_TIMEOUT)\n    if core[\"error\"] is not None:\n        return {\n            \"ok\": False,\n            \"passed\": False,\n            \"stdout\": core[\"stdout\"],\n            \"stderr\": core[\"stderr\"],\n            \"test_output\": \"\",\n            \"error\": core[\"error\"],\n            \"duration_ms\": core[\"duration_ms\"],\n            \"variables\": [],\n        }\n\n    failures = []\n    test_outputs = []\n    for index, test in enumerate(tests, start=1):\n        test_state = {\"finished\": False, \"error\": None}\n        test_out = io.StringIO()\n\n        def test_target(ns=namespace, source=test, st=test_state, buf=test_out):\n            old_stdout = sys.stdout\n            try:\n                sys.stdout = buf\n                sys.settrace(_Watchdog(time.perf_counter() + EXERCISE_TEST_TIMEOUT))\n                try:\n                    exec(compile(source, \"<neon-test-%d>\" % index, \"exec\"), ns)\n                finally:\n                    sys.settrace(None)\n            except AssertionError as exc:\n                st[\"error\"] = str(exc) or \"断言未通过\"\n            except SandboxTimeout:\n                st[\"error\"] = \"测试执行超时\"\n            except BaseException:\n                etype, evalue, _tb = sys.exc_info()\n                st[\"error\"] = \"%s: %s\" % (getattr(etype, \"__name__\", \"Error\"), evalue)\n            finally:\n                sys.stdout = old_stdout\n                st[\"finished\"] = True\n\n        worker = threading.Thread(target=test_target, daemon=True)\n        worker.start()\n        worker.join(EXERCISE_TEST_TIMEOUT + 1.0)\n        if not test_state[\"finished\"]:\n            test_state[\"error\"] = \"测试执行超时\"\n        # G3b: collect whatever the test printed (both for passing and failing\n        # cases) so diagnostic print() calls inside a test are visible.\n        test_outputs.append(test_out.getvalue())\n        if test_state[\"error\"] is not None:\n            failures.append(\n                {\"index\": index, \"test\": test.strip(), \"reason\": test_state[\"error\"]}\n            )\n\n    passed = not failures\n    message = (\n        \"\"\n        if passed\n        else \"共 %d 个用例,%d 个未通过;首个失败:%s\"\n        % (len(tests), len(failures), failures[0][\"reason\"])\n    )\n    return {\n        \"ok\": True,\n        \"passed\": passed,\n        \"stdout\": core[\"stdout\"],\n        \"stderr\": core[\"stderr\"],\n        \"test_output\": _truncate(\"\".join(test_outputs)),\n        \"error\": None\n        if passed\n        else {\"type\": \"TestFailed\", \"message\": message, \"traceback\": \"\"},\n        \"duration_ms\": core[\"duration_ms\"],\n        \"variables\": _snapshot(namespace),\n    }\n\n\ndef info_json():\n    return json.dumps(\n        {\n            \"python_version\": sys.version.split()[0],\n            \"platform\": sys.platform,\n        }\n    )\n\n\ndef run_code_json(payload_json):\n    payload = json.loads(payload_json)\n    result = run_code(\n        payload.get(\"code\", \"\"),\n        payload.get(\"stdin\") or [],\n        payload.get(\"timeout\", DEFAULT_TIMEOUT),\n    )\n    return json.dumps(result, ensure_ascii=False)\n\n\ndef check_exercise_json(payload_json):\n    payload = json.loads(payload_json)\n    result = check_exercise(\n        payload.get(\"code\", \"\"),\n        payload.get(\"tests\") or [],\n        payload.get(\"stdin\") or [],\n    )\n    return json.dumps(result, ensure_ascii=False)\n";
window.REPL_HELPER_PY = "\nimport json as _json\n_REPL_NS = {\"__name__\": \"__main__\"}\n\ndef repl_exec_json(payload_json):\n    \"\"\"在持久命名空间里执行一行/一段 REPL 代码,返回与 run_code 同构的结果。\"\"\"\n    p = _json.loads(payload_json)\n    code = p.get(\"code\", \"\")\n    res = _run_protected(code, _REPL_NS, p.get(\"stdin\") or [], float(p.get(\"timeout\", DEFAULT_TIMEOUT)))\n    r = dict(res)\n    r[\"ok\"] = res[\"error\"] is None\n    r[\"variables\"] = _snapshot(_REPL_NS)\n    return _json.dumps(r, ensure_ascii=False)\n\ndef repl_reset_json(payload_json=\"\"):\n    \"\"\"清空 REPL 会话命名空间(保留 __name__)。\"\"\"\n    _REPL_NS.clear()\n    _REPL_NS[\"__name__\"] = \"__main__\"\n    return \"{}\"\n";