thenukegun10x commited on
Commit
9b994a0
·
verified ·
1 Parent(s): 8a6c7fe

Upload DivCenterer v5 1.1M model

Browse files

Safetensors weights, custom config and tokenizer, implementation, and model card. Greedy exact CSS: 980/980 across the synthetic evaluation suite.

Files changed (6) hide show
  1. README.md +53 -0
  2. config.json +11 -0
  3. model.py +168 -0
  4. model.safetensors +3 -0
  5. tokenizer.json +2054 -0
  6. tokenizer.py +138 -0
README.md ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ tags:
3
+ - text-generation
4
+ - css
5
+ - synthetic-data
6
+ - custom-model
7
+ ---
8
+
9
+ # DivCenterer
10
+
11
+ A deliberately tiny, single-task decoder-only Transformer that generates CSS to center one div with either CSS Grid or Flexbox.
12
+
13
+ ## Model
14
+
15
+ - 1,107,216 parameters; 4 decoder layers, width 144, 4 attention heads, SwiGLU width 384.
16
+ - Trained from scratch with BF16 autocast and PyTorch SDPA on an AMD Radeon RX 9070 XT.
17
+ - Custom 768-token constrained byte-BPE tokenizer; this is not a Transformers `AutoModel` checkpoint.
18
+ - Files: `model.safetensors` (weights), `config.json`, `tokenizer.json`, `model.py`, and `tokenizer.py`.
19
+
20
+ Load the weights with the included implementation:
21
+
22
+ ```python
23
+ import json
24
+ import torch
25
+ from safetensors.torch import load_file
26
+ from model import DivCentererLM, ModelConfig
27
+
28
+ with open("config.json", encoding="utf-8") as stream:
29
+ config = ModelConfig(**json.load(stream))
30
+ model = DivCentererLM(config)
31
+ model.load_state_dict(load_file("model.safetensors"))
32
+ model.eval()
33
+ ```
34
+
35
+ ## Data and evaluation
36
+
37
+ The model was trained on a deterministic synthetic dataset. The prompts and HTML are generated from project-specific templates and word lists; no downloaded corpus or pretrained model weights were used. The model and dataset were developed with AI assistance.
38
+
39
+ Greedy exact-CSS results on the full synthetic evaluation suite:
40
+
41
+ | Split | Exact |
42
+ | --- | ---: |
43
+ | Validation | 410/410 |
44
+ | Held-out pairs | 410/410 |
45
+ | Unseen values | 64/64 |
46
+ | Held-out phrasing | 96/96 |
47
+ | **Total** | **980/980** |
48
+
49
+ These scores measure exact matches against the generated dataset's canonical CSS answers.
50
+
51
+ ## License
52
+
53
+ No reuse license is specified in this repository.
config.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "vocab_size": 768,
3
+ "block_size": 256,
4
+ "n_layer": 4,
5
+ "n_head": 4,
6
+ "n_embd": 144,
7
+ "ffn_dim": 384,
8
+ "rope_theta": 10000.0,
9
+ "dropout": 0.0,
10
+ "tie_embeddings": true
11
+ }
model.py ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import asdict, dataclass
4
+ import math
5
+
6
+ import torch
7
+ from torch import nn
8
+ from torch.nn import functional as F
9
+
10
+
11
+ @dataclass
12
+ class ModelConfig:
13
+ vocab_size: int
14
+ block_size: int = 256
15
+ n_layer: int = 3
16
+ n_head: int = 4
17
+ n_embd: int = 96
18
+ ffn_dim: int = 256
19
+ rope_theta: float = 10000.0
20
+ dropout: float = 0.0
21
+ tie_embeddings: bool = True
22
+
23
+ def validate(self) -> None:
24
+ if self.n_embd % self.n_head:
25
+ raise ValueError("n_embd must be divisible by n_head")
26
+ if (self.n_embd // self.n_head) % 2:
27
+ raise ValueError("RoPE requires an even attention head dimension")
28
+ if self.block_size < 2:
29
+ raise ValueError("block_size must be at least 2")
30
+
31
+
32
+ class RMSNorm(nn.Module):
33
+ def __init__(self, width: int, eps: float = 1e-6):
34
+ super().__init__()
35
+ self.weight = nn.Parameter(torch.ones(width))
36
+ self.eps = eps
37
+
38
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
39
+ source_dtype = x.dtype
40
+ normalized = x.float() * torch.rsqrt(x.float().pow(2).mean(dim=-1, keepdim=True) + self.eps)
41
+ return normalized.to(source_dtype) * self.weight.to(source_dtype)
42
+
43
+
44
+ class RotaryEmbedding(nn.Module):
45
+ def __init__(self, head_dim: int, theta: float):
46
+ super().__init__()
47
+ inverse_frequencies = 1.0 / (theta ** (torch.arange(0, head_dim, 2, dtype=torch.float32) / head_dim))
48
+ self.register_buffer("inverse_frequencies", inverse_frequencies, persistent=False)
49
+
50
+ def forward(self, sequence_length: int, device: torch.device, dtype: torch.dtype) -> tuple[torch.Tensor, torch.Tensor]:
51
+ positions = torch.arange(sequence_length, device=device, dtype=torch.float32)
52
+ angles = torch.outer(positions, self.inverse_frequencies.to(device=device))
53
+ return angles.cos().to(dtype=dtype)[None, None, :, :], angles.sin().to(dtype=dtype)[None, None, :, :]
54
+
55
+
56
+ def apply_rope(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor:
57
+ even = x[..., 0::2]
58
+ odd = x[..., 1::2]
59
+ result = torch.empty_like(x)
60
+ result[..., 0::2] = even * cos - odd * sin
61
+ result[..., 1::2] = even * sin + odd * cos
62
+ return result
63
+
64
+
65
+ class SelfAttention(nn.Module):
66
+ def __init__(self, config: ModelConfig):
67
+ super().__init__()
68
+ self.n_head = config.n_head
69
+ self.head_dim = config.n_embd // config.n_head
70
+ self.qkv = nn.Linear(config.n_embd, 3 * config.n_embd, bias=False)
71
+ self.output = nn.Linear(config.n_embd, config.n_embd, bias=False)
72
+ self.dropout = config.dropout
73
+
74
+ def forward(self, x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor:
75
+ batch, sequence_length, width = x.shape
76
+ qkv = self.qkv(x).view(batch, sequence_length, 3, self.n_head, self.head_dim)
77
+ query, key, value = qkv.unbind(dim=2)
78
+ query = query.transpose(1, 2)
79
+ key = key.transpose(1, 2)
80
+ value = value.transpose(1, 2)
81
+ query = apply_rope(query, cos, sin)
82
+ key = apply_rope(key, cos, sin)
83
+
84
+ # PyTorch SDPA selects the available fused backend (including Flash when supported).
85
+ attended = F.scaled_dot_product_attention(
86
+ query,
87
+ key,
88
+ value,
89
+ attn_mask=None,
90
+ dropout_p=self.dropout if self.training else 0.0,
91
+ is_causal=True,
92
+ )
93
+ attended = attended.transpose(1, 2).contiguous().view(batch, sequence_length, width)
94
+ return self.output(attended)
95
+
96
+
97
+ class SwiGLU(nn.Module):
98
+ def __init__(self, width: int, hidden_width: int):
99
+ super().__init__()
100
+ self.gate = nn.Linear(width, hidden_width, bias=False)
101
+ self.up = nn.Linear(width, hidden_width, bias=False)
102
+ self.down = nn.Linear(hidden_width, width, bias=False)
103
+
104
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
105
+ return self.down(F.silu(self.gate(x)) * self.up(x))
106
+
107
+
108
+ class TransformerBlock(nn.Module):
109
+ def __init__(self, config: ModelConfig):
110
+ super().__init__()
111
+ self.attention_norm = RMSNorm(config.n_embd)
112
+ self.attention = SelfAttention(config)
113
+ self.feed_forward_norm = RMSNorm(config.n_embd)
114
+ self.feed_forward = SwiGLU(config.n_embd, config.ffn_dim)
115
+ self.residual_dropout = nn.Dropout(config.dropout)
116
+
117
+ def forward(self, x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor:
118
+ x = x + self.residual_dropout(self.attention(self.attention_norm(x), cos, sin))
119
+ x = x + self.residual_dropout(self.feed_forward(self.feed_forward_norm(x)))
120
+ return x
121
+
122
+
123
+ class DivCentererLM(nn.Module):
124
+ def __init__(self, config: ModelConfig):
125
+ super().__init__()
126
+ config.validate()
127
+ self.config = config
128
+ self.token_embedding = nn.Embedding(config.vocab_size, config.n_embd)
129
+ self.rotary = RotaryEmbedding(config.n_embd // config.n_head, config.rope_theta)
130
+ self.layers = nn.ModuleList([TransformerBlock(config) for _ in range(config.n_layer)])
131
+ self.final_norm = RMSNorm(config.n_embd)
132
+ self.lm_head = nn.Linear(config.n_embd, config.vocab_size, bias=False)
133
+ if config.tie_embeddings:
134
+ self.lm_head.weight = self.token_embedding.weight
135
+ self.apply(self._initialize_weights)
136
+
137
+ @staticmethod
138
+ def _initialize_weights(module: nn.Module) -> None:
139
+ if isinstance(module, nn.Linear):
140
+ nn.init.normal_(module.weight, mean=0.0, std=0.02)
141
+ if module.bias is not None:
142
+ nn.init.zeros_(module.bias)
143
+ elif isinstance(module, nn.Embedding):
144
+ nn.init.normal_(module.weight, mean=0.0, std=0.02)
145
+
146
+ def forward(self, input_ids: torch.Tensor, labels: torch.Tensor | None = None) -> tuple[torch.Tensor, torch.Tensor | None]:
147
+ _, sequence_length = input_ids.shape
148
+ if sequence_length > self.config.block_size:
149
+ raise ValueError(f"Sequence length {sequence_length} exceeds block_size={self.config.block_size}")
150
+ x = self.token_embedding(input_ids)
151
+ cos, sin = self.rotary(sequence_length, input_ids.device, x.dtype)
152
+ for layer in self.layers:
153
+ x = layer(x, cos, sin)
154
+ logits = self.lm_head(self.final_norm(x))
155
+ loss = None
156
+ if labels is not None:
157
+ loss = F.cross_entropy(
158
+ logits.float().reshape(-1, self.config.vocab_size),
159
+ labels.reshape(-1),
160
+ ignore_index=-100,
161
+ )
162
+ return logits, loss
163
+
164
+ def parameter_count(self) -> int:
165
+ return sum(parameter.numel() for parameter in self.parameters())
166
+
167
+ def config_dict(self) -> dict:
168
+ return asdict(self.config)
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:88e2d9f7df927fde0c560d0e2fe52bff47399229f35cac26c8b9b5276bb50370
3
+ size 4874376
tokenizer.json ADDED
@@ -0,0 +1,2054 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "format": "divcenterer-constrained-byte-bpe-v1",
3
+ "base_vocab_size": 256,
4
+ "special_tokens": [
5
+ "<|user|>",
6
+ "<|assistant|>",
7
+ "<|end|>"
8
+ ],
9
+ "merges": [
10
+ [
11
+ 32,
12
+ 32
13
+ ],
14
+ [
15
+ 10,
16
+ 259
17
+ ],
18
+ [
19
+ 110,
20
+ 116
21
+ ],
22
+ [
23
+ 58,
24
+ 32
25
+ ],
26
+ [
27
+ 105,
28
+ 110
29
+ ],
30
+ [
31
+ 100,
32
+ 105
33
+ ],
34
+ [
35
+ 98,
36
+ 111
37
+ ],
38
+ [
39
+ 101,
40
+ 261
41
+ ],
42
+ [
43
+ 104,
44
+ 101
45
+ ],
46
+ [
47
+ 115,
48
+ 116
49
+ ],
50
+ [
51
+ 100,
52
+ 121
53
+ ],
54
+ [
55
+ 103,
56
+ 101
57
+ ],
58
+ [
59
+ 60,
60
+ 47
61
+ ],
62
+ [
63
+ 265,
64
+ 269
65
+ ],
66
+ [
67
+ 101,
68
+ 114
69
+ ],
70
+ [
71
+ 264,
72
+ 118
73
+ ],
74
+ [
75
+ 105,
76
+ 100
77
+ ],
78
+ [
79
+ 108,
80
+ 97
81
+ ],
82
+ [
83
+ 97,
84
+ 270
85
+ ],
86
+ [
87
+ 266,
88
+ 273
89
+ ],
90
+ [
91
+ 268,
92
+ 277
93
+ ],
94
+ [
95
+ 108,
96
+ 101
97
+ ],
98
+ [
99
+ 97,
100
+ 114
101
+ ],
102
+ [
103
+ 105,
104
+ 116
105
+ ],
106
+ [
107
+ 99,
108
+ 278
109
+ ],
110
+ [
111
+ 10,
112
+ 10
113
+ ],
114
+ [
115
+ 34,
116
+ 62
117
+ ],
118
+ [
119
+ 61,
120
+ 34
121
+ ],
122
+ [
123
+ 272,
124
+ 62
125
+ ],
126
+ [
127
+ 116,
128
+ 267
129
+ ],
130
+ [
131
+ 111,
132
+ 110
133
+ ],
134
+ [
135
+ 105,
136
+ 103
137
+ ],
138
+ [
139
+ 112,
140
+ 276
141
+ ],
142
+ [
143
+ 262,
144
+ 283
145
+ ],
146
+ [
147
+ 292,
148
+ 59
149
+ ],
150
+ [
151
+ 116,
152
+ 105
153
+ ],
154
+ [
155
+ 67,
156
+ 83
157
+ ],
158
+ [
159
+ 295,
160
+ 83
161
+ ],
162
+ [
163
+ 60,
164
+ 274
165
+ ],
166
+ [
167
+ 271,
168
+ 274
169
+ ],
170
+ [
171
+ 297,
172
+ 32
173
+ ],
174
+ [
175
+ 298,
176
+ 62
177
+ ],
178
+ [
179
+ 48,
180
+ 48
181
+ ],
182
+ [
183
+ 282,
184
+ 101
185
+ ],
186
+ [
187
+ 72,
188
+ 84
189
+ ],
190
+ [
191
+ 77,
192
+ 76
193
+ ],
194
+ [
195
+ 303,
196
+ 304
197
+ ],
198
+ [
199
+ 104,
200
+ 116
201
+ ],
202
+ [
203
+ 290,
204
+ 306
205
+ ],
206
+ [
207
+ 263,
208
+ 45
209
+ ],
210
+ [
211
+ 97,
212
+ 108
213
+ ],
214
+ [
215
+ 267,
216
+ 307
217
+ ],
218
+ [
219
+ 109,
220
+ 281
221
+ ],
222
+ [
223
+ 286,
224
+ 279
225
+ ],
226
+ [
227
+ 108,
228
+ 121
229
+ ],
230
+ [
231
+ 114,
232
+ 275
233
+ ],
234
+ [
235
+ 103,
236
+ 263
237
+ ],
238
+ [
239
+ 49,
240
+ 301
241
+ ],
242
+ [
243
+ 45,
244
+ 302
245
+ ],
246
+ [
247
+ 48,
248
+ 59
249
+ ],
250
+ [
251
+ 60,
252
+ 287
253
+ ],
254
+ [
255
+ 99,
256
+ 276
257
+ ],
258
+ [
259
+ 104,
260
+ 59
261
+ ],
262
+ [
263
+ 109,
264
+ 115
265
+ ],
266
+ [
267
+ 109,
268
+ 308
269
+ ],
270
+ [
271
+ 115,
272
+ 115
273
+ ],
274
+ [
275
+ 115,
276
+ 291
277
+ ],
278
+ [
279
+ 118,
280
+ 321
281
+ ],
282
+ [
283
+ 121,
284
+ 262
285
+ ],
286
+ [
287
+ 260,
288
+ 259
289
+ ],
290
+ [
291
+ 262,
292
+ 316
293
+ ],
294
+ [
295
+ 262,
296
+ 318
297
+ ],
298
+ [
299
+ 264,
300
+ 325
301
+ ],
302
+ [
303
+ 271,
304
+ 287
305
+ ],
306
+ [
307
+ 280,
308
+ 120
309
+ ],
310
+ [
311
+ 299,
312
+ 275
313
+ ],
314
+ [
315
+ 310,
316
+ 329
317
+ ],
318
+ [
319
+ 311,
320
+ 315
321
+ ],
322
+ [
323
+ 312,
324
+ 285
325
+ ],
326
+ [
327
+ 317,
328
+ 322
329
+ ],
330
+ [
331
+ 320,
332
+ 324
333
+ ],
334
+ [
335
+ 323,
336
+ 335
337
+ ],
338
+ [
339
+ 331,
340
+ 327
341
+ ],
342
+ [
343
+ 336,
344
+ 330
345
+ ],
346
+ [
347
+ 338,
348
+ 293
349
+ ],
350
+ [
351
+ 339,
352
+ 337
353
+ ],
354
+ [
355
+ 340,
356
+ 326
357
+ ],
358
+ [
359
+ 111,
360
+ 114
361
+ ],
362
+ [
363
+ 97,
364
+ 110
365
+ ],
366
+ [
367
+ 115,
368
+ 101
369
+ ],
370
+ [
371
+ 111,
372
+ 261
373
+ ],
374
+ [
375
+ 334,
376
+ 286
377
+ ],
378
+ [
379
+ 45,
380
+ 49
381
+ ],
382
+ [
383
+ 45,
384
+ 48
385
+ ],
386
+ [
387
+ 32,
388
+ 344
389
+ ],
390
+ [
391
+ 99,
392
+ 349
393
+ ],
394
+ [
395
+ 105,
396
+ 101
397
+ ],
398
+ [
399
+ 294,
400
+ 99
401
+ ],
402
+ [
403
+ 109,
404
+ 97
405
+ ],
406
+ [
407
+ 294,
408
+ 289
409
+ ],
410
+ [
411
+ 289,
412
+ 313
413
+ ],
414
+ [
415
+ 265,
416
+ 120
417
+ ],
418
+ [
419
+ 354,
420
+ 266
421
+ ],
422
+ [
423
+ 263,
424
+ 103
425
+ ],
426
+ [
427
+ 99,
428
+ 358
429
+ ],
430
+ [
431
+ 101,
432
+ 116
433
+ ],
434
+ [
435
+ 119,
436
+ 282
437
+ ],
438
+ [
439
+ 365,
440
+ 104
441
+ ],
442
+ [
443
+ 117,
444
+ 115
445
+ ],
446
+ [
447
+ 117,
448
+ 268
449
+ ],
450
+ [
451
+ 347,
452
+ 100
453
+ ],
454
+ [
455
+ 116,
456
+ 104
457
+ ],
458
+ [
459
+ 290,
460
+ 110
461
+ ],
462
+ [
463
+ 309,
464
+ 371
465
+ ],
466
+ [
467
+ 112,
468
+ 346
469
+ ],
470
+ [
471
+ 118,
472
+ 355
473
+ ],
474
+ [
475
+ 119,
476
+ 373
477
+ ],
478
+ [
479
+ 374,
480
+ 375
481
+ ],
482
+ [
483
+ 376,
484
+ 116
485
+ ],
486
+ [
487
+ 106,
488
+ 368
489
+ ],
490
+ [
491
+ 117,
492
+ 116
493
+ ],
494
+ [
495
+ 281,
496
+ 356
497
+ ],
498
+ [
499
+ 380,
500
+ 280
501
+ ],
502
+ [
503
+ 348,
504
+ 363
505
+ ],
506
+ [
507
+ 45,
508
+ 361
509
+ ],
510
+ [
511
+ 70,
512
+ 333
513
+ ],
514
+ [
515
+ 71,
516
+ 314
517
+ ],
518
+ [
519
+ 99,
520
+ 101
521
+ ],
522
+ [
523
+ 102,
524
+ 121
525
+ ],
526
+ [
527
+ 102,
528
+ 333
529
+ ],
530
+ [
531
+ 103,
532
+ 314
533
+ ],
534
+ [
535
+ 105,
536
+ 387
537
+ ],
538
+ [
539
+ 291,
540
+ 386
541
+ ],
542
+ [
543
+ 341,
544
+ 388
545
+ ],
546
+ [
547
+ 341,
548
+ 389
549
+ ],
550
+ [
551
+ 372,
552
+ 343
553
+ ],
554
+ [
555
+ 378,
556
+ 390
557
+ ],
558
+ [
559
+ 383,
560
+ 293
561
+ ],
562
+ [
563
+ 384,
564
+ 360
565
+ ],
566
+ [
567
+ 391,
568
+ 343
569
+ ],
570
+ [
571
+ 392,
572
+ 59
573
+ ],
574
+ [
575
+ 393,
576
+ 59
577
+ ],
578
+ [
579
+ 395,
580
+ 396
581
+ ],
582
+ [
583
+ 357,
584
+ 263
585
+ ],
586
+ [
587
+ 270,
588
+ 116
589
+ ],
590
+ [
591
+ 114,
592
+ 111
593
+ ],
594
+ [
595
+ 105,
596
+ 115
597
+ ],
598
+ [
599
+ 367,
600
+ 362
601
+ ],
602
+ [
603
+ 309,
604
+ 313
605
+ ],
606
+ [
607
+ 117,
608
+ 112
609
+ ],
610
+ [
611
+ 117,
612
+ 114
613
+ ],
614
+ [
615
+ 114,
616
+ 101
617
+ ],
618
+ [
619
+ 97,
620
+ 116
621
+ ],
622
+ [
623
+ 364,
624
+ 409
625
+ ],
626
+ [
627
+ 412,
628
+ 110
629
+ ],
630
+ [
631
+ 104,
632
+ 346
633
+ ],
634
+ [
635
+ 116,
636
+ 281
637
+ ],
638
+ [
639
+ 415,
640
+ 403
641
+ ],
642
+ [
643
+ 101,
644
+ 108
645
+ ],
646
+ [
647
+ 118,
648
+ 273
649
+ ],
650
+ [
651
+ 60,
652
+ 381
653
+ ],
654
+ [
655
+ 271,
656
+ 381
657
+ ],
658
+ [
659
+ 419,
660
+ 353
661
+ ],
662
+ [
663
+ 420,
664
+ 62
665
+ ],
666
+ [
667
+ 60,
668
+ 382
669
+ ],
670
+ [
671
+ 271,
672
+ 382
673
+ ],
674
+ [
675
+ 423,
676
+ 353
677
+ ],
678
+ [
679
+ 424,
680
+ 62
681
+ ],
682
+ [
683
+ 85,
684
+ 348
685
+ ],
686
+ [
687
+ 105,
688
+ 122
689
+ ],
690
+ [
691
+ 299,
692
+ 344
693
+ ],
694
+ [
695
+ 60,
696
+ 402
697
+ ],
698
+ [
699
+ 271,
700
+ 402
701
+ ],
702
+ [
703
+ 430,
704
+ 353
705
+ ],
706
+ [
707
+ 431,
708
+ 62
709
+ ],
710
+ [
711
+ 107,
712
+ 101
713
+ ],
714
+ [
715
+ 282,
716
+ 115
717
+ ],
718
+ [
719
+ 101,
720
+ 100
721
+ ],
722
+ [
723
+ 67,
724
+ 278
725
+ ],
726
+ [
727
+ 111,
728
+ 102
729
+ ],
730
+ [
731
+ 99,
732
+ 116
733
+ ],
734
+ [
735
+ 101,
736
+ 280
737
+ ],
738
+ [
739
+ 101,
740
+ 110
741
+ ],
742
+ [
743
+ 114,
744
+ 413
745
+ ],
746
+ [
747
+ 108,
748
+ 108
749
+ ],
750
+ [
751
+ 265,
752
+ 370
753
+ ],
754
+ [
755
+ 121,
756
+ 280
757
+ ],
758
+ [
759
+ 112,
760
+ 347
761
+ ],
762
+ [
763
+ 349,
764
+ 407
765
+ ],
766
+ [
767
+ 356,
768
+ 407
769
+ ],
770
+ [
771
+ 414,
772
+ 428
773
+ ],
774
+ [
775
+ 418,
776
+ 448
777
+ ],
778
+ [
779
+ 449,
780
+ 447
781
+ ],
782
+ [
783
+ 112,
784
+ 379
785
+ ],
786
+ [
787
+ 84,
788
+ 267
789
+ ],
790
+ [
791
+ 112,
792
+ 281
793
+ ],
794
+ [
795
+ 454,
796
+ 266
797
+ ],
798
+ [
799
+ 105,
800
+ 118
801
+ ],
802
+ [
803
+ 109,
804
+ 266
805
+ ],
806
+ [
807
+ 440,
808
+ 457
809
+ ],
810
+ [
811
+ 446,
812
+ 417
813
+ ],
814
+ [
815
+ 111,
816
+ 99
817
+ ],
818
+ [
819
+ 97,
820
+ 434
821
+ ],
822
+ [
823
+ 116,
824
+ 111
825
+ ],
826
+ [
827
+ 119,
828
+ 275
829
+ ],
830
+ [
831
+ 463,
832
+ 403
833
+ ],
834
+ [
835
+ 97,
836
+ 348
837
+ ],
838
+ [
839
+ 280,
840
+ 465
841
+ ],
842
+ [
843
+ 99,
844
+ 281
845
+ ],
846
+ [
847
+ 467,
848
+ 100
849
+ ],
850
+ [
851
+ 268,
852
+ 445
853
+ ],
854
+ [
855
+ 108,
856
+ 355
857
+ ],
858
+ [
859
+ 112,
860
+ 470
861
+ ],
862
+ [
863
+ 115,
864
+ 408
865
+ ],
866
+ [
867
+ 471,
868
+ 100
869
+ ],
870
+ [
871
+ 472,
872
+ 473
873
+ ],
874
+ [
875
+ 114,
876
+ 302
877
+ ],
878
+ [
879
+ 110,
880
+ 111
881
+ ],
882
+ [
883
+ 370,
884
+ 411
885
+ ],
886
+ [
887
+ 97,
888
+ 112
889
+ ],
890
+ [
891
+ 82,
892
+ 413
893
+ ],
894
+ [
895
+ 109,
896
+ 275
897
+ ],
898
+ [
899
+ 379,
900
+ 452
901
+ ],
902
+ [
903
+ 115,
904
+ 111
905
+ ],
906
+ [
907
+ 111,
908
+ 119
909
+ ],
910
+ [
911
+ 115,
912
+ 99
913
+ ],
914
+ [
915
+ 354,
916
+ 404
917
+ ],
918
+ [
919
+ 485,
920
+ 108
921
+ ],
922
+ [
923
+ 99,
924
+ 414
925
+ ],
926
+ [
927
+ 347,
928
+ 487
929
+ ],
930
+ [
931
+ 410,
932
+ 441
933
+ ],
934
+ [
935
+ 49,
936
+ 55
937
+ ],
938
+ [
939
+ 98,
940
+ 106
941
+ ],
942
+ [
943
+ 101,
944
+ 439
945
+ ],
946
+ [
947
+ 111,
948
+ 491
949
+ ],
950
+ [
951
+ 493,
952
+ 492
953
+ ],
954
+ [
955
+ 357,
956
+ 308
957
+ ],
958
+ [
959
+ 102,
960
+ 460
961
+ ],
962
+ [
963
+ 370,
964
+ 362
965
+ ],
966
+ [
967
+ 496,
968
+ 367
969
+ ],
970
+ [
971
+ 49,
972
+ 56
973
+ ],
974
+ [
975
+ 49,
976
+ 54
977
+ ],
978
+ [
979
+ 49,
980
+ 50
981
+ ],
982
+ [
983
+ 49,
984
+ 57
985
+ ],
986
+ [
987
+ 267,
988
+ 404
989
+ ],
990
+ [
991
+ 49,
992
+ 51
993
+ ],
994
+ [
995
+ 49,
996
+ 52
997
+ ],
998
+ [
999
+ 48,
1000
+ 51
1001
+ ],
1002
+ [
1003
+ 48,
1004
+ 52
1005
+ ],
1006
+ [
1007
+ 49,
1008
+ 53
1009
+ ],
1010
+ [
1011
+ 377,
1012
+ 45
1013
+ ],
1014
+ [
1015
+ 110,
1016
+ 101
1017
+ ],
1018
+ [
1019
+ 111,
1020
+ 117
1021
+ ],
1022
+ [
1023
+ 48,
1024
+ 50
1025
+ ],
1026
+ [
1027
+ 48,
1028
+ 54
1029
+ ],
1030
+ [
1031
+ 77,
1032
+ 461
1033
+ ],
1034
+ [
1035
+ 469,
1036
+ 115
1037
+ ],
1038
+ [
1039
+ 366,
1040
+ 263
1041
+ ],
1042
+ [
1043
+ 264,
1044
+ 410
1045
+ ],
1046
+ [
1047
+ 351,
1048
+ 50
1049
+ ],
1050
+ [
1051
+ 363,
1052
+ 115
1053
+ ],
1054
+ [
1055
+ 517,
1056
+ 519
1057
+ ],
1058
+ [
1059
+ 80,
1060
+ 379
1061
+ ],
1062
+ [
1063
+ 275,
1064
+ 101
1065
+ ],
1066
+ [
1067
+ 476,
1068
+ 116
1069
+ ],
1070
+ [
1071
+ 484,
1072
+ 489
1073
+ ],
1074
+ [
1075
+ 351,
1076
+ 55
1077
+ ],
1078
+ [
1079
+ 80,
1080
+ 466
1081
+ ],
1082
+ [
1083
+ 87,
1084
+ 475
1085
+ ],
1086
+ [
1087
+ 283,
1088
+ 115
1089
+ ],
1090
+ [
1091
+ 107,
1092
+ 408
1093
+ ],
1094
+ [
1095
+ 311,
1096
+ 529
1097
+ ],
1098
+ [
1099
+ 48,
1100
+ 56
1101
+ ],
1102
+ [
1103
+ 48,
1104
+ 55
1105
+ ],
1106
+ [
1107
+ 71,
1108
+ 456
1109
+ ],
1110
+ [
1111
+ 100,
1112
+ 280
1113
+ ],
1114
+ [
1115
+ 480,
1116
+ 534
1117
+ ],
1118
+ [
1119
+ 533,
1120
+ 101
1121
+ ],
1122
+ [
1123
+ 45,
1124
+ 301
1125
+ ],
1126
+ [
1127
+ 283,
1128
+ 436
1129
+ ],
1130
+ [
1131
+ 79,
1132
+ 481
1133
+ ],
1134
+ [
1135
+ 102,
1136
+ 105
1137
+ ],
1138
+ [
1139
+ 112,
1140
+ 404
1141
+ ],
1142
+ [
1143
+ 540,
1144
+ 443
1145
+ ],
1146
+ [
1147
+ 541,
1148
+ 118
1149
+ ],
1150
+ [
1151
+ 351,
1152
+ 51
1153
+ ],
1154
+ [
1155
+ 351,
1156
+ 57
1157
+ ],
1158
+ [
1159
+ 49,
1160
+ 285
1161
+ ],
1162
+ [
1163
+ 352,
1164
+ 55
1165
+ ],
1166
+ [
1167
+ 48,
1168
+ 285
1169
+ ],
1170
+ [
1171
+ 53,
1172
+ 285
1173
+ ],
1174
+ [
1175
+ 351,
1176
+ 56
1177
+ ],
1178
+ [
1179
+ 352,
1180
+ 53
1181
+ ],
1182
+ [
1183
+ 48,
1184
+ 57
1185
+ ],
1186
+ [
1187
+ 351,
1188
+ 52
1189
+ ],
1190
+ [
1191
+ 352,
1192
+ 52
1193
+ ],
1194
+ [
1195
+ 352,
1196
+ 54
1197
+ ],
1198
+ [
1199
+ 352,
1200
+ 56
1201
+ ],
1202
+ [
1203
+ 352,
1204
+ 50
1205
+ ],
1206
+ [
1207
+ 351,
1208
+ 54
1209
+ ],
1210
+ [
1211
+ 352,
1212
+ 51
1213
+ ],
1214
+ [
1215
+ 101,
1216
+ 112
1217
+ ],
1218
+ [
1219
+ 351,
1220
+ 53
1221
+ ],
1222
+ [
1223
+ 52,
1224
+ 285
1225
+ ],
1226
+ [
1227
+ 352,
1228
+ 57
1229
+ ],
1230
+ [
1231
+ 478,
1232
+ 112
1233
+ ],
1234
+ [
1235
+ 54,
1236
+ 285
1237
+ ],
1238
+ [
1239
+ 48,
1240
+ 53
1241
+ ],
1242
+ [
1243
+ 50,
1244
+ 285
1245
+ ],
1246
+ [
1247
+ 55,
1248
+ 285
1249
+ ],
1250
+ [
1251
+ 108,
1252
+ 117
1253
+ ],
1254
+ [
1255
+ 99,
1256
+ 408
1257
+ ],
1258
+ [
1259
+ 100,
1260
+ 100
1261
+ ],
1262
+ [
1263
+ 102,
1264
+ 117
1265
+ ],
1266
+ [
1267
+ 51,
1268
+ 285
1269
+ ],
1270
+ [
1271
+ 56,
1272
+ 285
1273
+ ],
1274
+ [
1275
+ 97,
1276
+ 439
1277
+ ],
1278
+ [
1279
+ 57,
1280
+ 285
1281
+ ],
1282
+ [
1283
+ 108,
1284
+ 100
1285
+ ],
1286
+ [
1287
+ 350,
1288
+ 459
1289
+ ],
1290
+ [
1291
+ 113,
1292
+ 117
1293
+ ],
1294
+ [
1295
+ 114,
1296
+ 405
1297
+ ],
1298
+ [
1299
+ 334,
1300
+ 312
1301
+ ],
1302
+ [
1303
+ 350,
1304
+ 464
1305
+ ],
1306
+ [
1307
+ 350,
1308
+ 486
1309
+ ],
1310
+ [
1311
+ 350,
1312
+ 488
1313
+ ],
1314
+ [
1315
+ 109,
1316
+ 111
1317
+ ],
1318
+ [
1319
+ 350,
1320
+ 494
1321
+ ],
1322
+ [
1323
+ 99,
1324
+ 111
1325
+ ],
1326
+ [
1327
+ 350,
1328
+ 416
1329
+ ],
1330
+ [
1331
+ 350,
1332
+ 458
1333
+ ],
1334
+ [
1335
+ 350,
1336
+ 495
1337
+ ],
1338
+ [
1339
+ 350,
1340
+ 361
1341
+ ],
1342
+ [
1343
+ 350,
1344
+ 497
1345
+ ],
1346
+ [
1347
+ 350,
1348
+ 498
1349
+ ],
1350
+ [
1351
+ 99,
1352
+ 461
1353
+ ],
1354
+ [
1355
+ 97,
1356
+ 268
1357
+ ],
1358
+ [
1359
+ 350,
1360
+ 283
1361
+ ],
1362
+ [
1363
+ 350,
1364
+ 360
1365
+ ],
1366
+ [
1367
+ 350,
1368
+ 468
1369
+ ],
1370
+ [
1371
+ 105,
1372
+ 99
1373
+ ],
1374
+ [
1375
+ 350,
1376
+ 503
1377
+ ],
1378
+ [
1379
+ 99,
1380
+ 108
1381
+ ],
1382
+ [
1383
+ 601,
1384
+ 511
1385
+ ],
1386
+ [
1387
+ 112,
1388
+ 466
1389
+ ],
1390
+ [
1391
+ 509,
1392
+ 310
1393
+ ],
1394
+ [
1395
+ 115,
1396
+ 522
1397
+ ],
1398
+ [
1399
+ 263,
1400
+ 605
1401
+ ],
1402
+ [
1403
+ 75,
1404
+ 101
1405
+ ],
1406
+ [
1407
+ 103,
1408
+ 456
1409
+ ],
1410
+ [
1411
+ 117,
1412
+ 309
1413
+ ],
1414
+ [
1415
+ 118,
1416
+ 405
1417
+ ],
1418
+ [
1419
+ 607,
1420
+ 560
1421
+ ],
1422
+ [
1423
+ 608,
1424
+ 441
1425
+ ],
1426
+ [
1427
+ 610,
1428
+ 609
1429
+ ],
1430
+ [
1431
+ 83,
1432
+ 364
1433
+ ],
1434
+ [
1435
+ 97,
1436
+ 115
1437
+ ],
1438
+ [
1439
+ 102,
1440
+ 346
1441
+ ],
1442
+ [
1443
+ 115,
1444
+ 428
1445
+ ],
1446
+ [
1447
+ 283,
1448
+ 362
1449
+ ],
1450
+ [
1451
+ 509,
1452
+ 617
1453
+ ],
1454
+ [
1455
+ 619,
1456
+ 436
1457
+ ],
1458
+ [
1459
+ 68,
1460
+ 111
1461
+ ],
1462
+ [
1463
+ 98,
1464
+ 404
1465
+ ],
1466
+ [
1467
+ 100,
1468
+ 483
1469
+ ],
1470
+ [
1471
+ 111,
1472
+ 481
1473
+ ],
1474
+ [
1475
+ 115,
1476
+ 273
1477
+ ],
1478
+ [
1479
+ 119,
1480
+ 263
1481
+ ],
1482
+ [
1483
+ 119,
1484
+ 475
1485
+ ],
1486
+ [
1487
+ 119,
1488
+ 625
1489
+ ],
1490
+ [
1491
+ 410,
1492
+ 627
1493
+ ],
1494
+ [
1495
+ 622,
1496
+ 628
1497
+ ],
1498
+ [
1499
+ 626,
1500
+ 623
1501
+ ],
1502
+ [
1503
+ 267,
1504
+ 364
1505
+ ],
1506
+ [
1507
+ 515,
1508
+ 632
1509
+ ],
1510
+ [
1511
+ 105,
1512
+ 261
1513
+ ],
1514
+ [
1515
+ 111,
1516
+ 634
1517
+ ],
1518
+ [
1519
+ 112,
1520
+ 289
1521
+ ],
1522
+ [
1523
+ 112,
1524
+ 635
1525
+ ],
1526
+ [
1527
+ 115,
1528
+ 636
1529
+ ],
1530
+ [
1531
+ 410,
1532
+ 638
1533
+ ],
1534
+ [
1535
+ 480,
1536
+ 637
1537
+ ],
1538
+ [
1539
+ 639,
1540
+ 100
1541
+ ],
1542
+ [
1543
+ 101,
1544
+ 281
1545
+ ],
1546
+ [
1547
+ 357,
1548
+ 434
1549
+ ],
1550
+ [
1551
+ 564,
1552
+ 642
1553
+ ],
1554
+ [
1555
+ 73,
1556
+ 116
1557
+ ],
1558
+ [
1559
+ 97,
1560
+ 120
1561
+ ],
1562
+ [
1563
+ 97,
1564
+ 121
1565
+ ],
1566
+ [
1567
+ 101,
1568
+ 115
1569
+ ],
1570
+ [
1571
+ 109,
1572
+ 101
1573
+ ],
1574
+ [
1575
+ 117,
1576
+ 348
1577
+ ],
1578
+ [
1579
+ 119,
1580
+ 647
1581
+ ],
1582
+ [
1583
+ 543,
1584
+ 522
1585
+ ],
1586
+ [
1587
+ 645,
1588
+ 115
1589
+ ],
1590
+ [
1591
+ 646,
1592
+ 648
1593
+ ],
1594
+ [
1595
+ 651,
1596
+ 115
1597
+ ],
1598
+ [
1599
+ 99,
1600
+ 104
1601
+ ],
1602
+ [
1603
+ 105,
1604
+ 112
1605
+ ],
1606
+ [
1607
+ 110,
1608
+ 657
1609
+ ],
1610
+ [
1611
+ 112,
1612
+ 364
1613
+ ],
1614
+ [
1615
+ 112,
1616
+ 411
1617
+ ],
1618
+ [
1619
+ 115,
1620
+ 658
1621
+ ],
1622
+ [
1623
+ 289,
1624
+ 101
1625
+ ],
1626
+ [
1627
+ 370,
1628
+ 405
1629
+ ],
1630
+ [
1631
+ 660,
1632
+ 656
1633
+ ],
1634
+ [
1635
+ 661,
1636
+ 659
1637
+ ],
1638
+ [
1639
+ 65,
1640
+ 571
1641
+ ],
1642
+ [
1643
+ 97,
1644
+ 100
1645
+ ],
1646
+ [
1647
+ 97,
1648
+ 105
1649
+ ],
1650
+ [
1651
+ 97,
1652
+ 118
1653
+ ],
1654
+ [
1655
+ 98,
1656
+ 280
1657
+ ],
1658
+ [
1659
+ 98,
1660
+ 417
1661
+ ],
1662
+ [
1663
+ 100,
1664
+ 101
1665
+ ],
1666
+ [
1667
+ 112,
1668
+ 277
1669
+ ],
1670
+ [
1671
+ 114,
1672
+ 117
1673
+ ],
1674
+ [
1675
+ 115,
1676
+ 119
1677
+ ],
1678
+ [
1679
+ 276,
1680
+ 670
1681
+ ],
1682
+ [
1683
+ 280,
1684
+ 115
1685
+ ],
1686
+ [
1687
+ 347,
1688
+ 675
1689
+ ],
1690
+ [
1691
+ 668,
1692
+ 676
1693
+ ],
1694
+ [
1695
+ 669,
1696
+ 679
1697
+ ],
1698
+ [
1699
+ 671,
1700
+ 483
1701
+ ],
1702
+ [
1703
+ 672,
1704
+ 667
1705
+ ],
1706
+ [
1707
+ 674,
1708
+ 677
1709
+ ],
1710
+ [
1711
+ 678,
1712
+ 273
1713
+ ],
1714
+ [
1715
+ 83,
1716
+ 116
1717
+ ],
1718
+ [
1719
+ 98,
1720
+ 121
1721
+ ],
1722
+ [
1723
+ 101,
1724
+ 120
1725
+ ],
1726
+ [
1727
+ 104,
1728
+ 511
1729
+ ],
1730
+ [
1731
+ 115,
1732
+ 435
1733
+ ],
1734
+ [
1735
+ 115,
1736
+ 688
1737
+ ],
1738
+ [
1739
+ 460,
1740
+ 570
1741
+ ],
1742
+ [
1743
+ 685,
1744
+ 445
1745
+ ],
1746
+ [
1747
+ 687,
1748
+ 575
1749
+ ],
1750
+ [
1751
+ 690,
1752
+ 577
1753
+ ],
1754
+ [
1755
+ 691,
1756
+ 121
1757
+ ],
1758
+ [
1759
+ 510,
1760
+ 436
1761
+ ],
1762
+ [
1763
+ 45,
1764
+ 310
1765
+ ],
1766
+ [
1767
+ 105,
1768
+ 109
1769
+ ],
1770
+ [
1771
+ 109,
1772
+ 263
1773
+ ],
1774
+ [
1775
+ 275,
1776
+ 436
1777
+ ],
1778
+ [
1779
+ 443,
1780
+ 697
1781
+ ],
1782
+ [
1783
+ 482,
1784
+ 569
1785
+ ],
1786
+ [
1787
+ 542,
1788
+ 362
1789
+ ],
1790
+ [
1791
+ 543,
1792
+ 700
1793
+ ],
1794
+ [
1795
+ 572,
1796
+ 701
1797
+ ],
1798
+ [
1799
+ 698,
1800
+ 309
1801
+ ],
1802
+ [
1803
+ 699,
1804
+ 706
1805
+ ],
1806
+ [
1807
+ 702,
1808
+ 358
1809
+ ],
1810
+ [
1811
+ 45,
1812
+ 50
1813
+ ],
1814
+ [
1815
+ 49,
1816
+ 49
1817
+ ],
1818
+ [
1819
+ 265,
1820
+ 116
1821
+ ],
1822
+ [
1823
+ 404,
1824
+ 711
1825
+ ],
1826
+ [
1827
+ 101,
1828
+ 478
1829
+ ],
1830
+ [
1831
+ 111,
1832
+ 116
1833
+ ],
1834
+ [
1835
+ 115,
1836
+ 438
1837
+ ],
1838
+ [
1839
+ 116,
1840
+ 713
1841
+ ],
1842
+ [
1843
+ 355,
1844
+ 116
1845
+ ],
1846
+ [
1847
+ 579,
1848
+ 717
1849
+ ],
1850
+ [
1851
+ 715,
1852
+ 116
1853
+ ],
1854
+ [
1855
+ 716,
1856
+ 714
1857
+ ],
1858
+ [
1859
+ 98,
1860
+ 282
1861
+ ],
1862
+ [
1863
+ 101,
1864
+ 277
1865
+ ],
1866
+ [
1867
+ 110,
1868
+ 281
1869
+ ],
1870
+ [
1871
+ 346,
1872
+ 721
1873
+ ],
1874
+ [
1875
+ 569,
1876
+ 723
1877
+ ],
1878
+ [
1879
+ 722,
1880
+ 114
1881
+ ],
1882
+ [
1883
+ 97,
1884
+ 109
1885
+ ],
1886
+ [
1887
+ 98,
1888
+ 273
1889
+ ],
1890
+ [
1891
+ 104,
1892
+ 111
1893
+ ],
1894
+ [
1895
+ 112,
1896
+ 580
1897
+ ],
1898
+ [
1899
+ 114,
1900
+ 478
1901
+ ],
1902
+ [
1903
+ 346,
1904
+ 347
1905
+ ],
1906
+ [
1907
+ 443,
1908
+ 483
1909
+ ],
1910
+ [
1911
+ 727,
1912
+ 728
1913
+ ],
1914
+ [
1915
+ 729,
1916
+ 733
1917
+ ],
1918
+ [
1919
+ 730,
1920
+ 109
1921
+ ],
1922
+ [
1923
+ 731,
1924
+ 275
1925
+ ],
1926
+ [
1927
+ 732,
1928
+ 270
1929
+ ],
1930
+ [
1931
+ 97,
1932
+ 103
1933
+ ],
1934
+ [
1935
+ 98,
1936
+ 314
1937
+ ],
1938
+ [
1939
+ 98,
1940
+ 368
1941
+ ],
1942
+ [
1943
+ 100,
1944
+ 114
1945
+ ],
1946
+ [
1947
+ 105,
1948
+ 114
1949
+ ],
1950
+ [
1951
+ 108,
1952
+ 302
1953
+ ],
1954
+ [
1955
+ 109,
1956
+ 364
1957
+ ],
1958
+ [
1959
+ 111,
1960
+ 370
1961
+ ],
1962
+ [
1963
+ 115,
1964
+ 309
1965
+ ],
1966
+ [
1967
+ 115,
1968
+ 411
1969
+ ],
1970
+ [
1971
+ 115,
1972
+ 579
1973
+ ],
1974
+ [
1975
+ 115,
1976
+ 585
1977
+ ],
1978
+ [
1979
+ 116,
1980
+ 121
1981
+ ],
1982
+ [
1983
+ 404,
1984
+ 741
1985
+ ],
1986
+ [
1987
+ 410,
1988
+ 108
1989
+ ],
1990
+ [
1991
+ 417,
1992
+ 744
1993
+ ],
1994
+ [
1995
+ 587,
1996
+ 745
1997
+ ],
1998
+ [
1999
+ 739,
2000
+ 289
2001
+ ],
2002
+ [
2003
+ 740,
2004
+ 270
2005
+ ],
2006
+ [
2007
+ 742,
2008
+ 756
2009
+ ],
2010
+ [
2011
+ 743,
2012
+ 753
2013
+ ],
2014
+ [
2015
+ 747,
2016
+ 751
2017
+ ],
2018
+ [
2019
+ 748,
2020
+ 754
2021
+ ],
2022
+ [
2023
+ 749,
2024
+ 759
2025
+ ],
2026
+ [
2027
+ 750,
2028
+ 746
2029
+ ],
2030
+ [
2031
+ 98,
2032
+ 379
2033
+ ],
2034
+ [
2035
+ 98,
2036
+ 580
2037
+ ],
2038
+ [
2039
+ 99,
2040
+ 280
2041
+ ],
2042
+ [
2043
+ 103,
2044
+ 266
2045
+ ]
2046
+ ],
2047
+ "vocab_size": 768,
2048
+ "requested_vocab_size": 768,
2049
+ "min_pair_frequency": 2,
2050
+ "atomizer": "syntax-aware-html-css-segments-v1",
2051
+ "merge_policy": "BPE merges stay within prose lexemes, whitespace runs, complete HTML tags, or recognized CSS declarations.",
2052
+ "training_rows": 3276,
2053
+ "training_note": "Trained on the train split only; merges cannot cross pretokenized HTML/CSS atoms."
2054
+ }
tokenizer.py ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import re
5
+ from pathlib import Path
6
+ from typing import Iterable
7
+
8
+ BASE_VOCAB_SIZE = 256
9
+ ATOM_RE = re.compile(
10
+ r"<!--[\s\S]*?-->|<!doctype\s+html>|</?[A-Za-z][^<>]*>|"
11
+ r"\b(?:display|place-items|align-items|justify-content|min-height|margin|font|padding|border|border-radius|background)\s*:\s*[^;{}]+;?|"
12
+ r"\s+|[A-Za-z_$][A-Za-z0-9_$-]*|[0-9]+(?:\.[0-9]+)?(?:%|px|rem|em|vh|vw)?|[^\x00-\x7F]|[^\w\s]",
13
+ re.IGNORECASE | re.ASCII,
14
+ )
15
+
16
+
17
+ class ConstrainedBPE:
18
+ """Byte-fallback BPE matching DivCenterer's syntax-aware Node tokenizer."""
19
+
20
+ def __init__(self, model: dict):
21
+ if model.get("format") != "divcenterer-constrained-byte-bpe-v1":
22
+ raise ValueError("Unrecognized tokenizer model format")
23
+ self.special_tokens: list[str] = model["special_tokens"]
24
+ self.special_to_id = {
25
+ token: BASE_VOCAB_SIZE + index
26
+ for index, token in enumerate(self.special_tokens)
27
+ }
28
+ self.special_by_id = {value: key for key, value in self.special_to_id.items()}
29
+ self.merge_ranks: dict[tuple[int, int], int] = {}
30
+ self.token_bytes: list[bytes | None] = [bytes([index]) for index in range(BASE_VOCAB_SIZE)]
31
+ self.token_bytes.extend([None] * len(self.special_tokens))
32
+ for rank, (left, right) in enumerate(model["merges"]):
33
+ if self.token_bytes[left] is None or self.token_bytes[right] is None:
34
+ raise ValueError(f"Merge {rank} refers to a special or missing token")
35
+ token_id = BASE_VOCAB_SIZE + len(self.special_tokens) + rank
36
+ self.merge_ranks[(left, right)] = rank
37
+ self.token_bytes.append(self.token_bytes[left] + self.token_bytes[right])
38
+ self._special_re = re.compile("(" + "|".join(re.escape(t) for t in self.special_tokens) + ")")
39
+ self._atom_cache: dict[str, list[int]] = {}
40
+
41
+ @classmethod
42
+ def from_file(cls, filename: str | Path) -> "ConstrainedBPE":
43
+ with Path(filename).open("r", encoding="utf-8") as stream:
44
+ return cls(json.load(stream))
45
+
46
+ def _atoms(self, text: str) -> list[str]:
47
+ atoms: list[str] = []
48
+ cursor = 0
49
+ for match in ATOM_RE.finditer(text):
50
+ if match.start() != cursor:
51
+ raise ValueError(f"Pretokenizer left a gap at character {cursor}")
52
+ atoms.append(match.group(0))
53
+ cursor = match.end()
54
+ if cursor != len(text):
55
+ raise ValueError(f"Pretokenizer left a gap at character {cursor}")
56
+ return atoms
57
+
58
+ @staticmethod
59
+ def _apply_merge(ids: list[int], left: int, right: int, merged: int) -> list[int]:
60
+ result: list[int] = []
61
+ index = 0
62
+ while index < len(ids):
63
+ if index + 1 < len(ids) and ids[index] == left and ids[index + 1] == right:
64
+ result.append(merged)
65
+ index += 2
66
+ else:
67
+ result.append(ids[index])
68
+ index += 1
69
+ return result
70
+
71
+ def _encode_atom(self, atom: str) -> list[int]:
72
+ cached = self._atom_cache.get(atom)
73
+ if cached is not None:
74
+ return cached.copy()
75
+ ids = list(atom.encode("utf-8"))
76
+ while len(ids) > 1:
77
+ best_rank: int | None = None
78
+ best_pair: tuple[int, int] | None = None
79
+ for left, right in zip(ids, ids[1:]):
80
+ rank = self.merge_ranks.get((left, right))
81
+ if rank is not None and (best_rank is None or rank < best_rank):
82
+ best_rank = rank
83
+ best_pair = (left, right)
84
+ if best_pair is None or best_rank is None:
85
+ break
86
+ merged_id = BASE_VOCAB_SIZE + len(self.special_tokens) + best_rank
87
+ ids = self._apply_merge(ids, best_pair[0], best_pair[1], merged_id)
88
+ self._atom_cache[atom] = ids.copy()
89
+ return ids
90
+
91
+ def encode(self, text: str) -> list[int]:
92
+ result: list[int] = []
93
+ for part in self._special_re.split(text):
94
+ if not part:
95
+ continue
96
+ special_id = self.special_to_id.get(part)
97
+ if special_id is not None:
98
+ result.append(special_id)
99
+ else:
100
+ for atom in self._atoms(part):
101
+ result.extend(self._encode_atom(atom))
102
+ return result
103
+
104
+ def decode(self, ids: Iterable[int]) -> str:
105
+ output: list[str] = []
106
+ byte_buffer = bytearray()
107
+
108
+ def flush_bytes() -> None:
109
+ if byte_buffer:
110
+ output.append(bytes(byte_buffer).decode("utf-8", errors="replace"))
111
+ byte_buffer.clear()
112
+
113
+ for token_id in ids:
114
+ special = self.special_by_id.get(int(token_id))
115
+ if special is not None:
116
+ flush_bytes()
117
+ output.append(special)
118
+ elif 0 <= int(token_id) < len(self.token_bytes) and self.token_bytes[int(token_id)] is not None:
119
+ byte_buffer.extend(self.token_bytes[int(token_id)])
120
+ else:
121
+ raise ValueError(f"Unknown token id: {token_id}")
122
+ flush_bytes()
123
+ return "".join(output)
124
+
125
+ def special_id(self, token: str) -> int:
126
+ return self.special_to_id[token]
127
+
128
+ @property
129
+ def vocab_size(self) -> int:
130
+ return len(self.token_bytes)
131
+
132
+
133
+ def conversation_prefix(prompt: str) -> str:
134
+ return f"<|user|>\n{prompt}\n<|assistant|>\n"
135
+
136
+
137
+ def conversation_text(prompt: str, answer: str) -> str:
138
+ return f"{conversation_prefix(prompt)}{answer}\n<|end|>"