diff --git a/.trinity/seals/t27f_outfit_atlas.json b/.trinity/seals/t27f_outfit_atlas.json new file mode 100644 index 0000000000..aafe457aeb --- /dev/null +++ b/.trinity/seals/t27f_outfit_atlas.json @@ -0,0 +1,28 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+9448ab4be", + "closure": [], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:887d8eff62ec6f7a45819d58197f100b0220134a1aef49e2206db9473b239c3f", + "gen_hash_rust": "sha256:070b5624ad881574633e0a8bcbc5c8736d981962d96c08c8614ac9028d3227d8", + "gen_hash_verilog": "sha256:2af546b35d9a94ad20644c7f4db12bf25b923c86504b1da92f07ac0025bd8aa6", + "gen_hash_zig": "sha256:6354045604c2d6fa24b006f5d1f1cfce0208c47ff4502f3b7b592e43756fc795", + "module": "outfit_atlas", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-18T10:08:24Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:6fb47290e7fae3d22258b5a8dfe1282fd58b96790ecb357a9513683c4035cac6", + "spec_path": "specs/t27f/outfit_atlas.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 5, + "total": 5, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/.trinity/seals/t27f_token_floor.json b/.trinity/seals/t27f_token_floor.json index e9a12a9c1a..5e2e0ced81 100644 --- a/.trinity/seals/t27f_token_floor.json +++ b/.trinity/seals/t27f_token_floor.json @@ -1,10 +1,14 @@ { - "built_by": "t27c-bootstrap@0.5.2+4ab200073", + "built_by": "t27c-bootstrap@0.5.2+9448ab4be", "closure": [ { "spec": "specs/physics/physical_limits.t27", "spec_hash": "sha256:adb7092831e266f0e4bed92f74e7795c78c1bdb1051b5cd9a0c1be94b5e5b19a" }, + { + "spec": "specs/t27f/outfit_atlas.t27", + "spec_hash": "sha256:6fb47290e7fae3d22258b5a8dfe1282fd58b96790ecb357a9513683c4035cac6" + }, { "spec": "specs/t27f/particle_engine.t27", "spec_hash": "sha256:1b99d151382c41e99a4e928842ee0abcb984d2a0ee43abf5fbd602097d6abadd" @@ -15,23 +19,23 @@ } ], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:f7f9b371e7578312e700bdd68b5d6340b25cbbb60a6ca3ee6f12ea11f1d5781d", - "gen_hash_rust": "sha256:dec4aa24239c65338ebd28514ce59bdb7a4d58422e06f23bcf47d575cbb0f7c8", - "gen_hash_verilog": "sha256:ef97651283475d338acd324ec27a65977c513c2f22a467fc093048780e5454a7", - "gen_hash_zig": "sha256:955fd458f8475e720800d9d493cc935fb64d6418347da8decf117586226dbf81", + "gen_hash_c": "sha256:1fa93912469188771edb693d7e40d4ece49d22abebe79c31d4cb9077799fdb9c", + "gen_hash_rust": "sha256:892f285d9bc8177684644938f41ec61632f3d6dd29494f200e520367c55f1c99", + "gen_hash_verilog": "sha256:ec9fc178b4408e530d3f341c18082a622aa1c27f40382b17499eed0cba492d72", + "gen_hash_zig": "sha256:c04875378f32a1dbb0df1215ffd9991dc352a6b9f06e0da72c53eff04ed56dff", "module": "token_floor", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-18T09:43:50Z", + "sealed_at": "2026-10-18T10:08:27Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:1ecbe3ef0f03c7cc284d795d61cf771a8e37f9c58f0339c27aada4aebe063a19", + "spec_hash": "sha256:1fd3b3b3d50b2d163cfe2809587ae1b859ac0f79fddf666813602a53d5adf780", "spec_path": "specs/t27f/token_floor.t27", "tests": { "failed": 0, "failing": [], "forced": false, - "passed": 19, - "total": 19, + "passed": 20, + "total": 20, "vacuous": 0 }, "toolchain": { diff --git a/.trinity/seals/t27f_token_floor_mux.json b/.trinity/seals/t27f_token_floor_mux.json index 94bae7464a..58e7de1e1c 100644 --- a/.trinity/seals/t27f_token_floor_mux.json +++ b/.trinity/seals/t27f_token_floor_mux.json @@ -1,5 +1,5 @@ { - "built_by": "t27c-bootstrap@0.5.2+4ab200073", + "built_by": "t27c-bootstrap@0.5.2+9448ab4be", "closure": [ { "spec": "specs/automation/owner_voice_reel.t27", @@ -11,20 +11,20 @@ }, { "spec": "specs/t27f/token_floor.t27", - "spec_hash": "sha256:1ecbe3ef0f03c7cc284d795d61cf771a8e37f9c58f0339c27aada4aebe063a19" + "spec_hash": "sha256:1fd3b3b3d50b2d163cfe2809587ae1b859ac0f79fddf666813602a53d5adf780" } ], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:f7e5928cd0ddf08d2daa110da9c4a07efe4856a4ad399989435fa7ee20ddbc47", - "gen_hash_rust": "sha256:06f62f3afd1b6af2c769e8a73403970cf97e197755cecaa9b5ea84e415ecc645", - "gen_hash_verilog": "sha256:b4566ce132ed3acdfb979e8a5801b6120528040ef536a4be134bdd014b406d81", - "gen_hash_zig": "sha256:267cfc1d230d6a4f57ec64a6a685ef8dc524729d596716d5163ceff679626905", + "gen_hash_c": "sha256:e8bd1f62065c8ee00dd0f8e84fdc9a0fa61bfdaf96b729a4c182b2a3449e450f", + "gen_hash_rust": "sha256:fd99dc4cffc96c4ebd7503050f31520103d084b15c5b30296319300f6353835e", + "gen_hash_verilog": "sha256:2ae59b987cd2c0478cddd53792ec071716a6a77a106a56bb37b6bc333795ef15", + "gen_hash_zig": "sha256:1ce803fd58f0f90b0700052b2145a431d84c4efe788f7a0b884cb4d83eddb167", "module": "token_floor_mux", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-18T09:43:50Z", + "sealed_at": "2026-10-18T10:08:27Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:716ecd0d6dff38647ba48ddad3bf3ded2b9ecbc9b31746bd6e8d9dd5cbf0ca9c", + "spec_hash": "sha256:98fac62ffbec00868232d5a09b82c754951d1819a2f7c2e8f4317371a8039162", "spec_path": "specs/t27f/token_floor_mux.t27", "tests": { "failed": 0, diff --git a/specs/t27f/outfit_atlas.t27 b/specs/t27f/outfit_atlas.t27 new file mode 100644 index 0000000000..4de54c8858 --- /dev/null +++ b/specs/t27f/outfit_atlas.t27 @@ -0,0 +1,562 @@ +// SPDX-License-Identifier: Apache-2.0 +// specs/t27f/outfit_atlas.t27 -- t27.ai's display face, Outfit ExtraBold, as a coverage grid: the +// 39 characters specs/t27f/token_floor.t27 writes with particles, drawn 12 dots to the cap height. +// +// WHY: token_floor's letters were a 5x7 dot-matrix font, seven dots to the cap height and every +// glyph five dots wide. The owner asked for the site's own fonts and better quality. t27.ai sets its +// headings in Outfit (apps/website, `outfit-latin.woff2`); this module is that face at weight 800, +// sampled on a grid fine enough that a label of particles reads as Outfit, with proportional widths +// and the outline's antialiasing kept as a dot's light. +// +// WHAT IS HERE: ATLAS is tool-written data, not hand-typed. ADV and LSB are the font's own hmtx +// table. The functions only index them; token_floor places the dots. +// +// PROVENANCE (Railway lab, 2026-10-18; every step is a stock tool, no script): +// outfit-latin.woff2 from t27.ai, sha256 92684e4acde79ef07758cd09380b7e01e9824d8b061eddeda046f78c166d7b12 +// fonttools varLib.instancer -q outfit-latin.woff2 wght=800 -o outfit-800.ttf +// sha256 6254f38359c3af8a7f4f0a2fd40278ec50ae96d2db15c3c20b0b28e77b9fee48 (static: ffmpeg 5.1's +// drawtext ignores a variable font's named instances and draws the default, Outfit Thin) +// ttx -t hmtx -t glyf outfit-800.ttf -> ADV, LSB; cap height 709 units (H: yMin 0, yMax 709) +// textfile = the 39 characters in glyph order, three spaces between them, one line +// ffmpeg -f lavfi -i color=black:s=6400x128:d=1 -frames:v 1 -filter_script:v F -f rawvideo +// -pix_fmt gray atlas.raw, with F = drawtext=fontfile=outfit-800.ttf:textfile=T:fontsize=135: +// fontcolor=white:x=16:y=6,format=gray,scale=800:16:flags=area +// sha256 cecf12f7c145acee7f022e4a2792258a7c946f4f0c7cc5af545fa67e9474db4e, 12800 bytes +// od -An -v -tu1 -w32 atlas.raw -> the rows of ATLAS below +// One string, not one drawtext a glyph, so every glyph shares the baseline (ffmpeg 5.1 aligns a +// lone glyph by its top). 135 px to the em at 8 px a dot puts the cap height at 11.96 dots. +// +// NOT CLAIMED: the dots are not hinted, and pairs are not kerned (Outfit kerns in GPOS, which +// drawtext does not read); token_floor spaces glyphs by advance alone. +// phi^2 + 1/phi^2 = 3 | TRINITY + +module outfit_atlas; + +pub const ATLAS_W : usize = 800; +pub const ATLAS_H : usize = 16; +pub const CAP_TOP : i64 = 1; // the first row of a capital +pub const CAP_ROWS : f64 = 12.0; // rows from the cap top to the baseline +pub const FONT_PX : f64 = 135.0; // the em the atlas was drawn at, px +pub const SS : f64 = 8.0; // drawn px to one dot +pub const UPM : f64 = 1000.0; +pub const CAP_UNITS : f64 = 709.0; +pub const GLYPHS : usize = 39; // 0-9, A-Z, '.', '-', ','; the space has no ink + +// hmtx, font units, in glyph order 0-9, A-Z, '.', '-', ',' and last the space. +pub const ADV : [40]i64 = [671, 390, 576, 569, 613, 568, 579, 530, 569, 579, 742, 650, 692, 761, 627, 602, 791, 739, 305, 550, 721, 567, 866, 748, 812, 640, 819, 663, 596, 649, 708, 722, 1012, 724, 694, 617, 304, 463, 287, 185]; +pub const LSB : [40]i64 = [27, 16, 18, 11, 18, 8, 21, 16, 25, 20, 11, 63, 28, 63, 63, 63, 28, 63, 63, 10, 63, 63, 63, 63, 27, 63, 28, 63, 11, 18, 52, 11, 13, 11, 9, 28, 53, 51, 47, 0]; + +// Coverage 0..255, row-major, ATLAS_W a row. +pub const ATLAS : [12800]u8 = [ + 0, 0, 0, 0, 0, 0, 6, 30, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 30, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 61, 43, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 30, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 25, 26, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4, 27, 25, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 27, 23, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 19, 157, 246, 255, 254, 198, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 224, 224, 53, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 79, 206, 255, 255, 251, 178, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 255, 255, 255, 255, 255, + 255, 255, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, 240, 240, 186, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 252, 255, 255, 255, 255, 255, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 224, 240, 239, 71, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 28, 224, 224, 224, 224, 224, 224, 224, 224, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 190, 255, 255, 255, 221, + 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 157, 246, 255, 255, 203, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 84, 240, 240, 220, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 224, 224, 224, 224, 224, 220, 180, 65, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 163, 241, 255, 255, 245, 175, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 168, + 224, 225, 225, 224, 221, 195, 133, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 224, 224, 224, 224, 224, 224, 224, 224, 56, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 224, 224, 224, 224, 224, 224, 224, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, + 180, 247, 255, 255, 242, 168, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 224, 224, 137, 0, 0, 0, 0, 224, 224, 220, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 224, 224, 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 240, 240, 27, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 224, 224, 81, 0, 0, 8, 189, 224, 224, 172, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, + 224, 224, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 224, 222, 47, 0, 0, 0, 0, 0, 0, 52, 223, 224, 53, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 224, 211, 26, 0, 0, 0, 0, 196, 224, 224, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 97, 191, 241, 253, 232, 172, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 224, 224, 224, 224, 223, 211, 151, 32, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 173, 233, 253, 241, 191, 97, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 224, 224, 224, 224, 224, 217, 175, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 160, 244, 255, 255, 234, 146, 22, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 154, 224, 224, 224, 224, 224, 224, 224, 224, 224, 154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 180, + 240, 240, 57, 0, 0, 0, 117, 240, 240, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 199, 224, 224, 69, 0, 0, 0, 0, 129, 224, 224, 126, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 224, 224, 131, 0, 0, 0, 120, 224, 224, 29, 0, 0, 5, 213, 224, 214, 6, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 125, 224, 224, 209, 16, 0, 0, 36, 221, 224, 223, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 223, 224, 224, 51, 0, 0, + 0, 162, 224, 224, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 224, 224, 224, 224, 224, 224, 224, 224, 56, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 20, 217, 255, 255, 255, 255, 255, 251, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 60, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 113, 255, 255, 255, 255, 255, 255, 227, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 255, 255, 255, 255, 255, + 255, 255, 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 230, 255, 255, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 22, 255, 255, 255, 255, 255, 255, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 176, 255, 255, 153, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 31, 255, 255, 255, 255, 255, 255, 255, 255, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 207, 255, 255, 255, 255, 255, + 244, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 216, 255, 255, 255, 255, 255, 252, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 185, 255, 255, 255, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 255, 255, 255, 255, 255, 255, 255, 253, 73, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 94, 248, 255, 255, 255, 255, 255, 255, 253, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, + 255, 255, 255, 255, 255, 255, 255, 249, 113, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 255, 255, 255, 255, 255, 63, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 255, 255, 255, 255, 255, 239, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 139, 255, + 255, 255, 255, 255, 255, 255, 251, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 156, 0, 0, 0, 0, 255, 255, 251, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223, 255, 255, 28, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 92, 0, 0, 152, 255, 255, 235, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, + 255, 255, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 255, 255, 206, 5, 0, 0, 0, 0, 6, 210, 255, 255, 60, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 196, 6, 0, 0, 0, 223, 255, 255, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 21, 198, 255, 255, 255, 255, 255, 255, 255, 153, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 255, 255, 255, 255, 255, 238, + 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 155, 255, 255, 255, 255, 255, 255, 255, 198, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 215, 255, 255, 255, 255, 255, 255, 210, + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 175, 255, 255, 255, 255, 255, 255, 255, 255, 255, 175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, + 255, 255, 60, 0, 0, 0, 124, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 255, 255, 162, 0, 0, 0, 3, 230, 255, 255, 53, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 254, 255, 220, 0, 0, 0, 207, 255, 255, 103, 0, 0, 64, 255, 255, 175, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 13, 222, 255, 255, 152, 0, 1, 186, 255, 255, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 168, 255, 255, 190, 0, 0, + 62, 255, 255, 246, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223, 255, 255, 255, 255, 255, 255, 255, 255, 63, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 163, 255, 255, 222, 136, 182, 255, 255, 239, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 160, 231, 255, 255, 60, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2, 196, 255, 252, 162, 144, 242, 255, 255, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 128, 128, 128, 209, 255, + 255, 206, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 255, 255, 208, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 48, 255, 255, 187, 128, 128, 128, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 94, 255, 255, 215, 11, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 20, 160, 160, 160, 160, 165, 255, 255, 242, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 255, 255, 217, 76, 178, 255, + 255, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 138, 255, 255, 226, 136, 184, 255, 255, 230, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 30, 253, 255, 255, 255, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 255, 255, 233, 96, 98, 182, 255, 255, 191, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 253, 255, 255, 237, 172, 169, 228, 255, 170, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, + 255, 255, 207, 192, 198, 241, 255, 255, 255, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 223, 193, 193, 193, 193, 193, 48, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 215, 193, 193, 193, 193, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 255, 255, + 255, 222, 167, 175, 237, 255, 255, 113, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 156, 0, 0, 0, 0, 255, 255, 251, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223, 255, 255, 28, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 92, 0, 91, 255, 255, 252, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, + 255, 255, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 255, 255, 255, 123, 0, 0, 0, 0, 129, 255, 255, 255, 60, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 255, 147, 0, 0, 0, 223, 255, 255, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 200, 255, 255, 252, 188, 164, 208, 255, 255, 255, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 194, 96, 101, 198, 255, 255, + 182, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 255, 255, 208, 164, 189, 252, 255, 255, 200, 5, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 253, 96, 97, 148, 255, 255, 246, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 255, 255, 217, 106, 120, 222, 237, 50, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 132, 192, 192, 192, 255, 255, 254, 192, 192, 192, 132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, + 255, 255, 60, 0, 0, 0, 124, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 255, 255, 242, 10, 0, 0, 67, 255, 255, 211, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 255, 255, 40, 0, 27, 255, 255, 255, 178, 0, 0, 140, 255, 255, 94, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 62, 253, 255, 254, 59, 88, 255, 255, 229, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 244, 255, 255, 75, 1, + 201, 255, 255, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 160, 160, 160, 160, 241, 255, 255, 235, 23, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 26, 252, 255, 242, 25, 0, 1, 172, 255, 255, 118, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, 60, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 11, 181, 90, 0, 0, 114, 255, 255, 155, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 252, 255, + 224, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 221, 255, 255, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 74, 255, 255, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 239, 255, 249, 50, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 255, 255, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 255, 255, 136, 0, 72, 255, + 255, 117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 255, 255, 42, 0, 1, 200, 255, 255, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 126, 255, 255, 240, 255, 251, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 255, 255, 219, 0, 0, 40, 255, 255, 214, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 212, 255, 255, 187, 14, 0, 0, 6, 102, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, + 255, 255, 60, 0, 0, 9, 147, 255, 255, 245, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 124, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 247, 255, 255, + 125, 3, 0, 0, 13, 165, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 156, 0, 0, 0, 0, 255, 255, 251, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223, 255, 255, 28, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 92, 42, 244, 255, 255, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, + 255, 255, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 255, 255, 255, 248, 43, 0, 0, 47, 250, 255, 255, 255, 60, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 255, 255, 92, 0, 0, 223, 255, 255, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 103, 255, 255, 238, 50, 0, 0, 0, 100, 255, 255, 253, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 156, 0, 0, 23, 255, 255, + 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 254, 255, 254, 98, 0, 0, 0, 53, 238, 255, 255, 102, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 251, 0, 0, 0, 205, 255, 255, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125, 255, 255, 152, 0, 0, 14, 42, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 251, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, + 255, 255, 60, 0, 0, 0, 124, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 203, 255, 255, 85, 0, 0, 158, 255, 255, 114, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 118, 255, 255, 116, 0, 101, 255, 255, 255, 244, 8, 0, 215, 255, 251, 18, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 140, 255, 255, 213, 230, 255, 255, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 122, 255, 255, 211, 89, + 255, 255, 221, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 255, 255, 254, 75, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 91, 255, 255, 166, 0, 0, 0, 72, 255, 255, 186, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, 60, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 128, 255, 255, 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 242, 255, 255, + 120, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 255, 255, 213, 20, 48, 48, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 100, 255, 255, 222, 221, 197, 112, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 183, 255, 255, 255, 240, 164, 27, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 177, 255, 255, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 226, 255, 231, 108, 196, 255, + 252, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 255, 255, 12, 0, 0, 172, 255, 255, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 223, 255, 237, 112, 255, 255, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 255, 255, 228, 64, 66, 156, 255, 255, 147, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 255, 255, 250, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, + 255, 255, 60, 0, 0, 0, 2, 209, 255, 255, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 157, 64, 64, 64, 64, 48, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 93, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 108, 255, 255, 184, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 181, 64, 64, 64, 64, 255, 255, 251, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223, 255, 255, 28, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 104, 214, 255, 255, 168, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, + 255, 255, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 255, 255, 255, 255, 202, 4, 5, 205, 255, 255, 255, 255, 60, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 255, 255, 245, 47, 0, 223, 255, 255, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 198, 255, 255, 112, 0, 0, 0, 0, 0, 177, 255, 255, 133, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 156, 0, 0, 22, 255, 255, + 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, 255, 255, 176, 0, 0, 0, 0, 0, 113, 255, 255, 197, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 251, 0, 0, 24, 234, 255, 255, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 255, 255, 255, 186, 89, 10, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 251, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, + 255, 255, 60, 0, 0, 0, 124, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 255, 255, 174, 0, 8, 240, 255, 250, 23, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 255, 255, 191, 0, 175, 255, 255, 255, 255, 71, 35, 255, 255, 187, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 8, 213, 255, 255, 255, 255, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 220, 255, 255, 247, + 255, 255, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 241, 255, 255, 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 122, 255, 255, 130, 0, 0, 0, 35, 255, 255, 217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, 60, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 245, 255, 255, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 115, 255, 255, 255, + 255, 207, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 209, 255, 255, 90, 95, 255, 255, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 119, 255, 255, 255, 255, 255, 255, 174, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 255, 255, 255, 255, 255, 255, 225, 22, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 251, 255, 228, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 254, 255, 255, 255, 255, + 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, 255, 255, 159, 40, 90, 248, 255, 246, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 68, 255, 255, 153, 22, 251, 255, 212, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 255, 255, 255, 255, 255, 255, 255, 211, 12, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, 255, 255, 205, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, + 255, 255, 60, 0, 0, 0, 0, 140, 255, 255, 149, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 255, 255, 255, 255, 191, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 255, 255, 255, 255, 255, 175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 255, 255, 107, + 0, 0, 132, 224, 224, 224, 224, 224, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223, 255, 255, 28, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 232, 255, 255, 215, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, + 255, 255, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 255, 255, 255, 255, 255, 118, 123, 255, 255, 255, 255, 255, 60, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 226, 255, 255, 220, 17, 223, 255, 255, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 244, 255, 255, 45, 0, 0, 0, 0, 0, 108, 255, 255, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 194, 96, 101, 197, 255, 255, + 182, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 180, 255, 255, 108, 0, 0, 0, 12, 2, 45, 255, 255, 243, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 224, 224, 249, 255, 255, 181, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 211, 255, 255, 255, 255, 238, 118, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 251, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, + 255, 255, 60, 0, 0, 0, 124, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 249, 255, 248, 15, 83, 255, 255, 176, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 212, 255, 251, 23, 243, 255, 179, 251, 255, 146, 111, 255, 255, 106, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 51, 251, 255, 255, 230, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, 255, 255, 255, + 255, 182, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 200, 255, 255, 203, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 123, 255, 255, 129, 0, 0, 0, 33, 255, 255, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, 60, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 236, 255, 255, 167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 103, 119, 224, + 255, 255, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 255, 255, 218, 3, 95, 255, 255, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 10, 161, 112, 68, 107, 239, 255, 255, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 213, 255, 255, 139, 72, 180, 255, 255, 147, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 115, 255, 255, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 229, 255, 252, 200, 241, 255, + 249, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 236, 255, 255, 255, 255, 255, 255, 146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 166, 255, 255, 63, 0, 186, 255, 255, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 255, 255, 255, 255, 255, 255, 255, 255, 123, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 255, 255, 198, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, + 255, 255, 60, 0, 0, 0, 0, 134, 255, 255, 154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 255, 255, 255, 255, 191, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 255, 255, 255, 255, 255, 175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 255, 255, 100, + 0, 0, 150, 255, 255, 255, 255, 255, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223, 255, 255, 28, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 203, 255, 255, 242, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, + 255, 255, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 255, 255, 200, 223, 255, 248, 249, 255, 220, 202, 255, 255, 60, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 156, 146, 255, 255, 181, 225, 255, 255, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 250, 255, 255, 38, 0, 0, 0, 0, 0, 103, 255, 255, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 255, 255, 255, 255, 255, 239, + 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 187, 255, 255, 102, 0, 0, 15, 200, 154, 40, 255, 255, 250, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 255, 255, 251, 161, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 151, 247, 255, 255, 255, 255, 133, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 251, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, + 255, 255, 60, 0, 0, 0, 124, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 171, 255, 255, 97, 173, 255, 255, 79, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 255, 255, 156, 255, 255, 88, 194, 255, 220, 186, 255, 254, 27, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 65, 254, 255, 255, 244, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 181, 255, 255, + 249, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 255, 242, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 128, 128, 128, 128, 128, 80, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 95, 255, 255, 161, 0, 0, 0, 65, 255, 255, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, 60, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 232, 255, 255, 183, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, + 255, 255, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, 255, 255, 242, 224, 235, 255, 255, 243, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 118, 255, 255, 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 255, 255, 210, 0, 0, 19, 255, 255, 212, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 211, 255, 255, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, 255, 255, 93, 0, 33, 249, + 255, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 190, 254, 255, 255, 255, 226, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 18, 247, 255, 247, 98, 96, 167, 255, 255, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 255, 255, 219, 0, 1, 50, 242, 255, 250, 21, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 255, 255, 242, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, + 255, 255, 60, 0, 0, 0, 0, 186, 255, 255, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 157, 64, 64, 64, 64, 48, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 194, 160, 160, 160, 160, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, 255, 255, 157, + 0, 0, 94, 160, 160, 194, 255, 255, 171, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 181, 64, 64, 64, 64, 255, 255, 251, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 255, 255, 28, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 94, 184, 255, 255, 208, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, + 255, 255, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 255, 255, 187, 68, 254, 255, 255, 254, 63, 191, 255, 255, 60, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 156, 6, 196, 255, 255, 255, 255, 255, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 217, 255, 255, 88, 0, 0, 0, 0, 0, 153, 255, 255, 155, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 243, 224, 223, 211, 153, 34, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 154, 255, 255, 153, 0, 0, 133, 255, 255, 207, 255, 255, 218, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 252, 174, 255, 255, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 100, 212, 255, 255, 245, + 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 251, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, + 255, 255, 62, 0, 0, 0, 126, 255, 255, 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75, 255, 255, 199, 248, 255, 233, 5, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 255, 255, 254, 255, 252, 18, 120, 255, 255, 253, 255, 199, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 13, 223, 255, 255, 255, 255, 188, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 255, 255, + 190, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 254, 255, 255, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 255, 255, 159, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 32, 254, 255, 236, 15, 0, 0, 155, 255, 255, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, 60, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 35, 229, 255, 255, 177, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 5, 0, 0, 56, + 255, 255, 217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 31, 0, 0, 0, 126, 255, 255, 149, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 255, 255, 218, 2, 0, 29, 255, 255, 214, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 255, 255, 209, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 187, 255, 255, 44, 0, 1, 234, + 255, 251, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 242, 255, 254, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 107, 255, 255, 255, 255, 255, 255, 255, 255, 240, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 255, 255, 219, 0, 0, 0, 195, 255, 255, 60, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 223, 255, 255, 144, 1, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, + 255, 255, 60, 0, 0, 0, 85, 255, 255, 254, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 124, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 253, 255, 250, + 60, 0, 0, 0, 5, 188, 255, 255, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 156, 0, 0, 0, 0, 255, 255, 251, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 1, 0, 8, 242, 255, 255, 13, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 92, 23, 232, 255, 255, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, + 255, 255, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 255, 255, 187, 0, 153, 255, 255, 147, 0, 191, 255, 255, 60, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 156, 0, 26, 231, 255, 255, 255, 255, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 139, 255, 255, 212, 15, 0, 0, 0, 48, 246, 255, 255, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 156, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, 255, 255, 246, 47, 0, 6, 178, 255, 255, 255, 255, 141, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 251, 19, 230, 255, 255, 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, 7, 0, 0, 9, 239, 255, 255, + 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 251, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, + 255, 255, 120, 0, 0, 0, 181, 255, 255, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 231, 255, 255, 255, 255, 141, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 224, 255, 255, 255, 195, 0, 45, 255, 255, 255, 255, 118, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 154, 255, 255, 172, 230, 255, 255, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 255, 255, + 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 231, 255, 255, 158, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 23, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 255, 255, 159, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 176, 255, 255, 203, 105, 151, 255, 255, 247, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, 60, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 31, 225, 255, 255, 232, 101, 96, 96, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 228, 203, 112, 114, 223, + 255, 255, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 160, 160, 160, 160, 195, 255, 255, 218, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 91, 250, 163, 102, 135, 247, 255, 255, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 218, 255, 255, 168, 104, 204, 255, 255, 153, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 149, 255, 255, 117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 255, 255, 187, 72, 143, 255, + 255, 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 183, 255, 255, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 206, 255, 255, 255, 255, 255, 255, 255, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 255, 255, 224, 32, 33, 83, 248, 255, 255, 31, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 255, 255, 255, 190, 110, 105, 166, 253, 134, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, + 255, 255, 159, 129, 135, 186, 254, 255, 255, 154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 190, 129, 129, 129, 129, 129, 48, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 255, 255, + 249, 155, 103, 116, 205, 255, 255, 225, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 156, 0, 0, 0, 0, 255, 255, 251, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 205, 173, 102, 182, 255, 255, 207, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 92, 0, 71, 254, 255, 255, 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, + 255, 255, 238, 129, 129, 129, 129, 129, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 255, 255, 187, 0, 14, 106, 106, 12, 0, 191, 255, 255, 60, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 156, 0, 0, 62, 251, 255, 255, 255, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 25, 238, 255, 255, 223, 124, 100, 144, 243, 255, 255, 201, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 156, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 200, 255, 255, 243, 143, 100, 132, 254, 255, 255, 250, 28, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 251, 0, 74, 255, 255, 242, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 210, 101, 67, 114, 253, 255, 245, + 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 251, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, + 255, 255, 250, 144, 102, 169, 255, 255, 243, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139, 255, 255, 255, 255, 44, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 255, 255, 255, 121, 0, 1, 225, 255, 255, 255, 37, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 73, 255, 255, 237, 22, 90, 255, 255, 241, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 255, 255, + 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 182, 255, 255, 248, 107, 96, 96, 96, 96, 12, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 149, 255, 223, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 12, 193, 245, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 29, 230, 255, 255, 255, 255, 255, 255, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, 60, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 221, 255, 255, 255, 255, 255, 255, 255, 223, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 209, 255, 255, 255, 255, 255, + 255, 241, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 41, 252, 255, 255, 255, 255, 255, 255, 205, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, 253, 255, 255, 255, 255, 255, 234, 29, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 238, 255, 253, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 251, 255, 255, 255, 255, 255, + 255, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 255, 255, 218, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, + 255, 255, 215, 0, 0, 0, 0, 91, 255, 255, 189, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 255, 255, 255, 255, 255, 255, 255, 255, 168, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 126, 255, 255, 255, 255, 255, 255, 255, 255, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, + 255, 255, 255, 255, 255, 255, 255, 255, 176, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 255, 255, 255, 255, 255, 95, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 182, 255, + 255, 255, 255, 255, 255, 255, 242, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 156, 0, 0, 0, 0, 255, 255, 251, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 202, 255, 255, 255, 255, 255, 254, 75, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 92, 0, 0, 139, 255, 255, 242, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, + 255, 255, 255, 255, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 255, 255, 187, 0, 0, 0, 0, 0, 0, 191, 255, 255, 60, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 156, 0, 0, 0, 112, 255, 255, 255, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 68, 245, 255, 255, 255, 255, 255, 255, 255, 225, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 156, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 222, 255, 255, 255, 255, 255, 255, 255, 255, 255, 151, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 251, 0, 0, 153, 255, 255, 203, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 239, 255, 255, 255, 255, 255, 255, 255, 138, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 251, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 162, 255, 255, 255, 255, 255, 255, 255, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 255, 255, 255, 202, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 255, 255, 255, 47, 0, 0, 151, 255, 255, 211, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 17, 228, 255, 255, 100, 0, 1, 190, 255, 255, 182, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 255, 255, + 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 255, 255, 255, 255, 255, 255, 255, 255, 255, 31, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 248, 255, 255, 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 86, 255, 255, 244, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 32, 184, 255, 255, 255, 229, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, 60, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 223, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 176, 252, 255, 255, 255, + 194, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 76, 207, 255, 255, 255, 250, 162, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 224, 255, 255, 255, 194, 40, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 86, 255, 255, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 217, 255, 255, 255, 237, + 115, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 249, 255, 252, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147, + 255, 255, 125, 0, 0, 0, 0, 13, 246, 255, 254, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 255, 255, 255, 255, 255, 255, 241, 141, 9, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 196, 255, 255, 255, 255, 220, 105, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, + 255, 255, 255, 255, 255, 251, 197, 89, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 255, 255, 255, 255, 255, 95, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 109, + 223, 255, 255, 255, 251, 176, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 156, 0, 0, 0, 0, 255, 255, 251, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 221, 255, 255, 255, 224, 81, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 255, 255, 92, 0, 0, 6, 202, 255, 255, 208, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, + 255, 255, 255, 255, 255, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 255, 255, 187, 0, 0, 0, 0, 0, 0, 191, 255, 255, 60, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 156, 0, 0, 0, 1, 167, 255, 255, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 43, 186, 254, 255, 255, 255, 251, 161, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 255, 255, 156, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 155, 249, 255, 255, 255, 255, 193, 189, 255, 255, 83, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 251, 0, 0, 12, 220, 255, 255, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 176, 251, 255, 255, 255, 237, 125, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 251, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 124, 238, 255, 255, 255, 221, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 202, 255, 255, 105, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 234, 255, 227, 1, 0, 0, 77, 255, 255, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 163, 255, 255, 195, 2, 0, 0, 42, 250, 255, 255, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 255, 255, + 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 255, 255, 255, 255, 255, 255, 255, 255, 255, 31, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 175, 255, 239, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 41, 246, 255, 216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 26, 61, 47, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 32, 32, 8, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 32, 32, 32, 32, 32, 32, 32, 32, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 56, 59, 25, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 16, 16, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 28, 60, 55, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 44, 62, 31, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 18, 32, 32, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 37, 61, 47, 5, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 32, 32, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, + 32, 32, 10, 0, 0, 0, 0, 0, 27, 32, 32, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 32, 32, 32, 32, 32, 28, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 57, 59, 32, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, + 32, 32, 32, 32, 29, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 32, 32, 32, 32, 32, 32, 32, 32, 12, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 32, 32, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 35, 61, 53, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 32, 32, 20, 0, 0, 0, 0, 32, 32, 32, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 32, 32, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 43, 62, 40, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 32, 32, 12, 0, 0, 0, 13, 32, 32, 32, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, + 32, 32, 32, 32, 32, 32, 32, 32, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 32, 32, 24, 0, 0, 0, 0, 0, 0, 24, 32, 32, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 32, 32, 20, 0, 0, 0, 0, 7, 32, 32, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 29, 80, 93, 73, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 48, 48, 30, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 71, 93, 81, 32, 0, 9, 186, 146, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 32, 32, 32, 0, 0, 0, 17, 32, 32, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 52, 62, 43, 4, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 6, 47, 62, 38, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 32, 32, 7, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 32, 24, 0, 0, 0, 5, 32, 32, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 32, 32, 32, 14, 0, 0, 0, 0, 26, 32, 32, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 48, 48, + 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 32, 32, 32, 32, 32, 32, 32, 32, 32, 4, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 4, 55, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 120, 255, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 228, 214, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 25, 180, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +]; + +pub fn cov(col: usize, row: usize) -> i64 { + return @as(i64, @intCast(ATLAS[row * ATLAS_W + col])); +} + +fn col_ink(col: usize) -> bool { + var r : usize = 0; + while (r < ATLAS_H) { + if (cov(col, r) > 0) { + return true; + } + r = r + 1; + } + return false; +} + +// Where the g-th run of inked columns starts: glyph g's ink, the glyphs drawn left to right. +// ATLAS_W when there is no such run. +pub fn run_start(g: usize) -> usize { + var k : usize = 0; + var c : usize = 0; + while (c < ATLAS_W) { + if (col_ink(c) && (c == 0 || col_ink(c - 1) == false)) { + if (k == g) { + return c; + } + k = k + 1; + } + c = c + 1; + } + return ATLAS_W; +} + +// One past the last inked column of glyph g. +pub fn run_end(g: usize) -> usize { + var c = run_start(g); + while (c < ATLAS_W && col_ink(c)) { + c = c + 1; + } + return c; +} + +fn runs() -> usize { + var g : usize = 0; + while (run_start(g) < ATLAS_W) { + g = g + 1; + } + return g; +} + +pub fn em_dots() -> f64 { + return FONT_PX / SS; +} + +// The widest coverage of glyph g on row r. +fn row_max(g: usize, r: usize) -> i64 { + var m : i64 = 0; + var c = run_start(g); + const e = run_end(g); + while (c < e) { + if (cov(c, r) > m) { + m = cov(c, r); + } + c = c + 1; + } + return m; +} + +fn width(g: usize) -> f64 { + return @as(f64, @floatFromInt(run_end(g) - run_start(g))); +} + +// ---- tests ---- + +test "one run of ink a glyph: 39 glyphs, the space has none" { + assert(runs() == GLYPHS); + assert(ADV[GLYPHS] == 185); + assert(LSB[GLYPHS] == 0); +} + +test "the cap height is 12 dots: 135 px to the em at 8 px a dot" { + assert(@abs(CAP_UNITS * em_dots() / UPM - CAP_ROWS) < 0.05); +} + +test "the raster agrees with the outlines: ink widths are glyf's bounding boxes" { + // H 63..676, O 27..785, comma 47..248, period 53..252, hyphen 51..412 (ttx -t glyf) + assert(@abs(width(17) - 613.0 * em_dots() / UPM) < 1.5); + assert(@abs(width(24) - 758.0 * em_dots() / UPM) < 1.5); + assert(@abs(width(38) - 201.0 * em_dots() / UPM) < 1.5); + assert(@abs(width(36) - 199.0 * em_dots() / UPM) < 1.5); + assert(@abs(width(37) - 361.0 * em_dots() / UPM) < 1.5); + assert(width(18) < width(32)); + assert(width(1) < width(0)); +} + +test "an H stands on rows 1 to 12, with nothing above and only a fringe below" { + assert(row_max(17, 0) == 0); + assert(row_max(17, 1) > 200); + assert(row_max(17, 12) > 200); + assert(row_max(17, 13) < 64); + assert(row_max(17, 14) == 0); +} + +test "the comma descends, the period sits on the baseline, the hyphen floats" { + assert(row_max(38, 14) > 96); + assert(row_max(36, 14) < 64); + assert(row_max(36, 12) > 200); + assert(row_max(37, 1) == 0); + assert(row_max(37, 12) == 0); + assert(row_max(37, 9) > 200); +} diff --git a/specs/t27f/token_floor.t27 b/specs/t27f/token_floor.t27 index 500992ca59..5d578683fb 100644 --- a/specs/t27f/token_floor.t27 +++ b/specs/t27f/token_floor.t27 @@ -25,8 +25,10 @@ // contact between neighbours and with the floor (contact_f_over_r) with the engine's dashpot // C_CONTACT, integrated by velocity Verlet at STEPS substeps of H per frame. Air drag on a 34 mm // ball at these speeds is under 5 percent of its weight and is left out. -// Every letter is made of particles. Each lit dot of a 5x7 dot-matrix glyph is a body steered to -// its place by the engine's critically damped PD controller (KP, KD): control enters as a force, +// Every letter is made of particles, set in t27.ai's own display face, Outfit ExtraBold +// (specs/t27f/outfit_atlas.t27): the outline sampled 12 dots to the cap height, each glyph as wide +// as its advance, and a dot's light the share of its point the outline covers. Each dot is a body +// steered to its place by the engine's critically damped PD controller (KP, KD): control enters as a force, // never as a position. The labels of a ball are emitted by that ball at the frame the integrator // finds it touching the stack: the floor label from the white ball, the decade tick beside each // ball from the ball itself. The exponent of the counter "X10^k" is one pool of particles that @@ -71,6 +73,17 @@ use t27f::particle_engine::glow_of; use t27f::reel_particles::lcg; use t27f::reel_particles::unit; use t27f::reel_particles::quantize; +use t27f::outfit_atlas::ATLAS_H; +use t27f::outfit_atlas::CAP_TOP; +use t27f::outfit_atlas::CAP_ROWS; +use t27f::outfit_atlas::UPM; +use t27f::outfit_atlas::GLYPHS; +use t27f::outfit_atlas::ADV; +use t27f::outfit_atlas::LSB; +use t27f::outfit_atlas::cov; +use t27f::outfit_atlas::run_start; +use t27f::outfit_atlas::run_end; +use t27f::outfit_atlas::em_dots; pub const ID : str = "token-floor"; pub const VERSION : u8 = 1; @@ -128,10 +141,12 @@ pub const HALO : f64 = 0.10; pub const HALO_R : f64 = 1.25; // ---- the letters ---- -pub const DOT_R : f64 = 0.42; // a dot's radius as a share of the glyph pitch +pub const DOT_R : f64 = 0.45; // a dot's radius as a share of the grid pitch +pub const DOT_MIN : i64 = 96; // the cover (of 255) an atlas point needs to hold a dot +pub const DOT_FULL : f64 = 192.0; // the cover at which a dot has its full light pub const RAMP : f64 = 6.0; // frames for a new dot to light pub const OFF_FADE : f64 = 10.0; // frames for a counter dot no longer needed to go out -pub const POOL : usize = 48; // the counter's particles +pub const POOL : usize = 160; // the counter's particles pub const COUNTER_GAIN : f64 = 5.0; // the counter steers five times as fast as the letters pub const SCAT_X : f64 = 700.0; pub const SCAT_Y : f64 = 420.0; @@ -143,8 +158,8 @@ pub const TICK_X : f64 = 916.0; pub const TICK_PITCH : f64 = 4.0; pub const TICK_DELAY : f64 = 3.0; -pub const MAX_DOTS : usize = 6144; -pub const MAX_SPLATS : usize = 12416; // every dot, its mirror image, and 128 more +pub const MAX_DOTS : usize = 22528; +pub const MAX_SPLATS : usize = 45312; // every dot, its mirror image, and 256 more pub const ROW_BYTES : usize = 3240; // Splat kinds. @@ -184,50 +199,6 @@ pub const COL : [12]f64 = [2.4, 1.45, 0.35, 2.3, 2.3, 2.3, 1.0, 1.0, 1.0, 0.6, 0 // Base light of the white ball and of a gold ball, r g b. pub const BALL_COL : [6]f64 = [1.5, 1.5, 1.6, 1.7, 1.0, 0.22]; -// A 5x7 dot-matrix font, seven rows a glyph, bit 4 the leftmost column: 0-9, A-Z, '.', '-', ',', ' '. -pub const FONT : [280]u8 = [ - 14, 17, 19, 21, 25, 17, 14, - 4, 12, 4, 4, 4, 4, 14, - 14, 17, 1, 2, 4, 8, 31, - 31, 2, 4, 2, 1, 17, 14, - 2, 6, 10, 18, 31, 2, 2, - 31, 16, 30, 1, 1, 17, 14, - 6, 8, 16, 30, 17, 17, 14, - 31, 1, 2, 4, 8, 8, 8, - 14, 17, 17, 14, 17, 17, 14, - 14, 17, 17, 15, 1, 2, 12, - 14, 17, 17, 31, 17, 17, 17, - 30, 17, 17, 30, 17, 17, 30, - 14, 17, 16, 16, 16, 17, 14, - 28, 18, 17, 17, 17, 18, 28, - 31, 16, 16, 30, 16, 16, 31, - 31, 16, 16, 30, 16, 16, 16, - 14, 17, 16, 23, 17, 17, 15, - 17, 17, 17, 31, 17, 17, 17, - 14, 4, 4, 4, 4, 4, 14, - 7, 2, 2, 2, 2, 18, 12, - 17, 18, 20, 24, 20, 18, 17, - 16, 16, 16, 16, 16, 16, 31, - 17, 27, 21, 21, 17, 17, 17, - 17, 17, 25, 21, 19, 17, 17, - 14, 17, 17, 17, 17, 17, 14, - 30, 17, 17, 30, 16, 16, 16, - 14, 17, 17, 17, 21, 18, 13, - 30, 17, 17, 30, 20, 18, 17, - 15, 16, 16, 14, 1, 1, 30, - 31, 4, 4, 4, 4, 4, 4, - 17, 17, 17, 17, 17, 17, 14, - 17, 17, 17, 17, 17, 10, 4, - 17, 17, 17, 21, 21, 21, 10, - 17, 17, 10, 4, 10, 17, 17, - 17, 17, 17, 10, 4, 4, 4, - 31, 1, 2, 4, 8, 16, 31, - 0, 0, 0, 0, 0, 12, 12, - 0, 0, 0, 14, 0, 0, 0, - 0, 0, 0, 0, 12, 4, 8, - 0, 0, 0, 0, 0, 0, 0 -]; - // ---- the fact ---- fn floor_j() -> f64 { @@ -313,45 +284,100 @@ fn glyph(c: u8) -> i64 { return 0 - 1; } -// Is the dot at row `row`, column `col` of character c lit? -fn lit(c: u8, row: i64, col: i64) -> bool { +// ---- the letters' shape: t27.ai's Outfit ExtraBold, specs/t27f/outfit_atlas.t27 ---- + +// The grid a label's dots sit on. A label's cap height stays 7 pitches, the height the layout was +// drawn for; the atlas cuts it into CAP_ROWS rows. +pub fn grid(p: f64) -> f64 { + return 7.0 * p / CAP_ROWS; +} + +// px a font unit, on a label of pitch p. +fn unit_px(p: f64) -> f64 { + return grid(p) * em_dots() / UPM; +} + +fn adv_px(c: u8, p: f64) -> f64 { const g = glyph(c); if (g < 0) { - return false; + return 0.0; } - const v = @as(i64, @intCast(FONT[@as(usize, @intCast(g * 7 + row))])); - var p : i64 = 16; - var k : i64 = 0; - while (k < col) { - p = @divTrunc(p, 2); - k = k + 1; + return @as(f64, @floatFromInt(ADV[@as(usize, @intCast(g))])) * unit_px(p); +} + +fn lsb_px(g: usize, p: f64) -> f64 { + return @as(f64, @floatFromInt(LSB[g])) * unit_px(p); +} + +// An atlas point holds a dot when the outline covers enough of it; the dot's light is that cover. +fn holds(col: usize, row: usize) -> bool { + return cov(col, row) >= DOT_MIN; +} + +fn weight(col: usize, row: usize) -> f64 { + const w = @as(f64, @floatFromInt(cov(col, row))) / DOT_FULL; + if (w > 1.0) { + return 1.0; } - return @mod(@divTrunc(v, p), 2) == 1; + return w; } -fn label_dots(l: usize) -> i64 { - const n = seg_len(l); +// The glyph index of a character with ink, or -1 (the space, and anything without a glyph). +fn inked(c: u8) -> i64 { + const g = glyph(c); + if (g < 0 || g >= @as(i64, @intCast(GLYPHS))) { + return 0 - 1; + } + return g; +} + +fn glyph_dots(c: u8) -> i64 { + const g = inked(c); + if (g < 0) { + return 0; + } + const gu = @as(usize, @intCast(g)); var d : i64 = 0; - var c : usize = 0; - while (c < n) { - const ch = label_char(l, c); - var r : i64 = 0; - while (r < 7) { - var b : i64 = 0; - while (b < 5) { - if (lit(ch, r, b)) { - d = d + 1; - } - b = b + 1; + var cc = run_start(gu); + const ce = run_end(gu); + while (cc < ce) { + var r : usize = 0; + while (r < ATLAS_H) { + if (holds(cc, r)) { + d = d + 1; } r = r + 1; } + cc = cc + 1; + } + return d; +} + +pub fn label_dots(l: usize) -> i64 { + const n = seg_len(l); + var d : i64 = 0; + var c : usize = 0; + while (c < n) { + d = d + glyph_dots(label_char(l, c)); c = c + 1; } return d; } -fn total_dots() -> i64 { +// How far a label runs to the right, px: the sum of its advances. +pub fn label_width(l: usize) -> f64 { + const n = seg_len(l); + const p = lbl_f(l, 2); + var w : f64 = 0.0; + var c : usize = 0; + while (c < n) { + w = w + adv_px(label_char(l, c), p); + c = c + 1; + } + return w; +} + +pub fn total_dots() -> i64 { var d : i64 = 0; var l : usize = 0; while (l < LABELS) { @@ -421,7 +447,7 @@ fn is_counter(l: usize) -> bool { } // The most dots any exponent needs; the pool holds them all. -fn counter_need() -> i64 { +pub fn counter_need() -> i64 { var m : i64 = 0; var l : usize = FIXED; while (l < FIXED + 19) { @@ -709,13 +735,14 @@ pub fn main() -> void { } // one particle per lit dot of every label, with its place in the label - var dl : [6144]usize = undefined; - var dtx : [6144]f64 = undefined; - var dty : [6144]f64 = undefined; - var dx : [6144]f64 = undefined; - var dy : [6144]f64 = undefined; - var dvx : [6144]f64 = undefined; - var dvy : [6144]f64 = undefined; + var dl : [22528]usize = undefined; + var dtx : [22528]f64 = undefined; + var dty : [22528]f64 = undefined; + var dx : [22528]f64 = undefined; + var dy : [22528]f64 = undefined; + var dvx : [22528]f64 = undefined; + var dvy : [22528]f64 = undefined; + var dw : [22528]f64 = undefined; var nd : usize = 0; var lfirst : [53]usize = undefined; var lnum : [53]usize = undefined; @@ -727,27 +754,38 @@ pub fn main() -> void { const x0 = lbl_f(l, 0); const y0 = lbl_f(l, 1); const p = lbl_f(l, 2); + const q = grid(p); + var pen : f64 = x0; var c : usize = 0; while (c < ln) { const ch = TEXT[st + c]; - var r : i64 = 0; - while (r < 7) { - var b : i64 = 0; - while (b < 5) { - if (lit(ch, r, b)) { - dl[nd] = l; - dtx[nd] = x0 + (@as(f64, @floatFromInt(c)) * 6.0 + @as(f64, @floatFromInt(b)) + 0.5) * p; - dty[nd] = y0 + (@as(f64, @floatFromInt(r)) + 0.5) * p; - dx[nd] = dtx[nd]; - dy[nd] = dty[nd]; - dvx[nd] = 0.0; - dvy[nd] = 0.0; - nd = nd + 1; + const g = inked(ch); + if (g >= 0) { + const gu = @as(usize, @intCast(g)); + const rs = run_start(gu); + const re = run_end(gu); + const ix = pen + lsb_px(gu, p); + var cc = rs; + while (cc < re) { + var r : usize = 0; + while (r < ATLAS_H) { + if (holds(cc, r)) { + dl[nd] = l; + dtx[nd] = ix + (@as(f64, @floatFromInt(cc - rs)) + 0.5) * q; + dty[nd] = y0 + (@as(f64, @floatFromInt(@as(i64, @intCast(r)) - CAP_TOP)) + 0.5) * q; + dw[nd] = weight(cc, r); + dx[nd] = dtx[nd]; + dy[nd] = dty[nd]; + dvx[nd] = 0.0; + dvy[nd] = 0.0; + nd = nd + 1; + } + r = r + 1; } - b = b + 1; + cc = cc + 1; } - r = r + 1; } + pen = pen + adv_px(ch, p); c = c + 1; } lnum[l] = nd - lfirst[l]; @@ -755,32 +793,34 @@ pub fn main() -> void { } // the counter's exponent is one pool of particles that re-forms into each next number; the // labels 14-32 only hold the places its dots go - var cx : [48]f64 = undefined; - var cy : [48]f64 = undefined; - var cvx : [48]f64 = undefined; - var cvy : [48]f64 = undefined; - var ctx : [48]f64 = undefined; - var cty : [48]f64 = undefined; - var ca : [48]f64 = undefined; - var cborn : [48]bool = undefined; + var cx : [160]f64 = undefined; + var cy : [160]f64 = undefined; + var cvx : [160]f64 = undefined; + var cvy : [160]f64 = undefined; + var ctx : [160]f64 = undefined; + var cty : [160]f64 = undefined; + var ca : [160]f64 = undefined; + var cw : [160]f64 = undefined; + var cborn : [160]bool = undefined; var ci0 : usize = 0; while (ci0 < POOL) { ca[ci0] = 0.0; + cw[ci0] = 1.0; cborn[ci0] = false; ci0 = ci0 + 1; } - var sx : [12416]f64 = undefined; - var sy : [12416]f64 = undefined; - var sr : [12416]f64 = undefined; - var sw : [12416]f64 = undefined; - var sa : [12416]f64 = undefined; - var sc : [12416]f64 = undefined; - var scr : [12416]f64 = undefined; - var scg : [12416]f64 = undefined; - var scb : [12416]f64 = undefined; - var sk : [12416]u8 = undefined; - var sm : [12416]u8 = undefined; + var sx : [45312]f64 = undefined; + var sy : [45312]f64 = undefined; + var sr : [45312]f64 = undefined; + var sw : [45312]f64 = undefined; + var sa : [45312]f64 = undefined; + var sc : [45312]f64 = undefined; + var scr : [45312]f64 = undefined; + var scg : [45312]f64 = undefined; + var scb : [45312]f64 = undefined; + var sk : [45312]u8 = undefined; + var sm : [45312]u8 = undefined; var accr : [1080]f64 = undefined; var accg : [1080]f64 = undefined; var accb : [1080]f64 = undefined; @@ -866,6 +906,7 @@ pub fn main() -> void { const td = lfirst[FIXED + kc - 1] + pi; ctx[pi] = dtx[td]; cty[pi] = dty[td]; + cw[pi] = dw[td]; if (cborn[pi] == false) { var seed : i64 = lcg(@as(i64, @intCast(pi)) * 6007 + 211); const u1 = unit(seed); @@ -913,13 +954,13 @@ pub fn main() -> void { const a = clamp01(@as(f64, @floatFromInt(frame - on + 1)) / RAMP); if (a > 0.0) { const ci = @as(usize, @intFromFloat(lbl_f(ld, 3))) * 3; - const p = lbl_f(ld, 2); - const gl = glow_of(dvx[d] / PXM, dvy[d] / PXM); - sx[n] = dx[d]; sy[n] = dy[d]; sr[n] = DOT_R * p; sw[n] = 0.0; sa[n] = a * gl; + const q = grid(lbl_f(ld, 2)); + const gl = glow_of(dvx[d] / PXM, dvy[d] / PXM) * dw[d]; + sx[n] = dx[d]; sy[n] = dy[d]; sr[n] = DOT_R * q; sw[n] = 0.0; sa[n] = a * gl; scr[n] = COL[ci]; scg[n] = COL[ci + 1]; scb[n] = COL[ci + 2]; sk[n] = K_DOT; sm[n] = 0; n = n + 1; if (dy[d] < FLOOR_PX && FLOOR_PX - dy[d] < REFLECT_SPAN) { - sx[n] = dx[d]; sy[n] = 2.0 * FLOOR_PX - dy[d]; sr[n] = DOT_R * p; sw[n] = 0.0; + sx[n] = dx[d]; sy[n] = 2.0 * FLOOR_PX - dy[d]; sr[n] = DOT_R * q; sw[n] = 0.0; sa[n] = a * gl * reflect_alpha(dy[d]); scr[n] = COL[ci]; scg[n] = COL[ci + 1]; scb[n] = COL[ci + 2]; sk[n] = K_DOT; sm[n] = 1; n = n + 1; @@ -932,8 +973,8 @@ pub fn main() -> void { pi = 0; while (pi < POOL) { if (cborn[pi] && ca[pi] > 0.0) { - sx[n] = cx[pi]; sy[n] = cy[pi]; sr[n] = DOT_R * EXP_PITCH; sw[n] = 0.0; - sa[n] = ca[pi] * glow_of(cvx[pi] / PXM, cvy[pi] / PXM); + sx[n] = cx[pi]; sy[n] = cy[pi]; sr[n] = DOT_R * grid(EXP_PITCH); sw[n] = 0.0; + sa[n] = ca[pi] * cw[pi] * glow_of(cvx[pi] / PXM, cvy[pi] / PXM); scr[n] = COL[0]; scg[n] = COL[1]; scb[n] = COL[2]; sk[n] = K_DOT; sm[n] = 0; n = n + 1; } @@ -1159,25 +1200,42 @@ test "every character on the screen has a glyph, and there are 53 labels" { assert(glyph(124) < 0); } -test "the glyphs: rows are five bits, the 1 has a foot, the E a full top bar" { - var i : usize = 0; - while (i < 280) { - assert(FONT[i] < 32); - i = i + 1; +test "the letters are Outfit's: a W outweighs an I, a space has no ink, a 7-pitch cap is 12 rows" { + // measured on the lab: W 120 dots, I 36, the period 9 + assert(glyph_dots(87) == 120); + assert(glyph_dots(73) == 36); + assert(glyph_dots(46) == 9); + assert(glyph_dots(32) == 0); + assert(adv_px(87, 10.0) > adv_px(73, 10.0) * 3.0); + assert(@abs(grid(12.0) * CAP_ROWS - 7.0 * 12.0) < 0.000001); + // an H's stem: the outer column is an edge, half lit; the next is solid + assert(weight(run_start(17), 6) < 0.6); + assert(weight(run_start(17) + 1, 6) == 1.0); +} + +test "every label stays inside the frame" { + var l : usize = 0; + var out : i64 = 0; + while (l < LABELS) { + const x0 = lbl_f(l, 0); + if (x0 < 0.0 || x0 + label_width(l) > @as(f64, @floatFromInt(FRAME_W)) - 16.0) { + out = out + 1; + } + // a line left of the tube, above the floor, ends before the glass + if (x0 < TUBE_X && lbl_f(l, 1) < FLOOR_PX && x0 + label_width(l) > TUBE_X - TUBE_HALF - 24.0) { + out = out + 1; + } + l = l + 1; } - assert(lit(49, 6, 1) && lit(49, 6, 2) && lit(49, 6, 3)); - assert(lit(49, 6, 0) == false); - assert(lit(69, 0, 0) && lit(69, 0, 4)); - assert(lit(48, 0, 0) == false); - assert(lit(48, 0, 1)); - assert(lit(32, 3, 2) == false); - assert(label_dots(5) == 42); + assert(out == 0); + assert(label_dots(5) > 150); } test "every dot and its image fit the buffers" { - assert(total_dots() > 2000); + assert(total_dots() > 8000); assert(total_dots() <= @as(i64, @intCast(MAX_DOTS))); - assert(2 * MAX_DOTS + 128 == MAX_SPLATS); + assert(2 * MAX_DOTS + 256 == MAX_SPLATS); + assert(POOL + 3 * BALLS + 5 <= 256); } test "a dot reaches its letter on the critically damped path and never overshoots" { diff --git a/specs/t27f/token_floor_mux.t27 b/specs/t27f/token_floor_mux.t27 index 88c108f12b..8aa7235265 100644 --- a/specs/t27f/token_floor_mux.t27 +++ b/specs/t27f/token_floor_mux.t27 @@ -27,6 +27,7 @@ // THE CAPTIONS: the v1 reel centred them at y=1600; here that is the floor's labels. The free // band on this frame is the left column between the bill label and the floor labels, left of the // tube, so captions are left-aligned at BAND_X, centred on CAPTION_Y, and sized to fit BAND_W. +// They are set in t27.ai's own caption face, JetBrains Mono ExtraBold, not the v1 reel's Inter. // // THE OUTPUT: `main` writes the graph to stderr, for `ffmpeg -filter_complex_script`. Inputs: // 0 = the frames, 1..6 = the phrases p0.wav..p5.wav, 7 = the music (bgmusic.mp3). @@ -42,7 +43,6 @@ use automation::owner_voice_reel::CAPTION_BORDER_PX; use automation::owner_voice_reel::CAPTION_SHADOW_Y_PX; use automation::owner_voice_reel::CAPTION_TAIL_MS; use automation::owner_voice_reel::CAPTION_FONT_MAX; -use automation::owner_voice_reel::CHAR_WIDTH_PERMILLE; use automation::owner_voice_reel::VOICE_LUFS; use automation::owner_voice_reel::VOICE_LRA; use automation::owner_voice_reel::MUSIC_VOLUME_PERMILLE; @@ -50,7 +50,6 @@ use automation::owner_voice_reel::MUSIC_FADE_IN_MS; use automation::owner_voice_reel::MUSIC_FADE_OUT_START_MS; use automation::owner_voice_reel::LIMIT_PERMILLE; use automation::owner_voice_reel::font_size; -use t27f::reel_particles_mux::CAPTION_FONT; use t27f::reel_particles_mux::MUSIC_FADE_OUT_MS; use t27f::reel_particles_mux::secs; use t27f::reel_particles_mux::millis; @@ -65,11 +64,20 @@ use t27f::token_floor::land_frame; pub const ID : str = "token-floor-mux"; pub const WORD_DIR : str = "/root/tf/w"; +// ---- the caption face: t27.ai's JetBrains Mono ExtraBold ---- +// t27.ai serves JetBrains Mono as variable woff2 files, one per script. ffmpeg's drawtext reads +// no variation axes, so each was cut to a static wght=800 TTF on the lab (fontTools varLib +// instancer). The Cyrillic subset has no digits, so the three words that are numbers ("19", "19", +// "1,7") use the Latin one. Every glyph is 600 of the 1000-unit em wide. +pub const CAPTION_FONT_CYR : str = "/root/tf/fonts/jbm-cyr-800.ttf"; +pub const CAPTION_FONT_LAT : str = "/root/tf/fonts/jbm-lat-800.ttf"; +pub const MONO_ADVANCE_PERMILLE : i32 = 600; + // ---- the caption band (design choices, px) ---- pub const BAND_X : i32 = 60; pub const BAND_W : i32 = 720; pub const CAPTION_Y : i32 = 1250; -pub const GLYPH_ROWS : f64 = 7.0; // token_floor's dot font is seven rows tall +pub const GLYPH_ROWS : f64 = 8.2; // a label is 7 pitches to its cap; a comma hangs 14/12 of that // ---- the phrases ---- pub const PHRASES : usize = 6; @@ -85,6 +93,8 @@ pub const WORD_PHRASE : [24]usize = [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3 pub const WORD_START_MS : [24]i32 = [10, 630, 910, 1150, 0, 520, 860, 1100, 0, 680, 1020, 1220, 1660, 1800, 0, 560, 1160, 1420, 0, 760, 1120, 1960, 0, 520]; pub const WORD_END_MS : [24]i32 = [630, 910, 1150, 1650, 520, 740, 1100, 1660, 680, 1020, 1220, 1660, 1800, 2060, 560, 1000, 1420, 2080, 760, 1120, 1960, 2560, 520, 1080]; pub const WORD_CHARS : [24]i32 = [4, 5, 6, 6, 5, 3, 6, 9, 6, 7, 3, 8, 2, 6, 2, 5, 2, 8, 8, 5, 3, 6, 9, 9]; +// The words written in digits: P3's two "19" and P4's "1,7". +pub const WORD_LATIN : [24]bool = [false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, true, false, false, false]; // ---- the events ---- @@ -137,9 +147,16 @@ fn word_until(k: usize) -> i32 { return u; } -// The v1 size rule, narrowed to the band. +fn caption_font(k: usize) -> str { + if (WORD_LATIN[k]) { + return CAPTION_FONT_LAT; + } + return CAPTION_FONT_CYR; +} + +// The v1 size rule, narrowed to the band, at the mono face's advance. fn band_font(chars: i32) -> i32 { - const fit : i32 = @divTrunc(BAND_W * 1000, chars * CHAR_WIDTH_PERMILLE); + const fit : i32 = @divTrunc(BAND_W * 1000, chars * MONO_ADVANCE_PERMILLE); const v1 = font_size(chars); if (fit < v1) { return fit; @@ -168,7 +185,7 @@ pub fn main() -> void { while (k < WORDS) { const a = word_from(k); const b = word_until(k); - std.debug.print(",drawtext=fontfile={s}:textfile={s}/w{d:0>2}.txt:fontsize={d}:fontcolor=0xFFFF00:borderw={d}:bordercolor=0x000000:shadowx=0:shadowy={d}:shadowcolor=0x000000@0.6:x={d}:y={d}-text_h/2:enable='between(t,{d}.{d:0>3},{d}.{d:0>3})'", .{ CAPTION_FONT, WORD_DIR, k + 1, band_font(WORD_CHARS[k]), CAPTION_BORDER_PX, CAPTION_SHADOW_Y_PX, BAND_X, CAPTION_Y, secs(a), millis(a), secs(b), millis(b) }); + std.debug.print(",drawtext=fontfile={s}:textfile={s}/w{d:0>2}.txt:fontsize={d}:fontcolor=0xFFFF00:borderw={d}:bordercolor=0x000000:shadowx=0:shadowy={d}:shadowcolor=0x000000@0.6:x={d}:y={d}-text_h/2:enable='between(t,{d}.{d:0>3},{d}.{d:0>3})'", .{ caption_font(k), WORD_DIR, k + 1, band_font(WORD_CHARS[k]), CAPTION_BORDER_PX, CAPTION_SHADOW_Y_PX, BAND_X, CAPTION_Y, secs(a), millis(a), secs(b), millis(b) }); k = k + 1; } std.debug.print("[v];", .{}); @@ -256,10 +273,14 @@ test "the voice and the last caption end before the music fades" { test "the widest caption fits the band" { var k : usize = 0; while (k < WORDS) { - assert(WORD_CHARS[k] * CHAR_WIDTH_PERMILLE * band_font(WORD_CHARS[k]) <= BAND_W * 1000); + assert(WORD_CHARS[k] * MONO_ADVANCE_PERMILLE * band_font(WORD_CHARS[k]) + 2 * CAPTION_BORDER_PX * 1000 <= BAND_W * 1000); k = k + 1; } - assert(band_font(9) == 111); + assert(band_font(9) == CAPTION_FONT_MAX); + assert(band_font(12) == 100); + assert(WORD_LATIN[14] && WORD_LATIN[16] && WORD_LATIN[20]); + assert(WORD_CHARS[14] == 2 && WORD_CHARS[20] == 3); + assert(WORD_LATIN[0] == false && WORD_LATIN[23] == false); assert(band_font(8) == CAPTION_FONT_MAX); assert(band_font(2) == CAPTION_FONT_MAX); }