diff --git a/code/align/assets/tokenizer/goedel-prover-v2-8b/PROVENANCE.md b/code/align/assets/tokenizer/goedel-prover-v2-8b/PROVENANCE.md
new file mode 100644
index 0000000000000000000000000000000000000000..2ca6ff52b0f0b5dd5b77803a45d4ab4007427936
--- /dev/null
+++ b/code/align/assets/tokenizer/goedel-prover-v2-8b/PROVENANCE.md
@@ -0,0 +1,7 @@
+Goedel-LM/Goedel-Prover-V2-8B tokenizer files (Qwen3 byte-level BPE; Apache-2.0), vendored so
+P2 tokenisation does not need the vLLM venv or the 16 GB model snapshot. Copied byte-for-byte
+from the HF snapshot dfd02e6271a58375dfbf3ece0175277cf6b6a89a. sha256:
+ tokenizer.json aeb13307a71acd8fe81861d94ad54ab689df773318809eed3cbe794b4492dae4
+ tokenizer_config.json 5d27a191a7ba6f93bcc2a8e78bc633764bbaaee263e9f3d9225e2c8621a5dd72
+ special_tokens_map.json 76862e765266b85aa9459767e33cbaf13970f327a0e88d1c65846c2ddd3a1ecd
+Tokenizer only: no config.json, no weights, no vocab.json/merges.txt (redundant with tokenizer.json).
diff --git a/code/align/assets/tokenizer/goedel-prover-v2-8b/special_tokens_map.json b/code/align/assets/tokenizer/goedel-prover-v2-8b/special_tokens_map.json
new file mode 100644
index 0000000000000000000000000000000000000000..ac23c0aaa2434523c494330aeb79c58395378103
--- /dev/null
+++ b/code/align/assets/tokenizer/goedel-prover-v2-8b/special_tokens_map.json
@@ -0,0 +1,31 @@
+{
+ "additional_special_tokens": [
+ "<|im_start|>",
+ "<|im_end|>",
+ "<|object_ref_start|>",
+ "<|object_ref_end|>",
+ "<|box_start|>",
+ "<|box_end|>",
+ "<|quad_start|>",
+ "<|quad_end|>",
+ "<|vision_start|>",
+ "<|vision_end|>",
+ "<|vision_pad|>",
+ "<|image_pad|>",
+ "<|video_pad|>"
+ ],
+ "eos_token": {
+ "content": "<|im_end|>",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false
+ },
+ "pad_token": {
+ "content": "<|endoftext|>",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false
+ }
+}
diff --git a/code/align/assets/tokenizer/goedel-prover-v2-8b/tokenizer_config.json b/code/align/assets/tokenizer/goedel-prover-v2-8b/tokenizer_config.json
new file mode 100644
index 0000000000000000000000000000000000000000..e8bb905c2fb94a128cc19a801677504be2183910
--- /dev/null
+++ b/code/align/assets/tokenizer/goedel-prover-v2-8b/tokenizer_config.json
@@ -0,0 +1,241 @@
+{
+ "add_bos_token": false,
+ "add_prefix_space": false,
+ "added_tokens_decoder": {
+ "151643": {
+ "content": "<|endoftext|>",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": true
+ },
+ "151644": {
+ "content": "<|im_start|>",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": true
+ },
+ "151645": {
+ "content": "<|im_end|>",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": true
+ },
+ "151646": {
+ "content": "<|object_ref_start|>",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": true
+ },
+ "151647": {
+ "content": "<|object_ref_end|>",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": true
+ },
+ "151648": {
+ "content": "<|box_start|>",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": true
+ },
+ "151649": {
+ "content": "<|box_end|>",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": true
+ },
+ "151650": {
+ "content": "<|quad_start|>",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": true
+ },
+ "151651": {
+ "content": "<|quad_end|>",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": true
+ },
+ "151652": {
+ "content": "<|vision_start|>",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": true
+ },
+ "151653": {
+ "content": "<|vision_end|>",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": true
+ },
+ "151654": {
+ "content": "<|vision_pad|>",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": true
+ },
+ "151655": {
+ "content": "<|image_pad|>",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": true
+ },
+ "151656": {
+ "content": "<|video_pad|>",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": true
+ },
+ "151657": {
+ "content": "",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": false
+ },
+ "151658": {
+ "content": "",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": false
+ },
+ "151659": {
+ "content": "<|fim_prefix|>",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": false
+ },
+ "151660": {
+ "content": "<|fim_middle|>",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": false
+ },
+ "151661": {
+ "content": "<|fim_suffix|>",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": false
+ },
+ "151662": {
+ "content": "<|fim_pad|>",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": false
+ },
+ "151663": {
+ "content": "<|repo_name|>",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": false
+ },
+ "151664": {
+ "content": "<|file_sep|>",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": false
+ },
+ "151665": {
+ "content": "",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": false
+ },
+ "151666": {
+ "content": "",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": false
+ },
+ "151667": {
+ "content": "",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": false
+ },
+ "151668": {
+ "content": "",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false,
+ "special": false
+ }
+ },
+ "additional_special_tokens": [
+ "<|im_start|>",
+ "<|im_end|>",
+ "<|object_ref_start|>",
+ "<|object_ref_end|>",
+ "<|box_start|>",
+ "<|box_end|>",
+ "<|quad_start|>",
+ "<|quad_end|>",
+ "<|vision_start|>",
+ "<|vision_end|>",
+ "<|vision_pad|>",
+ "<|image_pad|>",
+ "<|video_pad|>"
+ ],
+ "bos_token": null,
+ "chat_template": "{%- if tools %}\n {{- '<|im_start|>system\\n' }}\n {%- if messages[0].role == 'system' %}\n {{- messages[0].content + '\\n\\n' }}\n {%- endif %}\n {{- \"# Tools\\n\\nYou may call one or more functions to assist with the user query.\\n\\nYou are provided with function signatures within XML tags:\\n\" }}\n {%- for tool in tools %}\n {{- \"\\n\" }}\n {{- tool | tojson }}\n {%- endfor %}\n {{- \"\\n\\n\\nFor each function call, return a json object with function name and arguments within XML tags:\\n\\n{\\\"name\\\": , \\\"arguments\\\": }\\n<|im_end|>\\n\" }}\n{%- else %}\n {%- if messages[0].role == 'system' %}\n {{- '<|im_start|>system\\n' + messages[0].content + '<|im_end|>\\n' }}\n {%- endif %}\n{%- endif %}\n{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}\n{%- for message in messages[::-1] %}\n {%- set index = (messages|length - 1) - loop.index0 %}\n {%- if ns.multi_step_tool and message.role == \"user\" and message.content is string and not(message.content.startswith('') and message.content.endswith('')) %}\n {%- set ns.multi_step_tool = false %}\n {%- set ns.last_query_index = index %}\n {%- endif %}\n{%- endfor %}\n{%- for message in messages %}\n {%- if message.content is string %}\n {%- set content = message.content %}\n {%- else %}\n {%- set content = '' %}\n {%- endif %}\n {%- if (message.role == \"user\") or (message.role == \"system\" and not loop.first) %}\n {{- '<|im_start|>' + message.role + '\\n' + content + '<|im_end|>' + '\\n' }}\n {%- elif message.role == \"assistant\" %}\n {%- set reasoning_content = '' %}\n {%- if message.reasoning_content is string %}\n {%- set reasoning_content = message.reasoning_content %}\n {%- else %}\n {%- if '' in content %}\n {%- set reasoning_content = content.split('')[0].rstrip('\\n').split('')[-1].lstrip('\\n') %}\n {%- set content = content.split('')[-1].lstrip('\\n') %}\n {%- endif %}\n {%- endif %}\n {%- if loop.index0 > ns.last_query_index %}\n {%- if loop.last or (not loop.last and reasoning_content) %}\n {{- '<|im_start|>' + message.role + '\\n\\n' + reasoning_content.strip('\\n') + '\\n\\n\\n' + content.lstrip('\\n') }}\n {%- else %}\n {{- '<|im_start|>' + message.role + '\\n' + content }}\n {%- endif %}\n {%- else %}\n {{- '<|im_start|>' + message.role + '\\n' + content }}\n {%- endif %}\n {%- if message.tool_calls %}\n {%- for tool_call in message.tool_calls %}\n {%- if (loop.first and content) or (not loop.first) %}\n {{- '\\n' }}\n {%- endif %}\n {%- if tool_call.function %}\n {%- set tool_call = tool_call.function %}\n {%- endif %}\n {{- '\\n{\"name\": \"' }}\n {{- tool_call.name }}\n {{- '\", \"arguments\": ' }}\n {%- if tool_call.arguments is string %}\n {{- tool_call.arguments }}\n {%- else %}\n {{- tool_call.arguments | tojson }}\n {%- endif %}\n {{- '}\\n' }}\n {%- endfor %}\n {%- endif %}\n {{- '<|im_end|>\\n' }}\n {%- elif message.role == \"tool\" %}\n {%- if loop.first or (messages[loop.index0 - 1].role != \"tool\") %}\n {{- '<|im_start|>user' }}\n {%- endif %}\n {{- '\\n\\n' }}\n {{- content }}\n {{- '\\n' }}\n {%- if loop.last or (messages[loop.index0 + 1].role != \"tool\") %}\n {{- '<|im_end|>\\n' }}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n {%- if enable_thinking is defined and enable_thinking is false %}\n {{- '\\n\\n\\n\\n' }}\n {%- endif %}\n{%- endif %}",
+ "clean_up_tokenization_spaces": false,
+ "eos_token": "<|im_end|>",
+ "errors": "replace",
+ "extra_special_tokens": {},
+ "model_max_length": 131072,
+ "pad_token": "<|endoftext|>",
+ "padding_side": "right",
+ "split_special_tokens": false,
+ "tokenizer_class": "Qwen2Tokenizer",
+ "unk_token": null
+}
diff --git a/code/align/model/artifacts/logit_noise_floor.json b/code/align/model/artifacts/logit_noise_floor.json
new file mode 100644
index 0000000000000000000000000000000000000000..8153dd90d553f9c0c027e793b6abc070c363cb89
--- /dev/null
+++ b/code/align/model/artifacts/logit_noise_floor.json
@@ -0,0 +1,20 @@
+{
+ "2": {
+ "max_abs_dlogit": 0.75586,
+ "mean_abs_dlogit": 0.015027,
+ "argmax_flips": 1,
+ "positions": 794
+ },
+ "4": {
+ "max_abs_dlogit": 0.78516,
+ "mean_abs_dlogit": 0.01436,
+ "argmax_flips": 1,
+ "positions": 794
+ },
+ "8": {
+ "max_abs_dlogit": 0.78516,
+ "mean_abs_dlogit": 0.014431,
+ "argmax_flips": 1,
+ "positions": 794
+ }
+}
\ No newline at end of file
diff --git a/code/align/model/artifacts/sdpa_baseline.json b/code/align/model/artifacts/sdpa_baseline.json
new file mode 100644
index 0000000000000000000000000000000000000000..b6d26c1841152ed68ea119be86bb3adf6671894f
--- /dev/null
+++ b/code/align/model/artifacts/sdpa_baseline.json
@@ -0,0 +1,17 @@
+{
+ "512": {
+ "FLASH_ATTENTION": 0.18939885194413364,
+ "EFFICIENT_ATTENTION": NaN,
+ "MATH": 4.349638050189242
+ },
+ "1024": {
+ "FLASH_ATTENTION": 0.6236543995328248,
+ "EFFICIENT_ATTENTION": NaN,
+ "MATH": 16.252079099649563
+ },
+ "2048": {
+ "FLASH_ATTENTION": 2.281787298852578,
+ "EFFICIENT_ATTENTION": NaN,
+ "MATH": 61.56193719943985
+ }
+}
\ No newline at end of file
diff --git a/code/align/model/artifacts/triton_fa2_equivalence.json b/code/align/model/artifacts/triton_fa2_equivalence.json
new file mode 100644
index 0000000000000000000000000000000000000000..9f7fc5fa7f9880bd23f34053daa33cd0729e510b
--- /dev/null
+++ b/code/align/model/artifacts/triton_fa2_equivalence.json
@@ -0,0 +1,25 @@
+{
+ "environment": {
+ "compute_capability": "8.6",
+ "device_name": "NVIDIA A10G",
+ "torch": "2.11.0+cu128",
+ "triton": "3.6.0"
+ },
+ "kernel_sha256": "5f2cbe2088a7e583895268c59895366e39a4eb79d4dc8b719cbca2e57c2eed4b",
+ "schema_version": 1,
+ "shapes_covered": [
+ "gqa (n_q_heads, n_kv_heads) in [(8,2), (32,8)] plus MHA-degenerate (4,4)",
+ "head_dim in [64, 128]",
+ "seq in [192, 256, 512, 1000] (block-divisible AND non-divisible)",
+ "causal and non-causal",
+ "key_padding_mask, right-padded, non-block-aligned",
+ "every autotune config individually pinned (fwd + both bwd kernels)",
+ "dtype bfloat16 only (fp16/fp32 rejected)"
+ ],
+ "tolerances": {
+ "absolute_floor": 0.02,
+ "policy": "max |err| vs fp32 reference <= max(3.0 x torch-SDPA-bf16 error, 2e-2)",
+ "slack_over_sdpa": 3.0
+ },
+ "verified_at": "2026-08-18T13:18:04+00:00"
+}
diff --git a/code/align/rl/envs/__init__.py b/code/align/rl/envs/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..74cf3d4df8eec454e0a02b1f4915d93db7fe5712
--- /dev/null
+++ b/code/align/rl/envs/__init__.py
@@ -0,0 +1,35 @@
+"""RL environments (opt-in; nothing in `align.rl` imports this package).
+
+ lean_multiturn.py multi-turn Lean prover episodes with Goedel correction feedback,
+ multi-turn loss masks and per-turn advantage assignment.
+"""
+
+from .lean_multiturn import (
+ EpisodeRecord,
+ EpisodeSpec,
+ GenRequest,
+ Generation,
+ LeanMultiTurnConfig,
+ LeanMultiTurnEnv,
+ PrefixBreak,
+ Problem,
+ SandboxNotWired,
+ TrainingRow,
+ TurnRecord,
+ assign_advantages,
+)
+
+__all__ = [
+ "EpisodeRecord",
+ "EpisodeSpec",
+ "GenRequest",
+ "Generation",
+ "LeanMultiTurnConfig",
+ "LeanMultiTurnEnv",
+ "PrefixBreak",
+ "Problem",
+ "SandboxNotWired",
+ "TrainingRow",
+ "TurnRecord",
+ "assign_advantages",
+]
diff --git a/code/align/rl/envs/lean_multiturn.py b/code/align/rl/envs/lean_multiturn.py
new file mode 100644
index 0000000000000000000000000000000000000000..150b54b23ab7a68709433fef3cc8fa9bf42929f2
--- /dev/null
+++ b/code/align/rl/envs/lean_multiturn.py
@@ -0,0 +1,538 @@
+"""Multi-turn Lean prover environment (P4-B): proof attempt -> compiler feedback -> retry.
+
+An EPISODE is up to K turns on one formal statement. Turn 0 is the Goedel round-0 prompt.
+If the verifier rejects the proof with compile errors, turn k+1's prompt is the Goedel
+CORRECTION prompt: the history, the failed attempt as an assistant message, and a user
+message carrying the ``-annotated compiler output. The episode ends at the
+first verified turn, at K turns, or at the first rejection that feedback cannot repair.
+
+Everything below exists to prevent one named failure; each is pinned by a test.
+
+* PROMPT DRIFT. The prompts are NOT re-expressed here. `extras/vllm_rollout/lean_prompts.py`
+ (byte-faithful to Goedel-Prover-V2) is loaded BY FILE PATH, read-only, the way
+ tests/test_lean_gen_contract.py and scripts/p2/p2_common.py do. A second copy of the
+ correction template would drift by one newline and silently change the policy sampled.
+
+* LOSS ON FEEDBACK TOKENS. The compiler's error text is environment output, not policy
+ output. If it carried loss the model would be trained to *write* error messages, and the
+ gradient would reward predicting them. Rows carry `response_mask` that is 1 ONLY on tokens
+ the generator returned; prompt, chat-template glue and feedback are 0. `turn_ids` labels
+ each token with its turn (-1 unlabeled) so tests and the loss can audit it.
+
+* RE-TOKENIZATION DRIFT. Token ids are never re-derived from text for generated tokens.
+ layout="concatenated" builds ONE row per episode: context_k = context_{k-1} + generated_{k-1}
+ + encode(template delta). That is only valid when the chat template renders turn k's
+ prompt as a pure extension of (turn k-1's prompt + completion text); templates that rewrite
+ earlier assistant content (Qwen3 strips old blocks) break it. We CHECK the prefix
+ property and raise `PrefixBreak` rather than train on tokens the policy never saw.
+ layout="per_turn" (the default) emits one row per turn from that turn's exact prompt ids;
+ it is always exact, at the price of re-running the shared prefix.
+
+* STATEMENT REWRITING IS NOT A SOLVE. `problem_check` re-inserts the ORIGINAL statement over
+ whatever theorem the model displayed, so a model that states an easier theorem and proves
+ that is *accepted* if its tactics happen to close the original. Goedel's evaluation credits
+ it; a TRAINING reward must not (it teaches the model to misstate the problem). Before the
+ verifier is consulted, `scripts/p2/p2_common.displayed_statement_matches` (loaded by path)
+ must hold, else the turn is terminal with reason `statement_rewrite` and reward 0.
+
+* TRUNCATION IS NOT A SUBMISSION. A generation that hit the length limit is rejected
+ (`truncated`) even if its plan contains an earlier closed code fence; the final answer was
+ cut off, and verifying a stale fence rewards an accident.
+
+* SPARSE REWARD, NO LENGTH IN THE LOSS. Episode reward = turn_discount ** solved_turn
+ (0 if never solved) plus an optional DAPO soft-overlong term on TOTAL generated tokens,
+ weighted by `overlong_weight`. The penalty lives in reward composition (reusing
+ `align.rl.rewards.soft_overlong_punishment`), never in the loss.
+
+* CREDIT ASSIGNMENT. advantage_mode="episode": every generated token gets the Dr.GRPO
+ group-centered episode reward (the repo's `compute_group_advantages`, reused).
+ advantage_mode="per_turn": turn t gets G_t - mean_group(G_t) with
+ G_t = sum_{s>=t} credit_gamma^(s-t) * u_s, u_s the turn's own outcome reward. With
+ credit_gamma=0 a failed first attempt gets no credit for the repair that followed (the
+ failure mode of episode-level sharing: rewarding the bad first draft); with gamma>0 it
+ shares in it, because its error context is what made the repair possible. The baseline for
+ turn t averages only episodes that HAVE a turn t.
+
+* DEAD VERIFIER != ZERO REWARD. `LeanInfraError` from the verifier propagates unchanged. A
+ verifier that is dying SOFTLY (every proof comes back `crash`/`protocol`/`memory`/
+ `output_too_large` instead of raising) would otherwise be turned by this env into
+ `rejected:crash`, reward 0, a batch of zeros that reads as "the model got worse".
+ `LeanReward.accuracy_batch` refuses that; the env bypasses `LeanReward`, so it carries the
+ same guard: once at least `min_infra_batch` verifications have run in a `run_group`, a
+ cumulative infra-failure share above `max_infra_fraction` raises `LeanInfraError`.
+
+* ENGINE FINISH REASONS. The P3 engine reports `eos|stop_token|stop_string|max_new_tokens|
+ max_seq_len`, the lean_gen contract reports `stop|length`. Only `length` used to count as
+ truncated, so an engine-capped generation (`max_new_tokens`/`max_seq_len`) was VERIFIED as
+ if it were a finished answer. Both vocabularies are accepted; an unknown reason raises
+ (a new engine reason must be classified by a human, not guessed).
+
+* CONTEXT BUDGET. Compiler feedback is unbounded and each repair turn re-sends the whole
+ history. With `max_prompt_tokens` set, a repair prompt over the budget ends the episode
+ (`context_overflow`, reward 0 for the unsolved episode) instead of being handed to an
+ engine that would refuse it (or silently truncate the prompt from the left).
+
+* EPISODE KEY COLLISIONS. `generate` adapters key seeds / KV state by `episode_key`; two
+ specs with the same (problem_id, sample_idx, statement) would share one stream. Refused.
+
+Sandbox: `LeanMultiTurnConfig.sandbox=True` is a pass-through request for the
+`align/verify/lean/sandbox.py` REPL. The pool is not wired to it yet, so the env raises
+`SandboxNotWired` unless the pool itself advertises a truthy `sandbox` attribute.
+
+Error positions: the Goedel format needs REPL message dicts (`pos`, `endPos`, `data`)
+indexing `compile_view(code)`. `StrictResult.diagnostics["repl_errors"]` is the contract for
+those; `StrictVerifier` does not populate it yet (it keeps only 3 truncated message strings).
+Absent positions the env degrades per `unpositioned_feedback` ("message_only" renders the
+messages with no code excerpt; "raise" refuses) instead of inventing an `` location.
+
+stdlib-only (+ align.rl.rewards / align.rl.advantages); no torch tensors are built here.
+"""
+
+from __future__ import annotations
+
+import hashlib
+import importlib.util
+import json
+from dataclasses import asdict, dataclass, field
+from pathlib import Path
+from typing import Any, Callable, Optional, Sequence
+
+from align.rl.advantages import compute_group_advantages
+from align.rl.rewards import soft_overlong_punishment
+
+_REPO = Path(__file__).resolve().parents[3]
+
+_MODS: dict[str, Any] = {}
+
+
+def _load_by_path(key: str, rel: str):
+ if key not in _MODS:
+ spec = importlib.util.spec_from_file_location(key, _REPO / rel)
+ mod = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(mod)
+ _MODS[key] = mod
+ return _MODS[key]
+
+
+def _lean_prompts():
+ return _load_by_path("align_envs_lean_prompts", "extras/vllm_rollout/lean_prompts.py")
+
+
+def _p2_common():
+ return _load_by_path("align_envs_p2_common", "scripts/p2/p2_common.py")
+
+
+#: finish reasons that mean "the generator ended on its own" / "the generator was cut off"
+STOP_REASONS = frozenset({"stop", "eos", "stop_token", "stop_string"})
+TRUNCATED_REASONS = frozenset({"length", "max_new_tokens", "max_seq_len"})
+
+
+class SandboxNotWired(RuntimeError):
+ """sandbox=True was requested but the verifier pool has no sandboxed REPL."""
+
+
+class PrefixBreak(RuntimeError):
+ """The chat template did not render turn k's prompt as an extension of turn k-1's
+ prompt + completion, so one concatenated token row would not be what the policy saw."""
+
+
+@dataclass(frozen=True)
+class Problem:
+ problem_id: str
+ formal_statement: str
+
+
+@dataclass(frozen=True)
+class EpisodeSpec:
+ problem: Problem
+ sample_idx: int = 0
+
+
+@dataclass(frozen=True)
+class GenRequest:
+ episode_key: str
+ turn: int
+ prompt_ids: tuple
+ prompt_text: str
+
+
+@dataclass(frozen=True)
+class Generation:
+ """`token_ids`: exactly what the engine sampled (incl. a trailing EOS if sampled).
+ `text`: the decoded completion WITHOUT the EOS string. `logprobs`: per generated token."""
+
+ token_ids: tuple
+ text: str
+ logprobs: Optional[tuple] = None
+ finish_reason: str = "stop"
+
+
+@dataclass(frozen=True)
+class LeanMultiTurnConfig:
+ max_turns: int = 2
+ layout: str = "per_turn" # "per_turn" | "concatenated"
+ turn_discount: float = 1.0
+ overlong_max_len: Optional[int] = None
+ overlong_cache_len: Optional[int] = None
+ overlong_weight: float = 1.0
+ advantage_mode: str = "episode" # "episode" | "per_turn"
+ credit_gamma: float = 0.0
+ retry_reasons: tuple = ("compile_error",)
+ reject_truncated: bool = True
+ #: ABLATION ONLY: accept a proof whose displayed theorem differs from the statement.
+ allow_statement_rewrite: bool = False
+ error_thres: bool = True
+ unpositioned_feedback: str = "message_only" # "message_only" | "raise"
+ sandbox: bool = False
+ #: ceiling on the repair prompt length in tokens (None = unbounded, the old behaviour)
+ max_prompt_tokens: Optional[int] = None
+ #: soft-infra-failure guard (see module docstring); same defaults as LeanReward
+ max_infra_fraction: float = 0.5
+ min_infra_batch: int = 8
+ eos_token_id: Optional[int] = None
+ eos_text: str = "<|im_end|>"
+
+ def __post_init__(self) -> None:
+ if self.max_turns < 1:
+ raise ValueError(f"max_turns must be >= 1, got {self.max_turns}")
+ if self.layout not in ("per_turn", "concatenated"):
+ raise ValueError(f"layout must be 'per_turn' or 'concatenated', got {self.layout!r}")
+ if not 0.0 < self.turn_discount <= 1.0:
+ raise ValueError(f"turn_discount must be in (0, 1], got {self.turn_discount}")
+ if self.advantage_mode not in ("episode", "per_turn"):
+ raise ValueError(f"advantage_mode must be 'episode' or 'per_turn', got {self.advantage_mode!r}")
+ if not 0.0 <= self.credit_gamma <= 1.0:
+ raise ValueError(f"credit_gamma must be in [0, 1], got {self.credit_gamma}")
+ if self.max_prompt_tokens is not None and self.max_prompt_tokens < 1:
+ raise ValueError(f"max_prompt_tokens must be None or >= 1, got {self.max_prompt_tokens}")
+ if not 0.0 <= self.max_infra_fraction <= 1.0:
+ raise ValueError(f"max_infra_fraction must be in [0, 1], got {self.max_infra_fraction}")
+ if self.min_infra_batch < 1:
+ raise ValueError(f"min_infra_batch must be >= 1, got {self.min_infra_batch}")
+ if self.unpositioned_feedback not in ("message_only", "raise"):
+ raise ValueError(f"unpositioned_feedback must be 'message_only' or 'raise'")
+ # same construction-time refusals as CompositeReward: a half-armed penalty is silently off
+ if self.overlong_max_len is None and self.overlong_cache_len is not None:
+ raise ValueError("overlong_cache_len given without overlong_max_len: penalty would be silently OFF")
+ if self.overlong_max_len is not None:
+ if self.overlong_cache_len is None or not 0 < self.overlong_cache_len < self.overlong_max_len:
+ raise ValueError("need 0 < overlong_cache_len < overlong_max_len")
+
+
+@dataclass
+class TurnRecord:
+ turn: int
+ prompt_text: str
+ prompt_ids: list
+ completion_ids: list
+ completion_text: str
+ finish_reason: str
+ lean_code: Optional[str] = None
+ code_error: Optional[str] = None
+ displayed_statement_ok: Optional[bool] = None
+ reasons: tuple = ()
+ verified: bool = False
+ credited: bool = False
+ feedback_text: Optional[str] = None
+ feedback_positioned: Optional[bool] = None
+
+
+@dataclass
+class TrainingRow:
+ input_ids: list
+ #: 1 ONLY on tokens the generator returned (input space, like RolloutBatch.response_mask)
+ response_mask: list
+ #: turn index of each generated token, -1 elsewhere
+ turn_ids: list
+ #: per-token engine logprobs in input space (0.0 where unlabeled); None if not supplied
+ logprobs: Optional[list] = None
+ #: per-token advantage (0.0 where unlabeled); filled by `assign_advantages`
+ advantages: Optional[list] = None
+
+
+@dataclass
+class EpisodeRecord:
+ episode_key: str
+ problem_id: str
+ sample_idx: int
+ turns: list
+ rows: list
+ terminal: str = ""
+ solved_turn: Optional[int] = None
+ reward: dict = field(default_factory=dict)
+ turn_rewards: list = field(default_factory=list) # u_t, the per-turn outcome rewards
+ n_generated: int = 0
+
+ def to_json(self) -> str:
+ return json.dumps(asdict(self), sort_keys=True, ensure_ascii=False)
+
+
+# ------------------------------------------------------------------------- reward composition
+
+
+def compose_episode_reward(cfg: LeanMultiTurnConfig, solved_turn: Optional[int], n_turns: int, n_generated: int):
+ """(terms, u): itemised episode reward terms and the per-turn outcome rewards u_t.
+ The overlong term is attached to the LAST turn's u (the turn that ended the episode)."""
+ u = [0.0] * n_turns
+ acc = 0.0
+ if solved_turn is not None:
+ acc = cfg.turn_discount ** solved_turn
+ u[solved_turn] = acc
+ ov = 0.0
+ if cfg.overlong_max_len is not None:
+ ov = cfg.overlong_weight * soft_overlong_punishment(n_generated, cfg.overlong_max_len, cfg.overlong_cache_len)
+ u[-1] += ov
+ terms = {
+ "reward/accuracy": 1.0 if solved_turn is not None else 0.0,
+ "reward/turn_discount": acc,
+ "reward/overlong": ov,
+ "reward/total": acc + ov,
+ }
+ return terms, u
+
+
+def assign_advantages(episodes: Sequence[EpisodeRecord], cfg: LeanMultiTurnConfig) -> None:
+ """Fill `row.advantages` in place. Episodes of one problem_id form a group (input order
+ preserved). Groups must be equal-sized in "episode" mode (the shared helper's layout)."""
+ groups: dict[str, list] = {}
+ for ep in episodes:
+ groups.setdefault(ep.problem_id, []).append(ep)
+ for eps in groups.values():
+ n_turn_max = max(len(e.turns) for e in eps)
+ if cfg.advantage_mode == "episode":
+ import numpy as np
+
+ adv = compute_group_advantages(np.array([e.reward["reward/total"] for e in eps]), len(eps), "centered")
+ per_turn = [[float(a)] * n_turn_max for a in adv]
+ else:
+ G = []
+ for e in eps:
+ g, acc = [0.0] * len(e.turns), 0.0
+ for t in range(len(e.turns) - 1, -1, -1):
+ acc = e.turn_rewards[t] + cfg.credit_gamma * acc
+ g[t] = acc
+ G.append(g)
+ per_turn = [[0.0] * n_turn_max for _ in eps]
+ for t in range(n_turn_max):
+ have = [i for i, e in enumerate(eps) if t < len(e.turns)]
+ base = sum(G[i][t] for i in have) / len(have)
+ for i in have:
+ per_turn[i][t] = G[i][t] - base
+ for e, adv_t in zip(eps, per_turn):
+ for row in e.rows:
+ row.advantages = [adv_t[tid] if tid >= 0 else 0.0 for tid in row.turn_ids]
+
+
+# ------------------------------------------------------------------------- the environment
+
+
+class _Ep:
+ def __init__(self, spec: EpisodeSpec, key: str):
+ self.spec, self.key = spec, key
+ self.turns: list[TurnRecord] = []
+ self.messages: list[dict] = []
+ self.prompt_text = ""
+ self.prompt_ids: list = []
+ self.ctx_ids: list = [] # concatenated layout: running context
+ self.gens: list[Generation] = []
+ self.done = False
+ self.terminal = ""
+ self.solved_turn: Optional[int] = None
+ self.pending: Optional[tuple] = None
+
+
+class LeanMultiTurnEnv:
+ """`verifier`: anything with `verify(statement, code) -> StrictResult` (and optionally
+ `verify_batch(items)`), e.g. `StrictVerifier`. `tokenizer`: `apply_chat_template(messages,
+ tokenize=False, add_generation_prompt=True)`. `encode`: text -> ids."""
+
+ def __init__(self, verifier, tokenizer, encode: Callable[[str], list], cfg: LeanMultiTurnConfig = LeanMultiTurnConfig()):
+ if cfg.sandbox and not getattr(getattr(verifier, "pool", verifier), "sandbox", None):
+ raise SandboxNotWired(
+ "sandbox=True requested but the verifier pool exposes no sandboxed REPL "
+ "(align/verify/lean/sandbox.py is not wired into repl_pool yet)"
+ )
+ self.verifier, self.tok, self.encode, self.cfg = verifier, tokenizer, encode, cfg
+
+ # ---- public
+ def run_episode(self, spec: EpisodeSpec, generate: Callable[[list], list]) -> EpisodeRecord:
+ return self.run_group([spec], generate)[0]
+
+ def run_group(self, specs: Sequence[EpisodeSpec], generate: Callable[[list], list]) -> list:
+ """Lockstep over episodes: each round, ONE `generate(list[GenRequest]) -> list[Generation]`
+ call for all active episodes, then one batched verification. Output order = input order."""
+ eps = [_Ep(s, self._key(s)) for s in specs]
+ if len({e.key for e in eps}) != len(eps):
+ dup = sorted({e.key for e in eps if sum(x.key == e.key for x in eps) > 1})
+ raise ValueError(f"duplicate episode keys {dup}: specs must differ in problem_id/sample_idx/statement")
+ n_ver = n_infra = 0
+ for ep in eps:
+ self._start(ep)
+ active = list(eps)
+ while active:
+ reqs = [GenRequest(e.key, len(e.turns), tuple(e.prompt_ids), e.prompt_text) for e in active]
+ gens = generate(reqs)
+ if len(gens) != len(active):
+ raise ValueError(f"generate returned {len(gens)} generations for {len(active)} requests")
+ for e, g in zip(active, gens):
+ self._absorb(e, g)
+ todo = [e for e in active if not e.done]
+ if todo:
+ items = [e.pending for e in todo]
+ vb = getattr(self.verifier, "verify_batch", None)
+ results = vb(items) if vb is not None else [self.verifier.verify(s, c) for s, c in items]
+ if len(results) != len(todo):
+ raise ValueError(f"verifier returned {len(results)} results for {len(todo)} items")
+ n_ver += len(results)
+ n_infra += sum(1 for r in results if getattr(r, "infra_failure", False))
+ self._check_infra(n_infra, n_ver)
+ for e, r in zip(todo, results):
+ self._judge(e, r)
+ active = [e for e in active if not e.done]
+ return [self._finish(e) for e in eps]
+
+ # ---- internals
+ def _check_infra(self, n_infra: int, n_ver: int) -> None:
+ cfg = self.cfg
+ if n_ver >= cfg.min_infra_batch and n_infra / n_ver > cfg.max_infra_fraction:
+ from align.verify.lean.strict import LeanInfraError
+
+ raise LeanInfraError(
+ f"{n_infra}/{n_ver} verifications died for infrastructure reasons: either the pool is "
+ f"dead or the policy is emitting REPL-killing proofs; refusing to emit episodes of "
+ f"reward 0 that read as 'model got worse'"
+ )
+
+ def _key(self, s: EpisodeSpec) -> str:
+ h = hashlib.sha256(f"{s.problem.problem_id}\0{s.sample_idx}\0{s.problem.formal_statement}".encode()).hexdigest()
+ return h[:16]
+
+ def _render(self, messages: list) -> str:
+ return self.tok.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
+
+ def _start(self, ep: _Ep) -> None:
+ text, msgs = _lean_prompts().initial_prompt(ep.spec.problem.formal_statement, self.tok)
+ ep.messages, ep.prompt_text = list(msgs), text
+ ep.prompt_ids = list(self.encode(text))
+ ep.ctx_ids = list(ep.prompt_ids)
+
+ def _absorb(self, ep: _Ep, g: Generation) -> None:
+ cfg, lp = self.cfg, _lean_prompts()
+ if g.logprobs is not None and len(g.logprobs) != len(g.token_ids):
+ raise ValueError(f"{len(g.logprobs)} logprobs for {len(g.token_ids)} generated tokens")
+ t = TurnRecord(len(ep.turns), ep.prompt_text, list(ep.prompt_ids), list(g.token_ids), g.text, g.finish_reason)
+ ep.turns.append(t)
+ ep.gens.append(g)
+ stmt = ep.spec.problem.formal_statement
+ if g.finish_reason not in STOP_REASONS and g.finish_reason not in TRUNCATED_REASONS:
+ raise ValueError(
+ f"unknown finish_reason {g.finish_reason!r}; classify it in STOP_REASONS or TRUNCATED_REASONS"
+ )
+ if cfg.reject_truncated and g.finish_reason in TRUNCATED_REASONS:
+ return self._end(ep, "truncated")
+ code, err = lp.lean_code_for_record(stmt, g.text)
+ t.lean_code, t.code_error = code, err
+ if code is None:
+ return self._end(ep, f"no_code:{err}")
+ if not cfg.allow_statement_rewrite:
+ t.displayed_statement_ok = bool(_p2_common().displayed_statement_matches(stmt, g.text))
+ if not t.displayed_statement_ok:
+ return self._end(ep, "statement_rewrite")
+ ep.pending = (stmt, code)
+
+ def _judge(self, ep: _Ep, r) -> None:
+ cfg, t = self.cfg, ep.turns[-1]
+ t.reasons, t.verified = tuple(r.reasons), bool(r.accepted)
+ if r.accepted:
+ t.credited, ep.solved_turn = True, t.turn
+ return self._end(ep, "verified")
+ if t.turn + 1 >= cfg.max_turns:
+ return self._end(ep, "max_turns")
+ if not all(x in cfg.retry_reasons for x in t.reasons):
+ return self._end(ep, f"rejected:{t.reasons[0] if t.reasons else 'unknown'}")
+ fb = self._feedback(ep, r)
+ if fb is None:
+ return self._end(ep, "no_feedback")
+ err_str, positioned = fb
+ t.feedback_text, t.feedback_positioned = err_str, positioned
+ lp = _lean_prompts()
+ text, msgs = lp.DeepSeekCoTHandler().generate_correction_prompt(
+ lp.goedel_statement(ep.spec.problem.formal_statement), ep.messages, t.completion_text,
+ err_str, self.tok, t.turn + 1,
+ )
+ if cfg.max_prompt_tokens is not None and len(self.encode(text)) > cfg.max_prompt_tokens:
+ return self._end(ep, "context_overflow")
+ ep.messages = msgs
+ prev_end = ep.prompt_text + t.completion_text
+ if cfg.layout == "concatenated":
+ if not text.startswith(prev_end):
+ raise PrefixBreak(
+ f"turn {t.turn + 1} prompt does not extend turn {t.turn} prompt + completion "
+ f"(template rewrites earlier assistant content, e.g. Qwen3 stripping); "
+ f"use layout='per_turn'"
+ )
+ delta = text[len(prev_end):]
+ gen = ep.gens[-1]
+ if (cfg.eos_token_id is not None and gen.token_ids and gen.token_ids[-1] == cfg.eos_token_id
+ and delta.startswith(cfg.eos_text)):
+ delta = delta[len(cfg.eos_text):] # the sampled EOS is already in the context
+ ep.ctx_ids = ep.ctx_ids + list(gen.token_ids) + list(self.encode(delta))
+ ep.prompt_ids = list(ep.ctx_ids)
+ else:
+ ep.prompt_ids = list(self.encode(text))
+ ep.prompt_text = text
+
+ def _feedback(self, ep: _Ep, r) -> Optional[tuple]:
+ lp = _lean_prompts()
+ diag = r.diagnostics or {}
+ t = ep.turns[-1]
+ errs = diag.get("repl_errors")
+ if errs and all(isinstance(e, dict) and e.get("pos") and "data" in e for e in errs):
+ view = diag.get("error_code_view") or lp.compile_view(t.lean_code)
+ return lp.get_error_str(view, errs, self.cfg.error_thres), True
+ msgs = diag.get("errors")
+ if not msgs and not errs:
+ return None
+ if self.cfg.unpositioned_feedback == "raise":
+ raise ValueError("verifier result has no positioned REPL errors (diagnostics['repl_errors'])")
+ raw = [e["data"] if isinstance(e, dict) else str(e) for e in (errs or msgs)]
+ return "".join(f"\nError {i + 1}:\n\nError Message: {m}\n" for i, m in enumerate(raw)), False
+
+ def _end(self, ep: _Ep, why: str) -> None:
+ ep.done, ep.terminal = True, why
+
+ def _finish(self, ep: _Ep) -> EpisodeRecord:
+ n_gen = sum(len(g.token_ids) for g in ep.gens)
+ terms, u = compose_episode_reward(self.cfg, ep.solved_turn, len(ep.turns), n_gen)
+ rows = self._rows(ep)
+ s = ep.spec
+ return EpisodeRecord(ep.key, s.problem.problem_id, s.sample_idx, ep.turns, rows, ep.terminal,
+ ep.solved_turn, terms, u, n_gen)
+
+ def _rows(self, ep: _Ep) -> list:
+ if self.cfg.layout == "per_turn":
+ out = []
+ for t, g in zip(ep.turns, ep.gens):
+ n = len(g.token_ids)
+ out.append(TrainingRow(
+ list(t.prompt_ids) + list(g.token_ids),
+ [0] * len(t.prompt_ids) + [1] * n,
+ [-1] * len(t.prompt_ids) + [t.turn] * n,
+ ([0.0] * len(t.prompt_ids) + list(g.logprobs)) if g.logprobs is not None else None,
+ ))
+ return out
+ ids, mask, tids, lp, have_lp = [], [], [], [], all(g.logprobs is not None for g in ep.gens)
+ for t, g in zip(ep.turns, ep.gens):
+ n_ctx = len(t.prompt_ids) - len(ids) # new context tokens (prompt / feedback glue)
+ ids += list(t.prompt_ids[len(ids):])
+ mask += [0] * n_ctx
+ tids += [-1] * n_ctx
+ lp += [0.0] * n_ctx
+ ids += list(g.token_ids)
+ mask += [1] * len(g.token_ids)
+ tids += [t.turn] * len(g.token_ids)
+ if have_lp:
+ lp += list(g.logprobs)
+ else:
+ lp += [0.0] * len(g.token_ids)
+ return [TrainingRow(ids, mask, tids, lp if have_lp else None)]
diff --git a/code/align/rl/envs/subgoal_repair.py b/code/align/rl/envs/subgoal_repair.py
new file mode 100644
index 0000000000000000000000000000000000000000..f4c4a414c8fc99d90b5cd71359250da488104436
--- /dev/null
+++ b/code/align/rl/envs/subgoal_repair.py
@@ -0,0 +1,694 @@
+"""APOLLO-style subgoal extraction and reassembly for failed Lean 4 proofs (K5 agentic arm, docs/K5_LEVERS.md).
+
+Pure Python (stdlib only) plus read-only Lean calls through the existing `align.verify.lean` API. Nothing here
+changes the verifier: the reassembled proof is only a CANDIDATE; the strict verifier (`StrictVerifier`) is the sole
+judge of acceptance, against the ORIGINAL statement. So a lemma that drifted from the goal it was cut from (the
+pretty-printer is not a round trip) can cost a wasted attempt, never a false accept.
+
+Pipeline (APOLLO, arXiv 2505.05758, section 3; DeepSeek-Prover-V2, arXiv 2504.21801, section 2.1: "decompose, prove the
+subgoals separately, recombine"):
+
+ 1. `locate_targets` map every Lean error of a failed proof to the smallest repairable unit that contains it:
+ a failing `have name : T := ` (kind "have", innermost wins), a failing main-line tactic
+ (kind "tactic"), or "the proof ended with goals left" (kind "tail"). Errors on a `have`
+ HEADER (the statement itself does not elaborate) are not repairable and are reported.
+ 2. `build_skeleton` replace each target's text by `sorry` ("sorrifier"). Targets that contain another target are
+ dropped (the inner failure is the finer cut; the outer one is picked up by the next round).
+ 3. `plan_subgoals` run the skeleton in Lean once: its `sorries` carry the exact goal state (hypotheses and
+ goal) at each gap. Each state becomes a standalone `theorem k5_sg_ (hyps) : goal`,
+ which is checked to elaborate. These are ordinary problems for the prover.
+ 4. `reassemble` splice a prover-supplied tactic body back into the skeleton in place of the `sorry`
+ (inline: the lemma's hypotheses carry the context's names; inaccessible ones are
+ re-introduced with `rename_i`). Unproven gaps get their original text back.
+
+Coordinates. Lean errors index `compile_view(code)` (header lines removed, `p2_common.compile_view`), not the full
+code. `view_line_map` translates; all text surgery happens on the FULL code so the result can be verified as is.
+"""
+
+from __future__ import annotations
+
+import hashlib
+import re
+from dataclasses import dataclass, field
+from typing import Any, Callable, Optional
+
+# --------------------------------------------------------------------------- lexical masking
+
+_OPEN = "([{⟨"
+_CLOSE = ")]}⟩"
+
+
+def mask(text: str) -> str:
+ """`text` with comments and string literals blanked (newlines kept), so indentation and bracket scans cannot be
+ fooled by prose. Same length as the input: offsets and (line, column) positions are preserved."""
+ out = list(text)
+ i, n = 0, len(text)
+ while i < n:
+ c = text[i]
+ if c == "-" and text.startswith("--", i):
+ j = text.find("\n", i)
+ j = n if j < 0 else j
+ for k in range(i, j):
+ out[k] = " "
+ i = j
+ elif c == "/" and text.startswith("/-", i):
+ depth, j = 1, i + 2
+ while j < n and depth:
+ if text.startswith("/-", j):
+ depth += 1
+ j += 2
+ elif text.startswith("-/", j):
+ depth -= 1
+ j += 2
+ else:
+ j += 1
+ for k in range(i, min(j, n)):
+ if out[k] != "\n":
+ out[k] = " "
+ i = j
+ elif c == '"':
+ j = i + 1
+ while j < n and text[j] != '"':
+ j += 2 if text[j] == "\\" else 1
+ for k in range(i + 1, min(j, n)):
+ if out[k] != "\n":
+ out[k] = " "
+ i = j + 1
+ else:
+ i += 1
+ return "".join(out)
+
+
+def strip_comments(text: str) -> str:
+ """`text` with block and line comments deleted (used on headers only, which hold no string literals)."""
+ text = re.sub(r"/-.*?-/", "", text, flags=re.S)
+ return re.sub(r"--[^\n]*", "", text)
+
+
+def view_line_map(code: str) -> list[int]:
+ """`map[i]` = index in `code.split("\\n")` of line `i` of `compile_view(code)` (the REPL's coordinate system).
+ `compile_view` (the reference's `handle`) drops every line whose stripped text starts with import / set_option /
+ open, nothing else."""
+ out = []
+ for i, line in enumerate(code.split("\n")):
+ s = line.strip()
+ if s.startswith("import") or s.startswith("set_option") or s.startswith("open"):
+ continue
+ out.append(i)
+ return out
+
+
+# --------------------------------------------------------------------------- proof structure
+
+
+@dataclass
+class _Lines:
+ """Masked, bullet-normalised working copy of the code."""
+
+ raw: list[str]
+ m: list[str] # masked lines (comments blanked); bullets `· ` replaced by two spaces
+ indent: list[int]
+ blank: list[bool]
+ depth_before: list[int] # open-bracket depth at the start of each line
+ cont: list[bool] # the line continues the previous statement at the same indent
+ ends: list[int] = field(default_factory=list) # extent end line of a statement starting here (-1: not a start)
+
+
+_BULLET = re.compile(r"^(\s*)[·.](?=\s)")
+
+
+def _analyse(code: str) -> _Lines:
+ raw = code.split("\n")
+ mk = mask(code).split("\n")
+ m = []
+ for ln in mk:
+ mm = _BULLET.match(ln)
+ if mm:
+ ln = mm.group(1) + " " + ln[len(mm.group(1)) + 1:]
+ m.append(ln)
+ indent = [len(x) - len(x.lstrip(" ")) for x in m]
+ blank = [not x.strip() for x in m]
+ depth, db = 0, []
+ for x in m:
+ db.append(depth)
+ for ch in x:
+ if ch in _OPEN:
+ depth += 1
+ elif ch in _CLOSE:
+ depth = max(0, depth - 1)
+ cont = [False] * len(m)
+ prev = None
+ for i, x in enumerate(m):
+ if blank[i]:
+ continue
+ s = x.strip()
+ if prev is not None and (db[i] > 0 or s.startswith("<;>") or m[prev].rstrip().endswith("<;>")):
+ cont[i] = True
+ prev = i
+ L = _Lines(raw, m, indent, blank, db, cont)
+ L.ends = [-1] * len(m)
+ for i in range(len(m)):
+ if blank[i] or cont[i]:
+ continue
+ e = i
+ for j in range(i + 1, len(m)):
+ if blank[j]:
+ continue
+ if indent[j] > indent[i] or (indent[j] == indent[i] and cont[j]):
+ e = j
+ else:
+ break
+ L.ends[i] = e
+ return L
+
+
+_THEOREM = re.compile(r"^\s*(?:theorem|lemma)\b")
+
+
+@dataclass(frozen=True)
+class Have:
+ start: int # first line
+ end: int # last line of the unit (inclusive)
+ name: str
+ assign_line: int # line holding the depth-0 `:=`
+ assign_col: int # column of that `:=`
+
+
+def _first_assign(L: _Lines, s: int, e: int) -> Optional[tuple[int, int]]:
+ depth = 0
+ for i in range(s, e + 1):
+ x = L.m[i]
+ for c, ch in enumerate(x):
+ if ch in _OPEN:
+ depth += 1
+ elif ch in _CLOSE:
+ depth = max(0, depth - 1)
+ elif ch == ":" and depth == 0 and x.startswith(":=", c):
+ return i, c
+ return None
+
+
+def find_haves(code: str, L: Optional[_Lines] = None) -> list[Have]:
+ """Every `have : T := ` unit (any nesting depth) that has a `:=`. Side-goal haves (no `:=`, closed by
+ following bullets) and `have ... from` are not units this module can cut."""
+ L = L or _analyse(code)
+ out = []
+ for i, x in enumerate(L.m):
+ if L.ends[i] < 0:
+ continue
+ s = x.strip()
+ if not (s.startswith("have ") or s.startswith("have:") or s.startswith("haveI ")):
+ continue
+ a = _first_assign(L, i, L.ends[i])
+ if a is None:
+ continue
+ toks = s.split()
+ name = toks[1] if len(toks) > 1 and toks[1] not in (":", ":=") and not toks[1].startswith(("⟨", "(", ":")) else "_"
+ out.append(Have(i, L.ends[i], name, a[0], a[1]))
+ return out
+
+
+def theorem_by_line(L: _Lines) -> Optional[tuple[int, int]]:
+ """(line, column just after `by`) of the main theorem's `:= by`; None when absent."""
+ for i, x in enumerate(L.m):
+ if _THEOREM.match(x):
+ a = _first_assign(L, i, min(len(L.m) - 1, i + 12))
+ if a is None:
+ return None
+ j = L.m[a[0]].find("by", a[1])
+ return (a[0], j + 2) if j >= 0 else None
+ return None
+
+
+# --------------------------------------------------------------------------- targets
+
+
+@dataclass
+class Target:
+ kind: str # have | tactic | tail
+ start: tuple[int, int] # (line, col) of the text to replace, 0-based, full-code coordinates
+ end: tuple[int, int] # exclusive end (line, col); for "tail" start == end (insertion point)
+ original: str # the replaced text, restorable verbatim
+ indent: int # indentation of the statement the gap lives in
+ name: str = ""
+ errors: list = field(default_factory=list)
+
+
+def _cut(raw: list[str], a: tuple[int, int], b: tuple[int, int]) -> str:
+ if a[0] == b[0]:
+ return raw[a[0]][a[1]:b[1]]
+ parts = [raw[a[0]][a[1]:]] + raw[a[0] + 1:b[0]] + [raw[b[0]][:b[1]]]
+ return "\n".join(parts)
+
+
+def tail_target(L: _Lines, tb: tuple[int, int]) -> Target:
+ """Insertion point for a `sorry` after the proof's last line: the proof ended with goals left."""
+ body_lines = [i for i in range(tb[0] + 1, len(L.m)) if not L.blank[i]]
+ ind = min((L.indent[i] for i in body_lines), default=2)
+ last = max(body_lines, default=tb[0])
+ pos = (last, len(L.raw[last]))
+ return Target("tail", pos, pos, "", ind)
+
+
+def locate_targets(code: str, errors: list[dict], repl_code: Optional[str] = None) -> tuple[list[Target], list[dict]]:
+ """Map Lean errors (positions in `repl_code`, default `compile_view(code)`) to repairable targets.
+
+ Returns (targets sorted by position, unrepairable errors with a `why`). Only severity-error messages are used.
+ Rules, in order, for an error at line l, column c:
+ * inside the rhs of a `have` -> kind "have" (innermost such have); target = text after its `:=`
+ * on the theorem's `:= by` line, or the proof ends with goals left -> kind "tail"
+ * on a `have` header (before its `:=`) -> unrepairable "have_header" (the statement itself is wrong)
+ * otherwise -> kind "tactic": the statement containing the error, as one unit
+ Targets that contain another target are dropped; the finer cut wins."""
+ L = _analyse(code)
+ vmap = view_line_map(code)
+ if repl_code is not None and len(repl_code.split("\n")) != len(vmap):
+ raise ValueError("repl_code does not match compile_view(code): the error positions would index other lines")
+ haves = find_haves(code, L)
+ tb = theorem_by_line(L)
+ found: dict[tuple, Target] = {}
+ bad: list[dict] = []
+ for e in errors:
+ if e.get("severity", "error") != "error" or not e.get("pos"):
+ continue
+ vl, c = int(e["pos"]["line"]) - 1, int(e["pos"]["column"])
+ if not 0 <= vl < len(vmap):
+ bad.append({**e, "why": "position_outside_code"})
+ continue
+ l = vmap[vl]
+ inside = [h for h in haves if h.start <= l <= h.end and (l, c) > (h.assign_line, h.assign_col + 1)]
+ if inside:
+ h = max(inside, key=lambda x: x.start)
+ rhs_start = (h.assign_line, h.assign_col + 2)
+ end = (h.end, len(L.raw[h.end]))
+ key = ("have", rhs_start)
+ t = found.setdefault(key, Target("have", rhs_start, end, _cut(L.raw, rhs_start, end), L.indent[h.start], h.name))
+ t.errors.append(e)
+ continue
+ if tb is not None and l == tb[0]:
+ t = found.setdefault(("tail",), tail_target(L, tb))
+ t.errors.append(e)
+ continue
+ if any(h.start <= l <= h.assign_line and (l, c) <= (h.assign_line, h.assign_col + 1) for h in haves):
+ bad.append({**e, "why": "have_header"})
+ continue
+ starts = [i for i in range(l + 1) if L.ends[i] >= l and not L.blank[i]]
+ if not starts or (tb is not None and l < tb[0]):
+ bad.append({**e, "why": "no_statement"})
+ continue
+ s = max(starts)
+ end = (L.ends[s], len(L.raw[L.ends[s]]))
+ st = (s, L.indent[s])
+ t = found.setdefault(("tactic", st), Target("tactic", st, end, _cut(L.raw, st, end), L.indent[s]))
+ t.errors.append(e)
+ ts = sorted(found.values(), key=lambda t: (t.start, t.end))
+ keep = []
+ for t in ts:
+ if t.kind != "tail" and any(o is not t and o.kind != "tail" and t.start <= o.start and o.end <= t.end and (t.start, t.end) != (o.start, o.end)
+ for o in ts):
+ continue # contains a finer target
+ keep.append(t)
+ return keep, bad
+
+
+# --------------------------------------------------------------------------- skeleton
+
+
+GAP = "⟪K5GAP{}⟫"
+
+
+def build_skeleton(code: str, targets: list[Target]) -> tuple[str, list[tuple[int, int]]]:
+ """(skeleton, [(line, col) of the i-th target's `sorry` in the skeleton]). Order of positions == order of
+ `targets` as given (they are applied bottom-up on a copy)."""
+ raw = code.split("\n")
+ order = sorted(range(len(targets)), key=lambda i: (targets[i].start, targets[i].end), reverse=True)
+ for i in order:
+ t = targets[i]
+ if t.kind == "have":
+ ins = " by " + GAP.format(i)
+ elif t.kind == "tactic":
+ ins = GAP.format(i)
+ else: # tail: a new line after the last proof line
+ raw.insert(t.start[0] + 1, " " * t.indent + GAP.format(i))
+ continue
+ a, b = t.start, t.end
+ raw[a[0]:b[0] + 1] = [raw[a[0]][:a[1]] + ins + raw[b[0]][b[1]:]]
+ text = "\n".join(raw)
+ pos: dict[int, tuple[int, int]] = {}
+ out_lines = []
+ for ln_no, ln in enumerate(text.split("\n")):
+ while True:
+ m = re.search(r"⟪K5GAP(\d+)⟫", ln)
+ if not m:
+ break
+ pos[int(m.group(1))] = (ln_no, m.start())
+ ln = ln[:m.start()] + "sorry" + ln[m.end():]
+ out_lines.append(ln)
+ return "\n".join(out_lines), [pos[i] for i in range(len(targets))]
+
+
+# --------------------------------------------------------------------------- goal state -> lemma
+
+
+_INACC = re.compile(r"✝[⁰¹²³⁴⁵⁶⁷⁸⁹]*$")
+
+
+@dataclass
+class GoalState:
+ case: Optional[str]
+ hyps: list[tuple[list[str], str]] # (names, type)
+ goal: str
+ has_let: bool = False
+
+
+def parse_goal_state(text: str) -> GoalState:
+ """Parse a Lean goal display (`case h` / `x y : T` / ... / `⊢ goal`). Wrapped lines (indented) are joined."""
+ lines = text.split("\n")
+ case = None
+ blocks: list[str] = []
+ goal_lines: list[str] = []
+ in_goal = False
+ for ln in lines:
+ if in_goal:
+ goal_lines.append(ln.strip())
+ elif ln.startswith("⊢"):
+ in_goal = True
+ goal_lines.append(ln[1:].strip())
+ elif ln.startswith("case ") and not blocks:
+ case = ln[5:].strip()
+ elif ln.startswith((" ", "\t")) and blocks:
+ blocks[-1] += " " + ln.strip()
+ elif ln.strip():
+ blocks.append(ln.strip())
+ hyps, has_let = [], False
+ for b in blocks:
+ names, sep, typ = b.partition(" : ")
+ if not sep:
+ continue
+ if re.search(r"\s:=\s", typ):
+ has_let = True
+ hyps.append((names.split(), typ.strip()))
+ return GoalState(case, hyps, " ".join(goal_lines), has_let)
+
+
+@dataclass
+class Lemma:
+ name: str
+ statement: str # `theorem name (binders) :\n goal := by sorry` (no header)
+ rename: list[str] # `rename_i` argument list ("_" for instances); [] when no hypothesis is inaccessible
+
+
+def lemma_from_goal(gs: GoalState, name: str) -> Optional[Lemma]:
+ """Standalone lemma for a goal state, or None when unsupported (let-bound hypotheses).
+
+ Inaccessible hypotheses (`n✝`, shadowed or auto-named) get fresh names `k5h` and every later mention of the old name
+ in a type or the goal is rewritten, so the lemma is closed; `rename` lists them (and `_` for instances) in context order
+ for the `rename_i` that gives the inlined proof the same names."""
+ if gs.has_let or not gs.goal:
+ return None
+ mp: dict[str, str] = {}
+ k = 0
+ for names, _ in gs.hyps:
+ inst = all(_INACC.search(n) and n.startswith("inst") for n in names)
+ if inst:
+ continue
+ for n in names:
+ if _INACC.search(n):
+ k += 1
+ mp[n] = f"k5h{k}"
+ pats = sorted(mp, key=len, reverse=True)
+
+ def fix(text: str) -> str:
+ for old in pats:
+ text = re.sub(r"(? list[Gap]:
+ return [g for g in self.gaps if g.lemma is not None]
+
+ @property
+ def complete(self) -> bool:
+ """Every gap has a lemma and the skeleton has no error of its own."""
+ return bool(self.gaps) and all(g.lemma for g in self.gaps) and not self.residual_errors and not self.unrepairable
+
+
+Probe = Callable[[str], dict]
+"""`probe(code) -> {"errors": [...], "sorries": [{"pos": {"line","column"}, "goal": str}, ...], "view_lines": int}`: runs
+`compile_view(code)` in the statement's header environment (one REPL command) and returns the error messages and the
+`sorries` the REPL reports. `repl_probe` builds one from a pool worker."""
+
+MAX_GAPS = 6
+
+
+def header_of(statement: str) -> str:
+ """Everything before the first `theorem`/`lemma`, comments removed: the header every derived lemma must carry."""
+ m = re.search(r"\b(?:theorem|lemma)\b", statement)
+ head = statement[:m.start()] if m else statement
+ return strip_comments(head).rstrip() + "\n\n"
+
+
+def _sorry_map(resp: dict, skeleton: str) -> dict:
+ vmap = view_line_map(skeleton)
+ out = {}
+ for s in resp.get("sorries", []):
+ p = s.get("pos") or {}
+ if "line" in p and 0 <= int(p["line"]) - 1 < len(vmap):
+ out[(vmap[int(p["line"]) - 1], int(p["column"]))] = s.get("goal", "")
+ return out
+
+
+def plan_subgoals(statement: str, code: str, errors: list[dict], probe: Probe, parent_id: str = "p",
+ repl_code: Optional[str] = None, max_gaps: int = MAX_GAPS) -> SubgoalPlan:
+ """Cut a failed proof into gaps (see the module docstring). Two passes: the have / tactic cuts first; the "proof ended
+ with goals left" tail cut only when the skeleton still shows that error (after a parse error Lean reports a cascade
+ of `unsolved goals` at the theorem's `by` that vanishes once the broken step is cut)."""
+ targets, bad = locate_targets(code, errors, repl_code)
+ header = header_of(statement)
+ if not targets or len(targets) > max_gaps:
+ return SubgoalPlan(code, code, [], bad + ([{"why": f"too_many_gaps:{len(targets)}"}] if targets else []), [], header)
+ main = [t for t in targets if t.kind != "tail"]
+ tails = [t for t in targets if t.kind == "tail"]
+ first = main or tails
+ skeleton, pos = build_skeleton(code, first)
+ resp = probe(skeleton)
+ tail_gap = None
+ if main and tails:
+ L2 = _analyse(skeleton)
+ tb2 = theorem_by_line(L2)
+ vmap2 = view_line_map(skeleton)
+ at_by = [e for e in resp.get("errors", []) if e.get("pos") and tb2 is not None
+ and 0 <= int(e["pos"]["line"]) - 1 < len(vmap2) and vmap2[int(e["pos"]["line"]) - 1] == tb2[0]]
+ if at_by:
+ tt = tail_target(L2, tb2)
+ tt.errors = at_by
+ skeleton2, pos2 = build_skeleton(skeleton, [tt])
+ tail_gap = (tt, pos2[0])
+ skeleton = skeleton2
+ resp = probe(skeleton)
+ sorries = _sorry_map(resp, skeleton)
+ gaps = []
+ for i, t in enumerate(first):
+ gaps.append(_make_gap(t, pos[i], sorries, parent_id))
+ if tail_gap is not None:
+ gaps.append(_make_gap(tail_gap[0], tail_gap[1], sorries, parent_id))
+ resid = [e for e in resp.get("errors", []) if isinstance(e, dict)]
+ return SubgoalPlan(code, skeleton, gaps, bad, resid, header)
+
+
+def _make_gap(t: Target, pos: tuple[int, int], sorries: dict, parent_id: str) -> Gap:
+ g = Gap(t, pos)
+ goal = sorries.get(pos)
+ if goal is None:
+ g.why_not = "no_sorry_reported"
+ return g
+ g.goal_state = goal
+ h = hashlib.sha256((parent_id + "|" + goal).encode()).hexdigest()[:10]
+ lem = lemma_from_goal(parse_goal_state(goal), f"k5_sg_{h}")
+ if lem is None:
+ g.why_not = "unsupported_goal_state"
+ else:
+ g.lemma = lem
+ return g
+
+
+def lemma_problem_statement(plan: SubgoalPlan, gap: Gap) -> str:
+ """The reference statement for the prover: header + the lemma ending `:= by sorry` (what `lean_gen` problems carry)."""
+ assert gap.lemma is not None
+ return plan.header + gap.lemma.statement
+
+
+# --------------------------------------------------------------------------- reassembly
+
+
+_LEMMA_HEAD = re.compile(r"theorem\s+(\S+)[\s\S]*?:=\s*by", re.M)
+
+
+def lemma_body(lemma_code: str, lemma_name: str) -> Optional[str]:
+ """The tactic text after the lemma's own `:= by` in a prover-produced code (`lean_code` of a record), or None when
+ the code is not a proof of exactly that lemma (another declaration, no body). Comments are masked for the search."""
+ mk = mask(lemma_code)
+ m = None
+ for cand in re.finditer(r"theorem\s+" + re.escape(lemma_name) + r"\b[\s\S]*?:=\s*by", mk):
+ m = cand
+ break
+ if m is None:
+ return None
+ body = lemma_code[m.end():]
+ if re.search(r"^\s*(?:theorem|lemma)\s", mask(body), re.M):
+ return None # a second declaration: not an inline-able body
+ return body
+
+
+def _indent_block(body: str, indent: int) -> list[str]:
+ ls = body.split("\n")
+ while ls and not ls[0].strip():
+ ls.pop(0)
+ while ls and not ls[-1].strip():
+ ls.pop()
+ if not ls:
+ return []
+ first = ls[0]
+ # `:= by simp` leaves the first tactic on the lemma's line: its text starts after one space
+ if body[:1] not in ("\n", "\r") and body.strip():
+ ls[0] = first.strip()
+ rest = [x for x in ls[1:]]
+ mi = min((len(x) - len(x.lstrip(" ")) for x in rest if x.strip()), default=0)
+ return [" " * indent + ls[0]] + [" " * indent + x[mi:] if x.strip() else "" for x in rest]
+ mi = min(len(x) - len(x.lstrip(" ")) for x in ls if x.strip())
+ return [" " * indent + x[mi:] if x.strip() else "" for x in ls]
+
+
+def reassemble(plan: SubgoalPlan, bodies: dict[int, str], restore_unproven: bool = True) -> Optional[str]:
+ """Full code with each proven gap's tactic body spliced in.
+
+ `bodies[i]` = tactic text for `plan.gaps[i]` (from `lemma_body`). An unproven gap gets its original text back when
+ `restore_unproven` (the result is then still a failing proof, but with every proven cut fixed: a better parent for
+ the next round), else the function returns None as soon as one is missing."""
+ raw = plan.skeleton.split("\n")
+ for i in sorted(range(len(plan.gaps)), key=lambda k: plan.gaps[k].sorry_pos, reverse=True):
+ g = plan.gaps[i]
+ ln, col = g.sorry_pos
+ line = raw[ln]
+ assert line[col:col + 5] == "sorry", "skeleton drifted from the plan"
+ body = bodies.get(i)
+ if body is None or not body.strip():
+ if not restore_unproven:
+ return None
+ if g.target.kind == "have": # the skeleton holds `... := by sorry`: put the original rhs back
+ raw[ln] = line[:col - len(" by ")] + g.target.original + line[col + 5:]
+ elif g.target.kind == "tactic":
+ raw[ln] = line[:col] + g.target.original + line[col + 5:]
+ else:
+ del raw[ln]
+ continue
+ pre = ["rename_i " + " ".join(g.lemma.rename)] if g.lemma is not None and g.lemma.rename else []
+ if g.target.kind == "have":
+ ind = g.target.indent + 2
+ raw[ln:ln + 1] = [line[:col].rstrip()] + [" " * ind + p for p in pre] + _indent_block(body, ind)
+ elif g.target.kind == "tactic":
+ blk = [" " * g.target.indent + p for p in pre] + _indent_block(body, g.target.indent)
+ raw[ln:ln + 1] = [line[:col] + blk[0].lstrip()] + blk[1:]
+ else: # tail: the inserted line holds only the gap
+ raw[ln:ln + 1] = [" " * g.target.indent + p for p in pre] + _indent_block(body, g.target.indent)
+ return "\n".join(raw)
+
+
+# --------------------------------------------------------------------------- Lean (read-only use of align.verify.lean)
+
+
+def repl_probe(worker: Any, statement: str, timeout_s: float = 120.0) -> Probe:
+ """A `Probe` over one pool worker (a `Worker` from `ReplPool.acquire`/`map`): the statement's header environment is
+ built (cached by the worker) and `compile_view(code)` is run in it, exactly as `verify_generations.collect_feedback`
+ does. Read-only: no change to the verifier, the pool or its limits."""
+ from align.verify.lean.strict import parse_statement
+
+ ref = parse_statement(statement)
+ if not ref.ok:
+ raise ValueError(f"statement not supported by the strict parser: {ref.problem}")
+
+ def probe(code: str) -> dict:
+ view = "\n".join(code.split("\n")[i] for i in view_line_map(code))
+ env = worker.header_env(ref.header_text, "")
+ resp = worker.command(view, env, timeout_s)
+ errs = [{"severity": "error", "pos": m.get("pos"), "endPos": m.get("endPos"), "data": m.get("data", "")}
+ for m in resp.get("messages", []) if isinstance(m, dict) and m.get("severity") == "error"]
+ return {"errors": errs, "sorries": resp.get("sorries", []), "view": view}
+
+ return probe
+
+
+def check_lemma_elaborates(worker: Any, plan: SubgoalPlan, gap: Gap, timeout_s: float = 120.0) -> bool:
+ """True iff `lemma_problem_statement` elaborates (no error; the `sorry` warning is expected). Gaps whose lemma does
+ not elaborate (pretty-printer round-trip failures) are dropped by the caller, not sent to the prover."""
+ from align.verify.lean.strict import parse_statement
+
+ stmt = lemma_problem_statement(plan, gap)
+ ref = parse_statement(stmt)
+ if not ref.ok:
+ return False
+ env = worker.header_env(ref.header_text, "")
+ view = stmt[len(plan.header):]
+ resp = worker.command(view, env, timeout_s)
+ return not any(isinstance(m, dict) and m.get("severity") == "error" for m in resp.get("messages", []))
+
+
+# --------------------------------------------------------------------------- (de)serialisation (route -> assemble hand-off)
+
+
+def plan_to_dict(plan: SubgoalPlan) -> dict:
+ return {
+ "code": plan.code, "skeleton": plan.skeleton, "header": plan.header,
+ "unrepairable": plan.unrepairable, "residual_errors": plan.residual_errors,
+ "gaps": [{
+ "kind": g.target.kind, "start": list(g.target.start), "end": list(g.target.end), "original": g.target.original,
+ "indent": g.target.indent, "name": g.target.name, "sorry_pos": list(g.sorry_pos), "goal_state": g.goal_state,
+ "why_not": g.why_not,
+ "lemma": None if g.lemma is None else {"name": g.lemma.name, "statement": g.lemma.statement, "rename": g.lemma.rename},
+ } for g in plan.gaps],
+ }
+
+
+def plan_from_dict(d: dict) -> SubgoalPlan:
+ gaps = []
+ for g in d["gaps"]:
+ t = Target(g["kind"], tuple(g["start"]), tuple(g["end"]), g["original"], g["indent"], g.get("name", ""))
+ lem = None if g["lemma"] is None else Lemma(g["lemma"]["name"], g["lemma"]["statement"], list(g["lemma"]["rename"]))
+ gaps.append(Gap(t, tuple(g["sorry_pos"]), g.get("goal_state"), lem, g.get("why_not")))
+ return SubgoalPlan(d["code"], d["skeleton"], gaps, d.get("unrepairable", []), d.get("residual_errors", []), d["header"])
diff --git a/code/align/rl/lean_rl.py b/code/align/rl/lean_rl.py
new file mode 100644
index 0000000000000000000000000000000000000000..8c27b3d1d5ef58be3590fed2aa2f7b81bfed7677
--- /dev/null
+++ b/code/align/rl/lean_rl.py
@@ -0,0 +1,659 @@
+"""Single-turn on-policy RL for Lean provers (lane R): config, rollout->batch, losses, gradient step.
+
+The run loop, checkpoints, ledger and PEFT export live in `align.rl.lean_rl_run`; the CLI is
+`scripts/p2/rl_lean.py`. Nothing here imports vLLM or runs Lean: generation and verification are
+file contracts (the P2 Generation / Verification JSONL records), so this module is testable on CPU
+with a fake generator and a fake verifier.
+
+THREE LOGPROB BASES, kept apart and stamped (Codex P13-32, audit U04-3):
+ behaviour log pi_infer(token) recorded by the sampler at sampling time (`sampling_logprobs`).
+ Requires top_p == 1.0 and top_k disabled: under a truncated sampler the sampled
+ distribution is NOT softmax(logits/T), so no trainer-side ratio against it is meaningful.
+ old trainer logprobs at the rollout-time weights. Single optimizer step per iteration: this is
+ `current.detach()` (no extra forward); it is used ONLY for the engine-mismatch telemetry
+ |old - behaviour|, never as the importance denominator.
+ current the differentiable forward of the step.
+The importance ratio is r = pi_current / pi_BEHAVIOUR, so the CISPO clamp (and the PPO clip of
+`dr_grpo`) is a trust region against the policy that actually produced the tokens, including checkpoint
+lag and the sampler/trainer kernel mismatch (U04-3). No separate rho factor exists to double count.
+
+LOSS MODES (all token-level, all normalised by an EXPLICIT global token count, see `norm_count`):
+ cispo -A * sg(min(r, 1+eps_high)[, floor 1-eps_low]) * log pi (MiniMax-M1 Eq. 4-5)
+ dr_grpo PPO clip max(-A r, -A clip(r, 1-lo, 1+hi)), centered advantages, no std, no length norm
+ ei -log pi on ACCEPTED samples only, weight 1: the same pipeline with negatives dropped
+ (K1: positive-only SFT vs signed updates on the SAME cached rollouts, Codex P13-12)
+Optional `neg_seq_ratio_floor` (cispo / dr_grpo): a sequence with A < 0 whose geometric-mean ratio is below
+the floor is masked whole (pushing down what the policy already made unlikely is the unstable direction).
+
+LOSS NORMALISATION (Codex P13-31). loss = sum_tokens(per-token loss) / N, N = the number of RESPONSE tokens
+of every sample the batch builder kept, over ALL micro-batches of ALL ranks (each rank all-reduces its local
+count and the result is checked against the count of the full list). Loss-time masks (negative-sequence
+mask, ratio clamp) do not shrink N: a masked token still counts, exactly as a clipped token does in DAPO.
+Consequence tested: the gradient is invariant to micro-batch partitioning and to the number of ranks.
+
+REWARDS. accepted -> 1.0, rejected -> 0.0. A verdict whose reason is in `no_signal_reasons` (timeout, and
+infra deaths) is MASKED: dropped from the batch AND from its group's baseline, never scored 0.
+Advantages are group-centered over the valid samples only (Dr.GRPO: no /std).
+"""
+
+from __future__ import annotations
+
+import dataclasses
+import hashlib
+import json
+import math
+import random
+from dataclasses import dataclass, field
+from typing import Any, Optional, Sequence
+
+import numpy as np
+import torch
+
+from align.rl.advantages import DEAD_ATOL, GroupFilterStats
+from align.rl.cispo import cispo_policy_loss, fp32_head_forward
+from align.rl.ops import log_softmax_and_gather
+
+LOSS_MODES = ("cispo", "dr_grpo", "ei")
+NORMS = ("global_token_mean", "constant")
+#: Verdict reasons that carry NO reward signal. `memory` / `output_too_large` are deliberately absent: a
+#: proof that kills its REPL is the policy's doing and scores 0 (scripts/p2/verify_generations.py).
+DEFAULT_NO_SIGNAL = ("timeout", "crash", "protocol")
+
+
+class RLConfigError(ValueError):
+ """A config that cannot launch."""
+
+
+# ---------------------------------------------------------------------------------------- config
+
+
+@dataclass
+class LossCfg:
+ mode: str = "cispo"
+ eps_high: float = 4.0 # CISPO IS-weight clamp (pre-registered sweep knob; paper does not state it)
+ eps_low: Optional[float] = None # None = no lower clamp (the paper's setting)
+ clip_lower: float = 0.2 # dr_grpo PPO clip
+ clip_higher: float = 0.28
+ neg_seq_ratio_floor: Optional[float] = None
+ norm: str = "global_token_mean"
+ constant: float = 0.0 # denominator when norm == "constant"
+ fp32_head: bool = False
+ fp32_head_chunk_tokens: int = 2048
+ temperature: float = 1.0
+ max_grad_norm: float = 1.0
+
+ def validate(self) -> None:
+ if self.mode not in LOSS_MODES:
+ raise RLConfigError(f"loss.mode={self.mode!r}: one of {LOSS_MODES}")
+ if self.norm not in NORMS:
+ raise RLConfigError(f"loss.norm={self.norm!r}: one of {NORMS}")
+ if self.norm == "constant" and not self.constant > 0:
+ raise RLConfigError("loss.norm=constant needs loss.constant > 0")
+ if not (self.eps_high > 0 and math.isfinite(self.eps_high)):
+ raise RLConfigError("loss.eps_high must be finite and > 0")
+ if self.eps_low is not None and not (self.eps_low > 0 and math.isfinite(self.eps_low)):
+ raise RLConfigError("loss.eps_low must be None or finite > 0")
+ if self.neg_seq_ratio_floor is not None and not (0 < self.neg_seq_ratio_floor < 1):
+ raise RLConfigError("loss.neg_seq_ratio_floor must be in (0, 1)")
+ if self.neg_seq_ratio_floor is not None and self.mode == "ei":
+ raise RLConfigError("neg_seq_ratio_floor is meaningless in ei mode (no negatives)")
+ if not self.temperature > 0:
+ raise RLConfigError("loss.temperature must be > 0")
+
+
+@dataclass
+class BatchCfg:
+ group_size: int = 8 # G rollouts per prompt
+ min_valid_per_group: int = 2 # after masking timeouts; fewer -> group dropped
+ no_signal_reasons: tuple = DEFAULT_NO_SIGNAL
+ #: ei only: keep the positives of all-solved groups (rejection-sampling FT keeps them); signed modes always
+ #: drop dead groups (zero advantage = zero gradient, wasted tokens).
+ ei_keep_all_solved: bool = True
+ micro_tokens: int = 16384 # padded tokens per micro-batch (rows x max len)
+ max_total_len: int = 0 # >0: drop samples whose prompt+completion exceeds this (counted)
+
+
+@dataclass
+class RolloutCfg:
+ top_p: float = 1.0 # REQUIRED 1.0 (Codex P13-32)
+ top_k: int = 0
+ temperature: float = 1.0
+ cap_tokens: int = 16384
+ max_model_len: int = 20480
+ tp: int = 4
+ adapter_mode: str = "unmerged" # "unmerged" (vLLM LoRARequest) | "merged_export" (fallback)
+ rollout_python: str = ".venv-rollout/bin/python"
+ gen_extra_args: tuple = ()
+
+ def validate(self) -> None:
+ if self.top_p != 1.0:
+ raise RLConfigError(f"rollout.top_p={self.top_p}: RL rollouts need top_p == 1.0 (Codex P13-32)")
+ if self.top_k not in (0, -1):
+ raise RLConfigError(f"rollout.top_k={self.top_k}: must be 0/-1 (disabled)")
+ if self.adapter_mode not in ("unmerged", "merged_export"):
+ raise RLConfigError(f"rollout.adapter_mode={self.adapter_mode!r}")
+
+
+@dataclass
+class OptimCfg:
+ lr: float = 1e-5
+ betas: tuple = (0.9, 0.95)
+ weight_decay: float = 0.0
+ eps: float = 1e-8
+ warmup_iters: int = 0
+ steps_per_iteration: int = 1 # optimizer steps taken on one rollout batch
+
+
+@dataclass
+class LoraCfg:
+ r: int = 64
+ alpha: float = 128.0
+ rslora: bool = False
+ targets: tuple = ("q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj")
+ adapter: str = "policy"
+ param_dtype: str = "float32"
+
+
+@dataclass
+class RLConfig:
+ run_name: str = "p2-rl-lean"
+ run_dir: str = ""
+ model_snapshot: str = "" # HF snapshot dir of the base (Goedel-8B / Kimina-Distill-8B)
+ problems: str = "" # P2 problem jsonl (training pool)
+ iterations: int = 1
+ prompts_per_iter: int = 8
+ seed: int = 0
+ keep_checkpoints: int = 2
+ export_merged: bool = False
+ replay_from: str = ""
+ verify_workers: int = 8
+ verify_budget_s: float = 180.0
+ verify_python: str = ".venv/bin/python"
+ loss: LossCfg = field(default_factory=LossCfg)
+ batch: BatchCfg = field(default_factory=BatchCfg)
+ rollout: RolloutCfg = field(default_factory=RolloutCfg)
+ optim: OptimCfg = field(default_factory=OptimCfg)
+ lora: LoraCfg = field(default_factory=LoraCfg)
+
+ def validate(self) -> None:
+ self.loss.validate()
+ self.rollout.validate()
+ if self.loss.temperature != self.rollout.temperature:
+ raise RLConfigError(
+ f"loss.temperature={self.loss.temperature} != rollout.temperature={self.rollout.temperature}: the "
+ "trainer must score tokens under the distribution that sampled them"
+ )
+ if self.batch.group_size < 2:
+ raise RLConfigError("batch.group_size must be >= 2 (a group of 1 has no baseline)")
+ if self.batch.min_valid_per_group < 2:
+ raise RLConfigError("batch.min_valid_per_group must be >= 2")
+ if self.prompts_per_iter < 1 or self.iterations < 1:
+ raise RLConfigError("prompts_per_iter and iterations must be >= 1")
+ if self.optim.steps_per_iteration < 1:
+ raise RLConfigError("optim.steps_per_iteration must be >= 1")
+ if self.loss.mode == "cispo" and self.loss.fp32_head and self.loss.fp32_head_chunk_tokens < 1:
+ raise RLConfigError("fp32_head_chunk_tokens must be >= 1")
+
+ def fingerprint(self) -> str:
+ """Hash of everything that changes the experiment (not paths of scratch). A resume under a different
+ fingerprint is a different run and is refused."""
+ d = dataclasses.asdict(self)
+ for k in ("run_dir", "run_name", "iterations", "replay_from", "verify_workers", "keep_checkpoints", "export_merged"):
+ d.pop(k, None)
+ return hashlib.sha256(json.dumps(d, sort_keys=True, default=str).encode()).hexdigest()
+
+
+_SECTIONS = {"loss": LossCfg, "batch": BatchCfg, "rollout": RolloutCfg, "optim": OptimCfg, "lora": LoraCfg}
+
+
+def config_from_dict(d: dict) -> RLConfig:
+ """Strict: an unknown key anywhere is an error (a typo'd knob silently keeping its default is how a
+ pre-registered setting gets lost)."""
+ d = dict(d)
+ kw: dict[str, Any] = {}
+ for name, cls in _SECTIONS.items():
+ sec = dict(d.pop(name, {}))
+ names = {f.name for f in dataclasses.fields(cls)}
+ bad = sorted(set(sec) - names)
+ if bad:
+ raise RLConfigError(f"[{name}] unknown keys {bad}")
+ for k, v in list(sec.items()):
+ if isinstance(v, list):
+ sec[k] = tuple(v)
+ kw[name] = cls(**sec)
+ names = {f.name for f in dataclasses.fields(RLConfig)} - set(_SECTIONS)
+ bad = sorted(set(d) - names)
+ if bad:
+ raise RLConfigError(f"unknown top-level keys {bad}")
+ cfg = RLConfig(**d, **kw)
+ cfg.validate()
+ return cfg
+
+
+def load_config(path: str, overrides: Sequence[str] = ()) -> RLConfig:
+ import tomllib
+
+ with open(path, "rb") as f:
+ d = tomllib.load(f)
+ for ov in overrides:
+ key, _, raw = ov.partition("=")
+ try:
+ val = tomllib.loads(f"v = {raw}")["v"]
+ except tomllib.TOMLDecodeError:
+ val = raw
+ cur = d
+ parts = key.split(".")
+ for p in parts[:-1]:
+ cur = cur.setdefault(p, {})
+ cur[parts[-1]] = val
+ return config_from_dict(d)
+
+
+# ------------------------------------------------------------------------- prompt stream (U04-2)
+
+
+def prompt_block(ids: Sequence[str], seed: int, per_iter: int, k: int) -> list[str]:
+ """The prompt ids of iteration `k`: a PURE function of (ids, seed, per_iter, k).
+
+ Replays the stream from the start (cheap: id lists). Each pass over the set is an independent shuffle
+ seeded by (seed, pass index) -- epoch-indexed, so passes differ -- and the tail of one pass is carried
+ into the next block (nothing is dropped, every prompt is drawn once per pass). A block never repeats a
+ prompt unless it is larger than the pool.
+ """
+ n = len(ids)
+ if n == 0:
+ raise ValueError("empty prompt pool")
+ order: list[int] = []
+ cursor = 0
+ passes = 0
+ out: list[str] = []
+ for it in range(k + 1):
+ block: list[int] = []
+ while len(block) < per_iter:
+ if cursor >= len(order):
+ order = list(range(n))
+ random.Random(seed * 1000003 + passes * 7919 + 11).shuffle(order)
+ passes += 1
+ cursor = 0
+ i = order[cursor]
+ if per_iter <= n and i in block:
+ for j in range(cursor + 1, len(order)):
+ if order[j] not in block:
+ order[cursor], order[j] = order[j], order[cursor]
+ i = order[cursor]
+ break
+ cursor += 1
+ block.append(i)
+ out = [ids[i] for i in block]
+ return out
+
+
+def stream_position(n_pool: int, per_iter: int, k: int) -> dict:
+ """Position stored in the checkpoint (draws made, pass index); recomputable, kept as a cross-check."""
+ draws = k * per_iter
+ return {"draws": draws, "pass": draws // n_pool, "offset": draws % n_pool}
+
+
+# -------------------------------------------------------------------------------- rollout -> batch
+
+
+@dataclass
+class Sample:
+ key: tuple # (problem_id, sample_idx, round, cap_tokens)
+ group: str # problem_id
+ prompt_ids: list
+ completion_ids: list
+ behaviour_lp: list # one per completion token
+ reward: float
+ adv: float = 0.0
+
+ @property
+ def n_resp(self) -> int:
+ return len(self.completion_ids)
+
+ @property
+ def total_len(self) -> int:
+ return len(self.prompt_ids) + len(self.completion_ids)
+
+
+def validate_rl_generation(rec: dict) -> None:
+ """The rollout-time contract an RL record must meet (raises with the key named)."""
+ key = (rec.get("problem_id"), rec.get("sample_idx"))
+ s = rec.get("sampling") or {}
+ if s.get("top_p") != 1.0:
+ raise RLConfigError(f"{key}: sampling.top_p={s.get('top_p')!r}; RL rollouts require top_p == 1.0")
+ if s.get("top_k") not in (0, -1, None):
+ raise RLConfigError(f"{key}: sampling.top_k={s.get('top_k')!r}; RL rollouts require top_k disabled")
+ lp = rec.get("sampling_logprobs")
+ comp = rec.get("completion_ids")
+ if not isinstance(lp, list) or not isinstance(comp, list) or len(lp) != len(comp) or not comp:
+ raise RLConfigError(f"{key}: sampling_logprobs must be a list aligned with completion_ids")
+ arr = np.asarray(lp, dtype=np.float64)
+ if not np.all(np.isfinite(arr)) or np.any(arr > 1e-4):
+ raise RLConfigError(f"{key}: sampling_logprobs must be finite and <= 0")
+
+
+def reward_of(ver: dict, no_signal: Sequence[str]) -> tuple[Optional[float], Optional[str]]:
+ """(reward, None) or (None, why-masked). Timeouts / infra deaths carry NO signal."""
+ reasons = list(ver.get("reasons") or [])
+ if ver.get("accepted"):
+ return 1.0, None
+ hit = [r for r in reasons if r in no_signal]
+ if hit:
+ return None, hit[0]
+ return 0.0, None
+
+
+def build_samples(gens: Sequence[dict], vers: dict, cfg: RLConfig) -> tuple[list[Sample], dict]:
+ """Join generations with verdicts -> trainable samples + telemetry.
+
+ Dead groups (all-rewards-equal over the VALID samples) are dropped in signed modes; `ei` keeps the
+ positives of all-solved groups when `batch.ei_keep_all_solved`. Masked (no-signal) samples are removed
+ before the baseline is computed.
+ """
+ b = cfg.batch
+ stats = {"samples": len(gens), "masked": collections_counter(), "groups": 0, "groups_too_few_valid": 0,
+ "dropped_overlength": 0, "accepted": 0, "valid": 0}
+ by_group: dict[str, list[Sample]] = {}
+ for g in gens:
+ validate_rl_generation(g)
+ key = (g["problem_id"], g["sample_idx"], g["round"], g["cap_tokens"])
+ v = vers.get(key)
+ if v is None:
+ raise RLConfigError(f"{key}: generation has no verification record (verify before training)")
+ r, why = reward_of(v, b.no_signal_reasons)
+ if r is None:
+ stats["masked"][why] += 1
+ continue
+ s = Sample(key, g["problem_id"], list(g["prompt_ids"]), list(g["completion_ids"]),
+ [float(x) for x in g["sampling_logprobs"]], r)
+ if b.max_total_len and s.total_len > b.max_total_len:
+ stats["dropped_overlength"] += 1
+ continue
+ stats["valid"] += 1
+ stats["accepted"] += int(r == 1.0)
+ by_group.setdefault(g["problem_id"], []).append(s)
+ fstats = GroupFilterStats(max_possible_score=1.0)
+ out: list[Sample] = []
+ for pid in sorted(by_group):
+ grp = sorted(by_group[pid], key=lambda s: s.key)
+ stats["groups"] += 1
+ if len(grp) < b.min_valid_per_group:
+ stats["groups_too_few_valid"] += 1
+ continue
+ rewards = np.array([s.reward for s in grp])
+ dead = fstats.observe(rewards)
+ if cfg.loss.mode == "ei":
+ keep = [s for s in grp if s.reward == 1.0]
+ all_solved = bool(np.all(rewards == 1.0))
+ if not keep or (all_solved and not b.ei_keep_all_solved):
+ continue
+ for s in keep:
+ s.adv = 1.0
+ out.extend(keep)
+ continue
+ if dead:
+ continue
+ mean = float(rewards.mean())
+ for s in grp:
+ s.adv = s.reward - mean
+ out.extend(grp)
+ stats["masked"] = dict(stats["masked"])
+ stats["group_filter"] = fstats.as_log_dict()
+ stats["kept_samples"] = len(out)
+ stats["kept_resp_tokens"] = sum(s.n_resp for s in out)
+ return out, stats
+
+
+def collections_counter():
+ import collections
+
+ return collections.Counter()
+
+
+# ------------------------------------------------------------------------------- sharding / packing
+
+
+def shard_samples(samples: Sequence[Sample], world: int) -> list[list[Sample]]:
+ """Deterministic length-balanced split (longest-first onto the lightest rank)."""
+ order = sorted(samples, key=lambda s: (-s.total_len, s.key))
+ loads = [0] * world
+ shards: list[list[Sample]] = [[] for _ in range(world)]
+ for s in order:
+ r = min(range(world), key=lambda i: (loads[i], i))
+ shards[r].append(s)
+ loads[r] += s.total_len
+ return shards
+
+
+@dataclass
+class Micro:
+ input_ids: torch.Tensor # [B, T]
+ attention_mask: torch.Tensor # [B, T]
+ resp: torch.Tensor # [B, T-1] bool, label space
+ behaviour: torch.Tensor # [B, T-1]
+ adv: torch.Tensor # [B]
+ dummy: bool = False
+
+ @property
+ def n_resp(self) -> int:
+ return 0 if self.dummy else int(self.resp.sum())
+
+
+def pack_micros(samples: Sequence[Sample], micro_tokens: int, pad_id: int = 0, dtype=torch.float32) -> list[Micro]:
+ """Token-budget micro-batches (rows x padded max length <= micro_tokens; a longer lone row gets its own)."""
+ rows = sorted(samples, key=lambda s: (s.total_len, s.key))
+ micros: list[Micro] = []
+ cur: list[Sample] = []
+ cur_max = 0
+ for s in rows:
+ m = max(cur_max, s.total_len)
+ if cur and m * (len(cur) + 1) > micro_tokens:
+ micros.append(_collate(cur, pad_id, dtype))
+ cur, m = [], s.total_len
+ cur.append(s)
+ cur_max = m
+ if cur:
+ micros.append(_collate(cur, pad_id, dtype))
+ return micros
+
+
+def _collate(rows: Sequence[Sample], pad_id: int, dtype) -> Micro:
+ T = max(s.total_len for s in rows)
+ B = len(rows)
+ ids = torch.full((B, T), pad_id, dtype=torch.int64)
+ att = torch.zeros((B, T), dtype=torch.int64)
+ resp = torch.zeros((B, T - 1), dtype=torch.bool)
+ beh = torch.zeros((B, T - 1), dtype=dtype)
+ adv = torch.zeros(B, dtype=dtype)
+ for i, s in enumerate(rows):
+ n = s.total_len
+ ids[i, :n] = torch.tensor(s.prompt_ids + s.completion_ids)
+ att[i, :n] = 1
+ p = len(s.prompt_ids)
+ resp[i, p - 1 : n - 1] = True # label t+1 in the completion
+ beh[i, p - 1 : n - 1] = torch.tensor(s.behaviour_lp, dtype=dtype)
+ adv[i] = s.adv
+ return Micro(ids, att, resp, beh, adv)
+
+
+def dummy_micro(like: Optional[Micro], pad_id: int = 0, dtype=torch.float32) -> Micro:
+ """A micro that trains on nothing but runs the same forward/backward (so every rank issues the same
+ collectives): one row, empty response mask, zero advantage."""
+ if like is not None:
+ n = int(like.attention_mask[0].sum())
+ ids = like.input_ids[:1, :n].clone()
+ else:
+ ids = torch.full((1, 2), pad_id, dtype=torch.int64)
+ T = ids.shape[1]
+ return Micro(ids, torch.ones_like(ids), torch.zeros((1, T - 1), dtype=torch.bool),
+ torch.zeros((1, T - 1), dtype=dtype), torch.zeros(1, dtype=dtype), dummy=True)
+
+
+# ---------------------------------------------------------------------------------------- the loss
+
+
+def token_loss(new_lp: torch.Tensor, behaviour: torch.Tensor, adv: torch.Tensor, resp: torch.Tensor,
+ cfg: LossCfg) -> tuple[torch.Tensor, dict]:
+ """Unreduced per-token loss [B, T-1] (zero off the response mask) + diagnostic tensors.
+
+ ratio = exp(new - behaviour): importance weight against the BEHAVIOUR policy (U04-3).
+ """
+ m = resp.to(new_lp.dtype)
+ A = adv.to(new_lp.dtype).unsqueeze(-1).expand_as(new_lp)
+ diff = (new_lp - behaviour).clamp(-20.0, 20.0)
+ ratio = torch.exp(diff)
+ clipfrac = torch.zeros_like(new_lp)
+ row_w = torch.ones(new_lp.shape[0], dtype=new_lp.dtype, device=new_lp.device)
+ if cfg.mode == "ei":
+ loss = -new_lp
+ else:
+ if cfg.mode == "cispo":
+ loss, clipfrac = cispo_policy_loss(new_lp, ratio, A, cfg.eps_high, cfg.eps_low)
+ else: # dr_grpo
+ l1 = -A * ratio
+ l2 = -A * torch.clamp(ratio, 1.0 - cfg.clip_lower, 1.0 + cfg.clip_higher)
+ loss = torch.max(l1, l2)
+ clipfrac = (l2 > l1).to(new_lp.dtype)
+ if cfg.neg_seq_ratio_floor is not None:
+ n = m.sum(-1).clamp_min(1.0)
+ geo = torch.exp((diff.detach() * m).sum(-1) / n)
+ drop = (adv < 0) & (geo < cfg.neg_seq_ratio_floor)
+ row_w = (~drop).to(new_lp.dtype)
+ loss = loss * m * row_w.unsqueeze(-1)
+ return loss, {"ratio": ratio.detach(), "clipfrac": clipfrac.detach() * m, "row_w": row_w.detach()}
+
+
+def policy_logprobs(model, mb: Micro, cfg: LossCfg) -> torch.Tensor:
+ """Label-aligned [B, T-1] logprobs of the batch's own tokens under the CURRENT policy."""
+ if cfg.fp32_head:
+ return fp32_head_forward(model, mb.input_ids, mb.attention_mask, cfg.temperature, cfg.fp32_head_chunk_tokens)
+ out = model(input_ids=mb.input_ids, attention_mask=mb.attention_mask)
+ logits = getattr(out, "logits", out)
+ if cfg.temperature != 1.0:
+ logits = logits / cfg.temperature
+ return log_softmax_and_gather(logits[:, :-1], mb.input_ids[:, 1:])
+
+
+# ------------------------------------------------------------------------------- distributed bits
+
+
+def _dist_on() -> bool:
+ import torch.distributed as dist
+
+ return dist.is_available() and dist.is_initialized()
+
+
+def world_size() -> int:
+ import torch.distributed as dist
+
+ return dist.get_world_size() if _dist_on() else 1
+
+
+def all_reduce_ints(vals: list[int], op: str = "sum") -> list[int]:
+ if not _dist_on():
+ return [int(v) for v in vals]
+ import torch.distributed as dist
+
+ from align.utils.dist import gloo_group
+
+ g = gloo_group() or dist.group.WORLD
+ t = torch.tensor([int(v) for v in vals], dtype=torch.int64)
+ dist.all_reduce(t, op=dist.ReduceOp.SUM if op == "sum" else dist.ReduceOp.MAX, group=g)
+ return [int(v) for v in t.tolist()]
+
+
+def all_reduce_floats(vals: list[float]) -> list[float]:
+ if not _dist_on():
+ return [float(v) for v in vals]
+ import torch.distributed as dist
+
+ from align.utils.dist import gloo_group
+
+ g = gloo_group() or dist.group.WORLD
+ t = torch.tensor([float(v) for v in vals], dtype=torch.float64)
+ dist.all_reduce(t, op=dist.ReduceOp.SUM, group=g)
+ return [float(v) for v in t.tolist()]
+
+
+def norm_count(local_resp_tokens: int, cfg: LossCfg, expect_total: Optional[int] = None) -> float:
+ """The explicit loss denominator N (module docstring). Global = SUM over ranks of the local response-token
+ counts, verified against the full-list count when the caller can supply it."""
+ if cfg.norm == "constant":
+ return float(cfg.constant)
+ total = all_reduce_ints([local_resp_tokens])[0]
+ if expect_total is not None and total != expect_total:
+ raise RuntimeError(f"global token count {total} != full-batch count {expect_total}: ranks hold different data")
+ if total <= 0:
+ raise RuntimeError("global response-token count is 0")
+ return float(total)
+
+
+def _is_sharded(params: Sequence[torch.nn.Parameter]) -> bool:
+ return any(hasattr(p, "to_local") for p in params)
+
+
+def accumulate_gradients(model, micros: Sequence[Micro], cfg: LossCfg, denom: float,
+ params: Sequence[torch.nn.Parameter], pad_id: int = 0) -> dict:
+ """Forward+backward over this rank's micro-batches with the GLOBAL denominator; returns summed stats.
+
+ Every rank runs the same number of micro-steps (agreed by MAX; short ranks run zero-weight dummies).
+ Gradient scale: loss_i / N. FSDP2 reduce-scatter AVERAGES over ranks, so a sharded model multiplies by the
+ world size; a replicated model (CPU tests, single GPU) sums gradients explicitly with an all-reduce SUM.
+ Either way grad = d/dtheta [ sum over ALL ranks/micros/tokens of per-token loss / N ].
+ """
+ world = world_size()
+ n_agreed = all_reduce_ints([len(micros)], "max")[0]
+ sharded = _is_sharded(params)
+ scale = float(world) if (sharded and world > 1) else 1.0
+ like = micros[0] if micros else None
+ dtype = like.behaviour.dtype if like is not None else torch.float32
+ acc = {"loss_sum": 0.0, "resp_tokens": 0.0, "clip_tokens": 0.0, "ratio_sum": 0.0, "rows": 0.0,
+ "rows_masked_negseq": 0.0, "mismatch_abs_sum": 0.0, "adv_pos_rows": 0.0, "entropy_proxy": 0.0}
+ for i in range(n_agreed):
+ mb = micros[i] if i < len(micros) else dummy_micro(like, pad_id, dtype)
+ new_lp = policy_logprobs(model, mb, cfg)
+ tok, d = token_loss(new_lp, mb.behaviour.to(new_lp.dtype), mb.adv, mb.resp, cfg)
+ loss = tok.sum() / denom * scale
+ loss.backward()
+ with torch.no_grad():
+ m = mb.resp.to(torch.float64)
+ acc["loss_sum"] += float(tok.detach().double().sum())
+ acc["resp_tokens"] += float(m.sum())
+ acc["clip_tokens"] += float((d["clipfrac"].double() * m).sum())
+ acc["ratio_sum"] += float((d["ratio"].double() * m).sum())
+ acc["mismatch_abs_sum"] += float(((new_lp.detach() - mb.behaviour.to(new_lp.dtype)).abs().double() * m).sum())
+ if not mb.dummy:
+ acc["rows"] += float(mb.input_ids.shape[0])
+ acc["rows_masked_negseq"] += float((d["row_w"] == 0).sum())
+ acc["adv_pos_rows"] += float((mb.adv > 0).sum())
+ if world > 1 and not sharded:
+ import torch.distributed as dist
+
+ from align.utils.dist import gloo_group
+
+ g = gloo_group() or dist.group.WORLD
+ for p in params:
+ if p.grad is None:
+ p.grad = torch.zeros_like(p)
+ dist.all_reduce(p.grad, op=dist.ReduceOp.SUM, group=g)
+ keys = sorted(acc)
+ red = all_reduce_floats([acc[k] for k in keys])
+ return dict(zip(keys, red))
+
+
+def summarize(stats: dict, denom: float, grad_norm: float, cfg: LossCfg) -> dict:
+ t = max(stats["resp_tokens"], 1.0)
+ return {
+ "loss": stats["loss_sum"] / denom,
+ "denominator": denom,
+ "resp_tokens": stats["resp_tokens"],
+ "rows": stats["rows"],
+ "clipfrac": stats["clip_tokens"] / t,
+ "ratio_mean": stats["ratio_sum"] / t,
+ # engine mismatch |log pi_current(=old at step 0) - log pi_behaviour|, mean over response tokens
+ "old_vs_behaviour_abs_logratio": stats["mismatch_abs_sum"] / t,
+ "negseq_masked_rows": stats["rows_masked_negseq"],
+ "grad_norm": grad_norm,
+ "loss_mode": cfg.mode,
+ }
diff --git a/code/align/rl/lean_rl_run.py b/code/align/rl/lean_rl_run.py
new file mode 100644
index 0000000000000000000000000000000000000000..22f40080d62ee4ae27f5d434af70438d65d09495
--- /dev/null
+++ b/code/align/rl/lean_rl_run.py
@@ -0,0 +1,716 @@
+"""Lean RL run loop: iteration stages, atomic checkpoints, PEFT export, budget ledger, backends.
+
+ iteration k (policy = ckpt/iter_k's adapter):
+ gen generator.generate -> iters/iter_k/gens.jsonl (CURRENT policy; behaviour logprobs recorded)
+ verify verifier.verify -> iters/iter_k/ver.jsonl
+ train trainer.train(k) -> ckpt/iter_{k+1}/ (adapter, optimizer, RNG, stream, ledger)
+
+Every stage is idempotent and commits with a marker (`gen.done`, `ver.done`) or a directory rename
+(checkpoints), so a kill at any point resumes into the same state: a killed `train` leaves only a `.tmp_*`
+directory, which is deleted, and the stage re-runs from the same gens/ver files and the same ckpt k.
+
+GPU HANDOFF. The default real flow keeps the loop process CPU-only: generation (vLLM, tp=4) and training
+(torchrun, FSDP2) are separate child processes that run one after the other, so neither holds the GPUs while the
+other runs; the loop itself never imports a model. `InProcessTrainer` (tests, single-device runs) calls
+`train_iteration` directly. This module imports torch/numpy/safetensors/stdlib/align only.
+"""
+
+from __future__ import annotations
+
+import contextlib
+import hashlib
+import json
+import os
+import shutil
+import subprocess
+import sys
+import time
+from typing import Callable, Optional
+
+import torch
+
+from align.rl import lean_rl as L
+from align.utils.dist import capture_rng_state, restore_rng_state
+
+ADAPTER_NAME_PEFT = "adapter_model.safetensors"
+
+
+# ------------------------------------------------------------------------------------ fs helpers
+
+
+def fsync_dir(path: str) -> None:
+ fd = os.open(path, os.O_RDONLY)
+ try:
+ os.fsync(fd)
+ finally:
+ os.close(fd)
+
+
+def atomic_write_text(path: str, text: str) -> None:
+ tmp = path + ".partial"
+ with open(tmp, "w") as f:
+ f.write(text)
+ f.flush()
+ os.fsync(f.fileno())
+ os.replace(tmp, path)
+
+
+def read_jsonl(path: str) -> list[dict]:
+ out = []
+ with open(path, encoding="utf-8") as f:
+ for n, line in enumerate(f, 1):
+ if line.strip():
+ try:
+ out.append(json.loads(line))
+ except json.JSONDecodeError as e:
+ raise ValueError(f"{path}:{n}: {e}") from e
+ return out
+
+
+def write_jsonl_atomic(path: str, rows) -> None:
+ atomic_write_text(path, "".join(json.dumps(r, sort_keys=True) + "\n" for r in rows))
+
+
+def sha256_file(path: str) -> str:
+ h = hashlib.sha256()
+ with open(path, "rb") as f:
+ for c in iter(lambda: f.read(1 << 20), b""):
+ h.update(c)
+ return h.hexdigest()
+
+
+# --------------------------------------------------------------------------------- PEFT export
+
+
+def peft_key_map(qcfg) -> dict[str, str]:
+ """align module path -> HF module path, for every projection LoRA can target."""
+ from align.model.qwen3_checkpoint import _table
+
+ out = {}
+ for hf_w, al_w in _table(qcfg).items():
+ if hf_w.endswith(".weight") and ".layers." in hf_w:
+ out[al_w[: -len(".weight")]] = hf_w[: -len(".weight")]
+ return out
+
+
+def lora_to_peft_tensors(lora_state: dict, qcfg) -> dict[str, torch.Tensor]:
+ m = peft_key_map(qcfg)
+ out = {}
+ for k, v in lora_state.items():
+ path, ab = k.rsplit(".", 1)
+ if path not in m:
+ raise KeyError(f"adapter path {path!r} has no HF counterpart")
+ out[f"base_model.model.{m[path]}.lora_{ab}.weight"] = v.detach().to("cpu").contiguous()
+ return out
+
+
+def peft_to_lora_state(tensors: dict, qcfg) -> dict[str, torch.Tensor]:
+ inv = {v: k for k, v in peft_key_map(qcfg).items()}
+ out = {}
+ for k, v in tensors.items():
+ assert k.startswith("base_model.model.")
+ body = k[len("base_model.model."):]
+ hf_path, lora_ab = body.rsplit(".lora_", 1)
+ out[f"{inv[hf_path]}.{lora_ab.split('.')[0]}"] = v
+ return out
+
+
+def write_peft_dir(out_dir: str, lora_state: dict, qcfg, lcfg: L.LoraCfg, base_name: str, dtype=torch.float32) -> None:
+ """A PEFT adapter directory (adapter_config.json + adapter_model.safetensors), written to a sibling temp dir
+ and renamed in, so a reader (vLLM) never sees half of it."""
+ import safetensors.torch as st
+
+ tmp = out_dir.rstrip("/") + ".partial"
+ shutil.rmtree(tmp, ignore_errors=True)
+ os.makedirs(tmp)
+ tens = {k: v.to(dtype) for k, v in lora_to_peft_tensors(lora_state, qcfg).items()}
+ st.save_file(tens, os.path.join(tmp, ADAPTER_NAME_PEFT), metadata={"format": "pt"})
+ cfg = {
+ "peft_type": "LORA", "task_type": "CAUSAL_LM", "r": lcfg.r, "lora_alpha": lcfg.alpha, "lora_dropout": 0.0,
+ "bias": "none", "use_rslora": bool(lcfg.rslora), "target_modules": list(lcfg.targets),
+ "base_model_name_or_path": base_name, "inference_mode": True, "fan_in_fan_out": False,
+ "init_lora_weights": True, "modules_to_save": None,
+ }
+ with open(os.path.join(tmp, "adapter_config.json"), "w") as f:
+ json.dump(cfg, f, indent=1, sort_keys=True)
+ f.flush()
+ os.fsync(f.fileno())
+ if os.path.isdir(out_dir):
+ shutil.rmtree(out_dir)
+ os.replace(tmp, out_dir)
+
+
+def read_peft_dir(path: str, qcfg) -> dict[str, torch.Tensor]:
+ import safetensors.torch as st
+
+ return peft_to_lora_state(st.load_file(os.path.join(path, ADAPTER_NAME_PEFT)), qcfg)
+
+
+def export_merged_checkpoint(model, adapter: str, qcfg, out_dir: str, tokenizer_dir: Optional[str] = None,
+ dtype=torch.bfloat16) -> dict:
+ """FALLBACK for serving without un-merged LoRA: base+adapter folded into an HF checkpoint. Lossy by
+ construction (bf16 cast drops small delta elements; the fidelity report is returned). Collective under FSDP:
+ every rank must call; rank 0 writes."""
+ from align.model.qwen3_checkpoint import save_hf_qwen3
+ from align.ppo.lora import export_merged_state_dict
+ from align.utils.dist import is_rank0
+
+ if L._is_sharded(list(model.parameters())):
+ from torch.distributed.checkpoint.state_dict import StateDictOptions, get_model_state_dict
+
+ sd = get_model_state_dict(model, options=StateDictOptions(full_state_dict=True, cpu_offload=True))
+ else:
+ sd = {k: v.detach().to("cpu") for k, v in model.state_dict().items()}
+ report: dict = {}
+ if sd:
+ merged = export_merged_state_dict(model, sd, adapter, report=report)
+ if is_rank0():
+ tmp = out_dir.rstrip("/") + ".partial"
+ shutil.rmtree(tmp, ignore_errors=True)
+ save_hf_qwen3(merged, tmp, cfg=qcfg, dtype=dtype, tokenizer_dir=tokenizer_dir)
+ if os.path.isdir(out_dir):
+ shutil.rmtree(out_dir)
+ os.replace(tmp, out_dir)
+ return report
+
+
+# --------------------------------------------------------------------------------- checkpoints
+
+
+def ckpt_path(run_dir: str, k: int) -> str:
+ return os.path.join(run_dir, "ckpt", f"iter_{k:06d}")
+
+
+def latest_complete(run_dir: str) -> Optional[int]:
+ root = os.path.join(run_dir, "ckpt")
+ if not os.path.isdir(root):
+ return None
+ best = None
+ for name in os.listdir(root):
+ if name.startswith("iter_") and os.path.isfile(os.path.join(root, name, "COMPLETE")):
+ k = int(name[5:])
+ best = k if best is None else max(best, k)
+ return best
+
+
+def clean_partial(run_dir: str) -> list[str]:
+ """Delete half-written checkpoints (`.tmp_*`) and incomplete `iter_*` dirs: they look resumable and are not."""
+ root = os.path.join(run_dir, "ckpt")
+ gone = []
+ if not os.path.isdir(root):
+ return gone
+ for name in sorted(os.listdir(root)):
+ p = os.path.join(root, name)
+ if name.startswith(".tmp_") or (name.startswith("iter_") and not os.path.isfile(os.path.join(p, "COMPLETE"))):
+ shutil.rmtree(p, ignore_errors=True)
+ gone.append(name)
+ return gone
+
+
+def _rank() -> int:
+ import torch.distributed as dist
+
+ return dist.get_rank() if L._dist_on() else 0
+
+
+def _barrier() -> None:
+ import torch.distributed as dist
+
+ if L._dist_on():
+ dist.barrier()
+
+
+def save_checkpoint(run_dir: str, k: int, *, model, optimizer, lora_state: dict, qcfg, cfg: L.RLConfig, ledger: list,
+ bases: dict, base_name: str, base_fingerprint: Optional[str]) -> str:
+ """Commit checkpoint k atomically: write everything into `.tmp_iter_k`, then rename. `COMPLETE` is the
+ last file written inside; the rename is the commit. `lora_state` must already be gathered (collective done
+ by the caller on every rank)."""
+ final = ckpt_path(run_dir, k)
+ tmp = os.path.join(run_dir, "ckpt", f".tmp_iter_{k:06d}")
+ r = _rank()
+ if r == 0:
+ os.makedirs(os.path.join(run_dir, "ckpt"), exist_ok=True)
+ shutil.rmtree(tmp, ignore_errors=True)
+ os.makedirs(tmp)
+ _barrier()
+ optim_sd = optimizer_state_for_save(model, optimizer) # collective on every rank
+ if r == 0:
+ torch.save(_cpu_tree(optim_sd), os.path.join(tmp, "optim.pt"))
+ state = {
+ "iteration": k, "cfg_fingerprint": cfg.fingerprint(), "world_size": L.world_size(),
+ "adapter": {a: t.detach().cpu() for a, t in lora_state.items()}, "rng": capture_rng_state(),
+ "stream": L.stream_position(_pool_size(cfg), cfg.prompts_per_iter, k), "ledger": ledger,
+ "bases": bases, "base_fingerprint": base_fingerprint,
+ }
+ torch.save(state, os.path.join(tmp, "state.pt"))
+ write_peft_dir(os.path.join(tmp, "adapter"), lora_state, qcfg, cfg.lora, base_name)
+ write_jsonl_atomic(os.path.join(tmp, "ledger.jsonl"), ledger)
+ atomic_write_text(os.path.join(tmp, "COMPLETE"), json.dumps({"iteration": k, "t": time.time()}))
+ _barrier()
+ if r == 0:
+ if os.path.isdir(final):
+ shutil.rmtree(final)
+ fsync_dir(tmp)
+ os.replace(tmp, final)
+ fsync_dir(os.path.dirname(final))
+ write_jsonl_atomic(os.path.join(run_dir, "ledger.jsonl"), ledger)
+ _prune(run_dir, cfg.keep_checkpoints)
+ _barrier()
+ return final
+
+
+def _prune(run_dir: str, keep: int) -> None:
+ root = os.path.join(run_dir, "ckpt")
+ done = sorted(n for n in os.listdir(root) if n.startswith("iter_") and os.path.isfile(os.path.join(root, n, "COMPLETE")))
+ for n in done[:-max(keep, 1)]:
+ shutil.rmtree(os.path.join(root, n), ignore_errors=True)
+
+
+def load_checkpoint_state(run_dir: str, k: int, cfg: L.RLConfig) -> dict:
+ d = ckpt_path(run_dir, k)
+ if not os.path.isfile(os.path.join(d, "COMPLETE")):
+ raise FileNotFoundError(f"no complete checkpoint {d}")
+ st = torch.load(os.path.join(d, "state.pt"), weights_only=True)
+ if st["cfg_fingerprint"] != cfg.fingerprint():
+ raise RuntimeError("checkpoint was written under a different config fingerprint; refusing to resume")
+ return st
+
+
+def optimizer_state_for_save(model, optimizer) -> dict:
+ """Optimizer state in a world-size-independent form. Replicated model: the local state (identical on every
+ rank). FSDP2-sharded: the FULL state via DCP (a collective; rank 0 receives it, the others {})."""
+ if L._is_sharded(list(model.parameters())):
+ from torch.distributed.checkpoint.state_dict import StateDictOptions, get_optimizer_state_dict
+
+ return get_optimizer_state_dict(model, optimizer, options=StateDictOptions(full_state_dict=True, cpu_offload=True))
+ return optimizer.state_dict()
+
+
+def load_optimizer(run_dir: str, k: int, model, optimizer) -> None:
+ p = os.path.join(ckpt_path(run_dir, k), "optim.pt")
+ sd = torch.load(p, weights_only=True)
+ if L._is_sharded(list(model.parameters())):
+ from torch.distributed.checkpoint.state_dict import StateDictOptions, set_optimizer_state_dict
+
+ set_optimizer_state_dict(model, optimizer, optim_state_dict=sd,
+ options=StateDictOptions(full_state_dict=True, broadcast_from_rank0=False))
+ else:
+ optimizer.load_state_dict(sd)
+
+
+def _cpu_tree(x):
+ if isinstance(x, torch.Tensor):
+ return x.detach().cpu()
+ if isinstance(x, dict):
+ return {k: _cpu_tree(v) for k, v in x.items()}
+ if isinstance(x, (list, tuple)):
+ return type(x)(_cpu_tree(v) for v in x)
+ return x
+
+
+def _pool_size(cfg: L.RLConfig) -> int:
+ return len(read_jsonl(cfg.problems))
+
+
+def load_problems(path: str) -> list[dict]:
+ rows = read_jsonl(path)
+ ids = [r["problem_id"] for r in rows]
+ if len(set(ids)) != len(ids):
+ raise ValueError(f"{path}: duplicate problem_id")
+ return rows
+
+
+# ------------------------------------------------------------------------------- iteration files
+
+
+def iter_dir(run_dir: str, k: int) -> str:
+ return os.path.join(run_dir, "iters", f"iter_{k:04d}")
+
+
+def stage_marker(d: str, name: str) -> str:
+ return os.path.join(d, f"{name}.done")
+
+
+def add_stage_time(d: str, stage: str, wall_s: float) -> None:
+ p = os.path.join(d, "stage_times.json")
+ cur = json.load(open(p)) if os.path.exists(p) else {}
+ cur[f"{stage}_wall_s"] = cur.get(f"{stage}_wall_s", 0.0) + wall_s
+ cur[f"{stage}_attempts"] = cur.get(f"{stage}_attempts", 0) + 1
+ atomic_write_text(p, json.dumps(cur, sort_keys=True))
+
+
+def ledger_entry(cfg: L.RLConfig, k: int, d: str, bstats: dict, metrics: dict, train_wall_s: float,
+ padded_tokens: float, prev: list) -> dict:
+ gens = read_jsonl(os.path.join(d, "gens.jsonl"))
+ vers = read_jsonl(os.path.join(d, "ver.jsonl"))
+ st = json.load(open(os.path.join(d, "stage_times.json"))) if os.path.exists(os.path.join(d, "stage_times.json")) else {}
+ gen_tokens = sum(len(g["completion_ids"]) for g in gens)
+ ver_s = sum(float(v.get("verify_s", 0.0)) for v in vers)
+ e = {
+ "iteration": k, "loss_mode": cfg.loss.mode, "prompts": cfg.prompts_per_iter, "samples": len(gens),
+ "generated_tokens": gen_tokens, "prompt_tokens": sum(len(g["prompt_ids"]) for g in gens),
+ "verifier_seconds": ver_s, "verifier_wall_s": st.get("verify_wall_s", 0.0),
+ "generate_wall_s": st.get("gen_wall_s", 0.0), "train_wall_s": train_wall_s,
+ "train_tokens": metrics.get("resp_tokens_total", 0.0), "train_padded_tokens": padded_tokens,
+ "stage_attempts": {s: st.get(f"{s}_attempts", 0) for s in ("gen", "verify")},
+ "updated": bool(metrics.get("updated", False)), "batch": bstats, "metrics": metrics,
+ "bases": metrics.get("bases", {}),
+ }
+ e["wall_s"] = e["generate_wall_s"] + e["verifier_wall_s"] + train_wall_s
+ keys = ("generated_tokens", "verifier_seconds", "train_tokens", "train_padded_tokens", "wall_s")
+ e["cumulative"] = {c: (prev[-1]["cumulative"][c] if prev else 0.0) + e[c] for c in keys}
+ return e
+
+
+# ---------------------------------------------------------------------------- the train iteration
+
+
+def bases_stamp(cfg: L.RLConfig, gens: list) -> dict:
+ g0 = gens[0] if gens else {}
+ s = g0.get("sampling", {})
+ return {
+ "behaviour": {"source": "sampler", "field": "sampling_logprobs", "temperature": s.get("temperature"),
+ "top_p": s.get("top_p"), "top_k": s.get("top_k"), "model_rev": g0.get("model_rev"),
+ "serving": (g0.get("adapter") or {}).get("serving", "merged_or_base")},
+ "old": {"source": "trainer", "weights": "rollout-time (= current at the first optimizer step)",
+ "role": "mismatch telemetry only; NOT the importance denominator", "fp32_head": cfg.loss.fp32_head},
+ "current": {"source": "trainer", "differentiable": True, "fp32_head": cfg.loss.fp32_head,
+ "temperature": cfg.loss.temperature},
+ "importance_ratio": "pi_current / pi_behaviour",
+ }
+
+
+def lr_at(cfg: L.RLConfig, k: int) -> float:
+ w = cfg.optim.warmup_iters
+ return cfg.optim.lr * (min(1.0, (k + 1) / w) if w > 0 else 1.0)
+
+
+def train_iteration(cfg: L.RLConfig, k: int, *, model, params, optimizer, qcfg, pad_id: int, base_name: str,
+ base_fingerprint: Optional[str] = None, tokenizer_dir: Optional[str] = None) -> dict:
+ """One training iteration on iteration k's cached gens/ver, committing checkpoint k+1.
+
+ `model` carries the injected LoRA adapter `cfg.lora.adapter`; `params` are its trainable parameters;
+ `optimizer` is built over them. State is loaded from checkpoint k (adapter, optimizer, RNG)."""
+ from align.ppo.lora import load_lora_state_dict, lora_state_dict
+
+ run = cfg.run_dir
+ d = iter_dir(run, k)
+ st = load_checkpoint_state(run, k, cfg)
+ load_lora_state_dict(model, cfg.lora.adapter, st["adapter"], strict=True)
+ load_optimizer(run, k, model, optimizer)
+ restore_rng_state(st["rng"])
+ ledger = list(st["ledger"])
+ if len(ledger) != k:
+ raise RuntimeError(f"checkpoint {k} carries {len(ledger)} ledger rows")
+ pos = L.stream_position(_pool_size(cfg), cfg.prompts_per_iter, k)
+ if st["stream"] != pos:
+ raise RuntimeError(f"prompt stream position {st['stream']} != recomputed {pos}")
+
+ gens = read_jsonl(os.path.join(d, "gens.jsonl"))
+ vers = {(v["problem_id"], v["sample_idx"], v["round"], v["cap_tokens"]): v for v in read_jsonl(os.path.join(d, "ver.jsonl"))}
+ samples, bstats = L.build_samples(gens, vers, cfg)
+ world = L.world_size()
+ r = _rank()
+ t0 = time.perf_counter()
+ metrics: dict = {"updated": False, "bases": bases_stamp(cfg, gens), "resp_tokens_total": 0.0}
+ padded = 0.0
+ if samples:
+ dtype = next(iter(params)).dtype
+ steps = cfg.optim.steps_per_iteration
+ groups = sorted({s.group for s in samples})
+ chunks = [[s for s in samples if groups.index(s.group) % steps == i] for i in range(steps)]
+ step_metrics = []
+ for i, chunk in enumerate(chunks):
+ if not chunk:
+ continue
+ mine = L.shard_samples(chunk, world)[r]
+ micros = L.pack_micros(mine, cfg.batch.micro_tokens, pad_id, dtype)
+ expect = sum(s.n_resp for s in chunk)
+ denom = L.norm_count(sum(m.n_resp for m in micros), cfg.loss, expect)
+ for p in params:
+ p.grad = None
+ t_step = time.perf_counter()
+ acc = L.accumulate_gradients(model, micros, cfg.loss, denom, params, pad_id)
+ total = torch.nn.utils.clip_grad_norm_(list(params), cfg.loss.max_grad_norm)
+ if hasattr(total, "full_tensor"):
+ total = total.full_tensor()
+ for g in optimizer.param_groups:
+ g["lr"] = lr_at(cfg, k)
+ optimizer.step()
+ if torch.cuda.is_available() and torch.cuda.is_initialized():
+ torch.cuda.synchronize()
+ for p in params:
+ p.grad = None
+ padded += L.all_reduce_floats([float(sum(m.input_ids.numel() for m in micros))])[0]
+ sm = L.summarize(acc, denom, float(total), cfg.loss)
+ sm["step_wall_s"] = time.perf_counter() - t_step
+ sm["n_micro_this_rank"] = len(micros)
+ if torch.cuda.is_available() and torch.cuda.is_initialized():
+ sm["cuda_peak_alloc_gb"] = torch.cuda.max_memory_allocated() / 2**30
+ sm["cuda_peak_reserved_gb"] = torch.cuda.max_memory_reserved() / 2**30
+ step_metrics.append(sm)
+ metrics.update(step_metrics[-1])
+ metrics["steps"] = step_metrics
+ metrics["updated"] = True
+ metrics["resp_tokens_total"] = float(sum(m["resp_tokens"] for m in step_metrics))
+ train_wall = time.perf_counter() - t0
+ lora = lora_state_dict(model, cfg.lora.adapter) # collective under FSDP
+ entry = ledger_entry(cfg, k, d, bstats, metrics, train_wall, padded, ledger)
+ ledger.append(entry)
+ if cfg.export_merged:
+ entry["merged_export"] = export_merged_checkpoint(model, cfg.lora.adapter, qcfg,
+ os.path.join(ckpt_path(run, k + 1) + ".merged_tmp"), tokenizer_dir)
+ save_checkpoint(run, k + 1, model=model, optimizer=optimizer, lora_state=lora, qcfg=qcfg, cfg=cfg, ledger=ledger,
+ bases=metrics["bases"], base_name=base_name, base_fingerprint=base_fingerprint)
+ if cfg.export_merged and _rank() == 0:
+ src = ckpt_path(run, k + 1) + ".merged_tmp"
+ if os.path.isdir(src):
+ os.replace(src, os.path.join(ckpt_path(run, k + 1), "merged"))
+ _barrier()
+ return entry
+
+
+def init_checkpoint(cfg: L.RLConfig, *, model, optimizer, qcfg, base_name: str, base_fingerprint: Optional[str] = None) -> None:
+ """Checkpoint 0 = the freshly initialised adapter (B = 0, so it IS the base policy) and a fresh optimizer."""
+ from align.ppo.lora import lora_state_dict
+
+ if latest_complete(cfg.run_dir) is not None:
+ return
+ save_checkpoint(cfg.run_dir, 0, model=model, optimizer=optimizer, lora_state=lora_state_dict(model, cfg.lora.adapter), qcfg=qcfg,
+ cfg=cfg, ledger=[], bases={}, base_fingerprint=base_fingerprint, base_name=base_name)
+
+
+def make_optimizer(cfg: L.RLConfig, params):
+ return torch.optim.AdamW(list(params), lr=cfg.optim.lr, betas=tuple(cfg.optim.betas), eps=cfg.optim.eps,
+ weight_decay=cfg.optim.weight_decay)
+
+
+# ----------------------------------------------------------------------------- real policy build
+
+
+def build_real_policy(cfg: L.RLConfig) -> tuple:
+ """Rank-serial load of the frozen bf16 base -> GPU -> LoRA (identical init on every rank, B = 0) -> adapter-only
+ trainable -> activation checkpointing -> FSDP2 (world > 1). Same recipe as `align.train.qwen3_stage`, without
+ its RunConfig. Returns the tuple `InProcessTrainer` expects."""
+ import gc
+
+ from align.model import qwen3 as q3
+ from align.model.checkpoint import load_hf_safetensors, resolve_snapshot_dir
+ from align.model.qwen3_checkpoint import hf_to_align, read_qwen3_config
+ from align.ppo.lora import LoRAConfig, inject_lora, mark_only_adapter_trainable
+ from align.train import fsdp
+ from align.train.qwen3_stage import base_fingerprint
+ from align.utils.dist import barrier, local_rank, rank, world_size
+
+ snapshot = resolve_snapshot_dir(cfg.model_snapshot)
+ qcfg = read_qwen3_config(snapshot).replace(attn_implementation="sdpa")
+ device = torch.device("cuda", local_rank())
+ torch.cuda.set_device(device)
+ model = None
+ for r in range(world_size()):
+ if rank() == r:
+ sd = hf_to_align(load_hf_safetensors(snapshot, dtype=torch.bfloat16), qcfg)
+ model = q3.Qwen3ForCausalLM.from_state_dict(qcfg, sd, device="cuda").to(device)
+ del sd
+ gc.collect()
+ barrier()
+ fp = base_fingerprint(model.state_dict()) # whole tensors, before inject/FSDP
+ lc = cfg.lora
+ gen = torch.Generator().manual_seed(cfg.seed) # identical A on every rank: FSDP2 shards, never broadcasts
+ hit = inject_lora(model, LoRAConfig(r=lc.r, alpha=lc.alpha, target_modules=tuple(lc.targets), rslora=lc.rslora),
+ adapter=lc.adapter, generator=gen)
+ expected = len(lc.targets) * qcfg.n_layers
+ if len(hit) != expected:
+ raise RuntimeError(f"LoRA hit {len(hit)} layers, expected {expected}")
+ mark_only_adapter_trainable(model, lc.adapter)
+ fsdp.apply_activation_checkpointing(model)
+ if world_size() > 1:
+ fsdp.apply_fsdp(model)
+ params = [p for p in model.parameters() if p.requires_grad]
+ pad = qcfg.pad_token_id if qcfg.pad_token_id is not None else (qcfg.eos_token_id or 0)
+ return model, params, qcfg, pad, snapshot, fp, snapshot
+
+
+# --------------------------------------------------------------------------------------- backends
+
+
+class InProcessTrainer:
+ """Trainer backend that runs `train_iteration` in this process. `builder()` -> (model, params, qcfg, pad_id,
+ base_name, base_fp, tokenizer_dir)."""
+
+ def __init__(self, cfg: L.RLConfig, builder: Callable[[], tuple]):
+ self.cfg, self.builder = cfg, builder
+
+ def _build(self):
+ model, params, qcfg, pad_id, base_name, base_fp, tok = self.builder()
+ return model, params, make_optimizer(self.cfg, params), qcfg, pad_id, base_name, base_fp, tok
+
+ def init(self) -> None:
+ model, params, opt, qcfg, pad_id, base_name, base_fp, _ = self._build()
+ init_checkpoint(self.cfg, model=model, optimizer=opt, qcfg=qcfg, base_name=base_name, base_fingerprint=base_fp)
+
+ def train(self, k: int) -> dict:
+ model, params, opt, qcfg, pad_id, base_name, base_fp, tok = self._build()
+ return train_iteration(self.cfg, k, model=model, params=params, optimizer=opt, qcfg=qcfg, pad_id=pad_id,
+ base_name=base_name, base_fingerprint=base_fp, tokenizer_dir=tok)
+
+
+def repo_root() -> str:
+ return os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+
+
+class LeanGenCliGenerator:
+ """Real generator: `extras/vllm_rollout/lean_gen.py` as a child process (needs the genharness flags
+ `--lora-adapter`, `--top-p`, `--temperature` and `--record-logprobs`)."""
+
+ def __init__(self, cfg: L.RLConfig, config_path: str = ""):
+ self.cfg = cfg
+
+ def command(self, k: int, d: str, problems_path: str, adapter_dir: str, merged_dir: Optional[str], seed: int) -> list[str]:
+ c, ro = self.cfg, self.cfg.rollout
+ root = repo_root()
+ py = ro.rollout_python if os.path.isabs(ro.rollout_python) else os.path.join(root, ro.rollout_python)
+ model = c.model_snapshot
+ cmd = [py, os.path.join(root, "extras", "vllm_rollout", "lean_gen.py"), "--problems", problems_path,
+ "--out", os.path.join(d, "gens.jsonl"), "--n", str(c.batch.group_size), "--cap-tokens", str(ro.cap_tokens),
+ "--round", "0", "--tp", str(ro.tp), "--wait-lock", "--seed", str(seed), "--max-model-len", str(ro.max_model_len),
+ "--temperature", str(ro.temperature), "--top-p", "1.0", "--record-logprobs",
+ "--label", f"rl-lean-gen-{k}"]
+ if ro.adapter_mode == "unmerged":
+ cmd += ["--model", model, "--lora-adapter", adapter_dir]
+ else:
+ if not merged_dir:
+ raise L.RLConfigError("adapter_mode=merged_export needs export_merged=true")
+ cmd += ["--model", merged_dir]
+ return cmd + list(ro.gen_extra_args)
+
+ def generate(self, k: int, d: str, problems_path: str, adapter_dir: str, merged_dir: Optional[str], seed: int) -> None:
+ subprocess.run(self.command(k, d, problems_path, adapter_dir, merged_dir, seed), check=True)
+
+
+class VerifyCliVerifier:
+ def __init__(self, cfg: L.RLConfig):
+ self.cfg = cfg
+
+ def command(self, d: str, problems_path: str) -> list[str]:
+ c, root = self.cfg, repo_root()
+ py = c.verify_python if os.path.isabs(c.verify_python) else os.path.join(root, c.verify_python)
+ return [py, os.path.join(root, "scripts", "p2", "verify_generations.py"), "--generations", os.path.join(d, "gens.jsonl"),
+ "--problems", problems_path, "--out", os.path.join(d, "ver.jsonl"), "--workers", str(c.verify_workers),
+ "--budget-s", str(c.verify_budget_s)]
+
+ def verify(self, d: str, problems_path: str) -> None:
+ subprocess.run(self.command(d, problems_path), check=True)
+
+
+class ReplayBackend:
+ """Replay cached rollouts + verdicts of another run (K1: positive-only vs signed on the SAME rollouts).
+ Rollouts beyond iteration 0 were sampled by the SOURCE arm's policy, so they are off-policy for this arm;
+ the importance ratio against the recorded behaviour logprobs keeps the update well defined."""
+
+ def __init__(self, src_run_dir: str):
+ self.src = src_run_dir
+
+ def generate(self, k, d, problems_path, adapter_dir, merged_dir, seed) -> None:
+ shutil.copyfile(os.path.join(iter_dir(self.src, k), "gens.jsonl"), os.path.join(d, "gens.jsonl"))
+
+ def verify(self, d, problems_path) -> None:
+ # k is encoded in d's name; copy the sibling verdicts
+ k = int(os.path.basename(d).split("_")[1])
+ shutil.copyfile(os.path.join(iter_dir(self.src, k), "ver.jsonl"), os.path.join(d, "ver.jsonl"))
+
+
+def torchrun_command(cfg_path: str, k: int, nproc: int, overrides=()) -> list[str]:
+ return ["torchrun", "--standalone", f"--nproc_per_node={nproc}", os.path.join(repo_root(), "scripts", "p2", "rl_lean.py"),
+ "train-iter", cfg_path, "--iteration", str(k), *[f"--set={o}" for o in overrides]]
+
+
+class TorchrunTrainer:
+ def __init__(self, cfg: L.RLConfig, cfg_path: str, nproc: int, overrides=()):
+ self.cfg, self.cfg_path, self.nproc, self.overrides = cfg, cfg_path, nproc, tuple(overrides)
+
+ def init(self) -> None:
+ subprocess.run(torchrun_command(self.cfg_path, -1, self.nproc, self.overrides), check=True)
+
+ def train(self, k: int) -> dict:
+ subprocess.run(torchrun_command(self.cfg_path, k, self.nproc, self.overrides), check=True)
+ return {}
+
+
+# ------------------------------------------------------------------------------------- the loop
+
+
+def derive_seed(seed: int, k: int) -> int:
+ return int.from_bytes(hashlib.sha256(f"rl|{seed}|{k}".encode()).digest()[:8], "big") % (2**31)
+
+
+def run_loop(cfg: L.RLConfig, generator, verifier, trainer, hook: Optional[Callable[[str, int], None]] = None,
+ until: Optional[int] = None) -> list[dict]:
+ """Run (or resume) iterations up to `until` (default cfg.iterations). `hook(stage, k)` fires after each
+ committed stage (tests inject crashes there). Returns the committed ledger."""
+ run = cfg.run_dir
+ os.makedirs(run, exist_ok=True)
+ cfg_json = os.path.join(run, "run_config.json")
+ import dataclasses
+
+ fp = cfg.fingerprint()
+ if os.path.exists(cfg_json):
+ old = json.load(open(cfg_json))["fingerprint"]
+ if old != fp:
+ raise RuntimeError(f"{run} was started under config {old[:12]}, this is {fp[:12]}: refusing to mix runs")
+ else:
+ atomic_write_text(cfg_json, json.dumps({"fingerprint": fp, "config": dataclasses.asdict(cfg)}, sort_keys=True, default=str, indent=1))
+ clean_partial(run)
+ problems = load_problems(cfg.problems)
+ by_id = {p["problem_id"]: p for p in problems}
+ ids = [p["problem_id"] for p in problems]
+ if latest_complete(run) is None:
+ trainer.init()
+ k0 = latest_complete(run)
+ if k0 is None:
+ raise RuntimeError("no checkpoint after init")
+ hook = hook or (lambda stage, k: None)
+ end = cfg.iterations if until is None else min(until, cfg.iterations)
+ for k in range(k0, end):
+ d = iter_dir(run, k)
+ os.makedirs(d, exist_ok=True)
+ probs_path = os.path.join(d, "problems.jsonl")
+ if not os.path.exists(probs_path):
+ block = L.prompt_block(ids, cfg.seed, cfg.prompts_per_iter, k)
+ write_jsonl_atomic(probs_path, [by_id[i] for i in block])
+ if not os.path.exists(stage_marker(d, "gen")):
+ gp = os.path.join(d, "gens.jsonl")
+ adapter_dir = os.path.join(ckpt_path(run, k), "adapter")
+ merged = os.path.join(ckpt_path(run, k), "merged") if cfg.export_merged else None
+ t0 = time.perf_counter()
+ gen = ReplayBackend(cfg.replay_from) if cfg.replay_from else generator
+ gen.generate(k, d, probs_path, adapter_dir, merged, derive_seed(cfg.seed, k))
+ add_stage_time(d, "gen", time.perf_counter() - t0)
+ _check_generations(gp, cfg, [by_id_id["problem_id"] for by_id_id in read_jsonl(probs_path)])
+ atomic_write_text(stage_marker(d, "gen"), json.dumps({"sha256": sha256_file(gp)}))
+ hook("gen", k)
+ if not os.path.exists(stage_marker(d, "verify")):
+ vp = os.path.join(d, "ver.jsonl")
+ t0 = time.perf_counter()
+ (ReplayBackend(cfg.replay_from) if cfg.replay_from else verifier).verify(d, probs_path)
+ add_stage_time(d, "verify", time.perf_counter() - t0)
+ n_g, n_v = len(read_jsonl(os.path.join(d, "gens.jsonl"))), len(read_jsonl(vp))
+ if n_g != n_v:
+ raise RuntimeError(f"{n_v} verdicts for {n_g} generations")
+ atomic_write_text(stage_marker(d, "verify"), json.dumps({"sha256": sha256_file(vp)}))
+ hook("verify", k)
+ t0 = time.perf_counter()
+ trainer.train(k)
+ add_stage_time(d, "train_proc", time.perf_counter() - t0) # whole child: model load + step + commit
+ hook("train", k)
+ last = latest_complete(run)
+ return read_jsonl(os.path.join(run, "ledger.jsonl")) if last else []
+
+
+def _check_generations(path: str, cfg: L.RLConfig, expect_ids: list[str]) -> None:
+ gens = read_jsonl(path)
+ want = len(expect_ids) * cfg.batch.group_size
+ if len(gens) != want:
+ raise RuntimeError(f"{path}: {len(gens)} generations, expected {want} ({len(expect_ids)} prompts x G={cfg.batch.group_size})")
+ for g in gens:
+ L.validate_rl_generation(g)
diff --git a/code/align/rl/lean_rl_testing.py b/code/align/rl/lean_rl_testing.py
new file mode 100644
index 0000000000000000000000000000000000000000..be2ec91ac8beec75aaefbe827c9bec4bfb7d5136
--- /dev/null
+++ b/code/align/rl/lean_rl_testing.py
@@ -0,0 +1,174 @@
+"""TEST SUPPORT for the Lean RL loop (CPU only): a tiny Qwen3-shaped model, a FAKE generator that really
+samples from it (so the loop is genuinely on-policy), and a FAKE verifier. No vLLM, no Lean.
+
+Not used by any real run; it lives in the package so the tests and `scripts/p2/rl_lean.py --selftest` share it.
+"""
+
+from __future__ import annotations
+
+import hashlib
+import json
+import os
+import random
+
+import torch
+
+from align.model.qwen3 import Qwen3Config, Qwen3ForCausalLM
+from align.ppo.lora import LoRAConfig, inject_lora, lora_modules, mark_only_adapter_trainable
+from align.rl import lean_rl as L
+from align.rl import lean_rl_run as R
+
+TINY = Qwen3Config(name="tiny", vocab_size=48, hidden_size=32, intermediate_size=64, n_layers=2, n_heads=4,
+ n_kv_heads=2, head_dim=8, max_position_embeddings=256, bos_token_id=1, eos_token_id=2,
+ pad_token_id=0)
+EOS = 2
+
+
+def tiny_base(seed: int = 0, dtype=torch.float32) -> Qwen3ForCausalLM:
+ torch.manual_seed(seed)
+ m = Qwen3ForCausalLM(TINY, attn_implementation="eager", device="cpu")
+ return m.to(dtype)
+
+
+def tiny_policy(lcfg: L.LoraCfg, seed: int = 0, dtype=torch.float32, adapter_seed: int = 1234):
+ """(model, params) with LoRA injected (B = 0 unless randomised) and only the adapter trainable."""
+ m = tiny_base(seed, torch.float32)
+ gen = torch.Generator().manual_seed(adapter_seed)
+ inject_lora(m, LoRAConfig(r=lcfg.r, alpha=lcfg.alpha, target_modules=tuple(lcfg.targets), rslora=lcfg.rslora),
+ adapter=lcfg.adapter, generator=gen)
+ mark_only_adapter_trainable(m, lcfg.adapter)
+ m = m.to(dtype)
+ params = [p for p in m.parameters() if p.requires_grad]
+ return m, params
+
+
+def randomize_adapter(model, adapter: str, seed: int, std: float = 0.05) -> None:
+ g = torch.Generator().manual_seed(seed)
+ with torch.no_grad():
+ for _, mod in lora_modules(model):
+ a = mod.adapters[adapter]
+ a.A.copy_(torch.randn(a.A.shape, generator=g) * std)
+ a.B.copy_(torch.randn(a.B.shape, generator=g) * std)
+
+
+def tiny_builder(cfg: L.RLConfig, dtype=torch.float32):
+ def build():
+ model, params = tiny_policy(cfg.lora, dtype=dtype)
+ return model, params, TINY, 0, "tiny-base", None, None
+
+ return build
+
+
+def synthetic_problems(n: int) -> list[dict]:
+ rows = []
+ for i in range(n):
+ fs = f"theorem t{i} : {i} = {i} := by"
+ rows.append({"problem_id": f"p{i:03d}", "source": "synthetic", "split": "train", "formal_statement": fs,
+ "informal_prefix": "", "statement_sha": hashlib.sha256(fs.encode()).hexdigest()})
+ return rows
+
+
+def prompt_ids_for(pid: str) -> list[int]:
+ h = int.from_bytes(hashlib.sha256(pid.encode()).digest()[:4], "big")
+ rng = random.Random(h)
+ return [1] + [rng.randrange(3, TINY.vocab_size) for _ in range(rng.randrange(2, 6))]
+
+
+def _key_hash(*parts) -> int:
+ return int.from_bytes(hashlib.sha256("|".join(map(str, parts)).encode()).digest()[:8], "big")
+
+
+class FakeGenerator:
+ """Samples from the tiny model + the adapter in `adapter_dir` (the CURRENT policy), temperature 1, top_p 1,
+ recording the sampled-token logprobs. A real on-policy rollout, CPU-sized. `crash_in_gen` (set by a test)
+ kills the process mid-stage after writing nothing, like a vLLM crash before the output is complete."""
+
+ def __init__(self, cfg: L.RLConfig, max_new: int = 8, base_seed: int = 0, dtype=torch.float32):
+ self.cfg, self.max_new, self.base_seed, self.dtype = cfg, max_new, base_seed, dtype
+ self.calls = 0
+
+ def generate(self, k, d, problems_path, adapter_dir, merged_dir, seed) -> None:
+ from align.ppo.lora import load_lora_state_dict
+
+ self.calls += 1
+ model, _ = tiny_policy(self.cfg.lora, seed=self.base_seed, dtype=self.dtype)
+ load_lora_state_dict(model, self.cfg.lora.adapter, R.read_peft_dir(adapter_dir, TINY), strict=True)
+ model.eval()
+ recs = []
+ for prob in R.read_jsonl(problems_path):
+ pid = prob["problem_id"]
+ prompt = prompt_ids_for(pid)
+ for si in range(self.cfg.batch.group_size):
+ g = torch.Generator().manual_seed(_key_hash(seed, pid, si))
+ ids = list(prompt)
+ lps = []
+ finish = "length"
+ for _ in range(self.max_new):
+ with torch.no_grad():
+ logits = model(input_ids=torch.tensor([ids]))[0, -1].double()
+ lp = torch.log_softmax(logits, -1)
+ tok = int(torch.multinomial(lp.exp(), 1, generator=g))
+ ids.append(tok)
+ lps.append(float(lp[tok]))
+ if tok == EOS:
+ finish = "stop"
+ break
+ comp = ids[len(prompt):]
+ recs.append({
+ "problem_id": pid, "sample_idx": si, "round": 0, "cap_tokens": self.cfg.rollout.cap_tokens,
+ "prompt_ids": prompt, "completion_ids": comp, "prompt_text": "", "completion_text": " ".join(map(str, comp)),
+ "finish_reason": finish, "lean_code": "theorem x := by\n trivial", "messages": [],
+ "model_path": "tiny", "model_rev": "tiny+adapter",
+ "sampling": {"temperature": 1.0, "top_p": 1.0, "top_k": 0, "n": 1, "max_tokens": self.max_new, "seed": seed},
+ "context_clipped": False, "parent": None, "sampling_logprobs": lps,
+ "adapter": {"serving": "unmerged_vllm_lora"},
+ })
+ R.write_jsonl_atomic(os.path.join(d, "gens.jsonl"), recs)
+
+
+class FakeVerifier:
+ """accepted iff the completion contains an even token > 10 in its first 3 tokens (learnable, ~50% under a
+ random policy); every 9th sample by key hash is a TIMEOUT (no signal)."""
+
+ def __init__(self, timeout_mod: int = 9):
+ self.timeout_mod = timeout_mod
+ self.calls = 0
+
+ def verify(self, d, problems_path) -> None:
+ self.calls += 1
+ out = []
+ for g in R.read_jsonl(os.path.join(d, "gens.jsonl")):
+ key = (g["problem_id"], g["sample_idx"], g["round"], g["cap_tokens"])
+ timeout = self.timeout_mod and _key_hash(*key) % self.timeout_mod == 0
+ ok = any(t % 2 == 0 and t > 10 for t in g["completion_ids"][:3])
+ out.append({"problem_id": key[0], "sample_idx": key[1], "round": key[2], "cap_tokens": key[3],
+ "accepted": bool(ok and not timeout), "reasons": ["timeout"] if timeout else ([] if ok else ["compile_error"]),
+ "verify_s": 0.25, "budget_s": 1.0, "verifier": {"fake": True},
+ "code_sha256": hashlib.sha256(g["lean_code"].encode()).hexdigest(), "stage": "fake", "diagnostics": {}})
+ R.write_jsonl_atomic(os.path.join(d, "ver.jsonl"), out)
+
+
+def synthetic_rollouts(n_prompts: int, G: int, seed: int, timeouts: int = 0, dead_prompts: int = 0,
+ cap: int = 64) -> tuple[list[dict], dict]:
+ """Random gens + verdicts with UNEVEN lengths, behaviour logprobs unrelated to any model (so the importance
+ ratio spans well below 1 and above the clamp), `dead_prompts` all-equal groups and `timeouts` masked samples."""
+ rng = random.Random(seed)
+ gens, vers = [], {}
+ for pi in range(n_prompts):
+ pid = f"p{pi:03d}"
+ prompt = prompt_ids_for(pid)
+ dead = pi < dead_prompts
+ for si in range(G):
+ n = rng.randrange(1, 10)
+ comp = [rng.randrange(3, TINY.vocab_size) for _ in range(n)]
+ lps = [-rng.uniform(0.05, 4.0) for _ in range(n)]
+ gens.append({"problem_id": pid, "sample_idx": si, "round": 0, "cap_tokens": cap, "prompt_ids": prompt,
+ "completion_ids": comp, "finish_reason": "stop", "lean_code": "x",
+ "sampling": {"temperature": 1.0, "top_p": 1.0, "top_k": 0}, "sampling_logprobs": lps})
+ acc = False if dead else (rng.random() < 0.5)
+ reasons = [] if acc else ["compile_error"]
+ vers[(pid, si, 0, cap)] = {"accepted": acc, "reasons": reasons, "verify_s": 0.1}
+ keys = list(vers)
+ for key in rng.sample([k for k in keys if int(k[0][1:]) >= dead_prompts], timeouts):
+ vers[key] = {"accepted": False, "reasons": ["timeout"], "verify_s": 5.0}
+ return gens, vers
diff --git a/code/align/rl/multiturn_source.py b/code/align/rl/multiturn_source.py
new file mode 100644
index 0000000000000000000000000000000000000000..9c2814a74a8fa8febd4389ce71665f7cfe0a3f50
--- /dev/null
+++ b/code/align/rl/multiturn_source.py
@@ -0,0 +1,454 @@
+"""Multi-turn Lean rollouts -> GRPO rows with PER-TOKEN advantages (opt-in).
+
+`GRPOTrainModule` used to see one scalar advantage per row, so the credit the
+multi-turn env computes per turn could not reach the loss. This module is the
+producer side of the fix (the consumer side is `expand_advantages` in
+grpo_loss.py): it runs `align/rl/envs/lean_multiturn.py` episodes, assigns
+advantages, and emits rows whose ``advantages`` is [B, T] in INPUT space,
+nonzero only on tokens the policy generated.
+
+Every design choice below names the failure it prevents.
+
+ROW LAYOUT: one row per TURN (the env's ``layout="per_turn"``, the default).
+ * A packed row per episode is valid only when the chat template renders
+ turn k's prompt as an exact extension of turn k-1's prompt + completion.
+ Qwen3-family templates (Goedel-Prover-V2 is one) strip earlier
+ blocks, so the env raises `PrefixBreak` on them; per-turn rows are exact on
+ every template because each row carries the exact prompt ids the policy
+ saw at that turn.
+ * Memory: a per-turn row is bounded by one turn's prompt + completion, so
+ micro-batch padding is bounded by the single longest TURN. A packed row is
+ the SUM of all turns' context and pads every micro-batch row to that sum.
+ Per-turn rows pay for it by re-running the shared prefix (~K/2 x extra
+ forward tokens at K turns) -- accepted: it is bounded and the alternative
+ cannot always be built.
+ * Rows are independent in the loss. The Dr.GRPO denominator is a constant
+ and advantages are already per-token, so no statistic crosses rows: a
+ micro-slice or rank boundary that splits one episode's rows changes
+ nothing (a test pins that the summed loss is invariant to row order and
+ split). The packed layout is still accepted (the env's ``layout=
+ "concatenated"``) when the template permits it; the same masks and
+ advantages come out.
+ * DENOMINATOR: the constant D is per OPTIMIZER STEP. A multi-turn episode
+ can generate max_turns x max_response_len tokens, so D should be sized
+ prompts_per_step x G x max_turns x max_response_len
+ (`recommended_loss_denominator`); sizing it for one turn silently scales
+ every gradient by up to max_turns. The A3 gate still only requires a
+ positive finite float; the value is the caller's pre-registered choice.
+
+CREDIT (`MultiTurnCreditConfig`):
+ mode="episode" (DEFAULT): one number per episode, A_ep = R_ep - mean_group(R),
+ the Dr.GRPO centered advantage over the episode's TOTAL reward, broadcast
+ to EVERY generated token of EVERY turn. Prevents: a baseline computed on a
+ thinned sample (see below) and the variance of fitting a per-turn value
+ function from G <= 16 episodes. It is the exact single-turn algebra, so
+ group centering, dead-group semantics and A3 all carry over unchanged.
+ Its cost is credit blur: a failed first draft is rewarded for the repair
+ that followed. That is wrong only if the draft did not make the repair
+ possible; with turn_discount < 1 the solved-late episode already scores
+ lower than a solved-first one, which is the ordering we want taught.
+ mode="per_turn_loo": turn t gets G_t - b_t, G_t = sum_{s>=t} gamma^(s-t) u_s
+ (the env's return; gamma=0 -> each turn judged on its own outcome).
+ turn 0 : b_0 = mean over the WHOLE group. Every episode has a turn 0,
+ so this is the ordinary group baseline and the turn-0
+ advantages are exactly centered.
+ turn t>=1: b_t = mean over the OTHER episodes of the group that reached
+ turn t (leave-one-out). The old rule averaged over ALL
+ episodes that reached turn t INCLUDING self; with one repair
+ in the group the baseline was the sample's own return, so the
+ lone repair that SOLVED the problem got advantage exactly 0
+ (and one that failed also 0). LOO removes self-inclusion, and
+ the LOO advantages of a cell sum to exactly 0 (x_i - mean_{-i}
+ = n/(n-1) (x_i - mean)).
+ lone cell: if no other episode of the group reached turn t there is no
+ within-group peer. The baseline is then the mean of the other
+ episodes at turn t POOLED across the other groups of the same
+ rollout step (>= ``min_pool_peers`` of them; still leave-one-
+ out and independent of the sample's own action, so the
+ estimator stays unbiased). With fewer, the cell's advantage
+ is 0 and counted (`credit/cells_no_baseline`) -- a baseline
+ invented from nothing would be a made-up gradient.
+ Pooled cells trade within-group centering for a baseline that exists; the
+ telemetry counts how many cells took each path.
+
+DEAD GROUPS. "episode": dead iff the group's episode rewards have zero
+variance, classified all_zero / all_solved / nonzero_std0 by the shared
+`GroupFilterStats` (max_possible_score=1.0: the best episode reward). "per_turn
+_loo": dead iff EVERY token advantage of the group is exactly 0 -- the group
+contributes no gradient, so keeping it only spends compute; classified from
+the rewards the same way. Dead groups are dropped whole, as in single-turn
+assembly; an all-dead step returns None (the driver resamples).
+
+No CUDA, no Lean here: the env, generator and verifier are injected.
+"""
+
+from __future__ import annotations
+
+import hashlib
+from collections import OrderedDict
+from dataclasses import dataclass, field
+from typing import Any, Callable, Optional, Sequence
+
+import numpy as np
+import torch
+
+from align.rl.advantages import GroupFilterStats, compute_group_advantages
+from align.rl.batch_source import GrpoBatchSource
+from align.rl.envs.lean_multiturn import (
+ EpisodeRecord,
+ EpisodeSpec,
+ LeanMultiTurnEnv,
+ Problem,
+)
+
+CREDIT_MODES = ("episode", "per_turn_loo")
+
+
+@dataclass(frozen=True)
+class MultiTurnCreditConfig:
+ mode: str = "episode"
+ #: per-turn return discount (per_turn_loo only): 0 = a turn is judged on its own outcome,
+ #: >0 = an earlier turn shares in the later turns' outcomes
+ gamma: float = 0.0
+ #: pooled-baseline floor for a turn no group-mate reached (per_turn_loo only)
+ min_pool_peers: int = 2
+
+ def __post_init__(self) -> None:
+ if self.mode not in CREDIT_MODES:
+ raise ValueError(f"mode must be one of {CREDIT_MODES}, got {self.mode!r}")
+ if not 0.0 <= self.gamma <= 1.0:
+ raise ValueError(f"gamma must be in [0, 1], got {self.gamma}")
+ if self.min_pool_peers < 1:
+ raise ValueError(f"min_pool_peers must be >= 1, got {self.min_pool_peers}")
+ if self.mode == "episode" and self.gamma != 0.0:
+ raise ValueError("gamma is read only by mode='per_turn_loo'; it would be silently ignored")
+
+
+def recommended_loss_denominator(
+ prompts_per_step: int, n_samples: int, max_turns: int, max_response_len: int
+) -> float:
+ """Dr.GRPO constant sized for the worst-case GENERATED tokens of one optimizer step."""
+ return float(prompts_per_step * n_samples * max_turns * max_response_len)
+
+
+# ---------------------------------------------------------------------------- credit
+
+
+def turn_returns(u: Sequence[float], gamma: float) -> list[float]:
+ """G_t = sum_{s>=t} gamma^(s-t) u_s (the env's definition)."""
+ out, acc = [0.0] * len(u), 0.0
+ for t in range(len(u) - 1, -1, -1):
+ acc = u[t] + gamma * acc
+ out[t] = acc
+ return out
+
+
+@dataclass
+class CreditResult:
+ #: per episode (input order), per turn advantage
+ turn_adv: list
+ #: path taken per (episode, turn) cell: "episode" | "group_mean" | "group_loo" | "pooled" | "none"
+ cell_path: list
+ counts: dict = field(default_factory=dict)
+
+
+def compute_turn_credit(episodes: Sequence[EpisodeRecord], credit: MultiTurnCreditConfig) -> CreditResult:
+ """Advantage per (episode, turn). Episodes of one problem_id form a group."""
+ n = len(episodes)
+ groups: "OrderedDict[str, list[int]]" = OrderedDict()
+ for i, e in enumerate(episodes):
+ groups.setdefault(e.problem_id, []).append(i)
+
+ ep_adv = [0.0] * n
+ for idx in groups.values():
+ a = compute_group_advantages(
+ np.array([episodes[i].reward["reward/total"] for i in idx]), len(idx), "centered"
+ )
+ for i, v in zip(idx, a):
+ ep_adv[i] = float(v)
+
+ n_turns = [len(e.turns) for e in episodes]
+ if credit.mode == "episode":
+ return CreditResult(
+ [[ep_adv[i]] * n_turns[i] for i in range(n)],
+ [["episode"] * n_turns[i] for i in range(n)],
+ {"credit/cells_episode": sum(n_turns)},
+ )
+
+ G = [turn_returns(e.turn_rewards, credit.gamma) for e in episodes]
+ turn_adv = [[0.0] * n_turns[i] for i in range(n)]
+ path = [[""] * n_turns[i] for i in range(n)]
+ counts = {"credit/cells_group_mean": 0, "credit/cells_group_loo": 0,
+ "credit/cells_pooled": 0, "credit/cells_no_baseline": 0}
+ for idx in groups.values():
+ for i in idx: # turn 0: the ordinary group baseline (every episode has a turn 0)
+ base = sum(G[j][0] for j in idx) / len(idx)
+ turn_adv[i][0] = G[i][0] - base
+ path[i][0] = "group_mean"
+ counts["credit/cells_group_mean"] += 1
+ for gidx in groups.values():
+ for i in gidx:
+ for t in range(1, n_turns[i]):
+ peers = [G[j][t] for j in gidx if j != i and n_turns[j] > t]
+ if peers:
+ turn_adv[i][t] = G[i][t] - sum(peers) / len(peers)
+ path[i][t] = "group_loo"
+ counts["credit/cells_group_loo"] += 1
+ continue
+ pool = [G[j][t] for j in range(n) if j != i and n_turns[j] > t and j not in gidx]
+ if len(pool) >= credit.min_pool_peers:
+ turn_adv[i][t] = G[i][t] - sum(pool) / len(pool)
+ path[i][t] = "pooled"
+ counts["credit/cells_pooled"] += 1
+ else:
+ path[i][t] = "none"
+ counts["credit/cells_no_baseline"] += 1
+ return CreditResult(turn_adv, path, counts)
+
+
+# ---------------------------------------------------------------------------- assembly
+
+
+@dataclass
+class AssembledMultiTurn:
+ """Duck-types `AssembledRollout` (the fields `GrpoBatchSource._produce_step` reads),
+ except ``advantages`` is [B, T] in input space."""
+
+ input_ids: torch.Tensor
+ attention_mask: torch.Tensor
+ response_mask: torch.Tensor
+ advantages: torch.Tensor
+ vllm_logprobs: torch.Tensor
+ group_ids: torch.Tensor
+ rewards: dict
+ turn_ids: torch.Tensor
+ stats: dict = field(default_factory=dict)
+ kept_groups: int = 0
+ dropped_groups: int = 0
+
+
+#: A group is dead below this spread. `np.std(...) == 0` (the single-turn rule) is an EXACT test, and
+#: with discounted multi-turn rewards it misfires: seven episodes that all solved on turn 1 at
+#: discount 0.9 have std 1.1e-16, not 0, because ``mean(x, x, ..., x) != x`` in float64. Such a group
+#: was kept with advantages of +-1e-16 (a zero-gradient group paid for in full, and counted as
+#: ``groups/kept``). Rewards live in [-1, 1] and real differences are >= ~1e-3, so 1e-9 separates
+#: rounding noise from signal by six orders of magnitude on each side.
+DEAD_ATOL = 1e-9
+
+
+def _observe_group(stats: GroupFilterStats, rewards: np.ndarray, live: bool) -> None:
+ """Record one group in the three-way telemetry with an externally decided liveness."""
+ if live:
+ stats.kept += 1
+ return
+ if np.all(np.abs(rewards) <= DEAD_ATOL):
+ stats.all_zero += 1
+ elif np.all(np.abs(rewards - stats.max_possible_score) <= DEAD_ATOL):
+ stats.all_solved += 1
+ else:
+ stats.nonzero_std0 += 1
+
+
+def assemble_multiturn(
+ episodes: Sequence[EpisodeRecord],
+ credit: MultiTurnCreditConfig,
+ pad_token_id: int,
+ filter_stats: Optional[GroupFilterStats] = None,
+) -> Optional[AssembledMultiTurn]:
+ """Episodes -> one training batch with per-token advantages; None if every group is dead."""
+ if not episodes:
+ raise ValueError("no episodes")
+ stats = filter_stats if filter_stats is not None else GroupFilterStats(max_possible_score=1.0)
+ res = compute_turn_credit(episodes, credit)
+
+ groups: "OrderedDict[str, list[int]]" = OrderedDict()
+ for i, e in enumerate(episodes):
+ groups.setdefault(e.problem_id, []).append(i)
+
+ keep: list[list[int]] = []
+ for idx in groups.values():
+ rewards = np.array([episodes[i].reward["reward/total"] for i in idx], dtype=np.float64)
+ if credit.mode == "episode":
+ # the single-turn rule (zero reward variance) with a rounding tolerance, see DEAD_ATOL
+ dead = float(np.ptp(rewards)) <= DEAD_ATOL
+ else:
+ dead = not any(abs(a) > DEAD_ATOL for i in idx for a in res.turn_adv[i])
+ _observe_group(stats, rewards, live=not dead)
+ if not dead:
+ keep.append(idx)
+ if not keep:
+ return None
+
+ rows = [] # (group_index, episode, row, per-token adv)
+ for gi, idx in enumerate(keep):
+ for i in idx:
+ ep = episodes[i]
+ for row in ep.rows:
+ if row.logprobs is None:
+ raise ValueError(
+ f"episode {ep.episode_key}: generation carries no logprobs; the rho "
+ "correction cannot align without the engine's per-token logprobs"
+ )
+ adv = [res.turn_adv[i][tid] if tid >= 0 else 0.0 for tid in row.turn_ids]
+ rows.append((gi, ep, row, adv))
+ B = len(rows)
+ T = max(len(r.input_ids) for _, _, r, _ in rows)
+ input_ids = torch.full((B, T), pad_token_id, dtype=torch.int64)
+ attention_mask = torch.zeros((B, T), dtype=torch.int64)
+ response_mask = torch.zeros((B, T), dtype=torch.int64)
+ turn_ids = torch.full((B, T), -1, dtype=torch.int64)
+ advantages = torch.zeros((B, T), dtype=torch.float32)
+ vllm_lp = torch.zeros((B, T - 1), dtype=torch.float32)
+ group_ids = torch.zeros((B,), dtype=torch.int64)
+ term_names = list(rows[0][1].reward)
+ reward_tensors = {k: torch.zeros(B, dtype=torch.float32) for k in term_names}
+ for b, (gi, ep, row, adv) in enumerate(rows):
+ L = len(row.input_ids)
+ if not (len(row.response_mask) == len(row.turn_ids) == len(row.logprobs) == L):
+ raise ValueError(f"row {b} of episode {ep.episode_key} is ragged across ids/mask/turn_ids/logprobs")
+ # Credit is placed by ``turn_ids`` and trained on by ``response_mask``. If the two disagree
+ # by even one position, the loss-side stray check (`expand_advantages`) catches it only
+ # when the misplaced credit lands on an excluded position that exists: a row whose last
+ # generated token is its last column (every micro-batch's longest per-turn row) has no
+ # position past it, so a right-shifted span slips through. Refuse here, where it is exact.
+ if [int(t >= 0) for t in row.turn_ids] != [int(m) for m in row.response_mask]:
+ raise ValueError(
+ f"row {b} of episode {ep.episode_key}: turn_ids >= 0 does not coincide with "
+ f"response_mask; per-token credit would be misaligned with the trained tokens"
+ )
+ input_ids[b, :L] = torch.tensor(row.input_ids, dtype=torch.int64)
+ attention_mask[b, :L] = 1
+ response_mask[b, :L] = torch.tensor(row.response_mask, dtype=torch.int64)
+ turn_ids[b, :L] = torch.tensor(row.turn_ids, dtype=torch.int64)
+ advantages[b, :L] = torch.tensor(adv, dtype=torch.float32)
+ # label position j <-> input token j+1
+ vllm_lp[b, : L - 1] = torch.tensor(row.logprobs[1:], dtype=torch.float32)
+ group_ids[b] = gi
+ for k in term_names:
+ reward_tensors[k][b] = float(ep.reward[k])
+
+ kept_eps = [episodes[i] for idx in keep for i in idx]
+ gsz = [len(idx) for idx in keep]
+ first = [e.solved_turn == 0 for e in kept_eps]
+ failed_first = [e for e in kept_eps if e.solved_turn != 0]
+ stat = {
+ "multiturn/episodes": float(len(kept_eps)),
+ "multiturn/rows": float(B),
+ "multiturn/turns_mean": float(np.mean([len(e.turns) for e in kept_eps])),
+ "multiturn/solved_turn0": float(np.mean(first)),
+ "multiturn/solved": float(np.mean([e.solved_turn is not None for e in kept_eps])),
+ "multiturn/repair_rate": (
+ float(np.mean([e.solved_turn is not None for e in failed_first])) if failed_first else float("nan")
+ ),
+ "multiturn/generated_tokens_mean": float(np.mean([e.n_generated for e in kept_eps])),
+ "multiturn/group_size_min": float(min(gsz)),
+ "multiturn/group_size_max": float(max(gsz)),
+ **{k: float(v) for k, v in res.counts.items()},
+ **{k: float(v) for k, v in stats.as_log_dict().items()},
+ }
+ return AssembledMultiTurn(
+ input_ids, attention_mask, response_mask, advantages, vllm_lp, group_ids, reward_tensors,
+ turn_ids, stat, kept_groups=len(keep), dropped_groups=len(groups) - len(keep),
+ )
+
+
+# ---------------------------------------------------------------------------- the batch source
+
+
+class MultiTurnBatchSource(GrpoBatchSource):
+ """`GrpoBatchSource` whose rollout is a batch of multi-turn Lean episodes.
+
+ Everything downstream of rollout production (rank-0 produce + broadcast,
+ row slicing, no-grad old_logprobs, the validated batch, the collapse
+ tripwire, the draw-position resume state) is inherited unchanged; only
+ `_rollout_once` differs. Rows are sliced along dim 0 and the [B, T]
+ advantages slice with them.
+
+ ``prompt_rows``: dicts with ``formal_statement`` (or ``problem``) and a
+ UNIQUE ``problem_id`` (the group key; duplicates are refused -- two
+ problems sharing an id would be centered against each other).
+ ``generate_factory(seed=..., weights_dir=...)`` returns the env's
+ ``generate(list[GenRequest]) -> list[Generation]`` for this step.
+ """
+
+ def __init__(
+ self,
+ prompt_rows: Sequence[dict[str, Any]],
+ *,
+ env: LeanMultiTurnEnv,
+ credit: MultiTurnCreditConfig = MultiTurnCreditConfig(),
+ prompts_per_step: int,
+ n_samples_per_prompt: int,
+ max_response_len: int,
+ temperature: float,
+ optimizer_steps: int,
+ grad_accum: int,
+ seed: int,
+ dead_group_retries: int = 5,
+ ):
+ rows, seen = [], set()
+ for i, r in enumerate(prompt_rows):
+ stmt = r.get("formal_statement", r.get("problem"))
+ if not isinstance(stmt, str) or not stmt:
+ raise ValueError(f"prompt row {i} lacks formal_statement/problem: {sorted(r)}")
+ pid = str(r.get("problem_id") or hashlib.sha256(stmt.encode()).hexdigest()[:12])
+ if pid in seen:
+ raise ValueError(f"duplicate problem_id {pid!r}: it is the advantage group key")
+ seen.add(pid)
+ rows.append({"problem": stmt, "answer": "", "problem_id": pid})
+ super().__init__(
+ rows,
+ prompts_per_step=prompts_per_step,
+ n_samples_per_prompt=n_samples_per_prompt,
+ max_response_len=max_response_len,
+ temperature=temperature,
+ optimizer_steps=optimizer_steps,
+ grad_accum=grad_accum,
+ seed=seed,
+ prompt_builder=lambda p: p, # unused: the env builds the Goedel prompts
+ dead_group_retries=dead_group_retries,
+ )
+ self.env = env
+ self.credit = credit
+ # Multi-turn episode reward is <= 1.0 (turn_discount ** 0); the single-turn
+ # source's 1.1 (accuracy + format bonus) would misfile all-solved groups.
+ self.filter_stats.max_possible_score = 1.0
+ self.last_rollout_stats: dict[str, float] = {}
+
+ def bind( # noqa: D401 - deliberately narrower than the parent's (no client/reward/verifier)
+ self,
+ *,
+ generate_factory: Callable[..., Callable[[list], list]],
+ old_logprob_fn,
+ weights_dir_fn,
+ rank: int,
+ world_size: int,
+ device: str = "cpu",
+ canary=None,
+ canary_generate_fn=None,
+ collapse_tripwire=None,
+ trainer=None,
+ canary_log_path=None,
+ ) -> None:
+ self.generate_factory = generate_factory
+ super().bind(
+ client=None, reward=None, verifier=None, old_logprob_fn=old_logprob_fn,
+ weights_dir_fn=weights_dir_fn, rank=rank, world_size=world_size, device=device,
+ canary=canary, canary_generate_fn=canary_generate_fn, collapse_tripwire=collapse_tripwire,
+ trainer=trainer, canary_log_path=canary_log_path,
+ )
+
+ def _rollout_once(self, indices: list[int], optimizer_step: int = 0) -> Optional[AssembledMultiTurn]:
+ specs = [
+ EpisodeSpec(Problem(self.prompt_rows[i]["problem_id"], self.prompt_rows[i]["problem"]), s)
+ for i in indices
+ for s in range(self.n_samples_per_prompt)
+ ]
+ generate = self.generate_factory(
+ seed=self.rollout_seed(optimizer_step), weights_dir=self.weights_dir_fn()
+ )
+ episodes = self.env.run_group(specs, generate)
+ assembled = assemble_multiturn(episodes, self.credit, self.pad_token_id, self.filter_stats)
+ if assembled is not None:
+ self.last_rollout_stats = assembled.stats
+ return assembled
diff --git a/code/align/rl/ops.py b/code/align/rl/ops.py
new file mode 100644
index 0000000000000000000000000000000000000000..afa012c67f8c38c164f0f13326bc03451723b028
--- /dev/null
+++ b/code/align/rl/ops.py
@@ -0,0 +1,99 @@
+"""Pure tensor ops for the GRPO layer.
+
+Vendored from allenai/open-instruct @ 8759c91:
+ masked_mean <- open_instruct/rl_utils.py:426-448
+ entropy_from_logits <- open_instruct/model_utils.py:326-334
+ log_softmax_and_gather <- open_instruct/model_utils.py:588-601
+ estimate_kl <- open_instruct/model_utils.py:793-819
+
+Bodies are verbatim; only the module docstrings/imports differ. Any edit to
+these functions breaks parity with the upstream line citations in
+docs/research/nmo-research/docs/04_ALLENAI_CODEBASE_MAP.md and must be
+recorded as a deliberate divergence, not slipped in.
+"""
+
+from __future__ import annotations
+
+from typing import Optional, Union
+
+import torch
+
+
+def masked_mean(
+ values: torch.Tensor,
+ mask: torch.Tensor,
+ axis: Optional[int] = None,
+ denominator: Optional[float] = None,
+) -> torch.Tensor:
+ """Compute mean of tensor with masked values.
+
+ Returns 0 if mask is empty (no valid elements) to avoid division by zero.
+ This can happen with sequence parallel when a chunk contains only
+ query/padding tokens.
+
+ A3 note: passing a constant float ``denominator`` is what realises the
+ Dr.GRPO constant-global-denominator form; leaving it None realises the
+ DAPO token-mean. align's conformance gate requires the float.
+ """
+ extra_dims = values.ndim - mask.ndim
+ if axis is None:
+ sum_dims: int | tuple[int, ...] = tuple(range(extra_dims, values.ndim))
+ elif axis >= 0:
+ sum_dims = axis + extra_dims
+ else:
+ sum_dims = axis
+ numerator = (values * mask).sum(dim=sum_dims)
+ denom = mask.sum(dim=axis) if denominator is None else denominator
+ if isinstance(denom, torch.Tensor):
+ result = torch.where(denom > 0, numerator / denom, torch.zeros_like(numerator))
+ else:
+ result = numerator / denom if denom > 0 else torch.zeros_like(numerator)
+ return result.flatten(extra_dims).mean(-1) if result.ndim > extra_dims else result
+
+
+def entropy_from_logits(logits: torch.Tensor) -> torch.Tensor:
+ """Compute the entropy of the logits.
+
+ Upstream credit chain: open-instruct borrowed this from verl
+ (verl/utils/torch_functional.py). logsumexp form avoids materialising
+ log-probabilities next to probabilities.
+ """
+ pd = torch.nn.functional.softmax(logits, dim=-1)
+ entropy = torch.logsumexp(logits, dim=-1) - torch.sum(pd * logits, dim=-1)
+ return entropy
+
+
+def log_softmax_and_gather(logits: torch.Tensor, index: torch.Tensor) -> torch.Tensor:
+ """`log_softmax -> gather` without allocating a [B, T, V] logprob tensor.
+
+ Upstream compiles this (open-instruct PR #584); align leaves compilation to
+ the caller because torch.compile on this box requires the vendored-header
+ toolchain fix (align/kernels/_toolchain.py) and is opted into per-site.
+ """
+ logprobs = logits.log_softmax(dim=-1)
+ return torch.gather(logprobs, dim=-1, index=index.unsqueeze(-1)).squeeze(-1)
+
+
+def estimate_kl(ref_logprobs_diff: torch.Tensor, ratio: torch.Tensor) -> torch.Tensor:
+ """Compute 4 KL divergence estimators between current and reference policies.
+
+ Args:
+ ref_logprobs_diff: (new_logprobs - ref_logprobs), pre-clamped to
+ [-40, 40] by the caller for numerical stability.
+ ratio: exp(new_logprobs - old_logprobs) vs the step-start policy.
+
+ Returns:
+ Tensor of shape [4, *ref_logprobs_diff.shape]:
+ [0] linear approximation
+ [1] quadratic approximation (diff^2 / 2)
+ [2] numerically stable form (expm1(-diff) + diff) -- default
+ [3] importance-weighted (ratio * diff)
+ """
+ return torch.stack(
+ [
+ ref_logprobs_diff,
+ (ref_logprobs_diff) ** 2 / 2,
+ torch.expm1(-ref_logprobs_diff) + ref_logprobs_diff,
+ ratio * ref_logprobs_diff,
+ ]
+ )
diff --git a/code/align/rl/rewards.py b/code/align/rl/rewards.py
new file mode 100644
index 0000000000000000000000000000000000000000..d80a4d5eda41b185cd14dc95e3fce16813c3c5ac
--- /dev/null
+++ b/code/align/rl/rewards.py
@@ -0,0 +1,98 @@
+"""Reward composition -- the ONE genuinely net-new A3 piece.
+
+The corpus's conformance table (04_ALLENAI_CODEBASE_MAP.md item 9) found soft
+overlong punishment absent from open-instruct entirely (its
+``mask_truncated_completions`` is DAPO's HARD binary filter, a different
+mechanism). Per the same table's architectural note: this lives in the
+REWARD-COMPOSITION layer, never in the loss -- keeping compute_grpo_loss
+provably unmodified from upstream is an auditability property of the
+pre-registration, not a style preference.
+
+soft_overlong_punishment implements DAPO eq. (arXiv:2503.14476, "Soft
+Overlong Punishment"): zero inside the budget, linear ramp to -1 across the
+final ``cache`` tokens, -1 beyond max length.
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass
+from typing import Optional, Sequence
+
+
+def soft_overlong_punishment(response_len: int, max_len: int, cache_len: int) -> float:
+ """DAPO soft length penalty in [-1, 0].
+
+ 0 for len <= max_len - cache_len
+ ((max_len - cache_len) - len)/cache_len inside the buffer (linear to -1)
+ -1 for len > max_len
+ """
+ if cache_len <= 0 or cache_len >= max_len:
+ raise ValueError(f"need 0 < cache_len < max_len, got cache={cache_len} max={max_len}")
+ soft_start = max_len - cache_len
+ if response_len <= soft_start:
+ return 0.0
+ if response_len > max_len:
+ return -1.0
+ return (soft_start - response_len) / cache_len
+
+
+@dataclass
+class CompositeReward:
+ """A3 reward: verifier accuracy + soft format term + soft overlong penalty.
+
+ ``accuracy`` and ``format_score`` arrive from the verifier layer (rule
+ verifier only -- no PRM/neural RM, A3's hardest lock and the field's
+ strongest cross-lab consensus per doc 08). This class only composes and
+ itemises them so history.jsonl can log each term separately -- the
+ format/structural-consistency instrument (doc 09 amendment) reads the
+ per-term stream, not the sum.
+ """
+
+ format_weight: float = 0.1
+ overlong_max_len: Optional[int] = None
+ overlong_cache_len: Optional[int] = None
+
+ def __post_init__(self) -> None:
+ # Fail at CONSTRUCTION, not at the first rollout. The dangerous half is
+ # cache_len set with max_len unset: __call__ takes the "overlong off"
+ # branch and the soft length penalty silently never applies while the
+ # config reads as if it were armed (a disarmed-instrument shape).
+ if self.overlong_max_len is None and self.overlong_cache_len is not None:
+ raise ValueError(
+ f"overlong_cache_len={self.overlong_cache_len} given without overlong_max_len: "
+ "the soft overlong penalty would be silently OFF"
+ )
+ if self.overlong_max_len is not None:
+ if self.overlong_cache_len is None:
+ raise ValueError("overlong_cache_len required when overlong_max_len is set")
+ if not 0 < self.overlong_cache_len < self.overlong_max_len:
+ raise ValueError(
+ f"need 0 < overlong_cache_len < overlong_max_len, got "
+ f"cache={self.overlong_cache_len} max={self.overlong_max_len}"
+ )
+
+ def __call__(self, accuracy: float, format_score: float, response_len: int) -> dict[str, float]:
+ terms: dict[str, float] = {
+ "reward/accuracy": float(accuracy),
+ "reward/format": self.format_weight * float(format_score),
+ }
+ if self.overlong_max_len is not None:
+ if self.overlong_cache_len is None:
+ raise ValueError("overlong_cache_len required when overlong_max_len is set")
+ terms["reward/overlong"] = soft_overlong_punishment(
+ response_len, self.overlong_max_len, self.overlong_cache_len
+ )
+ else:
+ terms["reward/overlong"] = 0.0
+ terms["reward/total"] = (
+ terms["reward/accuracy"] + terms["reward/format"] + terms["reward/overlong"]
+ )
+ return terms
+
+ def batch(
+ self, accuracies: Sequence[float], format_scores: Sequence[float], lengths: Sequence[int]
+ ) -> list[float]:
+ return [
+ self(a, f, n)["reward/total"]
+ for a, f, n in zip(accuracies, format_scores, lengths, strict=True)
+ ]
diff --git a/code/align/rl/stage_config.py b/code/align/rl/stage_config.py
new file mode 100644
index 0000000000000000000000000000000000000000..eb0d477d4f0e3dcb8711d008283fd46b1e61ea13
--- /dev/null
+++ b/code/align/rl/stage_config.py
@@ -0,0 +1,253 @@
+"""Typed stage-b config: configs/grpo_stageb.toml -> dataclasses + launch refusals.
+
+Deliberately NOT align.config.RunConfig: that is the trainer's surface, this is
+the stage's pre-registration. Collapsing them would let a trainer default
+silently become an experimental parameter (the same separation dpo_stage
+draws). The trainer still gets a RunConfig -- built FROM this, explicitly,
+in scripts/grpo.py.
+
+Every check in `validate()` is a launch refusal for a failure phase-1 paid
+for: the denominator arithmetic (obligation a's config half), the
+save-cadence/weight-sync alignment (a drifted engine trains against stale
+rho baselines silently), the ceiling receipt (the 0/100-perfect-policy
+trap), max_grad_norm (R3), a live canary cadence (R4).
+"""
+
+from __future__ import annotations
+
+import json
+import os
+from dataclasses import dataclass, field
+
+from align.rl.grpo_loss import GRPOLossConfig, GRPOLossType
+
+
+@dataclass
+class RolloutShape:
+ prompts_per_step: int
+ max_response_len: int
+ temperature: float = 1.0
+ overlong_cache_len: int = 128
+ #: Where completions come from. "spool" = the external vLLM worker (own venv,
+ #: extras/vllm_rollout; the backend the pre-registration describes) and is
+ #: the default so an existing config keeps its generation engine. "native" =
+ #: the in-repo torch sampler on a STANDALONE engine copy on a dedicated GPU
+ #: (NMO_ROLLOUT_GPU), synced by reloading the latest checkpoint; GPU-unverified
+ #: at 7B at the time of the port, so it is opt-in. A silent switch changes
+ #: throughput by an order of magnitude and the logprob basis (native logprobs
+ #: are of the temperature-scaled distribution), hence never inferred.
+ backend: str = "spool"
+ #: INFORMATIONAL ONLY: configs/grpo_stageb.toml carries `vllm_env` as a note of the
+ #: worker environment the pre-registration ran under. Nothing reads it -- the env is
+ #: pinned in code (align/rollout/client.py) -- so it is declared here, rather than
+ #: left to the unknown-key refusal, purely so that refusal can be strict for every
+ #: OTHER key. A value here does NOT change the worker environment.
+ vllm_env: str = ""
+
+
+@dataclass
+class RewardCfg:
+ format_weight: float = 0.1
+
+
+@dataclass
+class DataCfg:
+ band_file: str
+ banding_manifest: str
+
+
+@dataclass
+class TrainCfg:
+ lr: float
+ optimizer_steps: int
+ warmup_frac: float = 0.03
+ micro_batch_size: int = 1
+ save_every_micro_steps: int = 100
+ keep_checkpoints: int = 2
+ #: Per-rank optimizer shards (TrainConfig.per_rank_optimizer_state); forwarded
+ #: by scripts/grpo.py::_run_config. Default False = gathered layout.
+ per_rank_optimizer_state: bool = False
+ max_grad_norm: float | None = None
+ seed: int = 41
+ optimizer_name: str = "adamw8bit"
+ grad_accum: int = 8
+ attn_implementation: str = "sdpa"
+
+ # -- run-health bounds (G0), forwarded to TrainConfig by the stage script's
+ # _run_config: a field that is not copied trains at the default while the
+ # TOML says otherwise. Semantics: align/config.py::TrainConfig.
+ max_wall_seconds: float | None = None
+ max_consecutive_skipped_steps: int = 3
+ max_skipped_fraction: float = 0.05
+
+
+@dataclass
+class CanaryCfg:
+ every: int
+ n_problems: int
+ source: str = "gsm8k"
+
+
+@dataclass
+class GrpoStageConfig:
+ sft_checkpoint: str
+ run_dir: str
+ grpo: GRPOLossConfig
+ rollout: RolloutShape
+ reward: RewardCfg
+ data: DataCfg
+ train: TrainCfg
+ canary: CanaryCfg
+ armed_tripwires: tuple[str, ...] = ("grad_norm",)
+ ceiling_receipt: str = "outputs/eval/ceiling_test.json"
+ rollout_timeout_s: float = 1800.0
+ dead_group_retries: int = 5
+ extras: dict = field(default_factory=dict)
+
+ def validate(self) -> None:
+ g = self.grpo
+ if self.rollout.backend not in ("spool", "native"):
+ raise ValueError(
+ f"rollout.backend {self.rollout.backend!r}: choose 'spool' (external vLLM "
+ "worker) or 'native' (in-repo torch sampler)"
+ )
+ expected = float(
+ self.rollout.prompts_per_step
+ * g.num_samples_per_prompt_rollout
+ * self.rollout.max_response_len
+ )
+ if not isinstance(g.loss_denominator, float) or g.loss_denominator != expected:
+ raise ValueError(
+ f"loss_denominator {g.loss_denominator!r} != prompts_per_step x G x "
+ f"max_response_len = {self.rollout.prompts_per_step} x "
+ f"{g.num_samples_per_prompt_rollout} x {self.rollout.max_response_len} "
+ f"= {expected}. The Dr.GRPO constant is DERIVED, never hand-typed."
+ )
+ if not os.path.isfile(self.data.band_file):
+ raise FileNotFoundError(
+ f"band file missing: {self.data.band_file}. Run "
+ "scripts/build_stageb_data.py first; the driver never builds data."
+ )
+ if self.train.save_every_micro_steps % self.train.grad_accum != 0:
+ raise ValueError(
+ f"save_every_micro_steps={self.train.save_every_micro_steps} is not a "
+ f"multiple of grad_accum={self.train.grad_accum}: checkpoint saves ARE "
+ "the weight sync (the engine reloads the latest checkpoint), so a save "
+ "landing mid-accumulation would sync half-stepped weights."
+ )
+ if self.train.max_grad_norm is None or self.train.max_grad_norm <= 0:
+ raise ValueError(
+ "train.max_grad_norm must be a positive float: R3 (2026-08-23 audit) "
+ "-- the recorded phase-1 rerun blew up to grad_norm 355,830 with no "
+ "clipping anywhere. Unclipped RL is not a configuration this stage "
+ "accepts."
+ )
+ if self.canary.every <= 0 or self.canary.n_problems <= 0:
+ raise ValueError(
+ "canary cadence/n must be positive: R4 -- 727 optimizer steps of "
+ "phase-1 preference training ran with zero task-accuracy signal."
+ )
+ if "grad_norm" not in self.armed_tripwires:
+ raise ValueError("stage-b requires the grad_norm HALT tripwire armed (R3)")
+ if not g.record_entropy:
+ raise ValueError("record_entropy must be true: the collapse tripwire reads entropy")
+
+ def validate_ceiling_receipt(self) -> dict:
+ """Separate from validate(): tests exercise config logic without a receipt."""
+ if not os.path.isfile(self.ceiling_receipt):
+ raise FileNotFoundError(
+ f"ceiling receipt missing: {self.ceiling_receipt}. No GRPO rollout "
+ "launches until the reward path is certified (the 0/100-perfect-policy "
+ "incident, mistral-prm-ppo 2026-08-14)."
+ )
+ with open(self.ceiling_receipt) as f:
+ receipt = json.load(f)
+ if not receipt.get("overall_pass"):
+ raise RuntimeError(f"ceiling receipt {self.ceiling_receipt} is not a PASS")
+ return receipt
+
+ def validate_world(self, world_size: int) -> None:
+ """Checks that need the launch topology."""
+ rows_per_step = self.rollout.prompts_per_step * self.grpo.num_samples_per_prompt_rollout
+ per_micro = rows_per_step // (world_size * self.train.grad_accum)
+ if per_micro < 1:
+ raise ValueError(
+ f"rollout shape too small for the topology: {rows_per_step} completions "
+ f"per step cannot fill world_size({world_size}) x "
+ f"grad_accum({self.train.grad_accum}) micro-slices"
+ )
+
+
+def load_stage_config(path: str, smoke: bool = False) -> GrpoStageConfig:
+ from align.config import load_toml
+
+ raw = load_toml(path)
+ g = dict(raw["grpo"])
+ g["loss_fn"] = GRPOLossType(g.get("loss_fn", "dapo"))
+ # TOML floats survive; TOML ints for float fields do not -- coerce the one
+ # field whose TYPE is load-bearing (the conformance gate refuses non-float).
+ if "loss_denominator" in g:
+ g["loss_denominator"] = float(g["loss_denominator"])
+ t = dict(raw["train"])
+ t["optimizer_name"] = raw.get("optimizer", {}).get("name", t.pop("optimizer_name", "adamw8bit"))
+ t["grad_accum"] = raw.get("optimizer", {}).get("grad_accum", t.pop("grad_accum", 8))
+ # STRUCT MODE for [train]. This used to filter through
+ # TrainCfg.__dataclass_fields__, which made a typo'd or not-yet-plumbed key
+ # (e.g. per_rank_optimizer_state before TrainCfg had it) ACCEPTED AND
+ # SILENTLY IGNORED: the TOML looked armed and the run was not. Raise at
+ # launch, naming the key, like DpoStageConfig's struct-mode loader.
+ unknown = sorted(k for k in t if k not in TrainCfg.__dataclass_fields__)
+ if unknown:
+ raise ValueError(
+ f"unknown key(s) {unknown} in [train] are not fields of TrainCfg "
+ f"(struct mode: a silently ignored key is a disarmed config). "
+ f"Known fields: {sorted(TrainCfg.__dataclass_fields__)}"
+ )
+ # STRUCT MODE for [rollout] too. The same filter-through-__dataclass_fields__
+ # idiom that [train] shed above survived here: `[rollout] max_reponse_len = 512`
+ # (typo) or a not-yet-plumbed `top_p = 0.9` was DROPPED, the shape kept its
+ # default, and `validate()` then checked the loss denominator against the
+ # default -- the pre-registered rollout looked armed and was not.
+ r = dict(raw["rollout"])
+ unknown_rollout = sorted(k for k in r if k not in RolloutShape.__dataclass_fields__)
+ if unknown_rollout:
+ raise ValueError(
+ f"unknown key(s) {unknown_rollout} in [rollout] are not fields of RolloutShape "
+ f"(struct mode: a silently ignored key is a disarmed config). "
+ f"Known fields: {sorted(RolloutShape.__dataclass_fields__)}"
+ )
+ cfg = GrpoStageConfig(
+ sft_checkpoint=raw["sft_checkpoint"],
+ run_dir=raw["run_dir"],
+ grpo=GRPOLossConfig(**g),
+ rollout=RolloutShape(**r),
+ reward=RewardCfg(**raw.get("reward", {})),
+ data=DataCfg(**raw["data"]),
+ train=TrainCfg(**t),
+ canary=CanaryCfg(**raw["canary"]),
+ armed_tripwires=tuple(raw.get("tripwires", {}).get("armed", ("grad_norm",))),
+ )
+ if smoke:
+ cfg = _smokeify(cfg)
+ cfg.validate()
+ return cfg
+
+
+def _smokeify(cfg: GrpoStageConfig) -> GrpoStageConfig:
+ """Tiny everything; the derived denominator is recomputed, not hand-edited."""
+ cfg.rollout.prompts_per_step = 2
+ cfg.grpo.num_samples_per_prompt_rollout = 8 # A3 floor is G >= 8, smoke included
+ # 256 tokens, not 32: the sixth smoke run proved a cap real answers
+ # cannot fit under produces zero reward variance -> every group dead ->
+ # the (correct) band-mischosen refusal. The smoke needs a policy with
+ # SOME task signal (the real SFT substrate) and room to finish answers.
+ cfg.rollout.max_response_len = 256
+ cfg.rollout.overlong_cache_len = 64
+ cfg.grpo.loss_denominator = float(2 * 8 * 256)
+ cfg.train.optimizer_steps = 10
+ cfg.train.grad_accum = 2
+ cfg.train.save_every_micro_steps = 10 # sync twice in a 20-micro smoke
+ cfg.canary.every = 5
+ cfg.canary.n_problems = 4
+ cfg.run_dir = cfg.run_dir + "_smoke"
+ return cfg
diff --git a/code/align/rl/tripwire.py b/code/align/rl/tripwire.py
new file mode 100644
index 0000000000000000000000000000000000000000..6910bf78b74805d8e7a014eb815161e8fc8c217d
--- /dev/null
+++ b/code/align/rl/tripwire.py
@@ -0,0 +1,202 @@
+"""Entropy/length collapse tripwire -- the second net-new A3 piece.
+
+Upstream has the loggable INPUTS (record_entropy=True) but no tripwire logic
+(corpus conformance table item 7: "the knob exists, the tripwire mechanism
+does not"). This is the GRPO sibling of the DPO chosen-logprob tripwire that
+fired -- and autonomously launched its pre-registered response -- on Arm B
+(2026-08-22, runs/dpo_arm_b_response/).
+
+Design contract, identical to the DPO one:
+ * The tripwire DETECTS and RECORDS. The stage script owns the response,
+ and the only admissible response is the pre-registered one
+ (KL=0 collapse -> ONE rerun with beta=1e-4, recorded AS the result;
+ no third configuration). No improvisation lives here by construction.
+ * Fed from the same metrics stream that history.jsonl records, so a fire
+ is always reproducible from the run's own log after the fact.
+
+Signals (cross-lab consensus per doc 08: entropy collapse is the 2025
+field-wide GRPO failure mode; length collapse is its usual companion):
+ E: policy entropy trend -- median over the trailing window falls below
+ ``entropy_floor_frac`` x the median over the baseline window.
+ L: mean response length trend -- same construction, lengths shrinking
+ below ``length_floor_frac`` of baseline (degenerate short answers).
+Either alone fires; both are cheap medians over deques, no model access.
+
+Resume (2026-10-12 review). The instrument is STATEFUL -- a baseline measured
+over the first ``baseline_steps`` observations, a trailing window, a step
+counter and a latch -- so a resume that rebuilds it fresh silently changes the
+experiment: the baseline is re-measured on the POST-resume (possibly already
+collapsed) policy, which makes the collapse the new "normal", and
+``min_steps_before_fire`` restarts at 0, blinding the guard for ~100 steps.
+``state_dict()`` / ``load_state_dict()`` carry all of it through the Trainer's
+``extra_state`` slot in resume.pt (``GRPOTrainModule.extra_state``).
+"""
+
+from __future__ import annotations
+
+import logging
+import math
+from collections import deque
+from dataclasses import dataclass, field
+from statistics import median
+from typing import Optional
+
+logger = logging.getLogger(__name__)
+
+
+@dataclass
+class CollapseTripwire:
+ baseline_steps: int = 50
+ window_steps: int = 50
+ entropy_floor_frac: float = 0.2
+ length_floor_frac: float = 0.4
+ min_steps_before_fire: int = 100
+
+ _baseline_entropy: list[float] = field(default_factory=list)
+ _baseline_length: list[float] = field(default_factory=list)
+ _entropy: deque = field(default_factory=deque)
+ _length: deque = field(default_factory=deque)
+ _steps: int = 0
+ fired: bool = False
+ fire_reason: Optional[str] = None
+ fire_step: Optional[int] = None
+
+ def __post_init__(self) -> None:
+ self._entropy = deque(maxlen=self.window_steps)
+ self._length = deque(maxlen=self.window_steps)
+
+ def observe(self, step: int, entropy: float, mean_response_len: float) -> bool:
+ """Feed one optimizer step's metrics; returns True the step it fires.
+
+ Once fired it stays fired (a latched instrument, like the DPO one) --
+ the stage script reads ``fired``/``fire_reason`` and executes the
+ pre-registered response exactly once.
+ """
+ if not (math.isfinite(entropy) and math.isfinite(mean_response_len)):
+ # NaN compares False against every threshold below (`nan > 0`,
+ # `nan < x`), so a NaN entropy stream would leave the entropy arm
+ # permanently inert while the object reads as armed -- the
+ # disarmed-tripwire shape (audit D1). `_on_step_boundary` feeds NaN
+ # when a metric key is missing. Refuse loudly instead.
+ raise ValueError(
+ f"CollapseTripwire.observe(step={step}): non-finite input "
+ f"(entropy={entropy}, mean_response_len={mean_response_len}); a guard "
+ "fed NaN can never fire"
+ )
+ self._steps += 1
+ if len(self._baseline_entropy) < self.baseline_steps:
+ self._baseline_entropy.append(entropy)
+ self._baseline_length.append(mean_response_len)
+ return False
+ self._entropy.append(entropy)
+ self._length.append(mean_response_len)
+
+ if (
+ self.fired
+ or self._steps < self.min_steps_before_fire
+ or len(self._entropy) < self.window_steps
+ ):
+ return False
+
+ base_e = median(self._baseline_entropy)
+ base_l = median(self._baseline_length)
+ cur_e = median(self._entropy)
+ cur_l = median(self._length)
+ reasons = []
+ if base_e > 0 and cur_e < self.entropy_floor_frac * base_e:
+ reasons.append(
+ f"entropy collapse: trailing median {cur_e:.4f} < {self.entropy_floor_frac} x baseline {base_e:.4f}"
+ )
+ if base_l > 0 and cur_l < self.length_floor_frac * base_l:
+ reasons.append(
+ f"length collapse: trailing median {cur_l:.1f} < {self.length_floor_frac} x baseline {base_l:.1f}"
+ )
+ if reasons:
+ self.fired = True
+ self.fire_step = step
+ self.fire_reason = "; ".join(reasons)
+ logger.error(
+ "TRIPWIRE (GRPO collapse) at step %d: %s -- pre-registered response: ONE beta=1e-4 "
+ "rerun recorded AS the result; no third configuration.",
+ step,
+ self.fire_reason,
+ )
+ return True
+ return False
+
+ #: Bump on any change to the persisted layout; a mismatch refuses to load.
+ STATE_VERSION = 1
+
+ def _config_dict(self) -> dict:
+ return {
+ "baseline_steps": self.baseline_steps,
+ "window_steps": self.window_steps,
+ "entropy_floor_frac": self.entropy_floor_frac,
+ "length_floor_frac": self.length_floor_frac,
+ "min_steps_before_fire": self.min_steps_before_fire,
+ }
+
+ def state_dict(self) -> dict:
+ """Everything `observe` reads, as primitives only.
+
+ Plain dict/list/float/int/bool/None/str so it survives the Trainer's
+ ``torch.load(weights_only=True)`` of resume.pt -- unpickling arbitrary
+ objects to restore a counter would be a code-execution path for nothing.
+ The thresholds are included so `load_state_dict` can refuse a state cut
+ for a different configuration.
+ """
+ return {
+ "version": self.STATE_VERSION,
+ "config": self._config_dict(),
+ "baseline_entropy": [float(x) for x in self._baseline_entropy],
+ "baseline_length": [float(x) for x in self._baseline_length],
+ "entropy": [float(x) for x in self._entropy],
+ "length": [float(x) for x in self._length],
+ "steps": int(self._steps),
+ "fired": bool(self.fired),
+ "fire_reason": self.fire_reason,
+ "fire_step": self.fire_step,
+ }
+
+ def load_state_dict(self, sd: dict) -> None:
+ """Restore `state_dict()` output; refuse anything that is not exactly it.
+
+ A silently half-restored tripwire is the disarmed-instrument shape, so
+ every inconsistency raises: wrong version, thresholds that differ from
+ this instance's (a different experiment -- rebuild it deliberately),
+ or windows longer than the configured sizes. Validation happens BEFORE
+ any field is assigned, so a refused load leaves the instance untouched.
+ """
+ if sd.get("version") != self.STATE_VERSION:
+ raise ValueError(
+ f"CollapseTripwire state version {sd.get('version')!r} != {self.STATE_VERSION}"
+ )
+ if sd.get("config") != self._config_dict():
+ raise ValueError(
+ f"CollapseTripwire state was saved under {sd.get('config')!r} but this "
+ f"instance is configured {self._config_dict()!r}; resuming under different "
+ "thresholds is a different experiment."
+ )
+ base_e, base_l = list(sd["baseline_entropy"]), list(sd["baseline_length"])
+ ent, length = list(sd["entropy"]), list(sd["length"])
+ if len(base_e) != len(base_l) or len(ent) != len(length):
+ raise ValueError("CollapseTripwire state has mismatched entropy/length series")
+ if len(base_e) > self.baseline_steps or len(ent) > self.window_steps:
+ raise ValueError("CollapseTripwire state windows exceed the configured sizes")
+ if not all(math.isfinite(x) for x in base_e + base_l + ent + length):
+ raise ValueError("CollapseTripwire state contains non-finite observations")
+ self._baseline_entropy = [float(x) for x in base_e]
+ self._baseline_length = [float(x) for x in base_l]
+ self._entropy = deque((float(x) for x in ent), maxlen=self.window_steps)
+ self._length = deque((float(x) for x in length), maxlen=self.window_steps)
+ self._steps = int(sd["steps"])
+ self.fired = bool(sd["fired"])
+ self.fire_reason = sd["fire_reason"]
+ self.fire_step = sd["fire_step"]
+
+ def as_log_dict(self) -> dict:
+ return {
+ "tripwire/fired": self.fired,
+ "tripwire/fire_step": self.fire_step,
+ "tripwire/fire_reason": self.fire_reason,
+ }
diff --git a/code/align/rl/types.py b/code/align/rl/types.py
new file mode 100644
index 0000000000000000000000000000000000000000..1830c133c566445997d5445fca608b8d9c0f8c49
--- /dev/null
+++ b/code/align/rl/types.py
@@ -0,0 +1,84 @@
+"""Rollout data types for stage-b GRPO.
+
+The rollout ORCHESTRATOR (M-D, separate build) produces these; the
+GRPOTrainModule consumes them. Alignment convention, stated once: per-token
+logprob tensors are LABEL-ALIGNED -- position t holds the logprob of
+input_ids[:, t+1], so they have length T-1 and pair with
+`shifted_response_mask`. Getting this half-open boundary wrong is the same
+off-by-one family as the decode-position incident (section-09) and the
+completion-mask defect (audit R1); the validating constructor exists so the
+mismatch fails at batch construction, not as a silently shifted objective.
+"""
+
+from __future__ import annotations
+
+from dataclasses import dataclass, field
+
+import torch
+
+
+@dataclass
+class RolloutBatch:
+ """One training micro-batch of rollout data.
+
+ Shapes (B = completions in the batch, T = padded sequence length):
+ input_ids [B, T] int64 prompt + completion, right-padded
+ attention_mask [B, T] int64 1 on real tokens
+ response_mask [B, T] int64 1 on COMPLETION tokens only (input space)
+ advantages [B] float per-sequence Dr.GRPO centered advantage
+ old_logprobs [B, T-1] policy-at-rollout logprobs (label-aligned)
+ vllm_logprobs [B, T-1] inference-engine logprobs (label-aligned)
+ ref_logprobs [B, T-1]? reference-policy logprobs; REQUIRED iff
+ the loss config has beta > 0 (the KL
+ ablation), absent otherwise
+ rewards {name: [B]} per-completion reward terms (reward/total,
+ reward/accuracy, ... -- itemised so the
+ format-consistency instrument reads terms,
+ not sums)
+ group_ids [B] int64 prompt-group id of each completion
+ """
+
+ input_ids: torch.Tensor
+ attention_mask: torch.Tensor
+ response_mask: torch.Tensor
+ advantages: torch.Tensor
+ old_logprobs: torch.Tensor
+ vllm_logprobs: torch.Tensor
+ group_ids: torch.Tensor
+ rewards: dict[str, torch.Tensor] = field(default_factory=dict)
+ ref_logprobs: torch.Tensor | None = None
+
+ def __post_init__(self) -> None:
+ B, T = self.input_ids.shape
+ for name, t, shape in (
+ ("attention_mask", self.attention_mask, (B, T)),
+ ("response_mask", self.response_mask, (B, T)),
+ ("advantages", self.advantages, (B,)),
+ ("old_logprobs", self.old_logprobs, (B, T - 1)),
+ ("vllm_logprobs", self.vllm_logprobs, (B, T - 1)),
+ ("group_ids", self.group_ids, (B,)),
+ ):
+ if tuple(t.shape) != shape:
+ raise ValueError(
+ f"RolloutBatch.{name} has shape {tuple(t.shape)}, expected {shape} "
+ f"(B={B}, T={T}; logprob tensors are label-aligned, length T-1)"
+ )
+ if self.ref_logprobs is not None and tuple(self.ref_logprobs.shape) != (B, T - 1):
+ raise ValueError(
+ f"RolloutBatch.ref_logprobs has shape {tuple(self.ref_logprobs.shape)}, "
+ f"expected {(B, T - 1)}"
+ )
+ for name, t in self.rewards.items():
+ if tuple(t.shape) != (B,):
+ raise ValueError(
+ f"RolloutBatch.rewards[{name!r}] has shape {tuple(t.shape)}, expected {(B,)}"
+ )
+
+ @property
+ def shifted_response_mask(self) -> torch.Tensor:
+ """Response mask in LABEL space [B, T-1], pairing with the logprobs."""
+ return self.response_mask[:, 1:]
+
+ @property
+ def n_response_tokens(self) -> int:
+ return int(self.shifted_response_mask.sum())
diff --git a/code/align/rollout/__init__.py b/code/align/rollout/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..ecfbb6eca6c640d93736a8ec532b215f1c902c03
--- /dev/null
+++ b/code/align/rollout/__init__.py
@@ -0,0 +1,74 @@
+"""Rollout: request/result shapes, the native sampler, and the optional spool client.
+
+Backends (selected by `align.rollout.client.make_rollout_client`):
+
+ 'native' (default) `native.NativeRolloutBackend` -- in-process torch KV-cache
+ sampler; zero dependencies beyond torch.
+ 'spool' `client.RolloutClient` -- file protocol to the optional
+ vLLM worker in extras/vllm_rollout/ (its own venv).
+
+Nothing under align/ imports vllm or ray, and nothing under align/ imports
+extras/ (tests/test_dependency_firewall.py enforces both). The extras worker
+imports `align.rollout.protocol`, which is stdlib-only BY CONTRACT; this
+package `__init__` therefore imports ONLY that module eagerly. The torch-using
+pieces (`NativeRolloutBackend`, `make_rollout_client`) resolve lazily so the
+worker's venv never pays for, or is constrained by, them.
+
+Per the 2026-08-22 sovereignty amendment (RESULTS.md dated entry): vLLM is
+permitted for GENERATION only, in an isolated venv (.venv-rollout), behind a
+process boundary. The v1 topology (DESIGN v3 section B.1b, decided 2026-08-24):
+3 FSDP train ranks + 1 dedicated engine GPU; weight handoff via checkpoint
+directory + engine reload every `sync_every` optimizer steps;
+use_rho_correction=True absorbs the resulting off-policyness -- that is the
+term's entire purpose. The native backend honours the same contract
+(`load_weights_fn` on a changed weights_dir).
+"""
+
+from align.rollout.protocol import (
+ RolloutBackend,
+ RolloutRequest,
+ RolloutResult,
+ read_request,
+ read_result,
+ write_request,
+ write_result,
+)
+
+__all__ = [
+ "PagedEngine",
+ "PagedEngineConfig",
+ "PagedResult",
+ "SamplingParams",
+ "NativeRolloutBackend",
+ "NativeRolloutConfig",
+ "RolloutBackend",
+ "RolloutRequest",
+ "RolloutResult",
+ "checkpoint_weight_loader",
+ "make_rollout_client",
+ "read_request",
+ "read_result",
+ "write_request",
+ "write_result",
+]
+
+_LAZY = {
+ # P3 opt-in paged continuous-batching sampler (docs/P3_ENGINE.md). Nothing
+ # in the default GRPO/rollout path imports these.
+ "PagedEngine": "align.rollout.engine",
+ "PagedEngineConfig": "align.rollout.engine",
+ "PagedResult": "align.rollout.engine",
+ "SamplingParams": "align.rollout.engine",
+ "NativeRolloutBackend": "align.rollout.native",
+ "NativeRolloutConfig": "align.rollout.native",
+ "checkpoint_weight_loader": "align.rollout.native",
+ "make_rollout_client": "align.rollout.client",
+}
+
+
+def __getattr__(name):
+ if name in _LAZY:
+ import importlib
+
+ return getattr(importlib.import_module(_LAZY[name]), name)
+ raise AttributeError(f"module 'align.rollout' has no attribute {name!r}")
diff --git a/code/align/rollout/client.py b/code/align/rollout/client.py
new file mode 100644
index 0000000000000000000000000000000000000000..6d003cc38cf6676e0d9964a8f791a1e52b3e4a32
--- /dev/null
+++ b/code/align/rollout/client.py
@@ -0,0 +1,234 @@
+"""Trainer-side rollout clients and the backend selector.
+
+Two backends serve the same `RolloutRequest -> RolloutResult` contract
+(align/rollout/protocol.py), chosen by `make_rollout_client(backend=...)`:
+
+ 'native' (DEFAULT) -- `native.NativeRolloutBackend`: in-process torch
+ KV-cache sampler on the training/passed model. Zero extra dependencies;
+ this is what makes GRPO runnable on torch + triton + numpy + safetensors.
+ 'spool' -- `RolloutClient` below: file protocol to the optional
+ vLLM worker (extras/vllm_rollout/rollout_worker.py) running in its OWN
+ venv. A throughput option, never a requirement.
+
+This module lives in the TRAINING venv and imports nothing from vLLM -- the
+process boundary is the sovereignty line. `spawn_worker` launches the extras
+worker under the ISOLATED rollout venv with the full required environment
+baked in, so a caller cannot forget the flashinfer workaround or point a cache
+at the 13GB root filesystem.
+"""
+
+from __future__ import annotations
+
+import os
+import subprocess
+from typing import Optional
+
+from align.rollout.protocol import (
+ RolloutBackend,
+ RolloutRequest,
+ RolloutResult,
+ wait_for_result,
+ write_request,
+)
+
+#: The selectable backends. Data, so error messages and CLIs iterate it.
+BACKENDS: tuple[str, ...] = ("native", "spool")
+DEFAULT_BACKEND = "native"
+
+#: Repo root, derived from this file's location -- the client and the worker
+#: script it spawns always come from the same checkout.
+REPO_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+ROLLOUT_PYTHON = os.path.join(REPO_ROOT, ".venv-rollout", "bin", "python")
+#: The vLLM worker is an EXTRA (own venv, see extras/vllm_rollout/README.md),
+#: not part of the align package: nothing under align/ may import it, but a
+#: path string to launch it as a subprocess is the intended coupling.
+WORKER_SCRIPT = os.path.join(REPO_ROOT, "extras", "vllm_rollout", "rollout_worker.py")
+
+
+class RolloutClient:
+ """The 'spool' backend: submit rollout requests and block on their results.
+
+ Timeouts are the caller's budget decision (stage-b sets them from the
+ measured pilot throughput); the stale-heartbeat threshold distinguishes
+ a slow request from a dead worker -- see protocol.wait_for_result.
+ """
+
+ def __init__(self, spool: str, timeout_s: float, stale_heartbeat_s: float = 600.0):
+ self.spool = spool
+ self.timeout_s = float(timeout_s)
+ self.stale_heartbeat_s = float(stale_heartbeat_s)
+
+ def submit(self, request: RolloutRequest) -> str:
+ write_request(self.spool, request)
+ return request.request_id
+
+ def collect(self, request_id: str) -> RolloutResult:
+ return wait_for_result(
+ self.spool,
+ request_id,
+ timeout_s=self.timeout_s,
+ stale_heartbeat_s=self.stale_heartbeat_s,
+ )
+
+
+#: torchrun/torchelastic variables that MUST NOT reach the worker: vLLM's
+#: engine initialises torch.distributed internally, and a child that inherits
+#: the trainer's rendezvous (MASTER_ADDR/RANK/WORLD_SIZE/...) tries to JOIN
+#: the trainer's process group -- observed on the 2026-08-24 smoke as the
+#: engine core dialing torchrun's TCP store while ranks 1-2 timed out waiting
+#: for rank 0. The worker is a separate world of size 1, always.
+_POISON_ENV_KEYS = (
+ "MASTER_ADDR",
+ "MASTER_PORT",
+ "RANK",
+ "WORLD_SIZE",
+ "LOCAL_RANK",
+ "LOCAL_WORLD_SIZE",
+ "GROUP_RANK",
+ "GROUP_WORLD_SIZE",
+ "ROLE_RANK",
+ "ROLE_WORLD_SIZE",
+ "ROLE_NAME",
+ "NODE_RANK",
+)
+_POISON_ENV_PREFIXES = ("TORCHELASTIC_", "PET_", "TORCH_NCCL_")
+
+
+def _worker_env() -> dict:
+ """A copy of os.environ with every trainer-distributed variable scrubbed."""
+ return {
+ k: v
+ for k, v in os.environ.items()
+ if k not in _POISON_ENV_KEYS and not k.startswith(_POISON_ENV_PREFIXES)
+ }
+
+
+def spawn_worker(
+ spool: str,
+ gpu: int,
+ log_path: str,
+ gpu_mem_util: float = 0.85,
+ tp: int = 1,
+ idle_exit_s: float = 0.0,
+ rollout_python: Optional[str] = None,
+) -> subprocess.Popen:
+ """Launch the vLLM worker on ONE dedicated GPU with the required env.
+
+ The env block is the launch checklist, encoded: the flashinfer sampler
+ is disabled (its JIT ninja build fails on this box's toolchain, measured
+ 2026-08-23), every cache/tmp path lands on /apps, and the HF hub cache is
+ the rlhf one the stage-b snapshots live in.
+ """
+ python = rollout_python or ROLLOUT_PYTHON
+ if not os.path.isfile(python):
+ raise FileNotFoundError(
+ f"rollout venv python not found at {python}; build .venv-rollout first "
+ "(uv venv + torch==2.11.0 triton==3.6.0 vllm==0.26.0 ray pyarrow)."
+ )
+ env = _worker_env()
+ env.update(
+ {
+ "CUDA_VISIBLE_DEVICES": str(gpu),
+ "VLLM_USE_FLASHINFER_SAMPLER": "0",
+ "TMPDIR": os.path.join(REPO_ROOT, ".tmp"),
+ "XDG_CACHE_HOME": os.path.join(REPO_ROOT, ".cache"),
+ "HF_HUB_CACHE": "/apps/development/rlhf/hf_cache/hub",
+ # worker logs must flush per line or a killed worker leaves an
+ # empty log (first smoke run, 2026-08-24)
+ "PYTHONUNBUFFERED": "1",
+ }
+ )
+ os.makedirs(os.path.dirname(os.path.abspath(log_path)), exist_ok=True)
+ log = open(log_path, "a")
+ return subprocess.Popen(
+ [
+ python,
+ WORKER_SCRIPT,
+ "--spool",
+ spool,
+ "--gpu-mem-util",
+ str(gpu_mem_util),
+ "--tp",
+ str(tp),
+ "--idle-exit-s",
+ str(idle_exit_s),
+ ],
+ stdout=log,
+ stderr=subprocess.STDOUT,
+ env=env,
+ cwd=REPO_ROOT,
+ # own process GROUP so teardown can reap vLLM's EngineCore child --
+ # terminating only the parent orphaned a 19.6GB engine core on GPU 3
+ # (fifth smoke run, 2026-08-24)
+ start_new_session=True,
+ )
+
+
+def terminate_worker(proc: subprocess.Popen, timeout_s: float = 30.0) -> None:
+ """Kill the worker's WHOLE process group (worker + vLLM engine core).
+
+ proc.terminate() reaches only the parent; the engine core reparents to
+ init and keeps its VRAM. Group-SIGTERM, grace, then group-SIGKILL.
+ """
+ import signal
+
+ try:
+ pgid = os.getpgid(proc.pid)
+ except ProcessLookupError:
+ return
+ try:
+ os.killpg(pgid, signal.SIGTERM)
+ proc.wait(timeout=timeout_s)
+ except subprocess.TimeoutExpired:
+ os.killpg(pgid, signal.SIGKILL)
+ proc.wait(timeout=10)
+ except ProcessLookupError:
+ pass
+
+
+#: Explicit name for the spool backend; `RolloutClient` is kept as the
+#: original spelling because existing callers and tests import it.
+SpoolRolloutClient = RolloutClient
+
+
+def make_rollout_client(
+ backend: str = DEFAULT_BACKEND,
+ *,
+ # -- native -----------------------------------------------------------------
+ model=None,
+ tokenizer=None,
+ native_config=None,
+ load_weights_fn=None,
+ device=None,
+ # -- spool ------------------------------------------------------------------
+ spool: Optional[str] = None,
+ timeout_s: float = 3600.0,
+ stale_heartbeat_s: float = 600.0,
+) -> RolloutBackend:
+ """Build the selected rollout backend.
+
+ Each backend REFUSES the other's arguments' absence loudly instead of
+ falling back: 'native' without a model is not "spool by default", and
+ 'spool' without a spool dir is not "native by default". A silent switch of
+ generation backend changes throughput by an order of magnitude and
+ off-policyness by a sync interval, and would show up first as a mystery in
+ the step time.
+ """
+ if backend == "native":
+ if model is None or tokenizer is None:
+ raise ValueError(
+ "backend='native' needs model= and tokenizer= (the in-process sampler runs "
+ "on the model it is given); pass backend='spool' to use the external worker"
+ )
+ # lazy: keeps `import align.rollout.client` free of torch/engine for
+ # spool-only callers
+ from align.rollout.native import NativeRolloutBackend
+
+ return NativeRolloutBackend(
+ model, tokenizer, native_config, load_weights_fn=load_weights_fn, device=device
+ )
+ if backend == "spool":
+ if not spool:
+ raise ValueError("backend='spool' needs spool= (the request/result spool directory)")
+ return RolloutClient(spool, timeout_s=timeout_s, stale_heartbeat_s=stale_heartbeat_s)
+ raise ValueError(f"unknown rollout backend {backend!r}; choose one of {BACKENDS}")
diff --git a/code/align/rollout/engine.py b/code/align/rollout/engine.py
new file mode 100644
index 0000000000000000000000000000000000000000..22fbe0311f4a7ff8f039b25aabedfba45b1cbfbf
--- /dev/null
+++ b/code/align/rollout/engine.py
@@ -0,0 +1,674 @@
+"""Continuous-batching sampler over a paged KV cache (P3, opt-in).
+
+This is the native-engine step ROADMAP Track L calls P3: many sequences of very
+different lengths in flight at once, admitted and retired independently,
+backed by `align.rollout.paged`'s block tables. It is model-agnostic over
+align's causal LMs (Mistral, Qwen3, OLMoE) WITHOUT touching them: the engine
+drives the model's own submodules (`embed_tokens`, per-block `attn_norm` /
+`attn.{q,k,v,o}_proj` / optional `q_norm`,`k_norm` / `mlp_norm` / `mlp`,
+`norm`, `lm_head`, `rope`) layer by layer and replaces only the attention
+kernel, which is the one thing a paged cache changes. Qwen3's per-head QK-norm
+and OLMoE's full-width QK-norm (+ `clip_qkv`) are told apart by the norm weight
+width, the same distinction the model files' docstrings warn about.
+
+THE SCHEDULER, in one paragraph. Every sequence is `tokens` (prompt + sampled)
+and `num_computed` (how many have KV in the cache). A step schedules, per
+sequence, the next `n_new = min(len(tokens) - num_computed, chunk_size,
+budget)` tokens. Prefill, chunked prefill, decode and post-preemption
+recompute are therefore ONE code path; a sequence samples exactly when its
+scheduled tokens reach the end of `tokens`. Mixed prefill chunks and decode
+tokens share a step (one flat ragged batch), so a long prompt cannot stall
+the decoders behind it (the head-of-line blocking a prefill-then-decode engine
+has).
+
+MEMORY PRESSURE. Blocks are allocated lazily as sequences grow. When a running
+sequence cannot get its next block the YOUNGEST unscheduled running sequence is
+preempted by RECOMPUTE: its blocks are freed, `num_computed` returns to 0, and
+it re-enters the front of the waiting queue with its tokens (and its sampler
+state) intact, so it re-prefills and carries on. Recompute rather than swap
+because there is no host swap space on this box by design. The generator is
+only advanced when a token is actually sampled, so a preemption changes WHEN a
+token is drawn, never WHICH.
+
+BATCH INVARIANCE (the property RL rollouts care about). A sequence's tokens
+and logprobs should not depend on who else is in the batch, when it was
+admitted, how its prompt was chunked, or whether it was preempted: otherwise
+`pi_infer_old` (the logprobs that feed the rho correction) is a function of the
+scheduler's mood, and two reruns of the same rollout seed disagree. Floating
+point breaks this through (a) GEMM kernels whose reduction order depends on the
+number of rows, and (b) attention whose reduction length depends on padding or
+chunk size. `numerics='invariant'` removes both by construction: every
+row-wise module call (projections, norms, MLP, lm_head) is issued on one row
+at a time, and attention is one SDPA call per query row over exactly its own
+keys. Every operand the kernel sees then depends only on the sequence itself.
+It is slow and is the reference/RL-safe mode. `numerics='fast'` batches rows
+and pads decode attention: faster, equal to 'invariant' to rounding only
+(fp64: ~1e-15 on logits and tokens identical; bf16: expect occasional
+argmax/sample flips on near-ties -- docs/P3_ENGINE.md has the measured rate).
+
+SAMPLING. One `torch.Generator` PER SEQUENCE, seeded from `SamplingParams.seed`
+(or a stable hash of the request id), advanced only by that sequence's own
+draws. A shared engine generator (what `align.inference.Engine` uses) makes a
+sampled token depend on how many other rows drew before it in the step, i.e. on
+batch composition. Temperature, top-k, then top-p (minimal nucleus) are applied
+to the fp32 logits; the RETURNED logprob is `log_softmax(logits/T)[token]`
+UNTRUNCATED, the same basis as `align.rollout.native.LOGPROB_BASIS`, so the
+rho correction contract is unchanged.
+
+PREFIX SHARING (opt-in). Requests whose prompts share leading FULL blocks with
+a running sequence adopt that sequence's blocks by reference instead of
+recomputing them (GRPO: G samples of one prompt prefill it once). A follower
+waits until the leader has computed the shared region, never shares a partial
+block, and always recomputes at least its last prompt token (it needs that
+row's logits). The allocator's refcounts keep shared blocks alive until the
+last owner leaves; `PagedKVCache.slots` refuses writes into a shared block.
+"""
+
+from __future__ import annotations
+
+import hashlib
+import math
+import time
+from dataclasses import dataclass, field
+from typing import Callable, Optional, Sequence
+
+import torch
+import torch.nn as nn
+
+from align.inference.engine import _sampling_distribution, find_stop
+from align.model.mistral import apply_rotary, kv_head_index_map
+from align.rollout.paged import (
+ OutOfBlocks,
+ PagedAttention,
+ PagedKVCache,
+ Segment,
+ StepBatch,
+ TorchPagedAttention,
+)
+
+FINISH_REASONS = ("eos", "stop_token", "stop_string", "max_new_tokens", "max_seq_len")
+
+
+# ---------------------------------------------------------------------------
+# Public dataclasses
+# ---------------------------------------------------------------------------
+
+
+@dataclass
+class SamplingParams:
+ """Per-sequence sampling. `temperature` 0/None = greedy."""
+
+ max_tokens: int = 64
+ temperature: Optional[float] = 0.0
+ top_p: float = 1.0
+ #: 0 disables. Applied BEFORE top-p (vLLM's order).
+ top_k: int = 0
+ #: None -> stable hash of the request id (NOT admission order, which would
+ #: make "unseeded" depend on batch composition).
+ seed: Optional[int] = None
+ stop_token_ids: tuple[int, ...] = ()
+ #: text stops need a tokenizer; the completion keeps the token that
+ #: completed the stop string (token ids and logprobs stay aligned).
+ stop: tuple[str, ...] = ()
+
+ def validate(self) -> None:
+ if self.max_tokens < 1:
+ raise ValueError(f"max_tokens={self.max_tokens} must be >= 1")
+ t = self.temperature
+ if t is not None and (math.isnan(t) or t < 0):
+ raise ValueError(f"temperature={t} must be >= 0")
+ if math.isnan(self.top_p) or not (0.0 < self.top_p <= 1.0):
+ raise ValueError(f"top_p={self.top_p} must be in (0, 1]")
+ if self.top_k < 0:
+ raise ValueError(f"top_k={self.top_k} must be >= 0")
+
+ @property
+ def greedy(self) -> bool:
+ return self.temperature is None or self.temperature == 0.0
+
+
+@dataclass
+class PagedEngineConfig:
+ #: Tokens per KV block. 16 is vLLM's default; smaller wastes less tail,
+ #: larger gathers fewer indices.
+ block_size: int = 16
+ num_blocks: int = 256
+ #: Max sequences resident (admitted) at once.
+ max_num_seqs: int = 32
+ #: Per-step token budget across all segments (decode tokens + prefill chunks).
+ max_num_batched_tokens: int = 512
+ #: Max prompt tokens one sequence contributes to a step. None -> the budget.
+ chunk_size: Optional[int] = None
+ #: Hard cap on prompt + completion. None -> the model's max_position_embeddings.
+ max_seq_len: Optional[int] = None
+ #: 'invariant' (bit-reproducible across batch compositions; slow) or 'fast'.
+ numerics: str = "invariant"
+ prefix_sharing: bool = False
+ eos_token_id: Optional[int] = None
+ #: KV dtype. None -> the model's parameter dtype.
+ kv_dtype: Optional[torch.dtype] = None
+ #: Replace the attention backend (e.g. a future Triton kernel).
+ attention: Optional[PagedAttention] = None
+
+ def validate(self) -> None:
+ if self.numerics not in ("invariant", "fast"):
+ raise ValueError(f"numerics={self.numerics!r}; expected 'invariant' or 'fast'")
+ if min(self.block_size, self.num_blocks, self.max_num_seqs, self.max_num_batched_tokens) < 1:
+ raise ValueError(f"bad PagedEngineConfig: {self}")
+ if self.chunk_size is not None and self.chunk_size < 1:
+ raise ValueError(f"chunk_size={self.chunk_size} must be >= 1")
+
+
+@dataclass
+class PagedResult:
+ rid: str
+ prompt_ids: list[int]
+ token_ids: list[int]
+ logprobs: list[float]
+ finish_reason: str
+ preemptions: int = 0
+ shared_prefix_tokens: int = 0
+ text: Optional[str] = None
+ step_logits: Optional[list[torch.Tensor]] = None
+
+
+@dataclass
+class _Seq:
+ rid: str
+ prompt: list[int]
+ params: SamplingParams
+ order: int # monotonically increasing arrival index (age for preemption)
+ generator: torch.Generator
+ tokens: list[int] = field(default_factory=list)
+ num_computed: int = 0
+ logprobs: list[float] = field(default_factory=list)
+ finish: Optional[str] = None
+ preemptions: int = 0
+ shared_tokens: int = 0
+ logits_log: Optional[list[torch.Tensor]] = None
+
+ @property
+ def remaining(self) -> int:
+ return len(self.tokens) - self.num_computed
+
+ @property
+ def n_generated(self) -> int:
+ return len(self.tokens) - len(self.prompt)
+
+
+# ---------------------------------------------------------------------------
+# Sampling
+# ---------------------------------------------------------------------------
+
+
+def top_k_filter(logits: torch.Tensor, k: int) -> torch.Tensor:
+ """Keep the k largest logits of a [V] / [B,V] tensor, others -> -inf.
+
+ Ties at the k-th value are resolved by `torch.topk`'s index order, which is
+ deterministic; the cut is by COUNT, never by value, so exactly k survive."""
+ if k <= 0 or k >= logits.shape[-1]:
+ return logits
+ idx = torch.topk(logits, k, dim=-1).indices
+ out = torch.full_like(logits, float("-inf"))
+ return out.scatter(-1, idx, logits.gather(-1, idx))
+
+
+def row_distribution(logits: torch.Tensor, p: SamplingParams) -> torch.Tensor:
+ """The distribution a non-greedy draw is made from: softmax(logits/T) over the
+ top-k survivors, truncated to the minimal top-p nucleus, renormalised. [V] fp32.
+ Split out so tests can compare it with an independent fp64 reference instead
+ of inferring it from draw frequencies."""
+ masked = top_k_filter(logits.float(), p.top_k)
+ return _sampling_distribution(masked.unsqueeze(0), float(p.temperature), p.top_p)[0]
+
+
+def sample_row(
+ logits: torch.Tensor, p: SamplingParams, gen: torch.Generator
+) -> tuple[int, float]:
+ """One row [V] -> (token, logprob). Greedy = argmax. The logprob is of the
+ UNTRUNCATED tempered distribution (T=1 for greedy): top-k/top-p decide which
+ token is drawn, they are not part of the distribution the training objective
+ models (see align.rollout.native for why folding them in breaks rho)."""
+ x = logits.float()
+ if p.greedy:
+ tok = int(x.argmax())
+ return tok, float(torch.log_softmax(x, dim=-1)[tok])
+ lp = torch.log_softmax(x / float(p.temperature), dim=-1)
+ probs = row_distribution(logits, p)
+ tok = int(torch.multinomial(probs.unsqueeze(0), 1, generator=gen)[0, 0])
+ return tok, float(lp[tok])
+
+
+def _stable_seed(rid: str) -> int:
+ return int.from_bytes(hashlib.sha256(rid.encode()).digest()[:8], "little") & ((1 << 62) - 1)
+
+
+# ---------------------------------------------------------------------------
+# Model adapter
+# ---------------------------------------------------------------------------
+
+
+class _ModelView:
+ """Reads align's causal-LM structure and runs it layer by layer on a flat
+ token batch. Raises at construction on anything it does not recognise, so a
+ new architecture fails at build, not as wrong logits."""
+
+ def __init__(self, model: nn.Module):
+ try:
+ inner = model.model
+ self.embed, self.layers, self.final_norm, self.rope = (
+ inner.embed_tokens, list(inner.layers), inner.norm, inner.rope,
+ )
+ self.lm_head = model.lm_head
+ except AttributeError as e:
+ raise TypeError(
+ f"{type(model).__name__} does not look like an align causal LM "
+ f"(needs .model.{{embed_tokens,layers,norm,rope}} and .lm_head): {e}"
+ ) from None
+ a0 = self.layers[0].attn
+ for attr in ("n_heads", "n_kv_heads", "head_dim", "scale", "q_proj", "k_proj", "v_proj", "o_proj"):
+ if not hasattr(a0, attr):
+ raise TypeError(f"attention module lacks `{attr}`; unsupported architecture")
+ self.n_layers = len(self.layers)
+ self.n_heads, self.n_kv_heads, self.head_dim, self.scale = (
+ a0.n_heads, a0.n_kv_heads, a0.head_dim, a0.scale,
+ )
+ # The paged backends use SDPA's enable_gqa, i.e. the canonical BLOCKED map
+ # q_head // group -> kv_head. Anything else would be silently wrong.
+ group = self.n_heads // self.n_kv_heads
+ idx = kv_head_index_map(self.n_heads, self.n_kv_heads, device=torch.device("cpu"))
+ if not torch.equal(idx, torch.arange(self.n_heads) // group):
+ raise NotImplementedError("non-canonical kv-head map is not supported by the paged engine")
+ self.qk_norm = self._qk_norm_kind(a0)
+ self.clip = getattr(a0, "clip_qkv", None)
+
+ def _qk_norm_kind(self, attn) -> Optional[str]:
+ if not hasattr(attn, "q_norm"):
+ return None
+ w = attn.q_norm.weight.numel()
+ if w == self.head_dim:
+ return "head" # Qwen3
+ if w == self.n_heads * self.head_dim:
+ return "full" # OLMoE
+ raise NotImplementedError(f"q_norm width {w} matches neither head_dim nor n_heads*head_dim")
+
+ def qkv(self, attn, h: torch.Tensor):
+ """h [1,t,D] -> q [1,t,H,Dh], k/v [1,t,Hkv,Dh] BEFORE RoPE."""
+ b, t, _ = h.shape
+ q, k, v = attn.q_proj(h), attn.k_proj(h), attn.v_proj(h)
+ if self.qk_norm == "full":
+ q, k = attn.q_norm(q), attn.k_norm(k)
+ if self.clip is not None:
+ c = self.clip
+ q, k, v = q.clamp(-c, c), k.clamp(-c, c), v.clamp(-c, c)
+ q = q.view(b, t, self.n_heads, self.head_dim)
+ k = k.view(b, t, self.n_kv_heads, self.head_dim)
+ v = v.view(b, t, self.n_kv_heads, self.head_dim)
+ if self.qk_norm == "head":
+ q, k = attn.q_norm(q), attn.k_norm(k)
+ return q, k, v
+
+
+def _assert_finite_logits(logits: torch.Tensor, rids: list[str]) -> None:
+ """Raise FloatingPointError if any sampled row holds NaN or +inf.
+
+ Sampled rows would fail loudly inside torch.multinomial, but GREEDY rows do not:
+ argmax of a row containing NaN returns the NaN's index and `log_softmax` returns
+ NaN, so a bf16 overflow silently becomes a plausible-looking token with a NaN
+ logprob that then poisons the rho correction. -inf is allowed (a masked vocab).
+ Called BEFORE `num_computed` advances, so the step is retryable and no sequence has
+ consumed a draw from its generator."""
+ ok = (logits == logits) & (logits != float("inf"))
+ if logits.numel() and not bool(ok.all()):
+ bad = [rids[i] for i in range(len(rids)) if not bool(ok[i].all())]
+ raise FloatingPointError(f"non-finite logits (NaN/+inf) for request(s) {bad}")
+
+
+def _rows(fn: Callable, x: torch.Tensor, rowwise: bool):
+ """Apply `fn` to x [1,T,D]; if `rowwise`, once per row [1,1,D] and re-join.
+
+ This is the whole invariance trick for non-attention ops: a GEMM with M=1
+ runs the same kernel on the same operands whatever the step contains."""
+ if not rowwise or x.shape[1] == 1:
+ return fn(x)
+ outs = [fn(x[:, i : i + 1]) for i in range(x.shape[1])]
+ if isinstance(outs[0], tuple):
+ return tuple(torch.cat([o[j] for o in outs], dim=1) for j in range(len(outs[0])))
+ return torch.cat(outs, dim=1)
+
+
+# ---------------------------------------------------------------------------
+# Engine
+# ---------------------------------------------------------------------------
+
+
+class PagedEngine:
+ """Continuous-batching sampler. Usage:
+
+ eng = PagedEngine(model, PagedEngineConfig(num_blocks=64))
+ eng.add_request("a", prompt_ids, SamplingParams(max_tokens=32, seed=1))
+ while eng.has_unfinished():
+ eng.step() # may add_request between steps
+ res = eng.results["a"]
+ """
+
+ def __init__(self, model: nn.Module, cfg: Optional[PagedEngineConfig] = None, *,
+ tokenizer=None, return_logits: bool = False):
+ self.cfg = cfg if cfg is not None else PagedEngineConfig()
+ self.cfg.validate()
+ self.model = model
+ self.view = _ModelView(model)
+ self.tokenizer = tokenizer
+ self.return_logits = return_logits
+ p0 = next(model.parameters())
+ self.device = p0.device
+ kv_dtype = self.cfg.kv_dtype or p0.dtype
+ mcfg = getattr(model, "cfg", None)
+ self.max_seq_len = self.cfg.max_seq_len or getattr(mcfg, "max_position_embeddings", 2048)
+ self.cache = PagedKVCache(
+ self.view.n_layers, self.cfg.num_blocks, self.cfg.block_size,
+ self.view.n_kv_heads, self.view.head_dim, kv_dtype, self.device,
+ )
+ self.rowwise = self.cfg.numerics == "invariant"
+ self.attention: PagedAttention = self.cfg.attention or TorchPagedAttention(
+ "row" if self.rowwise else "segment"
+ )
+ self.waiting: list[_Seq] = []
+ self.running: list[_Seq] = []
+ self.results: dict[str, PagedResult] = {}
+ self._known: set[str] = set()
+ self._arrivals = 0
+ self.stats = dict(steps=0, tokens_forward=0, preemptions=0, prefix_tokens_shared=0,
+ peak_blocks_used=0, tokens_sampled=0, forward_seconds=0.0)
+
+ # -- requests ---------------------------------------------------------------
+
+ def add_request(self, rid: str, prompt_ids: Sequence[int], params: Optional[SamplingParams] = None) -> None:
+ params = params if params is not None else SamplingParams()
+ params.validate()
+ if rid in self._known:
+ raise ValueError(f"duplicate request id {rid!r}")
+ prompt = [int(t) for t in prompt_ids]
+ if not prompt:
+ raise ValueError(f"{rid!r}: empty prompt")
+ # An out-of-range id fails INSIDE the embedding lookup during `step()`, long after
+ # add_request returned: on CPU it is an IndexError that re-fires on every later step
+ # (the poisoned request stays in `running`, so one bad prompt wedges every other
+ # request), on CUDA a device-side assert that corrupts the whole context. Refuse it here.
+ vocab = self.view.embed.weight.shape[0]
+ bad = next((t for t in prompt if not 0 <= t < vocab), None)
+ if bad is not None:
+ raise ValueError(f"{rid!r}: token id {bad} outside the model vocabulary [0, {vocab})")
+ if len(prompt) >= self.max_seq_len:
+ raise ValueError(f"{rid!r}: prompt of {len(prompt)} tokens reaches max_seq_len={self.max_seq_len}")
+ if params.stop and self.tokenizer is None:
+ raise ValueError("text stop sequences need a tokenizer")
+ # The LAST sampled token is never fed back, so it never gets KV: a request
+ # that ends with `worst` tokens holds `worst - 1` in the cache. Sizing the guard
+ # on `worst` refuses a request that fits exactly (prompt 3 + 6 new, block 4 ->
+ # 8 cached tokens = 2 blocks, yet a 2-block pool was rejected as needing 3).
+ worst = min(len(prompt) + params.max_tokens, self.max_seq_len) - 1
+ if self.cache.blocks_needed(worst) > self.cache.num_blocks:
+ raise ValueError(
+ f"{rid!r} can need {self.cache.blocks_needed(worst)} blocks but the pool has "
+ f"{self.cache.num_blocks}: it could never finish even running alone"
+ )
+ seed = params.seed if params.seed is not None else _stable_seed(rid)
+ gen = torch.Generator(device=self.device)
+ gen.manual_seed(int(seed))
+ seq = _Seq(rid=rid, prompt=prompt, params=params, order=self._arrivals, generator=gen,
+ tokens=list(prompt), logits_log=[] if self.return_logits else None)
+ self._arrivals += 1
+ self._known.add(rid)
+ self.waiting.append(seq)
+
+ def has_unfinished(self) -> bool:
+ return bool(self.waiting or self.running)
+
+ # -- scheduling -------------------------------------------------------------
+
+ def _lcp(self, a: list[int], b: list[int]) -> int:
+ n = min(len(a), len(b))
+ i = 0
+ while i < n and a[i] == b[i]:
+ i += 1
+ return i
+
+ def _find_share(self, seq: _Seq) -> Optional[tuple[list[int], bool]]:
+ """-> (blocks to adopt, defer?) for the best leader among running seqs."""
+ bs = self.cfg.block_size
+ cap_blocks = (len(seq.tokens) - 1) // bs # always recompute the last token
+ best: list[int] = []
+ defer = False
+ for src in self.running:
+ lcp_blocks = min(self._lcp(seq.tokens, src.tokens), len(src.prompt)) // bs
+ lcp_blocks = min(lcp_blocks, cap_blocks)
+ if lcp_blocks == 0:
+ continue
+ ready = min(lcp_blocks, src.num_computed // bs)
+ if ready < lcp_blocks and src.num_computed < len(src.prompt):
+ defer = True # the leader is still computing blocks we could adopt
+ if ready > len(best):
+ best = list(self.cache.tables[src.rid][:ready])
+ return best, defer
+
+ @staticmethod
+ def _pick_victim(victims: list[_Seq]) -> _Seq:
+ """The YOUNGEST (latest arrival): it has the least computed work to throw
+ away, and older sequences keep making progress, so the oldest request is
+ never starved by newer ones."""
+ return max(victims, key=lambda s: s.order)
+
+ def _preempt(self, seq: _Seq) -> None:
+ self.cache.free_seq(seq.rid)
+ self.running.remove(seq)
+ seq.num_computed = 0
+ seq.shared_tokens = 0
+ seq.preemptions += 1
+ self.stats["preemptions"] += 1
+ self.waiting.append(seq)
+ self.waiting.sort(key=lambda s: s.order)
+
+ def _schedule(self) -> list[tuple[_Seq, int]]:
+ cfg = self.cfg
+ chunk = cfg.chunk_size or cfg.max_num_batched_tokens
+ budget = cfg.max_num_batched_tokens
+ sched: list[tuple[_Seq, int]] = []
+ scheduled: set[str] = set()
+
+ # 1) resident sequences: decodes first, then prefill chunks, oldest first
+ for seq in sorted(self.running, key=lambda s: (s.remaining > 1, s.order)):
+ if seq not in self.running or budget <= 0:
+ continue
+ n = min(seq.remaining, chunk, budget)
+ while True:
+ try:
+ self.cache.ensure_capacity(seq.rid, seq.num_computed + n)
+ break
+ except OutOfBlocks:
+ victims = [s for s in self.running if s is not seq and s.rid not in scheduled]
+ if not victims:
+ n = 0
+ break
+ self._preempt(self._pick_victim(victims))
+ if n == 0:
+ continue
+ sched.append((seq, n))
+ scheduled.add(seq.rid)
+ budget -= n
+
+ # 2) admit waiting sequences (FIFO, deferred followers skipped)
+ for seq in list(self.waiting):
+ if budget <= 0 or len(self.running) >= cfg.max_num_seqs:
+ break
+ shared: list[int] = []
+ if cfg.prefix_sharing:
+ got = self._find_share(seq)
+ shared, defer = got
+ if defer:
+ continue
+ start = len(shared) * cfg.block_size
+ n = min(len(seq.tokens) - start, chunk, budget)
+ need = self.cache.blocks_needed(len(seq.tokens)) - len(shared)
+ if not self.cache.allocator.can_allocate(need):
+ break # keep FIFO: do not let a small request leapfrog a starving big one
+ self.waiting.remove(seq)
+ self.cache.add_seq(seq.rid, shared)
+ seq.num_computed = start
+ seq.shared_tokens = start
+ self.stats["prefix_tokens_shared"] += start
+ self.cache.ensure_capacity(seq.rid, start + n)
+ self.running.append(seq)
+ sched.append((seq, n))
+ scheduled.add(seq.rid)
+ budget -= n
+ return sched
+
+ # -- one step ---------------------------------------------------------------
+
+ def step(self) -> list[tuple[str, int, Optional[str]]]:
+ """Run one scheduler step. Returns [(rid, token, finish_reason|None)] for
+ every token sampled this step."""
+ sched = self._schedule()
+ if not sched:
+ if self.running or self.waiting:
+ raise RuntimeError(
+ "scheduler made no progress: no sequence can get the blocks it needs "
+ f"(free={self.cache.allocator.num_free}, running={len(self.running)}, "
+ f"waiting={len(self.waiting)})"
+ )
+ return []
+ batch = StepBatch()
+ toks: list[int] = []
+ pos: list[int] = []
+ off = 0
+ for seq, n in sched:
+ batch.segments.append(Segment(seq.rid, off, n, seq.num_computed))
+ toks.extend(seq.tokens[seq.num_computed : seq.num_computed + n])
+ pos.extend(range(seq.num_computed, seq.num_computed + n))
+ off += n
+ # rows whose logits we need: the last row of every segment that reaches
+ # the end of its sequence's tokens
+ sample_rows = [
+ (seq, seg.offset + seg.n_new - 1)
+ for (seq, n), seg in zip(sched, batch.segments)
+ if seq.num_computed + n == len(seq.tokens)
+ ]
+ t0 = time.perf_counter()
+ with torch.no_grad():
+ logits = self._forward(
+ torch.tensor(toks, dtype=torch.long, device=self.device),
+ torch.tensor(pos, dtype=torch.long, device=self.device),
+ batch,
+ [r for _, r in sample_rows],
+ )
+ # The finiteness check reads a device scalar, i.e. it synchronises: placing it BEFORE
+ # the timer stops makes `forward_seconds` include the GPU work (CUDA launches are
+ # async, so without a sync here the stat measured kernel-launch time only).
+ _assert_finite_logits(logits, [seq.rid for seq, _ in sample_rows])
+ self.stats["forward_seconds"] += time.perf_counter() - t0
+ self.stats["steps"] += 1
+ self.stats["tokens_forward"] += off
+ self.stats["peak_blocks_used"] = max(self.stats["peak_blocks_used"], self.cache.allocator.num_used)
+
+ for seq, n in sched:
+ seq.num_computed += n
+ self.cache.lengths[seq.rid] = seq.num_computed
+
+ events = []
+ for i, (seq, _) in enumerate(sample_rows):
+ row = logits[i]
+ tok, lp = sample_row(row, seq.params, seq.generator)
+ if seq.logits_log is not None:
+ seq.logits_log.append(row.detach().float().cpu())
+ seq.tokens.append(tok)
+ seq.logprobs.append(lp)
+ self.stats["tokens_sampled"] += 1
+ reason = self._finish_reason(seq)
+ events.append((seq.rid, tok, reason))
+ if reason is not None:
+ self._retire(seq, reason)
+ return events
+
+ def _finish_reason(self, seq: _Seq) -> Optional[str]:
+ p, gen = seq.params, seq.tokens[len(seq.prompt):]
+ last = gen[-1]
+ if self.cfg.eos_token_id is not None and last == self.cfg.eos_token_id:
+ return "eos"
+ if last in p.stop_token_ids:
+ return "stop_token"
+ if p.stop:
+ text = self.tokenizer.decode(gen, skip_special_tokens=True)
+ if find_stop(text, p.stop) is not None:
+ return "stop_string"
+ if len(gen) >= p.max_tokens:
+ return "max_new_tokens"
+ if len(seq.tokens) >= self.max_seq_len:
+ return "max_seq_len"
+ return None
+
+ def _retire(self, seq: _Seq, reason: str) -> None:
+ seq.finish = reason
+ self.cache.free_seq(seq.rid)
+ self.running.remove(seq)
+ gen = seq.tokens[len(seq.prompt):]
+ text = self.tokenizer.decode(gen, skip_special_tokens=True) if self.tokenizer is not None else None
+ self.results[seq.rid] = PagedResult(
+ rid=seq.rid, prompt_ids=list(seq.prompt), token_ids=list(gen), logprobs=list(seq.logprobs),
+ finish_reason=reason, preemptions=seq.preemptions, shared_prefix_tokens=seq.shared_tokens,
+ text=text, step_logits=seq.logits_log,
+ )
+
+ # -- the model, layer by layer -------------------------------------------------
+
+ def _forward(self, tokens: torch.Tensor, positions: torch.Tensor, batch: StepBatch,
+ sample_row_ids: list[int]) -> torch.Tensor:
+ v, rw = self.view, self.rowwise
+ x = v.embed(tokens).unsqueeze(0) # [1, T, D]
+ cos, sin = v.rope(positions.unsqueeze(0))
+ for li, blk in enumerate(v.layers):
+ attn = blk.attn
+ h = _rows(blk.attn_norm, x, rw)
+ q, k, val = _rows(lambda z: v.qkv(attn, z), h, rw)
+ qh, kh = apply_rotary(q.transpose(1, 2), k.transpose(1, 2), cos, sin) # [1,H,T,Dh]
+ out = self.attention(
+ li, qh[0].transpose(0, 1), kh[0].transpose(0, 1), val[0],
+ self.cache, batch, v.scale,
+ ) # [T, H, Dh]
+ x = x + _rows(attn.o_proj, out.reshape(1, out.shape[0], -1), rw)
+
+ def mlp(z, blk=blk):
+ o = blk.mlp(blk.mlp_norm(z))
+ return o[0] if isinstance(o, tuple) else o
+
+ x = x + _rows(mlp, x, rw)
+ if not sample_row_ids:
+ return x.new_zeros((0, 1))
+ sel = x[:, torch.tensor(sample_row_ids, device=x.device)]
+ sel = _rows(v.final_norm, sel, rw)
+ return _rows(v.lm_head, sel, rw)[0]
+
+ # -- convenience ----------------------------------------------------------------
+
+ def generate(
+ self,
+ requests: Sequence[tuple[str, Sequence[int], SamplingParams]],
+ arrival_step: Optional[dict[str, int]] = None,
+ max_steps: int = 1_000_000,
+ ) -> dict[str, PagedResult]:
+ """Run `requests` to completion. `arrival_step[rid] = s` admits that
+ request to the queue just before step s (0 = immediately), which is how
+ tests stage 'admitted at different steps'."""
+ arrival_step = arrival_step or {}
+ pending = sorted(requests, key=lambda r: arrival_step.get(r[0], 0))
+ i, s = 0, 0
+ while i < len(pending) or self.has_unfinished():
+ while i < len(pending) and arrival_step.get(pending[i][0], 0) <= s:
+ self.add_request(*pending[i])
+ i += 1
+ if self.has_unfinished():
+ self.step()
+ s += 1
+ if s > max_steps:
+ raise RuntimeError("generate exceeded max_steps")
+ return {r[0]: self.results[r[0]] for r in requests}
diff --git a/code/align/rollout/native.py b/code/align/rollout/native.py
new file mode 100644
index 0000000000000000000000000000000000000000..1434abc453a679dd39902b671cb7b2e21c4d36e1
--- /dev/null
+++ b/code/align/rollout/native.py
@@ -0,0 +1,372 @@
+"""Native rollout: GRPO generation on torch alone, in-process, zero extra deps.
+
+This is the CORE rollout backend. vLLM (extras/vllm_rollout/) is an optional
+throughput upgrade behind a process boundary; this module is what makes the
+GRPO stage runnable with exactly torch + triton + numpy + safetensors. It
+satisfies the same `RolloutBackend` surface (submit/collect) and returns the
+same `RolloutResult` the spool client returns, so `align.rl.batch_source`
+cannot tell which backend served it.
+
+What it does, in one paragraph: tokenize each prompt ONCE (those ids are
+returned in `extras["prompt_token_ids"]` and are what assembly concatenates --
+prompts are never re-tokenized), expand to P x G `GenerationRequest`s, run
+them through `align.inference.engine.Engine` (varlen batched prefill,
+contiguous per-slot KV, completion-based refill) with
+`return_logprobs=True`, and regroup into completions[i][j] / logprobs[i][j] /
+texts[i][j]. The per-token logprobs are captured AT SAMPLING TIME from the
+very logits the token was drawn from; they are never recomputed from text.
+
+THE RHO-CORRECTION CONTRACT (the reason logprobs travel with the rollout).
+The GRPO module's `old_logprobs` is a teacher-forced pass over
+(prompt + completion): `log_softmax(logits / T)` gathered at the label.
+`vllm_logprobs` (pi^infer_old) must be the same quantity as seen by the
+sampler. With the native engine they differ only by floating-point noise
+(KV-cache decode vs full-sequence forward), so rho = exp(old - infer) == 1 up
+to ~1e-6 in fp32 -- which `tests/test_rollout_native.py` pins by recomputing
+the teacher-forced gather on the same model. The basis is therefore
+log_softmax(logits / T), UNTRUNCATED: top-p shapes which token gets drawn but
+is not a part of the distribution the training objective models, and folding
+it into the logprob would make rho != 1 on-policy for every top_p < 1.
+
+TOPOLOGY CAVEAT. The backend drives a model it can call standalone on its own
+device. That is a dedicated engine copy (the v1 topology: one engine GPU
+beside the FSDP train ranks) or a single-process run (CPU tests, 1-GPU
+smoke). It is NOT valid to point it at an FSDP2-sharded module from one rank:
+the decode loop's forwards are collectives and the other ranks would never
+join them. `GrpoBatchSource` only generates on rank 0, which is exactly why the
+sharded case needs a separate engine-side copy plus `load_weights_fn`.
+
+WEIGHT SYNC. `request.weights_dir` is provenance, not an input, unless the
+caller supplies `load_weights_fn`: then a CHANGED weights_dir triggers
+`load_weights_fn(weights_dir)` (and `engine_reloaded=True`), mirroring the
+spool worker's "checkpoint saves ARE the weight sync" contract. With no hook
+the model is assumed live (shared parameters), so there is nothing to sync.
+"""
+
+from __future__ import annotations
+
+import inspect
+import logging
+import time
+from dataclasses import dataclass
+from typing import Callable, Optional
+
+import torch
+
+from align.inference.engine import Engine, GenerationRequest, InferenceConfig
+from align.rollout.protocol import RolloutRequest, RolloutResult
+
+logger = logging.getLogger(__name__)
+
+#: How the returned logprobs are defined; stamped into `extras` so a result
+#: file is self-describing about the one thing the rho correction depends on.
+LOGPROB_BASIS = "log_softmax(logits/T)[token], fp32, no top-p truncation (T=1 for greedy)"
+
+
+def checkpoint_weight_loader(model, dtype: Optional[torch.dtype] = None) -> Callable[[str], None]:
+ """A `load_weights_fn` that refreshes a STANDALONE engine model from a
+ trainer checkpoint directory ("checkpoint saves ARE the weight sync").
+
+ Reads the HF-layout safetensors the Trainer wrote (single file or sharded
+ snapshot), converts to the align key layout and copies INTO the model's
+ existing parameters (`assign=False`): the Engine and any CUDA graphs hold
+ references to those tensors, so rebinding them would silently leave the
+ sampler on stale weights. `strict=True`: a checkpoint that does not cover
+ every parameter is a wrong sync, not a partial one.
+ """
+ from align.model import checkpoint as ckpt
+
+ def _load(weights_dir: str) -> None:
+ if dtype is None:
+ target = next(model.parameters()).dtype
+ else:
+ target = dtype
+ state = ckpt.hf_to_nmo(ckpt.load_hf_safetensors(weights_dir, dtype=target))
+ with torch.no_grad():
+ model.load_state_dict(state, strict=True)
+
+ return _load
+
+
+@dataclass
+class NativeRolloutConfig:
+ """Knobs of the native backend, one docstring per field."""
+
+ #: Concurrent sequences = KV slots. Each slot owns `max_seq_len` tokens of
+ #: KV for the engine's life (128 KiB/token at 7B bf16), so this multiplies
+ #: the cache budget directly. The engine is built with
+ #: min(n_slots, sequences-in-the-request) so a small request does not pay
+ #: for a big cache.
+ n_slots: int = 32
+ #: HARD cap on per-slot sequence length (prompt + completion). The engine
+ #: is sized to the request's actual need (longest prompt + max_tokens)
+ #: rounded up to `seq_len_granularity` and never above this; a sequence
+ #: that reaches the cap ends with finish_reason 'max_seq_len' (recorded in
+ #: extras), while a PROMPT that alone reaches it is refused loudly -- the
+ #: truncation of a prompt must be visible upstream, not done here.
+ max_seq_len_cap: int = 2048
+ #: Engine rebuilds are expensive (the KV is preallocated), so the length is
+ #: rounded up to a multiple of this to make growth rare.
+ seq_len_granularity: int = 64
+ #: Passed to the tokenizer when encoding prompts. True matches what vLLM's
+ #: `LLM.generate(prompts)` does (prepends BOS for Mistral), which keeps the
+ #: native and spool backends token-identical on the same prompt text.
+ add_special_tokens: bool = True
+ #: Used when a request carries `seed=None`: the n-th unseeded request gets
+ #: default_seed + n, so even "unseeded" rollouts are reproducible per run.
+ default_seed: int = 0
+ #: Ends a sequence when sampled. None -> take it from the tokenizer.
+ eos_token_id: Optional[int] = None
+ #: Filler for right-padded prefill rows; never read back.
+ pad_token_id: int = 0
+ #: 'length' admits shortest-first (measured default, see InferenceConfig).
+ admission_order: str = "length"
+ #: Run the engine's VRAM preflight when the KV cache is allocated on CUDA.
+ preflight: bool = True
+
+
+def _make_encoder(tokenizer, add_special_tokens: bool) -> Callable[[str], list[int]]:
+ """prompt text -> token ids, over the minimal tokenizer contract.
+
+ The contract is `encode(text) -> list[int]` and
+ `decode(ids, skip_special_tokens=True) -> str`. If `encode` takes an
+ `add_special_tokens` keyword it is passed; otherwise the tokenizer is
+ taken to apply its own convention and the flag is not forwarded. Decided
+ once from the signature, not by catching TypeError per call -- a TypeError
+ raised INSIDE a tokenizer must surface, not be mistaken for "no such kwarg".
+ """
+ enc = tokenizer.encode
+ try:
+ params = inspect.signature(enc).parameters
+ except (TypeError, ValueError): # builtins / odd callables
+ params = {}
+ accepts = "add_special_tokens" in params or any(
+ p.kind is inspect.Parameter.VAR_KEYWORD for p in params.values()
+ )
+
+ def encode(text: str) -> list[int]:
+ out = enc(text, add_special_tokens=add_special_tokens) if accepts else enc(text)
+ ids = getattr(out, "ids", out) # tolerate an Encoding-like return
+ return [int(t) for t in ids]
+
+ return encode
+
+
+class NativeRolloutBackend:
+ """In-process rollout on a torch model via the repo's own KV-cache engine.
+
+ `submit(request)` runs the whole request synchronously and parks the
+ result; `collect(request_id)` hands it over. That keeps the two-call
+ surface of the spool client (so `GrpoBatchSource` is backend-agnostic)
+ while being honest that there is no worker to wait on.
+ """
+
+ def __init__(
+ self,
+ model,
+ tokenizer,
+ cfg: Optional[NativeRolloutConfig] = None,
+ *,
+ load_weights_fn: Optional[Callable[[str], None]] = None,
+ device: Optional[torch.device] = None,
+ ):
+ if tokenizer is None:
+ raise ValueError(
+ "native rollout needs a tokenizer: prompts must be encoded here (and their "
+ "ids returned) and completions decoded to the exact text the reward path scores"
+ )
+ self.model = model
+ self.tokenizer = tokenizer
+ self.cfg = cfg if cfg is not None else NativeRolloutConfig()
+ if self.cfg.n_slots < 1 or self.cfg.max_seq_len_cap < 2 or self.cfg.seq_len_granularity < 1:
+ raise ValueError(f"bad NativeRolloutConfig: {self.cfg}")
+ self.load_weights_fn = load_weights_fn
+ self.device = device
+ eos = self.cfg.eos_token_id
+ self.eos_token_id = eos if eos is not None else getattr(tokenizer, "eos_token_id", None)
+ self._encode = _make_encoder(tokenizer, self.cfg.add_special_tokens)
+
+ self._engine: Optional[Engine] = None
+ self._results: dict[str, RolloutResult] = {}
+ self._loaded_weights_dir: Optional[str] = None
+ self._unseeded = 0
+
+ # -- the RolloutBackend surface --------------------------------------------
+
+ def submit(self, request: RolloutRequest) -> str:
+ if request.request_id in self._results:
+ # same refusal as the spool protocol's write_request
+ raise FileExistsError(f"duplicate request_id {request.request_id!r}")
+ self._results[request.request_id] = self.generate(request)
+ return request.request_id
+
+ def collect(self, request_id: str) -> RolloutResult:
+ try:
+ return self._results.pop(request_id)
+ except KeyError:
+ raise KeyError(
+ f"no pending native result for {request_id!r} (never submitted, or already "
+ "collected: results are single-use, as the spool's consumed files are)"
+ ) from None
+
+ # -- core -------------------------------------------------------------------
+
+ def generate(self, request: RolloutRequest) -> RolloutResult:
+ """One request -> one RolloutResult. Exceptions propagate directly: there
+ is no worker boundary to convert them into an `error` result across."""
+ request.validate(check_weights_dir=False)
+ reloaded = self._maybe_sync_weights(request.weights_dir)
+
+ prompt_ids = [self._encode(p) for p in request.prompts]
+ n, g = len(request.prompts), request.n_samples_per_prompt
+ engine = self._engine_for(
+ need_len=max(len(p) for p in prompt_ids) + request.max_tokens, n_sequences=n * g
+ )
+
+ seed = request.seed
+ if seed is None:
+ seed = self.cfg.default_seed + self._unseeded
+ self._unseeded += 1
+ # Re-seed per request: the sample stream is a pure function of
+ # (weights, prompts, seed), not of how many requests came before.
+ engine.generator.manual_seed(int(seed))
+
+ gen_requests = [
+ GenerationRequest(
+ rid=f"{request.request_id}:{i}:{j}",
+ prompt_ids=prompt_ids[i],
+ max_new_tokens=request.max_tokens,
+ temperature=request.temperature,
+ top_p=request.top_p,
+ )
+ for i in range(n)
+ for j in range(g)
+ ]
+
+ t0 = time.perf_counter()
+ with _rollout_mode(self.model):
+ results = engine.generate(gen_requests, return_logprobs=True)
+ gen_s = time.perf_counter() - t0
+
+ completions, logprobs, texts, finish = [], [], [], []
+ for i in range(n):
+ row = results[i * g : (i + 1) * g]
+ for r in row:
+ if r.logprobs is None or len(r.logprobs) != len(r.token_ids):
+ raise RuntimeError(
+ f"{r.rid}: engine returned {None if r.logprobs is None else len(r.logprobs)}"
+ f" logprobs for {len(r.token_ids)} tokens; the rho correction cannot run"
+ )
+ if r.text is None: # unreachable with a tokenizer; cheap to keep honest
+ raise RuntimeError(f"{r.rid}: engine returned no decoded text")
+ completions.append([r.token_ids for r in row])
+ logprobs.append([r.logprobs for r in row])
+ texts.append([r.text for r in row])
+ finish.append([r.finish_reason for r in row])
+
+ return RolloutResult(
+ request_id=request.request_id,
+ completions=completions,
+ logprobs=logprobs,
+ texts=texts,
+ weights_dir=request.weights_dir,
+ engine_reloaded=reloaded,
+ generate_seconds=gen_s,
+ extras={
+ # THE tokenization of each prompt that the completions continue
+ # from; assembly forbids re-deriving it (audit R1 class).
+ "prompt_token_ids": [list(p) for p in prompt_ids],
+ "finish_reasons": finish,
+ "backend": "native",
+ "seed": int(seed),
+ "logprob_basis": LOGPROB_BASIS,
+ "engine_max_seq_len": engine.cfg.max_seq_len,
+ "engine_n_slots": engine.cfg.n_slots,
+ },
+ )
+
+ # -- internals ----------------------------------------------------------------
+
+ def _maybe_sync_weights(self, weights_dir: str) -> bool:
+ if self.load_weights_fn is None or not weights_dir:
+ return False
+ if weights_dir == self._loaded_weights_dir:
+ return False
+ t0 = time.perf_counter()
+ self.load_weights_fn(weights_dir)
+ self._loaded_weights_dir = weights_dir
+ logger.info("native rollout: weights synced from %s in %.1fs", weights_dir,
+ time.perf_counter() - t0)
+ return True
+
+ def _engine_for(self, need_len: int, n_sequences: int) -> Engine:
+ """The cached engine if it is big enough, else a (re)built one.
+
+ Grows only: a shorter later request reuses the bigger cache, so a run
+ converges to one engine after a few steps instead of thrashing on
+ prompt-length variance. The old engine's KV is freed BEFORE the new
+ one is allocated -- both alive at once would double the cache.
+ """
+ gran = self.cfg.seq_len_granularity
+ want_len = min(self.cfg.max_seq_len_cap, -(-need_len // gran) * gran)
+ want_slots = min(self.cfg.n_slots, n_sequences)
+ eng = self._engine
+ if eng is not None and eng.cfg.max_seq_len >= want_len and eng.cfg.n_slots >= want_slots:
+ return eng
+ if eng is not None:
+ want_len = max(want_len, eng.cfg.max_seq_len)
+ want_slots = max(want_slots, eng.cfg.n_slots)
+ self._engine = None
+ del eng
+ if torch.cuda.is_available():
+ torch.cuda.empty_cache()
+ icfg = InferenceConfig(
+ n_slots=want_slots,
+ max_seq_len=want_len,
+ max_new_tokens=1, # always overridden per request
+ temperature=1.0,
+ top_p=1.0,
+ seed=self.cfg.default_seed,
+ eos_token_id=self.eos_token_id,
+ pad_token_id=self.cfg.pad_token_id,
+ admission_order=self.cfg.admission_order,
+ )
+ self._engine = Engine(
+ self.model,
+ icfg,
+ tokenizer=self.tokenizer,
+ device=self.device,
+ preflight=self.cfg.preflight,
+ )
+ return self._engine
+
+
+class _rollout_mode:
+ """Borrow a TRAINING model for sampling, then give it back untouched.
+
+ The engine flips every attention module's backend per phase (prefill vs
+ decode) and runs the model as-is; on a model that is also being trained
+ that would leak: a decode-phase 'sdpa' left on a `triton_fa2` model would
+ silently change the next TRAIN step's kernel, and a model left in eval()
+ would stay there. Both are restored in `__exit__`, including on error.
+ """
+
+ def __init__(self, model):
+ self.model = model
+
+ def __enter__(self):
+ m = self.model
+ self.was_training = m.training
+ layers = getattr(getattr(m, "model", None), "layers", None)
+ self.backends = (
+ [(blk.attn, blk.attn.attn_implementation) for blk in layers] if layers else []
+ )
+ m.eval()
+ return m
+
+ def __exit__(self, *exc):
+ for attn, impl in self.backends:
+ attn.attn_implementation = impl
+ if self.was_training:
+ self.model.train()
+ return False
diff --git a/code/align/rollout/paged.py b/code/align/rollout/paged.py
new file mode 100644
index 0000000000000000000000000000000000000000..43089b005604153192a58045716bc5f49e0300d7
--- /dev/null
+++ b/code/align/rollout/paged.py
@@ -0,0 +1,494 @@
+"""Paged KV cache for the native continuous-batching sampler (P3).
+
+WHY PAGING (the failure mode this replaces). `align.inference.engine.Engine`
+gives every slot a CONTIGUOUS `max_seq_len` strip of KV for the engine's whole
+life. For a prover-RL rollout the length distribution is brutal: most
+completions stop at a few thousand tokens, a few run to the 16k/40k cap. A
+strip sized for the cap wastes ~all of it on the short ones, so concurrency is
+`budget / cap` regardless of what is actually in flight. On the 23 GB A10G
+that is the difference between "a handful of sequences" and "as many as the
+live tokens allow" (numbers: `plan_capacity`, docs/P3_ENGINE.md). Paging cuts
+the cache into fixed `block_size`-token blocks handed out on demand from a
+free list; a sequence owns a *block table* (list of block ids) and the waste
+is bounded by one partially filled block per sequence.
+
+What lives here:
+
+ * `BlockAllocator` free-list allocator with refcounts. The refcount is what
+ makes prefix sharing safe: a block returns to the free
+ list only when its LAST owner releases it.
+ * `PagedKVCache` per-layer `[num_blocks, block_size, Hkv, D]` tensors +
+ block tables. Append is COPY-FREE: the new token's K/V is
+ written in place at (block, offset); nothing is moved or
+ re-concatenated when a sequence grows (the dense-cache
+ `torch.cat` path is O(L) per token per layer).
+ * `StepBatch` the flat, ragged description of one engine step.
+ * `TorchPagedAttention` the pure-torch attention backend (gather from the
+ block table, SDPA on the gathered K/V). Correctness
+ first. Three granularities, see its docstring.
+ * `TritonPagedAttention` INTERFACE ONLY (raises NotImplementedError): the
+ signature a fused kernel must satisfy, so the engine
+ does not change when the kernel lands.
+ * `kv_bytes_per_token`, `plan_capacity` the memory accounting.
+
+Nothing here imports anything outside torch + stdlib (+ `align.model` for the
+GQA map check), so the dependency firewall is untouched.
+"""
+
+from __future__ import annotations
+
+import math
+from dataclasses import dataclass, field
+from typing import Optional, Protocol, Sequence
+
+import torch
+import torch.nn.functional as F
+
+
+class OutOfBlocks(RuntimeError):
+ """The free list cannot satisfy an allocation. The engine catches exactly this
+ to decide on preemption; any other error is a bug and must propagate."""
+
+
+# ---------------------------------------------------------------------------
+# Allocator
+# ---------------------------------------------------------------------------
+
+
+class BlockAllocator:
+ """Free-list block allocator with reference counts.
+
+ LIFO free list: a just-freed block is the next one handed out, which keeps
+ the working set of block ids small and -- more usefully for tests --
+ deterministic, so a failing property run replays exactly.
+
+ Failure modes each check prevents:
+ * double free -> the block would sit on the free list TWICE and be handed
+ to two sequences at once; their KV would silently overwrite each other.
+ `free` raises on refcount 0.
+ * free of an out-of-range / never-issued id -> same corruption, one hop
+ later.
+ * leaks -> `check_consistent` proves `free + referenced == total` and that
+ the free list has no duplicates; the property test calls it after every
+ event.
+ """
+
+ def __init__(self, num_blocks: int):
+ if num_blocks < 1:
+ raise ValueError(f"num_blocks={num_blocks} must be >= 1")
+ self.num_blocks = int(num_blocks)
+ # reversed so pop() yields block 0 first
+ self._free: list[int] = list(range(self.num_blocks - 1, -1, -1))
+ self._ref: list[int] = [0] * self.num_blocks
+
+ @property
+ def num_free(self) -> int:
+ return len(self._free)
+
+ @property
+ def num_used(self) -> int:
+ return self.num_blocks - len(self._free)
+
+ def refcount(self, block: int) -> int:
+ return self._ref[block]
+
+ def can_allocate(self, n: int) -> bool:
+ return n <= len(self._free)
+
+ def allocate(self) -> int:
+ if not self._free:
+ raise OutOfBlocks("no free KV blocks")
+ b = self._free.pop()
+ self._ref[b] = 1
+ return b
+
+ def incref(self, block: int) -> None:
+ if not (0 <= block < self.num_blocks) or self._ref[block] <= 0:
+ raise ValueError(f"incref of block {block} which is not allocated")
+ self._ref[block] += 1
+
+ def free(self, block: int) -> None:
+ if not (0 <= block < self.num_blocks):
+ raise ValueError(f"free of out-of-range block {block}")
+ if self._ref[block] <= 0:
+ raise ValueError(f"double free of block {block}")
+ self._ref[block] -= 1
+ if self._ref[block] == 0:
+ self._free.append(block)
+
+ def check_consistent(self) -> None:
+ """Raise AssertionError unless the books balance. O(num_blocks)."""
+ free = self._free
+ assert len(set(free)) == len(free), "duplicate block on the free list"
+ for b in free:
+ assert self._ref[b] == 0, f"block {b} is free but has refcount {self._ref[b]}"
+ referenced = sum(1 for r in self._ref if r > 0)
+ assert referenced + len(free) == self.num_blocks, (
+ f"leak: {referenced} referenced + {len(free)} free != {self.num_blocks}"
+ )
+ assert all(r >= 0 for r in self._ref), "negative refcount"
+
+
+# ---------------------------------------------------------------------------
+# KV cache
+# ---------------------------------------------------------------------------
+
+
+class PagedKVCache:
+ """Block-table KV cache. One K and one V tensor per layer, each
+ `[num_blocks, block_size, n_kv_heads, head_dim]`.
+
+ A sequence is `(block_table, length)`. Token `p` of a sequence lives at
+ block `table[p // block_size]`, offset `p % block_size`. Block 0 is NOT
+ reserved: nothing pads a table with a sentinel, because every reader slices
+ by `length` instead.
+ """
+
+ def __init__(
+ self,
+ n_layers: int,
+ num_blocks: int,
+ block_size: int,
+ n_kv_heads: int,
+ head_dim: int,
+ dtype: torch.dtype = torch.float32,
+ device: Optional[torch.device | str] = None,
+ ):
+ if block_size < 1:
+ raise ValueError(f"block_size={block_size} must be >= 1")
+ self.n_layers, self.block_size = n_layers, int(block_size)
+ self.n_kv_heads, self.head_dim = n_kv_heads, head_dim
+ self.dtype, self.device = dtype, torch.device(device) if device is not None else torch.device("cpu")
+ self.allocator = BlockAllocator(num_blocks)
+ shape = (num_blocks, self.block_size, n_kv_heads, head_dim)
+ self.k = [torch.zeros(shape, dtype=dtype, device=self.device) for _ in range(n_layers)]
+ self.v = [torch.zeros(shape, dtype=dtype, device=self.device) for _ in range(n_layers)]
+ self.tables: dict[str, list[int]] = {}
+ self.lengths: dict[str, int] = {}
+
+ # -- accounting -----------------------------------------------------------
+
+ @property
+ def num_blocks(self) -> int:
+ return self.allocator.num_blocks
+
+ def blocks_needed(self, n_tokens: int) -> int:
+ return -(-n_tokens // self.block_size)
+
+ def bytes_total(self) -> int:
+ itemsize = torch.empty((), dtype=self.dtype).element_size()
+ return self.n_layers * 2 * self.num_blocks * self.block_size * self.n_kv_heads * self.head_dim * itemsize
+
+ # -- sequence lifecycle ----------------------------------------------------
+
+ def add_seq(self, seq_id: str, shared_blocks: Sequence[int] = ()) -> None:
+ """Register a sequence, optionally adopting FULL blocks of another one.
+
+ Only whole blocks can be shared: a partially filled tail block is still
+ being appended to by its owner, and a second writer (or even a reader
+ racing the owner's append) would see/clobber tokens that are not part
+ of the shared prefix. The shared blocks are incref'd, and the length
+ starts at the end of the last shared block.
+ """
+ if seq_id in self.tables:
+ raise ValueError(f"sequence {seq_id!r} already registered")
+ for b in shared_blocks:
+ self.allocator.incref(b)
+ self.tables[seq_id] = list(shared_blocks)
+ self.lengths[seq_id] = len(shared_blocks) * self.block_size
+
+ def free_seq(self, seq_id: str) -> int:
+ """Release every block of a sequence (shared blocks only drop a ref)."""
+ table = self.tables.pop(seq_id)
+ self.lengths.pop(seq_id)
+ for b in table:
+ self.allocator.free(b)
+ return len(table)
+
+ def ensure_capacity(self, seq_id: str, total_tokens: int) -> None:
+ """Grow the table to hold `total_tokens`. ALL-OR-NOTHING: if the free
+ list cannot cover the whole growth nothing is allocated, so a failed
+ reservation never strands blocks on a sequence that is about to be
+ preempted (which would make the preemption free them again -- fine --
+ but a *skipped* sequence would hold them forever)."""
+ table = self.tables[seq_id]
+ need = self.blocks_needed(total_tokens) - len(table)
+ if need <= 0:
+ return
+ if not self.allocator.can_allocate(need):
+ raise OutOfBlocks(f"need {need} blocks for {seq_id!r}, {self.allocator.num_free} free")
+ for _ in range(need):
+ table.append(self.allocator.allocate())
+
+ def slots(self, seq_id: str, start: int, n: int) -> torch.Tensor:
+ """Flat slot ids (block * block_size + offset) for positions [start, start+n).
+
+ Refuses to hand out a slot inside a block somebody else also references:
+ a write there would corrupt the sharer's KV with no error anywhere.
+ """
+ table = self.tables[seq_id]
+ bs = self.block_size
+ if start + n > len(table) * bs:
+ raise ValueError(f"{seq_id!r}: positions up to {start + n} exceed reserved {len(table) * bs}")
+ pos = torch.arange(start, start + n)
+ blocks = torch.tensor(table, dtype=torch.long)[pos // bs]
+ for b in set(blocks.tolist()):
+ if self.allocator.refcount(b) != 1:
+ raise RuntimeError(
+ f"{seq_id!r}: write into shared block {b} (refcount {self.allocator.refcount(b)})"
+ )
+ return (blocks * bs + pos % bs).to(self.device)
+
+ # -- data movement ----------------------------------------------------------
+
+ def write(self, layer: int, slots: torch.Tensor, k: torch.Tensor, v: torch.Tensor) -> None:
+ """In-place scatter of `k`,`v` [n, Hkv, D] at flat `slots`. No allocation,
+ no copy of existing KV (the 'copy-free append')."""
+ self.k[layer].view(-1, self.n_kv_heads, self.head_dim)[slots] = k.to(self.dtype)
+ self.v[layer].view(-1, self.n_kv_heads, self.head_dim)[slots] = v.to(self.dtype)
+
+ def gather(self, layer: int, seq_id: str, length: int) -> tuple[torch.Tensor, torch.Tensor]:
+ """K, V of the first `length` tokens as contiguous `[1, Hkv, length, D]`.
+
+ Contiguity is deliberate: SDPA can pick different kernels / reduction
+ orders for strided inputs, and the batch-invariance guarantee needs the
+ SAME bytes in the SAME layout whatever else is in the batch."""
+ table = self.tables[seq_id]
+ idx = torch.tensor(table[: self.blocks_needed(length)], dtype=torch.long, device=self.device)
+ k = self.k[layer][idx].reshape(-1, self.n_kv_heads, self.head_dim)[:length]
+ v = self.v[layer][idx].reshape(-1, self.n_kv_heads, self.head_dim)[:length]
+ return (
+ k.permute(1, 0, 2).unsqueeze(0).contiguous(),
+ v.permute(1, 0, 2).unsqueeze(0).contiguous(),
+ )
+
+ def block_table_tensor(self, seq_ids: Sequence[str], pad: int = 0) -> torch.Tensor:
+ """`[B, max_blocks]` int32 table (padded with `pad`; readers slice by
+ context length). This is the argument a fused kernel consumes."""
+ width = max((len(self.tables[s]) for s in seq_ids), default=0)
+ out = torch.full((len(seq_ids), max(width, 1)), pad, dtype=torch.int32)
+ for i, s in enumerate(seq_ids):
+ t = self.tables[s]
+ if t:
+ out[i, : len(t)] = torch.tensor(t, dtype=torch.int32)
+ return out.to(self.device)
+
+
+# ---------------------------------------------------------------------------
+# One engine step, flat and ragged
+# ---------------------------------------------------------------------------
+
+
+@dataclass
+class Segment:
+ """`n_new` consecutive tokens of one sequence in the flat batch.
+
+ `ctx` is the number of tokens of this sequence whose KV is ALREADY in the
+ cache before this step; the segment's token j sits at position `ctx + j`
+ and attends to positions `[0, ctx + j]`. A decode token is a segment with
+ n_new == 1; a prefill chunk is n_new > 1; they coexist in one step.
+ """
+
+ seq_id: str
+ offset: int # row of the segment's first token in the flat batch
+ n_new: int
+ ctx: int
+
+
+@dataclass
+class StepBatch:
+ segments: list[Segment] = field(default_factory=list)
+
+ @property
+ def n_tokens(self) -> int:
+ return sum(s.n_new for s in self.segments)
+
+
+# ---------------------------------------------------------------------------
+# Attention backends
+# ---------------------------------------------------------------------------
+
+
+class PagedAttention(Protocol):
+ """The seam between the engine and an attention implementation.
+
+ `q` is `[T, H, D]` (flat rows, RoPE already applied), `k_new`/`v_new` are
+ `[T, Hkv, D]` for the same rows. The backend must (1) write the new K/V into
+ `cache` for `layer` at the segments' positions, then (2) return the
+ attention output `[T, H, D]` where row r attends to the cache contents
+ `[0, position(r)]` of its own sequence. Splitting write from read inside the
+ backend lets a fused kernel do both in one launch.
+ """
+
+ def __call__(
+ self, layer: int, q: torch.Tensor, k_new: torch.Tensor, v_new: torch.Tensor,
+ cache: PagedKVCache, batch: StepBatch, scale: float,
+ ) -> torch.Tensor: ...
+
+
+class TorchPagedAttention:
+ """Pure-torch paged attention: gather K/V from the block table, run SDPA.
+
+ `mode`:
+ 'row' one SDPA call per QUERY ROW, over exactly that row's keys
+ `[0, pos]`, no mask. The operands of every call depend only on
+ the sequence's own tokens, never on what else is in the step or
+ how the prompt was chunked, so the output bits are identical
+ across batch compositions and chunk schedules. Slow; it is the
+ correctness/invariance reference and the mode RL rollouts that
+ need bit-reproducible logprobs should use.
+ 'segment' one SDPA per segment (a prefill chunk is one causal call with
+ an explicit bool mask; a decode token is mask-free) -- but all
+ decode segments of a step are *padded and batched* into one
+ call. Faster; the softmax/matmul reduction length now depends on
+ the longest sequence in the batch (padding) or the chunk size,
+ so results match 'row' to rounding only (see docs/P3_ENGINE.md
+ for the measured bf16 expectation).
+ """
+
+ def __init__(self, mode: str = "row"):
+ if mode not in ("row", "segment"):
+ raise ValueError(f"mode={mode!r}; expected 'row' or 'segment'")
+ self.mode = mode
+
+ def __call__(self, layer, q, k_new, v_new, cache, batch, scale):
+ group = q.shape[1] // k_new.shape[1]
+ gqa = group > 1
+ out = torch.empty_like(q)
+ # 1) copy-free append of every segment's new K/V
+ for s in batch.segments:
+ sl = slice(s.offset, s.offset + s.n_new)
+ cache.write(layer, cache.slots(s.seq_id, s.ctx, s.n_new), k_new[sl], v_new[sl])
+ # 2) read
+ decode = [s for s in batch.segments if s.n_new == 1] if self.mode == "segment" else []
+ decode_ids = {id(s) for s in decode}
+ for s in batch.segments:
+ if id(s) in decode_ids:
+ continue
+ total = s.ctx + s.n_new
+ kk, vv = cache.gather(layer, s.seq_id, total)
+ if kk.dtype != q.dtype: # a cache stored narrower than the model (kv_dtype=bf16/fp8): SDPA
+ kk, vv = kk.to(q.dtype), vv.to(q.dtype) # insists on one dtype, so dequantise on read
+ if self.mode == "row":
+ for j in range(s.n_new):
+ ln = s.ctx + j + 1
+ qj = q[s.offset + j].view(1, q.shape[1], 1, q.shape[2])
+ o = F.scaled_dot_product_attention(
+ qj, kk[:, :, :ln].contiguous(), vv[:, :, :ln].contiguous(),
+ enable_gqa=gqa, scale=scale,
+ )
+ out[s.offset + j] = o[0, :, 0]
+ else:
+ qs = q[s.offset : s.offset + s.n_new].permute(1, 0, 2).unsqueeze(0)
+ qpos = torch.arange(s.ctx, total, device=q.device)[:, None]
+ keep = qpos >= torch.arange(total, device=q.device)[None, :]
+ o = F.scaled_dot_product_attention(qs, kk, vv, attn_mask=keep, enable_gqa=gqa, scale=scale)
+ out[s.offset : s.offset + s.n_new] = o[0].permute(1, 0, 2)
+ if decode:
+ lens = [s.ctx + 1 for s in decode]
+ lmax = max(lens)
+ kp = q.new_zeros(len(decode), k_new.shape[1], lmax, q.shape[2])
+ vp = torch.zeros_like(kp)
+ keep = torch.zeros(len(decode), 1, 1, lmax, dtype=torch.bool, device=q.device)
+ for i, s in enumerate(decode):
+ kk, vv = cache.gather(layer, s.seq_id, lens[i])
+ kp[i, :, : lens[i]] = kk[0]
+ vp[i, :, : lens[i]] = vv[0]
+ keep[i, ..., : lens[i]] = True
+ rows = torch.tensor([s.offset for s in decode], device=q.device)
+ qd = q[rows].unsqueeze(2) # [B, H, 1, D]
+ o = F.scaled_dot_product_attention(qd, kp, vp, attn_mask=keep, enable_gqa=gqa, scale=scale)
+ out[rows] = o[:, :, 0]
+ return out
+
+
+class TritonPagedAttention:
+ """INTERFACE ONLY. A later step replaces `TorchPagedAttention` with a fused
+ kernel; this class pins what that kernel receives so the engine is untouched.
+
+ Kernel contract (vLLM-style "paged attention v1/v2" over a ragged batch):
+
+ q [T, H, D] flat query rows (RoPE applied)
+ k_cache [NB, BS, Hkv, D] this layer's K blocks
+ v_cache [NB, BS, Hkv, D]
+ block_tables [B, max_blocks] i32 from `PagedKVCache.block_table_tensor`
+ cu_seqlens_q [B+1] i32 row offsets of each segment in q
+ ctx_lens [B] i32 tokens already cached BEFORE this step
+ scale float
+ out [T, H, D]
+
+ Row r of segment b (position ctx_lens[b] + j) attends to cached positions
+ [0, ctx_lens[b] + j]; the new K/V for the step must be written to the cache
+ BEFORE the launch (or fused in the same kernel via `slot_mapping`
+ [T] i64 = block * BS + offset). Bit-invariance requirement for RL use: the
+ reduction over keys for a given row must not depend on B, T or on other
+ segments (fixed KV-split count per context length, no atomics).
+ """
+
+ def __call__(self, layer, q, k_new, v_new, cache, batch, scale): # pragma: no cover
+ raise NotImplementedError(
+ "TritonPagedAttention is an interface placeholder; use TorchPagedAttention. "
+ "See the class docstring for the kernel contract."
+ )
+
+
+# ---------------------------------------------------------------------------
+# Memory accounting
+# ---------------------------------------------------------------------------
+
+
+def kv_bytes_per_token(n_layers: int, n_kv_heads: int, head_dim: int, dtype: torch.dtype = torch.bfloat16,
+ tp_degree: int = 1) -> int:
+ """Bytes of KV one token costs on ONE device: layers x 2 (K,V) x kv-heads/tp x head_dim x itemsize.
+
+ `tp_degree` shards the KV heads (vLLM's tensor parallelism does exactly this),
+ which is why a TP=4 probe's "tokens in cache" is not comparable to a
+ single-device engine's without it. Qwen3-8B (36, 8, 128, bf16) -> 147456 B
+ = 144 KiB = the '147 KB/token' figure.
+ """
+ if n_kv_heads % tp_degree:
+ raise ValueError(f"n_kv_heads={n_kv_heads} not divisible by tp_degree={tp_degree}")
+ itemsize = torch.empty((), dtype=dtype).element_size()
+ return n_layers * 2 * (n_kv_heads // tp_degree) * head_dim * itemsize
+
+
+@dataclass(frozen=True)
+class CapacityPlan:
+ kv_bytes_per_token: int
+ kv_budget_bytes: int
+ num_blocks: int
+ block_size: int
+ kv_tokens: int
+ max_concurrent_at_cap: int # sequences that can each hold `cap` tokens simultaneously
+ cap: int
+
+
+def plan_capacity(
+ device_total_bytes: int,
+ weights_bytes: int,
+ kv_bytes_per_tok: int,
+ *,
+ utilization: float = 0.9,
+ activation_bytes: int = 0,
+ other_bytes: int = 0,
+ block_size: int = 16,
+ cap: int = 16384,
+) -> CapacityPlan:
+ """How much KV fits, and how many `cap`-token sequences that sustains.
+
+ budget = utilization * device_total - weights - activation peak - other.
+ Paging rounds each sequence up to whole blocks, so `max_concurrent_at_cap`
+ = num_blocks // ceil(cap / block_size) (a dense per-slot strip would give
+ the same number for sequences that all reach the cap; paging's advantage is
+ for the ones that do not, which is why the engine also reports live usage).
+ """
+ budget = int(utilization * device_total_bytes) - weights_bytes - activation_bytes - other_bytes
+ budget = max(budget, 0)
+ per_block = kv_bytes_per_tok * block_size
+ nb = budget // per_block
+ per_seq_blocks = math.ceil(cap / block_size)
+ return CapacityPlan(
+ kv_bytes_per_token=kv_bytes_per_tok, kv_budget_bytes=budget, num_blocks=int(nb),
+ block_size=block_size, kv_tokens=int(nb * block_size),
+ max_concurrent_at_cap=int(nb // per_seq_blocks), cap=cap,
+ )
diff --git a/code/align/rollout/protocol.py b/code/align/rollout/protocol.py
new file mode 100644
index 0000000000000000000000000000000000000000..7c4d93b26cdf1da363ea0569fbc31e6927649d99
--- /dev/null
+++ b/code/align/rollout/protocol.py
@@ -0,0 +1,227 @@
+"""Request/result shapes for rollout, plus the file protocol the spool worker speaks.
+
+DEPENDENCY-FREE ON PURPOSE: this module is imported by the training venv
+(torch 2.11/cu128), by the native in-process backend (`native.py`), AND by the
+optional vLLM worker in extras/vllm_rollout/ (its own venv, vllm/cu130); it
+may import nothing beyond the standard library, or the environments' pin sets
+start constraining each other -- the exact coupling the isolated-venv
+design exists to prevent.
+
+`RolloutRequest` / `RolloutResult` are the backend-neutral contract. The
+default backend (`native.NativeRolloutBackend`) fills them in-process with no
+file I/O; the 'spool' backend moves the SAME dataclasses through the files
+described below. The trainer cannot tell which one served it, by design.
+
+Mechanics: one JSON file per message, written atomically (.partial ->
+os.replace) into a spool directory. The worker polls `requests/`, writes
+into `results/`, and touches `heartbeat` every poll so the trainer can
+distinguish "generating" from "dead" (mtime-based liveness, per the house
+monitor-mtime-not-PIDs rule). Completions travel as token id lists plus
+the engine's per-token logprobs -- the trainer needs vllm_logprobs for the
+rho correction and must NOT re-derive them from text (re-tokenization is
+not identity).
+"""
+
+from __future__ import annotations
+
+import json
+import os
+import time
+from dataclasses import asdict, dataclass, field
+from typing import Optional, Protocol, runtime_checkable
+
+PROTOCOL_VERSION = 1
+
+
+@dataclass
+class RolloutRequest:
+ request_id: str
+ weights_dir: str # HF-loadable checkpoint dir; worker reloads on change
+ prompts: list[str]
+ n_samples_per_prompt: int
+ max_tokens: int
+ temperature: float = 1.0
+ top_p: float = 1.0
+ seed: Optional[int] = None
+ protocol_version: int = PROTOCOL_VERSION
+
+ def validate(self, *, check_weights_dir: bool = True) -> None:
+ """Shape checks always; the weights_dir existence check only for
+ backends that LOAD from it. The native backend samples from a live
+ in-process model, where weights_dir is provenance, not an input -- a
+ step-0 request legitimately names a checkpoint that has not been
+ written yet."""
+ if not self.prompts:
+ raise ValueError("empty prompt list")
+ if self.n_samples_per_prompt < 1 or self.max_tokens < 1:
+ raise ValueError(
+ f"bad shape: n={self.n_samples_per_prompt}, max_tokens={self.max_tokens}"
+ )
+ if check_weights_dir and not os.path.isdir(self.weights_dir):
+ raise FileNotFoundError(f"weights_dir does not exist: {self.weights_dir}")
+
+
+@dataclass
+class RolloutResult:
+ """One request's samples, in the form `align.rl.assembly` consumes.
+
+ Whatever the backend, the contract the trainer relies on is:
+ logprobs[i][j][t] is the logprob of completions[i][j][t] under the
+ SAMPLING-TEMPERATURE-scaled, untruncated policy distribution, so
+ rho = exp(old - infer) is ~1 when the weights agree. And
+ `extras["prompt_token_ids"]` (one id list per prompt, request order) is
+ REQUIRED: completions continue from exactly those ids, and assembly
+ refuses to re-tokenize prompt text.
+ """
+
+ request_id: str
+ #: completions[i][j] = j-th sample for prompt i: token ids WITHOUT the prompt.
+ completions: list[list[list[int]]]
+ #: logprobs[i][j][t] = engine logprob of completions[i][j][t] (the rho
+ #: correction's pi^infer_old side; label-aligned with the completion ids).
+ logprobs: list[list[list[float]]]
+ #: decoded completion texts, same nesting -- the reward path consumes text
+ #: and must see EXACTLY what the engine's tokenizer decodes, not a re-decode.
+ texts: list[list[str]]
+ weights_dir: str
+ engine_reloaded: bool
+ generate_seconds: float
+ error: Optional[str] = None
+ protocol_version: int = PROTOCOL_VERSION
+ extras: dict = field(default_factory=dict)
+
+
+@runtime_checkable
+class RolloutBackend(Protocol):
+ """What `GrpoBatchSource` consumes: submit a request, collect its result.
+
+ Implemented by `native.NativeRolloutBackend` (in-process, synchronous: the
+ work happens in `submit`) and `client.RolloutClient` (file spool to an
+ external worker: the work happens between `submit` and `collect`).
+ """
+
+ def submit(self, request: RolloutRequest) -> str: ...
+
+ def collect(self, request_id: str) -> RolloutResult: ...
+
+
+def _atomic_write_json(payload: dict, path: str) -> None:
+ tmp = path + ".partial"
+ with open(tmp, "w") as f:
+ json.dump(payload, f)
+ os.replace(tmp, path)
+
+
+def spool_dirs(spool: str) -> tuple[str, str]:
+ req = os.path.join(spool, "requests")
+ res = os.path.join(spool, "results")
+ os.makedirs(req, exist_ok=True)
+ os.makedirs(res, exist_ok=True)
+ return req, res
+
+
+def write_request(spool: str, request: RolloutRequest) -> str:
+ request.validate()
+ req_dir, _ = spool_dirs(spool)
+ path = os.path.join(req_dir, f"{request.request_id}.json")
+ if os.path.exists(path):
+ raise FileExistsError(f"duplicate request_id {request.request_id!r}")
+ _atomic_write_json(asdict(request), path)
+ return path
+
+
+def read_request(path: str) -> RolloutRequest:
+ with open(path) as f:
+ payload = json.load(f)
+ if payload.get("protocol_version") != PROTOCOL_VERSION:
+ raise RuntimeError(
+ f"protocol version mismatch: request carries "
+ f"{payload.get('protocol_version')!r}, this side speaks {PROTOCOL_VERSION}"
+ )
+ return RolloutRequest(**payload)
+
+
+def write_result(spool: str, result: RolloutResult) -> str:
+ _, res_dir = spool_dirs(spool)
+ path = os.path.join(res_dir, f"{result.request_id}.json")
+ _atomic_write_json(asdict(result), path)
+ return path
+
+
+def read_result(spool: str, request_id: str) -> Optional[RolloutResult]:
+ """Non-blocking: None until the result file exists (atomically complete)."""
+ _, res_dir = spool_dirs(spool)
+ path = os.path.join(res_dir, f"{request_id}.json")
+ if not os.path.exists(path):
+ return None
+ with open(path) as f:
+ payload = json.load(f)
+ if payload.get("protocol_version") != PROTOCOL_VERSION:
+ raise RuntimeError(
+ f"protocol version mismatch on result {request_id!r}: "
+ f"{payload.get('protocol_version')!r} vs {PROTOCOL_VERSION}"
+ )
+ return RolloutResult(**payload)
+
+
+def touch_heartbeat(spool: str) -> None:
+ path = os.path.join(spool, "heartbeat")
+ with open(path, "a"):
+ os.utime(path, None)
+
+
+def heartbeat_age_seconds(spool: str) -> float:
+ """Inf when no heartbeat exists yet (worker never started != worker slow)."""
+ path = os.path.join(spool, "heartbeat")
+ if not os.path.exists(path):
+ return float("inf")
+ return time.time() - os.path.getmtime(path)
+
+
+def wait_for_result(
+ spool: str,
+ request_id: str,
+ timeout_s: float,
+ stale_heartbeat_s: float = 600.0,
+ poll_s: float = 2.0,
+ startup_grace_s: float = 300.0,
+) -> RolloutResult:
+ """Block until the result lands; raise on timeout, dead worker, or error.
+
+ THREE distinct failure signatures, kept distinct on purpose: a TIMEOUT
+ with a live heartbeat means the request is too big for the budget; a
+ STALE heartbeat means the worker died mid-service; and NO heartbeat at
+ all means the worker has not finished booting -- which is fatal only
+ after `startup_grace_s`, measured from this wait's start. The first
+ smoke run (2026-08-24) found the missing third case the hard way: a
+ step-0 canary raced the worker's ~30s vLLM import, age=inf compared
+ `> stale_heartbeat_s` as instantly dead, and the teardown killed a
+ perfectly healthy worker before its first log line flushed.
+ """
+ start = time.time()
+ while True:
+ result = read_result(spool, request_id)
+ if result is not None:
+ if result.error:
+ raise RuntimeError(f"rollout worker reported error: {result.error}")
+ return result
+ age = heartbeat_age_seconds(spool)
+ if age == float("inf"):
+ if time.time() - start > startup_grace_s:
+ raise RuntimeError(
+ f"rollout worker produced NO heartbeat within {startup_grace_s:.0f}s "
+ f"of waiting on request {request_id!r}: it never started. Check its "
+ "log (spawn env, venv, import errors)."
+ )
+ elif age > stale_heartbeat_s:
+ raise RuntimeError(
+ f"rollout worker heartbeat is {age:.0f}s stale "
+ f"(> {stale_heartbeat_s:.0f}s) while request {request_id!r} is pending: "
+ "worker presumed dead. Check its log; do not resubmit blindly."
+ )
+ if time.time() - start > timeout_s:
+ raise TimeoutError(
+ f"rollout request {request_id!r} exceeded {timeout_s:.0f}s with a live "
+ "worker: the request shape is too big for the time budget."
+ )
+ time.sleep(poll_s)
diff --git a/code/align/rollout/tp_engine.py b/code/align/rollout/tp_engine.py
new file mode 100644
index 0000000000000000000000000000000000000000..3b036bce230017e2c86852344453860886761ff6
--- /dev/null
+++ b/code/align/rollout/tp_engine.py
@@ -0,0 +1,169 @@
+"""Tensor-parallel driver for `PagedEngine` (P3, opt-in): same scheduler, same sampler, a TP-sharded model.
+
+WHAT CHANGES vs the single-device engine, and what deliberately does not.
+
+ * The model is `align.model.tp`'s shard. `PagedEngine` drives it UNCHANGED through its own submodules, so the
+ paged KV cache is sharded by kv head for free: `_ModelView` reads the LOCAL `n_kv_heads` (Qwen3-8B at tp=4:
+ 8 -> 2) and sizes `PagedKVCache` with it. Per-rank KV bytes are exactly 1/tp (tested), the block tables,
+ refcounts, prefix sharing and preemption logic are the original code, and attention is purely local (a rank's
+ q heads read only kv heads it owns, see `align.model.tp`).
+
+ * REPLICATED SCHEDULER, NOT A BROADCAST ONE. The task offered "scheduler on rank 0 broadcasting decisions".
+ Chosen instead: every rank runs the identical deterministic scheduler on identical inputs, and rank 0 is
+ authoritative for the INPUTS (the request stream, broadcast once by `generate`) and the VERIFIER of the
+ replication. Why:
+ - Broadcasting decisions means shipping a ragged object (segments, block tables, the sampled-row list) every
+ step: a pickle + host sync on the critical path, per step, for information every rank can recompute for
+ free from state it already holds.
+ - The scheduler is a pure function of (request stream, sampled tokens). Sampled tokens are identical on
+ every rank because the logits are: the lm_head ALL-GATHER hands every rank the same bytes, and sampling
+ (per-sequence seeded generators, `_stable_seed` = sha256 not `hash()`) is a deterministic function of the
+ logits row. So replicated sampling needs no extra collective; rank-0-only sampling would need a token
+ broadcast between every forward and the next (a serial hop) PLUS the full-logit row on rank 0 anyway.
+ - The one real risk of replication is SILENT DIVERGENCE (a single differing token or schedule makes ranks
+ issue different collectives: an NCCL hang for the timeout, or all-reduces of mismatched shapes). That is
+ closed by two tiny consensus all-gathers per step (8 bytes/rank each, vs 2*n_layers+1 all-reduces of
+ [rows, hidden]): `_schedule` checks a fingerprint of the step's schedule BEFORE any model collective, and
+ `has_unfinished` checks the previous step's events (tokens, finish reasons) and the loop
+ predicate. The check is SYMMETRIC (all-gather, every rank sees every fingerprint), so a divergence raises
+ `TPDesyncError` on ALL ranks instead of leaving the agreeing ones blocked in the next collective.
+
+ * Residual rounding. Under bf16 the row-parallel all-reduce sums per-rank partials each rounded to bf16, so TP
+ logits differ from the unsharded model's by bf16 rounding (measured in docs/P3_ENGINE.md); in fp64 they agree
+ to ~1e-15. `numerics='invariant'` keeps its meaning under TP: row-wise calls keep every all-reduce the same
+ shape whatever the batch holds, so a sequence's bits do not depend on its companions (tested bitwise).
+
+Every rank must call the same methods with the same arguments in the same order (`add_request`, `step`, `generate`);
+`generate` takes its requests from rank 0 and broadcasts them, so only rank 0 needs the real list.
+"""
+
+from __future__ import annotations
+
+import hashlib
+import struct
+from typing import Optional, Sequence
+
+import torch
+
+from align.model.tp import TPGroup
+from align.rollout.engine import PagedEngine, PagedEngineConfig, PagedResult, SamplingParams
+
+
+class TPDesyncError(RuntimeError):
+ """Ranks disagree about the schedule, the sampled tokens or whether work remains. Raised on EVERY rank."""
+
+
+class _FullVocabEmbed:
+ """What `PagedEngine.add_request` sees as the embedding: a `.weight` whose shape[0] is the FULL vocabulary
+ (the real weight is this rank's shard, whose row count would reject every valid id >= V/tp), and the sharded
+ lookup as the call. The meta tensor allocates nothing."""
+
+ def __init__(self, emb):
+ self._emb = emb
+ self.weight = torch.empty((emb.full_vocab, emb.weight.shape[1]), device="meta")
+
+ def __call__(self, ids):
+ return self._emb(ids)
+
+
+def _digest(parts: Sequence[bytes]) -> int:
+ h = hashlib.sha256()
+ for p in parts:
+ h.update(struct.pack(" int:
+ """Hash of everything that decides the step's collectives and shapes: for each scheduled sequence its id, how
+ many tokens it already has computed, how many it adds, and the token ids it feeds. Module-level so a test can
+ substitute a constant (divergence then goes undetected until a collective hangs)."""
+ parts = []
+ for seq, n in sched:
+ parts.append(f"{seq.rid}|{seq.num_computed}|{n}".encode())
+ parts.append(struct.pack(f"<{n}q", *seq.tokens[seq.num_computed : seq.num_computed + n]))
+ return _digest(parts)
+
+
+def events_fingerprint(events) -> int:
+ """Hash of a step's sampled events: request id, token and finish reason. (Logprobs need no hash of their own: they are
+ a deterministic function of the all-gathered logits row and the token, both already agreed.)"""
+ return _digest([f"{rid}|{tok}|{reason}".encode() for rid, tok, reason in events])
+
+
+class TPPagedEngine(PagedEngine):
+ """`PagedEngine` over a `align.model.tp` shard. `verify=False` drops the two consensus all-gathers (a
+ measured-cost opt-out for a job that has proven itself; the default keeps the hang-proofing)."""
+
+ def __init__(self, model: torch.nn.Module, tp: Optional[TPGroup] = None, cfg: Optional[PagedEngineConfig] = None, *,
+ tokenizer=None, return_logits: bool = False, verify: bool = True):
+ tp = tp if tp is not None else getattr(model, "tp", None)
+ if tp is None:
+ raise ValueError("TPPagedEngine needs a TPGroup (or a model built by align.model.tp, which carries one)")
+ super().__init__(model, cfg, tokenizer=tokenizer, return_logits=return_logits)
+ self.tp, self.verify = tp, verify and tp.world_size > 1
+ emb = self.view.embed
+ if not hasattr(emb, "full_vocab"):
+ raise TypeError("model is not a tensor-parallel shard (embed_tokens has no `full_vocab`)")
+ emb.check_range = False # add_request already validates ids; skip the per-step device sync
+ self.view.embed = _FullVocabEmbed(emb)
+ self._last_events = 0
+ self.stats.update(consensus_checks=0)
+
+ # -- replication guards ----------------------------------------------------------
+
+ def _agree(self, label: str, values: list[int]) -> None:
+ got = self.tp.all_gather_i64(values, self.device)
+ self.stats["consensus_checks"] += 1
+ if any(g != got[0] for g in got):
+ raise TPDesyncError(
+ f"tensor-parallel ranks diverged at {label}: per-rank fingerprints {got} (this is rank {self.tp.rank}). "
+ "Every rank must see the same requests in the same order with the same config."
+ )
+
+ def has_unfinished(self) -> bool:
+ local = super().has_unfinished()
+ if self.verify:
+ self._agree("loop predicate / previous step's events", [int(local), self._last_events])
+ return local
+
+ def _schedule(self):
+ sched = super()._schedule()
+ if self.verify:
+ self._agree("the step schedule", [schedule_fingerprint(sched)])
+ return sched
+
+ def step(self):
+ events = super().step()
+ self._last_events = events_fingerprint(events)
+ return events
+
+ # -- driver ---------------------------------------------------------------------------
+
+ def generate(self, requests=None, arrival_step: Optional[dict[str, int]] = None, max_steps: int = 1_000_000
+ ) -> dict[str, PagedResult]:
+ """As `PagedEngine.generate`, but rank 0's `requests`/`arrival_step` are broadcast to every rank (other
+ ranks may pass None): the request stream has exactly one author."""
+ payload = None
+ if self.tp.rank == 0:
+ # Built BEFORE the broadcast, so a malformed stream would raise on rank 0 alone and leave every other
+ # rank blocked in the broadcast until the process-group timeout (minutes to hours on NCCL). The error
+ # is therefore shipped as data and re-raised everywhere.
+ try:
+ payload = ("ok", ([(rid, list(ids), p) for rid, ids, p in requests], dict(arrival_step or {})))
+ except Exception as e: # noqa: BLE001
+ payload = ("err", f"{type(e).__name__}: {e}")
+ tag, body = self.tp.broadcast_object(payload)
+ if tag == "err":
+ raise ValueError(f"rank 0 could not build the request stream: {body}")
+ reqs, arrivals = body
+ reqs = [(rid, ids, p if isinstance(p, SamplingParams) else SamplingParams(**p)) for rid, ids, p in reqs]
+ return super().generate(reqs, arrivals, max_steps)
+
+ # -- accounting --------------------------------------------------------------------------
+
+ def kv_bytes_per_rank(self) -> int:
+ return self.cache.bytes_total()
+
+
+__all__ = ["TPPagedEngine", "TPDesyncError", "schedule_fingerprint", "events_fingerprint"]
diff --git a/code/align/tokenizer/__init__.py b/code/align/tokenizer/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..8eb1a0a3412956b3efa957b75c8a7172b38e3a9d
--- /dev/null
+++ b/code/align/tokenizer/__init__.py
@@ -0,0 +1,85 @@
+"""align.tokenizer -- pure-Python (stdlib-only) Mistral-7B-v0.3 BPE tokenizer.
+
+ from align.tokenizer import Tokenizer, load_tokenizer
+ tok = load_tokenizer("/path/to/dir/with/tokenizer.json")
+ tok("a\\nb")["input_ids"] # [1, 1032, <0x0A id>, 29494] ( ▁a <0x0A> b)
+ tok.decode(ids, skip_special_tokens=True)
+
+It replaces `transformers.AutoTokenizer` for this codebase (dependency policy:
+torch/triton/numpy/safetensors only). Parity is certified against the legacy
+transformers tokenizer by the `oracle`-marked test in tests/test_tokenizer.py.
+
+Two places pure reimplementations usually get wrong -- both are load-bearing:
+
+1. Metaspace prepend_scheme="first".
+ The pre-tokenizer replaces ' ' with U+2581 and prepends U+2581 to split #0 of
+ the whole input -- and only if that split is NOT an added token. Added tokens
+ (, [INST], [control_N], ...) are carved out of the raw text before the
+ pre-tokenizer runs; they occupy a split index but skip the pre-tokenizer, so
+ "first" is neither "first character of the string" nor "first segment after
+ each special token" nor "first non-added segment":
+ "helloworld" -> ▁hello world (prefix on split 0 only)
+ "hello" -> hello (bare 'hello': the leading used
+ up split 0 -- measured, not guessed)
+ " hello" -> ▁hello (already starts with U+2581: no
+ second prefix)
+ With split=false the whole non-added segment is a single BPE "word", so
+ merges can run across spaces and newlines ('\\n' is not touched by Metaspace;
+ it has no vocab piece and falls back to the byte token <0x0A>, the id the vocab assigns it).
+
+2. Strip(" ", start=1) at the END of the decoder chain.
+ decode = Replace(U+2581 -> ' ') / ByteFallback / Fuse / Strip. Strip removes
+ at most ONE leading space from the FUSED string. Consequences: decode of the
+ ids for "hello" is "hello" (the prepended ▁ is stripped, round-trip exact),
+ but decoding a mid-sequence chunk [▁world] gives "world", not " world" -- so
+ decoding token deltas and concatenating drops spaces. Decode the whole
+ sequence and diff the TEXT (the inference engine does exactly that). Also:
+ ByteFallback decodes each maximal run of <0xNN> pieces as one UTF-8 unit, and
+ an invalid run yields one U+FFFD per byte (not Python's errors="replace").
+
+The loader refuses (NotImplementedError) any tokenizer.json whose pipeline
+differs from the one above, rather than silently producing wrong ids.
+
+Byte-level BPE (Qwen2/Qwen3, e.g. Goedel-Prover-V2-8B) lives in `.bytelevel`
+(`ByteLevelTokenizer`); see that module's docstring. `load_tokenizer` dispatches on
+tokenizer.json (model.type + pre_tokenizer): Metaspace -> the Mistral `Tokenizer`
+above, completely unchanged; Split+ByteLevel -> `ByteLevelTokenizer`; anything else
+-> NotImplementedError (never a guess).
+"""
+from __future__ import annotations
+
+import json
+import os
+from typing import Union
+
+from .bytelevel import ByteLevelTokenizer
+from .core import Tokenizer, _resolve_dir
+
+__all__ = ["Tokenizer", "ByteLevelTokenizer", "load_tokenizer"]
+
+
+def _pre_tokenizer_kinds(pt) -> set:
+ if not pt:
+ return set()
+ if pt.get("type") == "Sequence":
+ return {p.get("type") for p in pt.get("pretokenizers", [])}
+ return {pt.get("type")}
+
+
+def load_tokenizer(path: Union[str, os.PathLike]):
+ """Local-only loader. Mistral (Metaspace) -> Tokenizer; Qwen (ByteLevel) ->
+ ByteLevelTokenizer. The choice is made from tokenizer.json, never from the
+ directory name, and every other family fails loudly instead of being fed to a
+ pipeline that would encode it wrong."""
+ d = _resolve_dir(os.fspath(path))
+ with open(os.path.join(d, "tokenizer.json"), "rb") as f:
+ tj = json.loads(f.read())
+ kinds = _pre_tokenizer_kinds(tj.get("pre_tokenizer"))
+ if "ByteLevel" in kinds:
+ return ByteLevelTokenizer.from_pretrained(d)
+ if kinds == {"Metaspace"}:
+ return Tokenizer.from_pretrained(d)
+ raise NotImplementedError(
+ f"no tokenizer implementation for pre_tokenizer {sorted(k for k in kinds if k)!r} "
+ f"in {d!r}; supported: Metaspace (Mistral), Split+ByteLevel (Qwen2/3)"
+ )
diff --git a/code/align/tokenizer/_qwen2_unicode.py b/code/align/tokenizer/_qwen2_unicode.py
new file mode 100644
index 0000000000000000000000000000000000000000..b6a7ca79a1641e93a0b07cb4c385d6dbad27d01e
--- /dev/null
+++ b/code/align/tokenizer/_qwen2_unicode.py
@@ -0,0 +1,875 @@
+"""GENERATED by tests/data/make_qwen2_unicode_tables.py -- do not edit.
+
+Probed from transformers 5.19.0 / tokenizers 0.23.3 against Python unicodedata 15.0.0.
+See that script for why these are frozen.
+"""
+PYTHON_UNIDATA_VERSION = '15.0.0' # the unicodedata these deltas were measured against
+
+#: \p{L} / \p{N} exactly as the Rust regex engine sees them (inclusive ranges).
+L_RANGES = (
+ (0x41, 0x5a),
+ (0x61, 0x7a),
+ (0xaa, 0xaa),
+ (0xb5, 0xb5),
+ (0xba, 0xba),
+ (0xc0, 0xd6),
+ (0xd8, 0xf6),
+ (0xf8, 0x2c1),
+ (0x2c6, 0x2d1),
+ (0x2e0, 0x2e4),
+ (0x2ec, 0x2ec),
+ (0x2ee, 0x2ee),
+ (0x370, 0x374),
+ (0x376, 0x377),
+ (0x37a, 0x37d),
+ (0x37f, 0x37f),
+ (0x386, 0x386),
+ (0x388, 0x38a),
+ (0x38c, 0x38c),
+ (0x38e, 0x3a1),
+ (0x3a3, 0x3f5),
+ (0x3f7, 0x481),
+ (0x48a, 0x52f),
+ (0x531, 0x556),
+ (0x559, 0x559),
+ (0x560, 0x588),
+ (0x5d0, 0x5ea),
+ (0x5ef, 0x5f2),
+ (0x620, 0x64a),
+ (0x66e, 0x66f),
+ (0x671, 0x6d3),
+ (0x6d5, 0x6d5),
+ (0x6e5, 0x6e6),
+ (0x6ee, 0x6ef),
+ (0x6fa, 0x6fc),
+ (0x6ff, 0x6ff),
+ (0x710, 0x710),
+ (0x712, 0x72f),
+ (0x74d, 0x7a5),
+ (0x7b1, 0x7b1),
+ (0x7ca, 0x7ea),
+ (0x7f4, 0x7f5),
+ (0x7fa, 0x7fa),
+ (0x800, 0x815),
+ (0x81a, 0x81a),
+ (0x824, 0x824),
+ (0x828, 0x828),
+ (0x840, 0x858),
+ (0x860, 0x86a),
+ (0x870, 0x887),
+ (0x889, 0x88e),
+ (0x8a0, 0x8c9),
+ (0x904, 0x939),
+ (0x93d, 0x93d),
+ (0x950, 0x950),
+ (0x958, 0x961),
+ (0x971, 0x980),
+ (0x985, 0x98c),
+ (0x98f, 0x990),
+ (0x993, 0x9a8),
+ (0x9aa, 0x9b0),
+ (0x9b2, 0x9b2),
+ (0x9b6, 0x9b9),
+ (0x9bd, 0x9bd),
+ (0x9ce, 0x9ce),
+ (0x9dc, 0x9dd),
+ (0x9df, 0x9e1),
+ (0x9f0, 0x9f1),
+ (0x9fc, 0x9fc),
+ (0xa05, 0xa0a),
+ (0xa0f, 0xa10),
+ (0xa13, 0xa28),
+ (0xa2a, 0xa30),
+ (0xa32, 0xa33),
+ (0xa35, 0xa36),
+ (0xa38, 0xa39),
+ (0xa59, 0xa5c),
+ (0xa5e, 0xa5e),
+ (0xa72, 0xa74),
+ (0xa85, 0xa8d),
+ (0xa8f, 0xa91),
+ (0xa93, 0xaa8),
+ (0xaaa, 0xab0),
+ (0xab2, 0xab3),
+ (0xab5, 0xab9),
+ (0xabd, 0xabd),
+ (0xad0, 0xad0),
+ (0xae0, 0xae1),
+ (0xaf9, 0xaf9),
+ (0xb05, 0xb0c),
+ (0xb0f, 0xb10),
+ (0xb13, 0xb28),
+ (0xb2a, 0xb30),
+ (0xb32, 0xb33),
+ (0xb35, 0xb39),
+ (0xb3d, 0xb3d),
+ (0xb5c, 0xb5d),
+ (0xb5f, 0xb61),
+ (0xb71, 0xb71),
+ (0xb83, 0xb83),
+ (0xb85, 0xb8a),
+ (0xb8e, 0xb90),
+ (0xb92, 0xb95),
+ (0xb99, 0xb9a),
+ (0xb9c, 0xb9c),
+ (0xb9e, 0xb9f),
+ (0xba3, 0xba4),
+ (0xba8, 0xbaa),
+ (0xbae, 0xbb9),
+ (0xbd0, 0xbd0),
+ (0xc05, 0xc0c),
+ (0xc0e, 0xc10),
+ (0xc12, 0xc28),
+ (0xc2a, 0xc39),
+ (0xc3d, 0xc3d),
+ (0xc58, 0xc5a),
+ (0xc5d, 0xc5d),
+ (0xc60, 0xc61),
+ (0xc80, 0xc80),
+ (0xc85, 0xc8c),
+ (0xc8e, 0xc90),
+ (0xc92, 0xca8),
+ (0xcaa, 0xcb3),
+ (0xcb5, 0xcb9),
+ (0xcbd, 0xcbd),
+ (0xcdd, 0xcde),
+ (0xce0, 0xce1),
+ (0xcf1, 0xcf2),
+ (0xd04, 0xd0c),
+ (0xd0e, 0xd10),
+ (0xd12, 0xd3a),
+ (0xd3d, 0xd3d),
+ (0xd4e, 0xd4e),
+ (0xd54, 0xd56),
+ (0xd5f, 0xd61),
+ (0xd7a, 0xd7f),
+ (0xd85, 0xd96),
+ (0xd9a, 0xdb1),
+ (0xdb3, 0xdbb),
+ (0xdbd, 0xdbd),
+ (0xdc0, 0xdc6),
+ (0xe01, 0xe30),
+ (0xe32, 0xe33),
+ (0xe40, 0xe46),
+ (0xe81, 0xe82),
+ (0xe84, 0xe84),
+ (0xe86, 0xe8a),
+ (0xe8c, 0xea3),
+ (0xea5, 0xea5),
+ (0xea7, 0xeb0),
+ (0xeb2, 0xeb3),
+ (0xebd, 0xebd),
+ (0xec0, 0xec4),
+ (0xec6, 0xec6),
+ (0xedc, 0xedf),
+ (0xf00, 0xf00),
+ (0xf40, 0xf47),
+ (0xf49, 0xf6c),
+ (0xf88, 0xf8c),
+ (0x1000, 0x102a),
+ (0x103f, 0x103f),
+ (0x1050, 0x1055),
+ (0x105a, 0x105d),
+ (0x1061, 0x1061),
+ (0x1065, 0x1066),
+ (0x106e, 0x1070),
+ (0x1075, 0x1081),
+ (0x108e, 0x108e),
+ (0x10a0, 0x10c5),
+ (0x10c7, 0x10c7),
+ (0x10cd, 0x10cd),
+ (0x10d0, 0x10fa),
+ (0x10fc, 0x1248),
+ (0x124a, 0x124d),
+ (0x1250, 0x1256),
+ (0x1258, 0x1258),
+ (0x125a, 0x125d),
+ (0x1260, 0x1288),
+ (0x128a, 0x128d),
+ (0x1290, 0x12b0),
+ (0x12b2, 0x12b5),
+ (0x12b8, 0x12be),
+ (0x12c0, 0x12c0),
+ (0x12c2, 0x12c5),
+ (0x12c8, 0x12d6),
+ (0x12d8, 0x1310),
+ (0x1312, 0x1315),
+ (0x1318, 0x135a),
+ (0x1380, 0x138f),
+ (0x13a0, 0x13f5),
+ (0x13f8, 0x13fd),
+ (0x1401, 0x166c),
+ (0x166f, 0x167f),
+ (0x1681, 0x169a),
+ (0x16a0, 0x16ea),
+ (0x16f1, 0x16f8),
+ (0x1700, 0x1711),
+ (0x171f, 0x1731),
+ (0x1740, 0x1751),
+ (0x1760, 0x176c),
+ (0x176e, 0x1770),
+ (0x1780, 0x17b3),
+ (0x17d7, 0x17d7),
+ (0x17dc, 0x17dc),
+ (0x1820, 0x1878),
+ (0x1880, 0x1884),
+ (0x1887, 0x18a8),
+ (0x18aa, 0x18aa),
+ (0x18b0, 0x18f5),
+ (0x1900, 0x191e),
+ (0x1950, 0x196d),
+ (0x1970, 0x1974),
+ (0x1980, 0x19ab),
+ (0x19b0, 0x19c9),
+ (0x1a00, 0x1a16),
+ (0x1a20, 0x1a54),
+ (0x1aa7, 0x1aa7),
+ (0x1b05, 0x1b33),
+ (0x1b45, 0x1b4c),
+ (0x1b83, 0x1ba0),
+ (0x1bae, 0x1baf),
+ (0x1bba, 0x1be5),
+ (0x1c00, 0x1c23),
+ (0x1c4d, 0x1c4f),
+ (0x1c5a, 0x1c7d),
+ (0x1c80, 0x1c8a),
+ (0x1c90, 0x1cba),
+ (0x1cbd, 0x1cbf),
+ (0x1ce9, 0x1cec),
+ (0x1cee, 0x1cf3),
+ (0x1cf5, 0x1cf6),
+ (0x1cfa, 0x1cfa),
+ (0x1d00, 0x1dbf),
+ (0x1e00, 0x1f15),
+ (0x1f18, 0x1f1d),
+ (0x1f20, 0x1f45),
+ (0x1f48, 0x1f4d),
+ (0x1f50, 0x1f57),
+ (0x1f59, 0x1f59),
+ (0x1f5b, 0x1f5b),
+ (0x1f5d, 0x1f5d),
+ (0x1f5f, 0x1f7d),
+ (0x1f80, 0x1fb4),
+ (0x1fb6, 0x1fbc),
+ (0x1fbe, 0x1fbe),
+ (0x1fc2, 0x1fc4),
+ (0x1fc6, 0x1fcc),
+ (0x1fd0, 0x1fd3),
+ (0x1fd6, 0x1fdb),
+ (0x1fe0, 0x1fec),
+ (0x1ff2, 0x1ff4),
+ (0x1ff6, 0x1ffc),
+ (0x2071, 0x2071),
+ (0x207f, 0x207f),
+ (0x2090, 0x209c),
+ (0x2102, 0x2102),
+ (0x2107, 0x2107),
+ (0x210a, 0x2113),
+ (0x2115, 0x2115),
+ (0x2119, 0x211d),
+ (0x2124, 0x2124),
+ (0x2126, 0x2126),
+ (0x2128, 0x2128),
+ (0x212a, 0x212d),
+ (0x212f, 0x2139),
+ (0x213c, 0x213f),
+ (0x2145, 0x2149),
+ (0x214e, 0x214e),
+ (0x2183, 0x2184),
+ (0x2c00, 0x2ce4),
+ (0x2ceb, 0x2cee),
+ (0x2cf2, 0x2cf3),
+ (0x2d00, 0x2d25),
+ (0x2d27, 0x2d27),
+ (0x2d2d, 0x2d2d),
+ (0x2d30, 0x2d67),
+ (0x2d6f, 0x2d6f),
+ (0x2d80, 0x2d96),
+ (0x2da0, 0x2da6),
+ (0x2da8, 0x2dae),
+ (0x2db0, 0x2db6),
+ (0x2db8, 0x2dbe),
+ (0x2dc0, 0x2dc6),
+ (0x2dc8, 0x2dce),
+ (0x2dd0, 0x2dd6),
+ (0x2dd8, 0x2dde),
+ (0x2e2f, 0x2e2f),
+ (0x3005, 0x3006),
+ (0x3031, 0x3035),
+ (0x303b, 0x303c),
+ (0x3041, 0x3096),
+ (0x309d, 0x309f),
+ (0x30a1, 0x30fa),
+ (0x30fc, 0x30ff),
+ (0x3105, 0x312f),
+ (0x3131, 0x318e),
+ (0x31a0, 0x31bf),
+ (0x31f0, 0x31ff),
+ (0x3400, 0x4dbf),
+ (0x4e00, 0xa48c),
+ (0xa4d0, 0xa4fd),
+ (0xa500, 0xa60c),
+ (0xa610, 0xa61f),
+ (0xa62a, 0xa62b),
+ (0xa640, 0xa66e),
+ (0xa67f, 0xa69d),
+ (0xa6a0, 0xa6e5),
+ (0xa717, 0xa71f),
+ (0xa722, 0xa788),
+ (0xa78b, 0xa7cd),
+ (0xa7d0, 0xa7d1),
+ (0xa7d3, 0xa7d3),
+ (0xa7d5, 0xa7dc),
+ (0xa7f2, 0xa801),
+ (0xa803, 0xa805),
+ (0xa807, 0xa80a),
+ (0xa80c, 0xa822),
+ (0xa840, 0xa873),
+ (0xa882, 0xa8b3),
+ (0xa8f2, 0xa8f7),
+ (0xa8fb, 0xa8fb),
+ (0xa8fd, 0xa8fe),
+ (0xa90a, 0xa925),
+ (0xa930, 0xa946),
+ (0xa960, 0xa97c),
+ (0xa984, 0xa9b2),
+ (0xa9cf, 0xa9cf),
+ (0xa9e0, 0xa9e4),
+ (0xa9e6, 0xa9ef),
+ (0xa9fa, 0xa9fe),
+ (0xaa00, 0xaa28),
+ (0xaa40, 0xaa42),
+ (0xaa44, 0xaa4b),
+ (0xaa60, 0xaa76),
+ (0xaa7a, 0xaa7a),
+ (0xaa7e, 0xaaaf),
+ (0xaab1, 0xaab1),
+ (0xaab5, 0xaab6),
+ (0xaab9, 0xaabd),
+ (0xaac0, 0xaac0),
+ (0xaac2, 0xaac2),
+ (0xaadb, 0xaadd),
+ (0xaae0, 0xaaea),
+ (0xaaf2, 0xaaf4),
+ (0xab01, 0xab06),
+ (0xab09, 0xab0e),
+ (0xab11, 0xab16),
+ (0xab20, 0xab26),
+ (0xab28, 0xab2e),
+ (0xab30, 0xab5a),
+ (0xab5c, 0xab69),
+ (0xab70, 0xabe2),
+ (0xac00, 0xd7a3),
+ (0xd7b0, 0xd7c6),
+ (0xd7cb, 0xd7fb),
+ (0xf900, 0xfa6d),
+ (0xfa70, 0xfad9),
+ (0xfb00, 0xfb06),
+ (0xfb13, 0xfb17),
+ (0xfb1d, 0xfb1d),
+ (0xfb1f, 0xfb28),
+ (0xfb2a, 0xfb36),
+ (0xfb38, 0xfb3c),
+ (0xfb3e, 0xfb3e),
+ (0xfb40, 0xfb41),
+ (0xfb43, 0xfb44),
+ (0xfb46, 0xfbb1),
+ (0xfbd3, 0xfd3d),
+ (0xfd50, 0xfd8f),
+ (0xfd92, 0xfdc7),
+ (0xfdf0, 0xfdfb),
+ (0xfe70, 0xfe74),
+ (0xfe76, 0xfefc),
+ (0xff21, 0xff3a),
+ (0xff41, 0xff5a),
+ (0xff66, 0xffbe),
+ (0xffc2, 0xffc7),
+ (0xffca, 0xffcf),
+ (0xffd2, 0xffd7),
+ (0xffda, 0xffdc),
+ (0x10000, 0x1000b),
+ (0x1000d, 0x10026),
+ (0x10028, 0x1003a),
+ (0x1003c, 0x1003d),
+ (0x1003f, 0x1004d),
+ (0x10050, 0x1005d),
+ (0x10080, 0x100fa),
+ (0x10280, 0x1029c),
+ (0x102a0, 0x102d0),
+ (0x10300, 0x1031f),
+ (0x1032d, 0x10340),
+ (0x10342, 0x10349),
+ (0x10350, 0x10375),
+ (0x10380, 0x1039d),
+ (0x103a0, 0x103c3),
+ (0x103c8, 0x103cf),
+ (0x10400, 0x1049d),
+ (0x104b0, 0x104d3),
+ (0x104d8, 0x104fb),
+ (0x10500, 0x10527),
+ (0x10530, 0x10563),
+ (0x10570, 0x1057a),
+ (0x1057c, 0x1058a),
+ (0x1058c, 0x10592),
+ (0x10594, 0x10595),
+ (0x10597, 0x105a1),
+ (0x105a3, 0x105b1),
+ (0x105b3, 0x105b9),
+ (0x105bb, 0x105bc),
+ (0x105c0, 0x105f3),
+ (0x10600, 0x10736),
+ (0x10740, 0x10755),
+ (0x10760, 0x10767),
+ (0x10780, 0x10785),
+ (0x10787, 0x107b0),
+ (0x107b2, 0x107ba),
+ (0x10800, 0x10805),
+ (0x10808, 0x10808),
+ (0x1080a, 0x10835),
+ (0x10837, 0x10838),
+ (0x1083c, 0x1083c),
+ (0x1083f, 0x10855),
+ (0x10860, 0x10876),
+ (0x10880, 0x1089e),
+ (0x108e0, 0x108f2),
+ (0x108f4, 0x108f5),
+ (0x10900, 0x10915),
+ (0x10920, 0x10939),
+ (0x10980, 0x109b7),
+ (0x109be, 0x109bf),
+ (0x10a00, 0x10a00),
+ (0x10a10, 0x10a13),
+ (0x10a15, 0x10a17),
+ (0x10a19, 0x10a35),
+ (0x10a60, 0x10a7c),
+ (0x10a80, 0x10a9c),
+ (0x10ac0, 0x10ac7),
+ (0x10ac9, 0x10ae4),
+ (0x10b00, 0x10b35),
+ (0x10b40, 0x10b55),
+ (0x10b60, 0x10b72),
+ (0x10b80, 0x10b91),
+ (0x10c00, 0x10c48),
+ (0x10c80, 0x10cb2),
+ (0x10cc0, 0x10cf2),
+ (0x10d00, 0x10d23),
+ (0x10d4a, 0x10d65),
+ (0x10d6f, 0x10d85),
+ (0x10e80, 0x10ea9),
+ (0x10eb0, 0x10eb1),
+ (0x10ec2, 0x10ec4),
+ (0x10f00, 0x10f1c),
+ (0x10f27, 0x10f27),
+ (0x10f30, 0x10f45),
+ (0x10f70, 0x10f81),
+ (0x10fb0, 0x10fc4),
+ (0x10fe0, 0x10ff6),
+ (0x11003, 0x11037),
+ (0x11071, 0x11072),
+ (0x11075, 0x11075),
+ (0x11083, 0x110af),
+ (0x110d0, 0x110e8),
+ (0x11103, 0x11126),
+ (0x11144, 0x11144),
+ (0x11147, 0x11147),
+ (0x11150, 0x11172),
+ (0x11176, 0x11176),
+ (0x11183, 0x111b2),
+ (0x111c1, 0x111c4),
+ (0x111da, 0x111da),
+ (0x111dc, 0x111dc),
+ (0x11200, 0x11211),
+ (0x11213, 0x1122b),
+ (0x1123f, 0x11240),
+ (0x11280, 0x11286),
+ (0x11288, 0x11288),
+ (0x1128a, 0x1128d),
+ (0x1128f, 0x1129d),
+ (0x1129f, 0x112a8),
+ (0x112b0, 0x112de),
+ (0x11305, 0x1130c),
+ (0x1130f, 0x11310),
+ (0x11313, 0x11328),
+ (0x1132a, 0x11330),
+ (0x11332, 0x11333),
+ (0x11335, 0x11339),
+ (0x1133d, 0x1133d),
+ (0x11350, 0x11350),
+ (0x1135d, 0x11361),
+ (0x11380, 0x11389),
+ (0x1138b, 0x1138b),
+ (0x1138e, 0x1138e),
+ (0x11390, 0x113b5),
+ (0x113b7, 0x113b7),
+ (0x113d1, 0x113d1),
+ (0x113d3, 0x113d3),
+ (0x11400, 0x11434),
+ (0x11447, 0x1144a),
+ (0x1145f, 0x11461),
+ (0x11480, 0x114af),
+ (0x114c4, 0x114c5),
+ (0x114c7, 0x114c7),
+ (0x11580, 0x115ae),
+ (0x115d8, 0x115db),
+ (0x11600, 0x1162f),
+ (0x11644, 0x11644),
+ (0x11680, 0x116aa),
+ (0x116b8, 0x116b8),
+ (0x11700, 0x1171a),
+ (0x11740, 0x11746),
+ (0x11800, 0x1182b),
+ (0x118a0, 0x118df),
+ (0x118ff, 0x11906),
+ (0x11909, 0x11909),
+ (0x1190c, 0x11913),
+ (0x11915, 0x11916),
+ (0x11918, 0x1192f),
+ (0x1193f, 0x1193f),
+ (0x11941, 0x11941),
+ (0x119a0, 0x119a7),
+ (0x119aa, 0x119d0),
+ (0x119e1, 0x119e1),
+ (0x119e3, 0x119e3),
+ (0x11a00, 0x11a00),
+ (0x11a0b, 0x11a32),
+ (0x11a3a, 0x11a3a),
+ (0x11a50, 0x11a50),
+ (0x11a5c, 0x11a89),
+ (0x11a9d, 0x11a9d),
+ (0x11ab0, 0x11af8),
+ (0x11bc0, 0x11be0),
+ (0x11c00, 0x11c08),
+ (0x11c0a, 0x11c2e),
+ (0x11c40, 0x11c40),
+ (0x11c72, 0x11c8f),
+ (0x11d00, 0x11d06),
+ (0x11d08, 0x11d09),
+ (0x11d0b, 0x11d30),
+ (0x11d46, 0x11d46),
+ (0x11d60, 0x11d65),
+ (0x11d67, 0x11d68),
+ (0x11d6a, 0x11d89),
+ (0x11d98, 0x11d98),
+ (0x11ee0, 0x11ef2),
+ (0x11f02, 0x11f02),
+ (0x11f04, 0x11f10),
+ (0x11f12, 0x11f33),
+ (0x11fb0, 0x11fb0),
+ (0x12000, 0x12399),
+ (0x12480, 0x12543),
+ (0x12f90, 0x12ff0),
+ (0x13000, 0x1342f),
+ (0x13441, 0x13446),
+ (0x13460, 0x143fa),
+ (0x14400, 0x14646),
+ (0x16100, 0x1611d),
+ (0x16800, 0x16a38),
+ (0x16a40, 0x16a5e),
+ (0x16a70, 0x16abe),
+ (0x16ad0, 0x16aed),
+ (0x16b00, 0x16b2f),
+ (0x16b40, 0x16b43),
+ (0x16b63, 0x16b77),
+ (0x16b7d, 0x16b8f),
+ (0x16d40, 0x16d6c),
+ (0x16e40, 0x16e7f),
+ (0x16f00, 0x16f4a),
+ (0x16f50, 0x16f50),
+ (0x16f93, 0x16f9f),
+ (0x16fe0, 0x16fe1),
+ (0x16fe3, 0x16fe3),
+ (0x17000, 0x187f7),
+ (0x18800, 0x18cd5),
+ (0x18cff, 0x18d08),
+ (0x1aff0, 0x1aff3),
+ (0x1aff5, 0x1affb),
+ (0x1affd, 0x1affe),
+ (0x1b000, 0x1b122),
+ (0x1b132, 0x1b132),
+ (0x1b150, 0x1b152),
+ (0x1b155, 0x1b155),
+ (0x1b164, 0x1b167),
+ (0x1b170, 0x1b2fb),
+ (0x1bc00, 0x1bc6a),
+ (0x1bc70, 0x1bc7c),
+ (0x1bc80, 0x1bc88),
+ (0x1bc90, 0x1bc99),
+ (0x1d400, 0x1d454),
+ (0x1d456, 0x1d49c),
+ (0x1d49e, 0x1d49f),
+ (0x1d4a2, 0x1d4a2),
+ (0x1d4a5, 0x1d4a6),
+ (0x1d4a9, 0x1d4ac),
+ (0x1d4ae, 0x1d4b9),
+ (0x1d4bb, 0x1d4bb),
+ (0x1d4bd, 0x1d4c3),
+ (0x1d4c5, 0x1d505),
+ (0x1d507, 0x1d50a),
+ (0x1d50d, 0x1d514),
+ (0x1d516, 0x1d51c),
+ (0x1d51e, 0x1d539),
+ (0x1d53b, 0x1d53e),
+ (0x1d540, 0x1d544),
+ (0x1d546, 0x1d546),
+ (0x1d54a, 0x1d550),
+ (0x1d552, 0x1d6a5),
+ (0x1d6a8, 0x1d6c0),
+ (0x1d6c2, 0x1d6da),
+ (0x1d6dc, 0x1d6fa),
+ (0x1d6fc, 0x1d714),
+ (0x1d716, 0x1d734),
+ (0x1d736, 0x1d74e),
+ (0x1d750, 0x1d76e),
+ (0x1d770, 0x1d788),
+ (0x1d78a, 0x1d7a8),
+ (0x1d7aa, 0x1d7c2),
+ (0x1d7c4, 0x1d7cb),
+ (0x1df00, 0x1df1e),
+ (0x1df25, 0x1df2a),
+ (0x1e030, 0x1e06d),
+ (0x1e100, 0x1e12c),
+ (0x1e137, 0x1e13d),
+ (0x1e14e, 0x1e14e),
+ (0x1e290, 0x1e2ad),
+ (0x1e2c0, 0x1e2eb),
+ (0x1e4d0, 0x1e4eb),
+ (0x1e5d0, 0x1e5ed),
+ (0x1e5f0, 0x1e5f0),
+ (0x1e7e0, 0x1e7e6),
+ (0x1e7e8, 0x1e7eb),
+ (0x1e7ed, 0x1e7ee),
+ (0x1e7f0, 0x1e7fe),
+ (0x1e800, 0x1e8c4),
+ (0x1e900, 0x1e943),
+ (0x1e94b, 0x1e94b),
+ (0x1ee00, 0x1ee03),
+ (0x1ee05, 0x1ee1f),
+ (0x1ee21, 0x1ee22),
+ (0x1ee24, 0x1ee24),
+ (0x1ee27, 0x1ee27),
+ (0x1ee29, 0x1ee32),
+ (0x1ee34, 0x1ee37),
+ (0x1ee39, 0x1ee39),
+ (0x1ee3b, 0x1ee3b),
+ (0x1ee42, 0x1ee42),
+ (0x1ee47, 0x1ee47),
+ (0x1ee49, 0x1ee49),
+ (0x1ee4b, 0x1ee4b),
+ (0x1ee4d, 0x1ee4f),
+ (0x1ee51, 0x1ee52),
+ (0x1ee54, 0x1ee54),
+ (0x1ee57, 0x1ee57),
+ (0x1ee59, 0x1ee59),
+ (0x1ee5b, 0x1ee5b),
+ (0x1ee5d, 0x1ee5d),
+ (0x1ee5f, 0x1ee5f),
+ (0x1ee61, 0x1ee62),
+ (0x1ee64, 0x1ee64),
+ (0x1ee67, 0x1ee6a),
+ (0x1ee6c, 0x1ee72),
+ (0x1ee74, 0x1ee77),
+ (0x1ee79, 0x1ee7c),
+ (0x1ee7e, 0x1ee7e),
+ (0x1ee80, 0x1ee89),
+ (0x1ee8b, 0x1ee9b),
+ (0x1eea1, 0x1eea3),
+ (0x1eea5, 0x1eea9),
+ (0x1eeab, 0x1eebb),
+ (0x20000, 0x2a6df),
+ (0x2a700, 0x2b739),
+ (0x2b740, 0x2b81d),
+ (0x2b820, 0x2cea1),
+ (0x2ceb0, 0x2ebe0),
+ (0x2ebf0, 0x2ee5d),
+ (0x2f800, 0x2fa1d),
+ (0x30000, 0x3134a),
+ (0x31350, 0x323af),
+)
+N_RANGES = (
+ (0x30, 0x39),
+ (0xb2, 0xb3),
+ (0xb9, 0xb9),
+ (0xbc, 0xbe),
+ (0x660, 0x669),
+ (0x6f0, 0x6f9),
+ (0x7c0, 0x7c9),
+ (0x966, 0x96f),
+ (0x9e6, 0x9ef),
+ (0x9f4, 0x9f9),
+ (0xa66, 0xa6f),
+ (0xae6, 0xaef),
+ (0xb66, 0xb6f),
+ (0xb72, 0xb77),
+ (0xbe6, 0xbf2),
+ (0xc66, 0xc6f),
+ (0xc78, 0xc7e),
+ (0xce6, 0xcef),
+ (0xd58, 0xd5e),
+ (0xd66, 0xd78),
+ (0xde6, 0xdef),
+ (0xe50, 0xe59),
+ (0xed0, 0xed9),
+ (0xf20, 0xf33),
+ (0x1040, 0x1049),
+ (0x1090, 0x1099),
+ (0x1369, 0x137c),
+ (0x16ee, 0x16f0),
+ (0x17e0, 0x17e9),
+ (0x17f0, 0x17f9),
+ (0x1810, 0x1819),
+ (0x1946, 0x194f),
+ (0x19d0, 0x19da),
+ (0x1a80, 0x1a89),
+ (0x1a90, 0x1a99),
+ (0x1b50, 0x1b59),
+ (0x1bb0, 0x1bb9),
+ (0x1c40, 0x1c49),
+ (0x1c50, 0x1c59),
+ (0x2070, 0x2070),
+ (0x2074, 0x2079),
+ (0x2080, 0x2089),
+ (0x2150, 0x2182),
+ (0x2185, 0x2189),
+ (0x2460, 0x249b),
+ (0x24ea, 0x24ff),
+ (0x2776, 0x2793),
+ (0x2cfd, 0x2cfd),
+ (0x3007, 0x3007),
+ (0x3021, 0x3029),
+ (0x3038, 0x303a),
+ (0x3192, 0x3195),
+ (0x3220, 0x3229),
+ (0x3248, 0x324f),
+ (0x3251, 0x325f),
+ (0x3280, 0x3289),
+ (0x32b1, 0x32bf),
+ (0xa620, 0xa629),
+ (0xa6e6, 0xa6ef),
+ (0xa830, 0xa835),
+ (0xa8d0, 0xa8d9),
+ (0xa900, 0xa909),
+ (0xa9d0, 0xa9d9),
+ (0xa9f0, 0xa9f9),
+ (0xaa50, 0xaa59),
+ (0xabf0, 0xabf9),
+ (0xff10, 0xff19),
+ (0x10107, 0x10133),
+ (0x10140, 0x10178),
+ (0x1018a, 0x1018b),
+ (0x102e1, 0x102fb),
+ (0x10320, 0x10323),
+ (0x10341, 0x10341),
+ (0x1034a, 0x1034a),
+ (0x103d1, 0x103d5),
+ (0x104a0, 0x104a9),
+ (0x10858, 0x1085f),
+ (0x10879, 0x1087f),
+ (0x108a7, 0x108af),
+ (0x108fb, 0x108ff),
+ (0x10916, 0x1091b),
+ (0x109bc, 0x109bd),
+ (0x109c0, 0x109cf),
+ (0x109d2, 0x109ff),
+ (0x10a40, 0x10a48),
+ (0x10a7d, 0x10a7e),
+ (0x10a9d, 0x10a9f),
+ (0x10aeb, 0x10aef),
+ (0x10b58, 0x10b5f),
+ (0x10b78, 0x10b7f),
+ (0x10ba9, 0x10baf),
+ (0x10cfa, 0x10cff),
+ (0x10d30, 0x10d39),
+ (0x10d40, 0x10d49),
+ (0x10e60, 0x10e7e),
+ (0x10f1d, 0x10f26),
+ (0x10f51, 0x10f54),
+ (0x10fc5, 0x10fcb),
+ (0x11052, 0x1106f),
+ (0x110f0, 0x110f9),
+ (0x11136, 0x1113f),
+ (0x111d0, 0x111d9),
+ (0x111e1, 0x111f4),
+ (0x112f0, 0x112f9),
+ (0x11450, 0x11459),
+ (0x114d0, 0x114d9),
+ (0x11650, 0x11659),
+ (0x116c0, 0x116c9),
+ (0x116d0, 0x116e3),
+ (0x11730, 0x1173b),
+ (0x118e0, 0x118f2),
+ (0x11950, 0x11959),
+ (0x11bf0, 0x11bf9),
+ (0x11c50, 0x11c6c),
+ (0x11d50, 0x11d59),
+ (0x11da0, 0x11da9),
+ (0x11f50, 0x11f59),
+ (0x11fc0, 0x11fd4),
+ (0x12400, 0x1246e),
+ (0x16130, 0x16139),
+ (0x16a60, 0x16a69),
+ (0x16ac0, 0x16ac9),
+ (0x16b50, 0x16b59),
+ (0x16b5b, 0x16b61),
+ (0x16d70, 0x16d79),
+ (0x16e80, 0x16e96),
+ (0x1ccf0, 0x1ccf9),
+ (0x1d2c0, 0x1d2d3),
+ (0x1d2e0, 0x1d2f3),
+ (0x1d360, 0x1d378),
+ (0x1d7ce, 0x1d7ff),
+ (0x1e140, 0x1e149),
+ (0x1e2f0, 0x1e2f9),
+ (0x1e4f0, 0x1e4f9),
+ (0x1e5f1, 0x1e5fa),
+ (0x1e8c7, 0x1e8cf),
+ (0x1e950, 0x1e959),
+ (0x1ec71, 0x1ecab),
+ (0x1ecad, 0x1ecaf),
+ (0x1ecb1, 0x1ecb4),
+ (0x1ed01, 0x1ed2d),
+ (0x1ed2f, 0x1ed3d),
+ (0x1f100, 0x1f10c),
+ (0x1fbf0, 0x1fbf9),
+)
+
+#: Code points the Rust NFC treats as combining-class 0 although unicodedata says otherwise,
+#: plus second members of compositions the Rust side lacks. NFC is applied around them.
+NFC_BARRIERS = (
+ (0x7fd, 0x7fd),
+ (0x898, 0x89f),
+ (0x8ca, 0x8d3),
+ (0x9fe, 0x9fe),
+ (0xc3c, 0xc3c),
+ (0xd3b, 0xd3c),
+ (0xeba, 0xeba),
+ (0x1715, 0x1715),
+ (0x1abf, 0x1ace),
+ (0x1df6, 0x1dfa),
+ (0xa82c, 0xa82c),
+ (0x10d24, 0x10d27),
+ (0x10eab, 0x10eac),
+ (0x10efd, 0x10eff),
+ (0x10f46, 0x10f50),
+ (0x10f82, 0x10f85),
+ (0x11070, 0x11070),
+ (0x1133b, 0x1133b),
+ (0x1145e, 0x1145e),
+ (0x11839, 0x1183a),
+ (0x11930, 0x11930),
+ (0x1193d, 0x1193e),
+ (0x11943, 0x11943),
+ (0x119e0, 0x119e0),
+ (0x11a34, 0x11a34),
+ (0x11a47, 0x11a47),
+ (0x11a99, 0x11a99),
+ (0x11d42, 0x11d42),
+ (0x11d44, 0x11d45),
+ (0x11d97, 0x11d97),
+ (0x11f41, 0x11f42),
+ (0x16ff0, 0x16ff1),
+ (0x1e08f, 0x1e08f),
+ (0x1e130, 0x1e136),
+ (0x1e2ae, 0x1e2ae),
+ (0x1e2ec, 0x1e2ef),
+ (0x1e4ec, 0x1e4ef),
+)
diff --git a/code/align/tokenizer/bytelevel.py b/code/align/tokenizer/bytelevel.py
new file mode 100644
index 0000000000000000000000000000000000000000..c0f768f93f9cdd8b56fc5ce67b82e5dcb8fc426f
--- /dev/null
+++ b/code/align/tokenizer/bytelevel.py
@@ -0,0 +1,693 @@
+"""Pure-Python (stdlib only) byte-level BPE tokenizer for Qwen2/Qwen3 (Goedel-Prover-V2-8B).
+
+Why this exists. P2 (LoRA SFT on Goedel-Prover-V2-8B's own verified samples) needs
+token ids for prompts and completions. Before this module the only way to get
+them for a Qwen3 vocabulary was `transformers.AutoTokenizer`, i.e. the isolated
+vLLM venv: the training process would have had to import (or shell out to) a
+second Python environment just to turn text into ints, and a version skew
+between that venv and the one that trained would silently change the ids
+(measured elsewhere in this repo: transformers 5.x REBUILDS pipelines from the
+config rather than from tokenizer.json). This module reads tokenizer.json
+directly and is certified byte-identical (ids AND decode) against the HF tokenizer
+on a frozen oracle corpus by tests/test_tokenizer_bytelevel.py.
+
+Pipeline (encode), exactly what tokenizer.json declares and nothing else:
+
+ text
+ -> split on ADDED tokens (<|im_start|>, , ...), leftmost-longest,
+ on the RAW text (all are normalized=false)
+ -> per remaining segment: NFC normalizer
+ -> Split(Isolated) with the Qwen2 regex
+ -> ByteLevel: UTF-8 bytes of each piece mapped through GPT-2 bytes_to_unicode
+ -> BPE (rank-ordered merges) per piece
+ -> post-processor ByteLevel: adds nothing (no BOS/EOS)
+
+Decode: ids -> pieces (optionally drop special=true added tokens) -> join ->
+byte-decode -> UTF-8 with U+FFFD replacement.
+
+The three places a reimplementation gets this wrong -- each has a mutant in the
+test file that must make parity FAIL:
+
+1. NFC per SEGMENT, not per string. Added tokens are carved out first, so a
+ combining mark right after "<|im_end|>" has nothing to compose with. NFC on the
+ whole string would compose across the special token. Dropping NFC entirely
+ changes the ids of every decomposed-form string (e.g. "e" + U+0301).
+2. The regex has `\\p{N}` (ONE digit per piece, so "2024" is four pieces) and
+ `\\p{L}+` / `[^\\r\\n\\p{L}\\p{N}]?` (a single non-letter prefix glues onto a word).
+ Python `re` has no \\p{..}: the classes are explicit ranges (_class_body)
+ built from the frozen Rust-side tables in _qwen2_unicode.py. A "\\d+" or "\\w+" shortcut is
+ not equivalent (superscripts are \\p{N} but not \\d; "_" is \\w but not \\p{L}).
+3. BPE tie-break: lowest merge RANK wins, ties go to the LEFTMOST pair. Using
+ vocab id (or longest-match) instead of rank changes tokens inside long
+ identifiers and whitespace runs, silently.
+
+Unicode tables (measured, pinned in tests): the Rust tokenizer's Unicode tables are not
+those of any one Python. \\p{L}/\\p{N} are NEWER than CPython 3.12's unicodedata (5004
+Unicode-16 code points are letters/digits there and unassigned here) and the NFC
+normalizer is OLDER (108 combining marks of Unicode 10-15 keep combining class 0, and
+U+11935 U+11930 does not compose). A unicodedata-derived translation therefore split
+"X's" differently for those code points and canonically reordered marks HF leaves alone.
+Both are now FROZEN from the real tokenizer (_qwen2_unicode.py, generated by
+tests/data/make_qwen2_unicode_tables.py), so the result does not depend on the Python
+version for the regex classes; NFC still uses unicodedata for everything else and is pinned
+to the certified unicodedata version by a test.
+
+The loader VALIDATES the whole pipeline configuration and raises
+NotImplementedError on anything not implemented (a different regex, a missing
+NFC, ignore_merges, normalized added tokens, clean_up_tokenization_spaces,
+an unknown chat template ...). A different tokenizer.json must fail loudly, not
+silently encode wrong ids.
+"""
+from __future__ import annotations
+
+import hashlib
+import heapq
+import json
+import os
+import re
+import unicodedata
+from typing import Iterable, Optional, Sequence, Union
+
+from .core import _TOKENIZER_FILES, _as_list, _content, _expect, _resolve_dir
+from ._qwen2_unicode import L_RANGES, N_RANGES, NFC_BARRIERS
+
+__all__ = ["ByteLevelTokenizer", "QWEN2_PRETOKENIZE_REGEX", "bytes_to_unicode", "render_qwen3_chat"]
+
+#: The pre_tokenizer Split pattern the Qwen2/Qwen2.5/Qwen3 tokenizer.json files ship.
+#: The loader requires tokenizer.json to carry EXACTLY this string: the Python
+#: translation below (_build_pattern) is only valid for it.
+QWEN2_PRETOKENIZE_REGEX = (
+ r"(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\r\n\p{L}\p{N}]?\p{L}+|\p{N}| ?[^\s\p{L}\p{N}]+[\r\n]*"
+ r"|\s*[\r\n]+|\s+(?!\S)|\s+"
+)
+
+#: sha256 of the Qwen3 chat_template string that render_qwen3_chat reimplements
+#: (the subset without tools). A config carrying any other template makes
+#: apply_chat_template raise instead of rendering with the wrong one.
+QWEN3_CHAT_TEMPLATE_SHA256 = "a55ee1b1660128b7098723e0abcd92caa0788061051c62d51cbe87d9cf1974d8"
+
+_CACHE_MAX_ENTRIES = 200_000
+
+# Unicode White_Space property (what the Rust regex `\s` means). NOT Python's
+# str.isspace(): that also accepts U+001C..U+001F, which are not White_Space.
+_WHITE_SPACE = (
+ (0x09, 0x0D), (0x20, 0x20), (0x85, 0x85), (0xA0, 0xA0), (0x1680, 0x1680),
+ (0x2000, 0x200A), (0x2028, 0x2029), (0x202F, 0x202F), (0x205F, 0x205F), (0x3000, 0x3000),
+)
+
+
+def bytes_to_unicode() -> dict[int, str]:
+ """GPT-2 byte -> printable-char map (the ByteLevel alphabet): printable Latin-1
+ bytes map to themselves, the other 68 bytes to U+0100 and up, in byte order."""
+ keep = list(range(0x21, 0x7F)) + list(range(0xA1, 0xAD)) + list(range(0xAE, 0x100))
+ out: dict[int, str] = {}
+ n = 0
+ for b in range(256):
+ if b in keep:
+ out[b] = chr(b)
+ else:
+ out[b] = chr(256 + n)
+ n += 1
+ return out
+
+
+def _ranges(pred) -> list[tuple[int, int]]:
+ out: list[tuple[int, int]] = []
+ start = None
+ for cp in range(0x110000):
+ ok = pred(cp)
+ if ok and start is None:
+ start = cp
+ elif not ok and start is not None:
+ out.append((start, cp - 1))
+ start = None
+ if start is not None:
+ out.append((start, 0x10FFFF))
+ return out
+
+
+def _class_body(ranges: Iterable[tuple[int, int]]) -> str:
+ """Body of a regex character class (without the brackets) for explicit ranges."""
+ parts = []
+ for a, b in ranges:
+ parts.append(f"\\U{a:08x}" if a == b else f"\\U{a:08x}-\\U{b:08x}")
+ return "".join(parts)
+
+
+_PATTERN: Optional[re.Pattern] = None
+_BARRIER_RE: Optional[re.Pattern] = None
+
+
+def _build_pattern() -> re.Pattern:
+ """Python-`re` translation of QWEN2_PRETOKENIZE_REGEX. \\p{L} / \\p{N} are the FROZEN
+ Rust-side tables (L_RANGES / N_RANGES), \\s = White_Space. The contraction group is
+ spelled out instead of (?i:...): Python's IGNORECASE is Unicode-aware and would also
+ accept U+017F (long s) for 's -- the oracle test pins what the reference does for it."""
+ global _PATTERN
+ if _PATTERN is not None:
+ return _PATTERN
+ L = _class_body(L_RANGES)
+ N = _class_body(N_RANGES)
+ S = _class_body(_WHITE_SPACE)
+ pat = (
+ r"'(?:[sS]|[tT]|[rR][eE]|[vV][eE]|[mM]|[lL][lL]|[dD])"
+ rf"|[^\r\n{L}{N}]?[{L}]+"
+ rf"|[{N}]"
+ rf"| ?[^{S}{L}{N}]+[\r\n]*"
+ rf"|[{S}]*[\r\n]+"
+ rf"|[{S}]+(?![^{S}])"
+ rf"|[{S}]+"
+ )
+ _PATTERN = re.compile(pat)
+ return _PATTERN
+
+
+def _barrier_re() -> re.Pattern:
+ global _BARRIER_RE
+ if _BARRIER_RE is None:
+ _BARRIER_RE = re.compile("([" + _class_body(NFC_BARRIERS) + "])")
+ return _BARRIER_RE
+
+
+class ByteLevelTokenizer:
+ """Qwen2/Qwen3 byte-level BPE, API-compatible with align.tokenizer.Tokenizer
+ for the calls this repo makes (encode / __call__ / decode / ids / save).
+
+ Build with `ByteLevelTokenizer.from_pretrained(dir)` or `load_tokenizer(dir)`.
+ """
+
+ def __init__(
+ self,
+ tokenizer_json: dict,
+ tokenizer_config: Optional[dict] = None,
+ special_tokens_map: Optional[dict] = None,
+ *,
+ files: Optional[dict] = None,
+ ):
+ self._files: dict[str, bytes] = dict(files or {})
+ self._config = dict(tokenizer_config or {})
+ self._special_map = dict(special_tokens_map or {})
+ self._tokenizer_json = tokenizer_json
+ self._validate_pipeline(tokenizer_json, self._config)
+
+ model = tokenizer_json["model"]
+ base_vocab: dict[str, int] = dict(model["vocab"])
+ self._base_vocab = base_vocab
+ vocab = dict(base_vocab)
+
+ self._added: dict[str, int] = {}
+ self._special_contents: set[str] = set()
+ for t in tokenizer_json.get("added_tokens", []):
+ _expect(
+ not (t.get("lstrip") or t.get("rstrip") or t.get("single_word")),
+ f"added token {t['content']!r} with lstrip/rstrip/single_word",
+ )
+ _expect(
+ not t.get("normalized"),
+ f"added token {t['content']!r} with normalized=true (NFC-before-match not implemented)",
+ )
+ vocab[t["content"]] = int(t["id"])
+ self._added[t["content"]] = int(t["id"])
+ if t.get("special"):
+ self._special_contents.add(t["content"])
+ self._vocab = vocab
+ size = max(vocab.values()) + 1
+ self._id_to_token: list[Optional[str]] = [None] * size
+ for tok, i in vocab.items():
+ self._id_to_token[i] = tok
+
+ self._b2u = bytes_to_unicode()
+ self._u2b = {c: b for b, c in self._b2u.items()}
+ missing = [b for b, c in self._b2u.items() if c not in base_vocab]
+ _expect(not missing, f"vocab lacks ByteLevel alphabet symbols for bytes {missing[:5]}")
+
+ self._ranks: dict[tuple[str, str], int] = {}
+ for rank, m in enumerate(model["merges"]):
+ a, b = (m[0], m[1]) if isinstance(m, (list, tuple)) else m.split(" ")
+ if (a + b) not in base_vocab or a not in base_vocab or b not in base_vocab:
+ raise ValueError(f"merge #{rank} {a!r}+{b!r} references a token outside the vocab")
+ self._ranks[(a, b)] = rank # last duplicate wins (Rust HashMap semantics)
+
+ if self._added:
+ alts = sorted(self._added, key=len, reverse=True)
+ self._added_re: Optional[re.Pattern] = re.compile("|".join(re.escape(a) for a in alts))
+ else:
+ self._added_re = None
+ self._cache: dict[str, tuple[int, ...]] = {}
+ self._id_bytes: list[Optional[bytes]] = [None] * size
+
+ def pick(name: str, default: Optional[str] = None) -> Optional[str]:
+ v = _content(self._config.get(f"{name}_token"))
+ if v is None:
+ v = _content(self._special_map.get(f"{name}_token"))
+ if v is None:
+ v = default
+ return v if (v is None or v in self._vocab) else None
+
+ self.bos_token = pick("bos")
+ self.eos_token = pick("eos")
+ self.unk_token = pick("unk")
+ self.pad_token = pick("pad")
+
+ # ------------------------------------------------------------------ config
+
+ @staticmethod
+ def _validate_pipeline(tj: dict, cfg: dict) -> None:
+ model = tj["model"]
+ _expect(model.get("type") == "BPE", f"model.type={model.get('type')!r}")
+ _expect(not model.get("dropout"), "BPE dropout")
+ _expect(not model.get("continuing_subword_prefix"), "continuing_subword_prefix")
+ _expect(not model.get("end_of_word_suffix"), "end_of_word_suffix")
+ _expect(not model.get("byte_fallback"), "byte_fallback")
+ _expect(not model.get("ignore_merges"), "ignore_merges")
+ _expect(not model.get("fuse_unk"), "fuse_unk")
+ _expect(model.get("unk_token") is None, "an unk_token")
+ _expect(tj.get("normalizer") == {"type": "NFC"}, f"normalizer {tj.get('normalizer')!r} (only NFC)")
+ pt = tj.get("pre_tokenizer") or {}
+ _expect(pt.get("type") == "Sequence", f"pre_tokenizer {pt.get('type')!r}")
+ seq = pt.get("pretokenizers") or []
+ _expect(len(seq) == 2, "pre_tokenizer Sequence other than [Split, ByteLevel]")
+ sp, bl = seq
+ _expect(sp.get("type") == "Split", f"pre_tokenizer[0] {sp.get('type')!r}")
+ _expect(
+ (sp.get("pattern") or {}).get("Regex") == QWEN2_PRETOKENIZE_REGEX,
+ "a Split regex other than the Qwen2 one",
+ )
+ _expect(sp.get("behavior") == "Isolated", f"Split behavior {sp.get('behavior')!r}")
+ _expect(sp.get("invert") is False, "Split invert=true")
+ _expect(bl.get("type") == "ByteLevel", f"pre_tokenizer[1] {bl.get('type')!r}")
+ _expect(not bl.get("add_prefix_space"), "ByteLevel add_prefix_space=true")
+ _expect(bl.get("use_regex") is False, "ByteLevel use_regex=true (GPT-2 regex on top)")
+ dec = tj.get("decoder") or {}
+ _expect(dec.get("type") == "ByteLevel", f"decoder {dec.get('type')!r}")
+ pp = tj.get("post_processor")
+ if pp is not None:
+ _expect(
+ pp.get("type") == "ByteLevel" and not pp.get("add_prefix_space") and not pp.get("trim_offsets"),
+ f"post_processor {pp.get('type')!r} other than a no-op ByteLevel",
+ )
+ # tokenizer.json can carry truncation/padding; the Rust encoder applies them
+ # (silently clipping every encode). Neither is implemented.
+ _expect(tj.get("truncation") is None, "a tokenizer.json truncation setting")
+ _expect(tj.get("padding") is None, "a tokenizer.json padding setting")
+ ByteLevelTokenizer._check_added_tokens_decoder(tj, cfg)
+ # transformers applies these from the CONFIG on top of tokenizer.json.
+ _expect(not cfg.get("add_prefix_space"), "tokenizer_config.json add_prefix_space=true")
+ _expect(not cfg.get("add_bos_token"), "tokenizer_config.json add_bos_token=true")
+ _expect(
+ not cfg.get("clean_up_tokenization_spaces"),
+ "tokenizer_config.json clean_up_tokenization_spaces=true",
+ )
+ _expect(not cfg.get("split_special_tokens"), "tokenizer_config.json split_special_tokens=true")
+ _expect(cfg.get("errors", "replace") == "replace", f"decode errors={cfg.get('errors')!r}")
+
+ @staticmethod
+ def _check_added_tokens_decoder(tj: dict, cfg: dict) -> None:
+ """tokenizer_config.json `added_tokens_decoder` must AGREE with tokenizer.json's
+ added_tokens (same ids, contents and lstrip/rstrip/single_word/normalized/special).
+
+ Incident this prevents: transformers builds the added-token matcher from the
+ CONFIG as well as tokenizer.json. Measured (transformers 5.19.0, Goedel snapshot
+ with only `.lstrip=true` flipped in tokenizer_config.json): HF encodes
+ "a b" as [64, 151667, 293] (the space before the token is eaten) while a
+ reader of tokenizer.json alone gives [64, 220, 151667, 293]. A config/json pair
+ that disagrees must not load silently."""
+ adt = cfg.get("added_tokens_decoder")
+ if adt is None:
+ return
+ declared = {int(k): v for k, v in adt.items()}
+ in_json = {int(t["id"]): t for t in tj.get("added_tokens", [])}
+ _expect(
+ set(declared) == set(in_json),
+ "tokenizer_config.json added_tokens_decoder ids differ from tokenizer.json added_tokens",
+ )
+ for i, t in in_json.items():
+ c = declared[i]
+ _expect(c.get("content") == t["content"], f"added token {i} content differs between config and json")
+ for flag in ("special", "normalized", "lstrip", "rstrip", "single_word"):
+ _expect(
+ bool(c.get(flag)) == bool(t.get(flag)),
+ f"added token {t['content']!r}: {flag} differs between tokenizer_config.json and tokenizer.json",
+ )
+
+ # ------------------------------------------------------------- vocabulary
+
+ @property
+ def bos_token_id(self) -> Optional[int]:
+ return None if self.bos_token is None else self._vocab[self.bos_token]
+
+ @property
+ def eos_token_id(self) -> Optional[int]:
+ return None if self.eos_token is None else self._vocab[self.eos_token]
+
+ @property
+ def pad_token_id(self) -> Optional[int]:
+ return None if self.pad_token is None else self._vocab[self.pad_token]
+
+ @property
+ def unk_token_id(self) -> Optional[int]:
+ return None
+
+ @property
+ def vocab_size(self) -> int:
+ """BPE base vocabulary only (HF semantics): 151643 for Qwen3; len(tok) adds
+ the added tokens. The model's embedding rows (151936) are padding on top."""
+ return len(self._base_vocab)
+
+ @property
+ def all_special_tokens(self) -> list[str]:
+ return sorted(self._special_contents, key=lambda t: self._vocab[t])
+
+ @property
+ def all_special_ids(self) -> list[int]:
+ return [self._vocab[t] for t in self.all_special_tokens]
+
+ def get_vocab(self) -> dict[str, int]:
+ return dict(self._vocab)
+
+ def get_added_vocab(self) -> dict[str, int]:
+ return dict(self._added)
+
+ def __len__(self) -> int:
+ return len(self._vocab)
+
+ def __repr__(self) -> str:
+ return f"ByteLevelTokenizer(n={len(self)}, merges={len(self._ranks)}, added={len(self._added)})"
+
+ def _piece(self, i: int) -> Optional[str]:
+ if i < 0:
+ raise ValueError(f"negative token id {i}: a label-mask value leaked into a token-id path")
+ return self._id_to_token[i] if i < len(self._id_to_token) else None
+
+ def convert_ids_to_tokens(self, ids: Union[int, Iterable[int]]):
+ if isinstance(ids, int):
+ return self._piece(ids)
+ return [self._piece(int(i)) for i in _as_list(ids)]
+
+ def convert_tokens_to_ids(self, tokens: Union[str, Iterable[str]]):
+ if isinstance(tokens, str):
+ return self._vocab.get(tokens)
+ return [self._vocab.get(t) for t in tokens]
+
+ # ------------------------------------------------------------------ encode
+
+ def __call__(self, text: Union[str, Sequence[str]], add_special_tokens: bool = True) -> dict:
+ if isinstance(text, str):
+ return {"input_ids": self.encode(text, add_special_tokens=add_special_tokens)}
+ return {"input_ids": [self.encode(t, add_special_tokens=add_special_tokens) for t in text]}
+
+ def encode(self, text: str, add_special_tokens: bool = True) -> list[int]:
+ """`add_special_tokens` is accepted for API parity; Qwen3 adds none."""
+ if not isinstance(text, str):
+ raise TypeError(f"encode() takes str, got {type(text).__name__}")
+ try:
+ text.encode("utf-8")
+ except UnicodeEncodeError as e:
+ raise ValueError(f"text is not valid Unicode (lone surrogate at index {e.start})") from e
+ ids: list[int] = []
+ for is_added, seg in self._split_added(text):
+ if is_added:
+ ids.append(self._added[seg])
+ else:
+ ids.extend(self._encode_segment(self._normalize(seg)))
+ return ids
+
+ @staticmethod
+ def _normalize(seg: str) -> str:
+ """NFC as the Rust normalizer computes it. ASCII is NFC-invariant. A code point in
+ NFC_BARRIERS is a starter there (never reordered, never composed) but not in
+ Python's newer table, so the text is normalized AROUND it, never across it.
+
+ Incident this prevents: "\\u0301\\U00011d45" (acute, then Masaram Gondi virama) is
+ kept in that order by HF, but unicodedata.normalize moves the virama (ccc 9) in
+ front of the acute (ccc 230): same text, different ids."""
+ if seg.isascii():
+ return seg
+ rx = _barrier_re()
+ if rx.search(seg) is None:
+ return unicodedata.normalize("NFC", seg)
+ parts = rx.split(seg) # [text, barrier, text, barrier, ..., text]
+ return "".join(p if k & 1 else unicodedata.normalize("NFC", p) for k, p in enumerate(parts))
+
+ def _split_added(self, text: str):
+ if self._added_re is None or not text:
+ if text:
+ yield False, text
+ return
+ pos = 0
+ for m in self._added_re.finditer(text):
+ if m.start() > pos:
+ yield False, text[pos : m.start()]
+ yield True, m.group(0)
+ pos = m.end()
+ if pos < len(text):
+ yield False, text[pos:]
+
+ def _pretokenize(self, seg: str):
+ """Split(Isolated): matches AND the gaps between them become pieces (the
+ regex's last alternatives cover every character, so gaps cannot occur for
+ the Qwen2 pattern; kept so a pattern change cannot silently drop text)."""
+ pos = 0
+ for m in _build_pattern().finditer(seg):
+ if m.start() > pos:
+ yield seg[pos : m.start()]
+ yield m.group(0)
+ pos = m.end()
+ if pos < len(seg):
+ yield seg[pos:]
+
+ def _encode_segment(self, seg: str) -> list[int]:
+ out: list[int] = []
+ cache = self._cache
+ for piece in self._pretokenize(seg):
+ r = cache.get(piece)
+ if r is None:
+ r = self._bpe(piece)
+ if len(cache) >= _CACHE_MAX_ENTRIES:
+ cache.clear()
+ cache[piece] = r
+ out.extend(r)
+ return out
+
+ def _bpe(self, piece: str) -> tuple[int, ...]:
+ b2u = self._b2u
+ syms = [b2u[b] for b in piece.encode("utf-8")]
+ return self._merge(syms)
+
+ def _merge(self, syms: list[str]) -> tuple[int, ...]:
+ """Rank-ordered pair merging (linked list + lazy-deletion min-heap, O(n log n)
+ so a multi-thousand-char whitespace run is not quadratic). Tie-break:
+ lowest merge rank, then leftmost position."""
+ n = len(syms)
+ vocab = self._base_vocab
+ if n < 2:
+ return tuple(vocab[s] for s in syms)
+ ranks = self._ranks
+ nxt = list(range(1, n + 1))
+ nxt[-1] = -1
+ prv = list(range(-1, n - 1))
+ heap: list[tuple[int, int, str, str]] = []
+ for i in range(n - 1):
+ r = ranks.get((syms[i], syms[i + 1]))
+ if r is not None:
+ heap.append((r, i, syms[i], syms[i + 1]))
+ heapq.heapify(heap)
+ pop, push = heapq.heappop, heapq.heappush
+ while heap:
+ _, i, left, right = pop(heap)
+ if syms[i] != left:
+ continue
+ j = nxt[i]
+ if j == -1 or syms[j] != right:
+ continue
+ new = left + right
+ syms[i] = new
+ syms[j] = None # type: ignore[call-overload]
+ nj = nxt[j]
+ nxt[i] = nj
+ if nj != -1:
+ prv[nj] = i
+ p = prv[i]
+ if p != -1:
+ r = ranks.get((syms[p], new))
+ if r is not None:
+ push(heap, (r, p, syms[p], new))
+ if nj != -1:
+ r = ranks.get((new, syms[nj]))
+ if r is not None:
+ push(heap, (r, i, new, syms[nj]))
+ return tuple(vocab[s] for s in syms if s is not None)
+
+ # ------------------------------------------------------------------ decode
+
+ def _bytes_of(self, i: int) -> Optional[bytes]:
+ b = self._id_bytes[i]
+ if b is None:
+ t = self._id_to_token[i]
+ u2b = self._u2b
+ # Same rule as the Rust ByteLevel decoder: a token whose every char is in
+ # the alphabet is byte-decoded; anything else passes through as literal text.
+ if all(c in u2b for c in t): # type: ignore[union-attr]
+ b = bytes(u2b[c] for c in t) # type: ignore[union-attr]
+ else:
+ b = t.encode("utf-8") # type: ignore[union-attr]
+ self._id_bytes[i] = b
+ return b
+
+ def decode(self, ids, skip_special_tokens: bool = False) -> str:
+ """Join all tokens' bytes THEN decode UTF-8 once: a multi-byte character split
+ across tokens decodes correctly, a truncated one yields U+FFFD (never an
+ exception). Ids outside the vocab are skipped (HF behaviour). With
+ skip_special_tokens only added tokens flagged special=true are dropped
+ (// are special=false and stay)."""
+ special = self._special_contents
+ buf = bytearray()
+ for i in _as_list(ids):
+ i = int(i)
+ t = self._piece(i)
+ if t is None:
+ continue
+ if skip_special_tokens and t in special:
+ continue
+ buf += self._bytes_of(i) # type: ignore[arg-type]
+ return bytes(buf).decode("utf-8", errors="replace")
+
+ # ------------------------------------------------------------ chat template
+
+ def apply_chat_template(
+ self,
+ messages: Sequence[dict],
+ *,
+ tokenize: bool = False,
+ add_generation_prompt: bool = False,
+ enable_thinking: Optional[bool] = None,
+ tools=None,
+ ):
+ """Qwen3 chat template, the system/user/assistant subset. See render_qwen3_chat.
+ Raises NotImplementedError if the shipped chat_template is not the one this
+ implements (checked by sha256) or if tools are requested.
+
+ `tokenize=True` returns a plain list[int]. transformers 5.x returns a BatchEncoding
+ ({"input_ids", "attention_mask"}) there (measured, 5.19.0), so code written against
+ HF's tokenize=True result must index ["input_ids"] on HF but not here; callers in
+ this repo use tokenize=False + encode()."""
+ tpl = self._config.get("chat_template")
+ _expect(
+ tpl is not None and hashlib.sha256(tpl.encode("utf-8")).hexdigest() == QWEN3_CHAT_TEMPLATE_SHA256,
+ "a chat_template other than the Qwen3 one (sha256 mismatch)",
+ )
+ text = render_qwen3_chat(
+ messages, add_generation_prompt=add_generation_prompt, enable_thinking=enable_thinking, tools=tools
+ )
+ return self.encode(text, add_special_tokens=False) if tokenize else text
+
+ # -------------------------------------------------------------------- save
+
+ def save_pretrained(self, save_directory: str) -> tuple[str, ...]:
+ """Copy-through of the source files (atomic tmp + os.replace per file)."""
+ os.makedirs(save_directory, exist_ok=True)
+ files = dict(self._files)
+ if "tokenizer.json" not in files:
+ files["tokenizer.json"] = json.dumps(self._tokenizer_json, ensure_ascii=False).encode("utf-8")
+ if "tokenizer_config.json" not in files and self._config:
+ files["tokenizer_config.json"] = json.dumps(self._config, indent=2).encode("utf-8")
+ if "special_tokens_map.json" not in files and self._special_map:
+ files["special_tokens_map.json"] = json.dumps(self._special_map, indent=2).encode("utf-8")
+ written = []
+ for name, blob in files.items():
+ dst = os.path.join(save_directory, name)
+ tmp = dst + f".tmp.{os.getpid()}"
+ with open(tmp, "wb") as f:
+ f.write(blob)
+ f.flush()
+ os.fsync(f.fileno())
+ os.replace(tmp, dst)
+ written.append(dst)
+ return tuple(written)
+
+ @classmethod
+ def from_pretrained(cls, path: Union[str, os.PathLike]) -> "ByteLevelTokenizer":
+ """LOCAL ONLY: a dir holding tokenizer.json, a tokenizer.json path, or an HF
+ cache model dir. Never touches the network."""
+ d = _resolve_dir(os.fspath(path))
+ files: dict[str, bytes] = {}
+ for name in _TOKENIZER_FILES:
+ p = os.path.join(d, name)
+ if os.path.isfile(p):
+ with open(p, "rb") as f:
+ files[name] = f.read()
+ tj = json.loads(files["tokenizer.json"])
+ cfg = json.loads(files["tokenizer_config.json"]) if "tokenizer_config.json" in files else None
+ smap = json.loads(files["special_tokens_map.json"]) if "special_tokens_map.json" in files else None
+ return cls(tj, cfg, smap, files=files)
+
+
+def render_qwen3_chat(
+ messages: Sequence[dict],
+ *,
+ add_generation_prompt: bool = False,
+ enable_thinking: Optional[bool] = None,
+ tools=None,
+) -> str:
+ """Render the Qwen3 chat template for system/user/assistant messages.
+
+ Incident this prevents: P2 trains on (prompt, completion) pairs whose prompt
+ bytes must equal what Goedel-Prover sampled under; correction rounds replay
+ earlier assistant turns, and the template REWRITES them (earlier turns lose
+ their block; only turns after the last user query keep
+ "\\n..\\n\\n\\n"). Hand-rolled "<|im_start|>" concatenation
+ gets the multi-turn case wrong without any error. Verified against the HF
+ template output in tests/test_tokenizer_bytelevel.py.
+
+ Deliberate deviations from the Jinja (each raises instead of silently differing):
+ tools / role "tool" / tool_calls -> NotImplementedError; a role outside
+ system/user/assistant -> ValueError (Jinja drops the message); non-str content
+ -> TypeError (Jinja renders ''); empty messages -> ValueError.
+ """
+ if tools:
+ raise NotImplementedError("Qwen3 chat template with tools is not implemented")
+ if not messages:
+ raise ValueError("messages is empty")
+ for k, m in enumerate(messages):
+ role = m.get("role")
+ if role == "tool" or m.get("tool_calls"):
+ raise NotImplementedError("tool messages / tool_calls are not implemented")
+ if role not in ("system", "user", "assistant"):
+ raise ValueError(f"message {k}: unsupported role {role!r}")
+ if not isinstance(m.get("content"), str):
+ raise TypeError(f"message {k}: content must be str, got {type(m.get('content')).__name__}")
+ out: list[str] = []
+ if messages[0]["role"] == "system":
+ out.append("<|im_start|>system\n" + messages[0]["content"] + "<|im_end|>\n")
+ last_query_index = len(messages) - 1
+ for idx in range(len(messages) - 1, -1, -1):
+ c = messages[idx]["content"]
+ if messages[idx]["role"] == "user" and not (c.startswith("") and c.endswith("")):
+ last_query_index = idx
+ break
+ n = len(messages)
+ for idx, m in enumerate(messages):
+ role, content = m["role"], m["content"]
+ if role == "user" or (role == "system" and idx != 0):
+ out.append("<|im_start|>" + role + "\n" + content + "<|im_end|>\n")
+ elif role == "assistant":
+ reasoning = ""
+ rc = m.get("reasoning_content")
+ if isinstance(rc, str):
+ reasoning = rc
+ elif "" in content:
+ reasoning = content.split("")[0].rstrip("\n").split("")[-1].lstrip("\n")
+ content = content.split("")[-1].lstrip("\n")
+ is_last = idx == n - 1
+ if idx > last_query_index and (is_last or reasoning):
+ out.append(
+ "<|im_start|>assistant\n\n" + reasoning.strip("\n") + "\n\n\n" + content.lstrip("\n")
+ )
+ else:
+ out.append("<|im_start|>assistant\n" + content)
+ out.append("<|im_end|>\n")
+ if add_generation_prompt:
+ out.append("<|im_start|>assistant\n")
+ if enable_thinking is False:
+ out.append("\n\n\n\n")
+ return "".join(out)
diff --git a/code/align/tokenizer/core.py b/code/align/tokenizer/core.py
new file mode 100644
index 0000000000000000000000000000000000000000..8ffa76bf71cae3341d49e12c6ce78a55fa57cec5
--- /dev/null
+++ b/code/align/tokenizer/core.py
@@ -0,0 +1,615 @@
+"""Pure-Python (stdlib only) BPE tokenizer for Mistral-7B-v0.3.
+
+This is a from-scratch reimplementation of exactly the pipeline that
+`tokenizers`/`transformers` run for the v0.3 `tokenizer.json`, and nothing
+else. It exists so the training stack has zero dependency on `transformers`
+(see docs/DEPENDENCY_POLICY.md); parity with the legacy stack is certified by
+the `oracle`-marked test in tests/test_tokenizer.py (hundreds of strings,
+ids AND decode byte-identical).
+
+Pipeline (encode):
+
+ text
+ -> split on ADDED tokens (771: [INST] ... [control_N]),
+ leftmost-longest, raw text, BEFORE any BPE (_split_added)
+ -> per non-added segment: Metaspace pre-tokenizer (_metaspace)
+ ' ' -> U+2581, prepend U+2581 per prepend_scheme
+ -> BPE over the WHOLE segment (split=false => ONE "word") (_bpe)
+ chars not in vocab -> <0xNN> byte-fallback pieces
+ -> post-processor: prepend (id 1) iff add_special_tokens
+
+Decode: ids -> pieces, optionally drop special pieces, then the decoder chain
+Replace(U+2581 -> ' ') / ByteFallback / Fuse / Strip(' ', start=1).
+
+The loader VALIDATES the pipeline configuration and raises
+NotImplementedError on anything this module does not implement. A different
+tokenizer.json must fail loudly, not silently encode wrong ids.
+"""
+from __future__ import annotations
+
+import heapq
+import json
+import os
+import re
+from typing import Iterable, Optional, Sequence, Union
+
+__all__ = ["Tokenizer", "load_tokenizer"]
+
+#: U+2581 LOWER ONE EIGHTH BLOCK, the SentencePiece "space" glyph.
+_SP = "▁"
+_HEX = frozenset("0123456789abcdefABCDEF")
+#: Cap on the per-segment encode cache. With split=false a "word" is a whole
+#: text segment, so the cache only pays off for short, repeated strings
+#: (stop strings, role tags, "\n"); long segments are never cached.
+_CACHE_MAX_ENTRIES = 50_000
+_CACHE_MAX_SEGMENT_CHARS = 128
+
+_TOKENIZER_FILES = ("tokenizer.json", "tokenizer_config.json", "special_tokens_map.json")
+#: Optional passthrough: AutoTokenizer on a LlamaTokenizer-class config wants it.
+_OPTIONAL_FILES = ("tokenizer.model",)
+
+
+def _content(tok) -> Optional[str]:
+ """HF configs store a special token as either a bare string or a dict
+ {"content": ...}; None means 'unset'."""
+ if tok is None:
+ return None
+ if isinstance(tok, dict):
+ return tok.get("content")
+ return str(tok)
+
+
+def _expect(cond: bool, what: str) -> None:
+ if not cond:
+ raise NotImplementedError(
+ f"tokenizer.json uses a configuration this pure-Python tokenizer does not "
+ f"implement: {what}. Refusing to load rather than silently encode wrong ids."
+ )
+
+
+class Tokenizer:
+ """Drop-in for the subset of the HF tokenizer API this repo calls.
+
+ Build with `Tokenizer.from_pretrained(dir)` / `load_tokenizer(dir)`.
+ """
+
+ def __init__(
+ self,
+ tokenizer_json: dict,
+ tokenizer_config: Optional[dict] = None,
+ special_tokens_map: Optional[dict] = None,
+ *,
+ files: Optional[dict] = None,
+ ):
+ #: raw bytes of the source files, kept so save_pretrained is a true
+ #: copy-through that survives the source dir being pruned/rotated.
+ self._files: dict[str, bytes] = dict(files or {})
+ self._config = dict(tokenizer_config or {})
+ self._special_map = dict(special_tokens_map or {})
+ self._tokenizer_json = tokenizer_json
+ self._validate_pipeline(tokenizer_json)
+ # transformers REBUILDS the Metaspace pre-tokenizer from the config's
+ # add_prefix_space (measured on 5.15: False changes the ids, while
+ # add_bos_token/add_eos_token are ignored by the tokenizer.json path). The
+ # pure-Python pipeline follows tokenizer.json only, so refuse loudly rather
+ # than silently disagree with the legacy stack. Every v0.3 config has True.
+ _expect(
+ self._config.get("add_prefix_space") is not False,
+ "tokenizer_config.json add_prefix_space=false",
+ )
+
+ model = tokenizer_json["model"]
+ vocab: dict[str, int] = dict(model["vocab"])
+
+ # -- added tokens: override/extend the model vocab, and are matched first.
+ added = list(tokenizer_json.get("added_tokens", []))
+ self._added: dict[str, int] = {}
+ self._special_contents: set[str] = set()
+ for t in added:
+ _expect(
+ not (t.get("lstrip") or t.get("rstrip") or t.get("single_word")),
+ f"added token {t['content']!r} with lstrip/rstrip/single_word",
+ )
+ vocab[t["content"]] = int(t["id"])
+ self._added[t["content"]] = int(t["id"])
+ if t.get("special"):
+ self._special_contents.add(t["content"])
+ self._vocab = vocab
+
+ size = max(vocab.values()) + 1
+ self._id_to_token: list[Optional[str]] = [None] * size
+ for tok, i in vocab.items():
+ self._id_to_token[i] = tok
+ self._base_vocab = model["vocab"] # the BPE model's own vocab (ids for symbols)
+
+ # -- BPE tables. Merges may be [a, b] pairs (older saves) or "a b" strings.
+ self._ranks: dict[tuple[str, str], int] = {}
+ for rank, m in enumerate(model["merges"]):
+ a, b = (m[0], m[1]) if isinstance(m, (list, tuple)) else m.split(" ")
+ # Rust's BPE builder errors if the merge result is not in the vocab.
+ if (a + b) not in self._base_vocab or a not in self._base_vocab or b not in self._base_vocab:
+ raise ValueError(f"merge #{rank} {a!r}+{b!r} references a token outside the vocab")
+ # LAST duplicate wins: Rust collects merges into a HashMap, so a repeated
+ # pair keeps its final rank (measured against tokenizers 0.22). v0.3 has none.
+ self._ranks[(a, b)] = rank
+ self._byte_fallback = bool(model.get("byte_fallback"))
+ self._byte_pieces: list[Optional[str]] = [
+ (f"<0x{b:02X}>" if f"<0x{b:02X}>" in self._base_vocab else None) for b in range(256)
+ ]
+ self._fuse_unk = bool(model.get("fuse_unk"))
+ self._model_unk = model.get("unk_token")
+ self._ignore_merges = bool(model.get("ignore_merges"))
+ self._cache: dict[str, tuple[int, ...]] = {}
+
+ # -- added-token splitter. Alternation sorted longest-first == leftmost-longest.
+ if self._added:
+ alts = sorted(self._added, key=len, reverse=True)
+ self._added_re: Optional[re.Pattern] = re.compile("|".join(re.escape(a) for a in alts))
+ else:
+ self._added_re = None
+
+ # -- Metaspace
+ pt = tokenizer_json["pre_tokenizer"]
+ self._prepend_scheme = pt.get("prepend_scheme", "always" if pt.get("add_prefix_space", True) else "never")
+
+ # -- special tokens (strings), resolved config -> special_tokens_map -> defaults
+ def pick(name: str, default: Optional[str] = None) -> Optional[str]:
+ v = _content(self._config.get(f"{name}_token"))
+ if v is None:
+ v = _content(self._special_map.get(f"{name}_token"))
+ if v is None:
+ v = default
+ return v if (v is None or v in self._vocab) else None
+
+ self.bos_token = pick("bos", "")
+ self.eos_token = pick("eos", "")
+ self.unk_token = pick("unk", self._model_unk)
+ self.pad_token = pick("pad") # v0.3 ships none; callers fall back to eos
+ self._post_bos = self._post_processor_bos(tokenizer_json.get("post_processor"))
+
+ # ------------------------------------------------------------------ config
+
+ @staticmethod
+ def _validate_pipeline(tj: dict) -> None:
+ model = tj["model"]
+ _expect(model.get("type") == "BPE", f"model.type={model.get('type')!r}")
+ _expect(not model.get("dropout"), "BPE dropout")
+ _expect(not model.get("continuing_subword_prefix"), "continuing_subword_prefix")
+ _expect(not model.get("end_of_word_suffix"), "end_of_word_suffix")
+ _expect(tj.get("normalizer") is None, "a non-null normalizer")
+ pt = tj.get("pre_tokenizer") or {}
+ _expect(pt.get("type") == "Metaspace", f"pre_tokenizer {pt.get('type')!r}")
+ _expect(pt.get("replacement") == _SP, "Metaspace replacement other than U+2581")
+ _expect(pt.get("split") is False, "Metaspace split=true")
+ _expect(
+ pt.get("prepend_scheme", "always") in ("first", "always", "never"),
+ f"Metaspace prepend_scheme {pt.get('prepend_scheme')!r}",
+ )
+ dec = tj.get("decoder") or {}
+ chain = dec.get("decoders") if dec.get("type") == "Sequence" else None
+ _expect(chain is not None and len(chain) == 4, "decoder other than the 4-step Sequence")
+ r, bf, fu, st = chain
+ _expect(
+ r.get("type") == "Replace"
+ and r.get("pattern") == {"String": _SP}
+ and r.get("content") == " ",
+ "decoder[0] other than Replace(U+2581 -> ' ')",
+ )
+ _expect(bf.get("type") == "ByteFallback", "decoder[1] other than ByteFallback")
+ _expect(fu.get("type") == "Fuse", "decoder[2] other than Fuse")
+ _expect(
+ st.get("type") == "Strip"
+ and st.get("content") == " "
+ and st.get("start") == 1
+ and st.get("stop") == 0,
+ "decoder[3] other than Strip(' ', start=1, stop=0)",
+ )
+
+ @staticmethod
+ def _post_processor_bos(pp: Optional[dict]) -> Optional[str]:
+ """The single-sequence template must be exactly [, A]; returns
+ that special token's content (the BOS), or None if there is no post-processor."""
+ if pp is None:
+ return None
+ _expect(pp.get("type") == "TemplateProcessing", f"post_processor {pp.get('type')!r}")
+ single = pp.get("single", [])
+ _expect(
+ len(single) == 2 and "SpecialToken" in single[0] and "Sequence" in single[1],
+ "post_processor single template other than [SpecialToken, Sequence]",
+ )
+ return single[0]["SpecialToken"]["id"]
+
+ # --------------------------------------------------------------- id access
+
+ @property
+ def bos_token_id(self) -> Optional[int]:
+ return self._vocab.get(self.bos_token) if self.bos_token is not None else None
+
+ @property
+ def eos_token_id(self) -> Optional[int]:
+ return self._vocab.get(self.eos_token) if self.eos_token is not None else None
+
+ @property
+ def pad_token_id(self) -> Optional[int]:
+ """None on v0.3 -- callers (align.data.encoding.pad_token_id) fall back to eos."""
+ return self._vocab.get(self.pad_token) if self.pad_token is not None else None
+
+ @property
+ def unk_token_id(self) -> Optional[int]:
+ return self._vocab.get(self.unk_token) if self.unk_token is not None else None
+
+ @property
+ def vocab_size(self) -> int:
+ return len(self._base_vocab)
+
+ @property
+ def all_special_tokens(self) -> list[str]:
+ return sorted(self._special_contents, key=lambda t: self._vocab[t])
+
+ @property
+ def all_special_ids(self) -> list[int]:
+ return [self._vocab[t] for t in self.all_special_tokens]
+
+ def get_vocab(self) -> dict[str, int]:
+ return dict(self._vocab)
+
+ def get_added_vocab(self) -> dict[str, int]:
+ return dict(self._added)
+
+ def __len__(self) -> int:
+ return len(self._vocab)
+
+ def __repr__(self) -> str:
+ return f"Tokenizer(n={len(self)}, merges={len(self._ranks)}, added={len(self._added)})"
+
+ def convert_ids_to_tokens(self, ids: Union[int, Iterable[int]]):
+ """Pieces for ids; None for ids outside the vocab (HF returns None too)."""
+ if isinstance(ids, int):
+ return self._piece(ids)
+ return [self._piece(int(i)) for i in _as_list(ids)]
+
+ def convert_tokens_to_ids(self, tokens: Union[str, Iterable[str]]):
+ """Ids for pieces; unknown pieces map to the unk id (HF behaviour)."""
+ unk = self.unk_token_id
+ if isinstance(tokens, str):
+ return self._vocab.get(tokens, unk)
+ return [self._vocab.get(t, unk) for t in tokens]
+
+ def _piece(self, i: int) -> Optional[str]:
+ if i < 0:
+ raise ValueError(f"negative token id {i}: a label-mask value leaked into a token-id path")
+ return self._id_to_token[i] if i < len(self._id_to_token) else None
+
+ # ------------------------------------------------------------------ encode
+
+ def __call__(self, text: Union[str, Sequence[str]], add_special_tokens: bool = True) -> dict:
+ """{"input_ids": ...}. A list of strings gives a list of id lists.
+ Padding/truncation/tensors are deliberately NOT supported (TypeError on
+ unknown kwargs) -- callers pad their own batches."""
+ if isinstance(text, str):
+ return {"input_ids": self.encode(text, add_special_tokens=add_special_tokens)}
+ return {"input_ids": [self.encode(t, add_special_tokens=add_special_tokens) for t in text]}
+
+ def encode(self, text: str, add_special_tokens: bool = True) -> list[int]:
+ if not isinstance(text, str):
+ raise TypeError(f"encode() takes str, got {type(text).__name__}")
+ try:
+ text.encode("utf-8")
+ except UnicodeEncodeError as e: # lone surrogate: Rust strings cannot hold one
+ raise ValueError(f"text is not valid Unicode (lone surrogate at index {e.start})") from e
+ ids: list[int] = []
+ if add_special_tokens and self._post_bos is not None:
+ ids.append(self._vocab[self._post_bos])
+ for k, (is_added, seg) in enumerate(self._split_added(text)):
+ if is_added:
+ ids.append(self._added[seg])
+ continue
+ piece = self._metaspace(seg, k == 0)
+ if piece:
+ ids.extend(self._bpe(piece))
+ return ids
+
+ def _split_added(self, text: str):
+ """Yield (is_added, substring). Added tokens are matched on the RAW text,
+ leftmost-longest, and are never fed to BPE (so nothing merges across them).
+ No empty segments are produced (matches HF: "" encodes to no tokens)."""
+ if self._added_re is None or not text:
+ if text:
+ yield False, text
+ return
+ pos = 0
+ for m in self._added_re.finditer(text):
+ if m.start() > pos:
+ yield False, text[pos : m.start()]
+ yield True, m.group(0)
+ pos = m.end()
+ if pos < len(text):
+ yield False, text[pos:]
+
+ def _metaspace(self, seg: str, is_first_split: bool) -> str:
+ """Metaspace(replacement=U+2581, prepend_scheme=..., split=false).
+
+ ' ' -> U+2581 (ONLY the space char; '\\n' and '\\t' stay literal and later
+ byte-fall-back). prepend_scheme="first" prepends U+2581 only when this
+ segment is split #0 of the WHOLE input, i.e. the text does not begin with
+ an added token. Added-token splits are counted (they occupy an index)
+ but skip the pre-tokenizer, so a leading special token CONSUMES the
+ 'first' slot and nothing after it is ever prefixed:
+ "helloworld" -> ▁hello world
+ "hello" -> hello (bare 'hello', NO ▁ -- measured
+ against the legacy tokenizer)
+ A segment that already starts with U+2581 (leading space / literal U+2581)
+ gets no second prefix. 'always' prefixes every such segment; 'never' none."""
+ s = seg.replace(" ", _SP)
+ scheme = self._prepend_scheme
+ if scheme == "never" or s.startswith(_SP):
+ return s
+ if scheme == "always" or is_first_split:
+ return _SP + s
+ return s
+
+ def _bpe(self, word: str) -> tuple[int, ...]:
+ cached = self._cache.get(word)
+ if cached is not None:
+ return cached
+ vocab = self._base_vocab
+ if self._ignore_merges and word in vocab:
+ res = (vocab[word],)
+ else:
+ res = self._merge(self._initial_symbols(word))
+ if len(word) <= _CACHE_MAX_SEGMENT_CHARS:
+ if len(self._cache) >= _CACHE_MAX_ENTRIES:
+ self._cache.clear()
+ self._cache[word] = res
+ return res
+
+ def _initial_symbols(self, word: str) -> list[str]:
+ """One symbol per char if the char is a vocab piece; else its UTF-8 bytes
+ as <0xNN> pieces (byte_fallback); else (consecutive unks fuse).
+
+ Mirrors the Rust `BPE::merge_word` loop INCLUDING a quirk: an unknown char
+ only records a PENDING unk, and the pending unk is flushed solely by the
+ next in-vocab char (or end of word). A byte-fallback expansion in between
+ neither flushes nor resets it, so the unk lands AFTER those byte pieces
+ (and, with fuse_unk, unks on both sides of a byte-fallback char fuse into
+ one). Unreachable for v0.3 (all 256 <0xNN> pieces exist, so the unk path
+ never fires), but the loader accepts other byte_fallback tables."""
+ vocab = self._base_vocab
+ syms: list[str] = []
+ unk = self._model_unk
+ pending_unk = False
+ for ch in word:
+ if ch in vocab:
+ if pending_unk:
+ syms.append(unk) # type: ignore[arg-type]
+ pending_unk = False
+ syms.append(ch)
+ continue
+ if self._byte_fallback:
+ pieces = [self._byte_pieces[b] for b in ch.encode("utf-8")]
+ if None not in pieces:
+ syms.extend(pieces) # type: ignore[arg-type]
+ continue # pending unk is deliberately left untouched (Rust parity)
+ if unk is not None:
+ if pending_unk and not self._fuse_unk:
+ syms.append(unk)
+ pending_unk = True
+ if pending_unk:
+ syms.append(unk) # type: ignore[arg-type]
+ return syms
+
+ def _merge(self, syms: list[str]) -> tuple[int, ...]:
+ """Rank-ordered pair merging over a doubly linked list with a lazy-deletion
+ min-heap: O(n log n), so a multi-thousand-char segment (split=false makes
+ every text segment ONE word) is not quadratic. Tie-break is lowest rank,
+ then leftmost position -- identical to the Rust implementation."""
+ n = len(syms)
+ vocab = self._base_vocab
+ if n < 2:
+ return tuple(vocab[s] for s in syms)
+ ranks = self._ranks
+ nxt = list(range(1, n + 1))
+ nxt[-1] = -1
+ prv = list(range(-1, n - 1))
+ heap: list[tuple[int, int, str, str]] = []
+ for i in range(n - 1):
+ r = ranks.get((syms[i], syms[i + 1]))
+ if r is not None:
+ heap.append((r, i, syms[i], syms[i + 1]))
+ heapq.heapify(heap)
+ pop, push = heapq.heappop, heapq.heappush
+ while heap:
+ _, i, left, right = pop(heap)
+ if syms[i] != left: # stale: left side was merged or absorbed (None)
+ continue
+ j = nxt[i]
+ if j == -1 or syms[j] != right: # stale: right neighbour changed
+ continue
+ new = left + right
+ syms[i] = new
+ syms[j] = None # type: ignore[call-overload]
+ nj = nxt[j]
+ nxt[i] = nj
+ if nj != -1:
+ prv[nj] = i
+ p = prv[i]
+ if p != -1:
+ r = ranks.get((syms[p], new))
+ if r is not None:
+ push(heap, (r, p, syms[p], new))
+ if nj != -1:
+ r = ranks.get((new, syms[nj]))
+ if r is not None:
+ push(heap, (r, i, new, syms[nj]))
+ out = []
+ i = 0
+ while i != -1:
+ out.append(vocab[syms[i]])
+ i = nxt[i]
+ return tuple(out)
+
+ # ------------------------------------------------------------------ decode
+
+ def decode(self, ids, skip_special_tokens: bool = False) -> str:
+ """ids -> text. Default skip_special_tokens=False matches HF (callers that
+ want specials dropped, e.g. the inference engine, pass True explicitly).
+
+ Ids outside the vocab are silently dropped (HF/Rust behaviour); negative
+ ids raise (they are masks that leaked, never tokens).
+
+ Decoder chain, applied to the list of pieces:
+ Replace(U+2581 -> ' ') per piece;
+ ByteFallback: each maximal run of <0xNN> pieces is UTF-8 decoded as ONE
+ unit; an INVALID run becomes one U+FFFD **per byte** (Rust semantics,
+ not Python errors='replace', which collapses ill-formed subparts);
+ Fuse: concatenate;
+ Strip(' ', start=1): drop at most ONE leading space of the fused text.
+ The Strip is why decode([id_of('▁hello')]) == 'hello', not ' hello': a
+ chunk decoded in isolation loses its leading space. Never decode token
+ deltas and concatenate -- decode the whole sequence (the engine does).
+ """
+ pieces: list[str] = []
+ special = self._special_contents
+ for i in _as_list(ids):
+ t = self._piece(int(i))
+ if t is None:
+ continue
+ if skip_special_tokens and t in special:
+ continue
+ pieces.append(t)
+ return self._decode_pieces(pieces)
+
+ @staticmethod
+ def _decode_pieces(pieces: list[str]) -> str:
+ out: list[str] = []
+ run = bytearray()
+
+ def flush() -> None:
+ if run:
+ try:
+ out.append(run.decode("utf-8"))
+ except UnicodeDecodeError:
+ out.append("�" * len(run))
+ run.clear()
+
+ for p in pieces:
+ p = p.replace(_SP, " ")
+ if len(p) == 6 and p.startswith("<0x") and p.endswith(">") and p[3] in _HEX and p[4] in _HEX:
+ run.append(int(p[3:5], 16))
+ else:
+ flush()
+ out.append(p)
+ flush()
+ text = "".join(out) # Fuse
+ return text[1:] if text.startswith(" ") else text # Strip(' ', start=1)
+
+ # -------------------------------------------------------------------- save
+
+ def save_pretrained(self, save_directory: str) -> tuple[str, ...]:
+ """Write tokenizer.json, tokenizer_config.json, special_tokens_map.json
+ (+ tokenizer.model if the source had one). Copy-through of the source
+ bytes when loaded from a dir; synthesized (HF-shaped) otherwise. Each file
+ is written to a sibling temp name then os.replace()d, so a kill mid-save
+ leaves the old file or the new file, never a torn one."""
+ os.makedirs(save_directory, exist_ok=True)
+ files = dict(self._files)
+ if "tokenizer.json" not in files:
+ files["tokenizer.json"] = json.dumps(self._tokenizer_json, ensure_ascii=False).encode("utf-8")
+ if "tokenizer_config.json" not in files:
+ files["tokenizer_config.json"] = json.dumps(self._synth_config(), indent=2).encode("utf-8")
+ if "special_tokens_map.json" not in files:
+ files["special_tokens_map.json"] = json.dumps(self._synth_special_map(), indent=2).encode("utf-8")
+ written = []
+ for name, blob in files.items():
+ dst = os.path.join(save_directory, name)
+ tmp = dst + f".tmp.{os.getpid()}"
+ with open(tmp, "wb") as f:
+ f.write(blob)
+ f.flush()
+ os.fsync(f.fileno())
+ os.replace(tmp, dst)
+ written.append(dst)
+ return tuple(written)
+
+ def _synth_config(self) -> dict:
+ return {
+ "add_bos_token": self._post_bos is not None,
+ "add_eos_token": False,
+ "bos_token": self.bos_token,
+ "eos_token": self.eos_token,
+ "unk_token": self.unk_token,
+ "pad_token": self.pad_token,
+ "clean_up_tokenization_spaces": False,
+ "legacy": False,
+ "model_max_length": int(1e30),
+ "tokenizer_class": "LlamaTokenizer",
+ }
+
+ def _synth_special_map(self) -> dict:
+ d = {}
+ for name in ("bos", "eos", "unk", "pad"):
+ tok = getattr(self, f"{name}_token")
+ if tok is not None:
+ d[f"{name}_token"] = {
+ "content": tok,
+ "lstrip": False,
+ "normalized": False,
+ "rstrip": False,
+ "single_word": False,
+ }
+ return d
+
+ # -------------------------------------------------------------------- load
+
+ @classmethod
+ def from_pretrained(cls, path: Union[str, os.PathLike]) -> "Tokenizer":
+ """Load from a directory holding tokenizer.json (a checkpoint dir, or an HF
+ snapshot dir), a path to a tokenizer.json, or an HF cache model dir
+ (`models--org--name`, newest snapshot with a tokenizer.json). LOCAL ONLY:
+ never touches the network, never downloads."""
+ d = _resolve_dir(os.fspath(path))
+ files: dict[str, bytes] = {}
+ for name in _TOKENIZER_FILES + _OPTIONAL_FILES:
+ p = os.path.join(d, name)
+ if os.path.isfile(p):
+ with open(p, "rb") as f:
+ files[name] = f.read()
+ tj = json.loads(files["tokenizer.json"])
+ cfg = json.loads(files["tokenizer_config.json"]) if "tokenizer_config.json" in files else None
+ smap = json.loads(files["special_tokens_map.json"]) if "special_tokens_map.json" in files else None
+ return cls(tj, cfg, smap, files=files)
+
+
+def _as_list(ids) -> list:
+ """Accept list/tuple/iterable, numpy arrays and torch tensors (via .tolist())."""
+ if hasattr(ids, "tolist"):
+ ids = ids.tolist()
+ if isinstance(ids, int):
+ return [ids]
+ return list(ids)
+
+
+def _resolve_dir(path: str) -> str:
+ if os.path.isfile(path) and os.path.basename(path) == "tokenizer.json":
+ return os.path.dirname(os.path.abspath(path))
+ if os.path.isfile(os.path.join(path, "tokenizer.json")):
+ return path
+ snaps = os.path.join(path, "snapshots")
+ if os.path.isdir(snaps):
+ cands = [
+ os.path.join(snaps, s)
+ for s in os.listdir(snaps)
+ if os.path.isfile(os.path.join(snaps, s, "tokenizer.json"))
+ ]
+ if cands:
+ return max(cands, key=os.path.getmtime)
+ raise FileNotFoundError(
+ f"no tokenizer.json at {path!r} (also tried {snaps!r}). align.tokenizer is "
+ "local-only by design: it never downloads. Pass a checkpoint dir or an HF "
+ "snapshot dir containing tokenizer.json."
+ )
+
+
+def load_tokenizer(path: Union[str, os.PathLike]) -> Tokenizer:
+ """Load a Tokenizer from a local dir/file. See Tokenizer.from_pretrained."""
+ return Tokenizer.from_pretrained(path)
diff --git a/code/align/train/__init__.py b/code/align/train/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..d02b4166304de506f2f266bb80025f31daab3373
--- /dev/null
+++ b/code/align/train/__init__.py
@@ -0,0 +1,16 @@
+"""The distributed training core (section-07).
+
+ * `offload_adamw.py` -- optimizer-state-only CPU offload; params and grads
+ stay on the training device, moments and the fp32 master live in host RAM.
+ * `fsdp.py` -- FSDP2 wrapping + mixed-precision policy, called from
+ the launch scripts, never from the Trainer.
+ * `trainer.py` -- the one Trainer for SFT and DPO: micro-step loop,
+ seeded lossless resume, optimizer-step scheduler, checkpoint cadence.
+ * `probes.py` -- trunk-movement probe, run-health tripwires, the
+ 20-step throughput probe.
+ * `sft_loss.py` -- adapter from a collated batch to section-05's
+ chunked fused CE.
+
+Every module here imports torch at module scope, so nothing above the resource
+guards may import this package.
+"""
diff --git a/code/align/train/adamw8bit.py b/code/align/train/adamw8bit.py
new file mode 100644
index 0000000000000000000000000000000000000000..3537b404be950dc5b89d6e005f8fcde68b6bfc48
--- /dev/null
+++ b/code/align/train/adamw8bit.py
@@ -0,0 +1,630 @@
+"""Block-wise 8-bit AdamW in pure torch: the optimizer that lets full-FT 7B launch.
+
+WHY THIS EXISTS (the arithmetic, from .tmp/handoff/review_trainer.md): every
+shipped full-FT config names `optimizer.name = "adamw8bit"`, and the two
+alternatives are both unlaunchable on this box at 7.248B params / 4 ranks:
+
+ * fp32 AdamW on the device needs 14.5GB/rank of moments on top of a 9.6GB
+ activation peak against a 23GB A10G.
+ * `OffloadAdamW` pins the WHOLE rank master (n/world*4 B) while its staging
+ cap is two layer-shards (n/layers/world*4*2 B): required/cap = layers /
+ (world * shards) = 4, so it refuses to construct. Past that, its host RSS
+ projects to ~105GB against ~85GB MemAvailable, and the checkpoint clones
+ master + both moments (12 B/param).
+
+This optimizer keeps both Adam moments as uint8 codes ON THE DEVICE and has no
+master copy at all.
+
+MEMORY BUDGET, per rank-local parameter element (block_size B = 2048):
+
+ exp_avg 1 byte code + 4/B byte absmax = 1.00195 B
+ exp_avg_sq 1 byte code + 4/B byte absmax = 1.00195 B
+ ------------------------------------------------------------
+ total 2 + 8/B = 2.0039 B/param (fp32 AdamW: 8; OffloadAdamW: 12 host)
+
+ 7.248e9 params / 4 ranks = 1.812e9 elements/rank -> 3.63 GB (3.38 GiB)/rank
+ 14.5 GB across the job, all on the GPU, ZERO host RSS (which is what
+ `guard.projected_full_ft_rss_gb` already assumes for non-offload).
+
+ Tensors below `min_8bit_size` (4096) elements -- norm scales, biases -- keep
+ fp32 moments (8 B/param); there are ~65 of them in a 32-layer Mistral and
+ they total well under a megabyte. The step additionally holds one CHUNK of
+ fp32 transients (default 2**21 elements, < 100MB), never a whole tensor.
+ `projected_state_bytes` computes the exact figure from element counts and is
+ what the launch banner prints.
+
+THE STEP, per chunk of whole blocks: dequantize both moments -> fp32 AdamW
+update (decoupled weight decay, bias correction; the same op order as
+`torch.optim.AdamW`'s single-tensor path, so the fp32 fallback is BIT-EXACT
+against it) -> requantize -> store. The update uses the freshly computed
+fp32 moments, not the requantized ones: the quantization error only enters
+through the NEXT step's decayed moment, which is the same order bitsandbytes'
+fused kernel uses (quantization is a storage format, not part of the rule).
+
+KNOWN LIMITATION, measured and bounded (not a bug to chase): SECOND-MOMENT
+UNDERFLOW. v is a square, so its dynamic range is the SQUARE of the
+gradient's. The smallest nonzero unsigned code is 3.25e-7 of the block absmax,
+so an element whose gradient is < ~5e-4 of its block's largest one stores v = 0.
+The update never divides by that stored zero (it uses the fresh fp32 moment,
+see above), but the decayed history of v is lost for those elements: right
+after a cold start a tiny-gradient element sees only (1-beta2)*g^2 instead of
+the running average, so its step is up to lr / sqrt(1 - beta2) (4.5 lr at
+beta2 = 0.95) instead of ~lr. Measured on a Gaussian-gradient linear layer: a
+handful of elements (~0.1%) are off by ~1-2 lr on the second step and the mean
+error over all elements is ~1% of the travel -- and that is a COLD-START /
+CHANGING-GRADIENT picture. It does NOT relax for an element whose gradient
+stays below the threshold: v is re-floored to 0 every step, so the over-step is
+PERMANENT, and it GROWS with beta2 (adversarial review, constant g = 1e-4 of the
+block max, 300 steps, travel vs torch.optim.AdamW: 3.9x at beta2 = 0.95, 9.9x at
+0.999 and still rising toward 1/sqrt(1-beta2) = 31.6x; g = 1e-3 of the max is
+1.07x, g = 1e-2 is 1.00x). Clamping every positive v up to the smallest nonzero
+code would turn that into a bounded UNDER-step (0.16x, independent of beta2) at
+the price of freezing those elements; that is a different optimizer, so it is
+the owner's call (`.tmp/review_adamw8bit/probe3.py`), not made here.
+bitsandbytes has the same property
+(same map, same nearest-code rounding); the alternative of storing sqrt(v)
+doubles the relative error of the denominator in exchange for the range, which
+is a different optimizer than the one specified, so it is not done here.
+`tests/test_adamw8bit.py::test_second_moment_underflow_...` pins the bound.
+
+NO MASTER COPY, AND WHAT THAT COSTS. The parameters are bf16 and the update is
+tiny: at lr 1e-5 an update is ~1e-5 against a bf16 weight of ~2e-2 whose
+spacing is 1.2e-4, so round-to-nearest drops almost every update -- a run
+whose loss curve is flat and whose weights "never move" (the same
+silent-no-train class as the ZeRO-3 + Adafactor incident). The fp32 master that
+`OffloadAdamW` pays 4 B/param for exists to prevent exactly this. Here the
+write-back to a bf16 parameter is STOCHASTIC ROUNDING by default: the fp32
+result is rounded up or down with probability proportional to proximity, so the
+parameter moves by the right amount IN EXPECTATION. It is deterministic given
+the seed, and its generator state is checkpointed, so resume stays bit-exact.
+Pass `stochastic_rounding=False` for round-to-nearest (what bnb does).
+
+FSDP2 / DTENSOR (mirrors `OffloadAdamW`, same defect history): under
+`fully_shard` the parameters are DTensors. AdamW is elementwise, so stepping
+`param.to_local()` against the grad's local shard is EXACT; every access goes
+through `_localize` (a storage-sharing view, so in-place updates reach the
+DTensor). Numels are THIS RANK'S, never the global count.
+
+GLOBAL GRAD NORM (mirrors `OffloadAdamW._grad_norm`): the sum of squares is
+all-reduced across ranks before the root, so `last_grad_norm` is the true
+global norm, identical on every rank, and is recorded BEFORE clipping --
+the tripwires that read it must see an explosion, not its clipped image.
+The same all-reduced value is the finiteness gate: a non-finite gradient on
+ANY rank makes the global sum non-finite on EVERY rank, so all ranks skip
+together with one collective instead of two. (In float64 the sum of squares
+of finite fp32 values cannot overflow, so non-finite sum <=> non-finite grad.)
+
+RESUME: `state_dict()` is a snapshot of the uint8 codes, the absmax, the step
+counts, the hyper-parameters and the rounding RNG, all cloned to CPU. Reloading
+copies them back byte for byte, so a resumed run is bit-identical to an
+uninterrupted one. The state is SHARD-shaped: it records `world_size` and
+every tensor's element count and REFUSES a mismatched resume rather than
+aligning rank r's moments to a different slice of the parameter.
+"""
+
+from __future__ import annotations
+
+import logging
+import math
+from typing import Iterable, Optional, Sequence
+
+import torch
+
+from ..utils.dist import rank, world_size
+from .offload_adamw import OffloadAdamW
+from .quant8 import (
+ codebook,
+ dequantize_rows,
+ quantize_rows,
+ zero_code,
+)
+
+logger = logging.getLogger(__name__)
+
+STATE_FORMAT = "adamw8bit-blockwise-v1"
+DEFAULT_BLOCK_SIZE = 2048
+#: bnb's `min_8bit_size`: below this a tensor's moments stay fp32.
+DEFAULT_MIN_8BIT_SIZE = 4096
+#: Elements processed per fused dequant/update/requant pass (rounded down to
+#: whole blocks). Bounds the fp32 transients; see the module docstring.
+DEFAULT_CHUNK_ELEMS = 2**21
+_FP32_MAX = float(torch.finfo(torch.float32).max)
+
+
+def adamw_update_(
+ p: torch.Tensor,
+ g: torch.Tensor,
+ m: torch.Tensor,
+ v: torch.Tensor,
+ *,
+ lr: float,
+ beta1: float,
+ beta2: float,
+ eps: float,
+ weight_decay: float,
+ step: int,
+) -> None:
+ """One AdamW update on fp32 tensors, IN PLACE on `p`, `m`, `v`.
+
+ The op sequence is `torch.optim.AdamW._single_tensor_adamw` verbatim
+ (decoupled decay first, `lerp_` for the first moment, `addcmul_` for the
+ second, bias-correction folded into step_size and the denominator). That
+ is what makes "fp32 fallback == torch AdamW" a property of the
+ construction: a reordering that is algebraically identical is NOT
+ bit-identical in fp32, and a test that tolerates 1e-6 would hide the day
+ someone swaps `lerp_` for a hand-written EMA.
+ """
+ if weight_decay != 0:
+ p.mul_(1 - lr * weight_decay)
+ m.lerp_(g, 1 - beta1)
+ v.mul_(beta2).addcmul_(g, g, value=1 - beta2)
+ bias_correction1 = 1 - beta1**step
+ bias_correction2 = 1 - beta2**step
+ step_size = lr / bias_correction1
+ bias_correction2_sqrt = bias_correction2**0.5
+ denom = (v.sqrt() / bias_correction2_sqrt).add_(eps)
+ p.addcdiv_(m, denom, value=-step_size)
+
+
+def stochastic_round_to_bf16(x: torch.Tensor, generator: Optional[torch.Generator]) -> torch.Tensor:
+ """fp32 -> bf16 with stochastic rounding (unbiased: E[out] == x).
+
+ bf16 is exactly the top 16 bits of an fp32. Adding a uniform random
+ integer in [0, 2**16) to the raw bit pattern and truncating the low 16 bits
+ rounds UP with probability (low16 / 2**16) -- i.e. in proportion to how
+ close x is to the next representable value -- and an already-representable
+ x (low16 == 0) is returned unchanged. The add acts on magnitude for both
+ signs, because the sign is the top bit and the carry never reaches it for
+ finite inputs. Truncation back to bf16 is then exact (`.to(bfloat16)` of a
+ value whose low 16 bits are zero).
+ """
+ x = x.contiguous()
+ noise = torch.randint(
+ 0, 1 << 16, x.shape, dtype=torch.int32, device=x.device, generator=generator
+ )
+ bits = (x.view(torch.int32) + noise) & -65536
+ return bits.view(torch.float32).to(torch.bfloat16)
+
+
+class BlockwiseAdamW8bit:
+ """AdamW with block-wise 8-bit moments, stepping each rank's LOCAL shard.
+
+ `step()` returns False when the grad-skip gate refused the step (same
+ contract as `OffloadAdamW`), True when it applied.
+
+ Args:
+ params: the parameters (plain tensors or FSDP2 DTensors). Floating
+ point, contiguous locally, no duplicates.
+ lr, betas, eps, weight_decay: AdamW; decay is DECOUPLED (applied to
+ the weight, not folded into the gradient).
+ max_grad_norm: if set, gradients are scaled by
+ `min(1, max_grad_norm / (norm + 1e-6))` -- `clip_grad_norm_`'s rule
+ -- using the GLOBAL norm. `last_grad_norm` is the pre-clip norm.
+ The scale is applied inside the fused update; `p.grad` itself is
+ not rewritten (it is about to be zeroed, and rewriting a bf16 grad
+ in place would only add rounding).
+ block_size: elements per absmax block (default 2048).
+ min_8bit_size: tensors with fewer local elements keep fp32 moments.
+ chunk_elems: fp32-transient bound for the fused update.
+ stochastic_rounding: stochastic (default) vs nearest rounding when the
+ fp32 result is written back to a bf16 parameter. See module doc.
+ seed: base seed of the rounding RNG (offset by rank).
+ """
+
+ def __init__(
+ self,
+ params: Iterable[torch.Tensor],
+ lr: float,
+ betas: tuple[float, float] = (0.9, 0.95),
+ eps: float = 1e-8,
+ weight_decay: float = 0.0,
+ max_grad_norm: Optional[float] = None,
+ block_size: int = DEFAULT_BLOCK_SIZE,
+ min_8bit_size: int = DEFAULT_MIN_8BIT_SIZE,
+ chunk_elems: int = DEFAULT_CHUNK_ELEMS,
+ stochastic_rounding: bool = True,
+ seed: int = 0,
+ ) -> None:
+ self.params: list[torch.Tensor] = list(params)
+ if not self.params:
+ raise ValueError("BlockwiseAdamW8bit got an empty parameter list")
+ if len({id(p) for p in self.params}) != len(self.params):
+ raise ValueError(
+ "BlockwiseAdamW8bit got the same parameter object twice (tied weights?): "
+ "it would be stepped twice per step with one shared state slot"
+ )
+ if not (isinstance(block_size, int) and block_size >= 1):
+ raise ValueError(f"block_size must be a positive int, got {block_size!r}")
+ if min_8bit_size < 0 or chunk_elems < 1:
+ raise ValueError("min_8bit_size must be >= 0 and chunk_elems >= 1")
+ if lr < 0 or eps <= 0 or weight_decay < 0:
+ raise ValueError(f"invalid AdamW hyper-parameters lr={lr} eps={eps} wd={weight_decay}")
+ if not (0.0 <= betas[0] < 1.0 and 0.0 <= betas[1] < 1.0):
+ raise ValueError(f"betas must be in [0, 1), got {betas}")
+ if max_grad_norm is not None and not max_grad_norm > 0:
+ raise ValueError(f"max_grad_norm must be positive or None, got {max_grad_norm}")
+
+ self.block_size = block_size
+ self.min_8bit_size = min_8bit_size
+ self.chunk_blocks = max(1, chunk_elems // block_size)
+ self.stochastic_rounding = bool(stochastic_rounding)
+ self.seed = int(seed)
+ self.banner_tag = "adamw8bit"
+ self.skipped_steps = 0
+ self.last_grad_norm = float("nan")
+ #: One group, like OffloadAdamW: `Trainer._apply_scheduled_lr` writes
+ #: the scheduled lr into every group, and `step` reads it from here.
+ self.param_groups = [
+ {
+ "params": self.params,
+ "lr": float(lr),
+ "betas": (float(betas[0]), float(betas[1])),
+ "eps": float(eps),
+ "weight_decay": float(weight_decay),
+ "max_grad_norm": None if max_grad_norm is None else float(max_grad_norm),
+ }
+ ]
+ self._generators: dict[str, torch.Generator] = {}
+
+ self._state: list[dict] = []
+ for i, p in enumerate(self.params):
+ if not p.dtype.is_floating_point:
+ raise ValueError(f"param {i} has non-floating dtype {p.dtype}")
+ local = self._localize(p)
+ if not local.is_contiguous():
+ raise ValueError(
+ f"param {i} (local shape {tuple(local.shape)}) is not contiguous: the "
+ "blockwise state is laid out over the flat local shard. FSDP2 dim-0 "
+ "shards are contiguous; a transposed/strided view is not supported."
+ )
+ self._state.append(self._init_slot(local))
+
+ logger.info(
+ "BlockwiseAdamW8bit: %d tensors, %.2fM local elements, state %.3fGiB "
+ "(block %d, min_8bit_size %d, stochastic_rounding=%s)",
+ len(self.params),
+ sum(s["numel"] for s in self._state) / 1e6,
+ self.state_bytes() / 2**30,
+ block_size,
+ min_8bit_size,
+ self.stochastic_rounding,
+ )
+
+ # -- layout --------------------------------------------------------------
+
+ @staticmethod
+ def _localize(t):
+ """This rank's local shard of an FSDP2 DTensor (a storage-sharing
+ view), or `t` itself. Identical contract to OffloadAdamW._localize."""
+ return t.to_local() if hasattr(t, "to_local") else t
+
+ def _is_quantized(self, numel: int) -> bool:
+ return numel >= self.min_8bit_size and numel > 0
+
+ def _init_slot(self, local: torch.Tensor) -> dict:
+ n = local.numel()
+ dev = local.device
+ slot: dict = {"numel": n, "step": 0, "quantized": self._is_quantized(n)}
+ if slot["quantized"]:
+ nb = -(-n // self.block_size)
+ # Codes are allocated in WHOLE blocks (the last block is padded with
+ # the zero code) so every chunk is a clean [blocks, block_size] row
+ # matrix; the pad costs < one block per tensor.
+ slot["m_codes"] = torch.full(
+ (nb, self.block_size), zero_code(True), dtype=torch.uint8, device=dev
+ )
+ slot["v_codes"] = torch.full(
+ (nb, self.block_size), zero_code(False), dtype=torch.uint8, device=dev
+ )
+ slot["m_absmax"] = torch.zeros(nb, dtype=torch.float32, device=dev)
+ slot["v_absmax"] = torch.zeros(nb, dtype=torch.float32, device=dev)
+ else:
+ slot["m"] = torch.zeros(n, dtype=torch.float32, device=dev)
+ slot["v"] = torch.zeros(n, dtype=torch.float32, device=dev)
+ return slot
+
+ @staticmethod
+ def projected_state_bytes(
+ local_numels: Sequence[int],
+ block_size: int = DEFAULT_BLOCK_SIZE,
+ min_8bit_size: int = DEFAULT_MIN_8BIT_SIZE,
+ ) -> int:
+ """Exact optimizer-state bytes for these RANK-LOCAL element counts.
+
+ Large tensors: `2*n + 8*n/block_size` (two uint8 code arrays plus two
+ fp32 absmax arrays), with `n` rounded up to whole blocks because that
+ is how the codes are allocated. Small tensors: 8*n (two fp32 moments).
+ Computable BEFORE construction, so a launch banner or preflight can
+ state the number without allocating it.
+ """
+ total = 0
+ for n in local_numels:
+ if n >= min_8bit_size and n > 0:
+ nb = -(-n // block_size)
+ total += 2 * nb * block_size + 8 * nb
+ else:
+ total += 8 * n
+ return int(total)
+
+ def state_bytes(self) -> int:
+ """Bytes actually held by this optimizer's state tensors on this rank."""
+ total = 0
+ for s in self._state:
+ for k, v in s.items():
+ if isinstance(v, torch.Tensor):
+ total += v.numel() * v.element_size()
+ return total
+
+ def describe_placement(self) -> dict:
+ """Where the state lives (device/dtype/bytes), for the banner and the
+ placement test -- a 'the state is 8-bit' claim nobody checks is how it
+ quietly stops being true."""
+ ents = []
+ for s in self._state:
+ for k, v in s.items():
+ if isinstance(v, torch.Tensor):
+ ents.append(
+ {"key": k, "device": v.device.type, "dtype": str(v.dtype),
+ "bytes": v.numel() * v.element_size()}
+ )
+ return {"state": ents, "state_bytes": self.state_bytes()}
+
+ # -- rounding RNG --------------------------------------------------------
+
+ def _generator(self, device: torch.device) -> torch.Generator:
+ key = device.type
+ gen = self._generators.get(key)
+ if gen is None:
+ gen = torch.Generator(device=device)
+ gen.manual_seed(self.seed + rank())
+ self._generators[key] = gen
+ return gen
+
+ def _write_param(self, dst: torch.Tensor, src32: torch.Tensor) -> None:
+ """Write the fp32 result into the (possibly bf16) parameter chunk."""
+ if dst.dtype == torch.float32:
+ if dst.data_ptr() != src32.data_ptr():
+ dst.copy_(src32)
+ elif dst.dtype == torch.bfloat16 and self.stochastic_rounding:
+ dst.copy_(stochastic_round_to_bf16(src32, self._generator(dst.device)))
+ else:
+ dst.copy_(src32)
+
+ # -- the step ------------------------------------------------------------
+
+ @torch.no_grad()
+ def step(self) -> bool:
+ """One optimizer step, or a refusal.
+
+ Order is load-bearing: the (global) norm / finiteness verdict is reached
+ BEFORE any moment is touched, so a skipped step leaves the state
+ byte-identical. A discovered-too-late NaN would already have poisoned
+ the second moment, and every later step is scaled by it.
+ """
+ grads = [None if p.grad is None else self._localize(p.grad) for p in self.params]
+ present = [g for g in grads if g is not None]
+ if not present:
+ raise RuntimeError(
+ "BlockwiseAdamW8bit.step() called with no gradients on any parameter. "
+ "A step that silently does nothing is indistinguishable from a run "
+ "that trained."
+ )
+ for i, (g, s) in enumerate(zip(grads, self._state)):
+ if g is not None and g.numel() != s["numel"]:
+ raise ValueError(
+ f"param {i}: local grad has {g.numel()} elements but the state was "
+ f"built for {s['numel']} (grad of a different shard layout?)"
+ )
+
+ norm = OffloadAdamW._grad_norm(present)
+ if not math.isfinite(norm):
+ self.skipped_steps += 1
+ # NaN, not 0.0: a 0.0 in the log reads as 'perfectly converged'.
+ self.last_grad_norm = float("nan")
+ logger.warning(
+ "grad-skip gate: non-finite gradient, skipping optimizer step "
+ "(%d skipped so far). Moments and step counts left untouched.",
+ self.skipped_steps,
+ )
+ return False
+ self.last_grad_norm = norm # PRE-clip, on purpose
+
+ group = self.param_groups[0]
+ coef = 1.0
+ max_norm = group["max_grad_norm"]
+ if max_norm is not None:
+ coef = min(1.0, max_norm / (norm + 1e-6))
+
+ for p, g, st in zip(self.params, grads, self._state):
+ if g is None or st["numel"] == 0:
+ continue
+ st["step"] += 1
+ local = self._localize(p).detach().view(-1)
+ gflat = g.detach().reshape(-1)
+ if st["quantized"]:
+ self._step_quantized(local, gflat, st, group, coef)
+ else:
+ self._step_fp32(local, gflat, st, group, coef)
+ return True
+
+ def _hyper(self, group: dict, st: dict) -> dict:
+ return dict(
+ lr=group["lr"],
+ beta1=group["betas"][0],
+ beta2=group["betas"][1],
+ eps=group["eps"],
+ weight_decay=group["weight_decay"],
+ step=st["step"],
+ )
+
+ def _step_fp32(self, pflat, gflat, st, group, coef) -> None:
+ g32 = gflat.to(torch.float32)
+ if coef < 1.0:
+ g32 = g32 * coef # out of place: g32 may alias the live .grad
+ p32 = pflat.to(torch.float32) # aliases pflat when it is already fp32
+ adamw_update_(p32, g32, st["m"], st["v"], **self._hyper(group, st))
+ self._write_param(pflat, p32)
+
+ def _step_quantized(self, pflat, gflat, st, group, coef) -> None:
+ B = self.block_size
+ n = st["numel"]
+ n_blocks = st["m_codes"].shape[0]
+ cb_m = codebook(True, pflat.device)
+ cb_v = codebook(False, pflat.device)
+ hyper = self._hyper(group, st)
+ for b0 in range(0, n_blocks, self.chunk_blocks):
+ b1 = min(n_blocks, b0 + self.chunk_blocks)
+ lo, hi = b0 * B, min(n, b1 * B)
+ ln = hi - lo
+ g32 = gflat[lo:hi].to(torch.float32)
+ if coef < 1.0:
+ g32 = g32 * coef
+ m_full = dequantize_rows(st["m_codes"][b0:b1], st["m_absmax"][b0:b1], cb_m).view(-1)
+ v_full = dequantize_rows(st["v_codes"][b0:b1], st["v_absmax"][b0:b1], cb_v).view(-1)
+ pchunk = pflat[lo:hi]
+ p32 = pchunk.to(torch.float32)
+ # [:ln] are views of the block-padded buffers: the pad tail is never
+ # touched by the update (it stays exactly 0), then the whole padded
+ # rows are requantized.
+ adamw_update_(p32, g32, m_full[:ln], v_full[:ln], **hyper)
+ self._write_param(pchunk, p32)
+ # THE V-OVERFLOW POISON (adversarial review, 2026-10-12). The finiteness
+ # gate in `step` tests the NORM, which is accumulated in float64 and so
+ # stays finite for a gradient the fp32 SQUARE cannot hold: one element of
+ # 1e21 gives v = (1-beta2)*g^2 = inf. torch.optim.AdamW survives that
+ # (m / (inf + eps) == 0), but here inf becomes the block's absmax, the
+ # requantize divides inf/inf = NaN, the NaN lands in the uint8 codes,
+ # and the block's weights go NaN on the NEXT step, with the gate seeing
+ # only finite gradients. Clamping v to the largest finite fp32 keeps the
+ # update identical (m / 1.8e19 is still 0 for any sane m) and the stored
+ # state finite. Only v can overflow: m is a convex mix of finite values.
+ v_full.clamp_(max=_FP32_MAX)
+ codes, absmax = quantize_rows(m_full.view(-1, B), cb_m)
+ st["m_codes"][b0:b1] = codes
+ st["m_absmax"][b0:b1] = absmax
+ codes, absmax = quantize_rows(v_full.view(-1, B), cb_v)
+ st["v_codes"][b0:b1] = codes
+ st["v_absmax"][b0:b1] = absmax
+
+ def zero_grad(self, set_to_none: bool = True) -> None:
+ for p in self.params:
+ if set_to_none:
+ p.grad = None
+ elif p.grad is not None:
+ p.grad.zero_()
+
+ # -- resume --------------------------------------------------------------
+
+ def state_dict(self) -> dict:
+ """A DETACHED CPU COPY of everything the next step depends on.
+
+ `.to("cpu", copy=True)`, not `.to("cpu")`: on a CPU tensor the latter
+ returns the SAME object, and a "snapshot" that aliases the live codes
+ is mutated by the very next step -- the shared-state aliasing incident
+ OffloadAdamW documents, here with uint8 codes that no comparison would
+ flag as drifting. Everything is copied so the dict is safe to pickle
+ straight into the checkpoint gather.
+ """
+ slots = []
+ for s in self._state:
+ slots.append(
+ {
+ k: (v.detach().to("cpu", copy=True) if isinstance(v, torch.Tensor) else v)
+ for k, v in s.items()
+ }
+ )
+ group = self.param_groups[0]
+ return {
+ "format": STATE_FORMAT,
+ "world_size": world_size(),
+ "block_size": self.block_size,
+ "min_8bit_size": self.min_8bit_size,
+ "stochastic_rounding": self.stochastic_rounding,
+ "param_group": {k: v for k, v in group.items() if k != "params"},
+ "state": slots,
+ "skipped_steps": self.skipped_steps,
+ "last_grad_norm": self.last_grad_norm,
+ "sr_rng": {k: g.get_state().clone() for k, g in self._generators.items()},
+ }
+
+ def load_state_dict(self, sd: dict) -> None:
+ """Restore the state byte for byte. Validates EVERYTHING before writing
+ anything, so a refused load leaves the optimizer exactly as it was."""
+ if sd.get("format") != STATE_FORMAT:
+ raise ValueError(
+ f"optimizer state format {sd.get('format')!r} is not {STATE_FORMAT!r}; "
+ "refusing to interpret it (a 32-bit or bnb state is not loadable here)"
+ )
+ saved_ws, now_ws = sd.get("world_size"), world_size()
+ if saved_ws is not None and saved_ws != now_ws:
+ raise ValueError(
+ f"this optimizer state was saved at world_size={saved_ws} but the current "
+ f"run has world_size={now_ws}. The 8-bit moments are SHARD-shaped: loading "
+ "them under a different world size would align each rank's state to a "
+ "different slice of the parameter, and training would continue plausibly "
+ "on corrupted state. Resume at the original world size, or start fresh."
+ )
+ if sd["block_size"] != self.block_size or sd["min_8bit_size"] != self.min_8bit_size:
+ raise ValueError(
+ f"state was built with block_size={sd['block_size']}, min_8bit_size="
+ f"{sd['min_8bit_size']} but this optimizer has {self.block_size}, "
+ f"{self.min_8bit_size}: the absmax blocks would not line up"
+ )
+ saved = sd["state"]
+ if len(saved) != len(self._state):
+ raise ValueError(
+ f"checkpoint holds {len(saved)} tensors but this optimizer has {len(self._state)}"
+ )
+ for i, (cur, new) in enumerate(zip(self._state, saved)):
+ if cur["numel"] != new["numel"] or cur["quantized"] != new["quantized"]:
+ raise ValueError(
+ f"tensor {i}: checkpoint has numel={new['numel']} quantized="
+ f"{new['quantized']}, this optimizer has numel={cur['numel']} quantized="
+ f"{cur['quantized']} (a different shard layout)"
+ )
+ for k, v in cur.items():
+ if isinstance(v, torch.Tensor):
+ nv = new.get(k)
+ if nv is None or nv.shape != v.shape or nv.dtype != v.dtype:
+ raise ValueError(
+ f"tensor {i} field {k!r}: checkpoint "
+ f"{None if nv is None else (tuple(nv.shape), nv.dtype)} vs "
+ f"expected {(tuple(v.shape), v.dtype)}"
+ )
+ if bool(sd.get("stochastic_rounding", self.stochastic_rounding)) != self.stochastic_rounding:
+ logger.warning(
+ "resuming with stochastic_rounding=%s but the checkpoint was written with %s: "
+ "the trajectory will differ from an uninterrupted run",
+ self.stochastic_rounding,
+ sd.get("stochastic_rounding"),
+ )
+
+ # The rounding RNG is part of the state being restored. A generator that
+ # already exists here (this optimizer has stepped) and is ABSENT from `sd`
+ # (saved before any bf16 step, or from a fp32 run) would otherwise keep its
+ # advanced stream, and the rewound optimizer would round differently from an
+ # uninterrupted one -- "byte for byte" quietly untrue (adversarial review,
+ # 2026-10-12). Dropping them re-seeds lazily from `seed + rank`, which is
+ # exactly the state a never-stepped optimizer has.
+ self._generators.clear()
+
+ # copy_ INTO the existing tensors: keeps their device, and means loading
+ # one checkpoint dict into two optimizers cannot make them share state.
+ for cur, new in zip(self._state, saved):
+ for k, v in cur.items():
+ if isinstance(v, torch.Tensor):
+ v.copy_(new[k])
+ cur["step"] = int(new["step"])
+ group = self.param_groups[0]
+ for k, v in sd["param_group"].items():
+ group[k] = tuple(v) if k == "betas" else v
+ self.skipped_steps = int(sd.get("skipped_steps", 0))
+ self.last_grad_norm = float(sd.get("last_grad_norm", float("nan")))
+ for key, st in sd.get("sr_rng", {}).items():
+ first = self.params[0].device
+ gen = self._generator(first if first.type == key else torch.device(key))
+ gen.set_state(st.detach().to("cpu", copy=True))
+
+
+#: The name the factory and docs use.
+AdamW8bit = BlockwiseAdamW8bit
diff --git a/code/align/train/ckpt_format.py b/code/align/train/ckpt_format.py
new file mode 100644
index 0000000000000000000000000000000000000000..073e77124d29b0ec55cb3cd469d869fcb65b86a1
--- /dev/null
+++ b/code/align/train/ckpt_format.py
@@ -0,0 +1,124 @@
+"""Weight-file formats the Trainer can write and read: the model-family seam.
+
+Why this exists. `Trainer.save_checkpoint` / `resume_from` called the dense
+Mistral converters (`save_nmo_as_hf`, `hf_to_nmo(expected_n_layers=
+cfg.model.n_layers)`) directly. Those tables know the 7B dense key names and
+nothing about routers or stacked 3-D expert tensors, so an OLMoE run could be
+built and stepped but never checkpointed -- the first save would raise on a
+key it cannot map (or, worse for a model family added later, silently drop
+parameters if a converter ever became lenient). The seam makes the family an
+explicit, injected choice with ONE contract:
+
+ save_weights(align_state_dict, out_dir, dtype) -> writes model weights +
+ config.json, atomically
+ load_weights(path) -> align state dict
+
+and nothing else about the checkpoint changes: optimizer state, `resume.pt`,
+fsync/rename/completeness all stay in the Trainer and are family-independent.
+
+The default is the dense format built from `cfg.model`, so every existing
+caller is unchanged. A family is selected by construction (`Trainer(...,
+weight_format=OlmoeFormat(olmoe_cfg))`), never sniffed from the state dict:
+guessing the family from key names is how a misconfigured run loads the wrong
+converter and half-works.
+"""
+
+from __future__ import annotations
+
+from typing import Any, Mapping, Optional, Protocol
+
+import torch
+
+
+class WeightFormat(Protocol):
+ def save_weights(
+ self, state: Mapping[str, torch.Tensor], out_dir: str, dtype: Optional[torch.dtype]
+ ) -> None: ...
+
+ def load_weights(self, path: str) -> dict[str, torch.Tensor]: ...
+
+ def copy_tokenizer(self, snapshot: str, dst_dir: str) -> list[str]:
+ """Copy the tokenizer files that belong to THIS family into `dst_dir`;
+ return the names copied (empty = weights-only checkpoint)."""
+ ...
+
+
+class DenseFormat:
+ """The original behaviour: dense Mistral-family HF checkpoints."""
+
+ def __init__(self, model_cfg: Any):
+ self.model_cfg = model_cfg
+
+ def save_weights(self, state, out_dir, dtype):
+ from ..model import checkpoint as ckpt
+
+ ckpt.save_nmo_as_hf(dict(state), self.model_cfg, out_dir, dtype=dtype)
+
+ def load_weights(self, path):
+ from ..model import checkpoint as ckpt
+
+ return ckpt.hf_to_nmo(
+ ckpt.load_hf_safetensors(path), expected_n_layers=self.model_cfg.n_layers
+ )
+
+ def copy_tokenizer(self, snapshot, dst_dir):
+ from ..model import checkpoint as ckpt
+
+ return ckpt.copy_tokenizer_files(snapshot, dst_dir)
+
+
+#: Files that make up an HF tokenizer, in either of the layouts OLMoE-class
+#: models ship (BPE `tokenizer.json`, or `vocab.json` + `merges.txt`).
+OLMOE_TOKENIZER_FILES = (
+ "tokenizer.json",
+ "tokenizer_config.json",
+ "special_tokens_map.json",
+ "vocab.json",
+ "merges.txt",
+ "tokenizer.model",
+ "generation_config.json",
+)
+
+
+class OlmoeFormat:
+ """OLMoE-architecture checkpoints (routers + stacked experts), HF-compatible.
+
+ TOKENIZER. The dense path copies the pinned Mistral v0.3 tokenizer into
+ every checkpoint (same vocab by construction); doing that for an OLMoE
+ checkpoint would stamp a 32768-token tokenizer onto a 50304-token model --
+ loads fine, tokenizes wrong. So this format copies tokenizer files only
+ from an EXPLICIT `tokenizer_dir`, and without one writes a weights-only
+ checkpoint (and says so), never a guess.
+ """
+
+ def __init__(self, olmoe_cfg: Any, tokenizer_dir: Optional[str] = None):
+ self.olmoe_cfg = olmoe_cfg
+ self.tokenizer_dir = tokenizer_dir
+
+ def save_weights(self, state, out_dir, dtype):
+ from ..model.olmoe_checkpoint import save_olmoe_as_hf
+
+ save_olmoe_as_hf(dict(state), self.olmoe_cfg, out_dir, dtype=dtype)
+
+ def load_weights(self, path):
+ from ..model.olmoe_checkpoint import hf_to_align
+ from ..model.checkpoint import load_hf_safetensors
+
+ return hf_to_align(load_hf_safetensors(path), self.olmoe_cfg)
+
+ def copy_tokenizer(self, snapshot, dst_dir): # noqa: ARG002 -- the dense snapshot is not ours
+ import os
+ import shutil
+
+ if not self.tokenizer_dir:
+ return []
+ copied = []
+ for name in OLMOE_TOKENIZER_FILES:
+ src = os.path.join(self.tokenizer_dir, name)
+ if os.path.isfile(src):
+ shutil.copyfile(src, os.path.join(dst_dir, name))
+ copied.append(name)
+ return copied
+
+
+__all__ = ["WeightFormat", "DenseFormat", "OlmoeFormat"]
diff --git a/code/align/train/dpo_loss.py b/code/align/train/dpo_loss.py
new file mode 100644
index 0000000000000000000000000000000000000000..e61d4c9e690b170a9d6b947677f8141ba8b711ab
--- /dev/null
+++ b/code/align/train/dpo_loss.py
@@ -0,0 +1,237 @@
+"""DPO loss adapter: paired batch -> section-05's chunked fused DPO loss.
+
+Mirror of `sft_loss.py`: this module owns plumbing only -- unpacking the paired
+batch, the caller-side label shift, and packaging metrics -- while the kernel in
+`align.kernels.fused_dpo` owns the objective.
+
+The one thing worth stating here rather than in the kernel: the reference
+log-probabilities arrive as PRECOMPUTED CONSTANTS, produced offline by
+section-12 in a single no-grad pass over the SFT checkpoint. There is no
+reference model resident at DPO time, which is what makes 7B DPO fit on 4x A10G
+at all. The kernel detaches them on entry and a test proves no gradient path
+survives; this adapter's job is simply never to build one, so it refuses a
+batch that does not carry them rather than silently substituting zeros -- a
+zero reference turns DPO into plain likelihood maximisation on the chosen side,
+which trains, converges, and is not DPO.
+
+`DpoLossConfig` is the trainer-facing default surface. Note `token_chunk_size`:
+the pre-registered config is micro_batch 1 x seq 1024, i.e. ONE pair per step,
+and at one pair the pair-chunking does nothing at all. Leaving it None there
+would silently restore the [1024, 32768] logit tensor the fused loss exists to
+remove, so the trainer-facing default sets it.
+"""
+
+from __future__ import annotations
+
+import logging
+from dataclasses import dataclass
+from typing import Any, Optional
+
+import torch
+
+from .trainer import LossOutput
+
+logger = logging.getLogger(__name__)
+
+#: Section-05's kernel module, named so the both-directions test can block the
+#: import and exercise the refusal branch (same pattern as `sft_loss`).
+FUSED_DPO_MODULE = "align.kernels.fused_dpo"
+
+#: Fallback only; the authority is the kernel's constant and section-03's
+#: collator, which both use -100.
+IGNORE_INDEX = -100
+
+
+@dataclass
+class DpoLossConfig:
+ """Trainer-facing DPO loss settings. Section-12 nests this in its stage
+ config; every default that encodes a decision says why."""
+
+ #: Preference sharpness. 0.1 is the pre-registered value for both arms.
+ beta: float = 0.1
+ #: One of align.kernels.fused_dpo.DPO_VARIANTS. `dpop` is the pre-registered
+ #: tripwire RESPONSE for logps/chosen declining while the margin rises; it
+ #: is not the primary configuration and switching to it is a documented
+ #: deviation, not a tuning knob.
+ variant: str = "sigmoid"
+ #: Pairs per chunk.
+ chunk_size: int = 1
+ #: Tokens per inner chunk. NOT None: at micro_batch 1 there is one pair per
+ #: step and pair-chunking alone would materialise the full logit tensor.
+ #: 128 rather than 256, DERIVED from `fused_ce.chunked_peak_bound` at the
+ #: 7B shapes (V 32768, H 4096, 2x1024 rows): 256 predicts a 192 MiB peak
+ #: against a 256 MiB budget (0.75, too tight to survive an allocator
+ #: fluctuation in the M5 smoke), while 128 predicts 128 MiB (0.50). The
+ #: matmul is still large enough to be efficient at 128 x 32768.
+ token_chunk_size: Optional[int] = 128
+ #: NLL auxiliary on the chosen completion. 0.0 is provably inert (the term
+ #: is skipped structurally, not multiplied by zero), so the primary
+ #: pre-registered run is unaffected by this field existing.
+ nll_coeff: float = 0.0
+ #: DPO-Positive penalty weight; read only when variant == "dpop".
+ dpop_lambda: float = 50.0
+
+
+def _load_fused():
+ try:
+ import importlib
+
+ return importlib.import_module(FUSED_DPO_MODULE)
+ except Exception: # noqa: BLE001
+ return None
+
+
+def fused_dpo_available() -> bool:
+ return _load_fused() is not None
+
+
+def _unpack(batch: Any):
+ """Accept a paired batch object or a mapping. Six tensors are mandatory and
+ none of them has a defensible default."""
+ required = (
+ "input_ids_chosen",
+ "labels_chosen",
+ "input_ids_rejected",
+ "labels_rejected",
+ "ref_logp_chosen",
+ "ref_logp_rejected",
+ )
+ if isinstance(batch, dict):
+ got = {k: batch.get(k) for k in required}
+ else:
+ got = {k: getattr(batch, k, None) for k in required}
+ missing = [k for k, v in got.items() if v is None]
+ if missing:
+ raise TypeError(
+ f"DPO batch is missing {missing}. The reference log-probabilities "
+ "in particular have no default: substituting zeros would turn DPO "
+ "into likelihood maximisation on the chosen completion, which "
+ "trains and converges and is not DPO. Section-12's "
+ "precompute step produces them."
+ )
+ return got
+
+
+def make_dpo_loss_adapter(
+ config: Optional[DpoLossConfig] = None,
+ require_fused: bool = False,
+ decoupled_backward: bool = False,
+ grad_scale: float = 1.0,
+):
+ """Build the DPO loss adapter the Trainer calls once per micro-step.
+
+ `require_fused=True` refuses the reference path, which is what the 7B stage
+ scripts pass: the eager reference materialises BOTH [P, T, 32768] logit
+ tensors and is a memory profile no A10G survives at 7B.
+
+ `decoupled_backward=True` (the 7B stage setting) backwards each side's
+ trunk separately so both checkpointed graphs are never resident at once;
+ `grad_scale` must then be 1/grad_accum, matching the scaling the Trainer
+ applies to the loss it backwards.
+ """
+ cfg = config or DpoLossConfig()
+ module = _load_fused()
+ if module is None:
+ if require_fused:
+ raise RuntimeError(
+ f"the fused chunked DPO loss ({FUSED_DPO_MODULE}."
+ "fused_dpo_loss) is not importable. Refusing to fall back to "
+ "the reference path for a full-scale run: it materialises both "
+ "[P, T, 32768] logit tensors and their gradients."
+ )
+ raise RuntimeError(
+ f"{FUSED_DPO_MODULE} is not importable and there is no reference "
+ "fallback worth offering for DPO at any useful scale."
+ )
+ if cfg.variant not in module.DPO_VARIANTS:
+ raise ValueError(f"unknown DPO variant {cfg.variant!r}; known: {list(module.DPO_VARIANTS)}")
+ ignore_index = module.IGNORE_INDEX
+
+ def _kernel(h_c, h_r, w, got, target_c, target_r):
+ return module.fused_dpo_loss(
+ h_c,
+ h_r,
+ w,
+ target_c,
+ target_r,
+ got["ref_logp_chosen"],
+ got["ref_logp_rejected"],
+ beta=cfg.beta,
+ variant=cfg.variant,
+ chunk_size=cfg.chunk_size,
+ nll_coeff=cfg.nll_coeff,
+ dpop_lambda=cfg.dpop_lambda,
+ ignore_index=ignore_index,
+ token_chunk_size=cfg.token_chunk_size,
+ )
+
+ def _full_w(model):
+ # FSDP2 params are sharded DTensors OUTSIDE a module __call__; passing
+ # one into the fused kernel's mm mixes Tensor and DTensor (the
+ # hook-bypass class, DPO edition). full_tensor() is the DIFFERENTIABLE
+ # all-gather: backward reduce-scatters d_weight onto the shards.
+ w = model.lm_head.weight
+ return w.full_tensor() if hasattr(w, "full_tensor") else w
+
+ def adapter(model, batch) -> LossOutput:
+ got = _unpack(batch)
+ # Caller-shifted, exactly as in sft_loss: position t predicts t+1.
+ target_c = got["labels_chosen"][:, 1:]
+ target_r = got["labels_rejected"][:, 1:]
+
+ if not decoupled_backward:
+ hidden_c = model.forward_hidden_states(input_ids=got["input_ids_chosen"])[:, :-1]
+ hidden_r = model.forward_hidden_states(input_ids=got["input_ids_rejected"])[:, :-1]
+ loss, metrics = _kernel(hidden_c, hidden_r, _full_w(model), got, target_c, target_r)
+ module.require_metrics(metrics)
+ n_tokens = int((target_c != ignore_index).sum() + (target_r != ignore_index).sum())
+ return LossOutput(loss=loss, n_tokens=n_tokens, metrics=dict(metrics))
+
+ # DECOUPLED BACKWARD -- one trunk graph at a time. Holding BOTH
+ # sides' checkpointed 7B graphs plus fp32 shard gradients peaked at
+ # 21.5GB and OOM'd every rank (measured; batch-size independent).
+ # DPO's coupling between the sides is one scalar per pair, so:
+ # 1. no-grad trunk forwards; the kernel runs over LEAF copies of
+ # the hiddens plus the gathered lm_head, and one backward on
+ # that small graph yields d_hidden per side analytically and
+ # accumulates the lm_head shard grad;
+ # 2. each side then re-forwards WITH grad and backwards against
+ # its saved d_hidden -- never two trunk graphs at once.
+ # The re-forward recomputes hiddens that differ from the no-grad
+ # pass only by kernel-dispatch noise (~1e-7 relative, the same class
+ # the batch-invariance receipt records); act-ckpt already relies on
+ # this determinism. The trainer's `(loss/accum).backward()` contract
+ # is honoured with a detached leaf proxy; the REAL grads are scaled
+ # here by grad_scale (1/accum), so nothing is double-counted.
+ with torch.no_grad():
+ h_c0 = model.forward_hidden_states(input_ids=got["input_ids_chosen"])[:, :-1]
+ h_r0 = model.forward_hidden_states(input_ids=got["input_ids_rejected"])[:, :-1]
+ h_c = h_c0.detach().requires_grad_(True)
+ h_r = h_r0.detach().requires_grad_(True)
+ loss, metrics = _kernel(h_c, h_r, _full_w(model), got, target_c, target_r)
+ module.require_metrics(metrics)
+ (loss * grad_scale).backward()
+ g_c, g_r = h_c.grad, h_r.grad
+ if g_c is None or g_r is None:
+ raise RuntimeError(
+ "decoupled DPO backward produced no hidden gradients; the "
+ "kernel did not differentiate w.r.t. the hidden leaves"
+ )
+ del h_c, h_r, h_c0, h_r0
+
+ hc = model.forward_hidden_states(input_ids=got["input_ids_chosen"])[:, :-1]
+ hc.backward(gradient=g_c)
+ del hc, g_c
+ hr = model.forward_hidden_states(input_ids=got["input_ids_rejected"])[:, :-1]
+ hr.backward(gradient=g_r)
+ del hr, g_r
+
+ n_tokens = int((target_c != ignore_index).sum() + (target_r != ignore_index).sum())
+ # Leaf proxy: the trainer's (loss/accum).backward() runs on THIS and
+ # touches no model gradient; the value is the real loss for the log.
+ proxy = loss.detach().clone().requires_grad_(True)
+ return LossOutput(loss=proxy, n_tokens=n_tokens, metrics=dict(metrics))
+
+ adapter.uses_fused = True
+ adapter.config = cfg
+ return adapter
diff --git a/code/align/train/dpo_stage.py b/code/align/train/dpo_stage.py
new file mode 100644
index 0000000000000000000000000000000000000000..706d7d3fbd03b5b770f89ce88bd0dbb868a5e639
--- /dev/null
+++ b/code/align/train/dpo_stage.py
@@ -0,0 +1,1362 @@
+"""DPO stage: config, entry gates, banner, tripwire response, arm manifest.
+
+Section-12 is the second and last GPU training stage, run as TWO
+pre-registered arms. Everything numerical belongs to other sections -- the
+objective is section-05's `fused_dpo`, the loop is section-07's `Trainer`, the
+tripwire DETECTORS are section-07's `probes.py`, the reference constants are
+`ref_cache.py`. What lives here is the part that decides whether a run is
+allowed to start and what its result is permitted to be.
+
+That sounds like bureaucracy and is not. The predecessor project's expensive
+failures were never wrong arithmetic; they were runs that completed, produced
+publishable-looking numbers, and measured something other than what was
+claimed. So:
+
+ * **Entry gates run in a fixed order and any failure blocks everything
+ after it, including the model load.** Ordered by cost, so a stage that was
+ never going to be valid dies in milliseconds instead of after putting
+ 14.5 GB onto a shared no-swap box.
+ * **The tripwire response is pre-registered.** `logps/chosen` declining
+ while the margin rises is the DPO chosen-likelihood-collapse mode, and it
+ is *maximal for Arm B by design*: pi_SFT already assigns the chosen
+ continuations high likelihood, so the optimizer's cheapest margin comes
+ from crushing both logprobs. The response -- rerun with `dpop` or the NLL
+ auxiliary, and record THAT as the arm's result -- was written down before
+ any run, and no third configuration is reachable without a new
+ pre-registration.
+ * **The primary metrics are margin and held-out preference accuracy**, not
+ benchmark accuracy. The pre-registered behavioural expectations are Arm A
+ within +-2 pp of SFT (below floor is the EXPECTED outcome) and Arm B's
+ honest range INCLUDING ZERO. Section-13 owns the paired benchmark
+ evaluation; a stage that graded itself on benchmarks would be choosing its
+ own success criterion after seeing the data.
+"""
+
+from __future__ import annotations
+
+import collections
+import json
+import logging
+import math
+import os
+from dataclasses import dataclass, field, fields, replace
+from typing import Any, Callable, Mapping, Optional, Sequence
+
+logger = logging.getLogger(__name__)
+
+NAN = float("nan")
+
+#: Entry gates, in the order they run. Ordered by COST: the cheap file reads
+#: come first so an invalid stage fails before it touches a GPU. Data, so the
+#: test iterates it instead of copying a list.
+GATE_ORDER: tuple[str, ...] = (
+ "sft_quality_gate", # section-11 ran and passed its own gate
+ "build_manifest", # shard counts match the pinned values; Arm A DESCOPED
+ "ref_cache_key", # the reference constants belong to THIS run
+ "path_bias", # measured, and its decision honored
+ "throughput_probe", # 20 steps -> banner wall-clock (never asserted ahead)
+ "rss_preflight", # host RSS headroom for the full-FT job class
+)
+
+#: Banner keys this stage adds on top of `registry.MANDATORY_BANNER_KEYS`.
+#:
+#: Deliberately NOT registered into the global registry at import time.
+#: `registry.register_mandatory_key` mutates a module-level list shared by
+#: every script in the process; importing this module would then make an
+#: unrelated banner (trace, generate) incomplete. The contract is identical --
+#: a missing key raises, a partial banner never prints -- it is just enforced
+#: without a global side effect.
+DPO_BANNER_KEYS: tuple[str, ...] = (
+ "arm",
+ "loss_type",
+ "beta",
+ "variant",
+ "lr",
+ "epochs",
+ "global_batch",
+ "grad_accum",
+ "expected_pairs",
+ "optimizer_step_formula",
+ "scheduler_horizon_optimizer_steps",
+ "dataset_hashes",
+ "ref_cache_key",
+ "path_bias",
+ "probe_step_time_s",
+ "probe_derived_wall_clock_h",
+ "rss_budget",
+ "attention_backend",
+ "length_drift_baseline",
+ "length_drift_baseline_source",
+ "sft_checkpoint",
+ "pre_registration",
+)
+
+#: The only configurations the pre-registration permits when the
+#: chosen-logprob tripwire fires. Anything else requires a new dated entry in
+#: docs/RESULTS.md written BEFORE the rerun, not after seeing it.
+#:
+#: `dpop_nll_aux` is the default because it is what the frozen
+#: pre-registration literally says: "rerun THAT arm once with dpop + NLL
+#: auxiliary". The section-12 spec phrases the same response as "the `dpop`
+#: variant OR the NLL auxiliary", so the two single-knob forms are kept
+#: available -- but docs/RESULTS.md is the frozen document and the conjunction
+#: is what it froze, so the conjunction is the default. (Discrepancy raised
+#: with the lead rather than resolved silently in either direction.)
+PRE_REGISTERED_RESPONSES: tuple[str, ...] = ("dpop_nll_aux", "dpop", "nll_aux")
+
+#: NLL auxiliary weight used when the response is `nll_aux`. Small: the term
+#: exists to put a floor under the chosen logprob, not to turn DPO into SFT.
+NLL_AUX_COEFF = 0.1
+
+
+# ---------------------------------------------------------------------------
+# Config
+# ---------------------------------------------------------------------------
+
+
+@dataclass
+class DpoStageConfig:
+ """Stage settings for one arm. Per-field docstrings, per repo convention:
+ the config file is where someone will try to change these at 2am."""
+
+ #: `arm_a` (argilla, response-level, 2,310 pairs) or `arm_b` (curated
+ #: PRM800K shared-prefix step-local pairs). Selects everything else.
+ arm: str = "arm_a"
+ #: Section-11's SFT checkpoint. Both arms fine-tune from the SAME one, so
+ #: any arm-vs-arm difference is the preference data, not the start point.
+ sft_checkpoint: str = ""
+ #: Section-11's quality-gate receipt. The stage refuses to launch unless
+ #: it exists AND records a pass.
+ sft_quality_gate_path: str = ""
+ #: Pair shards for this arm (train split) and the problem-level held-out
+ #: split used for preference accuracy.
+ shard_dir: str = ""
+ heldout_shard_dir: str = ""
+ #: Reference-logprob caches written by scripts/precompute_ref_logprobs.py.
+ ref_cache_path: str = ""
+ heldout_ref_cache_path: str = ""
+ #: The mandatory pre-launch path-bias measurement's report.
+ path_bias_report_path: str = ""
+ #: Section-03's build manifest. Its hash is part of the ref-cache key, so
+ #: a shard rebuild cannot leave a stale cache looking valid.
+ manifest_path: str = "/apps/development/rlhf/align/data/manifest.json"
+ #: docs/RESULTS.md must already carry this arm's pre-registration
+ #: (expectations + tripwire response) before the run starts.
+ #: The pre-registration is a FROZEN, dated document that lives with the
+ #: predecessor experiment record (read-only; the gate only greps it).
+ #: Copying it would fork the thing that is supposed to be frozen, so the
+ #: default stays on the original until it is migrated deliberately.
+ results_doc: str = "/apps/development/rlhf/nano-mistral-o1/docs/RESULTS.md"
+ run_dir: str = ""
+
+ #: Post-filter pair count for this arm, from the build manifest. Drives
+ #: the optimizer-step horizon, so it is a measured number, never a guess.
+ expected_pairs: int = 2310
+
+ # -- objective ---------------------------------------------------------
+ #: Pre-registered for both arms. `dpop`/`nll_aux` are reachable only as
+ #: the tripwire RESPONSE, and switching is a recorded deviation.
+ variant: str = "sigmoid"
+ #: Preference sharpness. 0.1, both arms, pre-registered.
+ beta: float = 0.1
+ #: NLL auxiliary weight. 0.0 is provably inert in section-05's kernel (the
+ #: term is skipped structurally, not multiplied by zero).
+ nll_coeff: float = 0.0
+ dpop_lambda: float = 50.0
+ #: Which pre-registered response to apply if the chosen-logprob tripwire
+ #: fires. Defaults to the conjunction docs/RESULTS.md froze; the two
+ #: single-knob forms are permitted, anything else raises.
+ tripwire_response: str = "dpop_nll_aux"
+
+ # -- model -------------------------------------------------------------
+ #: Attention backend for the policy model. NEVER handed raw to a
+ #: constructor: `NmoMistralForCausalLM.__init__` routes it through
+ #: `align.model.attention.select_attn_implementation`, which RAISES if the
+ #: request cannot be honoured -- for `triton_fa2` that means section-06's
+ #: equivalence-gate receipt must be valid for THIS kernel source on THIS
+ #: GPU. The pre-registered value for BOTH arms is `triton_fa2`
+ #: (docs/RESULTS.md dated entry, 2026-08-17); the neutral default here is
+ #: sdpa so the pin lives in the arm configs, where changing it is a
+ #: visible diff against the pre-registration. Membership is validated at
+ #: config load: a typo'd backend raises at launch, in milliseconds, not
+ #: as a selector error with four GPUs claimed.
+ attn_implementation: str = "sdpa"
+
+ # -- optimization ------------------------------------------------------
+ #: 5e-7, pre-registered. Two orders below SFT's: DPO moves a model that is
+ #: already where it should be.
+ lr: float = 5e-7
+ #: 2 pre-registered, 3 maximum.
+ epochs: int = 2
+ #: ~32 via gradient accumulation (e.g. 4 ranks x micro 2 x accum 4).
+ global_batch: int = 32
+ #: >= 4 is MANDATORY for full-FT with offloaded AdamW (section-07 refuses
+ #: less): at accum 1 the CPU optimizer step dominates wall-clock.
+ grad_accum: int = 4
+ micro_batch_size: int = 2
+ num_workers: int = 2
+ save_every_micro_steps: int = 100
+ keep_checkpoints: int = 2
+ #: Each rank writes its own optimizer_rank{r}.pt instead of gathering the
+ #: full optimizer state (see TrainConfig.per_rank_optimizer_state). Without
+ #: this field the key was REJECTED by struct mode in DPO TOMLs and, worse,
+ #: scripts/dpo.py built its RunConfig without ever setting it -- so the
+ #: trainer-level flag was unreachable from a DPO run. Default False keeps
+ #: the gathered layout; a resume at a different world_size is refused by
+ #: the trainer when True.
+ per_rank_optimizer_state: bool = False
+
+ #: Global-norm gradient clip (OptimizerConfig.max_grad_norm), forwarded by
+ #: scripts/dpo.py::_run_config. Declared here because the DPO RunConfig is
+ #: built from defaults: before this field the clip could not be stated in a
+ #: DPO TOML at all, only inherited. The recorded grad norm stays PRE-clip.
+ max_grad_norm: Optional[float] = 1.0
+
+ # -- run-health bounds (G0), forwarded to TrainConfig by the stage script's
+ # _run_config: a field that is not copied trains at the default while the
+ # TOML says otherwise. Semantics: align/config.py::TrainConfig.
+ max_wall_seconds: Optional[float] = None
+ max_consecutive_skipped_steps: int = 3
+ max_skipped_fraction: float = 0.05
+
+ # -- measurement -------------------------------------------------------
+ #: Fires the path-bias rerun when max|delta ref-margin| exceeds this
+ #: fraction of the early-margin scale. 0.05 = a 5% perturbation of the
+ #: quantity DPO reads. Chosen as a threshold to MEASURE against, not a
+ #: tolerance to pass: the measured single-GPU-vs-sharded deltas are
+ #: expected orders below it, and if they are not, the cache is rebuilt
+ #: through the sharded forward rather than argued about.
+ path_bias_threshold: float = 0.05
+ #: Denominator of that ratio: the typical magnitude of the implicit margin
+ #: early in the run, where the loss argument sits near zero and a
+ #: reference bias is least attenuated.
+ path_bias_early_margin_scale: float = 0.5
+ #: Held-out preference accuracy cadence, in optimizer steps.
+ heldout_every_optimizer_steps: int = 50
+ #: Pairs the chosen-longer fraction is computed over, as a ROLLING
+ #: WINDOW. Not per micro-batch, which is what the statistic naively is:
+ #: at the pre-registered micro_batch 2 the per-batch fraction can only be
+ #: 0, 0.5 or 1, so against a +-0.10 band it false-fires ~78% of the time.
+ #:
+ #: 512 is DERIVED, not chosen. SE of a fraction near the 0.5489 baseline
+ #: is sqrt(p(1-p)/k), so the band sits at 0.10/SE standard errors:
+ #: k=2 -> 0.28 SE (78% false-fire) k=128 -> 2.27 SE (2.3e-02)
+ #: k=32 -> 1.14 SE (26%) k=256 -> 3.22 SE (1.3e-03)
+ #: k=512 -> 4.55 SE (5.4e-06)
+ #: At 512 the per-check false-fire rate is 5e-06; over Arm B's ~3,000
+ #: logged micro-steps that is ~1.6e-02 expected spurious alarms for the
+ #: whole run. 256 would give ~4 -- enough to bury a real one.
+ length_window_pairs: int = 512
+ #: Which tripwires this arm ARMS, copied into `RunConfig.tripwires.armed`.
+ #: Both, for both arms: the collapse tripwire is the one with a
+ #: pre-registered response, and the length-drift one guards the 0.5489
+ #: baseline. Section-07 halts at the first log step if an armed tripwire's
+ #: inputs are missing or NaN, so arming here is a commitment that this
+ #: stage's loss adapter emits them -- not a hopeful setting.
+ armed_tripwires: tuple[str, ...] = ("logps_chosen", "length_drift", "grad_norm")
+ #: Length-drift band around the arm's MEASURED chosen-longer baseline
+ #: (Arm A: 0.5489 from the build manifest -- never hardcoded here).
+ length_band: float = 0.10
+ logps_window: int = 20
+
+ def __post_init__(self) -> None:
+ if self.arm not in ("arm_a", "arm_b"):
+ raise ValueError(f"arm={self.arm!r} must be 'arm_a' or 'arm_b'")
+ if self.tripwire_response not in PRE_REGISTERED_RESPONSES:
+ raise ValueError(
+ f"tripwire_response={self.tripwire_response!r} is not "
+ f"pre-registered; permitted: {list(PRE_REGISTERED_RESPONSES)}"
+ )
+ # Imported here, not at module top: align.model.attention pulls torch,
+ # and this dataclass must stay constructible before the resource
+ # guards have configured the process.
+ from ..model.attention import NMO_IMPLEMENTED
+
+ if self.attn_implementation not in NMO_IMPLEMENTED:
+ raise ValueError(
+ f"attn_implementation={self.attn_implementation!r} is not an "
+ f"align attention code path; permitted: {sorted(NMO_IMPLEMENTED)}. "
+ "A typo must raise at config load, never fall through to a "
+ "default backend -- a run that reports one backend while "
+ "executing another poisons every downstream comparison."
+ )
+
+
+def load_stage_config(path: str, overrides: Optional[Sequence[str]] = None) -> DpoStageConfig:
+ """TOML (+ dotted `key=value` overrides) -> DpoStageConfig, struct mode on:
+ a typo'd key raises at launch instead of silently training with the
+ default it failed to override."""
+ from align.config import load_config
+
+ return load_config(DpoStageConfig, path, list(overrides or []))
+
+
+def rotate_stale_checkpoints(ckpt_dir: str) -> Optional[str]:
+ """Move a previous run's checkpoint dir aside, ONCE, for a fresh arm.
+
+ COLLECTIVE: every rank calls it. Only rank 0 touches the filesystem and
+ every rank then waits at a barrier, so all ranks proceed with the same
+ directory state. Run on every rank, the bare `os.rename` let exactly one
+ rank win and the other three die with FileNotFoundError (ranks straddling
+ a second boundary even computed different destinations); the survivors
+ had already entered Trainer construction. Returns the destination on
+ rank 0 when a rotation happened, else None.
+ """
+ import time
+
+ from ..utils.dist import barrier, is_rank0
+
+ dst = None
+ if is_rank0() and os.path.isdir(ckpt_dir) and os.listdir(ckpt_dir):
+ dst = f"{ckpt_dir}_stale_{time.strftime('%Y%m%d-%H%M%S')}"
+ os.rename(ckpt_dir, dst)
+ logger.warning("rotated stale checkpoints to %s", dst)
+ barrier()
+ return dst
+
+
+def probe_micro_cap(micro_step: int, probe_steps: int) -> int:
+ """The `max_micro_steps` cap for the launch probe, on the ABSOLUTE counter.
+
+ The Trainer tests `micro_step >= limit` against the absolute micro-step
+ count, which a resume restores. A bare `probe_steps` cap on a run resumed
+ at micro N >= probe_steps therefore trained exactly one micro-step and
+ left `throughput_probe` one time to aggregate (ValueError), so every
+ supervisor retry died here without a checkpoint. The probe is the first
+ `probe_steps` micro-steps of THIS process, wherever it starts.
+ """
+ return int(micro_step) + int(probe_steps)
+
+
+def reduce_tripwire_alarms(alarms: Sequence[Mapping]) -> list[dict]:
+ """Every rank's tripwire alarms, unioned: the same list on all ranks.
+
+ The pre-registered rerun is a COLLECTIVE decision (a second model load, a
+ new FSDP wrap, a new Trainer). The trainer's alarms are built from
+ rank-local metrics over different pairs, and only HALT verdicts are
+ reduced inside the trainer, so a non-HALT alarm (the chosen-logprob
+ decline) can fire on some ranks only. Acting on the local list sends the
+ firing ranks into the rerun's collectives while the rest write the
+ manifest and exit. COLLECTIVE: every rank must call it. Each alarm gains
+ a `rank` key naming the rank that raised it.
+ """
+ from ..utils.dist import all_gather_objects
+
+ union: list[dict] = []
+ for r, rank_alarms in enumerate(all_gather_objects([dict(a) for a in alarms])):
+ for a in rank_alarms:
+ union.append({**a, "rank": r})
+ return union
+
+
+def resolve_pair_split_dir(shard_dir: str, split: str) -> str:
+ """The directory holding `-NNNN.receipt.json`, found by DISCOVERY.
+
+ THE HAND-COMPOSED-PATH DEFECT, DPO edition. The attempt-1 configs pointed
+ at `data/shards/arm_a-train`, composed from a naming convention; the store
+ actually writes `data/shards/arm_a/train/arm_a-train-0000.receipt.json`.
+ Section-11 hit the identical defect and fixed it with
+ `stage_smoke.resolve_split_dir`, which searches for the receipts the store
+ itself wrote; this delegates to that same function (read-only) rather than
+ growing a second, drifting copy. Accepts either the shard root or the
+ exact split directory -- both contain the split's receipts -- and a
+ missing split reports the splits that ARE present.
+ """
+ from .stage_smoke import resolve_split_dir
+
+ return str(resolve_split_dir(shard_dir, split))
+
+
+def is_hf_checkpoint(path: Optional[str]) -> bool:
+ """A directory is a loadable checkpoint iff it carries `config.json` --
+ the file `AutoConfig.from_pretrained` reads first. A run dir that merely
+ CONTAINS checkpoints is not itself one."""
+ return bool(path) and os.path.isfile(os.path.join(str(path), "config.json"))
+
+
+def resolve_sft_checkpoint(configured: str, receipt_checkpoint: Optional[str]) -> tuple[str, str]:
+ """The policy's starting checkpoint: `(path, source)`, or a raise.
+
+ Section-11's Trainer writes `/checkpoints/micro_NNNNNNNN`
+ (trainer.save_checkpoint) and there is NO stable `final` alias -- the
+ attempt-1 configs pointed at `checkpoints/sft/final`, a path nothing ever
+ writes, which would have failed at model load with the gates already
+ passed. What IS stable is the quality-gate receipt: section-11 records
+ WHICH micro-step checkpoint passed its gate in the receipt's `checkpoint`
+ field, so that is the discovery mechanism. A configured path that is
+ itself a checkpoint wins, as the explicit override for rerunning against
+ a pinned checkpoint.
+ """
+ if is_hf_checkpoint(configured):
+ return configured, "config"
+ if is_hf_checkpoint(receipt_checkpoint):
+ return str(receipt_checkpoint), "sft_quality_gate_receipt"
+ raise FileNotFoundError(
+ f"no SFT checkpoint found. Configured sft_checkpoint={configured!r} "
+ "is not a checkpoint directory (no config.json), and the quality-gate "
+ f"receipt records checkpoint={receipt_checkpoint!r}, which is not one "
+ "either. Section-11's trainer writes "
+ "/checkpoints/micro_NNNNNNNN and its receipt names the one "
+ "that passed the gate; refusing to guess a starting point for a "
+ "full-FT arm."
+ )
+
+
+# ---------------------------------------------------------------------------
+# Step arithmetic
+# ---------------------------------------------------------------------------
+
+
+def optimizer_steps(pairs: int, global_batch: int, epochs: int) -> int:
+ """epochs * ceil(pairs / global_batch).
+
+ Written once, here, because the iterations-vs-steps confusion is a
+ documented predecessor incident: an LR horizon quoted in MICRO-steps
+ schedules the cosine over 4-8x the real run, so the LR never decays and
+ the run ends at nearly its peak rate with no sign anything was wrong.
+ Ceiling, not floor -- a trailing partial batch is still an optimizer step.
+ """
+ if pairs < 1:
+ raise ValueError(f"pairs={pairs} must be >= 1")
+ if global_batch < 1:
+ raise ValueError(f"global_batch={global_batch} must be >= 1")
+ if epochs < 1:
+ raise ValueError(f"epochs={epochs} must be >= 1")
+ return epochs * math.ceil(pairs / global_batch)
+
+
+# ---------------------------------------------------------------------------
+# Entry gates
+# ---------------------------------------------------------------------------
+
+
+def run_entry_gates(
+ cfg: DpoStageConfig, gates: Mapping[str, Callable[[DpoStageConfig], dict]]
+) -> list[dict]:
+ """Run every gate in `GATE_ORDER`, stopping at the first failure.
+
+ Returns one record per gate that ran. A gate absent from `gates` RAISES:
+ an unimplemented gate treated as passed is how a run launches with an
+ unchecked reference cache, and the whole point of the ordered sequence is
+ that reaching gate N means gates 1..N-1 held.
+ """
+ report: list[dict] = []
+ for name in GATE_ORDER:
+ fn = gates.get(name)
+ if fn is None:
+ raise RuntimeError(
+ f"entry gate {name!r} has no implementation. A missing gate is "
+ "not a passed gate; refusing to launch."
+ )
+ result = fn(cfg)
+ record = {"gate": name}
+ if isinstance(result, Mapping):
+ record.update(result)
+ report.append(record)
+ logger.info("entry gate ok: %s", name)
+ return report
+
+
+#: The gate whose pass this stage depends on, named in section-11's own
+#: `stage_gates.GATE_ORDER` rather than spelled here as a string this module
+#: invented.
+SFT_GATE_NAME = "quality_gate"
+
+
+def read_sft_quality_gate(path: str) -> dict:
+ """Section-11's quality-gate verdict: exists, and records a PASS.
+
+ Reads section-11's OWN artifact. `scripts/sft.py` records its gates
+ through `stage_gates.GateLedger`, which writes `stage_gates.json` as
+ `{"eval_source_hash": ..., "gates": {"quality_gate": {"passed": ...}}}`.
+ An earlier draft of this function read a standalone
+ `{"passed": true}` receipt that nothing writes -- the gate would have
+ raised FileNotFoundError with four GPUs already claimed. `path` may name
+ the ledger file or the run directory containing it.
+
+ The comparison itself is NOT restated here. Section-11's
+ `stage_gates.quality_gate` delegates to section-10's `assert_meets_golden`,
+ which compares against the WILSON LOWER BOUND -- the golden anchors are
+ themselves measurements with sampling noise, so demanding the point
+ estimate meet them would fail correct runs about half the time. This stage
+ consumes that verdict; a second copy of the CI reasoning here would drift
+ from it, and the drift would be invisible.
+
+ Three states, and the third is the one that gets skipped. Missing: section
+ 11 never ran, or ran and never recorded. Present and failed: obvious.
+ Present and SILENT on this gate: also a refusal, because an artifact that
+ does not answer the question is indistinguishable from one that answers
+ 'no', and defaulting to 'yes' would let a partial ledger authorise eight
+ GPU-hours.
+ """
+ from .stage_gates import LEDGER_FILENAME, PENDING
+
+ resolved = path
+ if path and os.path.isdir(path):
+ resolved = os.path.join(path, LEDGER_FILENAME)
+ if not resolved or not os.path.isfile(resolved):
+ raise FileNotFoundError(
+ f"SFT quality gate ledger {resolved!r} is missing. DPO fine-tunes "
+ "the SFT checkpoint, so an SFT run that never passed its own gate "
+ "makes both arms measurements of a broken starting point -- at "
+ "full GPU cost, with nothing downstream able to tell. Point "
+ "sft_quality_gate_path at section-11's run dir or its "
+ f"{LEDGER_FILENAME}."
+ )
+ with open(resolved) as f:
+ blob = json.load(f)
+
+ # Accept the ledger shape, and a bare {"passed": ...} object too: the
+ # latter is what a hand-written override looks like, and refusing it would
+ # push someone toward editing the ledger instead.
+ gates = blob.get("gates", blob if "passed" in blob else {})
+ entry = gates.get(SFT_GATE_NAME, blob if "passed" in blob else PENDING)
+ if not isinstance(entry, dict) or "passed" not in entry:
+ raise RuntimeError(
+ f"{resolved!r} does not record whether the SFT {SFT_GATE_NAME!r} "
+ f"passed (it reads {entry!r}). An artifact that cannot answer the "
+ "question is not a pass."
+ )
+ if not entry["passed"]:
+ raise RuntimeError(
+ f"the SFT quality gate did not pass: "
+ f"{entry.get('detail', '(no detail recorded)')}. Fix section-11 "
+ "before spending this stage's GPU budget."
+ )
+ return {
+ "passed": True,
+ "detail": entry.get("detail", ""),
+ "recorded_at": entry.get("recorded_at"),
+ "eval_source_hash": blob.get("eval_source_hash"),
+ "checkpoint": blob.get("checkpoint", entry.get("checkpoint")),
+ "ledger": resolved,
+ }
+
+
+def check_arm_descoped(arm: str, manifest: Mapping) -> dict:
+ """Honour section-03's Arm A DESCOPED marker (filter yield < 1,500).
+
+ The failure this prevents is quiet and expensive: training Arm A on
+ whatever survived the filter and reporting it under the pre-registered
+ arm's name. A descoped arm is a RESULT -- it gets a manifest saying so --
+ not an absence.
+ """
+ descoped = bool(manifest.get("arm_a_descoped", False)) and arm == "arm_a"
+ return {
+ "arm": arm,
+ "descoped": descoped,
+ "train": not descoped,
+ "detail": (
+ "section-03 marked Arm A descoped (filter yield below the "
+ "1,500 floor); refusing to train it and recording the "
+ "descope as this arm's result"
+ if descoped
+ else "arm is in scope"
+ ),
+ }
+
+
+def build_manifest_gate(cfg: DpoStageConfig, manifest: Mapping) -> dict:
+ """Counts match the pinned values and the build verified its own pins."""
+ if not manifest.get("pins_verified", False):
+ raise RuntimeError(
+ "the build manifest reports pins_verified=false: the corpus was "
+ "built in re-measurement mode and its counts were never checked "
+ "against the pinned values. Sections 11 and 12 refuse to launch "
+ f"against it. Violations: {manifest.get('pin_violations')}"
+ )
+ counts = manifest.get("counts", {})
+ key = "arm_a_pairs" if cfg.arm == "arm_a" else "arm_b_curated"
+ measured = counts.get(key)
+ if measured is None:
+ raise KeyError(
+ f"build manifest carries no {key!r} count, so the optimizer-step "
+ "horizon cannot be derived from a measured pair count."
+ )
+ if int(measured) != int(cfg.expected_pairs):
+ raise RuntimeError(
+ f"{cfg.arm}: manifest reports {measured} pairs but the stage "
+ f"config expects {cfg.expected_pairs}. The horizon, the LR "
+ "schedule and the ref cache were all built for the config number; "
+ "one of the two is stale."
+ )
+ return {"pairs": int(measured), **check_arm_descoped(cfg.arm, manifest)}
+
+
+# ---------------------------------------------------------------------------
+# Banner
+# ---------------------------------------------------------------------------
+
+
+def measure_chosen_longer_fraction(rows: Sequence[Any]) -> float:
+ """Fraction of pairs whose CHOSEN completion is longer, from the rows.
+
+ Needed because section-03's build manifest records Arm A's baseline
+ (0.5489) and not Arm B's, while the length-drift tripwire needs one per
+ arm: `LengthDriftTripwire` refuses a NaN baseline outright, and borrowing
+ Arm A's would arm Arm B against a distribution it was never drawn from --
+ a tripwire that then fires forever or never, both of which are worse than
+ no tripwire because they look like measurements.
+
+ Completion length, not sequence length: Arm B's two sides share their
+ entire prefix, so total length is dominated by a term identical on both
+ sides and would compress the fraction toward 0.5 regardless of the data.
+ """
+ import numpy as np
+
+ longer = 0
+ counted = 0
+ for r in rows:
+ lc = len(np.asarray(r["chosen_input_ids"])) - int(r["chosen_prompt_len"])
+ lr = len(np.asarray(r["rejected_input_ids"])) - int(r["rejected_prompt_len"])
+ if lc <= 0 and lr <= 0:
+ continue # no completion on either side
+ counted += 1
+ longer += int(lc > lr)
+ return longer / counted if counted else NAN
+
+
+def dpo_banner(
+ *,
+ cfg: DpoStageConfig,
+ manifest: Mapping,
+ ref_cache_key,
+ path_bias: Mapping,
+ probe: Mapping,
+ rss: Mapping,
+ backend: str,
+ sft_gate: Mapping,
+ length_baseline: Optional[tuple[float, str]] = None,
+ pre_registration: str = "docs/RESULTS.md",
+) -> dict:
+ """Everything this run depends on, or a raise.
+
+ Two things are enforced here rather than merely reported. The length-drift
+ baseline is READ FROM THE MANIFEST (Arm A measured 0.5489; Arm B's is
+ different, so a constant would mis-arm the tripwire on one arm forever),
+ and a path-bias decision of 'rerun through the sharded forward' must
+ actually have been honoured -- a measurement taken, recorded, and ignored
+ is worse than one never taken, because it leaves evidence that the
+ question was considered.
+ """
+ baseline_key = f"{cfg.arm}_chosen_longer_fraction"
+ baselines = manifest.get("length_bias_baseline", {})
+ if length_baseline is not None:
+ baseline, baseline_source = length_baseline
+ if math.isnan(baseline):
+ raise ValueError(
+ f"{baseline_key}: the supplied baseline is NaN, which means "
+ "nobody measured it. The length-drift tripwire refuses a NaN "
+ "baseline, so this would disarm it silently."
+ )
+ elif baseline_key in baselines:
+ baseline, baseline_source = baselines[baseline_key], "build_manifest"
+ else:
+ raise KeyError(
+ f"build manifest has no {baseline_key!r} and no measured baseline "
+ "was supplied. The length-drift tripwire needs THIS arm's "
+ "chosen-longer fraction; defaulting to the other arm's would "
+ "mis-arm it permanently. scripts/dpo.py measures it from the "
+ "arm's own shards when the manifest lacks it "
+ "(measure_chosen_longer_fraction)."
+ )
+
+ if path_bias.get("fired") and getattr(ref_cache_key, "forward_impl", None) != "fsdp2_sharded":
+ raise RuntimeError(
+ "the path-bias measurement fired (action="
+ f"{path_bias.get('action')!r}, ratio={path_bias.get('ratio')}) but "
+ f"the reference cache is tagged "
+ f"{getattr(ref_cache_key, 'forward_impl', None)!r}, not "
+ "'fsdp2_sharded'. Re-run the precompute through the sharded "
+ "forward. A decision that was measured, recorded and then ignored "
+ "is worse than one never taken."
+ )
+
+ horizon = optimizer_steps(cfg.expected_pairs, cfg.global_batch, cfg.epochs)
+ return {
+ "arm": cfg.arm,
+ "loss_type": f"dpo_{cfg.variant}",
+ "variant": cfg.variant,
+ "beta": cfg.beta,
+ "lr": cfg.lr,
+ "epochs": cfg.epochs,
+ "global_batch": cfg.global_batch,
+ "grad_accum": cfg.grad_accum,
+ "expected_pairs": cfg.expected_pairs,
+ "optimizer_step_formula": f"{cfg.epochs} x ceil({cfg.expected_pairs}/{cfg.global_batch}) "
+ f"= {horizon}",
+ "scheduler_horizon_optimizer_steps": horizon,
+ "dataset_hashes": dict(manifest.get("hashes", {})),
+ "ref_cache_key": json.loads(ref_cache_key.to_json()),
+ "path_bias": dict(path_bias),
+ "probe_step_time_s": probe.get("step_time_s_median", NAN),
+ "probe_derived_wall_clock_h": probe.get("projected_wall_clock_h", NAN),
+ "rss_budget": dict(rss),
+ "attention_backend": backend,
+ "length_drift_baseline": baseline,
+ "length_drift_baseline_source": baseline_source,
+ "sft_checkpoint": sft_gate.get("checkpoint", cfg.sft_checkpoint),
+ "pre_registration": pre_registration,
+ }
+
+
+def render_dpo_banner(resolved: Mapping, title: str = "align dpo") -> str:
+ """Render, or raise. Never a partial banner.
+
+ Checks this stage's keys AND section-01's global mandatory set, without
+ mutating the global registry -- see DPO_BANNER_KEYS for why.
+ """
+ from ..registry import MANDATORY_BANNER_KEYS
+
+ required = list(DPO_BANNER_KEYS) + [
+ k for k in MANDATORY_BANNER_KEYS if k not in DPO_BANNER_KEYS
+ ]
+ missing = [k for k in DPO_BANNER_KEYS if k not in resolved]
+ unresolved = [k for k in DPO_BANNER_KEYS if k in resolved and resolved[k] is None]
+ if missing or unresolved:
+ raise RuntimeError(
+ "DPO launch banner incomplete -- refusing to start. A run that "
+ f"cannot answer its question does not begin. missing={missing} "
+ f"unresolved(None)={unresolved}. Undefined values must be "
+ "EXPLICIT: float('nan') or 'PENDING', never absent."
+ )
+ width = 78
+ lines = ["=" * width, f"| {title} [{resolved['arm']}]", "=" * width]
+ for key in required:
+ if key in resolved:
+ lines.append(f"| {key:<36} {resolved[key]!r}")
+ for key in sorted(k for k in resolved if k not in required):
+ lines.append(f"| {key:<36} {resolved[key]!r} (extra)")
+ lines.append("=" * width)
+ return "\n".join(lines)
+
+
+# ---------------------------------------------------------------------------
+# Tripwire response
+# ---------------------------------------------------------------------------
+
+
+def apply_tripwire_response(
+ cfg: DpoStageConfig, alarms: Sequence[Mapping]
+) -> tuple[DpoStageConfig, dict]:
+ """Map a fired chosen-logprob tripwire to the PRE-REGISTERED response.
+
+ Returns `(config to rerun the arm with, record)`. The record goes into the
+ arm manifest, so the deviation is part of the result rather than a note
+ somebody remembers.
+
+ The alarm being responded to: `logps/chosen` declining while the margin
+ improves. DPO's objective is satisfied by separating the two logprobs, and
+ crushing the rejected one separates them just as well as raising the
+ chosen one -- while degrading the policy. The mode is MAXIMAL FOR ARM B by
+ design, because pi_SFT already assigns those chosen continuations high
+ likelihood, so there is little headroom above and a great deal below.
+
+ Every permitted response puts a floor under the chosen logprob: `dpop`
+ adds an explicit penalty when it falls below the reference, `nll_aux` adds
+ a likelihood term on the chosen completion, and `dpop_nll_aux` -- the
+ frozen pre-registration's wording -- does both. Beta is NOT changed, and
+ the arm re-runs ONCE: a second rerun would be a third configuration.
+ """
+ # "logps_chosen" is the WIRE NAME the trainer records (trainer.py appends
+ # {"alarm": name} for the armed wire key). This filter matched a name
+ # nobody emits ("logps_chosen_decline") until 2026-08-17 -- a fired
+ # collapse tripwire could never trigger the pre-registered rerun, and the
+ # unit test asserted the filter against its own literal instead of the
+ # trainer's. Silent-failure trap #7's shape: armed-looking, disconnected.
+ fired = [a for a in alarms if a.get("alarm") == "logps_chosen"]
+ if not fired:
+ return cfg, {
+ "fired": False,
+ "rerun_required": False,
+ "pre_registered": True,
+ "configuration": f"{cfg.variant} beta={cfg.beta}",
+ "detail": "no chosen-logprob alarm; primary configuration stands as the arm's result",
+ }
+
+ if cfg.tripwire_response not in PRE_REGISTERED_RESPONSES:
+ raise ValueError(
+ f"tripwire_response={cfg.tripwire_response!r} is outside the "
+ f"pre-registration ({list(PRE_REGISTERED_RESPONSES)}). A third "
+ "configuration requires a NEW pre-registration written before the "
+ "rerun -- choosing one after seeing the data is how a stage picks "
+ "its own success criterion."
+ )
+ if cfg.tripwire_response == "dpop_nll_aux":
+ new = replace(cfg, variant="dpop", nll_coeff=NLL_AUX_COEFF)
+ elif cfg.tripwire_response == "dpop":
+ new = replace(cfg, variant="dpop")
+ else:
+ new = replace(cfg, variant="sigmoid", nll_coeff=NLL_AUX_COEFF)
+ record = {
+ "fired": True,
+ "rerun_required": True,
+ "pre_registered": True,
+ "alarms": [dict(a) for a in fired],
+ "response": cfg.tripwire_response,
+ "configuration": (
+ f"{new.variant} beta={new.beta} nll_coeff={new.nll_coeff} dpop_lambda={new.dpop_lambda}"
+ ),
+ "detail": (
+ "chosen-logprob collapse detected; the arm re-runs with the "
+ "pre-registered response and THAT configuration is recorded "
+ "as the arm's result"
+ ),
+ }
+ logger.error("tripwire response applied: %s", record["configuration"])
+ return new, record
+
+
+# ---------------------------------------------------------------------------
+# Arm result manifest
+# ---------------------------------------------------------------------------
+
+
+def arm_result_manifest(
+ *,
+ cfg: DpoStageConfig,
+ descoped: bool,
+ summary: Mapping,
+ heldout: Mapping,
+ banner: Mapping,
+ checkpoint: Optional[str],
+ tripwire: Optional[Mapping] = None,
+) -> dict:
+ """What section-13 reads to select an arm and run the paired evaluation.
+
+ A descoped arm still produces a manifest. Its accuracy is NaN, not 0.0:
+ zero is a plausible catastrophic result and would read as one, while NaN
+ says nobody measured -- the distinction section-13's arm-selection rule
+ depends on.
+ """
+ return {
+ "arm": cfg.arm,
+ "status": "descoped" if descoped else "trained",
+ "checkpoint": None if descoped else checkpoint,
+ "loss_variant_actually_used": None if descoped else cfg.variant,
+ "beta": cfg.beta,
+ "nll_coeff": cfg.nll_coeff,
+ "lr": cfg.lr,
+ "epochs": cfg.epochs,
+ "global_batch": cfg.global_batch,
+ "optimizer_steps_planned": optimizer_steps(
+ cfg.expected_pairs, cfg.global_batch, cfg.epochs
+ ),
+ "optimizer_steps_run": summary.get("optimizer_step", 0),
+ "heldout_preference_accuracy": heldout.get("accuracy", NAN),
+ "heldout_ci": [heldout.get("ci_low", NAN), heldout.get("ci_high", NAN)],
+ "heldout_n": heldout.get("n", 0),
+ "final_mean_margin": heldout.get("mean_margin", NAN),
+ "tripwire": dict(tripwire) if tripwire else {"fired": False},
+ "tripwire_alarms": list(summary.get("tripwire_alarms", [])),
+ "trunk_report": dict(summary.get("trunk_report", {})),
+ "checkpoints_written": list(summary.get("checkpoints_written", [])),
+ "banner": dict(banner),
+ }
+
+
+def write_arm_result(path: str, manifest: Mapping) -> str:
+ """Write the arm manifest: rank 0 only, atomically.
+
+ COLLECTIVE (every rank calls it; the closing barrier keeps ranks together).
+ Run on all four torchrun ranks with a bare `open(path, "w")`, the ranks
+ truncated and wrote one path within a millisecond, with rank-local content
+ (`checkpoint` is "" off rank 0): the recorded Arm B manifest is
+ unparseable. Section-13 reads this file to select an arm, so it is written
+ once, via tmp + os.replace, and rank 0's `checkpoint` is the real one.
+ """
+ from ..utils.dist import barrier, is_rank0
+ from ..utils.guard import assert_paths_off_root
+ from .ref_cache import _atomic_write
+
+ assert_paths_off_root([os.path.dirname(os.path.abspath(path))])
+ if is_rank0():
+
+ def _dump(tmp: str) -> None:
+ with open(tmp, "w") as f:
+ # default=str: the manifest is assembled by the caller from eval
+ # output and this writes an ARM'S RESULT after its full DPO run --
+ # the exact shape (harness-measured values serialized at the end)
+ # that killed SFT attempt 6 with a Tensor in a gate receipt.
+ json.dump(manifest, f, indent=2, default=str)
+ f.flush()
+ os.fsync(f.fileno())
+
+ _atomic_write(path, _dump)
+ logger.info("arm result manifest -> %s", path)
+ barrier()
+ return path
+
+
+# ---------------------------------------------------------------------------
+# Trainer-facing batch + loss adapter
+# ---------------------------------------------------------------------------
+
+
+class DpoBatch:
+ """A `Preference` batch, iterable over its pair ids.
+
+ Section-07's `Trainer` iterates the object it is handed and logs
+ `list(batch)` as that step's identity -- its own tests pass samplers that
+ yield row-index lists, for which `list(batch)` is exactly right. A
+ `Preference` dataclass is not iterable, so a bare one would raise
+ TypeError at the first log step.
+
+ Wrapping supplies the identity the log wants, and supplies a better one
+ than row indices: which PAIRS a step consumed. Row indices are meaningless
+ across a reshuffle or a rebuild; pair ids survive both, so a suspicious
+ step stays reproducible afterwards.
+ """
+
+ __slots__ = ("preference", "pair_ids")
+
+ def __init__(self, preference):
+ self.preference = preference
+ self.pair_ids = [(m or {}).get("pair_id") for m in preference.metadata]
+
+ def __iter__(self):
+ return iter(self.pair_ids)
+
+ def __len__(self) -> int:
+ return len(self.pair_ids)
+
+ def to(self, device) -> "DpoBatch":
+ return DpoBatch(self.preference.to(device))
+
+
+class PreferenceBatchSource:
+ """What section-07's `Trainer` iterates: `set_epoch` / `__iter__` / `__len__`.
+
+ The Trainer walks a batch source directly rather than a `DataLoader`, and
+ that is a deliberate section-07 choice: batch order becomes a pure
+ function of (seed, epoch), which is what makes the seeded-resume contract
+ checkable. It also means there are no dataloader worker processes in this
+ loop at all -- the `<=2 workers per rank` rule that the /dev/shm SIGBUS
+ incident produced has nothing to bind to here, because nothing is forked.
+ Collation happens inline, in the training process.
+
+ That is affordable for DPO specifically: pair shards are memory-mapped and
+ a micro-batch is two short sequences, so collation is microseconds against
+ a 7B forward. It would not be affordable for a tokenization-heavy loop.
+ """
+
+ def __init__(self, rows: Sequence[Any], sampler, pad_token_id: int):
+ self.rows = list(rows)
+ self.sampler = sampler
+ self.pad_token_id = int(pad_token_id)
+
+ def set_epoch(self, epoch: int) -> None:
+ self.sampler.set_epoch(epoch)
+
+ def __len__(self) -> int:
+ return len(self.sampler)
+
+ def __iter__(self):
+ from ..data.loader import collate_preference
+
+ for indices in self.sampler:
+ yield DpoBatch(
+ collate_preference([self.rows[i] for i in indices], pad_token_id=self.pad_token_id)
+ )
+
+
+def make_preference_sampler(
+ rows, *, max_tokens: int, max_rows: int, seed: int = 0, rank=None, world_size=None
+):
+ """Section-03's length-grouped sampler, RANK-SHARDED.
+
+ THE UNSHARDED-SAMPLER INCIDENT (~24 GPU-hours on Arm A). This used to
+ return the bare `LengthGroupedSampler`, so every rank walked the same
+ batch list in the same order and all four trained the SAME pair every
+ micro-step. The effective global batch was micro x accum (8), not
+ micro x accum x world (32) as pre-registered; one "epoch" therefore
+ consumed a quarter of the corpus; the horizon covered half an epoch; and
+ past it the cosine schedule clamps to lr=0, so the run kept stepping on
+ frozen weights for 287 more steps. Nothing failed -- the loss curve was
+ plausible and the manifest reported the intended batch size throughout.
+
+ SHARDING THE BATCHES, NOT THE ROWS, and the choice matters twice over:
+
+ * `PreferenceBatchSource` indexes the FULL row list with whatever
+ indices the sampler yields, so slicing batches keeps every index
+ valid and needs no remapping -- one seam instead of two that can
+ disagree.
+ * `DistributedBatchSampler` truncates the batch list to a multiple of
+ world_size so EVERY RANK RUNS THE SAME NUMBER OF STEPS. A rank that
+ finished early would leave its peers blocked inside a collective,
+ which surfaces hours later as an NCCL timeout rather than as an error
+ where it happened. Row-slicing (`rows[rank::world]` before batching)
+ cannot promise this: the sampler is token-budgeted, so different row
+ slices yield different batch counts.
+
+ The cost is a bounded tail: up to (world_size - 1) batches are dropped per
+ epoch. Section-03 chose that over padding with repeats, which would
+ double-count rows and corrupt the epoch semantics the horizon is derived
+ from.
+
+ `rank`/`world_size` default to the live environment but are explicit
+ parameters so four ranks can be simulated in one CPU process -- which is
+ how this contract is tested, since the incident is a property of the
+ relationship BETWEEN ranks and no single-process run can see it.
+ """
+ from ..data.loader import (
+ DistributedBatchSampler,
+ LengthGroupedSampler,
+ preference_length_key,
+ )
+ from ..utils.dist import rank as env_rank
+ from ..utils.dist import world_size as env_world_size
+
+ lengths = [preference_length_key(r) for r in rows]
+ base = LengthGroupedSampler(lengths, max_tokens=max_tokens, max_rows=max_rows, seed=seed)
+
+ r = env_rank() if rank is None else int(rank)
+ ws = env_world_size() if world_size is None else int(world_size)
+ if ws <= 1:
+ return base
+ if not 0 <= r < ws:
+ raise ValueError(f"rank {r} is outside world_size {ws}")
+ return DistributedBatchSampler(base, rank=r, world_size=ws)
+
+
+def _check_global_batch(
+ global_batch: int, world_size: int, micro_batch_size: int, grad_accum: int
+) -> int:
+ """The arithmetic behind both launch refusals; returns the live global batch."""
+ live = world_size * micro_batch_size * grad_accum
+ if global_batch != live:
+ raise RuntimeError(
+ f"global_batch={global_batch} (the pre-registered value the banner "
+ f"and horizon are quoted in) != world_size {world_size} x micro "
+ f"{micro_batch_size} x grad_accum {grad_accum} = {live}. The run "
+ "would consume a different number of pairs per optimizer step than "
+ "it reports. Launch with the pre-registered rank count or change "
+ "the config deliberately."
+ )
+ return live
+
+
+def live_world_size() -> int:
+ """The data-parallel width this process is ACTUALLY part of.
+
+ The initialised process group wins over the environment: `WORLD_SIZE` is
+ what torchrun exported, the group is what the collectives will average
+ over, and a launch decision belongs to the latter. Falls back to the
+ environment before `init_process_group` (and to 1 outside torchrun).
+ """
+ import torch.distributed as dist
+
+ if dist.is_available() and dist.is_initialized():
+ return int(dist.get_world_size())
+ from ..utils.dist import world_size
+
+ return int(world_size())
+
+
+def assert_launch_topology(cfg: "DpoStageConfig", *, world_size: Optional[int] = None) -> dict:
+ """LAUNCH REFUSAL: world_size x micro x grad_accum must equal the
+ pre-registered `global_batch` (2026-10-12 review).
+
+ Nothing used to check it. `global_batch` is a config number that the
+ banner, `optimizer_steps()` and the arm manifest all quote, while what a
+ run really consumes per optimizer step is `world_size * micro_batch_size *
+ grad_accum`. A 2-rank launch of the 4-rank pre-registration therefore ran
+ 16 pairs per step under a banner that said 32 and a quoted horizon of 146
+ (really 290): the experiment recorded as one thing and executed as
+ another, with every number finite. `assert_dpo_horizon_consistent`
+ carries the same check but sits AFTER the 14.5GB model load and was never
+ passed the pre-registered value by the launcher; this one runs before any
+ gate, in milliseconds, fed by the live topology.
+
+ `world_size=None` reads it from the live process group (see
+ `live_world_size`); tests and tools pass it explicitly.
+ """
+ ws = live_world_size() if world_size is None else int(world_size)
+ if ws < 1:
+ raise ValueError(f"world_size={ws} must be >= 1")
+ live = _check_global_batch(cfg.global_batch, ws, cfg.micro_batch_size, cfg.grad_accum)
+ return {
+ "world_size": ws,
+ "micro_batch_size": cfg.micro_batch_size,
+ "grad_accum": cfg.grad_accum,
+ "global_batch": live,
+ }
+
+
+def expected_ref_cache_key(cfg: "DpoStageConfig", manifest: Mapping, sft_checkpoint: str):
+ """The key this run requires of its reference caches.
+
+ Five components are EXPECTATIONS built from this run's own facts (content
+ hash of the SFT weights, tokenizer, shard manifest, max_length, truncation)
+ and one is a RECORD: `forward_impl`, how the cache's own pass ran. The
+ record is read from the cache itself, never from the path-bias report.
+
+ WHY NOT THE REPORT (2026-10-12 review). The launcher used to take
+ forward_impl from the report's `cache_forward_impl`, which
+ precompute_ref_logprobs wrote as "fsdp2_sharded" whenever `--path-bias`
+ was passed -- but a `--path-bias` run does NOT recompute the caches, so the
+ field described the measuring run, not the cache. The documented happy
+ path (single-GPU precompute, then `--path-bias`, decision fired=False)
+ was refused at the cache gate: "Mismatched: forward_impl
+ (cache='single_gpu', run='fsdp2_sharded')". The rule that a FIRED decision
+ must be honoured by a sharded cache is enforced where it belongs, by
+ `dpo_banner`; here the held-out cache must agree with the train cache's
+ tag, and everything else is asserted by `load_ref_cache(expect=...)`.
+ """
+ from .ref_cache import build_ref_cache_key, peek_ref_cache_key
+
+ cache_key = peek_ref_cache_key(cfg.ref_cache_path)
+ forward_impl = cache_key.forward_impl if cache_key is not None else "single_gpu"
+ return build_ref_cache_key(sft_checkpoint, dict(manifest), forward_impl=forward_impl)
+
+
+def load_run_ref_caches(cfg: "DpoStageConfig", manifest: Mapping, sft_checkpoint: str):
+ """Load (train, held-out) reference caches under `expected_ref_cache_key`.
+
+ Both caches are loaded with `expect=`, so a missing file, a stale
+ checkpoint, a rebuilt shard set, a different tokenizer/max_length, or a
+ held-out cache computed through a different forward than the train cache
+ all raise here, at the gate, naming the guilty component.
+ """
+ from .ref_cache import load_ref_cache
+
+ key = expected_ref_cache_key(cfg, manifest, sft_checkpoint)
+ train = load_ref_cache(cfg.ref_cache_path, expect=key)
+ held = load_ref_cache(cfg.heldout_ref_cache_path, expect=key)
+ return train, held
+
+
+def assert_dpo_horizon_consistent(
+ scheduler_total_steps: int,
+ *,
+ n_pairs: int,
+ world_size: int,
+ micro_batch_size: int,
+ grad_accum: int,
+ epochs: int,
+ slack: Optional[int] = None,
+ global_batch: Optional[int] = None,
+) -> dict:
+ """Refuse to launch when the LR horizon disagrees with the corpus.
+
+ `global_batch`, when given, is the PRE-REGISTERED number the banner and the
+ horizon formula (`optimizer_steps`) are quoted in; it must equal what the
+ live topology actually consumes per optimizer step,
+ `world_size * micro_batch_size * grad_accum`. Without this the banner can
+ say global_batch 32 / horizon 146 while a 2-rank launch really runs 16 /
+ 290: the scheduler-vs-corpus check below passes (it uses the live world
+ size) and the banner is simply wrong about what ran.
+
+ THE ZOMBIE-RUN REFUSAL. Arm A ran a 146-step horizon over a corpus needing
+ ~289 per-rank steps, so the cosine finished at half an epoch; past it
+ `WarmupCosineSchedule` clamps to exactly 0 (min_lr_frac=0) and the loop
+ kept stepping on frozen weights for 287 more steps. The horizon is a
+ PRE-REGISTERED quantity: if it and the arithmetic disagree, one of them is
+ wrong and no amount of training will reconcile them.
+
+ Computed from the SHARDED per-rank row count, because that is what a rank
+ actually walks. `slack` absorbs the token-budgeted sampler landing a batch
+ or two either side of the row-count arithmetic; it does not absorb a
+ factor.
+ """
+ import math as _math
+
+ if min(n_pairs, world_size, micro_batch_size, grad_accum, epochs) < 1:
+ raise ValueError("every horizon input must be >= 1")
+
+ if global_batch is not None:
+ _check_global_batch(global_batch, world_size, micro_batch_size, grad_accum)
+ per_rank_rows = _math.ceil(n_pairs / world_size)
+ rows_per_step = micro_batch_size * grad_accum
+ expected = epochs * _math.ceil(per_rank_rows / rows_per_step)
+ if slack is None:
+ # PROPORTIONAL slack: the token-budgeted sampler legitimately cuts
+ # batches short of max_rows, so the sampler-true horizon runs a few
+ # PERCENT over the row arithmetic (Arm B: 728 vs 708, 2.8%, which a
+ # fixed slack of 2 wrongly refused). 5% absorbs batch-shortfall while
+ # still catching the zombie class (Arm A was 2.1x, far outside).
+ slack = max(2, _math.ceil(0.05 * expected))
+ report = {
+ "scheduler_total_steps": int(scheduler_total_steps),
+ "expected_steps": expected,
+ "per_rank_rows": per_rank_rows,
+ "rows_per_optimizer_step_per_rank": rows_per_step,
+ "unique_rows_per_optimizer_step": rows_per_step * world_size,
+ "epochs_covered": (scheduler_total_steps / expected * epochs if expected else float("nan")),
+ }
+ if abs(int(scheduler_total_steps) - expected) > slack:
+ covered = report["epochs_covered"]
+ raise RuntimeError(
+ f"DPO horizon is inconsistent with the corpus: the scheduler will "
+ f"run {scheduler_total_steps} optimizer steps but "
+ f"{n_pairs} pairs / {world_size} ranks = {per_rank_rows} rows per "
+ f"rank at {rows_per_step} rows per step needs {expected} "
+ f"(epochs={epochs}, slack={slack}). That horizon covers "
+ f"{covered:.2f} epoch(s).\n\nA horizon shorter than the data is "
+ "not a shorter run -- past it the cosine schedule clamps the "
+ "learning rate to exactly 0 and the loop keeps stepping on frozen "
+ "weights, which is how Arm A spent ~24 GPU-hours training "
+ "nothing. Fix the horizon or the corpus arithmetic before "
+ "launching."
+ )
+ return report
+
+
+#: The canonical name for the implicit reward margin -- section-05's kernel
+#: emits it and section-07's Trainer reads it. Imported from the Trainer
+#: rather than spelled again here: a second copy of the string, asserting what
+#: the Trainer reads, is precisely the drift that disarmed the tripwire the
+#: first time.
+from .trainer import MARGIN_KEY as KERNEL_MARGIN_KEY # noqa: E402
+
+
+def _arm_tripwire_metrics(metrics: dict, preference, window=None) -> dict:
+ """Assert this adapter supplies what the armed tripwires consume.
+
+ THE DISARMED-TRIPWIRE INCIDENT, in its final form. Section-05's kernel
+ emits the implicit margin as `rewards/margins`; section-07's Trainer used
+ to seed a DIFFERENT key, `margin`, with NaN and feed the tripwire from
+ that. Nothing produced it, `_slope` returns NaN for any window containing
+ NaN, and the trip condition requires a non-NaN slope -- so the
+ chosen-likelihood-collapse tripwire, the one with a pre-registered
+ response and the one maximal for Arm B by design, could never fire while
+ appearing fully wired.
+
+ The cure is section-07's, not this module's: the Trainer now reads
+ `MARGIN_KEY` directly, tripwires declare `INPUT_KEYS`, and
+ `assert_tripwires_armed` halts at the first log step if an armed guard's
+ inputs are missing or NaN. This function no longer bridges names -- an
+ alias here would write a key nothing reads.
+
+ What remains is a PRESENCE check at the seam, which is still worth its
+ line: it catches a section-05 rename here, one function call after the
+ kernel returns, rather than at the first log step with the metric already
+ gone. `chosen_longer_fraction` is supplied for the same reason -- the
+ length-drift tripwire's only input, and this adapter is the only thing
+ that can compute it.
+ """
+ if KERNEL_MARGIN_KEY not in metrics:
+ raise KeyError(
+ f"the DPO kernel did not emit {KERNEL_MARGIN_KEY!r} (present: "
+ f"{sorted(metrics)}). That is the canonical margin key the "
+ "chosen-logprob tripwire consumes; without it the armed tripwire "
+ "self-test halts the run, and this raise names the cause one call "
+ "after the kernel returned instead of at the first log step."
+ )
+ if window is not None:
+ window.extend(_chosen_longer_flags(preference))
+ metrics["chosen_longer_fraction"] = sum(window) / len(window) if window else NAN
+ metrics["chosen_longer_pairs"] = float(len(window))
+ metrics.setdefault("chosen_longer_fraction", preference.stats()["chosen_longer_fraction"])
+ metrics.setdefault("chosen_longer_fraction_batch", preference.stats()["chosen_longer_fraction"])
+ return metrics
+
+
+def _chosen_longer_flags(preference) -> list[int]:
+ """Per-pair `chosen completion is longer`, skipping pairs with no
+ completion on either side.
+
+ Per PAIR, because the length-drift tripwire's input has to be a fraction
+ over a sample whose size we control; the batch-level statistic is that
+ same fraction over whatever happened to be in the micro-batch, which at
+ micro_batch 2 is three possible values.
+
+ Completion tokens, not sequence tokens: Arm B's two sides share their
+ entire prefix, so a sequence-length comparison is dominated by a term
+ identical on both sides and would compress the fraction toward 0.5
+ regardless of the data -- hiding exactly the drift being watched for.
+ """
+ c = preference.chosen_completion_mask.sum(dim=1)
+ r = preference.rejected_completion_mask.sum(dim=1)
+ return [int(ci > ri) for ci, ri in zip(c.tolist(), r.tolist()) if (ci + ri) > 0]
+
+
+def make_dpo_stage_adapter(
+ cache, cfg: Optional[DpoStageConfig] = None, require_fused: bool = False
+):
+ """Trainer loss adapter: attach cached refs by pair_id, then delegate.
+
+ Bridges three things section-05 and section-03 do not agree on, and does
+ it in one place so the disagreement cannot be re-resolved differently
+ somewhere else:
+
+ * NAMES. Section-03's `Preference` carries `chosen_input_ids` /
+ `ref_chosen_logps`; section-05's adapter unpacks `input_ids_chosen` /
+ `ref_logp_chosen`. Neither is wrong; they were written to different
+ drafts.
+ * LABELS. `Preference` carries a completion MASK, `fused_dpo` wants
+ labels with IGNORE_INDEX outside the completion. The conversion is one
+ `masked_fill`, and getting it backwards would score the prompt -- which
+ cancels in the DPO difference while inflating both magnitudes.
+ * REFERENCES. They come from the cache, joined by pair_id, never from
+ batch order and never defaulted.
+
+ The reference tensors enter as plain constants; section-05's kernel
+ detaches them and proves no gradient path survives. This adapter's only
+ obligation is never to build one, which it discharges by reading them out
+ of a dict of floats.
+ """
+ from ..kernels.fused_dpo import IGNORE_INDEX
+ from .dpo_loss import DpoLossConfig, make_dpo_loss_adapter
+ from .ref_cache import attach_refs
+
+ stage = cfg or DpoStageConfig()
+ #: Rolling per-PAIR history feeding the length-drift tripwire. Lives on
+ #: the adapter, not on the batch: the statistic the tripwire needs spans
+ #: batches, and computing it per batch is what makes it unusable.
+ length_window = collections.deque(maxlen=stage.length_window_pairs)
+ inner = make_dpo_loss_adapter(
+ DpoLossConfig(
+ beta=stage.beta,
+ variant=stage.variant,
+ nll_coeff=stage.nll_coeff,
+ dpop_lambda=stage.dpop_lambda,
+ ),
+ require_fused=require_fused,
+ # One trunk graph at a time: both sides' checkpointed 7B graphs plus
+ # fp32 shard grads measured 21.5GB and OOM'd (batch-size independent).
+ decoupled_backward=require_fused,
+ grad_scale=1.0 / max(1, stage.grad_accum),
+ )
+
+ def adapter(model, batch):
+ import torch
+
+ pref = batch.preference if isinstance(batch, DpoBatch) else batch
+ pref = attach_refs(pref, cache)
+ # The WHOLE batch to the compute device, refs included. The model
+ # forward moves input_ids internally, which let CPU labels survive
+ # to the loss's gather ("index is on cpu") on the stage's first
+ # real step -- the SFT script moves its collated batch explicitly,
+ # and this adapter is the DPO stage's equivalent place.
+ #
+ # Follow the MODEL's device, not `cuda.is_available()`: on a box with
+ # visible GPUs a CPU-resident model (the unit-test tiny model) would
+ # otherwise get CUDA batches and die inside the embedding. Under FSDP2
+ # the parameters are DTensors on this rank's cuda device, so the real
+ # path is unchanged.
+ param = next(iter(model.parameters()), None)
+ if param is not None and param.device.type == "cuda":
+ pref = pref.to(torch.device("cuda", torch.cuda.current_device()))
+ elif param is None and torch.cuda.is_available():
+ pref = pref.to(torch.device("cuda", torch.cuda.current_device()))
+ bound = {
+ "input_ids_chosen": pref.chosen_input_ids,
+ "labels_chosen": pref.chosen_input_ids.masked_fill(
+ pref.chosen_completion_mask == 0, IGNORE_INDEX
+ ),
+ "input_ids_rejected": pref.rejected_input_ids,
+ "labels_rejected": pref.rejected_input_ids.masked_fill(
+ pref.rejected_completion_mask == 0, IGNORE_INDEX
+ ),
+ "ref_logp_chosen": pref.ref_chosen_logps,
+ "ref_logp_rejected": pref.ref_rejected_logps,
+ }
+ out = inner(model, bound)
+ _arm_tripwire_metrics(out.metrics, pref, window=length_window)
+ return out
+
+ adapter.uses_fused = getattr(inner, "uses_fused", True)
+ adapter.config = inner.config
+ adapter.stage_config = stage
+ return adapter
diff --git a/code/align/train/fsdp.py b/code/align/train/fsdp.py
new file mode 100644
index 0000000000000000000000000000000000000000..5c65a3f7d05ab416a814e3037c4dc216526d6c42
--- /dev/null
+++ b/code/align/train/fsdp.py
@@ -0,0 +1,124 @@
+"""FSDP2 wrapping and activation checkpointing.
+
+Called from the LAUNCH SCRIPTS, never from the Trainer: the Trainer receives an
+already-wrapped model, which is what keeps it identical between the SFT and DPO
+stages and testable on CPU without a process group.
+
+Two policies here are not configurable, deliberately:
+
+ * `use_reentrant=False`, hard-asserted. Reentrant activation checkpointing
+ silently ZEROES trunk gradients under sharding. The run trains -- loss
+ falls, throughput is normal -- while only the embeddings and the LM head
+ move. There is no knob to turn it back on, because the only reason anyone
+ would set it is not knowing this. The trunk-movement probe in `probes.py`
+ is the runtime backstop; this is the compile-time one.
+ * No `CPUOffloadPolicy`. FSDP's own CPU offload pays per-layer transfers on
+ every forward AND backward; our offload is optimizer-state-only and
+ happens once per optimizer step (see `offload_adamw.py`). Turning both on
+ would move the same bytes twice.
+
+Sharding is PER TRANSFORMER BLOCK, then the root. Per-block units are what
+make the all-gather granularity match the compute granularity: one unit for
+the whole model would gather all 7.25B parameters before the first matmul.
+"""
+
+from __future__ import annotations
+
+import functools
+import logging
+from typing import Any, Optional
+
+import torch
+
+logger = logging.getLogger(__name__)
+
+#: bf16 compute, fp32 reduction. bf16 is mandatory (fp16 overflows the
+#: 32768-vocab logits); the fp32 REDUCE matters independently -- gradient
+#: all-reduce in bf16 loses small updates across 4 ranks precisely when they
+#: are averaged, which is every step.
+PARAM_DTYPE = torch.bfloat16
+REDUCE_DTYPE = torch.float32
+
+
+def transformer_blocks(model) -> list:
+ """The per-block FSDP units: every `DecoderBlock` in the stack.
+
+ Found structurally rather than by isinstance so this module does not
+ import the model (and so a wrapped/compiled model still works).
+ """
+ inner = getattr(model, "model", model)
+ layers = getattr(inner, "layers", None)
+ if layers is None:
+ raise ValueError(
+ "no `.layers` found on the model: FSDP2 here shards per "
+ "transformer block, and a model with no block list would be "
+ "wrapped as ONE unit -- gathering all 7.25B parameters before the "
+ "first matmul, which does not fit on a 23GB card."
+ )
+ return list(layers)
+
+
+def mixed_precision_policy():
+ """`MixedPrecisionPolicy(param_dtype=bf16, reduce_dtype=fp32)`."""
+ from torch.distributed.fsdp import MixedPrecisionPolicy
+
+ return MixedPrecisionPolicy(param_dtype=PARAM_DTYPE, reduce_dtype=REDUCE_DTYPE)
+
+
+def apply_fsdp(model, mesh: Optional[Any] = None, reshard_after_forward: bool = True):
+ """Shard per transformer block, then the root.
+
+ Order is load-bearing: the root must be sharded LAST, after its children
+ are already units, or the root unit would own their parameters too.
+ """
+ from torch.distributed.fsdp import fully_shard
+
+ policy = mixed_precision_policy()
+ blocks = transformer_blocks(model)
+ for block in blocks:
+ fully_shard(block, mesh=mesh, mp_policy=policy, reshard_after_forward=reshard_after_forward)
+ fully_shard(model, mesh=mesh, mp_policy=policy, reshard_after_forward=reshard_after_forward)
+ logger.info(
+ "FSDP2: %d per-block units + root, param=%s reduce=%s",
+ len(blocks),
+ PARAM_DTYPE,
+ REDUCE_DTYPE,
+ )
+ return model
+
+
+def _checkpointed_forward(block, *args, **kwargs):
+ from torch.utils.checkpoint import checkpoint
+
+ return checkpoint(block._nmo_inner_forward, *args, use_reentrant=False, **kwargs)
+
+
+def apply_activation_checkpointing(model, use_reentrant: bool = False):
+ """Wrap every transformer block in non-reentrant activation checkpointing.
+
+ Patches each block's `forward` rather than wrapping the block in a new
+ module: a wrapper module would insert a level into the module tree and
+ change every parameter name, which would silently break the HF checkpoint
+ mapping and the interp pillar that loads through it.
+ """
+ if use_reentrant:
+ raise ValueError(
+ "use_reentrant=True is refused. Reentrant activation checkpointing "
+ "silently ZEROES trunk gradients under sharding: the run trains "
+ "the embeddings and the LM head, the loss falls, throughput looks "
+ "normal, and the body of the model never moves. This is a "
+ "documented predecessor failure and there is no configuration in "
+ "which we want it."
+ )
+ blocks = transformer_blocks(model)
+ for block in blocks:
+ if hasattr(block, "_nmo_inner_forward"):
+ continue # idempotent: never double-wrap
+ block._nmo_inner_forward = block.forward
+ block.forward = functools.partial(_checkpointed_forward, block)
+ logger.info("activation checkpointing on %d blocks (use_reentrant=False)", len(blocks))
+ return model
+
+
+def is_checkpointed(block) -> bool:
+ return hasattr(block, "_nmo_inner_forward")
diff --git a/code/align/train/lora_scale.py b/code/align/train/lora_scale.py
new file mode 100644
index 0000000000000000000000000000000000000000..7409f8d6055448bbf8d7240d1f4754afd5685f55
--- /dev/null
+++ b/code/align/train/lora_scale.py
@@ -0,0 +1,110 @@
+"""LoRA scale sweep WITHOUT retraining (SOTA plan K4).
+
+A LoRA layer computes `W x + s * B A x` with s = alpha / r. Evaluating the SAME trained adapter at a
+multiple `k` of that delta (k in {0.5, 0.7, 1.0}: "alpha curve") needs no optimisation: set s -> k*s.
+k = 1 is the trained model, k = 0 is the base, and the curve between them tells whether a regression
+is "too much adapter" (a smaller k recovers it) or "the wrong direction" (it does not).
+
+Two entry points, matching the two ways an adapter is evaluated:
+
+ * `scaled_adapter(model, adapter, k)` -- a context manager over a LIVE model with the adapter attached
+ (the un-merged serving option); `sweep(...)` runs an eval callable at each k.
+ * `merged_state_dict_at_scale(model, full_state, adapter, k)` -- a BASE-keyed merged state dict at
+ scale k, for merged exports, with exactly the bf16-cast fidelity report of
+ `align.ppo.lora.export_merged_state_dict` (the delta is k times smaller, so MORE of it can fall into
+ the bf16 dead zone: read `report["cosine"]` before trusting a small-k merged eval).
+
+Refusals: a layer that has the adapter MERGED into its weight cannot be rescaled (the delta is already
+inside W); the context manager raises instead of evaluating a polluted point. The model is never left
+rescaled: the original scaling is restored in `finally`, on error and on KeyboardInterrupt alike.
+"""
+
+from __future__ import annotations
+
+import contextlib
+import math
+from collections.abc import Callable, Iterable, Iterator
+from typing import Any, Optional
+
+import torch
+import torch.nn as nn
+
+from ..ppo.lora import export_merged_state_dict, lora_modules
+
+#: The plan's K4 grid: fractions of the TRAINED adapter delta.
+DEFAULT_SCALES: tuple[float, ...] = (0.5, 0.7, 1.0)
+
+
+def _check_scale(k: float) -> float:
+ k = float(k)
+ if not math.isfinite(k) or k < 0.0:
+ raise ValueError(f"LoRA delta scale must be a finite number >= 0, got {k!r}")
+ return k
+
+
+@contextlib.contextmanager
+def scaled_adapter(model: nn.Module, adapter: str, k: float) -> Iterator[int]:
+ """Within the block, every layer's `adapter` contributes `k` times its trained delta.
+
+ Yields the number of layers rescaled. Raises (before changing anything) if no layer carries the
+ adapter or if any layer has it merged.
+ """
+ k = _check_scale(k)
+ layers = [(p, m) for p, m in lora_modules(model) if adapter in m.adapters]
+ if not layers:
+ raise KeyError(f"no layer carries adapter {adapter!r}; nothing to scale")
+ merged = [p for p, m in layers if m._merged is not None]
+ if merged:
+ raise RuntimeError(
+ f"adapter {adapter!r} is merged into {len(merged)} layer(s) (first: {merged[0]}); the delta is "
+ "already inside the weight and cannot be rescaled. Unmerge first, or use "
+ "merged_state_dict_at_scale on the unmerged model."
+ )
+ original = [(m.adapters[adapter], m.adapters[adapter].scaling) for _, m in layers]
+ try:
+ for ad, s in original:
+ ad.scaling = s * k
+ yield len(layers)
+ finally:
+ for ad, s in original:
+ ad.scaling = s
+
+
+def sweep(
+ model: nn.Module,
+ adapter: str,
+ eval_fn: Callable[[nn.Module], Any],
+ scales: Iterable[float] = DEFAULT_SCALES,
+) -> dict[float, Any]:
+ """`{k: eval_fn(model)}` with the adapter delta scaled to k each time. `eval_fn` sees the live model."""
+ out: dict[float, Any] = {}
+ for k in scales:
+ k = _check_scale(k)
+ if k in out:
+ raise ValueError(f"duplicate scale {k} in the sweep")
+ with scaled_adapter(model, adapter, k):
+ out[k] = eval_fn(model)
+ return out
+
+
+def merged_state_dict_at_scale(
+ model: nn.Module,
+ full_state: dict[str, torch.Tensor],
+ adapter: str,
+ k: float,
+ report: Optional[dict] = None,
+ min_cosine: Optional[float] = None,
+) -> dict[str, torch.Tensor]:
+ """`export_merged_state_dict` with the adapter delta scaled by `k` (the model is not mutated).
+
+ k = 1.0 is bit-identical to `export_merged_state_dict`. k = 0 returns the base weights (every nonzero
+ delta is zero, so `report` has cosine 1.0 and delta_norm 0). `report["scale"]` records k.
+ """
+ k = _check_scale(k)
+ rep: dict = {}
+ with scaled_adapter(model, adapter, k):
+ out = export_merged_state_dict(model, full_state, adapter, report=rep, min_cosine=min_cosine)
+ rep["scale"] = k
+ if report is not None:
+ report.update(rep)
+ return out
diff --git a/code/align/train/moe_module.py b/code/align/train/moe_module.py
new file mode 100644
index 0000000000000000000000000000000000000000..47d60891564061dc10145bc7b21275c46a952cfb
--- /dev/null
+++ b/code/align/train/moe_module.py
@@ -0,0 +1,691 @@
+"""MoE auxiliary losses on the training seam: `MoETrainModule`.
+
+The model (`align.model.olmoe`) adds NO aux loss: it RETURNS per-layer router
+statistics (`RouterStats`) when asked. This module is where those become a
+training objective, because only the TrainModule sees all three things the
+objective depends on: the loss adapter's CE (an opaque scalar), the
+gradient-accumulation window (`grad_accum`), and the data-parallel group.
+
+THE MATH, and the one approximation in it
+-----------------------------------------
+Switch/OLMoE load balance, per layer: `E * sum_i f_i * P_i` with `f_i` the hard
+top-k hit fraction (no gradient) and `P_i` the mean router probability
+(gradient). Which batch `f_i` and `P_i` are measured over is the whole question
+(Qiu et al., "Demons in the Detail"): per-micro-batch statistics over-constrain
+the router when micro-batches are domain-homogeneous, because a sequence of
+code must be allowed to use the code experts heavily. The loss should balance
+over the GLOBAL batch -- every rank, every micro-batch of the optimizer step.
+
+Exact global `f_i` is not available at micro-batch `m`: the window's later
+micro-batches have not been run, and their backward cannot be deferred without
+retaining every micro-batch's activations. Demons' Algorithm 1 resolves it with
+a BUFFER: keep the window's running (all-reduced) counts, and take `f_i` from
+the buffer as it stands when micro-batch `m` runs. The approximation is
+therefore confined to `f_i` of micro-batches 1..accum-1; at the LAST micro-batch
+`f_i` is exactly the global-batch fraction, and the logged `moe/lb_loss` (computed
+from the full buffer at the optimizer-step boundary) is exact.
+
+SCALING (why `world * m`). The Trainer divides each micro loss by `grad_accum`
+and the gradient reduction averages over `world` ranks, so micro-batch `m` on
+rank `r` reaches the weights with weight `1/(world*accum)`. For the window's
+gradient to equal the gradient of ONE global loss
+`E * sum_i f_i * (sum_{r,m} prob_sum_{r,m,i}) / N`, each term must be multiplied
+by `world * accum / N_window`, and `N_window` (all valid tokens of the window) is
+unknown until the end; estimated as `n_buf * accum / m` it gives
+`world * m / n_buf`. At `m = accum` the estimate is exact. The same factor
+scales the z-loss. In "micro" scope (the per-rank formulation, kept for
+ablations) the factor is 1 and everything is local.
+
+WHAT IS ALL-REDUCED, and when. One packed float64 tensor per micro-batch
+[counts L*E | tokens L | prob_sum L*E | z_sum L | entropy_sum L]. DETACHED data
+only: the gradient of the loss is each rank differentiating its own
+`prob_sum`/`z_sum`, never a gradient crossing ranks here. Every rank calls the
+collective the same number of times (the Trainer runs micro-steps in lockstep),
+and the buffer is reset at `optim_step()`, which the Trainer calls on every
+boundary whether or not the optimizer applied the step.
+
+DEFAULTS. Load balance and z-loss are OFF for post-training stages and
+0.01 / 0.001 for `pretrain` / `proxy` (see `MoEAuxConfig`); telemetry
+(MaxVio, utilization, dead experts, router entropy) is logged either way.
+
+LIMITS (stated, not hidden)
+ * Adapters that run their own backward and return a detached proxy (the DPO
+ decoupled-backward adapter) cannot carry an aux loss: the router graph is
+ consumed by the adapter's backward. With a nonzero weight this raises; with
+ zero weights telemetry still works (it reads detached data).
+ * Not validated under FSDP2 on GPU (no GPU in this lane): the stats ride out
+ of the wrapped forward as plain dataclass fields rather than pytree leaves.
+ * The returned `LossOutput.loss` is the CE alone (comparable with dense runs).
+ If an aux term is non-finite the returned loss is NaN, so the Trainer's
+ non-finite-loss accounting sees it.
+
+EXPERT PARALLELISM (opt-in: `ep_groups`, default None = nothing below applies)
+-----------------------------------------------------------------------------
+`align.model.moe_ep` shards the experts across an EP group. The caller applies it
+(`apply_expert_parallel(model, groups)`) BEFORE the optimizer is built -- the
+optimizer must hold the shard parameters, and sharding after construction would
+leave it stepping tensors that are no longer in the model. This module then does
+the three things only the TrainModule can:
+ * the aux losses need NO change: the router is replicated, `RouterStats` come from
+ local tokens exactly as without EP, and `MoEAux` all-reduces them over the
+ DATA-parallel group (`group`), which contains the EP group.
+ * `optim_step` puts expert-shard gradients on the replicated-gradient scale
+ (`finalize_expert_grads`: they arrive SUMMED over the EP group) and clips by the
+ EP-GLOBAL norm (`clip_grads_`). The wrapped optimizer must therefore NOT clip
+ itself (`max_grad_norm` must be None): its norm would be shard-local, ranks would
+ clip by different coefficients and the replicated parameters would drift apart.
+ Pass the clip value as `ep_max_grad_norm`; `last_grad_norm` is the PRE-clip global.
+ * `full_model_state_dict` / `load_model_state_dict` gather / slice the expert shards
+ (a collective over the EP group), so a checkpoint is the same file a non-EP run writes.
+"""
+
+from __future__ import annotations
+
+import logging
+import math
+from dataclasses import dataclass
+from typing import Any, Optional
+
+import torch
+
+from ..config import MoEAuxConfig
+from ..model import moe
+from .train_module import LossAdapter, LossOutput, TrainModule
+
+logger = logging.getLogger(__name__)
+
+NAN = float("nan")
+
+#: (lb_weight, z_weight) per stage when the config leaves a weight as None.
+#: Stages that train a router FROM SCRATCH balance it; everything else leaves
+#: the pretrained router alone. See MoEAuxConfig for the argument.
+STAGE_AUX_DEFAULTS: dict[str, tuple[float, float]] = {
+ "pretrain": (0.01, 0.001),
+ "proxy": (0.01, 0.001),
+}
+POST_TRAINING_AUX_DEFAULT = (0.0, 0.0)
+LB_SCOPES = ("global", "micro")
+
+
+def resolve_aux_weights(aux: MoEAuxConfig, stage: str) -> tuple[float, float]:
+ """(lb_weight, z_weight) after applying the stage default to any `None`."""
+ d_lb, d_z = STAGE_AUX_DEFAULTS.get(stage, POST_TRAINING_AUX_DEFAULT)
+ lb = d_lb if aux.lb_weight is None else float(aux.lb_weight)
+ z = d_z if aux.z_weight is None else float(aux.z_weight)
+ for name, v in (("lb_weight", lb), ("z_weight", z)):
+ if not (math.isfinite(v) and v >= 0.0):
+ raise ValueError(f"moe_aux.{name}={v!r} must be a finite number >= 0")
+ if aux.lb_scope not in LB_SCOPES:
+ raise ValueError(f"moe_aux.lb_scope={aux.lb_scope!r}; known: {LB_SCOPES}")
+ return lb, z
+
+
+# ---------------------------------------------------------------------------
+# the window buffer (Demons Alg. 1)
+# ---------------------------------------------------------------------------
+
+
+@dataclass
+class WindowTotals:
+ """Detached sums over (ranks x micro-batches so far): everything the aux
+ losses and the telemetry need, nothing with a gradient."""
+
+ counts: torch.Tensor # [L, E] float64: top-k hits
+ n_tokens: torch.Tensor # [L] float64: valid tokens
+ prob_sum: torch.Tensor # [L, E] float64: sum of softmax probs
+ z_sum: torch.Tensor # [L] float64: sum of lse^2
+ entropy_sum: torch.Tensor # [L] float64
+
+ def __add__(self, o: "WindowTotals") -> "WindowTotals":
+ return WindowTotals(
+ self.counts + o.counts,
+ self.n_tokens + o.n_tokens,
+ self.prob_sum + o.prob_sum,
+ self.z_sum + o.z_sum,
+ self.entropy_sum + o.entropy_sum,
+ )
+
+ @classmethod
+ def of(cls, stats: list[moe.RouterStats]) -> "WindowTotals":
+ d = torch.float64
+ return cls(
+ counts=torch.stack([s.counts for s in stats]).to(d),
+ n_tokens=torch.stack([s.n_tokens for s in stats]).to(d),
+ prob_sum=torch.stack([s.prob_sum.detach() for s in stats]).to(d),
+ z_sum=torch.stack([s.z_sum.detach() for s in stats]).to(d),
+ entropy_sum=torch.stack([s.entropy_sum.detach() for s in stats]).to(d),
+ )
+
+ def all_reduce(self, group=None) -> "WindowTotals":
+ """Sum over a process group in ONE collective (packed)."""
+ import torch.distributed as dist
+
+ n_layers, e = self.counts.shape
+ packed = torch.cat(
+ [
+ self.counts.reshape(-1),
+ self.n_tokens,
+ self.prob_sum.reshape(-1),
+ self.z_sum,
+ self.entropy_sum,
+ ]
+ ).contiguous()
+ dist.all_reduce(packed, group=group)
+ le = n_layers * e
+ o = 0
+ counts = packed[o : o + le].reshape(n_layers, e)
+ o += le
+ n = packed[o : o + n_layers]
+ o += n_layers
+ ps = packed[o : o + le].reshape(n_layers, e)
+ o += le
+ z = packed[o : o + n_layers]
+ o += n_layers
+ ent = packed[o : o + n_layers]
+ return WindowTotals(counts, n, ps, z, ent)
+
+
+def merge_call_stats(calls: list[list[moe.RouterStats]]) -> list[moe.RouterStats]:
+ """Combine the stats of several grad-enabled forwards of ONE micro-batch.
+
+ One call (SFT, pretrain) is the identity. Several (a DPO adapter that runs
+ chosen and rejected separately) are summed per layer: they are different
+ token populations of the same optimizer step, so counts, probability sums,
+ z sums and token counts all ADD, and the losses are then over their union.
+ """
+ if not calls:
+ raise ValueError("no router stats captured")
+ if len(calls) == 1:
+ return calls[0]
+ n_layers = len(calls[0])
+ if any(len(c) != n_layers for c in calls):
+ raise ValueError(f"forwards returned different layer counts: {[len(c) for c in calls]}")
+ out = []
+ for layer in range(n_layers):
+ parts = [c[layer] for c in calls]
+ out.append(
+ moe.RouterStats(
+ counts=sum(p.counts for p in parts),
+ prob_sum=sum(p.prob_sum for p in parts),
+ n_tokens=sum(p.n_tokens for p in parts),
+ z_sum=sum(p.z_sum for p in parts),
+ entropy_sum=sum(p.entropy_sum for p in parts),
+ )
+ )
+ return out
+
+
+class MoEAux:
+ """The aux-loss math and the window buffer, independent of any Trainer.
+
+ `micro(stats)` is called once per micro-batch, in lockstep on every rank,
+ and returns `(lb_term, z_term)` ALREADY scaled for the Trainer's
+ `/grad_accum` and the gradient reduction's mean over ranks (see the module
+ docstring). `window_end()` returns the exact global-window telemetry and
+ resets the buffer.
+ """
+
+ def __init__(
+ self,
+ top_k: int,
+ grad_accum: int,
+ *,
+ scope: str = "global",
+ group=None,
+ world_size: Optional[int] = None,
+ ):
+ if scope not in LB_SCOPES:
+ raise ValueError(f"scope {scope!r}; known: {LB_SCOPES}")
+ if grad_accum < 1:
+ raise ValueError(f"grad_accum must be >= 1, got {grad_accum}")
+ self.top_k = int(top_k)
+ self.grad_accum = int(grad_accum)
+ self.scope = scope
+ self.group = group
+ self._world = world_size
+ self.m = 0 # micro-batches seen in the current window
+ self.buf: Optional[WindowTotals] = None
+
+ # -- world ----------------------------------------------------------
+ def world(self) -> int:
+ if self._world is not None:
+ return self._world
+ import torch.distributed as dist
+
+ if dist.is_available() and dist.is_initialized():
+ return dist.get_world_size(self.group)
+ return 1
+
+ def _distributed(self) -> bool:
+ import torch.distributed as dist
+
+ return dist.is_available() and dist.is_initialized() and self.world() > 1
+
+ def reset(self) -> None:
+ self.m = 0
+ self.buf = None
+
+ # -- one micro-batch --------------------------------------------------
+ def micro(
+ self, stats: list[moe.RouterStats], want_lb: bool = True, want_z: bool = True
+ ) -> tuple[Optional[torch.Tensor], Optional[torch.Tensor]]:
+ if self.m >= self.grad_accum:
+ raise RuntimeError(
+ f"{self.m + 1} micro-batches in one accumulation window (grad_accum="
+ f"{self.grad_accum}): optim_step()/reset() was not called at the boundary"
+ )
+ self.m += 1
+ local = WindowTotals.of(stats)
+ glob = local.all_reduce(self.group) if self._distributed() else local
+ self.buf = glob if self.buf is None else self.buf + glob
+ buf = self.buf
+ if not bool((buf.n_tokens > 0).all()):
+ raise ValueError(
+ "a MoE layer has seen zero valid tokens in this window: every position was masked. "
+ "The balance and z-losses are undefined over zero tokens (and returning 0.0 would "
+ "report a perfectly balanced router)."
+ )
+ lb = z = None
+ if self.scope == "global":
+ scale = float(self.world() * self.m)
+ if want_lb:
+ lb = scale * moe.load_balancing_loss(
+ stats,
+ self.top_k,
+ global_totals=moe.Totals(counts=buf.counts, n_tokens=buf.n_tokens),
+ )
+ if want_z:
+ # n_tokens is identical across layers (every layer routes the
+ # same valid tokens); the scalar is what router_z_loss takes.
+ z = scale * moe.router_z_loss(stats, loss_div_factor=buf.n_tokens[0])
+ else:
+ if want_lb:
+ lb = moe.load_balancing_loss(stats, self.top_k)
+ if want_z:
+ z = moe.router_z_loss(stats)
+ return lb, z
+
+ # -- the boundary -----------------------------------------------------
+ def window_values(self) -> dict[str, Any]:
+ """Exact global-window quantities (float64 tensors) from the buffer."""
+ buf = self.buf
+ if buf is None:
+ raise RuntimeError("window_values() before any micro-batch")
+ n_layers, e = buf.counts.shape
+ n = buf.n_tokens.unsqueeze(1)
+ f = buf.counts / (self.top_k * n)
+ p = buf.prob_sum / n
+ lb = (e * (f * p).sum(dim=1)).mean()
+ z = (buf.z_sum / buf.n_tokens).mean()
+ ent = buf.entropy_sum / buf.n_tokens
+ per_layer = [moe.expert_load_metrics(buf.counts[i]) for i in range(n_layers)]
+ return {
+ "lb": lb,
+ "z": z,
+ "entropy": ent,
+ "f": f,
+ "max_vio": torch.stack([d["max_vio"] for d in per_layer]),
+ "utilization": torch.stack([d["utilization"] for d in per_layer]),
+ "frac_dead": torch.stack([d["frac_dead"] for d in per_layer]),
+ "load_entropy": torch.stack([d["load_entropy"] for d in per_layer]),
+ }
+
+
+def window_metrics(values: dict[str, Any], per_layer: bool = False) -> dict[str, float]:
+ """The `moe/*` history keys from `MoEAux.window_values()`. ONE host sync."""
+ n_layers = int(values["max_vio"].numel())
+ flat = torch.cat(
+ [
+ values["lb"].reshape(1),
+ values["z"].reshape(1),
+ values["entropy"],
+ values["max_vio"],
+ values["utilization"],
+ values["frac_dead"],
+ values["load_entropy"],
+ ]
+ ).tolist()
+ lb, z = flat[0], flat[1]
+ o = 2
+ ent = flat[o : o + n_layers]
+ o += n_layers
+ vio = flat[o : o + n_layers]
+ o += n_layers
+ util = flat[o : o + n_layers]
+ o += n_layers
+ dead = flat[o : o + n_layers]
+ o += n_layers
+ lent = flat[o : o + n_layers]
+ out = {
+ "moe/lb_loss": lb,
+ "moe/z_loss": z,
+ "moe/max_vio": sum(vio) / n_layers,
+ "moe/max_vio_worst": max(vio),
+ "moe/utilization": sum(util) / n_layers,
+ "moe/utilization_worst": min(util),
+ "moe/frac_dead_worst": max(dead),
+ "moe/load_entropy": sum(lent) / n_layers,
+ "moe/router_entropy": sum(ent) / n_layers,
+ }
+ if per_layer:
+ for i in range(n_layers):
+ out[f"moe/l{i}/max_vio"] = vio[i]
+ out[f"moe/l{i}/utilization"] = util[i]
+ return out
+
+
+#: The keys a MoE run logs, NaN until the first window completes. Fixed set so
+#: every history.jsonl row has the same columns.
+def undefined_window_metrics(n_layers: Optional[int] = None, per_layer: bool = False) -> dict[str, float]:
+ keys = [
+ "moe/lb_loss",
+ "moe/z_loss",
+ "moe/max_vio",
+ "moe/max_vio_worst",
+ "moe/utilization",
+ "moe/utilization_worst",
+ "moe/frac_dead_worst",
+ "moe/load_entropy",
+ "moe/router_entropy",
+ ]
+ out = {k: NAN for k in keys}
+ if per_layer and n_layers:
+ for i in range(n_layers):
+ out[f"moe/l{i}/max_vio"] = NAN
+ out[f"moe/l{i}/utilization"] = NAN
+ return out
+
+
+# ---------------------------------------------------------------------------
+# the model proxy
+# ---------------------------------------------------------------------------
+
+
+class _StatsModel:
+ """What the loss adapter is handed instead of the model.
+
+ Adapters call `model(input_ids=..., hidden_consumer=...)` and expect the
+ loss (or logits) back. The proxy adds `return_router_stats=True` (only when
+ gradients are enabled -- a reference-model forward under `no_grad` has
+ nothing to balance), unwraps the `(out, stats)` tuple, and stashes the
+ stats for the module. Every other attribute is delegated, so adapters that
+ read `model.lm_head` or `model.attn_implementation` are unaffected.
+ """
+
+ def __init__(self, owner: "MoETrainModule"):
+ object.__setattr__(self, "_owner", owner)
+ object.__setattr__(self, "calls", [])
+ object.__setattr__(self, "attention_mask", None)
+
+ def __call__(self, *args, **kwargs):
+ owner = self._owner
+ model = owner.model
+ want = torch.is_grad_enabled()
+ if want:
+ kwargs["return_router_stats"] = True
+ if kwargs.get("attention_mask") is None and self.attention_mask is not None:
+ # The batch's real-token mask: padding must not vote in the
+ # balance loss. The model's attention applies it too (a no-op
+ # for valid tokens under right padding, identical outputs).
+ kwargs["attention_mask"] = self.attention_mask
+ try:
+ out = model(*args, **kwargs)
+ except TypeError as e:
+ if want and "return_router_stats" in str(e):
+ raise RuntimeError(
+ "MoETrainModule needs a model whose forward accepts return_router_stats=True "
+ f"(got {type(model).__name__}); a dense model has no router to balance. "
+ "Set moe_aux weights to 0 and use the plain TrainModule."
+ ) from e
+ raise
+ if not want:
+ return out
+ out, stats = out
+ self.calls.append(stats)
+ return out
+
+ def forward_hidden_states(self, **kwargs):
+ """Route `forward_hidden_states` through THIS proxy's `__call__`.
+
+ The DPO adapter (coupled AND decoupled) never calls `model(...)`: it
+ calls `model.forward_hidden_states(...)`. Without this method the
+ attribute falls through `__getattr__` to the REAL model's bound method,
+ which runs without `return_router_stats`, so the adapter "ran no
+ grad-enabled forward" as far as the module can tell and the first
+ micro-step raises -- with default settings (telemetry on) on a stage
+ whose docstring promises telemetry still works. Mirrors
+ `OlmoeForCausalLM.forward_hidden_states`, which is `self(...,
+ return_hidden_states=True)`.
+ """
+ return self(**kwargs, return_hidden_states=True)
+
+ def __getattr__(self, name):
+ return getattr(self._owner.model, name)
+
+
+# ---------------------------------------------------------------------------
+# the train module
+# ---------------------------------------------------------------------------
+
+
+class MoETrainModule(TrainModule):
+ """`TrainModule` + router aux losses + MoE telemetry.
+
+ `train_batch` does what the base does -- forward through the loss adapter,
+ then `(loss / grad_accum).backward()` -- with `loss = CE + lb_weight * lb +
+ z_weight * z`. See the module docstring for the window buffer and scaling.
+ """
+
+ def __init__(
+ self,
+ model,
+ loss_adapter: LossAdapter,
+ optimizer,
+ grad_accum: int,
+ *,
+ aux: Optional[MoEAuxConfig] = None,
+ stage: str = "sft",
+ top_k: Optional[int] = None,
+ weight_format=None,
+ group=None,
+ world_size: Optional[int] = None,
+ ep_groups=None,
+ ep_max_grad_norm: Optional[float] = None,
+ ):
+ super().__init__(model, loss_adapter, optimizer, grad_accum)
+ self.ep_groups = ep_groups
+ self.ep_max_grad_norm = ep_max_grad_norm
+ self._ep_norm = float("nan")
+ if ep_groups is not None:
+ self._check_expert_parallel(model, optimizer, ep_groups)
+ self.aux_cfg = aux or MoEAuxConfig()
+ self.stage = stage
+ self.lb_weight, self.z_weight = resolve_aux_weights(self.aux_cfg, stage)
+ if top_k is None:
+ top_k = getattr(getattr(model, "cfg", None), "top_k", None)
+ if top_k is None:
+ raise ValueError("top_k not given and model.cfg.top_k not found")
+ self.top_k = int(top_k)
+ self.weight_format = weight_format
+ self.collect = bool(self.aux_cfg.telemetry or self.lb_weight > 0 or self.z_weight > 0)
+ self._aux = MoEAux(
+ self.top_k, grad_accum, scope=self.aux_cfg.lb_scope, group=group, world_size=world_size
+ )
+ self._proxy = _StatsModel(self)
+ self._last_metrics: dict[str, float] = {}
+ logger.info(
+ "MoE aux: stage=%s lb_weight=%g z_weight=%g scope=%s telemetry=%s top_k=%d",
+ stage,
+ self.lb_weight,
+ self.z_weight,
+ self.aux_cfg.lb_scope,
+ self.aux_cfg.telemetry,
+ self.top_k,
+ )
+
+ # -- expert parallelism (opt-in) ---------------------------------------
+ @staticmethod
+ def _check_expert_parallel(model, optimizer, groups) -> None:
+ """Refuse the three silent EP misconfigurations instead of training through them."""
+ from ..model import moe_ep
+
+ blocks = moe_ep._moe_blocks(model)
+ if not blocks or any(b.ep is None for b in blocks):
+ raise ValueError(
+ "ep_groups was given but the model's MoE blocks are not expert-parallel: call "
+ "align.model.moe_ep.apply_expert_parallel(model, groups) BEFORE building the optimizer"
+ )
+ if any(b.ep.groups is not groups for b in blocks):
+ raise ValueError("ep_groups is not the group set the model was sharded with")
+ if getattr(optimizer, "max_grad_norm", None) is not None:
+ raise ValueError(
+ "the optimizer clips by its own (shard-local) gradient norm; under expert parallelism "
+ "that clips every rank by a different coefficient. Build it with max_grad_norm=None and "
+ "pass ep_max_grad_norm to MoETrainModule"
+ )
+
+ def optim_step(self) -> bool:
+ if self.ep_groups is not None:
+ from ..model import moe_ep
+
+ moe_ep.finalize_expert_grads(self.model, self.ep_groups)
+ if self.ep_max_grad_norm is not None:
+ norm = moe_ep.clip_grads_(self.model, self.ep_groups, self.ep_max_grad_norm)
+ else:
+ norm = moe_ep.global_grad_norm(self.model, self.ep_groups)
+ self._ep_norm = float(norm)
+ try:
+ return super().optim_step()
+ finally:
+ self._aux.reset()
+
+ @property
+ def last_grad_norm(self) -> float:
+ return self._ep_norm if self.ep_groups is not None else super().last_grad_norm
+
+ def full_model_state_dict(self):
+ sd = super().full_model_state_dict()
+ if self.ep_groups is not None:
+ from ..model import moe_ep
+
+ # the gather is a collective (every rank calls it); like the base state dict (rank 0 only
+ # when nothing is a DTensor) the result is merged where the base returned something
+ gathered = moe_ep.gather_full_expert_state(self.model, self.ep_groups, offload_to_cpu=True)
+ if sd:
+ for k, v in gathered.items():
+ sd[k] = v.cpu()
+ return sd
+
+ def load_model_state_dict(self, nmo_state: dict) -> None:
+ if self.ep_groups is not None:
+ from ..model import moe_ep
+
+ nmo_state = {**nmo_state, **moe_ep.local_expert_slices(self.model, self.ep_groups, nmo_state)}
+ super().load_model_state_dict(nmo_state)
+
+ # -- the loop-facing call ---------------------------------------------
+ def train_batch(self, batch) -> LossOutput:
+ if not self.collect:
+ return super().train_batch(batch)
+
+ proxy = self._proxy
+ proxy.calls.clear()
+ mask = getattr(batch, "attention_mask", None)
+ proxy.attention_mask = mask if isinstance(mask, torch.Tensor) else None
+ out = self.loss_adapter(proxy, batch)
+ calls = list(proxy.calls)
+ proxy.calls.clear()
+ if not calls:
+ raise RuntimeError(
+ "the loss adapter ran no grad-enabled forward through the model, so no router "
+ "statistics exist; MoE aux losses/telemetry cannot be computed"
+ )
+ stats = merge_call_stats(calls)
+ want_lb, want_z = self.lb_weight > 0, self.z_weight > 0
+ lb, z = self._aux.micro(stats, want_lb=want_lb, want_z=want_z)
+
+ ce = out.loss
+ total = ce
+ decoupled = ce.grad_fn is None
+ if want_lb or want_z:
+ if decoupled:
+ raise NotImplementedError(
+ "the loss adapter returned a detached proxy (decoupled backward, e.g. DPO): "
+ "its backward already consumed the model graph, so a router aux loss cannot "
+ "be added. Set moe_aux.lb_weight = z_weight = 0 for this stage "
+ "(telemetry still works)."
+ )
+ if want_lb:
+ total = total + self.lb_weight * lb
+ if want_z:
+ total = total + self.z_weight * z
+ (total / self.grad_accum).backward()
+
+ metrics = dict(out.metrics)
+ if self._aux.m == self.grad_accum:
+ vals = self._aux.window_values()
+ self._last_metrics = window_metrics(vals, per_layer=self.aux_cfg.per_layer_metrics)
+ elif not self._last_metrics:
+ self._last_metrics = undefined_window_metrics(
+ len(stats), per_layer=self.aux_cfg.per_layer_metrics
+ )
+ metrics.update(self._last_metrics)
+
+ reported = ce.detach()
+ if want_lb or want_z:
+ # 0 * finite = 0 (loss stays exactly CE); 0 * NaN/inf = NaN, so a
+ # poisoned aux term cannot hide behind a healthy CE.
+ reported = reported + 0.0 * (total.detach() - ce.detach())
+ return LossOutput(loss=reported, n_tokens=out.n_tokens, metrics=metrics)
+
+ def zero_grads(self) -> None:
+ super().zero_grads()
+ self._aux.reset()
+
+
+def build_moe_train_module(
+ cfg,
+ model,
+ loss_adapter: LossAdapter,
+ optimizer,
+ *,
+ stage: str,
+ olmoe_cfg,
+ group=None,
+ ep_groups=None,
+ ep_max_grad_norm: Optional[float] = None,
+) -> MoETrainModule:
+ """The MoE module for an OLMoE-architecture model, wired from a RunConfig.
+
+ Carries the OLMoE weight format (the Trainer picks it up from the module),
+ `cfg.moe_aux` and `cfg.optimizer.grad_accum`.
+ """
+ from .ckpt_format import OlmoeFormat
+
+ return MoETrainModule(
+ model,
+ loss_adapter,
+ optimizer,
+ cfg.optimizer.grad_accum,
+ aux=cfg.moe_aux,
+ stage=stage,
+ top_k=olmoe_cfg.top_k,
+ weight_format=OlmoeFormat(olmoe_cfg),
+ group=group,
+ ep_groups=ep_groups,
+ ep_max_grad_norm=ep_max_grad_norm,
+ )
+
+
+__all__ = [
+ "STAGE_AUX_DEFAULTS",
+ "POST_TRAINING_AUX_DEFAULT",
+ "resolve_aux_weights",
+ "WindowTotals",
+ "merge_call_stats",
+ "MoEAux",
+ "window_metrics",
+ "undefined_window_metrics",
+ "MoETrainModule",
+ "build_moe_train_module",
+]
diff --git a/code/align/train/offload_adamw.py b/code/align/train/offload_adamw.py
new file mode 100644
index 0000000000000000000000000000000000000000..34016984e666a558e3e7bfa002ea4356cd4683e0
--- /dev/null
+++ b/code/align/train/offload_adamw.py
@@ -0,0 +1,680 @@
+"""Optimizer-state-only CPU offload for AdamW.
+
+The arithmetic that forces this design (measured on this box, not re-derived):
+full fine-tuning 7.25B params with AdamW needs ~116GB of optimizer state --
+~29GB per GPU even fully sharded across 4 ranks, against a 23GB A10G budget.
+Moving the state to host RAM fits it. Moving anything ELSE to host RAM does
+not pay: FSDP's own `CPUOffloadPolicy` transfers parameters every forward AND
+backward, while this transfers once per optimizer step, which at grad-accum 4
+is a quarter as often and overlappable with the next accumulation window.
+
+Measured cost (tmp/offload_bench.json): torch CPU AdamW (foreach) 2.56s per
+1.81B-param rank shard; PCIe 13.2GB/s solo scaling to 52.7/53.9GB/s aggregate
+across 4 ranks, giving ~0.82s of transfer per rank per optimizer step. At
+grad-accum >= 4 that is an estimated 20-30% step-time overhead; at accum 1 it
+would be 50-100%. This is WHY accum >= 4 is mandatory for full-FT, and the
+Trainer refuses less.
+
+Design note on the update rule: the CPU step is torch's own `AdamW` driven
+over the offloaded fp32 master copies, not a reimplementation. Bitwise
+equivalence with `torch.optim.AdamW` is then a property of the construction
+rather than something a test has to keep rediscovering after every torch
+release -- and the test still catches the failures that actually happen here,
+which are plumbing failures: a dropped weight_decay, a mis-forwarded beta, a
+master copy that round-trips through a narrower dtype.
+"""
+
+from __future__ import annotations
+
+import logging
+from typing import Iterable, Optional, Sequence
+
+import torch
+
+logger = logging.getLogger(__name__)
+
+#: Optimizer identities this repo KNOWS. Adafactor is deliberately absent
+#: and refused BY NAME -- see `make_optimizer`.
+#:
+#: `adamw8bit` is BUILDABLE again: `align/train/adamw8bit.py` is a torch-only
+#: block-wise 8-bit AdamW (the bitsandbytes dependency stays banned; see
+#: docs/DEPENDENCY_POLICY.md). The distinction between "a typo'd optimizer
+#: name" and "a known optimizer" must still survive in the error text: the
+#: former is `ValueError("unknown optimizer")`.
+KNOWN_OPTIMIZERS = ("offload_adamw", "adamw8bit")
+
+#: The default optimizer in `RunConfig` and the one the pinned-staging budget
+#: and the RSS projection model. Every SHIPPED full-FT config overrides it to
+#: `adamw8bit`, because at 7.25B params / 4 ranks the host cannot hold this
+#: optimizer's state (see adamw8bit.py for the arithmetic).
+OPTIMIZER_OF_RECORD = "offload_adamw"
+
+
+def pinned_staging_bytes(cfg) -> float:
+ """Total pinned staging budget across all ranks, DERIVED from the config.
+
+ One fp32 layer-shard is `n_params / n_layers / world_size` elements at 4
+ bytes; each rank is allowed `pinned_staging_shards_per_rank` of them.
+
+ Pinned memory is unswappable and unreclaimable, and this box has no swap,
+ so this is a hard budget rather than an estimate. It is the same formula
+ `guard.projected_full_ft_rss_gb` uses for the RSS projection -- deliberately
+ the same, so the launch banner and the preflight cannot disagree about how
+ much host RAM the job intends to lock down.
+ """
+ if cfg.model.n_params <= 0:
+ raise ValueError(
+ f"model.n_params={cfg.model.n_params} is not a parameter count, so "
+ "there is no staging budget to derive. The tiny CI config sets it "
+ "to -1 deliberately (the 7B projection does not apply there); a "
+ "negative budget must raise rather than silently become a negative "
+ "cap that every allocation trivially satisfies."
+ )
+ world = cfg.train.world_size
+ layer_shard_bytes = cfg.model.n_params / cfg.model.n_layers / world * 4.0
+ return layer_shard_bytes * cfg.train.pinned_staging_shards_per_rank * world
+
+
+class OffloadAdamW:
+ """AdamW whose state lives on the host; params and grads never move.
+
+ `step()` returns False when the grad-skip gate refused the step.
+ """
+
+ def __init__(
+ self,
+ params: Iterable[torch.Tensor],
+ lr: float,
+ betas: tuple[float, float] = (0.9, 0.95),
+ eps: float = 1e-8,
+ weight_decay: float = 0.0,
+ pinned_staging_cap_bytes: Optional[float] = None,
+ overlap: bool = True,
+ state_device: str = "cpu",
+ max_grad_norm: Optional[float] = None,
+ ) -> None:
+ self.params: list[torch.Tensor] = list(params)
+ if not self.params:
+ raise ValueError("OffloadAdamW got an empty parameter list")
+ if max_grad_norm is not None and not (float(max_grad_norm) > 0.0):
+ raise ValueError(
+ f"max_grad_norm must be a positive float or None, got {max_grad_norm!r}"
+ )
+ #: GLOBAL-norm clip (`clip_grad_norm_`'s rule, `min(1, c / (norm + 1e-6))`),
+ #: None = no clipping. THE SILENT-NO-CLIP DEFECT (H2 review, 2026-10-13):
+ #: `OptimizerConfig.max_grad_norm` defaults to 1.0 and every shipped TOML
+ #: states it, but this class had no such parameter and `make_optimizer`
+ #: never forwarded the field -- the one optimizer of record for full-FT
+ #: ran UNCLIPPED while `BlockwiseAdamW8bit` honoured the same config.
+ #: `last_grad_norm` stays the PRE-clip norm (the tripwires must see an
+ #: explosion, not its clipped image).
+ self.max_grad_norm = None if max_grad_norm is None else float(max_grad_norm)
+ self.state_device = torch.device(state_device)
+ self.overlap = bool(overlap)
+ self.banner_tag = "offload_adamw"
+
+ # THIS RANK'S bytes, so the staging cap is a per-rank quantity. Under
+ # FSDP2 every parameter is a DTensor whose `.numel()` is the GLOBAL
+ # element count: a world-size-times overcount that would have refused
+ # (or, worse, mis-banner) any sharded job.
+ self.staging_bytes_required = float(
+ sum(self._localize(p).numel() * 4 for p in self.params)
+ )
+ self.pinned_staging_cap_bytes = pinned_staging_cap_bytes
+ if (
+ pinned_staging_cap_bytes is not None
+ and self.staging_bytes_required > pinned_staging_cap_bytes
+ ):
+ raise ValueError(
+ f"pinned staging would need "
+ f"{self.staging_bytes_required / 2**30:.2f}GiB but the cap is "
+ f"{pinned_staging_cap_bytes / 2**30:.2f}GiB. Pinned memory is "
+ "unswappable and this box has no swap: exceeding the budget "
+ "does not degrade, it wedges the host. Lower "
+ "train.pinned_staging_shards_per_rank or shard further."
+ )
+
+ # fp32 master copies on the host. These are the AUTHORITATIVE weights:
+ # the device params are a bf16 view of them, refreshed every step.
+ self._pin = torch.cuda.is_available() and self.state_device.type == "cpu"
+ # THE FSDP2 DTENSOR DEFECT (found in the align port review, 2026-10-12;
+ # the predecessor never hit it because the shard-local 8-bit optimizer
+ # was the one that actually ran on FSDP2 parameters). Under `fully_shard`
+ # `model.parameters()` yields DTensors, and OffloadAdamW died on them
+ # twice: `is_pinned()` has no DTensor sharding rule at construction,
+ # and the grad-norm accumulator mixed a DTensor into a plain tensor at
+ # the first step. Masters, grads and the H2D copy-back therefore all go
+ # through `_localize` -- this rank's shard, a storage-sharing view -- the
+ # same contract `ShardLocalAdamW8bit` documents (AdamW is elementwise,
+ # so stepping the local shard is exact).
+ self._master = [
+ self._host_tensor(self._localize(p).detach().to(torch.float32)) for p in self.params
+ ]
+ self._inner = torch.optim.AdamW(
+ self._master,
+ lr=lr,
+ betas=betas,
+ eps=eps,
+ weight_decay=weight_decay,
+ )
+
+ self.skipped_steps = 0
+ self.last_grad_norm = float("nan")
+ logger.info(
+ "OffloadAdamW: %d tensors, %.2fGiB fp32 master on %s (pinned=%s), "
+ "staging %.2fGiB, overlap=%s",
+ len(self.params),
+ sum(m.numel() * 4 for m in self._master) / 2**30,
+ self.state_device,
+ self._pin,
+ self.staging_bytes_required / 2**30,
+ self.overlap,
+ )
+
+ # -- host staging -------------------------------------------------------
+
+ @staticmethod
+ def _localize(t):
+ """This rank's local shard of an FSDP2 DTensor (a view sharing the
+ parameter's storage), or `t` itself for an ordinary tensor."""
+ return t.to_local() if hasattr(t, "to_local") else t
+
+ def _host_tensor(self, t: torch.Tensor) -> torch.Tensor:
+ out = t.to(self.state_device, copy=True)
+ if self._pin and not out.is_pinned():
+ out = out.pin_memory()
+ return out
+
+ # -- the step -----------------------------------------------------------
+
+ def step(self) -> bool:
+ """One optimizer step, or a refusal.
+
+ Order matters and is load-bearing: finiteness is checked BEFORE any
+ state is touched. An implementation that discovers the NaN after
+ updating the moments has already poisoned the second-moment estimate,
+ and every subsequent step is scaled by it.
+ """
+ grads = [None if p.grad is None else self._localize(p.grad) for p in self.params]
+ if all(g is None for g in grads):
+ raise RuntimeError(
+ "OffloadAdamW.step() called with no gradients on any parameter. "
+ "A step that silently does nothing is indistinguishable from a "
+ "run that trained."
+ )
+
+ if not self._all_finite(grads):
+ self.skipped_steps += 1
+ # NaN, not 0.0: the norm of a non-finite gradient is undefined, and
+ # a 0.0 in the log reads as 'perfectly converged'.
+ self.last_grad_norm = float("nan")
+ logger.warning(
+ "grad-skip gate: non-finite gradient, skipping optimizer step "
+ "(%d skipped so far). Moments and step counts left untouched.",
+ self.skipped_steps,
+ )
+ return False
+
+ self.last_grad_norm = self._grad_norm(grads) # PRE-clip, on purpose
+
+ # `_grad_norm` all-reduces the sum of squares, so `coef` is the same on
+ # every rank: each rank scales its own disjoint shard by the GLOBAL factor.
+ coef = 1.0
+ if self.max_grad_norm is not None:
+ coef = min(1.0, self.max_grad_norm / (self.last_grad_norm + 1e-6))
+
+ # D2H: fp32 grads into the host staging that backs the master copies.
+ for master, g in zip(self._master, grads):
+ if g is None:
+ master.grad = None
+ continue
+ master.grad = self._host_tensor(g.detach().to(torch.float32))
+ if coef != 1.0:
+ master.grad.mul_(coef)
+
+ self._inner.step()
+
+ # H2D: updated master back into the device params, in their own dtype.
+ # copy_ keeps the storage identity FSDP's shard views depend on.
+ with torch.no_grad():
+ for p, master in zip(self.params, self._master):
+ local = self._localize(p).detach()
+ local.copy_(master.to(local.dtype))
+ return True
+
+ def zero_grad(self, set_to_none: bool = True) -> None:
+ for p in self.params:
+ if set_to_none:
+ p.grad = None
+ elif p.grad is not None:
+ p.grad.zero_()
+ for m in self._master:
+ m.grad = None
+
+ def _all_finite(self, grads: Sequence[Optional[torch.Tensor]]) -> bool:
+ """Global finiteness. Under FSDP a rank whose shard is finite must not
+ step while a peer's is not: the peers would diverge silently and only
+ surface as an NCCL mismatch much later, if at all."""
+ local = True
+ for g in grads:
+ if g is not None and not bool(torch.isfinite(g).all()):
+ local = False
+ break
+ return bool(self._all_reduce_flag(local))
+
+ @staticmethod
+ def _collective_device() -> torch.device:
+ """The device a tensor must live on to join a collective on the default
+ process group: this rank's OWN cuda device under NCCL (a bare .cuda()
+ would put every rank's tensor on device 0 and reduce the wrong ones),
+ CPU under gloo. Keyed on the group's backend, not on
+ `torch.cuda.is_available()`: a gloo group on a box that happens to have
+ GPUs would otherwise be handed a cuda tensor it cannot reduce."""
+ import torch.distributed as dist
+
+ if "nccl" in str(dist.get_backend()).lower() and torch.cuda.is_available():
+ from ..utils.dist import local_rank
+
+ return torch.device("cuda", local_rank())
+ return torch.device("cpu")
+
+ @staticmethod
+ def _all_reduce_flag(local: bool) -> bool:
+ import torch.distributed as dist
+
+ if not dist.is_available() or not dist.is_initialized():
+ return local
+ flag = torch.tensor([1.0 if local else 0.0], device=OffloadAdamW._collective_device())
+ dist.all_reduce(flag, op=dist.ReduceOp.MIN)
+ return bool(flag.item() > 0.0)
+
+ @staticmethod
+ def _grad_norm(grads: Sequence[Optional[torch.Tensor]]) -> float:
+ """The GLOBAL L2 norm of the gradient, identical on every rank.
+
+ Accumulate on the GRADS' device, not on CPU: the accumulator used to be
+ a bare `torch.zeros((), dtype=float64)`, a CPU tensor, which broke the
+ moment the shard-local 8-bit optimizer called it with CUDA grads:
+ "Expected all tensors to be on the same device, but found cuda:0 and cpu".
+
+ THE SHARD-LOCAL NORM DEFECT (align port review, 2026-10-12). Under FSDP2
+ each rank holds a DISJOINT 1/world slice of every gradient, so the sum of
+ squares over the local slice is a norm of a quarter of the gradient --
+ and a different number on every rank. `GradNormTripwire` is a HALT wire
+ reading this value: a rank-local norm lets ONE rank cross the ceiling and
+ raise while its peers continue into the next collective (a hang until the
+ NCCL watchdog), and it under-reads a true explosion by ~sqrt(world). The
+ sum of squares is therefore all-reduced (SUM) before the root. Every
+ rank reaches this call together or not at all, because the finiteness
+ gate that precedes it is itself a collective.
+ """
+ import torch.distributed as dist
+
+ present = [g for g in grads if g is not None]
+ distributed = dist.is_available() and dist.is_initialized()
+ if not present and not distributed:
+ return float("nan")
+ device = present[0].device if present else torch.device("cpu")
+ total = torch.zeros((), dtype=torch.float64, device=device)
+ for g in present:
+ total += g.detach().to(torch.float64).pow(2).sum()
+ if distributed:
+ total = total.to(OffloadAdamW._collective_device())
+ dist.all_reduce(total, op=dist.ReduceOp.SUM)
+ return float(total.sqrt())
+
+ # -- introspection ------------------------------------------------------
+
+ def describe_placement(self) -> dict:
+ """Where every tensor actually lives. Consumed by the launch banner and
+ by the device-partition test; a placement claim nobody checks is how
+ 'the state is offloaded' becomes untrue after a refactor."""
+
+ def entry(t: torch.Tensor) -> dict:
+ return {
+ "device": t.device.type,
+ "dtype": str(t.dtype),
+ "pinned": bool(t.is_pinned()) if t.device.type == "cpu" else False,
+ "bytes": t.numel() * t.element_size(),
+ }
+
+ state = []
+ for group in self._inner.state.values():
+ for key, v in group.items():
+ if isinstance(v, torch.Tensor):
+ state.append({"key": key, **entry(v)})
+ return {
+ "state": state,
+ "master": [entry(m) for m in self._master],
+ "params": [entry(self._localize(p)) for p in self.params],
+ "staging_bytes_required": self.staging_bytes_required,
+ "pinned_staging_cap_bytes": self.pinned_staging_cap_bytes,
+ }
+
+ def state_snapshot_for_tests(self) -> dict:
+ """Flat clone of every optimizer-state tensor, including the step
+ counts. Tests assert these are byte-identical across a skipped step."""
+ out = {}
+ for i, p in enumerate(self._master):
+ for key, v in self._inner.state.get(p, {}).items():
+ out[f"{i}.{key}"] = (
+ v.detach().clone() if isinstance(v, torch.Tensor) else torch.tensor(float(v))
+ )
+ return out
+
+ # -- resume -------------------------------------------------------------
+
+ def state_dict(self) -> dict:
+ """A DETACHED CLONE of every state tensor, on CPU.
+
+ THE SHARED-STATE ALIASING INCIDENT. Two traps stack here, and both are
+ silent:
+
+ 1. `torch.optim.Optimizer.state_dict()` returns per-param dicts that
+ SHARE the live state tensors, and `.to("cpu")` on a tensor already
+ on CPU returns the same object -- so a state dict built without
+ clones is a live view of the optimizer, not a snapshot of it.
+ 2. Mutating that returned dict in place (to clone the values) rebinds
+ the optimizer's OWN state entries, so the "snapshot" aliases the
+ live state no matter which order you do it in.
+
+ The result of getting this wrong is not a crash: `load_state_dict`
+ hands the loaded optimizer the same `step`/`exp_avg`/`exp_avg_sq`
+ objects the source still holds, both mutate them every step, Adam's
+ step counter advances twice per step, and the two runs diverge while
+ every state comparison between them says they are identical.
+
+ So this builds a NEW nested structure and never touches what the inner
+ optimizer returned.
+ """
+ inner = self._inner.state_dict()
+ return {
+ "param_groups": inner["param_groups"],
+ "state": {
+ idx: {
+ k: (v.detach().to("cpu").clone() if isinstance(v, torch.Tensor) else v)
+ for k, v in group.items()
+ }
+ for idx, group in inner["state"].items()
+ },
+ "master": [m.detach().to("cpu").clone() for m in self._master],
+ "skipped_steps": self.skipped_steps,
+ }
+
+ def load_state_dict(self, sd: dict) -> None:
+ """Restore moments AND master weights, then refresh the device params.
+
+ The master is the authoritative weight copy, so a resume that restored
+ only the moments would carry the pre-checkpoint parameters forward --
+ a run that looks like it resumed and is training a different model.
+ """
+ masters = sd.get("master")
+ if masters is None:
+ raise KeyError(
+ "optimizer state_dict has no 'master' entry; refusing to "
+ "resume from a checkpoint that carries moments but not the "
+ "fp32 master weights."
+ )
+ if len(masters) != len(self._master):
+ raise ValueError(
+ f"checkpoint has {len(masters)} master tensors but this "
+ f"optimizer holds {len(self._master)}"
+ )
+ with torch.no_grad():
+ for dst, src in zip(self._master, masters):
+ dst.copy_(src.to(dst.dtype))
+ for p, master in zip(self.params, self._master):
+ local = self._localize(p).detach()
+ local.copy_(master.to(local.dtype))
+ # Clone on the way IN as well: torch stores these objects directly when
+ # no dtype/device cast is needed, so loading one checkpoint dict into
+ # two optimizers would otherwise make them share moments. Symmetric
+ # with the cloning in state_dict(), and for the same reason.
+ inner_sd = {
+ "param_groups": sd["param_groups"],
+ "state": {
+ idx: {
+ k: (v.detach().clone() if isinstance(v, torch.Tensor) else v)
+ for k, v in group.items()
+ }
+ for idx, group in sd["state"].items()
+ },
+ }
+ self._inner.load_state_dict(inner_sd)
+ self.skipped_steps = int(sd.get("skipped_steps", 0))
+
+ def reseed_masters_from_params(self) -> None:
+ """Overwrite the fp32 masters with the CURRENT device params (audit U01-2).
+
+ The master is authoritative: every step rewrites the params from it. A resume that loads
+ weights but no optimizer state (a legacy checkpoint, `reshard(drop_optimizer=True)`) would
+ otherwise leave the masters at the LAUNCH weights and the first step would silently revert
+ the restored model. Moments stay fresh; only the master copy is re-seeded.
+ """
+ with torch.no_grad():
+ for p, master in zip(self.params, self._master):
+ master.copy_(self._localize(p).detach().to(torch.float32))
+
+ @property
+ def param_groups(self):
+ """Exposed so a torch LR scheduler can drive this optimizer directly."""
+ return self._inner.param_groups
+
+
+class ShardLocalAdamW8bit:
+ """Compatibility shim: the legacy name for the shard-local 8-bit AdamW.
+
+ The optimizer itself is `align.train.adamw8bit.BlockwiseAdamW8bit` (pure
+ torch; `make_optimizer` returns THAT directly). This class keeps the
+ legacy wrapper shape -- `_inner`, `_pairs`, and a `state_dict` that wraps
+ the inner state with `world_size`/`n_shards` -- because two invariants are
+ regression-tested against that shape WITHOUT constructing the class
+ (tests build a stub via `__new__` and hand it a fake `_inner`):
+
+ * the recursive CPU clone: a one-level clone once passed ~3.2GB of live
+ CUDA moments straight into the checkpoint pickle, and `gather_object`
+ restored them onto the SENDER'S device on rank 0 and OOM'd the DPO
+ run at its first checkpoint;
+ * the world-size refusal: the saved state is SHARD-shaped, and reloading
+ it under a different world size would silently align rank r's moments
+ to a different slice of the parameter.
+
+ WHY SHARD-LOCAL STEPPING IS EXACT, not an approximation: AdamW is
+ ELEMENTWISE. Every update depends only on that element's own gradient and
+ its own two moment scalars, so there is nothing for a shard boundary to
+ cut. (This is exactly what Adafactor cannot claim, and why Adafactor is
+ banned here: its factored second moment is a row/column statistic that
+ DOES cross shard boundaries.) `to_local()` is a VIEW sharing storage with
+ the DTensor, so stepping it updates the parameter -- measured on 4 real
+ ranks: storage pointers stable across calls and steps, and the DTensor
+ observes the update.
+
+ HISTORY: the first version wrapped bitsandbytes, whose fused kernel
+ cannot consume DTensors ("optimizer_update_8bit_blockwise got mixed
+ torch.Tensor and DTensor"), and was then retired with the dependency. It
+ is now a thin delegate to the in-repo implementation, which does the
+ gating, the GLOBAL grad norm and the state handling itself; the shim only
+ mirrors `skipped_steps` / `last_grad_norm` so legacy readers keep working.
+ """
+
+ def __init__(
+ self,
+ params: Iterable[torch.Tensor],
+ lr: float,
+ betas: tuple[float, float] = (0.9, 0.95),
+ eps: float = 1e-8,
+ weight_decay: float = 0.0,
+ ):
+ # Consume the parameter iterable first so a generator-backed caller
+ # (`model.parameters()`) behaves the same however it is used, and
+ # validate the empty case before anything is built.
+ params = list(params)
+ if not params:
+ raise ValueError("ShardLocalAdamW8bit got an empty parameter list")
+ # Lazy: adamw8bit imports this module at top level (it reuses the
+ # collective helpers), so the reverse import cannot be top-level.
+ from .adamw8bit import BlockwiseAdamW8bit
+
+ self._inner = BlockwiseAdamW8bit(
+ params, lr=lr, betas=betas, eps=eps, weight_decay=weight_decay
+ )
+ self._pairs = [(p, self._localize(p).detach()) for p in params]
+ self.skipped_steps = 0
+ self.last_grad_norm = float("nan")
+ self.banner_tag = self._inner.banner_tag
+
+ @staticmethod
+ def _localize(t):
+ return t.to_local() if hasattr(t, "to_local") else t
+
+ def step(self) -> bool:
+ """One optimizer step, or a refusal. Same contract as OffloadAdamW.
+
+ Gating, the global grad norm and the update all live in the inner
+ optimizer; doing them here too would pay the collectives twice."""
+ stepped = self._inner.step()
+ self.skipped_steps = self._inner.skipped_steps
+ self.last_grad_norm = self._inner.last_grad_norm
+ return stepped
+
+ def zero_grad(self, set_to_none: bool = True) -> None:
+ for p, local in self._pairs:
+ p.grad = None if set_to_none else p.grad
+ local.grad = None
+
+ def state_dict(self) -> dict:
+ from ..utils.dist import world_size
+
+ inner = self._inner.state_dict()
+
+ # DETACHED CPU CLONES, RECURSIVELY -- same two aliasing traps
+ # OffloadAdamW.state_dict documents, plus the one that survived the
+ # first fix: bnb nests its real tensors under
+ # state[idx]["__bnb_optimizer_quant_state__"], a dict, so a one-level
+ # clone passed ~3.2GB of live CUDA moments straight into the pickle.
+ # gather_object then restored them onto the SENDER'S device on rank 0
+ # and OOM'd the DPO run at its first checkpoint (SFT's end-save
+ # survived the same bug on VRAM headroom alone).
+ def _to_cpu(o):
+ if isinstance(o, torch.Tensor):
+ return o.detach().to("cpu").clone()
+ if isinstance(o, dict):
+ return {k: _to_cpu(v) for k, v in o.items()}
+ if isinstance(o, (list, tuple)):
+ return type(o)(_to_cpu(v) for v in o)
+ return o
+
+ return {
+ "inner": _to_cpu(inner),
+ "world_size": world_size(),
+ "n_shards": len(self._pairs),
+ "skipped_steps": self.skipped_steps,
+ }
+
+ def load_state_dict(self, sd: dict) -> None:
+ from ..utils.dist import world_size
+
+ saved_ws, now_ws = sd.get("world_size"), world_size()
+ if saved_ws is not None and saved_ws != now_ws:
+ raise ValueError(
+ f"this optimizer state was saved at world_size={saved_ws} but "
+ f"the current run has world_size={now_ws}. The 8-bit moments "
+ "are SHARD-shaped: loading them under a different world size "
+ "would align each rank's state to a different slice of the "
+ "parameter, and training would continue plausibly on "
+ "corrupted state. Resume at the original world size, or start "
+ "fresh."
+ )
+ saved_n = sd.get("n_shards")
+ if saved_n is not None and saved_n != len(self._pairs):
+ raise ValueError(
+ f"checkpoint holds {saved_n} shards, this optimizer has {len(self._pairs)}"
+ )
+ self._inner.load_state_dict(sd["inner"])
+ self.skipped_steps = int(sd.get("skipped_steps", 0))
+
+ @property
+ def param_groups(self):
+ return self._inner.param_groups
+
+
+def make_optimizer(cfg, params: Iterable[torch.Tensor]):
+ """Build the optimizer named by the config, and tag it for the banner.
+
+ Which optimizer actually ran is recorded in the banner: a run that used a
+ different optimizer than its config names is not reproducible from that
+ config.
+
+ `adamw8bit` is the pre-registered on-device fallback for the case where
+ host RAM cannot hold the offloaded state (every shipped full-FT config
+ names it). It is the in-repo torch-only `BlockwiseAdamW8bit`; the banner
+ states its state size from the RANK-LOCAL element count
+ (`2*n + 8*n/block` bytes), so the launch log cannot claim a footprint the
+ optimizer does not have. It is never silently swapped for `offload_adamw`:
+ a run whose banner says one optimizer while the config names another is
+ exactly the irreproducibility this function exists to prevent.
+ """
+ name = cfg.optimizer.name
+ lowered = str(name).lower()
+
+ if lowered == "adafactor":
+ raise ValueError(
+ "Adafactor is BANNED in this repo. Its factored second moment "
+ "crosses DTensor shard boundaries: under ZeRO-3 on this box it "
+ "produced a perfect-looking loss curve while moving ~1% of "
+ "tensors -- a run that trained nothing and reported success for "
+ "hours. AdamW only (offload_adamw is the optimizer of record)."
+ )
+ if lowered not in KNOWN_OPTIMIZERS:
+ raise ValueError(
+ f"unknown optimizer {name!r}; known: {list(KNOWN_OPTIMIZERS)}. "
+ "A typo must not fall through to a default -- the whole point of "
+ "naming the optimizer in the config is that the banner can state "
+ "which one ran."
+ )
+
+ o = cfg.optimizer
+ if lowered == "offload_adamw":
+ # The staging cap is a full-FT host-RAM budget. The tiny CPU harness
+ # has no layer shards to size it from (n_params = -1), so it runs
+ # uncapped rather than against a fabricated number.
+ cap = pinned_staging_bytes(cfg) if cfg.train.full_ft else None
+ opt = OffloadAdamW(
+ params,
+ lr=o.lr,
+ betas=tuple(o.betas),
+ eps=o.eps,
+ weight_decay=o.weight_decay,
+ pinned_staging_cap_bytes=cap,
+ max_grad_norm=getattr(o, "max_grad_norm", None),
+ )
+ opt.banner_tag = (
+ f"offload_adamw (fp32 master + moments on host, "
+ f"{opt.staging_bytes_required / 2**30:.2f}GiB staging)"
+ )
+ return opt
+
+ # Only 'adamw8bit' can reach here (unknown names raised above).
+ # Lazy import: adamw8bit imports this module for the collective helpers.
+ from .adamw8bit import BlockwiseAdamW8bit
+
+ opt = BlockwiseAdamW8bit(
+ params,
+ lr=o.lr,
+ betas=tuple(o.betas),
+ eps=o.eps,
+ weight_decay=o.weight_decay,
+ # OptimizerConfig.max_grad_norm (default 1.0), same rule and same pre-clip
+ # `last_grad_norm` record as the offload_adamw branch above.
+ max_grad_norm=getattr(o, "max_grad_norm", None),
+ )
+ n_local = sum(s["numel"] for s in opt._state)
+ state_bytes = BlockwiseAdamW8bit.projected_state_bytes(
+ [s["numel"] for s in opt._state], opt.block_size, opt.min_8bit_size
+ )
+ opt.banner_tag = (
+ f"adamw8bit (block-wise 8-bit moments ON DEVICE, block {opt.block_size}, "
+ f"{state_bytes / 2**30:.2f}GiB state for {n_local / 1e6:.1f}M local params "
+ f"= 2B + 8B/block per param; no master copy; zero host RSS)"
+ )
+ return opt
diff --git a/code/align/train/probes.py b/code/align/train/probes.py
new file mode 100644
index 0000000000000000000000000000000000000000..c80512613400437011ba4864572d0025acdec83a
--- /dev/null
+++ b/code/align/train/probes.py
@@ -0,0 +1,627 @@
+"""Probes and tripwires: the checks a loss curve cannot make.
+
+Every mechanism here exists because the predecessor shipped through the
+failure it detects, with healthy-looking metrics the whole way:
+
+ * TRUNK MOVEMENT. Reentrant gradient checkpointing silently zeroes trunk
+ gradients under sharding. The run trains -- loss falls, throughput is
+ normal -- while only the embeddings and the LM head move. Nothing in the
+ loss, the grad norm, or the throughput distinguishes it from a healthy
+ run, so the probe compares actual parameter values and RAISES at the first
+ log step rather than warning at the hundredth.
+ * logps/chosen DECLINE. DPO's objective only asks for a MARGIN, which is
+ equally well served by pushing the rejected logprob down as by pulling the
+ chosen one up. When it takes the second route the model gets better at the
+ loss and worse at the task. Maximal for Arm B by design, since pi_SFT
+ already assigns the chosen sequence high likelihood.
+ * LENGTH DRIFT. Preference optimization has a documented length bias. The
+ alarm is baseline-RELATIVE (Arm A measured 54.9% chosen-longer), because
+ an absolute threshold says nothing about a dataset whose baseline is not
+ 50%.
+
+Tripwires are pure functions over metric windows, so they are tested without a
+model. NaN in means NaN out and never a trip: a NaN coerced to 0.0 reads as
+'flat', and flat-chosen-with-rising-margin is itself the alarm condition.
+"""
+
+from __future__ import annotations
+
+import logging
+import math
+from dataclasses import dataclass, field
+from typing import Any, Optional, Sequence
+
+import torch
+
+logger = logging.getLogger(__name__)
+
+NAN = float("nan")
+
+#: Parameter-name fragments that are NOT trunk. The probe must exclude them:
+#: the incident's signature is "head and embeddings moved, trunk did not", so
+#: a probe that tracked them would call that run healthy.
+NON_TRUNK_FRAGMENTS = ("embed_tokens", "lm_head", "embed_out", "wte", "wpe")
+
+
+class TrunkMovementProbe:
+ """Snapshot trunk parameters, then assert they actually moved."""
+
+ def __init__(self, model, max_tracked: int = 16, seed: int = 0):
+ self._model = model
+ self.tracked_names: list[str] = []
+ self._snapshots: list[torch.Tensor] = []
+
+ # Only TRAINABLE parameters can move. On a LoRA/partially-frozen model a
+ # stride that landed on frozen base weights would report "trunk frozen"
+ # on a perfectly healthy run (found in the PPO port: the probe was
+ # rebuilt on the real model at resume and tracked frozen tensors).
+ candidates = [
+ (n, p)
+ for n, p in model.named_parameters()
+ if p.requires_grad and not any(f in n for f in NON_TRUNK_FRAGMENTS)
+ ]
+ if not candidates:
+ raise ValueError(
+ "TrunkMovementProbe found no trunk parameters to track "
+ f"(every TRAINABLE parameter was absent or matched {NON_TRUNK_FRAGMENTS}). It cannot report "
+ "'no movement' on an empty set -- that is the alarm condition, "
+ "and reporting it from nothing would be a false alarm forever."
+ )
+ # Deterministic even sample across depth, so a probe on a 32-layer
+ # model does not track 16 tensors from layer 0.
+ stride = max(1, len(candidates) // max_tracked)
+ for name, p in candidates[::stride][:max_tracked]:
+ self.tracked_names.append(name)
+ self._snapshots.append(self.snapshot_tensor(p))
+ self.n_tracked = len(self._snapshots)
+
+ def release(self) -> None:
+ """Drop the model reference and the snapshots (U01-11): the probe is dead after the
+ post-train handoff and must not keep the training replica alive."""
+ self._model = None
+ self._snapshots = []
+
+ @staticmethod
+ def snapshot_tensor(t: Any) -> torch.Tensor:
+ """Detached CPU copy of a parameter, reaching through DTensor.
+
+ Under FSDP2 every sharded parameter is a DTensor whose global view is
+ an unmaterialised placeholder; `.to_local()` is the rank's actual
+ shard. Comparing global views would compare something that never
+ changes on this rank.
+ """
+ if hasattr(t, "to_local"):
+ t = t.to_local()
+ if hasattr(t, "detach"):
+ t = t.detach()
+ return t.to("cpu", copy=True).to(torch.float32)
+
+ def check(self, step: int, model=None, raise_on_frozen: bool = True) -> dict:
+ """Compare against the snapshot. Zero movement RAISES."""
+ target = model or self._model
+ if target is None:
+ raise RuntimeError("TrunkMovementProbe.check after release(): the model reference was dropped")
+ current = dict(target.named_parameters())
+ tensors = [current[n] for n in self.tracked_names]
+ deltas = []
+ for snap, t in zip(self._snapshots, tensors):
+ now = self.snapshot_tensor(t)
+ deltas.append((now - snap).abs().max().item())
+
+ moved = sum(1 for d in deltas if d > 0.0)
+ report = {
+ "step": step,
+ "n_tracked": self.n_tracked,
+ "moved_fraction": moved / self.n_tracked if self.n_tracked else NAN,
+ "max_abs_delta": max(deltas) if deltas else NAN,
+ "tracked_names": list(self.tracked_names),
+ }
+ if moved == 0 and raise_on_frozen:
+ raise RuntimeError(
+ f"TRUNK MOVEMENT PROBE FAILED at step {step}: not one of "
+ f"{self.n_tracked} tracked trunk parameters changed by any "
+ "amount since step 0.\n\nThis is the reentrant-checkpointing "
+ "incident: `torch.utils.checkpoint` with use_reentrant=True "
+ "silently zeroes trunk gradients under sharding, and the run "
+ "keeps training the embeddings and the LM head while the body "
+ "stays frozen. Loss still falls. Throughput is normal. Nothing "
+ "else detects it.\n\nCheck that activation checkpointing was "
+ "wrapped with use_reentrant=False, that the trunk parameters "
+ "are in the optimizer's param groups, and that requires_grad "
+ f"is set on them. Tracked: {self.tracked_names[:4]}..."
+ )
+ logger.info(
+ "trunk probe ok at step %d: %d/%d moved, max|d|=%.3e",
+ step,
+ moved,
+ self.n_tracked,
+ report["max_abs_delta"],
+ )
+ return report
+
+
+# ---------------------------------------------------------------------------
+# tripwires
+# ---------------------------------------------------------------------------
+
+
+@dataclass
+class TripwireResult:
+ tripped: bool
+ response: str
+ value: float = NAN
+ logps_slope: float = NAN
+ margin_slope: float = NAN
+ detail: str = ""
+
+
+def _slope(values: Sequence[float]) -> float:
+ """Least-squares slope over an evenly spaced window. NaN anywhere in the
+ window makes the slope NaN -- a window with missing data has no trend, and
+ pretending otherwise is how a tripwire fires on absent measurements."""
+ n = len(values)
+ if n < 2 or any(math.isnan(v) for v in values):
+ return NAN
+ xs = list(range(n))
+ mx = sum(xs) / n
+ my = sum(values) / n
+ denom = sum((x - mx) ** 2 for x in xs)
+ if denom == 0:
+ return NAN
+ return sum((x - mx) * (y - my) for x, y in zip(xs, values)) / denom
+
+
+class LogpsChosenTripwire:
+ """Fires when PER-TOKEN chosen NLL rises materially while the margin improves.
+
+ R2 RECALIBRATION (2026-08-23 audit; docs/audits/phase1_postmortem.md).
+ The v1 instrument read raw SUMMED logps/chosen -- corr -0.89 with batch
+ completion length -- and applied a zero-threshold slope test over a
+ 20-raw-row window (~1.25 optimizer steps). On the primary Arm B run it
+ fired 3,049 times; on a SHUFFLED copy of the same series, 2,987 times:
+ zero discriminating power. Its false alarm at micro-step 43 selected the
+ objective of the recorded (destroyed) rerun. By the correct metric --
+ per-token NLL, DPOP's own published diagnostic -- that primary run showed
+ no collapse at all (0.487 -> 0.548 nats/token across 727 steps).
+
+ v2 semantics: per-token chosen NLL (= -logps/chosen / lengths/chosen),
+ baseline median over the first `window` rows vs trailing median over the
+ last `window` rows; fires only when the trailing median exceeds baseline
+ by `nll_rise_nats` (default 0.15 -- healthy phase-1 drift measured 0.06,
+ Smaug's documented collapse is +1.45) AND the margin median has risen.
+ LATCHED: once fired it stays fired and stops logging. The calibration
+ contract is pinned by tests: it must NOT fire on a shuffled copy of a
+ healthy series, and MUST fire on an injected +0.3 nats/token shift.
+ """
+
+ #: The metric keys this tripwire READS, declared so the wiring can be
+ #: checked instead of assumed (THE DISARMED-TRIPWIRE INCIDENT, below).
+ INPUT_KEYS: tuple[str, ...] = ("logps/chosen", "lengths/chosen", "rewards/margins")
+
+ #: The response is PRE-REGISTERED: decided before the run, so a tripped
+ #: alarm produces a recorded configuration change rather than a debate
+ #: about whether the number is real. Recorded as the arm's configuration.
+ PRE_REGISTERED_RESPONSE = (
+ "rerun this arm with DPOP (chosen-logprob floor) or an NLL auxiliary "
+ "term, and record the switch as the arm's configuration"
+ )
+
+ def __init__(self, window: int = 400, nll_rise_nats: float = 0.15):
+ self.window = int(window)
+ self.nll_rise_nats = float(nll_rise_nats)
+ self._baseline_nll: list[float] = []
+ self._baseline_margin: list[float] = []
+ self._nll: list[float] = []
+ self._margin: list[float] = []
+ self.fired = False
+ self._fired_at_row: Optional[int] = None
+ self._rows = 0
+
+ def state_dict(self) -> dict:
+ """Baselines, window and latch (U01-6): primitives only, so resume.pt stays
+ `weights_only`-loadable. Without it every supervised relaunch re-baselined."""
+ return {
+ "baseline_nll": [float(v) for v in self._baseline_nll],
+ "baseline_margin": [float(v) for v in self._baseline_margin],
+ "nll": [float(v) for v in self._nll],
+ "margin": [float(v) for v in self._margin],
+ "fired": bool(self.fired),
+ "fired_at_row": self._fired_at_row,
+ "rows": int(self._rows),
+ }
+
+ def load_state_dict(self, sd: dict) -> None:
+ self._baseline_nll = [float(v) for v in sd["baseline_nll"]]
+ self._baseline_margin = [float(v) for v in sd["baseline_margin"]]
+ self._nll = [float(v) for v in sd["nll"]]
+ self._margin = [float(v) for v in sd["margin"]]
+ self.fired = bool(sd["fired"])
+ fr = sd.get("fired_at_row")
+ self._fired_at_row = None if fr is None else int(fr)
+ self._rows = int(sd["rows"])
+
+ @staticmethod
+ def _median(values: Sequence[float]) -> float:
+ # NaN-poisoning median: any NaN in the window makes the statistic NaN,
+ # so missing data can never fire (blind != healthy != alarm).
+ if any(math.isnan(v) for v in values):
+ return NAN
+ s = sorted(values)
+ n = len(s)
+ return s[n // 2] if n % 2 else 0.5 * (s[n // 2 - 1] + s[n // 2])
+
+ def update(self, logps_chosen: float, lengths_chosen: float, margin: float) -> TripwireResult:
+ self._rows += 1
+ lp, ln = float(logps_chosen), float(lengths_chosen)
+ nll = -lp / ln if (not math.isnan(lp) and not math.isnan(ln) and ln > 0) else NAN
+ m = float(margin)
+
+ if self.fired:
+ return TripwireResult(
+ True,
+ self.PRE_REGISTERED_RESPONSE,
+ detail=f"latched since row {self._fired_at_row}",
+ )
+ if len(self._baseline_nll) < self.window:
+ self._baseline_nll.append(nll)
+ self._baseline_margin.append(m)
+ return TripwireResult(
+ False, "", detail=f"baselining {len(self._baseline_nll)}/{self.window}"
+ )
+ self._nll.append(nll)
+ self._margin.append(m)
+ self._nll = self._nll[-self.window :]
+ self._margin = self._margin[-self.window :]
+ if len(self._nll) < self.window:
+ return TripwireResult(False, "", detail=f"windowing {len(self._nll)}/{self.window}")
+
+ base_nll = self._median(self._baseline_nll)
+ cur_nll = self._median(self._nll)
+ base_m = self._median(self._baseline_margin)
+ cur_m = self._median(self._margin)
+ rise = cur_nll - base_nll
+ tripped = (
+ not math.isnan(rise)
+ and not math.isnan(cur_m)
+ and not math.isnan(base_m)
+ and rise > self.nll_rise_nats
+ and cur_m > base_m
+ )
+ if tripped:
+ self.fired = True
+ self._fired_at_row = self._rows
+ logger.error(
+ "TRIPWIRE: per-token chosen NLL rose %.3f nats/token over baseline "
+ "(%.3f -> %.3f) while the DPO margin rises (%.4f -> %.4f). The "
+ "objective is degrading chosen likelihood in real terms. "
+ "Pre-registered response: %s",
+ rise,
+ base_nll,
+ cur_nll,
+ base_m,
+ cur_m,
+ self.PRE_REGISTERED_RESPONSE,
+ )
+ return TripwireResult(
+ tripped,
+ self.PRE_REGISTERED_RESPONSE if tripped else "",
+ value=rise,
+ logps_slope=rise, # field kept for record-schema compat: nll shift
+ margin_slope=(cur_m - base_m) if not math.isnan(cur_m) else NAN,
+ )
+
+
+class GradNormTripwire:
+ """HALTS the run on a gradient-norm explosion. R3 (2026-08-23 audit).
+
+ grad_norm was logged every optimizer step of both destroyed-run attempts
+ and read by NOTHING: the recorded rerun's first step showed 2,851 vs the
+ primary's 22 (~130x), and the terminal blow-up reached 355,830 with the
+ run continuing 221 steps past the break. A ceiling here would have fired
+ at step 1. Unlike the diagnostic tripwires this one HALTS (HALT=True):
+ training past a gradient explosion spends GPU-hours destroying weights.
+ """
+
+ INPUT_KEYS: tuple[str, ...] = ("grad_norm",)
+ PRE_REGISTERED_RESPONSE = (
+ "halt the run; a gradient explosion is a launch/config defect, not a "
+ "tunable -- diagnose before any rerun"
+ )
+ HALT = True
+ #: `grad_norm` is a TRAINER-produced metric that is legitimately NaN on
+ #: every micro-step without an applied optimizer step (the NaN-reporting
+ #: contract). That is a periodic honest state, not a disarmed feed --
+ #: this wire handles NaN as "blind, cannot fire" itself. The arm-time
+ #: self-test therefore still requires the KEY (name drift stays a launch
+ #: failure) but tolerates NaN for wires that declare this.
+ TOLERATES_NAN_INPUT = True
+
+ def __init__(
+ self,
+ absolute_ceiling: float = 500.0,
+ relative_factor: float = 10.0,
+ window: int = 50,
+ warmup: int = 10,
+ ):
+ self.absolute_ceiling = float(absolute_ceiling)
+ self.relative_factor = float(relative_factor)
+ self.window = int(window)
+ self.warmup = int(warmup)
+ self._history: list[float] = []
+ self.fired = False
+
+ def state_dict(self) -> dict:
+ """Trailing history and latch (U01-6); primitives only."""
+ return {"history": [float(v) for v in self._history], "fired": bool(self.fired)}
+
+ def load_state_dict(self, sd: dict) -> None:
+ self._history = [float(v) for v in sd["history"]]
+ self.fired = bool(sd["fired"])
+
+ def update(self, grad_norm: float) -> TripwireResult:
+ g = float(grad_norm)
+ if self.fired:
+ return TripwireResult(True, self.PRE_REGISTERED_RESPONSE, value=g, detail="latched")
+ if math.isnan(g):
+ return TripwireResult(False, "", value=NAN, detail="NaN grad_norm: blind, cannot fire")
+ history = self._history
+ trailing = sorted(history[-self.window :])
+ med = trailing[len(trailing) // 2] if trailing else NAN
+ over_abs = g > self.absolute_ceiling
+ over_rel = (
+ len(history) >= self.warmup
+ and not math.isnan(med)
+ and med > 0
+ and g > self.relative_factor * med
+ )
+ history.append(g)
+ if over_abs or over_rel:
+ self.fired = True
+ logger.error(
+ "TRIPWIRE (HALT): grad_norm %.1f exceeds %s (trailing median %.2f). %s",
+ g,
+ f"absolute ceiling {self.absolute_ceiling}"
+ if over_abs
+ else f"{self.relative_factor}x trailing median",
+ med,
+ self.PRE_REGISTERED_RESPONSE,
+ )
+ return TripwireResult(True, self.PRE_REGISTERED_RESPONSE, value=g)
+ return TripwireResult(False, "", value=g)
+
+
+class LengthDriftTripwire:
+ """Fires when the chosen-longer fraction leaves a band around the measured
+ baseline for the arm -- but only once it has seen enough pairs to mean it.
+
+ THREE DECLARED STATES, and keeping them distinct is the whole design:
+
+ * BLIND -- input absent or NaN. The guard cannot see AND cannot say
+ so. Forbidden; `assert_tripwires_armed` halts the run.
+ * WARMING UP -- inputs present and finite, cumulative pairs below
+ `min_pairs`. The guard is watching and honestly reports
+ insufficient sample. Valid; the self-test accepts it.
+ * WATCHING -- armed and evaluating.
+
+ Why `min_pairs` exists (sec09's measurement): at micro_batch 2 the
+ per-batch chosen-longer fraction can only take the values {0, 0.5, 1}, two
+ of which sit outside a +-0.10 band around 0.549 -- so the alarm fired on
+ 78% of batches while measuring nothing but the batch size. Their rolling
+ window fixes the estimator; this threshold fixes when it may speak. From
+ their standard-error table: 512 pairs gives 5.4e-06 false-fire per check,
+ 256 gives roughly 4 spurious alarms per Arm-B run. An alarm that fires
+ four times a run is an alarm nobody reads the fifth time.
+
+ The baseline is a config field, never a constant here: Arm A measured
+ 0.549 and Arm B's is different, so a hardcoded threshold would mis-fire on
+ one of them permanently.
+ """
+
+ INPUT_KEYS: tuple[str, ...] = ("chosen_longer_fraction", "pairs")
+
+ RESPONSE = (
+ "length bias suspected: report length-controlled win-rate "
+ "alongside the raw metric and record the drift band that fired"
+ )
+
+ def __init__(self, baseline: float, band: float = 0.10, min_pairs: int = 512):
+ if math.isnan(baseline):
+ raise ValueError(
+ "LengthDriftTripwire needs a measured baseline; NaN means "
+ "nobody measured the arm and the check would be meaningless"
+ )
+ self.baseline = float(baseline)
+ self.band = float(band)
+ self.min_pairs = int(min_pairs)
+ self._pairs_seen = 0
+
+ def state_dict(self) -> dict:
+ return {"pairs_seen": int(self._pairs_seen)}
+
+ def load_state_dict(self, sd: dict) -> None:
+ self._pairs_seen = int(sd["pairs_seen"])
+
+ def state(self) -> str:
+ """`warming_up` until the cumulative sample reaches min_pairs, then
+ `watching`. Reported in the manifest so 'armed' is never ambiguous."""
+ return "watching" if self._pairs_seen >= self.min_pairs else "warming_up"
+
+ def update(self, chosen_longer_fraction: float, n_pairs: int = 1) -> TripwireResult:
+ self._pairs_seen += int(n_pairs)
+ v = float(chosen_longer_fraction)
+ if math.isnan(v):
+ # No measurement is not a drift and not an all-clear.
+ return TripwireResult(False, "", value=NAN, detail="chosen_longer_fraction undefined")
+ if self._pairs_seen < self.min_pairs:
+ return TripwireResult(
+ False, "", value=v, detail=f"warming up: {self._pairs_seen}/{self.min_pairs} pairs"
+ )
+
+ tripped = abs(v - self.baseline) > self.band
+ if tripped:
+ logger.error(
+ "TRIPWIRE: chosen-longer fraction %.3f is outside %.3f +- %.3f over %d pairs",
+ v,
+ self.baseline,
+ self.band,
+ self._pairs_seen,
+ )
+ return TripwireResult(
+ tripped,
+ self.RESPONSE if tripped else "",
+ value=v,
+ detail=f"watching ({self._pairs_seen} pairs)",
+ )
+
+
+def assert_tripwires_armed(metrics: dict, tripwires: dict) -> None:
+ """Every ARMED tripwire's inputs must be present and finite, or halt.
+
+ THE DISARMED-TRIPWIRE INCIDENT. section-05's kernel emits the implicit
+ margin as `rewards/margins`; section-07's Trainer seeded `margin` with NaN
+ and the tripwire read that. Nothing produced the key, `_slope` returns NaN
+ for any window containing one, and the trip condition requires a non-NaN
+ slope -- so the chosen-likelihood-collapse tripwire could never fire while
+ appearing fully wired: constructed, updated every log step, and listed as
+ armed in every manifest. A guard that cannot fire is worse than no guard,
+ because it is also a claim that the failure mode is being watched.
+
+ TWO DIFFERENT CONTRACTS, and conflating them is what caused this:
+
+ * A REPORTED statistic that is undefined must be NaN. `0.0` would read
+ as a real measurement ("no preference learned"), which is a finding
+ rather than a missing number.
+ * A tripwire INPUT must never be NaN-seeded. An armed guard reading NaN
+ is running blind, and blind is indistinguishable from healthy in every
+ log it writes.
+
+ Run at the first log step of any stage with armed tripwires, so metric-name
+ drift becomes a launch failure instead of a silent disarming.
+
+ A tripwire reporting `warming_up` PASSES this check. Its inputs are
+ present and finite; it simply has not accumulated enough sample to speak
+ yet, and it says so. Treating that as a failure would halt every DPO run
+ on its first batch, and the fix for THAT would be to delete the self-test
+ -- which is exactly how the original disarmed tripwire came about.
+ """
+ problems = []
+ for name, wire in tripwires.items():
+ for key in getattr(wire, "INPUT_KEYS", ()):
+ if key not in metrics:
+ problems.append(
+ f"{name}: input {key!r} is ABSENT from the logged metrics "
+ f"(present: {sorted(metrics)[:8]})"
+ )
+ elif (
+ isinstance(metrics[key], float)
+ and math.isnan(metrics[key])
+ and not getattr(wire, "TOLERATES_NAN_INPUT", False)
+ ):
+ problems.append(f"{name}: input {key!r} is NaN -- the guard would run blind")
+ if problems:
+ raise RuntimeError(
+ "ARMED TRIPWIRE SELF-TEST FAILED -- halting before training "
+ "proceeds:\n - "
+ + "\n - ".join(problems)
+ + "\n\nAn armed tripwire whose input is missing or NaN cannot "
+ "fire, and will report itself armed in every manifest anyway. "
+ "Either supply the metric (the loss adapter owns this) or do not "
+ "arm the tripwire for this stage. Canonical margin key: "
+ "'rewards/margins' -- the name section-05's kernel emits."
+ )
+
+
+# ---------------------------------------------------------------------------
+# throughput probe + RSS projection
+# ---------------------------------------------------------------------------
+
+
+def _percentile(sorted_vals: Sequence[float], q: float) -> float:
+ if not sorted_vals:
+ return NAN
+ idx = min(len(sorted_vals) - 1, max(0, int(round(q * (len(sorted_vals) - 1)))))
+ return sorted_vals[idx]
+
+
+def aggregate_micro_step_times(micro_times_s: Sequence[float], grad_accum: int) -> list[float]:
+ """Sum consecutive micro-step durations into OPTIMIZER-step durations.
+
+ The unit bug this exists to kill (task #25): both probes measure
+ per-MICRO durations, while `throughput_report` takes per-OPTIMIZER token
+ counts and horizon. Feeding micro times straight in under-projects
+ wall-clock and over-reports tokens/s by ~grad_accum x (8x on the DPO
+ config) -- a banner that costs GPU-hours wrong by an order of magnitude.
+
+ Only COMPLETE groups are kept: a trailing partial group would read as one
+ impossibly fast optimizer step. Raises when no complete group exists --
+ same contract as throughput_report itself (resolved or refused, never
+ quietly wrong); size probe_steps >= grad_accum at the config level.
+ """
+ if grad_accum <= 0:
+ raise ValueError(f"grad_accum={grad_accum} is not a schedule")
+ times = [float(t) for t in micro_times_s]
+ groups = [
+ sum(times[i : i + grad_accum]) for i in range(0, len(times) - grad_accum + 1, grad_accum)
+ ]
+ if not groups:
+ raise ValueError(
+ f"{len(times)} micro-step times cannot fill one optimizer step at "
+ f"grad_accum={grad_accum}; raise probe_steps to at least grad_accum"
+ )
+ return groups
+
+
+def throughput_report(
+ step_times_s: Sequence[float],
+ tokens_per_step: int,
+ optimizer_steps_planned: int,
+ offload_transfer_s: Optional[float] = None,
+ offload_cpu_step_s: Optional[float] = None,
+) -> dict:
+ """Turn measured step times into the banner's throughput entry.
+
+ Refuses to produce a partial entry: a stage that starts without knowing its
+ step time is a stage committing GPU-hours to a schedule nobody costed. When
+ offload is inactive the offload timings are NaN -- explicitly, because a
+ 0.0 there would read as 'offload is free'.
+ """
+ times = [float(t) for t in step_times_s]
+ if not times:
+ raise ValueError(
+ "throughput_report got no step times. A probe that cannot fill "
+ "its banner keys must raise: the banner's contract is that every "
+ "value is resolved or explicitly NaN, never quietly absent."
+ )
+ if optimizer_steps_planned <= 0:
+ raise ValueError(
+ f"optimizer_steps_planned={optimizer_steps_planned} is not a "
+ "schedule; the projected wall-clock would be meaningless"
+ )
+
+ mean = sum(times) / len(times)
+ ordered = sorted(times)
+ return {
+ "n_steps": len(times),
+ "mean_step_time_s": mean,
+ "p50_step_time_s": _percentile(ordered, 0.50),
+ "p90_step_time_s": _percentile(ordered, 0.90),
+ "tokens_per_s": tokens_per_step / mean if mean > 0 else NAN,
+ "projected_epoch_s": optimizer_steps_planned * mean,
+ "offload_transfer_s": (NAN if offload_transfer_s is None else float(offload_transfer_s)),
+ "offload_cpu_step_s": (NAN if offload_cpu_step_s is None else float(offload_cpu_step_s)),
+ }
+
+
+def projected_job_rss(cfg) -> dict:
+ """Projected host RSS for this job, for the guard and the launch banner.
+
+ Delegates to `guard.projected_full_ft_rss_gb` rather than restating the
+ arithmetic. Two formulas for the same budget WILL drift, and then the
+ banner advertises a number the preflight is not enforcing -- while the
+ preflight is the only thing standing between this job and the 2026-08-12
+ host crash (2GB free, load 619, no swap, a healthy run killed by an NCCL
+ timeout five hours into a stalled checkpoint).
+ """
+ from ..utils.guard import projected_full_ft_rss_gb
+
+ return projected_full_ft_rss_gb(cfg)
diff --git a/code/align/train/quant8.py b/code/align/train/quant8.py
new file mode 100644
index 0000000000000000000000000000000000000000..7282de99450421800c57842e1c8e5b7cb7d7deea
--- /dev/null
+++ b/code/align/train/quant8.py
@@ -0,0 +1,193 @@
+"""Block-wise 8-bit dynamic quantization, in plain torch.
+
+This is the numeric core of `adamw8bit.py` and carries NO optimizer logic, so
+the codebook and the quantize/dequantize round trip can be tested (and
+mis-implemented) on their own. The scheme is Dettmers et al. 2022, "8-bit
+Optimizers via Block-wise Quantization" (arXiv:2110.02861), rebuilt from the
+paper's description with torch only (docs/DEPENDENCY_POLICY.md: bitsandbytes
+is not on the allowed list).
+
+WHY BLOCK-WISE: one absmax for a whole 134M-element embedding shard lets a
+single outlier set the scale for every element, and the other 134M-1 values
+land in the bottom few codes. Normalising each block of `block_size` (default
+2048) elements by its OWN absmax confines an outlier's damage to its block and
+makes the quantization independent of how the tensor happens to be sharded
+across ranks -- which matters, because under FSDP2 the blocks are cut from the
+rank-LOCAL shard.
+
+WHY A DYNAMIC (NON-LINEAR) CODEBOOK: Adam's moments span many decades inside
+one block (the second moment is a square of a heavy-tailed gradient). A
+linear 8-bit grid has uniform ABSOLUTE resolution, so everything below
+~absmax/256 collapses to zero -- and a zero second moment makes the update
+`m / (sqrt(0) + eps)`, i.e. m / 1e-8. The dynamic map spends its 256 codes as
+an exponent (a power of ten, 7 of them) plus a linear fraction inside each
+decade: relative resolution of a few percent from absmax down to ~1e-6*absmax.
+
+THE ONE THING THIS MODULE DOES NOT DO is decide when quantization is safe:
+the caller keeps tensors below `min_8bit_size` in fp32 (small tensors such as
+norm scales are both cheap to keep and the most sensitive to rounding).
+"""
+
+from __future__ import annotations
+
+import functools
+
+import torch
+
+#: 8-bit codes. Not configurable: the code type is `uint8` and the lookup
+#: arithmetic assumes exactly 256 entries.
+N_CODES = 256
+
+
+def create_dynamic_map(signed: bool = True, max_exponent_bits: int = 7, total_bits: int = 8):
+ """The sorted 256-entry dynamic codebook, float32, built like bnb's
+ `create_dynamic_map` (the construction the paper describes).
+
+ Layout: for each exponent `i` in `0..max_exponent_bits-1` the decade
+ `10**(i - (max_exponent_bits-1)) * [0.1, 1]` is cut into linear fraction
+ bins and each bin contributes its MIDPOINT (so no code sits on a decade
+ edge and codes are strictly increasing). Decade `i` gets `2**i` fraction
+ values when signed and `2**(i+1)` when unsigned -- the unsigned map spends
+ the sign bit on one more bit of fraction. Exact `0.0` and `1.0` are then
+ appended. Counting, signed: 2 * (1+2+...+64) + 2 = 256; unsigned:
+ (2+4+...+128) + 2 = 256. Both assertions below fire if someone changes a
+ parameter without redoing the arithmetic.
+
+ Signed map range: [-0.99297, 1.0] (there is no exact -1.0; the most
+ negative code is the midpoint of the top bin, 0.7% from -1). That is the
+ same asymmetry bnb has, and it is bounded: the block's largest-magnitude
+ element, if negative, is stored within 0.7%.
+ Unsigned map range: [0.0, 1.0].
+
+ Built in float64 and cast once, so the table is bit-identical across
+ machines and torch versions (linspace/mean in fp32 is not guaranteed to be).
+ """
+ if total_bits != 8:
+ raise ValueError("create_dynamic_map builds 256-entry (8-bit) maps only")
+ non_sign_bits = total_bits - 1
+ if not 1 <= max_exponent_bits <= non_sign_bits:
+ raise ValueError(f"max_exponent_bits must be in [1, {non_sign_bits}]")
+ additional_items = 2 ** (non_sign_bits - max_exponent_bits) - 1
+
+ data: list[float] = []
+ for i in range(max_exponent_bits):
+ if signed:
+ fraction_items = 2 ** (i + non_sign_bits - max_exponent_bits) + 1
+ else:
+ fraction_items = 2 ** (i + non_sign_bits - max_exponent_bits + 1) + 1
+ boundaries = torch.linspace(0.1, 1.0, fraction_items, dtype=torch.float64)
+ means = (boundaries[:-1] + boundaries[1:]) / 2.0
+ scale = 10.0 ** (-(max_exponent_bits - 1) + i)
+ data += (scale * means).tolist()
+ if signed:
+ data += (-scale * means).tolist()
+ if additional_items > 0:
+ boundaries = torch.linspace(0.1, 1.0, additional_items + 1, dtype=torch.float64)
+ means = (boundaries[:-1] + boundaries[1:]) / 2.0
+ data += means.tolist()
+ if signed:
+ data += (-means).tolist()
+ data.append(0.0)
+ data.append(1.0)
+ data.sort()
+ if len(data) != N_CODES:
+ raise AssertionError(f"dynamic map has {len(data)} entries, expected {N_CODES}")
+ out = torch.tensor(data, dtype=torch.float64).to(torch.float32)
+ if not bool((out[1:] > out[:-1]).all()):
+ raise AssertionError("dynamic map is not strictly increasing in float32")
+ return out
+
+
+@functools.lru_cache(maxsize=None)
+def _cached_map(signed: bool) -> torch.Tensor:
+ return create_dynamic_map(signed=signed)
+
+
+_DEVICE_CACHE: dict = {}
+
+
+def codebook(signed: bool, device) -> torch.Tensor:
+ """The shared codebook on `device` (cached; never mutate the result)."""
+ device = torch.device(device)
+ key = (bool(signed), device.type, device.index)
+ cb = _DEVICE_CACHE.get(key)
+ if cb is None:
+ cb = _cached_map(bool(signed)).to(device)
+ _DEVICE_CACHE[key] = cb
+ return cb
+
+
+def zero_code(signed: bool) -> int:
+ """Index of the exact-0.0 code (the all-zero state initialises to it)."""
+ return int((_cached_map(bool(signed)) == 0.0).nonzero()[0])
+
+
+def nearest_code(x: torch.Tensor, cb: torch.Tensor) -> torch.Tensor:
+ """Nearest-code index of every element of `x` (any shape), as uint8.
+
+ `searchsorted` gives, for each x, the first index `hi` with cb[hi] >= x; the
+ nearest code is then either `hi` or `hi-1`, decided by comparing x against
+ their MIDPOINT (ties go to the lower code). Clamping `hi` to [1, 255]
+ first makes both candidates always exist, and values outside the table
+ (x <= cb[0] or x >= cb[-1]) resolve to the end codes by the same
+ comparison -- no special-casing, so no off-by-one at the edges.
+
+ `out_int32=True`: the index tensor is the largest transient in a chunked
+ step, and int32 halves it. (The final uint8 cast is what is stored.)
+ """
+ flat = x.reshape(-1).contiguous()
+ hi = torch.searchsorted(cb, flat, out_int32=True).clamp_(1, N_CODES - 1)
+ lo = hi - 1
+ mid = (cb[lo] + cb[hi]) * 0.5
+ pick_hi = flat > mid
+ return torch.where(pick_hi, hi, lo).to(torch.uint8).reshape(x.shape)
+
+
+def quantize_rows(x: torch.Tensor, cb: torch.Tensor):
+ """Quantize a `[n_blocks, block_size]` fp32 matrix, one block per row.
+
+ Returns `(codes uint8 [n_blocks, block_size], absmax fp32 [n_blocks])`.
+ An all-zero block has absmax 0; dividing by it would put NaN through the
+ lookup, so the divisor is replaced by 1 there (the block's values are all
+ 0, which then quantize to the zero code and dequantize to 0 * absmax = 0).
+ """
+ absmax = x.abs().amax(dim=1)
+ safe = torch.where(absmax > 0, absmax, torch.ones_like(absmax))
+ normalized = x / safe.unsqueeze(1)
+ return nearest_code(normalized, cb), absmax
+
+
+def dequantize_rows(codes: torch.Tensor, absmax: torch.Tensor, cb: torch.Tensor) -> torch.Tensor:
+ """Inverse of `quantize_rows`: fp32 `[n_blocks, block_size]`.
+
+ The index MUST be a signed integer tensor: `cb[codes]` with a uint8 `codes`
+ is interpreted by torch as a (deprecated) boolean-mask index, not a gather.
+ `index_select` with an int32 index has no such reading.
+ """
+ n_blocks, block_size = codes.shape
+ values = cb.index_select(0, codes.reshape(-1).to(torch.int32)).reshape(n_blocks, block_size)
+ return values * absmax.unsqueeze(1)
+
+
+def quantize_blockwise(x: torch.Tensor, block_size: int = 2048, signed: bool = True):
+ """Quantize any tensor (flattened) -> `(codes uint8 [n_blocks*block_size], absmax)`.
+
+ The final partial block is zero-padded; the pad quantizes to the zero code
+ and is dropped by `dequantize_blockwise(..., numel=)`.
+ """
+ flat = x.detach().reshape(-1).to(torch.float32)
+ n = flat.numel()
+ n_blocks = max(1, -(-n // block_size))
+ padded = torch.zeros(n_blocks * block_size, dtype=torch.float32, device=flat.device)
+ padded[:n] = flat
+ codes, absmax = quantize_rows(padded.view(n_blocks, block_size), codebook(signed, flat.device))
+ return codes.reshape(-1), absmax
+
+
+def dequantize_blockwise(
+ codes: torch.Tensor, absmax: torch.Tensor, numel: int, block_size: int = 2048, signed: bool = True
+) -> torch.Tensor:
+ """Inverse of `quantize_blockwise`, trimmed to `numel` elements (fp32, flat)."""
+ rows = codes.reshape(-1, block_size)
+ out = dequantize_rows(rows, absmax, codebook(signed, codes.device))
+ return out.reshape(-1)[:numel]
diff --git a/code/align/train/qwen3_stage.py b/code/align/train/qwen3_stage.py
new file mode 100644
index 0000000000000000000000000000000000000000..4eaabc3409bcc30d44567c8e71d66b876fadd906
--- /dev/null
+++ b/code/align/train/qwen3_stage.py
@@ -0,0 +1,466 @@
+"""Qwen3 (Goedel-Prover-V2-8B) SFT stage pieces: model build, LoRA train module, weight format.
+
+`scripts/sft.py` dispatches here when `model.arch == "qwen3"`; the dense-Mistral path never
+imports this module. Everything the Trainer needs that is family-specific lives behind the
+two seams the Trainer already has: `weight_format` (what a checkpoint's weight file is) and
+`train_module` (what touches the model).
+
+THE LoRA CHECKPOINT CONTRACT (same as `PPOTrainModule`, for the same reasons):
+
+ * The weight file of every checkpoint is the MERGED bf16 model (base + scaling * B @ A,
+ accumulated in fp32, cast once). Checkpoint saves are how vLLM / transformers / the
+ rollout engine receive the policy, and they load HF base weights, not adapters.
+ * The adapter itself (fp32, exact) rides in the optimizer payload, so a resume is lossless:
+ base is reloaded from the pinned snapshot at launch, the adapter and the 8-bit moments
+ come back from `optimizer_rank{r}.pt`.
+ * `load_weights` on a LoRA resume reads only the safetensors HEADERS (key set + shapes, on
+ the meta device). Materialising the merged 16 GB on each of four ranks would cost 64 GB of
+ host RAM to verify a key set and then be thrown away -- the 2026-08-12 shape.
+
+FSDP2 + frozen base. Per-block `fully_shard` shards the frozen bf16 base (16.4 GB / world) and
+the fp32 adapters together; only adapter gradients are reduce-scattered. Activation
+checkpointing wraps each block. The root unit also owns embed_tokens / lm_head / final norm.
+"""
+
+from __future__ import annotations
+
+import gc
+import logging
+import os
+from typing import Any, Optional
+
+import torch
+
+from .ckpt_format import WeightFormat # noqa: F401 (the Protocol this implements)
+from .train_module import TrainModule
+
+logger = logging.getLogger(__name__)
+
+SUPPORTED_ARCHS = ("mistral", "qwen3")
+
+
+class Qwen3StageError(RuntimeError):
+ """A qwen3 stage config that cannot be launched as written."""
+
+
+# ---------------------------------------------------------------------------
+# Config validation
+# ---------------------------------------------------------------------------
+
+
+def validate_qwen3_cfg(cfg, qcfg) -> None:
+ """Refuse launches whose declared numbers disagree with the snapshot they will load.
+
+ Derived-vs-hardcoded: `model.n_params` feeds the RSS projection and the banner; the
+ architecture truth is the snapshot's config.json. A TOML that still carries the Mistral
+ defaults (7.25B / 32 layers) would project the wrong host footprint and print a banner
+ describing a different model.
+ """
+ from ..model.qwen3 import count_params
+
+ n = count_params(qcfg)
+ problems = []
+ if cfg.model.n_params != n:
+ problems.append(f"model.n_params={cfg.model.n_params} but the snapshot implies {n}")
+ if cfg.model.n_layers != qcfg.n_layers:
+ problems.append(f"model.n_layers={cfg.model.n_layers} but the snapshot has {qcfg.n_layers}")
+ if cfg.model.vocab_size != qcfg.vocab_size:
+ problems.append(f"model.vocab_size={cfg.model.vocab_size} but the snapshot has {qcfg.vocab_size}")
+ if cfg.model.hidden_size != qcfg.hidden_size:
+ problems.append(f"model.hidden_size={cfg.model.hidden_size} but the snapshot has {qcfg.hidden_size}")
+ if cfg.model.precision != "bf16":
+ problems.append(f"model.precision={cfg.model.precision!r}: bf16 only")
+ if problems:
+ raise Qwen3StageError("qwen3 stage config disagrees with its snapshot: " + "; ".join(problems))
+ lo = cfg.lora
+ if lo.enabled:
+ if lo.dropout != 0.0:
+ raise Qwen3StageError(
+ f"lora.dropout={lo.dropout}: align.ppo.lora has no dropout path (train-time and "
+ "score-time logprobs would differ); set 0.0"
+ )
+ if cfg.train.full_ft:
+ raise Qwen3StageError("lora.enabled with train.full_ft=true: LoRA freezes the base; set train.full_ft=false")
+ elif not cfg.train.full_ft:
+ raise Qwen3StageError("train.full_ft=false requires lora.enabled=true (otherwise nothing trains)")
+
+
+def resolve_qwen3(cfg):
+ """(snapshot_dir, Qwen3Config) from `paths.model_snapshot`, with the attention request applied."""
+ from ..model import checkpoint as ckpt
+ from ..model.qwen3_checkpoint import read_qwen3_config
+
+ snapshot = ckpt.resolve_snapshot_dir(cfg.paths.model_snapshot)
+ qcfg = read_qwen3_config(snapshot).replace(attn_implementation=cfg.model.attn_implementation)
+ validate_qwen3_cfg(cfg, qcfg)
+ return snapshot, qcfg
+
+
+# ---------------------------------------------------------------------------
+# Weight format
+# ---------------------------------------------------------------------------
+
+
+class Qwen3Format:
+ """`WeightFormat` for Qwen3: sharded HF safetensors + config.json + tokenizer files."""
+
+ def __init__(self, qcfg, tokenizer_dir: str, lora: bool):
+ self.qcfg = qcfg
+ self.tokenizer_dir = tokenizer_dir
+ self.lora = bool(lora)
+
+ def save_weights(self, state, out_dir, dtype):
+ from ..model.qwen3_checkpoint import save_hf_qwen3
+
+ save_hf_qwen3(dict(state), out_dir, cfg=self.qcfg, dtype=dtype)
+
+ def load_weights(self, path):
+ from ..model.checkpoint import load_hf_safetensors
+ from ..model.qwen3_checkpoint import hf_to_align
+
+ if self.lora:
+ return hf_to_align(_hf_headers_on_meta(path), self.qcfg)
+ return hf_to_align(load_hf_safetensors(path), self.qcfg)
+
+ def copy_tokenizer(self, snapshot, dst_dir): # noqa: ARG002 -- the configured dir is authoritative
+ from ..model.qwen3_checkpoint import copy_tokenizer_files
+
+ return copy_tokenizer_files(self.tokenizer_dir, dst_dir)
+
+
+def _hf_headers_on_meta(path: str) -> dict:
+ """{hf key: empty meta tensor of the stored shape/dtype} for a checkpoint dir. Reads the
+ safetensors headers only -- no tensor data is touched."""
+ import json
+
+ from safetensors import safe_open
+
+ from ..model.checkpoint import INDEX_NAME, WEIGHTS_NAME
+
+ idx = os.path.join(path, INDEX_NAME)
+ if os.path.isfile(idx):
+ with open(idx) as f:
+ files = sorted(set(json.load(f)["weight_map"].values()))
+ elif os.path.isfile(os.path.join(path, WEIGHTS_NAME)):
+ files = [WEIGHTS_NAME]
+ else:
+ raise FileNotFoundError(f"no {WEIGHTS_NAME} or {INDEX_NAME} at {path!r}")
+ out: dict[str, torch.Tensor] = {}
+ for fname in files:
+ with safe_open(os.path.join(path, fname), framework="pt") as f:
+ for k in f.keys():
+ sl = f.get_slice(k)
+ dt = _ST_DTYPES[sl.get_dtype()]
+ out[k] = torch.empty(sl.get_shape(), dtype=dt, device="meta")
+ return out
+
+
+_ST_DTYPES = {"BF16": torch.bfloat16, "F32": torch.float32, "F16": torch.float16}
+
+
+# ---------------------------------------------------------------------------
+# Base identity (a LoRA resume must reattach the adapter to the base it was trained on)
+# ---------------------------------------------------------------------------
+
+
+def base_fingerprint(state) -> str:
+ """sha256 over the frozen base's identity: every non-adapter tensor's name/shape/dtype, every
+ 1-D tensor in full (norm weights differ between fine-tunes of one architecture), and the first
+ and last 8 rows of every matrix (embeddings, lm_head and each projection). Cheap (~tens of MB
+ hashed for 8B) yet distinguishes two bases with the SAME key set and shapes -- the only
+ difference a LoRA resume, which never loads the checkpoint's weights, could otherwise miss.
+ Needs plain (unsharded, non-meta) tensors: a shard-local hash would differ per rank."""
+ import hashlib
+
+ h = hashlib.sha256()
+ for k in sorted(k for k in state if ".adapters." not in k):
+ t = state[k]
+ if hasattr(t, "to_local") or t.device.type == "meta":
+ raise ValueError(f"cannot fingerprint {k!r}: sharded or meta tensor")
+ t = t.detach()
+ h.update(f"{k}|{tuple(t.shape)}|{t.dtype}".encode())
+ parts = [t] if t.dim() <= 1 else [t[:8], t[-8:]]
+ for part in parts:
+ h.update(part.to("cpu").contiguous().reshape(-1).view(torch.uint8).numpy().tobytes())
+ return h.hexdigest()
+
+
+# ---------------------------------------------------------------------------
+# LoRA train module
+# ---------------------------------------------------------------------------
+
+
+class LoRASFTModule(TrainModule):
+ """`TrainModule` whose checkpoints publish merged weights and persist the adapter."""
+
+ def __init__(self, model, loss_adapter, optimizer, grad_accum: int, adapter: str, weight_format=None):
+ super().__init__(model, loss_adapter, optimizer, grad_accum)
+ from ..ppo.lora import has_lora
+
+ if not has_lora(model):
+ raise Qwen3StageError("LoRASFTModule needs a model with injected LoRA layers")
+ self.adapter = adapter
+ if weight_format is not None:
+ self.weight_format = weight_format
+ # Identity of the frozen base this adapter trains on (recorded in the optimizer payload,
+ # checked on resume). `build_qwen3_stage_model` stamps it BEFORE the FSDP wrap, when the
+ # tensors are still whole; a directly built model is fingerprinted here if it can be.
+ fp = getattr(model, "_base_fingerprint", None)
+ if fp is None:
+ try:
+ fp = base_fingerprint(model.state_dict())
+ except ValueError as e:
+ logger.warning("LoRA base fingerprint unavailable (%s); a resume cannot verify its base", e)
+ self.base_fingerprint: Optional[str] = fp
+
+ def full_model_state_dict(self):
+ """BASE-keyed weights with the adapter FOLDED IN (see the module docstring).
+
+ `get_model_state_dict(full_state_dict=True, cpu_offload=True)` returns the full dict on
+ RANK 0 ONLY; every other rank gets `{}`. The gather is the collective every rank
+ joins; merging an empty dict is a KeyError on ranks 1..N-1 at the FIRST save, which
+ strands rank 0 in the next collective until the watchdog kills the run.
+ """
+ import torch.distributed as dist
+
+ from ..ppo.lora import export_merged_state_dict
+
+ sd = super().full_model_state_dict()
+ if not sd and dist.is_available() and dist.is_initialized() and dist.get_rank() != 0:
+ return sd
+ self.last_export_report: dict = {} # retained-delta fidelity of the bf16 cast (warned on when poor)
+ return export_merged_state_dict(self.model, sd, self.adapter, report=self.last_export_report)
+
+ def load_model_state_dict(self, nmo_state: dict) -> None:
+ """LoRA resume: the checkpoint's weights are MERGED and are deliberately NOT loaded --
+ the frozen base was reloaded from the snapshot at launch, and loading merged weights
+ into it would double-count the delta once the adapter is restored from the optimizer
+ payload. The key set is still verified so a checkpoint of another architecture cannot
+ resume silently."""
+ base_keys = {k for k in self.model.state_dict() if ".adapters." not in k}
+ missing, unexpected = sorted(base_keys - set(nmo_state)), sorted(set(nmo_state) - base_keys)
+ if missing or unexpected:
+ raise KeyError(f"resume state mismatch: missing={missing[:5]} unexpected={unexpected[:5]}")
+ logger.info("LoRA resume: merged weights verified (%d tensors) and NOT loaded; adapter comes from the optimizer payload", len(base_keys))
+
+ def optim_state_dict(self) -> dict:
+ from ..ppo.lora import lora_state_dict
+
+ lora = lora_state_dict(self.model, self.adapter)
+ # The ZeRO-3 save defect, structurally: a sharded placeholder serialises as an EMPTY
+ # tensor and the file loads without error while being untrained.
+ empty = [k for k, v in lora.items() if v.numel() == 0]
+ if empty:
+ raise RuntimeError(
+ f"{len(empty)} adapter tensors are empty (e.g. {empty[:2]}): a sharded placeholder was "
+ "serialised instead of the gathered parameter. Refusing to write a checkpoint that "
+ "loads without error and is untrained."
+ )
+ return {"optimizer": self.optimizer.state_dict(), "lora": lora, "base_fingerprint": self.base_fingerprint}
+
+ def load_optim_state_dict(self, sd: dict) -> None:
+ from ..ppo.lora import load_lora_state_dict
+
+ if "lora" not in sd or "optimizer" not in sd:
+ raise KeyError(
+ "checkpoint optimizer payload lacks 'lora'/'optimizer'; resuming would restart the "
+ "policy from the bare base while claiming a resume"
+ )
+ # THE UNBOUND-ADAPTER DEFECT. The checkpoint's merged weights are not loaded, so nothing
+ # else ties this adapter to the base reloaded at launch: a repointed model_snapshot or a
+ # second HF-cache snapshot (same keys, same shapes) would resume "fine" on the wrong base.
+ saved = sd.get("base_fingerprint")
+ if saved is None or self.base_fingerprint is None:
+ logger.warning(
+ "LoRA resume cannot verify the base: base fingerprint missing (checkpoint=%s, this launch=%s)",
+ saved is not None, self.base_fingerprint is not None,
+ )
+ elif saved != self.base_fingerprint:
+ raise RuntimeError(
+ "LoRA resume: the base model loaded at launch is not the base this adapter was trained on "
+ f"(fingerprint {self.base_fingerprint[:12]} != checkpoint {saved[:12]}). Check "
+ "paths.model_snapshot and the HF cache for a second snapshot."
+ )
+ self.optimizer.load_state_dict(sd["optimizer"])
+ load_lora_state_dict(self.model, self.adapter, sd["lora"], strict=True)
+
+
+# ---------------------------------------------------------------------------
+# Model build
+# ---------------------------------------------------------------------------
+
+
+def _vm_rss_gb(field: str = "VmRSS") -> float:
+ with open("/proc/self/status") as f:
+ for line in f:
+ if line.startswith(field + ":"):
+ return float(line.split()[1]) / 1024**2
+ return float("nan")
+
+
+def build_qwen3_stage_model(cfg, attn_implementation: Optional[str] = None):
+ """Rank-serial load -> GPU -> (LoRA inject, freeze base) -> activation checkpointing -> FSDP2.
+
+ RANK-SERIAL because each rank materialises the 16.4 GB bf16 state dict on the host before
+ moving it to its card; four at once is 66 GB on a no-swap box. Ranks load one at a time
+ (barrier-ordered) and return freed heap to the OS (`malloc_trim`) before the next starts.
+ """
+ from ..model import qwen3 as q3
+ from ..model.qwen3_checkpoint import hf_to_align
+ from ..model.checkpoint import load_hf_safetensors
+ from ..ppo.lora import LoRAConfig, inject_lora, mark_only_adapter_trainable
+ from ..utils.dist import barrier, local_rank, rank, world_size
+ from . import fsdp
+
+ snapshot, qcfg = resolve_qwen3(cfg)
+ if attn_implementation:
+ qcfg = qcfg.replace(attn_implementation=attn_implementation)
+ device = torch.device("cuda", local_rank())
+ torch.cuda.set_device(device)
+
+ model = None
+ peaks: dict[str, Any] = {}
+ for r in range(world_size()):
+ if rank() == r:
+ sd = hf_to_align(load_hf_safetensors(snapshot, dtype=torch.bfloat16), qcfg)
+ model = q3.Qwen3ForCausalLM.from_state_dict(qcfg, sd, device="cuda")
+ del sd
+ model = model.to(device)
+ peaks["rss_hwm_gb"] = round(_vm_rss_gb("VmHWM"), 2) # high-water mark of the load, not the residual
+ gc.collect()
+ try:
+ import ctypes
+
+ ctypes.CDLL("libc.so.6").malloc_trim(0)
+ except (OSError, AttributeError):
+ pass
+ peaks["rss_after_trim_gb"] = round(_vm_rss_gb(), 2)
+ barrier()
+
+ if cfg.lora.enabled:
+ model._base_fingerprint = base_fingerprint(model.state_dict()) # whole tensors, before inject/FSDP
+ lc = LoRAConfig(r=cfg.lora.r, alpha=cfg.lora.alpha, target_modules=tuple(cfg.lora.target_modules), rslora=cfg.lora.rslora)
+ # Identical adapter init on every rank: FSDP2 shards what each rank already holds and
+ # does NOT broadcast, so a per-rank random A would be a different adapter per shard.
+ gen = torch.Generator().manual_seed(cfg.train.seed)
+ hit = inject_lora(model, lc, adapter=cfg.lora.adapter, generator=gen)
+ n_trainable = mark_only_adapter_trainable(model, cfg.lora.adapter)
+ expected = len(cfg.lora.target_modules) * qcfg.n_layers
+ if len(hit) != expected:
+ raise Qwen3StageError(f"LoRA hit {len(hit)} layers, expected {expected} ({len(cfg.lora.target_modules)} targets x {qcfg.n_layers} layers)")
+ peaks["lora_trainable_params"] = n_trainable
+ fsdp.apply_activation_checkpointing(model)
+ if world_size() > 1:
+ fsdp.apply_fsdp(model)
+ model._stage_load_stats = peaks # for the banner / probe records
+ return model
+
+
+def qwen3_trainer_extras(cfg, model, loss_adapter, run_dir: str) -> dict:
+ """The Trainer kwargs a qwen3 stage adds: weight_format, per-rank tracker, and for LoRA the
+ train_module and the probe view.
+
+ PER-RANK TRACKER. The Trainer's default constructs a `Tracker` on EVERY rank, all appending
+ to the same `/history.jsonl`: four interleaved streams with no rank field, so
+ "did each rank see different rows?" cannot be answered from the record (and torn
+ interleaving is one fsync away). Rank 0 keeps ``; rank r writes `/rank{r}`.
+ """
+ from ..utils.dist import rank as _rank
+ from ..utils.tracking import Tracker
+ from .offload_adamw import make_optimizer
+ from .trainer import MANDATORY_METRICS
+
+ snapshot, qcfg = resolve_qwen3(cfg)
+ r = _rank()
+ extras: dict = {
+ "weight_format": Qwen3Format(qcfg, tokenizer_dir=snapshot, lora=cfg.lora.enabled),
+ "tracker": Tracker(run_dir if r == 0 else os.path.join(run_dir, f"rank{r}"), "sft",
+ mandatory_keys=MANDATORY_METRICS, run_name=cfg.run_name),
+ }
+ if not cfg.lora.enabled:
+ return extras
+ optimizer = make_optimizer(cfg, [p for p in model.parameters() if p.requires_grad])
+ extras["train_module"] = LoRASFTModule(
+ model, loss_adapter, optimizer, cfg.optimizer.grad_accum, cfg.lora.adapter,
+ weight_format=extras["weight_format"],
+ )
+ from ..ppo.trainer import AdapterProbeView
+
+ extras["probe_model"] = AdapterProbeView(model, cfg.lora.adapter)
+ return extras
+
+
+def qwen3_entry_banner(cfg, ledger) -> dict:
+ """The qwen3 counterpart of `stage_gates.run_entry_gates`: what can be verified WITHOUT the
+ Mistral-specific machinery, and an explicit PENDING for what cannot.
+
+ NOT APPLICABLE here, and why (each is a Mistral/PRM800K artifact, not a skipped check):
+ * golden regression + MATH-500/GSM8K quality gate: they certify the Mistral eval stack
+ against a Mistral checkpoint's recorded score. The qwen3 experiment's evaluation is the
+ Lean verifier loop (P2-B/C).
+ * the 10-step 7B smoke / 20-step probe: `build_stage_model` is Mistral-only. The qwen3
+ memory and throughput are measured by scripts/qwen3_sft_probe.py and recorded in
+ docs; the banner says PENDING rather than quoting a number this launch did not measure.
+ KEPT: batch arithmetic, shard receipts (hashes into the banner), the RSS projection, the
+ sampler-derived scheduler horizon, the snapshot-vs-config agreement.
+ """
+ from ..utils.guard import projected_full_ft_rss_gb
+ from .stage_gates import PENDING, assemble_sft_banner, assert_batch_arithmetic_consistent
+ from .stage_smoke import build_training_sampler, resolve_split_dir, sampler_derived_horizon
+
+ snapshot, qcfg = resolve_qwen3(cfg)
+ assert_batch_arithmetic_consistent(cfg)
+ sampler, dataset = build_training_sampler(cfg)
+ horizon = sampler_derived_horizon(cfg, sampler)
+ # THE OVERLONG-ROW DEFECT. The length-grouped sampler budgets tokens, but a row longer than the
+ # budget is not rejected: it becomes a batch of one and trains. seq_len is the length the
+ # memory envelope was MEASURED at (scripts/qwen3_sft_probe.py); a longer row would be the
+ # first thing to run at an untested length, on the last GPU-hour of an epoch.
+ lengths = dataset.lengths()
+ over = int((lengths > cfg.train.seq_len).sum())
+ if over:
+ raise Qwen3StageError(
+ f"{over} of {len(lengths)} rows are longer than train.seq_len={cfg.train.seq_len} "
+ f"(longest {int(lengths.max())}). Filter them at data-build time or raise seq_len to a "
+ "length that has been measured; the sampler would otherwise train each as a lone batch."
+ )
+ split_dir = resolve_split_dir(cfg.data.shard_dir, cfg.data.sft_split)
+ import json
+
+ hashes = {}
+ for rp in sorted(split_dir.glob("*.receipt.json")):
+ with open(rp) as f:
+ r = json.load(f)
+ hashes[r["name"]] = r["content_hash"]
+ if not hashes:
+ raise Qwen3StageError(f"no shard receipts under {split_dir}")
+ banner = assemble_sft_banner(
+ cfg,
+ attention_backend=qcfg.attn_implementation or "auto(sdpa)",
+ optimizer_impl=f"{cfg.optimizer.name} lora={'on r=%d alpha=%g' % (cfg.lora.r, cfg.lora.alpha) if cfg.lora.enabled else 'off'}",
+ dataset_hashes=hashes,
+ ceiling_results={"lean_verifier": PENDING, "note": "qwen3 stage: evaluated by the Lean loop (P2), not MATH/GSM8K"},
+ probe_report=None,
+ rss_projection=projected_full_ft_rss_gb(cfg),
+ mde={"note": PENDING},
+ scheduler_horizon_optimizer_steps=horizon,
+ scheduler_horizon_rows_derived=None,
+ )
+ banner.update({"arch": cfg.model.arch, "snapshot": snapshot, "n_rows": len(dataset), "seq_len": cfg.train.seq_len})
+ # The Mistral gate ledger enforces the golden->smoke->probe order, which does not apply here
+ # (recording a pass for a gate that did not run would be a lie), so the entry is persisted as
+ # its own receipt next to the ledger instead.
+ from ..utils.dist import is_rank0
+
+ if is_rank0():
+ tmp = os.path.join(ledger.run_dir, "qwen3_entry.json.partial")
+ with open(tmp, "w") as f:
+ json.dump(banner, f, indent=1, sort_keys=True, default=str)
+ os.replace(tmp, os.path.join(ledger.run_dir, "qwen3_entry.json"))
+ return banner
+
+
+__all__ = [
+ "SUPPORTED_ARCHS", "Qwen3StageError", "base_fingerprint", "Qwen3Format", "LoRASFTModule", "validate_qwen3_cfg",
+ "resolve_qwen3", "build_qwen3_stage_model", "qwen3_trainer_extras", "qwen3_entry_banner",
+]
diff --git a/code/align/train/ref_cache.py b/code/align/train/ref_cache.py
new file mode 100644
index 0000000000000000000000000000000000000000..5e534c4cfe7742f5aad476bbd10d3fc84222ee52
--- /dev/null
+++ b/code/align/train/ref_cache.py
@@ -0,0 +1,834 @@
+"""Reference log-probabilities: precompute, cache, join, and the path-bias rule.
+
+DPO needs, for every pair, what the FROZEN reference policy thought of the
+chosen and rejected completions. The textbook implementation keeps a second
+7B model resident and forwards it alongside the policy. That does not fit:
+14.5 GB of policy weights plus optimizer shards plus activations already fills
+a 23 GB A10G, and a second copy fills a second one for no gradient.
+
+So the reference is computed once, offline, and consumed as constants. The
+whole arrangement rests on one property -- with dropout disabled, a no-grad
+pass over a fixed dataset yields the same numbers every time -- and on one
+discipline: the cache must be keyed on EVERYTHING that could change those
+numbers, because every failure mode of a stale cache is silent. A cache from
+the wrong checkpoint, the wrong tokenizer, or the pre-rebuild shards still
+produces a finite log-sigmoid of a difference of plausible numbers. The loss
+decreases. The margin it reports is a margin against something that does not
+exist.
+
+The key therefore carries six components, and each one is a way somebody has
+or could break this:
+
+ * `checkpoint_hash` -- the obvious one; a cache from the previous SFT run.
+ Must be a CONTENT hash (`checkpoint_content_hash`): the tensor-inventory
+ hash cannot tell two SFT checkpoints of one architecture apart.
+ (A seventh, DIAGNOSTIC field, `checkpoint_inventory_hash`, carries the
+ inventory hash beside it: it is compared like the rest, but its only job
+ is to make a `checkpoint_hash` mismatch legible -- inventory equal means
+ "same architecture, different weights", the stale-SFT-attempt case.)
+ * `tokenizer_hash` -- retokenized pairs are different pairs.
+ * `manifest_hash` -- the one the plan did not list and this project needs
+ most: section-03's shards are being REBUILT (the duplicated `# Answer`
+ marker fix). A rebuild changes every pair's tokens while leaving the
+ checkpoint, the tokenizer and max_length identical, so nothing else in the
+ key moves.
+ * `max_length` and `truncation_mode` -- a completion truncated differently
+ has a different summed logprob, and summed logprobs are what DPO compares.
+ * `forward_impl` -- a single-GPU forward and an FSDP2-sharded forward are
+ not bitwise identical. That is the PATH-BIAS risk, and it is measured
+ (`path_bias_decision`) rather than assumed away, because early in DPO the
+ policy IS the reference and the loss argument sits at zero, which is
+ exactly where a systematic reference bias does the most damage.
+
+EXISTING CACHES MUST BE RE-PRECOMPUTED (2026-10-12). Before this date the
+launchers filled `checkpoint_hash` from the tensor-inventory hash
+(`checkpoint_fingerprint()["manifest_sha256"]`). Every cache written that way
+carries a key that can no longer be reproduced from a checkpoint's weights, so
+it is refused by `load_ref_cache(expect=...)` -- correctly, because it could not
+have told its own checkpoint from any sibling's. The refusal names
+`checkpoint_hash`; the fix is to re-run scripts/precompute_ref_logprobs.py, not
+to loosen the comparison. Both launchers now build the key through
+`build_ref_cache_key`, the single place the six facts are assembled.
+"""
+
+from __future__ import annotations
+
+import hashlib
+import json
+import logging
+import math
+import os
+from dataclasses import asdict, dataclass, field, fields
+from typing import Any, Iterable, Optional, Sequence
+
+import torch
+
+logger = logging.getLogger(__name__)
+
+NAN = float("nan")
+
+#: One entry per pair. Lengths are carried because `sigmoid_norm` divides by
+#: them; a length of 0 (a prompt-only row) must make the normalised quantity
+#: NaN rather than raise a ZeroDivisionError three weeks later.
+ENTRY_FIELDS = ("ref_logp_chosen", "ref_logp_rejected", "len_chosen", "len_rejected")
+
+#: How the pass actually ran. Not a preference -- a record.
+FORWARD_IMPLS = ("single_gpu", "fsdp2_sharded")
+
+
+@dataclass(frozen=True)
+class RefCacheKey:
+ """Everything that could silently change a cached reference logprob.
+
+ Frozen and compared component-by-component so a mismatch names the guilty
+ field. `RuntimeError: ref cache mismatch` with no further detail sends
+ somebody hunting through three sections; `mismatched: manifest_hash
+ (cache=man-abc, run=man-xyz)` sends them to the shard rebuild.
+ """
+
+ checkpoint_hash: str
+ tokenizer_hash: str
+ manifest_hash: str
+ max_length: int
+ truncation_mode: str
+ forward_impl: str
+ #: DIAGNOSTIC companion to `checkpoint_hash` (see the module docstring).
+ #: Empty only on caches written before 2026-10-12, which are refused on
+ #: `checkpoint_hash` anyway.
+ checkpoint_inventory_hash: str = ""
+
+ def __post_init__(self) -> None:
+ if self.forward_impl not in FORWARD_IMPLS:
+ raise ValueError(
+ f"forward_impl={self.forward_impl!r} must be one of "
+ f"{FORWARD_IMPLS}: it records how the pass ACTUALLY ran, and "
+ "an unrecognised tag cannot be compared against the run's."
+ )
+
+ def to_json(self) -> str:
+ return json.dumps(asdict(self), sort_keys=True)
+
+ @classmethod
+ def from_json(cls, blob: str) -> "RefCacheKey":
+ return cls(**json.loads(blob))
+
+ def mismatches(self, other: "RefCacheKey") -> list[str]:
+ return [f.name for f in fields(self) if getattr(self, f.name) != getattr(other, f.name)]
+
+
+@dataclass
+class RefCache:
+ key: RefCacheKey
+ #: pair_id -> (ref_logp_chosen, ref_logp_rejected, len_chosen, len_rejected)
+ entries: dict[str, tuple[float, float, float, float]] = field(default_factory=dict)
+
+ def __len__(self) -> int:
+ return len(self.entries)
+
+ def require(self, pair_id: str) -> tuple[float, float, float, float]:
+ """Look up a pair, or raise. There is no default: a zero reference
+ turns DPO into likelihood maximisation on the chosen side, which
+ trains and converges and is not DPO."""
+ try:
+ return self.entries[pair_id]
+ except KeyError:
+ raise KeyError(
+ f"pair_id {pair_id!r} is not in the reference cache "
+ f"({len(self.entries)} entries, key {self.key.to_json()}). "
+ "Refusing to substitute a default reference logprob. Either "
+ "the precompute did not cover this split, or these shards are "
+ "not the ones it ran over."
+ ) from None
+
+
+def build_ref_cache_key(
+ sft_checkpoint: str, manifest: dict, *, forward_impl: str
+) -> RefCacheKey:
+ """The ONE place a launcher turns facts into a `RefCacheKey`.
+
+ Both scripts/precompute_ref_logprobs.py (writer) and scripts/dpo.py
+ (reader) call this, so the key they compare cannot drift apart again. The
+ drift it replaces: each script assembled the key inline and BOTH filled
+ `checkpoint_hash` from the tensor-inventory hash (2026-10-12 review), so
+ two different-weight SFT checkpoints produced the same key. Here
+ `checkpoint_hash` is the WEIGHT-BYTES hash (`checkpoint_content_hash`; one
+ streamed read of the checkpoint, ~14.5GB at 7B) and the inventory hash
+ rides along as `checkpoint_inventory_hash`.
+
+ `manifest_hash` covers the shard rebuild nothing else in the key notices;
+ `forward_impl` is a record of how the pass ran and is the caller's to state.
+ """
+ from ..data.receipts import content_hash
+
+ return RefCacheKey(
+ checkpoint_hash=checkpoint_content_hash(sft_checkpoint),
+ tokenizer_hash=manifest["tokenizer_hash"],
+ manifest_hash=content_hash(
+ [manifest["hashes"][k] for k in sorted(manifest.get("hashes", {}))]
+ ),
+ max_length=int(manifest.get("pinned", {}).get("max_len", 2048)),
+ truncation_mode="right",
+ forward_impl=forward_impl,
+ checkpoint_inventory_hash=checkpoint_inventory_hash(sft_checkpoint),
+ )
+
+
+def peek_ref_cache_key(path: str) -> Optional[RefCacheKey]:
+ """The key stored in a cache file's header, or None when there is no file.
+
+ INSPECTION ONLY -- it validates nothing about the cache. It exists so a
+ launcher can read the one component that is a RECORD rather than an
+ expectation (`forward_impl`: how the cache's own pass ran) from the cache
+ itself, build the expected key around it, and then `load_ref_cache(path,
+ expect=...)`, which asserts the other components. Never a substitute for
+ that load.
+ """
+ from safetensors import safe_open
+
+ if not os.path.isfile(path):
+ return None
+ with safe_open(path, framework="pt") as f:
+ meta = f.metadata() or {}
+ if "key" not in meta:
+ return None
+ return RefCacheKey.from_json(meta["key"])
+
+
+#: What `cache_forward_impl` reads when the report is written before any cache
+#: exists. Not a member of FORWARD_IMPLS on purpose: it can never equal a real
+#: cache's tag, so nothing can mistake "no cache yet" for a measured pass.
+NO_CACHE_YET = "no_cache_at_report_time"
+
+
+def path_bias_report_cache_forward_impl(cache_path: str) -> str:
+ """The `forward_impl` a path-bias report should record as `cache_forward_impl`.
+
+ The report's field is about the CACHE the training run will consume; the
+ `--path-bias` run measures it but does not recompute it. So the value is
+ the on-disk train cache's own tag, or `NO_CACHE_YET` when the measurement
+ was taken first. The launcher does not read this field back for its
+ expectation (it reads the cache), so this is a record, not a lever.
+ """
+ key = peek_ref_cache_key(cache_path)
+ return key.forward_impl if key is not None else NO_CACHE_YET
+
+
+def _checkpoint_files(path: str) -> list[str]:
+ """The weight files of a checkpoint, in the order they are hashed.
+
+ A `.safetensors` file, a directory holding `model.safetensors`, or a
+ sharded directory (index + shards, sorted by name).
+ """
+ from ..model.checkpoint import INDEX_NAME, WEIGHTS_NAME
+
+ if os.path.isfile(path):
+ files = [path]
+ else:
+ single = os.path.join(path, WEIGHTS_NAME)
+ index = os.path.join(path, INDEX_NAME)
+ if os.path.isfile(single):
+ files = [single]
+ elif os.path.isfile(index):
+ with open(index) as f:
+ shards = sorted(set(json.load(f)["weight_map"].values()))
+ files = [os.path.join(path, s) for s in shards]
+ missing = [s for s in files if not os.path.isfile(s)]
+ if missing:
+ raise FileNotFoundError(f"{INDEX_NAME} names shard(s) absent from {path!r}: {missing}")
+ else:
+ raise FileNotFoundError(f"no {WEIGHTS_NAME} or {INDEX_NAME} at {path!r}")
+ return files
+
+
+def checkpoint_inventory_hash(path: str) -> str:
+ """Hash of the tensor INVENTORY (name, shape, dtype) read from the safetensors
+ HEADERS only -- no tensor data is materialised.
+
+ This is the DIAGNOSTIC companion to `checkpoint_content_hash`, not a
+ substitute: it is exactly the kind of hash that cannot distinguish two
+ checkpoints of one architecture, which is its use -- when `checkpoint_hash`
+ mismatches and this one does not, the cache is from the same architecture
+ with different weights (the stale-SFT-attempt case). Deliberately NOT
+ `checkpoint_fingerprint()["manifest_sha256"]`: that routes through
+ `load_hf_safetensors`, which reads the whole 14.5GB into RAM on every rank
+ just to print shapes -- four ranks of it at gate 3 on a no-swap box.
+ """
+ from safetensors import safe_open
+
+ inventory = {}
+ for fp in _checkpoint_files(path):
+ with safe_open(fp, framework="pt") as f:
+ for k in f.keys():
+ sl = f.get_slice(k)
+ inventory[k] = [list(sl.get_shape()), str(sl.get_dtype())]
+ return hashlib.sha256(json.dumps(inventory, sort_keys=True).encode()).hexdigest()[:16]
+
+
+def checkpoint_content_hash(path: str) -> str:
+ """Hash of the checkpoint's WEIGHT BYTES: the value `RefCacheKey.checkpoint_hash` must carry.
+
+ WHY THIS EXISTS (review of 2026-10-12). The launchers used to fill
+ `checkpoint_hash` from `checkpoint_fingerprint(path)["manifest_sha256"]`,
+ which hashes only the tensor inventory (name, shape, dtype). Every full-FT
+ SFT checkpoint of one architecture has the SAME inventory, so a cache from
+ a previous SFT attempt, or from micro_00000100 when the gate passed
+ micro_00000200, produced an identical key and loaded without complaint --
+ the exact stale-cache failure this key is documented to defend against
+ ("a cache from the previous SFT run"). The receipt module says so itself
+ (`weights_sha256` is the content hash; `manifest_sha256` is a structure
+ hash), and `test_fingerprint_does_not_call_a_structure_hash_a_content_hash`
+ pins it. Reading ~14.5GB once per launch is the price of the key meaning
+ what it says; it is streamed in 64MB blocks and never held in memory.
+
+ Accepts a `.safetensors` file, a directory holding `model.safetensors`, or
+ a sharded directory (index + shards). Shard names are hashed together with
+ their bytes, in sorted order, so a renamed or reordered shard set changes
+ the hash too.
+ """
+ files = _checkpoint_files(path)
+ h = hashlib.sha256()
+ for fp in files:
+ h.update(os.path.basename(fp).encode() + b"\0")
+ with open(fp, "rb") as fh:
+ for block in iter(lambda: fh.read(64 << 20), b""):
+ h.update(block)
+ return h.hexdigest()[:16]
+
+
+def _atomic_write(path: str, write) -> None:
+ """Run `write(tmp_path)` then `os.replace` onto `path`.
+
+ A cache or report written straight to its final name and killed mid-write
+ (the host has seized mid-checkpoint before) leaves a truncated file at the
+ name the next launch trusts, and REPLACES a previously good one. The temp
+ file sits beside the target (same filesystem, on /apps) so the rename is
+ atomic, and is removed if the write raises.
+ """
+ tmp = f"{path}.tmp.{os.getpid()}"
+ try:
+ write(tmp)
+ os.replace(tmp, path)
+ except BaseException:
+ if os.path.exists(tmp):
+ os.remove(tmp)
+ raise
+
+
+# ---------------------------------------------------------------------------
+# I/O
+# ---------------------------------------------------------------------------
+
+
+def write_ref_cache(path: str, cache: RefCache) -> str:
+ """One safetensors file per (arm, split): tensors for the values, the key
+ and the pair-id order in the metadata.
+
+ One file, not a tensor file plus a JSON sidecar, because a cache split
+ across two files can half-exist -- and a half-existing cache is the
+ resumable-looking partial checkpoint problem in another costume.
+ """
+ from safetensors.torch import save_file
+
+ from ..utils.guard import assert_paths_off_root
+
+ assert_paths_off_root([os.path.dirname(os.path.abspath(path))])
+ pair_ids = list(cache.entries)
+ cols = list(zip(*(cache.entries[p] for p in pair_ids))) if pair_ids else [(), (), (), ()]
+ # float64 throughout: the values arrive as fp32 (the log-softmax is upcast
+ # even in a bf16 forward) and float32 -> float64 is exact, so the stored
+ # number IS the computed number. Storing them as fp32 would be equally
+ # exact today and would silently truncate if the compute dtype ever rose.
+ tensors = {
+ name: torch.tensor(col, dtype=torch.float64) for name, col in zip(ENTRY_FIELDS, cols)
+ }
+ from ..data.loader import PREFERENCE_MASK_VERSION
+
+ _atomic_write(
+ path,
+ lambda tmp: save_file(
+ tensors,
+ tmp,
+ metadata={
+ "key": cache.key.to_json(),
+ "pair_ids": json.dumps(pair_ids),
+ "mask_version": PREFERENCE_MASK_VERSION,
+ },
+ ),
+ )
+ logger.info("ref cache written: %s (%d pairs)", path, len(pair_ids))
+ return path
+
+
+def load_ref_cache(path: str, expect: Optional[RefCacheKey] = None) -> RefCache:
+ """Load a cache and, when `expect` is given, ASSERT the key.
+
+ Loading without `expect` is for inspection tools only. Every training path
+ passes it; that assertion is the entire defence against a stale cache.
+ """
+ from safetensors import safe_open
+
+ if not os.path.isfile(path):
+ raise FileNotFoundError(
+ f"reference cache {path!r} does not exist. Run "
+ "scripts/precompute_ref_logprobs.py for this arm first; DPO "
+ "cannot proceed without it and will not invent one."
+ )
+ with safe_open(path, framework="pt") as f:
+ meta = f.metadata() or {}
+ cols = {name: f.get_tensor(name) for name in ENTRY_FIELDS}
+ if "key" not in meta or "pair_ids" not in meta:
+ raise RuntimeError(
+ f"{path!r} carries no cache key. An unkeyed cache cannot be shown "
+ "to belong to this checkpoint, tokenizer and shard build, so it is "
+ "refused rather than trusted."
+ )
+ from ..data.loader import PREFERENCE_MASK_VERSION
+
+ cache_mask = meta.get("mask_version", "prompt_len_v1")
+ if cache_mask != PREFERENCE_MASK_VERSION:
+ raise RuntimeError(
+ f"{path!r} was precomputed under completion-mask semantics "
+ f"{cache_mask!r} but this build uses {PREFERENCE_MASK_VERSION!r} "
+ "(audit R1, 2026-08-23: the boundary moved from prompt_len to "
+ "shared_prefix_len). Reference logprobs are mask-dependent; "
+ "recompute the cache rather than mixing semantics."
+ )
+ key = RefCacheKey.from_json(meta["key"])
+ if expect is not None:
+ bad = key.mismatches(expect)
+ if bad:
+ detail = "; ".join(
+ f"{n} (cache={getattr(key, n)!r}, run={getattr(expect, n)!r})" for n in bad
+ )
+ legacy = ""
+ if "checkpoint_hash" in bad and not key.checkpoint_inventory_hash:
+ legacy = (
+ " This cache carries no checkpoint_inventory_hash: it predates "
+ "the 2026-10-12 content-hash key, whose checkpoint_hash was a "
+ "tensor-inventory hash that cannot identify a checkpoint. It "
+ "must be re-precomputed."
+ )
+ raise RuntimeError(
+ f"reference cache {path!r} does not match this run. "
+ f"Mismatched: {detail}.{legacy} A stale reference cache does not fail "
+ "loudly at train time -- the loss stays finite, the curve "
+ "stays healthy, and the margin is measured against a policy "
+ "that never existed. Re-run the precompute."
+ )
+ pair_ids = json.loads(meta["pair_ids"])
+ entries = {p: tuple(float(cols[n][i]) for n in ENTRY_FIELDS) for i, p in enumerate(pair_ids)}
+ return RefCache(key=key, entries=entries)
+
+
+# ---------------------------------------------------------------------------
+# Precompute
+# ---------------------------------------------------------------------------
+
+
+def _labels(
+ input_ids: torch.Tensor, completion_mask: torch.Tensor, ignore_index: int
+) -> torch.Tensor:
+ """Completion-only labels: prompt AND pad positions ignored.
+
+ Excluding the prompt is not cosmetic. For Arm B the entire shared prefix
+ is identical across the two sides, so including it adds the same term to
+ both logprobs: it cancels in the DPO difference while inflating both
+ magnitudes, and any length-normalised variant computed from it is wrong.
+ """
+ return input_ids.masked_fill(completion_mask == 0, ignore_index)
+
+
+@torch.no_grad()
+def _side_logprobs(
+ model, input_ids: torch.Tensor, completion_mask: torch.Tensor, ignore_index: int
+) -> tuple[torch.Tensor, torch.Tensor]:
+ """Summed completion logprobs via the SAME routine `fused_dpo` uses.
+
+ Not a reimplementation: `completion_logprobs_eager` is imported from the
+ kernel module, and the caller-side shift here (`logits[:, :-1]` against
+ `labels[:, 1:]`) is the shift the DPO adapter applies at train time. If
+ precompute and consumption disagreed by one position, DPO would start from
+ a nonzero margin against itself and spend the run optimising a constant
+ offset it can never remove.
+ """
+ from ..kernels.fused_dpo import completion_logprobs_eager
+
+ labels = _labels(input_ids, completion_mask, ignore_index)
+ logits = model(input_ids=input_ids)
+ return completion_logprobs_eager(logits[:, :-1], labels[:, 1:], ignore_index)
+
+
+def precompute_ref_logprobs(
+ model,
+ rows: Sequence[Any],
+ *,
+ key: RefCacheKey,
+ pad_token_id: int,
+ batch_size: int = 8,
+ device: Optional[torch.device] = None,
+) -> RefCache:
+ """One no-grad pass over `rows`, returning a keyed cache.
+
+ `rows` are in the shape `collate_preference` consumes (section-03's
+ `preference_row_from_store` produces them); every row must carry a
+ `pair_id`, because pair_id is how refs find their way back to the right
+ training example after any reshuffle.
+
+ NOTE on batch size -- why it is NOT a key component, MEASURED not assumed.
+ Right-padding plus causal masking makes padded columns unreachable, so the
+ value is mathematically independent of how rows are grouped; the fp32
+ reduction ORDER is not. Measured on the tiny CPU harness (40 pairs, batch
+ size 1 vs 2/4/8/20/40): max |delta logp| 3.8e-06 = 9.7e-08 relative, max
+ |delta reference MARGIN| 5.7e-06, mean delta ~1e-07. Symmetric rounding
+ noise, not a systematic offset -- which is the distinction that decides
+ it, since only a systematic offset biases the loss argument. Run through
+ this module's own `path_bias_decision` the ratio is ~1e-05 against a 0.05
+ threshold, so the same rule that governs single-GPU-vs-sharded says
+ negligible here. Bitwise determinism holds at a FIXED batch size, which is
+ the property the caching design actually needs, and
+ `test_batch_size_variation_is_below_the_path_bias_threshold` keeps this
+ paragraph honest.
+ """
+ from ..data.loader import collate_preference
+ from ..kernels.fused_dpo import IGNORE_INDEX
+
+ if getattr(model, "training", False):
+ raise RuntimeError(
+ "the reference model is in train mode. Mistral-v0.3 sets "
+ "attention_dropout 0.0 so this is PROBABLY harmless -- which is "
+ "exactly why nobody would notice, and why 'probably' is not a "
+ "basis for numbers that get cached and consumed as constants for "
+ "the rest of the stage. Call model.eval()."
+ )
+ if batch_size < 1:
+ raise ValueError(f"batch_size={batch_size} must be >= 1")
+
+ rows = list(rows)
+ entries: dict[str, tuple[float, float, float, float]] = {}
+ for start in range(0, len(rows), batch_size):
+ chunk = rows[start : start + batch_size]
+ batch = collate_preference(chunk, pad_token_id=pad_token_id)
+ if device is not None:
+ batch = batch.to(device)
+ lp_c, len_c = _side_logprobs(
+ model, batch.chosen_input_ids, batch.chosen_completion_mask, IGNORE_INDEX
+ )
+ lp_r, len_r = _side_logprobs(
+ model, batch.rejected_input_ids, batch.rejected_completion_mask, IGNORE_INDEX
+ )
+ for i, meta in enumerate(batch.metadata):
+ pid = meta.get("pair_id")
+ if pid is None:
+ raise ValueError(
+ "a preference row carries no pair_id. Refs are joined to "
+ "training rows BY pair_id; a row without one could only be "
+ "joined by position, which mis-pairs under any reshuffle."
+ )
+ if pid in entries:
+ raise ValueError(
+ f"duplicate pair_id {pid!r}: the cache is a mapping, so a "
+ "duplicate would silently overwrite one pair's reference "
+ "with another's."
+ )
+ entries[pid] = (float(lp_c[i]), float(lp_r[i]), float(len_c[i]), float(len_r[i]))
+ logger.info("precomputed %d reference pairs (%s)", len(entries), key.forward_impl)
+ return RefCache(key=key, entries=entries)
+
+
+# ---------------------------------------------------------------------------
+# Joining refs to batches
+# ---------------------------------------------------------------------------
+
+
+def attach_refs(batch, cache: RefCache):
+ """Fill a `Preference` batch's ref tensors FROM THE CACHE, by pair_id.
+
+ By pair_id and never by position. The section-03 loader is length-grouped
+ and shuffled per epoch, so batch order is not cache order and never will
+ be; a positional join would attach a perfectly plausible reference to the
+ wrong row, produce a finite loss, and point the gradient somewhere
+ arbitrary. Nothing downstream can see it.
+ """
+ pair_ids = []
+ for i, meta in enumerate(batch.metadata):
+ pid = (meta or {}).get("pair_id")
+ if pid is None:
+ raise ValueError(
+ f"row {i} of this batch carries no pair_id, so its reference "
+ "logprobs cannot be looked up. Falling back to batch order is "
+ "the silent-substitution incident this join exists to prevent."
+ )
+ pair_ids.append(pid)
+
+ vals = [cache.require(p) for p in pair_ids]
+ dtype = torch.float32
+ batch.ref_chosen_logps = torch.tensor([v[0] for v in vals], dtype=dtype)
+ batch.ref_rejected_logps = torch.tensor([v[1] for v in vals], dtype=dtype)
+ return batch
+
+
+# ---------------------------------------------------------------------------
+# Held-out preference accuracy
+# ---------------------------------------------------------------------------
+
+
+@torch.no_grad()
+def heldout_preference_accuracy(
+ model, batches: Iterable[Any], cache: RefCache, beta: float, z: float = 1.96
+) -> dict:
+ """Fraction of held-out pairs the policy's IMPLICIT reward prefers.
+
+ The implicit reward difference is beta * [(logp_c - logp_r) - (ref_c -
+ ref_r)]; the pair counts as correct when it is strictly positive. This is
+ the stage's primary health metric together with the margin, and it is
+ deliberately NOT benchmark accuracy: the pre-registered expectations for
+ both arms are about preference fit, and section-13 owns the paired
+ benchmark evaluation.
+
+ Empty or degenerate held-out sets report NaN, never 0. Note the
+ distinction the test pins: at initialisation the policy IS the reference,
+ every implicit margin is exactly 0.0, and nothing is STRICTLY preferred --
+ accuracy 0.0 over a real denominator. That is an honest measurement, and
+ it is a different fact from NaN over a denominator of zero.
+ """
+ from ..eval.metrics import wilson_interval
+ from ..kernels.fused_dpo import IGNORE_INDEX
+
+ was_training = getattr(model, "training", False)
+ model.eval()
+ n = 0
+ correct = 0
+ margin_sum = 0.0
+ try:
+ for batch in batches:
+ if int(batch.chosen_input_ids.shape[0]) == 0:
+ continue
+ lp_c, _ = _side_logprobs(
+ model, batch.chosen_input_ids, batch.chosen_completion_mask, IGNORE_INDEX
+ )
+ lp_r, _ = _side_logprobs(
+ model, batch.rejected_input_ids, batch.rejected_completion_mask, IGNORE_INDEX
+ )
+ for i, meta in enumerate(batch.metadata):
+ pid = (meta or {}).get("pair_id")
+ if pid is None:
+ raise ValueError("held-out row carries no pair_id")
+ ref_c, ref_r, _, _ = cache.require(pid)
+ m = beta * ((float(lp_c[i]) - float(lp_r[i])) - (ref_c - ref_r))
+ margin_sum += m
+ correct += int(m > 0.0)
+ n += 1
+ finally:
+ if was_training:
+ model.train()
+
+ if n == 0:
+ return {
+ "accuracy": NAN,
+ "ci_low": NAN,
+ "ci_high": NAN,
+ "mean_margin": NAN,
+ "n": 0,
+ "correct": 0,
+ "beta": beta,
+ }
+ lo, hi = wilson_interval(correct, n, z)
+ return {
+ "accuracy": correct / n,
+ "ci_low": lo,
+ "ci_high": hi,
+ "mean_margin": margin_sum / n,
+ "n": n,
+ "correct": correct,
+ "beta": beta,
+ }
+
+
+# ---------------------------------------------------------------------------
+# Path bias
+# ---------------------------------------------------------------------------
+
+
+def path_bias_decision(
+ deltas: Sequence[float], *, beta: float, early_margin_scale: float, threshold: float
+) -> dict:
+ """Decide whether a single-GPU reference cache is usable under FSDP2.
+
+ `deltas` are per-pair differences of the REFERENCE MARGIN (ref_c - ref_r)
+ between the single-GPU forward and the sharded forward. The two are not
+ bitwise identical -- different reduction trees, different collective
+ orders -- and the question is whether the difference matters.
+
+ It matters more here than anywhere else in the project. DPO's loss
+ argument is beta * (pi_margin - ref_margin). Early in the run the policy
+ IS the reference, so that argument sits at zero, which is where the
+ log-sigmoid's gradient is largest and where a systematic offset in
+ ref_margin is least attenuated. A bias that would be invisible against a
+ large margin is fully visible against a zero one.
+
+ The rule compares max|delta| to the early-margin scale. Beta multiplies
+ the perturbation and the reference scale alike, so it CANCELS out of the
+ ratio; it stays in the signature because the receipt must record the beta
+ the decision was made under, and because a reader who does not see the
+ cancellation stated will assume it was forgotten.
+
+ NaN is not small. A NaN delta means a pair could not be compared, and an
+ uncomparable pair forces the firing path rather than comparing False and
+ vanishing. So does an empty measurement: not measuring is not passing.
+ """
+ if not (early_margin_scale > 0) or math.isnan(early_margin_scale):
+ raise ValueError(
+ f"early_margin_scale={early_margin_scale!r} must be a positive "
+ "measured scale; it is the denominator of the decision ratio and "
+ "a zero or NaN one makes the decision undefined rather than lenient"
+ )
+ if not (threshold > 0):
+ raise ValueError(f"threshold={threshold!r} must be positive")
+
+ vals = [float(d) for d in deltas]
+ n_nan = sum(1 for d in vals if math.isnan(d))
+ if not vals or n_nan:
+ detail = (
+ "no deltas were measured; not measuring is not passing"
+ if not vals
+ else f"{n_nan} of {len(vals)} deltas are NaN and a NaN delta is not a small delta"
+ )
+ return {
+ "fired": True,
+ "action": "rerun_precompute_sharded",
+ "ratio": NAN,
+ "max_abs_delta": NAN,
+ "mean_delta": NAN,
+ "median_abs_delta": NAN,
+ "p95_abs_delta": NAN,
+ "n": len(vals),
+ "n_nan": n_nan,
+ "beta": beta,
+ "early_margin_scale": early_margin_scale,
+ "threshold": threshold,
+ "detail": detail,
+ }
+
+ absd = sorted(abs(d) for d in vals)
+ max_abs = absd[-1]
+ ratio = max_abs / early_margin_scale
+ fired = ratio > threshold
+ return {
+ "fired": fired,
+ "action": ("rerun_precompute_sharded" if fired else "keep_single_gpu_cache"),
+ "ratio": ratio,
+ "max_abs_delta": max_abs,
+ "mean_delta": sum(vals) / len(vals),
+ "median_abs_delta": _quantile(absd, 0.5),
+ "p95_abs_delta": _quantile(absd, 0.95),
+ "n": len(vals),
+ "n_nan": 0,
+ "beta": beta,
+ "early_margin_scale": early_margin_scale,
+ "threshold": threshold,
+ "detail": (
+ f"max|delta| {max_abs:.3e} is "
+ f"{'above' if fired else 'within'} {threshold:g} x the "
+ f"early-margin scale {early_margin_scale:g}"
+ ),
+ }
+
+
+def _quantile(sorted_vals: Sequence[float], q: float) -> float:
+ if not sorted_vals:
+ return NAN
+ idx = min(len(sorted_vals) - 1, max(0, int(round(q * (len(sorted_vals) - 1)))))
+ return float(sorted_vals[idx])
+
+
+def batch_invariance_report(
+ model,
+ rows: Sequence[Any],
+ *,
+ key: RefCacheKey,
+ pad_token_id: int,
+ beta: float,
+ early_margin_scale: float,
+ threshold: float,
+ batch_sizes: Sequence[int] = (2, 4, 8, 16),
+) -> dict:
+ """Batched-vs-single-row precompute deltas, as a receipt.
+
+ A SECOND path-bias question, distinct from the single-GPU-vs-sharded one
+ and cheaper to answer: does it matter how many pairs share a forward?
+ Right-padding plus causal masking makes the value grouping-independent in
+ exact arithmetic; the fp32 reduction order is not, so the honest answer is
+ a measurement.
+
+ It decides one thing: whether `batch_size` belongs in the cache key. It is
+ judged by the SAME rule as the sharded comparison (`path_bias_decision`),
+ because it is the same kind of error entering the same place -- a
+ perturbation of the reference margin, read at the point where the loss
+ argument sits near zero.
+
+ Reported per batch size, plus the worst case across all of them. A
+ systematic MEAN delta matters more than a large max: symmetric rounding
+ averages out of the gradient, a consistent offset does not.
+ """
+ single = precompute_ref_logprobs(model, rows, key=key, pad_token_id=pad_token_id, batch_size=1)
+ per_size = {}
+ worst = None
+ for bs in batch_sizes:
+ got = precompute_ref_logprobs(
+ model, rows, key=key, pad_token_id=pad_token_id, batch_size=bs
+ )
+ deltas = [
+ (got.entries[p][0] - got.entries[p][1]) - (single.entries[p][0] - single.entries[p][1])
+ for p in single.entries
+ ]
+ d = path_bias_decision(
+ deltas, beta=beta, early_margin_scale=early_margin_scale, threshold=threshold
+ )
+ per_size[str(bs)] = d
+ if worst is None or (not math.isnan(d["ratio"]) and d["ratio"] > worst["ratio"]):
+ worst = d
+ return {
+ "comparison": "batched_vs_single_row",
+ "n_examples": len(list(rows)),
+ "batch_sizes": list(batch_sizes),
+ "per_batch_size": per_size,
+ "worst": worst,
+ "belongs_in_cache_key": bool(worst and worst["fired"]),
+ "detail": (
+ "if belongs_in_cache_key is true, batch_size must join the "
+ "six key components; measured false means regrouping is "
+ "below the threshold that governs the sharded comparison"
+ ),
+ }
+
+
+def write_path_bias_report(path: str, decision: dict, *, n_examples: int, forward_impl: str) -> str:
+ """The decision is written, not remembered: precompute and training are
+ separate heavy jobs (serialized by the flock, never concurrent) in
+ different processes, and the banner reads this file."""
+ from ..utils.guard import assert_paths_off_root
+
+ assert_paths_off_root([os.path.dirname(os.path.abspath(path))])
+ payload = dict(decision)
+ payload["n_examples"] = int(n_examples)
+ payload["cache_forward_impl"] = forward_impl
+ def _dump(tmp: str) -> None:
+ with open(tmp, "w") as f:
+ json.dump(payload, f, indent=2)
+
+ _atomic_write(path, _dump)
+ logger.info("path-bias report -> %s (fired=%s)", path, decision["fired"])
+ return path
+
+
+def read_path_bias_report(path: str) -> dict:
+ if not os.path.isfile(path):
+ raise FileNotFoundError(
+ f"path-bias report {path!r} is missing. The measurement is "
+ "MANDATORY before a DPO launch: a sharded forward that disagrees "
+ "with the cached single-GPU reference biases the loss argument "
+ "exactly where it is most sensitive. Run "
+ "scripts/precompute_ref_logprobs.py --path-bias."
+ )
+ with open(path) as f:
+ return json.load(f)
diff --git a/code/align/train/sft_loss.py b/code/align/train/sft_loss.py
new file mode 100644
index 0000000000000000000000000000000000000000..d9e27fcd132081a153ee25aa8b7081a4501b098b
--- /dev/null
+++ b/code/align/train/sft_loss.py
@@ -0,0 +1,150 @@
+"""SFT loss adapter: collated batch -> section-05's chunked fused CE.
+
+LOSS NORMALISATION (G3 audit U03-2). DEFAULT UNCHANGED; the global token mean is OPT-IN via
+`train.loss_normalization = "global_token_mean"` (Trainer window look-ahead +
+`TrainModule.train_batch(window_tokens=...)`; this adapter supplies `count_tokens`).
+The paragraph below describes the default, "micro_mean".
+The returned loss is the TOKEN MEAN OF ONE MICRO-BATCH, and `n_tokens` is only
+reported. Under grad-accum A and world W the window gradient is therefore the
+mean of A*W micro-batch means -- not the gradient of the global per-token mean
+(sum CE / sum n_tokens): a target token carries weight 1/(A*W*n_micro), so with
+token-budget batches whose target counts differ (280 vs 6) the short batch's
+tokens weigh ~47x more. The claim "matches one large batch" holds only for
+equal-n micro-batches. Making it a global token mean needs the window's token
+total (a look-ahead over the window's batches plus a cross-rank reduce) and the
+adapter returning CE_sum; that changes the objective every recorded SFT run
+was trained under, so it is a protocol decision, not a bug fix made here.
+
+The adapter owns plumbing only -- label shifting, masking, token accounting --
+and section-05 owns the kernel. Keeping the shift on THIS side (caller-shifted
+views) is the arbitrated contract: `fused_linear_cross_entropy` takes hidden
+states and labels that already line up, so there is exactly one place in the
+repo that decides what predicts what.
+
+Why the fused path matters, so the reason survives the handoff: at seq 1024 and
+micro-batch 8 the `[B*T, 32768]` logit tensor is 1.0GB in bf16 and its gradient
+is another 1.0GB. Materialising both spends 2GB of a 23GB card on a tensor
+nobody reads. The fused loss consumes HIDDEN STATES and applies the LM head per
+chunk, which is why `NmoMistralForCausalLM.forward_hidden_states` exists as a
+separate entry point.
+"""
+
+from __future__ import annotations
+
+import logging
+from typing import Any, Optional
+
+import torch
+import torch.nn.functional as F
+
+from .trainer import LossOutput
+
+logger = logging.getLogger(__name__)
+
+#: Section-05's kernel module. Named here so the both-directions test can block
+#: the import and exercise the refusal branch.
+FUSED_CE_MODULE = "align.kernels.fused_ce"
+
+#: Fallback only. The authority is section-05's constant (imported below when
+#: available) and section-03's collator, which both use -100; this literal
+#: exists so the module still loads if the kernel is missing.
+IGNORE_INDEX = -100
+
+
+def _load_fused():
+ """Return (fused_fn, ignore_index) or (None, IGNORE_INDEX)."""
+ try:
+ import importlib
+
+ mod = importlib.import_module(FUSED_CE_MODULE)
+ return mod.fused_linear_cross_entropy, mod.IGNORE_INDEX
+ except Exception: # noqa: BLE001
+ return None, IGNORE_INDEX
+
+
+def fused_ce_available() -> bool:
+ return _load_fused()[0] is not None
+
+
+def make_sft_loss_adapter(chunk_size: Optional[int] = None, require_fused: bool = False):
+ """Build the SFT loss adapter the Trainer calls once per micro-step.
+
+ `require_fused=True` refuses to run on the reference path -- which is what
+ the 7B stage scripts pass, so a GPU run cannot quietly spend 2GB of card on
+ a materialised logit tensor because section-05's import broke.
+ """
+ fused_fn, ignore_index = _load_fused()
+ if require_fused and fused_fn is None:
+ raise RuntimeError(
+ f"the fused chunked cross-entropy ({FUSED_CE_MODULE}."
+ "fused_linear_cross_entropy) is not importable. Refusing to fall "
+ "back to the reference path for a full-scale run: it materialises "
+ "the [B*T, 32768] logit tensor AND its gradient, ~2GB of a 23GB "
+ "card at seq 1024."
+ )
+ if fused_fn is None:
+ logger.warning(
+ "%s unavailable; using the reference path. Correct, and "
+ "memory-hungry -- small-model use only.",
+ FUSED_CE_MODULE,
+ )
+
+ kwargs = {} if chunk_size is None else {"chunk_size": chunk_size}
+
+ def adapter(model, batch) -> LossOutput:
+ input_ids, labels = _unpack(batch)
+ # Caller-shifted: position t predicts token t+1. Done once, here.
+ target = labels[:, 1:]
+ n_tokens = int((target != ignore_index).sum())
+ if n_tokens == 0:
+ raise ValueError(
+ "SFT batch has no unmasked target tokens: every position is "
+ f"ignore_index ({ignore_index}). The mean cross-entropy over "
+ "zero positions is undefined, and returning 0.0 would report a "
+ "perfectly fit batch. Check prompt_len against sequence length "
+ "in the collator."
+ )
+
+ if fused_fn is not None:
+ # The loss runs INSIDE the model's forward via `hidden_consumer`,
+ # so section-05 still owns the lm_head matmul while FSDP2 has both
+ # the hidden states and lm_head.weight gathered. Computing it
+ # after the forward returned would see `lm_head.weight` re-sharded
+ # -- the mixed-DTensor failure that killed the first 4-rank smoke.
+ def _consume(hidden, lm_head_weight):
+ return fused_fn(
+ hidden[:, :-1], lm_head_weight, target, ignore_index=ignore_index, **kwargs
+ )
+
+ loss = model(input_ids=input_ids, hidden_consumer=_consume)
+ else:
+ logits = model(input_ids=input_ids)
+ loss = F.cross_entropy(
+ logits[:, :-1].reshape(-1, logits.shape[-1]).float(),
+ target.reshape(-1),
+ ignore_index=ignore_index,
+ )
+
+ return LossOutput(loss=loss, n_tokens=n_tokens, metrics={"tokens_in_loss": float(n_tokens)})
+
+ def count_tokens(batch) -> int:
+ """Unmasked target tokens of `batch` (what `n_tokens` will be), without a forward pass.
+ The Trainer sums this over a look-ahead window for the global token mean."""
+ _, labels = _unpack(batch)
+ return int((labels[:, 1:] != ignore_index).sum())
+
+ adapter.uses_fused = fused_fn is not None
+ adapter.count_tokens = count_tokens
+ return adapter
+
+
+def _unpack(batch: Any) -> tuple[torch.Tensor, torch.Tensor]:
+ """Accept either a `Collated` from section-03 or a plain (ids, labels)."""
+ if hasattr(batch, "input_ids") and hasattr(batch, "labels"):
+ return batch.input_ids, batch.labels
+ if isinstance(batch, (tuple, list)) and len(batch) == 2:
+ return batch[0], batch[1]
+ raise TypeError(
+ f"SFT loss adapter got {type(batch).__name__}; expected a Collated "
+ "batch (input_ids, labels) from align.data.loader.collate_sft"
+ )
diff --git a/code/align/train/sharded_ckpt.py b/code/align/train/sharded_ckpt.py
new file mode 100644
index 0000000000000000000000000000000000000000..e89b6640ed36a953cb67df7b7d5048bb4be68412
--- /dev/null
+++ b/code/align/train/sharded_ckpt.py
@@ -0,0 +1,1285 @@
+"""Sharded streaming checkpoints: every rank writes ONLY the weights it owns.
+
+OPT-IN (`train.checkpoint_format = "sharded"`; the default `"gathered"` is
+untouched). Runtime deps: torch + stdlib. The converter half (`export_hf`,
+`reshard`) never touches a GPU and never imports transformers/safetensors.
+
+THE PROBLEM, MEASURED (docs/MEASUREMENTS.md, Track M, OLMoE-1B-7B, 4 ranks)
+ * save 228-241 s, rank-0 RSS peak +28 GB. `get_model_state_dict(
+ full_state_dict=True, cpu_offload=True)` is a full all-gather into rank 0's
+ host RAM (1x weights), and the HF converter then builds a second dict of
+ cloned expert slices plus `.contiguous()` copies (the other 1x): 2x the
+ weights on ONE rank of a no-swap, shared box -- the 2026-08-12 OOM shape.
+ Ranks 1..N-1 sit in a barrier for the whole four minutes.
+ * resume peaks at 90 GiB SUMMED RSS across ranks: `resume_from` reads the
+ whole `model.safetensors` on EVERY rank and then slices out the 1/N it
+ owns.
+OLMo-core avoids both with distributed checkpointing (per-rank files, no full
+gather). This is the same idea at the size this repo needs.
+
+LAYOUT (a checkpoint directory in sharded mode)
+ model_rank00000.safetensors ... one per rank; that rank's LOCAL shards,
+ bf16, standard safetensors (any reader
+ opens them), written tensor by tensor
+ optimizer_rank{r}.pt unchanged: the existing per-rank optimizer
+ files (sharded mode implies them)
+ shard_manifest.json global shapes, placements, per-rank row
+ ranges, world size, per-file bytes and
+ sha256, the HF export recipe
+ resume.pt unchanged loop state (no weights)
+ tokenizer files as in the gathered layout
+The manifest is written last, inside the `.partial` staging directory, and the
+directory is published by the existing fsync-then-rename protocol. A directory
+without a complete manifest and every rank file the manifest names is not a
+checkpoint (`sharded_problem`, called from `trainer.checkpoint_problem`).
+
+WHAT EACH PIECE PREVENTS (incident-first, repo style)
+
+THE WRONG-SHARD DEFECT. A rank's file holds rows [off, off+n) of each tensor.
+Loaded under a different world size, rank r would receive rows cut for another
+partitioning and training would continue plausibly on scrambled weights --
+nothing crashes, the loss is merely worse. Resume therefore reads the manifest
+FIRST and REFUSES a different world size with a message naming the offline
+reshard (`scripts/ckpt_convert.py reshard`); it never "tries" a mismatch. The
+per-row ranges recorded in the manifest are also compared against the LIVE
+model's own sharding tensor by tensor, so a model rebuilt with a different mesh
+or an uneven-sharding change is caught even at the same world size.
+
+THE SILENT-CORRUPTION DEFECT. Per-file sha256 is recorded at save and checked
+by each rank, on its OWN file, before any weight is copied. The verdict is then
+reduced across ranks (`any_rank`): a bit-rotted file on rank 2 alone would
+otherwise make rank 2 raise while ranks 0/1/3 entered the next collective and
+hung until the watchdog -- the lone-rank shape every other decision in the
+trainer avoids. The save side uses the same reduction: a rank whose disk fills
+mid-write must stop its peers at once, not leave them in a gather for the
+watchdog's 30 minutes.
+
+THE TWO-COPIES-ON-RANK-0 DEFECT (the measured 28 GB). There is no gather. Each
+rank's peak is ONE tensor (`emit` copies one local shard to host, writes it,
+drops it). The export side is byte-copy `pread` -> `write` in 8 MiB pieces: for
+1:1 families it never builds a tensor at all.
+
+THE PARTIAL-WRITE DEFECT (same as the gathered format). Rank files are written
+to `*.tmp`, fsynced, renamed into the `.partial` staging dir; the staging dir
+is fsync_tree'd and renamed to `micro_N` only after rank 0 has the manifest of
+every rank. A kill anywhere leaves a `.partial` that `_discard_partials`
+removes at the next start, never a `micro_N` that looks resumable.
+
+WHAT THIS DOES NOT DO (read before relying on it)
+ * It is not loadable by `eval_checkpoint.py` / the rollout weight sync /
+ `ref_cache` until exported: those read `model.safetensors`. Export is
+ `ckpt_convert.py export` (streaming, bounded memory).
+ * Placements: `Shard(0)` and `Replicate()` on a 1-D mesh (what
+ `fully_shard` produces). Anything else (HSDP, TP, Shard(d>0)) is refused
+ at SAVE time, not discovered at resume.
+ * A train module that changes what "the weights" mean (LoRA folds an
+ adapter into merged weights; PPO has its own gather) must provide
+ `sharded_model_state()` / `load_sharded_model_state()` or the Trainer
+ refuses `"sharded"` at construction. Silent fallback to the base-class
+ behaviour would checkpoint the UNMERGED base under a merged name.
+"""
+
+from __future__ import annotations
+
+import hashlib
+import json
+import logging
+import os
+import re
+import shutil
+import struct
+from dataclasses import dataclass
+from typing import Any, Callable, Iterable, Mapping, Optional
+
+import torch
+
+logger = logging.getLogger(__name__)
+
+SHARD_MANIFEST_NAME = "shard_manifest.json"
+FORMAT_NAME = "align-sharded-ckpt"
+FORMAT_VERSION = 1
+CHECKPOINT_FORMATS = ("gathered", "sharded")
+
+#: Bytes moved per pread/write while exporting or resharding. Peak RSS of the
+#: converter is a small multiple of this plus one optimizer tensor, whatever the
+#: model size.
+COPY_CHUNK = 8 * 1024 * 1024
+
+#: Files in a sharded checkpoint directory that are NOT tokenizer/config
+#: payload (the exporter copies everything else next to the weights).
+_OWN_FILE_RE = re.compile(
+ r"^(model_rank\d+\.safetensors(\.tmp)?|optimizer_rank\d+\.pt(\.tmp)?|resume\.pt|"
+ + r"integrity\.json|"
+ + re.escape(SHARD_MANIFEST_NAME)
+ + r")$"
+)
+
+
+class ShardedCheckpointError(RuntimeError):
+ """A sharded checkpoint that is malformed, mismatched, or corrupt."""
+
+
+class CheckpointIntegrityError(ShardedCheckpointError):
+ """A payload file's sha256 differs from the one recorded at save time."""
+
+
+class ShardedWorldSizeError(ShardedCheckpointError):
+ """Resume under a world size other than the one that saved the shards."""
+
+
+# ---------------------------------------------------------------------------
+# dtypes
+# ---------------------------------------------------------------------------
+
+_ST_TO_TORCH = {
+ "BOOL": torch.bool,
+ "U8": torch.uint8,
+ "I8": torch.int8,
+ "I16": torch.int16,
+ "F16": torch.float16,
+ "BF16": torch.bfloat16,
+ "I32": torch.int32,
+ "F32": torch.float32,
+ "F64": torch.float64,
+ "I64": torch.int64,
+}
+_TORCH_TO_ST = {v: k for k, v in _ST_TO_TORCH.items()}
+#: safetensors serialises tensors sorted by (dtype DESC, name); this is the
+#: dtype order of its enum. Byte-identical output to `save_file` is a tested
+#: property (it is what lets "export equals gathered export" be a file hash).
+_ST_ORDER = ["BOOL", "U8", "I8", "I16", "F16", "BF16", "I32", "F32", "F64", "I64"]
+_ST_ITEMSIZE = {"BOOL": 1, "U8": 1, "I8": 1, "I16": 2, "F16": 2, "BF16": 2, "I32": 4, "F32": 4, "F64": 8, "I64": 8}
+
+
+def _prod(shape: Iterable[int]) -> int:
+ n = 1
+ for s in shape:
+ n *= int(s)
+ return n
+
+
+def chunk_range(n: int, world_size: int, rank: int) -> tuple[int, int]:
+ """(row offset, row count) of `rank`'s `Shard(0)` slice of `n` rows.
+
+ `torch.chunk` semantics, which is what `DTensor` `Shard(0)` uses: ceil(n/ws)
+ rows per rank, the tail ranks shorter or EMPTY (5 rows over 4 ranks is
+ 2,2,1,0). Evenly dividing would put row 4 nowhere. One function for save,
+ load, reshard and export so they cannot disagree about who owns a row.
+ """
+ c = -(-int(n) // int(world_size))
+ off = min(int(rank) * c, int(n))
+ return off, min(c, int(n) - off)
+
+
+# ---------------------------------------------------------------------------
+# safetensors, streaming (writer) and random-access (reader)
+# ---------------------------------------------------------------------------
+
+
+@dataclass
+class StEntry:
+ """One tensor to be written: `emit(f)` must write exactly `nbytes` bytes."""
+
+ name: str
+ st_dtype: str
+ shape: tuple
+ emit: Callable[[Any], None]
+
+ @property
+ def nbytes(self) -> int:
+ return _prod(self.shape) * _ST_ITEMSIZE[self.st_dtype]
+
+
+def _st_sort_key(e: StEntry):
+ return (-_ST_ORDER.index(e.st_dtype), e.name)
+
+
+def safetensors_header(entries: list[StEntry], metadata: Optional[Mapping[str, str]] = None) -> tuple[bytes, list[StEntry]]:
+ """(8-byte length prefix + JSON header padded to 8 bytes, entries in file order)."""
+ ordered = sorted(entries, key=_st_sort_key)
+ header: dict[str, Any] = {}
+ if metadata:
+ header["__metadata__"] = dict(metadata)
+ off = 0
+ for e in ordered:
+ header[e.name] = {"dtype": e.st_dtype, "shape": [int(s) for s in e.shape], "data_offsets": [off, off + e.nbytes]}
+ off += e.nbytes
+ raw = json.dumps(header, separators=(",", ":")).encode("utf-8")
+ raw += b" " * ((-len(raw)) % 8)
+ return struct.pack(" int:
+ self.sha.update(b)
+ self.n += len(b)
+ return self.f.write(b)
+
+
+def write_safetensors_streaming(
+ path: str, entries: list[StEntry], metadata: Optional[Mapping[str, str]] = None
+) -> dict:
+ """Write `path` tensor by tensor, fsync it, return {"bytes", "sha256"}.
+
+ Peak memory is whatever ONE `emit` holds. `safetensors.save_file` needs the
+ whole dict resident, which is the 2x-weights transient this module exists
+ to remove.
+ """
+ head, ordered = safetensors_header(entries, metadata)
+ with open(path, "wb") as raw:
+ w = _HashingWriter(raw)
+ w.write(head)
+ for e in ordered:
+ before = w.n
+ e.emit(w)
+ if w.n - before != e.nbytes:
+ raise ShardedCheckpointError(
+ f"{e.name}: emitted {w.n - before} bytes, header promised {e.nbytes}"
+ )
+ raw.flush()
+ os.fsync(raw.fileno())
+ return {"bytes": w.n, "sha256": w.sha.hexdigest()}
+
+
+def tensor_bytes_emitter(t: torch.Tensor) -> Callable[[Any], None]:
+ def emit(f):
+ c = t.detach().contiguous().reshape(-1)
+ if c.numel():
+ f.write(memoryview(c.view(torch.uint8).numpy()))
+
+ return emit
+
+
+class ShardFile:
+ """Random-access reader over one safetensors file (header parsed once)."""
+
+ def __init__(self, path: str):
+ self.path = path
+ self.fd = os.open(path, os.O_RDONLY)
+ try:
+ size = os.fstat(self.fd).st_size
+ if size < 8:
+ raise ShardedCheckpointError(f"{os.path.basename(path)} is {size} bytes (no header)")
+ (n,) = struct.unpack(" size - 8 or n > 256 * 2**20:
+ raise ShardedCheckpointError(f"{os.path.basename(path)} header claims {n} bytes of {size}")
+ header = json.loads(os.pread(self.fd, n, 8))
+ self.data_start = 8 + n
+ self.tensors: dict[str, tuple[str, tuple, int, int]] = {}
+ end = 0
+ for k, v in header.items():
+ if k == "__metadata__":
+ continue
+ a, b = v["data_offsets"]
+ self.tensors[k] = (v["dtype"], tuple(v["shape"]), int(a), int(b))
+ end = max(end, int(b))
+ if size < self.data_start + end:
+ raise ShardedCheckpointError(
+ f"{os.path.basename(path)} truncated: {size} bytes, header needs {self.data_start + end}"
+ )
+ except BaseException:
+ os.close(self.fd)
+ raise
+
+ def close(self) -> None:
+ if self.fd >= 0:
+ os.close(self.fd)
+ self.fd = -1
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *a):
+ self.close()
+
+ def copy_range(self, name: str, lo: int, hi: int, sink) -> None:
+ """Stream bytes [lo, hi) of tensor `name` into `sink.write`."""
+ _dt, _shape, a, b = self.tensors[name]
+ if not (0 <= lo <= hi <= b - a):
+ raise ShardedCheckpointError(f"{name}: byte range [{lo},{hi}) outside the {b - a}-byte tensor")
+ pos = self.data_start + a + lo
+ left = hi - lo
+ while left:
+ buf = os.pread(self.fd, min(COPY_CHUNK, left), pos)
+ if not buf:
+ raise ShardedCheckpointError(f"{self.path}: short read at {pos}")
+ sink.write(buf)
+ pos += len(buf)
+ left -= len(buf)
+
+ def tensor(self, name: str) -> torch.Tensor:
+ dt, shape, a, b = self.tensors[name]
+ torch_dt = _ST_TO_TORCH[dt]
+ if b == a:
+ return torch.empty(shape, dtype=torch_dt)
+ buf = bytearray(b - a)
+ mv = memoryview(buf)
+ pos, got = self.data_start + a, 0
+ while got < len(buf):
+ n = os.preadv(self.fd, [mv[got : got + COPY_CHUNK]], pos + got)
+ if n <= 0:
+ raise ShardedCheckpointError(f"{self.path}: short read of {name}")
+ got += n
+ return torch.frombuffer(buf, dtype=torch_dt).reshape(shape)
+
+
+def file_sha256(path: str) -> str:
+ h = hashlib.sha256()
+ with open(path, "rb") as f:
+ while True:
+ b = f.read(COPY_CHUNK)
+ if not b:
+ break
+ h.update(b)
+ return h.hexdigest()
+
+
+# ---------------------------------------------------------------------------
+# payload integrity sidecar (audit U01-3)
+# ---------------------------------------------------------------------------
+
+#: `/integrity.json`: {"version": 1, "sha256": {file name: hex}} for the payloads the
+#: weights manifest does not already cover (resume.pt, optimizer_rank*.pt, and the gathered
+#: layout's model.safetensors). A torch.save zip passes the structural check after a same-size
+#: bit flip in a stored tensor, so structure alone cannot catch silent media corruption.
+INTEGRITY_NAME = "integrity.json"
+
+
+def write_integrity(out_dir: str, sha256: Mapping[str, str]) -> None:
+ path = os.path.join(out_dir, INTEGRITY_NAME)
+ with open(path, "w", encoding="utf-8") as f:
+ f.write(json.dumps({"version": 1, "sha256": dict(sha256)}, indent=1, sort_keys=True) + "\n")
+ f.flush()
+ os.fsync(f.fileno())
+
+
+def read_integrity(path: str) -> Optional[dict]:
+ """{file name: sha256} or None for a checkpoint that predates the sidecar. A sidecar that
+ exists but cannot be parsed is an error, never 'legacy' (fail closed)."""
+ fp = os.path.join(path, INTEGRITY_NAME)
+ if not os.path.isfile(fp):
+ return None
+ try:
+ with open(fp, encoding="utf-8") as f:
+ blob = json.load(f)
+ out = {str(k): str(v) for k, v in blob["sha256"].items()}
+ except (OSError, ValueError, KeyError, AttributeError, TypeError) as e:
+ raise CheckpointIntegrityError(f"{fp} is unreadable ({type(e).__name__}: {e})") from e
+ return out
+
+
+def verify_integrity(path: str, names: Iterable[str], recorded: Optional[dict] = None) -> Optional[str]:
+ """None iff every name in `names` that the sidecar records matches its sha256.
+
+ A name the sidecar does not list is skipped (a legacy or partial sidecar); a listed file that
+ is missing or differs is a problem.
+ """
+ recorded = read_integrity(path) if recorded is None else recorded
+ if recorded is None:
+ return None
+ for n in names:
+ want = recorded.get(n)
+ if want is None:
+ continue
+ fp = os.path.join(path, n)
+ if not os.path.isfile(fp):
+ return f"{n} is missing (integrity.json records it)"
+ got = file_sha256(fp)
+ if got != want:
+ return f"{n} sha256 {got[:12]}.. != recorded {want[:12]}.. (bit rot or a partial copy)"
+ return None
+
+
+# ---------------------------------------------------------------------------
+# describing a live (possibly DTensor) tensor
+# ---------------------------------------------------------------------------
+
+
+def _local_and_desc(t: torch.Tensor, rank: int, world_size: int) -> tuple[torch.Tensor, dict]:
+ """(this rank's local tensor, descriptor) for a plain tensor or a DTensor.
+
+ Descriptor: global `shape`, `placement` ("shard0" | "replicate"), and for
+ shard0 this rank's `row` = [offset, count]. Everything else is refused HERE
+ (save time): a layout the loader cannot place must not produce a file.
+ """
+ if hasattr(t, "to_local") and hasattr(t, "placements"):
+ pl = tuple(t.placements)
+ mesh = t.device_mesh
+ if mesh.ndim != 1 or mesh.size() != world_size:
+ raise ShardedCheckpointError(
+ f"unsupported mesh {tuple(mesh.shape)} for world_size={world_size}: sharded checkpoints "
+ "support a 1-D mesh over all ranks (what fully_shard builds). HSDP/TP meshes are not "
+ "implemented; use checkpoint_format='gathered'."
+ )
+ if mesh.get_local_rank() != rank:
+ raise ShardedCheckpointError(f"mesh local rank {mesh.get_local_rank()} != process rank {rank}")
+ local = t.to_local()
+ shape = tuple(int(s) for s in t.shape)
+ from torch.distributed.tensor import Replicate, Shard
+
+ if len(pl) == 1 and isinstance(pl[0], Shard) and pl[0].dim == 0:
+ off, n = chunk_range(shape[0], world_size, rank)
+ if tuple(local.shape) != (n,) + shape[1:]:
+ raise ShardedCheckpointError(
+ f"local shard {tuple(local.shape)} != chunk_range-derived {(n,) + shape[1:]} "
+ f"(global {shape}, rank {rank}/{world_size}): uneven/padded layout not understood"
+ )
+ return local, {"shape": list(shape), "placement": "shard0", "row": [off, n]}
+ if len(pl) == 1 and isinstance(pl[0], Replicate):
+ return local, {"shape": list(shape), "placement": "replicate", "row": None}
+ raise ShardedCheckpointError(f"unsupported placements {pl}: only Shard(0) and Replicate() are implemented")
+ return t, {"shape": [int(s) for s in t.shape], "placement": "replicate", "row": None}
+
+
+def _store_dtype(t: torch.Tensor, store_dtype: torch.dtype) -> str:
+ dt = store_dtype if t.is_floating_point() else t.dtype
+ try:
+ return _TORCH_TO_ST[dt]
+ except KeyError as e:
+ raise ShardedCheckpointError(f"unsupported tensor dtype {dt}") from e
+
+
+def rank_file_name(rank: int) -> str:
+ return f"model_rank{rank:05d}.safetensors"
+
+
+# ---------------------------------------------------------------------------
+# save (one rank)
+# ---------------------------------------------------------------------------
+
+
+def save_rank_shard(
+ state: Mapping[str, torch.Tensor],
+ out_dir: str,
+ *,
+ rank: int,
+ world_size: int,
+ store_dtype: torch.dtype = torch.bfloat16,
+) -> dict:
+ """Write THIS rank's local shards to `out_dir/model_rank{r}.safetensors`.
+
+ Atomic per file (tmp + fsync + rename). Returns this rank's info for the
+ manifest: {"file": {name, bytes, sha256}, "tensors": {name: descriptor}}.
+ Peak host memory is one tensor.
+ """
+ entries: list[StEntry] = []
+ tensors: dict[str, dict] = {}
+ for name in sorted(state):
+ local, desc = _local_and_desc(state[name], rank, world_size)
+ st_dt = _store_dtype(local, store_dtype)
+ desc["dtype"] = st_dt
+ tensors[name] = desc
+
+ def emit(f, local=local, st_dt=st_dt):
+ host = local.detach().to(device="cpu", dtype=_ST_TO_TORCH[st_dt])
+ tensor_bytes_emitter(host)(f)
+
+ entries.append(StEntry(name, st_dt, tuple(local.shape), emit))
+ fname = rank_file_name(rank)
+ final = os.path.join(out_dir, fname)
+ tmp = final + ".tmp"
+ try:
+ meta = write_safetensors_streaming(tmp, entries, {"format": "pt"})
+ os.replace(tmp, final)
+ except BaseException:
+ for p in (tmp, final):
+ try:
+ os.remove(p)
+ except OSError:
+ pass
+ raise
+ return {"file": {"name": fname, **meta}, "tensors": tensors, "order": list(state)}
+
+
+def build_manifest(
+ infos: list[dict],
+ *,
+ param_order: Optional[list[str]] = None,
+ export: Optional[dict] = None,
+ extra: Optional[dict] = None,
+) -> dict:
+ """Merge every rank's info (rank-ordered) into the manifest. Cross-checks
+ that all ranks saw the same tensors and that shard0 rows tile [0, n)."""
+ ws = len(infos)
+ names = sorted(infos[0]["tensors"])
+ tensors: dict[str, dict] = {}
+ for n in names:
+ d0 = infos[0]["tensors"][n]
+ rows = []
+ for r, info in enumerate(infos):
+ if sorted(info["tensors"]) != names:
+ raise ShardedCheckpointError(f"rank {r} wrote a different tensor set than rank 0")
+ d = info["tensors"][n]
+ for k in ("shape", "placement", "dtype"):
+ if d[k] != d0[k]:
+ raise ShardedCheckpointError(f"{n}: rank {r} disagrees with rank 0 on {k}: {d[k]} vs {d0[k]}")
+ rows.append(d["row"])
+ entry = {"shape": d0["shape"], "dtype": d0["dtype"], "placement": d0["placement"]}
+ if d0["placement"] == "shard0":
+ pos = 0
+ for off, cnt in rows:
+ if off != min(pos, d0["shape"][0]) and cnt:
+ raise ShardedCheckpointError(f"{n}: shard rows do not tile ({rows})")
+ pos += cnt
+ if pos != d0["shape"][0]:
+ raise ShardedCheckpointError(f"{n}: shard rows sum to {pos}, global rows {d0['shape'][0]}")
+ entry["rows"] = rows
+ tensors[n] = entry
+ m: dict[str, Any] = {
+ "format": FORMAT_NAME,
+ "version": FORMAT_VERSION,
+ "world_size": ws,
+ "files": {str(r): infos[r]["file"] for r in range(ws)},
+ "tensors": tensors,
+ }
+ if infos[0].get("order") is not None:
+ # The model's own state-dict order: shard planning (qwen3's 5 GB files)
+ # is greedy over insertion order, and the manifest JSON is key-sorted,
+ # so without this the exported file split would differ from the
+ # gathered writer's.
+ m["key_order"] = list(infos[0]["order"])
+ if param_order is not None:
+ m["param_order"] = list(param_order)
+ if export is not None:
+ m["export"] = export
+ if extra:
+ m.update(extra)
+ return m
+
+
+def write_manifest(out_dir: str, manifest: dict) -> None:
+ path = os.path.join(out_dir, SHARD_MANIFEST_NAME)
+ with open(path, "w", encoding="utf-8") as f:
+ f.write(json.dumps(manifest, indent=1, sort_keys=True) + "\n")
+ f.flush()
+ os.fsync(f.fileno())
+
+
+# ---------------------------------------------------------------------------
+# read side: manifest, completeness, load
+# ---------------------------------------------------------------------------
+
+
+def is_sharded(path: str) -> bool:
+ return os.path.isfile(os.path.join(path, SHARD_MANIFEST_NAME))
+
+
+def read_manifest(path: str) -> dict:
+ """Parse + structurally validate; raises `ShardedCheckpointError`."""
+ mp = os.path.join(path, SHARD_MANIFEST_NAME)
+ try:
+ with open(mp, encoding="utf-8") as f:
+ m = json.load(f)
+ except (OSError, ValueError) as e:
+ raise ShardedCheckpointError(f"unreadable {SHARD_MANIFEST_NAME} ({e})") from e
+ if not isinstance(m, dict) or m.get("format") != FORMAT_NAME:
+ raise ShardedCheckpointError(f"{SHARD_MANIFEST_NAME} is not an {FORMAT_NAME} manifest")
+ if m.get("version") != FORMAT_VERSION:
+ raise ShardedCheckpointError(f"manifest version {m.get('version')} != supported {FORMAT_VERSION}")
+ ws = m.get("world_size")
+ if not isinstance(ws, int) or ws < 1:
+ raise ShardedCheckpointError(f"manifest world_size {ws!r} is not a positive int")
+ files, tensors = m.get("files"), m.get("tensors")
+ if not isinstance(files, dict) or sorted(files, key=int) != [str(r) for r in range(ws)]:
+ raise ShardedCheckpointError("manifest does not name exactly one file per rank")
+ for r in range(ws):
+ fe = files[str(r)]
+ if not (isinstance(fe, dict) and {"name", "bytes", "sha256"} <= fe.keys()):
+ raise ShardedCheckpointError(f"manifest file entry for rank {r} is malformed")
+ # An EMPTY tensor set is legal: a module whose weights are reconstructible
+ # elsewhere (a frozen base + adapter) may declare `sharded_model_state() == {}`
+ # and checkpoint only its optimizer/adapter state.
+ if not isinstance(tensors, dict):
+ raise ShardedCheckpointError("manifest tensors entry is not an object")
+ for n, e in tensors.items():
+ if e.get("placement") not in ("shard0", "replicate") or e.get("dtype") not in _ST_ITEMSIZE:
+ raise ShardedCheckpointError(f"{n}: malformed tensor entry")
+ if e["placement"] == "shard0" and len(e.get("rows", [])) != ws:
+ raise ShardedCheckpointError(f"{n}: shard0 entry has {len(e.get('rows', []))} row ranges, world {ws}")
+ return m
+
+
+def sharded_problem(path: str, st_header_problem: Callable[[str], Optional[str]]) -> Optional[str]:
+ """None iff every rank file the manifest names is present, the size it
+ recorded, and a structurally complete safetensors file. CHEAP (no payload
+ reads): sha256 is verified on the rank that loads the file."""
+ try:
+ m = read_manifest(path)
+ except ShardedCheckpointError as e:
+ return str(e)
+ for r in range(m["world_size"]):
+ fe = m["files"][str(r)]
+ fp = os.path.join(path, fe["name"])
+ if not os.path.isfile(fp):
+ return f"missing shard file {fe['name']}"
+ if os.path.getsize(fp) != fe["bytes"]:
+ return f"{fe['name']} is {os.path.getsize(fp)} bytes, manifest recorded {fe['bytes']}"
+ problem = st_header_problem(fp)
+ if problem:
+ return problem
+ return None
+
+
+def verify_rank_file(path: str, rank: int) -> Optional[str]:
+ """sha256 of THIS rank's file against the manifest; None if it matches."""
+ m = read_manifest(path)
+ fe = m["files"][str(rank)]
+ got = file_sha256(os.path.join(path, fe["name"]))
+ if got != fe["sha256"]:
+ return f"{fe['name']} sha256 {got[:12]}.. != manifest {fe['sha256'][:12]}.. (bit rot or a partial copy)"
+ return None
+
+
+def check_world_size(path: str, manifest: dict, world_size: int) -> None:
+ if manifest["world_size"] != world_size:
+ raise ShardedWorldSizeError(
+ f"checkpoint {path} was saved with world_size={manifest['world_size']} but this run has "
+ f"world_size={world_size}. Sharded weights are rank-local row slices and cannot be loaded "
+ "under another partitioning. Resume with the same number of ranks, or reshard offline: "
+ f"python scripts/ckpt_convert.py reshard {path} --world-size {world_size}"
+ )
+
+
+def load_rank_shard(
+ state: Mapping[str, torch.Tensor], path: str, *, rank: int, world_size: int, verify_hash: bool = False
+) -> int:
+ """Copy this rank's file into the LIVE tensors of `state` (a model state
+ dict whose DTensors alias the parameters). Reads only this rank's file, one
+ tensor at a time. Returns the number of tensors loaded."""
+ m = read_manifest(path)
+ check_world_size(path, m, world_size)
+ mt = m["tensors"]
+ missing, unexpected = sorted(set(mt) - set(state)), sorted(set(state) - set(mt))
+ if missing or unexpected:
+ raise KeyError(f"resume state mismatch: missing={missing[:5]} unexpected={unexpected[:5]}")
+ if verify_hash:
+ bad = verify_rank_file(path, rank)
+ if bad:
+ raise ShardedCheckpointError(bad)
+ # validate EVERYTHING before the first copy_: a mismatch on tensor 300 must
+ # not leave a model half-overwritten.
+ plan = []
+ for name in sorted(mt):
+ local, desc = _local_and_desc(state[name], rank, world_size)
+ e = mt[name]
+ if desc["shape"] != e["shape"] or desc["placement"] != e["placement"]:
+ raise ShardedCheckpointError(
+ f"{name}: live {desc['placement']} {desc['shape']} != checkpoint {e['placement']} {e['shape']}"
+ )
+ if e["placement"] == "shard0" and list(e["rows"][rank]) != list(desc["row"]):
+ raise ShardedCheckpointError(
+ f"{name}: live rows {desc['row']} != checkpoint rows {e['rows'][rank]} for rank {rank}"
+ )
+ if local.is_floating_point() != _ST_TO_TORCH[e["dtype"]].is_floating_point:
+ raise ShardedCheckpointError(f"{name}: dtype class differs (live {local.dtype}, ckpt {e['dtype']})")
+ plan.append((name, local))
+ with ShardFile(os.path.join(path, m["files"][str(rank)]["name"])) as sf:
+ missing_in_file = [n for n, _ in plan if n not in sf.tensors]
+ if missing_in_file:
+ raise ShardedCheckpointError(f"{sf.path} lacks tensors {missing_in_file[:5]}")
+ with torch.no_grad():
+ for name, local in plan:
+ t = sf.tensor(name)
+ if tuple(t.shape) != tuple(local.shape):
+ raise ShardedCheckpointError(f"{name}: file shape {tuple(t.shape)} != live {tuple(local.shape)}")
+ local.copy_(t.to(device=local.device))
+ return len(plan)
+
+
+class _ListSink:
+ def __init__(self):
+ self.parts: list[bytes] = []
+
+ def write(self, b):
+ self.parts.append(bytes(b))
+ return len(b)
+
+
+class GlobalReader:
+ """Byte-range reads over the GLOBAL (row-major) tensors of a sharded checkpoint."""
+
+ def __init__(self, path: str, manifest: Optional[dict] = None):
+ self.path = path
+ self.m = manifest or read_manifest(path)
+ self.files = {
+ r: ShardFile(os.path.join(path, self.m["files"][str(r)]["name"])) for r in range(self.m["world_size"])
+ }
+
+ def close(self):
+ for f in self.files.values():
+ f.close()
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *a):
+ self.close()
+
+ def info(self, name: str) -> tuple[str, tuple]:
+ e = self.m["tensors"][name]
+ return e["dtype"], tuple(e["shape"])
+
+ def nbytes(self, name: str) -> int:
+ dt, shape = self.info(name)
+ return _prod(shape) * _ST_ITEMSIZE[dt]
+
+ def copy_range(self, name: str, lo: int, hi: int, sink) -> None:
+ """Stream global bytes [lo, hi) of tensor `name` into `sink`."""
+ e = self.m["tensors"][name]
+ if not (0 <= lo <= hi <= self.nbytes(name)):
+ raise ShardedCheckpointError(f"{name}: byte range [{lo},{hi}) outside tensor")
+ if e["placement"] == "replicate":
+ self.files[0].copy_range(name, lo, hi, sink)
+ return
+ dt, shape = self.info(name)
+ rb = _prod(shape[1:]) * _ST_ITEMSIZE[dt]
+ for r, (off, cnt) in enumerate(e["rows"]):
+ a, b = max(lo, off * rb), min(hi, (off + cnt) * rb)
+ if a < b:
+ self.files[r].copy_range(name, a - off * rb, b - off * rb, sink)
+
+ def tensor(self, name: str) -> torch.Tensor:
+ dt, shape = self.info(name)
+ sink = _ListSink()
+ self.copy_range(name, 0, self.nbytes(name), sink)
+ buf = bytearray(b"".join(sink.parts))
+ if not buf:
+ return torch.empty(shape, dtype=_ST_TO_TORCH[dt])
+ return torch.frombuffer(buf, dtype=_ST_TO_TORCH[dt]).reshape(shape)
+
+
+def load_full_state_dict(path: str) -> dict[str, torch.Tensor]:
+ """The WHOLE state dict, assembled in host memory. For tests and small
+ models only -- the production path is `export_hf`, which never does this."""
+ with GlobalReader(path) as g:
+ return {n: g.tensor(n) for n in g.m["tensors"]}
+
+
+# ---------------------------------------------------------------------------
+# describing the weight format (so the checkpoint is self-exporting)
+# ---------------------------------------------------------------------------
+
+
+def describe_weight_format(weight_format: Any, dtype: torch.dtype = torch.bfloat16) -> Optional[dict]:
+ """{"family", "hf_config"} for the formats we can export; None otherwise
+ (the checkpoint still saves and resumes, `export` then refuses with a clear
+ message instead of guessing a config)."""
+ # Subclasses count: scripts wrap OlmoeFormat to serialise its loads
+ # (olmoe_sft_smoke.SerialWeightFormat) and must stay exportable.
+ names = {c.__name__ for c in type(weight_format).__mro__}
+ name = next((n for n in ("DenseFormat", "OlmoeFormat", "Qwen3Format") if n in names), type(weight_format).__name__)
+ try:
+ if name == "DenseFormat":
+ from ..model import hf_config as hfc
+
+ return {"family": "dense", "hf_config": hfc.to_hf_config_dict(weight_format.model_cfg, dtype=dtype)}
+ if name == "OlmoeFormat":
+ from ..model.olmoe_checkpoint import config_to_hf_dict
+
+ return {"family": "olmoe", "hf_config": config_to_hf_dict(weight_format.olmoe_cfg, dtype=dtype)}
+ if name == "Qwen3Format":
+ return {"family": "qwen3", "hf_config": weight_format.qcfg.to_hf_config(dtype=dtype)}
+ except Exception as e: # noqa: BLE001 -- a config we cannot describe must not block saving weights
+ logger.warning("cannot describe weight format %s for export (%s: %s)", name, type(e).__name__, e)
+ return None
+
+
+# ---------------------------------------------------------------------------
+# HF export plan + streaming export
+# ---------------------------------------------------------------------------
+
+
+@dataclass
+class HFEntry:
+ hf_name: str
+ st_dtype: str
+ shape: tuple
+ src: str # align key
+ lo: int # global byte range in the source tensor
+ hi: int
+
+
+def _meta(manifest: dict) -> dict[str, torch.Tensor]:
+ order = manifest.get("key_order") or list(manifest["tensors"])
+ return {
+ n: torch.empty(tuple(manifest["tensors"][n]["shape"]), dtype=_ST_TO_TORCH[manifest["tensors"][n]["dtype"]], device="meta")
+ for n in order
+ }
+
+
+def build_hf_plan(manifest: dict) -> list[HFEntry]:
+ """Plan the HF tensor list from the manifest ALONE (no data). 1:1 families
+ run the family's own converter on META tensors and map outputs back by
+ object identity, so the name table is the converter's, not a copy of it.
+ OLMoE's stacked experts become per-expert byte ranges of the fused tensor."""
+ if not manifest["tensors"]:
+ raise ShardedCheckpointError("this checkpoint holds no weight tensors (adapter/optimizer-only); nothing to export")
+ ex = manifest.get("export")
+ if not ex:
+ raise ShardedCheckpointError(
+ "this checkpoint carries no export recipe (its weight format is not Dense/Olmoe/Qwen3); "
+ "export cannot guess a config"
+ )
+ fam, hfc_d = ex["family"], ex["hf_config"]
+ meta = _meta(manifest)
+ ident = {id(v): k for k, v in meta.items()}
+ entries: list[HFEntry] = []
+
+ def one_to_one(out: Mapping[str, torch.Tensor]) -> None:
+ for hf_name, v in out.items():
+ src = ident.get(id(v))
+ if src is None:
+ raise ShardedCheckpointError(f"{hf_name}: not a 1:1 image of an align tensor")
+ e = manifest["tensors"][src]
+ entries.append(HFEntry(hf_name, e["dtype"], tuple(e["shape"]), src, 0, _prod(e["shape"]) * _ST_ITEMSIZE[e["dtype"]]))
+
+ if fam == "dense":
+ from ..model import checkpoint as ckpt
+ from ..model import hf_config as hfc
+
+ cfg = hfc.from_hf_config_dict(hfc_d)
+ one_to_one(ckpt.nmo_to_hf(meta, expected_n_layers=cfg.n_layers))
+ elif fam == "qwen3":
+ from ..model.qwen3 import Qwen3Config
+ from ..model.qwen3_checkpoint import align_to_hf
+
+ one_to_one(align_to_hf(meta, Qwen3Config.from_hf_config(hfc_d)))
+ elif fam == "olmoe":
+ from ..model import olmoe_checkpoint as oc
+
+ cfg = oc.config_from_hf_dict(hfc_d)
+ real = oc.align_to_hf(meta, cfg) # the converter's own key set and shapes: the oracle
+ inter = cfg.intermediate_size
+ fused = set()
+ for i in range(cfg.n_layers):
+ gu_k, dn_k = (k.format(i=i) for k in oc._AL_FUSED)
+ fused.update((gu_k, dn_k))
+ gu, dn = manifest["tensors"][gu_k], manifest["tensors"][dn_k]
+ es = _ST_ITEMSIZE[gu["dtype"]]
+ e_n, two_i, d = gu["shape"]
+ for e in range(e_n):
+ base = e * two_i * d * es
+ half = inter * d * es
+ entries.append(HFEntry(oc._HF_EXPERT.format(i=i, e=e, p="gate"), gu["dtype"], (inter, d), gu_k, base, base + half))
+ entries.append(HFEntry(oc._HF_EXPERT.format(i=i, e=e, p="up"), gu["dtype"], (inter, d), gu_k, base + half, base + 2 * half))
+ dbase = e * dn["shape"][1] * dn["shape"][2] * es
+ entries.append(HFEntry(oc._HF_EXPERT.format(i=i, e=e, p="down"), dn["dtype"], tuple(dn["shape"][1:]), dn_k, dbase, dbase + _prod(dn["shape"][1:]) * es))
+ rest = {k: v for k, v in real.items() if id(v) in ident and ident[id(v)] not in fused}
+ one_to_one(rest)
+ got = {e.hf_name: e.shape for e in entries}
+ want = {k: tuple(v.shape) for k, v in real.items()}
+ if got != want:
+ raise ShardedCheckpointError(
+ f"olmoe export plan disagrees with align_to_hf: only-plan={sorted(set(got) - set(want))[:3]} "
+ f"only-converter={sorted(set(want) - set(got))[:3]} "
+ f"shape-diff={[k for k in got if k in want and got[k] != want[k]][:3]}"
+ )
+ else:
+ raise ShardedCheckpointError(f"unknown export family {fam!r}")
+ return entries
+
+
+def export_hf(src: str, dst: str, *, verify: bool = True) -> dict:
+ """Sharded checkpoint -> HF-layout directory, tensor by tensor.
+
+ Byte-identical to what the gathered path writes (`save_nmo_as_hf` /
+ `save_olmoe_as_hf` / `save_hf_qwen3`) -- tested by file hash -- with peak RSS
+ of one `COPY_CHUNK` instead of 2x the weights. `dst` is staged as
+ `dst.partial`, fsynced and renamed: an interrupted export leaves nothing at
+ `dst`.
+ """
+ if os.path.exists(dst):
+ raise FileExistsError(f"{dst} exists; export never overwrites")
+ m = read_manifest(src)
+ if verify:
+ for r in range(m["world_size"]):
+ bad = verify_rank_file(src, r)
+ if bad:
+ raise ShardedCheckpointError(bad)
+ plan = build_hf_plan(m)
+ fam = m["export"]["family"]
+ staging = dst.rstrip("/") + ".partial"
+ if os.path.isdir(staging):
+ shutil.rmtree(staging)
+ os.makedirs(staging)
+ try:
+ with GlobalReader(src, m) as g:
+ def make_entry(h: HFEntry) -> StEntry:
+ def emit(f, h=h):
+ g.copy_range(h.src, h.lo, h.hi, f)
+
+ return StEntry(h.hf_name, h.st_dtype, h.shape, emit)
+
+ by_name = {h.hf_name: h for h in plan}
+ if fam == "qwen3":
+ from ..model.qwen3_checkpoint import MAX_SHARD_BYTES, _plan_shards
+
+ sizes = {h.hf_name: _prod(h.shape) * _ST_ITEMSIZE[h.st_dtype] for h in plan}
+ groups = _plan_shards(sizes, MAX_SHARD_BYTES)
+ weight_map: dict[str, str] = {}
+ for n, keys in enumerate(groups, start=1):
+ fname = f"model-{n:05d}-of-{len(groups):05d}.safetensors"
+ write_safetensors_streaming(
+ os.path.join(staging, fname), [make_entry(by_name[k]) for k in keys], {"format": "pt"}
+ )
+ weight_map.update({k: fname for k in keys})
+ with open(os.path.join(staging, "model.safetensors.index.json"), "w") as fh:
+ json.dump({"metadata": {"total_size": sum(sizes.values())}, "weight_map": weight_map}, fh, indent=2, sort_keys=True)
+ fh.flush()
+ os.fsync(fh.fileno())
+ else:
+ write_safetensors_streaming(
+ os.path.join(staging, "model.safetensors"), [make_entry(h) for h in plan], {"format": "pt"}
+ )
+ with open(os.path.join(staging, "config.json"), "w", encoding="utf-8") as fh:
+ fh.write(json.dumps(m["export"]["hf_config"], indent=2, sort_keys=True) + "\n")
+ fh.flush()
+ os.fsync(fh.fileno())
+ for fn in sorted(os.listdir(src)):
+ if not _OWN_FILE_RE.match(fn) and os.path.isfile(os.path.join(src, fn)) and fn != "config.json":
+ shutil.copyfile(os.path.join(src, fn), os.path.join(staging, fn))
+ _fsync_tree(staging)
+ os.rename(staging, dst)
+ _fsync_dir(os.path.dirname(os.path.abspath(dst)))
+ except BaseException:
+ shutil.rmtree(staging, ignore_errors=True)
+ raise
+ return {"tensors": len(plan), "family": fam, "dst": dst}
+
+
+def _fsync_dir(p: str) -> None:
+ fd = os.open(p, os.O_RDONLY)
+ try:
+ os.fsync(fd)
+ finally:
+ os.close(fd)
+
+
+def _fsync_tree(root: str) -> None:
+ dirs = []
+ for dp, _dn, fns in os.walk(root):
+ dirs.append(dp)
+ for fn in fns:
+ fd = os.open(os.path.join(dp, fn), os.O_RDONLY)
+ try:
+ os.fsync(fd)
+ finally:
+ os.close(fd)
+ for d in reversed(dirs):
+ _fsync_dir(d)
+
+
+# ---------------------------------------------------------------------------
+# offline reshard
+# ---------------------------------------------------------------------------
+
+
+class ReshardUnsupported(ShardedCheckpointError):
+ """Optimizer state whose layout cannot be re-cut without knowing the optimizer."""
+
+
+def _check_opt_leaf(vals: list, shapes_old: list[tuple], name: str, where: str) -> None:
+ """Every old rank's piece must have exactly the LOCAL shard shape of the parameter it is
+ filed under (shape metadata only: reads nothing from an mmap'd file)."""
+ for r, (v, want) in enumerate(zip(vals, shapes_old)):
+ if tuple(v.shape) != tuple(want):
+ raise ReshardUnsupported(
+ f"{where}: tensor shape {tuple(v.shape)} on old rank {r} != the local shard {tuple(want)} of "
+ f"parameter {name!r}. This optimizer state is not elementwise-per-shard (quantised/blocked "
+ "moments?) or its parameter order differs from the model's; it cannot be re-cut safely."
+ )
+
+
+def _cut_opt_leaf(vals: list, old_rows: Optional[list], new_ws: int, new_rank: int) -> torch.Tensor:
+ """NEW rank `new_rank`'s piece of one per-param optimizer tensor, built from only the
+ old pieces that overlap its rows (no whole-tensor concatenation, no other new rank)."""
+ if old_rows is None: # replicated parameter
+ return vals[0].clone()
+ total = sum(c for _o, c in old_rows)
+ off, n = chunk_range(total, new_ws, new_rank)
+ parts = []
+ for v, (qo, qc) in zip(vals, old_rows):
+ a, b = max(off, qo), min(off + n, qo + qc)
+ if a < b:
+ parts.append(v[a - qo : b - qo])
+ if not parts:
+ return torch.empty((0,) + tuple(vals[0].shape[1:]), dtype=vals[0].dtype)
+ return torch.cat(parts, dim=0) if len(parts) > 1 else parts[0].clone()
+
+
+def iter_reshard_optimizer(old: list[dict], manifest: dict, new_ws: int):
+ """Yield the re-cut optimizer state dict of NEW rank 0, 1, ... one at a time.
+
+ THE ALL-RANKS-AT-ONCE DEFECT this shape prevents. The first version returned a
+ list holding every new rank's state, so its peak was the WHOLE optimizer
+ (12 B/param for fp32-master AdamW: 83 GB for a 7B model) while the CLI
+ estimated `optimizer_bytes / new_ws` and the 45 GB floor check was sized on
+ that. Measured on 4 old -> 4 new ranks, 50 M params: anonymous-RSS growth 576
+ MiB against an estimate of 144 MiB, i.e. `new_ws` times the estimate. On this
+ no-swap shared box a reshard that passes the preflight at 21 GB and takes 83
+ GB is the 2026-08-12 OOM. Here only the old pieces that overlap the rank being
+ built are read (mmap'd), and the caller writes and drops each yielded dict
+ before asking for the next, so the peak is one new rank's state.
+
+ Every shape/consistency refusal is raised BEFORE the first yield, so a
+ resharder that cannot prove it understands the state writes no file at all.
+ """
+ order = manifest.get("param_order")
+ if not order:
+ raise ReshardUnsupported("manifest has no param_order: cannot map optimizer slots to parameters")
+ ws = manifest["world_size"]
+ if len(old) != ws:
+ raise ReshardUnsupported(f"{len(old)} optimizer file(s) for a {ws}-rank checkpoint")
+ if "world_size" in old[0]:
+ raise ReshardUnsupported("optimizer state is stamped with its world_size (quantised shard-local state)")
+
+ def entry(pname: str) -> dict:
+ e = manifest["tensors"].get(pname)
+ if e is None:
+ raise ReshardUnsupported(f"param_order names {pname!r}, which is not a tensor of this checkpoint")
+ return e
+
+ def local_shape(pname: str, r: int) -> tuple:
+ e = entry(pname)
+ if e["placement"] == "replicate":
+ return tuple(e["shape"])
+ return (e["rows"][r][1],) + tuple(e["shape"][1:])
+
+ def rows_of(pname: str) -> Optional[list]:
+ e = entry(pname)
+ return None if e["placement"] == "replicate" else [tuple(x) for x in e["rows"]]
+
+ def agree(vals: list, where: str):
+ v0 = vals[0]
+ for v in vals[1:]:
+ same = torch.equal(v, v0) if isinstance(v0, torch.Tensor) else v == v0
+ if not same:
+ raise ReshardUnsupported(f"{where}: differs across old ranks ({v0!r} vs {v!r})")
+ return v0
+
+ # -- pass 1: validate everything, build a per-key plan (no tensor payload read) -----------
+ plan: list[tuple] = []
+ for key in old[0]:
+ vals = [o[key] for o in old]
+ if key == "state":
+ leaves = [] # (idx, k, kind, payload)
+ for idx in vals[0]:
+ if not 0 <= int(idx) < len(order):
+ raise ReshardUnsupported(f"state slot {idx} is outside param_order ({len(order)} entries)")
+ pname = order[int(idx)]
+ for k in vals[0][idx]:
+ leaf = [v[idx][k] for v in vals]
+ where = f"state[{idx}][{k}]"
+ if isinstance(leaf[0], torch.Tensor) and leaf[0].ndim >= 1:
+ _check_opt_leaf(leaf, [local_shape(pname, r) for r in range(ws)], pname, where)
+ leaves.append((idx, k, "cut", (leaf, rows_of(pname))))
+ else:
+ leaves.append((idx, k, "same", agree(leaf, where)))
+ plan.append((key, "state", leaves))
+ elif key == "master":
+ if len(vals[0]) != len(order) or any(len(v) != len(order) for v in vals):
+ raise ReshardUnsupported(
+ f"master has {[len(v) for v in vals]} entries per old rank, param_order {len(order)}: the "
+ "optimizer was not built over the model's trainable parameters in order"
+ )
+ masters = []
+ for i, pname in enumerate(order):
+ leaf = [v[i] for v in vals]
+ _check_opt_leaf(leaf, [local_shape(pname, r) for r in range(ws)], pname, f"master[{i}]")
+ masters.append((leaf, rows_of(pname)))
+ plan.append((key, "master", masters))
+ else:
+ if _has_tensor(vals[0], min_ndim=1):
+ raise ReshardUnsupported(f"optimizer key {key!r} holds sharded tensors this resharder does not understand")
+ plan.append((key, "plain", _agree_tree(vals, key)))
+
+ # -- pass 2: build ONE new rank at a time ---------------------------------------------------
+ for r in range(new_ws):
+ out: dict = {}
+ for key, kind, payload in plan:
+ if kind == "state":
+ st: dict = {}
+ for idx, k, how, p in payload:
+ st.setdefault(idx, {})[k] = _cut_opt_leaf(p[0], p[1], new_ws, r) if how == "cut" else p
+ out[key] = st
+ elif kind == "master":
+ out[key] = [_cut_opt_leaf(leaf, rows, new_ws, r) for leaf, rows in payload]
+ else:
+ out[key] = payload
+ yield out
+
+
+def reshard_optimizer(old: list[dict], manifest: dict, new_ws: int) -> list[dict]:
+ """Re-cut per-rank optimizer state dicts for `new_ws`, all at once (tests, small models).
+
+ Production resharding uses `iter_reshard_optimizer` and writes each rank
+ before building the next; this wrapper holds every new rank in memory.
+
+ Handles the layout `torch.optim.AdamW` / `OffloadAdamW` produce:
+ `state[idx][k]` tensors and a `master` list, each shaped like the LOCAL
+ shard of parameter `param_order[idx]`. Scalars (`step`) and non-tensor
+ leaves must agree across old ranks. ANYTHING else that holds a tensor with
+ ndim >= 1 (adamw8bit's quantised blocks, a `world_size`-stamped state)
+ raises `ReshardUnsupported`: re-cutting what we do not understand is how a
+ resume loads plausible, wrong moments.
+ """
+ return list(iter_reshard_optimizer(old, manifest, new_ws))
+
+
+def _has_tensor(o: Any, min_ndim: int) -> bool:
+ if isinstance(o, torch.Tensor):
+ return o.ndim >= min_ndim
+ if isinstance(o, dict):
+ return any(_has_tensor(v, min_ndim) for v in o.values())
+ if isinstance(o, (list, tuple)):
+ return any(_has_tensor(v, min_ndim) for v in o)
+ return False
+
+
+def _agree_tree(vals: list, where: str):
+ v0 = vals[0]
+ for v in vals[1:]:
+ if repr(v) != repr(v0):
+ raise ReshardUnsupported(f"{where}: differs across old ranks")
+ return v0
+
+
+def reshard(src: str, dst: str, new_ws: int, *, drop_optimizer: bool = False) -> dict:
+ """Offline reshard of a sharded checkpoint to `new_ws` ranks.
+
+ Model shards are re-cut by byte range (bounded memory). Optimizer files are
+ re-cut by `reshard_optimizer` (peak RSS = one NEW rank's optimizer state --
+ at 7B fp32-master AdamW that is 12 B/param/new_ws; run it on an idle box).
+ With `drop_optimizer=True` the optimizer files are omitted and the loop
+ state is marked, so resume starts FRESH moments: a recorded deviation, and
+ NOT lossless when the optimizer holds the fp32 master.
+ `resume.pt` is carried over with its world-size stamp rewritten and the
+ per-rank RNG list dropped (the trainer restarts RNG from the seed with a
+ warning; carrying N_old streams into N_new ranks would be wrong).
+ """
+ if new_ws < 1:
+ raise ValueError("new_ws must be >= 1")
+ m = read_manifest(src)
+ for r in range(m["world_size"]):
+ bad = verify_rank_file(src, r)
+ if bad:
+ raise ShardedCheckpointError(bad)
+ if os.path.exists(dst):
+ raise FileExistsError(f"{dst} exists; reshard never overwrites")
+ staging = dst.rstrip("/") + ".partial"
+ if os.path.isdir(staging):
+ shutil.rmtree(staging)
+ os.makedirs(staging)
+ try:
+ infos = []
+ with GlobalReader(src, m) as g:
+ for r in range(new_ws):
+ entries, tdesc = [], {}
+ for name in sorted(m["tensors"]):
+ e = m["tensors"][name]
+ dt, shape = e["dtype"], tuple(e["shape"])
+ if e["placement"] == "shard0":
+ off, cnt = chunk_range(shape[0], new_ws, r)
+ lshape = (cnt,) + shape[1:]
+ rb = _prod(shape[1:]) * _ST_ITEMSIZE[dt]
+ lo, hi = off * rb, (off + cnt) * rb
+ tdesc[name] = {"shape": list(shape), "placement": "shard0", "row": [off, cnt], "dtype": dt}
+ else:
+ lshape, lo, hi = shape, 0, g.nbytes(name)
+ tdesc[name] = {"shape": list(shape), "placement": "replicate", "row": None, "dtype": dt}
+
+ def emit(f, name=name, lo=lo, hi=hi):
+ g.copy_range(name, lo, hi, f)
+
+ entries.append(StEntry(name, dt, lshape, emit))
+ fname = rank_file_name(r)
+ meta = write_safetensors_streaming(os.path.join(staging, fname), entries, {"format": "pt"})
+ infos.append({"file": {"name": fname, **meta}, "tensors": tdesc, "order": m.get("key_order")})
+ new_manifest = build_manifest(
+ infos,
+ param_order=m.get("param_order"),
+ export=m.get("export"),
+ extra={"resharded_from": {"world_size": m["world_size"], "source": os.path.basename(src.rstrip("/"))}},
+ )
+ write_manifest(staging, new_manifest)
+
+ state = torch.load(os.path.join(src, "resume.pt"), weights_only=True, map_location="cpu")
+ state.pop("rng_per_rank", None)
+ if drop_optimizer:
+ state["optimizer_state"] = {"layout": "none", "world_size": new_ws}
+ else:
+ state["optimizer_state"] = {"layout": "per_rank_files", "world_size": new_ws}
+ olds = [
+ torch.load(os.path.join(src, f"optimizer_rank{r}.pt"), weights_only=True, map_location="cpu", mmap=True)
+ for r in range(m["world_size"])
+ ]
+ for r, sd in enumerate(iter_reshard_optimizer(olds, m, new_ws)):
+ p = os.path.join(staging, f"optimizer_rank{r}.pt")
+ with open(p, "wb") as f:
+ torch.save(sd, f)
+ f.flush()
+ os.fsync(f.fileno())
+ del sd # one new rank's state alive at a time
+ del olds
+ with open(os.path.join(staging, "resume.pt"), "wb") as f:
+ torch.save(state, f)
+ f.flush()
+ os.fsync(f.fileno())
+ write_integrity(
+ staging,
+ {
+ fn: file_sha256(os.path.join(staging, fn))
+ for fn in sorted(os.listdir(staging))
+ if fn == "resume.pt" or re.fullmatch(r"optimizer_rank\d+\.pt", fn)
+ },
+ )
+ for fn in sorted(os.listdir(src)):
+ if not _OWN_FILE_RE.match(fn) and os.path.isfile(os.path.join(src, fn)):
+ shutil.copyfile(os.path.join(src, fn), os.path.join(staging, fn))
+ _fsync_tree(staging)
+ os.rename(staging, dst)
+ _fsync_dir(os.path.dirname(os.path.abspath(dst)))
+ except BaseException:
+ shutil.rmtree(staging, ignore_errors=True)
+ raise
+ return {"dst": dst, "world_size": new_ws, "from": m["world_size"], "optimizer": "dropped" if drop_optimizer else "resharded"}
+
+
+def peak_rss_kb() -> int:
+ """VmHWM of this process in kB (psutil is not a dependency)."""
+ with open("/proc/self/status") as f:
+ for line in f:
+ if line.startswith("VmHWM:"):
+ return int(line.split()[1])
+ return -1
+
+
+__all__ = [
+ "CHECKPOINT_FORMATS",
+ "SHARD_MANIFEST_NAME",
+ "ShardedCheckpointError",
+ "ShardedWorldSizeError",
+ "ReshardUnsupported",
+ "chunk_range",
+ "save_rank_shard",
+ "build_manifest",
+ "write_manifest",
+ "read_manifest",
+ "is_sharded",
+ "sharded_problem",
+ "verify_rank_file",
+ "load_rank_shard",
+ "load_full_state_dict",
+ "describe_weight_format",
+ "build_hf_plan",
+ "export_hf",
+ "reshard",
+ "reshard_optimizer",
+ "iter_reshard_optimizer",
+ "peak_rss_kb",
+]
diff --git a/code/align/train/stage_gates.py b/code/align/train/stage_gates.py
new file mode 100644
index 0000000000000000000000000000000000000000..ea10246115985b0e503234867134ddccc62e9d75
--- /dev/null
+++ b/code/align/train/stage_gates.py
@@ -0,0 +1,1002 @@
+"""Stage gate sequencing and decisions (section-11).
+
+This is the first stage that spends real GPU-hours, so the job of this module
+is to make wasting them structurally impossible rather than merely unlikely.
+Everything here is a pure decision over recorded facts; the stage script drives
+it and owns the side effects.
+
+WHY THE ORDER IS FIXED, and why it is enforced instead of documented:
+
+ golden_regression -> smoke_7b -> probe_20step -> rss_preflight
+ -> train -> eval -> quality_gate
+
+ * The golden regression runs before ANY new training because it validates
+ the eval stack itself against numbers that are already known. A defective
+ harness silently corrupts every downstream measurement, and a full-epoch
+ run that under-performs a 0.35-epoch run then looks like a modeling
+ result instead of the stack bug it is.
+ * The 7B smoke runs before the probe because the chunked-loss peak-memory
+ assertion is only meaningful at 7B on the real 4-GPU topology. A
+ unit-scale memory check cannot catch what a 32768-vocab logit tensor does
+ to a 23GB card.
+ * The probe runs before the preflight and the launch because a measured step
+ time that invalidates the schedule must stop the run BEFORE it starts, not
+ three hours in.
+
+MODULE-LEVEL IMPORTS ARE DELIBERATELY TORCH-FREE. `scripts/sft.py` imports this
+module above `apply_resource_guards()`, and torch reads
+PYTORCH_CUDA_ALLOC_CONF, the cache paths and the thread caps AT IMPORT -- so
+anything that pulls torch in transitively (probes, trainer, model) is imported
+lazily inside the function that needs it. A test asserts this.
+"""
+
+from __future__ import annotations
+
+import hashlib
+import json
+import logging
+import math
+import os
+from dataclasses import dataclass
+from datetime import datetime, timezone
+from typing import Any, Optional
+
+from ..registry import MANDATORY_BANNER_KEYS, assert_banner_complete
+
+logger = logging.getLogger(__name__)
+
+NAN = float("nan")
+
+#: Explicit "not measured". Never absent, never a plausible substitute.
+PENDING = "PENDING"
+
+#: The fixed stage sequence. Data, not prose: the ledger iterates it.
+GATE_ORDER: tuple[str, ...] = (
+ "golden_regression",
+ "smoke_7b",
+ "probe_20step",
+ "rss_preflight",
+ "train",
+ "eval",
+ "quality_gate",
+)
+
+#: Gates whose result depends on the eval stack. A change under `align/eval/`
+#: invalidates exactly these -- re-running the 7B smoke or the probe because
+#: someone edited a verifier would burn GPU-hours for nothing.
+EVAL_DEPENDENT_GATES: frozenset[str] = frozenset({"golden_regression", "eval", "quality_gate"})
+
+LEDGER_FILENAME = "stage_gates.json"
+
+
+def _is_rank0() -> bool:
+ """Rank from the environment, so this works before (and without) any
+ process group -- same convention as the heavy-job flock."""
+ return int(os.environ.get("RANK", "0")) == 0
+
+
+class GateOrderError(RuntimeError):
+ """Raised when a gate runs before its predecessors have passed."""
+
+
+@dataclass
+class GateDecision:
+ passed: bool
+ halt_message: str = ""
+ detail: dict = None # type: ignore[assignment]
+
+
+# ---------------------------------------------------------------------------
+# ledger
+# ---------------------------------------------------------------------------
+
+
+def eval_source_hash(repo_root: Optional[str] = None) -> str:
+ """Hash the `align/eval/` source tree.
+
+ The golden regression certifies the harness, so its pass is only valid for
+ the harness that produced it. Keying the ledger entry on this hash is what
+ turns "we ran the golden regression once" into "we ran it against THIS
+ code".
+ """
+ root = repo_root or os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+ eval_dir = os.path.join(root, "align", "eval")
+ h = hashlib.sha256()
+ for dirpath, _dirs, files in sorted(os.walk(eval_dir)):
+ for fn in sorted(files):
+ if not fn.endswith(".py"):
+ continue
+ path = os.path.join(dirpath, fn)
+ h.update(os.path.relpath(path, eval_dir).encode())
+ with open(path, "rb") as f:
+ h.update(f.read())
+ return h.hexdigest()[:16]
+
+
+class GateLedger:
+ """Records gate results, enforces the order, survives a restart."""
+
+ def __init__(self, run_dir: str, eval_source_hash: Optional[str] = None):
+ self.run_dir = run_dir
+ os.makedirs(run_dir, exist_ok=True)
+ self.path = os.path.join(run_dir, LEDGER_FILENAME)
+ self.eval_source_hash = eval_source_hash
+ self._entries: dict[str, dict] = {}
+ self._load()
+
+ # -- persistence --------------------------------------------------------
+
+ def _load(self) -> None:
+ if not os.path.isfile(self.path):
+ return
+ try:
+ with open(self.path) as f:
+ blob = json.load(f)
+ except (OSError, json.JSONDecodeError):
+ logger.warning("unreadable gate ledger at %s; starting fresh", self.path)
+ return
+
+ recorded_hash = blob.get("eval_source_hash")
+ self._entries = dict(blob.get("gates", {}))
+ if (
+ self.eval_source_hash is not None
+ and recorded_hash is not None
+ and recorded_hash != self.eval_source_hash
+ ):
+ dropped = [g for g in EVAL_DEPENDENT_GATES if g in self._entries]
+ for g in dropped:
+ del self._entries[g]
+ if dropped:
+ logger.warning(
+ "align/eval/ changed since this ledger was written (%s -> %s); "
+ "invalidated %s. The golden regression certifies the eval "
+ "stack, so its pass does not carry across a change to it.",
+ recorded_hash,
+ self.eval_source_hash,
+ sorted(dropped),
+ )
+
+ def _save(self) -> None:
+ """Persist. RANK 0 ONLY -- see `record`."""
+ if not _is_rank0():
+ return
+ tmp = self.path + ".partial"
+ with open(tmp, "w") as f:
+ json.dump(
+ {"eval_source_hash": self.eval_source_hash, "gates": self._entries},
+ f,
+ indent=1,
+ sort_keys=True,
+ # Runs on EVERY record: the site with the most chances to meet
+ # a non-serializable detail (attempt-6 class).
+ default=str,
+ )
+ os.replace(tmp, self.path)
+
+ # -- recording ----------------------------------------------------------
+
+ def record(self, gate: str, passed: bool, detail: str = "") -> dict:
+ if gate not in GATE_ORDER:
+ raise GateOrderError(f"unknown gate {gate!r}; the sequence is {list(GATE_ORDER)}")
+ idx = GATE_ORDER.index(gate)
+ for earlier in GATE_ORDER[:idx]:
+ if not self.is_passed(earlier):
+ raise GateOrderError(
+ f"refusing to record {gate!r}: {earlier!r} has not passed "
+ f"(it reads {self.status()[earlier]!r}). The stage sequence "
+ f"is fixed at {list(GATE_ORDER)} and each gate exists to "
+ "stop the next one from wasting GPU-hours. Run the earlier "
+ "gate, or fix why it failed."
+ )
+ entry = {
+ "passed": bool(passed),
+ "detail": detail,
+ "recorded_at": datetime.now(timezone.utc).isoformat(timespec="seconds"),
+ }
+ # EVERY rank updates its own in-memory view; only RANK 0 persists.
+ #
+ # THE LEDGER RENAME RACE (killed attempt 3, past the smoke): all four
+ # ranks must run the gates -- the 7B smoke needs the full FSDP
+ # topology -- so all four called this, all four wrote the SAME
+ # `.partial`, the first rename consumed it and the other three died on
+ # FileNotFoundError. A gate is a property of the JOB, not of a rank.
+ #
+ # NO BARRIER, deliberately, and this is the safer half of the choice.
+ # Every rank keeps an identical in-memory ledger (the order checks and
+ # the passed flag are deterministic from local state), so no rank ever
+ # needs to READ the file mid-run -- which means no collective here,
+ # which means no way for a rank-0 raise to strand its peers waiting on
+ # one. A barrier would have re-introduced the NCCL-timeout shape to
+ # fix a filesystem race. Gate failures are themselves deterministic
+ # across ranks (same config, same files, host-wide MemAvailable), so
+ # all ranks raise together and torchrun propagates it.
+ self._entries[gate] = entry
+ self._save()
+ logger.info("gate %s: %s (%s)", gate, "PASS" if passed else "FAIL", detail or "-")
+ return entry
+
+ # -- reading ------------------------------------------------------------
+
+ def is_passed(self, gate: str) -> bool:
+ return self._entries.get(gate, {}).get("passed") is True
+
+ def status(self) -> dict:
+ """Every gate present; unrun gates read PENDING, never absent."""
+ return {g: (self._entries[g] if g in self._entries else PENDING) for g in GATE_ORDER}
+
+ def next_gate(self) -> Optional[str]:
+ for gate in GATE_ORDER:
+ if not self.is_passed(gate):
+ return gate
+ return None
+
+ def require(self, gate: str) -> None:
+ if not self.is_passed(gate):
+ raise GateOrderError(
+ f"gate {gate!r} has not passed (reads {self.status()[gate]!r}); "
+ "refusing to continue"
+ )
+
+
+# ---------------------------------------------------------------------------
+# individual gates
+# ---------------------------------------------------------------------------
+
+
+def _mem_available_gb() -> float:
+ with open("/proc/meminfo") as f:
+ for line in f:
+ if line.startswith("MemAvailable:"):
+ return float(line.split()[1]) / 1024**2
+ raise RuntimeError("/proc/meminfo has no MemAvailable line")
+
+
+def rss_preflight(cfg, mem_available_gb: Optional[float] = None) -> dict:
+ """`MemAvailable >= projected_job_RSS + headroom`, or refuse to start.
+
+ The generic 45GB launch floor is insufficient for this job class and that
+ insufficiency IS the 2026-08-12 incident: the offloaded optimizer state
+ (~87GB) arrives over the first optimizer steps, AFTER any launch-time free
+ check has passed. On a box with no swap there is no overflow -- exhausting
+ RAM wedges the host in D-state and takes unrelated runs down with it.
+ """
+ from .probes import projected_job_rss # imports torch; kept lazy
+
+ projection = projected_job_rss(cfg)
+ projected = projection["total_gb"]
+ headroom = cfg.gates.rss_headroom_gb
+ required = projected + headroom
+ available = _mem_available_gb() if mem_available_gb is None else float(mem_available_gb)
+
+ result = {
+ "passed": available >= required,
+ "mem_available_gb": available,
+ "projected_job_rss_gb": projected,
+ "headroom_gb": headroom,
+ "required_gb": required,
+ "breakdown": projection,
+ }
+ if not result["passed"]:
+ raise RuntimeError(
+ f"RSS PREFLIGHT FAILED: MemAvailable {available:.1f}GB < projected "
+ f"job RSS {projected:.1f}GB + {headroom:.0f}GB headroom = "
+ f"{required:.1f}GB.\nBreakdown: {projection}\nThis box has NO SWAP, "
+ "so there is no overflow: the offloaded optimizer state lands after "
+ "launch and would wedge the host mid-run (2026-08-12: 2GB free, "
+ "load 619, a healthy run killed by a 5-hour NCCL stall). Wait for "
+ "the host to quiesce, or switch optimizer.name to adamw8bit, which "
+ "drops the host-state term entirely."
+ )
+ logger.info("RSS preflight ok: %.1fGB available >= %.1fGB required", available, required)
+ return result
+
+
+def schedule_check(probe_report: dict, cfg, optimizer_steps_planned: int) -> dict:
+ """Project epoch wall-clock from the measured step time, or refuse.
+
+ A measured step time that invalidates the schedule must stop the run before
+ it starts. Three hours into an epoch is too late to discover that the
+ epoch was never going to fit the window.
+ """
+ step_time = float(probe_report.get("mean_step_time_s", NAN))
+ if math.isnan(step_time):
+ raise RuntimeError(
+ "schedule check refused: the probe did not measure a step time "
+ "(mean_step_time_s is NaN). Every comparison against NaN is False, "
+ "so a naive budget check would read 'within budget' and launch. "
+ "Re-run the 20-step probe."
+ )
+ if optimizer_steps_planned <= 0:
+ raise ValueError(f"optimizer_steps_planned={optimizer_steps_planned} is not a schedule")
+
+ projected = step_time * optimizer_steps_planned
+ allowed = cfg.gates.epoch_budget_s * cfg.gates.schedule_budget_factor
+ result = {
+ "passed": projected <= allowed,
+ "mean_step_time_s": step_time,
+ "optimizer_steps_planned": optimizer_steps_planned,
+ "projected_epoch_s": projected,
+ "budget_s": cfg.gates.epoch_budget_s,
+ "allowed_s": allowed,
+ }
+ if not result["passed"]:
+ raise RuntimeError(
+ f"SCHEDULE CHECK FAILED: measured step time {step_time:.2f}s x "
+ f"{optimizer_steps_planned} optimizer steps projects an epoch "
+ f"wall-clock of {projected:.0f}s, beyond the allowed "
+ f"{allowed:.0f}s (budget {cfg.gates.epoch_budget_s:.0f}s x factor "
+ f"{cfg.gates.schedule_budget_factor}). Refusing to launch: the "
+ "GPU-hours are spent whether or not the epoch finishes. Either "
+ "raise gates.epoch_budget_s deliberately, or find the step-time "
+ "regression first."
+ )
+ logger.info("schedule ok: %.0fs projected <= %.0fs allowed", projected, allowed)
+ return result
+
+
+def quality_gate(report: dict, cfg) -> GateDecision:
+ """Does the new SFT checkpoint reach the golden anchors?
+
+ Delegates the accuracy-vs-CI comparison to section-10's
+ `assert_meets_golden` rather than restating it: that function compares
+ against the WILSON LOWER BOUND (the golden numbers are themselves
+ measurements with sampling noise, so demanding the point estimate meet
+ them would fail correct runs about half the time), and two copies of that
+ reasoning would drift.
+
+ NaN halts. That is the extraction-ceiling incident in gate form: a PERFECT
+ policy scored 0/100 for two full PPO runs because extraction was broken,
+ and an undefined accuracy that compared False would read as 'above golden'.
+ """
+ from ..eval.harness import assert_meets_golden
+
+ golden = {"math500": cfg.gates.golden_math500, "gsm8k": cfg.gates.golden_gsm8k}
+ try:
+ assert_meets_golden(report, golden)
+ except RuntimeError as exc:
+ return GateDecision(
+ passed=False,
+ halt_message=(
+ f"{exc}\n\nSTAGE HALTED. Section-12 (DPO) must not start on a "
+ "failed SFT gate. Bisect in this order before touching any "
+ "hyperparameter -- DATA (is the corpus the rebuilt one, and "
+ "does a decoded sample match the training format?), then LOSS "
+ "(does fused_ce still match the eager reference?), then "
+ "TRAINER (did the trunk-movement probe pass, and did the "
+ "schedule run in optimizer steps?), then EVAL (does the golden "
+ "regression still reproduce 0.09/0.258?). A full-epoch run "
+ "below a 0.35-epoch anchor is near-certain evidence of a "
+ "new-stack defect, not a modeling result."
+ ),
+ detail={"golden": golden, "report": report.get("per_set", {})},
+ )
+ return GateDecision(passed=True, detail={"golden": golden})
+
+
+def write_quality_gate_receipt(cfg, decision: "GateDecision", checkpoint: str = "") -> str:
+ """Write the receipt section-12 reads before it will start.
+
+ The contract is narrow on purpose: a JSON object with a BOOLEAN `passed`.
+ Section-12's `read_sft_quality_gate` refuses anything else -- absent,
+ PENDING, or a truthy string all read as "not passed", because the whole
+ point is that a DPO run cannot begin on an SFT stage that did not clear
+ its own gate.
+ """
+ path = cfg.paths.sft_quality_gate_path
+ if not _is_rank0():
+ # Same rename race as the ledger: one receipt, written by the job.
+ return path
+ os.makedirs(os.path.dirname(path), exist_ok=True)
+ payload = {
+ "passed": bool(decision.passed),
+ # `detail` is the key SECTION-12's reader quotes in its refusal
+ # message. Emitting only `halt_message` would leave their error
+ # reading "(no detail recorded)" -- technically a correct refusal,
+ # and useless to whoever has to act on it.
+ "detail": (
+ decision.halt_message.splitlines()[0]
+ if decision.halt_message
+ else "quality gate passed"
+ ),
+ "halt_message": decision.halt_message,
+ "checkpoint": checkpoint,
+ "golden": (decision.detail or {}).get("golden", {}),
+ "recorded_at": datetime.now(timezone.utc).isoformat(timespec="seconds"),
+ }
+ tmp = path + ".partial"
+ with open(tmp, "w") as f:
+ # default=str: this receipt is written AFTER the full epoch and both
+ # evals -- a non-serializable value here would discard the run's
+ # verdict at maximum cost (the smoke-receipt Tensor crash, attempt 6).
+ json.dump(payload, f, indent=1, sort_keys=True, default=str)
+ os.replace(tmp, path)
+ logger.info("SFT quality-gate receipt written: %s (passed=%s)", path, payload["passed"])
+ return path
+
+
+def assert_build_is_current(manifest_path: str, cfg) -> dict:
+ """Refuse to train on a data build that predates the last semantic fix.
+
+ SECTION-03 REOPENED: every SFT target rendered `# Answer` TWICE -- the raw
+ PRM800K final step carries the marker inline and the renderer prepended it
+ again. Extraction survived by taking the last match, so nothing failed
+ loudly; the cost is training on a format the extraction ceiling never
+ exercised.
+
+ Read honestly from the manifest, with no hardcoded dates: `pins_verified`
+ must be True, `pin_violations` must be empty, and `build_version` must
+ reach the configured minimum. A MISSING `build_version` raises -- an
+ unstamped build is not evidence of a current one.
+ """
+ if not os.path.isfile(manifest_path):
+ raise RuntimeError(
+ f"no data build manifest at {manifest_path}. The stage consumes "
+ "shard hashes from the build artifact and never rebuilds data; "
+ "run scripts/build_data.py first."
+ )
+
+ # Section-03 drops a DEFECTIVE marker beside a build it has condemned.
+ # Checked FIRST and independently of build_version: a human-written "do
+ # not train on this" is the most direct signal there is, and honouring it
+ # costs one stat() while ignoring it costs a 4-hour epoch.
+ defective = os.path.join(os.path.dirname(os.path.abspath(manifest_path)), "DEFECTIVE")
+ if os.path.isfile(defective):
+ with open(defective) as f:
+ reason = f.read().strip()
+ raise RuntimeError(
+ f"the data build at {os.path.dirname(defective)} is marked "
+ f"DEFECTIVE by section-03:\n\n{reason}\n\nRefusing to train on "
+ "condemned shards."
+ )
+
+ with open(manifest_path) as f:
+ manifest = json.load(f)
+
+ if manifest.get("pins_verified") is not True:
+ raise RuntimeError(
+ f"{manifest_path}: pins_verified is "
+ f"{manifest.get('pins_verified')!r}, not True. The corpus counts "
+ "were not corroborated against their pinned values, so the stage "
+ "cannot say what it would be training on."
+ )
+ violations = manifest.get("pin_violations") or []
+ if violations:
+ raise RuntimeError(
+ f"{manifest_path}: pins_verified is True but pin_violations is "
+ f"non-empty: {violations}. Trust the violations, not the flag."
+ )
+
+ version = manifest.get("build_version")
+ minimum = cfg.gates.min_build_version
+ if version is None:
+ raise RuntimeError(
+ f"{manifest_path} has no `build_version` field, so the stage "
+ "cannot tell whether this corpus predates the doubled-`# Answer`-"
+ "marker fix. An unstamped build is NOT assumed current: section-03 "
+ "stamps build_version (>= 2 after the fix; 1 is everything before "
+ "it) and this stage requires at least "
+ f"{minimum}. Rebuild the corpus, or add the field if the rebuild "
+ "already happened."
+ )
+ if int(version) < minimum:
+ raise RuntimeError(
+ f"{manifest_path}: build_version {version} < required {minimum}. "
+ "This corpus predates the doubled-`# Answer`-marker fix -- every "
+ "SFT target renders the marker twice, which trains a format the "
+ "extraction ceiling never exercised. Rebuild before spending "
+ "GPU-hours on it."
+ )
+ logger.info("data build ok: build_version=%s, pins verified", version)
+ return manifest
+
+
+def assert_distinct_devices() -> dict:
+ """Every rank must own a DIFFERENT physical GPU. Microseconds, at startup.
+
+ THE DUPLICATE-GPU DEFECT. torchrun starts four processes and assigns no
+ devices; nothing bound a rank to its LOCAL_RANK, so every `.cuda()` landed
+ on device 0 and NCCL died with "Duplicate GPU detected : rank 2 and rank 0
+ both on CUDA device 1b0" -> ncclInvalidUsage. The symptom appeared deep in
+ a 7B forward+backward and read like a sharding or dtype problem; the cause
+ was four processes sharing one card.
+
+ Two checks, and the second is the one that matters. `current_device() ==
+ local_rank()` catches the missing bind. But the REAL invariant is that the
+ four devices are physically distinct -- a correct-looking local index can
+ still collide under an unexpected CUDA_VISIBLE_DEVICES -- so the ranks
+ all-gather their device UUIDs and assert uniqueness. UUID rather than
+ pci_bus_id: `get_device_properties(i).pci_bus_id` returns the local index
+ (0 here), which is exactly the number that would agree while the hardware
+ collides.
+
+ Raises with the full rank -> device map, so the failure names itself
+ instead of surfacing 400 lines later as an NCCL error code.
+ """
+ import torch
+
+ from ..utils.dist import local_rank, world_size
+
+ if not torch.cuda.is_available():
+ return {"checked": False, "reason": "no CUDA device visible"}
+
+ current, lr = torch.cuda.current_device(), local_rank()
+ uuid = str(getattr(torch.cuda.get_device_properties(current), "uuid", current))
+ ws = world_size()
+
+ import torch.distributed as dist
+
+ distributed = ws > 1 and dist.is_available() and dist.is_initialized()
+
+ # THE COLLECTIVE COMES FIRST, and my own both-directions test is why. My
+ # first version raised the local mismatch BEFORE gathering: under a real
+ # collision ranks 1-3 raised immediately while rank 0 (whose
+ # current_device DID equal its local_rank) entered the all-gather alone
+ # and waited forever for peers that had already died. A guard against a
+ # hang must not be able to hang. So every rank enters the collective
+ # unconditionally, then all of them evaluate the SAME gathered map and
+ # raise identically.
+ gathered: list = [(0, lr, current, uuid)]
+ if distributed:
+ # GLOO, not the default NCCL group. NCCL cannot complete a collective
+ # on ranks that share a device -- it raises ncclInvalidUsage -- so a
+ # guard that gathers over NCCL reports the very failure it exists to
+ # name, in NCCL's words instead of ours. gloo is CPU-side and does not
+ # care about GPU binding, so the duplicate map survives to the error
+ # message. Verified: with all four ranks forced onto device 0 this
+ # prints the rank->device map; over NCCL it printed "invalid usage".
+ group = getattr(assert_distinct_devices, "_guard_group", None)
+ if group is None:
+ group = dist.new_group(backend="gloo")
+ assert_distinct_devices._guard_group = group
+ gathered = [None] * ws
+ dist.all_gather_object(gathered, (dist.get_rank(), lr, current, uuid), group=group)
+
+ device_map = {r: {"local_rank": l, "current_device": c, "uuid": u} for r, l, c, u in gathered}
+
+ mismatched = {r: v for r, v in device_map.items() if v["current_device"] != v["local_rank"]}
+ if mismatched:
+ raise RuntimeError(
+ f"rank binding is wrong on {sorted(mismatched)}: "
+ f"{mismatched}. Call align.utils.dist.init_distributed() BEFORE any "
+ "CUDA op -- it does init_process_group + set_device(local_rank) -- "
+ "and place tensors with torch.device('cuda', local_rank()), never "
+ "a bare .cuda()."
+ )
+
+ uuids = [v["uuid"] for v in device_map.values()]
+ if len(set(uuids)) != len(uuids):
+ collisions: dict = {}
+ for r, v in device_map.items():
+ collisions.setdefault(v["uuid"], []).append(r)
+ dupes = {u: rs for u, rs in collisions.items() if len(rs) > 1}
+ raise RuntimeError(
+ f"DUPLICATE GPU: {len(uuids)} ranks are sharing "
+ f"{len(set(uuids))} physical device(s). Ranks per device: {dupes}. "
+ f"Full map: {device_map}. Every rank must bind its own card before "
+ "any CUDA allocation; this is the ncclInvalidUsage that surfaces "
+ "later as a mysterious failure inside the model's forward."
+ )
+ logger.info("device binding ok: %d rank(s) on %d distinct GPU(s)", len(uuids), len(set(uuids)))
+ return {"checked": True, "world_size": len(uuids), "device_map": device_map}
+
+
+def derived_global_batch_rows(cfg) -> int:
+ """Rows per OPTIMIZER step, derived from the numbers that actually
+ produce them: grad_accum x micro_batch_size x world_size."""
+ return cfg.optimizer.grad_accum * cfg.train.micro_batch_size * cfg.train.world_size
+
+
+def assert_batch_arithmetic_consistent(cfg) -> dict:
+ """`global_batch_rows` must equal accum x micro x world, or refuse.
+
+ THE TWO-HORIZONS DEFECT (found pre-launch). configs/sft.toml carried
+ grad_accum 4 x micro_batch 3 x world 4 = 48 rows per optimizer step while
+ declaring global_batch_rows = 24. Nothing reconciled them, and the two
+ numbers feed DIFFERENT consumers: the banner's reported horizon is
+ ceil(rows / global_batch_rows), while the Trainer's LR schedule is
+ ceil(len(sampler) x epochs / accum) and never reads global_batch_rows at
+ all. An LR schedule that ends at half or a third of its reported horizon
+ is the iterations-vs-steps incident wearing a new coat -- the run finishes
+ at a learning rate nobody chose, and the banner records the intended one.
+
+ This makes the config internally consistent. It does NOT by itself make
+ the banner agree with the Trainer -- see `assert_horizon_agrees`, because
+ the length-grouped sampler batches by TOKEN budget, so its batch count is
+ not rows/global_batch under any value.
+ """
+ derived = derived_global_batch_rows(cfg)
+ declared = cfg.train.global_batch_rows
+ if declared != derived:
+ raise RuntimeError(
+ f"batch arithmetic is inconsistent: train.global_batch_rows="
+ f"{declared} but grad_accum({cfg.optimizer.grad_accum}) x "
+ f"micro_batch_size({cfg.train.micro_batch_size}) x world_size("
+ f"{cfg.train.world_size}) = {derived}. One of these is wrong and "
+ "the stage cannot tell which: they drive DIFFERENT consumers "
+ "(the banner's reported horizon vs the optimizer step the trainer "
+ "actually takes), so a mismatch means the LR schedule ends "
+ f"{max(declared, derived) / min(declared, derived):.1f}x away from "
+ "the horizon the banner reports. Set global_batch_rows to "
+ f"{derived}, or change accum/micro/world so the product is "
+ f"{declared}."
+ )
+ return {"global_batch_rows": declared, "derived": derived}
+
+
+def assert_horizon_agrees(sampler_horizon: int, cfg, tolerance: float = 0.0) -> dict:
+ """The banner's horizon and the Trainer's must be the SAME number.
+
+ The Trainer schedules on `ceil(len(sampler) x epochs / accum)`; the banner
+ reports `ceil(rows / global_batch_rows) x epochs`. Because the sampler
+ batches by token budget rather than row count, these agree only by
+ coincidence. Rather than pick one silently, this refuses on disagreement
+ and prints both -- an LR horizon is a pre-registered quantity and choosing
+ it is a human decision, not a rounding convention.
+ """
+ from .stage_smoke import _sft_rows
+
+ config_horizon = scheduler_horizon_optimizer_steps(
+ rows=_sft_rows(cfg), global_batch=cfg.train.global_batch_rows, epochs=cfg.train.epochs
+ )
+ if sampler_horizon != config_horizon:
+ spread = max(sampler_horizon, config_horizon) / max(1, min(sampler_horizon, config_horizon))
+ raise RuntimeError(
+ f"HORIZON MISMATCH: the trainer will schedule over "
+ f"{sampler_horizon} optimizer steps (len(sampler) x epochs / "
+ f"accum) but the config projects {config_horizon} "
+ f"(rows / global_batch_rows x epochs) -- {spread:.2f}x apart. The "
+ "LR schedule is a pre-registered quantity; the stage refuses to "
+ "guess which horizon was intended. Reconcile global_batch_rows "
+ "with the sampler's token budget, or set the horizon explicitly."
+ )
+ return {"horizon_optimizer_steps": sampler_horizon}
+
+
+def stamp_golden_report(path: str, repo_root: Optional[str] = None) -> str:
+ """Stamp the golden report with the hash of the eval source that PRODUCED it (audit U01-9).
+
+ Called by `scripts/eval.py --mode=golden` right after the run (also when the gate raised: the
+ failing report is evidence too). `record_golden_from_report` refuses a report whose stamp is
+ not the current eval source, so editing align/eval/ and re-recording from the old report
+ cannot re-certify the new code. Atomic rewrite; returns the stamped hash.
+ """
+ h = eval_source_hash(repo_root)
+ with open(path, "rb") as f:
+ report = json.loads(f.read())
+ report["eval_source_hash"] = h
+ tmp = path + ".partial"
+ with open(tmp, "w") as f:
+ json.dump(report, f, indent=1, default=str)
+ f.flush()
+ os.fsync(f.fileno())
+ os.replace(tmp, path)
+ return h
+
+
+def record_golden_from_report(
+ ledger: GateLedger, report_path: Optional[str] = None, cfg=None
+) -> dict:
+ """Bridge the golden-regression RUN to the gate LEDGER, by re-deciding.
+
+ THE MISSING-BRIDGE DEFECT (pre-launch): `scripts/eval.py --mode=golden`
+ writes its report and passes or raises IN PROCESS; nothing ever wrote the
+ verdict into the ledger. `require_golden_receipt` reads
+ `ledger.is_passed("golden_regression")`, so after a PASSING golden run the
+ SFT stage would still have refused on PENDING.
+
+ VERIFY FROM EVIDENCE, NOT FROM A FLAG. This does not take "the run
+ exited 0" as the receipt -- it loads the persisted report and re-runs the
+ verdict through section-10's own `assert_meets_golden`, so there is ONE
+ authority for what meeting the golden numbers means. A report that no
+ longer passes cannot leave a stale PASS in the ledger.
+
+ Three outcomes, and the middle one is the one that gets skipped:
+ * report passes -> record PASSED, with the path and sha256 in the
+ detail so the receipt names the exact bytes it certified;
+ * report FAILS -> record FAILED with the failure text. Failing is not
+ not-yet-run: a recorded failure BLOCKS every later gate, whereas
+ leaving it unrecorded would let a rerun quietly try again;
+ * report absent -> record NOTHING, leaving the gate PENDING. The run
+ has not happened; inventing a verdict from a missing file is the
+ failure this whole gate exists to prevent.
+
+ sha256 is computed inline rather than via `receipts.sha256_file` to keep
+ this module importable above the resource guards -- see the module header.
+ """
+ from ..eval.golden import GOLDEN, GOLDEN_N, assert_meets_golden
+
+ path = report_path or (cfg.paths.golden_report_path if cfg is not None else None)
+ if not path:
+ raise ValueError("record_golden_from_report needs a report path or a cfg")
+
+ if not os.path.isfile(path):
+ logger.warning(
+ "golden report %s is absent; leaving golden_regression PENDING. "
+ "Run `scripts/eval.py --mode=golden` before the stage.",
+ path,
+ )
+ return {"recorded": False, "passed": None, "path": path, "reason": "report absent"}
+
+ with open(path, "rb") as f:
+ raw = f.read()
+ digest = hashlib.sha256(raw).hexdigest()
+ report = json.loads(raw)
+
+ # U01-9: the ledger drops the golden pass when align/eval/ changes (`GateLedger._load`), but a
+ # report produced by the OLD source would otherwise be re-recorded as a fresh PASS here. The
+ # report must name the source that produced it, and it must be the source we are running.
+ stamped = report.get("eval_source_hash") if isinstance(report, dict) else None
+ current = ledger.eval_source_hash or eval_source_hash()
+ if stamped is None:
+ logger.warning(
+ "golden report %s carries no eval_source_hash (it predates producer stamping): "
+ "cannot verify it was produced by the current align/eval/ source",
+ path,
+ )
+ elif stamped != current:
+ logger.error(
+ "golden report %s was produced by eval source %s but align/eval/ is now %s; refusing to "
+ "re-certify it. Re-run `scripts/eval.py --mode=golden`.",
+ path,
+ stamped,
+ current,
+ )
+ return {
+ "recorded": False,
+ "passed": None,
+ "path": path,
+ "reason": f"golden report produced by a different eval source ({stamped} != {current})",
+ }
+
+ try:
+ assert_meets_golden(report, GOLDEN, expected_n=GOLDEN_N)
+ except (RuntimeError, AssertionError) as exc:
+ detail = json.dumps({"report": path, "sha256": digest, "failure": str(exc)[:600]})
+ ledger.record("golden_regression", passed=False, detail=detail)
+ logger.error("golden regression FAILED per %s: %s", path, exc)
+ return {
+ "recorded": True,
+ "passed": False,
+ "path": path,
+ "sha256": digest,
+ "failure": str(exc),
+ }
+
+ detail = json.dumps(
+ {
+ "report": path,
+ "sha256": digest,
+ "golden": GOLDEN,
+ "expected_n": GOLDEN_N,
+ "per_set": report.get("per_set", {}),
+ },
+ default=str,
+ )
+ ledger.record("golden_regression", passed=True, detail=detail)
+ logger.info("golden regression PASSED per %s (sha256 %s)", path, digest[:12])
+ return {"recorded": True, "passed": True, "path": path, "sha256": digest}
+
+
+def require_golden_receipt(ledger: GateLedger, cfg) -> None:
+ """The golden regression must have PASSED, not merely been attempted.
+
+ PENDING is never passed. This is the precondition that keeps a defective
+ eval stack from certifying everything downstream of it.
+ """
+ if not cfg.gates.require_golden_receipt:
+ logger.warning(
+ "gates.require_golden_receipt is False -- training will "
+ "start without a validated eval stack"
+ )
+ return
+ if not ledger.is_passed("golden_regression"):
+ raise GateOrderError(
+ "the golden-regression gate has not passed (it reads "
+ f"{ledger.status()['golden_regression']!r}). Refusing to train: "
+ "that run is what validates the eval stack -- verifier, prompt "
+ "template, extraction, inference path -- against numbers that are "
+ "already known. Without it, every accuracy this stage produces is "
+ "unfalsifiable. Run `scripts/eval.py --mode=golden` first."
+ )
+
+
+# ---------------------------------------------------------------------------
+# banner
+# ---------------------------------------------------------------------------
+
+
+def assemble_sft_banner(
+ cfg,
+ attention_backend: str,
+ optimizer_impl: str,
+ dataset_hashes: Optional[dict],
+ ceiling_results: Optional[dict],
+ probe_report: Optional[dict],
+ rss_projection: Optional[dict],
+ mde: Optional[dict],
+ scheduler_horizon_optimizer_steps: Optional[int],
+ scheduler_horizon_rows_derived: Optional[int] = None,
+) -> dict:
+ """Every mandatory banner value, resolved or explicitly PENDING/NaN.
+
+ `beta` is NaN for SFT rather than 0.0: 0.0 is a legitimate DPO beta, so a
+ zero here would be indistinguishable from a real setting.
+
+ THE HORIZON IS THE TRAINER'S, NOT THE CONFIG'S. `scheduler_horizon_
+ optimizer_steps` must be the sampler-derived number the scheduler will
+ actually run; the rows/global_batch projection rides beside it under a
+ LABELED secondary key. A banner that records a horizon the scheduler does
+ not run is the config-translation value-drift class in its purest form --
+ the record looks right and the run is something else. A mismatch is LOGGED
+ loudly here rather than raised; the hard refusal
+ (`assert_horizon_agrees`) is wired after the first SFT run so it cannot
+ become a fresh launch blocker.
+ """
+ banner = {
+ "attention_backend": attention_backend,
+ "loss_type": "sft_ce",
+ "beta": NAN,
+ "lr": cfg.optimizer.lr,
+ "scheduler_horizon_optimizer_steps": (
+ scheduler_horizon_optimizer_steps
+ if scheduler_horizon_optimizer_steps is not None
+ else PENDING
+ ),
+ "dataset_hashes": dataset_hashes,
+ "ceiling_results": ceiling_results,
+ "rss_budget": rss_projection,
+ "pinned_staging_gb": (rss_projection or {}).get("pinned_staging_gb", PENDING),
+ "probe_step_time_s": (probe_report or {}).get("mean_step_time_s", PENDING),
+ "mde": mde,
+ "optimizer_impl": optimizer_impl,
+ "grad_accum": cfg.optimizer.grad_accum,
+ "probe_p90_step_time_s": (probe_report or {}).get("p90_step_time_s", PENDING),
+ "scheduler_horizon_rows_derived": (
+ scheduler_horizon_rows_derived
+ if scheduler_horizon_rows_derived is not None
+ else PENDING
+ ),
+ }
+ if (
+ scheduler_horizon_rows_derived is not None
+ and scheduler_horizon_optimizer_steps is not None
+ and scheduler_horizon_rows_derived != scheduler_horizon_optimizer_steps
+ ):
+ logger.warning(
+ "HORIZON DIVERGENCE: the scheduler will run %s optimizer steps "
+ "(sampler-derived, AUTHORITATIVE) but rows/global_batch_rows "
+ "projects %s -- %.2fx apart. The banner records the former "
+ "because that is what runs. Reconcile global_batch_rows with the "
+ "sampler's token budget; the hard refusal lands after this run.",
+ scheduler_horizon_optimizer_steps,
+ scheduler_horizon_rows_derived,
+ max(scheduler_horizon_optimizer_steps, scheduler_horizon_rows_derived)
+ / max(1, min(scheduler_horizon_optimizer_steps, scheduler_horizon_rows_derived)),
+ )
+ # Raises rather than printing a partial banner: a run that cannot answer
+ # its own question does not begin.
+ assert_banner_complete(banner)
+ return banner
+
+
+def assert_probe_resolved(banner: dict) -> None:
+ """A stage that REQUIRES the probe refuses to launch on PENDING.
+
+ Banner completeness and banner resolution are different properties: every
+ key being present is not the same as every value having been measured.
+ """
+ pending = [
+ k
+ for k in ("probe_step_time_s", "rss_budget", "scheduler_horizon_optimizer_steps")
+ if banner.get(k) == PENDING or banner.get(k) is None
+ ]
+ if pending:
+ raise RuntimeError(
+ f"refusing to launch: {pending} still read PENDING. The 20-step "
+ "probe and the RSS projection must be MEASURED before the epoch "
+ "starts -- their whole purpose is to stop a run that cannot fit "
+ "its schedule or its host RAM."
+ )
+ step_time = banner["probe_step_time_s"]
+ if isinstance(step_time, float) and math.isnan(step_time):
+ raise RuntimeError(
+ "probe_step_time_s is NaN: the probe ran but measured nothing. "
+ "Refusing to launch on an unmeasured schedule."
+ )
+
+
+# ---------------------------------------------------------------------------
+# CLI contract + stage drivers (thin; scripts/sft.py owns the side effects)
+# ---------------------------------------------------------------------------
+
+
+def parse_args(argv: Optional[list[str]] = None):
+ """`torchrun --nproc_per_node=4 scripts/sft.py configs/sft.toml
+ --optimizer.lr=1e-5` -- first positional is the TOML, the rest are dotted
+ overrides handed to section-01's loader (struct mode on, so a typo'd key
+ raises at launch instead of training with the default it failed to
+ override).
+
+ Parsed by hand rather than with argparse: `--optimizer.lr=1e-5` is not an
+ argparse option, it is a dotted-override entry (`section.field=value`), and argparse rejects
+ every one of them as unrecognized.
+ """
+ import sys as _sys
+
+ argv = list(_sys.argv[1:] if argv is None else argv)
+ if "-h" in argv or "--help" in argv:
+ print(__doc__)
+ raise SystemExit(0)
+
+ positional = [a for a in argv if not a.startswith("-")]
+ overrides = [a.lstrip("-") for a in argv if a.startswith("-")]
+ if not positional:
+ raise SystemExit(
+ "usage: sft.py [--section.field=value ...]\n"
+ "The stage TOML is required: a stage that picks its own defaults "
+ "is a stage nobody can reproduce."
+ )
+ if len(positional) > 1:
+ raise SystemExit(
+ f"expected exactly one config TOML, got {positional}. Extra "
+ "positional arguments are silently ignored by most launchers, "
+ "which is how a run ends up using the wrong config."
+ )
+ bad = [o for o in overrides if "=" not in o]
+ if bad:
+ raise SystemExit(f"overrides must be dotted key=value pairs; got {bad}")
+ return type("Args", (), {"config": positional[0], "overrides": overrides})()
+
+
+def run_entry_gates(
+ cfg,
+ ledger: GateLedger,
+ smoke_steps: int = 10,
+ probe_steps: int = 20,
+ mem_available_gb: Optional[float] = None,
+ optimizer_steps_planned: Optional[int] = None,
+) -> dict:
+ """The M5 entry sequence, in order: 7B smoke -> 20-step probe -> RSS
+ preflight. GPU-only; imported lazily so this module stays torch-free."""
+ from .stage_smoke import run_smoke_and_probe # GPU-only helpers
+
+ require_golden_receipt(ledger, cfg)
+ assert_batch_arithmetic_consistent(cfg)
+
+ smoke = run_smoke_and_probe(cfg, smoke_steps=smoke_steps, probe_steps=probe_steps)
+ ledger.record("smoke_7b", passed=True, detail=json.dumps(smoke["smoke"], default=str))
+ ledger.record("probe_20step", passed=True, detail=json.dumps(smoke["probe"], default=str))
+
+ steps = optimizer_steps_planned or smoke["optimizer_steps_planned"]
+ schedule_check(smoke["probe"], cfg, optimizer_steps_planned=steps)
+ rss = rss_preflight(cfg, mem_available_gb=mem_available_gb)
+ ledger.record(
+ "rss_preflight",
+ passed=True,
+ detail=json.dumps({k: v for k, v in rss.items() if k != "breakdown"}),
+ )
+
+ return assemble_sft_banner(
+ cfg,
+ attention_backend=smoke["attention_backend"],
+ optimizer_impl=smoke["optimizer_impl"],
+ dataset_hashes=smoke["dataset_hashes"],
+ ceiling_results=smoke["ceiling_results"],
+ probe_report=smoke["probe"],
+ rss_projection=rss["breakdown"],
+ mde=smoke["mde"],
+ scheduler_horizon_optimizer_steps=steps,
+ scheduler_horizon_rows_derived=smoke.get("optimizer_steps_rows_derived"),
+ )
+
+
+def evaluate_and_gate(cfg, ledger: GateLedger, checkpoint: str) -> GateDecision:
+ """Run the post-training eval and apply the quality gate, recording both."""
+ from .stage_smoke import run_stage_eval # GPU-only helper
+
+ report = run_stage_eval(cfg, checkpoint)
+ ledger.record("eval", passed=True, detail=json.dumps(report.get("per_set", {}), default=str))
+ decision = quality_gate(report, cfg)
+ ledger.record("quality_gate", passed=decision.passed, detail=decision.halt_message[:400])
+ write_quality_gate_receipt(cfg, decision, checkpoint=checkpoint)
+ return decision
diff --git a/code/align/train/stage_smoke.py b/code/align/train/stage_smoke.py
new file mode 100644
index 0000000000000000000000000000000000000000..1392c7bceee886967c548b3b880b2435dd5afbe6
--- /dev/null
+++ b/code/align/train/stage_smoke.py
@@ -0,0 +1,530 @@
+"""GPU-only stage helpers: the 7B smoke, the throughput probe, the stage eval.
+
+Separated from `stage_gates.py` so that module stays importable above the
+resource guards. Everything here touches torch, the 7B weights, or the eval
+harness, so nothing here runs in the CPU suite -- the gate DECISIONS these
+feed are what the CPU tests cover.
+
+The peak-memory assertion lives here rather than in the unit tests because it
+is only meaningful at 7B on the real topology: the tensor it proves we avoid
+is `[B*T, 32768]`, which at seq 1024 x micro-batch 8 is 1.0GB in bf16 plus
+another 1.0GB of gradient. A unit-scale check cannot see that.
+"""
+
+from __future__ import annotations
+
+import logging
+import time
+from typing import Optional
+
+import torch
+
+logger = logging.getLogger(__name__)
+
+
+def _load_snapshot_state(snapshot: str) -> dict:
+ """HF-layout state dict from a snapshot dir, single-file OR sharded, cast
+ to bf16 shard-by-shard.
+
+ The pinned Mistral-7B-v0.3 snapshot is THREE shards plus
+ `model.safetensors.index.json`; `transformers.from_pretrained` used to
+ resolve that. The layout logic (and the index-vs-shards completeness
+ check) now lives in `ckpt.load_hf_safetensors`, so every caller pointed at
+ a base snapshot gets the same behaviour; this wrapper only pins the dtype.
+ """
+ from ..model import checkpoint as ckpt
+
+ return ckpt.load_hf_safetensors(snapshot, dtype=torch.bfloat16)
+
+
+def build_stage_model(
+ cfg, attn_implementation: Optional[str] = None, weights_dir: Optional[str] = None
+):
+ """The real 7B model, FSDP2-sharded per block with activation
+ checkpointing. Wrapping happens HERE (the stage), never inside the
+ Trainer -- the Trainer receives an already-wrapped model, which is what
+ keeps it identical between the SFT and DPO stages.
+
+ `weights_dir` names an HF-layout checkpoint whose weights replace the
+ pinned snapshot's (the architecture still comes from the snapshot's
+ config.json, which a full-FT checkpoint shares). Without it the model is
+ the PRETRAINED BASE -- right for training, wrong for evaluating a trained
+ checkpoint (G3 audit U01-4: the stage-11 gate scored the base)."""
+ from ..model import checkpoint as ckpt
+ from ..model.hf_config import read_config
+ from ..model.mistral import NmoMistralForCausalLM
+ from . import fsdp
+
+ # Local only -- never let a stage reach the hub and start a 15GB download
+ # onto a shared box mid-launch. `resolve_snapshot_dir` accepts the
+ # configured `snapshots/` parent and resolves the one hash dir under it.
+ snapshot = ckpt.resolve_snapshot_dir(cfg.paths.model_snapshot)
+ # The pre-port path went through transformers' `from_pretrained` (and its
+ # MistralConfig defaults); `read_config` states every architecture field
+ # explicitly and refuses a config.json that omits one.
+ model_cfg = read_config(snapshot)
+ model_cfg.attn_implementation = attn_implementation or cfg.model.attn_implementation
+ # Shards are read straight from safetensors (the snapshot is bf16 on disk;
+ # the cast is a no-op there and a safety net for an fp32 checkpoint).
+ hf_state = _load_snapshot_state(weights_dir if weights_dir is not None else snapshot)
+ model = NmoMistralForCausalLM.from_state_dict(
+ model_cfg,
+ ckpt.hf_to_nmo(hf_state),
+ attn_implementation=model_cfg.attn_implementation,
+ )
+ del hf_state
+
+ # Onto THIS rank's device before sharding. The weights arrive on CPU from
+ # safetensors, and `fully_shard` on CPU parameters shards the wrong
+ # thing; the batches are already `.cuda()`. Uses the local rank explicitly
+ # rather than a bare `.cuda()`, which is device 0 for every rank.
+ from ..utils.dist import local_rank
+
+ device = torch.device("cuda", local_rank()) if torch.cuda.is_available() else None
+ if device is not None:
+ torch.cuda.set_device(device)
+ model = model.to(device)
+
+ fsdp.apply_activation_checkpointing(model)
+ fsdp.apply_fsdp(model)
+ return model
+
+
+def assert_chunked_loss_memory(cfg, model, batch) -> dict:
+ """The chunked-loss peak-memory assertion, at 7B scale.
+
+ MEASURES THE LOSS, NOT THE STEP -- and the first 7B run is why. I had
+ wrapped the whole adapter forward+backward, so the measured peak was
+ 6103 MiB against a [1002, 32768] logit tensor of 125 MiB: a 40x "failure"
+ that was really the 7B model's own activations and FSDP all-gathers being
+ attributed to the loss. The gate asserts that CHUNKING controls the loss's
+ memory; feeding it the model's memory makes it unsatisfiable by any
+ implementation, which is the same unsatisfiable-gate trap section-05
+ already hit once with the lm_head gradient.
+
+ So the hidden states are produced OUTSIDE the measured region and
+ detached, and the lm_head weight is materialised once via `full_tensor()`
+ (it is a DTensor under FSDP2 and the fused loss needs the whole thing).
+ What remains inside the measurement is exactly the chunked loss and its
+ backward.
+
+ The full step's peak is still REPORTED, as `full_step_peak_bytes` -- it is
+ the number that tells us what a rank actually costs -- it just is not what
+ this gate asserts on.
+ """
+ from ..kernels.fused_ce import (
+ IGNORE_INDEX,
+ assert_chunking_saves,
+ assert_peak_memory_below_full_logits,
+ fused_linear_cross_entropy,
+ lm_head_grad_allowance,
+ )
+ from .sft_loss import make_sft_loss_adapter
+
+ input_ids, labels = batch
+ target = labels[:, 1:]
+
+ # 1. The real step, executed and measured for the record (this is also the
+ # end-to-end wiring check: FSDP2 hooks, checkpointing, fused path).
+ torch.cuda.reset_peak_memory_stats()
+ out = make_sft_loss_adapter(require_fused=True)(model, (input_ids, labels))
+ out.loss.backward()
+ torch.cuda.synchronize()
+ full_step_peak = int(torch.cuda.max_memory_allocated())
+ model.zero_grad(set_to_none=True)
+ loss_value = float(out.loss.detach())
+ del out
+
+ # 2. The gate: the chunked loss in isolation, over detached hidden states.
+ with torch.no_grad():
+ hidden = model(input_ids=input_ids, return_hidden_states=True)
+ h = hidden[:, :-1].detach().clone().requires_grad_(True)
+ del hidden
+ # n_rows DERIVED FROM THE TENSOR, never from the config. The measured
+ # batch is 1002 rows where seq_len x micro_batch would predict 3072 --
+ # the sampler is token-budgeted -- and a budget computed from the config
+ # would silently be 3x the shape actually being measured.
+ n_rows = int(h.reshape(-1, h.shape[-1]).shape[0])
+ w = model.lm_head.weight
+ w_full = w.full_tensor() if hasattr(w, "full_tensor") else w
+ w_full = w_full.detach().clone().requires_grad_(True)
+
+ def _loss_only():
+ loss = fused_linear_cross_entropy(h, w_full, target, ignore_index=IGNORE_INDEX)
+ loss.backward()
+ return loss
+
+ report = assert_peak_memory_below_full_logits(
+ _loss_only,
+ n_rows,
+ cfg.model.vocab_size,
+ device=h.device,
+ # DTYPE-AWARE, and section-05's gate now refuses the alternative. The
+ # fp32 default (8 bytes/element) came to 1024 MiB, which EXCEEDED the
+ # entire 775.8 MiB measured peak -- so `attributable` clamped to zero
+ # and the gate passed no matter what the loss did. Under FSDP2
+ # mixed-precision the lm_head is bf16 with fp32 gradient accumulation:
+ # 6 bytes/element, 768 MiB. That leaves a real 7.8 MiB attributable
+ # against a 125.2 MiB budget -- a measurement instead of a rubber stamp.
+ # `param_dtype` is the lm_head's own dtype under FSDP2
+ # mixed-precision (bf16); `accum_dtype` is the fp32 the fused kernel
+ # accumulates d_weight in. Their sum -- 6 bytes/element, 768 MiB -- is
+ # what the module provably holds at the instant d_weight is cast to
+ # the parameter dtype, which is the peak.
+ unavoidable_bytes=lm_head_grad_allowance(
+ cfg.model.vocab_size,
+ cfg.model.hidden_size,
+ param_dtype=torch.bfloat16,
+ accum_dtype=torch.float32,
+ ),
+ label="section-11 M5 7B smoke",
+ )
+
+ # DIFFERENTIAL gate: chunked vs unchunked at the SAME shape, so every
+ # confounder cancels and no allowance is needed. An implementation that
+ # materialised the full logit tensor would show ~0 saving. This is the
+ # check that cannot be made vacuous by a mis-specified allowance, which is
+ # the failure the previous version of this gate actually had.
+ def _make_fn(chunk_size: int):
+ def _run():
+ h.grad = None
+ w_full.grad = None
+ fused_linear_cross_entropy(
+ h, w_full, target, chunk_size=chunk_size, ignore_index=IGNORE_INDEX
+ ).backward()
+
+ return _run
+
+ report["chunking_saves"] = assert_chunking_saves(
+ _make_fn,
+ n_rows=n_rows,
+ vocab=cfg.model.vocab_size,
+ chunk_rows=128,
+ device=h.device,
+ label="section-11 M5 7B smoke (differential)",
+ )
+
+ report["full_step_peak_bytes"] = full_step_peak
+ report["step_loss"] = loss_value
+ logger.info(
+ "7B smoke: loss=%.4f full-step peak=%.0fMiB loss-only peak=%.0fMiB",
+ loss_value,
+ full_step_peak / 2**20,
+ report.get("peak_bytes", 0) / 2**20,
+ )
+ return report
+
+
+def resolve_split_dir(shard_dir, split: str):
+ """Find the directory holding `-NNNN.receipt.json`, by DISCOVERY.
+
+ THE HAND-COMPOSED-PATH DEFECT (caught at launch). I wrote
+ `ShardDataset(cfg.data.shard_dir, names=[cfg.data.sft_split])`, expecting a
+ flat `data/shards/sft-train.receipt.json`. Section-03's real layout is
+ `data/shards/sft/train/sft-train-0000.receipt.json`. Resolved by searching
+ for the receipts the store itself wrote rather than rebuilding
+ `//` from a naming convention, so a layout change moves
+ this by itself and a missing split reports the splits that ARE present.
+ """
+ from pathlib import Path
+
+ root = Path(shard_dir)
+ if not root.is_dir():
+ raise FileNotFoundError(
+ f"shard root {root} does not exist; the stage consumes shards "
+ "built by scripts/build_data.py and never builds them itself"
+ )
+
+ found: dict[str, "Path"] = {}
+ for receipt in root.rglob("*.receipt.json"):
+ prefix = receipt.name.split(".receipt.json")[0].rsplit("-", 1)[0]
+ found.setdefault(prefix, receipt.parent)
+ if split not in found:
+ raise FileNotFoundError(
+ f"no shards for split {split!r} under {root}. Splits present: "
+ f"{sorted(found)}. (The stage never composes this path from a "
+ "naming convention -- it looks for the receipts the store wrote.)"
+ )
+ return found[split]
+
+
+def open_split(cfg, split: Optional[str] = None):
+ """The ONE way this stage opens shards. Both the smoke batches and the
+ training sampler go through here, so they cannot disagree about layout."""
+ from ..data.store import ShardDataset
+
+ name = split or cfg.data.sft_split
+ return ShardDataset(resolve_split_dir(cfg.data.shard_dir, name))
+
+
+def build_training_sampler(cfg, rank: int | None = None, world_size: int | None = None):
+ """The sampler the Trainer will actually iterate -- THIS RANK's share.
+
+ Built HERE and reused by scripts/sft.py and scripts/pretrain.py so the
+ stage cannot time, project, and then train on three different batch
+ streams. The LR horizon is derived from THIS object's length, which is
+ therefore the per-rank micro-step count the Trainer really runs.
+
+ Under world_size > 1 the token-budgeted batch list is sharded by
+ `DistributedBatchSampler` (the same seam dpo_stage uses). Before this,
+ every rank iterated the full unsharded list: a 4-rank run averaged four
+ IDENTICAL gradients -- the throughput of one GPU at the cost of four --
+ and scheduled on a horizon 4x the per-rank work. Nothing failed; the loss
+ curve was that of a smaller-batch run.
+
+ `rank`/`world_size` default to the live environment and are explicit so
+ the inter-rank contract can be tested in one CPU process.
+ """
+ from ..data.loader import DistributedBatchSampler, LengthGroupedSampler
+ from ..utils.dist import rank as env_rank
+ from ..utils.dist import world_size as env_world_size
+
+ dataset = open_split(cfg)
+ lengths = [len(dataset[i].input_ids) for i in range(len(dataset))]
+ base = LengthGroupedSampler(
+ lengths, max_tokens=cfg.train.seq_len * cfg.train.micro_batch_size, seed=cfg.train.seed
+ )
+ r = env_rank() if rank is None else int(rank)
+ ws = env_world_size() if world_size is None else int(world_size)
+ if ws <= 1:
+ return base, dataset
+ if not 0 <= r < ws:
+ raise ValueError(f"rank {r} is outside world_size {ws}")
+ return DistributedBatchSampler(base, rank=r, world_size=ws), dataset
+
+
+def sampler_derived_horizon(cfg, sampler) -> int:
+ """The horizon the TRAINER will schedule on: the same arithmetic
+ `Trainer.__init__` uses, `ceil(len(sampler) x epochs / accum)`.
+
+ This is the authoritative number. The rows/global_batch figure is a
+ projection over a ROW count, but the sampler batches by TOKEN budget, so
+ the two agree only by coincidence -- and the banner must record the
+ schedule that runs, not the one that was estimated.
+ """
+ import math as _math
+
+ total_micro = len(sampler) * cfg.train.epochs
+ return max(1, _math.ceil(total_micro / cfg.optimizer.grad_accum))
+
+
+def run_smoke_and_probe(cfg, smoke_steps: int = 10, probe_steps: int = 20) -> dict:
+ """10-step 7B smoke (with the peak-memory assertion) then the 20-step
+ throughput probe. Returns everything the launch banner needs."""
+ from ..data.loader import assert_worker_count
+ from .offload_adamw import make_optimizer
+ from .probes import aggregate_micro_step_times, throughput_report
+ from .sft_loss import make_sft_loss_adapter
+ from .trainer import scheduler_horizon_optimizer_steps
+
+ assert_worker_count(cfg.data.num_workers)
+ model = build_stage_model(cfg)
+ optimizer = make_optimizer(cfg, [p for p in model.parameters() if p.requires_grad])
+ adapter = make_sft_loss_adapter(require_fused=True)
+
+ batches = _stage_batches(cfg, n=smoke_steps + probe_steps)
+ memory_report = assert_chunked_loss_memory(cfg, model, batches[0])
+
+ step_times: list[float] = []
+ for i, batch in enumerate(batches):
+ t0 = time.perf_counter()
+ out = adapter(model, batch)
+ (out.loss / cfg.optimizer.grad_accum).backward()
+ if (i + 1) % cfg.optimizer.grad_accum == 0:
+ optimizer.step()
+ optimizer.zero_grad()
+ torch.cuda.synchronize()
+ elapsed = time.perf_counter() - t0
+ if i >= smoke_steps: # the smoke steps are warmup
+ step_times.append(elapsed)
+
+ # AUTHORITATIVE: what the Trainer will actually schedule on.
+ sampler, _dataset = build_training_sampler(cfg)
+ planned = sampler_derived_horizon(cfg, sampler)
+ # SECONDARY, labeled: the rows/global_batch projection, kept beside it so
+ # a divergence is visible in the record rather than silently resolved.
+ rows_derived = scheduler_horizon_optimizer_steps(
+ rows=_sft_rows(cfg), global_batch=cfg.train.global_batch_rows, epochs=cfg.train.epochs
+ )
+ probe = throughput_report(
+ # micro -> optimizer-step durations (task #25): tokens_per_step and
+ # `planned` below are per-optimizer-step units.
+ step_times_s=aggregate_micro_step_times(step_times, cfg.optimizer.grad_accum),
+ tokens_per_step=cfg.train.seq_len
+ * cfg.train.micro_batch_size
+ * cfg.optimizer.grad_accum
+ * cfg.train.world_size,
+ optimizer_steps_planned=planned,
+ offload_transfer_s=None,
+ offload_cpu_step_s=None,
+ )
+
+ return {
+ "smoke": {"steps": smoke_steps, **memory_report},
+ "probe": probe,
+ "optimizer_steps_planned": planned,
+ "optimizer_steps_rows_derived": rows_derived,
+ "attention_backend": model.attn_implementation,
+ "optimizer_impl": getattr(optimizer, "banner_tag", cfg.optimizer.name),
+ "dataset_hashes": _manifest_hashes(cfg),
+ "ceiling_results": _ceiling_results(cfg),
+ "mde": _mde(cfg),
+ }
+
+
+def run_stage_eval(cfg, checkpoint: str) -> dict:
+ """Evaluate a checkpoint on MATH-500 + GSM8K under section-10's harness."""
+ from ..eval.harness import EvalConfig, load_eval_set, run_eval
+ from ..eval.verifier import OutcomeVerifier
+
+ verifier = OutcomeVerifier()
+ reports = {}
+ for name, path, limit in (
+ ("math500", cfg.paths.math500_path, cfg.eval.math500_n),
+ ("gsm8k", cfg.paths.gsm8k_path, cfg.eval.gsm8k_n),
+ ):
+ problems = load_eval_set(path, limit=limit)
+ report = run_eval(
+ problems, _stage_generate_fn(cfg, checkpoint), verifier, EvalConfig(), model_name=name
+ )
+ # EvalReport has no `.results`; per-set scores live in `per_set`,
+ # keyed by each problem's source (one key for a single-set call).
+ # The old attribute error was latent -- this path never ran to
+ # completion before 2026-08-18.
+ reports[name] = report.per_set[name]
+ return {
+ "per_set": reports,
+ "config": {
+ "mde": _mde(cfg),
+ "n_problems": sum(r.get("n", 0) for r in reports.values()),
+ "eval_set_hash": _manifest_hashes(cfg),
+ },
+ }
+
+
+# -- small helpers the stage script fills in from the build artifact ---------
+
+
+def _stage_batches(cfg, n: int):
+ from ..data.loader import collate_sft
+ from ..utils.dist import local_rank
+
+ dataset = open_split(cfg)
+ mb = cfg.train.micro_batch_size
+ out = []
+ for i in range(n):
+ rows = [dataset[(i * mb + j) % len(dataset)] for j in range(mb)]
+ batch = collate_sft(rows, pad_token_id=cfg.data.pad_token_id)
+ # Explicit device: a bare .cuda() is device 0 for every rank unless
+ # set_device already ran, and relying on that ordering is what the
+ # duplicate-GPU guard exists to stop us doing.
+ dev = torch.device("cuda", local_rank())
+ out.append((batch.input_ids.to(dev), batch.labels.to(dev)))
+ return out
+
+
+def _sft_rows(cfg) -> int:
+ """Corpus size from the BUILD ARTIFACT, never a literal.
+
+ The plan says "~11.8K"; the build measured 12,410 (walk- and max_len-
+ dependent). The schedule horizon must follow the corpus that actually
+ exists, or the LR decays against a row count nobody trained on.
+ """
+ import json
+
+ with open(cfg.data.manifest_path) as f:
+ counts = json.load(f).get("counts", {})
+ rows = counts.get("sft_rows")
+ if not rows:
+ raise RuntimeError(
+ f"{cfg.data.manifest_path} has no counts.sft_rows; the scheduler "
+ "horizon cannot be derived and must not be guessed."
+ )
+ return int(rows)
+
+
+def _manifest_hashes(cfg) -> dict:
+ import json
+
+ with open(cfg.data.manifest_path) as f:
+ return json.load(f).get("hashes", {})
+
+
+def _ceiling_results(cfg) -> dict:
+ """The extraction ceiling, re-run against THIS stage's eval sets.
+
+ `preflight_extraction` returns NaN with a loud warning when no source
+ exists, and a NaN here is what the banner shows and the gate refuses --
+ which is the point: an outcome signal nobody ceiling-tested is how a
+ PERFECT policy scored 0/100 for two full PPO runs.
+ """
+ from ..eval.roundtrip import preflight_extraction
+
+ return {
+ "extraction_roundtrip": preflight_extraction(
+ [cfg.paths.math500_path, cfg.paths.gsm8k_path], eos="
", label="section-11 SFT stage"
+ )
+ }
+
+
+def _mde(cfg) -> dict:
+ from ..eval.metrics import min_detectable_effect
+
+ return {
+ "math500": min_detectable_effect(cfg.eval.math500_n),
+ "gsm8k": min_detectable_effect(cfg.eval.gsm8k_n),
+ }
+
+
+def _stage_generate_fn(cfg, checkpoint: str):
+ """Bind section-09's Engine to the harness's `generate_fn(prompts, cfg,
+ seeds)` contract, over the weights IN `checkpoint`.
+
+ THE BASE-MODEL-GATE DEFECT (G3 audit U01-4). This used to build the model
+ from `cfg.paths.model_snapshot` and use `checkpoint` only to find
+ tokenizer.json, so the post-epoch quality gate (and the receipt section-12
+ reads) scored the untouched base model; and its signature
+ `(prompts, seeds=None, **kw)` could not even accept the harness's three
+ positional arguments. Generation is the golden gate's greedy path
+ (`golden_generate_fn`), so the numbers are comparable to the anchors the
+ gate compares against.
+
+ PAIRED SEEDS. The Engine owns ONE sampling generator per instance, so
+ per-problem seeds cannot be threaded through it. Under greedy decoding
+ (the eval default) the seeds are irrelevant and dropping them is exact; a
+ SAMPLED eval would silently decorrelate the arms and invalidate every
+ McNemar comparison, so it is refused instead.
+ """
+ import os
+
+ from ..eval.golden import golden_generate_fn
+ from ..model.checkpoint import resolve_snapshot_dir
+ from ..tokenizer import load_tokenizer
+
+ # Weights-only checkpoints (pre tokenizer-copy fix) have no tokenizer.json
+ # -- that crash cost stage-11's first post-epoch eval. Vocab never changes
+ # during full-FT, so the pinned snapshot's tokenizer IS the checkpoint's.
+ # (`load_tokenizer` reads tokenizer.json directly; there is no slow
+ # sentencepiece-conversion path left to fall into.)
+ tok_src = checkpoint
+ if not os.path.isfile(os.path.join(checkpoint, "tokenizer.json")):
+ tok_src = resolve_snapshot_dir(cfg.paths.model_snapshot)
+ logger.warning(
+ "checkpoint %s has no tokenizer.json; loading tokenizer from the pinned snapshot %s",
+ checkpoint,
+ tok_src,
+ )
+ tokenizer = load_tokenizer(tok_src)
+ model = build_stage_model(cfg, weights_dir=checkpoint)
+ greedy_fn = golden_generate_fn(model, tokenizer)
+
+ def generate_fn(prompts, eval_cfg, seeds):
+ if eval_cfg.temperature != 0.0:
+ raise ValueError(
+ f"stage eval is greedy-only: temperature={eval_cfg.temperature} needs the "
+ "paired per-problem seeds, which the Engine cannot thread (one generator "
+ "per engine); dropping them would invalidate every paired comparison"
+ )
+ return greedy_fn(prompts, eval_cfg, seeds)
+
+ return generate_fn
diff --git a/code/align/train/train_module.py b/code/align/train/train_module.py
new file mode 100644
index 0000000000000000000000000000000000000000..92a1bded452a1822f169cc4c52f4f37387a88395
--- /dev/null
+++ b/code/align/train/train_module.py
@@ -0,0 +1,283 @@
+"""The TrainModule: model + loss adapter + optimizer behind one seam.
+
+This is the OLMo-core separation (olmo_core/train/train_module/train_module.py)
+ported as a PATTERN, not as code: the Trainer owns the loop, the schedule, the
+checkpoint orchestration, the instruments and history.jsonl; the TrainModule
+owns everything that touches the model -- forward, backward, the gated
+optimizer step, and the model/optimizer state movement that FSDP2 makes
+non-trivial. The Trainer never calls `.backward()` or `.step()` itself and
+never inspects a DTensor; the TrainModule never opens a file, never logs a
+metric, and never decides WHEN anything happens.
+
+Why the seam matters here and not just aesthetically: stage-b GRPO (M-C/M-D)
+needs a second module -- same Trainer loop, same checkpoint orchestration,
+same tripwires-and-history machinery, different train_batch (rollout batch,
+vendored GRPO loss, rho weights). Under the old single-class Trainer that
+would have meant a second trainer or a stage flag forest; under this seam it
+is one new TrainModule subclass.
+
+Two contracts are load-bearing and inherited verbatim from the old Trainer:
+
+ * `train_batch` performs EXACTLY `(out.loss / grad_accum).backward()`. The
+ DPO decoupled-backward adapter returns a detached leaf proxy sized so
+ that this call applies the trainer's accumulation contract to gradients
+ the adapter already planted -- any cleverness here (scaling elsewhere,
+ calling backward twice, retaining graphs) silently double-scales DPO
+ gradients.
+ * The FSDP2 state movement (full gather on save, CPU-slice per-tensor load
+ on resume) keeps its exact production-proven form; both carry incident
+ history in their docstrings.
+"""
+
+from __future__ import annotations
+
+import logging
+from dataclasses import dataclass, field
+from typing import Any, Optional, Protocol
+
+import torch
+
+logger = logging.getLogger(__name__)
+
+
+@dataclass
+class LossOutput:
+ """What every loss adapter returns.
+
+ `metrics` carries stage-specific values for the log (DPO's margin,
+ logps/chosen, logps/rejected). Undefined entries must be NaN, never 0.0 --
+ a 0.0 margin reads as 'no preference learned', which is a finding, not a
+ missing measurement.
+ """
+
+ loss: torch.Tensor
+ n_tokens: int
+ metrics: dict[str, float] = field(default_factory=dict)
+
+
+class LossAdapter(Protocol):
+ """The interface the TrainModule consumes. Section-05 owns the two real
+ implementations; neither the Trainer nor the TrainModule imports either.
+
+ SFT: batch -> chunked fused CE over hidden states, so the
+ [B*T, 32768] logit tensor never materialises.
+ DPO: batch (with precomputed reference logprobs as constants) ->
+ fused DPO loss, reporting margin and both logps in `metrics`.
+ """
+
+ def __call__(self, model: Any, batch: Any) -> LossOutput: ...
+
+
+LOSS_NORMALIZATIONS = ("micro_mean", "global_token_mean")
+
+
+def reduce_window_tokens(local_tokens: int) -> int:
+ """SUM of `local_tokens` over every rank (gloo group; identity on one process).
+
+ The denominator of the global token mean: the number of target tokens in the whole accumulation
+ window of the WHOLE world. A collective -- every rank must call it at the same program point.
+ """
+ import torch.distributed as dist
+
+ if not (dist.is_available() and dist.is_initialized()):
+ return int(local_tokens)
+ from ..utils.dist import gloo_group
+
+ g = gloo_group()
+ if g is None:
+ return int(local_tokens)
+ t = torch.tensor([int(local_tokens)], dtype=torch.int64)
+ dist.all_reduce(t, op=dist.ReduceOp.SUM, group=g)
+ return int(t.item())
+
+
+def _dp_world_size() -> int:
+ import torch.distributed as dist
+
+ return dist.get_world_size() if (dist.is_available() and dist.is_initialized()) else 1
+
+
+class TrainModule:
+ """Owns the model, the loss adapter, and the optimizer.
+
+ The default module wraps the existing SFT/DPO path unchanged; stage-b
+ GRPO subclasses `train_batch`.
+ """
+
+ def __init__(self, model, loss_adapter: LossAdapter, optimizer, grad_accum: int):
+ if grad_accum < 1:
+ raise ValueError(f"grad_accum must be >= 1, got {grad_accum}")
+ self.model = model
+ self.loss_adapter = loss_adapter
+ self.optimizer = optimizer
+ self.grad_accum = int(grad_accum)
+
+ # -- the two loop-facing calls -------------------------------------------
+
+ def train_batch(self, batch, window_tokens: Optional[int] = None) -> LossOutput:
+ """Forward + backward for one micro-batch.
+
+ Scales by accumulation so the gradient matches one large batch rather
+ than the sum of `grad_accum` batches. This exact call is the contract
+ the DPO decoupled-backward proxy honors -- see the module docstring.
+
+ `window_tokens` (None = `loss_normalization="micro_mean"`, the original
+ behaviour, byte-for-byte the line above) switches to the GLOBAL TOKEN
+ MEAN (audit U03-2): the adapter's micro-batch mean is turned back into
+ the micro-batch's summed CE (`loss * n_tokens`) and divided by the
+ target-token count of the whole window across all ranks. FSDP2/DDP then
+ AVERAGE the per-rank gradients over the world, so the local term is
+ multiplied by the world size to make that average a SUM:
+ grad = (1/N) * sum over every rank and micro-batch of grad(sum CE).
+ """
+ out = self.loss_adapter(self.model, batch)
+ if window_tokens is None:
+ (out.loss / self.grad_accum).backward()
+ else:
+ if window_tokens < 1:
+ raise ValueError(f"window_tokens must be >= 1, got {window_tokens}")
+ scale = _dp_world_size() * float(out.n_tokens) / float(window_tokens)
+ (out.loss * scale).backward()
+ return out
+
+ def optim_step(self) -> bool:
+ """Gated optimizer step + zero_grad; True iff the step was applied.
+
+ The gate (non-finite grads skip the step) lives in the optimizer
+ wrapper; this module only reports what happened so the Trainer can
+ keep its rule that the schedule counts steps that ACTUALLY happened.
+ """
+ stepped = self.optimizer.step()
+ self.optimizer.zero_grad()
+ return stepped
+
+ def zero_grads(self) -> None:
+ self.optimizer.zero_grad()
+
+ def set_train(self) -> None:
+ self.model.train()
+
+ # -- optimizer telemetry the Trainer logs --------------------------------
+
+ @property
+ def last_grad_norm(self) -> float:
+ return self.optimizer.last_grad_norm
+
+ @property
+ def skipped_steps(self) -> int:
+ return getattr(self.optimizer, "skipped_steps", 0)
+
+ # -- state movement -------------------------------------------------------
+
+ def full_model_state_dict(self):
+ """An UNSHARDED, CPU state dict that safetensors can actually write.
+
+ THE DTENSOR CHECKPOINT DEFECT (found before it could fire, one gate
+ after the ledger race). Under FSDP2 `model.state_dict()` returns
+ DTensors -- global shape, local shard -- and safetensors dies on them
+ with "Attempted to access the data pointer on an invalid python
+ storage". Measured on the tiny model at 4 ranks: a [64,64] weight
+ comes back as a DTensor holding a [16,64] shard. Nothing in the CPU
+ suite can see this; a single process has no sharding.
+
+ `get_model_state_dict(full_state_dict=True, cpu_offload=True)` is the
+ canonical gather. It costs a transient full copy in host RAM per rank,
+ which is why the write is rank-0-only rather than the gather being
+ skipped.
+ """
+ import torch.distributed as dist
+
+ if not (dist.is_available() and dist.is_initialized()):
+ return self.model.state_dict()
+ from torch.distributed.checkpoint.state_dict import StateDictOptions, get_model_state_dict
+
+ return get_model_state_dict(
+ self.model, options=StateDictOptions(full_state_dict=True, cpu_offload=True)
+ )
+
+ def load_model_state_dict(self, nmo_state: dict) -> None:
+ """Load full CPU tensors into a possibly-FSDP2-sharded model.
+
+ FSDP2 params are DTensors: a plain load_state_dict copies full
+ tensors into shards and throws "mixed torch.Tensor and DTensor"
+ -- first exercised IN PRODUCTION at the stage-11 tail resume,
+ since every earlier launch resumed from nothing. And
+ set_model_state_dict(full_state_dict=True) is the wrong mirror
+ here, and so is per-tensor distribute_tensor over full tensors:
+ both put each WHOLE tensor on GPU before sharding, and the
+ varying-shape transients churn reserved memory toward
+ full-model size per rank (measured: rank 0 at 14GB against a
+ 3.4GB shard; the 4-rank resume OOM'd on a card also carrying
+ peers' NCCL IPC mappings). Every rank already read the FULL
+ state from disk, so: slice on CPU, ship ONLY the local shard.
+ """
+ if any(hasattr(p, "to_local") for p in self.model.parameters()):
+ from torch.distributed.tensor import Shard, distribute_tensor
+
+ params = dict(self.model.named_parameters())
+ missing = sorted(set(params) - set(nmo_state))
+ unexpected = sorted(set(nmo_state) - set(params))
+ if missing or unexpected:
+ raise KeyError(
+ f"resume state mismatch: missing={missing[:5]} unexpected={unexpected[:5]}"
+ )
+ with torch.no_grad():
+ for pname, p in params.items():
+ full = nmo_state[pname].to(p.dtype)
+ local = p.to_local()
+ if tuple(p.placements) == (Shard(0),):
+ ws = p.device_mesh.size()
+ rk = p.device_mesh.get_local_rank()
+ chunk = full.chunk(ws, dim=0)[rk]
+ if chunk.shape != local.shape:
+ raise RuntimeError(
+ f"{pname}: local shard {tuple(local.shape)} != "
+ f"chunk {tuple(chunk.shape)} (uneven sharding?)"
+ )
+ local.copy_(chunk.to(local.device))
+ else:
+ p.copy_(
+ distribute_tensor(full, p.device_mesh, p.placements, src_data_rank=None)
+ )
+ else:
+ self.model.load_state_dict(nmo_state, strict=True)
+
+ def optim_state_dict(self) -> dict:
+ return self.optimizer.state_dict()
+
+ def load_optim_state_dict(self, sd: dict) -> None:
+ self.optimizer.load_state_dict(sd)
+
+ def reseed_optimizer_from_params(self) -> bool:
+ """After a resume that restored weights but NO optimizer state: let an optimizer that owns an
+ fp32 master copy (OffloadAdamW) re-seed it from the restored params. True iff it did."""
+ fn = getattr(self.optimizer, "reseed_masters_from_params", None)
+ if fn is None:
+ return False
+ fn()
+ return True
+
+ # -- module-owned state that must survive a kill --------------------------
+
+ def extra_state(self) -> dict:
+ """Module-private resume state, saved under `resume.pt["extra_state"]`.
+
+ The Trainer owns weights, optimizer, schedule and stream position; it
+ cannot know about state a SUBCLASS accumulates across steps (the GRPO
+ collapse tripwire's baseline window and latch). That state used to die
+ with the process: a resume re-measured the baseline on the post-resume
+ policy, so a collapse that began before the kill was invisible for
+ ~min_steps_before_fire steps. Subclasses override this pair; the default
+ is an empty dict so the key always exists and `load_extra_state({})`
+ is a no-op, never a KeyError on an older checkpoint's neighbour.
+
+ Contract: only tensors, dicts, lists and primitives. `resume.pt` is
+ read back with `weights_only=True`, so anything else would make the
+ checkpoint unloadable -- fail at SAVE time, not at the resume you
+ needed it for.
+ """
+ return {}
+
+ def load_extra_state(self, sd: dict) -> None:
+ """Inverse of `extra_state`. Called BEFORE the scheduler/stream restore."""
+ return None
diff --git a/code/align/train/trainer.py b/code/align/train/trainer.py
new file mode 100644
index 0000000000000000000000000000000000000000..5da8a80ecba15d9ceb50656ace8ac6c7637f5b82
--- /dev/null
+++ b/code/align/train/trainer.py
@@ -0,0 +1,2405 @@
+"""The one Trainer for SFT and DPO, plus the resume machinery it depends on.
+
+The stage scripts (sections 11/12) differ only in loss adapter, dataset, and
+config; the loop, the checkpoint cadence, the schedule, the probes, and the
+tripwires are shared so a fix to any of them cannot land in one stage and miss
+the other.
+
+M-A (course-correction, 2026-08-22): the OLMo-core Trainer/TrainModule split.
+Everything that touches the model -- forward, backward, the gated optimizer
+step, FSDP2 state movement -- now lives in `align.train.train_module.TrainModule`;
+this class owns the loop, the schedule, the checkpoint ORCHESTRATION (cadence,
+idempotence, barriers, prune), the instruments and history.jsonl. `model`,
+`optimizer` and `loss_adapter` remain readable and assignable on the Trainer
+(scripts and tests use both), delegating to the module.
+
+Three properties here are load-bearing, and each of them is a documented
+predecessor failure rather than a design preference:
+
+ * THE SCHEDULE IS COUNTED IN OPTIMIZER STEPS. Under grad-accum 4, a horizon
+ counted in dataloader iterations decays the learning rate four times too
+ fast, and the run ends at a learning rate its config never asked for.
+ * THE CHECKPOINT CADENCE IS COUNTED IN MICRO-STEPS. That keeps "a kill loses
+ at most N micro-steps of work" true when someone changes accumulation. The
+ host on this box seizes without warning; a 3-hour run that checkpoints on
+ an accumulation-dependent cadence loses an unpredictable amount.
+ * RESUME IS LOSSLESS AND SEEDED. The batch stream's offset is the number of
+ batches CONSUMED, advanced before the yield -- see ResumableBatchStream.
+
+Checkpoints are bf16-weights-only, which is safe precisely because the fp32
+optimizer master is restored alongside them and is the authoritative copy.
+
+G0 control plane (2026-10-12). Everything below is a DECISION that more than
+one rank must take identically, and the failure it prevents is the same each
+time: one rank acts alone while its peers sit in a collective, so the run does
+not crash, it hangs until the NCCL watchdog fires (the 5-hour stall of
+2026-08-12 had exactly this shape). Hence one rule: a rank-local fact (a
+signal, a wall clock, a tripwire verdict, a non-finite loss) becomes an action
+only after a MAX all-reduce on the gloo group, at a program point every rank
+reaches.
+
+ * stop (SIGTERM/SIGINT, a supervisor's STOP_REQUEST file,
+ `train.max_wall_seconds`) -- agreed at each OPTIMIZER-STEP boundary, saved
+ there, never mid-accumulation;
+ * HALT tripwires and the arm-time self-test -- rank-reduced before raising;
+ * the skip cap -- computed from reduced values, so every rank counts the same;
+ * checkpoint integrity -- fsync before the atomic rename, and a completeness
+ check so a truncated directory is skipped by `latest_checkpoint`.
+"""
+
+from __future__ import annotations
+
+import json
+import logging
+import math
+import os
+import re
+import shutil
+import signal
+import struct
+import threading
+import time
+import zipfile
+from typing import Optional
+
+import torch
+
+from ..exit_codes import (
+ EXIT_NONFINITE_HALT,
+ EXIT_TRIPWIRE_HALT,
+ EXIT_WALL_BUDGET,
+ ExitCodeError,
+ write_halt_marker,
+)
+from ..utils.supervisor import (
+ DRAIN_READY_NAME,
+ STOP_MARKER_NAME,
+ STOP_REQUEST_NAME,
+ clear_run_marker,
+ read_run_marker,
+ write_run_marker,
+)
+from ..utils.dist import (
+ all_gather_objects,
+ all_reduce_max,
+ any_rank,
+ capture_rng_state,
+ restore_rng_state,
+ world_size as _dist_world_size,
+)
+from ..utils.guard import assert_paths_off_root
+from ..utils.tracking import Tracker
+from . import sharded_ckpt
+from .ckpt_format import DenseFormat
+from .offload_adamw import make_optimizer
+from .probes import (
+ GradNormTripwire,
+ LengthDriftTripwire,
+ LogpsChosenTripwire,
+ TrunkMovementProbe,
+ aggregate_micro_step_times,
+ assert_tripwires_armed,
+ throughput_report,
+)
+
+# Loss surface lives with the TrainModule now; re-exported here because the
+# loss adapters and tests import them from this module.
+from .train_module import ( # noqa: F401
+ LOSS_NORMALIZATIONS,
+ LossAdapter,
+ LossOutput,
+ TrainModule,
+ reduce_window_tokens,
+)
+
+logger = logging.getLogger(__name__)
+
+NAN = float("nan")
+
+#: Every log record must carry these. A missing key raises in the Tracker --
+#: undefined values are NaN, never absent and never 0.
+MANDATORY_METRICS = ("loss", "lr", "micro_step", "optimizer_step", "grad_norm")
+
+#: THE CANONICAL MARGIN KEY, repo-wide. This is the name section-05's DPO
+#: kernel emits, and the Trainer reads it DIRECTLY rather than through an
+#: alias. One name, one authority: the previous arrangement had the kernel
+#: emitting `rewards/margins` while the Trainer seeded and read `margin`, which
+#: disarmed the chosen-likelihood-collapse tripwire completely.
+MARGIN_KEY = "rewards/margins"
+LOGPS_CHOSEN_KEY = "logps/chosen"
+LENGTHS_CHOSEN_KEY = "lengths/chosen"
+GRAD_NORM_KEY = "grad_norm"
+
+#: Tripwire names the Trainer knows how to arm.
+TRIPWIRE_BUILDERS = ("logps_chosen", "length_drift", "grad_norm")
+
+#: Measured on this box (tmp/offload_bench.json): 2.56s CPU AdamW per 1.81B
+#: rank shard + ~0.82s PCIe transfer per rank per optimizer step. At accum 4
+#: that is 20-30% step-time overhead; at accum 1 it is 50-100%.
+MIN_GRAD_ACCUM_FULL_FT = 4
+
+
+#: Skip-cap defaults (read with getattr off `cfg.train`, because config.py is
+#: another lane's file; a config that carries the field overrides these, and
+#: `None` disables that cap).
+DEFAULT_MAX_CONSECUTIVE_SKIPPED_STEPS = 3
+DEFAULT_MAX_SKIPPED_FRACTION = 0.05
+#: The fraction cap is not evaluated before this many optimizer-step
+#: boundaries: 1 skip in the first 5 steps is 20% and says nothing.
+SKIP_FRACTION_MIN_STEPS = 100
+
+
+class HaltError(ExitCodeError):
+ """A run-ending decision that every rank raises together.
+
+ Subclasses RuntimeError (via ExitCodeError) so every pre-existing
+ `pytest.raises(RuntimeError, match=...)` on a tripwire HALT keeps matching.
+ `exit_code` is the process exit status the stage scripts map it to
+ (`align.exit_codes.run_with_exit_codes`) and that the supervisor refuses to
+ retry; `Trainer.train` also writes it to `/HALT.json`, because
+ torchrun reports every worker failure as exit 1.
+ """
+
+ exit_code = EXIT_TRIPWIRE_HALT
+
+
+class NonFiniteHaltError(HaltError):
+ """The skip cap (consecutive / fractional) fired: the run is not training."""
+
+ exit_code = EXIT_NONFINITE_HALT
+
+
+class _StopSignals:
+ """SIGTERM/SIGINT -> a flag, for the duration of one `train()` call.
+
+ THE SIGTERM-MID-SAVE DEFECT (REFERENCE_GAP_ANALYSIS gap 1; scripts/sft.py
+ documents a torchrun SIGTERM landing mid-save). Python's default SIGTERM
+ action kills the process wherever it is -- including inside a 13.5GB
+ checkpoint write or between a rank's gather and its peers'. The handler does
+ the only thing a handler may safely do (set a flag); the loop acts on it at
+ an optimizer-step boundary, after all ranks have agreed.
+
+ A SECOND SIGINT raises KeyboardInterrupt: an operator whose first Ctrl-C is
+ being honoured slowly (a save in flight) still needs a way out, and the
+ save path already deletes its `.partial` on any BaseException.
+ Handlers can only be installed from the main thread; elsewhere (a test
+ harness thread) this degrades to "no handlers", loudly.
+ """
+
+ SIGNALS = (signal.SIGTERM, signal.SIGINT)
+
+ def __init__(self) -> None:
+ self.signum: Optional[int] = None
+ self._previous: dict = {}
+ self.installed = False
+
+ def _handle(self, signum, frame) -> None: # noqa: ARG002
+ if self.signum is not None and signum == signal.SIGINT:
+ raise KeyboardInterrupt
+ if self.signum is None:
+ self.signum = int(signum)
+
+ def __enter__(self) -> "_StopSignals":
+ if threading.current_thread() is not threading.main_thread():
+ logger.warning(
+ "Trainer.train() is not on the main thread: SIGTERM/SIGINT handlers "
+ "cannot be installed, so no coordinated stop on signal"
+ )
+ return self
+ for sig in self.SIGNALS:
+ self._previous[sig] = signal.signal(sig, self._handle)
+ self.installed = True
+ return self
+
+ def __exit__(self, *exc) -> None:
+ for sig, prev in self._previous.items():
+ signal.signal(sig, signal.SIG_DFL if prev is None else prev)
+ self._previous.clear()
+ self.installed = False
+
+
+# ---------------------------------------------------------------------------
+# checkpoint durability and completeness
+# ---------------------------------------------------------------------------
+
+
+def _fsync_path(path: str) -> None:
+ """fsync a file OR a directory by path (a directory fsync is what makes a
+ rename/create inside it durable)."""
+ fd = os.open(path, os.O_RDONLY)
+ try:
+ os.fsync(fd)
+ finally:
+ os.close(fd)
+
+
+def fsync_tree(root: str) -> None:
+ """fsync every file under `root`, then every directory (leaves first).
+
+ THE ATOMIC-RENAME-IS-NOT-DURABLE DEFECT. `os.rename(staging, final)` is
+ atomic against a process crash, not against a power loss or a host seizure
+ (the box has seized before): without an fsync the rename can reach the
+ journal before the file DATA reaches disk, leaving a `micro_N` directory of
+ zero-length files that passes every `os.path.isdir` check -- a half-written
+ checkpoint that looks complete. Data first, then the directory entries, then
+ (caller) the rename, then the parent directory.
+ """
+ dirs = []
+ for dirpath, _dirnames, filenames in os.walk(root):
+ dirs.append(dirpath)
+ for f in filenames:
+ _fsync_path(os.path.join(dirpath, f))
+ for d in reversed(dirs):
+ _fsync_path(d)
+
+
+def _torch_save_synced(obj, path: str) -> None:
+ """`torch.save` to `path` and fsync it before returning."""
+ with open(path, "wb") as f:
+ torch.save(obj, f)
+ f.flush()
+ os.fsync(f.fileno())
+
+
+def _safetensors_problem(path: str) -> Optional[str]:
+ """None if `path` is a structurally complete safetensors file, else why not.
+
+ Reads ONLY the header: 8-byte little-endian length, then that many bytes of
+ JSON whose `data_offsets` say how long the tensor buffer must be. A file cut
+ anywhere short of that fails the size check, so truncation is detected
+ without reading 13GB.
+ """
+ if not os.path.isfile(path):
+ return f"missing weights file {os.path.basename(path)}"
+ size = os.path.getsize(path)
+ if size < 8:
+ return f"{os.path.basename(path)} is {size} bytes (no safetensors header)"
+ with open(path, "rb") as f:
+ (n,) = struct.unpack(" size - 8 or n > 100 * 2**20:
+ return f"{os.path.basename(path)} header claims {n} bytes of a {size}-byte file"
+ try:
+ header = json.loads(f.read(n))
+ except ValueError:
+ return f"{os.path.basename(path)} header is not valid JSON (truncated?)"
+ end = 0
+ for k, v in header.items():
+ if k == "__metadata__":
+ continue
+ end = max(end, int(v["data_offsets"][1]))
+ if size < 8 + n + end:
+ return (
+ f"{os.path.basename(path)} is truncated: {size} bytes on disk, header "
+ f"requires {8 + n + end}"
+ )
+ return None
+
+
+def _zip_problem(path: str) -> Optional[str]:
+ """None if `path` is a readable torch.save zip archive (central directory
+ intact -- it is written LAST, so any truncation destroys it)."""
+ if not os.path.isfile(path):
+ return f"missing {os.path.basename(path)}"
+ if os.path.getsize(path) == 0:
+ return f"{os.path.basename(path)} is empty"
+ try:
+ with zipfile.ZipFile(path) as z:
+ if not any(n.endswith("data.pkl") for n in z.namelist()):
+ return f"{os.path.basename(path)} has no data.pkl"
+ except (zipfile.BadZipFile, OSError) as e:
+ return f"{os.path.basename(path)} is not a readable torch archive ({e})"
+ return None
+
+
+def checkpoint_problem(path: str) -> Optional[str]:
+ """`_checkpoint_problem` made TOTAL: it returns a reason, it never raises.
+
+ THE VERDICT-THAT-CRASHES DEFECT. The completeness check parses files a dying
+ host may have cut anywhere, so a half-written manifest can be syntactically
+ valid but structurally wrong (a `per_rank_files` layout with no
+ `world_size`, a safetensors header that is JSON but not an object). Those
+ surfaced as a KeyError/AttributeError out of the check, and every caller
+ that asks "is this the newest GOOD checkpoint?" -- `latest_checkpoint`
+ (resume), the supervisor's progress rule -- died on the exact directory it
+ exists to step over, instead of skipping it and falling back. Unknown is
+ not complete: any exception is the finding.
+ """
+ try:
+ return _checkpoint_problem(path)
+ except Exception as e: # noqa: BLE001 -- see docstring
+ return f"checkpoint metadata is malformed ({type(e).__name__}: {e})"
+
+
+def _checkpoint_problem(path: str) -> Optional[str]:
+ """None iff the checkpoint directory is COMPLETE; otherwise the first reason.
+
+ "Complete" is what resume will actually need, checked cheaply (headers and
+ file presence, no 13GB reads):
+
+ * weights: `model.safetensors` -- or every shard named by
+ `model.safetensors.index.json` -- present with a header whose declared
+ tensor extent fits inside the file;
+ * `resume.pt`: a readable archive whose top-level object is a dict with
+ its loop state (`micro_step`, `stream`, `scheduler`);
+ * when its manifest says `per_rank_files`: one readable
+ `optimizer_rank{r}.pt` for EVERY rank the manifest names (the SAVED
+ world size, not the current one -- a rank-count mismatch is refused
+ later with its own message and must not be disguised as "incomplete").
+
+ The rename-after-fsync protocol means a complete directory is the norm; this
+ exists for the hosts that seize between the rename and the data reaching
+ disk, for a checkpoint copied by hand, and for a disk that filled mid-copy.
+ """
+ if not os.path.isdir(path):
+ return "not a directory"
+ if sharded_ckpt.is_sharded(path):
+ # Sharded layout (train.checkpoint_format="sharded"): the manifest names
+ # one weights file per rank. The directory's own manifest decides, not
+ # the current config, exactly as for per_rank_files below.
+ problem = sharded_ckpt.sharded_problem(path, _safetensors_problem)
+ if problem:
+ return problem
+ shards = []
+ else:
+ index = os.path.join(path, "model.safetensors.index.json")
+ if os.path.isfile(index):
+ try:
+ with open(index) as f:
+ shards = sorted(set(json.load(f)["weight_map"].values()))
+ except (ValueError, KeyError, OSError) as e:
+ return f"unreadable model.safetensors.index.json ({e})"
+ else:
+ shards = ["model.safetensors"]
+ for shard in shards:
+ problem = _safetensors_problem(os.path.join(path, shard))
+ if problem:
+ return problem
+
+ resume = os.path.join(path, "resume.pt")
+ problem = _zip_problem(resume)
+ if problem:
+ return problem
+ try:
+ # mmap: the gather-mode resume.pt carries every rank's optimizer state
+ # (tens of GB); we want the manifest, not the bytes.
+ try:
+ state = torch.load(resume, weights_only=True, map_location="cpu", mmap=True)
+ except RuntimeError:
+ state = torch.load(resume, weights_only=True, map_location="cpu")
+ except Exception as e: # noqa: BLE001 -- any failure to load IS the finding
+ return f"resume.pt does not load ({type(e).__name__}: {e})"
+ if not isinstance(state, dict) or not {"micro_step", "stream", "scheduler"} <= state.keys():
+ return "resume.pt is missing its loop state (micro_step/stream/scheduler)"
+ layout = state.get("optimizer_state")
+ if isinstance(layout, dict) and layout.get("layout") == "per_rank_files":
+ for r in range(int(layout["world_size"])):
+ problem = _zip_problem(os.path.join(path, f"optimizer_rank{r}.pt"))
+ if problem:
+ return f"per-rank optimizer state incomplete: {problem}"
+ return None
+
+
+# ---------------------------------------------------------------------------
+# the batch stream
+# ---------------------------------------------------------------------------
+
+
+class ResumableBatchStream:
+ """A seeded, resumable iterator over (epoch, batch) with an exact offset.
+
+ THE OFF-BY-ONE. `offset` is the number of batches CONSUMED, and it is
+ advanced BEFORE the yield. Advancing it after the yield looks equivalent
+ and is not: a generator does not execute past its yield until the consumer
+ asks for the next item, so a checkpoint taken while holding batch i would
+ record offset i and the resumed run would replay batch i. One duplicated
+ batch is one step of stale gradient -- nothing crashes, no metric moves,
+ and the run stops being the run its seed describes.
+
+ A sibling lane shipped exactly this bug: their trainer-level resume test
+ passed while the stream offset was off by one, and only a dedicated
+ offset test found it.
+ """
+
+ def __init__(self, sampler, epochs: int = 1):
+ self.sampler = sampler
+ self.epochs = int(epochs)
+ self.epoch = 0
+ self.offset = 0
+ self.consumed_total = 0
+
+ def __iter__(self):
+ while self.epoch < self.epochs:
+ self.sampler.set_epoch(self.epoch)
+ batches = list(self.sampler)
+ for i in range(self.offset, len(batches)):
+ self.offset = i + 1
+ self.consumed_total += 1
+ yield batches[i]
+ self.epoch += 1
+ self.offset = 0
+
+ def unconsume(self) -> None:
+ """Hand back the batch most recently yielded, because it was never trained.
+
+ The loop must pull a batch before it can decide the run is over, so a
+ horizon stop leaves the stream one batch ahead of the work actually
+ done. Without this the end-of-training checkpoint records an offset
+ (and `consumed_total`) of micro_step + 1: the resumed stream would skip
+ a batch no step ever saw, which is the off-by-one the class docstring
+ describes, arriving from the other side. Exact because a generator is
+ suspended AT its yield: `offset == i + 1` and the epoch rollover has not
+ run yet.
+ """
+ if self.offset < 1 or self.consumed_total < 1:
+ raise RuntimeError("unconsume() with nothing consumed")
+ self.offset -= 1
+ self.consumed_total -= 1
+
+ def state_dict(self) -> dict:
+ return {"epoch": self.epoch, "offset": self.offset, "consumed_total": self.consumed_total}
+
+ def load_state_dict(self, sd: dict) -> None:
+ self.epoch = int(sd["epoch"])
+ self.offset = int(sd["offset"])
+ self.consumed_total = int(sd["consumed_total"])
+
+
+# ---------------------------------------------------------------------------
+# schedule
+# ---------------------------------------------------------------------------
+
+
+def scheduler_horizon_optimizer_steps(rows: int, global_batch: int, epochs: int) -> int:
+ """`epochs * ceil(rows / global_batch)`, in OPTIMIZER steps.
+
+ The unit is the whole point. Counting this in dataloader iterations under
+ accum 4 decays the LR four times too fast, and the symptom -- a run that
+ plateaus early -- looks like a data or capacity problem, not a scheduler
+ bug.
+ """
+ if rows <= 0 or global_batch <= 0 or epochs <= 0:
+ raise ValueError(
+ f"horizon needs positive rows/global_batch/epochs, got {rows}/{global_batch}/{epochs}"
+ )
+ return epochs * math.ceil(rows / global_batch)
+
+
+class WarmupCosineSchedule:
+ """Linear warmup then cosine decay, stepped once per OPTIMIZER step."""
+
+ def __init__(
+ self, base_lr: float, total_steps: int, warmup_frac: float = 0.03, min_lr_frac: float = 0.0
+ ):
+ if total_steps <= 0:
+ raise ValueError("total_steps must be positive")
+ self.base_lr = float(base_lr)
+ self.total_steps = int(total_steps)
+ self.warmup_steps = max(1, int(round(warmup_frac * total_steps)))
+ self.min_lr_frac = float(min_lr_frac)
+ self.step_count = 0
+
+ def lr(self) -> float:
+ t = self.step_count
+ if t < self.warmup_steps:
+ return self.base_lr * (t + 1) / self.warmup_steps
+ # Clamped at the horizon: a scheduler stepped past its total must not
+ # produce a negative or oscillating LR.
+ progress = min(1.0, (t - self.warmup_steps) / max(1, self.total_steps - self.warmup_steps))
+ cosine = 0.5 * (1.0 + math.cos(math.pi * progress))
+ return self.base_lr * (self.min_lr_frac + (1.0 - self.min_lr_frac) * cosine)
+
+ def step(self) -> None:
+ self.step_count += 1
+
+ def state_dict(self) -> dict:
+ return {
+ "step_count": self.step_count,
+ "total_steps": self.total_steps,
+ "warmup_steps": self.warmup_steps,
+ "base_lr": self.base_lr,
+ }
+
+ def load_state_dict(self, sd: dict) -> None:
+ self.step_count = int(sd["step_count"])
+ self.total_steps = int(sd["total_steps"])
+ self.warmup_steps = int(sd["warmup_steps"])
+ self.base_lr = float(sd["base_lr"])
+
+
+# ---------------------------------------------------------------------------
+# the Trainer
+# ---------------------------------------------------------------------------
+
+
+class Trainer:
+ """Micro-step loop, accumulation, gated optimizer step, cadence, resume."""
+
+ def __init__(
+ self,
+ cfg,
+ model,
+ sampler,
+ loss_adapter: LossAdapter,
+ run_dir: str,
+ optimizer=None,
+ scheduler=None,
+ tracker=None,
+ stage: str = "sft",
+ train_module: Optional[TrainModule] = None,
+ weight_format=None,
+ allow_resume_mismatch: bool = False,
+ ):
+ self.cfg = cfg
+ self.sampler = sampler
+ self.stage = stage
+
+ accum = cfg.optimizer.grad_accum
+ # RULED (team-lead, pre-launch): keyed to `offload_adamw`, because a
+ # guard must be keyed to the FAILURE IT PREVENTS, not to the
+ # circumstances that usually accompany it. This rule's failure mode is
+ # offload amortization -- the measured 2.56s CPU optimizer step and
+ # 0.82s PCIe transfer that only that optimizer pays. Under adamw8bit
+ # the state lives on-GPU and that failure mode is ABSENT, so the guard
+ # is inapplicable rather than weakened. Firing it there would have
+ # forced two pre-registration edits (global batch size and a halved
+ # horizon) to satisfy a constraint with nothing behind it.
+ if (
+ cfg.train.full_ft
+ and cfg.optimizer.name == "offload_adamw"
+ and accum < MIN_GRAD_ACCUM_FULL_FT
+ ):
+ raise ValueError(
+ f"grad_accum={accum} is below {MIN_GRAD_ACCUM_FULL_FT}, which "
+ "full-FT with offloaded AdamW requires. Measured on this box: "
+ "the CPU optimizer step is 2.56s per 1.81B-param rank shard "
+ "and PCIe transfer is ~0.82s per rank per optimizer step. "
+ "Amortised over 4 accumulation micro-steps that is 20-30% "
+ "step-time overhead; at accum 1 it is 50-100% and the run "
+ "spends more wall-clock moving state than computing. Raise "
+ "grad_accum, or set train.full_ft=false for a small model. "
+ "If host RAM forced the adamw8bit fallback, set "
+ "optimizer.name=adamw8bit -- its state is on-GPU and this "
+ "rule does not apply to it."
+ )
+ self.accum = accum
+ #: U01-5: resume refuses a checkpoint whose seed / grad_accum / data fingerprint differ
+ #: from this run's unless the operator says so (kwarg, or `train.allow_resume_mismatch`).
+ self.allow_resume_mismatch = bool(
+ allow_resume_mismatch or getattr(cfg.train, "allow_resume_mismatch", False)
+ )
+ #: U01-1: micro-batches accumulated since the last optimizer boundary, and what became of
+ #: a partial tail window ({"micros", "action"}), None when the run ended on a boundary.
+ self._pending_micros = 0
+ self.tail_window: Optional[dict] = None
+
+ # THE MID-ACCUMULATION SAVE DEFECT (align port review, 2026-10-12). A
+ # checkpoint holds weights, optimizer state, schedule and stream offset --
+ # NOT the gradients accumulated so far in the current window. A cadence
+ # that does not land on an optimizer-step boundary therefore saves a
+ # half-filled window: the resumed run restarts the counter at the saved
+ # micro_step, applies its first step after only (accum - k) micro-batches
+ # (each already scaled by 1/accum), and silently trains a different run.
+ # Measured on the tiny harness (accum 2, save_every 3): max weight
+ # divergence 3e-3 at lr 1e-3 against the uninterrupted run, no error, no
+ # metric out of line. The RL stage config already refuses this (its saves
+ # are weight syncs); the shared Trainer is where SFT/DPO/pretrain need it.
+ save_every = cfg.train.save_every_micro_steps
+ if save_every % accum != 0:
+ raise ValueError(
+ f"train.save_every_micro_steps={save_every} is not a multiple of "
+ f"optimizer.grad_accum={accum}: a checkpoint taken mid-accumulation "
+ "drops the window's accumulated gradients, so a resume from it is "
+ "NOT lossless. Pick a cadence that lands on an optimizer-step "
+ f"boundary (e.g. {max(1, save_every // accum) * accum})."
+ )
+
+ assert_paths_off_root([run_dir])
+ self.run_dir = run_dir
+ self.ckpt_root = os.path.join(run_dir, "checkpoints")
+ os.makedirs(self.ckpt_root, exist_ok=True)
+ self._discard_partials()
+
+ self.stream = ResumableBatchStream(sampler, epochs=cfg.train.epochs)
+ # The module is the ONLY holder of model/loss/optimizer; the Trainer's
+ # `model`/`optimizer`/`loss_adapter` attributes below are delegating
+ # properties, kept assignable because scripts free the optimizer
+ # before eval (`trainer.optimizer = None`) and tests swap adapters.
+ self.train_module = train_module or TrainModule(
+ model=model,
+ loss_adapter=loss_adapter,
+ optimizer=optimizer
+ or make_optimizer(cfg, [p for p in model.parameters() if p.requires_grad]),
+ grad_accum=accum,
+ )
+
+ # U03-2: "micro_mean" (default) is the original loop, untouched. "global_token_mean" buffers
+ # each accumulation window, all-reduces its target-token count and scales every micro-batch
+ # by n_micro / N_window. Refuse, at construction, every combination that cannot honour it.
+ self.loss_normalization = getattr(cfg.train, "loss_normalization", "micro_mean")
+ if self.loss_normalization not in LOSS_NORMALIZATIONS:
+ raise ValueError(
+ f"train.loss_normalization={self.loss_normalization!r}; expected one of {LOSS_NORMALIZATIONS}"
+ )
+ if self.loss_normalization == "global_token_mean":
+ if type(self.train_module).train_batch is not TrainModule.train_batch:
+ raise ValueError(
+ f"train.loss_normalization='global_token_mean' needs the stock TrainModule.train_batch; "
+ f"{type(self.train_module).__name__} overrides it (RL/PPO/MoE modules own their loss scaling)"
+ )
+ if not callable(getattr(self.train_module.loss_adapter, "count_tokens", None)):
+ raise ValueError(
+ "train.loss_normalization='global_token_mean' needs a loss adapter exposing "
+ "`count_tokens(batch)` (make_sft_loss_adapter provides it)"
+ )
+ self._window_tokens: Optional[int] = None
+
+ # Which converter writes/reads the weight file (align/train/ckpt_format.py).
+ # Explicit argument > the module's own (an MoE module knows its family)
+ # > dense from cfg.model, the behaviour every pre-MoE caller has.
+ self.weight_format = (
+ weight_format
+ or getattr(self.train_module, "weight_format", None)
+ or DenseFormat(cfg.model)
+ )
+
+ self._validate_checkpoint_format()
+
+ total_micro = len(sampler) * cfg.train.epochs
+ horizon = max(1, math.ceil(total_micro / self.accum))
+ self.scheduler = scheduler or WarmupCosineSchedule(
+ base_lr=cfg.optimizer.lr, total_steps=horizon, warmup_frac=cfg.train.warmup_frac
+ )
+
+ self.tracker = tracker or Tracker(
+ run_dir, stage, mandatory_keys=MANDATORY_METRICS, run_name=cfg.run_name
+ )
+ # Remember what the probe was built on: resume rebuilds it, and it must be
+ # rebuilt on the SAME view (a PPO run passes an adapter-only view because
+ # the frozen base can never move), not on the raw model.
+ self._probe_target = model
+ self.trunk_probe = TrunkMovementProbe(model)
+ self.trunk_report: dict = {}
+ tw = cfg.tripwires
+ # Only ARMED tripwires are constructed. An unarmed stage does not get
+ # a guard it cannot feed -- SFT has no preference margin, and arming
+ # the collapse tripwire there would either halt every run or (the old
+ # behaviour) quietly feed it NaN forever.
+ unknown = set(tw.armed) - set(TRIPWIRE_BUILDERS)
+ if unknown:
+ raise ValueError(
+ f"tripwires.armed names {sorted(unknown)}, which the Trainer "
+ f"cannot build; known: {list(TRIPWIRE_BUILDERS)}"
+ )
+ self.tripwires: dict = {}
+ if "logps_chosen" in tw.armed:
+ self.tripwires["logps_chosen"] = LogpsChosenTripwire(window=tw.logps_window)
+ if "grad_norm" in tw.armed:
+ self.tripwires["grad_norm"] = GradNormTripwire()
+ if "length_drift" in tw.armed:
+ self.tripwires["length_drift"] = LengthDriftTripwire(
+ baseline=tw.chosen_longer_baseline,
+ band=tw.length_band,
+ min_pairs=tw.length_min_pairs,
+ )
+ self.tripwire_alarms: list[dict] = []
+ self._tripwire_self_test_done = False
+
+ self.micro_step = 0
+ self.optimizer_step = 0
+ self._last_applied_lr: Optional[float] = None
+ #: Applied optimizer steps SINCE the trunk snapshot was taken. Not the
+ #: same as optimizer_step: after a resume the total is already nonzero
+ #: while the fresh snapshot has seen no movement yet, and probing on
+ #: the total would fire on every resumed run.
+ self._applied_since_snapshot = 0
+ self.checkpoints_written: list[int] = []
+ #: R5 (2026-08-23 audit): checkpoints pinned against pruning. Only the
+ #: final two checkpoints of each destroyed phase-1 run survived
+ #: (keep-2), so nothing from before the instability existed to rewind
+ #: to. The FIRST fire of any tripwire pins the NEXT save.
+ self.pinned_checkpoints: set[int] = set()
+ self._pin_next_save = False
+ self.step_times: list[float] = []
+ self._probe_done = False
+
+ # -- G0 control plane ------------------------------------------------
+ tr = cfg.train
+ self.max_wall_seconds = getattr(tr, "max_wall_seconds", None)
+ if self.max_wall_seconds is not None and not float(self.max_wall_seconds) > 0:
+ raise ValueError(
+ f"train.max_wall_seconds={self.max_wall_seconds!r} must be > 0 (or None to disable)"
+ )
+ self.max_consecutive_skipped = getattr(
+ tr, "max_consecutive_skipped_steps", DEFAULT_MAX_CONSECUTIVE_SKIPPED_STEPS
+ )
+ self.max_skipped_fraction = getattr(tr, "max_skipped_fraction", DEFAULT_MAX_SKIPPED_FRACTION)
+ if self.max_consecutive_skipped is not None and int(self.max_consecutive_skipped) < 0:
+ raise ValueError(
+ f"train.max_consecutive_skipped_steps={self.max_consecutive_skipped!r} must be >= 0"
+ )
+ if self.max_skipped_fraction is not None and not 0.0 <= float(self.max_skipped_fraction) <= 1.0:
+ raise ValueError(
+ f"train.max_skipped_fraction={self.max_skipped_fraction!r} must be in [0, 1]"
+ )
+ self._stop_signals = _StopSignals()
+ self.stop_reason: Optional[str] = None
+ self._train_t0: Optional[float] = None
+ #: On-device OR of "this micro-batch's loss was non-finite" since the
+ #: last optimizer-step boundary; read once per boundary (one sync).
+ self._nonfinite_loss_acc: Optional[torch.Tensor] = None
+ #: Replicated on every rank (updated from reduced values only) and
+ #: persisted in resume.pt, so a resume does not forgive a bad run.
+ self.skip_stats = {"consecutive": 0, "bad_total": 0, "boundaries": 0}
+
+ # -- delegation to the TrainModule ---------------------------------------
+
+ @property
+ def model(self):
+ return self.train_module.model
+
+ @model.setter
+ def model(self, value) -> None:
+ self.train_module.model = value
+ if value is None:
+ # U01-11: clearing the model is the post-train handoff; the probe's own references
+ # (`_probe_target`, `trunk_probe._model`) would otherwise keep the replica alive.
+ self._drop_probe_refs()
+
+ def _drop_probe_refs(self) -> None:
+ self._probe_target = None
+ probe = getattr(self, "trunk_probe", None)
+ if probe is not None:
+ probe.release()
+
+ def release(self) -> None:
+ """Free everything the Trainer holds on the training replica (model, optimizer, probe
+ snapshots). Call before building the post-train eval model; the Trainer cannot resume or
+ probe afterwards."""
+ self.model = None
+ self.optimizer = None
+ self._drop_probe_refs()
+
+ @property
+ def optimizer(self):
+ return self.train_module.optimizer
+
+ @optimizer.setter
+ def optimizer(self, value) -> None:
+ self.train_module.optimizer = value
+
+ @property
+ def loss_adapter(self) -> LossAdapter:
+ return self.train_module.loss_adapter
+
+ @loss_adapter.setter
+ def loss_adapter(self, value: LossAdapter) -> None:
+ self.train_module.loss_adapter = value
+
+ # -- the loop -----------------------------------------------------------
+
+ def train(self) -> dict:
+ self.stop_reason = None
+ with self._stop_signals:
+ self._train_t0 = time.monotonic()
+ self._train_wall_t0 = time.time()
+ self._announce_drain(True)
+ try:
+ return self._train_loop()
+ except HaltError as e:
+ # The verdict must survive the launcher: torchrun exits 1 for
+ # any worker failure, so the supervisor reads this file instead
+ # (align.exit_codes). Identical on every rank; rank 0 writes.
+ self._write_halt_marker(e.exit_code, str(e))
+ raise
+ finally:
+ self._announce_drain(False)
+
+ def _announce_drain(self, on: bool) -> None:
+ """Rank 0 tells a supervisor, through `DRAIN_READY.json`, that this
+ process polls `STOP_REQUEST` at every boundary -- so a stop should be
+ REQUESTED and waited for rather than signalled and killed (G3 audit
+ U01-1: a signal stop needs a boundary plus a save, longer than the
+ launcher's SIGTERM->SIGKILL window). Present only while `train()` runs:
+ before it there is no boundary to wait for, after it the post-train
+ eval is not interruptible this way."""
+ from ..utils.dist import is_rank0
+
+ if not is_rank0():
+ return
+ if on:
+ write_run_marker(self.run_dir, DRAIN_READY_NAME, pid=os.getpid())
+ else:
+ clear_run_marker(self.run_dir, DRAIN_READY_NAME)
+
+ def _stop_requested_by_file(self) -> bool:
+ """True iff a supervisor's `STOP_REQUEST` written during THIS train()
+ call exists. Read by rank 0 only (a shared run dir); the boundary
+ all-reduce spreads it, so a rank with a stale view cannot stop alone.
+ A request older than this call is a leftover and is ignored."""
+ from ..utils.dist import is_rank0
+
+ if not is_rank0():
+ return False
+ since = getattr(self, "_train_wall_t0", 0.0)
+ return read_run_marker(self.run_dir, STOP_REQUEST_NAME, since) is not None
+
+ def _write_stop_marker(self) -> None:
+ """Leave `STOP.json` (code 128+signum) so the supervisor can tell an
+ operator's stop from a crash: torchrun reports both as exit 1 (G3
+ audit U01-2). Written when the stop is AGREED, before the save, so a
+ launcher that SIGKILLs the save still leaves the verdict."""
+ from ..utils.dist import is_rank0
+
+ if is_rank0():
+ code = 128 + int(self._stop_signals.signum or signal.SIGTERM)
+ write_run_marker(
+ self.run_dir, STOP_MARKER_NAME, code=code, reason="coordinated stop (signal)"
+ )
+
+ def _write_halt_marker(self, code: int, reason: str) -> None:
+ from ..utils.dist import is_rank0
+
+ if is_rank0():
+ write_halt_marker(self.run_dir, code, reason[:2000])
+
+ @property
+ def stop_exit_code(self) -> int:
+ """The process exit status the stage script should return after `train()`.
+
+ 0 for a run that ended on its own terms (data exhausted, horizon, step
+ cap). A COORDINATED STOP is not a finished stage: continuing into the
+ post-train eval/gate of a half-trained model (what the scripts did)
+ would pass or fail it on weights nobody meant to evaluate. A wall-clock
+ stop is `EXIT_WALL_BUDGET` (the budget is spent; the supervisor must not
+ relaunch it); a signal stop is the shell convention 128+signum.
+ """
+ if self.stop_reason == "wall_clock":
+ return EXIT_WALL_BUDGET
+ if self.stop_reason == "signal":
+ return 128 + int(self._stop_signals.signum or signal.SIGTERM)
+ return 0
+
+ def _train_loop(self) -> dict:
+ cfg = self.cfg
+ limit = cfg.train.max_micro_steps
+ self.train_module.set_train()
+
+ stop_reason = "data_exhausted"
+ global_mean = self.loss_normalization == "global_token_mean"
+ it = iter(self.stream)
+ lookahead: list = [] # global_token_mean only: the rest of the current window, already pulled
+ while True:
+ if lookahead:
+ batch = lookahead.pop(0)
+ else:
+ try:
+ batch = next(it)
+ except StopIteration:
+ break
+ # THE ZOMBIE-RUN GUARD. Past the scheduler's horizon the cosine
+ # clamps, and with min_lr_frac=0 it clamps to EXACTLY 0 -- the
+ # loop then runs forward, backward, step, log and checkpoint on
+ # frozen weights, with every metric looking normal. DPO Arm A did
+ # that for 287 optimizer steps (~24 GPU-hours) because nothing
+ # stopped it. There is no learning left past the horizon by
+ # construction, so the run ends and says why.
+ if self.optimizer_step >= self.scheduler.total_steps:
+ stop_reason = "horizon"
+ logger.info(
+ "stopping at the scheduler horizon: %d optimizer steps "
+ "completed of %d planned. Past this point the cosine "
+ "schedule clamps the learning rate to %.3g and further "
+ "steps would not change the weights.",
+ self.optimizer_step,
+ self.scheduler.total_steps,
+ self.scheduler.lr(),
+ )
+ # The batch just pulled is not going to be trained on: give it
+ # back so the end-of-training checkpoint's stream offset is the
+ # number of batches TRAINED, not trained + 1.
+ self.stream.unconsume()
+ break
+
+ t0 = time.perf_counter()
+ if global_mean and self.micro_step % self.accum == 0:
+ # Window start: pull the rest of the window, count its target tokens, reduce over
+ # ranks. Saves/stops only happen on window boundaries, so the stream being up to
+ # accum-1 batches ahead mid-window is never observable in a checkpoint.
+ window = [batch]
+ while len(window) < self.accum:
+ try:
+ window.append(next(it))
+ except StopIteration:
+ break
+ lookahead = window[1:]
+ count = self.train_module.loss_adapter.count_tokens
+ self._window_tokens = reduce_window_tokens(sum(int(count(b)) for b in window))
+ self.micro_step += 1
+
+ if global_mean:
+ out = self.train_module.train_batch(batch, window_tokens=self._window_tokens)
+ else:
+ out = self.train_module.train_batch(batch)
+ self._note_loss(out.loss)
+ self._pending_micros += 1
+
+ stepped = False
+ stop: Optional[str] = None
+ if self.micro_step % self.accum == 0:
+ stepped, stop = self._optimizer_boundary()
+ # U01-8: the armed grad_norm HALT wire must inspect EVERY optimizer step. `_log`
+ # feeds it only on logged micros, so with log_every=10 / grad_accum=2 four steps in
+ # five were never seen. Feed the boundaries `_log` will not cover (a logged boundary
+ # is fed there, from the same value: one observation per step, never two).
+ if (
+ stepped
+ and "grad_norm" in self.tripwires
+ and self.micro_step % cfg.train.log_every_micro_steps != 0
+ ):
+ self._check_tripwires(
+ {GRAD_NORM_KEY: self.train_module.last_grad_norm}, names=("grad_norm",)
+ )
+ self.step_times.append(time.perf_counter() - t0)
+
+ if self.micro_step % cfg.train.log_every_micro_steps == 0:
+ self._log(batch, out, stepped)
+ if self.micro_step % cfg.train.save_every_micro_steps == 0:
+ self.save_checkpoint()
+ if stop is not None:
+ # Before the `limit` test: a stop request must win, because the
+ # capped-run branch below skips the final save and a SIGTERM
+ # that discarded the window's work is the failure this prevents.
+ stop_reason = stop
+ if stop == "signal":
+ self._write_stop_marker()
+ logger.warning(
+ "coordinated stop (%s) at optimizer step %d (micro %d)",
+ stop,
+ self.optimizer_step,
+ self.micro_step,
+ )
+ break
+ if limit is not None and self.micro_step >= limit:
+ stop_reason = "max_micro_steps"
+ break
+
+ stop_reason = self._finish_partial_window(stop_reason)
+
+ # A COORDINATED STOP always saves: the boundary is the one point where
+ # the checkpoint is lossless (no half-filled accumulation window), and
+ # the cadence save may be up to save_every micro-steps away.
+ if stop_reason in ("signal", "wall_clock"):
+ self.save_checkpoint()
+
+ # END-OF-TRAINING SAVE. The cadence saves at multiples of save_every;
+ # an epoch whose total is not divisible leaves the tail only in
+ # process memory. Stage-11's first full epoch ended at micro 2250
+ # with the last save at 2000, then the post-epoch eval crashed -- the
+ # final 125 optimizer steps existed nowhere on disk. Only for
+ # uncapped runs: a capped probe must never spend minutes on a 14.5GB
+ # gather or leave a probe artifact that keep-N pruning could count
+ # against real checkpoints.
+ elif limit is None and self.micro_step % cfg.train.save_every_micro_steps != 0:
+ self.save_checkpoint()
+
+ self.stop_reason = stop_reason
+ if stop_reason == "wall_clock":
+ self._write_halt_marker(EXIT_WALL_BUDGET, "train.max_wall_seconds reached; checkpointed")
+ summary = self.summary()
+ summary["stop_reason"] = stop_reason
+ logger.info(
+ "training finished (%s): %d micro-steps, %d optimizer steps",
+ stop_reason,
+ self.micro_step,
+ self.optimizer_step,
+ )
+ return summary
+
+ # -- G0 control plane -----------------------------------------------------
+
+ def _finish_partial_window(self, stop_reason: str) -> str:
+ """U01-1: decide what happens to a PARTIAL accumulation window when the loop ends.
+
+ THE DROPPED-TAIL DEFECT (Codex audit U01-1). `train.epochs * len(sampler)` micro-batches
+ that are not a multiple of `grad_accum` left the last `< accum` micro-batches accumulated
+ but never stepped: their .grad stayed on the params (a later forward/eval, or an optimizer
+ reused after the run, would see it), the scheduler horizon (ceil) planned a step that was
+ never taken, and nothing recorded the loss.
+
+ End of DATA (`data_exhausted`): the tail is a real, if short, window -- step it, with the
+ gradient rescaled to a mean over the `k` micro-batches it holds (each was scaled 1/accum),
+ so the horizon (ceil) is consistent with the steps taken. Under `global_token_mean` the
+ window is already normalised by its own token count (see `train_batch`), so no rescale.
+ A module that overrides `train_batch` owns its loss scaling, so the correct factor is
+ unknown: its tail is DROPPED, loudly, rather than stepped with a wrong scale. Any other
+ ending (`max_micro_steps` cap) drops the tail the same way. Dropping always zeroes the
+ grads and records `tail_window` in the summary.
+ """
+ k = int(self._pending_micros)
+ if k == 0:
+ return stop_reason
+ tm = self.train_module
+ if stop_reason == "data_exhausted":
+ stock = type(tm).train_batch is TrainModule.train_batch
+ if stock:
+ if self.loss_normalization == "micro_mean" and k < self.accum:
+ factor = float(self.accum) / float(k)
+ with torch.no_grad():
+ for prm in self.model.parameters():
+ if prm.grad is not None:
+ prm.grad.mul_(factor)
+ logger.warning(
+ "data exhausted mid-window: stepping the %d-micro-batch tail (grad_accum=%d)",
+ k,
+ self.accum,
+ )
+ _stepped, stop = self._optimizer_boundary()
+ self.tail_window = {"micros": k, "action": "stepped"}
+ if stop == "signal":
+ self._write_stop_marker()
+ if stop is not None:
+ return stop
+ return stop_reason
+ tm.zero_grads()
+ self._pending_micros = 0
+ self._nonfinite_loss_acc = None
+ self.tail_window = {"micros": k, "action": "dropped"}
+ logger.warning(
+ "run ended (%s) with a partial accumulation window of %d micro-batch(es); its gradients "
+ "were DROPPED and zeroed (recorded in the summary as tail_window)",
+ stop_reason,
+ k,
+ )
+ return stop_reason
+
+ def _optimizer_boundary(self) -> tuple[bool, Optional[str]]:
+ """The optimizer-step boundary: apply the LR, step, advance the schedule, agree on stop/skip.
+
+ Returns (stepped, stop_reason). Shared by the in-loop boundary and the end-of-data tail
+ window (U01-1) so both take exactly the same path.
+ """
+ self._pending_micros = 0
+ # THE UNAPPLIED-SCHEDULE DEFECT (found in the align port, present
+ # in the legacy trainer too). The scheduler was stepped and its LR
+ # was LOGGED, but nothing ever wrote it into the optimizer: the
+ # optimizer was built once with lr=cfg.optimizer.lr, so every
+ # SFT/DPO/GRPO run trained at a CONSTANT lr (no warmup, no decay)
+ # while history.jsonl reported a decaying one. Measured on the tiny
+ # harness: scheduler.lr() 1e-3 -> 7.5e-4 -> 2.5e-4 while
+ # optimizer.param_groups[0]["lr"] stayed 1e-3. The logged value and
+ # the applied value must be the same number, so apply it here,
+ # immediately before the step it governs. Idempotent with modules
+ # that set their own lr (PPO).
+ self._apply_scheduled_lr()
+ skipped_before = self.train_module.skipped_steps
+ stepped = self.train_module.optim_step()
+ # A step that was not applied is only a SKIP if the optimizer's
+ # grad gate said so (its `skipped_steps` counter moved). A
+ # module may decline to step by design -- PPO's critic-only
+ # warmup iterations return False every time -- and counting
+ # those would halt a healthy run on its fourth warmup window.
+ gate_skipped = self.train_module.skipped_steps > skipped_before
+ if stepped:
+ # The schedule counts steps that ACTUALLY happened: a run
+ # whose grads went non-finite for a stretch must not decay
+ # its LR on schedule while training nothing.
+ self.scheduler.step()
+ self.optimizer_step += 1
+ self._applied_since_snapshot += 1
+ # ONE reduced decision per optimizer step: stop request, skip
+ # health. Raises (identically on every rank) past the skip cap.
+ stop = self._boundary_control(gate_skipped)
+ return stepped, stop
+
+ def _note_loss(self, loss) -> None:
+ """Remember, on-device, whether any micro-batch loss in this window was
+ non-finite.
+
+ THE LOSS-ONLY-NAN BLIND SPOT. The skip gate looks at GRADIENTS. A
+ non-finite loss whose gradient is finite (a decoupled-backward proxy, a
+ NaN in a logged-only term) steps happily, and a loss that is NaN every
+ micro-step looks like a healthy run to the gate. The flag is OR-ed on
+ the loss's own device and read once per optimizer step, so it costs no
+ extra host sync per micro-step.
+ """
+ bad = ~torch.isfinite(loss.detach()).all()
+ acc = self._nonfinite_loss_acc
+ self._nonfinite_loss_acc = bad if acc is None else (acc | bad)
+
+ def _wall_limit_hit(self) -> bool:
+ if self.max_wall_seconds is None or self._train_t0 is None:
+ return False
+ return (time.monotonic() - self._train_t0) >= float(self.max_wall_seconds)
+
+ def _boundary_control(self, gate_skipped: bool) -> Optional[str]:
+ """The optimizer-step-boundary agreement. Returns the stop reason or None.
+
+ ONE MAX all-reduce carries four rank-local facts: [signalled,
+ wall-clock expired, a loss was non-finite, the grad gate skipped the step].
+ Every rank therefore sees the same four numbers and takes the same
+ branch -- the only way "any rank signalled => all stop" can be true
+ without a rank stopping alone while a peer waits in the next
+ micro-step's FSDP all-gather. Wall clocks are per-rank, so they are
+ reduced too: ranks disagree about elapsed time by the skew of their
+ launch.
+
+ Called only on a boundary, so a stop never lands mid-accumulation (a
+ checkpoint holds no half-filled gradient window; see __init__).
+ """
+ nonfinite = False
+ if self._nonfinite_loss_acc is not None:
+ nonfinite = bool(self._nonfinite_loss_acc)
+ self._nonfinite_loss_acc = None
+ sig, wall, nf, gate_skipped = all_reduce_max(
+ [
+ int(self._stop_signals.signum is not None or self._stop_requested_by_file()),
+ int(self._wall_limit_hit()),
+ int(nonfinite),
+ int(gate_skipped),
+ ]
+ )
+ self._record_step_health(bool(nf), bool(gate_skipped))
+ if sig:
+ return "signal"
+ if wall:
+ return "wall_clock"
+ return None
+
+ def _record_step_health(self, nonfinite_loss: bool, gate_skipped: bool) -> None:
+ """Count bad optimizer steps; HALT past the consecutive or fractional cap.
+
+ THE SILENT-SKIP RUN. The grad gate skips a non-finite step and trains
+ on; if the cause is persistent (an overflowing loss scale, a poisoned
+ shard, an LR that has already blown the weights) it skips FOREVER:
+ every step "succeeds", the schedule stalls (it counts applied steps),
+ and the run burns GPU-hours learning nothing. `skipped_steps` was
+ logged but nothing ever read it. A step is BAD if the grad gate skipped it OR
+ any of its micro-batch losses was non-finite. The inputs are the
+ already-reduced flags, so every rank holds identical counters and
+ raises the identical error with no further collective.
+ """
+ st = self.skip_stats
+ st["boundaries"] += 1
+ if nonfinite_loss or gate_skipped:
+ st["consecutive"] += 1
+ st["bad_total"] += 1
+ else:
+ st["consecutive"] = 0
+ cause = (
+ "non-finite loss" + (" and skipped step" if gate_skipped else "")
+ if nonfinite_loss
+ else "skipped step (non-finite gradient)"
+ )
+ cap = self.max_consecutive_skipped
+ if cap is not None and st["consecutive"] > int(cap):
+ self._halt_skip_cap(
+ f"{st['consecutive']} consecutive bad optimizer steps exceed "
+ f"train.max_consecutive_skipped_steps={cap} (last cause: {cause})",
+ cause,
+ )
+ frac = self.max_skipped_fraction
+ if frac is not None and st["boundaries"] >= SKIP_FRACTION_MIN_STEPS:
+ observed = st["bad_total"] / st["boundaries"]
+ if observed > float(frac):
+ self._halt_skip_cap(
+ f"{st['bad_total']} of {st['boundaries']} optimizer steps were bad "
+ f"({observed:.1%}), above train.max_skipped_fraction={frac} "
+ f"(last cause: {cause})",
+ cause,
+ )
+
+ def _halt_skip_cap(self, detail: str, cause: str) -> None:
+ self.tripwire_alarms.append(
+ {"step": self.micro_step, "alarm": "skip_cap", "response": f"HALT: {cause}"}
+ )
+ raise NonFiniteHaltError(
+ f"HALT skip cap at micro_step {self.micro_step} (optimizer step "
+ f"{self.optimizer_step}): {detail}. The run is not training: fix the "
+ "cause (loss scale / LR / data) or raise the cap deliberately; the last "
+ "good checkpoint is on disk."
+ )
+
+ def _agree_or_raise(
+ self, local_exc: Optional[BaseException], what: str, halt_on_failure: bool = False
+ ) -> None:
+ """Make a rank-local failure a GLOBAL one before anyone raises.
+
+ THE LONE-RAISER HANG. A tripwire verdict, the arm-time self-test and the
+ trunk probe all read rank-local numbers (this rank's data, this rank's
+ shard). A rank that raises alone unwinds out of the loop while its peers
+ enter the next micro-step's collective and wait for it -- the process
+ group does not fail, it blocks until the watchdog fires 30 minutes
+ later. So: reduce the verdict (MAX) first, every rank at the same
+ program point, then raise everywhere.
+
+ A `HaltError` is raised IDENTICALLY on every rank, built from the
+ rank-ordered gather of every failing rank's message. Any other
+ exception (a KeyError from a missing metric) is re-raised as itself on
+ the rank that hit it, and peers get a `HaltError` naming it.
+
+ `halt_on_failure=True` (the trunk probe and the arm-time self-test) converts ANY failure
+ into a `HaltError` on the originating rank too (U01-21): those checks are deterministic,
+ so a plain exception there was an ordinary crash with no HALT.json -- the supervisor
+ relaunched a run that would fail identically.
+ """
+ if (
+ halt_on_failure
+ and local_exc is not None
+ and not isinstance(local_exc, HaltError)
+ ):
+ converted = HaltError(f"{what} failed: {type(local_exc).__name__}: {local_exc}")
+ converted.__cause__ = local_exc
+ local_exc = converted
+ if _dist_world_size() == 1:
+ if local_exc is not None:
+ raise local_exc
+ return
+ from ..utils.dist import any_rank
+
+ if not any_rank(local_exc is not None):
+ return
+ # (message, exit code) per rank, so the identical HaltError every rank
+ # raises carries the SAME exit status: a non-finite verdict on one rank
+ # must not be reported as a tripwire HALT by its peers.
+ msgs = all_gather_objects(
+ None
+ if local_exc is None
+ else (
+ f"{type(local_exc).__name__}: {local_exc}",
+ int(getattr(local_exc, "exit_code", EXIT_TRIPWIRE_HALT)),
+ )
+ )
+ failed = [(r, m) for r, m in enumerate(msgs) if m is not None]
+ if local_exc is not None and not isinstance(local_exc, HaltError):
+ raise local_exc
+ cls = NonFiniteHaltError if any(c == EXIT_NONFINITE_HALT for _, (_, c) in failed) else HaltError
+ raise cls(
+ "; ".join(f"[rank {r}] {m}" for r, (m, _) in failed)
+ + f" (rank-reduced during {what}: all ranks stop together)"
+ )
+
+ def _apply_scheduled_lr(self) -> None:
+ """Write the scheduler's current LR into every optimizer param group.
+
+ A no-op for an optimizer that exposes no `param_groups` (a test stub);
+ a real optimizer that does expose them gets the scheduled value.
+ """
+ # Remember the value so the log reports what the step USED. The scheduler
+ # is stepped right after the optimizer step, so `scheduler.lr()` at log
+ # time is lr_{k+1} on the micro that applied step k: the log ran one
+ # schedule step ahead of the optimizer (review finding, low).
+ lr = self.scheduler.lr()
+ self._last_applied_lr = lr
+ opt = self.optimizer
+ groups = getattr(opt, "param_groups", None)
+ if not groups:
+ return
+ for g in groups:
+ g["lr"] = lr
+
+ def _log(self, batch, out: LossOutput, stepped: bool) -> None:
+ metrics = {
+ "loss": float(out.loss.detach()),
+ # The lr the optimizer last APPLIED; before the first step (or just
+ # after a resume) the lr the next step will apply.
+ "lr": (
+ self._last_applied_lr
+ if getattr(self, "_last_applied_lr", None) is not None
+ else self.scheduler.lr()
+ ),
+ "micro_step": self.micro_step,
+ "optimizer_step": self.optimizer_step,
+ # Undefined on a micro-step that did not step, and on a skipped
+ # step. NaN, never 0.0 -- a 0.0 grad norm reads as 'converged'.
+ "grad_norm": (self.train_module.last_grad_norm if stepped else NAN),
+ "batch": list(batch),
+ "n_tokens": out.n_tokens,
+ "skipped_steps": self.train_module.skipped_steps,
+ }
+ # NOTE: tripwire INPUTS are deliberately NOT seeded here. Seeding
+ # `margin`/`logps/chosen` with NaN is exactly what disarmed the
+ # collapse tripwire: it made the key always present and always
+ # unusable, so the guard ran blind and reported itself armed. The loss
+ # adapter supplies them when they exist; when it does not, the
+ # arm-time self-test below halts rather than pretending.
+ metrics.update(out.metrics)
+ self.tracker.log(self.micro_step, metrics)
+
+ # Fail fast: the trunk probe runs at the first log step AFTER the
+ # first APPLIED optimizer step. Not earlier -- before any step lands,
+ # "the trunk has not moved" is trivially true and the probe would fire
+ # on every run. Not later: the whole point is to fail before
+ # GPU-hours accumulate behind a frozen trunk.
+ if not self._probe_done and self._applied_since_snapshot >= 1:
+ err = None
+ try:
+ self.trunk_report = self.trunk_probe.check(step=self.micro_step)
+ except Exception as e: # noqa: BLE001 -- re-raised by _agree_or_raise
+ err = e
+ self._agree_or_raise(err, "trunk-movement probe", halt_on_failure=True)
+ self._probe_done = True
+
+ # Arm-time self-test, once, at the first log step: every armed
+ # tripwire's inputs present and finite, or halt before training
+ # proceeds. This turns metric-name drift into a launch failure.
+ if not self._tripwire_self_test_done:
+ err = None
+ try:
+ assert_tripwires_armed(metrics, self.tripwires)
+ except Exception as e: # noqa: BLE001 -- re-raised by _agree_or_raise
+ err = e
+ self._agree_or_raise(err, "tripwire arm-time self-test", halt_on_failure=True)
+ self._tripwire_self_test_done = True
+
+ self._check_tripwires(metrics)
+
+ def _check_tripwires(self, metrics: dict, names: Optional[tuple] = None) -> None:
+ """Feed each armed tripwire from the CANONICAL metric keys.
+
+ Reads `metrics[...]` directly and lets a KeyError surface: an armed
+ tripwire whose input has gone missing mid-run must fail loudly, not
+ fall back to a default. The arm-time self-test catches this at step
+ one; this is the backstop if a later adapter stops emitting the key.
+ """
+ halt: Optional[BaseException] = None
+ for name, wire in self.tripwires.items():
+ if names is not None and name not in names:
+ continue
+ try:
+ if name == "logps_chosen":
+ r = wire.update(
+ metrics[LOGPS_CHOSEN_KEY], metrics[LENGTHS_CHOSEN_KEY], metrics[MARGIN_KEY]
+ )
+ elif name == "grad_norm":
+ r = wire.update(metrics[GRAD_NORM_KEY])
+ elif name == "length_drift":
+ # `pairs` is the sample the fraction was computed over: the
+ # tripwire needs it to know whether it has seen enough to
+ # speak, and it is a key the DPO adapter already emits.
+ r = wire.update(metrics["chosen_longer_fraction"], n_pairs=metrics["pairs"])
+ else: # pragma: no cover
+ raise RuntimeError(f"no feed defined for tripwire {name!r}")
+ except Exception as e: # noqa: BLE001 -- a missing input is a rank-local failure
+ halt = e
+ break
+ if r.tripped:
+ if not any(a["alarm"] == name for a in self.tripwire_alarms):
+ # R5: first fire of this wire pins the next checkpoint, so
+ # an early-stop/rewind decision has somewhere to rewind TO.
+ self._pin_next_save = True
+ self.tripwire_alarms.append(
+ {"step": self.micro_step, "alarm": name, "response": r.response}
+ )
+ if getattr(wire, "HALT", False):
+ # R3 (2026-08-23 audit): a HALT tripwire stops the run at
+ # the alarm. The recorded rerun trained 221 optimizer steps
+ # past a 355,830 grad-norm blow-up because every guard only
+ # logged; a gradient explosion is a defect, not a metric.
+ halt = HaltError(
+ f"HALT tripwire {name!r} fired at micro_step "
+ f"{self.micro_step}: {r.response} (value={r.value})"
+ )
+ break
+ # The verdict is rank-local (this rank's data, this rank's shard);
+ # reducing it is what keeps a rank that raised from leaving its peers
+ # in the next micro-step's collective. Skipped only when NO tripwire is
+ # armed -- config-derived, hence the same on every rank.
+ if self.tripwires:
+ self._agree_or_raise(halt, "tripwire check")
+ elif halt is not None: # pragma: no cover -- nothing to feed
+ raise halt
+
+ def summary(self) -> dict:
+ return {
+ "micro_step": self.micro_step,
+ "optimizer_step": self.optimizer_step,
+ "skipped_steps": self.train_module.skipped_steps,
+ "checkpoints_written": list(self.checkpoints_written),
+ "pinned_checkpoints": sorted(self.pinned_checkpoints),
+ "trunk_report": self.trunk_report,
+ "tripwire_alarms": list(self.tripwire_alarms),
+ "skip_stats": dict(self.skip_stats),
+ "tail_window": None if self.tail_window is None else dict(self.tail_window),
+ }
+
+ def throughput_probe(self, tokens_per_step: int) -> dict:
+ """The probe's report, for the launch banner.
+
+ `self.step_times` holds per-MICRO durations; `tokens_per_step` and
+ the scheduler horizon are per-OPTIMIZER-step. The aggregation is the
+ unit conversion (task #25) -- without it the banner over-reported
+ tokens/s and under-projected wall-clock by ~grad_accum x.
+ """
+ return throughput_report(
+ step_times_s=aggregate_micro_step_times(
+ self.step_times[: self.cfg.train.probe_steps], self.accum
+ ),
+ tokens_per_step=tokens_per_step,
+ optimizer_steps_planned=self.scheduler.total_steps,
+ )
+
+ # -- checkpointing ------------------------------------------------------
+
+ def full_state_dict(self):
+ """The module's unsharded CPU gather; kept on the Trainer because
+ callers (eval handoff, tests) address the Trainer. Incident history
+ lives on `TrainModule.full_model_state_dict`."""
+ return self.train_module.full_model_state_dict()
+
+ def save_checkpoint(self) -> str:
+ """Write bf16 weights + resume state, atomically.
+
+ Staged under `.partial` and renamed into place, so a host seizure
+ mid-write leaves nothing that looks resumable. A half-written
+ checkpoint is worse than none: it is found, loaded, and trusted.
+ """
+ import torch.distributed as dist
+
+ from ..utils.dist import is_rank0
+
+ # Idempotent BEFORE the collective: a re-save of an existing
+ # micro-step (resume that re-reaches an already-saved boundary) must
+ # neither crash on the rename nor delete a good checkpoint. Checked
+ # on every rank against the shared FS so all ranks skip together and
+ # nobody enters the state-gather collective alone.
+ name = f"micro_{self.micro_step:08d}"
+ # THE RANK-LOCAL PIN DEFECT (G3 audit U01-3). The flag is raised by a
+ # tripwire verdict, and logps_chosen / length_drift are fed from THIS
+ # rank's batch, so it can be set on rank 1 alone -- while pruning and
+ # resume.pt's pin list are rank 0's. Reduce it (MAX) so every rank pins
+ # or none does. save_checkpoint is entered by every rank at the same
+ # micro-step (cadence, coordinated stop, end-of-training), and the
+ # idempotence check just below is already a collective on this path.
+ if bool(all_reduce_max([int(self._pin_next_save)])[0]):
+ self._pin_next_save = True
+ if self._pin_next_save:
+ # R5: pinned on EVERY rank and on every save path (fresh, re-save,
+ # idempotent skip) -- prune consults this set on rank 0 only, but
+ # the flag must clear everywhere or a resume re-pins arbitrarily.
+ self.pinned_checkpoints.add(self.micro_step)
+ self._pin_next_save = False
+ logger.info("checkpoint %s PINNED (first tripwire fire precedes it)", name)
+ if self._resave_is_redundant(name):
+ logger.info("checkpoint %s already exists; skipping re-save", name)
+ if self.micro_step not in self.checkpoints_written:
+ self.checkpoints_written.append(self.micro_step)
+ return os.path.join(self.ckpt_root, name) if is_rank0() else ""
+
+ if self._sharded_checkpoints():
+ return self._save_checkpoint_sharded(name)
+
+ # Gathered on EVERY rank (it is a collective); written by rank 0 only.
+ # The same rename race that killed the gate ledger lives here: four
+ # ranks renaming one `.partial` directory, first one wins, three die.
+ state = self.full_state_dict()
+
+ # Optimizer state is SHARD-LOCAL per rank; a resume.pt holding only
+ # rank 0's moments hands ranks 1..N-1 the wrong slices on resume --
+ # plausible training on corrupted state, the exact failure the
+ # world-size guard names, through a different door. Gather every
+ # rank's CPU-cloned state to rank 0 over gloo (control plane; NCCL
+ # would bind this to GPU state).
+ from ..utils.dist import gloo_group
+
+ opt_sd = self.train_module.optim_state_dict()
+ from ..utils.dist import barrier
+
+ # Every rank's RNG streams (collective, symmetric, a few KB each); only
+ # rank 0 writes them. Gathered BEFORE the layout dispatch so both layouts
+ # share one collective sequence on every rank.
+ rng_all = all_gather_objects(capture_rng_state())
+ tw_all = all_gather_objects(self._tripwire_state())
+
+ if self._per_rank_optimizer_files():
+ return self._save_checkpoint_per_rank(name, state, opt_sd, rng_all, tw_all)
+
+ group = gloo_group()
+ if group is not None:
+ per_rank: list = [None] * dist.get_world_size()
+ dist.gather_object(opt_sd, per_rank if is_rank0() else None, dst=0, group=group)
+ else:
+ per_rank = [opt_sd]
+
+ if not is_rank0():
+ self.checkpoints_written.append(self.micro_step)
+ # WAIT for rank 0's write. Without this, peers ran ahead into the
+ # NEXT training step's FSDP all-gathers while rank 0 spent minutes
+ # writing 14.5GB; rank 0 could not join and the NCCL watchdog
+ # SIGABRT'd the run at the micro-300 save (Arm A rerun,
+ # 2026-08-20). The old no-barrier rationale (rank-0 crash mid-write
+ # strands peers) still ends the same way -- watchdog kills all --
+ # so the barrier costs nothing on the failure path and fixes the
+ # healthy one.
+ barrier()
+ return ""
+
+ final = os.path.join(self.ckpt_root, name)
+ staging = final + ".partial"
+ if os.path.isdir(staging):
+ shutil.rmtree(staging)
+ os.makedirs(staging, exist_ok=True)
+
+ try:
+ self.weight_format.save_weights(state, staging, torch.bfloat16)
+ # HF-layout completeness: the eval stage loads AutoTokenizer from
+ # the checkpoint dir, and a weights-only dir sent it down the
+ # slow-tokenizer conversion path (no sentencepiece installed) at
+ # hour 9 of stage-11. Vocab never changes during full-FT, so the
+ # pinned snapshot's tokenizer files ARE this checkpoint's.
+ self._copy_tokenizer(staging, name)
+ payload = self._resume_payload(rng_all, tw_all)
+ payload["optimizer_per_rank"] = per_rank
+ _torch_save_synced(payload, os.path.join(staging, "resume.pt"))
+ self._write_integrity(staging)
+ self._publish(staging, final)
+ except BaseException:
+ shutil.rmtree(staging, ignore_errors=True)
+ raise
+
+ # NO BARRIER AFTER THE WRITE, and my own 4-rank test is why. I had one
+ # here; a rank-0 failure during the write (in that run, a bad optimizer
+ # stub) meant rank 0 raised BEFORE reaching it while three peers sat in
+ # it -- a 400-second hang instead of a crash. That is the NCCL-timeout
+ # shape, produced by a barrier added for safety.
+ #
+ self.checkpoints_written.append(self.micro_step)
+ self._prune_checkpoints()
+ logger.info("checkpoint written: %s", final)
+ # Release the peers (see the barrier at the non-rank0 return above:
+ # both sides of the old barrier argument have now happened in
+ # production, and the watchdog makes the crash-path cost identical
+ # either way -- the barrier fixes the healthy path).
+ from ..utils.dist import barrier
+
+ barrier()
+ return final
+
+ def _resave_is_redundant(self, name: str) -> bool:
+ """COLLECTIVE. True iff a COMPLETE `name` is already on disk.
+
+ THE TRUSTED-CORRUPTION DEFECT. "A directory called micro_N exists" was
+ taken as "micro_N is saved". But `latest_checkpoint` skips a micro_N
+ that fails `checkpoint_problem` (the host seized after the rename, the
+ disk filled mid-copy) and resumes from the previous good one -- and the
+ resumed run then walks back up to N, finds the directory, logs "already
+ exists" and WRITES NOTHING. The broken directory stays the newest
+ entry, the weights at N exist nowhere on disk, and (worse) it sits in
+ `checkpoints_written`, so keep-N pruning counts it as one of the two
+ kept and deletes the last good one when the next save lands.
+
+ So: an existing directory is reused only if it is complete. A broken one
+ is moved aside (renamed, never deleted: an operator may want to look at
+ it; the name no longer matches `micro_*`, so neither resume nor the
+ supervisor sees it) and the save proceeds.
+
+ The verdict is rank 0's alone, reduced (MAX) to every rank. Deciding
+ from each rank's own view of the filesystem means a rank that saw the
+ directory (cached attributes, a node-local disk) skips while a peer
+ enters the state-gather collective alone, which hangs until the
+ watchdog -- the lone-rank shape every other decision here avoids.
+ """
+ from ..utils.dist import is_rank0
+
+ redundant = 0
+ final = os.path.join(self.ckpt_root, name)
+ if is_rank0() and os.path.isdir(final):
+ problem = checkpoint_problem(final)
+ if problem is None:
+ redundant = 1
+ else:
+ aside = os.path.join(self.ckpt_root, f"corrupt_{name}.{int(time.time())}")
+ logger.warning(
+ "checkpoint %s exists but is INCOMPLETE (%s): moved to %s and re-saving",
+ name,
+ problem,
+ os.path.basename(aside),
+ )
+ os.rename(final, aside)
+ _fsync_path(self.ckpt_root)
+ return bool(all_reduce_max([redundant])[0])
+
+ def _copy_tokenizer(self, staging: str, name: str) -> None:
+ """Best-effort by design: weights are the irreplaceable part, and eval
+ has a snapshot fallback. Loud, never silent. WHICH tokenizer is the
+ weight format's decision (a dense checkpoint gets the pinned Mistral
+ files; an OLMoE one only an explicitly configured directory)."""
+ try:
+ copied = self.weight_format.copy_tokenizer(self.cfg.paths.model_snapshot, staging)
+ except (FileNotFoundError, OSError):
+ copied = []
+ if not copied:
+ logger.warning(
+ "no tokenizer files copied for checkpoint %s (%s); it is weights-only "
+ "and eval must fall back to the snapshot",
+ name,
+ type(self.weight_format).__name__,
+ )
+
+ # -- per-rank optimizer-state files ---------------------------------------
+
+ def _per_rank_optimizer_files(self) -> bool:
+ """Config flag `train.per_rank_optimizer_state` (default OFF).
+
+ Default mode gathers every rank's optimizer state to rank 0 over gloo:
+ rank 0 holds the whole thing in host RAM at save time (12 B/param for
+ fp32 AdamW, 2 B/param under adamw8bit) on a shared box with no swap.
+ The per-rank mode has each rank write its OWN shard
+ (`optimizer_rank{r}.pt`) and gathers nothing. Read with getattr so a
+ config without the field (config.py is another lane's file) means OFF.
+ """
+ return bool(getattr(self.cfg.train, "per_rank_optimizer_state", False))
+
+ @staticmethod
+ def _write_integrity(staging: str, opt_shas: Optional[dict] = None, include_weights: bool = True) -> None:
+ """Record sha256 of the payloads the weights manifest does not cover (U01-3): resume.pt,
+ the gathered layout's safetensors weights, and (per-rank / sharded layouts) every rank's
+ optimizer file, whose hashes the ranks gathered. Written LAST, before the publish."""
+ sha = {}
+ for fn in sorted(os.listdir(staging)):
+ if fn == "resume.pt" or (include_weights and fn.endswith(".safetensors")):
+ sha[fn] = sharded_ckpt.file_sha256(os.path.join(staging, fn))
+ sha.update(opt_shas or {})
+ sharded_ckpt.write_integrity(staging, sha)
+
+ def _publish(self, staging: str, final: str) -> None:
+ """Make `staging` durable, rename it into place, make the rename durable.
+
+ fsync every file and directory under `staging` BEFORE the rename (see
+ `fsync_tree`), then fsync the checkpoint root so the new directory entry
+ itself survives a power loss. Order is the whole point.
+ """
+ fsync_tree(staging)
+ os.rename(staging, final)
+ _fsync_path(os.path.dirname(final))
+
+ def _data_fingerprint(self) -> dict:
+ """What identifies the data this run trains on, as primitives (U01-5). `len(sampler)` and
+ `epochs` fix the horizon; a sampler may add its own `fingerprint()` (content/order hash)."""
+ fp: dict = {}
+ sampler = getattr(self, "sampler", None)
+ try:
+ fp["len"] = int(len(sampler))
+ except (TypeError, AttributeError):
+ pass
+ epochs = getattr(getattr(getattr(self, "cfg", None), "train", None), "epochs", None)
+ if epochs is not None:
+ fp["epochs"] = int(epochs)
+ fn = getattr(sampler, "fingerprint", None)
+ if callable(fn):
+ fp["sampler"] = str(fn())
+ return fp
+
+ def _tripwire_state(self) -> dict:
+ """{wire name: its state_dict()} for every armed wire that can persist (U01-6)."""
+ out = {}
+ for name, wire in (getattr(self, "tripwires", None) or {}).items():
+ fn = getattr(wire, "state_dict", None)
+ if callable(fn):
+ out[name] = fn()
+ return out
+
+ def _resume_payload(
+ self, rng_per_rank: Optional[list] = None, tripwire_per_rank: Optional[list] = None
+ ) -> dict:
+ payload = {
+ "stream": self.stream.state_dict(),
+ "scheduler": self.scheduler.state_dict(),
+ "micro_step": self.micro_step,
+ "optimizer_step": self.optimizer_step,
+ "seed": self.cfg.train.seed,
+ # U01-5: what resume must still agree with. Recorded, never silently assumed.
+ "grad_accum": getattr(self, "accum", None),
+ "data_fingerprint": self._data_fingerprint(),
+ # U01-6: tripwire baselines / latches (rank-ordered, like the RNG streams: logps_chosen
+ # and length_drift are fed from each rank's own batch).
+ "tripwire_per_rank": (
+ tripwire_per_rank if tripwire_per_rank is not None else [self._tripwire_state()]
+ ),
+ # So the logged lr is continuous across a resume (the lr the last
+ # step used, not the next one).
+ "last_applied_lr": self._last_applied_lr,
+ # R5 (the review that found this): the pin set lived only in process
+ # memory, so a resumed run forgot which checkpoints were pinned and
+ # the first prune after the resume deleted the one pinned
+ # checkpoint it had been told to keep.
+ # `or` tolerates the bare Trainer stubs other lanes' tests call
+ # `Trainer._resume_payload(stub)` with.
+ "pinned_checkpoints": sorted(getattr(self, "pinned_checkpoints", None) or ()),
+ "skip_stats": dict(getattr(self, "skip_stats", None) or {}),
+ # One entry per rank (rank-ordered): RNG streams are per-rank, and
+ # rank 0 is the only writer of resume.pt.
+ "rng_per_rank": (
+ rng_per_rank if rng_per_rank is not None else [capture_rng_state()]
+ ),
+ }
+ # Generic extension slot: modules with loop-external resumable state
+ # (tripwire windows, a latest-weights pointer) expose extra_state().
+ fn = getattr(self.train_module, "extra_state", None)
+ if fn is not None:
+ payload["extra_state"] = fn()
+ # Sampler-owned state. The stream's (epoch, offset) is enough for a
+ # sampler whose order is a pure function of (seed, epoch); a sampler
+ # whose position depends on what happened at run time (GRPO's prompt
+ # cursor advances on every dead-group retry) must carry its own.
+ sfn = getattr(self.sampler, "state_dict", None)
+ if callable(sfn):
+ payload["sampler_state"] = sfn()
+ return payload
+
+ def _save_checkpoint_per_rank(self, name: str, state, opt_sd, rng_all=None, tw_all=None) -> str:
+ """Barriered, atomic per-rank save. Every rank runs this in lockstep.
+
+ Protocol (barrier count identical on all ranks, so none can strand):
+ rank 0 makes a clean `.partial` dir -> B1 -> every rank writes its own
+ `optimizer_rank{r}.pt` via tmp + os.replace (a rank removes ONLY its
+ own tmp/final file on failure) -> B2 -> rank 0 writes weights and
+ resume.pt (which records the world size: the manifest), renames the
+ dir, prunes -> B3. A rank that dies before its write leaves B2
+ unreached on its peers, and the rename never happens: the checkpoint
+ does not exist rather than existing incomplete.
+ """
+ import torch.distributed as dist # noqa: F401 (symmetry with the default path)
+
+ from ..utils.dist import barrier, is_rank0, rank as _rank, world_size as _world
+
+ final = os.path.join(self.ckpt_root, name)
+ staging = final + ".partial"
+ if is_rank0():
+ if os.path.isdir(staging):
+ shutil.rmtree(staging)
+ os.makedirs(staging, exist_ok=True)
+ barrier()
+
+ mine = os.path.join(staging, f"optimizer_rank{_rank()}.pt")
+ tmp = mine + ".tmp"
+ try:
+ _torch_save_synced(opt_sd, tmp)
+ os.replace(tmp, mine)
+ my_sha = {os.path.basename(mine): sharded_ckpt.file_sha256(mine)}
+ except BaseException:
+ for f in (tmp, mine):
+ try:
+ os.remove(f)
+ except OSError:
+ pass
+ raise
+ barrier()
+ opt_shas: dict = {}
+ for d in all_gather_objects(my_sha):
+ opt_shas.update(d)
+
+ if not is_rank0():
+ self.checkpoints_written.append(self.micro_step)
+ barrier()
+ return ""
+
+ try:
+ self.weight_format.save_weights(state, staging, torch.bfloat16)
+ self._copy_tokenizer(staging, name)
+ payload = self._resume_payload(rng_all, tw_all)
+ payload["optimizer_state"] = {"layout": "per_rank_files", "world_size": _world()}
+ _torch_save_synced(payload, os.path.join(staging, "resume.pt"))
+ self._write_integrity(staging, opt_shas)
+ self._publish(staging, final)
+ except BaseException:
+ shutil.rmtree(staging, ignore_errors=True)
+ raise
+ self.checkpoints_written.append(self.micro_step)
+ self._prune_checkpoints()
+ logger.info("checkpoint written (per-rank optimizer files): %s", final)
+ barrier()
+ return final
+
+ # -- sharded checkpoints (train.checkpoint_format = "sharded") -------------
+
+ def _sharded_checkpoints(self) -> bool:
+ """Config flag `train.checkpoint_format == "sharded"` (default "gathered").
+ Read with getattr so a config without the field means gathered."""
+ return getattr(self.cfg.train, "checkpoint_format", "gathered") == "sharded"
+
+ def _module_has_sharded_hooks(self) -> bool:
+ tm = self.train_module
+ return callable(getattr(tm, "sharded_model_state", None)) and callable(
+ getattr(tm, "load_sharded_model_state", None)
+ )
+
+ def _validate_checkpoint_format(self) -> None:
+ """Fail at CONSTRUCTION, not at the first save an hour in.
+
+ THE MERGED-NAME DEFECT this prevents: a module that overrides
+ `full_model_state_dict` (LoRA folds the adapter into merged weights; PPO
+ has its own gather) defines what "the weights" mean. The base-class
+ sharded path reads `model.state_dict()` directly, so it would write the
+ UNMERGED base under the checkpoint's usual name and a resume/eval would
+ load a model without the adapter -- no error, a worse model. Such a
+ module must provide `sharded_model_state()` / `load_sharded_model_state()`
+ or it cannot use the sharded format.
+ """
+ fmt = getattr(self.cfg.train, "checkpoint_format", "gathered")
+ if fmt not in sharded_ckpt.CHECKPOINT_FORMATS:
+ raise ValueError(
+ f"train.checkpoint_format={fmt!r}; expected one of {sharded_ckpt.CHECKPOINT_FORMATS}"
+ )
+ if fmt != "sharded":
+ return
+ tm = self.train_module
+ plain = (
+ type(tm).full_model_state_dict is TrainModule.full_model_state_dict
+ and type(tm).load_model_state_dict is TrainModule.load_model_state_dict
+ )
+ if not plain:
+ # MoETrainModule overrides both methods, but without expert
+ # parallelism they are `super()` calls verbatim -- and that is the
+ # OLMoE case this format was built for (Track M). WITH ep_groups the
+ # experts live in EP groups, not in the FSDP shards, and the
+ # state-dict methods do real work: refused.
+ try:
+ from .moe_module import MoETrainModule
+
+ plain = (
+ isinstance(tm, MoETrainModule)
+ and getattr(tm, "ep_groups", None) is None
+ and type(tm).full_model_state_dict is MoETrainModule.full_model_state_dict
+ and type(tm).load_model_state_dict is MoETrainModule.load_model_state_dict
+ )
+ except ImportError:
+ plain = False
+ if not (plain or self._module_has_sharded_hooks()):
+ raise ValueError(
+ f"train.checkpoint_format='sharded' with {type(tm).__name__}, which overrides the "
+ "weight state-dict methods (merged/adapter weights) and provides no "
+ "sharded_model_state()/load_sharded_model_state(): the sharded path would checkpoint "
+ "the unmerged base. Use checkpoint_format='gathered' or add the two hooks."
+ )
+
+ def _sharded_state(self):
+ fn = getattr(self.train_module, "sharded_model_state", None)
+ return fn() if callable(fn) and self._module_has_sharded_hooks() else self.train_module.model.state_dict()
+
+ def _save_checkpoint_sharded(self, name: str) -> str:
+ """COLLECTIVE. Every rank writes its own shards; nothing is gathered.
+
+ Protocol (every rank runs it; the collective sequence is identical):
+ rng all-gather -> rank 0 makes a clean `.partial` -> B1 -> each rank
+ writes `model_rank{r}.safetensors` + `optimizer_rank{r}.pt` (tmp +
+ fsync + rename) -> any-rank-failed reduction (a full disk on ONE rank
+ stops every rank now instead of stranding peers in the next gather) ->
+ all-gather of the per-rank infos -> rank 0 writes manifest, tokenizer,
+ resume.pt, fsync_tree + rename, prune -> any-rank-failed reduction
+ (doubles as the closing sync; rank-0 failure raises everywhere).
+ The peers' wait is the slowest rank's own 1/N write, not rank 0 writing
+ 14.5 GB while three ranks idle.
+ """
+ from ..utils.dist import barrier, is_rank0, rank as _rank, world_size as _world
+
+ final = os.path.join(self.ckpt_root, name)
+ staging = final + ".partial"
+ rng_all = all_gather_objects(capture_rng_state())
+ tw_all = all_gather_objects(self._tripwire_state())
+ opt_sd = self.train_module.optim_state_dict()
+ if is_rank0():
+ if os.path.isdir(staging):
+ shutil.rmtree(staging)
+ os.makedirs(staging, exist_ok=True)
+ barrier()
+
+ info, err, my_sha = None, None, {}
+ mine = os.path.join(staging, f"optimizer_rank{_rank()}.pt")
+ try:
+ info = sharded_ckpt.save_rank_shard(
+ self._sharded_state(), staging, rank=_rank(), world_size=_world()
+ )
+ _torch_save_synced(opt_sd, mine + ".tmp")
+ os.replace(mine + ".tmp", mine)
+ my_sha = {os.path.basename(mine): sharded_ckpt.file_sha256(mine)}
+ except BaseException as e: # noqa: BLE001 -- stay in lockstep with the peers, then re-raise
+ err = e
+ for f in (mine + ".tmp", mine):
+ try:
+ os.remove(f)
+ except OSError:
+ pass
+ if any_rank(err is not None):
+ if is_rank0():
+ shutil.rmtree(staging, ignore_errors=True)
+ if err is not None:
+ raise err
+ raise sharded_ckpt.ShardedCheckpointError(
+ f"a peer rank failed writing its shard of {name}; checkpoint abandoned"
+ )
+ infos = all_gather_objects(info)
+ opt_shas: dict = {}
+ for d in all_gather_objects(my_sha):
+ opt_shas.update(d)
+
+ err = None
+ if is_rank0():
+ try:
+ fmt = sharded_ckpt.describe_weight_format(self.weight_format)
+ manifest = sharded_ckpt.build_manifest(
+ infos,
+ # The OPTIMIZER's slot order, not the model's: the optimizer is built over
+ # `p.requires_grad` params only (Trainer default, qwen3/olmoe/grpo launchers),
+ # so with a frozen parameter every slot index after it points one name
+ # earlier in the full list. `reshard` then refused (or, for same-shape
+ # neighbours, re-cut the wrong tensor's moments under the wrong name).
+ param_order=[
+ n for n, p in self.train_module.model.named_parameters() if p.requires_grad
+ ],
+ export=fmt,
+ )
+ sharded_ckpt.write_manifest(staging, manifest)
+ self._copy_tokenizer(staging, name)
+ payload = self._resume_payload(rng_all, tw_all)
+ payload["optimizer_state"] = {"layout": "per_rank_files", "world_size": _world()}
+ _torch_save_synced(payload, os.path.join(staging, "resume.pt"))
+ self._write_integrity(staging, opt_shas, include_weights=False)
+ self._publish(staging, final)
+ except BaseException as e: # noqa: BLE001
+ err = e
+ shutil.rmtree(staging, ignore_errors=True)
+ if any_rank(err is not None):
+ if err is not None:
+ raise err
+ raise sharded_ckpt.ShardedCheckpointError(f"rank 0 failed publishing {name}; checkpoint abandoned")
+ self.checkpoints_written.append(self.micro_step)
+ if is_rank0():
+ self._prune_checkpoints()
+ logger.info("checkpoint written (sharded, %d rank files): %s", _world(), final)
+ return final
+ return ""
+
+ def _load_sharded_weights(self, path: str) -> None:
+ """COLLECTIVE. Each rank reads ONLY its own shard file.
+
+ Rank-local verdicts (manifest/world size are identical everywhere, but a
+ sha256 mismatch or a truncated file is one rank's alone) are reduced
+ before any weight is copied, so a bad shard on rank 2 stops all four
+ ranks with the same error instead of leaving three of them in the next
+ collective.
+ """
+ from ..utils.dist import rank as _rank, world_size as _world
+
+ err = None
+ try:
+ m = sharded_ckpt.read_manifest(path)
+ sharded_ckpt.check_world_size(path, m, _world())
+ bad = sharded_ckpt.verify_rank_file(path, _rank())
+ if bad:
+ raise sharded_ckpt.ShardedCheckpointError(bad)
+ except BaseException as e: # noqa: BLE001
+ err = e
+ if any_rank(err is not None):
+ if err is not None:
+ raise err
+ raise sharded_ckpt.ShardedCheckpointError(
+ f"a peer rank cannot load its shard of {path}; resume abandoned"
+ )
+ if m.get("resharded_from"):
+ logger.warning(
+ "checkpoint %s was RESHARDED from world_size=%s: weights and optimizer are exact, but "
+ "data-order position and RNG streams follow the old partitioning",
+ path,
+ m["resharded_from"].get("world_size"),
+ )
+ # THE LONE-READER DEFECT. The pre-checks above are deterministic across ranks; the reads
+ # below are not (EIO / a stale NFS handle on ONE rank's file, an OOM-killed page-in). That
+ # rank would raise while its peers moved on to the optimizer load and then into the first
+ # training collective, where they wait for the watchdog (30 minutes) instead of crashing
+ # with the cause. Same reduction as the save side: every rank stops together.
+ err = None
+ try:
+ loader = getattr(self.train_module, "load_sharded_model_state", None)
+ if callable(loader) and self._module_has_sharded_hooks():
+ loader(path, rank=_rank(), world_size=_world())
+ else:
+ n = sharded_ckpt.load_rank_shard(
+ self.train_module.model.state_dict(), path, rank=_rank(), world_size=_world()
+ )
+ logger.info("loaded %d local shards from %s", n, m["files"][str(_rank())]["name"])
+ except BaseException as e: # noqa: BLE001 -- stay in lockstep with the peers, then re-raise
+ err = e
+ if any_rank(err is not None):
+ if err is not None:
+ raise err
+ raise sharded_ckpt.ShardedCheckpointError(
+ f"a peer rank failed reading its shard of {path}; resume abandoned"
+ )
+
+ def _prune_checkpoints(self) -> None:
+ # Prune ONLY checkpoints THIS trainer wrote. Name-sorting the whole
+ # dir let a previous run's stale higher-numbered dirs shadow the live
+ # run's fresh saves: keep-2 kept the zombies and DELETED the new
+ # micro_00000100/200 (Arm A rerun, 2026-08-20). Stale dirs are a
+ # launch-hygiene problem, not this function's to silently absorb.
+ keep = self.cfg.train.keep_checkpoints
+ # R5: pinned checkpoints are outside the keep-N budget entirely.
+ prunable = set(self.checkpoints_written) - self.pinned_checkpoints
+ mine = sorted(f"micro_{m:08d}" for m in prunable)
+ for stale in mine[:-keep] if keep > 0 else mine:
+ shutil.rmtree(os.path.join(self.ckpt_root, stale), ignore_errors=True)
+
+ def _older_checkpoint_steps(self, below: int) -> list[int]:
+ """Micro-steps of the `micro_NNNNNNNN` directories on disk strictly below `below` (no partials)."""
+ if not os.path.isdir(self.ckpt_root):
+ return []
+ out = []
+ for d in os.listdir(self.ckpt_root):
+ m = re.fullmatch(r"micro_(\d{8})", d)
+ if m and int(m.group(1)) < below:
+ out.append(int(m.group(1)))
+ return sorted(out)
+
+ def _discard_partials(self) -> None:
+ """Delete leftover partials at startup. They masquerade as resumable."""
+ if not os.path.isdir(self.ckpt_root):
+ return
+ for entry in os.listdir(self.ckpt_root):
+ if entry.endswith(".partial"):
+ shutil.rmtree(os.path.join(self.ckpt_root, entry), ignore_errors=True)
+ logger.warning("discarded partial checkpoint %s", entry)
+
+ def latest_checkpoint(self) -> Optional[str]:
+ """The newest COMPLETE checkpoint directory, or None.
+
+ THE FOUND-LOADED-AND-TRUSTED DEFECT. Name order alone picked the highest
+ `micro_N` -- including one whose rename survived a host seizure but
+ whose data did not (zero-length weights, a cut resume.pt, a missing
+ optimizer shard). Resume then died in the middle of a 13GB load, or
+ worse, resumed from a partial state. Each candidate passes
+ `checkpoint_problem` first; an incomplete one is SKIPPED WITH A WARNING
+ and left on disk (an operator may want to inspect or repair it; deleting
+ evidence is not this function's job) and the next-newest is tried.
+ """
+ if not os.path.isdir(self.ckpt_root):
+ return None
+ dirs = sorted(
+ (
+ d
+ for d in os.listdir(self.ckpt_root)
+ if d.startswith("micro_") and not d.endswith(".partial")
+ ),
+ reverse=True,
+ )
+ for d in dirs:
+ path = os.path.join(self.ckpt_root, d)
+ problem = checkpoint_problem(path)
+ if problem is None:
+ return path
+ logger.warning(
+ "skipping INCOMPLETE checkpoint %s: %s (left on disk, not deleted)", d, problem
+ )
+ return None
+
+ def resume_from_latest(self) -> Optional[str]:
+ path = self.latest_checkpoint()
+ if path is None:
+ logger.info("no checkpoint to resume from; starting fresh")
+ return None
+ self.resume_from(path)
+ return path
+
+ def _verify_payload_integrity(self, path: str) -> None:
+ """COLLECTIVE. sha256-verify the payloads this rank is about to read (U01-3).
+
+ Rank 0 checks the shared files (resume.pt and, for the gathered layout, the weights); every
+ rank checks its own `optimizer_rank{r}.pt`. The verdicts are reduced before anyone raises,
+ so a bad file on one rank stops all ranks together instead of stranding the peers in the
+ next collective. A checkpoint with NO `integrity.json` predates the sidecar: it loads with
+ a warning. A sidecar that exists and disagrees never loads.
+ """
+ from ..utils.dist import is_rank0, rank as _rank
+
+ recorded = None
+ problem: Optional[str] = None
+ try:
+ recorded = sharded_ckpt.read_integrity(path)
+ if recorded is not None:
+ names = [f"optimizer_rank{_rank()}.pt"]
+ if is_rank0():
+ names += [
+ n
+ for n in recorded
+ if n == "resume.pt" or (n.endswith(".safetensors") and not n.startswith("model_rank"))
+ ]
+ problem = sharded_ckpt.verify_integrity(path, names, recorded)
+ except sharded_ckpt.CheckpointIntegrityError as e:
+ problem = str(e)
+ if recorded is None and problem is None:
+ logger.warning(
+ "checkpoint %s has no integrity.json (it predates payload hashing): resume.pt and "
+ "the optimizer files are NOT sha256-verified",
+ path,
+ )
+ if any_rank(problem is not None):
+ if problem is not None:
+ raise sharded_ckpt.CheckpointIntegrityError(
+ f"checkpoint {path} failed its sha256 integrity check: {problem}"
+ )
+ raise sharded_ckpt.CheckpointIntegrityError(
+ f"checkpoint {path}: a peer rank failed its sha256 integrity check"
+ )
+
+ def _check_resume_compat(self, state: dict, path: str) -> None:
+ """Refuse a resume whose seed / grad_accum / data fingerprint differ from the saved run (U01-5).
+
+ A stateless sampler's order is a pure function of (seed, epoch, data), so changing any of
+ them resumes at a stream offset that now means a different batch: weights, optimizer and
+ schedule all load cleanly and nothing looks wrong. A field the checkpoint does not record
+ (it predates this check) is warned about, never refused. `allow_resume_mismatch` is the
+ explicit operator override; it logs what it overrode.
+ """
+ diffs: list[str] = []
+ missing: list[str] = []
+ if "seed" in state:
+ if int(state["seed"]) != int(self.cfg.train.seed):
+ diffs.append(f"seed {state['seed']} -> {self.cfg.train.seed}")
+ else:
+ missing.append("seed")
+ if state.get("grad_accum") is not None:
+ if int(state["grad_accum"]) != int(self.accum):
+ diffs.append(f"grad_accum {state['grad_accum']} -> {self.accum}")
+ else:
+ missing.append("grad_accum")
+ saved_fp = state.get("data_fingerprint")
+ if isinstance(saved_fp, dict):
+ now_fp = self._data_fingerprint()
+ for k in sorted(set(saved_fp) & set(now_fp)):
+ if saved_fp[k] != now_fp[k]:
+ diffs.append(f"data fingerprint {k} {saved_fp[k]!r} -> {now_fp[k]!r}")
+ else:
+ missing.append("data fingerprint")
+ if missing:
+ logger.warning(
+ "checkpoint %s predates resume-compatibility recording (%s): cannot check them",
+ path,
+ ", ".join(missing),
+ )
+ if not diffs:
+ return
+ msg = (
+ f"checkpoint {path} was saved under a different configuration than this run: "
+ + "; ".join(diffs)
+ + ". The stream offset, schedule and optimizer state would be applied to a different "
+ "run with no error. Resume with the original seed / grad_accum / data, or pass "
+ "allow_resume_mismatch=True (train.allow_resume_mismatch) to override deliberately."
+ )
+ if not self.allow_resume_mismatch:
+ raise RuntimeError(msg)
+ logger.warning("OVERRIDDEN (allow_resume_mismatch): %s", msg)
+
+ def _restore_tripwire_state(self, state: dict, path: str) -> None:
+ """Restore tripwire baselines / latches (U01-6). Absent in older checkpoints: warn."""
+ from ..utils.dist import rank as _rank
+
+ if not self.tripwires:
+ return
+ per_rank = state.get("tripwire_per_rank")
+ if per_rank is None:
+ logger.warning(
+ "checkpoint %s predates tripwire-state persistence: tripwire baselines restart", path
+ )
+ return
+ if len(per_rank) != _dist_world_size():
+ logger.warning(
+ "checkpoint %s holds tripwire state for %d rank(s) but this run has %d: "
+ "tripwire baselines restart",
+ path,
+ len(per_rank),
+ _dist_world_size(),
+ )
+ return
+ for name, sd in per_rank[_rank()].items():
+ wire = self.tripwires.get(name)
+ loader = getattr(wire, "load_state_dict", None)
+ if not callable(loader):
+ continue
+ try:
+ loader(sd)
+ except Exception as e: # noqa: BLE001 -- a wire whose layout changed restarts, loudly
+ logger.warning("tripwire %r state in %s could not be restored (%s: %s)", name, path, type(e).__name__, e)
+
+ def resume_from(self, path: str) -> None:
+ """Restore weights, then optimizer, then loop state.
+
+ Order matters: the optimizer's fp32 master is the AUTHORITATIVE weight
+ copy, so it is loaded after the bf16 weights and overwrites them. That
+ is what makes a bf16-weights-only checkpoint a lossless resume.
+ """
+
+ self._verify_payload_integrity(path)
+ if sharded_ckpt.is_sharded(path):
+ # Self-describing, like per_rank_files: the directory's manifest, not
+ # the current flag, decides how weights are read.
+ self._load_sharded_weights(path)
+ else:
+ nmo_state = self.weight_format.load_weights(path)
+ # The CPU-slice per-tensor load (and its OOM incident history) lives
+ # on the module; the Trainer only sequences it against the rest of
+ # the resume payload below.
+ self.train_module.load_model_state_dict(nmo_state)
+
+ # weights_only=True: a checkpoint is a file on disk, and the resume
+ # payload is only tensors, dicts, lists and primitives. Unpickling
+ # arbitrary objects to reload our own state would be a code-execution
+ # path we do not need.
+ # map_location="cpu": legacy resume.pt files carry cuda:0 tensors,
+ # and letting torch.load restore device puts every rank's copy on
+ # GPU 0 (the tail-resume OOM). Loading lands on CPU; the optimizer's
+ # own load_state_dict moves state to each param's device.
+ state = torch.load(os.path.join(path, "resume.pt"), weights_only=True, map_location="cpu")
+ from ..utils.dist import rank as _rank
+
+ self._check_resume_compat(state, path)
+ per_rank = state.get("optimizer_per_rank")
+ layout = state.get("optimizer_state")
+ if layout is not None and layout.get("layout") == "per_rank_files":
+ # The checkpoint is self-describing: its manifest, not the current
+ # flag, decides how to read it. Shards are rank-local slices, so a
+ # different rank count would hand rank r a slice cut for another
+ # partitioning -- plausible training on wrong state. Refuse.
+ from ..utils.dist import world_size as _world
+
+ saved_w = int(layout["world_size"])
+ if saved_w != _world():
+ raise RuntimeError(
+ f"checkpoint {path} holds per-rank optimizer state for "
+ f"world_size={saved_w}, but this run has world_size={_world()}. "
+ "Per-rank shards cannot be re-sharded; resume with the same "
+ "number of ranks."
+ )
+ f = os.path.join(path, f"optimizer_rank{_rank()}.pt")
+ if not os.path.isfile(f):
+ raise FileNotFoundError(f"per-rank optimizer file missing: {f}")
+ self.train_module.load_optim_state_dict(
+ torch.load(f, weights_only=True, map_location="cpu")
+ )
+ elif per_rank is not None:
+ # THE SILENT-SHARD DEFECT (gather layout). The per-rank-FILES
+ # layout records its world size and refuses a mismatch; this
+ # layout recorded nothing, so a 2-rank resume of a 4-rank save
+ # quietly handed ranks 0/1 the first two shards of a four-way
+ # partitioning (and a 4-rank resume of a 2-rank save died with an
+ # IndexError on ranks 2/3 only, leaving ranks 0/1 in the next
+ # collective). The list's length IS the saved world size.
+ if len(per_rank) != _dist_world_size():
+ raise RuntimeError(
+ f"checkpoint {path} holds optimizer state for {len(per_rank)} rank(s), "
+ f"but this run has world_size={_dist_world_size()}. Per-rank optimizer "
+ "shards cannot be re-sharded; resume with the same number of ranks."
+ )
+ self.train_module.load_optim_state_dict(per_rank[_rank()])
+ else:
+ # Pre-fix checkpoint: only rank 0's shard-local moments were ever
+ # persisted; the other ranks' are gone. Loading rank 0's into
+ # every rank would be silently wrong state, so ALL ranks resume
+ # with FRESH moments -- consistent, honest, and recorded. Weights,
+ # schedule and stream position remain exact.
+ logger.warning(
+ "LEGACY resume.pt (%s) has no per-rank optimizer state; "
+ "resuming with FRESH optimizer moments on all ranks. This is "
+ "a recorded deviation, not a lossless resume.",
+ path,
+ )
+ # U01-2: fresh MOMENTS are the recorded deviation; fresh MASTERS would be a silent weight
+ # revert (the optimizer's fp32 master is authoritative and is still the launch weights).
+ if self.train_module.reseed_optimizer_from_params():
+ logger.warning(
+ "re-seeded the optimizer's fp32 master weights from the restored parameters"
+ )
+ if "extra_state" in state:
+ loader = getattr(self.train_module, "load_extra_state", None)
+ if loader is None:
+ logger.warning("checkpoint carries extra_state but the module cannot load it")
+ else:
+ loader(state["extra_state"])
+ self._restore_tripwire_state(state, path)
+ sampler_loader = getattr(self.sampler, "load_state_dict", None)
+ if callable(sampler_loader):
+ if "sampler_state" not in state:
+ # Strict on purpose: a stateful sampler resumed without its
+ # state restarts its data order from the beginning. Weights,
+ # optimizer and schedule would all be exact, so nothing would
+ # look wrong -- the run would just train on the wrong data.
+ raise RuntimeError(
+ f"checkpoint {path} carries no sampler state, but this run's "
+ f"sampler ({type(self.sampler).__name__}) keeps run-time "
+ "position. Resuming would replay its data from the start. "
+ "The checkpoint predates sampler-state persistence; start "
+ "the stage fresh or resume from a newer checkpoint."
+ )
+ sampler_loader(state["sampler_state"])
+ elif "sampler_state" in state:
+ raise RuntimeError(
+ f"checkpoint {path} carries sampler state but this run's sampler "
+ f"({type(self.sampler).__name__}) cannot load it: the data "
+ "source changed between save and resume."
+ )
+ self._last_applied_lr = state.get("last_applied_lr")
+ # Absent in checkpoints that predate G0: leave the in-memory defaults.
+ if "pinned_checkpoints" in state:
+ self.pinned_checkpoints = {int(m) for m in state["pinned_checkpoints"]}
+ if "skip_stats" in state:
+ self.skip_stats = {k: int(v) for k, v in state["skip_stats"].items()}
+ self._nonfinite_loss_acc = None
+ self.scheduler.load_state_dict(state["scheduler"])
+ self.stream.load_state_dict(state["stream"])
+ self.micro_step = int(state["micro_step"])
+ self.optimizer_step = int(state["optimizer_step"])
+ # `checkpoints_written` is a PER-PROCESS list: a fresh process starts
+ # with it empty. Two consumers key off it, and both read an empty list
+ # as "nothing has ever been saved":
+ # * the launch scripts' `_latest_weights` (rollout engine weights):
+ # after a resume the first rollout was generated by the SFT
+ # SUBSTRATE -- the policy before every update -- while the trainer
+ # computed ratios against the resumed policy;
+ # * keep-N pruning: the resumed checkpoint was invisible to it, so it
+ # was never pruned and `keep_checkpoints` was exceeded by one for
+ # the rest of the run (13.5GB each).
+ # The checkpoint we just resumed from IS the newest one on disk by
+ # construction (resume_from_latest picks it; an explicit older path is
+ # the operator's call), so register it. Idempotent: a re-resume or an
+ # idempotent re-save must not list one micro-step twice.
+ if self.micro_step not in self.checkpoints_written:
+ self.checkpoints_written.append(self.micro_step)
+ # PREDECESSORS TOO (Track M, 2026-10-12: the SIGTERM-resumed OLMoE run left micro_40, _44 and _80 on
+ # disk = 83GB with keep_checkpoints=2, on a disk that is 95% full). The interrupted attempt wrote
+ # micro_40 and micro_44; the new process knew only 44, so after saving 80 the list was [44, 80] and 40
+ # could never be pruned -- one leaked checkpoint per resume. Directories NUMBERED BELOW the one we
+ # resumed from are this lineage's older saves; adopting them is what keep-N means. Directories numbered
+ # ABOVE it are deliberately NOT adopted: those are the stale-run zombies `_prune_checkpoints` was
+ # written to ignore (a higher-numbered zombie shadowing a fresh save).
+ # OLDEST FIRST: consumers read `checkpoints_written[-1]` as "the newest save" (scripts/ppo.py
+ # `_latest_weights` hands it to the rollout engine), so the adopted predecessors go in FRONT of the
+ # resumed checkpoint, never after it.
+ adopted = [m for m in self._older_checkpoint_steps(self.micro_step) if m not in self.checkpoints_written]
+ self.checkpoints_written[:0] = adopted
+ # The trunk snapshot must be retaken: comparing post-resume parameters
+ # against a pre-resume snapshot would report movement that the resumed
+ # run did not produce.
+ self.trunk_probe = TrunkMovementProbe(self._probe_target if self._probe_target is not None else self.model)
+ self._probe_done = False
+ self._applied_since_snapshot = 0
+ # Last: nothing above may draw from an RNG after this point, or the
+ # restored stream would already be advanced.
+ rng = state.get("rng_per_rank")
+ if rng is None:
+ logger.warning(
+ "checkpoint %s predates RNG persistence: RNG streams restart from the seed", path
+ )
+ elif len(rng) != _dist_world_size():
+ logger.warning(
+ "checkpoint %s holds RNG state for %d rank(s) but this run has %d: "
+ "RNG streams restart from the seed",
+ path,
+ len(rng),
+ _dist_world_size(),
+ )
+ else:
+ restore_rng_state(rng[_rank()])
+ logger.info(
+ "resumed from %s at micro_step %d (optimizer_step %d)",
+ path,
+ self.micro_step,
+ self.optimizer_step,
+ )
diff --git a/code/align/utils/__init__.py b/code/align/utils/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/code/align/utils/dist.py b/code/align/utils/dist.py
new file mode 100644
index 0000000000000000000000000000000000000000..82c3b0caa6196312e8aaae8bab0c6f5a93af4c42
--- /dev/null
+++ b/code/align/utils/dist.py
@@ -0,0 +1,391 @@
+"""Thin distributed helpers -- env-var based so they work before
+init_process_group, plus the seeded-determinism policy and the
+wait-condition helper.
+
+No framework: torchrun sets RANK/WORLD_SIZE/LOCAL_RANK; we read them.
+"""
+
+from __future__ import annotations
+
+import os
+import random
+
+
+def rank() -> int:
+ return int(os.environ.get("RANK", "0"))
+
+
+def local_rank() -> int:
+ return int(os.environ.get("LOCAL_RANK", "0"))
+
+
+def world_size() -> int:
+ return int(os.environ.get("WORLD_SIZE", "1"))
+
+
+def is_rank0() -> bool:
+ return rank() == 0
+
+
+#: Default collective timeout. torch's own default is 10 min for NCCL (30 min
+#: for gloo), which is shorter than a 14.5GB checkpoint write on this box
+#: while peers wait in the post-save barrier; 30 min is long enough for that
+#: and short enough that a genuinely wedged rank is reported in the same
+#: sitting rather than after the 5-hour stall of the 2026-08-12 incident.
+DEFAULT_PG_TIMEOUT_S = 1800.0
+
+PG_TIMEOUT_ENV = "ALIGN_PG_TIMEOUT_S"
+
+
+def pg_timeout_s(environ=None) -> float:
+ """The process-group timeout in seconds: `ALIGN_PG_TIMEOUT_S` or 30 min.
+
+ Parsed strictly. A typo ("30m") that silently fell back to the default
+ would leave an operator believing a 60-second watchdog was armed; a
+ non-positive value would make every collective time out instantly (or,
+ in torch, mean "no timeout"). Both raise instead.
+ """
+ environ = os.environ if environ is None else environ
+ raw = environ.get(PG_TIMEOUT_ENV)
+ if raw is None or raw.strip() == "":
+ return DEFAULT_PG_TIMEOUT_S
+ try:
+ val = float(raw)
+ except ValueError:
+ raise ValueError(f"{PG_TIMEOUT_ENV}={raw!r} is not a number of seconds") from None
+ if not (val > 0.0) or val == float("inf"):
+ raise ValueError(f"{PG_TIMEOUT_ENV}={raw!r} must be a finite number of seconds > 0")
+ return val
+
+
+#: The timeout `init_distributed` armed the default process group with. The
+#: gloo CONTROL group (`gloo_group`) must use the same number -- see there.
+_armed_timeout_s: float | None = None
+
+
+#: The flight-recorder buffer-size variable was renamed TORCH_NCCL_TRACE_BUFFER_SIZE ->
+#: TORCH_FR_BUFFER_SIZE when the recorder moved out of ProcessGroupNCCL (torch 2.11:
+#: FlightRecorder.hpp:111 looks up {"TORCH_FR_BUFFER_SIZE", "TORCH_NCCL_TRACE_BUFFER_SIZE"}
+#: in that order). Both are read by 2.11; only the new one is the supported name.
+_FR_BUFFER_NEW = "TORCH_FR_BUFFER_SIZE"
+_FR_BUFFER_LEGACY = "TORCH_NCCL_TRACE_BUFFER_SIZE"
+_FR_RENAME_VERSION = (2, 11)
+
+
+def _torch_version_tuple(version: str | None = None) -> tuple[int, int]:
+ """(major, minor) of the installed torch, or of an explicit version string."""
+ import re
+
+ if version is None:
+ import torch
+
+ version = torch.__version__
+ m = re.match(r"^(\d+)\.(\d+)", str(version))
+ if not m:
+ raise ValueError(f"cannot parse a torch version from {version!r}")
+ return int(m.group(1)), int(m.group(2))
+
+
+def configure_nccl_diagnostics(
+ run_dir: str | None = None, environ=None, torch_version: str | None = None
+) -> dict:
+ """Turn the PyTorch NCCL flight recorder on, with its dump under the run dir.
+
+ Must run BEFORE `init_process_group`: the recorder and the watchdog read
+ these variables when the NCCL process group is constructed. Defaults only
+ (`setdefault`): an operator's explicit export always wins. Returns what it
+ set, for the launch banner.
+
+ Variable names verified against the installed torch 2.11.0 (headers under
+ torch/include/torch/csrc/distributed/c10d/ and the strings in
+ libtorch_cpu.so / libtorch_cuda.so):
+
+ * TORCH_FR_BUFFER_SIZE (torch >= 2.11, FlightRecorder.hpp:111) or, below 2.11,
+ TORCH_NCCL_TRACE_BUFFER_SIZE (ProcessGroupNCCL.hpp:114; still read as the
+ fallback alias on 2.11). Ring-buffer entries;
+ the 2.11 default is 2000, which one FSDP2 step with 32 layers can wrap, so we
+ ask for 20000. >0 enables the recorder.
+ * TORCH_NCCL_DUMP_ON_TIMEOUT (ProcessGroupNCCL.hpp:73). Dump the buffer when
+ the watchdog times out. Per the header it must be set together with
+ TORCH_NCCL_ENABLE_MONITORING=1 and a buffer size > 0; we do NOT set the
+ monitoring variable (its abort-on-stuck-heartbeat side effect is a separate
+ decision) and have not re-verified its 2.11 default, so on a build where it
+ is off the dump is simply not triggered.
+ * TORCH_FR_DUMP_TEMP_FILE (new name) / TORCH_NCCL_DEBUG_INFO_TEMP_FILE (legacy
+ name; both strings present in libtorch_cpu.so, neither in a header because the
+ dump writer is compiled). The value is a FILE PREFIX; torch appends the
+ global rank. Without it the dump goes to /tmp/nccl_trace_rank_, which this
+ box forbids (/tmp is noexec and not durable). We set both names so either
+ generation of the reader finds it.
+
+ `run_dir=None` falls back to `/nccl_trace` rather than the /tmp default.
+
+ THE DEPRECATED-NAME DEFECT (H2 review, 2026-10-13): on torch >= 2.11 the buffer
+ size is `TORCH_FR_BUFFER_SIZE`; the old `TORCH_NCCL_TRACE_BUFFER_SIZE` is only a
+ fallback alias, so exporting it alone arms the recorder through a name the
+ next release is free to drop -- and the recorder going dark is invisible until
+ the hang it was meant to explain. The legacy name is set ONLY below 2.11, where
+ it is the only one the build knows. An operator's explicit export of EITHER name
+ wins: the new name outranks the old in torch's lookup, so setting it over an
+ operator's legacy export would silently override their value.
+ """
+ environ = os.environ if environ is None else environ
+ new_torch = _torch_version_tuple(torch_version) >= _FR_RENAME_VERSION
+ buffer_name = _FR_BUFFER_NEW if new_torch else _FR_BUFFER_LEGACY
+ operator_set_buffer = _FR_BUFFER_NEW in environ or _FR_BUFFER_LEGACY in environ
+ trace_dir = os.path.join(run_dir or os.getcwd(), "nccl_trace")
+ os.makedirs(trace_dir, exist_ok=True)
+ prefix = os.path.join(trace_dir, "rank_")
+ wanted = {
+ buffer_name: "20000",
+ "TORCH_NCCL_DUMP_ON_TIMEOUT": "1",
+ "TORCH_FR_DUMP_TEMP_FILE": prefix,
+ "TORCH_NCCL_DEBUG_INFO_TEMP_FILE": prefix,
+ }
+ applied = {}
+ for k, v in wanted.items():
+ if k == buffer_name and operator_set_buffer:
+ continue
+ if k not in environ:
+ environ[k] = v
+ applied[k] = v
+ return applied
+
+
+def init_distributed(
+ backend: str = "nccl", run_dir: str | None = None, timeout_s: float | None = None
+) -> None:
+ """Idempotent process-group init for torchrun launches; no-op on a
+ single process.
+
+ Always passes an EXPLICIT timeout (`ALIGN_PG_TIMEOUT_S`, default 30 min)
+ instead of inheriting torch's backend-dependent default, and, for NCCL,
+ arms the flight recorder first (see `configure_nccl_diagnostics`) so a hang
+ leaves a per-rank trace of the last collectives instead of only a
+ watchdog SIGABRT.
+ """
+ if world_size() == 1:
+ return
+ import datetime
+
+ import torch.distributed as dist
+
+ if not dist.is_initialized():
+ if backend == "nccl":
+ configure_nccl_diagnostics(run_dir)
+ global _armed_timeout_s
+ t = pg_timeout_s() if timeout_s is None else float(timeout_s)
+ _armed_timeout_s = t
+ dist.init_process_group(backend=backend, timeout=datetime.timedelta(seconds=t))
+ import torch
+
+ # Only NCCL needs a device bound per rank; a gloo group on a box whose
+ # GPUs are hidden (the CPU test children) must not touch CUDA.
+ if backend == "nccl" and torch.cuda.is_available():
+ torch.cuda.set_device(local_rank())
+
+
+def barrier() -> None:
+ """No-op-safe barrier: single process or uninitialized group passes."""
+ if world_size() == 1:
+ return
+ import torch.distributed as dist
+
+ if dist.is_initialized():
+ dist.barrier()
+
+
+def seed_everything(seed: int, rank_offset: bool = True) -> int:
+ """The seeded-determinism policy.
+
+ `rank_offset=True` gives each rank a distinct-but-derived stream
+ (seed + rank) -- required for dataloader shuffling; checkpoint-resume
+ tests (section-07) and crosscoder streaming (section-08) rely on this
+ exact derivation to reproduce batch order after a restart.
+ """
+ s = seed + (rank() if rank_offset else 0)
+ random.seed(s)
+ try:
+ import numpy as np
+
+ np.random.seed(s % 2**32)
+ except ImportError:
+ pass
+ import sys
+
+ if "torch" in sys.modules: # never import torch just to seed it
+ import torch
+
+ torch.manual_seed(s)
+ return s
+
+
+def find_pids(interpreter_substr: str, script_path: str, exclude_self: bool = True) -> list[int]:
+ """Find processes by INTERPRETER + FULL SCRIPT PATH, never bare name.
+
+ Incident: a stage-4 launcher waited on `pgrep -f `, matched
+ its own command line, and idled 4 GPUs for 3 hours. Matching both the
+ interpreter and the absolute script path (and excluding ourselves and
+ our parent shell) cannot self-match.
+ """
+ me = os.getpid()
+ parent = os.getppid()
+ hits = []
+ for pid_dir in os.listdir("/proc"):
+ if not pid_dir.isdigit():
+ continue
+ pid = int(pid_dir)
+ if exclude_self and pid in (me, parent):
+ continue
+ try:
+ with open(f"/proc/{pid}/cmdline", "rb") as f:
+ argv = f.read().decode(errors="replace").split("\0")
+ except OSError:
+ continue
+ if len(argv) < 2:
+ continue
+ if interpreter_substr in os.path.basename(argv[0]) and any(a == script_path for a in argv):
+ hits.append(pid)
+ return hits
+
+
+def gloo_group():
+ """Cached CPU-side subgroup for object collectives and control-plane
+ gathers. None on a single process. gloo, never NCCL: object collectives
+ stage through pickled CPU tensors, and NCCL binds them to GPU state that
+ the control plane must not depend on.
+
+ THE UNARMED-WATCHDOG DEFECT. `dist.new_group(backend="gloo")` with no
+ `timeout` does NOT inherit the default group's timeout: torch fills in its
+ own per-backend default (`_get_default_timeout`, 30 min for gloo) whatever
+ `init_process_group` was given. Every stop / HALT / skip-cap decision and
+ every checkpoint gather runs on THIS group, so an operator who exported
+ `ALIGN_PG_TIMEOUT_S=120` to be told about a wedged rank within two minutes
+ still waited 30 on the optimizer-step boundary all-reduce -- the exact
+ shape of the 2026-08-12 stall, behind a knob that read as armed. The group
+ is therefore built with the timeout `init_distributed` armed the default
+ group with (or, when a caller initialised the group itself, the env value).
+ """
+ import datetime
+
+ import torch.distributed as dist
+
+ if world_size() == 1 or not dist.is_initialized():
+ return None
+ g = getattr(gloo_group, "_group", None)
+ if g is None:
+ t = _armed_timeout_s if _armed_timeout_s is not None else pg_timeout_s()
+ g = dist.new_group(backend="gloo", timeout=datetime.timedelta(seconds=t))
+ gloo_group._group = g
+ return g
+
+
+def all_reduce_max(values: list[int]) -> list[int]:
+ """Element-wise MAX of a short int vector across ranks, on the gloo group.
+
+ The control-plane primitive: "did ANY rank ...?" decisions (stop signal,
+ wall-clock, HALT, bad step) are made by reducing a 0/1 flag with MAX so that
+ every rank acts on the same answer at the same program point. gloo, not
+ NCCL, for the same reason as `gloo_group` -- and so a decision never
+ queues behind (or deadlocks with) in-flight CUDA collectives. A single
+ process returns the input unchanged.
+ """
+ import torch
+ import torch.distributed as dist
+
+ g = gloo_group()
+ if g is None:
+ return [int(v) for v in values]
+ t = torch.tensor([int(v) for v in values], dtype=torch.int64)
+ dist.all_reduce(t, op=dist.ReduceOp.MAX, group=g)
+ return [int(v) for v in t.tolist()]
+
+
+def any_rank(flag: bool) -> bool:
+ """True iff `flag` is True on at least one rank (identical on all ranks)."""
+ return bool(all_reduce_max([1 if flag else 0])[0])
+
+
+def all_gather_objects(obj) -> list:
+ """`obj` from every rank, rank-ordered, on the gloo group ([obj] on one process)."""
+ import torch.distributed as dist
+
+ g = gloo_group()
+ if g is None:
+ return [obj]
+ out: list = [None] * dist.get_world_size()
+ dist.all_gather_object(out, obj, group=g)
+ return out
+
+
+def capture_rng_state() -> dict:
+ """This process's RNG state: torch CPU, torch CUDA (current device only),
+ python `random`, numpy. Every leaf is a tensor, list, int, float, str or
+ None, because `resume.pt` is read back with `weights_only=True`.
+
+ WHY. `seed_everything` seeds the streams at launch, but a resumed process
+ re-seeds from scratch and so replays the first draws of the original run:
+ dropout masks, sampling noise and any shuffle that is not a pure function
+ of (seed, epoch) restart instead of continuing. The run is then not the run
+ its uninterrupted twin would have been. State is per rank (the streams are
+ seeded seed + rank).
+
+ CUDA: only the CURRENT device, and only if CUDA is already initialised.
+ `torch.cuda.get_rng_state_all()` would create a context on every visible
+ GPU from every rank -- under torchrun that is world_size x n_gpus contexts
+ on a box with 23GB cards.
+ """
+ import torch
+
+ py = random.getstate()
+ out: dict = {
+ "torch": torch.get_rng_state(),
+ "python": [py[0], list(py[1]), py[2]],
+ }
+ try:
+ import numpy as np
+
+ ns = np.random.get_state()
+ out["numpy"] = {
+ "name": str(ns[0]),
+ "keys": [int(k) for k in ns[1]],
+ "pos": int(ns[2]),
+ "has_gauss": int(ns[3]),
+ "cached_gaussian": float(ns[4]),
+ }
+ except ImportError: # pragma: no cover -- numpy is a hard dependency
+ pass
+ if torch.cuda.is_available() and torch.cuda.is_initialized():
+ dev = torch.cuda.current_device()
+ out["cuda"] = {str(dev): torch.cuda.get_rng_state(dev)}
+ return out
+
+
+def restore_rng_state(sd: dict) -> None:
+ """Inverse of `capture_rng_state`. Missing sections are skipped (an older
+ checkpoint or a CPU-only save resumed on GPU)."""
+ import torch
+
+ if "python" in sd:
+ ver, keys, gauss = sd["python"]
+ random.setstate((int(ver), tuple(int(k) for k in keys), gauss))
+ if "numpy" in sd:
+ import numpy as np
+
+ n = sd["numpy"]
+ np.random.set_state(
+ (
+ n["name"],
+ np.array(n["keys"], dtype=np.uint32),
+ int(n["pos"]),
+ int(n["has_gauss"]),
+ float(n["cached_gaussian"]),
+ )
+ )
+ if "torch" in sd:
+ torch.set_rng_state(sd["torch"].cpu())
+ if "cuda" in sd and torch.cuda.is_available():
+ for idx, state in sd["cuda"].items():
+ if int(idx) < torch.cuda.device_count():
+ # lazily applied if CUDA is not initialised yet
+ torch.cuda.set_rng_state(state.cpu(), device=int(idx))
diff --git a/code/align/utils/guard.py b/code/align/utils/guard.py
new file mode 100644
index 0000000000000000000000000000000000000000..31aa13acafc607237542c44eabcac4c931b522a3
--- /dev/null
+++ b/code/align/utils/guard.py
@@ -0,0 +1,664 @@
+"""Resource guards. Import and call BEFORE torch, in every single script.
+
+Each rule corresponds to a way this specific box has already been made to
+fall over:
+
+ * / has ~13GB free (and /home lives on it). A default HF cache or TMPDIR
+ fills it and then EVERY process on the machine starts failing. /tmp is
+ additionally noexec here, and it is tmpfs -- a different device from '/',
+ which is why the path assertion requires the /apps device specifically
+ rather than merely "not the root device".
+ * 186GB RAM, NO SWAP. 2026-08-12: two external jobs grew to ~59GB RSS each,
+ free memory fell to 2GB, ~23 processes wedged in D-state, load hit 619,
+ a 13.5GB checkpoint stalled at 1.9GB, and NCCL timed out after 5 hours,
+ killing a healthy run. Same day, a 12-worker data build against a live
+ 4-rank run exhausted /dev/shm and killed it via SIGBUS.
+ * 4x A10G at 23GB. Two 7B models on one device is an OOM, not a tight fit.
+ * 48 shared cores; unbounded OMP threads starve the dataloaders.
+
+Lockfile-inode discipline: the heavy-job lockfile is NEVER deleted, by any
+code path, ever. flock binds to the INODE -- deleting the file lets a second
+job lock a fresh inode while the first still "holds" the old one, and both
+run. Staleness is handled by flock's automatic release on process death.
+A grep-level regression test enforces this.
+"""
+
+from __future__ import annotations
+
+import logging
+import os
+import shutil
+import subprocess
+from dataclasses import dataclass
+from typing import Iterable
+
+from ..exit_codes import PreflightRefused
+
+logger = logging.getLogger(__name__)
+
+# Absolute paths, verified on this box. Never relative, never $HOME, never /tmp.
+APPS_ROOT = "/apps"
+PROJECT_ROOT = "/apps/development/rlhf/align"
+HF_HOME = "/apps/voxtral/.cache/huggingface" # already holds Mistral-7B-v0.3
+HF_HUB_CACHE = "/apps/voxtral/.cache/huggingface/hub"
+
+MIN_FREE_DISK_GB = 40.0
+
+#: Launch-time host-pressure floor. NOTE: for full-FT stages this alone is
+#: INSUFFICIENT -- offloaded optimizer state arrives over the first optimizer
+#: steps, after launch checks pass. Those stages must also call
+#: preflight_rss(cfg), which requires MemAvailable >= projected RSS + this.
+MIN_AVAILABLE_RAM_GB = 45.0
+#: 2026-10-15 incident: the old 2.0 allowed load 96 on 48 cores (~200% oversubscribed); unbounded cargo/lake
+#: builds then drove the host to 100% CPU and down. 0.75/core matches hostguard's 70% start line.
+MAX_LOAD_PER_CORE = 0.75
+#: Host-wide busy CPU %% above which a heavy job refuses to start (align/utils/hostguard.py Policy.start_cpu_max).
+MAX_START_CPU_PCT = 70.0
+MAX_DISK_IN_FLIGHT = 64
+
+#: Only one heavy align job at a time. Held for the process lifetime; released
+#: by the kernel on process death. See module docstring: never deleted.
+LOCK_PATH = f"{PROJECT_ROOT}/.align_heavy_job.lock"
+_lock_handle = None
+
+# RSS-projection constants with provenance. These are the ONLY hardcoded
+# numbers in the projection; everything param-sized derives from the config.
+#: Python + CUDA host-side allocations + NCCL buffers per rank process,
+#: observed ~4GB/rank on the predecessor's 4-rank 7B FSDP runs.
+PER_RANK_FIXED_OVERHEAD_GB = 4.0
+#: Dataloader allowance per rank at the mandated <=2 workers with
+#: memory-mapped shards (workers touch pages, they do not copy shards).
+DATALOADER_ALLOWANCE_GB_PER_RANK = 1.5
+#: adamw8bit state layout, restated (this module must import before torch, so
+#: it cannot import `align.train.adamw8bit`): two uint8 code arrays (2 B/param)
+#: plus two fp32 absmax arrays per block (8 B / block). tests/test_guards.py
+#: pins these against `adamw8bit.DEFAULT_BLOCK_SIZE` so they cannot drift.
+ADAMW8BIT_BLOCK_SIZE = 2048
+#: Host-RAM multiple of the TOTAL optimizer-state bytes that a checkpoint save
+#: transiently holds. gather mode (default): every rank's CPU clone (1x summed
+#: over ranks) + the pickle copy each rank makes for `gather_object` (1x) + the
+#: unpickled copy on rank 0 (1x) = 3x. per-rank-file mode: the CPU clone only
+#: (torch.save streams to the file) = 1x. An ESTIMATE from the code path, not a
+#: measurement -- no 7B save has been run on this tree.
+CKPT_GATHER_TRANSIENT_FACTOR = 3.0
+CKPT_PER_RANK_TRANSIENT_FACTOR = 1.0
+
+
+def apply_resource_guards(
+ cpu_threads: int = 8,
+ niceness: int = 5,
+ min_free_disk_gb: float = MIN_FREE_DISK_GB,
+ heavy: bool = False,
+ label: str = "job",
+ strict_host_check: bool = True,
+ rss_cfg=None,
+) -> None:
+ """Set every environment variable that must be set before `import torch`,
+ then ASSERT the resolved paths landed on /apps.
+
+ `expandable_segments` matters more than it looks: alternating generation
+ (many small KV allocations) and training (few huge activation blocks)
+ fragments the caching allocator badly enough to OOM with gigabytes
+ nominally free.
+
+ Pass `rss_cfg` (a RunConfig) for full-FT stages to run the RSS-aware
+ preflight in addition to the generic host check.
+ """
+ os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "expandable_segments:True")
+
+ # Keep all caches on /apps.
+ os.environ.setdefault("HF_HOME", HF_HOME)
+ os.environ.setdefault("HF_HUB_CACHE", HF_HUB_CACHE)
+ os.environ.setdefault("HF_HUB_DISABLE_XET", "1")
+ os.environ.setdefault("TMPDIR", f"{PROJECT_ROOT}/.tmp")
+ os.environ.setdefault("TRITON_CACHE_DIR", f"{PROJECT_ROOT}/.cache/triton")
+ os.environ.setdefault("XDG_CACHE_HOME", f"{PROJECT_ROOT}/.cache")
+
+ # Assert the RESOLVED values, not the intended defaults: setdefault keeps
+ # a pre-set TMPDIR=/tmp, and only this assertion catches it -- at launch,
+ # not mid-run when the root filesystem fills.
+ writable = [
+ os.environ[k]
+ for k in (
+ "TMPDIR",
+ "TRITON_CACHE_DIR",
+ "XDG_CACHE_HOME",
+ "HF_HOME",
+ "HF_HUB_CACHE",
+ )
+ ]
+ assert_paths_off_root(writable)
+
+ # Bound CPU parallelism: default OMP is one thread per core, and on 48
+ # shared cores the main process fights its own dataloaders.
+ for var in (
+ "OMP_NUM_THREADS",
+ "MKL_NUM_THREADS",
+ "OPENBLAS_NUM_THREADS",
+ "NUMEXPR_NUM_THREADS",
+ ):
+ os.environ.setdefault(var, str(cpu_threads))
+
+ # Tokenizers' Rust parallelism deadlocks after fork in dataloader workers.
+ os.environ.setdefault("TOKENIZERS_PARALLELISM", "false")
+
+ try:
+ os.nice(niceness)
+ except OSError:
+ pass
+
+ _check_disk(min_free_disk_gb)
+
+ # `heavy=True` for anything that loads a 7B model, spawns workers, or
+ # writes multi-GB checkpoints -- but NEVER inside a multiprocessing
+ # child. `spawn` re-imports the entry script in every worker; a child
+ # that re-runs this call tries to take the EXCLUSIVE lock its parent
+ # holds, dies on BlockingIOError, and the pool silently breaks (the
+ # predecessor then trained for hours against a dead verifier). The lock
+ # and host checks are properties of the JOB; only the process that IS
+ # the job takes them.
+ if heavy and _in_multiprocessing_child():
+ heavy = False
+ logger.debug("multiprocessing child: heavy-job guards not applicable")
+ if heavy:
+ preflight_host_check(strict=strict_host_check)
+ if rss_cfg is not None:
+ preflight_rss(rss_cfg)
+ acquire_heavy_job_lock(label)
+
+ logger.info(
+ "resource guards applied (threads=%d nice=+%d heavy=%s alloc=%s)",
+ cpu_threads,
+ niceness,
+ heavy,
+ os.environ["PYTORCH_CUDA_ALLOC_CONF"],
+ )
+
+
+def assert_paths_off_root(paths: Iterable[str]) -> None:
+ """Assert every path stat-resolves onto the /apps device.
+
+ Stronger than "not the root device": on this box /tmp is tmpfs and /home
+ sits on its own mapping, so a device-inequality check against '/' alone
+ would wrongly accept both. The only durable, large filesystem here is
+ /apps; everything writable must land on it. Fail at launch, not mid-run.
+ """
+ apps_dev = os.stat(APPS_ROOT).st_dev
+ root_dev = os.stat("/").st_dev
+ for p in paths:
+ os.makedirs(p, exist_ok=True)
+ dev = os.stat(p).st_dev
+ if dev == root_dev or dev != apps_dev:
+ raise RuntimeError(
+ f"writable path {p!r} resolves to device {dev}, not the "
+ f"/apps device {apps_dev}. / has ~13GB free and /tmp is a "
+ "noexec tmpfs; a cache or TMPDIR off /apps takes down the "
+ "whole box. Fix the environment before launch."
+ )
+ logger.info("path policy ok: %d writable paths on /apps", len(list(paths)))
+
+
+# ---------------------------------------------------------------------------
+# RSS-aware preflight (review C2)
+# ---------------------------------------------------------------------------
+
+
+def projected_full_ft_rss_gb(cfg) -> dict:
+ """Project host RSS for a full-FT job FROM THE CONFIG, never hardcoded.
+
+ Components:
+ * offloaded optimizer state: 2 x fp32 moments = 8 bytes/param, only
+ when optimizer == offload_adamw (adamw8bit keeps state on GPU);
+ * fp32 master params: 4 bytes/param when offloading;
+ * pinned staging: <= cfg.train.pinned_staging_shards_per_rank
+ layer-shards per rank x world_size, at fp32 layer-shard size;
+ * fixed per-rank overhead + dataloader allowance (module constants,
+ provenance documented at their definition).
+
+ For 7.25B offloaded this projects ~105GB -- which is why the generic
+ 45GB launch floor alone would re-create the 2026-08-12 crash shape.
+
+ adamw8bit (the optimizer every shipped full-FT config names) keeps its
+ state ON the device: zero host optimizer RSS, so `total_gb` is just the
+ per-rank overhead. Two numbers the steady-state total cannot show are
+ reported separately:
+ * `device_optimizer_state_gb_per_rank`: the on-GPU budget
+ ((2 + 8/block) B/param over the rank's 1/world slice);
+ * `checkpoint_save_transient_gb`: the host RAM a SAVE transiently holds
+ (CKPT_*_FACTOR x total state; per-rank-file mode is 3x smaller than the
+ rank-0 gather). `peak_gb` = total + transient, and `preflight_rss`
+ gates on the PEAK -- a save is exactly when the box has been wedged.
+ For offload_adamw the transient is NOT modelled (it was not before this
+ change either; that config cannot launch 7.25B/4 ranks anyway), so there
+ peak == total.
+ """
+ n = float(cfg.model.n_params)
+ gib = 2.0**30
+ offloading = cfg.optimizer.name == "offload_adamw"
+ on_device_8bit = cfg.optimizer.name == "adamw8bit"
+
+ optimizer_state_gb = (n * 8.0 / gib) if offloading else 0.0
+ master_params_gb = (n * 4.0 / gib) if offloading else 0.0
+
+ world = cfg.train.world_size
+ layer_shard_gb = n / cfg.model.n_layers / world * 4.0 / gib
+ staging_cap = cfg.train.pinned_staging_shards_per_rank
+ pinned_staging_gb = (layer_shard_gb * staging_cap * world) if offloading else 0.0
+
+ per_rank_overhead_gb = (PER_RANK_FIXED_OVERHEAD_GB + DATALOADER_ALLOWANCE_GB_PER_RANK) * world
+
+ total = optimizer_state_gb + master_params_gb + pinned_staging_gb + per_rank_overhead_gb
+
+ state_8bit_gb = n * (2.0 + 8.0 / ADAMW8BIT_BLOCK_SIZE) / gib if on_device_8bit else 0.0
+ device_state_per_rank_gb = state_8bit_gb / world
+ per_rank_files = bool(getattr(cfg.train, "per_rank_optimizer_state", False))
+ factor = CKPT_PER_RANK_TRANSIENT_FACTOR if per_rank_files else CKPT_GATHER_TRANSIENT_FACTOR
+ ckpt_transient_gb = state_8bit_gb * factor
+ return {
+ "optimizer_state_gb": round(optimizer_state_gb, 2),
+ "master_params_gb": round(master_params_gb, 2),
+ "pinned_staging_gb": round(pinned_staging_gb, 2),
+ "per_rank_overhead_gb": round(per_rank_overhead_gb, 2),
+ "total_gb": round(total, 2),
+ "device_optimizer_state_gb_per_rank": round(device_state_per_rank_gb, 2),
+ "checkpoint_save_transient_gb": round(ckpt_transient_gb, 2),
+ "peak_gb": round(total + ckpt_transient_gb, 2),
+ }
+
+
+def preflight_rss(cfg, headroom_gb: float = 45.0) -> dict:
+ """Refuse to start when MemAvailable < projected job RSS + headroom.
+
+ The generic 45GB launch floor is INSUFFICIENT for this job class: the
+ offloaded optimizer state arrives over the first optimizer steps, AFTER
+ launch checks would have passed. This is the guard that closes that gap.
+ It gates on `peak_gb` (steady state + the checkpoint-save transient), not
+ `total_gb`: for adamw8bit the steady-state host footprint is just the
+ per-rank overhead and the save is the only thing that can push the box over.
+ Returns the projection breakdown for the launch banner.
+ """
+ proj = projected_full_ft_rss_gb(cfg)
+ st = host_state()
+ required = proj["peak_gb"] + headroom_gb
+ if st["ram_available_gb"] < required:
+ raise PreflightRefused(
+ f"RSS preflight FAILED: MemAvailable {st['ram_available_gb']:.1f}GB "
+ f"< projected job peak RSS {proj['peak_gb']:.1f}GB + {headroom_gb:.0f}GB "
+ f"headroom = {required:.1f}GB. Breakdown: {proj}. This box has no "
+ "swap; the offloaded state lands after launch and would wedge the "
+ "host mid-run. Wait for the host to quiesce or use adamw8bit. Under "
+ "adamw8bit the PEAK is a checkpoint save: set "
+ "train.per_rank_optimizer_state = true to cut the save transient "
+ "3x (per-rank files instead of a rank-0 gather)."
+ )
+ logger.info(
+ "RSS preflight ok: %.0fGB available >= %.0fGB required (%s)",
+ st["ram_available_gb"],
+ required,
+ proj,
+ )
+ return proj
+
+
+def _in_multiprocessing_child() -> bool:
+ """Are we executing inside a multiprocessing worker (or its import of us)?
+
+ Three signals, because they become true at different moments and the one
+ that matters is the EARLIEST. During `spawn`, CPython's `spawn._main` ->
+ `prepare()` marks the current process `_inheriting` BEFORE re-importing
+ the parent's `__main__`; `parent_process()` only attaches later in
+ `_bootstrap`. Module-level code in the re-imported entry script therefore
+ runs while `parent_process()` is still None -- checking that alone misses
+ exactly the case this exists for.
+ """
+ import multiprocessing as mp
+
+ cur = mp.current_process()
+ if getattr(cur, "_inheriting", False): # mid-`spawn` re-import
+ return True
+ if cur.name != "MainProcess": # named worker
+ return True
+ return mp.parent_process() is not None # fully bootstrapped child
+
+
+def _check_disk(min_free_gb: float) -> None:
+ free_gb = shutil.disk_usage(APPS_ROOT).free / 2**30
+ if free_gb < min_free_gb:
+ raise PreflightRefused(
+ f"/apps has only {free_gb:.1f}GB free (need >= {min_free_gb}GB). "
+ "Refusing to start: a run that fills the disk corrupts its own "
+ "checkpoints. Free space or lower min_free_disk_gb deliberately."
+ )
+ logger.info("disk ok: /apps has %.1fGB free", free_gb)
+
+
+# ---------------------------------------------------------------------------
+# Host-pressure preflight
+# ---------------------------------------------------------------------------
+
+
+def host_state() -> dict:
+ """Snapshot the shared-machine conditions that decide whether to start."""
+ mem: dict[str, float] = {}
+ with open("/proc/meminfo") as f:
+ for line in f:
+ k, _, v = line.partition(":")
+ mem[k] = float(v.split()[0]) / 1024**2 # kB -> GiB
+ load1, load5, load15 = os.getloadavg()
+ n_cores = os.cpu_count() or 1
+
+ in_flight = 0
+ try:
+ with open("/proc/diskstats") as f:
+ for line in f:
+ p = line.split()
+ if len(p) > 11 and p[2] == "nvme2n1":
+ in_flight = int(p[11])
+ break
+ except OSError:
+ pass
+
+ d_state = 0
+ try:
+ out = subprocess.run(
+ ["ps", "-eo", "stat"], capture_output=True, text=True, timeout=15
+ ).stdout
+ d_state = sum(1 for ln in out.splitlines() if ln.strip().startswith("D"))
+ except Exception:
+ pass
+
+ return {
+ "ram_total_gb": mem.get("MemTotal", 0.0),
+ "ram_available_gb": mem.get("MemAvailable", 0.0),
+ "swap_total_gb": mem.get("SwapTotal", 0.0),
+ "load1": load1,
+ "load5": load5,
+ "n_cores": n_cores,
+ "load_per_core": load1 / n_cores,
+ "disk_in_flight": in_flight,
+ "d_state_procs": d_state,
+ }
+
+
+def preflight_host_check(
+ min_available_ram_gb: float = MIN_AVAILABLE_RAM_GB,
+ max_load_per_core: float = MAX_LOAD_PER_CORE,
+ max_disk_in_flight: int = MAX_DISK_IN_FLIGHT,
+ strict: bool = True,
+) -> dict:
+ """Refuse to start a heavy job on an already-stressed shared machine.
+
+ This is the guard that did not exist on 2026-08-12. Each threshold maps
+ to one observed symptom of that incident. Failing loudly here costs
+ seconds; not failing cost a 5-hour stall, a corrupted checkpoint, and a
+ dead run.
+ """
+ st = host_state()
+ problems = []
+
+ if st["ram_available_gb"] < min_available_ram_gb:
+ problems.append(
+ f"only {st['ram_available_gb']:.1f}GB RAM available "
+ f"(need >= {min_available_ram_gb:.0f}GB). This box has "
+ f"{st['swap_total_gb']:.0f}GB swap, so there is NO overflow: "
+ "exhausting RAM wedges the whole machine in D-state."
+ )
+ if st["load_per_core"] > max_load_per_core:
+ problems.append(
+ f"load average {st['load1']:.1f} over {st['n_cores']} cores "
+ f"= {st['load_per_core']:.1f}x (max {max_load_per_core}). The host "
+ "is already oversubscribed; adding ranks makes it worse for "
+ "everyone and will not run faster."
+ )
+ from align.utils import hostguard as _hg
+ cpu_pct = _hg.cpu_percent(1.0)
+ st["cpu_pct"] = cpu_pct
+ if cpu_pct > MAX_START_CPU_PCT:
+ problems.append(f"host CPU {cpu_pct:.0f}% busy (max {MAX_START_CPU_PCT:.0f}% at start; the watchdog freezes "
+ "our jobs at 92%).")
+ st["in_align_slice"] = _hg.in_slice()
+ if not st["in_align_slice"] and os.environ.get("ALIGN_ALLOW_UNSCOPED") != "1":
+ problems.append("not running inside align.slice: launch through `scripts/hostguard.py run -- ` so the "
+ "kernel caps CPU / memory for this job and every child (set ALIGN_ALLOW_UNSCOPED=1 only for tests).")
+ if st["disk_in_flight"] > max_disk_in_flight:
+ problems.append(
+ f"disk queue depth {st['disk_in_flight']} (max {max_disk_in_flight}). "
+ "Writeback is not keeping up; a multi-GB checkpoint write would "
+ "stall mid-file and can take the run down with an NCCL timeout."
+ )
+
+ if problems:
+ msg = (
+ "host preflight FAILED -- refusing to start:\n - "
+ + "\n - ".join(problems)
+ + "\n\nWait for the host to quiesce, or pass strict=False "
+ "deliberately if you accept the risk."
+ )
+ if strict:
+ raise PreflightRefused(msg)
+ logger.warning(msg)
+ else:
+ logger.info(
+ "host ok: %.0fGB RAM avail, load %.1f (%.2fx/core), disk q=%d",
+ st["ram_available_gb"],
+ st["load1"],
+ st["load_per_core"],
+ st["disk_in_flight"],
+ )
+ return st
+
+
+def acquire_heavy_job_lock(
+ label: str, blocking: bool = False, lock_path: str | None = None
+) -> bool:
+ """Ensure only ONE heavy align job runs at a time.
+
+ Uses flock, so the lock is released automatically by the kernel when the
+ process dies -- that is the ONLY staleness mechanism. The lockfile itself
+ is never deleted (see module docstring: flock binds to the inode).
+ Rank-aware: only rank 0 of a distributed job takes it.
+ """
+ global _lock_handle
+ import fcntl
+
+ if int(os.environ.get("RANK", "0")) != 0:
+ return True
+
+ path = lock_path or LOCK_PATH
+ _lock_handle = open(path, "a+")
+ flags = fcntl.LOCK_EX if blocking else (fcntl.LOCK_EX | fcntl.LOCK_NB)
+ try:
+ fcntl.flock(_lock_handle.fileno(), flags)
+ except BlockingIOError:
+ try:
+ _lock_handle.seek(0)
+ holder = _lock_handle.read().strip()
+ except OSError:
+ holder = "unknown"
+ _lock_handle.close()
+ _lock_handle = None
+ raise PreflightRefused(
+ f"another heavy align job holds the lock ({holder}). Two heavy jobs "
+ "at once is what killed the predecessor's first SFT run. WAIT for "
+ "the holder to exit -- flock auto-releases on process death. Do "
+ "NOT delete the lockfile: flock binds to the inode, and deleting "
+ "it lets two jobs run at once."
+ )
+ _lock_handle.seek(0)
+ _lock_handle.truncate()
+ _lock_handle.write(f"{label} pid={os.getpid()}\n")
+ _lock_handle.flush()
+ logger.info("acquired heavy-job lock for %r", label)
+ return True
+
+
+def _die_with_parent(parent_pid: int) -> None:
+ """Make the kernel SIGTERM this process when the lock-holding parent dies (Linux PR_SET_PDEATHSIG).
+
+ THE UNPROTECTED-ORPHAN DEFECT (R2 review). The lock lives and dies with the SUPERVISOR's file
+ description; a child that merely *borrowed* it keeps running after a SIGKILL / OOM-kill of the
+ supervisor, while the kernel has already released the flock. The next job then starts on a
+ host that still has an 8B generation or an 8-worker Lean pool running -- the "two heavy jobs at
+ once" the lock exists to prevent, reached through the very code written to keep it held. A
+ child that cannot be tied to its parent's lifetime must not borrow the lock, so a failure to
+ install the signal RAISES. The `getppid` re-check closes the window between the ownership check
+ and the prctl (a parent that died in it has already re-parented us to init).
+ """
+ import ctypes
+ import signal
+
+ try:
+ libc = ctypes.CDLL(None, use_errno=True)
+ rc = libc.prctl(1, int(signal.SIGTERM), 0, 0, 0) # PR_SET_PDEATHSIG = 1
+ err = ctypes.get_errno()
+ except (OSError, AttributeError) as e:
+ raise PreflightRefused(f"inherit_heavy_job_lock: cannot call prctl(PR_SET_PDEATHSIG): {e}") from e
+ if rc != 0:
+ raise PreflightRefused(
+ f"inherit_heavy_job_lock: prctl(PR_SET_PDEATHSIG) failed (errno {err}); a child that outlives its "
+ "lock-holding parent would run unprotected, so it must not borrow the lock."
+ )
+ if os.getppid() != parent_pid:
+ raise PreflightRefused(f"inherit_heavy_job_lock: parent {parent_pid} died while the lock was being inherited.")
+
+
+def inherit_heavy_job_lock(parent_pid: int, lock_path: str | None = None) -> bool:
+ """Adopt the heavy-job flock that this process's PARENT holds, instead of taking it.
+
+ A supervisor that must hold the lock across several consecutive heavy children (generation at
+ two caps, then Lean verification) cannot hand it down: the children's own
+ `acquire_heavy_job_lock` opens a new file description and blocks forever on the parent's.
+ Letting a child skip the lock on an unchecked flag would also let ANY process claim to hold it,
+ so the claim is verified three ways, and a failure raises instead of running unprotected:
+
+ 1. we are a direct child of `parent_pid` (not an unrelated process that merely knows the pid);
+ 2. the lock file's recorded holder says that pid took it;
+ 3. the flock is genuinely held right now (a fresh non-blocking attempt must FAIL).
+ """
+ import fcntl
+
+ if os.getppid() != int(parent_pid):
+ raise PreflightRefused(
+ f"inherit_heavy_job_lock: parent pid is {os.getppid()}, not {parent_pid}; refusing to claim a lock "
+ "this process did not inherit."
+ )
+ path = lock_path or LOCK_PATH
+ with open(path, "a+") as fh:
+ fh.seek(0)
+ holder = fh.read().strip()
+ if f"pid={int(parent_pid)}" not in holder.split():
+ raise PreflightRefused(f"lock file names holder {holder!r}, not pid={parent_pid}; refusing to inherit.")
+ try:
+ fcntl.flock(fh.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
+ except BlockingIOError:
+ _die_with_parent(int(parent_pid))
+ logger.info("inherited the heavy-job lock held by pid %s (%s)", parent_pid, holder)
+ return True
+ fcntl.flock(fh.fileno(), fcntl.LOCK_UN)
+ raise PreflightRefused(
+ f"lock file names pid={parent_pid} but nobody holds the flock (stale label): the parent did not take the "
+ "lock, so this child would run unprotected."
+ )
+
+
+def release_heavy_job_lock_for_tests() -> None:
+ """TESTS ONLY: drop the flock by closing the handle. Production code
+ never releases the lock explicitly -- it is held for process lifetime and
+ released by the kernel on exit."""
+ global _lock_handle
+ if _lock_handle is not None:
+ _lock_handle.close()
+ _lock_handle = None
+
+
+def pin_visible_devices(devices: str) -> None:
+ """Narrow CUDA_VISIBLE_DEVICES. MUST be called before `import torch`."""
+ import sys
+
+ if "torch" in sys.modules:
+ raise RuntimeError(
+ "pin_visible_devices() called after torch was imported; the device "
+ "list is read at torch's CUDA init and will be ignored. Move this "
+ "call to the top of the script."
+ )
+ os.environ["CUDA_VISIBLE_DEVICES"] = devices
+ logger.info("CUDA_VISIBLE_DEVICES=%s", devices)
+
+
+@dataclass(frozen=True)
+class GpuState:
+ index: int
+ name: str
+ total_gb: float
+ free_gb: float
+ compute_capability: tuple[int, int]
+
+
+def query_gpus() -> list[GpuState]:
+ """Query GPUs via nvidia-smi so this works before torch is imported."""
+ out = subprocess.run(
+ [
+ "nvidia-smi",
+ "--query-gpu=index,name,memory.total,memory.free,compute_cap",
+ "--format=csv,noheader,nounits",
+ ],
+ capture_output=True,
+ text=True,
+ check=True,
+ ).stdout.strip()
+ gpus = []
+ for line in out.splitlines():
+ idx, name, total, free, cap = [f.strip() for f in line.split(",")]
+ major, minor = cap.split(".")
+ gpus.append(
+ GpuState(
+ index=int(idx),
+ name=name,
+ total_gb=float(total) / 1024,
+ free_gb=float(free) / 1024,
+ compute_capability=(int(major), int(minor)),
+ )
+ )
+ return gpus
+
+
+def preflight_vram_check(
+ required_gb: float, device_idx: int = 0, label: str = "allocation"
+) -> None:
+ """Fail fast if `device_idx` cannot hold `required_gb`. The point is to
+ fail in 50ms with a clear message instead of 20 minutes into a run with
+ a CUDA OOM traceback that names no model."""
+ gpus = {g.index: g for g in query_gpus()}
+ if device_idx not in gpus:
+ raise RuntimeError(f"cuda:{device_idx} does not exist; have {sorted(gpus)}")
+ g = gpus[device_idx]
+ if g.free_gb < required_gb:
+ raise RuntimeError(
+ f"preflight failed for '{label}': cuda:{device_idx} has "
+ f"{g.free_gb:.1f}GB free but {required_gb:.1f}GB is required. "
+ f"({g.name}, {g.total_gb:.1f}GB total). Either free the device or "
+ "shard this model across more GPUs."
+ )
+ logger.info(
+ "preflight ok [%s]: cuda:%d %.1fGB free >= %.1fGB required",
+ label,
+ device_idx,
+ g.free_gb,
+ required_gb,
+ )
+
+
+def assert_ampere_or_newer() -> tuple[int, int]:
+ """bf16 needs SM80+. A10G is SM86, so this passes here -- but the check
+ exists so a future move to an older card fails loudly instead of silently
+ producing NaNs through an fp16 fallback."""
+ gpus = query_gpus()
+ if not gpus:
+ raise RuntimeError("no GPUs visible")
+ cc = min(g.compute_capability for g in gpus)
+ if cc < (8, 0):
+ raise RuntimeError(
+ f"compute capability {cc} < (8,0): bf16 is unavailable and fp16 "
+ "training of this model is numerically unsafe."
+ )
+ return cc
diff --git a/code/align/utils/host_state_gate.py b/code/align/utils/host_state_gate.py
new file mode 100644
index 0000000000000000000000000000000000000000..88f94c665ea58a1d354aaf3916c034e602053a24
--- /dev/null
+++ b/code/align/utils/host_state_gate.py
@@ -0,0 +1,159 @@
+"""Sustained D-state gate: /apps/CLAUDE.md hard rule 4. STDLIB ONLY.
+
+The rule: "`ps -eo stat | grep -c '^D'` above ~5 sustained means the host is
+wedged on I/O; do not add load, and expect writes to stall mid-file."
+`align.utils.guard.preflight_host_check` gates RAM, load and disk queue depth
+but not this, and the 2026-08-12 incident is the case where it matters: ~23
+processes sat in uninterruptible sleep while each of the other three numbers
+could be read as merely "busy". D-state processes cannot be killed, cannot be
+timed out and do not show up as CPU load, so the symptom the operator is told
+to watch for had no programmatic check at all.
+
+WHY "SUSTAINED". One `/proc` scan is a lottery ticket: a burst of reads on a
+healthy box parks a dozen processes in D for a few milliseconds (an `ls` over
+a cold NFS-ish directory, a writeback flush), and a gate that refuses on a
+single sample would refuse on a healthy host and be switched off within a day.
+A wedge is the opposite shape: the count STAYS high. So the gate samples
+`samples` times, `interval_s` apart, and refuses only if EVERY sample exceeds
+the threshold -- one clean reading is proof the host is draining.
+
+WHAT IT COUNTS. Processes (one per PID, the thread-group leader's state field
+from `/proc//stat`), not threads: that is what `ps -eo stat` prints, and
+therefore what the rule's "~5" was calibrated against. The state is parsed
+from AFTER the last ')' because the command name (field 2) may itself contain
+spaces and parentheses.
+
+This module is deliberately NOT wired into `guard.py` (a long run was live
+when it was written and its `preflight_host_check` behaviour was frozen). The
+recommended wiring is one line, below. It is opt-in until then: call
+`preflight_dstate_check()` from a new entry point, or run
+
+ python -m align.utils.host_state_gate [--max-d-state 5] [--samples 3]
+
+which exits 0 when the host is clear and `EXIT_PREFLIGHT_REFUSED` (83) when it
+is wedged, so a shell wrapper or the supervisor's `preflight=` hook can use it.
+
+RECOMMENDED WIRING (one line, in `align/utils/guard.py::apply_resource_guards`,
+directly after the existing `preflight_host_check()` call for `heavy=True`):
+
+ from .host_state_gate import preflight_dstate_check; preflight_dstate_check()
+
+Caveat for that wiring: the job's own processes count. A relaunch while the
+previous run's ranks are still unwinding in D-state is exactly what the gate
+should refuse, but a launcher that has already forked >5 D-state workers
+(heavy checkpoint load) must run it BEFORE spawning them -- which `heavy=True`
+at script start does.
+"""
+
+from __future__ import annotations
+
+import argparse
+import logging
+import os
+import sys
+import time
+from typing import Callable, Optional
+
+from ..exit_codes import EXIT_PREFLIGHT_REFUSED, PreflightRefused
+
+logger = logging.getLogger(__name__)
+
+#: /apps/CLAUDE.md rule 4: "above ~5 sustained".
+MAX_D_STATE_PROCESSES = 5
+#: Consecutive over-threshold readings required to call it sustained.
+DEFAULT_SAMPLES = 3
+DEFAULT_INTERVAL_S = 1.0
+
+
+def _state_of(stat_line: str) -> Optional[str]:
+ """Process state letter from one `/proc//stat` line, or None if malformed.
+
+ Parsed from after the LAST ')': field 2 is the command name in parentheses
+ and may contain ')' and spaces; the state is the first token after it."""
+ close = stat_line.rfind(")")
+ if close < 0:
+ return None
+ rest = stat_line[close + 1 :].split()
+ return rest[0] if rest else None
+
+
+def count_d_state_processes(proc_root: str = "/proc") -> int:
+ """Number of processes currently in uninterruptible sleep ('D').
+
+ A process that exits between `listdir` and `open` is simply not counted;
+ unreadable entries are skipped. An unreadable `/proc` raises (the gate must
+ not report "0 D-state" for a scan that did not happen)."""
+ n = 0
+ for entry in os.listdir(proc_root):
+ if not entry.isdigit():
+ continue
+ try:
+ with open(os.path.join(proc_root, entry, "stat")) as fh:
+ state = _state_of(fh.read())
+ except OSError:
+ continue
+ if state == "D":
+ n += 1
+ return n
+
+
+def preflight_dstate_check(
+ max_d_state: int = MAX_D_STATE_PROCESSES,
+ samples: int = DEFAULT_SAMPLES,
+ interval_s: float = DEFAULT_INTERVAL_S,
+ strict: bool = True,
+ counter: Optional[Callable[[], int]] = None,
+ sleep: Callable[[float], None] = time.sleep,
+) -> dict:
+ """Refuse (PreflightRefused, exit 83) iff the D-state count exceeds
+ `max_d_state` in EVERY one of `samples` readings taken `interval_s` apart.
+
+ Returns `{"counts": [...], "max_d_state": n, "sustained": bool}`. With
+ `strict=False` a sustained wedge is logged, not raised. Stops sampling at
+ the first clean reading: a host that is draining is not wedged."""
+ if samples < 1:
+ raise ValueError(f"samples must be >= 1, got {samples}")
+ if max_d_state < 0:
+ raise ValueError(f"max_d_state must be >= 0, got {max_d_state}")
+ read = counter or count_d_state_processes
+ counts: list[int] = []
+ for i in range(samples):
+ if i:
+ sleep(interval_s)
+ counts.append(int(read()))
+ if counts[-1] <= max_d_state:
+ break
+ sustained = len(counts) == samples and all(c > max_d_state for c in counts)
+ result = {"counts": counts, "max_d_state": max_d_state, "sustained": sustained}
+ if sustained:
+ msg = (
+ f"host D-state gate FAILED: {counts} processes in uninterruptible sleep over "
+ f"{samples} samples {interval_s:g}s apart (max {max_d_state}). The host is wedged "
+ "on I/O (the 2026-08-12 shape: ~23 D-state processes, load 619): do not add "
+ "load, and expect writes to stall mid-file. Wait for it to drain."
+ )
+ if strict:
+ raise PreflightRefused(msg)
+ logger.warning(msg)
+ else:
+ logger.info("D-state ok: counts=%s (max %d)", counts, max_d_state)
+ return result
+
+
+def main(argv=None) -> int:
+ ap = argparse.ArgumentParser(description="Refuse if the host has a sustained D-state pile-up.")
+ ap.add_argument("--max-d-state", type=int, default=MAX_D_STATE_PROCESSES)
+ ap.add_argument("--samples", type=int, default=DEFAULT_SAMPLES)
+ ap.add_argument("--interval", type=float, default=DEFAULT_INTERVAL_S)
+ a = ap.parse_args(argv)
+ try:
+ res = preflight_dstate_check(a.max_d_state, a.samples, a.interval)
+ except PreflightRefused as e:
+ print(str(e), file=sys.stderr)
+ return EXIT_PREFLIGHT_REFUSED
+ print(f"D-state ok: counts={res['counts']} (max {res['max_d_state']})")
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/code/align/utils/hostguard.py b/code/align/utils/hostguard.py
new file mode 100644
index 0000000000000000000000000000000000000000..333cc89edc5cc66356689bee41780e3a67baad72
--- /dev/null
+++ b/code/align/utils/hostguard.py
@@ -0,0 +1,447 @@
+"""Host guard: keep this shared box alive while our jobs run (stdlib only).
+
+Incident 2026-10-14/15: CPU-only build jobs (cargo / rustc / Lean `lake`) launched by an agent ignored the
+Python-level thread caps, drove the 48-core host to 100% CPU, starved the vLLM workers (RPC timeout) and
+finally took the machine down. The old guard (`align.utils.guard`) only checked the host at START and only
+for jobs that opted into the heavy lock; nothing bounded a job after it started.
+
+Three layers, each independent of the code being run:
+
+1. **Kernel caps on our jobs** (`align.slice`, a systemd user slice; cgroup v2). Every job launched through
+ `scripts/hostguard.py run` gets its own transient scope inside the slice with CPUQuota / MemoryMax /
+ TasksMax. The kernel enforces them on the whole process tree (cargo, rustc, lake, Lean, vLLM workers),
+ so a job cannot exceed them no matter how many children it forks.
+2. **Host watchdog** (`align-hostguard.service`). Samples host-wide CPU, MemAvailable, PSI, D-state count,
+ GPU temperature and disk headroom every few seconds, and acts on OUR processes only:
+ * host CPU >= `cpu_soft` -> lower the slice CPUQuota (throttle);
+ * host CPU >= `cpu_hard` (92%) -> freeze the slice (cgroup.freeze; state and memory kept) and SIGSTOP
+ heavy processes of ours that run outside the slice; thaw once CPU stays below `cpu_resume`;
+ * MemAvailable < `mem_warn_gb` -> SIGTERM the largest process of ours; < `mem_kill_gb` -> SIGKILL the slice;
+ * D-state, GPU temperature, disk headroom -> freeze.
+3. **Preflight** (`preflight()` here, also called from `guard.preflight_host_check`): a job refuses to start
+ when the host is already busy.
+
+Everything here is a pure decision function over a `Sample` plus thin actuators, so the policy is unit-tested
+without touching the host.
+"""
+from __future__ import annotations
+
+import dataclasses
+import json
+import os
+import shutil
+import signal
+import subprocess
+import time
+from pathlib import Path
+from typing import Iterable, Optional
+
+SLICE = "align.slice"
+EVENT_LOG = Path(os.environ.get("ALIGN_HOSTGUARD_LOG", "/apps/development/rlhf/align/.tmp/hostguard/events.jsonl"))
+STOP_FILE = Path(os.environ.get("ALIGN_HOSTGUARD_STOP", "/apps/development/rlhf/align/.tmp/hostguard/STOP"))
+
+#: process names of ours the watchdog never signals (the session, editors, the watchdog itself).
+ALLOWLIST = frozenset({
+ "claude", "node", "sshd", "bash", "sh", "systemd", "(sd-pam)", "dbus-broker", "dbus-broker-lau",
+ "sleep", "tmux", "screen", "ps", "python3-hostguard",
+})
+
+
+@dataclasses.dataclass(frozen=True)
+class Policy:
+ cpu_soft: float = 85.0 # % of all cores, host-wide
+ cpu_hard: float = 92.0 # the line the host must not cross
+ cpu_resume: float = 75.0 # thaw only below this ...
+ resume_after_s: float = 30.0 # ... for this long
+ hard_after_s: float = 6.0 # hard action once the hard line is held this long (two samples)
+ mem_warn_gb: float = 30.0 # MemAvailable: SIGTERM the largest of ours
+ mem_kill_gb: float = 15.0 # MemAvailable: SIGKILL every process in the slice
+ psi_mem_full: float = 20.0 # /proc/pressure/memory full avg10 (%) treated like mem_warn
+ dstate_max: int = 10 # D-state processes host-wide
+ gpu_temp_max: float = 85.0 # degC
+ home_min_gb: float = 1.0 # /home filled on 2026-10-14 and broke every tool call
+ apps_min_gb: float = 20.0
+ root_min_gb: float = 3.0 # / is 30 GB and shared with the OS
+ tmp_min_gb: float = 1.0
+ slice_cpu_cores: float = 40.0 # normal align.slice CPUQuota (cores); 40/48 = 83%
+ slice_cpu_floor: float = 8.0 # throttle never goes below this
+ slice_mem_gb: float = 130.0 # align.slice MemoryMax
+ # preflight (a job refuses to START above these)
+ start_cpu_max: float = 70.0
+ start_mem_min_gb: float = 45.0
+
+
+@dataclasses.dataclass
+class Sample:
+ t: float
+ cpu_pct: float # host-wide busy % over the last interval
+ mem_avail_gb: float
+ mem_total_gb: float
+ psi_mem_full: float
+ psi_cpu_some: float
+ dstate: int
+ gpu_temps: list
+ gpu_mem_used_frac: list
+ home_free_gb: float
+ apps_free_gb: float
+ root_free_gb: float = float("inf")
+ tmp_free_gb: float = float("inf")
+
+
+#: environment variables that decide where downloads, caches and temp files land. All must be under /apps
+#: (2026-10-19 rule: nothing is downloaded or written to /, /home or /tmp).
+PATH_ENV = ("TMPDIR", "TMP", "TEMP", "XDG_CACHE_HOME", "HF_HOME", "PIP_CACHE_DIR", "UV_CACHE_DIR", "npm_config_cache",
+ "TORCH_HOME", "TRITON_CACHE_DIR", "TORCHINDUCTOR_CACHE_DIR", "CUDA_CACHE_PATH", "GOCACHE", "GOMODCACHE",
+ "CARGO_HOME", "RUSTUP_HOME", "OPAMROOT", "ELAN_HOME", "PLAYWRIGHT_BROWSERS_PATH", "PUPPETEER_CACHE_DIR")
+APPS_ONLY_ENV = "/apps/development/rlhf/env/apps_only.sh"
+
+
+def off_apps_paths(env: dict) -> list[str]:
+ """`VAR=path` entries that would send downloads/caches/temp outside /apps (unset vars are reported too
+ for the temp + XDG cache ones, since tools then fall back to /tmp or ~/.cache)."""
+ bad = []
+ for k in PATH_ENV:
+ v = env.get(k)
+ if v is None:
+ if k in ("TMPDIR", "XDG_CACHE_HOME", "HF_HOME"):
+ bad.append(f"{k} unset")
+ continue
+ if not os.path.realpath(v).startswith("/apps/"):
+ bad.append(f"{k}={v}")
+ return bad
+
+
+# --------------------------------------------------------------------------------------------- sampling
+
+def _read_cpu_times() -> tuple[int, int]:
+ with open("/proc/stat") as f:
+ parts = f.readline().split()[1:]
+ vals = [int(x) for x in parts]
+ idle = vals[3] + (vals[4] if len(vals) > 4 else 0) # idle + iowait
+ return sum(vals[:8]), idle
+
+
+def cpu_percent(interval: float = 1.0) -> float:
+ t0, i0 = _read_cpu_times()
+ time.sleep(interval)
+ t1, i1 = _read_cpu_times()
+ dt = t1 - t0
+ return 0.0 if dt <= 0 else 100.0 * (1.0 - (i1 - i0) / dt)
+
+
+def _meminfo() -> dict:
+ out = {}
+ with open("/proc/meminfo") as f:
+ for line in f:
+ k, v = line.split(":", 1)
+ out[k] = int(v.split()[0]) / (1024 * 1024) # kB -> GiB
+ return out
+
+
+def _psi(kind: str, line: str) -> float:
+ try:
+ with open(f"/proc/pressure/{kind}") as f:
+ for l in f:
+ if l.startswith(line):
+ return float(l.split("avg10=")[1].split()[0])
+ except OSError:
+ pass
+ return 0.0
+
+
+def _dstate_count() -> int:
+ n = 0
+ for pid in os.listdir("/proc"):
+ if pid.isdigit():
+ try:
+ with open(f"/proc/{pid}/stat") as f:
+ if f.read().rsplit(")", 1)[1].split()[0] == "D":
+ n += 1
+ except (OSError, IndexError):
+ pass
+ return n
+
+
+def _gpus() -> tuple[list, list]:
+ if not shutil.which("nvidia-smi"):
+ return [], []
+ try:
+ out = subprocess.run(["nvidia-smi", "--query-gpu=temperature.gpu,memory.used,memory.total",
+ "--format=csv,noheader,nounits"], capture_output=True, text=True, timeout=10).stdout
+ except (subprocess.TimeoutExpired, OSError):
+ return [], []
+ temps, mem = [], []
+ for row in out.strip().splitlines():
+ try:
+ t, u, tot = (float(x) for x in row.split(","))
+ temps.append(t); mem.append(u / tot if tot else 0.0)
+ except ValueError:
+ pass
+ return temps, mem
+
+
+def _free_gb(path: str) -> float:
+ try:
+ return shutil.disk_usage(path).free / 2**30
+ except OSError:
+ return float("inf")
+
+
+def take_sample(interval: float = 2.0) -> Sample:
+ cpu = cpu_percent(interval)
+ m = _meminfo()
+ temps, gmem = _gpus()
+ return Sample(t=time.time(), cpu_pct=cpu, mem_avail_gb=m.get("MemAvailable", 0.0), mem_total_gb=m.get("MemTotal", 0.0),
+ psi_mem_full=_psi("memory", "full"), psi_cpu_some=_psi("cpu", "some"), dstate=_dstate_count(),
+ gpu_temps=temps, gpu_mem_used_frac=gmem, home_free_gb=_free_gb("/home"), apps_free_gb=_free_gb("/apps"),
+ root_free_gb=_free_gb("/"), tmp_free_gb=_free_gb("/tmp"))
+
+
+# --------------------------------------------------------------------------------------------- policy
+
+@dataclasses.dataclass
+class State:
+ frozen: bool = False
+ frozen_reason: str = ""
+ hard_since: Optional[float] = None
+ calm_since: Optional[float] = None
+ slice_cores: Optional[float] = None
+
+
+def decide(s: Sample, st: State, p: Policy) -> list[tuple]:
+ """Pure policy: returns actions as tuples, updates `st` in place.
+
+ Actions: ("kill_slice", reason) | ("term_largest", reason) | ("freeze", reason) | ("thaw", reason)
+ | ("set_cpu", cores, reason) | ("stop_outside", reason) | ("cont_outside", reason)."""
+ acts: list[tuple] = []
+ # memory first: it is the failure mode with no recovery (no swap on this box)
+ if s.mem_avail_gb < p.mem_kill_gb:
+ acts.append(("kill_slice", f"MemAvailable {s.mem_avail_gb:.1f} GB < {p.mem_kill_gb}"))
+ elif s.mem_avail_gb < p.mem_warn_gb or s.psi_mem_full >= p.psi_mem_full:
+ acts.append(("term_largest", f"MemAvailable {s.mem_avail_gb:.1f} GB / PSI full {s.psi_mem_full:.1f}"))
+
+ hard = []
+ if s.cpu_pct >= p.cpu_hard:
+ hard.append(f"host CPU {s.cpu_pct:.1f}% >= {p.cpu_hard}")
+ if s.dstate > p.dstate_max:
+ hard.append(f"{s.dstate} D-state processes > {p.dstate_max}")
+ hot = [t for t in s.gpu_temps if t >= p.gpu_temp_max]
+ if hot:
+ hard.append(f"GPU temp {max(hot):.0f}C >= {p.gpu_temp_max}")
+ if s.home_free_gb < p.home_min_gb:
+ hard.append(f"/home free {s.home_free_gb:.2f} GB < {p.home_min_gb}")
+ if s.apps_free_gb < p.apps_min_gb:
+ hard.append(f"/apps free {s.apps_free_gb:.1f} GB < {p.apps_min_gb}")
+ if s.root_free_gb < p.root_min_gb:
+ hard.append(f"/ free {s.root_free_gb:.1f} GB < {p.root_min_gb}")
+ if s.tmp_free_gb < p.tmp_min_gb:
+ hard.append(f"/tmp free {s.tmp_free_gb:.1f} GB < {p.tmp_min_gb}")
+
+ if hard:
+ st.calm_since = None
+ if st.hard_since is None:
+ st.hard_since = s.t
+ if not st.frozen and s.t - st.hard_since >= p.hard_after_s - 1e-9:
+ st.frozen, st.frozen_reason = True, "; ".join(hard)
+ acts += [("freeze", st.frozen_reason), ("stop_outside", st.frozen_reason)]
+ else:
+ st.hard_since = None
+ calm = s.cpu_pct < p.cpu_resume
+ if st.frozen:
+ if calm:
+ if st.calm_since is None:
+ st.calm_since = s.t
+ if s.t - st.calm_since >= p.resume_after_s - 1e-9:
+ st.frozen = False
+ acts += [("thaw", f"host CPU {s.cpu_pct:.1f}% < {p.cpu_resume} for {p.resume_after_s:.0f}s"),
+ ("cont_outside", "resume")]
+ st.calm_since = None
+ else:
+ st.calm_since = None
+
+ # soft throttle of the slice quota (only while not frozen)
+ if not st.frozen:
+ cur = st.slice_cores if st.slice_cores is not None else p.slice_cpu_cores
+ if s.cpu_pct >= p.cpu_soft:
+ new = max(p.slice_cpu_floor, cur * 0.6)
+ if new < cur - 1e-9:
+ acts.append(("set_cpu", new, f"host CPU {s.cpu_pct:.1f}% >= soft {p.cpu_soft}"))
+ st.slice_cores = new
+ elif s.cpu_pct < p.cpu_resume and cur < p.slice_cpu_cores:
+ new = min(p.slice_cpu_cores, cur * 1.5)
+ acts.append(("set_cpu", new, f"host CPU {s.cpu_pct:.1f}% < {p.cpu_resume}: relax"))
+ st.slice_cores = new
+ return acts
+
+
+def preflight(s: Sample, p: Policy) -> list[str]:
+ """Reasons a new job must not start now (empty = OK)."""
+ why = []
+ if s.cpu_pct > p.start_cpu_max:
+ why.append(f"host CPU {s.cpu_pct:.1f}% > {p.start_cpu_max}")
+ if s.mem_avail_gb < p.start_mem_min_gb:
+ why.append(f"MemAvailable {s.mem_avail_gb:.1f} GB < {p.start_mem_min_gb}")
+ if s.dstate > p.dstate_max:
+ why.append(f"{s.dstate} D-state processes")
+ if s.home_free_gb < p.home_min_gb * 2:
+ why.append(f"/home free {s.home_free_gb:.2f} GB")
+ if s.apps_free_gb < p.apps_min_gb:
+ why.append(f"/apps free {s.apps_free_gb:.1f} GB")
+ if s.root_free_gb < p.root_min_gb * 2:
+ why.append(f"/ free {s.root_free_gb:.1f} GB")
+ if any(t >= p.gpu_temp_max - 5 for t in s.gpu_temps):
+ why.append(f"GPU temp {max(s.gpu_temps):.0f}C")
+ return why
+
+
+# --------------------------------------------------------------------------------------------- actuators
+
+def _systemctl(*args: str) -> subprocess.CompletedProcess:
+ return subprocess.run(["systemctl", "--user", *args], capture_output=True, text=True, timeout=30)
+
+
+def slice_cgroup() -> Optional[Path]:
+ uid = os.getuid()
+ p = Path(f"/sys/fs/cgroup/user.slice/user-{uid}.slice/user@{uid}.service/{SLICE}")
+ return p if p.exists() else None
+
+
+def slice_pids() -> list[int]:
+ root = slice_cgroup()
+ if root is None:
+ return []
+ pids = []
+ for procs in root.rglob("cgroup.procs"):
+ try:
+ pids += [int(x) for x in procs.read_text().split()]
+ except OSError:
+ pass
+ return pids
+
+
+def in_slice(pid: int = 0) -> bool:
+ try:
+ return f"/{SLICE}/" in Path(f"/proc/{pid or os.getpid()}/cgroup").read_text()
+ except OSError:
+ return False
+
+
+def _rss_gb(pid: int) -> float:
+ try:
+ with open(f"/proc/{pid}/statm") as f:
+ return int(f.read().split()[1]) * os.sysconf("SC_PAGE_SIZE") / 2**30
+ except (OSError, IndexError, ValueError):
+ return 0.0
+
+
+def _comm(pid: int) -> str:
+ try:
+ return Path(f"/proc/{pid}/comm").read_text().strip()
+ except OSError:
+ return ""
+
+
+def own_heavy_outside(min_cpu_pct: float = 50.0, sample_s: float = 1.0, exclude: Iterable[int] = ()) -> list[int]:
+ """Our (same uid) processes OUTSIDE the slice using > min_cpu_pct of one core, excluding the allowlist,
+ the slice, our own ancestry and `exclude`. This is what catches ad-hoc commands launched without the
+ runner (the 2026-10-14 cargo builds)."""
+ uid, ex = os.getuid(), set(exclude) | {os.getpid(), os.getppid()}
+ sl = set(slice_pids())
+
+ def ticks():
+ out = {}
+ for pid in os.listdir("/proc"):
+ if not pid.isdigit():
+ continue
+ i = int(pid)
+ try:
+ if os.stat(f"/proc/{pid}").st_uid != uid:
+ continue
+ with open(f"/proc/{pid}/stat") as f:
+ rest = f.read().rsplit(")", 1)[1].split()
+ out[i] = int(rest[11]) + int(rest[12])
+ except (OSError, IndexError, ValueError):
+ pass
+ return out
+
+ a = ticks(); time.sleep(sample_s); b = ticks()
+ hz = os.sysconf("SC_CLK_TCK")
+ hits = []
+ for pid, t in b.items():
+ if pid in ex or pid in sl or _comm(pid) in ALLOWLIST:
+ continue
+ if 100.0 * (t - a.get(pid, t)) / (hz * sample_s) >= min_cpu_pct:
+ hits.append(pid)
+ return hits
+
+
+def _signal(pids: Iterable[int], sig: int) -> list[int]:
+ done = []
+ for pid in pids:
+ try:
+ os.kill(pid, sig); done.append(pid)
+ except OSError:
+ pass
+ return done
+
+
+def log_event(kind: str, **kw) -> None:
+ EVENT_LOG.parent.mkdir(parents=True, exist_ok=True)
+ with open(EVENT_LOG, "a") as f:
+ f.write(json.dumps({"t": time.strftime("%Y-%m-%dT%H:%M:%S"), "event": kind, **kw}) + "\n")
+
+
+class Actuator:
+ def __init__(self, policy: Policy, dry_run: bool = False):
+ self.p, self.dry = policy, dry_run
+ self.stopped_outside: set[int] = set()
+
+ def apply(self, act: tuple, s: Sample) -> None:
+ kind = act[0]
+ log_event(kind, detail=act[1:], cpu=round(s.cpu_pct, 1), mem_avail_gb=round(s.mem_avail_gb, 1),
+ dstate=s.dstate, dry_run=self.dry)
+ if self.dry:
+ return
+ if kind == "freeze":
+ _systemctl("freeze", SLICE)
+ elif kind == "thaw":
+ _systemctl("thaw", SLICE)
+ elif kind == "set_cpu":
+ _systemctl("set-property", "--runtime", SLICE, f"CPUQuota={int(act[1] * 100)}%")
+ elif kind == "kill_slice":
+ _systemctl("kill", "--signal=SIGKILL", SLICE)
+ _signal(self.stopped_outside, signal.SIGKILL)
+ elif kind == "term_largest":
+ cands = [pid for pid in set(slice_pids()) | set(self.stopped_outside) if _comm(pid) not in ALLOWLIST]
+ if cands:
+ victim = max(cands, key=_rss_gb)
+ log_event("term_victim", pid=victim, comm=_comm(victim), rss_gb=round(_rss_gb(victim), 1))
+ _signal([victim], signal.SIGCONT); _signal([victim], signal.SIGTERM)
+ elif kind == "stop_outside":
+ pids = own_heavy_outside()
+ if pids:
+ log_event("sigstop_outside", pids=pids, comms=[_comm(p) for p in pids])
+ self.stopped_outside |= set(_signal(pids, signal.SIGSTOP))
+ elif kind == "cont_outside":
+ _signal(self.stopped_outside, signal.SIGCONT)
+ self.stopped_outside.clear()
+
+
+def watch(policy: Policy = Policy(), interval: float = 3.0, dry_run: bool = False, max_iters: Optional[int] = None) -> None:
+ st, act = State(), Actuator(policy, dry_run)
+ log_event("watchdog_start", policy=dataclasses.asdict(policy), dry_run=dry_run)
+ last_status = 0.0
+ i = 0
+ while max_iters is None or i < max_iters:
+ i += 1
+ s = take_sample(interval)
+ if STOP_FILE.exists():
+ act.apply(("kill_slice", f"kill switch {STOP_FILE}"), s)
+ STOP_FILE.unlink(missing_ok=True)
+ for a in decide(s, st, policy):
+ act.apply(a, s)
+ if s.t - last_status >= 60:
+ last_status = s.t
+ log_event("status", cpu=round(s.cpu_pct, 1), mem_avail_gb=round(s.mem_avail_gb, 1), psi_mem_full=s.psi_mem_full,
+ dstate=s.dstate, gpu_temps=s.gpu_temps, home_free_gb=round(s.home_free_gb, 2),
+ frozen=st.frozen, slice_cores=st.slice_cores, slice_pids=len(slice_pids()))
diff --git a/code/align/utils/supervisor.py b/code/align/utils/supervisor.py
new file mode 100644
index 0000000000000000000000000000000000000000..abc72b7f8e13e7eec74f0b7000527927e09e33d8
--- /dev/null
+++ b/code/align/utils/supervisor.py
@@ -0,0 +1,573 @@
+"""Bounded-retry launch supervisor (G0 / roadmap P0-11). STDLIB ONLY.
+
+Why this exists. A 4-rank job on this shared, swapless box dies for reasons
+that are sometimes transient (an NCCL timeout behind someone else's I/O storm,
+a host seizure) and sometimes terminal (a gradient explosion, a poisoned
+checkpoint). A human re-typing `torchrun` after each is slow; a bash
+`while true` loop is how a deterministic crash gets relaunched forever, burning
+GPU-hours and, worse, re-launching onto a host that is still wedged -- the
+2026-08-12 shape. This supervisor automates ONLY the safe relaunch:
+
+ retry iff exit code != 0
+ AND exit code is not a HALT/terminal code (see below)
+ AND the child was not signal-stopped (143/130/129, or killed by
+ SIGTERM/SIGINT/SIGHUP) unless --retry-on-stop: a stop somebody
+ ASKED FOR is a decision, not a fault
+ AND retries used < max_retries (default 2)
+ AND progress was made DURING the failed attempt
+ AND preflight_host_check() passes right now
+
+Every clause is a way a blind retry loop goes wrong:
+
+ * HALT codes. A tripwire HALT (gradient explosion, non-finite streak) is a
+ verdict about the RUN, not about the host. Relaunching resumes from the
+ last checkpoint and walks straight back into the same blow-up. Never retry.
+ * Progress. "Progress" means BOTH `history.jsonl` mtime advanced (the trainer
+ fsyncs a record per log step, so it moved only if training logged) AND the
+ latest `checkpoints/micro_NNNNNNNN` number increased (so a resume would
+ start from further along than the attempt did). History alone can advance
+ on a run that crashes every 50 steps with no checkpoint in between -- each
+ retry would redo the same 50 steps forever. Checkpoint alone can be a stale
+ value from a previous run. Both together mean "a resume is strictly ahead".
+ This is deliberately conservative: a crash before the first checkpoint of
+ a fresh run stops the supervisor rather than looping on a config defect.
+ * Preflight. Relaunching onto a host that is still at 2GB free / load 619 is
+ the crash we are preventing; the check is the same one the job runs itself.
+ * No concurrency. The previous child's whole process group (torchrun + its
+ workers) is SIGKILLed and confirmed empty before the next launch, so a
+ straggler rank can never overlap a relaunch. The supervisor never holds
+ the heavy-job flock itself; the child takes it, and a leaked rank holding
+ it would make the relaunch fail loudly rather than run two jobs.
+
+The child runs in its OWN session/process group (start_new_session) so that
+killing it cannot take the supervisor with it and so the supervisor can kill
+the whole tree. On SIGTERM/SIGINT/SIGHUP to the supervisor the group is
+SIGTERMed, given `grace_s`, then SIGKILLed: an orphaned torchrun that outlives
+its supervisor keeps GPUs and /dev/shm pinned with nobody watching.
+
+Attempts are appended (fsynced) to `/supervisor.jsonl`.
+
+STOPS ARE DRAINED, NOT KILLED (G3 audit U01-1 / U01-2). A coordinated stop
+needs up to one optimizer-step boundary (~68s at the P2 shape) plus a save
+(~3.3 min); torchrun SIGKILLs its workers 30s after a SIGTERM and `grace_s` is
+20s, so a SIGTERM-driven stop could never finish inside either. A trainer that
+can drain says so (`DRAIN_READY.json`, written when `train()` starts); the
+supervisor then asks it to stop through `STOP_REQUEST` -- a file polled at the
+boundary, no signal -- and waits up to `drain_s` before the SIGTERM->SIGKILL
+escalation. And because `torchrun` exits 1 whatever its worker did, a trainer
+that stopped on an operator's signal also leaves `STOP.json` (code 128+signum)
+so a `kill ` is read as the operator stop it is, not as a retryable
+crash. All three files carry their own `written_at`, like HALT.json.
+
+The exit codes (`align.exit_codes`) are the CONTRACT with the trainer, carried
+on TWO channels because `torchrun` collapses every worker's exit code to 1: the
+child's exit code, and `/HALT.json`, which a halting rank writes before
+it raises. A marker written after the attempt started is the verdict whatever
+the launcher said (`halt_marker` in the log). The progress rule remains the
+backstop for a halt that managed to write neither.
+"""
+
+from __future__ import annotations
+
+import json
+import os
+import re
+import signal
+import subprocess
+import threading
+import time
+from dataclasses import dataclass, field
+from typing import Callable, Optional, Sequence
+
+# Exit-code contract: defined once in `align.exit_codes` (the trainer imports
+# the same module, so neither side can drift) and re-exported here because the
+# supervisor's tests and CLI have always imported them from this module.
+from ..exit_codes import ( # noqa: E402
+ EXIT_NONFINITE_HALT,
+ EXIT_PREFLIGHT_REFUSED,
+ EXIT_SUPERVISOR_GAVE_UP,
+ EXIT_TRIPWIRE_HALT,
+ EXIT_WALL_BUDGET,
+ NON_RETRYABLE_EXIT_CODES,
+ read_halt_marker,
+)
+
+#: Supervisor -> trainer: "stop at the next optimizer-step boundary and save".
+STOP_REQUEST_NAME = "STOP_REQUEST"
+#: Trainer -> supervisor: "I stopped because I was told to" (code 128+signum).
+STOP_MARKER_NAME = "STOP.json"
+#: Trainer -> supervisor: "I poll STOP_REQUEST; wait for me instead of killing me".
+DRAIN_READY_NAME = "DRAIN_READY.json"
+
+
+def write_run_marker(run_dir: str, name: str, **fields) -> bool:
+ """Atomically (tmp + fsync + rename) write `/` as JSON with a
+ `written_at` stamp. Best-effort like `write_halt_marker`: False on OSError,
+ because a stop must proceed even if its marker cannot be written."""
+ path = os.path.join(run_dir, name)
+ tmp = path + f".tmp{os.getpid()}"
+ try:
+ os.makedirs(run_dir, exist_ok=True)
+ with open(tmp, "w") as fh:
+ json.dump({**fields, "written_at": time.time()}, fh)
+ fh.flush()
+ os.fsync(fh.fileno())
+ os.replace(tmp, path)
+ except OSError:
+ try:
+ os.unlink(tmp)
+ except OSError:
+ pass
+ return False
+ return True
+
+
+def read_run_marker(run_dir: str, name: str, since: float = 0.0) -> Optional[dict]:
+ """The marker `name` if it exists AND its own `written_at` is >= `since`
+ (the file's contents, not its mtime, for the reason `read_halt_marker`
+ gives). Unreadable or malformed -> None."""
+ try:
+ with open(os.path.join(run_dir, name)) as fh:
+ rec = json.load(fh)
+ if not isinstance(rec, dict) or float(rec["written_at"]) < since:
+ return None
+ return rec
+ except (OSError, ValueError, KeyError, TypeError):
+ return None
+
+
+def clear_run_marker(run_dir: str, name: str) -> None:
+ try:
+ os.unlink(os.path.join(run_dir, name))
+ except OSError:
+ pass
+
+
+def read_stop_marker(run_dir: str, since: float = 0.0) -> Optional[dict]:
+ """STOP.json, only if its code is a stop-by-signal code (128+n)."""
+ rec = read_run_marker(run_dir, STOP_MARKER_NAME, since)
+ try:
+ if rec is not None and int(rec["code"]) in OPERATOR_STOP_CODES:
+ return rec
+ except (KeyError, TypeError, ValueError):
+ pass
+ return None
+
+
+#: Child statuses that mean "something SIGNALLED this run to stop", in both
+#: spellings: the shell convention 128+n (what the trainer returns after its
+#: coordinated checkpoint-and-stop, `Trainer.stop_exit_code`) and Popen's
+#: negative returncode (a launcher killed by the signal itself). Deliberately
+#: only the three signals an operator or service manager sends to ask for a
+#: stop: SIGKILL (137/-9, the OOM killer) and SIGSEGV/SIGABRT are CRASHES and
+#: stay retryable under the normal progress+preflight rules.
+OPERATOR_STOP_SIGNALS = (signal.SIGTERM, signal.SIGINT, signal.SIGHUP)
+OPERATOR_STOP_CODES = frozenset(
+ [128 + int(s) for s in OPERATOR_STOP_SIGNALS] + [-int(s) for s in OPERATOR_STOP_SIGNALS]
+)
+
+SUPERVISOR_LOG_NAME = "supervisor.jsonl"
+HISTORY_NAME = "history.jsonl" # mirrors align.utils.tracking.HISTORY_NAME
+_CKPT_RE = re.compile(r"^micro_(\d+)$") # `.partial` dirs never match
+
+
+def latest_checkpoint_micro_step(
+ run_dir: str, validator: Optional[Callable[[str], Optional[str]]] = None
+) -> int:
+ """Highest COMPLETE `checkpoints/micro_NNNNNNNN` number, or -1 if none.
+
+ Parsed from directory names (the trainer's own naming) rather than by
+ importing the Trainer: this module must stay stdlib-only and importable
+ before torch. `.partial` staging dirs are excluded -- they masquerade as
+ resumable and must not count as progress.
+
+ `validator(path) -> None | reason` is the completeness check. The CLI wires
+ `align.train.trainer.checkpoint_problem` into it (that function reads
+ headers and the resume manifest, hence needs torch, which this module may
+ not import). Without it a directory that exists but is truncated -- the
+ host seized between the publishing rename and the data reaching disk --
+ would count as progress and the supervisor would relaunch onto a
+ checkpoint `latest_checkpoint` will refuse. A validator that RAISES is
+ treated as "invalid": unknown is not progress."""
+ root = os.path.join(run_dir, "checkpoints")
+ best = -1
+ try:
+ entries = os.listdir(root)
+ except OSError:
+ return -1
+ for d in entries:
+ m = _CKPT_RE.match(d)
+ path = os.path.join(root, d)
+ if not (m and os.path.isdir(path)):
+ continue
+ n = int(m.group(1))
+ if n <= best:
+ continue # cannot raise the max; skip the (expensive) validation
+ if validator is not None:
+ try:
+ if validator(path) is not None:
+ continue
+ except Exception: # noqa: BLE001
+ continue
+ best = n
+ return best
+
+
+def history_mtime_ns(run_dir: str) -> int:
+ """mtime of history.jsonl in ns, or -1 when absent."""
+ try:
+ return os.stat(os.path.join(run_dir, HISTORY_NAME)).st_mtime_ns
+ except OSError:
+ return -1
+
+
+class _SignalFlag:
+ """A stop flag that is SAFE TO SET FROM A SIGNAL HANDLER.
+
+ THE HANDLER-DEADLOCK DEFECT. `threading.Event.set()` acquires the event's
+ internal (non-reentrant) lock. CPython runs a Python signal handler on the
+ main thread between two bytecodes of whatever it was executing; if that was
+ `Event.wait()` -- the supervisor spends its entire backoff there -- holding
+ that very lock, the handler's `set()` blocks on a lock its own thread owns
+ and the supervisor hangs, ignoring SIGTERM, with the whole job pinned.
+ The window is a few bytecodes wide, which is why it would show up once in a
+ blue moon on a night nobody is watching.
+
+ A plain attribute store takes no lock. `wait` therefore polls in short
+ slices instead of blocking on a condition variable; the slice bounds the
+ reaction time to a signal that lands mid-sleep.
+ """
+
+ _SLICE_S = 0.1
+
+ def __init__(self) -> None:
+ self._flag = False
+
+ def set(self) -> None:
+ self._flag = True
+
+ def is_set(self) -> bool:
+ return self._flag
+
+ def wait(self, timeout: float) -> bool:
+ deadline = time.monotonic() + timeout
+ while not self._flag:
+ left = deadline - time.monotonic()
+ if left <= 0:
+ return False
+ time.sleep(min(self._SLICE_S, left))
+ return True
+
+
+@dataclass(frozen=True)
+class Snapshot:
+ history_mtime_ns: int
+ ckpt_micro_step: int
+
+ @classmethod
+ def take(cls, run_dir: str, validator=None) -> "Snapshot":
+ return cls(history_mtime_ns(run_dir), latest_checkpoint_micro_step(run_dir, validator))
+
+
+def made_progress(before: Snapshot, after: Snapshot) -> bool:
+ """BOTH signals must advance (see module docstring)."""
+ return (
+ after.history_mtime_ns > before.history_mtime_ns
+ and after.ckpt_micro_step > before.ckpt_micro_step
+ )
+
+
+@dataclass
+class SupervisorResult:
+ exit_code: int # what the supervisor process should exit with
+ reason: str # "success" | "retries_exhausted" | "halt_exit_code" | ...
+ attempts: int
+ child_codes: list[int] = field(default_factory=list)
+
+
+class Supervisor:
+ """Run `cmd` as a child process group with bounded, gated retries."""
+
+ def __init__(
+ self,
+ cmd: Sequence[str],
+ run_dir: str,
+ *,
+ max_retries: int = 2,
+ backoff_s: float = 30.0,
+ grace_s: float = 20.0,
+ preflight: Optional[Callable[[], object]] = None,
+ sleep: Optional[Callable[[float], None]] = None,
+ cwd: Optional[str] = None,
+ env: Optional[dict] = None,
+ checkpoint_validator: Optional[Callable[[str], Optional[str]]] = None,
+ retry_on_stop: bool = False,
+ drain_s: float = 900.0,
+ ):
+ if max_retries < 0:
+ raise ValueError(f"max_retries must be >= 0, got {max_retries}")
+ if not cmd:
+ raise ValueError("empty command")
+ self.cmd = list(cmd)
+ self.run_dir = run_dir
+ self.max_retries = int(max_retries)
+ self.backoff_s = float(backoff_s)
+ self.grace_s = float(grace_s)
+ self._preflight = preflight
+ self._stop = _SignalFlag()
+ self._sleep = sleep or (lambda s: self._stop.wait(s))
+ self.cwd = cwd
+ self.env = env
+ self._validator = checkpoint_validator
+ #: THE RELAUNCH-AFTER-OPERATOR-STOP DEFECT (H2 review, 2026-10-13). An
+ #: operator who `kill`s the trainer (or torchrun) to stop a run gets a
+ #: checkpointed 143 -- which is not a HALT code, left a checkpoint behind
+ #: (so "progress"), and therefore passed every retry clause: the
+ #: supervisor relaunched exactly what the human had just stopped. A stop
+ #: that was ASKED FOR is a decision, not a fault; it ends the supervisor
+ #: unless the caller opts in (`--retry-on-stop`, e.g. a service manager
+ #: that SIGTERMs for host maintenance and wants the job back afterwards).
+ self.retry_on_stop = bool(retry_on_stop)
+ #: How long a SIGNAL to the supervisor waits for a drain-capable trainer
+ #: to checkpoint and exit before SIGTERM/SIGKILL (see module docstring:
+ #: one boundary + one save, with margin). 0 disables the drain.
+ self.drain_s = float(drain_s)
+ self._drain_deadline: Optional[float] = None # monotonic; set while a drain is pending
+ self._drain_logged = False
+ self._t_launch = 0.0
+ self._proc: Optional[subprocess.Popen] = None
+ self._term_signal: Optional[int] = None
+ os.makedirs(run_dir, exist_ok=True)
+ self.log_path = os.path.join(run_dir, SUPERVISOR_LOG_NAME)
+
+ # -- logging ------------------------------------------------------------
+ def _log(self, **rec) -> None:
+ rec["t"] = time.time()
+ line = json.dumps(rec, sort_keys=True) + "\n"
+ with open(self.log_path, "a") as fh:
+ fh.write(line)
+ fh.flush()
+ os.fsync(fh.fileno())
+
+ # -- preflight ----------------------------------------------------------
+ def _preflight_ok(self) -> tuple[bool, str]:
+ fn = self._preflight
+ if fn is None:
+ from .guard import preflight_host_check # stdlib-only module
+
+ fn = preflight_host_check
+ try:
+ fn()
+ except Exception as e: # preflight raises RuntimeError on refusal
+ return False, str(e)
+ return True, ""
+
+ # -- process-group control ---------------------------------------------
+ def _kill_group(self, pgid: int, first: int = signal.SIGTERM) -> bool:
+ """SIGTERM -> grace -> SIGKILL the group; True once it is empty."""
+ for sig, wait_s in ((first, self.grace_s), (signal.SIGKILL, 10.0)):
+ try:
+ os.killpg(pgid, sig)
+ except ProcessLookupError:
+ return True
+ deadline = time.monotonic() + wait_s
+ while time.monotonic() < deadline:
+ if self._proc is not None:
+ self._proc.poll() # reap the leader so killpg(0) sees truth
+ if not _group_alive(pgid):
+ return True
+ time.sleep(0.05)
+ return not _group_alive(pgid)
+
+ def _try_drain(self) -> bool:
+ """Ask a drain-capable trainer to stop itself. Runs in the signal
+ handler: file I/O only, no locks."""
+ if self.drain_s <= 0:
+ return False
+ if read_run_marker(self.run_dir, DRAIN_READY_NAME, self._t_launch) is None:
+ return False
+ if not write_run_marker(self.run_dir, STOP_REQUEST_NAME, by="supervisor"):
+ return False
+ self._drain_deadline = time.monotonic() + self.drain_s
+ return True
+
+ def _on_signal(self, signum, _frame) -> None:
+ first = self._term_signal is None
+ self._term_signal = signum
+ self._stop.set()
+ p = self._proc
+ if p is None or p.poll() is not None:
+ return
+ if first and self._try_drain():
+ return # the wait loop escalates after drain_s; a second signal does not wait
+ self._drain_deadline = None
+ try:
+ os.killpg(p.pid, signal.SIGTERM) # the wait loop escalates
+ except ProcessLookupError:
+ pass
+
+ def _wait_child(self, p: subprocess.Popen) -> int:
+ """Wait for the leader; if we were told to terminate and it ignores
+ SIGTERM, escalate to SIGKILL after grace_s rather than hang forever.
+ A pending drain defers the SIGTERM until its deadline."""
+ t_term: Optional[float] = None
+ while True:
+ try:
+ return p.wait(timeout=0.2)
+ except subprocess.TimeoutExpired:
+ if self._term_signal is not None:
+ dl = self._drain_deadline
+ if dl is not None:
+ if not self._drain_logged:
+ self._drain_logged = True
+ self._log(event="drain_requested", drain_s=self.drain_s)
+ if time.monotonic() < dl:
+ continue
+ self._drain_deadline = None
+ self._log(event="drain_timeout", drain_s=self.drain_s)
+ try:
+ os.killpg(p.pid, signal.SIGTERM)
+ except ProcessLookupError:
+ pass
+ t_term = t_term or time.monotonic()
+ if time.monotonic() - t_term > self.grace_s:
+ try:
+ os.killpg(p.pid, signal.SIGKILL)
+ except ProcessLookupError:
+ pass
+
+ # -- main loop ----------------------------------------------------------
+ def run(self, install_signal_handlers: bool = True) -> SupervisorResult:
+ old = {}
+ if install_signal_handlers and threading.current_thread() is threading.main_thread():
+ for s in (signal.SIGTERM, signal.SIGINT, signal.SIGHUP):
+ old[s] = signal.signal(s, self._on_signal)
+ try:
+ return self._run()
+ finally:
+ p = self._proc
+ if p is not None: # never leave a group behind, whatever happened
+ self._kill_group(p.pid, signal.SIGKILL)
+ for s, h in old.items():
+ signal.signal(s, h)
+
+ def _result(self, code, reason, attempt, codes) -> SupervisorResult:
+ self._log(event="stop", reason=reason, exit_code=code, attempts=attempt)
+ return SupervisorResult(code, reason, attempt, codes)
+
+ def _run(self) -> SupervisorResult:
+ codes: list[int] = []
+ attempt = 0
+ while True:
+ attempt += 1
+ before = Snapshot.take(self.run_dir, self._validator)
+ t_launch = time.time()
+ self._log(event="launch", attempt=attempt, cmd=self.cmd,
+ history_mtime_ns=before.history_mtime_ns,
+ ckpt_micro_step=before.ckpt_micro_step)
+ if self._stop.is_set(): # signalled during backoff
+ return self._result(128 + (self._term_signal or signal.SIGTERM),
+ "supervisor_terminated", attempt - 1, codes)
+ # A RETRY is a resume by definition: the attempt being retried
+ # made progress (that is the retry precondition), so relaunching it
+ # fresh would discard that progress -- and stage scripts that rotate
+ # stale checkpoints on a fresh launch (scripts/dpo.py) would move it
+ # out of the way. The child reads ALIGN_RESUME=1 as "resume from the
+ # newest valid checkpoint instead of starting a fresh arm".
+ child_env = dict(os.environ if self.env is None else self.env)
+ if attempt > 1:
+ child_env["ALIGN_RESUME"] = "1"
+ # A request/ready file from an earlier attempt must not drain or
+ # condemn this one (the readers also compare written_at to launch).
+ clear_run_marker(self.run_dir, STOP_REQUEST_NAME)
+ clear_run_marker(self.run_dir, DRAIN_READY_NAME)
+ self._t_launch = t_launch
+ self._drain_deadline = None
+ self._drain_logged = False
+ self._proc = subprocess.Popen(
+ self.cmd, cwd=self.cwd, env=child_env, start_new_session=True
+ )
+ pgid = self._proc.pid
+ if self._term_signal is not None:
+ # THE LOST-SIGNAL WINDOW. A signal that lands after the stop
+ # check above but before `self._proc` is assigned finds no
+ # child for `_on_signal` to forward to; the flag is set, the
+ # child is launched anyway, and `_wait_child` then only
+ # escalates to SIGKILL after grace_s -- the job never gets the
+ # SIGTERM that makes it checkpoint and stop. Forward it now.
+ try:
+ os.killpg(pgid, signal.SIGTERM)
+ except ProcessLookupError:
+ pass
+ rc = self._wait_child(self._proc)
+ # The leader exiting does not mean its workers did. Clear the
+ # whole group BEFORE deciding anything: a straggler rank must not
+ # overlap a relaunch (or keep GPUs pinned after we give up).
+ group_clear = self._kill_group(
+ pgid, signal.SIGTERM if self._term_signal is not None else signal.SIGKILL
+ )
+ self._proc = None
+ clear_run_marker(self.run_dir, STOP_REQUEST_NAME)
+ codes.append(rc)
+ after = Snapshot.take(self.run_dir, self._validator)
+ progressed = made_progress(before, after)
+ self._log(event="exit", attempt=attempt, child_exit=rc, progressed=progressed,
+ group_clear=group_clear,
+ history_mtime_ns=after.history_mtime_ns,
+ ckpt_micro_step=after.ckpt_micro_step)
+
+ if self._term_signal is not None:
+ return self._result(128 + self._term_signal, "supervisor_terminated",
+ attempt, codes)
+ if rc == 0:
+ return self._result(0, "success", attempt, codes)
+ if rc in NON_RETRYABLE_EXIT_CODES:
+ return self._result(rc, "halt_exit_code", attempt, codes)
+ # torchrun reports EVERY worker failure as exit 1, so the code
+ # above only fires for a launcher-less run. The marker is how a
+ # halting rank's verdict survives the launcher.
+ marker = read_halt_marker(self.run_dir, since=t_launch)
+ if marker is not None:
+ self._log(event="halt_marker", attempt=attempt, code=marker["code"],
+ reason=marker.get("reason"))
+ return self._result(int(marker["code"]), "halt_marker", attempt, codes)
+ # torchrun also collapses a worker's checkpointed 143 into exit 1;
+ # the trainer's STOP.json is how that stop survives the launcher.
+ stop_marker = read_stop_marker(self.run_dir, since=t_launch)
+ if (rc in OPERATOR_STOP_CODES or stop_marker is not None) and not self.retry_on_stop:
+ code = rc if rc in OPERATOR_STOP_CODES else int(stop_marker["code"])
+ self._log(event="operator_stop", attempt=attempt, child_exit=rc,
+ marker=stop_marker is not None)
+ return self._result(code if code > 0 else 128 - code, "operator_stop", attempt, codes)
+ if not group_clear:
+ return self._result(EXIT_SUPERVISOR_GAVE_UP, "process_group_not_dead",
+ attempt, codes)
+ if attempt - 1 >= self.max_retries:
+ return self._result(rc if rc > 0 else EXIT_SUPERVISOR_GAVE_UP,
+ "retries_exhausted", attempt, codes)
+ if not progressed:
+ return self._result(rc if rc > 0 else EXIT_SUPERVISOR_GAVE_UP,
+ "no_progress", attempt, codes)
+ delay = self.backoff_s * (2 ** (attempt - 1))
+ self._log(event="backoff", attempt=attempt, seconds=delay)
+ self._sleep(delay)
+ if self._stop.is_set():
+ return self._result(128 + (self._term_signal or signal.SIGTERM),
+ "supervisor_terminated", attempt, codes)
+ # Preflight AFTER the backoff: the point of waiting is to let the
+ # host quiesce, and a pre-wait reading is stale by then.
+ ok, why = self._preflight_ok()
+ if not ok:
+ self._log(event="preflight_failed", attempt=attempt, detail=why)
+ return self._result(rc if rc > 0 else EXIT_SUPERVISOR_GAVE_UP,
+ "preflight_failed", attempt, codes)
+
+
+def _group_alive(pgid: int) -> bool:
+ try:
+ os.killpg(pgid, 0)
+ except ProcessLookupError:
+ return False
+ except PermissionError: # exists, not ours: cannot be our child
+ return False
+ return True
diff --git a/code/align/utils/tracking.py b/code/align/utils/tracking.py
new file mode 100644
index 0000000000000000000000000000000000000000..58a0c9324081fedf3377562091ba63a59dd65ac0
--- /dev/null
+++ b/code/align/utils/tracking.py
@@ -0,0 +1,159 @@
+"""Experiment tracking: a dependency-free, kill-surviving run record.
+
+(wandb was removed under docs/DEPENDENCY_POLICY.md. Nothing replaces its UI;
+the on-disk record below is the single source of truth.)
+
+Files under `/`:
+ * `history.jsonl` -- append-only, one JSON object per log step, flushed AND
+ fsync'd every write. The host can seize at any moment (it has); this file
+ loses at most the one line being written, and post-run analysis loads THIS
+ full history -- never the sampled monitor lines (three predecessor
+ analyses were retracted for interpreting every-5th-iteration monitor
+ samples as data). `read_history` tolerates a torn final line for that
+ reason.
+ * `config.json` -- written by `log_config` via write-to-`.partial` +
+ `os.replace`, so a reader never sees a half-written config.
+
+LIVENESS: house rules judge "is the run progressing" by the history file's
+mtime (`stat -c %y`), NOT by PIDs -- a wedged NCCL job keeps its processes. So
+every `log` must touch history.jsonl; the fsync after each append guarantees
+the mtime reaches the disk.
+
+Contract, exercised for real by the Trainer:
+ * missing mandatory metric keys RAISE;
+ * None RAISES;
+ * undefined statistics are logged as NaN -- never 0, never omitted.
+"""
+
+from __future__ import annotations
+
+import json
+import math
+import os
+import sys
+from typing import Iterable, Mapping
+
+HISTORY_NAME = "history.jsonl"
+CONFIG_NAME = "config.json"
+
+
+def read_history(run_dir: str) -> list[dict]:
+ """All records of `/history.jsonl`, in order. Missing file -> [].
+
+ A kill mid-append can leave one truncated trailing line; that line (and
+ only a trailing one) is dropped. A corrupt line in the MIDDLE is real
+ damage and raises rather than being skipped.
+ """
+ path = os.path.join(run_dir, HISTORY_NAME)
+ if not os.path.exists(path):
+ return []
+ with open(path, "r") as fh:
+ lines = fh.read().split("\n")
+ out: list[dict] = []
+ # split leaves a final "" when the file ends in a newline.
+ last = max((i for i, l in enumerate(lines) if l.strip()), default=-1)
+ for i, line in enumerate(lines):
+ if not line.strip():
+ continue
+ try:
+ out.append(json.loads(line))
+ except json.JSONDecodeError:
+ if i == last:
+ continue # torn tail from a kill mid-write
+ raise
+ return out
+
+
+def _repair_torn_tail(path: str) -> None:
+ """Make an existing history file end on a record boundary before append.
+
+ A kill mid-append leaves a final line with no newline. Appending to it
+ glues the next record onto the fragment and `read_history` then raises
+ (the glued line is no longer the last one). A tail that is a complete JSON
+ record only missing its newline is kept and terminated; an unparseable
+ fragment is truncated away (it was never a whole record).
+ """
+ if not os.path.exists(path):
+ return
+ with open(path, "rb+") as fh:
+ data = fh.read()
+ if not data or data.endswith(b"\n"):
+ return
+ cut = data.rfind(b"\n") + 1 # 0 when the file is a single torn line
+ try:
+ json.loads(data[cut:].decode("utf-8"))
+ fh.seek(0, os.SEEK_END)
+ fh.write(b"\n")
+ except (json.JSONDecodeError, UnicodeDecodeError):
+ fh.truncate(cut)
+ fh.flush()
+ os.fsync(fh.fileno())
+
+
+class Tracker:
+ def __init__(
+ self,
+ run_dir: str,
+ stage: str,
+ mandatory_keys: Iterable[str] = (),
+ run_name: str = "dev",
+ stdout_every: int = 0,
+ ) -> None:
+ self.run_dir = run_dir
+ self.stage = stage
+ self.run_name = run_name
+ self.mandatory_keys = tuple(mandatory_keys)
+ self.stdout_every = int(stdout_every)
+ os.makedirs(run_dir, exist_ok=True)
+ self.history_path = os.path.join(run_dir, HISTORY_NAME)
+ self.config_path = os.path.join(run_dir, CONFIG_NAME)
+ _repair_torn_tail(self.history_path)
+ self._fh = open(self.history_path, "a")
+ self._n = 0
+
+ def log_config(self, config: Mapping) -> None:
+ """Write config.json atomically (.partial + os.replace)."""
+ tmp = self.config_path + ".partial"
+ with open(tmp, "w") as fh:
+ json.dump(dict(config), fh, indent=2, sort_keys=True, default=str)
+ fh.flush()
+ os.fsync(fh.fileno())
+ os.replace(tmp, self.config_path)
+
+ def log(self, step: int, metrics: Mapping | None = None, **kw) -> None:
+ """Append one record. Metrics come as a mapping, kwargs, or both."""
+ metrics = {**(dict(metrics) if metrics else {}), **kw}
+ missing = [k for k in self.mandatory_keys if k not in metrics]
+ if missing:
+ raise RuntimeError(
+ f"stage {self.stage!r} step {step}: mandatory metrics missing "
+ f"{missing}. Undefined statistics must be logged as "
+ "float('nan') explicitly -- omission is how silent failures "
+ "start."
+ )
+ nones = [k for k, v in metrics.items() if v is None]
+ if nones:
+ raise RuntimeError(
+ f"stage {self.stage!r} step {step}: metrics {nones} are None. "
+ "Use float('nan') for undefined values -- None serializes to "
+ "null and downstream tooling coerces it to 0."
+ )
+ record = {"step": step, "stage": self.stage, **metrics}
+ self._fh.write(json.dumps(record) + "\n")
+ self._fh.flush()
+ os.fsync(self._fh.fileno())
+ self._n += 1
+ if self.stdout_every > 0 and (self._n % self.stdout_every == 0):
+ body = " ".join(
+ f"{k}={v:.4g}" if isinstance(v, float) and math.isfinite(v) else f"{k}={v}"
+ for k, v in metrics.items()
+ )
+ print(f"[{self.stage}] step {step} {body}", file=sys.stdout, flush=True)
+
+ def finish(self) -> None:
+ if not self._fh.closed:
+ self._fh.flush()
+ os.fsync(self._fh.fileno())
+ self._fh.close()
+
+ close = finish # legacy name used by callers
diff --git a/code/align/verify/__init__.py b/code/align/verify/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..81f7ef556ed280b03be0c326b7ab4249728a11be
--- /dev/null
+++ b/code/align/verify/__init__.py
@@ -0,0 +1,48 @@
+"""`align.verify`: answer extraction, normalisation and equivalence.
+
+The in-repo replacement for `math_verify` + `sympy` (see docs/DEPENDENCY_POLICY.md).
+Pure stdlib. Ceiling-tested: `tests/test_verify_ceiling.py` scores a gold-vs-gold
+set at 100/100 (the house rule: ceiling-test the grader before trusting any
+accuracy -- a perfect policy once scored 0/100) and, under the oracle marker,
+measures agreement with the legacy engine.
+"""
+
+from __future__ import annotations
+
+from .equiv import is_equiv, is_readable
+from .expr import VerifyError, VerifyTimeout, deadline
+from .extract import extract_boxed, extract_final_answer, extract_gold_answer, last_number
+from .normalize import normalize_answer
+
+__all__ = [
+ "VerifyError",
+ "VerifyTimeout",
+ "deadline",
+ "extract_boxed",
+ "extract_final_answer",
+ "extract_gold_answer",
+ "is_equiv",
+ "is_readable",
+ "last_number",
+ "normalize_answer",
+ "verify_text",
+]
+
+
+def verify_text(gold_text: str | None, pred_text: str | None) -> bool:
+ """Extract a final answer from each side, then test equivalence.
+
+ The gold goes through `extract_gold_answer`, NOT the prose extractor: a
+ bare `\\frac{1}{2}` is an answer, and the prose extractor's last-number
+ fallback would read it as "2". A prediction with no extractable answer
+ is False.
+ """
+ if pred_text is None:
+ return False
+ pred = extract_final_answer(pred_text)
+ if pred is None:
+ return False
+ gold = extract_gold_answer(gold_text)
+ if gold is None:
+ return False
+ return is_equiv(gold, pred)
diff --git a/code/align/verify/equiv.py b/code/align/verify/equiv.py
new file mode 100644
index 0000000000000000000000000000000000000000..572a2c7b2f1586a1f7208d0e960bc9a11c96fe52
--- /dev/null
+++ b/code/align/verify/equiv.py
@@ -0,0 +1,616 @@
+"""Answer equivalence: is `pred` the same mathematical answer as `gold`?
+
+Replaces `math_verify.verify`. Pure stdlib (`fractions`, `decimal`).
+
+DECISION LADDER (first rung that can decide, decides):
+
+ 1. identical after normalisation -> True
+ 2. TEXT answers (words, not math): casefolded alphanumeric key -> exact
+ 3. MATRICES (`\\begin{pmatrix}`): same shape, cell-wise equivalence
+ 4. UNIONS (`\\cup`) and BARE LISTS (`1,-2`, `A,B,E`, `x=1, x=2`):
+ ORDER-INSENSITIVE multiset match (a bare list is a solution set;
+ `\\pm` expands to two members)
+ 5. BRACKETED sequences: `(a,b)` `[a,b)` -- ORDER-SENSITIVE and
+ BRACKET-SENSITIVE (a tuple is not a set, `(1,2)` is not `[1,2]`);
+ `\\{a,b\\}` is a SET and is order-insensitive
+ 6. RELATIONS: equations compared as polynomials up to a nonzero scalar
+ (`y=2x+3` == `2x-y+3=0`, but `x+y-z+1=0` != `x+y-z-1=0`); inequalities
+ are put in `<`/`<=` orientation and compared sidewise
+ 7. SCALARS, in this order:
+ a. canonical polynomial equality (exact, symbolic)
+ b. numeric evaluation: Fractions compared EXACTLY; irrational values
+ compared at 60 digits with relative tolerance (see
+ `expr.values_close`); free variables are checked at several fixed
+ rational sample points and must agree at every valid one
+ c. canonical-string equality as the last resort for unparseable input
+
+PERCENT is a deliberate two-sided rule: `18\\%` may match `18` (a unit
+suffix) or `0.18` (its value), but `18` does not match `0.18\\%`. The
+predecessor pinned `18\\%` vs `18` as a known false negative; this engine
+closes it, so the sentinel test was retired with a recorded improvement.
+
+EVERY FAILURE FOLDS TO False, NEVER TO AN EXCEPTION, with one exception that
+is the point: `VerifyTimeout` (a BaseException) propagates so a deadline
+cannot be swallowed. Unexpected programming errors (TypeError, KeyError, ...)
+are also NOT swallowed here -- `_score_one` catches BaseException like the
+predecessor's worker, but the ceiling tests run `is_equiv` directly, so a bug
+fails a test instead of reading as a weak policy.
+"""
+
+from __future__ import annotations
+
+import decimal
+import hashlib
+import random
+import re
+from fractions import Fraction
+
+from .expr import (
+ Budget,
+ Parsed,
+ VerifyError,
+ evaluate,
+ free_vars,
+ is_bare_literal,
+ parse_expr,
+ poly_proportional,
+ to_poly,
+ values_close,
+)
+from .extract import match_brace
+from .normalize import normalize_answer, normalize_labeled, split_var_prefix, strip_var_prefix
+
+__all__ = ["is_equiv", "is_readable"]
+
+_MAX_LEN = 2_000
+_MAX_DEPTH = 6
+_SOFT = (VerifyError, ArithmeticError, ValueError, RecursionError, MemoryError, decimal.DecimalException)
+
+# Free-variable checks use RANDOMISED rational sample points, seeded from the
+# (sorted) expression pair so a verdict is deterministic and symmetric yet no
+# fixed grid exists for an adversary (or an accident: `sin(prod/1e9)` vanishing
+# at a fixed set, two variables 3 apart sharing a draw) to hit. EVERY variable
+# gets its own independent draw at every point. Point 0 is the all-zero point
+# and a share of draws are the special values {0, 1, -1}, so `x/x` vs `1`
+# (undefined at 0) is caught: an expression undefined where the other is
+# defined is NOT equivalent.
+_N_POINTS = 24
+_MIN_VALID = 8
+_SPECIAL = (Fraction(0), Fraction(1), Fraction(-1))
+
+
+def _sample_envs(names: list[str], g: str, p: str) -> list[dict[str, Fraction]]:
+ a, b = sorted((g, p))
+ seed = hashlib.sha256(f"{a}\x00{b}".encode()).digest()
+ rng = random.Random(seed)
+ envs: list[dict[str, Fraction]] = [{n: Fraction(0) for n in names}]
+ for _ in range(_N_POINTS - 1):
+ env = {}
+ for n in names:
+ if rng.random() < 0.12:
+ env[n] = rng.choice(_SPECIAL)
+ else:
+ env[n] = Fraction(rng.randint(-60, 60), rng.randint(1, 13))
+ envs.append(env)
+ return envs
+
+
+_KNOWN_WORDS = re.compile(
+ r"(?|=")
+
+
+# ---------------------------------------------------------------------------
+# String structure helpers
+# ---------------------------------------------------------------------------
+
+
+def _split_top(s: str, sep: str) -> list[str]:
+ """Split on `sep` at bracket depth 0. `sep` is "," or a macro like "\\cup"."""
+ parts: list[str] = []
+ depth = 0
+ last = 0
+ i = 0
+ n = len(s)
+ while i < n:
+ c = s[i]
+ if c == "\\":
+ if sep != "," and s.startswith(sep, i) and depth == 0:
+ end = i + len(sep)
+ if end >= n or not s[end].isalpha():
+ parts.append(s[last:i])
+ i = end
+ last = i
+ continue
+ nxt = s[i + 1 : i + 2]
+ if nxt in ("{", "}"):
+ depth += 1 if nxt == "{" else -1
+ i += 2
+ continue
+ if c in "([{":
+ depth += 1
+ elif c in ")]}":
+ depth -= 1
+ elif sep == "," and c == "," and depth == 0:
+ parts.append(s[last:i])
+ last = i + 1
+ i += 1
+ parts.append(s[last:])
+ return [p.strip() for p in parts]
+
+
+def _is_texty(s: str) -> bool:
+ rest = re.sub(r"\\[A-Za-z]+", " ", s)
+ rest = _KNOWN_WORDS.sub(" ", rest)
+ return re.search(r"[A-Za-z]{3,}", rest) is not None
+
+
+def _is_wordlike(s: str) -> bool:
+ """Letters and spaces only, at least two letters: `No`, `Monday`, `ab`."""
+ return re.fullmatch(r"[A-Za-z][A-Za-z ]*", s) is not None and sum(c.isalpha() for c in s) >= 2
+
+
+def _text_key(s: str) -> str:
+ return re.sub(r"[^0-9a-z]", "", s.casefold())
+
+
+def _canon_str(s: str) -> str:
+ return re.sub(r"\s+|\\left|\\right", "", s)
+
+
+def _expand_pm(elem: str) -> list[str]:
+ if "\\pm" not in elem and "\\mp" not in elem:
+ return [elem]
+ plus = elem.replace("\\pm", "+").replace("\\mp", "-")
+ minus = elem.replace("\\pm", "-").replace("\\mp", "+")
+ return [plus.lstrip("+").strip(), minus.strip()]
+
+
+def _list_labeled(s: str) -> list[tuple[str | None, str]]:
+ """Members of a bare list as (label, value). `x=1, y=2` keeps its labels:
+ they are part of the answer (`x=1, y=2` is not `x=2, y=1`)."""
+ out: list[tuple[str | None, str]] = []
+ for part in _split_top(s, ","):
+ if not part:
+ continue
+ label, rhs = split_var_prefix(part)
+ out.extend((label, e) for e in _expand_pm(rhs))
+ return out
+
+
+def _list_elems(s: str) -> list[str]:
+ return [e for _, e in _list_labeled(s)]
+
+
+def _match_labeled(G: list[tuple[str | None, str]], P: list[tuple[str | None, str]], B: Budget, depth: int) -> bool:
+ """Order-insensitive list match that respects `var=` labels.
+
+ * both sides labelled: a member matches only a member with the SAME label;
+ * one side labelled by a single variable (`x=1, x=2` vs `1, 2`): a solution
+ set of that variable, values compared as an unordered set;
+ * one side labelled by several variables vs a bare list: only the labelled
+ ORDER can be read as positional (`x=1, y=2` ~ `1, 2`, never `2, 1`);
+ * anything else partially labelled: not confirmable -> False.
+ """
+ if len(G) != len(P):
+ return False
+ lg = {l for l, _ in G}
+ lp = {l for l, _ in P}
+ g_lab, p_lab = lg != {None}, lp != {None}
+ if g_lab and p_lab:
+ if None in lg or None in lp:
+ return False
+ used = [False] * len(P)
+ for la, a in G:
+ a_n = normalize_answer(a)
+ for j, (lb, b) in enumerate(P):
+ if not used[j] and lb == la and _eq(a_n, normalize_answer(b), B, depth + 1):
+ used[j] = True
+ break
+ else:
+ return False
+ return True
+ if g_lab or p_lab:
+ lab, bare = (G, P) if g_lab else (P, G)
+ labels = [l for l, _ in lab]
+ if None in labels:
+ return False
+ if len(set(labels)) == 1:
+ return _match_unordered([e for _, e in G], [e for _, e in P], B, depth)
+ if len(set(labels)) != len(labels):
+ return False
+ return all(
+ _eq(normalize_answer(a), normalize_answer(b), B, depth + 1)
+ for (_, a), (_, b) in zip(G, P)
+ )
+ return _match_unordered([e for _, e in G], [e for _, e in P], B, depth)
+
+
+def _bracketed(s: str) -> tuple[str, str, list[str]] | None:
+ """`(a,b)` / `[a,b)` -> (open, close, [a,b]) when it is ONE bracket group
+ with a top-level comma inside; else None."""
+ if len(s) < 2 or s[0] not in "([" or s[-1] not in ")]":
+ return None
+ # the opener must close at the very end, not earlier: `(a)(b)` is not a tuple
+ depth = 0
+ for i, c in enumerate(s):
+ if c == "\\":
+ continue
+ if c in "([{":
+ depth += 1
+ elif c in ")]}":
+ depth -= 1
+ if depth == 0 and i != len(s) - 1:
+ return None
+ inner = s[1:-1]
+ elems = _split_top(inner, ",")
+ if len(elems) < 2:
+ return None
+ return s[0], s[-1], elems
+
+
+def _braced_set(s: str) -> list[str] | None:
+ if s.startswith("\\{") and s.endswith("\\}") and len(s) >= 4:
+ return [e for e in _split_top(s[2:-2], ",") if e != ""]
+ return None
+
+
+_MATRIX = re.compile(
+ r"^\\begin\{(?P[pbBvV]?matrix|array)\}(?:\{[^{}]*\})?(?P.*)\\end\{(?P=env)\}$",
+ re.DOTALL,
+)
+
+
+def _matrix(s: str) -> list[list[str]] | None:
+ m = _MATRIX.match(s.strip())
+ if not m:
+ return None
+ rows = [r for r in re.split(r"\\\\", m.group("body")) if r.strip()]
+ return [[c.strip() for c in r.split("&")] for r in rows]
+
+
+# ---------------------------------------------------------------------------
+# The ladder
+# ---------------------------------------------------------------------------
+
+
+def is_equiv(gold: str | None, pred: str | None) -> bool:
+ """True iff `pred` denotes the same answer as `gold` (module docstring)."""
+ if gold is None or pred is None:
+ return False
+ lg, g = normalize_labeled(gold)
+ lp, p = normalize_labeled(pred)
+ if not g or not p:
+ return False
+ if lg is not None and lp is not None and lg != lp:
+ return False # `x=1` is not `y=1`
+ if len(g) > _MAX_LEN or len(p) > _MAX_LEN:
+ return False
+ try:
+ return _eq(g, p, Budget(), 0)
+ except _SOFT:
+ return False
+
+
+def _eq(g: str, p: str, B: Budget, depth: int) -> bool:
+ B.tick()
+ if depth > _MAX_DEPTH:
+ return False
+ if g == p:
+ return True
+ g, p = g.strip(), p.strip()
+ if not g or not p:
+ return False
+ if g == p:
+ return True
+
+ # 3. matrices (before the text test: `pmatrix` is a macro, not a word)
+ mg, mp = _matrix(g), _matrix(p)
+ if mg is not None or mp is not None:
+ if mg is None or mp is None:
+ return False
+ if len(mg) != len(mp) or any(len(a) != len(b) for a, b in zip(mg, mp)):
+ return False
+ return all(
+ _eq(normalize_answer(x), normalize_answer(y), B, depth + 1)
+ for ra, rb in zip(mg, mp)
+ for x, y in zip(ra, rb)
+ )
+
+ # 2. text answers
+ if _is_wordlike(g) and _is_wordlike(p) and _text_key(g) == _text_key(p):
+ return True
+ tg, tp = _is_texty(g), _is_texty(p)
+ if tg or tp:
+ return tg and tp and _text_key(g) == _text_key(p)
+
+ # 4a. unions
+ ug, up = _split_top(g, "\\cup"), _split_top(p, "\\cup")
+ if len(ug) > 1 or len(up) > 1:
+ return _match_unordered(ug, up, B, depth)
+
+ # 4b. bare lists (order-insensitive); `\pm` expands
+ lg_, lp_ = _list_labeled(g), _list_labeled(p)
+ if len(lg_) > 1 or len(lp_) > 1:
+ return _match_labeled(lg_, lp_, B, depth)
+ if not lg_ or not lp_:
+ return False
+ g, p = lg_[0][1], lp_[0][1]
+ if g == p:
+ return True
+
+ # 5. bracketed sequences / sets
+ bg, bp = _bracketed(g), _bracketed(p)
+ sg, sp = _braced_set(g), _braced_set(p)
+ if (bg is None) != (bp is None):
+ # `x > 3` against `(3, \infty)`: a single-variable inequality IS an interval.
+ text, seq = (g, p) if bg is None else (p, g)
+ rel = _split_relation(text, _REL.findall(text))
+ interval = _inequality_as_interval(rel) if rel is not None else None
+ if interval is not None:
+ return _eq(normalize_answer(interval), normalize_answer(seq), B, depth + 1)
+ if bg or bp:
+ if not (bg and bp) or bg[0] != bp[0] or bg[1] != bp[1] or len(bg[2]) != len(bp[2]):
+ return False
+ return all(
+ _eq(normalize_answer(x), normalize_answer(y), B, depth + 1)
+ for x, y in zip(bg[2], bp[2])
+ )
+ if sg is not None or sp is not None:
+ if sg is None or sp is None:
+ # `\{1\}` against a bare `1`: a one-element set IS its element.
+ one, other = (sg, p) if sg is not None else (sp, g)
+ if len(one) == 1:
+ return _eq(normalize_answer(one[0]), normalize_answer(other), B, depth + 1)
+ return False
+ return _match_unordered(sg, sp, B, depth)
+
+ # 6. relations
+ rg, rp = _REL.findall(g), _REL.findall(p)
+ if rg or rp:
+ return _eq_relation(g, p, rg, rp, B, depth)
+
+ # 7. scalars
+ return _eq_scalar(g, p, B)
+
+
+def _match_unordered(A: list[str], Bl: list[str], B: Budget, depth: int) -> bool:
+ if len(A) != len(Bl):
+ return False
+ used = [False] * len(Bl)
+ for a in A:
+ a_n = normalize_answer(a)
+ for j, b in enumerate(Bl):
+ if used[j]:
+ continue
+ if _eq(a_n, normalize_answer(b), B, depth + 1):
+ used[j] = True
+ break
+ else:
+ return False
+ return True
+
+
+# -- relations ---------------------------------------------------------------
+
+
+def _split_relation(s: str, rels: list[str]) -> tuple[str, str, str] | None:
+ if len(rels) != 1:
+ return None
+ m = re.search(re.escape(rels[0]), s)
+ if not m:
+ return None
+ return s[: m.start()].strip(), rels[0], s[m.end() :].strip()
+
+
+def _orient(rel: tuple[str, str, str]) -> tuple[str, str, str]:
+ lhs, op, rhs = rel
+ if op == ">":
+ return rhs, "<", lhs
+ if op == "\\geq":
+ return rhs, "\\leq", lhs
+ return rel
+
+
+def _eq_relation(g: str, p: str, rg: list[str], rp: list[str], B: Budget, depth: int) -> bool:
+ relg, relp = _split_relation(g, rg), _split_relation(p, rp)
+ if relg is None and relp is None:
+ return _canon_str(g) == _canon_str(p)
+ if relg is None or relp is None:
+ # `y=2x+3` against a bare `2x+3`: compare the right-hand side.
+ eq, other = (relg, p) if relg is not None else (relp, g)
+ assert eq is not None
+ lhs, op, rhs = eq
+ if op == "=" and re.fullmatch(r"[A-Za-z]|\\[A-Za-z]+", lhs):
+ return _eq(normalize_answer(rhs), normalize_answer(other), B, depth + 1)
+ return False
+ (lg, og, rg_), (lp, op_, rp_) = relg, relp
+ if og in ("=", "\\neq") and op_ == og:
+ # Symmetric relation: polynomial proportionality, else sidewise.
+ try:
+ pg = _poly_of_diff(lg, rg_, B)
+ pp = _poly_of_diff(lp, rp_, B)
+ except VerifyError:
+ pg = pp = None
+ if pg is not None and pp is not None:
+ if poly_proportional(pg, pp):
+ return True
+ return (
+ _eq_scalar_safe(lg, lp, B) and _eq_scalar_safe(rg_, rp_, B)
+ ) or (_eq_scalar_safe(lg, rp_, B) and _eq_scalar_safe(rg_, lp, B))
+ og2, op2 = _orient(relg), _orient(relp)
+ if og2[1] != op2[1]:
+ return False
+ return _eq_scalar_safe(og2[0], op2[0], B) and _eq_scalar_safe(og2[2], op2[2], B)
+
+
+def _inequality_as_interval(rel: tuple[str, str, str]) -> str | None:
+ lhs, op, rhs = _orient_var_left(rel)
+ if op in ("=", "\\neq") or not re.fullmatch(r"[A-Za-z]", lhs) or re.search(r"[A-Za-z]", rhs.replace("pi", "").replace("\\infty", "")):
+ return None
+ return {
+ "<": f"(-\\infty, {rhs})",
+ "\\leq": f"(-\\infty, {rhs}]",
+ ">": f"({rhs}, \\infty)",
+ "\\geq": f"[{rhs}, \\infty)",
+ }.get(op)
+
+
+def _orient_var_left(rel: tuple[str, str, str]) -> tuple[str, str, str]:
+ """Put the single variable on the left: `3 < x` -> `x > 3`."""
+ lhs, op, rhs = rel
+ if re.fullmatch(r"[A-Za-z]", rhs) and not re.fullmatch(r"[A-Za-z]", lhs):
+ flip = {"<": ">", ">": "<", "\\leq": "\\geq", "\\geq": "\\leq"}
+ return rhs, flip.get(op, op), lhs
+ return rel
+
+
+def _poly_of_diff(lhs: str, rhs: str, B: Budget):
+ from .expr import _padd # internal helper, same package
+
+ a = _poly_or_none(lhs, B)
+ b = _poly_or_none(rhs, B)
+ if a is None or b is None:
+ return None
+ return _padd(a, b, -1)
+
+
+def _poly_or_none(s: str, B: Budget):
+ q = to_poly(parse_expr(normalize_answer(s), B).ast, B)
+ return q
+
+
+def _eq_scalar_safe(a: str, b: str, B: Budget) -> bool:
+ a_n, b_n = normalize_answer(a), normalize_answer(b)
+ if not a_n or not b_n:
+ return False
+ return _eq_scalar(a_n, b_n, B)
+
+
+# -- scalars -----------------------------------------------------------------
+
+
+def _percent_variants(s: str) -> list[str]:
+ if "%" not in s:
+ return [s]
+ return [s, s.replace("%", "").strip()]
+
+
+def _eq_scalar(g: str, p: str, B: Budget) -> bool:
+ for gv in _percent_variants(g):
+ for pv in _percent_variants(p):
+ if not gv or not pv:
+ continue
+ # `18%` -> `18` is allowed against a bare number, but if BOTH carry
+ # a percent sign the unstripped pair is the only honest reading.
+ if ("%" in g and "%" in p) and (gv != g or pv != p):
+ continue
+ if _eq_expr(gv, pv, B):
+ return True
+ return _canon_str(g) == _canon_str(p)
+
+
+def _eq_expr(g: str, p: str, B: Budget) -> bool:
+ try:
+ pg: Parsed = parse_expr(g, B)
+ pp: Parsed = parse_expr(p, B)
+ except VerifyError:
+ return False
+ # (a) exact symbolic: canonical polynomials
+ qg, qp = to_poly(pg.ast, B), to_poly(pp.ast, B)
+ if qg is not None and qp is not None and qg == qp:
+ return True
+ # (b) numeric / sample-point evaluation. A decimal literal licenses the
+ # rounding tolerance only when it IS the whole side (`0.714286`); a decimal
+ # inside arithmetic (`10000000+0.1-0.1`) is exact arithmetic, not rounding.
+ names = sorted(free_vars(pg.ast) | free_vars(pp.ast))
+ user_rounding = (pg.has_decimal and is_bare_literal(pg.ast)) or (
+ pp.has_decimal and is_bare_literal(pp.ast)
+ )
+ if not names:
+ try:
+ return values_close(
+ evaluate(pg.ast, {}, B), evaluate(pp.ast, {}, B), user_rounding=user_rounding
+ )
+ except VerifyError:
+ return False
+ valid = 0
+ for env in _sample_envs(names, g, p):
+ try:
+ vg = evaluate(pg.ast, env, B)
+ except VerifyError:
+ vg = None
+ try:
+ vp = evaluate(pp.ast, env, B)
+ except VerifyError:
+ vp = None
+ if vg is None and vp is None:
+ continue # both undefined here (domain): no information
+ if vg is None or vp is None:
+ return False # defined on one side only: NOT the same function
+ if not values_close(vg, vp, user_rounding=user_rounding):
+ return False
+ valid += 1
+ return valid >= _MIN_VALID
+
+
+# ---------------------------------------------------------------------------
+# Readability: "could the engine read this at all?"
+# ---------------------------------------------------------------------------
+
+
+def is_readable(answer: str | None) -> bool:
+ """True iff `answer` parses into something the engine can reason about.
+
+ This is NOT `is_equiv(a, a)`: identical normalised strings short-circuit
+ to True in `is_equiv` on purpose (a perfect policy that emits the gold
+ verbatim must score correct even when the notation is exotic -- the
+ gold-vs-gold ceiling). Readability is the separate question the corpus
+ instrumentation needs: "is a disagreement evidence about the answer, or
+ about the instrument's blind spot?"
+ """
+ n = normalize_answer(answer)
+ if not n or len(n) > _MAX_LEN:
+ return False
+ try:
+ return _readable(n, Budget(), 0)
+ except _SOFT:
+ return False
+
+
+def _readable(s: str, B: Budget, depth: int) -> bool:
+ B.tick()
+ if depth > _MAX_DEPTH or not s.strip():
+ return False
+ if _is_wordlike(s) or _is_texty(s):
+ return True
+ m = _matrix(s)
+ if m is not None:
+ return all(_readable(normalize_answer(c), B, depth + 1) for row in m for c in row)
+ parts = _split_top(s, "\\cup")
+ if len(parts) > 1:
+ return all(_readable(normalize_answer(x), B, depth + 1) for x in parts)
+ elems = _list_elems(s)
+ if len(elems) > 1:
+ return all(_readable(normalize_answer(x), B, depth + 1) for x in elems)
+ if not elems:
+ return False
+ s = elems[0]
+ br = _bracketed(s)
+ if br is not None:
+ return all(_readable(normalize_answer(x), B, depth + 1) for x in br[2])
+ st = _braced_set(s)
+ if st is not None:
+ return all(_readable(normalize_answer(x), B, depth + 1) for x in st)
+ rels = _REL.findall(s)
+ if rels:
+ rel = _split_relation(s, rels)
+ if rel is None:
+ return False
+ return all(_readable(normalize_answer(x), B, depth + 1) for x in (rel[0], rel[2]))
+ try:
+ parse_expr(s.replace("%", ""), B)
+ except VerifyError:
+ return False
+ return True
diff --git a/code/align/verify/expr.py b/code/align/verify/expr.py
new file mode 100644
index 0000000000000000000000000000000000000000..ab4bdc1c78e76d1103bd9204cc860914331e42d0
--- /dev/null
+++ b/code/align/verify/expr.py
@@ -0,0 +1,1046 @@
+"""A small expression engine: recursive-descent parser, exact/decimal
+evaluator, and a tiny multivariate polynomial normaliser. Pure stdlib.
+
+Replaces the sympy half of the predecessor's verifier. Three jobs:
+
+ parse LaTeX-ish / ASCII text -> AST (tuples). `+ - * / ^`, parentheses,
+ `\\frac`, `\\sqrt[n]{}`, `\\binom`, `!`, postfix `%`, pi, e, common
+ functions, IMPLICIT multiplication (`2\\sqrt{3}`, `3x`, `(a)(b)`),
+ and mixed numbers (`2\\frac{1}{3}` = 7/3, not 2/3).
+ evaluate AST -> `Fraction` (exact) or `Decimal` (60 digits). Rational
+ arithmetic stays exact; `sqrt` of a perfect-square rational stays
+ exact; everything else is high-precision decimal.
+ to_poly AST -> canonical polynomial dict, or None. Handles multivariate
+ polynomials with rational coefficients, `sqrt(k)` atoms reduced to
+ square-free form (`sqrt(12)` = `2*sqrt(3)`, `sqrt(3)^2` = 3), `pi`,
+ `e`, and `i` with i^2 = -1.
+
+HOSTILE-INPUT DISCIPLINE (the predecessor's hazards 1-5, re-derived for an
+engine we now own). There is no sympy printer to hang in `str()`, but there are
+still three ways to wedge a worker, each capped here:
+
+ * unbounded POWER: `9...9 ^ 9...9`, `10**10**10`. Every integer power is
+ size-checked BEFORE it is computed (`_MAX_BITS`); decimal exponents are
+ capped; factorial / binomial arguments are capped.
+ * unbounded WORK: a global `Budget` ticks in the tokenizer, parser, evaluator
+ and polynomial multiplier; exhausting it raises `VerifyError`.
+ * unbounded DEPTH: nesting depth is capped (`_MAX_DEPTH`), so `\\sqrt{` x 10^5
+ raises `VerifyError`, never `RecursionError`.
+
+`VerifyTimeout` subclasses BaseException ON PURPOSE, exactly like the exception
+it replaces: an `except Exception` in some caller must not be able to swallow a
+deadline. `deadline()` arms it via SIGALRM (main thread of the worker process,
+which is what `ProcessPoolExecutor` workers are).
+"""
+
+from __future__ import annotations
+
+import contextlib
+import decimal
+import math
+import re
+import signal
+import threading
+from dataclasses import dataclass
+from decimal import Decimal
+from fractions import Fraction
+from typing import Iterator, Union
+
+__all__ = [
+ "Budget",
+ "is_bare_literal",
+ "Parsed",
+ "VerifyError",
+ "VerifyTimeout",
+ "deadline",
+ "evaluate",
+ "free_vars",
+ "parse_expr",
+ "poly_proportional",
+ "to_poly",
+ "values_close",
+]
+
+_PREC = 60
+_MAX_BITS = 60_000 # largest exact integer power we will materialise
+_MAX_DEPTH = 60
+_MAX_TOKENS = 4_000
+_MAX_FACT = 3_000
+_MAX_POLY_TERMS = 3_000
+_MAX_POLY_EXP = 64
+
+Num = Union[Fraction, Decimal]
+
+PI = Decimal(
+ "3.14159265358979323846264338327950288419716939937510582097494459230781640628620899"
+)
+E = Decimal(
+ "2.71828182845904523536028747135266249775724709369995957496696762772407663035354759"
+)
+
+
+class VerifyError(Exception):
+ """Input could not be parsed/evaluated within the safety bounds. A SOFT
+ failure: the caller folds it to 'could not confirm equivalence'."""
+
+
+class VerifyTimeout(BaseException):
+ """Deadline expired. BaseException so `except Exception` cannot eat it."""
+
+
+@contextlib.contextmanager
+def deadline(seconds: float) -> Iterator[None]:
+ """Arm a SIGALRM deadline iff we are the main thread on a platform with
+ SIGALRM; otherwise a no-op (the `Budget` still bounds the work)."""
+ if (
+ seconds <= 0
+ or not hasattr(signal, "setitimer")
+ or threading.current_thread() is not threading.main_thread()
+ ):
+ yield
+ return
+
+ def _fire(signum, frame): # pragma: no cover - exercised via timing only
+ raise VerifyTimeout()
+
+ old = signal.signal(signal.SIGALRM, _fire)
+ signal.setitimer(signal.ITIMER_REAL, float(seconds))
+ try:
+ yield
+ finally:
+ signal.setitimer(signal.ITIMER_REAL, 0.0)
+ signal.signal(signal.SIGALRM, old)
+
+
+class Budget:
+ """Global work counter for one comparison."""
+
+ __slots__ = ("left",)
+
+ def __init__(self, n: int = 300_000):
+ self.left = n
+
+ def tick(self, k: int = 1) -> None:
+ self.left -= k
+ if self.left < 0:
+ raise VerifyError("work budget exhausted")
+
+
+# ---------------------------------------------------------------------------
+# Tokenizer
+# ---------------------------------------------------------------------------
+
+_TOK_RE = re.compile(
+ r"""
+ (?P\s+)
+ | (?P(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?)
+ | (?P\\[A-Za-z]+|\\.)
+ | (?P[A-Za-z]+)
+ | (?P\*\*|[-+*/^_!%(){}\[\]|,=<>])
+ """,
+ re.VERBOSE,
+)
+
+_FUNCS = {
+ "sin", "cos", "tan", "cot", "sec", "csc", "ln", "log", "exp", "lg",
+}
+_WORD_NAMES = sorted(_FUNCS | {"pi", "sqrt"}, key=len, reverse=True)
+_GREEK = {
+ "theta", "alpha", "beta", "gamma", "delta", "phi", "lambda", "mu", "sigma",
+ "omega", "rho", "tau", "epsilon", "varphi", "vartheta", "psi", "eta", "kappa",
+ "nu", "xi", "zeta", "chi",
+}
+_STRUCT_CMDS = {"frac", "sqrt", "binom", "dbinom", "tbinom"}
+
+
+@dataclass
+class _Tok:
+ kind: str # num | var | const | fn | sqrt | frac | binom | op
+ text: str
+ sp: bool = False # whitespace precedes
+
+
+def _tokenize(s: str, B: Budget) -> list[_Tok]:
+ if len(s) > 2_000:
+ raise VerifyError("expression too long")
+ toks: list[_Tok] = []
+ i = 0
+ sp = False
+ n = len(s)
+ while i < n:
+ B.tick()
+ m = _TOK_RE.match(s, i)
+ if not m:
+ raise VerifyError(f"bad character at {i}")
+ i = m.end()
+ kind = m.lastgroup
+ text = m.group()
+ if kind == "space":
+ sp = True
+ continue
+ if kind == "num":
+ toks.append(_Tok("num", text, sp))
+ elif kind == "cmd":
+ name = text[1:]
+ if name in _STRUCT_CMDS:
+ toks.append(_Tok({"dbinom": "binom", "tbinom": "binom"}.get(name, name), text, sp))
+ elif name == "pi":
+ toks.append(_Tok("const", "pi", sp))
+ elif name in ("cdot", "times", "ast"):
+ toks.append(_Tok("op", "*", sp))
+ elif name == "div":
+ toks.append(_Tok("op", "/", sp))
+ elif name in _FUNCS or name in ("arcsin", "arccos", "arctan"):
+ toks.append(_Tok("fn", name, sp))
+ elif name in _GREEK:
+ toks.append(_Tok("var", name, sp))
+ elif name in ("{", "}"):
+ # literal braces never occur inside a scalar expression
+ raise VerifyError("literal brace in expression")
+ else:
+ raise VerifyError(f"unsupported macro {text}")
+ elif kind == "word":
+ w = text
+ first = True
+ while w:
+ for name in _WORD_NAMES:
+ if w.startswith(name):
+ if name == "pi":
+ toks.append(_Tok("const", "pi", sp and first))
+ elif name == "sqrt":
+ toks.append(_Tok("sqrt", "sqrt", sp and first))
+ else:
+ toks.append(_Tok("fn", name, sp and first))
+ w = w[len(name) :]
+ break
+ else:
+ c = w[0]
+ toks.append(_Tok("const" if c == "e" else "var", c, sp and first))
+ w = w[1:]
+ first = False
+ else:
+ op = "^" if text == "**" else text
+ toks.append(_Tok("op", op, sp))
+ sp = False
+ if len(toks) > _MAX_TOKENS:
+ raise VerifyError("too many tokens")
+ return toks
+
+
+# ---------------------------------------------------------------------------
+# Parser
+# ---------------------------------------------------------------------------
+
+Node = tuple
+
+
+@dataclass
+class Parsed:
+ ast: Node
+ has_decimal: bool # a decimal-point literal appeared (user may have rounded)
+
+
+class _Parser:
+ def __init__(self, toks: list[_Tok], B: Budget):
+ self.t = toks
+ self.i = 0
+ self.B = B
+ self.depth = 0
+ self.has_decimal = False
+
+ # -- plumbing ---------------------------------------------------------
+ def _peek(self) -> _Tok | None:
+ return self.t[self.i] if self.i < len(self.t) else None
+
+ def _next(self) -> _Tok:
+ if self.i >= len(self.t):
+ raise VerifyError("unexpected end")
+ tok = self.t[self.i]
+ self.i += 1
+ return tok
+
+ def _is_op(self, tok: _Tok | None, *texts: str) -> bool:
+ return tok is not None and tok.kind == "op" and tok.text in texts
+
+ def _expect(self, text: str) -> None:
+ tok = self._next()
+ if not (tok.kind == "op" and tok.text == text):
+ raise VerifyError(f"expected {text!r}")
+
+ def _enter(self) -> None:
+ self.B.tick()
+ self.depth += 1
+ if self.depth > _MAX_DEPTH:
+ raise VerifyError("nesting too deep")
+
+ def _leave(self) -> None:
+ self.depth -= 1
+
+ # -- grammar ----------------------------------------------------------
+ def parse(self) -> Node:
+ if not self.t:
+ raise VerifyError("empty")
+ node = self._expr()
+ if self.i != len(self.t):
+ raise VerifyError("trailing tokens")
+ return node
+
+ def _expr(self) -> Node:
+ self._enter()
+ node = self._term()
+ while True:
+ tok = self._peek()
+ if self._is_op(tok, "+", "-"):
+ self.i += 1
+ rhs = self._term()
+ node = ("add" if tok.text == "+" else "sub", node, rhs)
+ else:
+ break
+ self._leave()
+ return node
+
+ def _starts_atom(self, tok: _Tok) -> bool:
+ if tok.kind in ("num", "var", "const", "fn", "sqrt", "frac", "binom"):
+ return True
+ return tok.kind == "op" and tok.text in ("(", "[", "{")
+
+ def _term(self) -> Node:
+ node = self._unary()
+ while True:
+ tok = self._peek()
+ if tok is None:
+ break
+ if self._is_op(tok, "*", "/"):
+ self.i += 1
+ rhs = self._unary()
+ node = ("mul" if tok.text == "*" else "div", node, rhs)
+ elif self._starts_atom(tok):
+ prev = self.t[self.i - 1]
+ if prev.kind == "num" and tok.kind == "num":
+ raise VerifyError("adjacent numbers")
+ rhs = self._power()
+ node = ("mul", node, rhs)
+ else:
+ break
+ return node
+
+ def _unary(self) -> Node:
+ tok = self._peek()
+ if self._is_op(tok, "-"):
+ self.i += 1
+ self._enter()
+ inner = self._unary()
+ self._leave()
+ return ("neg", inner)
+ if self._is_op(tok, "+"):
+ self.i += 1
+ return self._unary()
+ return self._power()
+
+ def _power(self) -> Node:
+ base = self._postfix()
+ tok = self._peek()
+ if self._is_op(tok, "^"):
+ self.i += 1
+ self._enter()
+ exp = self._exp_operand()
+ self._leave()
+ return ("pow", base, exp)
+ return base
+
+ def _exp_operand(self) -> Node:
+ tok = self._peek()
+ if tok is None:
+ raise VerifyError("dangling ^")
+ if self._is_op(tok, "{"):
+ self.i += 1
+ node = self._expr()
+ self._expect("}")
+ return node
+ if self._is_op(tok, "-"):
+ self.i += 1
+ return ("neg", self._exp_operand())
+ if self._is_op(tok, "+"):
+ self.i += 1
+ return self._exp_operand()
+ return self._power()
+
+ def _postfix(self) -> Node:
+ node = self._primary()
+ while True:
+ tok = self._peek()
+ if self._is_op(tok, "!"):
+ self.i += 1
+ node = ("fact", node)
+ elif self._is_op(tok, "%"):
+ self.i += 1
+ node = ("div", node, ("num", Fraction(100)))
+ else:
+ break
+ return node
+
+ def _group_arg(self) -> Node:
+ """A LaTeX macro argument: `{expr}` or ONE token (`\\frac12`, `\\sqrt2`)."""
+ tok = self._peek()
+ if tok is None:
+ raise VerifyError("missing argument")
+ if self._is_op(tok, "{"):
+ self.i += 1
+ node = self._expr()
+ self._expect("}")
+ return node
+ if tok.kind == "num":
+ if "." not in tok.text and len(tok.text) > 1 and tok.text.isdigit():
+ # `\frac12`: take ONE digit, leave the rest in the stream.
+ tok.text, head = tok.text[1:], tok.text[0]
+ return ("num", Fraction(int(head)))
+ self.i += 1
+ return self._num_node(tok.text)
+ if tok.kind in ("var", "const"):
+ self.i += 1
+ return ("const", tok.text) if tok.kind == "const" else ("var", tok.text)
+ if tok.kind in ("frac", "sqrt", "binom", "fn"):
+ return self._primary()
+ raise VerifyError("bad macro argument")
+
+ def _num_node(self, text: str) -> Node:
+ if len(text) > 400:
+ raise VerifyError("numeral too long")
+ mant, exp = text, 0
+ m = re.fullmatch(r"(.*?)[eE]([+-]?\d+)", text)
+ if m:
+ # Scientific literal `1e-3` = 0.001. It is NOT `1*e-3` (Euler's e):
+ # reading it that way made `e-3` and `1e-3` the same answer.
+ mant, exp = m.group(1), int(m.group(2))
+ if abs(exp) > 300:
+ raise VerifyError("exponent too large")
+ if "." in mant and mant.split(".", 1)[1].strip("0"):
+ self.has_decimal = True
+ val = Fraction(mant if not mant.endswith(".") else mant[:-1])
+ if exp:
+ val *= Fraction(10) ** exp
+ return ("num", val)
+
+ def _primary(self) -> Node:
+ tok = self._next()
+ self.B.tick()
+ k = tok.kind
+ if k == "num":
+ node = self._num_node(tok.text)
+ nxt = self._peek()
+ # Mixed number: `2\frac{1}{3}` is 2 + 1/3 when the lead is an
+ # integer literal and the fraction is integer/integer.
+ if (
+ nxt is not None
+ and nxt.kind == "frac"
+ and "." not in tok.text
+ and not self._is_op(self._prev_nonspace(), "^")
+ ):
+ frac = self._primary()
+ if frac[0] == "div" and frac[1][0] == "num" and frac[2][0] == "num":
+ return ("add", node, frac)
+ return ("mul", node, frac)
+ return node
+ if k == "op":
+ if tok.text in ("(", "["):
+ node = self._expr()
+ close = self._next()
+ want = ")" if tok.text == "(" else "]"
+ if not (close.kind == "op" and close.text == want):
+ raise VerifyError("unbalanced bracket")
+ return node
+ if tok.text == "{":
+ node = self._expr()
+ self._expect("}")
+ return node
+ if tok.text == "|":
+ node = self._expr()
+ self._expect("|")
+ return ("abs", node)
+ raise VerifyError(f"unexpected {tok.text!r}")
+ if k == "const":
+ return ("const", tok.text)
+ if k == "var":
+ name = tok.text
+ nxt = self._peek()
+ if self._is_op(nxt, "_"):
+ self.i += 1
+ name += "_" + self._subscript()
+ return ("var", name)
+ if k == "frac":
+ a = self._group_arg()
+ b = self._group_arg()
+ return ("div", a, b)
+ if k == "binom":
+ a = self._group_arg()
+ b = self._group_arg()
+ return ("binom", a, b)
+ if k == "sqrt":
+ n_node = None
+ if self._is_op(self._peek(), "["):
+ self.i += 1
+ n_node = self._expr()
+ self._expect("]")
+ tok2 = self._peek()
+ if self._is_op(tok2, "("):
+ self.i += 1
+ arg = self._expr()
+ self._expect(")")
+ else:
+ arg = self._group_arg()
+ return ("sqrt", arg, n_node)
+ if k == "fn":
+ return self._function(tok.text)
+ raise VerifyError("bad token")
+
+ def _prev_nonspace(self) -> _Tok | None:
+ return self.t[self.i - 2] if self.i >= 2 else None
+
+ def _subscript(self) -> str:
+ tok = self._next()
+ if self._is_op(tok, "{"):
+ parts: list[str] = []
+ while True:
+ t2 = self._next()
+ if self._is_op(t2, "}"):
+ break
+ parts.append(t2.text)
+ if len(parts) > 20:
+ raise VerifyError("subscript too long")
+ return "".join(parts)
+ return tok.text
+
+ def _function(self, name: str) -> Node:
+ base: Node | None = None
+ if self._is_op(self._peek(), "_"):
+ self.i += 1
+ base = self._group_arg()
+ exp: Node | None = None
+ if self._is_op(self._peek(), "^"):
+ self.i += 1
+ exp = self._exp_operand()
+ if self._is_op(self._peek(), "("):
+ self.i += 1
+ arg = self._expr()
+ self._expect(")")
+ else:
+ arg = self._power()
+ node: Node = ("fn", name, arg, base)
+ if exp is not None:
+ node = ("pow", node, exp)
+ return node
+
+
+def parse_expr(s: str, B: Budget | None = None) -> Parsed:
+ B = B or Budget()
+ toks = _tokenize(s, B)
+ p = _Parser(toks, B)
+ ast = p.parse()
+ return Parsed(ast, p.has_decimal)
+
+
+def is_bare_literal(node: Node) -> bool:
+ """True iff the whole expression is ONE numeric literal (optionally signed
+ or `%`): the only shape where a decimal can be a human ROUNDING of a value.
+ `10000000+0.1-0.1` is not a rounded number, it is arithmetic."""
+ while True:
+ k = node[0]
+ if k == "num":
+ return True
+ if k == "neg":
+ node = node[1]
+ elif k == "div" and node[2] == ("num", Fraction(100)):
+ node = node[1]
+ else:
+ return False
+
+
+def free_vars(node: Node, acc: set[str] | None = None) -> set[str]:
+ acc = set() if acc is None else acc
+ kind = node[0]
+ if kind == "var":
+ acc.add(node[1])
+ elif kind in ("num", "const"):
+ pass
+ elif kind in ("neg", "fact", "abs"):
+ free_vars(node[1], acc)
+ elif kind == "sqrt":
+ free_vars(node[1], acc)
+ if node[2] is not None:
+ free_vars(node[2], acc)
+ elif kind == "fn":
+ free_vars(node[2], acc)
+ if node[3] is not None:
+ free_vars(node[3], acc)
+ else:
+ free_vars(node[1], acc)
+ free_vars(node[2], acc)
+ return acc
+
+
+# ---------------------------------------------------------------------------
+# Evaluation
+# ---------------------------------------------------------------------------
+
+
+def _ctx() -> decimal.Context:
+ return decimal.Context(
+ prec=_PREC,
+ rounding=decimal.ROUND_HALF_EVEN,
+ Emax=100_000,
+ Emin=-100_000,
+ traps=[decimal.InvalidOperation, decimal.DivisionByZero, decimal.Overflow],
+ )
+
+
+def _dec(x: Num) -> Decimal:
+ if isinstance(x, Decimal):
+ return x
+ return Decimal(x.numerator) / Decimal(x.denominator)
+
+
+def _check(x: Num) -> Num:
+ if isinstance(x, Decimal):
+ if not x.is_finite():
+ raise VerifyError("non-finite")
+ elif x.numerator.bit_length() > 4 * _MAX_BITS or x.denominator.bit_length() > 4 * _MAX_BITS:
+ raise VerifyError("number too large")
+ return x
+
+
+def _iroot(n: int, k: int) -> int | None:
+ """Exact integer k-th root of n >= 0, or None."""
+ if n < 0:
+ return None
+ if n < 2:
+ return n
+ if k == 2:
+ r = math.isqrt(n)
+ return r if r * r == n else None
+ lo, hi = 1, 1 << (n.bit_length() // k + 1)
+ while lo < hi:
+ mid = (lo + hi) // 2
+ if mid**k < n:
+ lo = mid + 1
+ else:
+ hi = mid
+ return lo if lo**k == n else None
+
+
+def _frac_root(x: Fraction, k: int) -> Fraction | None:
+ if x < 0:
+ return None
+ a, b = _iroot(x.numerator, k), _iroot(x.denominator, k)
+ if a is None or b is None:
+ return None
+ return Fraction(a, b)
+
+
+def _int_pow(x: Fraction, n: int) -> Fraction:
+ if n == 0:
+ return Fraction(1)
+ size = max(x.numerator.bit_length(), x.denominator.bit_length(), 1)
+ if size * abs(n) > _MAX_BITS:
+ raise VerifyError("power too large")
+ if n < 0:
+ if x == 0:
+ raise VerifyError("0 ** negative")
+ return Fraction(1) / (x**-n)
+ return x**n
+
+
+def _pow(a: Num, b: Num) -> Num:
+ if isinstance(b, Fraction) and b.denominator == 1:
+ n = int(b)
+ if isinstance(a, Fraction):
+ return _int_pow(a, n)
+ if abs(n) > 10_000:
+ raise VerifyError("exponent too large")
+ if a == 0 and n < 0:
+ raise VerifyError("0 ** negative")
+ return a**n
+ if isinstance(a, Fraction) and isinstance(b, Fraction):
+ if a < 0:
+ raise VerifyError("negative base, fractional exponent")
+ if b.denominator <= 64 and abs(b.numerator) <= 10_000:
+ r = _frac_root(a, b.denominator)
+ if r is not None:
+ return _int_pow(r, b.numerator)
+ da, db = _dec(a), _dec(b)
+ if da < 0:
+ raise VerifyError("negative base, non-integer exponent")
+ if abs(db) > 10_000:
+ raise VerifyError("exponent too large")
+ if da == 0:
+ if db < 0:
+ raise VerifyError("0 ** negative")
+ return Fraction(0)
+ return da**db
+
+
+def _sqrt(a: Num, n: int) -> Num:
+ if n < 2 or n > 64:
+ raise VerifyError("bad root index")
+ if isinstance(a, Fraction):
+ if a < 0:
+ if n % 2 == 1:
+ r = _sqrt(-a, n)
+ return -r if isinstance(r, Fraction) else -r
+ raise VerifyError("even root of negative")
+ r = _frac_root(a, n)
+ if r is not None:
+ return r
+ d = _dec(a)
+ else:
+ d = a
+ if d < 0:
+ if n % 2 == 1:
+ return -_sqrt(-d, n)
+ raise VerifyError("even root of negative")
+ if n == 2:
+ return d.sqrt()
+ return d ** (Decimal(1) / Decimal(n))
+
+
+def _reduce_angle(x: Decimal) -> Decimal:
+ if abs(x) > Decimal(10) ** 6:
+ raise VerifyError("angle too large")
+ two_pi = 2 * PI
+ x = x % two_pi
+ return x
+
+
+def _sin(x: Decimal) -> Decimal:
+ x = _reduce_angle(x)
+ term, total, k = x, x, 1
+ eps = Decimal(10) ** (-(_PREC + 5))
+ while abs(term) > eps and k < 400:
+ term = -term * x * x / ((2 * k) * (2 * k + 1))
+ total += term
+ k += 1
+ return total
+
+
+def _cos(x: Decimal) -> Decimal:
+ x = _reduce_angle(x)
+ term, total, k = Decimal(1), Decimal(1), 1
+ eps = Decimal(10) ** (-(_PREC + 5))
+ while abs(term) > eps and k < 400:
+ term = -term * x * x / ((2 * k - 1) * (2 * k))
+ total += term
+ k += 1
+ return total
+
+
+def _fn(name: str, a: Num, base: Num | None) -> Num:
+ if base is not None:
+ # A base is meaningful (and validated) ONLY for log, and BEFORE any
+ # shortcut: `log_0(1)` and `log_{-2}(1)` are undefined, not 0.
+ if name != "log":
+ raise VerifyError("base on a non-log function")
+ db0 = _dec(base)
+ if db0 <= 0 or db0 == 1:
+ raise VerifyError("log base")
+ if name in ("log", "lg", "ln"):
+ if isinstance(a, Fraction):
+ if a <= 0:
+ raise VerifyError("log domain")
+ if a == 1:
+ return Fraction(0)
+ elif a <= 0:
+ raise VerifyError("log domain")
+ d = _dec(a)
+ if name == "ln":
+ return d.ln()
+ if base is None:
+ return d.log10()
+ db = _dec(base)
+ if db <= 0 or db == 1:
+ raise VerifyError("log base")
+ return d.ln() / db.ln()
+ if name == "exp":
+ if isinstance(a, Fraction) and a == 0:
+ return Fraction(1)
+ d = _dec(a)
+ if abs(d) > 1000:
+ raise VerifyError("exp too large")
+ return d.exp()
+ if isinstance(a, Fraction) and a == 0 and name in ("sin", "tan", "arcsin", "arctan"):
+ return Fraction(0)
+ if isinstance(a, Fraction) and a == 0 and name == "cos":
+ return Fraction(1)
+ d = _dec(a)
+ if name == "sin":
+ return _sin(d)
+ if name == "cos":
+ return _cos(d)
+ s, c = _sin(d), _cos(d)
+ if name == "tan":
+ if abs(c) < Decimal(10) ** -30:
+ raise VerifyError("tan pole")
+ return s / c
+ if name == "cot":
+ if abs(s) < Decimal(10) ** -30:
+ raise VerifyError("cot pole")
+ return c / s
+ if name == "sec":
+ if abs(c) < Decimal(10) ** -30:
+ raise VerifyError("sec pole")
+ return 1 / c
+ if name == "csc":
+ if abs(s) < Decimal(10) ** -30:
+ raise VerifyError("csc pole")
+ return 1 / s
+ raise VerifyError(f"unsupported function {name}")
+
+
+def _ev(node: Node, env: dict[str, Num], B: Budget) -> Num:
+ B.tick()
+ k = node[0]
+ if k == "num":
+ return node[1]
+ if k == "const":
+ return PI if node[1] == "pi" else E
+ if k == "var":
+ if node[1] not in env:
+ raise VerifyError(f"free variable {node[1]}")
+ return env[node[1]]
+ if k == "neg":
+ return -_ev(node[1], env, B)
+ if k == "abs":
+ return abs(_ev(node[1], env, B))
+ if k in ("add", "sub", "mul"):
+ a, b = _ev(node[1], env, B), _ev(node[2], env, B)
+ if isinstance(a, Fraction) and isinstance(b, Fraction):
+ r = a + b if k == "add" else a - b if k == "sub" else a * b
+ else:
+ da, db = _dec(a), _dec(b)
+ r = da + db if k == "add" else da - db if k == "sub" else da * db
+ return _check(r)
+ if k == "div":
+ a, b = _ev(node[1], env, B), _ev(node[2], env, B)
+ if b == 0:
+ raise VerifyError("division by zero")
+ if isinstance(a, Fraction) and isinstance(b, Fraction):
+ return _check(a / b)
+ return _check(_dec(a) / _dec(b))
+ if k == "pow":
+ return _check(_pow(_ev(node[1], env, B), _ev(node[2], env, B)))
+ if k == "sqrt":
+ n = 2
+ if node[2] is not None:
+ nv = _ev(node[2], env, B)
+ if not (isinstance(nv, Fraction) and nv.denominator == 1):
+ raise VerifyError("root index must be an integer")
+ n = int(nv)
+ return _check(_sqrt(_ev(node[1], env, B), n))
+ if k == "fact":
+ a = _ev(node[1], env, B)
+ if not (isinstance(a, Fraction) and a.denominator == 1 and 0 <= a <= _MAX_FACT):
+ raise VerifyError("factorial domain")
+ return Fraction(math.factorial(int(a)))
+ if k == "binom":
+ a, b = _ev(node[1], env, B), _ev(node[2], env, B)
+ if not (
+ isinstance(a, Fraction)
+ and isinstance(b, Fraction)
+ and a.denominator == 1
+ and b.denominator == 1
+ and 0 <= b <= a <= _MAX_FACT
+ ):
+ raise VerifyError("binom domain")
+ return Fraction(math.comb(int(a), int(b)))
+ if k == "fn":
+ a = _ev(node[2], env, B)
+ base = _ev(node[3], env, B) if node[3] is not None else None
+ return _check(_fn(node[1], a, base))
+ raise VerifyError(f"unknown node {k}")
+
+
+def evaluate(node: Node, env: dict[str, Num] | None = None, B: Budget | None = None) -> Num:
+ """Evaluate under a fixed 60-digit decimal context."""
+ with decimal.localcontext(_ctx()):
+ return _ev(node, env or {}, B or Budget())
+
+
+def values_close(a: Num, b: Num, *, user_rounding: bool) -> bool:
+ """Equality of two evaluated numbers.
+
+ Both exact and no rounded literal -> exact equality, period. Otherwise
+ RELATIVE closeness: 1e-6 when one side carried a decimal literal with a
+ nonzero fractional part (a human or model rounding, as in `0.714286` vs
+ `5/7`; `5.0` does not count), else 1e-12 (floating noise between
+ two exact irrational computations). Relative, never absolute: the
+ predecessor's `float_rounding=6` made every value below 1e-6 mutually
+ equivalent (hazard 7: 1/2004! "equals" 1/2006!).
+ """
+ if isinstance(a, Fraction) and isinstance(b, Fraction) and not user_rounding:
+ return a == b
+ with decimal.localcontext(_ctx()):
+ da, db = _dec(a), _dec(b)
+ if da == db:
+ return True
+ denom = max(abs(da), abs(db))
+ if denom == 0:
+ return True
+ tol = Decimal("1e-6") if user_rounding else Decimal("1e-12")
+ return abs(da - db) / denom < tol
+
+
+# ---------------------------------------------------------------------------
+# Polynomial normaliser
+# ---------------------------------------------------------------------------
+
+Mono = tuple # sorted tuple of (symbol, exponent)
+Poly = dict
+
+
+def _sqfree(k: int, B: Budget) -> tuple[int, int]:
+ """k = out^2 * sf with sf square-free (trial division, bounded)."""
+ if k < 1:
+ raise VerifyError("sqfree domain")
+ out, sf, d = 1, k, 2
+ while d * d <= sf and d <= 31_623:
+ B.tick()
+ dd = d * d
+ while sf % dd == 0:
+ sf //= dd
+ out *= d
+ d += 1 if d == 2 else 2
+ return out, sf
+
+
+def _norm_mono(mono: dict[str, int]) -> tuple[Fraction, Mono]:
+ """Apply sqrt(k)^2 = k and i^2 = -1; return (coefficient factor, monomial)."""
+ coef = Fraction(1)
+ out: list[tuple[str, int]] = []
+ for sym, e in mono.items():
+ if e == 0:
+ continue
+ if sym.startswith("sqrt:"):
+ k = int(sym[5:])
+ q, r = divmod(e, 2)
+ coef *= Fraction(k) ** q
+ e = r
+ elif sym == "i":
+ # i^4 = 1, i^2 = -1, i^3 = -i.
+ r = e % 4
+ if r in (2, 3):
+ coef = -coef
+ e = 1 if r in (1, 3) else 0
+ if e:
+ out.append((sym, e))
+ return coef, tuple(sorted(out))
+
+
+def _padd(p: Poly, q: Poly, sign: int = 1) -> Poly:
+ r = dict(p)
+ for m, c in q.items():
+ v = r.get(m, 0) + sign * c
+ if v == 0:
+ r.pop(m, None)
+ else:
+ r[m] = v
+ return r
+
+
+def _pmul(p: Poly, q: Poly, B: Budget) -> Poly:
+ if len(p) * len(q) > _MAX_POLY_TERMS * 4:
+ raise VerifyError("polynomial too large")
+ r: Poly = {}
+ for m1, c1 in p.items():
+ for m2, c2 in q.items():
+ B.tick()
+ acc: dict[str, int] = dict(m1)
+ for s, e in m2:
+ acc[s] = acc.get(s, 0) + e
+ f, m = _norm_mono(acc)
+ v = r.get(m, 0) + c1 * c2 * f
+ if v == 0:
+ r.pop(m, None)
+ else:
+ r[m] = v
+ if len(r) > _MAX_POLY_TERMS:
+ raise VerifyError("polynomial too large")
+ return r
+
+
+def _pconst(p: Poly) -> Fraction | None:
+ if not p:
+ return Fraction(0)
+ if len(p) == 1 and () in p:
+ return p[()]
+ return None
+
+
+def to_poly(node: Node, B: Budget | None = None) -> Poly | None:
+ """Canonical polynomial of `node`, or None if it is not one."""
+ B = B or Budget()
+ try:
+ return _poly(node, B)
+ except _NotPoly:
+ return None
+
+
+class _NotPoly(Exception):
+ pass
+
+
+def _poly(node: Node, B: Budget) -> Poly:
+ B.tick()
+ k = node[0]
+ if k == "num":
+ return {(): node[1]} if node[1] != 0 else {}
+ if k == "const":
+ return {((node[1], 1),): Fraction(1)}
+ if k == "var":
+ return {((node[1], 1),): Fraction(1)}
+ if k == "neg":
+ return {m: -c for m, c in _poly(node[1], B).items()}
+ if k in ("add", "sub"):
+ return _padd(_poly(node[1], B), _poly(node[2], B), 1 if k == "add" else -1)
+ if k == "mul":
+ return _pmul(_poly(node[1], B), _poly(node[2], B), B)
+ if k == "div":
+ d = _pconst(_poly(node[2], B))
+ if d is None or d == 0:
+ raise _NotPoly
+ return {m: c / d for m, c in _poly(node[1], B).items()}
+ if k == "pow":
+ e = _pconst(_poly(node[2], B))
+ if e is None or e.denominator != 1 or not (0 <= e <= _MAX_POLY_EXP):
+ raise _NotPoly
+ base = _poly(node[1], B)
+ result: Poly = {(): Fraction(1)}
+ for _ in range(int(e)):
+ result = _pmul(result, base, B)
+ return result
+ if k == "sqrt":
+ n = 2
+ if node[2] is not None:
+ nv = _pconst(_poly(node[2], B))
+ if nv is None or nv != 2:
+ raise _NotPoly
+ c = _pconst(_poly(node[1], B))
+ if c is None or c < 0:
+ raise _NotPoly
+ if c == 0:
+ return {}
+ p, q = c.numerator, c.denominator
+ out, sf = _sqfree(p * q, B)
+ coef = Fraction(out, q)
+ if sf == 1:
+ return {(): coef}
+ return {((f"sqrt:{sf}", 1),): coef}
+ if k in ("fact", "binom"):
+ raise _NotPoly # numeric path handles these exactly
+ raise _NotPoly
+
+
+def poly_proportional(p: Poly, q: Poly) -> bool:
+ """p = c * q for a nonzero rational c (both nonzero)."""
+ if not p or not q or p.keys() != q.keys():
+ return False
+ m0 = next(iter(p))
+ ratio = p[m0] / q[m0]
+ return all(p[m] == ratio * q[m] for m in p)
diff --git a/code/align/verify/extract.py b/code/align/verify/extract.py
new file mode 100644
index 0000000000000000000000000000000000000000..11dcff756fc724c04c34cddd126b0a8f07dd8bb7
--- /dev/null
+++ b/code/align/verify/extract.py
@@ -0,0 +1,200 @@
+"""Final-answer extraction from model text and gold strings.
+
+Replaces the extraction half of `math_verify.parse`. Pure stdlib.
+
+Priority, highest first (the order is a contract, tested):
+
+ 1. the LAST `\\boxed{...}` / `\\fbox{...}` with BRACE BALANCING. Balancing is
+ not optional: `\\boxed{\\frac{1}{2}}` and `\\boxed{\\{1,2\\}}` contain
+ braces, and a regex like `\\boxed\\{([^}]*)\\}` truncates both at the first
+ `}` -- a silent false negative on exactly the LaTeX-heavy answers a
+ stronger policy emits more often. Escaped braces (`\\{`, `\\}`) are
+ LaTeX literals and do not open or close a group.
+ 2. GSM8K's `#### 42` terminator (last occurrence).
+ 3. "final answer is ...", "the answer is ...", "Final Answer: ..." (last).
+ 4. FALLBACK: the last number in the text.
+
+The fallback is deliberately last and deliberately weak: a model that never
+states an answer but happens to end on a number is being read charitably, not
+verified. `extract_final_answer` returns None only when no number exists at
+all, which is the NO_ANSWER signal upstream (a different fact from a wrong
+answer).
+"""
+
+from __future__ import annotations
+
+import re
+
+__all__ = [
+ "extract_boxed",
+ "extract_gold_answer",
+ "extract_final_answer",
+ "last_number",
+ "match_brace",
+]
+
+_BOXED_OPEN = re.compile(r"\\(?:boxed|fbox)\s*\{")
+_BOXED_BARE = re.compile(r"\\boxed\s+([^\s${}]+)")
+
+
+def match_brace(s: str, open_idx: int) -> int:
+ """Index of the `}` closing the `{` at `open_idx`, or -1 if unbalanced.
+
+ Skips backslash-escaped characters so `\\{` / `\\}` / `\\\\` never change
+ depth. O(n); no recursion, so a hostile 10^5-deep brace string cannot
+ blow the stack.
+ """
+ if open_idx >= len(s) or s[open_idx] != "{":
+ return -1
+ depth = 0
+ j = open_idx
+ n = len(s)
+ while j < n:
+ c = s[j]
+ if c == "\\":
+ j += 2
+ continue
+ if c == "{":
+ depth += 1
+ elif c == "}":
+ depth -= 1
+ if depth == 0:
+ return j
+ j += 1
+ return -1
+
+
+def extract_boxed(text: str | None) -> str | None:
+ """Contents of the LAST balanced `\\boxed{...}` / `\\fbox{...}`, else None.
+
+ "Last" because chain-of-thought often boxes an intermediate value; the
+ committed answer is the final one. An unbalanced trailing box (a truncated
+ generation) is skipped in favour of the previous balanced one.
+ """
+ if not text:
+ return None
+ opens = list(_BOXED_OPEN.finditer(text))
+ for m in reversed(opens):
+ close = match_brace(text, m.end() - 1)
+ if close != -1:
+ return text[m.end() : close]
+ # `\boxed 5` (no braces) is legal LaTeX and appears in a few corpora.
+ bare = list(_BOXED_BARE.finditer(text))
+ if bare:
+ return bare[-1].group(1)
+ return None
+
+
+_HASHES = re.compile(r"####\s*([^\n]+)")
+# "final answer is", "the answer is", "Final Answer:", "answer = ". Last wins.
+_ANSWER_IS = re.compile(
+ r"(?:final\s+answer|answer)\s*(?:is|:|=)\s*",
+ re.IGNORECASE,
+)
+_NUMBER = re.compile(r"-?\d[\d,]*(?:\.\d+)?(?:\s*/\s*\d+)?|-?\.\d+")
+_INLINE_MATH = re.compile(r"\$\$?(.+?)\$\$?|\\\((.+?)\\\)|\\\[(.+?)\\\]", re.DOTALL)
+
+
+def _clean_tail(s: str) -> str:
+ """Trim presentation noise from a free-text answer span."""
+ s = s.strip()
+ # Markdown emphasis around the answer.
+ s = s.strip("*_` ")
+ s = s.lstrip(":").strip()
+ # A leading inline-math span wins over the prose that follows it.
+ m = _INLINE_MATH.match(s)
+ if m:
+ s = next(g for g in m.groups() if g is not None)
+ s = s.strip().strip("$").strip()
+ # Cut at a sentence terminator that is NOT a decimal point: a period
+ # followed by whitespace+uppercase or end of string.
+ cut = re.search(r"\.(?=\s+[A-Z]|\s*$)", s)
+ if cut:
+ s = s[: cut.start()]
+ return s.strip().rstrip(".").strip()
+
+
+def last_number(text: str | None) -> str | None:
+ """The last number-looking token, thousands separators removed."""
+ if not text:
+ return None
+ hits = _NUMBER.findall(text)
+ if not hits:
+ return None
+ tok = hits[-1].replace(" ", "")
+ if re.fullmatch(r"-?\d{1,3}(?:,\d{3})+(?:\.\d+)?", tok):
+ tok = tok.replace(",", "")
+ return tok.rstrip(",")
+
+
+def extract_final_answer(text: str | None) -> str | None:
+ """Best single answer string from free model text (see module docstring)."""
+ if text is None:
+ return None
+ text = str(text)
+ if not text.strip():
+ return None
+
+ boxed = extract_boxed(text)
+ if boxed is not None and boxed.strip():
+ return boxed.strip()
+
+ hashes = _HASHES.findall(text)
+ if hashes:
+ tail = _clean_tail(hashes[-1])
+ if tail:
+ return tail
+
+ last = None
+ for last in _ANSWER_IS.finditer(text):
+ pass
+ if last is not None:
+ rest = text[last.end() :].split("\n", 1)[0]
+ tail = _clean_tail(rest)
+ if tail:
+ return tail
+
+ # A text that is ALREADY just math (`\\frac{3}{4}`, `(1, 2)`, `x=5`) is an
+ # answer, not prose. Without this the last-number fallback reads a bare
+ # `\\frac{3}{4}` as "4" -- the gold-vs-gold ceiling caught exactly that.
+ if _looks_like_bare_answer(text):
+ return _clean_tail(text.strip().strip("$")) or last_number(text)
+
+ return last_number(text)
+
+
+def _looks_like_bare_answer(text: str) -> bool:
+ t = text.strip()
+ if not t or "\n" in t or len(t) > 200:
+ return False
+ t = re.sub(r"\\(?:begin|end)\{[^{}]*\}", " ", t) # matrix environments
+ t = re.sub(r"\\[A-Za-z]+", " ", t) # LaTeX macros are not words
+ t = re.sub(r"\b(?:pi|sin|cos|tan|cot|sec|csc|ln|log|exp|sqrt)\b", " ", t)
+ return re.search(r"[A-Za-z]{3,}", t) is None and re.search(r"\d", t) is not None
+
+
+def extract_gold_answer(gold: str | None) -> str | None:
+ """The answer inside a GOLD string.
+
+ A gold is already an answer, not prose, so the prose heuristics must NOT
+ run on it. This is the exact bug the gold-vs-gold ceiling caught on the
+ first run: `extract_final_answer("\\frac{3}{4}")` falls through to the
+ last-number fallback and returns "4", so every unboxed fractional gold
+ "failed to verify against itself" -- the 0/100 incident in miniature.
+ Order: a `\\boxed{}` wins (golds are sometimes stored boxed), then a
+ GSM8K `#### n` terminator, else the whole string, verbatim.
+ """
+ if gold is None:
+ return None
+ g = str(gold)
+ if not g.strip():
+ return None
+ boxed = extract_boxed(g)
+ if boxed is not None and boxed.strip():
+ return boxed.strip()
+ hashes = _HASHES.findall(g)
+ if hashes:
+ tail = _clean_tail(hashes[-1])
+ if tail:
+ return tail
+ return g.strip()
diff --git a/code/align/verify/lean/__init__.py b/code/align/verify/lean/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..ccb3046145b3ec908fb61378bda1fe7f86c0dab0
--- /dev/null
+++ b/code/align/verify/lean/__init__.py
@@ -0,0 +1,73 @@
+"""`align.verify.lean`: a strict Lean 4 verifier (reward + evaluator), stdlib only.
+
+The Lean toolchain itself is an external binary installed by `scripts/lean_setup.sh`
+(docs/DEPENDENCY_POLICY.md, "Lean toolchain"); nothing here imports anything outside the
+standard library. See docs/LEAN_VERIFIER.md for the pin, the measurements and the
+pre-registered ceiling test.
+
+ toolchain.py locate the install, build the sanitized child environment
+ repl_pool.py persistent REPL workers, hard memory/time/output limits, watchdog
+ strict.py lexical gate + compile + Lean-side audit (axioms, statement bridge, ...)
+ recheck.py opt-in independent second check in a fresh REPL (docs/LEAN_RECHECK.md)
+ reward.py {0,1} accuracy producer for `align.rl.rewards.CompositeReward`
+"""
+
+from .repl_pool import (
+ HostBusy,
+ ReplCrash,
+ ReplError,
+ ReplLimits,
+ ReplMemoryExceeded,
+ ReplOutputTooLarge,
+ ReplPool,
+ ReplProcess,
+ ReplProtocolError,
+ ReplTimeout,
+ Worker,
+ default_workers,
+ plan_workers,
+ process_memory,
+)
+from .recheck import IndependentRechecker, RecheckVerdict
+from .reward import LeanAccuracy, LeanScore, extract_lean_code
+from .strict import (
+ AXIOM_WHITELIST,
+ LeanInfraError,
+ StrictConfig,
+ StrictResult,
+ StrictVerifier,
+ static_check,
+)
+from .toolchain import LeanToolchain, ToolchainNotFound, find_toolchain, toolchain_available
+
+__all__ = [
+ "AXIOM_WHITELIST",
+ "HostBusy",
+ "IndependentRechecker",
+ "LeanAccuracy",
+ "LeanInfraError",
+ "LeanScore",
+ "LeanToolchain",
+ "ReplCrash",
+ "ReplError",
+ "ReplLimits",
+ "ReplMemoryExceeded",
+ "ReplOutputTooLarge",
+ "ReplPool",
+ "ReplProcess",
+ "ReplProtocolError",
+ "RecheckVerdict",
+ "ReplTimeout",
+ "StrictConfig",
+ "StrictResult",
+ "StrictVerifier",
+ "ToolchainNotFound",
+ "Worker",
+ "default_workers",
+ "extract_lean_code",
+ "find_toolchain",
+ "plan_workers",
+ "process_memory",
+ "static_check",
+ "toolchain_available",
+]
diff --git a/code/align/verify/lean/recheck.py b/code/align/verify/lean/recheck.py
new file mode 100644
index 0000000000000000000000000000000000000000..a724507b16536421537c668e420104e6c61b0c68
--- /dev/null
+++ b/code/align/verify/lean/recheck.py
@@ -0,0 +1,310 @@
+"""An opt-in, INDEPENDENT second check for proofs the strict verifier accepted.
+
+The "comparator" pattern of the Lean community (Lean FRO's `comparator`, the Kimina/AXLE
+audits): never let the thing that judged the proof also be the only thing that judges the
+verdict. `StrictVerifier` already audits a candidate inside the same REPL worker that
+compiled it, with a Lean metaprogram (`AUDIT_PRELUDE`) we wrote. If that worker's state, or
+that audit, were wrong, the policy under RL would find the hole. This module re-derives the
+two facts that matter in a different process with different code:
+
+ (a) elaborate the ORIGINAL problem statement (the problem record's `statement`, never
+ anything the model wrote) as a reference `_cmp_ref ... := by sorry`;
+ (b) elaborate the accepted candidate on top of it, and confirm that the candidate
+ theorem's TYPE is definitionally equal to the reference type (the elaborator's
+ `isDefEq` AND the kernel's `Kernel.isDefEq`; both must say yes);
+ (c) `#print axioms `: Lean's own command, not `alignCollectAxioms`; the axiom set
+ must be a subset of {propext, Classical.choice, Quot.sound}.
+
+What is shared and what is not
+------------------------------
+Independent: the OS process (a new REPL is spawned for every check and killed after it, so
+no worker state, no environment ids, no cached reference env, no audit prelude survives
+from the first pass), the prelude (`CMP_PRELUDE`, a different metaprogram that never calls
+`alignCollectAxioms` or `addDecl`), the axiom walker (`#print axioms` instead of our own
+traversal), the defeq judge (`Meta.isDefEq` + `Kernel.isDefEq` instead of a kernel
+`addDecl` of a bridge theorem) and the reference text (rebuilt from the statement here).
+
+Shared, on purpose: the Lean toolchain, the tokenizer (`lex`) and `parse_statement`, which
+split the TRUSTED reference. This module does not re-implement the lexical gate; it is a
+second semantic check, not a second policy check. A policy-only reject in `strict`
+(`banned:*`, `target_modifier`, ...) is not re-judged here, so this module run alone on a
+policy violation may agree with a proof that strict rejects; with the flag on, strict runs
+first and the re-check only ever sees what strict accepted.
+
+Verdict. Both checks must agree. The re-check can only turn an accept into a reject:
+
+ strict reject -> reject (re-check never runs)
+ strict accept, re-check agrees -> accept
+ strict accept, re-check disagrees -> REJECT, reasons `recheck_disagree:`
+
+Failure policy: the candidate failing to elaborate, using `sorry`, or timing out in the
+fresh process is a disagreement (reject with a reason). A fresh process that cannot even
+build the trusted base environment (imports, prelude, reference header) is an
+infrastructure fault and raises `LeanInfraError`, exactly like strict: a dead toolchain
+must not read as "every proof was disagreed with".
+
+Stdlib only.
+"""
+
+from __future__ import annotations
+
+import json
+import re
+import secrets
+import time
+from dataclasses import dataclass, field
+from typing import Callable, Optional
+
+from .repl_pool import ReplError, ReplProcess, Worker
+from .strict import AXIOM_WHITELIST, LeanInfraError, lex, parse_statement
+
+CMP_REF = "_cmp_ref"
+
+# ============================================================================ Lean prelude
+#: Trusted comparison code, compiled into the FRESH process before any candidate text exists.
+#: Deliberately does not reuse anything from `strict.AUDIT_PRELUDE`.
+CMP_PRELUDE = r'''
+open Lean Elab Command Meta in
+elab "#align_cmp " nonce:str refId:ident candId:ident : command => do
+ let env ← getEnv
+ let tag := "ALIGN_CMP " ++ nonce.getString ++ " "
+ let some r := env.find? refId.getId
+ | logInfo m!"{tag}{(Json.mkObj [("ref_found", Json.bool false)]).compress}"; return
+ let some c := env.find? candId.getId
+ | logInfo m!"{tag}{(Json.mkObj [("ref_found", Json.bool true), ("cand_found", Json.bool false)]).compress}"; return
+ let kind := match c with
+ | .thmInfo _ => "theorem" | .defnInfo _ => "def" | .axiomInfo _ => "axiom"
+ | .opaqueInfo _ => "opaque" | .inductInfo _ => "inductive" | .ctorInfo _ => "ctor"
+ | .recInfo _ => "rec" | .quotInfo _ => "quot"
+ let n := r.levelParams.length
+ let lpOk := n == c.levelParams.length
+ let mut metaEq := false
+ let mut kernelEq := false
+ let mut err := ""
+ if lpOk then
+ let ps : List Level := (List.range n).map fun i => mkLevelParam (Name.mkSimple s!"cmpU{i}")
+ let tr := r.type.instantiateLevelParams r.levelParams ps
+ let tc := c.type.instantiateLevelParams c.levelParams ps
+ try
+ metaEq ← liftTermElabM (isDefEq tr tc)
+ catch e =>
+ err := ← e.toMessageData.toString
+ match Lean.Kernel.isDefEq env {} tr tc with
+ | .ok b => kernelEq := b
+ | .error _ => kernelEq := false
+ let j := Json.mkObj [("ref_found", Json.bool true), ("cand_found", Json.bool true), ("kind", kind),
+ ("level_params_match", Json.bool lpOk), ("meta_defeq", Json.bool metaEq),
+ ("kernel_defeq", Json.bool kernelEq), ("err", err)]
+ logInfo m!"{tag}{j.compress}"
+'''
+
+# ============================================================================ result types
+
+
+@dataclass
+class RecheckVerdict:
+ #: True only when every independent check passed.
+ agree: bool
+ #: Reasons for a disagreement (stable codes, no `recheck_disagree:` prefix).
+ reasons: tuple = ()
+ diagnostics: dict = field(default_factory=dict)
+ elapsed_s: float = 0.0
+
+ def summary(self) -> dict:
+ d = {"agree": self.agree, "reasons": list(self.reasons), "elapsed_s": round(self.elapsed_s, 3)}
+ d.update(self.diagnostics)
+ return d
+
+
+# ============================================================================ pure helpers
+# Each of these is a seam the unit tests mutate (see tests/test_lean_recheck.py): the logic
+# that matters lives in small named functions so that "mutant X survives" is a testable claim.
+
+_AX_DEPENDS = re.compile(r"^'(?P[^']+)' depends on axioms: \[(?P.*)\]\s*$", re.S)
+_AX_NONE = re.compile(r"^'(?P[^']+)' does not depend on any axioms\s*$")
+
+
+def parse_print_axioms(resp: dict, target: str) -> Optional[list[str]]:
+ """The axiom list from a `#print axioms` response, or None if it is absent/unparseable.
+
+ Only an info message whose quoted name is exactly `target` counts. More than one such
+ message, or a differently-named one, is ambiguity and parses to None: the caller treats
+ None as a REJECT (fail closed)."""
+ found: list[list[str]] = []
+ for m in resp.get("messages", []):
+ if not isinstance(m, dict) or m.get("severity") != "info":
+ continue
+ d = m.get("data")
+ if not isinstance(d, str):
+ continue
+ mn = _AX_NONE.match(d.strip())
+ if mn:
+ if mn.group("name") != target:
+ continue
+ found.append([])
+ continue
+ md = _AX_DEPENDS.match(d.strip())
+ if md and md.group("name") == target:
+ found.append([a.strip() for a in re.split(r",\s*", md.group("list").replace("\n", " ")) if a.strip()])
+ return found[0] if len(found) == 1 else None
+
+
+def parse_cmp(resp: dict, nonce: str) -> Optional[dict]:
+ """The nonce-tagged comparison message. Messages without OUR nonce are ignored, so a
+ candidate that prints a forged verdict (`logInfo` in a macro) cannot supply one."""
+ tag = f"ALIGN_CMP {nonce} "
+ for m in resp.get("messages", []):
+ d = m.get("data", "") if isinstance(m, dict) else ""
+ if isinstance(m, dict) and m.get("severity") == "info" and isinstance(d, str) and d.startswith(tag):
+ try:
+ return json.loads(d[len(tag) :])
+ except ValueError:
+ return None
+ return None
+
+
+def reference_signature(statement_sig_text: str, candidate_body: str) -> str:
+ """The signature the reference is elaborated from. ALWAYS the original problem
+ statement's: the model's own displayed statement is exactly what must not be trusted."""
+ return statement_sig_text
+
+
+def check_axioms(axioms: Optional[list[str]], whitelist=AXIOM_WHITELIST) -> list[str]:
+ """Reasons from the `#print axioms` result. None (unparseable) fails closed."""
+ if axioms is None:
+ return ["axioms_unparsed"]
+ return [("sorry" if a == "sorryAx" else f"axiom:{a}") for a in axioms if a not in whitelist]
+
+
+def check_types(cmp: Optional[dict]) -> list[str]:
+ """Reasons from the comparison message: the candidate's type must be defeq to the
+ reference's under BOTH the elaborator and the kernel."""
+ if cmp is None:
+ return ["cmp_missing"]
+ if not cmp.get("ref_found"):
+ return ["reference_missing"]
+ if not cmp.get("cand_found"):
+ return ["target_missing"]
+ out: list[str] = []
+ if cmp.get("kind") != "theorem":
+ out.append("not_theorem")
+ if not cmp.get("level_params_match"):
+ out.append("level_params_mismatch")
+ elif not (cmp.get("meta_defeq") and cmp.get("kernel_defeq")):
+ out.append("type_mismatch")
+ return out
+
+
+# ============================================================================ the re-checker
+
+
+def _msgs(resp: dict) -> list[dict]:
+ return [m for m in resp.get("messages", []) if isinstance(m, dict)]
+
+
+def _short(s: str, n: int = 300) -> str:
+ return s if len(s) <= n else s[: n - 3] + "..."
+
+
+class IndependentRechecker:
+ """Re-check one accepted (statement, code) pair in a FRESH REPL process.
+
+ `launcher(idx) -> ReplProcess` is the same factory the pool uses (so the re-check child
+ gets the same sanitized environment and resource limits), but every call to `check`
+ makes its own `Worker`, i.e. its own OS process, and kills it afterwards.
+ """
+
+ def __init__(
+ self,
+ launcher: Callable[[int], ReplProcess],
+ axiom_whitelist=AXIOM_WHITELIST,
+ timeout_s: float = 180.0,
+ prepend_missing_header: bool = False,
+ ) -> None:
+ self.launcher = launcher
+ self.axiom_whitelist = frozenset(axiom_whitelist)
+ self.timeout_s = timeout_s
+ self.prepend_missing_header = prepend_missing_header
+ self._n = 0
+
+ # -- the process this check runs in; a test seam (the "reuse a worker" mutant swaps it)
+ def _fresh_worker(self) -> Worker:
+ self._n += 1
+ return Worker(10_000 + self._n, self.launcher, max_requests=10**9)
+
+ def check(self, statement: str, code: Optional[str]) -> RecheckVerdict:
+ t0 = time.monotonic()
+ v = self._check(statement, code, t0 + self.timeout_s)
+ v.elapsed_s = time.monotonic() - t0
+ return v
+
+ def _split(self, statement: str, code: Optional[str]):
+ """(ref, body) or a RecheckVerdict that already disagrees."""
+ ref = parse_statement(statement)
+ if not ref.ok:
+ return RecheckVerdict(False, ("reference_unparseable",), {"detail": _short(ref.problem)})
+ if code is None or not code.strip():
+ return RecheckVerdict(False, ("no_code",))
+ lx = lex(code)
+ if lx.broken:
+ return RecheckVerdict(False, ("parse_error",), {"detail": lx.broken})
+ if self.prepend_missing_header and not any(t.text == "import" for t in lx.toks):
+ code = ref.header_text + "\n" + code
+ lx = lex(code)
+ nh = len(ref.header_toks)
+ if tuple(t.text for t in lx.toks[:nh]) != ref.header_toks:
+ return RecheckVerdict(False, ("header_mismatch",))
+ body = code[lx.toks[nh - 1].end :] if nh else code
+ return ref, body
+
+ def _check(self, statement: str, code: Optional[str], deadline: float) -> RecheckVerdict:
+ split = self._split(statement, code)
+ if isinstance(split, RecheckVerdict):
+ return split
+ ref, body = split
+ name = ref.name
+ w = self._fresh_worker()
+ diag: dict = {"pid": None}
+ try:
+ try:
+ hdr_env = w.header_env(ref.header_text, CMP_PRELUDE)
+ except ReplError as e:
+ raise LeanInfraError(f"re-check cannot build its base environment: {e}") from e
+ diag["pid"] = w.proc.pid if w.proc is not None else None
+
+ # (a) the reference, from the ORIGINAL statement, before any candidate text exists
+ sig = reference_signature(ref.sig_text, body)
+ r = w.command(f"theorem {CMP_REF} {sig} := by\n sorry", hdr_env, max(5.0, deadline - time.monotonic()))
+ errs = [m for m in _msgs(r) if m.get("severity") == "error"]
+ if errs or "env" not in r:
+ return RecheckVerdict(False, ("reference_invalid",), {**diag, "detail": _short(json.dumps(errs[:2] or r), 300)})
+ ref_env = r["env"]
+
+ # the candidate, in the environment that already holds the reference
+ resp = w.command(body, ref_env, max(1.0, deadline - time.monotonic()))
+ msgs = _msgs(resp)
+ errors = [m for m in msgs if m.get("severity") == "error"]
+ if errors or "env" not in resp:
+ diag["errors"] = [_short(m.get("data", ""), 240) for m in errors[:3]]
+ return RecheckVerdict(False, ("compile_error",), diag)
+ if resp.get("sorries") or any("sorry" in str(m.get("data", "")) for m in msgs if m.get("severity") == "warning"):
+ return RecheckVerdict(False, ("sorry",), diag)
+ cand_env = resp["env"]
+
+ # (c) Lean's own axiom report for the target
+ ax_resp = w.command(f"#print axioms _root_.{name}", cand_env, max(1.0, deadline - time.monotonic()))
+ axioms = parse_print_axioms(ax_resp, name)
+ diag["axioms"] = axioms
+ reasons = list(check_axioms(axioms, self.axiom_whitelist))
+
+ # (b) type of the target vs the type of the reference
+ nonce = secrets.token_hex(8)
+ cmp_resp = w.command(f'#align_cmp "{nonce}" {CMP_REF} {name}', cand_env, max(1.0, deadline - time.monotonic()))
+ cmp = parse_cmp(cmp_resp, nonce)
+ diag["cmp"] = cmp
+ reasons += check_types(cmp)
+ return RecheckVerdict(not reasons, tuple(dict.fromkeys(reasons)), diag)
+ except ReplError as e:
+ return RecheckVerdict(False, (f"repl_{e.code}",), {**diag, "detail": _short(str(e))})
+ finally:
+ w.discard()
diff --git a/code/align/verify/lean/repl_pool.py b/code/align/verify/lean/repl_pool.py
new file mode 100644
index 0000000000000000000000000000000000000000..3b68600dfe162a25f1e7d0852362a4c69dd3419a
--- /dev/null
+++ b/code/align/verify/lean/repl_pool.py
@@ -0,0 +1,946 @@
+"""A pool of persistent Lean REPL processes with hard resource limits.
+
+Why a pool of PERSISTENT processes
+----------------------------------
+`import Mathlib` costs ~3.2s warm (measured, docs/LEAN_VERIFIER.md) and maps ~3.4GB of
+.olean files. A verifier that spawns one `lean` per proof pays that on every proof;
+a REPL keeps the imported environment alive and elaborates the next proof on top of
+it in ~15ms. The imported-header environment is cached per worker (`Worker.header_env`),
+which is the import-keyed cache that gives Kimina's server its 1.94x.
+
+What the pool must survive (each guard below maps to a way this goes wrong)
+----------------------------------------------------------------------------
+* A proof that never terminates (`decide` on a huge term, `simp` loop with
+ `maxHeartbeats 0`): a per-request WALL-CLOCK deadline, enforced by SIGKILL of the whole
+ process group (the REPL never reaches a cooperative check). The worker is then
+ re-spawned lazily; the caller sees `ReplTimeout`, never a hang.
+* A proof that eats RAM: this box has NO SWAP and one OOM already wedged it
+ (/apps/CLAUDE.md). Two layers: (1) a watchdog that polls `/proc//smaps_rollup`
+ and kills on `Pss_Anon` over the cap. We cap ANONYMOUS memory, not RSS: RSS of a
+ Mathlib REPL is ~3.65GB of which 3.40GB is the file-backed, shared, read-only .olean
+ mapping (Pss_Anon is ~0.25GB) -- an RSS cap would either be uselessly high or kill every
+ healthy worker. (2) `prlimit --as` as a backstop for the allocation burst that fits
+ between two watchdog ticks.
+* A proof that prints without bound (`set_option trace...`): output is capped, the
+ worker is killed on overflow.
+* A REPL that dies (OOM-killer, stack overflow, SIGXFSZ): EOF on stdout -> `ReplCrash`
+ with the stderr tail; the next acquire re-spawns.
+* REPL environment growth: every command stores a new environment snapshot. Workers are
+ recycled after `max_requests` commands.
+* Zombies / orphans: children are session leaders (`start_new_session`), killed with
+ `killpg` and always `wait()`ed; `close()` and an `atexit` hook reap every live child.
+ Those hooks do not run when THIS process is SIGKILLed or OOM-killed, and a REPL that is busy
+ elaborating never reads the EOF on its stdin: it would run on, unwatched, holding GBs. So every
+ real child runs under the launcher of `sandbox.py` (`ReplProcess.supervise`, on by default),
+ a tiny single-threaded python that polls its parent PROCESS and SIGKILLs the REPL the moment the
+ parent is gone. (`PR_SET_PDEATHSIG` alone is not enough: it fires on death of the spawning
+ THREAD, and the pool spawns from short-lived executor threads.) `ReplPool.from_toolchain`
+ additionally puts the REPL in the namespace sandbox by default (`sandbox=True`).
+* Stale environment ids: a worker that dies between requests is re-spawned with an EMPTY
+ environment table; replaying an id from the dead process would make the real REPL answer
+ `Unknown environment.`, which the verifier would score as a compile error. `Worker.command`
+ raises `ReplCrash` instead (infra failure, not a verdict).
+
+The REPL does not flush stdout when it is a pipe (only a pty line-buffers it), so the
+binary is wrapped in `stdbuf -o0 -e0`; see toolchain.py.
+
+Stdlib only. Everything that talks to a real Lean binary goes through `ReplProcess`'s
+`argv`, so the unit tests drive the identical code with a fake REPL script.
+"""
+
+from __future__ import annotations
+
+import atexit
+import json
+import os
+import re
+import select
+import shutil
+import signal
+import subprocess
+import threading
+import time
+import weakref
+from collections import OrderedDict
+from concurrent.futures import ThreadPoolExecutor
+from contextlib import contextmanager
+from dataclasses import dataclass, field
+from typing import TYPE_CHECKING, Any, Callable, Iterator, Optional, Sequence
+
+if TYPE_CHECKING: # the sandbox module is imported lazily
+ from align.verify.lean.sandbox import SandboxSpec
+
+# --------------------------------------------------------------------------- errors
+
+
+class ReplError(Exception):
+ """Base class. `.code` is the failure-taxonomy token (ROADMAP P4-1)."""
+
+ code = "repl_error"
+
+
+class ReplTimeout(ReplError):
+ code = "timeout"
+
+
+class ReplCrash(ReplError):
+ code = "crash"
+
+
+class ReplMemoryExceeded(ReplError):
+ code = "memory"
+
+
+class ReplOutputTooLarge(ReplError):
+ code = "output_too_large"
+
+
+class ReplProtocolError(ReplError):
+ code = "protocol"
+
+
+class HeaderError(ReplProtocolError):
+ """A header-building command failed to elaborate. `stage` is `imports` / `prelude` (the verifier's own
+ infrastructure is broken) or `header` (one reference's `open`/`set_option` lines do not elaborate: a bad
+ item, not a dead verifier)."""
+
+ def __init__(self, stage: str, msg: str) -> None:
+ super().__init__(msg)
+ self.stage = stage
+
+
+class HostBusy(RuntimeError):
+ """The host cannot take a Lean pool right now (the /apps/CLAUDE.md preflight)."""
+
+
+# --------------------------------------------------------------------------- memory
+
+
+def read_smaps_rollup(pid: int) -> Optional[dict[str, int]]:
+ """Parse `/proc//smaps_rollup` into {field: kB}. None if the pid is gone.
+
+ This (not `VmRSS`) is the source of every memory number in this package: RSS
+ double-counts shared .olean pages across workers, PSS divides them by the number of
+ sharers, and `Pss_Anon` is the private heap that actually scales with the proof.
+ """
+ try:
+ with open(f"/proc/{pid}/smaps_rollup") as f:
+ text = f.read()
+ except OSError:
+ return None
+ out: dict[str, int] = {}
+ for line in text.splitlines():
+ key, sep, rest = line.partition(":")
+ if not sep:
+ continue
+ parts = rest.split()
+ if parts and parts[0].isdigit():
+ out[key.strip()] = int(parts[0])
+ return out or None
+
+
+def process_memory(pid: int) -> Optional[dict[str, float]]:
+ """Memory report for one process, in MiB. Keys: rss, pss, pss_anon, pss_file,
+ anon, private_dirty, vm_size, threads."""
+ sm = read_smaps_rollup(pid)
+ if sm is None:
+ return None
+ rep = {
+ "rss": sm.get("Rss", 0) / 1024.0,
+ "pss": sm.get("Pss", 0) / 1024.0,
+ "pss_anon": sm.get("Pss_Anon", sm.get("Anonymous", 0)) / 1024.0,
+ "pss_file": sm.get("Pss_File", 0) / 1024.0,
+ "anon": sm.get("Anonymous", 0) / 1024.0,
+ "private_dirty": sm.get("Private_Dirty", 0) / 1024.0,
+ "vm_size": 0.0,
+ "threads": 0.0,
+ }
+ try:
+ with open(f"/proc/{pid}/status") as f:
+ for line in f:
+ if line.startswith("VmSize:"):
+ rep["vm_size"] = int(line.split()[1]) / 1024.0
+ elif line.startswith("Threads:"):
+ rep["threads"] = float(line.split()[1])
+ except (OSError, ValueError, IndexError):
+ pass
+ return rep
+
+
+# --------------------------------------------------------------------------- limits
+
+
+@dataclass(frozen=True)
+class ReplLimits:
+ #: Kill the worker when its private (anonymous) memory exceeds this. See module doc
+ #: for why anonymous and not RSS. Measured healthy worker: ~0.25 GiB.
+ anon_cap_gb: float = 4.0
+ #: `prlimit --as` backstop. Virtual size of a healthy worker is ~4.2GiB (the .olean
+ #: mappings), so this is "cap + mappings + slack", not a working-set limit.
+ as_limit_gb: float = 16.0
+ #: Largest file the child may write (stderr spool, any file a proof creates).
+ fsize_mb: int = 256
+ nice: int = 5
+ max_output_bytes: int = 16 << 20
+ #: Wait granularity of the read loop (also the deadline resolution).
+ poll_s: float = 0.1
+ #: How often the watchdog samples smaps_rollup while a request is in flight.
+ mem_poll_s: float = 0.5
+ #: Consecutive unreadable memory samples (while the child is alive) after which the request is
+ #: killed as `memory`: an unmeasurable process must not run uncapped on a no-swap box.
+ mem_miss_limit: int = 3
+ #: Deadline for the cold `import Mathlib` header command.
+ header_timeout_s: float = 600.0
+
+
+# --------------------------------------------------------------------------- process
+
+_LIVE: "weakref.WeakSet[ReplProcess]" = weakref.WeakSet()
+
+
+def _reap_all() -> None: # pragma: no cover - interpreter shutdown path
+ for p in list(_LIVE):
+ try:
+ p.kill()
+ except Exception:
+ pass
+
+
+atexit.register(_reap_all)
+
+
+class ReplProcess:
+ """One persistent REPL child speaking blank-line-delimited JSON on stdin/stdout.
+
+ `argv` is the bare command. With `wrap=True` it is prefixed by
+ `nice -n N prlimit --as=.. --core=0 --fsize=.. -- stdbuf -o0 -e0`; each of those
+ `exec`s the next program, so `self.pid` is the REPL's own pid and the memory
+ watchdog reads the right /proc entry. `wrap=False` is for fake REPLs in tests.
+ """
+
+ def __init__(
+ self,
+ argv: Sequence[str],
+ env: dict[str, str],
+ cwd: Optional[str] = None,
+ limits: ReplLimits = ReplLimits(),
+ stderr_path: Optional[str] = None,
+ wrap: bool = True,
+ sandbox: Optional["SandboxSpec"] = None,
+ supervise: Optional[bool] = None,
+ ) -> None:
+ #: OS sandbox (align/verify/lean/sandbox.py: namespaces, allow-list root, no network). None = no isolation.
+ self.sandbox = sandbox
+ #: Run the child under the launcher/supervisor, which SIGKILLs the REPL when this PROCESS (not just the
+ #: spawning thread) disappears: SIGKILL/OOM of the trainer must not orphan a live Lean elaboration. On by
+ #: default for real (wrapped) children; implied by `sandbox`.
+ self.supervise = bool(sandbox is not None or (wrap if supervise is None else supervise))
+ self.argv = list(argv)
+ self.env = dict(env)
+ self.cwd = cwd
+ self.limits = limits
+ self.stderr_path = stderr_path
+ self.wrap = wrap
+ self.proc: Optional[subprocess.Popen] = None
+ self._stderr_f = None
+ self._poller: Optional[select.poll] = None
+ self.n_requests = 0
+ self.started_at = 0.0
+ self.peak_anon_mb = 0.0
+
+ # -- lifecycle
+
+ def _wrapped_argv(self) -> list[str]:
+ if not self.wrap:
+ return list(self.argv)
+ lim = self.limits
+ pre: list[str] = []
+ nice = shutil.which("nice")
+ if nice and lim.nice:
+ pre += [nice, "-n", str(int(lim.nice))]
+ prlimit = shutil.which("prlimit")
+ if prlimit:
+ pre += [
+ prlimit,
+ f"--as={int(lim.as_limit_gb * (1 << 30))}",
+ "--core=0",
+ f"--fsize={int(lim.fsize_mb) << 20}",
+ "--",
+ ]
+ stdbuf = shutil.which("stdbuf")
+ if stdbuf:
+ pre += [stdbuf, "-o0", "-e0"]
+ return pre + self.argv
+
+ def _spec(self) -> "SandboxSpec":
+ """The launcher spec: the caller's sandbox, or a lifetime-only one (no namespaces)."""
+ if self.sandbox is not None:
+ return self.sandbox
+ from align.verify.lean.sandbox import SandboxSpec
+
+ return SandboxSpec(scratch_dir=self.cwd or os.getcwd(), cwd=self.cwd, env=dict(self.env), isolate=False)
+
+ def _spawn_argv(self) -> list[str]:
+ """What `Popen` runs: `[launcher --] nice prlimit stdbuf repl`. The launcher is OUTSIDE the resource
+ limits, which run inside it."""
+ argv = self._wrapped_argv()
+ if not self.supervise:
+ return argv
+ from align.verify.lean.sandbox import sandbox_argv
+
+ return sandbox_argv(self._spec(), argv)
+
+ def _spawn_env(self) -> dict[str, str]:
+ if not self.supervise:
+ return self.env
+ from align.verify.lean.sandbox import helper_env
+
+ return helper_env() # the launcher gets nothing; the REPL's env travels in the spec
+
+ def memory_pid(self) -> Optional[int]:
+ """Host pid of the REPL itself. Under the supervisor `Popen.pid` is the launcher, whose
+ private memory is ~10 MB: the memory watchdog must read the REPL (its only child)."""
+ pid = self.pid
+ if pid is None or not self.supervise:
+ return pid
+ try:
+ with open(f"/proc/{pid}/task/{pid}/children") as f:
+ kids = [int(x) for x in f.read().split()]
+ if kids:
+ return kids[0]
+ except (OSError, ValueError):
+ pass
+ spec = self._spec()
+ if spec.pidfile:
+ from align.verify.lean.sandbox import inner_pid
+
+ return inner_pid(spec, pid)
+ return None
+
+ def start(self) -> None:
+ if self.alive:
+ return
+ self.kill()
+ if self.stderr_path:
+ os.makedirs(os.path.dirname(self.stderr_path), exist_ok=True)
+ self._stderr_f = open(self.stderr_path, "wb") # truncated per spawn
+ err = self._stderr_f
+ else:
+ err = subprocess.DEVNULL
+ self.proc = subprocess.Popen(
+ self._spawn_argv(),
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=err,
+ env=self._spawn_env(),
+ cwd=self.cwd,
+ start_new_session=True, # own process group => killpg reaps every descendant
+ close_fds=True,
+ )
+ assert self.proc.stdin is not None and self.proc.stdout is not None
+ os.set_blocking(self.proc.stdin.fileno(), False)
+ os.set_blocking(self.proc.stdout.fileno(), False)
+ self._poller = select.poll()
+ self._poller.register(self.proc.stdout.fileno(), select.POLLIN | select.POLLHUP | select.POLLERR)
+ self.n_requests = 0
+ self.started_at = time.monotonic()
+ self.peak_anon_mb = 0.0
+ _LIVE.add(self)
+
+ @property
+ def pid(self) -> Optional[int]:
+ return self.proc.pid if self.proc is not None else None
+
+ @property
+ def alive(self) -> bool:
+ return self.proc is not None and self.proc.poll() is None
+
+ def kill(self) -> None:
+ """SIGKILL the whole process group and reap. Idempotent."""
+ p, self.proc = self.proc, None
+ if p is not None:
+ try:
+ os.killpg(p.pid, signal.SIGKILL)
+ except (ProcessLookupError, PermissionError):
+ try:
+ p.kill()
+ except Exception:
+ pass
+ try:
+ p.wait(timeout=15)
+ except Exception:
+ pass
+ for f in (p.stdin, p.stdout):
+ try:
+ if f is not None:
+ f.close()
+ except Exception:
+ pass
+ if self._stderr_f is not None:
+ try:
+ self._stderr_f.close()
+ except Exception:
+ pass
+ self._stderr_f = None
+ self._poller = None
+
+ def stderr_tail(self, n: int = 2000) -> str:
+ if not self.stderr_path:
+ return ""
+ try:
+ with open(self.stderr_path, "rb") as f:
+ f.seek(0, os.SEEK_END)
+ f.seek(max(0, f.tell() - n))
+ return f.read().decode("utf-8", "replace")
+ except OSError:
+ return ""
+
+ def memory(self) -> Optional[dict[str, float]]:
+ pid = self.memory_pid()
+ return process_memory(pid) if pid else None
+
+ # -- I/O
+
+ def _write_all(self, data: bytes, deadline: float) -> None:
+ assert self.proc is not None and self.proc.stdin is not None
+ fd = self.proc.stdin.fileno()
+ view = memoryview(data)
+ w = select.poll()
+ w.register(fd, select.POLLOUT | select.POLLERR | select.POLLHUP)
+ while view:
+ left = deadline - time.monotonic()
+ if left <= 0:
+ self.kill()
+ raise ReplTimeout("timed out writing the request (child not reading)")
+ if not w.poll(min(left, self.limits.poll_s) * 1000):
+ continue
+ try:
+ n = os.write(fd, view)
+ except BlockingIOError:
+ continue
+ except (BrokenPipeError, OSError) as e:
+ rc = self.proc.poll()
+ tail = self.stderr_tail()
+ self.kill()
+ raise ReplCrash(f"REPL died before reading the request (rc={rc}): {e!r} {tail[-300:]}")
+ view = view[n:]
+
+ def request(self, payload: dict, timeout: float) -> dict:
+ """Send one JSON command, return the parsed JSON response.
+
+ Raises a `ReplError` subclass on timeout/crash/memory/oversize/garbage; in every
+ such case the child has ALREADY been killed (a REPL in an unknown state is never
+ reused: the next request would read the previous request's late answer).
+ """
+ if not self.alive:
+ raise ReplCrash("REPL is not running")
+ assert self.proc is not None and self.proc.stdout is not None
+ lim = self.limits
+ t0 = time.monotonic()
+ deadline = t0 + timeout
+ fd = self.proc.stdout.fileno()
+ # Unsolicited output (a late second answer to the PREVIOUS request) would be read as
+ # THIS request's answer and attributed to the wrong proof. A REPL that does that is
+ # corrupt: kill it instead of trusting the stream.
+ try:
+ stray = os.read(fd, 1 << 16)
+ except BlockingIOError:
+ stray = b""
+ if stray:
+ self.kill()
+ raise ReplProtocolError(f"unsolicited output before request: {stray[:80]!r}")
+ try:
+ data = (json.dumps(payload, ensure_ascii=False) + "\n\n").encode("utf-8")
+ except UnicodeEncodeError as e: # a lone surrogate: nothing was written, the child is untouched
+ raise ReplProtocolError(f"request is not encodable as UTF-8: {e}") from e
+ try:
+ return self._exchange(data, t0, deadline, fd, lim)
+ except BaseException:
+ # ANY exit that is not a returned answer (a ReplError, KeyboardInterrupt, MemoryError inside the
+ # watchdog...) leaves a pending answer on the pipe: a REPL in an unknown state is never reused.
+ self.kill()
+ raise
+
+ def _exchange(self, data: bytes, t0: float, deadline: float, fd: int, lim: "ReplLimits") -> dict:
+ timeout = deadline - t0
+ self._write_all(data, deadline)
+ buf = bytearray()
+ next_mem = t0 + lim.mem_poll_s
+ cap_mb = lim.anon_cap_gb * 1024.0
+ misses = 0
+ self.n_requests += 1
+ while True:
+ now = time.monotonic()
+ if now >= deadline:
+ self.kill()
+ raise ReplTimeout(f"no response within {timeout:.1f}s")
+ assert self._poller is not None
+ events = self._poller.poll(min(deadline - now, lim.poll_s) * 1000)
+ if events:
+ try:
+ chunk = os.read(fd, 1 << 16)
+ except BlockingIOError:
+ chunk = None
+ if chunk == b"":
+ rc = self.proc.poll()
+ if rc is None:
+ try:
+ rc = self.proc.wait(timeout=2)
+ except Exception:
+ rc = None
+ tail = self.stderr_tail()
+ self.kill()
+ raise ReplCrash(f"REPL exited (rc={rc}) mid-request. stderr tail: {tail[-400:]!r}")
+ if chunk:
+ buf += chunk
+ if len(buf) > lim.max_output_bytes:
+ self.kill()
+ raise ReplOutputTooLarge(f"response over {lim.max_output_bytes} bytes")
+ idx = buf.find(b"\n\n")
+ if idx >= 0:
+ if idx + 2 != len(buf):
+ self.kill()
+ raise ReplProtocolError("extra bytes after the response terminator")
+ break
+ if time.monotonic() >= next_mem:
+ next_mem = time.monotonic() + lim.mem_poll_s
+ mem = self.memory()
+ if mem is None:
+ misses += 1
+ if misses >= lim.mem_miss_limit:
+ self.kill()
+ raise ReplMemoryExceeded(f"memory unmeasurable for {misses} consecutive samples (fail closed)")
+ else:
+ misses = 0
+ if mem is not None:
+ self.peak_anon_mb = max(self.peak_anon_mb, mem["pss_anon"])
+ if mem["pss_anon"] > cap_mb:
+ self.kill()
+ raise ReplMemoryExceeded(
+ f"private memory {mem['pss_anon']:.0f}MiB over cap {cap_mb:.0f}MiB"
+ )
+ try:
+ return json.loads(buf.decode("utf-8", "replace"))
+ except ValueError as e:
+ self.kill()
+ raise ReplProtocolError(f"unparsable REPL response: {e}; head={bytes(buf[:200])!r}")
+
+
+# --------------------------------------------------------------------------- worker/pool
+
+
+_IMPORT_LINE = re.compile(r"\s*(?:--[^\n]*\n\s*)*import[ \t]+([A-Za-z0-9_.\u00ab\u00bb']+)")
+
+
+def split_imports(header: str) -> tuple[str, str]:
+ """Split a Lean header into (canonical `import` lines, everything after them).
+
+ Why the split exists (MEASURED, not hypothetical): a SECOND fresh `import Mathlib` in a
+ live REPL process cannot re-mmap the .olean files at their fixed base addresses (they are
+ already mapped), so Lean falls back to reading them into malloc'd memory: Pss_Anon went
+ from 0.25GiB to 4.1GiB and tripped the 4GiB watchdog. The pool therefore imports ONCE per
+ process and layers every header variant (`open`, `set_option`, the audit prelude) on top
+ of that import environment as ordinary commands, which cost milliseconds and kilobytes.
+ """
+ imports: list[str] = []
+ pos = 0
+ while True:
+ m = _IMPORT_LINE.match(header, pos)
+ if not m:
+ break
+ imports.append(f"import {m.group(1)}")
+ pos = m.end()
+ return "\n".join(imports), header[pos:]
+
+
+class Worker:
+ """One pool slot: a lazily (re)started REPL plus its per-process caches.
+
+ The caches (`header_envs`, `memo`) hold REPL environment ids, which are meaningless
+ in any other process; they are cleared whenever the process is killed or recycled.
+ """
+
+ MAX_HEADER_ENVS = 8
+ MAX_MEMO = 256
+
+ def __init__(
+ self,
+ idx: int,
+ launcher: Callable[[int], ReplProcess],
+ max_requests: int,
+ on_error: Optional[Callable[["ReplError"], None]] = None,
+ ) -> None:
+ self.idx = idx
+ self._on_error = on_error
+ self._launcher = launcher
+ self.max_requests = max_requests
+ self.proc: Optional[ReplProcess] = None
+ self.header_envs: "OrderedDict[tuple, int]" = OrderedDict()
+ #: the one import set this process has loaded, and the env id right after it
+ self._imports: Optional[str] = None
+ self._import_env: Optional[int] = None
+ self._layers: dict[tuple, int] = {}
+ self.memo: "OrderedDict[Any, Any]" = OrderedDict()
+ self.total_requests = 0
+ self.restarts = 0
+ self._restarted = False
+
+ def _drop_caches(self) -> None:
+ self.header_envs.clear()
+ self._layers.clear()
+ self._imports = None
+ self._import_env = None
+ self.memo.clear()
+
+ def ensure(self) -> ReplProcess:
+ self._restarted = False
+ if self.proc is None:
+ self.proc = self._launcher(self.idx)
+ if not self.proc.alive:
+ self._drop_caches()
+ self.proc.start()
+ self.restarts += 1
+ self._restarted = True
+ return self.proc
+
+ def discard(self) -> None:
+ if self.proc is not None:
+ self.proc.kill()
+ self._drop_caches()
+
+ def command(self, cmd: str, env: Optional[int] = None, timeout: float = 120.0) -> dict:
+ proc = self.ensure()
+ payload: dict[str, Any] = {"cmd": cmd}
+ if env is not None:
+ payload["env"] = env
+ self.total_requests += 1
+ try:
+ if env is not None and self._restarted:
+ # The process this env id belonged to died between requests and ensure() just spawned a new one:
+ # the id names nothing here (the real REPL would answer "Unknown environment." and the verifier
+ # would score it as a compile error). Surface it as the crash it is.
+ raise ReplCrash("REPL died between requests; the environment id is stale (new process)")
+ resp = proc.request(payload, timeout)
+ if "env" not in resp and "Unknown environment" in str(resp.get("message", "")):
+ proc.kill()
+ raise ReplCrash(f"REPL reported a stale environment id: {resp.get('message')}")
+ return resp
+ except ReplError as e:
+ self.discard() # normally the child is already dead (its env ids died with it); never keep a live one
+ if self._on_error is not None:
+ self._on_error(e)
+ raise
+
+ def _checked(self, resp: dict, what: str) -> int:
+ errs = [m for m in resp.get("messages", []) if isinstance(m, dict) and m.get("severity") == "error"]
+ if errs or "env" not in resp:
+ self.discard()
+ raise HeaderError(what, f"{what} failed to elaborate: {errs[:2] or resp}")
+ return resp["env"]
+
+ def header_env(self, header: str, prelude: str = "", timeout: Optional[float] = None) -> int:
+ """Environment id for `header` (imports, opens, options) with `prelude` (trusted
+ commands) compiled BEFORE the header's opens, cached per worker.
+
+ Layering: [imports, once per process] -> [prelude] -> [rest of header]. The prelude
+ goes before the header's `open ... Real Nat` so it elaborates in a pristine
+ namespace and cannot be affected by what the header opens. A header with a DIFFERENT
+ import set recycles the process (see `split_imports` for why a second import in the
+ same process is not an option).
+ """
+ imports, rest = split_imports(header)
+ key = (imports, prelude, rest)
+ if key in self.header_envs and self.proc is not None and self.proc.alive:
+ self.header_envs.move_to_end(key)
+ return self.header_envs[key]
+ proc = self.ensure()
+ t = timeout if timeout is not None else proc.limits.header_timeout_s
+ if self._import_env is not None and imports != self._imports:
+ self.discard()
+ proc = self.ensure()
+ if self._import_env is None:
+ env = self._checked(self.command(imports if imports else "-- no imports", None, t), "imports")
+ self._imports, self._import_env = imports, env
+ env = self._import_env
+ if prelude.strip():
+ lk = ("prelude", prelude)
+ if lk not in self._layers:
+ self._layers[lk] = self._checked(self.command(prelude, env, t), "prelude")
+ env = self._layers[lk]
+ if rest.strip():
+ lk = ("rest", prelude, rest)
+ if lk not in self._layers:
+ self._layers[lk] = self._checked(self.command(rest, env, t), "header")
+ env = self._layers[lk]
+ self.header_envs[key] = env
+ while len(self.header_envs) > self.MAX_HEADER_ENVS:
+ self.header_envs.popitem(last=False)
+ return env
+
+ def remember(self, key: Any, build: Callable[[], Any]) -> Any:
+ if key in self.memo:
+ self.memo.move_to_end(key)
+ return self.memo[key]
+ val = build()
+ self.memo[key] = val
+ while len(self.memo) > self.MAX_MEMO:
+ self.memo.popitem(last=False)
+ return val
+
+ def should_retire(self) -> bool:
+ return self.proc is not None and self.proc.alive and self.proc.n_requests >= self.max_requests
+
+ def memory(self) -> Optional[dict[str, float]]:
+ return self.proc.memory() if self.proc is not None and self.proc.alive else None
+
+
+def default_workers(cores: Optional[int] = None) -> int:
+ """`min(8, cores // 4)`, at least 1. 8 is where Kimina's measured scaling is already
+ sublinear; cores//4 leaves the rest of the box to the trainer."""
+ c = cores if cores is not None else (os.cpu_count() or 4)
+ return max(1, min(8, c // 4))
+
+
+def plan_workers(
+ requested: int,
+ per_worker_private_gb: float,
+ shared_gb: float,
+ mem_available_gb: float,
+ floor_gb: float = 45.0,
+) -> int:
+ """Largest n <= requested with `shared + n * private <= available - floor`.
+
+ The .olean mapping is shared across workers (counted once); each worker adds its
+ private heap, budgeted at the anon CAP (worst case), so the pool can never push
+ MemAvailable under the /apps/CLAUDE.md floor even if every worker hits its cap.
+ Raises `HostBusy` if not even one worker fits.
+ """
+ budget = mem_available_gb - floor_gb - shared_gb
+ n = int(budget // per_worker_private_gb) if per_worker_private_gb > 0 else requested
+ n = min(requested, n)
+ if n < 1:
+ raise HostBusy(
+ f"cannot fit one Lean worker: available {mem_available_gb:.1f}GB - floor "
+ f"{floor_gb:.0f}GB - shared {shared_gb:.1f}GB leaves {budget:.1f}GB "
+ f"< {per_worker_private_gb:.1f}GB"
+ )
+ return n
+
+
+def meminfo_available_gb() -> float:
+ with open("/proc/meminfo") as f:
+ for line in f:
+ if line.startswith("MemAvailable:"):
+ return int(line.split()[1]) / 1024**2
+ return 0.0
+
+
+@dataclass
+class PoolStats:
+ spawned: int = 0
+ timeouts: int = 0
+ crashes: int = 0
+ memory_kills: int = 0
+ oversize: int = 0
+ protocol: int = 0
+ requests: int = 0
+ recycled: int = 0
+ per_code: dict = field(default_factory=dict)
+
+
+class ReplPool:
+ """Bounded set of `Worker`s handed out one-at-a-time via `acquire()`.
+
+ Thread-safe. Workers start lazily (the first `acquire` pays the import), so
+ constructing a pool is free and a pool of 8 never exists as 8 imports unless 8
+ jobs run concurrently. `warm()` front-loads them.
+ """
+
+ def __init__(
+ self,
+ launcher: Callable[[int], ReplProcess],
+ n_workers: Optional[int] = None,
+ max_requests: int = 1000,
+ default_timeout_s: float = 120.0,
+ ) -> None:
+ self.n_workers = n_workers if n_workers is not None else default_workers()
+ if self.n_workers < 1:
+ raise ValueError("n_workers must be >= 1")
+ self.default_timeout_s = default_timeout_s
+ #: the process factory, kept so an independent re-check can spawn a FRESH process
+ self.launcher = launcher
+ self.stats = PoolStats()
+ self.workers = [Worker(i, launcher, max_requests, self._count) for i in range(self.n_workers)]
+ self._idle: list[Worker] = list(reversed(self.workers))
+ self._cv = threading.Condition()
+ self._closed = False
+
+ # -- construction from the real toolchain
+
+ @classmethod
+ def from_toolchain(
+ cls,
+ toolchain,
+ n_workers: Optional[int] = None,
+ limits: ReplLimits = ReplLimits(),
+ scratch_dir: Optional[str] = None,
+ max_requests: int = 1000,
+ default_timeout_s: float = 120.0,
+ preflight: bool = False,
+ sandbox: bool = True,
+ ) -> "ReplPool":
+ """`sandbox=True` (the DEFAULT) runs every REPL inside the namespace sandbox of `sandbox.py` (no network,
+ allow-list filesystem, own pid namespace, no capabilities): the elaborator executes code a policy wrote.
+ It FAILS CLOSED: if the kernel refuses the namespaces this raises `SandboxUnavailable` instead of silently
+ running unisolated. `sandbox=False` is the explicit opt-out (a lifetime-only supervisor remains, so the REPL
+ still dies with this process). For the pre-/post-SFT comparability of a run, use the same value in both."""
+ n = n_workers if n_workers is not None else default_workers()
+ if preflight:
+ n = preflight_pool(n, limits)
+ scratch = scratch_dir or os.path.join(str(toolchain.root), "tmp", "pool")
+ os.makedirs(scratch, exist_ok=True)
+ if sandbox:
+ from align.verify.lean.sandbox import SandboxUnavailable, sandbox_supported
+
+ ok, why = sandbox_supported()
+ if not ok:
+ raise SandboxUnavailable(
+ f"sandbox=True but namespaces are unavailable ({why}); pass sandbox=False to opt out explicitly"
+ )
+
+ def launcher(idx: int) -> ReplProcess:
+ spec = None
+ if sandbox:
+ from align.verify.lean.sandbox import SandboxSpec, minimal_env
+
+ wdir = os.path.join(scratch, f"sandbox{idx}")
+ os.makedirs(wdir, exist_ok=True)
+ spec = SandboxSpec(
+ scratch_dir=wdir,
+ ro_paths=tuple(str(toolchain.root / d) for d in ("elan", "repl", "workspace")),
+ cwd=toolchain.cwd,
+ pidfile=os.path.join(scratch, f"worker{idx}.pid"),
+ env=minimal_env(toolchain.child_env, wdir),
+ )
+ return ReplProcess(
+ toolchain.argv(),
+ toolchain.child_env,
+ cwd=toolchain.cwd,
+ limits=limits,
+ stderr_path=os.path.join(scratch, f"worker{idx}.stderr"),
+ sandbox=spec,
+ )
+
+ return cls(launcher, n, max_requests, default_timeout_s)
+
+ # -- scheduling
+
+ @contextmanager
+ def acquire(self, timeout: Optional[float] = None) -> Iterator[Worker]:
+ deadline = None if timeout is None else time.monotonic() + timeout
+ with self._cv:
+ while True:
+ if self._closed:
+ raise RuntimeError("pool is closed")
+ if self._idle:
+ w = self._idle.pop()
+ break
+ left = None if deadline is None else deadline - time.monotonic()
+ if left is not None and left <= 0:
+ raise TimeoutError("no free Lean worker")
+ self._cv.wait(left)
+ try:
+ yield w
+ finally:
+ try:
+ if w.should_retire():
+ w.discard()
+ self.stats.recycled += 1
+ finally:
+ with self._cv:
+ self.stats.requests = sum(x.total_requests for x in self.workers)
+ self.stats.spawned = sum(x.restarts for x in self.workers)
+ self._idle.append(w)
+ self._cv.notify()
+
+ def _count(self, e: ReplError) -> None:
+ s = self.stats
+ s.per_code[e.code] = s.per_code.get(e.code, 0) + 1
+ if isinstance(e, ReplTimeout):
+ s.timeouts += 1
+ elif isinstance(e, ReplCrash):
+ s.crashes += 1
+ elif isinstance(e, ReplMemoryExceeded):
+ s.memory_kills += 1
+ elif isinstance(e, ReplOutputTooLarge):
+ s.oversize += 1
+ elif isinstance(e, ReplProtocolError):
+ s.protocol += 1
+
+ def map(self, fn: Callable[[Worker, Any], Any], items: Sequence[Any]) -> list[Any]:
+ """Run `fn(worker, item)` over items on up to `n_workers` threads. Exceptions are
+ RETURNED in place (a crashed item must not abort a batch of 10k proofs)."""
+
+ def one(item: Any) -> Any:
+ try:
+ with self.acquire() as w:
+ return fn(w, item)
+ except BaseException as e: # noqa: BLE001 - returned, not swallowed
+ return e
+
+ with ThreadPoolExecutor(max_workers=self.n_workers) as ex:
+ return list(ex.map(one, items))
+
+ def warm(self, header: str, prelude: str = "", timeout: Optional[float] = None) -> list[Optional[BaseException]]:
+ """Start every worker and cache `header` in each (parallel). Returns per-worker
+ exception-or-None."""
+
+ def one(_i: int) -> Optional[BaseException]:
+ try:
+ with self.acquire() as w:
+ w.header_env(header, prelude, timeout)
+ return None
+ except BaseException as e: # noqa: BLE001
+ return e
+
+ # n threads each take one worker; they overlap because acquire() hands out
+ # distinct idle workers while earlier ones are still importing.
+ with ThreadPoolExecutor(max_workers=self.n_workers) as ex:
+ return list(ex.map(one, range(self.n_workers)))
+
+ def memory_report(self) -> list[Optional[dict[str, float]]]:
+ return [w.memory() for w in self.workers]
+
+ def close(self) -> None:
+ with self._cv:
+ self._closed = True
+ self._cv.notify_all()
+ for w in self.workers:
+ w.discard()
+
+ def __enter__(self) -> "ReplPool":
+ return self
+
+ def __exit__(self, *exc) -> None:
+ self.close()
+
+
+def preflight_pool(requested: int, limits: ReplLimits, shared_gb: float = 4.0) -> int:
+ """Apply the host rules and return the worker count that fits.
+
+ Delegates the three host checks (MemAvailable >= 45GB, load < 2x cores, disk queue
+ < 64) to `align.utils.guard.preflight_host_check` -- the same function the training
+ scripts use -- then shrinks `n` so that even every worker at its anon cap leaves the
+ 45GB floor intact.
+ """
+ try:
+ from align.utils.guard import preflight_host_check
+ from align.exit_codes import PreflightRefused
+
+ try:
+ preflight_host_check(strict=True)
+ except PreflightRefused as e:
+ raise HostBusy(str(e)) from e
+ except ImportError: # pragma: no cover - guard is part of this repo
+ pass
+ return plan_workers(requested, limits.anon_cap_gb, shared_gb, meminfo_available_gb())
diff --git a/code/align/verify/lean/reward.py b/code/align/verify/lean/reward.py
new file mode 100644
index 0000000000000000000000000000000000000000..730f8d798299323036cf303d8c751b4bcef50f03
--- /dev/null
+++ b/code/align/verify/lean/reward.py
@@ -0,0 +1,141 @@
+"""Lean accuracy as a reward term: {0, 1}, 1 only when the STRICT tier passes.
+
+The seam (read, not changed): `align.rl.rewards.CompositeReward.__call__(accuracy,
+format_score, response_len)` takes an `accuracy` float produced by a verifier layer and
+only composes/itemises it; `align.rl.assembly.assemble_rollout` consumes a
+`verify_batch(golds, preds) -> outcomes` surface whose outcomes expose `.name ==
+"CORRECT"`. This module provides an accuracy producer for both entry points and leaves
+the GRPO loss, `CompositeReward` and `assemble_rollout` untouched:
+
+ acc = LeanAccuracy(StrictVerifier(pool))
+ accuracy, result = acc.score(statement, completion) # float in {0,1}, diagnostics
+ terms = composite(accuracy=accuracy, format_score=f, response_len=n) # existing API
+
+Binary on purpose. Leanabell-V2 found shaped rewards did slightly WORSE than binary
+success; and any partial credit ("compiles but has sorry") is exactly the gradient a
+policy follows to the exploit. All the information that is NOT in the reward (why a proof
+was rejected) goes in `StrictResult.reasons/diagnostics`, returned separately, so the
+trainer can log exploit signatures (`axiom:*`, `statement_mismatch`, `banned:*`) without
+paying for them in the reward.
+
+Failure semantics: a proof that times out, OOMs or kills its REPL scores 0 (that is a
+property of the proof). A DEAD VERIFIER must not: `LeanInfraError` is raised when the base
+environment cannot be built, or when more than `max_infra_fraction` of a batch (of at
+least `min_batch`) died for infrastructure reasons -- otherwise a broken toolchain would
+feed the policy an all-zero reward that reads as "the model got worse".
+"""
+
+from __future__ import annotations
+
+import re
+from dataclasses import dataclass
+from typing import Optional, Sequence
+
+from .strict import LeanInfraError, StrictResult, StrictVerifier
+
+_FENCE = re.compile(r"```[ \t]*([A-Za-z0-9_+-]*)[^\n]*\n(.*?)```", re.S)
+_THINK = re.compile(r".*?", re.S)
+_LEAN_LANGS = {"lean", "lean4", "Lean", "Lean4", ""}
+_CODE_START = re.compile(r"\s*(import|open|set_option|theorem|lemma)\b")
+
+
+def extract_lean_code(completion: Optional[str]) -> Optional[str]:
+ """The model's final Lean code block, or None.
+
+ Policy: the LAST closed ```lean4/```lean (or bare) fence outside `` wins -- the
+ convention of the Goedel/DeepSeek prompts, where the plan precedes the code. A fence
+ that never closes (generation cut off by the length limit) is NOT salvaged: a
+ truncated proof is not a proof. Unfenced text counts only if it already starts like a
+ Lean file.
+ """
+ if not completion:
+ return None
+ text = _THINK.sub("", completion)
+ blocks = [m.group(2) for m in _FENCE.finditer(text) if m.group(1) in _LEAN_LANGS]
+ if blocks:
+ return blocks[-1]
+ if "```" not in text and _CODE_START.match(text):
+ return text
+ return None
+
+
+@dataclass(frozen=True)
+class LeanScore:
+ accuracy: float
+ result: StrictResult
+
+
+class LeanAccuracy:
+ def __init__(
+ self,
+ verifier: StrictVerifier,
+ max_infra_fraction: float = 0.5,
+ min_batch: int = 8,
+ ) -> None:
+ if not verifier.cfg.lexical_gate:
+ raise ValueError("StrictConfig(lexical_gate=False) is an ablation switch; not a reward")
+ self.verifier = verifier
+ self.max_infra_fraction = max_infra_fraction
+ self.min_batch = min_batch
+
+ # ---- single
+ def score(self, statement: str, completion: Optional[str]) -> LeanScore:
+ res = self.verifier.verify(statement, extract_lean_code(completion))
+ return LeanScore(res.accuracy, res)
+
+ def __call__(self, statement: str, completion: Optional[str]) -> float:
+ return self.score(statement, completion).accuracy
+
+ # ---- batch
+ def accuracy_batch(
+ self,
+ statements: Sequence[str],
+ completions: Sequence[Optional[str]],
+ progress=None,
+ ) -> tuple[list[float], list[StrictResult]]:
+ if len(statements) != len(completions):
+ raise ValueError(f"{len(statements)} statements vs {len(completions)} completions")
+ items = [(s, extract_lean_code(c)) for s, c in zip(statements, completions)]
+ results = self.verifier.verify_batch(items, progress=progress) if progress else self.verifier.verify_batch(items)
+ n_infra = sum(1 for r in results if r.infra_failure)
+ if len(results) >= self.min_batch and n_infra / len(results) > self.max_infra_fraction:
+ from collections import Counter
+
+ by = Counter(r.reasons[0] for r in results if r.infra_failure)
+ raise LeanInfraError(
+ f"{n_infra}/{len(results)} verifications died for infrastructure reasons "
+ f"{dict(by)}: either the pool is dead or the policy is emitting REPL-killing "
+ f"proofs; refusing to emit a batch of zeros that reads as 'model got worse'"
+ )
+ return [r.accuracy for r in results], results
+
+ # ---- seams
+ def verify_batch(self, golds: Sequence[str], preds: Sequence[Optional[str]]) -> list:
+ """`OutcomeVerifier.verify_batch` surface (golds = formal statements, preds =
+ completion text or already-extracted code) for `assemble_rollout`-style callers."""
+ from align.eval.verifier import Outcome # lazy: keeps this package import-light
+
+ _, results = self.accuracy_batch(golds, preds)
+ out = []
+ for r in results:
+ if r.accepted:
+ out.append(Outcome.CORRECT)
+ elif r.reasons == ("no_code",):
+ out.append(Outcome.NO_ANSWER)
+ elif r.undecided:
+ out.append(Outcome.UNDECIDED) # timeout / crash / memory: not a negative label
+ else:
+ out.append(Outcome.INCORRECT)
+ return out
+
+ def reward_terms(
+ self,
+ composite,
+ statement: str,
+ completion: Optional[str],
+ format_score: float,
+ response_len: int,
+ ) -> tuple[dict, StrictResult]:
+ """Itemised `CompositeReward` terms for one completion, plus the diagnostics."""
+ sc = self.score(statement, completion)
+ return composite(accuracy=sc.accuracy, format_score=format_score, response_len=response_len), sc.result
diff --git a/code/align/verify/lean/sandbox.py b/code/align/verify/lean/sandbox.py
new file mode 100644
index 0000000000000000000000000000000000000000..d6f43f768655914704fe73f7c946c9cf4f292ca2
--- /dev/null
+++ b/code/align/verify/lean/sandbox.py
@@ -0,0 +1,503 @@
+"""OS-level sandbox for the Lean REPL child (opt-in; stdlib + ctypes only).
+
+Why this exists
+---------------
+The static gate (`strict.py`) and the audit (`#print axioms`) decide whether a proof is
+ACCEPTED. They do not constrain what the elaborator DOES while it checks one. Lean is a
+programming language: `#eval`, `run_cmd`, `initialize`, a `macro`/`elab` that calls
+`IO.Process.spawn`, `IO.FS.writeFile`, or `IO.getStdout` all execute arbitrary code, as the
+user who runs the RL job, on a box that also holds the training checkpoints, `hf_cache/token`
+and `~/.ssh`. A policy that is rewarded for compiling proofs is an optimiser pointed at
+that surface: the cheapest "proof" that reads the reward cache, or kills the neighbouring
+training run (same uid => `kill(2)` and `ptrace(2)` are allowed), is one gradient step
+away. Until now the only containment was `prlimit` + a memory watchdog (resource
+exhaustion, not capability). This module closes that gap with kernel namespaces, using
+nothing that needs root (`unshare -Urn true` works here; there is no bwrap/firejail).
+
+What the child sees (each line is pinned by tests/test_lean_sandbox.py)
+------------------------------------------------------------------------
+* NETWORK new net namespace: only a DOWN loopback. `connect()` to anything, including
+ 127.0.0.1, fails (ENETUNREACH). Abstract AF_UNIX sockets are per-netns too.
+* FILESYSTEM an ALLOW-LIST root, not a read-only view of `/`. The child's `/` is a fresh
+ tmpfs (itself remounted read-only) containing only: the system dirs the
+ dynamic loader needs (`/usr`, plus the `/bin /lib /lib64 /sbin` symlinks),
+ a few `/etc` files, `/dev/{null,zero,full,random,urandom}`, a procfs of the
+ NEW pid namespace, the paths in `ro_paths` (the Lean install) bound READ-ONLY,
+ and ONE writable dir: `scratch_dir` (bound rw) plus a small noexec tmpfs on
+ `/tmp`. `/home`, `/root`, `/apps/*` other than the toolchain, checkpoints and
+ tokens simply do not exist. (A read-only view of `/` was the fallback plan; it
+ is unnecessary because unprivileged `pivot_root` into a tmpfs works here, and
+ it is strictly better: it also hides secrets rather than only protecting them
+ from writes.)
+* PIDS new pid namespace + new procfs: the child cannot see, signal or ptrace any
+ other process of this uid (the neighbouring training job). Its pid 1 is the
+ REPL; when it dies the kernel SIGKILLs everything else in the namespace, so a
+ `setsid`+double-fork daemon from a proof cannot outlive the worker.
+* PRIVILEGE `PR_SET_NO_NEW_PRIVS`; uid/gid mapped 1:1 to the caller (NOT fake root), so
+ the exec drops every capability: `CapEff=0` in the REPL. `setgroups` denied.
+* IPC/UTS new ipc and uts namespaces (no SysV shm/sem/queues shared with the host).
+* ENV only the variables the caller lists; `PATH` is pruned to entries that exist
+ in the new root; `HOME`/`TMPDIR` point at the private dirs.
+* LIFETIME own session/process group, `RLIMIT_CORE=0`, a supervisor that SIGKILLs the
+ REPL when the parent process (not just the spawning thread) disappears.
+
+What is NOT claimed
+-------------------
+* No seccomp filter: the child can still make any syscall the kernel namespaces allow.
+ Kernel bugs in those paths are out of scope. Namespaces are a capability boundary, not a
+ proof of confinement.
+* CPU/memory/file-size limits are still the pool's (`prlimit`, `smaps_rollup` watchdog).
+ The sandbox is composed OUTSIDE them: `sandbox -- nice prlimit stdbuf repl`.
+* GPUs are not exposed (no `/dev/nvidia*`): the REPL never needs them.
+* The toolchain is shared read-only between workers; scratch is per worker.
+
+Why a supervisor process instead of `preexec_fn`
+-------------------------------------------------
+`ReplPool` spawns from `ThreadPoolExecutor` threads: `preexec_fn` is unsafe in a threaded
+parent and `unshare(CLONE_NEWUSER)` requires a single-threaded caller. `PR_SET_PDEATHSIG`
+is documented (prctl(2)) to fire when the spawning THREAD exits, which for the pool is the
+end of every `ReplPool.map`; on this host's kernel (6.1) it was measured NOT to fire for a
+non-leader thread, but the guarantee differs across kernels, so nothing here depends on
+it either way: the supervisor polls its parent PROCESS (`getppid()` changes when the
+parent dies) and kills the REPL itself. So the launcher is a tiny single-threaded python
+(`python -I -S sandbox.py ...`) that does the unshares, forks the REPL into the new pid
+namespace, and stays outside as that supervisor. `Popen.pid` is therefore the supervisor,
+not the REPL; `inner_pid()` returns the REPL's host pid (used by the memory watchdog) from a
+pidfile that lives OUTSIDE the sandbox.
+"""
+
+from __future__ import annotations
+
+import ctypes
+import ctypes.util
+import errno
+import json
+import os
+import select
+import signal
+import stat
+import subprocess
+import sys
+import time
+from dataclasses import asdict, dataclass, field
+from typing import Optional, Sequence
+
+# --------------------------------------------------------------------------- constants
+
+CLONE_NEWNS = 0x00020000
+CLONE_NEWUTS = 0x04000000
+CLONE_NEWIPC = 0x08000000
+CLONE_NEWUSER = 0x10000000
+CLONE_NEWPID = 0x20000000
+CLONE_NEWNET = 0x40000000
+
+MS_RDONLY = 1
+MS_NOSUID = 2
+MS_NODEV = 4
+MS_NOEXEC = 8
+MS_REMOUNT = 32
+MS_NOATIME = 1024
+MS_NODIRATIME = 2048
+MS_BIND = 4096
+MS_REC = 16384
+MS_PRIVATE = 1 << 18
+MS_RELATIME = 1 << 21
+MNT_DETACH = 2
+
+PR_SET_PDEATHSIG = 1
+PR_SET_NO_NEW_PRIVS = 38
+
+#: statvfs.f_flag -> MS_* (the bits a bind-remount of a userns-locked mount must preserve)
+_ST_TO_MS = (
+ (getattr(os, "ST_NOSUID", 2), MS_NOSUID),
+ (getattr(os, "ST_NODEV", 4), MS_NODEV),
+ (getattr(os, "ST_NOEXEC", 8), MS_NOEXEC),
+ (getattr(os, "ST_NOATIME", 1024), MS_NOATIME),
+ (getattr(os, "ST_NODIRATIME", 2048), MS_NODIRATIME),
+ (getattr(os, "ST_RELATIME", 4096), MS_RELATIME),
+)
+
+#: Directories the dynamic loader / coreutils wrappers (`nice`, `prlimit`, `stdbuf`) need.
+DEFAULT_SYSTEM_DIRS = ("/usr",)
+#: Individual files (never directories) bound read-only from /etc when they exist.
+DEFAULT_ETC_FILES = (
+ "/etc/ld.so.cache",
+ "/etc/ld.so.conf",
+ "/etc/nsswitch.conf",
+ "/etc/passwd",
+ "/etc/group",
+ "/etc/localtime",
+)
+DEFAULT_DEVICES = ("null", "zero", "full", "random", "urandom")
+#: Environment variables that survive `minimal_env` (the REPL child's contract).
+ENV_KEEP = ("LEAN_PATH", "LD_LIBRARY_PATH", "LEAN_SYSROOT", "MALLOC_ARENA_MAX", "LANG", "LC_ALL", "PATH")
+
+
+class SandboxUnavailable(RuntimeError):
+ """The kernel refuses the namespaces we need (userns disabled, seccomp'd container...)."""
+
+
+@dataclass(frozen=True)
+class SandboxSpec:
+ """Everything the in-sandbox setup needs, JSON-serialisable (it travels in argv)."""
+
+ #: The ONLY writable directory (bound rw at the same path inside). Must exist.
+ scratch_dir: str
+ #: Extra read-only paths (the Lean install root). Files and dirs both fine.
+ ro_paths: tuple = ()
+ system_dirs: tuple = DEFAULT_SYSTEM_DIRS
+ etc_files: tuple = DEFAULT_ETC_FILES
+ devices: tuple = DEFAULT_DEVICES
+ #: chdir here after the pivot (must lie inside an allowed path).
+ cwd: Optional[str] = None
+ #: Where the supervisor writes the REPL's HOST pid. Keep it OUTSIDE scratch/ro_paths.
+ pidfile: Optional[str] = None
+ tmpfs_mb: int = 64
+ #: Environment handed to the final exec (already minimal; see `minimal_env`).
+ env: dict = field(default_factory=dict)
+ #: Supervisor poll period for "did my parent die" (also bounds exit-propagation lag).
+ parent_poll_s: float = 0.25
+ #: False = LIFETIME ONLY: no namespaces and no mounts (the explicit `sandbox=False` opt-out of the pool), but the
+ #: child is still forked under the supervisor, so it dies with the parent process. Never the default.
+ isolate: bool = True
+
+ def to_json(self) -> str:
+ d = asdict(self)
+ return json.dumps(d, sort_keys=True)
+
+ @staticmethod
+ def from_json(s: str) -> "SandboxSpec":
+ d = json.loads(s)
+ for k in ("ro_paths", "system_dirs", "etc_files", "devices"):
+ d[k] = tuple(d[k])
+ return SandboxSpec(**d)
+
+
+# --------------------------------------------------------------------------- parent side
+
+def minimal_env(env: dict, scratch_dir: str, keep: Sequence[str] = ENV_KEEP) -> dict:
+ """The environment the REPL gets inside the sandbox.
+
+ `keep` allow-list only (inherited secrets such as `HF_TOKEN`, `WANDB_API_KEY`,
+ `SSH_AUTH_SOCK` must not reach code a policy wrote); `HOME` -> scratch, `TMPDIR` ->
+ the in-sandbox `/tmp`. `PATH` is pruned inside the sandbox (entries under /home etc.
+ do not exist there).
+ """
+ out = {k: v for k, v in env.items() if k in keep}
+ out["HOME"] = scratch_dir
+ out["TMPDIR"] = "/tmp"
+ return out
+
+
+def sandbox_argv(spec: SandboxSpec, cmd: Sequence[str]) -> list[str]:
+ """argv that runs `cmd` inside the sandbox. `cmd[0]` must be an absolute path.
+
+ Intended use: `Popen(sandbox_argv(spec, pre + repl_argv), env=HELPER_ENV, cwd=...)`
+ where `HELPER_ENV` is `helper_env()` (NOT the lean env: LD_LIBRARY_PATH would then be
+ applied to the python helper too). The lean env travels in `spec.env`.
+ """
+ if not cmd or not os.path.isabs(cmd[0]):
+ raise ValueError("sandboxed command must be an absolute path")
+ return [sys.executable, "-I", "-S", os.path.abspath(__file__), spec.to_json(), "--", *cmd]
+
+
+def helper_env() -> dict:
+ """Environment for the launcher python itself: nothing the sandboxed code could use."""
+ return {"LANG": "C.UTF-8", "LC_ALL": "C.UTF-8"}
+
+
+def inner_pid(spec: SandboxSpec, supervisor_pid: Optional[int] = None) -> Optional[int]:
+ """Host pid of the REPL (pid 1 of the sandbox), or None before it has started.
+
+ Pass the supervisor's pid (`Popen.pid`) whenever it is known. The pidfile outlives the
+ process it names: after a kill/respawn (or if the new launcher dies in setup before
+ rewriting it) it still holds the PREVIOUS REPL's pid, and on a box that has been up for
+ weeks that number can already belong to an unrelated process -- the memory watchdog would
+ then bill the neighbouring training job's RSS to a healthy proof worker and kill it. The
+ REPL is, by construction, a direct child of its supervisor, so a pid whose parent is not
+ `supervisor_pid` is stale and is reported as None."""
+ if not spec.pidfile:
+ return None
+ try:
+ with open(spec.pidfile) as f:
+ pid = int(f.read().strip() or 0) or None
+ except (OSError, ValueError):
+ return None
+ if pid and supervisor_pid is not None:
+ try:
+ with open(f"/proc/{pid}/stat") as f:
+ ppid = int(f.read().rsplit(")", 1)[1].split()[1])
+ except (OSError, ValueError, IndexError):
+ return None
+ if ppid != supervisor_pid:
+ return None
+ return pid
+
+
+_SUPPORTED: Optional[tuple] = None
+
+
+def sandbox_supported(refresh: bool = False) -> tuple:
+ """(ok, reason). Actually runs the launcher on `/usr/bin/true` -- a `/proc` flag is not
+ proof (containers and LSMs deny namespaces in ways no sysctl advertises)."""
+ global _SUPPORTED
+ if _SUPPORTED is not None and not refresh:
+ return _SUPPORTED
+ import tempfile
+
+ base = os.environ.get("TMPDIR") or tempfile.gettempdir()
+ try:
+ with tempfile.TemporaryDirectory(dir=base) as d:
+ spec = SandboxSpec(scratch_dir=d, env=helper_env())
+ r = subprocess.run(
+ sandbox_argv(spec, ["/usr/bin/true"]),
+ env=helper_env(),
+ capture_output=True,
+ timeout=30,
+ )
+ ok = r.returncode == 0
+ _SUPPORTED = (ok, "" if ok else r.stderr.decode("utf-8", "replace")[-400:])
+ except Exception as e: # noqa: BLE001
+ _SUPPORTED = (False, repr(e))
+ return _SUPPORTED
+
+
+# --------------------------------------------------------------------------- child side
+
+_libc = None
+
+
+def _libc_():
+ global _libc
+ if _libc is None:
+ _libc = ctypes.CDLL(ctypes.util.find_library("c") or "libc.so.6", use_errno=True)
+ return _libc
+
+
+def _check(rc: int, what: str) -> None:
+ if rc != 0:
+ e = ctypes.get_errno()
+ raise OSError(e, f"{what}: {os.strerror(e)}")
+
+
+def _mount(source, target, fstype, flags, data=None) -> None:
+ def b(x):
+ return None if x is None else os.fsencode(x)
+
+ rc = _libc_().mount(b(source), b(target), b(fstype), ctypes.c_ulong(flags), b(data))
+ _check(rc, f"mount({source!r} -> {target!r}, flags={flags:#x})")
+
+
+def _preserved_flags(path: str) -> int:
+ f = os.statvfs(path).f_flag
+ out = 0
+ for st, ms in _ST_TO_MS:
+ if f & st:
+ out |= ms
+ return out
+
+
+def _bind(src: str, dst: str, rdonly: bool) -> None:
+ """Non-recursive bind (submounts of src are NOT carried: a nested rw mount would
+ punch a writable hole in a read-only tree) then remount with preserved flags."""
+ keep = _preserved_flags(src)
+ _mount(src, dst, None, MS_BIND, None)
+ flags = MS_BIND | MS_REMOUNT | keep | (MS_RDONLY if rdonly else 0)
+ _mount(None, dst, None, flags, None)
+
+
+def _ensure_target(newroot: str, path: str, is_dir: bool) -> str:
+ t = newroot + path
+ if is_dir:
+ os.makedirs(t, exist_ok=True)
+ else:
+ os.makedirs(os.path.dirname(t), exist_ok=True)
+ if not os.path.exists(t):
+ open(t, "wb").close()
+ return t
+
+
+def _bind_ro_into(newroot: str, path: str, done: set, rdonly: bool = True, as_path: Optional[str] = None) -> None:
+ """Make `path` appear (read-only) at the same path under newroot. A symlink is
+ recreated as a symlink and its target is bound too, so `/bin -> usr/bin` keeps working.
+
+ `as_path` binds the (already resolved) source at a DIFFERENT in-sandbox path: the spelling
+ the caller used for it (`LEAN_PATH`, `cwd`, `HOME` are all unresolved strings). Without
+ it a toolchain reached through a symlinked parent exists in the sandbox only under its
+ real name, and every later `chdir`/`open` of the configured spelling is ENOENT."""
+ dst = as_path or path
+ if dst in done:
+ return
+ done.add(dst)
+ if os.path.islink(path):
+ tgt = os.readlink(path)
+ link = newroot + path
+ os.makedirs(os.path.dirname(link), exist_ok=True)
+ if not os.path.lexists(link):
+ os.symlink(tgt, link)
+ real = os.path.normpath(os.path.join(os.path.dirname(path), tgt))
+ _bind_ro_into(newroot, real, done, rdonly)
+ return
+ if not os.path.exists(path):
+ return
+ is_dir = stat.S_ISDIR(os.stat(path).st_mode)
+ t = _ensure_target(newroot, dst, is_dir)
+ _bind(path, t, rdonly)
+
+
+def _write(path: str, data: str) -> None:
+ with open(path, "w") as f:
+ f.write(data)
+
+
+def _setup_mounts(spec: SandboxSpec) -> None:
+ """Runs in the pid-1 child. Builds the allow-list root and pivots into it."""
+ _mount(None, "/", None, MS_REC | MS_PRIVATE) # nothing we do may propagate to the host
+ scratch = os.path.realpath(spec.scratch_dir)
+ if not os.path.isdir(scratch):
+ # makedirs below would otherwise CREATE the path on the host and run "sandboxed"
+ # with a scratch the caller never made (and a typo'd path would silently work).
+ raise FileNotFoundError(f"scratch_dir does not exist: {spec.scratch_dir!r}")
+ newroot = os.path.join(scratch, ".sandbox-root")
+ os.makedirs(newroot, exist_ok=True)
+ _mount("tmpfs", newroot, "tmpfs", MS_NOSUID | MS_NODEV, "size=1m,mode=755")
+
+ done: set = set()
+ for d in spec.system_dirs:
+ _bind_ro_into(newroot, d, done)
+ for link in ("/bin", "/sbin", "/lib", "/lib64"): # usrmerge symlinks
+ if os.path.islink(link):
+ _bind_ro_into(newroot, link, done)
+ for p in spec.ro_paths:
+ real, spelled = os.path.realpath(p), os.path.abspath(p)
+ _bind_ro_into(newroot, real, done)
+ if spelled != real: # reached through a symlink: keep the caller's spelling valid too
+ _bind_ro_into(newroot, real, done, as_path=spelled)
+ for f in spec.etc_files:
+ _bind_ro_into(newroot, f, done)
+
+ # devices: individual nodes only. (nodev is a property of the SOURCE mount; /dev is
+ # devtmpfs without it, and the tmpfs they land on has nodev cleared for the dev dir.)
+ devdir = newroot + "/dev"
+ os.makedirs(devdir, exist_ok=True)
+ _mount("tmpfs", devdir, "tmpfs", MS_NOSUID | MS_NOEXEC, "size=64k,mode=755")
+ for dname in spec.devices:
+ src = "/dev/" + dname
+ if os.path.exists(src):
+ tgt = devdir + "/" + dname
+ open(tgt, "wb").close()
+ _mount(src, tgt, None, MS_BIND, None)
+ _mount(None, devdir, None, MS_REMOUNT | MS_RDONLY | MS_NOSUID | MS_NOEXEC, "size=64k,mode=755")
+
+ os.makedirs(newroot + "/proc", exist_ok=True)
+ _mount("proc", newroot + "/proc", "proc", MS_NOSUID | MS_NODEV | MS_NOEXEC)
+
+ os.makedirs(newroot + "/tmp", exist_ok=True)
+ _mount("tmpfs", newroot + "/tmp", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC,
+ f"size={int(spec.tmpfs_mb)}m,mode=1777")
+
+ # the ONE writable host directory (rw bind). Done last so nothing above can alias it.
+ for where in dict.fromkeys((scratch, os.path.abspath(spec.scratch_dir))): # real + spelled
+ _bind(scratch, _ensure_target(newroot, where, True), rdonly=False)
+ # scratch/.sandbox-root is a non-recursive bind artefact; hide it from the child.
+ # (Non-rec bind: the tmpfs mounted on it is not carried, so it is just an empty dir.)
+
+ # make the new root itself read-only, then pivot. pivot_root(".", ".") stacks the old
+ # root over the new one; detaching it leaves no fd or path to the host filesystem.
+ _mount(None, newroot, None, MS_REMOUNT | MS_RDONLY | MS_NOSUID | MS_NODEV, "size=1m,mode=755")
+ os.chdir(newroot)
+ _check(_libc_().syscall(155 if os.uname().machine == "x86_64" else 41, b".", b"."), "pivot_root")
+ _check(_libc_().umount2(b".", MNT_DETACH), "umount old root")
+ os.chdir("/")
+
+
+def _prctl(option: int, arg2: int = 0) -> None:
+ _check(_libc_().prctl(option, ctypes.c_ulong(arg2), 0, 0, 0), f"prctl({option})")
+
+
+def _inner(spec: SandboxSpec, cmd: Sequence[str], supervisor_pid: int) -> None:
+ # (getppid() is 0 here -- the parent lives outside our pid namespace -- so the tiny
+ # fork->prctl race cannot be checked; PDEATHSIG covers every later supervisor death.)
+ _prctl(PR_SET_PDEATHSIG, signal.SIGKILL)
+ if spec.isolate:
+ _setup_mounts(spec)
+ env = dict(spec.env)
+ if "PATH" in env:
+ env["PATH"] = ":".join(p for p in env["PATH"].split(":") if p and os.path.isdir(p))
+ if spec.cwd:
+ os.chdir(spec.cwd)
+ import resource
+
+ resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
+ _prctl(PR_SET_NO_NEW_PRIVS, 1)
+ # nothing but stdio crosses the exec
+ os.closerange(3, 1 << 16)
+ os.execve(cmd[0], list(cmd), env)
+
+
+def _supervise(inner: int, parent: int, spec: SandboxSpec) -> None:
+ """Outside the pid namespace. Never returns."""
+ if spec.pidfile:
+ tmp = spec.pidfile + ".tmp"
+ _write(tmp, str(inner))
+ os.replace(tmp, spec.pidfile)
+
+ def die(signum, _frm):
+ try:
+ os.kill(inner, signal.SIGKILL)
+ finally:
+ os._exit(128 + signum)
+
+ for s in (signal.SIGTERM, signal.SIGINT, signal.SIGHUP):
+ signal.signal(s, die)
+ pidfd = os.pidfd_open(inner)
+ while True:
+ r, _, _ = select.select([pidfd], [], [], spec.parent_poll_s)
+ if r:
+ _, status = os.waitpid(inner, 0)
+ os._exit(os.waitstatus_to_exitcode(status) % 256 if os.waitstatus_to_exitcode(status) >= 0
+ else 128 - os.waitstatus_to_exitcode(status))
+ if os.getppid() != parent: # reparented => the spawning PROCESS is gone
+ os.kill(inner, signal.SIGKILL)
+ os.waitpid(inner, 0)
+ os._exit(137)
+
+
+def run(spec: SandboxSpec, cmd: Sequence[str]) -> None:
+ """Entry point of the launcher process. Does not return (execs or exits)."""
+ parent = os.getppid()
+ try:
+ os.setsid()
+ except OSError:
+ pass # already a session leader (Popen(start_new_session=True))
+ uid, gid = os.getuid(), os.getgid()
+ flags = CLONE_NEWUSER | CLONE_NEWNET | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | CLONE_NEWPID
+ if spec.isolate:
+ try:
+ _check(_libc_().unshare(flags), "unshare")
+ _write("/proc/self/setgroups", "deny")
+ _write("/proc/self/uid_map", f"{uid} {uid} 1\n")
+ _write("/proc/self/gid_map", f"{gid} {gid} 1\n")
+ except OSError as e:
+ sys.stderr.write(f"sandbox unavailable: {e}\n")
+ os._exit(125)
+ me = os.getpid()
+ pid = os.fork() # first child after CLONE_NEWPID is pid 1 of the new namespace
+ if pid:
+ _supervise(pid, parent, spec)
+ try:
+ _inner(spec, cmd, me)
+ except BaseException as e: # noqa: BLE001 - must not fall through into the caller
+ sys.stderr.write(f"sandbox setup failed: {e!r}\n")
+ os._exit(126)
+
+
+def main(argv: Sequence[str]) -> None:
+ if len(argv) < 4 or argv[2] != "--":
+ sys.stderr.write("usage: sandbox.py SPEC_JSON -- CMD...\n")
+ os._exit(2)
+ run(SandboxSpec.from_json(argv[1]), argv[3:])
+
+
+if __name__ == "__main__":
+ main(sys.argv)
diff --git a/code/align/verify/lean/strict.py b/code/align/verify/lean/strict.py
new file mode 100644
index 0000000000000000000000000000000000000000..862665bc2cb185ae33fba12ece857c410703fcdb
--- /dev/null
+++ b/code/align/verify/lean/strict.py
@@ -0,0 +1,980 @@
+"""The STRICT tier: "it compiles" is not "it proves the statement".
+
+A candidate proof is ACCEPTED only if ALL of the following hold. Each rule is a measured
+or published way a policy under RL scores reward without proving anything (the audit
+literature: AXLE, Lean FRO's comparator, miniF2F audits; Pythagoras saw models rewrite
+statements to fit flawed proofs). This house has already scored a perfect policy 0/100
+because of a broken grader, and the mirror image -- a grader that accepts junk -- is
+worse, because the policy finds it.
+
+1. **Lexical gate** (pure Python, no Lean; `static_check`). No `sorry`/`admit`,
+ `native_decide`, `unsafe`/`partial`, `implemented_by`/`extern`/`export`, `axiom`/`opaque`,
+ no meta-programming (`elab`/`macro`/`syntax`/`notation`/`run_cmd`/`initialize`/...), no
+ `#`-commands, no `namespace`/`section`/`variable`/`attribute`/`local`/`scoped`, a
+ WHITELIST of `set_option` names (`debug.skipKernelTC` would switch the kernel off),
+ and, unless `allow_helper_defs`, no `def`/`abbrev`/`instance`. The candidate must start
+ with the reference's header token-for-token and contain exactly one declaration of the
+ reference's name, with no `private`/`protected`/attributes on it. In `statement_mode =
+ "strict"` its signature must equal the reference's token-for-token (whitespace and
+ comments ignored).
+2. **Compile** in a REPL environment that already contains the REFERENCE statement,
+ elaborated by us in a clean environment as `_align_ref`. No error, no `sorry` warning,
+ empty `sorries` list.
+3. **Audit** (Lean metaprogram, `AUDIT_PRELUDE`, trusted: it is compiled into the base
+ environment BEFORE any candidate text exists, so a candidate can add constants but not
+ change what the audit does):
+ * the target is a `theorem`;
+ * its axioms (our own transitive walk, same algorithm as `#print axioms`; Lean 4.9 has
+ no public `collectAxioms`) are within {propext, Quot.sound, Classical.choice}. This is
+ what catches `native_decide` (Lean.ofReduceBool) and `sorry` (sorryAx) even if rule 1's
+ lexer were fooled;
+ * **statement bridge**: the kernel is asked to check `theorem _align_bridge :
+ := `. That is the definition of "proves the
+ given statement", stronger than comparing strings and stronger than `isDefEq` in the
+ elaborator: it defeats a definition-hole (candidate shadows `sqrt`, or adds a
+ coercion/instance, so the SAME text means something else) because the reference type
+ was elaborated before the candidate existed;
+ * `debug.skipKernelTC` is off, and (`kernel_recheck=True`, opt-in: 2-3x cost) every local
+ theorem is re-added with kernel checking forced ON, so even if checking had been disabled
+ while the candidate elaborated the kernel still sees every proof term;
+ * no NEW local constant mentions `Lean.Elab/Meta/Macro/Parser/...`, `IO`, `unsafeCast`:
+ the signature of an `elab`/`macro` that slipped past the lexer. The baseline (the
+ prelude's own meta constants) is measured on the reference environment and subtracted.
+
+Lean-lexer desync is the residual risk of rule 1 (a construct my tokenizer reads as a
+comment/string but Lean reads as code). The tokenizer is Lean-faithful for nested block
+comments, line comments, string escapes, raw strings, char literals, `«»`, numerals (a numeral
+never swallows following letters: `1macro` is `1` + `macro`) and the longest-match symbols that
+end in `/` (`\\/`, `//`: in `a \\/-1` the `-1` is code, not a comment). A comment opener glued to
+any other symbol character is REFUSED as ambiguous (`parse_error`) instead of guessed; on ANY
+unusual construct (`s!"..{..}"` interpolation, raw string, `«»`, `'"'`) it flags an
+anomaly and the gate falls back to a RAW scan that rejects the banned words even inside
+comments/strings. Rule 3 is independent of the lexer.
+
+Failure taxonomy (`StrictResult.reasons`): `no_code`, `parse_error`, `unsupported_statement`,
+`header_mismatch`, `target_missing`, `target_modifier`, `statement_mismatch`,
+`banned:`, `compile_error`, `sorry`, `not_theorem`, `axiom:`, `bridge_failed`,
+`kernel_recheck_failed`, `meta_decl`, `skip_kernel_tc`, `timeout`, `crash`, `memory`,
+`output_too_large`, `protocol`, `reference_invalid`.
+
+Stdlib only.
+"""
+
+from __future__ import annotations
+
+import json
+import re
+import secrets
+import time
+from dataclasses import dataclass, field
+from functools import lru_cache
+from typing import Callable, Optional, Sequence
+
+from .repl_pool import HeaderError, ReplError, ReplPool, ReplProtocolError, Worker
+
+AXIOM_WHITELIST = frozenset({"propext", "Quot.sound", "Classical.choice"})
+REF_NAME = "_align_ref"
+#: Reported as an axiom when the Lean-side axiom walk ran out of fuel (see AUDIT_PRELUDE): never whitelisted.
+FUEL_SENTINEL = "_align_fuel_exhausted"
+#: Identifiers a candidate may not spell: our reference alias and bridge (a sorry-backed theorem lives under the first).
+INTERNAL_ID_PREFIXES = ("_align",)
+
+# ============================================================================ Lean prelude
+#: Trusted audit code, compiled into each worker's base environment (after the header, before
+#: any candidate). Verified against Lean 4.9.0-rc1 (the pinned toolchain); see tests.
+AUDIT_PRELUDE = r'''
+open Lean in
+def alignCollectAxioms (env : Environment) (start : Name) : Array Name := Id.run do
+ let mut visited : NameSet := {}
+ let mut axs : Array Name := #[]
+ let mut stack : Array Name := #[start]
+ let mut fuel := 2000000
+ while !stack.isEmpty && fuel > 0 do
+ fuel := fuel - 1
+ let n := stack.back
+ stack := stack.pop
+ if visited.contains n then continue
+ visited := visited.insert n
+ match env.find? n with
+ | none => pure ()
+ | some ci =>
+ stack := ci.type.getUsedConstants.foldl (fun s c => s.push c) stack
+ match ci with
+ | .axiomInfo _ => axs := axs.push n
+ | .defnInfo v => stack := v.value.getUsedConstants.foldl (fun s c => s.push c) stack
+ | .thmInfo v => stack := v.value.getUsedConstants.foldl (fun s c => s.push c) stack
+ | .opaqueInfo v => stack := v.value.getUsedConstants.foldl (fun s c => s.push c) stack
+ | .quotInfo _ => pure ()
+ | .ctorInfo v => stack := stack.push v.induct
+ | .recInfo v => stack := v.all.foldl (fun s c => s.push c) stack
+ | .inductInfo v => stack := v.ctors.foldl (fun s c => s.push c) stack
+ -- FAIL CLOSED: running out of fuel means the walk is INCOMPLETE; an incomplete axiom list must never
+ -- read as a clean one, so report a name that is not on any whitelist.
+ if !stack.isEmpty then
+ axs := axs.push `_align_fuel_exhausted
+ return axs
+
+open Lean in
+def alignReDecl (i : Nat) (ci : ConstantInfo) : TheoremVal :=
+ { name := Name.mkSimple ("_align_re" ++ toString i), levelParams := ci.levelParams, type := ci.type, value := ci.value! }
+
+open Lean Elab Command Meta in
+elab "#align_audit " nonce:str flags:num cand:ident ref:ident : command => do
+ let env0 ← getEnv
+ let candName := cand.getId
+ let refName := ref.getId
+ let tag := "ALIGN_AUDIT " ++ nonce.getString ++ " "
+ let some candCi := env0.find? candName
+ | logInfo m!"{tag}{(Json.mkObj [("found", Json.bool false)]).compress}"; return
+ let some refCi := env0.find? refName
+ | logInfo m!"{tag}{(Json.mkObj [("found", Json.bool true), ("ref_found", Json.bool false)]).compress}"; return
+ let kind := match candCi with
+ | .thmInfo _ => "theorem" | .defnInfo _ => "def" | .axiomInfo _ => "axiom"
+ | .opaqueInfo _ => "opaque" | .inductInfo _ => "inductive" | .ctorInfo _ => "ctor"
+ | .recInfo _ => "rec" | .quotInfo _ => "quot"
+ let axioms := alignCollectAxioms env0 candName
+ -- statement bridge: the kernel checks that the candidate inhabits the REFERENCE type
+ let mut bridge := false
+ let mut bridgeErr := ""
+ if candCi.levelParams.length != refCi.levelParams.length then
+ bridgeErr := "level_params_mismatch"
+ else
+ let decl := Declaration.thmDecl {
+ name := `_align_bridge, levelParams := refCi.levelParams, type := refCi.type,
+ value := mkConst candName (refCi.levelParams.map mkLevelParam) }
+ try
+ withScope (fun s => { s with opts := s.opts.setBool `debug.skipKernelTC false }) do
+ liftCoreM (addDecl decl)
+ bridge := true
+ catch e =>
+ bridgeErr := (← e.toMessageData.toString)
+ setEnv env0
+ -- local declarations that reference meta-programming or IO (slipped-past elab/macro)
+ let metaPrefixes : List Name := [`Lean.Elab, `Lean.Meta, `Lean.Macro, `Lean.MacroM, `Lean.Parser,
+ `Lean.ParserDescr, `Lean.PrettyPrinter, `Lean.Syntax, `Lean.TSyntax, `Lean.Environment,
+ `Lean.Core, `Lean.CoreM, `IO, `EIO, `BaseIO, `unsafeCast, `Lean.Compiler]
+ let isMeta (n : Name) : Bool := metaPrefixes.any fun p => p.isPrefixOf n
+ let mut metaDecls : Array String := #[]
+ let mut nLocal := 0
+ let mut localThms : Array (Name × ConstantInfo) := #[]
+ for (n, ci) in env0.constants.map₂.toList do
+ nLocal := nLocal + 1
+ let used := ci.type.getUsedConstants ++ (match ci.value? with | some v => v.getUsedConstants | none => #[])
+ if used.any isMeta then metaDecls := metaDecls.push n.toString
+ if let .thmInfo _ := ci then
+ if n != refName then localThms := localThms.push (n, ci)
+ -- kernel re-check of every local theorem with kernel checking forced ON
+ let mut recheckErr := ""
+ if flags.getNat &&& 1 == 1 then
+ let mut i := 0
+ for (_, ci) in localThms do
+ i := i + 1
+ try
+ withScope (fun s => { s with opts := s.opts.setBool `debug.skipKernelTC false }) do
+ liftCoreM (addDecl (Declaration.thmDecl (alignReDecl i ci)))
+ catch e =>
+ recheckErr := (← e.toMessageData.toString)
+ break
+ setEnv env0
+ let foreign := candCi.type.getUsedConstants.filter fun n => (env0.getModuleIdxFor? n).isNone
+ let skipTC := (← getOptions).getBool `debug.skipKernelTC false
+ let j := Json.mkObj [("found", Json.bool true), ("kind", kind),
+ ("axioms", toJson (axioms.map toString)), ("bridge", Json.bool bridge),
+ ("bridge_err", bridgeErr), ("foreign", toJson (foreign.map toString)),
+ ("skip_tc", Json.bool skipTC), ("meta_decls", toJson metaDecls),
+ ("n_local", toJson nLocal), ("recheck_err", recheckErr)]
+ logInfo m!"{tag}{j.compress}"
+'''
+
+# ============================================================================ tokenizer
+
+_SUBSCRIPT = set(range(0x2080, 0x208A)) | set(range(0x2090, 0x209D)) | set(range(0x1D62, 0x1D6B))
+_NUM = re.compile(
+ r"0[xX][0-9a-fA-F]+|0[bB][01]+|0[oO][0-7]+|[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?"
+)
+_RAW_OPEN = re.compile(r'r(#*)"')
+_NOT_LETTERLIKE = {"λ", "Π", "Σ"} # Lean keeps these as binders/keywords, not identifier chars
+
+_MULTI_SYMS = sorted(
+ [
+ ":=", "=>", "->", "<-", "<=", ">=", "!=", "==", "&&", "||", "::", "++", "...", "..",
+ "<;>", "|>.", "|>", "<|", "\\/", "/\\", "//", "@[", "#[", "∘", "≠", "←", "↔", "→", "≤", "≥", "⟨", "⟩",
+ ],
+ key=len,
+ reverse=True,
+)
+
+
+#: characters after which `--`/`/-` is unambiguously a comment opener (whitespace, identifier/number characters
+#: and closing brackets are never the tail of a longer Lean symbol token).
+_SAFE_BEFORE_COMMENT = frozenset(" \t\r\n_'!?)]}⟩⦄\"»")
+
+
+def _id_start(c: str) -> bool:
+ if c == "_":
+ return True
+ if c in _NOT_LETTERLIKE:
+ return False
+ return c.isalpha()
+
+
+def _id_rest(c: str) -> bool:
+ return (
+ _id_start(c)
+ or c.isdigit()
+ or c in ("'", "!", "?")
+ or ord(c) in _SUBSCRIPT
+ )
+
+
+@dataclass(frozen=True)
+class Tok:
+ kind: str # id | hash | num | sym | str | char
+ text: str
+ start: int
+ end: int
+
+
+@dataclass
+class Lexed:
+ toks: list[Tok]
+ #: Unusual constructs the faithful-lexer assumption is weakest on; non-empty => the
+ #: banned-word gate also runs on the RAW text.
+ anomalies: set[str] = field(default_factory=set)
+ #: Lean would reject the file (parse error); no point sending it to the REPL.
+ broken: Optional[str] = None
+
+
+def lex(src: str) -> Lexed:
+ """Tokenize Lean 4 source, dropping whitespace and comments, mirroring Lean's rules:
+ `--` line comments, NESTED `/- -/` block comments (incl. `/--` and `/-!`), string
+ literals with backslash escapes, raw strings `r#"..."#`, char literals, `«ident»`."""
+ toks: list[Tok] = []
+ an: set[str] = set()
+ broken: Optional[str] = None
+ glued = False
+ i, n = 0, len(src)
+ while i < n:
+ c = src[i]
+ if c.isspace():
+ i += 1
+ continue
+ if (c == "-" and src.startswith("--", i)) or (c == "/" and src.startswith("/-", i)):
+ if i > 0 and src[i - 1] not in _SAFE_BEFORE_COMMENT and not src[i - 1].isalnum():
+ # a comment opener glued to a symbol character: whether it opens a comment depends on
+ # Lean's token table (`\/-1`, `//-1`, `|--` are NOT comments there). We do not guess.
+ an.add("glued_comment")
+ glued = True
+ if c == "-" and src.startswith("--", i):
+ j = src.find("\n", i)
+ i = n if j < 0 else j + 1
+ continue
+ if c == "/" and src.startswith("/-", i):
+ depth, j = 1, i + 2
+ while j < n and depth:
+ if src.startswith("/-", j):
+ depth += 1
+ j += 2
+ elif src.startswith("-/", j):
+ depth -= 1
+ j += 2
+ else:
+ j += 1
+ if depth:
+ broken = "unterminated block comment"
+ i = j
+ continue
+ start = i
+ if c == "«":
+ j = src.find("»", i + 1)
+ if j < 0:
+ broken = "unterminated «"
+ j = n - 1
+ an.add("guillemet")
+ toks.append(Tok("id", src[i : j + 1], i, j + 1))
+ i = j + 1
+ continue
+ if c == "r" and (m := _RAW_OPEN.match(src, i)):
+ hashes = m.group(1)
+ close = '"' + hashes
+ j = src.find(close, m.end())
+ an.add("raw_string")
+ if j < 0:
+ broken = "unterminated raw string"
+ j = n - len(close)
+ toks.append(Tok("str", src[i : j + len(close)], i, j + len(close)))
+ i = j + len(close)
+ continue
+ if _id_start(c):
+ j = i + 1
+ while j < n and _id_rest(src[j]):
+ j += 1
+ # dotted continuation: a.b.c ; only when a letter (or «) follows the dot
+ while j + 1 < n and src[j] == "." and (_id_start(src[j + 1]) or src[j + 1] == "«"):
+ if src[j + 1] == "«":
+ k = src.find("»", j + 2)
+ an.add("guillemet")
+ j = n if k < 0 else k + 1
+ else:
+ j += 2
+ while j < n and _id_rest(src[j]):
+ j += 1
+ text = src[i:j]
+ if text in ("s!", "f!", "m!"):
+ # interpolation: `s! "{term}"` runs a TERM inside the string, and Lean allows whitespace, newlines
+ # and comments between the `!` and the quote. Do not look at what follows: always flag it.
+ an.add("interp_string")
+ toks.append(Tok("id", text, i, j))
+ i = j
+ continue
+ if c.isdigit():
+ # Lean: a numeral is digits (or 0x/0b/0o + digits) with an optional fraction and exponent. It does NOT
+ # swallow following letters: `1macro` is the numeral `1` then the keyword `macro`.
+ m = _NUM.match(src, i)
+ j = m.end() if m else i + 1 # a non-ASCII digit (`²`, Arabic-Indic): one token, never glued
+ toks.append(Tok("num", src[i:j], i, j))
+ i = j
+ continue
+ if c == '"':
+ j = i + 1
+ while j < n and src[j] != '"':
+ j += 2 if src[j] == "\\" else 1
+ if j >= n:
+ broken = "unterminated string"
+ j = n - 1
+ toks.append(Tok("str", src[i : j + 1], i, j + 1))
+ i = j + 1
+ continue
+ if c == "'":
+ m = re.match(r"'(\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|.)|[^\\'])'", src[i : i + 12])
+ if m:
+ if m.group(1) in ('"', "\\"):
+ an.add("char_quote")
+ toks.append(Tok("char", m.group(0), i, i + len(m.group(0))))
+ i += len(m.group(0))
+ continue
+ toks.append(Tok("sym", "'", i, i + 1))
+ i += 1
+ continue
+ if c == "#" and i + 1 < n and _id_start(src[i + 1]):
+ j = i + 1
+ while j < n and _id_rest(src[j]):
+ j += 1
+ toks.append(Tok("hash", src[i:j], i, j))
+ i = j
+ continue
+ for s in _MULTI_SYMS:
+ if src.startswith(s, i):
+ toks.append(Tok("sym", s, i, i + len(s)))
+ i += len(s)
+ break
+ else:
+ toks.append(Tok("sym", c, i, i + 1))
+ i += 1
+ _ = start
+ if glued and broken is None:
+ broken = "comment opener glued to a symbol (ambiguous tokenisation)"
+ return Lexed(toks, an, broken)
+
+
+# ============================================================================ banned constructs
+
+#: token -> reason label. Whole-token matches only (`sorry_lemma` is fine, `sorry` is not).
+BANNED_IDS: dict[str, str] = {
+ "sorry": "sorry", "admit": "sorry", "sorryAx": "sorry",
+ "native_decide": "native_decide",
+ "unsafe": "unsafe", "partial": "partial", "unsafeCast": "unsafe",
+ "implemented_by": "implemented_by", "extern": "extern", "export": "extern",
+ "initialize": "initialize", "builtin_initialize": "initialize",
+ "axiom": "axiom", "opaque": "axiom",
+ "elab": "meta", "elab_rules": "meta", "macro": "meta", "macro_rules": "meta",
+ "syntax": "meta", "declare_syntax_cat": "meta", "notation": "meta",
+ "infix": "meta", "infixl": "meta", "infixr": "meta", "prefix": "meta", "postfix": "meta",
+ "run_cmd": "meta", "run_elab": "meta", "run_meta": "meta", "run_tac": "meta",
+ "unif_hint": "meta", "deriving": "meta", "mutual": "meta", "register_simp_attr": "meta",
+ "notation3": "meta", "binder_predicate": "meta", "declare_simp_like_tactic": "meta",
+ "register_option": "meta", "register_label_attr": "meta", "register_tactic_tag": "meta",
+ "namespace": "namespace", "section": "namespace", "end": "namespace",
+ "variable": "variable", "universe": "variable", "attribute": "attribute",
+ "local": "local", "scoped": "local", "import": "import",
+ "structure": "decl", "inductive": "decl", "class": "decl",
+ "instance": "instance", "irreducible_def": "decl",
+ "exact?": "search", "apply?": "search", "rw?": "search", "hint": "search",
+ "decide?": "search", "ofReduceBool": "native_decide", "reduceBool": "native_decide",
+ "reduceNat": "native_decide", "lcProof": "axiom",
+}
+#: banned only unless `allow_helper_defs`.
+DEF_IDS = {"def": "def", "abbrev": "def"}
+#: identifier PREFIXES that name meta-programming / IO APIs (a tactic proof never spells them).
+BANNED_ID_PREFIXES = ("Lean.", "IO.", "System.", "Unsafe.", "EIO.", "BaseIO.")
+#: First dotted component that marks meta-programming even after an `open Lean`
+#: (`open Lean Elab Tactic` makes `Elab.Tactic.evalTactic` spellable without the `Lean.` prefix).
+BANNED_ID_ROOTS = frozenset({"Lean", "Elab", "Meta", "CoreM", "MetaM", "TacticM", "TermElabM",
+ "CommandElabM", "MacroM", "IO", "EIO", "BaseIO"})
+#: attribute names (inside `@[...]`) that change compilation, elaboration or the parser.
+BANNED_ATTRS = {
+ "implemented_by", "extern", "export", "csimp", "init", "builtin_init", "command_elab",
+ "term_elab", "tactic", "macro", "builtin_command_elab", "builtin_term_elab",
+ "builtin_tactic", "command_parser", "term_parser", "tactic_parser", "parser", "instance",
+ "default_instance", "unification_hint", "coe", "never_extract", "noinline", "inline",
+ "specialize", "simproc", "simprocs", "norm_num", "positivity", "gcongr", "mono",
+}
+#: Allowed `set_option` names (exact) and prefixes. Anything else is rejected: the dangerous
+#: one is `debug.skipKernelTC`; `trace.*` floods the output; `compiler.*`/`interpreter.*`
+#: change evaluation. maxRecDepth is bounded separately.
+SET_OPTION_EXACT = {
+ "maxHeartbeats", "maxRecDepth", "synthInstance.maxHeartbeats", "synthInstance.maxSize",
+ "maxSynthPendingDepth", "autoImplicit", "relaxedAutoImplicit", "pp.proofs",
+ "pp.proofs.withType", "pp.unicode.fun", "tactic.hygienic", "linter.unusedVariables",
+ "exponentiation.threshold", "backward.synthInstance.canonInstances",
+ "genInjectivity", "structureDiamondWarning", "quotPrecheck",
+}
+SET_OPTION_PREFIX = ("linter.", "push_neg.", "pp.", "simprocs")
+MAX_REC_DEPTH_LIMIT = 16384
+
+_RAW_BAN_RE = re.compile(
+ r"(? list[str]:
+ bad: list[str] = []
+ i = 0
+ while i < len(toks):
+ if toks[i].text == "@[":
+ depth, j = 1, i + 1
+ while j < len(toks) and depth:
+ t = toks[j].text
+ if t in ("[", "@["):
+ depth += 1
+ elif t == "]":
+ depth -= 1
+ elif toks[j].kind == "id" and (t in BANNED_ATTRS or t.rsplit(".", 1)[-1] in BANNED_ATTRS):
+ bad.append(f"banned:attr:{t}")
+ j += 1
+ i = j
+ else:
+ i += 1
+ return bad
+
+
+def _scan_set_options(toks: Sequence[Tok]) -> list[str]:
+ bad: list[str] = []
+ for i, t in enumerate(toks):
+ if t.kind == "id" and t.text == "set_option":
+ if i + 1 >= len(toks):
+ bad.append("banned:set_option:?")
+ continue
+ name = toks[i + 1].text
+ ok = name in SET_OPTION_EXACT or name.startswith(SET_OPTION_PREFIX)
+ if not ok:
+ bad.append(f"banned:set_option:{name}")
+ elif name == "maxRecDepth":
+ v = toks[i + 2].text if i + 2 < len(toks) else ""
+ if not v.isdigit() or int(v) > MAX_REC_DEPTH_LIMIT:
+ bad.append(f"banned:set_option:maxRecDepth>{MAX_REC_DEPTH_LIMIT}")
+ return bad
+
+
+def _canonical_id(text: str) -> str:
+ """The name Lean resolves, for ban matching: `_root_.` prefixes (any number) and `«»` quoting removed."""
+ text = text.replace("«", "").replace("»", "")
+ while text.startswith("_root_."):
+ text = text[len("_root_."):]
+ return text
+
+
+def scan_banned(src: str, lx: Lexed, allow_helper_defs: bool = False) -> list[str]:
+ """Reasons (`banned:*`) for forbidden constructs in `src` (deduplicated, ordered)."""
+ out: list[str] = []
+
+ def add(r: str) -> None:
+ if r not in out:
+ out.append(r)
+
+ for t in lx.toks:
+ if t.kind == "id":
+ text = _canonical_id(t.text)
+ lab = BANNED_IDS.get(text)
+ if lab is None and not allow_helper_defs:
+ lab = DEF_IDS.get(text)
+ if lab is None and (text.startswith(BANNED_ID_PREFIXES) or text.split(".", 1)[0] in BANNED_ID_ROOTS):
+ lab = "meta_api"
+ if lab is None and text.startswith(INTERNAL_ID_PREFIXES):
+ lab = "align_internal"
+ if lab is not None:
+ add(f"banned:{lab}" if lab != "meta_api" else f"banned:meta_api:{text.split('.')[0]}")
+ elif t.kind == "hash":
+ add(f"banned:#{t.text[1:]}")
+ for r in _scan_attributes(lx.toks) + _scan_set_options(lx.toks):
+ add(r)
+ if lx.anomalies:
+ # Weakest-assumption fallback: the faithful lexer could be out of sync with Lean's,
+ # so also reject banned words appearing ANYWHERE in the raw text (comments and
+ # strings included). Conservative on purpose; costs only exotic-but-legal inputs.
+ for m in _RAW_BAN_RE.finditer(src):
+ w = m.group(0)
+ lab = BANNED_IDS.get(w, w.lstrip("#"))
+ add(f"banned:{lab}")
+ return out
+
+
+# ============================================================================ statement parsing
+
+_DECL_KW = {"theorem", "lemma"}
+_OTHER_CMD_KW = {
+ "def", "abbrev", "instance", "example", "structure", "inductive", "class", "axiom",
+ "opaque", "namespace", "section", "variable", "universe", "mutual", "private",
+ "protected", "noncomputable", "nonrec",
+}
+_OPEN = {"(": ")", "[": "]", "{": "}", "⟨": "⟩", "⦃": "⦄", "@[": "]"}
+_CLOSE = {")", "]", "}", "⟩", "⦄"}
+
+
+def find_sig_end(toks: Sequence[Tok], start: int) -> Optional[int]:
+ """Index of the `:=` that ends a declaration signature, scanning from `start`.
+
+ The naive "first `:=`" is wrong (roadmap P4-1 warns about it): `:=` also appears inside
+ binders `(x : Nat := 3)`, structure instances `{ x := 1 }`, and `let x := ..; body` in
+ the TYPE. We track bracket depth and skip one `:=` per pending `let`/`have`.
+ """
+ depth = 0
+ pending = 0
+ for j in range(start, len(toks)):
+ t = toks[j]
+ if t.text in _OPEN:
+ depth += 1
+ elif t.text in _CLOSE:
+ depth = max(0, depth - 1)
+ elif depth == 0 and t.kind == "id" and t.text in ("let", "have"):
+ pending += 1
+ elif depth == 0 and t.text == ":=":
+ if pending:
+ pending -= 1
+ else:
+ return j
+ elif t.kind == "id" and t.text in _DECL_KW and j > start:
+ return None
+ return None
+
+
+@dataclass(frozen=True)
+class StatementParse:
+ ok: bool
+ problem: str = ""
+ header_text: str = ""
+ header_toks: tuple = ()
+ name: str = ""
+ sig_text: str = ""
+ sig_toks: tuple = ()
+
+
+@lru_cache(maxsize=4096)
+def parse_statement(src: str) -> StatementParse:
+ """Split a reference formal statement into (header, name, signature).
+
+ Supported shape (miniF2F / Goedel / DeepSeek / PutnamBench-style): header commands
+ (`import`, `set_option`, `open`) then ONE `theorem`/`lemma` ending `:= by sorry`.
+ Anything else -> `ok=False` with a reason; the verifier then scores 0 and says why,
+ instead of guessing."""
+ lx = lex(src)
+ if lx.broken:
+ return StatementParse(False, f"reference does not lex: {lx.broken}")
+ toks = lx.toks
+ k = next(
+ (
+ i
+ for i, t in enumerate(toks)
+ if t.text == "@[" or (t.kind == "id" and (t.text in _DECL_KW or t.text in _OTHER_CMD_KW))
+ ),
+ None,
+ )
+ if k is None or toks[k].text not in _DECL_KW:
+ return StatementParse(False, "reference has no plain `theorem`/`lemma` after its header")
+ for t in toks[:k]:
+ if t.kind == "id" and t.text in ("def", "abbrev", "instance", "variable", "namespace", "section", "universe"):
+ return StatementParse(False, f"reference header contains `{t.text}`")
+ if k + 1 >= len(toks) or toks[k + 1].kind != "id":
+ return StatementParse(False, "reference theorem has no name")
+ j = find_sig_end(toks, k + 2)
+ if j is None:
+ return StatementParse(False, "reference theorem has no `:=`")
+ if any(t.kind == "id" and t.text in _DECL_KW for t in toks[j:]):
+ return StatementParse(False, "reference contains more than one theorem")
+ hdr_end = toks[k - 1].end if k > 0 else 0
+ sig_toks = tuple(t.text for t in toks[k + 2 : j])
+ sig_text = src[toks[k + 2].start : toks[j].start] if j > k + 2 else ""
+ return StatementParse(
+ True,
+ header_text=src[:hdr_end],
+ header_toks=tuple(t.text for t in toks[:k]),
+ name=toks[k + 1].text,
+ sig_text=sig_text,
+ sig_toks=sig_toks,
+ )
+
+
+# ============================================================================ static check
+
+
+@dataclass(frozen=True)
+class StrictConfig:
+ axiom_whitelist: frozenset = AXIOM_WHITELIST
+ #: "strict": signature token-equal to the reference AND kernel bridge.
+ #: "defeq": kernel bridge only (allows a restated-but-defeq signature).
+ statement_mode: str = "strict"
+ allow_helper_defs: bool = False
+ #: Wall-clock budget for compile + audit of ONE candidate (seconds).
+ proof_timeout_s: float = 180.0
+ max_code_chars: int = 200_000
+ #: Re-add every local theorem with kernel checking forced on (see module docstring).
+ #: Measured cost in isolation: +0.04-0.12 s per proof on `nlinarith`-heavy proofs whose
+ #: elaboration takes 20-43 s (lean_workbook_10009/10013), i.e. negligible; on the pinned
+ #: Lean there is no way to switch kernel checking off short of meta-programming
+ #: (`debug.skipKernelTC` does not exist in 4.9.0-rc1), so this is defence in depth that
+ #: guards a pin upgrade. (An earlier "2-3x" reading was a contended-host artefact and was
+ #: retracted: see docs/LEAN_VERIFIER.md section 7.)
+ kernel_recheck: bool = True
+ #: Prepend the reference header when the candidate has none at all. Off by default:
+ #: "the completion must be a complete file" is the unambiguous contract.
+ prepend_missing_header: bool = False
+ #: ABLATION ONLY. False skips the banned-construct scan, the target-modifier rule and the
+ #: signature-token rule, leaving header split + target lookup + the Lean-side audit. The
+ #: ceiling test uses it to measure how much the semantic layer catches by itself;
+ #: `LeanAccuracy` refuses a verifier built with it off.
+ lexical_gate: bool = True
+ #: OPT-IN second opinion (align/verify/lean/recheck.py, docs/LEAN_RECHECK.md). After the
+ #: strict tier accepts, re-elaborate the ORIGINAL statement and the candidate in a FRESH
+ #: REPL process and require (type defeq) AND (`#print axioms` within the whitelist).
+ #: Costs one `import Mathlib` + a second elaboration of the proof per accepted proof.
+ #: Default off: with it off nothing below changes by a byte.
+ independent_recheck: bool = False
+ #: Wall-clock budget of the re-check alone (seconds); `None` = `proof_timeout_s`.
+ recheck_timeout_s: Optional[float] = None
+
+ def __post_init__(self) -> None:
+ if self.statement_mode not in ("strict", "defeq"):
+ raise ValueError(f"statement_mode must be 'strict' or 'defeq', got {self.statement_mode!r}")
+
+
+@dataclass(frozen=True)
+class Plan:
+ name: str
+ header: str # the reference header (imports + opens + options), compared token-for-token
+ ref_cmd: str
+ body: str # candidate text after the header
+ reasons: tuple = ()
+
+
+@dataclass
+class StrictResult:
+ accepted: bool
+ reasons: tuple[str, ...]
+ stage: str
+ diagnostics: dict = field(default_factory=dict)
+ elapsed_s: float = 0.0
+
+ @property
+ def accuracy(self) -> float:
+ return 1.0 if self.accepted else 0.0
+
+ @property
+ def undecided(self) -> bool:
+ """No verdict about the PROOF: it timed out, or the verifier/host failed. Scores 0 as a reward (never an
+ accept) but must never be counted as a negative label (docs/SOTA_PLAN.md: timeouts are undecided)."""
+ return any(r in ("timeout", "crash", "protocol", "memory", "output_too_large") for r in self.reasons)
+
+ @property
+ def infra_failure(self) -> bool:
+ return any(r in ("crash", "protocol", "memory", "output_too_large") for r in self.reasons)
+
+
+def static_check(statement: str, code: Optional[str], cfg: StrictConfig = StrictConfig()) -> tuple[Optional[Plan], list[str], dict]:
+ """The pure-Python half. Returns (plan or None, reasons, diagnostics). `plan` is
+ non-None only when every lexical rule passed and Lean should now be consulted."""
+ diag: dict = {}
+ if code is None or not code.strip():
+ return None, ["no_code"], diag
+ if len(code) > cfg.max_code_chars:
+ return None, ["parse_error"], {"detail": f"code over {cfg.max_code_chars} chars"}
+ try:
+ code.encode("utf-8")
+ except UnicodeEncodeError:
+ # a lone surrogate (broken detokenisation) is not Lean source and cannot be sent to the REPL as UTF-8: this
+ # item scores 0; it must not abort the whole batch.
+ return None, ["parse_error"], {"detail": "code is not valid unicode text (lone surrogate)"}
+ try:
+ statement.encode("utf-8")
+ except UnicodeEncodeError:
+ return None, ["unsupported_statement"], {"detail": "statement is not valid unicode text"}
+ ref = parse_statement(statement)
+ if not ref.ok:
+ return None, ["unsupported_statement"], {"detail": ref.problem}
+ lx = lex(code)
+ if lx.broken:
+ return None, ["parse_error"], {"detail": lx.broken}
+ toks = lx.toks
+ nh = len(ref.header_toks)
+ if cfg.prepend_missing_header and not any(t.text == "import" for t in toks):
+ code = ref.header_text + "\n" + code
+ lx = lex(code)
+ toks = lx.toks
+ if tuple(t.text for t in toks[:nh]) != ref.header_toks:
+ return None, ["header_mismatch"], diag
+ body_toks = toks[nh:]
+ body_start = toks[nh - 1].end if nh else 0
+ body = code[body_start:]
+
+ reasons: list[str] = []
+ # banned constructs are scanned over the BODY only (the header was just proven equal to
+ # the trusted reference header, which legitimately contains `import` and `set_option`).
+ body_lx = Lexed(list(body_toks), lx.anomalies, None)
+ if cfg.lexical_gate:
+ reasons += scan_banned(code[body_start:], body_lx, cfg.allow_helper_defs)
+
+ targets = [
+ i
+ for i, t in enumerate(body_toks)
+ if t.kind == "id" and t.text in _DECL_KW and i + 1 < len(body_toks) and body_toks[i + 1].text == ref.name
+ ]
+ if not targets:
+ reasons.append("target_missing")
+ elif len(targets) > 1:
+ reasons.append("target_missing")
+ diag["detail"] = "target declared more than once"
+ else:
+ i = targets[0]
+ j = i - 1
+ # modifiers / attributes attached to the target
+ mods: list[str] = []
+ while j >= 0 and body_toks[j].text in ("private", "protected", "noncomputable", "nonrec"):
+ mods.append(body_toks[j].text)
+ j -= 1
+ if j >= 0 and body_toks[j].text == "]":
+ mods.append("@[...]")
+ if mods and cfg.lexical_gate:
+ reasons.append("target_modifier")
+ if cfg.statement_mode == "strict" and cfg.lexical_gate:
+ e = find_sig_end(body_toks, i + 2)
+ if e is None:
+ reasons.append("statement_mismatch")
+ diag["detail"] = "target has no `:=`"
+ elif tuple(t.text for t in body_toks[i + 2 : e]) != ref.sig_toks:
+ reasons.append("statement_mismatch")
+ if reasons:
+ return None, reasons, diag
+ ref_cmd = f"theorem {REF_NAME} {ref.sig_text} := by\n sorry"
+ plan = Plan(
+ name=ref.name,
+ header=ref.header_text,
+ ref_cmd=ref_cmd,
+ body=body,
+ )
+ return plan, [], diag
+
+
+# ============================================================================ execution
+
+
+class LeanInfraError(RuntimeError):
+ """The verifier infrastructure is broken (header will not elaborate, toolchain gone).
+ Raised, never converted to a 0: a dead verifier scoring every proof 0 is the same
+ constant-reward masquerade as the 0/100 perfect-policy incident."""
+
+
+def _msgs(resp: dict) -> list[dict]:
+ return [m for m in resp.get("messages", []) if isinstance(m, dict)]
+
+
+def _short(s: str, n: int = 300) -> str:
+ return s if len(s) <= n else s[: n - 3] + "..."
+
+
+def _parse_audit(resp: dict, nonce: str) -> Optional[dict]:
+ tag = f"ALIGN_AUDIT {nonce} "
+ for m in _msgs(resp):
+ d = m.get("data", "")
+ if m.get("severity") == "info" and isinstance(d, str) and d.startswith(tag):
+ try:
+ return json.loads(d[len(tag) :])
+ except ValueError:
+ return None
+ return None
+
+
+class StrictVerifier:
+ """Strict verification of (statement, candidate code) pairs on a `ReplPool`."""
+
+ def __init__(self, pool: ReplPool, cfg: StrictConfig = StrictConfig(), rechecker=None) -> None:
+ self.pool = pool
+ self.cfg = cfg
+ self.rechecker = rechecker
+ if cfg.independent_recheck and rechecker is None:
+ from .recheck import IndependentRechecker # lazy: unused (and unimported) when off
+
+ self.rechecker = IndependentRechecker(
+ pool.launcher,
+ axiom_whitelist=cfg.axiom_whitelist,
+ timeout_s=cfg.recheck_timeout_s if cfg.recheck_timeout_s is not None else cfg.proof_timeout_s,
+ prepend_missing_header=cfg.prepend_missing_header,
+ )
+
+ def _second_opinion(self, res: StrictResult, statement: str, code: Optional[str]) -> StrictResult:
+ """The opt-in independent re-check. A no-op unless `cfg.independent_recheck` and the
+ strict tier accepted; it can only turn an accept into a reject."""
+ if not self.cfg.independent_recheck or not res.accepted:
+ return res
+ v = self.rechecker.check(statement, code)
+ diag = dict(res.diagnostics)
+ diag["recheck"] = v.summary()
+ if v.agree:
+ res.diagnostics = diag
+ return res
+ reasons = tuple(f"recheck_disagree:{r}" for r in v.reasons) or ("recheck_disagree:unknown",)
+ return StrictResult(False, reasons, "recheck", diag, res.elapsed_s)
+
+ # ---- public
+ def verify(self, statement: str, code: Optional[str]) -> StrictResult:
+ t0 = time.monotonic()
+ plan, reasons, diag = static_check(statement, code, self.cfg)
+ if plan is None:
+ return StrictResult(False, tuple(reasons), "static", diag, time.monotonic() - t0)
+ try:
+ with self.pool.acquire() as w:
+ # the proof's budget starts when it GETS a worker, not while it queues behind other proofs
+ res = self._run(w, plan, time.monotonic() + self.cfg.proof_timeout_s)
+ res = self._second_opinion(res, statement, code)
+ except LeanInfraError:
+ raise
+ except ReplError as e:
+ res = StrictResult(False, (e.code,), "repl", {"detail": _short(str(e))})
+ res.elapsed_s = time.monotonic() - t0
+ return res
+
+ def verify_batch(
+ self,
+ items: Sequence[tuple[str, Optional[str]]],
+ progress: Optional[Callable[[int, StrictResult], None]] = None,
+ ) -> list[StrictResult]:
+ """Verify many (statement, code) pairs on the pool's workers; results keep input order.
+
+ `progress(index, result)` (optional) is called from a worker thread as each item
+ completes, so a long batch is observable."""
+ indexed = list(enumerate(items))
+
+ def one(w, ix_it):
+ ix, it = ix_it
+ r = self._verify_on(w, it)
+ if progress is not None:
+ progress(ix, r)
+ return r
+
+ out = self.pool.map(one, indexed)
+ for o in out:
+ if not isinstance(o, StrictResult):
+ # LeanInfraError, or a programming error: either way a loud failure, never a 0.
+ raise o
+ return out # type: ignore[return-value]
+
+ # ---- internals
+ def _verify_on(self, w: Worker, item: tuple[str, Optional[str]]) -> StrictResult:
+ t0 = time.monotonic()
+ plan, reasons, diag = static_check(item[0], item[1], self.cfg)
+ if plan is None:
+ return StrictResult(False, tuple(reasons), "static", diag, time.monotonic() - t0)
+ try:
+ res = self._run(w, plan, t0 + self.cfg.proof_timeout_s)
+ res = self._second_opinion(res, item[0], item[1])
+ except LeanInfraError:
+ raise
+ except ReplError as e:
+ res = StrictResult(False, (e.code,), "repl", {"detail": _short(str(e))})
+ res.elapsed_s = time.monotonic() - t0
+ return res
+
+ def _audit(self, w: Worker, env: int, cand: str, deadline: float, recheck: bool) -> dict:
+ nonce = secrets.token_hex(8)
+ flags = 1 if recheck else 0
+ resp = w.command(f'#align_audit "{nonce}" {flags} {cand} {REF_NAME}', env, max(1.0, deadline - time.monotonic()))
+ a = _parse_audit(resp, nonce)
+ if a is None:
+ raise ReplProtocolError(f"audit produced no result: {_short(json.dumps(resp), 400)}")
+ return a
+
+ def _run(self, w: Worker, plan: Plan, deadline: float) -> StrictResult:
+ diag: dict = {"worker": w.idx}
+ try:
+ hdr_env = w.header_env(plan.header, AUDIT_PRELUDE)
+ except HeaderError as e:
+ if e.stage == "header":
+ # THIS reference's own header does not elaborate (a bad dataset row), while the imports and our
+ # prelude did: a fact about this item, not a dead verifier. Score it 0 with a reason.
+ return StrictResult(False, ("reference_invalid",), "reference", {"detail": _short(str(e), 400)})
+ raise LeanInfraError(f"cannot build the base environment: {e}") from e
+ except ReplError as e:
+ raise LeanInfraError(f"cannot build the base environment: {e}") from e
+
+ def build_ref() -> int:
+ r = w.command(plan.ref_cmd, hdr_env, max(5.0, deadline - time.monotonic()))
+ errs = [m for m in _msgs(r) if m.get("severity") == "error"]
+ if errs or "env" not in r:
+ raise _RefInvalid(_short(json.dumps(errs[:2] or r), 400))
+ return r["env"]
+
+ try:
+ ref_env = w.remember(("ref", hdr_env, plan.ref_cmd), build_ref)
+ except _RefInvalid as e:
+ return StrictResult(False, ("reference_invalid",), "reference", {"detail": str(e)})
+
+ def build_base() -> list:
+ a = self._audit(w, ref_env, REF_NAME, deadline, recheck=False)
+ return list(a.get("meta_decls", []))
+
+ base_meta = set(w.remember(("meta_base", ref_env), build_base))
+
+ resp = w.command(plan.body, ref_env, max(1.0, deadline - time.monotonic()))
+ msgs = _msgs(resp)
+ errors = [m for m in msgs if m.get("severity") == "error"]
+ diag["n_messages"] = len(msgs)
+ if errors:
+ diag["errors"] = [_short(m.get("data", ""), 240) for m in errors[:3]]
+ return StrictResult(False, ("compile_error",), "compile", diag)
+ if "env" not in resp:
+ diag["detail"] = _short(json.dumps(resp), 300)
+ return StrictResult(False, ("compile_error",), "compile", diag)
+ if resp.get("sorries") or any("sorry" in str(m.get("data", "")) for m in msgs if m.get("severity") == "warning"):
+ return StrictResult(False, ("sorry",), "compile", diag)
+
+ a = self._audit(w, resp["env"], plan.name, deadline, recheck=self.cfg.kernel_recheck)
+ diag["axioms"] = a.get("axioms")
+ reasons: list[str] = []
+ if not a.get("found"):
+ reasons.append("target_missing")
+ else:
+ if a.get("kind") != "theorem":
+ reasons.append("not_theorem")
+ if not isinstance(a.get("axioms"), list):
+ reasons.append("audit_incomplete") # fail closed: no axiom list is not an empty axiom list
+ for ax in a.get("axioms") or []:
+ if ax not in self.cfg.axiom_whitelist:
+ reasons.append("sorry" if ax == "sorryAx" else f"axiom:{ax}")
+ if not a.get("bridge"):
+ reasons.append("statement_mismatch" if self.cfg.statement_mode == "strict" else "bridge_failed")
+ diag["bridge_err"] = _short(str(a.get("bridge_err", "")), 300)
+ if a.get("skip_tc"):
+ reasons.append("skip_kernel_tc")
+ if a.get("recheck_err"):
+ reasons.append("kernel_recheck_failed")
+ diag["recheck_err"] = _short(str(a["recheck_err"]), 300)
+ new_meta = sorted(set(a.get("meta_decls", [])) - base_meta)
+ if new_meta:
+ reasons.append("meta_decl")
+ diag["meta_decls"] = new_meta[:5]
+ diag["foreign"] = a.get("foreign", [])
+ return StrictResult(not reasons, tuple(dict.fromkeys(reasons)), "audit", diag)
+
+
+class _RefInvalid(Exception):
+ pass
diff --git a/code/align/verify/lean/toolchain.py b/code/align/verify/lean/toolchain.py
new file mode 100644
index 0000000000000000000000000000000000000000..9edbe783026f57c03858bf655aa63897a7055fd7
--- /dev/null
+++ b/code/align/verify/lean/toolchain.py
@@ -0,0 +1,181 @@
+"""Locate the pinned Lean 4 toolchain and build the sanitized child environment.
+
+Why this module exists
+----------------------
+The Lean toolchain is an EXTERNAL BINARY (docs/DEPENDENCY_POLICY.md, "Lean
+toolchain"); the Python side only ever reaches it through `subprocess`. Three
+facts learned from the install on this box shape the code below, and each one
+is a failure that is silent if you do not know it:
+
+1. `lake env` prints RELATIVE paths (`././.lake/packages/mathlib/.lake/build/lib`)
+ resolved against the CURRENT DIRECTORY. A REPL spawned from any other cwd
+ fails at `import Mathlib` ("unknown package"). We therefore absolutise every
+ path-list variable once, at discovery time, and still set `cwd` to the
+ workspace.
+2. The REPL does not flush stdout after a response when stdout is a PIPE
+ (only a pty, which is what Goedel-Prover-V2's pexpect scheduler gives it,
+ line-buffers). Over a pipe the client blocks forever on a response that the
+ REPL "sent" into a userspace buffer: the pool wraps the binary in
+ `stdbuf -o0 -e0`. A pty would also work but needs `stty -icanon` or any input
+ line over 4096 bytes is truncated.
+3. The child gets a MINIMAL environment, not ours. A proof is attacker-shaped text
+ executed by a Lean elaborator; whatever environment the child has, a
+ meta-programming escape could read (API keys, HF tokens in the parent
+ environment). PATH/LEAN_PATH/LD_LIBRARY_PATH/LEAN_SYSROOT and nothing else.
+
+Stdlib only.
+"""
+
+from __future__ import annotations
+
+import json
+import os
+import shutil
+import subprocess
+from dataclasses import dataclass, field
+from pathlib import Path
+
+#: Where `scripts/lean_setup.sh` installs. Overridable for tests and other boxes.
+DEFAULT_LEAN_ROOT = "/apps/development/rlhf/lean"
+ENV_ROOT = "ALIGN_LEAN_ROOT"
+
+#: Variables that hold `:`-separated path lists and must be absolute.
+_PATH_LIST_VARS = ("LEAN_PATH", "LD_LIBRARY_PATH", "PATH", "LEAN_SRC_PATH")
+#: The only variables the REPL child receives (plus the fixed ones added below).
+_CHILD_VARS = ("LEAN_PATH", "LD_LIBRARY_PATH", "PATH", "LEAN_SYSROOT")
+
+
+class ToolchainNotFound(RuntimeError):
+ """The Lean install is absent or incomplete. Carries the setup command."""
+
+
+@dataclass(frozen=True)
+class LeanToolchain:
+ root: Path
+ workspace: Path
+ repl_bin: Path
+ lake: Path
+ pins: dict = field(default_factory=dict)
+ #: The child environment (already minimal and absolute).
+ child_env: dict = field(default_factory=dict)
+
+ def argv(self) -> list[str]:
+ """The bare REPL command (the pool adds nice/prlimit/stdbuf around it)."""
+ return [str(self.repl_bin)]
+
+ @property
+ def cwd(self) -> str:
+ return str(self.workspace)
+
+ def describe(self) -> str:
+ p = self.pins
+ return (
+ f"{p.get('lean_toolchain', '?')} mathlib={str(p.get('mathlib_rev', '?'))[:10]} "
+ f"repl={p.get('repl_tag', '?')}"
+ )
+
+
+def lean_root(root: str | os.PathLike | None = None) -> Path:
+ return Path(root or os.environ.get(ENV_ROOT) or DEFAULT_LEAN_ROOT)
+
+
+def _absolutise(value: str, base: Path) -> str:
+ parts = []
+ for p in value.split(":"):
+ if not p:
+ continue
+ parts.append(p if os.path.isabs(p) else os.path.normpath(os.path.join(base, p)))
+ return ":".join(parts)
+
+
+def parse_env0(blob: bytes) -> dict[str, str]:
+ """Parse `env -0` output (NUL-separated KEY=VALUE). Values may hold newlines."""
+ out: dict[str, str] = {}
+ for item in blob.decode("utf-8", "replace").split("\0"):
+ if "=" in item:
+ k, v = item.split("=", 1)
+ out[k] = v
+ return out
+
+
+def build_child_env(lake_env: dict[str, str], workspace: Path) -> dict[str, str]:
+ """Reduce `lake env`'s environment to what the REPL child may see.
+
+ Pure function (unit-tested without Lean): relative path lists become absolute
+ against `workspace`, everything not in `_CHILD_VARS` is dropped, and glibc's
+ arena count is pinned to 2. The last matters: without it each of the 48 cores
+ can get a 64MB arena reservation, which makes RLIMIT_AS (the pool's backstop)
+ useless because the virtual size is dominated by reservations, not use.
+ """
+ env: dict[str, str] = {}
+ for k in _CHILD_VARS:
+ if k in lake_env:
+ env[k] = _absolutise(lake_env[k], workspace) if k in _PATH_LIST_VARS else lake_env[k]
+ env["MALLOC_ARENA_MAX"] = "2"
+ env["LANG"] = "C.UTF-8"
+ env["LC_ALL"] = "C.UTF-8"
+ return env
+
+
+def find_toolchain(root: str | os.PathLike | None = None) -> LeanToolchain:
+ """Resolve the install, or raise `ToolchainNotFound` with the fix."""
+ r = lean_root(root)
+ ws = r / "workspace"
+ repl_bin = r / "repl" / ".lake" / "build" / "bin" / "repl"
+ lake = r / "elan" / "bin" / "lake"
+ missing = [
+ str(p)
+ for p in (lake, repl_bin, ws / "lakefile.lean", ws / "lake-manifest.json")
+ if not p.exists()
+ ]
+ if missing:
+ raise ToolchainNotFound(
+ f"Lean toolchain incomplete under {r} (missing: {', '.join(missing)}). "
+ "Run: bash scripts/lean_setup.sh"
+ )
+ pins: dict = {}
+ pins_path = r / "PINS.json"
+ if pins_path.exists():
+ try:
+ pins = json.loads(pins_path.read_text())
+ except (OSError, ValueError):
+ pins = {}
+ base = {"ELAN_HOME": str(r / "elan"), "PATH": os.environ.get("PATH", "/usr/bin:/bin")}
+ try:
+ proc = subprocess.run(
+ [str(lake), "env", "env", "-0"],
+ cwd=ws,
+ capture_output=True,
+ env=base,
+ timeout=60,
+ )
+ except (OSError, subprocess.TimeoutExpired) as e:
+ raise ToolchainNotFound(f"`lake env` failed under {ws}: {e!r}") from e
+ if proc.returncode != 0:
+ raise ToolchainNotFound(
+ f"`lake env` exited {proc.returncode}: {proc.stderr.decode('utf-8', 'replace')[:300]}"
+ )
+ lake_env = parse_env0(proc.stdout)
+ if "LEAN_PATH" not in lake_env:
+ raise ToolchainNotFound("`lake env` did not set LEAN_PATH")
+ return LeanToolchain(
+ root=r,
+ workspace=ws,
+ repl_bin=repl_bin,
+ lake=lake,
+ pins=pins,
+ child_env=build_child_env(lake_env, ws),
+ )
+
+
+def toolchain_available(root: str | os.PathLike | None = None) -> bool:
+ """Cheap existence probe for `pytest.mark.skipif` (no subprocess)."""
+ r = lean_root(root)
+ return (r / "repl" / ".lake" / "build" / "bin" / "repl").exists() and (
+ r / "workspace" / ".lake" / "packages" / "mathlib" / ".lake" / "build" / "lib" / "Mathlib.olean"
+ ).exists()
+
+
+def which_wrapper(name: str) -> str | None:
+ """Absolute path of a coreutils/util-linux wrapper (`nice`, `prlimit`, `stdbuf`)."""
+ return shutil.which(name)
diff --git a/code/align/verify/normalize.py b/code/align/verify/normalize.py
new file mode 100644
index 0000000000000000000000000000000000000000..d9e595054ecbf7595d0e2ffae27a011b9ab27559
--- /dev/null
+++ b/code/align/verify/normalize.py
@@ -0,0 +1,291 @@
+"""Answer-string normalisation: presentation noise OUT, mathematical content IN.
+
+Replaces the normalisation half of `math_verify.parse`. Pure stdlib.
+
+THE ASYMMETRY THAT GOVERNS EVERY RULE HERE
+------------------------------------------
+A normalisation that is too weak costs a FALSE NEGATIVE (a correct answer
+scored wrong -- measured 5-12% on the predecessor's engine, and concentrated
+on unusual-but-correct formats a stronger policy emits more often). One that
+is too strong costs a FALSE POSITIVE (a wrong answer scored right), which
+silently inflates every reported accuracy. So each rule below strips only
+things that are presentation by construction:
+
+ * whole-string / trailing `\\text{...}` words and unit words, but only after a
+ number-ish prefix (`5 \\text{ cm}` -> `5`; `x \\text{ and } y` is untouched);
+ * degree marks, `$`, thousands separators, `\\left`/`\\right`, size macros,
+ `\\dfrac`->`\\frac`, trailing periods;
+ * a leading `x=` ONLY when the right-hand side is a constant expression
+ (`x=5` -> `5`, but `y=2x+3` stays an equation).
+
+Percent is NOT stripped here: `18\\%` and `18` are equal only as a deliberate,
+two-sided decision made in `equiv.py` (so `0.18` still matches `18\\%` and `18`
+still does not match `0.18\\%`).
+"""
+
+from __future__ import annotations
+
+import re
+
+from .extract import match_brace
+
+__all__ = ["normalize_answer", "normalize_labeled", "strip_var_prefix", "split_var_prefix", "is_const_expr"]
+
+_TEXT_NAMES = (
+ "text|textbf|textit|textrm|textsf|mbox|mathrm|mathbf|mathit|mathsf|operatorname|rm|hbox"
+)
+#: Macros whose body is PROSE. The others (`\\mathrm`, `\\mathbf`, `\\operatorname`,
+#: ...) typeset MATH: `2\\mathrm{pi}` is 2*pi, never "2 plus a unit".
+_PROSE_MACROS = frozenset({"text", "textbf", "textit", "textrm", "textsf", "mbox", "hbox"})
+_MATH_WORDS = frozenset(
+ "pi e i sin cos tan cot sec csc ln log lg exp sqrt arcsin arccos arctan infty infinity inf".split()
+)
+_TEXT_MACRO = re.compile(r"\\(?:" + _TEXT_NAMES + r")\s*\{([^{}]*)\}")
+_WHOLE_TEXT = re.compile(r"^\\(?:" + _TEXT_NAMES + r")\s*\{([^{}]*)\}$")
+# A text macro (plus an optional ^2 / ^3) hanging off the END of a number-ish
+# head: the MATH-style `5\text{ cm}^2`, `10\mbox{ degrees}`.
+_TRAIL_TEXT = re.compile(
+ r"^(?P.*[0-9\}\)])\s*\\(?P" + _TEXT_NAMES + r")\s*\{(?P[^{}]*)\}"
+ r"(?:\s*\^\s*\{?\s*[23]\s*\}?)?\s*$",
+ re.DOTALL,
+)
+
+#: Unit / counting words stripped after a number. Single letters (m, h, s, g,
+#: l) are deliberately absent: `3m` is far more likely `3*m` than metres.
+_UNIT_WORDS = frozenset(
+ """
+ unit units square sq cubic cm cms mm km kms meter meters metre metres
+ inch inches foot feet ft yard yards yd yds mile miles mi mph kph
+ hour hours hr hrs minute minutes min mins second seconds sec secs
+ day days week weeks month months year years
+ degree degrees deg radian radians rad
+ dollar dollars cent cents percent
+ kg kgs gram grams lb lbs pound pounds ounce ounces oz
+ liter liters litre litres ml gallon gallons
+ people person students student ways pieces points times
+ """.split()
+)
+_GLUED_UNITS = frozenset(w for w in _UNIT_WORDS if len(w) >= 2)
+
+_TRAIL_UNIT = re.compile(
+ r"^(?P.*[0-9\}\)])(?P\s*)(?P[A-Za-z][A-Za-z\s\.]*?)"
+ r"(?:\s*\^\s*\{?\s*[23]\s*\}?)?\s*$",
+ re.DOTALL,
+)
+
+_UNICODE = {
+ "\u2212": "-", # minus sign
+ "\u2013": "-",
+ "\u2014": "-",
+ "\u00d7": r"\times ",
+ "\u00f7": r"\div ",
+ "\u00b7": r"\cdot ",
+ "\u03c0": "pi",
+ "\u221e": r"\infty ",
+ "\u2264": r"\leq ",
+ "\u2265": r"\geq ",
+ "\u2260": r"\neq ",
+ "\u222a": r"\cup ",
+ "\u00b1": r"\pm ",
+ "\u221a": r"\sqrt",
+ "\u00a0": " ",
+}
+
+_GREEK_VAR = (
+ "theta|alpha|beta|gamma|delta|phi|lambda|mu|sigma|omega|rho|tau|epsilon|varphi|vartheta"
+)
+_CONST_WORDS = re.compile(
+ r"(? str | None:
+ m = re.match(r"^\\(?:boxed|fbox)\s*\{", t)
+ if not m:
+ return None
+ close = match_brace(t, m.end() - 1)
+ if close == len(t) - 1:
+ return t[m.end() : close]
+ return None
+
+
+def _strip_wrappers(t: str) -> str:
+ changed = True
+ while changed:
+ changed = False
+ t = t.strip()
+ inner = _unwrap_whole_boxed(t)
+ if inner is not None:
+ t, changed = inner, True
+ elif len(t) >= 4 and t.startswith("$$") and t.endswith("$$"):
+ t, changed = t[2:-2], True
+ elif len(t) >= 2 and t.startswith("$") and t.endswith("$") and t.count("$") == 2:
+ t, changed = t[1:-1], True
+ elif t.startswith("\\(") and t.endswith("\\)"):
+ t, changed = t[2:-2], True
+ elif t.startswith("\\[") and t.endswith("\\]"):
+ t, changed = t[2:-2], True
+ return t
+
+
+def is_const_expr(rhs: str) -> bool:
+ """True if `rhs` has no free variable: only numbers, pi, e, functions."""
+ if re.search(r"\\(?:" + _GREEK_VAR + r")(?![A-Za-z])", rhs):
+ return False
+ rest = re.sub(r"\\[A-Za-z]+", " ", rhs)
+ rest = _CONST_WORDS.sub(" ", rest)
+ return re.search(r"[A-Za-z]", rest) is None
+
+
+def split_var_prefix(t: str, *, require_const: bool = True) -> tuple[str | None, str]:
+ """`x=5` -> (`x`, `5`); anything else -> (None, t). The label is returned
+ canonicalised (no spaces / braces) so callers can compare it."""
+ m = _VAR_PREFIX.match(t)
+ if not m:
+ return None, t
+ rhs = t[m.end() :]
+ if "=" in rhs or not rhs.strip():
+ return None, t
+ if require_const and not is_const_expr(rhs):
+ return None, t
+ label = re.sub(r"[\s{}]", "", t[: m.end()].rsplit("=", 1)[0])
+ return label, rhs.strip()
+
+
+def strip_var_prefix(t: str, *, require_const: bool = True) -> str:
+ """`x=5` -> `5`; leaves `y=2x+3` (non-constant RHS) and chained `a=b=c`."""
+ return split_var_prefix(t, require_const=require_const)[1]
+
+
+def _strip_trailing_text_units(t: str) -> str:
+ for _ in range(4):
+ m = _TRAIL_TEXT.match(t)
+ if not m:
+ break
+ body = m.group("body").strip()
+ # Multi-letter words only: `2\mathrm{e}` is a variable/constant, not a unit.
+ if len(body) >= 2 and re.fullmatch(r"[A-Za-z][A-Za-z .]*", body):
+ words = [w.lower() for w in body.split()]
+ if any(w in _MATH_WORDS for w in words):
+ break # `2\text{pi}` is 2*pi, not a number with a unit
+ # Math-font macros (`\mathrm{cm}`) are units only when they ARE units.
+ if m.group("macro") not in _PROSE_MACROS and not all(w in _UNIT_WORDS for w in words):
+ break
+ t = m.group("head").rstrip()
+ else:
+ break
+ return t
+
+
+def _strip_trailing_unit_words(t: str) -> str:
+ for _ in range(3):
+ m = _TRAIL_UNIT.match(t)
+ if not m:
+ break
+ words = re.findall(r"[A-Za-z]+", m.group("words"))
+ if not words:
+ break
+ low = [w.lower() for w in words]
+ if not all(w in _UNIT_WORDS for w in low):
+ break
+ # Glued ("5cm") only for multi-letter units; spaced ("5 cm") for any.
+ if not m.group("gap") and not all(w in _GLUED_UNITS for w in low):
+ break
+ t = m.group("head").rstrip()
+ return t
+
+
+def normalize_answer(s: str | None) -> str:
+ """Canonical comparison form of one answer string (see module docstring)."""
+ return strip_var_prefix(_normalize_core(s))
+
+
+def normalize_labeled(s: str | None) -> tuple[str | None, str]:
+ """Like `normalize_answer`, but also returns the stripped `x=` label (or
+ None). `x=1` vs `y=1` must not be equal: the label is part of the answer
+ whenever BOTH sides state one."""
+ return split_var_prefix(_normalize_core(s))
+
+
+def _normalize_core(s: str | None) -> str:
+ if s is None:
+ return ""
+ t = _strip_wrappers(str(s))
+ if not t:
+ return ""
+
+ for k, v in _UNICODE.items():
+ t = t.replace(k, v)
+
+ # --- LaTeX presentation macros ---------------------------------------
+ t = re.sub(r"\\[dtc]frac(?![A-Za-z])", r"\\frac", t)
+ t = t.replace("\\displaystyle", "").replace("\\textstyle", "")
+ t = re.sub(r"\\(?:left|right)\s*\.", "", t)
+ t = re.sub(r"\\(?:left|right|bigl|bigr|Bigl|Bigr|biggl|biggr|bigg|Bigg|big|Big)(?![A-Za-z])", "", t)
+ t = re.sub(r"\\lbrace(?![A-Za-z])", r"\\{", t)
+ t = re.sub(r"\\rbrace(?![A-Za-z])", r"\\}", t)
+ t = re.sub(r"\\[lr]vert(?![A-Za-z])", "|", t)
+ t = re.sub(r"\\(?:le|leqslant)(?![A-Za-z])", r"\\leq", t)
+ t = re.sub(r"\\(?:ge|geqslant)(?![A-Za-z])", r"\\geq", t)
+ t = re.sub(r"\\ne(?![A-Za-z])", r"\\neq", t)
+
+ # --- spacing -----------------------------------------------------------
+ # Thousands written with a LaTeX space (`12\,345`) close up; every other
+ # spacing macro becomes a plain space. The `(? "x=1, x=2" ---------------------------------------
+ if re.search(r"[0-9=]", t):
+ t = re.sub(r"\s+(?:or|and)\s+", ", ", t)
+
+ # --- tail hygiene -----------------------------------------------------
+ t = re.sub(r"\\\.\s*$", "", t)
+ t = re.sub(r"[\s.;,]+$", "", t)
+ t = re.sub(r"\s+", " ", t).strip()
+ if t.startswith("+"):
+ t = t[1:].lstrip()
+
+ # `x \in [2,5)` -> `[2,5)`: membership prefix on an interval/set answer.
+ t = re.sub(r"^[A-Za-z](?:_\{?[A-Za-z0-9]+\}?)?\s*\\in\s*(?=[\[\(\\{])", "", t)
+
+ # Thousands separators: whole-string only. `(1,000)` stays a tuple.
+ if re.fullmatch(r"-?\d{1,3}(?:,\d{3})+(?:\.\d+)?", t):
+ t = t.replace(",", "")
+ # `(C)` -> `C` for multiple-choice letters.
+ t = re.sub(r"^\(\s*([A-Za-z])\s*\)$", r"\1", t)
+
+ return t
diff --git a/code/align/verify_backends/__init__.py b/code/align/verify_backends/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..6174c7cfddcd5d7592590d7fc929780602a3c809
--- /dev/null
+++ b/code/align/verify_backends/__init__.py
@@ -0,0 +1,5 @@
+"""Optional verification backends that sit BESIDE (never inside) `align.verify.lean`.
+
+Nothing here may produce an accept on its own: a backend is a pre-filter / throughput layer,
+and every accept still comes from `align.verify.lean.StrictVerifier`. See `kimina_server.py`.
+"""
diff --git a/code/align/verify_backends/kimina_server.py b/code/align/verify_backends/kimina_server.py
new file mode 100644
index 0000000000000000000000000000000000000000..e0508e4a177e94021b169bd1fc54f1e5ab8869da
--- /dev/null
+++ b/code/align/verify_backends/kimina_server.py
@@ -0,0 +1,186 @@
+"""Optional PRE-FILTER backed by a Kimina Lean server (project-numina/kimina-lean-server, Apache-2.0).
+
+Safety contract (the reason this module is shaped the way it is)
+----------------------------------------------------------------
+The strict verifier (`align.verify.lean`, docs/LEAN_VERIFIER.md) is the ONLY authority that can
+accept a proof: lexical gate, statement bridge, axiom audit, kernel recheck, sandboxed REPL.
+Kimina's `/api/check` is a compile-only REPL front end: it has no axiom audit, no statement
+bridge, no lexical gate and no sandbox. Therefore:
+
+ * `PrefilterVerdict` has NO `accepted` field and no truthiness; its verdicts are
+ `reject` (the code certainly fails: a Lean error or a `sorry`), `maybe` (nothing found; this
+ is NOT an accept) and `error` (the server could not decide: timeout, dead server, bad reply).
+ * `GatedVerifier.verify` reports an accept only when the wrapped strict verifier
+ returned an accept for the SAME code. `maybe` and `error` always go to the strict verifier;
+ only `reject` short-circuits, and a short-circuit can only turn a would-be-accept into a
+ reject (lost recall, never lost soundness). If the server is down, behaviour degrades to
+ "strict verifier only".
+ * Code never reaches the (unsandboxed) server unless it first passes the strict verifier's lexical
+ ban scan (`strict.scan_banned`: `#eval`, `run_cmd`, `IO.*`, `Lean.*`, `initialize`, `extern`, ...).
+ Banned code is a certain strict reject, so this is free. Residual risk: an execution path the
+ scan misses would run unsandboxed here but sandboxed in the strict tier; keep the server loopback-
+ only, inside a hostguard scope, and do not run it on untrusted input at scale.
+ * Nothing in this file touches `align/verify/lean/`; it only imports `extract_lean_code`,
+ `lex`/`scan_banned` and the strict verifier's public surface.
+
+Stdlib only (dependency policy): HTTP through `urllib`. Start the server with
+`toolchains/work/kimina-run/start.sh` (see docs/KIMINA_BACKEND.md).
+"""
+
+from __future__ import annotations
+
+import json
+import time
+import urllib.error
+import urllib.request
+import uuid
+from dataclasses import dataclass, field
+from typing import Any, Optional
+
+REJECT = "reject"
+MAYBE = "maybe"
+ERROR = "error"
+
+
+@dataclass(frozen=True)
+class PrefilterVerdict:
+ """Outcome of the compile-only pre-filter. Deliberately NOT an accept/reject boolean."""
+
+ verdict: str # REJECT | MAYBE | ERROR
+ reasons: tuple[str, ...] = ()
+ elapsed_s: float = 0.0
+ detail: dict = field(default_factory=dict)
+
+ @property
+ def is_reject(self) -> bool:
+ return self.verdict == REJECT
+
+ def __bool__(self) -> bool: # `if verdict:` would silently read MAYBE as an accept
+ raise TypeError("PrefilterVerdict has no truth value: compare .verdict explicitly (MAYBE is not an accept)")
+
+
+def classify_response(resp: dict) -> PrefilterVerdict:
+ """Map one Kimina `ReplResponse` dict ({id, response|error, time}) to a verdict.
+
+ reject : a Lean message of severity `error`, a `sorry` warning, or a non-empty `sorries`.
+ error : the server reported an error (`error` field: timeout / REPL died), a reply with no
+ `response`, or a Lean-level `message` the REPL itself returned instead of a result.
+ maybe : anything else. NEVER an accept."""
+ t = float(resp.get("time") or 0.0)
+ if resp.get("error"):
+ err = str(resp["error"])
+ why = "timeout" if "timed out" in err else "server_error"
+ return PrefilterVerdict(ERROR, (why,), t, {"error": err[:300]})
+ body = resp.get("response")
+ if not isinstance(body, dict):
+ return PrefilterVerdict(ERROR, ("protocol",), t, {"raw": str(resp)[:300]})
+ if "message" in body and "env" not in body and "messages" not in body: # REPL-level {"message": "..."}
+ return PrefilterVerdict(ERROR, ("repl_message",), t, {"message": str(body["message"])[:300]})
+ msgs = [m for m in body.get("messages", []) if isinstance(m, dict)]
+ errors = [m for m in msgs if m.get("severity") == "error"]
+ if errors:
+ return PrefilterVerdict(REJECT, ("compile_error",), t, {"first_error": str(errors[0].get("data", ""))[:240], "n_errors": len(errors)})
+ if body.get("sorries") or any("sorry" in str(m.get("data", "")) for m in msgs):
+ return PrefilterVerdict(REJECT, ("sorry",), t)
+ return PrefilterVerdict(MAYBE, (), t)
+
+
+class KiminaClient:
+ """Minimal stdlib client for `POST /api/check`."""
+
+ def __init__(self, url: str = "http://127.0.0.1:18000", api_key: Optional[str] = None) -> None:
+ if not url.startswith(("http://127.0.0.1", "http://localhost", "http://[::1]")):
+ # Proof text is attacker-shaped; the server has no sandbox. Loopback only, unless the caller
+ # deliberately builds their own client.
+ raise ValueError(f"refusing non-loopback Kimina URL {url!r}")
+ self.url = url.rstrip("/")
+ self.api_key = api_key
+
+ def check(self, code: str, timeout_s: float = 180.0, snippet_id: Optional[str] = None) -> dict:
+ sid = snippet_id or uuid.uuid4().hex
+ payload = json.dumps({"snippets": [{"id": sid, "code": code}], "timeout": timeout_s, "reuse": True}).encode()
+ headers = {"Content-Type": "application/json"}
+ if self.api_key:
+ headers["Authorization"] = f"Bearer {self.api_key}"
+ req = urllib.request.Request(self.url + "/api/check", data=payload, headers=headers, method="POST")
+ with urllib.request.urlopen(req, timeout=timeout_s + 90.0) as r:
+ data = json.loads(r.read().decode("utf-8"))
+ results = data.get("results")
+ if not isinstance(results, list) or len(results) != 1:
+ raise ValueError(f"unexpected Kimina reply: {str(data)[:200]}")
+ return results[0]
+
+ def healthy(self) -> bool:
+ try:
+ with urllib.request.urlopen(self.url + "/health", timeout=5) as r:
+ return r.status == 200
+ except Exception: # noqa: BLE001
+ return False
+
+
+class KiminaPrefilter:
+ def __init__(self, client: Optional[KiminaClient] = None, timeout_s: float = 180.0) -> None:
+ self.client = client or KiminaClient()
+ self.timeout_s = timeout_s
+
+ def check_code(self, code: Optional[str]) -> PrefilterVerdict:
+ if code is None or not code.strip():
+ return PrefilterVerdict(REJECT, ("no_code",))
+ # Code-execution gate BEFORE anything leaves this process: the Kimina REPL is not namespace-
+ # sandboxed, and Lean code can execute (`#eval`, `run_cmd`, `IO.*`, `initialize`, ...). Run the strict
+ # verifier's own lexical ban scan first; a banned construct is a certain strict reject anyway, so
+ # rejecting here costs nothing in soundness or recall. If the scan itself fails, send nothing.
+ try:
+ from align.verify.lean.strict import lex, scan_banned
+
+ banned = scan_banned(code, lex(code))
+ except Exception as e: # noqa: BLE001
+ return PrefilterVerdict(ERROR, ("lexical_gate_failed",), 0.0, {"exc": repr(e)[:200]})
+ if banned:
+ return PrefilterVerdict(REJECT, tuple(banned), 0.0, {"gate": "strict.scan_banned"})
+ t0 = time.monotonic()
+ try:
+ resp = self.client.check(code, self.timeout_s)
+ except urllib.error.HTTPError as e:
+ return PrefilterVerdict(ERROR, (f"http_{e.code}",), time.monotonic() - t0)
+ except Exception as e: # noqa: BLE001 (URLError, timeouts, JSON: all mean "no opinion")
+ return PrefilterVerdict(ERROR, ("unreachable",), time.monotonic() - t0, {"exc": repr(e)[:200]})
+ v = classify_response(resp)
+ return PrefilterVerdict(v.verdict, v.reasons, v.elapsed_s or (time.monotonic() - t0), v.detail)
+
+ def check_completion(self, completion: Optional[str]) -> PrefilterVerdict:
+ from align.verify.lean.reward import extract_lean_code
+
+ return self.check_code(extract_lean_code(completion))
+
+
+@dataclass(frozen=True)
+class GatedResult:
+ prefilter: Optional[PrefilterVerdict]
+ strict: Any # StrictResult, or None when the pre-filter short-circuited with a reject
+ skipped_strict: bool
+
+ @property
+ def accepted(self) -> bool:
+ # The ONLY path to True. Derived from the strict verifier's own verdict; never from the pre-filter.
+ return bool(self.strict is not None and getattr(self.strict, "accepted", False) is True)
+
+ @property
+ def reasons(self) -> tuple[str, ...]:
+ if self.strict is not None:
+ return tuple(self.strict.reasons)
+ return tuple(f"prefilter:{r}" for r in (self.prefilter.reasons if self.prefilter else ()))
+
+
+class GatedVerifier:
+ """Pre-filter, then the strict verifier on everything the pre-filter did not reject."""
+
+ def __init__(self, strict_verifier, prefilter: KiminaPrefilter) -> None:
+ self.strict = strict_verifier
+ self.prefilter = prefilter
+
+ def verify(self, statement: str, code: Optional[str]) -> GatedResult:
+ pf = self.prefilter.check_code(code)
+ if pf.verdict == REJECT:
+ return GatedResult(pf, None, True)
+ return GatedResult(pf, self.strict.verify(statement, code), False)
diff --git a/code/assets/tokenizer/mistral-7b-v0.3/PROVENANCE.md b/code/assets/tokenizer/mistral-7b-v0.3/PROVENANCE.md
new file mode 100644
index 0000000000000000000000000000000000000000..2ce3acb242e8317bb72c64b911da393d7a87b964
--- /dev/null
+++ b/code/assets/tokenizer/mistral-7b-v0.3/PROVENANCE.md
@@ -0,0 +1,9 @@
+Mistral-7B-v0.3 tokenizer files (Apache-2.0, mistralai/Mistral-7B-v0.3), vendored so the
+test-suite and data pipeline do not depend on a rotating checkpoint dir or a missing HF
+cache snapshot. Byte-identical copies were found in the legacy nano-mistral-o1 SFT
+checkpoints (micro_00002000 and micro_00002250). md5:
+ tokenizer.json 32d192f59838957494261e9ae868506b
+ tokenizer.model 2bbc01eba250283314fdbd53d05de94b
+ tokenizer_config.json c4103b11a988818d399e4082a1bd7d5c
+ special_tokens_map.json e60ae4706e691cf378545ad0bd042f6f
+Tokenizer only: no config.json, no weights.
diff --git a/code/assets/tokenizer/mistral-7b-v0.3/special_tokens_map.json b/code/assets/tokenizer/mistral-7b-v0.3/special_tokens_map.json
new file mode 100644
index 0000000000000000000000000000000000000000..451134b2ddc2e78555d1e857518c54b4bdc2e87d
--- /dev/null
+++ b/code/assets/tokenizer/mistral-7b-v0.3/special_tokens_map.json
@@ -0,0 +1,23 @@
+{
+ "bos_token": {
+ "content": "",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false
+ },
+ "eos_token": {
+ "content": "",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false
+ },
+ "unk_token": {
+ "content": "",
+ "lstrip": false,
+ "normalized": false,
+ "rstrip": false,
+ "single_word": false
+ }
+}
diff --git a/code/assets/tokenizer/mistral-7b-v0.3/tokenizer.json b/code/assets/tokenizer/mistral-7b-v0.3/tokenizer.json
new file mode 100644
index 0000000000000000000000000000000000000000..8d7d23136b474a3df1182ed829db19984e295ee7
--- /dev/null
+++ b/code/assets/tokenizer/mistral-7b-v0.3/tokenizer.json
@@ -0,0 +1,98793 @@
+{
+ "version": "1.0",
+ "truncation": null,
+ "padding": null,
+ "added_tokens": [
+ {
+ "id": 0,
+ "content": "",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 1,
+ "content": "",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 2,
+ "content": "",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 3,
+ "content": "[INST]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 4,
+ "content": "[/INST]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 5,
+ "content": "[TOOL_CALLS]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 6,
+ "content": "[AVAILABLE_TOOLS]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 7,
+ "content": "[/AVAILABLE_TOOLS]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 8,
+ "content": "[TOOL_RESULTS]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 9,
+ "content": "[/TOOL_RESULTS]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 10,
+ "content": "[control_8]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 11,
+ "content": "[control_9]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 12,
+ "content": "[control_10]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 13,
+ "content": "[control_11]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 14,
+ "content": "[control_12]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 15,
+ "content": "[control_13]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 16,
+ "content": "[control_14]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 17,
+ "content": "[control_15]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 18,
+ "content": "[control_16]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 19,
+ "content": "[control_17]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 20,
+ "content": "[control_18]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 21,
+ "content": "[control_19]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 22,
+ "content": "[control_20]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 23,
+ "content": "[control_21]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 24,
+ "content": "[control_22]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 25,
+ "content": "[control_23]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 26,
+ "content": "[control_24]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 27,
+ "content": "[control_25]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 28,
+ "content": "[control_26]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 29,
+ "content": "[control_27]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 30,
+ "content": "[control_28]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 31,
+ "content": "[control_29]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 32,
+ "content": "[control_30]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 33,
+ "content": "[control_31]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 34,
+ "content": "[control_32]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 35,
+ "content": "[control_33]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 36,
+ "content": "[control_34]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 37,
+ "content": "[control_35]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 38,
+ "content": "[control_36]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 39,
+ "content": "[control_37]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 40,
+ "content": "[control_38]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 41,
+ "content": "[control_39]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 42,
+ "content": "[control_40]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 43,
+ "content": "[control_41]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 44,
+ "content": "[control_42]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 45,
+ "content": "[control_43]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 46,
+ "content": "[control_44]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 47,
+ "content": "[control_45]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 48,
+ "content": "[control_46]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 49,
+ "content": "[control_47]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 50,
+ "content": "[control_48]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 51,
+ "content": "[control_49]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 52,
+ "content": "[control_50]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 53,
+ "content": "[control_51]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 54,
+ "content": "[control_52]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 55,
+ "content": "[control_53]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 56,
+ "content": "[control_54]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 57,
+ "content": "[control_55]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 58,
+ "content": "[control_56]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 59,
+ "content": "[control_57]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 60,
+ "content": "[control_58]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 61,
+ "content": "[control_59]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 62,
+ "content": "[control_60]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 63,
+ "content": "[control_61]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 64,
+ "content": "[control_62]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 65,
+ "content": "[control_63]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 66,
+ "content": "[control_64]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 67,
+ "content": "[control_65]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 68,
+ "content": "[control_66]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 69,
+ "content": "[control_67]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 70,
+ "content": "[control_68]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 71,
+ "content": "[control_69]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 72,
+ "content": "[control_70]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 73,
+ "content": "[control_71]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 74,
+ "content": "[control_72]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 75,
+ "content": "[control_73]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 76,
+ "content": "[control_74]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 77,
+ "content": "[control_75]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 78,
+ "content": "[control_76]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 79,
+ "content": "[control_77]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 80,
+ "content": "[control_78]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 81,
+ "content": "[control_79]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 82,
+ "content": "[control_80]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 83,
+ "content": "[control_81]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 84,
+ "content": "[control_82]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 85,
+ "content": "[control_83]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 86,
+ "content": "[control_84]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 87,
+ "content": "[control_85]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 88,
+ "content": "[control_86]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 89,
+ "content": "[control_87]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 90,
+ "content": "[control_88]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 91,
+ "content": "[control_89]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 92,
+ "content": "[control_90]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 93,
+ "content": "[control_91]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 94,
+ "content": "[control_92]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 95,
+ "content": "[control_93]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 96,
+ "content": "[control_94]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 97,
+ "content": "[control_95]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 98,
+ "content": "[control_96]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 99,
+ "content": "[control_97]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 100,
+ "content": "[control_98]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 101,
+ "content": "[control_99]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 102,
+ "content": "[control_100]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 103,
+ "content": "[control_101]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 104,
+ "content": "[control_102]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 105,
+ "content": "[control_103]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 106,
+ "content": "[control_104]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 107,
+ "content": "[control_105]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 108,
+ "content": "[control_106]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 109,
+ "content": "[control_107]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 110,
+ "content": "[control_108]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 111,
+ "content": "[control_109]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 112,
+ "content": "[control_110]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 113,
+ "content": "[control_111]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 114,
+ "content": "[control_112]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 115,
+ "content": "[control_113]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 116,
+ "content": "[control_114]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 117,
+ "content": "[control_115]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 118,
+ "content": "[control_116]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 119,
+ "content": "[control_117]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 120,
+ "content": "[control_118]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 121,
+ "content": "[control_119]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 122,
+ "content": "[control_120]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 123,
+ "content": "[control_121]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 124,
+ "content": "[control_122]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 125,
+ "content": "[control_123]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 126,
+ "content": "[control_124]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 127,
+ "content": "[control_125]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 128,
+ "content": "[control_126]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 129,
+ "content": "[control_127]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 130,
+ "content": "[control_128]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 131,
+ "content": "[control_129]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 132,
+ "content": "[control_130]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 133,
+ "content": "[control_131]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 134,
+ "content": "[control_132]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 135,
+ "content": "[control_133]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 136,
+ "content": "[control_134]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 137,
+ "content": "[control_135]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 138,
+ "content": "[control_136]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 139,
+ "content": "[control_137]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 140,
+ "content": "[control_138]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 141,
+ "content": "[control_139]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 142,
+ "content": "[control_140]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 143,
+ "content": "[control_141]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 144,
+ "content": "[control_142]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 145,
+ "content": "[control_143]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 146,
+ "content": "[control_144]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 147,
+ "content": "[control_145]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 148,
+ "content": "[control_146]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 149,
+ "content": "[control_147]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 150,
+ "content": "[control_148]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 151,
+ "content": "[control_149]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 152,
+ "content": "[control_150]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 153,
+ "content": "[control_151]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 154,
+ "content": "[control_152]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 155,
+ "content": "[control_153]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 156,
+ "content": "[control_154]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 157,
+ "content": "[control_155]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 158,
+ "content": "[control_156]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 159,
+ "content": "[control_157]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 160,
+ "content": "[control_158]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 161,
+ "content": "[control_159]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 162,
+ "content": "[control_160]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 163,
+ "content": "[control_161]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 164,
+ "content": "[control_162]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 165,
+ "content": "[control_163]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 166,
+ "content": "[control_164]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 167,
+ "content": "[control_165]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 168,
+ "content": "[control_166]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 169,
+ "content": "[control_167]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 170,
+ "content": "[control_168]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 171,
+ "content": "[control_169]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 172,
+ "content": "[control_170]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 173,
+ "content": "[control_171]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 174,
+ "content": "[control_172]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 175,
+ "content": "[control_173]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 176,
+ "content": "[control_174]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 177,
+ "content": "[control_175]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 178,
+ "content": "[control_176]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 179,
+ "content": "[control_177]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 180,
+ "content": "[control_178]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 181,
+ "content": "[control_179]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 182,
+ "content": "[control_180]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 183,
+ "content": "[control_181]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 184,
+ "content": "[control_182]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 185,
+ "content": "[control_183]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 186,
+ "content": "[control_184]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 187,
+ "content": "[control_185]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 188,
+ "content": "[control_186]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 189,
+ "content": "[control_187]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 190,
+ "content": "[control_188]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 191,
+ "content": "[control_189]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 192,
+ "content": "[control_190]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 193,
+ "content": "[control_191]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 194,
+ "content": "[control_192]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 195,
+ "content": "[control_193]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 196,
+ "content": "[control_194]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 197,
+ "content": "[control_195]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 198,
+ "content": "[control_196]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 199,
+ "content": "[control_197]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 200,
+ "content": "[control_198]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 201,
+ "content": "[control_199]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 202,
+ "content": "[control_200]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 203,
+ "content": "[control_201]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 204,
+ "content": "[control_202]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 205,
+ "content": "[control_203]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 206,
+ "content": "[control_204]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 207,
+ "content": "[control_205]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 208,
+ "content": "[control_206]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 209,
+ "content": "[control_207]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 210,
+ "content": "[control_208]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 211,
+ "content": "[control_209]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 212,
+ "content": "[control_210]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 213,
+ "content": "[control_211]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 214,
+ "content": "[control_212]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 215,
+ "content": "[control_213]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 216,
+ "content": "[control_214]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 217,
+ "content": "[control_215]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 218,
+ "content": "[control_216]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 219,
+ "content": "[control_217]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 220,
+ "content": "[control_218]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 221,
+ "content": "[control_219]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 222,
+ "content": "[control_220]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 223,
+ "content": "[control_221]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 224,
+ "content": "[control_222]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 225,
+ "content": "[control_223]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 226,
+ "content": "[control_224]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 227,
+ "content": "[control_225]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 228,
+ "content": "[control_226]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 229,
+ "content": "[control_227]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 230,
+ "content": "[control_228]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 231,
+ "content": "[control_229]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 232,
+ "content": "[control_230]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 233,
+ "content": "[control_231]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 234,
+ "content": "[control_232]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 235,
+ "content": "[control_233]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 236,
+ "content": "[control_234]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 237,
+ "content": "[control_235]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 238,
+ "content": "[control_236]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 239,
+ "content": "[control_237]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 240,
+ "content": "[control_238]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 241,
+ "content": "[control_239]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 242,
+ "content": "[control_240]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 243,
+ "content": "[control_241]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 244,
+ "content": "[control_242]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 245,
+ "content": "[control_243]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 246,
+ "content": "[control_244]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 247,
+ "content": "[control_245]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 248,
+ "content": "[control_246]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 249,
+ "content": "[control_247]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 250,
+ "content": "[control_248]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 251,
+ "content": "[control_249]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 252,
+ "content": "[control_250]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 253,
+ "content": "[control_251]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 254,
+ "content": "[control_252]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 255,
+ "content": "[control_253]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 256,
+ "content": "[control_254]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 257,
+ "content": "[control_255]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 258,
+ "content": "[control_256]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 259,
+ "content": "[control_257]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 260,
+ "content": "[control_258]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 261,
+ "content": "[control_259]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 262,
+ "content": "[control_260]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 263,
+ "content": "[control_261]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 264,
+ "content": "[control_262]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 265,
+ "content": "[control_263]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 266,
+ "content": "[control_264]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 267,
+ "content": "[control_265]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 268,
+ "content": "[control_266]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 269,
+ "content": "[control_267]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 270,
+ "content": "[control_268]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 271,
+ "content": "[control_269]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 272,
+ "content": "[control_270]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 273,
+ "content": "[control_271]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 274,
+ "content": "[control_272]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 275,
+ "content": "[control_273]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 276,
+ "content": "[control_274]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 277,
+ "content": "[control_275]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 278,
+ "content": "[control_276]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 279,
+ "content": "[control_277]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 280,
+ "content": "[control_278]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 281,
+ "content": "[control_279]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 282,
+ "content": "[control_280]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 283,
+ "content": "[control_281]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 284,
+ "content": "[control_282]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 285,
+ "content": "[control_283]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 286,
+ "content": "[control_284]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 287,
+ "content": "[control_285]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 288,
+ "content": "[control_286]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 289,
+ "content": "[control_287]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 290,
+ "content": "[control_288]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 291,
+ "content": "[control_289]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 292,
+ "content": "[control_290]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 293,
+ "content": "[control_291]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 294,
+ "content": "[control_292]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 295,
+ "content": "[control_293]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 296,
+ "content": "[control_294]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 297,
+ "content": "[control_295]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 298,
+ "content": "[control_296]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 299,
+ "content": "[control_297]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 300,
+ "content": "[control_298]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 301,
+ "content": "[control_299]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 302,
+ "content": "[control_300]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 303,
+ "content": "[control_301]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 304,
+ "content": "[control_302]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 305,
+ "content": "[control_303]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 306,
+ "content": "[control_304]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 307,
+ "content": "[control_305]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 308,
+ "content": "[control_306]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 309,
+ "content": "[control_307]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 310,
+ "content": "[control_308]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 311,
+ "content": "[control_309]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 312,
+ "content": "[control_310]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 313,
+ "content": "[control_311]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 314,
+ "content": "[control_312]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 315,
+ "content": "[control_313]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 316,
+ "content": "[control_314]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 317,
+ "content": "[control_315]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 318,
+ "content": "[control_316]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 319,
+ "content": "[control_317]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 320,
+ "content": "[control_318]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 321,
+ "content": "[control_319]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 322,
+ "content": "[control_320]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 323,
+ "content": "[control_321]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 324,
+ "content": "[control_322]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 325,
+ "content": "[control_323]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 326,
+ "content": "[control_324]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 327,
+ "content": "[control_325]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 328,
+ "content": "[control_326]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 329,
+ "content": "[control_327]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 330,
+ "content": "[control_328]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 331,
+ "content": "[control_329]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 332,
+ "content": "[control_330]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 333,
+ "content": "[control_331]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 334,
+ "content": "[control_332]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 335,
+ "content": "[control_333]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 336,
+ "content": "[control_334]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 337,
+ "content": "[control_335]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 338,
+ "content": "[control_336]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 339,
+ "content": "[control_337]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 340,
+ "content": "[control_338]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 341,
+ "content": "[control_339]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 342,
+ "content": "[control_340]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 343,
+ "content": "[control_341]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 344,
+ "content": "[control_342]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 345,
+ "content": "[control_343]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 346,
+ "content": "[control_344]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 347,
+ "content": "[control_345]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 348,
+ "content": "[control_346]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 349,
+ "content": "[control_347]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 350,
+ "content": "[control_348]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 351,
+ "content": "[control_349]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 352,
+ "content": "[control_350]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 353,
+ "content": "[control_351]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 354,
+ "content": "[control_352]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 355,
+ "content": "[control_353]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 356,
+ "content": "[control_354]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 357,
+ "content": "[control_355]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 358,
+ "content": "[control_356]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 359,
+ "content": "[control_357]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 360,
+ "content": "[control_358]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 361,
+ "content": "[control_359]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 362,
+ "content": "[control_360]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 363,
+ "content": "[control_361]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 364,
+ "content": "[control_362]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 365,
+ "content": "[control_363]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 366,
+ "content": "[control_364]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 367,
+ "content": "[control_365]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 368,
+ "content": "[control_366]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 369,
+ "content": "[control_367]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 370,
+ "content": "[control_368]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 371,
+ "content": "[control_369]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 372,
+ "content": "[control_370]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 373,
+ "content": "[control_371]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 374,
+ "content": "[control_372]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 375,
+ "content": "[control_373]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 376,
+ "content": "[control_374]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 377,
+ "content": "[control_375]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 378,
+ "content": "[control_376]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 379,
+ "content": "[control_377]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 380,
+ "content": "[control_378]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 381,
+ "content": "[control_379]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 382,
+ "content": "[control_380]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 383,
+ "content": "[control_381]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 384,
+ "content": "[control_382]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 385,
+ "content": "[control_383]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 386,
+ "content": "[control_384]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 387,
+ "content": "[control_385]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 388,
+ "content": "[control_386]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 389,
+ "content": "[control_387]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 390,
+ "content": "[control_388]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 391,
+ "content": "[control_389]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 392,
+ "content": "[control_390]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 393,
+ "content": "[control_391]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 394,
+ "content": "[control_392]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 395,
+ "content": "[control_393]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 396,
+ "content": "[control_394]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 397,
+ "content": "[control_395]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 398,
+ "content": "[control_396]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 399,
+ "content": "[control_397]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 400,
+ "content": "[control_398]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 401,
+ "content": "[control_399]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 402,
+ "content": "[control_400]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 403,
+ "content": "[control_401]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 404,
+ "content": "[control_402]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 405,
+ "content": "[control_403]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 406,
+ "content": "[control_404]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 407,
+ "content": "[control_405]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 408,
+ "content": "[control_406]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 409,
+ "content": "[control_407]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 410,
+ "content": "[control_408]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 411,
+ "content": "[control_409]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 412,
+ "content": "[control_410]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 413,
+ "content": "[control_411]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 414,
+ "content": "[control_412]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 415,
+ "content": "[control_413]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 416,
+ "content": "[control_414]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 417,
+ "content": "[control_415]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 418,
+ "content": "[control_416]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 419,
+ "content": "[control_417]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 420,
+ "content": "[control_418]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 421,
+ "content": "[control_419]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 422,
+ "content": "[control_420]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 423,
+ "content": "[control_421]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 424,
+ "content": "[control_422]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 425,
+ "content": "[control_423]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 426,
+ "content": "[control_424]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 427,
+ "content": "[control_425]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 428,
+ "content": "[control_426]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 429,
+ "content": "[control_427]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 430,
+ "content": "[control_428]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 431,
+ "content": "[control_429]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 432,
+ "content": "[control_430]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 433,
+ "content": "[control_431]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 434,
+ "content": "[control_432]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 435,
+ "content": "[control_433]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 436,
+ "content": "[control_434]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 437,
+ "content": "[control_435]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 438,
+ "content": "[control_436]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 439,
+ "content": "[control_437]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 440,
+ "content": "[control_438]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 441,
+ "content": "[control_439]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 442,
+ "content": "[control_440]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 443,
+ "content": "[control_441]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 444,
+ "content": "[control_442]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 445,
+ "content": "[control_443]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 446,
+ "content": "[control_444]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 447,
+ "content": "[control_445]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 448,
+ "content": "[control_446]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 449,
+ "content": "[control_447]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 450,
+ "content": "[control_448]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 451,
+ "content": "[control_449]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 452,
+ "content": "[control_450]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 453,
+ "content": "[control_451]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 454,
+ "content": "[control_452]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 455,
+ "content": "[control_453]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 456,
+ "content": "[control_454]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 457,
+ "content": "[control_455]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 458,
+ "content": "[control_456]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 459,
+ "content": "[control_457]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 460,
+ "content": "[control_458]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 461,
+ "content": "[control_459]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 462,
+ "content": "[control_460]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 463,
+ "content": "[control_461]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 464,
+ "content": "[control_462]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 465,
+ "content": "[control_463]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 466,
+ "content": "[control_464]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 467,
+ "content": "[control_465]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 468,
+ "content": "[control_466]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 469,
+ "content": "[control_467]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 470,
+ "content": "[control_468]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 471,
+ "content": "[control_469]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 472,
+ "content": "[control_470]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 473,
+ "content": "[control_471]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 474,
+ "content": "[control_472]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 475,
+ "content": "[control_473]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 476,
+ "content": "[control_474]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 477,
+ "content": "[control_475]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 478,
+ "content": "[control_476]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 479,
+ "content": "[control_477]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 480,
+ "content": "[control_478]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 481,
+ "content": "[control_479]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 482,
+ "content": "[control_480]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 483,
+ "content": "[control_481]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 484,
+ "content": "[control_482]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 485,
+ "content": "[control_483]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 486,
+ "content": "[control_484]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 487,
+ "content": "[control_485]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 488,
+ "content": "[control_486]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 489,
+ "content": "[control_487]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 490,
+ "content": "[control_488]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 491,
+ "content": "[control_489]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 492,
+ "content": "[control_490]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 493,
+ "content": "[control_491]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 494,
+ "content": "[control_492]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 495,
+ "content": "[control_493]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 496,
+ "content": "[control_494]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 497,
+ "content": "[control_495]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 498,
+ "content": "[control_496]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 499,
+ "content": "[control_497]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 500,
+ "content": "[control_498]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 501,
+ "content": "[control_499]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 502,
+ "content": "[control_500]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 503,
+ "content": "[control_501]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 504,
+ "content": "[control_502]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 505,
+ "content": "[control_503]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 506,
+ "content": "[control_504]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 507,
+ "content": "[control_505]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 508,
+ "content": "[control_506]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 509,
+ "content": "[control_507]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 510,
+ "content": "[control_508]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 511,
+ "content": "[control_509]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 512,
+ "content": "[control_510]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 513,
+ "content": "[control_511]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 514,
+ "content": "[control_512]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 515,
+ "content": "[control_513]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 516,
+ "content": "[control_514]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 517,
+ "content": "[control_515]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 518,
+ "content": "[control_516]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 519,
+ "content": "[control_517]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 520,
+ "content": "[control_518]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 521,
+ "content": "[control_519]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 522,
+ "content": "[control_520]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 523,
+ "content": "[control_521]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 524,
+ "content": "[control_522]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 525,
+ "content": "[control_523]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 526,
+ "content": "[control_524]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 527,
+ "content": "[control_525]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 528,
+ "content": "[control_526]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 529,
+ "content": "[control_527]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 530,
+ "content": "[control_528]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 531,
+ "content": "[control_529]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 532,
+ "content": "[control_530]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 533,
+ "content": "[control_531]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 534,
+ "content": "[control_532]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 535,
+ "content": "[control_533]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 536,
+ "content": "[control_534]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 537,
+ "content": "[control_535]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 538,
+ "content": "[control_536]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 539,
+ "content": "[control_537]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 540,
+ "content": "[control_538]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 541,
+ "content": "[control_539]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 542,
+ "content": "[control_540]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 543,
+ "content": "[control_541]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 544,
+ "content": "[control_542]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 545,
+ "content": "[control_543]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 546,
+ "content": "[control_544]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 547,
+ "content": "[control_545]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 548,
+ "content": "[control_546]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 549,
+ "content": "[control_547]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 550,
+ "content": "[control_548]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 551,
+ "content": "[control_549]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 552,
+ "content": "[control_550]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 553,
+ "content": "[control_551]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 554,
+ "content": "[control_552]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 555,
+ "content": "[control_553]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 556,
+ "content": "[control_554]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 557,
+ "content": "[control_555]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 558,
+ "content": "[control_556]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 559,
+ "content": "[control_557]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 560,
+ "content": "[control_558]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 561,
+ "content": "[control_559]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 562,
+ "content": "[control_560]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 563,
+ "content": "[control_561]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 564,
+ "content": "[control_562]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 565,
+ "content": "[control_563]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 566,
+ "content": "[control_564]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 567,
+ "content": "[control_565]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 568,
+ "content": "[control_566]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 569,
+ "content": "[control_567]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 570,
+ "content": "[control_568]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 571,
+ "content": "[control_569]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 572,
+ "content": "[control_570]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 573,
+ "content": "[control_571]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 574,
+ "content": "[control_572]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 575,
+ "content": "[control_573]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 576,
+ "content": "[control_574]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 577,
+ "content": "[control_575]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 578,
+ "content": "[control_576]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 579,
+ "content": "[control_577]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 580,
+ "content": "[control_578]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 581,
+ "content": "[control_579]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 582,
+ "content": "[control_580]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 583,
+ "content": "[control_581]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 584,
+ "content": "[control_582]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 585,
+ "content": "[control_583]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 586,
+ "content": "[control_584]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 587,
+ "content": "[control_585]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 588,
+ "content": "[control_586]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 589,
+ "content": "[control_587]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 590,
+ "content": "[control_588]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 591,
+ "content": "[control_589]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 592,
+ "content": "[control_590]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 593,
+ "content": "[control_591]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 594,
+ "content": "[control_592]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 595,
+ "content": "[control_593]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 596,
+ "content": "[control_594]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 597,
+ "content": "[control_595]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 598,
+ "content": "[control_596]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 599,
+ "content": "[control_597]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 600,
+ "content": "[control_598]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 601,
+ "content": "[control_599]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 602,
+ "content": "[control_600]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 603,
+ "content": "[control_601]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 604,
+ "content": "[control_602]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 605,
+ "content": "[control_603]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 606,
+ "content": "[control_604]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 607,
+ "content": "[control_605]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 608,
+ "content": "[control_606]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 609,
+ "content": "[control_607]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 610,
+ "content": "[control_608]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 611,
+ "content": "[control_609]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 612,
+ "content": "[control_610]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 613,
+ "content": "[control_611]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 614,
+ "content": "[control_612]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 615,
+ "content": "[control_613]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 616,
+ "content": "[control_614]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 617,
+ "content": "[control_615]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 618,
+ "content": "[control_616]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 619,
+ "content": "[control_617]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 620,
+ "content": "[control_618]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 621,
+ "content": "[control_619]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 622,
+ "content": "[control_620]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 623,
+ "content": "[control_621]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 624,
+ "content": "[control_622]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 625,
+ "content": "[control_623]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 626,
+ "content": "[control_624]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 627,
+ "content": "[control_625]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 628,
+ "content": "[control_626]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 629,
+ "content": "[control_627]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 630,
+ "content": "[control_628]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 631,
+ "content": "[control_629]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 632,
+ "content": "[control_630]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 633,
+ "content": "[control_631]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 634,
+ "content": "[control_632]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 635,
+ "content": "[control_633]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 636,
+ "content": "[control_634]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 637,
+ "content": "[control_635]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 638,
+ "content": "[control_636]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 639,
+ "content": "[control_637]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 640,
+ "content": "[control_638]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 641,
+ "content": "[control_639]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 642,
+ "content": "[control_640]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 643,
+ "content": "[control_641]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 644,
+ "content": "[control_642]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 645,
+ "content": "[control_643]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 646,
+ "content": "[control_644]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 647,
+ "content": "[control_645]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 648,
+ "content": "[control_646]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 649,
+ "content": "[control_647]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 650,
+ "content": "[control_648]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 651,
+ "content": "[control_649]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 652,
+ "content": "[control_650]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 653,
+ "content": "[control_651]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 654,
+ "content": "[control_652]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 655,
+ "content": "[control_653]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 656,
+ "content": "[control_654]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 657,
+ "content": "[control_655]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 658,
+ "content": "[control_656]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 659,
+ "content": "[control_657]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 660,
+ "content": "[control_658]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 661,
+ "content": "[control_659]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 662,
+ "content": "[control_660]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 663,
+ "content": "[control_661]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 664,
+ "content": "[control_662]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 665,
+ "content": "[control_663]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 666,
+ "content": "[control_664]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 667,
+ "content": "[control_665]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 668,
+ "content": "[control_666]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 669,
+ "content": "[control_667]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 670,
+ "content": "[control_668]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 671,
+ "content": "[control_669]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 672,
+ "content": "[control_670]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 673,
+ "content": "[control_671]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 674,
+ "content": "[control_672]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 675,
+ "content": "[control_673]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 676,
+ "content": "[control_674]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 677,
+ "content": "[control_675]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 678,
+ "content": "[control_676]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 679,
+ "content": "[control_677]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 680,
+ "content": "[control_678]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 681,
+ "content": "[control_679]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 682,
+ "content": "[control_680]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 683,
+ "content": "[control_681]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 684,
+ "content": "[control_682]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 685,
+ "content": "[control_683]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 686,
+ "content": "[control_684]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 687,
+ "content": "[control_685]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 688,
+ "content": "[control_686]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 689,
+ "content": "[control_687]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 690,
+ "content": "[control_688]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 691,
+ "content": "[control_689]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 692,
+ "content": "[control_690]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 693,
+ "content": "[control_691]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 694,
+ "content": "[control_692]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 695,
+ "content": "[control_693]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 696,
+ "content": "[control_694]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 697,
+ "content": "[control_695]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 698,
+ "content": "[control_696]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 699,
+ "content": "[control_697]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 700,
+ "content": "[control_698]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 701,
+ "content": "[control_699]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 702,
+ "content": "[control_700]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 703,
+ "content": "[control_701]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 704,
+ "content": "[control_702]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 705,
+ "content": "[control_703]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 706,
+ "content": "[control_704]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 707,
+ "content": "[control_705]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 708,
+ "content": "[control_706]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 709,
+ "content": "[control_707]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 710,
+ "content": "[control_708]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 711,
+ "content": "[control_709]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 712,
+ "content": "[control_710]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 713,
+ "content": "[control_711]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 714,
+ "content": "[control_712]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 715,
+ "content": "[control_713]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 716,
+ "content": "[control_714]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 717,
+ "content": "[control_715]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 718,
+ "content": "[control_716]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 719,
+ "content": "[control_717]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 720,
+ "content": "[control_718]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 721,
+ "content": "[control_719]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 722,
+ "content": "[control_720]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 723,
+ "content": "[control_721]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 724,
+ "content": "[control_722]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 725,
+ "content": "[control_723]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 726,
+ "content": "[control_724]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 727,
+ "content": "[control_725]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 728,
+ "content": "[control_726]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 729,
+ "content": "[control_727]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 730,
+ "content": "[control_728]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 731,
+ "content": "[control_729]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 732,
+ "content": "[control_730]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 733,
+ "content": "[control_731]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 734,
+ "content": "[control_732]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 735,
+ "content": "[control_733]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 736,
+ "content": "[control_734]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 737,
+ "content": "[control_735]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 738,
+ "content": "[control_736]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 739,
+ "content": "[control_737]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 740,
+ "content": "[control_738]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 741,
+ "content": "[control_739]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 742,
+ "content": "[control_740]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 743,
+ "content": "[control_741]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 744,
+ "content": "[control_742]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 745,
+ "content": "[control_743]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 746,
+ "content": "[control_744]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 747,
+ "content": "[control_745]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 748,
+ "content": "[control_746]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 749,
+ "content": "[control_747]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 750,
+ "content": "[control_748]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 751,
+ "content": "[control_749]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 752,
+ "content": "[control_750]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 753,
+ "content": "[control_751]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 754,
+ "content": "[control_752]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 755,
+ "content": "[control_753]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 756,
+ "content": "[control_754]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 757,
+ "content": "[control_755]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 758,
+ "content": "[control_756]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 759,
+ "content": "[control_757]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 760,
+ "content": "[control_758]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 761,
+ "content": "[control_759]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 762,
+ "content": "[control_760]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 763,
+ "content": "[control_761]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 764,
+ "content": "[control_762]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 765,
+ "content": "[control_763]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 766,
+ "content": "[control_764]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 767,
+ "content": "[control_765]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 768,
+ "content": "[control_766]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 769,
+ "content": "[control_767]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ },
+ {
+ "id": 770,
+ "content": "[control_768]",
+ "single_word": false,
+ "lstrip": false,
+ "rstrip": false,
+ "normalized": false,
+ "special": true
+ }
+ ],
+ "normalizer": null,
+ "pre_tokenizer": {
+ "type": "Metaspace",
+ "replacement": "▁",
+ "prepend_scheme": "first",
+ "split": false
+ },
+ "post_processor": {
+ "type": "TemplateProcessing",
+ "single": [
+ {
+ "SpecialToken": {
+ "id": "",
+ "type_id": 0
+ }
+ },
+ {
+ "Sequence": {
+ "id": "A",
+ "type_id": 0
+ }
+ }
+ ],
+ "pair": [
+ {
+ "SpecialToken": {
+ "id": "",
+ "type_id": 0
+ }
+ },
+ {
+ "Sequence": {
+ "id": "A",
+ "type_id": 0
+ }
+ },
+ {
+ "SpecialToken": {
+ "id": "",
+ "type_id": 1
+ }
+ },
+ {
+ "Sequence": {
+ "id": "B",
+ "type_id": 1
+ }
+ }
+ ],
+ "special_tokens": {
+ "": {
+ "id": "",
+ "ids": [
+ 1
+ ],
+ "tokens": [
+ ""
+ ]
+ }
+ }
+ },
+ "decoder": {
+ "type": "Sequence",
+ "decoders": [
+ {
+ "type": "Replace",
+ "pattern": {
+ "String": "▁"
+ },
+ "content": " "
+ },
+ {
+ "type": "ByteFallback"
+ },
+ {
+ "type": "Fuse"
+ },
+ {
+ "type": "Strip",
+ "content": " ",
+ "start": 1,
+ "stop": 0
+ }
+ ]
+ },
+ "model": {
+ "type": "BPE",
+ "dropout": null,
+ "unk_token": "",
+ "continuing_subword_prefix": null,
+ "end_of_word_suffix": null,
+ "fuse_unk": true,
+ "byte_fallback": true,
+ "ignore_merges": false,
+ "vocab": {
+ "": 0,
+ "": 1,
+ "": 2,
+ "[INST]": 3,
+ "[/INST]": 4,
+ "[TOOL_CALLS]": 5,
+ "[AVAILABLE_TOOLS]": 6,
+ "[/AVAILABLE_TOOLS]": 7,
+ "[TOOL_RESULTS]": 8,
+ "[/TOOL_RESULTS]": 9,
+ "[control_8]": 10,
+ "[control_9]": 11,
+ "[control_10]": 12,
+ "[control_11]": 13,
+ "[control_12]": 14,
+ "[control_13]": 15,
+ "[control_14]": 16,
+ "[control_15]": 17,
+ "[control_16]": 18,
+ "[control_17]": 19,
+ "[control_18]": 20,
+ "[control_19]": 21,
+ "[control_20]": 22,
+ "[control_21]": 23,
+ "[control_22]": 24,
+ "[control_23]": 25,
+ "[control_24]": 26,
+ "[control_25]": 27,
+ "[control_26]": 28,
+ "[control_27]": 29,
+ "[control_28]": 30,
+ "[control_29]": 31,
+ "[control_30]": 32,
+ "[control_31]": 33,
+ "[control_32]": 34,
+ "[control_33]": 35,
+ "[control_34]": 36,
+ "[control_35]": 37,
+ "[control_36]": 38,
+ "[control_37]": 39,
+ "[control_38]": 40,
+ "[control_39]": 41,
+ "[control_40]": 42,
+ "[control_41]": 43,
+ "[control_42]": 44,
+ "[control_43]": 45,
+ "[control_44]": 46,
+ "[control_45]": 47,
+ "[control_46]": 48,
+ "[control_47]": 49,
+ "[control_48]": 50,
+ "[control_49]": 51,
+ "[control_50]": 52,
+ "[control_51]": 53,
+ "[control_52]": 54,
+ "[control_53]": 55,
+ "[control_54]": 56,
+ "[control_55]": 57,
+ "[control_56]": 58,
+ "[control_57]": 59,
+ "[control_58]": 60,
+ "[control_59]": 61,
+ "[control_60]": 62,
+ "[control_61]": 63,
+ "[control_62]": 64,
+ "[control_63]": 65,
+ "[control_64]": 66,
+ "[control_65]": 67,
+ "[control_66]": 68,
+ "[control_67]": 69,
+ "[control_68]": 70,
+ "[control_69]": 71,
+ "[control_70]": 72,
+ "[control_71]": 73,
+ "[control_72]": 74,
+ "[control_73]": 75,
+ "[control_74]": 76,
+ "[control_75]": 77,
+ "[control_76]": 78,
+ "[control_77]": 79,
+ "[control_78]": 80,
+ "[control_79]": 81,
+ "[control_80]": 82,
+ "[control_81]": 83,
+ "[control_82]": 84,
+ "[control_83]": 85,
+ "[control_84]": 86,
+ "[control_85]": 87,
+ "[control_86]": 88,
+ "[control_87]": 89,
+ "[control_88]": 90,
+ "[control_89]": 91,
+ "[control_90]": 92,
+ "[control_91]": 93,
+ "[control_92]": 94,
+ "[control_93]": 95,
+ "[control_94]": 96,
+ "[control_95]": 97,
+ "[control_96]": 98,
+ "[control_97]": 99,
+ "[control_98]": 100,
+ "[control_99]": 101,
+ "[control_100]": 102,
+ "[control_101]": 103,
+ "[control_102]": 104,
+ "[control_103]": 105,
+ "[control_104]": 106,
+ "[control_105]": 107,
+ "[control_106]": 108,
+ "[control_107]": 109,
+ "[control_108]": 110,
+ "[control_109]": 111,
+ "[control_110]": 112,
+ "[control_111]": 113,
+ "[control_112]": 114,
+ "[control_113]": 115,
+ "[control_114]": 116,
+ "[control_115]": 117,
+ "[control_116]": 118,
+ "[control_117]": 119,
+ "[control_118]": 120,
+ "[control_119]": 121,
+ "[control_120]": 122,
+ "[control_121]": 123,
+ "[control_122]": 124,
+ "[control_123]": 125,
+ "[control_124]": 126,
+ "[control_125]": 127,
+ "[control_126]": 128,
+ "[control_127]": 129,
+ "[control_128]": 130,
+ "[control_129]": 131,
+ "[control_130]": 132,
+ "[control_131]": 133,
+ "[control_132]": 134,
+ "[control_133]": 135,
+ "[control_134]": 136,
+ "[control_135]": 137,
+ "[control_136]": 138,
+ "[control_137]": 139,
+ "[control_138]": 140,
+ "[control_139]": 141,
+ "[control_140]": 142,
+ "[control_141]": 143,
+ "[control_142]": 144,
+ "[control_143]": 145,
+ "[control_144]": 146,
+ "[control_145]": 147,
+ "[control_146]": 148,
+ "[control_147]": 149,
+ "[control_148]": 150,
+ "[control_149]": 151,
+ "[control_150]": 152,
+ "[control_151]": 153,
+ "[control_152]": 154,
+ "[control_153]": 155,
+ "[control_154]": 156,
+ "[control_155]": 157,
+ "[control_156]": 158,
+ "[control_157]": 159,
+ "[control_158]": 160,
+ "[control_159]": 161,
+ "[control_160]": 162,
+ "[control_161]": 163,
+ "[control_162]": 164,
+ "[control_163]": 165,
+ "[control_164]": 166,
+ "[control_165]": 167,
+ "[control_166]": 168,
+ "[control_167]": 169,
+ "[control_168]": 170,
+ "[control_169]": 171,
+ "[control_170]": 172,
+ "[control_171]": 173,
+ "[control_172]": 174,
+ "[control_173]": 175,
+ "[control_174]": 176,
+ "[control_175]": 177,
+ "[control_176]": 178,
+ "[control_177]": 179,
+ "[control_178]": 180,
+ "[control_179]": 181,
+ "[control_180]": 182,
+ "[control_181]": 183,
+ "[control_182]": 184,
+ "[control_183]": 185,
+ "[control_184]": 186,
+ "[control_185]": 187,
+ "[control_186]": 188,
+ "[control_187]": 189,
+ "[control_188]": 190,
+ "[control_189]": 191,
+ "[control_190]": 192,
+ "[control_191]": 193,
+ "[control_192]": 194,
+ "[control_193]": 195,
+ "[control_194]": 196,
+ "[control_195]": 197,
+ "[control_196]": 198,
+ "[control_197]": 199,
+ "[control_198]": 200,
+ "[control_199]": 201,
+ "[control_200]": 202,
+ "[control_201]": 203,
+ "[control_202]": 204,
+ "[control_203]": 205,
+ "[control_204]": 206,
+ "[control_205]": 207,
+ "[control_206]": 208,
+ "[control_207]": 209,
+ "[control_208]": 210,
+ "[control_209]": 211,
+ "[control_210]": 212,
+ "[control_211]": 213,
+ "[control_212]": 214,
+ "[control_213]": 215,
+ "[control_214]": 216,
+ "[control_215]": 217,
+ "[control_216]": 218,
+ "[control_217]": 219,
+ "[control_218]": 220,
+ "[control_219]": 221,
+ "[control_220]": 222,
+ "[control_221]": 223,
+ "[control_222]": 224,
+ "[control_223]": 225,
+ "[control_224]": 226,
+ "[control_225]": 227,
+ "[control_226]": 228,
+ "[control_227]": 229,
+ "[control_228]": 230,
+ "[control_229]": 231,
+ "[control_230]": 232,
+ "[control_231]": 233,
+ "[control_232]": 234,
+ "[control_233]": 235,
+ "[control_234]": 236,
+ "[control_235]": 237,
+ "[control_236]": 238,
+ "[control_237]": 239,
+ "[control_238]": 240,
+ "[control_239]": 241,
+ "[control_240]": 242,
+ "[control_241]": 243,
+ "[control_242]": 244,
+ "[control_243]": 245,
+ "[control_244]": 246,
+ "[control_245]": 247,
+ "[control_246]": 248,
+ "[control_247]": 249,
+ "[control_248]": 250,
+ "[control_249]": 251,
+ "[control_250]": 252,
+ "[control_251]": 253,
+ "[control_252]": 254,
+ "[control_253]": 255,
+ "[control_254]": 256,
+ "[control_255]": 257,
+ "[control_256]": 258,
+ "[control_257]": 259,
+ "[control_258]": 260,
+ "[control_259]": 261,
+ "[control_260]": 262,
+ "[control_261]": 263,
+ "[control_262]": 264,
+ "[control_263]": 265,
+ "[control_264]": 266,
+ "[control_265]": 267,
+ "[control_266]": 268,
+ "[control_267]": 269,
+ "[control_268]": 270,
+ "[control_269]": 271,
+ "[control_270]": 272,
+ "[control_271]": 273,
+ "[control_272]": 274,
+ "[control_273]": 275,
+ "[control_274]": 276,
+ "[control_275]": 277,
+ "[control_276]": 278,
+ "[control_277]": 279,
+ "[control_278]": 280,
+ "[control_279]": 281,
+ "[control_280]": 282,
+ "[control_281]": 283,
+ "[control_282]": 284,
+ "[control_283]": 285,
+ "[control_284]": 286,
+ "[control_285]": 287,
+ "[control_286]": 288,
+ "[control_287]": 289,
+ "[control_288]": 290,
+ "[control_289]": 291,
+ "[control_290]": 292,
+ "[control_291]": 293,
+ "[control_292]": 294,
+ "[control_293]": 295,
+ "[control_294]": 296,
+ "[control_295]": 297,
+ "[control_296]": 298,
+ "[control_297]": 299,
+ "[control_298]": 300,
+ "[control_299]": 301,
+ "[control_300]": 302,
+ "[control_301]": 303,
+ "[control_302]": 304,
+ "[control_303]": 305,
+ "[control_304]": 306,
+ "[control_305]": 307,
+ "[control_306]": 308,
+ "[control_307]": 309,
+ "[control_308]": 310,
+ "[control_309]": 311,
+ "[control_310]": 312,
+ "[control_311]": 313,
+ "[control_312]": 314,
+ "[control_313]": 315,
+ "[control_314]": 316,
+ "[control_315]": 317,
+ "[control_316]": 318,
+ "[control_317]": 319,
+ "[control_318]": 320,
+ "[control_319]": 321,
+ "[control_320]": 322,
+ "[control_321]": 323,
+ "[control_322]": 324,
+ "[control_323]": 325,
+ "[control_324]": 326,
+ "[control_325]": 327,
+ "[control_326]": 328,
+ "[control_327]": 329,
+ "[control_328]": 330,
+ "[control_329]": 331,
+ "[control_330]": 332,
+ "[control_331]": 333,
+ "[control_332]": 334,
+ "[control_333]": 335,
+ "[control_334]": 336,
+ "[control_335]": 337,
+ "[control_336]": 338,
+ "[control_337]": 339,
+ "[control_338]": 340,
+ "[control_339]": 341,
+ "[control_340]": 342,
+ "[control_341]": 343,
+ "[control_342]": 344,
+ "[control_343]": 345,
+ "[control_344]": 346,
+ "[control_345]": 347,
+ "[control_346]": 348,
+ "[control_347]": 349,
+ "[control_348]": 350,
+ "[control_349]": 351,
+ "[control_350]": 352,
+ "[control_351]": 353,
+ "[control_352]": 354,
+ "[control_353]": 355,
+ "[control_354]": 356,
+ "[control_355]": 357,
+ "[control_356]": 358,
+ "[control_357]": 359,
+ "[control_358]": 360,
+ "[control_359]": 361,
+ "[control_360]": 362,
+ "[control_361]": 363,
+ "[control_362]": 364,
+ "[control_363]": 365,
+ "[control_364]": 366,
+ "[control_365]": 367,
+ "[control_366]": 368,
+ "[control_367]": 369,
+ "[control_368]": 370,
+ "[control_369]": 371,
+ "[control_370]": 372,
+ "[control_371]": 373,
+ "[control_372]": 374,
+ "[control_373]": 375,
+ "[control_374]": 376,
+ "[control_375]": 377,
+ "[control_376]": 378,
+ "[control_377]": 379,
+ "[control_378]": 380,
+ "[control_379]": 381,
+ "[control_380]": 382,
+ "[control_381]": 383,
+ "[control_382]": 384,
+ "[control_383]": 385,
+ "[control_384]": 386,
+ "[control_385]": 387,
+ "[control_386]": 388,
+ "[control_387]": 389,
+ "[control_388]": 390,
+ "[control_389]": 391,
+ "[control_390]": 392,
+ "[control_391]": 393,
+ "[control_392]": 394,
+ "[control_393]": 395,
+ "[control_394]": 396,
+ "[control_395]": 397,
+ "[control_396]": 398,
+ "[control_397]": 399,
+ "[control_398]": 400,
+ "[control_399]": 401,
+ "[control_400]": 402,
+ "[control_401]": 403,
+ "[control_402]": 404,
+ "[control_403]": 405,
+ "[control_404]": 406,
+ "[control_405]": 407,
+ "[control_406]": 408,
+ "[control_407]": 409,
+ "[control_408]": 410,
+ "[control_409]": 411,
+ "[control_410]": 412,
+ "[control_411]": 413,
+ "[control_412]": 414,
+ "[control_413]": 415,
+ "[control_414]": 416,
+ "[control_415]": 417,
+ "[control_416]": 418,
+ "[control_417]": 419,
+ "[control_418]": 420,
+ "[control_419]": 421,
+ "[control_420]": 422,
+ "[control_421]": 423,
+ "[control_422]": 424,
+ "[control_423]": 425,
+ "[control_424]": 426,
+ "[control_425]": 427,
+ "[control_426]": 428,
+ "[control_427]": 429,
+ "[control_428]": 430,
+ "[control_429]": 431,
+ "[control_430]": 432,
+ "[control_431]": 433,
+ "[control_432]": 434,
+ "[control_433]": 435,
+ "[control_434]": 436,
+ "[control_435]": 437,
+ "[control_436]": 438,
+ "[control_437]": 439,
+ "[control_438]": 440,
+ "[control_439]": 441,
+ "[control_440]": 442,
+ "[control_441]": 443,
+ "[control_442]": 444,
+ "[control_443]": 445,
+ "[control_444]": 446,
+ "[control_445]": 447,
+ "[control_446]": 448,
+ "[control_447]": 449,
+ "[control_448]": 450,
+ "[control_449]": 451,
+ "[control_450]": 452,
+ "[control_451]": 453,
+ "[control_452]": 454,
+ "[control_453]": 455,
+ "[control_454]": 456,
+ "[control_455]": 457,
+ "[control_456]": 458,
+ "[control_457]": 459,
+ "[control_458]": 460,
+ "[control_459]": 461,
+ "[control_460]": 462,
+ "[control_461]": 463,
+ "[control_462]": 464,
+ "[control_463]": 465,
+ "[control_464]": 466,
+ "[control_465]": 467,
+ "[control_466]": 468,
+ "[control_467]": 469,
+ "[control_468]": 470,
+ "[control_469]": 471,
+ "[control_470]": 472,
+ "[control_471]": 473,
+ "[control_472]": 474,
+ "[control_473]": 475,
+ "[control_474]": 476,
+ "[control_475]": 477,
+ "[control_476]": 478,
+ "[control_477]": 479,
+ "[control_478]": 480,
+ "[control_479]": 481,
+ "[control_480]": 482,
+ "[control_481]": 483,
+ "[control_482]": 484,
+ "[control_483]": 485,
+ "[control_484]": 486,
+ "[control_485]": 487,
+ "[control_486]": 488,
+ "[control_487]": 489,
+ "[control_488]": 490,
+ "[control_489]": 491,
+ "[control_490]": 492,
+ "[control_491]": 493,
+ "[control_492]": 494,
+ "[control_493]": 495,
+ "[control_494]": 496,
+ "[control_495]": 497,
+ "[control_496]": 498,
+ "[control_497]": 499,
+ "[control_498]": 500,
+ "[control_499]": 501,
+ "[control_500]": 502,
+ "[control_501]": 503,
+ "[control_502]": 504,
+ "[control_503]": 505,
+ "[control_504]": 506,
+ "[control_505]": 507,
+ "[control_506]": 508,
+ "[control_507]": 509,
+ "[control_508]": 510,
+ "[control_509]": 511,
+ "[control_510]": 512,
+ "[control_511]": 513,
+ "[control_512]": 514,
+ "[control_513]": 515,
+ "[control_514]": 516,
+ "[control_515]": 517,
+ "[control_516]": 518,
+ "[control_517]": 519,
+ "[control_518]": 520,
+ "[control_519]": 521,
+ "[control_520]": 522,
+ "[control_521]": 523,
+ "[control_522]": 524,
+ "[control_523]": 525,
+ "[control_524]": 526,
+ "[control_525]": 527,
+ "[control_526]": 528,
+ "[control_527]": 529,
+ "[control_528]": 530,
+ "[control_529]": 531,
+ "[control_530]": 532,
+ "[control_531]": 533,
+ "[control_532]": 534,
+ "[control_533]": 535,
+ "[control_534]": 536,
+ "[control_535]": 537,
+ "[control_536]": 538,
+ "[control_537]": 539,
+ "[control_538]": 540,
+ "[control_539]": 541,
+ "[control_540]": 542,
+ "[control_541]": 543,
+ "[control_542]": 544,
+ "[control_543]": 545,
+ "[control_544]": 546,
+ "[control_545]": 547,
+ "[control_546]": 548,
+ "[control_547]": 549,
+ "[control_548]": 550,
+ "[control_549]": 551,
+ "[control_550]": 552,
+ "[control_551]": 553,
+ "[control_552]": 554,
+ "[control_553]": 555,
+ "[control_554]": 556,
+ "[control_555]": 557,
+ "[control_556]": 558,
+ "[control_557]": 559,
+ "[control_558]": 560,
+ "[control_559]": 561,
+ "[control_560]": 562,
+ "[control_561]": 563,
+ "[control_562]": 564,
+ "[control_563]": 565,
+ "[control_564]": 566,
+ "[control_565]": 567,
+ "[control_566]": 568,
+ "[control_567]": 569,
+ "[control_568]": 570,
+ "[control_569]": 571,
+ "[control_570]": 572,
+ "[control_571]": 573,
+ "[control_572]": 574,
+ "[control_573]": 575,
+ "[control_574]": 576,
+ "[control_575]": 577,
+ "[control_576]": 578,
+ "[control_577]": 579,
+ "[control_578]": 580,
+ "[control_579]": 581,
+ "[control_580]": 582,
+ "[control_581]": 583,
+ "[control_582]": 584,
+ "[control_583]": 585,
+ "[control_584]": 586,
+ "[control_585]": 587,
+ "[control_586]": 588,
+ "[control_587]": 589,
+ "[control_588]": 590,
+ "[control_589]": 591,
+ "[control_590]": 592,
+ "[control_591]": 593,
+ "[control_592]": 594,
+ "[control_593]": 595,
+ "[control_594]": 596,
+ "[control_595]": 597,
+ "[control_596]": 598,
+ "[control_597]": 599,
+ "[control_598]": 600,
+ "[control_599]": 601,
+ "[control_600]": 602,
+ "[control_601]": 603,
+ "[control_602]": 604,
+ "[control_603]": 605,
+ "[control_604]": 606,
+ "[control_605]": 607,
+ "[control_606]": 608,
+ "[control_607]": 609,
+ "[control_608]": 610,
+ "[control_609]": 611,
+ "[control_610]": 612,
+ "[control_611]": 613,
+ "[control_612]": 614,
+ "[control_613]": 615,
+ "[control_614]": 616,
+ "[control_615]": 617,
+ "[control_616]": 618,
+ "[control_617]": 619,
+ "[control_618]": 620,
+ "[control_619]": 621,
+ "[control_620]": 622,
+ "[control_621]": 623,
+ "[control_622]": 624,
+ "[control_623]": 625,
+ "[control_624]": 626,
+ "[control_625]": 627,
+ "[control_626]": 628,
+ "[control_627]": 629,
+ "[control_628]": 630,
+ "[control_629]": 631,
+ "[control_630]": 632,
+ "[control_631]": 633,
+ "[control_632]": 634,
+ "[control_633]": 635,
+ "[control_634]": 636,
+ "[control_635]": 637,
+ "[control_636]": 638,
+ "[control_637]": 639,
+ "[control_638]": 640,
+ "[control_639]": 641,
+ "[control_640]": 642,
+ "[control_641]": 643,
+ "[control_642]": 644,
+ "[control_643]": 645,
+ "[control_644]": 646,
+ "[control_645]": 647,
+ "[control_646]": 648,
+ "[control_647]": 649,
+ "[control_648]": 650,
+ "[control_649]": 651,
+ "[control_650]": 652,
+ "[control_651]": 653,
+ "[control_652]": 654,
+ "[control_653]": 655,
+ "[control_654]": 656,
+ "[control_655]": 657,
+ "[control_656]": 658,
+ "[control_657]": 659,
+ "[control_658]": 660,
+ "[control_659]": 661,
+ "[control_660]": 662,
+ "[control_661]": 663,
+ "[control_662]": 664,
+ "[control_663]": 665,
+ "[control_664]": 666,
+ "[control_665]": 667,
+ "[control_666]": 668,
+ "[control_667]": 669,
+ "[control_668]": 670,
+ "[control_669]": 671,
+ "[control_670]": 672,
+ "[control_671]": 673,
+ "[control_672]": 674,
+ "[control_673]": 675,
+ "[control_674]": 676,
+ "[control_675]": 677,
+ "[control_676]": 678,
+ "[control_677]": 679,
+ "[control_678]": 680,
+ "[control_679]": 681,
+ "[control_680]": 682,
+ "[control_681]": 683,
+ "[control_682]": 684,
+ "[control_683]": 685,
+ "[control_684]": 686,
+ "[control_685]": 687,
+ "[control_686]": 688,
+ "[control_687]": 689,
+ "[control_688]": 690,
+ "[control_689]": 691,
+ "[control_690]": 692,
+ "[control_691]": 693,
+ "[control_692]": 694,
+ "[control_693]": 695,
+ "[control_694]": 696,
+ "[control_695]": 697,
+ "[control_696]": 698,
+ "[control_697]": 699,
+ "[control_698]": 700,
+ "[control_699]": 701,
+ "[control_700]": 702,
+ "[control_701]": 703,
+ "[control_702]": 704,
+ "[control_703]": 705,
+ "[control_704]": 706,
+ "[control_705]": 707,
+ "[control_706]": 708,
+ "[control_707]": 709,
+ "[control_708]": 710,
+ "[control_709]": 711,
+ "[control_710]": 712,
+ "[control_711]": 713,
+ "[control_712]": 714,
+ "[control_713]": 715,
+ "[control_714]": 716,
+ "[control_715]": 717,
+ "[control_716]": 718,
+ "[control_717]": 719,
+ "[control_718]": 720,
+ "[control_719]": 721,
+ "[control_720]": 722,
+ "[control_721]": 723,
+ "[control_722]": 724,
+ "[control_723]": 725,
+ "[control_724]": 726,
+ "[control_725]": 727,
+ "[control_726]": 728,
+ "[control_727]": 729,
+ "[control_728]": 730,
+ "[control_729]": 731,
+ "[control_730]": 732,
+ "[control_731]": 733,
+ "[control_732]": 734,
+ "[control_733]": 735,
+ "[control_734]": 736,
+ "[control_735]": 737,
+ "[control_736]": 738,
+ "[control_737]": 739,
+ "[control_738]": 740,
+ "[control_739]": 741,
+ "[control_740]": 742,
+ "[control_741]": 743,
+ "[control_742]": 744,
+ "[control_743]": 745,
+ "[control_744]": 746,
+ "[control_745]": 747,
+ "[control_746]": 748,
+ "[control_747]": 749,
+ "[control_748]": 750,
+ "[control_749]": 751,
+ "[control_750]": 752,
+ "[control_751]": 753,
+ "[control_752]": 754,
+ "[control_753]": 755,
+ "[control_754]": 756,
+ "[control_755]": 757,
+ "[control_756]": 758,
+ "[control_757]": 759,
+ "[control_758]": 760,
+ "[control_759]": 761,
+ "[control_760]": 762,
+ "[control_761]": 763,
+ "[control_762]": 764,
+ "[control_763]": 765,
+ "[control_764]": 766,
+ "[control_765]": 767,
+ "[control_766]": 768,
+ "[control_767]": 769,
+ "[control_768]": 770,
+ "<0x00>": 771,
+ "<0x01>": 772,
+ "<0x02>": 773,
+ "<0x03>": 774,
+ "<0x04>": 775,
+ "<0x05>": 776,
+ "<0x06>": 777,
+ "<0x07>": 778,
+ "<0x08>": 779,
+ "<0x09>": 780,
+ "<0x0A>": 781,
+ "<0x0B>": 782,
+ "<0x0C>": 783,
+ "<0x0D>": 784,
+ "<0x0E>": 785,
+ "<0x0F>": 786,
+ "<0x10>": 787,
+ "<0x11>": 788,
+ "<0x12>": 789,
+ "<0x13>": 790,
+ "<0x14>": 791,
+ "<0x15>": 792,
+ "<0x16>": 793,
+ "<0x17>": 794,
+ "<0x18>": 795,
+ "<0x19>": 796,
+ "<0x1A>": 797,
+ "<0x1B>": 798,
+ "<0x1C>": 799,
+ "<0x1D>": 800,
+ "<0x1E>": 801,
+ "<0x1F>": 802,
+ "<0x20>": 803,
+ "<0x21>": 804,
+ "<0x22>": 805,
+ "<0x23>": 806,
+ "<0x24>": 807,
+ "<0x25>": 808,
+ "<0x26>": 809,
+ "<0x27>": 810,
+ "<0x28>": 811,
+ "<0x29>": 812,
+ "<0x2A>": 813,
+ "<0x2B>": 814,
+ "<0x2C>": 815,
+ "<0x2D>": 816,
+ "<0x2E>": 817,
+ "<0x2F>": 818,
+ "<0x30>": 819,
+ "<0x31>": 820,
+ "<0x32>": 821,
+ "<0x33>": 822,
+ "<0x34>": 823,
+ "<0x35>": 824,
+ "<0x36>": 825,
+ "<0x37>": 826,
+ "<0x38>": 827,
+ "<0x39>": 828,
+ "<0x3A>": 829,
+ "<0x3B>": 830,
+ "<0x3C>": 831,
+ "<0x3D>": 832,
+ "<0x3E>": 833,
+ "<0x3F>": 834,
+ "<0x40>": 835,
+ "<0x41>": 836,
+ "<0x42>": 837,
+ "<0x43>": 838,
+ "<0x44>": 839,
+ "<0x45>": 840,
+ "<0x46>": 841,
+ "<0x47>": 842,
+ "<0x48>": 843,
+ "<0x49>": 844,
+ "<0x4A>": 845,
+ "<0x4B>": 846,
+ "<0x4C>": 847,
+ "<0x4D>": 848,
+ "<0x4E>": 849,
+ "<0x4F>": 850,
+ "<0x50>": 851,
+ "<0x51>": 852,
+ "<0x52>": 853,
+ "<0x53>": 854,
+ "<0x54>": 855,
+ "<0x55>": 856,
+ "<0x56>": 857,
+ "<0x57>": 858,
+ "<0x58>": 859,
+ "<0x59>": 860,
+ "<0x5A>": 861,
+ "<0x5B>": 862,
+ "<0x5C>": 863,
+ "<0x5D>": 864,
+ "<0x5E>": 865,
+ "<0x5F>": 866,
+ "<0x60>": 867,
+ "<0x61>": 868,
+ "<0x62>": 869,
+ "<0x63>": 870,
+ "<0x64>": 871,
+ "<0x65>": 872,
+ "<0x66>": 873,
+ "<0x67>": 874,
+ "<0x68>": 875,
+ "<0x69>": 876,
+ "<0x6A>": 877,
+ "<0x6B>": 878,
+ "<0x6C>": 879,
+ "<0x6D>": 880,
+ "<0x6E>": 881,
+ "<0x6F>": 882,
+ "<0x70>": 883,
+ "<0x71>": 884,
+ "<0x72>": 885,
+ "<0x73>": 886,
+ "<0x74>": 887,
+ "<0x75>": 888,
+ "<0x76>": 889,
+ "<0x77>": 890,
+ "<0x78>": 891,
+ "<0x79>": 892,
+ "<0x7A>": 893,
+ "<0x7B>": 894,
+ "<0x7C>": 895,
+ "<0x7D>": 896,
+ "<0x7E>": 897,
+ "<0x7F>": 898,
+ "<0x80>": 899,
+ "<0x81>": 900,
+ "<0x82>": 901,
+ "<0x83>": 902,
+ "<0x84>": 903,
+ "<0x85>": 904,
+ "<0x86>": 905,
+ "<0x87>": 906,
+ "<0x88>": 907,
+ "<0x89>": 908,
+ "<0x8A>": 909,
+ "<0x8B>": 910,
+ "<0x8C>": 911,
+ "<0x8D>": 912,
+ "<0x8E>": 913,
+ "<0x8F>": 914,
+ "<0x90>": 915,
+ "<0x91>": 916,
+ "<0x92>": 917,
+ "<0x93>": 918,
+ "<0x94>": 919,
+ "<0x95>": 920,
+ "<0x96>": 921,
+ "<0x97>": 922,
+ "<0x98>": 923,
+ "<0x99>": 924,
+ "<0x9A>": 925,
+ "<0x9B>": 926,
+ "<0x9C>": 927,
+ "<0x9D>": 928,
+ "<0x9E>": 929,
+ "<0x9F>": 930,
+ "<0xA0>": 931,
+ "<0xA1>": 932,
+ "<0xA2>": 933,
+ "<0xA3>": 934,
+ "<0xA4>": 935,
+ "<0xA5>": 936,
+ "<0xA6>": 937,
+ "<0xA7>": 938,
+ "<0xA8>": 939,
+ "<0xA9>": 940,
+ "<0xAA>": 941,
+ "<0xAB>": 942,
+ "<0xAC>": 943,
+ "<0xAD>": 944,
+ "<0xAE>": 945,
+ "<0xAF>": 946,
+ "<0xB0>": 947,
+ "<0xB1>": 948,
+ "<0xB2>": 949,
+ "<0xB3>": 950,
+ "<0xB4>": 951,
+ "<0xB5>": 952,
+ "<0xB6>": 953,
+ "<0xB7>": 954,
+ "<0xB8>": 955,
+ "<0xB9>": 956,
+ "<0xBA>": 957,
+ "<0xBB>": 958,
+ "<0xBC>": 959,
+ "<0xBD>": 960,
+ "<0xBE>": 961,
+ "<0xBF>": 962,
+ "<0xC0>": 963,
+ "<0xC1>": 964,
+ "<0xC2>": 965,
+ "<0xC3>": 966,
+ "<0xC4>": 967,
+ "<0xC5>": 968,
+ "<0xC6>": 969,
+ "<0xC7>": 970,
+ "<0xC8>": 971,
+ "<0xC9>": 972,
+ "<0xCA>": 973,
+ "<0xCB>": 974,
+ "<0xCC>": 975,
+ "<0xCD>": 976,
+ "<0xCE>": 977,
+ "<0xCF>": 978,
+ "<0xD0>": 979,
+ "<0xD1>": 980,
+ "<0xD2>": 981,
+ "<0xD3>": 982,
+ "<0xD4>": 983,
+ "<0xD5>": 984,
+ "<0xD6>": 985,
+ "<0xD7>": 986,
+ "<0xD8>": 987,
+ "<0xD9>": 988,
+ "<0xDA>": 989,
+ "<0xDB>": 990,
+ "<0xDC>": 991,
+ "<0xDD>": 992,
+ "<0xDE>": 993,
+ "<0xDF>": 994,
+ "<0xE0>": 995,
+ "<0xE1>": 996,
+ "<0xE2>": 997,
+ "<0xE3>": 998,
+ "<0xE4>": 999,
+ "<0xE5>": 1000,
+ "<0xE6>": 1001,
+ "<0xE7>": 1002,
+ "<0xE8>": 1003,
+ "<0xE9>": 1004,
+ "<0xEA>": 1005,
+ "<0xEB>": 1006,
+ "<0xEC>": 1007,
+ "<0xED>": 1008,
+ "<0xEE>": 1009,
+ "<0xEF>": 1010,
+ "<0xF0>": 1011,
+ "<0xF1>": 1012,
+ "<0xF2>": 1013,
+ "<0xF3>": 1014,
+ "<0xF4>": 1015,
+ "<0xF5>": 1016,
+ "<0xF6>": 1017,
+ "<0xF7>": 1018,
+ "<0xF8>": 1019,
+ "<0xF9>": 1020,
+ "<0xFA>": 1021,
+ "<0xFB>": 1022,
+ "<0xFC>": 1023,
+ "<0xFD>": 1024,
+ "<0xFE>": 1025,
+ "<0xFF>": 1026,
+ "▁▁": 1027,
+ "▁▁▁▁": 1028,
+ "▁t": 1029,
+ "in": 1030,
+ "er": 1031,
+ "▁a": 1032,
+ "he": 1033,
+ "on": 1034,
+ "re": 1035,
+ "▁s": 1036,
+ "en": 1037,
+ "at": 1038,
+ "or": 1039,
+ "▁the": 1040,
+ "▁▁▁▁▁▁▁▁": 1041,
+ "es": 1042,
+ "▁w": 1043,
+ "an": 1044,
+ "▁c": 1045,
+ "is": 1046,
+ "it": 1047,
+ "ou": 1048,
+ "▁d": 1049,
+ "al": 1050,
+ "ar": 1051,
+ "▁p": 1052,
+ "▁f": 1053,
+ "ed": 1054,
+ "▁b": 1055,
+ "ing": 1056,
+ "▁o": 1057,
+ "▁m": 1058,
+ "le": 1059,
+ "nd": 1060,
+ "as": 1061,
+ "ic": 1062,
+ "▁h": 1063,
+ "ion": 1064,
+ "▁in": 1065,
+ "▁to": 1066,
+ "et": 1067,
+ "om": 1068,
+ "el": 1069,
+ "▁of": 1070,
+ "st": 1071,
+ "▁and": 1072,
+ "▁l": 1073,
+ "▁th": 1074,
+ "▁n": 1075,
+ "ent": 1076,
+ "il": 1077,
+ "ct": 1078,
+ "ro": 1079,
+ "▁re": 1080,
+ "id": 1081,
+ "am": 1082,
+ "▁I": 1083,
+ "ad": 1084,
+ "▁e": 1085,
+ "▁S": 1086,
+ "▁g": 1087,
+ "▁T": 1088,
+ "im": 1089,
+ "ot": 1090,
+ "ac": 1091,
+ "ur": 1092,
+ "▁(": 1093,
+ "ig": 1094,
+ "▁=": 1095,
+ "ol": 1096,
+ "ut": 1097,
+ "▁A": 1098,
+ "se": 1099,
+ "▁u": 1100,
+ "ve": 1101,
+ "▁C": 1102,
+ "if": 1103,
+ "ow": 1104,
+ "▁y": 1105,
+ "ch": 1106,
+ "ay": 1107,
+ "▁de": 1108,
+ "▁st": 1109,
+ "▁|": 1110,
+ "ver": 1111,
+ ");": 1112,
+ "▁\"": 1113,
+ "ly": 1114,
+ "▁be": 1115,
+ "**": 1116,
+ "▁is": 1117,
+ "od": 1118,
+ "▁M": 1119,
+ "ation": 1120,
+ "ul": 1121,
+ "▁for": 1122,
+ "▁▁▁▁▁": 1123,
+ "▁on": 1124,
+ "ag": 1125,
+ "ce": 1126,
+ "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁": 1127,
+ "ter": 1128,
+ "ir": 1129,
+ "th": 1130,
+ "▁v": 1131,
+ "qu": 1132,
+ "▁B": 1133,
+ "em": 1134,
+ "▁P": 1135,
+ "▁you": 1136,
+ "▁that": 1137,
+ "un": 1138,
+ "▁{": 1139,
+ "ith": 1140,
+ "ri": 1141,
+ "est": 1142,
+ "ab": 1143,
+ "--": 1144,
+ "ap": 1145,
+ "▁it": 1146,
+ "▁con": 1147,
+ "ate": 1148,
+ "us": 1149,
+ "▁H": 1150,
+ "um": 1151,
+ "▁D": 1152,
+ "os": 1153,
+ "pe": 1154,
+ "▁-": 1155,
+ "▁wh": 1156,
+ "▁al": 1157,
+ "▁as": 1158,
+ "and": 1159,
+ "ist": 1160,
+ "▁L": 1161,
+ "▁W": 1162,
+ "▁with": 1163,
+ "▁an": 1164,
+ "ere": 1165,
+ "▁*": 1166,
+ "▁R": 1167,
+ "▁he": 1168,
+ "▁F": 1169,
+ "oc": 1170,
+ "▁was": 1171,
+ "ers": 1172,
+ "ke": 1173,
+ "out": 1174,
+ "ht": 1175,
+ "▁r": 1176,
+ "ess": 1177,
+ "op": 1178,
+ "res": 1179,
+ "ie": 1180,
+ "▁E": 1181,
+ "▁\\": 1182,
+ "▁The": 1183,
+ "end": 1184,
+ "ld": 1185,
+ "▁N": 1186,
+ "ort": 1187,
+ "▁G": 1188,
+ "//": 1189,
+ "▁#": 1190,
+ "our": 1191,
+ "te": 1192,
+ "ill": 1193,
+ "ain": 1194,
+ "▁se": 1195,
+ "▁▁▁▁▁▁": 1196,
+ "▁$": 1197,
+ "▁pro": 1198,
+ "ore": 1199,
+ "▁com": 1200,
+ "ame": 1201,
+ "tr": 1202,
+ "▁ne": 1203,
+ "rom": 1204,
+ "ub": 1205,
+ "▁at": 1206,
+ "▁ex": 1207,
+ "ant": 1208,
+ "ue": 1209,
+ "▁or": 1210,
+ "▁}": 1211,
+ "art": 1212,
+ "ction": 1213,
+ "▁k": 1214,
+ "pt": 1215,
+ "nt": 1216,
+ "iv": 1217,
+ "de": 1218,
+ "▁O": 1219,
+ "pl": 1220,
+ "urn": 1221,
+ "ight": 1222,
+ "all": 1223,
+ "▁this": 1224,
+ "ser": 1225,
+ "ave": 1226,
+ "▁not": 1227,
+ "▁are": 1228,
+ "▁j": 1229,
+ "▁le": 1230,
+ "iz": 1231,
+ "▁'": 1232,
+ "age": 1233,
+ "ment": 1234,
+ "▁tr": 1235,
+ "ack": 1236,
+ "ust": 1237,
+ "()": 1238,
+ "->": 1239,
+ "ity": 1240,
+ "ine": 1241,
+ "ould": 1242,
+ "▁J": 1243,
+ "og": 1244,
+ "▁from": 1245,
+ "▁we": 1246,
+ "ell": 1247,
+ "▁sh": 1248,
+ "▁en": 1249,
+ "ure": 1250,
+ "port": 1251,
+ "▁ch": 1252,
+ "ne": 1253,
+ "▁by": 1254,
+ "per": 1255,
+ "ard": 1256,
+ "ass": 1257,
+ "ge": 1258,
+ "ak": 1259,
+ "are": 1260,
+ "ok": 1261,
+ "av": 1262,
+ "ive": 1263,
+ "ff": 1264,
+ "ies": 1265,
+ "ath": 1266,
+ "turn": 1267,
+ "▁U": 1268,
+ "int": 1269,
+ "----": 1270,
+ "▁im": 1271,
+ "ost": 1272,
+ "ial": 1273,
+ "▁have": 1274,
+ "ind": 1275,
+ "ip": 1276,
+ "ans": 1277,
+ "xt": 1278,
+ "▁do": 1279,
+ "cl": 1280,
+ "▁if": 1281,
+ "con": 1282,
+ "ia": 1283,
+ "▁his": 1284,
+ "ult": 1285,
+ "rou": 1286,
+ "▁su": 1287,
+ "ra": 1288,
+ "▁un": 1289,
+ "able": 1290,
+ "▁<": 1291,
+ "▁K": 1292,
+ "ome": 1293,
+ "▁qu": 1294,
+ "get": 1295,
+ "▁me": 1296,
+ "ast": 1297,
+ "ect": 1298,
+ "▁##": 1299,
+ "to": 1300,
+ "▁cl": 1301,
+ "▁ab": 1302,
+ "ice": 1303,
+ "ire": 1304,
+ "ber": 1305,
+ "one": 1306,
+ "ich": 1307,
+ "hen": 1308,
+ "▁can": 1309,
+ "▁Th": 1310,
+ "▁la": 1311,
+ "▁all": 1312,
+ "ime": 1313,
+ "ile": 1314,
+ "ide": 1315,
+ "\",": 1316,
+ "▁pl": 1317,
+ "▁V": 1318,
+ "ru": 1319,
+ "orm": 1320,
+ "▁had": 1321,
+ "ud": 1322,
+ "ase": 1323,
+ "ord": 1324,
+ "),": 1325,
+ "▁▁▁▁▁▁▁▁▁▁▁▁": 1326,
+ "▁her": 1327,
+ "▁In": 1328,
+ "ace": 1329,
+ "▁but": 1330,
+ "ata": 1331,
+ "::": 1332,
+ "****": 1333,
+ "ong": 1334,
+ "▁&": 1335,
+ "..": 1336,
+ "▁▁▁▁▁▁▁▁▁▁▁▁▁": 1337,
+ "ite": 1338,
+ "ype": 1339,
+ "act": 1340,
+ "ode": 1341,
+ "▁your": 1342,
+ "▁out": 1343,
+ "▁go": 1344,
+ "lic": 1345,
+ "ally": 1346,
+ "▁so": 1347,
+ "ork": 1348,
+ "au": 1349,
+ "▁up": 1350,
+ "▁_": 1351,
+ "ll": 1352,
+ "==": 1353,
+ "▁my": 1354,
+ "pp": 1355,
+ "cc": 1356,
+ "▁//": 1357,
+ "▁they": 1358,
+ "gh": 1359,
+ "▁us": 1360,
+ "ib": 1361,
+ "ions": 1362,
+ "ach": 1363,
+ "ens": 1364,
+ "▁ar": 1365,
+ "ob": 1366,
+ "elf": 1367,
+ "ook": 1368,
+ "ated": 1369,
+ "ang": 1370,
+ "ign": 1371,
+ "▁return": 1372,
+ "▁res": 1373,
+ "ck": 1374,
+ "ous": 1375,
+ "ст": 1376,
+ ").": 1377,
+ "▁п": 1378,
+ ".\"": 1379,
+ "на": 1380,
+ "▁i": 1381,
+ "ail": 1382,
+ "ep": 1383,
+ "▁ad": 1384,
+ "ance": 1385,
+ "(\"": 1386,
+ "▁**": 1387,
+ "ther": 1388,
+ "ake": 1389,
+ "▁will": 1390,
+ "▁comp": 1391,
+ "▁one": 1392,
+ "▁get": 1393,
+ "ov": 1394,
+ "▁Y": 1395,
+ "ary": 1396,
+ "ock": 1397,
+ "▁she": 1398,
+ "che": 1399,
+ "ft": 1400,
+ "▁new": 1401,
+ "▁des": 1402,
+ "▁li": 1403,
+ "ence": 1404,
+ "▁sa": 1405,
+ "ress": 1406,
+ "▁el": 1407,
+ "▁und": 1408,
+ "eg": 1409,
+ "fer": 1410,
+ "ry": 1411,
+ "ear": 1412,
+ "ose": 1413,
+ "very": 1414,
+ "',": 1415,
+ "▁+": 1416,
+ "▁в": 1417,
+ "▁He": 1418,
+ "ublic": 1419,
+ "▁their": 1420,
+ "ize": 1421,
+ "▁were": 1422,
+ "ink": 1423,
+ "own": 1424,
+ "In": 1425,
+ "{\\": 1426,
+ "▁has": 1427,
+ "▁per": 1428,
+ "▁It": 1429,
+ "▁St": 1430,
+ "her": 1431,
+ "ject": 1432,
+ "ра": 1433,
+ "ild": 1434,
+ "so": 1435,
+ "▁sp": 1436,
+ "ни": 1437,
+ "du": 1438,
+ "row": 1439,
+ "alue": 1440,
+ "set": 1441,
+ "form": 1442,
+ "com": 1443,
+ "▁man": 1444,
+ "ont": 1445,
+ "ull": 1446,
+ "▁cont": 1447,
+ "▁more": 1448,
+ "ick": 1449,
+ "▁would": 1450,
+ "▁ev": 1451,
+ "▁about": 1452,
+ "ition": 1453,
+ "▁z": 1454,
+ "ound": 1455,
+ "ree": 1456,
+ "▁Ch": 1457,
+ "▁which": 1458,
+ "io": 1459,
+ "();": 1460,
+ "▁who": 1461,
+ "err": 1462,
+ "ory": 1463,
+ "ount": 1464,
+ "ations": 1465,
+ "▁с": 1466,
+ "ring": 1467,
+ "": 1468,
+ "▁fe": 1469,
+ "ко": 1470,
+ "но": 1471,
+ "▁dis": 1472,
+ "ma": 1473,
+ "▁them": 1474,
+ "▁any": 1475,
+ "▁no": 1476,
+ "--------": 1477,
+ "▁pre": 1478,
+ "▁te": 1479,
+ "▁ro": 1480,
+ "▁him": 1481,
+ "▁:": 1482,
+ "up": 1483,
+ "▁int": 1484,
+ "▁ag": 1485,
+ "St": 1486,
+ "ark": 1487,
+ "ex": 1488,
+ "ph": 1489,
+ "ient": 1490,
+ "ely": 1491,
+ "▁pr": 1492,
+ "ER": 1493,
+ "▁import": 1494,
+ "▁time": 1495,
+ "ро": 1496,
+ "pro": 1497,
+ "User": 1498,
+ "lo": 1499,
+ "▁/": 1500,
+ "▁[": 1501,
+ "ors": 1502,
+ "=\"": 1503,
+ "▁there": 1504,
+ "▁like": 1505,
+ "old": 1506,
+ "▁when": 1507,
+ "vers": 1508,
+ "▁some": 1509,
+ "ings": 1510,
+ "))": 1511,
+ "▁part": 1512,
+ "ical": 1513,
+ "▁fun": 1514,
+ "▁kn": 1515,
+ "ays": 1516,
+ "ier": 1517,
+ "▁been": 1518,
+ "ove": 1519,
+ "▁sc": 1520,
+ "ian": 1521,
+ "▁over": 1522,
+ "iel": 1523,
+ "▁▁▁▁▁▁▁▁▁▁": 1524,
+ "▁pe": 1525,
+ "rib": 1526,
+ "put": 1527,
+ "ec": 1528,
+ "eth": 1529,
+ "aram": 1530,
+ "app": 1531,
+ "▁–": 1532,
+ "▁stat": 1533,
+ "pon": 1534,
+ "▁what": 1535,
+ "ption": 1536,
+ "we": 1537,
+ "ade": 1538,
+ "▁work": 1539,
+ "text": 1540,
+ "▁said": 1541,
+ "▁###": 1542,
+ "IN": 1543,
+ "▁just": 1544,
+ "irst": 1545,
+ "▁into": 1546,
+ "▁const": 1547,
+ "ource": 1548,
+ "tt": 1549,
+ "ps": 1550,
+ "pr": 1551,
+ "erv": 1552,
+ "itt": 1553,
+ "ug": 1554,
+ "_{": 1555,
+ "ents": 1556,
+ "ish": 1557,
+ "ener": 1558,
+ "▁inter": 1559,
+ "ple": 1560,
+ "oll": 1561,
+ "mer": 1562,
+ "ater": 1563,
+ "ool": 1564,
+ "ef": 1565,
+ "▁public": 1566,
+ "▁other": 1567,
+ "ре": 1568,
+ "▁def": 1569,
+ "▁@": 1570,
+ "го": 1571,
+ "oint": 1572,
+ "▁off": 1573,
+ "oid": 1574,
+ "return": 1575,
+ "▁set": 1576,
+ "wo": 1577,
+ "fter": 1578,
+ "sh": 1579,
+ "********": 1580,
+ "▁our": 1581,
+ "riv": 1582,
+ "iss": 1583,
+ "▁We": 1584,
+ "ng": 1585,
+ "▁ob": 1586,
+ "ss": 1587,
+ "gr": 1588,
+ "▁than": 1589,
+ "pect": 1590,
+ "ied": 1591,
+ "sc": 1592,
+ "iew": 1593,
+ "der": 1594,
+ "yst": 1595,
+ "ev": 1596,
+ "▁could": 1597,
+ "ann": 1598,
+ "enc": 1599,
+ "ON": 1600,
+ "ix": 1601,
+ "anc": 1602,
+ "▁also": 1603,
+ "reat": 1604,
+ "▁am": 1605,
+ "▁bec": 1606,
+ "▁и": 1607,
+ "ual": 1608,
+ "pec": 1609,
+ "▁.": 1610,
+ "▁bl": 1611,
+ "lect": 1612,
+ "ople": 1613,
+ "ys": 1614,
+ "▁gr": 1615,
+ "ict": 1616,
+ "ik": 1617,
+ "tring": 1618,
+ "▁This": 1619,
+ "▁back": 1620,
+ "▁о": 1621,
+ "▁fin": 1622,
+ "atch": 1623,
+ "Con": 1624,
+ "('": 1625,
+ "erm": 1626,
+ "▁==": 1627,
+ "__": 1628,
+ "name": 1629,
+ ",\"": 1630,
+ "▁did": 1631,
+ "ise": 1632,
+ "▁only": 1633,
+ "ruct": 1634,
+ "les": 1635,
+ "▁then": 1636,
+ "ause": 1637,
+ "ва": 1638,
+ "▁its": 1639,
+ "rit": 1640,
+ "▁know": 1641,
+ "ield": 1642,
+ "▁class": 1643,
+ "▁>": 1644,
+ "▁em": 1645,
+ "▁$\\": 1646,
+ "▁year": 1647,
+ "wn": 1648,
+ "},": 1649,
+ "▁del": 1650,
+ "ale": 1651,
+ "ty": 1652,
+ "fig": 1653,
+ "sp": 1654,
+ "hed": 1655,
+ "round": 1656,
+ "ew": 1657,
+ "▁di": 1658,
+ "▁der": 1659,
+ "ри": 1660,
+ "red": 1661,
+ "this": 1662,
+ "let": 1663,
+ "RE": 1664,
+ "ax": 1665,
+ "fr": 1666,
+ "essage": 1667,
+ "ough": 1668,
+ "▁comm": 1669,
+ "fo": 1670,
+ "uch": 1671,
+ "oy": 1672,
+ "▁people": 1673,
+ "ystem": 1674,
+ "▁first": 1675,
+ "▁function": 1676,
+ "ange": 1677,
+ "▁how": 1678,
+ "▁et": 1679,
+ "ah": 1680,
+ "▁look": 1681,
+ "то": 1682,
+ "und": 1683,
+ "▁under": 1684,
+ "ка": 1685,
+ "▁!": 1686,
+ "ray": 1687,
+ "ST": 1688,
+ "ific": 1689,
+ "ли": 1690,
+ "read": 1691,
+ "▁bet": 1692,
+ "ious": 1693,
+ "arg": 1694,
+ "▁need": 1695,
+ "math": 1696,
+ "▁на": 1697,
+ "ert": 1698,
+ "▁op": 1699,
+ "▁acc": 1700,
+ "Pro": 1701,
+ "▁est": 1702,
+ "▁Un": 1703,
+ "▁ent": 1704,
+ "▁rec": 1705,
+ "▁use": 1706,
+ "ен": 1707,
+ "▁par": 1708,
+ "az": 1709,
+ "▁д": 1710,
+ "▁Wh": 1711,
+ "self": 1712,
+ "▁ke": 1713,
+ "та": 1714,
+ "▁want": 1715,
+ "▁end": 1716,
+ "▁don": 1717,
+ "ek": 1718,
+ "ren": 1719,
+ "Name": 1720,
+ "▁=>": 1721,
+ "▁app": 1722,
+ "▁que": 1723,
+ "igh": 1724,
+ "▁bu": 1725,
+ "equ": 1726,
+ "vel": 1727,
+ "▁act": 1728,
+ "cre": 1729,
+ "AT": 1730,
+ "▁var": 1731,
+ "cess": 1732,
+ "====": 1733,
+ "Ex": 1734,
+ "▁add": 1735,
+ "▁mod": 1736,
+ "ung": 1737,
+ "▁where": 1738,
+ "ning": 1739,
+ "▁fl": 1740,
+ "als": 1741,
+ "tern": 1742,
+ "}}": 1743,
+ "▁Al": 1744,
+ "▁pos": 1745,
+ "ank": 1746,
+ "▁ap": 1747,
+ "eng": 1748,
+ "▁“": 1749,
+ "ble": 1750,
+ "▁reg": 1751,
+ "^{": 1752,
+ "▁She": 1753,
+ "▁*/": 1754,
+ "ude": 1755,
+ "add": 1756,
+ "▁two": 1757,
+ "▁col": 1758,
+ "▁sm": 1759,
+ "air": 1760,
+ "▁may": 1761,
+ "fore": 1762,
+ "▁You": 1763,
+ "rough": 1764,
+ "▁che": 1765,
+ "▁att": 1766,
+ "oth": 1767,
+ "ла": 1768,
+ "▁co": 1769,
+ "ates": 1770,
+ "▁rem": 1771,
+ "ood": 1772,
+ "Type": 1773,
+ "led": 1774,
+ "ful": 1775,
+ "▁self": 1776,
+ "of": 1777,
+ "▁Ar": 1778,
+ "que": 1779,
+ "▁every": 1780,
+ "ref": 1781,
+ "The": 1782,
+ "▁And": 1783,
+ "▁rel": 1784,
+ "OR": 1785,
+ "Id": 1786,
+ "▁even": 1787,
+ "EN": 1788,
+ "▁hand": 1789,
+ "ait": 1790,
+ "▁should": 1791,
+ "▁after": 1792,
+ "▁dif": 1793,
+ "ght": 1794,
+ "ife": 1795,
+ "ator": 1796,
+ "ash": 1797,
+ "ribut": 1798,
+ "umber": 1799,
+ "▁see": 1800,
+ "ms": 1801,
+ "▁call": 1802,
+ "yn": 1803,
+ "dd": 1804,
+ "▁es": 1805,
+ "▁make": 1806,
+ "other": 1807,
+ "▁—": 1808,
+ "\");": 1809,
+ "str": 1810,
+ "▁long": 1811,
+ "lement": 1812,
+ "▁wor": 1813,
+ "its": 1814,
+ "▁If": 1815,
+ "alse": 1816,
+ "ль": 1817,
+ "ward": 1818,
+ "▁по": 1819,
+ "val": 1820,
+ "ons": 1821,
+ "▁Z": 1822,
+ "▁now": 1823,
+ "data": 1824,
+ "amp": 1825,
+ "ense": 1826,
+ "▁through": 1827,
+ "▁down": 1828,
+ "att": 1829,
+ "▁static": 1830,
+ "ics": 1831,
+ "##": 1832,
+ "pos": 1833,
+ "▁void": 1834,
+ "aw": 1835,
+ "oun": 1836,
+ "▁way": 1837,
+ "ible": 1838,
+ "vent": 1839,
+ "ower": 1840,
+ "▁think": 1841,
+ "ts": 1842,
+ "*/": 1843,
+ "▁again": 1844,
+ "ating": 1845,
+ "те": 1846,
+ "ner": 1847,
+ "▁most": 1848,
+ "line": 1849,
+ "ym": 1850,
+ "▁sub": 1851,
+ "erson": 1852,
+ "▁requ": 1853,
+ "AL": 1854,
+ "AR": 1855,
+ "abel": 1856,
+ "ond": 1857,
+ "));": 1858,
+ "▁Se": 1859,
+ "▁But": 1860,
+ "alk": 1861,
+ "▁An": 1862,
+ "new": 1863,
+ "▁because": 1864,
+ "ger": 1865,
+ "ular": 1866,
+ "roup": 1867,
+ "ta": 1868,
+ "...": 1869,
+ "▁cons": 1870,
+ "▁right": 1871,
+ "▁fr": 1872,
+ "be": 1873,
+ "ily": 1874,
+ "ки": 1875,
+ "▁ph": 1876,
+ "ead": 1877,
+ "?\"": 1878,
+ "▁gu": 1879,
+ "▁else": 1880,
+ "▁som": 1881,
+ "rent": 1882,
+ "co": 1883,
+ "ement": 1884,
+ "▁str": 1885,
+ "ault": 1886,
+ "▁з": 1887,
+ "ло": 1888,
+ "sert": 1889,
+ "var": 1890,
+ "type": 1891,
+ "▁Com": 1892,
+ "ле": 1893,
+ "ins": 1894,
+ "me": 1895,
+ "way": 1896,
+ "ident": 1897,
+ "▁prov": 1898,
+ "▁м": 1899,
+ "▁true": 1900,
+ "▁Pro": 1901,
+ "fl": 1902,
+ "▁sl": 1903,
+ "▁As": 1904,
+ "}\\": 1905,
+ "ID": 1906,
+ "ues": 1907,
+ "▁inst": 1908,
+ "▁name": 1909,
+ "ox": 1910,
+ "▁)": 1911,
+ "li": 1912,
+ "ames": 1913,
+ "Res": 1914,
+ "▁sur": 1915,
+ "param": 1916,
+ "▁start": 1917,
+ "aj": 1918,
+ "SE": 1919,
+ "ask": 1920,
+ "IT": 1921,
+ "String": 1922,
+ "▁ass": 1923,
+ "▁play": 1924,
+ "ting": 1925,
+ "ton": 1926,
+ "▁before": 1927,
+ "▁pol": 1928,
+ "arch": 1929,
+ "▁well": 1930,
+ "Com": 1931,
+ "any": 1932,
+ "olog": 1933,
+ "▁err": 1934,
+ "▁these": 1935,
+ "ars": 1936,
+ "eb": 1937,
+ "▁br": 1938,
+ "▁incl": 1939,
+ "▁hel": 1940,
+ "ern": 1941,
+ "ody": 1942,
+ "во": 1943,
+ "▁ind": 1944,
+ "----------------": 1945,
+ "▁data": 1946,
+ "▁good": 1947,
+ "LE": 1948,
+ "],": 1949,
+ "▁av": 1950,
+ "▁ac": 1951,
+ "ider": 1952,
+ "не": 1953,
+ "▁Q": 1954,
+ "▁min": 1955,
+ "▁much": 1956,
+ "ci": 1957,
+ "els": 1958,
+ "▁cur": 1959,
+ "▁value": 1960,
+ "ery": 1961,
+ "uf": 1962,
+ "▁loc": 1963,
+ "reak": 1964,
+ "ative": 1965,
+ "imes": 1966,
+ "Cl": 1967,
+ "▁,": 1968,
+ "▁ser": 1969,
+ "▁die": 1970,
+ "▁trans": 1971,
+ "▁result": 1972,
+ "ext": 1973,
+ "▁aut": 1974,
+ "land": 1975,
+ "▁&&": 1976,
+ "Ch": 1977,
+ "ten": 1978,
+ "}$": 1979,
+ "▁type": 1980,
+ "cond": 1981,
+ "ices": 1982,
+ "▁very": 1983,
+ "▁own": 1984,
+ "▁fil": 1985,
+ "ities": 1986,
+ "▁produ": 1987,
+ "▁read": 1988,
+ "▁form": 1989,
+ "▁case": 1990,
+ "ather": 1991,
+ "ти": 1992,
+ "да": 1993,
+ "ер": 1994,
+ "Th": 1995,
+ "aut": 1996,
+ "▁spec": 1997,
+ "ij": 1998,
+ "bl": 1999,
+ "ility": 2000,
+ "▁é": 2001,
+ "▁er": 2002,
+ "▁does": 2003,
+ "▁here": 2004,
+ "the": 2005,
+ "ures": 2006,
+ "▁%": 2007,
+ "min": 2008,
+ "▁null": 2009,
+ "rap": 2010,
+ "\")": 2011,
+ "rr": 2012,
+ "List": 2013,
+ "right": 2014,
+ "▁User": 2015,
+ "UL": 2016,
+ "ational": 2017,
+ "▁being": 2018,
+ "AN": 2019,
+ "sk": 2020,
+ "▁car": 2021,
+ "ole": 2022,
+ "▁dist": 2023,
+ "plic": 2024,
+ "ollow": 2025,
+ "▁pres": 2026,
+ "▁such": 2027,
+ "ream": 2028,
+ "ince": 2029,
+ "gan": 2030,
+ "▁For": 2031,
+ "\":": 2032,
+ "son": 2033,
+ "rivate": 2034,
+ "▁years": 2035,
+ "▁serv": 2036,
+ "▁made": 2037,
+ "def": 2038,
+ ";\r": 2039,
+ "▁gl": 2040,
+ "▁bel": 2041,
+ "▁list": 2042,
+ "▁cor": 2043,
+ "▁det": 2044,
+ "ception": 2045,
+ "egin": 2046,
+ "▁б": 2047,
+ "▁char": 2048,
+ "trans": 2049,
+ "▁fam": 2050,
+ "▁!=": 2051,
+ "ouse": 2052,
+ "▁dec": 2053,
+ "ica": 2054,
+ "▁many": 2055,
+ "aking": 2056,
+ "▁à": 2057,
+ "▁sim": 2058,
+ "ages": 2059,
+ "uff": 2060,
+ "ased": 2061,
+ "man": 2062,
+ "▁Sh": 2063,
+ "iet": 2064,
+ "irect": 2065,
+ "▁Re": 2066,
+ "▁differ": 2067,
+ "▁find": 2068,
+ "ethod": 2069,
+ "▁\r": 2070,
+ "ines": 2071,
+ "▁inv": 2072,
+ "▁point": 2073,
+ "▁They": 2074,
+ "▁used": 2075,
+ "ctions": 2076,
+ "▁still": 2077,
+ "ió": 2078,
+ "ined": 2079,
+ "▁while": 2080,
+ "It": 2081,
+ "ember": 2082,
+ "▁say": 2083,
+ "▁help": 2084,
+ "▁cre": 2085,
+ "▁x": 2086,
+ "▁Tr": 2087,
+ "ument": 2088,
+ "▁sk": 2089,
+ "ought": 2090,
+ "ually": 2091,
+ "message": 2092,
+ "▁Con": 2093,
+ "▁mon": 2094,
+ "ared": 2095,
+ "work": 2096,
+ "):": 2097,
+ "ister": 2098,
+ "arn": 2099,
+ "ized": 2100,
+ "Data": 2101,
+ "orn": 2102,
+ "▁head": 2103,
+ "DE": 2104,
+ "▁Le": 2105,
+ "▁person": 2106,
+ "ments": 2107,
+ "ength": 2108,
+ "▁false": 2109,
+ "▁med": 2110,
+ "▁De": 2111,
+ "ache": 2112,
+ "ited": 2113,
+ "▁let": 2114,
+ "▁show": 2115,
+ "▁same": 2116,
+ "uss": 2117,
+ "▁gener": 2118,
+ "▁у": 2119,
+ "cur": 2120,
+ "▁real": 2121,
+ "ced": 2122,
+ "\">": 2123,
+ "struct": 2124,
+ "begin": 2125,
+ "cept": 2126,
+ "▁bo": 2127,
+ "ired": 2128,
+ "▁Fr": 2129,
+ "▁stud": 2130,
+ "dev": 2131,
+ "Ar": 2132,
+ "(\\": 2133,
+ "▁Cl": 2134,
+ "ween": 2135,
+ "▁too": 2136,
+ "▁test": 2137,
+ "▁day": 2138,
+ "oh": 2139,
+ "▁follow": 2140,
+ "ature": 2141,
+ "ze": 2142,
+ "ien": 2143,
+ "reg": 2144,
+ "ces": 2145,
+ "uring": 2146,
+ "amb": 2147,
+ "ina": 2148,
+ "cri": 2149,
+ "▁ed": 2150,
+ "SS": 2151,
+ "uck": 2152,
+ "▁/*": 2153,
+ "CT": 2154,
+ "▁There": 2155,
+ "▁take": 2156,
+ "par": 2157,
+ "ule": 2158,
+ "cal": 2159,
+ "for": 2160,
+ "****************": 2161,
+ "source": 2162,
+ "▁those": 2163,
+ "col": 2164,
+ "▁eff": 2165,
+ "mod": 2166,
+ "cont": 2167,
+ "}{": 2168,
+ "▁around": 2169,
+ "press": 2170,
+ "by": 2171,
+ "▁going": 2172,
+ "ponse": 2173,
+ "▁С": 2174,
+ "▁line": 2175,
+ "date": 2176,
+ "code": 2177,
+ "['": 2178,
+ "▁life": 2179,
+ "ason": 2180,
+ "▁using": 2181,
+ "▁val": 2182,
+ "▁du": 2183,
+ "yp": 2184,
+ "▁▁▁▁▁▁▁▁▁▁▁▁▁▁": 2185,
+ "▁On": 2186,
+ "▁found": 2187,
+ "olut": 2188,
+ "']": 2189,
+ "arent": 2190,
+ "▁string": 2191,
+ "▁met": 2192,
+ "▁wr": 2193,
+ "ush": 2194,
+ "string": 2195,
+ "size": 2196,
+ "▁ver": 2197,
+ "▁each": 2198,
+ "value": 2199,
+ "▁last": 2200,
+ "▁got": 2201,
+ "ven": 2202,
+ "back": 2203,
+ "Set": 2204,
+ "ey": 2205,
+ "rol": 2206,
+ "▁cr": 2207,
+ "thing": 2208,
+ "ret": 2209,
+ "és": 2210,
+ "ism": 2211,
+ "▁between": 2212,
+ "Ob": 2213,
+ "ething": 2214,
+ "mp": 2215,
+ "▁lo": 2216,
+ "ats": 2217,
+ "▁New": 2218,
+ "ви": 2219,
+ "ado": 2220,
+ "dex": 2221,
+ "ди": 2222,
+ "▁pass": 2223,
+ "wh": 2224,
+ "▁den": 2225,
+ "Get": 2226,
+ "apt": 2227,
+ "▁ask": 2228,
+ "▁sup": 2229,
+ "Value": 2230,
+ "ны": 2231,
+ "▁try": 2232,
+ "lation": 2233,
+ "day": 2234,
+ "ness": 2235,
+ "ets": 2236,
+ "▁exper": 2237,
+ "Tr": 2238,
+ "▁Mar": 2239,
+ "serv": 2240,
+ "br": 2241,
+ "▁number": 2242,
+ "inal": 2243,
+ "cent": 2244,
+ "/*": 2245,
+ "not": 2246,
+ "ional": 2247,
+ "▁final": 2248,
+ "')": 2249,
+ "▁run": 2250,
+ "over": 2251,
+ "▁never": 2252,
+ "uc": 2253,
+ "▁high": 2254,
+ "yle": 2255,
+ "▁ins": 2256,
+ "▁best": 2257,
+ "ittle": 2258,
+ "ric": 2259,
+ "▁sign": 2260,
+ "▁dem": 2261,
+ "iness": 2262,
+ "gy": 2263,
+ "▁war": 2264,
+ "ished": 2265,
+ "▁giv": 2266,
+ "key": 2267,
+ "▁X": 2268,
+ "($": 2269,
+ "▁child": 2270,
+ "less": 2271,
+ "ways": 2272,
+ "incl": 2273,
+ "rop": 2274,
+ "raw": 2275,
+ "://": 2276,
+ "▁«": 2277,
+ "no": 2278,
+ "indow": 2279,
+ "fe": 2280,
+ "riend": 2281,
+ "▁les": 2282,
+ "▁los": 2283,
+ "file": 2284,
+ "formation": 2285,
+ "ccess": 2286,
+ "▁В": 2287,
+ "na": 2288,
+ "▁il": 2289,
+ "ision": 2290,
+ "ler": 2291,
+ "▁art": 2292,
+ "Cont": 2293,
+ "▁world": 2294,
+ "▁turn": 2295,
+ "▁really": 2296,
+ "▁Ex": 2297,
+ "ма": 2298,
+ "▁П": 2299,
+ "ters": 2300,
+ "arget": 2301,
+ "Err": 2302,
+ "▁happ": 2303,
+ "time": 2304,
+ "▁So": 2305,
+ "div": 2306,
+ "▁didn": 2307,
+ "ada": 2308,
+ "oot": 2309,
+ "})": 2310,
+ "▁sch": 2311,
+ "▁cle": 2312,
+ "▁something": 2313,
+ "().": 2314,
+ "▁cour": 2315,
+ "ever": 2316,
+ "ants": 2317,
+ "▁?": 2318,
+ "To": 2319,
+ "▁`": 2320,
+ "try": 2321,
+ "ux": 2322,
+ "ais": 2323,
+ "ross": 2324,
+ "hip": 2325,
+ "▁rep": 2326,
+ "label": 2327,
+ "▁both": 2328,
+ "*,": 2329,
+ "ott": 2330,
+ "ми": 2331,
+ "ane": 2332,
+ "▁open": 2333,
+ "ww": 2334,
+ "▁come": 2335,
+ "▁ext": 2336,
+ "rem": 2337,
+ "_{\\": 2338,
+ "▁old": 2339,
+ "ched": 2340,
+ "._": 2341,
+ "ME": 2342,
+ "ify": 2343,
+ "gg": 2344,
+ "Col": 2345,
+ "view": 2346,
+ "▁bus": 2347,
+ "▁must": 2348,
+ "▁different": 2349,
+ "log": 2350,
+ "ists": 2351,
+ "roll": 2352,
+ "ai": 2353,
+ "▁за": 2354,
+ "▁system": 2355,
+ "ivers": 2356,
+ "atus": 2357,
+ "ote": 2358,
+ "med": 2359,
+ "].": 2360,
+ "akes": 2361,
+ "RO": 2362,
+ "▁cent": 2363,
+ "gram": 2364,
+ "▁private": 2365,
+ "▁great": 2366,
+ "\";": 2367,
+ "opy": 2368,
+ "▁feel": 2369,
+ "▁How": 2370,
+ "////": 2371,
+ "IC": 2372,
+ "▁dr": 2373,
+ "ains": 2374,
+ "lock": 2375,
+ "En": 2376,
+ "▁Sch": 2377,
+ "▁mat": 2378,
+ "▁home": 2379,
+ "perty": 2380,
+ "test": 2381,
+ "loc": 2382,
+ "▁wom": 2383,
+ "sw": 2384,
+ "arly": 2385,
+ "▁En": 2386,
+ "▁ко": 2387,
+ "den": 2388,
+ "ста": 2389,
+ "▁а": 2390,
+ "eter": 2391,
+ "▁includ": 2392,
+ "ULL": 2393,
+ "▁mem": 2394,
+ "▁po": 2395,
+ "▁little": 2396,
+ "▁arg": 2397,
+ "▁},": 2398,
+ "include": 2399,
+ "eta": 2400,
+ "▁place": 2401,
+ "idth": 2402,
+ "ustom": 2403,
+ "▁||": 2404,
+ "▁tem": 2405,
+ "ried": 2406,
+ "▁fact": 2407,
+ "ience": 2408,
+ "▁Pl": 2409,
+ "opt": 2410,
+ "ele": 2411,
+ "go": 2412,
+ "AC": 2413,
+ "inter": 2414,
+ "========": 2415,
+ "(),": 2416,
+ "ots": 2417,
+ "ral": 2418,
+ "ique": 2419,
+ "aving": 2420,
+ "ml": 2421,
+ "▁thought": 2422,
+ "frac": 2423,
+ "▁care": 2424,
+ "());": 2425,
+ "▁put": 2426,
+ "▁might": 2427,
+ "▁Amer": 2428,
+ "▁(!": 2429,
+ "ample": 2430,
+ "alth": 2431,
+ "▁few": 2432,
+ "▁state": 2433,
+ "sub": 2434,
+ "▁Or": 2435,
+ "];": 2436,
+ "▁size": 2437,
+ "▁Sp": 2438,
+ "▁without": 2439,
+ "▁poss": 2440,
+ "eq": 2441,
+ "play": 2442,
+ "▁expect": 2443,
+ "▁second": 2444,
+ "▁String": 2445,
+ "uild": 2446,
+ "▁next": 2447,
+ "++": 2448,
+ "requ": 2449,
+ "▁All": 2450,
+ "▁men": 2451,
+ "▁When": 2452,
+ "iter": 2453,
+ "ament": 2454,
+ "net": 2455,
+ "▁К": 2456,
+ "ron": 2457,
+ "aint": 2458,
+ "▁Is": 2459,
+ "ве": 2460,
+ "pend": 2461,
+ "translation": 2462,
+ "▁го": 2463,
+ "че": 2464,
+ "▁van": 2465,
+ "▁another": 2466,
+ "▁ret": 2467,
+ "▁La": 2468,
+ "Mod": 2469,
+ "ION": 2470,
+ "list": 2471,
+ "▁post": 2472,
+ "da": 2473,
+ "ware": 2474,
+ "▁word": 2475,
+ "Error": 2476,
+ "▁seem": 2477,
+ "▁contin": 2478,
+ "atic": 2479,
+ "▁three": 2480,
+ "Object": 2481,
+ "▁partic": 2482,
+ "$.": 2483,
+ "▁mark": 2484,
+ "▁vis": 2485,
+ "rc": 2486,
+ "▁sw": 2487,
+ "ptions": 2488,
+ "▁break": 2489,
+ "▁things": 2490,
+ "ute": 2491,
+ "ui": 2492,
+ "▁That": 2493,
+ "urs": 2494,
+ "gl": 2495,
+ "ру": 2496,
+ "▁file": 2497,
+ "use": 2498,
+ "igned": 2499,
+ "part": 2500,
+ "Un": 2501,
+ "▁equ": 2502,
+ "(&": 2503,
+ "▁lead": 2504,
+ "rm": 2505,
+ "ained": 2506,
+ "▁Be": 2507,
+ "path": 2508,
+ "▁small": 2509,
+ "ager": 2510,
+ "▁always": 2511,
+ "▁El": 2512,
+ "▁order": 2513,
+ "▁ey": 2514,
+ "▁won": 2515,
+ "ape": 2516,
+ "▁left": 2517,
+ "ava": 2518,
+ "item": 2519,
+ "hor": 2520,
+ "▁away": 2521,
+ "bb": 2522,
+ "fun": 2523,
+ "▁Ind": 2524,
+ "mb": 2525,
+ "▁struct": 2526,
+ "▁process": 2527,
+ "▁support": 2528,
+ ");\r": 2529,
+ "ión": 2530,
+ "LO": 2531,
+ "▁oper": 2532,
+ "UT": 2533,
+ "▁·": 2534,
+ "PE": 2535,
+ "load": 2536,
+ "off": 2537,
+ "▁No": 2538,
+ "ives": 2539,
+ "ican": 2540,
+ "▁ve": 2541,
+ "action": 2542,
+ "';": 2543,
+ "▁vo": 2544,
+ "$,": 2545,
+ "▁Gr": 2546,
+ "pre": 2547,
+ "ny": 2548,
+ "aining": 2549,
+ "ior": 2550,
+ "init": 2551,
+ "lection": 2552,
+ "arm": 2553,
+ "umn": 2554,
+ "ags": 2555,
+ "ци": 2556,
+ "ско": 2557,
+ "version": 2558,
+ "▁To": 2559,
+ "▁ref": 2560,
+ "stand": 2561,
+ "▁At": 2562,
+ "ift": 2563,
+ "▁ein": 2564,
+ "face": 2565,
+ "bo": 2566,
+ "ified": 2567,
+ "ved": 2568,
+ "sum": 2569,
+ "une": 2570,
+ "ital": 2571,
+ "ump": 2572,
+ "comm": 2573,
+ "▁mov": 2574,
+ "elt": 2575,
+ "▁von": 2576,
+ "velop": 2577,
+ "ctor": 2578,
+ "head": 2579,
+ "cle": 2580,
+ "▁build": 2581,
+ "inc": 2582,
+ ".'": 2583,
+ "bs": 2584,
+ "info": 2585,
+ "chn": 2586,
+ "▁week": 2587,
+ "▁book": 2588,
+ "HE": 2589,
+ "bar": 2590,
+ "icense": 2591,
+ "▁What": 2592,
+ "▁quest": 2593,
+ "urch": 2594,
+ "ato": 2595,
+ "left": 2596,
+ "▁mar": 2597,
+ "▁top": 2598,
+ "FF": 2599,
+ "▁friend": 2600,
+ "▁beh": 2601,
+ "▁field": 2602,
+ "▁against": 2603,
+ "ract": 2604,
+ "ization": 2605,
+ "user": 2606,
+ "chen": 2607,
+ "▁keep": 2608,
+ "AD": 2609,
+ "itor": 2610,
+ "▁non": 2611,
+ "ird": 2612,
+ "ope": 2613,
+ "▁rest": 2614,
+ "▁dev": 2615,
+ "▁__": 2616,
+ "▁una": 2617,
+ "▁term": 2618,
+ "IS": 2619,
+ "▁pop": 2620,
+ "rist": 2621,
+ "▁since": 2622,
+ "ves": 2623,
+ "▁hard": 2624,
+ "pi": 2625,
+ "util": 2626,
+ "▁soc": 2627,
+ "ene": 2628,
+ "Exception": 2629,
+ "▁local": 2630,
+ "▁direct": 2631,
+ "▁sure": 2632,
+ "▁bro": 2633,
+ "▁da": 2634,
+ "▁": 2635,
+ "▁current": 2636,
+ "':": 2637,
+ "Wh": 2638,
+ "▁information": 2639,
+ "▁ide": 2640,
+ "▁better": 2641,
+ "Text": 2642,
+ "raph": 2643,
+ "▁stand": 2644,
+ "▁check": 2645,
+ "▁к": 2646,
+ "▁na": 2647,
+ "((": 2648,
+ "outh": 2649,
+ "aps": 2650,
+ "▁unt": 2651,
+ "bf": 2652,
+ "▁conf": 2653,
+ "▁spe": 2654,
+ "itle": 2655,
+ "▁Col": 2656,
+ "class": 2657,
+ "ural": 2658,
+ "bers": 2659,
+ "MA": 2660,
+ "ession": 2661,
+ "▁М": 2662,
+ "Info": 2663,
+ "▁Br": 2664,
+ "▁eas": 2665,
+ "ervice": 2666,
+ "aus": 2667,
+ "ari": 2668,
+ "по": 2669,
+ "▁coun": 2670,
+ "де": 2671,
+ "())": 2672,
+ "ling": 2673,
+ "ED": 2674,
+ "ably": 2675,
+ "▁pat": 2676,
+ "org": 2677,
+ "▁id": 2678,
+ "▁г": 2679,
+ "▁tell": 2680,
+ "lex": 2681,
+ "▁allow": 2682,
+ "reen": 2683,
+ "my": 2684,
+ "▁consider": 2685,
+ "▁team": 2686,
+ "lease": 2687,
+ "htt": 2688,
+ "▁Pr": 2689,
+ "/**": 2690,
+ "▁sing": 2691,
+ "Requ": 2692,
+ "Re": 2693,
+ "ides": 2694,
+ "ches": 2695,
+ "▁object": 2696,
+ "ially": 2697,
+ "By": 2698,
+ "ся": 2699,
+ "ided": 2700,
+ "▁free": 2701,
+ "▁proble": 2702,
+ "cite": 2703,
+ "▁);": 2704,
+ "ission": 2705,
+ "▁during": 2706,
+ "▁--": 2707,
+ "ither": 2708,
+ "ля": 2709,
+ "▁leg": 2710,
+ "▁sit": 2711,
+ "ically": 2712,
+ "▁key": 2713,
+ "leg": 2714,
+ "tra": 2715,
+ "▁mom": 2716,
+ "▁expl": 2717,
+ "▁develop": 2718,
+ "▁event": 2719,
+ "▁NULL": 2720,
+ "ohn": 2721,
+ "▁///": 2722,
+ "▁business": 2723,
+ "ча": 2724,
+ "▁prof": 2725,
+ "error": 2726,
+ "▁por": 2727,
+ "▁commun": 2728,
+ "Ind": 2729,
+ "ium": 2730,
+ "Test": 2731,
+ "▁Ad": 2732,
+ "ouble": 2733,
+ "▁son": 2734,
+ "rite": 2735,
+ "ready": 2736,
+ "▁{\r": 2737,
+ "▁thing": 2738,
+ "ня": 2739,
+ "▁Ph": 2740,
+ "ped": 2741,
+ "сь": 2742,
+ "ived": 2743,
+ "You": 2744,
+ "arl": 2745,
+ "const": 2746,
+ "../": 2747,
+ "Se": 2748,
+ "Sh": 2749,
+ "▁power": 2750,
+ "ribute": 2751,
+ "▁My": 2752,
+ "▁talk": 2753,
+ "itch": 2754,
+ "▁called": 2755,
+ "▁came": 2756,
+ "▁belie": 2757,
+ "UR": 2758,
+ "Add": 2759,
+ "▁Res": 2760,
+ "aster": 2761,
+ "ella": 2762,
+ "obal": 2763,
+ "▁until": 2764,
+ "▁hum": 2765,
+ "CO": 2766,
+ "ately": 2767,
+ "####": 2768,
+ "public": 2769,
+ "[]": 2770,
+ "▁room": 2771,
+ "len": 2772,
+ "▁family": 2773,
+ "por": 2774,
+ "▁program": 2775,
+ "▁hist": 2776,
+ "▁mus": 2777,
+ "arge": 2778,
+ "oney": 2779,
+ "Im": 2780,
+ "else": 2781,
+ "ails": 2782,
+ "af": 2783,
+ "▁love": 2784,
+ "är": 2785,
+ "ases": 2786,
+ "pha": 2787,
+ "ours": 2788,
+ "dis": 2789,
+ "map": 2790,
+ "iver": 2791,
+ "ör": 2792,
+ "▁Bl": 2793,
+ "ateg": 2794,
+ "state": 2795,
+ "State": 2796,
+ "ertain": 2797,
+ "▁effect": 2798,
+ "print": 2799,
+ "▁big": 2800,
+ "index": 2801,
+ "▁pub": 2802,
+ "vert": 2803,
+ "ero": 2804,
+ "md": 2805,
+ "▁method": 2806,
+ "▁game": 2807,
+ "ries": 2808,
+ "lete": 2809,
+ "Item": 2810,
+ "ING": 2811,
+ "resent": 2812,
+ "ality": 2813,
+ "pty": 2814,
+ "ley": 2815,
+ "ocument": 2816,
+ "▁beg": 2817,
+ "TR": 2818,
+ "}.": 2819,
+ "▁school": 2820,
+ "hes": 2821,
+ "до": 2822,
+ "▁lot": 2823,
+ "▁took": 2824,
+ "▁adv": 2825,
+ "▁cap": 2826,
+ "MP": 2827,
+ "unk": 2828,
+ "▁light": 2829,
+ "▁later": 2830,
+ ".,": 2831,
+ "Key": 2832,
+ "itions": 2833,
+ "▁enough": 2834,
+ "▁/**": 2835,
+ "▁went": 2836,
+ "ão": 2837,
+ "▁though": 2838,
+ "▁group": 2839,
+ "▁mean": 2840,
+ "ски": 2841,
+ "AP": 2842,
+ "▁num": 2843,
+ "▁cond": 2844,
+ "ні": 2845,
+ "▁given": 2846,
+ "▁why": 2847,
+ "▁rece": 2848,
+ "▁side": 2849,
+ "▁far": 2850,
+ "Context": 2851,
+ "ме": 2852,
+ "▁log": 2853,
+ "View": 2854,
+ "▁<<": 2855,
+ "fil": 2856,
+ "aces": 2857,
+ "ency": 2858,
+ "oad": 2859,
+ "ered": 2860,
+ "▁product": 2861,
+ "ET": 2862,
+ "▁param": 2863,
+ "▁prote": 2864,
+ "tes": 2865,
+ "Time": 2866,
+ "je": 2867,
+ "olution": 2868,
+ "▁ра": 2869,
+ "▁month": 2870,
+ "ference": 2871,
+ "▁appe": 2872,
+ "▁face": 2873,
+ "ened": 2874,
+ "tract": 2875,
+ "▁less": 2876,
+ "AS": 2877,
+ "ée": 2878,
+ "▁give": 2879,
+ "▁kind": 2880,
+ "▁count": 2881,
+ "count": 2882,
+ "▁stop": 2883,
+ "▁gover": 2884,
+ "ka": 2885,
+ "▁error": 2886,
+ "ences": 2887,
+ "▁mil": 2888,
+ "alf": 2889,
+ "ync": 2890,
+ "vious": 2891,
+ "ho": 2892,
+ "▁night": 2893,
+ "era": 2894,
+ "▁про": 2895,
+ "▁sol": 2896,
+ "men": 2897,
+ "▁water": 2898,
+ "ering": 2899,
+ "▁lim": 2900,
+ "Param": 2901,
+ "▁house": 2902,
+ "▁System": 2903,
+ "▁pay": 2904,
+ "▁:=": 2905,
+ "uro": 2906,
+ "oci": 2907,
+ "zy": 2908,
+ "▁already": 2909,
+ ",\\": 2910,
+ "length": 2911,
+ "▁si": 2912,
+ "▁interest": 2913,
+ "aff": 2914,
+ "cted": 2915,
+ "ention": 2916,
+ "▁до": 2917,
+ "ume": 2918,
+ "▁appro": 2919,
+ "bre": 2920,
+ "IG": 2921,
+ "▁throw": 2922,
+ "mathcal": 2923,
+ "irl": 2924,
+ "▁prom": 2925,
+ "oss": 2926,
+ "▁request": 2927,
+ "equation": 2928,
+ "ology": 2929,
+ "mit": 2930,
+ "▁pack": 2931,
+ "ino": 2932,
+ "array": 2933,
+ "za": 2934,
+ "til": 2935,
+ "UN": 2936,
+ "▁present": 2937,
+ "▁organ": 2938,
+ "File": 2939,
+ "▁orig": 2940,
+ "▁full": 2941,
+ "istr": 2942,
+ "▁flo": 2943,
+ "hr": 2944,
+ "▁assert": 2945,
+ "ards": 2946,
+ "url": 2947,
+ "enn": 2948,
+ "sl": 2949,
+ "▁А": 2950,
+ "▁cho": 2951,
+ "▁level": 2952,
+ "OT": 2953,
+ "word": 2954,
+ "▁body": 2955,
+ "▁user": 2956,
+ "ía": 2957,
+ "Qu": 2958,
+ "▁main": 2959,
+ "AB": 2960,
+ "ploy": 2961,
+ "Event": 2962,
+ "▁super": 2963,
+ "oken": 2964,
+ "▁Н": 2965,
+ "As": 2966,
+ "thers": 2967,
+ "мо": 2968,
+ "ку": 2969,
+ "▁days": 2970,
+ "▁done": 2971,
+ "▁view": 2972,
+ "side": 2973,
+ "си": 2974,
+ "');": 2975,
+ "▁vol": 2976,
+ "▁tot": 2977,
+ "case": 2978,
+ "▁aff": 2979,
+ "Request": 2980,
+ "▁Man": 2981,
+ "\\\\": 2982,
+ "▁John": 2983,
+ "▁Б": 2984,
+ "orth": 2985,
+ "▁je": 2986,
+ "▁une": 2987,
+ "la": 2988,
+ "[\"": 2989,
+ "field": 2990,
+ "▁US": 2991,
+ "ico": 2992,
+ "▁perform": 2993,
+ "ailable": 2994,
+ "Config": 2995,
+ "Or": 2996,
+ "▁model": 2997,
+ "ales": 2998,
+ "▁create": 2999,
+ "▁ann": 3000,
+ "ances": 3001,
+ "IL": 3002,
+ "ination": 3003,
+ "▁Im": 3004,
+ "ante": 3005,
+ "ana": 3006,
+ "ан": 3007,
+ "▁told": 3008,
+ "config": 3009,
+ "\"]": 3010,
+ "met": 3011,
+ "lt": 3012,
+ "▁text": 3013,
+ "▁May": 3014,
+ "▁org": 3015,
+ "▁port": 3016,
+ "Pl": 3017,
+ "ently": 3018,
+ "▁door": 3019,
+ "US": 3020,
+ "▁(*": 3021,
+ "kt": 3022,
+ "ES": 3023,
+ "ential": 3024,
+ "▁iss": 3025,
+ "▁inc": 3026,
+ "Node": 3027,
+ "ively": 3028,
+ "▁asked": 3029,
+ "irt": 3030,
+ "▁Te": 3031,
+ "▁report": 3032,
+ "▁chang": 3033,
+ "сти": 3034,
+ "▁along": 3035,
+ "▁change": 3036,
+ "Size": 3037,
+ "▁ever": 3038,
+ "▁occ": 3039,
+ "ury": 3040,
+ "▁mind": 3041,
+ "order": 3042,
+ "point": 3043,
+ "сто": 3044,
+ "▁whe": 3045,
+ "▁important": 3046,
+ "des": 3047,
+ "▁Not": 3048,
+ "▁writ": 3049,
+ "▁eyes": 3050,
+ "▁desc": 3051,
+ "most": 3052,
+ "ks": 3053,
+ "▁bit": 3054,
+ "▁▁▁": 3055,
+ "▁success": 3056,
+ "ть": 3057,
+ "бо": 3058,
+ "core": 3059,
+ "}(": 3060,
+ "▁array": 3061,
+ "lin": 3062,
+ "lish": 3063,
+ "▁following": 3064,
+ "Field": 3065,
+ "ids": 3066,
+ "hing": 3067,
+ "▁cal": 3068,
+ "Is": 3069,
+ "aring": 3070,
+ "lev": 3071,
+ "alt": 3072,
+ "CH": 3073,
+ "▁dé": 3074,
+ "alpha": 3075,
+ "▁four": 3076,
+ "▁law": 3077,
+ "▁се": 3078,
+ "iron": 3079,
+ "▁disc": 3080,
+ "се": 3081,
+ "ken": 3082,
+ "node": 3083,
+ "▁Par": 3084,
+ "▁Eng": 3085,
+ "▁move": 3086,
+ "▁License": 3087,
+ "cul": 3088,
+ "ione": 3089,
+ ")$": 3090,
+ "▁tw": 3091,
+ "We": 3092,
+ "sel": 3093,
+ "▁With": 3094,
+ "▁once": 3095,
+ "Service": 3096,
+ "bol": 3097,
+ "ured": 3098,
+ "ida": 3099,
+ "▁Qu": 3100,
+ "▁grow": 3101,
+ "▁conne": 3102,
+ "EX": 3103,
+ "▁htt": 3104,
+ "▁};": 3105,
+ "▁walk": 3106,
+ "▁init": 3107,
+ "nal": 3108,
+ "ender": 3109,
+ "cription": 3110,
+ "mber": 3111,
+ "lected": 3112,
+ "po": 3113,
+ "▁nil": 3114,
+ "▁prob": 3115,
+ "чи": 3116,
+ "▁Ste": 3117,
+ "ison": 3118,
+ "ands": 3119,
+ "osed": 3120,
+ "же": 3121,
+ "▁His": 3122,
+ "ür": 3123,
+ "Man": 3124,
+ "Element": 3125,
+ "▁able": 3126,
+ "Index": 3127,
+ "search": 3128,
+ "▁mag": 3129,
+ "ар": 3130,
+ "▁course": 3131,
+ "▁Car": 3132,
+ "▁exp": 3133,
+ "aph": 3134,
+ "▁mit": 3135,
+ "▁doesn": 3136,
+ "▁default": 3137,
+ "/>": 3138,
+ "aim": 3139,
+ "▁service": 3140,
+ "▁within": 3141,
+ "angu": 3142,
+ "▁Д": 3143,
+ "uffer": 3144,
+ "AG": 3145,
+ "▁Do": 3146,
+ "▁incre": 3147,
+ "▁understand": 3148,
+ "}^": 3149,
+ "▁looked": 3150,
+ "gen": 3151,
+ "ailed": 3152,
+ "▁е": 3153,
+ "ayer": 3154,
+ "▁One": 3155,
+ "▁bas": 3156,
+ "▁job": 3157,
+ "mu": 3158,
+ "but": 3159,
+ "elta": 3160,
+ "▁Christ": 3161,
+ "uration": 3162,
+ "▁record": 3163,
+ "▁Univers": 3164,
+ "ivid": 3165,
+ "valid": 3166,
+ "▁Р": 3167,
+ "▁hold": 3168,
+ "▁table": 3169,
+ "ones": 3170,
+ "link": 3171,
+ "▁Ge": 3172,
+ "▁offer": 3173,
+ "ster": 3174,
+ "Form": 3175,
+ "={": 3176,
+ "▁не": 3177,
+ "stance": 3178,
+ "▁govern": 3179,
+ "▁techn": 3180,
+ "▁prim": 3181,
+ "*.": 3182,
+ "cho": 3183,
+ "max": 3184,
+ "▁fore": 3185,
+ "▁Can": 3186,
+ "▁polit": 3187,
+ "ories": 3188,
+ "▁times": 3189,
+ "▁dans": 3190,
+ "▁air": 3191,
+ "▁anything": 3192,
+ "▁sever": 3193,
+ "acy": 3194,
+ "}_": 3195,
+ "He": 3196,
+ "▁least": 3197,
+ "ips": 3198,
+ "ENT": 3199,
+ "do": 3200,
+ "▁от": 3201,
+ "▁cost": 3202,
+ ".”": 3203,
+ "▁children": 3204,
+ "ability": 3205,
+ "But": 3206,
+ "▁path": 3207,
+ "result": 3208,
+ "acter": 3209,
+ "▁element": 3210,
+ "ee": 3211,
+ "▁wait": 3212,
+ "▁money": 3213,
+ "Map": 3214,
+ "td": 3215,
+ "oin": 3216,
+ "iving": 3217,
+ "icht": 3218,
+ "icy": 3219,
+ "sch": 3220,
+ "ste": 3221,
+ "ду": 3222,
+ "ored": 3223,
+ "oud": 3224,
+ "ille": 3225,
+ "ised": 3226,
+ "plication": 3227,
+ "▁custom": 3228,
+ "▁having": 3229,
+ "ponent": 3230,
+ "▁By": 3231,
+ "ules": 3232,
+ "ued": 3233,
+ "atter": 3234,
+ "And": 3235,
+ "itive": 3236,
+ "Def": 3237,
+ "▁moment": 3238,
+ "aterial": 3239,
+ "Class": 3240,
+ "ograph": 3241,
+ "ike": 3242,
+ "▁large": 3243,
+ "▁####": 3244,
+ "▁either": 3245,
+ "duct": 3246,
+ "▁Then": 3247,
+ "▁Gu": 3248,
+ "olean": 3249,
+ "pert": 3250,
+ "▁Get": 3251,
+ "▁Ab": 3252,
+ "▁short": 3253,
+ "On": 3254,
+ "iment": 3255,
+ "▁project": 3256,
+ "cript": 3257,
+ "▁including": 3258,
+ "ния": 3259,
+ "▁making": 3260,
+ "▁someone": 3261,
+ "▁Fl": 3262,
+ "▁sat": 3263,
+ "▁company": 3264,
+ "ocus": 3265,
+ "pu": 3266,
+ "▁God": 3267,
+ "ification": 3268,
+ "No": 3269,
+ "▁sn": 3270,
+ "ano": 3271,
+ "ga": 3272,
+ "▁au": 3273,
+ "▁cou": 3274,
+ "ás": 3275,
+ "ended": 3276,
+ "ту": 3277,
+ "ober": 3278,
+ "▁nothing": 3279,
+ "▁net": 3280,
+ "▁pot": 3281,
+ "▁typ": 3282,
+ "▁item": 3283,
+ "rew": 3284,
+ "Att": 3285,
+ "▁young": 3286,
+ "}\r": 3287,
+ "nder": 3288,
+ "start": 3289,
+ "▁Sc": 3290,
+ "*)": 3291,
+ "▁enc": 3292,
+ "▁women": 3293,
+ "▁looking": 3294,
+ "▁ро": 3295,
+ "▁health": 3296,
+ "Path": 3297,
+ "▁After": 3298,
+ "▁mult": 3299,
+ "▁{\\": 3300,
+ "▁land": 3301,
+ "orld": 3302,
+ "▁Des": 3303,
+ "▁eng": 3304,
+ "input": 3305,
+ "▁Pol": 3306,
+ "\"\"": 3307,
+ "Code": 3308,
+ "▁supp": 3309,
+ "ainer": 3310,
+ "heck": 3311,
+ "▁mor": 3312,
+ "▁mill": 3313,
+ "▁aw": 3314,
+ "fs": 3315,
+ "▁doing": 3316,
+ "tings": 3317,
+ "ades": 3318,
+ "▁toget": 3319,
+ "▁certain": 3320,
+ "▁together": 3321,
+ "CE": 3322,
+ "ideo": 3323,
+ "▁American": 3324,
+ "ony": 3325,
+ "idd": 3326,
+ "II": 3327,
+ "ged": 3328,
+ "ables": 3329,
+ "▁ident": 3330,
+ "iod": 3331,
+ "▁parent": 3332,
+ "For": 3333,
+ "ambda": 3334,
+ "ando": 3335,
+ "=\\": 3336,
+ "aged": 3337,
+ "ending": 3338,
+ "Int": 3339,
+ "▁possible": 3340,
+ "▁со": 3341,
+ "ivity": 3342,
+ "num": 3343,
+ "rt": 3344,
+ "ajor": 3345,
+ "create": 3346,
+ "ride": 3347,
+ "▁knew": 3348,
+ "bit": 3349,
+ "itional": 3350,
+ "▁lik": 3351,
+ "▁Her": 3352,
+ "ension": 3353,
+ "\".": 3354,
+ "oto": 3355,
+ "▁exist": 3356,
+ "aken": 3357,
+ "▁actually": 3358,
+ "ca": 3359,
+ "▁Г": 3360,
+ "хо": 3361,
+ "inn": 3362,
+ "All": 3363,
+ "buf": 3364,
+ "▁Me": 3365,
+ "▁seen": 3366,
+ "ops": 3367,
+ "▁▁▁▁▁▁▁▁▁": 3368,
+ "Not": 3369,
+ "▁control": 3370,
+ "▁respon": 3371,
+ "};": 3372,
+ "ilt": 3373,
+ "isk": 3374,
+ "▁bad": 3375,
+ "▁often": 3376,
+ "▁past": 3377,
+ "aper": 3378,
+ "▁reason": 3379,
+ "eters": 3380,
+ "▁wanted": 3381,
+ "ura": 3382,
+ "table": 3383,
+ "ormal": 3384,
+ "width": 3385,
+ "га": 3386,
+ "ptr": 3387,
+ "▁dest": 3388,
+ "▁design": 3389,
+ "▁sound": 3390,
+ "▁plan": 3391,
+ "▁base": 3392,
+ "hand": 3393,
+ "gs": 3394,
+ "▁says": 3395,
+ "function": 3396,
+ "▁tri": 3397,
+ "mt": 3398,
+ "▁invest": 3399,
+ "▁available": 3400,
+ "ayout": 3401,
+ "▁och": 3402,
+ "▁las": 3403,
+ "illed": 3404,
+ "Val": 3405,
+ "▁ф": 3406,
+ "iety": 3407,
+ "mon": 3408,
+ "Hand": 3409,
+ "Fr": 3410,
+ "iam": 3411,
+ "pace": 3412,
+ "▁Ob": 3413,
+ "▁para": 3414,
+ "▁meet": 3415,
+ "▁sum": 3416,
+ "Message": 3417,
+ "ici": 3418,
+ "▁known": 3419,
+ "▁gen": 3420,
+ "amma": 3421,
+ "arr": 3422,
+ "▁tre": 3423,
+ "oke": 3424,
+ "uth": 3425,
+ "~\\": 3426,
+ "▁experience": 3427,
+ "icle": 3428,
+ "▁Il": 3429,
+ "▁sent": 3430,
+ "▁others": 3431,
+ "▁soft": 3432,
+ "IP": 3433,
+ "▁max": 3434,
+ "ball": 3435,
+ "▁market": 3436,
+ "▁pour": 3437,
+ "pression": 3438,
+ "eps": 3439,
+ "▁saw": 3440,
+ "▁across": 3441,
+ "▁Su": 3442,
+ "Over": 3443,
+ "ние": 3444,
+ "ulation": 3445,
+ "▁Reg": 3446,
+ "▁+=": 3447,
+ "body": 3448,
+ ")\\": 3449,
+ "▁print": 3450,
+ "▁при": 3451,
+ "db": 3452,
+ "ources": 3453,
+ "wards": 3454,
+ "▁black": 3455,
+ "со": 3456,
+ "ili": 3457,
+ "▁Ed": 3458,
+ "▁complet": 3459,
+ "▁single": 3460,
+ "▁IN": 3461,
+ "ached": 3462,
+ "bt": 3463,
+ "▁code": 3464,
+ "▁bool": 3465,
+ "▁area": 3466,
+ "▁require": 3467,
+ "▁problem": 3468,
+ "aced": 3469,
+ "Equ": 3470,
+ "▁config": 3471,
+ "vec": 3472,
+ "ney": 3473,
+ "cy": 3474,
+ "Al": 3475,
+ "▁account": 3476,
+ "ymbol": 3477,
+ "▁ste": 3478,
+ "ges": 3479,
+ "Array": 3480,
+ "empl": 3481,
+ "context": 3482,
+ "Des": 3483,
+ "Result": 3484,
+ "ecut": 3485,
+ "▁target": 3486,
+ "▁getting": 3487,
+ "\"/>": 3488,
+ "ogle": 3489,
+ "▁himself": 3490,
+ "▁wasn": 3491,
+ "▁block": 3492,
+ "▁ant": 3493,
+ "▁York": 3494,
+ "▁become": 3495,
+ "iff": 3496,
+ "ports": 3497,
+ "reate": 3498,
+ "='": 3499,
+ "cd": 3500,
+ "location": 3501,
+ "ет": 3502,
+ "▁access": 3503,
+ "gress": 3504,
+ "ros": 3505,
+ "Up": 3506,
+ "▁working": 3507,
+ "▁Am": 3508,
+ "iqu": 3509,
+ "cer": 3510,
+ "▁((": 3511,
+ "▁Per": 3512,
+ "▁func": 3513,
+ "▁girl": 3514,
+ "▁above": 3515,
+ "pen": 3516,
+ "пи": 3517,
+ "ido": 3518,
+ "▁version": 3519,
+ "TY": 3520,
+ "▁;": 3521,
+ "mary": 3522,
+ "abled": 3523,
+ "annel": 3524,
+ "▁example": 3525,
+ "▁context": 3526,
+ "OP": 3527,
+ "▁red": 3528,
+ "▁cir": 3529,
+ "sm": 3530,
+ "Log": 3531,
+ "▁space": 3532,
+ "▁fut": 3533,
+ "▁Gener": 3534,
+ "ills": 3535,
+ "▁dri": 3536,
+ "_.": 3537,
+ "▁felt": 3538,
+ "▁offic": 3539,
+ "▁===": 3540,
+ "ii": 3541,
+ "▁started": 3542,
+ "▁Т": 3543,
+ "▁});": 3544,
+ "js": 3545,
+ "▁front": 3546,
+ "▁almost": 3547,
+ "irm": 3548,
+ "!\"": 3549,
+ "signed": 3550,
+ "▁yet": 3551,
+ "▁trad": 3552,
+ "ients": 3553,
+ "ama": 3554,
+ "▁input": 3555,
+ "lim": 3556,
+ "па": 3557,
+ "▁ка": 3558,
+ "▁camp": 3559,
+ "ibr": 3560,
+ "fect": 3561,
+ "unt": 3562,
+ "▁half": 3563,
+ "▁cover": 3564,
+ "anguage": 3565,
+ "▁ben": 3566,
+ "ha": 3567,
+ "▁diff": 3568,
+ "_\\": 3569,
+ "▁об": 3570,
+ "])": 3571,
+ "odes": 3572,
+ "hel": 3573,
+ "ios": 3574,
+ "▁О": 3575,
+ "▁mot": 3576,
+ "▁social": 3577,
+ "////////": 3578,
+ "▁stre": 3579,
+ "ground": 3580,
+ "ів": 3581,
+ "object": 3582,
+ "ples": 3583,
+ "reed": 3584,
+ "▁een": 3585,
+ "▁based": 3586,
+ "▁range": 3587,
+ "An": 3588,
+ "urg": 3589,
+ "▁learn": 3590,
+ "▁exc": 3591,
+ "▁imp": 3592,
+ "▁means": 3593,
+ "▁wur": 3594,
+ "ends": 3595,
+ "void": 3596,
+ "▁std": 3597,
+ "▁particular": 3598,
+ "ja": 3599,
+ "▁source": 3600,
+ "default": 3601,
+ "py": 3602,
+ "▁als": 3603,
+ "scri": 3604,
+ "status": 3605,
+ "▁story": 3606,
+ "▁begin": 3607,
+ "▁position": 3608,
+ "▁special": 3609,
+ "php": 3610,
+ "▁bar": 3611,
+ "▁pract": 3612,
+ "call": 3613,
+ "▁das": 3614,
+ "▁rad": 3615,
+ "▁close": 3616,
+ "www": 3617,
+ "ере": 3618,
+ "gu": 3619,
+ "▁Er": 3620,
+ "▁dom": 3621,
+ "AM": 3622,
+ "▁bed": 3623,
+ "▁several": 3624,
+ "aul": 3625,
+ "box": 3626,
+ "▁low": 3627,
+ "pack": 3628,
+ "Reg": 3629,
+ "Of": 3630,
+ "atures": 3631,
+ "én": 3632,
+ "eder": 3633,
+ "uilder": 3634,
+ "cast": 3635,
+ "conom": 3636,
+ "raft": 3637,
+ "▁makes": 3638,
+ "Loc": 3639,
+ "http": 3640,
+ "▁abs": 3641,
+ "resh": 3642,
+ "▁Will": 3643,
+ "break": 3644,
+ "▁options": 3645,
+ "fort": 3646,
+ "▁из": 3647,
+ "▁anal": 3648,
+ "▁env": 3649,
+ "({": 3650,
+ "event": 3651,
+ "▁page": 3652,
+ "ternal": 3653,
+ "▁distribut": 3654,
+ "▁food": 3655,
+ "check": 3656,
+ "CK": 3657,
+ "▁во": 3658,
+ "assert": 3659,
+ "án": 3660,
+ "base": 3661,
+ "▁whole": 3662,
+ "ación": 3663,
+ "OD": 3664,
+ "▁turned": 3665,
+ "igma": 3666,
+ "▁response": 3667,
+ "▁University": 3668,
+ "▁div": 3669,
+ "apter": 3670,
+ "▁results": 3671,
+ "▁represent": 3672,
+ "▁everything": 3673,
+ "▁Cent": 3674,
+ "utes": 3675,
+ "rix": 3676,
+ "▁Some": 3677,
+ "▁behind": 3678,
+ "▁creat": 3679,
+ "place": 3680,
+ "su": 3681,
+ "▁Part": 3682,
+ "umb": 3683,
+ "mathbb": 3684,
+ "ping": 3685,
+ "▁match": 3686,
+ "Out": 3687,
+ "dom": 3688,
+ "▁situ": 3689,
+ "dr": 3690,
+ "ara": 3691,
+ "▁window": 3692,
+ "ns": 3693,
+ "lished": 3694,
+ "▁Ver": 3695,
+ "▁message": 3696,
+ "▁Em": 3697,
+ "▁human": 3698,
+ "perties": 3699,
+ "лу": 3700,
+ "lem": 3701,
+ "ORT": 3702,
+ "▁early": 3703,
+ "▁quick": 3704,
+ "▁та": 3705,
+ "roid": 3706,
+ "▁country": 3707,
+ "▁due": 3708,
+ "▁Die": 3709,
+ "▁trying": 3710,
+ "▁live": 3711,
+ "▁press": 3712,
+ "INT": 3713,
+ "With": 3714,
+ "oved": 3715,
+ "▁specific": 3716,
+ "▁fall": 3717,
+ "uk": 3718,
+ "yl": 3719,
+ "▁general": 3720,
+ "му": 3721,
+ "ну": 3722,
+ "▁names": 3723,
+ "where": 3724,
+ "▁These": 3725,
+ "▁sil": 3726,
+ "ét": 3727,
+ "▁ener": 3728,
+ "▁Now": 3729,
+ "▁address": 3730,
+ "Response": 3731,
+ "▁Mr": 3732,
+ "▁answ": 3733,
+ "▁film": 3734,
+ "▁strong": 3735,
+ "▁bring": 3736,
+ "▁United": 3737,
+ "▁ge": 3738,
+ "▁woman": 3739,
+ "New": 3740,
+ "ett": 3741,
+ ".)": 3742,
+ "ename": 3743,
+ "▁AN": 3744,
+ "▁describ": 3745,
+ "за": 3746,
+ "ising": 3747,
+ "EL": 3748,
+ "ql": 3749,
+ "▁fur": 3750,
+ "ying": 3751,
+ "▁Cal": 3752,
+ "▁Dr": 3753,
+ "ERR": 3754,
+ "▁\\\\": 3755,
+ "angle": 3756,
+ "urope": 3757,
+ "▁city": 3758,
+ "▁index": 3759,
+ "▁action": 3760,
+ "▁However": 3761,
+ "▁fig": 3762,
+ "ias": 3763,
+ "▁question": 3764,
+ "▁Jan": 3765,
+ "▁Med": 3766,
+ "▁Cont": 3767,
+ "amed": 3768,
+ "Call": 3769,
+ "plied": 3770,
+ "tty": 3771,
+ "▁individ": 3772,
+ "page": 3773,
+ "▁comb": 3774,
+ "section": 3775,
+ "▁Comm": 3776,
+ "uel": 3777,
+ "▁het": 3778,
+ "▁Bar": 3779,
+ "agement": 3780,
+ "fin": 3781,
+ "▁major": 3782,
+ "oper": 3783,
+ "api": 3784,
+ "room": 3785,
+ "▁„": 3786,
+ "▁hab": 3787,
+ "зи": 3788,
+ "▁auf": 3789,
+ "current": 3790,
+ "ni": 3791,
+ "▁include": 3792,
+ "▁qui": 3793,
+ "va": 3794,
+ "UE": 3795,
+ "▁idea": 3796,
+ ",'": 3797,
+ "▁required": 3798,
+ "▁heart": 3799,
+ "ibility": 3800,
+ "iction": 3801,
+ "Model": 3802,
+ "write": 3803,
+ "▁content": 3804,
+ "▁wer": 3805,
+ "▁hands": 3806,
+ "zen": 3807,
+ "char": 3808,
+ "}^{": 3809,
+ "▁mass": 3810,
+ "ply": 3811,
+ "▁nat": 3812,
+ "rel": 3813,
+ "▁dat": 3814,
+ "================": 3815,
+ "imal": 3816,
+ "▁probably": 3817,
+ "unch": 3818,
+ "▁mer": 3819,
+ "ilar": 3820,
+ "ires": 3821,
+ "▁watch": 3822,
+ "SI": 3823,
+ "▁cult": 3824,
+ "▁mother": 3825,
+ "▁government": 3826,
+ "ording": 3827,
+ "▁()": 3828,
+ "▁pri": 3829,
+ "▁link": 3830,
+ "group": 3831,
+ "OL": 3832,
+ "▁near": 3833,
+ "▁Ser": 3834,
+ "Ser": 3835,
+ "ito": 3836,
+ "▁values": 3837,
+ "▁java": 3838,
+ "fully": 3839,
+ "Count": 3840,
+ "++)": 3841,
+ "▁vi": 3842,
+ "▁white": 3843,
+ "mat": 3844,
+ "ctx": 3845,
+ "▁conc": 3846,
+ "▁stay": 3847,
+ "ging": 3848,
+ "▁clear": 3849,
+ "▁copy": 3850,
+ "selves": 3851,
+ "▁provide": 3852,
+ "▁words": 3853,
+ "comp": 3854,
+ "args": 3855,
+ "▁pick": 3856,
+ "uly": 3857,
+ "▁vari": 3858,
+ "▁believe": 3859,
+ "▁Co": 3860,
+ "Property": 3861,
+ "Group": 3862,
+ "▁ten": 3863,
+ "ischen": 3864,
+ "eturn": 3865,
+ "ival": 3866,
+ "System": 3867,
+ "CL": 3868,
+ "bed": 3869,
+ "▁total": 3870,
+ "▁ist": 3871,
+ "Input": 3872,
+ "uments": 3873,
+ "Manager": 3874,
+ "ши": 3875,
+ "▁win": 3876,
+ "leep": 3877,
+ "PI": 3878,
+ "ного": 3879,
+ "ruction": 3880,
+ "▁inte": 3881,
+ "App": 3882,
+ "avor": 3883,
+ "▁respect": 3884,
+ "ators": 3885,
+ "▁como": 3886,
+ "▁cut": 3887,
+ "FA": 3888,
+ "▁sus": 3889,
+ "▁App": 3890,
+ "rect": 3891,
+ "FI": 3892,
+ "▁began": 3893,
+ "oph": 3894,
+ "▁sort": 3895,
+ "though": 3896,
+ "је": 3897,
+ "icro": 3898,
+ "Trans": 3899,
+ "лі": 3900,
+ "▁Inst": 3901,
+ "request": 3902,
+ "ор": 3903,
+ "▁relations": 3904,
+ "-\\": 3905,
+ "Status": 3906,
+ "жи": 3907,
+ "▁father": 3908,
+ "cs": 3909,
+ "▁sex": 3910,
+ "isch": 3911,
+ "vo": 3912,
+ "}_{": 3913,
+ "aven": 3914,
+ "▁Ne": 3915,
+ "ATE": 3916,
+ "itten": 3917,
+ "▁ess": 3918,
+ "TH": 3919,
+ "ights": 3920,
+ "▁hom": 3921,
+ "▁today": 3922,
+ "▁zu": 3923,
+ "ita": 3924,
+ "▁isn": 3925,
+ "▁opt": 3926,
+ "ogn": 3927,
+ "ér": 3928,
+ "▁whether": 3929,
+ "ixed": 3930,
+ "phi": 3931,
+ "idence": 3932,
+ "ald": 3933,
+ "Client": 3934,
+ "At": 3935,
+ "▁death": 3936,
+ "▁Let": 3937,
+ "ius": 3938,
+ "ги": 3939,
+ "▁ре": 3940,
+ "ben": 3941,
+ ")\r": 3942,
+ "ba": 3943,
+ ">": 3944,
+ "avel": 3945,
+ "▁miss": 3946,
+ "▁node": 3947,
+ "▁($": 3948,
+ "▁color": 3949,
+ "▁obt": 3950,
+ "tot": 3951,
+ "▁пре": 3952,
+ "CON": 3953,
+ "ette": 3954,
+ "▁Go": 3955,
+ "Fl": 3956,
+ "▁Don": 3957,
+ "▁crit": 3958,
+ "▁ri": 3959,
+ "post": 3960,
+ "▁->": 3961,
+ "▁Just": 3962,
+ "What": 3963,
+ "atal": 3964,
+ "▁Min": 3965,
+ "▁Cor": 3966,
+ "▁dark": 3967,
+ "rl": 3968,
+ "▁larg": 3969,
+ "ding": 3970,
+ "ón": 3971,
+ "ouch": 3972,
+ "▁um": 3973,
+ "▁elect": 3974,
+ "▁dam": 3975,
+ "▁needs": 3976,
+ "▁matter": 3977,
+ "▁rather": 3978,
+ "from": 3979,
+ "ram": 3980,
+ "▁і": 3981,
+ "▁taken": 3982,
+ "▁deal": 3983,
+ "▁period": 3984,
+ "▁Mon": 3985,
+ "▁Л": 3986,
+ "▁Aug": 3987,
+ "run": 3988,
+ "mm": 3989,
+ "elle": 3990,
+ "▁export": 3991,
+ "Sc": 3992,
+ "vis": 3993,
+ "abor": 3994,
+ "▁author": 3995,
+ "ère": 3996,
+ "▁remember": 3997,
+ "▁redu": 3998,
+ "▁List": 3999,
+ "▁focus": 4000,
+ "▁character": 4001,
+ "Table": 4002,
+ "▁individual": 4003,
+ "▁needed": 4004,
+ "bum": 4005,
+ "▁style": 4006,
+ "inary": 4007,
+ "ersion": 4008,
+ "oute": 4009,
+ "▁Pe": 4010,
+ "▁hon": 4011,
+ "mut": 4012,
+ "see": 4013,
+ "▁became": 4014,
+ "▁dire": 4015,
+ "▁document": 4016,
+ "sec": 4017,
+ "ening": 4018,
+ "▁visit": 4019,
+ "▁fac": 4020,
+ "tx": 4021,
+ "down": 4022,
+ "plit": 4023,
+ "▁phys": 4024,
+ "itting": 4025,
+ "joy": 4026,
+ "▁hig": 4027,
+ "This": 4028,
+ "Ad": 4029,
+ "▁Brit": 4030,
+ "▁employ": 4031,
+ "▁ré": 4032,
+ "▁т": 4033,
+ "lambda": 4034,
+ "▁impro": 4035,
+ "▁Bo": 4036,
+ "iding": 4037,
+ "▁online": 4038,
+ "mem": 4039,
+ "atform": 4040,
+ "▁War": 4041,
+ "▁cas": 4042,
+ "asure": 4043,
+ "▁pur": 4044,
+ "medi": 4045,
+ "Dis": 4046,
+ "▁Germ": 4047,
+ "pc": 4048,
+ "са": 4049,
+ "▁friends": 4050,
+ "▁Mc": 4051,
+ "DI": 4052,
+ "▁plus": 4053,
+ "▁Set": 4054,
+ "iddle": 4055,
+ "itut": 4056,
+ "▁depend": 4057,
+ "rest": 4058,
+ "▁Je": 4059,
+ "▁hor": 4060,
+ "▁entire": 4061,
+ "Query": 4062,
+ "▁refer": 4063,
+ "▁hot": 4064,
+ "▁Aust": 4065,
+ "▁common": 4066,
+ "ці": 4067,
+ "▁pull": 4068,
+ "▁Add": 4069,
+ "▁season": 4070,
+ "▁invol": 4071,
+ "▁World": 4072,
+ "client": 4073,
+ "now": 4074,
+ "true": 4075,
+ "append": 4076,
+ "itted": 4077,
+ "empt": 4078,
+ "){": 4079,
+ "///": 4080,
+ "▁prop": 4081,
+ "imate": 4082,
+ "SC": 4083,
+ "▁hours": 4084,
+ "▁hope": 4085,
+ "andom": 4086,
+ "ід": 4087,
+ "istic": 4088,
+ "▁property": 4089,
+ "sg": 4090,
+ ">(": 4091,
+ "▁write": 4092,
+ "mark": 4093,
+ "find": 4094,
+ "▁personal": 4095,
+ "][": 4096,
+ "rown": 4097,
+ "Ph": 4098,
+ "▁foot": 4099,
+ "▁research": 4100,
+ "ironment": 4101,
+ "▁nom": 4102,
+ "▁instance": 4103,
+ "▁held": 4104,
+ "De": 4105,
+ "▁members": 4106,
+ "▁fire": 4107,
+ "▁history": 4108,
+ "▁map": 4109,
+ "▁discuss": 4110,
+ "▁espec": 4111,
+ "▁taking": 4112,
+ "▁services": 4113,
+ "▁indust": 4114,
+ "igen": 4115,
+ "▁Ass": 4116,
+ "▁expected": 4117,
+ "▁wurde": 4118,
+ "dir": 4119,
+ "▁among": 4120,
+ "▁sugg": 4121,
+ "rec": 4122,
+ "Inter": 4123,
+ "block": 4124,
+ "▁Rep": 4125,
+ "▁pain": 4126,
+ "▁five": 4127,
+ "▁fund": 4128,
+ "rid": 4129,
+ "arrow": 4130,
+ "▁treat": 4131,
+ "▁heard": 4132,
+ "▁determ": 4133,
+ "icult": 4134,
+ "▁sense": 4135,
+ "ese": 4136,
+ "Fun": 4137,
+ "▁months": 4138,
+ "json": 4139,
+ ",”": 4140,
+ "TI": 4141,
+ "orage": 4142,
+ "▁У": 4143,
+ "▁everyone": 4144,
+ "▁clos": 4145,
+ "iers": 4146,
+ "airs": 4147,
+ "define": 4148,
+ "If": 4149,
+ "osp": 4150,
+ "▁wonder": 4151,
+ "NA": 4152,
+ "query": 4153,
+ "pg": 4154,
+ "ites": 4155,
+ "▁material": 4156,
+ "yd": 4157,
+ "Read": 4158,
+ "html": 4159,
+ "TE": 4160,
+ "Pr": 4161,
+ "^{\\": 4162,
+ "▁gave": 4163,
+ "▁IS": 4164,
+ "▁suggest": 4165,
+ "Override": 4166,
+ "rodu": 4167,
+ "From": 4168,
+ "▁Europe": 4169,
+ "PO": 4170,
+ "▁soon": 4171,
+ "host": 4172,
+ "▁Ber": 4173,
+ "....": 4174,
+ "▁Har": 4175,
+ "▁energy": 4176,
+ "><": 4177,
+ "aves": 4178,
+ "▁easy": 4179,
+ "▁bre": 4180,
+ "frame": 4181,
+ "▁ground": 4182,
+ "with": 4183,
+ "▁inside": 4184,
+ "ief": 4185,
+ "▁mo": 4186,
+ "pm": 4187,
+ "pan": 4188,
+ "igr": 4189,
+ "▁om": 4190,
+ "next": 4191,
+ "omet": 4192,
+ "▁status": 4193,
+ "▁}\r": 4194,
+ "▁music": 4195,
+ "ora": 4196,
+ "iles": 4197,
+ "ki": 4198,
+ "▁esc": 4199,
+ "▁bes": 4200,
+ "▁Dis": 4201,
+ "▁host": 4202,
+ "▁comes": 4203,
+ "used": 4204,
+ "▁future": 4205,
+ "lick": 4206,
+ "aid": 4207,
+ "▁compet": 4208,
+ "▁voice": 4209,
+ "▁load": 4210,
+ "evel": 4211,
+ "▁neg": 4212,
+ "▁command": 4213,
+ "▁für": 4214,
+ "▁pie": 4215,
+ "▁quite": 4216,
+ "▁blo": 4217,
+ "agn": 4218,
+ "ilon": 4219,
+ "▁claim": 4220,
+ "▁teach": 4221,
+ "▁previous": 4222,
+ "▁site": 4223,
+ "color": 4224,
+ "attr": 4225,
+ "▁accept": 4226,
+ "▁exact": 4227,
+ ")}": 4228,
+ "aft": 4229,
+ "roller": 4230,
+ "он": 4231,
+ "oo": 4232,
+ "Date": 4233,
+ "▁ou": 4234,
+ "sy": 4235,
+ "▁pretty": 4236,
+ "▁image": 4237,
+ "BU": 4238,
+ "▁terms": 4239,
+ "▁search": 4240,
+ "▁è": 4241,
+ "▁Val": 4242,
+ "▁‘": 4243,
+ "▁Dav": 4244,
+ "MS": 4245,
+ "src": 4246,
+ "mar": 4247,
+ "incip": 4248,
+ "▁couldn": 4249,
+ "ados": 4250,
+ "▁dro": 4251,
+ "beta": 4252,
+ "imum": 4253,
+ "▁minutes": 4254,
+ "▁grand": 4255,
+ "▁»": 4256,
+ "▁Our": 4257,
+ "Str": 4258,
+ "VER": 4259,
+ "maz": 4260,
+ "▁original": 4261,
+ "ini": 4262,
+ "▁coll": 4263,
+ "loat": 4264,
+ "▁os": 4265,
+ "});": 4266,
+ "summary": 4267,
+ "▁wall": 4268,
+ "Color": 4269,
+ "▁vers": 4270,
+ "▁della": 4271,
+ "▁\"\"\"": 4272,
+ "mathbf": 4273,
+ "zer": 4274,
+ "aur": 4275,
+ "▁track": 4276,
+ "▁associ": 4277,
+ "▁suff": 4278,
+ "▁inde": 4279,
+ "ague": 4280,
+ "▁Apr": 4281,
+ "Le": 4282,
+ "roups": 4283,
+ "board": 4284,
+ "▁attack": 4285,
+ "▁series": 4286,
+ "▁instead": 4287,
+ "ham": 4288,
+ "book": 4289,
+ "▁six": 4290,
+ "▁Rec": 4291,
+ "▁coming": 4292,
+ "urt": 4293,
+ "▁global": 4294,
+ "▁necess": 4295,
+ "lege": 4296,
+ "Pos": 4297,
+ "▁leave": 4298,
+ "▁pod": 4299,
+ "ategory": 4300,
+ "uz": 4301,
+ "▁deep": 4302,
+ "▁km": 4303,
+ "▁outside": 4304,
+ "has": 4305,
+ "options": 4306,
+ "▁Sm": 4307,
+ "Sub": 4308,
+ "rows": 4309,
+ "▁ви": 4310,
+ "▁States": 4311,
+ "▁wrong": 4312,
+ "▁however": 4313,
+ "▁sem": 4314,
+ "▁catch": 4315,
+ "\"),": 4316,
+ "model": 4317,
+ "▁http": 4318,
+ "▁option": 4319,
+ "rie": 4320,
+ "▁ста": 4321,
+ "▁är": 4322,
+ "▁enjoy": 4323,
+ "nu": 4324,
+ "▁pas": 4325,
+ "▁amount": 4326,
+ "▁respons": 4327,
+ "▁Intern": 4328,
+ "▁myself": 4329,
+ "▁opp": 4330,
+ "▁Sim": 4331,
+ "▁sens": 4332,
+ "Ed": 4333,
+ "▁(\\": 4334,
+ "▁students": 4335,
+ "нов": 4336,
+ "▁points": 4337,
+ "arning": 4338,
+ "UP": 4339,
+ "elling": 4340,
+ "▁cannot": 4341,
+ "Be": 4342,
+ "▁length": 4343,
+ "null": 4344,
+ "uint": 4345,
+ "wise": 4346,
+ "▁double": 4347,
+ "ige": 4348,
+ "ista": 4349,
+ "▁estab": 4350,
+ "anch": 4351,
+ "▁ago": 4352,
+ "▁bound": 4353,
+ "▁fa": 4354,
+ "▁clean": 4355,
+ "▁simple": 4356,
+ "mi": 4357,
+ "########": 4358,
+ "ifier": 4359,
+ "▁General": 4360,
+ "▁seemed": 4361,
+ "ena": 4362,
+ "▁age": 4363,
+ "ной": 4364,
+ "endif": 4365,
+ "AA": 4366,
+ "▁caus": 4367,
+ "▁educ": 4368,
+ "▁cell": 4369,
+ "Gener": 4370,
+ "space": 4371,
+ "▁Your": 4372,
+ "▁beaut": 4373,
+ "gt": 4374,
+ "▁limit": 4375,
+ "▁date": 4376,
+ "Util": 4377,
+ "▁National": 4378,
+ "ows": 4379,
+ "pat": 4380,
+ "quad": 4381,
+ "▁ok": 4382,
+ "▁И": 4383,
+ "arth": 4384,
+ "hat": 4385,
+ "▁community": 4386,
+ "oul": 4387,
+ "▁econom": 4388,
+ "Component": 4389,
+ "bor": 4390,
+ "usion": 4391,
+ "▁below": 4392,
+ "earch": 4393,
+ "ores": 4394,
+ "ban": 4395,
+ "▁August": 4396,
+ "▁further": 4397,
+ "sigma": 4398,
+ "▁ha": 4399,
+ "ji": 4400,
+ "▁comput": 4401,
+ "гра": 4402,
+ "▁None": 4403,
+ "▁ter": 4404,
+ "▁anyone": 4405,
+ "▁task": 4406,
+ "ente": 4407,
+ "position": 4408,
+ "pped": 4409,
+ "▁aus": 4410,
+ "Attribute": 4411,
+ "req": 4412,
+ "addr": 4413,
+ "light": 4414,
+ "ше": 4415,
+ "▁arm": 4416,
+ "cover": 4417,
+ "upport": 4418,
+ "▁Gl": 4419,
+ "▁San": 4420,
+ "▁writing": 4421,
+ "▁lost": 4422,
+ "▁Mark": 4423,
+ "▁gre": 4424,
+ "TYPE": 4425,
+ "▁South": 4426,
+ "▁perfect": 4427,
+ "▁package": 4428,
+ "▁infl": 4429,
+ "haps": 4430,
+ "▁Ang": 4431,
+ "respon": 4432,
+ "ris": 4433,
+ "ptember": 4434,
+ "▁building": 4435,
+ "VAL": 4436,
+ "free": 4437,
+ "▁ce": 4438,
+ "HT": 4439,
+ "▁From": 4440,
+ "ds": 4441,
+ "roy": 4442,
+ "achine": 4443,
+ "nown": 4444,
+ "▁saying": 4445,
+ "▁бы": 4446,
+ "oe": 4447,
+ "Ref": 4448,
+ "▁network": 4449,
+ "parent": 4450,
+ "uge": 4451,
+ "▁similar": 4452,
+ ">\r": 4453,
+ "Builder": 4454,
+ "▁living": 4455,
+ "▁continue": 4456,
+ "anger": 4457,
+ "▁Red": 4458,
+ "▁hair": 4459,
+ "anced": 4460,
+ "ians": 4461,
+ "▁dead": 4462,
+ "▁boolean": 4463,
+ "ication": 4464,
+ "▁де": 4465,
+ "▁client": 4466,
+ "uct": 4467,
+ "▁•": 4468,
+ "SP": 4469,
+ "older": 4470,
+ "пе": 4471,
+ "udio": 4472,
+ "▁deg": 4473,
+ "asing": 4474,
+ "▁step": 4475,
+ "▁pers": 4476,
+ "ção": 4477,
+ "obj": 4478,
+ "oz": 4479,
+ "ula": 4480,
+ "▁round": 4481,
+ "▁upon": 4482,
+ "▁resource": 4483,
+ "▁valid": 4484,
+ "▁II": 4485,
+ "bug": 4486,
+ "std": 4487,
+ "▁ang": 4488,
+ "span": 4489,
+ "pol": 4490,
+ "ialog": 4491,
+ "▁phot": 4492,
+ "?'": 4493,
+ "DB": 4494,
+ "▁Fin": 4495,
+ "VE": 4496,
+ "Em": 4497,
+ "▁cam": 4498,
+ "target": 4499,
+ "pected": 4500,
+ "Hel": 4501,
+ "▁ut": 4502,
+ "▁Test": 4503,
+ "▁town": 4504,
+ "align": 4505,
+ "▁webs": 4506,
+ "inner": 4507,
+ "augh": 4508,
+ "▁except": 4509,
+ "▁initial": 4510,
+ "enty": 4511,
+ "lich": 4512,
+ "▁Aut": 4513,
+ "top": 4514,
+ "▁fail": 4515,
+ "ona": 4516,
+ "▁benef": 4517,
+ "anks": 4518,
+ "ische": 4519,
+ ".*": 4520,
+ "▁signific": 4521,
+ "▁contact": 4522,
+ "Rec": 4523,
+ "ario": 4524,
+ "ottom": 4525,
+ "▁relationship": 4526,
+ "]);": 4527,
+ "▁На": 4528,
+ "Head": 4529,
+ "format": 4530,
+ "▁ét": 4531,
+ "▁More": 4532,
+ "actory": 4533,
+ "portun": 4534,
+ "+\\": 4535,
+ "▁simply": 4536,
+ "▁ep": 4537,
+ "▁Russ": 4538,
+ "ní": 4539,
+ "ua": 4540,
+ "erc": 4541,
+ "▁longer": 4542,
+ "inition": 4543,
+ "ector": 4544,
+ "aption": 4545,
+ "▁profess": 4546,
+ "▁Mus": 4547,
+ "ilities": 4548,
+ "ès": 4549,
+ "▁Act": 4550,
+ "offset": 4551,
+ "▁ill": 4552,
+ "band": 4553,
+ "▁Ag": 4554,
+ "▁По": 4555,
+ "би": 4556,
+ "content": 4557,
+ "icon": 4558,
+ "▁works": 4559,
+ "ynam": 4560,
+ "plement": 4561,
+ "Resource": 4562,
+ "Action": 4563,
+ "▁difficult": 4564,
+ "▁West": 4565,
+ "▁video": 4566,
+ "▁THE": 4567,
+ "▁decl": 4568,
+ "ondon": 4569,
+ "ded": 4570,
+ "}{\\": 4571,
+ "ocr": 4572,
+ "▁City": 4573,
+ "▁я": 4574,
+ "uer": 4575,
+ "cz": 4576,
+ "▁imag": 4577,
+ "cr": 4578,
+ "ete": 4579,
+ "idget": 4580,
+ "▁Mod": 4581,
+ "▁forward": 4582,
+ "▁pict": 4583,
+ "orge": 4584,
+ "▁subject": 4585,
+ "update": 4586,
+ "attle": 4587,
+ "sa": 4588,
+ "▁Ant": 4589,
+ "▁running": 4590,
+ "▁sal": 4591,
+ "conne": 4592,
+ "▁output": 4593,
+ "adata": 4594,
+ "ML": 4595,
+ "Check": 4596,
+ "ledge": 4597,
+ "▁paper": 4598,
+ "params": 4599,
+ "avy": 4600,
+ "▁af": 4601,
+ "▁eine": 4602,
+ "▁jour": 4603,
+ "AY": 4604,
+ "▁itself": 4605,
+ "▁Str": 4606,
+ "style": 4607,
+ "That": 4608,
+ "▁million": 4609,
+ "▁language": 4610,
+ "OS": 4611,
+ "ving": 4612,
+ "▁ма": 4613,
+ "▁то": 4614,
+ ")(": 4615,
+ "▁buy": 4616,
+ "./": 4617,
+ "▁...": 4618,
+ "▁tried": 4619,
+ "▁compl": 4620,
+ "▁activ": 4621,
+ "apped": 4622,
+ "Button": 4623,
+ "Token": 4624,
+ "▁provided": 4625,
+ "iber": 4626,
+ "▁created": 4627,
+ "curity": 4628,
+ "End": 4629,
+ "ał": 4630,
+ "uster": 4631,
+ "izing": 4632,
+ "omb": 4633,
+ "▁sich": 4634,
+ "▁compon": 4635,
+ "▁See": 4636,
+ "▁uint": 4637,
+ "▁label": 4638,
+ "vol": 4639,
+ "ów": 4640,
+ "ocol": 4641,
+ "▁received": 4642,
+ "▁intern": 4643,
+ "це": 4644,
+ "Run": 4645,
+ "▁road": 4646,
+ "▁Oct": 4647,
+ "▁Comp": 4648,
+ "▁study": 4649,
+ "▁те": 4650,
+ "Act": 4651,
+ "▁tour": 4652,
+ "▁State": 4653,
+ "▁added": 4654,
+ "https": 4655,
+ "stream": 4656,
+ "▁lower": 4657,
+ "▁box": 4658,
+ "▁Sk": 4659,
+ "▁themselves": 4660,
+ "▁cross": 4661,
+ "▁echo": 4662,
+ "▁device": 4663,
+ "pose": 4664,
+ "▁games": 4665,
+ "PL": 4666,
+ "Window": 4667,
+ "ises": 4668,
+ "title": 4669,
+ "Stream": 4670,
+ "zt": 4671,
+ "▁Sw": 4672,
+ "▁role": 4673,
+ "iant": 4674,
+ "ku": 4675,
+ "sequ": 4676,
+ "▁late": 4677,
+ "▁sold": 4678,
+ "ря": 4679,
+ "Comm": 4680,
+ "▁entre": 4681,
+ "▁dog": 4682,
+ "device": 4683,
+ "Par": 4684,
+ "▁likely": 4685,
+ "^{-": 4686,
+ "▁len": 4687,
+ "▁Paul": 4688,
+ "▁tool": 4689,
+ "Off": 4690,
+ "▁famil": 4691,
+ "▁draw": 4692,
+ "apping": 4693,
+ "▁events": 4694,
+ "cret": 4695,
+ "rought": 4696,
+ "Content": 4697,
+ "▁software": 4698,
+ "ria": 4699,
+ "msg": 4700,
+ "gamma": 4701,
+ "▁hear": 4702,
+ "Oper": 4703,
+ "▁yourself": 4704,
+ "▁liter": 4705,
+ "emp": 4706,
+ "▁separ": 4707,
+ "▁З": 4708,
+ "▁title": 4709,
+ "Method": 4710,
+ "mathrm": 4711,
+ "▁slow": 4712,
+ "▁Rom": 4713,
+ "!!": 4714,
+ "▁tax": 4715,
+ "ска": 4716,
+ "emplate": 4717,
+ "oi": 4718,
+ "▁Art": 4719,
+ "false": 4720,
+ "astic": 4721,
+ "сть": 4722,
+ "ocket": 4723,
+ "▁ens": 4724,
+ "TO": 4725,
+ "amente": 4726,
+ "local": 4727,
+ "chie": 4728,
+ "▁pan": 4729,
+ "ний": 4730,
+ "chema": 4731,
+ "▁North": 4732,
+ "зо": 4733,
+ "▁>=": 4734,
+ "Aut": 4735,
+ "▁dig": 4736,
+ "▁seems": 4737,
+ "▁morning": 4738,
+ "sole": 4739,
+ "umer": 4740,
+ "delta": 4741,
+ "ité": 4742,
+ "abase": 4743,
+ "raf": 4744,
+ "▁observ": 4745,
+ "▁Est": 4746,
+ "▁seg": 4747,
+ "▁[]": 4748,
+ "▁Pres": 4749,
+ "iful": 4750,
+ "push": 4751,
+ "▁Off": 4752,
+ "ipe": 4753,
+ "ati": 4754,
+ "▁dim": 4755,
+ "ceed": 4756,
+ "Ent": 4757,
+ "____": 4758,
+ "entry": 4759,
+ "▁fight": 4760,
+ "▁cred": 4761,
+ "▁OR": 4762,
+ "▁Dep": 4763,
+ "${": 4764,
+ "лен": 4765,
+ "Create": 4766,
+ "▁April": 4767,
+ "ministr": 4768,
+ "FL": 4769,
+ "▁Ap": 4770,
+ "▁Here": 4771,
+ "private": 4772,
+ "Instance": 4773,
+ "iem": 4774,
+ "▁office": 4775,
+ "▁third": 4776,
+ "▁update": 4777,
+ "Line": 4778,
+ "tag": 4779,
+ "▁especially": 4780,
+ "▁года": 4781,
+ "▁cu": 4782,
+ "▁kill": 4783,
+ "aught": 4784,
+ "▁swe": 4785,
+ "Options": 4786,
+ "IM": 4787,
+ "CC": 4788,
+ "▁compan": 4789,
+ "just": 4790,
+ "▁While": 4791,
+ "izer": 4792,
+ "▁мо": 4793,
+ "ке": 4794,
+ "▁auto": 4795,
+ "▁band": 4796,
+ "мен": 4797,
+ "iques": 4798,
+ "▁ple": 4799,
+ "NO": 4800,
+ "▁OF": 4801,
+ "▁song": 4802,
+ "▁Acc": 4803,
+ "EXT": 4804,
+ "ensor": 4805,
+ "ining": 4806,
+ "▁lat": 4807,
+ "big": 4808,
+ "▁King": 4809,
+ "och": 4810,
+ "si": 4811,
+ "▁Hist": 4812,
+ "▁quality": 4813,
+ "mode": 4814,
+ "▁opportun": 4815,
+ "▁wouldn": 4816,
+ ":**": 4817,
+ "output": 4818,
+ "▁feet": 4819,
+ "▁mis": 4820,
+ "df": 4821,
+ "aging": 4822,
+ "▁ме": 4823,
+ "▁tro": 4824,
+ "▁defined": 4825,
+ "▁review": 4826,
+ "▁Fil": 4827,
+ ">>": 4828,
+ "▁princip": 4829,
+ "Base": 4830,
+ "dict": 4831,
+ "verage": 4832,
+ "icient": 4833,
+ "IF": 4834,
+ "▁hit": 4835,
+ "Page": 4836,
+ "▁perm": 4837,
+ "cel": 4838,
+ "ít": 4839,
+ "▁express": 4840,
+ "▁indic": 4841,
+ "▁September": 4842,
+ "image": 4843,
+ "▁products": 4844,
+ "▁media": 4845,
+ "change": 4846,
+ "igger": 4847,
+ "▁send": 4848,
+ "last": 4849,
+ "ming": 4850,
+ "pa": 4851,
+ "uary": 4852,
+ "▁speak": 4853,
+ "ный": 4854,
+ "ще": 4855,
+ "ysis": 4856,
+ "lying": 4857,
+ "▁ч": 4858,
+ "like": 4859,
+ "ры": 4860,
+ "ві": 4861,
+ "▁Mich": 4862,
+ "MO": 4863,
+ "▁Jah": 4864,
+ "ensive": 4865,
+ "▁share": 4866,
+ "▁development": 4867,
+ "CP": 4868,
+ "spec": 4869,
+ "▁fast": 4870,
+ "het": 4871,
+ "HO": 4872,
+ "▁particip": 4873,
+ "Block": 4874,
+ "▁viol": 4875,
+ "▁frame": 4876,
+ "▁qual": 4877,
+ "tre": 4878,
+ "▁Ф": 4879,
+ "▁toward": 4880,
+ "fg": 4881,
+ "Box": 4882,
+ "Column": 4883,
+ "▁milit": 4884,
+ "▁March": 4885,
+ "▁various": 4886,
+ "pass": 4887,
+ "▁Park": 4888,
+ "▁Ben": 4889,
+ "Frame": 4890,
+ "▁normal": 4891,
+ "open": 4892,
+ "px": 4893,
+ "▁phone": 4894,
+ "▁Even": 4895,
+ "▁ma": 4896,
+ "ibrary": 4897,
+ "Start": 4898,
+ "idden": 4899,
+ "rho": 4900,
+ "graph": 4901,
+ "acing": 4902,
+ "'.": 4903,
+ "arter": 4904,
+ "mes": 4905,
+ "inst": 4906,
+ "▁ir": 4907,
+ "active": 4908,
+ "▁fem": 4909,
+ "▁moved": 4910,
+ "▁store": 4911,
+ "▁price": 4912,
+ "\").": 4913,
+ "berg": 4914,
+ "▁nov": 4915,
+ "▁card": 4916,
+ "ellow": 4917,
+ "▁party": 4918,
+ "▁Mor": 4919,
+ "ael": 4920,
+ "▁percent": 4921,
+ "▁training": 4922,
+ "▁ing": 4923,
+ "imer": 4924,
+ "▁Sam": 4925,
+ "Default": 4926,
+ "▁fuck": 4927,
+ "▁complete": 4928,
+ "uid": 4929,
+ "▁details": 4930,
+ "▁led": 4931,
+ "Point": 4932,
+ "▁Count": 4933,
+ "▁regard": 4934,
+ "zo": 4935,
+ "▁Bro": 4936,
+ "▁recogn": 4937,
+ "▁Hol": 4938,
+ "UM": 4939,
+ "element": 4940,
+ "Mode": 4941,
+ "▁exam": 4942,
+ "▁EX": 4943,
+ "Image": 4944,
+ "verse": 4945,
+ "riter": 4946,
+ "soft": 4947,
+ "▁introdu": 4948,
+ "▁surpr": 4949,
+ "Buffer": 4950,
+ "lector": 4951,
+ "aren": 4952,
+ "anged": 4953,
+ "▁Pat": 4954,
+ "▁Pal": 4955,
+ "▁contr": 4956,
+ "Handler": 4957,
+ "▁features": 4958,
+ "iple": 4959,
+ "▁CON": 4960,
+ "Fil": 4961,
+ "▁Port": 4962,
+ "▁thinking": 4963,
+ "doc": 4964,
+ "wer": 4965,
+ "▁worked": 4966,
+ "PC": 4967,
+ "cm": 4968,
+ "dat": 4969,
+ "PRO": 4970,
+ "▁Every": 4971,
+ "▁era": 4972,
+ "▁First": 4973,
+ "gn": 4974,
+ "▁immedi": 4975,
+ "ovember": 4976,
+ "apan": 4977,
+ "▁extra": 4978,
+ "▁section": 4979,
+ "▁June": 4980,
+ "▁via": 4981,
+ "▁gone": 4982,
+ "come": 4983,
+ "▁stri": 4984,
+ "^\\": 4985,
+ "antly": 4986,
+ "▁arch": 4987,
+ "Source": 4988,
+ "▁conv": 4989,
+ "▁London": 4990,
+ "Number": 4991,
+ "▁questions": 4992,
+ "andid": 4993,
+ "▁played": 4994,
+ "env": 4995,
+ "▁School": 4996,
+ "▁natural": 4997,
+ "can": 4998,
+ "▁news": 4999,
+ "DR": 5000,
+ "▁chall": 5001,
+ "▁Soc": 5002,
+ "▁э": 5003,
+ "▁attempt": 5004,
+ "*}": 5005,
+ "Null": 5006,
+ "rote": 5007,
+ "▁bi": 5008,
+ "▁written": 5009,
+ "▁blood": 5010,
+ "▁happened": 5011,
+ "▁cause": 5012,
+ "ashing": 5013,
+ "▁William": 5014,
+ "adem": 5015,
+ "▁brought": 5016,
+ "▁display": 5017,
+ "ima": 5018,
+ "▁finally": 5019,
+ "tab": 5020,
+ "▁returned": 5021,
+ "ных": 5022,
+ "nie": 5023,
+ "▁q": 5024,
+ "▁hers": 5025,
+ "▁Pre": 5026,
+ "▁dou": 5027,
+ "buffer": 5028,
+ "▁effort": 5029,
+ "aine": 5030,
+ "xy": 5031,
+ "▁histor": 5032,
+ "enu": 5033,
+ "▁arriv": 5034,
+ "▁Dem": 5035,
+ "▁favor": 5036,
+ "▁handle": 5037,
+ "SET": 5038,
+ "▁Public": 5039,
+ "rupt": 5040,
+ "▁ur": 5041,
+ "▁force": 5042,
+ "▁és": 5043,
+ "ube": 5044,
+ "Pre": 5045,
+ "рі": 5046,
+ "iny": 5047,
+ "theta": 5048,
+ "isf": 5049,
+ "▁national": 5050,
+ "Equal": 5051,
+ "rench": 5052,
+ "▁wife": 5053,
+ "▁capt": 5054,
+ "▁Inter": 5055,
+ "tau": 5056,
+ "▁sleep": 5057,
+ "../../": 5058,
+ "▁issue": 5059,
+ "▁member": 5060,
+ "▁await": 5061,
+ "▁Dan": 5062,
+ "zi": 5063,
+ "inate": 5064,
+ "▁sym": 5065,
+ "chan": 5066,
+ "▁Jack": 5067,
+ "▁English": 5068,
+ "▁sz": 5069,
+ "ributes": 5070,
+ "▁ign": 5071,
+ "ál": 5072,
+ "▁appear": 5073,
+ "rad": 5074,
+ "idge": 5075,
+ "▁couple": 5076,
+ "▁ship": 5077,
+ "lig": 5078,
+ "web": 5079,
+ "▁usually": 5080,
+ "▁ready": 5081,
+ "▁vill": 5082,
+ "▁Why": 5083,
+ "ebru": 5084,
+ "▁grad": 5085,
+ "ords": 5086,
+ "▁inf": 5087,
+ "▁loss": 5088,
+ "▁od": 5089,
+ "▁Phil": 5090,
+ "server": 5091,
+ "▁Up": 5092,
+ "▁buff": 5093,
+ "▁filename": 5094,
+ "ABLE": 5095,
+ "iting": 5096,
+ "efore": 5097,
+ "()->": 5098,
+ "▁conditions": 5099,
+ "vm": 5100,
+ "eld": 5101,
+ "itz": 5102,
+ "▁Trans": 5103,
+ "▁weight": 5104,
+ "▁higher": 5105,
+ "▁rate": 5106,
+ "▁accom": 5107,
+ "vider": 5108,
+ "OM": 5109,
+ "▁ways": 5110,
+ "coming": 5111,
+ "▁lock": 5112,
+ "▁etc": 5113,
+ "▁avec": 5114,
+ "▁takes": 5115,
+ "▁Char": 5116,
+ "▁November": 5117,
+ "method": 5118,
+ "▁Austral": 5119,
+ "▁America": 5120,
+ "long": 5121,
+ "cember": 5122,
+ "▁political": 5123,
+ "flow": 5124,
+ "▁maybe": 5125,
+ "▁amb": 5126,
+ "Layout": 5127,
+ "iled": 5128,
+ "omen": 5129,
+ "ola": 5130,
+ "icip": 5131,
+ "partial": 5132,
+ "True": 5133,
+ "▁floor": 5134,
+ "▁Def": 5135,
+ "▁concern": 5136,
+ "yr": 5137,
+ "▁shows": 5138,
+ "ih": 5139,
+ "▁answer": 5140,
+ "acc": 5141,
+ "▁ball": 5142,
+ "▁Rev": 5143,
+ "▁sun": 5144,
+ "▁quickly": 5145,
+ "▁somet": 5146,
+ "mente": 5147,
+ "▁Mal": 5148,
+ "undred": 5149,
+ "▁issues": 5150,
+ "ecause": 5151,
+ "pes": 5152,
+ "▁player": 5153,
+ "▁parents": 5154,
+ "▁popular": 5155,
+ "▁mode": 5156,
+ "▁mention": 5157,
+ "NE": 5158,
+ "Load": 5159,
+ "▁regular": 5160,
+ "aved": 5161,
+ "?:": 5162,
+ "year": 5163,
+ "func": 5164,
+ "▁performance": 5165,
+ "▁July": 5166,
+ "thern": 5167,
+ "▁website": 5168,
+ "ford": 5169,
+ "PR": 5170,
+ "ela": 5171,
+ "level": 5172,
+ "uit": 5173,
+ "flags": 5174,
+ "▁worth": 5175,
+ "▁correspon": 5176,
+ "▁British": 5177,
+ "sim": 5178,
+ "▁alone": 5179,
+ "▁har": 5180,
+ "▁ones": 5181,
+ "obile": 5182,
+ "▁dru": 5183,
+ "chi": 5184,
+ "▁David": 5185,
+ "▁problems": 5186,
+ "▁column": 5187,
+ "();\r": 5188,
+ "ZE": 5189,
+ "▁relig": 5190,
+ "ological": 5191,
+ "▁region": 5192,
+ "ady": 5193,
+ "IO": 5194,
+ "ander": 5195,
+ "Net": 5196,
+ "▁built": 5197,
+ "▁install": 5198,
+ "▁approach": 5199,
+ "Cur": 5200,
+ "▁fine": 5201,
+ "▁talking": 5202,
+ "▁changes": 5203,
+ "Style": 5204,
+ "▁Mart": 5205,
+ "лю": 5206,
+ "response": 5207,
+ "teger": 5208,
+ "{\r": 5209,
+ "irit": 5210,
+ "▁protected": 5211,
+ "▁rele": 5212,
+ "ership": 5213,
+ "тель": 5214,
+ "unsigned": 5215,
+ "ialize": 5216,
+ "▁https": 5217,
+ "Tag": 5218,
+ "▁$(": 5219,
+ "more": 5220,
+ "ypes": 5221,
+ "▁stream": 5222,
+ "etch": 5223,
+ "▁engine": 5224,
+ "KE": 5225,
+ "cmd": 5226,
+ "script": 5227,
+ "ttp": 5228,
+ "▁avoid": 5229,
+ "▁terr": 5230,
+ "▁rock": 5231,
+ "▁ful": 5232,
+ "Update": 5233,
+ "▁environment": 5234,
+ "▁prec": 5235,
+ "▁са": 5236,
+ "▁cases": 5237,
+ "▁offset": 5238,
+ "▁rais": 5239,
+ "lib": 5240,
+ "ées": 5241,
+ "aa": 5242,
+ "yt": 5243,
+ "▁arr": 5244,
+ "opyright": 5245,
+ "first": 5246,
+ "▁util": 5247,
+ "▁feature": 5248,
+ "posed": 5249,
+ "ffect": 5250,
+ "жа": 5251,
+ "itude": 5252,
+ "ements": 5253,
+ "asc": 5254,
+ "ador": 5255,
+ "lections": 5256,
+ "▁club": 5257,
+ "]{": 5258,
+ "▁*)": 5259,
+ "ство": 5260,
+ "▁imm": 5261,
+ "▁former": 5262,
+ "▁rights": 5263,
+ "▁decided": 5264,
+ "▁rev": 5265,
+ "▁ment": 5266,
+ "ani": 5267,
+ "▁stru": 5268,
+ "▁attention": 5269,
+ "artment": 5270,
+ "▁Ital": 5271,
+ "alle": 5272,
+ "▁bis": 5273,
+ "gener": 5274,
+ "▁integr": 5275,
+ "ello": 5276,
+ "rypt": 5277,
+ "▁achie": 5278,
+ "nes": 5279,
+ "▁stra": 5280,
+ "sb": 5281,
+ "▁types": 5282,
+ "▁RE": 5283,
+ "Init": 5284,
+ "▁comment": 5285,
+ "▁addition": 5286,
+ "▁ID": 5287,
+ "ART": 5288,
+ "FO": 5289,
+ "щи": 5290,
+ "Conne": 5291,
+ "▁squ": 5292,
+ "▁considered": 5293,
+ "idad": 5294,
+ "▁October": 5295,
+ "cial": 5296,
+ "▁Of": 5297,
+ "▁travel": 5298,
+ "▁boy": 5299,
+ "').": 5300,
+ "uy": 5301,
+ "illa": 5302,
+ "istry": 5303,
+ "▁va": 5304,
+ "▁Che": 5305,
+ "ERT": 5306,
+ "ende": 5307,
+ "ungen": 5308,
+ "aby": 5309,
+ "▁Rober": 5310,
+ "▁playing": 5311,
+ "ils": 5312,
+ "▁sam": 5313,
+ "▁execut": 5314,
+ "▁Us": 5315,
+ "▁mut": 5316,
+ "▁bal": 5317,
+ "asse": 5318,
+ "▁kids": 5319,
+ "▁financ": 5320,
+ "gor": 5321,
+ "▁Sec": 5322,
+ "bert": 5323,
+ "▁High": 5324,
+ "▁је": 5325,
+ "▁kept": 5326,
+ "button": 5327,
+ "itory": 5328,
+ "▁Rem": 5329,
+ "▁DE": 5330,
+ "▁reach": 5331,
+ "▁bur": 5332,
+ "Label": 5333,
+ "át": 5334,
+ "ago": 5335,
+ "▁passed": 5336,
+ "▁behav": 5337,
+ "xFF": 5338,
+ "▁Return": 5339,
+ "STR": 5340,
+ "▁Les": 5341,
+ "▁ord": 5342,
+ "ala": 5343,
+ "inger": 5344,
+ "▁Since": 5345,
+ "▁experi": 5346,
+ "▁shall": 5347,
+ "▁star": 5348,
+ "non": 5349,
+ "▁gun": 5350,
+ "▁Bel": 5351,
+ "▁obj": 5352,
+ "ares": 5353,
+ "rs": 5354,
+ "▁weeks": 5355,
+ "nen": 5356,
+ "▁Stre": 5357,
+ "oring": 5358,
+ "▁î": 5359,
+ "▁serious": 5360,
+ "times": 5361,
+ "▁House": 5362,
+ "▁roll": 5363,
+ "▁register": 5364,
+ "▁module": 5365,
+ "▁applic": 5366,
+ "IR": 5367,
+ "▁cook": 5368,
+ "aux": 5369,
+ "▁save": 5370,
+ "▁Cr": 5371,
+ ",\r": 5372,
+ "▁states": 5373,
+ "▁empty": 5374,
+ "▁autom": 5375,
+ "figure": 5376,
+ "iance": 5377,
+ "▁happy": 5378,
+ "▁fn": 5379,
+ "▁jud": 5380,
+ "▁hat": 5381,
+ "ACK": 5382,
+ "▁Fe": 5383,
+ "$-": 5384,
+ "ivil": 5385,
+ "oted": 5386,
+ "▁sizeof": 5387,
+ "▁situation": 5388,
+ "▁lives": 5389,
+ "▁feeling": 5390,
+ "▁risk": 5391,
+ "▁January": 5392,
+ "▁Object": 5393,
+ "▁recomm": 5394,
+ "▁вы": 5395,
+ "▁potential": 5396,
+ "eah": 5397,
+ "▁complex": 5398,
+ "printf": 5399,
+ "istance": 5400,
+ "irth": 5401,
+ "lik": 5402,
+ "aste": 5403,
+ "▁whose": 5404,
+ "Arg": 5405,
+ "▁modern": 5406,
+ "iones": 5407,
+ "▁че": 5408,
+ "▁sett": 5409,
+ "▁Mag": 5410,
+ "ae": 5411,
+ "▁condition": 5412,
+ "Length": 5413,
+ "▁fit": 5414,
+ "ounds": 5415,
+ "▁changed": 5416,
+ "▁guy": 5417,
+ "filter": 5418,
+ "atever": 5419,
+ "éd": 5420,
+ "remove": 5421,
+ "▁hop": 5422,
+ "▁Out": 5423,
+ "▁Rich": 5424,
+ "child": 5425,
+ "▁included": 5426,
+ "$\\": 5427,
+ "▁Tom": 5428,
+ "eline": 5429,
+ "▁sometimes": 5430,
+ "▁drink": 5431,
+ "▁quant": 5432,
+ "▁please": 5433,
+ "▁Int": 5434,
+ "rief": 5435,
+ "▁exactly": 5436,
+ "cing": 5437,
+ "▁allowed": 5438,
+ "build": 5439,
+ "▁beautiful": 5440,
+ "▁Well": 5441,
+ "▁looks": 5442,
+ "▁ü": 5443,
+ "▁chance": 5444,
+ "▁wrote": 5445,
+ "▁nor": 5446,
+ "▁failed": 5447,
+ "Met": 5448,
+ "▁prior": 5449,
+ "▁hundred": 5450,
+ "ской": 5451,
+ "oria": 5452,
+ "▁cy": 5453,
+ "▁web": 5454,
+ "▁mess": 5455,
+ "leq": 5456,
+ "dy": 5457,
+ "tex": 5458,
+ "▁anim": 5459,
+ "atur": 5460,
+ "▁structure": 5461,
+ "option": 5462,
+ "▁actual": 5463,
+ "▁Franc": 5464,
+ "enced": 5465,
+ ".": 5466,
+ "▁flow": 5467,
+ "▁Afr": 5468,
+ "det": 5469,
+ "▁Ke": 5470,
+ "ety": 5471,
+ "ский": 5472,
+ "▁stuff": 5473,
+ "itter": 5474,
+ "▁args": 5475,
+ "▁album": 5476,
+ "▁]": 5477,
+ "ugin": 5478,
+ "SU": 5479,
+ "Per": 5480,
+ "▁circ": 5481,
+ "▁correct": 5482,
+ "▁lines": 5483,
+ "▁completely": 5484,
+ "known": 5485,
+ "▁tree": 5486,
+ "root": 5487,
+ "▁Japan": 5488,
+ "oles": 5489,
+ "endo": 5490,
+ "▁location": 5491,
+ "▁Х": 5492,
+ "▁mid": 5493,
+ "aling": 5494,
+ "GL": 5495,
+ "iano": 5496,
+ "▁{}": 5497,
+ "lang": 5498,
+ "▁equip": 5499,
+ "ERROR": 5500,
+ "▁memory": 5501,
+ "▁(\"": 5502,
+ "▁nature": 5503,
+ "google": 5504,
+ "abs": 5505,
+ "BC": 5506,
+ "▁gets": 5507,
+ "Command": 5508,
+ "TER": 5509,
+ "aled": 5510,
+ "cp": 5511,
+ "▁purch": 5512,
+ "▁Den": 5513,
+ "▁herself": 5514,
+ "▁Ir": 5515,
+ "▁sie": 5516,
+ "gar": 5517,
+ "Ap": 5518,
+ "▁nel": 5519,
+ "ota": 5520,
+ ")]": 5521,
+ "cor": 5522,
+ "acht": 5523,
+ "(*": 5524,
+ "irtual": 5525,
+ "▁police": 5526,
+ "▁skin": 5527,
+ "ship": 5528,
+ "efined": 5529,
+ "aughter": 5530,
+ "inding": 5531,
+ "▁Sl": 5532,
+ "▁influ": 5533,
+ "▁mount": 5534,
+ "▁az": 5535,
+ "▁wood": 5536,
+ "otes": 5537,
+ "ega": 5538,
+ "▁according": 5539,
+ "▁namespace": 5540,
+ "Delta": 5541,
+ "stant": 5542,
+ "▁published": 5543,
+ "aker": 5544,
+ "▁Black": 5545,
+ "ln": 5546,
+ "▁industry": 5547,
+ "SON": 5548,
+ "Rep": 5549,
+ "▁choice": 5550,
+ "▁inn": 5551,
+ "kl": 5552,
+ "▁pal": 5553,
+ "▁aud": 5554,
+ "▁standard": 5555,
+ "▁knowledge": 5556,
+ "**,": 5557,
+ "▁Frank": 5558,
+ "sq": 5559,
+ "Output": 5560,
+ "▁för": 5561,
+ "Valid": 5562,
+ "ugh": 5563,
+ "▁books": 5564,
+ "▁James": 5565,
+ "ko": 5566,
+ "▁companies": 5567,
+ "anning": 5568,
+ "▁vict": 5569,
+ "▁repl": 5570,
+ "▁sche": 5571,
+ "▁happen": 5572,
+ "fty": 5573,
+ "acity": 5574,
+ "ira": 5575,
+ "▁implement": 5576,
+ "ского": 5577,
+ "number": 5578,
+ "SH": 5579,
+ "iro": 5580,
+ "▁fear": 5581,
+ "▁touch": 5582,
+ "▁cast": 5583,
+ "ASS": 5584,
+ "▁consist": 5585,
+ "Task": 5586,
+ "▁sig": 5587,
+ "ба": 5588,
+ "igation": 5589,
+ "▁Most": 5590,
+ "▁Der": 5591,
+ "}(\\": 5592,
+ ":\"": 5593,
+ "▁Fig": 5594,
+ "ali": 5595,
+ "iner": 5596,
+ "'),": 5597,
+ "▁Coun": 5598,
+ "(_": 5599,
+ "▁distributed": 5600,
+ "NAME": 5601,
+ "▁mur": 5602,
+ "▁career": 5603,
+ "~~": 5604,
+ "pers": 5605,
+ "aries": 5606,
+ "enses": 5607,
+ "▁Also": 5608,
+ "Version": 5609,
+ "▁unique": 5610,
+ "▁France": 5611,
+ "BA": 5612,
+ "ky": 5613,
+ "▁Febru": 5614,
+ "▁died": 5615,
+ "omega": 5616,
+ "▁Form": 5617,
+ "▁width": 5618,
+ "tocol": 5619,
+ "▁lie": 5620,
+ "She": 5621,
+ "ém": 5622,
+ "▁straight": 5623,
+ "▁nach": 5624,
+ "▁stood": 5625,
+ "olds": 5626,
+ "▁goes": 5627,
+ "cell": 5628,
+ "▁till": 5629,
+ "LI": 5630,
+ "draw": 5631,
+ "▁satisf": 5632,
+ "▁reading": 5633,
+ "ATION": 5634,
+ "▁Are": 5635,
+ "▁Ac": 5636,
+ ")*": 5637,
+ "▁additional": 5638,
+ "wood": 5639,
+ "cil": 5640,
+ "пу": 5641,
+ "ULT": 5642,
+ "▁bill": 5643,
+ "mas": 5644,
+ "ania": 5645,
+ "су": 5646,
+ "anz": 5647,
+ "height": 5648,
+ "jo": 5649,
+ "▁dos": 5650,
+ "\\\"": 5651,
+ "▁/>": 5652,
+ "▁production": 5653,
+ "iger": 5654,
+ "▁ст": 5655,
+ "show": 5656,
+ "▁population": 5657,
+ "▁park": 5658,
+ "▁Ze": 5659,
+ "▁necessary": 5660,
+ "▁trust": 5661,
+ "▁shown": 5662,
+ "module": 5663,
+ "GE": 5664,
+ "▁lay": 5665,
+ "▁announ": 5666,
+ "▁className": 5667,
+ "▁calcul": 5668,
+ "Function": 5669,
+ "▁Sal": 5670,
+ "OK": 5671,
+ "TP": 5672,
+ "▁entry": 5673,
+ "▁Stud": 5674,
+ "▁items": 5675,
+ "▁security": 5676,
+ "Entry": 5677,
+ "float": 5678,
+ "ls": 5679,
+ "ibly": 5680,
+ "▁contribut": 5681,
+ "▁Check": 5682,
+ "MD": 5683,
+ "▁improve": 5684,
+ "Part": 5685,
+ "▁systems": 5686,
+ "Bl": 5687,
+ "▁policy": 5688,
+ "▁screen": 5689,
+ "▁Any": 5690,
+ "▁opened": 5691,
+ "alloc": 5692,
+ "▁December": 5693,
+ "▁É": 5694,
+ "▁email": 5695,
+ "ader": 5696,
+ "=>": 5697,
+ "▁Hen": 5698,
+ "▁info": 5699,
+ "▁float": 5700,
+ "▁switch": 5701,
+ "ран": 5702,
+ "urance": 5703,
+ "▁assum": 5704,
+ "ustr": 5705,
+ "▁groups": 5706,
+ "▁Read": 5707,
+ "▁wat": 5708,
+ "Sp": 5709,
+ "вер": 5710,
+ "RAN": 5711,
+ "hib": 5712,
+ "ALL": 5713,
+ "▁hus": 5714,
+ "Spec": 5715,
+ "\"))": 5716,
+ "▁French": 5717,
+ "▁Class": 5718,
+ "▁president": 5719,
+ "▁definit": 5720,
+ "▁Nor": 5721,
+ "▁Thom": 5722,
+ "aign": 5723,
+ "Width": 5724,
+ "Do": 5725,
+ "▁{@": 5726,
+ "agon": 5727,
+ "▁Lu": 5728,
+ "▁followed": 5729,
+ "MM": 5730,
+ "asons": 5731,
+ "tmp": 5732,
+ "▁throws": 5733,
+ "ITY": 5734,
+ "ном": 5735,
+ "▁fair": 5736,
+ "▁pen": 5737,
+ "ég": 5738,
+ "▁interface": 5739,
+ "▁saf": 5740,
+ "oon": 5741,
+ "Back": 5742,
+ "▁speed": 5743,
+ "▁extends": 5744,
+ "empty": 5745,
+ "▁пере": 5746,
+ "▁proper": 5747,
+ "▁driv": 5748,
+ "фи": 5749,
+ "▁center": 5750,
+ "header": 5751,
+ "▁})": 5752,
+ "wa": 5753,
+ "▁middle": 5754,
+ "▁choose": 5755,
+ "▁Stad": 5756,
+ "SO": 5757,
+ "Factory": 5758,
+ "Dev": 5759,
+ "icles": 5760,
+ "▁application": 5761,
+ "▁models": 5762,
+ "pite": 5763,
+ "cap": 5764,
+ "xi": 5765,
+ "ospital": 5766,
+ "▁dream": 5767,
+ "END": 5768,
+ "▁contract": 5769,
+ "icrosoft": 5770,
+ "▁thous": 5771,
+ "izes": 5772,
+ "▁да": 5773,
+ "▁CO": 5774,
+ "▁direction": 5775,
+ "▁``": 5776,
+ "▁drive": 5777,
+ "Max": 5778,
+ "cia": 5779,
+ "▁continu": 5780,
+ "▁Alex": 5781,
+ "▁gold": 5782,
+ "▁prep": 5783,
+ "▁origin": 5784,
+ "▁rap": 5785,
+ "Op": 5786,
+ "ously": 5787,
+ "▁areas": 5788,
+ "PORT": 5789,
+ "она": 5790,
+ "▁safe": 5791,
+ "▁professional": 5792,
+ "apache": 5793,
+ "▁temper": 5794,
+ "sz": 5795,
+ "▁unit": 5796,
+ "▁cop": 5797,
+ "eqn": 5798,
+ "Listener": 5799,
+ "▁format": 5800,
+ "select": 5801,
+ "▁comfort": 5802,
+ "▁meant": 5803,
+ "iday": 5804,
+ "eme": 5805,
+ "▁active": 5806,
+ "▁note": 5807,
+ "▁Mil": 5808,
+ "only": 5809,
+ "▁<=": 5810,
+ "▁neigh": 5811,
+ "ao": 5812,
+ "▁blue": 5813,
+ "▁TV": 5814,
+ "Child": 5815,
+ "▁reached": 5816,
+ "Address": 5817,
+ "ств": 5818,
+ "▁closed": 5819,
+ "inder": 5820,
+ "olo": 5821,
+ "▁alt": 5822,
+ "▁adm": 5823,
+ "Format": 5824,
+ "UI": 5825,
+ "▁Ham": 5826,
+ "▁frequ": 5827,
+ "▁independ": 5828,
+ "▁easily": 5829,
+ "▁Land": 5830,
+ "▁tor": 5831,
+ "ography": 5832,
+ "infty": 5833,
+ "▁Work": 5834,
+ "iven": 5835,
+ "▁County": 5836,
+ "▁src": 5837,
+ "}$,": 5838,
+ "parse": 5839,
+ "CD": 5840,
+ "▁Cour": 5841,
+ "▁fol": 5842,
+ "Entity": 5843,
+ "pgf": 5844,
+ "▁China": 5845,
+ "▁Sub": 5846,
+ "hood": 5847,
+ "▁fields": 5848,
+ "▁yes": 5849,
+ "rend": 5850,
+ "▁towards": 5851,
+ "▁staff": 5852,
+ "▁Air": 5853,
+ "▁station": 5854,
+ "atives": 5855,
+ "▁impact": 5856,
+ "вы": 5857,
+ "▁directly": 5858,
+ "issions": 5859,
+ "iva": 5860,
+ "|\\": 5861,
+ "Ptr": 5862,
+ "▁Sant": 5863,
+ "Pol": 5864,
+ "▁progress": 5865,
+ "itar": 5866,
+ "▁parts": 5867,
+ "▁plant": 5868,
+ "▁absolut": 5869,
+ "▁guess": 5870,
+ "eqref": 5871,
+ "▁tim": 5872,
+ "▁Lou": 5873,
+ "▁cool": 5874,
+ "alu": 5875,
+ "▁mouth": 5876,
+ "них": 5877,
+ "▁height": 5878,
+ "gest": 5879,
+ "▁Post": 5880,
+ "▁board": 5881,
+ "▁tit": 5882,
+ "▁hour": 5883,
+ "▁server": 5884,
+ "▁players": 5885,
+ "rier": 5886,
+ "Link": 5887,
+ "▁President": 5888,
+ "](": 5889,
+ "▁construct": 5890,
+ "handle": 5891,
+ "}$.": 5892,
+ "rying": 5893,
+ "▁shop": 5894,
+ "iana": 5895,
+ "exp": 5896,
+ "Helper": 5897,
+ "Offset": 5898,
+ "aches": 5899,
+ "▁connection": 5900,
+ "▁difference": 5901,
+ "service": 5902,
+ "▁gas": 5903,
+ "▁priv": 5904,
+ "▁univers": 5905,
+ "▁wish": 5906,
+ "Rem": 5907,
+ "Url": 5908,
+ "geb": 5909,
+ "So": 5910,
+ "ensions": 5911,
+ "Module": 5912,
+ "SIZE": 5913,
+ "▁prem": 5914,
+ "window": 5915,
+ "▁dies": 5916,
+ "del": 5917,
+ "▁row": 5918,
+ "▁average": 5919,
+ "xim": 5920,
+ "▁pu": 5921,
+ "anç": 5922,
+ "Det": 5923,
+ "ker": 5924,
+ "ya": 5925,
+ "▁Det": 5926,
+ "▁på": 5927,
+ "▁named": 5928,
+ "▁decision": 5929,
+ "win": 5930,
+ "▁George": 5931,
+ "arily": 5932,
+ "▁solution": 5933,
+ "▁multiple": 5934,
+ "ategy": 5935,
+ "▁learning": 5936,
+ "▁secret": 5937,
+ "DO": 5938,
+ "▁nice": 5939,
+ "////////////////": 5940,
+ "Su": 5941,
+ "itation": 5942,
+ "▁join": 5943,
+ "▁elements": 5944,
+ "▁emer": 5945,
+ "tilde": 5946,
+ "▁dep": 5947,
+ "▁shot": 5948,
+ "▁platform": 5949,
+ "othing": 5950,
+ "My": 5951,
+ "edia": 5952,
+ "oms": 5953,
+ "aily": 5954,
+ "([": 5955,
+ "▁dress": 5956,
+ "▁official": 5957,
+ "estern": 5958,
+ "▁discover": 5959,
+ "▁mi": 5960,
+ "ные": 5961,
+ "CA": 5962,
+ "oding": 5963,
+ "▁Found": 5964,
+ "▁affect": 5965,
+ "Vis": 5966,
+ "stract": 5967,
+ "iced": 5968,
+ "debug": 5969,
+ "▁related": 5970,
+ "▁spect": 5971,
+ "ushed": 5972,
+ "сько": 5973,
+ "▁bank": 5974,
+ "▁cele": 5975,
+ "AND": 5976,
+ "olf": 5977,
+ "ем": 5978,
+ "▁fill": 5979,
+ "▁gives": 5980,
+ "▁бу": 5981,
+ "aron": 5982,
+ "▁Jes": 5983,
+ "REG": 5984,
+ "▁sudd": 5985,
+ "dated": 5986,
+ "vi": 5987,
+ "▁gi": 5988,
+ "send": 5989,
+ "cpp": 5990,
+ "▁spent": 5991,
+ "ande": 5992,
+ "▁operation": 5993,
+ "process": 5994,
+ "▁inform": 5995,
+ "▁Free": 5996,
+ "yond": 5997,
+ "▁perhaps": 5998,
+ "▁surv": 5999,
+ "▁Loc": 6000,
+ "▁concl": 6001,
+ "▁раз": 6002,
+ "▁Over": 6003,
+ "hol": 6004,
+ "raz": 6005,
+ "Write": 6006,
+ "▁giving": 6007,
+ "rd": 6008,
+ "instance": 6009,
+ "▁released": 6010,
+ "▁Ro": 6011,
+ "RA": 6012,
+ "▁practice": 6013,
+ "▁graph": 6014,
+ "▁increase": 6015,
+ "▁figure": 6016,
+ "Filter": 6017,
+ "HECK": 6018,
+ "idx": 6019,
+ "▁glass": 6020,
+ "ski": 6021,
+ "comes": 6022,
+ "▁cat": 6023,
+ "▁cold": 6024,
+ "goto": 6025,
+ "ufact": 6026,
+ "▁Copyright": 6027,
+ "}}\\": 6028,
+ "▁streng": 6029,
+ "▁dir": 6030,
+ "token": 6031,
+ "▁occur": 6032,
+ "arlier": 6033,
+ "▁measure": 6034,
+ "▁sec": 6035,
+ "▁más": 6036,
+ "▁Net": 6037,
+ "▁argument": 6038,
+ "▁sou": 6039,
+ "▁moving": 6040,
+ "▁prefer": 6041,
+ "mask": 6042,
+ "<<": 6043,
+ "▁breath": 6044,
+ "▁physical": 6045,
+ "▁positive": 6046,
+ "▁sor": 6047,
+ "▁depart": 6048,
+ "▁remove": 6049,
+ "▁kit": 6050,
+ "▁meeting": 6051,
+ "▁Data": 6052,
+ "ograf": 6053,
+ "actions": 6054,
+ "▁parameters": 6055,
+ "▁Att": 6056,
+ "esch": 6057,
+ "▁involved": 6058,
+ "ät": 6059,
+ "LL": 6060,
+ "Bar": 6061,
+ "▁си": 6062,
+ "ech": 6063,
+ "GET": 6064,
+ "▁prevent": 6065,
+ "▁beyond": 6066,
+ "▁Other": 6067,
+ "än": 6068,
+ "byte": 6069,
+ "▁sudden": 6070,
+ "olve": 6071,
+ "▁но": 6072,
+ "LOG": 6073,
+ "unit": 6074,
+ "▁truth": 6075,
+ "rat": 6076,
+ "SD": 6077,
+ "▁eat": 6078,
+ "▁Mad": 6079,
+ "▁provides": 6080,
+ "▁session": 6081,
+ "Dele": 6082,
+ "▁convers": 6083,
+ "center": 6084,
+ "▁continued": 6085,
+ "otion": 6086,
+ "cache": 6087,
+ "display": 6088,
+ "▁protect": 6089,
+ "ams": 6090,
+ "▁pow": 6091,
+ "CTION": 6092,
+ "▁Mac": 6093,
+ "mo": 6094,
+ "ха": 6095,
+ "▁distance": 6096,
+ "▁Time": 6097,
+ "gi": 6098,
+ "▁sequ": 6099,
+ "Target": 6100,
+ "сле": 6101,
+ "Server": 6102,
+ "▁wide": 6103,
+ "close": 6104,
+ "▁cru": 6105,
+ "Ext": 6106,
+ "▁select": 6107,
+ "▁pattern": 6108,
+ "\"));": 6109,
+ "Provider": 6110,
+ "URL": 6111,
+ "▁green": 6112,
+ "▁waiting": 6113,
+ "proto": 6114,
+ "▁immediately": 6115,
+ "common": 6116,
+ "azione": 6117,
+ "river": 6118,
+ "▁sen": 6119,
+ "▁!==": 6120,
+ "▁February": 6121,
+ "urb": 6122,
+ "▁Sen": 6123,
+ "dest": 6124,
+ "": 6125,
+ "▁edge": 6126,
+ "▁mais": 6127,
+ "gorith": 6128,
+ "cpu": 6129,
+ "▁education": 6130,
+ "▁associated": 6131,
+ "None": 6132,
+ "hi": 6133,
+ "▁poor": 6134,
+ "sem": 6135,
+ "▁Wil": 6136,
+ "▁bud": 6137,
+ "▁auch": 6138,
+ "eller": 6139,
+ "▁Life": 6140,
+ "▁files": 6141,
+ "▁leading": 6142,
+ "▁obtain": 6143,
+ "▁Jul": 6144,
+ "atory": 6145,
+ "гу": 6146,
+ "itable": 6147,
+ "▁onto": 6148,
+ "▁born": 6149,
+ "orem": 6150,
+ "▁Street": 6151,
+ "▁maint": 6152,
+ "Params": 6153,
+ "rip": 6154,
+ "▁ST": 6155,
+ "uv": 6156,
+ "main": 6157,
+ "▁▁▁▁▁▁▁": 6158,
+ "▁recent": 6159,
+ "Web": 6160,
+ "ova": 6161,
+ "ца": 6162,
+ "aise": 6163,
+ "yles": 6164,
+ "▁described": 6165,
+ "▁beginning": 6166,
+ "▁Day": 6167,
+ "▁Vol": 6168,
+ "▁huge": 6169,
+ "Has": 6170,
+ "ancy": 6171,
+ "Header": 6172,
+ "▁aren": 6173,
+ "ван": 6174,
+ "▁ensure": 6175,
+ "▁pet": 6176,
+ "mult": 6177,
+ "▁Like": 6178,
+ "▁management": 6179,
+ "PS": 6180,
+ "while": 6181,
+ "▁background": 6182,
+ "ounter": 6183,
+ "bool": 6184,
+ "FC": 6185,
+ "Num": 6186,
+ "RL": 6187,
+ "▁excl": 6188,
+ "▁eye": 6189,
+ "img": 6190,
+ "▁rom": 6191,
+ "▁Hel": 6192,
+ "Option": 6193,
+ "▁stopped": 6194,
+ "▁thread": 6195,
+ "totype": 6196,
+ ")))": 6197,
+ "▁stage": 6198,
+ "▁über": 6199,
+ "▁although": 6200,
+ "Types": 6201,
+ "▁Oh": 6202,
+ "▁eight": 6203,
+ "▁description": 6204,
+ "''": 6205,
+ "ön": 6206,
+ "▁surface": 6207,
+ "▁International": 6208,
+ "▁charg": 6209,
+ "▁collection": 6210,
+ "▁users": 6211,
+ "▁obvious": 6212,
+ "▁century": 6213,
+ "icks": 6214,
+ "▁article": 6215,
+ "▁\"\\": 6216,
+ "dim": 6217,
+ "▁sin": 6218,
+ "enge": 6219,
+ "Control": 6220,
+ "▁commit": 6221,
+ "ensity": 6222,
+ "▁tra": 6223,
+ "criptor": 6224,
+ "▁NOT": 6225,
+ "well": 6226,
+ "▁Michael": 6227,
+ "▁nod": 6228,
+ "▁mort": 6229,
+ "ivo": 6230,
+ "isation": 6231,
+ "▁Po": 6232,
+ "▁Paris": 6233,
+ "▁administr": 6234,
+ "burg": 6235,
+ "cdot": 6236,
+ "▁military": 6237,
+ "▁Best": 6238,
+ "▁Ка": 6239,
+ "INE": 6240,
+ "▁throughout": 6241,
+ "Sl": 6242,
+ "▁impl": 6243,
+ "control": 6244,
+ "▁Ч": 6245,
+ "▁uit": 6246,
+ "▁unsigned": 6247,
+ "▁Mary": 6248,
+ "Char": 6249,
+ "мі": 6250,
+ "▁threat": 6251,
+ "▁court": 6252,
+ "ville": 6253,
+ "▁ш": 6254,
+ "▁Cam": 6255,
+ ".\r": 6256,
+ "▁currently": 6257,
+ "rot": 6258,
+ "▁Date": 6259,
+ "▁shit": 6260,
+ "▁${\\": 6261,
+ "unn": 6262,
+ "Us": 6263,
+ "▁buffer": 6264,
+ "▁sont": 6265,
+ "▁letter": 6266,
+ "inated": 6267,
+ "Change": 6268,
+ "▁href": 6269,
+ "▁lack": 6270,
+ "▁oil": 6271,
+ "▁Cons": 6272,
+ "▁Jer": 6273,
+ "BUG": 6274,
+ "iforn": 6275,
+ "▁properties": 6276,
+ "▁random": 6277,
+ "▁brother": 6278,
+ "▁piece": 6279,
+ "бу": 6280,
+ "istics": 6281,
+ "▁technology": 6282,
+ "global": 6283,
+ "▁transform": 6284,
+ "erd": 6285,
+ "▁Because": 6286,
+ "PECT": 6287,
+ "pret": 6288,
+ "▁году": 6289,
+ "▁Met": 6290,
+ "▁psy": 6291,
+ "▁од": 6292,
+ "▁god": 6293,
+ "▁Del": 6294,
+ "based": 6295,
+ "▁voor": 6296,
+ "▁Call": 6297,
+ "SA": 6298,
+ "▁filter": 6299,
+ "▁includes": 6300,
+ "olutions": 6301,
+ "fd": 6302,
+ "▁wind": 6303,
+ "▁бо": 6304,
+ "▁ability": 6305,
+ "card": 6306,
+ "▁numer": 6307,
+ "address": 6308,
+ "▁goal": 6309,
+ "ashington": 6310,
+ "▁slight": 6311,
+ "aba": 6312,
+ "▁Log": 6313,
+ "Settings": 6314,
+ "adow": 6315,
+ "▁pi": 6316,
+ "iring": 6317,
+ "FT": 6318,
+ "▁numbers": 6319,
+ "conf": 6320,
+ "task": 6321,
+ "▁în": 6322,
+ "ты": 6323,
+ "▁receive": 6324,
+ "▁root": 6325,
+ "▁India": 6326,
+ "patch": 6327,
+ "él": 6328,
+ "▁summer": 6329,
+ "▁methods": 6330,
+ "▁places": 6331,
+ "▁Ма": 6332,
+ "▁capital": 6333,
+ "▁evidence": 6334,
+ "▁German": 6335,
+ "\\,": 6336,
+ "DA": 6337,
+ "ecute": 6338,
+ "column": 6339,
+ "▁functions": 6340,
+ "▁counter": 6341,
+ "▁arms": 6342,
+ "▁feed": 6343,
+ "vey": 6344,
+ "hent": 6345,
+ "MAX": 6346,
+ "▁acqu": 6347,
+ "▁apply": 6348,
+ "▁husband": 6349,
+ "▁killed": 6350,
+ "▁Spec": 6351,
+ "entity": 6352,
+ "▁earlier": 6353,
+ "▁Miss": 6354,
+ "▁setting": 6355,
+ "itect": 6356,
+ "▁ded": 6357,
+ "Row": 6358,
+ "▁ran": 6359,
+ "▁Yes": 6360,
+ "▁financial": 6361,
+ "session": 6362,
+ "lear": 6363,
+ "ishing": 6364,
+ "▁nearly": 6365,
+ "▁dur": 6366,
+ "▁machine": 6367,
+ "xff": 6368,
+ "bro": 6369,
+ "▁symbol": 6370,
+ "lands": 6371,
+ "Acc": 6372,
+ "di": 6373,
+ "▁Robert": 6374,
+ "prop": 6375,
+ "urity": 6376,
+ "▁#####": 6377,
+ "▁walked": 6378,
+ "▁international": 6379,
+ "▁Е": 6380,
+ "Yes": 6381,
+ "▁release": 6382,
+ "▁starting": 6383,
+ "static": 6384,
+ "▁bei": 6385,
+ "allow": 6386,
+ "▁People": 6387,
+ "ez": 6388,
+ "▁parameter": 6389,
+ "Cache": 6390,
+ "▁$$": 6391,
+ "ampions": 6392,
+ "▁Mer": 6393,
+ "▁kom": 6394,
+ "leted": 6395,
+ "ois": 6396,
+ "▁Open": 6397,
+ "types": 6398,
+ "▁fue": 6399,
+ "acters": 6400,
+ "▁reference": 6401,
+ "Equals": 6402,
+ "▁aware": 6403,
+ "▁hol": 6404,
+ "▁demand": 6405,
+ "lor": 6406,
+ "▁veh": 6407,
+ "▁notice": 6408,
+ "▁component": 6409,
+ "fn": 6410,
+ "▁analysis": 6411,
+ "match": 6412,
+ "▁effective": 6413,
+ "product": 6414,
+ "ник": 6415,
+ "▁legal": 6416,
+ "ей": 6417,
+ "semb": 6418,
+ "▁located": 6419,
+ "▁су": 6420,
+ "QL": 6421,
+ "inct": 6422,
+ "eto": 6423,
+ "Draw": 6424,
+ "▁scale": 6425,
+ "ров": 6426,
+ "▁wants": 6427,
+ "How": 6428,
+ "▁wel": 6429,
+ "isions": 6430,
+ "▁deliver": 6431,
+ "under": 6432,
+ "▁deb": 6433,
+ "▁ju": 6434,
+ "values": 6435,
+ "▁sister": 6436,
+ "ков": 6437,
+ "▁Create": 6438,
+ "▁Inc": 6439,
+ "▁aux": 6440,
+ "▁White": 6441,
+ "Menu": 6442,
+ "aud": 6443,
+ "resource": 6444,
+ "▁cab": 6445,
+ "▁lif": 6446,
+ "▁culture": 6447,
+ "iche": 6448,
+ "▁whatever": 6449,
+ "▁designed": 6450,
+ "▁repe": 6451,
+ "▁Mont": 6452,
+ "▁charge": 6453,
+ "Names": 6454,
+ "▁insp": 6455,
+ "▁customers": 6456,
+ "osa": 6457,
+ "▁daughter": 6458,
+ "▁East": 6459,
+ "EQ": 6460,
+ "▁opin": 6461,
+ "▁Fre": 6462,
+ "▁seek": 6463,
+ "▁push": 6464,
+ "▁nav": 6465,
+ "▁burn": 6466,
+ "arden": 6467,
+ "hash": 6468,
+ "▁opportunity": 6469,
+ "▁Mat": 6470,
+ "oyal": 6471,
+ "▁pun": 6472,
+ "scale": 6473,
+ "ynamic": 6474,
+ "▁Type": 6475,
+ "iling": 6476,
+ "▁query": 6477,
+ "▁mist": 6478,
+ "ror": 6479,
+ "force": 6480,
+ "▁Once": 6481,
+ "▁medical": 6482,
+ "lie": 6483,
+ "▁student": 6484,
+ "ederal": 6485,
+ "▁lov": 6486,
+ "iform": 6487,
+ "▁altern": 6488,
+ "bin": 6489,
+ "oder": 6490,
+ "▁returns": 6491,
+ "register": 6492,
+ "uts": 6493,
+ "CI": 6494,
+ "▁Tor": 6495,
+ "CR": 6496,
+ "▁Los": 6497,
+ "amily": 6498,
+ "aire": 6499,
+ "++;": 6500,
+ "Controller": 6501,
+ "wide": 6502,
+ "xx": 6503,
+ "rowser": 6504,
+ "▁Book": 6505,
+ "Container": 6506,
+ "pload": 6507,
+ "▁Ev": 6508,
+ "▁tal": 6509,
+ "▁theory": 6510,
+ "eqnarray": 6511,
+ "бе": 6512,
+ "▁reported": 6513,
+ "▁meaning": 6514,
+ "▁sy": 6515,
+ "ribe": 6516,
+ "icate": 6517,
+ "hold": 6518,
+ "▁offers": 6519,
+ "▁templ": 6520,
+ "css": 6521,
+ "▁picture": 6522,
+ "▁async": 6523,
+ "▁stock": 6524,
+ "▁internal": 6525,
+ "ti": 6526,
+ "BO": 6527,
+ "Ver": 6528,
+ "спо": 6529,
+ "▁demon": 6530,
+ "▁laugh": 6531,
+ "▁End": 6532,
+ "▁kon": 6533,
+ "▁ideas": 6534,
+ "▁candid": 6535,
+ "Mem": 6536,
+ "izz": 6537,
+ "refix": 6538,
+ "▁AND": 6539,
+ "egen": 6540,
+ "El": 6541,
+ "▁campaign": 6542,
+ "Http": 6543,
+ "▁Rob": 6544,
+ "ді": 6545,
+ "▁bul": 6546,
+ "▁Ко": 6547,
+ "▁countries": 6548,
+ "».": 6549,
+ "▁expression": 6550,
+ "▁England": 6551,
+ "sf": 6552,
+ "▁certainly": 6553,
+ "agen": 6554,
+ "▁ча": 6555,
+ "▁ANY": 6556,
+ "▁connect": 6557,
+ "FE": 6558,
+ "▁android": 6559,
+ "▁Gold": 6560,
+ "▁oppos": 6561,
+ "overn": 6562,
+ "▁Commun": 6563,
+ ",_": 6564,
+ "asion": 6565,
+ "La": 6566,
+ "▁firm": 6567,
+ "▁Although": 6568,
+ "▁Good": 6569,
+ "▁Law": 6570,
+ "erve": 6571,
+ "▁brand": 6572,
+ "Min": 6573,
+ "fill": 6574,
+ "'],": 6575,
+ "▁Jew": 6576,
+ "iler": 6577,
+ "ingle": 6578,
+ "ithub": 6579,
+ "▁Div": 6580,
+ "▁cert": 6581,
+ "Height": 6582,
+ "rael": 6583,
+ "There": 6584,
+ "itute": 6585,
+ "▁amaz": 6586,
+ "look": 6587,
+ "▁SE": 6588,
+ "▁jo": 6589,
+ "▁pulled": 6590,
+ "▁resources": 6591,
+ "▁Max": 6592,
+ "▁agreed": 6593,
+ "asy": 6594,
+ "▁treatment": 6595,
+ "\">": 6596,
+ "ман": 6597,
+ "▁Err": 6598,
+ "orig": 6599,
+ "cos": 6600,
+ "▁Maybe": 6601,
+ "otal": 6602,
+ "▁train": 6603,
+ "▁Service": 6604,
+ "▁ih": 6605,
+ "▁spirit": 6606,
+ "Comp": 6607,
+ "sqrt": 6608,
+ "▁broad": 6609,
+ "}[": 6610,
+ "▁shape": 6611,
+ "▁doc": 6612,
+ "how": 6613,
+ "▁tag": 6614,
+ "atalog": 6615,
+ "sd": 6616,
+ "▁meas": 6617,
+ "▁Ро": 6618,
+ "▁exception": 6619,
+ "▁Tw": 6620,
+ "▁interesting": 6621,
+ "ATA": 6622,
+ "▁Rel": 6623,
+ "ár": 6624,
+ "▁useful": 6625,
+ "useum": 6626,
+ "▁bottom": 6627,
+ "▁otherwise": 6628,
+ "▁agree": 6629,
+ "cht": 6630,
+ "then": 6631,
+ "▁significant": 6632,
+ "}/": 6633,
+ "▁channel": 6634,
+ "icial": 6635,
+ "тив": 6636,
+ "vare": 6637,
+ "▁enter": 6638,
+ "Eng": 6639,
+ "uj": 6640,
+ "URE": 6641,
+ "queue": 6642,
+ "ono": 6643,
+ "▁contains": 6644,
+ "MI": 6645,
+ "▁nation": 6646,
+ "▁rules": 6647,
+ "fol": 6648,
+ "▁pa": 6649,
+ "arp": 6650,
+ "▁quiet": 6651,
+ "▁thus": 6652,
+ "ipped": 6653,
+ "annot": 6654,
+ "udes": 6655,
+ "():": 6656,
+ "names": 6657,
+ "▁compos": 6658,
+ "▁inj": 6659,
+ "una": 6660,
+ "bind": 6661,
+ "▁fully": 6662,
+ "ras": 6663,
+ "Utils": 6664,
+ "anges": 6665,
+ "dule": 6666,
+ "▁Christian": 6667,
+ "▁reve": 6668,
+ "änd": 6669,
+ "▁collect": 6670,
+ "▁celebr": 6671,
+ "anda": 6672,
+ "ín": 6673,
+ "join": 6674,
+ "▁paid": 6675,
+ "Core": 6676,
+ "Ge": 6677,
+ ".$": 6678,
+ "▁fif": 6679,
+ "▁uma": 6680,
+ "▁~": 6681,
+ "ervices": 6682,
+ "▁recently": 6683,
+ "desc": 6684,
+ "▁heavy": 6685,
+ "▁rule": 6686,
+ "▁Please": 6687,
+ "psi": 6688,
+ "▁console": 6689,
+ "▁fort": 6690,
+ ".\\": 6691,
+ "▁Washington": 6692,
+ "▁gar": 6693,
+ "▁Group": 6694,
+ "▁interview": 6695,
+ "anned": 6696,
+ "sql": 6697,
+ "▁anc": 6698,
+ "ја": 6699,
+ "Pack": 6700,
+ "▁Club": 6701,
+ "▁mask": 6702,
+ "▁concept": 6703,
+ "▁['": 6704,
+ "▁selected": 6705,
+ "▁Use": 6706,
+ "▁ele": 6707,
+ "ears": 6708,
+ "▁race": 6709,
+ "hy": 6710,
+ "Om": 6711,
+ "▁steps": 6712,
+ "ila": 6713,
+ "ests": 6714,
+ "eds": 6715,
+ "▁street": 6716,
+ "ners": 6717,
+ "▁birth": 6718,
+ "pop": 6719,
+ "▁ли": 6720,
+ "MB": 6721,
+ "кра": 6722,
+ "cir": 6723,
+ "epsilon": 6724,
+ "▁constant": 6725,
+ "ques": 6726,
+ "adas": 6727,
+ "▁knows": 6728,
+ "▁Py": 6729,
+ "cles": 6730,
+ "▁cit": 6731,
+ "▁pair": 6732,
+ "inese": 6733,
+ "▁Peter": 6734,
+ "▁finished": 6735,
+ "▁master": 6736,
+ "▁twenty": 6737,
+ "▁fell": 6738,
+ "▁central": 6739,
+ "▁mes": 6740,
+ "rev": 6741,
+ "STAT": 6742,
+ "stat": 6743,
+ "▁allows": 6744,
+ "▁gro": 6745,
+ "Click": 6746,
+ "▁stories": 6747,
+ "Fe": 6748,
+ "år": 6749,
+ "▁baby": 6750,
+ "encia": 6751,
+ "▁einer": 6752,
+ "Are": 6753,
+ "ebug": 6754,
+ "store": 6755,
+ "\",\"": 6756,
+ "lam": 6757,
+ "▁sv": 6758,
+ "ции": 6759,
+ "NULL": 6760,
+ "▁Leg": 6761,
+ "▁movie": 6762,
+ "▁hous": 6763,
+ "▁learned": 6764,
+ "bon": 6765,
+ "▁transfer": 6766,
+ "ifornia": 6767,
+ "psilon": 6768,
+ "▁Soft": 6769,
+ "▁commer": 6770,
+ "▁hadn": 6771,
+ "▁Ein": 6772,
+ "▁Two": 6773,
+ "craft": 6774,
+ "Process": 6775,
+ "▁под": 6776,
+ "argin": 6777,
+ "▁estim": 6778,
+ "▁Mem": 6779,
+ "ika": 6780,
+ "▁Tod": 6781,
+ "duc": 6782,
+ "▁danger": 6783,
+ "rive": 6784,
+ "Don": 6785,
+ "▁Que": 6786,
+ "hal": 6787,
+ "▁mm": 6788,
+ "▁Sur": 6789,
+ "Order": 6790,
+ "▁distribution": 6791,
+ "fa": 6792,
+ "▁Many": 6793,
+ "plicit": 6794,
+ "Empty": 6795,
+ "Handle": 6796,
+ "▁token": 6797,
+ "▁epis": 6798,
+ "▁assist": 6799,
+ "▁purpose": 6800,
+ "▁ц": 6801,
+ "NU": 6802,
+ "iders": 6803,
+ "rate": 6804,
+ "They": 6805,
+ "Parameter": 6806,
+ "Dec": 6807,
+ "▁strugg": 6808,
+ "▁shoot": 6809,
+ "IV": 6810,
+ "▁Great": 6811,
+ "▁Sil": 6812,
+ "▁loved": 6813,
+ "▁click": 6814,
+ "▁reserv": 6815,
+ "▁ве": 6816,
+ "▁spread": 6817,
+ "▁og": 6818,
+ "▁${": 6819,
+ "▁miles": 6820,
+ "▁successful": 6821,
+ "oj": 6822,
+ "▁Direct": 6823,
+ "▁ax": 6824,
+ "▁growth": 6825,
+ "Work": 6826,
+ "▁church": 6827,
+ "Inst": 6828,
+ "ICE": 6829,
+ "sten": 6830,
+ "род": 6831,
+ "▁Center": 6832,
+ "ses": 6833,
+ "got": 6834,
+ "delete": 6835,
+ "▁Ma": 6836,
+ "%%": 6837,
+ "▁crow": 6838,
+ "DF": 6839,
+ "front": 6840,
+ "▁blog": 6841,
+ "▁computer": 6842,
+ "ная": 6843,
+ "▁mir": 6844,
+ "▁Super": 6845,
+ "','": 6846,
+ "▁multi": 6847,
+ "▁gru": 6848,
+ "▁Jo": 6849,
+ "▁Canada": 6850,
+ "▁Thomas": 6851,
+ "▁larger": 6852,
+ "▁compar": 6853,
+ "Current": 6854,
+ "that": 6855,
+ "▁drop": 6856,
+ "ент": 6857,
+ "▁Republic": 6858,
+ "▁dise": 6859,
+ "▁effects": 6860,
+ "▁girls": 6861,
+ "encies": 6862,
+ "ellig": 6863,
+ "▁Note": 6864,
+ "▁Associ": 6865,
+ "▁uses": 6866,
+ "elled": 6867,
+ "▁warm": 6868,
+ "thread": 6869,
+ "font": 6870,
+ "▁zum": 6871,
+ "▁follows": 6872,
+ "▁whom": 6873,
+ "TA": 6874,
+ "▁wild": 6875,
+ "▁AR": 6876,
+ "iable": 6877,
+ "▁True": 6878,
+ "Position": 6879,
+ "▁sell": 6880,
+ "cher": 6881,
+ "▁Bus": 6882,
+ "▁lean": 6883,
+ "ACE": 6884,
+ "▁served": 6885,
+ "hw": 6886,
+ "▁Cur": 6887,
+ "▁north": 6888,
+ "Dat": 6889,
+ "▁>>": 6890,
+ "command": 6891,
+ "atz": 6892,
+ "▁mal": 6893,
+ "став": 6894,
+ "▁Press": 6895,
+ "▁characters": 6896,
+ "▁zero": 6897,
+ "AGE": 6898,
+ "rapper": 6899,
+ "▁kitchen": 6900,
+ "aming": 6901,
+ "▁restr": 6902,
+ "XX": 6903,
+ "▁College": 6904,
+ "▁Array": 6905,
+ "▁fresh": 6906,
+ "▁shift": 6907,
+ "▁specified": 6908,
+ "plete": 6909,
+ "ITE": 6910,
+ "▁Camp": 6911,
+ "rial": 6912,
+ "cb": 6913,
+ "▁TH": 6914,
+ "IB": 6915,
+ "osen": 6916,
+ "▁ú": 6917,
+ "▁params": 6918,
+ "ignment": 6919,
+ "adding": 6920,
+ "▁degree": 6921,
+ "Local": 6922,
+ "Oh": 6923,
+ "▁zur": 6924,
+ "▁levels": 6925,
+ "CS": 6926,
+ "finished": 6927,
+ "Case": 6928,
+ "riage": 6929,
+ "Vector": 6930,
+ "▁sea": 6931,
+ "antic": 6932,
+ "▁League": 6933,
+ "▁therefore": 6934,
+ "One": 6935,
+ "Return": 6936,
+ "Access": 6937,
+ "vas": 6938,
+ "▁ос": 6939,
+ "▁rat": 6940,
+ "Big": 6941,
+ "▁behavior": 6942,
+ "kr": 6943,
+ "▁undefined": 6944,
+ "▁Es": 6945,
+ "▁appeared": 6946,
+ "eles": 6947,
+ "▁WAR": 6948,
+ "Stat": 6949,
+ "▁Google": 6950,
+ "▁credit": 6951,
+ "▁File": 6952,
+ "anging": 6953,
+ "house": 6954,
+ "romise": 6955,
+ "gent": 6956,
+ "▁habit": 6957,
+ "▁society": 6958,
+ "▁encour": 6959,
+ "▁paint": 6960,
+ "pet": 6961,
+ "▁UK": 6962,
+ "aws": 6963,
+ "onom": 6964,
+ "Gl": 6965,
+ "}_{\\": 6966,
+ "eless": 6967,
+ "emy": 6968,
+ "▁Cong": 6969,
+ "▁developed": 6970,
+ "▁images": 6971,
+ "▁ö": 6972,
+ "▁font": 6973,
+ "clear": 6974,
+ "gin": 6975,
+ "▁Lord": 6976,
+ "▁transport": 6977,
+ "▁::": 6978,
+ "▁cup": 6979,
+ "ulate": 6980,
+ "▁During": 6981,
+ "priv": 6982,
+ "▁extrem": 6983,
+ "▁Di": 6984,
+ "▁doubt": 6985,
+ "Py": 6986,
+ "ifying": 6987,
+ "split": 6988,
+ "ego": 6989,
+ "github": 6990,
+ "▁),": 6991,
+ "ROM": 6992,
+ "▁chair": 6993,
+ "▁trade": 6994,
+ "▁nicht": 6995,
+ "Top": 6996,
+ "Store": 6997,
+ "▁parte": 6998,
+ "project": 6999,
+ "nia": 7000,
+ "▁від": 7001,
+ "war": 7002,
+ "▁Prof": 7003,
+ "▁caught": 7004,
+ "Thread": 7005,
+ "ства": 7006,
+ "author": 7007,
+ "▁doll": 7008,
+ "▁harm": 7009,
+ "▁Gen": 7010,
+ "tree": 7011,
+ "etime": 7012,
+ "cfg": 7013,
+ "▁guys": 7014,
+ "▁California": 7015,
+ "▁Green": 7016,
+ "▁movement": 7017,
+ "iej": 7018,
+ "▁statement": 7019,
+ "▁seeing": 7020,
+ "▁haven": 7021,
+ "vention": 7022,
+ "SL": 7023,
+ "chedul": 7024,
+ "iert": 7025,
+ "▁primary": 7026,
+ "▁civil": 7027,
+ "rian": 7028,
+ "▁button": 7029,
+ "▁lived": 7030,
+ "Pass": 7031,
+ "sor": 7032,
+ "▁watching": 7033,
+ "▁skills": 7034,
+ "tee": 7035,
+ "Level": 7036,
+ "▁scient": 7037,
+ "hs": 7038,
+ "▁agre": 7039,
+ "cat": 7040,
+ "▁tend": 7041,
+ "▁Mill": 7042,
+ "▁Cap": 7043,
+ "ORD": 7044,
+ "gle": 7045,
+ "▁сво": 7046,
+ "»,": 7047,
+ "▁ahead": 7048,
+ "vest": 7049,
+ "▁Jose": 7050,
+ "ischer": 7051,
+ "și": 7052,
+ "▁leaving": 7053,
+ "▁для": 7054,
+ "▁south": 7055,
+ "▁consum": 7056,
+ "Range": 7057,
+ "▁activities": 7058,
+ "Sec": 7059,
+ "▁sales": 7060,
+ "▁fix": 7061,
+ "▁jed": 7062,
+ "rum": 7063,
+ "vector": 7064,
+ "▁spot": 7065,
+ "▁manufact": 7066,
+ "кт": 7067,
+ "orrow": 7068,
+ "sign": 7069,
+ "▁college": 7070,
+ "▁driver": 7071,
+ "▁definitely": 7072,
+ "▁spend": 7073,
+ "mission": 7074,
+ "зу": 7075,
+ "atively": 7076,
+ "bi": 7077,
+ "Callback": 7078,
+ "▁particularly": 7079,
+ "▁hell": 7080,
+ "▁pool": 7081,
+ "PRE": 7082,
+ "▁clearly": 7083,
+ "PT": 7084,
+ "othes": 7085,
+ "▁Id": 7086,
+ "Location": 7087,
+ "▁Run": 7088,
+ "▁fixed": 7089,
+ "▁Hand": 7090,
+ "bal": 7091,
+ "double": 7092,
+ "Can": 7093,
+ "Omega": 7094,
+ "▁challeng": 7095,
+ "▁standing": 7096,
+ "iten": 7097,
+ "▁mechan": 7098,
+ "▁durch": 7099,
+ "▁dell": 7100,
+ "▁raised": 7101,
+ "▁weak": 7102,
+ "▁Du": 7103,
+ "grad": 7104,
+ "▁scene": 7105,
+ "poss": 7106,
+ "▁ton": 7107,
+ "▁earth": 7108,
+ "ulations": 7109,
+ "▁strength": 7110,
+ "aked": 7111,
+ "▁remain": 7112,
+ "▁Bi": 7113,
+ "▁customer": 7114,
+ "range": 7115,
+ "▁interested": 7116,
+ "ONE": 7117,
+ "▁coff": 7118,
+ "require": 7119,
+ "▁Only": 7120,
+ "▁Web": 7121,
+ "▁farm": 7122,
+ "▁activity": 7123,
+ "▁rout": 7124,
+ "bling": 7125,
+ "SY": 7126,
+ "▁Richard": 7127,
+ "▁Ref": 7128,
+ "▁кон": 7129,
+ "▁jun": 7130,
+ "born": 7131,
+ "ijn": 7132,
+ "Configuration": 7133,
+ "uman": 7134,
+ "EE": 7135,
+ "▁married": 7136,
+ "▁За": 7137,
+ "▁fat": 7138,
+ "▁kid": 7139,
+ "▁Tur": 7140,
+ "▁offered": 7141,
+ "nic": 7142,
+ "▁Big": 7143,
+ "Gamma": 7144,
+ "▁Health": 7145,
+ "▁TR": 7146,
+ "▁się": 7147,
+ "▁construction": 7148,
+ "▁Church": 7149,
+ "▁Bet": 7150,
+ "bus": 7151,
+ "▁earn": 7152,
+ "rict": 7153,
+ "▁пра": 7154,
+ "▁brain": 7155,
+ "▁fra": 7156,
+ "▁Op": 7157,
+ "FIG": 7158,
+ "ema": 7159,
+ "▁European": 7160,
+ "▁Saint": 7161,
+ "ARE": 7162,
+ "uri": 7163,
+ "▁River": 7164,
+ "{}": 7165,
+ "▁sitting": 7166,
+ "▁understanding": 7167,
+ "▁plans": 7168,
+ "ropri": 7169,
+ "▁older": 7170,
+ "▁pressure": 7171,
+ "Impl": 7172,
+ "▁peace": 7173,
+ "Connection": 7174,
+ "▁fi": 7175,
+ "rich": 7176,
+ "▁shut": 7177,
+ "apers": 7178,
+ "Port": 7179,
+ "▁Look": 7180,
+ "rim": 7181,
+ "auth": 7182,
+ "auto": 7183,
+ "▁highly": 7184,
+ "▁unless": 7185,
+ "▁Wal": 7186,
+ "▁ren": 7187,
+ "ws": 7188,
+ "▁core": 7189,
+ "(-": 7190,
+ "▁clim": 7191,
+ "ruit": 7192,
+ "▁callback": 7193,
+ "hest": 7194,
+ "▁Charles": 7195,
+ "▁Long": 7196,
+ "}=": 7197,
+ "ър": 7198,
+ "▁shared": 7199,
+ "ulated": 7200,
+ "gorithm": 7201,
+ "▁Home": 7202,
+ "▁village": 7203,
+ "ees": 7204,
+ "sv": 7205,
+ "▁restaur": 7206,
+ "rey": 7207,
+ "▁Cast": 7208,
+ "▁Person": 7209,
+ "кий": 7210,
+ "▁organiz": 7211,
+ "▁Rad": 7212,
+ "ponents": 7213,
+ "▁werden": 7214,
+ "▁bow": 7215,
+ "sen": 7216,
+ "ami": 7217,
+ "Interface": 7218,
+ "▁basis": 7219,
+ "▁Company": 7220,
+ "ernel": 7221,
+ "itu": 7222,
+ "Hash": 7223,
+ "▁aan": 7224,
+ "▁х": 7225,
+ "▁smile": 7226,
+ "xml": 7227,
+ "▁scen": 7228,
+ "amm": 7229,
+ "tool": 7230,
+ "aria": 7231,
+ "▁accur": 7232,
+ "settings": 7233,
+ "▁Jesus": 7234,
+ "acement": 7235,
+ "power": 7236,
+ "(!": 7237,
+ "▁calls": 7238,
+ "▁basic": 7239,
+ "▁settings": 7240,
+ "ript": 7241,
+ "pool": 7242,
+ "ctors": 7243,
+ "▁Foundation": 7244,
+ "▁weap": 7245,
+ "KEY": 7246,
+ "foot": 7247,
+ "▁radio": 7248,
+ "▁helped": 7249,
+ "mann": 7250,
+ "▁jump": 7251,
+ "▁tick": 7252,
+ "▁growing": 7253,
+ "aten": 7254,
+ "real": 7255,
+ "▁increasing": 7256,
+ "Device": 7257,
+ "varepsilon": 7258,
+ "▁sets": 7259,
+ "▁advant": 7260,
+ "Open": 7261,
+ "▁reasons": 7262,
+ "▁supposed": 7263,
+ "oes": 7264,
+ "ede": 7265,
+ "teen": 7266,
+ "ifdef": 7267,
+ "▁delete": 7268,
+ "▁&=": 7269,
+ "▁Bill": 7270,
+ "▁aim": 7271,
+ "▁Ok": 7272,
+ "▁Av": 7273,
+ "reci": 7274,
+ "acks": 7275,
+ "iste": 7276,
+ "Properties": 7277,
+ "▁tmp": 7278,
+ "▁dei": 7279,
+ "PER": 7280,
+ "DC": 7281,
+ "sta": 7282,
+ "нии": 7283,
+ "▁limited": 7284,
+ "▁greater": 7285,
+ "description": 7286,
+ "ori": 7287,
+ "aints": 7288,
+ "▁hy": 7289,
+ "▁Mel": 7290,
+ "▁CH": 7291,
+ "cons": 7292,
+ "▁surround": 7293,
+ "▁Who": 7294,
+ "arc": 7295,
+ "▁telev": 7296,
+ "itution": 7297,
+ "▁equal": 7298,
+ "кі": 7299,
+ "▁Israel": 7300,
+ "äh": 7301,
+ "▁Caption": 7302,
+ "▁exerc": 7303,
+ "empor": 7304,
+ "▁++": 7305,
+ "▁lib": 7306,
+ "make": 7307,
+ "▁MA": 7308,
+ "copy": 7309,
+ "friend": 7310,
+ "▁кото": 7311,
+ "▁damage": 7312,
+ "▁\\,": 7313,
+ "oded": 7314,
+ "▁none": 7315,
+ "▁evalu": 7316,
+ "ston": 7317,
+ ">,": 7318,
+ "FOR": 7319,
+ "▁norm": 7320,
+ "appe": 7321,
+ "Session": 7322,
+ "▁adult": 7323,
+ "▁hospital": 7324,
+ "▁recommend": 7325,
+ "property": 7326,
+ "stein": 7327,
+ "final": 7328,
+ "▁nu": 7329,
+ "second": 7330,
+ "▁aspect": 7331,
+ "\")]": 7332,
+ "жен": 7333,
+ "amento": 7334,
+ "▁rac": 7335,
+ "save": 7336,
+ "▁football": 7337,
+ "Ab": 7338,
+ "ungs": 7339,
+ "abil": 7340,
+ "▁Arch": 7341,
+ "system": 7342,
+ "hist": 7343,
+ "▁luck": 7344,
+ "render": 7345,
+ "▁sein": 7346,
+ "ioni": 7347,
+ "▁rot": 7348,
+ "▁corner": 7349,
+ "▁appropri": 7350,
+ "▁Software": 7351,
+ "▁tele": 7352,
+ "Delete": 7353,
+ "▁According": 7354,
+ "▁prison": 7355,
+ "▁lic": 7356,
+ "▁ми": 7357,
+ "term": 7358,
+ "sets": 7359,
+ "▁vel": 7360,
+ "▁rank": 7361,
+ "▁existing": 7362,
+ "▁Vir": 7363,
+ "▁trip": 7364,
+ "▁му": 7365,
+ "avax": 7366,
+ "▁ris": 7367,
+ "▁define": 7368,
+ "▁heat": 7369,
+ "car": 7370,
+ "▁convert": 7371,
+ "email": 7372,
+ "▁Under": 7373,
+ "▁Ш": 7374,
+ "▁Grand": 7375,
+ "▁exists": 7376,
+ "sys": 7377,
+ "eff": 7378,
+ "▁Top": 7379,
+ "▁č": 7380,
+ "▁tempor": 7381,
+ "▁arguments": 7382,
+ "▁supported": 7383,
+ "ensed": 7384,
+ "▁Francis": 7385,
+ "▁coord": 7386,
+ "▁achieve": 7387,
+ "▁Name": 7388,
+ "▁Jahr": 7389,
+ "▁Gi": 7390,
+ "she": 7391,
+ "▁Dev": 7392,
+ "▁alla": 7393,
+ "▁WIT": 7394,
+ "agment": 7395,
+ "custom": 7396,
+ "alls": 7397,
+ "&&": 7398,
+ "WE": 7399,
+ "▁holding": 7400,
+ "prototype": 7401,
+ "▁fing": 7402,
+ "▁bag": 7403,
+ "▁Party": 7404,
+ "stack": 7405,
+ "▁economic": 7406,
+ "▁Gal": 7407,
+ "idents": 7408,
+ "▁Jun": 7409,
+ "▁showed": 7410,
+ "osh": 7411,
+ "▁Bay": 7412,
+ "mail": 7413,
+ "▁SO": 7414,
+ "▁\"<": 7415,
+ "graphics": 7416,
+ "▁fu": 7417,
+ "click": 7418,
+ "▁battle": 7419,
+ "{{": 7420,
+ "▁Event": 7421,
+ "rior": 7422,
+ "chaft": 7423,
+ "▁favorite": 7424,
+ "usive": 7425,
+ "support": 7426,
+ "bm": 7427,
+ "Kind": 7428,
+ "▁safety": 7429,
+ "▁Ent": 7430,
+ "cup": 7431,
+ "▁Australia": 7432,
+ "▁destroy": 7433,
+ "▁organization": 7434,
+ "iden": 7435,
+ "################": 7436,
+ "dec": 7437,
+ "▁za": 7438,
+ "▁seven": 7439,
+ "arely": 7440,
+ "▁flag": 7441,
+ "Dir": 7442,
+ "▁Carl": 7443,
+ "▁doctor": 7444,
+ "▁variety": 7445,
+ "▁Lin": 7446,
+ "▁tom": 7447,
+ "^{(": 7448,
+ "Bo": 7449,
+ "antes": 7450,
+ "▁mine": 7451,
+ "▁Mit": 7452,
+ "▁describe": 7453,
+ "Args": 7454,
+ "LS": 7455,
+ "API": 7456,
+ "▁Luc": 7457,
+ "phone": 7458,
+ "▁science": 7459,
+ "▁Oper": 7460,
+ "Next": 7461,
+ "▁investig": 7462,
+ "▁demonstr": 7463,
+ "▁Govern": 7464,
+ "▁objects": 7465,
+ "▁Louis": 7466,
+ "▁Returns": 7467,
+ "▁han": 7468,
+ "nam": 7469,
+ "▁comme": 7470,
+ "▁presence": 7471,
+ "▁pel": 7472,
+ "▁detect": 7473,
+ ")=": 7474,
+ "▁Chinese": 7475,
+ "▁rich": 7476,
+ "▁classes": 7477,
+ "▁expand": 7478,
+ "▁Dom": 7479,
+ "▁Dec": 7480,
+ "sn": 7481,
+ "peed": 7482,
+ "▁Jim": 7483,
+ "should": 7484,
+ "▁Smith": 7485,
+ "▁pages": 7486,
+ "▁Jean": 7487,
+ "rics": 7488,
+ "▁Sund": 7489,
+ "ads": 7490,
+ "▁Their": 7491,
+ "unicip": 7492,
+ "ву": 7493,
+ "▁download": 7494,
+ "▁stress": 7495,
+ "▁Pet": 7496,
+ "menu": 7497,
+ "reme": 7498,
+ "▁compared": 7499,
+ "Ste": 7500,
+ "IND": 7501,
+ "container": 7502,
+ "▁Indian": 7503,
+ "oren": 7504,
+ "▁ses": 7505,
+ "▁Whe": 7506,
+ "▁roku": 7507,
+ "▁established": 7508,
+ "▁generally": 7509,
+ "▁fle": 7510,
+ "__(": 7511,
+ "=\"+": 7512,
+ "Var": 7513,
+ "▁Make": 7514,
+ "▁removed": 7515,
+ "zz": 7516,
+ "ün": 7517,
+ "▁mix": 7518,
+ "erk": 7519,
+ "iation": 7520,
+ "outer": 7521,
+ "SK": 7522,
+ "▁becomes": 7523,
+ "▁Hall": 7524,
+ "scious": 7525,
+ "▁watched": 7526,
+ "▁gather": 7527,
+ "▁Result": 7528,
+ "proof": 7529,
+ "pay": 7530,
+ "▁produced": 7531,
+ "▁|=": 7532,
+ "▁border": 7533,
+ "▁din": 7534,
+ "▁script": 7535,
+ "▁actions": 7536,
+ "▁mas": 7537,
+ "ща": 7538,
+ "ooth": 7539,
+ "▁Techn": 7540,
+ "Json": 7541,
+ "▁filled": 7542,
+ "ден": 7543,
+ "undle": 7544,
+ "сту": 7545,
+ "Tool": 7546,
+ "▁king": 7547,
+ "▁ven": 7548,
+ "stra": 7549,
+ "▁predict": 7550,
+ "▁lui": 7551,
+ "▁WARRAN": 7552,
+ "▁Fun": 7553,
+ "Script": 7554,
+ "▁powerful": 7555,
+ "▁lose": 7556,
+ "atically": 7557,
+ "▁daily": 7558,
+ "▁ring": 7559,
+ "▁arrived": 7560,
+ "Stack": 7561,
+ "scope": 7562,
+ "▁Back": 7563,
+ "elij": 7564,
+ "▁ze": 7565,
+ "keys": 7566,
+ "{\"": 7567,
+ "VID": 7568,
+ "▁license": 7569,
+ "what": 7570,
+ "▁proced": 7571,
+ "rant": 7572,
+ "estival": 7573,
+ "agram": 7574,
+ "▁LO": 7575,
+ "▁Henry": 7576,
+ "▁flags": 7577,
+ "Down": 7578,
+ "scription": 7579,
+ "▁families": 7580,
+ "isse": 7581,
+ "bour": 7582,
+ "▁Bur": 7583,
+ "—\"": 7584,
+ "▁brief": 7585,
+ "▁creating": 7586,
+ "▁clients": 7587,
+ "rangle": 7588,
+ "▁amazing": 7589,
+ "▁sind": 7590,
+ "▁covered": 7591,
+ "Well": 7592,
+ "сте": 7593,
+ "тор": 7594,
+ "▁Bas": 7595,
+ "total": 7596,
+ "▁Init": 7597,
+ "▁sand": 7598,
+ "Unit": 7599,
+ "▁murder": 7600,
+ "▁bright": 7601,
+ "▁trav": 7602,
+ "icans": 7603,
+ "▁attribute": 7604,
+ "fc": 7605,
+ "▁placed": 7606,
+ "EST": 7607,
+ "Vari": 7608,
+ "▁cos": 7609,
+ "▁attract": 7610,
+ "anel": 7611,
+ "}).": 7612,
+ "bytes": 7613,
+ "▁parse": 7614,
+ "▁belong": 7615,
+ "BN": 7616,
+ "▁Sol": 7617,
+ "Po": 7618,
+ "`,": 7619,
+ "▁calling": 7620,
+ "▁?>": 7621,
+ "▁iter": 7622,
+ "▁url": 7623,
+ "▁evening": 7624,
+ "reek": 7625,
+ "▁honest": 7626,
+ "▁director": 7627,
+ "RC": 7628,
+ "▁solid": 7629,
+ "▁phil": 7630,
+ "iene": 7631,
+ "FAULT": 7632,
+ "cope": 7633,
+ "▁History": 7634,
+ "▁Team": 7635,
+ "reedom": 7636,
+ "▁ru": 7637,
+ "UB": 7638,
+ "▁worse": 7639,
+ "imo": 7640,
+ "Mat": 7641,
+ "▁Mex": 7642,
+ "actor": 7643,
+ "▁vor": 7644,
+ "ться": 7645,
+ "▁experiment": 7646,
+ "▁Play": 7647,
+ "▁Another": 7648,
+ "▁happens": 7649,
+ "uan": 7650,
+ "▁patients": 7651,
+ "▁rend": 7652,
+ "▁Mo": 7653,
+ "▁Tex": 7654,
+ "▁wed": 7655,
+ "tn": 7656,
+ "insert": 7657,
+ "▁па": 7658,
+ "▁anti": 7659,
+ "Match": 7660,
+ "ampionship": 7661,
+ "▁forces": 7662,
+ "▁Hot": 7663,
+ "▁phase": 7664,
+ "▁template": 7665,
+ "stop": 7666,
+ "icated": 7667,
+ "▁managed": 7668,
+ "wait": 7669,
+ "▁*(": 7670,
+ "GB": 7671,
+ "▁appoint": 7672,
+ "ła": 7673,
+ "▁stick": 7674,
+ "▁FOR": 7675,
+ "▁Vis": 7676,
+ "tor": 7677,
+ "▁př": 7678,
+ "quest": 7679,
+ "uses": 7680,
+ "\");\r": 7681,
+ "▁suddenly": 7682,
+ "éc": 7683,
+ "ND": 7684,
+ "urop": 7685,
+ "ред": 7686,
+ "▁insurance": 7687,
+ "access": 7688,
+ "unfinished": 7689,
+ "▁tamb": 7690,
+ "▁sac": 7691,
+ "▁Court": 7692,
+ "▁missing": 7693,
+ "▁Where": 7694,
+ "▁Sum": 7695,
+ "}^{\\": 7696,
+ "▁sua": 7697,
+ "_,": 7698,
+ "▁thick": 7699,
+ "▁Trump": 7700,
+ "▁operations": 7701,
+ "FS": 7702,
+ "▁deux": 7703,
+ "dz": 7704,
+ "Template": 7705,
+ "▁\"/": 7706,
+ "▁odd": 7707,
+ "▁reality": 7708,
+ "▁teams": 7709,
+ "▁cer": 7710,
+ "oma": 7711,
+ "▁și": 7712,
+ "▁cloud": 7713,
+ "▁Department": 7714,
+ "Ne": 7715,
+ "▁requires": 7716,
+ "items": 7717,
+ "▁III": 7718,
+ "rightarrow": 7719,
+ ")->": 7720,
+ "▁writer": 7721,
+ "replace": 7722,
+ "▁thr": 7723,
+ "jen": 7724,
+ "▁ot": 7725,
+ "▁occup": 7726,
+ "▁eventually": 7727,
+ "▁Math": 7728,
+ "▁conserv": 7729,
+ "amer": 7730,
+ "▁Fort": 7731,
+ "▁dry": 7732,
+ "▁sexual": 7733,
+ "▁costs": 7734,
+ "▁forms": 7735,
+ "▁Vict": 7736,
+ "PAR": 7737,
+ "framework": 7738,
+ "▁ди": 7739,
+ "Operation": 7740,
+ "зна": 7741,
+ "which": 7742,
+ "▁tight": 7743,
+ "Invalid": 7744,
+ "▁partner": 7745,
+ "▁пред": 7746,
+ "▁thank": 7747,
+ "▁guard": 7748,
+ "hem": 7749,
+ "Body": 7750,
+ "▁emot": 7751,
+ "IX": 7752,
+ "fast": 7753,
+ "що": 7754,
+ "ño": 7755,
+ "night": 7756,
+ "▁Sci": 7757,
+ "ника": 7758,
+ "▁TO": 7759,
+ "▁individuals": 7760,
+ "сси": 7761,
+ "}),": 7762,
+ "False": 7763,
+ "(\"%": 7764,
+ "▁optim": 7765,
+ "▁-->": 7766,
+ "▁factor": 7767,
+ "▁smaller": 7768,
+ "▁contain": 7769,
+ "spect": 7770,
+ "Engine": 7771,
+ "▁announced": 7772,
+ "▁Democr": 7773,
+ "▁rob": 7774,
+ "▁flat": 7775,
+ "osoph": 7776,
+ "Search": 7777,
+ "ahl": 7778,
+ "▁Exception": 7779,
+ "▁Ol": 7780,
+ "equals": 7781,
+ "▁unter": 7782,
+ "shape": 7783,
+ "NS": 7784,
+ "Obj": 7785,
+ "▁species": 7786,
+ "weight": 7787,
+ "you": 7788,
+ "▁este": 7789,
+ "▁View": 7790,
+ "▁mission": 7791,
+ "▁journal": 7792,
+ "Values": 7793,
+ "▁einem": 7794,
+ "ismo": 7795,
+ "▁projects": 7796,
+ "▁Das": 7797,
+ "rible": 7798,
+ "▁serve": 7799,
+ "▁opening": 7800,
+ "▁hur": 7801,
+ "▁programs": 7802,
+ "▁USA": 7803,
+ "iliar": 7804,
+ "idos": 7805,
+ "Br": 7806,
+ "estamp": 7807,
+ "▁tools": 7808,
+ "anner": 7809,
+ "RT": 7810,
+ "▁Start": 7811,
+ "▁bath": 7812,
+ "▁coffee": 7813,
+ "orter": 7814,
+ "internal": 7815,
+ "files": 7816,
+ "INVAL": 7817,
+ "ako": 7818,
+ "dt": 7819,
+ "▁Second": 7820,
+ "▁alloc": 7821,
+ "▁ended": 7822,
+ "acional": 7823,
+ "▁manager": 7824,
+ "▁Sun": 7825,
+ "agg": 7826,
+ "▁leader": 7827,
+ "olved": 7828,
+ "▁что": 7829,
+ "▁traditional": 7830,
+ "shot": 7831,
+ "rup": 7832,
+ "CF": 7833,
+ "▁Each": 7834,
+ "wr": 7835,
+ "▁Som": 7836,
+ "▁materials": 7837,
+ "▁msg": 7838,
+ "▁syn": 7839,
+ "▁produce": 7840,
+ "▁storage": 7841,
+ "subsection": 7842,
+ "▁Sie": 7843,
+ "▁IP": 7844,
+ "CESS": 7845,
+ "▁wa": 7846,
+ "Record": 7847,
+ "▁marketing": 7848,
+ "plet": 7849,
+ "Dialog": 7850,
+ "▁mentioned": 7851,
+ "▁Na": 7852,
+ "▁Union": 7853,
+ "▁API": 7854,
+ "▁negative": 7855,
+ "txt": 7856,
+ "▁easier": 7857,
+ "legal": 7858,
+ "Dep": 7859,
+ "▁novel": 7860,
+ "eur": 7861,
+ "ació": 7862,
+ "▁Bud": 7863,
+ "▁carry": 7864,
+ "schaft": 7865,
+ "▁broken": 7866,
+ "▁trees": 7867,
+ ">();": 7868,
+ "▁emb": 7869,
+ "ieder": 7870,
+ "▁route": 7871,
+ "ikel": 7872,
+ "▁listen": 7873,
+ "ashion": 7874,
+ "▁Mrs": 7875,
+ "▁equipment": 7876,
+ "agger": 7877,
+ "▁Thus": 7878,
+ "▁matrix": 7879,
+ "alla": 7880,
+ "▁Tour": 7881,
+ "▁conversation": 7882,
+ "Mon": 7883,
+ "ournal": 7884,
+ "▁minute": 7885,
+ "Am": 7886,
+ "Api": 7887,
+ "▁forget": 7888,
+ "Me": 7889,
+ "levant": 7890,
+ "temp": 7891,
+ "▁telling": 7892,
+ "move": 7893,
+ "▁independent": 7894,
+ "toString": 7895,
+ "edit": 7896,
+ "▁Jac": 7897,
+ "azz": 7898,
+ "react": 7899,
+ "▁cin": 7900,
+ "▁Prov": 7901,
+ "isted": 7902,
+ "▁hash": 7903,
+ "onna": 7904,
+ "iki": 7905,
+ "▁generated": 7906,
+ "Render": 7907,
+ "▁psych": 7908,
+ "nav": 7909,
+ "▁entr": 7910,
+ "пра": 7911,
+ "rx": 7912,
+ "ATH": 7913,
+ "▁assume": 7914,
+ "Tree": 7915,
+ "sembly": 7916,
+ "▁Matt": 7917,
+ "caption": 7918,
+ "▁solutions": 7919,
+ "▁faith": 7920,
+ "▁digital": 7921,
+ "▁excell": 7922,
+ "▁Version": 7923,
+ "Debug": 7924,
+ "▁жи": 7925,
+ "▁carried": 7926,
+ "reset": 7927,
+ "▁slowly": 7928,
+ "ancing": 7929,
+ "▁owner": 7930,
+ "▁Ter": 7931,
+ "▁Did": 7932,
+ "▁gest": 7933,
+ "▁été": 7934,
+ "▁proof": 7935,
+ "Font": 7936,
+ "▁nob": 7937,
+ "Co": 7938,
+ "▁GNU": 7939,
+ "▁liber": 7940,
+ "itness": 7941,
+ "▁hij": 7942,
+ "▁vert": 7943,
+ "ша": 7944,
+ "FLAG": 7945,
+ "MENT": 7946,
+ "▁Son": 7947,
+ "Mult": 7948,
+ "▁district": 7949,
+ "connect": 7950,
+ "jection": 7951,
+ "lymp": 7952,
+ "▁realized": 7953,
+ "mos": 7954,
+ "ye": 7955,
+ "▁render": 7956,
+ "rio": 7957,
+ "▁interpret": 7958,
+ "▁slightly": 7959,
+ "fix": 7960,
+ "▁studies": 7961,
+ "▁rid": 7962,
+ "atre": 7963,
+ "▁benefits": 7964,
+ "▁Face": 7965,
+ "ivery": 7966,
+ "рия": 7967,
+ "document": 7968,
+ "▁asking": 7969,
+ "Last": 7970,
+ "arante": 7971,
+ "▁Martin": 7972,
+ "▁Ell": 7973,
+ "▁vector": 7974,
+ "▁forced": 7975,
+ "оло": 7976,
+ "PH": 7977,
+ "WR": 7978,
+ "▁Kl": 7979,
+ "▁sky": 7980,
+ "▁strategy": 7981,
+ "ocked": 7982,
+ "▁neck": 7983,
+ "ści": 7984,
+ "OUT": 7985,
+ ")),": 7986,
+ "Custom": 7987,
+ "▁wie": 7988,
+ "▁sweet": 7989,
+ "▁temp": 7990,
+ "▁foreign": 7991,
+ "▁hall": 7992,
+ "astr": 7993,
+ "Ass": 7994,
+ "MODE": 7995,
+ "▁maximum": 7996,
+ "annels": 7997,
+ "▁tip": 7998,
+ "▁seconds": 7999,
+ "▁stack": 8000,
+ "iga": 8001,
+ "▁raise": 8002,
+ "enable": 8003,
+ "oir": 8004,
+ "▁soul": 8005,
+ "Ke": 8006,
+ ")$.": 8007,
+ "▁Tim": 8008,
+ "ALSE": 8009,
+ "iser": 8010,
+ "contin": 8011,
+ "bel": 8012,
+ "▁mad": 8013,
+ "lichen": 8014,
+ "abe": 8015,
+ "safe": 8016,
+ "▁concent": 8017,
+ "bound": 8018,
+ "▁Requ": 8019,
+ "switch": 8020,
+ "▁stone": 8021,
+ "▁transl": 8022,
+ "▁vac": 8023,
+ "andon": 8024,
+ "▁Fore": 8025,
+ "▁sounds": 8026,
+ "▁Pop": 8027,
+ "▁HT": 8028,
+ "lia": 8029,
+ "enter": 8030,
+ "▁helps": 8031,
+ "edy": 8032,
+ "ствен": 8033,
+ "anted": 8034,
+ "▁Its": 8035,
+ "▁Step": 8036,
+ "Icon": 8037,
+ "▁EXPECT": 8038,
+ "ialized": 8039,
+ "Post": 8040,
+ "aze": 8041,
+ "▁Carol": 8042,
+ "▁req": 8043,
+ "▁critical": 8044,
+ "DS": 8045,
+ "▁seat": 8046,
+ "aped": 8047,
+ "▁upper": 8048,
+ "▁Sy": 8049,
+ "▁explain": 8050,
+ "▁'./": 8051,
+ "utils": 8052,
+ "possible": 8053,
+ "▁dont": 8054,
+ "Host": 8055,
+ "▁approxim": 8056,
+ "Async": 8057,
+ "▁grab": 8058,
+ "▁sources": 8059,
+ "▁Mos": 8060,
+ "▁Germany": 8061,
+ "▁rub": 8062,
+ "CHAN": 8063,
+ "▁rain": 8064,
+ "▁truly": 8065,
+ "▁joined": 8066,
+ "▁": 8067,
+ "▁Lo": 8068,
+ "Description": 8069,
+ "akt": 8070,
+ "▁Ann": 8071,
+ "^*": 8072,
+ "idae": 8073,
+ "(:": 8074,
+ "tw": 8075,
+ "Mar": 8076,
+ "produ": 8077,
+ "▁spoke": 8078,
+ "ют": 8079,
+ "▁walking": 8080,
+ "▁nodded": 8081,
+ "Props": 8082,
+ "Enabled": 8083,
+ "irk": 8084,
+ "FILE": 8085,
+ "equal": 8086,
+ "pping": 8087,
+ "oli": 8088,
+ "EV": 8089,
+ "enz": 8090,
+ "eting": 8091,
+ "▁sample": 8092,
+ "▁artist": 8093,
+ "[$": 8094,
+ "ità": 8095,
+ "йо": 8096,
+ "props": 8097,
+ "bu": 8098,
+ "ев": 8099,
+ "▁responsible": 8100,
+ "MT": 8101,
+ "▁caused": 8102,
+ "▁theme": 8103,
+ "▁Was": 8104,
+ "▁Before": 8105,
+ "acle": 8106,
+ "▁року": 8107,
+ "cu": 8108,
+ "DEV": 8109,
+ "▁hung": 8110,
+ "textbf": 8111,
+ "▁spin": 8112,
+ "▁latest": 8113,
+ "entially": 8114,
+ "▁Program": 8115,
+ "Metadata": 8116,
+ "password": 8117,
+ "▁hurt": 8118,
+ "кс": 8119,
+ "▁Aus": 8120,
+ "sey": 8121,
+ "allet": 8122,
+ "xF": 8123,
+ "▁Road": 8124,
+ "ется": 8125,
+ "▁rent": 8126,
+ "ция": 8127,
+ "▁Assert": 8128,
+ "іль": 8129,
+ "ück": 8130,
+ "▁sites": 8131,
+ "Document": 8132,
+ "▁obtained": 8133,
+ "▁ci": 8134,
+ "▁[\"": 8135,
+ "▁completed": 8136,
+ "aset": 8137,
+ "raid": 8138,
+ "▁sorry": 8139,
+ "▁fab": 8140,
+ "▁schools": 8141,
+ "ходи": 8142,
+ "▁scr": 8143,
+ "▁incor": 8144,
+ "▁'/": 8145,
+ "▁spr": 8146,
+ "▁Text": 8147,
+ "▁commercial": 8148,
+ "ingly": 8149,
+ "▁opinion": 8150,
+ "▁Star": 8151,
+ "Sign": 8152,
+ "▁javax": 8153,
+ "wi": 8154,
+ "lat": 8155,
+ "▁Key": 8156,
+ "varphi": 8157,
+ "ды": 8158,
+ "▁connected": 8159,
+ "▁adjust": 8160,
+ "▁Az": 8161,
+ "▁planning": 8162,
+ "---": 8163,
+ "Integer": 8164,
+ "auf": 8165,
+ "expected": 8166,
+ "▁fant": 8167,
+ "▁tou": 8168,
+ "Parent": 8169,
+ "▁Lat": 8170,
+ "▁thoughts": 8171,
+ "▁Jud": 8172,
+ "Parameters": 8173,
+ "Gr": 8174,
+ "ром": 8175,
+ "IA": 8176,
+ "▁Bob": 8177,
+ "lict": 8178,
+ "lan": 8179,
+ "omic": 8180,
+ "▁apart": 8181,
+ "▁trou": 8182,
+ "▁appreci": 8183,
+ "▁Christmas": 8184,
+ "irq": 8185,
+ "thon": 8186,
+ "▁Error": 8187,
+ "▁score": 8188,
+ "rome": 8189,
+ "▁neighbor": 8190,
+ "▁Mur": 8191,
+ "admin": 8192,
+ "▁Film": 8193,
+ "Rect": 8194,
+ "▁configuration": 8195,
+ "▁cs": 8196,
+ "gun": 8197,
+ "channel": 8198,
+ "▁Report": 8199,
+ "▁strateg": 8200,
+ "▁workers": 8201,
+ "fields": 8202,
+ "Schema": 8203,
+ "appa": 8204,
+ "olic": 8205,
+ "EO": 8206,
+ "▁Charl": 8207,
+ "▁Cup": 8208,
+ "png": 8209,
+ "▁Hill": 8210,
+ "owe": 8211,
+ "▁mostly": 8212,
+ "”.": 8213,
+ "▁finish": 8214,
+ "▁Со": 8215,
+ "▁stars": 8216,
+ "player": 8217,
+ "▁inner": 8218,
+ "component": 8219,
+ "tim": 8220,
+ "IE": 8221,
+ "▁ther": 8222,
+ "▁smart": 8223,
+ "▁sad": 8224,
+ "▁Council": 8225,
+ "area": 8226,
+ "lay": 8227,
+ "▁ба": 8228,
+ "▁gradu": 8229,
+ "▁chem": 8230,
+ "▁ho": 8231,
+ "Select": 8232,
+ "▁instr": 8233,
+ "▁kl": 8234,
+ "ifications": 8235,
+ "Long": 8236,
+ "▁sobre": 8237,
+ "▁Old": 8238,
+ "west": 8239,
+ "},\\": 8240,
+ "ingu": 8241,
+ "▁spring": 8242,
+ "▁nur": 8243,
+ "example": 8244,
+ "When": 8245,
+ "▁advice": 8246,
+ "▁ult": 8247,
+ "ennis": 8248,
+ "▁Love": 8249,
+ "▁\"\"": 8250,
+ "▁increased": 8251,
+ "▁finding": 8252,
+ "irty": 8253,
+ "istrict": 8254,
+ "▁layer": 8255,
+ "template": 8256,
+ "First": 8257,
+ "ным": 8258,
+ "igration": 8259,
+ "rency": 8260,
+ "owie": 8261,
+ "▁np": 8262,
+ "▁selection": 8263,
+ "▁Nach": 8264,
+ "▁PRO": 8265,
+ "▁polic": 8266,
+ "▁database": 8267,
+ "▁byte": 8268,
+ "▁providing": 8269,
+ "mac": 8270,
+ "▁metal": 8271,
+ "modules": 8272,
+ "▁Georg": 8273,
+ "▁Sa": 8274,
+ "▁establish": 8275,
+ "...\"": 8276,
+ "iu": 8277,
+ "kin": 8278,
+ "▁eth": 8279,
+ "▁Sand": 8280,
+ "▁Chapter": 8281,
+ "▁gal": 8282,
+ "▁ice": 8283,
+ "Red": 8284,
+ "▁dal": 8285,
+ "▁principal": 8286,
+ "Msg": 8287,
+ "▁remains": 8288,
+ "нг": 8289,
+ "Title": 8290,
+ "Rel": 8291,
+ "Display": 8292,
+ "Non": 8293,
+ "▁definition": 8294,
+ "▁attr": 8295,
+ "▁signal": 8296,
+ "hl": 8297,
+ "▁sel": 8298,
+ "▁volume": 8299,
+ "▁cache": 8300,
+ "hens": 8301,
+ "▁wird": 8302,
+ "[\\": 8303,
+ "NOT": 8304,
+ "▁election": 8305,
+ "utt": 8306,
+ "▁Window": 8307,
+ "ental": 8308,
+ "ifest": 8309,
+ "xf": 8310,
+ "▁Ра": 8311,
+ "▁overall": 8312,
+ "blic": 8313,
+ "▁editor": 8314,
+ "aden": 8315,
+ "▁cart": 8316,
+ "Left": 8317,
+ "uls": 8318,
+ "bing": 8319,
+ "Right": 8320,
+ "▁sé": 8321,
+ "Sim": 8322,
+ "▁camera": 8323,
+ "▁fav": 8324,
+ "Decl": 8325,
+ "spring": 8326,
+ "▁errors": 8327,
+ "Tab": 8328,
+ "println": 8329,
+ "▁Bern": 8330,
+ "nab": 8331,
+ "▁Base": 8332,
+ "▁auth": 8333,
+ "▁apparent": 8334,
+ "▁presented": 8335,
+ "▁remained": 8336,
+ "▁wet": 8337,
+ "Enc": 8338,
+ "INFO": 8339,
+ "▁Sing": 8340,
+ "package": 8341,
+ ")));": 8342,
+ "▁Social": 8343,
+ "▁Mass": 8344,
+ "▁despite": 8345,
+ "▁mobile": 8346,
+ "▁labor": 8347,
+ "Go": 8348,
+ "▁esp": 8349,
+ "▁Table": 8350,
+ "▁expert": 8351,
+ "▁flex": 8352,
+ "▁profession": 8353,
+ "▁pil": 8354,
+ "Collection": 8355,
+ "LOCK": 8356,
+ "▁applied": 8357,
+ "aller": 8358,
+ "orph": 8359,
+ "ENSE": 8360,
+ "▁был": 8361,
+ "▁db": 8362,
+ "overline": 8363,
+ "▁Code": 8364,
+ "▁bytes": 8365,
+ "▁trouble": 8366,
+ "▁насе": 8367,
+ "DD": 8368,
+ "▁Year": 8369,
+ "mbox": 8370,
+ "▁keeping": 8371,
+ "▁kick": 8372,
+ "äng": 8373,
+ "▁corresponding": 8374,
+ "▁library": 8375,
+ "▁*/\r": 8376,
+ "callback": 8377,
+ "ums": 8378,
+ "▁json": 8379,
+ "▁Mount": 8380,
+ "▁Stand": 8381,
+ "IGHT": 8382,
+ "▁News": 8383,
+ "▁comments": 8384,
+ "returns": 8385,
+ "Cal": 8386,
+ "▁award": 8387,
+ "▁bought": 8388,
+ "includegraphics": 8389,
+ "▁ле": 8390,
+ "dot": 8391,
+ "ronic": 8392,
+ "▁extremely": 8393,
+ "▁minor": 8394,
+ "ifer": 8395,
+ "java": 8396,
+ "endar": 8397,
+ "layout": 8398,
+ "plies": 8399,
+ "▁buf": 8400,
+ "▁Island": 8401,
+ "▁About": 8402,
+ "▁west": 8403,
+ "▁Scott": 8404,
+ "ACT": 8405,
+ "Why": 8406,
+ "▁largest": 8407,
+ "▁container": 8408,
+ "▁temperature": 8409,
+ "▁£": 8410,
+ "▁reduce": 8411,
+ "▁foi": 8412,
+ "han": 8413,
+ "▁bod": 8414,
+ "▁Van": 8415,
+ "▁nullptr": 8416,
+ "▁dating": 8417,
+ "▁chain": 8418,
+ "Flags": 8419,
+ "iento": 8420,
+ "sort": 8421,
+ "▁fan": 8422,
+ "▁determine": 8423,
+ "▁wear": 8424,
+ "BE": 8425,
+ "▁appropriate": 8426,
+ "лся": 8427,
+ "тов": 8428,
+ "▁goals": 8429,
+ "▁Map": 8430,
+ "▁Sar": 8431,
+ "▁Option": 8432,
+ "▁hate": 8433,
+ "▁zijn": 8434,
+ ",-": 8435,
+ "▁implied": 8436,
+ "bits": 8437,
+ "▁Men": 8438,
+ "skip": 8439,
+ "▁Mond": 8440,
+ "▁Hon": 8441,
+ "▁prove": 8442,
+ "van": 8443,
+ "▁traff": 8444,
+ "▁intr": 8445,
+ "pic": 8446,
+ "▁dropped": 8447,
+ "▁werd": 8448,
+ "▁separate": 8449,
+ "isa": 8450,
+ "▁tab": 8451,
+ "tml": 8452,
+ "▁\"$": 8453,
+ "mutex": 8454,
+ "▁Pan": 8455,
+ "serve": 8456,
+ "▁hotel": 8457,
+ "▁Last": 8458,
+ "step": 8459,
+ "▁vir": 8460,
+ "Rule": 8461,
+ "istan": 8462,
+ "oting": 8463,
+ "arks": 8464,
+ "(__": 8465,
+ "▁els": 8466,
+ "Player": 8467,
+ "]]": 8468,
+ "вич": 8469,
+ "ych": 8470,
+ "exception": 8471,
+ "=\"../": 8472,
+ "▁imagine": 8473,
+ "\"},": 8474,
+ "icago": 8475,
+ "eler": 8476,
+ "▁vs": 8477,
+ "▁Africa": 8478,
+ "▁Business": 8479,
+ "ocks": 8480,
+ "▁prz": 8481,
+ "▁fucking": 8482,
+ "▁picked": 8483,
+ "▁ві": 8484,
+ "▁\",": 8485,
+ "▁bott": 8486,
+ "▁failure": 8487,
+ "[:": 8488,
+ "▁Gar": 8489,
+ "apes": 8490,
+ "uple": 8491,
+ "▁fer": 8492,
+ "▁purchase": 8493,
+ "▁пер": 8494,
+ "▁bird": 8495,
+ "Widget": 8496,
+ "▁Sunday": 8497,
+ "▁Amaz": 8498,
+ "▁consult": 8499,
+ "utsch": 8500,
+ "anto": 8501,
+ "Storage": 8502,
+ "▁header": 8503,
+ "ühr": 8504,
+ "▁Ha": 8505,
+ "▁Association": 8506,
+ "▁sight": 8507,
+ "Cell": 8508,
+ "▁profile": 8509,
+ "▁female": 8510,
+ "ån": 8511,
+ "▁wid": 8512,
+ "zn": 8513,
+ "Direct": 8514,
+ "▁stret": 8515,
+ "aat": 8516,
+ "▁patient": 8517,
+ "here": 8518,
+ "▁Atl": 8519,
+ "inet": 8520,
+ "Definition": 8521,
+ "imary": 8522,
+ "Policy": 8523,
+ "▁dut": 8524,
+ "▁majority": 8525,
+ "сі": 8526,
+ "▁Project": 8527,
+ "ById": 8528,
+ "▁believed": 8529,
+ "▁Music": 8530,
+ "зы": 8531,
+ "anti": 8532,
+ "▁oder": 8533,
+ "Channel": 8534,
+ "▁sle": 8535,
+ "▁sequence": 8536,
+ "▁pieces": 8537,
+ "▁kne": 8538,
+ "▁absolutely": 8539,
+ "▁Philip": 8540,
+ "abilities": 8541,
+ "Que": 8542,
+ "▁Kar": 8543,
+ "Execut": 8544,
+ "▁Devel": 8545,
+ "▁electric": 8546,
+ "full": 8547,
+ "rolled": 8548,
+ "Dom": 8549,
+ "▁river": 8550,
+ "▁healthy": 8551,
+ "▁extern": 8552,
+ "fit": 8553,
+ "▁coach": 8554,
+ "▁Kr": 8555,
+ "asta": 8556,
+ "Compat": 8557,
+ "▁exit": 8558,
+ "▁Const": 8559,
+ "after": 8560,
+ "▁shoulder": 8561,
+ "▁jobs": 8562,
+ "zone": 8563,
+ "▁sale": 8564,
+ "ixel": 8565,
+ "▁determined": 8566,
+ "▁anyway": 8567,
+ "orf": 8568,
+ "▁Ger": 8569,
+ "allel": 8570,
+ "rees": 8571,
+ "asm": 8572,
+ "ims": 8573,
+ "▁records": 8574,
+ "▁corpor": 8575,
+ "▁intellig": 8576,
+ "▁Prem": 8577,
+ "▁driving": 8578,
+ "▁marriage": 8579,
+ "▁Thank": 8580,
+ "▁willing": 8581,
+ "MC": 8582,
+ "Fields": 8583,
+ "Items": 8584,
+ "▁micro": 8585,
+ "▁lift": 8586,
+ "irection": 8587,
+ "Account": 8588,
+ "▁architect": 8589,
+ "track": 8590,
+ "▁prin": 8591,
+ "PA": 8592,
+ "▁runs": 8593,
+ "▁Texas": 8594,
+ "isher": 8595,
+ "ensure": 8596,
+ "▁Both": 8597,
+ "ком": 8598,
+ "▁Color": 8599,
+ "Register": 8600,
+ "▁Joe": 8601,
+ "geq": 8602,
+ "lets": 8603,
+ "ading": 8604,
+ "▁army": 8605,
+ "▁Bank": 8606,
+ "otic": 8607,
+ "Product": 8608,
+ "import": 8609,
+ "▁Wed": 8610,
+ "▁cry": 8611,
+ "grade": 8612,
+ "dig": 8613,
+ "gal": 8614,
+ "кла": 8615,
+ "ested": 8616,
+ "ões": 8617,
+ "gers": 8618,
+ "ologie": 8619,
+ "том": 8620,
+ "razy": 8621,
+ "▁dinner": 8622,
+ "QU": 8623,
+ "▁fingers": 8624,
+ "ULE": 8625,
+ "claim": 8626,
+ "▁advantage": 8627,
+ "▁variable": 8628,
+ "▁medic": 8629,
+ "▁male": 8630,
+ "▁circum": 8631,
+ "▁мі": 8632,
+ "▁internet": 8633,
+ "WN": 8634,
+ "▁lab": 8635,
+ "azine": 8636,
+ "чно": 8637,
+ "▁loop": 8638,
+ "▁pred": 8639,
+ "▁consequ": 8640,
+ "▁balance": 8641,
+ "fortun": 8642,
+ "▁gift": 8643,
+ "▁drug": 8644,
+ "▁cash": 8645,
+ "ских": 8646,
+ "rg": 8647,
+ "istribut": 8648,
+ "▁highest": 8649,
+ "ême": 8650,
+ "emph": 8651,
+ "emon": 8652,
+ "▁performed": 8653,
+ "cut": 8654,
+ "▁closer": 8655,
+ "▁becoming": 8656,
+ "▁\"\",": 8657,
+ "star": 8658,
+ "pub": 8659,
+ "▁prepar": 8660,
+ "▁vote": 8661,
+ "ilde": 8662,
+ "▁impress": 8663,
+ "▁employees": 8664,
+ "▁einen": 8665,
+ "▁smooth": 8666,
+ "▁snow": 8667,
+ "▁purs": 8668,
+ "▁voc": 8669,
+ "▁Microsoft": 8670,
+ "PU": 8671,
+ "▁income": 8672,
+ "inos": 8673,
+ "▁operator": 8674,
+ "▁equival": 8675,
+ "▁password": 8676,
+ "ción": 8677,
+ "success": 8678,
+ "▁emp": 8679,
+ "HOUT": 8680,
+ "▁ca": 8681,
+ "flag": 8682,
+ "illy": 8683,
+ "crete": 8684,
+ "frak": 8685,
+ "▁hidden": 8686,
+ "▁\"%": 8687,
+ "ERN": 8688,
+ "рова": 8689,
+ "▁UN": 8690,
+ "roke": 8691,
+ "miss": 8692,
+ "▁split": 8693,
+ "Reference": 8694,
+ ")$,": 8695,
+ "eper": 8696,
+ "▁NO": 8697,
+ "▁square": 8698,
+ "sur": 8699,
+ "чен": 8700,
+ "ester": 8701,
+ "нь": 8702,
+ "}\"": 8703,
+ "rawn": 8704,
+ "rule": 8705,
+ "▁audience": 8706,
+ "este": 8707,
+ "ems": 8708,
+ "ICENSE": 8709,
+ "▁Ill": 8710,
+ "USE": 8711,
+ "▁bon": 8712,
+ "bur": 8713,
+ "▁sick": 8714,
+ "▁horse": 8715,
+ "▁Educ": 8716,
+ "▁benefit": 8717,
+ "▁cro": 8718,
+ "Application": 8719,
+ "▁corre": 8720,
+ "▁guarante": 8721,
+ "DATA": 8722,
+ "▁explained": 8723,
+ "TX": 8724,
+ "▁ont": 8725,
+ "▁Flor": 8726,
+ "▁reports": 8727,
+ "▁Real": 8728,
+ "uded": 8729,
+ "lean": 8730,
+ "▁citiz": 8731,
+ "▁decide": 8732,
+ "WS": 8733,
+ "▁domain": 8734,
+ "▁reflect": 8735,
+ "▁minimum": 8736,
+ "▁legs": 8737,
+ "▁smiled": 8738,
+ "fi": 8739,
+ "▁pure": 8740,
+ "▁Custom": 8741,
+ "▁essential": 8742,
+ "▁observed": 8743,
+ "Bytes": 8744,
+ "▁ctx": 8745,
+ "▁rates": 8746,
+ "mbre": 8747,
+ "▁worry": 8748,
+ ")^": 8749,
+ "▁Research": 8750,
+ "Root": 8751,
+ "Windows": 8752,
+ "ulture": 8753,
+ "▁relative": 8754,
+ "▁seu": 8755,
+ "▁nie": 8756,
+ "▁shook": 8757,
+ "iously": 8758,
+ "▁advert": 8759,
+ "See": 8760,
+ "▁Central": 8761,
+ "▁batter": 8762,
+ "▁signed": 8763,
+ "TS": 8764,
+ "oni": 8765,
+ "▁prepared": 8766,
+ "gate": 8767,
+ "▁Care": 8768,
+ "care": 8769,
+ "▁supply": 8770,
+ "Exp": 8771,
+ "bolds": 8772,
+ "▁trail": 8773,
+ "▁fish": 8774,
+ "▁units": 8775,
+ "venue": 8776,
+ "хи": 8777,
+ "▁Wood": 8778,
+ "▁category": 8779,
+ "▁ble": 8780,
+ "▁override": 8781,
+ "foo": 8782,
+ "▁influence": 8783,
+ "enth": 8784,
+ "rij": 8785,
+ "▁adapt": 8786,
+ "icians": 8787,
+ "deleted": 8788,
+ "▁vision": 8789,
+ "ctrl": 8790,
+ "Lambda": 8791,
+ "tp": 8792,
+ "mond": 8793,
+ "aturday": 8794,
+ "normal": 8795,
+ "▁thousand": 8796,
+ "▁Profess": 8797,
+ "▁disease": 8798,
+ "clip": 8799,
+ "▁гра": 8800,
+ "boldsymbol": 8801,
+ "OB": 8802,
+ "▁challenge": 8803,
+ "▁motion": 8804,
+ "▁whis": 8805,
+ "▁leaders": 8806,
+ "▁colon": 8807,
+ "▁suit": 8808,
+ "mid": 8809,
+ "ampion": 8810,
+ "ág": 8811,
+ "▁views": 8812,
+ "▁appears": 8813,
+ "ancel": 8814,
+ "▁zwe": 8815,
+ "IST": 8816,
+ "▁leaves": 8817,
+ "▁enh": 8818,
+ "Active": 8819,
+ "▁dit": 8820,
+ "ificate": 8821,
+ "matrix": 8822,
+ "Expression": 8823,
+ "Reader": 8824,
+ "▁mental": 8825,
+ "embre": 8826,
+ "▁decor": 8827,
+ "arts": 8828,
+ "▁vent": 8829,
+ "nel": 8830,
+ "lines": 8831,
+ "upid": 8832,
+ "erved": 8833,
+ "▁boys": 8834,
+ "аль": 8835,
+ "MOD": 8836,
+ "isl": 8837,
+ "▁[[": 8838,
+ "phy": 8839,
+ "▁..": 8840,
+ "▁agent": 8841,
+ "▁Services": 8842,
+ "▁iron": 8843,
+ "▁components": 8844,
+ "▁fre": 8845,
+ "ictionary": 8846,
+ "▁tests": 8847,
+ ".~\\": 8848,
+ "obs": 8849,
+ "▁Ми": 8850,
+ "▁обла": 8851,
+ "▁assess": 8852,
+ "▁Friday": 8853,
+ "▁weather": 8854,
+ "kg": 8855,
+ "стра": 8856,
+ ".}": 8857,
+ "endant": 8858,
+ "anna": 8859,
+ "▁Japanese": 8860,
+ "cmp": 8861,
+ "▁Army": 8862,
+ "onym": 8863,
+ "▁relax": 8864,
+ "dates": 8865,
+ "▁Russian": 8866,
+ "▁excellent": 8867,
+ "'))": 8868,
+ "ILITY": 8869,
+ "▁showing": 8870,
+ "▁Daniel": 8871,
+ "мя": 8872,
+ "▁Main": 8873,
+ "Phi": 8874,
+ "▁Rock": 8875,
+ "▁grew": 8876,
+ "▁yield": 8877,
+ "ière": 8878,
+ "seg": 8879,
+ "}}$": 8880,
+ "▁strict": 8881,
+ "▁vehicle": 8882,
+ "UD": 8883,
+ "AF": 8884,
+ "Sw": 8885,
+ "▁chest": 8886,
+ "▁officer": 8887,
+ "▁ear": 8888,
+ "HER": 8889,
+ "noon": 8890,
+ "▁journey": 8891,
+ "NT": 8892,
+ "▁divers": 8893,
+ "▁Finally": 8894,
+ "Found": 8895,
+ "▁AS": 8896,
+ "rik": 8897,
+ "▁constr": 8898,
+ "▁sust": 8899,
+ "account": 8900,
+ "▁walls": 8901,
+ "▁entirely": 8902,
+ "Iter": 8903,
+ "cha": 8904,
+ "ishes": 8905,
+ "IVE": 8906,
+ "▁prime": 8907,
+ "▁…": 8908,
+ "xe": 8909,
+ "uten": 8910,
+ "arse": 8911,
+ "▁Pa": 8912,
+ "pute": 8913,
+ "äl": 8914,
+ "▁protection": 8915,
+ "▁keys": 8916,
+ "May": 8917,
+ "Byte": 8918,
+ "Const": 8919,
+ "BL": 8920,
+ "▁пе": 8921,
+ "▁spl": 8922,
+ "▁clothes": 8923,
+ "ashed": 8924,
+ "Mark": 8925,
+ "ème": 8926,
+ "▁fait": 8927,
+ "▁introduced": 8928,
+ "unlock": 8929,
+ "▁Instead": 8930,
+ "ansion": 8931,
+ "region": 8932,
+ "▁Americans": 8933,
+ "▁indeed": 8934,
+ "widget": 8935,
+ "▁realize": 8936,
+ "▁fro": 8937,
+ "BIT": 8938,
+ "▁React": 8939,
+ "READ": 8940,
+ "asket": 8941,
+ "never": 8942,
+ "▁poll": 8943,
+ "icol": 8944,
+ "▁prev": 8945,
+ "▁hyp": 8946,
+ "▁Fur": 8947,
+ "cloud": 8948,
+ "▁Lee": 8949,
+ "pling": 8950,
+ "▁Child": 8951,
+ "▁ideal": 8952,
+ "Selector": 8953,
+ "STATUS": 8954,
+ "ucture": 8955,
+ "▁wine": 8956,
+ "▁possibly": 8957,
+ "▁putting": 8958,
+ "▁riv": 8959,
+ "▁wearing": 8960,
+ "▁Source": 8961,
+ "▁Cas": 8962,
+ "Changed": 8963,
+ "▁thanks": 8964,
+ "TIME": 8965,
+ "▁sport": 8966,
+ "▁Award": 8967,
+ "▁glad": 8968,
+ "▁Pass": 8969,
+ "▁Pos": 8970,
+ "sche": 8971,
+ "▁CD": 8972,
+ "▁afford": 8973,
+ "▁Women": 8974,
+ "▁District": 8975,
+ "▁identity": 8976,
+ "▁parties": 8977,
+ ":%": 8978,
+ "▁drag": 8979,
+ "▁mai": 8980,
+ "!(": 8981,
+ "langle": 8982,
+ "▁knowing": 8983,
+ "Project": 8984,
+ "▁regarding": 8985,
+ "▁Joseph": 8986,
+ "ге": 8987,
+ "▁Dar": 8988,
+ "▁Hor": 8989,
+ "▁animals": 8990,
+ "▁extension": 8991,
+ "ская": 8992,
+ "▁Han": 8993,
+ "btn": 8994,
+ "aciones": 8995,
+ "▁familiar": 8996,
+ "holder": 8997,
+ ":\r": 8998,
+ "stood": 8999,
+ "▁liked": 9000,
+ "CODE": 9001,
+ "▁enable": 9002,
+ "▁ped": 9003,
+ "iti": 9004,
+ "hab": 9005,
+ "DIR": 9006,
+ "▁beat": 9007,
+ "ті": 9008,
+ "▁Minister": 9009,
+ "▁py": 9010,
+ "Pat": 9011,
+ "▁exhib": 9012,
+ "▁Build": 9013,
+ "▁Field": 9014,
+ "ician": 9015,
+ "▁collabor": 9016,
+ "▁quarter": 9017,
+ "▁False": 9018,
+ "km": 9019,
+ "▁virtual": 9020,
+ "owa": 9021,
+ "▁Jon": 9022,
+ "amin": 9023,
+ "uen": 9024,
+ "▁ин": 9025,
+ "imation": 9026,
+ "oving": 9027,
+ "▁testing": 9028,
+ "sect": 9029,
+ "ITION": 9030,
+ "!\\": 9031,
+ "apy": 9032,
+ "▁transition": 9033,
+ "ository": 9034,
+ "ODO": 9035,
+ "PD": 9036,
+ "né": 9037,
+ "▁generate": 9038,
+ "▁native": 9039,
+ "▁('": 9040,
+ "▁elle": 9041,
+ "RR": 9042,
+ "▁hun": 9043,
+ "_->": 9044,
+ "agnost": 9045,
+ "▁proposed": 9046,
+ "▁Game": 9047,
+ "▁efforts": 9048,
+ "вя": 9049,
+ "tc": 9050,
+ "ск": 9051,
+ "▁intent": 9052,
+ "▁Bre": 9053,
+ "isc": 9054,
+ "▁protest": 9055,
+ "▁holds": 9056,
+ "ometry": 9057,
+ "▁Have": 9058,
+ "▁detail": 9059,
+ "▁WITHOUT": 9060,
+ "yer": 9061,
+ "▁Kon": 9062,
+ "▁noticed": 9063,
+ "▁requirements": 9064,
+ "DEBUG": 9065,
+ "kins": 9066,
+ "▁Span": 9067,
+ "▁cars": 9068,
+ "meta": 9069,
+ "▁kil": 9070,
+ "▁Bron": 9071,
+ "▁experienced": 9072,
+ "▁remind": 9073,
+ "ourse": 9074,
+ "▁Western": 9075,
+ "tered": 9076,
+ "▁devices": 9077,
+ "▁pictures": 9078,
+ "▁tut": 9079,
+ "\"`": 9080,
+ "▁impossible": 9081,
+ "▁rail": 9082,
+ "▁feels": 9083,
+ "icas": 9084,
+ "illing": 9085,
+ "▁accident": 9086,
+ "▁'@": 9087,
+ "________": 9088,
+ "▁notes": 9089,
+ "oman": 9090,
+ "Parser": 9091,
+ "▁discovered": 9092,
+ "▁Roman": 9093,
+ "▁budget": 9094,
+ "▁guide": 9095,
+ "king": 9096,
+ "▁incred": 9097,
+ "olar": 9098,
+ "enden": 9099,
+ "Desc": 9100,
+ "▁wave": 9101,
+ "бли": 9102,
+ "igt": 9103,
+ "▁restrict": 9104,
+ "▁Ret": 9105,
+ "▁mac": 9106,
+ "ур": 9107,
+ "BS": 9108,
+ "ís": 9109,
+ "▁generation": 9110,
+ "dem": 9111,
+ "alo": 9112,
+ "бра": 9113,
+ "▁ordered": 9114,
+ "drop": 9115,
+ "▁pp": 9116,
+ "▁Review": 9117,
+ "▁literally": 9118,
+ "▁Sir": 9119,
+ "▁Yeah": 9120,
+ "▁density": 9121,
+ "riz": 9122,
+ "inde": 9123,
+ "▁gain": 9124,
+ "▁panel": 9125,
+ "jet": 9126,
+ "▁Times": 9127,
+ "▁nella": 9128,
+ "▁previously": 9129,
+ "points": 9130,
+ "Send": 9131,
+ "▁Brown": 9132,
+ "each": 9133,
+ "▁trigger": 9134,
+ "ometimes": 9135,
+ "icos": 9136,
+ "GR": 9137,
+ "Panel": 9138,
+ "ogen": 9139,
+ "▁cm": 9140,
+ "ructions": 9141,
+ "▁kiss": 9142,
+ "▁solo": 9143,
+ "▁famous": 9144,
+ "ran": 9145,
+ "про": 9146,
+ "▁thro": 9147,
+ "Graph": 9148,
+ "imit": 9149,
+ "▁Value": 9150,
+ "▁starts": 9151,
+ "ipeline": 9152,
+ "hd": 9153,
+ "TC": 9154,
+ "▁discussion": 9155,
+ "▁truck": 9156,
+ "aka": 9157,
+ "Only": 9158,
+ "▁Equ": 9159,
+ "▁kö": 9160,
+ "▁Bes": 9161,
+ "▁critic": 9162,
+ "▁propos": 9163,
+ "▁batt": 9164,
+ "▁Section": 9165,
+ "Show": 9166,
+ "gp": 9167,
+ "STATE": 9168,
+ "POST": 9169,
+ "▁Nord": 9170,
+ "▁innov": 9171,
+ "▁crim": 9172,
+ "axis": 9173,
+ "▁Turn": 9174,
+ "conn": 9175,
+ "Runtime": 9176,
+ "▁remaining": 9177,
+ "oston": 9178,
+ "▁Э": 9179,
+ "▁windows": 9180,
+ "▁Royal": 9181,
+ "▁vide": 9182,
+ "PP": 9183,
+ "chron": 9184,
+ "▁san": 9185,
+ "▁rise": 9186,
+ "▁delle": 9187,
+ "▁Dur": 9188,
+ "▁rapid": 9189,
+ "cert": 9190,
+ "LA": 9191,
+ "edge": 9192,
+ "▁\\]": 9193,
+ "▁entered": 9194,
+ "▁laws": 9195,
+ "▁photo": 9196,
+ "▁applications": 9197,
+ "▁Berlin": 9198,
+ "▁arrest": 9199,
+ "▁federal": 9200,
+ "▁Russia": 9201,
+ "▁usual": 9202,
+ "▁raw": 9203,
+ "▁più": 9204,
+ "être": 9205,
+ "JSON": 9206,
+ "SION": 9207,
+ "xture": 9208,
+ "istent": 9209,
+ "▁Power": 9210,
+ "Bit": 9211,
+ "▁capacity": 9212,
+ "▁cards": 9213,
+ "UID": 9214,
+ "iments": 9215,
+ "▁dar": 9216,
+ "▁Chicago": 9217,
+ "▁comfortable": 9218,
+ "tip": 9219,
+ "bas": 9220,
+ "▁mu": 9221,
+ "▁enemy": 9222,
+ "yan": 9223,
+ "▁фи": 9224,
+ "▁updated": 9225,
+ "ango": 9226,
+ "Ev": 9227,
+ "Effect": 9228,
+ "osing": 9229,
+ "rence": 9230,
+ "▁Congress": 9231,
+ "▁defe": 9232,
+ "▁ip": 9233,
+ "▁tout": 9234,
+ "▁freedom": 9235,
+ "▁ao": 9236,
+ "▁Therefore": 9237,
+ "Edit": 9238,
+ "▁Virgin": 9239,
+ "REE": 9240,
+ "argo": 9241,
+ "▁Dam": 9242,
+ "▁traffic": 9243,
+ "ños": 9244,
+ "▁alle": 9245,
+ "▁depth": 9246,
+ "Now": 9247,
+ "▁sides": 9248,
+ "▁годи": 9249,
+ "Descriptor": 9250,
+ "▁artikel": 9251,
+ "▁narrow": 9252,
+ "___": 9253,
+ "kw": 9254,
+ "uto": 9255,
+ "▁Facebook": 9256,
+ "tegr": 9257,
+ "boolean": 9258,
+ "nik": 9259,
+ "bd": 9260,
+ "Track": 9261,
+ "▁gran": 9262,
+ "reshold": 9263,
+ "вет": 9264,
+ "wrap": 9265,
+ "▁noise": 9266,
+ "igu": 9267,
+ "▁Bon": 9268,
+ "▁wy": 9269,
+ "linux": 9270,
+ "cks": 9271,
+ "▁fans": 9272,
+ "▁mach": 9273,
+ "▁prices": 9274,
+ "év": 9275,
+ "outs": 9276,
+ "standing": 9277,
+ "▁categ": 9278,
+ ";\\": 9279,
+ "▁decre": 9280,
+ "▁Saturday": 9281,
+ "▁menu": 9282,
+ "▁Nov": 9283,
+ "▁Yet": 9284,
+ "▁так": 9285,
+ "liche": 9286,
+ "▁Academ": 9287,
+ "▁communication": 9288,
+ "using": 9289,
+ "▁Society": 9290,
+ "▁nuc": 9291,
+ "pective": 9292,
+ "orial": 9293,
+ "▁afraid": 9294,
+ "▁animal": 9295,
+ "▁turning": 9296,
+ "dst": 9297,
+ "mathfrak": 9298,
+ "lers": 9299,
+ "▁lots": 9300,
+ "▁á": 9301,
+ "▁Tra": 9302,
+ "np": 9303,
+ "▁rose": 9304,
+ "▁GL": 9305,
+ "▁helping": 9306,
+ "▁winter": 9307,
+ "▁ком": 9308,
+ "Mock": 9309,
+ "▁investment": 9310,
+ "Use": 9311,
+ "▁Canad": 9312,
+ "нд": 9313,
+ "Copy": 9314,
+ "▁fly": 9315,
+ "SER": 9316,
+ "▁Far": 9317,
+ "▁Ros": 9318,
+ "amil": 9319,
+ "▁fighting": 9320,
+ "▁religious": 9321,
+ "super": 9322,
+ "screen": 9323,
+ "▁furn": 9324,
+ "▁surprised": 9325,
+ "▁replied": 9326,
+ "Activity": 9327,
+ "▁Down": 9328,
+ "▁insert": 9329,
+ "▁Olymp": 9330,
+ "▁pointed": 9331,
+ "▁Card": 9332,
+ "driver": 9333,
+ "▁Da": 9334,
+ "!--": 9335,
+ "roud": 9336,
+ "undo": 9337,
+ "▁messages": 9338,
+ "▁Point": 9339,
+ "VM": 9340,
+ "▁plane": 9341,
+ "xc": 9342,
+ "▁television": 9343,
+ "ён": 9344,
+ "▁thousands": 9345,
+ "▁cris": 9346,
+ "▁delay": 9347,
+ "▁Next": 9348,
+ "▁nombre": 9349,
+ "▁tu": 9350,
+ "▁skip": 9351,
+ "road": 9352,
+ "istration": 9353,
+ "▁tur": 9354,
+ "▁Develop": 9355,
+ "▁Па": 9356,
+ "▁дру": 9357,
+ "▁wonderful": 9358,
+ ">&": 9359,
+ "▁Liber": 9360,
+ "▁scope": 9361,
+ "▁manage": 9362,
+ "▁dass": 9363,
+ "▁recall": 9364,
+ "PM": 9365,
+ "▁relevant": 9366,
+ "▁Earth": 9367,
+ "▁как": 9368,
+ "▁apr": 9369,
+ "▁ASS": 9370,
+ "ién": 9371,
+ "▁SH": 9372,
+ "oom": 9373,
+ "itet": 9374,
+ "none": 9375,
+ "asi": 9376,
+ "▁motor": 9377,
+ "▁Show": 9378,
+ "nb": 9379,
+ "▁factors": 9380,
+ "▁forest": 9381,
+ "▁вре": 9382,
+ "thm": 9383,
+ "▁municip": 9384,
+ "▁turns": 9385,
+ "▁Division": 9386,
+ "EC": 9387,
+ "▁disappe": 9388,
+ "structor": 9389,
+ "▁somewhere": 9390,
+ "▁African": 9391,
+ "▁Institute": 9392,
+ "Grid": 9393,
+ "▁teacher": 9394,
+ "uries": 9395,
+ "▁respectively": 9396,
+ "▁SD": 9397,
+ "▁alive": 9398,
+ "▁pou": 9399,
+ "▁Water": 9400,
+ "фе": 9401,
+ "▁changing": 9402,
+ "▁afternoon": 9403,
+ "▁orders": 9404,
+ "Ret": 9405,
+ "Pointer": 9406,
+ "▁sav": 9407,
+ "erg": 9408,
+ "oked": 9409,
+ "essions": 9410,
+ "▁Fire": 9411,
+ "aret": 9412,
+ "imm": 9413,
+ "▁desire": 9414,
+ "▁що": 9415,
+ "▁Design": 9416,
+ "uture": 9417,
+ "▁Office": 9418,
+ "▁cmd": 9419,
+ "▁eating": 9420,
+ "Network": 9421,
+ "▁rough": 9422,
+ "operator": 9423,
+ "IGN": 9424,
+ "▁sports": 9425,
+ "▁weren": 9426,
+ "▁noted": 9427,
+ "▁twice": 9428,
+ "III": 9429,
+ "▁anx": 9430,
+ "▁elim": 9431,
+ "▁ав": 9432,
+ "▁io": 9433,
+ "▁speech": 9434,
+ "▁condu": 9435,
+ "elles": 9436,
+ "idade": 9437,
+ "▁advance": 9438,
+ "RI": 9439,
+ "oca": 9440,
+ "/\\": 9441,
+ "apshot": 9442,
+ "▁tail": 9443,
+ "models": 9444,
+ "ogy": 9445,
+ "▁Jeff": 9446,
+ "iration": 9447,
+ "▁Kore": 9448,
+ "▁leads": 9449,
+ "bat": 9450,
+ "Adapter": 9451,
+ "category": 9452,
+ "angular": 9453,
+ "▁saved": 9454,
+ "▁uniform": 9455,
+ "▁né": 9456,
+ "▁businesses": 9457,
+ "Hist": 9458,
+ "▁ар": 9459,
+ "domain": 9460,
+ "▁Si": 9461,
+ "raise": 9462,
+ "▁warn": 9463,
+ "hetic": 9464,
+ "▁Gro": 9465,
+ ")).": 9466,
+ "}>": 9467,
+ "зе": 9468,
+ "▁Amazon": 9469,
+ "▁Organ": 9470,
+ "▁Lake": 9471,
+ "▁agreement": 9472,
+ "xa": 9473,
+ "▁perman": 9474,
+ "▁containing": 9475,
+ "▁strange": 9476,
+ "сті": 9477,
+ "▁stupid": 9478,
+ "▁speaking": 9479,
+ "▁Internet": 9480,
+ "prefix": 9481,
+ "esc": 9482,
+ "Assert": 9483,
+ "prote": 9484,
+ "▁manner": 9485,
+ "▁Sz": 9486,
+ "unte": 9487,
+ "iot": 9488,
+ "Profile": 9489,
+ "oven": 9490,
+ "▁formed": 9491,
+ "▁lit": 9492,
+ "▁economy": 9493,
+ "▁cz": 9494,
+ "wid": 9495,
+ "REQ": 9496,
+ "▁chosen": 9497,
+ "▁Produ": 9498,
+ "oster": 9499,
+ "stances": 9500,
+ "awa": 9501,
+ "▁Ren": 9502,
+ "▁confirm": 9503,
+ "▁Бо": 9504,
+ "▁billion": 9505,
+ "▁déc": 9506,
+ "ých": 9507,
+ "▁illustr": 9508,
+ "TIES": 9509,
+ "▁Pub": 9510,
+ "▁ban": 9511,
+ "aded": 9512,
+ "ahn": 9513,
+ "▁Cath": 9514,
+ "nonumber": 9515,
+ "▁worst": 9516,
+ "▁Ме": 9517,
+ "▁suggested": 9518,
+ "stats": 9519,
+ "▁cant": 9520,
+ "▁align": 9521,
+ "kappa": 9522,
+ "▁hen": 9523,
+ "▁initi": 9524,
+ "'])": 9525,
+ "BI": 9526,
+ "▁garden": 9527,
+ "▁secure": 9528,
+ "▁\\[": 9529,
+ "handler": 9530,
+ "elli": 9531,
+ "ldots": 9532,
+ "secut": 9533,
+ "▁extended": 9534,
+ "}-": 9535,
+ "anie": 9536,
+ "▁Find": 9537,
+ "▁Museum": 9538,
+ "▁Conne": 9539,
+ "yy": 9540,
+ "▁passion": 9541,
+ "akers": 9542,
+ "ahr": 9543,
+ "ologies": 9544,
+ "▁equation": 9545,
+ "▁occasion": 9546,
+ "Let": 9547,
+ "']['": 9548,
+ "Print": 9549,
+ "anes": 9550,
+ "iente": 9551,
+ "▁Today": 9552,
+ "LECT": 9553,
+ "▁Af": 9554,
+ ",,": 9555,
+ "▁Та": 9556,
+ "▁```": 9557,
+ "even": 9558,
+ "sin": 9559,
+ "urer": 9560,
+ "▁°": 9561,
+ "otimes": 9562,
+ "▁IO": 9563,
+ "▁poet": 9564,
+ "()));": 9565,
+ "▁−": 9566,
+ "▁adopt": 9567,
+ "phere": 9568,
+ "#[": 9569,
+ "▁centre": 9570,
+ "oves": 9571,
+ "▁ans": 9572,
+ "dp": 9573,
+ "▁Kir": 9574,
+ "▁applicable": 9575,
+ "fp": 9576,
+ "▁visual": 9577,
+ "▁okay": 9578,
+ "oro": 9579,
+ "▁opportunities": 9580,
+ "Repository": 9581,
+ "▁ll": 9582,
+ "▁Rod": 9583,
+ "▁shel": 9584,
+ "▁launch": 9585,
+ "▁conven": 9586,
+ "▁Spe": 9587,
+ "Amer": 9588,
+ "▁cette": 9589,
+ "Cond": 9590,
+ "dep": 9591,
+ "Own": 9592,
+ "▁hook": 9593,
+ "▁dict": 9594,
+ "▁Those": 9595,
+ "▁fellow": 9596,
+ "▁philosoph": 9597,
+ "vin": 9598,
+ "ferences": 9599,
+ "hav": 9600,
+ "▁adding": 9601,
+ "iverse": 9602,
+ "game": 9603,
+ "▁Blue": 9604,
+ "▁clin": 9605,
+ "note": 9606,
+ "▁Ram": 9607,
+ "мер": 9608,
+ "covery": 9609,
+ "ña": 9610,
+ "▁би": 9611,
+ "▁fashion": 9612,
+ "▁broke": 9613,
+ "▁'\\": 9614,
+ "▁reader": 9615,
+ "ное": 9616,
+ "ности": 9617,
+ "▁payment": 9618,
+ "▁Lic": 9619,
+ "▁lips": 9620,
+ "▁academ": 9621,
+ "▁Mot": 9622,
+ "ells": 9623,
+ "CHECK": 9624,
+ "▁ру": 9625,
+ "▁MS": 9626,
+ "Editor": 9627,
+ "▁zone": 9628,
+ "iture": 9629,
+ "▁IT": 9630,
+ "runtime": 9631,
+ "▁proceed": 9632,
+ "лов": 9633,
+ "▁Maria": 9634,
+ "olver": 9635,
+ "▁Thanks": 9636,
+ "▁shouldn": 9637,
+ "▁Joh": 9638,
+ "▁Model": 9639,
+ "▁Sov": 9640,
+ "!'": 9641,
+ "Di": 9642,
+ "▁cancer": 9643,
+ "Ident": 9644,
+ "▁exchange": 9645,
+ "iller": 9646,
+ "inf": 9647,
+ "LEN": 9648,
+ "(){": 9649,
+ "aga": 9650,
+ "\"],": 9651,
+ "uh": 9652,
+ "▁Ken": 9653,
+ "▁photos": 9654,
+ "▁tiny": 9655,
+ "▁gent": 9656,
+ "ül": 9657,
+ "▁Take": 9658,
+ "idel": 9659,
+ "outing": 9660,
+ "Internal": 9661,
+ "▁cells": 9662,
+ "ним": 9663,
+ "hard": 9664,
+ "▁Town": 9665,
+ "obe": 9666,
+ "plex": 9667,
+ "тер": 9668,
+ "tons": 9669,
+ "▁concentr": 9670,
+ "mock": 9671,
+ "vc": 9672,
+ "áz": 9673,
+ "▁Championship": 9674,
+ "▁бе": 9675,
+ "??": 9676,
+ "éri": 9677,
+ "aly": 9678,
+ "▁Ц": 9679,
+ "ierte": 9680,
+ "▁totally": 9681,
+ "▁Auf": 9682,
+ "▁ourselves": 9683,
+ "▁Self": 9684,
+ "Forms": 9685,
+ "ighter": 9686,
+ "▁island": 9687,
+ "fmt": 9688,
+ "▁rc": 9689,
+ "▁tells": 9690,
+ "BB": 9691,
+ "dit": 9692,
+ "▁variables": 9693,
+ "▁intended": 9694,
+ "izont": 9695,
+ "▁plays": 9696,
+ "dam": 9697,
+ "seq": 9698,
+ "▁Sup": 9699,
+ "▁cultural": 9700,
+ "▁scream": 9701,
+ "__,": 9702,
+ "cipl": 9703,
+ "Timeout": 9704,
+ "▁ж": 9705,
+ "orte": 9706,
+ "▁replaced": 9707,
+ "EM": 9708,
+ "▁abandon": 9709,
+ "▁Special": 9710,
+ "ellen": 9711,
+ "▁Bru": 9712,
+ "irmed": 9713,
+ "Te": 9714,
+ "olt": 9715,
+ "ju": 9716,
+ "Argument": 9717,
+ "▁neut": 9718,
+ "scape": 9719,
+ "▁Ray": 9720,
+ "▁Polit": 9721,
+ "▁crowd": 9722,
+ "▁Windows": 9723,
+ "iego": 9724,
+ "▁escape": 9725,
+ "▁Apache": 9726,
+ "sync": 9727,
+ "eben": 9728,
+ "ifies": 9729,
+ "ether": 9730,
+ "Meta": 9731,
+ "▁biggest": 9732,
+ "Game": 9733,
+ "▁transaction": 9734,
+ "Env": 9735,
+ "▁Мо": 9736,
+ "▁plenty": 9737,
+ "▁mel": 9738,
+ "пре": 9739,
+ "▁motiv": 9740,
+ "▁ор": 9741,
+ "organ": 9742,
+ "▁mock": 9743,
+ "▁$_": 9744,
+ "ене": 9745,
+ "▁Number": 9746,
+ "cknow": 9747,
+ "▁Update": 9748,
+ "zero": 9749,
+ "▁surprise": 9750,
+ "cean": 9751,
+ "pdf": 9752,
+ "Global": 9753,
+ "▁attend": 9754,
+ "▁fond": 9755,
+ "▁understood": 9756,
+ "Nav": 9757,
+ "▁Mic": 9758,
+ "=$": 9759,
+ "oking": 9760,
+ "▁Stadium": 9761,
+ "Close": 9762,
+ "▁competition": 9763,
+ "▁soldiers": 9764,
+ "▁OP": 9765,
+ "agne": 9766,
+ "▁Anton": 9767,
+ "Main": 9768,
+ "ák": 9769,
+ "▁#[": 9770,
+ "▁Commit": 9771,
+ "pyx": 9772,
+ "▁east": 9773,
+ "▁Order": 9774,
+ "Float": 9775,
+ "▁accepted": 9776,
+ "▁monitor": 9777,
+ "▁pad": 9778,
+ "onic": 9779,
+ "▁pushed": 9780,
+ "▁replace": 9781,
+ "CRE": 9782,
+ "▁ride": 9783,
+ "found": 9784,
+ "=%": 9785,
+ "вой": 9786,
+ "▁matches": 9787,
+ "▁Lie": 9788,
+ "▁experiences": 9789,
+ "Pool": 9790,
+ "ups": 9791,
+ "AV": 9792,
+ "▁existence": 9793,
+ "▁thin": 9794,
+ "▁magn": 9795,
+ "COMP": 9796,
+ "home": 9797,
+ "▁ni": 9798,
+ "▁wurden": 9799,
+ "лав": 9800,
+ "▁teeth": 9801,
+ "▁Stan": 9802,
+ "appro": 9803,
+ "anny": 9804,
+ "ifts": 9805,
+ "▁unknown": 9806,
+ "▁homes": 9807,
+ "▁entity": 9808,
+ "cie": 9809,
+ "ление": 9810,
+ "iar": 9811,
+ "▁compliance": 9812,
+ "▁focused": 9813,
+ "uzz": 9814,
+ "=\\\"": 9815,
+ "components": 9816,
+ "Attr": 9817,
+ "allery": 9818,
+ "▁identify": 9819,
+ "Ok": 9820,
+ "pie": 9821,
+ "▁Still": 9822,
+ "▁offering": 9823,
+ "▁busy": 9824,
+ "ctl": 9825,
+ "itors": 9826,
+ "▁concerned": 9827,
+ "▁brown": 9828,
+ "clk": 9829,
+ "Selected": 9830,
+ "▁Block": 9831,
+ "▁egy": 9832,
+ "icing": 9833,
+ "▁URL": 9834,
+ "▁topic": 9835,
+ "▁Product": 9836,
+ "▁чи": 9837,
+ "▁trial": 9838,
+ "▁weekend": 9839,
+ "lu": 9840,
+ "▁IV": 9841,
+ "▁Egy": 9842,
+ "xC": 9843,
+ "▁nove": 9844,
+ "▁lett": 9845,
+ "enne": 9846,
+ "()).": 9847,
+ ".**": 9848,
+ "▁promise": 9849,
+ "election": 9850,
+ "Auth": 9851,
+ "rv": 9852,
+ "ril": 9853,
+ "▁conduct": 9854,
+ "▁maintain": 9855,
+ "▁boat": 9856,
+ "▁opposite": 9857,
+ "spin": 9858,
+ "webpack": 9859,
+ "anta": 9860,
+ "▁orient": 9861,
+ "▁suc": 9862,
+ "▁exercise": 9863,
+ "▁efficient": 9864,
+ "▁tradition": 9865,
+ "▁zw": 9866,
+ "▁Sud": 9867,
+ "going": 9868,
+ "▁Pier": 9869,
+ "inv": 9870,
+ "ipes": 9871,
+ "ensuremath": 9872,
+ "▁conver": 9873,
+ "creen": 9874,
+ "▁terror": 9875,
+ "▁Dou": 9876,
+ "▁invalid": 9877,
+ "ceived": 9878,
+ "▁Arab": 9879,
+ "▁wire": 9880,
+ "application": 9881,
+ "shift": 9882,
+ "Generic": 9883,
+ "▁Plan": 9884,
+ "▁Wall": 9885,
+ "▁directory": 9886,
+ "▁egg": 9887,
+ "▁wealth": 9888,
+ "random": 9889,
+ "attribute": 9890,
+ "▁hide": 9891,
+ "Serial": 9892,
+ "cam": 9893,
+ "▁ital": 9894,
+ "▁Line": 9895,
+ "▁CHECK": 9896,
+ "ployment": 9897,
+ "▁massive": 9898,
+ "▁extract": 9899,
+ "chain": 9900,
+ "Rest": 9901,
+ "▁Las": 9902,
+ "▁bear": 9903,
+ "▁links": 9904,
+ "▁newsp": 9905,
+ "▁FC": 9906,
+ "Card": 9907,
+ "aks": 9908,
+ "▁visible": 9909,
+ "▁Marc": 9910,
+ "▁Boston": 9911,
+ "▁reserved": 9912,
+ "▁roof": 9913,
+ "licenses": 9914,
+ "dc": 9915,
+ "▁Information": 9916,
+ "▁witness": 9917,
+ "Sk": 9918,
+ "*),": 9919,
+ "Scope": 9920,
+ "'];": 9921,
+ "▁Mir": 9922,
+ "uding": 9923,
+ "▁trend": 9924,
+ "rep": 9925,
+ "▁musical": 9926,
+ "▁neither": 9927,
+ "▁Creat": 9928,
+ "▁positions": 9929,
+ "LC": 9930,
+ "ridge": 9931,
+ "▁officers": 9932,
+ "▁violence": 9933,
+ "▁Tem": 9934,
+ "▁Sus": 9935,
+ "▁Way": 9936,
+ "After": 9937,
+ "acket": 9938,
+ "▁Sou": 9939,
+ "acer": 9940,
+ "||": 9941,
+ "▁remark": 9942,
+ "water": 9943,
+ "ně": 9944,
+ "▁Са": 9945,
+ "▁sed": 9946,
+ "Each": 9947,
+ "▁photograph": 9948,
+ "▁letters": 9949,
+ "▁invent": 9950,
+ "▁Mas": 9951,
+ "▁songs": 9952,
+ "ól": 9953,
+ "kind": 9954,
+ "▁Non": 9955,
+ "▁dust": 9956,
+ "**:": 9957,
+ "nabla": 9958,
+ ".\",": 9959,
+ "Lock": 9960,
+ "▁До": 9961,
+ "▁cluster": 9962,
+ "loss": 9963,
+ "▁ASSERT": 9964,
+ "fall": 9965,
+ "▁reject": 9966,
+ "▁Spring": 9967,
+ "▁wedding": 9968,
+ "▁grav": 9969,
+ "ression": 9970,
+ "limit": 9971,
+ "RES": 9972,
+ "]}": 9973,
+ "▁listed": 9974,
+ "▁Tele": 9975,
+ "hline": 9976,
+ "▁chief": 9977,
+ "MEM": 9978,
+ "дар": 9979,
+ "▁expensive": 9980,
+ "trace": 9981,
+ "▁Rog": 9982,
+ "▁Coll": 9983,
+ "▁Author": 9984,
+ "▁Board": 9985,
+ "▁Capt": 9986,
+ "TEXT": 9987,
+ "▁recon": 9988,
+ "esta": 9989,
+ "▁properly": 9990,
+ "▁&\\": 9991,
+ "leton": 9992,
+ "iker": 9993,
+ "Gu": 9994,
+ "▁Kom": 9995,
+ "oco": 9996,
+ "▁anymore": 9997,
+ "▁taste": 9998,
+ "▁Santa": 9999,
+ "gex": 10000,
+ "▁Secret": 10001,
+ "▁talent": 10002,
+ "▁moments": 10003,
+ "▁Ba": 10004,
+ "▁extr": 10005,
+ "▁Commission": 10006,
+ "▁modify": 10007,
+ "▁Figure": 10008,
+ "▁domin": 10009,
+ "▁plot": 10010,
+ "enger": 10011,
+ "utch": 10012,
+ "▁cities": 10013,
+ "▁nut": 10014,
+ "profile": 10015,
+ "▁Stat": 10016,
+ "▁nodes": 10017,
+ "▁ns": 10018,
+ "essages": 10019,
+ "impl": 10020,
+ "icker": 10021,
+ "▁examples": 10022,
+ "abeth": 10023,
+ "▁stated": 10024,
+ "fire": 10025,
+ "bul": 10026,
+ "▁dangerous": 10027,
+ "▁Pay": 10028,
+ "▁Gre": 10029,
+ "▁Monday": 10030,
+ "esome": 10031,
+ "igan": 10032,
+ "rund": 10033,
+ "prise": 10034,
+ "fail": 10035,
+ "▁Never": 10036,
+ "Av": 10037,
+ "▁linear": 10038,
+ "▁ul": 10039,
+ "WAR": 10040,
+ "рен": 10041,
+ "▁AT": 10042,
+ "▁dop": 10043,
+ "▁nou": 10044,
+ "Dest": 10045,
+ "▁claims": 10046,
+ "enda": 10047,
+ "▁crazy": 10048,
+ "gel": 10049,
+ "oggle": 10050,
+ "▁representation": 10051,
+ "inen": 10052,
+ "▁alternative": 10053,
+ "DM": 10054,
+ "ABILITY": 10055,
+ "faces": 10056,
+ "▁doors": 10057,
+ "ativ": 10058,
+ "Look": 10059,
+ "▁JSON": 10060,
+ "▁appearance": 10061,
+ "бря": 10062,
+ "SQL": 10063,
+ "▁silence": 10064,
+ "udo": 10065,
+ "▁Director": 10066,
+ "Statement": 10067,
+ "selected": 10068,
+ "high": 10069,
+ "prime": 10070,
+ "▁ignore": 10071,
+ "▁colors": 10072,
+ "ushing": 10073,
+ "▁virt": 10074,
+ "manager": 10075,
+ "▁remote": 10076,
+ "ło": 10077,
+ "small": 10078,
+ "▁crime": 10079,
+ "rb": 10080,
+ "▁creation": 10081,
+ "▁flight": 10082,
+ "▁Sign": 10083,
+ "ILE": 10084,
+ "▁DO": 10085,
+ "comment": 10086,
+ "▁Cost": 10087,
+ ".__": 10088,
+ "▁Cop": 10089,
+ "▁vom": 10090,
+ "▁Science": 10091,
+ "ления": 10092,
+ "oop": 10093,
+ "interface": 10094,
+ "▁WARRANTIES": 10095,
+ "▁Page": 10096,
+ "******": 10097,
+ "ском": 10098,
+ "TRUE": 10099,
+ "▁repeated": 10100,
+ "▁его": 10101,
+ "шо": 10102,
+ "▁roz": 10103,
+ "Pe": 10104,
+ "▁ISBN": 10105,
+ "irts": 10106,
+ "poses": 10107,
+ "})$": 10108,
+ "▁І": 10109,
+ "children": 10110,
+ "bles": 10111,
+ "ECT": 10112,
+ "▁iz": 10113,
+ "▁builder": 10114,
+ "▁Media": 10115,
+ "iat": 10116,
+ "▁contrast": 10117,
+ "”,": 10118,
+ "▁Link": 10119,
+ "▁Education": 10120,
+ "▁joint": 10121,
+ "▁external": 10122,
+ "▁роз": 10123,
+ "▁bits": 10124,
+ "FORM": 10125,
+ "erman": 10126,
+ "wp": 10127,
+ "▁Mike": 10128,
+ "▁Master": 10129,
+ "▁senior": 10130,
+ "▁Nav": 10131,
+ "▁recorded": 10132,
+ "eling": 10133,
+ "esh": 10134,
+ "fx": 10135,
+ "кан": 10136,
+ "▁tall": 10137,
+ "▁Johnson": 10138,
+ "▁sono": 10139,
+ "▁anche": 10140,
+ "icken": 10141,
+ "loop": 10142,
+ "iciency": 10143,
+ "emporary": 10144,
+ "▁Does": 10145,
+ "▁relation": 10146,
+ "мы": 10147,
+ "was": 10148,
+ "low": 10149,
+ "ichte": 10150,
+ "▁Jones": 10151,
+ "▁bedroom": 10152,
+ "DIS": 10153,
+ "▁magnet": 10154,
+ "▁Engine": 10155,
+ "▁feelings": 10156,
+ "GC": 10157,
+ "▁torn": 10158,
+ "▁relationships": 10159,
+ "▁Ре": 10160,
+ "▁proud": 10161,
+ "▁twe": 10162,
+ "oval": 10163,
+ "▁waste": 10164,
+ "▁reduced": 10165,
+ "ilton": 10166,
+ "BP": 10167,
+ "▁forgot": 10168,
+ "▁bodies": 10169,
+ "▁Haw": 10170,
+ "lag": 10171,
+ "▁www": 10172,
+ "door": 10173,
+ "▁sufficient": 10174,
+ "▁dollars": 10175,
+ "Len": 10176,
+ "▁talked": 10177,
+ "▁bond": 10178,
+ "▁Bor": 10179,
+ "}}{": 10180,
+ "rod": 10181,
+ "Password": 10182,
+ "quare": 10183,
+ "▁lights": 10184,
+ "eren": 10185,
+ "▁thirty": 10186,
+ "NC": 10187,
+ "▁TODO": 10188,
+ "▁respond": 10189,
+ "ких": 10190,
+ "direct": 10191,
+ "ação": 10192,
+ "▁heav": 10193,
+ "Media": 10194,
+ "exit": 10195,
+ "License": 10196,
+ "`.": 10197,
+ "▁mixed": 10198,
+ "▁desk": 10199,
+ "▁teaching": 10200,
+ "▁maj": 10201,
+ "▁nerv": 10202,
+ "inations": 10203,
+ "typeof": 10204,
+ "▁coast": 10205,
+ "▁же": 10206,
+ "▁beside": 10207,
+ "ummy": 10208,
+ "Doc": 10209,
+ "▁schedule": 10210,
+ "▁recover": 10211,
+ "▁Further": 10212,
+ "▁steel": 10213,
+ "boot": 10214,
+ "▁Perhaps": 10215,
+ "▁съ": 10216,
+ "▁Os": 10217,
+ "rick": 10218,
+ "▁Ви": 10219,
+ "Support": 10220,
+ "▁(_": 10221,
+ "nil": 10222,
+ "pis": 10223,
+ "xpected": 10224,
+ "▁processing": 10225,
+ "Build": 10226,
+ "arian": 10227,
+ "▁icon": 10228,
+ "▁CA": 10229,
+ "wick": 10230,
+ "=(": 10231,
+ "▁algorithm": 10232,
+ "▁Young": 10233,
+ "▁Management": 10234,
+ "▁ancient": 10235,
+ "ность": 10236,
+ "oti": 10237,
+ "▁combination": 10238,
+ "world": 10239,
+ "nn": 10240,
+ "▁dram": 10241,
+ "enabled": 10242,
+ "Ac": 10243,
+ "CCESS": 10244,
+ "aration": 10245,
+ "▁blocks": 10246,
+ "▁Angeles": 10247,
+ "▁Qual": 10248,
+ "▁succeed": 10249,
+ "network": 10250,
+ "▁oblig": 10251,
+ "springframework": 10252,
+ "▁Tre": 10253,
+ "okes": 10254,
+ "mun": 10255,
+ "▁Network": 10256,
+ "Del": 10257,
+ "▁estate": 10258,
+ "▁liqu": 10259,
+ "▁pob": 10260,
+ "▁dad": 10261,
+ "▁distinct": 10262,
+ "▁Tit": 10263,
+ "▁Lear": 10264,
+ "ferred": 10265,
+ "android": 10266,
+ "▁subsequ": 10267,
+ "▁Florida": 10268,
+ "subset": 10269,
+ "▁whisper": 10270,
+ "Vol": 10271,
+ "ulous": 10272,
+ "▁crew": 10273,
+ "▁lug": 10274,
+ "pid": 10275,
+ "ocity": 10276,
+ "skb": 10277,
+ "▁tea": 10278,
+ "ун": 10279,
+ "▁honor": 10280,
+ "▁Ins": 10281,
+ "▁gew": 10282,
+ "Details": 10283,
+ "eneath": 10284,
+ "atar": 10285,
+ "▁_{": 10286,
+ "amen": 10287,
+ "▁setup": 10288,
+ "Transaction": 10289,
+ "▁blank": 10290,
+ "Failed": 10291,
+ "job": 10292,
+ "▁pret": 10293,
+ "ße": 10294,
+ "loor": 10295,
+ "ří": 10296,
+ "ncia": 10297,
+ "▁anywhere": 10298,
+ "▁Light": 10299,
+ "▁Ak": 10300,
+ "BD": 10301,
+ "▁excited": 10302,
+ "agers": 10303,
+ "▁warning": 10304,
+ "▁processes": 10305,
+ "hu": 10306,
+ "▁youth": 10307,
+ "▁dogs": 10308,
+ "▁oct": 10309,
+ "▁nine": 10310,
+ "Writer": 10311,
+ "grid": 10312,
+ "▁importance": 10313,
+ "estic": 10314,
+ "▁carefully": 10315,
+ "master": 10316,
+ "▁decisions": 10317,
+ "▁pin": 10318,
+ "▁crack": 10319,
+ "TEST": 10320,
+ "▁Local": 10321,
+ "▁Right": 10322,
+ "▁vast": 10323,
+ "▁faster": 10324,
+ "▁institut": 10325,
+ "▁annual": 10326,
+ "LAN": 10327,
+ "▁episode": 10328,
+ "▁XV": 10329,
+ "▁delivery": 10330,
+ "tl": 10331,
+ "FP": 10332,
+ "circ": 10333,
+ "▁typically": 10334,
+ "igo": 10335,
+ "▁intel": 10336,
+ "nat": 10337,
+ "xb": 10338,
+ "стро": 10339,
+ ")-": 10340,
+ "▁Bal": 10341,
+ "▁Jos": 10342,
+ "▁gonna": 10343,
+ "▁Rest": 10344,
+ "jor": 10345,
+ "onia": 10346,
+ "orship": 10347,
+ "overy": 10348,
+ "LINE": 10349,
+ "]:": 10350,
+ "Queue": 10351,
+ "▁compare": 10352,
+ "▁apartment": 10353,
+ "▁rul": 10354,
+ "Dr": 10355,
+ "gency": 10356,
+ "▁obviously": 10357,
+ "zie": 10358,
+ "ycl": 10359,
+ "fortunately": 10360,
+ "▁stepped": 10361,
+ "▁Seg": 10362,
+ "▁Which": 10363,
+ "▁PC": 10364,
+ "▁ast": 10365,
+ "endor": 10366,
+ "▁permission": 10367,
+ "COL": 10368,
+ "▁TEST": 10369,
+ "Pay": 10370,
+ "ères": 10371,
+ "▁studied": 10372,
+ "▁accompl": 10373,
+ "role": 10374,
+ "Where": 10375,
+ "protobuf": 10376,
+ "metadata": 10377,
+ "Job": 10378,
+ "▁Four": 10379,
+ "plements": 10380,
+ "disable": 10381,
+ "▁loud": 10382,
+ "▁happening": 10383,
+ "▁Using": 10384,
+ "rog": 10385,
+ "▁depends": 10386,
+ "ím": 10387,
+ "'\\": 10388,
+ "▁taught": 10389,
+ "shared": 10390,
+ "▁attributes": 10391,
+ "▁Action": 10392,
+ "▁dess": 10393,
+ "▁houses": 10394,
+ "▁reset": 10395,
+ "▁bien": 10396,
+ "▁explicit": 10397,
+ "LOW": 10398,
+ "->_": 10399,
+ "▁PM": 10400,
+ "Category": 10401,
+ "oice": 10402,
+ "into": 10403,
+ "▁mail": 10404,
+ "▁authority": 10405,
+ "▁unable": 10406,
+ "filename": 10407,
+ "ék": 10408,
+ "лей": 10409,
+ "▁sector": 10410,
+ "appoint": 10411,
+ "▁hang": 10412,
+ "▁cel": 10413,
+ "related": 10414,
+ "itate": 10415,
+ "▁'<": 10416,
+ "amber": 10417,
+ "▁cheap": 10418,
+ "▁enabled": 10419,
+ "▁division": 10420,
+ "Any": 10421,
+ "▁hier": 10422,
+ "▁Head": 10423,
+ "ntax": 10424,
+ "uda": 10425,
+ "▁limitations": 10426,
+ "▁studio": 10427,
+ "media": 10428,
+ "▁circle": 10429,
+ "нова": 10430,
+ "▁laug": 10431,
+ "acts": 10432,
+ "▁Во": 10433,
+ "ód": 10434,
+ "pled": 10435,
+ "LOC": 10436,
+ "Expr": 10437,
+ ">:": 10438,
+ "▁prés": 10439,
+ "▁laughed": 10440,
+ "▁Three": 10441,
+ "лы": 10442,
+ "▁ends": 10443,
+ "▁fundament": 10444,
+ "▁inher": 10445,
+ "▁liv": 10446,
+ "bid": 10447,
+ "▁responsibility": 10448,
+ "▁checked": 10449,
+ "▁Pac": 10450,
+ "▁fault": 10451,
+ "▁yellow": 10452,
+ "▁salt": 10453,
+ "▁Francisco": 10454,
+ "▁^": 10455,
+ "▁ON": 10456,
+ "▁beauty": 10457,
+ "yg": 10458,
+ "▁Aff": 10459,
+ "▁Eq": 10460,
+ "▁magic": 10461,
+ "▁handler": 10462,
+ "xE": 10463,
+ "▁numerous": 10464,
+ "▁hole": 10465,
+ "▁rooms": 10466,
+ "cción": 10467,
+ "▁Arm": 10468,
+ "person": 10469,
+ "▁buildings": 10470,
+ "▁plate": 10471,
+ "bled": 10472,
+ "errors": 10473,
+ "▁Again": 10474,
+ "▁Default": 10475,
+ "▁Hard": 10476,
+ "tó": 10477,
+ "hus": 10478,
+ "▁dimension": 10479,
+ "iale": 10480,
+ "▁Mult": 10481,
+ "▁Government": 10482,
+ "Func": 10483,
+ "▁blow": 10484,
+ "▁rect": 10485,
+ "erra": 10486,
+ "connection": 10487,
+ "▁passing": 10488,
+ "ßen": 10489,
+ "phas": 10490,
+ "ensional": 10491,
+ "record": 10492,
+ "cohol": 10493,
+ "▁Harry": 10494,
+ "izontal": 10495,
+ "▁finger": 10496,
+ "▁younger": 10497,
+ "▁SC": 10498,
+ "operation": 10499,
+ "BY": 10500,
+ "heim": 10501,
+ "▁Bad": 10502,
+ "▁storm": 10503,
+ "▁Nat": 10504,
+ "▁buying": 10505,
+ "▁Sometimes": 10506,
+ "▁Ста": 10507,
+ "essed": 10508,
+ "▁damn": 10509,
+ "▁meg": 10510,
+ "umes": 10511,
+ "ünd": 10512,
+ "тра": 10513,
+ "▁silver": 10514,
+ "wd": 10515,
+ "hidden": 10516,
+ "ardo": 10517,
+ "▁communities": 10518,
+ "▁diet": 10519,
+ "otted": 10520,
+ "▁bat": 10521,
+ "ancer": 10522,
+ "▁fmt": 10523,
+ "▁Pen": 10524,
+ "▁til": 10525,
+ "Enum": 10526,
+ "PATH": 10527,
+ "▁matters": 10528,
+ "timeout": 10529,
+ "------------": 10530,
+ "kan": 10531,
+ "▁Corpor": 10532,
+ "=\"../../": 10533,
+ "▁Ale": 10534,
+ "hentication": 10535,
+ "▁complic": 10536,
+ "▁Security": 10537,
+ "OFF": 10538,
+ "Rad": 10539,
+ "apse": 10540,
+ "▁dance": 10541,
+ "▁permissions": 10542,
+ "▁warrant": 10543,
+ "▁lad": 10544,
+ "▁isol": 10545,
+ "dl": 10546,
+ "▁Au": 10547,
+ "yes": 10548,
+ "▁tv": 10549,
+ "▁provider": 10550,
+ "▁terrible": 10551,
+ "▁department": 10552,
+ "eral": 10553,
+ "▁implementation": 10554,
+ "SR": 10555,
+ "▁hearing": 10556,
+ "▁Kn": 10557,
+ "FR": 10558,
+ "tv": 10559,
+ "▁diss": 10560,
+ "FUN": 10561,
+ "▁durante": 10562,
+ "osis": 10563,
+ "▁tasks": 10564,
+ "▁Blo": 10565,
+ "вод": 10566,
+ "▁branch": 10567,
+ "▁politics": 10568,
+ "▁Elle": 10569,
+ "▁leadership": 10570,
+ "expr": 10571,
+ "▁techniques": 10572,
+ "prec": 10573,
+ "Sigma": 10574,
+ "imately": 10575,
+ "tk": 10576,
+ "achment": 10577,
+ "▁Enter": 10578,
+ "▁creative": 10579,
+ "▁зна": 10580,
+ "appy": 10581,
+ "unched": 10582,
+ "▁'',": 10583,
+ "onder": 10584,
+ "{-": 10585,
+ "NUM": 10586,
+ "▁narr": 10587,
+ "Memory": 10588,
+ "▁winning": 10589,
+ "▁Follow": 10590,
+ "*/\r": 10591,
+ "vision": 10592,
+ "resents": 10593,
+ "zione": 10594,
+ "▁latter": 10595,
+ "▁requests": 10596,
+ "▁margin": 10597,
+ "▁{\"": 10598,
+ "video": 10599,
+ "cn": 10600,
+ "▁Image": 10601,
+ "Tim": 10602,
+ "CONFIG": 10603,
+ "▁allowing": 10604,
+ "▁combined": 10605,
+ "PUT": 10606,
+ "▁instanceof": 10607,
+ "igin": 10608,
+ "▁pero": 10609,
+ "▁''": 10610,
+ "▁confidence": 10611,
+ "▁equivalent": 10612,
+ "pad": 10613,
+ "effect": 10614,
+ "RX": 10615,
+ "▁lang": 10616,
+ "strong": 10617,
+ "▁bridge": 10618,
+ "aya": 10619,
+ "▁treated": 10620,
+ "▁forth": 10621,
+ "SW": 10622,
+ "▁accounts": 10623,
+ "▁PO": 10624,
+ "▁listening": 10625,
+ "Route": 10626,
+ "()))": 10627,
+ "cpy": 10628,
+ "▁reform": 10629,
+ "▁gate": 10630,
+ "▁Walk": 10631,
+ "▁somehow": 10632,
+ "tf": 10633,
+ "▁layout": 10634,
+ "umin": 10635,
+ "▁considering": 10636,
+ "▁premi": 10637,
+ "▁Mom": 10638,
+ "athan": 10639,
+ "Gen": 10640,
+ "▁planet": 10641,
+ "amples": 10642,
+ "▁MO": 10643,
+ "shop": 10644,
+ "▁premier": 10645,
+ "▁simpl": 10646,
+ "▁segu": 10647,
+ "LY": 10648,
+ "Sum": 10649,
+ "▁tables": 10650,
+ "ska": 10651,
+ "▁ž": 10652,
+ "pd": 10653,
+ "▁sous": 10654,
+ "▁conference": 10655,
+ "▁Dat": 10656,
+ "Scroll": 10657,
+ "▁standards": 10658,
+ "▁гру": 10659,
+ "esse": 10660,
+ "▁citizens": 10661,
+ "▁occurred": 10662,
+ "▁democr": 10663,
+ "▁elev": 10664,
+ "▁Sem": 10665,
+ "ensus": 10666,
+ "headers": 10667,
+ "▁Chris": 10668,
+ "imento": 10669,
+ "kom": 10670,
+ "Cor": 10671,
+ "MIN": 10672,
+ "usher": 10673,
+ "Database": 10674,
+ "▁formal": 10675,
+ "igne": 10676,
+ "▁organizations": 10677,
+ "▁Ire": 10678,
+ "Xml": 10679,
+ "из": 10680,
+ "▁pray": 10681,
+ "▁bomb": 10682,
+ "▁mand": 10683,
+ "erts": 10684,
+ "▁clock": 10685,
+ "▁buck": 10686,
+ "вали": 10687,
+ "ensch": 10688,
+ "▁volt": 10689,
+ "▁films": 10690,
+ "▁plants": 10691,
+ "inode": 10692,
+ "Boolean": 10693,
+ "▁restaurant": 10694,
+ "ían": 10695,
+ "▁debut": 10696,
+ "pages": 10697,
+ "▁wordt": 10698,
+ "▁Ба": 10699,
+ "▁greatest": 10700,
+ "(\"/": 10701,
+ "▁copyright": 10702,
+ "▁rit": 10703,
+ "sizeof": 10704,
+ "Trace": 10705,
+ "uent": 10706,
+ "тур": 10707,
+ "▁ko": 10708,
+ ":\\": 10709,
+ "▁bigger": 10710,
+ "▁perfectly": 10711,
+ "tenance": 10712,
+ "MASK": 10713,
+ "ré": 10714,
+ "▁ett": 10715,
+ "▁nose": 10716,
+ "▁craft": 10717,
+ "iteral": 10718,
+ "▁discussed": 10719,
+ "▁Jewish": 10720,
+ "Cap": 10721,
+ "▁Unless": 10722,
+ "▁Jackson": 10723,
+ "Attributes": 10724,
+ "▁lunch": 10725,
+ "öl": 10726,
+ "atr": 10727,
+ "▁paying": 10728,
+ "Parse": 10729,
+ "()\r": 10730,
+ "lad": 10731,
+ "▁rare": 10732,
+ "▁[];": 10733,
+ "stone": 10734,
+ "▁unc": 10735,
+ "▁defense": 10736,
+ "}+": 10737,
+ "▁Global": 10738,
+ "▁Soviet": 10739,
+ "▁Australian": 10740,
+ "▁gli": 10741,
+ "variant": 10742,
+ "▁Ron": 10743,
+ "▁loan": 10744,
+ "Step": 10745,
+ "member": 10746,
+ "Sch": 10747,
+ "▁Committee": 10748,
+ "▁spending": 10749,
+ "▁Tri": 10750,
+ "▁Journal": 10751,
+ "▁sugar": 10752,
+ "elly": 10753,
+ "HTML": 10754,
+ "▁advent": 10755,
+ "wing": 10756,
+ "▁Whether": 10757,
+ "oration": 10758,
+ "▁NE": 10759,
+ "iveness": 10760,
+ "▁hav": 10761,
+ "▁conscious": 10762,
+ "een": 10763,
+ "Symbol": 10764,
+ "▁ку": 10765,
+ "Logger": 10766,
+ "▁Little": 10767,
+ "widet": 10768,
+ "ocation": 10769,
+ "pin": 10770,
+ "▁symmet": 10771,
+ "▁AD": 10772,
+ "▁posts": 10773,
+ "shal": 10774,
+ "▁Conf": 10775,
+ "▁chose": 10776,
+ "mal": 10777,
+ "ulo": 10778,
+ "▁Method": 10779,
+ "▁missed": 10780,
+ "Remove": 10781,
+ "Auto": 10782,
+ "VALUE": 10783,
+ "thlet": 10784,
+ "▁Force": 10785,
+ "pf": 10786,
+ "▁Я": 10787,
+ "late": 10788,
+ "▁pul": 10789,
+ "Pop": 10790,
+ "▁advanced": 10791,
+ "aires": 10792,
+ "ressed": 10793,
+ "AME": 10794,
+ "bell": 10795,
+ "aching": 10796,
+ "ić": 10797,
+ "echo": 10798,
+ "HS": 10799,
+ "▁funny": 10800,
+ "рии": 10801,
+ "▁eer": 10802,
+ "▁veget": 10803,
+ "▁fourth": 10804,
+ "cf": 10805,
+ "transform": 10806,
+ "▁grown": 10807,
+ "▁McC": 10808,
+ "site": 10809,
+ "▁beneath": 10810,
+ "▁shell": 10811,
+ "xd": 10812,
+ "Play": 10813,
+ "short": 10814,
+ "Role": 10815,
+ "▁religion": 10816,
+ "inator": 10817,
+ "}": 10818,
+ "▁Eliz": 10819,
+ "Microsoft": 10820,
+ "▁vez": 10821,
+ "▁рабо": 10822,
+ "reich": 10823,
+ "vet": 10824,
+ "enum": 10825,
+ "▁welcome": 10826,
+ "nament": 10827,
+ "▁jan": 10828,
+ "▁cycle": 10829,
+ "▁acknow": 10830,
+ "▁wound": 10831,
+ "idi": 10832,
+ "▁possibility": 10833,
+ "annotation": 10834,
+ "▁technical": 10835,
+ "▁fold": 10836,
+ "eh": 10837,
+ "istence": 10838,
+ "▁reply": 10839,
+ "etes": 10840,
+ "▁decades": 10841,
+ "wan": 10842,
+ "▁кра": 10843,
+ "▁Lab": 10844,
+ "▁unf": 10845,
+ "▁imper": 10846,
+ "▁bug": 10847,
+ "▁Though": 10848,
+ "throws": 10849,
+ "Visible": 10850,
+ "prev": 10851,
+ "▁Ty": 10852,
+ "▁depending": 10853,
+ "▁policies": 10854,
+ "andy": 10855,
+ "▁Italian": 10856,
+ "uma": 10857,
+ "▁signs": 10858,
+ "▁Through": 10859,
+ "бы": 10860,
+ "bot": 10861,
+ "▁publish": 10862,
+ ")**": 10863,
+ "ATTR": 10864,
+ "iral": 10865,
+ "VT": 10866,
+ "▁recognized": 10867,
+ "▁Lind": 10868,
+ "ection": 10869,
+ "▁relatively": 10870,
+ "▁Ah": 10871,
+ "▁Dig": 10872,
+ "ць": 10873,
+ "icket": 10874,
+ "▁specifically": 10875,
+ "nost": 10876,
+ "▁grass": 10877,
+ "▁causes": 10878,
+ "тво": 10879,
+ "utter": 10880,
+ "▁Festival": 10881,
+ "greg": 10882,
+ "▁weapons": 10883,
+ "▁sir": 10884,
+ "▁Virginia": 10885,
+ "login": 10886,
+ "▁schedul": 10887,
+ "ського": 10888,
+ "▁losing": 10889,
+ "▁Europ": 10890,
+ "\"><": 10891,
+ "asp": 10892,
+ "ajo": 10893,
+ "exports": 10894,
+ "▁Node": 10895,
+ "▁jako": 10896,
+ "▁ya": 10897,
+ "▁successfully": 10898,
+ "▁friendly": 10899,
+ "buff": 10900,
+ "DEFAULT": 10901,
+ "▁pregn": 10902,
+ "Required": 10903,
+ "▁binary": 10904,
+ "isting": 10905,
+ "▁stared": 10906,
+ "▁circumstances": 10907,
+ "▁хо": 10908,
+ "rei": 10909,
+ "▁Го": 10910,
+ "Transform": 10911,
+ "cnt": 10912,
+ "▁Ext": 10913,
+ "report": 10914,
+ "VERSION": 10915,
+ "▁analy": 10916,
+ "▁Marg": 10917,
+ "▁alleg": 10918,
+ "builder": 10919,
+ "ToString": 10920,
+ "Layer": 10921,
+ "íst": 10922,
+ "Prop": 10923,
+ "▁Emp": 10924,
+ "}]": 10925,
+ "▁selling": 10926,
+ "▁queue": 10927,
+ "▁seriously": 10928,
+ "▁Lead": 10929,
+ "textit": 10930,
+ "testing": 10931,
+ "▁Пре": 10932,
+ "security": 10933,
+ "iał": 10934,
+ "ún": 10935,
+ "chip": 10936,
+ "▁candidate": 10937,
+ "▁minister": 10938,
+ "eria": 10939,
+ "▁Het": 10940,
+ "дин": 10941,
+ "▁Britain": 10942,
+ "▁barely": 10943,
+ "▁sty": 10944,
+ "▁Spanish": 10945,
+ "▁Ven": 10946,
+ "timer": 10947,
+ "ків": 10948,
+ "▁documents": 10949,
+ "('.": 10950,
+ "▁debug": 10951,
+ "▁contro": 10952,
+ "стоя": 10953,
+ "▁joy": 10954,
+ "Sn": 10955,
+ "Inv": 10956,
+ "▁protocol": 10957,
+ "▁faces": 10958,
+ "▁Despite": 10959,
+ "sed": 10960,
+ "Conf": 10961,
+ "ARG": 10962,
+ "▁evolution": 10963,
+ "▁tod": 10964,
+ "▁Promise": 10965,
+ "▁posted": 10966,
+ "Perm": 10967,
+ "bet": 10968,
+ "Ang": 10969,
+ "Just": 10970,
+ "▁rum": 10971,
+ "layer": 10972,
+ "▁behavi": 10973,
+ "ipping": 10974,
+ "▁dynam": 10975,
+ "▁scheme": 10976,
+ "▁proto": 10977,
+ ")/": 10978,
+ "Collections": 10979,
+ "riev": 10980,
+ "▁Click": 10981,
+ "▁uns": 10982,
+ "widetilde": 10983,
+ "▁remembered": 10984,
+ "гі": 10985,
+ "inates": 10986,
+ "▁incorpor": 10987,
+ "▁Description": 10988,
+ "▁prepare": 10989,
+ "▁Final": 10990,
+ "uation": 10991,
+ "▁Queen": 10992,
+ ">;": 10993,
+ "▁automatically": 10994,
+ "▁sharp": 10995,
+ "▁meat": 10996,
+ "ateur": 10997,
+ "astern": 10998,
+ "▁stuck": 10999,
+ "ASSERT": 11000,
+ "▁planned": 11001,
+ "dots": 11002,
+ "ookie": 11003,
+ "▁Histor": 11004,
+ "▁reviews": 11005,
+ "IMP": 11006,
+ "▁answered": 11007,
+ "Total": 11008,
+ "▁sau": 11009,
+ "▁Mexico": 11010,
+ "continue": 11011,
+ "▁Apple": 11012,
+ "likely": 11013,
+ "зва": 11014,
+ "users": 11015,
+ "▁identified": 11016,
+ "▁Lev": 11017,
+ "▁mol": 11018,
+ "▁Islam": 11019,
+ "▁committed": 11020,
+ "writ": 11021,
+ "бер": 11022,
+ "rift": 11023,
+ "▁interrupt": 11024,
+ "▁readonly": 11025,
+ "schema": 11026,
+ "Sm": 11027,
+ "Double": 11028,
+ "aza": 11029,
+ "▁Hal": 11030,
+ "Move": 11031,
+ "▁Series": 11032,
+ "inline": 11033,
+ "▁которы": 11034,
+ "soc": 11035,
+ "▁tent": 11036,
+ "▁amer": 11037,
+ "aki": 11038,
+ "▁lady": 11039,
+ "▁tired": 11040,
+ "ifi": 11041,
+ "▁même": 11042,
+ "ouver": 11043,
+ "▁aside": 11044,
+ "Did": 11045,
+ "',\r": 11046,
+ "▁bringing": 11047,
+ "Drawing": 11048,
+ "aro": 11049,
+ "▁Rh": 11050,
+ "▁Naz": 11051,
+ "esso": 11052,
+ "▁reaction": 11053,
+ "mitted": 11054,
+ "▁absolute": 11055,
+ "haust": 11056,
+ "(()": 11057,
+ "▁Task": 11058,
+ "ERS": 11059,
+ "▁^{": 11060,
+ "VD": 11061,
+ "▁tone": 11062,
+ "dist": 11063,
+ "vs": 11064,
+ "▁wheel": 11065,
+ "▁administration": 11066,
+ "▁interests": 11067,
+ "▁pointer": 11068,
+ "▁encounter": 11069,
+ "aver": 11070,
+ "▁nord": 11071,
+ "ket": 11072,
+ "▁beach": 11073,
+ "▁enjoyed": 11074,
+ "contains": 11075,
+ "▁append": 11076,
+ "Wait": 11077,
+ "▁squad": 11078,
+ "zel": 11079,
+ "▁medium": 11080,
+ "▁sending": 11081,
+ "▁Lady": 11082,
+ "ções": 11083,
+ "▁destination": 11084,
+ "nych": 11085,
+ "▁conflict": 11086,
+ "▁Ly": 11087,
+ "▁vul": 11088,
+ "▁basically": 11089,
+ "reated": 11090,
+ "black": 11091,
+ "ugins": 11092,
+ "▁calm": 11093,
+ "érie": 11094,
+ "har": 11095,
+ "лан": 11096,
+ "▁Се": 11097,
+ "watch": 11098,
+ "▁Put": 11099,
+ "▁dump": 11100,
+ "acher": 11101,
+ "scroll": 11102,
+ "▁claimed": 11103,
+ "▁Control": 11104,
+ "▁blind": 11105,
+ "enti": 11106,
+ "▁Keep": 11107,
+ "▁Development": 11108,
+ "images": 11109,
+ "▁tough": 11110,
+ "gebra": 11111,
+ "▁sept": 11112,
+ "hew": 11113,
+ "▁skill": 11114,
+ "▁Tay": 11115,
+ "▁któ": 11116,
+ "owner": 11117,
+ "pare": 11118,
+ "▁fee": 11119,
+ "▁continues": 11120,
+ "▁kan": 11121,
+ "bes": 11122,
+ "▁cha": 11123,
+ "ovo": 11124,
+ "▁Night": 11125,
+ "icture": 11126,
+ "shire": 11127,
+ "▁essay": 11128,
+ "▁suppose": 11129,
+ "etic": 11130,
+ "Art": 11131,
+ "acon": 11132,
+ "lla": 11133,
+ "words": 11134,
+ "▁comparison": 11135,
+ "▁BE": 11136,
+ "▁challenges": 11137,
+ "▁ol": 11138,
+ "citep": 11139,
+ "▁Foot": 11140,
+ "▁Such": 11141,
+ "▁papers": 11142,
+ "activ": 11143,
+ "quer": 11144,
+ "тя": 11145,
+ "▁То": 11146,
+ "ський": 11147,
+ "thur": 11148,
+ "done": 11149,
+ "▁shock": 11150,
+ "▁dedicated": 11151,
+ "▁correspond": 11152,
+ "Second": 11153,
+ "▁bull": 11154,
+ "life": 11155,
+ "indent": 11156,
+ "▁figures": 11157,
+ "▁Andrew": 11158,
+ "isp": 11159,
+ "▁favour": 11160,
+ "зда": 11161,
+ "▁Elect": 11162,
+ "Full": 11163,
+ "▁nearby": 11164,
+ "▁Register": 11165,
+ "Scale": 11166,
+ "ications": 11167,
+ "ин": 11168,
+ "▁AM": 11169,
+ "pair": 11170,
+ "▁perspective": 11171,
+ "▁nos": 11172,
+ "apa": 11173,
+ "ostał": 11174,
+ "▁Pers": 11175,
+ "icer": 11176,
+ "▁plastic": 11177,
+ "дов": 11178,
+ "ciples": 11179,
+ "zą": 11180,
+ "clos": 11181,
+ "▁уча": 11182,
+ "▁Á": 11183,
+ "plugin": 11184,
+ "▁angle": 11185,
+ "▁commission": 11186,
+ "▁funds": 11187,
+ "▁indu": 11188,
+ "▁drawn": 11189,
+ "ám": 11190,
+ "▁developing": 11191,
+ "▁segment": 11192,
+ "isme": 11193,
+ "scr": 11194,
+ "▁lies": 11195,
+ "▁IL": 11196,
+ "▁api": 11197,
+ "Extension": 11198,
+ "▁scal": 11199,
+ "install": 11200,
+ "▁Week": 11201,
+ "▁gentle": 11202,
+ "▁Canadian": 11203,
+ "▁dialog": 11204,
+ "▁articles": 11205,
+ "Theme": 11206,
+ "SM": 11207,
+ "▁Bul": 11208,
+ "▁leur": 11209,
+ "▁stom": 11210,
+ "Plugin": 11211,
+ "▁после": 11212,
+ "▁stead": 11213,
+ "▁ś": 11214,
+ "ipher": 11215,
+ "▁prze": 11216,
+ "▁draft": 11217,
+ "bottom": 11218,
+ "▁{};": 11219,
+ "▁stayed": 11220,
+ "feature": 11221,
+ "▁vot": 11222,
+ "▁fabric": 11223,
+ "ça": 11224,
+ "('#": 11225,
+ "rea": 11226,
+ "▁reput": 11227,
+ "▁Cir": 11228,
+ "▁AL": 11229,
+ "▁assertEquals": 11230,
+ "results": 11231,
+ "▁Cross": 11232,
+ "ursday": 11233,
+ "▁audio": 11234,
+ "▁gap": 11235,
+ "▁streets": 11236,
+ "▁scientific": 11237,
+ "platform": 11238,
+ "▁auss": 11239,
+ "▁Cro": 11240,
+ "▁partial": 11241,
+ "unc": 11242,
+ "▁choices": 11243,
+ "▁или": 11244,
+ "pred": 11245,
+ "▁heads": 11246,
+ "terday": 11247,
+ "▁Nick": 11248,
+ "▁weird": 11249,
+ "asant": 11250,
+ "▁represented": 11251,
+ "▁пи": 11252,
+ "DP": 11253,
+ "orders": 11254,
+ "clock": 11255,
+ "▁Ho": 11256,
+ "arters": 11257,
+ "Cmd": 11258,
+ "oga": 11259,
+ "Keys": 11260,
+ "Report": 11261,
+ "▁Vill": 11262,
+ "▁Mu": 11263,
+ "▁owned": 11264,
+ "SUCCESS": 11265,
+ "▁typeof": 11266,
+ "hdr": 11267,
+ "uable": 11268,
+ "▁neighborhood": 11269,
+ "▁AP": 11270,
+ "▁resulting": 11271,
+ "▁shadow": 11272,
+ "STRING": 11273,
+ "▁videos": 11274,
+ "лення": 11275,
+ "expect": 11276,
+ "▁Valley": 11277,
+ "▁goto": 11278,
+ "▁Sher": 11279,
+ "frastr": 11280,
+ "▁operating": 11281,
+ "▁это": 11282,
+ "▁Licensed": 11283,
+ "Variable": 11284,
+ "▁PR": 11285,
+ "▁Hans": 11286,
+ "clone": 11287,
+ "▁Gesch": 11288,
+ "▁Band": 11289,
+ "........": 11290,
+ "uing": 11291,
+ "▁hundreds": 11292,
+ "▁ок": 11293,
+ "▁emotional": 11294,
+ "▁Indust": 11295,
+ ")+": 11296,
+ "▁Egypt": 11297,
+ "▁franç": 11298,
+ "▁š": 11299,
+ "▁fasc": 11300,
+ "onto": 11301,
+ "▁Adam": 11302,
+ "▁laid": 11303,
+ "▁rig": 11304,
+ "▁detailed": 11305,
+ "▁implements": 11306,
+ "▁university": 11307,
+ "▁Hy": 11308,
+ "▁grid": 11309,
+ "▁regions": 11310,
+ "Stop": 11311,
+ "▁slot": 11312,
+ "▁angry": 11313,
+ "▁-=": 11314,
+ "▁waited": 11315,
+ "Vert": 11316,
+ "\":\"": 11317,
+ "▁elem": 11318,
+ "▁rég": 11319,
+ "owed": 11320,
+ "Member": 11321,
+ "▁ratio": 11322,
+ "isen": 11323,
+ "▁Lem": 11324,
+ "gery": 11325,
+ "▁cream": 11326,
+ "▁était": 11327,
+ "▁geb": 11328,
+ "unique": 11329,
+ "▁Deb": 11330,
+ "▁factory": 11331,
+ "że": 11332,
+ "dialog": 11333,
+ "▁Config": 11334,
+ "Sync": 11335,
+ "angers": 11336,
+ "▁governing": 11337,
+ "▁Hun": 11338,
+ "Space": 11339,
+ "▁jest": 11340,
+ "icious": 11341,
+ "▁emphas": 11342,
+ "umps": 11343,
+ "▁Esp": 11344,
+ "▁sul": 11345,
+ "▁historical": 11346,
+ "ija": 11347,
+ "▁lying": 11348,
+ "▁Steve": 11349,
+ "▁measures": 11350,
+ "osto": 11351,
+ "?”": 11352,
+ "▁pocket": 11353,
+ "▁Sat": 11354,
+ "▁pitch": 11355,
+ "▁natur": 11356,
+ "▁humans": 11357,
+ "▁Simon": 11358,
+ "adores": 11359,
+ "(\"\\": 11360,
+ "inking": 11361,
+ "▁expos": 11362,
+ "material": 11363,
+ "▁apparently": 11364,
+ "▁Camb": 11365,
+ "▁Box": 11366,
+ "▁spaces": 11367,
+ "exists": 11368,
+ "▁acting": 11369,
+ "ORY": 11370,
+ "зова": 11371,
+ "Good": 11372,
+ "ienne": 11373,
+ "▁Williams": 11374,
+ "▁fruit": 11375,
+ "iera": 11376,
+ "▁Lim": 11377,
+ "▁trait": 11378,
+ "▁artists": 11379,
+ "▁absor": 11380,
+ "rait": 11381,
+ "LOAD": 11382,
+ "▁movies": 11383,
+ "▁dynamic": 11384,
+ "asts": 11385,
+ "▁Integer": 11386,
+ "▁smoke": 11387,
+ "пі": 11388,
+ "angel": 11389,
+ ">(\"": 11390,
+ "▁instrument": 11391,
+ "▁fuel": 11392,
+ "ної": 11393,
+ "atalogue": 11394,
+ "▁serial": 11395,
+ "Files": 11396,
+ "▁bathroom": 11397,
+ "ilo": 11398,
+ "esto": 11399,
+ "▁pm": 11400,
+ "entials": 11401,
+ "▁Online": 11402,
+ "white": 11403,
+ "▁tips": 11404,
+ "▁capable": 11405,
+ "Fig": 11406,
+ "TV": 11407,
+ "▁он": 11408,
+ "ké": 11409,
+ "bitr": 11410,
+ "Mapping": 11411,
+ "▁tak": 11412,
+ "ющи": 11413,
+ "вля": 11414,
+ ")\",": 11415,
+ "▁Karl": 11416,
+ "▁Human": 11417,
+ "▁Pot": 11418,
+ "▁represents": 11419,
+ "▁consistent": 11420,
+ "_(": 11421,
+ "wen": 11422,
+ "▁Rose": 11423,
+ "law": 11424,
+ "▁FROM": 11425,
+ "▁begins": 11426,
+ "▁edit": 11427,
+ "▁mountain": 11428,
+ "▁chapter": 11429,
+ "▁wondered": 11430,
+ "▁industrial": 11431,
+ "▁Major": 11432,
+ "▁ges": 11433,
+ "▁directed": 11434,
+ "eros": 11435,
+ "▁Wild": 11436,
+ "liament": 11437,
+ "Book": 11438,
+ "username": 11439,
+ "hot": 11440,
+ "▁nam": 11441,
+ "▁league": 11442,
+ "bra": 11443,
+ "кон": 11444,
+ "▁Tal": 11445,
+ "▁Ва": 11446,
+ "▁exports": 11447,
+ "(@": 11448,
+ "▁sharing": 11449,
+ "▁Tro": 11450,
+ "ść": 11451,
+ "uesday": 11452,
+ "ylv": 11453,
+ "▁guitar": 11454,
+ "elen": 11455,
+ "Selection": 11456,
+ "▁confident": 11457,
+ "rypto": 11458,
+ "▁hors": 11459,
+ "editor": 11460,
+ "▁shoulders": 11461,
+ "getName": 11462,
+ "encing": 11463,
+ "SELECT": 11464,
+ "вши": 11465,
+ "▁kinds": 11466,
+ "▁Wel": 11467,
+ "▁purposes": 11468,
+ "Matrix": 11469,
+ "invalid": 11470,
+ "▁owners": 11471,
+ "▁Records": 11472,
+ "▁Process": 11473,
+ "▁chat": 11474,
+ "▁Dor": 11475,
+ "▁bin": 11476,
+ "redit": 11477,
+ "oire": 11478,
+ "▁Total": 11479,
+ "▁Family": 11480,
+ "ARY": 11481,
+ "▁bread": 11482,
+ "▁compre": 11483,
+ "▁shoes": 11484,
+ "▁raz": 11485,
+ "▁trace": 11486,
+ "nej": 11487,
+ "orted": 11488,
+ "hn": 11489,
+ "▁procedure": 11490,
+ "properties": 11491,
+ "plier": 11492,
+ "▁hero": 11493,
+ "panel": 11494,
+ "▁marked": 11495,
+ "▁worried": 11496,
+ "\\|": 11497,
+ "pts": 11498,
+ "▁Support": 11499,
+ "▁serving": 11500,
+ "Fail": 11501,
+ "▁disappoint": 11502,
+ "▁Scot": 11503,
+ "▁pleasure": 11504,
+ "▁judge": 11505,
+ "zeich": 11506,
+ "▁forever": 11507,
+ "▁Zeit": 11508,
+ "uous": 11509,
+ "inent": 11510,
+ "▁dw": 11511,
+ "▁waren": 11512,
+ "▁flash": 11513,
+ "▁troops": 11514,
+ "▁drugs": 11515,
+ "▁diam": 11516,
+ ".~": 11517,
+ "imp": 11518,
+ "inned": 11519,
+ "▁EV": 11520,
+ "Struct": 11521,
+ "▁justice": 11522,
+ "▁officials": 11523,
+ "ffff": 11524,
+ "▁Common": 11525,
+ "▁Cat": 11526,
+ "▁tomorrow": 11527,
+ "▁él": 11528,
+ "Texture": 11529,
+ "qpoint": 11530,
+ "▁Fried": 11531,
+ "▁Term": 11532,
+ "pgfqpoint": 11533,
+ "▁nem": 11534,
+ "norm": 11535,
+ "▁hardly": 11536,
+ "oda": 11537,
+ "zeta": 11538,
+ "emic": 11539,
+ "▁полу": 11540,
+ "▁loaded": 11541,
+ "kes": 11542,
+ "ció": 11543,
+ "▁fool": 11544,
+ "▁trick": 11545,
+ "▁dst": 11546,
+ "Find": 11547,
+ "▁все": 11548,
+ "}},": 11549,
+ "▁framework": 11550,
+ "▁merely": 11551,
+ "▁union": 11552,
+ "▁Edward": 11553,
+ "rif": 11554,
+ "Flag": 11555,
+ "▁crisis": 11556,
+ "▁finite": 11557,
+ "▁lol": 11558,
+ "▁Kim": 11559,
+ "ната": 11560,
+ "since": 11561,
+ "▁compat": 11562,
+ "▁pert": 11563,
+ "ibilities": 11564,
+ "▁también": 11565,
+ "ibli": 11566,
+ "▁teen": 11567,
+ "▁sympt": 11568,
+ "oral": 11569,
+ "ders": 11570,
+ "otte": 11571,
+ "при": 11572,
+ "▁Jane": 11573,
+ "▁originally": 11574,
+ "▁throat": 11575,
+ "mag": 11576,
+ "sup": 11577,
+ "uni": 11578,
+ "$$": 11579,
+ "▁Library": 11580,
+ "▁attacks": 11581,
+ "ingen": 11582,
+ "('/": 11583,
+ "▁hes": 11584,
+ "coin": 11585,
+ "ounce": 11586,
+ "▁Academy": 11587,
+ "MODULE": 11588,
+ "isms": 11589,
+ "▁Adv": 11590,
+ "▁Bol": 11591,
+ "▁incident": 11592,
+ ")^{": 11593,
+ "▁bij": 11594,
+ "▁Rome": 11595,
+ "▁Italy": 11596,
+ "events": 11597,
+ "▁Fern": 11598,
+ "▁ber": 11599,
+ "▁silent": 11600,
+ "▁pier": 11601,
+ "▁YO": 11602,
+ "▁plain": 11603,
+ "Bas": 11604,
+ "▁pill": 11605,
+ "rase": 11606,
+ "▁carrying": 11607,
+ "▁resp": 11608,
+ "ную": 11609,
+ "▁typical": 11610,
+ "Wrapper": 11611,
+ "▁gau": 11612,
+ "▁chemical": 11613,
+ "▁hal": 11614,
+ "throw": 11615,
+ "Cluster": 11616,
+ "▁Gab": 11617,
+ "▁Girl": 11618,
+ "quir": 11619,
+ "▁Arg": 11620,
+ "▁relief": 11621,
+ "▁Ве": 11622,
+ "dm": 11623,
+ "▁frustr": 11624,
+ "\\%": 11625,
+ "▁stores": 11626,
+ "▁bottle": 11627,
+ "▁Lew": 11628,
+ "two": 11629,
+ "stad": 11630,
+ "▁cheek": 11631,
+ "▁concerns": 11632,
+ "▁helpful": 11633,
+ "▁coverage": 11634,
+ "isi": 11635,
+ "ADD": 11636,
+ "async": 11637,
+ "▁approximately": 11638,
+ "iffer": 11639,
+ "hook": 11640,
+ "▁enum": 11641,
+ "ová": 11642,
+ "▁evil": 11643,
+ "▁constantly": 11644,
+ "apply": 11645,
+ "▁siè": 11646,
+ "▁practices": 11647,
+ "▁teachers": 11648,
+ "▁Sn": 11649,
+ "▁Awards": 11650,
+ "▁substant": 11651,
+ "▁$.": 11652,
+ "dk": 11653,
+ "▁mob": 11654,
+ "▁ingred": 11655,
+ "vere": 11656,
+ "Multi": 11657,
+ "пер": 11658,
+ "stal": 11659,
+ "yard": 11660,
+ "required": 11661,
+ "vement": 11662,
+ "▁intelligence": 11663,
+ "▁thinks": 11664,
+ "▁personally": 11665,
+ "▁trained": 11666,
+ "orney": 11667,
+ ")": 11668,
+ "gged": 11669,
+ "EINVAL": 11670,
+ "arna": 11671,
+ "▁Hamilton": 11672,
+ "merce": 11673,
+ "ekt": 11674,
+ "OF": 11675,
+ ")[": 11676,
+ "rug": 11677,
+ "ición": 11678,
+ "▁survey": 11679,
+ "nesday": 11680,
+ "▁pag": 11681,
+ "▁boundary": 11682,
+ "▁quantum": 11683,
+ "▁drawing": 11684,
+ "▁volunte": 11685,
+ "▁Word": 11686,
+ "sky": 11687,
+ "▁Greg": 11688,
+ "coll": 11689,
+ "hide": 11690,
+ "▁swim": 11691,
+ "▁revealed": 11692,
+ "adv": 11693,
+ "дя": 11694,
+ ".\");": 11695,
+ "▁explan": 11696,
+ "▁Current": 11697,
+ "▁gotten": 11698,
+ "▁falling": 11699,
+ "▁contained": 11700,
+ "UND": 11701,
+ "▁Should": 11702,
+ "▁killing": 11703,
+ "▁aspects": 11704,
+ "icted": 11705,
+ "▁Param": 11706,
+ "\",\r": 11707,
+ "TION": 11708,
+ "));\r": 11709,
+ "▁Iran": 11710,
+ "beit": 11711,
+ "▁Bu": 11712,
+ "▁[],": 11713,
+ "SSION": 11714,
+ "▁Mah": 11715,
+ "▁resolution": 11716,
+ "▁boss": 11717,
+ "lg": 11718,
+ "chor": 11719,
+ "▁Unter": 11720,
+ "▁debt": 11721,
+ "▁vid": 11722,
+ "gie": 11723,
+ "▁uno": 11724,
+ "CB": 11725,
+ "plom": 11726,
+ "LICENSE": 11727,
+ "▁Kenn": 11728,
+ "▁finns": 11729,
+ "ONG": 11730,
+ "▁somewhat": 11731,
+ "▁actor": 11732,
+ "▁Status": 11733,
+ "▁probability": 11734,
+ "fb": 11735,
+ "▁chart": 11736,
+ "▁stands": 11737,
+ "policy": 11738,
+ "▁onder": 11739,
+ "tabular": 11740,
+ "▁Ash": 11741,
+ "▁boost": 11742,
+ "▁desper": 11743,
+ "month": 11744,
+ "▁alert": 11745,
+ "▁suite": 11746,
+ "▁gén": 11747,
+ "▁vacc": 11748,
+ "▁Has": 11749,
+ "Mask": 11750,
+ "▁Thursday": 11751,
+ "▁proved": 11752,
+ "▁Nel": 11753,
+ "▁moral": 11754,
+ "▁ja": 11755,
+ "auer": 11756,
+ "codec": 11757,
+ "▁instant": 11758,
+ "amps": 11759,
+ "▁milk": 11760,
+ "WORD": 11761,
+ "▁Ö": 11762,
+ "Email": 11763,
+ "Elements": 11764,
+ "▁forma": 11765,
+ "Free": 11766,
+ "MAP": 11767,
+ "▁Ж": 11768,
+ "sym": 11769,
+ "▁ти": 11770,
+ "▁Econom": 11771,
+ "▁Vi": 11772,
+ "▁Columb": 11773,
+ "▁_,": 11774,
+ "oret": 11775,
+ "Sequ": 11776,
+ "plan": 11777,
+ "▁frequency": 11778,
+ "irement": 11779,
+ "▁assumed": 11780,
+ "▁Ca": 11781,
+ "▁Bit": 11782,
+ "▁коман": 11783,
+ "▁smell": 11784,
+ "Security": 11785,
+ "▁aqu": 11786,
+ "oor": 11787,
+ "price": 11788,
+ "inity": 11789,
+ "▁axis": 11790,
+ "release": 11791,
+ "▁resolve": 11792,
+ "▁tears": 11793,
+ "▁bother": 11794,
+ "▁Community": 11795,
+ "▁registered": 11796,
+ "▁revolution": 11797,
+ "?.": 11798,
+ "▁versions": 11799,
+ "%%%%": 11800,
+ "ydro": 11801,
+ "Success": 11802,
+ "▁Win": 11803,
+ "▁Boy": 11804,
+ "▁Dub": 11805,
+ "▁kw": 11806,
+ "▁noch": 11807,
+ "▁charges": 11808,
+ "arios": 11809,
+ "uar": 11810,
+ ";&": 11811,
+ "▁había": 11812,
+ "(`": 11813,
+ "▁tx": 11814,
+ "elve": 11815,
+ "▁años": 11816,
+ "▁math": 11817,
+ "▁Alf": 11818,
+ "▁Fund": 11819,
+ "▁manifest": 11820,
+ "▁attached": 11821,
+ "▁spiritual": 11822,
+ "▁Alexander": 11823,
+ "unes": 11824,
+ "▁seed": 11825,
+ "▁Но": 11826,
+ "▁magazine": 11827,
+ "▁eigen": 11828,
+ "▁обра": 11829,
+ "ea": 11830,
+ "▁PH": 11831,
+ "swing": 11832,
+ "▁Asia": 11833,
+ "ју": 11834,
+ "▁KIND": 11835,
+ "Identifier": 11836,
+ "once": 11837,
+ "▁alcohol": 11838,
+ "ції": 11839,
+ "styles": 11840,
+ "assertEqual": 11841,
+ "▁Ra": 11842,
+ "графи": 11843,
+ "▁millions": 11844,
+ "▁chunk": 11845,
+ "дер": 11846,
+ "Package": 11847,
+ "UST": 11848,
+ "▁Nothing": 11849,
+ "(\"#": 11850,
+ "▁Mid": 11851,
+ "▁нача": 11852,
+ "ły": 11853,
+ "AAAA": 11854,
+ "▁launched": 11855,
+ "▁wake": 11856,
+ "▁guests": 11857,
+ "▁differences": 11858,
+ "udi": 11859,
+ "▁aid": 11860,
+ "▁Sport": 11861,
+ "ulator": 11862,
+ "execute": 11863,
+ "plot": 11864,
+ "ching": 11865,
+ "▁Norm": 11866,
+ "tm": 11867,
+ "\\+": 11868,
+ "ARD": 11869,
+ "▁beer": 11870,
+ "▁під": 11871,
+ "IAL": 11872,
+ "storage": 11873,
+ "▁Anna": 11874,
+ "▁yards": 11875,
+ "▁technique": 11876,
+ "▁où": 11877,
+ "atten": 11878,
+ "UNT": 11879,
+ "don": 11880,
+ "фор": 11881,
+ "▁hoping": 11882,
+ "▁victory": 11883,
+ "itat": 11884,
+ "▁significantly": 11885,
+ "▁practical": 11886,
+ "ije": 11887,
+ "▁expansion": 11888,
+ "JS": 11889,
+ "ixels": 11890,
+ "USER": 11891,
+ "Shape": 11892,
+ "▁extent": 11893,
+ "lio": 11894,
+ "▁pued": 11895,
+ "olid": 11896,
+ "▁gam": 11897,
+ "▁sevent": 11898,
+ "▁Ga": 11899,
+ "anguages": 11900,
+ "(((": 11901,
+ "ъл": 11902,
+ "▁Exper": 11903,
+ "asty": 11904,
+ "rieg": 11905,
+ "gio": 11906,
+ "odo": 11907,
+ "▁colle": 11908,
+ "▁stored": 11909,
+ "▁Sche": 11910,
+ "istant": 11911,
+ "▁lip": 11912,
+ "BR": 11913,
+ "▁aug": 11914,
+ "▁Search": 11915,
+ ")=\\": 11916,
+ "▁Ur": 11917,
+ "▁sole": 11918,
+ "illo": 11919,
+ "▁mehr": 11920,
+ "kit": 11921,
+ "▁interior": 11922,
+ "LIST": 11923,
+ "adel": 11924,
+ "▁shopping": 11925,
+ "▁slä": 11926,
+ "Your": 11927,
+ "DITION": 11928,
+ "▁Http": 11929,
+ "raham": 11930,
+ "три": 11931,
+ "▁brings": 11932,
+ "Rev": 11933,
+ "▁propag": 11934,
+ "ityEngine": 11935,
+ "()),": 11936,
+ "▁ingår": 11937,
+ "▁Ireland": 11938,
+ "▁\"./": 11939,
+ "▁Harr": 11940,
+ "▁admin": 11941,
+ "eno": 11942,
+ "▁kr": 11943,
+ "▁está": 11944,
+ "▁props": 11945,
+ "tok": 11946,
+ "omorph": 11947,
+ "▁affected": 11948,
+ "Phone": 11949,
+ "▁degrees": 11950,
+ "some": 11951,
+ "▁nin": 11952,
+ "EVENT": 11953,
+ "▁interaction": 11954,
+ "▁Tuesday": 11955,
+ "iterator": 11956,
+ "▁Nob": 11957,
+ "▁scatter": 11958,
+ "ucket": 11959,
+ "complete": 11960,
+ "▁duty": 11961,
+ "▁answers": 11962,
+ "Progress": 11963,
+ "eed": 11964,
+ "рон": 11965,
+ "▁vie": 11966,
+ "▁depos": 11967,
+ "▁packet": 11968,
+ "▁tow": 11969,
+ "▁deleg": 11970,
+ "audio": 11971,
+ "▁vary": 11972,
+ "▁migr": 11973,
+ "фі": 11974,
+ "esa": 11975,
+ "Events": 11976,
+ "haus": 11977,
+ "▁Sav": 11978,
+ "▁Portug": 11979,
+ "▁сто": 11980,
+ "ilation": 11981,
+ "▁metadata": 11982,
+ "las": 11983,
+ "▁ai": 11984,
+ "▁anger": 11985,
+ "▁ham": 11986,
+ "▁Anal": 11987,
+ "▁frequently": 11988,
+ "▁FALSE": 11989,
+ "oche": 11990,
+ "rez": 11991,
+ "▁Viet": 11992,
+ "quis": 11993,
+ "▁charged": 11994,
+ "äs": 11995,
+ "▁Path": 11996,
+ "▁accurate": 11997,
+ "▁Plus": 11998,
+ "keit": 11999,
+ "▁Input": 12000,
+ "when": 12001,
+ "eras": 12002,
+ "▁воз": 12003,
+ "▁derived": 12004,
+ "aje": 12005,
+ "▁Had": 12006,
+ "uren": 12007,
+ "ór": 12008,
+ "}=\\": 12009,
+ "ureau": 12010,
+ "aland": 12011,
+ "Execution": 12012,
+ "eden": 12013,
+ "▁seeking": 12014,
+ "changed": 12015,
+ "▁trem": 12016,
+ "ску": 12017,
+ "▁Geme": 12018,
+ "inating": 12019,
+ "▁columns": 12020,
+ "EP": 12021,
+ "▁injury": 12022,
+ "endent": 12023,
+ "▁headed": 12024,
+ "ASE": 12025,
+ "▁Muslim": 12026,
+ "▁climate": 12027,
+ "▁fake": 12028,
+ "CMD": 12029,
+ "ји": 12030,
+ "▁Arts": 12031,
+ "fection": 12032,
+ "▁pit": 12033,
+ ">\\": 12034,
+ "anal": 12035,
+ "Section": 12036,
+ "plus": 12037,
+ "üt": 12038,
+ "▁embed": 12039,
+ "▁strings": 12040,
+ "Before": 12041,
+ "proc": 12042,
+ "▁спо": 12043,
+ "trl": 12044,
+ "vr": 12045,
+ "Background": 12046,
+ "logger": 12047,
+ "agraph": 12048,
+ "iest": 12049,
+ "▁goods": 12050,
+ "batch": 12051,
+ "▁optional": 12052,
+ "▁Taylor": 12053,
+ "▁recognize": 12054,
+ "walk": 12055,
+ "▁Hit": 12056,
+ "▁Elizabeth": 12057,
+ "}:": 12058,
+ "▁careful": 12059,
+ "краї": 12060,
+ "▁locations": 12061,
+ "▁structures": 12062,
+ "▁disk": 12063,
+ "▁ships": 12064,
+ "▁suo": 12065,
+ "▁sowie": 12066,
+ "▁Ess": 12067,
+ "▁Hash": 12068,
+ "▁reasonable": 12069,
+ "▁Moreover": 12070,
+ "▁formula": 12071,
+ "▁Centre": 12072,
+ "▁residents": 12073,
+ "RS": 12074,
+ "Ids": 12075,
+ "▁Know": 12076,
+ "▁trib": 12077,
+ "▁rés": 12078,
+ "▁stable": 12079,
+ "▁Would": 12080,
+ "▁breaking": 12081,
+ "▁meal": 12082,
+ "▁phen": 12083,
+ "▁fel": 12084,
+ "▁Fred": 12085,
+ "Author": 12086,
+ "▁capture": 12087,
+ "opts": 12088,
+ "▁everywhere": 12089,
+ "▁sque": 12090,
+ "▁moder": 12091,
+ "setup": 12092,
+ "▁Supp": 12093,
+ "▁whenever": 12094,
+ "{(": 12095,
+ "wart": 12096,
+ "▁toe": 12097,
+ "Prefix": 12098,
+ "hou": 12099,
+ "gage": 12100,
+ ">\"": 12101,
+ "▁frag": 12102,
+ "▁Theorem": 12103,
+ "memory": 12104,
+ "▁contents": 12105,
+ "docs": 12106,
+ "}'": 12107,
+ "▁Irish": 12108,
+ "Then": 12109,
+ "aats": 12110,
+ "Save": 12111,
+ "▁agency": 12112,
+ "▁име": 12113,
+ "дова": 12114,
+ "▁Function": 12115,
+ "NN": 12116,
+ "destroy": 12117,
+ "▁Message": 12118,
+ "▁cancel": 12119,
+ "▁superior": 12120,
+ "▁ec": 12121,
+ "▁literature": 12122,
+ "▁PART": 12123,
+ "Il": 12124,
+ "▁Cab": 12125,
+ "engine": 12126,
+ "▁basket": 12127,
+ "worth": 12128,
+ "▁Sel": 12129,
+ "fetch": 12130,
+ "▁Stadt": 12131,
+ "▁Ки": 12132,
+ "▁conj": 12133,
+ "▁seiner": 12134,
+ "▁confirmed": 12135,
+ "▁Argent": 12136,
+ "amar": 12137,
+ "pgfpath": 12138,
+ "▁struggle": 12139,
+ "Pattern": 12140,
+ "▁Middle": 12141,
+ "itan": 12142,
+ "▁moon": 12143,
+ "orough": 12144,
+ "▁Catholic": 12145,
+ "▁struck": 12146,
+ "]->": 12147,
+ "▁weapon": 12148,
+ "▁subst": 12149,
+ "▁instructions": 12150,
+ "▁occas": 12151,
+ "protected": 12152,
+ "▁Less": 12153,
+ "▁batch": 12154,
+ "▁contra": 12155,
+ "▁deck": 12156,
+ "▁ignored": 12157,
+ "▁refused": 12158,
+ "trigger": 12159,
+ "▁criminal": 12160,
+ "GA": 12161,
+ "olly": 12162,
+ "▁Bell": 12163,
+ "▁Ю": 12164,
+ "forward": 12165,
+ "▁prefix": 12166,
+ "▁immediate": 12167,
+ "▁assigned": 12168,
+ "▁elected": 12169,
+ "▁tonight": 12170,
+ "▁Dies": 12171,
+ "▁Beach": 12172,
+ "▁preced": 12173,
+ "ował": 12174,
+ "▁galax": 12175,
+ "▁logic": 12176,
+ "enza": 12177,
+ "▁Captain": 12178,
+ "▁Hay": 12179,
+ "▁facts": 12180,
+ "▁ни": 12181,
+ "té": 12182,
+ "▁sb": 12183,
+ "oped": 12184,
+ "▁combat": 12185,
+ "▁explore": 12186,
+ "▁(-": 12187,
+ "Loader": 12188,
+ "▁Wilson": 12189,
+ "▁locked": 12190,
+ ":": 12191,
+ "▁Od": 12192,
+ "▁Prote": 12193,
+ "▁disabled": 12194,
+ "▁hatte": 12195,
+ "▁shout": 12196,
+ "▁constructor": 12197,
+ "бі": 12198,
+ "▁tras": 12199,
+ "▁Father": 12200,
+ "▁adj": 12201,
+ "▁Carolina": 12202,
+ "▁Food": 12203,
+ "bad": 12204,
+ "atore": 12205,
+ "parameters": 12206,
+ "▁Full": 12207,
+ "[-": 12208,
+ "▁\"#": 12209,
+ "▁Try": 12210,
+ "ської": 12211,
+ "▁exhaust": 12212,
+ "▁scroll": 12213,
+ "_;": 12214,
+ "Who": 12215,
+ "▁delivered": 12216,
+ "▁referred": 12217,
+ "▁prospect": 12218,
+ "scan": 12219,
+ "▁modified": 12220,
+ "Generator": 12221,
+ "▁excess": 12222,
+ "▁kg": 12223,
+ "zet": 12224,
+ "icz": 12225,
+ "clipse": 12226,
+ "▁tank": 12227,
+ "▁guns": 12228,
+ "▁Ges": 12229,
+ "inton": 12230,
+ "▁Wednesday": 12231,
+ "▁mainly": 12232,
+ "parser": 12233,
+ "▁effectively": 12234,
+ "▁Ку": 12235,
+ "▁resident": 12236,
+ "▁Li": 12237,
+ "▁flying": 12238,
+ "▁mayor": 12239,
+ "üh": 12240,
+ "uta": 12241,
+ "▁colour": 12242,
+ "▁aircraft": 12243,
+ "terior": 12244,
+ "nr": 12245,
+ "▁keeps": 12246,
+ "fan": 12247,
+ "▁shirt": 12248,
+ "Compar": 12249,
+ "▁Eth": 12250,
+ "Mac": 12251,
+ "clean": 12252,
+ "slice": 12253,
+ "czy": 12254,
+ "▁gender": 12255,
+ "▁butter": 12256,
+ "AUT": 12257,
+ "▁Element": 12258,
+ "Fin": 12259,
+ "dma": 12260,
+ "sample": 12261,
+ "Registry": 12262,
+ "▁classic": 12263,
+ "▁drove": 12264,
+ "pb": 12265,
+ "defined": 12266,
+ "▁reward": 12267,
+ "yal": 12268,
+ "]),": 12269,
+ "▁BAS": 12270,
+ "▁hyper": 12271,
+ "▁Ни": 12272,
+ "▁).": 12273,
+ "Psi": 12274,
+ "▁entries": 12275,
+ "▁Kingdom": 12276,
+ "▁Song": 12277,
+ "▁prompt": 12278,
+ "centering": 12279,
+ "▁Holly": 12280,
+ "eman": 12281,
+ "▁painting": 12282,
+ "▁formation": 12283,
+ "▁Request": 12284,
+ "controller": 12285,
+ "Region": 12286,
+ "PY": 12287,
+ "idades": 12288,
+ "TL": 12289,
+ "▁disable": 12290,
+ "▁rein": 12291,
+ "rical": 12292,
+ "\"\r": 12293,
+ "%)": 12294,
+ "▁Sab": 12295,
+ "▁Without": 12296,
+ "Serv": 12297,
+ "▁Short": 12298,
+ "▁ю": 12299,
+ "▁resc": 12300,
+ "▁patterns": 12301,
+ "▁ArrayList": 12302,
+ "symbol": 12303,
+ "aco": 12304,
+ "▁Hom": 12305,
+ "help": 12306,
+ "▁hasta": 12307,
+ "▁installed": 12308,
+ "atie": 12309,
+ "▁visited": 12310,
+ "▁Бе": 12311,
+ "){\\": 12312,
+ "▁desde": 12313,
+ "JECT": 12314,
+ "▁drew": 12315,
+ "▁Stock": 12316,
+ "▁Cru": 12317,
+ "DEF": 12318,
+ "obby": 12319,
+ "izable": 12320,
+ "ogether": 12321,
+ "▁aber": 12322,
+ "▁dan": 12323,
+ "alis": 12324,
+ "tail": 12325,
+ "▁expressed": 12326,
+ "▁Access": 12327,
+ "Seg": 12328,
+ "▁Lib": 12329,
+ "▁supports": 12330,
+ "background": 12331,
+ "▁commune": 12332,
+ "called": 12333,
+ "▁printf": 12334,
+ "▁Prince": 12335,
+ "ните": 12336,
+ "depend": 12337,
+ "▁dels": 12338,
+ "neur": 12339,
+ "▁recommended": 12340,
+ "▁founded": 12341,
+ "▁markets": 12342,
+ "▁destroyed": 12343,
+ "▁abstract": 12344,
+ "▁serie": 12345,
+ "▁Dun": 12346,
+ "Term": 12347,
+ "▁portion": 12348,
+ "adapter": 12349,
+ "isset": 12350,
+ "чески": 12351,
+ "▁integer": 12352,
+ "▁returning": 12353,
+ "enties": 12354,
+ "▁Fair": 12355,
+ "▁USB": 12356,
+ "▁Price": 12357,
+ "igate": 12358,
+ "▁settled": 12359,
+ "({\\": 12360,
+ "nek": 12361,
+ "▁therm": 12362,
+ "▁cig": 12363,
+ "ány": 12364,
+ "▁investigation": 12365,
+ "ometer": 12366,
+ "SUP": 12367,
+ "Some": 12368,
+ "sing": 12369,
+ "Constant": 12370,
+ "▁retail": 12371,
+ "ży": 12372,
+ "▁drinking": 12373,
+ "▁Invest": 12374,
+ "SV": 12375,
+ "iginal": 12376,
+ "▁Bow": 12377,
+ "{{\\": 12378,
+ "▁assistance": 12379,
+ "▁intellect": 12380,
+ "INIT": 12381,
+ "aug": 12382,
+ "▁Leon": 12383,
+ "Sur": 12384,
+ "▁admit": 12385,
+ "▁Command": 12386,
+ "illes": 12387,
+ "rov": 12388,
+ "▁oh": 12389,
+ "▁não": 12390,
+ "▁matching": 12391,
+ "▁genu": 12392,
+ "▁Ox": 12393,
+ "тся": 12394,
+ "notation": 12395,
+ "GO": 12396,
+ "▁Nap": 12397,
+ "▁verify": 12398,
+ "▁aussi": 12399,
+ "DateTime": 12400,
+ "▁suitable": 12401,
+ "▁indicate": 12402,
+ "▁Live": 12403,
+ "Feature": 12404,
+ "▁tracks": 12405,
+ "▁hasn": 12406,
+ "▁Java": 12407,
+ "▁closely": 12408,
+ "▁Dad": 12409,
+ "ceive": 12410,
+ "▁Market": 12411,
+ "agy": 12412,
+ "▁\"-": 12413,
+ "awn": 12414,
+ "stell": 12415,
+ "pton": 12416,
+ "zeit": 12417,
+ "▁Vector": 12418,
+ "▁MAX": 12419,
+ "▁Federal": 12420,
+ "wall": 12421,
+ "▁Jen": 12422,
+ "delay": 12423,
+ "▁limits": 12424,
+ "▁Quest": 12425,
+ "Cam": 12426,
+ "▁Fel": 12427,
+ "writer": 12428,
+ "LP": 12429,
+ "▁moves": 12430,
+ "▁Execut": 12431,
+ "▁DB": 12432,
+ "oker": 12433,
+ "scribe": 12434,
+ "elijk": 12435,
+ "Constants": 12436,
+ "Addr": 12437,
+ "▁}}": 12438,
+ "▁channels": 12439,
+ "iy": 12440,
+ "riority": 12441,
+ "▁trading": 12442,
+ "▁facilities": 12443,
+ "▁Pack": 12444,
+ "▁sys": 12445,
+ "▁meta": 12446,
+ "▁estimate": 12447,
+ "▁Later": 12448,
+ "issue": 12449,
+ "▁Having": 12450,
+ "▁guest": 12451,
+ "▁nobody": 12452,
+ "depth": 12453,
+ "▁został": 12454,
+ "пера": 12455,
+ ")}\\": 12456,
+ "bg": 12457,
+ "▁Twitter": 12458,
+ "▁darkness": 12459,
+ "jpg": 12460,
+ "contr": 12461,
+ "kernel": 12462,
+ "]\\": 12463,
+ "▁extend": 12464,
+ "roc": 12465,
+ "NET": 12466,
+ "MSG": 12467,
+ "▁burst": 12468,
+ "▁repair": 12469,
+ "▁fetch": 12470,
+ "ieg": 12471,
+ "ús": 12472,
+ "Screen": 12473,
+ "blem": 12474,
+ "AppCompat": 12475,
+ "▁chap": 12476,
+ "ELD": 12477,
+ "▁Penn": 12478,
+ "▁promote": 12479,
+ "▁Ukr": 12480,
+ "arest": 12481,
+ "▁samples": 12482,
+ "▁Greek": 12483,
+ "▁constru": 12484,
+ "▁universe": 12485,
+ "elijke": 12486,
+ "▁preferred": 12487,
+ "▁Де": 12488,
+ "▁Ira": 12489,
+ "▁dow": 12490,
+ "agues": 12491,
+ "HERE": 12492,
+ "▁experts": 12493,
+ "Protocol": 12494,
+ "PIO": 12495,
+ "▁naz": 12496,
+ "▁Kh": 12497,
+ "hör": 12498,
+ "▁distingu": 12499,
+ "▁BY": 12500,
+ "▁seine": 12501,
+ "eping": 12502,
+ "▁fairly": 12503,
+ "▁Mean": 12504,
+ "ixer": 12505,
+ "insi": 12506,
+ "▁authors": 12507,
+ "**.": 12508,
+ "AI": 12509,
+ "▁edges": 12510,
+ "▁shooting": 12511,
+ "Admin": 12512,
+ "▁maps": 12513,
+ "chant": 12514,
+ "▁COVID": 12515,
+ "▁linked": 12516,
+ "▁ske": 12517,
+ "▁powers": 12518,
+ "ád": 12519,
+ "▁stomach": 12520,
+ "▁usage": 12521,
+ "▁defend": 12522,
+ "▁sustain": 12523,
+ "▁updates": 12524,
+ "▁assign": 12525,
+ "HL": 12526,
+ "▁Sea": 12527,
+ "▁discipl": 12528,
+ "Video": 12529,
+ "▁Chief": 12530,
+ "▁bunch": 12531,
+ "▁Obama": 12532,
+ "nis": 12533,
+ "vor": 12534,
+ "▁agents": 12535,
+ "cas": 12536,
+ "chter": 12537,
+ "▁glanced": 12538,
+ "supported": 12539,
+ "▁Consider": 12540,
+ "▁Everyone": 12541,
+ "▁lect": 12542,
+ "▁Stone": 12543,
+ "▁Jam": 12544,
+ "ogram": 12545,
+ "formance": 12546,
+ "▁\\\"": 12547,
+ "▁patch": 12548,
+ "▁vit": 12549,
+ "Power": 12550,
+ "▁harder": 12551,
+ "Anal": 12552,
+ "▁desired": 12553,
+ "▁jug": 12554,
+ "▁supporting": 12555,
+ "DU": 12556,
+ "]],": 12557,
+ "▁Administr": 12558,
+ "ucky": 12559,
+ "▁controller": 12560,
+ "▁issued": 12561,
+ "▁Sin": 12562,
+ "▁affili": 12563,
+ "▁partners": 12564,
+ "cdots": 12565,
+ "ctic": 12566,
+ "Car": 12567,
+ "▁NY": 12568,
+ "▁priority": 12569,
+ "original": 12570,
+ "Sql": 12571,
+ "▁declared": 12572,
+ "▁Hotel": 12573,
+ "▁browser": 12574,
+ "▁grande": 12575,
+ "}^\\": 12576,
+ "bow": 12577,
+ "▁accommod": 12578,
+ "Directory": 12579,
+ "▁suffering": 12580,
+ "▁logger": 12581,
+ "▁breakfast": 12582,
+ "uli": 12583,
+ "▁boot": 12584,
+ "▁contribution": 12585,
+ "NESS": 12586,
+ "▁Ten": 12587,
+ "semble": 12588,
+ "▁housing": 12589,
+ "Raw": 12590,
+ "ANCE": 12591,
+ "▁При": 12592,
+ "▁brit": 12593,
+ "essa": 12594,
+ "inson": 12595,
+ "▁Ball": 12596,
+ "entes": 12597,
+ "▁Bra": 12598,
+ "score": 12599,
+ "GER": 12600,
+ "route": 12601,
+ "apsed": 12602,
+ "рой": 12603,
+ "diff": 12604,
+ "▁broadcast": 12605,
+ "▁tar": 12606,
+ "▁delight": 12607,
+ ")?": 12608,
+ "chester": 12609,
+ "Platform": 12610,
+ "▁emergency": 12611,
+ "▁ces": 12612,
+ "nership": 12613,
+ "▁situations": 12614,
+ "▁familjen": 12615,
+ "▁Geb": 12616,
+ "enta": 12617,
+ "úblic": 12618,
+ "▁Place": 12619,
+ "ILL": 12620,
+ "▁march": 12621,
+ "▁fundamental": 12622,
+ "attributes": 12623,
+ "кти": 12624,
+ "▁Fu": 12625,
+ "FD": 12626,
+ "▁рас": 12627,
+ "▁academic": 12628,
+ "pres": 12629,
+ "▁rising": 12630,
+ "▁Braz": 12631,
+ "▁receiving": 12632,
+ "WARN": 12633,
+ "▁judg": 12634,
+ "▁necessarily": 12635,
+ "]=": 12636,
+ "▁deeply": 12637,
+ "▁gray": 12638,
+ "Headers": 12639,
+ "▁coal": 12640,
+ "\\{": 12641,
+ "Mut": 12642,
+ "bach": 12643,
+ "▁profit": 12644,
+ "вого": 12645,
+ "igs": 12646,
+ "ograp": 12647,
+ "\";\r": 12648,
+ "▁advoc": 12649,
+ "Generated": 12650,
+ "мери": 12651,
+ "▁Cond": 12652,
+ "▁agric": 12653,
+ "BASE": 12654,
+ "▁arrang": 12655,
+ "▁flowers": 12656,
+ "iw": 12657,
+ "▁];": 12658,
+ "▁вой": 12659,
+ "umerate": 12660,
+ "▁ihr": 12661,
+ "▁пар": 12662,
+ "▁mont": 12663,
+ "widehat": 12664,
+ "mg": 12665,
+ "▁btn": 12666,
+ "▁besk": 12667,
+ "▁acts": 12668,
+ "ós": 12669,
+ "~~~~": 12670,
+ "▁curve": 12671,
+ "language": 12672,
+ "▁TRUE": 12673,
+ "▁cleaning": 12674,
+ "Math": 12675,
+ "▁regional": 12676,
+ "▁estimated": 12677,
+ "arity": 12678,
+ "ierung": 12679,
+ "/{": 12680,
+ "jango": 12681,
+ "$_": 12682,
+ "▁threw": 12683,
+ "rq": 12684,
+ "cop": 12685,
+ "nergy": 12686,
+ "▁Account": 12687,
+ "pal": 12688,
+ "▁Nic": 12689,
+ "]))": 12690,
+ "▁awesome": 12691,
+ "▁Load": 12692,
+ "unnel": 12693,
+ "▁rows": 12694,
+ "▁foreach": 12695,
+ "▁Pod": 12696,
+ "▁EN": 12697,
+ "▁.=": 12698,
+ "uate": 12699,
+ "frastructure": 12700,
+ "▁Watch": 12701,
+ "Stand": 12702,
+ "▁routine": 12703,
+ "▁pic": 12704,
+ "helper": 12705,
+ "▁horses": 12706,
+ "▁requested": 12707,
+ "▁---": 12708,
+ "border": 12709,
+ "▁lifted": 12710,
+ "▁Ped": 12711,
+ "Import": 12712,
+ "ље": 12713,
+ "▁Ли": 12714,
+ "▁myst": 12715,
+ "THER": 12716,
+ "▁AC": 12717,
+ "Proxy": 12718,
+ "prov": 12719,
+ "▁Nik": 12720,
+ "hemat": 12721,
+ "ональ": 12722,
+ "▁\".": 12723,
+ "ului": 12724,
+ "▁improved": 12725,
+ "ieren": 12726,
+ "ocolate": 12727,
+ "Sche": 12728,
+ "unic": 12729,
+ "▁Professor": 12730,
+ "ieler": 12731,
+ "▁duration": 12732,
+ "▁timeout": 12733,
+ "hom": 12734,
+ "▁lux": 12735,
+ "▁trab": 12736,
+ "itary": 12737,
+ "ње": 12738,
+ "▁inspired": 12739,
+ "})\\": 12740,
+ "isely": 12741,
+ "ials": 12742,
+ "▁Vor": 12743,
+ "▁enhance": 12744,
+ "▁lucky": 12745,
+ "World": 12746,
+ "elo": 12747,
+ "ifiers": 12748,
+ "▁facing": 12749,
+ "▁appreciate": 12750,
+ "▁être": 12751,
+ "▁bench": 12752,
+ "atted": 12753,
+ "gence": 12754,
+ "course": 12755,
+ "▁tub": 12756,
+ "▁lors": 12757,
+ "▁mistake": 12758,
+ "nom": 12759,
+ "▁paus": 12760,
+ "▁\"\";": 12761,
+ "▁subs": 12762,
+ "▁stato": 12763,
+ "$)": 12764,
+ "▁gay": 12765,
+ "orry": 12766,
+ "▁vehicles": 12767,
+ "▁brill": 12768,
+ "may": 12769,
+ "resp": 12770,
+ "▁wore": 12771,
+ "ją": 12772,
+ "bp": 12773,
+ "onel": 12774,
+ "▁CR": 12775,
+ "▁diagn": 12776,
+ "mathsf": 12777,
+ "▁holiday": 12778,
+ "▁achieved": 12779,
+ "▁{'": 12780,
+ "▁Resource": 12781,
+ "▁hi": 12782,
+ "▁bra": 12783,
+ "▁CONDITION": 12784,
+ "ctr": 12785,
+ "▁Write": 12786,
+ "ishop": 12787,
+ "OLD": 12788,
+ "▁cpu": 12789,
+ "▁occurs": 12790,
+ "ół": 12791,
+ "straint": 12792,
+ "▁nuclear": 12793,
+ "Area": 12794,
+ "cluster": 12795,
+ "▁surrounding": 12796,
+ "▁Juan": 12797,
+ "▁prima": 12798,
+ "▁Southern": 12799,
+ "itty": 12800,
+ "▁Assembly": 12801,
+ "elem": 12802,
+ "adi": 12803,
+ "éral": 12804,
+ "▁Wat": 12805,
+ "▁Radio": 12806,
+ "▁gegen": 12807,
+ "▁Tony": 12808,
+ "pressed": 12809,
+ "▁Anne": 12810,
+ "▁NS": 12811,
+ "▁Pak": 12812,
+ "▁Civil": 12813,
+ "▁thrown": 12814,
+ "NONE": 12815,
+ "▁pump": 12816,
+ "▁solve": 12817,
+ "ENABLE": 12818,
+ "▁Phys": 12819,
+ "▁],": 12820,
+ "POSE": 12821,
+ "ktet": 12822,
+ "▁Fab": 12823,
+ "validate": 12824,
+ "Iterator": 12825,
+ "condition": 12826,
+ "redu": 12827,
+ "▁negoti": 12828,
+ "anno": 12829,
+ "▁sans": 12830,
+ "▁Ul": 12831,
+ "CHAR": 12832,
+ "▁edition": 12833,
+ "▁spectrum": 12834,
+ "orie": 12835,
+ "▁execution": 12836,
+ "Please": 12837,
+ "▁BO": 12838,
+ "URN": 12839,
+ "▁cow": 12840,
+ "стан": 12841,
+ "istribution": 12842,
+ "Domain": 12843,
+ "▁readers": 12844,
+ "▁consumer": 12845,
+ "▁styles": 12846,
+ "encode": 12847,
+ "▁Cy": 12848,
+ "Common": 12849,
+ "▁Prop": 12850,
+ "▁execute": 12851,
+ "▁eq": 12852,
+ "▁visitors": 12853,
+ "▁Amb": 12854,
+ "udad": 12855,
+ "qquad": 12856,
+ "▁Cert": 12857,
+ "▁trop": 12858,
+ "▁yesterday": 12859,
+ "tain": 12860,
+ "LD": 12861,
+ "atro": 12862,
+ "▁increases": 12863,
+ "▁Wars": 12864,
+ "ned": 12865,
+ "before": 12866,
+ "aupt": 12867,
+ "▁ERR": 12868,
+ "▁Ford": 12869,
+ "▁dalla": 12870,
+ "ULAR": 12871,
+ "▁strike": 12872,
+ "Arr": 12873,
+ "▁recovery": 12874,
+ "▁Response": 12875,
+ "▁strategies": 12876,
+ "▁ін": 12877,
+ "▁rear": 12878,
+ "▁adults": 12879,
+ "▁Не": 12880,
+ "windows": 12881,
+ "decl": 12882,
+ "olen": 12883,
+ "▁Jord": 12884,
+ "▁Kal": 12885,
+ "▁cui": 12886,
+ "▁Про": 12887,
+ "▁Sever": 12888,
+ "▁ale": 12889,
+ "▁peut": 12890,
+ "Stats": 12891,
+ "▁Ross": 12892,
+ "arten": 12893,
+ "shall": 12894,
+ "▁entertain": 12895,
+ "▁parking": 12896,
+ "нови": 12897,
+ "erre": 12898,
+ "▁funding": 12899,
+ "▁Cle": 12900,
+ "▁Ot": 12901,
+ "unst": 12902,
+ "assertEquals": 12903,
+ "▁cancell": 12904,
+ "TAG": 12905,
+ "▁Early": 12906,
+ "▁feedback": 12907,
+ "▁pand": 12908,
+ "yo": 12909,
+ "▁mirror": 12910,
+ "▁verb": 12911,
+ "▁highlight": 12912,
+ "erialize": 12913,
+ "▁grade": 12914,
+ "лась": 12915,
+ "▁Brook": 12916,
+ "▁LI": 12917,
+ "▁implies": 12918,
+ "▁enorm": 12919,
+ "ają": 12920,
+ "▁Wer": 12921,
+ "away": 12922,
+ "▁machines": 12923,
+ "▁dent": 12924,
+ "Idx": 12925,
+ "▁tid": 12926,
+ ")\"": 12927,
+ "▁mole": 12928,
+ "bold": 12929,
+ "CONT": 12930,
+ "▁ép": 12931,
+ "▁cutting": 12932,
+ "▁Neg": 12933,
+ "▁tong": 12934,
+ "▁networks": 12935,
+ "▁Fall": 12936,
+ "generated": 12937,
+ "▁Pri": 12938,
+ "UEST": 12939,
+ "▁Belg": 12940,
+ "▁sheet": 12941,
+ "кси": 12942,
+ "▁†": 12943,
+ "▁yeah": 12944,
+ "▁Victor": 12945,
+ "▁Rub": 12946,
+ "▁candidates": 12947,
+ "prés": 12948,
+ "▁EU": 12949,
+ "etr": 12950,
+ "▁rolled": 12951,
+ "▁Pas": 12952,
+ "▁Arthur": 12953,
+ "Arch": 12954,
+ "▁Mann": 12955,
+ "American": 12956,
+ "zes": 12957,
+ "inners": 12958,
+ "▁Auto": 12959,
+ "▁professor": 12960,
+ "▁);\r": 12961,
+ "▁addr": 12962,
+ "▁Medical": 12963,
+ "▁fired": 12964,
+ "▁Core": 12965,
+ "▁CONFIG": 12966,
+ "▁sql": 12967,
+ "▁Conserv": 12968,
+ "ichen": 12969,
+ "Vertex": 12970,
+ "▁HO": 12971,
+ "Yeah": 12972,
+ "Note": 12973,
+ "▁OK": 12974,
+ "mus": 12975,
+ "focus": 12976,
+ "aja": 12977,
+ "rá": 12978,
+ "▁hence": 12979,
+ "▁executive": 12980,
+ "▁liquid": 12981,
+ "uje": 12982,
+ "▁driven": 12983,
+ "igue": 12984,
+ "▁Wik": 12985,
+ "Rate": 12986,
+ "rand": 12987,
+ "Results": 12988,
+ "▁copies": 12989,
+ "▁tan": 12990,
+ "riteria": 12991,
+ "enen": 12992,
+ "}_\\": 12993,
+ "▁pobl": 12994,
+ "▁southern": 12995,
+ "eln": 12996,
+ "▁zwei": 12997,
+ "▁concrete": 12998,
+ "▁CONDITIONS": 12999,
+ "▁dreams": 13000,
+ "▁minim": 13001,
+ "▁employee": 13002,
+ "▁nap": 13003,
+ "▁suspect": 13004,
+ "Mouse": 13005,
+ "▁therapy": 13006,
+ "aval": 13007,
+ "▁Anth": 13008,
+ "START": 13009,
+ "sters": 13010,
+ "ishment": 13011,
+ "finite": 13012,
+ "WA": 13013,
+ "vy": 13014,
+ "▁mood": 13015,
+ "comfort": 13016,
+ "▁shr": 13017,
+ "▁decade": 13018,
+ "ября": 13019,
+ "▁'#": 13020,
+ "▁dot": 13021,
+ "▁hill": 13022,
+ "arry": 13023,
+ "catch": 13024,
+ "▁jQuery": 13025,
+ "▁corporate": 13026,
+ "▁BASIS": 13027,
+ "▁appointed": 13028,
+ "▁embar": 13029,
+ "ographie": 13030,
+ "▁pressed": 13031,
+ "▁champion": 13032,
+ "emit": 13033,
+ "▁Bed": 13034,
+ "вання": 13035,
+ "Gui": 13036,
+ "▁PUR": 13037,
+ "▁urban": 13038,
+ "▁sentence": 13039,
+ "bury": 13040,
+ "▁Video": 13041,
+ "▁regularly": 13042,
+ "vl": 13043,
+ "▁слу": 13044,
+ "ockey": 13045,
+ "evin": 13046,
+ "ultural": 13047,
+ "▁passage": 13048,
+ "▁состав": 13049,
+ "▁largely": 13050,
+ "orters": 13051,
+ "▁connections": 13052,
+ "▁surprising": 13053,
+ "bc": 13054,
+ "▁strongly": 13055,
+ "ansas": 13056,
+ "▁sist": 13057,
+ "▁extreme": 13058,
+ "whel": 13059,
+ "▁dealing": 13060,
+ "ographic": 13061,
+ "▁Republican": 13062,
+ "▁granted": 13063,
+ "▁CL": 13064,
+ "▁Hope": 13065,
+ "lessly": 13066,
+ "▁upload": 13067,
+ "▁-\\": 13068,
+ "нию": 13069,
+ "▁valuable": 13070,
+ "=[": 13071,
+ "Price": 13072,
+ "issance": 13073,
+ "iens": 13074,
+ "heit": 13075,
+ "▁suggests": 13076,
+ "сло": 13077,
+ "▁jur": 13078,
+ "}|": 13079,
+ "lp": 13080,
+ "▁invited": 13081,
+ "▁deriv": 13082,
+ "IMIT": 13083,
+ "rass": 13084,
+ "▁instruct": 13085,
+ "▁courses": 13086,
+ "äch": 13087,
+ "▁fifty": 13088,
+ "DEVICE": 13089,
+ "ASH": 13090,
+ "▁hip": 13091,
+ "Unknown": 13092,
+ "▁Catalogue": 13093,
+ "▁Roll": 13094,
+ "▁tensor": 13095,
+ "bec": 13096,
+ "été": 13097,
+ "Identity": 13098,
+ "&\\": 13099,
+ "▁Stephen": 13100,
+ "nodes": 13101,
+ "Dim": 13102,
+ "▁consists": 13103,
+ "▁normally": 13104,
+ "ubl": 13105,
+ "▁Police": 13106,
+ "▁Games": 13107,
+ "five": 13108,
+ "Have": 13109,
+ "▁padding": 13110,
+ "eres": 13111,
+ "anth": 13112,
+ "▁puts": 13113,
+ "uminate": 13114,
+ "ovie": 13115,
+ "▁Index": 13116,
+ "blue": 13117,
+ "Scal": 13118,
+ "▁giant": 13119,
+ "TF": 13120,
+ "pson": 13121,
+ "▁victim": 13122,
+ "serial": 13123,
+ "▁Sym": 13124,
+ "Single": 13125,
+ "▁md": 13126,
+ "▁attended": 13127,
+ "▁Stra": 13128,
+ "▁Dark": 13129,
+ ")|": 13130,
+ "▁span": 13131,
+ "▁maintenance": 13132,
+ "▁bind": 13133,
+ "Bean": 13134,
+ "ilarly": 13135,
+ "▁convent": 13136,
+ "▁José": 13137,
+ "udd": 13138,
+ "▁poly": 13139,
+ "▁idx": 13140,
+ "▁asks": 13141,
+ "▁enthus": 13142,
+ "▁suck": 13143,
+ "▁Cou": 13144,
+ "▁Corporation": 13145,
+ "usions": 13146,
+ "opher": 13147,
+ "▁symptoms": 13148,
+ "▁Johann": 13149,
+ "▁пу": 13150,
+ "▁html": 13151,
+ "▁ps": 13152,
+ "earing": 13153,
+ "gesch": 13154,
+ "▁Mother": 13155,
+ "RET": 13156,
+ "▁furniture": 13157,
+ "PF": 13158,
+ "▁Guard": 13159,
+ "pattern": 13160,
+ "▁lovely": 13161,
+ "alg": 13162,
+ "edly": 13163,
+ "sex": 13164,
+ "▁finds": 13165,
+ "Buf": 13166,
+ "▁над": 13167,
+ "▁км": 13168,
+ "▁Por": 13169,
+ "СР": 13170,
+ "Enter": 13171,
+ "▁esta": 13172,
+ "▁тре": 13173,
+ "▁\"*": 13174,
+ "▁Fox": 13175,
+ "▁cock": 13176,
+ "Bundle": 13177,
+ "▁puis": 13178,
+ "▁announce": 13179,
+ "▁guid": 13180,
+ "checked": 13181,
+ "icide": 13182,
+ "neg": 13183,
+ "▁Gil": 13184,
+ "schen": 13185,
+ "ologist": 13186,
+ "iso": 13187,
+ "groups": 13188,
+ "▁somebody": 13189,
+ "Day": 13190,
+ "tras": 13191,
+ "▁compact": 13192,
+ "▁organized": 13193,
+ "▁roles": 13194,
+ "▁hint": 13195,
+ "▁så": 13196,
+ "▁pays": 13197,
+ "▁Си": 13198,
+ "▁hoped": 13199,
+ "▁sail": 13200,
+ "▁Vers": 13201,
+ "▁embr": 13202,
+ "▁bot": 13203,
+ "▁exceed": 13204,
+ "BACK": 13205,
+ "▁gaze": 13206,
+ "▁spons": 13207,
+ "AST": 13208,
+ "▁torch": 13209,
+ "▁newspaper": 13210,
+ "▁Dist": 13211,
+ "▁bass": 13212,
+ "▁hanging": 13213,
+ "▁ears": 13214,
+ "ńsk": 13215,
+ "getValue": 13216,
+ "▁unus": 13217,
+ "▁Ele": 13218,
+ "services": 13219,
+ "▁dressed": 13220,
+ "lav": 13221,
+ "▁пла": 13222,
+ "Private": 13223,
+ "mic": 13224,
+ "▁parser": 13225,
+ "▁sections": 13226,
+ "▁fo": 13227,
+ "Errorf": 13228,
+ "inz": 13229,
+ "örd": 13230,
+ "▁metric": 13231,
+ "URI": 13232,
+ "▁vice": 13233,
+ "RED": 13234,
+ "▁nue": 13235,
+ "revs": 13236,
+ "▁collected": 13237,
+ "oose": 13238,
+ "▁mond": 13239,
+ "▁nas": 13240,
+ "▁Насе": 13241,
+ "▁å": 13242,
+ "Drop": 13243,
+ "▁abuse": 13244,
+ "▁sees": 13245,
+ "▁Hence": 13246,
+ "exec": 13247,
+ "}\\,": 13248,
+ "▁arbitr": 13249,
+ "▁Application": 13250,
+ "family": 13251,
+ "üd": 13252,
+ "▁magnetic": 13253,
+ "▁newly": 13254,
+ "▁reprodu": 13255,
+ "▁writers": 13256,
+ "▁headers": 13257,
+ "ší": 13258,
+ "рт": 13259,
+ "YPE": 13260,
+ "▁schema": 13261,
+ "▁Ce": 13262,
+ "▁Jews": 13263,
+ "▁Record": 13264,
+ "present": 13265,
+ "▁также": 13266,
+ "▁labels": 13267,
+ "Socket": 13268,
+ "▁equations": 13269,
+ "▁medicine": 13270,
+ "▁authorities": 13271,
+ "}`": 13272,
+ "стви": 13273,
+ "▁Corn": 13274,
+ "▁environmental": 13275,
+ "WARE": 13276,
+ "Mer": 13277,
+ "▁само": 13278,
+ "▁Technology": 13279,
+ "▁Saf": 13280,
+ "▁conn": 13281,
+ "▁Um": 13282,
+ "▁Pacific": 13283,
+ "тел": 13284,
+ "jan": 13285,
+ "▁uncertain": 13286,
+ "▁belief": 13287,
+ "counter": 13288,
+ "toBe": 13289,
+ "INS": 13290,
+ "weet": 13291,
+ "Light": 13292,
+ "primary": 13293,
+ "▁featured": 13294,
+ "▁touched": 13295,
+ "HTTP": 13296,
+ "▁tact": 13297,
+ "pository": 13298,
+ "▁eines": 13299,
+ "lass": 13300,
+ "ська": 13301,
+ "▁przez": 13302,
+ "▁fuer": 13303,
+ "▁exciting": 13304,
+ "▁Cub": 13305,
+ "agan": 13306,
+ "VO": 13307,
+ "▁'%": 13308,
+ "▁\\{": 13309,
+ "ubble": 13310,
+ "▁Fol": 13311,
+ "▁Kong": 13312,
+ "▁versch": 13313,
+ "FAIL": 13314,
+ "▁naar": 13315,
+ "ös": 13316,
+ "speed": 13317,
+ "▁territor": 13318,
+ "▁wrap": 13319,
+ "▁Jahre": 13320,
+ "lee": 13321,
+ "▁crossed": 13322,
+ "resolve": 13323,
+ "▁stim": 13324,
+ "Native": 13325,
+ "ursor": 13326,
+ "NotNull": 13327,
+ "▁Albert": 13328,
+ "▁signature": 13329,
+ "▁Ru": 13330,
+ "idas": 13331,
+ "▁decent": 13332,
+ "▁faced": 13333,
+ "▁лю": 13334,
+ "▁Spain": 13335,
+ "▁resistance": 13336,
+ "▁Brian": 13337,
+ "kwargs": 13338,
+ "▁interval": 13339,
+ "▁Ле": 13340,
+ "▁explo": 13341,
+ "▁semi": 13342,
+ "▁widely": 13343,
+ "dx": 13344,
+ "kov": 13345,
+ "▁Come": 13346,
+ "▁knife": 13347,
+ "Asp": 13348,
+ "uno": 13349,
+ "lineto": 13350,
+ "▁Bund": 13351,
+ "Cert": 13352,
+ "▁todo": 13353,
+ "tags": 13354,
+ "▁guarantee": 13355,
+ "▁vital": 13356,
+ "▁fought": 13357,
+ "▁Env": 13358,
+ "HD": 13359,
+ "Lower": 13360,
+ "Tx": 13361,
+ "▁Fa": 13362,
+ "▁anticip": 13363,
+ "Timer": 13364,
+ "mediate": 13365,
+ "▁proven": 13366,
+ "▁partir": 13367,
+ "AE": 13368,
+ "cursor": 13369,
+ "▁wooden": 13370,
+ "▁Contact": 13371,
+ "regs": 13372,
+ "▁provinc": 13373,
+ "▁DC": 13374,
+ "▁memories": 13375,
+ "▁ft": 13376,
+ "▁battery": 13377,
+ "utenant": 13378,
+ "Login": 13379,
+ "ountry": 13380,
+ "▁compens": 13381,
+ "operatorname": 13382,
+ "▁Jacob": 13383,
+ "zed": 13384,
+ "ADDR": 13385,
+ "▁quad": 13386,
+ "*).": 13387,
+ "▁coat": 13388,
+ "▁fir": 13389,
+ "▁Michel": 13390,
+ "▁Standard": 13391,
+ "rf": 13392,
+ "mel": 13393,
+ "▁coeff": 13394,
+ "▁Iraq": 13395,
+ "▁Given": 13396,
+ "нима": 13397,
+ "▁FIT": 13398,
+ "▁peu": 13399,
+ "▁ig": 13400,
+ "▁Case": 13401,
+ "mé": 13402,
+ "▁parallel": 13403,
+ "cio": 13404,
+ "kow": 13405,
+ "▁institutions": 13406,
+ "ícul": 13407,
+ "aban": 13408,
+ "UX": 13409,
+ "▁Sarah": 13410,
+ "▁més": 13411,
+ "▁atmos": 13412,
+ "▁släktet": 13413,
+ "▁brothers": 13414,
+ "▁wanting": 13415,
+ "aaaa": 13416,
+ "▁fest": 13417,
+ "=-": 13418,
+ "▁forty": 13419,
+ "▁creates": 13420,
+ "hh": 13421,
+ "▁Android": 13422,
+ "anches": 13423,
+ "BT": 13424,
+ "upload": 13425,
+ "xis": 13426,
+ "Hz": 13427,
+ "бор": 13428,
+ "RAY": 13429,
+ "ntil": 13430,
+ "▁leaned": 13431,
+ "unda": 13432,
+ "▁ultimately": 13433,
+ "▁tok": 13434,
+ "neh": 13435,
+ "▁lawyer": 13436,
+ "hend": 13437,
+ "▁Vin": 13438,
+ "▁facility": 13439,
+ "▁likes": 13440,
+ "ento": 13441,
+ "Nodes": 13442,
+ "▁entrance": 13443,
+ "atto": 13444,
+ "rett": 13445,
+ "accept": 13446,
+ "theme": 13447,
+ "тан": 13448,
+ "osi": 13449,
+ "▁{},": 13450,
+ "pgfpathlineto": 13451,
+ "good": 13452,
+ "slot": 13453,
+ "▁innoc": 13454,
+ "▁proport": 13455,
+ "▁arrive": 13456,
+ "ého": 13457,
+ "▁pairs": 13458,
+ "▁wrapped": 13459,
+ "▁unw": 13460,
+ "▁explos": 13461,
+ "▁gel": 13462,
+ "Will": 13463,
+ "▁Zealand": 13464,
+ "ías": 13465,
+ "▁Jr": 13466,
+ "▁Fra": 13467,
+ "▁legit": 13468,
+ "▁illegal": 13469,
+ "клю": 13470,
+ "▁tort": 13471,
+ "▁pron": 13472,
+ "Fi": 13473,
+ "▁forg": 13474,
+ "export": 13475,
+ "▁Children": 13476,
+ "▁Abs": 13477,
+ "▁Send": 13478,
+ "▁discount": 13479,
+ "▁poster": 13480,
+ "ented": 13481,
+ "anim": 13482,
+ "verb": 13483,
+ "sto": 13484,
+ "▁Bible": 13485,
+ "pending": 13486,
+ "▁Phot": 13487,
+ "strap": 13488,
+ "ieron": 13489,
+ "PG": 13490,
+ "cular": 13491,
+ "crit": 13492,
+ "urd": 13493,
+ "ENO": 13494,
+ "▁northern": 13495,
+ "▁naturally": 13496,
+ "<'": 13497,
+ "weg": 13498,
+ "▁drunk": 13499,
+ "▁Dal": 13500,
+ "▁mouse": 13501,
+ "▁continuous": 13502,
+ "▁initially": 13503,
+ "agu": 13504,
+ "мпи": 13505,
+ "ANT": 13506,
+ "Div": 13507,
+ "▁recording": 13508,
+ "Bind": 13509,
+ "▁correctly": 13510,
+ "initial": 13511,
+ "▁Rights": 13512,
+ "▁debate": 13513,
+ "WRITE": 13514,
+ "built": 13515,
+ "▁permit": 13516,
+ "▁professionals": 13517,
+ "cv": 13518,
+ "▁DI": 13519,
+ "▁handed": 13520,
+ "▁Cu": 13521,
+ "▁Hospital": 13522,
+ "▁beskrevs": 13523,
+ "ней": 13524,
+ "ност": 13525,
+ "▁anxiety": 13526,
+ "▁heavily": 13527,
+ "▁Var": 13528,
+ "▁dispos": 13529,
+ "+\"": 13530,
+ "▁Ever": 13531,
+ "izon": 13532,
+ "▁operators": 13533,
+ "nego": 13534,
+ "▁Bry": 13535,
+ "▁votes": 13536,
+ "izione": 13537,
+ "▁рай": 13538,
+ "▁feat": 13539,
+ "▁western": 13540,
+ "▁confront": 13541,
+ "▁stronger": 13542,
+ "▁фа": 13543,
+ "stre": 13544,
+ "▁Valid": 13545,
+ "▁nad": 13546,
+ "▁checking": 13547,
+ "▁birds": 13548,
+ "▁Northern": 13549,
+ "▁intention": 13550,
+ "uce": 13551,
+ "▁covers": 13552,
+ "▁wondering": 13553,
+ "▁Optional": 13554,
+ "protocol": 13555,
+ "▁aggress": 13556,
+ "——": 13557,
+ "Vec": 13558,
+ "▁dates": 13559,
+ "quot": 13560,
+ "▁bom": 13561,
+ "▁scan": 13562,
+ "▁Item": 13563,
+ "▁Navy": 13564,
+ "▁Gran": 13565,
+ "▁everybody": 13566,
+ "▁unexpected": 13567,
+ "▁divor": 13568,
+ "▁ease": 13569,
+ "umbled": 13570,
+ "^+": 13571,
+ "cuss": 13572,
+ "▁pale": 13573,
+ "▁Inga": 13574,
+ "▁Broad": 13575,
+ "▁Medic": 13576,
+ "▁Roy": 13577,
+ "▁Inn": 13578,
+ "▁pens": 13579,
+ "PN": 13580,
+ ".:": 13581,
+ "▁principle": 13582,
+ "▁letting": 13583,
+ "▁conducted": 13584,
+ "FALSE": 13585,
+ "▁OS": 13586,
+ "Focus": 13587,
+ "▁measured": 13588,
+ "▁Democratic": 13589,
+ "High": 13590,
+ "▁pré": 13591,
+ "ennes": 13592,
+ "▁indicates": 13593,
+ "▁ending": 13594,
+ "▁Small": 13595,
+ "▁": 27113,
+ "olent": 27114,
+ "▁этого": 27115,
+ "▁Generic": 27116,
+ "▁*/,": 27117,
+ "▁combinations": 27118,
+ "▁rejo": 27119,
+ "спубли": 27120,
+ "capacity": 27121,
+ "▁traces": 27122,
+ "▁opacity": 27123,
+ "▁Official": 27124,
+ "icion": 27125,
+ "▁emotionally": 27126,
+ "▁Joel": 27127,
+ "ському": 27128,
+ "▁legendary": 27129,
+ "▁pam": 27130,
+ "▁También": 27131,
+ ".<": 27132,
+ "iba": 27133,
+ "midt": 27134,
+ "бом": 27135,
+ "▁ensuite": 27136,
+ "Authorization": 27137,
+ "Pag": 27138,
+ "▁helmet": 27139,
+ "▁territo": 27140,
+ "secondary": 27141,
+ "▁segunda": 27142,
+ "▁Wire": 27143,
+ "recated": 27144,
+ "▁invoked": 27145,
+ "▁ValueError": 27146,
+ "▁фо": 27147,
+ "ALIGN": 27148,
+ "CURRENT": 27149,
+ "\\+\\_\\": 27150,
+ "▁compilation": 27151,
+ "ær": 27152,
+ "▁Palmar": 27153,
+ "▁influences": 27154,
+ "/:": 27155,
+ "Mix": 27156,
+ "NOP": 27157,
+ "econom": 27158,
+ "▁tucked": 27159,
+ "▁});\r": 27160,
+ "ANK": 27161,
+ "reject": 27162,
+ "▁pension": 27163,
+ "▁generates": 27164,
+ "чё": 27165,
+ "▁incap": 27166,
+ "▁clicked": 27167,
+ "▁fus": 27168,
+ "ourses": 27169,
+ "▁Easter": 27170,
+ "%;": 27171,
+ "zin": 27172,
+ "▁obligations": 27173,
+ "▁Tips": 27174,
+ "};\r": 27175,
+ ".\"_": 27176,
+ "▁BSD": 27177,
+ "ática": 27178,
+ "▁expose": 27179,
+ "Pars": 27180,
+ "▁Amanda": 27181,
+ "куп": 27182,
+ "▁guessed": 27183,
+ "dsi": 27184,
+ "▁Leip": 27185,
+ "Broad": 27186,
+ "▁Hughes": 27187,
+ "ié": 27188,
+ "▁Wahl": 27189,
+ "▁formerly": 27190,
+ "Relative": 27191,
+ "▁Yu": 27192,
+ "▁Mountains": 27193,
+ "▁Enum": 27194,
+ "▁strang": 27195,
+ "_-": 27196,
+ "recht": 27197,
+ "viv": 27198,
+ "pause": 27199,
+ "▁Londres": 27200,
+ "▁elbow": 27201,
+ "▁Hawaii": 27202,
+ "▁Casino": 27203,
+ "Threshold": 27204,
+ "Units": 27205,
+ "Include": 27206,
+ "ито": 27207,
+ "asury": 27208,
+ "▁steht": 27209,
+ "▁damned": 27210,
+ "▁packets": 27211,
+ "▁Werk": 27212,
+ "▁elevator": 27213,
+ "iedad": 27214,
+ "govern": 27215,
+ "▁CONTRACT": 27216,
+ "mals": 27217,
+ "▁remem": 27218,
+ "▁entonces": 27219,
+ "▁vas": 27220,
+ "▁sympathy": 27221,
+ "▁befindet": 27222,
+ "incing": 27223,
+ "DataSet": 27224,
+ "▁additionally": 27225,
+ "▁musician": 27226,
+ "шего": 27227,
+ "▁listop": 27228,
+ ">\")": 27229,
+ "Printf": 27230,
+ "▁Felix": 27231,
+ "▁carved": 27232,
+ "▁nicely": 27233,
+ "гом": 27234,
+ "chap": 27235,
+ "▁Nieder": 27236,
+ "▁Lav": 27237,
+ "▁modifications": 27238,
+ "moment": 27239,
+ "▁balcon": 27240,
+ "▁dependency": 27241,
+ "CKET": 27242,
+ "▁vanished": 27243,
+ "▁fighters": 27244,
+ "▁zunächst": 27245,
+ "ioctl": 27246,
+ "▁defens": 27247,
+ "▁Nem": 27248,
+ "Utility": 27249,
+ "▁curv": 27250,
+ "▁DAMAGES": 27251,
+ "▁Rogers": 27252,
+ "▁gratitude": 27253,
+ "▁Denmark": 27254,
+ "рая": 27255,
+ "grpc": 27256,
+ "▁juni": 27257,
+ "▁октября": 27258,
+ "▁immense": 27259,
+ "▁prevented": 27260,
+ "▁foam": 27261,
+ "▁Extra": 27262,
+ "aimed": 27263,
+ "▁Criteria": 27264,
+ "▁Simply": 27265,
+ "boxes": 27266,
+ "▁Legend": 27267,
+ "▁Players": 27268,
+ "▁Mercedes": 27269,
+ "▁Branch": 27270,
+ "TERN": 27271,
+ "omena": 27272,
+ "▁incorporate": 27273,
+ "conde": 27274,
+ "▁Estado": 27275,
+ "▁wasted": 27276,
+ "▁complaining": 27277,
+ "▁warriors": 27278,
+ "oter": 27279,
+ "▁этом": 27280,
+ "▁conten": 27281,
+ "▁machinery": 27282,
+ "▁technological": 27283,
+ "▁TD": 27284,
+ "▁gras": 27285,
+ "▁minimize": 27286,
+ "▁Door": 27287,
+ "▁bzw": 27288,
+ "▁prac": 27289,
+ "TREE": 27290,
+ "▁Wing": 27291,
+ "▁Transaction": 27292,
+ "▁MVT": 27293,
+ "▁Klein": 27294,
+ "commons": 27295,
+ "▁}{": 27296,
+ "▁Heritage": 27297,
+ "▁fade": 27298,
+ "рок": 27299,
+ "setValue": 27300,
+ "▁Wallace": 27301,
+ "MX": 27302,
+ "▁ACT": 27303,
+ "▁footage": 27304,
+ "▁entstand": 27305,
+ "arga": 27306,
+ "▁nails": 27307,
+ "▁capitalism": 27308,
+ "▁Garc": 27309,
+ "▁suspension": 27310,
+ "ilis": 27311,
+ "▁Mov": 27312,
+ "uffled": 27313,
+ "Arc": 27314,
+ "▁Beautiful": 27315,
+ "WAY": 27316,
+ "Parallel": 27317,
+ "XXXX": 27318,
+ "diag": 27319,
+ "▁DT": 27320,
+ "mq": 27321,
+ "TextView": 27322,
+ "MLE": 27323,
+ "ennen": 27324,
+ "▁infected": 27325,
+ "▁therapist": 27326,
+ "INGS": 27327,
+ "▁cidade": 27328,
+ "ън": 27329,
+ "▁pdf": 27330,
+ "▁bump": 27331,
+ "CTX": 27332,
+ "▁INCLUDING": 27333,
+ "▁Gef": 27334,
+ "ENTIAL": 27335,
+ "▁handy": 27336,
+ "▁temporal": 27337,
+ "AtA": 27338,
+ "ISH": 27339,
+ "▁Pattern": 27340,
+ "▁lan": 27341,
+ "ependant": 27342,
+ "▁shining": 27343,
+ "idy": 27344,
+ "▁NT": 27345,
+ "▁Fran": 27346,
+ "▁nurses": 27347,
+ "▁betray": 27348,
+ "▁sensible": 27349,
+ "▁апреля": 27350,
+ "▁'[": 27351,
+ "▁thirteen": 27352,
+ ")}_{": 27353,
+ "▁Noah": 27354,
+ "INSERT": 27355,
+ "istically": 27356,
+ "▁Appendix": 27357,
+ "▁recher": 27358,
+ "Receiver": 27359,
+ "▁dernier": 27360,
+ "лла": 27361,
+ "лиза": 27362,
+ "▁Partido": 27363,
+ "▁maximal": 27364,
+ "snap": 27365,
+ "▁часть": 27366,
+ "STOP": 27367,
+ "▁ultra": 27368,
+ "▁développ": 27369,
+ "▁tegen": 27370,
+ "▁Чи": 27371,
+ "LIB": 27372,
+ "▁baseline": 27373,
+ "reload": 27374,
+ "▁Arbitro": 27375,
+ "▁kall": 27376,
+ "capture": 27377,
+ "Arm": 27378,
+ "quin": 27379,
+ "impse": 27380,
+ "zas": 27381,
+ "▁Cand": 27382,
+ "▁brains": 27383,
+ "▁hostile": 27384,
+ "▁marble": 27385,
+ "oons": 27386,
+ "▁Loss": 27387,
+ "MetaData": 27388,
+ "▁República": 27389,
+ "▁andra": 27390,
+ "oden": 27391,
+ "▁documented": 27392,
+ "▁Moses": 27393,
+ "odd": 27394,
+ "▁wax": 27395,
+ "usch": 27396,
+ "▁diagnosed": 27397,
+ "inkle": 27398,
+ "▁Xbox": 27399,
+ "▁seventy": 27400,
+ "cias": 27401,
+ "▁noviembre": 27402,
+ "Compute": 27403,
+ "});\r": 27404,
+ "▁Philippe": 27405,
+ "▁För": 27406,
+ "Leave": 27407,
+ "▁sage": 27408,
+ "▁unpre": 27409,
+ "▁Fortunately": 27410,
+ "▁apost": 27411,
+ "entities": 27412,
+ "▁ellos": 27413,
+ "authorized": 27414,
+ "GBT": 27415,
+ "▁insist": 27416,
+ "▁inspire": 27417,
+ "Mass": 27418,
+ "▁rôle": 27419,
+ "fee": 27420,
+ "ipart": 27421,
+ "цер": 27422,
+ "unate": 27423,
+ "▁CNN": 27424,
+ ":}": 27425,
+ "▁unhappy": 27426,
+ "▁imported": 27427,
+ "HIGH": 27428,
+ "rings": 27429,
+ "▁Instance": 27430,
+ "Bay": 27431,
+ "agles": 27432,
+ "mee": 27433,
+ "bery": 27434,
+ "▁Stories": 27435,
+ "▁Chase": 27436,
+ "▁carriage": 27437,
+ "▁misunder": 27438,
+ "▁imagin": 27439,
+ "pw": 27440,
+ "▁Meter": 27441,
+ "▁crowds": 27442,
+ "▁Fame": 27443,
+ "skill": 27444,
+ "▁comed": 27445,
+ "▁ranch": 27446,
+ "▁lacking": 27447,
+ "▁submar": 27448,
+ "iante": 27449,
+ "▁lanz": 27450,
+ "▁служ": 27451,
+ "-----------": 27452,
+ "▁obten": 27453,
+ "▁downstairs": 27454,
+ "YN": 27455,
+ "rotation": 27456,
+ "▁Jesse": 27457,
+ "$(\"#": 27458,
+ "▁puls": 27459,
+ "irling": 27460,
+ "▁Schaus": 27461,
+ "▁deployed": 27462,
+ "▁{}\",": 27463,
+ "▁Marvel": 27464,
+ "ENUM": 27465,
+ "▁Mathemat": 27466,
+ "▁nn": 27467,
+ "compet": 27468,
+ "ków": 27469,
+ "bil": 27470,
+ "Which": 27471,
+ "isine": 27472,
+ "▁rude": 27473,
+ "▁niveau": 27474,
+ "▁área": 27475,
+ "▁près": 27476,
+ "atis": 27477,
+ "▁[...]": 27478,
+ "fur": 27479,
+ "omm": 27480,
+ "packed": 27481,
+ "мене": 27482,
+ "scriptstyle": 27483,
+ "▁Ath": 27484,
+ "▁desp": 27485,
+ "eltemperaturen": 27486,
+ "▁talents": 27487,
+ "ocy": 27488,
+ "▁raises": 27489,
+ "LIMIT": 27490,
+ "▁editorial": 27491,
+ "▁Animal": 27492,
+ "drive": 27493,
+ "▁работа": 27494,
+ "bss": 27495,
+ "▁Sev": 27496,
+ "epoch": 27497,
+ "▁RC": 27498,
+ "UNUSED": 27499,
+ "▁mandatory": 27500,
+ "(?:": 27501,
+ "▁Bin": 27502,
+ "▁synthetic": 27503,
+ "▁gown": 27504,
+ "▁Dob": 27505,
+ "kap": 27506,
+ "▁harmon": 27507,
+ "▁liberty": 27508,
+ "▁Rice": 27509,
+ "▁prayers": 27510,
+ "▁mise": 27511,
+ "▁confusing": 27512,
+ "▁leap": 27513,
+ "▁arrives": 27514,
+ "kamp": 27515,
+ "▁thats": 27516,
+ "ACC": 27517,
+ "▁Parameters": 27518,
+ "▁одно": 27519,
+ "▁Bio": 27520,
+ "density": 27521,
+ "▁glimpse": 27522,
+ "FORE": 27523,
+ "▁Listen": 27524,
+ "Prev": 27525,
+ "}\\,\\": 27526,
+ "куль": 27527,
+ "▁SEC": 27528,
+ "▁explored": 27529,
+ "▁meantime": 27530,
+ "AIL": 27531,
+ "▁WP": 27532,
+ "▁raison": 27533,
+ "▁existe": 27534,
+ "▁lesser": 27535,
+ "▁Validate": 27536,
+ "▁caution": 27537,
+ "usta": 27538,
+ "heading": 27539,
+ "EFF": 27540,
+ ".'\"": 27541,
+ "▁Gilbert": 27542,
+ "▁limitation": 27543,
+ "▁retour": 27544,
+ "▁Commonwealth": 27545,
+ "▁gewann": 27546,
+ "▁miserable": 27547,
+ "▁networking": 27548,
+ "▁ottobre": 27549,
+ "▁Dise": 27550,
+ "edges": 27551,
+ "▁sede": 27552,
+ "вича": 27553,
+ "uniform": 27554,
+ "▁деятель": 27555,
+ "iros": 27556,
+ "▁desen": 27557,
+ "▁parc": 27558,
+ "▁Rico": 27559,
+ "Ns": 27560,
+ "guid": 27561,
+ "orio": 27562,
+ "avelength": 27563,
+ "▁Gle": 27564,
+ "inceton": 27565,
+ "Amaz": 27566,
+ "Construct": 27567,
+ "▁mx": 27568,
+ "▁Vern": 27569,
+ "▁Generation": 27570,
+ "Jack": 27571,
+ "romag": 27572,
+ "▁viagra": 27573,
+ "▁Peg": 27574,
+ "▁Updated": 27575,
+ "▁overlap": 27576,
+ "EventArgs": 27577,
+ "кро": 27578,
+ "▁*«": 27579,
+ "▁questioned": 27580,
+ "South": 27581,
+ "notice": 27582,
+ "▁permanently": 27583,
+ "lst": 27584,
+ "ficie": 27585,
+ "▁quella": 27586,
+ "▁colleges": 27587,
+ "▁disappointment": 27588,
+ "▁Luft": 27589,
+ "imgur": 27590,
+ "▁transitions": 27591,
+ "▁seller": 27592,
+ "▁июня": 27593,
+ "▁Og": 27594,
+ "▁ADD": 27595,
+ "▁Pays": 27596,
+ "COMMAND": 27597,
+ "grades": 27598,
+ "▁febbra": 27599,
+ "▁Cyr": 27600,
+ "▁febbraio": 27601,
+ "eti": 27602,
+ "▁arom": 27603,
+ "▁Claude": 27604,
+ "▁UEFA": 27605,
+ "▁живе": 27606,
+ "▁Victorian": 27607,
+ "keeping": 27608,
+ "ên": 27609,
+ "▁FIXME": 27610,
+ "itime": 27611,
+ "chestr": 27612,
+ "▁Samsung": 27613,
+ "▁doctrine": 27614,
+ "▁pear": 27615,
+ "▁Mediterranean": 27616,
+ "▁Ya": 27617,
+ "▁vault": 27618,
+ "▁Historic": 27619,
+ "▁sedan": 27620,
+ "▁heated": 27621,
+ "▁política": 27622,
+ "Proof": 27623,
+ ":{": 27624,
+ "fem": 27625,
+ "▁Frankfurt": 27626,
+ "pectives": 27627,
+ "MG": 27628,
+ "▁Eye": 27629,
+ "dai": 27630,
+ "▁reserves": 27631,
+ "NER": 27632,
+ "▁tobacco": 27633,
+ "▁fragments": 27634,
+ "icc": 27635,
+ "▁booth": 27636,
+ "▁cruise": 27637,
+ "▁Testament": 27638,
+ "cola": 27639,
+ "▁Leop": 27640,
+ "▁noon": 27641,
+ "▁terrified": 27642,
+ "vb": 27643,
+ "intel": 27644,
+ "alie": 27645,
+ "▁verification": 27646,
+ "yster": 27647,
+ "ADER": 27648,
+ "chied": 27649,
+ "▁datasets": 27650,
+ "▁зі": 27651,
+ "▁miem": 27652,
+ "ulates": 27653,
+ "▁uuid": 27654,
+ "▁Pictures": 27655,
+ "▁Brend": 27656,
+ "Billboard": 27657,
+ "▁stern": 27658,
+ "▁denom": 27659,
+ "▁accidents": 27660,
+ "сня": 27661,
+ "▁packing": 27662,
+ "ција": 27663,
+ "iblical": 27664,
+ "▁Так": 27665,
+ "▁whisk": 27666,
+ "▁luego": 27667,
+ "▁rectangle": 27668,
+ "▁hooks": 27669,
+ "▁neglect": 27670,
+ "▁sober": 27671,
+ "proposition": 27672,
+ "Multiple": 27673,
+ ":\",": 27674,
+ "▁bapt": 27675,
+ "Parts": 27676,
+ "▁Selection": 27677,
+ "▁Alpha": 27678,
+ "weights": 27679,
+ "hall": 27680,
+ "соб": 27681,
+ "▁lur": 27682,
+ "▁época": 27683,
+ "▁rested": 27684,
+ "ambigu": 27685,
+ "▁tastes": 27686,
+ "amazonaws": 27687,
+ "▁confess": 27688,
+ "▁diciembre": 27689,
+ "implement": 27690,
+ "▁absorption": 27691,
+ "Hal": 27692,
+ "LEAN": 27693,
+ "▁Zach": 27694,
+ "▁freeze": 27695,
+ "LBL": 27696,
+ "STM": 27697,
+ "▁calc": 27698,
+ "={()": 27699,
+ "=*/": 27700,
+ "▁bt": 27701,
+ "Reb": 27702,
+ "▁Wien": 27703,
+ "anska": 27704,
+ "▁surn": 27705,
+ "iative": 27706,
+ "▁invån": 27707,
+ "CY": 27708,
+ "▁là": 27709,
+ "amba": 27710,
+ "leen": 27711,
+ "wahl": 27712,
+ "▁functioning": 27713,
+ "ția": 27714,
+ "getContext": 27715,
+ "gart": 27716,
+ "▁обе": 27717,
+ "Pen": 27718,
+ "vik": 27719,
+ "Slider": 27720,
+ "▁Accept": 27721,
+ "Gap": 27722,
+ "▁Jorge": 27723,
+ "SIG": 27724,
+ "▁вос": 27725,
+ "▁голо": 27726,
+ "▁periodo": 27727,
+ "шта": 27728,
+ "▁patches": 27729,
+ "кої": 27730,
+ "äre": 27731,
+ "engono": 27732,
+ "lista": 27733,
+ "horn": 27734,
+ "▁Complex": 27735,
+ "Sent": 27736,
+ "trfs": 27737,
+ "▁convex": 27738,
+ "Generation": 27739,
+ "▁місце": 27740,
+ "compress": 27741,
+ "▁Sax": 27742,
+ "▁uid": 27743,
+ "▁Lebens": 27744,
+ "Completion": 27745,
+ "\\|_{": 27746,
+ "insky": 27747,
+ "▁schon": 27748,
+ "▁masters": 27749,
+ "independ": 27750,
+ "neys": 27751,
+ "▁lied": 27752,
+ "▁aspir": 27753,
+ "чні": 27754,
+ "▁breakdown": 27755,
+ "▁Harm": 27756,
+ "▁designing": 27757,
+ "hf": 27758,
+ "▁Angela": 27759,
+ "▁confer": 27760,
+ "▁partido": 27761,
+ "▁interference": 27762,
+ "mao": 27763,
+ "▁absorbed": 27764,
+ "▁Vall": 27765,
+ "ErrorCode": 27766,
+ "▁Publishing": 27767,
+ "vano": 27768,
+ "BITS": 27769,
+ "▁deer": 27770,
+ "▁Campaign": 27771,
+ "▁graz": 27772,
+ "CHANGE": 27773,
+ "▁feder": 27774,
+ "iffe": 27775,
+ "handed": 27776,
+ "cq": 27777,
+ "umbing": 27778,
+ "▁unre": 27779,
+ "▁siendo": 27780,
+ "▁simpler": 27781,
+ "why": 27782,
+ "arettes": 27783,
+ "anst": 27784,
+ "▁hass": 27785,
+ "▁Enterprise": 27786,
+ "▁mois": 27787,
+ "▁Fo": 27788,
+ "▁участ": 27789,
+ "ffen": 27790,
+ "▁MODULE": 27791,
+ "▁activated": 27792,
+ "▁internacional": 27793,
+ "▁Mittel": 27794,
+ "degree": 27795,
+ "▁откры": 27796,
+ "▁&(": 27797,
+ "getProperty": 27798,
+ "isz": 27799,
+ "cedure": 27800,
+ "▁enters": 27801,
+ "▁Sally": 27802,
+ "▁Train": 27803,
+ "▁logged": 27804,
+ "▁Rav": 27805,
+ "▁Avoid": 27806,
+ "▁Kaiser": 27807,
+ "▁expend": 27808,
+ "aphor": 27809,
+ "▁brass": 27810,
+ "▁melod": 27811,
+ "▁attitudes": 27812,
+ "*\"": 27813,
+ "Wall": 27814,
+ "▁owe": 27815,
+ "▁bamb": 27816,
+ "shader": 27817,
+ "cester": 27818,
+ "▁PP": 27819,
+ "▁migrations": 27820,
+ "entric": 27821,
+ "▁Setup": 27822,
+ "▁Artist": 27823,
+ "hre": 27824,
+ "▁polite": 27825,
+ "ahan": 27826,
+ "▁luglio": 27827,
+ "▁predecess": 27828,
+ "▁SIG": 27829,
+ "тів": 27830,
+ "▁RF": 27831,
+ "▁Dry": 27832,
+ "▁maker": 27833,
+ "шим": 27834,
+ "▁Sounds": 27835,
+ "▁implementing": 27836,
+ "▁ah": 27837,
+ "▁gev": 27838,
+ "▁duplicate": 27839,
+ "▁Logan": 27840,
+ "▁Grade": 27841,
+ "DUCT": 27842,
+ "íses": 27843,
+ "ért": 27844,
+ "▁nonsense": 27845,
+ "backup": 27846,
+ "Attachment": 27847,
+ "▁ecc": 27848,
+ "▁Squadron": 27849,
+ "learn": 27850,
+ "deprecated": 27851,
+ "▁Aub": 27852,
+ "▁Gol": 27853,
+ "▁overl": 27854,
+ "SERVICE": 27855,
+ "▁beautifully": 27856,
+ "REL": 27857,
+ "▁Gian": 27858,
+ "▁Papa": 27859,
+ "respond": 27860,
+ "▁Caribbean": 27861,
+ "rn": 27862,
+ "▁худож": 27863,
+ "Cfg": 27864,
+ "rai": 27865,
+ "▁sniff": 27866,
+ "tto": 27867,
+ "ологи": 27868,
+ "▁rb": 27869,
+ "▁incidents": 27870,
+ "▁duck": 27871,
+ "▁PROVIDED": 27872,
+ "Sources": 27873,
+ "▁Chelsea": 27874,
+ "▁tek": 27875,
+ "▁налази": 27876,
+ "▁pilots": 27877,
+ "тки": 27878,
+ "▁traded": 27879,
+ "▁Beijing": 27880,
+ "▁Gregory": 27881,
+ "scalar": 27882,
+ "▁inclined": 27883,
+ "▁Kamp": 27884,
+ "▁Marian": 27885,
+ "▁fierce": 27886,
+ "▁theft": 27887,
+ "ющих": 27888,
+ "▁Into": 27889,
+ "constraint": 27890,
+ "parentNode": 27891,
+ "idental": 27892,
+ "▁gouvernement": 27893,
+ "▁SND": 27894,
+ "▁Ruby": 27895,
+ "▁monaster": 27896,
+ "Records": 27897,
+ "▁Kab": 27898,
+ "▁Universe": 27899,
+ "▁approximate": 27900,
+ "Water": 27901,
+ "▁Physical": 27902,
+ "appers": 27903,
+ "oubtedly": 27904,
+ "ложен": 27905,
+ "▁towel": 27906,
+ "▁siblings": 27907,
+ "eph": 27908,
+ "icios": 27909,
+ "рами": 27910,
+ "▁outrage": 27911,
+ "▁també": 27912,
+ "SRC": 27913,
+ "телем": 27914,
+ "Vi": 27915,
+ ".');": 27916,
+ "LM": 27917,
+ "▁mitt": 27918,
+ "▁weed": 27919,
+ "▁crops": 27920,
+ "iman": 27921,
+ "Claim": 27922,
+ "insula": 27923,
+ "▁(“": 27924,
+ "▁Changes": 27925,
+ "▁invånare": 27926,
+ "again": 27927,
+ "▁cnt": 27928,
+ "▁Gaz": 27929,
+ "▁austral": 27930,
+ "overlay": 27931,
+ "▁Mechan": 27932,
+ "▁slammed": 27933,
+ "▁trailing": 27934,
+ "▁Biography": 27935,
+ "▁appealing": 27936,
+ "IVER": 27937,
+ "▁Ave": 27938,
+ "▁Plot": 27939,
+ "voj": 27940,
+ "▁sung": 27941,
+ "▁unos": 27942,
+ "Effects": 27943,
+ "vv": 27944,
+ "cook": 27945,
+ "Buttons": 27946,
+ "▁transm": 27947,
+ "ierto": 27948,
+ "CONTEXT": 27949,
+ "▁dignity": 27950,
+ "aired": 27951,
+ "javax": 27952,
+ "▁Alberto": 27953,
+ "▁Recently": 27954,
+ "▁facial": 27955,
+ "mathop": 27956,
+ "ało": 27957,
+ "вид": 27958,
+ "cott": 27959,
+ "Variables": 27960,
+ "▁Ran": 27961,
+ "▁bunk": 27962,
+ "amiliar": 27963,
+ "CAST": 27964,
+ "▁frü": 27965,
+ "VED": 27966,
+ "▁NOTICE": 27967,
+ "▁turno": 27968,
+ "validator": 27969,
+ "▁Portuguese": 27970,
+ "▁questioning": 27971,
+ "}})": 27972,
+ "▁lear": 27973,
+ "Xamarin": 27974,
+ "▁disadv": 27975,
+ "encoded": 27976,
+ "▁Kot": 27977,
+ "rated": 27978,
+ "▁Theory": 27979,
+ "cius": 27980,
+ "▁Darwin": 27981,
+ "ђе": 27982,
+ "▁décl": 27983,
+ "▁область": 27984,
+ "рович": 27985,
+ "▁mobility": 27986,
+ "VF": 27987,
+ "▁хи": 27988,
+ "until": 27989,
+ "▁barriers": 27990,
+ "gif": 27991,
+ "▁Roh": 27992,
+ "▁aging": 27993,
+ "▁Widget": 27994,
+ "olk": 27995,
+ "▁farms": 27996,
+ "Checker": 27997,
+ "Introduction": 27998,
+ "смо": 27999,
+ "▁Russians": 28000,
+ "naments": 28001,
+ "▁Insert": 28002,
+ "▁Whenever": 28003,
+ "erset": 28004,
+ "itori": 28005,
+ "▁Dort": 28006,
+ "▁costume": 28007,
+ "▁mathematical": 28008,
+ "▁Bast": 28009,
+ "▁nominated": 28010,
+ "▁restoration": 28011,
+ "posal": 28012,
+ "▁unfortunate": 28013,
+ "Ps": 28014,
+ "LIN": 28015,
+ "▁intact": 28016,
+ "▁provoc": 28017,
+ "▁située": 28018,
+ "▁ноября": 28019,
+ "ermo": 28020,
+ "▁fisher": 28021,
+ "гля": 28022,
+ "▁conting": 28023,
+ "▁Doug": 28024,
+ "\"?": 28025,
+ "▁Eva": 28026,
+ "▁tops": 28027,
+ "▁Remote": 28028,
+ "▁artwork": 28029,
+ "▁artillery": 28030,
+ "quick": 28031,
+ "▁Arabia": 28032,
+ "▁SDValue": 28033,
+ "▁Dakota": 28034,
+ "iated": 28035,
+ "▁Optim": 28036,
+ "buttons": 28037,
+ "▁cottage": 28038,
+ "▁wherein": 28039,
+ "▁tutorial": 28040,
+ "▁Scre": 28041,
+ "▁sweep": 28042,
+ "▁Coffee": 28043,
+ "})}": 28044,
+ "▁музы": 28045,
+ "hostname": 28046,
+ "▁Temp": 28047,
+ "▁Fut": 28048,
+ "respect": 28049,
+ "ocz": 28050,
+ "▁predomin": 28051,
+ "Indicator": 28052,
+ "encial": 28053,
+ "UMENT": 28054,
+ "▁SHALL": 28055,
+ "▁commanded": 28056,
+ "▁withdrawal": 28057,
+ "iour": 28058,
+ "REGION": 28059,
+ "sprintf": 28060,
+ "▁вме": 28061,
+ "▁Payment": 28062,
+ "▁Anim": 28063,
+ "publish": 28064,
+ "▁seeks": 28065,
+ "ouw": 28066,
+ "▁GM": 28067,
+ "rugu": 28068,
+ "ustain": 28069,
+ "▁))": 28070,
+ "▁consulting": 28071,
+ "▁Dialog": 28072,
+ "▁Lars": 28073,
+ "▁critique": 28074,
+ "▁circulation": 28075,
+ "▁landsc": 28076,
+ "managed": 28077,
+ "▁Craft": 28078,
+ "▁herman": 28079,
+ "afi": 28080,
+ "amy": 28081,
+ "▁discour": 28082,
+ "<>(": 28083,
+ "▁Steph": 28084,
+ "▁tolerance": 28085,
+ "typename": 28086,
+ "ventions": 28087,
+ "ział": 28088,
+ "стов": 28089,
+ "▁sticking": 28090,
+ "ASC": 28091,
+ "ISO": 28092,
+ "▁Spencer": 28093,
+ "▁Didn": 28094,
+ "gomery": 28095,
+ "imiter": 28096,
+ "dru": 28097,
+ "Clause": 28098,
+ "▁slides": 28099,
+ "###": 28100,
+ "▁Sugar": 28101,
+ "HY": 28102,
+ "▁эти": 28103,
+ "▁Edwards": 28104,
+ "▁cents": 28105,
+ "oya": 28106,
+ "serts": 28107,
+ "▁Hass": 28108,
+ "▁ingen": 28109,
+ "стри": 28110,
+ "▁saddle": 28111,
+ "solid": 28112,
+ "▁champions": 28113,
+ "-)": 28114,
+ "▁Slov": 28115,
+ "▁shiny": 28116,
+ "▁*)&": 28117,
+ "▁Define": 28118,
+ "če": 28119,
+ "▁scrut": 28120,
+ "onden": 28121,
+ "'\",": 28122,
+ "uffs": 28123,
+ "▁olymp": 28124,
+ "idential": 28125,
+ "wand": 28126,
+ "▁annually": 28127,
+ "▁Arkansas": 28128,
+ "▁saint": 28129,
+ "▁gleich": 28130,
+ "▁perfection": 28131,
+ ")>": 28132,
+ "▁shorts": 28133,
+ "▁justified": 28134,
+ "peated": 28135,
+ "packages": 28136,
+ "driven": 28137,
+ "▁Liberty": 28138,
+ "▁stripped": 28139,
+ "шение": 28140,
+ "▁fünf": 28141,
+ "▁ecosystem": 28142,
+ "ixa": 28143,
+ "▁Fresh": 28144,
+ "vart": 28145,
+ "▁treats": 28146,
+ "▁stance": 28147,
+ "чёт": 28148,
+ "▁pity": 28149,
+ "adém": 28150,
+ "▁окон": 28151,
+ "▁Chand": 28152,
+ "rab": 28153,
+ "вший": 28154,
+ "inski": 28155,
+ "▁continually": 28156,
+ "▁Daddy": 28157,
+ "▁nightmare": 28158,
+ "icional": 28159,
+ "▁efect": 28160,
+ "ueblo": 28161,
+ "▁lanç": 28162,
+ "▁Collections": 28163,
+ "due": 28164,
+ "ampton": 28165,
+ "▁memcpy": 28166,
+ "▁**(": 28167,
+ "issent": 28168,
+ "▁Insp": 28169,
+ "▁Glasgow": 28170,
+ "▁furono": 28171,
+ "▁kindness": 28172,
+ "Bi": 28173,
+ "▁competed": 28174,
+ "▁oak": 28175,
+ "Large": 28176,
+ "▁disgu": 28177,
+ "▁kings": 28178,
+ "тами": 28179,
+ "▁stuffed": 28180,
+ "▁hilar": 28181,
+ "published": 28182,
+ "▁stressed": 28183,
+ "▁Peak": 28184,
+ "▁loader": 28185,
+ "Keyboard": 28186,
+ "▁reconstruction": 28187,
+ "▁vod": 28188,
+ "▁dun": 28189,
+ "▁understands": 28190,
+ "tenant": 28191,
+ "▁chaque": 28192,
+ "▁prejud": 28193,
+ "utat": 28194,
+ "▁uso": 28195,
+ "▁Heavy": 28196,
+ "▁cuatro": 28197,
+ "▁sidewalk": 28198,
+ "▁Bug": 28199,
+ "▁månaden": 28200,
+ "geo": 28201,
+ "▁united": 28202,
+ "▁Files": 28203,
+ "▁Аль": 28204,
+ "▁rugby": 28205,
+ "▁financing": 28206,
+ "▁comply": 28207,
+ "": 28208,
+ "▁rushing": 28209,
+ "▁fen": 28210,
+ "mong": 28211,
+ "▁spé": 28212,
+ "▁presenting": 28213,
+ "INCLUDING": 28214,
+ "ěl": 28215,
+ "zeichnung": 28216,
+ "Backup": 28217,
+ "▁petit": 28218,
+ "▁allerg": 28219,
+ "нут": 28220,
+ "▁worrying": 28221,
+ "▁mamm": 28222,
+ "▁operand": 28223,
+ ":%.*]]": 28224,
+ "▁realise": 28225,
+ "Commands": 28226,
+ "▁Bew": 28227,
+ "▁assumes": 28228,
+ "▁Covid": 28229,
+ "▁quand": 28230,
+ "tyard": 28231,
+ "▁Mono": 28232,
+ "linked": 28233,
+ "MARK": 28234,
+ "Esp": 28235,
+ "▁blessing": 28236,
+ "▁eyebrows": 28237,
+ "▁NV": 28238,
+ "▁стру": 28239,
+ "▁modeling": 28240,
+ "▁greeted": 28241,
+ "Workspace": 28242,
+ "▁pedest": 28243,
+ "▁неза": 28244,
+ "lemagne": 28245,
+ "Statistics": 28246,
+ "▁aument": 28247,
+ "▁speeds": 28248,
+ "▁syndrome": 28249,
+ "CONNECT": 28250,
+ "zahl": 28251,
+ "verso": 28252,
+ "ército": 28253,
+ "▁astronom": 28254,
+ "▁aprile": 28255,
+ "žen": 28256,
+ "веро": 28257,
+ "draft": 28258,
+ "▁gioc": 28259,
+ "▁comport": 28260,
+ "▁variance": 28261,
+ "▁realizing": 28262,
+ "EDIT": 28263,
+ "олові": 28264,
+ "▁estar": 28265,
+ "▁sost": 28266,
+ "NORMAL": 28267,
+ "▁ó": 28268,
+ "▁Andr": 28269,
+ "ATTRIB": 28270,
+ "▁rede": 28271,
+ "▁toes": 28272,
+ "▁advances": 28273,
+ "▁Against": 28274,
+ "TOM": 28275,
+ "rss": 28276,
+ "MMMM": 28277,
+ "▁newest": 28278,
+ "▁VER": 28279,
+ "▁phrases": 28280,
+ "anter": 28281,
+ "Launch": 28282,
+ "▁chr": 28283,
+ "▁manufactured": 28284,
+ "$),": 28285,
+ "rollment": 28286,
+ "eston": 28287,
+ "▁peint": 28288,
+ "”)": 28289,
+ "endet": 28290,
+ "▁Hair": 28291,
+ "ivalent": 28292,
+ "▁upright": 28293,
+ "gren": 28294,
+ "anked": 28295,
+ "wright": 28296,
+ "▁mast": 28297,
+ "▁onChange": 28298,
+ "▁debris": 28299,
+ "▁grap": 28300,
+ "etry": 28301,
+ "▁(__": 28302,
+ "▁Commerce": 28303,
+ "BOX": 28304,
+ "Tax": 28305,
+ "▁отри": 28306,
+ "▁prevention": 28307,
+ "▁Feel": 28308,
+ "▁exotic": 28309,
+ "▁Bark": 28310,
+ "▁Steam": 28311,
+ "fon": 28312,
+ "olin": 28313,
+ "▁eliminated": 28314,
+ "▁bc": 28315,
+ "▁Cycl": 28316,
+ "▁$(\"#": 28317,
+ "▁Parl": 28318,
+ "manuel": 28319,
+ "ospher": 28320,
+ "WF": 28321,
+ "Analy": 28322,
+ "▁navig": 28323,
+ "▁renown": 28324,
+ "Rx": 28325,
+ "▁Walt": 28326,
+ "uffed": 28327,
+ "▁foster": 28328,
+ "$:": 28329,
+ "shore": 28330,
+ "Connector": 28331,
+ "фика": 28332,
+ "▁realization": 28333,
+ "Li": 28334,
+ "ctxt": 28335,
+ "ahoo": 28336,
+ "▁miracle": 28337,
+ "▁ET": 28338,
+ "▁GPS": 28339,
+ "▁Observable": 28340,
+ "▁hf": 28341,
+ "▁magnificent": 28342,
+ "него": 28343,
+ "BIN": 28344,
+ "▁Dorf": 28345,
+ "ieck": 28346,
+ "vee": 28347,
+ "▁Craw": 28348,
+ "/#": 28349,
+ "▁pci": 28350,
+ "ippet": 28351,
+ "▁Hillary": 28352,
+ "▁gir": 28353,
+ "▁rand": 28354,
+ "▁laying": 28355,
+ "▁Different": 28356,
+ "boys": 28357,
+ "virt": 28358,
+ "▁encryption": 28359,
+ "ász": 28360,
+ "пор": 28361,
+ "▁smelled": 28362,
+ "▁suscept": 28363,
+ "cluded": 28364,
+ "▁Carn": 28365,
+ "igten": 28366,
+ "▁Chuck": 28367,
+ "▁Provinc": 28368,
+ "▁perí": 28369,
+ "▁Marshal": 28370,
+ "мож": 28371,
+ "gfx": 28372,
+ "oshi": 28373,
+ "▁WHE": 28374,
+ "▁relaxation": 28375,
+ ",.": 28376,
+ "were": 28377,
+ "▁varieties": 28378,
+ "▁Won": 28379,
+ "▁gaps": 28380,
+ "▁stole": 28381,
+ "igua": 28382,
+ "ющие": 28383,
+ "▁Hampshire": 28384,
+ "phrase": 28385,
+ "▁película": 28386,
+ "Processing": 28387,
+ "▁initialization": 28388,
+ "oustic": 28389,
+ "▁Josef": 28390,
+ "icating": 28391,
+ "▁goodness": 28392,
+ "TES": 28393,
+ "▁cope": 28394,
+ "▁ignorance": 28395,
+ "▁Brist": 28396,
+ "▁paras": 28397,
+ "▁accidentally": 28398,
+ "▁tand": 28399,
+ "ittest": 28400,
+ "▁ули": 28401,
+ "▁shipped": 28402,
+ "▁ост": 28403,
+ "elseif": 28404,
+ "▁usize": 28405,
+ "horizontal": 28406,
+ "▁Carr": 28407,
+ "▁precip": 28408,
+ "roz": 28409,
+ "pathetic": 28410,
+ "rived": 28411,
+ "rok": 28412,
+ "▁digging": 28413,
+ "мом": 28414,
+ "▁Mull": 28415,
+ "▁XIII": 28416,
+ "▁peas": 28417,
+ "▁foul": 28418,
+ "▁travels": 28419,
+ "▁Ng": 28420,
+ "▁составе": 28421,
+ "Mont": 28422,
+ "arde": 28423,
+ "▁Stefan": 28424,
+ "^^^^": 28425,
+ "▁Kiss": 28426,
+ "▁Ek": 28427,
+ "▁oktober": 28428,
+ "▁memorable": 28429,
+ "')).": 28430,
+ "▁Vision": 28431,
+ "▁Nina": 28432,
+ "▁Solar": 28433,
+ "▁highlighted": 28434,
+ "▁memo": 28435,
+ "meisterschaft": 28436,
+ "sidebar": 28437,
+ "SEE": 28438,
+ "▁Nevada": 28439,
+ "Da": 28440,
+ "▁drawer": 28441,
+ "astically": 28442,
+ "elde": 28443,
+ "scribed": 28444,
+ "▁priests": 28445,
+ "▁hommes": 28446,
+ "▁instructor": 28447,
+ "клад": 28448,
+ "▁spett": 28449,
+ "\\-": 28450,
+ "▁мира": 28451,
+ "▁Looks": 28452,
+ "▁sleeve": 28453,
+ "▁strongest": 28454,
+ "▁tête": 28455,
+ "▁Nicole": 28456,
+ "imper": 28457,
+ "нача": 28458,
+ "ipper": 28459,
+ "▁inwon": 28460,
+ "ilers": 28461,
+ "▁Deputy": 28462,
+ "oge": 28463,
+ "▁depressed": 28464,
+ "▁arte": 28465,
+ "▁combining": 28466,
+ "LAST": 28467,
+ "inted": 28468,
+ "▁Average": 28469,
+ "▁pollution": 28470,
+ "▁Phillips": 28471,
+ "▁WM": 28472,
+ "}}}\\": 28473,
+ "Added": 28474,
+ "▁peripher": 28475,
+ "Creation": 28476,
+ "▁italien": 28477,
+ "▁Choice": 28478,
+ "▁EXPRESS": 28479,
+ "▁Struct": 28480,
+ "ysz": 28481,
+ "Resize": 28482,
+ "ARGS": 28483,
+ "▁repo": 28484,
+ "▁чтобы": 28485,
+ "▁pref": 28486,
+ "▁earthqu": 28487,
+ "▁Мекси": 28488,
+ "▁Finale": 28489,
+ "▁hecho": 28490,
+ "requests": 28491,
+ "Cut": 28492,
+ "▁deserved": 28493,
+ "гово": 28494,
+ "▁Recent": 28495,
+ "▁дивизи": 28496,
+ "▁supportive": 28497,
+ "прави": 28498,
+ "▁irrelevant": 28499,
+ "'\r": 28500,
+ "▁ctrl": 28501,
+ "▁Deal": 28502,
+ "izada": 28503,
+ "uo": 28504,
+ "▁nort": 28505,
+ "geometry": 28506,
+ "▁Individual": 28507,
+ "ereg": 28508,
+ "▁приня": 28509,
+ "cref": 28510,
+ "══": 28511,
+ "▁comerc": 28512,
+ "=_": 28513,
+ "bund": 28514,
+ "тах": 28515,
+ "ilen": 28516,
+ "чита": 28517,
+ "▁corporation": 28518,
+ "esz": 28519,
+ "▁==>": 28520,
+ "ablish": 28521,
+ "Apr": 28522,
+ "▁ripped": 28523,
+ "Vars": 28524,
+ "stret": 28525,
+ "▁Francesco": 28526,
+ "NaN": 28527,
+ "▁anytime": 28528,
+ "▁automated": 28529,
+ "ostream": 28530,
+ "▁drawings": 28531,
+ "▁enhancement": 28532,
+ "okrat": 28533,
+ "▁Issue": 28534,
+ "вра": 28535,
+ "Currency": 28536,
+ "▁wyn": 28537,
+ "izarre": 28538,
+ "ético": 28539,
+ "multiple": 28540,
+ "▁Rate": 28541,
+ "▁Ich": 28542,
+ "▁Auss": 28543,
+ "▁Former": 28544,
+ "Curve": 28545,
+ "▁marvel": 28546,
+ "attro": 28547,
+ "▁сп": 28548,
+ "BOOL": 28549,
+ "сия": 28550,
+ "gold": 28551,
+ "▁Nintendo": 28552,
+ "▁Salvador": 28553,
+ "▁Solution": 28554,
+ "ADC": 28555,
+ "бора": 28556,
+ "▁Bennett": 28557,
+ "▁FR": 28558,
+ "▁pueden": 28559,
+ "patient": 28560,
+ "▁PG": 28561,
+ "▁Jin": 28562,
+ "▁crashed": 28563,
+ "▁denen": 28564,
+ "▁Sample": 28565,
+ "▁Quebec": 28566,
+ "itories": 28567,
+ "▁blinked": 28568,
+ "▁lion": 28569,
+ "▁voce": 28570,
+ "▁Impact": 28571,
+ "▁Mau": 28572,
+ "▁Nie": 28573,
+ "▁lob": 28574,
+ "▁две": 28575,
+ "orneys": 28576,
+ "▁coastal": 28577,
+ "▁sensors": 28578,
+ "▁XII": 28579,
+ "▁illusion": 28580,
+ "oji": 28581,
+ "▁INC": 28582,
+ "▁Duncan": 28583,
+ "yk": 28584,
+ "▁affecting": 28585,
+ "pul": 28586,
+ "▁Napoleon": 28587,
+ "▁акаде": 28588,
+ "▁compt": 28589,
+ "▁profitable": 28590,
+ "loe": 28591,
+ "▁deuxième": 28592,
+ "▁WC": 28593,
+ "▁viable": 28594,
+ "▁Drug": 28595,
+ "TextBox": 28596,
+ "▁luminos": 28597,
+ "auté": 28598,
+ "yc": 28599,
+ "ště": 28600,
+ "▁affiliates": 28601,
+ "ilda": 28602,
+ "conduct": 28603,
+ "▁ebenfalls": 28604,
+ "▁AMD": 28605,
+ "▁Monitor": 28606,
+ "▁Companies": 28607,
+ "▁corrected": 28608,
+ "äck": 28609,
+ "SYSTEM": 28610,
+ "otherapy": 28611,
+ "▁перед": 28612,
+ "▁blues": 28613,
+ "atisf": 28614,
+ "although": 28615,
+ "rost": 28616,
+ "SCAN": 28617,
+ "▁RAM": 28618,
+ "ціональ": 28619,
+ "▁vendors": 28620,
+ "▁customs": 28621,
+ "▁activate": 28622,
+ "▁blogs": 28623,
+ "▁brace": 28624,
+ "▁strat": 28625,
+ "anje": 28626,
+ "щё": 28627,
+ "▁tide": 28628,
+ "▁Brigade": 28629,
+ "getOperand": 28630,
+ "▁aliment": 28631,
+ "▁achievements": 28632,
+ "▁suspicion": 28633,
+ "▁touchdown": 28634,
+ "broad": 28635,
+ "iore": 28636,
+ "Comparison": 28637,
+ "▁mum": 28638,
+ "English": 28639,
+ "▁Picture": 28640,
+ "▁Mouse": 28641,
+ "amd": 28642,
+ "▁[`": 28643,
+ "▁denomin": 28644,
+ "▁Aleks": 28645,
+ "▁prevents": 28646,
+ "ób": 28647,
+ "fed": 28648,
+ "▁Pray": 28649,
+ "▁shine": 28650,
+ "▁clutch": 28651,
+ "mux": 28652,
+ "Appro": 28653,
+ "▁notably": 28654,
+ "chio": 28655,
+ "nage": 28656,
+ "HAS": 28657,
+ "▁')": 28658,
+ "▁Miche": 28659,
+ "tg": 28660,
+ "::~": 28661,
+ "▁amely": 28662,
+ "▁rodz": 28663,
+ "zs": 28664,
+ "trait": 28665,
+ "▁klass": 28666,
+ "fö": 28667,
+ "▁destac": 28668,
+ "▁Clara": 28669,
+ "frequency": 28670,
+ "▁Git": 28671,
+ "▁поль": 28672,
+ "▁frequencies": 28673,
+ "▁febrero": 28674,
+ "▁stumbled": 28675,
+ "кою": 28676,
+ "▁Names": 28677,
+ "▁Flight": 28678,
+ "▁prey": 28679,
+ "▁medio": 28680,
+ "▁VAR": 28681,
+ "▁Float": 28682,
+ "▁Ernest": 28683,
+ "▁Marcatori": 28684,
+ "oport": 28685,
+ "▁cancellation": 28686,
+ "▁Bryan": 28687,
+ "————": 28688,
+ "Luc": 28689,
+ "▁libre": 28690,
+ "▁título": 28691,
+ "*>": 28692,
+ "▁Sandy": 28693,
+ "▁Marina": 28694,
+ "Been": 28695,
+ "▁wal": 28696,
+ "▁Kultur": 28697,
+ "▁explode": 28698,
+ "▁limiting": 28699,
+ "▁presumably": 28700,
+ "▁pb": 28701,
+ "▁Merc": 28702,
+ "▁реки": 28703,
+ "learning": 28704,
+ "Catalog": 28705,
+ "▁Census": 28706,
+ "lte": 28707,
+ "▁NET": 28708,
+ "raising": 28709,
+ "ське": 28710,
+ "staff": 28711,
+ "▁Quinn": 28712,
+ "▁memorial": 28713,
+ "пня": 28714,
+ "▁cuenta": 28715,
+ "▁XI": 28716,
+ "lbl": 28717,
+ "▁varies": 28718,
+ "▁fluctuations": 28719,
+ "▁долж": 28720,
+ "▁особи": 28721,
+ "▁warehouse": 28722,
+ "However": 28723,
+ "▁corrections": 28724,
+ "dhd": 28725,
+ "▁fals": 28726,
+ "▁controversy": 28727,
+ "▁curse": 28728,
+ "▁télé": 28729,
+ "řed": 28730,
+ "▁AU": 28731,
+ "▁тор": 28732,
+ "▁crít": 28733,
+ "idan": 28734,
+ "iliary": 28735,
+ "▁Panel": 28736,
+ "cule": 28737,
+ "▁Poor": 28738,
+ "▁BA": 28739,
+ "▁ignorant": 28740,
+ "èmes": 28741,
+ "▁aesthetic": 28742,
+ "Linked": 28743,
+ "getInt": 28744,
+ "Unicode": 28745,
+ "[@": 28746,
+ "▁Zent": 28747,
+ "Manifest": 28748,
+ "▁vars": 28749,
+ "PB": 28750,
+ "▁ву": 28751,
+ "▁Describe": 28752,
+ "▁Anything": 28753,
+ "oirs": 28754,
+ "▁socks": 28755,
+ "▁imped": 28756,
+ "▁neue": 28757,
+ "▁dispers": 28758,
+ "Collect": 28759,
+ "filer": 28760,
+ "▁Frau": 28761,
+ "▁Hockey": 28762,
+ "▁teens": 28763,
+ "▁Roberto": 28764,
+ "lauf": 28765,
+ "вать": 28766,
+ "▁ско": 28767,
+ "isArray": 28768,
+ "▁teenager": 28769,
+ "Built": 28770,
+ "▁loudly": 28771,
+ "Capacity": 28772,
+ "▁adventures": 28773,
+ "▁Molly": 28774,
+ "recogn": 28775,
+ "bars": 28776,
+ "▁Lor": 28777,
+ "▁può": 28778,
+ "▁mong": 28779,
+ "inement": 28780,
+ "Assignment": 28781,
+ "▁diz": 28782,
+ "lessness": 28783,
+ "▁Halloween": 28784,
+ "▁bitmap": 28785,
+ "Rom": 28786,
+ "нар": 28787,
+ "▁rebel": 28788,
+ "▁radial": 28789,
+ "measure": 28790,
+ "nit": 28791,
+ "▁Assume": 28792,
+ "▁assignments": 28793,
+ "▁Isn": 28794,
+ "▁altre": 28795,
+ "ßer": 28796,
+ "наль": 28797,
+ "▁flies": 28798,
+ "▁droit": 28799,
+ "▁thickness": 28800,
+ "▁enjo": 28801,
+ "▁dwell": 28802,
+ "▁homosexual": 28803,
+ "▁eval": 28804,
+ "$_{": 28805,
+ "asia": 28806,
+ "▁philos": 28807,
+ "getCurrent": 28808,
+ "▁veterans": 28809,
+ "▁Berkeley": 28810,
+ "▁wildlife": 28811,
+ "Cop": 28812,
+ "vern": 28813,
+ "▁Ú": 28814,
+ "tos": 28815,
+ "▁Led": 28816,
+ "▁keywords": 28817,
+ "▁medications": 28818,
+ "neum": 28819,
+ "▁jamais": 28820,
+ "▁Buc": 28821,
+ "▁PD": 28822,
+ "▁Statement": 28823,
+ "▁PI": 28824,
+ "▁Jackie": 28825,
+ "▁ordin": 28826,
+ "▁kör": 28827,
+ "enze": 28828,
+ "▁utilized": 28829,
+ "áct": 28830,
+ "azed": 28831,
+ "▁severely": 28832,
+ "▁även": 28833,
+ "▁libro": 28834,
+ "▁Eu": 28835,
+ "äst": 28836,
+ "PART": 28837,
+ "▁Butler": 28838,
+ "▁puzzle": 28839,
+ "Fall": 28840,
+ "Country": 28841,
+ "pfn": 28842,
+ "▁україн": 28843,
+ "▁Orchestra": 28844,
+ "▁alto": 28845,
+ "▁ancora": 28846,
+ "▁decomposition": 28847,
+ "▁م": 28848,
+ "▁appetite": 28849,
+ "adu": 28850,
+ "▁THAT": 28851,
+ "▁comenz": 28852,
+ "mina": 28853,
+ "▁initiated": 28854,
+ "▁Tat": 28855,
+ "▁sometime": 28856,
+ "rek": 28857,
+ "bread": 28858,
+ "▁Statistics": 28859,
+ "▁Cob": 28860,
+ "Follow": 28861,
+ "▁geometric": 28862,
+ "шла": 28863,
+ "▁proceedings": 28864,
+ "Dlg": 28865,
+ "seven": 28866,
+ "▁[-": 28867,
+ "▁Buffalo": 28868,
+ "▁blacks": 28869,
+ "▁sov": 28870,
+ "▁custody": 28871,
+ "▁ras": 28872,
+ "▁tattoo": 28873,
+ "öffentlicht": 28874,
+ "Blo": 28875,
+ "Austral": 28876,
+ "▁recuper": 28877,
+ "лев": 28878,
+ "▁bem": 28879,
+ "▁thou": 28880,
+ "oriented": 28881,
+ "vir": 28882,
+ "▁colony": 28883,
+ "▁Stanford": 28884,
+ "Absolute": 28885,
+ "adrat": 28886,
+ "▁Situ": 28887,
+ "▁souvent": 28888,
+ "EXEC": 28889,
+ "▁mű": 28890,
+ "▁apartments": 28891,
+ "▁случа": 28892,
+ "▁ano": 28893,
+ "WINDO": 28894,
+ "acci": 28895,
+ "▁Lau": 28896,
+ "court": 28897,
+ "▁manifold": 28898,
+ "▁coalition": 28899,
+ "▁XIV": 28900,
+ "Attrib": 28901,
+ "ascade": 28902,
+ "▁wheat": 28903,
+ "▁strengths": 28904,
+ "FREE": 28905,
+ "EMPTY": 28906,
+ "▁hey": 28907,
+ "ascular": 28908,
+ "▁plasma": 28909,
+ "▁bob": 28910,
+ "Separator": 28911,
+ "=\"${": 28912,
+ "▁Zag": 28913,
+ "▁projet": 28914,
+ "▁smoothly": 28915,
+ "SEQU": 28916,
+ "analy": 28917,
+ "attachment": 28918,
+ "▁ES": 28919,
+ "▁popped": 28920,
+ "ős": 28921,
+ "tom": 28922,
+ "▁són": 28923,
+ "▁rott": 28924,
+ "Utilities": 28925,
+ "hadoop": 28926,
+ "▁sotto": 28927,
+ "autor": 28928,
+ "▁Georges": 28929,
+ "▁který": 28930,
+ "▁gruppo": 28931,
+ "▁когда": 28932,
+ "▁меда": 28933,
+ "▁instrumental": 28934,
+ "▁Writer": 28935,
+ "▁setTimeout": 28936,
+ "ikk": 28937,
+ "▁Dopo": 28938,
+ "]);\r": 28939,
+ "▁practicing": 28940,
+ "▁Ronald": 28941,
+ "▁уби": 28942,
+ "▁agrees": 28943,
+ "▁denoted": 28944,
+ "ismiss": 28945,
+ "▁interviewed": 28946,
+ "templates": 28947,
+ "ři": 28948,
+ "administr": 28949,
+ "▁Butter": 28950,
+ "▁XVII": 28951,
+ "▁positioned": 28952,
+ "▁Fourth": 28953,
+ "▁overwhelmed": 28954,
+ "▁Regular": 28955,
+ "▁reprezent": 28956,
+ "кономи": 28957,
+ "▁expects": 28958,
+ "Indices": 28959,
+ "▁marijuana": 28960,
+ "▁zaj": 28961,
+ "▁Bren": 28962,
+ "▁begg": 28963,
+ "▁nahm": 28964,
+ "▁interrog": 28965,
+ "тие": 28966,
+ "▁Bun": 28967,
+ "▁серед": 28968,
+ "▁shelves": 28969,
+ "▁которых": 28970,
+ "▁Frauen": 28971,
+ "▁Sergeant": 28972,
+ "▁успе": 28973,
+ "matched": 28974,
+ "▁donne": 28975,
+ "▁touches": 28976,
+ "abort": 28977,
+ "▁vale": 28978,
+ "▁institutional": 28979,
+ "▁Mons": 28980,
+ "▁ambitious": 28981,
+ "▁nonetheless": 28982,
+ "jd": 28983,
+ "пей": 28984,
+ "▁backpack": 28985,
+ "dao": 28986,
+ "вия": 28987,
+ "▁surroundings": 28988,
+ "|_{": 28989,
+ "▁gegründ": 28990,
+ "disp": 28991,
+ "▁moisture": 28992,
+ "▁wyd": 28993,
+ "▁traders": 28994,
+ "▁Erst": 28995,
+ "▁Galaxy": 28996,
+ "▁воло": 28997,
+ "▁Peru": 28998,
+ "▁priorities": 28999,
+ "▁pronounced": 29000,
+ "▁CBS": 29001,
+ "▁Palm": 29002,
+ "▁expans": 29003,
+ "▁energet": 29004,
+ "▁Condition": 29005,
+ "▁Sver": 29006,
+ "nested": 29007,
+ "▁февраля": 29008,
+ "hero": 29009,
+ "▁коло": 29010,
+ "▁Films": 29011,
+ "Bon": 29012,
+ "éal": 29013,
+ "ployed": 29014,
+ "trained": 29015,
+ "▁első": 29016,
+ "▁lust": 29017,
+ "atinum": 29018,
+ "oyle": 29019,
+ "▁Jet": 29020,
+ "ждения": 29021,
+ "▁surveys": 29022,
+ "bee": 29023,
+ "workers": 29024,
+ "records": 29025,
+ "calendar": 29026,
+ "bbing": 29027,
+ "regation": 29028,
+ "dashboard": 29029,
+ "King": 29030,
+ "▁vista": 29031,
+ "▁depicted": 29032,
+ "▁occurring": 29033,
+ "▁офи": 29034,
+ "▁sandwich": 29035,
+ "rcu": 29036,
+ "kern": 29037,
+ "▁minut": 29038,
+ "▁смер": 29039,
+ "▁td": 29040,
+ "solete": 29041,
+ "Complex": 29042,
+ "▁tunn": 29043,
+ "▁scarc": 29044,
+ "stead": 29045,
+ "▁Fail": 29046,
+ "▁Rs": 29047,
+ "▁trails": 29048,
+ "kem": 29049,
+ "▁Romans": 29050,
+ "ativity": 29051,
+ "Previous": 29052,
+ "▁depress": 29053,
+ "▁resigned": 29054,
+ "getDefault": 29055,
+ "▁Tibet": 29056,
+ "▁Franco": 29057,
+ "\")));": 29058,
+ "▁injection": 29059,
+ "removed": 29060,
+ "▁praised": 29061,
+ "▁Asc": 29062,
+ "erase": 29063,
+ "▁commissioned": 29064,
+ "MAIL": 29065,
+ "▁Boh": 29066,
+ "Poly": 29067,
+ "▁cinq": 29068,
+ "▁Above": 29069,
+ "▁Joshua": 29070,
+ "ZERO": 29071,
+ "▁summit": 29072,
+ "▁Urs": 29073,
+ "▁curl": 29074,
+ "▁visa": 29075,
+ "▁resur": 29076,
+ "={'": 29077,
+ "feat": 29078,
+ "▁absorb": 29079,
+ "▁planets": 29080,
+ "▁princess": 29081,
+ "▁Jahrhunderts": 29082,
+ "xp": 29083,
+ "▁NBC": 29084,
+ "▁коми": 29085,
+ "▁FUN": 29086,
+ "▁neuen": 29087,
+ "▁déjà": 29088,
+ "▁Oz": 29089,
+ "bben": 29090,
+ "VIDEO": 29091,
+ "▁ejempl": 29092,
+ "▁considers": 29093,
+ "atri": 29094,
+ "▁arrog": 29095,
+ "ioso": 29096,
+ "▁hace": 29097,
+ "▁contacted": 29098,
+ "▁unple": 29099,
+ "▁sponsored": 29100,
+ "▁trainer": 29101,
+ "sbi": 29102,
+ "▁занима": 29103,
+ "Criterion": 29104,
+ "ното": 29105,
+ "scheme": 29106,
+ "ennial": 29107,
+ "perform": 29108,
+ "▁fixing": 29109,
+ "▁постро": 29110,
+ "arb": 29111,
+ "EXIT": 29112,
+ "▁café": 29113,
+ "ituted": 29114,
+ "riages": 29115,
+ "Tur": 29116,
+ "▁haber": 29117,
+ "elasticsearch": 29118,
+ "▁ал": 29119,
+ "rh": 29120,
+ "▁voll": 29121,
+ "CLU": 29122,
+ "Mil": 29123,
+ "▁membres": 29124,
+ "▁remarked": 29125,
+ "вана": 29126,
+ "=\"_": 29127,
+ "Less": 29128,
+ "(\"\");": 29129,
+ "▁Yale": 29130,
+ "berries": 29131,
+ "▁releasing": 29132,
+ "▁imports": 29133,
+ "idea": 29134,
+ "▁(+": 29135,
+ "▁arqu": 29136,
+ "ificación": 29137,
+ "▁пара": 29138,
+ "▁Rangers": 29139,
+ "Mic": 29140,
+ "▁nederbörd": 29141,
+ "▁imaginary": 29142,
+ "▁specialists": 29143,
+ "▁hoof": 29144,
+ "Modules": 29145,
+ "▁sadly": 29146,
+ "ceil": 29147,
+ "TabIndex": 29148,
+ "ationale": 29149,
+ "▁Partner": 29150,
+ "tbody": 29151,
+ "▁leverage": 29152,
+ "DN": 29153,
+ "▁Prec": 29154,
+ "▁Sé": 29155,
+ "▁Mam": 29156,
+ "▁afin": 29157,
+ "isValid": 29158,
+ "Pse": 29159,
+ "▁сторо": 29160,
+ "▁chopped": 29161,
+ "▁Minor": 29162,
+ "▁dabei": 29163,
+ "David": 29164,
+ "ussia": 29165,
+ "▁деревня": 29166,
+ "▁Identity": 29167,
+ "▁LGBT": 29168,
+ "ције": 29169,
+ "▁Orts": 29170,
+ "▁parti": 29171,
+ "▁Bachelor": 29172,
+ "uga": 29173,
+ "▁OPT": 29174,
+ "▁Seth": 29175,
+ "▁LIABLE": 29176,
+ "▁inaugur": 29177,
+ "▁Shanghai": 29178,
+ "▁relaxing": 29179,
+ "циона": 29180,
+ "\"%": 29181,
+ "▁obey": 29182,
+ "▁Airlines": 29183,
+ "Links": 29184,
+ "▁Celt": 29185,
+ "▁Admin": 29186,
+ "agation": 29187,
+ "▁worries": 29188,
+ "INTE": 29189,
+ "arith": 29190,
+ "Fatalf": 29191,
+ "]])": 29192,
+ "colm": 29193,
+ "▁archae": 29194,
+ "▁brushed": 29195,
+ "▁tät": 29196,
+ "▁structured": 29197,
+ "тии": 29198,
+ "▁homem": 29199,
+ "[:,": 29200,
+ "▁navy": 29201,
+ "getKey": 29202,
+ "powered": 29203,
+ "▁sucked": 29204,
+ "▁zomb": 29205,
+ "issant": 29206,
+ "▁Might": 29207,
+ "▁Pull": 29208,
+ "rir": 29209,
+ "▁пі": 29210,
+ "▁seas": 29211,
+ "▁Wrest": 29212,
+ "▁tense": 29213,
+ "▁atm": 29214,
+ "▁havet": 29215,
+ "▁pierws": 29216,
+ "▁tragic": 29217,
+ "▁Diff": 29218,
+ "▁confidential": 29219,
+ "successful": 29220,
+ "ęż": 29221,
+ "▁Chain": 29222,
+ "▁Kenya": 29223,
+ "Choice": 29224,
+ "ocur": 29225,
+ "aniu": 29226,
+ "▁consultant": 29227,
+ "▁Advis": 29228,
+ "Lif": 29229,
+ "▁Lors": 29230,
+ "avorite": 29231,
+ "▁utilizing": 29232,
+ "▁vintage": 29233,
+ "Matcher": 29234,
+ "▁membre": 29235,
+ "▁Expect": 29236,
+ "▁tracing": 29237,
+ "nog": 29238,
+ "▁dej": 29239,
+ "▁уче": 29240,
+ "▁loops": 29241,
+ "▁onclick": 29242,
+ "▁GPU": 29243,
+ "▁Albums": 29244,
+ "▁Archives": 29245,
+ "вата": 29246,
+ "▁stove": 29247,
+ "шли": 29248,
+ "ancies": 29249,
+ "▁gemeente": 29250,
+ "mob": 29251,
+ "PDF": 29252,
+ "eso": 29253,
+ "▁vég": 29254,
+ "Resolve": 29255,
+ "▁teaches": 29256,
+ "ложе": 29257,
+ "▁ство": 29258,
+ "▁Одна": 29259,
+ "▁fid": 29260,
+ "Something": 29261,
+ "▁nebo": 29262,
+ "▁Valentine": 29263,
+ "rowning": 29264,
+ "▁але": 29265,
+ "awi": 29266,
+ "ishi": 29267,
+ "▁SPI": 29268,
+ "▁spel": 29269,
+ "▁біль": 29270,
+ "▁participant": 29271,
+ "▁Ned": 29272,
+ "▁Gast": 29273,
+ "▁blond": 29274,
+ "▁saves": 29275,
+ "colored": 29276,
+ "▁ACTION": 29277,
+ "▁Politiker": 29278,
+ "}$)": 29279,
+ "▁Dum": 29280,
+ "dentry": 29281,
+ "Student": 29282,
+ "▁~=": 29283,
+ "loads": 29284,
+ "▁Foster": 29285,
+ "一个": 29286,
+ "▁PK": 29287,
+ "▁SB": 29288,
+ "▁Hern": 29289,
+ "▁Exhib": 29290,
+ "Listeners": 29291,
+ "Sun": 29292,
+ "plac": 29293,
+ "▁Bever": 29294,
+ "▁incluy": 29295,
+ "▁dc": 29296,
+ "argc": 29297,
+ "▁ged": 29298,
+ "спа": 29299,
+ "▁Formula": 29300,
+ "▁сем": 29301,
+ "▁empt": 29302,
+ "unregister": 29303,
+ "▁Queensland": 29304,
+ "ández": 29305,
+ "otive": 29306,
+ "▁alley": 29307,
+ "▁Democrat": 29308,
+ "▁travail": 29309,
+ "▁$,": 29310,
+ "RP": 29311,
+ "рое": 29312,
+ "personal": 29313,
+ "▁période": 29314,
+ "HOME": 29315,
+ "omes": 29316,
+ "▁recognised": 29317,
+ "heng": 29318,
+ "▁Jung": 29319,
+ "▁Roland": 29320,
+ "▁convicted": 29321,
+ "Locked": 29322,
+ "▁mari": 29323,
+ "▁Luxem": 29324,
+ "referto": 29325,
+ "Deleted": 29326,
+ "intent": 29327,
+ "▁Staats": 29328,
+ "▁області": 29329,
+ "ит": 29330,
+ "▁саве": 29331,
+ "▁Protocol": 29332,
+ "ając": 29333,
+ "chk": 29334,
+ "TypeInfo": 29335,
+ "▁pkt": 29336,
+ "▁scandal": 29337,
+ "▁individually": 29338,
+ "FMT": 29339,
+ "▁nj": 29340,
+ "abile": 29341,
+ "▁Rivers": 29342,
+ "PROPERTY": 29343,
+ "VB": 29344,
+ "wort": 29345,
+ "▁splitting": 29346,
+ "achten": 29347,
+ "▁ARISING": 29348,
+ "▁sip": 29349,
+ "▁fres": 29350,
+ "▁groom": 29351,
+ "Hol": 29352,
+ "▁canon": 29353,
+ "▁abruptly": 29354,
+ "▁afterward": 29355,
+ "▁Running": 29356,
+ "▁ji": 29357,
+ "▁%,": 29358,
+ "▁Palestinian": 29359,
+ "RW": 29360,
+ "pgfscope": 29361,
+ "▁countryside": 29362,
+ "▁fortunate": 29363,
+ "▁cél": 29364,
+ "▁Pointer": 29365,
+ "ensors": 29366,
+ "rating": 29367,
+ "▁buffers": 29368,
+ "▁remot": 29369,
+ "▁PropTypes": 29370,
+ "▁Nah": 29371,
+ "altern": 29372,
+ "▁easiest": 29373,
+ "▁invas": 29374,
+ "▁clk": 29375,
+ "copyright": 29376,
+ "▁blanc": 29377,
+ "SAMP": 29378,
+ "▁Cohen": 29379,
+ "▁Shell": 29380,
+ "▁destroying": 29381,
+ "▁Zel": 29382,
+ "dater": 29383,
+ "čen": 29384,
+ "▁filing": 29385,
+ "▁integrate": 29386,
+ "xit": 29387,
+ "▁RET": 29388,
+ "lene": 29389,
+ "calls": 29390,
+ "▁slaughter": 29391,
+ "initialized": 29392,
+ "unches": 29393,
+ "▁Trace": 29394,
+ "efficient": 29395,
+ "▁Woods": 29396,
+ "▁longitud": 29397,
+ "GN": 29398,
+ "▁Kont": 29399,
+ "▁chunks": 29400,
+ "ách": 29401,
+ "▁unemployment": 29402,
+ "acom": 29403,
+ "▁slowed": 29404,
+ "▁outlined": 29405,
+ "xffff": 29406,
+ "▁ikke": 29407,
+ "▁workspace": 29408,
+ "Mc": 29409,
+ "▁kicking": 29410,
+ "▁embedding": 29411,
+ "chnitt": 29412,
+ "erten": 29413,
+ "▁Interior": 29414,
+ "▁Songs": 29415,
+ "mmc": 29416,
+ "▁analyzed": 29417,
+ "▁Coupe": 29418,
+ "▁favorites": 29419,
+ "▁tt": 29420,
+ "▁той": 29421,
+ "Routing": 29422,
+ "▁Silva": 29423,
+ "▁anderem": 29424,
+ "▁honom": 29425,
+ "▁использова": 29426,
+ ".\"]": 29427,
+ "▁Wu": 29428,
+ "legt": 29429,
+ "▁spoon": 29430,
+ "▁jap": 29431,
+ "▁Extension": 29432,
+ "erne": 29433,
+ "▁vagy": 29434,
+ "▁села": 29435,
+ "▁функ": 29436,
+ "▁analytics": 29437,
+ "▁sug": 29438,
+ "▁Async": 29439,
+ "▁peaks": 29440,
+ "▁Gym": 29441,
+ "▁lawsuit": 29442,
+ "<>": 29443,
+ "ialis": 29444,
+ "etric": 29445,
+ "faced": 29446,
+ "▁disrupt": 29447,
+ "▁få": 29448,
+ "Inputs": 29449,
+ "`);": 29450,
+ "▁Mend": 29451,
+ "gon": 29452,
+ "▁\",\"": 29453,
+ "▁nerves": 29454,
+ "▁doubts": 29455,
+ "sap": 29456,
+ "▁sow": 29457,
+ ",\\,\\": 29458,
+ "▁BS": 29459,
+ "▁Glad": 29460,
+ "▁aster": 29461,
+ "œuvre": 29462,
+ "▁Bangl": 29463,
+ "▁iPad": 29464,
+ "useppe": 29465,
+ "▁conducting": 29466,
+ "▁({\\": 29467,
+ "▁Harbor": 29468,
+ "psz": 29469,
+ "▁FIFA": 29470,
+ "_**": 29471,
+ "emor": 29472,
+ "▁": 29473,
+ "e": 29474,
+ "t": 29475,
+ "a": 29476,
+ "o": 29477,
+ "i": 29478,
+ "n": 29479,
+ "r": 29480,
+ "s": 29481,
+ "l": 29482,
+ "d": 29483,
+ "h": 29484,
+ "c": 29485,
+ "u": 29486,
+ "m": 29487,
+ "p": 29488,
+ "g": 29489,
+ "f": 29490,
+ ".": 29491,
+ "y": 29492,
+ ",": 29493,
+ "b": 29494,
+ "w": 29495,
+ "v": 29496,
+ "k": 29497,
+ "_": 29498,
+ ")": 29499,
+ "(": 29500,
+ "-": 29501,
+ "0": 29502,
+ "S": 29503,
+ "*": 29504,
+ "I": 29505,
+ "T": 29506,
+ "\"": 29507,
+ "1": 29508,
+ "A": 29509,
+ "'": 29510,
+ "C": 29511,
+ "x": 29512,
+ ";": 29513,
+ "=": 29514,
+ ":": 29515,
+ "/": 29516,
+ "E": 29517,
+ "2": 29518,
+ "{": 29519,
+ "}": 29520,
+ "P": 29521,
+ "R": 29522,
+ "M": 29523,
+ "\\": 29524,
+ "D": 29525,
+ "L": 29526,
+ "N": 29527,
+ "B": 29528,
+ "о": 29529,
+ "O": 29530,
+ "а": 29531,
+ "z": 29532,
+ "F": 29533,
+ "|": 29534,
+ ">": 29535,
+ "j": 29536,
+ "H": 29537,
+ "3": 29538,
+ "#": 29539,
+ "и": 29540,
+ "е": 29541,
+ "9": 29542,
+ "q": 29543,
+ "$": 29544,
+ "G": 29545,
+ "н": 29546,
+ "U": 29547,
+ "W": 29548,
+ "4": 29549,
+ "5": 29550,
+ "8": 29551,
+ "6": 29552,
+ "р": 29553,
+ "т": 29554,
+ "7": 29555,
+ "с": 29556,
+ "<": 29557,
+ "V": 29558,
+ "в": 29559,
+ "[": 29560,
+ "]": 29561,
+ "л": 29562,
+ "к": 29563,
+ "K": 29564,
+ "é": 29565,
+ "J": 29566,
+ "д": 29567,
+ "&": 29568,
+ "\r": 29569,
+ "Y": 29570,
+ "м": 29571,
+ "?": 29572,
+ "у": 29573,
+ "+": 29574,
+ "п": 29575,
+ "!": 29576,
+ "’": 29577,
+ "г": 29578,
+ "я": 29579,
+ "з": 29580,
+ "і": 29581,
+ "X": 29582,
+ "^": 29583,
+ "–": 29584,
+ "б": 29585,
+ "@": 29586,
+ "й": 29587,
+ "á": 29588,
+ "—": 29589,
+ "ь": 29590,
+ "%": 29591,
+ "Q": 29592,
+ "ó": 29593,
+ "ч": 29594,
+ "í": 29595,
+ "Z": 29596,
+ "ы": 29597,
+ "ä": 29598,
+ "х": 29599,
+ "`": 29600,
+ "ц": 29601,
+ "ö": 29602,
+ "“": 29603,
+ "ж": 29604,
+ "ü": 29605,
+ "”": 29606,
+ "à": 29607,
+ "è": 29608,
+ "ш": 29609,
+ "ю": 29610,
+ "ł": 29611,
+ "С": 29612,
+ "~": 29613,
+ "ф": 29614,
+ "П": 29615,
+ "»": 29616,
+ "В": 29617,
+ "«": 29618,
+ "å": 29619,
+ "К": 29620,
+ "щ": 29621,
+ "·": 29622,
+ "ј": 29623,
+ "М": 29624,
+ "ç": 29625,
+ "А": 29626,
+ "Н": 29627,
+ "Р": 29628,
+ "Б": 29629,
+ "č": 29630,
+ "ú": 29631,
+ "ę": 29632,
+ "ã": 29633,
+ "ą": 29634,
+ "ă": 29635,
+ "Д": 29636,
+ "ї": 29637,
+ "ъ": 29638,
+ "ě": 29639,
+ "Г": 29640,
+ "š": 29641,
+ "О": 29642,
+ "Т": 29643,
+ "ê": 29644,
+ "ñ": 29645,
+ "…": 29646,
+ "ž": 29647,
+ "ß": 29648,
+ "ё": 29649,
+ "ż": 29650,
+ "ř": 29651,
+ "ś": 29652,
+ "Л": 29653,
+ "ő": 29654,
+ "„": 29655,
+ "э": 29656,
+ "ý": 29657,
+ "У": 29658,
+ "â": 29659,
+ "И": 29660,
+ "є": 29661,
+ "‘": 29662,
+ "î": 29663,
+ "З": 29664,
+ "Ф": 29665,
+ "ò": 29666,
+ "•": 29667,
+ "ć": 29668,
+ "É": 29669,
+ "°": 29670,
+ "ș": 29671,
+ "Х": 29672,
+ "ț": 29673,
+ "ô": 29674,
+ "Е": 29675,
+ "ń": 29676,
+ "Ч": 29677,
+ "Ш": 29678,
+ "ø": 29679,
+ "ù": 29680,
+ "ů": 29681,
+ "的": 29682,
+ "ا": 29683,
+ "æ": 29684,
+ "њ": 29685,
+ "љ": 29686,
+ "ë": 29687,
+ "ï": 29688,
+ "Э": 29689,
+ "£": 29690,
+ "−": 29691,
+ ",": 29692,
+ "õ": 29693,
+ "ћ": 29694,
+ "": 29695,
+ "Ц": 29696,
+ "І": 29697,
+ "ā": 29698,
+ "ű": 29699,
+ "†": 29700,
+ "ل": 29701,
+ "ō": 29702,
+ "": 29703,
+ "º": 29704,
+ "Я": 29705,
+ "′": 29706,
+ "Á": 29707,
+ "Ö": 29708,
+ "²": 29709,
+ "Ж": 29710,
+ "ì": 29711,
+ "。": 29712,
+ "数": 29713,
+ "×": 29714,
+ "ر": 29715,
+ "α": 29716,
+ "́": 29717,
+ "Ю": 29718,
+ "û": 29719,
+ "œ": 29720,
+ "ı": 29721,
+ "م": 29722,
+ "ن": 29723,
+ "ª": 29724,
+ "ź": 29725,
+ "ο": 29726,
+ "″": 29727,
+ "€": 29728,
+ "Ü": 29729,
+ "و": 29730,
+ "用": 29731,
+ "À": 29732,
+ "Č": 29733,
+ "Š": 29734,
+ "ت": 29735,
+ "د": 29736,
+ "一": 29737,
+ "¿": 29738,
+ "是": 29739,
+ "ي": 29740,
+ "ђ": 29741,
+ "®": 29742,
+ "ی": 29743,
+ "ν": 29744,
+ "đ": 29745,
+ "τ": 29746,
+ "─": 29747,
+ "ι": 29748,
+ "ε": 29749,
+ "→": 29750,
+ "ب": 29751,
+ "Å": 29752,
+ "ū": 29753,
+ "№": 29754,
+ "ş": 29755,
+ "不": 29756,
+ "џ": 29757,
+ "ー": 29758,
+ "中": 29759,
+ "Î": 29760,
+ "の": 29761,
+ ":": 29762,
+ "个": 29763,
+ "Й": 29764,
+ "ρ": 29765,
+ "有": 29766,
+ "Ä": 29767,
+ " ": 29768,
+ "ī": 29769,
+ "©": 29770,
+ "为": 29771,
+ "ه": 29772,
+ "י": 29773,
+ "ו": 29774,
+ "时": 29775,
+ "س": 29776,
+ "Ś": 29777,
+ "在": 29778,
+ "件": 29779,
+ "取": 29780,
+ "ς": 29781,
+ "™": 29782,
+ "이": 29783,
+ "σ": 29784,
+ "μ": 29785,
+ "定": 29786,
+ "文": 29787,
+ "据": 29788,
+ "置": 29789,
+ "Ž": 29790,
+ "±": 29791,
+ "表": 29792,
+ "成": 29793,
+ "ň": 29794,
+ "λ": 29795,
+ "¡": 29796,
+ "È": 29797,
+ "π": 29798,
+ "字": 29799,
+ "│": 29800,
+ "Ј": 29801,
+ "回": 29802,
+ "Є": 29803,
+ "到": 29804,
+ "行": 29805,
+ "§": 29806,
+ "½": 29807,
+ "ع": 29808,
+ "、": 29809,
+ "Ł": 29810,
+ "다": 29811,
+ "ン": 29812,
+ "κ": 29813,
+ "名": 29814,
+ "ה": 29815,
+ "入": 29816,
+ "η": 29817,
+ "大": 29818,
+ "对": 29819,
+ "可": 29820,
+ "Â": 29821,
+ "上": 29822,
+ "█": 29823,
+ "新": 29824,
+ "ف": 29825,
+ "加": 29826,
+ "要": 29827,
+ "Ż": 29828,
+ "下": 29829,
+ "分": 29830,
+ "值": 29831,
+ "ת": 29832,
+ "出": 29833,
+ "类": 29834,
+ "请": 29835,
+ "": 29836,
+ "息": 29837,
+ "Ú": 29838,
+ "υ": 29839,
+ "获": 29840,
+ "示": 29841,
+ "以": 29842,
+ "ר": 29843,
+ "接": 29844,
+ "ל": 29845,
+ "を": 29846,
+ "存": 29847,
+ "信": 29848,
+ "设": 29849,
+ "方": 29850,
+ "ش": 29851,
+ "能": 29852,
+ "点": 29853,
+ "人": 29854,
+ "前": 29855,
+ "ğ": 29856,
+ "作": 29857,
+ "═": 29858,
+ "↘": 29859,
+ "ð": 29860,
+ "理": 29861,
+ "■": 29862,
+ "法": 29863,
+ "️": 29864,
+ "ˈ": 29865,
+ "果": 29866,
+ "发": 29867,
+ "ح": 29868,
+ "γ": 29869,
+ "ɵ": 29870,
+ "า": 29871,
+ "َ": 29872,
+ "了": 29873,
+ "户": 29874,
+ "Í": 29875,
+ "ə": 29876,
+ "ス": 29877,
+ "查": 29878,
+ "し": 29879,
+ "מ": 29880,
+ "单": 29881,
+ "ť": 29882,
+ "ق": 29883,
+ "る": 29884,
+ "间": 29885,
+ "如": 29886,
+ "本": 29887,
+ "后": 29888,
+ "ί": 29889,
+ "式": 29890,
+ "ト": 29891,
+ "Щ": 29892,
+ "Ó": 29893,
+ "す": 29894,
+ "א": 29895,
+ "生": 29896,
+ "动": 29897,
+ "ک": 29898,
+ "和": 29899,
+ "い": 29900,
+ "": 29901,
+ "ა": 29902,
+ "가": 29903,
+ "하": 29904,
+ "�": 29905,
+ "小": 29906,
+ "返": 29907,
+ "否": 29908,
+ "ة": 29909,
+ "日": 29910,
+ "로": 29911,
+ "标": 29912,
+ "码": 29913,
+ "地": 29914,
+ "位": 29915,
+ "에": 29916,
+ " ": 29917,
+ "列": 29918,
+ "수": 29919,
+ "β": 29920,
+ "除": 29921,
+ "使": 29922,
+ "ש": 29923,
+ "ج": 29924,
+ "イ": 29925,
+ "δ": 29926,
+ "自": 29927,
+ "于": 29928,
+ "지": 29929,
+ "当": 29930,
+ "所": 29931,
+ "기": 29932,
+ "ი": 29933,
+ "ב": 29934,
+ "ร": 29935,
+ "★": 29936,
+ "子": 29937,
+ "号": 29938,
+ "ك": 29939,
+ "参": 29940,
+ "型": 29941,
+ "に": 29942,
+ "는": 29943,
+ "这": 29944,
+ "开": 29945,
+ "น": 29946,
+ "会": 29947,
+ "器": 29948,
+ "面": 29949,
+ "ル": 29950,
+ "图": 29951,
+ "度": 29952,
+ ")": 29953,
+ "(": 29954,
+ "의": 29955,
+ "内": 29956,
+ "을": 29957,
+ "最": 29958,
+ "": 29959,
+ "化": 29960,
+ "建": 29961,
+ "니": 29962,
+ "量": 29963,
+ "😂": 29964,
+ "始": 29965,
+ "ē": 29966,
+ "خ": 29967,
+ "를": 29968,
+ "ά": 29969,
+ "过": 29970,
+ "³": 29971,
+ "´": 29972,
+ "组": 29973,
+ "功": 29974,
+ "": 29975,
+ "": 29976,
+ "区": 29977,
+ "ز": 29978,
+ "ґ": 29979,
+ "ό": 29980,
+ "ッ": 29981,
+ "ω": 29982,
+ "Ç": 29983,
+ "选": 29984,
+ "通": 29985,
+ "结": 29986,
+ "录": 29987,
+ "改": 29988,
+ "ク": 29989,
+ "目": 29990,
+ "指": 29991,
+ "务": 29992,
+ "๐": 29993,
+ "输": 29994,
+ "た": 29995,
+ "อ": 29996,
+ "关": 29997,
+ "で": 29998,
+ "调": 29999,
+ "ा": 30000,
+ "정": 30001,
+ "合": 30002,
+ "已": 30003,
+ "시": 30004,
+ "部": 30005,
+ "页": 30006,
+ "━": 30007,
+ "ː": 30008,
+ "ま": 30009,
+ "我": 30010,
+ "求": 30011,
+ "市": 30012,
+ "次": 30013,
+ "נ": 30014,
+ "实": 30015,
+ "将": 30016,
+ "重": 30017,
+ "更": 30018,
+ "制": 30019,
+ "符": 30020,
+ "配": 30021,
+ "象": 30022,
+ "θ": 30023,
+ "ก": 30024,
+ "て": 30025,
+ "进": 30026,
+ "需": 30027,
+ "Đ": 30028,
+ "性": 30029,
+ "认": 30030,
+ "来": 30031,
+ "题": 30032,
+ "程": 30033,
+ "模": 30034,
+ "!": 30035,
+ "失": 30036,
+ "口": 30037,
+ "な": 30038,
+ "έ": 30039,
+ "": 30040,
+ "空": 30041,
+ "": 30042,
+ "期": 30043,
+ "者": 30044,
+ "は": 30045,
+ "Ђ": 30046,
+ "提": 30047,
+ "ή": 30048,
+ "ラ": 30049,
+ "한": 30050,
+ "态": 30051,
+ "复": 30052,
+ "ง": 30053,
+ "ე": 30054,
+ "Ø": 30055,
+ "리": 30056,
+ "修": 30057,
+ "‚": 30058,
+ "得": 30059,
+ "多": 30060,
+ "格": 30061,
+ "자": 30062,
+ "ע": 30063,
+ "่": 30064,
+ "函": 30065,
+ "应": 30066,
+ "↗": 30067,
+ "्": 30068,
+ "เ": 30069,
+ "正": 30070,
+ "注": 30071,
+ "스": 30072,
+ "서": 30073,
+ "リ": 30074,
+ "φ": 30075,
+ "ص": 30076,
+ "が": 30077,
+ "则": 30078,
+ "消": 30079,
+ "节": 30080,
+ "序": 30081,
+ "代": 30082,
+ "사": 30083,
+ "と": 30084,
+ "ד": 30085,
+ "้": 30086,
+ "र": 30087,
+ "此": 30088,
+ "保": 30089,
+ "ア": 30090,
+ "ư": 30091,
+ "인": 30092,
+ "ė": 30093,
+ "处": 30094,
+ "删": 30095,
+ "ɛ": 30096,
+ "容": 30097,
+ "ط": 30098,
+ "": 30099,
+ "之": 30100,
+ "包": 30101,
+ "状": 30102,
+ "ド": 30103,
+ "İ": 30104,
+ "体": 30105,
+ "同": 30106,
+ "事": 30107,
+ "🙂": 30108,
+ "タ": 30109,
+ "χ": 30110,
+ "ʿ": 30111,
+ "Ș": 30112,
+ "主": 30113,
+ "品": 30114,
+ "ק": 30115,
+ "询": 30116,
+ "创": 30117,
+ "该": 30118,
+ " ": 30119,
+ "元": 30120,
+ "第": 30121,
+ "天": 30122,
+ "或": 30123,
+ "年": 30124,
+ "转": 30125,
+ "ח": 30126,
+ "传": 30127,
+ "ţ": 30128,
+ "路": 30129,
+ "例": 30130,
+ "机": 30131,
+ "Ã": 30132,
+ "ď": 30133,
+ "高": 30134,
+ "相": 30135,
+ "โ": 30136,
+ "片": 30137,
+ "―": 30138,
+ "操": 30139,
+ "ա": 30140,
+ "ม": 30141,
+ "全": 30142,
+ "无": 30143,
+ "月": 30144,
+ "称": 30145,
+ "ั": 30146,
+ "就": 30147,
+ "": 30148,
+ "明": 30149,
+ "计": 30150,
+ "你": 30151,
+ "败": 30152,
+ "密": 30153,
+ "解": 30154,
+ "れ": 30155,
+ "أ": 30156,
+ "变": 30157,
+ "段": 30158,
+ "条": 30159,
+ "默": 30160,
+ "●": 30161,
+ "ล": 30162,
+ "色": 30163,
+ "断": 30164,
+ "商": 30165,
+ "ם": 30166,
+ "か": 30167,
+ "里": 30168,
+ "系": 30169,
+ "编": 30170,
+ "错": 30171,
+ "트": 30172,
+ "只": 30173,
+ "县": 30174,
+ "ს": 30175,
+ "常": 30176,
+ "初": 30177,
+ "ɔ": 30178,
+ "Α": 30179,
+ "フ": 30180,
+ "►": 30181,
+ "等": 30182,
+ "일": 30183,
+ "・": 30184,
+ "Ō": 30185,
+ "情": 30186,
+ "现": 30187,
+ "Ř": 30188,
+ "ِ": 30189,
+ "さ": 30190,
+ "ạ": 30191,
+ "용": 30192,
+ "证": 30193,
+ "해": 30194,
+ "手": 30195,
+ "支": 30196,
+ "입": 30197,
+ "服": 30198,
+ "்": 30199,
+ "道": 30200,
+ "어": 30201,
+ "送": 30202,
+ "载": 30203,
+ "限": 30204,
+ "线": 30205,
+ "属": 30206,
+ "": 30207,
+ "他": 30208,
+ "放": 30209,
+ "记": 30210,
+ "公": 30211,
+ "没": 30212,
+ "添": 30213,
+ "显": 30214,
+ "บ": 30215,
+ "ย": 30216,
+ "რ": 30217,
+ "其": 30218,
+ "集": 30219,
+ "金": 30220,
+ "国": 30221,
+ "任": 30222,
+ "ە": 30223,
+ "话": 30224,
+ "并": 30225,
+ "被": 30226,
+ "ύ": 30227,
+ "都": 30228,
+ "گ": 30229,
+ "意": 30230,
+ "כ": 30231,
+ "经": 30232,
+ "성": 30233,
+ "看": 30234,
+ "פ": 30235,
+ "址": 30236,
+ "ס": 30237,
+ "드": 30238,
+ "交": 30239,
+ "¼": 30240,
+ "Џ": 30241,
+ "完": 30242,
+ "Δ": 30243,
+ "义": 30244,
+ "보": 30245,
+ "向": 30246,
+ "换": 30247,
+ "山": 30248,
+ "算": 30249,
+ "二": 30250,
+ "پ": 30251,
+ "⁄": 30252,
+ "判": 30253,
+ "级": 30254,
+ "工": 30255,
+ "ด": 30256,
+ "⠀": 30257,
+ "家": 30258,
+ "レ": 30259,
+ "三": 30260,
+ "原": 30261,
+ "】": 30262,
+ "长": 30263,
+ "া": 30264,
+ "管": 30265,
+ "ѝ": 30266,
+ "क": 30267,
+ "学": 30268,
+ "ロ": 30269,
+ "验": 30270,
+ "写": 30271,
+ "Œ": 30272,
+ "从": 30273,
+ "【": 30274,
+ "收": 30275,
+ "ả": 30276,
+ "未": 30277,
+ "登": 30278,
+ "고": 30279,
+ "源": 30280,
+ "每": 30281,
+ "µ": 30282,
+ "误": 30283,
+ "り": 30284,
+ "요": 30285,
+ "按": 30286,
+ "ว": 30287,
+ "权": 30288,
+ "根": 30289,
+ "プ": 30290,
+ "串": 30291,
+ "ส": 30292,
+ "›": 30293,
+ "제": 30294,
+ "シ": 30295,
+ "Ş": 30296,
+ "确": 30297,
+ "好": 30298,
+ "统": 30299,
+ "效": 30300,
+ "网": 30301,
+ "\u0001": 30302,
+ "物": 30303,
+ "아": 30304,
+ "也": 30305,
+ "은": 30306,
+ "ệ": 30307,
+ "न": 30308,
+ "项": 30309,
+ "资": 30310,
+ "こ": 30311,
+ "引": 30312,
+ "ジ": 30313,
+ "ค": 30314,
+ "版": 30315,
+ "ท": 30316,
+ "平": 30317,
+ "们": 30318,
+ "与": 30319,
+ "き": 30320,
+ "移": 30321,
+ "ि": 30322,
+ "素": 30323,
+ "执": 30324,
+ "주": 30325,
+ "‐": 30326,
+ "Ґ": 30327,
+ "ี": 30328,
+ "板": 30329,
+ "问": 30330,
+ "Ε": 30331,
+ "安": 30332,
+ "면": 30333,
+ "소": 30334,
+ "ต": 30335,
+ "ิ": 30336,
+ "持": 30337,
+ "습": 30338,
+ "Σ": 30339,
+ "ら": 30340,
+ "コ": 30341,
+ "心": 30342,
+ "Π": 30343,
+ "打": 30344,
+ "」": 30345,
+ "상": 30346,
+ "「": 30347,
+ "检": 30348,
+ "库": 30349,
+ "÷": 30350,
+ "으": 30351,
+ "测": 30352,
+ "ん": 30353,
+ "े": 30354,
+ "ُ": 30355,
+ "力": 30356,
+ "直": 30357,
+ "由": 30358,
+ "ى": 30359,
+ "试": 30360,
+ "必": 30361,
+ "端": 30362,
+ "ʻ": 30363,
+ "先": 30364,
+ "↑": 30365,
+ "命": 30366,
+ "도": 30367,
+ "전": 30368,
+ "ห": 30369,
+ "员": 30370,
+ "ɪ": 30371,
+ "있": 30372,
+ "比": 30373,
+ "ṣ": 30374,
+ "時": 30375,
+ "择": 30376,
+ "ذ": 30377,
+ "テ": 30378,
+ "": 30379,
+ "构": 30380,
+ "备": 30381,
+ "그": 30382,
+ "链": 30383,
+ "说": 30384,
+ "ლ": 30385,
+ "ן": 30386,
+ "签": 30387,
+ "う": 30388,
+ "غ": 30389,
+ "ế": 30390,
+ "ض": 30391,
+ "ḥ": 30392,
+ "启": 30393,
+ "력": 30394,
+ "ო": 30395,
+ "付": 30396,
+ "მ": 30397,
+ "索": 30398,
+ "特": 30399,
+ "ג": 30400,
+ "西": 30401,
+ "대": 30402,
+ "├": 30403,
+ "": 30404,
+ "": 30405,
+ "外": 30406,
+ "צ": 30407,
+ "头": 30408,
+ "连": 30409,
+ "流": 30410,
+ "◄": 30411,
+ "デ": 30412,
+ "カ": 30413,
+ "র": 30414,
+ "오": 30415,
+ "找": 30416,
+ "清": 30417,
+ "🤣": 30418,
+ "去": 30419,
+ "₹": 30420,
+ "경": 30421,
+ "グ": 30422,
+ "ْ": 30423,
+ "¢": 30424,
+ "因": 30425,
+ "": 30426,
+ "Κ": 30427,
+ "增": 30428,
+ "知": 30429,
+ "¶": 30430,
+ "像": 30431,
+ "♥": 30432,
+ "터": 30433,
+ "く": 30434,
+ "ậ": 30435,
+ "メ": 30436,
+ "Æ": 30437,
+ "省": 30438,
+ "स": 30439,
+ "म": 30440,
+ "❤": 30441,
+ "あ": 30442,
+ "样": 30443,
+ "起": 30444,
+ "台": 30445,
+ "读": 30446,
+ "角": 30447,
+ "南": 30448,
+ "整": 30449,
+ "订": 30450,
+ "\f": 30451,
+ "ט": 30452,
+ "マ": 30453,
+ "্": 30454,
+ "우": 30455,
+ "ն": 30456,
+ "您": 30457,
+ "ئ": 30458,
+ "基": 30459,
+ "水": 30460,
+ "생": 30461,
+ "‑": 30462,
+ "나": 30463,
+ "画": 30464,
+ "描": 30465,
+ "击": 30466,
+ "っ": 30467,
+ "라": 30468,
+ "ნ": 30469,
+ "ր": 30470,
+ "业": 30471,
+ "ბ": 30472,
+ "别": 30473,
+ "♦": 30474,
+ "ィ": 30475,
+ "त": 30476,
+ "给": 30477,
+ "문": 30478,
+ "形": 30479,
+ "控": 30480,
+ "然": 30481,
+ "동": 30482,
+ "Њ": 30483,
+ "": 30484,
+ "东": 30485,
+ "ป": 30486,
+ "州": 30487,
+ "排": 30488,
+ "세": 30489,
+ "装": 30490,
+ "할": 30491,
+ "Ć": 30492,
+ "∞": 30493,
+ "海": 30494,
+ "城": 30495,
+ "键": 30496,
+ "径": 30497,
+ "호": 30498,
+ "화": 30499,
+ "្": 30500,
+ "料": 30501,
+ "ơ": 30502,
+ "ी": 30503,
+ "ウ": 30504,
+ "具": 30505,
+ "ブ": 30506,
+ "块": 30507,
+ "再": 30508,
+ "ố": 30509,
+ "电": 30510,
+ ";": 30511,
+ "위": 30512,
+ "两": 30513,
+ "而": 30514,
+ "장": 30515,
+ "آ": 30516,
+ "Ț": 30517,
+ "バ": 30518,
+ "还": 30519,
+ "令": 30520,
+ "キ": 30521,
+ "ّ": 30522,
+ "값": 30523,
+ "번": 30524,
+ "만": 30525,
+ "总": 30526,
+ "ल": 30527,
+ "▲": 30528,
+ "异": 30529,
+ "光": 30530,
+ "客": 30531,
+ "非": 30532,
+ "ị": 30533,
+ "": 30534,
+ "þ": 30535,
+ "設": 30536,
+ "述": 30537,
+ "합": 30538,
+ "?": 30539,
+ "✔": 30540,
+ "导": 30541,
+ "ṇ": 30542,
+ "부": 30543,
+ "˙": 30544,
+ "Τ": 30545,
+ "も": 30546,
+ "구": 30547,
+ "镇": 30548,
+ "작": 30549,
+ "░": 30550,
+ "步": 30551,
+ "ộ": 30552,
+ "活": 30553,
+ "พ": 30554,
+ "←": 30555,
+ "ǎ": 30556,
+ "จ": 30557,
+ "束": 30558,
+ "ـ": 30559,
+ "": 30560,
+ "那": 30561,
+ "प": 30562,
+ "エ": 30563,
+ "志": 30564,
+ "么": 30565,
+ "运": 30566,
+ "北": 30567,
+ "超": 30568,
+ "་": 30569,
+ "布": 30570,
+ "ώ": 30571,
+ "͡": 30572,
+ "少": 30573,
+ "파": 30574,
+ "ʃ": 30575,
+ "ム": 30576,
+ "": 30577,
+ "卡": 30578,
+ "ন": 30579,
+ "Μ": 30580,
+ "ɑ": 30581,
+ "😉": 30582,
+ "辑": 30583,
+ "원": 30584,
+ "美": 30585,
+ "产": 30586,
+ "利": 30587,
+ "모": 30588,
+ "联": 30589,
+ "界": 30590,
+ "체": 30591,
+ "种": 30592,
+ "王": 30593,
+ "ľ": 30594,
+ "여": 30595,
+ "메": 30596,
+ "域": 30597,
+ "ვ": 30598,
+ "立": 30599,
+ "록": 30600,
+ "게": 30601,
+ "إ": 30602,
+ "ṭ": 30603,
+ "神": 30604,
+ "ո": 30605,
+ "音": 30606,
+ "☆": 30607,
+ "Ñ": 30608,
+ "조": 30609,
+ "動": 30610,
+ "缓": 30611,
+ "과": 30612,
+ "报": 30613,
+ "ʼ": 30614,
+ "ា": 30615,
+ "되": 30616,
+ "ե": 30617,
+ "视": 30618,
+ "ช": 30619,
+ "详": 30620,
+ "แ": 30621,
+ "¦": 30622,
+ "把": 30623,
+ "க": 30624,
+ "ি": 30625,
+ "출": 30626,
+ "비": 30627,
+ "边": 30628,
+ "框": 30629,
+ "व": 30630,
+ "サ": 30631,
+ "Ι": 30632,
+ "Ο": 30633,
+ "オ": 30634,
+ "¾": 30635,
+ "历": 30636,
+ "ŏ": 30637,
+ "门": 30638,
+ "ข": 30639,
+ "含": 30640,
+ "¬": 30641,
+ "周": 30642,
+ "填": 30643,
+ "待": 30644,
+ "ะ": 30645,
+ "დ": 30646,
+ "Ї": 30647,
+ "额": 30648,
+ "음": 30649,
+ "四": 30650,
+ "だ": 30651,
+ "회": 30652,
+ "止": 30653,
+ "率": 30654,
+ "环": 30655,
+ "パ": 30656,
+ "래": 30657,
+ "闭": 30658,
+ "̀": 30659,
+ "语": 30660,
+ "개": 30661,
+ "身": 30662,
+ "藏": 30663,
+ "य": 30664,
+ "된": 30665,
+ "即": 30666,
+ "拉": 30667,
+ "선": 30668,
+ "변": 30669,
+ "≥": 30670,
+ "ุ": 30671,
+ "些": 30672,
+ "🤷": 30673,
+ "せ": 30674,
+ "左": 30675,
+ "ợ": 30676,
+ "右": 30677,
+ "ể": 30678,
+ "내": 30679,
+ "ּ": 30680,
+ "ז": 30681,
+ "ে": 30682,
+ "告": 30683,
+ "ấ": 30684,
+ "白": 30685,
+ "账": 30686,
+ "费": 30687,
+ "江": 30688,
+ "み": 30689,
+ "‹": 30690,
+ "์": 30691,
+ "": 30692,
+ "造": 30693,
+ "但": 30694,
+ "十": 30695,
+ "它": 30696,
+ "ं": 30697,
+ "ŋ": 30698,
+ "ў": 30699,
+ "セ": 30700,
+ "女": 30701,
+ "⣿": 30702,
+ "ի": 30703,
+ "京": 30704,
+ "触": 30705,
+ "함": 30706,
+ "들": 30707,
+ "Ā": 30708,
+ "": 30709,
+ "石": 30710,
+ "よ": 30711,
+ "田": 30712,
+ "易": 30713,
+ "规": 30714,
+ "展": 30715,
+ "¯": 30716,
+ "做": 30717,
+ "星": 30718,
+ "უ": 30719,
+ "✓": 30720,
+ "თ": 30721,
+ "供": 30722,
+ "명": 30723,
+ "ξ": 30724,
+ "己": 30725,
+ "且": 30726,
+ "插": 30727,
+ "景": 30728,
+ "切": 30729,
+ "ไ": 30730,
+ "없": 30731,
+ "ョ": 30732,
+ "及": 30733,
+ "Ν": 30734,
+ "미": 30735,
+ "ث": 30736,
+ "데": 30737,
+ "价": 30738,
+ "乡": 30739,
+ "ह": 30740,
+ "チ": 30741,
+ "真": 30742,
+ "太": 30743,
+ "ู": 30744,
+ "ダ": 30745,
+ "局": 30746,
+ "♂": 30747,
+ "退": 30748,
+ "ு": 30749,
+ "ক": 30750,
+ "ி": 30751,
+ "何": 30752,
+ "😭": 30753,
+ "¥": 30754,
+ "": 30755,
+ "≈": 30756,
+ "司": 30757,
+ "层": 30758,
+ "실": 30759,
+ "站": 30760,
+ "首": 30761,
+ "款": 30762,
+ "រ": 30763,
+ "間": 30764,
+ "ָ": 30765,
+ "저": 30766,
+ "监": 30767,
+ "ァ": 30768,
+ "册": 30769,
+ "案": 30770,
+ "ो": 30771,
+ "反": 30772,
+ "听": 30773,
+ "族": 30774,
+ "析": 30775,
+ "ื": 30776,
+ "秒": 30777,
+ "공": 30778,
+ "": 30779,
+ "🚀": 30780,
+ "거": 30781,
+ "재": 30782,
+ "": 30783,
+ "場": 30784,
+ "广": 30785,
+ "播": 30786,
+ "║": 30787,
+ "⋅": 30788,
+ "技": 30789,
+ "贴": 30790,
+ "想": 30791,
+ "ʁ": 30792,
+ "ớ": 30793,
+ "ャ": 30794,
+ "중": 30795,
+ "》": 30796,
+ "速": 30797,
+ "频": 30798,
+ "队": 30799,
+ "ำ": 30800,
+ "け": 30801,
+ "ु": 30802,
+ "≤": 30803,
+ "↓": 30804,
+ "须": 30805,
+ "菜": 30806,
+ "̃": 30807,
+ "剪": 30808,
+ "버": 30809,
+ "ェ": 30810,
+ "Λ": 30811,
+ "细": 30812,
+ "選": 30813,
+ "द": 30814,
+ "¹": 30815,
+ "许": 30816,
+ "ầ": 30817,
+ "世": 30818,
+ "ュ": 30819,
+ "ء": 30820,
+ "‡": 30821,
+ "候": 30822,
+ "共": 30823,
+ "크": 30824,
+ "ธ": 30825,
+ "설": 30826,
+ "快": 30827,
+ "友": 30828,
+ "ְ": 30829,
+ "车": 30830,
+ "推": 30831,
+ "花": 30832,
+ "言": 30833,
+ "چ": 30834,
+ "至": 30835,
+ "開": 30836,
+ "校": 30837,
+ "個": 30838,
+ "村": 30839,
+ "つ": 30840,
+ "▌": 30841,
+ "ப": 30842,
+ "결": 30843,
+ "ņ": 30844,
+ "优": 30845,
+ "ន": 30846,
+ "达": 30847,
+ "核": 30848,
+ "ナ": 30849,
+ "场": 30850,
+ "影": 30851,
+ "🏻": 30852,
+ "钮": 30853,
+ "ظ": 30854,
+ "Þ": 30855,
+ "▼": 30856,
+ "お": 30857,
+ "份": 30858,
+ "微": 30859,
+ "ờ": 30860,
+ "识": 30861,
+ "행": 30862,
+ "《": 30863,
+ "ใ": 30864,
+ "ọ": 30865,
+ "预": 30866,
+ "ব": 30867,
+ "த": 30868,
+ "": 30869,
+ "ų": 30870,
+ "마": 30871,
+ "않": 30872,
+ "ɡ": 30873,
+ "계": 30874,
+ "연": 30875,
+ "五": 30876,
+ "Ź": 30877,
+ "め": 30878,
+ "很": 30879,
+ "간": 30880,
+ "無": 30881,
+ "ប": 30882,
+ "社": 30883,
+ "Ê": 30884,
+ "书": 30885,
+ "顶": 30886,
+ "ტ": 30887,
+ "才": 30888,
+ "云": 30889,
+ "└": 30890,
+ "ζ": 30891,
+ "،": 30892,
+ "搜": 30893,
+ "신": 30894,
+ "유": 30895,
+ "": 30896,
+ "✅": 30897,
+ "⭐": 30898,
+ "照": 30899,
+ "短": 30900,
+ "川": 30901,
+ "後": 30902,
+ "范": 30903,
+ "民": 30904,
+ "治": 30905,
+ "章": 30906,
+ "ề": 30907,
+ "바": 30908,
+ "ә": 30909,
+ "⚭": 30910,
+ "河": 30911,
+ "论": 30912,
+ "え": 30913,
+ "Ω": 30914,
+ "√": 30915,
+ "Ă": 30916,
+ "Γ": 30917,
+ "坐": 30918,
+ "적": 30919,
+ "停": 30920,
+ "추": 30921,
+ "受": 30922,
+ "♀": 30923,
+ "ʾ": 30924,
+ "树": 30925,
+ "林": 30926,
+ "치": 30927,
+ "fi": 30928,
+ "▒": 30929,
+ "张": 30930,
+ "着": 30931,
+ "访": 30932,
+ "考": 30933,
+ "教": 30934,
+ "ग": 30935,
+ "准": 30936,
+ "印": 30937,
+ "精": 30938,
+ "窗": 30939,
+ "宝": 30940,
+ "ち": 30941,
+ "围": 30942,
+ "ַ": 30943,
+ "致": 30944,
+ "モ": 30945,
+ "때": 30946,
+ "随": 30947,
+ "储": 30948,
+ "况": 30949,
+ "邮": 30950,
+ "武": 30951,
+ "⛔": 30952,
+ "维": 30953,
+ "ү": 30954,
+ "跳": 30955,
+ "ब": 30956,
+ "投": 30957,
+ "ủ": 30958,
+ "표": 30959,
+ "반": 30960,
+ "英": 30961,
+ "ʰ": 30962,
+ "👍": 30963,
+ "ज": 30964,
+ "带": 30965,
+ "為": 30966,
+ "续": 30967,
+ "ɨ": 30968,
+ "처": 30969,
+ "₂": 30970,
+ "클": 30971,
+ "群": 30972,
+ "현": 30973,
+ "风": 30974,
+ "购": 30975,
+ "ក": 30976,
+ "老": 30977,
+ "留": 30978,
+ "球": 30979,
+ "프": 30980,
+ "▄": 30981,
+ "史": 30982,
+ "Љ": 30983,
+ "⟩": 30984,
+ "분": 30985,
+ "გ": 30986,
+ "店": 30987,
+ "审": 30988,
+ "료": 30989,
+ "목": 30990,
+ "略": 30991,
+ "관": 30992,
+ "ִ": 30993,
+ "科": 30994,
+ "货": 30995,
+ "ம": 30996,
+ "络": 30997,
+ "阳": 30998,
+ "Ḥ": 30999,
+ "資": 31000,
+ "若": 31001,
+ "স": 31002,
+ "ہ": 31003,
+ "宽": 31004,
+ "见": 31005,
+ "ズ": 31006,
+ "游": 31007,
+ "방": 31008,
+ "ồ": 31009,
+ "ɾ": 31010,
+ "열": 31011,
+ "러": 31012,
+ "ך": 31013,
+ "\u001b": 31014,
+ "်": 31015,
+ "余": 31016,
+ "响": 31017,
+ "缩": 31018,
+ "ட": 31019,
+ "评": 31020,
+ "允": 31021,
+ "离": 31022,
+ "🤔": 31023,
+ "Ё": 31024,
+ "ʊ": 31025,
+ "黑": 31026,
+ "马": 31027,
+ "⟨": 31028,
+ "値": 31029,
+ "箱": 31030,
+ "야": 31031,
+ "ម": 31032,
+ "Ő": 31033,
+ "感": 31034,
+ "ツ": 31035,
+ "ụ": 31036,
+ "ポ": 31037,
+ "확": 31038,
+ "声": 31039,
+ "战": 31040,
+ "ѕ": 31041,
+ "変": 31042,
+ "와": 31043,
+ "父": 31044,
+ "ベ": 31045,
+ "助": 31046,
+ "업": 31047,
+ "ʲ": 31048,
+ "ÿ": 31049,
+ "充": 31050,
+ "强": 31051,
+ "博": 31052,
+ "ミ": 31053,
+ "销": 31054,
+ "당": 31055,
+ "記": 31056,
+ "什": 31057,
+ "匹": 31058,
+ "ւ": 31059,
+ "そ": 31060,
+ "코": 31061,
+ "ল": 31062,
+ "ŭ": 31063,
+ "午": 31064,
+ "ニ": 31065,
+ "\u0012": 31066,
+ "ʒ": 31067,
+ "შ": 31068,
+ "某": 31069,
+ "ォ": 31070,
+ "足": 31071,
+ "타": 31072,
+ "Ð": 31073,
+ "ხ": 31074,
+ "름": 31075,
+ "木": 31076,
+ "楼": 31077,
+ "최": 31078,
+ "红": 31079,
+ "¨": 31080,
+ "古": 31081,
+ "\u0006": 31082,
+ "단": 31083,
+ "今": 31084,
+ "ʔ": 31085,
+ "ट": 31086,
+ "ম": 31087,
+ "斯": 31088,
+ "語": 31089,
+ "Ÿ": 31090,
+ "🙄": 31091,
+ "牌": 31092,
+ "안": 31093,
+ "ស": 31094,
+ "颜": 31095,
+ "~": 31096,
+ "克": 31097,
+ "深": 31098,
+ "금": 31099,
+ "會": 31100,
+ "尔": 31101,
+ "释": 31102,
+ "批": 31103,
+ "산": 31104,
+ "野": 31105,
+ "防": 31106,
+ "Η": 31107,
+ "ө": 31108,
+ "ψ": 31109,
+ "ボ": 31110,
+ "": 31111,
+ "各": 31112,
+ "진": 31113,
+ "追": 31114,
+ "句": 31115,
+ "警": 31116,
+ "Φ": 31117,
+ "ѣ": 31118,
+ "ḍ": 31119,
+ "词": 31120,
+ "男": 31121,
+ "글": 31122,
+ "식": 31123,
+ "隐": 31124,
+ "복": 31125,
+ "盘": 31126,
+ "Ì": 31127,
+ "申": 31128,
+ "议": 31129,
+ "ザ": 31130,
+ "近": 31131,
+ "능": 31132,
+ "য": 31133,
+ "東": 31134,
+ "這": 31135,
+ "ர": 31136,
+ "距": 31137,
+ "院": 31138,
+ "德": 31139,
+ "ǐ": 31140,
+ "针": 31141,
+ "▀": 31142,
+ "↔": 31143,
+ "房": 31144,
+ "青": 31145,
+ "政": 31146,
+ "😅": 31147,
+ "递": 31148,
+ "প": 31149,
+ "波": 31150,
+ "ソ": 31151,
+ "绑": 31152,
+ "ビ": 31153,
+ "ễ": 31154,
+ "포": 31155,
+ "\u0010": 31156,
+ "ử": 31157,
+ "등": 31158,
+ "환": 31159,
+ "士": 31160,
+ "ত": 31161,
+ "Θ": 31162,
+ "초": 31163,
+ "境": 31164,
+ "差": 31165,
+ "采": 31166,
+ "디": 31167,
+ "ĩ": 31168,
+ "升": 31169,
+ "背": 31170,
+ "배": 31171,
+ "龙": 31172,
+ "街": 31173,
+ "್": 31174,
+ "ṛ": 31175,
+ "ু": 31176,
+ "弹": 31177,
+ "魔": 31178,
+ "객": 31179,
+ "‰": 31180,
+ "⌁": 31181,
+ "ἐ": 31182,
+ "禁": 31183,
+ "ผ": 31184,
+ "қ": 31185,
+ "島": 31186,
+ "ா": 31187,
+ "♭": 31188,
+ "百": 31189,
+ "ứ": 31190,
+ "ネ": 31191,
+ "专": 31192,
+ "來": 31193,
+ "刷": 31194,
+ "필": 31195,
+ "յ": 31196,
+ "ắ": 31197,
+ "华": 31198,
+ "Β": 31199,
+ "श": 31200,
+ "¸": 31201,
+ "屏": 31202,
+ "死": 31203,
+ "遍": 31204,
+ "검": 31205,
+ "Χ": 31206,
+ "것": 31207,
+ "八": 31208,
+ "览": 31209,
+ "택": 31210,
+ "唯": 31211,
+ "∙": 31212,
+ "¤": 31213,
+ "페": 31214,
+ "让": 31215,
+ "锁": 31216,
+ "무": 31217,
+ "思": 31218,
+ "隔": 31219,
+ "Ô": 31220,
+ "\u0013": 31221,
+ "ṃ": 31222,
+ "ワ": 31223,
+ "低": 31224,
+ "션": 31225,
+ "半": 31226,
+ "较": 31227,
+ "ត": 31228,
+ "享": 31229,
+ "积": 31230,
+ "": 31231,
+ "😊": 31232,
+ "典": 31233,
+ "ǔ": 31234,
+ "六": 31235,
+ "便": 31236,
+ "ɐ": 31237,
+ "简": 31238,
+ "继": 31239,
+ "仅": 31240,
+ "尾": 31241,
+ "": 31242,
+ "வ": 31243,
+ "կ": 31244,
+ "": 31245,
+ "영": 31246,
+ "火": 31247,
+ "湖": 31248,
+ "書": 31249,
+ "발": 31250,
+ "ハ": 31251,
+ "循": 31252,
+ "术": 31253,
+ "結": 31254,
+ "ļ": 31255,
+ "乐": 31256,
+ "滤": 31257,
+ "종": 31258,
+ "ถ": 31259,
+ "ὶ": 31260,
+ "满": 31261,
+ "╝": 31262,
+ "わ": 31263,
+ "ど": 31264,
+ "็": 31265,
+ "형": 31266,
+ "國": 31267,
+ "ự": 31268,
+ "線": 31269,
+ "블": 31270,
+ "封": 31271,
+ "確": 31272,
+ "依": 31273,
+ "ս": 31274,
+ "永": 31275,
+ "색": 31276,
+ "歌": 31277,
+ "數": 31278,
+ "福": 31279,
+ "삭": 31280,
+ "実": 31281,
+ "레": 31282,
+ "ſ": 31283,
+ "千": 31284,
+ "\u000e": 31285,
+ "母": 31286,
+ "더": 31287,
+ "임": 31288,
+ "տ": 31289,
+ "ے": 31290,
+ "几": 31291,
+ "双": 31292,
+ "노": 31293,
+ "ณ": 31294,
+ "掉": 31295,
+ "Ρ": 31296,
+ "ἀ": 31297,
+ "標": 31298,
+ "長": 31299,
+ "档": 31300,
+ "태": 31301,
+ "ペ": 31302,
+ "본": 31303,
+ "": 31304,
+ "底": 31305,
+ "终": 31306,
+ "請": 31307,
+ "კ": 31308,
+ "̯": 31309,
+ "예": 31310,
+ "▬": 31311,
+ "報": 31312,
+ "ピ": 31313,
+ "๏": 31314,
+ "暂": 31315,
+ "李": 31316,
+ "Υ": 31317,
+ "\u0005": 31318,
+ "\u0002": 31319,
+ "替": 31320,
+ "운": 31321,
+ "射": 31322,
+ "\u0018": 31323,
+ "매": 31324,
+ "\u0011": 31325,
+ "🏼": 31326,
+ "票": 31327,
+ "附": 31328,
+ "ノ": 31329,
+ "ũ": 31330,
+ "压": 31331,
+ "阿": 31332,
+ "Ò": 31333,
+ "테": 31334,
+ "∼": 31335,
+ "万": 31336,
+ "մ": 31337,
+ "후": 31338,
+ "普": 31339,
+ "截": 31340,
+ "속": 31341,
+ "括": 31342,
+ "😀": 31343,
+ "ை": 31344,
+ "▶": 31345,
+ "까": 31346,
+ "ট": 31347,
+ "曲": 31348,
+ "师": 31349,
+ "钱": 31350,
+ "栏": 31351,
+ "Ы": 31352,
+ "走": 31353,
+ "ữ": 31354,
+ "": 31355,
+ "归": 31356,
+ "점": 31357,
+ "🔥": 31358,
+ "었": 31359,
+ "連": 31360,
+ "私": 31361,
+ "청": 31362,
+ "刘": 31363,
+ "免": 31364,
+ "": 31365,
+ "奖": 31366,
+ "見": 31367,
+ "ֹ": 31368,
+ "☺": 31369,
+ "ケ": 31370,
+ "역": 31371,
+ "际": 31372,
+ "받": 31373,
+ "望": 31374,
+ "帝": 31375,
+ "减": 31376,
+ "두": 31377,
+ "领": 31378,
+ "": 31379,
+ "钟": 31380,
+ "ガ": 31381,
+ "架": 31382,
+ "든": 31383,
+ "ல": 31384,
+ "松": 31385,
+ "□": 31386,
+ "越": 31387,
+ "答": 31388,
+ "ɕ": 31389,
+ "ῦ": 31390,
+ "染": 31391,
+ "": 31392,
+ "质": 31393,
+ "顺": 31394,
+ "气": 31395,
+ "╗": 31396,
+ "計": 31397,
+ "ქ": 31398,
+ "亮": 31399,
+ "🤦": 31400,
+ "̂": 31401,
+ "ٹ": 31402,
+ "座": 31403,
+ "ˌ": 31404,
+ "均": 31405,
+ "\u000b": 31406,
+ "官": 31407,
+ "适": 31408,
+ "护": 31409,
+ "久": 31410,
+ "春": 31411,
+ "曹": 31412,
+ "皇": 31413,
+ "脚": 31414,
+ "池": 31415,
+ "延": 31416,
+ "키": 31417,
+ "품": 31418,
+ "現": 31419,
+ "檔": 31420,
+ "ば": 31421,
+ "ⴰ": 31422,
+ "希": 31423,
+ "玩": 31424,
+ "固": 31425,
+ "黄": 31426,
+ "": 31427,
+ "☽": 31428,
+ "银": 31429,
+ "\u0003": 31430,
+ "┃": 31431,
+ "👏": 31432,
+ "불": 31433,
+ "攻": 31434,
+ "へ": 31435,
+ "决": 31436,
+ "⊙": 31437,
+ "宁": 31438,
+ "च": 31439,
+ "機": 31440,
+ "義": 31441,
+ "ɲ": 31442,
+ "\u0015": 31443,
+ "했": 31444,
+ "ẩ": 31445,
+ "愛": 31446,
+ "矩": 31447,
+ "패": 31448,
+ "ặ": 31449,
+ "郎": 31450,
+ "Ь": 31451,
+ "绘": 31452,
+ "负": 31453,
+ "ổ": 31454,
+ "ய": 31455,
+ "汉": 31456,
+ "編": 31457,
+ "ێ": 31458,
+ "്": 31459,
+ "じ": 31460,
+ "카": 31461,
+ "似": 31462,
+ "ں": 31463,
+ "や": 31464,
+ "認": 31465,
+ "\u000f": 31466,
+ "過": 31467,
+ "통": 31468,
+ "▪": 31469,
+ "约": 31470,
+ "香": 31471,
+ "买": 31472,
+ "住": 31473,
+ "╚": 31474,
+ "😁": 31475,
+ "扩": 31476,
+ "静": 31477,
+ "려": 31478,
+ "학": 31479,
+ "钥": 31480,
+ "증": 31481,
+ "ỉ": 31482,
+ "她": 31483,
+ "食": 31484,
+ "往": 31485,
+ "點": 31486,
+ "偏": 31487,
+ "康": 31488,
+ "\u0014": 31489,
+ "į": 31490,
+ "준": 31491,
+ "\u0004": 31492,
+ "ฟ": 31493,
+ "♣": 31494,
+ "戏": 31495,
+ "ʂ": 31496,
+ "井": 31497,
+ "军": 31498,
+ "爱": 31499,
+ "ٱ": 31500,
+ "七": 31501,
+ "차": 31502,
+ "币": 31503,
+ "♠": 31504,
+ "哈": 31505,
+ "阅": 31506,
+ "介": 31507,
+ "观": 31508,
+ "區": 31509,
+ "˜": 31510,
+ "ً": 31511,
+ "又": 31512,
+ "冲": 31513,
+ "朝": 31514,
+ "姓": 31515,
+ "课": 31516,
+ "龍": 31517,
+ "각": 31518,
+ "∈": 31519,
+ "米": 31520,
+ "ƒ": 31521,
+ "喜": 31522,
+ "夜": 31523,
+ "团": 31524,
+ "⇒": 31525,
+ "远": 31526,
+ "\u001a": 31527,
+ "ὐ": 31528,
+ "承": 31529,
+ "ಿ": 31530,
+ "室": 31531,
+ "ʀ": 31532,
+ "ង": 31533,
+ "अ": 31534,
+ "罗": 31535,
+ "🙏": 31536,
+ "软": 31537,
+ "🟡": 31538,
+ "건": 31539,
+ "؟": 31540,
+ "း": 31541,
+ "ᴇ": 31542,
+ "ユ": 31543,
+ "토": 31544,
+ "策": 31545,
+ "̄": 31546,
+ "국": 31547,
+ "ֶ": 31548,
+ "协": 31549,
+ "营": 31550,
+ "関": 31551,
+ "吉": 31552,
+ "💀": 31553,
+ "奇": 31554,
+ "滚": 31555,
+ "轴": 31556,
+ "処": 31557,
+ "土": 31558,
+ "划": 31559,
+ "ड": 31560,
+ "临": 31561,
+ "ֵ": 31562,
+ "航": 31563,
+ "浏": 31564,
+ "ゴ": 31565,
+ "別": 31566,
+ "寺": 31567,
+ "於": 31568,
+ "進": 31569,
+ "ὸ": 31570,
+ "風": 31571,
+ "ன": 31572,
+ "班": 31573,
+ "◼": 31574,
+ "九": 31575,
+ "̥": 31576,
+ "號": 31577,
+ "류": 31578,
+ "础": 31579,
+ "般": 31580,
+ "︙": 31581,
+ "̈": 31582,
+ "番": 31583,
+ "✨": 31584,
+ "😎": 31585,
+ "ো": 31586,
+ "😍": 31587,
+ "單": 31588,
+ "帧": 31589,
+ "授": 31590,
+ "赋": 31591,
+ "巴": 31592,
+ "占": 31593,
+ "假": 31594,
+ "ṅ": 31595,
+ "透": 31596,
+ "項": 31597,
+ "ħ": 31598,
+ "馬": 31599,
+ "🟢": 31600,
+ "Ľ": 31601,
+ "լ": 31602,
+ "券": 31603,
+ "같": 31604,
+ "類": 31605,
+ "對": 31606,
+ "월": 31607,
+ "激": 31608,
+ "\u0017": 31609,
+ "戦": 31610,
+ "独": 31611,
+ "訊": 31612,
+ "ិ": 31613,
+ "套": 31614,
+ "ʷ": 31615,
+ "跟": 31616,
+ "ở": 31617,
+ "渲": 31618,
+ "顯": 31619,
+ "降": 31620,
+ "ာ": 31621,
+ "尼": 31622,
+ "血": 31623,
+ "언": 31624,
+ "牛": 31625,
+ "將": 31626,
+ "ศ": 31627,
+ "拍": 31628,
+ "刻": 31629,
+ "ზ": 31630,
+ "╔": 31631,
+ "藤": 31632,
+ "్": 31633,
+ "ῶ": 31634,
+ "🟠": 31635,
+ "良": 31636,
+ "김": 31637,
+ "দ": 31638,
+ "Ṣ": 31639,
+ "録": 31640,
+ "伊": 31641,
+ "落": 31642,
+ "雄": 31643,
+ "雪": 31644,
+ "映": 31645,
+ "著": 31646,
+ "른": 31647,
+ "ფ": 31648,
+ "対": 31649,
+ "智": 31650,
+ "译": 31651,
+ "┬": 31652,
+ "抽": 31653,
+ "ῖ": 31654,
+ "酒": 31655,
+ "Ћ": 31656,
+ "股": 31657,
+ "់": 31658,
+ "순": 31659,
+ "직": 31660,
+ "भ": 31661,
+ "谷": 31662,
+ "물": 31663,
+ "ǒ": 31664,
+ "⠄": 31665,
+ "热": 31666,
+ "終": 31667,
+ "夹": 31668,
+ "干": 31669,
+ "彩": 31670,
+ "敗": 31671,
+ "ќ": 31672,
+ "♯": 31673,
+ "̣": 31674,
+ "վ": 31675,
+ "轮": 31676,
+ "阵": 31677,
+ "夏": 31678,
+ "幕": 31679,
+ "吧": 31680,
+ "港": 31681,
+ "益": 31682,
+ "儿": 31683,
+ "액": 31684,
+ "售": 31685,
+ "兵": 31686,
+ "惠": 31687,
+ "欢": 31688,
+ "": 31689,
+ "零": 31690,
+ "學": 31691,
+ "": 31692,
+ "員": 31693,
+ "ỗ": 31694,
+ "玉": 31695,
+ "逻": 31696,
+ "᥀": 31697,
+ "吗": 31698,
+ "沒": 31699,
+ "≠": 31700,
+ "너": 31701,
+ "ச": 31702,
+ "\u0016": 31703,
+ "夫": 31704,
+ "წ": 31705,
+ "堂": 31706,
+ "電": 31707,
+ "≡": 31708,
+ "陆": 31709,
+ "져": 31710,
+ "研": 31711,
+ "荐": 31712,
+ "健": 31713,
+ "碼": 31714,
+ "练": 31715,
+ "検": 31716,
+ "송": 31717,
+ "ै": 31718,
+ "哪": 31719,
+ "圆": 31720,
+ "Ա": 31721,
+ "↩": 31722,
+ "托": 31723,
+ "̪": 31724,
+ "ू": 31725,
+ "缀": 31726,
+ "네": 31727,
+ "沙": 31728,
+ "兴": 31729,
+ "病": 31730,
+ "\u0007": 31731,
+ "ល": 31732,
+ "ừ": 31733,
+ "Ἀ": 31734,
+ "강": 31735,
+ "항": 31736,
+ "\u0019": 31737,
+ "換": 31738,
+ "温": 31739,
+ "帖": 31740,
+ "ទ": 31741,
+ "込": 31742,
+ "削": 31743,
+ "알": 31744,
+ "征": 31745,
+ "习": 31746,
+ "법": 31747,
+ "栈": 31748,
+ "绝": 31749,
+ "": 31750,
+ "ڕ": 31751,
+ "圖": 31752,
+ "苏": 31753,
+ "発": 31754,
+ "ု": 31755,
+ "町": 31756,
+ "互": 31757,
+ "়": 31758,
+ "ც": 31759,
+ "守": 31760,
+ "새": 31761,
+ "侧": 31762,
+ "草": 31763,
+ "ས": 31764,
+ "扫": 31765,
+ "‒": 31766,
+ "恢": 31767,
+ "ң": 31768,
+ "ण": 31769,
+ "ற": 31770,
+ "째": 31771,
+ "්": 31772,
+ "拟": 31773,
+ "派": 31774,
+ "🏽": 31775,
+ "呼": 31776,
+ "": 31777,
+ "演": 31778,
+ "究": 31779,
+ "교": 31780,
+ "ɣ": 31781,
+ "ए": 31782,
+ "ី": 31783,
+ "ף": 31784,
+ "富": 31785,
+ "駅": 31786,
+ "ず": 31787,
+ "♪": 31788,
+ "😆": 31789,
+ "접": 31790,
+ "ғ": 31791,
+ "▓": 31792,
+ "존": 31793,
+ "ಾ": 31794,
+ "旋": 31795,
+ "ゃ": 31796,
+ "补": 31797,
+ "ץ": 31798,
+ "門": 31799,
+ "ច": 31800,
+ "날": 31801,
+ "ภ": 31802,
+ "ག": 31803,
+ "傳": 31804,
+ "∆": 31805,
+ "": 31806,
+ "ׁ": 31807,
+ "缺": 31808,
+ "頭": 31809,
+ "怪": 31810,
+ "組": 31811,
+ "별": 31812,
+ "Ъ": 31813,
+ "發": 31814,
+ "雷": 31815,
+ "ರ": 31816,
+ "ซ": 31817,
+ "び": 31818,
+ "翻": 31819,
+ "ھ": 31820,
+ "პ": 31821,
+ "題": 31822,
+ "居": 31823,
+ "집": 31824,
+ "🌍": 31825,
+ "˚": 31826,
+ "避": 31827,
+ "줄": 31828,
+ "ុ": 31829,
+ "滑": 31830,
+ "故": 31831,
+ "ญ": 31832,
+ "〜": 31833,
+ "ನ": 31834,
+ "양": 31835,
+ "완": 31836,
+ "ள": 31837,
+ "倍": 31838,
+ "宗": 31839,
+ "択": 31840,
+ "브": 31841,
+ "ɴ": 31842,
+ "効": 31843,
+ "尺": 31844,
+ "視": 31845,
+ "ẽ": 31846,
+ "覆": 31847,
+ "ध": 31848,
+ "骨": 31849,
+ "달": 31850,
+ "ᴛ": 31851,
+ "蓝": 31852,
+ "關": 31853,
+ "額": 31854,
+ "Õ": 31855,
+ "∗": 31856,
+ "卷": 31857,
+ "갑": 31858,
+ "르": 31859,
+ "众": 31860,
+ "ᴀ": 31861,
+ "態": 31862,
+ "ٰ": 31863,
+ "暗": 31864,
+ "君": 31865,
+ "錯": 31866,
+ "ɒ": 31867,
+ "យ": 31868,
+ "ḫ": 31869,
+ "ῆ": 31870,
+ "亚": 31871,
+ "♡": 31872,
+ "割": 31873,
+ "鼠": 31874,
+ "̶": 31875,
+ "Ë": 31876,
+ "読": 31877,
+ "격": 31878,
+ "ゲ": 31879,
+ "眼": 31880,
+ "Ý": 31881,
+ "ژ": 31882,
+ "雨": 31883,
+ "宮": 31884,
+ "쪽": 31885,
+ "ष": 31886,
+ "複": 31887,
+ "剩": 31888,
+ "早": 31889,
+ "杂": 31890,
+ "焦": 31891,
+ "贝": 31892,
+ "突": 31893,
+ "워": 31894,
+ "另": 31895,
+ "摄": 31896,
+ "\b": 31897,
+ "": 31898,
+ "府": 31899,
+ "외": 31900,
+ "盖": 31901,
+ "\u001c": 31902,
+ "ษ": 31903,
+ "佛": 31904,
+ "概": 31905,
+ "與": 31906,
+ "經": 31907,
+ "-": 31908,
+ "һ": 31909,
+ "問": 31910,
+ "ು": 31911,
+ "ἰ": 31912,
+ "話": 31913,
+ "倒": 31914,
+ "葛": 31915,
+ "べ": 31916,
+ "ろ": 31917,
+ "\u001e": 31918,
+ "।": 31919,
+ "ေ": 31920,
+ "ᴏ": 31921,
+ "训": 31922,
+ "體": 31923,
+ "👌": 31924,
+ "內": 31925,
+ "က": 31926,
+ "企": 31927,
+ "약": 31928,
+ "찾": 31929,
+ "ོ": 31930,
+ "破": 31931,
+ "輸": 31932,
+ "림": 31933,
+ "塔": 31934,
+ "턴": 31935,
+ "杀": 31936,
+ "』": 31937,
+ "味": 31938,
+ "浮": 31939,
+ "┆": 31940,
+ "ġ": 31941,
+ "郡": 31942,
+ "┐": 31943,
+ "『": 31944,
+ "阶": 31945,
+ "雅": 31946,
+ "┈": 31947,
+ "园": 31948,
+ ".": 31949,
+ "吃": 31950,
+ "남": 31951,
+ " ": 31952,
+ "ར": 31953,
+ "帮": 31954,
+ "毛": 31955,
+ "耗": 31956,
+ "举": 31957,
+ "ర": 31958,
+ "拿": 31959,
+ "밀": 31960,
+ "ご": 31961,
+ "够": 31962,
+ "礼": 31963,
+ "ព": 31964,
+ "ね": 31965,
+ "": 31966,
+ "兰": 31967,
+ "❌": 31968,
+ "折": 31969,
+ "십": 31970,
+ "💎": 31971,
+ "業": 31972,
+ "诸": 31973,
+ "孙": 31974,
+ "བ": 31975,
+ "😳": 31976,
+ "種": 31977,
+ "Ï": 31978,
+ "ึ": 31979,
+ "": 31980,
+ "医": 31981,
+ "拼": 31982,
+ "↵": 31983,
+ "⅓": 31984,
+ "\u001f": 31985,
+ "မ": 31986,
+ "叫": 31987,
+ "জ": 31988,
+ "予": 31989,
+ "寸": 31990,
+ "梅": 31991,
+ "醒": 31992,
+ "津": 31993,
+ "န": 31994,
+ "ి": 31995,
+ "厂": 31996,
+ "屋": 31997,
+ "ख": 31998,
+ "師": 31999,
+ "👀": 32000,
+ "ỏ": 32001,
+ "ヤ": 32002,
+ "ὰ": 32003,
+ "\u001d": 32004,
+ "◆": 32005,
+ "ដ": 32006,
+ "材": 32007,
+ "ホ": 32008,
+ "張": 32009,
+ "洞": 32010,
+ "餐": 32011,
+ "천": 32012,
+ "হ": 32013,
+ "達": 32014,
+ "們": 32015,
+ "斗": 32016,
+ "横": 32017,
+ "백": 32018,
+ "ំ": 32019,
+ "ۆ": 32020,
+ "말": 32021,
+ "গ": 32022,
+ "佳": 32023,
+ "랜": 32024,
+ "仁": 32025,
+ "陈": 32026,
+ "飞": 32027,
+ "极": 32028,
+ "": 32029,
+ "및": 32030,
+ "仓": 32031,
+ "⬛": 32032,
+ "昌": 32033,
+ "錢": 32034,
+ "殊": 32035,
+ "┴": 32036,
+ "○": 32037,
+ "길": 32038,
+ "泉": 32039,
+ "甲": 32040,
+ "활": 32041,
+ "ひ": 32042,
+ "শ": 32043,
+ "ን": 32044,
+ "Ť": 32045,
+ "ღ": 32046,
+ "皮": 32047,
+ "強": 32048,
+ "赛": 32049,
+ "ా": 32050,
+ "預": 32051,
+ "င": 32052,
+ "튼": 32053,
+ "플": 32054,
+ "ყ": 32055,
+ "⋆": 32056,
+ "ք": 32057,
+ "ા": 32058,
+ "尚": 32059,
+ "또": 32060,
+ "բ": 32061,
+ "┌": 32062,
+ "節": 32063,
+ "森": 32064,
+ "आ": 32065,
+ "办": 32066,
+ "園": 32067,
+ "牙": 32068,
+ "庆": 32069,
+ "隆": 32070,
+ "😔": 32071,
+ "叉": 32072,
+ "գ": 32073,
+ "피": 32074,
+ "ギ": 32075,
+ "啊": 32076,
+ "続": 32077,
+ "灵": 32078,
+ "ヒ": 32079,
+ "忽": 32080,
+ "ʌ": 32081,
+ "량": 32082,
+ "油": 32083,
+ "讯": 32084,
+ "ⵉ": 32085,
+ "릭": 32086,
+ "刚": 32087,
+ "氏": 32088,
+ "ိ": 32089,
+ "Ī": 32090,
+ "誤": 32091,
+ "齐": 32092,
+ "末": 32093,
+ "🙌": 32094,
+ "̞": 32095,
+ "圈": 32096,
+ "念": 32097,
+ "숫": 32098,
+ "毫": 32099,
+ "當": 32100,
+ "規": 32101,
+ "판": 32102,
+ "ు": 32103,
+ "旧": 32104,
+ "卖": 32105,
+ "ฉ": 32106,
+ "幸": 32107,
+ "署": 32108,
+ "근": 32109,
+ "ই": 32110,
+ "岛": 32111,
+ "դ": 32112,
+ "觉": 32113,
+ "害": 32114,
+ "毕": 32115,
+ "ฐ": 32116,
+ "威": 32117,
+ "育": 32118,
+ "呢": 32119,
+ "峰": 32120,
+ "职": 32121,
+ "陽": 32122,
+ "ි": 32123,
+ "亞": 32124,
+ "ұ": 32125,
+ "₃": 32126,
+ "따": 32127,
+ "施": 32128,
+ "泰": 32129,
+ "載": 32130,
+ "
": 32131,
+ "笑": 32132,
+ "華": 32133,
+ "迎": 32134,
+ "됩": 32135,
+ "豆": 32136,
+ "嘉": 32137,
+ "🤡": 32138,
+ "ĕ": 32139,
+ "庄": 32140,
+ "級": 32141,
+ "Ψ": 32142,
+ "ི": 32143,
+ "気": 32144,
+ "责": 32145,
+ "հ": 32146,
+ "អ": 32147,
+ "乱": 32148,
+ "休": 32149,
+ "約": 32150,
+ "ฆ": 32151,
+ "∑": 32152,
+ "察": 32153,
+ "온": 32154,
+ "😬": 32155,
+ "ড": 32156,
+ "乘": 32157,
+ "람": 32158,
+ "इ": 32159,
+ "Ά": 32160,
+ "ந": 32161,
+ "ើ": 32162,
+ "亲": 32163,
+ "េ": 32164,
+ "委": 32165,
+ "赤": 32166,
+ "됨": 32167,
+ "勝": 32168,
+ "怎": 32169,
+ "감": 32170,
+ "宋": 32171,
+ "調": 32172,
+ "짜": 32173,
+ "ী": 32174,
+ "难": 32175,
+ "못": 32176,
+ "티": 32177,
+ "備": 32178,
+ "塞": 32179,
+ "វ": 32180,
+ "险": 32181,
+ "旅": 32182,
+ "虚": 32183,
+ "↳": 32184,
+ "笔": 32185,
+ "馆": 32186,
+ "Қ": 32187,
+ "⚡": 32188,
+ "ೆ": 32189,
+ "※": 32190,
+ "唐": 32191,
+ "律": 32192,
+ "稍": 32193,
+ "散": 32194,
+ "ર": 32195,
+ "ヴ": 32196,
+ "副": 32197,
+ "尽": 32198,
+ "挂": 32199,
+ "県": 32200,
+ "⚠": 32201,
+ "洋": 32202,
+ "鬼": 32203,
+ "암": 32204,
+ "孩": 32205,
+ "℃": 32206,
+ "並": 32207,
+ "ց": 32208,
+ "ូ": 32209,
+ "ℓ": 32210,
+ "ⵏ": 32211,
+ "扣": 32212,
+ "铁": 32213,
+ "闻": 32214,
+ "ˆ": 32215,
+ "戳": 32216,
+ "む": 32217,
+ "秀": 32218,
+ "細": 32219,
+ "ပ": 32220,
+ "御": 32221,
+ "拖": 32222,
+ "좌": 32223,
+ "ؤ": 32224,
+ "绍": 32225,
+ "ỹ": 32226,
+ "참": 32227,
+ "향": 32228,
+ "Ď": 32229,
+ "끝": 32230,
+ "민": 32231,
+ "ძ": 32232,
+ "贵": 32233,
+ "纪": 32234,
+ "秋": 32235,
+ "ಕ": 32236,
+ "ӏ": 32237,
+ "網": 32238,
+ "铺": 32239,
+ "恋": 32240,
+ "fl": 32241,
+ "兼": 32242,
+ "羽": 32243,
+ "창": 32244,
+ "啟": 32245,
+ "弟": 32246,
+ "년": 32247,
+ "慢": 32248,
+ "효": 32249,
+ "許": 32250,
+ "硬": 32251,
+ "잘": 32252,
+ "템": 32253,
+ "્": 32254,
+ "න": 32255,
+ "術": 32256,
+ "ڈ": 32257,
+ "溪": 32258,
+ "": 32259,
+ "暴": 32260,
+ "混": 32261,
+ "夢": 32262,
+ "랑": 32263,
+ "আ": 32264,
+ "還": 32265,
+ "探": 32266,
+ "祖": 32267,
+ "织": 32268,
+ "軍": 32269,
+ "թ": 32270,
+ "務": 32271,
+ "艺": 32272,
+ "ད": 32273,
+ "ት": 32274,
+ "ṁ": 32275,
+ "應": 32276,
+ "擇": 32277,
+ "🥰": 32278,
+ "ķ": 32279,
+ "渡": 32280,
+ "葉": 32281,
+ "령": 32282,
+ "決": 32283,
+ "刀": 32284,
+ "從": 32285,
+ "變": 32286,
+ "올": 32287,
+ "💪": 32288,
+ "灣": 32289,
+ "ር": 32290,
+ "평": 32291,
+ "衣": 32292,
+ "😄": 32293,
+ "ി": 32294,
+ "ჩ": 32295,
+ "ὁ": 32296,
+ "ほ": 32297,
+ "Û": 32298,
+ "চ": 32299,
+ "ර": 32300,
+ "製": 32301,
+ "隊": 32302,
+ "₱": 32303,
+ "纳": 32304,
+ "赖": 32305,
+ "农": 32306,
+ "桥": 32307,
+ "ỳ": 32308,
+ "🏾": 32309,
+ "阻": 32310,
+ "ជ": 32311,
+ "秘": 32312,
+ "박": 32313,
+ "伤": 32314,
+ "稿": 32315,
+ "ం": 32316,
+ "拦": 32317,
+ "넣": 32318,
+ "💕": 32319,
+ "₁": 32320,
+ "宿": 32321,
+ "錄": 32322,
+ "镜": 32323,
+ "채": 32324,
+ "Ə": 32325,
+ "ང": 32326,
+ "⇔": 32327,
+ "☼": 32328,
+ "ུ": 32329,
+ "党": 32330,
+ "급": 32331,
+ "洲": 32332,
+ "ղ": 32333,
+ "說": 32334,
+ "ĭ": 32335,
+ "尝": 32336,
+ "담": 32337,
+ "फ": 32338,
+ "哥": 32339,
+ "圣": 32340,
+ "萨": 32341,
+ "😏": 32342,
+ "ʏ": 32343,
+ "ெ": 32344,
+ "丁": 32345,
+ "虎": 32346,
+ "권": 32347,
+ "善": 32348,
+ "岩": 32349,
+ "커": 32350,
+ "◦": 32351,
+ "抛": 32352,
+ "석": 32353,
+ "Έ": 32354,
+ "宣": 32355,
+ "拳": 32356,
+ "팅": 32357,
+ "枚": 32358,
+ "洛": 32359,
+ "証": 32360,
+ "陵": 32361,
+ "佐": 32362,
+ "館": 32363,
+ "누": 32364,
+ "돌": 32365,
+ "₄": 32366,
+ "稱": 32367,
+ "聊": 32368,
+ "車": 32369,
+ "루": 32370,
+ "״": 32371,
+ "ಠ": 32372,
+ "庫": 32373,
+ "མ": 32374,
+ "統": 32375,
+ "련": 32376,
+ "़": 32377,
+ "ṯ": 32378,
+ "ക": 32379,
+ "旗": 32380,
+ "励": 32381,
+ "紀": 32382,
+ "忠": 32383,
+ "າ": 32384,
+ "杨": 32385,
+ "丹": 32386,
+ "Ù": 32387,
+ "ฝ": 32388,
+ "却": 32389,
+ "舞": 32390,
+ "轉": 32391,
+ "တ": 32392,
+ "丽": 32393,
+ "借": 32394,
+ "ා": 32395,
+ "ょ": 32396,
+ "옵": 32397,
+ "편": 32398,
+ "蒙": 32399,
+ "衡": 32400,
+ "ʋ": 32401,
+ "叶": 32402,
+ "̇": 32403,
+ "⬜": 32404,
+ "🇺": 32405,
+ "Հ": 32406,
+ "谢": 32407,
+ "Ą": 32408,
+ "ே": 32409,
+ "ằ": 32410,
+ "既": 32411,
+ "济": 32412,
+ "≯": 32413,
+ "準": 32414,
+ "답": 32415,
+ "ಲ": 32416,
+ "残": 32417,
+ "虑": 32418,
+ "̆": 32419,
+ "┘": 32420,
+ "急": 32421,
+ "招": 32422,
+ "막": 32423,
+ "≮": 32424,
+ "產": 32425,
+ "Ṭ": 32426,
+ "😢": 32427,
+ "垂": 32428,
+ "親": 32429,
+ "ģ": 32430,
+ "־": 32431,
+ "猫": 32432,
+ "ʟ": 32433,
+ "☃": 32434,
+ "✪": 32435,
+ "刪": 32436,
+ "胡": 32437,
+ "☉": 32438,
+ "晚": 32439,
+ "군": 32440,
+ "승": 32441,
+ "న": 32442,
+ "ὴ": 32443,
+ "曾": 32444,
+ "論": 32445,
+ "ɯ": 32446,
+ "త": 32447,
+ "戰": 32448,
+ "鱼": 32449,
+ "ǧ": 32450,
+ "寶": 32451,
+ "특": 32452,
+ "💯": 32453,
+ "崎": 32454,
+ "甘": 32455,
+ "該": 32456,
+ "링": 32457,
+ "😡": 32458,
+ "उ": 32459,
+ "ែ": 32460,
+ "頁": 32461,
+ "큰": 32462,
+ "➤": 32463,
+ "총": 32464,
+ "💰": 32465,
+ "∂": 32466,
+ "毁": 32467,
+ "聖": 32468,
+ "麻": 32469,
+ "ʐ": 32470,
+ "敏": 32471,
+ "運": 32472,
+ "될": 32473,
+ "쓰": 32474,
+ "ಸ": 32475,
+ "စ": 32476,
+ "✦": 32477,
+ "젝": 32478,
+ "復": 32479,
+ "寻": 32480,
+ "茶": 32481,
+ "ਾ": 32482,
+ "竹": 32483,
+ "遇": 32484,
+ "順": 32485,
+ "며": 32486,
+ "累": 32487,
+ "ĝ": 32488,
+ "ˇ": 32489,
+ "覧": 32490,
+ "এ": 32491,
+ "株": 32492,
+ "취": 32493,
+ "ስ": 32494,
+ "争": 32495,
+ "势": 32496,
+ "宇": 32497,
+ "橋": 32498,
+ "Ӏ": 32499,
+ "堆": 32500,
+ "ⵙ": 32501,
+ "丶": 32502,
+ "棋": 32503,
+ "肉": 32504,
+ "የ": 32505,
+ "": 32506,
+ "❶": 32507,
+ "季": 32508,
+ "ል": 32509,
+ "殿": 32510,
+ "優": 32511,
+ "試": 32512,
+ "첫": 32513,
+ "Ό": 32514,
+ "戶": 32515,
+ "ண": 32516,
+ "羅": 32517,
+ "桃": 32518,
+ "립": 32519,
+ "浪": 32520,
+ "脑": 32521,
+ "😛": 32522,
+ "弃": 32523,
+ "炮": 32524,
+ "轻": 32525,
+ "울": 32526,
+ "": 32527,
+ "ヘ": 32528,
+ "奥": 32529,
+ "💜": 32530,
+ "忘": 32531,
+ "遠": 32532,
+ "飛": 32533,
+ "魏": 32534,
+ "Ē": 32535,
+ "汇": 32536,
+ "央": 32537,
+ "逆": 32538,
+ "露": 32539,
+ "須": 32540,
+ "ѐ": 32541,
+ "ḷ": 32542,
+ "ದ": 32543,
+ "✭": 32544,
+ "寄": 32545,
+ "盟": 32546,
+ "财": 32547,
+ "際": 32548,
+ "ἔ": 32549,
+ "ǫ": 32550,
+ "थ": 32551,
+ "ാ": 32552,
+ "宫": 32553,
+ "巨": 32554,
+ "途": 32555,
+ "ʹ": 32556,
+ "ಗ": 32557,
+ "帐": 32558,
+ "": 32559,
+ "拒": 32560,
+ "药": 32561,
+ "🙃": 32562,
+ "ŕ": 32563,
+ "亡": 32564,
+ "壁": 32565,
+ "ም": 32566,
+ "參": 32567,
+ "😩": 32568,
+ "շ": 32569,
+ "ವ": 32570,
+ "ណ": 32571,
+ "丰": 32572,
+ "獲": 32573,
+ "莉": 32574,
+ "좋": 32575,
+ "ရ": 32576,
+ "₦": 32577,
+ "겠": 32578,
+ "👉": 32579,
+ "吴": 32580,
+ "岡": 32581,
+ "诉": 32582,
+ "읽": 32583,
+ "🥺": 32584,
+ "爆": 32585,
+ "🇸": 32586,
+ "ভ": 32587,
+ "迭": 32588,
+ "엔": 32589,
+ "ἄ": 32590,
+ "捷": 32591,
+ "納": 32592,
+ "邀": 32593,
+ "ಯ": 32594,
+ "爾": 32595,
+ "船": 32596,
+ "赞": 32597,
+ "胜": 32598,
+ "므": 32599,
+ "သ": 32600,
+ "構": 32601,
+ "磁": 32602,
+ "冰": 32603,
+ "딩": 32604,
+ "ે": 32605,
+ "媒": 32606,
+ "繁": 32607,
+ "☠": 32608,
+ "❒": 32609,
+ "仪": 32610,
+ "렬": 32611,
+ "昭": 32612,
+ "珠": 32613,
+ "離": 32614,
+ "ན": 32615,
+ "ల": 32616,
+ "ತ": 32617,
+ "拷": 32618,
+ "粉": 32619,
+ "벤": 32620,
+ "⇽": 32621,
+ "乌": 32622,
+ "拥": 32623,
+ "ҳ": 32624,
+ "ය": 32625,
+ "ེ": 32626,
+ "仙": 32627,
+ "塊": 32628,
+ "幅": 32629,
+ "🎉": 32630,
+ "Մ": 32631,
+ "跨": 32632,
+ "ٔ": 32633,
+ "恩": 32634,
+ "损": 32635,
+ "养": 32636,
+ "奈": 32637,
+ "ǀ": 32638,
+ "严": 32639,
+ "卫": 32640,
+ "迟": 32641,
+ "様": 32642,
+ "裡": 32643,
+ "난": 32644,
+ "았": 32645,
+ "͜": 32646,
+ "Ζ": 32647,
+ "ਰ": 32648,
+ "պ": 32649,
+ "ং": 32650,
+ "丢": 32651,
+ "伝": 32652,
+ "컨": 32653,
+ "ව": 32654,
+ "ြ": 32655,
+ "冷": 32656,
+ "遗": 32657,
+ "銀": 32658,
+ "̌": 32659,
+ "ᴜ": 32660,
+ "瑞": 32661,
+ "ฌ": 32662,
+ "❍": 32663,
+ "ふ": 32664,
+ "聚": 32665,
+ "碎": 32666,
+ "衛": 32667,
+ "অ": 32668,
+ "ញ": 32669,
+ "퍼": 32670,
+ "Ս": 32671,
+ "ນ": 32672,
+ "ẓ": 32673,
+ "✌": 32674,
+ "孝": 32675,
+ "陳": 32676,
+ "히": 32677,
+ "ක": 32678,
+ "黒": 32679,
+ "💖": 32680,
+ "ḩ": 32681,
+ "応": 32682,
+ "饰": 32683,
+ "∪": 32684,
+ "宜": 32685,
+ "樂": 32686,
+ "則": 32687,
+ "勇": 32688,
+ "徐": 32689,
+ "ⵓ": 32690,
+ "權": 32691,
+ "鲁": 32692,
+ "‟": 32693,
+ "庭": 32694,
+ "苗": 32695,
+ "🔴": 32696,
+ "闲": 32697,
+ "독": 32698,
+ "ɹ": 32699,
+ "ҽ": 32700,
+ "ថ": 32701,
+ "宏": 32702,
+ "尊": 32703,
+ "總": 32704,
+ "裝": 32705,
+ "ම": 32706,
+ "▸": 32707,
+ "測": 32708,
+ "ಮ": 32709,
+ "አ": 32710,
+ "轩": 32711,
+ "兄": 32712,
+ "剑": 32713,
+ "ન": 32714,
+ "朱": 32715,
+ "ǝ": 32716,
+ "Ḩ": 32717,
+ "担": 32718,
+ "灰": 32719,
+ "讲": 32720,
+ "롤": 32721,
+ "︎": 32722,
+ "😤": 32723,
+ "ោ": 32724,
+ "애": 32725,
+ "였": 32726,
+ "질": 32727,
+ "振": 32728,
+ "灯": 32729,
+ "ĉ": 32730,
+ "ස": 32731,
+ "閉": 32732,
+ "램": 32733,
+ "ಂ": 32734,
+ "げ": 32735,
+ "̧": 32736,
+ "狂": 32737,
+ "融": 32738,
+ "仍": 32739,
+ "實": 32740,
+ "楽": 32741,
+ "範": 32742,
+ "ٌ": 32743,
+ "వ": 32744,
+ "嵌": 32745,
+ "摩": 32746,
+ "袁": 32747,
+ "ষ": 32748,
+ "乎": 32749,
+ "규": 32750,
+ "岗": 32751,
+ "糊": 32752,
+ "క": 32753,
+ "雲": 32754,
+ "심": 32755,
+ "ई": 32756,
+ "འ": 32757,
+ "ἡ": 32758,
+ "丝": 32759,
+ "Ħ": 32760,
+ "ٍ": 32761,
+ "ٓ": 32762,
+ "အ": 32763,
+ "執": 32764,
+ "벨": 32765,
+ "ゼ": 32766,
+ "梦": 32767
+ },
+ "merges": [
+ "▁ t",
+ "i n",
+ "e r",
+ "▁ a",
+ "h e",
+ "o n",
+ "r e",
+ "▁ s",
+ "e n",
+ "a t",
+ "o r",
+ "▁t he",
+ "▁th e",
+ "▁ the",
+ "e s",
+ "▁ w",
+ "a n",
+ "▁ c",
+ "i s",
+ "i t",
+ "o u",
+ "▁ d",
+ "a l",
+ "a r",
+ "▁ p",
+ "▁ f",
+ "e d",
+ "▁ b",
+ "in g",
+ "i ng",
+ "▁ o",
+ "▁ m",
+ "l e",
+ "n d",
+ "a s",
+ "i c",
+ "▁ h",
+ "io n",
+ "i on",
+ "▁i n",
+ "▁ in",
+ "▁t o",
+ "▁ to",
+ "e t",
+ "o m",
+ "e l",
+ "▁o f",
+ "▁ of",
+ "s t",
+ "▁a nd",
+ "▁an d",
+ "▁ and",
+ "▁ l",
+ "▁t h",
+ "▁ th",
+ "▁ n",
+ "en t",
+ "e nt",
+ "i l",
+ "c t",
+ "r o",
+ "▁r e",
+ "▁ re",
+ "i d",
+ "a m",
+ "▁ I",
+ "a d",
+ "▁ e",
+ "▁ S",
+ "▁ g",
+ "▁ T",
+ "i m",
+ "o t",
+ "a c",
+ "u r",
+ "▁ (",
+ "i g",
+ "▁ =",
+ "o l",
+ "u t",
+ "▁ A",
+ "s e",
+ "▁ u",
+ "v e",
+ "▁ C",
+ "i f",
+ "o w",
+ "▁ y",
+ "c h",
+ "a y",
+ "▁d e",
+ "▁ de",
+ "▁s t",
+ "▁ st",
+ "▁ |",
+ "ve r",
+ "v er",
+ ") ;",
+ "▁ \"",
+ "l y",
+ "▁b e",
+ "▁ be",
+ "* *",
+ "▁i s",
+ "▁ is",
+ "o d",
+ "▁ M",
+ "at ion",
+ "ati on",
+ "atio n",
+ "u l",
+ "▁f or",
+ "▁fo r",
+ "▁ for",
+ "▁o n",
+ "▁ on",
+ "a g",
+ "c e",
+ "te r",
+ "t er",
+ "i r",
+ "t h",
+ "▁ v",
+ "q u",
+ "▁ B",
+ "e m",
+ "▁ P",
+ "▁y ou",
+ "▁yo u",
+ "▁ you",
+ "▁t hat",
+ "▁th at",
+ "▁ that",
+ "u n",
+ "▁ {",
+ "it h",
+ "i th",
+ "r i",
+ "es t",
+ "e st",
+ "a b",
+ "- -",
+ "a p",
+ "▁i t",
+ "▁ it",
+ "▁c on",
+ "▁co n",
+ "▁ con",
+ "at e",
+ "a te",
+ "u s",
+ "▁ H",
+ "u m",
+ "▁ D",
+ "o s",
+ "p e",
+ "▁ -",
+ "▁w h",
+ "▁ wh",
+ "▁a l",
+ "▁ al",
+ "▁a s",
+ "▁ as",
+ "an d",
+ "a nd",
+ "is t",
+ "i st",
+ "▁ L",
+ "▁ W",
+ "▁w ith",
+ "▁ with",
+ "▁a n",
+ "▁ an",
+ "er e",
+ "e re",
+ "▁ *",
+ "▁ R",
+ "▁h e",
+ "▁ he",
+ "▁ F",
+ "o c",
+ "▁w as",
+ "▁wa s",
+ "▁ was",
+ "er s",
+ "e rs",
+ "k e",
+ "ou t",
+ "o ut",
+ "h t",
+ "▁ r",
+ "es s",
+ "e ss",
+ "o p",
+ "re s",
+ "r es",
+ "i e",
+ "▁ E",
+ "▁ \\",
+ "▁T he",
+ "▁Th e",
+ "▁ The",
+ "en d",
+ "e nd",
+ "l d",
+ "▁ N",
+ "or t",
+ "o rt",
+ "▁ G",
+ "/ /",
+ "▁ #",
+ "ou r",
+ "o ur",
+ "t e",
+ "il l",
+ "i ll",
+ "ai n",
+ "a in",
+ "▁s e",
+ "▁ se",
+ "▁ $",
+ "▁p ro",
+ "▁pr o",
+ "▁ pro",
+ "or e",
+ "o re",
+ "▁c om",
+ "▁co m",
+ "▁ com",
+ "am e",
+ "a me",
+ "t r",
+ "▁n e",
+ "▁ ne",
+ "ro m",
+ "r om",
+ "u b",
+ "▁a t",
+ "▁ at",
+ "▁e x",
+ "▁ ex",
+ "an t",
+ "a nt",
+ "u e",
+ "▁o r",
+ "▁ or",
+ "▁ }",
+ "ar t",
+ "a rt",
+ "ct ion",
+ "▁ k",
+ "p t",
+ "n t",
+ "i v",
+ "d e",
+ "▁ O",
+ "p l",
+ "ur n",
+ "u rn",
+ "ig ht",
+ "igh t",
+ "i ght",
+ "al l",
+ "a ll",
+ "▁t his",
+ "▁th is",
+ "▁ this",
+ "se r",
+ "s er",
+ "av e",
+ "a ve",
+ "▁n ot",
+ "▁no t",
+ "▁ not",
+ "▁a re",
+ "▁ar e",
+ "▁ are",
+ "▁ j",
+ "▁l e",
+ "▁ le",
+ "i z",
+ "▁ '",
+ "ag e",
+ "a ge",
+ "me nt",
+ "men t",
+ "m ent",
+ "▁t r",
+ "▁ tr",
+ "ac k",
+ "a ck",
+ "us t",
+ "u st",
+ "( )",
+ "- >",
+ "it y",
+ "i ty",
+ "in e",
+ "i ne",
+ "ou ld",
+ "oul d",
+ "o uld",
+ "▁ J",
+ "o g",
+ "▁f rom",
+ "▁fr om",
+ "▁fro m",
+ "▁ from",
+ "▁w e",
+ "▁ we",
+ "el l",
+ "e ll",
+ "▁s h",
+ "▁ sh",
+ "▁e n",
+ "▁ en",
+ "ur e",
+ "u re",
+ "por t",
+ "po rt",
+ "p ort",
+ "▁c h",
+ "▁ ch",
+ "n e",
+ "▁b y",
+ "▁ by",
+ "pe r",
+ "p er",
+ "ar d",
+ "a rd",
+ "as s",
+ "a ss",
+ "g e",
+ "a k",
+ "ar e",
+ "a re",
+ "o k",
+ "a v",
+ "iv e",
+ "i ve",
+ "f f",
+ "ie s",
+ "i es",
+ "at h",
+ "a th",
+ "tu rn",
+ "t urn",
+ "▁ U",
+ "in t",
+ "i nt",
+ "-- --",
+ "--- -",
+ "- ---",
+ "▁i m",
+ "▁ im",
+ "os t",
+ "o st",
+ "ia l",
+ "i al",
+ "▁h ave",
+ "▁ha ve",
+ "▁hav e",
+ "▁ have",
+ "in d",
+ "i nd",
+ "i p",
+ "an s",
+ "a ns",
+ "x t",
+ "▁d o",
+ "▁ do",
+ "c l",
+ "▁i f",
+ "▁ if",
+ "co n",
+ "c on",
+ "i a",
+ "▁h is",
+ "▁hi s",
+ "▁ his",
+ "ul t",
+ "u lt",
+ "ro u",
+ "r ou",
+ "▁s u",
+ "▁ su",
+ "r a",
+ "▁u n",
+ "▁ un",
+ "ab le",
+ "abl e",
+ "a ble",
+ "▁ <",
+ "▁ K",
+ "om e",
+ "o me",
+ "▁q u",
+ "▁ qu",
+ "ge t",
+ "g et",
+ "▁m e",
+ "▁ me",
+ "as t",
+ "a st",
+ "ec t",
+ "e ct",
+ "▁# #",
+ "▁ ##",
+ "t o",
+ "▁c l",
+ "▁ cl",
+ "▁a b",
+ "▁ ab",
+ "ic e",
+ "i ce",
+ "ir e",
+ "i re",
+ "be r",
+ "b er",
+ "on e",
+ "o ne",
+ "ic h",
+ "i ch",
+ "he n",
+ "h en",
+ "▁c an",
+ "▁ca n",
+ "▁ can",
+ "▁T h",
+ "▁ Th",
+ "▁l a",
+ "▁ la",
+ "▁a ll",
+ "▁al l",
+ "▁ all",
+ "im e",
+ "i me",
+ "il e",
+ "i le",
+ "id e",
+ "i de",
+ "\" ,",
+ "▁p l",
+ "▁ pl",
+ "▁ V",
+ "r u",
+ "or m",
+ "o rm",
+ "▁h ad",
+ "▁ha d",
+ "▁ had",
+ "u d",
+ "as e",
+ "a se",
+ "or d",
+ "o rd",
+ ") ,",
+ "▁h er",
+ "▁he r",
+ "▁ her",
+ "▁I n",
+ "▁ In",
+ "ac e",
+ "a ce",
+ "▁b ut",
+ "▁bu t",
+ "▁ but",
+ "at a",
+ "a ta",
+ ": :",
+ "** **",
+ "*** *",
+ "* ***",
+ "on g",
+ "o ng",
+ "▁ &",
+ ". .",
+ "it e",
+ "i te",
+ "yp e",
+ "y pe",
+ "ac t",
+ "a ct",
+ "od e",
+ "o de",
+ "▁y our",
+ "▁you r",
+ "▁yo ur",
+ "▁ your",
+ "▁o ut",
+ "▁ou t",
+ "▁ out",
+ "▁g o",
+ "▁ go",
+ "li c",
+ "l ic",
+ "al ly",
+ "all y",
+ "▁s o",
+ "▁ so",
+ "or k",
+ "a u",
+ "▁u p",
+ "▁ up",
+ "▁ _",
+ "l l",
+ "= =",
+ "▁m y",
+ "▁ my",
+ "p p",
+ "c c",
+ "▁/ /",
+ "▁ //",
+ "▁the y",
+ "▁th ey",
+ "▁ they",
+ "g h",
+ "▁u s",
+ "▁ us",
+ "i b",
+ "ion s",
+ "io ns",
+ "i ons",
+ "ac h",
+ "a ch",
+ "en s",
+ "e ns",
+ "▁a r",
+ "▁ ar",
+ "o b",
+ "el f",
+ "oo k",
+ "o ok",
+ "at ed",
+ "ate d",
+ "a ted",
+ "an g",
+ "a ng",
+ "ig n",
+ "i gn",
+ "▁re turn",
+ "▁r eturn",
+ "▁ret urn",
+ "▁ return",
+ "▁re s",
+ "▁r es",
+ "▁ res",
+ "c k",
+ "ou s",
+ "o us",
+ "с т",
+ ") .",
+ "▁ п",
+ ". \"",
+ "н а",
+ "▁ i",
+ "ai l",
+ "a il",
+ "e p",
+ "▁a d",
+ "▁ ad",
+ "an ce",
+ "anc e",
+ "( \"",
+ "▁* *",
+ "▁ **",
+ "th er",
+ "the r",
+ "t her",
+ "ak e",
+ "a ke",
+ "▁w ill",
+ "▁ will",
+ "▁c omp",
+ "▁com p",
+ "▁co mp",
+ "▁ comp",
+ "▁o ne",
+ "▁on e",
+ "▁ one",
+ "▁g et",
+ "▁ge t",
+ "▁ get",
+ "o v",
+ "▁ Y",
+ "ar y",
+ "a ry",
+ "oc k",
+ "o ck",
+ "▁s he",
+ "▁sh e",
+ "▁ she",
+ "ch e",
+ "c he",
+ "f t",
+ "▁n ew",
+ "▁ne w",
+ "▁ new",
+ "▁d es",
+ "▁de s",
+ "▁ des",
+ "▁l i",
+ "▁ li",
+ "en ce",
+ "enc e",
+ "▁s a",
+ "▁ sa",
+ "re ss",
+ "res s",
+ "r ess",
+ "▁e l",
+ "▁ el",
+ "▁u nd",
+ "▁un d",
+ "▁ und",
+ "e g",
+ "fe r",
+ "f er",
+ "r y",
+ "ea r",
+ "e ar",
+ "os e",
+ "o se",
+ "ve ry",
+ "ver y",
+ "v ery",
+ "' ,",
+ "▁ +",
+ "▁ в",
+ "▁H e",
+ "▁ He",
+ "ub lic",
+ "ubl ic",
+ "u blic",
+ "▁the ir",
+ "iz e",
+ "i ze",
+ "▁w ere",
+ "▁we re",
+ "▁wer e",
+ "▁ were",
+ "in k",
+ "ow n",
+ "o wn",
+ "I n",
+ "{ \\",
+ "▁h as",
+ "▁ha s",
+ "▁ has",
+ "▁p er",
+ "▁pe r",
+ "▁ per",
+ "▁I t",
+ "▁ It",
+ "▁S t",
+ "▁ St",
+ "he r",
+ "h er",
+ "je ct",
+ "j ect",
+ "р а",
+ "il d",
+ "i ld",
+ "s o",
+ "▁s p",
+ "▁ sp",
+ "н и",
+ "d u",
+ "ro w",
+ "r ow",
+ "al ue",
+ "alu e",
+ "se t",
+ "s et",
+ "fo rm",
+ "for m",
+ "f orm",
+ "co m",
+ "c om",
+ "▁m an",
+ "▁ma n",
+ "▁ man",
+ "on t",
+ "o nt",
+ "ul l",
+ "u ll",
+ "▁c ont",
+ "▁con t",
+ "▁co nt",
+ "▁ cont",
+ "▁m ore",
+ "▁mor e",
+ "▁mo re",
+ "▁ more",
+ "ic k",
+ "i ck",
+ "▁w ould",
+ "▁wo uld",
+ "▁e v",
+ "▁ ev",
+ "▁ab out",
+ "▁ about",
+ "it ion",
+ "iti on",
+ "▁ z",
+ "ou nd",
+ "oun d",
+ "o und",
+ "re e",
+ "r ee",
+ "▁C h",
+ "▁ Ch",
+ "▁wh ich",
+ "▁ which",
+ "i o",
+ "() ;",
+ "( );",
+ "▁w ho",
+ "▁wh o",
+ "▁ who",
+ "er r",
+ "e rr",
+ "or y",
+ "o ry",
+ "ou nt",
+ "oun t",
+ "o unt",
+ "at ions",
+ "ation s",
+ "ati ons",
+ "atio ns",
+ "▁ с",
+ "ri ng",
+ "rin g",
+ "r ing",
+ "< /",
+ "▁f e",
+ "▁ fe",
+ "к о",
+ "н о",
+ "▁d is",
+ "▁di s",
+ "▁ dis",
+ "m a",
+ "▁t hem",
+ "▁the m",
+ "▁th em",
+ "▁a ny",
+ "▁an y",
+ "▁ any",
+ "▁n o",
+ "▁ no",
+ "-- ------",
+ "---- ----",
+ "--- -----",
+ "----- ---",
+ "------ --",
+ "------- -",
+ "- -------",
+ "▁p re",
+ "▁pr e",
+ "▁ pre",
+ "▁t e",
+ "▁ te",
+ "▁r o",
+ "▁ ro",
+ "▁h im",
+ "▁hi m",
+ "▁ him",
+ "▁ :",
+ "u p",
+ "▁in t",
+ "▁i nt",
+ "▁ int",
+ "▁a g",
+ "▁ ag",
+ "S t",
+ "ar k",
+ "e x",
+ "p h",
+ "ie nt",
+ "ien t",
+ "i ent",
+ "el y",
+ "e ly",
+ "▁p r",
+ "▁ pr",
+ "E R",
+ "▁im port",
+ "▁imp ort",
+ "▁ import",
+ "▁t ime",
+ "▁tim e",
+ "▁ti me",
+ "▁ time",
+ "р о",
+ "pr o",
+ "p ro",
+ "Us er",
+ "Use r",
+ "U ser",
+ "l o",
+ "▁ /",
+ "▁ [",
+ "or s",
+ "o rs",
+ "= \"",
+ "▁t here",
+ "▁the re",
+ "▁th ere",
+ "▁ther e",
+ "▁ there",
+ "▁l ike",
+ "▁li ke",
+ "▁lik e",
+ "▁ like",
+ "ol d",
+ "o ld",
+ "▁w hen",
+ "▁wh en",
+ "▁whe n",
+ "▁ when",
+ "ve rs",
+ "ver s",
+ "v ers",
+ "▁s ome",
+ "▁so me",
+ "▁som e",
+ "▁ some",
+ "in gs",
+ "ing s",
+ ") )",
+ "▁p art",
+ "▁par t",
+ "▁pa rt",
+ "▁ part",
+ "ic al",
+ "ica l",
+ "i cal",
+ "▁f un",
+ "▁fu n",
+ "▁ fun",
+ "▁k n",
+ "▁ kn",
+ "ay s",
+ "a ys",
+ "ie r",
+ "i er",
+ "▁b een",
+ "▁be en",
+ "ov e",
+ "o ve",
+ "▁s c",
+ "▁ sc",
+ "ia n",
+ "i an",
+ "▁o ver",
+ "▁ov er",
+ "▁ over",
+ "ie l",
+ "i el",
+ "▁p e",
+ "▁ pe",
+ "ri b",
+ "r ib",
+ "pu t",
+ "p ut",
+ "e c",
+ "et h",
+ "e th",
+ "ar am",
+ "ara m",
+ "a ram",
+ "ap p",
+ "a pp",
+ "▁ –",
+ "▁s tat",
+ "▁st at",
+ "▁sta t",
+ "▁ stat",
+ "po n",
+ "p on",
+ "▁w hat",
+ "▁wh at",
+ "▁ what",
+ "pt ion",
+ "w e",
+ "ad e",
+ "a de",
+ "▁w ork",
+ "▁wor k",
+ "▁ work",
+ "te xt",
+ "tex t",
+ "t ext",
+ "▁s aid",
+ "▁sa id",
+ "▁# ##",
+ "▁## #",
+ "▁ ###",
+ "I N",
+ "▁j ust",
+ "▁ju st",
+ "▁ just",
+ "ir st",
+ "irs t",
+ "▁in to",
+ "▁int o",
+ "▁ into",
+ "▁con st",
+ "▁cons t",
+ "▁ const",
+ "our ce",
+ "t t",
+ "p s",
+ "p r",
+ "er v",
+ "e rv",
+ "it t",
+ "i tt",
+ "u g",
+ "_ {",
+ "en ts",
+ "ent s",
+ "is h",
+ "i sh",
+ "en er",
+ "ene r",
+ "e ner",
+ "▁in ter",
+ "▁int er",
+ "▁inte r",
+ "▁ inter",
+ "pl e",
+ "p le",
+ "ol l",
+ "o ll",
+ "me r",
+ "m er",
+ "at er",
+ "ate r",
+ "a ter",
+ "oo l",
+ "o ol",
+ "e f",
+ "▁p ublic",
+ "▁pub lic",
+ "▁pu blic",
+ "▁publi c",
+ "▁ public",
+ "▁o ther",
+ "▁ot her",
+ "▁ other",
+ "р е",
+ "▁d ef",
+ "▁de f",
+ "▁ def",
+ "▁ @",
+ "г о",
+ "oin t",
+ "oi nt",
+ "o int",
+ "▁o ff",
+ "▁of f",
+ "▁ off",
+ "oi d",
+ "o id",
+ "re turn",
+ "ret urn",
+ "r eturn",
+ "▁s et",
+ "▁se t",
+ "▁ set",
+ "w o",
+ "ft er",
+ "fte r",
+ "f ter",
+ "s h",
+ "** ******",
+ "**** ****",
+ "****** **",
+ "▁o ur",
+ "▁ou r",
+ "▁ our",
+ "ri v",
+ "r iv",
+ "is s",
+ "i ss",
+ "▁W e",
+ "▁ We",
+ "n g",
+ "▁o b",
+ "▁ ob",
+ "s s",
+ "g r",
+ "▁t han",
+ "▁th an",
+ "▁ than",
+ "pe ct",
+ "pec t",
+ "p ect",
+ "ie d",
+ "i ed",
+ "s c",
+ "ie w",
+ "i ew",
+ "de r",
+ "d er",
+ "ys t",
+ "y st",
+ "e v",
+ "▁c ould",
+ "▁co uld",
+ "▁cou ld",
+ "▁ could",
+ "an n",
+ "a nn",
+ "en c",
+ "e nc",
+ "O N",
+ "i x",
+ "an c",
+ "a nc",
+ "▁al so",
+ "▁als o",
+ "▁ also",
+ "re at",
+ "rea t",
+ "▁a m",
+ "▁ am",
+ "▁b ec",
+ "▁be c",
+ "▁ bec",
+ "▁ и",
+ "ua l",
+ "u al",
+ "pe c",
+ "p ec",
+ "▁ .",
+ "▁b l",
+ "▁ bl",
+ "le ct",
+ "l ect",
+ "op le",
+ "opl e",
+ "o ple",
+ "y s",
+ "▁g r",
+ "▁ gr",
+ "ic t",
+ "i ct",
+ "i k",
+ "tr ing",
+ "tri ng",
+ "t ring",
+ "▁T his",
+ "▁Th is",
+ "▁ This",
+ "▁b ack",
+ "▁ba ck",
+ "▁ back",
+ "▁ о",
+ "▁f in",
+ "▁fi n",
+ "▁ fin",
+ "at ch",
+ "Co n",
+ "C on",
+ "( '",
+ "er m",
+ "e rm",
+ "▁= =",
+ "▁ ==",
+ "_ _",
+ "na me",
+ "nam e",
+ "n ame",
+ ", \"",
+ "▁d id",
+ "▁di d",
+ "▁ did",
+ "is e",
+ "i se",
+ "▁on ly",
+ "▁ only",
+ "ru ct",
+ "r uct",
+ "le s",
+ "l es",
+ "▁t hen",
+ "▁the n",
+ "▁th en",
+ "▁ then",
+ "au se",
+ "aus e",
+ "a use",
+ "в а",
+ "▁it s",
+ "▁i ts",
+ "▁ its",
+ "ri t",
+ "r it",
+ "▁k now",
+ "▁kn ow",
+ "▁ know",
+ "ie ld",
+ "iel d",
+ "i eld",
+ "▁c lass",
+ "▁cl ass",
+ "▁clas s",
+ "▁ class",
+ "▁ >",
+ "▁e m",
+ "▁ em",
+ "▁$ \\",
+ "▁ $\\",
+ "▁y ear",
+ "▁ye ar",
+ "▁ year",
+ "w n",
+ "} ,",
+ "▁d el",
+ "▁de l",
+ "▁ del",
+ "al e",
+ "a le",
+ "t y",
+ "fi g",
+ "f ig",
+ "s p",
+ "he d",
+ "h ed",
+ "ro und",
+ "rou nd",
+ "r ound",
+ "e w",
+ "▁d i",
+ "▁ di",
+ "▁d er",
+ "▁de r",
+ "▁ der",
+ "р и",
+ "re d",
+ "r ed",
+ "th is",
+ "t his",
+ "le t",
+ "l et",
+ "R E",
+ "a x",
+ "f r",
+ "ess age",
+ "essa ge",
+ "ou gh",
+ "o ugh",
+ "▁c omm",
+ "▁com m",
+ "▁co mm",
+ "▁ comm",
+ "f o",
+ "uc h",
+ "u ch",
+ "o y",
+ "▁pe ople",
+ "▁ people",
+ "yst em",
+ "ys tem",
+ "▁f irst",
+ "▁fir st",
+ "▁ first",
+ "▁f unction",
+ "▁fun ction",
+ "▁ function",
+ "an ge",
+ "ang e",
+ "▁h ow",
+ "▁ho w",
+ "▁ how",
+ "▁e t",
+ "▁ et",
+ "a h",
+ "▁l ook",
+ "▁lo ok",
+ "▁ look",
+ "т о",
+ "un d",
+ "u nd",
+ "▁u nder",
+ "▁un der",
+ "▁und er",
+ "▁ under",
+ "к а",
+ "▁ !",
+ "ra y",
+ "r ay",
+ "S T",
+ "if ic",
+ "ifi c",
+ "i fic",
+ "л и",
+ "re ad",
+ "rea d",
+ "r ead",
+ "▁b et",
+ "▁be t",
+ "▁ bet",
+ "io us",
+ "i ous",
+ "ar g",
+ "a rg",
+ "▁n eed",
+ "▁ne ed",
+ "▁ need",
+ "ma th",
+ "mat h",
+ "m ath",
+ "▁н а",
+ "▁ на",
+ "er t",
+ "e rt",
+ "▁o p",
+ "▁ op",
+ "▁a cc",
+ "▁ac c",
+ "▁ acc",
+ "Pr o",
+ "P ro",
+ "▁e st",
+ "▁es t",
+ "▁ est",
+ "▁U n",
+ "▁ Un",
+ "▁e nt",
+ "▁en t",
+ "▁ ent",
+ "▁re c",
+ "▁r ec",
+ "▁ rec",
+ "▁u se",
+ "▁us e",
+ "▁ use",
+ "е н",
+ "▁p ar",
+ "▁pa r",
+ "▁ par",
+ "a z",
+ "▁ д",
+ "▁W h",
+ "▁ Wh",
+ "sel f",
+ "s elf",
+ "▁k e",
+ "▁ ke",
+ "т а",
+ "▁w ant",
+ "▁wa nt",
+ "▁ want",
+ "▁e nd",
+ "▁en d",
+ "▁ end",
+ "▁d on",
+ "▁do n",
+ "▁ don",
+ "e k",
+ "re n",
+ "r en",
+ "Na me",
+ "N ame",
+ "▁= >",
+ "▁ =>",
+ "▁a pp",
+ "▁ap p",
+ "▁ app",
+ "▁qu e",
+ "▁q ue",
+ "▁ que",
+ "ig h",
+ "i gh",
+ "▁b u",
+ "▁ bu",
+ "eq u",
+ "e qu",
+ "ve l",
+ "v el",
+ "▁a ct",
+ "▁ac t",
+ "▁ act",
+ "cr e",
+ "c re",
+ "A T",
+ "▁v ar",
+ "▁va r",
+ "▁ var",
+ "ce ss",
+ "ces s",
+ "c ess",
+ "== ==",
+ "=== =",
+ "= ===",
+ "E x",
+ "▁a dd",
+ "▁ad d",
+ "▁ add",
+ "▁m od",
+ "▁mo d",
+ "▁ mod",
+ "un g",
+ "u ng",
+ "▁w here",
+ "▁wh ere",
+ "▁whe re",
+ "▁ where",
+ "ni ng",
+ "n ing",
+ "▁f l",
+ "▁ fl",
+ "al s",
+ "a ls",
+ "ter n",
+ "te rn",
+ "t ern",
+ "} }",
+ "▁A l",
+ "▁ Al",
+ "▁p os",
+ "▁po s",
+ "▁ pos",
+ "an k",
+ "▁a p",
+ "▁ ap",
+ "en g",
+ "e ng",
+ "▁ “",
+ "bl e",
+ "b le",
+ "▁re g",
+ "▁r eg",
+ "▁ reg",
+ "^ {",
+ "▁S he",
+ "▁Sh e",
+ "▁ She",
+ "▁* /",
+ "▁ */",
+ "ud e",
+ "u de",
+ "ad d",
+ "a dd",
+ "▁t wo",
+ "▁tw o",
+ "▁ two",
+ "▁c ol",
+ "▁co l",
+ "▁ col",
+ "▁s m",
+ "▁ sm",
+ "ai r",
+ "a ir",
+ "▁m ay",
+ "▁ma y",
+ "▁ may",
+ "fo re",
+ "for e",
+ "f ore",
+ "▁Y ou",
+ "▁ You",
+ "ro ugh",
+ "rou gh",
+ "r ough",
+ "▁c he",
+ "▁ch e",
+ "▁ che",
+ "▁a tt",
+ "▁at t",
+ "▁ att",
+ "ot h",
+ "o th",
+ "л а",
+ "▁c o",
+ "▁ co",
+ "at es",
+ "ate s",
+ "a tes",
+ "▁re m",
+ "▁r em",
+ "▁ rem",
+ "oo d",
+ "o od",
+ "Ty pe",
+ "Typ e",
+ "T ype",
+ "le d",
+ "l ed",
+ "fu l",
+ "f ul",
+ "▁s elf",
+ "▁sel f",
+ "▁ self",
+ "o f",
+ "▁A r",
+ "▁ Ar",
+ "qu e",
+ "q ue",
+ "▁e very",
+ "▁ev ery",
+ "▁ever y",
+ "▁ every",
+ "re f",
+ "r ef",
+ "Th e",
+ "T he",
+ "▁A nd",
+ "▁An d",
+ "▁ And",
+ "▁re l",
+ "▁r el",
+ "▁ rel",
+ "O R",
+ "I d",
+ "▁e ven",
+ "▁ev en",
+ "▁ even",
+ "E N",
+ "▁h and",
+ "▁ha nd",
+ "▁han d",
+ "▁ hand",
+ "ai t",
+ "a it",
+ "▁sh ould",
+ "▁ should",
+ "▁a fter",
+ "▁af ter",
+ "▁ after",
+ "▁d if",
+ "▁di f",
+ "gh t",
+ "g ht",
+ "if e",
+ "i fe",
+ "at or",
+ "ato r",
+ "a tor",
+ "as h",
+ "a sh",
+ "ri but",
+ "rib ut",
+ "ribu t",
+ "um ber",
+ "umb er",
+ "u mber",
+ "▁s ee",
+ "▁se e",
+ "▁ see",
+ "m s",
+ "▁c all",
+ "▁cal l",
+ "▁ca ll",
+ "▁ call",
+ "y n",
+ "d d",
+ "▁e s",
+ "▁ es",
+ "▁m ake",
+ "▁ma ke",
+ "▁ make",
+ "ot her",
+ "oth er",
+ "othe r",
+ "o ther",
+ "▁ —",
+ "\") ;",
+ "\" );",
+ "st r",
+ "s tr",
+ "▁l ong",
+ "▁lo ng",
+ "▁lon g",
+ "▁ long",
+ "le ment",
+ "lem ent",
+ "l ement",
+ "▁w or",
+ "▁wo r",
+ "▁ wor",
+ "it s",
+ "i ts",
+ "▁I f",
+ "▁ If",
+ "al se",
+ "als e",
+ "л ь",
+ "wa rd",
+ "war d",
+ "w ard",
+ "▁п о",
+ "▁ по",
+ "va l",
+ "v al",
+ "on s",
+ "o ns",
+ "▁ Z",
+ "▁n ow",
+ "▁no w",
+ "▁ now",
+ "da ta",
+ "dat a",
+ "d ata",
+ "am p",
+ "a mp",
+ "en se",
+ "ens e",
+ "▁th rough",
+ "▁thr ough",
+ "▁thro ugh",
+ "▁ through",
+ "▁d own",
+ "▁do wn",
+ "▁dow n",
+ "▁ down",
+ "at t",
+ "a tt",
+ "▁st atic",
+ "▁stat ic",
+ "▁ static",
+ "ic s",
+ "i cs",
+ "# #",
+ "po s",
+ "p os",
+ "▁v oid",
+ "▁vo id",
+ "▁ void",
+ "a w",
+ "ou n",
+ "o un",
+ "▁w ay",
+ "▁wa y",
+ "▁ way",
+ "ib le",
+ "i ble",
+ "ve nt",
+ "ven t",
+ "v ent",
+ "ow er",
+ "owe r",
+ "o wer",
+ "▁th ink",
+ "▁thin k",
+ "▁ think",
+ "t s",
+ "* /",
+ "▁a gain",
+ "▁ag ain",
+ "▁ again",
+ "at ing",
+ "ati ng",
+ "atin g",
+ "a ting",
+ "т е",
+ "ne r",
+ "n er",
+ "▁m ost",
+ "▁mo st",
+ "▁mos t",
+ "▁ most",
+ "li ne",
+ "lin e",
+ "l ine",
+ "y m",
+ "▁s ub",
+ "▁su b",
+ "▁ sub",
+ "er son",
+ "ers on",
+ "▁re qu",
+ "▁r equ",
+ "▁req u",
+ "▁ requ",
+ "A L",
+ "A R",
+ "ab el",
+ "abe l",
+ "a bel",
+ "on d",
+ "o nd",
+ ")) ;",
+ ") );",
+ "▁S e",
+ "▁ Se",
+ "▁B ut",
+ "▁Bu t",
+ "▁ But",
+ "al k",
+ "▁A n",
+ "▁ An",
+ "ne w",
+ "n ew",
+ "▁b ecause",
+ "▁bec ause",
+ "▁ because",
+ "ge r",
+ "g er",
+ "ul ar",
+ "ula r",
+ "u lar",
+ "ro up",
+ "rou p",
+ "r oup",
+ "t a",
+ ".. .",
+ ". ..",
+ "▁c ons",
+ "▁con s",
+ "▁co ns",
+ "▁ cons",
+ "▁r ight",
+ "▁ri ght",
+ "▁rig ht",
+ "▁ right",
+ "▁f r",
+ "▁ fr",
+ "b e",
+ "il y",
+ "i ly",
+ "к и",
+ "▁p h",
+ "▁ ph",
+ "ea d",
+ "e ad",
+ "? \"",
+ "▁g u",
+ "▁ gu",
+ "▁el se",
+ "▁els e",
+ "▁ else",
+ "▁s om",
+ "▁so m",
+ "▁ som",
+ "re nt",
+ "ren t",
+ "r ent",
+ "c o",
+ "em ent",
+ "eme nt",
+ "emen t",
+ "e ment",
+ "▁s tr",
+ "▁st r",
+ "▁ str",
+ "au lt",
+ "aul t",
+ "a ult",
+ "▁ з",
+ "л о",
+ "se rt",
+ "ser t",
+ "s ert",
+ "va r",
+ "v ar",
+ "ty pe",
+ "typ e",
+ "t ype",
+ "▁C om",
+ "▁Co m",
+ "▁ Com",
+ "л е",
+ "in s",
+ "i ns",
+ "m e",
+ "wa y",
+ "w ay",
+ "id ent",
+ "ide nt",
+ "iden t",
+ "▁p rov",
+ "▁pro v",
+ "▁pr ov",
+ "▁ prov",
+ "▁ м",
+ "▁tr ue",
+ "▁ true",
+ "▁P ro",
+ "▁Pr o",
+ "▁ Pro",
+ "f l",
+ "▁s l",
+ "▁ sl",
+ "▁A s",
+ "▁ As",
+ "} \\",
+ "I D",
+ "ue s",
+ "u es",
+ "▁in st",
+ "▁ins t",
+ "▁ inst",
+ "▁n ame",
+ "▁na me",
+ "▁nam e",
+ "▁ name",
+ "o x",
+ "▁ )",
+ "l i",
+ "am es",
+ "ame s",
+ "a mes",
+ "Re s",
+ "R es",
+ "▁s ur",
+ "▁su r",
+ "▁ sur",
+ "par am",
+ "pa ram",
+ "para m",
+ "p aram",
+ "▁st art",
+ "▁star t",
+ "▁sta rt",
+ "▁ start",
+ "a j",
+ "S E",
+ "as k",
+ "a sk",
+ "I T",
+ "St ring",
+ "Str ing",
+ "S tring",
+ "▁a ss",
+ "▁as s",
+ "▁ ass",
+ "▁p lay",
+ "▁pl ay",
+ "▁ play",
+ "ti ng",
+ "t ing",
+ "to n",
+ "t on",
+ "▁b efore",
+ "▁be fore",
+ "▁bef ore",
+ "▁ before",
+ "▁p ol",
+ "▁po l",
+ "▁ pol",
+ "ar ch",
+ "arc h",
+ "▁w ell",
+ "▁we ll",
+ "▁wel l",
+ "▁ well",
+ "Co m",
+ "C om",
+ "an y",
+ "a ny",
+ "ol og",
+ "olo g",
+ "o log",
+ "▁e rr",
+ "▁er r",
+ "▁ err",
+ "▁the se",
+ "▁th ese",
+ "ar s",
+ "a rs",
+ "e b",
+ "▁b r",
+ "▁ br",
+ "▁in cl",
+ "▁inc l",
+ "▁ incl",
+ "▁h el",
+ "▁he l",
+ "▁ hel",
+ "er n",
+ "e rn",
+ "od y",
+ "o dy",
+ "в о",
+ "▁in d",
+ "▁i nd",
+ "▁ ind",
+ "-- --------------",
+ "---- ------------",
+ "-------- --------",
+ "--- -------------",
+ "------------ ----",
+ "----- -----------",
+ "---------- ------",
+ "------ ----------",
+ "------------- ---",
+ "-------------- --",
+ "--------- -------",
+ "------- ---------",
+ "----------- -----",
+ "▁d ata",
+ "▁da ta",
+ "▁dat a",
+ "▁ data",
+ "▁g ood",
+ "▁go od",
+ "▁ good",
+ "L E",
+ "] ,",
+ "▁a v",
+ "▁ av",
+ "▁a c",
+ "▁ ac",
+ "id er",
+ "ide r",
+ "i der",
+ "н е",
+ "▁ Q",
+ "▁m in",
+ "▁mi n",
+ "▁ min",
+ "▁m uch",
+ "▁mu ch",
+ "c i",
+ "el s",
+ "e ls",
+ "▁c ur",
+ "▁cu r",
+ "▁ cur",
+ "▁v alue",
+ "▁val ue",
+ "▁ value",
+ "er y",
+ "e ry",
+ "u f",
+ "▁l oc",
+ "▁lo c",
+ "▁ loc",
+ "re ak",
+ "rea k",
+ "at ive",
+ "ati ve",
+ "ativ e",
+ "im es",
+ "ime s",
+ "i mes",
+ "C l",
+ "▁ ,",
+ "▁s er",
+ "▁se r",
+ "▁ ser",
+ "▁d ie",
+ "▁di e",
+ "▁ die",
+ "▁tr ans",
+ "▁tra ns",
+ "▁ trans",
+ "▁res ult",
+ "▁ result",
+ "ex t",
+ "e xt",
+ "▁a ut",
+ "▁au t",
+ "▁ aut",
+ "la nd",
+ "lan d",
+ "l and",
+ "▁& &",
+ "▁ &&",
+ "C h",
+ "te n",
+ "t en",
+ "} $",
+ "▁t ype",
+ "▁typ e",
+ "▁ty pe",
+ "▁ type",
+ "con d",
+ "co nd",
+ "c ond",
+ "ic es",
+ "ice s",
+ "i ces",
+ "▁v ery",
+ "▁ver y",
+ "▁ve ry",
+ "▁ very",
+ "▁o wn",
+ "▁ own",
+ "▁f il",
+ "▁fi l",
+ "▁ fil",
+ "it ies",
+ "iti es",
+ "i ties",
+ "▁p rodu",
+ "▁pro du",
+ "▁prod u",
+ "▁ produ",
+ "▁re ad",
+ "▁r ead",
+ "▁ read",
+ "▁f orm",
+ "▁for m",
+ "▁fo rm",
+ "▁ form",
+ "▁c ase",
+ "▁cas e",
+ "▁ca se",
+ "▁ case",
+ "at her",
+ "ath er",
+ "a ther",
+ "т и",
+ "д а",
+ "е р",
+ "T h",
+ "au t",
+ "a ut",
+ "▁s pec",
+ "▁sp ec",
+ "▁spe c",
+ "▁ spec",
+ "i j",
+ "b l",
+ "il ity",
+ "ili ty",
+ "▁ é",
+ "▁e r",
+ "▁ er",
+ "▁d oes",
+ "▁do es",
+ "▁ does",
+ "▁h ere",
+ "▁he re",
+ "▁her e",
+ "▁ here",
+ "th e",
+ "t he",
+ "ur es",
+ "ure s",
+ "u res",
+ "▁ %",
+ "mi n",
+ "m in",
+ "▁n ull",
+ "▁nu ll",
+ "▁ null",
+ "ra p",
+ "r ap",
+ "\" )",
+ "r r",
+ "Li st",
+ "L ist",
+ "ri ght",
+ "rig ht",
+ "r ight",
+ "▁U ser",
+ "▁Us er",
+ "▁Use r",
+ "▁ User",
+ "U L",
+ "at ional",
+ "ation al",
+ "ati onal",
+ "atio nal",
+ "▁b eing",
+ "▁be ing",
+ "▁bei ng",
+ "▁ being",
+ "A N",
+ "s k",
+ "▁c ar",
+ "▁ca r",
+ "▁ car",
+ "ol e",
+ "o le",
+ "▁d ist",
+ "▁dis t",
+ "▁di st",
+ "▁ dist",
+ "pl ic",
+ "p lic",
+ "ol low",
+ "oll ow",
+ "▁p res",
+ "▁pre s",
+ "▁pr es",
+ "▁ pres",
+ "▁s uch",
+ "▁su ch",
+ "▁suc h",
+ "▁ such",
+ "re am",
+ "rea m",
+ "in ce",
+ "inc e",
+ "ga n",
+ "g an",
+ "▁F or",
+ "▁Fo r",
+ "▁ For",
+ "\" :",
+ "so n",
+ "s on",
+ "riv ate",
+ "▁y ears",
+ "▁year s",
+ "▁ye ars",
+ "▁s erv",
+ "▁se rv",
+ "▁ser v",
+ "▁ serv",
+ "▁m ade",
+ "▁ma de",
+ "▁mad e",
+ "▁ made",
+ "de f",
+ "d ef",
+ "; \r",
+ "▁g l",
+ "▁ gl",
+ "▁b el",
+ "▁be l",
+ "▁ bel",
+ "▁l ist",
+ "▁li st",
+ "▁ list",
+ "▁c or",
+ "▁co r",
+ "▁ cor",
+ "▁d et",
+ "▁de t",
+ "▁ det",
+ "ce ption",
+ "cept ion",
+ "eg in",
+ "e gin",
+ "▁ б",
+ "▁c har",
+ "▁ch ar",
+ "▁cha r",
+ "▁ char",
+ "tr ans",
+ "tra ns",
+ "▁f am",
+ "▁fa m",
+ "▁! =",
+ "▁ !=",
+ "ou se",
+ "ous e",
+ "o use",
+ "▁d ec",
+ "▁de c",
+ "▁ dec",
+ "ic a",
+ "i ca",
+ "▁m any",
+ "▁man y",
+ "▁ma ny",
+ "▁ many",
+ "ak ing",
+ "aki ng",
+ "a king",
+ "▁ à",
+ "▁s im",
+ "▁si m",
+ "▁ sim",
+ "ag es",
+ "age s",
+ "a ges",
+ "uf f",
+ "u ff",
+ "as ed",
+ "ase d",
+ "a sed",
+ "ma n",
+ "m an",
+ "▁S h",
+ "▁ Sh",
+ "ie t",
+ "i et",
+ "ir ect",
+ "ire ct",
+ "i rect",
+ "▁R e",
+ "▁ Re",
+ "▁d iffer",
+ "▁dif fer",
+ "▁diff er",
+ "▁f ind",
+ "▁fin d",
+ "▁fi nd",
+ "▁ find",
+ "eth od",
+ "▁ \r",
+ "in es",
+ "ine s",
+ "i nes",
+ "▁in v",
+ "▁i nv",
+ "▁ inv",
+ "▁p oint",
+ "▁po int",
+ "▁poi nt",
+ "▁ point",
+ "▁The y",
+ "▁Th ey",
+ "▁ They",
+ "▁u sed",
+ "▁us ed",
+ "▁use d",
+ "▁ used",
+ "ct ions",
+ "ction s",
+ "▁st ill",
+ "i ó",
+ "in ed",
+ "ine d",
+ "i ned",
+ "▁wh ile",
+ "▁ while",
+ "I t",
+ "em ber",
+ "emb er",
+ "e mber",
+ "▁s ay",
+ "▁sa y",
+ "▁ say",
+ "▁he lp",
+ "▁hel p",
+ "▁ help",
+ "▁c re",
+ "▁cr e",
+ "▁ cre",
+ "▁ x",
+ "▁T r",
+ "▁ Tr",
+ "um ent",
+ "ume nt",
+ "umen t",
+ "u ment",
+ "▁s k",
+ "▁ sk",
+ "ou ght",
+ "ough t",
+ "ual ly",
+ "u ally",
+ "m essage",
+ "▁C on",
+ "▁Co n",
+ "▁ Con",
+ "▁m on",
+ "▁mo n",
+ "▁ mon",
+ "ar ed",
+ "are d",
+ "a red",
+ "wor k",
+ "w ork",
+ ") :",
+ "is ter",
+ "ist er",
+ "iste r",
+ "i ster",
+ "ar n",
+ "a rn",
+ "iz ed",
+ "ize d",
+ "i zed",
+ "Dat a",
+ "Da ta",
+ "D ata",
+ "or n",
+ "o rn",
+ "▁h ead",
+ "▁he ad",
+ "▁ head",
+ "D E",
+ "▁L e",
+ "▁ Le",
+ "▁p erson",
+ "▁per son",
+ "▁pers on",
+ "▁ person",
+ "ment s",
+ "men ts",
+ "m ents",
+ "eng th",
+ "e ngth",
+ "▁f alse",
+ "▁fal se",
+ "▁fals e",
+ "▁ false",
+ "▁m ed",
+ "▁me d",
+ "▁ med",
+ "▁D e",
+ "▁ De",
+ "ac he",
+ "ach e",
+ "a che",
+ "it ed",
+ "ite d",
+ "i ted",
+ "▁l et",
+ "▁le t",
+ "▁ let",
+ "▁s how",
+ "▁sh ow",
+ "▁ show",
+ "▁s ame",
+ "▁sa me",
+ "▁sam e",
+ "▁ same",
+ "us s",
+ "u ss",
+ "▁g ener",
+ "▁gen er",
+ "▁ge ner",
+ "▁gene r",
+ "▁ gener",
+ "▁ у",
+ "cu r",
+ "c ur",
+ "▁re al",
+ "▁ real",
+ "ce d",
+ "c ed",
+ "\" >",
+ "st ruct",
+ "str uct",
+ "stru ct",
+ "be gin",
+ "b egin",
+ "ce pt",
+ "cep t",
+ "▁b o",
+ "▁ bo",
+ "ir ed",
+ "ire d",
+ "i red",
+ "▁F r",
+ "▁ Fr",
+ "▁st ud",
+ "▁ stud",
+ "de v",
+ "d ev",
+ "A r",
+ "( \\",
+ "▁C l",
+ "▁ Cl",
+ "we en",
+ "w een",
+ "▁t oo",
+ "▁to o",
+ "▁ too",
+ "▁t est",
+ "▁te st",
+ "▁ test",
+ "▁d ay",
+ "▁da y",
+ "▁ day",
+ "o h",
+ "▁f ollow",
+ "▁fol low",
+ "▁ follow",
+ "at ure",
+ "atur e",
+ "atu re",
+ "z e",
+ "ie n",
+ "i en",
+ "re g",
+ "r eg",
+ "ce s",
+ "c es",
+ "ur ing",
+ "uri ng",
+ "u ring",
+ "am b",
+ "a mb",
+ "in a",
+ "i na",
+ "cr i",
+ "c ri",
+ "▁e d",
+ "▁ ed",
+ "S S",
+ "uc k",
+ "u ck",
+ "▁/ *",
+ "▁ /*",
+ "C T",
+ "▁T here",
+ "▁The re",
+ "▁Th ere",
+ "▁Ther e",
+ "▁ There",
+ "▁t ake",
+ "▁tak e",
+ "▁ta ke",
+ "▁ take",
+ "pa r",
+ "p ar",
+ "ul e",
+ "u le",
+ "ca l",
+ "c al",
+ "fo r",
+ "f or",
+ "** **************",
+ "**** ************",
+ "******** ********",
+ "************ ****",
+ "************** **",
+ "s ource",
+ "▁th ose",
+ "co l",
+ "c ol",
+ "▁e ff",
+ "▁ eff",
+ "mo d",
+ "m od",
+ "con t",
+ "co nt",
+ "c ont",
+ "} {",
+ "▁a round",
+ "▁ar ound",
+ "▁ around",
+ "pr ess",
+ "pre ss",
+ "pres s",
+ "p ress",
+ "b y",
+ "▁go ing",
+ "▁ going",
+ "pon se",
+ "pons e",
+ "▁ С",
+ "▁l ine",
+ "▁li ne",
+ "▁lin e",
+ "▁ line",
+ "da te",
+ "dat e",
+ "d ate",
+ "co de",
+ "cod e",
+ "c ode",
+ "[ '",
+ "▁l ife",
+ "▁li fe",
+ "▁lif e",
+ "▁ life",
+ "as on",
+ "a son",
+ "▁u sing",
+ "▁us ing",
+ "▁ using",
+ "▁v al",
+ "▁va l",
+ "▁ val",
+ "▁d u",
+ "▁ du",
+ "y p",
+ "▁O n",
+ "▁ On",
+ "▁f ound",
+ "▁fo und",
+ "▁fou nd",
+ "▁ found",
+ "ol ut",
+ "olu t",
+ "' ]",
+ "ar ent",
+ "are nt",
+ "aren t",
+ "a rent",
+ "▁s tring",
+ "▁st ring",
+ "▁str ing",
+ "▁stri ng",
+ "▁ string",
+ "▁m et",
+ "▁me t",
+ "▁ met",
+ "▁w r",
+ "▁ wr",
+ "us h",
+ "u sh",
+ "st ring",
+ "str ing",
+ "stri ng",
+ "s tring",
+ "si ze",
+ "s ize",
+ "▁v er",
+ "▁ve r",
+ "▁ ver",
+ "▁e ach",
+ "▁ each",
+ "val ue",
+ "v alue",
+ "▁l ast",
+ "▁la st",
+ "▁las t",
+ "▁ last",
+ "▁g ot",
+ "▁go t",
+ "▁ got",
+ "ve n",
+ "v en",
+ "ba ck",
+ "b ack",
+ "Se t",
+ "S et",
+ "e y",
+ "ro l",
+ "r ol",
+ "▁c r",
+ "▁ cr",
+ "th ing",
+ "t hing",
+ "re t",
+ "r et",
+ "é s",
+ "is m",
+ "i sm",
+ "▁bet ween",
+ "▁ between",
+ "O b",
+ "et hing",
+ "eth ing",
+ "e thing",
+ "m p",
+ "▁l o",
+ "▁ lo",
+ "at s",
+ "a ts",
+ "▁N ew",
+ "▁Ne w",
+ "▁ New",
+ "в и",
+ "ad o",
+ "a do",
+ "de x",
+ "d ex",
+ "д и",
+ "▁p ass",
+ "▁pas s",
+ "▁pa ss",
+ "▁ pass",
+ "w h",
+ "▁d en",
+ "▁de n",
+ "▁ den",
+ "Ge t",
+ "G et",
+ "ap t",
+ "a pt",
+ "▁a sk",
+ "▁as k",
+ "▁ ask",
+ "▁s up",
+ "▁su p",
+ "▁ sup",
+ "Val ue",
+ "V alue",
+ "н ы",
+ "▁t ry",
+ "▁tr y",
+ "▁ try",
+ "lat ion",
+ "l ation",
+ "da y",
+ "d ay",
+ "ne ss",
+ "nes s",
+ "n ess",
+ "et s",
+ "e ts",
+ "▁ex per",
+ "▁exp er",
+ "▁ exper",
+ "T r",
+ "▁M ar",
+ "▁Ma r",
+ "▁ Mar",
+ "se rv",
+ "ser v",
+ "s erv",
+ "b r",
+ "▁n umber",
+ "▁num ber",
+ "▁nu mber",
+ "▁ number",
+ "in al",
+ "ina l",
+ "i nal",
+ "ce nt",
+ "cen t",
+ "c ent",
+ "/ *",
+ "no t",
+ "n ot",
+ "ion al",
+ "io nal",
+ "iona l",
+ "i onal",
+ "▁f inal",
+ "▁fin al",
+ "▁fi nal",
+ "▁ final",
+ "' )",
+ "▁r un",
+ "▁ru n",
+ "▁ run",
+ "ov er",
+ "ove r",
+ "o ver",
+ "▁n ever",
+ "▁ne ver",
+ "▁ never",
+ "u c",
+ "▁h igh",
+ "▁hig h",
+ "▁hi gh",
+ "▁ high",
+ "yl e",
+ "y le",
+ "▁in s",
+ "▁i ns",
+ "▁ ins",
+ "▁b est",
+ "▁be st",
+ "▁bes t",
+ "▁ best",
+ "it tle",
+ "itt le",
+ "ri c",
+ "r ic",
+ "▁s ign",
+ "▁si gn",
+ "▁sig n",
+ "▁ sign",
+ "▁d em",
+ "▁de m",
+ "▁ dem",
+ "in ess",
+ "ine ss",
+ "ines s",
+ "i ness",
+ "g y",
+ "▁w ar",
+ "▁wa r",
+ "▁ war",
+ "is hed",
+ "ish ed",
+ "▁g iv",
+ "▁gi v",
+ "ke y",
+ "k ey",
+ "▁ X",
+ "( $",
+ "▁ch ild",
+ "▁chi ld",
+ "▁ child",
+ "le ss",
+ "les s",
+ "l ess",
+ "way s",
+ "wa ys",
+ "w ays",
+ "in cl",
+ "inc l",
+ "ro p",
+ "r op",
+ "ra w",
+ "r aw",
+ ": //",
+ "▁ «",
+ "n o",
+ "ind ow",
+ "indo w",
+ "f e",
+ "ri end",
+ "rie nd",
+ "rien d",
+ "▁l es",
+ "▁le s",
+ "▁ les",
+ "▁l os",
+ "▁lo s",
+ "▁ los",
+ "fil e",
+ "fi le",
+ "f ile",
+ "form ation",
+ "format ion",
+ "cc ess",
+ "c cess",
+ "▁ В",
+ "n a",
+ "▁i l",
+ "▁ il",
+ "is ion",
+ "isi on",
+ "le r",
+ "l er",
+ "▁a rt",
+ "▁ar t",
+ "▁ art",
+ "Con t",
+ "Co nt",
+ "C ont",
+ "▁w orld",
+ "▁wor ld",
+ "▁ world",
+ "▁t urn",
+ "▁tu rn",
+ "▁tur n",
+ "▁ turn",
+ "▁re ally",
+ "▁real ly",
+ "▁E x",
+ "▁ Ex",
+ "м а",
+ "▁ П",
+ "ter s",
+ "te rs",
+ "t ers",
+ "ar get",
+ "arg et",
+ "arge t",
+ "Er r",
+ "E rr",
+ "▁h app",
+ "▁ha pp",
+ "ti me",
+ "tim e",
+ "t ime",
+ "▁S o",
+ "▁ So",
+ "di v",
+ "d iv",
+ "▁did n",
+ "▁di dn",
+ "ad a",
+ "a da",
+ "oo t",
+ "o ot",
+ "} )",
+ "▁s ch",
+ "▁sc h",
+ "▁ sch",
+ "▁c le",
+ "▁cl e",
+ "▁ cle",
+ "▁some thing",
+ "▁som ething",
+ "▁somet hing",
+ "▁ something",
+ "() .",
+ "( ).",
+ "▁c our",
+ "▁co ur",
+ "▁cou r",
+ "ev er",
+ "eve r",
+ "e ver",
+ "an ts",
+ "ant s",
+ "▁ ?",
+ "T o",
+ "▁ `",
+ "tr y",
+ "t ry",
+ "u x",
+ "ai s",
+ "a is",
+ "ro ss",
+ "ros s",
+ "r oss",
+ "hi p",
+ "h ip",
+ "▁re p",
+ "▁r ep",
+ "▁ rep",
+ "la bel",
+ "lab el",
+ "l abel",
+ "▁b oth",
+ "▁bo th",
+ "▁bot h",
+ "▁ both",
+ "* ,",
+ "ot t",
+ "o tt",
+ "м и",
+ "an e",
+ "a ne",
+ "▁o pen",
+ "▁op en",
+ "▁ open",
+ "w w",
+ "▁c ome",
+ "▁com e",
+ "▁co me",
+ "▁ come",
+ "▁e xt",
+ "▁ex t",
+ "▁ ext",
+ "re m",
+ "r em",
+ "_{ \\",
+ "_ {\\",
+ "▁o ld",
+ "▁ol d",
+ "▁ old",
+ "ch ed",
+ "che d",
+ "c hed",
+ ". _",
+ "M E",
+ "if y",
+ "i fy",
+ "g g",
+ "Co l",
+ "C ol",
+ "vi ew",
+ "v iew",
+ "▁b us",
+ "▁bu s",
+ "▁ bus",
+ "▁m ust",
+ "▁mus t",
+ "▁mu st",
+ "▁ must",
+ "▁d ifferent",
+ "▁differ ent",
+ "lo g",
+ "l og",
+ "is ts",
+ "ist s",
+ "i sts",
+ "ro ll",
+ "rol l",
+ "r oll",
+ "a i",
+ "▁з а",
+ "▁ за",
+ "▁s ystem",
+ "▁sys tem",
+ "▁syst em",
+ "▁ system",
+ "iv ers",
+ "ive rs",
+ "iver s",
+ "i vers",
+ "at us",
+ "atu s",
+ "ot e",
+ "o te",
+ "me d",
+ "m ed",
+ "] .",
+ "ak es",
+ "ake s",
+ "a kes",
+ "R O",
+ "▁c ent",
+ "▁ce nt",
+ "▁ cent",
+ "gr am",
+ "gra m",
+ "g ram",
+ "▁p rivate",
+ "▁priv ate",
+ "▁ private",
+ "▁g reat",
+ "▁gre at",
+ "\" ;",
+ "op y",
+ "o py",
+ "▁fe el",
+ "▁fee l",
+ "▁H ow",
+ "▁Ho w",
+ "▁ How",
+ "// //",
+ "/// /",
+ "/ ///",
+ "I C",
+ "▁d r",
+ "▁ dr",
+ "ain s",
+ "ai ns",
+ "a ins",
+ "lo ck",
+ "loc k",
+ "l ock",
+ "E n",
+ "▁S ch",
+ "▁Sc h",
+ "▁ Sch",
+ "▁m at",
+ "▁ma t",
+ "▁ mat",
+ "▁h ome",
+ "▁hom e",
+ "▁ho me",
+ "▁ home",
+ "per ty",
+ "pert y",
+ "te st",
+ "tes t",
+ "t est",
+ "lo c",
+ "l oc",
+ "▁w om",
+ "▁wo m",
+ "s w",
+ "ar ly",
+ "arl y",
+ "▁E n",
+ "▁ En",
+ "▁к о",
+ "▁ ко",
+ "de n",
+ "d en",
+ "ст а",
+ "с та",
+ "▁ а",
+ "et er",
+ "ete r",
+ "e ter",
+ "▁incl ud",
+ "▁inclu d",
+ "UL L",
+ "U LL",
+ "▁m em",
+ "▁me m",
+ "▁ mem",
+ "▁p o",
+ "▁ po",
+ "▁l ittle",
+ "▁lit tle",
+ "▁litt le",
+ "▁a rg",
+ "▁ar g",
+ "▁ arg",
+ "▁} ,",
+ "▁ },",
+ "in clude",
+ "incl ude",
+ "et a",
+ "e ta",
+ "▁p lace",
+ "▁pl ace",
+ "▁plac e",
+ "▁ place",
+ "id th",
+ "us tom",
+ "ust om",
+ "▁| |",
+ "▁ ||",
+ "▁t em",
+ "▁te m",
+ "▁ tem",
+ "ri ed",
+ "rie d",
+ "r ied",
+ "▁f act",
+ "▁fac t",
+ "▁fa ct",
+ "▁ fact",
+ "ien ce",
+ "i ence",
+ "▁P l",
+ "▁ Pl",
+ "op t",
+ "o pt",
+ "el e",
+ "e le",
+ "g o",
+ "A C",
+ "in ter",
+ "int er",
+ "inte r",
+ "==== ====",
+ "() ,",
+ "( ),",
+ "ot s",
+ "o ts",
+ "ra l",
+ "r al",
+ "iqu e",
+ "iq ue",
+ "i que",
+ "av ing",
+ "avi ng",
+ "a ving",
+ "m l",
+ "▁th ought",
+ "▁though t",
+ "▁thou ght",
+ "fr ac",
+ "f rac",
+ "▁c are",
+ "▁car e",
+ "▁ca re",
+ "▁ care",
+ "() );",
+ "()) ;",
+ "( ));",
+ "▁p ut",
+ "▁pu t",
+ "▁ put",
+ "▁m ight",
+ "▁mi ght",
+ "▁mig ht",
+ "▁A mer",
+ "▁Am er",
+ "▁ Amer",
+ "▁( !",
+ "▁ (!",
+ "am ple",
+ "amp le",
+ "al th",
+ "alt h",
+ "▁f ew",
+ "▁fe w",
+ "▁st ate",
+ "▁stat e",
+ "▁sta te",
+ "▁ state",
+ "su b",
+ "s ub",
+ "▁O r",
+ "▁ Or",
+ "] ;",
+ "▁s ize",
+ "▁si ze",
+ "▁ size",
+ "▁S p",
+ "▁ Sp",
+ "▁with out",
+ "▁ without",
+ "▁p oss",
+ "▁pos s",
+ "▁po ss",
+ "▁ poss",
+ "e q",
+ "pl ay",
+ "p lay",
+ "▁ex pect",
+ "▁exp ect",
+ "▁ expect",
+ "▁se cond",
+ "▁sec ond",
+ "▁ second",
+ "▁S tring",
+ "▁St ring",
+ "▁Str ing",
+ "▁ String",
+ "ui ld",
+ "u ild",
+ "▁n ext",
+ "▁ne xt",
+ "▁ next",
+ "+ +",
+ "re qu",
+ "req u",
+ "r equ",
+ "▁A ll",
+ "▁Al l",
+ "▁ All",
+ "▁m en",
+ "▁me n",
+ "▁ men",
+ "▁W hen",
+ "▁Wh en",
+ "▁Whe n",
+ "▁ When",
+ "it er",
+ "ite r",
+ "i ter",
+ "am ent",
+ "ame nt",
+ "amen t",
+ "a ment",
+ "ne t",
+ "n et",
+ "▁ К",
+ "ro n",
+ "r on",
+ "ain t",
+ "ai nt",
+ "a int",
+ "▁I s",
+ "▁ Is",
+ "в е",
+ "pe nd",
+ "pen d",
+ "p end",
+ "trans lation",
+ "transl ation",
+ "▁г о",
+ "▁ го",
+ "ч е",
+ "▁v an",
+ "▁va n",
+ "▁ van",
+ "▁an other",
+ "▁ano ther",
+ "▁re t",
+ "▁r et",
+ "▁ ret",
+ "▁L a",
+ "▁ La",
+ "Mo d",
+ "M od",
+ "IO N",
+ "I ON",
+ "li st",
+ "l ist",
+ "▁p ost",
+ "▁pos t",
+ "▁po st",
+ "▁ post",
+ "d a",
+ "wa re",
+ "war e",
+ "w are",
+ "▁w ord",
+ "▁wor d",
+ "▁wo rd",
+ "▁ word",
+ "Err or",
+ "Er ror",
+ "▁se em",
+ "▁see m",
+ "▁cont in",
+ "▁ contin",
+ "at ic",
+ "ati c",
+ "▁th ree",
+ "▁thr ee",
+ "▁ three",
+ "Ob ject",
+ "Obj ect",
+ "▁part ic",
+ "▁parti c",
+ "$ .",
+ "▁m ark",
+ "▁mar k",
+ "▁ mark",
+ "▁v is",
+ "▁vi s",
+ "▁ vis",
+ "r c",
+ "▁s w",
+ "▁ sw",
+ "pt ions",
+ "ption s",
+ "▁b reak",
+ "▁bre ak",
+ "▁ break",
+ "▁th ings",
+ "▁thing s",
+ "▁thin gs",
+ "ut e",
+ "u te",
+ "u i",
+ "▁T hat",
+ "▁Th at",
+ "▁ That",
+ "ur s",
+ "u rs",
+ "g l",
+ "р у",
+ "▁f ile",
+ "▁fil e",
+ "▁fi le",
+ "▁ file",
+ "us e",
+ "u se",
+ "ig ned",
+ "ign ed",
+ "igne d",
+ "par t",
+ "pa rt",
+ "p art",
+ "U n",
+ "▁e qu",
+ "▁eq u",
+ "▁ equ",
+ "( &",
+ "▁l ead",
+ "▁le ad",
+ "r m",
+ "ain ed",
+ "ai ned",
+ "aine d",
+ "a ined",
+ "▁B e",
+ "▁ Be",
+ "pat h",
+ "pa th",
+ "p ath",
+ "▁sm all",
+ "▁ small",
+ "ag er",
+ "age r",
+ "a ger",
+ "▁al ways",
+ "▁ always",
+ "▁E l",
+ "▁ El",
+ "▁or der",
+ "▁ord er",
+ "▁ order",
+ "▁e y",
+ "▁ ey",
+ "▁w on",
+ "▁wo n",
+ "▁ won",
+ "ap e",
+ "a pe",
+ "▁l eft",
+ "▁le ft",
+ "▁ left",
+ "av a",
+ "a va",
+ "it em",
+ "ite m",
+ "i tem",
+ "ho r",
+ "h or",
+ "▁a way",
+ "▁aw ay",
+ "▁ away",
+ "b b",
+ "fu n",
+ "f un",
+ "▁I nd",
+ "▁In d",
+ "▁ Ind",
+ "m b",
+ "▁st ruct",
+ "▁str uct",
+ "▁stru ct",
+ "▁ struct",
+ "▁pro cess",
+ "▁proc ess",
+ "▁proces s",
+ "▁ process",
+ "▁s upport",
+ "▁sup port",
+ "▁supp ort",
+ "▁ support",
+ "); \r",
+ ") ;\r",
+ "ió n",
+ "i ón",
+ "L O",
+ "▁o per",
+ "▁op er",
+ "▁ oper",
+ "U T",
+ "▁ ·",
+ "P E",
+ "lo ad",
+ "l oad",
+ "of f",
+ "o ff",
+ "▁N o",
+ "▁ No",
+ "iv es",
+ "ive s",
+ "i ves",
+ "ic an",
+ "ica n",
+ "i can",
+ "▁v e",
+ "▁ ve",
+ "act ion",
+ "a ction",
+ "' ;",
+ "▁v o",
+ "▁ vo",
+ "$ ,",
+ "▁G r",
+ "▁ Gr",
+ "pr e",
+ "p re",
+ "n y",
+ "ain ing",
+ "ai ning",
+ "a ining",
+ "io r",
+ "i or",
+ "in it",
+ "ini t",
+ "i nit",
+ "le ction",
+ "lect ion",
+ "l ection",
+ "ar m",
+ "a rm",
+ "um n",
+ "u mn",
+ "ag s",
+ "a gs",
+ "ц и",
+ "ск о",
+ "с ко",
+ "vers ion",
+ "v ersion",
+ "▁T o",
+ "▁ To",
+ "▁re f",
+ "▁r ef",
+ "▁ ref",
+ "st and",
+ "sta nd",
+ "stan d",
+ "▁A t",
+ "▁ At",
+ "if t",
+ "i ft",
+ "▁e in",
+ "fa ce",
+ "fac e",
+ "f ace",
+ "b o",
+ "if ied",
+ "ifi ed",
+ "ve d",
+ "v ed",
+ "su m",
+ "s um",
+ "un e",
+ "u ne",
+ "it al",
+ "ita l",
+ "i tal",
+ "um p",
+ "u mp",
+ "com m",
+ "co mm",
+ "c omm",
+ "▁m ov",
+ "▁mo v",
+ "▁ mov",
+ "el t",
+ "e lt",
+ "▁v on",
+ "▁vo n",
+ "vel op",
+ "ct or",
+ "c tor",
+ "he ad",
+ "h ead",
+ "cl e",
+ "c le",
+ "▁b uild",
+ "▁bu ild",
+ "▁ build",
+ "in c",
+ "i nc",
+ ". '",
+ "b s",
+ "in fo",
+ "inf o",
+ "ch n",
+ "c hn",
+ "▁we ek",
+ "▁ week",
+ "▁b ook",
+ "▁bo ok",
+ "▁ book",
+ "H E",
+ "ba r",
+ "b ar",
+ "ic ense",
+ "▁W hat",
+ "▁Wh at",
+ "▁ What",
+ "▁qu est",
+ "▁que st",
+ "▁q uest",
+ "▁ quest",
+ "ur ch",
+ "at o",
+ "a to",
+ "le ft",
+ "l eft",
+ "▁m ar",
+ "▁ma r",
+ "▁ mar",
+ "▁t op",
+ "▁to p",
+ "▁ top",
+ "F F",
+ "▁f riend",
+ "▁ friend",
+ "▁b eh",
+ "▁be h",
+ "▁f ield",
+ "▁fi eld",
+ "▁ field",
+ "▁again st",
+ "ra ct",
+ "rac t",
+ "r act",
+ "iz ation",
+ "us er",
+ "use r",
+ "u ser",
+ "ch en",
+ "che n",
+ "c hen",
+ "▁ke ep",
+ "▁ keep",
+ "A D",
+ "it or",
+ "ito r",
+ "i tor",
+ "▁n on",
+ "▁no n",
+ "▁ non",
+ "ir d",
+ "i rd",
+ "op e",
+ "o pe",
+ "▁re st",
+ "▁r est",
+ "▁res t",
+ "▁ rest",
+ "▁d ev",
+ "▁de v",
+ "▁ dev",
+ "▁_ _",
+ "▁ __",
+ "▁u na",
+ "▁un a",
+ "▁ una",
+ "▁t erm",
+ "▁te rm",
+ "▁ter m",
+ "▁ term",
+ "I S",
+ "▁p op",
+ "▁po p",
+ "▁ pop",
+ "ri st",
+ "ris t",
+ "r ist",
+ "▁s ince",
+ "▁sin ce",
+ "▁sinc e",
+ "▁ since",
+ "ve s",
+ "v es",
+ "▁h ard",
+ "▁ha rd",
+ "▁har d",
+ "▁ hard",
+ "p i",
+ "ut il",
+ "uti l",
+ "u til",
+ "▁s oc",
+ "▁so c",
+ "▁ soc",
+ "en e",
+ "e ne",
+ "Ex ception",
+ "▁l ocal",
+ "▁loc al",
+ "▁lo cal",
+ "▁ local",
+ "▁d irect",
+ "▁di rect",
+ "▁dire ct",
+ "▁dir ect",
+ "▁ direct",
+ "▁s ure",
+ "▁su re",
+ "▁sur e",
+ "▁ sure",
+ "▁b ro",
+ "▁br o",
+ "▁ bro",
+ "▁d a",
+ "▁ da",
+ "▁< /",
+ "▁ ",
+ "▁cur rent",
+ "▁curr ent",
+ "▁ current",
+ "' :",
+ "W h",
+ "▁in formation",
+ "▁inform ation",
+ "▁ information",
+ "▁i de",
+ "▁id e",
+ "▁ ide",
+ "▁bet ter",
+ "Te xt",
+ "Tex t",
+ "T ext",
+ "ra ph",
+ "rap h",
+ "r aph",
+ "▁st and",
+ "▁stan d",
+ "▁sta nd",
+ "▁ stand",
+ "▁c heck",
+ "▁che ck",
+ "▁ check",
+ "▁ к",
+ "▁n a",
+ "▁ na",
+ "( (",
+ "ou th",
+ "out h",
+ "o uth",
+ "ap s",
+ "a ps",
+ "▁u nt",
+ "▁un t",
+ "▁ unt",
+ "b f",
+ "▁con f",
+ "▁co nf",
+ "▁ conf",
+ "▁s pe",
+ "▁sp e",
+ "▁ spe",
+ "it le",
+ "i tle",
+ "▁C ol",
+ "▁Co l",
+ "▁ Col",
+ "cl ass",
+ "c lass",
+ "ur al",
+ "ura l",
+ "u ral",
+ "ber s",
+ "be rs",
+ "b ers",
+ "M A",
+ "ess ion",
+ "▁ М",
+ "In fo",
+ "Inf o",
+ "▁B r",
+ "▁ Br",
+ "▁e as",
+ "erv ice",
+ "au s",
+ "a us",
+ "ar i",
+ "a ri",
+ "п о",
+ "▁c oun",
+ "▁co un",
+ "▁cou n",
+ "д е",
+ "() )",
+ "( ))",
+ "li ng",
+ "lin g",
+ "l ing",
+ "E D",
+ "ab ly",
+ "abl y",
+ "▁p at",
+ "▁pa t",
+ "▁ pat",
+ "or g",
+ "o rg",
+ "▁i d",
+ "▁ id",
+ "▁ г",
+ "▁t ell",
+ "▁te ll",
+ "▁tel l",
+ "le x",
+ "l ex",
+ "▁al low",
+ "▁all ow",
+ "▁ allow",
+ "re en",
+ "ree n",
+ "r een",
+ "m y",
+ "▁cons ider",
+ "▁consid er",
+ "▁te am",
+ "▁tea m",
+ "▁ team",
+ "le ase",
+ "ht t",
+ "h tt",
+ "▁P r",
+ "▁ Pr",
+ "/* *",
+ "/ **",
+ "▁s ing",
+ "▁si ng",
+ "▁sin g",
+ "▁ sing",
+ "Re qu",
+ "Req u",
+ "R equ",
+ "R e",
+ "id es",
+ "ide s",
+ "i des",
+ "ch es",
+ "che s",
+ "c hes",
+ "▁ob ject",
+ "▁obj ect",
+ "▁ object",
+ "ial ly",
+ "i ally",
+ "B y",
+ "с я",
+ "id ed",
+ "ide d",
+ "i ded",
+ "▁f ree",
+ "▁fr ee",
+ "▁fre e",
+ "▁ free",
+ "▁pro ble",
+ "▁prob le",
+ "ci te",
+ "cit e",
+ "c ite",
+ "▁) ;",
+ "▁ );",
+ "iss ion",
+ "▁d uring",
+ "▁du ring",
+ "▁dur ing",
+ "▁- -",
+ "▁ --",
+ "it her",
+ "ith er",
+ "i ther",
+ "л я",
+ "▁l eg",
+ "▁le g",
+ "▁ leg",
+ "▁s it",
+ "▁si t",
+ "ic ally",
+ "ical ly",
+ "▁k ey",
+ "▁ke y",
+ "▁ key",
+ "le g",
+ "l eg",
+ "tr a",
+ "t ra",
+ "▁m om",
+ "▁mo m",
+ "▁ex pl",
+ "▁exp l",
+ "▁ expl",
+ "▁de velop",
+ "▁ develop",
+ "▁e vent",
+ "▁ev ent",
+ "▁even t",
+ "▁ event",
+ "▁N ULL",
+ "▁ NULL",
+ "oh n",
+ "o hn",
+ "▁// /",
+ "▁/ //",
+ "▁ ///",
+ "▁bus iness",
+ "▁ business",
+ "ч а",
+ "▁pro f",
+ "▁pr of",
+ "▁ prof",
+ "er ror",
+ "err or",
+ "▁p or",
+ "▁po r",
+ "▁ por",
+ "▁com mun",
+ "▁comm un",
+ "▁ commun",
+ "In d",
+ "I nd",
+ "iu m",
+ "i um",
+ "Te st",
+ "T est",
+ "▁A d",
+ "▁ Ad",
+ "ou ble",
+ "▁s on",
+ "▁so n",
+ "▁ son",
+ "ri te",
+ "rit e",
+ "r ite",
+ "re ady",
+ "read y",
+ "rea dy",
+ "▁{ \r",
+ "▁ {\r",
+ "▁t hing",
+ "▁th ing",
+ "▁thin g",
+ "▁ thing",
+ "н я",
+ "▁P h",
+ "▁ Ph",
+ "pe d",
+ "p ed",
+ "с ь",
+ "iv ed",
+ "ive d",
+ "i ved",
+ "Y ou",
+ "ar l",
+ "a rl",
+ "con st",
+ "cons t",
+ ".. /",
+ ". ./",
+ "S e",
+ "S h",
+ "▁p ower",
+ "▁po wer",
+ "▁pow er",
+ "▁ power",
+ "rib ute",
+ "ribut e",
+ "ribu te",
+ "▁M y",
+ "▁ My",
+ "▁t alk",
+ "▁tal k",
+ "▁ talk",
+ "it ch",
+ "▁c alled",
+ "▁call ed",
+ "▁cal led",
+ "▁ called",
+ "▁c ame",
+ "▁cam e",
+ "▁ca me",
+ "▁be lie",
+ "▁bel ie",
+ "U R",
+ "Ad d",
+ "A dd",
+ "▁R es",
+ "▁Re s",
+ "▁ Res",
+ "as ter",
+ "ast er",
+ "aste r",
+ "a ster",
+ "el la",
+ "ell a",
+ "e lla",
+ "ob al",
+ "oba l",
+ "o bal",
+ "▁u ntil",
+ "▁un til",
+ "▁unt il",
+ "▁ until",
+ "▁h um",
+ "▁ hum",
+ "C O",
+ "at ely",
+ "ate ly",
+ "atel y",
+ "## ##",
+ "### #",
+ "# ###",
+ "pu blic",
+ "pub lic",
+ "p ublic",
+ "[ ]",
+ "▁r oom",
+ "▁ro om",
+ "▁ room",
+ "le n",
+ "l en",
+ "▁f amily",
+ "▁fam ily",
+ "▁famil y",
+ "▁ family",
+ "po r",
+ "p or",
+ "▁pro gram",
+ "▁pr ogram",
+ "▁ program",
+ "▁h ist",
+ "▁his t",
+ "▁hi st",
+ "▁ hist",
+ "▁m us",
+ "▁mu s",
+ "▁ mus",
+ "ar ge",
+ "arg e",
+ "on ey",
+ "one y",
+ "o ney",
+ "I m",
+ "el se",
+ "els e",
+ "ail s",
+ "ai ls",
+ "a ils",
+ "a f",
+ "▁l ove",
+ "▁lo ve",
+ "▁lov e",
+ "▁ love",
+ "ä r",
+ "as es",
+ "ase s",
+ "a ses",
+ "ph a",
+ "p ha",
+ "ou rs",
+ "our s",
+ "o urs",
+ "di s",
+ "d is",
+ "ma p",
+ "m ap",
+ "iv er",
+ "ive r",
+ "i ver",
+ "ö r",
+ "▁B l",
+ "▁ Bl",
+ "at eg",
+ "ate g",
+ "st ate",
+ "stat e",
+ "sta te",
+ "St ate",
+ "Stat e",
+ "er tain",
+ "ert ain",
+ "erta in",
+ "▁e ffect",
+ "▁eff ect",
+ "▁ effect",
+ "pr int",
+ "▁b ig",
+ "▁bi g",
+ "▁ big",
+ "in dex",
+ "ind ex",
+ "inde x",
+ "▁p ub",
+ "▁pu b",
+ "▁ pub",
+ "ve rt",
+ "ver t",
+ "v ert",
+ "er o",
+ "e ro",
+ "m d",
+ "▁m ethod",
+ "▁meth od",
+ "▁ method",
+ "▁g ame",
+ "▁gam e",
+ "▁ga me",
+ "▁ game",
+ "ri es",
+ "rie s",
+ "r ies",
+ "le te",
+ "let e",
+ "l ete",
+ "It em",
+ "I tem",
+ "IN G",
+ "I NG",
+ "re sent",
+ "res ent",
+ "al ity",
+ "ali ty",
+ "pt y",
+ "p ty",
+ "le y",
+ "l ey",
+ "oc ument",
+ "▁b eg",
+ "▁be g",
+ "T R",
+ "} .",
+ "▁sch ool",
+ "▁ school",
+ "he s",
+ "h es",
+ "д о",
+ "▁l ot",
+ "▁lo t",
+ "▁ lot",
+ "▁t ook",
+ "▁to ok",
+ "▁too k",
+ "▁a dv",
+ "▁ad v",
+ "▁ adv",
+ "▁c ap",
+ "▁ca p",
+ "▁ cap",
+ "M P",
+ "un k",
+ "▁l ight",
+ "▁li ght",
+ "▁lig ht",
+ "▁ light",
+ "▁l ater",
+ "▁la ter",
+ "▁late r",
+ "▁lat er",
+ ". ,",
+ "Ke y",
+ "K ey",
+ "it ions",
+ "ition s",
+ "iti ons",
+ "▁en ough",
+ "▁/ **",
+ "▁/* *",
+ "▁ /**",
+ "▁w ent",
+ "▁we nt",
+ "▁wen t",
+ "ã o",
+ "▁th ough",
+ "▁thou gh",
+ "▁ though",
+ "▁g roup",
+ "▁gr oup",
+ "▁gro up",
+ "▁ group",
+ "▁me an",
+ "▁ mean",
+ "ск и",
+ "с ки",
+ "A P",
+ "▁n um",
+ "▁nu m",
+ "▁ num",
+ "▁c ond",
+ "▁con d",
+ "▁co nd",
+ "▁ cond",
+ "н і",
+ "▁g iven",
+ "▁giv en",
+ "▁give n",
+ "▁gi ven",
+ "▁w hy",
+ "▁wh y",
+ "▁ why",
+ "▁re ce",
+ "▁rec e",
+ "▁s ide",
+ "▁si de",
+ "▁sid e",
+ "▁ side",
+ "▁f ar",
+ "▁fa r",
+ "▁ far",
+ "Con text",
+ "Cont ext",
+ "м е",
+ "▁l og",
+ "▁lo g",
+ "▁ log",
+ "Vi ew",
+ "V iew",
+ "▁< <",
+ "▁ <<",
+ "fi l",
+ "f il",
+ "ac es",
+ "ace s",
+ "a ces",
+ "en cy",
+ "enc y",
+ "oa d",
+ "o ad",
+ "er ed",
+ "ere d",
+ "e red",
+ "▁pro duct",
+ "▁produ ct",
+ "▁prod uct",
+ "▁ product",
+ "E T",
+ "▁p aram",
+ "▁par am",
+ "▁para m",
+ "▁pa ram",
+ "▁ param",
+ "▁p rote",
+ "▁pro te",
+ "▁pr ote",
+ "▁prot e",
+ "▁ prote",
+ "te s",
+ "t es",
+ "Tim e",
+ "T ime",
+ "j e",
+ "ol ution",
+ "olut ion",
+ "▁р а",
+ "▁ ра",
+ "▁mon th",
+ "▁mont h",
+ "▁ month",
+ "fer ence",
+ "fe rence",
+ "▁a ppe",
+ "▁app e",
+ "▁ap pe",
+ "▁ appe",
+ "▁f ace",
+ "▁fac e",
+ "▁fa ce",
+ "▁ face",
+ "en ed",
+ "ene d",
+ "e ned",
+ "tr act",
+ "tra ct",
+ "t ract",
+ "▁l ess",
+ "▁le ss",
+ "▁les s",
+ "▁ less",
+ "A S",
+ "é e",
+ "▁g ive",
+ "▁giv e",
+ "▁gi ve",
+ "▁k ind",
+ "▁ki nd",
+ "▁kin d",
+ "▁ kind",
+ "▁c ount",
+ "▁co unt",
+ "▁coun t",
+ "▁cou nt",
+ "▁ count",
+ "co unt",
+ "cou nt",
+ "c ount",
+ "▁s top",
+ "▁st op",
+ "▁sto p",
+ "▁ stop",
+ "▁g over",
+ "▁go ver",
+ "k a",
+ "▁err or",
+ "▁er ror",
+ "▁ error",
+ "en ces",
+ "ence s",
+ "enc es",
+ "▁m il",
+ "▁mi l",
+ "▁ mil",
+ "al f",
+ "yn c",
+ "y nc",
+ "vi ous",
+ "v ious",
+ "h o",
+ "▁n ight",
+ "▁ni ght",
+ "▁ night",
+ "er a",
+ "e ra",
+ "▁п ро",
+ "▁пр о",
+ "▁ про",
+ "▁s ol",
+ "▁so l",
+ "▁ sol",
+ "me n",
+ "m en",
+ "▁w ater",
+ "▁wat er",
+ "▁wa ter",
+ "▁ water",
+ "er ing",
+ "eri ng",
+ "e ring",
+ "▁l im",
+ "▁li m",
+ "▁ lim",
+ "Par am",
+ "P aram",
+ "▁h ouse",
+ "▁hous e",
+ "▁ho use",
+ "▁ house",
+ "▁S ystem",
+ "▁ System",
+ "▁p ay",
+ "▁pa y",
+ "▁ pay",
+ "▁: =",
+ "ur o",
+ "u ro",
+ "oc i",
+ "o ci",
+ "z y",
+ "▁al ready",
+ ", \\",
+ "le ngth",
+ "l ength",
+ "▁s i",
+ "▁ si",
+ "▁inter est",
+ "▁inte rest",
+ "▁ interest",
+ "af f",
+ "a ff",
+ "ct ed",
+ "c ted",
+ "ent ion",
+ "enti on",
+ "▁д о",
+ "▁ до",
+ "um e",
+ "u me",
+ "▁app ro",
+ "▁ap pro",
+ "▁ appro",
+ "br e",
+ "b re",
+ "I G",
+ "▁th row",
+ "▁thr ow",
+ "▁thro w",
+ "▁ throw",
+ "math cal",
+ "ir l",
+ "i rl",
+ "▁p rom",
+ "▁pro m",
+ "▁pr om",
+ "▁ prom",
+ "os s",
+ "o ss",
+ "▁re quest",
+ "▁requ est",
+ "▁req uest",
+ "▁ request",
+ "equ ation",
+ "eq uation",
+ "ol ogy",
+ "olog y",
+ "olo gy",
+ "mi t",
+ "m it",
+ "▁p ack",
+ "▁pa ck",
+ "▁pac k",
+ "▁ pack",
+ "in o",
+ "i no",
+ "ar ray",
+ "arr ay",
+ "z a",
+ "ti l",
+ "t il",
+ "U N",
+ "▁p resent",
+ "▁pre sent",
+ "▁pres ent",
+ "▁ present",
+ "▁or gan",
+ "▁org an",
+ "▁ organ",
+ "Fil e",
+ "Fi le",
+ "F ile",
+ "▁o rig",
+ "▁or ig",
+ "▁ orig",
+ "▁f ull",
+ "▁ful l",
+ "▁fu ll",
+ "▁ full",
+ "is tr",
+ "ist r",
+ "i str",
+ "▁f lo",
+ "▁fl o",
+ "h r",
+ "▁as sert",
+ "▁ass ert",
+ "▁ assert",
+ "ar ds",
+ "ard s",
+ "ur l",
+ "u rl",
+ "en n",
+ "e nn",
+ "s l",
+ "▁ А",
+ "▁c ho",
+ "▁ch o",
+ "▁ cho",
+ "▁l evel",
+ "▁le vel",
+ "▁lev el",
+ "▁ level",
+ "O T",
+ "wo rd",
+ "wor d",
+ "w ord",
+ "▁b ody",
+ "▁bo dy",
+ "▁bod y",
+ "▁ body",
+ "▁u ser",
+ "▁us er",
+ "▁use r",
+ "▁ user",
+ "í a",
+ "Q u",
+ "▁m ain",
+ "▁ma in",
+ "▁mai n",
+ "▁ main",
+ "A B",
+ "pl oy",
+ "plo y",
+ "Ev ent",
+ "Even t",
+ "E vent",
+ "▁s uper",
+ "▁su per",
+ "▁sup er",
+ "▁ super",
+ "ok en",
+ "oke n",
+ "o ken",
+ "▁ Н",
+ "A s",
+ "th ers",
+ "ther s",
+ "the rs",
+ "м о",
+ "к у",
+ "▁d ays",
+ "▁day s",
+ "▁da ys",
+ "▁ days",
+ "▁d one",
+ "▁do ne",
+ "▁don e",
+ "▁ done",
+ "▁v iew",
+ "▁vi ew",
+ "▁vie w",
+ "▁ view",
+ "si de",
+ "sid e",
+ "s ide",
+ "с и",
+ "') ;",
+ "' );",
+ "▁v ol",
+ "▁vo l",
+ "▁ vol",
+ "▁t ot",
+ "▁to t",
+ "▁ tot",
+ "ca se",
+ "cas e",
+ "c ase",
+ "▁a ff",
+ "▁af f",
+ "▁ aff",
+ "Requ est",
+ "Re quest",
+ "Req uest",
+ "▁M an",
+ "▁Ma n",
+ "▁ Man",
+ "\\ \\",
+ "▁J ohn",
+ "▁Jo hn",
+ "▁Joh n",
+ "▁ John",
+ "▁ Б",
+ "or th",
+ "ort h",
+ "▁j e",
+ "▁ je",
+ "▁u ne",
+ "▁un e",
+ "▁ une",
+ "l a",
+ "[ \"",
+ "fi eld",
+ "f ield",
+ "▁U S",
+ "▁ US",
+ "ic o",
+ "i co",
+ "▁per form",
+ "▁perf orm",
+ "▁ perform",
+ "ail able",
+ "Con fig",
+ "Conf ig",
+ "O r",
+ "▁mod el",
+ "▁mo del",
+ "▁mode l",
+ "▁ model",
+ "al es",
+ "ale s",
+ "a les",
+ "▁c reate",
+ "▁cre ate",
+ "▁creat e",
+ "▁ create",
+ "▁a nn",
+ "▁an n",
+ "▁ ann",
+ "an ces",
+ "ance s",
+ "anc es",
+ "I L",
+ "in ation",
+ "▁I m",
+ "▁ Im",
+ "an te",
+ "ant e",
+ "a nte",
+ "an a",
+ "a na",
+ "а н",
+ "▁t old",
+ "▁to ld",
+ "con fig",
+ "conf ig",
+ "\" ]",
+ "me t",
+ "m et",
+ "l t",
+ "▁t ext",
+ "▁te xt",
+ "▁tex t",
+ "▁ text",
+ "▁M ay",
+ "▁Ma y",
+ "▁ May",
+ "▁o rg",
+ "▁or g",
+ "▁ org",
+ "▁p ort",
+ "▁po rt",
+ "▁por t",
+ "▁ port",
+ "P l",
+ "ent ly",
+ "▁d oor",
+ "▁do or",
+ "▁ door",
+ "U S",
+ "▁( *",
+ "▁ (*",
+ "k t",
+ "E S",
+ "ent ial",
+ "enti al",
+ "▁is s",
+ "▁i ss",
+ "▁ iss",
+ "▁in c",
+ "▁i nc",
+ "▁ inc",
+ "No de",
+ "N ode",
+ "iv ely",
+ "ive ly",
+ "ivel y",
+ "▁as ked",
+ "▁ask ed",
+ "ir t",
+ "i rt",
+ "▁T e",
+ "▁ Te",
+ "▁re port",
+ "▁rep ort",
+ "▁repo rt",
+ "▁ report",
+ "▁c hang",
+ "▁ch ang",
+ "▁cha ng",
+ "ст и",
+ "с ти",
+ "▁a long",
+ "▁al ong",
+ "▁ch ange",
+ "▁chang e",
+ "▁ change",
+ "Si ze",
+ "S ize",
+ "▁e ver",
+ "▁ev er",
+ "▁ ever",
+ "▁o cc",
+ "▁oc c",
+ "▁ occ",
+ "ur y",
+ "u ry",
+ "▁m ind",
+ "▁min d",
+ "▁mi nd",
+ "▁ mind",
+ "or der",
+ "ord er",
+ "po int",
+ "p oint",
+ "ст о",
+ "с то",
+ "▁w he",
+ "▁wh e",
+ "▁ whe",
+ "▁import ant",
+ "▁ important",
+ "de s",
+ "d es",
+ "▁N ot",
+ "▁No t",
+ "▁ Not",
+ "▁w rit",
+ "▁wr it",
+ "▁ writ",
+ "▁e yes",
+ "▁ey es",
+ "▁eye s",
+ "▁d esc",
+ "▁de sc",
+ "▁des c",
+ "▁ desc",
+ "mo st",
+ "mos t",
+ "m ost",
+ "k s",
+ "▁b it",
+ "▁bi t",
+ "▁ bit",
+ "▁su ccess",
+ "▁suc cess",
+ "▁succ ess",
+ "▁ success",
+ "т ь",
+ "б о",
+ "co re",
+ "cor e",
+ "c ore",
+ "} (",
+ "▁ar ray",
+ "▁arr ay",
+ "▁ array",
+ "li n",
+ "l in",
+ "li sh",
+ "l ish",
+ "▁follow ing",
+ "Fi eld",
+ "F ield",
+ "id s",
+ "i ds",
+ "hi ng",
+ "hin g",
+ "h ing",
+ "▁c al",
+ "▁ca l",
+ "▁ cal",
+ "I s",
+ "ar ing",
+ "ari ng",
+ "arin g",
+ "a ring",
+ "le v",
+ "l ev",
+ "al t",
+ "a lt",
+ "C H",
+ "▁d é",
+ "al pha",
+ "alph a",
+ "▁f our",
+ "▁fo ur",
+ "▁fou r",
+ "▁ four",
+ "▁l aw",
+ "▁la w",
+ "▁ law",
+ "▁с е",
+ "▁ се",
+ "ir on",
+ "iro n",
+ "i ron",
+ "▁d isc",
+ "▁dis c",
+ "▁di sc",
+ "с е",
+ "ke n",
+ "k en",
+ "no de",
+ "nod e",
+ "n ode",
+ "▁P ar",
+ "▁Pa r",
+ "▁ Par",
+ "▁E ng",
+ "▁En g",
+ "▁ Eng",
+ "▁m ove",
+ "▁mov e",
+ "▁mo ve",
+ "▁ move",
+ "▁L icense",
+ "▁Lic ense",
+ "▁ License",
+ "cu l",
+ "c ul",
+ "ion e",
+ "io ne",
+ "i one",
+ ") $",
+ "▁t w",
+ "▁ tw",
+ "W e",
+ "se l",
+ "s el",
+ "▁W ith",
+ "▁Wi th",
+ "▁ With",
+ "▁on ce",
+ "▁ once",
+ "Serv ice",
+ "S ervice",
+ "bo l",
+ "b ol",
+ "ur ed",
+ "ure d",
+ "u red",
+ "id a",
+ "i da",
+ "▁Q u",
+ "▁ Qu",
+ "▁g row",
+ "▁gr ow",
+ "▁gro w",
+ "▁ grow",
+ "▁c onne",
+ "▁con ne",
+ "▁conn e",
+ "▁ conne",
+ "E X",
+ "▁h tt",
+ "▁ htt",
+ "▁} ;",
+ "▁ };",
+ "▁w alk",
+ "▁wal k",
+ "▁ walk",
+ "▁in it",
+ "▁i nit",
+ "▁ init",
+ "na l",
+ "n al",
+ "en der",
+ "end er",
+ "ende r",
+ "e nder",
+ "cri ption",
+ "cript ion",
+ "mb er",
+ "m ber",
+ "le cted",
+ "lect ed",
+ "p o",
+ "▁n il",
+ "▁ni l",
+ "▁ nil",
+ "▁p rob",
+ "▁pro b",
+ "▁pr ob",
+ "▁ prob",
+ "ч и",
+ "▁S te",
+ "▁St e",
+ "▁ Ste",
+ "is on",
+ "iso n",
+ "i son",
+ "an ds",
+ "and s",
+ "os ed",
+ "ose d",
+ "o sed",
+ "ж е",
+ "▁H is",
+ "▁Hi s",
+ "▁ His",
+ "ü r",
+ "Ma n",
+ "M an",
+ "El ement",
+ "Elem ent",
+ "E lement",
+ "▁a ble",
+ "▁ab le",
+ "▁ able",
+ "In dex",
+ "Ind ex",
+ "se arch",
+ "s earch",
+ "▁m ag",
+ "▁ma g",
+ "▁ mag",
+ "а р",
+ "▁c ourse",
+ "▁cour se",
+ "▁cours e",
+ "▁ course",
+ "▁C ar",
+ "▁Ca r",
+ "▁ Car",
+ "▁e xp",
+ "▁ex p",
+ "▁ exp",
+ "ap h",
+ "a ph",
+ "▁m it",
+ "▁mi t",
+ "▁ mit",
+ "▁does n",
+ "▁def ault",
+ "▁ default",
+ "/ >",
+ "ai m",
+ "a im",
+ "▁s ervice",
+ "▁serv ice",
+ "▁ service",
+ "▁with in",
+ "an gu",
+ "ang u",
+ "▁ Д",
+ "uf fer",
+ "uff er",
+ "A G",
+ "▁D o",
+ "▁ Do",
+ "▁in cre",
+ "▁inc re",
+ "▁under stand",
+ "} ^",
+ "▁look ed",
+ "▁lo oked",
+ "ge n",
+ "g en",
+ "ail ed",
+ "ai led",
+ "a iled",
+ "▁ е",
+ "ay er",
+ "aye r",
+ "a yer",
+ "▁O ne",
+ "▁On e",
+ "▁ One",
+ "▁b as",
+ "▁ba s",
+ "▁ bas",
+ "▁j ob",
+ "▁jo b",
+ "▁ job",
+ "m u",
+ "bu t",
+ "b ut",
+ "el ta",
+ "elt a",
+ "▁Ch rist",
+ "▁Chris t",
+ "▁ Christ",
+ "ur ation",
+ "▁re cord",
+ "▁rec ord",
+ "▁ record",
+ "▁Un ivers",
+ "▁ Univers",
+ "iv id",
+ "ivi d",
+ "i vid",
+ "val id",
+ "▁ Р",
+ "▁h old",
+ "▁hol d",
+ "▁ho ld",
+ "▁ hold",
+ "▁t able",
+ "▁tab le",
+ "▁ta ble",
+ "▁ table",
+ "on es",
+ "one s",
+ "o nes",
+ "lin k",
+ "l ink",
+ "▁G e",
+ "▁ Ge",
+ "▁of fer",
+ "▁off er",
+ "st er",
+ "ste r",
+ "s ter",
+ "For m",
+ "F orm",
+ "= {",
+ "▁н е",
+ "▁ не",
+ "st ance",
+ "stan ce",
+ "▁g overn",
+ "▁go vern",
+ "▁gover n",
+ "▁ govern",
+ "▁te chn",
+ "▁tech n",
+ "▁ techn",
+ "▁p rim",
+ "▁pr im",
+ "▁pri m",
+ "▁ prim",
+ "* .",
+ "ch o",
+ "c ho",
+ "ma x",
+ "m ax",
+ "▁f ore",
+ "▁for e",
+ "▁fo re",
+ "▁ fore",
+ "▁C an",
+ "▁Ca n",
+ "▁ Can",
+ "▁pol it",
+ "▁po lit",
+ "▁ polit",
+ "or ies",
+ "ori es",
+ "orie s",
+ "o ries",
+ "▁t imes",
+ "▁time s",
+ "▁tim es",
+ "▁ti mes",
+ "▁ times",
+ "▁d ans",
+ "▁da ns",
+ "▁dan s",
+ "▁a ir",
+ "▁ai r",
+ "▁ air",
+ "▁any thing",
+ "▁s ever",
+ "▁se ver",
+ "ac y",
+ "a cy",
+ "} _",
+ "H e",
+ "▁l east",
+ "▁le ast",
+ "ip s",
+ "i ps",
+ "EN T",
+ "E NT",
+ "d o",
+ "▁о т",
+ "▁ от",
+ "▁c ost",
+ "▁co st",
+ "▁cos t",
+ "▁ cost",
+ ". ”",
+ "▁child ren",
+ "▁ children",
+ "ab ility",
+ "abil ity",
+ "Bu t",
+ "B ut",
+ "▁p ath",
+ "▁pat h",
+ "▁pa th",
+ "▁ path",
+ "res ult",
+ "ac ter",
+ "act er",
+ "▁e lement",
+ "▁el ement",
+ "▁ele ment",
+ "▁elem ent",
+ "▁ element",
+ "e e",
+ "▁w ait",
+ "▁wa it",
+ "▁ wait",
+ "▁m oney",
+ "▁mon ey",
+ "▁mo ney",
+ "Ma p",
+ "M ap",
+ "t d",
+ "oi n",
+ "o in",
+ "iv ing",
+ "ivi ng",
+ "i ving",
+ "ic ht",
+ "ich t",
+ "i cht",
+ "ic y",
+ "i cy",
+ "sc h",
+ "s ch",
+ "st e",
+ "s te",
+ "д у",
+ "or ed",
+ "ore d",
+ "o red",
+ "ou d",
+ "o ud",
+ "il le",
+ "ill e",
+ "i lle",
+ "is ed",
+ "ise d",
+ "i sed",
+ "pl ication",
+ "plic ation",
+ "▁c ustom",
+ "▁cust om",
+ "▁ custom",
+ "▁h aving",
+ "▁ha ving",
+ "▁hav ing",
+ "pon ent",
+ "po nent",
+ "▁B y",
+ "▁ By",
+ "ul es",
+ "ule s",
+ "u les",
+ "ue d",
+ "u ed",
+ "at ter",
+ "att er",
+ "atte r",
+ "An d",
+ "A nd",
+ "it ive",
+ "iti ve",
+ "De f",
+ "D ef",
+ "▁m oment",
+ "▁mom ent",
+ "▁mo ment",
+ "▁ moment",
+ "at erial",
+ "ate rial",
+ "ater ial",
+ "Cl ass",
+ "C lass",
+ "og raph",
+ "ograp h",
+ "o graph",
+ "ik e",
+ "i ke",
+ "▁l arge",
+ "▁larg e",
+ "▁ large",
+ "▁# ###",
+ "▁## ##",
+ "▁### #",
+ "▁ ####",
+ "▁e ither",
+ "du ct",
+ "duc t",
+ "d uct",
+ "▁T hen",
+ "▁The n",
+ "▁Th en",
+ "▁ Then",
+ "▁G u",
+ "▁ Gu",
+ "ole an",
+ "o lean",
+ "pe rt",
+ "per t",
+ "p ert",
+ "▁G et",
+ "▁Ge t",
+ "▁ Get",
+ "▁A b",
+ "▁ Ab",
+ "▁sh ort",
+ "▁ short",
+ "O n",
+ "im ent",
+ "ime nt",
+ "imen t",
+ "i ment",
+ "▁pro ject",
+ "▁ project",
+ "cri pt",
+ "cr ipt",
+ "c ript",
+ "▁incl uding",
+ "▁includ ing",
+ "▁inclu ding",
+ "▁ including",
+ "ни я",
+ "▁m aking",
+ "▁ma king",
+ "▁ making",
+ "▁some one",
+ "▁F l",
+ "▁ Fl",
+ "▁s at",
+ "▁sa t",
+ "▁ sat",
+ "▁comp any",
+ "▁compan y",
+ "▁ company",
+ "oc us",
+ "p u",
+ "▁G od",
+ "▁Go d",
+ "▁ God",
+ "if ication",
+ "ific ation",
+ "N o",
+ "▁s n",
+ "▁ sn",
+ "an o",
+ "a no",
+ "g a",
+ "▁a u",
+ "▁ au",
+ "▁c ou",
+ "▁co u",
+ "▁ cou",
+ "á s",
+ "en ded",
+ "end ed",
+ "ende d",
+ "т у",
+ "ob er",
+ "obe r",
+ "o ber",
+ "▁n othing",
+ "▁not hing",
+ "▁no thing",
+ "▁n et",
+ "▁ne t",
+ "▁ net",
+ "▁p ot",
+ "▁po t",
+ "▁ pot",
+ "▁t yp",
+ "▁ty p",
+ "▁ typ",
+ "▁it em",
+ "▁i tem",
+ "▁ item",
+ "re w",
+ "r ew",
+ "At t",
+ "A tt",
+ "▁you ng",
+ "▁yo ung",
+ "} \r",
+ "nd er",
+ "nde r",
+ "n der",
+ "st art",
+ "sta rt",
+ "star t",
+ "▁S c",
+ "▁ Sc",
+ "* )",
+ "▁e nc",
+ "▁en c",
+ "▁ enc",
+ "▁w omen",
+ "▁wom en",
+ "▁wo men",
+ "▁look ing",
+ "▁lo oking",
+ "▁ looking",
+ "▁р о",
+ "▁ ро",
+ "▁he alth",
+ "▁heal th",
+ "▁ health",
+ "Pat h",
+ "P ath",
+ "▁A fter",
+ "▁Af ter",
+ "▁ After",
+ "▁m ult",
+ "▁mu lt",
+ "▁mul t",
+ "▁ mult",
+ "▁{ \\",
+ "▁ {\\",
+ "▁l and",
+ "▁la nd",
+ "▁lan d",
+ "▁ land",
+ "or ld",
+ "▁D es",
+ "▁De s",
+ "▁ Des",
+ "▁e ng",
+ "▁en g",
+ "▁ eng",
+ "in put",
+ "▁P ol",
+ "▁Po l",
+ "▁ Pol",
+ "\" \"",
+ "Co de",
+ "C ode",
+ "▁s upp",
+ "▁su pp",
+ "▁sup p",
+ "▁ supp",
+ "ain er",
+ "ai ner",
+ "aine r",
+ "a iner",
+ "he ck",
+ "▁m or",
+ "▁mo r",
+ "▁ mor",
+ "▁m ill",
+ "▁mil l",
+ "▁mi ll",
+ "▁ mill",
+ "▁a w",
+ "▁ aw",
+ "f s",
+ "▁do ing",
+ "ting s",
+ "t ings",
+ "ad es",
+ "ade s",
+ "a des",
+ "▁to get",
+ "▁c ertain",
+ "▁cert ain",
+ "▁cer tain",
+ "▁t ogether",
+ "▁toget her",
+ "C E",
+ "ide o",
+ "▁Amer ican",
+ "▁America n",
+ "▁ American",
+ "on y",
+ "o ny",
+ "id d",
+ "i dd",
+ "I I",
+ "ge d",
+ "g ed",
+ "ab les",
+ "able s",
+ "abl es",
+ "a bles",
+ "▁ide nt",
+ "▁id ent",
+ "▁ ident",
+ "io d",
+ "i od",
+ "▁p arent",
+ "▁par ent",
+ "▁pa rent",
+ "▁pare nt",
+ "▁ parent",
+ "F or",
+ "amb da",
+ "an do",
+ "and o",
+ "= \\",
+ "ag ed",
+ "age d",
+ "a ged",
+ "en ding",
+ "end ing",
+ "In t",
+ "I nt",
+ "▁poss ible",
+ "▁ possible",
+ "▁с о",
+ "▁ со",
+ "iv ity",
+ "ivi ty",
+ "nu m",
+ "n um",
+ "r t",
+ "aj or",
+ "ajo r",
+ "a jor",
+ "cre ate",
+ "creat e",
+ "c reate",
+ "ri de",
+ "rid e",
+ "r ide",
+ "▁k new",
+ "▁kn ew",
+ "▁kne w",
+ "bi t",
+ "b it",
+ "it ional",
+ "ition al",
+ "iti onal",
+ "▁l ik",
+ "▁li k",
+ "▁ lik",
+ "▁H er",
+ "▁He r",
+ "▁ Her",
+ "ens ion",
+ "\" .",
+ "ot o",
+ "o to",
+ "▁ex ist",
+ "▁ exist",
+ "ak en",
+ "ake n",
+ "a ken",
+ "▁act ually",
+ "▁actual ly",
+ "c a",
+ "▁ Г",
+ "х о",
+ "in n",
+ "i nn",
+ "Al l",
+ "A ll",
+ "bu f",
+ "b uf",
+ "▁M e",
+ "▁ Me",
+ "▁s een",
+ "▁se en",
+ "▁see n",
+ "▁ seen",
+ "op s",
+ "o ps",
+ "No t",
+ "N ot",
+ "▁cont rol",
+ "▁contr ol",
+ "▁contro l",
+ "▁ control",
+ "▁res pon",
+ "▁resp on",
+ "▁ respon",
+ "} ;",
+ "il t",
+ "i lt",
+ "is k",
+ "i sk",
+ "▁b ad",
+ "▁ba d",
+ "▁ bad",
+ "▁o ften",
+ "▁of ten",
+ "▁p ast",
+ "▁pas t",
+ "▁pa st",
+ "ap er",
+ "ape r",
+ "a per",
+ "▁re ason",
+ "▁ reason",
+ "et ers",
+ "eter s",
+ "ete rs",
+ "e ters",
+ "▁w anted",
+ "▁want ed",
+ "ur a",
+ "u ra",
+ "ta ble",
+ "tab le",
+ "t able",
+ "or mal",
+ "orm al",
+ "wid th",
+ "w idth",
+ "г а",
+ "pt r",
+ "p tr",
+ "▁d est",
+ "▁de st",
+ "▁des t",
+ "▁ dest",
+ "▁de sign",
+ "▁des ign",
+ "▁ design",
+ "▁s ound",
+ "▁so und",
+ "▁sou nd",
+ "▁ sound",
+ "▁p lan",
+ "▁pl an",
+ "▁ plan",
+ "▁b ase",
+ "▁bas e",
+ "▁ba se",
+ "▁ base",
+ "ha nd",
+ "han d",
+ "h and",
+ "g s",
+ "▁s ays",
+ "▁sa ys",
+ "▁say s",
+ "fun ction",
+ "f unction",
+ "▁t ri",
+ "▁tr i",
+ "▁ tri",
+ "m t",
+ "▁in vest",
+ "▁inv est",
+ "▁av ailable",
+ "▁ available",
+ "ay out",
+ "a yout",
+ "▁o ch",
+ "▁oc h",
+ "▁ och",
+ "▁l as",
+ "▁la s",
+ "▁ las",
+ "il led",
+ "ill ed",
+ "ille d",
+ "V al",
+ "▁ ф",
+ "ie ty",
+ "iet y",
+ "i ety",
+ "mo n",
+ "m on",
+ "Ha nd",
+ "H and",
+ "F r",
+ "ia m",
+ "i am",
+ "pa ce",
+ "p ace",
+ "▁O b",
+ "▁ Ob",
+ "▁p ara",
+ "▁par a",
+ "▁pa ra",
+ "▁ para",
+ "▁me et",
+ "▁s um",
+ "▁su m",
+ "▁ sum",
+ "M essage",
+ "ic i",
+ "i ci",
+ "▁k nown",
+ "▁kn own",
+ "▁know n",
+ "▁ known",
+ "▁g en",
+ "▁ge n",
+ "▁ gen",
+ "am ma",
+ "amm a",
+ "a mma",
+ "ar r",
+ "a rr",
+ "▁t re",
+ "▁tr e",
+ "▁ tre",
+ "ok e",
+ "o ke",
+ "ut h",
+ "u th",
+ "~ \\",
+ "▁exper ience",
+ "▁experi ence",
+ "ic le",
+ "icl e",
+ "i cle",
+ "▁I l",
+ "▁ Il",
+ "▁s ent",
+ "▁se nt",
+ "▁sen t",
+ "▁ sent",
+ "▁o thers",
+ "▁other s",
+ "▁ others",
+ "▁s oft",
+ "▁so ft",
+ "▁ soft",
+ "I P",
+ "▁m ax",
+ "▁ma x",
+ "▁ max",
+ "ba ll",
+ "bal l",
+ "b all",
+ "▁mark et",
+ "▁mar ket",
+ "▁ market",
+ "▁p our",
+ "▁po ur",
+ "▁pou r",
+ "pr ession",
+ "press ion",
+ "p ression",
+ "ep s",
+ "e ps",
+ "▁s aw",
+ "▁sa w",
+ "▁a cross",
+ "▁ac ross",
+ "▁S u",
+ "▁ Su",
+ "O ver",
+ "ни е",
+ "ul ation",
+ "u lation",
+ "▁R eg",
+ "▁Re g",
+ "▁ Reg",
+ "▁+ =",
+ "▁ +=",
+ "bo dy",
+ "b ody",
+ ") \\",
+ "▁pr int",
+ "▁pri nt",
+ "▁prin t",
+ "▁ print",
+ "▁п ри",
+ "▁пр и",
+ "▁ при",
+ "d b",
+ "our ces",
+ "ource s",
+ "ward s",
+ "war ds",
+ "w ards",
+ "▁bl ack",
+ "▁ black",
+ "с о",
+ "il i",
+ "i li",
+ "▁E d",
+ "▁ Ed",
+ "▁com plet",
+ "▁comp let",
+ "▁compl et",
+ "▁s ingle",
+ "▁sing le",
+ "▁sin gle",
+ "▁ single",
+ "▁I N",
+ "▁ IN",
+ "ac hed",
+ "ach ed",
+ "ache d",
+ "a ched",
+ "b t",
+ "▁c ode",
+ "▁co de",
+ "▁cod e",
+ "▁ code",
+ "▁b ool",
+ "▁bo ol",
+ "▁ bool",
+ "▁a rea",
+ "▁are a",
+ "▁ar ea",
+ "▁ area",
+ "▁re quire",
+ "▁requ ire",
+ "▁ require",
+ "▁pro blem",
+ "▁proble m",
+ "▁prob lem",
+ "ac ed",
+ "ace d",
+ "a ced",
+ "Eq u",
+ "E qu",
+ "▁con fig",
+ "▁conf ig",
+ "▁ config",
+ "ve c",
+ "v ec",
+ "ne y",
+ "n ey",
+ "c y",
+ "A l",
+ "▁acc ount",
+ "▁ac count",
+ "▁ account",
+ "ym bol",
+ "▁s te",
+ "▁st e",
+ "▁ ste",
+ "ge s",
+ "g es",
+ "Ar ray",
+ "Arr ay",
+ "em pl",
+ "emp l",
+ "con text",
+ "cont ext",
+ "De s",
+ "D es",
+ "Res ult",
+ "ec ut",
+ "e cut",
+ "▁t arget",
+ "▁tar get",
+ "▁ target",
+ "▁get ting",
+ "\" />",
+ "og le",
+ "o gle",
+ "▁him self",
+ "▁was n",
+ "▁wa sn",
+ "▁b lock",
+ "▁bl ock",
+ "▁blo ck",
+ "▁ block",
+ "▁a nt",
+ "▁an t",
+ "▁ ant",
+ "▁Y ork",
+ "▁be come",
+ "▁bec ome",
+ "if f",
+ "i ff",
+ "port s",
+ "por ts",
+ "p orts",
+ "re ate",
+ "reat e",
+ "rea te",
+ "= '",
+ "c d",
+ "loc ation",
+ "l ocation",
+ "е т",
+ "▁a ccess",
+ "▁acc ess",
+ "▁ac cess",
+ "▁ access",
+ "gr ess",
+ "gre ss",
+ "gres s",
+ "g ress",
+ "ro s",
+ "r os",
+ "U p",
+ "▁work ing",
+ "▁wor king",
+ "▁ working",
+ "▁A m",
+ "▁ Am",
+ "iq u",
+ "i qu",
+ "ce r",
+ "c er",
+ "▁( (",
+ "▁ ((",
+ "▁P er",
+ "▁Pe r",
+ "▁ Per",
+ "▁f unc",
+ "▁fun c",
+ "▁fu nc",
+ "▁ func",
+ "▁g irl",
+ "▁gi rl",
+ "▁gir l",
+ "▁ girl",
+ "▁ab ove",
+ "pe n",
+ "p en",
+ "п и",
+ "id o",
+ "i do",
+ "▁v ersion",
+ "▁vers ion",
+ "▁ version",
+ "T Y",
+ "▁ ;",
+ "ma ry",
+ "mar y",
+ "m ary",
+ "ab led",
+ "able d",
+ "abl ed",
+ "a bled",
+ "an nel",
+ "ann el",
+ "anne l",
+ "▁ex ample",
+ "▁exam ple",
+ "▁ example",
+ "▁con text",
+ "▁cont ext",
+ "▁ context",
+ "O P",
+ "▁re d",
+ "▁r ed",
+ "▁ red",
+ "▁c ir",
+ "▁ci r",
+ "▁ cir",
+ "s m",
+ "Lo g",
+ "L og",
+ "▁s pace",
+ "▁sp ace",
+ "▁ space",
+ "▁f ut",
+ "▁fu t",
+ "▁G ener",
+ "▁Ge ner",
+ "▁Gen er",
+ "▁Gene r",
+ "▁ Gener",
+ "il ls",
+ "ill s",
+ "▁d ri",
+ "▁dr i",
+ "_ .",
+ "▁f elt",
+ "▁fe lt",
+ "▁fel t",
+ "▁o ffic",
+ "▁of fic",
+ "▁off ic",
+ "▁= ==",
+ "▁== =",
+ "▁ ===",
+ "i i",
+ "▁start ed",
+ "▁star ted",
+ "▁ Т",
+ "▁} );",
+ "▁}) ;",
+ "▁ });",
+ "j s",
+ "▁fr ont",
+ "▁fro nt",
+ "▁ front",
+ "▁al most",
+ "ir m",
+ "i rm",
+ "! \"",
+ "sign ed",
+ "sig ned",
+ "s igned",
+ "▁y et",
+ "▁ye t",
+ "▁t rad",
+ "▁tr ad",
+ "▁tra d",
+ "ient s",
+ "ien ts",
+ "i ents",
+ "am a",
+ "a ma",
+ "▁in put",
+ "▁ input",
+ "li m",
+ "l im",
+ "п а",
+ "▁к а",
+ "▁ ка",
+ "▁c amp",
+ "▁cam p",
+ "▁ca mp",
+ "▁ camp",
+ "ib r",
+ "i br",
+ "fe ct",
+ "f ect",
+ "un t",
+ "u nt",
+ "▁h alf",
+ "▁hal f",
+ "▁ half",
+ "▁c over",
+ "▁co ver",
+ "▁cov er",
+ "▁ cover",
+ "angu age",
+ "▁b en",
+ "▁be n",
+ "▁ ben",
+ "h a",
+ "▁d iff",
+ "▁di ff",
+ "▁dif f",
+ "▁ diff",
+ "_ \\",
+ "▁о б",
+ "▁ об",
+ "] )",
+ "od es",
+ "ode s",
+ "o des",
+ "he l",
+ "h el",
+ "io s",
+ "i os",
+ "▁ О",
+ "▁m ot",
+ "▁mo t",
+ "▁ mot",
+ "▁s ocial",
+ "▁so cial",
+ "▁soc ial",
+ "▁soci al",
+ "▁ social",
+ "//// ////",
+ "▁s tre",
+ "▁st re",
+ "▁str e",
+ "▁ stre",
+ "gr ound",
+ "gro und",
+ "g round",
+ "і в",
+ "ob ject",
+ "obj ect",
+ "pl es",
+ "ple s",
+ "p les",
+ "re ed",
+ "ree d",
+ "r eed",
+ "▁e en",
+ "▁ een",
+ "▁b ased",
+ "▁bas ed",
+ "▁base d",
+ "▁ba sed",
+ "▁ based",
+ "▁r ange",
+ "▁ran ge",
+ "▁rang e",
+ "▁ range",
+ "A n",
+ "ur g",
+ "u rg",
+ "▁le arn",
+ "▁lear n",
+ "▁ learn",
+ "▁e xc",
+ "▁ex c",
+ "▁ exc",
+ "▁im p",
+ "▁i mp",
+ "▁ imp",
+ "▁me ans",
+ "▁mean s",
+ "▁w ur",
+ "en ds",
+ "end s",
+ "vo id",
+ "v oid",
+ "▁s td",
+ "▁st d",
+ "▁ std",
+ "▁part icular",
+ "▁partic ular",
+ "▁particul ar",
+ "▁parti cular",
+ "j a",
+ "▁s ource",
+ "▁sour ce",
+ "▁ source",
+ "def ault",
+ "p y",
+ "▁a ls",
+ "▁al s",
+ "▁ als",
+ "sc ri",
+ "scr i",
+ "s cri",
+ "st atus",
+ "stat us",
+ "▁st ory",
+ "▁stor y",
+ "▁sto ry",
+ "▁ story",
+ "▁b egin",
+ "▁be gin",
+ "▁beg in",
+ "▁ begin",
+ "▁pos ition",
+ "▁posit ion",
+ "▁ position",
+ "▁spec ial",
+ "▁spe cial",
+ "▁ special",
+ "ph p",
+ "p hp",
+ "▁b ar",
+ "▁ba r",
+ "▁ bar",
+ "▁p ract",
+ "▁pr act",
+ "▁pra ct",
+ "▁prac t",
+ "cal l",
+ "ca ll",
+ "c all",
+ "▁d as",
+ "▁da s",
+ "▁ das",
+ "▁r ad",
+ "▁ra d",
+ "▁ rad",
+ "▁cl ose",
+ "▁clos e",
+ "▁clo se",
+ "▁ close",
+ "ww w",
+ "w ww",
+ "ер е",
+ "е ре",
+ "g u",
+ "▁E r",
+ "▁ Er",
+ "▁d om",
+ "▁do m",
+ "▁ dom",
+ "A M",
+ "▁b ed",
+ "▁be d",
+ "▁ bed",
+ "▁sever al",
+ "au l",
+ "a ul",
+ "bo x",
+ "b ox",
+ "▁l ow",
+ "▁lo w",
+ "▁ low",
+ "pa ck",
+ "p ack",
+ "Re g",
+ "R eg",
+ "O f",
+ "at ures",
+ "ature s",
+ "atur es",
+ "atu res",
+ "é n",
+ "ed er",
+ "ede r",
+ "e der",
+ "uild er",
+ "ca st",
+ "cas t",
+ "c ast",
+ "con om",
+ "co nom",
+ "c onom",
+ "ra ft",
+ "raf t",
+ "r aft",
+ "▁m akes",
+ "▁make s",
+ "▁ma kes",
+ "Lo c",
+ "L oc",
+ "ht tp",
+ "htt p",
+ "h ttp",
+ "▁a bs",
+ "▁ab s",
+ "▁ abs",
+ "re sh",
+ "res h",
+ "r esh",
+ "▁W ill",
+ "▁Wil l",
+ "▁Wi ll",
+ "▁ Will",
+ "bre ak",
+ "b reak",
+ "▁o ptions",
+ "▁opt ions",
+ "▁option s",
+ "▁ options",
+ "fo rt",
+ "for t",
+ "f ort",
+ "▁и з",
+ "▁ из",
+ "▁a nal",
+ "▁an al",
+ "▁ anal",
+ "▁e nv",
+ "▁en v",
+ "▁ env",
+ "( {",
+ "ev ent",
+ "even t",
+ "eve nt",
+ "e vent",
+ "▁p age",
+ "▁pa ge",
+ "▁pag e",
+ "▁ page",
+ "ter nal",
+ "tern al",
+ "▁d istribut",
+ "▁dist ribut",
+ "▁f ood",
+ "▁fo od",
+ "▁foo d",
+ "▁ food",
+ "che ck",
+ "c heck",
+ "C K",
+ "▁в о",
+ "▁ во",
+ "as sert",
+ "ass ert",
+ "asse rt",
+ "á n",
+ "ba se",
+ "bas e",
+ "b ase",
+ "▁w hole",
+ "▁wh ole",
+ "▁who le",
+ "ac ión",
+ "ació n",
+ "aci ón",
+ "a ción",
+ "O D",
+ "▁turn ed",
+ "▁tur ned",
+ "ig ma",
+ "▁res ponse",
+ "▁respon se",
+ "▁respons e",
+ "▁ response",
+ "▁Univers ity",
+ "▁d iv",
+ "▁di v",
+ "▁ div",
+ "ap ter",
+ "apt er",
+ "▁result s",
+ "▁ results",
+ "▁re present",
+ "▁rep resent",
+ "▁every thing",
+ "▁C ent",
+ "▁Ce nt",
+ "▁ Cent",
+ "ut es",
+ "ute s",
+ "u tes",
+ "ri x",
+ "r ix",
+ "▁S ome",
+ "▁So me",
+ "▁Som e",
+ "▁ Some",
+ "▁be hind",
+ "▁beh ind",
+ "▁c reat",
+ "▁cre at",
+ "▁ creat",
+ "pl ace",
+ "plac e",
+ "p lace",
+ "s u",
+ "▁P art",
+ "▁Par t",
+ "▁Pa rt",
+ "▁ Part",
+ "um b",
+ "u mb",
+ "math bb",
+ "pi ng",
+ "pin g",
+ "p ing",
+ "▁m atch",
+ "▁mat ch",
+ "▁ match",
+ "O ut",
+ "do m",
+ "d om",
+ "▁s itu",
+ "▁sit u",
+ "▁si tu",
+ "d r",
+ "ar a",
+ "a ra",
+ "▁w indow",
+ "▁wind ow",
+ "▁ window",
+ "n s",
+ "lish ed",
+ "l ished",
+ "▁V er",
+ "▁Ve r",
+ "▁ Ver",
+ "▁m essage",
+ "▁mess age",
+ "▁ message",
+ "▁E m",
+ "▁ Em",
+ "▁h uman",
+ "▁hum an",
+ "▁ human",
+ "per ties",
+ "pert ies",
+ "л у",
+ "le m",
+ "l em",
+ "OR T",
+ "O RT",
+ "▁e arly",
+ "▁ear ly",
+ "▁qu ick",
+ "▁qui ck",
+ "▁ quick",
+ "▁т а",
+ "▁ та",
+ "ro id",
+ "r oid",
+ "▁c ountry",
+ "▁coun try",
+ "▁count ry",
+ "▁countr y",
+ "▁ country",
+ "▁d ue",
+ "▁du e",
+ "▁ due",
+ "▁D ie",
+ "▁Di e",
+ "▁ Die",
+ "▁t rying",
+ "▁tr ying",
+ "▁try ing",
+ "▁l ive",
+ "▁li ve",
+ "▁liv e",
+ "▁ live",
+ "▁p ress",
+ "▁pre ss",
+ "▁pr ess",
+ "▁pres s",
+ "▁ press",
+ "IN T",
+ "I NT",
+ "W ith",
+ "ov ed",
+ "ove d",
+ "o ved",
+ "▁spec ific",
+ "▁ specific",
+ "▁f all",
+ "▁fa ll",
+ "▁fal l",
+ "▁ fall",
+ "u k",
+ "y l",
+ "▁gener al",
+ "▁gen eral",
+ "▁gene ral",
+ "▁ general",
+ "м у",
+ "н у",
+ "▁n ames",
+ "▁name s",
+ "▁na mes",
+ "▁nam es",
+ "▁ names",
+ "wh ere",
+ "whe re",
+ "w here",
+ "▁The se",
+ "▁Th ese",
+ "▁ These",
+ "▁s il",
+ "▁si l",
+ "▁ sil",
+ "é t",
+ "▁e ner",
+ "▁en er",
+ "▁ ener",
+ "▁N ow",
+ "▁No w",
+ "▁ Now",
+ "▁add ress",
+ "▁addr ess",
+ "▁ address",
+ "Res ponse",
+ "▁M r",
+ "▁ Mr",
+ "▁an sw",
+ "▁ans w",
+ "▁fil m",
+ "▁fi lm",
+ "▁ film",
+ "▁str ong",
+ "▁stro ng",
+ "▁ strong",
+ "▁b ring",
+ "▁br ing",
+ "▁Un ited",
+ "▁Unit ed",
+ "▁g e",
+ "▁ ge",
+ "▁w oman",
+ "▁wom an",
+ "▁wo man",
+ "▁ woman",
+ "Ne w",
+ "N ew",
+ "et t",
+ "e tt",
+ ". )",
+ "en ame",
+ "ena me",
+ "e name",
+ "▁A N",
+ "▁ AN",
+ "▁de scrib",
+ "▁desc rib",
+ "з а",
+ "is ing",
+ "isi ng",
+ "i sing",
+ "E L",
+ "q l",
+ "▁f ur",
+ "▁fu r",
+ "▁ fur",
+ "y ing",
+ "▁C al",
+ "▁Ca l",
+ "▁ Cal",
+ "▁D r",
+ "▁ Dr",
+ "ER R",
+ "E RR",
+ "▁\\ \\",
+ "▁ \\\\",
+ "an gle",
+ "ang le",
+ "ur ope",
+ "uro pe",
+ "urop e",
+ "▁c ity",
+ "▁cit y",
+ "▁ci ty",
+ "▁ city",
+ "▁in dex",
+ "▁ind ex",
+ "▁inde x",
+ "▁ index",
+ "▁a ction",
+ "▁act ion",
+ "▁ action",
+ "▁How ever",
+ "▁ However",
+ "▁f ig",
+ "▁fi g",
+ "▁ fig",
+ "ia s",
+ "i as",
+ "▁quest ion",
+ "▁ question",
+ "▁J an",
+ "▁Ja n",
+ "▁ Jan",
+ "▁M ed",
+ "▁Me d",
+ "▁ Med",
+ "▁C ont",
+ "▁Con t",
+ "▁Co nt",
+ "▁ Cont",
+ "am ed",
+ "ame d",
+ "a med",
+ "Cal l",
+ "C all",
+ "pl ied",
+ "tt y",
+ "t ty",
+ "▁ind ivid",
+ "pa ge",
+ "pag e",
+ "p age",
+ "▁c omb",
+ "▁com b",
+ "▁co mb",
+ "▁ comb",
+ "se ction",
+ "sect ion",
+ "s ection",
+ "▁C omm",
+ "▁Com m",
+ "▁Co mm",
+ "▁ Comm",
+ "ue l",
+ "u el",
+ "▁h et",
+ "▁he t",
+ "▁ het",
+ "▁B ar",
+ "▁Ba r",
+ "▁ Bar",
+ "ag ement",
+ "age ment",
+ "agem ent",
+ "fi n",
+ "f in",
+ "▁m ajor",
+ "▁ma jor",
+ "▁maj or",
+ "▁ major",
+ "op er",
+ "ope r",
+ "o per",
+ "ap i",
+ "a pi",
+ "ro om",
+ "r oom",
+ "▁ „",
+ "▁h ab",
+ "▁ha b",
+ "▁ hab",
+ "з и",
+ "▁a uf",
+ "▁au f",
+ "▁ auf",
+ "cur rent",
+ "curr ent",
+ "n i",
+ "▁in clude",
+ "▁incl ude",
+ "▁includ e",
+ "▁inclu de",
+ "▁ include",
+ "▁qu i",
+ "▁q ui",
+ "v a",
+ "U E",
+ "▁ide a",
+ "▁id ea",
+ "▁ idea",
+ ", '",
+ "▁requ ired",
+ "▁require d",
+ "▁ required",
+ "▁he art",
+ "▁hear t",
+ "▁ heart",
+ "ib ility",
+ "ibil ity",
+ "ict ion",
+ "i ction",
+ "Mod el",
+ "Mode l",
+ "Mo del",
+ "wr ite",
+ "writ e",
+ "w rite",
+ "▁cont ent",
+ "▁conten t",
+ "▁ content",
+ "▁w er",
+ "▁we r",
+ "▁ wer",
+ "▁h ands",
+ "▁hand s",
+ "▁han ds",
+ "ze n",
+ "z en",
+ "ch ar",
+ "cha r",
+ "c har",
+ "}^ {",
+ "} ^{",
+ "▁m ass",
+ "▁ma ss",
+ "▁mas s",
+ "▁ mass",
+ "pl y",
+ "p ly",
+ "▁n at",
+ "▁na t",
+ "▁ nat",
+ "re l",
+ "r el",
+ "▁d at",
+ "▁da t",
+ "▁ dat",
+ "==== ============",
+ "======== ========",
+ "============ ====",
+ "im al",
+ "ima l",
+ "i mal",
+ "▁pro bably",
+ "▁prob ably",
+ "un ch",
+ "unc h",
+ "▁m er",
+ "▁me r",
+ "▁ mer",
+ "il ar",
+ "ila r",
+ "i lar",
+ "ir es",
+ "ire s",
+ "i res",
+ "▁w atch",
+ "▁wat ch",
+ "▁ watch",
+ "S I",
+ "▁c ult",
+ "▁cu lt",
+ "▁cul t",
+ "▁m other",
+ "▁mot her",
+ "▁mo ther",
+ "▁ mother",
+ "▁govern ment",
+ "or ding",
+ "ord ing",
+ "▁( )",
+ "▁ ()",
+ "▁p ri",
+ "▁pr i",
+ "▁l ink",
+ "▁lin k",
+ "▁ link",
+ "gr oup",
+ "gro up",
+ "g roup",
+ "O L",
+ "▁n ear",
+ "▁ne ar",
+ "▁S er",
+ "▁Se r",
+ "▁ Ser",
+ "Se r",
+ "S er",
+ "it o",
+ "i to",
+ "▁value s",
+ "▁val ues",
+ "▁ values",
+ "▁j ava",
+ "▁ja va",
+ "▁ java",
+ "ful ly",
+ "full y",
+ "f ully",
+ "Co unt",
+ "C ount",
+ "++ )",
+ "▁v i",
+ "▁ vi",
+ "▁wh ite",
+ "▁ white",
+ "ma t",
+ "m at",
+ "ct x",
+ "c tx",
+ "▁con c",
+ "▁co nc",
+ "▁ conc",
+ "▁st ay",
+ "▁sta y",
+ "gi ng",
+ "gin g",
+ "g ing",
+ "▁c lear",
+ "▁cl ear",
+ "▁cle ar",
+ "▁ clear",
+ "▁c opy",
+ "▁co py",
+ "▁cop y",
+ "▁ copy",
+ "sel ves",
+ "▁prov ide",
+ "▁w ords",
+ "▁wor ds",
+ "▁word s",
+ "▁ words",
+ "com p",
+ "co mp",
+ "c omp",
+ "ar gs",
+ "arg s",
+ "▁p ick",
+ "▁pi ck",
+ "▁pic k",
+ "▁ pick",
+ "ul y",
+ "u ly",
+ "▁v ari",
+ "▁var i",
+ "▁va ri",
+ "▁ vari",
+ "▁bel ieve",
+ "▁belie ve",
+ "▁C o",
+ "▁ Co",
+ "Pro perty",
+ "Gr oup",
+ "G roup",
+ "▁t en",
+ "▁te n",
+ "▁ ten",
+ "is chen",
+ "isch en",
+ "ische n",
+ "isc hen",
+ "i schen",
+ "et urn",
+ "e turn",
+ "iv al",
+ "iva l",
+ "i val",
+ "Sys tem",
+ "S ystem",
+ "C L",
+ "be d",
+ "b ed",
+ "▁t otal",
+ "▁to tal",
+ "▁tot al",
+ "▁ total",
+ "▁is t",
+ "▁i st",
+ "▁ ist",
+ "In put",
+ "um ents",
+ "ument s",
+ "umen ts",
+ "u ments",
+ "Man ager",
+ "ш и",
+ "▁w in",
+ "▁ win",
+ "le ep",
+ "lee p",
+ "P I",
+ "но го",
+ "н ого",
+ "ru ction",
+ "ruct ion",
+ "r uction",
+ "▁in te",
+ "▁i nte",
+ "▁int e",
+ "▁ inte",
+ "Ap p",
+ "A pp",
+ "av or",
+ "avo r",
+ "a vor",
+ "▁re spect",
+ "▁res pect",
+ "▁resp ect",
+ "▁ respect",
+ "at ors",
+ "ator s",
+ "ato rs",
+ "▁c omo",
+ "▁com o",
+ "▁co mo",
+ "▁c ut",
+ "▁cu t",
+ "▁ cut",
+ "F A",
+ "▁s us",
+ "▁su s",
+ "▁A pp",
+ "▁Ap p",
+ "▁ App",
+ "re ct",
+ "rec t",
+ "r ect",
+ "F I",
+ "▁be gan",
+ "▁beg an",
+ "op h",
+ "o ph",
+ "▁s ort",
+ "▁so rt",
+ "▁sor t",
+ "▁ sort",
+ "th ough",
+ "ј е",
+ "ic ro",
+ "i cro",
+ "Tr ans",
+ "Tra ns",
+ "л і",
+ "▁In st",
+ "▁Ins t",
+ "▁ Inst",
+ "re quest",
+ "requ est",
+ "req uest",
+ "о р",
+ "▁rel ations",
+ "▁relation s",
+ "- \\",
+ "St atus",
+ "Stat us",
+ "ж и",
+ "▁f ather",
+ "▁fa ther",
+ "▁fat her",
+ "▁ father",
+ "c s",
+ "▁s ex",
+ "▁se x",
+ "▁ sex",
+ "is ch",
+ "isc h",
+ "i sch",
+ "v o",
+ "}_ {",
+ "} _{",
+ "ave n",
+ "av en",
+ "a ven",
+ "▁N e",
+ "▁ Ne",
+ "AT E",
+ "A TE",
+ "it ten",
+ "itt en",
+ "itte n",
+ "▁e ss",
+ "▁es s",
+ "▁ ess",
+ "T H",
+ "ight s",
+ "igh ts",
+ "▁h om",
+ "▁ho m",
+ "▁ hom",
+ "▁t oday",
+ "▁to day",
+ "▁tod ay",
+ "▁toda y",
+ "▁z u",
+ "▁ zu",
+ "it a",
+ "i ta",
+ "▁is n",
+ "▁i sn",
+ "▁o pt",
+ "▁op t",
+ "▁ opt",
+ "og n",
+ "o gn",
+ "é r",
+ "▁wh ether",
+ "▁whe ther",
+ "ix ed",
+ "ph i",
+ "p hi",
+ "id ence",
+ "iden ce",
+ "al d",
+ "a ld",
+ "Cl ient",
+ "A t",
+ "▁de ath",
+ "▁L et",
+ "▁Le t",
+ "▁ Let",
+ "iu s",
+ "i us",
+ "г и",
+ "▁р е",
+ "▁ ре",
+ "be n",
+ "b en",
+ ") \r",
+ "b a",
+ ">< /",
+ "> ",
+ "ave l",
+ "av el",
+ "a vel",
+ "▁m iss",
+ "▁mis s",
+ "▁mi ss",
+ "▁ miss",
+ "▁n ode",
+ "▁no de",
+ "▁nod e",
+ "▁ node",
+ "▁( $",
+ "▁ ($",
+ "▁col or",
+ "▁co lor",
+ "▁ color",
+ "▁o bt",
+ "▁ob t",
+ "to t",
+ "t ot",
+ "▁п ре",
+ "▁пр е",
+ "▁ пре",
+ "CO N",
+ "C ON",
+ "et te",
+ "ett e",
+ "▁G o",
+ "▁ Go",
+ "F l",
+ "▁D on",
+ "▁Do n",
+ "▁ Don",
+ "▁c rit",
+ "▁cr it",
+ "▁cri t",
+ "▁ crit",
+ "▁r i",
+ "▁ ri",
+ "pos t",
+ "po st",
+ "p ost",
+ "▁- >",
+ "▁ ->",
+ "▁J ust",
+ "▁Ju st",
+ "▁ Just",
+ "Wh at",
+ "W hat",
+ "at al",
+ "ata l",
+ "a tal",
+ "▁M in",
+ "▁Mi n",
+ "▁ Min",
+ "▁C or",
+ "▁Co r",
+ "▁ Cor",
+ "▁d ark",
+ "▁dar k",
+ "▁ dark",
+ "r l",
+ "▁l arg",
+ "▁la rg",
+ "▁ larg",
+ "di ng",
+ "d ing",
+ "ó n",
+ "ou ch",
+ "o uch",
+ "▁u m",
+ "▁ um",
+ "▁e lect",
+ "▁el ect",
+ "▁ele ct",
+ "▁ elect",
+ "▁d am",
+ "▁da m",
+ "▁ dam",
+ "▁ne eds",
+ "▁need s",
+ "▁m atter",
+ "▁mat ter",
+ "▁matt er",
+ "▁r ather",
+ "▁rat her",
+ "▁ra ther",
+ "fr om",
+ "f rom",
+ "ra m",
+ "r am",
+ "▁ і",
+ "▁t aken",
+ "▁take n",
+ "▁tak en",
+ "▁ta ken",
+ "▁de al",
+ "▁per iod",
+ "▁ period",
+ "▁M on",
+ "▁Mo n",
+ "▁ Mon",
+ "▁ Л",
+ "▁A ug",
+ "▁Au g",
+ "▁ Aug",
+ "ru n",
+ "r un",
+ "m m",
+ "el le",
+ "ell e",
+ "e lle",
+ "▁ex port",
+ "▁exp ort",
+ "▁ export",
+ "S c",
+ "vi s",
+ "v is",
+ "ab or",
+ "a bor",
+ "▁aut hor",
+ "▁auth or",
+ "▁ author",
+ "è re",
+ "▁re member",
+ "▁rem ember",
+ "▁remem ber",
+ "▁re du",
+ "▁r edu",
+ "▁red u",
+ "▁ redu",
+ "▁L ist",
+ "▁Li st",
+ "▁Lis t",
+ "▁ List",
+ "▁f ocus",
+ "▁ focus",
+ "▁char acter",
+ "▁ character",
+ "Tab le",
+ "T able",
+ "▁individ ual",
+ "▁need ed",
+ "bu m",
+ "b um",
+ "▁st yle",
+ "▁sty le",
+ "▁ style",
+ "in ary",
+ "ina ry",
+ "inar y",
+ "ers ion",
+ "ou te",
+ "out e",
+ "o ute",
+ "▁P e",
+ "▁ Pe",
+ "▁h on",
+ "▁ho n",
+ "▁ hon",
+ "mu t",
+ "m ut",
+ "se e",
+ "s ee",
+ "▁bec ame",
+ "▁d ire",
+ "▁di re",
+ "▁dir e",
+ "▁ dire",
+ "▁d ocument",
+ "▁doc ument",
+ "▁ document",
+ "se c",
+ "s ec",
+ "en ing",
+ "eni ng",
+ "e ning",
+ "▁vis it",
+ "▁ visit",
+ "▁f ac",
+ "▁fa c",
+ "▁ fac",
+ "t x",
+ "do wn",
+ "d own",
+ "pl it",
+ "p lit",
+ "▁ph ys",
+ "▁ phys",
+ "it ting",
+ "itt ing",
+ "jo y",
+ "j oy",
+ "▁h ig",
+ "▁hi g",
+ "Th is",
+ "T his",
+ "A d",
+ "▁B rit",
+ "▁Br it",
+ "▁em ploy",
+ "▁r é",
+ "▁ ré",
+ "▁ т",
+ "l ambda",
+ "▁im pro",
+ "▁imp ro",
+ "▁B o",
+ "▁ Bo",
+ "id ing",
+ "idi ng",
+ "i ding",
+ "▁on line",
+ "▁ online",
+ "me m",
+ "m em",
+ "at form",
+ "▁W ar",
+ "▁Wa r",
+ "▁ War",
+ "▁c as",
+ "▁ca s",
+ "▁ cas",
+ "as ure",
+ "a sure",
+ "▁p ur",
+ "▁pu r",
+ "▁ pur",
+ "me di",
+ "med i",
+ "m edi",
+ "Di s",
+ "D is",
+ "▁G erm",
+ "▁Ge rm",
+ "▁Ger m",
+ "p c",
+ "с а",
+ "▁friend s",
+ "▁M c",
+ "▁ Mc",
+ "D I",
+ "▁pl us",
+ "▁ plus",
+ "▁S et",
+ "▁Se t",
+ "▁ Set",
+ "idd le",
+ "it ut",
+ "itu t",
+ "▁de pend",
+ "▁dep end",
+ "▁ depend",
+ "re st",
+ "res t",
+ "r est",
+ "▁J e",
+ "▁ Je",
+ "▁h or",
+ "▁ho r",
+ "▁ hor",
+ "▁ent ire",
+ "Qu ery",
+ "Que ry",
+ "▁re fer",
+ "▁ref er",
+ "▁ refer",
+ "▁h ot",
+ "▁ho t",
+ "▁ hot",
+ "▁A ust",
+ "▁Aus t",
+ "▁Au st",
+ "▁com mon",
+ "▁comm on",
+ "▁ common",
+ "ц і",
+ "▁p ull",
+ "▁pu ll",
+ "▁pul l",
+ "▁ pull",
+ "▁A dd",
+ "▁Ad d",
+ "▁ Add",
+ "▁se ason",
+ "▁sea son",
+ "▁seas on",
+ "▁ season",
+ "▁in vol",
+ "▁inv ol",
+ "▁W orld",
+ "▁Wor ld",
+ "▁ World",
+ "cl ient",
+ "cli ent",
+ "no w",
+ "n ow",
+ "tr ue",
+ "ap pend",
+ "app end",
+ "appe nd",
+ "appen d",
+ "it ted",
+ "itt ed",
+ "itte d",
+ "em pt",
+ "emp t",
+ ") {",
+ "// /",
+ "/ //",
+ "▁p rop",
+ "▁pro p",
+ "▁pr op",
+ "▁ prop",
+ "im ate",
+ "ima te",
+ "imat e",
+ "i mate",
+ "S C",
+ "▁h ours",
+ "▁hour s",
+ "▁ho urs",
+ "▁h ope",
+ "▁hop e",
+ "▁ho pe",
+ "an dom",
+ "and om",
+ "ando m",
+ "і д",
+ "ist ic",
+ "isti c",
+ "▁pro perty",
+ "▁proper ty",
+ "▁ property",
+ "s g",
+ "> (",
+ "▁w rite",
+ "▁wr ite",
+ "▁writ e",
+ "▁ write",
+ "mar k",
+ "m ark",
+ "fin d",
+ "fi nd",
+ "f ind",
+ "▁person al",
+ "▁pers onal",
+ "▁persona l",
+ "▁ personal",
+ "] [",
+ "ro wn",
+ "row n",
+ "r own",
+ "P h",
+ "▁f oot",
+ "▁fo ot",
+ "▁foo t",
+ "▁ foot",
+ "▁re search",
+ "▁res earch",
+ "iron ment",
+ "▁n om",
+ "▁no m",
+ "▁ nom",
+ "▁in stance",
+ "▁inst ance",
+ "▁ instance",
+ "▁h eld",
+ "▁he ld",
+ "▁hel d",
+ "▁ held",
+ "D e",
+ "▁mem bers",
+ "▁member s",
+ "▁ members",
+ "▁f ire",
+ "▁fi re",
+ "▁fir e",
+ "▁ fire",
+ "▁hist ory",
+ "▁histor y",
+ "▁hi story",
+ "▁ history",
+ "▁m ap",
+ "▁ma p",
+ "▁ map",
+ "▁dis cuss",
+ "▁disc uss",
+ "▁e spec",
+ "▁es pec",
+ "▁esp ec",
+ "▁ espec",
+ "▁t aking",
+ "▁tak ing",
+ "▁ta king",
+ "▁s ervices",
+ "▁serv ices",
+ "▁service s",
+ "▁ services",
+ "▁ind ust",
+ "▁indu st",
+ "▁ indust",
+ "ig en",
+ "ige n",
+ "i gen",
+ "▁A ss",
+ "▁As s",
+ "▁ Ass",
+ "▁e xpected",
+ "▁ex pected",
+ "▁expect ed",
+ "▁ expected",
+ "▁wur de",
+ "di r",
+ "d ir",
+ "▁a mong",
+ "▁am ong",
+ "▁s ugg",
+ "▁su gg",
+ "▁sug g",
+ "re c",
+ "r ec",
+ "In ter",
+ "Int er",
+ "bl ock",
+ "blo ck",
+ "b lock",
+ "▁R ep",
+ "▁Re p",
+ "▁ Rep",
+ "▁p ain",
+ "▁pa in",
+ "▁f ive",
+ "▁fi ve",
+ "▁ five",
+ "▁f und",
+ "▁fun d",
+ "▁fu nd",
+ "▁ fund",
+ "ri d",
+ "r id",
+ "ar row",
+ "arr ow",
+ "▁t reat",
+ "▁tre at",
+ "▁he ard",
+ "▁hear d",
+ "▁de term",
+ "▁det erm",
+ "▁deter m",
+ "ic ult",
+ "▁s ense",
+ "▁sens e",
+ "▁sen se",
+ "es e",
+ "e se",
+ "F un",
+ "▁month s",
+ "▁mont hs",
+ "js on",
+ "j son",
+ ", ”",
+ "T I",
+ "or age",
+ "ora ge",
+ "o rage",
+ "▁ У",
+ "▁every one",
+ "▁c los",
+ "▁cl os",
+ "▁clo s",
+ "▁ clos",
+ "ie rs",
+ "ier s",
+ "i ers",
+ "air s",
+ "ai rs",
+ "a irs",
+ "def ine",
+ "I f",
+ "os p",
+ "o sp",
+ "▁w onder",
+ "▁won der",
+ "▁wo nder",
+ "N A",
+ "qu ery",
+ "que ry",
+ "quer y",
+ "p g",
+ "it es",
+ "ite s",
+ "i tes",
+ "▁m aterial",
+ "▁mat erial",
+ "▁mate rial",
+ "▁mater ial",
+ "▁ material",
+ "y d",
+ "Re ad",
+ "R ead",
+ "ht ml",
+ "h tml",
+ "T E",
+ "P r",
+ "^{ \\",
+ "^ {\\",
+ "▁g ave",
+ "▁ga ve",
+ "▁I S",
+ "▁ IS",
+ "▁s uggest",
+ "▁sugg est",
+ "▁sug gest",
+ "Over ride",
+ "ro du",
+ "rod u",
+ "Fr om",
+ "F rom",
+ "▁E urope",
+ "▁Europ e",
+ "▁Euro pe",
+ "▁ Europe",
+ "P O",
+ "▁s oon",
+ "▁so on",
+ "ho st",
+ "hos t",
+ "h ost",
+ "▁B er",
+ "▁Be r",
+ "▁ Ber",
+ ".. ..",
+ "... .",
+ ". ...",
+ "▁H ar",
+ "▁Ha r",
+ "▁ Har",
+ "▁e nergy",
+ "▁ener gy",
+ "▁energ y",
+ "▁ energy",
+ "> <",
+ "ave s",
+ "av es",
+ "a ves",
+ "▁e asy",
+ "▁eas y",
+ "▁b re",
+ "▁br e",
+ "▁ bre",
+ "fr ame",
+ "▁g round",
+ "▁gr ound",
+ "▁gro und",
+ "▁ ground",
+ "wi th",
+ "w ith",
+ "▁in side",
+ "▁ins ide",
+ "ie f",
+ "i ef",
+ "▁m o",
+ "▁ mo",
+ "p m",
+ "pa n",
+ "p an",
+ "ig r",
+ "i gr",
+ "▁o m",
+ "▁ om",
+ "ne xt",
+ "nex t",
+ "n ext",
+ "om et",
+ "ome t",
+ "o met",
+ "▁st atus",
+ "▁stat us",
+ "▁ status",
+ "▁} \r",
+ "▁ }\r",
+ "▁mus ic",
+ "or a",
+ "o ra",
+ "il es",
+ "ile s",
+ "i les",
+ "k i",
+ "▁e sc",
+ "▁es c",
+ "▁ esc",
+ "▁b es",
+ "▁be s",
+ "▁ bes",
+ "▁D is",
+ "▁Di s",
+ "▁ Dis",
+ "▁h ost",
+ "▁ho st",
+ "▁ host",
+ "▁c omes",
+ "▁com es",
+ "▁co mes",
+ "▁come s",
+ "▁ comes",
+ "us ed",
+ "use d",
+ "u sed",
+ "▁f uture",
+ "▁fut ure",
+ "▁ future",
+ "lic k",
+ "li ck",
+ "l ick",
+ "ai d",
+ "a id",
+ "▁com pet",
+ "▁comp et",
+ "▁ compet",
+ "▁v oice",
+ "▁vo ice",
+ "▁ voice",
+ "▁l oad",
+ "▁lo ad",
+ "▁ load",
+ "ev el",
+ "eve l",
+ "e vel",
+ "▁n eg",
+ "▁ne g",
+ "▁ neg",
+ "▁com mand",
+ "▁comm and",
+ "▁ command",
+ "▁f ür",
+ "▁p ie",
+ "▁pi e",
+ "▁ pie",
+ "▁qu ite",
+ "▁qui te",
+ "▁quit e",
+ "▁b lo",
+ "▁bl o",
+ "▁ blo",
+ "ag n",
+ "a gn",
+ "il on",
+ "ilo n",
+ "i lon",
+ "▁cl aim",
+ "▁ claim",
+ "▁t each",
+ "▁te ach",
+ "▁tea ch",
+ "▁pre vious",
+ "▁prev ious",
+ "▁ previous",
+ "▁s ite",
+ "▁sit e",
+ "▁si te",
+ "▁ site",
+ "co lor",
+ "col or",
+ "colo r",
+ "at tr",
+ "att r",
+ "▁ac cept",
+ "▁ accept",
+ "▁ex act",
+ ") }",
+ "af t",
+ "a ft",
+ "rol ler",
+ "roll er",
+ "о н",
+ "o o",
+ "Dat e",
+ "Da te",
+ "D ate",
+ "▁o u",
+ "▁ ou",
+ "s y",
+ "▁pre tty",
+ "▁pret ty",
+ "▁im age",
+ "▁imag e",
+ "▁ image",
+ "B U",
+ "▁term s",
+ "▁ter ms",
+ "▁s earch",
+ "▁se arch",
+ "▁sear ch",
+ "▁ search",
+ "▁ è",
+ "▁V al",
+ "▁Va l",
+ "▁ Val",
+ "▁ ‘",
+ "▁D av",
+ "▁Da v",
+ "M S",
+ "sr c",
+ "s rc",
+ "ma r",
+ "m ar",
+ "in cip",
+ "inc ip",
+ "▁could n",
+ "ad os",
+ "ado s",
+ "▁d ro",
+ "▁dr o",
+ "▁ dro",
+ "be ta",
+ "bet a",
+ "b eta",
+ "im um",
+ "▁min utes",
+ "▁minute s",
+ "▁minut es",
+ "▁g rand",
+ "▁gr and",
+ "▁gran d",
+ "▁gra nd",
+ "▁ grand",
+ "▁ »",
+ "▁O ur",
+ "▁ Our",
+ "St r",
+ "S tr",
+ "VE R",
+ "V ER",
+ "ma z",
+ "m az",
+ "▁or iginal",
+ "▁orig inal",
+ "▁origin al",
+ "▁ original",
+ "in i",
+ "i ni",
+ "▁c oll",
+ "▁col l",
+ "▁co ll",
+ "▁ coll",
+ "lo at",
+ "▁o s",
+ "▁ os",
+ "}) ;",
+ "} );",
+ "sum mary",
+ "▁w all",
+ "▁wa ll",
+ "▁wal l",
+ "▁ wall",
+ "Col or",
+ "Co lor",
+ "▁v ers",
+ "▁ver s",
+ "▁ve rs",
+ "▁ vers",
+ "▁d ella",
+ "▁de lla",
+ "▁del la",
+ "▁dell a",
+ "▁\" \"\"",
+ "▁\"\" \"",
+ "▁ \"\"\"",
+ "math bf",
+ "ze r",
+ "z er",
+ "au r",
+ "a ur",
+ "▁tr ack",
+ "▁tra ck",
+ "▁ track",
+ "▁ass oci",
+ "▁ associ",
+ "▁s uff",
+ "▁su ff",
+ "▁in de",
+ "▁i nde",
+ "▁ind e",
+ "▁ inde",
+ "ag ue",
+ "agu e",
+ "a gue",
+ "▁A pr",
+ "▁Ap r",
+ "▁ Apr",
+ "L e",
+ "ro ups",
+ "rou ps",
+ "roup s",
+ "bo ard",
+ "b oard",
+ "▁att ack",
+ "▁s eries",
+ "▁se ries",
+ "▁ser ies",
+ "▁serie s",
+ "▁ series",
+ "▁in stead",
+ "▁inst ead",
+ "ha m",
+ "h am",
+ "bo ok",
+ "b ook",
+ "▁s ix",
+ "▁si x",
+ "▁ six",
+ "▁R ec",
+ "▁Re c",
+ "▁ Rec",
+ "▁c oming",
+ "▁com ing",
+ "▁co ming",
+ "▁ coming",
+ "ur t",
+ "u rt",
+ "▁gl obal",
+ "▁glob al",
+ "▁glo bal",
+ "▁ global",
+ "▁ne cess",
+ "▁neces s",
+ "▁ necess",
+ "le ge",
+ "leg e",
+ "Po s",
+ "P os",
+ "▁le ave",
+ "▁ leave",
+ "▁p od",
+ "▁po d",
+ "▁ pod",
+ "ateg ory",
+ "ategor y",
+ "u z",
+ "▁de ep",
+ "▁ deep",
+ "▁k m",
+ "▁ km",
+ "▁out side",
+ "▁outs ide",
+ "ha s",
+ "h as",
+ "opt ions",
+ "option s",
+ "o ptions",
+ "▁S m",
+ "▁ Sm",
+ "Su b",
+ "S ub",
+ "ro ws",
+ "row s",
+ "r ows",
+ "▁в и",
+ "▁ ви",
+ "▁St ates",
+ "▁State s",
+ "▁Stat es",
+ "▁Sta tes",
+ "▁ States",
+ "▁wr ong",
+ "▁how ever",
+ "▁s em",
+ "▁se m",
+ "▁ sem",
+ "▁c atch",
+ "▁cat ch",
+ "▁ catch",
+ "\") ,",
+ "\" ),",
+ "mod el",
+ "mode l",
+ "mo del",
+ "▁h ttp",
+ "▁htt p",
+ "▁ http",
+ "▁o ption",
+ "▁opt ion",
+ "▁ option",
+ "ri e",
+ "r ie",
+ "▁с та",
+ "▁ст а",
+ "▁ ста",
+ "▁ä r",
+ "▁ är",
+ "▁en joy",
+ "▁enjo y",
+ "n u",
+ "▁p as",
+ "▁pa s",
+ "▁ pas",
+ "▁a mount",
+ "▁am ount",
+ "▁ amount",
+ "▁res pons",
+ "▁respon s",
+ "▁resp ons",
+ "▁ respons",
+ "▁In tern",
+ "▁Inter n",
+ "▁Int ern",
+ "▁ Intern",
+ "▁my self",
+ "▁o pp",
+ "▁op p",
+ "▁ opp",
+ "▁S im",
+ "▁Si m",
+ "▁ Sim",
+ "▁s ens",
+ "▁se ns",
+ "▁sen s",
+ "E d",
+ "▁( \\",
+ "▁ (\\",
+ "▁stud ents",
+ "▁student s",
+ "но в",
+ "н ов",
+ "▁point s",
+ "▁ points",
+ "ar ning",
+ "arn ing",
+ "U P",
+ "el ling",
+ "ell ing",
+ "elli ng",
+ "▁c annot",
+ "▁can not",
+ "B e",
+ "▁l ength",
+ "▁le ngth",
+ "▁ length",
+ "nu ll",
+ "n ull",
+ "ui nt",
+ "u int",
+ "wi se",
+ "w ise",
+ "▁d ouble",
+ "▁dou ble",
+ "▁doub le",
+ "▁ double",
+ "ig e",
+ "i ge",
+ "is ta",
+ "ist a",
+ "i sta",
+ "▁est ab",
+ "▁es tab",
+ "▁esta b",
+ "an ch",
+ "anc h",
+ "▁a go",
+ "▁ag o",
+ "▁ ago",
+ "▁b ound",
+ "▁bo und",
+ "▁bou nd",
+ "▁ bound",
+ "▁f a",
+ "▁ fa",
+ "▁c lean",
+ "▁cle an",
+ "▁ clean",
+ "▁sim ple",
+ "▁simpl e",
+ "▁ simple",
+ "m i",
+ "#### ####",
+ "if ier",
+ "ifi er",
+ "▁Gener al",
+ "▁Gen eral",
+ "▁Gene ral",
+ "▁ General",
+ "▁se emed",
+ "▁see med",
+ "▁seem ed",
+ "en a",
+ "e na",
+ "▁a ge",
+ "▁ag e",
+ "▁ age",
+ "но й",
+ "end if",
+ "A A",
+ "▁c aus",
+ "▁ca us",
+ "▁e duc",
+ "▁ed uc",
+ "▁ educ",
+ "▁c ell",
+ "▁ce ll",
+ "▁cel l",
+ "▁ cell",
+ "Ge ner",
+ "Gen er",
+ "G ener",
+ "sp ace",
+ "s pace",
+ "▁Y our",
+ "▁You r",
+ "▁ Your",
+ "▁be aut",
+ "g t",
+ "▁l imit",
+ "▁li mit",
+ "▁lim it",
+ "▁ limit",
+ "▁d ate",
+ "▁da te",
+ "▁dat e",
+ "▁ date",
+ "Ut il",
+ "U til",
+ "▁N ational",
+ "▁Nat ional",
+ "▁Nation al",
+ "▁ National",
+ "ow s",
+ "o ws",
+ "pa t",
+ "p at",
+ "qu ad",
+ "▁o k",
+ "▁ ok",
+ "▁ И",
+ "ar th",
+ "art h",
+ "ha t",
+ "h at",
+ "▁comm unity",
+ "▁commun ity",
+ "ou l",
+ "o ul",
+ "▁e conom",
+ "▁ec onom",
+ "▁ econom",
+ "Com ponent",
+ "bo r",
+ "b or",
+ "us ion",
+ "▁be low",
+ "▁bel ow",
+ "ear ch",
+ "e arch",
+ "or es",
+ "ore s",
+ "o res",
+ "ba n",
+ "b an",
+ "▁Aug ust",
+ "▁fur ther",
+ "sig ma",
+ "s igma",
+ "▁h a",
+ "▁ ha",
+ "j i",
+ "▁com put",
+ "▁comp ut",
+ "▁ comput",
+ "г ра",
+ "▁N one",
+ "▁No ne",
+ "▁Non e",
+ "▁ None",
+ "▁t er",
+ "▁te r",
+ "▁ ter",
+ "▁any one",
+ "▁t ask",
+ "▁ta sk",
+ "▁ task",
+ "en te",
+ "ent e",
+ "e nte",
+ "pos ition",
+ "pp ed",
+ "ppe d",
+ "p ped",
+ "▁a us",
+ "▁au s",
+ "▁ aus",
+ "Att ribute",
+ "Attrib ute",
+ "re q",
+ "r eq",
+ "ad dr",
+ "add r",
+ "li ght",
+ "lig ht",
+ "l ight",
+ "ш е",
+ "▁a rm",
+ "▁ar m",
+ "▁ arm",
+ "co ver",
+ "cov er",
+ "c over",
+ "up port",
+ "upp ort",
+ "▁G l",
+ "▁ Gl",
+ "▁S an",
+ "▁Sa n",
+ "▁ San",
+ "▁wr iting",
+ "▁writ ing",
+ "▁ writing",
+ "▁l ost",
+ "▁lo st",
+ "▁los t",
+ "▁M ark",
+ "▁Mar k",
+ "▁ Mark",
+ "▁g re",
+ "▁gr e",
+ "▁ gre",
+ "TY PE",
+ "T YPE",
+ "▁S outh",
+ "▁So uth",
+ "▁Sou th",
+ "▁Sout h",
+ "▁ South",
+ "▁per fect",
+ "▁perf ect",
+ "▁pack age",
+ "▁ package",
+ "▁in fl",
+ "▁inf l",
+ "▁ infl",
+ "ha ps",
+ "h aps",
+ "▁A ng",
+ "▁An g",
+ "▁ Ang",
+ "res pon",
+ "resp on",
+ "ri s",
+ "r is",
+ "pt ember",
+ "pte mber",
+ "▁build ing",
+ "▁ building",
+ "VA L",
+ "V AL",
+ "fr ee",
+ "fre e",
+ "f ree",
+ "▁c e",
+ "▁ ce",
+ "H T",
+ "▁F rom",
+ "▁Fr om",
+ "▁Fro m",
+ "▁ From",
+ "d s",
+ "ro y",
+ "r oy",
+ "ach ine",
+ "achi ne",
+ "no wn",
+ "now n",
+ "n own",
+ "▁sa ying",
+ "▁say ing",
+ "▁б ы",
+ "▁ бы",
+ "o e",
+ "Re f",
+ "R ef",
+ "▁net work",
+ "▁ network",
+ "par ent",
+ "pa rent",
+ "pare nt",
+ "paren t",
+ "p arent",
+ "ug e",
+ "u ge",
+ "▁sim ilar",
+ "> \r",
+ "Build er",
+ "B uilder",
+ "▁l iving",
+ "▁li ving",
+ "▁liv ing",
+ "▁contin ue",
+ "▁continu e",
+ "▁ continue",
+ "an ger",
+ "ang er",
+ "ange r",
+ "▁R ed",
+ "▁Re d",
+ "▁ Red",
+ "▁h air",
+ "▁ha ir",
+ "an ced",
+ "ance d",
+ "anc ed",
+ "ia ns",
+ "ian s",
+ "i ans",
+ "▁d ead",
+ "▁de ad",
+ "▁ dead",
+ "▁bo olean",
+ "▁ boolean",
+ "ic ation",
+ "▁д е",
+ "▁ де",
+ "▁cl ient",
+ "▁ client",
+ "uc t",
+ "u ct",
+ "▁ •",
+ "S P",
+ "ol der",
+ "old er",
+ "п е",
+ "ud io",
+ "udi o",
+ "▁d eg",
+ "▁de g",
+ "▁ deg",
+ "as ing",
+ "asi ng",
+ "a sing",
+ "▁st ep",
+ "▁ste p",
+ "▁ step",
+ "▁p ers",
+ "▁per s",
+ "▁pe rs",
+ "▁ pers",
+ "ç ão",
+ "ob j",
+ "o z",
+ "ul a",
+ "u la",
+ "▁r ound",
+ "▁ro und",
+ "▁rou nd",
+ "▁ round",
+ "▁u pon",
+ "▁up on",
+ "▁re source",
+ "▁res ource",
+ "▁ resource",
+ "▁val id",
+ "▁ valid",
+ "▁I I",
+ "▁ II",
+ "bu g",
+ "b ug",
+ "st d",
+ "s td",
+ "▁a ng",
+ "▁an g",
+ "▁ ang",
+ "sp an",
+ "s pan",
+ "po l",
+ "p ol",
+ "ial og",
+ "ia log",
+ "▁p hot",
+ "▁ph ot",
+ "? '",
+ "D B",
+ "▁F in",
+ "▁Fi n",
+ "▁ Fin",
+ "V E",
+ "E m",
+ "▁c am",
+ "▁ca m",
+ "▁ cam",
+ "tar get",
+ "t arget",
+ "pe cted",
+ "pect ed",
+ "pec ted",
+ "He l",
+ "H el",
+ "▁u t",
+ "▁ ut",
+ "▁T est",
+ "▁Te st",
+ "▁Tes t",
+ "▁ Test",
+ "▁t own",
+ "▁to wn",
+ "▁tow n",
+ "▁ town",
+ "al ign",
+ "ali gn",
+ "▁we bs",
+ "▁web s",
+ "in ner",
+ "inn er",
+ "au gh",
+ "aug h",
+ "a ugh",
+ "▁ex cept",
+ "▁ except",
+ "▁init ial",
+ "▁initi al",
+ "▁ initial",
+ "en ty",
+ "ent y",
+ "lic h",
+ "li ch",
+ "l ich",
+ "▁A ut",
+ "▁Au t",
+ "▁ Aut",
+ "to p",
+ "t op",
+ "▁f ail",
+ "▁fa il",
+ "▁ fail",
+ "on a",
+ "o na",
+ "▁ben ef",
+ "an ks",
+ "ank s",
+ "is che",
+ "isch e",
+ "isc he",
+ "i sche",
+ ". *",
+ "▁sign ific",
+ "▁cont act",
+ "▁ contact",
+ "Re c",
+ "R ec",
+ "ar io",
+ "ari o",
+ "a rio",
+ "ot tom",
+ "ott om",
+ "otto m",
+ "▁rel ationship",
+ "▁relations hip",
+ "▁relation ship",
+ "]) ;",
+ "] );",
+ "▁Н а",
+ "▁ На",
+ "He ad",
+ "H ead",
+ "form at",
+ "for mat",
+ "▁é t",
+ "▁ ét",
+ "▁M ore",
+ "▁Mor e",
+ "▁Mo re",
+ "▁ More",
+ "act ory",
+ "actor y",
+ "port un",
+ "+ \\",
+ "▁sim ply",
+ "▁simpl y",
+ "▁e p",
+ "▁ ep",
+ "▁R uss",
+ "▁Ru ss",
+ "▁Rus s",
+ "n í",
+ "u a",
+ "er c",
+ "e rc",
+ "▁long er",
+ "▁lon ger",
+ "in ition",
+ "init ion",
+ "ect or",
+ "ec tor",
+ "e ctor",
+ "apt ion",
+ "a ption",
+ "▁prof ess",
+ "▁profes s",
+ "▁M us",
+ "▁Mu s",
+ "▁ Mus",
+ "il ities",
+ "ili ties",
+ "è s",
+ "▁A ct",
+ "▁Ac t",
+ "▁ Act",
+ "off set",
+ "offs et",
+ "▁i ll",
+ "▁il l",
+ "▁ ill",
+ "ba nd",
+ "ban d",
+ "b and",
+ "▁A g",
+ "▁ Ag",
+ "▁П о",
+ "▁ По",
+ "б и",
+ "cont ent",
+ "ic on",
+ "ico n",
+ "i con",
+ "▁work s",
+ "▁wor ks",
+ "▁ works",
+ "yn am",
+ "yna m",
+ "y nam",
+ "pl ement",
+ "ple ment",
+ "p lement",
+ "Res ource",
+ "Re source",
+ "Act ion",
+ "A ction",
+ "▁diff icult",
+ "▁W est",
+ "▁We st",
+ "▁Wes t",
+ "▁ West",
+ "▁v ideo",
+ "▁vide o",
+ "▁ video",
+ "▁T HE",
+ "▁TH E",
+ "▁ THE",
+ "▁de cl",
+ "▁dec l",
+ "▁ decl",
+ "on don",
+ "ond on",
+ "ondo n",
+ "de d",
+ "d ed",
+ "}{ \\",
+ "} {\\",
+ "oc r",
+ "o cr",
+ "▁C ity",
+ "▁Cit y",
+ "▁Ci ty",
+ "▁ City",
+ "▁ я",
+ "ue r",
+ "u er",
+ "c z",
+ "▁im ag",
+ "▁i mag",
+ "▁ imag",
+ "c r",
+ "et e",
+ "e te",
+ "id get",
+ "idge t",
+ "▁M od",
+ "▁Mo d",
+ "▁ Mod",
+ "▁for ward",
+ "▁ forward",
+ "▁p ict",
+ "▁pi ct",
+ "▁pic t",
+ "or ge",
+ "org e",
+ "▁sub ject",
+ "▁ subject",
+ "up date",
+ "at tle",
+ "att le",
+ "s a",
+ "▁A nt",
+ "▁An t",
+ "▁ Ant",
+ "▁r unning",
+ "▁run ning",
+ "▁ running",
+ "▁s al",
+ "▁sa l",
+ "▁ sal",
+ "con ne",
+ "conn e",
+ "c onne",
+ "▁out put",
+ "▁ output",
+ "ad ata",
+ "ada ta",
+ "a data",
+ "M L",
+ "Che ck",
+ "C heck",
+ "led ge",
+ "l edge",
+ "▁p aper",
+ "▁pa per",
+ "▁pap er",
+ "▁ paper",
+ "param s",
+ "par ams",
+ "para ms",
+ "av y",
+ "a vy",
+ "▁a f",
+ "▁ af",
+ "▁e ine",
+ "▁ein e",
+ "▁j our",
+ "▁jo ur",
+ "▁jou r",
+ "▁ jour",
+ "A Y",
+ "▁it self",
+ "▁its elf",
+ "▁S tr",
+ "▁St r",
+ "▁ Str",
+ "st yle",
+ "sty le",
+ "Th at",
+ "T hat",
+ "▁m illion",
+ "▁mill ion",
+ "▁l anguage",
+ "▁ language",
+ "O S",
+ "vi ng",
+ "vin g",
+ "v ing",
+ "▁м а",
+ "▁ ма",
+ "▁т о",
+ "▁ то",
+ ") (",
+ "▁b uy",
+ "▁bu y",
+ ". /",
+ "▁. ..",
+ "▁.. .",
+ "▁ ...",
+ "▁t ried",
+ "▁tr ied",
+ "▁tri ed",
+ "▁com pl",
+ "▁comp l",
+ "▁act iv",
+ "▁ activ",
+ "ap ped",
+ "app ed",
+ "appe d",
+ "a pped",
+ "But ton",
+ "B utton",
+ "To ken",
+ "Tok en",
+ "T oken",
+ "▁prov ided",
+ "▁provide d",
+ "ib er",
+ "ibe r",
+ "i ber",
+ "▁c reated",
+ "▁cre ated",
+ "▁create d",
+ "▁creat ed",
+ "▁ created",
+ "cur ity",
+ "c urity",
+ "En d",
+ "E nd",
+ "a ł",
+ "us ter",
+ "ust er",
+ "u ster",
+ "iz ing",
+ "izi ng",
+ "i zing",
+ "om b",
+ "o mb",
+ "▁s ich",
+ "▁si ch",
+ "▁com pon",
+ "▁comp on",
+ "▁S ee",
+ "▁Se e",
+ "▁ See",
+ "▁u int",
+ "▁ui nt",
+ "▁ uint",
+ "▁l abel",
+ "▁la bel",
+ "▁lab el",
+ "▁ label",
+ "vo l",
+ "v ol",
+ "ó w",
+ "oc ol",
+ "oco l",
+ "o col",
+ "▁re ceived",
+ "▁rece ived",
+ "▁receive d",
+ "▁in tern",
+ "▁int ern",
+ "▁inter n",
+ "▁inte rn",
+ "▁ intern",
+ "ц е",
+ "R un",
+ "▁r oad",
+ "▁ro ad",
+ "▁ road",
+ "▁O ct",
+ "▁ Oct",
+ "▁C omp",
+ "▁Com p",
+ "▁Co mp",
+ "▁ Comp",
+ "▁stud y",
+ "▁т е",
+ "▁ те",
+ "Ac t",
+ "A ct",
+ "▁t our",
+ "▁to ur",
+ "▁tou r",
+ "▁St ate",
+ "▁Stat e",
+ "▁Sta te",
+ "▁ State",
+ "▁ad ded",
+ "▁add ed",
+ "▁ added",
+ "htt ps",
+ "http s",
+ "st ream",
+ "stre am",
+ "▁l ower",
+ "▁lo wer",
+ "▁low er",
+ "▁ lower",
+ "▁b ox",
+ "▁bo x",
+ "▁ box",
+ "▁S k",
+ "▁ Sk",
+ "▁them selves",
+ "▁c ross",
+ "▁cr oss",
+ "▁cro ss",
+ "▁ cross",
+ "▁e cho",
+ "▁ec ho",
+ "▁ echo",
+ "▁dev ice",
+ "▁ device",
+ "pos e",
+ "po se",
+ "p ose",
+ "▁g ames",
+ "▁game s",
+ "▁gam es",
+ "▁ga mes",
+ "P L",
+ "W indow",
+ "is es",
+ "ise s",
+ "i ses",
+ "ti tle",
+ "tit le",
+ "t itle",
+ "St ream",
+ "z t",
+ "▁S w",
+ "▁ Sw",
+ "▁r ole",
+ "▁ro le",
+ "▁ role",
+ "ia nt",
+ "ian t",
+ "i ant",
+ "k u",
+ "se qu",
+ "seq u",
+ "s equ",
+ "▁l ate",
+ "▁la te",
+ "▁lat e",
+ "▁ late",
+ "▁s old",
+ "▁so ld",
+ "▁sol d",
+ "р я",
+ "Com m",
+ "Co mm",
+ "C omm",
+ "▁en tre",
+ "▁ent re",
+ "▁entr e",
+ "▁ entre",
+ "▁d og",
+ "▁do g",
+ "▁ dog",
+ "dev ice",
+ "P ar",
+ "▁like ly",
+ "▁lik ely",
+ "▁ likely",
+ "^{ -",
+ "^ {-",
+ "▁l en",
+ "▁le n",
+ "▁ len",
+ "▁P aul",
+ "▁Pa ul",
+ "▁ Paul",
+ "▁t ool",
+ "▁to ol",
+ "▁too l",
+ "▁ tool",
+ "Of f",
+ "O ff",
+ "▁f amil",
+ "▁fam il",
+ "▁fa mil",
+ "▁d raw",
+ "▁dr aw",
+ "▁ draw",
+ "ap ping",
+ "app ing",
+ "a pping",
+ "▁ev ents",
+ "▁even ts",
+ "▁event s",
+ "▁ events",
+ "cre t",
+ "cr et",
+ "c ret",
+ "rou ght",
+ "rough t",
+ "r ought",
+ "Cont ent",
+ "▁soft ware",
+ "ri a",
+ "r ia",
+ "ms g",
+ "m sg",
+ "ga mma",
+ "g amma",
+ "▁h ear",
+ "▁he ar",
+ "Op er",
+ "O per",
+ "▁your self",
+ "▁yours elf",
+ "▁l iter",
+ "▁li ter",
+ "▁lit er",
+ "▁ liter",
+ "em p",
+ "e mp",
+ "▁se par",
+ "▁sep ar",
+ "▁ separ",
+ "▁ З",
+ "▁t itle",
+ "▁tit le",
+ "▁ti tle",
+ "▁ title",
+ "M ethod",
+ "math rm",
+ "▁s low",
+ "▁sl ow",
+ "▁R om",
+ "▁Ro m",
+ "▁ Rom",
+ "! !",
+ "▁t ax",
+ "▁ta x",
+ "▁ tax",
+ "ск а",
+ "с ка",
+ "empl ate",
+ "emp late",
+ "o i",
+ "▁A rt",
+ "▁Ar t",
+ "▁ Art",
+ "f alse",
+ "ast ic",
+ "ст ь",
+ "с ть",
+ "oc ket",
+ "ock et",
+ "▁e ns",
+ "▁en s",
+ "▁ ens",
+ "T O",
+ "am ente",
+ "ame nte",
+ "ament e",
+ "amen te",
+ "a mente",
+ "lo cal",
+ "loc al",
+ "l ocal",
+ "ch ie",
+ "chi e",
+ "▁p an",
+ "▁pa n",
+ "▁ pan",
+ "ни й",
+ "ch ema",
+ "che ma",
+ "chem a",
+ "▁N orth",
+ "▁Nor th",
+ "▁Nort h",
+ "з о",
+ "▁> =",
+ "▁ >=",
+ "A ut",
+ "▁d ig",
+ "▁di g",
+ "▁ dig",
+ "▁se ems",
+ "▁see ms",
+ "▁seem s",
+ "▁mor ning",
+ "so le",
+ "sol e",
+ "s ole",
+ "um er",
+ "ume r",
+ "u mer",
+ "del ta",
+ "d elta",
+ "it é",
+ "i té",
+ "ab ase",
+ "aba se",
+ "a base",
+ "ra f",
+ "r af",
+ "▁ob serv",
+ "▁obs erv",
+ "▁ observ",
+ "▁E st",
+ "▁Es t",
+ "▁ Est",
+ "▁s eg",
+ "▁se g",
+ "▁ seg",
+ "▁[ ]",
+ "▁ []",
+ "▁P res",
+ "▁Pr es",
+ "▁Pre s",
+ "▁ Pres",
+ "if ul",
+ "i ful",
+ "pu sh",
+ "pus h",
+ "p ush",
+ "▁O ff",
+ "▁Of f",
+ "▁ Off",
+ "ip e",
+ "i pe",
+ "at i",
+ "a ti",
+ "▁d im",
+ "▁di m",
+ "▁ dim",
+ "ce ed",
+ "c eed",
+ "En t",
+ "E nt",
+ "__ __",
+ "___ _",
+ "_ ___",
+ "en try",
+ "ent ry",
+ "entr y",
+ "▁f ight",
+ "▁fig ht",
+ "▁fi ght",
+ "▁c red",
+ "▁cre d",
+ "▁cr ed",
+ "▁ cred",
+ "▁O R",
+ "▁ OR",
+ "▁D ep",
+ "▁De p",
+ "▁ Dep",
+ "$ {",
+ "ле н",
+ "л ен",
+ "Creat e",
+ "C reate",
+ "▁Apr il",
+ "▁Ap ril",
+ "min istr",
+ "F L",
+ "▁A p",
+ "▁ Ap",
+ "▁H ere",
+ "▁He re",
+ "▁Her e",
+ "▁ Here",
+ "priv ate",
+ "p rivate",
+ "In stance",
+ "Inst ance",
+ "ie m",
+ "i em",
+ "▁off ice",
+ "▁offic e",
+ "▁th ird",
+ "▁ third",
+ "▁up date",
+ "▁ update",
+ "Lin e",
+ "Li ne",
+ "L ine",
+ "ta g",
+ "t ag",
+ "▁e specially",
+ "▁espec ially",
+ "▁especial ly",
+ "▁ especially",
+ "▁го да",
+ "▁год а",
+ "▁c u",
+ "▁ cu",
+ "▁k ill",
+ "▁kil l",
+ "▁ki ll",
+ "▁ kill",
+ "au ght",
+ "augh t",
+ "aug ht",
+ "▁s we",
+ "▁sw e",
+ "Option s",
+ "Opt ions",
+ "O ptions",
+ "I M",
+ "C C",
+ "▁com pan",
+ "▁comp an",
+ "ju st",
+ "j ust",
+ "▁Wh ile",
+ "▁ While",
+ "iz er",
+ "ize r",
+ "i zer",
+ "▁м о",
+ "▁ мо",
+ "к е",
+ "▁a uto",
+ "▁aut o",
+ "▁au to",
+ "▁ auto",
+ "▁b and",
+ "▁ban d",
+ "▁ba nd",
+ "▁ band",
+ "ме н",
+ "м ен",
+ "ique s",
+ "iqu es",
+ "iq ues",
+ "i ques",
+ "▁p le",
+ "▁pl e",
+ "▁ ple",
+ "N O",
+ "▁O F",
+ "▁ OF",
+ "▁s ong",
+ "▁so ng",
+ "▁son g",
+ "▁A cc",
+ "▁Ac c",
+ "▁ Acc",
+ "EX T",
+ "E XT",
+ "en sor",
+ "ens or",
+ "enso r",
+ "in ing",
+ "ini ng",
+ "i ning",
+ "▁l at",
+ "▁la t",
+ "▁ lat",
+ "bi g",
+ "b ig",
+ "▁K ing",
+ "▁Ki ng",
+ "▁Kin g",
+ "▁ King",
+ "oc h",
+ "o ch",
+ "s i",
+ "▁H ist",
+ "▁His t",
+ "▁Hi st",
+ "▁ Hist",
+ "▁qu ality",
+ "▁qual ity",
+ "▁ quality",
+ "mod e",
+ "mo de",
+ "m ode",
+ "▁op portun",
+ "▁would n",
+ ":* *",
+ ": **",
+ "out put",
+ "▁fe et",
+ "▁fee t",
+ "▁m is",
+ "▁mi s",
+ "d f",
+ "ag ing",
+ "agi ng",
+ "a ging",
+ "▁м е",
+ "▁ ме",
+ "▁t ro",
+ "▁tr o",
+ "▁d efined",
+ "▁def ined",
+ "▁define d",
+ "▁defin ed",
+ "▁ defined",
+ "▁re view",
+ "▁rev iew",
+ "▁ review",
+ "▁F il",
+ "▁Fi l",
+ "▁ Fil",
+ "> >",
+ "▁pr incip",
+ "▁prin cip",
+ "Bas e",
+ "B ase",
+ "di ct",
+ "d ict",
+ "ve rage",
+ "ver age",
+ "ic ient",
+ "ici ent",
+ "I F",
+ "▁h it",
+ "▁hi t",
+ "▁ hit",
+ "Pag e",
+ "P age",
+ "▁p erm",
+ "▁per m",
+ "▁pe rm",
+ "▁ perm",
+ "ce l",
+ "c el",
+ "í t",
+ "▁ex press",
+ "▁exp ress",
+ "▁expr ess",
+ "▁ express",
+ "▁ind ic",
+ "▁Se ptember",
+ "▁Sept ember",
+ "im age",
+ "ima ge",
+ "imag e",
+ "▁product s",
+ "▁ products",
+ "▁m edia",
+ "▁med ia",
+ "▁medi a",
+ "▁ media",
+ "ch ange",
+ "chan ge",
+ "ig ger",
+ "igg er",
+ "▁s end",
+ "▁se nd",
+ "▁sen d",
+ "▁ send",
+ "la st",
+ "las t",
+ "l ast",
+ "min g",
+ "mi ng",
+ "m ing",
+ "p a",
+ "ua ry",
+ "uar y",
+ "u ary",
+ "▁spe ak",
+ "ны й",
+ "щ е",
+ "ys is",
+ "y sis",
+ "ly ing",
+ "l ying",
+ "▁ ч",
+ "li ke",
+ "lik e",
+ "l ike",
+ "р ы",
+ "в і",
+ "▁M ich",
+ "▁Mic h",
+ "▁Mi ch",
+ "M O",
+ "▁J ah",
+ "▁Ja h",
+ "ens ive",
+ "▁sh are",
+ "▁shar e",
+ "▁sha re",
+ "▁ share",
+ "▁develop ment",
+ "C P",
+ "sp ec",
+ "spe c",
+ "s pec",
+ "▁f ast",
+ "▁fa st",
+ "▁ fast",
+ "he t",
+ "h et",
+ "H O",
+ "▁part icip",
+ "▁partic ip",
+ "▁parti cip",
+ "Bl ock",
+ "Blo ck",
+ "B lock",
+ "▁vi ol",
+ "▁fr ame",
+ "▁fra me",
+ "▁fram e",
+ "▁ frame",
+ "▁qu al",
+ "▁q ual",
+ "▁ qual",
+ "tr e",
+ "t re",
+ "▁ Ф",
+ "▁to ward",
+ "▁tow ard",
+ "f g",
+ "Bo x",
+ "B ox",
+ "Col umn",
+ "▁mil it",
+ "▁mi lit",
+ "▁M arch",
+ "▁Mar ch",
+ "▁Marc h",
+ "▁var ious",
+ "▁vari ous",
+ "pa ss",
+ "pas s",
+ "p ass",
+ "▁P ark",
+ "▁Par k",
+ "▁B en",
+ "▁Be n",
+ "▁ Ben",
+ "Fr ame",
+ "▁n ormal",
+ "▁nor mal",
+ "▁norm al",
+ "▁ normal",
+ "op en",
+ "ope n",
+ "o pen",
+ "p x",
+ "▁ph one",
+ "▁ phone",
+ "▁E ven",
+ "▁Ev en",
+ "▁Eve n",
+ "▁ Even",
+ "▁m a",
+ "▁ ma",
+ "ibr ary",
+ "St art",
+ "Star t",
+ "id den",
+ "idd en",
+ "rh o",
+ "r ho",
+ "gr aph",
+ "gra ph",
+ "g raph",
+ "ac ing",
+ "aci ng",
+ "a cing",
+ "' .",
+ "ar ter",
+ "art er",
+ "arte r",
+ "me s",
+ "m es",
+ "in st",
+ "ins t",
+ "▁i r",
+ "▁ ir",
+ "act ive",
+ "activ e",
+ "▁f em",
+ "▁fe m",
+ "▁ fem",
+ "▁m oved",
+ "▁mov ed",
+ "▁move d",
+ "▁mo ved",
+ "▁st ore",
+ "▁stor e",
+ "▁sto re",
+ "▁ store",
+ "▁p rice",
+ "▁pr ice",
+ "▁pri ce",
+ "▁ price",
+ "\") .",
+ "\" ).",
+ "ber g",
+ "be rg",
+ "b erg",
+ "▁n ov",
+ "▁no v",
+ "▁ nov",
+ "▁c ard",
+ "▁car d",
+ "▁ca rd",
+ "▁ card",
+ "el low",
+ "ell ow",
+ "ello w",
+ "▁part y",
+ "▁par ty",
+ "▁ party",
+ "▁M or",
+ "▁Mo r",
+ "ae l",
+ "a el",
+ "▁per cent",
+ "▁ percent",
+ "▁tr aining",
+ "▁tra ining",
+ "▁train ing",
+ "▁ training",
+ "▁in g",
+ "▁i ng",
+ "▁ ing",
+ "im er",
+ "ime r",
+ "i mer",
+ "▁S am",
+ "▁Sa m",
+ "▁ Sam",
+ "Def ault",
+ "▁f uck",
+ "▁fu ck",
+ "▁com plete",
+ "▁comp lete",
+ "▁complet e",
+ "▁compl ete",
+ "▁ complete",
+ "ui d",
+ "u id",
+ "▁det ails",
+ "▁detail s",
+ "▁ details",
+ "▁l ed",
+ "▁le d",
+ "▁ led",
+ "Po int",
+ "P oint",
+ "▁C ount",
+ "▁Co unt",
+ "▁Coun t",
+ "▁Cou nt",
+ "▁ Count",
+ "▁reg ard",
+ "z o",
+ "▁B ro",
+ "▁Br o",
+ "▁ Bro",
+ "▁rec ogn",
+ "▁ recogn",
+ "▁H ol",
+ "▁Ho l",
+ "▁ Hol",
+ "U M",
+ "el ement",
+ "ele ment",
+ "elem ent",
+ "e lement",
+ "Mod e",
+ "Mo de",
+ "M ode",
+ "▁ex am",
+ "▁E X",
+ "▁ EX",
+ "Im age",
+ "ver se",
+ "vers e",
+ "ri ter",
+ "rit er",
+ "rite r",
+ "r iter",
+ "so ft",
+ "s oft",
+ "▁int rodu",
+ "▁intro du",
+ "▁sur pr",
+ "Buf fer",
+ "Buff er",
+ "B uffer",
+ "le ctor",
+ "lect or",
+ "l ector",
+ "ar en",
+ "are n",
+ "a ren",
+ "an ged",
+ "ang ed",
+ "ange d",
+ "▁P at",
+ "▁Pa t",
+ "▁ Pat",
+ "▁P al",
+ "▁Pa l",
+ "▁ Pal",
+ "▁con tr",
+ "▁cont r",
+ "▁ contr",
+ "Hand ler",
+ "Handle r",
+ "▁fe atures",
+ "▁feature s",
+ "▁feat ures",
+ "▁ features",
+ "ip le",
+ "i ple",
+ "▁C ON",
+ "▁CO N",
+ "▁ CON",
+ "Fi l",
+ "F il",
+ "▁P ort",
+ "▁Po rt",
+ "▁Por t",
+ "▁ Port",
+ "▁th inking",
+ "▁think ing",
+ "▁thin king",
+ "do c",
+ "d oc",
+ "we r",
+ "w er",
+ "▁work ed",
+ "▁wor ked",
+ "P C",
+ "c m",
+ "da t",
+ "d at",
+ "PR O",
+ "P RO",
+ "▁E very",
+ "▁Ev ery",
+ "▁Ever y",
+ "▁Eve ry",
+ "▁ Every",
+ "▁e ra",
+ "▁er a",
+ "▁ era",
+ "▁F irst",
+ "▁ First",
+ "g n",
+ "▁im medi",
+ "▁imm edi",
+ "ov ember",
+ "ove mber",
+ "ap an",
+ "apa n",
+ "a pan",
+ "▁ex tra",
+ "▁ext ra",
+ "▁extr a",
+ "▁ extra",
+ "▁s ection",
+ "▁se ction",
+ "▁sect ion",
+ "▁ section",
+ "▁J une",
+ "▁Jun e",
+ "▁Ju ne",
+ "▁v ia",
+ "▁vi a",
+ "▁ via",
+ "▁g one",
+ "▁go ne",
+ "com e",
+ "co me",
+ "c ome",
+ "▁s tri",
+ "▁st ri",
+ "▁str i",
+ "▁ stri",
+ "^ \\",
+ "ant ly",
+ "▁ar ch",
+ "▁arc h",
+ "▁ arch",
+ "S ource",
+ "▁con v",
+ "▁co nv",
+ "▁ conv",
+ "▁L ondon",
+ "▁Lond on",
+ "▁ London",
+ "Num ber",
+ "N umber",
+ "▁quest ions",
+ "▁question s",
+ "an did",
+ "and id",
+ "▁play ed",
+ "en v",
+ "e nv",
+ "▁Sch ool",
+ "▁nat ural",
+ "▁natur al",
+ "▁ natural",
+ "ca n",
+ "c an",
+ "▁ne ws",
+ "▁new s",
+ "▁ news",
+ "D R",
+ "▁c hall",
+ "▁ch all",
+ "▁cha ll",
+ "▁S oc",
+ "▁So c",
+ "▁ э",
+ "▁att empt",
+ "* }",
+ "N ull",
+ "ro te",
+ "rot e",
+ "r ote",
+ "▁b i",
+ "▁ bi",
+ "▁wr itten",
+ "▁writ ten",
+ "▁ written",
+ "▁bl ood",
+ "▁blo od",
+ "▁happ ened",
+ "▁happen ed",
+ "▁c ause",
+ "▁caus e",
+ "▁ca use",
+ "as hing",
+ "ash ing",
+ "ashi ng",
+ "▁Will iam",
+ "ad em",
+ "ade m",
+ "a dem",
+ "▁b rought",
+ "▁br ought",
+ "▁dis play",
+ "▁displ ay",
+ "▁disp lay",
+ "▁ display",
+ "im a",
+ "i ma",
+ "▁fin ally",
+ "▁final ly",
+ "ta b",
+ "t ab",
+ "▁return ed",
+ "ны х",
+ "ni e",
+ "n ie",
+ "▁ q",
+ "▁h ers",
+ "▁he rs",
+ "▁her s",
+ "▁P re",
+ "▁Pr e",
+ "▁ Pre",
+ "▁d ou",
+ "▁do u",
+ "buf fer",
+ "buff er",
+ "b uffer",
+ "▁eff ort",
+ "ain e",
+ "ai ne",
+ "a ine",
+ "x y",
+ "▁his tor",
+ "▁hist or",
+ "en u",
+ "e nu",
+ "▁ar riv",
+ "▁arr iv",
+ "▁D em",
+ "▁De m",
+ "▁ Dem",
+ "▁f avor",
+ "▁fa vor",
+ "▁fav or",
+ "▁hand le",
+ "▁ handle",
+ "SE T",
+ "S ET",
+ "▁P ublic",
+ "▁Pub lic",
+ "▁Pu blic",
+ "▁ Public",
+ "ru pt",
+ "rup t",
+ "r upt",
+ "▁u r",
+ "▁ ur",
+ "▁for ce",
+ "▁ force",
+ "▁é s",
+ "▁ és",
+ "ub e",
+ "u be",
+ "Pr e",
+ "P re",
+ "р і",
+ "in y",
+ "i ny",
+ "th eta",
+ "the ta",
+ "is f",
+ "i sf",
+ "▁n ational",
+ "▁nat ional",
+ "▁nation al",
+ "Equ al",
+ "Eq ual",
+ "E qual",
+ "ren ch",
+ "▁w ife",
+ "▁c apt",
+ "▁cap t",
+ "▁ca pt",
+ "▁In ter",
+ "▁Int er",
+ "▁ Inter",
+ "ta u",
+ "t au",
+ "▁s leep",
+ "▁sle ep",
+ "▁ sleep",
+ "../ ../",
+ "▁iss ue",
+ "▁ issue",
+ "▁m ember",
+ "▁me mber",
+ "▁mem ber",
+ "▁ member",
+ "▁a wait",
+ "▁aw ait",
+ "▁ await",
+ "▁D an",
+ "▁Da n",
+ "▁ Dan",
+ "z i",
+ "in ate",
+ "ina te",
+ "i nate",
+ "▁s ym",
+ "▁sy m",
+ "▁ sym",
+ "ch an",
+ "cha n",
+ "c han",
+ "▁J ack",
+ "▁Jac k",
+ "▁Ja ck",
+ "▁ Jack",
+ "▁Eng lish",
+ "▁ English",
+ "▁s z",
+ "▁ sz",
+ "rib utes",
+ "ribut es",
+ "ribute s",
+ "ribu tes",
+ "▁i gn",
+ "▁ig n",
+ "▁ ign",
+ "á l",
+ "▁app ear",
+ "▁appe ar",
+ "ra d",
+ "r ad",
+ "id ge",
+ "▁co uple",
+ "▁cou ple",
+ "▁coup le",
+ "▁s hip",
+ "▁sh ip",
+ "▁ ship",
+ "li g",
+ "l ig",
+ "we b",
+ "w eb",
+ "▁us ually",
+ "▁usual ly",
+ "▁re ady",
+ "▁read y",
+ "▁ ready",
+ "▁v ill",
+ "▁vi ll",
+ "▁vil l",
+ "▁W hy",
+ "▁Wh y",
+ "▁ Why",
+ "eb ru",
+ "e bru",
+ "▁g rad",
+ "▁gr ad",
+ "▁gra d",
+ "▁ grad",
+ "or ds",
+ "ord s",
+ "▁in f",
+ "▁i nf",
+ "▁ inf",
+ "▁l oss",
+ "▁lo ss",
+ "▁los s",
+ "▁ loss",
+ "▁o d",
+ "▁ od",
+ "▁Ph il",
+ "▁ Phil",
+ "ser ver",
+ "serv er",
+ "serve r",
+ "▁U p",
+ "▁ Up",
+ "▁b uff",
+ "▁bu ff",
+ "▁buf f",
+ "▁ buff",
+ "▁fil ename",
+ "▁file name",
+ "▁ filename",
+ "AB LE",
+ "it ing",
+ "iti ng",
+ "i ting",
+ "ef ore",
+ "e fore",
+ "() ->",
+ "( )->",
+ "▁cond itions",
+ "▁condition s",
+ "▁ conditions",
+ "v m",
+ "el d",
+ "e ld",
+ "it z",
+ "i tz",
+ "▁Tr ans",
+ "▁Tra ns",
+ "▁ Trans",
+ "▁w eight",
+ "▁we ight",
+ "▁weigh t",
+ "▁ weight",
+ "▁high er",
+ "▁hig her",
+ "▁r ate",
+ "▁rat e",
+ "▁ra te",
+ "▁ rate",
+ "▁acc om",
+ "▁ac com",
+ "vi der",
+ "vid er",
+ "v ider",
+ "O M",
+ "▁w ays",
+ "▁way s",
+ "▁wa ys",
+ "▁ ways",
+ "com ing",
+ "co ming",
+ "c oming",
+ "▁l ock",
+ "▁loc k",
+ "▁lo ck",
+ "▁ lock",
+ "▁e tc",
+ "▁et c",
+ "▁ etc",
+ "▁a vec",
+ "▁av ec",
+ "▁ave c",
+ "▁t akes",
+ "▁take s",
+ "▁tak es",
+ "▁ta kes",
+ "▁C har",
+ "▁Ch ar",
+ "▁Cha r",
+ "▁ Char",
+ "▁N ovember",
+ "▁Nov ember",
+ "m ethod",
+ "▁A ustral",
+ "▁Aust ral",
+ "▁ Austral",
+ "▁Amer ica",
+ "▁ America",
+ "lo ng",
+ "lon g",
+ "l ong",
+ "ce mber",
+ "c ember",
+ "▁polit ical",
+ "fl ow",
+ "f low",
+ "▁may be",
+ "▁ maybe",
+ "▁a mb",
+ "▁am b",
+ "▁ amb",
+ "La yout",
+ "L ayout",
+ "il ed",
+ "ile d",
+ "i led",
+ "om en",
+ "ome n",
+ "o men",
+ "ol a",
+ "o la",
+ "ic ip",
+ "ici p",
+ "i cip",
+ "part ial",
+ "Tr ue",
+ "▁f loor",
+ "▁fl oor",
+ "▁flo or",
+ "▁ floor",
+ "▁D ef",
+ "▁De f",
+ "▁ Def",
+ "▁conc ern",
+ "▁conce rn",
+ "▁concer n",
+ "y r",
+ "▁sh ows",
+ "▁show s",
+ "i h",
+ "▁an swer",
+ "▁answ er",
+ "▁ans wer",
+ "▁ answer",
+ "ac c",
+ "a cc",
+ "▁b all",
+ "▁bal l",
+ "▁ba ll",
+ "▁ ball",
+ "▁R ev",
+ "▁Re v",
+ "▁ Rev",
+ "▁s un",
+ "▁su n",
+ "▁ sun",
+ "▁quick ly",
+ "▁s omet",
+ "▁so met",
+ "▁some t",
+ "▁som et",
+ "ment e",
+ "me nte",
+ "men te",
+ "m ente",
+ "▁M al",
+ "▁Ma l",
+ "▁ Mal",
+ "und red",
+ "▁iss ues",
+ "▁issue s",
+ "▁ issues",
+ "ec ause",
+ "eca use",
+ "pe s",
+ "p es",
+ "▁p layer",
+ "▁pl ayer",
+ "▁play er",
+ "▁ player",
+ "▁par ents",
+ "▁parent s",
+ "▁ parents",
+ "▁pop ular",
+ "▁popula r",
+ "▁popul ar",
+ "▁m ode",
+ "▁mod e",
+ "▁mo de",
+ "▁ mode",
+ "▁m ention",
+ "▁ment ion",
+ "N E",
+ "Lo ad",
+ "L oad",
+ "▁reg ular",
+ "▁regul ar",
+ "▁ regular",
+ "ave d",
+ "av ed",
+ "a ved",
+ "? :",
+ "ye ar",
+ "y ear",
+ "fun c",
+ "fu nc",
+ "f unc",
+ "▁per formance",
+ "▁perform ance",
+ "▁J uly",
+ "▁Jul y",
+ "▁Ju ly",
+ "th ern",
+ "ther n",
+ "the rn",
+ "▁we bsite",
+ "▁webs ite",
+ "▁web site",
+ "fo rd",
+ "for d",
+ "f ord",
+ "P R",
+ "el a",
+ "e la",
+ "le vel",
+ "lev el",
+ "l evel",
+ "ui t",
+ "u it",
+ "fl ags",
+ "flag s",
+ "▁w orth",
+ "▁wor th",
+ "▁ worth",
+ "▁cor respon",
+ "▁Brit ish",
+ "si m",
+ "s im",
+ "▁al one",
+ "▁ alone",
+ "▁h ar",
+ "▁ha r",
+ "▁ har",
+ "▁o nes",
+ "▁on es",
+ "▁one s",
+ "▁ ones",
+ "ob ile",
+ "obi le",
+ "obil e",
+ "▁d ru",
+ "▁dr u",
+ "▁ dru",
+ "ch i",
+ "c hi",
+ "▁D avid",
+ "▁Dav id",
+ "▁Da vid",
+ "▁ David",
+ "▁proble ms",
+ "▁problem s",
+ "▁col umn",
+ "▁ column",
+ "() ;\r",
+ "(); \r",
+ "( );\r",
+ "Z E",
+ "▁re lig",
+ "▁rel ig",
+ "▁reli g",
+ "olog ical",
+ "▁reg ion",
+ "▁ region",
+ "ad y",
+ "a dy",
+ "I O",
+ "an der",
+ "and er",
+ "ande r",
+ "a nder",
+ "Ne t",
+ "N et",
+ "▁bu ilt",
+ "▁ built",
+ "▁inst all",
+ "▁ install",
+ "▁appro ach",
+ "C ur",
+ "▁f ine",
+ "▁fin e",
+ "▁fi ne",
+ "▁talk ing",
+ "▁tal king",
+ "▁ch anges",
+ "▁chang es",
+ "▁change s",
+ "▁ changes",
+ "St yle",
+ "▁M art",
+ "▁Mar t",
+ "▁Ma rt",
+ "▁ Mart",
+ "л ю",
+ "res ponse",
+ "respon se",
+ "respons e",
+ "te ger",
+ "{ \r",
+ "ir it",
+ "iri t",
+ "i rit",
+ "▁prote cted",
+ "▁protect ed",
+ "▁ protected",
+ "▁re le",
+ "▁r ele",
+ "▁rel e",
+ "er ship",
+ "ers hip",
+ "те ль",
+ "тел ь",
+ "un signed",
+ "uns igned",
+ "ial ize",
+ "▁htt ps",
+ "▁http s",
+ "▁ https",
+ "T ag",
+ "▁$ (",
+ "▁ $(",
+ "mo re",
+ "mor e",
+ "m ore",
+ "ype s",
+ "yp es",
+ "y pes",
+ "▁st ream",
+ "▁stre am",
+ "▁ stream",
+ "et ch",
+ "etc h",
+ "▁eng ine",
+ "▁ engine",
+ "K E",
+ "cm d",
+ "c md",
+ "sc ript",
+ "scri pt",
+ "scr ipt",
+ "s cript",
+ "tt p",
+ "t tp",
+ "▁a void",
+ "▁av oid",
+ "▁t err",
+ "▁te rr",
+ "▁ter r",
+ "▁r ock",
+ "▁ro ck",
+ "▁ rock",
+ "▁f ul",
+ "▁fu l",
+ "▁ ful",
+ "Up date",
+ "▁env ironment",
+ "▁environ ment",
+ "▁ environment",
+ "▁p rec",
+ "▁pre c",
+ "▁pr ec",
+ "▁ prec",
+ "▁с а",
+ "▁ са",
+ "▁c ases",
+ "▁case s",
+ "▁cas es",
+ "▁ca ses",
+ "▁ cases",
+ "▁off set",
+ "▁ offset",
+ "▁r ais",
+ "▁ra is",
+ "▁ rais",
+ "li b",
+ "l ib",
+ "ée s",
+ "é es",
+ "a a",
+ "y t",
+ "▁a rr",
+ "▁ar r",
+ "▁ arr",
+ "opy right",
+ "f irst",
+ "▁u til",
+ "▁ut il",
+ "▁ util",
+ "▁fe ature",
+ "▁feat ure",
+ "▁ feature",
+ "pos ed",
+ "po sed",
+ "pose d",
+ "p osed",
+ "ff ect",
+ "f fect",
+ "ж а",
+ "it ude",
+ "itu de",
+ "itud e",
+ "em ents",
+ "ement s",
+ "emen ts",
+ "e ments",
+ "as c",
+ "a sc",
+ "ad or",
+ "ado r",
+ "le ctions",
+ "lect ions",
+ "lection s",
+ "▁cl ub",
+ "▁ club",
+ "] {",
+ "▁* )",
+ "▁ *)",
+ "ст во",
+ "ств о",
+ "с тво",
+ "▁im m",
+ "▁i mm",
+ "▁ imm",
+ "▁for mer",
+ "▁form er",
+ "▁forme r",
+ "▁ former",
+ "▁r ights",
+ "▁right s",
+ "▁dec ided",
+ "▁decide d",
+ "▁decid ed",
+ "▁re v",
+ "▁r ev",
+ "▁ rev",
+ "▁m ent",
+ "▁me nt",
+ "▁men t",
+ "▁ ment",
+ "an i",
+ "a ni",
+ "▁st ru",
+ "▁str u",
+ "▁ stru",
+ "▁att ention",
+ "art ment",
+ "▁I tal",
+ "▁It al",
+ "al le",
+ "all e",
+ "a lle",
+ "▁b is",
+ "▁bi s",
+ "▁ bis",
+ "ge ner",
+ "gen er",
+ "g ener",
+ "▁in tegr",
+ "▁int egr",
+ "▁inte gr",
+ "▁ integr",
+ "el lo",
+ "ell o",
+ "ry pt",
+ "▁a chie",
+ "ne s",
+ "n es",
+ "▁s tra",
+ "▁st ra",
+ "▁str a",
+ "▁ stra",
+ "s b",
+ "▁t ypes",
+ "▁type s",
+ "▁typ es",
+ "▁ty pes",
+ "▁ types",
+ "▁R E",
+ "▁ RE",
+ "In it",
+ "I nit",
+ "▁com ment",
+ "▁comm ent",
+ "▁comme nt",
+ "▁ comment",
+ "▁add ition",
+ "▁I D",
+ "▁ ID",
+ "AR T",
+ "A RT",
+ "F O",
+ "щ и",
+ "Con ne",
+ "Conn e",
+ "C onne",
+ "▁s qu",
+ "▁sq u",
+ "▁consider ed",
+ "▁consid ered",
+ "id ad",
+ "ida d",
+ "▁Oct ober",
+ "ci al",
+ "cia l",
+ "c ial",
+ "▁O f",
+ "▁ Of",
+ "▁tr avel",
+ "▁tra vel",
+ "▁trav el",
+ "▁b oy",
+ "▁bo y",
+ "▁ boy",
+ "') .",
+ "' ).",
+ "u y",
+ "il la",
+ "ill a",
+ "i lla",
+ "is try",
+ "ist ry",
+ "istr y",
+ "▁v a",
+ "▁ va",
+ "▁C he",
+ "▁Ch e",
+ "▁ Che",
+ "ER T",
+ "E RT",
+ "en de",
+ "end e",
+ "e nde",
+ "un gen",
+ "ung en",
+ "unge n",
+ "ab y",
+ "a by",
+ "▁R ober",
+ "▁Ro ber",
+ "▁Rob er",
+ "▁play ing",
+ "il s",
+ "i ls",
+ "▁s am",
+ "▁sa m",
+ "▁ sam",
+ "▁ex ecut",
+ "▁exec ut",
+ "▁ execut",
+ "▁U s",
+ "▁ Us",
+ "▁m ut",
+ "▁mu t",
+ "▁ mut",
+ "▁b al",
+ "▁ba l",
+ "▁ bal",
+ "as se",
+ "ass e",
+ "▁k ids",
+ "▁kid s",
+ "▁ki ds",
+ "▁fin anc",
+ "go r",
+ "g or",
+ "▁S ec",
+ "▁Se c",
+ "▁ Sec",
+ "ber t",
+ "be rt",
+ "b ert",
+ "▁H igh",
+ "▁Hig h",
+ "▁Hi gh",
+ "▁ High",
+ "▁ је",
+ "▁ke pt",
+ "but ton",
+ "b utton",
+ "it ory",
+ "itor y",
+ "ito ry",
+ "▁R em",
+ "▁Re m",
+ "▁ Rem",
+ "▁D E",
+ "▁ DE",
+ "▁re ach",
+ "▁r each",
+ "▁ reach",
+ "▁b ur",
+ "▁bu r",
+ "▁ bur",
+ "La bel",
+ "L abel",
+ "á t",
+ "ag o",
+ "a go",
+ "▁pass ed",
+ "▁pas sed",
+ "▁be hav",
+ "▁beh av",
+ "xF F",
+ "x FF",
+ "▁R eturn",
+ "▁Re turn",
+ "▁Ret urn",
+ "▁ Return",
+ "ST R",
+ "S TR",
+ "▁L es",
+ "▁Le s",
+ "▁ Les",
+ "▁o rd",
+ "▁or d",
+ "▁ ord",
+ "al a",
+ "a la",
+ "in ger",
+ "ing er",
+ "inge r",
+ "▁S ince",
+ "▁Sin ce",
+ "▁ Since",
+ "▁exper i",
+ "▁exp eri",
+ "▁s hall",
+ "▁sh all",
+ "▁sha ll",
+ "▁ shall",
+ "▁s tar",
+ "▁st ar",
+ "▁sta r",
+ "▁ star",
+ "no n",
+ "n on",
+ "▁g un",
+ "▁gu n",
+ "▁ gun",
+ "▁B el",
+ "▁Be l",
+ "▁ Bel",
+ "▁ob j",
+ "▁ obj",
+ "ar es",
+ "are s",
+ "a res",
+ "r s",
+ "▁we eks",
+ "▁week s",
+ "ne n",
+ "n en",
+ "▁S tre",
+ "▁St re",
+ "▁Str e",
+ "or ing",
+ "ori ng",
+ "o ring",
+ "▁ î",
+ "▁ser ious",
+ "time s",
+ "ti mes",
+ "tim es",
+ "t imes",
+ "▁H ouse",
+ "▁Ho use",
+ "▁Hou se",
+ "▁r oll",
+ "▁ro ll",
+ "▁ roll",
+ "▁reg ister",
+ "▁ register",
+ "▁mod ule",
+ "▁mo dule",
+ "▁ module",
+ "▁app lic",
+ "▁ap plic",
+ "▁appl ic",
+ "I R",
+ "▁c ook",
+ "▁co ok",
+ "▁ cook",
+ "au x",
+ "a ux",
+ "▁s ave",
+ "▁sa ve",
+ "▁sav e",
+ "▁ save",
+ "▁C r",
+ "▁ Cr",
+ ", \r",
+ "▁st ates",
+ "▁stat es",
+ "▁state s",
+ "▁sta tes",
+ "▁ states",
+ "▁em pty",
+ "▁emp ty",
+ "▁empt y",
+ "▁ empty",
+ "▁aut om",
+ "▁au tom",
+ "▁auto m",
+ "▁ autom",
+ "fig ure",
+ "ian ce",
+ "i ance",
+ "▁h appy",
+ "▁happ y",
+ "▁f n",
+ "▁ fn",
+ "▁j ud",
+ "▁ju d",
+ "▁ jud",
+ "▁h at",
+ "▁ha t",
+ "▁ hat",
+ "AC K",
+ "A CK",
+ "▁F e",
+ "▁ Fe",
+ "$ -",
+ "iv il",
+ "ivi l",
+ "i vil",
+ "ot ed",
+ "ote d",
+ "o ted",
+ "▁size of",
+ "▁ sizeof",
+ "▁sit uation",
+ "▁situ ation",
+ "▁l ives",
+ "▁li ves",
+ "▁live s",
+ "▁liv es",
+ "▁fe eling",
+ "▁feel ing",
+ "▁fee ling",
+ "▁r isk",
+ "▁ri sk",
+ "▁ris k",
+ "▁Jan uary",
+ "▁Januar y",
+ "▁Ob ject",
+ "▁ Object",
+ "▁re comm",
+ "▁rec omm",
+ "▁в ы",
+ "▁ вы",
+ "▁pot ential",
+ "ea h",
+ "e ah",
+ "▁com plex",
+ "▁comp lex",
+ "▁compl ex",
+ "▁ complex",
+ "print f",
+ "ist ance",
+ "istan ce",
+ "i stance",
+ "ir th",
+ "irt h",
+ "li k",
+ "l ik",
+ "as te",
+ "ast e",
+ "a ste",
+ "▁wh ose",
+ "▁who se",
+ "Ar g",
+ "A rg",
+ "▁mod ern",
+ "▁mo dern",
+ "▁mode rn",
+ "▁moder n",
+ "ion es",
+ "io nes",
+ "ione s",
+ "i ones",
+ "▁ч е",
+ "▁ че",
+ "▁s ett",
+ "▁se tt",
+ "▁set t",
+ "▁M ag",
+ "▁Ma g",
+ "▁ Mag",
+ "a e",
+ "▁cond ition",
+ "▁ condition",
+ "Le ngth",
+ "L ength",
+ "▁f it",
+ "▁fi t",
+ "▁ fit",
+ "ound s",
+ "oun ds",
+ "▁ch anged",
+ "▁chang ed",
+ "▁change d",
+ "▁ changed",
+ "▁g uy",
+ "▁gu y",
+ "fil ter",
+ "at ever",
+ "ate ver",
+ "é d",
+ "re move",
+ "rem ove",
+ "▁h op",
+ "▁ho p",
+ "▁ hop",
+ "▁O ut",
+ "▁ Out",
+ "▁R ich",
+ "▁Ric h",
+ "▁ Rich",
+ "ch ild",
+ "chi ld",
+ "▁in cluded",
+ "▁incl uded",
+ "▁includ ed",
+ "▁include d",
+ "▁inclu ded",
+ "$ \\",
+ "▁T om",
+ "▁To m",
+ "▁ Tom",
+ "el ine",
+ "eli ne",
+ "elin e",
+ "e line",
+ "▁s ometimes",
+ "▁some times",
+ "▁somet imes",
+ "▁sometime s",
+ "▁dr ink",
+ "▁qu ant",
+ "▁ quant",
+ "▁p lease",
+ "▁ple ase",
+ "▁I nt",
+ "▁In t",
+ "▁ Int",
+ "ri ef",
+ "rie f",
+ "r ief",
+ "▁ex actly",
+ "▁exact ly",
+ "ci ng",
+ "cin g",
+ "c ing",
+ "▁all owed",
+ "▁allow ed",
+ "▁ allowed",
+ "bu ild",
+ "b uild",
+ "▁beaut iful",
+ "▁W ell",
+ "▁We ll",
+ "▁Wel l",
+ "▁ Well",
+ "▁look s",
+ "▁lo oks",
+ "▁ ü",
+ "▁ch ance",
+ "▁w rote",
+ "▁wr ote",
+ "▁n or",
+ "▁no r",
+ "▁ nor",
+ "▁f ailed",
+ "▁fa iled",
+ "▁fail ed",
+ "▁ failed",
+ "Me t",
+ "M et",
+ "▁p rior",
+ "▁pr ior",
+ "▁pri or",
+ "▁h undred",
+ "ско й",
+ "с кой",
+ "or ia",
+ "ori a",
+ "o ria",
+ "▁c y",
+ "▁ cy",
+ "▁w eb",
+ "▁we b",
+ "▁ web",
+ "▁m ess",
+ "▁me ss",
+ "▁mes s",
+ "le q",
+ "l eq",
+ "d y",
+ "te x",
+ "t ex",
+ "▁a nim",
+ "▁an im",
+ "▁ anim",
+ "at ur",
+ "atu r",
+ "▁str ucture",
+ "▁struct ure",
+ "▁ structure",
+ "opt ion",
+ "o ption",
+ "▁act ual",
+ "▁ actual",
+ "▁Fr anc",
+ "▁Fra nc",
+ "▁Fran c",
+ "en ced",
+ "ence d",
+ "enc ed",
+ ".< /",
+ ". ",
+ "▁f low",
+ "▁fl ow",
+ "▁flo w",
+ "▁ flow",
+ "▁A fr",
+ "▁Af r",
+ "de t",
+ "d et",
+ "▁K e",
+ "▁ Ke",
+ "et y",
+ "e ty",
+ "ски й",
+ "с кий",
+ "▁st uff",
+ "it ter",
+ "itt er",
+ "itte r",
+ "▁ar gs",
+ "▁arg s",
+ "▁ args",
+ "▁al bum",
+ "▁ album",
+ "▁ ]",
+ "ug in",
+ "u gin",
+ "S U",
+ "Pe r",
+ "P er",
+ "▁cir c",
+ "▁ci rc",
+ "▁ circ",
+ "▁cor rect",
+ "▁corre ct",
+ "▁ correct",
+ "▁l ines",
+ "▁li nes",
+ "▁line s",
+ "▁lin es",
+ "▁ lines",
+ "▁complet ely",
+ "▁complete ly",
+ "kn own",
+ "know n",
+ "k nown",
+ "▁t ree",
+ "▁tr ee",
+ "▁tre e",
+ "▁ tree",
+ "ro ot",
+ "r oot",
+ "▁J apan",
+ "▁Ja pan",
+ "▁Jap an",
+ "ol es",
+ "ole s",
+ "o les",
+ "en do",
+ "end o",
+ "▁l ocation",
+ "▁loc ation",
+ "▁ location",
+ "▁ Х",
+ "▁m id",
+ "▁mi d",
+ "▁ mid",
+ "al ing",
+ "ali ng",
+ "alin g",
+ "a ling",
+ "G L",
+ "ia no",
+ "ian o",
+ "i ano",
+ "▁{ }",
+ "▁ {}",
+ "la ng",
+ "lan g",
+ "l ang",
+ "▁equ ip",
+ "ERR OR",
+ "▁mem ory",
+ "▁memor y",
+ "▁memo ry",
+ "▁ memory",
+ "▁( \"",
+ "▁ (\"",
+ "▁n ature",
+ "▁nat ure",
+ "▁natur e",
+ "go ogle",
+ "ab s",
+ "a bs",
+ "B C",
+ "▁g ets",
+ "▁get s",
+ "▁ge ts",
+ "▁ gets",
+ "Com mand",
+ "Comm and",
+ "TE R",
+ "T ER",
+ "al ed",
+ "ale d",
+ "a led",
+ "c p",
+ "▁p urch",
+ "▁pur ch",
+ "▁D en",
+ "▁De n",
+ "▁ Den",
+ "▁her self",
+ "▁hers elf",
+ "▁I r",
+ "▁ Ir",
+ "▁s ie",
+ "▁si e",
+ "ga r",
+ "g ar",
+ "A p",
+ "▁n el",
+ "▁ne l",
+ "▁ nel",
+ "ot a",
+ "o ta",
+ ") ]",
+ "co r",
+ "c or",
+ "ac ht",
+ "ach t",
+ "a cht",
+ "( *",
+ "irt ual",
+ "▁pol ice",
+ "▁polic e",
+ "▁s kin",
+ "▁sk in",
+ "▁ski n",
+ "▁ skin",
+ "sh ip",
+ "s hip",
+ "ef ined",
+ "augh ter",
+ "aught er",
+ "in ding",
+ "ind ing",
+ "indi ng",
+ "▁S l",
+ "▁ Sl",
+ "▁in flu",
+ "▁infl u",
+ "▁inf lu",
+ "▁m ount",
+ "▁mo unt",
+ "▁mou nt",
+ "▁ mount",
+ "▁a z",
+ "▁ az",
+ "▁w ood",
+ "▁wo od",
+ "▁ wood",
+ "ot es",
+ "ote s",
+ "o tes",
+ "eg a",
+ "e ga",
+ "▁acc ording",
+ "▁accord ing",
+ "▁name space",
+ "▁names pace",
+ "▁ namespace",
+ "Del ta",
+ "D elta",
+ "st ant",
+ "sta nt",
+ "stan t",
+ "▁pub lished",
+ "▁publish ed",
+ "▁ published",
+ "ak er",
+ "ake r",
+ "a ker",
+ "▁Bl ack",
+ "▁ Black",
+ "l n",
+ "▁indust ry",
+ "SO N",
+ "S ON",
+ "Re p",
+ "R ep",
+ "▁ch oice",
+ "▁cho ice",
+ "▁ choice",
+ "▁in n",
+ "▁i nn",
+ "▁ inn",
+ "k l",
+ "▁p al",
+ "▁pa l",
+ "▁ pal",
+ "▁a ud",
+ "▁au d",
+ "▁ aud",
+ "▁stand ard",
+ "▁ standard",
+ "▁know ledge",
+ "** ,",
+ "* *,",
+ "▁F rank",
+ "▁Fr ank",
+ "▁Fran k",
+ "s q",
+ "Out put",
+ "▁f ör",
+ "▁fö r",
+ "▁ för",
+ "Val id",
+ "ug h",
+ "u gh",
+ "▁bo oks",
+ "▁book s",
+ "▁ books",
+ "▁J ames",
+ "▁Jam es",
+ "▁Ja mes",
+ "k o",
+ "▁compan ies",
+ "an ning",
+ "ann ing",
+ "anni ng",
+ "▁v ict",
+ "▁vi ct",
+ "▁vic t",
+ "▁re pl",
+ "▁rep l",
+ "▁s che",
+ "▁sc he",
+ "▁sch e",
+ "▁ sche",
+ "▁h appen",
+ "▁happ en",
+ "▁ha ppen",
+ "ft y",
+ "f ty",
+ "ac ity",
+ "aci ty",
+ "a city",
+ "ir a",
+ "i ra",
+ "▁im plement",
+ "▁imp lement",
+ "▁impl ement",
+ "▁ implement",
+ "ско го",
+ "ск ого",
+ "с кого",
+ "num ber",
+ "nu mber",
+ "n umber",
+ "S H",
+ "ir o",
+ "i ro",
+ "▁f ear",
+ "▁fe ar",
+ "▁t ouch",
+ "▁to uch",
+ "▁tou ch",
+ "▁ touch",
+ "▁c ast",
+ "▁cas t",
+ "▁ca st",
+ "▁ cast",
+ "AS S",
+ "A SS",
+ "▁cons ist",
+ "T ask",
+ "▁s ig",
+ "▁si g",
+ "▁ sig",
+ "б а",
+ "ig ation",
+ "▁M ost",
+ "▁Mo st",
+ "▁Mos t",
+ "▁ Most",
+ "▁D er",
+ "▁De r",
+ "▁ Der",
+ "}( \\",
+ "} (\\",
+ ": \"",
+ "▁F ig",
+ "▁Fi g",
+ "▁ Fig",
+ "al i",
+ "a li",
+ "in er",
+ "ine r",
+ "i ner",
+ "') ,",
+ "' ),",
+ "▁C oun",
+ "▁Co un",
+ "▁Cou n",
+ "( _",
+ "▁d istributed",
+ "▁distribut ed",
+ "▁distribute d",
+ "NA ME",
+ "N AME",
+ "▁m ur",
+ "▁mu r",
+ "▁care er",
+ "~ ~",
+ "pe rs",
+ "per s",
+ "p ers",
+ "ar ies",
+ "ari es",
+ "a ries",
+ "en ses",
+ "ens es",
+ "ense s",
+ "▁Al so",
+ "▁Als o",
+ "Vers ion",
+ "V ersion",
+ "▁un ique",
+ "▁uniqu e",
+ "▁ unique",
+ "▁Fr ance",
+ "▁Franc e",
+ "▁Fran ce",
+ "B A",
+ "k y",
+ "▁F ebru",
+ "▁Fe bru",
+ "▁Feb ru",
+ "▁d ied",
+ "▁di ed",
+ "▁die d",
+ "om ega",
+ "ome ga",
+ "▁F orm",
+ "▁For m",
+ "▁Fo rm",
+ "▁ Form",
+ "▁w idth",
+ "▁wid th",
+ "▁ width",
+ "to col",
+ "t ocol",
+ "▁l ie",
+ "▁li e",
+ "▁ lie",
+ "Sh e",
+ "S he",
+ "é m",
+ "▁stra ight",
+ "▁n ach",
+ "▁na ch",
+ "▁st ood",
+ "▁sto od",
+ "▁ stood",
+ "ol ds",
+ "old s",
+ "▁g oes",
+ "▁go es",
+ "ce ll",
+ "cel l",
+ "c ell",
+ "▁t ill",
+ "▁til l",
+ "▁ti ll",
+ "L I",
+ "dr aw",
+ "d raw",
+ "▁s atisf",
+ "▁sat isf",
+ "▁re ading",
+ "▁read ing",
+ "AT ION",
+ "A TION",
+ "▁A re",
+ "▁Ar e",
+ "▁ Are",
+ "▁A c",
+ "▁ Ac",
+ ") *",
+ "▁add itional",
+ "▁addition al",
+ "wo od",
+ "w ood",
+ "ci l",
+ "c il",
+ "п у",
+ "UL T",
+ "U LT",
+ "▁b ill",
+ "▁bi ll",
+ "▁bil l",
+ "ma s",
+ "m as",
+ "an ia",
+ "ani a",
+ "a nia",
+ "с у",
+ "an z",
+ "he ight",
+ "h eight",
+ "j o",
+ "▁d os",
+ "▁do s",
+ "\\ \"",
+ "▁/ >",
+ "▁ />",
+ "▁p roduction",
+ "▁produ ction",
+ "▁product ion",
+ "▁prod uction",
+ "▁ production",
+ "ig er",
+ "ige r",
+ "i ger",
+ "▁с т",
+ "▁ ст",
+ "sh ow",
+ "s how",
+ "▁pop ulation",
+ "▁popul ation",
+ "▁p ark",
+ "▁par k",
+ "▁ park",
+ "▁Z e",
+ "▁necess ary",
+ "▁ necessary",
+ "▁t rust",
+ "▁tr ust",
+ "▁sh own",
+ "▁show n",
+ "mod ule",
+ "mo dule",
+ "G E",
+ "▁l ay",
+ "▁la y",
+ "▁ lay",
+ "▁ann oun",
+ "▁class Name",
+ "▁ className",
+ "▁cal cul",
+ "▁calc ul",
+ "Fun ction",
+ "F unction",
+ "▁S al",
+ "▁Sa l",
+ "▁ Sal",
+ "O K",
+ "T P",
+ "▁en try",
+ "▁ent ry",
+ "▁entr y",
+ "▁ entry",
+ "▁St ud",
+ "▁ Stud",
+ "▁it ems",
+ "▁item s",
+ "▁ items",
+ "▁se curity",
+ "▁sec urity",
+ "▁secur ity",
+ "▁ security",
+ "En try",
+ "Ent ry",
+ "f loat",
+ "l s",
+ "ib ly",
+ "▁cont ribut",
+ "▁C heck",
+ "▁Che ck",
+ "▁ Check",
+ "M D",
+ "▁impro ve",
+ "Par t",
+ "P art",
+ "▁system s",
+ "▁syst ems",
+ "B l",
+ "▁pol icy",
+ "▁polic y",
+ "▁ policy",
+ "▁s creen",
+ "▁sc reen",
+ "▁scr een",
+ "▁ screen",
+ "▁A ny",
+ "▁An y",
+ "▁ Any",
+ "▁op ened",
+ "▁open ed",
+ "al loc",
+ "all oc",
+ "allo c",
+ "▁De cember",
+ "▁Dec ember",
+ "▁ É",
+ "▁e mail",
+ "▁em ail",
+ "▁ email",
+ "ad er",
+ "ade r",
+ "a der",
+ "= >",
+ "▁H en",
+ "▁He n",
+ "▁ Hen",
+ "▁in fo",
+ "▁inf o",
+ "▁ info",
+ "▁f loat",
+ "▁flo at",
+ "▁ float",
+ "▁sw itch",
+ "▁ switch",
+ "ра н",
+ "р ан",
+ "ur ance",
+ "▁as sum",
+ "▁ass um",
+ "us tr",
+ "ust r",
+ "u str",
+ "▁g roups",
+ "▁group s",
+ "▁gro ups",
+ "▁ groups",
+ "▁R ead",
+ "▁Re ad",
+ "▁ Read",
+ "▁w at",
+ "▁wa t",
+ "S p",
+ "ве р",
+ "в ер",
+ "RA N",
+ "R AN",
+ "hi b",
+ "h ib",
+ "AL L",
+ "A LL",
+ "▁h us",
+ "▁ hus",
+ "Sp ec",
+ "Spe c",
+ "S pec",
+ "\") )",
+ "\" ))",
+ "▁F rench",
+ "▁C lass",
+ "▁Cl ass",
+ "▁ Class",
+ "▁pres ident",
+ "▁presid ent",
+ "▁def init",
+ "▁defin it",
+ "▁N or",
+ "▁No r",
+ "▁T hom",
+ "▁Th om",
+ "ai gn",
+ "a ign",
+ "W idth",
+ "D o",
+ "▁{ @",
+ "ag on",
+ "ago n",
+ "a gon",
+ "▁L u",
+ "▁ Lu",
+ "▁follow ed",
+ "M M",
+ "as ons",
+ "ason s",
+ "tm p",
+ "t mp",
+ "▁th rows",
+ "▁throw s",
+ "▁thr ows",
+ "▁thro ws",
+ "▁ throws",
+ "IT Y",
+ "I TY",
+ "но м",
+ "▁f air",
+ "▁fa ir",
+ "▁p en",
+ "▁pe n",
+ "▁ pen",
+ "é g",
+ "▁inter face",
+ "▁ interface",
+ "▁s af",
+ "▁sa f",
+ "oo n",
+ "o on",
+ "B ack",
+ "▁s peed",
+ "▁sp eed",
+ "▁spe ed",
+ "▁ speed",
+ "▁ext ends",
+ "▁extend s",
+ "em pty",
+ "empt y",
+ "emp ty",
+ "▁п ере",
+ "▁пер е",
+ "▁пе ре",
+ "▁pro per",
+ "▁pr oper",
+ "▁prop er",
+ "▁d riv",
+ "▁dr iv",
+ "▁dri v",
+ "ф и",
+ "▁c enter",
+ "▁cent er",
+ "▁ center",
+ "he ader",
+ "head er",
+ "▁} )",
+ "▁ })",
+ "w a",
+ "▁m iddle",
+ "▁ middle",
+ "▁ch oose",
+ "▁cho ose",
+ "▁St ad",
+ "▁Sta d",
+ "S O",
+ "Fact ory",
+ "Factor y",
+ "F actory",
+ "De v",
+ "D ev",
+ "ic les",
+ "icle s",
+ "icl es",
+ "i cles",
+ "▁ap plication",
+ "▁applic ation",
+ "▁appl ication",
+ "▁ application",
+ "▁mod els",
+ "▁model s",
+ "▁mode ls",
+ "▁ models",
+ "pi te",
+ "pit e",
+ "p ite",
+ "ca p",
+ "c ap",
+ "x i",
+ "osp ital",
+ "▁d ream",
+ "▁dre am",
+ "EN D",
+ "E ND",
+ "▁con tract",
+ "▁cont ract",
+ "▁contr act",
+ "▁contra ct",
+ "▁ contract",
+ "icro soft",
+ "▁th ous",
+ "▁thou s",
+ "iz es",
+ "ize s",
+ "i zes",
+ "▁д а",
+ "▁ да",
+ "▁C O",
+ "▁ CO",
+ "▁d irection",
+ "▁di rection",
+ "▁direct ion",
+ "▁dire ction",
+ "▁dir ection",
+ "▁ direction",
+ "▁` `",
+ "▁ ``",
+ "▁d rive",
+ "▁dr ive",
+ "▁dri ve",
+ "▁driv e",
+ "▁ drive",
+ "Ma x",
+ "M ax",
+ "ci a",
+ "c ia",
+ "▁contin u",
+ "▁A lex",
+ "▁Al ex",
+ "▁Ale x",
+ "▁ Alex",
+ "▁g old",
+ "▁go ld",
+ "▁gol d",
+ "▁ gold",
+ "▁p rep",
+ "▁pre p",
+ "▁pr ep",
+ "▁or igin",
+ "▁orig in",
+ "▁ origin",
+ "▁r ap",
+ "▁ra p",
+ "▁ rap",
+ "O p",
+ "ous ly",
+ "▁are as",
+ "▁area s",
+ "PO RT",
+ "P ORT",
+ "он а",
+ "о на",
+ "▁sa fe",
+ "▁saf e",
+ "▁ safe",
+ "▁profess ional",
+ "▁profession al",
+ "ap ache",
+ "apa che",
+ "▁t emper",
+ "▁tem per",
+ "▁temp er",
+ "s z",
+ "▁u nit",
+ "▁un it",
+ "▁ unit",
+ "▁c op",
+ "▁co p",
+ "▁ cop",
+ "eq n",
+ "List ener",
+ "Listen er",
+ "▁for mat",
+ "▁form at",
+ "▁forma t",
+ "▁ format",
+ "se lect",
+ "sel ect",
+ "s elect",
+ "▁com fort",
+ "▁ comfort",
+ "▁me ant",
+ "▁mean t",
+ "id ay",
+ "ida y",
+ "i day",
+ "em e",
+ "e me",
+ "▁act ive",
+ "▁activ e",
+ "▁ active",
+ "▁n ote",
+ "▁not e",
+ "▁no te",
+ "▁ note",
+ "▁M il",
+ "▁Mi l",
+ "▁ Mil",
+ "on ly",
+ "▁< =",
+ "▁ <=",
+ "▁ne igh",
+ "▁nei gh",
+ "a o",
+ "▁bl ue",
+ "▁ blue",
+ "▁T V",
+ "▁ TV",
+ "Ch ild",
+ "▁re ached",
+ "▁reach ed",
+ "Add ress",
+ "Addr ess",
+ "ст в",
+ "▁cl osed",
+ "▁close d",
+ "▁clos ed",
+ "▁clo sed",
+ "▁ closed",
+ "in der",
+ "ind er",
+ "inde r",
+ "i nder",
+ "ol o",
+ "o lo",
+ "▁a lt",
+ "▁al t",
+ "▁ alt",
+ "▁a dm",
+ "▁ad m",
+ "Form at",
+ "For mat",
+ "U I",
+ "▁H am",
+ "▁Ha m",
+ "▁f requ",
+ "▁fr equ",
+ "▁fre qu",
+ "▁in depend",
+ "▁inde pend",
+ "▁ independ",
+ "▁eas ily",
+ "▁L and",
+ "▁La nd",
+ "▁Lan d",
+ "▁ Land",
+ "▁t or",
+ "▁to r",
+ "▁ tor",
+ "ograph y",
+ "ograp hy",
+ "in fty",
+ "inf ty",
+ "▁W ork",
+ "▁Wor k",
+ "▁ Work",
+ "iv en",
+ "ive n",
+ "i ven",
+ "▁Count y",
+ "▁Coun ty",
+ "▁s rc",
+ "▁ src",
+ "}$ ,",
+ "} $,",
+ "par se",
+ "pars e",
+ "p arse",
+ "C D",
+ "▁C our",
+ "▁Co ur",
+ "▁Cou r",
+ "▁f ol",
+ "▁fo l",
+ "▁ fol",
+ "Ent ity",
+ "pg f",
+ "▁Ch ina",
+ "▁Chi na",
+ "▁S ub",
+ "▁Su b",
+ "▁ Sub",
+ "ho od",
+ "h ood",
+ "▁field s",
+ "▁ fields",
+ "▁y es",
+ "▁ye s",
+ "▁ yes",
+ "re nd",
+ "ren d",
+ "r end",
+ "▁to wards",
+ "▁toward s",
+ "▁tow ards",
+ "▁st aff",
+ "▁sta ff",
+ "▁ staff",
+ "▁A ir",
+ "▁ Air",
+ "▁st ation",
+ "▁stat ion",
+ "▁ station",
+ "at ives",
+ "ative s",
+ "ati ves",
+ "ativ es",
+ "▁imp act",
+ "в ы",
+ "▁direct ly",
+ "iss ions",
+ "ission s",
+ "iv a",
+ "i va",
+ "| \\",
+ "Pt r",
+ "P tr",
+ "▁S ant",
+ "▁San t",
+ "▁Sa nt",
+ "Po l",
+ "P ol",
+ "▁pro gress",
+ "▁ progress",
+ "it ar",
+ "ita r",
+ "i tar",
+ "▁p arts",
+ "▁part s",
+ "▁par ts",
+ "▁ parts",
+ "▁pl ant",
+ "▁plan t",
+ "▁ plant",
+ "▁abs olut",
+ "▁gu ess",
+ "eq ref",
+ "▁t im",
+ "▁ti m",
+ "▁ tim",
+ "▁L ou",
+ "▁Lo u",
+ "▁ Lou",
+ "▁c ool",
+ "▁co ol",
+ "al u",
+ "a lu",
+ "▁m outh",
+ "▁mo uth",
+ "▁mou th",
+ "▁ mouth",
+ "ни х",
+ "▁h eight",
+ "▁he ight",
+ "▁ height",
+ "ge st",
+ "ges t",
+ "g est",
+ "▁P ost",
+ "▁Po st",
+ "▁Pos t",
+ "▁ Post",
+ "▁b oard",
+ "▁bo ard",
+ "▁ board",
+ "▁t it",
+ "▁ti t",
+ "▁ tit",
+ "▁h our",
+ "▁ho ur",
+ "▁ hour",
+ "▁ser ver",
+ "▁serv er",
+ "▁serve r",
+ "▁ server",
+ "▁p layers",
+ "▁play ers",
+ "▁player s",
+ "ri er",
+ "rie r",
+ "r ier",
+ "Lin k",
+ "L ink",
+ "▁Pres ident",
+ "] (",
+ "▁con struct",
+ "▁const ruct",
+ "▁constr uct",
+ "▁constru ct",
+ "▁ construct",
+ "hand le",
+ "}$ .",
+ "} $.",
+ "ry ing",
+ "r ying",
+ "▁s hop",
+ "▁sh op",
+ "▁ shop",
+ "ia na",
+ "ian a",
+ "i ana",
+ "ex p",
+ "e xp",
+ "Hel per",
+ "Help er",
+ "Off set",
+ "ac hes",
+ "ach es",
+ "ache s",
+ "a ches",
+ "▁conne ction",
+ "▁connect ion",
+ "▁conn ection",
+ "▁ connection",
+ "▁d ifference",
+ "▁dif ference",
+ "▁differ ence",
+ "serv ice",
+ "s ervice",
+ "▁g as",
+ "▁ga s",
+ "▁ gas",
+ "▁p riv",
+ "▁pr iv",
+ "▁pri v",
+ "▁ priv",
+ "▁un ivers",
+ "▁ univers",
+ "▁w ish",
+ "▁wis h",
+ "Re m",
+ "R em",
+ "U rl",
+ "ge b",
+ "g eb",
+ "S o",
+ "ens ions",
+ "ension s",
+ "Mod ule",
+ "Mo dule",
+ "SI ZE",
+ "▁p rem",
+ "▁pre m",
+ "▁pr em",
+ "wind ow",
+ "w indow",
+ "▁d ies",
+ "▁di es",
+ "▁die s",
+ "de l",
+ "d el",
+ "▁r ow",
+ "▁ro w",
+ "▁ row",
+ "▁a verage",
+ "▁aver age",
+ "▁ave rage",
+ "xi m",
+ "x im",
+ "▁p u",
+ "▁ pu",
+ "an ç",
+ "De t",
+ "D et",
+ "ke r",
+ "k er",
+ "y a",
+ "▁D et",
+ "▁De t",
+ "▁ Det",
+ "▁p å",
+ "▁n amed",
+ "▁name d",
+ "▁na med",
+ "▁nam ed",
+ "▁ named",
+ "▁dec ision",
+ "▁decis ion",
+ "wi n",
+ "w in",
+ "▁Ge orge",
+ "▁Georg e",
+ "ar ily",
+ "ari ly",
+ "▁s olution",
+ "▁sol ution",
+ "▁mult iple",
+ "▁multi ple",
+ "▁multip le",
+ "▁ multiple",
+ "at egy",
+ "ate gy",
+ "ateg y",
+ "▁le arning",
+ "▁learn ing",
+ "▁lear ning",
+ "▁ learning",
+ "▁se cret",
+ "▁sec ret",
+ "▁secre t",
+ "▁ secret",
+ "D O",
+ "▁n ice",
+ "▁ni ce",
+ "▁nic e",
+ "▁ nice",
+ "//////// ////////",
+ "S u",
+ "it ation",
+ "itat ion",
+ "▁j oin",
+ "▁jo in",
+ "▁ join",
+ "▁el ements",
+ "▁element s",
+ "▁ele ments",
+ "▁elem ents",
+ "▁ elements",
+ "▁e mer",
+ "▁em er",
+ "til de",
+ "t ilde",
+ "▁d ep",
+ "▁de p",
+ "▁ dep",
+ "▁s hot",
+ "▁sh ot",
+ "▁ shot",
+ "▁pl atform",
+ "▁plat form",
+ "▁ platform",
+ "ot hing",
+ "oth ing",
+ "o thing",
+ "M y",
+ "ed ia",
+ "edi a",
+ "om s",
+ "o ms",
+ "ail y",
+ "ai ly",
+ "a ily",
+ "( [",
+ "▁d ress",
+ "▁dr ess",
+ "▁dre ss",
+ "▁off icial",
+ "▁offic ial",
+ "es tern",
+ "est ern",
+ "ester n",
+ "este rn",
+ "▁dis cover",
+ "▁disc over",
+ "▁disco ver",
+ "▁m i",
+ "▁ mi",
+ "ны е",
+ "C A",
+ "od ing",
+ "odi ng",
+ "o ding",
+ "▁F ound",
+ "▁Fou nd",
+ "▁Fo und",
+ "▁ Found",
+ "▁a ffect",
+ "▁aff ect",
+ "▁af fect",
+ "Vi s",
+ "V is",
+ "st ract",
+ "str act",
+ "stra ct",
+ "s tract",
+ "ic ed",
+ "ice d",
+ "i ced",
+ "de bug",
+ "d ebug",
+ "▁rel ated",
+ "▁relate d",
+ "▁ related",
+ "▁s pect",
+ "▁sp ect",
+ "▁spec t",
+ "▁spe ct",
+ "▁ spect",
+ "us hed",
+ "ush ed",
+ "сь ко",
+ "▁b ank",
+ "▁ban k",
+ "▁ bank",
+ "▁c ele",
+ "▁ce le",
+ "▁cel e",
+ "AN D",
+ "A ND",
+ "ol f",
+ "е м",
+ "▁f ill",
+ "▁fil l",
+ "▁fi ll",
+ "▁ fill",
+ "▁g ives",
+ "▁giv es",
+ "▁give s",
+ "▁gi ves",
+ "▁б у",
+ "▁ бу",
+ "ar on",
+ "aro n",
+ "a ron",
+ "▁J es",
+ "▁Je s",
+ "RE G",
+ "▁s udd",
+ "▁su dd",
+ "▁sud d",
+ "date d",
+ "da ted",
+ "dat ed",
+ "d ated",
+ "v i",
+ "▁g i",
+ "▁ gi",
+ "se nd",
+ "sen d",
+ "s end",
+ "cp p",
+ "c pp",
+ "▁s pent",
+ "▁sp ent",
+ "▁spe nt",
+ "an de",
+ "and e",
+ "a nde",
+ "▁oper ation",
+ "▁ operation",
+ "pro cess",
+ "proc ess",
+ "▁in form",
+ "▁inf orm",
+ "▁info rm",
+ "▁F ree",
+ "▁Fr ee",
+ "▁Fre e",
+ "▁ Free",
+ "yo nd",
+ "y ond",
+ "▁per haps",
+ "▁su rv",
+ "▁sur v",
+ "▁L oc",
+ "▁Lo c",
+ "▁ Loc",
+ "▁con cl",
+ "▁conc l",
+ "▁ра з",
+ "▁ раз",
+ "▁O ver",
+ "▁ Over",
+ "ho l",
+ "h ol",
+ "ra z",
+ "r az",
+ "Wr ite",
+ "Writ e",
+ "W rite",
+ "▁g iving",
+ "▁giv ing",
+ "▁gi ving",
+ "r d",
+ "in stance",
+ "inst ance",
+ "▁re leased",
+ "▁rele ased",
+ "▁release d",
+ "▁R o",
+ "▁ Ro",
+ "R A",
+ "▁pract ice",
+ "▁g raph",
+ "▁gr aph",
+ "▁gra ph",
+ "▁grap h",
+ "▁ graph",
+ "▁incre ase",
+ "▁fig ure",
+ "▁ figure",
+ "Fil ter",
+ "HE CK",
+ "id x",
+ "i dx",
+ "▁g lass",
+ "▁gl ass",
+ "▁ glass",
+ "sk i",
+ "s ki",
+ "com es",
+ "co mes",
+ "come s",
+ "c omes",
+ "▁c at",
+ "▁ca t",
+ "▁ cat",
+ "▁c old",
+ "▁col d",
+ "▁co ld",
+ "go to",
+ "got o",
+ "g oto",
+ "uf act",
+ "u fact",
+ "▁C opyright",
+ "▁Copy right",
+ "▁ Copyright",
+ "}} \\",
+ "} }\\",
+ "▁str eng",
+ "▁stre ng",
+ "▁d ir",
+ "▁di r",
+ "▁ dir",
+ "to ken",
+ "tok en",
+ "t oken",
+ "▁occ ur",
+ "▁oc cur",
+ "arl ier",
+ "▁me asure",
+ "▁meas ure",
+ "▁ measure",
+ "▁s ec",
+ "▁se c",
+ "▁ sec",
+ "▁m ás",
+ "▁má s",
+ "▁N et",
+ "▁Ne t",
+ "▁ Net",
+ "▁arg ument",
+ "▁ argument",
+ "▁s ou",
+ "▁so u",
+ "▁m oving",
+ "▁mov ing",
+ "▁mo ving",
+ "▁p refer",
+ "▁pre fer",
+ "▁pref er",
+ "ma sk",
+ "mas k",
+ "m ask",
+ "< <",
+ "▁bre ath",
+ "▁breat h",
+ "▁phys ical",
+ "▁pos itive",
+ "▁posit ive",
+ "▁s or",
+ "▁so r",
+ "▁ sor",
+ "▁de part",
+ "▁dep art",
+ "▁re move",
+ "▁rem ove",
+ "▁ remove",
+ "▁k it",
+ "▁ki t",
+ "▁ kit",
+ "▁me eting",
+ "▁meet ing",
+ "▁D ata",
+ "▁Da ta",
+ "▁Dat a",
+ "▁ Data",
+ "og raf",
+ "act ions",
+ "action s",
+ "a ctions",
+ "▁param eters",
+ "▁parameter s",
+ "▁ parameters",
+ "▁A tt",
+ "▁At t",
+ "▁ Att",
+ "es ch",
+ "esc h",
+ "e sch",
+ "▁inv olved",
+ "▁invol ved",
+ "▁involve d",
+ "ä t",
+ "L L",
+ "B ar",
+ "▁с и",
+ "▁ си",
+ "ec h",
+ "e ch",
+ "GE T",
+ "G ET",
+ "▁pre vent",
+ "▁pr event",
+ "▁prev ent",
+ "▁ prevent",
+ "▁be yond",
+ "▁O ther",
+ "▁Ot her",
+ "▁ Other",
+ "ä n",
+ "by te",
+ "▁sudd en",
+ "▁sud den",
+ "ol ve",
+ "olv e",
+ "▁н о",
+ "▁ но",
+ "LO G",
+ "L OG",
+ "un it",
+ "uni t",
+ "u nit",
+ "▁tr uth",
+ "ra t",
+ "r at",
+ "S D",
+ "▁e at",
+ "▁M ad",
+ "▁Ma d",
+ "▁ Mad",
+ "▁prov ides",
+ "▁provide s",
+ "▁s ession",
+ "▁ session",
+ "De le",
+ "Del e",
+ "D ele",
+ "▁con vers",
+ "▁conv ers",
+ "▁conver s",
+ "▁conve rs",
+ "cent er",
+ "cen ter",
+ "c enter",
+ "▁contin ued",
+ "▁continue d",
+ "▁continu ed",
+ "ot ion",
+ "oti on",
+ "ca che",
+ "c ache",
+ "dis play",
+ "disp lay",
+ "▁prote ct",
+ "▁prot ect",
+ "am s",
+ "a ms",
+ "▁p ow",
+ "▁po w",
+ "▁ pow",
+ "CT ION",
+ "C TION",
+ "▁M ac",
+ "▁Ma c",
+ "▁ Mac",
+ "m o",
+ "х а",
+ "▁d istance",
+ "▁di stance",
+ "▁dist ance",
+ "▁ distance",
+ "▁T ime",
+ "▁Tim e",
+ "▁Ti me",
+ "▁ Time",
+ "g i",
+ "▁s equ",
+ "▁se qu",
+ "▁seq u",
+ "▁ sequ",
+ "T arget",
+ "с ле",
+ "Ser ver",
+ "Serv er",
+ "▁w ide",
+ "▁wid e",
+ "▁ wide",
+ "cl ose",
+ "clos e",
+ "▁c ru",
+ "▁cr u",
+ "Ex t",
+ "E xt",
+ "▁s elect",
+ "▁se lect",
+ "▁sel ect",
+ "▁sele ct",
+ "▁ select",
+ "▁pat tern",
+ "▁ pattern",
+ "\") );",
+ "\")) ;",
+ "\" ));",
+ "Pro vider",
+ "Prov ider",
+ "UR L",
+ "U RL",
+ "▁g reen",
+ "▁gr een",
+ "▁gre en",
+ "▁ green",
+ "▁wait ing",
+ "▁wa iting",
+ "pro to",
+ "pr oto",
+ "prot o",
+ "▁immedi ately",
+ "▁immediate ly",
+ "com mon",
+ "comm on",
+ "az ione",
+ "azi one",
+ "a zione",
+ "ri ver",
+ "riv er",
+ "rive r",
+ "r iver",
+ "▁s en",
+ "▁se n",
+ "▁ sen",
+ "▁! ==",
+ "▁!= =",
+ "▁Febru ary",
+ "▁Februar y",
+ "ur b",
+ "u rb",
+ "▁S en",
+ "▁Se n",
+ "de st",
+ "des t",
+ "d est",
+ "< ?",
+ "▁ed ge",
+ "▁ edge",
+ "▁m ais",
+ "▁ma is",
+ "▁mai s",
+ "gor ith",
+ "cp u",
+ "c pu",
+ "▁educ ation",
+ "▁associ ated",
+ "▁associate d",
+ "No ne",
+ "Non e",
+ "N one",
+ "h i",
+ "▁p oor",
+ "▁po or",
+ "se m",
+ "s em",
+ "▁W il",
+ "▁Wi l",
+ "▁b ud",
+ "▁bu d",
+ "▁ bud",
+ "▁a uch",
+ "▁au ch",
+ "▁ auch",
+ "el ler",
+ "ell er",
+ "elle r",
+ "▁L ife",
+ "▁Li fe",
+ "▁ Life",
+ "▁f iles",
+ "▁fil es",
+ "▁file s",
+ "▁fi les",
+ "▁ files",
+ "▁le ading",
+ "▁lead ing",
+ "▁ leading",
+ "▁ob tain",
+ "▁obt ain",
+ "▁J ul",
+ "▁Ju l",
+ "at ory",
+ "ator y",
+ "ato ry",
+ "г у",
+ "it able",
+ "ita ble",
+ "i table",
+ "▁on to",
+ "▁ont o",
+ "▁ onto",
+ "▁b orn",
+ "▁bo rn",
+ "▁bor n",
+ "▁ born",
+ "or em",
+ "ore m",
+ "o rem",
+ "▁Stre et",
+ "▁m aint",
+ "▁main t",
+ "▁ma int",
+ "▁mai nt",
+ "Param s",
+ "Par ams",
+ "ri p",
+ "r ip",
+ "▁S T",
+ "▁ ST",
+ "u v",
+ "ma in",
+ "m ain",
+ "▁re cent",
+ "▁rec ent",
+ "▁rece nt",
+ "We b",
+ "W eb",
+ "ov a",
+ "o va",
+ "ц а",
+ "ais e",
+ "ai se",
+ "a ise",
+ "yle s",
+ "yl es",
+ "y les",
+ "▁de scribed",
+ "▁desc ribed",
+ "▁describ ed",
+ "▁describe d",
+ "▁begin ning",
+ "▁D ay",
+ "▁Da y",
+ "▁ Day",
+ "▁V ol",
+ "▁Vo l",
+ "▁ Vol",
+ "▁h uge",
+ "▁hug e",
+ "Ha s",
+ "H as",
+ "an cy",
+ "anc y",
+ "He ader",
+ "Head er",
+ "▁a ren",
+ "▁are n",
+ "▁ar en",
+ "▁ aren",
+ "ва н",
+ "в ан",
+ "▁en sure",
+ "▁ens ure",
+ "▁ ensure",
+ "▁p et",
+ "▁pe t",
+ "▁ pet",
+ "mu lt",
+ "mul t",
+ "m ult",
+ "▁L ike",
+ "▁Li ke",
+ "▁ Like",
+ "▁man agement",
+ "▁manage ment",
+ "▁ management",
+ "P S",
+ "wh ile",
+ "▁back ground",
+ "▁ background",
+ "ount er",
+ "oun ter",
+ "o unter",
+ "bo ol",
+ "b ool",
+ "F C",
+ "N um",
+ "R L",
+ "▁ex cl",
+ "▁exc l",
+ "▁e ye",
+ "▁ey e",
+ "im g",
+ "i mg",
+ "▁r om",
+ "▁ro m",
+ "▁ rom",
+ "▁H el",
+ "▁He l",
+ "▁ Hel",
+ "Opt ion",
+ "O ption",
+ "▁stop ped",
+ "▁sto pped",
+ "▁th read",
+ "▁thr ead",
+ "▁ thread",
+ "to type",
+ "tot ype",
+ "t otype",
+ ")) )",
+ ") ))",
+ "▁st age",
+ "▁stag e",
+ "▁sta ge",
+ "▁ stage",
+ "▁ü ber",
+ "▁ über",
+ "▁al though",
+ "▁ although",
+ "Type s",
+ "Ty pes",
+ "Typ es",
+ "T ypes",
+ "▁O h",
+ "▁ Oh",
+ "▁e ight",
+ "▁ eight",
+ "▁de scription",
+ "▁des cription",
+ "▁ description",
+ "' '",
+ "ö n",
+ "▁sur face",
+ "▁surf ace",
+ "▁ surface",
+ "▁Intern ational",
+ "▁ch arg",
+ "▁char g",
+ "▁cha rg",
+ "▁ charg",
+ "▁col lection",
+ "▁coll ection",
+ "▁collect ion",
+ "▁colle ction",
+ "▁ collection",
+ "▁us ers",
+ "▁use rs",
+ "▁user s",
+ "▁ users",
+ "▁ob vious",
+ "▁cent ury",
+ "▁ century",
+ "ic ks",
+ "ick s",
+ "i cks",
+ "▁art icle",
+ "▁artic le",
+ "▁ article",
+ "▁\" \\",
+ "▁ \"\\",
+ "di m",
+ "d im",
+ "▁s in",
+ "▁si n",
+ "▁ sin",
+ "en ge",
+ "eng e",
+ "Cont rol",
+ "▁com mit",
+ "▁comm it",
+ "▁ commit",
+ "ens ity",
+ "▁t ra",
+ "▁tr a",
+ "▁ tra",
+ "cript or",
+ "▁N OT",
+ "▁NO T",
+ "▁ NOT",
+ "we ll",
+ "w ell",
+ "▁M ichael",
+ "▁Mich ael",
+ "▁n od",
+ "▁no d",
+ "▁ nod",
+ "▁m ort",
+ "▁mor t",
+ "▁mo rt",
+ "iv o",
+ "i vo",
+ "is ation",
+ "▁P o",
+ "▁ Po",
+ "▁P aris",
+ "▁Par is",
+ "▁Pa ris",
+ "▁ad ministr",
+ "▁admin istr",
+ "▁ administr",
+ "bu rg",
+ "bur g",
+ "b urg",
+ "cd ot",
+ "c dot",
+ "▁mil itary",
+ "▁milit ary",
+ "▁militar y",
+ "▁B est",
+ "▁Be st",
+ "▁Bes t",
+ "▁ Best",
+ "▁К а",
+ "▁ Ка",
+ "IN E",
+ "I NE",
+ "▁through out",
+ "S l",
+ "▁im pl",
+ "▁imp l",
+ "▁ impl",
+ "cont rol",
+ "contr ol",
+ "▁ Ч",
+ "▁u it",
+ "▁ui t",
+ "▁ uit",
+ "▁un signed",
+ "▁uns igned",
+ "▁ unsigned",
+ "▁M ary",
+ "▁Mar y",
+ "▁Ma ry",
+ "Ch ar",
+ "C har",
+ "м і",
+ "▁th reat",
+ "▁c ourt",
+ "▁co urt",
+ "▁cour t",
+ "▁cou rt",
+ "▁ court",
+ "vi lle",
+ "vil le",
+ "v ille",
+ "▁ ш",
+ "▁C am",
+ "▁Ca m",
+ "▁ Cam",
+ ". \r",
+ "▁current ly",
+ "▁curr ently",
+ "ro t",
+ "r ot",
+ "▁D ate",
+ "▁Da te",
+ "▁Dat e",
+ "▁ Date",
+ "▁s hit",
+ "▁sh it",
+ "▁ shit",
+ "▁$ {\\",
+ "▁${ \\",
+ "un n",
+ "u nn",
+ "U s",
+ "▁b uffer",
+ "▁buff er",
+ "▁buf fer",
+ "▁ buffer",
+ "▁s ont",
+ "▁so nt",
+ "▁son t",
+ "▁let ter",
+ "▁lett er",
+ "▁ letter",
+ "in ated",
+ "ina ted",
+ "inate d",
+ "Ch ange",
+ "▁h ref",
+ "▁hr ef",
+ "▁ href",
+ "▁l ack",
+ "▁la ck",
+ "▁lac k",
+ "▁o il",
+ "▁C ons",
+ "▁Con s",
+ "▁Co ns",
+ "▁ Cons",
+ "▁J er",
+ "▁Je r",
+ "BU G",
+ "B UG",
+ "if orn",
+ "▁pro perties",
+ "▁proper ties",
+ "▁ properties",
+ "▁r andom",
+ "▁ran dom",
+ "▁rand om",
+ "▁ random",
+ "▁br other",
+ "▁bro ther",
+ "▁p iece",
+ "▁pie ce",
+ "▁ piece",
+ "б у",
+ "ist ics",
+ "istic s",
+ "isti cs",
+ "▁techn ology",
+ "gl obal",
+ "glob al",
+ "▁trans form",
+ "▁ transform",
+ "er d",
+ "e rd",
+ "▁B ecause",
+ "▁ Because",
+ "PE CT",
+ "P ECT",
+ "pr et",
+ "pre t",
+ "p ret",
+ "▁го ду",
+ "▁год у",
+ "▁M et",
+ "▁Me t",
+ "▁ Met",
+ "▁p sy",
+ "▁ps y",
+ "▁ psy",
+ "▁о д",
+ "▁g od",
+ "▁go d",
+ "▁ god",
+ "▁D el",
+ "▁De l",
+ "▁ Del",
+ "base d",
+ "ba sed",
+ "bas ed",
+ "b ased",
+ "▁v oor",
+ "▁vo or",
+ "▁C all",
+ "▁Cal l",
+ "▁Ca ll",
+ "▁ Call",
+ "S A",
+ "▁fil ter",
+ "▁ filter",
+ "▁incl udes",
+ "▁includ es",
+ "▁include s",
+ "▁inclu des",
+ "▁ includes",
+ "olut ions",
+ "olution s",
+ "f d",
+ "▁w ind",
+ "▁win d",
+ "▁ wind",
+ "▁б о",
+ "▁ бо",
+ "▁ab ility",
+ "▁ ability",
+ "ca rd",
+ "car d",
+ "c ard",
+ "▁n umer",
+ "▁num er",
+ "▁nu mer",
+ "▁ numer",
+ "add ress",
+ "addr ess",
+ "▁go al",
+ "ash ington",
+ "ashing ton",
+ "▁s light",
+ "▁sl ight",
+ "ab a",
+ "a ba",
+ "▁L og",
+ "▁Lo g",
+ "▁ Log",
+ "Set tings",
+ "Setting s",
+ "ad ow",
+ "ado w",
+ "▁p i",
+ "▁ pi",
+ "ir ing",
+ "iri ng",
+ "i ring",
+ "F T",
+ "▁number s",
+ "▁num bers",
+ "con f",
+ "co nf",
+ "ta sk",
+ "t ask",
+ "▁î n",
+ "т ы",
+ "▁re ceive",
+ "▁rece ive",
+ "▁r oot",
+ "▁ro ot",
+ "▁ root",
+ "▁Ind ia",
+ "pat ch",
+ "p atch",
+ "é l",
+ "▁sum mer",
+ "▁method s",
+ "▁ methods",
+ "▁pl aces",
+ "▁place s",
+ "▁plac es",
+ "▁М а",
+ "▁ Ма",
+ "▁cap ital",
+ "▁capit al",
+ "▁ev idence",
+ "▁G erman",
+ "▁Germ an",
+ "▁Ger man",
+ "\\ ,",
+ "D A",
+ "ec ute",
+ "ecut e",
+ "col umn",
+ "▁fun ctions",
+ "▁function s",
+ "▁ functions",
+ "▁c ounter",
+ "▁co unter",
+ "▁coun ter",
+ "▁count er",
+ "▁ counter",
+ "▁ar ms",
+ "▁arm s",
+ "▁ arms",
+ "▁f eed",
+ "▁fe ed",
+ "▁fee d",
+ "▁ feed",
+ "ve y",
+ "v ey",
+ "he nt",
+ "hen t",
+ "h ent",
+ "MA X",
+ "M AX",
+ "▁ac qu",
+ "▁app ly",
+ "▁ap ply",
+ "▁appl y",
+ "▁ apply",
+ "▁hus band",
+ "▁k illed",
+ "▁kill ed",
+ "▁kil led",
+ "▁S pec",
+ "▁Sp ec",
+ "▁Spe c",
+ "▁ Spec",
+ "ent ity",
+ "enti ty",
+ "▁e arlier",
+ "▁M iss",
+ "▁Mi ss",
+ "▁Mis s",
+ "▁ Miss",
+ "▁set ting",
+ "▁sett ing",
+ "▁ setting",
+ "it ect",
+ "ite ct",
+ "▁d ed",
+ "▁de d",
+ "▁ ded",
+ "Ro w",
+ "R ow",
+ "▁r an",
+ "▁ra n",
+ "▁ ran",
+ "▁Y es",
+ "▁Ye s",
+ "▁ Yes",
+ "▁fin ancial",
+ "▁financ ial",
+ "s ession",
+ "le ar",
+ "l ear",
+ "is hing",
+ "ish ing",
+ "ishi ng",
+ "▁ne arly",
+ "▁near ly",
+ "▁d ur",
+ "▁du r",
+ "▁m achine",
+ "▁mach ine",
+ "▁ machine",
+ "xf f",
+ "x ff",
+ "br o",
+ "b ro",
+ "▁s ymbol",
+ "▁sym bol",
+ "▁ symbol",
+ "land s",
+ "lan ds",
+ "l ands",
+ "Ac c",
+ "A cc",
+ "d i",
+ "▁Rober t",
+ "▁Ro bert",
+ "▁Rob ert",
+ "pro p",
+ "pr op",
+ "p rop",
+ "ur ity",
+ "uri ty",
+ "▁# ####",
+ "▁## ###",
+ "▁### ##",
+ "▁#### #",
+ "▁walk ed",
+ "▁wal ked",
+ "▁intern ational",
+ "▁internation al",
+ "▁ Е",
+ "Y es",
+ "▁re lease",
+ "▁rele ase",
+ "▁ release",
+ "▁start ing",
+ "▁star ting",
+ "st atic",
+ "stat ic",
+ "▁b ei",
+ "▁be i",
+ "al low",
+ "all ow",
+ "allo w",
+ "▁Pe ople",
+ "▁ People",
+ "e z",
+ "▁param eter",
+ "▁ parameter",
+ "C ache",
+ "▁$ $",
+ "▁ $$",
+ "amp ions",
+ "ampion s",
+ "▁M er",
+ "▁Me r",
+ "▁ Mer",
+ "▁k om",
+ "▁ko m",
+ "▁ kom",
+ "le ted",
+ "let ed",
+ "lete d",
+ "l eted",
+ "oi s",
+ "o is",
+ "▁O pen",
+ "▁Op en",
+ "▁ Open",
+ "ty pes",
+ "type s",
+ "typ es",
+ "t ypes",
+ "▁f ue",
+ "▁fu e",
+ "ac ters",
+ "act ers",
+ "acter s",
+ "▁re ference",
+ "▁refer ence",
+ "▁ reference",
+ "Equ als",
+ "Equal s",
+ "Eq uals",
+ "▁a ware",
+ "▁aw are",
+ "▁ aware",
+ "▁h ol",
+ "▁ho l",
+ "▁ hol",
+ "▁de mand",
+ "▁dem and",
+ "lo r",
+ "l or",
+ "▁v eh",
+ "▁ve h",
+ "▁ veh",
+ "▁not ice",
+ "▁ notice",
+ "▁com ponent",
+ "▁compon ent",
+ "▁ component",
+ "f n",
+ "▁anal ysis",
+ "▁analy sis",
+ "▁analys is",
+ "▁ analysis",
+ "mat ch",
+ "m atch",
+ "▁effect ive",
+ "▁ effective",
+ "pro duct",
+ "produ ct",
+ "prod uct",
+ "ни к",
+ "▁le gal",
+ "▁leg al",
+ "▁ legal",
+ "е й",
+ "se mb",
+ "sem b",
+ "s emb",
+ "▁loc ated",
+ "▁locate d",
+ "▁с у",
+ "▁ су",
+ "Q L",
+ "in ct",
+ "inc t",
+ "et o",
+ "e to",
+ "Dr aw",
+ "D raw",
+ "▁sc ale",
+ "▁scal e",
+ "▁ scale",
+ "ро в",
+ "р ов",
+ "▁w ants",
+ "▁want s",
+ "H ow",
+ "▁w el",
+ "▁we l",
+ "is ions",
+ "ision s",
+ "isi ons",
+ "▁de liver",
+ "▁del iver",
+ "un der",
+ "und er",
+ "unde r",
+ "u nder",
+ "▁d eb",
+ "▁de b",
+ "▁j u",
+ "▁ ju",
+ "val ues",
+ "value s",
+ "▁s ister",
+ "▁si ster",
+ "▁sist er",
+ "ко в",
+ "к ов",
+ "▁C reate",
+ "▁Creat e",
+ "▁Cre ate",
+ "▁ Create",
+ "▁I nc",
+ "▁In c",
+ "▁a ux",
+ "▁au x",
+ "▁ aux",
+ "▁Wh ite",
+ "▁Whit e",
+ "▁ White",
+ "Me nu",
+ "Men u",
+ "M enu",
+ "au d",
+ "a ud",
+ "re source",
+ "res ource",
+ "▁c ab",
+ "▁ca b",
+ "▁l if",
+ "▁li f",
+ "▁ lif",
+ "▁c ulture",
+ "▁cult ure",
+ "ic he",
+ "ich e",
+ "i che",
+ "▁wh atever",
+ "▁what ever",
+ "▁de signed",
+ "▁des igned",
+ "▁design ed",
+ "▁re pe",
+ "▁rep e",
+ "▁M ont",
+ "▁Mon t",
+ "▁Mo nt",
+ "▁ Mont",
+ "▁ch arge",
+ "▁char ge",
+ "▁charg e",
+ "▁ charge",
+ "Name s",
+ "Na mes",
+ "N ames",
+ "▁in sp",
+ "▁ins p",
+ "▁custom ers",
+ "▁customer s",
+ "os a",
+ "o sa",
+ "▁d aughter",
+ "▁E ast",
+ "E Q",
+ "▁o pin",
+ "▁op in",
+ "▁F re",
+ "▁Fr e",
+ "▁se ek",
+ "▁see k",
+ "▁ seek",
+ "▁p ush",
+ "▁pu sh",
+ "▁ push",
+ "▁n av",
+ "▁na v",
+ "▁ nav",
+ "▁b urn",
+ "▁bu rn",
+ "▁bur n",
+ "▁ burn",
+ "ar den",
+ "ard en",
+ "arde n",
+ "ha sh",
+ "has h",
+ "h ash",
+ "▁opportun ity",
+ "▁M at",
+ "▁Ma t",
+ "▁ Mat",
+ "oy al",
+ "oya l",
+ "o yal",
+ "▁p un",
+ "▁pu n",
+ "sc ale",
+ "scal e",
+ "yn amic",
+ "ynam ic",
+ "yna mic",
+ "▁T ype",
+ "▁Ty pe",
+ "▁Typ e",
+ "▁ Type",
+ "il ing",
+ "ili ng",
+ "i ling",
+ "▁qu ery",
+ "▁que ry",
+ "▁quer y",
+ "▁ query",
+ "▁m ist",
+ "▁mis t",
+ "▁mi st",
+ "ro r",
+ "r or",
+ "for ce",
+ "▁On ce",
+ "▁ Once",
+ "▁med ical",
+ "▁medic al",
+ "▁medi cal",
+ "li e",
+ "l ie",
+ "▁stud ent",
+ "▁ student",
+ "ed eral",
+ "eder al",
+ "ede ral",
+ "▁l ov",
+ "▁lo v",
+ "▁ lov",
+ "if orm",
+ "i form",
+ "▁al tern",
+ "▁alt ern",
+ "▁alter n",
+ "▁ altern",
+ "bi n",
+ "b in",
+ "od er",
+ "ode r",
+ "o der",
+ "▁return s",
+ "▁ returns",
+ "reg ister",
+ "ut s",
+ "u ts",
+ "C I",
+ "▁T or",
+ "▁To r",
+ "▁ Tor",
+ "C R",
+ "▁L os",
+ "▁Lo s",
+ "▁ Los",
+ "am ily",
+ "ami ly",
+ "amil y",
+ "air e",
+ "ai re",
+ "a ire",
+ "++ ;",
+ "Cont roller",
+ "Control ler",
+ "wi de",
+ "wid e",
+ "w ide",
+ "x x",
+ "row ser",
+ "rows er",
+ "▁B ook",
+ "▁Bo ok",
+ "▁ Book",
+ "Cont ainer",
+ "pl oad",
+ "plo ad",
+ "p load",
+ "▁E v",
+ "▁ Ev",
+ "▁t al",
+ "▁ta l",
+ "▁ tal",
+ "▁the ory",
+ "eqn array",
+ "б е",
+ "▁rep orted",
+ "▁report ed",
+ "▁me aning",
+ "▁mean ing",
+ "▁s y",
+ "▁ sy",
+ "ri be",
+ "rib e",
+ "r ibe",
+ "ic ate",
+ "ica te",
+ "ho ld",
+ "hol d",
+ "h old",
+ "▁of fers",
+ "▁off ers",
+ "▁offer s",
+ "▁t empl",
+ "▁tem pl",
+ "▁temp l",
+ "cs s",
+ "c ss",
+ "▁p icture",
+ "▁pict ure",
+ "▁ picture",
+ "▁a sync",
+ "▁as ync",
+ "▁ async",
+ "▁st ock",
+ "▁sto ck",
+ "▁ stock",
+ "▁in ternal",
+ "▁inter nal",
+ "▁intern al",
+ "▁ internal",
+ "t i",
+ "B O",
+ "V er",
+ "с по",
+ "▁d emon",
+ "▁de mon",
+ "▁dem on",
+ "▁demo n",
+ "▁l augh",
+ "▁la ugh",
+ "▁laug h",
+ "▁E nd",
+ "▁En d",
+ "▁ End",
+ "▁k on",
+ "▁ko n",
+ "▁ kon",
+ "▁ide as",
+ "▁idea s",
+ "▁c andid",
+ "▁can did",
+ "▁cand id",
+ "Me m",
+ "M em",
+ "iz z",
+ "i zz",
+ "re fix",
+ "ref ix",
+ "▁A ND",
+ "▁AN D",
+ "▁ AND",
+ "eg en",
+ "e gen",
+ "E l",
+ "▁camp aign",
+ "H ttp",
+ "▁R ob",
+ "▁Ro b",
+ "▁ Rob",
+ "д і",
+ "▁b ul",
+ "▁bu l",
+ "▁ bul",
+ "▁К о",
+ "▁ Ко",
+ "▁count ries",
+ "▁countr ies",
+ "» .",
+ "▁ex pression",
+ "▁exp ression",
+ "▁express ion",
+ "▁expr ession",
+ "▁ expression",
+ "▁Eng land",
+ "s f",
+ "▁certain ly",
+ "ag en",
+ "age n",
+ "a gen",
+ "▁ч а",
+ "▁ ча",
+ "▁A NY",
+ "▁AN Y",
+ "▁ ANY",
+ "▁conne ct",
+ "▁conn ect",
+ "▁ connect",
+ "F E",
+ "▁and roid",
+ "▁ android",
+ "▁G old",
+ "▁Go ld",
+ "▁Gol d",
+ "▁ Gold",
+ "▁op pos",
+ "▁opp os",
+ "ov ern",
+ "ove rn",
+ "over n",
+ "o vern",
+ "▁Com mun",
+ "▁Comm un",
+ ", _",
+ "as ion",
+ "asi on",
+ "L a",
+ "▁f irm",
+ "▁fi rm",
+ "▁fir m",
+ "▁Al though",
+ "▁G ood",
+ "▁Go od",
+ "▁ Good",
+ "▁L aw",
+ "▁La w",
+ "er ve",
+ "erv e",
+ "▁b rand",
+ "▁br and",
+ "▁bra nd",
+ "▁ brand",
+ "M in",
+ "fil l",
+ "fi ll",
+ "f ill",
+ "'] ,",
+ "' ],",
+ "▁J ew",
+ "▁Je w",
+ "il er",
+ "ile r",
+ "i ler",
+ "in gle",
+ "ing le",
+ "it hub",
+ "ith ub",
+ "▁D iv",
+ "▁Di v",
+ "▁ Div",
+ "▁c ert",
+ "▁ce rt",
+ "▁cer t",
+ "▁ cert",
+ "He ight",
+ "H eight",
+ "ra el",
+ "r ael",
+ "The re",
+ "Th ere",
+ "T here",
+ "it ute",
+ "itut e",
+ "itu te",
+ "▁a maz",
+ "▁am az",
+ "▁ amaz",
+ "lo ok",
+ "l ook",
+ "▁S E",
+ "▁ SE",
+ "▁j o",
+ "▁ jo",
+ "▁pull ed",
+ "▁pul led",
+ "▁re sources",
+ "▁res ources",
+ "▁resource s",
+ "▁ resources",
+ "▁M ax",
+ "▁Ma x",
+ "▁ Max",
+ "▁ag reed",
+ "▁agree d",
+ "▁agre ed",
+ "as y",
+ "a sy",
+ "▁treat ment",
+ "\"> ",
+ "\">< /",
+ "\" >",
+ "ма н",
+ "м ан",
+ "▁E rr",
+ "▁Er r",
+ "▁ Err",
+ "or ig",
+ "ori g",
+ "o rig",
+ "co s",
+ "c os",
+ "▁May be",
+ "▁ Maybe",
+ "ot al",
+ "ota l",
+ "o tal",
+ "▁tr ain",
+ "▁tra in",
+ "▁ train",
+ "▁S ervice",
+ "▁Serv ice",
+ "▁ Service",
+ "▁i h",
+ "▁ ih",
+ "▁sp irit",
+ "▁spir it",
+ "Com p",
+ "Co mp",
+ "C omp",
+ "sq rt",
+ "▁b road",
+ "▁br oad",
+ "▁bro ad",
+ "▁ broad",
+ "} [",
+ "▁sh ape",
+ "▁sha pe",
+ "▁ shape",
+ "▁d oc",
+ "▁do c",
+ "▁ doc",
+ "ho w",
+ "h ow",
+ "▁t ag",
+ "▁ta g",
+ "▁ tag",
+ "ata log",
+ "atal og",
+ "s d",
+ "▁me as",
+ "▁Р о",
+ "▁ex ception",
+ "▁except ion",
+ "▁ exception",
+ "▁T w",
+ "▁ Tw",
+ "▁interest ing",
+ "AT A",
+ "A TA",
+ "▁R el",
+ "▁Re l",
+ "▁ Rel",
+ "á r",
+ "▁use ful",
+ "use um",
+ "▁b ottom",
+ "▁bott om",
+ "▁bot tom",
+ "▁ bottom",
+ "▁other wise",
+ "▁ag ree",
+ "▁agre e",
+ "ch t",
+ "c ht",
+ "th en",
+ "the n",
+ "t hen",
+ "▁signific ant",
+ "} /",
+ "▁ch annel",
+ "▁ channel",
+ "ic ial",
+ "ici al",
+ "icia l",
+ "i cial",
+ "ти в",
+ "var e",
+ "va re",
+ "v are",
+ "▁en ter",
+ "▁ent er",
+ "▁ enter",
+ "En g",
+ "E ng",
+ "u j",
+ "UR E",
+ "U RE",
+ "que ue",
+ "on o",
+ "o no",
+ "▁cont ains",
+ "▁contain s",
+ "▁ contains",
+ "M I",
+ "▁n ation",
+ "▁nat ion",
+ "▁r ules",
+ "▁rule s",
+ "▁ru les",
+ "▁rul es",
+ "▁ rules",
+ "fo l",
+ "f ol",
+ "▁p a",
+ "▁ pa",
+ "ar p",
+ "a rp",
+ "▁qu iet",
+ "▁qui et",
+ "▁t hus",
+ "▁th us",
+ "ip ped",
+ "ipp ed",
+ "i pped",
+ "an not",
+ "ann ot",
+ "anno t",
+ "ud es",
+ "ude s",
+ "u des",
+ "() :",
+ "( ):",
+ "name s",
+ "na mes",
+ "nam es",
+ "n ames",
+ "▁com pos",
+ "▁comp os",
+ "▁in j",
+ "un a",
+ "u na",
+ "bin d",
+ "bi nd",
+ "b ind",
+ "▁f ully",
+ "▁full y",
+ "▁ful ly",
+ "▁ fully",
+ "ra s",
+ "r as",
+ "Util s",
+ "Ut ils",
+ "an ges",
+ "ang es",
+ "ange s",
+ "du le",
+ "d ule",
+ "▁Christ ian",
+ "▁re ve",
+ "▁r eve",
+ "▁rev e",
+ "än d",
+ "ä nd",
+ "▁col lect",
+ "▁coll ect",
+ "▁colle ct",
+ "▁ collect",
+ "▁cele br",
+ "an da",
+ "and a",
+ "í n",
+ "jo in",
+ "j oin",
+ "▁p aid",
+ "▁pa id",
+ "▁ paid",
+ "Co re",
+ "Cor e",
+ "C ore",
+ "G e",
+ ". $",
+ "▁f if",
+ "▁fi f",
+ "▁ fif",
+ "▁u ma",
+ "▁um a",
+ "▁ uma",
+ "▁ ~",
+ "erv ices",
+ "ervice s",
+ "▁rec ently",
+ "▁recent ly",
+ "de sc",
+ "des c",
+ "d esc",
+ "▁he avy",
+ "▁heav y",
+ "▁r ule",
+ "▁ru le",
+ "▁rul e",
+ "▁ rule",
+ "▁P lease",
+ "▁Ple ase",
+ "▁ Please",
+ "ps i",
+ "p si",
+ "▁con sole",
+ "▁cons ole",
+ "▁ console",
+ "▁f ort",
+ "▁for t",
+ "▁fo rt",
+ "▁ fort",
+ ". \\",
+ "▁W ashington",
+ "▁g ar",
+ "▁ga r",
+ "▁ gar",
+ "▁G roup",
+ "▁Gr oup",
+ "▁Gro up",
+ "▁ Group",
+ "▁inter view",
+ "an ned",
+ "ann ed",
+ "anne d",
+ "sq l",
+ "s ql",
+ "▁a nc",
+ "▁an c",
+ "▁ anc",
+ "ј а",
+ "P ack",
+ "▁Cl ub",
+ "▁m ask",
+ "▁ma sk",
+ "▁mas k",
+ "▁ mask",
+ "▁con cept",
+ "▁conce pt",
+ "▁[ '",
+ "▁ ['",
+ "▁se lected",
+ "▁select ed",
+ "▁sele cted",
+ "▁ selected",
+ "▁U se",
+ "▁Us e",
+ "▁ Use",
+ "▁e le",
+ "▁el e",
+ "▁ ele",
+ "ear s",
+ "ea rs",
+ "e ars",
+ "▁r ace",
+ "▁rac e",
+ "▁ra ce",
+ "h y",
+ "O m",
+ "▁st eps",
+ "▁ste ps",
+ "▁step s",
+ "▁ steps",
+ "il a",
+ "i la",
+ "es ts",
+ "est s",
+ "e sts",
+ "ed s",
+ "e ds",
+ "▁stre et",
+ "ne rs",
+ "ner s",
+ "n ers",
+ "▁b irth",
+ "po p",
+ "p op",
+ "▁ ли",
+ "M B",
+ "к ра",
+ "ci r",
+ "c ir",
+ "eps ilon",
+ "e psilon",
+ "▁con stant",
+ "▁const ant",
+ "▁ constant",
+ "qu es",
+ "que s",
+ "q ues",
+ "ad as",
+ "ada s",
+ "a das",
+ "▁kn ows",
+ "▁know s",
+ "▁P y",
+ "▁ Py",
+ "cl es",
+ "cle s",
+ "c les",
+ "▁c it",
+ "▁ci t",
+ "▁ cit",
+ "▁p air",
+ "▁pa ir",
+ "▁ pair",
+ "in ese",
+ "ine se",
+ "ines e",
+ "▁P eter",
+ "▁Pe ter",
+ "▁Pet er",
+ "▁Pete r",
+ "▁fin ished",
+ "▁finish ed",
+ "▁ finished",
+ "▁m aster",
+ "▁ma ster",
+ "▁mas ter",
+ "▁mast er",
+ "▁ master",
+ "▁tw enty",
+ "▁f ell",
+ "▁fe ll",
+ "▁fel l",
+ "▁cent ral",
+ "▁m es",
+ "▁me s",
+ "▁ mes",
+ "re v",
+ "r ev",
+ "ST AT",
+ "st at",
+ "sta t",
+ "s tat",
+ "▁all ows",
+ "▁allow s",
+ "▁g ro",
+ "▁gr o",
+ "▁ gro",
+ "Cl ick",
+ "C lick",
+ "▁st ories",
+ "▁stor ies",
+ "▁sto ries",
+ "F e",
+ "å r",
+ "▁b aby",
+ "▁bab y",
+ "▁ba by",
+ "en cia",
+ "enc ia",
+ "enci a",
+ "e ncia",
+ "▁e iner",
+ "▁ein er",
+ "▁eine r",
+ "Ar e",
+ "A re",
+ "eb ug",
+ "e bug",
+ "st ore",
+ "sto re",
+ "\", \"",
+ "\" ,\"",
+ "la m",
+ "l am",
+ "▁s v",
+ "▁ sv",
+ "ци и",
+ "NU LL",
+ "N ULL",
+ "▁L eg",
+ "▁Le g",
+ "▁ Leg",
+ "▁m ovie",
+ "▁mov ie",
+ "▁h ous",
+ "▁ho us",
+ "▁learn ed",
+ "▁lear ned",
+ "bo n",
+ "b on",
+ "▁trans fer",
+ "▁ transfer",
+ "iforn ia",
+ "ps ilon",
+ "psi lon",
+ "▁S oft",
+ "▁So ft",
+ "▁Sof t",
+ "▁ Soft",
+ "▁com mer",
+ "▁comm er",
+ "▁comme r",
+ "▁had n",
+ "▁ha dn",
+ "▁E in",
+ "▁T wo",
+ "▁Tw o",
+ "▁ Two",
+ "cr aft",
+ "c raft",
+ "Pro cess",
+ "Proc ess",
+ "▁по д",
+ "ar gin",
+ "arg in",
+ "▁est im",
+ "▁es tim",
+ "▁M em",
+ "▁Me m",
+ "▁ Mem",
+ "ik a",
+ "i ka",
+ "▁T od",
+ "▁To d",
+ "du c",
+ "d uc",
+ "▁d anger",
+ "▁dan ger",
+ "ri ve",
+ "riv e",
+ "r ive",
+ "Do n",
+ "D on",
+ "▁Q ue",
+ "▁Qu e",
+ "▁ Que",
+ "ha l",
+ "h al",
+ "▁m m",
+ "▁ mm",
+ "▁S ur",
+ "▁Su r",
+ "▁ Sur",
+ "Or der",
+ "Ord er",
+ "▁d istribution",
+ "▁distribut ion",
+ "f a",
+ "▁M any",
+ "▁Man y",
+ "▁Ma ny",
+ "▁ Many",
+ "pl icit",
+ "plic it",
+ "Em pty",
+ "Emp ty",
+ "Hand le",
+ "▁t oken",
+ "▁to ken",
+ "▁tok en",
+ "▁ token",
+ "▁e pis",
+ "▁ep is",
+ "▁ass ist",
+ "▁pur pose",
+ "▁ ц",
+ "N U",
+ "id ers",
+ "ide rs",
+ "ider s",
+ "i ders",
+ "ra te",
+ "rat e",
+ "r ate",
+ "The y",
+ "Th ey",
+ "Param eter",
+ "De c",
+ "D ec",
+ "▁str ugg",
+ "▁stru gg",
+ "▁sh oot",
+ "I V",
+ "▁G reat",
+ "▁Gre at",
+ "▁ Great",
+ "▁S il",
+ "▁Si l",
+ "▁ Sil",
+ "▁l oved",
+ "▁lo ved",
+ "▁love d",
+ "▁lov ed",
+ "▁c lick",
+ "▁cl ick",
+ "▁ click",
+ "▁re serv",
+ "▁res erv",
+ "▁в е",
+ "▁ ве",
+ "▁s pread",
+ "▁sp read",
+ "▁spr ead",
+ "▁o g",
+ "▁ og",
+ "▁$ {",
+ "▁ ${",
+ "▁m iles",
+ "▁mil es",
+ "▁mi les",
+ "▁mile s",
+ "▁success ful",
+ "▁ successful",
+ "o j",
+ "▁D irect",
+ "▁Di rect",
+ "▁Dire ct",
+ "▁Dir ect",
+ "▁ Direct",
+ "▁a x",
+ "▁ ax",
+ "▁grow th",
+ "W ork",
+ "▁ch urch",
+ "In st",
+ "Ins t",
+ "IC E",
+ "I CE",
+ "st en",
+ "ste n",
+ "s ten",
+ "ро д",
+ "▁C enter",
+ "▁Cent er",
+ "▁ Center",
+ "se s",
+ "s es",
+ "go t",
+ "g ot",
+ "de lete",
+ "del ete",
+ "▁M a",
+ "▁ Ma",
+ "% %",
+ "▁c row",
+ "▁cr ow",
+ "▁cro w",
+ "D F",
+ "fr ont",
+ "▁b log",
+ "▁bl og",
+ "▁blo g",
+ "▁ blog",
+ "▁comp uter",
+ "▁comput er",
+ "▁compute r",
+ "на я",
+ "▁m ir",
+ "▁mi r",
+ "▁ mir",
+ "▁S uper",
+ "▁Su per",
+ "▁Sup er",
+ "▁ Super",
+ "', '",
+ "' ,'",
+ "▁mult i",
+ "▁mul ti",
+ "▁ multi",
+ "▁g ru",
+ "▁gr u",
+ "▁ gru",
+ "▁J o",
+ "▁ Jo",
+ "▁Can ada",
+ "▁Canad a",
+ "▁Th omas",
+ "▁Thom as",
+ "▁large r",
+ "▁larg er",
+ "▁com par",
+ "▁comp ar",
+ "▁ compar",
+ "Cur rent",
+ "th at",
+ "tha t",
+ "t hat",
+ "▁d rop",
+ "▁dr op",
+ "▁dro p",
+ "▁ drop",
+ "ен т",
+ "▁Re public",
+ "▁Rep ublic",
+ "▁Repub lic",
+ "▁d ise",
+ "▁dis e",
+ "▁di se",
+ "▁effect s",
+ "▁girl s",
+ "▁gir ls",
+ "en cies",
+ "enc ies",
+ "enci es",
+ "el lig",
+ "ell ig",
+ "elli g",
+ "▁N ote",
+ "▁No te",
+ "▁Not e",
+ "▁ Note",
+ "▁Ass oci",
+ "▁ Associ",
+ "▁u ses",
+ "▁us es",
+ "▁use s",
+ "▁ uses",
+ "el led",
+ "ell ed",
+ "elle d",
+ "▁w arm",
+ "▁war m",
+ "▁wa rm",
+ "th read",
+ "fo nt",
+ "fon t",
+ "f ont",
+ "▁z um",
+ "▁zu m",
+ "▁follow s",
+ "▁w hom",
+ "▁wh om",
+ "▁who m",
+ "T A",
+ "▁w ild",
+ "▁A R",
+ "▁ AR",
+ "ia ble",
+ "i able",
+ "▁Tr ue",
+ "▁Tru e",
+ "▁ True",
+ "Pos ition",
+ "▁s ell",
+ "▁se ll",
+ "▁sel l",
+ "ch er",
+ "che r",
+ "c her",
+ "▁B us",
+ "▁Bu s",
+ "▁ Bus",
+ "▁le an",
+ "▁ lean",
+ "AC E",
+ "A CE",
+ "▁s erved",
+ "▁ser ved",
+ "▁serv ed",
+ "▁serve d",
+ "h w",
+ "▁C ur",
+ "▁Cu r",
+ "▁ Cur",
+ "▁n orth",
+ "▁nor th",
+ "▁nort h",
+ "Da t",
+ "D at",
+ "▁> >",
+ "▁ >>",
+ "com mand",
+ "comm and",
+ "at z",
+ "a tz",
+ "▁m al",
+ "▁ma l",
+ "▁ mal",
+ "ста в",
+ "▁P ress",
+ "▁Pr ess",
+ "▁Pres s",
+ "▁Pre ss",
+ "▁ Press",
+ "▁char acters",
+ "▁character s",
+ "▁z ero",
+ "▁ze ro",
+ "▁ zero",
+ "AG E",
+ "A GE",
+ "rap per",
+ "▁kit chen",
+ "am ing",
+ "ami ng",
+ "amin g",
+ "a ming",
+ "▁re str",
+ "▁r estr",
+ "▁res tr",
+ "▁rest r",
+ "X X",
+ "▁Col lege",
+ "▁Ar ray",
+ "▁Arr ay",
+ "▁ Array",
+ "▁f resh",
+ "▁fr esh",
+ "▁fre sh",
+ "▁fres h",
+ "▁sh ift",
+ "▁ shift",
+ "▁spec ified",
+ "pl ete",
+ "ple te",
+ "plet e",
+ "p lete",
+ "IT E",
+ "I TE",
+ "▁C amp",
+ "▁Cam p",
+ "▁Ca mp",
+ "▁ Camp",
+ "ri al",
+ "ria l",
+ "r ial",
+ "c b",
+ "▁T H",
+ "▁ TH",
+ "I B",
+ "os en",
+ "ose n",
+ "o sen",
+ "▁ ú",
+ "▁par ams",
+ "▁param s",
+ "▁para ms",
+ "▁ params",
+ "ign ment",
+ "ad ding",
+ "add ing",
+ "▁deg ree",
+ "▁ degree",
+ "Loc al",
+ "Lo cal",
+ "L ocal",
+ "O h",
+ "▁z ur",
+ "▁zu r",
+ "▁level s",
+ "▁lev els",
+ "C S",
+ "fin ished",
+ "finish ed",
+ "C ase",
+ "ri age",
+ "ria ge",
+ "Vec tor",
+ "V ector",
+ "▁s ea",
+ "▁se a",
+ "▁ sea",
+ "ant ic",
+ "anti c",
+ "▁Le ague",
+ "▁there fore",
+ "▁ther efore",
+ "On e",
+ "O ne",
+ "Re turn",
+ "Ret urn",
+ "R eturn",
+ "Acc ess",
+ "Ac cess",
+ "A ccess",
+ "va s",
+ "v as",
+ "▁о с",
+ "▁r at",
+ "▁ra t",
+ "▁ rat",
+ "Bi g",
+ "B ig",
+ "▁be havior",
+ "▁behav ior",
+ "▁behavi or",
+ "k r",
+ "▁un defined",
+ "▁und efined",
+ "▁ undefined",
+ "▁E s",
+ "▁ Es",
+ "▁appe ared",
+ "▁appear ed",
+ "el es",
+ "ele s",
+ "e les",
+ "▁W AR",
+ "▁WA R",
+ "▁ WAR",
+ "St at",
+ "S tat",
+ "▁Go ogle",
+ "▁ Google",
+ "▁c redit",
+ "▁cre dit",
+ "▁cr edit",
+ "▁cred it",
+ "▁F ile",
+ "▁Fil e",
+ "▁Fi le",
+ "▁ File",
+ "an ging",
+ "ang ing",
+ "ho use",
+ "hou se",
+ "h ouse",
+ "rom ise",
+ "ge nt",
+ "gen t",
+ "g ent",
+ "▁hab it",
+ "▁ha bit",
+ "▁soc iety",
+ "▁soci ety",
+ "▁societ y",
+ "▁enc our",
+ "▁p aint",
+ "▁pain t",
+ "▁pa int",
+ "pe t",
+ "p et",
+ "▁U K",
+ "▁ UK",
+ "aw s",
+ "a ws",
+ "on om",
+ "ono m",
+ "o nom",
+ "G l",
+ "}_ {\\",
+ "}_{ \\",
+ "} _{\\",
+ "el ess",
+ "ele ss",
+ "eles s",
+ "e less",
+ "em y",
+ "e my",
+ "▁C ong",
+ "▁Con g",
+ "▁Co ng",
+ "▁develop ed",
+ "▁im ages",
+ "▁image s",
+ "▁imag es",
+ "▁ images",
+ "▁ ö",
+ "▁f ont",
+ "▁fo nt",
+ "▁fon t",
+ "▁ font",
+ "cl ear",
+ "cle ar",
+ "c lear",
+ "gi n",
+ "g in",
+ "▁L ord",
+ "▁Lo rd",
+ "▁Lor d",
+ "▁trans port",
+ "▁ transport",
+ "▁: :",
+ "▁ ::",
+ "▁c up",
+ "▁cu p",
+ "▁ cup",
+ "ul ate",
+ "ula te",
+ "u late",
+ "▁D uring",
+ "▁Du ring",
+ "▁Dur ing",
+ "pr iv",
+ "p riv",
+ "▁ext rem",
+ "▁extr em",
+ "▁D i",
+ "▁ Di",
+ "▁d oubt",
+ "▁dou bt",
+ "▁doub t",
+ "P y",
+ "if ying",
+ "ify ing",
+ "sp lit",
+ "spl it",
+ "s plit",
+ "eg o",
+ "e go",
+ "git hub",
+ "g ithub",
+ "▁) ,",
+ "▁ ),",
+ "RO M",
+ "R OM",
+ "▁ch air",
+ "▁cha ir",
+ "▁ chair",
+ "▁t rade",
+ "▁tr ade",
+ "▁trad e",
+ "▁tra de",
+ "▁n icht",
+ "▁ni cht",
+ "▁nic ht",
+ "To p",
+ "T op",
+ "St ore",
+ "▁p arte",
+ "▁part e",
+ "▁par te",
+ "pro ject",
+ "ni a",
+ "n ia",
+ "▁в ід",
+ "▁ві д",
+ "wa r",
+ "w ar",
+ "▁Pro f",
+ "▁Pr of",
+ "▁c aught",
+ "Th read",
+ "ст ва",
+ "ств а",
+ "с тва",
+ "aut hor",
+ "auth or",
+ "▁d oll",
+ "▁do ll",
+ "▁dol l",
+ "▁h arm",
+ "▁ha rm",
+ "▁har m",
+ "▁ harm",
+ "▁G en",
+ "▁Ge n",
+ "▁ Gen",
+ "tr ee",
+ "tre e",
+ "t ree",
+ "et ime",
+ "eti me",
+ "e time",
+ "cf g",
+ "c fg",
+ "▁gu ys",
+ "▁guy s",
+ "▁Cal ifornia",
+ "▁G reen",
+ "▁Gr een",
+ "▁Gre en",
+ "▁Gree n",
+ "▁ Green",
+ "▁mov ement",
+ "▁move ment",
+ "▁mo vement",
+ "ie j",
+ "i ej",
+ "▁stat ement",
+ "▁state ment",
+ "▁ statement",
+ "▁se eing",
+ "▁see ing",
+ "▁h aven",
+ "▁have n",
+ "▁ha ven",
+ "▁hav en",
+ "vent ion",
+ "v ention",
+ "S L",
+ "ched ul",
+ "ie rt",
+ "ier t",
+ "i ert",
+ "▁pr imary",
+ "▁prim ary",
+ "▁pri mary",
+ "▁prima ry",
+ "▁ primary",
+ "▁c ivil",
+ "▁ci vil",
+ "▁civ il",
+ "ri an",
+ "ria n",
+ "r ian",
+ "▁b utton",
+ "▁but ton",
+ "▁butt on",
+ "▁ button",
+ "▁l ived",
+ "▁li ved",
+ "▁live d",
+ "▁liv ed",
+ "P ass",
+ "so r",
+ "s or",
+ "▁watch ing",
+ "▁wat ching",
+ "▁sk ills",
+ "▁skill s",
+ "te e",
+ "t ee",
+ "Le vel",
+ "L evel",
+ "▁sc ient",
+ "h s",
+ "▁a gre",
+ "▁ag re",
+ "ca t",
+ "c at",
+ "▁t end",
+ "▁te nd",
+ "▁ten d",
+ "▁M ill",
+ "▁Mil l",
+ "▁Mi ll",
+ "▁ Mill",
+ "▁C ap",
+ "▁Ca p",
+ "▁ Cap",
+ "OR D",
+ "O RD",
+ "gl e",
+ "g le",
+ "▁с во",
+ "» ,",
+ "▁a head",
+ "▁ah ead",
+ "ve st",
+ "ves t",
+ "v est",
+ "▁J ose",
+ "▁Jo se",
+ "▁Jos e",
+ "is cher",
+ "isch er",
+ "ische r",
+ "isc her",
+ "ș i",
+ "▁le aving",
+ "▁д ля",
+ "▁s outh",
+ "▁so uth",
+ "▁sou th",
+ "▁sout h",
+ "▁con sum",
+ "▁cons um",
+ "▁ consum",
+ "R ange",
+ "▁activ ities",
+ "Se c",
+ "S ec",
+ "▁s ales",
+ "▁sa les",
+ "▁sal es",
+ "▁sale s",
+ "▁f ix",
+ "▁fi x",
+ "▁ fix",
+ "▁j ed",
+ "▁je d",
+ "▁ jed",
+ "ru m",
+ "r um",
+ "ve ctor",
+ "vec tor",
+ "v ector",
+ "▁s pot",
+ "▁sp ot",
+ "▁spo t",
+ "▁ spot",
+ "▁man ufact",
+ "к т",
+ "or row",
+ "orr ow",
+ "si gn",
+ "sig n",
+ "s ign",
+ "▁col lege",
+ "▁colle ge",
+ "▁colleg e",
+ "▁d river",
+ "▁dr iver",
+ "▁dri ver",
+ "▁driv er",
+ "▁drive r",
+ "▁ driver",
+ "▁def initely",
+ "▁definit ely",
+ "▁s pend",
+ "▁sp end",
+ "▁spe nd",
+ "miss ion",
+ "m ission",
+ "з у",
+ "at ively",
+ "ative ly",
+ "ativ ely",
+ "b i",
+ "Call back",
+ "▁particular ly",
+ "▁particul arly",
+ "▁h ell",
+ "▁he ll",
+ "▁hel l",
+ "▁ hell",
+ "▁p ool",
+ "▁po ol",
+ "▁ pool",
+ "PR E",
+ "P RE",
+ "▁cle arly",
+ "▁clear ly",
+ "P T",
+ "ot hes",
+ "oth es",
+ "othe s",
+ "▁I d",
+ "▁ Id",
+ "Loc ation",
+ "L ocation",
+ "▁R un",
+ "▁Ru n",
+ "▁ Run",
+ "▁f ixed",
+ "▁fix ed",
+ "▁ fixed",
+ "▁H and",
+ "▁Ha nd",
+ "▁Han d",
+ "▁ Hand",
+ "ba l",
+ "b al",
+ "d ouble",
+ "C an",
+ "Om ega",
+ "▁chall eng",
+ "▁stand ing",
+ "▁stan ding",
+ "▁ standing",
+ "it en",
+ "ite n",
+ "i ten",
+ "▁me chan",
+ "▁d urch",
+ "▁dur ch",
+ "▁d ell",
+ "▁de ll",
+ "▁del l",
+ "▁rais ed",
+ "▁raise d",
+ "▁ra ised",
+ "▁we ak",
+ "▁ weak",
+ "▁D u",
+ "▁ Du",
+ "gr ad",
+ "gra d",
+ "g rad",
+ "▁sc ene",
+ "▁scen e",
+ "▁ scene",
+ "pos s",
+ "po ss",
+ "p oss",
+ "▁t on",
+ "▁to n",
+ "▁ ton",
+ "▁e arth",
+ "▁ear th",
+ "ul ations",
+ "ulation s",
+ "▁str ength",
+ "▁stre ngth",
+ "▁streng th",
+ "ak ed",
+ "ake d",
+ "a ked",
+ "▁re main",
+ "▁rem ain",
+ "▁B i",
+ "▁ Bi",
+ "▁custom er",
+ "▁cust omer",
+ "▁ customer",
+ "ran ge",
+ "r ange",
+ "▁inter ested",
+ "▁interest ed",
+ "ON E",
+ "O NE",
+ "▁c off",
+ "▁co ff",
+ "re quire",
+ "requ ire",
+ "▁On ly",
+ "▁ Only",
+ "▁W eb",
+ "▁We b",
+ "▁ Web",
+ "▁f arm",
+ "▁far m",
+ "▁fa rm",
+ "▁act ivity",
+ "▁activ ity",
+ "▁ activity",
+ "▁r out",
+ "▁ro ut",
+ "▁rou t",
+ "bl ing",
+ "b ling",
+ "S Y",
+ "▁Rich ard",
+ "▁Ric hard",
+ "▁R ef",
+ "▁Re f",
+ "▁ Ref",
+ "▁ко н",
+ "▁к он",
+ "▁ кон",
+ "▁j un",
+ "▁ju n",
+ "bo rn",
+ "bor n",
+ "b orn",
+ "ij n",
+ "Config uration",
+ "um an",
+ "uma n",
+ "u man",
+ "E E",
+ "▁mar ried",
+ "▁З а",
+ "▁ За",
+ "▁f at",
+ "▁fa t",
+ "▁k id",
+ "▁ki d",
+ "▁T ur",
+ "▁Tu r",
+ "▁ Tur",
+ "▁off ered",
+ "▁offer ed",
+ "ni c",
+ "n ic",
+ "▁B ig",
+ "▁Bi g",
+ "▁ Big",
+ "Ga mma",
+ "G amma",
+ "▁He alth",
+ "▁ Health",
+ "▁T R",
+ "▁ TR",
+ "▁s ię",
+ "▁si ę",
+ "▁const ruction",
+ "▁construct ion",
+ "▁constr uction",
+ "▁constru ction",
+ "▁ construction",
+ "▁Ch urch",
+ "▁B et",
+ "▁Be t",
+ "▁ Bet",
+ "bu s",
+ "b us",
+ "▁e arn",
+ "▁ear n",
+ "ri ct",
+ "ric t",
+ "r ict",
+ "▁п ра",
+ "▁пр а",
+ "▁ пра",
+ "▁br ain",
+ "▁bra in",
+ "▁f ra",
+ "▁fr a",
+ "▁O p",
+ "▁ Op",
+ "FI G",
+ "F IG",
+ "em a",
+ "e ma",
+ "▁Europe an",
+ "▁S aint",
+ "▁Sa int",
+ "▁ Saint",
+ "AR E",
+ "A RE",
+ "ur i",
+ "u ri",
+ "▁R iver",
+ "{ }",
+ "▁s itting",
+ "▁sit ting",
+ "▁under standing",
+ "▁understand ing",
+ "▁pl ans",
+ "▁plan s",
+ "rop ri",
+ "▁old er",
+ "▁ol der",
+ "▁ older",
+ "▁pres sure",
+ "▁press ure",
+ "Im pl",
+ "Imp l",
+ "▁pe ace",
+ "Conne ction",
+ "Conn ection",
+ "Connect ion",
+ "▁f i",
+ "▁ fi",
+ "ri ch",
+ "ric h",
+ "r ich",
+ "▁sh ut",
+ "ap ers",
+ "ape rs",
+ "aper s",
+ "a pers",
+ "Po rt",
+ "P ort",
+ "▁L ook",
+ "▁Lo ok",
+ "▁ Look",
+ "ri m",
+ "r im",
+ "au th",
+ "aut h",
+ "a uth",
+ "au to",
+ "aut o",
+ "a uto",
+ "▁high ly",
+ "▁un less",
+ "▁W al",
+ "▁Wa l",
+ "▁re n",
+ "▁r en",
+ "▁ ren",
+ "w s",
+ "▁c ore",
+ "▁co re",
+ "▁cor e",
+ "▁ core",
+ "( -",
+ "▁c lim",
+ "▁cl im",
+ "ru it",
+ "r uit",
+ "▁call back",
+ "▁ callback",
+ "he st",
+ "hes t",
+ "h est",
+ "▁Char les",
+ "▁Charl es",
+ "▁L ong",
+ "▁Lo ng",
+ "▁ Long",
+ "} =",
+ "ъ р",
+ "▁sh ared",
+ "▁share d",
+ "▁shar ed",
+ "▁sha red",
+ "▁ shared",
+ "ul ated",
+ "ula ted",
+ "ulate d",
+ "gorith m",
+ "▁H ome",
+ "▁Ho me",
+ "▁Hom e",
+ "▁ Home",
+ "▁vill age",
+ "▁vil lage",
+ "ee s",
+ "e es",
+ "s v",
+ "▁rest aur",
+ "re y",
+ "r ey",
+ "▁C ast",
+ "▁Cas t",
+ "▁Ca st",
+ "▁ Cast",
+ "▁P erson",
+ "▁Per son",
+ "▁Pers on",
+ "▁ Person",
+ "ки й",
+ "▁organ iz",
+ "▁R ad",
+ "▁Ra d",
+ "▁ Rad",
+ "pon ents",
+ "ponent s",
+ "▁wer den",
+ "▁werd en",
+ "▁b ow",
+ "▁bo w",
+ "▁ bow",
+ "se n",
+ "s en",
+ "am i",
+ "a mi",
+ "Inter face",
+ "▁b asis",
+ "▁bas is",
+ "▁ba sis",
+ "▁Comp any",
+ "▁Compan y",
+ "▁ Company",
+ "er nel",
+ "ern el",
+ "erne l",
+ "it u",
+ "i tu",
+ "Has h",
+ "Ha sh",
+ "H ash",
+ "▁a an",
+ "▁ х",
+ "▁s mile",
+ "▁sm ile",
+ "x ml",
+ "▁s cen",
+ "▁sc en",
+ "am m",
+ "a mm",
+ "to ol",
+ "too l",
+ "t ool",
+ "ar ia",
+ "ari a",
+ "a ria",
+ "▁acc ur",
+ "▁ac cur",
+ "▁ accur",
+ "set tings",
+ "setting s",
+ "▁Jes us",
+ "ac ement",
+ "ace ment",
+ "po wer",
+ "pow er",
+ "p ower",
+ "( !",
+ "▁c alls",
+ "▁call s",
+ "▁cal ls",
+ "▁ calls",
+ "▁bas ic",
+ "▁ basic",
+ "▁set tings",
+ "▁sett ings",
+ "▁setting s",
+ "▁ settings",
+ "ri pt",
+ "rip t",
+ "r ipt",
+ "po ol",
+ "p ool",
+ "ct ors",
+ "ctor s",
+ "▁Found ation",
+ "▁ Foundation",
+ "▁we ap",
+ "KE Y",
+ "K EY",
+ "fo ot",
+ "foo t",
+ "f oot",
+ "▁r adio",
+ "▁rad io",
+ "▁radi o",
+ "▁ radio",
+ "▁hel ped",
+ "▁help ed",
+ "ma nn",
+ "man n",
+ "m ann",
+ "▁j ump",
+ "▁ju mp",
+ "▁t ick",
+ "▁ti ck",
+ "▁ tick",
+ "▁gr owing",
+ "▁grow ing",
+ "▁gro wing",
+ "at en",
+ "ate n",
+ "a ten",
+ "re al",
+ "rea l",
+ "▁incre asing",
+ "Dev ice",
+ "var epsilon",
+ "vare psilon",
+ "▁s ets",
+ "▁se ts",
+ "▁set s",
+ "▁ sets",
+ "▁adv ant",
+ "Op en",
+ "O pen",
+ "▁re asons",
+ "▁reason s",
+ "▁sup posed",
+ "▁supp osed",
+ "▁suppose d",
+ "oe s",
+ "o es",
+ "ed e",
+ "e de",
+ "te en",
+ "tee n",
+ "t een",
+ "if def",
+ "▁de lete",
+ "▁del ete",
+ "▁delet e",
+ "▁ delete",
+ "▁& =",
+ "▁ &=",
+ "▁B ill",
+ "▁Bi ll",
+ "▁Bil l",
+ "▁ Bill",
+ "▁a im",
+ "▁ai m",
+ "▁ aim",
+ "▁O k",
+ "▁ Ok",
+ "▁A v",
+ "▁ Av",
+ "re ci",
+ "rec i",
+ "ac ks",
+ "ack s",
+ "a cks",
+ "is te",
+ "ist e",
+ "i ste",
+ "Pro perties",
+ "▁t mp",
+ "▁tm p",
+ "▁ tmp",
+ "▁d ei",
+ "▁de i",
+ "PE R",
+ "P ER",
+ "D C",
+ "st a",
+ "s ta",
+ "ни и",
+ "▁lim ited",
+ "▁limit ed",
+ "▁ limited",
+ "▁great er",
+ "▁gre ater",
+ "de scription",
+ "des cription",
+ "or i",
+ "o ri",
+ "ain ts",
+ "aint s",
+ "▁h y",
+ "▁ hy",
+ "▁M el",
+ "▁Me l",
+ "▁C H",
+ "▁ CH",
+ "con s",
+ "co ns",
+ "c ons",
+ "▁sur round",
+ "▁W ho",
+ "▁Wh o",
+ "▁ Who",
+ "ar c",
+ "a rc",
+ "▁te lev",
+ "▁tele v",
+ "▁tel ev",
+ "it ution",
+ "itut ion",
+ "▁e qual",
+ "▁equ al",
+ "▁eq ual",
+ "▁ equal",
+ "к і",
+ "▁Is rael",
+ "ä h",
+ "▁C aption",
+ "▁Capt ion",
+ "▁Ca ption",
+ "▁ex erc",
+ "em por",
+ "emp or",
+ "▁+ +",
+ "▁ ++",
+ "▁l ib",
+ "▁li b",
+ "▁ lib",
+ "ma ke",
+ "m ake",
+ "▁M A",
+ "▁ MA",
+ "co py",
+ "cop y",
+ "c opy",
+ "f riend",
+ "▁ко то",
+ "▁ кото",
+ "▁dam age",
+ "▁\\ ,",
+ "▁ \\,",
+ "od ed",
+ "ode d",
+ "o ded",
+ "▁n one",
+ "▁no ne",
+ "▁non e",
+ "▁ none",
+ "▁ev alu",
+ "▁eval u",
+ "▁ evalu",
+ "st on",
+ "sto n",
+ "s ton",
+ "> ,",
+ "FO R",
+ "F OR",
+ "▁n orm",
+ "▁no rm",
+ "▁nor m",
+ "▁ norm",
+ "ap pe",
+ "app e",
+ "a ppe",
+ "S ession",
+ "▁ad ult",
+ "▁h ospital",
+ "▁hosp ital",
+ "▁recomm end",
+ "pro perty",
+ "ste in",
+ "fin al",
+ "fi nal",
+ "f inal",
+ "▁n u",
+ "▁ nu",
+ "se cond",
+ "sec ond",
+ "▁a spect",
+ "▁as pect",
+ "▁asp ect",
+ "\") ]",
+ "\" )]",
+ "же н",
+ "ж ен",
+ "am ento",
+ "ament o",
+ "amen to",
+ "▁r ac",
+ "▁ra c",
+ "▁ rac",
+ "sa ve",
+ "s ave",
+ "▁foot ball",
+ "A b",
+ "un gs",
+ "ung s",
+ "ab il",
+ "abi l",
+ "a bil",
+ "▁Ar ch",
+ "▁Arc h",
+ "▁ Arch",
+ "sys tem",
+ "s ystem",
+ "hi st",
+ "his t",
+ "h ist",
+ "▁l uck",
+ "▁lu ck",
+ "▁luc k",
+ "re nder",
+ "ren der",
+ "rend er",
+ "r ender",
+ "▁se in",
+ "▁sei n",
+ "ion i",
+ "io ni",
+ "i oni",
+ "▁r ot",
+ "▁ro t",
+ "▁ rot",
+ "▁cor ner",
+ "▁corn er",
+ "▁app ropri",
+ "▁ap propri",
+ "▁ appropri",
+ "▁Soft ware",
+ "▁t ele",
+ "▁te le",
+ "▁tel e",
+ "▁ tele",
+ "De lete",
+ "Dele te",
+ "Del ete",
+ "▁Acc ording",
+ "▁pr ison",
+ "▁pri son",
+ "▁ prison",
+ "▁l ic",
+ "▁li c",
+ "▁ lic",
+ "▁м и",
+ "▁ ми",
+ "ter m",
+ "te rm",
+ "t erm",
+ "se ts",
+ "set s",
+ "s ets",
+ "▁v el",
+ "▁ve l",
+ "▁ vel",
+ "▁r ank",
+ "▁ran k",
+ "▁ rank",
+ "▁ex isting",
+ "▁exist ing",
+ "▁ existing",
+ "▁V ir",
+ "▁Vi r",
+ "▁t rip",
+ "▁tr ip",
+ "▁tri p",
+ "▁м у",
+ "▁ му",
+ "av ax",
+ "ava x",
+ "▁r is",
+ "▁ri s",
+ "▁ ris",
+ "▁def ine",
+ "▁defin e",
+ "▁ define",
+ "▁he at",
+ "ca r",
+ "c ar",
+ "▁con vert",
+ "▁conv ert",
+ "▁conver t",
+ "▁conve rt",
+ "▁ convert",
+ "em ail",
+ "ema il",
+ "e mail",
+ "▁U nder",
+ "▁Un der",
+ "▁Und er",
+ "▁ Under",
+ "▁ Ш",
+ "▁G rand",
+ "▁Gr and",
+ "▁Gran d",
+ "▁Gra nd",
+ "▁ex ists",
+ "▁exist s",
+ "▁ exists",
+ "sy s",
+ "s ys",
+ "ef f",
+ "e ff",
+ "▁T op",
+ "▁To p",
+ "▁ Top",
+ "▁ č",
+ "▁t empor",
+ "▁tem por",
+ "▁temp or",
+ "▁tempo r",
+ "▁arg uments",
+ "▁argument s",
+ "▁ arguments",
+ "▁support ed",
+ "▁supp orted",
+ "▁ supported",
+ "en sed",
+ "ens ed",
+ "ense d",
+ "▁Franc is",
+ "▁co ord",
+ "▁ coord",
+ "▁achie ve",
+ "▁N ame",
+ "▁Na me",
+ "▁Nam e",
+ "▁ Name",
+ "▁J ahr",
+ "▁Jah r",
+ "▁Ja hr",
+ "▁G i",
+ "sh e",
+ "s he",
+ "▁D ev",
+ "▁De v",
+ "▁ Dev",
+ "▁a lla",
+ "▁al la",
+ "▁all a",
+ "▁ alla",
+ "▁W IT",
+ "ag ment",
+ "c ustom",
+ "al ls",
+ "all s",
+ "& &",
+ "W E",
+ "▁h olding",
+ "▁hold ing",
+ "▁hol ding",
+ "pro totype",
+ "proto type",
+ "prot otype",
+ "▁f ing",
+ "▁fin g",
+ "▁fi ng",
+ "▁b ag",
+ "▁ba g",
+ "▁ bag",
+ "▁Par ty",
+ "▁Part y",
+ "st ack",
+ "sta ck",
+ "▁econom ic",
+ "▁G al",
+ "▁Ga l",
+ "id ents",
+ "ident s",
+ "iden ts",
+ "▁J un",
+ "▁Ju n",
+ "▁sh owed",
+ "▁show ed",
+ "os h",
+ "o sh",
+ "▁B ay",
+ "▁Ba y",
+ "▁ Bay",
+ "ma il",
+ "m ail",
+ "▁S O",
+ "▁ SO",
+ "▁\" <",
+ "graph ics",
+ "▁f u",
+ "▁ fu",
+ "cl ick",
+ "cli ck",
+ "c lick",
+ "▁b attle",
+ "▁batt le",
+ "▁bat tle",
+ "{ {",
+ "▁E vent",
+ "▁Even t",
+ "▁Ev ent",
+ "▁Eve nt",
+ "▁ Event",
+ "ri or",
+ "rio r",
+ "r ior",
+ "ch aft",
+ "cha ft",
+ "▁f avorite",
+ "▁favor ite",
+ "us ive",
+ "sup port",
+ "supp ort",
+ "s upport",
+ "b m",
+ "K ind",
+ "▁saf ety",
+ "▁safe ty",
+ "▁E nt",
+ "▁En t",
+ "▁ Ent",
+ "cu p",
+ "c up",
+ "▁Austral ia",
+ "▁dest roy",
+ "▁destro y",
+ "▁ destroy",
+ "▁organ ization",
+ "▁organiz ation",
+ "id en",
+ "ide n",
+ "i den",
+ "######## ########",
+ "de c",
+ "d ec",
+ "▁z a",
+ "▁ za",
+ "▁s even",
+ "▁se ven",
+ "▁ seven",
+ "ar ely",
+ "are ly",
+ "arel y",
+ "▁f lag",
+ "▁fl ag",
+ "▁ flag",
+ "Di r",
+ "D ir",
+ "▁C arl",
+ "▁Car l",
+ "▁Ca rl",
+ "▁do ctor",
+ "▁doc tor",
+ "▁var iety",
+ "▁vari ety",
+ "▁L in",
+ "▁Li n",
+ "▁ Lin",
+ "▁t om",
+ "▁to m",
+ "▁ tom",
+ "^{ (",
+ "^ {(",
+ "B o",
+ "an tes",
+ "ant es",
+ "ante s",
+ "▁m ine",
+ "▁min e",
+ "▁mi ne",
+ "▁ mine",
+ "▁M it",
+ "▁Mi t",
+ "▁de scribe",
+ "▁desc ribe",
+ "▁describ e",
+ "Ar gs",
+ "Arg s",
+ "L S",
+ "AP I",
+ "A PI",
+ "▁L uc",
+ "▁Lu c",
+ "▁ Luc",
+ "ph one",
+ "▁sc ience",
+ "▁ science",
+ "▁O per",
+ "▁Op er",
+ "▁ Oper",
+ "Ne xt",
+ "N ext",
+ "▁invest ig",
+ "▁demon str",
+ "▁G overn",
+ "▁Go vern",
+ "▁object s",
+ "▁ objects",
+ "▁Lou is",
+ "▁Lo uis",
+ "▁Return s",
+ "▁ Returns",
+ "▁h an",
+ "▁ha n",
+ "▁ han",
+ "na m",
+ "n am",
+ "▁com me",
+ "▁comm e",
+ "▁pres ence",
+ "▁p el",
+ "▁pe l",
+ "▁ pel",
+ "▁det ect",
+ "▁ detect",
+ ") =",
+ "▁Ch inese",
+ "▁r ich",
+ "▁ri ch",
+ "▁ric h",
+ "▁ rich",
+ "▁class es",
+ "▁classe s",
+ "▁clas ses",
+ "▁ classes",
+ "▁exp and",
+ "▁ expand",
+ "▁D om",
+ "▁Do m",
+ "▁ Dom",
+ "▁D ec",
+ "▁De c",
+ "▁ Dec",
+ "s n",
+ "pe ed",
+ "p eed",
+ "▁J im",
+ "▁Ji m",
+ "sh ould",
+ "▁Sm ith",
+ "▁p ages",
+ "▁page s",
+ "▁pa ges",
+ "▁pag es",
+ "▁ pages",
+ "▁Je an",
+ "ri cs",
+ "ric s",
+ "r ics",
+ "▁S und",
+ "▁Su nd",
+ "▁Sun d",
+ "ad s",
+ "a ds",
+ "▁The ir",
+ "un icip",
+ "uni cip",
+ "unic ip",
+ "в у",
+ "▁down load",
+ "▁ download",
+ "▁st ress",
+ "▁str ess",
+ "▁stre ss",
+ "▁P et",
+ "▁Pe t",
+ "▁ Pet",
+ "me nu",
+ "men u",
+ "m enu",
+ "re me",
+ "rem e",
+ "r eme",
+ "▁com pared",
+ "▁comp ared",
+ "▁compar ed",
+ "▁compare d",
+ "St e",
+ "S te",
+ "IN D",
+ "I ND",
+ "cont ainer",
+ "▁Ind ian",
+ "▁India n",
+ "or en",
+ "ore n",
+ "o ren",
+ "▁s es",
+ "▁se s",
+ "▁ ses",
+ "▁W he",
+ "▁Wh e",
+ "▁ Whe",
+ "▁r oku",
+ "▁ro ku",
+ "▁estab lished",
+ "▁establish ed",
+ "▁gener ally",
+ "▁general ly",
+ "▁f le",
+ "▁fl e",
+ "__ (",
+ "_ _(",
+ "=\" +",
+ "= \"+",
+ "V ar",
+ "▁M ake",
+ "▁Ma ke",
+ "▁Mak e",
+ "▁ Make",
+ "▁rem oved",
+ "▁remove d",
+ "▁ removed",
+ "z z",
+ "ü n",
+ "▁m ix",
+ "▁mi x",
+ "▁ mix",
+ "er k",
+ "iat ion",
+ "i ation",
+ "ou ter",
+ "out er",
+ "oute r",
+ "o uter",
+ "S K",
+ "▁be comes",
+ "▁bec omes",
+ "▁become s",
+ "▁H all",
+ "▁Ha ll",
+ "▁Hal l",
+ "sc ious",
+ "▁w atched",
+ "▁watch ed",
+ "▁wat ched",
+ "▁g ather",
+ "▁ga ther",
+ "▁ gather",
+ "▁Res ult",
+ "▁ Result",
+ "pro of",
+ "pa y",
+ "p ay",
+ "▁produ ced",
+ "▁produce d",
+ "▁prod uced",
+ "▁| =",
+ "▁b order",
+ "▁bord er",
+ "▁bor der",
+ "▁ border",
+ "▁d in",
+ "▁di n",
+ "▁s cript",
+ "▁sc ript",
+ "▁scr ipt",
+ "▁ script",
+ "▁a ctions",
+ "▁act ions",
+ "▁action s",
+ "▁ actions",
+ "▁m as",
+ "▁ma s",
+ "▁ mas",
+ "щ а",
+ "oot h",
+ "oo th",
+ "o oth",
+ "▁Te chn",
+ "▁Tech n",
+ "Js on",
+ "J son",
+ "▁f illed",
+ "▁fil led",
+ "▁fill ed",
+ "▁ filled",
+ "де н",
+ "д ен",
+ "und le",
+ "ст у",
+ "с ту",
+ "To ol",
+ "Too l",
+ "T ool",
+ "▁k ing",
+ "▁ki ng",
+ "▁kin g",
+ "▁ king",
+ "▁v en",
+ "▁ve n",
+ "▁ ven",
+ "st ra",
+ "str a",
+ "s tra",
+ "▁pre dict",
+ "▁pred ict",
+ "▁ predict",
+ "▁l ui",
+ "▁lu i",
+ "▁WAR RAN",
+ "▁F un",
+ "▁Fu n",
+ "▁ Fun",
+ "Sc ript",
+ "S cript",
+ "▁power ful",
+ "▁l ose",
+ "▁lo se",
+ "▁los e",
+ "at ically",
+ "atic ally",
+ "▁d aily",
+ "▁da ily",
+ "▁dai ly",
+ "▁r ing",
+ "▁ri ng",
+ "▁ ring",
+ "▁ar rived",
+ "▁arriv ed",
+ "▁arr ived",
+ "▁arrive d",
+ "St ack",
+ "sc ope",
+ "s cope",
+ "▁B ack",
+ "▁Ba ck",
+ "▁ Back",
+ "el ij",
+ "eli j",
+ "e lij",
+ "▁z e",
+ "▁ ze",
+ "ke ys",
+ "key s",
+ "{ \"",
+ "VI D",
+ "V ID",
+ "▁l icense",
+ "▁lic ense",
+ "▁ license",
+ "wh at",
+ "w hat",
+ "▁pro ced",
+ "▁proc ed",
+ "ra nt",
+ "ran t",
+ "r ant",
+ "est ival",
+ "ag ram",
+ "agr am",
+ "agra m",
+ "a gram",
+ "▁L O",
+ "▁ LO",
+ "▁Hen ry",
+ "▁fl ags",
+ "▁flag s",
+ "▁ flags",
+ "Do wn",
+ "D own",
+ "scri ption",
+ "script ion",
+ "s cription",
+ "▁famil ies",
+ "▁familie s",
+ "is se",
+ "iss e",
+ "bo ur",
+ "b our",
+ "▁B ur",
+ "▁Bu r",
+ "— \"",
+ "▁b rief",
+ "▁br ief",
+ "▁ brief",
+ "▁cre ating",
+ "▁creat ing",
+ "▁cl ients",
+ "▁client s",
+ "ran gle",
+ "r angle",
+ "▁amaz ing",
+ "▁s ind",
+ "▁si nd",
+ "▁sin d",
+ "▁cover ed",
+ "▁cov ered",
+ "▁ covered",
+ "We ll",
+ "W ell",
+ "ст е",
+ "с те",
+ "то р",
+ "т ор",
+ "▁B as",
+ "▁Ba s",
+ "▁ Bas",
+ "to tal",
+ "tot al",
+ "t otal",
+ "▁I nit",
+ "▁In it",
+ "▁ Init",
+ "▁s and",
+ "▁sa nd",
+ "▁san d",
+ "Un it",
+ "U nit",
+ "▁mur der",
+ "▁b right",
+ "▁br ight",
+ "▁brig ht",
+ "▁t rav",
+ "▁tr av",
+ "▁tra v",
+ "ic ans",
+ "ica ns",
+ "ican s",
+ "▁att ribute",
+ "▁attribut e",
+ "▁ attribute",
+ "f c",
+ "▁pl aced",
+ "▁place d",
+ "▁plac ed",
+ "ES T",
+ "E ST",
+ "Var i",
+ "V ari",
+ "▁c os",
+ "▁co s",
+ "▁ cos",
+ "▁at tract",
+ "▁att ract",
+ "▁attr act",
+ "▁attra ct",
+ "an el",
+ "ane l",
+ "a nel",
+ "}) .",
+ "} ).",
+ "by tes",
+ "byte s",
+ "▁p arse",
+ "▁par se",
+ "▁ parse",
+ "▁be long",
+ "▁bel ong",
+ "B N",
+ "▁S ol",
+ "▁So l",
+ "P o",
+ "` ,",
+ "▁c alling",
+ "▁call ing",
+ "▁cal ling",
+ "▁? >",
+ "▁ ?>",
+ "▁it er",
+ "▁i ter",
+ "▁ iter",
+ "▁u rl",
+ "▁ur l",
+ "▁ url",
+ "▁ev ening",
+ "▁even ing",
+ "re ek",
+ "ree k",
+ "▁hon est",
+ "▁direct or",
+ "▁dire ctor",
+ "▁dir ector",
+ "R C",
+ "▁s olid",
+ "▁sol id",
+ "▁ solid",
+ "▁ph il",
+ "ie ne",
+ "ien e",
+ "i ene",
+ "FA ULT",
+ "co pe",
+ "cop e",
+ "c ope",
+ "▁Hist ory",
+ "▁Histor y",
+ "▁Hi story",
+ "▁ History",
+ "▁Te am",
+ "▁ Team",
+ "ree dom",
+ "reed om",
+ "▁r u",
+ "▁ ru",
+ "U B",
+ "▁w orse",
+ "▁wor se",
+ "im o",
+ "i mo",
+ "Ma t",
+ "M at",
+ "▁M ex",
+ "▁Me x",
+ "ac tor",
+ "act or",
+ "a ctor",
+ "▁v or",
+ "▁vo r",
+ "▁ vor",
+ "ть ся",
+ "▁exper iment",
+ "▁experi ment",
+ "▁P lay",
+ "▁Pl ay",
+ "▁ Play",
+ "▁An other",
+ "▁happ ens",
+ "▁happen s",
+ "ua n",
+ "u an",
+ "▁pat ients",
+ "▁patient s",
+ "▁re nd",
+ "▁r end",
+ "▁ren d",
+ "▁ rend",
+ "▁M o",
+ "▁ Mo",
+ "▁T ex",
+ "▁Te x",
+ "▁ Tex",
+ "▁w ed",
+ "▁we d",
+ "▁ wed",
+ "t n",
+ "in sert",
+ "ins ert",
+ "▁п а",
+ "▁ па",
+ "▁an ti",
+ "▁ant i",
+ "▁ anti",
+ "Mat ch",
+ "M atch",
+ "ampions hip",
+ "ampion ship",
+ "▁for ces",
+ "▁force s",
+ "▁H ot",
+ "▁Ho t",
+ "▁ Hot",
+ "▁ph ase",
+ "▁ phase",
+ "▁t emplate",
+ "▁templ ate",
+ "▁temp late",
+ "▁ template",
+ "st op",
+ "sto p",
+ "s top",
+ "ic ated",
+ "ica ted",
+ "icate d",
+ "▁man aged",
+ "▁manage d",
+ "▁ managed",
+ "wa it",
+ "w ait",
+ "▁* (",
+ "▁ *(",
+ "G B",
+ "▁app oint",
+ "▁ap point",
+ "▁ appoint",
+ "ł a",
+ "▁s tick",
+ "▁st ick",
+ "▁ stick",
+ "▁F OR",
+ "▁FO R",
+ "▁ FOR",
+ "▁V is",
+ "▁Vi s",
+ "▁ Vis",
+ "to r",
+ "t or",
+ "▁p ř",
+ "qu est",
+ "que st",
+ "ques t",
+ "q uest",
+ "us es",
+ "use s",
+ "u ses",
+ "\"); \r",
+ "\") ;\r",
+ "\" );\r",
+ "▁sudden ly",
+ "▁sud denly",
+ "é c",
+ "N D",
+ "ur op",
+ "uro p",
+ "u rop",
+ "ре д",
+ "▁ins urance",
+ "ac cess",
+ "acc ess",
+ "a ccess",
+ "un finished",
+ "▁t amb",
+ "▁ta mb",
+ "▁tam b",
+ "▁s ac",
+ "▁sa c",
+ "▁C ourt",
+ "▁Co urt",
+ "▁Cour t",
+ "▁Cou rt",
+ "▁miss ing",
+ "▁mis sing",
+ "▁ missing",
+ "▁W here",
+ "▁Wh ere",
+ "▁Whe re",
+ "▁ Where",
+ "▁S um",
+ "▁Su m",
+ "▁ Sum",
+ "}^ {\\",
+ "}^{ \\",
+ "} ^{\\",
+ "▁s ua",
+ "▁su a",
+ "_ ,",
+ "▁th ick",
+ "▁Tr ump",
+ "▁Tru mp",
+ "▁oper ations",
+ "▁operation s",
+ "▁ operations",
+ "F S",
+ "▁de ux",
+ "d z",
+ "Temp late",
+ "T emplate",
+ "▁\" /",
+ "▁o dd",
+ "▁od d",
+ "▁ odd",
+ "▁re ality",
+ "▁real ity",
+ "▁te ams",
+ "▁team s",
+ "▁tea ms",
+ "▁c er",
+ "▁ce r",
+ "▁ cer",
+ "om a",
+ "o ma",
+ "▁ și",
+ "▁cl oud",
+ "▁clo ud",
+ "▁ cloud",
+ "▁Dep artment",
+ "N e",
+ "▁requ ires",
+ "▁require s",
+ "it ems",
+ "ite ms",
+ "item s",
+ "▁I II",
+ "▁II I",
+ "▁ III",
+ "right arrow",
+ ")- >",
+ ") ->",
+ "▁w riter",
+ "▁wr iter",
+ "▁writ er",
+ "▁write r",
+ "▁ writer",
+ "re place",
+ "rep lace",
+ "▁t hr",
+ "▁th r",
+ "je n",
+ "j en",
+ "▁o t",
+ "▁ ot",
+ "▁occ up",
+ "▁oc cup",
+ "▁ occup",
+ "▁event ually",
+ "▁M ath",
+ "▁Mat h",
+ "▁Ma th",
+ "▁ Math",
+ "▁con serv",
+ "▁cons erv",
+ "▁conse rv",
+ "am er",
+ "ame r",
+ "a mer",
+ "▁F ort",
+ "▁For t",
+ "▁Fo rt",
+ "▁d ry",
+ "▁dr y",
+ "▁sex ual",
+ "▁co sts",
+ "▁cost s",
+ "▁cos ts",
+ "▁for ms",
+ "▁form s",
+ "▁ forms",
+ "▁V ict",
+ "▁Vi ct",
+ "▁Vic t",
+ "PA R",
+ "P AR",
+ "frame work",
+ "▁д и",
+ "▁ ди",
+ "Oper ation",
+ "з на",
+ "wh ich",
+ "▁t ight",
+ "▁ti ght",
+ "In valid",
+ "▁part ner",
+ "▁п ред",
+ "▁пре д",
+ "▁th ank",
+ "▁than k",
+ "▁gu ard",
+ "▁ guard",
+ "he m",
+ "h em",
+ "Bo dy",
+ "B ody",
+ "▁e mot",
+ "▁em ot",
+ "I X",
+ "fa st",
+ "fas t",
+ "f ast",
+ "щ о",
+ "ñ o",
+ "ni ght",
+ "n ight",
+ "▁S ci",
+ "▁Sc i",
+ "ни ка",
+ "ник а",
+ "▁T O",
+ "▁ TO",
+ "▁individ uals",
+ "▁individual s",
+ "сс и",
+ "с си",
+ "}) ,",
+ "} ),",
+ "F alse",
+ "(\" %",
+ "( \"%",
+ "▁op tim",
+ "▁opt im",
+ "▁ optim",
+ "▁- ->",
+ "▁-- >",
+ "▁ -->",
+ "▁f actor",
+ "▁fact or",
+ "▁fac tor",
+ "▁fa ctor",
+ "▁ factor",
+ "▁sm aller",
+ "▁small er",
+ "▁con tain",
+ "▁cont ain",
+ "sp ect",
+ "spec t",
+ "spe ct",
+ "s pect",
+ "Eng ine",
+ "▁ann ounced",
+ "▁announ ced",
+ "▁announce d",
+ "▁Dem ocr",
+ "▁r ob",
+ "▁ro b",
+ "▁ rob",
+ "▁f lat",
+ "▁fl at",
+ "▁ flat",
+ "os oph",
+ "oso ph",
+ "Se arch",
+ "S earch",
+ "ah l",
+ "a hl",
+ "▁Ex ception",
+ "▁Except ion",
+ "▁ Exception",
+ "▁O l",
+ "equ als",
+ "eq uals",
+ "equal s",
+ "▁un ter",
+ "▁unt er",
+ "▁ unter",
+ "sh ape",
+ "sha pe",
+ "N S",
+ "Ob j",
+ "▁spec ies",
+ "▁spe cies",
+ "we ight",
+ "wei ght",
+ "w eight",
+ "yo u",
+ "y ou",
+ "▁e ste",
+ "▁est e",
+ "▁es te",
+ "▁ este",
+ "▁V iew",
+ "▁Vi ew",
+ "▁ View",
+ "▁m ission",
+ "▁miss ion",
+ "▁ mission",
+ "▁j ournal",
+ "▁jour nal",
+ "▁ journal",
+ "Value s",
+ "Val ues",
+ "▁ein em",
+ "▁eine m",
+ "is mo",
+ "ism o",
+ "▁project s",
+ "▁ projects",
+ "▁D as",
+ "▁Da s",
+ "ri ble",
+ "rib le",
+ "r ible",
+ "▁s erve",
+ "▁ser ve",
+ "▁serv e",
+ "▁ serve",
+ "▁op ening",
+ "▁open ing",
+ "▁h ur",
+ "▁program s",
+ "▁U SA",
+ "▁US A",
+ "▁ USA",
+ "il iar",
+ "ili ar",
+ "ilia r",
+ "id os",
+ "ido s",
+ "B r",
+ "est amp",
+ "esta mp",
+ "▁t ools",
+ "▁to ols",
+ "▁too ls",
+ "▁tool s",
+ "▁ tools",
+ "an ner",
+ "ann er",
+ "anne r",
+ "R T",
+ "▁St art",
+ "▁Star t",
+ "▁Sta rt",
+ "▁ Start",
+ "▁b ath",
+ "▁bat h",
+ "▁ba th",
+ "▁coff ee",
+ "or ter",
+ "ort er",
+ "orte r",
+ "in ternal",
+ "inter nal",
+ "intern al",
+ "file s",
+ "fil es",
+ "fi les",
+ "f iles",
+ "IN VAL",
+ "ak o",
+ "a ko",
+ "d t",
+ "▁Se cond",
+ "▁Sec ond",
+ "▁ Second",
+ "▁al loc",
+ "▁all oc",
+ "▁ alloc",
+ "▁en ded",
+ "▁end ed",
+ "▁ende d",
+ "▁ ended",
+ "ac ional",
+ "aci onal",
+ "acion al",
+ "acio nal",
+ "▁man ager",
+ "▁manage r",
+ "▁ manager",
+ "▁S un",
+ "▁Su n",
+ "▁ Sun",
+ "ag g",
+ "a gg",
+ "▁le ader",
+ "▁lead er",
+ "ol ved",
+ "olve d",
+ "olv ed",
+ "▁ч то",
+ "▁trad itional",
+ "▁tradition al",
+ "sh ot",
+ "s hot",
+ "ru p",
+ "r up",
+ "C F",
+ "▁E ach",
+ "▁ Each",
+ "w r",
+ "▁S om",
+ "▁So m",
+ "▁ Som",
+ "▁material s",
+ "▁mater ials",
+ "▁m sg",
+ "▁ms g",
+ "▁ msg",
+ "▁s yn",
+ "▁sy n",
+ "▁ syn",
+ "▁produ ce",
+ "▁prod uce",
+ "▁st orage",
+ "▁stor age",
+ "▁sto rage",
+ "▁ storage",
+ "sub section",
+ "▁S ie",
+ "▁Si e",
+ "▁I P",
+ "▁ IP",
+ "CE SS",
+ "▁w a",
+ "▁ wa",
+ "Re cord",
+ "Rec ord",
+ "▁mark eting",
+ "▁market ing",
+ "pl et",
+ "ple t",
+ "p let",
+ "D ialog",
+ "▁mention ed",
+ "▁ment ioned",
+ "▁N a",
+ "▁ Na",
+ "▁Un ion",
+ "▁ Union",
+ "▁A PI",
+ "▁AP I",
+ "▁ API",
+ "▁neg ative",
+ "▁ negative",
+ "tx t",
+ "t xt",
+ "▁eas ier",
+ "le gal",
+ "leg al",
+ "De p",
+ "D ep",
+ "▁no vel",
+ "▁nov el",
+ "▁nove l",
+ "eu r",
+ "e ur",
+ "ac ió",
+ "aci ó",
+ "a ció",
+ "▁B ud",
+ "▁Bu d",
+ "▁c arry",
+ "▁car ry",
+ "sch aft",
+ "s chaft",
+ "▁br oken",
+ "▁bro ken",
+ "▁broke n",
+ "▁t rees",
+ "▁tr ees",
+ "▁tre es",
+ "▁tree s",
+ ">( );",
+ ">() ;",
+ "> ();",
+ "▁e mb",
+ "▁em b",
+ "▁ emb",
+ "ie der",
+ "ied er",
+ "i eder",
+ "▁r oute",
+ "▁ro ute",
+ "▁rout e",
+ "▁rou te",
+ "▁ route",
+ "ik el",
+ "ike l",
+ "i kel",
+ "▁l isten",
+ "▁li sten",
+ "▁list en",
+ "▁ listen",
+ "ash ion",
+ "ashi on",
+ "▁M rs",
+ "▁Mr s",
+ "▁equip ment",
+ "ag ger",
+ "agg er",
+ "▁T hus",
+ "▁Th us",
+ "▁mat rix",
+ "▁ matrix",
+ "al la",
+ "all a",
+ "a lla",
+ "▁T our",
+ "▁To ur",
+ "▁con versation",
+ "▁convers ation",
+ "Mo n",
+ "M on",
+ "our nal",
+ "▁min ute",
+ "▁minut e",
+ "▁ minute",
+ "A m",
+ "Ap i",
+ "A pi",
+ "▁for get",
+ "▁forg et",
+ "M e",
+ "lev ant",
+ "te mp",
+ "tem p",
+ "t emp",
+ "▁t elling",
+ "▁tell ing",
+ "▁tel ling",
+ "mo ve",
+ "mov e",
+ "m ove",
+ "▁in dependent",
+ "▁independ ent",
+ "to String",
+ "ed it",
+ "edi t",
+ "e dit",
+ "▁J ac",
+ "▁Ja c",
+ "az z",
+ "a zz",
+ "re act",
+ "rea ct",
+ "▁c in",
+ "▁ci n",
+ "▁ cin",
+ "▁P rov",
+ "▁Pro v",
+ "▁Pr ov",
+ "▁ Prov",
+ "is ted",
+ "ist ed",
+ "iste d",
+ "i sted",
+ "▁h ash",
+ "▁has h",
+ "▁ha sh",
+ "▁ hash",
+ "on na",
+ "ik i",
+ "i ki",
+ "▁gener ated",
+ "▁generate d",
+ "▁gene rated",
+ "▁ generated",
+ "Re nder",
+ "Rend er",
+ "R ender",
+ "▁psy ch",
+ "▁ps ych",
+ "na v",
+ "n av",
+ "▁en tr",
+ "▁ent r",
+ "▁ entr",
+ "п ра",
+ "r x",
+ "AT H",
+ "A TH",
+ "▁ass ume",
+ "▁assum e",
+ "Tr ee",
+ "T ree",
+ "semb ly",
+ "sembl y",
+ "▁M att",
+ "▁Mat t",
+ "▁Ma tt",
+ "ca ption",
+ "c aption",
+ "▁s olutions",
+ "▁solution s",
+ "▁fa ith",
+ "▁fait h",
+ "▁dig ital",
+ "▁digit al",
+ "▁ex cell",
+ "▁exc ell",
+ "▁V ersion",
+ "▁Vers ion",
+ "▁ Version",
+ "De bug",
+ "D ebug",
+ "▁ж и",
+ "▁ жи",
+ "▁car ried",
+ "re set",
+ "res et",
+ "▁slow ly",
+ "an cing",
+ "anc ing",
+ "▁own er",
+ "▁ owner",
+ "▁T er",
+ "▁Te r",
+ "▁D id",
+ "▁Di d",
+ "▁ Did",
+ "▁g est",
+ "▁ge st",
+ "▁ges t",
+ "▁ gest",
+ "▁é té",
+ "▁ét é",
+ "▁ été",
+ "▁pro of",
+ "▁ proof",
+ "F ont",
+ "▁n ob",
+ "▁no b",
+ "▁ nob",
+ "C o",
+ "▁G NU",
+ "▁l iber",
+ "▁li ber",
+ "▁lib er",
+ "it ness",
+ "▁h ij",
+ "▁hi j",
+ "▁v ert",
+ "▁ver t",
+ "▁ve rt",
+ "▁ vert",
+ "ш а",
+ "FL AG",
+ "ME NT",
+ "M ENT",
+ "▁S on",
+ "▁So n",
+ "Mu lt",
+ "M ult",
+ "▁d istrict",
+ "▁di strict",
+ "▁dist rict",
+ "conne ct",
+ "conn ect",
+ "ject ion",
+ "je ction",
+ "j ection",
+ "ly mp",
+ "▁real ized",
+ "▁realize d",
+ "▁realiz ed",
+ "mo s",
+ "m os",
+ "y e",
+ "▁re nder",
+ "▁r ender",
+ "▁ren der",
+ "▁rend er",
+ "▁ render",
+ "ri o",
+ "r io",
+ "▁inter pret",
+ "▁ interpret",
+ "▁slight ly",
+ "fi x",
+ "f ix",
+ "▁stud ies",
+ "▁r id",
+ "▁ri d",
+ "▁ rid",
+ "at re",
+ "atr e",
+ "a tre",
+ "▁benef its",
+ "▁benefit s",
+ "▁F ace",
+ "▁Fa ce",
+ "▁Fac e",
+ "▁ Face",
+ "iv ery",
+ "ive ry",
+ "iver y",
+ "i very",
+ "ри я",
+ "doc ument",
+ "d ocument",
+ "▁as king",
+ "▁ask ing",
+ "La st",
+ "L ast",
+ "ar ante",
+ "ara nte",
+ "aran te",
+ "▁Mart in",
+ "▁E ll",
+ "▁El l",
+ "▁v ector",
+ "▁ve ctor",
+ "▁vec tor",
+ "▁ vector",
+ "▁for ced",
+ "▁force d",
+ "▁ forced",
+ "о ло",
+ "P H",
+ "W R",
+ "▁K l",
+ "▁s ky",
+ "▁sk y",
+ "▁ sky",
+ "▁str ategy",
+ "▁strateg y",
+ "▁strat egy",
+ "oc ked",
+ "ock ed",
+ "▁ne ck",
+ "ś ci",
+ "O UT",
+ ")) ,",
+ ") ),",
+ "C ustom",
+ "▁w ie",
+ "▁ wie",
+ "▁s weet",
+ "▁swe et",
+ "▁t emp",
+ "▁te mp",
+ "▁tem p",
+ "▁ temp",
+ "▁fore ign",
+ "▁h all",
+ "▁ha ll",
+ "▁hal l",
+ "▁ hall",
+ "as tr",
+ "ast r",
+ "a str",
+ "As s",
+ "A ss",
+ "MO DE",
+ "MOD E",
+ "▁max imum",
+ "▁maxim um",
+ "an nels",
+ "ann els",
+ "annel s",
+ "anne ls",
+ "▁t ip",
+ "▁ti p",
+ "▁ tip",
+ "▁second s",
+ "▁sec onds",
+ "▁ seconds",
+ "▁st ack",
+ "▁sta ck",
+ "▁ stack",
+ "ig a",
+ "i ga",
+ "▁r aise",
+ "▁rais e",
+ "▁ra ise",
+ "▁ raise",
+ "en able",
+ "ena ble",
+ "oi r",
+ "o ir",
+ "▁s oul",
+ "▁so ul",
+ "▁sou l",
+ "K e",
+ ")$ .",
+ ") $.",
+ "▁T im",
+ "▁Ti m",
+ "▁ Tim",
+ "AL SE",
+ "is er",
+ "ise r",
+ "i ser",
+ "cont in",
+ "be l",
+ "b el",
+ "▁m ad",
+ "▁ma d",
+ "▁ mad",
+ "lic hen",
+ "li chen",
+ "lich en",
+ "liche n",
+ "l ichen",
+ "ab e",
+ "a be",
+ "sa fe",
+ "▁con cent",
+ "▁conc ent",
+ "▁conce nt",
+ "bo und",
+ "b ound",
+ "▁R equ",
+ "▁Re qu",
+ "▁ Requ",
+ "sw itch",
+ "▁st one",
+ "▁sto ne",
+ "▁ stone",
+ "▁trans l",
+ "▁ transl",
+ "▁v ac",
+ "▁va c",
+ "an don",
+ "and on",
+ "ando n",
+ "▁F ore",
+ "▁For e",
+ "▁Fo re",
+ "▁ Fore",
+ "▁s ounds",
+ "▁sound s",
+ "▁P op",
+ "▁Po p",
+ "▁ Pop",
+ "▁H T",
+ "▁ HT",
+ "li a",
+ "l ia",
+ "en ter",
+ "ent er",
+ "ente r",
+ "▁hel ps",
+ "▁help s",
+ "ed y",
+ "e dy",
+ "ст вен",
+ "ств ен",
+ "стве н",
+ "an ted",
+ "ant ed",
+ "ante d",
+ "▁I ts",
+ "▁It s",
+ "▁St ep",
+ "▁Ste p",
+ "▁ Step",
+ "I con",
+ "▁EX PECT",
+ "▁ EXPECT",
+ "ial ized",
+ "ialize d",
+ "Pos t",
+ "Po st",
+ "P ost",
+ "az e",
+ "a ze",
+ "▁Car ol",
+ "▁Ca rol",
+ "▁re q",
+ "▁r eq",
+ "▁ req",
+ "▁crit ical",
+ "▁critic al",
+ "D S",
+ "▁se at",
+ "▁sea t",
+ "ap ed",
+ "ape d",
+ "a ped",
+ "▁up per",
+ "▁upp er",
+ "▁ upper",
+ "▁S y",
+ "▁ Sy",
+ "▁ex plain",
+ "▁expl ain",
+ "▁' ./",
+ "▁'. /",
+ "ut ils",
+ "util s",
+ "uti ls",
+ "poss ible",
+ "▁d ont",
+ "▁do nt",
+ "▁don t",
+ "H ost",
+ "▁appro xim",
+ "▁approx im",
+ "As ync",
+ "A sync",
+ "▁g rab",
+ "▁gr ab",
+ "▁gra b",
+ "▁s ources",
+ "▁source s",
+ "▁sour ces",
+ "▁ sources",
+ "▁M os",
+ "▁Mo s",
+ "▁Germ any",
+ "▁German y",
+ "▁Ger many",
+ "▁r ub",
+ "▁ru b",
+ "▁ rub",
+ "CH AN",
+ "▁r ain",
+ "▁ra in",
+ "▁tr uly",
+ "▁join ed",
+ "▁jo ined",
+ "▁< ?",
+ "▁ ",
+ "▁L o",
+ "▁ Lo",
+ "Des cription",
+ "De scription",
+ "ak t",
+ "a kt",
+ "▁A nn",
+ "▁An n",
+ "▁ Ann",
+ "^ *",
+ "id ae",
+ "ida e",
+ "( :",
+ "t w",
+ "Ma r",
+ "M ar",
+ "pro du",
+ "prod u",
+ "p rodu",
+ "▁sp oke",
+ "▁spo ke",
+ "ю т",
+ "▁walk ing",
+ "▁wal king",
+ "▁nod ded",
+ "Pro ps",
+ "Pr ops",
+ "Prop s",
+ "En abled",
+ "Enable d",
+ "ir k",
+ "FI LE",
+ "FIL E",
+ "F ILE",
+ "equ al",
+ "eq ual",
+ "e qual",
+ "pp ing",
+ "p ping",
+ "ol i",
+ "o li",
+ "E V",
+ "en z",
+ "et ing",
+ "eti ng",
+ "e ting",
+ "▁s ample",
+ "▁sam ple",
+ "▁ sample",
+ "▁art ist",
+ "[ $",
+ "it à",
+ "й о",
+ "pro ps",
+ "pr ops",
+ "prop s",
+ "b u",
+ "е в",
+ "▁respons ible",
+ "M T",
+ "▁caus ed",
+ "▁cause d",
+ "▁ca used",
+ "▁the me",
+ "▁th eme",
+ "▁them e",
+ "▁ theme",
+ "▁W as",
+ "▁Wa s",
+ "▁ Was",
+ "▁B efore",
+ "▁Be fore",
+ "▁ Before",
+ "ac le",
+ "acl e",
+ "a cle",
+ "▁ро ку",
+ "c u",
+ "DE V",
+ "D EV",
+ "▁h ung",
+ "▁hun g",
+ "▁ hung",
+ "text bf",
+ "▁s pin",
+ "▁sp in",
+ "▁ spin",
+ "▁la test",
+ "▁late st",
+ "▁lat est",
+ "▁ latest",
+ "ent ially",
+ "ential ly",
+ "enti ally",
+ "▁Pro gram",
+ "▁Pr ogram",
+ "▁ Program",
+ "Met adata",
+ "Meta data",
+ "pass word",
+ "▁h urt",
+ "▁hur t",
+ "к с",
+ "▁A us",
+ "▁Au s",
+ "se y",
+ "s ey",
+ "al let",
+ "all et",
+ "alle t",
+ "x F",
+ "▁R oad",
+ "▁Ro ad",
+ "ет ся",
+ "е тся",
+ "▁re nt",
+ "▁r ent",
+ "▁ren t",
+ "▁ rent",
+ "ци я",
+ "▁As sert",
+ "▁Ass ert",
+ "▁ Assert",
+ "і ль",
+ "ü ck",
+ "▁s ites",
+ "▁sit es",
+ "▁si tes",
+ "▁site s",
+ "Doc ument",
+ "D ocument",
+ "▁obt ained",
+ "▁obtain ed",
+ "▁c i",
+ "▁ ci",
+ "▁[ \"",
+ "▁ [\"",
+ "▁com pleted",
+ "▁comp leted",
+ "▁complet ed",
+ "▁compl eted",
+ "▁complete d",
+ "as et",
+ "ase t",
+ "a set",
+ "ra id",
+ "rai d",
+ "r aid",
+ "▁s orry",
+ "▁sor ry",
+ "▁f ab",
+ "▁fa b",
+ "▁ fab",
+ "▁sch ools",
+ "▁school s",
+ "хо ди",
+ "ход и",
+ "▁s cr",
+ "▁sc r",
+ "▁ scr",
+ "▁in cor",
+ "▁inc or",
+ "▁' /",
+ "▁s pr",
+ "▁sp r",
+ "▁ spr",
+ "▁T ext",
+ "▁Te xt",
+ "▁Tex t",
+ "▁ Text",
+ "▁com mercial",
+ "▁commer cial",
+ "in gly",
+ "ing ly",
+ "▁opin ion",
+ "▁S tar",
+ "▁St ar",
+ "▁Sta r",
+ "▁ Star",
+ "Si gn",
+ "Sig n",
+ "S ign",
+ "▁j avax",
+ "▁java x",
+ "▁ javax",
+ "w i",
+ "la t",
+ "l at",
+ "▁K ey",
+ "▁Ke y",
+ "▁ Key",
+ "var phi",
+ "д ы",
+ "▁conne cted",
+ "▁connect ed",
+ "▁ connected",
+ "▁ad just",
+ "▁adj ust",
+ "▁ adjust",
+ "▁A z",
+ "▁ Az",
+ "▁pl anning",
+ "▁plan ning",
+ "-- -",
+ "- --",
+ "In teger",
+ "au f",
+ "a uf",
+ "ex pected",
+ "expect ed",
+ "e xpected",
+ "▁f ant",
+ "▁fa nt",
+ "▁fan t",
+ "▁t ou",
+ "▁to u",
+ "Par ent",
+ "P arent",
+ "▁L at",
+ "▁La t",
+ "▁ Lat",
+ "▁thought s",
+ "▁though ts",
+ "▁J ud",
+ "▁Ju d",
+ "Param eters",
+ "Parameter s",
+ "G r",
+ "ро м",
+ "I A",
+ "▁B ob",
+ "▁Bo b",
+ "lic t",
+ "li ct",
+ "l ict",
+ "la n",
+ "l an",
+ "om ic",
+ "omi c",
+ "o mic",
+ "▁a part",
+ "▁ap art",
+ "▁t rou",
+ "▁tr ou",
+ "▁tro u",
+ "▁app reci",
+ "▁Christ mas",
+ "ir q",
+ "i rq",
+ "th on",
+ "t hon",
+ "▁Er ror",
+ "▁Err or",
+ "▁ Error",
+ "▁s core",
+ "▁sc ore",
+ "▁ score",
+ "ro me",
+ "rom e",
+ "r ome",
+ "▁ne ighbor",
+ "▁neigh bor",
+ "▁neighb or",
+ "▁M ur",
+ "▁Mu r",
+ "ad min",
+ "▁Fil m",
+ "▁Fi lm",
+ "Re ct",
+ "Rec t",
+ "R ect",
+ "▁config uration",
+ "▁ configuration",
+ "▁c s",
+ "▁ cs",
+ "gu n",
+ "g un",
+ "ch annel",
+ "chan nel",
+ "▁Re port",
+ "▁Rep ort",
+ "▁ Report",
+ "▁str ateg",
+ "▁strat eg",
+ "▁work ers",
+ "▁wor kers",
+ "▁worker s",
+ "▁ workers",
+ "field s",
+ "Sch ema",
+ "Sche ma",
+ "S chema",
+ "ap pa",
+ "app a",
+ "ol ic",
+ "oli c",
+ "o lic",
+ "E O",
+ "▁Ch arl",
+ "▁Char l",
+ "▁Cha rl",
+ "▁C up",
+ "▁Cu p",
+ "pn g",
+ "p ng",
+ "▁H ill",
+ "▁Hi ll",
+ "▁Hil l",
+ "ow e",
+ "o we",
+ "▁most ly",
+ "” .",
+ "▁fin ish",
+ "▁ finish",
+ "▁С о",
+ "▁st ars",
+ "▁star s",
+ "▁sta rs",
+ "pl ayer",
+ "play er",
+ "p layer",
+ "▁in ner",
+ "▁inn er",
+ "▁ inner",
+ "com ponent",
+ "ti m",
+ "t im",
+ "I E",
+ "▁t her",
+ "▁the r",
+ "▁th er",
+ "▁ ther",
+ "▁s mart",
+ "▁sm art",
+ "▁ smart",
+ "▁s ad",
+ "▁sa d",
+ "▁Coun cil",
+ "ar ea",
+ "are a",
+ "a rea",
+ "la y",
+ "l ay",
+ "▁б а",
+ "▁ ба",
+ "▁gr adu",
+ "▁grad u",
+ "▁gra du",
+ "▁c hem",
+ "▁ch em",
+ "▁che m",
+ "▁ chem",
+ "▁h o",
+ "▁ ho",
+ "Se lect",
+ "S elect",
+ "▁in str",
+ "▁inst r",
+ "▁ins tr",
+ "▁ instr",
+ "▁k l",
+ "▁ kl",
+ "if ications",
+ "ific ations",
+ "ification s",
+ "Lo ng",
+ "L ong",
+ "▁s obre",
+ "▁so bre",
+ "▁sob re",
+ "▁O ld",
+ "▁Ol d",
+ "▁ Old",
+ "we st",
+ "w est",
+ "}, \\",
+ "} ,\\",
+ "in gu",
+ "ing u",
+ "▁sp ring",
+ "▁spr ing",
+ "▁ spring",
+ "▁n ur",
+ "▁nu r",
+ "ex ample",
+ "Wh en",
+ "Whe n",
+ "W hen",
+ "▁adv ice",
+ "▁u lt",
+ "▁ul t",
+ "▁ ult",
+ "en nis",
+ "enn is",
+ "▁L ove",
+ "▁Lo ve",
+ "▁Lov e",
+ "▁ Love",
+ "▁\" \"",
+ "▁ \"\"",
+ "▁incre ased",
+ "▁increase d",
+ "▁f inding",
+ "▁fin ding",
+ "▁find ing",
+ "ir ty",
+ "irt y",
+ "ist rict",
+ "istr ict",
+ "i strict",
+ "▁l ayer",
+ "▁la yer",
+ "▁lay er",
+ "▁ layer",
+ "temp late",
+ "t emplate",
+ "F irst",
+ "ны м",
+ "igr ation",
+ "ren cy",
+ "r ency",
+ "ow ie",
+ "owi e",
+ "o wie",
+ "▁n p",
+ "▁ np",
+ "▁s election",
+ "▁se lection",
+ "▁select ion",
+ "▁sel ection",
+ "▁sele ction",
+ "▁ selection",
+ "▁N ach",
+ "▁Na ch",
+ "▁P RO",
+ "▁PR O",
+ "▁ PRO",
+ "▁p olic",
+ "▁pol ic",
+ "▁po lic",
+ "▁data base",
+ "▁dat abase",
+ "▁ database",
+ "▁by te",
+ "▁ byte",
+ "▁prov iding",
+ "ma c",
+ "m ac",
+ "▁me tal",
+ "▁met al",
+ "▁meta l",
+ "mod ules",
+ "module s",
+ "▁Ge org",
+ "▁S a",
+ "▁ Sa",
+ "▁est ablish",
+ "▁estab lish",
+ ".. .\"",
+ "... \"",
+ "i u",
+ "ki n",
+ "k in",
+ "▁e th",
+ "▁et h",
+ "▁ eth",
+ "▁S and",
+ "▁San d",
+ "▁Sa nd",
+ "▁Ch apter",
+ "▁Chap ter",
+ "▁g al",
+ "▁ga l",
+ "▁ gal",
+ "▁i ce",
+ "▁ic e",
+ "▁ ice",
+ "Re d",
+ "R ed",
+ "▁d al",
+ "▁da l",
+ "▁ dal",
+ "▁pr incipal",
+ "▁princip al",
+ "Ms g",
+ "M sg",
+ "▁rem ains",
+ "▁remain s",
+ "н г",
+ "T itle",
+ "Re l",
+ "R el",
+ "Dis play",
+ "No n",
+ "N on",
+ "▁def inition",
+ "▁definit ion",
+ "▁defin ition",
+ "▁ definition",
+ "▁at tr",
+ "▁att r",
+ "▁ attr",
+ "▁sign al",
+ "▁sig nal",
+ "▁ signal",
+ "h l",
+ "▁s el",
+ "▁se l",
+ "▁ sel",
+ "▁vol ume",
+ "▁ volume",
+ "▁c ache",
+ "▁ca che",
+ "▁ cache",
+ "he ns",
+ "hen s",
+ "h ens",
+ "▁w ird",
+ "▁wir d",
+ "[ \\",
+ "NO T",
+ "N OT",
+ "▁e lection",
+ "▁el ection",
+ "▁elect ion",
+ "▁ele ction",
+ "▁ election",
+ "ut t",
+ "u tt",
+ "▁W indow",
+ "▁Wind ow",
+ "▁ Window",
+ "en tal",
+ "ent al",
+ "enta l",
+ "if est",
+ "ife st",
+ "x f",
+ "▁Р а",
+ "▁over all",
+ "bl ic",
+ "b lic",
+ "▁ed itor",
+ "▁edit or",
+ "▁ editor",
+ "ad en",
+ "ade n",
+ "a den",
+ "▁c art",
+ "▁car t",
+ "▁ca rt",
+ "▁ cart",
+ "Le ft",
+ "L eft",
+ "ul s",
+ "u ls",
+ "bin g",
+ "bi ng",
+ "b ing",
+ "R ight",
+ "▁s é",
+ "Si m",
+ "S im",
+ "▁came ra",
+ "▁cam era",
+ "▁ camera",
+ "▁f av",
+ "▁fa v",
+ "De cl",
+ "Dec l",
+ "sp ring",
+ "spr ing",
+ "▁err ors",
+ "▁er rors",
+ "▁error s",
+ "▁ errors",
+ "T ab",
+ "print ln",
+ "▁B ern",
+ "▁Be rn",
+ "▁Ber n",
+ "na b",
+ "n ab",
+ "▁B ase",
+ "▁Bas e",
+ "▁Ba se",
+ "▁ Base",
+ "▁a uth",
+ "▁aut h",
+ "▁au th",
+ "▁ auth",
+ "▁app arent",
+ "▁ap parent",
+ "▁appar ent",
+ "▁pres ented",
+ "▁present ed",
+ "▁rem ained",
+ "▁remain ed",
+ "▁w et",
+ "▁we t",
+ "En c",
+ "E nc",
+ "IN FO",
+ "▁S ing",
+ "▁Si ng",
+ "▁Sin g",
+ "▁ Sing",
+ "pack age",
+ ")) );",
+ "))) ;",
+ ") ));",
+ "▁S ocial",
+ "▁So cial",
+ "▁Soc ial",
+ "▁Soci al",
+ "▁M ass",
+ "▁Ma ss",
+ "▁Mas s",
+ "▁ Mass",
+ "▁des pite",
+ "▁desp ite",
+ "▁m obile",
+ "▁mob ile",
+ "▁mobil e",
+ "▁ mobile",
+ "▁l abor",
+ "▁la bor",
+ "▁lab or",
+ "G o",
+ "▁e sp",
+ "▁es p",
+ "▁ esp",
+ "▁T able",
+ "▁Ta ble",
+ "▁Tab le",
+ "▁ Table",
+ "▁ex pert",
+ "▁exper t",
+ "▁exp ert",
+ "▁f lex",
+ "▁fl ex",
+ "▁fle x",
+ "▁ flex",
+ "▁prof ession",
+ "▁profess ion",
+ "▁p il",
+ "▁pi l",
+ "Col lection",
+ "Coll ection",
+ "Collect ion",
+ "LO CK",
+ "LOC K",
+ "▁ap plied",
+ "▁appl ied",
+ "al ler",
+ "all er",
+ "alle r",
+ "or ph",
+ "orp h",
+ "EN SE",
+ "ENS E",
+ "▁бы л",
+ "▁d b",
+ "▁ db",
+ "over line",
+ "▁C ode",
+ "▁Co de",
+ "▁ Code",
+ "▁by tes",
+ "▁byte s",
+ "▁ bytes",
+ "▁tr ouble",
+ "▁trou ble",
+ "▁на се",
+ "D D",
+ "▁Y ear",
+ "▁Ye ar",
+ "▁ Year",
+ "mb ox",
+ "m box",
+ "▁ke eping",
+ "▁keep ing",
+ "▁ keeping",
+ "▁k ick",
+ "▁ki ck",
+ "än g",
+ "ä ng",
+ "▁correspon ding",
+ "▁correspond ing",
+ "▁l ibrary",
+ "▁ library",
+ "▁*/ \r",
+ "▁ */\r",
+ "call back",
+ "um s",
+ "u ms",
+ "▁j son",
+ "▁js on",
+ "▁ json",
+ "▁M ount",
+ "▁Mo unt",
+ "▁ Mount",
+ "▁St and",
+ "▁Stan d",
+ "▁Sta nd",
+ "▁ Stand",
+ "IG HT",
+ "IGH T",
+ "▁New s",
+ "▁Ne ws",
+ "▁ News",
+ "▁com ments",
+ "▁comm ents",
+ "▁comment s",
+ "▁ comments",
+ "return s",
+ "C al",
+ "▁a ward",
+ "▁aw ard",
+ "▁b ought",
+ "▁bou ght",
+ "include graphics",
+ "▁ ле",
+ "do t",
+ "d ot",
+ "ro nic",
+ "ron ic",
+ "r onic",
+ "▁extrem ely",
+ "▁extreme ly",
+ "▁min or",
+ "▁mi nor",
+ "if er",
+ "ife r",
+ "i fer",
+ "ja va",
+ "jav a",
+ "j ava",
+ "en dar",
+ "end ar",
+ "enda r",
+ "la yout",
+ "lay out",
+ "l ayout",
+ "pl ies",
+ "▁b uf",
+ "▁bu f",
+ "▁ buf",
+ "▁Is land",
+ "▁Ab out",
+ "▁ About",
+ "▁w est",
+ "▁we st",
+ "▁ west",
+ "▁S cott",
+ "▁Sc ott",
+ "▁Scot t",
+ "AC T",
+ "A CT",
+ "Wh y",
+ "W hy",
+ "▁large st",
+ "▁larg est",
+ "▁cont ainer",
+ "▁contain er",
+ "▁ container",
+ "▁t emperature",
+ "▁temper ature",
+ "▁ £",
+ "▁red uce",
+ "▁redu ce",
+ "▁ reduce",
+ "▁f oi",
+ "▁fo i",
+ "ha n",
+ "h an",
+ "▁b od",
+ "▁bo d",
+ "▁V an",
+ "▁Va n",
+ "▁null ptr",
+ "▁ nullptr",
+ "▁d ating",
+ "▁da ting",
+ "▁dat ing",
+ "▁ dating",
+ "▁ch ain",
+ "▁cha in",
+ "▁ chain",
+ "Fl ags",
+ "Flag s",
+ "ient o",
+ "ien to",
+ "i ento",
+ "so rt",
+ "sor t",
+ "s ort",
+ "▁f an",
+ "▁fa n",
+ "▁ fan",
+ "▁det ermine",
+ "▁determ ine",
+ "▁determin e",
+ "▁deter mine",
+ "▁w ear",
+ "▁we ar",
+ "▁ wear",
+ "B E",
+ "▁appropri ate",
+ "л ся",
+ "то в",
+ "т ов",
+ "▁go als",
+ "▁goal s",
+ "▁M ap",
+ "▁Ma p",
+ "▁ Map",
+ "▁S ar",
+ "▁Sa r",
+ "▁O ption",
+ "▁Opt ion",
+ "▁ Option",
+ "▁h ate",
+ "▁ha te",
+ "▁hat e",
+ "▁z ijn",
+ ", -",
+ "▁im plied",
+ "▁impl ied",
+ "bit s",
+ "bi ts",
+ "b its",
+ "▁M en",
+ "▁Me n",
+ "▁ Men",
+ "sk ip",
+ "ski p",
+ "▁M ond",
+ "▁Mon d",
+ "▁Mo nd",
+ "▁H on",
+ "▁Ho n",
+ "▁pro ve",
+ "▁pr ove",
+ "▁prov e",
+ "va n",
+ "v an",
+ "▁tr aff",
+ "▁tra ff",
+ "▁in tr",
+ "▁int r",
+ "▁ intr",
+ "pi c",
+ "p ic",
+ "▁dro pped",
+ "▁drop ped",
+ "▁w erd",
+ "▁we rd",
+ "▁wer d",
+ "▁separ ate",
+ "is a",
+ "i sa",
+ "▁t ab",
+ "▁ta b",
+ "▁ tab",
+ "tm l",
+ "t ml",
+ "▁\" $",
+ "mu tex",
+ "mut ex",
+ "▁P an",
+ "▁Pa n",
+ "▁ Pan",
+ "ser ve",
+ "serv e",
+ "s erve",
+ "▁hot el",
+ "▁L ast",
+ "▁La st",
+ "▁Las t",
+ "▁ Last",
+ "st ep",
+ "ste p",
+ "▁v ir",
+ "▁vi r",
+ "▁ vir",
+ "R ule",
+ "is tan",
+ "ist an",
+ "ista n",
+ "i stan",
+ "ot ing",
+ "oti ng",
+ "o ting",
+ "ar ks",
+ "ark s",
+ "(_ _",
+ "( __",
+ "▁e ls",
+ "▁el s",
+ "▁ els",
+ "Pl ayer",
+ "Play er",
+ "P layer",
+ "] ]",
+ "ви ч",
+ "yc h",
+ "y ch",
+ "ex ception",
+ "except ion",
+ "=\" ../",
+ "▁im agine",
+ "▁imag ine",
+ "▁imagin e",
+ "\"} ,",
+ "\" },",
+ "ic ago",
+ "ica go",
+ "el er",
+ "ele r",
+ "e ler",
+ "▁v s",
+ "▁ vs",
+ "▁A frica",
+ "▁Afr ica",
+ "▁Bus iness",
+ "oc ks",
+ "ock s",
+ "o cks",
+ "▁p rz",
+ "▁pr z",
+ "▁fuck ing",
+ "▁p icked",
+ "▁pick ed",
+ "▁pic ked",
+ "▁в і",
+ "▁ ві",
+ "▁\" ,",
+ "▁ \",",
+ "▁b ott",
+ "▁bo tt",
+ "▁bot t",
+ "▁fail ure",
+ "▁ failure",
+ "[ :",
+ "▁G ar",
+ "▁Ga r",
+ "ap es",
+ "ape s",
+ "a pes",
+ "up le",
+ "u ple",
+ "▁f er",
+ "▁fe r",
+ "▁ fer",
+ "▁p urchase",
+ "▁purch ase",
+ "▁п ер",
+ "▁пе р",
+ "▁ пер",
+ "▁b ird",
+ "▁bi rd",
+ "▁ bird",
+ "W idget",
+ "▁Sund ay",
+ "▁Sun day",
+ "▁A maz",
+ "▁Am az",
+ "▁ Amaz",
+ "▁cons ult",
+ "ut sch",
+ "uts ch",
+ "an to",
+ "ant o",
+ "St orage",
+ "▁he ader",
+ "▁head er",
+ "▁ header",
+ "üh r",
+ "ü hr",
+ "▁H a",
+ "▁ Ha",
+ "▁Associ ation",
+ "▁s ight",
+ "▁si ght",
+ "▁sig ht",
+ "▁sigh t",
+ "C ell",
+ "▁pro file",
+ "▁prof ile",
+ "▁ profile",
+ "▁fem ale",
+ "å n",
+ "▁w id",
+ "▁ wid",
+ "z n",
+ "Dir ect",
+ "Di rect",
+ "D irect",
+ "▁st ret",
+ "▁str et",
+ "▁stre t",
+ "▁ stret",
+ "aa t",
+ "a at",
+ "▁pat ient",
+ "▁ patient",
+ "he re",
+ "her e",
+ "h ere",
+ "▁A tl",
+ "▁At l",
+ "in et",
+ "ine t",
+ "i net",
+ "Def inition",
+ "im ary",
+ "ima ry",
+ "i mary",
+ "Pol icy",
+ "▁d ut",
+ "▁du t",
+ "▁major ity",
+ "с і",
+ "▁Pro ject",
+ "▁ Project",
+ "By Id",
+ "▁belie ved",
+ "▁believe d",
+ "▁Mus ic",
+ "▁ Music",
+ "з ы",
+ "an ti",
+ "ant i",
+ "▁o der",
+ "▁od er",
+ "▁ oder",
+ "Ch annel",
+ "▁s le",
+ "▁sl e",
+ "▁sequ ence",
+ "▁ sequence",
+ "▁pie ces",
+ "▁piece s",
+ "▁k ne",
+ "▁kn e",
+ "▁abs olutely",
+ "▁absolut ely",
+ "▁absolute ly",
+ "▁Phil ip",
+ "ab ilities",
+ "abil ities",
+ "Qu e",
+ "Q ue",
+ "▁K ar",
+ "▁Ka r",
+ "Ex ecut",
+ "Exec ut",
+ "▁D evel",
+ "▁De vel",
+ "▁Dev el",
+ "▁elect ric",
+ "ful l",
+ "fu ll",
+ "f ull",
+ "rol led",
+ "roll ed",
+ "Do m",
+ "D om",
+ "▁r iver",
+ "▁ri ver",
+ "▁riv er",
+ "▁ river",
+ "▁health y",
+ "▁heal thy",
+ "▁ex tern",
+ "▁ext ern",
+ "fi t",
+ "f it",
+ "▁co ach",
+ "▁K r",
+ "as ta",
+ "ast a",
+ "a sta",
+ "Com pat",
+ "Comp at",
+ "▁e xit",
+ "▁ex it",
+ "▁ exit",
+ "▁Con st",
+ "▁Cons t",
+ "▁ Const",
+ "af ter",
+ "aft er",
+ "a fter",
+ "▁should er",
+ "▁j obs",
+ "▁job s",
+ "▁jo bs",
+ "zo ne",
+ "zon e",
+ "z one",
+ "▁s ale",
+ "▁sa le",
+ "▁sal e",
+ "ix el",
+ "▁determ ined",
+ "▁determine d",
+ "▁determin ed",
+ "▁any way",
+ "or f",
+ "o rf",
+ "▁G er",
+ "▁Ge r",
+ "all el",
+ "alle l",
+ "re es",
+ "ree s",
+ "r ees",
+ "as m",
+ "a sm",
+ "im s",
+ "i ms",
+ "▁rec ords",
+ "▁record s",
+ "▁ records",
+ "▁cor por",
+ "▁int ellig",
+ "▁intel lig",
+ "▁P rem",
+ "▁Pr em",
+ "▁Pre m",
+ "▁d riving",
+ "▁dr iving",
+ "▁dri ving",
+ "▁driv ing",
+ "▁mar riage",
+ "▁Th ank",
+ "▁ Thank",
+ "▁w illing",
+ "▁will ing",
+ "M C",
+ "Field s",
+ "It ems",
+ "Item s",
+ "▁m icro",
+ "▁mi cro",
+ "▁mic ro",
+ "▁l ift",
+ "▁li ft",
+ "▁lif t",
+ "ir ection",
+ "ire ction",
+ "irect ion",
+ "i rection",
+ "Acc ount",
+ "Ac count",
+ "▁arch itect",
+ "tr ack",
+ "tra ck",
+ "▁p rin",
+ "▁pr in",
+ "▁pri n",
+ "P A",
+ "▁r uns",
+ "▁run s",
+ "▁ru ns",
+ "▁Tex as",
+ "is her",
+ "ish er",
+ "en sure",
+ "ens ure",
+ "▁B oth",
+ "▁Bo th",
+ "▁Bot h",
+ "ко м",
+ "▁Col or",
+ "▁Co lor",
+ "▁ Color",
+ "Reg ister",
+ "▁J oe",
+ "▁Jo e",
+ "ge q",
+ "g eq",
+ "le ts",
+ "let s",
+ "l ets",
+ "ad ing",
+ "adi ng",
+ "a ding",
+ "▁ar my",
+ "▁arm y",
+ "▁B ank",
+ "▁Ban k",
+ "▁ Bank",
+ "ot ic",
+ "oti c",
+ "Pro duct",
+ "Produ ct",
+ "im port",
+ "imp ort",
+ "▁W ed",
+ "▁We d",
+ "▁c ry",
+ "▁cr y",
+ "gr ade",
+ "grad e",
+ "gra de",
+ "g rade",
+ "di g",
+ "d ig",
+ "ga l",
+ "g al",
+ "к ла",
+ "es ted",
+ "est ed",
+ "este d",
+ "e sted",
+ "õ es",
+ "ge rs",
+ "ger s",
+ "g ers",
+ "olog ie",
+ "olo gie",
+ "то м",
+ "ra zy",
+ "raz y",
+ "r azy",
+ "▁d inner",
+ "▁din ner",
+ "Q U",
+ "▁fin gers",
+ "▁fing ers",
+ "▁finger s",
+ "UL E",
+ "U LE",
+ "cl aim",
+ "▁adv antage",
+ "▁advant age",
+ "▁var iable",
+ "▁vari able",
+ "▁ variable",
+ "▁med ic",
+ "▁medi c",
+ "▁m ale",
+ "▁ma le",
+ "▁mal e",
+ "▁circ um",
+ "▁м і",
+ "▁ мі",
+ "▁inter net",
+ "▁intern et",
+ "W N",
+ "▁l ab",
+ "▁la b",
+ "▁ lab",
+ "az ine",
+ "azi ne",
+ "ч но",
+ "▁l oop",
+ "▁lo op",
+ "▁ loop",
+ "▁p red",
+ "▁pre d",
+ "▁pr ed",
+ "▁ pred",
+ "▁con sequ",
+ "▁cons equ",
+ "▁conse qu",
+ "▁bal ance",
+ "▁ balance",
+ "fort un",
+ "▁g ift",
+ "▁gi ft",
+ "▁d rug",
+ "▁dr ug",
+ "▁dru g",
+ "▁c ash",
+ "▁cas h",
+ "▁ca sh",
+ "ски х",
+ "с ких",
+ "r g",
+ "ist ribut",
+ "▁high est",
+ "▁hig hest",
+ "êm e",
+ "ê me",
+ "em ph",
+ "emp h",
+ "em on",
+ "e mon",
+ "▁per formed",
+ "▁perform ed",
+ "cu t",
+ "c ut",
+ "▁cl oser",
+ "▁close r",
+ "▁clos er",
+ "▁clo ser",
+ "▁be coming",
+ "▁bec oming",
+ "▁\" \",",
+ "▁\"\" ,",
+ "st ar",
+ "sta r",
+ "s tar",
+ "pu b",
+ "p ub",
+ "▁pre par",
+ "▁prep ar",
+ "▁v ote",
+ "▁vo te",
+ "▁vot e",
+ "▁ vote",
+ "il de",
+ "ild e",
+ "▁im press",
+ "▁imp ress",
+ "▁employ ees",
+ "▁employee s",
+ "▁e inen",
+ "▁ein en",
+ "▁eine n",
+ "▁sm ooth",
+ "▁s now",
+ "▁sn ow",
+ "▁p urs",
+ "▁pur s",
+ "▁pu rs",
+ "▁v oc",
+ "▁vo c",
+ "▁M icrosoft",
+ "▁Micro soft",
+ "▁ Microsoft",
+ "P U",
+ "▁in come",
+ "▁inc ome",
+ "in os",
+ "ino s",
+ "i nos",
+ "▁oper ator",
+ "▁opera tor",
+ "▁ operator",
+ "▁equ ival",
+ "▁pass word",
+ "▁ password",
+ "ci ón",
+ "ció n",
+ "c ión",
+ "su ccess",
+ "▁e mp",
+ "▁em p",
+ "▁ emp",
+ "HO UT",
+ "H OUT",
+ "▁c a",
+ "▁ ca",
+ "fl ag",
+ "f lag",
+ "il ly",
+ "ill y",
+ "cre te",
+ "cr ete",
+ "cret e",
+ "fr ak",
+ "▁h idden",
+ "▁hid den",
+ "▁ hidden",
+ "▁\" %",
+ "▁ \"%",
+ "ER N",
+ "ро ва",
+ "ров а",
+ "▁U N",
+ "▁ UN",
+ "ro ke",
+ "rok e",
+ "r oke",
+ "mi ss",
+ "m iss",
+ "▁s plit",
+ "▁sp lit",
+ "▁spl it",
+ "▁ split",
+ "Re ference",
+ ")$ ,",
+ ") $,",
+ "ep er",
+ "e per",
+ "▁N O",
+ "▁ NO",
+ "▁s quare",
+ "▁squ are",
+ "▁ square",
+ "su r",
+ "s ur",
+ "че н",
+ "ч ен",
+ "es ter",
+ "est er",
+ "este r",
+ "e ster",
+ "н ь",
+ "} \"",
+ "ra wn",
+ "raw n",
+ "r awn",
+ "ru le",
+ "r ule",
+ "▁aud ience",
+ "es te",
+ "est e",
+ "e ste",
+ "em s",
+ "e ms",
+ "IC ENSE",
+ "▁I ll",
+ "▁Il l",
+ "▁ Ill",
+ "US E",
+ "U SE",
+ "▁b on",
+ "▁bo n",
+ "▁ bon",
+ "bu r",
+ "b ur",
+ "▁s ick",
+ "▁si ck",
+ "▁h orse",
+ "▁hor se",
+ "▁hors e",
+ "▁E duc",
+ "▁Ed uc",
+ "▁Edu c",
+ "▁benef it",
+ "▁c ro",
+ "▁cr o",
+ "▁ cro",
+ "Ap plication",
+ "▁cor re",
+ "▁gu arante",
+ "DA TA",
+ "DAT A",
+ "D ATA",
+ "▁expl ained",
+ "▁explain ed",
+ "T X",
+ "▁o nt",
+ "▁on t",
+ "▁ ont",
+ "▁F lor",
+ "▁Fl or",
+ "▁Flo r",
+ "▁re ports",
+ "▁rep orts",
+ "▁report s",
+ "▁Re al",
+ "▁ Real",
+ "ud ed",
+ "ude d",
+ "u ded",
+ "le an",
+ "▁cit iz",
+ "▁dec ide",
+ "▁decid e",
+ "W S",
+ "▁do main",
+ "▁dom ain",
+ "▁ domain",
+ "▁ref lect",
+ "▁ reflect",
+ "▁min imum",
+ "▁minim um",
+ "▁le gs",
+ "▁leg s",
+ "▁sm iled",
+ "▁smile d",
+ "f i",
+ "▁p ure",
+ "▁pur e",
+ "▁pu re",
+ "▁C ustom",
+ "▁ Custom",
+ "▁ess ential",
+ "▁observ ed",
+ "▁observe d",
+ "▁obs erved",
+ "By tes",
+ "Byte s",
+ "▁c tx",
+ "▁ ctx",
+ "▁r ates",
+ "▁rate s",
+ "▁rat es",
+ "▁ra tes",
+ "mb re",
+ "m bre",
+ "▁w orry",
+ "▁wor ry",
+ ") ^",
+ "▁Re search",
+ "▁Res earch",
+ "Ro ot",
+ "R oot",
+ "Window s",
+ "ult ure",
+ "ultur e",
+ "▁rel ative",
+ "▁relativ e",
+ "▁ relative",
+ "▁s eu",
+ "▁se u",
+ "▁n ie",
+ "▁ni e",
+ "▁ nie",
+ "▁s hook",
+ "▁sh ook",
+ "ious ly",
+ "i ously",
+ "▁ad vert",
+ "▁adv ert",
+ "Se e",
+ "S ee",
+ "▁Cent ral",
+ "▁b atter",
+ "▁batt er",
+ "▁bat ter",
+ "▁s igned",
+ "▁sign ed",
+ "▁sig ned",
+ "▁ signed",
+ "T S",
+ "on i",
+ "o ni",
+ "▁pre pared",
+ "▁prep ared",
+ "▁prepar ed",
+ "▁prepare d",
+ "ga te",
+ "g ate",
+ "▁C are",
+ "▁Car e",
+ "▁Ca re",
+ "ca re",
+ "car e",
+ "c are",
+ "▁sup ply",
+ "▁supp ly",
+ "Ex p",
+ "E xp",
+ "bol ds",
+ "bold s",
+ "b olds",
+ "▁tr ail",
+ "▁tra il",
+ "▁f ish",
+ "▁fi sh",
+ "▁fis h",
+ "▁ fish",
+ "▁un its",
+ "▁unit s",
+ "▁ units",
+ "ven ue",
+ "v enue",
+ "х и",
+ "▁W ood",
+ "▁Wo od",
+ "▁c ategory",
+ "▁categ ory",
+ "▁categor y",
+ "▁ category",
+ "▁b le",
+ "▁bl e",
+ "▁ ble",
+ "▁over ride",
+ "▁ override",
+ "fo o",
+ "f oo",
+ "▁influ ence",
+ "en th",
+ "ent h",
+ "ri j",
+ "r ij",
+ "▁ad apt",
+ "ic ians",
+ "ici ans",
+ "ician s",
+ "icia ns",
+ "de leted",
+ "del eted",
+ "delete d",
+ "▁v ision",
+ "▁vis ion",
+ "▁ vision",
+ "ct rl",
+ "ctr l",
+ "c trl",
+ "L ambda",
+ "t p",
+ "mon d",
+ "mo nd",
+ "m ond",
+ "atur day",
+ "norm al",
+ "nor mal",
+ "n ormal",
+ "▁thous and",
+ "▁Prof ess",
+ "▁dise ase",
+ "cl ip",
+ "cli p",
+ "▁г ра",
+ "▁ гра",
+ "bolds ymbol",
+ "bold symbol",
+ "O B",
+ "▁chall enge",
+ "▁challeng e",
+ "▁m otion",
+ "▁mot ion",
+ "▁w his",
+ "▁wh is",
+ "▁le aders",
+ "▁lead ers",
+ "▁leader s",
+ "▁col on",
+ "▁co lon",
+ "▁ colon",
+ "▁s uit",
+ "▁su it",
+ "▁ suit",
+ "mi d",
+ "m id",
+ "amp ion",
+ "á g",
+ "▁view s",
+ "▁vie ws",
+ "▁ views",
+ "▁app ears",
+ "▁appe ars",
+ "▁appear s",
+ "an cel",
+ "ance l",
+ "anc el",
+ "▁z we",
+ "▁zw e",
+ "IS T",
+ "I ST",
+ "▁le aves",
+ "▁leave s",
+ "▁e nh",
+ "▁en h",
+ "▁ enh",
+ "Act ive",
+ "Activ e",
+ "▁d it",
+ "▁di t",
+ "▁ dit",
+ "if icate",
+ "ific ate",
+ "ifica te",
+ "mat rix",
+ "Ex pression",
+ "Exp ression",
+ "Expr ession",
+ "Express ion",
+ "Re ader",
+ "Read er",
+ "▁m ental",
+ "▁men tal",
+ "▁ment al",
+ "em bre",
+ "emb re",
+ "e mbre",
+ "▁de cor",
+ "▁dec or",
+ "▁ decor",
+ "ar ts",
+ "art s",
+ "▁v ent",
+ "▁ve nt",
+ "▁ven t",
+ "▁ vent",
+ "ne l",
+ "n el",
+ "line s",
+ "li nes",
+ "lin es",
+ "l ines",
+ "up id",
+ "u pid",
+ "er ved",
+ "erv ed",
+ "erve d",
+ "▁bo ys",
+ "▁boy s",
+ "▁ boys",
+ "ал ь",
+ "а ль",
+ "MO D",
+ "M OD",
+ "is l",
+ "i sl",
+ "▁[ [",
+ "▁ [[",
+ "ph y",
+ "p hy",
+ "▁. .",
+ "▁ ..",
+ "▁a gent",
+ "▁ag ent",
+ "▁age nt",
+ "▁ agent",
+ "▁S ervices",
+ "▁Service s",
+ "▁Serv ices",
+ "▁ Services",
+ "▁i ron",
+ "▁ir on",
+ "▁ iron",
+ "▁com ponents",
+ "▁compon ents",
+ "▁component s",
+ "▁ components",
+ "▁f re",
+ "▁fr e",
+ "▁ fre",
+ "iction ary",
+ "▁t ests",
+ "▁te sts",
+ "▁test s",
+ "▁ tests",
+ ".~ \\",
+ ". ~\\",
+ "ob s",
+ "o bs",
+ "▁М и",
+ "▁об ла",
+ "▁ass ess",
+ "▁Fr iday",
+ "▁we ather",
+ "k g",
+ "ст ра",
+ "с тра",
+ ". }",
+ "end ant",
+ "enda nt",
+ "an na",
+ "ann a",
+ "▁Japan ese",
+ "cm p",
+ "c mp",
+ "▁Ar my",
+ "▁Arm y",
+ "on ym",
+ "ony m",
+ "o nym",
+ "▁rel ax",
+ "date s",
+ "da tes",
+ "dat es",
+ "d ates",
+ "▁R ussian",
+ "▁Russ ian",
+ "▁Russia n",
+ "▁excell ent",
+ "') )",
+ "' ))",
+ "IL ITY",
+ "▁sh owing",
+ "▁show ing",
+ "▁Dan iel",
+ "м я",
+ "▁M ain",
+ "▁Ma in",
+ "▁Mai n",
+ "▁ Main",
+ "Ph i",
+ "P hi",
+ "▁R ock",
+ "▁Ro ck",
+ "▁Roc k",
+ "▁g rew",
+ "▁gr ew",
+ "▁gre w",
+ "▁y ield",
+ "i ère",
+ "se g",
+ "s eg",
+ "}} $",
+ "} }$",
+ "▁st rict",
+ "▁str ict",
+ "▁stri ct",
+ "▁ strict",
+ "▁v ehicle",
+ "▁veh icle",
+ "U D",
+ "A F",
+ "S w",
+ "▁c hest",
+ "▁ch est",
+ "▁che st",
+ "▁off icer",
+ "▁offic er",
+ "▁office r",
+ "▁e ar",
+ "▁ ear",
+ "HE R",
+ "H ER",
+ "no on",
+ "n oon",
+ "▁jour ney",
+ "N T",
+ "▁d ivers",
+ "▁di vers",
+ "▁div ers",
+ "▁diver s",
+ "▁dive rs",
+ "▁Fin ally",
+ "▁Final ly",
+ "F ound",
+ "▁A S",
+ "▁ AS",
+ "ri k",
+ "r ik",
+ "▁con str",
+ "▁const r",
+ "▁cons tr",
+ "▁s ust",
+ "▁su st",
+ "▁sus t",
+ "ac count",
+ "acc ount",
+ "acco unt",
+ "▁w alls",
+ "▁wall s",
+ "▁wal ls",
+ "▁entire ly",
+ "It er",
+ "I ter",
+ "ch a",
+ "c ha",
+ "is hes",
+ "ish es",
+ "IV E",
+ "I VE",
+ "▁pr ime",
+ "▁prim e",
+ "▁pri me",
+ "▁ prime",
+ "▁ …",
+ "x e",
+ "ut en",
+ "ute n",
+ "u ten",
+ "ar se",
+ "ars e",
+ "▁P a",
+ "put e",
+ "pu te",
+ "p ute",
+ "ä l",
+ "▁prote ction",
+ "▁protect ion",
+ "▁prot ection",
+ "▁ke ys",
+ "▁key s",
+ "▁ keys",
+ "Ma y",
+ "M ay",
+ "By te",
+ "Con st",
+ "Cons t",
+ "B L",
+ "▁п е",
+ "▁ пе",
+ "▁s pl",
+ "▁sp l",
+ "▁ spl",
+ "▁cl othes",
+ "▁cloth es",
+ "as hed",
+ "ash ed",
+ "Mar k",
+ "M ark",
+ "è me",
+ "▁f ait",
+ "▁fa it",
+ "▁introdu ced",
+ "▁introduce d",
+ "un lock",
+ "▁In stead",
+ "▁Inst ead",
+ "ans ion",
+ "reg ion",
+ "▁Amer icans",
+ "▁American s",
+ "▁America ns",
+ "▁ind eed",
+ "▁inde ed",
+ "wid get",
+ "w idget",
+ "▁real ize",
+ "▁realiz e",
+ "▁f ro",
+ "▁fr o",
+ "BI T",
+ "B IT",
+ "▁Re act",
+ "▁ React",
+ "RE AD",
+ "as ket",
+ "ask et",
+ "ne ver",
+ "n ever",
+ "▁p oll",
+ "▁pol l",
+ "▁po ll",
+ "▁ poll",
+ "ic ol",
+ "ico l",
+ "i col",
+ "▁p rev",
+ "▁pre v",
+ "▁pr ev",
+ "▁ prev",
+ "▁h yp",
+ "▁hy p",
+ "▁F ur",
+ "▁Fu r",
+ "cl oud",
+ "▁L ee",
+ "▁Le e",
+ "pl ing",
+ "p ling",
+ "▁Ch ild",
+ "▁Chi ld",
+ "▁ Child",
+ "▁ide al",
+ "▁idea l",
+ "Se lector",
+ "Select or",
+ "STAT US",
+ "uct ure",
+ "▁w ine",
+ "▁win e",
+ "▁poss ibly",
+ "▁put ting",
+ "▁r iv",
+ "▁ri v",
+ "▁ riv",
+ "▁w earing",
+ "▁we aring",
+ "▁wear ing",
+ "▁S ource",
+ "▁ Source",
+ "▁C as",
+ "▁Ca s",
+ "Ch anged",
+ "Change d",
+ "▁th anks",
+ "▁than ks",
+ "▁thank s",
+ "TI ME",
+ "TIM E",
+ "T IME",
+ "▁s port",
+ "▁sp ort",
+ "▁spo rt",
+ "▁A ward",
+ "▁Aw ard",
+ "▁g lad",
+ "▁gl ad",
+ "▁P ass",
+ "▁Pa ss",
+ "▁Pas s",
+ "▁ Pass",
+ "▁P os",
+ "▁Po s",
+ "▁ Pos",
+ "sc he",
+ "sch e",
+ "s che",
+ "▁C D",
+ "▁ CD",
+ "▁aff ord",
+ "▁af ford",
+ "▁W omen",
+ "▁Wo men",
+ "▁D istrict",
+ "▁Di strict",
+ "▁Dist rict",
+ "▁id entity",
+ "▁ident ity",
+ "▁ identity",
+ "▁part ies",
+ "▁par ties",
+ "▁partie s",
+ "▁parti es",
+ ": %",
+ "▁d rag",
+ "▁dr ag",
+ "▁ drag",
+ "▁m ai",
+ "▁ma i",
+ "! (",
+ "lang le",
+ "lan gle",
+ "l angle",
+ "▁kn owing",
+ "▁know ing",
+ "Pro ject",
+ "▁reg arding",
+ "▁regard ing",
+ "▁Jose ph",
+ "▁Jos eph",
+ "г е",
+ "▁D ar",
+ "▁Da r",
+ "▁H or",
+ "▁Ho r",
+ "▁ Hor",
+ "▁anim als",
+ "▁animal s",
+ "▁ext ension",
+ "▁extens ion",
+ "▁ extension",
+ "ска я",
+ "▁H an",
+ "▁Ha n",
+ "bt n",
+ "b tn",
+ "ac iones",
+ "aci ones",
+ "acion es",
+ "acio nes",
+ "▁f amiliar",
+ "▁fam iliar",
+ "▁famil iar",
+ "▁familia r",
+ "hol der",
+ "hold er",
+ "h older",
+ ": \r",
+ "st ood",
+ "sto od",
+ "▁li ked",
+ "▁like d",
+ "▁lik ed",
+ "CO DE",
+ "▁en able",
+ "▁ enable",
+ "▁p ed",
+ "▁pe d",
+ "▁ ped",
+ "it i",
+ "i ti",
+ "ha b",
+ "h ab",
+ "DI R",
+ "D IR",
+ "▁be at",
+ "▁ beat",
+ "т і",
+ "▁Min ister",
+ "▁Mini ster",
+ "▁p y",
+ "▁ py",
+ "P at",
+ "▁ex hib",
+ "▁exh ib",
+ "▁B uild",
+ "▁Bu ild",
+ "▁ Build",
+ "▁F ield",
+ "▁Fi eld",
+ "▁ Field",
+ "ic ian",
+ "ici an",
+ "icia n",
+ "▁coll abor",
+ "▁qu arter",
+ "▁quart er",
+ "▁quar ter",
+ "▁F alse",
+ "▁Fal se",
+ "▁ False",
+ "k m",
+ "▁v irtual",
+ "▁virt ual",
+ "▁ virtual",
+ "ow a",
+ "o wa",
+ "▁J on",
+ "▁Jo n",
+ "am in",
+ "ami n",
+ "a min",
+ "ue n",
+ "u en",
+ "▁и н",
+ "▁ ин",
+ "im ation",
+ "imat ion",
+ "ov ing",
+ "ovi ng",
+ "o ving",
+ "▁test ing",
+ "▁ testing",
+ "se ct",
+ "sec t",
+ "s ect",
+ "IT ION",
+ "I TION",
+ "! \\",
+ "ap y",
+ "a py",
+ "▁trans ition",
+ "▁transit ion",
+ "▁ transition",
+ "os itory",
+ "OD O",
+ "O DO",
+ "P D",
+ "n é",
+ "▁gener ate",
+ "▁gene rate",
+ "▁ generate",
+ "▁n ative",
+ "▁nat ive",
+ "▁ native",
+ "▁( '",
+ "▁ ('",
+ "▁e lle",
+ "▁el le",
+ "▁ell e",
+ "▁ elle",
+ "R R",
+ "▁h un",
+ "_- >",
+ "_ ->",
+ "ag nost",
+ "agn ost",
+ "▁pro posed",
+ "▁prop osed",
+ "▁propos ed",
+ "▁propose d",
+ "▁G ame",
+ "▁Ga me",
+ "▁Gam e",
+ "▁ Game",
+ "▁eff orts",
+ "▁effort s",
+ "в я",
+ "t c",
+ "с к",
+ "▁int ent",
+ "▁inte nt",
+ "▁ intent",
+ "▁B re",
+ "▁Br e",
+ "is c",
+ "i sc",
+ "▁pro test",
+ "▁prote st",
+ "▁prot est",
+ "▁h olds",
+ "▁hold s",
+ "▁hol ds",
+ "▁ holds",
+ "om etry",
+ "ome try",
+ "omet ry",
+ "o metry",
+ "▁H ave",
+ "▁Ha ve",
+ "▁Hav e",
+ "▁ Have",
+ "▁de tail",
+ "▁det ail",
+ "▁ detail",
+ "▁WIT HOUT",
+ "▁WITH OUT",
+ "ye r",
+ "y er",
+ "▁K on",
+ "▁Ko n",
+ "▁not iced",
+ "▁notice d",
+ "▁require ments",
+ "▁requirement s",
+ "DE BUG",
+ "ki ns",
+ "kin s",
+ "k ins",
+ "▁S pan",
+ "▁Sp an",
+ "▁ Span",
+ "▁c ars",
+ "▁car s",
+ "▁ca rs",
+ "me ta",
+ "met a",
+ "m eta",
+ "▁k il",
+ "▁ki l",
+ "▁ kil",
+ "▁B ron",
+ "▁Br on",
+ "▁Bro n",
+ "▁experience d",
+ "▁experi enced",
+ "▁re mind",
+ "▁rem ind",
+ "our se",
+ "ours e",
+ "▁W estern",
+ "▁West ern",
+ "▁Wes tern",
+ "ter ed",
+ "te red",
+ "tere d",
+ "t ered",
+ "▁dev ices",
+ "▁device s",
+ "▁ devices",
+ "▁pict ures",
+ "▁picture s",
+ "▁t ut",
+ "▁tu t",
+ "\" `",
+ "▁im possible",
+ "▁r ail",
+ "▁ra il",
+ "▁fe els",
+ "▁feel s",
+ "▁fee ls",
+ "ic as",
+ "ica s",
+ "i cas",
+ "il ling",
+ "ill ing",
+ "▁acc ident",
+ "▁' @",
+ "____ ____",
+ "▁n otes",
+ "▁not es",
+ "▁no tes",
+ "▁note s",
+ "▁ notes",
+ "om an",
+ "oma n",
+ "o man",
+ "Par ser",
+ "Parse r",
+ "Pars er",
+ "▁dis covered",
+ "▁discover ed",
+ "▁R oman",
+ "▁Rom an",
+ "▁Ro man",
+ "▁Roma n",
+ "▁bud get",
+ "▁gu ide",
+ "▁guid e",
+ "ki ng",
+ "kin g",
+ "k ing",
+ "▁in cred",
+ "▁inc red",
+ "▁incre d",
+ "ol ar",
+ "ola r",
+ "o lar",
+ "en den",
+ "end en",
+ "ende n",
+ "Des c",
+ "De sc",
+ "D esc",
+ "▁w ave",
+ "▁wa ve",
+ "▁ wave",
+ "б ли",
+ "ig t",
+ "i gt",
+ "▁re strict",
+ "▁rest rict",
+ "▁restr ict",
+ "▁R et",
+ "▁Re t",
+ "▁ Ret",
+ "▁m ac",
+ "▁ma c",
+ "▁ mac",
+ "у р",
+ "B S",
+ "í s",
+ "▁gener ation",
+ "de m",
+ "d em",
+ "al o",
+ "a lo",
+ "б ра",
+ "▁order ed",
+ "▁ord ered",
+ "▁ ordered",
+ "dr op",
+ "dro p",
+ "d rop",
+ "▁p p",
+ "▁ pp",
+ "▁Re view",
+ "▁Rev iew",
+ "▁ Review",
+ "▁liter ally",
+ "▁literal ly",
+ "▁S ir",
+ "▁Si r",
+ "▁ Sir",
+ "▁Y eah",
+ "▁Ye ah",
+ "▁ Yeah",
+ "▁d ensity",
+ "▁dens ity",
+ "▁ density",
+ "ri z",
+ "r iz",
+ "in de",
+ "ind e",
+ "i nde",
+ "▁g ain",
+ "▁ga in",
+ "▁ gain",
+ "▁p anel",
+ "▁pan el",
+ "▁pa nel",
+ "▁ panel",
+ "je t",
+ "j et",
+ "▁T imes",
+ "▁Time s",
+ "▁Tim es",
+ "▁Ti mes",
+ "▁ Times",
+ "▁n ella",
+ "▁ne lla",
+ "▁nel la",
+ "▁nell a",
+ "▁pre viously",
+ "▁previous ly",
+ "▁prev iously",
+ "point s",
+ "Se nd",
+ "S end",
+ "▁B rown",
+ "▁Br own",
+ "▁Bro wn",
+ "▁Brow n",
+ "ea ch",
+ "e ach",
+ "▁tr igger",
+ "▁ trigger",
+ "ome times",
+ "omet imes",
+ "ic os",
+ "ico s",
+ "i cos",
+ "G R",
+ "Pane l",
+ "Pan el",
+ "P anel",
+ "og en",
+ "oge n",
+ "o gen",
+ "▁c m",
+ "▁ cm",
+ "ru ctions",
+ "ruct ions",
+ "ruction s",
+ "▁k iss",
+ "▁ki ss",
+ "▁s olo",
+ "▁so lo",
+ "▁sol o",
+ "▁f amous",
+ "▁fam ous",
+ "ra n",
+ "r an",
+ "п ро",
+ "▁th ro",
+ "▁thr o",
+ "Gr aph",
+ "G raph",
+ "im it",
+ "imi t",
+ "i mit",
+ "▁V alue",
+ "▁Val ue",
+ "▁ Value",
+ "▁st arts",
+ "▁start s",
+ "▁star ts",
+ "ip eline",
+ "ipe line",
+ "h d",
+ "T C",
+ "▁dis cussion",
+ "▁discuss ion",
+ "▁tr uck",
+ "ak a",
+ "a ka",
+ "On ly",
+ "▁E qu",
+ "▁Eq u",
+ "▁ Equ",
+ "▁k ö",
+ "▁ kö",
+ "▁B es",
+ "▁Be s",
+ "▁crit ic",
+ "▁pro pos",
+ "▁prop os",
+ "▁b att",
+ "▁bat t",
+ "▁ba tt",
+ "▁S ection",
+ "▁Se ction",
+ "▁ Section",
+ "Sh ow",
+ "S how",
+ "g p",
+ "ST ATE",
+ "STAT E",
+ "PO ST",
+ "POS T",
+ "P OST",
+ "▁N ord",
+ "▁No rd",
+ "▁Nor d",
+ "▁in nov",
+ "▁inn ov",
+ "▁c rim",
+ "▁cr im",
+ "▁cri m",
+ "▁ crim",
+ "ax is",
+ "a xis",
+ "▁T urn",
+ "▁Tur n",
+ "▁Tu rn",
+ "▁ Turn",
+ "con n",
+ "co nn",
+ "Run time",
+ "▁rem aining",
+ "▁remain ing",
+ "os ton",
+ "ost on",
+ "osto n",
+ "o ston",
+ "▁ Э",
+ "▁window s",
+ "▁wind ows",
+ "▁ windows",
+ "▁R oyal",
+ "▁Ro yal",
+ "▁Roy al",
+ "▁v ide",
+ "▁vi de",
+ "▁vid e",
+ "P P",
+ "ch ron",
+ "chr on",
+ "▁s an",
+ "▁sa n",
+ "▁ san",
+ "▁r ise",
+ "▁ri se",
+ "▁ris e",
+ "▁ rise",
+ "▁d elle",
+ "▁de lle",
+ "▁del le",
+ "▁dell e",
+ "▁D ur",
+ "▁Du r",
+ "▁rap id",
+ "▁ra pid",
+ "ce rt",
+ "cer t",
+ "c ert",
+ "L A",
+ "ed ge",
+ "▁\\ ]",
+ "▁ \\]",
+ "▁en tered",
+ "▁ent ered",
+ "▁enter ed",
+ "▁l aws",
+ "▁la ws",
+ "▁law s",
+ "▁ph oto",
+ "▁phot o",
+ "▁ photo",
+ "▁ap plications",
+ "▁applic ations",
+ "▁application s",
+ "▁appl ications",
+ "▁Ber lin",
+ "▁ar rest",
+ "▁arr est",
+ "▁f ederal",
+ "▁fed eral",
+ "▁feder al",
+ "▁R ussia",
+ "▁Russ ia",
+ "▁us ual",
+ "▁r aw",
+ "▁ra w",
+ "▁ raw",
+ "▁pi ù",
+ "êt re",
+ "ê tre",
+ "JS ON",
+ "J SON",
+ "SI ON",
+ "S ION",
+ "xt ure",
+ "ist ent",
+ "iste nt",
+ "isten t",
+ "▁P ower",
+ "▁Po wer",
+ "▁Pow er",
+ "▁ Power",
+ "Bi t",
+ "B it",
+ "▁cap acity",
+ "▁capac ity",
+ "▁ capacity",
+ "▁c ards",
+ "▁car ds",
+ "▁card s",
+ "▁ cards",
+ "UI D",
+ "U ID",
+ "im ents",
+ "iment s",
+ "imen ts",
+ "i ments",
+ "▁d ar",
+ "▁da r",
+ "▁ dar",
+ "▁Ch icago",
+ "▁comfort able",
+ "ti p",
+ "t ip",
+ "ba s",
+ "b as",
+ "▁m u",
+ "▁ mu",
+ "▁en emy",
+ "▁enem y",
+ "ya n",
+ "y an",
+ "▁ф и",
+ "▁ фи",
+ "▁up dated",
+ "▁update d",
+ "▁ updated",
+ "an go",
+ "ang o",
+ "E v",
+ "E ffect",
+ "os ing",
+ "osi ng",
+ "o sing",
+ "ren ce",
+ "r ence",
+ "▁Con gress",
+ "▁Cong ress",
+ "▁d efe",
+ "▁de fe",
+ "▁def e",
+ "▁i p",
+ "▁ ip",
+ "▁t out",
+ "▁to ut",
+ "▁tou t",
+ "▁f reedom",
+ "▁free dom",
+ "▁freed om",
+ "▁a o",
+ "▁ ao",
+ "▁There fore",
+ "▁Ther efore",
+ "Ed it",
+ "E dit",
+ "▁Vir gin",
+ "RE E",
+ "R EE",
+ "ar go",
+ "arg o",
+ "▁D am",
+ "▁Da m",
+ "▁ Dam",
+ "▁tra ffic",
+ "▁traff ic",
+ "ño s",
+ "ñ os",
+ "▁a lle",
+ "▁al le",
+ "▁all e",
+ "▁ alle",
+ "▁dep th",
+ "▁ depth",
+ "No w",
+ "N ow",
+ "▁s ides",
+ "▁side s",
+ "▁si des",
+ "▁sid es",
+ "▁го ди",
+ "▁год и",
+ "Des criptor",
+ "▁art ikel",
+ "▁n arrow",
+ "▁narr ow",
+ "▁nar row",
+ "__ _",
+ "_ __",
+ "k w",
+ "ut o",
+ "u to",
+ "▁Face book",
+ "▁Fac ebook",
+ "te gr",
+ "t egr",
+ "bo olean",
+ "ni k",
+ "n ik",
+ "b d",
+ "Tr ack",
+ "Tra ck",
+ "▁g ran",
+ "▁gr an",
+ "▁gra n",
+ "res hold",
+ "resh old",
+ "ве т",
+ "в ет",
+ "wr ap",
+ "w rap",
+ "▁n oise",
+ "▁no ise",
+ "ig u",
+ "i gu",
+ "▁B on",
+ "▁Bo n",
+ "▁ Bon",
+ "▁w y",
+ "▁ wy",
+ "lin ux",
+ "ck s",
+ "c ks",
+ "▁f ans",
+ "▁fa ns",
+ "▁fan s",
+ "▁m ach",
+ "▁ma ch",
+ "▁mac h",
+ "▁p rices",
+ "▁pr ices",
+ "▁pri ces",
+ "▁price s",
+ "é v",
+ "ou ts",
+ "out s",
+ "o uts",
+ "stand ing",
+ "stan ding",
+ "▁c ateg",
+ "▁cat eg",
+ "; \\",
+ "▁de cre",
+ "▁dec re",
+ "▁S aturday",
+ "▁m enu",
+ "▁me nu",
+ "▁men u",
+ "▁ menu",
+ "▁N ov",
+ "▁No v",
+ "▁Y et",
+ "▁Ye t",
+ "▁та к",
+ "lic he",
+ "li che",
+ "lich e",
+ "l iche",
+ "▁Ac adem",
+ "▁commun ication",
+ "us ing",
+ "u sing",
+ "▁Soc iety",
+ "▁Soci ety",
+ "▁n uc",
+ "▁nu c",
+ "pect ive",
+ "or ial",
+ "oria l",
+ "ori al",
+ "o rial",
+ "▁af raid",
+ "▁an imal",
+ "▁anim al",
+ "▁turn ing",
+ "▁tur ning",
+ "ds t",
+ "d st",
+ "math frak",
+ "le rs",
+ "ler s",
+ "l ers",
+ "▁l ots",
+ "▁lo ts",
+ "▁lot s",
+ "▁ á",
+ "▁T ra",
+ "▁Tr a",
+ "▁ Tra",
+ "n p",
+ "▁r ose",
+ "▁ro se",
+ "▁ rose",
+ "▁G L",
+ "▁ GL",
+ "▁hel ping",
+ "▁help ing",
+ "▁w inter",
+ "▁win ter",
+ "▁ко м",
+ "▁ ком",
+ "Mo ck",
+ "M ock",
+ "▁invest ment",
+ "Us e",
+ "U se",
+ "▁Can ad",
+ "н д",
+ "Co py",
+ "Cop y",
+ "C opy",
+ "▁f ly",
+ "▁fl y",
+ "▁ fly",
+ "SE R",
+ "S ER",
+ "▁F ar",
+ "▁Fa r",
+ "▁R os",
+ "▁Ro s",
+ "am il",
+ "ami l",
+ "a mil",
+ "▁fight ing",
+ "▁rel igious",
+ "▁relig ious",
+ "su per",
+ "sup er",
+ "s uper",
+ "sc reen",
+ "scr een",
+ "s creen",
+ "▁f urn",
+ "▁fur n",
+ "▁fu rn",
+ "▁surpr ised",
+ "▁surprise d",
+ "▁re plied",
+ "▁repl ied",
+ "Act ivity",
+ "Activ ity",
+ "▁D own",
+ "▁Do wn",
+ "▁Dow n",
+ "▁ Down",
+ "▁in sert",
+ "▁ins ert",
+ "▁ insert",
+ "▁O lymp",
+ "▁point ed",
+ "▁po inted",
+ "▁C ard",
+ "▁Car d",
+ "▁Ca rd",
+ "▁ Card",
+ "dr iver",
+ "drive r",
+ "d river",
+ "▁D a",
+ "▁ Da",
+ "! --",
+ "ro ud",
+ "rou d",
+ "r oud",
+ "un do",
+ "und o",
+ "▁m essages",
+ "▁message s",
+ "▁mess ages",
+ "▁ messages",
+ "▁P oint",
+ "▁Po int",
+ "▁ Point",
+ "V M",
+ "▁p lane",
+ "▁pl ane",
+ "▁plan e",
+ "▁ plane",
+ "x c",
+ "▁telev ision",
+ "▁tele vision",
+ "▁televis ion",
+ "ё н",
+ "▁thous ands",
+ "▁thousand s",
+ "▁c ris",
+ "▁cr is",
+ "▁cri s",
+ "▁de lay",
+ "▁del ay",
+ "▁ delay",
+ "▁N ext",
+ "▁Ne xt",
+ "▁ Next",
+ "▁no mbre",
+ "▁nom bre",
+ "▁t u",
+ "▁ tu",
+ "▁sk ip",
+ "▁ski p",
+ "▁ skip",
+ "ro ad",
+ "r oad",
+ "istr ation",
+ "▁t ur",
+ "▁tu r",
+ "▁De velop",
+ "▁Devel op",
+ "▁П а",
+ "▁д ру",
+ "▁др у",
+ "▁wonder ful",
+ "> &",
+ "▁L iber",
+ "▁Li ber",
+ "▁Lib er",
+ "▁s cope",
+ "▁sc ope",
+ "▁ scope",
+ "▁man age",
+ "▁ma nage",
+ "▁d ass",
+ "▁da ss",
+ "▁das s",
+ "▁re call",
+ "▁rec all",
+ "P M",
+ "▁re levant",
+ "▁relev ant",
+ "▁E arth",
+ "▁ка к",
+ "▁a pr",
+ "▁ap r",
+ "▁A SS",
+ "▁AS S",
+ "▁ ASS",
+ "ié n",
+ "i én",
+ "▁S H",
+ "▁ SH",
+ "oo m",
+ "o om",
+ "it et",
+ "ite t",
+ "no ne",
+ "non e",
+ "n one",
+ "as i",
+ "a si",
+ "▁mot or",
+ "▁mo tor",
+ "▁S how",
+ "▁Sh ow",
+ "▁ Show",
+ "n b",
+ "▁fact ors",
+ "▁fa ctors",
+ "▁factor s",
+ "▁f orest",
+ "▁for est",
+ "▁fore st",
+ "▁fo rest",
+ "▁в ре",
+ "th m",
+ "t hm",
+ "▁m unicip",
+ "▁turn s",
+ "▁tur ns",
+ "▁Div ision",
+ "▁Di vision",
+ "E C",
+ "▁dis appe",
+ "struct or",
+ "stru ctor",
+ "▁some where",
+ "▁Afr ican",
+ "▁Africa n",
+ "▁Inst itute",
+ "▁Institut e",
+ "Gr id",
+ "G rid",
+ "▁te acher",
+ "▁teach er",
+ "▁tea cher",
+ "ur ies",
+ "uri es",
+ "u ries",
+ "▁respect ively",
+ "▁respective ly",
+ "▁S D",
+ "▁ SD",
+ "▁a live",
+ "▁al ive",
+ "▁ali ve",
+ "▁p ou",
+ "▁po u",
+ "▁W ater",
+ "▁Wat er",
+ "▁Wa ter",
+ "▁ Water",
+ "ф е",
+ "▁ch anging",
+ "▁chang ing",
+ "▁ changing",
+ "▁after noon",
+ "▁or ders",
+ "▁order s",
+ "▁ord ers",
+ "▁ orders",
+ "Re t",
+ "R et",
+ "Point er",
+ "Po inter",
+ "▁s av",
+ "▁sa v",
+ "er g",
+ "e rg",
+ "ok ed",
+ "oke d",
+ "o ked",
+ "ess ions",
+ "ession s",
+ "▁F ire",
+ "▁Fi re",
+ "▁ Fire",
+ "ar et",
+ "are t",
+ "a ret",
+ "im m",
+ "i mm",
+ "▁des ire",
+ "▁ що",
+ "▁De sign",
+ "▁Des ign",
+ "▁ Design",
+ "ut ure",
+ "▁Off ice",
+ "▁c md",
+ "▁cm d",
+ "▁ cmd",
+ "▁e ating",
+ "▁eat ing",
+ "Net work",
+ "▁r ough",
+ "▁ro ugh",
+ "▁rou gh",
+ "▁ rough",
+ "oper ator",
+ "IG N",
+ "I GN",
+ "▁s ports",
+ "▁sp orts",
+ "▁sport s",
+ "▁w eren",
+ "▁we ren",
+ "▁were n",
+ "▁wer en",
+ "▁n oted",
+ "▁not ed",
+ "▁no ted",
+ "▁note d",
+ "▁tw ice",
+ "II I",
+ "I II",
+ "▁a nx",
+ "▁an x",
+ "▁e lim",
+ "▁el im",
+ "▁а в",
+ "▁i o",
+ "▁ io",
+ "▁spe ech",
+ "▁con du",
+ "▁cond u",
+ "el les",
+ "ell es",
+ "elle s",
+ "id ade",
+ "ida de",
+ "idad e",
+ "▁adv ance",
+ "R I",
+ "oc a",
+ "o ca",
+ "/ \\",
+ "ap shot",
+ "aps hot",
+ "▁t ail",
+ "▁ta il",
+ "▁ tail",
+ "mod els",
+ "model s",
+ "mode ls",
+ "og y",
+ "o gy",
+ "▁J eff",
+ "▁Je ff",
+ "ir ation",
+ "irat ion",
+ "▁K ore",
+ "▁Ko re",
+ "▁Kor e",
+ "▁le ads",
+ "▁lead s",
+ "ba t",
+ "b at",
+ "Ad apter",
+ "c ategory",
+ "ang ular",
+ "angu lar",
+ "▁s aved",
+ "▁sa ved",
+ "▁save d",
+ "▁sav ed",
+ "▁ saved",
+ "▁un iform",
+ "▁ uniform",
+ "▁n é",
+ "▁ né",
+ "▁business es",
+ "His t",
+ "Hi st",
+ "H ist",
+ "▁а р",
+ "▁ ар",
+ "do main",
+ "dom ain",
+ "▁S i",
+ "▁ Si",
+ "ra ise",
+ "rais e",
+ "rai se",
+ "r aise",
+ "▁w arn",
+ "▁war n",
+ "▁wa rn",
+ "▁ warn",
+ "het ic",
+ "h etic",
+ "▁G ro",
+ "▁Gr o",
+ ")) .",
+ ") ).",
+ "} >",
+ "з е",
+ "▁Amaz on",
+ "▁Or gan",
+ "▁ Organ",
+ "▁L ake",
+ "▁La ke",
+ "▁ag reement",
+ "▁agree ment",
+ "▁agre ement",
+ "x a",
+ "▁p erman",
+ "▁per man",
+ "▁perm an",
+ "▁cont aining",
+ "▁contain ing",
+ "▁st range",
+ "▁str ange",
+ "▁strang e",
+ "ст і",
+ "с ті",
+ "▁st upid",
+ "▁spe aking",
+ "▁speak ing",
+ "▁Intern et",
+ "▁Inter net",
+ "pre fix",
+ "pref ix",
+ "p refix",
+ "es c",
+ "e sc",
+ "As sert",
+ "Ass ert",
+ "pro te",
+ "pr ote",
+ "prot e",
+ "p rote",
+ "▁m anner",
+ "▁man ner",
+ "▁S z",
+ "un te",
+ "unt e",
+ "u nte",
+ "io t",
+ "i ot",
+ "Pro file",
+ "ov en",
+ "ove n",
+ "o ven",
+ "▁for med",
+ "▁form ed",
+ "▁forme d",
+ "▁ formed",
+ "▁l it",
+ "▁li t",
+ "▁ lit",
+ "▁econom y",
+ "▁ec onomy",
+ "▁c z",
+ "▁ cz",
+ "wi d",
+ "w id",
+ "RE Q",
+ "R EQ",
+ "▁ch osen",
+ "▁cho sen",
+ "▁chose n",
+ "▁P rodu",
+ "▁Pro du",
+ "▁ Produ",
+ "os ter",
+ "ost er",
+ "o ster",
+ "st ances",
+ "stance s",
+ "stan ces",
+ "aw a",
+ "a wa",
+ "▁R en",
+ "▁Re n",
+ "▁conf irm",
+ "▁ confirm",
+ "▁Б о",
+ "▁b illion",
+ "▁bill ion",
+ "▁d éc",
+ "▁dé c",
+ "ý ch",
+ "▁ill ustr",
+ "TI ES",
+ "T IES",
+ "▁P ub",
+ "▁Pu b",
+ "▁ Pub",
+ "▁b an",
+ "▁ba n",
+ "▁ ban",
+ "ad ed",
+ "ade d",
+ "a ded",
+ "ah n",
+ "a hn",
+ "▁C ath",
+ "▁Cat h",
+ "▁Ca th",
+ "no number",
+ "non umber",
+ "▁wor st",
+ "▁М е",
+ "▁sugg ested",
+ "▁suggest ed",
+ "st ats",
+ "stat s",
+ "sta ts",
+ "▁c ant",
+ "▁can t",
+ "▁ca nt",
+ "▁al ign",
+ "▁ali gn",
+ "▁ align",
+ "kap pa",
+ "k appa",
+ "▁h en",
+ "▁he n",
+ "▁ hen",
+ "▁in iti",
+ "▁init i",
+ "'] )",
+ "' ])",
+ "B I",
+ "▁g arden",
+ "▁gar den",
+ "▁gard en",
+ "▁sec ure",
+ "▁secur e",
+ "▁ secure",
+ "▁\\ [",
+ "▁ \\[",
+ "hand ler",
+ "handle r",
+ "el li",
+ "ell i",
+ "e lli",
+ "ld ots",
+ "l dots",
+ "se cut",
+ "sec ut",
+ "s ecut",
+ "▁ext ended",
+ "▁extend ed",
+ "} -",
+ "an ie",
+ "ani e",
+ "a nie",
+ "▁F ind",
+ "▁Fin d",
+ "▁Fi nd",
+ "▁ Find",
+ "▁M useum",
+ "▁Muse um",
+ "▁C onne",
+ "▁Con ne",
+ "▁ Conne",
+ "y y",
+ "▁pass ion",
+ "ak ers",
+ "ake rs",
+ "aker s",
+ "a kers",
+ "ah r",
+ "a hr",
+ "olog ies",
+ "ologie s",
+ "▁equ ation",
+ "▁eq uation",
+ "▁ equation",
+ "▁occ asion",
+ "▁occas ion",
+ "Le t",
+ "L et",
+ "'] ['",
+ "'][ '",
+ "' ]['",
+ "Pr int",
+ "an es",
+ "ane s",
+ "a nes",
+ "ie nte",
+ "ient e",
+ "ien te",
+ "i ente",
+ "▁T oday",
+ "▁To day",
+ "▁Tod ay",
+ "LE CT",
+ "L ECT",
+ "▁A f",
+ "▁ Af",
+ ", ,",
+ "▁Т а",
+ "▁` ``",
+ "▁`` `",
+ "ev en",
+ "eve n",
+ "e ven",
+ "si n",
+ "s in",
+ "ur er",
+ "ure r",
+ "u rer",
+ "▁ °",
+ "ot imes",
+ "oti mes",
+ "o times",
+ "▁I O",
+ "▁ IO",
+ "▁po et",
+ "() ));",
+ "()) );",
+ "())) ;",
+ "( )));",
+ "▁ −",
+ "▁ad opt",
+ "ph ere",
+ "pher e",
+ "p here",
+ "# [",
+ "▁c entre",
+ "▁cent re",
+ "ov es",
+ "ove s",
+ "o ves",
+ "▁a ns",
+ "▁an s",
+ "▁ ans",
+ "d p",
+ "▁K ir",
+ "▁Ki r",
+ "▁applic able",
+ "f p",
+ "▁vis ual",
+ "▁ok ay",
+ "or o",
+ "o ro",
+ "▁opportun ities",
+ "Re pository",
+ "Rep ository",
+ "▁l l",
+ "▁ ll",
+ "▁R od",
+ "▁Ro d",
+ "▁s hel",
+ "▁sh el",
+ "▁she l",
+ "▁la unch",
+ "▁con ven",
+ "▁conv en",
+ "▁conve n",
+ "▁S pe",
+ "▁Sp e",
+ "▁ Spe",
+ "Am er",
+ "A mer",
+ "▁c ette",
+ "▁cet te",
+ "Con d",
+ "Co nd",
+ "C ond",
+ "de p",
+ "d ep",
+ "O wn",
+ "▁h ook",
+ "▁ho ok",
+ "▁ hook",
+ "▁d ict",
+ "▁di ct",
+ "▁dic t",
+ "▁ dict",
+ "▁Th ose",
+ "▁f ellow",
+ "▁fell ow",
+ "▁fel low",
+ "▁phil osoph",
+ "▁philos oph",
+ "vi n",
+ "v in",
+ "fer ences",
+ "ference s",
+ "ha v",
+ "h av",
+ "▁ad ding",
+ "▁add ing",
+ "▁ adding",
+ "ivers e",
+ "iver se",
+ "i verse",
+ "ga me",
+ "g ame",
+ "▁Bl ue",
+ "▁ Blue",
+ "▁c lin",
+ "▁cl in",
+ "not e",
+ "no te",
+ "n ote",
+ "▁R am",
+ "▁Ra m",
+ "ме р",
+ "м ер",
+ "co very",
+ "cover y",
+ "cov ery",
+ "c overy",
+ "ñ a",
+ "▁б и",
+ "▁ би",
+ "▁f ashion",
+ "▁b roke",
+ "▁br oke",
+ "▁bro ke",
+ "▁' \\",
+ "▁ '\\",
+ "▁re ader",
+ "▁read er",
+ "▁ reader",
+ "но е",
+ "но сти",
+ "ност и",
+ "▁pay ment",
+ "▁ payment",
+ "▁L ic",
+ "▁Li c",
+ "▁l ips",
+ "▁li ps",
+ "▁lip s",
+ "▁ac adem",
+ "▁M ot",
+ "▁Mo t",
+ "el ls",
+ "ell s",
+ "C HECK",
+ "▁р у",
+ "▁ ру",
+ "▁M S",
+ "▁ MS",
+ "Ed itor",
+ "Edit or",
+ "▁z one",
+ "▁zo ne",
+ "▁ zone",
+ "it ure",
+ "itu re",
+ "▁I T",
+ "▁ IT",
+ "run time",
+ "▁pro ceed",
+ "▁proc eed",
+ "ло в",
+ "л ов",
+ "▁M aria",
+ "▁Mar ia",
+ "▁Ma ria",
+ "ol ver",
+ "olve r",
+ "olv er",
+ "▁Th anks",
+ "▁Thank s",
+ "▁ Thanks",
+ "▁should n",
+ "▁J oh",
+ "▁Jo h",
+ "▁Mod el",
+ "▁Mo del",
+ "▁Mode l",
+ "▁ Model",
+ "▁S ov",
+ "▁So v",
+ "! '",
+ "D i",
+ "▁c ancer",
+ "▁can cer",
+ "Id ent",
+ "▁ex change",
+ "il ler",
+ "ill er",
+ "ille r",
+ "in f",
+ "i nf",
+ "LE N",
+ "L EN",
+ "() {",
+ "( ){",
+ "ag a",
+ "a ga",
+ "\"] ,",
+ "\" ],",
+ "u h",
+ "▁K en",
+ "▁Ke n",
+ "▁ph otos",
+ "▁phot os",
+ "▁photo s",
+ "▁t iny",
+ "▁ti ny",
+ "▁tin y",
+ "▁ tiny",
+ "▁g ent",
+ "▁gen t",
+ "▁ge nt",
+ "▁ gent",
+ "ü l",
+ "▁T ake",
+ "▁Ta ke",
+ "▁Tak e",
+ "▁ Take",
+ "id el",
+ "ide l",
+ "i del",
+ "ou ting",
+ "out ing",
+ "In ternal",
+ "Inter nal",
+ "Intern al",
+ "▁c ells",
+ "▁cell s",
+ "▁cel ls",
+ "ни м",
+ "н им",
+ "ha rd",
+ "har d",
+ "h ard",
+ "▁T own",
+ "▁To wn",
+ "▁Tow n",
+ "ob e",
+ "o be",
+ "pl ex",
+ "ple x",
+ "p lex",
+ "те р",
+ "т ер",
+ "to ns",
+ "ton s",
+ "t ons",
+ "▁conc entr",
+ "▁concent r",
+ "mo ck",
+ "m ock",
+ "v c",
+ "á z",
+ "▁Ch ampionship",
+ "▁Champion ship",
+ "▁Champions hip",
+ "▁б е",
+ "▁ бе",
+ "? ?",
+ "ér i",
+ "é ri",
+ "al y",
+ "a ly",
+ "▁ Ц",
+ "ier te",
+ "iert e",
+ "▁tot ally",
+ "▁total ly",
+ "▁A uf",
+ "▁Au f",
+ "▁our selves",
+ "▁S elf",
+ "▁Sel f",
+ "▁ Self",
+ "Form s",
+ "For ms",
+ "ight er",
+ "igh ter",
+ "▁is land",
+ "fm t",
+ "f mt",
+ "▁r c",
+ "▁ rc",
+ "▁t ells",
+ "▁tell s",
+ "▁tel ls",
+ "B B",
+ "di t",
+ "d it",
+ "▁vari ables",
+ "▁variable s",
+ "▁ variables",
+ "▁int ended",
+ "▁intend ed",
+ "iz ont",
+ "izon t",
+ "izo nt",
+ "▁pl ays",
+ "▁play s",
+ "da m",
+ "d am",
+ "se q",
+ "s eq",
+ "▁S up",
+ "▁Su p",
+ "▁ Sup",
+ "▁c ultural",
+ "▁cult ural",
+ "▁sc ream",
+ "__ ,",
+ "_ _,",
+ "ci pl",
+ "cip l",
+ "Time out",
+ "▁ ж",
+ "or te",
+ "ort e",
+ "▁repl aced",
+ "▁replace d",
+ "E M",
+ "▁ab andon",
+ "▁Spec ial",
+ "▁Spe cial",
+ "▁ Special",
+ "el len",
+ "ell en",
+ "elle n",
+ "▁B ru",
+ "▁Br u",
+ "ir med",
+ "irm ed",
+ "T e",
+ "ol t",
+ "o lt",
+ "j u",
+ "Arg ument",
+ "▁ne ut",
+ "▁neu t",
+ "▁ neut",
+ "sc ape",
+ "▁R ay",
+ "▁Ra y",
+ "▁ Ray",
+ "▁Pol it",
+ "▁Po lit",
+ "▁crow d",
+ "▁cro wd",
+ "▁Window s",
+ "▁Wind ows",
+ "▁ Windows",
+ "ie go",
+ "ieg o",
+ "i ego",
+ "▁e scape",
+ "▁esc ape",
+ "▁ escape",
+ "▁Ap ache",
+ "sy nc",
+ "syn c",
+ "s ync",
+ "eb en",
+ "e ben",
+ "if ies",
+ "ifi es",
+ "et her",
+ "eth er",
+ "ethe r",
+ "e ther",
+ "Met a",
+ "Me ta",
+ "M eta",
+ "▁big gest",
+ "Ga me",
+ "G ame",
+ "▁trans action",
+ "▁ transaction",
+ "En v",
+ "E nv",
+ "▁М о",
+ "▁pl enty",
+ "▁m el",
+ "▁me l",
+ "▁ mel",
+ "п ре",
+ "▁mot iv",
+ "▁о р",
+ "▁ ор",
+ "or gan",
+ "org an",
+ "▁m ock",
+ "▁mo ck",
+ "▁ mock",
+ "▁$ _",
+ "▁ $_",
+ "ен е",
+ "е не",
+ "▁N umber",
+ "▁Num ber",
+ "▁Nu mber",
+ "▁ Number",
+ "ck now",
+ "c know",
+ "▁Up date",
+ "▁ Update",
+ "ze ro",
+ "zer o",
+ "z ero",
+ "▁sur prise",
+ "▁surpr ise",
+ "ce an",
+ "pd f",
+ "p df",
+ "Gl obal",
+ "▁att end",
+ "▁f ond",
+ "▁fo nd",
+ "▁fon d",
+ "▁under stood",
+ "Na v",
+ "N av",
+ "▁M ic",
+ "▁Mi c",
+ "▁ Mic",
+ "= $",
+ "ok ing",
+ "oki ng",
+ "o king",
+ "▁Stad ium",
+ "Cl ose",
+ "▁compet ition",
+ "▁sold iers",
+ "▁soldier s",
+ "▁O P",
+ "▁ OP",
+ "ag ne",
+ "agn e",
+ "▁An ton",
+ "▁Ant on",
+ "Ma in",
+ "M ain",
+ "á k",
+ "▁# [",
+ "▁ #[",
+ "▁Com mit",
+ "▁Comm it",
+ "▁ Commit",
+ "py x",
+ "▁e ast",
+ "▁eas t",
+ "▁ east",
+ "▁Or der",
+ "▁Ord er",
+ "▁ Order",
+ "F loat",
+ "▁accept ed",
+ "▁mon itor",
+ "▁ monitor",
+ "▁p ad",
+ "▁pa d",
+ "▁ pad",
+ "on ic",
+ "oni c",
+ "o nic",
+ "▁p ushed",
+ "▁push ed",
+ "▁re place",
+ "▁rep lace",
+ "▁repl ace",
+ "▁ replace",
+ "CR E",
+ "C RE",
+ "▁r ide",
+ "▁ri de",
+ "▁rid e",
+ "▁ ride",
+ "fo und",
+ "f ound",
+ "= %",
+ "во й",
+ "▁mat ches",
+ "▁match es",
+ "▁ matches",
+ "▁L ie",
+ "▁Li e",
+ "▁exper iences",
+ "▁experience s",
+ "▁experi ences",
+ "Po ol",
+ "P ool",
+ "up s",
+ "u ps",
+ "A V",
+ "▁ex istence",
+ "▁exist ence",
+ "▁t hin",
+ "▁th in",
+ "▁m agn",
+ "▁mag n",
+ "▁ma gn",
+ "CO MP",
+ "COM P",
+ "ho me",
+ "hom e",
+ "h ome",
+ "▁n i",
+ "▁ ni",
+ "▁wur den",
+ "▁wurde n",
+ "ла в",
+ "▁te eth",
+ "▁S tan",
+ "▁St an",
+ "▁Sta n",
+ "ap pro",
+ "app ro",
+ "an ny",
+ "ann y",
+ "if ts",
+ "ift s",
+ "▁un known",
+ "▁ unknown",
+ "▁h omes",
+ "▁home s",
+ "▁hom es",
+ "▁ho mes",
+ "▁ent ity",
+ "▁ entity",
+ "ci e",
+ "c ie",
+ "ле ние",
+ "ia r",
+ "i ar",
+ "▁compl iance",
+ "▁focus ed",
+ "uz z",
+ "u zz",
+ "=\\ \"",
+ "= \\\"",
+ "com ponents",
+ "component s",
+ "Att r",
+ "At tr",
+ "all ery",
+ "alle ry",
+ "aller y",
+ "▁ident ify",
+ "O k",
+ "pi e",
+ "p ie",
+ "▁St ill",
+ "▁off ering",
+ "▁offer ing",
+ "▁bu sy",
+ "▁bus y",
+ "ct l",
+ "c tl",
+ "it ors",
+ "itor s",
+ "ito rs",
+ "▁concern ed",
+ "▁concer ned",
+ "▁b rown",
+ "▁br own",
+ "▁bro wn",
+ "▁brow n",
+ "cl k",
+ "Se lected",
+ "Select ed",
+ "▁B lock",
+ "▁Bl ock",
+ "▁Blo ck",
+ "▁ Block",
+ "▁e gy",
+ "▁eg y",
+ "▁ egy",
+ "ic ing",
+ "ici ng",
+ "i cing",
+ "▁U RL",
+ "▁ URL",
+ "▁t opic",
+ "▁to pic",
+ "▁top ic",
+ "▁ topic",
+ "▁Pro duct",
+ "▁Produ ct",
+ "▁ Product",
+ "▁ч и",
+ "▁ чи",
+ "▁t rial",
+ "▁tr ial",
+ "▁tri al",
+ "▁week end",
+ "l u",
+ "▁I V",
+ "▁ IV",
+ "▁E gy",
+ "▁Eg y",
+ "x C",
+ "▁n ove",
+ "▁no ve",
+ "▁nov e",
+ "▁l ett",
+ "▁le tt",
+ "▁let t",
+ "▁ lett",
+ "en ne",
+ "enn e",
+ "() ).",
+ "()) .",
+ "( )).",
+ ".* *",
+ ". **",
+ "▁p romise",
+ "▁prom ise",
+ "el ection",
+ "ele ction",
+ "elect ion",
+ "e lection",
+ "Aut h",
+ "A uth",
+ "r v",
+ "ri l",
+ "r il",
+ "▁con duct",
+ "▁cond uct",
+ "▁condu ct",
+ "▁ conduct",
+ "▁main tain",
+ "▁maint ain",
+ "▁bo at",
+ "▁ boat",
+ "▁op posite",
+ "▁oppos ite",
+ "sp in",
+ "spi n",
+ "s pin",
+ "web pack",
+ "an ta",
+ "ant a",
+ "▁o rient",
+ "▁or ient",
+ "▁ orient",
+ "▁s uc",
+ "▁su c",
+ "▁ex ercise",
+ "▁exerc ise",
+ "▁eff icient",
+ "▁ efficient",
+ "▁trad ition",
+ "▁z w",
+ "▁ zw",
+ "▁S ud",
+ "▁Su d",
+ "go ing",
+ "▁P ier",
+ "▁Pi er",
+ "in v",
+ "i nv",
+ "ip es",
+ "ipe s",
+ "i pes",
+ "ensure math",
+ "▁con ver",
+ "▁conv er",
+ "▁conve r",
+ "cre en",
+ "cr een",
+ "c reen",
+ "▁t error",
+ "▁ter ror",
+ "▁terr or",
+ "▁D ou",
+ "▁Do u",
+ "▁in valid",
+ "▁ invalid",
+ "ce ived",
+ "ceive d",
+ "▁A rab",
+ "▁Ar ab",
+ "▁w ire",
+ "▁wir e",
+ "▁ wire",
+ "ap plication",
+ "sh ift",
+ "Gener ic",
+ "▁P lan",
+ "▁Pl an",
+ "▁ Plan",
+ "▁W all",
+ "▁Wal l",
+ "▁Wa ll",
+ "▁ Wall",
+ "▁direct ory",
+ "▁director y",
+ "▁ directory",
+ "▁e gg",
+ "▁eg g",
+ "▁we alth",
+ "▁ wealth",
+ "ran dom",
+ "rand om",
+ "r andom",
+ "att ribute",
+ "▁h ide",
+ "▁hi de",
+ "▁hid e",
+ "▁ hide",
+ "Se rial",
+ "Ser ial",
+ "S erial",
+ "ca m",
+ "c am",
+ "▁it al",
+ "▁i tal",
+ "▁ ital",
+ "▁L ine",
+ "▁Lin e",
+ "▁Li ne",
+ "▁ Line",
+ "▁C HECK",
+ "▁ CHECK",
+ "ploy ment",
+ "▁mass ive",
+ "▁ex tract",
+ "▁ext ract",
+ "▁extra ct",
+ "▁extr act",
+ "▁ extract",
+ "ch ain",
+ "cha in",
+ "Res t",
+ "Re st",
+ "R est",
+ "▁L as",
+ "▁La s",
+ "▁b ear",
+ "▁be ar",
+ "▁ bear",
+ "▁l inks",
+ "▁link s",
+ "▁lin ks",
+ "▁ links",
+ "▁new sp",
+ "▁news p",
+ "▁F C",
+ "▁ FC",
+ "Car d",
+ "C ard",
+ "ak s",
+ "a ks",
+ "▁v isible",
+ "▁vis ible",
+ "▁ visible",
+ "▁M arc",
+ "▁Mar c",
+ "▁Ma rc",
+ "▁B oston",
+ "▁Bo ston",
+ "▁Bos ton",
+ "▁res erved",
+ "▁reserv ed",
+ "▁reserve d",
+ "▁ro of",
+ "lic enses",
+ "license s",
+ "d c",
+ "▁In formation",
+ "▁ Information",
+ "▁w itness",
+ "S k",
+ "*) ,",
+ "* ),",
+ "Sc ope",
+ "S cope",
+ "'] ;",
+ "' ];",
+ "▁M ir",
+ "▁Mi r",
+ "▁ Mir",
+ "ud ing",
+ "udi ng",
+ "u ding",
+ "▁t rend",
+ "▁tr end",
+ "▁tre nd",
+ "▁tren d",
+ "re p",
+ "r ep",
+ "▁mus ical",
+ "▁music al",
+ "▁ne ither",
+ "▁nei ther",
+ "▁C reat",
+ "▁Cre at",
+ "▁ Creat",
+ "▁pos itions",
+ "▁position s",
+ "▁posit ions",
+ "L C",
+ "rid ge",
+ "r idge",
+ "▁offic ers",
+ "▁office rs",
+ "▁officer s",
+ "▁vi olence",
+ "▁viol ence",
+ "▁T em",
+ "▁Te m",
+ "▁S us",
+ "▁Su s",
+ "▁W ay",
+ "▁Wa y",
+ "Af ter",
+ "A fter",
+ "ac ket",
+ "ack et",
+ "▁S ou",
+ "▁So u",
+ "ac er",
+ "ace r",
+ "a cer",
+ "| |",
+ "▁re mark",
+ "▁r emark",
+ "▁rem ark",
+ "▁ remark",
+ "wa ter",
+ "w ater",
+ "n ě",
+ "▁С а",
+ "▁s ed",
+ "▁se d",
+ "▁ sed",
+ "E ach",
+ "▁phot ograph",
+ "▁photo graph",
+ "▁let ters",
+ "▁letter s",
+ "▁lett ers",
+ "▁in vent",
+ "▁inv ent",
+ "▁M as",
+ "▁Ma s",
+ "▁s ongs",
+ "▁son gs",
+ "▁song s",
+ "ó l",
+ "ki nd",
+ "kin d",
+ "k ind",
+ "▁N on",
+ "▁No n",
+ "▁ Non",
+ "▁d ust",
+ "▁du st",
+ "** :",
+ "* *:",
+ "nab la",
+ ".\" ,",
+ ". \",",
+ "Loc k",
+ "Lo ck",
+ "L ock",
+ "▁Д о",
+ "▁cl uster",
+ "▁ cluster",
+ "lo ss",
+ "los s",
+ "l oss",
+ "▁ASS ERT",
+ "▁ ASSERT",
+ "fa ll",
+ "f all",
+ "▁re ject",
+ "▁ reject",
+ "▁Sp ring",
+ "▁Spr ing",
+ "▁ Spring",
+ "▁wed ding",
+ "▁g rav",
+ "▁gr av",
+ "▁gra v",
+ "▁ grav",
+ "ress ion",
+ "r ession",
+ "li mit",
+ "lim it",
+ "l imit",
+ "RE S",
+ "R ES",
+ "] }",
+ "▁l isted",
+ "▁li sted",
+ "▁list ed",
+ "▁ listed",
+ "▁T ele",
+ "▁Te le",
+ "▁Tel e",
+ "▁ Tele",
+ "hl ine",
+ "h line",
+ "▁ch ief",
+ "▁chi ef",
+ "ME M",
+ "M EM",
+ "да р",
+ "д ар",
+ "▁exp ensive",
+ "tr ace",
+ "tra ce",
+ "▁R og",
+ "▁Ro g",
+ "▁C oll",
+ "▁Col l",
+ "▁Co ll",
+ "▁ Coll",
+ "▁Aut hor",
+ "▁Auth or",
+ "▁ Author",
+ "▁B oard",
+ "▁Bo ard",
+ "▁ Board",
+ "▁C apt",
+ "▁Cap t",
+ "▁Ca pt",
+ "▁ Capt",
+ "TE XT",
+ "T EXT",
+ "▁re con",
+ "▁rec on",
+ "es ta",
+ "est a",
+ "e sta",
+ "▁proper ly",
+ "▁& \\",
+ "▁ &\\",
+ "le ton",
+ "let on",
+ "l eton",
+ "ik er",
+ "ike r",
+ "i ker",
+ "G u",
+ "▁K om",
+ "▁Ko m",
+ "oc o",
+ "o co",
+ "▁any more",
+ "▁t aste",
+ "▁ta ste",
+ "▁tast e",
+ "▁S anta",
+ "▁San ta",
+ "▁Sant a",
+ "ge x",
+ "g ex",
+ "▁Se cret",
+ "▁Sec ret",
+ "▁ Secret",
+ "▁tal ent",
+ "▁tale nt",
+ "▁mom ents",
+ "▁moment s",
+ "▁mo ments",
+ "▁B a",
+ "▁ex tr",
+ "▁ext r",
+ "▁ extr",
+ "▁Com mission",
+ "▁Comm ission",
+ "▁mod ify",
+ "▁Fig ure",
+ "▁ Figure",
+ "▁d omin",
+ "▁do min",
+ "▁dom in",
+ "▁ domin",
+ "▁p lot",
+ "▁pl ot",
+ "▁ plot",
+ "en ger",
+ "eng er",
+ "enge r",
+ "ut ch",
+ "▁c ities",
+ "▁cit ies",
+ "▁ci ties",
+ "▁n ut",
+ "▁nu t",
+ "▁ nut",
+ "pro file",
+ "prof ile",
+ "▁S tat",
+ "▁St at",
+ "▁Sta t",
+ "▁ Stat",
+ "▁n odes",
+ "▁no des",
+ "▁node s",
+ "▁nod es",
+ "▁ nodes",
+ "▁n s",
+ "▁ ns",
+ "ess ages",
+ "essage s",
+ "essa ges",
+ "im pl",
+ "imp l",
+ "ic ker",
+ "ick er",
+ "i cker",
+ "▁ex amples",
+ "▁example s",
+ "▁exam ples",
+ "ab eth",
+ "abe th",
+ "abet h",
+ "▁st ated",
+ "▁stat ed",
+ "▁state d",
+ "▁sta ted",
+ "fi re",
+ "f ire",
+ "bu l",
+ "b ul",
+ "▁danger ous",
+ "▁P ay",
+ "▁Pa y",
+ "▁ Pay",
+ "▁G re",
+ "▁Gr e",
+ "▁ Gre",
+ "▁Mon day",
+ "▁Mond ay",
+ "es ome",
+ "eso me",
+ "e some",
+ "ig an",
+ "iga n",
+ "i gan",
+ "ru nd",
+ "run d",
+ "r und",
+ "pr ise",
+ "p rise",
+ "fa il",
+ "f ail",
+ "▁N ever",
+ "▁Ne ver",
+ "▁Nev er",
+ "▁ Never",
+ "A v",
+ "▁line ar",
+ "▁lin ear",
+ "▁ linear",
+ "▁u l",
+ "▁ ul",
+ "WA R",
+ "W AR",
+ "ре н",
+ "р ен",
+ "▁A T",
+ "▁ AT",
+ "▁d op",
+ "▁do p",
+ "▁n ou",
+ "▁no u",
+ "Des t",
+ "De st",
+ "D est",
+ "▁claim s",
+ "en da",
+ "end a",
+ "▁c razy",
+ "▁cr azy",
+ "ge l",
+ "g el",
+ "og gle",
+ "ogg le",
+ "▁rep resentation",
+ "▁represent ation",
+ "in en",
+ "ine n",
+ "i nen",
+ "▁altern ative",
+ "▁alter native",
+ "D M",
+ "AB ILITY",
+ "face s",
+ "fa ces",
+ "fac es",
+ "f aces",
+ "▁do ors",
+ "▁door s",
+ "▁ doors",
+ "at iv",
+ "ati v",
+ "Lo ok",
+ "L ook",
+ "▁J SON",
+ "▁JS ON",
+ "▁ JSON",
+ "▁appe arance",
+ "▁appear ance",
+ "б ря",
+ "S QL",
+ "▁sil ence",
+ "ud o",
+ "u do",
+ "▁Direct or",
+ "▁Dire ctor",
+ "▁Dir ector",
+ "State ment",
+ "Stat ement",
+ "se lected",
+ "select ed",
+ "hi gh",
+ "h igh",
+ "pr ime",
+ "prim e",
+ "▁ign ore",
+ "▁ignor e",
+ "▁ ignore",
+ "▁col ors",
+ "▁color s",
+ "▁ colors",
+ "us hing",
+ "ush ing",
+ "▁v irt",
+ "▁vi rt",
+ "▁vir t",
+ "▁ virt",
+ "man ager",
+ "▁rem ote",
+ "▁remot e",
+ "▁ remote",
+ "ł o",
+ "sm all",
+ "▁cr ime",
+ "▁crim e",
+ "▁cri me",
+ "r b",
+ "▁c reation",
+ "▁cre ation",
+ "▁creat ion",
+ "▁f light",
+ "▁fl ight",
+ "▁S ign",
+ "▁Si gn",
+ "▁Sig n",
+ "▁ Sign",
+ "IL E",
+ "I LE",
+ "▁D O",
+ "▁ DO",
+ "com ment",
+ "comm ent",
+ "▁C ost",
+ "▁Co st",
+ "▁Cos t",
+ "▁ Cost",
+ "._ _",
+ ". __",
+ "▁C op",
+ "▁Co p",
+ "▁ Cop",
+ "▁v om",
+ "▁vo m",
+ "▁Sc ience",
+ "▁Sci ence",
+ "ле ния",
+ "oo p",
+ "o op",
+ "inter face",
+ "▁WARRAN TIES",
+ "▁P age",
+ "▁Pa ge",
+ "▁ Page",
+ "** ****",
+ "**** **",
+ "*** ***",
+ "ско м",
+ "с ком",
+ "TR UE",
+ "▁re peated",
+ "▁repe ated",
+ "▁repeat ed",
+ "▁е го",
+ "ш о",
+ "▁r oz",
+ "▁ro z",
+ "▁ roz",
+ "P e",
+ "▁IS BN",
+ "ir ts",
+ "irt s",
+ "pos es",
+ "po ses",
+ "pose s",
+ "p oses",
+ "}) $",
+ "} )$",
+ "▁ І",
+ "child ren",
+ "ble s",
+ "bl es",
+ "b les",
+ "EC T",
+ "E CT",
+ "▁i z",
+ "▁ iz",
+ "▁b uilder",
+ "▁build er",
+ "▁ builder",
+ "▁M edia",
+ "▁Med ia",
+ "▁ Media",
+ "ia t",
+ "i at",
+ "▁contr ast",
+ "▁contra st",
+ "” ,",
+ "▁L ink",
+ "▁Lin k",
+ "▁ Link",
+ "▁Educ ation",
+ "▁j oint",
+ "▁join t",
+ "▁jo int",
+ "▁ joint",
+ "▁ex ternal",
+ "▁extern al",
+ "▁ external",
+ "▁ро з",
+ "▁b its",
+ "▁bit s",
+ "▁bi ts",
+ "▁ bits",
+ "FO RM",
+ "FOR M",
+ "F ORM",
+ "er man",
+ "erm an",
+ "w p",
+ "▁M ike",
+ "▁Mi ke",
+ "▁Mik e",
+ "▁M aster",
+ "▁Ma ster",
+ "▁Mas ter",
+ "▁ Master",
+ "▁sen ior",
+ "▁N av",
+ "▁Na v",
+ "▁ Nav",
+ "▁record ed",
+ "el ing",
+ "eli ng",
+ "elin g",
+ "e ling",
+ "es h",
+ "e sh",
+ "f x",
+ "ка н",
+ "к ан",
+ "▁t all",
+ "▁tal l",
+ "▁ta ll",
+ "▁John son",
+ "▁s ono",
+ "▁so no",
+ "▁son o",
+ "▁an che",
+ "▁anc he",
+ "▁anch e",
+ "▁ anche",
+ "ic ken",
+ "ick en",
+ "i cken",
+ "lo op",
+ "l oop",
+ "ici ency",
+ "empor ary",
+ "▁D oes",
+ "▁Do es",
+ "▁ Does",
+ "▁re lation",
+ "▁rel ation",
+ "▁ relation",
+ "м ы",
+ "wa s",
+ "w as",
+ "lo w",
+ "l ow",
+ "ich te",
+ "icht e",
+ "i chte",
+ "▁J ones",
+ "▁Jo nes",
+ "▁Jon es",
+ "▁bed room",
+ "DI S",
+ "D IS",
+ "▁mag net",
+ "▁magn et",
+ "▁Eng ine",
+ "▁ Engine",
+ "▁feel ings",
+ "▁feeling s",
+ "▁fee lings",
+ "G C",
+ "▁t orn",
+ "▁to rn",
+ "▁tor n",
+ "▁relationship s",
+ "▁relation ships",
+ "▁Р е",
+ "▁p roud",
+ "▁pro ud",
+ "▁pr oud",
+ "▁t we",
+ "▁tw e",
+ "ov al",
+ "ova l",
+ "o val",
+ "▁w aste",
+ "▁was te",
+ "▁wa ste",
+ "▁red uced",
+ "▁redu ced",
+ "▁reduce d",
+ "il ton",
+ "ilt on",
+ "B P",
+ "▁for got",
+ "▁forg ot",
+ "▁bod ies",
+ "▁H aw",
+ "▁Ha w",
+ "la g",
+ "l ag",
+ "▁w ww",
+ "▁ www",
+ "do or",
+ "d oor",
+ "▁s ufficient",
+ "▁suff icient",
+ "▁doll ars",
+ "▁dollar s",
+ "Le n",
+ "L en",
+ "▁talk ed",
+ "▁tal ked",
+ "▁b ond",
+ "▁bo nd",
+ "▁bon d",
+ "▁B or",
+ "▁Bo r",
+ "}} {",
+ "} }{",
+ "ro d",
+ "r od",
+ "Pass word",
+ "qu are",
+ "▁l ights",
+ "▁light s",
+ "▁ lights",
+ "er en",
+ "ere n",
+ "e ren",
+ "▁th irty",
+ "N C",
+ "▁T ODO",
+ "▁TO DO",
+ "▁res pond",
+ "▁respon d",
+ "▁resp ond",
+ "▁ respond",
+ "ки х",
+ "dir ect",
+ "di rect",
+ "dire ct",
+ "d irect",
+ "a ção",
+ "▁he av",
+ "Med ia",
+ "M edia",
+ "ex it",
+ "e xit",
+ "L icense",
+ "` .",
+ "▁m ixed",
+ "▁mix ed",
+ "▁d esk",
+ "▁de sk",
+ "▁des k",
+ "▁te aching",
+ "▁teach ing",
+ "▁tea ching",
+ "▁m aj",
+ "▁ma j",
+ "▁n erv",
+ "▁ne rv",
+ "▁ner v",
+ "in ations",
+ "ination s",
+ "type of",
+ "▁co ast",
+ "▁ж е",
+ "▁ же",
+ "▁be side",
+ "▁bes ide",
+ "um my",
+ "umm y",
+ "Do c",
+ "D oc",
+ "▁sche dule",
+ "▁schedul e",
+ "▁sched ule",
+ "▁ schedule",
+ "▁re cover",
+ "▁rec over",
+ "▁Fur ther",
+ "▁ste el",
+ "bo ot",
+ "b oot",
+ "▁Per haps",
+ "▁с ъ",
+ "▁O s",
+ "▁ Os",
+ "ri ck",
+ "ric k",
+ "r ick",
+ "▁В и",
+ "Supp ort",
+ "Sup port",
+ "S upport",
+ "▁( _",
+ "▁ (_",
+ "ni l",
+ "n il",
+ "pi s",
+ "p is",
+ "x pected",
+ "▁process ing",
+ "▁proces sing",
+ "▁ processing",
+ "Bu ild",
+ "B uild",
+ "ar ian",
+ "ari an",
+ "aria n",
+ "a rian",
+ "▁i con",
+ "▁ic on",
+ "▁ icon",
+ "▁C A",
+ "▁ CA",
+ "wi ck",
+ "w ick",
+ "= (",
+ "▁al gorithm",
+ "▁ algorithm",
+ "▁You ng",
+ "▁Man agement",
+ "▁ Management",
+ "▁anc ient",
+ "▁anci ent",
+ "но сть",
+ "ност ь",
+ "ot i",
+ "o ti",
+ "▁comb ination",
+ "wor ld",
+ "w orld",
+ "n n",
+ "▁d ram",
+ "▁dr am",
+ "en abled",
+ "ena bled",
+ "enable d",
+ "A c",
+ "C CESS",
+ "ar ation",
+ "▁bl ocks",
+ "▁block s",
+ "▁blo cks",
+ "▁ blocks",
+ "▁Ang eles",
+ "▁Angel es",
+ "▁Q ual",
+ "▁Qu al",
+ "▁ Qual",
+ "▁suc ceed",
+ "▁succ eed",
+ "net work",
+ "▁ob lig",
+ "spring framework",
+ "▁T re",
+ "▁Tr e",
+ "ok es",
+ "oke s",
+ "o kes",
+ "mu n",
+ "m un",
+ "▁Net work",
+ "▁ Network",
+ "De l",
+ "D el",
+ "▁e state",
+ "▁est ate",
+ "▁esta te",
+ "▁l iqu",
+ "▁li qu",
+ "▁p ob",
+ "▁po b",
+ "▁d ad",
+ "▁da d",
+ "▁dist inct",
+ "▁T it",
+ "▁Ti t",
+ "▁L ear",
+ "▁Le ar",
+ "fer red",
+ "and roid",
+ "andro id",
+ "▁sub sequ",
+ "▁subs equ",
+ "▁Flor ida",
+ "sub set",
+ "▁whis per",
+ "Vo l",
+ "V ol",
+ "ul ous",
+ "ulo us",
+ "▁c rew",
+ "▁cre w",
+ "▁cr ew",
+ "▁l ug",
+ "▁lu g",
+ "pi d",
+ "p id",
+ "oc ity",
+ "oci ty",
+ "o city",
+ "sk b",
+ "s kb",
+ "▁t ea",
+ "▁te a",
+ "у н",
+ "▁hon or",
+ "▁ho nor",
+ "▁I ns",
+ "▁In s",
+ "▁ Ins",
+ "▁g ew",
+ "▁ge w",
+ "▁ gew",
+ "Det ails",
+ "Detail s",
+ "ene ath",
+ "e neath",
+ "at ar",
+ "ata r",
+ "a tar",
+ "▁_ {",
+ "▁ _{",
+ "am en",
+ "ame n",
+ "a men",
+ "▁set up",
+ "▁ setup",
+ "Trans action",
+ "▁bl ank",
+ "▁ blank",
+ "Fail ed",
+ "F ailed",
+ "jo b",
+ "j ob",
+ "▁p ret",
+ "▁pre t",
+ "▁pr et",
+ "▁ pret",
+ "ß e",
+ "lo or",
+ "l oor",
+ "ř í",
+ "nc ia",
+ "n cia",
+ "▁any where",
+ "▁L ight",
+ "▁Li ght",
+ "▁ Light",
+ "▁A k",
+ "B D",
+ "▁exc ited",
+ "▁excit ed",
+ "ag ers",
+ "age rs",
+ "ager s",
+ "a gers",
+ "▁w arning",
+ "▁war ning",
+ "▁warn ing",
+ "▁ warning",
+ "▁process es",
+ "▁proces ses",
+ "h u",
+ "▁y outh",
+ "▁you th",
+ "▁yo uth",
+ "▁d ogs",
+ "▁do gs",
+ "▁dog s",
+ "▁o ct",
+ "▁oc t",
+ "▁ oct",
+ "▁n ine",
+ "▁ni ne",
+ "▁nin e",
+ "Write r",
+ "Wr iter",
+ "Writ er",
+ "W riter",
+ "gr id",
+ "g rid",
+ "▁import ance",
+ "est ic",
+ "▁care fully",
+ "▁careful ly",
+ "ma ster",
+ "mas ter",
+ "m aster",
+ "▁dec isions",
+ "▁decision s",
+ "▁decis ions",
+ "▁p in",
+ "▁pi n",
+ "▁ pin",
+ "▁cr ack",
+ "TE ST",
+ "TES T",
+ "T EST",
+ "▁L ocal",
+ "▁Loc al",
+ "▁Lo cal",
+ "▁ Local",
+ "▁R ight",
+ "▁ Right",
+ "▁v ast",
+ "▁va st",
+ "▁vas t",
+ "▁f aster",
+ "▁fa ster",
+ "▁fast er",
+ "▁inst itut",
+ "▁ann ual",
+ "LA N",
+ "L AN",
+ "▁e pisode",
+ "▁epis ode",
+ "▁X V",
+ "▁del ivery",
+ "▁deliver y",
+ "t l",
+ "F P",
+ "ci rc",
+ "cir c",
+ "▁typ ically",
+ "▁typical ly",
+ "ig o",
+ "i go",
+ "▁int el",
+ "▁inte l",
+ "▁ intel",
+ "na t",
+ "n at",
+ "x b",
+ "ст ро",
+ "с тро",
+ ") -",
+ "▁B al",
+ "▁Ba l",
+ "▁ Bal",
+ "▁J os",
+ "▁Jo s",
+ "▁g onna",
+ "▁R est",
+ "▁Re st",
+ "▁Res t",
+ "▁ Rest",
+ "jo r",
+ "j or",
+ "on ia",
+ "oni a",
+ "o nia",
+ "or ship",
+ "ors hip",
+ "ov ery",
+ "ove ry",
+ "over y",
+ "o very",
+ "LI NE",
+ "LIN E",
+ "L INE",
+ "] :",
+ "Que ue",
+ "▁com pare",
+ "▁comp are",
+ "▁compar e",
+ "▁ compare",
+ "▁ap artment",
+ "▁apart ment",
+ "▁r ul",
+ "▁ru l",
+ "D r",
+ "gen cy",
+ "g ency",
+ "▁ob viously",
+ "▁obvious ly",
+ "zi e",
+ "z ie",
+ "yc l",
+ "y cl",
+ "fort unately",
+ "fortun ately",
+ "fortunate ly",
+ "▁ste pped",
+ "▁step ped",
+ "▁S eg",
+ "▁Se g",
+ "▁ Seg",
+ "▁Wh ich",
+ "▁ Which",
+ "▁P C",
+ "▁ PC",
+ "▁a st",
+ "▁as t",
+ "▁ ast",
+ "end or",
+ "endo r",
+ "▁per mission",
+ "▁perm ission",
+ "▁ permission",
+ "CO L",
+ "C OL",
+ "▁T EST",
+ "▁TE ST",
+ "▁ TEST",
+ "P ay",
+ "ère s",
+ "è res",
+ "▁stud ied",
+ "▁accom pl",
+ "▁accomp l",
+ "ro le",
+ "rol e",
+ "r ole",
+ "Wh ere",
+ "Whe re",
+ "W here",
+ "proto buf",
+ "met adata",
+ "meta data",
+ "Jo b",
+ "J ob",
+ "▁F our",
+ "▁Fou r",
+ "▁Fo ur",
+ "pl ements",
+ "ple ments",
+ "plement s",
+ "dis able",
+ "▁l oud",
+ "▁lo ud",
+ "▁lou d",
+ "▁happ ening",
+ "▁happen ing",
+ "▁U sing",
+ "▁Us ing",
+ "▁ Using",
+ "ro g",
+ "r og",
+ "▁depend s",
+ "▁dep ends",
+ "í m",
+ "' \\",
+ "▁t aught",
+ "sh ared",
+ "sha red",
+ "share d",
+ "▁att ributes",
+ "▁attribute s",
+ "▁attribut es",
+ "▁ attributes",
+ "▁A ction",
+ "▁Act ion",
+ "▁ Action",
+ "▁d ess",
+ "▁de ss",
+ "▁des s",
+ "▁ dess",
+ "▁h ouses",
+ "▁house s",
+ "▁hous es",
+ "▁ho uses",
+ "▁re set",
+ "▁res et",
+ "▁ reset",
+ "▁b ien",
+ "▁bi en",
+ "▁ex plicit",
+ "▁expl icit",
+ "LO W",
+ "-> _",
+ "▁P M",
+ "▁ PM",
+ "C ategory",
+ "oi ce",
+ "o ice",
+ "in to",
+ "int o",
+ "▁m ail",
+ "▁ma il",
+ "▁mai l",
+ "▁ mail",
+ "▁author ity",
+ "▁un able",
+ "▁una ble",
+ "file name",
+ "fil ename",
+ "é k",
+ "ле й",
+ "л ей",
+ "▁s ector",
+ "▁se ctor",
+ "▁sec tor",
+ "▁sect or",
+ "ap point",
+ "app oint",
+ "▁h ang",
+ "▁ha ng",
+ "▁han g",
+ "▁ hang",
+ "▁c el",
+ "▁ce l",
+ "▁ cel",
+ "rel ated",
+ "it ate",
+ "ita te",
+ "itat e",
+ "▁' <",
+ "am ber",
+ "amb er",
+ "a mber",
+ "▁c heap",
+ "▁che ap",
+ "▁en abled",
+ "▁enable d",
+ "▁ enabled",
+ "▁di vision",
+ "▁div ision",
+ "▁divis ion",
+ "An y",
+ "A ny",
+ "▁h ier",
+ "▁hi er",
+ "▁H ead",
+ "▁He ad",
+ "▁ Head",
+ "nt ax",
+ "n tax",
+ "ud a",
+ "u da",
+ "▁lim itations",
+ "▁limit ations",
+ "▁limitation s",
+ "▁st udio",
+ "▁stud io",
+ "med ia",
+ "medi a",
+ "m edia",
+ "▁cir cle",
+ "▁circ le",
+ "▁ circle",
+ "но ва",
+ "нов а",
+ "▁l aug",
+ "▁la ug",
+ "ac ts",
+ "act s",
+ "▁В о",
+ "ó d",
+ "pl ed",
+ "ple d",
+ "p led",
+ "LO C",
+ "L OC",
+ "Ex pr",
+ "Exp r",
+ "> :",
+ "▁pr és",
+ "▁pré s",
+ "▁ prés",
+ "▁laugh ed",
+ "▁laug hed",
+ "▁Th ree",
+ "▁ Three",
+ "л ы",
+ "▁en ds",
+ "▁end s",
+ "▁ ends",
+ "▁fund ament",
+ "▁in her",
+ "▁ inher",
+ "▁l iv",
+ "▁li v",
+ "▁ liv",
+ "bi d",
+ "b id",
+ "▁respons ibility",
+ "▁check ed",
+ "▁ checked",
+ "▁P ac",
+ "▁Pa c",
+ "▁f ault",
+ "▁fa ult",
+ "▁y ellow",
+ "▁s alt",
+ "▁sa lt",
+ "▁sal t",
+ "▁Franc isco",
+ "▁Francis co",
+ "▁ ^",
+ "▁O N",
+ "▁ ON",
+ "▁beaut y",
+ "y g",
+ "▁A ff",
+ "▁Af f",
+ "▁ Aff",
+ "▁E q",
+ "▁ Eq",
+ "▁mag ic",
+ "▁hand ler",
+ "▁handle r",
+ "▁ handler",
+ "x E",
+ "▁numer ous",
+ "▁numero us",
+ "▁h ole",
+ "▁hol e",
+ "▁ho le",
+ "▁ hole",
+ "▁ro oms",
+ "▁room s",
+ "▁ rooms",
+ "cc ión",
+ "cció n",
+ "c ción",
+ "▁A rm",
+ "▁Ar m",
+ "▁ Arm",
+ "per son",
+ "pers on",
+ "p erson",
+ "▁build ings",
+ "▁building s",
+ "▁p late",
+ "▁pl ate",
+ "▁plat e",
+ "ble d",
+ "bl ed",
+ "b led",
+ "er rors",
+ "err ors",
+ "error s",
+ "▁A gain",
+ "▁Ag ain",
+ "▁Def ault",
+ "▁ Default",
+ "▁H ard",
+ "▁Har d",
+ "▁Ha rd",
+ "▁ Hard",
+ "t ó",
+ "hu s",
+ "h us",
+ "▁dim ension",
+ "ial e",
+ "ia le",
+ "i ale",
+ "▁M ult",
+ "▁Mu lt",
+ "▁Mul t",
+ "▁ Mult",
+ "▁Govern ment",
+ "Fun c",
+ "F unc",
+ "▁b low",
+ "▁bl ow",
+ "▁blo w",
+ "▁re ct",
+ "▁r ect",
+ "▁rec t",
+ "▁ rect",
+ "er ra",
+ "err a",
+ "conne ction",
+ "connect ion",
+ "conn ection",
+ "▁pass ing",
+ "▁pas sing",
+ "ße n",
+ "ß en",
+ "ph as",
+ "pha s",
+ "p has",
+ "ens ional",
+ "ension al",
+ "re cord",
+ "rec ord",
+ "co hol",
+ "▁H arry",
+ "▁Har ry",
+ "▁Harr y",
+ "izont al",
+ "izon tal",
+ "▁f inger",
+ "▁fin ger",
+ "▁fing er",
+ "▁young er",
+ "▁S C",
+ "▁ SC",
+ "oper ation",
+ "B Y",
+ "he im",
+ "▁B ad",
+ "▁Ba d",
+ "▁ Bad",
+ "▁st orm",
+ "▁stor m",
+ "▁sto rm",
+ "▁ storm",
+ "▁N at",
+ "▁Na t",
+ "▁bu ying",
+ "▁buy ing",
+ "▁S ometimes",
+ "▁Some times",
+ "▁С та",
+ "es sed",
+ "ess ed",
+ "esse d",
+ "▁da mn",
+ "▁dam n",
+ "▁m eg",
+ "▁me g",
+ "um es",
+ "ume s",
+ "u mes",
+ "ün d",
+ "ü nd",
+ "т ра",
+ "▁sil ver",
+ "w d",
+ "hid den",
+ "h idden",
+ "ar do",
+ "ard o",
+ "▁commun ities",
+ "▁d iet",
+ "▁di et",
+ "▁die t",
+ "ot ted",
+ "ott ed",
+ "otte d",
+ "▁b at",
+ "▁ba t",
+ "▁ bat",
+ "an cer",
+ "ance r",
+ "anc er",
+ "▁f mt",
+ "▁ fmt",
+ "▁P en",
+ "▁Pe n",
+ "▁ Pen",
+ "▁t il",
+ "▁ti l",
+ "▁ til",
+ "En um",
+ "E num",
+ "PA TH",
+ "P ATH",
+ "▁mat ters",
+ "▁matter s",
+ "▁matt ers",
+ "time out",
+ "-- ----------",
+ "---- --------",
+ "-------- ----",
+ "--- ---------",
+ "----- -------",
+ "---------- --",
+ "------ ------",
+ "--------- ---",
+ "------- -----",
+ "----------- -",
+ "- -----------",
+ "ka n",
+ "k an",
+ "▁Cor por",
+ "=\" ../../",
+ "=\"../ ../",
+ "▁A le",
+ "▁Al e",
+ "hent ication",
+ "hentic ation",
+ "▁com plic",
+ "▁comp lic",
+ "▁compl ic",
+ "▁Se curity",
+ "▁Sec urity",
+ "▁ Security",
+ "OF F",
+ "O FF",
+ "R ad",
+ "ap se",
+ "aps e",
+ "a pse",
+ "▁d ance",
+ "▁dan ce",
+ "▁perm issions",
+ "▁permission s",
+ "▁war rant",
+ "▁l ad",
+ "▁la d",
+ "▁ lad",
+ "▁is ol",
+ "▁i sol",
+ "d l",
+ "▁A u",
+ "ye s",
+ "y es",
+ "▁t v",
+ "▁ tv",
+ "▁pro vider",
+ "▁prov ider",
+ "▁provide r",
+ "▁ provider",
+ "▁ter rible",
+ "▁terr ible",
+ "▁dep artment",
+ "▁depart ment",
+ "er al",
+ "era l",
+ "e ral",
+ "▁implement ation",
+ "S R",
+ "▁h earing",
+ "▁he aring",
+ "▁hear ing",
+ "▁K n",
+ "F R",
+ "t v",
+ "▁d iss",
+ "▁dis s",
+ "▁di ss",
+ "F UN",
+ "▁dur ante",
+ "▁durant e",
+ "os is",
+ "osi s",
+ "o sis",
+ "▁task s",
+ "▁ tasks",
+ "▁B lo",
+ "▁Bl o",
+ "▁ Blo",
+ "во д",
+ "▁br anch",
+ "▁ branch",
+ "▁polit ics",
+ "▁E lle",
+ "▁El le",
+ "▁Ell e",
+ "▁lead ership",
+ "▁leader ship",
+ "▁leaders hip",
+ "ex pr",
+ "exp r",
+ "▁techn iques",
+ "▁technique s",
+ "pr ec",
+ "pre c",
+ "p rec",
+ "Sig ma",
+ "S igma",
+ "im ately",
+ "imate ly",
+ "imat ely",
+ "t k",
+ "ach ment",
+ "▁En ter",
+ "▁Ent er",
+ "▁ Enter",
+ "▁cre ative",
+ "▁creat ive",
+ "▁з на",
+ "▁ зна",
+ "ap py",
+ "app y",
+ "un ched",
+ "unch ed",
+ "unc hed",
+ "▁' ',",
+ "▁'' ,",
+ "on der",
+ "ond er",
+ "onde r",
+ "o nder",
+ "{ -",
+ "NU M",
+ "N UM",
+ "▁n arr",
+ "▁na rr",
+ "▁nar r",
+ "Mem ory",
+ "▁win ning",
+ "▁ winning",
+ "▁F ollow",
+ "▁Fol low",
+ "▁ Follow",
+ "*/ \r",
+ "vis ion",
+ "v ision",
+ "res ents",
+ "resent s",
+ "zi one",
+ "z ione",
+ "▁l atter",
+ "▁lat ter",
+ "▁requ ests",
+ "▁request s",
+ "▁ requests",
+ "▁m argin",
+ "▁mar gin",
+ "▁marg in",
+ "▁ margin",
+ "▁{ \"",
+ "▁ {\"",
+ "v ideo",
+ "c n",
+ "▁Im age",
+ "▁ Image",
+ "T im",
+ "CON FIG",
+ "CONF IG",
+ "▁all owing",
+ "▁allow ing",
+ "▁comb ined",
+ "▁combine d",
+ "PU T",
+ "P UT",
+ "▁instance of",
+ "ig in",
+ "igi n",
+ "i gin",
+ "▁p ero",
+ "▁per o",
+ "▁pe ro",
+ "▁' '",
+ "▁ ''",
+ "▁conf idence",
+ "▁equ ivalent",
+ "▁equival ent",
+ "pa d",
+ "p ad",
+ "ef fect",
+ "eff ect",
+ "e ffect",
+ "R X",
+ "▁l ang",
+ "▁la ng",
+ "▁lan g",
+ "▁ lang",
+ "str ong",
+ "▁b ridge",
+ "▁br idge",
+ "▁ bridge",
+ "ay a",
+ "a ya",
+ "▁t reated",
+ "▁tre ated",
+ "▁treat ed",
+ "▁f orth",
+ "▁for th",
+ "▁fort h",
+ "S W",
+ "▁account s",
+ "▁P O",
+ "▁ PO",
+ "▁list ening",
+ "▁listen ing",
+ "Ro ute",
+ "R oute",
+ "() ))",
+ "()) )",
+ "( )))",
+ "cp y",
+ "c py",
+ "▁re form",
+ "▁ref orm",
+ "▁g ate",
+ "▁ga te",
+ "▁ gate",
+ "▁W alk",
+ "▁Wal k",
+ "▁ Walk",
+ "▁some how",
+ "t f",
+ "▁l ayout",
+ "▁la yout",
+ "▁lay out",
+ "▁ layout",
+ "um in",
+ "umi n",
+ "u min",
+ "▁consider ing",
+ "▁consid ering",
+ "▁pre mi",
+ "▁pr emi",
+ "▁prem i",
+ "▁M om",
+ "▁Mo m",
+ "at han",
+ "ath an",
+ "a than",
+ "Ge n",
+ "G en",
+ "▁plan et",
+ "▁plane t",
+ "am ples",
+ "amp les",
+ "ample s",
+ "▁M O",
+ "▁ MO",
+ "sh op",
+ "s hop",
+ "▁prem ier",
+ "▁premi er",
+ "▁s impl",
+ "▁sim pl",
+ "▁s egu",
+ "▁se gu",
+ "▁seg u",
+ "L Y",
+ "Su m",
+ "S um",
+ "▁t ables",
+ "▁table s",
+ "▁tab les",
+ "▁ta bles",
+ "▁ tables",
+ "sk a",
+ "s ka",
+ "▁ ž",
+ "p d",
+ "▁s ous",
+ "▁so us",
+ "▁sou s",
+ "▁con ference",
+ "▁confer ence",
+ "▁D at",
+ "▁Da t",
+ "▁ Dat",
+ "Sc roll",
+ "▁stand ards",
+ "▁standard s",
+ "▁г ру",
+ "es se",
+ "ess e",
+ "▁citiz ens",
+ "▁citizen s",
+ "▁occur red",
+ "▁dem ocr",
+ "▁demo cr",
+ "▁e lev",
+ "▁el ev",
+ "▁ele v",
+ "▁S em",
+ "▁Se m",
+ "▁ Sem",
+ "ens us",
+ "he aders",
+ "head ers",
+ "header s",
+ "▁Ch ris",
+ "im ento",
+ "iment o",
+ "imen to",
+ "ko m",
+ "k om",
+ "Co r",
+ "C or",
+ "MI N",
+ "M IN",
+ "us her",
+ "ush er",
+ "Data base",
+ "Dat abase",
+ "▁f ormal",
+ "▁for mal",
+ "▁form al",
+ "▁forma l",
+ "ig ne",
+ "ign e",
+ "▁organ izations",
+ "▁organiz ations",
+ "▁organization s",
+ "▁I re",
+ "▁Ir e",
+ "X ml",
+ "и з",
+ "▁p ray",
+ "▁pr ay",
+ "▁pra y",
+ "▁b omb",
+ "▁bo mb",
+ "▁bom b",
+ "▁m and",
+ "▁man d",
+ "▁ma nd",
+ "▁ mand",
+ "er ts",
+ "ert s",
+ "▁c lock",
+ "▁cl ock",
+ "▁clo ck",
+ "▁ clock",
+ "▁b uck",
+ "▁bu ck",
+ "ва ли",
+ "вал и",
+ "в али",
+ "en sch",
+ "ens ch",
+ "▁v olt",
+ "▁vo lt",
+ "▁vol t",
+ "▁ volt",
+ "▁fil ms",
+ "▁film s",
+ "▁pl ants",
+ "▁plan ts",
+ "▁plant s",
+ "in ode",
+ "ino de",
+ "i node",
+ "Bo olean",
+ "▁restaur ant",
+ "ía n",
+ "í an",
+ "▁de but",
+ "▁deb ut",
+ "page s",
+ "pa ges",
+ "pag es",
+ "p ages",
+ "▁wor dt",
+ "▁word t",
+ "▁Б а",
+ "▁great est",
+ "(\" /",
+ "▁c opyright",
+ "▁copy right",
+ "▁ copyright",
+ "▁r it",
+ "▁ri t",
+ "▁ rit",
+ "size of",
+ "Tr ace",
+ "Tra ce",
+ "ue nt",
+ "uen t",
+ "u ent",
+ "ту р",
+ "т ур",
+ "▁k o",
+ "▁ ko",
+ ": \\",
+ "▁b igger",
+ "▁big ger",
+ "▁perfect ly",
+ "ten ance",
+ "MA SK",
+ "M ASK",
+ "r é",
+ "▁e tt",
+ "▁et t",
+ "▁ ett",
+ "▁n ose",
+ "▁no se",
+ "▁nos e",
+ "▁c raft",
+ "▁cr aft",
+ "▁ craft",
+ "it eral",
+ "ite ral",
+ "iter al",
+ "▁discuss ed",
+ "▁Jew ish",
+ "C ap",
+ "▁Un less",
+ "▁Jack son",
+ "Att ributes",
+ "Attribute s",
+ "Attrib utes",
+ "▁l unch",
+ "▁lun ch",
+ "ö l",
+ "at r",
+ "a tr",
+ "▁pay ing",
+ "▁pa ying",
+ "Par se",
+ "Pars e",
+ "P arse",
+ "() \r",
+ "( )\r",
+ "la d",
+ "l ad",
+ "▁r are",
+ "▁ra re",
+ "▁[ ];",
+ "▁[] ;",
+ "▁ [];",
+ "st one",
+ "ston e",
+ "sto ne",
+ "▁u nc",
+ "▁un c",
+ "▁ unc",
+ "▁def ense",
+ "▁defens e",
+ "} +",
+ "▁Gl obal",
+ "▁ Global",
+ "▁Sov iet",
+ "▁Austral ian",
+ "▁Australia n",
+ "▁g li",
+ "▁gl i",
+ "var iant",
+ "vari ant",
+ "▁R on",
+ "▁Ro n",
+ "▁lo an",
+ "St ep",
+ "Ste p",
+ "me mber",
+ "mem ber",
+ "m ember",
+ "Sc h",
+ "S ch",
+ "▁Commit tee",
+ "▁s pending",
+ "▁sp ending",
+ "▁spend ing",
+ "▁T ri",
+ "▁Tr i",
+ "▁ Tri",
+ "▁J ournal",
+ "▁Jour nal",
+ "▁ Journal",
+ "▁s ugar",
+ "▁su gar",
+ "▁sug ar",
+ "el ly",
+ "ell y",
+ "HT ML",
+ "▁ad vent",
+ "▁adv ent",
+ "win g",
+ "wi ng",
+ "w ing",
+ "▁Wh ether",
+ "▁Whe ther",
+ "or ation",
+ "▁N E",
+ "▁ NE",
+ "iv eness",
+ "ive ness",
+ "iven ess",
+ "▁h av",
+ "▁ha v",
+ "▁ hav",
+ "▁con scious",
+ "▁ conscious",
+ "ee n",
+ "e en",
+ "Sym bol",
+ "S ymbol",
+ "▁к у",
+ "▁ ку",
+ "Log ger",
+ "▁L ittle",
+ "▁Lit tle",
+ "wide t",
+ "wi det",
+ "wid et",
+ "oc ation",
+ "pi n",
+ "p in",
+ "▁sym met",
+ "▁A D",
+ "▁ AD",
+ "▁pos ts",
+ "▁po sts",
+ "▁post s",
+ "▁ posts",
+ "sh al",
+ "sha l",
+ "s hal",
+ "▁Con f",
+ "▁Co nf",
+ "▁ Conf",
+ "▁ch ose",
+ "▁cho se",
+ "ma l",
+ "m al",
+ "ul o",
+ "u lo",
+ "▁M ethod",
+ "▁ Method",
+ "▁miss ed",
+ "▁mis sed",
+ "Re move",
+ "Rem ove",
+ "Aut o",
+ "A uto",
+ "VAL UE",
+ "th let",
+ "▁For ce",
+ "▁ Force",
+ "p f",
+ "▁ Я",
+ "la te",
+ "lat e",
+ "l ate",
+ "▁p ul",
+ "▁pu l",
+ "▁ pul",
+ "Po p",
+ "P op",
+ "▁adv anced",
+ "▁advance d",
+ "air es",
+ "ai res",
+ "aire s",
+ "a ires",
+ "res sed",
+ "ress ed",
+ "resse d",
+ "r essed",
+ "AM E",
+ "A ME",
+ "be ll",
+ "bel l",
+ "b ell",
+ "ac hing",
+ "ach ing",
+ "achi ng",
+ "a ching",
+ "i ć",
+ "ec ho",
+ "ech o",
+ "e cho",
+ "H S",
+ "▁fun ny",
+ "ри и",
+ "▁e er",
+ "▁ve get",
+ "▁four th",
+ "c f",
+ "trans form",
+ "▁g rown",
+ "▁gr own",
+ "▁grow n",
+ "▁gro wn",
+ "▁Mc C",
+ "si te",
+ "s ite",
+ "▁b eneath",
+ "▁be neath",
+ "▁s hell",
+ "▁sh ell",
+ "▁she ll",
+ "▁shel l",
+ "▁ shell",
+ "x d",
+ "Pl ay",
+ "P lay",
+ "sh ort",
+ "Ro le",
+ "R ole",
+ "▁relig ion",
+ "in ator",
+ "ina tor",
+ "} ",
+ "▁El iz",
+ "▁Eli z",
+ "M icrosoft",
+ "▁v ez",
+ "▁ve z",
+ "▁ vez",
+ "▁ра бо",
+ "▁ рабо",
+ "re ich",
+ "rei ch",
+ "ve t",
+ "v et",
+ "en um",
+ "enu m",
+ "e num",
+ "▁w elcome",
+ "▁wel come",
+ "name nt",
+ "na ment",
+ "nam ent",
+ "n ament",
+ "▁j an",
+ "▁ja n",
+ "▁ jan",
+ "▁c ycle",
+ "▁cy cle",
+ "▁cycl e",
+ "▁ cycle",
+ "▁a cknow",
+ "▁ac know",
+ "▁w ound",
+ "▁wo und",
+ "id i",
+ "i di",
+ "▁poss ibility",
+ "an notation",
+ "annot ation",
+ "▁techn ical",
+ "▁f old",
+ "▁fol d",
+ "▁fo ld",
+ "▁ fold",
+ "e h",
+ "ist ence",
+ "isten ce",
+ "▁re ply",
+ "▁rep ly",
+ "▁repl y",
+ "▁ reply",
+ "et es",
+ "ete s",
+ "e tes",
+ "▁dec ades",
+ "▁decade s",
+ "wa n",
+ "w an",
+ "▁к ра",
+ "▁ кра",
+ "▁L ab",
+ "▁La b",
+ "▁u nf",
+ "▁un f",
+ "▁im per",
+ "▁imp er",
+ "▁ imper",
+ "▁b ug",
+ "▁bu g",
+ "▁ bug",
+ "▁Th ough",
+ "th rows",
+ "throw s",
+ "Vis ible",
+ "V isible",
+ "pr ev",
+ "pre v",
+ "p rev",
+ "▁T y",
+ "▁ Ty",
+ "▁de pending",
+ "▁depend ing",
+ "▁dep ending",
+ "▁pol icies",
+ "▁polic ies",
+ "an dy",
+ "and y",
+ "▁Ital ian",
+ "▁Italia n",
+ "um a",
+ "u ma",
+ "▁sign s",
+ "▁sig ns",
+ "▁Th rough",
+ "б ы",
+ "bo t",
+ "b ot",
+ "▁pub lish",
+ "▁publi sh",
+ "▁ publish",
+ ")* *",
+ ") **",
+ "AT TR",
+ "ATT R",
+ "ir al",
+ "ira l",
+ "i ral",
+ "V T",
+ "▁recogn ized",
+ "▁recognize d",
+ "▁L ind",
+ "▁Lin d",
+ "▁Li nd",
+ "ect ion",
+ "e ction",
+ "▁rel atively",
+ "▁relative ly",
+ "▁relativ ely",
+ "▁A h",
+ "▁ Ah",
+ "▁D ig",
+ "▁Di g",
+ "▁ Dig",
+ "ц ь",
+ "ic ket",
+ "ick et",
+ "▁specific ally",
+ "no st",
+ "nos t",
+ "n ost",
+ "▁g rass",
+ "▁gr ass",
+ "▁gra ss",
+ "▁gras s",
+ "▁c auses",
+ "▁caus es",
+ "▁cause s",
+ "▁ca uses",
+ "т во",
+ "ut ter",
+ "utt er",
+ "▁F estival",
+ "▁Fest ival",
+ "gr eg",
+ "gre g",
+ "g reg",
+ "▁weap ons",
+ "▁weapon s",
+ "▁s ir",
+ "▁si r",
+ "▁Virgin ia",
+ "lo gin",
+ "log in",
+ "▁s chedul",
+ "▁sched ul",
+ "сь кого",
+ "сько го",
+ "▁l osing",
+ "▁lo sing",
+ "▁los ing",
+ "▁E urop",
+ "▁Euro p",
+ "▁Eu rop",
+ "\"> <",
+ "\" ><",
+ "as p",
+ "a sp",
+ "aj o",
+ "a jo",
+ "ex ports",
+ "exp orts",
+ "export s",
+ "▁N ode",
+ "▁No de",
+ "▁ Node",
+ "▁j ako",
+ "▁ja ko",
+ "▁jak o",
+ "▁y a",
+ "▁ ya",
+ "▁success fully",
+ "▁successful ly",
+ "▁friend ly",
+ "▁ friendly",
+ "buf f",
+ "bu ff",
+ "b uff",
+ "DE FAULT",
+ "▁pre gn",
+ "▁preg n",
+ "Requ ired",
+ "Require d",
+ "▁b inary",
+ "▁bin ary",
+ "▁ binary",
+ "is ting",
+ "ist ing",
+ "isti ng",
+ "▁st ared",
+ "▁star ed",
+ "▁stare d",
+ "▁sta red",
+ "▁circum stances",
+ "▁х о",
+ "▁ хо",
+ "re i",
+ "r ei",
+ "▁Г о",
+ "Trans form",
+ "cn t",
+ "c nt",
+ "▁E xt",
+ "▁Ex t",
+ "▁ Ext",
+ "re port",
+ "rep ort",
+ "repo rt",
+ "VER SION",
+ "▁an aly",
+ "▁anal y",
+ "▁ analy",
+ "▁M arg",
+ "▁Mar g",
+ "▁Ma rg",
+ "▁al leg",
+ "▁all eg",
+ "▁alle g",
+ "build er",
+ "b uilder",
+ "To String",
+ "La yer",
+ "L ayer",
+ "ís t",
+ "í st",
+ "Pro p",
+ "Pr op",
+ "P rop",
+ "▁E mp",
+ "▁Em p",
+ "▁ Emp",
+ "} ]",
+ "▁s elling",
+ "▁sell ing",
+ "▁sel ling",
+ "▁ selling",
+ "▁que ue",
+ "▁ queue",
+ "▁ser iously",
+ "▁serious ly",
+ "▁L ead",
+ "▁Le ad",
+ "▁ Lead",
+ "text it",
+ "tex tit",
+ "test ing",
+ "tes ting",
+ "▁П ре",
+ "se curity",
+ "sec urity",
+ "ia ł",
+ "i ał",
+ "ú n",
+ "ch ip",
+ "chi p",
+ "c hip",
+ "▁c andidate",
+ "▁candid ate",
+ "▁min ister",
+ "▁mini ster",
+ "▁minist er",
+ "▁ minister",
+ "er ia",
+ "eri a",
+ "e ria",
+ "▁H et",
+ "▁He t",
+ "ди н",
+ "д ин",
+ "▁Brit ain",
+ "▁b arely",
+ "▁bar ely",
+ "▁bare ly",
+ "▁s ty",
+ "▁st y",
+ "▁ sty",
+ "▁Span ish",
+ "▁V en",
+ "▁Ve n",
+ "time r",
+ "ti mer",
+ "tim er",
+ "t imer",
+ "кі в",
+ "к ів",
+ "▁document s",
+ "▁doc uments",
+ "(' .",
+ "( '.",
+ "▁d ebug",
+ "▁de bug",
+ "▁deb ug",
+ "▁ debug",
+ "▁cont ro",
+ "▁contr o",
+ "сто я",
+ "▁j oy",
+ "▁jo y",
+ "▁ joy",
+ "S n",
+ "In v",
+ "I nv",
+ "▁pro tocol",
+ "▁proto col",
+ "▁prot ocol",
+ "▁ protocol",
+ "▁f aces",
+ "▁face s",
+ "▁fac es",
+ "▁fa ces",
+ "▁ faces",
+ "▁Des pite",
+ "se d",
+ "s ed",
+ "Con f",
+ "Co nf",
+ "AR G",
+ "A RG",
+ "▁e volution",
+ "▁ev olution",
+ "▁t od",
+ "▁to d",
+ "▁P romise",
+ "▁Prom ise",
+ "▁ Promise",
+ "▁pos ted",
+ "▁po sted",
+ "▁post ed",
+ "Per m",
+ "Pe rm",
+ "P erm",
+ "be t",
+ "b et",
+ "An g",
+ "A ng",
+ "J ust",
+ "▁r um",
+ "▁ru m",
+ "▁ rum",
+ "la yer",
+ "lay er",
+ "l ayer",
+ "▁beh avi",
+ "▁behav i",
+ "ip ping",
+ "ipp ing",
+ "ippi ng",
+ "i pping",
+ "▁d ynam",
+ "▁dy nam",
+ "▁dyn am",
+ "▁sch eme",
+ "▁sche me",
+ "▁ scheme",
+ "▁pro to",
+ "▁pr oto",
+ "▁prot o",
+ "▁ proto",
+ ") /",
+ "Col lections",
+ "Collection s",
+ "Collect ions",
+ "ri ev",
+ "rie v",
+ "r iev",
+ "▁C lick",
+ "▁Cl ick",
+ "▁ Click",
+ "▁u ns",
+ "▁un s",
+ "▁ uns",
+ "wide tilde",
+ "widet ilde",
+ "▁remember ed",
+ "г і",
+ "in ates",
+ "ina tes",
+ "inate s",
+ "▁incor por",
+ "▁De scription",
+ "▁Des cription",
+ "▁ Description",
+ "▁pre pare",
+ "▁prep are",
+ "▁prepar e",
+ "▁ prepare",
+ "▁F inal",
+ "▁Fin al",
+ "▁Fi nal",
+ "▁ Final",
+ "u ation",
+ "▁Qu een",
+ "▁Que en",
+ "> ;",
+ "▁autom atically",
+ "▁automatic ally",
+ "▁sh arp",
+ "▁shar p",
+ "▁sha rp",
+ "▁me at",
+ "at eur",
+ "ate ur",
+ "as tern",
+ "ast ern",
+ "aster n",
+ "aste rn",
+ "▁st uck",
+ "ASS ERT",
+ "▁pl anned",
+ "▁plan ned",
+ "do ts",
+ "dot s",
+ "d ots",
+ "ook ie",
+ "oo kie",
+ "▁His tor",
+ "▁Hist or",
+ "▁re views",
+ "▁review s",
+ "IM P",
+ "I MP",
+ "▁answ ered",
+ "▁answer ed",
+ "To tal",
+ "T otal",
+ "▁s au",
+ "▁sa u",
+ "▁Me xico",
+ "▁Mex ico",
+ "contin ue",
+ "▁App le",
+ "▁Ap ple",
+ "like ly",
+ "lik ely",
+ "з ва",
+ "us ers",
+ "use rs",
+ "user s",
+ "▁ident ified",
+ "▁L ev",
+ "▁Le v",
+ "▁m ol",
+ "▁mo l",
+ "▁Is lam",
+ "▁com mitted",
+ "▁comm itted",
+ "▁commit ted",
+ "wr it",
+ "w rit",
+ "бе р",
+ "б ер",
+ "ri ft",
+ "rif t",
+ "r ift",
+ "▁inter rupt",
+ "▁ interrupt",
+ "▁read only",
+ "sch ema",
+ "sche ma",
+ "s chema",
+ "S m",
+ "D ouble",
+ "az a",
+ "a za",
+ "▁H al",
+ "▁Ha l",
+ "▁ Hal",
+ "Mo ve",
+ "M ove",
+ "▁S eries",
+ "▁Se ries",
+ "▁Ser ies",
+ "▁Serie s",
+ "▁ Series",
+ "in line",
+ "▁кото ры",
+ "so c",
+ "s oc",
+ "▁t ent",
+ "▁te nt",
+ "▁ten t",
+ "▁a mer",
+ "▁am er",
+ "▁ amer",
+ "ak i",
+ "a ki",
+ "▁l ady",
+ "▁la dy",
+ "▁lad y",
+ "▁t ired",
+ "▁ti red",
+ "▁tire d",
+ "▁tir ed",
+ "if i",
+ "i fi",
+ "▁m ême",
+ "▁ même",
+ "ou ver",
+ "▁a side",
+ "▁as ide",
+ "Di d",
+ "D id",
+ "', \r",
+ "' ,\r",
+ "▁br inging",
+ "▁bring ing",
+ "Draw ing",
+ "ar o",
+ "a ro",
+ "▁R h",
+ "▁N az",
+ "▁Na z",
+ "es so",
+ "ess o",
+ "▁re action",
+ "▁react ion",
+ "mit ted",
+ "mitt ed",
+ "m itted",
+ "▁abs olute",
+ "▁absolut e",
+ "▁ absolute",
+ "ha ust",
+ "haus t",
+ "(( )",
+ "( ()",
+ "▁T ask",
+ "▁Ta sk",
+ "▁ Task",
+ "ER S",
+ "E RS",
+ "▁^ {",
+ "▁ ^{",
+ "V D",
+ "▁t one",
+ "▁to ne",
+ "▁ton e",
+ "dis t",
+ "di st",
+ "d ist",
+ "v s",
+ "▁whe el",
+ "▁ wheel",
+ "▁administr ation",
+ "▁admin istration",
+ "▁inter ests",
+ "▁interest s",
+ "▁point er",
+ "▁po inter",
+ "▁ pointer",
+ "▁en counter",
+ "▁enc ounter",
+ "ave r",
+ "av er",
+ "a ver",
+ "▁n ord",
+ "▁no rd",
+ "▁nor d",
+ "ke t",
+ "k et",
+ "▁b each",
+ "▁be ach",
+ "▁enjoy ed",
+ "cont ains",
+ "▁app end",
+ "▁ap pend",
+ "▁appe nd",
+ "▁ append",
+ "W ait",
+ "▁s quad",
+ "▁squ ad",
+ "ze l",
+ "z el",
+ "▁med ium",
+ "▁medi um",
+ "▁ medium",
+ "▁s ending",
+ "▁send ing",
+ "▁sen ding",
+ "▁L ady",
+ "▁La dy",
+ "▁Lad y",
+ "ç ões",
+ "▁dest ination",
+ "▁destin ation",
+ "▁ destination",
+ "ny ch",
+ "n ych",
+ "▁conf lict",
+ "▁conflic t",
+ "▁L y",
+ "▁v ul",
+ "▁vu l",
+ "▁bas ically",
+ "▁basic ally",
+ "re ated",
+ "reat ed",
+ "reate d",
+ "rea ted",
+ "bl ack",
+ "ug ins",
+ "ugin s",
+ "▁cal m",
+ "▁ca lm",
+ "ér ie",
+ "éri e",
+ "é rie",
+ "ha r",
+ "h ar",
+ "ла н",
+ "л ан",
+ "▁С е",
+ "w atch",
+ "▁P ut",
+ "▁Pu t",
+ "▁ Put",
+ "▁d ump",
+ "▁du mp",
+ "▁ dump",
+ "ac her",
+ "ach er",
+ "ache r",
+ "a cher",
+ "sc roll",
+ "scr oll",
+ "▁cl aimed",
+ "▁claim ed",
+ "▁ claimed",
+ "▁Cont rol",
+ "▁ Control",
+ "▁bl ind",
+ "en ti",
+ "ent i",
+ "▁Ke ep",
+ "▁ Keep",
+ "▁Develop ment",
+ "im ages",
+ "image s",
+ "ima ges",
+ "imag es",
+ "▁t ough",
+ "▁to ugh",
+ "▁tou gh",
+ "ge bra",
+ "geb ra",
+ "▁se pt",
+ "▁sep t",
+ "he w",
+ "h ew",
+ "▁s kill",
+ "▁sk ill",
+ "▁ski ll",
+ "▁ skill",
+ "▁T ay",
+ "▁Ta y",
+ "▁k tó",
+ "ow ner",
+ "own er",
+ "par e",
+ "pa re",
+ "p are",
+ "▁f ee",
+ "▁fe e",
+ "▁ fee",
+ "▁contin ues",
+ "▁continue s",
+ "▁continu es",
+ "▁k an",
+ "▁ka n",
+ "▁ kan",
+ "be s",
+ "b es",
+ "▁c ha",
+ "▁ch a",
+ "▁ cha",
+ "ov o",
+ "o vo",
+ "▁N ight",
+ "▁Ni ght",
+ "ict ure",
+ "sh ire",
+ "s hire",
+ "▁es say",
+ "▁ess ay",
+ "▁sup pose",
+ "▁supp ose",
+ "et ic",
+ "eti c",
+ "Ar t",
+ "A rt",
+ "ac on",
+ "aco n",
+ "a con",
+ "ll a",
+ "l la",
+ "word s",
+ "wor ds",
+ "w ords",
+ "▁compar ison",
+ "▁B E",
+ "▁ BE",
+ "▁challeng es",
+ "▁challenge s",
+ "▁o l",
+ "▁ ol",
+ "cite p",
+ "cit ep",
+ "▁F oot",
+ "▁Fo ot",
+ "▁ Foot",
+ "▁S uch",
+ "▁Su ch",
+ "▁ Such",
+ "▁p apers",
+ "▁paper s",
+ "▁pa pers",
+ "▁pap ers",
+ "act iv",
+ "qu er",
+ "que r",
+ "q uer",
+ "т я",
+ "▁Т о",
+ "сь кий",
+ "th ur",
+ "do ne",
+ "don e",
+ "d one",
+ "▁sh ock",
+ "▁ded icated",
+ "▁dedic ated",
+ "▁cor respond",
+ "▁correspon d",
+ "Se cond",
+ "Sec ond",
+ "▁b ull",
+ "▁bu ll",
+ "▁bul l",
+ "li fe",
+ "lif e",
+ "l ife",
+ "ind ent",
+ "inde nt",
+ "inden t",
+ "▁fig ures",
+ "▁figure s",
+ "▁And rew",
+ "▁Andre w",
+ "▁Andr ew",
+ "is p",
+ "i sp",
+ "▁fav our",
+ "зд а",
+ "з да",
+ "▁E lect",
+ "▁El ect",
+ "▁Ele ct",
+ "F ull",
+ "▁near by",
+ "▁Reg ister",
+ "▁ Register",
+ "Sc ale",
+ "Scal e",
+ "ic ations",
+ "ication s",
+ "и н",
+ "▁A M",
+ "▁ AM",
+ "pa ir",
+ "p air",
+ "▁pers pective",
+ "▁n os",
+ "▁no s",
+ "▁ nos",
+ "ap a",
+ "a pa",
+ "ost ał",
+ "osta ł",
+ "▁P ers",
+ "▁Per s",
+ "▁Pe rs",
+ "▁ Pers",
+ "ic er",
+ "ice r",
+ "i cer",
+ "▁pl astic",
+ "до в",
+ "д ов",
+ "ci ples",
+ "cipl es",
+ "cip les",
+ "z ą",
+ "cl os",
+ "c los",
+ "▁у ча",
+ "▁ Á",
+ "pl ugin",
+ "plug in",
+ "▁an gle",
+ "▁ang le",
+ "▁angl e",
+ "▁ angle",
+ "▁com mission",
+ "▁comm ission",
+ "▁fun ds",
+ "▁fund s",
+ "▁in du",
+ "▁ind u",
+ "▁d rawn",
+ "▁dr awn",
+ "▁draw n",
+ "á m",
+ "▁develop ing",
+ "▁seg ment",
+ "▁ segment",
+ "is me",
+ "ism e",
+ "sc r",
+ "s cr",
+ "▁l ies",
+ "▁li es",
+ "▁lie s",
+ "▁I L",
+ "▁ IL",
+ "▁a pi",
+ "▁ap i",
+ "▁ api",
+ "Ext ension",
+ "▁s cal",
+ "▁sc al",
+ "▁ scal",
+ "inst all",
+ "▁We ek",
+ "▁ Week",
+ "▁gen tle",
+ "▁gent le",
+ "▁Canad ian",
+ "▁d ialog",
+ "▁dial og",
+ "▁dia log",
+ "▁ dialog",
+ "▁art icles",
+ "▁article s",
+ "▁artic les",
+ "The me",
+ "Th eme",
+ "S M",
+ "▁B ul",
+ "▁Bu l",
+ "▁ Bul",
+ "▁l eur",
+ "▁le ur",
+ "▁s tom",
+ "▁st om",
+ "▁sto m",
+ "Pl ugin",
+ "▁по сле",
+ "▁пос ле",
+ "▁st ead",
+ "▁ste ad",
+ "▁ stead",
+ "▁ ś",
+ "ip her",
+ "iph er",
+ "i pher",
+ "▁pr ze",
+ "▁prz e",
+ "▁d raft",
+ "▁dr aft",
+ "▁ draft",
+ "bot tom",
+ "b ottom",
+ "▁{ };",
+ "▁{} ;",
+ "▁stay ed",
+ "fe ature",
+ "feat ure",
+ "▁v ot",
+ "▁vo t",
+ "▁fab ric",
+ "ç a",
+ "(' #",
+ "re a",
+ "r ea",
+ "▁re put",
+ "▁rep ut",
+ "▁C ir",
+ "▁Ci r",
+ "▁ Cir",
+ "▁A L",
+ "▁ AL",
+ "▁assert Equals",
+ "▁ assertEquals",
+ "result s",
+ "▁C ross",
+ "▁Cr oss",
+ "▁Cro ss",
+ "▁ Cross",
+ "urs day",
+ "▁a udio",
+ "▁aud io",
+ "▁ audio",
+ "▁g ap",
+ "▁ga p",
+ "▁stre ets",
+ "▁street s",
+ "▁scient ific",
+ "pl atform",
+ "▁a uss",
+ "▁au ss",
+ "▁aus s",
+ "▁C ro",
+ "▁Cr o",
+ "▁part ial",
+ "▁parti al",
+ "▁ partial",
+ "un c",
+ "u nc",
+ "▁cho ices",
+ "▁choice s",
+ "▁и ли",
+ "pr ed",
+ "pre d",
+ "p red",
+ "▁he ads",
+ "▁head s",
+ "▁ heads",
+ "ter day",
+ "▁N ick",
+ "▁Nic k",
+ "▁Ni ck",
+ "▁we ird",
+ "as ant",
+ "asa nt",
+ "▁represent ed",
+ "▁п и",
+ "▁ пи",
+ "D P",
+ "or ders",
+ "ord ers",
+ "order s",
+ "cl ock",
+ "c lock",
+ "▁H o",
+ "ar ters",
+ "art ers",
+ "arter s",
+ "arte rs",
+ "C md",
+ "og a",
+ "o ga",
+ "Key s",
+ "Ke ys",
+ "Re port",
+ "Rep ort",
+ "Repo rt",
+ "▁V ill",
+ "▁Vi ll",
+ "▁Vil l",
+ "▁M u",
+ "▁ Mu",
+ "▁own ed",
+ "▁ owned",
+ "SU CCESS",
+ "▁type of",
+ "▁ typeof",
+ "hd r",
+ "h dr",
+ "ua ble",
+ "u able",
+ "▁neighbor hood",
+ "▁A P",
+ "▁ AP",
+ "▁result ing",
+ "▁sh adow",
+ "▁ shadow",
+ "STR ING",
+ "▁video s",
+ "▁vide os",
+ "ле ння",
+ "лен ня",
+ "ex pect",
+ "exp ect",
+ "▁Val ley",
+ "▁Vall ey",
+ "▁g oto",
+ "▁go to",
+ "▁got o",
+ "▁ goto",
+ "▁S her",
+ "▁She r",
+ "▁Sh er",
+ "fr astr",
+ "▁oper ating",
+ "▁opera ting",
+ "▁э то",
+ "▁License d",
+ "▁Lic ensed",
+ "Var iable",
+ "Vari able",
+ "▁P R",
+ "▁ PR",
+ "▁H ans",
+ "▁Ha ns",
+ "▁Han s",
+ "cl one",
+ "▁G esch",
+ "▁Ge sch",
+ "▁Ges ch",
+ "▁B and",
+ "▁Ba nd",
+ "▁Ban d",
+ "▁ Band",
+ "... .....",
+ ".... ....",
+ "..... ...",
+ "ui ng",
+ "u ing",
+ "▁hundred s",
+ "▁о к",
+ "▁emot ional",
+ "▁emotion al",
+ "▁Ind ust",
+ ") +",
+ "▁Egy pt",
+ "▁fr anç",
+ "▁ š",
+ "▁f asc",
+ "▁fa sc",
+ "on to",
+ "ont o",
+ "▁A dam",
+ "▁Ad am",
+ "▁l aid",
+ "▁la id",
+ "▁r ig",
+ "▁ri g",
+ "▁ rig",
+ "▁det ailed",
+ "▁detail ed",
+ "▁im plements",
+ "▁implement s",
+ "▁impl ements",
+ "▁univers ity",
+ "▁H y",
+ "▁ Hy",
+ "▁g rid",
+ "▁gr id",
+ "▁gri d",
+ "▁ grid",
+ "▁reg ions",
+ "▁region s",
+ "St op",
+ "S top",
+ "▁s lot",
+ "▁sl ot",
+ "▁ slot",
+ "▁ang ry",
+ "▁- =",
+ "▁wait ed",
+ "▁wa ited",
+ "Ver t",
+ "V ert",
+ "\": \"",
+ "\" :\"",
+ "▁e lem",
+ "▁el em",
+ "▁ele m",
+ "▁ elem",
+ "▁r ég",
+ "▁ré g",
+ "ow ed",
+ "owe d",
+ "o wed",
+ "Mem ber",
+ "Me mber",
+ "M ember",
+ "▁r atio",
+ "▁rat io",
+ "▁ ratio",
+ "is en",
+ "ise n",
+ "i sen",
+ "▁L em",
+ "▁Le m",
+ "ge ry",
+ "ger y",
+ "g ery",
+ "▁c ream",
+ "▁cre am",
+ "▁ét ait",
+ "▁ était",
+ "▁g eb",
+ "▁ge b",
+ "▁ geb",
+ "un ique",
+ "uni que",
+ "▁D eb",
+ "▁De b",
+ "▁f actory",
+ "▁fact ory",
+ "▁factor y",
+ "▁ factory",
+ "ż e",
+ "d ialog",
+ "▁Con fig",
+ "▁Conf ig",
+ "▁ Config",
+ "Sy nc",
+ "S ync",
+ "an gers",
+ "ang ers",
+ "ange rs",
+ "anger s",
+ "▁gover ning",
+ "▁govern ing",
+ "▁H un",
+ "▁Hu n",
+ "Sp ace",
+ "S pace",
+ "▁j est",
+ "▁je st",
+ "ic ious",
+ "ici ous",
+ "icio us",
+ "▁em phas",
+ "▁emp has",
+ "um ps",
+ "ump s",
+ "▁E sp",
+ "▁Es p",
+ "▁ Esp",
+ "▁s ul",
+ "▁su l",
+ "▁histor ical",
+ "▁historic al",
+ "ij a",
+ "i ja",
+ "▁l ying",
+ "▁ly ing",
+ "▁ lying",
+ "▁St eve",
+ "▁Ste ve",
+ "▁me asures",
+ "▁measure s",
+ "▁meas ures",
+ "os to",
+ "ost o",
+ "o sto",
+ "? ”",
+ "▁p ocket",
+ "▁poc ket",
+ "▁S at",
+ "▁Sa t",
+ "▁p itch",
+ "▁pit ch",
+ "▁n atur",
+ "▁nat ur",
+ "▁hum ans",
+ "▁human s",
+ "▁Sim on",
+ "▁Si mon",
+ "ad ores",
+ "ado res",
+ "ador es",
+ "(\" \\",
+ "( \"\\",
+ "in king",
+ "ink ing",
+ "▁ex pos",
+ "▁exp os",
+ "mat erial",
+ "mate rial",
+ "m aterial",
+ "▁app arently",
+ "▁apparent ly",
+ "▁appar ently",
+ "▁C amb",
+ "▁Cam b",
+ "▁Ca mb",
+ "▁B ox",
+ "▁Bo x",
+ "▁ Box",
+ "▁s paces",
+ "▁sp aces",
+ "▁space s",
+ "ex ists",
+ "exist s",
+ "▁act ing",
+ "▁ac ting",
+ "OR Y",
+ "зо ва",
+ "Go od",
+ "G ood",
+ "ien ne",
+ "i enne",
+ "▁William s",
+ "▁f ruit",
+ "▁fr uit",
+ "▁fru it",
+ "ie ra",
+ "ier a",
+ "i era",
+ "▁L im",
+ "▁Li m",
+ "▁ Lim",
+ "▁t rait",
+ "▁tr ait",
+ "▁tra it",
+ "▁ trait",
+ "▁art ists",
+ "▁artist s",
+ "▁ab sor",
+ "▁abs or",
+ "ra it",
+ "rai t",
+ "r ait",
+ "LO AD",
+ "▁mov ies",
+ "▁movie s",
+ "▁d ynamic",
+ "▁dynam ic",
+ "▁dyn amic",
+ "▁ dynamic",
+ "as ts",
+ "ast s",
+ "a sts",
+ "▁In teger",
+ "▁ Integer",
+ "▁sm oke",
+ "п і",
+ "an gel",
+ "ang el",
+ "ange l",
+ ">( \"",
+ "> (\"",
+ "▁in strument",
+ "▁instr ument",
+ "▁f uel",
+ "▁fue l",
+ "▁fu el",
+ "но ї",
+ "atal ogue",
+ "atalog ue",
+ "▁s erial",
+ "▁se rial",
+ "▁ser ial",
+ "▁ serial",
+ "File s",
+ "Fil es",
+ "Fi les",
+ "F iles",
+ "▁bath room",
+ "il o",
+ "i lo",
+ "es to",
+ "est o",
+ "e sto",
+ "▁p m",
+ "▁ pm",
+ "ent ials",
+ "ential s",
+ "enti als",
+ "▁On line",
+ "wh ite",
+ "▁t ips",
+ "▁tip s",
+ "▁ti ps",
+ "▁cap able",
+ "Fi g",
+ "F ig",
+ "T V",
+ "▁о н",
+ "▁ он",
+ "k é",
+ "bit r",
+ "bi tr",
+ "b itr",
+ "Map ping",
+ "Ma pping",
+ "M apping",
+ "▁t ak",
+ "▁ta k",
+ "ю щи",
+ "в ля",
+ ")\" ,",
+ ") \",",
+ "▁K arl",
+ "▁Kar l",
+ "▁Ka rl",
+ "▁H uman",
+ "▁Hu man",
+ "▁Hum an",
+ "▁P ot",
+ "▁Po t",
+ "▁rep resents",
+ "▁represent s",
+ "▁cons istent",
+ "▁consist ent",
+ "_ (",
+ "we n",
+ "w en",
+ "▁R ose",
+ "▁Ro se",
+ "▁Ros e",
+ "la w",
+ "l aw",
+ "▁F ROM",
+ "▁FR OM",
+ "▁ FROM",
+ "▁beg ins",
+ "▁begin s",
+ "▁e dit",
+ "▁ed it",
+ "▁ edit",
+ "▁mount ain",
+ "▁ch apter",
+ "▁chap ter",
+ "▁wonder ed",
+ "▁indust rial",
+ "▁M ajor",
+ "▁Ma jor",
+ "▁Maj or",
+ "▁g es",
+ "▁ge s",
+ "▁ ges",
+ "▁direct ed",
+ "▁dire cted",
+ "er os",
+ "ero s",
+ "e ros",
+ "▁W ild",
+ "▁Wil d",
+ "▁Wi ld",
+ "li ament",
+ "lia ment",
+ "Bo ok",
+ "B ook",
+ "user name",
+ "ho t",
+ "h ot",
+ "▁n am",
+ "▁na m",
+ "▁ nam",
+ "▁le ague",
+ "br a",
+ "b ra",
+ "ко н",
+ "к он",
+ "▁T al",
+ "▁Ta l",
+ "▁В а",
+ "▁ex ports",
+ "▁exp orts",
+ "▁export s",
+ "▁ exports",
+ "( @",
+ "▁sh aring",
+ "▁shar ing",
+ "▁sha ring",
+ "▁T ro",
+ "▁Tr o",
+ "ś ć",
+ "ues day",
+ "yl v",
+ "y lv",
+ "▁gu itar",
+ "el en",
+ "ele n",
+ "e len",
+ "Se lection",
+ "Select ion",
+ "S election",
+ "▁conf ident",
+ "ry pto",
+ "rypt o",
+ "▁h ors",
+ "▁hor s",
+ "▁ho rs",
+ "ed itor",
+ "edit or",
+ "edi tor",
+ "▁should ers",
+ "▁shoulder s",
+ "get Name",
+ "en cing",
+ "enc ing",
+ "enci ng",
+ "SE LECT",
+ "SEL ECT",
+ "в ши",
+ "▁kind s",
+ "▁kin ds",
+ "▁W el",
+ "▁We l",
+ "▁pur poses",
+ "▁purpose s",
+ "Mat rix",
+ "in valid",
+ "▁own ers",
+ "▁owner s",
+ "▁ owners",
+ "▁Rec ords",
+ "▁Record s",
+ "▁ Records",
+ "▁Pro cess",
+ "▁ Process",
+ "▁c hat",
+ "▁ch at",
+ "▁cha t",
+ "▁ chat",
+ "▁D or",
+ "▁Do r",
+ "▁b in",
+ "▁bi n",
+ "▁ bin",
+ "re dit",
+ "red it",
+ "r edit",
+ "oi re",
+ "oir e",
+ "o ire",
+ "▁T otal",
+ "▁To tal",
+ "▁Tot al",
+ "▁ Total",
+ "▁F amily",
+ "▁Famil y",
+ "▁ Family",
+ "AR Y",
+ "▁b read",
+ "▁br ead",
+ "▁bre ad",
+ "▁ bread",
+ "▁com pre",
+ "▁comp re",
+ "▁compr e",
+ "▁sh oes",
+ "▁shoe s",
+ "▁r az",
+ "▁ra z",
+ "▁ raz",
+ "▁tr ace",
+ "▁tra ce",
+ "▁ trace",
+ "ne j",
+ "n ej",
+ "or ted",
+ "ort ed",
+ "orte d",
+ "h n",
+ "▁pro cedure",
+ "▁proced ure",
+ "pro perties",
+ "pl ier",
+ "▁h ero",
+ "▁he ro",
+ "▁her o",
+ "▁ hero",
+ "pan el",
+ "pa nel",
+ "p anel",
+ "▁mark ed",
+ "▁mar ked",
+ "▁wor ried",
+ "\\ |",
+ "pt s",
+ "p ts",
+ "▁S upport",
+ "▁Sup port",
+ "▁Supp ort",
+ "▁ Support",
+ "▁ser ving",
+ "▁serv ing",
+ "F ail",
+ "▁dis appoint",
+ "▁Sc ot",
+ "▁ple asure",
+ "▁j udge",
+ "▁jud ge",
+ "▁judg e",
+ "ze ich",
+ "▁for ever",
+ "▁fore ver",
+ "▁Ze it",
+ "uo us",
+ "u ous",
+ "in ent",
+ "ine nt",
+ "inen t",
+ "i nent",
+ "▁d w",
+ "▁ dw",
+ "▁w aren",
+ "▁war en",
+ "▁wa ren",
+ "▁ware n",
+ "▁fl ash",
+ "▁ flash",
+ "▁tro ops",
+ "▁dr ugs",
+ "▁dru gs",
+ "▁drug s",
+ "▁d iam",
+ "▁di am",
+ "▁dia m",
+ ". ~",
+ "im p",
+ "i mp",
+ "in ned",
+ "inn ed",
+ "▁E V",
+ "▁ EV",
+ "St ruct",
+ "Str uct",
+ "▁just ice",
+ "▁offic ials",
+ "▁official s",
+ "ff ff",
+ "fff f",
+ "f fff",
+ "▁Com mon",
+ "▁Comm on",
+ "▁ Common",
+ "▁C at",
+ "▁Ca t",
+ "▁ Cat",
+ "▁tom orrow",
+ "▁é l",
+ "▁ él",
+ "Text ure",
+ "Te xture",
+ "qp oint",
+ "q point",
+ "▁F ried",
+ "▁Fr ied",
+ "▁T erm",
+ "▁Te rm",
+ "▁Ter m",
+ "▁ Term",
+ "pgf qpoint",
+ "▁n em",
+ "▁ne m",
+ "▁ nem",
+ "no rm",
+ "nor m",
+ "n orm",
+ "▁hard ly",
+ "od a",
+ "o da",
+ "ze ta",
+ "zet a",
+ "z eta",
+ "em ic",
+ "emi c",
+ "e mic",
+ "▁по лу",
+ "▁пол у",
+ "▁lo aded",
+ "▁load ed",
+ "▁ loaded",
+ "ke s",
+ "k es",
+ "ci ó",
+ "c ió",
+ "▁f ool",
+ "▁fo ol",
+ "▁foo l",
+ "▁t rick",
+ "▁tr ick",
+ "▁tri ck",
+ "▁d st",
+ "▁ds t",
+ "▁ dst",
+ "Fin d",
+ "Fi nd",
+ "F ind",
+ "▁в се",
+ "}} ,",
+ "} },",
+ "▁frame work",
+ "▁ framework",
+ "▁mer ely",
+ "▁mere ly",
+ "▁un ion",
+ "▁ union",
+ "▁Ed ward",
+ "ri f",
+ "r if",
+ "Fl ag",
+ "F lag",
+ "▁cris is",
+ "▁cri sis",
+ "▁fin ite",
+ "▁ finite",
+ "▁l ol",
+ "▁lo l",
+ "▁K im",
+ "▁Ki m",
+ "на та",
+ "sin ce",
+ "s ince",
+ "▁com pat",
+ "▁comp at",
+ "▁ compat",
+ "▁p ert",
+ "▁per t",
+ "▁pe rt",
+ "▁ pert",
+ "ib ilities",
+ "ibil ities",
+ "▁tamb ién",
+ "ib li",
+ "▁t een",
+ "▁te en",
+ "▁ teen",
+ "▁sym pt",
+ "or al",
+ "ora l",
+ "o ral",
+ "de rs",
+ "der s",
+ "d ers",
+ "ot te",
+ "ott e",
+ "п ри",
+ "▁J ane",
+ "▁Jan e",
+ "▁Ja ne",
+ "▁original ly",
+ "▁origin ally",
+ "▁thro at",
+ "ma g",
+ "m ag",
+ "su p",
+ "s up",
+ "un i",
+ "u ni",
+ "$ $",
+ "▁L ibrary",
+ "▁ Library",
+ "▁att acks",
+ "▁attack s",
+ "in gen",
+ "ing en",
+ "inge n",
+ "(' /",
+ "▁h es",
+ "▁he s",
+ "▁ hes",
+ "co in",
+ "c oin",
+ "oun ce",
+ "▁Academ y",
+ "MOD ULE",
+ "is ms",
+ "ism s",
+ "▁A dv",
+ "▁Ad v",
+ "▁ Adv",
+ "▁B ol",
+ "▁Bo l",
+ "▁inc ident",
+ ")^ {",
+ ") ^{",
+ "▁b ij",
+ "▁bi j",
+ "▁R ome",
+ "▁Rom e",
+ "▁Ro me",
+ "▁It aly",
+ "▁Ital y",
+ "ev ents",
+ "event s",
+ "even ts",
+ "▁F ern",
+ "▁Fe rn",
+ "▁Fer n",
+ "▁b er",
+ "▁be r",
+ "▁ ber",
+ "▁sil ent",
+ "▁p ier",
+ "▁pie r",
+ "▁pi er",
+ "▁Y O",
+ "▁pl ain",
+ "▁ plain",
+ "B as",
+ "▁p ill",
+ "▁pi ll",
+ "▁pil l",
+ "ra se",
+ "ras e",
+ "r ase",
+ "▁car rying",
+ "▁carry ing",
+ "▁re sp",
+ "▁r esp",
+ "▁res p",
+ "▁ resp",
+ "ну ю",
+ "▁typ ical",
+ "Wrap per",
+ "W rapper",
+ "▁g au",
+ "▁ga u",
+ "▁chem ical",
+ "▁h al",
+ "▁ha l",
+ "▁ hal",
+ "th row",
+ "Cl uster",
+ "▁G ab",
+ "▁Ga b",
+ "▁G irl",
+ "▁Gi rl",
+ "▁Gir l",
+ "qu ir",
+ "▁A rg",
+ "▁Ar g",
+ "▁ Arg",
+ "▁rel ief",
+ "▁relie f",
+ "▁reli ef",
+ "▁В е",
+ "d m",
+ "▁fr ustr",
+ "▁fru str",
+ "\\ %",
+ "▁st ores",
+ "▁store s",
+ "▁stor es",
+ "▁sto res",
+ "▁bott le",
+ "▁bot tle",
+ "▁L ew",
+ "▁Le w",
+ "tw o",
+ "t wo",
+ "st ad",
+ "sta d",
+ "▁che ek",
+ "▁concern s",
+ "▁concer ns",
+ "▁help ful",
+ "▁co verage",
+ "▁cover age",
+ "is i",
+ "i si",
+ "AD D",
+ "A DD",
+ "as ync",
+ "asy nc",
+ "a sync",
+ "▁approxim ately",
+ "▁approx imately",
+ "▁approximate ly",
+ "if fer",
+ "iff er",
+ "iffe r",
+ "ho ok",
+ "h ook",
+ "▁e num",
+ "▁en um",
+ "▁ enum",
+ "ov á",
+ "o vá",
+ "▁e vil",
+ "▁ev il",
+ "▁const antly",
+ "▁constant ly",
+ "ap ply",
+ "app ly",
+ "▁si è",
+ "▁pract ices",
+ "▁practice s",
+ "▁te achers",
+ "▁teach ers",
+ "▁teacher s",
+ "▁S n",
+ "▁ Sn",
+ "▁A wards",
+ "▁Award s",
+ "▁Aw ards",
+ "▁sub stant",
+ "▁subst ant",
+ "▁$ .",
+ "▁ $.",
+ "d k",
+ "▁m ob",
+ "▁mo b",
+ "▁ mob",
+ "▁ing red",
+ "ve re",
+ "ver e",
+ "v ere",
+ "Mult i",
+ "пе р",
+ "п ер",
+ "st al",
+ "sta l",
+ "s tal",
+ "ya rd",
+ "yar d",
+ "y ard",
+ "requ ired",
+ "require d",
+ "ve ment",
+ "v ement",
+ "▁int elligence",
+ "▁intellig ence",
+ "▁th inks",
+ "▁think s",
+ "▁thin ks",
+ "▁person ally",
+ "▁personal ly",
+ "▁tr ained",
+ "▁tra ined",
+ "▁train ed",
+ "▁ trained",
+ "or ney",
+ "orn ey",
+ "orne y",
+ ") ",
+ "gg ed",
+ "g ged",
+ "E INVAL",
+ "ar na",
+ "arn a",
+ "▁Ham ilton",
+ "mer ce",
+ "ek t",
+ "e kt",
+ "O F",
+ ") [",
+ "ru g",
+ "r ug",
+ "ic ión",
+ "ici ón",
+ "ició n",
+ "i ción",
+ "▁sur vey",
+ "▁surv ey",
+ "▁surve y",
+ "nes day",
+ "▁p ag",
+ "▁pa g",
+ "▁ pag",
+ "▁bound ary",
+ "▁quant um",
+ "▁draw ing",
+ "▁vol unte",
+ "▁volunt e",
+ "▁W ord",
+ "▁Wo rd",
+ "▁Wor d",
+ "▁ Word",
+ "sk y",
+ "s ky",
+ "▁G reg",
+ "▁Gr eg",
+ "▁Gre g",
+ "co ll",
+ "col l",
+ "c oll",
+ "hi de",
+ "hid e",
+ "h ide",
+ "▁sw im",
+ "▁reve aled",
+ "▁reveal ed",
+ "ad v",
+ "a dv",
+ "д я",
+ ".\" );",
+ ".\") ;",
+ ". \");",
+ "▁ex plan",
+ "▁expl an",
+ "▁exp lan",
+ "▁Cur rent",
+ "▁ Current",
+ "▁got ten",
+ "▁f alling",
+ "▁fall ing",
+ "▁fal ling",
+ "▁cont ained",
+ "▁contain ed",
+ "UN D",
+ "U ND",
+ "▁Sh ould",
+ "▁ Should",
+ "▁k illing",
+ "▁kill ing",
+ "▁kil ling",
+ "▁aspect s",
+ "ic ted",
+ "ict ed",
+ "i cted",
+ "▁P aram",
+ "▁Par am",
+ "▁Pa ram",
+ "▁Para m",
+ "▁ Param",
+ "\", \r",
+ "\" ,\r",
+ "TI ON",
+ "T ION",
+ ")) ;\r",
+ ")); \r",
+ ") );\r",
+ "▁I ran",
+ "▁Ir an",
+ "▁Ira n",
+ "be it",
+ "▁B u",
+ "▁ Bu",
+ "▁[ ],",
+ "▁[] ,",
+ "▁ [],",
+ "SS ION",
+ "S SION",
+ "▁M ah",
+ "▁Ma h",
+ "▁res olution",
+ "▁b oss",
+ "▁bo ss",
+ "▁bos s",
+ "l g",
+ "ch or",
+ "cho r",
+ "c hor",
+ "▁Un ter",
+ "▁de bt",
+ "▁deb t",
+ "▁v id",
+ "▁vi d",
+ "▁ vid",
+ "gi e",
+ "g ie",
+ "▁u no",
+ "▁un o",
+ "▁ uno",
+ "C B",
+ "pl om",
+ "plo m",
+ "LIC ENSE",
+ "L ICENSE",
+ "▁K enn",
+ "▁Ke nn",
+ "▁Ken n",
+ "▁fin ns",
+ "ON G",
+ "O NG",
+ "▁some what",
+ "▁a ctor",
+ "▁act or",
+ "▁ac tor",
+ "▁ actor",
+ "▁St atus",
+ "▁Stat us",
+ "▁ Status",
+ "▁prob ability",
+ "f b",
+ "▁ch art",
+ "▁char t",
+ "▁cha rt",
+ "▁ chart",
+ "▁st ands",
+ "▁stand s",
+ "▁stan ds",
+ "pol icy",
+ "▁o nder",
+ "▁on der",
+ "▁onde r",
+ "▁ onder",
+ "tab ular",
+ "▁A sh",
+ "▁As h",
+ "▁bo ost",
+ "▁ boost",
+ "▁des per",
+ "▁desp er",
+ "mon th",
+ "mont h",
+ "▁al ert",
+ "▁ale rt",
+ "▁ alert",
+ "▁su ite",
+ "▁suit e",
+ "▁ suite",
+ "▁g én",
+ "▁gé n",
+ "▁v acc",
+ "▁va cc",
+ "▁vac c",
+ "▁H as",
+ "▁Ha s",
+ "▁ Has",
+ "Ma sk",
+ "M ask",
+ "▁Th ursday",
+ "▁pro ved",
+ "▁pr oved",
+ "▁prov ed",
+ "▁prove d",
+ "▁N el",
+ "▁Ne l",
+ "▁m oral",
+ "▁mor al",
+ "▁mo ral",
+ "▁j a",
+ "▁ ja",
+ "au er",
+ "a uer",
+ "co dec",
+ "code c",
+ "cod ec",
+ "▁in stant",
+ "▁inst ant",
+ "am ps",
+ "amp s",
+ "▁mil k",
+ "WO RD",
+ "W ORD",
+ "▁ Ö",
+ "Em ail",
+ "E mail",
+ "Element s",
+ "El ements",
+ "Elem ents",
+ "▁for ma",
+ "▁form a",
+ "Fr ee",
+ "F ree",
+ "MA P",
+ "M AP",
+ "▁ Ж",
+ "sy m",
+ "s ym",
+ "▁т и",
+ "▁ ти",
+ "▁E conom",
+ "▁Ec onom",
+ "▁V i",
+ "▁ Vi",
+ "▁Col umb",
+ "▁_ ,",
+ "▁ _,",
+ "or et",
+ "ore t",
+ "o ret",
+ "Se qu",
+ "Seq u",
+ "S equ",
+ "pl an",
+ "p lan",
+ "▁f requency",
+ "▁frequ ency",
+ "▁ frequency",
+ "ir ement",
+ "ire ment",
+ "▁ass umed",
+ "▁assum ed",
+ "▁assume d",
+ "▁C a",
+ "▁B it",
+ "▁Bi t",
+ "▁ Bit",
+ "▁ко ман",
+ "▁ком ан",
+ "▁sm ell",
+ "Se curity",
+ "Sec urity",
+ "▁a qu",
+ "▁ aqu",
+ "oo r",
+ "o or",
+ "pr ice",
+ "p rice",
+ "in ity",
+ "init y",
+ "ini ty",
+ "▁a xis",
+ "▁ax is",
+ "▁ axis",
+ "re lease",
+ "▁res olve",
+ "▁ resolve",
+ "▁t ears",
+ "▁te ars",
+ "▁tea rs",
+ "▁tear s",
+ "▁b other",
+ "▁bo ther",
+ "▁both er",
+ "▁bot her",
+ "▁Comm unity",
+ "▁Commun ity",
+ "▁register ed",
+ "▁re volution",
+ "▁rev olution",
+ "▁revol ution",
+ "? .",
+ "▁version s",
+ "▁vers ions",
+ "▁ versions",
+ "%% %%",
+ "yd ro",
+ "y dro",
+ "Su ccess",
+ "▁W in",
+ "▁Wi n",
+ "▁ Win",
+ "▁B oy",
+ "▁Bo y",
+ "▁D ub",
+ "▁Du b",
+ "▁k w",
+ "▁ kw",
+ "▁n och",
+ "▁no ch",
+ "▁char ges",
+ "▁charg es",
+ "▁charge s",
+ "ar ios",
+ "ari os",
+ "ario s",
+ "a rios",
+ "ua r",
+ "u ar",
+ "; &",
+ "▁hab ía",
+ "( `",
+ "▁t x",
+ "▁ tx",
+ "el ve",
+ "▁a ños",
+ "▁año s",
+ "▁m ath",
+ "▁mat h",
+ "▁ma th",
+ "▁ math",
+ "▁Al f",
+ "▁F und",
+ "▁Fun d",
+ "▁Fu nd",
+ "▁man ifest",
+ "▁manif est",
+ "▁att ached",
+ "▁attach ed",
+ "▁spirit ual",
+ "▁Alex ander",
+ "▁Alexand er",
+ "un es",
+ "une s",
+ "u nes",
+ "▁s eed",
+ "▁se ed",
+ "▁see d",
+ "▁ seed",
+ "▁Н о",
+ "▁mag azine",
+ "▁magaz ine",
+ "▁e igen",
+ "▁о бра",
+ "▁об ра",
+ "▁ обра",
+ "e a",
+ "▁P H",
+ "▁ PH",
+ "sw ing",
+ "s wing",
+ "▁As ia",
+ "ј у",
+ "▁K IND",
+ "Ident ifier",
+ "on ce",
+ "▁al cohol",
+ "ці ї",
+ "st yles",
+ "style s",
+ "sty les",
+ "assert Equal",
+ "▁R a",
+ "гра фи",
+ "▁mill ions",
+ "▁million s",
+ "▁ch unk",
+ "▁ chunk",
+ "де р",
+ "д ер",
+ "Pack age",
+ "US T",
+ "U ST",
+ "▁N othing",
+ "▁No thing",
+ "▁Not hing",
+ "▁ Nothing",
+ "(\" #",
+ "▁M id",
+ "▁Mi d",
+ "▁на ча",
+ "▁ нача",
+ "ł y",
+ "AA AA",
+ "▁la unched",
+ "▁launch ed",
+ "▁w ake",
+ "▁wa ke",
+ "▁ wake",
+ "▁gu ests",
+ "▁guest s",
+ "▁dif ferences",
+ "▁differ ences",
+ "▁difference s",
+ "ud i",
+ "u di",
+ "▁a id",
+ "▁ai d",
+ "▁ aid",
+ "▁S port",
+ "▁Sp ort",
+ "ul ator",
+ "ula tor",
+ "ex ecute",
+ "exec ute",
+ "execut e",
+ "pl ot",
+ "plo t",
+ "p lot",
+ "ch ing",
+ "chi ng",
+ "c hing",
+ "▁N orm",
+ "▁No rm",
+ "▁Nor m",
+ "▁ Norm",
+ "t m",
+ "\\ +",
+ "AR D",
+ "A RD",
+ "▁be er",
+ "▁п ід",
+ "▁пі д",
+ "IA L",
+ "I AL",
+ "st orage",
+ "sto rage",
+ "▁An na",
+ "▁Ann a",
+ "▁y ards",
+ "▁yard s",
+ "▁techn ique",
+ "▁o ù",
+ "at ten",
+ "att en",
+ "atte n",
+ "UN T",
+ "U NT",
+ "do n",
+ "d on",
+ "фо р",
+ "ф ор",
+ "▁h oping",
+ "▁hop ing",
+ "▁ho ping",
+ "▁vict ory",
+ "it at",
+ "ita t",
+ "i tat",
+ "▁signific antly",
+ "▁significant ly",
+ "▁pract ical",
+ "ij e",
+ "i je",
+ "▁exp ansion",
+ "▁expans ion",
+ "J S",
+ "ix els",
+ "ixel s",
+ "US ER",
+ "USE R",
+ "U SER",
+ "Sh ape",
+ "▁ext ent",
+ "li o",
+ "l io",
+ "▁p ued",
+ "▁pu ed",
+ "ol id",
+ "oli d",
+ "▁g am",
+ "▁ga m",
+ "▁s event",
+ "▁se vent",
+ "▁seven t",
+ "▁G a",
+ "▁ Ga",
+ "angu ages",
+ "anguage s",
+ "(( (",
+ "( ((",
+ "ъ л",
+ "▁Ex per",
+ "▁Exp er",
+ "▁ Exper",
+ "as ty",
+ "ast y",
+ "a sty",
+ "ri eg",
+ "rie g",
+ "r ieg",
+ "gi o",
+ "g io",
+ "od o",
+ "o do",
+ "▁col le",
+ "▁co lle",
+ "▁coll e",
+ "▁st ored",
+ "▁store d",
+ "▁stor ed",
+ "▁sto red",
+ "▁S che",
+ "▁Sch e",
+ "▁Sc he",
+ "▁ Sche",
+ "ist ant",
+ "ista nt",
+ "istan t",
+ "i stant",
+ "▁l ip",
+ "▁li p",
+ "B R",
+ "▁a ug",
+ "▁au g",
+ "▁ aug",
+ "▁S earch",
+ "▁Se arch",
+ "▁ Search",
+ ")= \\",
+ ") =\\",
+ "▁U r",
+ "▁s ole",
+ "▁so le",
+ "▁sol e",
+ "▁ sole",
+ "il lo",
+ "ill o",
+ "▁me hr",
+ "ki t",
+ "k it",
+ "▁in terior",
+ "▁inter ior",
+ "▁inte rior",
+ "LI ST",
+ "L IST",
+ "ad el",
+ "ade l",
+ "a del",
+ "▁shop ping",
+ "▁s lä",
+ "▁sl ä",
+ "You r",
+ "Y our",
+ "DI TION",
+ "D ITION",
+ "▁H ttp",
+ "▁ Http",
+ "ra ham",
+ "rah am",
+ "т ри",
+ "▁b rings",
+ "▁br ings",
+ "▁bring s",
+ "Re v",
+ "R ev",
+ "▁pro pag",
+ "▁prop ag",
+ "ity Engine",
+ "() ),",
+ "()) ,",
+ "( )),",
+ "▁ing år",
+ "▁Ir eland",
+ "▁Ire land",
+ "▁\" ./",
+ "▁\". /",
+ "▁H arr",
+ "▁Har r",
+ "▁Ha rr",
+ "▁ad min",
+ "▁adm in",
+ "▁ admin",
+ "en o",
+ "e no",
+ "▁k r",
+ "▁ kr",
+ "▁est á",
+ "▁pro ps",
+ "▁pr ops",
+ "▁prop s",
+ "▁ props",
+ "to k",
+ "t ok",
+ "om orph",
+ "▁affect ed",
+ "Ph one",
+ "▁deg rees",
+ "▁degree s",
+ "so me",
+ "som e",
+ "s ome",
+ "▁n in",
+ "▁ni n",
+ "EV ENT",
+ "▁inter action",
+ "▁inte raction",
+ "▁interact ion",
+ "▁T uesday",
+ "iter ator",
+ "▁N ob",
+ "▁No b",
+ "▁sc atter",
+ "uck et",
+ "uc ket",
+ "com plete",
+ "comp lete",
+ "▁d uty",
+ "▁du ty",
+ "▁dut y",
+ "▁answ ers",
+ "▁answer s",
+ "Pro gress",
+ "ee d",
+ "e ed",
+ "ро н",
+ "р он",
+ "▁v ie",
+ "▁vi e",
+ "▁de pos",
+ "▁dep os",
+ "▁p acket",
+ "▁pack et",
+ "▁pac ket",
+ "▁ packet",
+ "▁t ow",
+ "▁to w",
+ "▁de leg",
+ "▁del eg",
+ "▁ deleg",
+ "aud io",
+ "a udio",
+ "▁v ary",
+ "▁var y",
+ "▁va ry",
+ "▁m igr",
+ "▁mi gr",
+ "▁mig r",
+ "▁ migr",
+ "ф і",
+ "es a",
+ "e sa",
+ "Event s",
+ "Ev ents",
+ "Even ts",
+ "ha us",
+ "h aus",
+ "▁S av",
+ "▁Sa v",
+ "▁Port ug",
+ "▁с то",
+ "▁ст о",
+ "▁ сто",
+ "il ation",
+ "i lation",
+ "▁met adata",
+ "▁meta data",
+ "▁ metadata",
+ "la s",
+ "l as",
+ "▁a i",
+ "▁ ai",
+ "▁an ger",
+ "▁ang er",
+ "▁ange r",
+ "▁ anger",
+ "▁h am",
+ "▁ha m",
+ "▁ ham",
+ "▁A nal",
+ "▁An al",
+ "▁Ana l",
+ "▁ Anal",
+ "▁frequ ently",
+ "▁frequent ly",
+ "▁F ALSE",
+ "▁ FALSE",
+ "oc he",
+ "och e",
+ "o che",
+ "re z",
+ "r ez",
+ "▁V iet",
+ "▁Vi et",
+ "qu is",
+ "q uis",
+ "▁char ged",
+ "▁charg ed",
+ "▁charge d",
+ "ä s",
+ "▁P ath",
+ "▁Pat h",
+ "▁Pa th",
+ "▁ Path",
+ "▁accur ate",
+ "▁Pl us",
+ "▁ Plus",
+ "ke it",
+ "▁In put",
+ "▁ Input",
+ "wh en",
+ "whe n",
+ "w hen",
+ "er as",
+ "era s",
+ "e ras",
+ "▁во з",
+ "▁de rived",
+ "▁der ived",
+ "▁deriv ed",
+ "▁derive d",
+ "aj e",
+ "a je",
+ "▁H ad",
+ "▁Ha d",
+ "ur en",
+ "ure n",
+ "u ren",
+ "ó r",
+ "}= \\",
+ "} =\\",
+ "ur eau",
+ "ure au",
+ "al and",
+ "ala nd",
+ "a land",
+ "Execut ion",
+ "Exec ution",
+ "ed en",
+ "ede n",
+ "e den",
+ "▁se eking",
+ "▁see king",
+ "▁seek ing",
+ "ch anged",
+ "change d",
+ "chan ged",
+ "▁t rem",
+ "▁tr em",
+ "▁tre m",
+ "ск у",
+ "с ку",
+ "▁G eme",
+ "▁Ge me",
+ "▁Gem e",
+ "in ating",
+ "ina ting",
+ "▁column s",
+ "▁ columns",
+ "E P",
+ "▁inj ury",
+ "end ent",
+ "ende nt",
+ "enden t",
+ "▁he aded",
+ "▁head ed",
+ "▁ headed",
+ "AS E",
+ "A SE",
+ "▁Mus lim",
+ "▁cl imate",
+ "▁clim ate",
+ "▁f ake",
+ "▁fa ke",
+ "▁ fake",
+ "CM D",
+ "C MD",
+ "ј и",
+ "▁Ar ts",
+ "▁Art s",
+ "fe ction",
+ "fect ion",
+ "f ection",
+ "▁p it",
+ "▁pi t",
+ "▁ pit",
+ "> \\",
+ "an al",
+ "ana l",
+ "a nal",
+ "Se ction",
+ "S ection",
+ "pl us",
+ "ü t",
+ "▁em bed",
+ "▁emb ed",
+ "▁ embed",
+ "▁st rings",
+ "▁str ings",
+ "▁string s",
+ "▁ strings",
+ "Be fore",
+ "B efore",
+ "pro c",
+ "pr oc",
+ "p roc",
+ "▁с по",
+ "▁сп о",
+ "▁ спо",
+ "tr l",
+ "t rl",
+ "v r",
+ "Back ground",
+ "log ger",
+ "ag raph",
+ "agr aph",
+ "agra ph",
+ "a graph",
+ "ie st",
+ "ies t",
+ "i est",
+ "▁good s",
+ "bat ch",
+ "b atch",
+ "▁opt ional",
+ "▁option al",
+ "▁ optional",
+ "▁Tay lor",
+ "▁recogn ize",
+ "wal k",
+ "w alk",
+ "▁H it",
+ "▁Hi t",
+ "▁ Hit",
+ "▁Eliz abeth",
+ "} :",
+ "▁care ful",
+ "кра ї",
+ "▁loc ations",
+ "▁location s",
+ "▁struct ures",
+ "▁structure s",
+ "▁d isk",
+ "▁dis k",
+ "▁di sk",
+ "▁ disk",
+ "▁sh ips",
+ "▁ship s",
+ "▁ ships",
+ "▁s uo",
+ "▁su o",
+ "▁s owie",
+ "▁so wie",
+ "▁sow ie",
+ "▁E ss",
+ "▁Es s",
+ "▁H ash",
+ "▁Ha sh",
+ "▁Has h",
+ "▁ Hash",
+ "▁reason able",
+ "▁More over",
+ "▁form ula",
+ "▁C entre",
+ "▁Cent re",
+ "▁res idents",
+ "▁resident s",
+ "▁resid ents",
+ "R S",
+ "Id s",
+ "I ds",
+ "▁K now",
+ "▁Kn ow",
+ "▁t rib",
+ "▁tr ib",
+ "▁tri b",
+ "▁r és",
+ "▁ré s",
+ "▁s table",
+ "▁st able",
+ "▁sta ble",
+ "▁stab le",
+ "▁ stable",
+ "▁W ould",
+ "▁Wo uld",
+ "▁ Would",
+ "▁break ing",
+ "▁bre aking",
+ "▁ breaking",
+ "▁me al",
+ "▁p hen",
+ "▁ph en",
+ "▁f el",
+ "▁fe l",
+ "▁ fel",
+ "▁F red",
+ "▁Fr ed",
+ "▁Fre d",
+ "Aut hor",
+ "Auth or",
+ "▁c apture",
+ "▁capt ure",
+ "▁ capture",
+ "op ts",
+ "opt s",
+ "o pts",
+ "▁every where",
+ "▁s que",
+ "▁squ e",
+ "▁sq ue",
+ "▁m oder",
+ "▁mod er",
+ "▁mo der",
+ "▁mode r",
+ "set up",
+ "▁S upp",
+ "▁Su pp",
+ "▁Sup p",
+ "▁ Supp",
+ "▁when ever",
+ "▁whe never",
+ "{ (",
+ "wa rt",
+ "war t",
+ "w art",
+ "▁t oe",
+ "▁to e",
+ "Pre fix",
+ "Pref ix",
+ "P refix",
+ "ho u",
+ "h ou",
+ "ga ge",
+ "g age",
+ "> \"",
+ "▁f rag",
+ "▁fr ag",
+ "▁fra g",
+ "▁ frag",
+ "▁The orem",
+ "mem ory",
+ "▁cont ents",
+ "▁content s",
+ "▁conten ts",
+ "▁ contents",
+ "do cs",
+ "doc s",
+ "} '",
+ "▁Ir ish",
+ "The n",
+ "Th en",
+ "T hen",
+ "aa ts",
+ "aat s",
+ "a ats",
+ "Sa ve",
+ "S ave",
+ "▁a gency",
+ "▁ag ency",
+ "▁и ме",
+ "▁им е",
+ "до ва",
+ "дов а",
+ "▁F unction",
+ "▁Fun ction",
+ "▁ Function",
+ "N N",
+ "dest roy",
+ "▁M essage",
+ "▁Mess age",
+ "▁ Message",
+ "▁c ancel",
+ "▁can cel",
+ "▁ cancel",
+ "▁super ior",
+ "▁e c",
+ "▁ ec",
+ "▁liter ature",
+ "▁P ART",
+ "▁PA RT",
+ "▁PAR T",
+ "▁ PART",
+ "I l",
+ "▁C ab",
+ "▁Ca b",
+ "eng ine",
+ "▁b asket",
+ "▁bas ket",
+ "wor th",
+ "wort h",
+ "w orth",
+ "▁S el",
+ "▁Se l",
+ "f etch",
+ "▁St adt",
+ "▁Stad t",
+ "▁Sta dt",
+ "▁К и",
+ "▁con j",
+ "▁se iner",
+ "▁sein er",
+ "▁seine r",
+ "▁sei ner",
+ "▁conf irmed",
+ "▁confirm ed",
+ "▁Ar gent",
+ "▁Arg ent",
+ "am ar",
+ "ama r",
+ "a mar",
+ "pgf path",
+ "▁strugg le",
+ "Pat tern",
+ "▁M iddle",
+ "it an",
+ "ita n",
+ "i tan",
+ "▁m oon",
+ "▁mo on",
+ "or ough",
+ "oro ugh",
+ "o rough",
+ "▁Cath olic",
+ "▁str uck",
+ "▁stru ck",
+ "] ->",
+ "▁we apon",
+ "▁weap on",
+ "▁su bst",
+ "▁sub st",
+ "▁subs t",
+ "▁inst ructions",
+ "▁instruct ions",
+ "▁instruction s",
+ "▁occ as",
+ "▁oc cas",
+ "prote cted",
+ "▁L ess",
+ "▁Le ss",
+ "▁Les s",
+ "▁ Less",
+ "▁b atch",
+ "▁bat ch",
+ "▁ batch",
+ "▁con tra",
+ "▁cont ra",
+ "▁contr a",
+ "▁de ck",
+ "▁dec k",
+ "▁ deck",
+ "▁ign ored",
+ "▁ignore d",
+ "▁ignor ed",
+ "▁ref used",
+ "▁refuse d",
+ "tr igger",
+ "▁crim inal",
+ "G A",
+ "ol ly",
+ "oll y",
+ "▁B ell",
+ "▁Be ll",
+ "▁Bel l",
+ "▁ Ю",
+ "for ward",
+ "▁p refix",
+ "▁pre fix",
+ "▁pref ix",
+ "▁ prefix",
+ "▁im mediate",
+ "▁immedi ate",
+ "▁as signed",
+ "▁ass igned",
+ "▁assign ed",
+ "▁e lected",
+ "▁elect ed",
+ "▁ele cted",
+ "▁to night",
+ "▁ton ight",
+ "▁D ies",
+ "▁Die s",
+ "▁Di es",
+ "▁B each",
+ "▁Be ach",
+ "▁pre ced",
+ "▁prec ed",
+ "ow ał",
+ "owa ł",
+ "▁gal ax",
+ "▁log ic",
+ "en za",
+ "enz a",
+ "▁Cap tain",
+ "▁Capt ain",
+ "▁H ay",
+ "▁Ha y",
+ "▁f acts",
+ "▁fact s",
+ "▁fac ts",
+ "▁н и",
+ "▁ ни",
+ "t é",
+ "▁s b",
+ "▁ sb",
+ "op ed",
+ "ope d",
+ "o ped",
+ "▁com bat",
+ "▁comb at",
+ "▁expl ore",
+ "▁explo re",
+ "▁( -",
+ "▁ (-",
+ "Load er",
+ "Lo ader",
+ "▁Wil son",
+ "▁l ocked",
+ "▁loc ked",
+ "▁lock ed",
+ "▁ locked",
+ ": ",
+ "▁O d",
+ "▁P rote",
+ "▁Pro te",
+ "▁Pr ote",
+ "▁ Prote",
+ "▁dis abled",
+ "▁disable d",
+ "▁ disabled",
+ "▁h atte",
+ "▁hat te",
+ "▁sh out",
+ "▁con structor",
+ "▁construct or",
+ "▁constru ctor",
+ "▁ constructor",
+ "б і",
+ "▁t ras",
+ "▁tr as",
+ "▁tra s",
+ "▁ tras",
+ "▁F ather",
+ "▁Fa ther",
+ "▁Fat her",
+ "▁ad j",
+ "▁ adj",
+ "▁Carol ina",
+ "▁F ood",
+ "▁Fo od",
+ "ba d",
+ "b ad",
+ "at ore",
+ "ator e",
+ "ato re",
+ "param eters",
+ "parameter s",
+ "▁F ull",
+ "▁Fu ll",
+ "▁ Full",
+ "[ -",
+ "▁\" #",
+ "▁T ry",
+ "▁Tr y",
+ "▁ Try",
+ "сь кої",
+ "сько ї",
+ "▁ex haust",
+ "▁sc roll",
+ "▁scr oll",
+ "▁ scroll",
+ "_ ;",
+ "Wh o",
+ "W ho",
+ "▁deliver ed",
+ "▁re ferred",
+ "▁refer red",
+ "▁pro spect",
+ "▁pros pect",
+ "sc an",
+ "s can",
+ "▁mod ified",
+ "▁ modified",
+ "Gener ator",
+ "▁ex cess",
+ "▁exc ess",
+ "▁k g",
+ "▁ kg",
+ "ze t",
+ "z et",
+ "ic z",
+ "i cz",
+ "clip se",
+ "cli pse",
+ "▁t ank",
+ "▁tan k",
+ "▁g uns",
+ "▁gu ns",
+ "▁gun s",
+ "▁G es",
+ "▁Ge s",
+ "in ton",
+ "int on",
+ "into n",
+ "▁Wed nesday",
+ "▁main ly",
+ "par ser",
+ "parse r",
+ "pars er",
+ "▁effect ively",
+ "▁effective ly",
+ "▁К у",
+ "▁res ident",
+ "▁resid ent",
+ "▁L i",
+ "▁ Li",
+ "▁f lying",
+ "▁fl ying",
+ "▁fly ing",
+ "▁may or",
+ "▁mayo r",
+ "ü h",
+ "ut a",
+ "u ta",
+ "▁col our",
+ "▁air craft",
+ "ter ior",
+ "te rior",
+ "n r",
+ "▁ke eps",
+ "▁keep s",
+ "fa n",
+ "f an",
+ "▁sh irt",
+ "▁ shirt",
+ "Com par",
+ "Comp ar",
+ "▁E th",
+ "▁Et h",
+ "Ma c",
+ "M ac",
+ "cle an",
+ "c lean",
+ "sl ice",
+ "cz y",
+ "c zy",
+ "▁g ender",
+ "▁gen der",
+ "▁ge nder",
+ "▁ gender",
+ "▁b utter",
+ "▁but ter",
+ "▁butt er",
+ "AU T",
+ "A UT",
+ "▁E lement",
+ "▁El ement",
+ "▁Ele ment",
+ "▁ Element",
+ "Fi n",
+ "F in",
+ "dm a",
+ "d ma",
+ "sam ple",
+ "s ample",
+ "Reg istry",
+ "▁class ic",
+ "▁dr ove",
+ "▁dro ve",
+ "p b",
+ "def ined",
+ "define d",
+ "d efined",
+ "▁re ward",
+ "▁r eward",
+ "ya l",
+ "y al",
+ "]) ,",
+ "] ),",
+ "▁B AS",
+ "▁BA S",
+ "▁hy per",
+ "▁hyp er",
+ "▁ hyper",
+ "▁Н и",
+ "▁) .",
+ "▁ ).",
+ "Ps i",
+ "P si",
+ "▁ent ries",
+ "▁entr ies",
+ "▁ entries",
+ "▁King dom",
+ "▁S ong",
+ "▁So ng",
+ "▁Son g",
+ "▁prom pt",
+ "cent ering",
+ "center ing",
+ "▁H olly",
+ "▁Hol ly",
+ "▁Holl y",
+ "em an",
+ "ema n",
+ "e man",
+ "▁pain ting",
+ "▁paint ing",
+ "▁form ation",
+ "▁format ion",
+ "▁ formation",
+ "▁Re quest",
+ "▁Requ est",
+ "▁ Request",
+ "cont roller",
+ "control ler",
+ "Reg ion",
+ "P Y",
+ "id ades",
+ "ida des",
+ "idad es",
+ "idade s",
+ "T L",
+ "▁dis able",
+ "▁ disable",
+ "▁re in",
+ "ri cal",
+ "ric al",
+ "r ical",
+ "\" \r",
+ "% )",
+ "▁S ab",
+ "▁Sa b",
+ "▁With out",
+ "▁ Without",
+ "Se rv",
+ "Ser v",
+ "S erv",
+ "▁Sh ort",
+ "▁ Short",
+ "▁ ю",
+ "▁re sc",
+ "▁r esc",
+ "▁res c",
+ "▁ resc",
+ "▁pattern s",
+ "▁Array List",
+ "▁ ArrayList",
+ "sym bol",
+ "s ymbol",
+ "ac o",
+ "a co",
+ "▁H om",
+ "▁Ho m",
+ "▁ Hom",
+ "he lp",
+ "hel p",
+ "▁h asta",
+ "▁has ta",
+ "▁ha sta",
+ "▁hast a",
+ "▁inst alled",
+ "▁install ed",
+ "at ie",
+ "ati e",
+ "▁vis ited",
+ "▁visit ed",
+ "▁Б е",
+ "){ \\",
+ ") {\\",
+ "▁des de",
+ "J ECT",
+ "▁d rew",
+ "▁dr ew",
+ "▁dre w",
+ "▁St ock",
+ "▁Sto ck",
+ "▁C ru",
+ "▁Cr u",
+ "DE F",
+ "D EF",
+ "ob by",
+ "obb y",
+ "iz able",
+ "iza ble",
+ "og ether",
+ "oge ther",
+ "▁a ber",
+ "▁ab er",
+ "▁d an",
+ "▁da n",
+ "▁ dan",
+ "al is",
+ "ali s",
+ "ta il",
+ "t ail",
+ "▁ex pressed",
+ "▁exp ressed",
+ "▁express ed",
+ "▁expr essed",
+ "▁A ccess",
+ "▁Acc ess",
+ "▁Ac cess",
+ "▁ Access",
+ "Se g",
+ "S eg",
+ "▁L ib",
+ "▁Li b",
+ "▁ Lib",
+ "▁sup ports",
+ "▁support s",
+ "▁supp orts",
+ "back ground",
+ "▁comm une",
+ "▁commun e",
+ "cal led",
+ "call ed",
+ "c alled",
+ "▁print f",
+ "▁prin tf",
+ "▁ printf",
+ "▁Pr ince",
+ "▁Prin ce",
+ "ни те",
+ "de pend",
+ "dep end",
+ "▁d els",
+ "▁de ls",
+ "▁del s",
+ "ne ur",
+ "n eur",
+ "▁recomm ended",
+ "▁recommend ed",
+ "▁found ed",
+ "▁mark ets",
+ "▁market s",
+ "▁destroy ed",
+ "▁ab stract",
+ "▁abs tract",
+ "▁ abstract",
+ "▁s erie",
+ "▁se rie",
+ "▁ser ie",
+ "▁ serie",
+ "▁D un",
+ "▁Du n",
+ "Te rm",
+ "T erm",
+ "▁p ortion",
+ "▁port ion",
+ "ad apter",
+ "is set",
+ "iss et",
+ "isse t",
+ "че ски",
+ "▁in teger",
+ "▁inte ger",
+ "▁ integer",
+ "▁return ing",
+ "en ties",
+ "ent ies",
+ "enti es",
+ "▁F air",
+ "▁Fa ir",
+ "▁U SB",
+ "▁US B",
+ "▁ USB",
+ "▁P rice",
+ "▁Pr ice",
+ "▁Pri ce",
+ "▁ Price",
+ "ig ate",
+ "iga te",
+ "i gate",
+ "▁sett led",
+ "▁settle d",
+ "({ \\",
+ "( {\\",
+ "ne k",
+ "n ek",
+ "▁the rm",
+ "▁th erm",
+ "▁ther m",
+ "▁c ig",
+ "▁ci g",
+ "án y",
+ "á ny",
+ "▁invest igation",
+ "▁investig ation",
+ "om eter",
+ "ome ter",
+ "omet er",
+ "SU P",
+ "S UP",
+ "So me",
+ "Som e",
+ "S ome",
+ "si ng",
+ "sin g",
+ "s ing",
+ "Con stant",
+ "Const ant",
+ "▁re tail",
+ "▁ret ail",
+ "ż y",
+ "▁dr inking",
+ "▁drink ing",
+ "▁In vest",
+ "▁Inv est",
+ "S V",
+ "ig inal",
+ "igin al",
+ "igi nal",
+ "▁B ow",
+ "▁Bo w",
+ "{{ \\",
+ "{ {\\",
+ "▁ass istance",
+ "▁assist ance",
+ "▁intel lect",
+ "IN IT",
+ "au g",
+ "a ug",
+ "▁Le on",
+ "▁Leo n",
+ "Su r",
+ "S ur",
+ "▁ad mit",
+ "▁adm it",
+ "▁Com mand",
+ "▁Comm and",
+ "▁ Command",
+ "il les",
+ "ill es",
+ "ille s",
+ "ro v",
+ "r ov",
+ "▁o h",
+ "▁ oh",
+ "▁n ão",
+ "▁mat ching",
+ "▁match ing",
+ "▁g enu",
+ "▁gen u",
+ "▁ge nu",
+ "▁O x",
+ "т ся",
+ "not ation",
+ "G O",
+ "▁N ap",
+ "▁Na p",
+ "▁ver ify",
+ "▁ verify",
+ "▁aus si",
+ "▁auss i",
+ "Date Time",
+ "▁su itable",
+ "▁suit able",
+ "▁ind icate",
+ "▁indic ate",
+ "▁L ive",
+ "▁Li ve",
+ "▁Liv e",
+ "▁ Live",
+ "Fe ature",
+ "▁tr acks",
+ "▁track s",
+ "▁tra cks",
+ "▁has n",
+ "▁ha sn",
+ "▁J ava",
+ "▁Ja va",
+ "▁ Java",
+ "▁close ly",
+ "▁clos ely",
+ "▁D ad",
+ "▁Da d",
+ "ce ive",
+ "▁Mar ket",
+ "▁Mark et",
+ "ag y",
+ "a gy",
+ "▁\" -",
+ "aw n",
+ "a wn",
+ "st ell",
+ "ste ll",
+ "pt on",
+ "pto n",
+ "p ton",
+ "ze it",
+ "▁V ector",
+ "▁Ve ctor",
+ "▁Vec tor",
+ "▁ Vector",
+ "▁M AX",
+ "▁MA X",
+ "▁ MAX",
+ "▁F ederal",
+ "▁Feder al",
+ "▁Fed eral",
+ "wa ll",
+ "wal l",
+ "w all",
+ "▁J en",
+ "▁Je n",
+ "de lay",
+ "del ay",
+ "▁lim its",
+ "▁limit s",
+ "▁ limits",
+ "▁Q uest",
+ "▁Qu est",
+ "▁Que st",
+ "▁ Quest",
+ "C am",
+ "▁F el",
+ "▁Fe l",
+ "write r",
+ "wr iter",
+ "writ er",
+ "w riter",
+ "L P",
+ "▁m oves",
+ "▁mov es",
+ "▁move s",
+ "▁mo ves",
+ "▁Ex ecut",
+ "▁ Execut",
+ "▁D B",
+ "▁ DB",
+ "ok er",
+ "oke r",
+ "o ker",
+ "sc ribe",
+ "scri be",
+ "scr ibe",
+ "scrib e",
+ "el ijk",
+ "elij k",
+ "eli jk",
+ "Const ants",
+ "Constant s",
+ "Add r",
+ "Ad dr",
+ "▁} }",
+ "▁ }}",
+ "▁ch annels",
+ "▁channel s",
+ "▁ channels",
+ "i y",
+ "rior ity",
+ "▁tr ading",
+ "▁trad ing",
+ "▁tra ding",
+ "▁fac ilities",
+ "▁facil ities",
+ "▁P ack",
+ "▁Pa ck",
+ "▁Pac k",
+ "▁ Pack",
+ "▁s ys",
+ "▁sy s",
+ "▁ sys",
+ "▁m eta",
+ "▁me ta",
+ "▁met a",
+ "▁ meta",
+ "▁est imate",
+ "▁estim ate",
+ "▁L ater",
+ "▁La ter",
+ "▁Lat er",
+ "▁Late r",
+ "iss ue",
+ "▁H aving",
+ "▁Ha ving",
+ "▁Hav ing",
+ "▁g uest",
+ "▁gu est",
+ "▁no body",
+ "▁nob ody",
+ "dep th",
+ "▁z ostał",
+ "пе ра",
+ "пер а",
+ ")} \\",
+ ") }\\",
+ "b g",
+ "▁Tw itter",
+ "▁dark ness",
+ "j pg",
+ "con tr",
+ "cont r",
+ "ker nel",
+ "kern el",
+ "k ernel",
+ "] \\",
+ "▁ext end",
+ "▁ extend",
+ "ro c",
+ "r oc",
+ "NE T",
+ "N ET",
+ "MS G",
+ "M SG",
+ "▁b urst",
+ "▁bur st",
+ "▁re pair",
+ "▁rep air",
+ "▁f etch",
+ "▁fet ch",
+ "▁ fetch",
+ "ie g",
+ "i eg",
+ "ú s",
+ "Sc reen",
+ "S creen",
+ "ble m",
+ "bl em",
+ "b lem",
+ "App Compat",
+ "▁ch ap",
+ "▁cha p",
+ "▁ chap",
+ "EL D",
+ "E LD",
+ "▁P enn",
+ "▁Pe nn",
+ "▁Pen n",
+ "▁prom ote",
+ "▁promot e",
+ "▁U kr",
+ "ar est",
+ "are st",
+ "ares t",
+ "a rest",
+ "▁s amples",
+ "▁sam ples",
+ "▁sample s",
+ "▁ samples",
+ "▁G reek",
+ "▁Gre ek",
+ "▁Gree k",
+ "▁con stru",
+ "▁const ru",
+ "▁constr u",
+ "▁un iverse",
+ "▁univers e",
+ "elij ke",
+ "elijk e",
+ "▁pre ferred",
+ "▁prefer red",
+ "▁Д е",
+ "▁I ra",
+ "▁Ir a",
+ "▁d ow",
+ "▁do w",
+ "ag ues",
+ "ague s",
+ "agu es",
+ "HE RE",
+ "HER E",
+ "H ERE",
+ "▁exper ts",
+ "▁exp erts",
+ "▁expert s",
+ "Pro tocol",
+ "Proto col",
+ "PI O",
+ "P IO",
+ "▁n az",
+ "▁na z",
+ "▁K h",
+ "hö r",
+ "h ör",
+ "▁dist ingu",
+ "▁B Y",
+ "▁ BY",
+ "▁se ine",
+ "▁sein e",
+ "▁sei ne",
+ "ep ing",
+ "e ping",
+ "▁fair ly",
+ "▁Me an",
+ "ix er",
+ "in si",
+ "ins i",
+ "▁author s",
+ "▁auth ors",
+ "** .",
+ "* *.",
+ "A I",
+ "▁ed ges",
+ "▁edge s",
+ "▁ edges",
+ "▁shoot ing",
+ "Ad min",
+ "▁m aps",
+ "▁map s",
+ "▁ma ps",
+ "▁ maps",
+ "ch ant",
+ "chan t",
+ "cha nt",
+ "▁CO VID",
+ "▁link ed",
+ "▁lin ked",
+ "▁ linked",
+ "▁s ke",
+ "▁sk e",
+ "▁ ske",
+ "▁power s",
+ "▁pow ers",
+ "á d",
+ "▁stom ach",
+ "▁us age",
+ "▁ usage",
+ "▁def end",
+ "▁defe nd",
+ "▁s ustain",
+ "▁sus tain",
+ "▁sust ain",
+ "▁up dates",
+ "▁update s",
+ "▁as sign",
+ "▁ass ign",
+ "▁ assign",
+ "H L",
+ "▁S ea",
+ "▁Se a",
+ "▁dis cipl",
+ "V ideo",
+ "▁Ch ief",
+ "▁Chi ef",
+ "▁b unch",
+ "▁Ob ama",
+ "ni s",
+ "n is",
+ "vo r",
+ "v or",
+ "▁ag ents",
+ "▁agent s",
+ "ca s",
+ "c as",
+ "ch ter",
+ "cht er",
+ "chte r",
+ "▁gl anced",
+ "▁glance d",
+ "support ed",
+ "supp orted",
+ "▁Cons ider",
+ "▁Every one",
+ "▁l ect",
+ "▁le ct",
+ "▁ lect",
+ "▁St one",
+ "▁Sto ne",
+ "▁J am",
+ "▁Ja m",
+ "og ram",
+ "o gram",
+ "form ance",
+ "▁\\ \"",
+ "▁ \\\"",
+ "▁p atch",
+ "▁pat ch",
+ "▁ patch",
+ "▁v it",
+ "▁vi t",
+ "Po wer",
+ "P ower",
+ "▁hard er",
+ "▁har der",
+ "An al",
+ "A nal",
+ "▁des ired",
+ "▁desire d",
+ "▁j ug",
+ "▁ju g",
+ "▁support ing",
+ "D U",
+ "]] ,",
+ "] ],",
+ "▁Ad ministr",
+ "▁Admin istr",
+ "uck y",
+ "uc ky",
+ "▁cont roller",
+ "▁control ler",
+ "▁ controller",
+ "▁iss ued",
+ "▁issue d",
+ "▁S in",
+ "▁Si n",
+ "▁aff ili",
+ "▁part ners",
+ "▁partner s",
+ "cd ots",
+ "cdot s",
+ "c dots",
+ "ct ic",
+ "C ar",
+ "▁N Y",
+ "▁ NY",
+ "▁p riority",
+ "▁prior ity",
+ "▁ priority",
+ "or iginal",
+ "orig inal",
+ "origin al",
+ "S ql",
+ "▁decl ared",
+ "▁declare d",
+ "▁declar ed",
+ "▁Hot el",
+ "▁b rowser",
+ "▁brow ser",
+ "▁brows er",
+ "▁ browser",
+ "▁gr ande",
+ "▁grand e",
+ "▁gran de",
+ "▁gra nde",
+ "}^ \\",
+ "} ^\\",
+ "bo w",
+ "b ow",
+ "▁accom mod",
+ "Direct ory",
+ "▁suff ering",
+ "▁suffer ing",
+ "▁log ger",
+ "▁ logger",
+ "▁break fast",
+ "ul i",
+ "u li",
+ "▁b oot",
+ "▁bo ot",
+ "▁ boot",
+ "▁contribut ion",
+ "NE SS",
+ "▁T en",
+ "▁Te n",
+ "▁ Ten",
+ "sem ble",
+ "semb le",
+ "sembl e",
+ "▁h ousing",
+ "▁hous ing",
+ "▁ho using",
+ "R aw",
+ "AN CE",
+ "▁П ри",
+ "▁b rit",
+ "▁br it",
+ "▁ brit",
+ "es sa",
+ "ess a",
+ "in son",
+ "ins on",
+ "▁B all",
+ "▁Ba ll",
+ "▁Bal l",
+ "en tes",
+ "ent es",
+ "ente s",
+ "▁B ra",
+ "▁Br a",
+ "sc ore",
+ "s core",
+ "GE R",
+ "G ER",
+ "ro ute",
+ "rou te",
+ "r oute",
+ "ap sed",
+ "aps ed",
+ "apse d",
+ "ро й",
+ "di ff",
+ "d iff",
+ "▁broad cast",
+ "▁t ar",
+ "▁ta r",
+ "▁ tar",
+ "▁de light",
+ "▁del ight",
+ ") ?",
+ "ch ester",
+ "che ster",
+ "ches ter",
+ "Pl atform",
+ "▁emer gency",
+ "▁c es",
+ "▁ce s",
+ "▁ ces",
+ "ner ship",
+ "ners hip",
+ "n ership",
+ "▁sit uations",
+ "▁situ ations",
+ "▁situation s",
+ "▁famil jen",
+ "▁G eb",
+ "▁Ge b",
+ "en ta",
+ "ent a",
+ "ú blic",
+ "▁P lace",
+ "▁Pl ace",
+ "▁ Place",
+ "IL L",
+ "I LL",
+ "▁m arch",
+ "▁mar ch",
+ "▁fundament al",
+ "att ributes",
+ "attribute s",
+ "кт и",
+ "к ти",
+ "▁F u",
+ "F D",
+ "▁ра с",
+ "▁academ ic",
+ "pr es",
+ "pre s",
+ "p res",
+ "▁r ising",
+ "▁ri sing",
+ "▁ris ing",
+ "▁B raz",
+ "▁Br az",
+ "▁Bra z",
+ "▁rece iving",
+ "WAR N",
+ "▁jud g",
+ "▁necess arily",
+ "] =",
+ "▁deep ly",
+ "▁g ray",
+ "▁gr ay",
+ "▁gra y",
+ "▁ gray",
+ "He aders",
+ "Head ers",
+ "Header s",
+ "▁co al",
+ "\\ {",
+ "Mu t",
+ "M ut",
+ "ba ch",
+ "b ach",
+ "▁pro fit",
+ "▁prof it",
+ "▁ profit",
+ "во го",
+ "в ого",
+ "ig s",
+ "i gs",
+ "og rap",
+ "\"; \r",
+ "\" ;\r",
+ "▁adv oc",
+ "Gener ated",
+ "Generate d",
+ "ме ри",
+ "мер и",
+ "▁C ond",
+ "▁Con d",
+ "▁Co nd",
+ "▁ Cond",
+ "▁ag ric",
+ "BA SE",
+ "B ASE",
+ "▁arr ang",
+ "▁flow ers",
+ "▁flower s",
+ "i w",
+ "▁] ;",
+ "▁ ];",
+ "▁во й",
+ "▁ вой",
+ "ume rate",
+ "umer ate",
+ "▁i hr",
+ "▁ih r",
+ "▁п ар",
+ "▁па р",
+ "▁ пар",
+ "▁m ont",
+ "▁mon t",
+ "▁mo nt",
+ "▁ mont",
+ "wide hat",
+ "m g",
+ "▁b tn",
+ "▁bt n",
+ "▁ btn",
+ "▁b esk",
+ "▁be sk",
+ "▁bes k",
+ "▁act s",
+ "▁ac ts",
+ "▁ acts",
+ "ó s",
+ "~~ ~~",
+ "▁cur ve",
+ "▁curv e",
+ "l anguage",
+ "▁TR UE",
+ "▁ TRUE",
+ "▁cle aning",
+ "▁clean ing",
+ "Mat h",
+ "Ma th",
+ "M ath",
+ "▁reg ional",
+ "▁region al",
+ "▁est imated",
+ "▁estim ated",
+ "▁estimate d",
+ "ar ity",
+ "ari ty",
+ "ier ung",
+ "/ {",
+ "jan go",
+ "j ango",
+ "$ _",
+ "▁th rew",
+ "▁thr ew",
+ "r q",
+ "co p",
+ "c op",
+ "ner gy",
+ "▁Acc ount",
+ "▁Ac count",
+ "▁ Account",
+ "pa l",
+ "p al",
+ "▁N ic",
+ "▁Ni c",
+ "]) )",
+ "] ))",
+ "▁aw esome",
+ "▁L oad",
+ "▁Lo ad",
+ "▁ Load",
+ "un nel",
+ "unn el",
+ "▁r ows",
+ "▁ro ws",
+ "▁row s",
+ "▁ rows",
+ "▁for each",
+ "▁fore ach",
+ "▁fo reach",
+ "▁ foreach",
+ "▁P od",
+ "▁Po d",
+ "▁ Pod",
+ "▁E N",
+ "▁ EN",
+ "▁. =",
+ "ua te",
+ "u ate",
+ "frastr ucture",
+ "▁W atch",
+ "▁Wat ch",
+ "▁ Watch",
+ "St and",
+ "▁r outine",
+ "▁rout ine",
+ "▁p ic",
+ "▁pi c",
+ "▁ pic",
+ "hel per",
+ "help er",
+ "▁hor ses",
+ "▁horse s",
+ "▁hors es",
+ "▁requ ested",
+ "▁request ed",
+ "▁- --",
+ "▁-- -",
+ "▁ ---",
+ "bor der",
+ "b order",
+ "▁lif ted",
+ "▁lift ed",
+ "▁P ed",
+ "▁Pe d",
+ "Im port",
+ "Imp ort",
+ "љ е",
+ "▁Л и",
+ "▁m yst",
+ "▁my st",
+ "TH ER",
+ "THE R",
+ "T HER",
+ "▁A C",
+ "▁ AC",
+ "Pro xy",
+ "Pr oxy",
+ "pro v",
+ "pr ov",
+ "p rov",
+ "▁N ik",
+ "▁Ni k",
+ "he mat",
+ "hem at",
+ "h emat",
+ "он аль",
+ "она ль",
+ "о наль",
+ "▁\" .",
+ "▁ \".",
+ "ul ui",
+ "ulu i",
+ "▁impro ved",
+ "▁improve d",
+ "ie ren",
+ "ier en",
+ "iere n",
+ "i eren",
+ "oc olate",
+ "ocol ate",
+ "oco late",
+ "Sc he",
+ "Sch e",
+ "S che",
+ "un ic",
+ "uni c",
+ "u nic",
+ "▁Profess or",
+ "ie ler",
+ "iel er",
+ "iele r",
+ "i eler",
+ "▁d uration",
+ "▁dur ation",
+ "▁ duration",
+ "▁time out",
+ "▁ timeout",
+ "ho m",
+ "h om",
+ "▁l ux",
+ "▁lu x",
+ "▁t rab",
+ "▁tr ab",
+ "▁tra b",
+ "it ary",
+ "ita ry",
+ "itar y",
+ "њ е",
+ "▁insp ired",
+ "▁inspir ed",
+ "▁inspire d",
+ "}) \\",
+ "} )\\",
+ "is ely",
+ "ise ly",
+ "ial s",
+ "ia ls",
+ "i als",
+ "▁V or",
+ "▁Vo r",
+ "▁enh ance",
+ "▁l ucky",
+ "▁luck y",
+ "▁luc ky",
+ "W orld",
+ "el o",
+ "e lo",
+ "if iers",
+ "ifier s",
+ "ifi ers",
+ "▁f acing",
+ "▁fac ing",
+ "▁fa cing",
+ "▁appreci ate",
+ "▁ être",
+ "▁ben ch",
+ "▁ bench",
+ "at ted",
+ "att ed",
+ "atte d",
+ "gen ce",
+ "g ence",
+ "c ourse",
+ "▁t ub",
+ "▁tu b",
+ "▁l ors",
+ "▁lo rs",
+ "▁mis take",
+ "▁mist ake",
+ "no m",
+ "n om",
+ "▁p aus",
+ "▁pa us",
+ "▁\" \";",
+ "▁\"\" ;",
+ "▁su bs",
+ "▁sub s",
+ "▁st ato",
+ "▁stat o",
+ "▁sta to",
+ "$ )",
+ "▁g ay",
+ "▁ga y",
+ "or ry",
+ "orr y",
+ "▁veh icles",
+ "▁vehicle s",
+ "▁br ill",
+ "ma y",
+ "m ay",
+ "re sp",
+ "res p",
+ "r esp",
+ "▁w ore",
+ "▁wor e",
+ "▁wo re",
+ "j ą",
+ "b p",
+ "on el",
+ "one l",
+ "o nel",
+ "▁C R",
+ "▁ CR",
+ "▁di agn",
+ "▁dia gn",
+ "math sf",
+ "▁hol iday",
+ "▁achie ved",
+ "▁achieve d",
+ "▁{ '",
+ "▁ {'",
+ "▁Re source",
+ "▁Res ource",
+ "▁ Resource",
+ "▁h i",
+ "▁ hi",
+ "▁b ra",
+ "▁br a",
+ "▁ bra",
+ "▁CON DITION",
+ "ct r",
+ "c tr",
+ "▁W rite",
+ "▁Writ e",
+ "▁Wr ite",
+ "▁ Write",
+ "is hop",
+ "ish op",
+ "i shop",
+ "OL D",
+ "O LD",
+ "▁c pu",
+ "▁cp u",
+ "▁ cpu",
+ "▁occ urs",
+ "▁occur s",
+ "▁oc curs",
+ "ó ł",
+ "str aint",
+ "stra int",
+ "▁nu clear",
+ "▁nuc lear",
+ "▁nucle ar",
+ "Ar ea",
+ "Are a",
+ "A rea",
+ "cl uster",
+ "▁surround ing",
+ "▁J uan",
+ "▁Ju an",
+ "▁pr ima",
+ "▁prim a",
+ "▁pri ma",
+ "▁South ern",
+ "▁Sou thern",
+ "it ty",
+ "itt y",
+ "i tty",
+ "▁As sembly",
+ "▁ Assembly",
+ "el em",
+ "ele m",
+ "e lem",
+ "ad i",
+ "a di",
+ "ér al",
+ "éra l",
+ "é ral",
+ "▁W at",
+ "▁Wa t",
+ "▁R adio",
+ "▁Rad io",
+ "▁ Radio",
+ "▁g egen",
+ "▁ge gen",
+ "▁T ony",
+ "▁To ny",
+ "▁Ton y",
+ "pr essed",
+ "press ed",
+ "pres sed",
+ "p ressed",
+ "▁An ne",
+ "▁Ann e",
+ "▁N S",
+ "▁ NS",
+ "▁P ak",
+ "▁Pa k",
+ "▁C ivil",
+ "▁Ci vil",
+ "▁th rown",
+ "▁throw n",
+ "▁thr own",
+ "▁thro wn",
+ "NO NE",
+ "NON E",
+ "N ONE",
+ "▁p ump",
+ "▁pu mp",
+ "▁s olve",
+ "▁sol ve",
+ "EN ABLE",
+ "▁Ph ys",
+ "▁ Phys",
+ "▁] ,",
+ "▁ ],",
+ "PO SE",
+ "POS E",
+ "kt et",
+ "kte t",
+ "▁F ab",
+ "▁Fa b",
+ "valid ate",
+ "Iter ator",
+ "cond ition",
+ "re du",
+ "red u",
+ "r edu",
+ "▁neg oti",
+ "an no",
+ "ann o",
+ "▁s ans",
+ "▁sa ns",
+ "▁san s",
+ "▁U l",
+ "CH AR",
+ "▁ed ition",
+ "▁edit ion",
+ "▁spect rum",
+ "or ie",
+ "ori e",
+ "o rie",
+ "▁execut ion",
+ "▁exec ution",
+ "P lease",
+ "▁B O",
+ "▁ BO",
+ "UR N",
+ "▁c ow",
+ "▁co w",
+ "▁ cow",
+ "ст ан",
+ "ста н",
+ "с тан",
+ "istribut ion",
+ "Do main",
+ "Dom ain",
+ "▁re aders",
+ "▁read ers",
+ "▁reader s",
+ "▁cons umer",
+ "▁consum er",
+ "▁consume r",
+ "▁st yles",
+ "▁style s",
+ "▁sty les",
+ "▁ styles",
+ "en code",
+ "enc ode",
+ "▁C y",
+ "Com mon",
+ "Comm on",
+ "▁P rop",
+ "▁Pro p",
+ "▁Pr op",
+ "▁ Prop",
+ "▁ex ecute",
+ "▁execut e",
+ "▁exec ute",
+ "▁ execute",
+ "▁e q",
+ "▁ eq",
+ "▁vis itors",
+ "▁visit ors",
+ "▁visitor s",
+ "▁A mb",
+ "▁Am b",
+ "ud ad",
+ "uda d",
+ "q quad",
+ "▁C ert",
+ "▁Ce rt",
+ "▁Cer t",
+ "▁ Cert",
+ "▁t rop",
+ "▁tr op",
+ "▁tro p",
+ "▁yes terday",
+ "ta in",
+ "t ain",
+ "L D",
+ "at ro",
+ "atr o",
+ "▁incre ases",
+ "▁increase s",
+ "▁W ars",
+ "▁War s",
+ "▁Wa rs",
+ "ne d",
+ "n ed",
+ "be fore",
+ "b efore",
+ "au pt",
+ "a upt",
+ "▁E RR",
+ "▁ER R",
+ "▁ ERR",
+ "▁F ord",
+ "▁For d",
+ "▁Fo rd",
+ "▁d alla",
+ "▁da lla",
+ "▁dal la",
+ "▁dall a",
+ "UL AR",
+ "▁st rike",
+ "▁str ike",
+ "▁stri ke",
+ "Ar r",
+ "A rr",
+ "▁re covery",
+ "▁rec overy",
+ "▁recover y",
+ "▁Res ponse",
+ "▁ Response",
+ "▁strateg ies",
+ "▁і н",
+ "▁ ін",
+ "▁re ar",
+ "▁r ear",
+ "▁adult s",
+ "▁Н е",
+ "window s",
+ "wind ows",
+ "de cl",
+ "dec l",
+ "ol en",
+ "ole n",
+ "o len",
+ "▁J ord",
+ "▁Jo rd",
+ "▁K al",
+ "▁Ka l",
+ "▁c ui",
+ "▁cu i",
+ "▁П ро",
+ "▁S ever",
+ "▁Se ver",
+ "▁Sev er",
+ "▁a le",
+ "▁al e",
+ "▁ ale",
+ "▁pe ut",
+ "▁peu t",
+ "St ats",
+ "Stat s",
+ "▁R oss",
+ "▁Ro ss",
+ "▁Ros s",
+ "ar ten",
+ "art en",
+ "arte n",
+ "sh all",
+ "shal l",
+ "sha ll",
+ "s hall",
+ "▁ent ertain",
+ "▁enter tain",
+ "▁entert ain",
+ "▁par king",
+ "▁park ing",
+ "но ви",
+ "нов и",
+ "er re",
+ "err e",
+ "▁fun ding",
+ "▁fund ing",
+ "▁C le",
+ "▁Cl e",
+ "▁O t",
+ "un st",
+ "uns t",
+ "assert Equals",
+ "assertEqual s",
+ "▁c ancell",
+ "▁can cell",
+ "▁cancel l",
+ "TA G",
+ "T AG",
+ "▁E arly",
+ "▁Earl y",
+ "▁feed back",
+ "▁p and",
+ "▁pan d",
+ "▁pa nd",
+ "y o",
+ "▁mir ror",
+ "▁ver b",
+ "▁ve rb",
+ "▁ verb",
+ "▁high light",
+ "er ialize",
+ "erial ize",
+ "▁g rade",
+ "▁gr ade",
+ "▁grad e",
+ "▁gra de",
+ "▁ grade",
+ "ла сь",
+ "▁Br ook",
+ "▁Bro ok",
+ "▁L I",
+ "▁ LI",
+ "▁im plies",
+ "▁impl ies",
+ "▁e norm",
+ "▁en orm",
+ "aj ą",
+ "a ją",
+ "▁W er",
+ "▁We r",
+ "aw ay",
+ "awa y",
+ "a way",
+ "▁machine s",
+ "▁mach ines",
+ "▁d ent",
+ "▁de nt",
+ "▁den t",
+ "Id x",
+ "I dx",
+ "▁t id",
+ "▁ti d",
+ "▁ tid",
+ ") \"",
+ "▁m ole",
+ "▁mo le",
+ "▁mol e",
+ "bo ld",
+ "bol d",
+ "b old",
+ "CO NT",
+ "CON T",
+ "C ONT",
+ "▁é p",
+ "▁ ép",
+ "▁cut ting",
+ "▁N eg",
+ "▁Ne g",
+ "▁ Neg",
+ "▁t ong",
+ "▁to ng",
+ "▁ton g",
+ "▁net works",
+ "▁network s",
+ "▁F all",
+ "▁Fa ll",
+ "▁Fal l",
+ "▁ Fall",
+ "gener ated",
+ "generate d",
+ "▁P ri",
+ "▁Pr i",
+ "UE ST",
+ "UES T",
+ "U EST",
+ "▁Be lg",
+ "▁Bel g",
+ "▁s heet",
+ "▁she et",
+ "▁ sheet",
+ "кс и",
+ "к си",
+ "▁ †",
+ "▁y eah",
+ "▁ye ah",
+ "▁Vict or",
+ "▁Vi ctor",
+ "▁Vic tor",
+ "▁R ub",
+ "▁Ru b",
+ "▁candid ates",
+ "▁candidate s",
+ "pr és",
+ "▁E U",
+ "et r",
+ "e tr",
+ "▁roll ed",
+ "▁ rolled",
+ "▁P as",
+ "▁Pa s",
+ "▁Ar thur",
+ "Ar ch",
+ "Arc h",
+ "▁M ann",
+ "▁Man n",
+ "▁Ma nn",
+ "Amer ican",
+ "America n",
+ "ze s",
+ "z es",
+ "in ners",
+ "inn ers",
+ "inner s",
+ "▁A uto",
+ "▁Aut o",
+ "▁Au to",
+ "▁ Auto",
+ "▁profess or",
+ "▁profes sor",
+ "▁) ;\r",
+ "▁); \r",
+ "▁ );\r",
+ "▁ad dr",
+ "▁add r",
+ "▁ addr",
+ "▁Med ical",
+ "▁Medic al",
+ "▁f ired",
+ "▁fire d",
+ "▁fi red",
+ "▁fir ed",
+ "▁C ore",
+ "▁Co re",
+ "▁Cor e",
+ "▁ Core",
+ "▁CON FIG",
+ "▁ CONFIG",
+ "▁s ql",
+ "▁sq l",
+ "▁ sql",
+ "▁Con serv",
+ "▁Cons erv",
+ "▁Conse rv",
+ "ic hen",
+ "ich en",
+ "iche n",
+ "i chen",
+ "Ver tex",
+ "Vert ex",
+ "▁H O",
+ "▁ HO",
+ "Y eah",
+ "No te",
+ "Not e",
+ "N ote",
+ "▁O K",
+ "▁ OK",
+ "mu s",
+ "m us",
+ "f ocus",
+ "aj a",
+ "a ja",
+ "r á",
+ "▁h ence",
+ "▁hen ce",
+ "▁execut ive",
+ "▁liqu id",
+ "uj e",
+ "u je",
+ "▁d riven",
+ "▁dr iven",
+ "▁dri ven",
+ "▁driv en",
+ "▁drive n",
+ "▁ driven",
+ "ig ue",
+ "igu e",
+ "i gue",
+ "▁W ik",
+ "▁Wi k",
+ "R ate",
+ "ra nd",
+ "ran d",
+ "r and",
+ "Result s",
+ "▁cop ies",
+ "▁t an",
+ "▁ta n",
+ "▁ tan",
+ "rit eria",
+ "rite ria",
+ "riter ia",
+ "en en",
+ "ene n",
+ "e nen",
+ "}_ \\",
+ "} _\\",
+ "▁po bl",
+ "▁pob l",
+ "▁sou thern",
+ "▁south ern",
+ "el n",
+ "e ln",
+ "▁z wei",
+ "▁zwe i",
+ "▁zw ei",
+ "▁con crete",
+ "▁CONDITION S",
+ "▁dream s",
+ "▁dre ams",
+ "▁min im",
+ "▁mi nim",
+ "▁mini m",
+ "▁em ployee",
+ "▁employ ee",
+ "▁n ap",
+ "▁na p",
+ "▁su spect",
+ "▁sus pect",
+ "▁susp ect",
+ "Mo use",
+ "M ouse",
+ "▁ther apy",
+ "▁therap y",
+ "av al",
+ "ava l",
+ "a val",
+ "▁An th",
+ "▁Ant h",
+ "ST ART",
+ "st ers",
+ "ster s",
+ "ste rs",
+ "s ters",
+ "ish ment",
+ "fin ite",
+ "W A",
+ "v y",
+ "▁m ood",
+ "▁mo od",
+ "com fort",
+ "▁s hr",
+ "▁sh r",
+ "▁dec ade",
+ "я бря",
+ "▁' #",
+ "▁d ot",
+ "▁do t",
+ "▁ dot",
+ "▁h ill",
+ "▁hi ll",
+ "▁ hill",
+ "ar ry",
+ "arr y",
+ "cat ch",
+ "c atch",
+ "▁j Query",
+ "▁ jQuery",
+ "▁corpor ate",
+ "▁BAS IS",
+ "▁appoint ed",
+ "▁em bar",
+ "▁emb ar",
+ "ograph ie",
+ "▁p ressed",
+ "▁pr essed",
+ "▁pres sed",
+ "▁press ed",
+ "▁ pressed",
+ "▁ch ampion",
+ "▁champ ion",
+ "em it",
+ "emi t",
+ "e mit",
+ "▁B ed",
+ "▁Be d",
+ "ва ння",
+ "ван ня",
+ "Gu i",
+ "G ui",
+ "▁P UR",
+ "▁ur ban",
+ "▁urb an",
+ "▁sent ence",
+ "bu ry",
+ "bur y",
+ "b ury",
+ "▁V ideo",
+ "▁ Video",
+ "▁regular ly",
+ "▁regul arly",
+ "v l",
+ "▁с лу",
+ "▁ слу",
+ "oc key",
+ "ock ey",
+ "ev in",
+ "e vin",
+ "ult ural",
+ "ultur al",
+ "▁pass age",
+ "▁со став",
+ "▁соста в",
+ "▁large ly",
+ "▁larg ely",
+ "or ters",
+ "ort ers",
+ "orter s",
+ "orte rs",
+ "▁conne ctions",
+ "▁connection s",
+ "▁connect ions",
+ "▁surpr ising",
+ "b c",
+ "▁strong ly",
+ "ans as",
+ "▁s ist",
+ "▁si st",
+ "▁ext reme",
+ "▁extrem e",
+ "▁extr eme",
+ "wh el",
+ "whe l",
+ "w hel",
+ "▁de aling",
+ "▁deal ing",
+ "ograph ic",
+ "▁Republic an",
+ "▁gr anted",
+ "▁gran ted",
+ "▁grant ed",
+ "▁C L",
+ "▁ CL",
+ "▁H ope",
+ "▁Ho pe",
+ "▁Hop e",
+ "less ly",
+ "▁u pload",
+ "▁up load",
+ "▁ upload",
+ "▁- \\",
+ "▁ -\\",
+ "ни ю",
+ "▁val uable",
+ "= [",
+ "Pr ice",
+ "P rice",
+ "iss ance",
+ "ie ns",
+ "ien s",
+ "i ens",
+ "he it",
+ "▁sugg ests",
+ "▁suggest s",
+ "с ло",
+ "▁j ur",
+ "▁ju r",
+ "} |",
+ "l p",
+ "▁inv ited",
+ "▁invite d",
+ "▁de riv",
+ "▁der iv",
+ "IM IT",
+ "I MIT",
+ "ra ss",
+ "ras s",
+ "r ass",
+ "▁in struct",
+ "▁inst ruct",
+ "▁instr uct",
+ "▁c ourses",
+ "▁cour ses",
+ "▁course s",
+ "▁cours es",
+ "ä ch",
+ "▁fif ty",
+ "▁fi fty",
+ "DE VICE",
+ "DEV ICE",
+ "AS H",
+ "A SH",
+ "▁h ip",
+ "▁hi p",
+ "▁ hip",
+ "Un known",
+ "▁C atalogue",
+ "▁Catal ogue",
+ "▁R oll",
+ "▁Ro ll",
+ "▁Rol l",
+ "▁ Roll",
+ "▁t ensor",
+ "▁ten sor",
+ "▁tens or",
+ "▁ tensor",
+ "be c",
+ "b ec",
+ "ét é",
+ "é té",
+ "Id entity",
+ "Ident ity",
+ "& \\",
+ "▁Step hen",
+ "▁Steph en",
+ "no des",
+ "node s",
+ "nod es",
+ "n odes",
+ "Di m",
+ "D im",
+ "▁cons ists",
+ "▁consist s",
+ "▁normal ly",
+ "▁norm ally",
+ "ub l",
+ "u bl",
+ "▁Pol ice",
+ "▁G ames",
+ "▁Game s",
+ "▁Ga mes",
+ "▁Gam es",
+ "fi ve",
+ "f ive",
+ "Ha ve",
+ "H ave",
+ "▁p adding",
+ "▁pad ding",
+ "▁ padding",
+ "er es",
+ "ere s",
+ "e res",
+ "an th",
+ "ant h",
+ "▁p uts",
+ "▁put s",
+ "▁pu ts",
+ "um inate",
+ "umin ate",
+ "umi nate",
+ "ov ie",
+ "ovi e",
+ "▁In dex",
+ "▁Ind ex",
+ "▁ Index",
+ "bl ue",
+ "Sc al",
+ "S cal",
+ "▁g iant",
+ "▁gi ant",
+ "T F",
+ "ps on",
+ "p son",
+ "▁vict im",
+ "▁vic tim",
+ "se rial",
+ "ser ial",
+ "s erial",
+ "▁S ym",
+ "▁Sy m",
+ "▁ Sym",
+ "Sing le",
+ "S ingle",
+ "▁m d",
+ "▁ md",
+ "▁att ended",
+ "▁attend ed",
+ "▁S tra",
+ "▁St ra",
+ "▁Str a",
+ "▁D ark",
+ "▁Dar k",
+ "▁ Dark",
+ ") |",
+ "▁s pan",
+ "▁sp an",
+ "▁ span",
+ "▁main tenance",
+ "▁b ind",
+ "▁bi nd",
+ "▁bin d",
+ "▁ bind",
+ "Be an",
+ "il arly",
+ "ilar ly",
+ "▁con vent",
+ "▁conv ent",
+ "▁conven t",
+ "▁conve nt",
+ "▁Jos é",
+ "ud d",
+ "u dd",
+ "▁p oly",
+ "▁pol y",
+ "▁po ly",
+ "▁ poly",
+ "▁i dx",
+ "▁id x",
+ "▁ idx",
+ "▁as ks",
+ "▁ask s",
+ "▁ent hus",
+ "▁s uck",
+ "▁su ck",
+ "▁suc k",
+ "▁C ou",
+ "▁Co u",
+ "▁Corpor ation",
+ "us ions",
+ "usion s",
+ "op her",
+ "oph er",
+ "o pher",
+ "▁sympt oms",
+ "▁Joh ann",
+ "▁п у",
+ "▁ пу",
+ "▁h tml",
+ "▁ html",
+ "▁p s",
+ "▁ ps",
+ "ear ing",
+ "ea ring",
+ "e aring",
+ "ge sch",
+ "ges ch",
+ "g esch",
+ "▁M other",
+ "▁Mo ther",
+ "▁Mot her",
+ "RE T",
+ "R ET",
+ "▁furn iture",
+ "P F",
+ "▁Gu ard",
+ "▁ Guard",
+ "pat tern",
+ "▁love ly",
+ "▁lov ely",
+ "al g",
+ "a lg",
+ "ed ly",
+ "se x",
+ "s ex",
+ "▁fin ds",
+ "▁find s",
+ "Bu f",
+ "B uf",
+ "▁на д",
+ "▁ над",
+ "▁к м",
+ "▁P or",
+ "▁Po r",
+ "С Р",
+ "En ter",
+ "Ent er",
+ "▁e sta",
+ "▁est a",
+ "▁es ta",
+ "▁ esta",
+ "▁т ре",
+ "▁ тре",
+ "▁\" *",
+ "▁F ox",
+ "▁Fo x",
+ "▁c ock",
+ "▁co ck",
+ "▁coc k",
+ "▁ cock",
+ "B undle",
+ "▁p uis",
+ "▁pu is",
+ "▁ puis",
+ "▁ann ounce",
+ "▁announ ce",
+ "▁g uid",
+ "▁gu id",
+ "▁ guid",
+ "check ed",
+ "ic ide",
+ "ici de",
+ "ne g",
+ "n eg",
+ "▁G il",
+ "▁Gi l",
+ "sc hen",
+ "sch en",
+ "sche n",
+ "s chen",
+ "olog ist",
+ "is o",
+ "i so",
+ "group s",
+ "gro ups",
+ "g roups",
+ "▁some body",
+ "Da y",
+ "D ay",
+ "tr as",
+ "tra s",
+ "t ras",
+ "▁comp act",
+ "▁organ ized",
+ "▁organiz ed",
+ "▁organize d",
+ "▁r oles",
+ "▁ro les",
+ "▁role s",
+ "▁h int",
+ "▁hi nt",
+ "▁ hint",
+ "▁s å",
+ "▁p ays",
+ "▁pay s",
+ "▁pa ys",
+ "▁С и",
+ "▁h oped",
+ "▁hope d",
+ "▁hop ed",
+ "▁ho ped",
+ "▁s ail",
+ "▁sa il",
+ "▁V ers",
+ "▁Ver s",
+ "▁Ve rs",
+ "▁ Vers",
+ "▁em br",
+ "▁emb r",
+ "▁b ot",
+ "▁bo t",
+ "▁ bot",
+ "▁ex ceed",
+ "▁exc eed",
+ "BA CK",
+ "B ACK",
+ "▁g aze",
+ "▁gaz e",
+ "▁ga ze",
+ "▁s pons",
+ "▁sp ons",
+ "▁spo ns",
+ "AS T",
+ "A ST",
+ "▁tor ch",
+ "▁ torch",
+ "▁news paper",
+ "▁newsp aper",
+ "▁D ist",
+ "▁Dis t",
+ "▁Di st",
+ "▁ Dist",
+ "▁b ass",
+ "▁bas s",
+ "▁ba ss",
+ "▁h anging",
+ "▁han ging",
+ "▁hang ing",
+ "▁e ars",
+ "▁ear s",
+ "▁ ears",
+ "ń sk",
+ "get Value",
+ "▁un us",
+ "▁E le",
+ "▁El e",
+ "serv ices",
+ "service s",
+ "s ervices",
+ "▁d ressed",
+ "▁dr essed",
+ "▁dress ed",
+ "la v",
+ "l av",
+ "▁п ла",
+ "▁ пла",
+ "Priv ate",
+ "P rivate",
+ "mi c",
+ "m ic",
+ "▁par ser",
+ "▁parse r",
+ "▁ parser",
+ "▁se ctions",
+ "▁section s",
+ "▁sect ions",
+ "▁ sections",
+ "▁f o",
+ "▁ fo",
+ "Err orf",
+ "Error f",
+ "in z",
+ "ör d",
+ "ö rd",
+ "▁m etric",
+ "▁met ric",
+ "▁ metric",
+ "UR I",
+ "U RI",
+ "▁v ice",
+ "▁vi ce",
+ "▁vic e",
+ "RE D",
+ "R ED",
+ "▁n ue",
+ "▁nu e",
+ "re vs",
+ "rev s",
+ "▁col lected",
+ "▁collect ed",
+ "▁colle cted",
+ "oo se",
+ "o ose",
+ "▁m ond",
+ "▁mon d",
+ "▁mo nd",
+ "▁ mond",
+ "▁n as",
+ "▁na s",
+ "▁ nas",
+ "▁На се",
+ "▁ å",
+ "Dr op",
+ "D rop",
+ "▁ab use",
+ "▁s ees",
+ "▁se es",
+ "▁see s",
+ "▁H ence",
+ "▁Hen ce",
+ "ex ec",
+ "}\\ ,",
+ "} \\,",
+ "▁ar bitr",
+ "▁Ap plication",
+ "▁ Application",
+ "f amily",
+ "ü d",
+ "▁mag netic",
+ "▁magn etic",
+ "▁magnet ic",
+ "▁new ly",
+ "▁re produ",
+ "▁rep rodu",
+ "▁writ ers",
+ "▁write rs",
+ "▁writer s",
+ "▁he aders",
+ "▁head ers",
+ "▁header s",
+ "▁ headers",
+ "š í",
+ "р т",
+ "YP E",
+ "Y PE",
+ "▁s chema",
+ "▁sch ema",
+ "▁sche ma",
+ "▁ schema",
+ "▁C e",
+ "▁Je ws",
+ "▁Jew s",
+ "▁Re cord",
+ "▁Rec ord",
+ "▁ Record",
+ "pre sent",
+ "pres ent",
+ "p resent",
+ "▁так же",
+ "▁label s",
+ "▁lab els",
+ "▁ labels",
+ "S ocket",
+ "▁equ ations",
+ "▁equation s",
+ "▁eq uations",
+ "▁medic ine",
+ "▁author ities",
+ "} `",
+ "ст ви",
+ "ств и",
+ "▁C orn",
+ "▁Co rn",
+ "▁Cor n",
+ "▁environment al",
+ "WAR E",
+ "WA RE",
+ "W ARE",
+ "Me r",
+ "M er",
+ "▁са мо",
+ "▁Techn ology",
+ "▁S af",
+ "▁Sa f",
+ "▁con n",
+ "▁co nn",
+ "▁ conn",
+ "▁U m",
+ "▁Pac ific",
+ "те л",
+ "ja n",
+ "j an",
+ "▁unc ertain",
+ "▁bel ief",
+ "▁belie f",
+ "co unter",
+ "count er",
+ "c ounter",
+ "to Be",
+ "IN S",
+ "I NS",
+ "we et",
+ "Li ght",
+ "L ight",
+ "pr imary",
+ "prim ary",
+ "▁feature d",
+ "▁feat ured",
+ "▁touch ed",
+ "▁tou ched",
+ "HT TP",
+ "▁t act",
+ "▁ta ct",
+ "pos itory",
+ "p ository",
+ "▁e ines",
+ "▁ein es",
+ "▁eine s",
+ "la ss",
+ "las s",
+ "l ass",
+ "сь ка",
+ "▁prz ez",
+ "▁prze z",
+ "▁f uer",
+ "▁fue r",
+ "▁fu er",
+ "▁exc iting",
+ "▁excit ing",
+ "▁C ub",
+ "▁Cu b",
+ "ag an",
+ "aga n",
+ "a gan",
+ "V O",
+ "▁' %",
+ "▁\\ {",
+ "▁ \\{",
+ "ub ble",
+ "▁F ol",
+ "▁Fo l",
+ "▁K ong",
+ "▁Kon g",
+ "▁Ko ng",
+ "▁ver sch",
+ "▁vers ch",
+ "FA IL",
+ "F AIL",
+ "▁na ar",
+ "ö s",
+ "sp eed",
+ "spe ed",
+ "s peed",
+ "▁terr itor",
+ "▁territo r",
+ "▁w rap",
+ "▁wr ap",
+ "▁ wrap",
+ "▁Jah re",
+ "▁Jahr e",
+ "▁Ja hre",
+ "le e",
+ "l ee",
+ "▁cross ed",
+ "res olve",
+ "▁s tim",
+ "▁st im",
+ "N ative",
+ "ur sor",
+ "urs or",
+ "Not Null",
+ "▁Al bert",
+ "▁Alber t",
+ "▁Alb ert",
+ "▁sign ature",
+ "▁ signature",
+ "▁R u",
+ "id as",
+ "ida s",
+ "i das",
+ "▁de cent",
+ "▁dec ent",
+ "▁dece nt",
+ "▁f aced",
+ "▁face d",
+ "▁fac ed",
+ "▁fa ced",
+ "▁ faced",
+ "▁ лю",
+ "▁Sp ain",
+ "▁res istance",
+ "▁resist ance",
+ "▁B rian",
+ "▁Br ian",
+ "kw args",
+ "▁inter val",
+ "▁ interval",
+ "▁Л е",
+ "▁ex plo",
+ "▁expl o",
+ "▁exp lo",
+ "▁s emi",
+ "▁se mi",
+ "▁sem i",
+ "▁wide ly",
+ "▁wid ely",
+ "d x",
+ "ko v",
+ "k ov",
+ "▁C ome",
+ "▁Com e",
+ "▁Co me",
+ "▁ Come",
+ "▁kn ife",
+ "As p",
+ "A sp",
+ "un o",
+ "u no",
+ "line to",
+ "lin eto",
+ "▁B und",
+ "▁Bu nd",
+ "▁Bun d",
+ "C ert",
+ "▁t odo",
+ "▁to do",
+ "▁tod o",
+ "ta gs",
+ "tag s",
+ "t ags",
+ "▁guarante e",
+ "▁v ital",
+ "▁vi tal",
+ "▁vit al",
+ "▁vita l",
+ "▁f ought",
+ "▁fou ght",
+ "▁E nv",
+ "▁En v",
+ "▁ Env",
+ "H D",
+ "Lo wer",
+ "Low er",
+ "L ower",
+ "T x",
+ "▁F a",
+ "▁ant icip",
+ "▁anti cip",
+ "Time r",
+ "Tim er",
+ "T imer",
+ "med iate",
+ "medi ate",
+ "media te",
+ "▁pro ven",
+ "▁pr oven",
+ "▁prov en",
+ "▁prove n",
+ "▁part ir",
+ "▁parti r",
+ "A E",
+ "cur sor",
+ "curs or",
+ "c ursor",
+ "▁wood en",
+ "▁wo oden",
+ "▁Cont act",
+ "▁ Contact",
+ "re gs",
+ "reg s",
+ "▁prov inc",
+ "▁provin c",
+ "▁D C",
+ "▁ DC",
+ "▁mem ories",
+ "▁memor ies",
+ "▁memo ries",
+ "▁f t",
+ "▁ ft",
+ "▁b attery",
+ "▁batter y",
+ "▁batt ery",
+ "▁bat tery",
+ "ute nant",
+ "uten ant",
+ "u tenant",
+ "Log in",
+ "Lo gin",
+ "ount ry",
+ "oun try",
+ "▁comp ens",
+ "operator name",
+ "▁Jac ob",
+ "ze d",
+ "z ed",
+ "AD DR",
+ "ADD R",
+ "▁qu ad",
+ "▁ quad",
+ "*) .",
+ "* ).",
+ "▁co at",
+ "▁f ir",
+ "▁fi r",
+ "▁Mich el",
+ "▁Mic hel",
+ "▁Mi chel",
+ "▁Miche l",
+ "▁Stand ard",
+ "▁ Standard",
+ "r f",
+ "me l",
+ "m el",
+ "▁co eff",
+ "▁Ira q",
+ "▁G iven",
+ "▁Gi ven",
+ "▁Give n",
+ "ни ма",
+ "ним а",
+ "▁F IT",
+ "▁FI T",
+ "▁p eu",
+ "▁pe u",
+ "▁i g",
+ "▁ ig",
+ "▁C ase",
+ "▁Cas e",
+ "▁Ca se",
+ "▁ Case",
+ "m é",
+ "▁par allel",
+ "▁ parallel",
+ "ci o",
+ "c io",
+ "ko w",
+ "k ow",
+ "▁institut ions",
+ "▁institution s",
+ "í cul",
+ "ab an",
+ "aba n",
+ "a ban",
+ "U X",
+ "▁Sa rah",
+ "▁Sar ah",
+ "▁Sara h",
+ "▁m és",
+ "▁mé s",
+ "▁at mos",
+ "▁atm os",
+ "▁slä ktet",
+ "▁br others",
+ "▁bro thers",
+ "▁brother s",
+ "▁want ing",
+ "aa aa",
+ "▁f est",
+ "▁fe st",
+ "= -",
+ "▁for ty",
+ "▁fort y",
+ "▁cre ates",
+ "▁create s",
+ "▁creat es",
+ "h h",
+ "▁And roid",
+ "▁Andr oid",
+ "▁ Android",
+ "an ches",
+ "anc hes",
+ "anch es",
+ "anche s",
+ "B T",
+ "up load",
+ "u pload",
+ "xi s",
+ "x is",
+ "H z",
+ "бо р",
+ "б ор",
+ "RA Y",
+ "R AY",
+ "nt il",
+ "n til",
+ "▁le aned",
+ "▁lean ed",
+ "un da",
+ "und a",
+ "▁ult imately",
+ "▁ultimate ly",
+ "▁t ok",
+ "▁to k",
+ "▁ tok",
+ "ne h",
+ "n eh",
+ "▁law yer",
+ "he nd",
+ "hen d",
+ "h end",
+ "▁V in",
+ "▁Vi n",
+ "▁fac ility",
+ "▁facil ity",
+ "▁l ikes",
+ "▁li kes",
+ "▁like s",
+ "▁lik es",
+ "en to",
+ "ent o",
+ "Node s",
+ "No des",
+ "N odes",
+ "▁entr ance",
+ "at to",
+ "att o",
+ "a tto",
+ "re tt",
+ "ret t",
+ "r ett",
+ "ac cept",
+ "th eme",
+ "the me",
+ "та н",
+ "т ан",
+ "os i",
+ "o si",
+ "▁{ },",
+ "▁{} ,",
+ "▁ {},",
+ "pgfpath lineto",
+ "go od",
+ "g ood",
+ "sl ot",
+ "s lot",
+ "▁in noc",
+ "▁inn oc",
+ "▁pro port",
+ "▁pr oport",
+ "▁prop ort",
+ "▁ar rive",
+ "▁arriv e",
+ "▁arr ive",
+ "é ho",
+ "▁p airs",
+ "▁pa irs",
+ "▁pair s",
+ "▁wr apped",
+ "▁wrap ped",
+ "▁un w",
+ "▁expl os",
+ "▁exp los",
+ "▁explo s",
+ "▁g el",
+ "▁ge l",
+ "▁ gel",
+ "W ill",
+ "▁Ze aland",
+ "ía s",
+ "í as",
+ "▁J r",
+ "▁F ra",
+ "▁Fr a",
+ "▁le git",
+ "▁leg it",
+ "▁il legal",
+ "к лю",
+ "▁t ort",
+ "▁to rt",
+ "▁tor t",
+ "▁p ron",
+ "▁pro n",
+ "▁pr on",
+ "F i",
+ "▁f org",
+ "▁for g",
+ "▁fo rg",
+ "ex port",
+ "exp ort",
+ "▁Child ren",
+ "▁ Children",
+ "▁A bs",
+ "▁Ab s",
+ "▁ Abs",
+ "▁S end",
+ "▁Se nd",
+ "▁Sen d",
+ "▁ Send",
+ "▁dis count",
+ "▁disc ount",
+ "▁disco unt",
+ "▁p oster",
+ "▁pos ter",
+ "▁po ster",
+ "▁post er",
+ "en ted",
+ "ent ed",
+ "ente d",
+ "an im",
+ "ani m",
+ "a nim",
+ "ve rb",
+ "ver b",
+ "st o",
+ "s to",
+ "▁B ible",
+ "▁Bi ble",
+ "pend ing",
+ "pen ding",
+ "p ending",
+ "▁P hot",
+ "▁Ph ot",
+ "st rap",
+ "str ap",
+ "stra p",
+ "ie ron",
+ "ier on",
+ "iero n",
+ "i eron",
+ "P G",
+ "cul ar",
+ "cu lar",
+ "c ular",
+ "cri t",
+ "cr it",
+ "c rit",
+ "ur d",
+ "u rd",
+ "EN O",
+ "E NO",
+ "▁nor thern",
+ "▁north ern",
+ "▁natural ly",
+ "▁natur ally",
+ "< '",
+ "we g",
+ "w eg",
+ "▁dr unk",
+ "▁D al",
+ "▁Da l",
+ "▁m ouse",
+ "▁mo use",
+ "▁mou se",
+ "▁ mouse",
+ "▁contin uous",
+ "▁continu ous",
+ "▁init ially",
+ "▁initial ly",
+ "▁initi ally",
+ "ag u",
+ "a gu",
+ "м пи",
+ "AN T",
+ "A NT",
+ "Di v",
+ "D iv",
+ "▁rec ording",
+ "▁record ing",
+ "Bin d",
+ "Bi nd",
+ "B ind",
+ "▁correct ly",
+ "init ial",
+ "▁R ights",
+ "▁Right s",
+ "▁deb ate",
+ "WR ITE",
+ "bu ilt",
+ "▁per mit",
+ "▁perm it",
+ "▁professional s",
+ "▁profession als",
+ "c v",
+ "▁D I",
+ "▁ DI",
+ "▁h anded",
+ "▁hand ed",
+ "▁han ded",
+ "▁ handed",
+ "▁C u",
+ "▁H ospital",
+ "▁besk revs",
+ "не й",
+ "н ей",
+ "но ст",
+ "▁anx iety",
+ "▁heav ily",
+ "▁V ar",
+ "▁Va r",
+ "▁ Var",
+ "▁dis pos",
+ "▁disp os",
+ "+ \"",
+ "▁E ver",
+ "▁Ev er",
+ "▁Eve r",
+ "iz on",
+ "izo n",
+ "i zon",
+ "▁oper ators",
+ "▁operator s",
+ "ne go",
+ "neg o",
+ "n ego",
+ "▁B ry",
+ "▁Br y",
+ "▁v otes",
+ "▁vo tes",
+ "▁vote s",
+ "▁vot es",
+ "iz ione",
+ "izi one",
+ "izio ne",
+ "i zione",
+ "▁ра й",
+ "▁fe at",
+ "▁ feat",
+ "▁w estern",
+ "▁west ern",
+ "▁ western",
+ "▁con front",
+ "▁strong er",
+ "▁ф а",
+ "▁ фа",
+ "st re",
+ "str e",
+ "s tre",
+ "▁Val id",
+ "▁ Valid",
+ "▁n ad",
+ "▁na d",
+ "▁check ing",
+ "▁bird s",
+ "▁North ern",
+ "▁Nor thern",
+ "▁int ention",
+ "▁intent ion",
+ "uc e",
+ "u ce",
+ "▁co vers",
+ "▁cover s",
+ "▁cov ers",
+ "▁wonder ing",
+ "▁Option al",
+ "▁Opt ional",
+ "▁ Optional",
+ "pro tocol",
+ "proto col",
+ "prot ocol",
+ "▁ag gress",
+ "— —",
+ "V ec",
+ "▁d ates",
+ "▁da tes",
+ "▁dat es",
+ "▁date s",
+ "▁ dates",
+ "qu ot",
+ "▁b om",
+ "▁bo m",
+ "▁s can",
+ "▁sc an",
+ "▁ scan",
+ "▁I tem",
+ "▁It em",
+ "▁ Item",
+ "▁N avy",
+ "▁Na vy",
+ "▁Nav y",
+ "▁G ran",
+ "▁Gr an",
+ "▁Gra n",
+ "▁every body",
+ "▁un expected",
+ "▁une xpected",
+ "▁di vor",
+ "▁div or",
+ "▁e ase",
+ "▁eas e",
+ "um bled",
+ "umb led",
+ "umble d",
+ "^ +",
+ "cu ss",
+ "c uss",
+ "▁p ale",
+ "▁pal e",
+ "▁pa le",
+ "▁In ga",
+ "▁Ing a",
+ "▁B road",
+ "▁Br oad",
+ "▁Bro ad",
+ "▁ Broad",
+ "▁Med ic",
+ "▁R oy",
+ "▁Ro y",
+ "▁I nn",
+ "▁In n",
+ "▁p ens",
+ "▁pe ns",
+ "▁pen s",
+ "P N",
+ ". :",
+ "▁princip le",
+ "▁let ting",
+ "▁lett ing",
+ "▁condu cted",
+ "▁conduct ed",
+ "F ALSE",
+ "▁O S",
+ "▁ OS",
+ "F ocus",
+ "▁measure d",
+ "▁meas ured",
+ "▁Dem ocratic",
+ "▁Democr atic",
+ "▁Democrat ic",
+ "Hi gh",
+ "H igh",
+ "▁p ré",
+ "▁pr é",
+ "en nes",
+ "enn es",
+ "enne s",
+ "▁ind icates",
+ "▁indic ates",
+ "▁indicate s",
+ "▁en ding",
+ "▁end ing",
+ "▁ ending",
+ "▁Sm all",
+ "▁ Small",
+ "▁< !--",
+ "▁