Reza2kn commited on
Commit
feef108
·
verified ·
1 Parent(s): 50da2b0

Publish Bina 0.2 Rizeh Stage-8 release

Browse files
.gitattributes CHANGED
@@ -33,3 +33,7 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ checkpoint/best_accuracy.pdopt filter=lfs diff=lfs merge=lfs -text
37
+ checkpoint/best_accuracy.pdparams filter=lfs diff=lfs merge=lfs -text
38
+ inference/inference.pdiparams filter=lfs diff=lfs merge=lfs -text
39
+ inference.pdiparams filter=lfs diff=lfs merge=lfs -text
MODEL_CARD_METRICS.json ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model": "Bina 0.2 Rizeh",
3
+ "repo": "Reza2kn/Bina-0.2-Rizeh",
4
+ "benchmark": "Reza2kn/persian-ocr-double-benchmark",
5
+ "rows_per_split": 6669,
6
+ "missing_predictions": 0,
7
+ "splits": {
8
+ "handwriting": {
9
+ "wer": 19.0833,
10
+ "cer": 13.6399,
11
+ "s3": 13.7015,
12
+ "essential_error": 14.7567,
13
+ "triple_threat": 14.7656
14
+ },
15
+ "printed": {
16
+ "wer": 8.9257,
17
+ "cer": 2.3535,
18
+ "s3": 9.8512,
19
+ "essential_error": 12.0774,
20
+ "triple_threat": 8.1666
21
+ }
22
+ },
23
+ "macro_equal_split": {
24
+ "wer": 14.0045,
25
+ "cer": 7.9967,
26
+ "s3": 11.7764,
27
+ "essential_error": 13.4171,
28
+ "triple_threat": 11.4661
29
+ }
30
+ }
README.md ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ language:
4
+ - fa
5
+ pipeline_tag: image-to-text
6
+ library_name: paddleocr
7
+ tags:
8
+ - ocr
9
+ - persian
10
+ - handwriting
11
+ - paddleocr
12
+ - pp-ocrv6
13
+ - ctc
14
+ datasets:
15
+ - Reza2kn/persian-handwriting-pages-3.69m
16
+ - Reza2kn/persian-printed-ocr-3.5m
17
+ ---
18
+
19
+ # Bina 0.2 Rizeh
20
+
21
+ Bina 0.2 Rizeh is a compact Persian printed and handwriting recognizer fine-tuned from the
22
+ `PP-OCRv6_medium_rec` architecture. It uses PaddleOCR's CTC recognition head and ships both the
23
+ exported inference model and the resumable training checkpoint from the Stage-8 release.
24
+
25
+ ## Important model boundary
26
+
27
+ The neural checkpoint recognizes **text-line or text-region crops**. It is not, by itself, a
28
+ standalone full-page detector. The full-page benchmark below used the included adaptive75
29
+ **two-layer pipeline**: page-region detection and template isolation, CTC recognition of the
30
+ remaining regions, then reading-order assembly. The scripts used for that exact run are preserved
31
+ under `pipeline/` and the benchmark records are under `benchmark/`.
32
+
33
+ ## Persian OCR Triple Threat results
34
+
35
+ All 13,338 frozen benchmark rows were scored, with zero missing predictions. Lower is better.
36
+ Triple Threat is `20% WER + 20% CER + 60% S3`; Overall is a 50/50 macro-average of the printed and
37
+ handwriting splits.
38
+
39
+ | Split | Pages | WER | CER | S3 | Essential error | Triple Threat |
40
+ |---|---:|---:|---:|---:|---:|---:|
41
+ | Handwriting | 6,669 | 19.0833 | 13.6399 | 13.7015 | 14.7567 | 14.7656 |
42
+ | Printed | 6,669 | 8.9257 | 2.3535 | 9.8512 | 12.0774 | 8.1666 |
43
+ | Equal-split overall | 13,338 | 14.0045 | 7.9967 | 11.7764 | 13.4171 | **11.4661** |
44
+
45
+ Leaderboard: [Persian OCR Triple Threat](https://huggingface.co/spaces/Reza2kn/PersianOCR-TripleThreat)
46
+
47
+ The release did **not** reach the single-digit handwriting gate. Printed Triple Threat is already
48
+ single digit; handwriting remains the principal limitation.
49
+
50
+ ## Training snapshot
51
+
52
+ - Stage-8 training mix: 2,198,483 recognition crops.
53
+ - Mix composition: 1,000,000 clean handwriting, 1,000,000 printed, and 198,483 native/form crops.
54
+ - Best handwriting validation normalized edit distance: 0.9671727.
55
+ - Best printed validation normalized edit distance: 0.9071054.
56
+ - Optimizer state was preserved from Stage 4.
57
+ - The planned Stage-8 schedule completed without an overfitting stop signal.
58
+
59
+ The source pages came from
60
+ [`Reza2kn/persian-handwriting-pages-3.69m`](https://huggingface.co/datasets/Reza2kn/persian-handwriting-pages-3.69m)
61
+ and
62
+ [`Reza2kn/persian-printed-ocr-3.5m`](https://huggingface.co/datasets/Reza2kn/persian-printed-ocr-3.5m),
63
+ with line/region preparation and native form replay described by `data/MIX_MANIFEST.json`.
64
+
65
+ ## Inference artifacts
66
+
67
+ The exported Paddle model is available both at repository root and under `inference/`:
68
+
69
+ - `inference.json`
70
+ - `inference.pdiparams`
71
+ - `inference.yml`
72
+
73
+ With PaddleOCR 3.x, a recognition crop can be run with the custom model directory:
74
+
75
+ ```python
76
+ from paddleocr import TextRecognition
77
+
78
+ recognizer = TextRecognition(model_dir="./inference")
79
+ for result in recognizer.predict(input="persian-line.png", batch_size=1):
80
+ result.print()
81
+ ```
82
+
83
+ For page OCR, use a detector/layout stage first and pass its ordered text crops to this recognizer.
84
+ The bundled `pipeline/` scripts record the exact full-page benchmark workflow; their original
85
+ absolute cluster paths must be adapted to a new machine.
86
+
87
+ ## Repository layout
88
+
89
+ - `inference/`: exported Paddle inference graph, parameters, and configuration.
90
+ - `checkpoint/`: resumable best checkpoint, optimizer state, configs, and eval-gate state.
91
+ - `pipeline/`: detection, recognition, assembly, benchmark, and launch scripts.
92
+ - `benchmark/`: exact split metrics, logs, and run summaries.
93
+ - `data/MIX_MANIFEST.json`: exact Stage-8 train/eval composition.
94
+ - `RELEASE.json`: immutable release identity and headline metrics.
95
+ - `SHA256SUMS`: checksums for the verified packaged release.
96
+
97
+ ## Release identity
98
+
99
+ This public model name is **Bina 0.2 Rizeh**. The immutable packaging metadata retains the internal
100
+ release name `Bina-0.2-RizehPizeh` and release ID `stage8-gate42-20260726` so it remains traceable to
101
+ the exact trained artifact.
RELEASE.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model": "Reza2kn/Bina-0.2-RizehPizeh",
3
+ "release": "stage8-gate42-20260726",
4
+ "training": {
5
+ "checkpoint": "best_accuracy",
6
+ "handwriting_norm_edit_dis": 0.9671727329321572,
7
+ "printed_norm_edit_dis": 0.9071053593763533,
8
+ "optimizer_state_preserved_from_stage4": true,
9
+ "stage8_mix_rows": 2198483
10
+ },
11
+ "exact_benchmark": {
12
+ "dataset_rows_per_split": 6669,
13
+ "missing_predictions": 0,
14
+ "handwriting": {"wer": 19.0833, "cer": 13.6399, "s3": 13.7015},
15
+ "printed": {"wer": 8.9257, "cer": 2.3535, "s3": 9.8512},
16
+ "single_digit_gate_passed": false
17
+ },
18
+ "inference": {
19
+ "template_isolation_variant": "adaptive75",
20
+ "assembly_variant": "simple_spread_right"
21
+ }
22
+ }
SHA256SUMS ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ eaffb00430c5784e8c5275956e2f8d6710c3317fa980fcb5655d6160278d21a8 ./RELEASE.json
2
+ 77026e2d46fc243ceda286f3e14eec06ec48fcc598b689f4ebe0f1b5706b654e ./benchmark/decoder-summary.json
3
+ 63704185b5b4c765757abdca93b08faa22b62de0a665df4d57fa3625e962b4b2 ./benchmark/detection-summary.json
4
+ 239ad0244d66c76d2f0c62de05268cc58dd598598b7b8101144f3946fc6199ad ./benchmark/exact-summary.json
5
+ 73e6f96c1b255583206e320486ff9d03bb61f53a5e11948d0dacc6115eb17aae ./benchmark/pipeline-status.log
6
+ 53c1cac2a504a8566694db149d500d20d262c679fac0113f182e830b427cf4e2 ./benchmark/printed-summary.json
7
+ 2a6852e950e7f7f6e39c78c60c043bf4bbdeb3094f4eaef6d43d04b4ba6e2140 ./benchmark/recognition-summary.json
8
+ eae2631f9df501e41ebde1fb32a7c8a35c1409f76b06047cc9e13e5eed9ff82e ./benchmark/results.json
9
+ 54028b92c62fa7ae135746d9ad99a9470e844d74701e8c1b88191406bd62fae3 ./checkpoint/best_accuracy.pdopt
10
+ e9d8d6030144630173692297ba3f396427f9030099c5d9b0418644825e095053 ./checkpoint/best_accuracy.pdparams
11
+ 44f34d043a1996223e8d3f2aee2e42d02c395c6420ba527fba6cb924cd53eecd ./checkpoint/best_accuracy.states
12
+ 00190e883e668d351a5b7b85baa7cdafb3958306f0cd87ad7f5af7f6ec1e8403 ./checkpoint/config.yml
13
+ a5be28814a72aad57d2bfd0ee5550af7f4b332299dfb12a04a77c6e1d69a549e ./checkpoint/eval-gate-state.json
14
+ 00190e883e668d351a5b7b85baa7cdafb3958306f0cd87ad7f5af7f6ec1e8403 ./checkpoint/launch-config.yml
15
+ bf883ce1aee418b4df14bca9a8994e51de86493efcd6e1c06719ed1965599270 ./checkpoint/optimizer-patch.json
16
+ 16891a9615980307dc297d96841569874dcf76af6f44f9ffdaed8018a1dbe296 ./checkpoint/status.log
17
+ 5a31f6aef7744b964223d557167490ccb1a73345dc842de38406cf5b77e90484 ./data/MIX_MANIFEST.json
18
+ 3dfa3fb239f56c778a1e9b33eb31328349edd155a95806dac4378904e88527e5 ./inference/INFERENCE_READY
19
+ 0987ab8a740d125c48e0338e1c52114e6bfce927c4d9c25f628b8dc96f5e3e4c ./inference/export.log
20
+ b802ea6f182ffb341716b2f649b8f48a2c1f3e4fb6db26f4cfd31771d80cd8b8 ./inference/inference.json
21
+ f8e1b5b4e9ef11b46c521fed7d1c9b7e7b2d08c4c879aa01353acd7308a6e9fb ./inference/inference.pdiparams
22
+ 650bfd52635c3a5680479df99a4c5cb3b94e2875b25a7bc3aea45ec1bad6a841 ./inference/inference.yml
23
+ 512c099cbe1d748626a4f77c82ab0160570f9c9da19cb6e6e232eed6f4bad1e9 ./pipeline/assemble_bina02_two_layer.py
24
+ 9cfaa96f48e9ac6ae8aea47d1288101b3ffe83ba30801f6f0c663476925794d6 ./pipeline/benchmark_bina02_printed_only.py
25
+ bdb313a37a02d6af56a06149db1cdb4a5f11306a3d958c88b0983b20fa2c7b4c ./pipeline/bina02_template_layers.py
26
+ ff66338c6292e9b2224b9d45c165c0245efc3802720d464ca22367786570a6bf ./pipeline/decode_bina02_persian.py
27
+ 70a16a7f616f3dd9bf3b90552415157e85b0dbba4fba58ebae49bca832015b10 ./pipeline/detect_bina02_two_layer.py
28
+ 3e72edef7d79a74523b9a815b5f39bd93d7b2f54362b02df4bf306d075ba2132 ./pipeline/recognize_bina02_two_layer.py
29
+ 067cc0d2b6df7cd07271415f76e5b87184a467a0033c89f8146a393d89870cc6 ./pipeline/run_bina02_stage8_optimizer_resume_v2.sh
30
+ a5059985086f90c5857d067f1d1ed9ac5a693ccff13d9750f7e34c85b16a1ec3 ./pipeline/run_bina02_stage8_v2_exact.sh
31
+ 9cc3ce9a0a29211571607f53f7f4fd2fe0662e949f7c4342d0b06c4e83433d7f ./pipeline/run_bina02_stage8_v2_exact_core.sh
32
+ 8c497cf5d3744eb4611c6bf855ac92786100bfabb31b8923b4e08ae0f73fad7a ./pipeline/watch_bina02_stage4_eval.py
benchmark/decoder-summary.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "rows": 13338,
3
+ "changed_rows": 8179,
4
+ "changed_tokens": 54178,
5
+ "lexicon_words": 43210,
6
+ "delete_keys": 193439
7
+ }
benchmark/detection-summary.json ADDED
@@ -0,0 +1,6680 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "state": "complete",
3
+ "variant": "adaptive75",
4
+ "selected_indices": [
5
+ 0,
6
+ 1,
7
+ 2,
8
+ 3,
9
+ 4,
10
+ 5,
11
+ 6,
12
+ 7,
13
+ 8,
14
+ 9,
15
+ 10,
16
+ 11,
17
+ 12,
18
+ 13,
19
+ 14,
20
+ 15,
21
+ 16,
22
+ 17,
23
+ 18,
24
+ 19,
25
+ 20,
26
+ 21,
27
+ 22,
28
+ 23,
29
+ 24,
30
+ 25,
31
+ 26,
32
+ 27,
33
+ 28,
34
+ 29,
35
+ 30,
36
+ 31,
37
+ 32,
38
+ 33,
39
+ 34,
40
+ 35,
41
+ 36,
42
+ 37,
43
+ 38,
44
+ 39,
45
+ 40,
46
+ 41,
47
+ 42,
48
+ 43,
49
+ 44,
50
+ 45,
51
+ 46,
52
+ 47,
53
+ 48,
54
+ 49,
55
+ 50,
56
+ 51,
57
+ 52,
58
+ 53,
59
+ 54,
60
+ 55,
61
+ 56,
62
+ 57,
63
+ 58,
64
+ 59,
65
+ 60,
66
+ 61,
67
+ 62,
68
+ 63,
69
+ 64,
70
+ 65,
71
+ 66,
72
+ 67,
73
+ 68,
74
+ 69,
75
+ 70,
76
+ 71,
77
+ 72,
78
+ 73,
79
+ 74,
80
+ 75,
81
+ 76,
82
+ 77,
83
+ 78,
84
+ 79,
85
+ 80,
86
+ 81,
87
+ 82,
88
+ 83,
89
+ 84,
90
+ 85,
91
+ 86,
92
+ 87,
93
+ 88,
94
+ 89,
95
+ 90,
96
+ 91,
97
+ 92,
98
+ 93,
99
+ 94,
100
+ 95,
101
+ 96,
102
+ 97,
103
+ 98,
104
+ 99,
105
+ 100,
106
+ 101,
107
+ 102,
108
+ 103,
109
+ 104,
110
+ 105,
111
+ 106,
112
+ 107,
113
+ 108,
114
+ 109,
115
+ 110,
116
+ 111,
117
+ 112,
118
+ 113,
119
+ 114,
120
+ 115,
121
+ 116,
122
+ 117,
123
+ 118,
124
+ 119,
125
+ 120,
126
+ 121,
127
+ 122,
128
+ 123,
129
+ 124,
130
+ 125,
131
+ 126,
132
+ 127,
133
+ 128,
134
+ 129,
135
+ 130,
136
+ 131,
137
+ 132,
138
+ 133,
139
+ 134,
140
+ 135,
141
+ 136,
142
+ 137,
143
+ 138,
144
+ 139,
145
+ 140,
146
+ 141,
147
+ 142,
148
+ 143,
149
+ 144,
150
+ 145,
151
+ 146,
152
+ 147,
153
+ 148,
154
+ 149,
155
+ 150,
156
+ 151,
157
+ 152,
158
+ 153,
159
+ 154,
160
+ 155,
161
+ 156,
162
+ 157,
163
+ 158,
164
+ 159,
165
+ 160,
166
+ 161,
167
+ 162,
168
+ 163,
169
+ 164,
170
+ 165,
171
+ 166,
172
+ 167,
173
+ 168,
174
+ 169,
175
+ 170,
176
+ 171,
177
+ 172,
178
+ 173,
179
+ 174,
180
+ 175,
181
+ 176,
182
+ 177,
183
+ 178,
184
+ 179,
185
+ 180,
186
+ 181,
187
+ 182,
188
+ 183,
189
+ 184,
190
+ 185,
191
+ 186,
192
+ 187,
193
+ 188,
194
+ 189,
195
+ 190,
196
+ 191,
197
+ 192,
198
+ 193,
199
+ 194,
200
+ 195,
201
+ 196,
202
+ 197,
203
+ 198,
204
+ 199,
205
+ 200,
206
+ 201,
207
+ 202,
208
+ 203,
209
+ 204,
210
+ 205,
211
+ 206,
212
+ 207,
213
+ 208,
214
+ 209,
215
+ 210,
216
+ 211,
217
+ 212,
218
+ 213,
219
+ 214,
220
+ 215,
221
+ 216,
222
+ 217,
223
+ 218,
224
+ 219,
225
+ 220,
226
+ 221,
227
+ 222,
228
+ 223,
229
+ 224,
230
+ 225,
231
+ 226,
232
+ 227,
233
+ 228,
234
+ 229,
235
+ 230,
236
+ 231,
237
+ 232,
238
+ 233,
239
+ 234,
240
+ 235,
241
+ 236,
242
+ 237,
243
+ 238,
244
+ 239,
245
+ 240,
246
+ 241,
247
+ 242,
248
+ 243,
249
+ 244,
250
+ 245,
251
+ 246,
252
+ 247,
253
+ 248,
254
+ 249,
255
+ 250,
256
+ 251,
257
+ 252,
258
+ 253,
259
+ 254,
260
+ 255,
261
+ 256,
262
+ 257,
263
+ 258,
264
+ 259,
265
+ 260,
266
+ 261,
267
+ 262,
268
+ 263,
269
+ 264,
270
+ 265,
271
+ 266,
272
+ 267,
273
+ 268,
274
+ 269,
275
+ 270,
276
+ 271,
277
+ 272,
278
+ 273,
279
+ 274,
280
+ 275,
281
+ 276,
282
+ 277,
283
+ 278,
284
+ 279,
285
+ 280,
286
+ 281,
287
+ 282,
288
+ 283,
289
+ 284,
290
+ 285,
291
+ 286,
292
+ 287,
293
+ 288,
294
+ 289,
295
+ 290,
296
+ 291,
297
+ 292,
298
+ 293,
299
+ 294,
300
+ 295,
301
+ 296,
302
+ 297,
303
+ 298,
304
+ 299,
305
+ 300,
306
+ 301,
307
+ 302,
308
+ 303,
309
+ 304,
310
+ 305,
311
+ 306,
312
+ 307,
313
+ 308,
314
+ 309,
315
+ 310,
316
+ 311,
317
+ 312,
318
+ 313,
319
+ 314,
320
+ 315,
321
+ 316,
322
+ 317,
323
+ 318,
324
+ 319,
325
+ 320,
326
+ 321,
327
+ 322,
328
+ 323,
329
+ 324,
330
+ 325,
331
+ 326,
332
+ 327,
333
+ 328,
334
+ 329,
335
+ 330,
336
+ 331,
337
+ 332,
338
+ 333,
339
+ 334,
340
+ 335,
341
+ 336,
342
+ 337,
343
+ 338,
344
+ 339,
345
+ 340,
346
+ 341,
347
+ 342,
348
+ 343,
349
+ 344,
350
+ 345,
351
+ 346,
352
+ 347,
353
+ 348,
354
+ 349,
355
+ 350,
356
+ 351,
357
+ 352,
358
+ 353,
359
+ 354,
360
+ 355,
361
+ 356,
362
+ 357,
363
+ 358,
364
+ 359,
365
+ 360,
366
+ 361,
367
+ 362,
368
+ 363,
369
+ 364,
370
+ 365,
371
+ 366,
372
+ 367,
373
+ 368,
374
+ 369,
375
+ 370,
376
+ 371,
377
+ 372,
378
+ 373,
379
+ 374,
380
+ 375,
381
+ 376,
382
+ 377,
383
+ 378,
384
+ 379,
385
+ 380,
386
+ 381,
387
+ 382,
388
+ 383,
389
+ 384,
390
+ 385,
391
+ 386,
392
+ 387,
393
+ 388,
394
+ 389,
395
+ 390,
396
+ 391,
397
+ 392,
398
+ 393,
399
+ 394,
400
+ 395,
401
+ 396,
402
+ 397,
403
+ 398,
404
+ 399,
405
+ 400,
406
+ 401,
407
+ 402,
408
+ 403,
409
+ 404,
410
+ 405,
411
+ 406,
412
+ 407,
413
+ 408,
414
+ 409,
415
+ 410,
416
+ 411,
417
+ 412,
418
+ 413,
419
+ 414,
420
+ 415,
421
+ 416,
422
+ 417,
423
+ 418,
424
+ 419,
425
+ 420,
426
+ 421,
427
+ 422,
428
+ 423,
429
+ 424,
430
+ 425,
431
+ 426,
432
+ 427,
433
+ 428,
434
+ 429,
435
+ 430,
436
+ 431,
437
+ 432,
438
+ 433,
439
+ 434,
440
+ 435,
441
+ 436,
442
+ 437,
443
+ 438,
444
+ 439,
445
+ 440,
446
+ 441,
447
+ 442,
448
+ 443,
449
+ 444,
450
+ 445,
451
+ 446,
452
+ 447,
453
+ 448,
454
+ 449,
455
+ 450,
456
+ 451,
457
+ 452,
458
+ 453,
459
+ 454,
460
+ 455,
461
+ 456,
462
+ 457,
463
+ 458,
464
+ 459,
465
+ 460,
466
+ 461,
467
+ 462,
468
+ 463,
469
+ 464,
470
+ 465,
471
+ 466,
472
+ 467,
473
+ 468,
474
+ 469,
475
+ 470,
476
+ 471,
477
+ 472,
478
+ 473,
479
+ 474,
480
+ 475,
481
+ 476,
482
+ 477,
483
+ 478,
484
+ 479,
485
+ 480,
486
+ 481,
487
+ 482,
488
+ 483,
489
+ 484,
490
+ 485,
491
+ 486,
492
+ 487,
493
+ 488,
494
+ 489,
495
+ 490,
496
+ 491,
497
+ 492,
498
+ 493,
499
+ 494,
500
+ 495,
501
+ 496,
502
+ 497,
503
+ 498,
504
+ 499,
505
+ 500,
506
+ 501,
507
+ 502,
508
+ 503,
509
+ 504,
510
+ 505,
511
+ 506,
512
+ 507,
513
+ 508,
514
+ 509,
515
+ 510,
516
+ 511,
517
+ 512,
518
+ 513,
519
+ 514,
520
+ 515,
521
+ 516,
522
+ 517,
523
+ 518,
524
+ 519,
525
+ 520,
526
+ 521,
527
+ 522,
528
+ 523,
529
+ 524,
530
+ 525,
531
+ 526,
532
+ 527,
533
+ 528,
534
+ 529,
535
+ 530,
536
+ 531,
537
+ 532,
538
+ 533,
539
+ 534,
540
+ 535,
541
+ 536,
542
+ 537,
543
+ 538,
544
+ 539,
545
+ 540,
546
+ 541,
547
+ 542,
548
+ 543,
549
+ 544,
550
+ 545,
551
+ 546,
552
+ 547,
553
+ 548,
554
+ 549,
555
+ 550,
556
+ 551,
557
+ 552,
558
+ 553,
559
+ 554,
560
+ 555,
561
+ 556,
562
+ 557,
563
+ 558,
564
+ 559,
565
+ 560,
566
+ 561,
567
+ 562,
568
+ 563,
569
+ 564,
570
+ 565,
571
+ 566,
572
+ 567,
573
+ 568,
574
+ 569,
575
+ 570,
576
+ 571,
577
+ 572,
578
+ 573,
579
+ 574,
580
+ 575,
581
+ 576,
582
+ 577,
583
+ 578,
584
+ 579,
585
+ 580,
586
+ 581,
587
+ 582,
588
+ 583,
589
+ 584,
590
+ 585,
591
+ 586,
592
+ 587,
593
+ 588,
594
+ 589,
595
+ 590,
596
+ 591,
597
+ 592,
598
+ 593,
599
+ 594,
600
+ 595,
601
+ 596,
602
+ 597,
603
+ 598,
604
+ 599,
605
+ 600,
606
+ 601,
607
+ 602,
608
+ 603,
609
+ 604,
610
+ 605,
611
+ 606,
612
+ 607,
613
+ 608,
614
+ 609,
615
+ 610,
616
+ 611,
617
+ 612,
618
+ 613,
619
+ 614,
620
+ 615,
621
+ 616,
622
+ 617,
623
+ 618,
624
+ 619,
625
+ 620,
626
+ 621,
627
+ 622,
628
+ 623,
629
+ 624,
630
+ 625,
631
+ 626,
632
+ 627,
633
+ 628,
634
+ 629,
635
+ 630,
636
+ 631,
637
+ 632,
638
+ 633,
639
+ 634,
640
+ 635,
641
+ 636,
642
+ 637,
643
+ 638,
644
+ 639,
645
+ 640,
646
+ 641,
647
+ 642,
648
+ 643,
649
+ 644,
650
+ 645,
651
+ 646,
652
+ 647,
653
+ 648,
654
+ 649,
655
+ 650,
656
+ 651,
657
+ 652,
658
+ 653,
659
+ 654,
660
+ 655,
661
+ 656,
662
+ 657,
663
+ 658,
664
+ 659,
665
+ 660,
666
+ 661,
667
+ 662,
668
+ 663,
669
+ 664,
670
+ 665,
671
+ 666,
672
+ 667,
673
+ 668,
674
+ 669,
675
+ 670,
676
+ 671,
677
+ 672,
678
+ 673,
679
+ 674,
680
+ 675,
681
+ 676,
682
+ 677,
683
+ 678,
684
+ 679,
685
+ 680,
686
+ 681,
687
+ 682,
688
+ 683,
689
+ 684,
690
+ 685,
691
+ 686,
692
+ 687,
693
+ 688,
694
+ 689,
695
+ 690,
696
+ 691,
697
+ 692,
698
+ 693,
699
+ 694,
700
+ 695,
701
+ 696,
702
+ 697,
703
+ 698,
704
+ 699,
705
+ 700,
706
+ 701,
707
+ 702,
708
+ 703,
709
+ 704,
710
+ 705,
711
+ 706,
712
+ 707,
713
+ 708,
714
+ 709,
715
+ 710,
716
+ 711,
717
+ 712,
718
+ 713,
719
+ 714,
720
+ 715,
721
+ 716,
722
+ 717,
723
+ 718,
724
+ 719,
725
+ 720,
726
+ 721,
727
+ 722,
728
+ 723,
729
+ 724,
730
+ 725,
731
+ 726,
732
+ 727,
733
+ 728,
734
+ 729,
735
+ 730,
736
+ 731,
737
+ 732,
738
+ 733,
739
+ 734,
740
+ 735,
741
+ 736,
742
+ 737,
743
+ 738,
744
+ 739,
745
+ 740,
746
+ 741,
747
+ 742,
748
+ 743,
749
+ 744,
750
+ 745,
751
+ 746,
752
+ 747,
753
+ 748,
754
+ 749,
755
+ 750,
756
+ 751,
757
+ 752,
758
+ 753,
759
+ 754,
760
+ 755,
761
+ 756,
762
+ 757,
763
+ 758,
764
+ 759,
765
+ 760,
766
+ 761,
767
+ 762,
768
+ 763,
769
+ 764,
770
+ 765,
771
+ 766,
772
+ 767,
773
+ 768,
774
+ 769,
775
+ 770,
776
+ 771,
777
+ 772,
778
+ 773,
779
+ 774,
780
+ 775,
781
+ 776,
782
+ 777,
783
+ 778,
784
+ 779,
785
+ 780,
786
+ 781,
787
+ 782,
788
+ 783,
789
+ 784,
790
+ 785,
791
+ 786,
792
+ 787,
793
+ 788,
794
+ 789,
795
+ 790,
796
+ 791,
797
+ 792,
798
+ 793,
799
+ 794,
800
+ 795,
801
+ 796,
802
+ 797,
803
+ 798,
804
+ 799,
805
+ 800,
806
+ 801,
807
+ 802,
808
+ 803,
809
+ 804,
810
+ 805,
811
+ 806,
812
+ 807,
813
+ 808,
814
+ 809,
815
+ 810,
816
+ 811,
817
+ 812,
818
+ 813,
819
+ 814,
820
+ 815,
821
+ 816,
822
+ 817,
823
+ 818,
824
+ 819,
825
+ 820,
826
+ 821,
827
+ 822,
828
+ 823,
829
+ 824,
830
+ 825,
831
+ 826,
832
+ 827,
833
+ 828,
834
+ 829,
835
+ 830,
836
+ 831,
837
+ 832,
838
+ 833,
839
+ 834,
840
+ 835,
841
+ 836,
842
+ 837,
843
+ 838,
844
+ 839,
845
+ 840,
846
+ 841,
847
+ 842,
848
+ 843,
849
+ 844,
850
+ 845,
851
+ 846,
852
+ 847,
853
+ 848,
854
+ 849,
855
+ 850,
856
+ 851,
857
+ 852,
858
+ 853,
859
+ 854,
860
+ 855,
861
+ 856,
862
+ 857,
863
+ 858,
864
+ 859,
865
+ 860,
866
+ 861,
867
+ 862,
868
+ 863,
869
+ 864,
870
+ 865,
871
+ 866,
872
+ 867,
873
+ 868,
874
+ 869,
875
+ 870,
876
+ 871,
877
+ 872,
878
+ 873,
879
+ 874,
880
+ 875,
881
+ 876,
882
+ 877,
883
+ 878,
884
+ 879,
885
+ 880,
886
+ 881,
887
+ 882,
888
+ 883,
889
+ 884,
890
+ 885,
891
+ 886,
892
+ 887,
893
+ 888,
894
+ 889,
895
+ 890,
896
+ 891,
897
+ 892,
898
+ 893,
899
+ 894,
900
+ 895,
901
+ 896,
902
+ 897,
903
+ 898,
904
+ 899,
905
+ 900,
906
+ 901,
907
+ 902,
908
+ 903,
909
+ 904,
910
+ 905,
911
+ 906,
912
+ 907,
913
+ 908,
914
+ 909,
915
+ 910,
916
+ 911,
917
+ 912,
918
+ 913,
919
+ 914,
920
+ 915,
921
+ 916,
922
+ 917,
923
+ 918,
924
+ 919,
925
+ 920,
926
+ 921,
927
+ 922,
928
+ 923,
929
+ 924,
930
+ 925,
931
+ 926,
932
+ 927,
933
+ 928,
934
+ 929,
935
+ 930,
936
+ 931,
937
+ 932,
938
+ 933,
939
+ 934,
940
+ 935,
941
+ 936,
942
+ 937,
943
+ 938,
944
+ 939,
945
+ 940,
946
+ 941,
947
+ 942,
948
+ 943,
949
+ 944,
950
+ 945,
951
+ 946,
952
+ 947,
953
+ 948,
954
+ 949,
955
+ 950,
956
+ 951,
957
+ 952,
958
+ 953,
959
+ 954,
960
+ 955,
961
+ 956,
962
+ 957,
963
+ 958,
964
+ 959,
965
+ 960,
966
+ 961,
967
+ 962,
968
+ 963,
969
+ 964,
970
+ 965,
971
+ 966,
972
+ 967,
973
+ 968,
974
+ 969,
975
+ 970,
976
+ 971,
977
+ 972,
978
+ 973,
979
+ 974,
980
+ 975,
981
+ 976,
982
+ 977,
983
+ 978,
984
+ 979,
985
+ 980,
986
+ 981,
987
+ 982,
988
+ 983,
989
+ 984,
990
+ 985,
991
+ 986,
992
+ 987,
993
+ 988,
994
+ 989,
995
+ 990,
996
+ 991,
997
+ 992,
998
+ 993,
999
+ 994,
1000
+ 995,
1001
+ 996,
1002
+ 997,
1003
+ 998,
1004
+ 999,
1005
+ 1000,
1006
+ 1001,
1007
+ 1002,
1008
+ 1003,
1009
+ 1004,
1010
+ 1005,
1011
+ 1006,
1012
+ 1007,
1013
+ 1008,
1014
+ 1009,
1015
+ 1010,
1016
+ 1011,
1017
+ 1012,
1018
+ 1013,
1019
+ 1014,
1020
+ 1015,
1021
+ 1016,
1022
+ 1017,
1023
+ 1018,
1024
+ 1019,
1025
+ 1020,
1026
+ 1021,
1027
+ 1022,
1028
+ 1023,
1029
+ 1024,
1030
+ 1025,
1031
+ 1026,
1032
+ 1027,
1033
+ 1028,
1034
+ 1029,
1035
+ 1030,
1036
+ 1031,
1037
+ 1032,
1038
+ 1033,
1039
+ 1034,
1040
+ 1035,
1041
+ 1036,
1042
+ 1037,
1043
+ 1038,
1044
+ 1039,
1045
+ 1040,
1046
+ 1041,
1047
+ 1042,
1048
+ 1043,
1049
+ 1044,
1050
+ 1045,
1051
+ 1046,
1052
+ 1047,
1053
+ 1048,
1054
+ 1049,
1055
+ 1050,
1056
+ 1051,
1057
+ 1052,
1058
+ 1053,
1059
+ 1054,
1060
+ 1055,
1061
+ 1056,
1062
+ 1057,
1063
+ 1058,
1064
+ 1059,
1065
+ 1060,
1066
+ 1061,
1067
+ 1062,
1068
+ 1063,
1069
+ 1064,
1070
+ 1065,
1071
+ 1066,
1072
+ 1067,
1073
+ 1068,
1074
+ 1069,
1075
+ 1070,
1076
+ 1071,
1077
+ 1072,
1078
+ 1073,
1079
+ 1074,
1080
+ 1075,
1081
+ 1076,
1082
+ 1077,
1083
+ 1078,
1084
+ 1079,
1085
+ 1080,
1086
+ 1081,
1087
+ 1082,
1088
+ 1083,
1089
+ 1084,
1090
+ 1085,
1091
+ 1086,
1092
+ 1087,
1093
+ 1088,
1094
+ 1089,
1095
+ 1090,
1096
+ 1091,
1097
+ 1092,
1098
+ 1093,
1099
+ 1094,
1100
+ 1095,
1101
+ 1096,
1102
+ 1097,
1103
+ 1098,
1104
+ 1099,
1105
+ 1100,
1106
+ 1101,
1107
+ 1102,
1108
+ 1103,
1109
+ 1104,
1110
+ 1105,
1111
+ 1106,
1112
+ 1107,
1113
+ 1108,
1114
+ 1109,
1115
+ 1110,
1116
+ 1111,
1117
+ 1112,
1118
+ 1113,
1119
+ 1114,
1120
+ 1115,
1121
+ 1116,
1122
+ 1117,
1123
+ 1118,
1124
+ 1119,
1125
+ 1120,
1126
+ 1121,
1127
+ 1122,
1128
+ 1123,
1129
+ 1124,
1130
+ 1125,
1131
+ 1126,
1132
+ 1127,
1133
+ 1128,
1134
+ 1129,
1135
+ 1130,
1136
+ 1131,
1137
+ 1132,
1138
+ 1133,
1139
+ 1134,
1140
+ 1135,
1141
+ 1136,
1142
+ 1137,
1143
+ 1138,
1144
+ 1139,
1145
+ 1140,
1146
+ 1141,
1147
+ 1142,
1148
+ 1143,
1149
+ 1144,
1150
+ 1145,
1151
+ 1146,
1152
+ 1147,
1153
+ 1148,
1154
+ 1149,
1155
+ 1150,
1156
+ 1151,
1157
+ 1152,
1158
+ 1153,
1159
+ 1154,
1160
+ 1155,
1161
+ 1156,
1162
+ 1157,
1163
+ 1158,
1164
+ 1159,
1165
+ 1160,
1166
+ 1161,
1167
+ 1162,
1168
+ 1163,
1169
+ 1164,
1170
+ 1165,
1171
+ 1166,
1172
+ 1167,
1173
+ 1168,
1174
+ 1169,
1175
+ 1170,
1176
+ 1171,
1177
+ 1172,
1178
+ 1173,
1179
+ 1174,
1180
+ 1175,
1181
+ 1176,
1182
+ 1177,
1183
+ 1178,
1184
+ 1179,
1185
+ 1180,
1186
+ 1181,
1187
+ 1182,
1188
+ 1183,
1189
+ 1184,
1190
+ 1185,
1191
+ 1186,
1192
+ 1187,
1193
+ 1188,
1194
+ 1189,
1195
+ 1190,
1196
+ 1191,
1197
+ 1192,
1198
+ 1193,
1199
+ 1194,
1200
+ 1195,
1201
+ 1196,
1202
+ 1197,
1203
+ 1198,
1204
+ 1199,
1205
+ 1200,
1206
+ 1201,
1207
+ 1202,
1208
+ 1203,
1209
+ 1204,
1210
+ 1205,
1211
+ 1206,
1212
+ 1207,
1213
+ 1208,
1214
+ 1209,
1215
+ 1210,
1216
+ 1211,
1217
+ 1212,
1218
+ 1213,
1219
+ 1214,
1220
+ 1215,
1221
+ 1216,
1222
+ 1217,
1223
+ 1218,
1224
+ 1219,
1225
+ 1220,
1226
+ 1221,
1227
+ 1222,
1228
+ 1223,
1229
+ 1224,
1230
+ 1225,
1231
+ 1226,
1232
+ 1227,
1233
+ 1228,
1234
+ 1229,
1235
+ 1230,
1236
+ 1231,
1237
+ 1232,
1238
+ 1233,
1239
+ 1234,
1240
+ 1235,
1241
+ 1236,
1242
+ 1237,
1243
+ 1238,
1244
+ 1239,
1245
+ 1240,
1246
+ 1241,
1247
+ 1242,
1248
+ 1243,
1249
+ 1244,
1250
+ 1245,
1251
+ 1246,
1252
+ 1247,
1253
+ 1248,
1254
+ 1249,
1255
+ 1250,
1256
+ 1251,
1257
+ 1252,
1258
+ 1253,
1259
+ 1254,
1260
+ 1255,
1261
+ 1256,
1262
+ 1257,
1263
+ 1258,
1264
+ 1259,
1265
+ 1260,
1266
+ 1261,
1267
+ 1262,
1268
+ 1263,
1269
+ 1264,
1270
+ 1265,
1271
+ 1266,
1272
+ 1267,
1273
+ 1268,
1274
+ 1269,
1275
+ 1270,
1276
+ 1271,
1277
+ 1272,
1278
+ 1273,
1279
+ 1274,
1280
+ 1275,
1281
+ 1276,
1282
+ 1277,
1283
+ 1278,
1284
+ 1279,
1285
+ 1280,
1286
+ 1281,
1287
+ 1282,
1288
+ 1283,
1289
+ 1284,
1290
+ 1285,
1291
+ 1286,
1292
+ 1287,
1293
+ 1288,
1294
+ 1289,
1295
+ 1290,
1296
+ 1291,
1297
+ 1292,
1298
+ 1293,
1299
+ 1294,
1300
+ 1295,
1301
+ 1296,
1302
+ 1297,
1303
+ 1298,
1304
+ 1299,
1305
+ 1300,
1306
+ 1301,
1307
+ 1302,
1308
+ 1303,
1309
+ 1304,
1310
+ 1305,
1311
+ 1306,
1312
+ 1307,
1313
+ 1308,
1314
+ 1309,
1315
+ 1310,
1316
+ 1311,
1317
+ 1312,
1318
+ 1313,
1319
+ 1314,
1320
+ 1315,
1321
+ 1316,
1322
+ 1317,
1323
+ 1318,
1324
+ 1319,
1325
+ 1320,
1326
+ 1321,
1327
+ 1322,
1328
+ 1323,
1329
+ 1324,
1330
+ 1325,
1331
+ 1326,
1332
+ 1327,
1333
+ 1328,
1334
+ 1329,
1335
+ 1330,
1336
+ 1331,
1337
+ 1332,
1338
+ 1333,
1339
+ 1334,
1340
+ 1335,
1341
+ 1336,
1342
+ 1337,
1343
+ 1338,
1344
+ 1339,
1345
+ 1340,
1346
+ 1341,
1347
+ 1342,
1348
+ 1343,
1349
+ 1344,
1350
+ 1345,
1351
+ 1346,
1352
+ 1347,
1353
+ 1348,
1354
+ 1349,
1355
+ 1350,
1356
+ 1351,
1357
+ 1352,
1358
+ 1353,
1359
+ 1354,
1360
+ 1355,
1361
+ 1356,
1362
+ 1357,
1363
+ 1358,
1364
+ 1359,
1365
+ 1360,
1366
+ 1361,
1367
+ 1362,
1368
+ 1363,
1369
+ 1364,
1370
+ 1365,
1371
+ 1366,
1372
+ 1367,
1373
+ 1368,
1374
+ 1369,
1375
+ 1370,
1376
+ 1371,
1377
+ 1372,
1378
+ 1373,
1379
+ 1374,
1380
+ 1375,
1381
+ 1376,
1382
+ 1377,
1383
+ 1378,
1384
+ 1379,
1385
+ 1380,
1386
+ 1381,
1387
+ 1382,
1388
+ 1383,
1389
+ 1384,
1390
+ 1385,
1391
+ 1386,
1392
+ 1387,
1393
+ 1388,
1394
+ 1389,
1395
+ 1390,
1396
+ 1391,
1397
+ 1392,
1398
+ 1393,
1399
+ 1394,
1400
+ 1395,
1401
+ 1396,
1402
+ 1397,
1403
+ 1398,
1404
+ 1399,
1405
+ 1400,
1406
+ 1401,
1407
+ 1402,
1408
+ 1403,
1409
+ 1404,
1410
+ 1405,
1411
+ 1406,
1412
+ 1407,
1413
+ 1408,
1414
+ 1409,
1415
+ 1410,
1416
+ 1411,
1417
+ 1412,
1418
+ 1413,
1419
+ 1414,
1420
+ 1415,
1421
+ 1416,
1422
+ 1417,
1423
+ 1418,
1424
+ 1419,
1425
+ 1420,
1426
+ 1421,
1427
+ 1422,
1428
+ 1423,
1429
+ 1424,
1430
+ 1425,
1431
+ 1426,
1432
+ 1427,
1433
+ 1428,
1434
+ 1429,
1435
+ 1430,
1436
+ 1431,
1437
+ 1432,
1438
+ 1433,
1439
+ 1434,
1440
+ 1435,
1441
+ 1436,
1442
+ 1437,
1443
+ 1438,
1444
+ 1439,
1445
+ 1440,
1446
+ 1441,
1447
+ 1442,
1448
+ 1443,
1449
+ 1444,
1450
+ 1445,
1451
+ 1446,
1452
+ 1447,
1453
+ 1448,
1454
+ 1449,
1455
+ 1450,
1456
+ 1451,
1457
+ 1452,
1458
+ 1453,
1459
+ 1454,
1460
+ 1455,
1461
+ 1456,
1462
+ 1457,
1463
+ 1458,
1464
+ 1459,
1465
+ 1460,
1466
+ 1461,
1467
+ 1462,
1468
+ 1463,
1469
+ 1464,
1470
+ 1465,
1471
+ 1466,
1472
+ 1467,
1473
+ 1468,
1474
+ 1469,
1475
+ 1470,
1476
+ 1471,
1477
+ 1472,
1478
+ 1473,
1479
+ 1474,
1480
+ 1475,
1481
+ 1476,
1482
+ 1477,
1483
+ 1478,
1484
+ 1479,
1485
+ 1480,
1486
+ 1481,
1487
+ 1482,
1488
+ 1483,
1489
+ 1484,
1490
+ 1485,
1491
+ 1486,
1492
+ 1487,
1493
+ 1488,
1494
+ 1489,
1495
+ 1490,
1496
+ 1491,
1497
+ 1492,
1498
+ 1493,
1499
+ 1494,
1500
+ 1495,
1501
+ 1496,
1502
+ 1497,
1503
+ 1498,
1504
+ 1499,
1505
+ 1500,
1506
+ 1501,
1507
+ 1502,
1508
+ 1503,
1509
+ 1504,
1510
+ 1505,
1511
+ 1506,
1512
+ 1507,
1513
+ 1508,
1514
+ 1509,
1515
+ 1510,
1516
+ 1511,
1517
+ 1512,
1518
+ 1513,
1519
+ 1514,
1520
+ 1515,
1521
+ 1516,
1522
+ 1517,
1523
+ 1518,
1524
+ 1519,
1525
+ 1520,
1526
+ 1521,
1527
+ 1522,
1528
+ 1523,
1529
+ 1524,
1530
+ 1525,
1531
+ 1526,
1532
+ 1527,
1533
+ 1528,
1534
+ 1529,
1535
+ 1530,
1536
+ 1531,
1537
+ 1532,
1538
+ 1533,
1539
+ 1534,
1540
+ 1535,
1541
+ 1536,
1542
+ 1537,
1543
+ 1538,
1544
+ 1539,
1545
+ 1540,
1546
+ 1541,
1547
+ 1542,
1548
+ 1543,
1549
+ 1544,
1550
+ 1545,
1551
+ 1546,
1552
+ 1547,
1553
+ 1548,
1554
+ 1549,
1555
+ 1550,
1556
+ 1551,
1557
+ 1552,
1558
+ 1553,
1559
+ 1554,
1560
+ 1555,
1561
+ 1556,
1562
+ 1557,
1563
+ 1558,
1564
+ 1559,
1565
+ 1560,
1566
+ 1561,
1567
+ 1562,
1568
+ 1563,
1569
+ 1564,
1570
+ 1565,
1571
+ 1566,
1572
+ 1567,
1573
+ 1568,
1574
+ 1569,
1575
+ 1570,
1576
+ 1571,
1577
+ 1572,
1578
+ 1573,
1579
+ 1574,
1580
+ 1575,
1581
+ 1576,
1582
+ 1577,
1583
+ 1578,
1584
+ 1579,
1585
+ 1580,
1586
+ 1581,
1587
+ 1582,
1588
+ 1583,
1589
+ 1584,
1590
+ 1585,
1591
+ 1586,
1592
+ 1587,
1593
+ 1588,
1594
+ 1589,
1595
+ 1590,
1596
+ 1591,
1597
+ 1592,
1598
+ 1593,
1599
+ 1594,
1600
+ 1595,
1601
+ 1596,
1602
+ 1597,
1603
+ 1598,
1604
+ 1599,
1605
+ 1600,
1606
+ 1601,
1607
+ 1602,
1608
+ 1603,
1609
+ 1604,
1610
+ 1605,
1611
+ 1606,
1612
+ 1607,
1613
+ 1608,
1614
+ 1609,
1615
+ 1610,
1616
+ 1611,
1617
+ 1612,
1618
+ 1613,
1619
+ 1614,
1620
+ 1615,
1621
+ 1616,
1622
+ 1617,
1623
+ 1618,
1624
+ 1619,
1625
+ 1620,
1626
+ 1621,
1627
+ 1622,
1628
+ 1623,
1629
+ 1624,
1630
+ 1625,
1631
+ 1626,
1632
+ 1627,
1633
+ 1628,
1634
+ 1629,
1635
+ 1630,
1636
+ 1631,
1637
+ 1632,
1638
+ 1633,
1639
+ 1634,
1640
+ 1635,
1641
+ 1636,
1642
+ 1637,
1643
+ 1638,
1644
+ 1639,
1645
+ 1640,
1646
+ 1641,
1647
+ 1642,
1648
+ 1643,
1649
+ 1644,
1650
+ 1645,
1651
+ 1646,
1652
+ 1647,
1653
+ 1648,
1654
+ 1649,
1655
+ 1650,
1656
+ 1651,
1657
+ 1652,
1658
+ 1653,
1659
+ 1654,
1660
+ 1655,
1661
+ 1656,
1662
+ 1657,
1663
+ 1658,
1664
+ 1659,
1665
+ 1660,
1666
+ 1661,
1667
+ 1662,
1668
+ 1663,
1669
+ 1664,
1670
+ 1665,
1671
+ 1666,
1672
+ 1667,
1673
+ 1668,
1674
+ 1669,
1675
+ 1670,
1676
+ 1671,
1677
+ 1672,
1678
+ 1673,
1679
+ 1674,
1680
+ 1675,
1681
+ 1676,
1682
+ 1677,
1683
+ 1678,
1684
+ 1679,
1685
+ 1680,
1686
+ 1681,
1687
+ 1682,
1688
+ 1683,
1689
+ 1684,
1690
+ 1685,
1691
+ 1686,
1692
+ 1687,
1693
+ 1688,
1694
+ 1689,
1695
+ 1690,
1696
+ 1691,
1697
+ 1692,
1698
+ 1693,
1699
+ 1694,
1700
+ 1695,
1701
+ 1696,
1702
+ 1697,
1703
+ 1698,
1704
+ 1699,
1705
+ 1700,
1706
+ 1701,
1707
+ 1702,
1708
+ 1703,
1709
+ 1704,
1710
+ 1705,
1711
+ 1706,
1712
+ 1707,
1713
+ 1708,
1714
+ 1709,
1715
+ 1710,
1716
+ 1711,
1717
+ 1712,
1718
+ 1713,
1719
+ 1714,
1720
+ 1715,
1721
+ 1716,
1722
+ 1717,
1723
+ 1718,
1724
+ 1719,
1725
+ 1720,
1726
+ 1721,
1727
+ 1722,
1728
+ 1723,
1729
+ 1724,
1730
+ 1725,
1731
+ 1726,
1732
+ 1727,
1733
+ 1728,
1734
+ 1729,
1735
+ 1730,
1736
+ 1731,
1737
+ 1732,
1738
+ 1733,
1739
+ 1734,
1740
+ 1735,
1741
+ 1736,
1742
+ 1737,
1743
+ 1738,
1744
+ 1739,
1745
+ 1740,
1746
+ 1741,
1747
+ 1742,
1748
+ 1743,
1749
+ 1744,
1750
+ 1745,
1751
+ 1746,
1752
+ 1747,
1753
+ 1748,
1754
+ 1749,
1755
+ 1750,
1756
+ 1751,
1757
+ 1752,
1758
+ 1753,
1759
+ 1754,
1760
+ 1755,
1761
+ 1756,
1762
+ 1757,
1763
+ 1758,
1764
+ 1759,
1765
+ 1760,
1766
+ 1761,
1767
+ 1762,
1768
+ 1763,
1769
+ 1764,
1770
+ 1765,
1771
+ 1766,
1772
+ 1767,
1773
+ 1768,
1774
+ 1769,
1775
+ 1770,
1776
+ 1771,
1777
+ 1772,
1778
+ 1773,
1779
+ 1774,
1780
+ 1775,
1781
+ 1776,
1782
+ 1777,
1783
+ 1778,
1784
+ 1779,
1785
+ 1780,
1786
+ 1781,
1787
+ 1782,
1788
+ 1783,
1789
+ 1784,
1790
+ 1785,
1791
+ 1786,
1792
+ 1787,
1793
+ 1788,
1794
+ 1789,
1795
+ 1790,
1796
+ 1791,
1797
+ 1792,
1798
+ 1793,
1799
+ 1794,
1800
+ 1795,
1801
+ 1796,
1802
+ 1797,
1803
+ 1798,
1804
+ 1799,
1805
+ 1800,
1806
+ 1801,
1807
+ 1802,
1808
+ 1803,
1809
+ 1804,
1810
+ 1805,
1811
+ 1806,
1812
+ 1807,
1813
+ 1808,
1814
+ 1809,
1815
+ 1810,
1816
+ 1811,
1817
+ 1812,
1818
+ 1813,
1819
+ 1814,
1820
+ 1815,
1821
+ 1816,
1822
+ 1817,
1823
+ 1818,
1824
+ 1819,
1825
+ 1820,
1826
+ 1821,
1827
+ 1822,
1828
+ 1823,
1829
+ 1824,
1830
+ 1825,
1831
+ 1826,
1832
+ 1827,
1833
+ 1828,
1834
+ 1829,
1835
+ 1830,
1836
+ 1831,
1837
+ 1832,
1838
+ 1833,
1839
+ 1834,
1840
+ 1835,
1841
+ 1836,
1842
+ 1837,
1843
+ 1838,
1844
+ 1839,
1845
+ 1840,
1846
+ 1841,
1847
+ 1842,
1848
+ 1843,
1849
+ 1844,
1850
+ 1845,
1851
+ 1846,
1852
+ 1847,
1853
+ 1848,
1854
+ 1849,
1855
+ 1850,
1856
+ 1851,
1857
+ 1852,
1858
+ 1853,
1859
+ 1854,
1860
+ 1855,
1861
+ 1856,
1862
+ 1857,
1863
+ 1858,
1864
+ 1859,
1865
+ 1860,
1866
+ 1861,
1867
+ 1862,
1868
+ 1863,
1869
+ 1864,
1870
+ 1865,
1871
+ 1866,
1872
+ 1867,
1873
+ 1868,
1874
+ 1869,
1875
+ 1870,
1876
+ 1871,
1877
+ 1872,
1878
+ 1873,
1879
+ 1874,
1880
+ 1875,
1881
+ 1876,
1882
+ 1877,
1883
+ 1878,
1884
+ 1879,
1885
+ 1880,
1886
+ 1881,
1887
+ 1882,
1888
+ 1883,
1889
+ 1884,
1890
+ 1885,
1891
+ 1886,
1892
+ 1887,
1893
+ 1888,
1894
+ 1889,
1895
+ 1890,
1896
+ 1891,
1897
+ 1892,
1898
+ 1893,
1899
+ 1894,
1900
+ 1895,
1901
+ 1896,
1902
+ 1897,
1903
+ 1898,
1904
+ 1899,
1905
+ 1900,
1906
+ 1901,
1907
+ 1902,
1908
+ 1903,
1909
+ 1904,
1910
+ 1905,
1911
+ 1906,
1912
+ 1907,
1913
+ 1908,
1914
+ 1909,
1915
+ 1910,
1916
+ 1911,
1917
+ 1912,
1918
+ 1913,
1919
+ 1914,
1920
+ 1915,
1921
+ 1916,
1922
+ 1917,
1923
+ 1918,
1924
+ 1919,
1925
+ 1920,
1926
+ 1921,
1927
+ 1922,
1928
+ 1923,
1929
+ 1924,
1930
+ 1925,
1931
+ 1926,
1932
+ 1927,
1933
+ 1928,
1934
+ 1929,
1935
+ 1930,
1936
+ 1931,
1937
+ 1932,
1938
+ 1933,
1939
+ 1934,
1940
+ 1935,
1941
+ 1936,
1942
+ 1937,
1943
+ 1938,
1944
+ 1939,
1945
+ 1940,
1946
+ 1941,
1947
+ 1942,
1948
+ 1943,
1949
+ 1944,
1950
+ 1945,
1951
+ 1946,
1952
+ 1947,
1953
+ 1948,
1954
+ 1949,
1955
+ 1950,
1956
+ 1951,
1957
+ 1952,
1958
+ 1953,
1959
+ 1954,
1960
+ 1955,
1961
+ 1956,
1962
+ 1957,
1963
+ 1958,
1964
+ 1959,
1965
+ 1960,
1966
+ 1961,
1967
+ 1962,
1968
+ 1963,
1969
+ 1964,
1970
+ 1965,
1971
+ 1966,
1972
+ 1967,
1973
+ 1968,
1974
+ 1969,
1975
+ 1970,
1976
+ 1971,
1977
+ 1972,
1978
+ 1973,
1979
+ 1974,
1980
+ 1975,
1981
+ 1976,
1982
+ 1977,
1983
+ 1978,
1984
+ 1979,
1985
+ 1980,
1986
+ 1981,
1987
+ 1982,
1988
+ 1983,
1989
+ 1984,
1990
+ 1985,
1991
+ 1986,
1992
+ 1987,
1993
+ 1988,
1994
+ 1989,
1995
+ 1990,
1996
+ 1991,
1997
+ 1992,
1998
+ 1993,
1999
+ 1994,
2000
+ 1995,
2001
+ 1996,
2002
+ 1997,
2003
+ 1998,
2004
+ 1999,
2005
+ 2000,
2006
+ 2001,
2007
+ 2002,
2008
+ 2003,
2009
+ 2004,
2010
+ 2005,
2011
+ 2006,
2012
+ 2007,
2013
+ 2008,
2014
+ 2009,
2015
+ 2010,
2016
+ 2011,
2017
+ 2012,
2018
+ 2013,
2019
+ 2014,
2020
+ 2015,
2021
+ 2016,
2022
+ 2017,
2023
+ 2018,
2024
+ 2019,
2025
+ 2020,
2026
+ 2021,
2027
+ 2022,
2028
+ 2023,
2029
+ 2024,
2030
+ 2025,
2031
+ 2026,
2032
+ 2027,
2033
+ 2028,
2034
+ 2029,
2035
+ 2030,
2036
+ 2031,
2037
+ 2032,
2038
+ 2033,
2039
+ 2034,
2040
+ 2035,
2041
+ 2036,
2042
+ 2037,
2043
+ 2038,
2044
+ 2039,
2045
+ 2040,
2046
+ 2041,
2047
+ 2042,
2048
+ 2043,
2049
+ 2044,
2050
+ 2045,
2051
+ 2046,
2052
+ 2047,
2053
+ 2048,
2054
+ 2049,
2055
+ 2050,
2056
+ 2051,
2057
+ 2052,
2058
+ 2053,
2059
+ 2054,
2060
+ 2055,
2061
+ 2056,
2062
+ 2057,
2063
+ 2058,
2064
+ 2059,
2065
+ 2060,
2066
+ 2061,
2067
+ 2062,
2068
+ 2063,
2069
+ 2064,
2070
+ 2065,
2071
+ 2066,
2072
+ 2067,
2073
+ 2068,
2074
+ 2069,
2075
+ 2070,
2076
+ 2071,
2077
+ 2072,
2078
+ 2073,
2079
+ 2074,
2080
+ 2075,
2081
+ 2076,
2082
+ 2077,
2083
+ 2078,
2084
+ 2079,
2085
+ 2080,
2086
+ 2081,
2087
+ 2082,
2088
+ 2083,
2089
+ 2084,
2090
+ 2085,
2091
+ 2086,
2092
+ 2087,
2093
+ 2088,
2094
+ 2089,
2095
+ 2090,
2096
+ 2091,
2097
+ 2092,
2098
+ 2093,
2099
+ 2094,
2100
+ 2095,
2101
+ 2096,
2102
+ 2097,
2103
+ 2098,
2104
+ 2099,
2105
+ 2100,
2106
+ 2101,
2107
+ 2102,
2108
+ 2103,
2109
+ 2104,
2110
+ 2105,
2111
+ 2106,
2112
+ 2107,
2113
+ 2108,
2114
+ 2109,
2115
+ 2110,
2116
+ 2111,
2117
+ 2112,
2118
+ 2113,
2119
+ 2114,
2120
+ 2115,
2121
+ 2116,
2122
+ 2117,
2123
+ 2118,
2124
+ 2119,
2125
+ 2120,
2126
+ 2121,
2127
+ 2122,
2128
+ 2123,
2129
+ 2124,
2130
+ 2125,
2131
+ 2126,
2132
+ 2127,
2133
+ 2128,
2134
+ 2129,
2135
+ 2130,
2136
+ 2131,
2137
+ 2132,
2138
+ 2133,
2139
+ 2134,
2140
+ 2135,
2141
+ 2136,
2142
+ 2137,
2143
+ 2138,
2144
+ 2139,
2145
+ 2140,
2146
+ 2141,
2147
+ 2142,
2148
+ 2143,
2149
+ 2144,
2150
+ 2145,
2151
+ 2146,
2152
+ 2147,
2153
+ 2148,
2154
+ 2149,
2155
+ 2150,
2156
+ 2151,
2157
+ 2152,
2158
+ 2153,
2159
+ 2154,
2160
+ 2155,
2161
+ 2156,
2162
+ 2157,
2163
+ 2158,
2164
+ 2159,
2165
+ 2160,
2166
+ 2161,
2167
+ 2162,
2168
+ 2163,
2169
+ 2164,
2170
+ 2165,
2171
+ 2166,
2172
+ 2167,
2173
+ 2168,
2174
+ 2169,
2175
+ 2170,
2176
+ 2171,
2177
+ 2172,
2178
+ 2173,
2179
+ 2174,
2180
+ 2175,
2181
+ 2176,
2182
+ 2177,
2183
+ 2178,
2184
+ 2179,
2185
+ 2180,
2186
+ 2181,
2187
+ 2182,
2188
+ 2183,
2189
+ 2184,
2190
+ 2185,
2191
+ 2186,
2192
+ 2187,
2193
+ 2188,
2194
+ 2189,
2195
+ 2190,
2196
+ 2191,
2197
+ 2192,
2198
+ 2193,
2199
+ 2194,
2200
+ 2195,
2201
+ 2196,
2202
+ 2197,
2203
+ 2198,
2204
+ 2199,
2205
+ 2200,
2206
+ 2201,
2207
+ 2202,
2208
+ 2203,
2209
+ 2204,
2210
+ 2205,
2211
+ 2206,
2212
+ 2207,
2213
+ 2208,
2214
+ 2209,
2215
+ 2210,
2216
+ 2211,
2217
+ 2212,
2218
+ 2213,
2219
+ 2214,
2220
+ 2215,
2221
+ 2216,
2222
+ 2217,
2223
+ 2218,
2224
+ 2219,
2225
+ 2220,
2226
+ 2221,
2227
+ 2222,
2228
+ 2223,
2229
+ 2224,
2230
+ 2225,
2231
+ 2226,
2232
+ 2227,
2233
+ 2228,
2234
+ 2229,
2235
+ 2230,
2236
+ 2231,
2237
+ 2232,
2238
+ 2233,
2239
+ 2234,
2240
+ 2235,
2241
+ 2236,
2242
+ 2237,
2243
+ 2238,
2244
+ 2239,
2245
+ 2240,
2246
+ 2241,
2247
+ 2242,
2248
+ 2243,
2249
+ 2244,
2250
+ 2245,
2251
+ 2246,
2252
+ 2247,
2253
+ 2248,
2254
+ 2249,
2255
+ 2250,
2256
+ 2251,
2257
+ 2252,
2258
+ 2253,
2259
+ 2254,
2260
+ 2255,
2261
+ 2256,
2262
+ 2257,
2263
+ 2258,
2264
+ 2259,
2265
+ 2260,
2266
+ 2261,
2267
+ 2262,
2268
+ 2263,
2269
+ 2264,
2270
+ 2265,
2271
+ 2266,
2272
+ 2267,
2273
+ 2268,
2274
+ 2269,
2275
+ 2270,
2276
+ 2271,
2277
+ 2272,
2278
+ 2273,
2279
+ 2274,
2280
+ 2275,
2281
+ 2276,
2282
+ 2277,
2283
+ 2278,
2284
+ 2279,
2285
+ 2280,
2286
+ 2281,
2287
+ 2282,
2288
+ 2283,
2289
+ 2284,
2290
+ 2285,
2291
+ 2286,
2292
+ 2287,
2293
+ 2288,
2294
+ 2289,
2295
+ 2290,
2296
+ 2291,
2297
+ 2292,
2298
+ 2293,
2299
+ 2294,
2300
+ 2295,
2301
+ 2296,
2302
+ 2297,
2303
+ 2298,
2304
+ 2299,
2305
+ 2300,
2306
+ 2301,
2307
+ 2302,
2308
+ 2303,
2309
+ 2304,
2310
+ 2305,
2311
+ 2306,
2312
+ 2307,
2313
+ 2308,
2314
+ 2309,
2315
+ 2310,
2316
+ 2311,
2317
+ 2312,
2318
+ 2313,
2319
+ 2314,
2320
+ 2315,
2321
+ 2316,
2322
+ 2317,
2323
+ 2318,
2324
+ 2319,
2325
+ 2320,
2326
+ 2321,
2327
+ 2322,
2328
+ 2323,
2329
+ 2324,
2330
+ 2325,
2331
+ 2326,
2332
+ 2327,
2333
+ 2328,
2334
+ 2329,
2335
+ 2330,
2336
+ 2331,
2337
+ 2332,
2338
+ 2333,
2339
+ 2334,
2340
+ 2335,
2341
+ 2336,
2342
+ 2337,
2343
+ 2338,
2344
+ 2339,
2345
+ 2340,
2346
+ 2341,
2347
+ 2342,
2348
+ 2343,
2349
+ 2344,
2350
+ 2345,
2351
+ 2346,
2352
+ 2347,
2353
+ 2348,
2354
+ 2349,
2355
+ 2350,
2356
+ 2351,
2357
+ 2352,
2358
+ 2353,
2359
+ 2354,
2360
+ 2355,
2361
+ 2356,
2362
+ 2357,
2363
+ 2358,
2364
+ 2359,
2365
+ 2360,
2366
+ 2361,
2367
+ 2362,
2368
+ 2363,
2369
+ 2364,
2370
+ 2365,
2371
+ 2366,
2372
+ 2367,
2373
+ 2368,
2374
+ 2369,
2375
+ 2370,
2376
+ 2371,
2377
+ 2372,
2378
+ 2373,
2379
+ 2374,
2380
+ 2375,
2381
+ 2376,
2382
+ 2377,
2383
+ 2378,
2384
+ 2379,
2385
+ 2380,
2386
+ 2381,
2387
+ 2382,
2388
+ 2383,
2389
+ 2384,
2390
+ 2385,
2391
+ 2386,
2392
+ 2387,
2393
+ 2388,
2394
+ 2389,
2395
+ 2390,
2396
+ 2391,
2397
+ 2392,
2398
+ 2393,
2399
+ 2394,
2400
+ 2395,
2401
+ 2396,
2402
+ 2397,
2403
+ 2398,
2404
+ 2399,
2405
+ 2400,
2406
+ 2401,
2407
+ 2402,
2408
+ 2403,
2409
+ 2404,
2410
+ 2405,
2411
+ 2406,
2412
+ 2407,
2413
+ 2408,
2414
+ 2409,
2415
+ 2410,
2416
+ 2411,
2417
+ 2412,
2418
+ 2413,
2419
+ 2414,
2420
+ 2415,
2421
+ 2416,
2422
+ 2417,
2423
+ 2418,
2424
+ 2419,
2425
+ 2420,
2426
+ 2421,
2427
+ 2422,
2428
+ 2423,
2429
+ 2424,
2430
+ 2425,
2431
+ 2426,
2432
+ 2427,
2433
+ 2428,
2434
+ 2429,
2435
+ 2430,
2436
+ 2431,
2437
+ 2432,
2438
+ 2433,
2439
+ 2434,
2440
+ 2435,
2441
+ 2436,
2442
+ 2437,
2443
+ 2438,
2444
+ 2439,
2445
+ 2440,
2446
+ 2441,
2447
+ 2442,
2448
+ 2443,
2449
+ 2444,
2450
+ 2445,
2451
+ 2446,
2452
+ 2447,
2453
+ 2448,
2454
+ 2449,
2455
+ 2450,
2456
+ 2451,
2457
+ 2452,
2458
+ 2453,
2459
+ 2454,
2460
+ 2455,
2461
+ 2456,
2462
+ 2457,
2463
+ 2458,
2464
+ 2459,
2465
+ 2460,
2466
+ 2461,
2467
+ 2462,
2468
+ 2463,
2469
+ 2464,
2470
+ 2465,
2471
+ 2466,
2472
+ 2467,
2473
+ 2468,
2474
+ 2469,
2475
+ 2470,
2476
+ 2471,
2477
+ 2472,
2478
+ 2473,
2479
+ 2474,
2480
+ 2475,
2481
+ 2476,
2482
+ 2477,
2483
+ 2478,
2484
+ 2479,
2485
+ 2480,
2486
+ 2481,
2487
+ 2482,
2488
+ 2483,
2489
+ 2484,
2490
+ 2485,
2491
+ 2486,
2492
+ 2487,
2493
+ 2488,
2494
+ 2489,
2495
+ 2490,
2496
+ 2491,
2497
+ 2492,
2498
+ 2493,
2499
+ 2494,
2500
+ 2495,
2501
+ 2496,
2502
+ 2497,
2503
+ 2498,
2504
+ 2499,
2505
+ 2500,
2506
+ 2501,
2507
+ 2502,
2508
+ 2503,
2509
+ 2504,
2510
+ 2505,
2511
+ 2506,
2512
+ 2507,
2513
+ 2508,
2514
+ 2509,
2515
+ 2510,
2516
+ 2511,
2517
+ 2512,
2518
+ 2513,
2519
+ 2514,
2520
+ 2515,
2521
+ 2516,
2522
+ 2517,
2523
+ 2518,
2524
+ 2519,
2525
+ 2520,
2526
+ 2521,
2527
+ 2522,
2528
+ 2523,
2529
+ 2524,
2530
+ 2525,
2531
+ 2526,
2532
+ 2527,
2533
+ 2528,
2534
+ 2529,
2535
+ 2530,
2536
+ 2531,
2537
+ 2532,
2538
+ 2533,
2539
+ 2534,
2540
+ 2535,
2541
+ 2536,
2542
+ 2537,
2543
+ 2538,
2544
+ 2539,
2545
+ 2540,
2546
+ 2541,
2547
+ 2542,
2548
+ 2543,
2549
+ 2544,
2550
+ 2545,
2551
+ 2546,
2552
+ 2547,
2553
+ 2548,
2554
+ 2549,
2555
+ 2550,
2556
+ 2551,
2557
+ 2552,
2558
+ 2553,
2559
+ 2554,
2560
+ 2555,
2561
+ 2556,
2562
+ 2557,
2563
+ 2558,
2564
+ 2559,
2565
+ 2560,
2566
+ 2561,
2567
+ 2562,
2568
+ 2563,
2569
+ 2564,
2570
+ 2565,
2571
+ 2566,
2572
+ 2567,
2573
+ 2568,
2574
+ 2569,
2575
+ 2570,
2576
+ 2571,
2577
+ 2572,
2578
+ 2573,
2579
+ 2574,
2580
+ 2575,
2581
+ 2576,
2582
+ 2577,
2583
+ 2578,
2584
+ 2579,
2585
+ 2580,
2586
+ 2581,
2587
+ 2582,
2588
+ 2583,
2589
+ 2584,
2590
+ 2585,
2591
+ 2586,
2592
+ 2587,
2593
+ 2588,
2594
+ 2589,
2595
+ 2590,
2596
+ 2591,
2597
+ 2592,
2598
+ 2593,
2599
+ 2594,
2600
+ 2595,
2601
+ 2596,
2602
+ 2597,
2603
+ 2598,
2604
+ 2599,
2605
+ 2600,
2606
+ 2601,
2607
+ 2602,
2608
+ 2603,
2609
+ 2604,
2610
+ 2605,
2611
+ 2606,
2612
+ 2607,
2613
+ 2608,
2614
+ 2609,
2615
+ 2610,
2616
+ 2611,
2617
+ 2612,
2618
+ 2613,
2619
+ 2614,
2620
+ 2615,
2621
+ 2616,
2622
+ 2617,
2623
+ 2618,
2624
+ 2619,
2625
+ 2620,
2626
+ 2621,
2627
+ 2622,
2628
+ 2623,
2629
+ 2624,
2630
+ 2625,
2631
+ 2626,
2632
+ 2627,
2633
+ 2628,
2634
+ 2629,
2635
+ 2630,
2636
+ 2631,
2637
+ 2632,
2638
+ 2633,
2639
+ 2634,
2640
+ 2635,
2641
+ 2636,
2642
+ 2637,
2643
+ 2638,
2644
+ 2639,
2645
+ 2640,
2646
+ 2641,
2647
+ 2642,
2648
+ 2643,
2649
+ 2644,
2650
+ 2645,
2651
+ 2646,
2652
+ 2647,
2653
+ 2648,
2654
+ 2649,
2655
+ 2650,
2656
+ 2651,
2657
+ 2652,
2658
+ 2653,
2659
+ 2654,
2660
+ 2655,
2661
+ 2656,
2662
+ 2657,
2663
+ 2658,
2664
+ 2659,
2665
+ 2660,
2666
+ 2661,
2667
+ 2662,
2668
+ 2663,
2669
+ 2664,
2670
+ 2665,
2671
+ 2666,
2672
+ 2667,
2673
+ 2668,
2674
+ 2669,
2675
+ 2670,
2676
+ 2671,
2677
+ 2672,
2678
+ 2673,
2679
+ 2674,
2680
+ 2675,
2681
+ 2676,
2682
+ 2677,
2683
+ 2678,
2684
+ 2679,
2685
+ 2680,
2686
+ 2681,
2687
+ 2682,
2688
+ 2683,
2689
+ 2684,
2690
+ 2685,
2691
+ 2686,
2692
+ 2687,
2693
+ 2688,
2694
+ 2689,
2695
+ 2690,
2696
+ 2691,
2697
+ 2692,
2698
+ 2693,
2699
+ 2694,
2700
+ 2695,
2701
+ 2696,
2702
+ 2697,
2703
+ 2698,
2704
+ 2699,
2705
+ 2700,
2706
+ 2701,
2707
+ 2702,
2708
+ 2703,
2709
+ 2704,
2710
+ 2705,
2711
+ 2706,
2712
+ 2707,
2713
+ 2708,
2714
+ 2709,
2715
+ 2710,
2716
+ 2711,
2717
+ 2712,
2718
+ 2713,
2719
+ 2714,
2720
+ 2715,
2721
+ 2716,
2722
+ 2717,
2723
+ 2718,
2724
+ 2719,
2725
+ 2720,
2726
+ 2721,
2727
+ 2722,
2728
+ 2723,
2729
+ 2724,
2730
+ 2725,
2731
+ 2726,
2732
+ 2727,
2733
+ 2728,
2734
+ 2729,
2735
+ 2730,
2736
+ 2731,
2737
+ 2732,
2738
+ 2733,
2739
+ 2734,
2740
+ 2735,
2741
+ 2736,
2742
+ 2737,
2743
+ 2738,
2744
+ 2739,
2745
+ 2740,
2746
+ 2741,
2747
+ 2742,
2748
+ 2743,
2749
+ 2744,
2750
+ 2745,
2751
+ 2746,
2752
+ 2747,
2753
+ 2748,
2754
+ 2749,
2755
+ 2750,
2756
+ 2751,
2757
+ 2752,
2758
+ 2753,
2759
+ 2754,
2760
+ 2755,
2761
+ 2756,
2762
+ 2757,
2763
+ 2758,
2764
+ 2759,
2765
+ 2760,
2766
+ 2761,
2767
+ 2762,
2768
+ 2763,
2769
+ 2764,
2770
+ 2765,
2771
+ 2766,
2772
+ 2767,
2773
+ 2768,
2774
+ 2769,
2775
+ 2770,
2776
+ 2771,
2777
+ 2772,
2778
+ 2773,
2779
+ 2774,
2780
+ 2775,
2781
+ 2776,
2782
+ 2777,
2783
+ 2778,
2784
+ 2779,
2785
+ 2780,
2786
+ 2781,
2787
+ 2782,
2788
+ 2783,
2789
+ 2784,
2790
+ 2785,
2791
+ 2786,
2792
+ 2787,
2793
+ 2788,
2794
+ 2789,
2795
+ 2790,
2796
+ 2791,
2797
+ 2792,
2798
+ 2793,
2799
+ 2794,
2800
+ 2795,
2801
+ 2796,
2802
+ 2797,
2803
+ 2798,
2804
+ 2799,
2805
+ 2800,
2806
+ 2801,
2807
+ 2802,
2808
+ 2803,
2809
+ 2804,
2810
+ 2805,
2811
+ 2806,
2812
+ 2807,
2813
+ 2808,
2814
+ 2809,
2815
+ 2810,
2816
+ 2811,
2817
+ 2812,
2818
+ 2813,
2819
+ 2814,
2820
+ 2815,
2821
+ 2816,
2822
+ 2817,
2823
+ 2818,
2824
+ 2819,
2825
+ 2820,
2826
+ 2821,
2827
+ 2822,
2828
+ 2823,
2829
+ 2824,
2830
+ 2825,
2831
+ 2826,
2832
+ 2827,
2833
+ 2828,
2834
+ 2829,
2835
+ 2830,
2836
+ 2831,
2837
+ 2832,
2838
+ 2833,
2839
+ 2834,
2840
+ 2835,
2841
+ 2836,
2842
+ 2837,
2843
+ 2838,
2844
+ 2839,
2845
+ 2840,
2846
+ 2841,
2847
+ 2842,
2848
+ 2843,
2849
+ 2844,
2850
+ 2845,
2851
+ 2846,
2852
+ 2847,
2853
+ 2848,
2854
+ 2849,
2855
+ 2850,
2856
+ 2851,
2857
+ 2852,
2858
+ 2853,
2859
+ 2854,
2860
+ 2855,
2861
+ 2856,
2862
+ 2857,
2863
+ 2858,
2864
+ 2859,
2865
+ 2860,
2866
+ 2861,
2867
+ 2862,
2868
+ 2863,
2869
+ 2864,
2870
+ 2865,
2871
+ 2866,
2872
+ 2867,
2873
+ 2868,
2874
+ 2869,
2875
+ 2870,
2876
+ 2871,
2877
+ 2872,
2878
+ 2873,
2879
+ 2874,
2880
+ 2875,
2881
+ 2876,
2882
+ 2877,
2883
+ 2878,
2884
+ 2879,
2885
+ 2880,
2886
+ 2881,
2887
+ 2882,
2888
+ 2883,
2889
+ 2884,
2890
+ 2885,
2891
+ 2886,
2892
+ 2887,
2893
+ 2888,
2894
+ 2889,
2895
+ 2890,
2896
+ 2891,
2897
+ 2892,
2898
+ 2893,
2899
+ 2894,
2900
+ 2895,
2901
+ 2896,
2902
+ 2897,
2903
+ 2898,
2904
+ 2899,
2905
+ 2900,
2906
+ 2901,
2907
+ 2902,
2908
+ 2903,
2909
+ 2904,
2910
+ 2905,
2911
+ 2906,
2912
+ 2907,
2913
+ 2908,
2914
+ 2909,
2915
+ 2910,
2916
+ 2911,
2917
+ 2912,
2918
+ 2913,
2919
+ 2914,
2920
+ 2915,
2921
+ 2916,
2922
+ 2917,
2923
+ 2918,
2924
+ 2919,
2925
+ 2920,
2926
+ 2921,
2927
+ 2922,
2928
+ 2923,
2929
+ 2924,
2930
+ 2925,
2931
+ 2926,
2932
+ 2927,
2933
+ 2928,
2934
+ 2929,
2935
+ 2930,
2936
+ 2931,
2937
+ 2932,
2938
+ 2933,
2939
+ 2934,
2940
+ 2935,
2941
+ 2936,
2942
+ 2937,
2943
+ 2938,
2944
+ 2939,
2945
+ 2940,
2946
+ 2941,
2947
+ 2942,
2948
+ 2943,
2949
+ 2944,
2950
+ 2945,
2951
+ 2946,
2952
+ 2947,
2953
+ 2948,
2954
+ 2949,
2955
+ 2950,
2956
+ 2951,
2957
+ 2952,
2958
+ 2953,
2959
+ 2954,
2960
+ 2955,
2961
+ 2956,
2962
+ 2957,
2963
+ 2958,
2964
+ 2959,
2965
+ 2960,
2966
+ 2961,
2967
+ 2962,
2968
+ 2963,
2969
+ 2964,
2970
+ 2965,
2971
+ 2966,
2972
+ 2967,
2973
+ 2968,
2974
+ 2969,
2975
+ 2970,
2976
+ 2971,
2977
+ 2972,
2978
+ 2973,
2979
+ 2974,
2980
+ 2975,
2981
+ 2976,
2982
+ 2977,
2983
+ 2978,
2984
+ 2979,
2985
+ 2980,
2986
+ 2981,
2987
+ 2982,
2988
+ 2983,
2989
+ 2984,
2990
+ 2985,
2991
+ 2986,
2992
+ 2987,
2993
+ 2988,
2994
+ 2989,
2995
+ 2990,
2996
+ 2991,
2997
+ 2992,
2998
+ 2993,
2999
+ 2994,
3000
+ 2995,
3001
+ 2996,
3002
+ 2997,
3003
+ 2998,
3004
+ 2999,
3005
+ 3000,
3006
+ 3001,
3007
+ 3002,
3008
+ 3003,
3009
+ 3004,
3010
+ 3005,
3011
+ 3006,
3012
+ 3007,
3013
+ 3008,
3014
+ 3009,
3015
+ 3010,
3016
+ 3011,
3017
+ 3012,
3018
+ 3013,
3019
+ 3014,
3020
+ 3015,
3021
+ 3016,
3022
+ 3017,
3023
+ 3018,
3024
+ 3019,
3025
+ 3020,
3026
+ 3021,
3027
+ 3022,
3028
+ 3023,
3029
+ 3024,
3030
+ 3025,
3031
+ 3026,
3032
+ 3027,
3033
+ 3028,
3034
+ 3029,
3035
+ 3030,
3036
+ 3031,
3037
+ 3032,
3038
+ 3033,
3039
+ 3034,
3040
+ 3035,
3041
+ 3036,
3042
+ 3037,
3043
+ 3038,
3044
+ 3039,
3045
+ 3040,
3046
+ 3041,
3047
+ 3042,
3048
+ 3043,
3049
+ 3044,
3050
+ 3045,
3051
+ 3046,
3052
+ 3047,
3053
+ 3048,
3054
+ 3049,
3055
+ 3050,
3056
+ 3051,
3057
+ 3052,
3058
+ 3053,
3059
+ 3054,
3060
+ 3055,
3061
+ 3056,
3062
+ 3057,
3063
+ 3058,
3064
+ 3059,
3065
+ 3060,
3066
+ 3061,
3067
+ 3062,
3068
+ 3063,
3069
+ 3064,
3070
+ 3065,
3071
+ 3066,
3072
+ 3067,
3073
+ 3068,
3074
+ 3069,
3075
+ 3070,
3076
+ 3071,
3077
+ 3072,
3078
+ 3073,
3079
+ 3074,
3080
+ 3075,
3081
+ 3076,
3082
+ 3077,
3083
+ 3078,
3084
+ 3079,
3085
+ 3080,
3086
+ 3081,
3087
+ 3082,
3088
+ 3083,
3089
+ 3084,
3090
+ 3085,
3091
+ 3086,
3092
+ 3087,
3093
+ 3088,
3094
+ 3089,
3095
+ 3090,
3096
+ 3091,
3097
+ 3092,
3098
+ 3093,
3099
+ 3094,
3100
+ 3095,
3101
+ 3096,
3102
+ 3097,
3103
+ 3098,
3104
+ 3099,
3105
+ 3100,
3106
+ 3101,
3107
+ 3102,
3108
+ 3103,
3109
+ 3104,
3110
+ 3105,
3111
+ 3106,
3112
+ 3107,
3113
+ 3108,
3114
+ 3109,
3115
+ 3110,
3116
+ 3111,
3117
+ 3112,
3118
+ 3113,
3119
+ 3114,
3120
+ 3115,
3121
+ 3116,
3122
+ 3117,
3123
+ 3118,
3124
+ 3119,
3125
+ 3120,
3126
+ 3121,
3127
+ 3122,
3128
+ 3123,
3129
+ 3124,
3130
+ 3125,
3131
+ 3126,
3132
+ 3127,
3133
+ 3128,
3134
+ 3129,
3135
+ 3130,
3136
+ 3131,
3137
+ 3132,
3138
+ 3133,
3139
+ 3134,
3140
+ 3135,
3141
+ 3136,
3142
+ 3137,
3143
+ 3138,
3144
+ 3139,
3145
+ 3140,
3146
+ 3141,
3147
+ 3142,
3148
+ 3143,
3149
+ 3144,
3150
+ 3145,
3151
+ 3146,
3152
+ 3147,
3153
+ 3148,
3154
+ 3149,
3155
+ 3150,
3156
+ 3151,
3157
+ 3152,
3158
+ 3153,
3159
+ 3154,
3160
+ 3155,
3161
+ 3156,
3162
+ 3157,
3163
+ 3158,
3164
+ 3159,
3165
+ 3160,
3166
+ 3161,
3167
+ 3162,
3168
+ 3163,
3169
+ 3164,
3170
+ 3165,
3171
+ 3166,
3172
+ 3167,
3173
+ 3168,
3174
+ 3169,
3175
+ 3170,
3176
+ 3171,
3177
+ 3172,
3178
+ 3173,
3179
+ 3174,
3180
+ 3175,
3181
+ 3176,
3182
+ 3177,
3183
+ 3178,
3184
+ 3179,
3185
+ 3180,
3186
+ 3181,
3187
+ 3182,
3188
+ 3183,
3189
+ 3184,
3190
+ 3185,
3191
+ 3186,
3192
+ 3187,
3193
+ 3188,
3194
+ 3189,
3195
+ 3190,
3196
+ 3191,
3197
+ 3192,
3198
+ 3193,
3199
+ 3194,
3200
+ 3195,
3201
+ 3196,
3202
+ 3197,
3203
+ 3198,
3204
+ 3199,
3205
+ 3200,
3206
+ 3201,
3207
+ 3202,
3208
+ 3203,
3209
+ 3204,
3210
+ 3205,
3211
+ 3206,
3212
+ 3207,
3213
+ 3208,
3214
+ 3209,
3215
+ 3210,
3216
+ 3211,
3217
+ 3212,
3218
+ 3213,
3219
+ 3214,
3220
+ 3215,
3221
+ 3216,
3222
+ 3217,
3223
+ 3218,
3224
+ 3219,
3225
+ 3220,
3226
+ 3221,
3227
+ 3222,
3228
+ 3223,
3229
+ 3224,
3230
+ 3225,
3231
+ 3226,
3232
+ 3227,
3233
+ 3228,
3234
+ 3229,
3235
+ 3230,
3236
+ 3231,
3237
+ 3232,
3238
+ 3233,
3239
+ 3234,
3240
+ 3235,
3241
+ 3236,
3242
+ 3237,
3243
+ 3238,
3244
+ 3239,
3245
+ 3240,
3246
+ 3241,
3247
+ 3242,
3248
+ 3243,
3249
+ 3244,
3250
+ 3245,
3251
+ 3246,
3252
+ 3247,
3253
+ 3248,
3254
+ 3249,
3255
+ 3250,
3256
+ 3251,
3257
+ 3252,
3258
+ 3253,
3259
+ 3254,
3260
+ 3255,
3261
+ 3256,
3262
+ 3257,
3263
+ 3258,
3264
+ 3259,
3265
+ 3260,
3266
+ 3261,
3267
+ 3262,
3268
+ 3263,
3269
+ 3264,
3270
+ 3265,
3271
+ 3266,
3272
+ 3267,
3273
+ 3268,
3274
+ 3269,
3275
+ 3270,
3276
+ 3271,
3277
+ 3272,
3278
+ 3273,
3279
+ 3274,
3280
+ 3275,
3281
+ 3276,
3282
+ 3277,
3283
+ 3278,
3284
+ 3279,
3285
+ 3280,
3286
+ 3281,
3287
+ 3282,
3288
+ 3283,
3289
+ 3284,
3290
+ 3285,
3291
+ 3286,
3292
+ 3287,
3293
+ 3288,
3294
+ 3289,
3295
+ 3290,
3296
+ 3291,
3297
+ 3292,
3298
+ 3293,
3299
+ 3294,
3300
+ 3295,
3301
+ 3296,
3302
+ 3297,
3303
+ 3298,
3304
+ 3299,
3305
+ 3300,
3306
+ 3301,
3307
+ 3302,
3308
+ 3303,
3309
+ 3304,
3310
+ 3305,
3311
+ 3306,
3312
+ 3307,
3313
+ 3308,
3314
+ 3309,
3315
+ 3310,
3316
+ 3311,
3317
+ 3312,
3318
+ 3313,
3319
+ 3314,
3320
+ 3315,
3321
+ 3316,
3322
+ 3317,
3323
+ 3318,
3324
+ 3319,
3325
+ 3320,
3326
+ 3321,
3327
+ 3322,
3328
+ 3323,
3329
+ 3324,
3330
+ 3325,
3331
+ 3326,
3332
+ 3327,
3333
+ 3328,
3334
+ 3329,
3335
+ 3330,
3336
+ 3331,
3337
+ 3332,
3338
+ 3333,
3339
+ 3334,
3340
+ 3335,
3341
+ 3336,
3342
+ 3337,
3343
+ 3338,
3344
+ 3339,
3345
+ 3340,
3346
+ 3341,
3347
+ 3342,
3348
+ 3343,
3349
+ 3344,
3350
+ 3345,
3351
+ 3346,
3352
+ 3347,
3353
+ 3348,
3354
+ 3349,
3355
+ 3350,
3356
+ 3351,
3357
+ 3352,
3358
+ 3353,
3359
+ 3354,
3360
+ 3355,
3361
+ 3356,
3362
+ 3357,
3363
+ 3358,
3364
+ 3359,
3365
+ 3360,
3366
+ 3361,
3367
+ 3362,
3368
+ 3363,
3369
+ 3364,
3370
+ 3365,
3371
+ 3366,
3372
+ 3367,
3373
+ 3368,
3374
+ 3369,
3375
+ 3370,
3376
+ 3371,
3377
+ 3372,
3378
+ 3373,
3379
+ 3374,
3380
+ 3375,
3381
+ 3376,
3382
+ 3377,
3383
+ 3378,
3384
+ 3379,
3385
+ 3380,
3386
+ 3381,
3387
+ 3382,
3388
+ 3383,
3389
+ 3384,
3390
+ 3385,
3391
+ 3386,
3392
+ 3387,
3393
+ 3388,
3394
+ 3389,
3395
+ 3390,
3396
+ 3391,
3397
+ 3392,
3398
+ 3393,
3399
+ 3394,
3400
+ 3395,
3401
+ 3396,
3402
+ 3397,
3403
+ 3398,
3404
+ 3399,
3405
+ 3400,
3406
+ 3401,
3407
+ 3402,
3408
+ 3403,
3409
+ 3404,
3410
+ 3405,
3411
+ 3406,
3412
+ 3407,
3413
+ 3408,
3414
+ 3409,
3415
+ 3410,
3416
+ 3411,
3417
+ 3412,
3418
+ 3413,
3419
+ 3414,
3420
+ 3415,
3421
+ 3416,
3422
+ 3417,
3423
+ 3418,
3424
+ 3419,
3425
+ 3420,
3426
+ 3421,
3427
+ 3422,
3428
+ 3423,
3429
+ 3424,
3430
+ 3425,
3431
+ 3426,
3432
+ 3427,
3433
+ 3428,
3434
+ 3429,
3435
+ 3430,
3436
+ 3431,
3437
+ 3432,
3438
+ 3433,
3439
+ 3434,
3440
+ 3435,
3441
+ 3436,
3442
+ 3437,
3443
+ 3438,
3444
+ 3439,
3445
+ 3440,
3446
+ 3441,
3447
+ 3442,
3448
+ 3443,
3449
+ 3444,
3450
+ 3445,
3451
+ 3446,
3452
+ 3447,
3453
+ 3448,
3454
+ 3449,
3455
+ 3450,
3456
+ 3451,
3457
+ 3452,
3458
+ 3453,
3459
+ 3454,
3460
+ 3455,
3461
+ 3456,
3462
+ 3457,
3463
+ 3458,
3464
+ 3459,
3465
+ 3460,
3466
+ 3461,
3467
+ 3462,
3468
+ 3463,
3469
+ 3464,
3470
+ 3465,
3471
+ 3466,
3472
+ 3467,
3473
+ 3468,
3474
+ 3469,
3475
+ 3470,
3476
+ 3471,
3477
+ 3472,
3478
+ 3473,
3479
+ 3474,
3480
+ 3475,
3481
+ 3476,
3482
+ 3477,
3483
+ 3478,
3484
+ 3479,
3485
+ 3480,
3486
+ 3481,
3487
+ 3482,
3488
+ 3483,
3489
+ 3484,
3490
+ 3485,
3491
+ 3486,
3492
+ 3487,
3493
+ 3488,
3494
+ 3489,
3495
+ 3490,
3496
+ 3491,
3497
+ 3492,
3498
+ 3493,
3499
+ 3494,
3500
+ 3495,
3501
+ 3496,
3502
+ 3497,
3503
+ 3498,
3504
+ 3499,
3505
+ 3500,
3506
+ 3501,
3507
+ 3502,
3508
+ 3503,
3509
+ 3504,
3510
+ 3505,
3511
+ 3506,
3512
+ 3507,
3513
+ 3508,
3514
+ 3509,
3515
+ 3510,
3516
+ 3511,
3517
+ 3512,
3518
+ 3513,
3519
+ 3514,
3520
+ 3515,
3521
+ 3516,
3522
+ 3517,
3523
+ 3518,
3524
+ 3519,
3525
+ 3520,
3526
+ 3521,
3527
+ 3522,
3528
+ 3523,
3529
+ 3524,
3530
+ 3525,
3531
+ 3526,
3532
+ 3527,
3533
+ 3528,
3534
+ 3529,
3535
+ 3530,
3536
+ 3531,
3537
+ 3532,
3538
+ 3533,
3539
+ 3534,
3540
+ 3535,
3541
+ 3536,
3542
+ 3537,
3543
+ 3538,
3544
+ 3539,
3545
+ 3540,
3546
+ 3541,
3547
+ 3542,
3548
+ 3543,
3549
+ 3544,
3550
+ 3545,
3551
+ 3546,
3552
+ 3547,
3553
+ 3548,
3554
+ 3549,
3555
+ 3550,
3556
+ 3551,
3557
+ 3552,
3558
+ 3553,
3559
+ 3554,
3560
+ 3555,
3561
+ 3556,
3562
+ 3557,
3563
+ 3558,
3564
+ 3559,
3565
+ 3560,
3566
+ 3561,
3567
+ 3562,
3568
+ 3563,
3569
+ 3564,
3570
+ 3565,
3571
+ 3566,
3572
+ 3567,
3573
+ 3568,
3574
+ 3569,
3575
+ 3570,
3576
+ 3571,
3577
+ 3572,
3578
+ 3573,
3579
+ 3574,
3580
+ 3575,
3581
+ 3576,
3582
+ 3577,
3583
+ 3578,
3584
+ 3579,
3585
+ 3580,
3586
+ 3581,
3587
+ 3582,
3588
+ 3583,
3589
+ 3584,
3590
+ 3585,
3591
+ 3586,
3592
+ 3587,
3593
+ 3588,
3594
+ 3589,
3595
+ 3590,
3596
+ 3591,
3597
+ 3592,
3598
+ 3593,
3599
+ 3594,
3600
+ 3595,
3601
+ 3596,
3602
+ 3597,
3603
+ 3598,
3604
+ 3599,
3605
+ 3600,
3606
+ 3601,
3607
+ 3602,
3608
+ 3603,
3609
+ 3604,
3610
+ 3605,
3611
+ 3606,
3612
+ 3607,
3613
+ 3608,
3614
+ 3609,
3615
+ 3610,
3616
+ 3611,
3617
+ 3612,
3618
+ 3613,
3619
+ 3614,
3620
+ 3615,
3621
+ 3616,
3622
+ 3617,
3623
+ 3618,
3624
+ 3619,
3625
+ 3620,
3626
+ 3621,
3627
+ 3622,
3628
+ 3623,
3629
+ 3624,
3630
+ 3625,
3631
+ 3626,
3632
+ 3627,
3633
+ 3628,
3634
+ 3629,
3635
+ 3630,
3636
+ 3631,
3637
+ 3632,
3638
+ 3633,
3639
+ 3634,
3640
+ 3635,
3641
+ 3636,
3642
+ 3637,
3643
+ 3638,
3644
+ 3639,
3645
+ 3640,
3646
+ 3641,
3647
+ 3642,
3648
+ 3643,
3649
+ 3644,
3650
+ 3645,
3651
+ 3646,
3652
+ 3647,
3653
+ 3648,
3654
+ 3649,
3655
+ 3650,
3656
+ 3651,
3657
+ 3652,
3658
+ 3653,
3659
+ 3654,
3660
+ 3655,
3661
+ 3656,
3662
+ 3657,
3663
+ 3658,
3664
+ 3659,
3665
+ 3660,
3666
+ 3661,
3667
+ 3662,
3668
+ 3663,
3669
+ 3664,
3670
+ 3665,
3671
+ 3666,
3672
+ 3667,
3673
+ 3668,
3674
+ 3669,
3675
+ 3670,
3676
+ 3671,
3677
+ 3672,
3678
+ 3673,
3679
+ 3674,
3680
+ 3675,
3681
+ 3676,
3682
+ 3677,
3683
+ 3678,
3684
+ 3679,
3685
+ 3680,
3686
+ 3681,
3687
+ 3682,
3688
+ 3683,
3689
+ 3684,
3690
+ 3685,
3691
+ 3686,
3692
+ 3687,
3693
+ 3688,
3694
+ 3689,
3695
+ 3690,
3696
+ 3691,
3697
+ 3692,
3698
+ 3693,
3699
+ 3694,
3700
+ 3695,
3701
+ 3696,
3702
+ 3697,
3703
+ 3698,
3704
+ 3699,
3705
+ 3700,
3706
+ 3701,
3707
+ 3702,
3708
+ 3703,
3709
+ 3704,
3710
+ 3705,
3711
+ 3706,
3712
+ 3707,
3713
+ 3708,
3714
+ 3709,
3715
+ 3710,
3716
+ 3711,
3717
+ 3712,
3718
+ 3713,
3719
+ 3714,
3720
+ 3715,
3721
+ 3716,
3722
+ 3717,
3723
+ 3718,
3724
+ 3719,
3725
+ 3720,
3726
+ 3721,
3727
+ 3722,
3728
+ 3723,
3729
+ 3724,
3730
+ 3725,
3731
+ 3726,
3732
+ 3727,
3733
+ 3728,
3734
+ 3729,
3735
+ 3730,
3736
+ 3731,
3737
+ 3732,
3738
+ 3733,
3739
+ 3734,
3740
+ 3735,
3741
+ 3736,
3742
+ 3737,
3743
+ 3738,
3744
+ 3739,
3745
+ 3740,
3746
+ 3741,
3747
+ 3742,
3748
+ 3743,
3749
+ 3744,
3750
+ 3745,
3751
+ 3746,
3752
+ 3747,
3753
+ 3748,
3754
+ 3749,
3755
+ 3750,
3756
+ 3751,
3757
+ 3752,
3758
+ 3753,
3759
+ 3754,
3760
+ 3755,
3761
+ 3756,
3762
+ 3757,
3763
+ 3758,
3764
+ 3759,
3765
+ 3760,
3766
+ 3761,
3767
+ 3762,
3768
+ 3763,
3769
+ 3764,
3770
+ 3765,
3771
+ 3766,
3772
+ 3767,
3773
+ 3768,
3774
+ 3769,
3775
+ 3770,
3776
+ 3771,
3777
+ 3772,
3778
+ 3773,
3779
+ 3774,
3780
+ 3775,
3781
+ 3776,
3782
+ 3777,
3783
+ 3778,
3784
+ 3779,
3785
+ 3780,
3786
+ 3781,
3787
+ 3782,
3788
+ 3783,
3789
+ 3784,
3790
+ 3785,
3791
+ 3786,
3792
+ 3787,
3793
+ 3788,
3794
+ 3789,
3795
+ 3790,
3796
+ 3791,
3797
+ 3792,
3798
+ 3793,
3799
+ 3794,
3800
+ 3795,
3801
+ 3796,
3802
+ 3797,
3803
+ 3798,
3804
+ 3799,
3805
+ 3800,
3806
+ 3801,
3807
+ 3802,
3808
+ 3803,
3809
+ 3804,
3810
+ 3805,
3811
+ 3806,
3812
+ 3807,
3813
+ 3808,
3814
+ 3809,
3815
+ 3810,
3816
+ 3811,
3817
+ 3812,
3818
+ 3813,
3819
+ 3814,
3820
+ 3815,
3821
+ 3816,
3822
+ 3817,
3823
+ 3818,
3824
+ 3819,
3825
+ 3820,
3826
+ 3821,
3827
+ 3822,
3828
+ 3823,
3829
+ 3824,
3830
+ 3825,
3831
+ 3826,
3832
+ 3827,
3833
+ 3828,
3834
+ 3829,
3835
+ 3830,
3836
+ 3831,
3837
+ 3832,
3838
+ 3833,
3839
+ 3834,
3840
+ 3835,
3841
+ 3836,
3842
+ 3837,
3843
+ 3838,
3844
+ 3839,
3845
+ 3840,
3846
+ 3841,
3847
+ 3842,
3848
+ 3843,
3849
+ 3844,
3850
+ 3845,
3851
+ 3846,
3852
+ 3847,
3853
+ 3848,
3854
+ 3849,
3855
+ 3850,
3856
+ 3851,
3857
+ 3852,
3858
+ 3853,
3859
+ 3854,
3860
+ 3855,
3861
+ 3856,
3862
+ 3857,
3863
+ 3858,
3864
+ 3859,
3865
+ 3860,
3866
+ 3861,
3867
+ 3862,
3868
+ 3863,
3869
+ 3864,
3870
+ 3865,
3871
+ 3866,
3872
+ 3867,
3873
+ 3868,
3874
+ 3869,
3875
+ 3870,
3876
+ 3871,
3877
+ 3872,
3878
+ 3873,
3879
+ 3874,
3880
+ 3875,
3881
+ 3876,
3882
+ 3877,
3883
+ 3878,
3884
+ 3879,
3885
+ 3880,
3886
+ 3881,
3887
+ 3882,
3888
+ 3883,
3889
+ 3884,
3890
+ 3885,
3891
+ 3886,
3892
+ 3887,
3893
+ 3888,
3894
+ 3889,
3895
+ 3890,
3896
+ 3891,
3897
+ 3892,
3898
+ 3893,
3899
+ 3894,
3900
+ 3895,
3901
+ 3896,
3902
+ 3897,
3903
+ 3898,
3904
+ 3899,
3905
+ 3900,
3906
+ 3901,
3907
+ 3902,
3908
+ 3903,
3909
+ 3904,
3910
+ 3905,
3911
+ 3906,
3912
+ 3907,
3913
+ 3908,
3914
+ 3909,
3915
+ 3910,
3916
+ 3911,
3917
+ 3912,
3918
+ 3913,
3919
+ 3914,
3920
+ 3915,
3921
+ 3916,
3922
+ 3917,
3923
+ 3918,
3924
+ 3919,
3925
+ 3920,
3926
+ 3921,
3927
+ 3922,
3928
+ 3923,
3929
+ 3924,
3930
+ 3925,
3931
+ 3926,
3932
+ 3927,
3933
+ 3928,
3934
+ 3929,
3935
+ 3930,
3936
+ 3931,
3937
+ 3932,
3938
+ 3933,
3939
+ 3934,
3940
+ 3935,
3941
+ 3936,
3942
+ 3937,
3943
+ 3938,
3944
+ 3939,
3945
+ 3940,
3946
+ 3941,
3947
+ 3942,
3948
+ 3943,
3949
+ 3944,
3950
+ 3945,
3951
+ 3946,
3952
+ 3947,
3953
+ 3948,
3954
+ 3949,
3955
+ 3950,
3956
+ 3951,
3957
+ 3952,
3958
+ 3953,
3959
+ 3954,
3960
+ 3955,
3961
+ 3956,
3962
+ 3957,
3963
+ 3958,
3964
+ 3959,
3965
+ 3960,
3966
+ 3961,
3967
+ 3962,
3968
+ 3963,
3969
+ 3964,
3970
+ 3965,
3971
+ 3966,
3972
+ 3967,
3973
+ 3968,
3974
+ 3969,
3975
+ 3970,
3976
+ 3971,
3977
+ 3972,
3978
+ 3973,
3979
+ 3974,
3980
+ 3975,
3981
+ 3976,
3982
+ 3977,
3983
+ 3978,
3984
+ 3979,
3985
+ 3980,
3986
+ 3981,
3987
+ 3982,
3988
+ 3983,
3989
+ 3984,
3990
+ 3985,
3991
+ 3986,
3992
+ 3987,
3993
+ 3988,
3994
+ 3989,
3995
+ 3990,
3996
+ 3991,
3997
+ 3992,
3998
+ 3993,
3999
+ 3994,
4000
+ 3995,
4001
+ 3996,
4002
+ 3997,
4003
+ 3998,
4004
+ 3999,
4005
+ 4000,
4006
+ 4001,
4007
+ 4002,
4008
+ 4003,
4009
+ 4004,
4010
+ 4005,
4011
+ 4006,
4012
+ 4007,
4013
+ 4008,
4014
+ 4009,
4015
+ 4010,
4016
+ 4011,
4017
+ 4012,
4018
+ 4013,
4019
+ 4014,
4020
+ 4015,
4021
+ 4016,
4022
+ 4017,
4023
+ 4018,
4024
+ 4019,
4025
+ 4020,
4026
+ 4021,
4027
+ 4022,
4028
+ 4023,
4029
+ 4024,
4030
+ 4025,
4031
+ 4026,
4032
+ 4027,
4033
+ 4028,
4034
+ 4029,
4035
+ 4030,
4036
+ 4031,
4037
+ 4032,
4038
+ 4033,
4039
+ 4034,
4040
+ 4035,
4041
+ 4036,
4042
+ 4037,
4043
+ 4038,
4044
+ 4039,
4045
+ 4040,
4046
+ 4041,
4047
+ 4042,
4048
+ 4043,
4049
+ 4044,
4050
+ 4045,
4051
+ 4046,
4052
+ 4047,
4053
+ 4048,
4054
+ 4049,
4055
+ 4050,
4056
+ 4051,
4057
+ 4052,
4058
+ 4053,
4059
+ 4054,
4060
+ 4055,
4061
+ 4056,
4062
+ 4057,
4063
+ 4058,
4064
+ 4059,
4065
+ 4060,
4066
+ 4061,
4067
+ 4062,
4068
+ 4063,
4069
+ 4064,
4070
+ 4065,
4071
+ 4066,
4072
+ 4067,
4073
+ 4068,
4074
+ 4069,
4075
+ 4070,
4076
+ 4071,
4077
+ 4072,
4078
+ 4073,
4079
+ 4074,
4080
+ 4075,
4081
+ 4076,
4082
+ 4077,
4083
+ 4078,
4084
+ 4079,
4085
+ 4080,
4086
+ 4081,
4087
+ 4082,
4088
+ 4083,
4089
+ 4084,
4090
+ 4085,
4091
+ 4086,
4092
+ 4087,
4093
+ 4088,
4094
+ 4089,
4095
+ 4090,
4096
+ 4091,
4097
+ 4092,
4098
+ 4093,
4099
+ 4094,
4100
+ 4095,
4101
+ 4096,
4102
+ 4097,
4103
+ 4098,
4104
+ 4099,
4105
+ 4100,
4106
+ 4101,
4107
+ 4102,
4108
+ 4103,
4109
+ 4104,
4110
+ 4105,
4111
+ 4106,
4112
+ 4107,
4113
+ 4108,
4114
+ 4109,
4115
+ 4110,
4116
+ 4111,
4117
+ 4112,
4118
+ 4113,
4119
+ 4114,
4120
+ 4115,
4121
+ 4116,
4122
+ 4117,
4123
+ 4118,
4124
+ 4119,
4125
+ 4120,
4126
+ 4121,
4127
+ 4122,
4128
+ 4123,
4129
+ 4124,
4130
+ 4125,
4131
+ 4126,
4132
+ 4127,
4133
+ 4128,
4134
+ 4129,
4135
+ 4130,
4136
+ 4131,
4137
+ 4132,
4138
+ 4133,
4139
+ 4134,
4140
+ 4135,
4141
+ 4136,
4142
+ 4137,
4143
+ 4138,
4144
+ 4139,
4145
+ 4140,
4146
+ 4141,
4147
+ 4142,
4148
+ 4143,
4149
+ 4144,
4150
+ 4145,
4151
+ 4146,
4152
+ 4147,
4153
+ 4148,
4154
+ 4149,
4155
+ 4150,
4156
+ 4151,
4157
+ 4152,
4158
+ 4153,
4159
+ 4154,
4160
+ 4155,
4161
+ 4156,
4162
+ 4157,
4163
+ 4158,
4164
+ 4159,
4165
+ 4160,
4166
+ 4161,
4167
+ 4162,
4168
+ 4163,
4169
+ 4164,
4170
+ 4165,
4171
+ 4166,
4172
+ 4167,
4173
+ 4168,
4174
+ 4169,
4175
+ 4170,
4176
+ 4171,
4177
+ 4172,
4178
+ 4173,
4179
+ 4174,
4180
+ 4175,
4181
+ 4176,
4182
+ 4177,
4183
+ 4178,
4184
+ 4179,
4185
+ 4180,
4186
+ 4181,
4187
+ 4182,
4188
+ 4183,
4189
+ 4184,
4190
+ 4185,
4191
+ 4186,
4192
+ 4187,
4193
+ 4188,
4194
+ 4189,
4195
+ 4190,
4196
+ 4191,
4197
+ 4192,
4198
+ 4193,
4199
+ 4194,
4200
+ 4195,
4201
+ 4196,
4202
+ 4197,
4203
+ 4198,
4204
+ 4199,
4205
+ 4200,
4206
+ 4201,
4207
+ 4202,
4208
+ 4203,
4209
+ 4204,
4210
+ 4205,
4211
+ 4206,
4212
+ 4207,
4213
+ 4208,
4214
+ 4209,
4215
+ 4210,
4216
+ 4211,
4217
+ 4212,
4218
+ 4213,
4219
+ 4214,
4220
+ 4215,
4221
+ 4216,
4222
+ 4217,
4223
+ 4218,
4224
+ 4219,
4225
+ 4220,
4226
+ 4221,
4227
+ 4222,
4228
+ 4223,
4229
+ 4224,
4230
+ 4225,
4231
+ 4226,
4232
+ 4227,
4233
+ 4228,
4234
+ 4229,
4235
+ 4230,
4236
+ 4231,
4237
+ 4232,
4238
+ 4233,
4239
+ 4234,
4240
+ 4235,
4241
+ 4236,
4242
+ 4237,
4243
+ 4238,
4244
+ 4239,
4245
+ 4240,
4246
+ 4241,
4247
+ 4242,
4248
+ 4243,
4249
+ 4244,
4250
+ 4245,
4251
+ 4246,
4252
+ 4247,
4253
+ 4248,
4254
+ 4249,
4255
+ 4250,
4256
+ 4251,
4257
+ 4252,
4258
+ 4253,
4259
+ 4254,
4260
+ 4255,
4261
+ 4256,
4262
+ 4257,
4263
+ 4258,
4264
+ 4259,
4265
+ 4260,
4266
+ 4261,
4267
+ 4262,
4268
+ 4263,
4269
+ 4264,
4270
+ 4265,
4271
+ 4266,
4272
+ 4267,
4273
+ 4268,
4274
+ 4269,
4275
+ 4270,
4276
+ 4271,
4277
+ 4272,
4278
+ 4273,
4279
+ 4274,
4280
+ 4275,
4281
+ 4276,
4282
+ 4277,
4283
+ 4278,
4284
+ 4279,
4285
+ 4280,
4286
+ 4281,
4287
+ 4282,
4288
+ 4283,
4289
+ 4284,
4290
+ 4285,
4291
+ 4286,
4292
+ 4287,
4293
+ 4288,
4294
+ 4289,
4295
+ 4290,
4296
+ 4291,
4297
+ 4292,
4298
+ 4293,
4299
+ 4294,
4300
+ 4295,
4301
+ 4296,
4302
+ 4297,
4303
+ 4298,
4304
+ 4299,
4305
+ 4300,
4306
+ 4301,
4307
+ 4302,
4308
+ 4303,
4309
+ 4304,
4310
+ 4305,
4311
+ 4306,
4312
+ 4307,
4313
+ 4308,
4314
+ 4309,
4315
+ 4310,
4316
+ 4311,
4317
+ 4312,
4318
+ 4313,
4319
+ 4314,
4320
+ 4315,
4321
+ 4316,
4322
+ 4317,
4323
+ 4318,
4324
+ 4319,
4325
+ 4320,
4326
+ 4321,
4327
+ 4322,
4328
+ 4323,
4329
+ 4324,
4330
+ 4325,
4331
+ 4326,
4332
+ 4327,
4333
+ 4328,
4334
+ 4329,
4335
+ 4330,
4336
+ 4331,
4337
+ 4332,
4338
+ 4333,
4339
+ 4334,
4340
+ 4335,
4341
+ 4336,
4342
+ 4337,
4343
+ 4338,
4344
+ 4339,
4345
+ 4340,
4346
+ 4341,
4347
+ 4342,
4348
+ 4343,
4349
+ 4344,
4350
+ 4345,
4351
+ 4346,
4352
+ 4347,
4353
+ 4348,
4354
+ 4349,
4355
+ 4350,
4356
+ 4351,
4357
+ 4352,
4358
+ 4353,
4359
+ 4354,
4360
+ 4355,
4361
+ 4356,
4362
+ 4357,
4363
+ 4358,
4364
+ 4359,
4365
+ 4360,
4366
+ 4361,
4367
+ 4362,
4368
+ 4363,
4369
+ 4364,
4370
+ 4365,
4371
+ 4366,
4372
+ 4367,
4373
+ 4368,
4374
+ 4369,
4375
+ 4370,
4376
+ 4371,
4377
+ 4372,
4378
+ 4373,
4379
+ 4374,
4380
+ 4375,
4381
+ 4376,
4382
+ 4377,
4383
+ 4378,
4384
+ 4379,
4385
+ 4380,
4386
+ 4381,
4387
+ 4382,
4388
+ 4383,
4389
+ 4384,
4390
+ 4385,
4391
+ 4386,
4392
+ 4387,
4393
+ 4388,
4394
+ 4389,
4395
+ 4390,
4396
+ 4391,
4397
+ 4392,
4398
+ 4393,
4399
+ 4394,
4400
+ 4395,
4401
+ 4396,
4402
+ 4397,
4403
+ 4398,
4404
+ 4399,
4405
+ 4400,
4406
+ 4401,
4407
+ 4402,
4408
+ 4403,
4409
+ 4404,
4410
+ 4405,
4411
+ 4406,
4412
+ 4407,
4413
+ 4408,
4414
+ 4409,
4415
+ 4410,
4416
+ 4411,
4417
+ 4412,
4418
+ 4413,
4419
+ 4414,
4420
+ 4415,
4421
+ 4416,
4422
+ 4417,
4423
+ 4418,
4424
+ 4419,
4425
+ 4420,
4426
+ 4421,
4427
+ 4422,
4428
+ 4423,
4429
+ 4424,
4430
+ 4425,
4431
+ 4426,
4432
+ 4427,
4433
+ 4428,
4434
+ 4429,
4435
+ 4430,
4436
+ 4431,
4437
+ 4432,
4438
+ 4433,
4439
+ 4434,
4440
+ 4435,
4441
+ 4436,
4442
+ 4437,
4443
+ 4438,
4444
+ 4439,
4445
+ 4440,
4446
+ 4441,
4447
+ 4442,
4448
+ 4443,
4449
+ 4444,
4450
+ 4445,
4451
+ 4446,
4452
+ 4447,
4453
+ 4448,
4454
+ 4449,
4455
+ 4450,
4456
+ 4451,
4457
+ 4452,
4458
+ 4453,
4459
+ 4454,
4460
+ 4455,
4461
+ 4456,
4462
+ 4457,
4463
+ 4458,
4464
+ 4459,
4465
+ 4460,
4466
+ 4461,
4467
+ 4462,
4468
+ 4463,
4469
+ 4464,
4470
+ 4465,
4471
+ 4466,
4472
+ 4467,
4473
+ 4468,
4474
+ 4469,
4475
+ 4470,
4476
+ 4471,
4477
+ 4472,
4478
+ 4473,
4479
+ 4474,
4480
+ 4475,
4481
+ 4476,
4482
+ 4477,
4483
+ 4478,
4484
+ 4479,
4485
+ 4480,
4486
+ 4481,
4487
+ 4482,
4488
+ 4483,
4489
+ 4484,
4490
+ 4485,
4491
+ 4486,
4492
+ 4487,
4493
+ 4488,
4494
+ 4489,
4495
+ 4490,
4496
+ 4491,
4497
+ 4492,
4498
+ 4493,
4499
+ 4494,
4500
+ 4495,
4501
+ 4496,
4502
+ 4497,
4503
+ 4498,
4504
+ 4499,
4505
+ 4500,
4506
+ 4501,
4507
+ 4502,
4508
+ 4503,
4509
+ 4504,
4510
+ 4505,
4511
+ 4506,
4512
+ 4507,
4513
+ 4508,
4514
+ 4509,
4515
+ 4510,
4516
+ 4511,
4517
+ 4512,
4518
+ 4513,
4519
+ 4514,
4520
+ 4515,
4521
+ 4516,
4522
+ 4517,
4523
+ 4518,
4524
+ 4519,
4525
+ 4520,
4526
+ 4521,
4527
+ 4522,
4528
+ 4523,
4529
+ 4524,
4530
+ 4525,
4531
+ 4526,
4532
+ 4527,
4533
+ 4528,
4534
+ 4529,
4535
+ 4530,
4536
+ 4531,
4537
+ 4532,
4538
+ 4533,
4539
+ 4534,
4540
+ 4535,
4541
+ 4536,
4542
+ 4537,
4543
+ 4538,
4544
+ 4539,
4545
+ 4540,
4546
+ 4541,
4547
+ 4542,
4548
+ 4543,
4549
+ 4544,
4550
+ 4545,
4551
+ 4546,
4552
+ 4547,
4553
+ 4548,
4554
+ 4549,
4555
+ 4550,
4556
+ 4551,
4557
+ 4552,
4558
+ 4553,
4559
+ 4554,
4560
+ 4555,
4561
+ 4556,
4562
+ 4557,
4563
+ 4558,
4564
+ 4559,
4565
+ 4560,
4566
+ 4561,
4567
+ 4562,
4568
+ 4563,
4569
+ 4564,
4570
+ 4565,
4571
+ 4566,
4572
+ 4567,
4573
+ 4568,
4574
+ 4569,
4575
+ 4570,
4576
+ 4571,
4577
+ 4572,
4578
+ 4573,
4579
+ 4574,
4580
+ 4575,
4581
+ 4576,
4582
+ 4577,
4583
+ 4578,
4584
+ 4579,
4585
+ 4580,
4586
+ 4581,
4587
+ 4582,
4588
+ 4583,
4589
+ 4584,
4590
+ 4585,
4591
+ 4586,
4592
+ 4587,
4593
+ 4588,
4594
+ 4589,
4595
+ 4590,
4596
+ 4591,
4597
+ 4592,
4598
+ 4593,
4599
+ 4594,
4600
+ 4595,
4601
+ 4596,
4602
+ 4597,
4603
+ 4598,
4604
+ 4599,
4605
+ 4600,
4606
+ 4601,
4607
+ 4602,
4608
+ 4603,
4609
+ 4604,
4610
+ 4605,
4611
+ 4606,
4612
+ 4607,
4613
+ 4608,
4614
+ 4609,
4615
+ 4610,
4616
+ 4611,
4617
+ 4612,
4618
+ 4613,
4619
+ 4614,
4620
+ 4615,
4621
+ 4616,
4622
+ 4617,
4623
+ 4618,
4624
+ 4619,
4625
+ 4620,
4626
+ 4621,
4627
+ 4622,
4628
+ 4623,
4629
+ 4624,
4630
+ 4625,
4631
+ 4626,
4632
+ 4627,
4633
+ 4628,
4634
+ 4629,
4635
+ 4630,
4636
+ 4631,
4637
+ 4632,
4638
+ 4633,
4639
+ 4634,
4640
+ 4635,
4641
+ 4636,
4642
+ 4637,
4643
+ 4638,
4644
+ 4639,
4645
+ 4640,
4646
+ 4641,
4647
+ 4642,
4648
+ 4643,
4649
+ 4644,
4650
+ 4645,
4651
+ 4646,
4652
+ 4647,
4653
+ 4648,
4654
+ 4649,
4655
+ 4650,
4656
+ 4651,
4657
+ 4652,
4658
+ 4653,
4659
+ 4654,
4660
+ 4655,
4661
+ 4656,
4662
+ 4657,
4663
+ 4658,
4664
+ 4659,
4665
+ 4660,
4666
+ 4661,
4667
+ 4662,
4668
+ 4663,
4669
+ 4664,
4670
+ 4665,
4671
+ 4666,
4672
+ 4667,
4673
+ 4668,
4674
+ 4669,
4675
+ 4670,
4676
+ 4671,
4677
+ 4672,
4678
+ 4673,
4679
+ 4674,
4680
+ 4675,
4681
+ 4676,
4682
+ 4677,
4683
+ 4678,
4684
+ 4679,
4685
+ 4680,
4686
+ 4681,
4687
+ 4682,
4688
+ 4683,
4689
+ 4684,
4690
+ 4685,
4691
+ 4686,
4692
+ 4687,
4693
+ 4688,
4694
+ 4689,
4695
+ 4690,
4696
+ 4691,
4697
+ 4692,
4698
+ 4693,
4699
+ 4694,
4700
+ 4695,
4701
+ 4696,
4702
+ 4697,
4703
+ 4698,
4704
+ 4699,
4705
+ 4700,
4706
+ 4701,
4707
+ 4702,
4708
+ 4703,
4709
+ 4704,
4710
+ 4705,
4711
+ 4706,
4712
+ 4707,
4713
+ 4708,
4714
+ 4709,
4715
+ 4710,
4716
+ 4711,
4717
+ 4712,
4718
+ 4713,
4719
+ 4714,
4720
+ 4715,
4721
+ 4716,
4722
+ 4717,
4723
+ 4718,
4724
+ 4719,
4725
+ 4720,
4726
+ 4721,
4727
+ 4722,
4728
+ 4723,
4729
+ 4724,
4730
+ 4725,
4731
+ 4726,
4732
+ 4727,
4733
+ 4728,
4734
+ 4729,
4735
+ 4730,
4736
+ 4731,
4737
+ 4732,
4738
+ 4733,
4739
+ 4734,
4740
+ 4735,
4741
+ 4736,
4742
+ 4737,
4743
+ 4738,
4744
+ 4739,
4745
+ 4740,
4746
+ 4741,
4747
+ 4742,
4748
+ 4743,
4749
+ 4744,
4750
+ 4745,
4751
+ 4746,
4752
+ 4747,
4753
+ 4748,
4754
+ 4749,
4755
+ 4750,
4756
+ 4751,
4757
+ 4752,
4758
+ 4753,
4759
+ 4754,
4760
+ 4755,
4761
+ 4756,
4762
+ 4757,
4763
+ 4758,
4764
+ 4759,
4765
+ 4760,
4766
+ 4761,
4767
+ 4762,
4768
+ 4763,
4769
+ 4764,
4770
+ 4765,
4771
+ 4766,
4772
+ 4767,
4773
+ 4768,
4774
+ 4769,
4775
+ 4770,
4776
+ 4771,
4777
+ 4772,
4778
+ 4773,
4779
+ 4774,
4780
+ 4775,
4781
+ 4776,
4782
+ 4777,
4783
+ 4778,
4784
+ 4779,
4785
+ 4780,
4786
+ 4781,
4787
+ 4782,
4788
+ 4783,
4789
+ 4784,
4790
+ 4785,
4791
+ 4786,
4792
+ 4787,
4793
+ 4788,
4794
+ 4789,
4795
+ 4790,
4796
+ 4791,
4797
+ 4792,
4798
+ 4793,
4799
+ 4794,
4800
+ 4795,
4801
+ 4796,
4802
+ 4797,
4803
+ 4798,
4804
+ 4799,
4805
+ 4800,
4806
+ 4801,
4807
+ 4802,
4808
+ 4803,
4809
+ 4804,
4810
+ 4805,
4811
+ 4806,
4812
+ 4807,
4813
+ 4808,
4814
+ 4809,
4815
+ 4810,
4816
+ 4811,
4817
+ 4812,
4818
+ 4813,
4819
+ 4814,
4820
+ 4815,
4821
+ 4816,
4822
+ 4817,
4823
+ 4818,
4824
+ 4819,
4825
+ 4820,
4826
+ 4821,
4827
+ 4822,
4828
+ 4823,
4829
+ 4824,
4830
+ 4825,
4831
+ 4826,
4832
+ 4827,
4833
+ 4828,
4834
+ 4829,
4835
+ 4830,
4836
+ 4831,
4837
+ 4832,
4838
+ 4833,
4839
+ 4834,
4840
+ 4835,
4841
+ 4836,
4842
+ 4837,
4843
+ 4838,
4844
+ 4839,
4845
+ 4840,
4846
+ 4841,
4847
+ 4842,
4848
+ 4843,
4849
+ 4844,
4850
+ 4845,
4851
+ 4846,
4852
+ 4847,
4853
+ 4848,
4854
+ 4849,
4855
+ 4850,
4856
+ 4851,
4857
+ 4852,
4858
+ 4853,
4859
+ 4854,
4860
+ 4855,
4861
+ 4856,
4862
+ 4857,
4863
+ 4858,
4864
+ 4859,
4865
+ 4860,
4866
+ 4861,
4867
+ 4862,
4868
+ 4863,
4869
+ 4864,
4870
+ 4865,
4871
+ 4866,
4872
+ 4867,
4873
+ 4868,
4874
+ 4869,
4875
+ 4870,
4876
+ 4871,
4877
+ 4872,
4878
+ 4873,
4879
+ 4874,
4880
+ 4875,
4881
+ 4876,
4882
+ 4877,
4883
+ 4878,
4884
+ 4879,
4885
+ 4880,
4886
+ 4881,
4887
+ 4882,
4888
+ 4883,
4889
+ 4884,
4890
+ 4885,
4891
+ 4886,
4892
+ 4887,
4893
+ 4888,
4894
+ 4889,
4895
+ 4890,
4896
+ 4891,
4897
+ 4892,
4898
+ 4893,
4899
+ 4894,
4900
+ 4895,
4901
+ 4896,
4902
+ 4897,
4903
+ 4898,
4904
+ 4899,
4905
+ 4900,
4906
+ 4901,
4907
+ 4902,
4908
+ 4903,
4909
+ 4904,
4910
+ 4905,
4911
+ 4906,
4912
+ 4907,
4913
+ 4908,
4914
+ 4909,
4915
+ 4910,
4916
+ 4911,
4917
+ 4912,
4918
+ 4913,
4919
+ 4914,
4920
+ 4915,
4921
+ 4916,
4922
+ 4917,
4923
+ 4918,
4924
+ 4919,
4925
+ 4920,
4926
+ 4921,
4927
+ 4922,
4928
+ 4923,
4929
+ 4924,
4930
+ 4925,
4931
+ 4926,
4932
+ 4927,
4933
+ 4928,
4934
+ 4929,
4935
+ 4930,
4936
+ 4931,
4937
+ 4932,
4938
+ 4933,
4939
+ 4934,
4940
+ 4935,
4941
+ 4936,
4942
+ 4937,
4943
+ 4938,
4944
+ 4939,
4945
+ 4940,
4946
+ 4941,
4947
+ 4942,
4948
+ 4943,
4949
+ 4944,
4950
+ 4945,
4951
+ 4946,
4952
+ 4947,
4953
+ 4948,
4954
+ 4949,
4955
+ 4950,
4956
+ 4951,
4957
+ 4952,
4958
+ 4953,
4959
+ 4954,
4960
+ 4955,
4961
+ 4956,
4962
+ 4957,
4963
+ 4958,
4964
+ 4959,
4965
+ 4960,
4966
+ 4961,
4967
+ 4962,
4968
+ 4963,
4969
+ 4964,
4970
+ 4965,
4971
+ 4966,
4972
+ 4967,
4973
+ 4968,
4974
+ 4969,
4975
+ 4970,
4976
+ 4971,
4977
+ 4972,
4978
+ 4973,
4979
+ 4974,
4980
+ 4975,
4981
+ 4976,
4982
+ 4977,
4983
+ 4978,
4984
+ 4979,
4985
+ 4980,
4986
+ 4981,
4987
+ 4982,
4988
+ 4983,
4989
+ 4984,
4990
+ 4985,
4991
+ 4986,
4992
+ 4987,
4993
+ 4988,
4994
+ 4989,
4995
+ 4990,
4996
+ 4991,
4997
+ 4992,
4998
+ 4993,
4999
+ 4994,
5000
+ 4995,
5001
+ 4996,
5002
+ 4997,
5003
+ 4998,
5004
+ 4999,
5005
+ 5000,
5006
+ 5001,
5007
+ 5002,
5008
+ 5003,
5009
+ 5004,
5010
+ 5005,
5011
+ 5006,
5012
+ 5007,
5013
+ 5008,
5014
+ 5009,
5015
+ 5010,
5016
+ 5011,
5017
+ 5012,
5018
+ 5013,
5019
+ 5014,
5020
+ 5015,
5021
+ 5016,
5022
+ 5017,
5023
+ 5018,
5024
+ 5019,
5025
+ 5020,
5026
+ 5021,
5027
+ 5022,
5028
+ 5023,
5029
+ 5024,
5030
+ 5025,
5031
+ 5026,
5032
+ 5027,
5033
+ 5028,
5034
+ 5029,
5035
+ 5030,
5036
+ 5031,
5037
+ 5032,
5038
+ 5033,
5039
+ 5034,
5040
+ 5035,
5041
+ 5036,
5042
+ 5037,
5043
+ 5038,
5044
+ 5039,
5045
+ 5040,
5046
+ 5041,
5047
+ 5042,
5048
+ 5043,
5049
+ 5044,
5050
+ 5045,
5051
+ 5046,
5052
+ 5047,
5053
+ 5048,
5054
+ 5049,
5055
+ 5050,
5056
+ 5051,
5057
+ 5052,
5058
+ 5053,
5059
+ 5054,
5060
+ 5055,
5061
+ 5056,
5062
+ 5057,
5063
+ 5058,
5064
+ 5059,
5065
+ 5060,
5066
+ 5061,
5067
+ 5062,
5068
+ 5063,
5069
+ 5064,
5070
+ 5065,
5071
+ 5066,
5072
+ 5067,
5073
+ 5068,
5074
+ 5069,
5075
+ 5070,
5076
+ 5071,
5077
+ 5072,
5078
+ 5073,
5079
+ 5074,
5080
+ 5075,
5081
+ 5076,
5082
+ 5077,
5083
+ 5078,
5084
+ 5079,
5085
+ 5080,
5086
+ 5081,
5087
+ 5082,
5088
+ 5083,
5089
+ 5084,
5090
+ 5085,
5091
+ 5086,
5092
+ 5087,
5093
+ 5088,
5094
+ 5089,
5095
+ 5090,
5096
+ 5091,
5097
+ 5092,
5098
+ 5093,
5099
+ 5094,
5100
+ 5095,
5101
+ 5096,
5102
+ 5097,
5103
+ 5098,
5104
+ 5099,
5105
+ 5100,
5106
+ 5101,
5107
+ 5102,
5108
+ 5103,
5109
+ 5104,
5110
+ 5105,
5111
+ 5106,
5112
+ 5107,
5113
+ 5108,
5114
+ 5109,
5115
+ 5110,
5116
+ 5111,
5117
+ 5112,
5118
+ 5113,
5119
+ 5114,
5120
+ 5115,
5121
+ 5116,
5122
+ 5117,
5123
+ 5118,
5124
+ 5119,
5125
+ 5120,
5126
+ 5121,
5127
+ 5122,
5128
+ 5123,
5129
+ 5124,
5130
+ 5125,
5131
+ 5126,
5132
+ 5127,
5133
+ 5128,
5134
+ 5129,
5135
+ 5130,
5136
+ 5131,
5137
+ 5132,
5138
+ 5133,
5139
+ 5134,
5140
+ 5135,
5141
+ 5136,
5142
+ 5137,
5143
+ 5138,
5144
+ 5139,
5145
+ 5140,
5146
+ 5141,
5147
+ 5142,
5148
+ 5143,
5149
+ 5144,
5150
+ 5145,
5151
+ 5146,
5152
+ 5147,
5153
+ 5148,
5154
+ 5149,
5155
+ 5150,
5156
+ 5151,
5157
+ 5152,
5158
+ 5153,
5159
+ 5154,
5160
+ 5155,
5161
+ 5156,
5162
+ 5157,
5163
+ 5158,
5164
+ 5159,
5165
+ 5160,
5166
+ 5161,
5167
+ 5162,
5168
+ 5163,
5169
+ 5164,
5170
+ 5165,
5171
+ 5166,
5172
+ 5167,
5173
+ 5168,
5174
+ 5169,
5175
+ 5170,
5176
+ 5171,
5177
+ 5172,
5178
+ 5173,
5179
+ 5174,
5180
+ 5175,
5181
+ 5176,
5182
+ 5177,
5183
+ 5178,
5184
+ 5179,
5185
+ 5180,
5186
+ 5181,
5187
+ 5182,
5188
+ 5183,
5189
+ 5184,
5190
+ 5185,
5191
+ 5186,
5192
+ 5187,
5193
+ 5188,
5194
+ 5189,
5195
+ 5190,
5196
+ 5191,
5197
+ 5192,
5198
+ 5193,
5199
+ 5194,
5200
+ 5195,
5201
+ 5196,
5202
+ 5197,
5203
+ 5198,
5204
+ 5199,
5205
+ 5200,
5206
+ 5201,
5207
+ 5202,
5208
+ 5203,
5209
+ 5204,
5210
+ 5205,
5211
+ 5206,
5212
+ 5207,
5213
+ 5208,
5214
+ 5209,
5215
+ 5210,
5216
+ 5211,
5217
+ 5212,
5218
+ 5213,
5219
+ 5214,
5220
+ 5215,
5221
+ 5216,
5222
+ 5217,
5223
+ 5218,
5224
+ 5219,
5225
+ 5220,
5226
+ 5221,
5227
+ 5222,
5228
+ 5223,
5229
+ 5224,
5230
+ 5225,
5231
+ 5226,
5232
+ 5227,
5233
+ 5228,
5234
+ 5229,
5235
+ 5230,
5236
+ 5231,
5237
+ 5232,
5238
+ 5233,
5239
+ 5234,
5240
+ 5235,
5241
+ 5236,
5242
+ 5237,
5243
+ 5238,
5244
+ 5239,
5245
+ 5240,
5246
+ 5241,
5247
+ 5242,
5248
+ 5243,
5249
+ 5244,
5250
+ 5245,
5251
+ 5246,
5252
+ 5247,
5253
+ 5248,
5254
+ 5249,
5255
+ 5250,
5256
+ 5251,
5257
+ 5252,
5258
+ 5253,
5259
+ 5254,
5260
+ 5255,
5261
+ 5256,
5262
+ 5257,
5263
+ 5258,
5264
+ 5259,
5265
+ 5260,
5266
+ 5261,
5267
+ 5262,
5268
+ 5263,
5269
+ 5264,
5270
+ 5265,
5271
+ 5266,
5272
+ 5267,
5273
+ 5268,
5274
+ 5269,
5275
+ 5270,
5276
+ 5271,
5277
+ 5272,
5278
+ 5273,
5279
+ 5274,
5280
+ 5275,
5281
+ 5276,
5282
+ 5277,
5283
+ 5278,
5284
+ 5279,
5285
+ 5280,
5286
+ 5281,
5287
+ 5282,
5288
+ 5283,
5289
+ 5284,
5290
+ 5285,
5291
+ 5286,
5292
+ 5287,
5293
+ 5288,
5294
+ 5289,
5295
+ 5290,
5296
+ 5291,
5297
+ 5292,
5298
+ 5293,
5299
+ 5294,
5300
+ 5295,
5301
+ 5296,
5302
+ 5297,
5303
+ 5298,
5304
+ 5299,
5305
+ 5300,
5306
+ 5301,
5307
+ 5302,
5308
+ 5303,
5309
+ 5304,
5310
+ 5305,
5311
+ 5306,
5312
+ 5307,
5313
+ 5308,
5314
+ 5309,
5315
+ 5310,
5316
+ 5311,
5317
+ 5312,
5318
+ 5313,
5319
+ 5314,
5320
+ 5315,
5321
+ 5316,
5322
+ 5317,
5323
+ 5318,
5324
+ 5319,
5325
+ 5320,
5326
+ 5321,
5327
+ 5322,
5328
+ 5323,
5329
+ 5324,
5330
+ 5325,
5331
+ 5326,
5332
+ 5327,
5333
+ 5328,
5334
+ 5329,
5335
+ 5330,
5336
+ 5331,
5337
+ 5332,
5338
+ 5333,
5339
+ 5334,
5340
+ 5335,
5341
+ 5336,
5342
+ 5337,
5343
+ 5338,
5344
+ 5339,
5345
+ 5340,
5346
+ 5341,
5347
+ 5342,
5348
+ 5343,
5349
+ 5344,
5350
+ 5345,
5351
+ 5346,
5352
+ 5347,
5353
+ 5348,
5354
+ 5349,
5355
+ 5350,
5356
+ 5351,
5357
+ 5352,
5358
+ 5353,
5359
+ 5354,
5360
+ 5355,
5361
+ 5356,
5362
+ 5357,
5363
+ 5358,
5364
+ 5359,
5365
+ 5360,
5366
+ 5361,
5367
+ 5362,
5368
+ 5363,
5369
+ 5364,
5370
+ 5365,
5371
+ 5366,
5372
+ 5367,
5373
+ 5368,
5374
+ 5369,
5375
+ 5370,
5376
+ 5371,
5377
+ 5372,
5378
+ 5373,
5379
+ 5374,
5380
+ 5375,
5381
+ 5376,
5382
+ 5377,
5383
+ 5378,
5384
+ 5379,
5385
+ 5380,
5386
+ 5381,
5387
+ 5382,
5388
+ 5383,
5389
+ 5384,
5390
+ 5385,
5391
+ 5386,
5392
+ 5387,
5393
+ 5388,
5394
+ 5389,
5395
+ 5390,
5396
+ 5391,
5397
+ 5392,
5398
+ 5393,
5399
+ 5394,
5400
+ 5395,
5401
+ 5396,
5402
+ 5397,
5403
+ 5398,
5404
+ 5399,
5405
+ 5400,
5406
+ 5401,
5407
+ 5402,
5408
+ 5403,
5409
+ 5404,
5410
+ 5405,
5411
+ 5406,
5412
+ 5407,
5413
+ 5408,
5414
+ 5409,
5415
+ 5410,
5416
+ 5411,
5417
+ 5412,
5418
+ 5413,
5419
+ 5414,
5420
+ 5415,
5421
+ 5416,
5422
+ 5417,
5423
+ 5418,
5424
+ 5419,
5425
+ 5420,
5426
+ 5421,
5427
+ 5422,
5428
+ 5423,
5429
+ 5424,
5430
+ 5425,
5431
+ 5426,
5432
+ 5427,
5433
+ 5428,
5434
+ 5429,
5435
+ 5430,
5436
+ 5431,
5437
+ 5432,
5438
+ 5433,
5439
+ 5434,
5440
+ 5435,
5441
+ 5436,
5442
+ 5437,
5443
+ 5438,
5444
+ 5439,
5445
+ 5440,
5446
+ 5441,
5447
+ 5442,
5448
+ 5443,
5449
+ 5444,
5450
+ 5445,
5451
+ 5446,
5452
+ 5447,
5453
+ 5448,
5454
+ 5449,
5455
+ 5450,
5456
+ 5451,
5457
+ 5452,
5458
+ 5453,
5459
+ 5454,
5460
+ 5455,
5461
+ 5456,
5462
+ 5457,
5463
+ 5458,
5464
+ 5459,
5465
+ 5460,
5466
+ 5461,
5467
+ 5462,
5468
+ 5463,
5469
+ 5464,
5470
+ 5465,
5471
+ 5466,
5472
+ 5467,
5473
+ 5468,
5474
+ 5469,
5475
+ 5470,
5476
+ 5471,
5477
+ 5472,
5478
+ 5473,
5479
+ 5474,
5480
+ 5475,
5481
+ 5476,
5482
+ 5477,
5483
+ 5478,
5484
+ 5479,
5485
+ 5480,
5486
+ 5481,
5487
+ 5482,
5488
+ 5483,
5489
+ 5484,
5490
+ 5485,
5491
+ 5486,
5492
+ 5487,
5493
+ 5488,
5494
+ 5489,
5495
+ 5490,
5496
+ 5491,
5497
+ 5492,
5498
+ 5493,
5499
+ 5494,
5500
+ 5495,
5501
+ 5496,
5502
+ 5497,
5503
+ 5498,
5504
+ 5499,
5505
+ 5500,
5506
+ 5501,
5507
+ 5502,
5508
+ 5503,
5509
+ 5504,
5510
+ 5505,
5511
+ 5506,
5512
+ 5507,
5513
+ 5508,
5514
+ 5509,
5515
+ 5510,
5516
+ 5511,
5517
+ 5512,
5518
+ 5513,
5519
+ 5514,
5520
+ 5515,
5521
+ 5516,
5522
+ 5517,
5523
+ 5518,
5524
+ 5519,
5525
+ 5520,
5526
+ 5521,
5527
+ 5522,
5528
+ 5523,
5529
+ 5524,
5530
+ 5525,
5531
+ 5526,
5532
+ 5527,
5533
+ 5528,
5534
+ 5529,
5535
+ 5530,
5536
+ 5531,
5537
+ 5532,
5538
+ 5533,
5539
+ 5534,
5540
+ 5535,
5541
+ 5536,
5542
+ 5537,
5543
+ 5538,
5544
+ 5539,
5545
+ 5540,
5546
+ 5541,
5547
+ 5542,
5548
+ 5543,
5549
+ 5544,
5550
+ 5545,
5551
+ 5546,
5552
+ 5547,
5553
+ 5548,
5554
+ 5549,
5555
+ 5550,
5556
+ 5551,
5557
+ 5552,
5558
+ 5553,
5559
+ 5554,
5560
+ 5555,
5561
+ 5556,
5562
+ 5557,
5563
+ 5558,
5564
+ 5559,
5565
+ 5560,
5566
+ 5561,
5567
+ 5562,
5568
+ 5563,
5569
+ 5564,
5570
+ 5565,
5571
+ 5566,
5572
+ 5567,
5573
+ 5568,
5574
+ 5569,
5575
+ 5570,
5576
+ 5571,
5577
+ 5572,
5578
+ 5573,
5579
+ 5574,
5580
+ 5575,
5581
+ 5576,
5582
+ 5577,
5583
+ 5578,
5584
+ 5579,
5585
+ 5580,
5586
+ 5581,
5587
+ 5582,
5588
+ 5583,
5589
+ 5584,
5590
+ 5585,
5591
+ 5586,
5592
+ 5587,
5593
+ 5588,
5594
+ 5589,
5595
+ 5590,
5596
+ 5591,
5597
+ 5592,
5598
+ 5593,
5599
+ 5594,
5600
+ 5595,
5601
+ 5596,
5602
+ 5597,
5603
+ 5598,
5604
+ 5599,
5605
+ 5600,
5606
+ 5601,
5607
+ 5602,
5608
+ 5603,
5609
+ 5604,
5610
+ 5605,
5611
+ 5606,
5612
+ 5607,
5613
+ 5608,
5614
+ 5609,
5615
+ 5610,
5616
+ 5611,
5617
+ 5612,
5618
+ 5613,
5619
+ 5614,
5620
+ 5615,
5621
+ 5616,
5622
+ 5617,
5623
+ 5618,
5624
+ 5619,
5625
+ 5620,
5626
+ 5621,
5627
+ 5622,
5628
+ 5623,
5629
+ 5624,
5630
+ 5625,
5631
+ 5626,
5632
+ 5627,
5633
+ 5628,
5634
+ 5629,
5635
+ 5630,
5636
+ 5631,
5637
+ 5632,
5638
+ 5633,
5639
+ 5634,
5640
+ 5635,
5641
+ 5636,
5642
+ 5637,
5643
+ 5638,
5644
+ 5639,
5645
+ 5640,
5646
+ 5641,
5647
+ 5642,
5648
+ 5643,
5649
+ 5644,
5650
+ 5645,
5651
+ 5646,
5652
+ 5647,
5653
+ 5648,
5654
+ 5649,
5655
+ 5650,
5656
+ 5651,
5657
+ 5652,
5658
+ 5653,
5659
+ 5654,
5660
+ 5655,
5661
+ 5656,
5662
+ 5657,
5663
+ 5658,
5664
+ 5659,
5665
+ 5660,
5666
+ 5661,
5667
+ 5662,
5668
+ 5663,
5669
+ 5664,
5670
+ 5665,
5671
+ 5666,
5672
+ 5667,
5673
+ 5668,
5674
+ 5669,
5675
+ 5670,
5676
+ 5671,
5677
+ 5672,
5678
+ 5673,
5679
+ 5674,
5680
+ 5675,
5681
+ 5676,
5682
+ 5677,
5683
+ 5678,
5684
+ 5679,
5685
+ 5680,
5686
+ 5681,
5687
+ 5682,
5688
+ 5683,
5689
+ 5684,
5690
+ 5685,
5691
+ 5686,
5692
+ 5687,
5693
+ 5688,
5694
+ 5689,
5695
+ 5690,
5696
+ 5691,
5697
+ 5692,
5698
+ 5693,
5699
+ 5694,
5700
+ 5695,
5701
+ 5696,
5702
+ 5697,
5703
+ 5698,
5704
+ 5699,
5705
+ 5700,
5706
+ 5701,
5707
+ 5702,
5708
+ 5703,
5709
+ 5704,
5710
+ 5705,
5711
+ 5706,
5712
+ 5707,
5713
+ 5708,
5714
+ 5709,
5715
+ 5710,
5716
+ 5711,
5717
+ 5712,
5718
+ 5713,
5719
+ 5714,
5720
+ 5715,
5721
+ 5716,
5722
+ 5717,
5723
+ 5718,
5724
+ 5719,
5725
+ 5720,
5726
+ 5721,
5727
+ 5722,
5728
+ 5723,
5729
+ 5724,
5730
+ 5725,
5731
+ 5726,
5732
+ 5727,
5733
+ 5728,
5734
+ 5729,
5735
+ 5730,
5736
+ 5731,
5737
+ 5732,
5738
+ 5733,
5739
+ 5734,
5740
+ 5735,
5741
+ 5736,
5742
+ 5737,
5743
+ 5738,
5744
+ 5739,
5745
+ 5740,
5746
+ 5741,
5747
+ 5742,
5748
+ 5743,
5749
+ 5744,
5750
+ 5745,
5751
+ 5746,
5752
+ 5747,
5753
+ 5748,
5754
+ 5749,
5755
+ 5750,
5756
+ 5751,
5757
+ 5752,
5758
+ 5753,
5759
+ 5754,
5760
+ 5755,
5761
+ 5756,
5762
+ 5757,
5763
+ 5758,
5764
+ 5759,
5765
+ 5760,
5766
+ 5761,
5767
+ 5762,
5768
+ 5763,
5769
+ 5764,
5770
+ 5765,
5771
+ 5766,
5772
+ 5767,
5773
+ 5768,
5774
+ 5769,
5775
+ 5770,
5776
+ 5771,
5777
+ 5772,
5778
+ 5773,
5779
+ 5774,
5780
+ 5775,
5781
+ 5776,
5782
+ 5777,
5783
+ 5778,
5784
+ 5779,
5785
+ 5780,
5786
+ 5781,
5787
+ 5782,
5788
+ 5783,
5789
+ 5784,
5790
+ 5785,
5791
+ 5786,
5792
+ 5787,
5793
+ 5788,
5794
+ 5789,
5795
+ 5790,
5796
+ 5791,
5797
+ 5792,
5798
+ 5793,
5799
+ 5794,
5800
+ 5795,
5801
+ 5796,
5802
+ 5797,
5803
+ 5798,
5804
+ 5799,
5805
+ 5800,
5806
+ 5801,
5807
+ 5802,
5808
+ 5803,
5809
+ 5804,
5810
+ 5805,
5811
+ 5806,
5812
+ 5807,
5813
+ 5808,
5814
+ 5809,
5815
+ 5810,
5816
+ 5811,
5817
+ 5812,
5818
+ 5813,
5819
+ 5814,
5820
+ 5815,
5821
+ 5816,
5822
+ 5817,
5823
+ 5818,
5824
+ 5819,
5825
+ 5820,
5826
+ 5821,
5827
+ 5822,
5828
+ 5823,
5829
+ 5824,
5830
+ 5825,
5831
+ 5826,
5832
+ 5827,
5833
+ 5828,
5834
+ 5829,
5835
+ 5830,
5836
+ 5831,
5837
+ 5832,
5838
+ 5833,
5839
+ 5834,
5840
+ 5835,
5841
+ 5836,
5842
+ 5837,
5843
+ 5838,
5844
+ 5839,
5845
+ 5840,
5846
+ 5841,
5847
+ 5842,
5848
+ 5843,
5849
+ 5844,
5850
+ 5845,
5851
+ 5846,
5852
+ 5847,
5853
+ 5848,
5854
+ 5849,
5855
+ 5850,
5856
+ 5851,
5857
+ 5852,
5858
+ 5853,
5859
+ 5854,
5860
+ 5855,
5861
+ 5856,
5862
+ 5857,
5863
+ 5858,
5864
+ 5859,
5865
+ 5860,
5866
+ 5861,
5867
+ 5862,
5868
+ 5863,
5869
+ 5864,
5870
+ 5865,
5871
+ 5866,
5872
+ 5867,
5873
+ 5868,
5874
+ 5869,
5875
+ 5870,
5876
+ 5871,
5877
+ 5872,
5878
+ 5873,
5879
+ 5874,
5880
+ 5875,
5881
+ 5876,
5882
+ 5877,
5883
+ 5878,
5884
+ 5879,
5885
+ 5880,
5886
+ 5881,
5887
+ 5882,
5888
+ 5883,
5889
+ 5884,
5890
+ 5885,
5891
+ 5886,
5892
+ 5887,
5893
+ 5888,
5894
+ 5889,
5895
+ 5890,
5896
+ 5891,
5897
+ 5892,
5898
+ 5893,
5899
+ 5894,
5900
+ 5895,
5901
+ 5896,
5902
+ 5897,
5903
+ 5898,
5904
+ 5899,
5905
+ 5900,
5906
+ 5901,
5907
+ 5902,
5908
+ 5903,
5909
+ 5904,
5910
+ 5905,
5911
+ 5906,
5912
+ 5907,
5913
+ 5908,
5914
+ 5909,
5915
+ 5910,
5916
+ 5911,
5917
+ 5912,
5918
+ 5913,
5919
+ 5914,
5920
+ 5915,
5921
+ 5916,
5922
+ 5917,
5923
+ 5918,
5924
+ 5919,
5925
+ 5920,
5926
+ 5921,
5927
+ 5922,
5928
+ 5923,
5929
+ 5924,
5930
+ 5925,
5931
+ 5926,
5932
+ 5927,
5933
+ 5928,
5934
+ 5929,
5935
+ 5930,
5936
+ 5931,
5937
+ 5932,
5938
+ 5933,
5939
+ 5934,
5940
+ 5935,
5941
+ 5936,
5942
+ 5937,
5943
+ 5938,
5944
+ 5939,
5945
+ 5940,
5946
+ 5941,
5947
+ 5942,
5948
+ 5943,
5949
+ 5944,
5950
+ 5945,
5951
+ 5946,
5952
+ 5947,
5953
+ 5948,
5954
+ 5949,
5955
+ 5950,
5956
+ 5951,
5957
+ 5952,
5958
+ 5953,
5959
+ 5954,
5960
+ 5955,
5961
+ 5956,
5962
+ 5957,
5963
+ 5958,
5964
+ 5959,
5965
+ 5960,
5966
+ 5961,
5967
+ 5962,
5968
+ 5963,
5969
+ 5964,
5970
+ 5965,
5971
+ 5966,
5972
+ 5967,
5973
+ 5968,
5974
+ 5969,
5975
+ 5970,
5976
+ 5971,
5977
+ 5972,
5978
+ 5973,
5979
+ 5974,
5980
+ 5975,
5981
+ 5976,
5982
+ 5977,
5983
+ 5978,
5984
+ 5979,
5985
+ 5980,
5986
+ 5981,
5987
+ 5982,
5988
+ 5983,
5989
+ 5984,
5990
+ 5985,
5991
+ 5986,
5992
+ 5987,
5993
+ 5988,
5994
+ 5989,
5995
+ 5990,
5996
+ 5991,
5997
+ 5992,
5998
+ 5993,
5999
+ 5994,
6000
+ 5995,
6001
+ 5996,
6002
+ 5997,
6003
+ 5998,
6004
+ 5999,
6005
+ 6000,
6006
+ 6001,
6007
+ 6002,
6008
+ 6003,
6009
+ 6004,
6010
+ 6005,
6011
+ 6006,
6012
+ 6007,
6013
+ 6008,
6014
+ 6009,
6015
+ 6010,
6016
+ 6011,
6017
+ 6012,
6018
+ 6013,
6019
+ 6014,
6020
+ 6015,
6021
+ 6016,
6022
+ 6017,
6023
+ 6018,
6024
+ 6019,
6025
+ 6020,
6026
+ 6021,
6027
+ 6022,
6028
+ 6023,
6029
+ 6024,
6030
+ 6025,
6031
+ 6026,
6032
+ 6027,
6033
+ 6028,
6034
+ 6029,
6035
+ 6030,
6036
+ 6031,
6037
+ 6032,
6038
+ 6033,
6039
+ 6034,
6040
+ 6035,
6041
+ 6036,
6042
+ 6037,
6043
+ 6038,
6044
+ 6039,
6045
+ 6040,
6046
+ 6041,
6047
+ 6042,
6048
+ 6043,
6049
+ 6044,
6050
+ 6045,
6051
+ 6046,
6052
+ 6047,
6053
+ 6048,
6054
+ 6049,
6055
+ 6050,
6056
+ 6051,
6057
+ 6052,
6058
+ 6053,
6059
+ 6054,
6060
+ 6055,
6061
+ 6056,
6062
+ 6057,
6063
+ 6058,
6064
+ 6059,
6065
+ 6060,
6066
+ 6061,
6067
+ 6062,
6068
+ 6063,
6069
+ 6064,
6070
+ 6065,
6071
+ 6066,
6072
+ 6067,
6073
+ 6068,
6074
+ 6069,
6075
+ 6070,
6076
+ 6071,
6077
+ 6072,
6078
+ 6073,
6079
+ 6074,
6080
+ 6075,
6081
+ 6076,
6082
+ 6077,
6083
+ 6078,
6084
+ 6079,
6085
+ 6080,
6086
+ 6081,
6087
+ 6082,
6088
+ 6083,
6089
+ 6084,
6090
+ 6085,
6091
+ 6086,
6092
+ 6087,
6093
+ 6088,
6094
+ 6089,
6095
+ 6090,
6096
+ 6091,
6097
+ 6092,
6098
+ 6093,
6099
+ 6094,
6100
+ 6095,
6101
+ 6096,
6102
+ 6097,
6103
+ 6098,
6104
+ 6099,
6105
+ 6100,
6106
+ 6101,
6107
+ 6102,
6108
+ 6103,
6109
+ 6104,
6110
+ 6105,
6111
+ 6106,
6112
+ 6107,
6113
+ 6108,
6114
+ 6109,
6115
+ 6110,
6116
+ 6111,
6117
+ 6112,
6118
+ 6113,
6119
+ 6114,
6120
+ 6115,
6121
+ 6116,
6122
+ 6117,
6123
+ 6118,
6124
+ 6119,
6125
+ 6120,
6126
+ 6121,
6127
+ 6122,
6128
+ 6123,
6129
+ 6124,
6130
+ 6125,
6131
+ 6126,
6132
+ 6127,
6133
+ 6128,
6134
+ 6129,
6135
+ 6130,
6136
+ 6131,
6137
+ 6132,
6138
+ 6133,
6139
+ 6134,
6140
+ 6135,
6141
+ 6136,
6142
+ 6137,
6143
+ 6138,
6144
+ 6139,
6145
+ 6140,
6146
+ 6141,
6147
+ 6142,
6148
+ 6143,
6149
+ 6144,
6150
+ 6145,
6151
+ 6146,
6152
+ 6147,
6153
+ 6148,
6154
+ 6149,
6155
+ 6150,
6156
+ 6151,
6157
+ 6152,
6158
+ 6153,
6159
+ 6154,
6160
+ 6155,
6161
+ 6156,
6162
+ 6157,
6163
+ 6158,
6164
+ 6159,
6165
+ 6160,
6166
+ 6161,
6167
+ 6162,
6168
+ 6163,
6169
+ 6164,
6170
+ 6165,
6171
+ 6166,
6172
+ 6167,
6173
+ 6168,
6174
+ 6169,
6175
+ 6170,
6176
+ 6171,
6177
+ 6172,
6178
+ 6173,
6179
+ 6174,
6180
+ 6175,
6181
+ 6176,
6182
+ 6177,
6183
+ 6178,
6184
+ 6179,
6185
+ 6180,
6186
+ 6181,
6187
+ 6182,
6188
+ 6183,
6189
+ 6184,
6190
+ 6185,
6191
+ 6186,
6192
+ 6187,
6193
+ 6188,
6194
+ 6189,
6195
+ 6190,
6196
+ 6191,
6197
+ 6192,
6198
+ 6193,
6199
+ 6194,
6200
+ 6195,
6201
+ 6196,
6202
+ 6197,
6203
+ 6198,
6204
+ 6199,
6205
+ 6200,
6206
+ 6201,
6207
+ 6202,
6208
+ 6203,
6209
+ 6204,
6210
+ 6205,
6211
+ 6206,
6212
+ 6207,
6213
+ 6208,
6214
+ 6209,
6215
+ 6210,
6216
+ 6211,
6217
+ 6212,
6218
+ 6213,
6219
+ 6214,
6220
+ 6215,
6221
+ 6216,
6222
+ 6217,
6223
+ 6218,
6224
+ 6219,
6225
+ 6220,
6226
+ 6221,
6227
+ 6222,
6228
+ 6223,
6229
+ 6224,
6230
+ 6225,
6231
+ 6226,
6232
+ 6227,
6233
+ 6228,
6234
+ 6229,
6235
+ 6230,
6236
+ 6231,
6237
+ 6232,
6238
+ 6233,
6239
+ 6234,
6240
+ 6235,
6241
+ 6236,
6242
+ 6237,
6243
+ 6238,
6244
+ 6239,
6245
+ 6240,
6246
+ 6241,
6247
+ 6242,
6248
+ 6243,
6249
+ 6244,
6250
+ 6245,
6251
+ 6246,
6252
+ 6247,
6253
+ 6248,
6254
+ 6249,
6255
+ 6250,
6256
+ 6251,
6257
+ 6252,
6258
+ 6253,
6259
+ 6254,
6260
+ 6255,
6261
+ 6256,
6262
+ 6257,
6263
+ 6258,
6264
+ 6259,
6265
+ 6260,
6266
+ 6261,
6267
+ 6262,
6268
+ 6263,
6269
+ 6264,
6270
+ 6265,
6271
+ 6266,
6272
+ 6267,
6273
+ 6268,
6274
+ 6269,
6275
+ 6270,
6276
+ 6271,
6277
+ 6272,
6278
+ 6273,
6279
+ 6274,
6280
+ 6275,
6281
+ 6276,
6282
+ 6277,
6283
+ 6278,
6284
+ 6279,
6285
+ 6280,
6286
+ 6281,
6287
+ 6282,
6288
+ 6283,
6289
+ 6284,
6290
+ 6285,
6291
+ 6286,
6292
+ 6287,
6293
+ 6288,
6294
+ 6289,
6295
+ 6290,
6296
+ 6291,
6297
+ 6292,
6298
+ 6293,
6299
+ 6294,
6300
+ 6295,
6301
+ 6296,
6302
+ 6297,
6303
+ 6298,
6304
+ 6299,
6305
+ 6300,
6306
+ 6301,
6307
+ 6302,
6308
+ 6303,
6309
+ 6304,
6310
+ 6305,
6311
+ 6306,
6312
+ 6307,
6313
+ 6308,
6314
+ 6309,
6315
+ 6310,
6316
+ 6311,
6317
+ 6312,
6318
+ 6313,
6319
+ 6314,
6320
+ 6315,
6321
+ 6316,
6322
+ 6317,
6323
+ 6318,
6324
+ 6319,
6325
+ 6320,
6326
+ 6321,
6327
+ 6322,
6328
+ 6323,
6329
+ 6324,
6330
+ 6325,
6331
+ 6326,
6332
+ 6327,
6333
+ 6328,
6334
+ 6329,
6335
+ 6330,
6336
+ 6331,
6337
+ 6332,
6338
+ 6333,
6339
+ 6334,
6340
+ 6335,
6341
+ 6336,
6342
+ 6337,
6343
+ 6338,
6344
+ 6339,
6345
+ 6340,
6346
+ 6341,
6347
+ 6342,
6348
+ 6343,
6349
+ 6344,
6350
+ 6345,
6351
+ 6346,
6352
+ 6347,
6353
+ 6348,
6354
+ 6349,
6355
+ 6350,
6356
+ 6351,
6357
+ 6352,
6358
+ 6353,
6359
+ 6354,
6360
+ 6355,
6361
+ 6356,
6362
+ 6357,
6363
+ 6358,
6364
+ 6359,
6365
+ 6360,
6366
+ 6361,
6367
+ 6362,
6368
+ 6363,
6369
+ 6364,
6370
+ 6365,
6371
+ 6366,
6372
+ 6367,
6373
+ 6368,
6374
+ 6369,
6375
+ 6370,
6376
+ 6371,
6377
+ 6372,
6378
+ 6373,
6379
+ 6374,
6380
+ 6375,
6381
+ 6376,
6382
+ 6377,
6383
+ 6378,
6384
+ 6379,
6385
+ 6380,
6386
+ 6381,
6387
+ 6382,
6388
+ 6383,
6389
+ 6384,
6390
+ 6385,
6391
+ 6386,
6392
+ 6387,
6393
+ 6388,
6394
+ 6389,
6395
+ 6390,
6396
+ 6391,
6397
+ 6392,
6398
+ 6393,
6399
+ 6394,
6400
+ 6395,
6401
+ 6396,
6402
+ 6397,
6403
+ 6398,
6404
+ 6399,
6405
+ 6400,
6406
+ 6401,
6407
+ 6402,
6408
+ 6403,
6409
+ 6404,
6410
+ 6405,
6411
+ 6406,
6412
+ 6407,
6413
+ 6408,
6414
+ 6409,
6415
+ 6410,
6416
+ 6411,
6417
+ 6412,
6418
+ 6413,
6419
+ 6414,
6420
+ 6415,
6421
+ 6416,
6422
+ 6417,
6423
+ 6418,
6424
+ 6419,
6425
+ 6420,
6426
+ 6421,
6427
+ 6422,
6428
+ 6423,
6429
+ 6424,
6430
+ 6425,
6431
+ 6426,
6432
+ 6427,
6433
+ 6428,
6434
+ 6429,
6435
+ 6430,
6436
+ 6431,
6437
+ 6432,
6438
+ 6433,
6439
+ 6434,
6440
+ 6435,
6441
+ 6436,
6442
+ 6437,
6443
+ 6438,
6444
+ 6439,
6445
+ 6440,
6446
+ 6441,
6447
+ 6442,
6448
+ 6443,
6449
+ 6444,
6450
+ 6445,
6451
+ 6446,
6452
+ 6447,
6453
+ 6448,
6454
+ 6449,
6455
+ 6450,
6456
+ 6451,
6457
+ 6452,
6458
+ 6453,
6459
+ 6454,
6460
+ 6455,
6461
+ 6456,
6462
+ 6457,
6463
+ 6458,
6464
+ 6459,
6465
+ 6460,
6466
+ 6461,
6467
+ 6462,
6468
+ 6463,
6469
+ 6464,
6470
+ 6465,
6471
+ 6466,
6472
+ 6467,
6473
+ 6468,
6474
+ 6469,
6475
+ 6470,
6476
+ 6471,
6477
+ 6472,
6478
+ 6473,
6479
+ 6474,
6480
+ 6475,
6481
+ 6476,
6482
+ 6477,
6483
+ 6478,
6484
+ 6479,
6485
+ 6480,
6486
+ 6481,
6487
+ 6482,
6488
+ 6483,
6489
+ 6484,
6490
+ 6485,
6491
+ 6486,
6492
+ 6487,
6493
+ 6488,
6494
+ 6489,
6495
+ 6490,
6496
+ 6491,
6497
+ 6492,
6498
+ 6493,
6499
+ 6494,
6500
+ 6495,
6501
+ 6496,
6502
+ 6497,
6503
+ 6498,
6504
+ 6499,
6505
+ 6500,
6506
+ 6501,
6507
+ 6502,
6508
+ 6503,
6509
+ 6504,
6510
+ 6505,
6511
+ 6506,
6512
+ 6507,
6513
+ 6508,
6514
+ 6509,
6515
+ 6510,
6516
+ 6511,
6517
+ 6512,
6518
+ 6513,
6519
+ 6514,
6520
+ 6515,
6521
+ 6516,
6522
+ 6517,
6523
+ 6518,
6524
+ 6519,
6525
+ 6520,
6526
+ 6521,
6527
+ 6522,
6528
+ 6523,
6529
+ 6524,
6530
+ 6525,
6531
+ 6526,
6532
+ 6527,
6533
+ 6528,
6534
+ 6529,
6535
+ 6530,
6536
+ 6531,
6537
+ 6532,
6538
+ 6533,
6539
+ 6534,
6540
+ 6535,
6541
+ 6536,
6542
+ 6537,
6543
+ 6538,
6544
+ 6539,
6545
+ 6540,
6546
+ 6541,
6547
+ 6542,
6548
+ 6543,
6549
+ 6544,
6550
+ 6545,
6551
+ 6546,
6552
+ 6547,
6553
+ 6548,
6554
+ 6549,
6555
+ 6550,
6556
+ 6551,
6557
+ 6552,
6558
+ 6553,
6559
+ 6554,
6560
+ 6555,
6561
+ 6556,
6562
+ 6557,
6563
+ 6558,
6564
+ 6559,
6565
+ 6560,
6566
+ 6561,
6567
+ 6562,
6568
+ 6563,
6569
+ 6564,
6570
+ 6565,
6571
+ 6566,
6572
+ 6567,
6573
+ 6568,
6574
+ 6569,
6575
+ 6570,
6576
+ 6571,
6577
+ 6572,
6578
+ 6573,
6579
+ 6574,
6580
+ 6575,
6581
+ 6576,
6582
+ 6577,
6583
+ 6578,
6584
+ 6579,
6585
+ 6580,
6586
+ 6581,
6587
+ 6582,
6588
+ 6583,
6589
+ 6584,
6590
+ 6585,
6591
+ 6586,
6592
+ 6587,
6593
+ 6588,
6594
+ 6589,
6595
+ 6590,
6596
+ 6591,
6597
+ 6592,
6598
+ 6593,
6599
+ 6594,
6600
+ 6595,
6601
+ 6596,
6602
+ 6597,
6603
+ 6598,
6604
+ 6599,
6605
+ 6600,
6606
+ 6601,
6607
+ 6602,
6608
+ 6603,
6609
+ 6604,
6610
+ 6605,
6611
+ 6606,
6612
+ 6607,
6613
+ 6608,
6614
+ 6609,
6615
+ 6610,
6616
+ 6611,
6617
+ 6612,
6618
+ 6613,
6619
+ 6614,
6620
+ 6615,
6621
+ 6616,
6622
+ 6617,
6623
+ 6618,
6624
+ 6619,
6625
+ 6620,
6626
+ 6621,
6627
+ 6622,
6628
+ 6623,
6629
+ 6624,
6630
+ 6625,
6631
+ 6626,
6632
+ 6627,
6633
+ 6628,
6634
+ 6629,
6635
+ 6630,
6636
+ 6631,
6637
+ 6632,
6638
+ 6633,
6639
+ 6634,
6640
+ 6635,
6641
+ 6636,
6642
+ 6637,
6643
+ 6638,
6644
+ 6639,
6645
+ 6640,
6646
+ 6641,
6647
+ 6642,
6648
+ 6643,
6649
+ 6644,
6650
+ 6645,
6651
+ 6646,
6652
+ 6647,
6653
+ 6648,
6654
+ 6649,
6655
+ 6650,
6656
+ 6651,
6657
+ 6652,
6658
+ 6653,
6659
+ 6654,
6660
+ 6655,
6661
+ 6656,
6662
+ 6657,
6663
+ 6658,
6664
+ 6659,
6665
+ 6660,
6666
+ 6661,
6667
+ 6662,
6668
+ 6663,
6669
+ 6664,
6670
+ 6665,
6671
+ 6666,
6672
+ 6667,
6673
+ 6668
6674
+ ],
6675
+ "rows": 6669,
6676
+ "failed_pages": 0,
6677
+ "template_match_failures": 0,
6678
+ "detected_lines": 0,
6679
+ "completed_unix": 1785091235.9294703
6680
+ }
benchmark/exact-summary.json ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "handwriting": {
3
+ "count": 6669,
4
+ "wer": 19.0833,
5
+ "cer": 13.6399,
6
+ "s3": 13.7015,
7
+ "essential_error": 14.7567,
8
+ "triple_threat": 14.7656,
9
+ "reference_importance": 790969.366256,
10
+ "error_importance": 108374.845892,
11
+ "essential_words": 312569,
12
+ "essential_errors": 46125
13
+ },
14
+ "printed": {
15
+ "count": 6669,
16
+ "wer": 8.9257,
17
+ "cer": 2.3535,
18
+ "s3": 9.8512,
19
+ "essential_error": 12.0774,
20
+ "triple_threat": 8.1666,
21
+ "reference_importance": 44585.665084,
22
+ "error_importance": 4392.232612,
23
+ "essential_words": 22596,
24
+ "essential_errors": 2729
25
+ },
26
+ "missing_predictions": 0,
27
+ "gate_passed": false
28
+ }
benchmark/pipeline-status.log ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ 2026-07-26T18:55:25+00:00 EXACT_START
2
+ 2026-07-26T18:55:25+00:00 DETECTION_COMPLETE
3
+ 2026-07-26T18:55:25+00:00 HANDWRITING_RECOGNITION_COMPLETE
4
+ 2026-07-26T18:55:53+00:00 PRINTED_COMPLETE
benchmark/printed-summary.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "state": "complete",
3
+ "rows": 6669,
4
+ "failures": 0
5
+ }
benchmark/recognition-summary.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "state": "complete",
3
+ "rows": 6669,
4
+ "completed_unix": 1785091723.890636
5
+ }
benchmark/results.json ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "splits": {
3
+ "handwriting": {
4
+ "count": 6669,
5
+ "wer": 19.0833,
6
+ "cer": 13.6399,
7
+ "s3": 13.7015,
8
+ "essential_error": 14.7567,
9
+ "triple_threat": 14.7656,
10
+ "reference_importance": 790969.366256,
11
+ "error_importance": 108374.845892,
12
+ "essential_words": 312569,
13
+ "essential_errors": 46125
14
+ },
15
+ "printed": {
16
+ "count": 6669,
17
+ "wer": 8.9257,
18
+ "cer": 2.3535,
19
+ "s3": 9.8512,
20
+ "essential_error": 12.0774,
21
+ "triple_threat": 8.1666,
22
+ "reference_importance": 44585.665084,
23
+ "error_importance": 4392.232612,
24
+ "essential_words": 22596,
25
+ "essential_errors": 2729
26
+ }
27
+ },
28
+ "macro_equal_split": {
29
+ "wer": 14.0045,
30
+ "cer": 7.9967,
31
+ "s3": 11.7764,
32
+ "essential_error": 13.417,
33
+ "triple_threat": 11.4661
34
+ },
35
+ "triple_threat_weights": {
36
+ "wer": 0.2,
37
+ "cer": 0.2,
38
+ "s3": 0.6
39
+ },
40
+ "missing_predictions": 0,
41
+ "missing_ids": []
42
+ }
checkpoint/best_accuracy.pdopt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:54028b92c62fa7ae135746d9ad99a9470e844d74701e8c1b88191406bd62fae3
3
+ size 263781332
checkpoint/best_accuracy.pdparams ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e9d8d6030144630173692297ba3f396427f9030099c5d9b0418644825e095053
3
+ size 142364529
checkpoint/best_accuracy.states ADDED
Binary file (365 Bytes). View file
 
checkpoint/config.yml ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Global:
2
+ model_name: PP-OCRv6_medium_rec
3
+ debug: false
4
+ use_gpu: true
5
+ epoch_num: 3
6
+ log_smooth_window: 20
7
+ print_batch_step: 10
8
+ save_model_dir: /workspace/bina02-ppocr/run/train-stage8-optimizer-resume-v2
9
+ save_epoch_step: 1
10
+ eval_batch_step:
11
+ - 0
12
+ - 200
13
+ cal_metric_during_train: false
14
+ pretrained_model: null
15
+ checkpoints: /workspace/bina02-ppocr/run/train-stage8-optimizer-resume-v2/resume-stage4
16
+ save_inference_dir: null
17
+ use_visualdl: false
18
+ infer_img: doc/imgs_words/ch/word_1.jpg
19
+ character_dict_path: /workspace/bina02-ppocr/train-data/manifests/persian_arabic_bina02_dict.txt
20
+ max_text_length: 192
21
+ infer_mode: false
22
+ use_space_char: true
23
+ distributed: false
24
+ d2s_train_image_shape:
25
+ - 3
26
+ - 48
27
+ - 1536
28
+ freeze_batch_norm: true
29
+ trainable_prefixes:
30
+ - backbone.blocks4
31
+ - backbone.blocks5
32
+ - head.ctc_encoder
33
+ - head.ctc_head
34
+ eval_handwriting_rows: 14998
35
+ eval_printed_rows: 8192
36
+ use_amp: false
37
+ stage8_mix_manifest: /workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/MIX_MANIFEST.json
38
+ Optimizer:
39
+ name: Adam
40
+ beta1: 0.9
41
+ beta2: 0.999
42
+ lr:
43
+ name: Cosine
44
+ learning_rate: 3.0e-06
45
+ warmup_epoch: 0
46
+ regularizer:
47
+ name: L2
48
+ factor: 3.0e-05
49
+ Architecture:
50
+ model_type: rec
51
+ algorithm: SVTR_LCNet
52
+ Transform: null
53
+ Backbone:
54
+ name: PPLCNetV4
55
+ model_size: medium
56
+ train_pool_width: 192
57
+ force_pool_width: true
58
+ Head:
59
+ name: MultiHead
60
+ head_list:
61
+ - CTCHead:
62
+ Neck:
63
+ name: lightsvtr
64
+ dims: 192
65
+ depth: 2
66
+ mlp_ratio: 4.0
67
+ local_kernel: 7
68
+ use_guide: false
69
+ Head:
70
+ fc_decay: 1.0e-05
71
+ - NRTRHead:
72
+ nrtr_dim: 512
73
+ max_text_length: 192
74
+ Loss:
75
+ name: MultiLoss
76
+ weight_1: 1.0
77
+ weight_2: 0.0
78
+ loss_config_list:
79
+ - CTCLoss: null
80
+ - NRTRLoss: null
81
+ PostProcess:
82
+ name: CTCLabelDecode
83
+ Metric:
84
+ name: RecMetric
85
+ main_indicator: handwriting_norm_edit_dis
86
+ ignore_space: false
87
+ domain_split_index: 14998
88
+ Train:
89
+ dataset:
90
+ name: LMDBDataSet
91
+ data_dir: /workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train
92
+ ext_op_transform_idx: 1
93
+ transforms:
94
+ - DecodeImage:
95
+ img_mode: BGR
96
+ channel_first: false
97
+ - RecConAug:
98
+ prob: 0.0
99
+ ext_data_num: 0
100
+ image_shape:
101
+ - 48
102
+ - 1536
103
+ - 3
104
+ max_text_length: 192
105
+ - RecAug: null
106
+ - MultiLabelEncode:
107
+ gtc_encode: NRTRLabelEncode
108
+ - RecResizeImg:
109
+ image_shape:
110
+ - 3
111
+ - 48
112
+ - 1536
113
+ padding: true
114
+ - KeepKeys:
115
+ keep_keys:
116
+ - image
117
+ - label_ctc
118
+ - label_gtc
119
+ - length
120
+ - valid_ratio
121
+ loader:
122
+ shuffle: true
123
+ batch_size_per_card: 32
124
+ drop_last: false
125
+ num_workers: 4
126
+ Eval:
127
+ dataset:
128
+ name: LMDBDataSet
129
+ data_dir: /workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/eval
130
+ transforms:
131
+ - DecodeImage:
132
+ img_mode: BGR
133
+ channel_first: false
134
+ - MultiLabelEncode:
135
+ gtc_encode: NRTRLabelEncode
136
+ - RecResizeImg:
137
+ image_shape:
138
+ - 3
139
+ - 48
140
+ - 1536
141
+ padding: true
142
+ - KeepKeys:
143
+ keep_keys:
144
+ - image
145
+ - label_ctc
146
+ - label_gtc
147
+ - length
148
+ - valid_ratio
149
+ loader:
150
+ shuffle: false
151
+ drop_last: false
152
+ batch_size_per_card: 32
153
+ num_workers: 4
154
+ profiler_options: null
checkpoint/eval-gate-state.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "seen": 42,
3
+ "baseline_handwriting": 0.9296666392340331,
4
+ "baseline_printed": 0.8944015643799841,
5
+ "handwriting_latest": 0.9671727329321572,
6
+ "handwriting_best": 0.9671727329321572,
7
+ "handwriting_bad": 0,
8
+ "handwriting_margin": 0.002,
9
+ "printed_latest": 0.9071053593763533,
10
+ "printed_best": 0.9071053593763533,
11
+ "printed_floor": 0.8924015643799841,
12
+ "printed_bad": 0,
13
+ "printed_margin": 0.002,
14
+ "patience": 2,
15
+ "state": "monitoring"
16
+ }
checkpoint/launch-config.yml ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Global:
2
+ model_name: PP-OCRv6_medium_rec
3
+ debug: false
4
+ use_gpu: true
5
+ epoch_num: 3
6
+ log_smooth_window: 20
7
+ print_batch_step: 10
8
+ save_model_dir: /workspace/bina02-ppocr/run/train-stage8-optimizer-resume-v2
9
+ save_epoch_step: 1
10
+ eval_batch_step:
11
+ - 0
12
+ - 200
13
+ cal_metric_during_train: false
14
+ pretrained_model: null
15
+ checkpoints: /workspace/bina02-ppocr/run/train-stage8-optimizer-resume-v2/resume-stage4
16
+ save_inference_dir: null
17
+ use_visualdl: false
18
+ infer_img: doc/imgs_words/ch/word_1.jpg
19
+ character_dict_path: /workspace/bina02-ppocr/train-data/manifests/persian_arabic_bina02_dict.txt
20
+ max_text_length: 192
21
+ infer_mode: false
22
+ use_space_char: true
23
+ distributed: false
24
+ d2s_train_image_shape:
25
+ - 3
26
+ - 48
27
+ - 1536
28
+ freeze_batch_norm: true
29
+ trainable_prefixes:
30
+ - backbone.blocks4
31
+ - backbone.blocks5
32
+ - head.ctc_encoder
33
+ - head.ctc_head
34
+ eval_handwriting_rows: 14998
35
+ eval_printed_rows: 8192
36
+ use_amp: false
37
+ stage8_mix_manifest: /workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/MIX_MANIFEST.json
38
+ Optimizer:
39
+ name: Adam
40
+ beta1: 0.9
41
+ beta2: 0.999
42
+ lr:
43
+ name: Cosine
44
+ learning_rate: 3.0e-06
45
+ warmup_epoch: 0
46
+ regularizer:
47
+ name: L2
48
+ factor: 3.0e-05
49
+ Architecture:
50
+ model_type: rec
51
+ algorithm: SVTR_LCNet
52
+ Transform: null
53
+ Backbone:
54
+ name: PPLCNetV4
55
+ model_size: medium
56
+ train_pool_width: 192
57
+ force_pool_width: true
58
+ Head:
59
+ name: MultiHead
60
+ head_list:
61
+ - CTCHead:
62
+ Neck:
63
+ name: lightsvtr
64
+ dims: 192
65
+ depth: 2
66
+ mlp_ratio: 4.0
67
+ local_kernel: 7
68
+ use_guide: false
69
+ Head:
70
+ fc_decay: 1.0e-05
71
+ - NRTRHead:
72
+ nrtr_dim: 512
73
+ max_text_length: 192
74
+ Loss:
75
+ name: MultiLoss
76
+ weight_1: 1.0
77
+ weight_2: 0.0
78
+ loss_config_list:
79
+ - CTCLoss: null
80
+ - NRTRLoss: null
81
+ PostProcess:
82
+ name: CTCLabelDecode
83
+ Metric:
84
+ name: RecMetric
85
+ main_indicator: handwriting_norm_edit_dis
86
+ ignore_space: false
87
+ domain_split_index: 14998
88
+ Train:
89
+ dataset:
90
+ name: LMDBDataSet
91
+ data_dir: /workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train
92
+ ext_op_transform_idx: 1
93
+ transforms:
94
+ - DecodeImage:
95
+ img_mode: BGR
96
+ channel_first: false
97
+ - RecConAug:
98
+ prob: 0.0
99
+ ext_data_num: 0
100
+ image_shape:
101
+ - 48
102
+ - 1536
103
+ - 3
104
+ max_text_length: 192
105
+ - RecAug: null
106
+ - MultiLabelEncode:
107
+ gtc_encode: NRTRLabelEncode
108
+ - RecResizeImg:
109
+ image_shape:
110
+ - 3
111
+ - 48
112
+ - 1536
113
+ padding: true
114
+ - KeepKeys:
115
+ keep_keys:
116
+ - image
117
+ - label_ctc
118
+ - label_gtc
119
+ - length
120
+ - valid_ratio
121
+ loader:
122
+ shuffle: true
123
+ batch_size_per_card: 32
124
+ drop_last: false
125
+ num_workers: 4
126
+ Eval:
127
+ dataset:
128
+ name: LMDBDataSet
129
+ data_dir: /workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/eval
130
+ transforms:
131
+ - DecodeImage:
132
+ img_mode: BGR
133
+ channel_first: false
134
+ - MultiLabelEncode:
135
+ gtc_encode: NRTRLabelEncode
136
+ - RecResizeImg:
137
+ image_shape:
138
+ - 3
139
+ - 48
140
+ - 1536
141
+ padding: true
142
+ - KeepKeys:
143
+ keep_keys:
144
+ - image
145
+ - label_ctc
146
+ - label_gtc
147
+ - length
148
+ - valid_ratio
149
+ loader:
150
+ shuffle: false
151
+ drop_last: false
152
+ batch_size_per_card: 32
153
+ num_workers: 4
154
+ profiler_options: null
checkpoint/optimizer-patch.json ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "preserved_optimizer_entries": 1272,
3
+ "scheduler_after": {
4
+ "last_epoch": 0,
5
+ "last_lr": 3e-06
6
+ },
7
+ "scheduler_before": {
8
+ "last_epoch": 14600,
9
+ "last_lr": 3.179945016175143e-08
10
+ },
11
+ "source": "/workspace/bina02-ppocr/run/train-stage8-optimizer-resume-v1/resume-stage4.pdopt"
12
+ }
checkpoint/status.log ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ train_exit_code=0
2
+ finished_at=2026-07-26T18:15:59Z
data/MIX_MANIFEST.json ADDED
@@ -0,0 +1,823 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "schema": "bina02.stage8-optimizer-resume-mix.v1",
3
+ "train": {
4
+ "native": [
5
+ {
6
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/train/worker-00",
7
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/00_native/0000-train-worker-00",
8
+ "samples": 12246
9
+ },
10
+ {
11
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/train/worker-01",
12
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/00_native/0001-train-worker-01",
13
+ "samples": 12452
14
+ },
15
+ {
16
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/train/worker-02",
17
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/00_native/0002-train-worker-02",
18
+ "samples": 12581
19
+ },
20
+ {
21
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/train/worker-03",
22
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/00_native/0003-train-worker-03",
23
+ "samples": 12409
24
+ },
25
+ {
26
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/train/worker-04",
27
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/00_native/0004-train-worker-04",
28
+ "samples": 12269
29
+ },
30
+ {
31
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/train/worker-05",
32
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/00_native/0005-train-worker-05",
33
+ "samples": 12119
34
+ },
35
+ {
36
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/train/worker-06",
37
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/00_native/0006-train-worker-06",
38
+ "samples": 12255
39
+ },
40
+ {
41
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/train/worker-07",
42
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/00_native/0007-train-worker-07",
43
+ "samples": 12409
44
+ },
45
+ {
46
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/train/worker-08",
47
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/00_native/0008-train-worker-08",
48
+ "samples": 12876
49
+ },
50
+ {
51
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/train/worker-09",
52
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/00_native/0009-train-worker-09",
53
+ "samples": 12262
54
+ },
55
+ {
56
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/train/worker-10",
57
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/00_native/0010-train-worker-10",
58
+ "samples": 12936
59
+ },
60
+ {
61
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/train/worker-11",
62
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/00_native/0011-train-worker-11",
63
+ "samples": 12522
64
+ },
65
+ {
66
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/train/worker-12",
67
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/00_native/0012-train-worker-12",
68
+ "samples": 12090
69
+ },
70
+ {
71
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/train/worker-13",
72
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/00_native/0013-train-worker-13",
73
+ "samples": 12709
74
+ },
75
+ {
76
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/train/worker-14",
77
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/00_native/0014-train-worker-14",
78
+ "samples": 12523
79
+ },
80
+ {
81
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/train/worker-15",
82
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/00_native/0015-train-worker-15",
83
+ "samples": 11825
84
+ }
85
+ ],
86
+ "clean_handwriting": [
87
+ {
88
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0000-7300",
89
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0000-00_handwriting-full-0000-7300",
90
+ "samples": 8853
91
+ },
92
+ {
93
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0001-1056",
94
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0001-00_handwriting-full-0001-1056",
95
+ "samples": 8907
96
+ },
97
+ {
98
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0002-4173",
99
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0002-00_handwriting-full-0002-4173",
100
+ "samples": 9187
101
+ },
102
+ {
103
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0003-3425",
104
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0003-00_handwriting-full-0003-3425",
105
+ "samples": 9428
106
+ },
107
+ {
108
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0004-0284",
109
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0004-00_handwriting-full-0004-0284",
110
+ "samples": 8811
111
+ },
112
+ {
113
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0005-1455",
114
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0005-00_handwriting-full-0005-1455",
115
+ "samples": 8805
116
+ },
117
+ {
118
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0006-5815",
119
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0006-00_handwriting-full-0006-5815",
120
+ "samples": 8709
121
+ },
122
+ {
123
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0007-4451",
124
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0007-00_handwriting-full-0007-4451",
125
+ "samples": 9076
126
+ },
127
+ {
128
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0008-6052",
129
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0008-00_handwriting-full-0008-6052",
130
+ "samples": 8873
131
+ },
132
+ {
133
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0009-2769",
134
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0009-00_handwriting-full-0009-2769",
135
+ "samples": 9013
136
+ },
137
+ {
138
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0010-3720",
139
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0010-00_handwriting-full-0010-3720",
140
+ "samples": 8944
141
+ },
142
+ {
143
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0011-5181",
144
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0011-00_handwriting-full-0011-5181",
145
+ "samples": 8964
146
+ },
147
+ {
148
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0012-2888",
149
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0012-00_handwriting-full-0012-2888",
150
+ "samples": 8848
151
+ },
152
+ {
153
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0013-1484",
154
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0013-00_handwriting-full-0013-1484",
155
+ "samples": 8892
156
+ },
157
+ {
158
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0014-3859",
159
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0014-00_handwriting-full-0014-3859",
160
+ "samples": 8809
161
+ },
162
+ {
163
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0015-6255",
164
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0015-00_handwriting-full-0015-6255",
165
+ "samples": 9180
166
+ },
167
+ {
168
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0016-1883",
169
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0016-00_handwriting-full-0016-1883",
170
+ "samples": 8977
171
+ },
172
+ {
173
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0017-3999",
174
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0017-00_handwriting-full-0017-3999",
175
+ "samples": 9135
176
+ },
177
+ {
178
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0018-4859",
179
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0018-00_handwriting-full-0018-4859",
180
+ "samples": 8946
181
+ },
182
+ {
183
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0019-5047",
184
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0019-00_handwriting-full-0019-5047",
185
+ "samples": 8936
186
+ },
187
+ {
188
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0020-5932",
189
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0020-00_handwriting-full-0020-5932",
190
+ "samples": 9165
191
+ },
192
+ {
193
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0021-6173",
194
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0021-00_handwriting-full-0021-6173",
195
+ "samples": 8991
196
+ },
197
+ {
198
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0022-4759",
199
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0022-00_handwriting-full-0022-4759",
200
+ "samples": 8834
201
+ },
202
+ {
203
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0023-6582",
204
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0023-00_handwriting-full-0023-6582",
205
+ "samples": 9107
206
+ },
207
+ {
208
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0024-0386",
209
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0024-00_handwriting-full-0024-0386",
210
+ "samples": 9064
211
+ },
212
+ {
213
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0025-6153",
214
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0025-00_handwriting-full-0025-6153",
215
+ "samples": 9203
216
+ },
217
+ {
218
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0026-3002",
219
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0026-00_handwriting-full-0026-3002",
220
+ "samples": 8777
221
+ },
222
+ {
223
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0027-2310",
224
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0027-00_handwriting-full-0027-2310",
225
+ "samples": 9497
226
+ },
227
+ {
228
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0028-2667",
229
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0028-00_handwriting-full-0028-2667",
230
+ "samples": 9136
231
+ },
232
+ {
233
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0029-6605",
234
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0029-00_handwriting-full-0029-6605",
235
+ "samples": 8489
236
+ },
237
+ {
238
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0030-1575",
239
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0030-00_handwriting-full-0030-1575",
240
+ "samples": 9086
241
+ },
242
+ {
243
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0031-2901",
244
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0031-00_handwriting-full-0031-2901",
245
+ "samples": 8893
246
+ },
247
+ {
248
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0032-7025",
249
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0032-00_handwriting-full-0032-7025",
250
+ "samples": 9121
251
+ },
252
+ {
253
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0033-6563",
254
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0033-00_handwriting-full-0033-6563",
255
+ "samples": 9088
256
+ },
257
+ {
258
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0034-5956",
259
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0034-00_handwriting-full-0034-5956",
260
+ "samples": 8951
261
+ },
262
+ {
263
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0035-3070",
264
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0035-00_handwriting-full-0035-3070",
265
+ "samples": 8778
266
+ },
267
+ {
268
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0036-2450",
269
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0036-00_handwriting-full-0036-2450",
270
+ "samples": 8598
271
+ },
272
+ {
273
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0037-5038",
274
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0037-00_handwriting-full-0037-5038",
275
+ "samples": 8744
276
+ },
277
+ {
278
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0038-0989",
279
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0038-00_handwriting-full-0038-0989",
280
+ "samples": 8916
281
+ },
282
+ {
283
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0039-4621",
284
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0039-00_handwriting-full-0039-4621",
285
+ "samples": 9207
286
+ },
287
+ {
288
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0040-5586",
289
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0040-00_handwriting-full-0040-5586",
290
+ "samples": 8915
291
+ },
292
+ {
293
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0041-1792",
294
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0041-00_handwriting-full-0041-1792",
295
+ "samples": 9427
296
+ },
297
+ {
298
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0042-3473",
299
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0042-00_handwriting-full-0042-3473",
300
+ "samples": 9003
301
+ },
302
+ {
303
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0043-6558",
304
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0043-00_handwriting-full-0043-6558",
305
+ "samples": 8967
306
+ },
307
+ {
308
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0044-6329",
309
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0044-00_handwriting-full-0044-6329",
310
+ "samples": 9152
311
+ },
312
+ {
313
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0045-6494",
314
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0045-00_handwriting-full-0045-6494",
315
+ "samples": 9036
316
+ },
317
+ {
318
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0046-2485",
319
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0046-00_handwriting-full-0046-2485",
320
+ "samples": 9104
321
+ },
322
+ {
323
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0047-3682",
324
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0047-00_handwriting-full-0047-3682",
325
+ "samples": 9043
326
+ },
327
+ {
328
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0048-2537",
329
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0048-00_handwriting-full-0048-2537",
330
+ "samples": 9142
331
+ },
332
+ {
333
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0049-7279",
334
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0049-00_handwriting-full-0049-7279",
335
+ "samples": 9002
336
+ },
337
+ {
338
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0050-4497",
339
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0050-00_handwriting-full-0050-4497",
340
+ "samples": 9251
341
+ },
342
+ {
343
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0051-7264",
344
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0051-00_handwriting-full-0051-7264",
345
+ "samples": 9190
346
+ },
347
+ {
348
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0052-6079",
349
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0052-00_handwriting-full-0052-6079",
350
+ "samples": 9149
351
+ },
352
+ {
353
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0053-4449",
354
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0053-00_handwriting-full-0053-4449",
355
+ "samples": 9053
356
+ },
357
+ {
358
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0054-2438",
359
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0054-00_handwriting-full-0054-2438",
360
+ "samples": 9356
361
+ },
362
+ {
363
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0055-6492",
364
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0055-00_handwriting-full-0055-6492",
365
+ "samples": 9247
366
+ },
367
+ {
368
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0056-3795",
369
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0056-00_handwriting-full-0056-3795",
370
+ "samples": 9242
371
+ },
372
+ {
373
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0057-4456",
374
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0057-00_handwriting-full-0057-4456",
375
+ "samples": 8956
376
+ },
377
+ {
378
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0058-5481",
379
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0058-00_handwriting-full-0058-5481",
380
+ "samples": 8799
381
+ },
382
+ {
383
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0059-7282",
384
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0059-00_handwriting-full-0059-7282",
385
+ "samples": 8839
386
+ },
387
+ {
388
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0060-6591",
389
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0060-00_handwriting-full-0060-6591",
390
+ "samples": 9168
391
+ },
392
+ {
393
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0061-4348",
394
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0061-00_handwriting-full-0061-4348",
395
+ "samples": 9058
396
+ },
397
+ {
398
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0062-2474",
399
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0062-00_handwriting-full-0062-2474",
400
+ "samples": 8997
401
+ },
402
+ {
403
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0063-3956",
404
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0063-00_handwriting-full-0063-3956",
405
+ "samples": 9018
406
+ },
407
+ {
408
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0064-3899",
409
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0064-00_handwriting-full-0064-3899",
410
+ "samples": 9067
411
+ },
412
+ {
413
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0065-7188",
414
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0065-00_handwriting-full-0065-7188",
415
+ "samples": 8985
416
+ },
417
+ {
418
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0066-0121",
419
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0066-00_handwriting-full-0066-0121",
420
+ "samples": 8890
421
+ },
422
+ {
423
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0067-1108",
424
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0067-00_handwriting-full-0067-1108",
425
+ "samples": 8866
426
+ },
427
+ {
428
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0068-7099",
429
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0068-00_handwriting-full-0068-7099",
430
+ "samples": 8940
431
+ },
432
+ {
433
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0069-3474",
434
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0069-00_handwriting-full-0069-3474",
435
+ "samples": 8784
436
+ },
437
+ {
438
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0070-6126",
439
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0070-00_handwriting-full-0070-6126",
440
+ "samples": 9135
441
+ },
442
+ {
443
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0071-0575",
444
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0071-00_handwriting-full-0071-0575",
445
+ "samples": 9269
446
+ },
447
+ {
448
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0072-2944",
449
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0072-00_handwriting-full-0072-2944",
450
+ "samples": 9132
451
+ },
452
+ {
453
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0073-5818",
454
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0073-00_handwriting-full-0073-5818",
455
+ "samples": 8834
456
+ },
457
+ {
458
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0074-2554",
459
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0074-00_handwriting-full-0074-2554",
460
+ "samples": 8758
461
+ },
462
+ {
463
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0075-0473",
464
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0075-00_handwriting-full-0075-0473",
465
+ "samples": 9110
466
+ },
467
+ {
468
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0076-5288",
469
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0076-00_handwriting-full-0076-5288",
470
+ "samples": 8845
471
+ },
472
+ {
473
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0077-3148",
474
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0077-00_handwriting-full-0077-3148",
475
+ "samples": 8813
476
+ },
477
+ {
478
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0078-4189",
479
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0078-00_handwriting-full-0078-4189",
480
+ "samples": 8891
481
+ },
482
+ {
483
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0079-6863",
484
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0079-00_handwriting-full-0079-6863",
485
+ "samples": 8828
486
+ },
487
+ {
488
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0080-4544",
489
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0080-00_handwriting-full-0080-4544",
490
+ "samples": 9157
491
+ },
492
+ {
493
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0081-4864",
494
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0081-00_handwriting-full-0081-4864",
495
+ "samples": 9319
496
+ },
497
+ {
498
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0082-0367",
499
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0082-00_handwriting-full-0082-0367",
500
+ "samples": 9183
501
+ },
502
+ {
503
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0083-6393",
504
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0083-00_handwriting-full-0083-6393",
505
+ "samples": 8943
506
+ },
507
+ {
508
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0084-3767",
509
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0084-00_handwriting-full-0084-3767",
510
+ "samples": 9028
511
+ },
512
+ {
513
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0085-2375",
514
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0085-00_handwriting-full-0085-2375",
515
+ "samples": 8940
516
+ },
517
+ {
518
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0086-6110",
519
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0086-00_handwriting-full-0086-6110",
520
+ "samples": 9102
521
+ },
522
+ {
523
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0087-6897",
524
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0087-00_handwriting-full-0087-6897",
525
+ "samples": 8799
526
+ },
527
+ {
528
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0088-2696",
529
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0088-00_handwriting-full-0088-2696",
530
+ "samples": 8769
531
+ },
532
+ {
533
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0089-6103",
534
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0089-00_handwriting-full-0089-6103",
535
+ "samples": 9195
536
+ },
537
+ {
538
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0090-6618",
539
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0090-00_handwriting-full-0090-6618",
540
+ "samples": 8603
541
+ },
542
+ {
543
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0091-4877",
544
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0091-00_handwriting-full-0091-4877",
545
+ "samples": 8760
546
+ },
547
+ {
548
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0092-3448",
549
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0092-00_handwriting-full-0092-3448",
550
+ "samples": 9268
551
+ },
552
+ {
553
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0093-7364",
554
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0093-00_handwriting-full-0093-7364",
555
+ "samples": 8805
556
+ },
557
+ {
558
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0094-5908",
559
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0094-00_handwriting-full-0094-5908",
560
+ "samples": 8910
561
+ },
562
+ {
563
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0095-2174",
564
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0095-00_handwriting-full-0095-2174",
565
+ "samples": 9354
566
+ },
567
+ {
568
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0096-1698",
569
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0096-00_handwriting-full-0096-1698",
570
+ "samples": 9083
571
+ },
572
+ {
573
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0097-1677",
574
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0097-00_handwriting-full-0097-1677",
575
+ "samples": 8997
576
+ },
577
+ {
578
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0098-0886",
579
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0098-00_handwriting-full-0098-0886",
580
+ "samples": 9237
581
+ },
582
+ {
583
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0099-6968",
584
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0099-00_handwriting-full-0099-6968",
585
+ "samples": 9255
586
+ },
587
+ {
588
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0100-1619",
589
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0100-00_handwriting-full-0100-1619",
590
+ "samples": 8988
591
+ },
592
+ {
593
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0101-0987",
594
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0101-00_handwriting-full-0101-0987",
595
+ "samples": 8905
596
+ },
597
+ {
598
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0102-4362",
599
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0102-00_handwriting-full-0102-4362",
600
+ "samples": 9083
601
+ },
602
+ {
603
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0103-0810",
604
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0103-00_handwriting-full-0103-0810",
605
+ "samples": 8580
606
+ },
607
+ {
608
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0104-4569",
609
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0104-00_handwriting-full-0104-4569",
610
+ "samples": 9074
611
+ },
612
+ {
613
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0105-3558",
614
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0105-00_handwriting-full-0105-3558",
615
+ "samples": 8741
616
+ },
617
+ {
618
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0106-1478",
619
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0106-00_handwriting-full-0106-1478",
620
+ "samples": 8604
621
+ },
622
+ {
623
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0107-4276",
624
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0107-00_handwriting-full-0107-4276",
625
+ "samples": 9113
626
+ },
627
+ {
628
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0108-1011",
629
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0108-00_handwriting-full-0108-1011",
630
+ "samples": 9196
631
+ },
632
+ {
633
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0109-3406",
634
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0109-00_handwriting-full-0109-3406",
635
+ "samples": 9077
636
+ },
637
+ {
638
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/full-0110-3975",
639
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0110-00_handwriting-full-0110-3975",
640
+ "samples": 8754
641
+ },
642
+ {
643
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/00_handwriting/partial-0925",
644
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/01_clean_handwriting/0111-00_handwriting-partial-0925",
645
+ "samples": 979
646
+ }
647
+ ],
648
+ "printed": [
649
+ {
650
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/01_printed/full-0000-0011",
651
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/02_printed/0000-01_printed-full-0000-0011",
652
+ "samples": 99585
653
+ },
654
+ {
655
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/01_printed/full-0001-0027",
656
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/02_printed/0001-01_printed-full-0001-0027",
657
+ "samples": 30684
658
+ },
659
+ {
660
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/01_printed/full-0002-0005",
661
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/02_printed/0002-01_printed-full-0002-0005",
662
+ "samples": 99655
663
+ },
664
+ {
665
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/01_printed/full-0003-0034",
666
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/02_printed/0003-01_printed-full-0003-0034",
667
+ "samples": 30625
668
+ },
669
+ {
670
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/01_printed/full-0004-0029",
671
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/02_printed/0004-01_printed-full-0004-0029",
672
+ "samples": 30376
673
+ },
674
+ {
675
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/01_printed/full-0005-0013",
676
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/02_printed/0005-01_printed-full-0005-0013",
677
+ "samples": 431461
678
+ },
679
+ {
680
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/01_printed/full-0006-0004",
681
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/02_printed/0006-01_printed-full-0006-0004",
682
+ "samples": 99562
683
+ },
684
+ {
685
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/01_printed/full-0007-0031",
686
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/02_printed/0007-01_printed-full-0007-0031",
687
+ "samples": 30481
688
+ },
689
+ {
690
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/01_printed/full-0008-0006",
691
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/02_printed/0008-01_printed-full-0008-0006",
692
+ "samples": 99587
693
+ },
694
+ {
695
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/01_printed/full-0009-0036",
696
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/02_printed/0009-01_printed-full-0009-0036",
697
+ "samples": 30220
698
+ },
699
+ {
700
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/train/01_printed/partial-0010",
701
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/train/02_printed/0010-01_printed-partial-0010",
702
+ "samples": 17764
703
+ }
704
+ ]
705
+ },
706
+ "eval": {
707
+ "native": [
708
+ {
709
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/eval/worker-00",
710
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/eval/00_native/0000-eval-worker-00",
711
+ "samples": 401
712
+ },
713
+ {
714
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/eval/worker-01",
715
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/eval/00_native/0001-eval-worker-01",
716
+ "samples": 440
717
+ },
718
+ {
719
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/eval/worker-02",
720
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/eval/00_native/0002-eval-worker-02",
721
+ "samples": 404
722
+ },
723
+ {
724
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/eval/worker-03",
725
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/eval/00_native/0003-eval-worker-03",
726
+ "samples": 509
727
+ },
728
+ {
729
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/eval/worker-04",
730
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/eval/00_native/0004-eval-worker-04",
731
+ "samples": 428
732
+ },
733
+ {
734
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/eval/worker-05",
735
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/eval/00_native/0005-eval-worker-05",
736
+ "samples": 400
737
+ },
738
+ {
739
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/eval/worker-06",
740
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/eval/00_native/0006-eval-worker-06",
741
+ "samples": 382
742
+ },
743
+ {
744
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/eval/worker-07",
745
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/eval/00_native/0007-eval-worker-07",
746
+ "samples": 374
747
+ },
748
+ {
749
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/eval/worker-08",
750
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/eval/00_native/0008-eval-worker-08",
751
+ "samples": 407
752
+ },
753
+ {
754
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/eval/worker-09",
755
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/eval/00_native/0009-eval-worker-09",
756
+ "samples": 455
757
+ },
758
+ {
759
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/eval/worker-10",
760
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/eval/00_native/0010-eval-worker-10",
761
+ "samples": 437
762
+ },
763
+ {
764
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/eval/worker-11",
765
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/eval/00_native/0011-eval-worker-11",
766
+ "samples": 413
767
+ },
768
+ {
769
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/eval/worker-12",
770
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/eval/00_native/0012-eval-worker-12",
771
+ "samples": 435
772
+ },
773
+ {
774
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/eval/worker-13",
775
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/eval/00_native/0013-eval-worker-13",
776
+ "samples": 452
777
+ },
778
+ {
779
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/eval/worker-14",
780
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/eval/00_native/0014-eval-worker-14",
781
+ "samples": 430
782
+ },
783
+ {
784
+ "source": "/workspace/bina02-ppocr/data/corpus-v5-native-replay/replay-transfer-v2/full-fixed-v2/lmdb/eval/worker-15",
785
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/eval/00_native/0015-eval-worker-15",
786
+ "samples": 439
787
+ }
788
+ ],
789
+ "clean_handwriting": [
790
+ {
791
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/eval/00_handwriting",
792
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/eval/01_clean_handwriting/0000-eval-00_handwriting",
793
+ "samples": 8192
794
+ }
795
+ ],
796
+ "printed": [
797
+ {
798
+ "source": "/workspace/bina02-ppocr/data/corpus-v4/balanced/eval/01_printed",
799
+ "linked": "/workspace/bina02-ppocr/data/corpus-v8-optimizer-resume/stage8-mix-v1/eval/02_printed/0000-eval-01_printed",
800
+ "samples": 8192
801
+ }
802
+ ]
803
+ },
804
+ "train_counts": {
805
+ "native": 198483,
806
+ "clean_handwriting": 1000000,
807
+ "printed": 1000000
808
+ },
809
+ "eval_counts": {
810
+ "native": 6806,
811
+ "clean_handwriting": 8192,
812
+ "printed": 8192
813
+ },
814
+ "train_total": 2198483,
815
+ "train_proportions": {
816
+ "native": 0.09028179885857657,
817
+ "clean_handwriting": 0.4548591005707117,
818
+ "printed": 0.4548591005707117
819
+ },
820
+ "handwriting_eval_rows": 14998,
821
+ "printed_eval_rows": 8192,
822
+ "optimizer_resume_source": "/workspace/bina02-ppocr/run/train-stage4-clean2m-lr3e6/best_accuracy"
823
+ }
inference.json ADDED
The diff for this file is too large to render. See raw diff
 
inference.pdiparams ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f8e1b5b4e9ef11b46c521fed7d1c9b7e7b2d08c4c879aa01353acd7308a6e9fb
3
+ size 62146801
inference.yml ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Global:
2
+ model_name: PP-OCRv6_medium_rec
3
+ Hpi:
4
+ backend_configs:
5
+ paddle_infer:
6
+ trt_dynamic_shapes: &id001
7
+ x:
8
+ - - 1
9
+ - 3
10
+ - 48
11
+ - 160
12
+ - - 1
13
+ - 3
14
+ - 48
15
+ - 320
16
+ - - 8
17
+ - 3
18
+ - 48
19
+ - 3200
20
+ tensorrt:
21
+ dynamic_shapes: *id001
22
+ PreProcess:
23
+ transform_ops:
24
+ - DecodeImage:
25
+ channel_first: false
26
+ img_mode: BGR
27
+ - MultiLabelEncode:
28
+ gtc_encode: NRTRLabelEncode
29
+ - RecResizeImg:
30
+ image_shape:
31
+ - 3
32
+ - 48
33
+ - 1536
34
+ padding: true
35
+ - KeepKeys:
36
+ keep_keys:
37
+ - image
38
+ - label_ctc
39
+ - label_gtc
40
+ - length
41
+ - valid_ratio
42
+ PostProcess:
43
+ name: CTCLabelDecode
44
+ character_dict:
45
+ - '!'
46
+ - '"'
47
+ - '#'
48
+ - '%'
49
+ - '&'
50
+ - ''''
51
+ - (
52
+ - )
53
+ - '*'
54
+ - +
55
+ - ','
56
+ - '-'
57
+ - .
58
+ - /
59
+ - '0'
60
+ - '1'
61
+ - '2'
62
+ - '3'
63
+ - '4'
64
+ - '5'
65
+ - '6'
66
+ - '7'
67
+ - '8'
68
+ - '9'
69
+ - ':'
70
+ - '='
71
+ - '@'
72
+ - A
73
+ - B
74
+ - C
75
+ - D
76
+ - E
77
+ - F
78
+ - G
79
+ - H
80
+ - I
81
+ - J
82
+ - K
83
+ - L
84
+ - M
85
+ - N
86
+ - O
87
+ - P
88
+ - Q
89
+ - R
90
+ - S
91
+ - T
92
+ - U
93
+ - V
94
+ - W
95
+ - X
96
+ - Y
97
+ - Z
98
+ - '['
99
+ - ']'
100
+ - _
101
+ - a
102
+ - b
103
+ - c
104
+ - d
105
+ - e
106
+ - f
107
+ - g
108
+ - h
109
+ - i
110
+ - j
111
+ - k
112
+ - l
113
+ - m
114
+ - n
115
+ - o
116
+ - p
117
+ - q
118
+ - r
119
+ - s
120
+ - t
121
+ - u
122
+ - v
123
+ - w
124
+ - x
125
+ - y
126
+ - z
127
+ - «
128
+ - ­
129
+ - »
130
+ - â
131
+ - ،
132
+ - ؛
133
+ - ؟
134
+ - ء
135
+ - آ
136
+ - أ
137
+ - ؤ
138
+ - إ
139
+ - ئ
140
+ - ا
141
+ - ب
142
+ - ة
143
+ - ت
144
+ - ث
145
+ - ج
146
+ - ح
147
+ - خ
148
+ - د
149
+ - ذ
150
+ - ر
151
+ - ز
152
+ - س
153
+ - ش
154
+ - ص
155
+ - ض
156
+ - ط
157
+ - ظ
158
+ - ع
159
+ - غ
160
+ - ف
161
+ - ق
162
+ - ل
163
+ - م
164
+ - ن
165
+ - ه
166
+ - و
167
+ - ً
168
+ - ٌ
169
+ - ٍ
170
+ - َ
171
+ - ُ
172
+ - ِ
173
+ - ّ
174
+ - ْ
175
+ - ٔ
176
+ - ٪
177
+ - ٫
178
+ - ٬
179
+ - ٰ
180
+ - پ
181
+ - چ
182
+ - ژ
183
+ - ک
184
+ - گ
185
+ - ھ
186
+ - ۀ
187
+ - ی
188
+ - ە
189
+ - ۰
190
+ - ۱
191
+ - ۲
192
+ - ۳
193
+ - ۴
194
+ - ۵
195
+ - ۶
196
+ - ۷
197
+ - ۸
198
+ - ۹
199
+ - ‌
200
+ - –
201
+ - ’
202
+ - “
203
+ - ”
204
+ - •
205
+ - …
inference/INFERENCE_READY ADDED
@@ -0,0 +1 @@
 
 
1
+ READY
inference/export.log ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /workspace/bina02-ppocr/venv/lib/python3.12/site-packages/paddle/utils/cpp_extension/extension_utils.py:712: UserWarning: No ccache found. Please be aware that recompiling all source files may be required. You can download and install ccache from: https://github.com/ccache/ccache/blob/master/doc/INSTALL.md
2
+ warnings.warn(warning_message)
3
+ WARNING: OMP_NUM_THREADS set to 4, not 1. The computation speed will not be optimized if you use data parallel. It will fail if this PaddlePaddle binary is compiled with OpenBlas since OpenBlas does not support multi-threads.
4
+ PLEASE USE OMP_NUM_THREADS WISELY.
5
+ Skipping import of the encryption module.
6
+ W0726 18:20:31.932339 63135 gpu_resources.cc:116] Please NOTE: device: 0, GPU Compute Capability: 12.0, Driver API Version: 13.0, Runtime API Version: 13.0
7
+ [2026/07/26 18:20:32] ppocr INFO: resume from /workspace/bina02-ppocr/run/train-stage8-optimizer-resume-v2/resume-stage4
8
+ [2026/07/26 18:20:32] ppocr INFO: Export inference config file to /workspace/bina02-ppocr/run/posttrain-stage8-optimizer-resume-v2/inference-best/inference.yml
9
+ Skipping import of the encryption module
10
+ W0726 18:20:33.083880 63135 eager_utils.cc:3584] Paddle static graph(PIR) not support input out tensor for now!!!!!
11
+ [2026/07/26 18:20:34] ppocr INFO: inference model is saved to /workspace/bina02-ppocr/run/posttrain-stage8-optimizer-resume-v2/inference-best/inference
inference/inference.json ADDED
The diff for this file is too large to render. See raw diff
 
inference/inference.pdiparams ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f8e1b5b4e9ef11b46c521fed7d1c9b7e7b2d08c4c879aa01353acd7308a6e9fb
3
+ size 62146801
inference/inference.yml ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Global:
2
+ model_name: PP-OCRv6_medium_rec
3
+ Hpi:
4
+ backend_configs:
5
+ paddle_infer:
6
+ trt_dynamic_shapes: &id001
7
+ x:
8
+ - - 1
9
+ - 3
10
+ - 48
11
+ - 160
12
+ - - 1
13
+ - 3
14
+ - 48
15
+ - 320
16
+ - - 8
17
+ - 3
18
+ - 48
19
+ - 3200
20
+ tensorrt:
21
+ dynamic_shapes: *id001
22
+ PreProcess:
23
+ transform_ops:
24
+ - DecodeImage:
25
+ channel_first: false
26
+ img_mode: BGR
27
+ - MultiLabelEncode:
28
+ gtc_encode: NRTRLabelEncode
29
+ - RecResizeImg:
30
+ image_shape:
31
+ - 3
32
+ - 48
33
+ - 1536
34
+ padding: true
35
+ - KeepKeys:
36
+ keep_keys:
37
+ - image
38
+ - label_ctc
39
+ - label_gtc
40
+ - length
41
+ - valid_ratio
42
+ PostProcess:
43
+ name: CTCLabelDecode
44
+ character_dict:
45
+ - '!'
46
+ - '"'
47
+ - '#'
48
+ - '%'
49
+ - '&'
50
+ - ''''
51
+ - (
52
+ - )
53
+ - '*'
54
+ - +
55
+ - ','
56
+ - '-'
57
+ - .
58
+ - /
59
+ - '0'
60
+ - '1'
61
+ - '2'
62
+ - '3'
63
+ - '4'
64
+ - '5'
65
+ - '6'
66
+ - '7'
67
+ - '8'
68
+ - '9'
69
+ - ':'
70
+ - '='
71
+ - '@'
72
+ - A
73
+ - B
74
+ - C
75
+ - D
76
+ - E
77
+ - F
78
+ - G
79
+ - H
80
+ - I
81
+ - J
82
+ - K
83
+ - L
84
+ - M
85
+ - N
86
+ - O
87
+ - P
88
+ - Q
89
+ - R
90
+ - S
91
+ - T
92
+ - U
93
+ - V
94
+ - W
95
+ - X
96
+ - Y
97
+ - Z
98
+ - '['
99
+ - ']'
100
+ - _
101
+ - a
102
+ - b
103
+ - c
104
+ - d
105
+ - e
106
+ - f
107
+ - g
108
+ - h
109
+ - i
110
+ - j
111
+ - k
112
+ - l
113
+ - m
114
+ - n
115
+ - o
116
+ - p
117
+ - q
118
+ - r
119
+ - s
120
+ - t
121
+ - u
122
+ - v
123
+ - w
124
+ - x
125
+ - y
126
+ - z
127
+ - «
128
+ - ­
129
+ - »
130
+ - â
131
+ - ،
132
+ - ؛
133
+ - ؟
134
+ - ء
135
+ - آ
136
+ - أ
137
+ - ؤ
138
+ - إ
139
+ - ئ
140
+ - ا
141
+ - ب
142
+ - ة
143
+ - ت
144
+ - ث
145
+ - ج
146
+ - ح
147
+ - خ
148
+ - د
149
+ - ذ
150
+ - ر
151
+ - ز
152
+ - س
153
+ - ش
154
+ - ص
155
+ - ض
156
+ - ط
157
+ - ظ
158
+ - ع
159
+ - غ
160
+ - ف
161
+ - ق
162
+ - ل
163
+ - م
164
+ - ن
165
+ - ه
166
+ - و
167
+ - ً
168
+ - ٌ
169
+ - ٍ
170
+ - َ
171
+ - ُ
172
+ - ِ
173
+ - ّ
174
+ - ْ
175
+ - ٔ
176
+ - ٪
177
+ - ٫
178
+ - ٬
179
+ - ٰ
180
+ - پ
181
+ - چ
182
+ - ژ
183
+ - ک
184
+ - گ
185
+ - ھ
186
+ - ۀ
187
+ - ی
188
+ - ە
189
+ - ۰
190
+ - ۱
191
+ - ۲
192
+ - ۳
193
+ - ۴
194
+ - ۵
195
+ - ۶
196
+ - ۷
197
+ - ۸
198
+ - ۹
199
+ - ‌
200
+ - –
201
+ - ’
202
+ - “
203
+ - ”
204
+ - •
205
+ - …
pipeline/assemble_bina02_two_layer.py ADDED
@@ -0,0 +1,221 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Assemble isolated handwriting and Gemini template text in benchmark order."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import json
8
+ import os
9
+ import unicodedata
10
+ from pathlib import Path
11
+ from typing import Any, Callable
12
+
13
+ from rapidfuzz.distance import Levenshtein
14
+
15
+ PLACEHOLDER = "{{HANDWRITING}}"
16
+ VARIANTS: dict[str, tuple[float | None, bool]] = {
17
+ "simple_top": (None, False),
18
+ "simple_spread_right": (None, True),
19
+ "row055_top": (0.55, False),
20
+ "row070_top": (0.70, False),
21
+ "row055_spread_right": (0.55, True),
22
+ "row070_spread_right": (0.70, True),
23
+ }
24
+
25
+
26
+ def normalized(text: str) -> str:
27
+ return " ".join(unicodedata.normalize("NFKC", str(text)).split())
28
+
29
+
30
+ def merge_template_text(template_text: str, handwriting: str) -> tuple[str, int]:
31
+ handwriting = normalized(handwriting)
32
+ count = str(template_text).count(PLACEHOLDER)
33
+ if count:
34
+ merged = str(template_text).replace(PLACEHOLDER, handwriting, 1)
35
+ merged = merged.replace(PLACEHOLDER, "")
36
+ elif normalized(template_text):
37
+ merged = f"{template_text} {handwriting}"
38
+ else:
39
+ merged = handwriting
40
+ return normalized(merged), count
41
+
42
+
43
+ def center(line: dict[str, Any]) -> tuple[float, float]:
44
+ x0, y0, x1, y1 = (float(value) for value in line["bbox"])
45
+ return (x0 + x1) / 2.0, (y0 + y1) / 2.0
46
+
47
+
48
+ def height(line: dict[str, Any]) -> float:
49
+ return max(float(line["bbox"][3]) - float(line["bbox"][1]), 1.0)
50
+
51
+
52
+ def simple(lines: list[dict[str, Any]]) -> list[list[dict[str, Any]]]:
53
+ return [[line] for line in sorted(lines, key=lambda item: (center(item)[1], -center(item)[0]))]
54
+
55
+
56
+ def clustered(lines: list[dict[str, Any]], factor: float) -> list[list[dict[str, Any]]]:
57
+ ordered = sorted(lines, key=lambda item: center(item)[1])
58
+ rows: list[list[dict[str, Any]]] = []
59
+ for line in ordered:
60
+ if not rows:
61
+ rows.append([line])
62
+ continue
63
+ row = rows[-1]
64
+ mean_y = sum(center(item)[1] for item in row) / len(row)
65
+ mean_height = sum(height(item) for item in row) / len(row)
66
+ if abs(center(line)[1] - mean_y) <= factor * max(height(line), mean_height):
67
+ row.append(line)
68
+ else:
69
+ rows.append([line])
70
+ for row in rows:
71
+ row.sort(key=lambda item: center(item)[0], reverse=True)
72
+ return rows
73
+
74
+
75
+ def layout(
76
+ lines: list[dict[str, Any]],
77
+ width: int,
78
+ height_value: int,
79
+ factor: float | None,
80
+ spread: bool,
81
+ ) -> list[list[dict[str, Any]]]:
82
+ builder: Callable[[list[dict[str, Any]]], list[list[dict[str, Any]]]]
83
+ builder = simple if factor is None else lambda value: clustered(value, factor)
84
+ aspect = width / max(height_value, 1)
85
+ if spread and 1.03 <= aspect <= 1.42:
86
+ midpoint = width / 2.0
87
+ return builder([line for line in lines if center(line)[0] >= midpoint]) + builder(
88
+ [line for line in lines if center(line)[0] < midpoint]
89
+ )
90
+ return builder(lines)
91
+
92
+
93
+ def handwriting_prediction(row: dict[str, Any], variant: str) -> str:
94
+ factor, spread = VARIANTS[variant]
95
+ rows = layout(
96
+ row.get("lines", []),
97
+ int(row.get("width") or 0),
98
+ int(row.get("height") or 0),
99
+ factor,
100
+ spread,
101
+ )
102
+ return "\n".join(
103
+ " ".join(
104
+ str(line.get("text") or "").strip()
105
+ for line in group
106
+ if str(line.get("text") or "").strip()
107
+ )
108
+ for group in rows
109
+ if any(str(line.get("text") or "").strip() for line in group)
110
+ )
111
+
112
+
113
+ def metric(reference: str, hypothesis: str) -> dict[str, int]:
114
+ reference = normalized(reference)
115
+ hypothesis = normalized(hypothesis)
116
+ return {
117
+ "char_errors": int(Levenshtein.distance(reference, hypothesis)),
118
+ "ref_chars": len(reference),
119
+ "word_errors": int(
120
+ Levenshtein.distance(reference.split(), hypothesis.split())
121
+ ),
122
+ "ref_words": len(reference.split()),
123
+ }
124
+
125
+
126
+ def aggregate(rows: list[dict[str, Any]]) -> dict[str, float | int]:
127
+ totals = {key: 0 for key in ("char_errors", "ref_chars", "word_errors", "ref_words")}
128
+ for row in rows:
129
+ for key, value in metric(row["reference"], row["prediction"]).items():
130
+ totals[key] += value
131
+ return {
132
+ **totals,
133
+ "wer": totals["word_errors"] / max(totals["ref_words"], 1),
134
+ "cer": totals["char_errors"] / max(totals["ref_chars"], 1),
135
+ }
136
+
137
+
138
+ def main() -> None:
139
+ parser = argparse.ArgumentParser()
140
+ parser.add_argument("--recognized-lines", type=Path, required=True)
141
+ parser.add_argument("--baseline-predictions", type=Path, required=True)
142
+ parser.add_argument("--output-dir", type=Path, required=True)
143
+ parser.add_argument("--isolation-variant", required=True)
144
+ args = parser.parse_args()
145
+ args.output_dir.mkdir(parents=True, exist_ok=True)
146
+ recognized = [
147
+ json.loads(line)
148
+ for line in args.recognized_lines.read_text(encoding="utf-8").splitlines()
149
+ if line.strip()
150
+ ]
151
+ selected_ids = {str(row["id"]) for row in recognized}
152
+ baseline = [
153
+ json.loads(line)
154
+ for line in args.baseline_predictions.read_text(encoding="utf-8").splitlines()
155
+ if line.strip() and str(json.loads(line).get("id")) in selected_ids
156
+ ]
157
+ baseline_metrics = aggregate(baseline)
158
+ candidates = {}
159
+ for layout_variant in VARIANTS:
160
+ rows = []
161
+ missing_placeholders = 0
162
+ multiple_placeholders = 0
163
+ for row in recognized:
164
+ handwriting = handwriting_prediction(row, layout_variant)
165
+ prediction, placeholders = merge_template_text(
166
+ str(row.get("template_text") or ""),
167
+ handwriting,
168
+ )
169
+ missing_placeholders += int(placeholders == 0)
170
+ multiple_placeholders += int(placeholders > 1)
171
+ rows.append(
172
+ {
173
+ "id": row["id"],
174
+ "split": "handwriting",
175
+ "reference": row["reference"],
176
+ "prediction": prediction,
177
+ "handwriting_prediction": normalized(handwriting),
178
+ "template_id": row.get("template_id"),
179
+ "template_placeholder_count": placeholders,
180
+ "isolation_variant": args.isolation_variant,
181
+ "layout_variant": layout_variant,
182
+ "detected_lines": len(row.get("lines", [])),
183
+ **({"error": row["error"]} if row.get("error") else {}),
184
+ }
185
+ )
186
+ path = args.output_dir / f"predictions-{layout_variant}.jsonl"
187
+ temporary = path.with_suffix(".jsonl.tmp")
188
+ with temporary.open("w", encoding="utf-8") as handle:
189
+ for row in rows:
190
+ handle.write(json.dumps(row, ensure_ascii=False) + "\n")
191
+ os.replace(temporary, path)
192
+ candidates[layout_variant] = {
193
+ **aggregate(rows),
194
+ "missing_placeholders": missing_placeholders,
195
+ "multiple_placeholders": multiple_placeholders,
196
+ "path": str(path),
197
+ }
198
+ best = min(
199
+ candidates,
200
+ key=lambda name: (
201
+ candidates[name]["wer"] + candidates[name]["cer"],
202
+ candidates[name]["cer"],
203
+ ),
204
+ )
205
+ summary = {
206
+ "isolation_variant": args.isolation_variant,
207
+ "rows": len(recognized),
208
+ "baseline": baseline_metrics,
209
+ "candidates": candidates,
210
+ "best_layout_variant": best,
211
+ "best": candidates[best],
212
+ }
213
+ (args.output_dir / "assembly-summary.json").write_text(
214
+ json.dumps(summary, ensure_ascii=False, indent=2) + "\n",
215
+ encoding="utf-8",
216
+ )
217
+ print(json.dumps(summary, ensure_ascii=False, indent=2))
218
+
219
+
220
+ if __name__ == "__main__":
221
+ main()
pipeline/benchmark_bina02_printed_only.py ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Eight-way resumable printed-only exact benchmark for the Stage7 checkpoint."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import fcntl
8
+ import json
9
+ import os
10
+ import re
11
+ import time
12
+ import traceback
13
+ from pathlib import Path
14
+ from typing import Any
15
+
16
+ import cv2
17
+ import numpy as np
18
+ import pyarrow.parquet as pq
19
+ from paddleocr import PaddleOCR
20
+
21
+
22
+ ROWS = 6_669
23
+ LTR_RUN = re.compile(r"[a-zA-Z0-9 :*./%+-]")
24
+
25
+
26
+ def pred_reverse(text: str) -> str:
27
+ segments = []
28
+ current = ""
29
+ for character in text:
30
+ if LTR_RUN.search(character):
31
+ current += character
32
+ else:
33
+ if current:
34
+ segments.append(current)
35
+ current = ""
36
+ segments.append(character)
37
+ if current:
38
+ segments.append(current)
39
+ return "".join(reversed(segments))
40
+
41
+
42
+ def atomic_json(path: Path, payload: dict[str, Any]) -> None:
43
+ temporary = path.with_suffix(path.suffix + f".{os.getpid()}.tmp")
44
+ temporary.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n")
45
+ os.replace(temporary, path)
46
+
47
+
48
+ def ordered_text(payload: dict[str, Any]) -> tuple[str, int]:
49
+ texts = [pred_reverse(str(text)) for text in payload.get("rec_texts", [])]
50
+ boxes = payload.get("rec_boxes", [])
51
+ if len(boxes) != len(texts):
52
+ return "\n".join(text for text in texts if text.strip()), len(texts)
53
+ items = []
54
+ for text, box in zip(texts, boxes):
55
+ x0, y0, x1, y1 = (float(value) for value in box)
56
+ items.append(
57
+ {
58
+ "text": text,
59
+ "x": (x0 + x1) / 2,
60
+ "y": (y0 + y1) / 2,
61
+ "height": max(y1 - y0, 1),
62
+ }
63
+ )
64
+ items.sort(key=lambda item: item["y"])
65
+ rows = []
66
+ for item in items:
67
+ if not rows:
68
+ rows.append([item])
69
+ continue
70
+ row = rows[-1]
71
+ mean_y = sum(part["y"] for part in row) / len(row)
72
+ mean_h = sum(part["height"] for part in row) / len(row)
73
+ if abs(item["y"] - mean_y) <= 0.55 * max(item["height"], mean_h):
74
+ row.append(item)
75
+ else:
76
+ rows.append([item])
77
+ lines = []
78
+ for row in rows:
79
+ row.sort(key=lambda item: item["x"], reverse=True)
80
+ text = " ".join(item["text"].strip() for item in row if item["text"].strip())
81
+ if text:
82
+ lines.append(text)
83
+ return "\n".join(lines), len(texts)
84
+
85
+
86
+ def main() -> None:
87
+ parser = argparse.ArgumentParser()
88
+ parser.add_argument("--rank", type=int, required=True)
89
+ parser.add_argument("--world-size", type=int, default=8)
90
+ parser.add_argument("--data", type=Path, required=True)
91
+ parser.add_argument("--model-dir", type=Path, required=True)
92
+ parser.add_argument("--output-dir", type=Path, required=True)
93
+ parser.add_argument("--device", default="gpu:0")
94
+ args = parser.parse_args()
95
+ args.output_dir.mkdir(parents=True, exist_ok=True)
96
+ start = ROWS * args.rank // args.world_size
97
+ stop = ROWS * (args.rank + 1) // args.world_size
98
+ output_path = args.output_dir / f"predictions-rank{args.rank:02d}.jsonl"
99
+ status_path = args.output_dir / f"status-rank{args.rank:02d}.json"
100
+ done = set()
101
+ if output_path.is_file():
102
+ for line in output_path.read_text(encoding="utf-8").splitlines():
103
+ if line.strip():
104
+ done.add(json.loads(line)["id"])
105
+ ocr = PaddleOCR(
106
+ text_recognition_model_dir=str(args.model_dir),
107
+ text_recognition_batch_size=64,
108
+ use_doc_orientation_classify=False,
109
+ use_doc_unwarping=False,
110
+ use_textline_orientation=False,
111
+ text_rec_score_thresh=0.0,
112
+ text_detection_model_name="PP-OCRv6_medium_det",
113
+ device=args.device,
114
+ )
115
+ offset = 0
116
+ completed = 0
117
+ failures = 0
118
+ started = time.monotonic()
119
+ with output_path.open("a", encoding="utf-8", buffering=1) as output:
120
+ for shard in sorted((args.data / "data").glob("printed-*.parquet")):
121
+ table = pq.read_table(shard, columns=["image", "label"])
122
+ for local, row in enumerate(table.to_pylist()):
123
+ index = offset + local
124
+ if not start <= index < stop or f"printed:{index}" in done:
125
+ continue
126
+ value = row["image"]
127
+ encoded = value.get("bytes") if isinstance(value, dict) else value
128
+ image = cv2.imdecode(
129
+ np.frombuffer(encoded, dtype=np.uint8),
130
+ cv2.IMREAD_COLOR,
131
+ )
132
+ prediction = ""
133
+ line_count = 0
134
+ error = None
135
+ for attempt in range(1, 4):
136
+ try:
137
+ results = list(ocr.predict(image))
138
+ payload = results[0].json
139
+ if callable(payload):
140
+ payload = payload()
141
+ prediction, line_count = ordered_text(payload.get("res", payload))
142
+ error = None
143
+ break
144
+ except Exception as exc:
145
+ traceback.print_exc()
146
+ error = f"{type(exc).__name__}: {exc}"
147
+ time.sleep(attempt)
148
+ failures += int(error is not None)
149
+ result = {
150
+ "id": f"printed:{index}",
151
+ "split": "printed",
152
+ "reference": str(row.get("label") or ""),
153
+ "prediction": prediction,
154
+ "detected_lines": line_count,
155
+ }
156
+ if error:
157
+ result["error"] = error
158
+ output.write(json.dumps(result, ensure_ascii=False) + "\n")
159
+ completed += 1
160
+ offset += table.num_rows
161
+ atomic_json(
162
+ status_path,
163
+ {
164
+ "state": "complete",
165
+ "rank": args.rank,
166
+ "assigned": stop - start,
167
+ "completed_this_run": completed,
168
+ "failures": failures,
169
+ "rate_pages_per_second": completed / max(time.monotonic() - started, 1e-9),
170
+ },
171
+ )
172
+ with (args.output_dir / "merge.lock").open("a+") as lock:
173
+ fcntl.flock(lock, fcntl.LOCK_EX)
174
+ statuses = []
175
+ for rank in range(args.world_size):
176
+ path = args.output_dir / f"status-rank{rank:02d}.json"
177
+ if not path.is_file():
178
+ return
179
+ statuses.append(json.loads(path.read_text()))
180
+ rows = {}
181
+ for rank in range(args.world_size):
182
+ path = args.output_dir / f"predictions-rank{rank:02d}.jsonl"
183
+ for line in path.read_text(encoding="utf-8").splitlines():
184
+ if line.strip():
185
+ row = json.loads(line)
186
+ rows[row["id"]] = row
187
+ expected = [f"printed:{index}" for index in range(ROWS)]
188
+ if set(rows) != set(expected):
189
+ raise RuntimeError("printed merge contract failed")
190
+ with (args.output_dir / "predictions.jsonl.tmp").open("w", encoding="utf-8") as handle:
191
+ for row_id in expected:
192
+ handle.write(json.dumps(rows[row_id], ensure_ascii=False) + "\n")
193
+ os.replace(
194
+ args.output_dir / "predictions.jsonl.tmp",
195
+ args.output_dir / "predictions.jsonl",
196
+ )
197
+ atomic_json(
198
+ args.output_dir / "run-summary.json",
199
+ {
200
+ "state": "complete",
201
+ "rows": len(rows),
202
+ "failures": sum(value["failures"] for value in statuses),
203
+ },
204
+ )
205
+
206
+
207
+ if __name__ == "__main__":
208
+ main()
pipeline/bina02_template_layers.py ADDED
@@ -0,0 +1,393 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Template matching, handwriting isolation, and lossless template-text merging."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import json
7
+ import re
8
+ from dataclasses import dataclass
9
+ from pathlib import Path
10
+ from typing import Any
11
+
12
+ import cv2
13
+ import numpy as np
14
+ from PIL import Image
15
+
16
+
17
+ PLACEHOLDER = "{{HANDWRITING}}"
18
+ VARIANTS = {
19
+ "adaptive65": {"sigma": 2.5, "floor": 8.0, "percentile": 65.0},
20
+ "adaptive75": {"sigma": 4.0, "floor": 10.0, "percentile": 75.0},
21
+ "adaptive85": {"sigma": 5.5, "floor": 12.0, "percentile": 85.0},
22
+ "fixed20": {"fixed": 20.0},
23
+ "residual2": {"mode": "residual", "floor": 2.0, "gain": 2.0},
24
+ "residual3": {"mode": "residual", "floor": 3.0, "gain": 3.0},
25
+ "residual4": {"mode": "residual", "floor": 4.0, "gain": 4.0},
26
+ "residual6": {"mode": "residual", "floor": 6.0, "gain": 4.0},
27
+ "highpass08": {
28
+ "mode": "highpass",
29
+ "sigma": 0.8,
30
+ "floor": 1.5,
31
+ "gain": 4.0,
32
+ },
33
+ "highpass12": {
34
+ "mode": "highpass",
35
+ "sigma": 1.2,
36
+ "floor": 2.0,
37
+ "gain": 4.0,
38
+ },
39
+ "highpass20": {
40
+ "mode": "highpass",
41
+ "sigma": 2.0,
42
+ "floor": 2.0,
43
+ "gain": 3.0,
44
+ },
45
+ "highpass30": {
46
+ "mode": "highpass",
47
+ "sigma": 3.0,
48
+ "floor": 2.5,
49
+ "gain": 3.0,
50
+ },
51
+ }
52
+
53
+
54
+ @dataclass(frozen=True)
55
+ class Template:
56
+ id: str
57
+ file: str
58
+ image: np.ndarray
59
+ text_region: tuple[int, int, int, int]
60
+ template_text: str
61
+ blocks: tuple[dict[str, Any], ...]
62
+
63
+
64
+ @dataclass(frozen=True)
65
+ class TemplateMatch:
66
+ template: Template
67
+ median_abs_delta: float
68
+ p90_abs_delta: float
69
+ candidate_count: int
70
+
71
+
72
+ def normalize_text(value: str) -> str:
73
+ return " ".join(str(value).split())
74
+
75
+
76
+ def merge_template_text(template_text: str, handwriting: str) -> tuple[str, int]:
77
+ """Insert handwriting once and preserve every Gemini template token."""
78
+ template_text = str(template_text)
79
+ handwriting = normalize_text(handwriting)
80
+ count = template_text.count(PLACEHOLDER)
81
+ if count:
82
+ merged = template_text.replace(PLACEHOLDER, handwriting, 1)
83
+ merged = merged.replace(PLACEHOLDER, "")
84
+ elif normalize_text(template_text):
85
+ merged = f"{template_text} {handwriting}"
86
+ else:
87
+ merged = handwriting
88
+ return normalize_text(merged), count
89
+
90
+
91
+ class TemplateCatalog:
92
+ def __init__(self, background_root: Path, ocr_root: Path):
93
+ background_root = Path(background_root)
94
+ ocr_root = Path(ocr_root)
95
+ manifest = json.loads(
96
+ (background_root / "manifest.json").read_text(encoding="utf-8")
97
+ )
98
+ images: dict[str, np.ndarray] = {}
99
+ templates: list[Template] = []
100
+ for item in manifest:
101
+ filename = str(item["file"])
102
+ if filename not in images:
103
+ with Image.open(background_root / filename) as opened:
104
+ images[filename] = np.asarray(opened.convert("RGB"))
105
+ result: dict[str, Any] = {}
106
+ ocr_path = ocr_root / f"{item['id']}.json"
107
+ if ocr_path.is_file():
108
+ result = json.loads(
109
+ ocr_path.read_text(encoding="utf-8")
110
+ ).get("result", {})
111
+ templates.append(
112
+ Template(
113
+ id=str(item["id"]),
114
+ file=filename,
115
+ image=images[filename],
116
+ text_region=tuple(int(value) for value in item["text_region"]),
117
+ template_text=str(result.get("template_text", PLACEHOLDER)),
118
+ blocks=tuple(result.get("blocks", [])),
119
+ )
120
+ )
121
+ self.templates = tuple(templates)
122
+ self.by_id = {template.id: template for template in templates}
123
+
124
+ @staticmethod
125
+ def difference(image: np.ndarray, template: np.ndarray) -> np.ndarray:
126
+ if image.shape != template.shape:
127
+ raise ValueError(
128
+ f"image/template shape mismatch: {image.shape} != {template.shape}"
129
+ )
130
+ return np.max(
131
+ np.abs(image.astype(np.int16) - template.astype(np.int16)),
132
+ axis=2,
133
+ ).astype(np.float32)
134
+
135
+ def match(self, image: Image.Image | np.ndarray) -> TemplateMatch:
136
+ actual = (
137
+ np.asarray(image.convert("RGB"))
138
+ if isinstance(image, Image.Image)
139
+ else np.asarray(image)
140
+ )
141
+ height, width = actual.shape[:2]
142
+ candidates = [
143
+ template
144
+ for template in self.templates
145
+ if template.image.shape[:2] == (height, width)
146
+ ]
147
+ if not candidates:
148
+ raise ValueError(f"no template has native size {width}x{height}")
149
+
150
+ by_file: dict[str, tuple[float, float, np.ndarray]] = {}
151
+ for template in candidates:
152
+ if template.file in by_file:
153
+ continue
154
+ delta = self.difference(actual, template.image)
155
+ by_file[template.file] = (
156
+ float(np.median(delta)),
157
+ float(np.percentile(delta, 90)),
158
+ delta,
159
+ )
160
+ best_file = min(
161
+ by_file,
162
+ key=lambda name: (
163
+ by_file[name][0],
164
+ by_file[name][1],
165
+ name,
166
+ ),
167
+ )
168
+ same_file = [
169
+ template for template in candidates if template.file == best_file
170
+ ]
171
+ median, p90, delta = by_file[best_file]
172
+ if len(same_file) == 1:
173
+ selected = same_file[0]
174
+ else:
175
+ center = float(np.median(delta))
176
+ spread = float(np.median(np.abs(delta - center)))
177
+ threshold = max(8.0, center + 3.0 * max(spread, 1.0))
178
+
179
+ def region_energy(template: Template) -> float:
180
+ x0, y0, x1, y1 = template.text_region
181
+ x0 = max(0, min(width, x0))
182
+ x1 = max(0, min(width, x1))
183
+ y0 = max(0, min(height, y0))
184
+ y1 = max(0, min(height, y1))
185
+ region = delta[y0:y1, x0:x1]
186
+ if not region.size:
187
+ return -1.0
188
+ return float(np.maximum(region - threshold, 0.0).sum()) / region.size
189
+
190
+ selected = max(same_file, key=lambda template: (region_energy(template), template.id))
191
+ return TemplateMatch(
192
+ template=selected,
193
+ median_abs_delta=median,
194
+ p90_abs_delta=p90,
195
+ candidate_count=len(candidates),
196
+ )
197
+
198
+ @staticmethod
199
+ def _mask(delta: np.ndarray, variant: str) -> tuple[np.ndarray, float]:
200
+ if variant not in VARIANTS:
201
+ raise ValueError(f"unknown isolation variant: {variant}")
202
+ options = VARIANTS[variant]
203
+ if "fixed" in options:
204
+ threshold = float(options["fixed"])
205
+ else:
206
+ center = float(np.median(delta))
207
+ spread = float(np.median(np.abs(delta - center)))
208
+ threshold = max(
209
+ float(options["floor"]),
210
+ center + float(options["sigma"]) * max(spread, 1.0),
211
+ float(np.percentile(delta, float(options["percentile"]))),
212
+ )
213
+ mask = np.uint8(delta >= threshold)
214
+ count, labels, stats, _ = cv2.connectedComponentsWithStats(mask, 8)
215
+ filtered = np.zeros_like(mask)
216
+ for component in range(1, count):
217
+ if int(stats[component, cv2.CC_STAT_AREA]) >= 3:
218
+ filtered[labels == component] = 1
219
+ filtered = cv2.dilate(
220
+ filtered,
221
+ cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)),
222
+ iterations=1,
223
+ )
224
+ return filtered.astype(bool), threshold
225
+
226
+ @staticmethod
227
+ def _residual(
228
+ actual: np.ndarray,
229
+ template: np.ndarray,
230
+ variant: str,
231
+ ) -> tuple[np.ndarray, dict[str, float]]:
232
+ options = VARIANTS[variant]
233
+ channel_delta = template.astype(np.float32) - actual.astype(np.float32)
234
+ offsets = np.median(channel_delta.reshape(-1, 3), axis=0)
235
+ corrected = channel_delta - offsets.reshape(1, 1, 3)
236
+ signal = np.maximum(corrected.max(axis=2) - float(options["floor"]), 0.0)
237
+ signal = np.clip(signal * float(options["gain"]), 0.0, 245.0)
238
+ raw_mask = np.uint8(signal >= 3.0)
239
+ count, labels, stats, _ = cv2.connectedComponentsWithStats(raw_mask, 8)
240
+ mask = np.zeros_like(raw_mask)
241
+ for component in range(1, count):
242
+ if int(stats[component, cv2.CC_STAT_AREA]) >= 2:
243
+ mask[labels == component] = 1
244
+ darkness = np.zeros_like(signal)
245
+ darkness[mask.astype(bool)] = signal[mask.astype(bool)]
246
+ darkness = cv2.dilate(
247
+ darkness,
248
+ cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (2, 2)),
249
+ iterations=1,
250
+ )
251
+ output = np.full_like(actual, 255)
252
+ value = np.uint8(np.clip(255.0 - darkness, 0.0, 255.0))
253
+ retained = darkness >= 3.0
254
+ output[retained] = np.repeat(value[:, :, None], 3, axis=2)[retained]
255
+ return output, {
256
+ "threshold": float(options["floor"]),
257
+ "gain": float(options["gain"]),
258
+ "kept_fraction": float(retained.mean()),
259
+ "channel_offset_r": float(offsets[0]),
260
+ "channel_offset_g": float(offsets[1]),
261
+ "channel_offset_b": float(offsets[2]),
262
+ }
263
+
264
+ @staticmethod
265
+ def _highpass(
266
+ actual: np.ndarray,
267
+ template: np.ndarray,
268
+ variant: str,
269
+ ) -> tuple[np.ndarray, dict[str, float]]:
270
+ options = VARIANTS[variant]
271
+ sigma = float(options["sigma"])
272
+ actual_gray = cv2.cvtColor(actual, cv2.COLOR_RGB2GRAY).astype(np.float32)
273
+ template_gray = cv2.cvtColor(template, cv2.COLOR_RGB2GRAY).astype(np.float32)
274
+ actual_darkness = cv2.GaussianBlur(actual_gray, (0, 0), sigma) - actual_gray
275
+ template_darkness = (
276
+ cv2.GaussianBlur(template_gray, (0, 0), sigma) - template_gray
277
+ )
278
+ signal = np.maximum(
279
+ actual_darkness - template_darkness - float(options["floor"]),
280
+ 0.0,
281
+ )
282
+ signal = np.clip(signal * float(options["gain"]), 0.0, 245.0)
283
+ raw_mask = np.uint8(signal >= 3.0)
284
+ count, labels, stats, _ = cv2.connectedComponentsWithStats(raw_mask, 8)
285
+ mask = np.zeros_like(raw_mask)
286
+ for component in range(1, count):
287
+ if int(stats[component, cv2.CC_STAT_AREA]) >= 2:
288
+ mask[labels == component] = 1
289
+ darkness = np.zeros_like(signal)
290
+ darkness[mask.astype(bool)] = signal[mask.astype(bool)]
291
+ darkness = cv2.dilate(
292
+ darkness,
293
+ cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (2, 2)),
294
+ iterations=1,
295
+ )
296
+ retained = darkness >= 3.0
297
+ value = np.uint8(np.clip(255.0 - darkness, 0.0, 255.0))
298
+ output = np.repeat(value[:, :, None], 3, axis=2)
299
+ return output, {
300
+ "threshold": float(options["floor"]),
301
+ "gain": float(options["gain"]),
302
+ "sigma": sigma,
303
+ "kept_fraction": float(retained.mean()),
304
+ }
305
+
306
+ @classmethod
307
+ def _render(
308
+ cls,
309
+ actual: np.ndarray,
310
+ template: np.ndarray,
311
+ variant: str,
312
+ ) -> tuple[np.ndarray, dict[str, float]]:
313
+ options = VARIANTS.get(variant)
314
+ if options is None:
315
+ raise ValueError(f"unknown isolation variant: {variant}")
316
+ delta = cls.difference(actual, template)
317
+ if options.get("mode") == "residual":
318
+ output, metrics = cls._residual(actual, template, variant)
319
+ elif options.get("mode") == "highpass":
320
+ output, metrics = cls._highpass(actual, template, variant)
321
+ else:
322
+ mask, threshold = cls._mask(delta, variant)
323
+ output = np.full_like(actual, 255)
324
+ output[mask] = actual[mask]
325
+ metrics = {
326
+ "threshold": threshold,
327
+ "kept_fraction": float(mask.mean()),
328
+ }
329
+ metrics.update(
330
+ {
331
+ "median_abs_delta": float(np.median(delta)),
332
+ "p90_abs_delta": float(np.percentile(delta, 90)),
333
+ }
334
+ )
335
+ return output, metrics
336
+
337
+ def isolate_with_template(
338
+ self,
339
+ image: Image.Image | np.ndarray,
340
+ template: Template,
341
+ variant: str,
342
+ ) -> tuple[Image.Image, dict[str, float]]:
343
+ actual = (
344
+ np.asarray(image.convert("RGB"))
345
+ if isinstance(image, Image.Image)
346
+ else np.asarray(image)
347
+ )
348
+ output, metrics = self._render(actual, template.image, variant)
349
+ return Image.fromarray(output, mode="RGB"), metrics
350
+
351
+ def isolate(
352
+ self,
353
+ image: Image.Image | np.ndarray,
354
+ variant: str,
355
+ ) -> tuple[Image.Image, TemplateMatch, dict[str, float]]:
356
+ match = self.match(image)
357
+ isolated, metrics = self.isolate_with_template(
358
+ image,
359
+ match.template,
360
+ variant,
361
+ )
362
+ return isolated, match, metrics
363
+
364
+ def isolate_crop(
365
+ self,
366
+ image: Image.Image | np.ndarray,
367
+ template_id: str,
368
+ crop_bbox: list[int] | tuple[int, int, int, int],
369
+ variant: str,
370
+ ) -> tuple[Image.Image, dict[str, float]]:
371
+ actual = (
372
+ np.asarray(image.convert("RGB"))
373
+ if isinstance(image, Image.Image)
374
+ else np.asarray(image)
375
+ )
376
+ template = self.by_id[template_id]
377
+ x0, y0, x1, y1 = (int(value) for value in crop_bbox)
378
+ background = template.image[y0:y1, x0:x1]
379
+ if background.shape != actual.shape:
380
+ background = cv2.resize(
381
+ background,
382
+ (actual.shape[1], actual.shape[0]),
383
+ interpolation=cv2.INTER_LANCZOS4,
384
+ )
385
+ output, metrics = self._render(actual, background, variant)
386
+ return Image.fromarray(output, mode="RGB"), metrics
387
+
388
+
389
+ def index_from_id(value: str) -> int:
390
+ match = re.fullmatch(r"handwriting:(\d+)", str(value))
391
+ if not match:
392
+ raise ValueError(f"invalid handwriting row id: {value}")
393
+ return int(match.group(1))
pipeline/decode_bina02_persian.py ADDED
@@ -0,0 +1,315 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import json
6
+ import math
7
+ import re
8
+ import sqlite3
9
+ import unicodedata
10
+ from collections import defaultdict
11
+ from functools import lru_cache
12
+ from pathlib import Path
13
+ from typing import Any
14
+
15
+ from rapidfuzz.distance import Levenshtein
16
+
17
+
18
+ TOKEN_RE = re.compile(r"[\u0600-\u06ff]+")
19
+ DIACRITICS_RE = re.compile(r"[\u064b-\u065f\u0670\u06d6-\u06ed]")
20
+
21
+
22
+ def parse_args() -> argparse.Namespace:
23
+ parser = argparse.ArgumentParser()
24
+ parser.add_argument("--database", type=Path, required=True)
25
+ parser.add_argument("--input", type=Path, required=True)
26
+ parser.add_argument("--output", type=Path, required=True)
27
+ parser.add_argument("--details", type=Path)
28
+ parser.add_argument("--minimum-frequency", type=int, default=2)
29
+ parser.add_argument("--frequent-without-context", type=int, default=250)
30
+ parser.add_argument("--minimum-margin", type=float, default=4.0)
31
+ return parser.parse_args()
32
+
33
+
34
+ def normalize_word(value: str) -> str:
35
+ word = unicodedata.normalize("NFKC", value or "")
36
+ for source, target in (
37
+ ("ي", "ی"),
38
+ ("ك", "ک"),
39
+ ("ۀ", "ه"),
40
+ ("ة", "ه"),
41
+ ("أ", "ا"),
42
+ ("إ", "ا"),
43
+ ("ٱ", "ا"),
44
+ ("آ", "ا"),
45
+ ("ؤ", "و"),
46
+ ("ئ", "ی"),
47
+ ("ى", "ی"),
48
+ ):
49
+ word = word.replace(source, target)
50
+ word = DIACRITICS_RE.sub("", word)
51
+ return (
52
+ word.replace("\u200c", "")
53
+ .replace("\u200d", "")
54
+ .replace("\u200b", "")
55
+ )
56
+
57
+
58
+ def one_deletes(word: str) -> set[str]:
59
+ return {
60
+ word[:index] + word[index + 1 :]
61
+ for index in range(len(word))
62
+ }
63
+
64
+
65
+ class PersianLexiconDecoder:
66
+ def __init__(
67
+ self,
68
+ database: Path,
69
+ *,
70
+ minimum_frequency: int,
71
+ frequent_without_context: int,
72
+ minimum_margin: float,
73
+ ) -> None:
74
+ uri = f"file:{database.resolve()}?mode=ro"
75
+ self.connection = sqlite3.connect(uri, uri=True)
76
+ self.minimum_frequency = minimum_frequency
77
+ self.frequent_without_context = frequent_without_context
78
+ self.minimum_margin = minimum_margin
79
+ self.words: dict[str, tuple[int, bool]] = {}
80
+ for word, frequency, hardword in self.connection.execute(
81
+ "SELECT word, frequency, hardword FROM unigram "
82
+ "WHERE frequency >= ? OR hardword = 1",
83
+ (minimum_frequency,),
84
+ ):
85
+ self.words[str(word)] = (int(frequency), bool(hardword))
86
+
87
+ self.delete_index: dict[str, list[str]] = defaultdict(list)
88
+ for word in self.words:
89
+ if not 3 <= len(word) <= 32:
90
+ continue
91
+ for deletion in one_deletes(word):
92
+ self.delete_index[deletion].append(word)
93
+
94
+ @lru_cache(maxsize=500_000)
95
+ def bigram_frequency(self, left: str, right: str) -> int:
96
+ row = self.connection.execute(
97
+ "SELECT frequency FROM bigram "
98
+ "WHERE left_word = ? AND right_word = ?",
99
+ (left, right),
100
+ ).fetchone()
101
+ return int(row[0]) if row else 0
102
+
103
+ @lru_cache(maxsize=500_000)
104
+ def candidates(self, word: str) -> tuple[str, ...]:
105
+ normalized = normalize_word(word)
106
+ possible: set[str] = set()
107
+ if normalized in self.words:
108
+ possible.add(normalized)
109
+ deletes = one_deletes(normalized)
110
+ for deletion in deletes:
111
+ if deletion in self.words:
112
+ possible.add(deletion)
113
+ for key in {normalized, *deletes}:
114
+ possible.update(self.delete_index.get(key, ()))
115
+ return tuple(possible)
116
+
117
+ def correction(
118
+ self,
119
+ word: str,
120
+ left: str | None,
121
+ right: str | None,
122
+ ) -> tuple[str, dict[str, Any] | None]:
123
+ normalized = normalize_word(word)
124
+ if len(normalized) < 3 or normalized in self.words:
125
+ return word, None
126
+
127
+ maximum_distance = 1 if len(normalized) <= 6 else 2
128
+ ranked: list[tuple[float, str, int, int, int, bool]] = []
129
+ for candidate in self.candidates(normalized):
130
+ distance = Levenshtein.distance(
131
+ normalized,
132
+ candidate,
133
+ score_cutoff=maximum_distance,
134
+ )
135
+ if distance > maximum_distance:
136
+ continue
137
+ frequency, hardword = self.words[candidate]
138
+ left_frequency = (
139
+ self.bigram_frequency(left, candidate) if left else 0
140
+ )
141
+ right_frequency = (
142
+ self.bigram_frequency(candidate, right) if right else 0
143
+ )
144
+ contextual = left_frequency + right_frequency
145
+ if distance == 2 and not (
146
+ contextual
147
+ or frequency >= 2 * self.frequent_without_context
148
+ ):
149
+ continue
150
+ if not contextual and (
151
+ frequency < self.frequent_without_context
152
+ ):
153
+ continue
154
+ score = (
155
+ 0.85 * math.log1p(frequency)
156
+ + 1.35 * math.log1p(left_frequency)
157
+ + 1.35 * math.log1p(right_frequency)
158
+ + (0.8 if hardword else 0.0)
159
+ - 3.8 * distance
160
+ )
161
+ ranked.append(
162
+ (
163
+ score,
164
+ candidate,
165
+ distance,
166
+ frequency,
167
+ contextual,
168
+ hardword,
169
+ )
170
+ )
171
+
172
+ if not ranked:
173
+ return word, None
174
+ ranked.sort(reverse=True)
175
+ best = ranked[0]
176
+ second_score = ranked[1][0] if len(ranked) > 1 else -math.inf
177
+ margin = best[0] - second_score
178
+ if margin < self.minimum_margin:
179
+ return word, None
180
+
181
+ replacement = best[1]
182
+ return replacement, {
183
+ "original": word,
184
+ "normalized": normalized,
185
+ "replacement": replacement,
186
+ "distance": best[2],
187
+ "frequency": best[3],
188
+ "context_frequency": best[4],
189
+ "hardword": best[5],
190
+ "margin": round(margin, 6)
191
+ if math.isfinite(margin)
192
+ else None,
193
+ }
194
+
195
+ def decode(self, text: str) -> tuple[str, list[dict[str, Any]]]:
196
+ matches = list(TOKEN_RE.finditer(text))
197
+ if not matches:
198
+ return text, []
199
+ normalized_words = [
200
+ normalize_word(match.group(0)) for match in matches
201
+ ]
202
+ replacements: list[str] = []
203
+ changes: list[dict[str, Any]] = []
204
+ for index, match in enumerate(matches):
205
+ left = normalized_words[index - 1] if index else None
206
+ right = (
207
+ normalized_words[index + 1]
208
+ if index + 1 < len(normalized_words)
209
+ else None
210
+ )
211
+ replacement, detail = self.correction(
212
+ match.group(0),
213
+ left,
214
+ right,
215
+ )
216
+ replacements.append(replacement)
217
+ if detail is not None:
218
+ detail["token_index"] = index
219
+ changes.append(detail)
220
+
221
+ output: list[str] = []
222
+ cursor = 0
223
+ for match, replacement in zip(matches, replacements):
224
+ output.append(text[cursor : match.start()])
225
+ output.append(replacement)
226
+ cursor = match.end()
227
+ output.append(text[cursor:])
228
+ return "".join(output), changes
229
+
230
+
231
+ def main() -> None:
232
+ args = parse_args()
233
+ decoder = PersianLexiconDecoder(
234
+ args.database,
235
+ minimum_frequency=args.minimum_frequency,
236
+ frequent_without_context=args.frequent_without_context,
237
+ minimum_margin=args.minimum_margin,
238
+ )
239
+ if args.output.exists():
240
+ raise FileExistsError(
241
+ f"Refusing to replace existing output: {args.output}"
242
+ )
243
+ if args.details and args.details.exists():
244
+ raise FileExistsError(
245
+ f"Refusing to replace existing details: {args.details}"
246
+ )
247
+
248
+ args.output.parent.mkdir(parents=True, exist_ok=True)
249
+ details_handle = (
250
+ args.details.open("w", encoding="utf-8")
251
+ if args.details
252
+ else None
253
+ )
254
+ rows = 0
255
+ changed_rows = 0
256
+ changed_tokens = 0
257
+ try:
258
+ with args.input.open(encoding="utf-8") as source:
259
+ with args.output.open("w", encoding="utf-8") as destination:
260
+ for line in source:
261
+ if not line.strip():
262
+ continue
263
+ row = json.loads(line)
264
+ prediction = str(
265
+ row.get(
266
+ "prediction",
267
+ row.get("hyp", row.get("hypothesis", "")),
268
+ )
269
+ or ""
270
+ )
271
+ decoded, changes = decoder.decode(prediction)
272
+ row["prediction_before_persian_decoder"] = prediction
273
+ row["prediction"] = decoded
274
+ row["persian_decoder_changes"] = len(changes)
275
+ destination.write(
276
+ json.dumps(row, ensure_ascii=False) + "\n"
277
+ )
278
+ rows += 1
279
+ if changes:
280
+ changed_rows += 1
281
+ changed_tokens += len(changes)
282
+ if details_handle is not None:
283
+ details_handle.write(
284
+ json.dumps(
285
+ {
286
+ "id": row.get("id"),
287
+ "split": row.get("split"),
288
+ "changes": changes,
289
+ },
290
+ ensure_ascii=False,
291
+ )
292
+ + "\n"
293
+ )
294
+ finally:
295
+ if details_handle is not None:
296
+ details_handle.close()
297
+ decoder.connection.close()
298
+
299
+ print(
300
+ json.dumps(
301
+ {
302
+ "rows": rows,
303
+ "changed_rows": changed_rows,
304
+ "changed_tokens": changed_tokens,
305
+ "lexicon_words": len(decoder.words),
306
+ "delete_keys": len(decoder.delete_index),
307
+ },
308
+ ensure_ascii=False,
309
+ indent=2,
310
+ )
311
+ )
312
+
313
+
314
+ if __name__ == "__main__":
315
+ main()
pipeline/detect_bina02_two_layer.py ADDED
@@ -0,0 +1,314 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Resumable Surya detection on template-subtracted handwriting pages."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import fcntl
8
+ import io
9
+ import json
10
+ import os
11
+ import time
12
+ import traceback
13
+ from pathlib import Path
14
+ from typing import Any
15
+
16
+ import numpy as np
17
+ import pyarrow.parquet as pq
18
+ from PIL import Image
19
+ from surya.detection import DetectionPredictor
20
+
21
+ from bina02_template_layers import TemplateCatalog
22
+
23
+
24
+ ROWS = 6_669
25
+
26
+
27
+ def parse_args() -> argparse.Namespace:
28
+ parser = argparse.ArgumentParser()
29
+ parser.add_argument("--rank", type=int, required=True)
30
+ parser.add_argument("--world-size", type=int, required=True)
31
+ parser.add_argument("--data", type=Path, required=True)
32
+ parser.add_argument("--background-root", type=Path, required=True)
33
+ parser.add_argument("--ocr-root", type=Path, required=True)
34
+ parser.add_argument("--output-dir", type=Path, required=True)
35
+ parser.add_argument("--variant", required=True)
36
+ parser.add_argument("--limit", type=int, default=ROWS)
37
+ parser.add_argument("--batch-size", type=int, default=8)
38
+ parser.add_argument("--progress-every", type=int, default=20)
39
+ return parser.parse_args()
40
+
41
+
42
+ def atomic_json(path: Path, payload: dict[str, Any]) -> None:
43
+ temporary = path.with_suffix(path.suffix + f".{os.getpid()}.tmp")
44
+ temporary.write_text(
45
+ json.dumps(payload, ensure_ascii=False, indent=2) + "\n",
46
+ encoding="utf-8",
47
+ )
48
+ os.replace(temporary, path)
49
+
50
+
51
+ def selected_indices(limit: int) -> list[int]:
52
+ if not 1 <= limit <= ROWS:
53
+ raise ValueError(f"limit must be in [1, {ROWS}]")
54
+ if limit == ROWS:
55
+ return list(range(ROWS))
56
+ return sorted(
57
+ {
58
+ int(round(value))
59
+ for value in np.linspace(0, ROWS - 1, num=limit)
60
+ }
61
+ )
62
+
63
+
64
+ def load_rows(data: Path, wanted: set[int]) -> dict[int, dict[str, Any]]:
65
+ rows: dict[int, dict[str, Any]] = {}
66
+ offset = 0
67
+ paths = sorted((data / "data").glob("handwriting-*.parquet"))
68
+ if len(paths) != 14:
69
+ raise RuntimeError(f"expected 14 handwriting shards, found {len(paths)}")
70
+ for path in paths:
71
+ count = pq.ParquetFile(path).metadata.num_rows
72
+ local = sorted(index - offset for index in wanted if offset <= index < offset + count)
73
+ if local:
74
+ table = pq.read_table(path, columns=["image", "label"])
75
+ for index in local:
76
+ rows[offset + index] = table.slice(index, 1).to_pylist()[0]
77
+ offset += count
78
+ if offset != ROWS or rows.keys() != wanted:
79
+ raise RuntimeError(
80
+ f"benchmark row contract failed: total={offset}, loaded={len(rows)}, wanted={len(wanted)}"
81
+ )
82
+ return rows
83
+
84
+
85
+ def decode_image(row: dict[str, Any]) -> Image.Image:
86
+ value = row["image"]
87
+ encoded = value.get("bytes") if isinstance(value, dict) else value
88
+ if encoded is None and isinstance(value, dict) and value.get("path"):
89
+ encoded = Path(value["path"]).read_bytes()
90
+ if not encoded:
91
+ raise ValueError("dataset row has no image bytes")
92
+ return Image.open(io.BytesIO(encoded)).convert("RGB")
93
+
94
+
95
+ def line_payload(result: Any) -> list[dict[str, Any]]:
96
+ lines = []
97
+ for item in getattr(result, "bboxes", []):
98
+ bbox = getattr(item, "bbox", None)
99
+ if bbox is None or len(bbox) != 4:
100
+ continue
101
+ polygon = getattr(item, "polygon", None)
102
+ lines.append(
103
+ {
104
+ "bbox": [float(value) for value in bbox],
105
+ "confidence": float(getattr(item, "confidence", 0.0)),
106
+ "polygon": (
107
+ [[float(value) for value in point] for point in polygon]
108
+ if polygon is not None
109
+ else None
110
+ ),
111
+ }
112
+ )
113
+ return lines
114
+
115
+
116
+ def load_done(path: Path) -> set[str]:
117
+ if not path.is_file():
118
+ return set()
119
+ with path.open(encoding="utf-8") as handle:
120
+ return {
121
+ str(row["id"])
122
+ for line in handle
123
+ if line.strip()
124
+ for row in [json.loads(line)]
125
+ }
126
+
127
+
128
+ def merge_if_complete(
129
+ output_dir: Path,
130
+ world_size: int,
131
+ selected: list[int],
132
+ variant: str,
133
+ ) -> None:
134
+ with (output_dir / "merge.lock").open("a+") as lock:
135
+ fcntl.flock(lock, fcntl.LOCK_EX)
136
+ statuses = []
137
+ for rank in range(world_size):
138
+ path = output_dir / f"status-rank{rank:02d}.json"
139
+ if not path.is_file():
140
+ return
141
+ value = json.loads(path.read_text(encoding="utf-8"))
142
+ if value.get("state") != "complete":
143
+ return
144
+ statuses.append(value)
145
+ rows: dict[str, dict[str, Any]] = {}
146
+ duplicates = []
147
+ for rank in range(world_size):
148
+ path = output_dir / f"detections-rank{rank:02d}.jsonl"
149
+ with path.open(encoding="utf-8") as handle:
150
+ for line in handle:
151
+ row = json.loads(line)
152
+ row_id = str(row["id"])
153
+ if row_id in rows:
154
+ duplicates.append(row_id)
155
+ rows[row_id] = row
156
+ expected = [f"handwriting:{index}" for index in selected]
157
+ missing = sorted(set(expected) - rows.keys())
158
+ extras = sorted(rows.keys() - set(expected))
159
+ if duplicates or missing or extras:
160
+ raise RuntimeError(
161
+ f"detection merge failed: duplicates={duplicates[:3]} missing={missing[:3]} extras={extras[:3]}"
162
+ )
163
+ temporary = output_dir / "detections.jsonl.tmp"
164
+ with temporary.open("w", encoding="utf-8") as handle:
165
+ for row_id in expected:
166
+ handle.write(json.dumps(rows[row_id], ensure_ascii=False) + "\n")
167
+ os.replace(temporary, output_dir / "detections.jsonl")
168
+ atomic_json(
169
+ output_dir / "run-summary.json",
170
+ {
171
+ "state": "complete",
172
+ "variant": variant,
173
+ "selected_indices": selected,
174
+ "rows": len(rows),
175
+ "failed_pages": sum(row["failures"] for row in statuses),
176
+ "template_match_failures": sum(
177
+ row["template_match_failures"] for row in statuses
178
+ ),
179
+ "detected_lines": sum(row["detected_lines"] for row in statuses),
180
+ "completed_unix": time.time(),
181
+ },
182
+ )
183
+
184
+
185
+ def main() -> None:
186
+ args = parse_args()
187
+ selected = selected_indices(args.limit)
188
+ assigned = selected[args.rank :: args.world_size]
189
+ output_path = args.output_dir / f"detections-rank{args.rank:02d}.jsonl"
190
+ status_path = args.output_dir / f"status-rank{args.rank:02d}.json"
191
+ args.output_dir.mkdir(parents=True, exist_ok=True)
192
+ done = load_done(output_path)
193
+ wanted = {
194
+ index for index in assigned if f"handwriting:{index}" not in done
195
+ }
196
+ rows = load_rows(args.data, wanted) if wanted else {}
197
+ catalog = TemplateCatalog(args.background_root, args.ocr_root)
198
+ predictor = DetectionPredictor.local(device="cuda")
199
+ started = time.monotonic()
200
+ completed = 0
201
+ failures = 0
202
+ match_failures = 0
203
+ detected_lines = 0
204
+ pending: list[tuple[int, dict[str, Any], Image.Image, dict[str, Any]]] = []
205
+
206
+ with output_path.open("a", encoding="utf-8", buffering=1) as output:
207
+ def flush() -> None:
208
+ nonlocal completed, failures, detected_lines
209
+ if not pending:
210
+ return
211
+ images = [item[2] for item in pending]
212
+ try:
213
+ results: list[Any | Exception] = list(predictor(images))
214
+ if len(results) != len(images):
215
+ raise RuntimeError("detector returned wrong result count")
216
+ except Exception:
217
+ traceback.print_exc()
218
+ results = []
219
+ for image in images:
220
+ try:
221
+ results.append(list(predictor([image]))[0])
222
+ except Exception as exc:
223
+ results.append(exc)
224
+ for (index, row, image, layer), result in zip(pending, results):
225
+ lines = []
226
+ error = None
227
+ if isinstance(result, Exception):
228
+ failures += 1
229
+ error = f"{type(result).__name__}: {result}"
230
+ else:
231
+ lines = line_payload(result)
232
+ detected_lines += len(lines)
233
+ payload = {
234
+ "id": f"handwriting:{index}",
235
+ "split": "handwriting",
236
+ "reference": str(row.get("label") or ""),
237
+ "width": image.width,
238
+ "height": image.height,
239
+ "variant": args.variant,
240
+ "lines": lines,
241
+ **layer,
242
+ }
243
+ if error:
244
+ payload["error"] = error
245
+ output.write(json.dumps(payload, ensure_ascii=False) + "\n")
246
+ completed += 1
247
+ pending.clear()
248
+
249
+ for index in assigned:
250
+ if index not in wanted:
251
+ continue
252
+ row = rows[index]
253
+ try:
254
+ image = decode_image(row)
255
+ isolated, match, metrics = catalog.isolate(image, args.variant)
256
+ layer = {
257
+ "template_id": match.template.id,
258
+ "template_file": match.template.file,
259
+ "template_text": match.template.template_text,
260
+ "template_placeholder_count": match.template.template_text.count(
261
+ "{{HANDWRITING}}"
262
+ ),
263
+ "match_median_abs_delta": match.median_abs_delta,
264
+ "match_p90_abs_delta": match.p90_abs_delta,
265
+ "mask_threshold": metrics["threshold"],
266
+ "mask_kept_fraction": metrics["kept_fraction"],
267
+ }
268
+ pending.append((index, row, isolated, layer))
269
+ if len(pending) >= args.batch_size:
270
+ flush()
271
+ except Exception as exc:
272
+ traceback.print_exc()
273
+ failures += 1
274
+ match_failures += 1
275
+ completed += 1
276
+ output.write(
277
+ json.dumps(
278
+ {
279
+ "id": f"handwriting:{index}",
280
+ "split": "handwriting",
281
+ "reference": str(row.get("label") or ""),
282
+ "width": 0,
283
+ "height": 0,
284
+ "variant": args.variant,
285
+ "lines": [],
286
+ "error": f"{type(exc).__name__}: {exc}",
287
+ },
288
+ ensure_ascii=False,
289
+ )
290
+ + "\n"
291
+ )
292
+ flush()
293
+
294
+ elapsed = max(time.monotonic() - started, 1e-9)
295
+ atomic_json(
296
+ status_path,
297
+ {
298
+ "state": "complete",
299
+ "rank": args.rank,
300
+ "assigned": len(assigned),
301
+ "completed": completed,
302
+ "failures": failures,
303
+ "template_match_failures": match_failures,
304
+ "detected_lines": detected_lines,
305
+ "rate_pages_per_second": completed / elapsed,
306
+ "eta_seconds": 0,
307
+ "updated_unix": time.time(),
308
+ },
309
+ )
310
+ merge_if_complete(args.output_dir, args.world_size, selected, args.variant)
311
+
312
+
313
+ if __name__ == "__main__":
314
+ main()
pipeline/recognize_bina02_two_layer.py ADDED
@@ -0,0 +1,376 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Recognize cached Surya boxes from the same isolated handwriting layer."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import fcntl
8
+ import io
9
+ import json
10
+ import os
11
+ import re
12
+ import time
13
+ import traceback
14
+ from pathlib import Path
15
+ from typing import Any
16
+
17
+ import numpy as np
18
+ import pyarrow.parquet as pq
19
+ from paddleocr import TextRecognition
20
+ from PIL import Image
21
+
22
+ from bina02_template_layers import TemplateCatalog, index_from_id
23
+
24
+
25
+ ROWS = 6_669
26
+ LTR_RUN = re.compile(r"[a-zA-Z0-9 :*./%+-]")
27
+
28
+
29
+ def parse_args() -> argparse.Namespace:
30
+ parser = argparse.ArgumentParser()
31
+ parser.add_argument("--rank", type=int, required=True)
32
+ parser.add_argument("--world-size", type=int, required=True)
33
+ parser.add_argument("--data", type=Path, required=True)
34
+ parser.add_argument("--background-root", type=Path, required=True)
35
+ parser.add_argument("--ocr-root", type=Path, required=True)
36
+ parser.add_argument("--detections-dir", type=Path, required=True)
37
+ parser.add_argument("--model-dir", type=Path, required=True)
38
+ parser.add_argument("--output-dir", type=Path, required=True)
39
+ parser.add_argument("--variant", required=True)
40
+ parser.add_argument("--device", default="gpu:0")
41
+ parser.add_argument("--crop-buffer", type=int, default=512)
42
+ parser.add_argument("--recognition-batch-size", type=int, default=64)
43
+ parser.add_argument("--pad-x-ratio", type=float, default=0.01)
44
+ parser.add_argument("--pad-y-ratio", type=float, default=0.08)
45
+ parser.add_argument("--row-factor", type=float, default=0.70)
46
+ return parser.parse_args()
47
+
48
+
49
+ def atomic_json(path: Path, payload: dict[str, Any]) -> None:
50
+ temporary = path.with_suffix(path.suffix + f".{os.getpid()}.tmp")
51
+ temporary.write_text(
52
+ json.dumps(payload, ensure_ascii=False, indent=2) + "\n",
53
+ encoding="utf-8",
54
+ )
55
+ os.replace(temporary, path)
56
+
57
+
58
+ def pred_reverse(text: str) -> str:
59
+ segments: list[str] = []
60
+ current = ""
61
+ for character in text:
62
+ if LTR_RUN.search(character):
63
+ current += character
64
+ else:
65
+ if current:
66
+ segments.append(current)
67
+ current = ""
68
+ segments.append(character)
69
+ if current:
70
+ segments.append(current)
71
+ return "".join(reversed(segments))
72
+
73
+
74
+ def load_rows(data: Path, wanted: set[int]) -> dict[int, dict[str, Any]]:
75
+ rows: dict[int, dict[str, Any]] = {}
76
+ offset = 0
77
+ paths = sorted((data / "data").glob("handwriting-*.parquet"))
78
+ for path in paths:
79
+ count = pq.ParquetFile(path).metadata.num_rows
80
+ local = sorted(index - offset for index in wanted if offset <= index < offset + count)
81
+ if local:
82
+ table = pq.read_table(path, columns=["image", "label"])
83
+ for index in local:
84
+ rows[offset + index] = table.slice(index, 1).to_pylist()[0]
85
+ offset += count
86
+ if offset != ROWS or rows.keys() != wanted:
87
+ raise RuntimeError("benchmark row load contract failed")
88
+ return rows
89
+
90
+
91
+ def decode_image(row: dict[str, Any]) -> Image.Image:
92
+ value = row["image"]
93
+ encoded = value.get("bytes") if isinstance(value, dict) else value
94
+ if encoded is None and isinstance(value, dict) and value.get("path"):
95
+ encoded = Path(value["path"]).read_bytes()
96
+ if not encoded:
97
+ raise ValueError("dataset row has no image bytes")
98
+ return Image.open(io.BytesIO(encoded)).convert("RGB")
99
+
100
+
101
+ def center(line: dict[str, Any]) -> tuple[float, float]:
102
+ x0, y0, x1, y1 = (float(value) for value in line["bbox"])
103
+ return (x0 + x1) / 2.0, (y0 + y1) / 2.0
104
+
105
+
106
+ def height(line: dict[str, Any]) -> float:
107
+ return max(float(line["bbox"][3]) - float(line["bbox"][1]), 1.0)
108
+
109
+
110
+ def group_rows(lines: list[dict[str, Any]], factor: float) -> list[list[dict[str, Any]]]:
111
+ ordered = sorted(lines, key=lambda line: center(line)[1])
112
+ rows: list[list[dict[str, Any]]] = []
113
+ for line in ordered:
114
+ if not rows:
115
+ rows.append([line])
116
+ continue
117
+ previous = rows[-1]
118
+ mean_y = sum(center(item)[1] for item in previous) / len(previous)
119
+ mean_height = sum(height(item) for item in previous) / len(previous)
120
+ if abs(center(line)[1] - mean_y) <= factor * max(height(line), mean_height):
121
+ previous.append(line)
122
+ else:
123
+ rows.append([line])
124
+ for row in rows:
125
+ row.sort(key=lambda line: center(line)[0], reverse=True)
126
+ return rows
127
+
128
+
129
+ def grouped_lines(
130
+ lines: list[dict[str, Any]],
131
+ width: int,
132
+ height_value: int,
133
+ factor: float,
134
+ ) -> list[dict[str, Any]]:
135
+ aspect = width / max(height_value, 1)
136
+ if 1.03 <= aspect <= 1.42:
137
+ midpoint = width / 2.0
138
+ groups = group_rows(
139
+ [line for line in lines if center(line)[0] >= midpoint],
140
+ factor,
141
+ ) + group_rows(
142
+ [line for line in lines if center(line)[0] < midpoint],
143
+ factor,
144
+ )
145
+ else:
146
+ groups = group_rows(lines, factor)
147
+ return [
148
+ {
149
+ "bbox": [
150
+ min(float(line["bbox"][0]) for line in row),
151
+ min(float(line["bbox"][1]) for line in row),
152
+ max(float(line["bbox"][2]) for line in row),
153
+ max(float(line["bbox"][3]) for line in row),
154
+ ],
155
+ "confidence": sum(float(line.get("confidence", 0.0)) for line in row)
156
+ / len(row),
157
+ "member_count": len(row),
158
+ }
159
+ for row in groups
160
+ ]
161
+
162
+
163
+ def crop(
164
+ image: Image.Image,
165
+ bbox: list[float],
166
+ pad_x_ratio: float,
167
+ pad_y_ratio: float,
168
+ ) -> np.ndarray:
169
+ x0, y0, x1, y1 = (float(value) for value in bbox)
170
+ width = max(x1 - x0, 1.0)
171
+ height_value = max(y1 - y0, 1.0)
172
+ pad_x = max(2, int(width * pad_x_ratio))
173
+ pad_y = max(2, int(height_value * pad_y_ratio))
174
+ box = (
175
+ max(0, int(x0) - pad_x),
176
+ max(0, int(y0) - pad_y),
177
+ min(image.width, int(x1) + pad_x),
178
+ min(image.height, int(y1) + pad_y),
179
+ )
180
+ if box[2] <= box[0] or box[3] <= box[1]:
181
+ raise ValueError(f"invalid crop: {bbox}")
182
+ return np.asarray(image.crop(box).convert("RGB"))
183
+
184
+
185
+ def recognize(
186
+ model: TextRecognition,
187
+ crops: list[np.ndarray],
188
+ batch_size: int,
189
+ ) -> list[dict[str, Any]]:
190
+ if not crops:
191
+ return []
192
+ last_error: Exception | None = None
193
+ for attempt in range(1, 4):
194
+ try:
195
+ results = list(model.predict(input=crops, batch_size=batch_size))
196
+ if len(results) != len(crops):
197
+ raise RuntimeError("recognizer returned wrong result count")
198
+ output = []
199
+ for result in results:
200
+ payload = result.json
201
+ if callable(payload):
202
+ payload = payload()
203
+ payload = payload.get("res", payload)
204
+ raw = str(payload.get("rec_text", ""))
205
+ output.append(
206
+ {
207
+ "text": pred_reverse(raw),
208
+ "raw_text": raw,
209
+ "rec_score": float(payload.get("rec_score", 0.0)),
210
+ }
211
+ )
212
+ return output
213
+ except Exception as exc:
214
+ last_error = exc
215
+ traceback.print_exc()
216
+ time.sleep(attempt)
217
+ raise RuntimeError(f"recognition failed after retries: {last_error}")
218
+
219
+
220
+ def merge_if_complete(output_dir: Path, detections_dir: Path, world_size: int) -> None:
221
+ with (output_dir / "merge.lock").open("a+") as lock:
222
+ fcntl.flock(lock, fcntl.LOCK_EX)
223
+ for rank in range(world_size):
224
+ path = output_dir / f"status-rank{rank:02d}.json"
225
+ if not path.is_file() or json.loads(path.read_text()).get("state") != "complete":
226
+ return
227
+ expected = [
228
+ json.loads(line)["id"]
229
+ for line in (detections_dir / "detections.jsonl").read_text(
230
+ encoding="utf-8"
231
+ ).splitlines()
232
+ if line.strip()
233
+ ]
234
+ rows = {}
235
+ for rank in range(world_size):
236
+ path = output_dir / f"recognized-rank{rank:02d}.jsonl"
237
+ for line in path.read_text(encoding="utf-8").splitlines():
238
+ if line.strip():
239
+ row = json.loads(line)
240
+ rows[row["id"]] = row
241
+ if set(rows) != set(expected):
242
+ raise RuntimeError("recognition merge contract failed")
243
+ temporary = output_dir / "recognized-lines.jsonl.tmp"
244
+ with temporary.open("w", encoding="utf-8") as handle:
245
+ for row_id in expected:
246
+ handle.write(json.dumps(rows[row_id], ensure_ascii=False) + "\n")
247
+ os.replace(temporary, output_dir / "recognized-lines.jsonl")
248
+ atomic_json(
249
+ output_dir / "run-summary.json",
250
+ {"state": "complete", "rows": len(rows), "completed_unix": time.time()},
251
+ )
252
+
253
+
254
+ def main() -> None:
255
+ args = parse_args()
256
+ args.output_dir.mkdir(parents=True, exist_ok=True)
257
+ detections_path = args.detections_dir / f"detections-rank{args.rank:02d}.jsonl"
258
+ detections = [
259
+ json.loads(line)
260
+ for line in detections_path.read_text(encoding="utf-8").splitlines()
261
+ if line.strip()
262
+ ]
263
+ indices = {index_from_id(row["id"]) for row in detections}
264
+ rows = load_rows(args.data, indices)
265
+ catalog = TemplateCatalog(args.background_root, args.ocr_root)
266
+ model = TextRecognition(model_dir=str(args.model_dir), device=args.device)
267
+ output_path = args.output_dir / f"recognized-rank{args.rank:02d}.jsonl"
268
+ status_path = args.output_dir / f"status-rank{args.rank:02d}.json"
269
+ started = time.monotonic()
270
+ failures = 0
271
+ recognized_lines = 0
272
+ page_buffer: list[dict[str, Any]] = []
273
+ crop_buffer: list[np.ndarray] = []
274
+
275
+ with output_path.open("w", encoding="utf-8", buffering=1) as output:
276
+ def flush() -> None:
277
+ nonlocal failures, recognized_lines
278
+ if not page_buffer:
279
+ return
280
+ try:
281
+ values = recognize(model, crop_buffer, args.recognition_batch_size)
282
+ recognition_error = None
283
+ except Exception as exc:
284
+ values = [
285
+ {"text": "", "raw_text": "", "rec_score": 0.0}
286
+ for _ in crop_buffer
287
+ ]
288
+ recognition_error = f"{type(exc).__name__}: {exc}"
289
+ for page in page_buffer:
290
+ if recognition_error and not page.get("error"):
291
+ page["error"] = recognition_error
292
+ if page.get("error"):
293
+ failures += 1
294
+ for line in page["lines"]:
295
+ result_index = line.pop("_result_index", None)
296
+ if result_index is not None:
297
+ line.update(values[result_index])
298
+ recognized_lines += 1
299
+ output.write(json.dumps(page, ensure_ascii=False) + "\n")
300
+ page_buffer.clear()
301
+ crop_buffer.clear()
302
+
303
+ for detection in detections:
304
+ index = index_from_id(detection["id"])
305
+ page = {
306
+ key: detection.get(key)
307
+ for key in (
308
+ "id",
309
+ "reference",
310
+ "width",
311
+ "height",
312
+ "variant",
313
+ "template_id",
314
+ "template_file",
315
+ "template_text",
316
+ "template_placeholder_count",
317
+ "match_median_abs_delta",
318
+ "match_p90_abs_delta",
319
+ "mask_threshold",
320
+ "mask_kept_fraction",
321
+ )
322
+ }
323
+ page["lines"] = []
324
+ if detection.get("error"):
325
+ page["error"] = detection["error"]
326
+ try:
327
+ image = decode_image(rows[index])
328
+ template = catalog.by_id[str(detection["template_id"])]
329
+ isolated, _ = catalog.isolate_with_template(
330
+ image,
331
+ template,
332
+ args.variant,
333
+ )
334
+ for source in grouped_lines(
335
+ detection.get("lines", []),
336
+ isolated.width,
337
+ isolated.height,
338
+ args.row_factor,
339
+ ):
340
+ line = dict(source)
341
+ line["_result_index"] = len(crop_buffer)
342
+ crop_buffer.append(
343
+ crop(
344
+ isolated,
345
+ source["bbox"],
346
+ args.pad_x_ratio,
347
+ args.pad_y_ratio,
348
+ )
349
+ )
350
+ page["lines"].append(line)
351
+ except Exception as exc:
352
+ traceback.print_exc()
353
+ page["error"] = f"{type(exc).__name__}: {exc}"
354
+ page_buffer.append(page)
355
+ if len(crop_buffer) >= args.crop_buffer:
356
+ flush()
357
+ flush()
358
+
359
+ elapsed = max(time.monotonic() - started, 1e-9)
360
+ atomic_json(
361
+ status_path,
362
+ {
363
+ "state": "complete",
364
+ "rank": args.rank,
365
+ "assigned": len(detections),
366
+ "failures": failures,
367
+ "recognized_lines": recognized_lines,
368
+ "rate_pages_per_second": len(detections) / elapsed,
369
+ "eta_seconds": 0,
370
+ },
371
+ )
372
+ merge_if_complete(args.output_dir, args.detections_dir, args.world_size)
373
+
374
+
375
+ if __name__ == "__main__":
376
+ main()
pipeline/run_bina02_stage8_optimizer_resume_v2.sh ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ set -Eeuo pipefail
3
+
4
+ BASE=/workspace/bina02-ppocr
5
+ PADDLE_ROOT="$BASE/PaddleOCR"
6
+ PYTHON="$BASE/venv/bin/python"
7
+ V1_RUN="$BASE/run/train-stage8-optimizer-resume-v1"
8
+ RUN="$BASE/run/train-stage8-optimizer-resume-v2"
9
+ CHECKPOINT="$RUN/resume-stage4"
10
+ TRAIN_PID=
11
+ WATCH_PID=
12
+
13
+ mkdir -p "$RUN/distributed-logs"
14
+
15
+ cleanup() {
16
+ local status=$?
17
+ trap - EXIT TERM INT
18
+
19
+ if [[ -n "${WATCH_PID:-}" ]] && kill -0 "$WATCH_PID" 2>/dev/null; then
20
+ kill -TERM "$WATCH_PID" 2>/dev/null || true
21
+ wait "$WATCH_PID" 2>/dev/null || true
22
+ fi
23
+
24
+ if [[ -n "${TRAIN_PID:-}" ]] && kill -0 "$TRAIN_PID" 2>/dev/null; then
25
+ kill -TERM -- "-$TRAIN_PID" 2>/dev/null || true
26
+ for _ in $(seq 1 20); do
27
+ kill -0 "$TRAIN_PID" 2>/dev/null || break
28
+ sleep 1
29
+ done
30
+ if kill -0 "$TRAIN_PID" 2>/dev/null; then
31
+ kill -KILL -- "-$TRAIN_PID" 2>/dev/null || true
32
+ fi
33
+ wait "$TRAIN_PID" 2>/dev/null || true
34
+ fi
35
+
36
+ exit "$status"
37
+ }
38
+ trap cleanup EXIT TERM INT
39
+
40
+ export V1_RUN RUN CHECKPOINT
41
+ "$PYTHON" - <<'PY'
42
+ import json
43
+ import os
44
+ import pathlib
45
+ import shutil
46
+
47
+ import paddle
48
+ import yaml
49
+
50
+ v1 = pathlib.Path(os.environ["V1_RUN"])
51
+ run = pathlib.Path(os.environ["RUN"])
52
+ checkpoint = pathlib.Path(os.environ["CHECKPOINT"])
53
+
54
+ source_params = v1 / "resume-stage4.pdparams"
55
+ source_opt = v1 / "resume-stage4.pdopt"
56
+ source_states = v1 / "resume-stage4.states"
57
+ for source in (source_params, source_opt, source_states, v1 / "launch-config.yml"):
58
+ if not source.is_file():
59
+ raise FileNotFoundError(source)
60
+
61
+ target_params = pathlib.Path(f"{checkpoint}.pdparams")
62
+ target_opt = pathlib.Path(f"{checkpoint}.pdopt")
63
+ target_states = pathlib.Path(f"{checkpoint}.states")
64
+
65
+ if not target_params.exists():
66
+ os.link(source_params, target_params)
67
+ if not target_states.exists():
68
+ shutil.copy2(source_states, target_states)
69
+
70
+ if not target_opt.exists():
71
+ paddle.set_device("cpu")
72
+ optimizer_state = paddle.load(str(source_opt))
73
+ scheduler = optimizer_state.get("LR_Scheduler")
74
+ if not isinstance(scheduler, dict):
75
+ raise RuntimeError("Stage 4 optimizer has no LR_Scheduler state")
76
+
77
+ before = dict(scheduler)
78
+ scheduler["last_epoch"] = 0
79
+ scheduler["last_lr"] = 3.0e-6
80
+ paddle.save(optimizer_state, str(target_opt))
81
+
82
+ with (run / "optimizer-patch.json").open("w", encoding="utf-8") as handle:
83
+ json.dump(
84
+ {
85
+ "source": str(source_opt),
86
+ "preserved_optimizer_entries": len(optimizer_state) - 1,
87
+ "scheduler_before": before,
88
+ "scheduler_after": dict(scheduler),
89
+ },
90
+ handle,
91
+ ensure_ascii=True,
92
+ indent=2,
93
+ sort_keys=True,
94
+ )
95
+
96
+ with (v1 / "launch-config.yml").open("r", encoding="utf-8") as handle:
97
+ config = yaml.safe_load(handle)
98
+ config["Global"]["save_model_dir"] = str(run)
99
+ config["Global"]["checkpoints"] = str(checkpoint)
100
+ config["Global"]["pretrained_model"] = None
101
+ config["Global"]["epoch_num"] = 3
102
+ with (run / "launch-config.yml").open("w", encoding="utf-8") as handle:
103
+ yaml.safe_dump(config, handle, allow_unicode=True, sort_keys=False)
104
+ PY
105
+
106
+ cd "$PADDLE_ROOT"
107
+ export PYTHONPATH="$PADDLE_ROOT:${PYTHONPATH:-}"
108
+
109
+ setsid "$PYTHON" -m paddle.distributed.launch \
110
+ --gpus 0,1,2,3,4,5,6,7 \
111
+ --log_dir "$RUN/distributed-logs" \
112
+ tools/train.py \
113
+ -c "$RUN/launch-config.yml" \
114
+ >"$RUN/train-launch.log" 2>&1 &
115
+ TRAIN_PID=$!
116
+
117
+ "$PYTHON" "$BASE/watch_bina02_stage4_eval.py" \
118
+ --log "$RUN/train-launch.log" \
119
+ --pgid "$TRAIN_PID" \
120
+ --state "$RUN/eval-gate-state.json" \
121
+ --baseline-handwriting 0.9296666392340331 \
122
+ --baseline-printed 0.8944015643799841 \
123
+ --handwriting-margin 0.002 \
124
+ --printed-margin 0.002 \
125
+ --patience 2 \
126
+ --poll-seconds 5 \
127
+ >"$RUN/watcher.log" 2>&1 &
128
+ WATCH_PID=$!
129
+
130
+ set +e
131
+ wait "$TRAIN_PID"
132
+ TRAIN_STATUS=$?
133
+ set -e
134
+
135
+ if kill -0 "$WATCH_PID" 2>/dev/null; then
136
+ kill -TERM "$WATCH_PID" 2>/dev/null || true
137
+ wait "$WATCH_PID" 2>/dev/null || true
138
+ fi
139
+ WATCH_PID=
140
+ TRAIN_PID=
141
+
142
+ printf 'train_exit_code=%s\nfinished_at=%s\n' \
143
+ "$TRAIN_STATUS" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" >"$RUN/status.log"
144
+ exit "$TRAIN_STATUS"
pipeline/run_bina02_stage8_v2_exact.sh ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ set -Eeuo pipefail
3
+
4
+ ROOT=/workspace/bina02-ppocr
5
+ TRAIN="$ROOT/run/train-stage8-optimizer-resume-v2"
6
+ POST="$ROOT/run/posttrain-stage8-optimizer-resume-v2"
7
+ MODEL="$POST/inference-best"
8
+
9
+ if [[ ! -s "$POST/INFERENCE_READY" ]]; then
10
+ test -s "$TRAIN/best_accuracy.pdparams"
11
+ test -s "$TRAIN/best_accuracy.states"
12
+ mkdir -p "$POST"
13
+ cd "$ROOT/PaddleOCR"
14
+ export PYTHONPATH="$ROOT/PaddleOCR:$ROOT${PYTHONPATH:+:$PYTHONPATH}"
15
+ CUDA_VISIBLE_DEVICES=0 OMP_NUM_THREADS=4 \
16
+ "$ROOT/venv/bin/python" tools/export_model.py \
17
+ -c "$TRAIN/config.yml" \
18
+ -o "Global.pretrained_model=$TRAIN/best_accuracy" \
19
+ "Global.save_inference_dir=$MODEL" \
20
+ > "$POST/export.log" 2>&1
21
+ test -s "$MODEL/inference.pdiparams"
22
+ test -s "$MODEL/inference.json"
23
+ printf 'READY\n' > "$POST/INFERENCE_READY"
24
+ fi
25
+
26
+ exec /bin/bash "$ROOT/run_bina02_stage8_v2_exact_core.sh"
pipeline/run_bina02_stage8_v2_exact_core.sh ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT=/workspace/bina02-ppocr
5
+ RUN="$ROOT/run/stage8-v2-two-layer-exact-v1"
6
+ DATA="$ROOT/direct/benchmark-data"
7
+ SUPPORT="$ROOT/data/generator-support-v1-hf/work"
8
+ MODEL="$ROOT/run/posttrain-stage8-optimizer-resume-v2/inference-best"
9
+ SURYA_ENV=/workspace/bina02-surya-env
10
+ VARIANT=adaptive75
11
+ DET="$RUN/detection"
12
+ REC="$RUN/recognition"
13
+ ASM="$RUN/assembly"
14
+ PRINTED="$RUN/printed"
15
+ SCORES="$RUN/scores"
16
+
17
+ if test -s "$SCORES/exact-summary.json"; then
18
+ cat "$SCORES/exact-summary.json"
19
+ exit 0
20
+ fi
21
+ test -f "$ROOT/run/posttrain-stage8-optimizer-resume-v2/INFERENCE_READY"
22
+ mkdir -p "$RUN" "$DET" "$REC" "$ASM" "$PRINTED" "$SCORES"
23
+ printf '%s EXACT_START\n' "$(date -Is)" > "$RUN/status.log"
24
+
25
+ if [[ ! -s "$DET/detections.jsonl" ]]; then
26
+ pids=()
27
+ for rank in $(seq 0 31); do
28
+ gpu=$((rank % 8))
29
+ HF_HOME=/workspace/.hf_home CUDA_VISIBLE_DEVICES="$gpu" DETECTOR_BATCH_SIZE=8 \
30
+ "$SURYA_ENV/bin/python" "$ROOT/detect_bina02_two_layer.py" \
31
+ --rank "$rank" --world-size 32 \
32
+ --data "$DATA" \
33
+ --background-root "$SUPPORT/background-sources" \
34
+ --ocr-root "$SUPPORT/background-ocr-single" \
35
+ --output-dir "$DET" \
36
+ --variant "$VARIANT" \
37
+ --limit 6669 \
38
+ --batch-size 8 \
39
+ > "$DET/rank${rank}.log" 2>&1 &
40
+ pids+=("$!")
41
+ done
42
+ for pid in "${pids[@]}"; do wait "$pid"; done
43
+ test -s "$DET/detections.jsonl"
44
+ fi
45
+ test -s "$DET/detections.jsonl"
46
+ printf '%s DETECTION_COMPLETE\n' "$(date -Is)" >> "$RUN/status.log"
47
+
48
+ export PYTHONPATH="$ROOT/PaddleOCR:$ROOT${PYTHONPATH:+:$PYTHONPATH}"
49
+ if [[ ! -s "$REC/recognized-lines.jsonl" ]]; then
50
+ pids=()
51
+ for rank in $(seq 0 31); do
52
+ gpu=$((rank % 8))
53
+ CUDA_VISIBLE_DEVICES="$gpu" \
54
+ "$ROOT/venv/bin/python" "$ROOT/recognize_bina02_two_layer.py" \
55
+ --rank "$rank" --world-size 32 \
56
+ --data "$DATA" \
57
+ --background-root "$SUPPORT/background-sources" \
58
+ --ocr-root "$SUPPORT/background-ocr-single" \
59
+ --detections-dir "$DET" \
60
+ --model-dir "$MODEL" \
61
+ --output-dir "$REC" \
62
+ --variant "$VARIANT" \
63
+ --device gpu:0 \
64
+ --crop-buffer 512 \
65
+ --recognition-batch-size 64 \
66
+ > "$REC/rank${rank}.log" 2>&1 &
67
+ pids+=("$!")
68
+ done
69
+ for pid in "${pids[@]}"; do wait "$pid"; done
70
+ test -s "$REC/recognized-lines.jsonl"
71
+ fi
72
+ test -s "$REC/recognized-lines.jsonl"
73
+ printf '%s HANDWRITING_RECOGNITION_COMPLETE\n' "$(date -Is)" >> "$RUN/status.log"
74
+
75
+ pids=()
76
+ for rank in $(seq 0 7); do
77
+ CUDA_VISIBLE_DEVICES="$rank" \
78
+ "$ROOT/venv/bin/python" "$ROOT/benchmark_bina02_printed_only.py" \
79
+ --rank "$rank" --world-size 8 \
80
+ --data "$DATA" \
81
+ --model-dir "$MODEL" \
82
+ --output-dir "$PRINTED" \
83
+ --device gpu:0 \
84
+ > "$PRINTED/rank${rank}.log" 2>&1 &
85
+ pids+=("$!")
86
+ done
87
+ for pid in "${pids[@]}"; do wait "$pid"; done
88
+ test -s "$PRINTED/predictions.jsonl"
89
+ printf '%s PRINTED_COMPLETE\n' "$(date -Is)" >> "$RUN/status.log"
90
+
91
+ "$ROOT/scorer-venv/bin/python" "$ROOT/assemble_bina02_two_layer.py" \
92
+ --recognized-lines "$REC/recognized-lines.jsonl" \
93
+ --baseline-predictions "$PRINTED/predictions.jsonl" \
94
+ --output-dir "$ASM" \
95
+ --isolation-variant "$VARIANT" \
96
+ > "$ASM/assembly.log" 2>&1
97
+ "$ROOT/scorer-venv/bin/python" - \
98
+ "$PRINTED/predictions.jsonl" \
99
+ "$ASM/predictions-simple_spread_right.jsonl" \
100
+ "$SCORES/raw.jsonl" <<'PY'
101
+ import json
102
+ import sys
103
+ rows=[]
104
+ for path in sys.argv[1:3]:
105
+ with open(path,encoding="utf-8") as handle:
106
+ rows.extend(json.loads(line) for line in handle if line.strip())
107
+ rows.sort(key=lambda row:(0 if row["split"]=="printed" else 1,int(row["id"].split(":")[1])))
108
+ with open(sys.argv[3],"w",encoding="utf-8") as handle:
109
+ for row in rows: handle.write(json.dumps(row,ensure_ascii=False)+"\n")
110
+ if len(rows)!=13338: raise SystemExit(f"expected 13338 rows, got {len(rows)}")
111
+ PY
112
+ "$ROOT/scorer-venv/bin/python" "$ROOT/decode_bina02_persian.py" \
113
+ --database "$ROOT/eval-support/recovery-native-20260725/decoder/lexicon-v1/persian-ocr-lexicon.sqlite3" \
114
+ --input "$SCORES/raw.jsonl" \
115
+ --output "$SCORES/decoded.jsonl" \
116
+ --details "$SCORES/decoder-details.jsonl" \
117
+ > "$SCORES/decoder-summary.json" 2>&1
118
+ CUDA_VISIBLE_DEVICES=0 "$ROOT/scorer-venv/bin/python" \
119
+ "$ROOT/eval-support/layout-audit-temp-20260725/scoring/score_ocr_double_benchmark.py" \
120
+ --manifest "$SCORES/decoded.jsonl" \
121
+ --predictions "$SCORES/decoded.jsonl" \
122
+ --output "$SCORES/results.json" \
123
+ --details "$SCORES/s3-details.jsonl" \
124
+ --shenavasanj "$ROOT/models/ShenavaSanj-v1.0" \
125
+ --device cuda:0 \
126
+ > "$SCORES/score.log" 2>&1
127
+ "$ROOT/scorer-venv/bin/python" - "$SCORES/results.json" "$SCORES/exact-summary.json" <<'PY'
128
+ import json
129
+ import sys
130
+ result=json.load(open(sys.argv[1]))
131
+ hand=result["splits"]["handwriting"]
132
+ printed=result["splits"]["printed"]
133
+ gate=all(hand[key]<10 and printed[key]<10 for key in ("wer","cer","s3"))
134
+ summary={"handwriting":hand,"printed":printed,"missing_predictions":result["missing_predictions"],"gate_passed":gate}
135
+ open(sys.argv[2],"w").write(json.dumps(summary,ensure_ascii=False,indent=2)+"\n")
136
+ print(json.dumps(summary,ensure_ascii=False,indent=2))
137
+ raise SystemExit(0 if gate else 30)
138
+ PY
139
+ printf '%s EXACT_GATE_PASSED\n' "$(date -Is)" >> "$RUN/status.log"
pipeline/watch_bina02_stage4_eval.py ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Stop Stage4 when handwriting diverges or printed quality leaves its baseline band."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import json
8
+ import os
9
+ import re
10
+ import signal
11
+ import time
12
+ from pathlib import Path
13
+ from typing import Any
14
+
15
+
16
+ PATTERNS = {
17
+ "handwriting": re.compile(
18
+ r"handwriting_norm_edit_dis:\s*([0-9]+(?:\.[0-9]+)?)"
19
+ ),
20
+ "printed": re.compile(r"printed_norm_edit_dis:\s*([0-9]+(?:\.[0-9]+)?)"),
21
+ }
22
+
23
+
24
+ def atomic_json(path: Path, payload: dict[str, Any]) -> None:
25
+ temporary = path.with_suffix(path.suffix + f".tmp.{os.getpid()}")
26
+ temporary.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8")
27
+ os.replace(temporary, path)
28
+
29
+
30
+ def main() -> None:
31
+ parser = argparse.ArgumentParser()
32
+ parser.add_argument("--log", type=Path, required=True)
33
+ parser.add_argument("--pgid", type=int, required=True)
34
+ parser.add_argument("--state", type=Path, required=True)
35
+ parser.add_argument("--baseline-handwriting", type=float)
36
+ parser.add_argument("--baseline-printed", type=float)
37
+ parser.add_argument("--handwriting-margin", type=float, default=0.002)
38
+ parser.add_argument("--printed-margin", type=float, default=0.0015)
39
+ parser.add_argument("--patience", type=int, default=2)
40
+ parser.add_argument("--poll-seconds", type=float, default=5.0)
41
+ args = parser.parse_args()
42
+
43
+ offset = 0
44
+ seen = 0
45
+ baseline_handwriting = args.baseline_handwriting
46
+ baseline_printed = args.baseline_printed
47
+ best_handwriting = args.baseline_handwriting
48
+ best_printed = args.baseline_printed
49
+ handwriting_bad = 0
50
+ printed_bad = 0
51
+
52
+ while True:
53
+ try:
54
+ os.killpg(args.pgid, 0)
55
+ except ProcessLookupError:
56
+ break
57
+
58
+ if args.log.exists():
59
+ with args.log.open("r", encoding="utf-8", errors="replace") as handle:
60
+ handle.seek(offset)
61
+ for line in handle:
62
+ if "cur metric" not in line:
63
+ continue
64
+ matches = {
65
+ name: pattern.search(line) for name, pattern in PATTERNS.items()
66
+ }
67
+ if not all(matches.values()):
68
+ continue
69
+ handwriting = float(matches["handwriting"].group(1))
70
+ printed = float(matches["printed"].group(1))
71
+ seen += 1
72
+ if baseline_handwriting is None:
73
+ baseline_handwriting = handwriting
74
+ baseline_printed = printed
75
+ best_handwriting = handwriting
76
+ best_printed = printed
77
+ else:
78
+ best_handwriting = max(float(best_handwriting), handwriting)
79
+ best_printed = max(float(best_printed), printed)
80
+ if handwriting < float(best_handwriting) - args.handwriting_margin:
81
+ handwriting_bad += 1
82
+ else:
83
+ handwriting_bad = 0
84
+ printed_floor = float(baseline_printed) - args.printed_margin
85
+ if printed < printed_floor:
86
+ printed_bad += 1
87
+ else:
88
+ printed_bad = 0
89
+
90
+ printed_floor = float(baseline_printed) - args.printed_margin
91
+ state = {
92
+ "seen": seen,
93
+ "baseline_handwriting": baseline_handwriting,
94
+ "baseline_printed": baseline_printed,
95
+ "handwriting_latest": handwriting,
96
+ "handwriting_best": best_handwriting,
97
+ "handwriting_bad": handwriting_bad,
98
+ "handwriting_margin": args.handwriting_margin,
99
+ "printed_latest": printed,
100
+ "printed_best": best_printed,
101
+ "printed_floor": printed_floor,
102
+ "printed_bad": printed_bad,
103
+ "printed_margin": args.printed_margin,
104
+ "patience": args.patience,
105
+ "state": "monitoring",
106
+ }
107
+ atomic_json(args.state, state)
108
+ print(
109
+ f"EVAL_GATE seen={seen} "
110
+ f"handwriting={handwriting:.8f} "
111
+ f"best={float(best_handwriting):.8f} "
112
+ f"hw_bad={handwriting_bad}/{args.patience} "
113
+ f"printed={printed:.8f} "
114
+ f"floor={printed_floor:.8f} "
115
+ f"printed_bad={printed_bad}/{args.patience}",
116
+ flush=True,
117
+ )
118
+
119
+ if (
120
+ handwriting_bad >= args.patience
121
+ or printed_bad >= args.patience
122
+ ):
123
+ reason = (
124
+ "handwriting_divergence"
125
+ if handwriting_bad >= args.patience
126
+ else "printed_regression"
127
+ )
128
+ state["state"] = "stopping"
129
+ state["stop_reason"] = reason
130
+ atomic_json(args.state, state)
131
+ print(f"EVAL_GATE_STOP reason={reason}", flush=True)
132
+ os.killpg(args.pgid, signal.SIGTERM)
133
+ raise SystemExit(20)
134
+ offset = handle.tell()
135
+ time.sleep(args.poll_seconds)
136
+
137
+ final = {
138
+ "state": "trainer_exited",
139
+ "seen": seen,
140
+ "baseline_handwriting": baseline_handwriting,
141
+ "baseline_printed": baseline_printed,
142
+ "handwriting_best": best_handwriting,
143
+ "printed_best": best_printed,
144
+ "handwriting_bad": handwriting_bad,
145
+ "printed_bad": printed_bad,
146
+ "patience": args.patience,
147
+ "completed_unix": time.time(),
148
+ }
149
+ atomic_json(args.state, final)
150
+
151
+
152
+ if __name__ == "__main__":
153
+ main()