diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 1e75ea755d53b8604172bec228e8791b2f1e3bc8..0000000000000000000000000000000000000000 --- a/.dockerignore +++ /dev/null @@ -1,6 +0,0 @@ -__pycache__ -**/__pycache__ -*.pyc -**/target -.cache -runs diff --git a/.gitattributes b/.gitattributes index 3bb213f5c77385699f20716f58b485ad2f610b7a..ca2fc4367ddd164dae7cd48e08f0bc67a02daece 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,42 +1 @@ -*.7z filter=lfs diff=lfs merge=lfs -text -*.arrow filter=lfs diff=lfs merge=lfs -text -*.bin filter=lfs diff=lfs merge=lfs -text -*.bz2 filter=lfs diff=lfs merge=lfs -text -*.ckpt filter=lfs diff=lfs merge=lfs -text -*.ftz filter=lfs diff=lfs merge=lfs -text -*.gz filter=lfs diff=lfs merge=lfs -text -*.h5 filter=lfs diff=lfs merge=lfs -text -*.joblib filter=lfs diff=lfs merge=lfs -text -*.lfs.* filter=lfs diff=lfs merge=lfs -text -*.mlmodel filter=lfs diff=lfs merge=lfs -text -*.model filter=lfs diff=lfs merge=lfs -text -*.msgpack filter=lfs diff=lfs merge=lfs -text -*.npy filter=lfs diff=lfs merge=lfs -text -*.npz filter=lfs diff=lfs merge=lfs -text -*.onnx filter=lfs diff=lfs merge=lfs -text -*.ot filter=lfs diff=lfs merge=lfs -text -*.parquet filter=lfs diff=lfs merge=lfs -text -*.pb filter=lfs diff=lfs merge=lfs -text -*.pickle filter=lfs diff=lfs merge=lfs -text -*.pkl filter=lfs diff=lfs merge=lfs -text -*.pt filter=lfs diff=lfs merge=lfs -text -*.pth filter=lfs diff=lfs merge=lfs -text -*.rar filter=lfs diff=lfs merge=lfs -text -*.safetensors filter=lfs diff=lfs merge=lfs -text -saved_model/**/* filter=lfs diff=lfs merge=lfs -text -*.tar.* filter=lfs diff=lfs merge=lfs -text -*.tar filter=lfs diff=lfs merge=lfs -text -*.tflite filter=lfs diff=lfs merge=lfs -text -*.tgz filter=lfs diff=lfs merge=lfs -text -*.wasm filter=lfs diff=lfs merge=lfs -text -*.xz filter=lfs diff=lfs merge=lfs -text -*.zip filter=lfs diff=lfs merge=lfs -text -*.zst filter=lfs diff=lfs merge=lfs -text -*tfevents* filter=lfs diff=lfs merge=lfs -text -tracks/R06[[:space:]]Mix.track filter=lfs diff=lfs merge=lfs -text -tracks/R07[[:space:]]Mirage[[:space:]]Marina.track filter=lfs diff=lfs merge=lfs -text -tracks/R09[[:space:]]Eau[[:space:]]Noir.track filter=lfs diff=lfs merge=lfs -text -tracks/The[[:space:]]Champions[[:space:]]Trial.track filter=lfs diff=lfs merge=lfs -text -bin/altd-sim filter=lfs diff=lfs merge=lfs -text -bin/libaltd_gpu.so filter=lfs diff=lfs merge=lfs -text -bin/extract_saved_track filter=lfs diff=lfs merge=lfs -text +bin/* filter=lfs diff=lfs merge=lfs -text diff --git a/BETA_TESTING.md b/BETA_TESTING.md index 848626a248cf805a451bfacc7ea307622df856cc..5cf6196ac5a231994f0159f8eadf8dcbfacbc895 100644 --- a/BETA_TESTING.md +++ b/BETA_TESTING.md @@ -1,7 +1,7 @@ # Small-group beta entry gate -Current status: local UI/CPU testing is useful now. The expanded runner interface -has not yet been qualified on the hosted HF GPU runtime. Do not describe it as a +Current status: local UI/CPU testing is useful now. The refreshed runner interface +requires hosted CPU and GPU qualification before inviting a broad audience. Do not describe it as a stable release or invite a broad audience to an unqualified build. ## Protected beta address @@ -47,13 +47,13 @@ Before remote testers receive the expanded interface: A stop/error must remain visibly different from a complete batch. - Create/edit/export/re-import a small network. Inspect or edit a known weight and verify the exported value. Modified artifacts must not inherit old scores. -- Exercise default deletion, manual repeat downloads/delete, browser-only re-save, +- Exercise default deletion, opt-in repeat download, manual delete, browser-only re-save, result expiry, invalid files, missing files and connection failure. Current supported worker contract: Formula steering/acceleration, speed/raycast sensors, 2–4 layers with at most 16 nodes per layer, and bounded execution. Inspection can show larger networks, but that is not permission to train them. -The track map remains an intentional placeholder. Star challenges are explicitly +The track map previews selected tile layouts and terrain, including custom uploads, but does not show collision geometry. Star challenges are explicitly not evaluated. No driving improvement or real-game/leaderboard parity follows from a successful artifact lifecycle test. A bucket and broad network support remain separate work. diff --git a/BINARY_PROVENANCE.json b/BINARY_PROVENANCE.json index b0802dad1b3b2fa165c8a466e6cd88f2f291af58..2c34183813d6a4848cdd9e07ccfd906167124ed3 100644 --- a/BINARY_PROVENANCE.json +++ b/BINARY_PROVENANCE.json @@ -1,22 +1,22 @@ { - "source_space": "TiGa-RCE/ald-tao-gpu", - "source_revision": "ba07e35a39211af7a0deda0ebd9adb2c554b4f38", - "upstream": "d8ab74c25df70b6a86ed101080e33d5fb785ea6f", - "image_config": "sha256:31f7854e5fac0b54679eed2899c8951cd96571650ea6c74c27e2400a8c4aa6d3", - "artifacts": [ - { - "file": "bin/altd-sim", - "sha256": "2f2f2144ae14b0ef599ca8510f95712da00243c6875881491d384b394e7499f7", - "bytes": 13911800, - "layer": "sha256:d4f213ac74fe7556e8ef4d71aa6195a80dd619057697748f2f3863c0aa1cbca7", - "member": "app/github-simulator/source/target/release/altd-sim" + "source_revision": "b90b8af06ffc4ea4e4fd703becd1107a48b8e5e0", + "source_repository": "https://github.com/Zorro909/driving_sim_rs", + "github_actions_run": 38203558700, + "run_conclusion": "success", + "importer_source_revision": "d8ab74c25df70b6a86ed101080e33d5fb785ea6f", + "artifacts": { + "bin/altd-sim": { + "sha256": "0135ecc3d435a9d327be7a92e048cfb5c5291a02ef1e0dddd6ead1cc19b31166", + "bytes": 15774792 }, - { - "file": "bin/libaltd_gpu.so", - "sha256": "279d6353b4c691686d6aeeae6abfd4a82b959e9b604f079a6c45f9823b48a87a", - "bytes": 3933424, - "layer": "sha256:c61e59a8a076ae00d64a4b8c55102f2ba9643dbe165cd66130ed662e131982ce", - "member": "app/libaltd_gpu.so" + "bin/extract_saved_track": { + "sha256": "3a7733c45b4301a786fcbfe92660a1c484749272da5f608d62f34878bd4ff31a", + "bytes": 3247776 + }, + "bin/libaltd_gpu.so": { + "sha256": "0064e747543251bab90cb966f8df00b010716a92cc75910229d42c9b24c1dac9", + "bytes": 12291032 } - ] + }, + "note": "altd-sim and GPU library from the same successful upstream Actions run; importer was built and smoke-checked separately; hosted runtime acceptance is a separate check" } diff --git a/DATA_POLICY.md b/DATA_POLICY.md index 59730bf1e262228e1efb1167756f200f02413c03..26153554cc8e018723e89c61b4109f8c75a397a8 100644 --- a/DATA_POLICY.md +++ b/DATA_POLICY.md @@ -1,6 +1,6 @@ # Data handling & deletion policy — private integration test -Operator: TiGa (seeker-cyber-maker). Version: local runner candidate 2026-10-09. +Operator: TiGa (seeker-cyber-maker). Version: HF runner refresh 2026-10-12. Files are processed only for the requested job, not collected for our own training, analytics or public examples. Inputs remain in the browser until Run. @@ -28,14 +28,18 @@ Each job uses temporary files removed before a successful result is published. The application result store is memory-only. No public file URL is created. The browser holds a random job access key in memory, independently of optional HF sign-in. Anyone who obtains that key can access the job; do not share it. Reloading the -page loses access. Expiry is fixed at 30 minutes from result publication. +page loses access. Expiry is fixed at five minutes from result publication. -Automatic deletion is enabled by default. The browser validates the complete -download and acknowledges receipt; that deletes the result store's copy. One -download attempt is permitted. We cannot verify that the browser saved to disk. -An interrupted attempt is not retryable in automatic mode; Delete now and expiry -still work. Unchecking automatic deletion allows repeat downloads until expiry. -Delete now removes the stored result immediately. Downloads never reset expiry. +Automatic deletion is the default. The browser validates the complete download +and acknowledges receipt; that deletes the result store's copy. One server +download attempt is permitted in the default mode. We cannot verify that the +browser saved to disk. An interrupted default-mode download is not retryable; +Delete now and fixed expiry still work. Visitors can opt in for each single run +to keep its result in application memory for repeat downloads until the same +five-minute deadline. The checkbox resets after every run. Batch runs always +use automatic deletion. Delete now removes a stored result immediately, and +downloads never reset expiry. The browser can also save its received result +again from page memory. Access is refused at the deadline. While the server is running, the expiry loop releases expired stored data within approximately one second, subject to runtime @@ -70,8 +74,8 @@ The browser retains its received result in page memory so Save received result again can retry a local save without another server retrieval. Discard browser result clears the app's reference; closing or refreshing the page loses it. No bucket support, network sandbox or provider-wide ZDR guarantee is offered. -The service uses one result-owning process. Public deployment and its -framework/proxy behavior must be tested separately before release. +The service uses one result-owning process. HF framework and proxy behavior may have retention outside this app, so this is +an application-level near-zero-retention design, not a provider-wide ZDR claim. The runner candidate has a separate browser-local recipe library. Saving is explicit; settings and reward mixes, recipe names, and batch recipe references @@ -87,9 +91,7 @@ result deletion and browser result discard. Batch execution freezes the selected recipes when Start is pressed. It runs sequentially, using either the original network each time or the preceding candidate. Each intermediate result is downloaded to browser memory, checked by -size and SHA-256, and acknowledged for server deletion before the next run. Batch -intermediate results always use automatic deletion regardless of the single-run -checkbox. Only the last received candidate is retained in page memory for saving. +size and SHA-256, and acknowledged for server deletion before the next run. Batch results always use automatic deletion, regardless of the single-run retention choice. Only the last received candidate is retained in page memory for saving. Stop batch prevents the next run; it does not interrupt the current server job. Closing/reloading the page does not prove cancellation of an in-flight job. Failure to acknowledge a received result stops the batch and leaves Delete now diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 6184d412218dd894587b758bfb0ecdd3e25154e0..0000000000000000000000000000000000000000 --- a/Dockerfile +++ /dev/null @@ -1,24 +0,0 @@ -FROM rust:1.93-bookworm AS build -WORKDIR /build -COPY github-simulator/source /build -ENV RUSTFLAGS="-C target-cpu=x86-64" -RUN cargo build --locked --release --no-default-features --features cli -FROM nvidia/cuda:13.0.0-devel-ubuntu24.04 AS cuda -RUN apt-get update && apt-get install -y --no-install-recommends python3 g++ && rm -rf /var/lib/apt/lists/* -WORKDIR /build -COPY github-simulator/source /build -ENV CUDA_ARCH=sm_75,sm_86 -RUN bash gpu/build-cuda.sh -FROM nvidia/cuda:13.0.0-runtime-ubuntu24.04 -RUN apt-get update && apt-get install -y --no-install-recommends python3 && rm -rf /var/lib/apt/lists/* -RUN useradd -m -o -u 1000 user -WORKDIR /app -COPY --chown=user:user . /app -COPY --from=build --chown=user:user /build/target/release/altd-sim /app/github-simulator/source/target/release/altd-sim -COPY --from=cuda --chown=user:user /build/target/gpu/libaltd_gpu.so /app/libaltd_gpu.so -RUN chown user:user /app -USER user -ENV ALD_DASHBOARD_PORT=7860 PYTHONUNBUFFERED=1 -ENV ALD_REQUIRE_GPU=1 ALTD_GPU_LIB=/app/libaltd_gpu.so -EXPOSE 7860 -CMD ["python3","server.py"] diff --git a/FRONTEND_SOURCE.json b/FRONTEND_SOURCE.json index 59b8558a5c5f33434104069fb6e5412aa93d55fb..9b06bce66997b5686daf3ab47249832d22243ddd 100644 --- a/FRONTEND_SOURCE.json +++ b/FRONTEND_SOURCE.json @@ -1,8 +1,8 @@ { - "source": "User-provided local combined dashboard index.html", + "source": "Runner r14 single-engine browser UI; selected .track tile renderer added from simulator atlas", "source_sha256": "65d1e5f2c4e19b4951b828e82510005ab99a5270cf3dcb9f4eaa0fa1af8c40f8", - "output_sha256": "135a0c57be851af2879660ae6584a4bfa0a9280fc33e2c0372bc47b531f1960f", + "output_sha256": "d329965febb49db4939093ec0c512474db109a7b3a24e97cbabb18f97674757b", "scope": "Frontend only; no engine, private data, track assets, API calls or storage copied", - "status": "Local candidate: configurable training, browser recipes, bounded sequential batches, browser network editor; track renderer and star evaluation unconnected", - "candidate_sha256": "24fd848cd38d84d84236558e1d617d79554ae462317b3bea68b11c74d2802076" + "status": "Refresh candidate: track and terrain preview connected; star evaluation remains unconnected", + "track_atlas_sha256": "d97c8900e3e5fb17734b5bd61d1f4f4b196d9be30af9848e2e273ae7e4079ae4" } diff --git a/PACKAGE_MANIFEST.json b/PACKAGE_MANIFEST.json deleted file mode 100644 index 2fb7d9eca7f09f0e352b2ddfb7a210fb77a6caa1..0000000000000000000000000000000000000000 --- a/PACKAGE_MANIFEST.json +++ /dev/null @@ -1,328 +0,0 @@ -{ - ".dockerignore": "640b480c91c572b6d77411d9165f786a095d04fe5bc5b74e983ab6831aa4ba23", - ".gitattributes": "478a5f208decd29019ad7be4fba9fdffe25a4a4c9c652032e98e4cf3cbd6ef97", - "Dockerfile": "c592ca9eb888525821563ddb2707ecba0eeea3b06adfbe1aaa5e811339d032a7", - "README.md": "e7ed341a32da89a48dd5194ede7d3066fea0700aab2752f1886f23b7970d6ca8", - "UPSTREAM.json": "73d973fe445d7b51caea4980f35902f393c84720c8a256fd11622f84bb843806", - "backend_policy.py": "3991fb88e25bebb9c36b49c0547af4e4630738c504e0c7b58a5917081deea5fe", - "batch_runner.py": "c14777da239116a8025f800d59b33b307bf62fcfb80e3bcb59fdf81104a7c742", - "cloud-contract.json": "ac4d1946a278d3cf3aae6d750249603adfd89f3c421eda4c62bb69e2bbf7490f", - "cloud_core.py": "be517e165180baf610f6cb7e863bc76a6704233118952045a0366d72000d821a", - "demo.evolution": "2743e838371d737f3309c47dc84bc7b43f13657f4b008b2b6eb6f4b429531920", - "github-simulator/source/Cargo.lock": "d8b8dfcfd4ddd90388a9aadae6c12a675e46277a3f26dabcce5ba8af6ff12183", - "github-simulator/source/Cargo.toml": "fe4d592043df0b10f6699dcf6fe6cc7b588ed3df23bfadae11afb61924f25bd9", - "github-simulator/source/GODOT_LICENSE": "fb2b3bb82c60f1e76841ba2122eb902f8e39c151476fe21ce10c9a05d1045304", - "github-simulator/source/LICENSE": "53d5851b6440feeae36f72a5f57fcab37858c48b7084765ece4b0c54b9a28ed8", - "github-simulator/source/README.md": "cc11a5a17701416fbf11b392be303e0485aa308170ef2bf2f52815f60cac15de", - "github-simulator/source/assets/models/formula.json": "fc7849fac5e20a14427ca8adafd4dab61518920d385411a144a8798ae02de2ed", - "github-simulator/source/assets/models/rally.json": "5155711ab44e1db4aee6d3cb509d321e60d2a4ffabe1937603b3dd9daf187e1f", - "github-simulator/source/assets/networks/formula.json": "452647361c18ee196c7abd1059c384f9281ca57890135e5b164f9669fe45250c", - "github-simulator/source/assets/networks/rally.json": "75ecde48a073d1f343ced743955ffc3a7eaffcd035884452ce408b4d12707932", - "github-simulator/source/assets/random_track_settings.json": "a0c3daae1436e659a3157fcb1e4520d5a59c16f739eb6a9eb53b397862cf7179", - "github-simulator/source/assets/scenes/formula_template.json": "80f745df82bb7be45af52785fd582460bcddc63c5a4e1d8375d85b3122db4c6b", - "github-simulator/source/assets/scenes/rally_template.json": "7350a3fe28d9b4da7a0965ae6ea7ca3472fb7885303e342310d63a0dce80851c", - "github-simulator/source/assets/scenes/snowmobile_template.json": "12d30545876011fd6fccdd3e721f9c465938c9c58ac99338205481d5e7ac6742", - "github-simulator/source/assets/scenes/truck_template.json": "a83bffea4d5e818c135ab3aba68a47aa288d6ad0e7b8bd9c95ed0dea9e9df2f7", - "github-simulator/source/docs/cli.md": "095468c685d68362880323cd0aea56c0951517d2b9f5dbc281b67499e712cf28", - "github-simulator/source/docs/design.md": "5e768d010d41f021aca8bc88119709a87fe4b346aed95d31b24bb1563c7974fd", - "github-simulator/source/docs/fidelity.md": "1a58be54cc167aba2f8f78992382f3ae58005dfb7ecd6f7dd552466f10d66f54", - "github-simulator/source/docs/gpu.md": "ecdd400d988344da2a076db2b32a09d06b3578d44c6b17da67c151b2dfbecff7", - "github-simulator/source/docs/math-profiles.html": "69f7e3c9165e6c3c969805e77669ca0b13965a4e2b23c4a6e4dd8e234a9755f7", - "github-simulator/source/docs/performance.md": "fa47013ff2940458a951250166b8b4f1c406cd99734d979c701aea7b2792cca3", - "github-simulator/source/docs/reviews/2026-10-05-native-server-review.md": "4ed44f436e4348591d8728659d94e00f48bd8a2d559fda7c3fe2c695831e5b85", - "github-simulator/source/docs/server.md": "4b8899b87ba53008b66eeb878794b961f10d16280134f4716453efcab347e37a", - "github-simulator/source/docs/superpowers/specs/2026-10-05-native-server-design.md": "99ac6ee8e335ab71ddcfdd28ab4ed2d7d1cfbad7935101d72ebe39fe017fdf6b", - "github-simulator/source/docs/wasm.md": "888386a1b77546382c7ba39d4897e62b83015c6ce2ef725b436b6561031b47ef", - "github-simulator/source/gpu/README.md": "e831018c053fd7a47162d89b72b789d8c5ecd455e46f57282b23be2f9c460a43", - "github-simulator/source/gpu/benchmark.py": "413db669422dba553e54adcf083aa11372c7925232a02a23a96aef8b55954380", - "github-simulator/source/gpu/build-cuda.ps1": "dc5b4dbdf277c8522fe927f13db6e68df1554ccf5ecf78cb79128d34a1c63550", - "github-simulator/source/gpu/build-cuda.sh": "a1d64a98c968dffde896e6634914ae7312820ff04171bc29ddc4519f112c8a6d", - "github-simulator/source/gpu/build.sh": "563dfba35d55d33e1340fc80bd063931cc88129ff2214cc556d7205c71308183", - "github-simulator/source/gpu/gen_tables.py": "f0fe3dbcefa271a63093c1c7ee7c5c55166bdfd96a3daa0e7113ea37da0f6e2e", - "github-simulator/source/gpu/profile.sh": "f08fbab3a38ed6414548009e1b116f7cc289ea67d1baef7262c1c886926bde88", - "github-simulator/source/gpu/release-targets": "fdca8679baa13af1191fbc0179201e8aafdf7355ba8c5684ea378b6afeffe1a2", - "github-simulator/source/gpu/sim/altd_gpu.hip": "212f169713fd96a065822ef3c6fe712491a144b0cb1b63df0f083967f509065e", - "github-simulator/source/gpu/sim/compat.h": "23c60db69ca371dd8da6a7949911a6f69d52250448a3623e73a5c3c290331ff8", - "github-simulator/source/gpu/sim/double_tables.h": "472dbcd916293dcd91dcd3f882ba86ed36267ede819ffb1692daf8b6ad5a38ec", - "github-simulator/source/gpu/sim/engine_exceptions.h": "4568607466a307d1520d763ea2f1c7424219299caf365ffa03dd62084673a551", - "github-simulator/source/gpu/sim/math.h": "ae93aadb2d49492176a08a1bed4a1c5b687f99ae16da57736ef30661dcaaeb8b", - "github-simulator/source/gpu/sim/rays.h": "414fc93b6a5180c9ed906a0b802eea48a5d81bba8920097989dfa568bfa82f12", - "github-simulator/source/gpu/sim/sensors.h": "64390b210bf388abb49026ba0b8529144eb025b0d306fb3198745b345fe38942", - "github-simulator/source/gpu/sim/state.h": "3672b2e48d43e6696cb9a61abff7a1de6a131dc2b7cbf6d99413596565a1bb20", - "github-simulator/source/gpu/sim/stats.h": "dc0d785dceaa06e27d90f5aa2a4513eb75aea6d149bbfde9ed8965f79c233d82", - "github-simulator/source/gpu/sim/step.h": "022390d8f2eb5276969826c5b650dba05de9ffc9264f03c1cee012060693bec7", - "github-simulator/source/gpu/sim/track.h": "2dcb2a8ae356115965a5047cf3843e0bdb9c547e0b297b9ab87772385549184c", - "github-simulator/source/gpu/sim/vec.h": "715afdaaff174668892bc5ada76edab317445a770bc0d6e7e0e0e83cb459740c", - "github-simulator/source/gpu/sim/world.h": "cff41441c08ff645384d972ec56412122b5a38ff5d618e09c979870f70d15f6f", - "github-simulator/source/gpu/test_constructor_cleanup.py": "bdd1fc42aa1494e20558f8c7ce20a9ca4a3775aeae4c836fd9688581f5bb87e6", - "github-simulator/source/math/README.md": "33b210248e6ed5c4abe4104940abc27fd1728af2a5af07bf442c1dfa2bd4caf4", - "github-simulator/source/math/fixtures/atan2f.bin": "9ecf9251878ddcb789a43119f1f060e24025db3e3485cccdbcb1f4366cb68ed2", - "github-simulator/source/math/fixtures/cosf.bin": "c84017673fdbe2a29f03e8ae4f410292da3ee93f5f74d91e169053a04c6273d6", - "github-simulator/source/math/fixtures/exp.bin": "b0b063fc5bba32294b399f7305718a15797f4df65ba81cfbc785264e11126a9a", - "github-simulator/source/math/fixtures/log.bin": "9ef1de3473a7b1d8a2416eee3f2263a88eea073ad9c2af53bb751bca7e45813c", - "github-simulator/source/math/fixtures/pow.bin": "d2475d6b050e7b842f868c093591b4095509cd2e1350ba8ebb06df64452bf3cc", - "github-simulator/source/math/fixtures/sinf.bin": "bae316adc7a9ceb4a61a8375a7eb94004d9e9058e5563cc81e14c9ffe911a104", - "github-simulator/source/math/fixtures/tanh.bin": "b0f438bcf53c685c622158e0ab2f47b9a2c714e8c35cb8b90da1e539a53c7e2c", - "github-simulator/source/math/kernels/ucrt.h": "cf0787112fc4f3cadd88d965ba69f9250ea6fced7ab33fe2a3d669caae5dda5a", - "github-simulator/source/math/kernels/ucrt_tables.h": "0f2fd20c1513b5c88c6a138cdabdedfa68d652d07f6f19910e1a6a8a290af028", - "github-simulator/source/math/verification.md": "419e78815edbc8e47e3a39919d973a399cfa2d97b1556ac25fdfdd71540431c1", - "github-simulator/source/src/bin/altd-sim/args.rs": "9f833f026d1f71ef76fe05d948464c619c76c15e43cedc11b5f510bfc337bda2", - "github-simulator/source/src/bin/altd-sim/bench.rs": "76ddf4778cfafa0e8f2f821605c876fd250f2df37ef0d025028e7acaafc6ea0f", - "github-simulator/source/src/bin/altd-sim/comparisons.rs": "9e6859b759f5f0f181e3a0130478237187472a242d2b615d545cb3d19aad7c6e", - "github-simulator/source/src/bin/altd-sim/inputs.rs": "7c9c1b814dc9c072c2f77399836421c54547ef3029a2626f32c717434ef9c51d", - "github-simulator/source/src/bin/altd-sim/io.rs": "e90bd9b1dd7028d5187872bed08763e7ee8437a80b1491cd9a9bcfb8c47fcc97", - "github-simulator/source/src/bin/altd-sim/main.rs": "841956a5a1a927b1a31369ccbd9aa18161b6328745a5a3ca4f02bdcfedd7230c", - "github-simulator/source/src/bin/altd-sim/platform.rs": "da8d6ffbf03990ed04e9fe8ac6ccecc1c5f5da292708eebd2f68621043b882d0", - "github-simulator/source/src/bin/altd-sim/scratch/checkpoint.rs": "408f4c8438f8d8a8db2dd3888a55a7390a89723d1af1656ab7399897bf207eb5", - "github-simulator/source/src/bin/altd-sim/scratch/mod.rs": "524e9033a1fb29ac7b4d689a21fede12b7bb77a772c078fce4c0bf4763568bbe", - "github-simulator/source/src/bin/altd-sim/scratch/runner.rs": "20a78bd99dc6bd69cc9314ba2d531dac13b5d9f21fb6ca676d34c0fa472a46c0", - "github-simulator/source/src/bin/altd-sim/scratch/stop.rs": "b13d0b473376b0fb90d71d82713ae79d862337f5b1d874c1d53fa14ecf677c50", - "github-simulator/source/src/bin/altd-sim/scratch/tests.rs": "c59cbe78aca660ee9327c316d260e3d18cfbf302f41430e1985856090409a740", - "github-simulator/source/src/bin/altd-sim/train.rs": "7397016461d6d8a6642f03a2466d9bf22f1daabc1ab74ab22aaf7212821e23f1", - "github-simulator/source/src/data/tilemap_initial_cells.json": "97b5c28b80993f263e35fe22279944a91e7016f6f1471ef05317239c655acb53", - "github-simulator/source/src/data/track_catalog.json": "b1f71cd4c15aee7dcd81897ebe8549115bb270a26dc33728e9c5a4546f4408fe", - "github-simulator/source/src/data/track_resources.json": "e8b23c8a98f9c0c6573044ab0e7a01e8f707e6c46b234e96f9834a5370a61fab", - "github-simulator/source/src/gpu/hip.rs": "6618d96c39049e95a8dee98aa9656d27b1998ac71dd15fdb036210fedd68884f", - "github-simulator/source/src/gpu/mod.rs": "c8be62ad28ac998af80ebc9f5fb06320f8b791ab7f8a53db47c72b496506a7ae", - "github-simulator/source/src/gpu/simulation.rs": "3da4252813c8d7e83d48858a67b0f1a64ca75c274f4d930f656f80bc6e509b05", - "github-simulator/source/src/lib.rs": "a5d365de4631e9d4f7e1d049307f510c315dd74bb6a9ad6dec0e4e12a5c72138", - "github-simulator/source/src/math/double_math.rs": "9e5cce0ab7ab000ad2a40bd0315a09bc3af7910db97bc9296a8813b36ee6804b", - "github-simulator/source/src/math/double_math_tables.rs": "d27b8bbb61a1493efabb0a6a24dc095568fe096e4e34ba58470aefcdeb9735e2", - "github-simulator/source/src/math/float_reduction.rs": "21a5b0a46ecebe0c7fcb7bdeb1c9d3ae2589bc1796573615e8e00f2a1674d0ed", - "github-simulator/source/src/math/godot_math.rs": "5537ed27280374a776615baa9bfe8a134c479d38a15cacb9cba1dccb4e0c9d50", - "github-simulator/source/src/math/managed_sine_tables.rs": "fc4534f09a984ad78ca64f5584a2c2c868291841ff96f35353b8f82e89c31d8a", - "github-simulator/source/src/math/managed_trig.rs": "7497fdc77380ffc09dfea7921a938906cd12f6831faca118ad745c70053fc8c2", - "github-simulator/source/src/math/mod.rs": "c94f7656088d04220b5d59a9d28a340843f3f101fb39baec5aa48411d2290fd7", - "github-simulator/source/src/math/native_math.rs": "67efb2ce5a45c69c13006aa8b37c52ab3feb807b2d4b1e0d6bf04738e2ce8414", - "github-simulator/source/src/math/profile.rs": "df925dfdd799fed5691865c272e3e0ec1dea5bdd5961663edce4ccbd8c962d0c", - "github-simulator/source/src/math/pymath.rs": "ccadf6379fd4f4c7ba38e3c5aaab5e09d6d3f0388a7861a5c4525c70e7852357", - "github-simulator/source/src/math/ucrt.rs": "5cb27a66798d592f053ee1ecd8b5cc71a5cf250c31d2cbfbe00eb194fa4a57a4", - "github-simulator/source/src/math/vec2.rs": "18111a5d6412b7e4d6010c90d36f1892dd5553611b0c0778cea44db26b527afc", - "github-simulator/source/src/nn/mod.rs": "56200ce58b1191aadf594a44d2d967eb8ecd0e19d5f0d68b1299fdefd5d49e12", - "github-simulator/source/src/nn/network.rs": "af897658264b6bcc0c0cb4c6218da28a417fdee82b3531b90f833bab59502ca3", - "github-simulator/source/src/nn/network_simd.rs": "b4339e7da61d54aa617098981c3e27bb4757bac160f6d39c5c7bbb0c5ab86e60", - "github-simulator/source/src/physics/broadphase.rs": "65e90c5bbdd975b7dfa4ab9f81130e36a21df28f04faf193213c8522c39a837b", - "github-simulator/source/src/physics/car.rs": "b47eee90cd42d1bb9c9f8f98b1794ec85c3e3ca13b862d053f2dcc8048dab615", - "github-simulator/source/src/physics/collision.rs": "eb4b3ccc9d668a8054b843bd31624dd5602bd3db92c01f11dda8d8852a1d4a31", - "github-simulator/source/src/physics/mod.rs": "f24b54fe129527d6032bfda7f24704db6abc500130cc0d499937593dd4a61b48", - "github-simulator/source/src/physics/simulation.rs": "730da7a2a910f924cb23e2c4f67f9e59ac822ca3011596766953cf6188617e4f", - "github-simulator/source/src/physics/trace_state.rs": "83ac2de24529f195c506f90445ffef8e6c03d4d310264509d2a8ac915cc59699", - "github-simulator/source/src/server/dispatch.rs": "cf5206817e1eb2bcf52ae1a55f5cafde5d4b3583638eb6f8a250dfc933c29a82", - "github-simulator/source/src/server/mod.rs": "17672296ff7e9c6562443a913bdf290a12688b23bdadd61d57982ec40eb522e5", - "github-simulator/source/src/server/protocol.rs": "05b17adc585357659bd301495bd2026a39f52faa45fca61eb94fc42bc16c29c1", - "github-simulator/source/src/track/bsp.rs": "1555c30fdc8f1c2f68a877986095c6bb5146546e82394ed2c412bb89bade8be3", - "github-simulator/source/src/track/curve.rs": "b01a5d140b92a8eca03bfe7ca1d58fcf5e8455388d394b2f86d3094d98352b00", - "github-simulator/source/src/track/mod.rs": "4548f19925c2f61fac6a076ddbafacd062b29b75c8eecffcd7d7eb3037482900", - "github-simulator/source/src/track/path_segments.rs": "afa22462a3c5486d5bd9873313786cbb3aa28b932293fbebce37721ad9add24c", - "github-simulator/source/src/track/random_track.rs": "f60118f42f04b257188f89199956bad52139727ed4cff4376377a7bfb64d56e6", - "github-simulator/source/src/track/segment_grid.rs": "636b56c997ee10c5a56ed19a06f8525ff1e7a0756fbdc4dd4a73f8a02534f642", - "github-simulator/source/src/track/training_tracks.rs": "af7207f94db09f8e5d221844de25bb2a7fba44e82cacaa973c58e058a95332a3", - "github-simulator/source/src/track/world.rs": "81b4fab7ad2030629859cd7d725461b4e6adf6667f88b68629d289e8eb18fe52", - "github-simulator/source/src/training/agent.rs": "e3c0362788b7a23e47104d897c1d12025c8658f361c79f6328db0504c9416e02", - "github-simulator/source/src/training/ars.rs": "74672f1717b1eeea227e3f04479bbb951dc8289850a25e0c8ca59cecd6b77218", - "github-simulator/source/src/training/ars/tests.rs": "4273896832a470a0f10dd7c913b07726d21e3dceb6b7786abc89a195ecee05fe", - "github-simulator/source/src/training/batch_evaluation.rs": "6b975431eac59bfb197f3e5632ad58e1baa86c96f272360d351a4908df896f23", - "github-simulator/source/src/training/evaluation.rs": "66a6b86cdd3a052be533271a0012fb38a5a2ca2f973644681d013350f0b5ea27", - "github-simulator/source/src/training/evolution.rs": "d050036c9346561411feefc6c94d598cb0f3ee69e85a8960cf911d60b2129a61", - "github-simulator/source/src/training/game_random.rs": "04e90273f4960b36dbbba831bc4f95390998e31fd874fc3cca308580fe59228b", - "github-simulator/source/src/training/lineage.rs": "35ae0755d172ce54b7e26682ff626ef71d4ab724c553a2bb958084540d257c42", - "github-simulator/source/src/training/mod.rs": "8ef89fe999edd56502e85aba2b20664ab63871b9a03977c5c4b40293d945d905", - "github-simulator/source/src/training/optimizer.rs": "ee0ccc79494840e566e919d75879c2ceddd126fe5b5b49622f8c8d08af4aca92", - "github-simulator/source/src/training/pyrandom.rs": "1890f3338964d87e892589fea12d673ff181b2945ad7c121ecc0cde586e9d758", - "github-simulator/source/src/training/runner.rs": "5dcac344de3256dd27c6dead9cee92ab6ec1e782db38409002c7540fa11f2d42", - "github-simulator/source/src/training/runner/gpu.rs": "12475cb92731f5e9c311a9ca62c4e17f0dfde17bc46efe9f827f6ba4a91ebbac", - "github-simulator/source/src/training/runner/rays.rs": "e6467baf46fb091e81ca40aaa0ccc5b0f316f264cd8871e2a7f897cc4ae75320", - "github-simulator/source/src/training/runner/schedule.rs": "7bae3c881f2f2b086f1007734008056fcedab3316626cd9a5b13909daec02890", - "github-simulator/source/src/training/runner/tests.rs": "26fe931db6894bd72813136daf77fe0991307d1d715ffceea5f0e398e3e05b86", - "github-simulator/source/src/training/sensors.rs": "c3fe5cf32fb6ae0f75eaf08d826b482d3002ba9871a17e5c9b224c37cc9eda7c", - "github-simulator/source/src/training/session.rs": "608545b42d523766492d58e927b77e1beb5f4914476480ea5e64d0c325d62289", - "github-simulator/source/src/training/session/hip.rs": "a498f4712d2482d4229869748e3958c2ed502309b33e3465ac939d6d50ab3b2b", - "github-simulator/source/src/training/session/input.rs": "8d2ae4c9121a0a561797dbd394493c73a012d1687fbd5248ced4aa06c49eae90", - "github-simulator/source/src/training/stats.rs": "94075a6d675927b083c734e36049d12ec3faed67cb420e9a4c35e1de15a2a764", - "github-simulator/source/src/training/training_profile.rs": "2f801070e7e5b93fa774f2fe253642940f605167c8bb8a9e5f697e9dbacc36bc", - "github-simulator/source/src/wasm/float.wgsl": "6bfd8df71111352061ae5da4c6a0d496cfa3ff801a9c9ac9346a8dd940225584", - "github-simulator/source/src/wasm/gpu.rs": "065e78fe19c01f9ce4f09ff370d47696f77c3f53512fe54bb3d239f3a2860567", - "github-simulator/source/src/wasm/math_profile.rs": "63a59813aeb49686fa20ced1f0f379338ac12bffcda5d124f32abaa6a8fa524c", - "github-simulator/source/src/wasm/mod.rs": "96caf3e5bd084cbfb16e589430c952c6f4713fd843a63d3ca087d1b6aa391f2d", - "github-simulator/source/src/wasm/rays.wgsl": "0717e2a36bab9dd88cf2225104f6a5db198b767e9e4997ad40681a9bdf567448", - "github-simulator/source/src/wasm/sim/f64.wgsl": "b171bed33b9761f90ffd8222c36ff2532e9a260ff706fa85a10fb6f3ccb7cc46", - "github-simulator/source/src/wasm/sim/generated.wgsl": "a2f2947ecbd4b7eef088e074cf339c99e485c0d6c03365502686e3f9c80b0cf2", - "github-simulator/source/src/wasm/sim/helpers.wgsl": "bded7a38f12f5f5b37ccebb9e5a0701b476d353dad5f1cde230d97cae30e2737", - "github-simulator/source/src/wasm/sim/mod.rs": "1f4f7d45024a70768c6f0ff771991eadfb844118c0b64b17305f3ae40df611b7", - "github-simulator/source/src/wasm/sim/runtime.js": "fa6094b141f2db440d9627eb5eb203ed01a7670c3f3ba3933cc72cf49a046834", - "github-simulator/source/src/wasm/sim/window.wgsl": "532b56c9dd694381269513e0aec5831244aa7e5eb5dfdacf8ff66ec35b649f30", - "github-simulator/source/src/wasm/sim/world.rs": "76ebd261bb47ffe59f671451df00a769bc450c595780abeabfe542ed2aa31954", - "github_adapter.py": "7555cff8bceab47777386ee90a50074befa3711c433ad22cdca2ad4c1f652b3b", - "godot/assets/imported/A01 Beginner.model.json": "1d46f92b1c7d91af75e9f16423194f0fce5453d78b2cc0e7fb18eeb1842b5dd8", - "godot/assets/imported/A02 Sharp Turns.model.json": "40dbec0a1fa0bb029524cc71e0b420fe7dbcb7f4135a1f9ac21e1a6cd32786dc", - "godot/assets/imported/A03 High Speed.model.json": "2e7406e4292259a3bddf66c92dca6bf37237d80694cf6a68003744d179bf22bb", - "godot/assets/imported/A04 Dirt.model.json": "2dbcd29b0db5344dc2ae81be60c352a2dbddc3f2ac7b3427d2b76a8512d9733d", - "godot/assets/imported/A05 Two Terrains.model.json": "562b16348c0d79dfa5a22f483922acedd36446461bf44d2e4f94693f4910c58f", - "godot/assets/imported/A06 Ice.model.json": "56ccc3fa4149f3be4b25b5808482ca21edfcbf9559d497ff6fdb300aea14c76c", - "godot/assets/imported/A07 Three Terrains.model.json": "e0dad6f3dfdb8261e7a88b584a8120b69573634403c411ab48b9c69648d453e9", - "godot/assets/imported/A08 Long.model.json": "304998ba1effaecdfe8d9fcdaf59d8811db779c877458c454b355f70ad142d65", - "godot/assets/imported/A09 Sharpest.model.json": "a6948598cad6238f0c5802d67e1be56bb1748b648dfff675d23f6a61a05d0f56", - "godot/assets/imported/B01 Oval.model.json": "921fedb31214281dff3a64717da07abc6d85bb1bf0679411768c623a2dde02e9", - "godot/assets/imported/B02 Snake.model.json": "b319d59a1a21ca094ce50b781938e108582de7752036e5da0c028447964f462d", - "godot/assets/imported/B03 Slalom.model.json": "43008c2ea7475da7319f882a11a58d185ad093e46e002e5325eddbf80b00887a", - "godot/assets/imported/B04 Rally.model.json": "d5458902717421ab99680f8106de6472425b34f85a8e7842fdc639be442a6c1b", - "godot/assets/imported/B05 Racetrack.model.json": "203217155ac7d3d37e3bce4bf50199cfdece32a75533557435e78f3b1375f1d1", - "godot/assets/imported/B06 Hard.model.json": "c4460d0bddedd7d4be6d00976b6554994476d68bec39684a680cd6019cd32e7c", - "godot/assets/imported/B07 Drift.model.json": "d5b8b4418b84fe8a9d20f90e65abb1600510e122905b5eaa3e9d4f942549c97e", - "godot/assets/imported/B08 Arctic Circle.model.json": "e9b12a0194692bd90980ade5e5d852eb91ab43a55522a9cfb997c79811d4131a", - "godot/assets/imported/B09 Checkered.model.json": "183036504d32c8dfc1a58bae183d64501c629ab5c40e31908da4784cbdef8e0c", - "godot/assets/imported/R01 Blitzring.model.json": "3b0ec1c62d4d75f1ff7a27e8bcfc7e88495de97482037d213a5d661ca8249855", - "godot/assets/imported/R02 Desert Rally.model.json": "d3794fc462b24b6eaadd17f88c50fd98843cbae5b63f5e9765f9997e0244e04b", - "godot/assets/imported/R03 Glacier.model.json": "ec9b3c8201c8f7546c366c1aa9715dc76d875acbe5c0b0771c4abf0a25270d36", - "godot/assets/imported/R04 Truck Maze.model.json": "3ce6b887fdbf31a98e581715d77f7101251a1225e52bc072d0e8642a5d5e2fbe", - "godot/assets/imported/R05 Endurance 500.model.json": "19529f1a12cf2c5cd9ca5f1f9479e7323eb598d7db6b12332d16bf69f561c33e", - "godot/assets/imported/R06 Mix.model.json": "bee132139cf13457e293ce8cffe81ca8e8d5885e3276eea5a65465cc53594531", - "godot/assets/imported/R07 Mirage Marina.model.json": "b541eb4cd0cd714dff4e7e5e493e15ea0a01d99a6aa3a65b4a97044a87003a09", - "godot/assets/imported/R08 Demolition Derby.model.json": "b391029ce0b19f3ba6dea0831e53838337710136c9433ef84fce80317f4c9c07", - "godot/assets/imported/R09 Eau Noir.model.json": "25b9bf89b5ebb2fac8d18a547a1086d37f92563ff667b6cfa2a3afff83e4539d", - "godot/assets/imported/The Champions Trial.model.json": "9230869539197143ace6762107286000dba2cc050e6d35df7ef1d0c13fb5dc9b", - "index.html": "78e54dd9173c70d57b15003bf44f69f2a299c2e12b64216d9cedfca6f4177ca3", - "network_legality.py": "90196a2a6366768dda19fbb91dc03b2eeffa913b9af2ebffd33baa8f7a5067ec", - "preset_store.py": "c0edd11430c93d7015373d3e8579c0781e1a4ff51372808aed7872247d84b735", - "reward-catalog.json": "53b25e10a9e14b2ab1bd27259ca93d8fb0d49c4e27b3987f0ba2fc572bc63bf6", - "server.py": "02f6cf7fe207ae0a2e008eba27d12a10020384338b17c7b4f4e9dd9f9520de0f", - "tracks/A01 Beginner-compatible.track": "c52056d88edf9bebb9d3daf6f9839ddf67deb43ff93d93a93fb0fbb3566993c1", - "tracks/A01 Beginner.saved.json": "23c93ec4443c269978ede5901b4c66a3f627cc273f3b5f6fa96d8a190a7e365b", - "tracks/A01 Beginner.scene.json": "d79cc49de2a5dce8690cf3422ac74b76f3980f845f98235188a443a717cd1f10", - "tracks/A01 Beginner.spawn.json": "c491511a8202b0bb4ba996c9f96a8d579dc30b443f5ff09984a18c5daee2ad89", - "tracks/A01 Beginner.track": "d3261eda8f6cbf73c55fb55ced0404aa6cba162fbf24bdc7780638b23c29d6ed", - "tracks/A02 Sharp Turns.saved.json": "cab708be9732a30f67d6cb3f2c639260844b003744ab061c6b06f5986024cb6d", - "tracks/A02 Sharp Turns.scene.json": "ded6165101f191332485a311f453537816248adb177822558dd86d917db882cd", - "tracks/A02 Sharp Turns.spawn.json": "03f1c1a3e2c720643e64f812a7bd619da8bc9ef27c108ab29be6fea982880763", - "tracks/A02 Sharp Turns.track": "ade4031c6292f0342008a3479948bb32ed6f95235d74b5a4d0fe2762fdf9790f", - "tracks/A03 High Speed.saved.json": "f21aa853ba2becd9c9e9118404545d19c4bd6c45d64ee3ecd4b6bafcefd97fbc", - "tracks/A03 High Speed.scene.json": "b486b6b47cd36e609f5ec1be67d385e373df351c379c47193f8a0aba54c88924", - "tracks/A03 High Speed.spawn.json": "7f8a9020b2aef433d876679262fc337928a4218cea3d2efa3ac6798b2eb122eb", - "tracks/A03 High Speed.track": "0fb8666063a4ee503cb5ad453d3135d7aae00a030254c5538581e73e7b94325e", - "tracks/A04 Dirt.saved.json": "5feac332a0b8e72fdbc5b9aa29f38750ba22b02ef809eaacb9f58daae54e1b64", - "tracks/A04 Dirt.scene.json": "dd28eaccaf7d270ff2ce4ebe572bf69a61349e107d05dff9792ea2f2c793ccb1", - "tracks/A04 Dirt.spawn.json": "28d8b916b84e3073c1ba5d0dff9c9a7d85c375858ac8c6818b1d6314847961fe", - "tracks/A04 Dirt.track": "483adf3e712436d448bf1187c467649f93cc7812f60db43345d4d0e061daa752", - "tracks/A05 Two Terrains.saved.json": "847f869d1d9f3e410043ccbf253d3def0725afa730c695b1519ef642cebab60c", - "tracks/A05 Two Terrains.scene.json": "a1e767ed6ddfd1fdaabed07a13ca71beaa074c051cb9ce70ad6c476880562bf1", - "tracks/A05 Two Terrains.spawn.json": "f8969c365d01c04a7a95b8abd5596618b70f52531eb03689ff908a27228056c3", - "tracks/A05 Two Terrains.track": "3493c825514e7304110db828e5a0fc58cce18dfa1a2a118b50addb2d71d4ff08", - "tracks/A06 Ice.saved.json": "97b93faa93ffea17dbac296c610228b5e452d0c1dfb14de30062e3e680d6cc1c", - "tracks/A06 Ice.scene.json": "da6fce1651fbb318fcc312632feca8961dcf85a79b7e74813b2427e696c716a1", - "tracks/A06 Ice.spawn.json": "b0f8af0dc885fc751eab1522da10592b5493bbc64843869e2426e1eca64e440d", - "tracks/A06 Ice.track": "a9acc1478f1d48808b30077f94c8a55ed64027cd0aa4c65cf0a2e1718c524bd1", - "tracks/A07 Three Terrains.saved.json": "a13bc4079a744791d2831aa971781bec349daedbe6bc104084741a561a637825", - "tracks/A07 Three Terrains.scene.json": "2888b82fa94b84c3b679a7c0ce81b7b2ccaaac7756907c893f85aed17f1059b8", - "tracks/A07 Three Terrains.spawn.json": "6e010816e7b8acd2401b89473fb22e9894cbfb88ec58060b47ae48e3091cbd21", - "tracks/A07 Three Terrains.track": "c157d8246d250940b9601b7c87b6c4edeef1bd75b5bab9aa69427d76d95cc7e6", - "tracks/A08 Long.saved.json": "99557126538ff8d2fac553262178427a91094eceaca39b32949141a310673113", - "tracks/A08 Long.scene.json": "f02099b86907f7cad9696ccadd814c626c59da47550f00d53307a4da535774e0", - "tracks/A08 Long.spawn.json": "6c082f62cb29b0a3cbc791b7531a4c9aeaf774f7e6dfa644f9ab40a2c3c6a815", - "tracks/A08 Long.track": "ef34644dd6f9900bb76a41fbe759a183830b17b1b6eea7f19cab1ba43ea7d96e", - "tracks/A09 Sharpest.saved.json": "8eae9b874f9b05050afa36370fa47f78b762ccba6d0d9665113b947a1a9e18d7", - "tracks/A09 Sharpest.scene.json": "05d4907988d0b9343c1800c74d0b3d0ea3f13ecd96514426b2303a211a9610b4", - "tracks/A09 Sharpest.spawn.json": "1027c03c0ef1bfd373e913c7dc77b256d0058e7b3b3cc6dc7d014a534019d972", - "tracks/A09 Sharpest.track": "407106de14243819966ddf30cad20979d979f94a798e94551a31a5538b705b5d", - "tracks/B01 Oval.saved.json": "0c42ed69583b7a6041837b10a4d2de7d4bcf4426ca91f657b5c1ce005a77f9b7", - "tracks/B01 Oval.scene.json": "f90567dcbbf2e7ed4dd8e578c63efe2962bc8fa836692e6c8c9494f4788b05e8", - "tracks/B01 Oval.spawn.json": "38bbe40879c18d9187eda23c9850c70f7762ea769b5d72208607b49bc2327c71", - "tracks/B01 Oval.track": "b4152d06083e5c871ec356ceff917de1ef5f72d6e27b9618959125a5e41e5a8c", - "tracks/B02 Snake.saved.json": "54c4f010a73e3549115ab9b160c9823d0f96fa920602507fcf251a9fd29619b4", - "tracks/B02 Snake.scene.json": "5ed52574b620de959a0c2005c7fd371f6d2f29b5dc2f20e930b13e8d93fc0f4c", - "tracks/B02 Snake.spawn.json": "2fb95139538ed66324e10750278ee250e8bc159ccab3d27320efc21de7601e5b", - "tracks/B02 Snake.track": "fbd98acba4c3580b6e37cf72ed63b5b2e0477b86ce64320a8423449b59df7e0d", - "tracks/B03 Slalom.saved.json": "6132213bdb3ece3f3ba1297200169aebd818fdff7e64ddc37d212c097322e9fc", - "tracks/B03 Slalom.scene.json": "13acb54facddc61b033070621dc92bdcd82cc35deadfa811020b788f15c90b5c", - "tracks/B03 Slalom.spawn.json": "f8969c365d01c04a7a95b8abd5596618b70f52531eb03689ff908a27228056c3", - "tracks/B03 Slalom.track": "f0014f7d33fd982fdf56d3f5fcfbccf5470ae5b3735480d4bcc959f2d5e61707", - "tracks/B04 Rally.saved.json": "1ba74c6c2e3dedddc9864379b6f824e8f32d45bd9cf566fcfdcf4074d633d817", - "tracks/B04 Rally.scene.json": "b61381b0932d40bc99f422f550d5d04612b8902dafb48e6e99752de817f40c2e", - "tracks/B04 Rally.spawn.json": "a864506108c968ab8e5949403825a387a9ab679aa3c97c2f85f51f593530cf17", - "tracks/B04 Rally.track": "6e03ca93bbd59f6e1cb05e3382c7959cc09f3abb5d9f76e8a224dd7365a21844", - "tracks/B05 Racetrack.saved.json": "d8fddedd268be9dda3e30f337663c3f1d42777c1cbcd6cb92055a8b50d154471", - "tracks/B05 Racetrack.scene.json": "1b69b006af727c249097fe996bd280f2f66751d78463678045db4bdfa7b7ccda", - "tracks/B05 Racetrack.spawn.json": "caf4e703eeef12f246e0456e9d4fecfd92020c86d6295813136c4dd68226aae9", - "tracks/B05 Racetrack.track": "43cea4d2ffd3ca3bc19de5b662b67228f7a9a35adfccf1af84a17ade07b5124a", - "tracks/B06 Hard.saved.json": "fe3bf789f34b9092b0c8486c80705f1aaeb38603d9a7263bbc2e4bf24fa0a7e6", - "tracks/B06 Hard.scene.json": "18b7dadde263aac8316ddb4b10e8d28321b799bdd17acb4c69508281dbbe949b", - "tracks/B06 Hard.spawn.json": "64cd8fd30bc1909522aaa0cf2df52bf980e6e4df2c5f3c4be173e4ecdd6881bb", - "tracks/B06 Hard.track": "b45330b2a4ec12b6cc7a62ab403c5cd7b864aa3ca7ce698da9678d80fefd5277", - "tracks/B07 Drift.saved.json": "549247a1c38d9f5462d416a2d3a00c3d032e8ca0358d75f2503bae9e277b3d79", - "tracks/B07 Drift.scene.json": "d8e3bfeea412780ae10a46bfcfee216bec1241f70dcab408a8de3b26bb7abd65", - "tracks/B07 Drift.spawn.json": "0c39c94ec48e01cde57873716585fc104b61d5bca9c3eff841342129d9b02078", - "tracks/B07 Drift.track": "73e0f3b11458f16e99b5c67987253a0d2e31a8dddf93bbae95ccf2ef09f206a0", - "tracks/B08 Arctic Circle.saved.json": "02b2be3a2c62083835f7fefe31788e666e523e4681b9c840ef6b8f2375b213d6", - "tracks/B08 Arctic Circle.scene.json": "af5c3bbb39ba6c143244d604df0c8b68ad7d8ef2889299cca52ed53dbf2b0980", - "tracks/B08 Arctic Circle.spawn.json": "ee1454cbec1c9b48ca0cf88e4337ada23a62827716a1ff10e1a480cace798043", - "tracks/B08 Arctic Circle.track": "4ec9803238b933999c5a1a16f8718b7a6b12ebb2aa88615212b9caddef3cc154", - "tracks/B09 Checkered.saved.json": "9c015b35fb7085c622852b27f30881e28c0c02aeb5c82cc24e069cd5b838b878", - "tracks/B09 Checkered.scene.json": "1f8927da796828fde4b2f86bec1650a7896ab0cbf3855d59894b9c499e43c52d", - "tracks/B09 Checkered.spawn.json": "cd6e0efcdf24be3ce06afbeaefa2c956085172612f73b2c773dfcc4220debd5b", - "tracks/B09 Checkered.track": "a9df6dc6a7817eaab6d7233846e29ae44f826a9e035aa578ceaef386e12dcdd3", - "tracks/R01 Blitzring.saved.json": "92741bab01c659d294d6e5ddcb09905655f681de170dd9b9c850bb2a50ddb19b", - "tracks/R01 Blitzring.scene.json": "a2e6d19aadc4cc7e614f974c5db399e0ff0a105682e38a8fbfe6e9e4ece4686e", - "tracks/R01 Blitzring.spawn.json": "7795cac05e10bfe8f16a0cb0ef5a93edf0df443085df22d81d8955a8fb39ab2d", - "tracks/R01 Blitzring.track": "560aafb1ecb7fa07671cfaa79ea429e5db7c64ffa625f25a24fbb046732f13bc", - "tracks/R02 Desert Rally.saved.json": "3f5e88bb2c3ea08ed933d529112b8e99cce6817cb19e1896ac7f3f478eb4256e", - "tracks/R02 Desert Rally.scene.json": "3ae7ee8cb62758d5ac09004536dce9ce19b99b0d732459e1a753bdf433c28ef1", - "tracks/R02 Desert Rally.spawn.json": "fc8dc10779bb6750039b84308aaeb8e5b046fffd07932f82c88eb17654cfbe9c", - "tracks/R02 Desert Rally.track": "15269225ce25cc964e79783ba5833c9135f561a7ae7c482b1f1f402feb83aa67", - "tracks/R03 Glacier.saved.json": "899727f504a4552522a66f65803498dbc535b943280ba2e41979018ed7a6d77f", - "tracks/R03 Glacier.scene.json": "5926a121ab9fe5a37cda9037357ef27a53d1ba79c217a6ee1e8561bb199d23b7", - "tracks/R03 Glacier.spawn.json": "ae91e75e037ba1d8b039a638860b6a93535d25e0804638dd1ec533296394e82f", - "tracks/R03 Glacier.track": "93d85045b4af30be986fb1cf9e5a2462ed25f34dd17b1dc5bdebcc06f2f58f0c", - "tracks/R04 Truck Maze.saved.json": "3a0de310132581c730acdf493dbd9d6506f9d78479e5cfe83c55f8f788f0895e", - "tracks/R04 Truck Maze.scene.json": "6d0955f94394a2786601053aa6c3da3135457b470df069ae102e90238a96e295", - "tracks/R04 Truck Maze.spawn.json": "6632d1b815145ef63173b33f740a8038ace886fc80d3d19d153dc0317e700b64", - "tracks/R04 Truck Maze.track": "69f3b7813716bc408853f76368b9640a8b5fac60c0fe84bf68bb19f2f8cf5680", - "tracks/R05 Endurance 500.saved.json": "b13fe6d518cf8754ca362f8b04e09e5f1b58c5255c113879645b01c09df5c09f", - "tracks/R05 Endurance 500.scene.json": "3d61c23d726261d3d05f69561e80d21ae9335fe40d311953a9f1833b4130e4c1", - "tracks/R05 Endurance 500.spawn.json": "4012030b01cb4fd551aa5944a442ad6ec13e5afa66804728a4df855854eee339", - "tracks/R05 Endurance 500.track": "ab701400945b302b600270a3d510e6a3604a9ae29a3f7aa00df9628f7ecd9ac3", - "tracks/R06 Mix.saved.json": "e897ba1f8697b137c4866ebb76b8a50b873cd1ec2d185cb86389b1e8e166341c", - "tracks/R06 Mix.scene.json": "ead016d8e9ad39bb56f58d7456284321aef6d31f67684a5573845a5ac63a7c97", - "tracks/R06 Mix.spawn.json": "c491511a8202b0bb4ba996c9f96a8d579dc30b443f5ff09984a18c5daee2ad89", - "tracks/R06 Mix.track": "140d6aa9a0e4bfa1a6776ac05597bbcd7a75c073935426a74b540374312f9460", - "tracks/R07 Mirage Marina.saved.json": "a87a65b016d8ae787ec60f928c55184615d7bdcae9417ccae728d2ed8284295f", - "tracks/R07 Mirage Marina.scene.json": "6c9df3b4faf505605db29b48e4d86ca99c86ff6493859f93724b06ba8cfbf3cf", - "tracks/R07 Mirage Marina.spawn.json": "5f60a1fa3a25211fa2a9b3b4320565001c65e7600c8de6bf1bf33206b1fe6a2d", - "tracks/R07 Mirage Marina.track": "5eb0d06262c6e78dc00872591dec541780a1c9a0835ad608cea474f4b038d68f", - "tracks/R08 Demolition Derby.saved.json": "f1d4f738570d6f4d3139839c1b1bec01f22f4fb43ac272d971df30960e7a7736", - "tracks/R08 Demolition Derby.scene.json": "8526629ee38c06c7d10b490316aae5fde5be185c286678d7304bd5e0208bb743", - "tracks/R08 Demolition Derby.spawn.json": "62cf46bf13fd9a3fb57a8891edcbd74af430174346bd5782f57796c18653a12c", - "tracks/R08 Demolition Derby.track": "00816f68f620008cbe560a9098883834d5fb3eef664c37f00504c8fe9d025f0e", - "tracks/R09 Eau Noir.saved.json": "be13d95668e41982fe0aac22a3a69df934fdbb57234e80c53c5ef9bbdfbd6f55", - "tracks/R09 Eau Noir.scene.json": "e27ca05758a1ae98c7021f62d255745c1fd25350076ee9735131f4ce407a6a5e", - "tracks/R09 Eau Noir.spawn.json": "045e758d23b577e4a44597e1fef831b8689253bb3cbae3ad6d4309f4a4d79359", - "tracks/R09 Eau Noir.track": "ba77ee9396d44f13bf079ebcbbfb6aa6f44bae83fd8725b20eb559ac383ed79a", - "tracks/The Champions Trial.saved.json": "ccb4b73d7c01c607ae7afc655c476d92d15809cfd7ac66c1750646368ddfa2f2", - "tracks/The Champions Trial.scene.json": "5aead756212996a349023f4cfa2046ab0eced2a226777f9f32e27f9d533bf332", - "tracks/The Champions Trial.spawn.json": "b0ffd429583ecc6806854b672c2cf683ea305f17a49800942c9242d4490a95da", - "tracks/The Champions Trial.track": "27e612b7e5f4c0356a02c2d6ee0d0d03383543ce84dc36fc74a62a259e8493c3", - "tracks/catalog.json": "a043d34c557d35871bbf3b9c5ba018e882158a53b7e55d9ee8eec9782efad7ce", - "tracks/compatibility-receipt.json": "00948fe53db0995281e9a4593186d1322baaeaae0c41414b77724d8e59980122", - "vendor/games/__init__.py": "f6701b872373bd606865daad0cb3d55c6815b78a6bdf1a780ac6fa99f53f0a3a", - "vendor/games/ai_learns_to_drive/__init__.py": "53b2d4f6dac65249b79fd40331d2668f3d4ecf999616cf728912be39bf77431f", - "vendor/games/ai_learns_to_drive/batched_controller.py": "f1386798ab6a74a095720003fc4a7dbb5e01ada6e114c130e558e8a54215d2c1", - "vendor/games/ai_learns_to_drive/cli.py": "716d092b390dd205f2689c779f1a3a47b8c2dbd77597e01049ce4ba1d1f63856", - "vendor/games/ai_learns_to_drive/collision.py": "6179d7581c1a2f42d8f028ab30e701dd11c3ecd125eec270fc73c256ae114a39", - "vendor/games/ai_learns_to_drive/evolution.py": "adee47e38d62f1c9b7f303171552a7000f4c281384b03daa8410bf8c6fbd7fc7", - "vendor/games/ai_learns_to_drive/evolutionary_trainer.py": "739d11b924f2232ae57570da5d9f6dd395f2a272a2ac9f2ea383b9646ab0420a", - "vendor/games/ai_learns_to_drive/managed_math.py": "90d88e3376d8ec49b5913e26c6a5923d355a3aa303977d28ec74a027f690d59b", - "vendor/games/ai_learns_to_drive/physics.py": "942abdf7dee4d9d5b0644decf6351e9f9d7956a8e7613e46575fa361da68d547", - "vendor/games/ai_learns_to_drive/physics_profiles.py": "5e5dd4cf5ebc5be09c2180b0b69af9833b95e6a7e801da5cbd1b46f4527cddc9", - "vendor/games/ai_learns_to_drive/presentation.py": "6ffc09d2ca5d1791d7147ec1fded6c94860d66b51c411441d16c9a93b5e095d3", - "vendor/games/ai_learns_to_drive/provenance.py": "e739f162e3c863085b3fb960638f1d66a726456e2b4434a2e964c6b9888b8b2e", - "vendor/games/ai_learns_to_drive/records.py": "b7c65c165adb814b75dabad5595a139220a1a549958990315a9d3659194b0345", - "vendor/games/ai_learns_to_drive/reproduction.py": "02f096286d3f629f6a0feecf173dbcdfca4b7f898f07e55b853a490c5a953be9", - "vendor/games/ai_learns_to_drive/rewards.py": "a9f2833ca923dcc5c51d35f802615559cbd7cf0c8468803d83c94ce3efbcf3de", - "vendor/games/ai_learns_to_drive/runtime_cache.py": "b5d0f9e92cc15da25e5039b666733a7d7dd0d660b9db7516672945f2498ac846", - "vendor/games/ai_learns_to_drive/scoring.py": "66038035a63d181c60a4a657f39ac38ebf72aab6e0d91432edbcdb46d1a03f6f", - "vendor/games/ai_learns_to_drive/sensors.py": "77309aa3cbda26d521db81413cd2be8bee18f305358c9f849e346523efc9377c", - "vendor/games/ai_learns_to_drive/sessions.py": "072a306ebb1c8286a875c1476f4e56a4de1b7468b6f168d0a7778407391c672d", - "vendor/games/ai_learns_to_drive/settings.py": "b802210677437f0537e2902dd3fdbe72006a943e519f08ff53c7c2ee40383dc7", - "vendor/games/ai_learns_to_drive/simulation.py": "30b69106673f62e8ced5e58077058941a75abda2865d704c2a4e715781d7e406", - "vendor/games/ai_learns_to_drive/tracks.py": "24d8e098108d8be033da1292c9c72d2047935dd5dc1084fb2cdd24e6c55efa3d", - "vendor/games/ai_learns_to_drive/trainer.py": "5e17bcc7bc594a4bc27223106313497112488f7e6bb6c75618239878426355cd", - "vendor/reward_extraction.py": "69d10fb82594ec5f391ac6814f2677355a6448c5eb1a9d28a3c677333cdc6384" -} diff --git a/README.md b/README.md index 684e419debc5659b25f997e01bc165d7c78e5246..b2170eceb5a60f5ad36bc57c3ab0a9272a0da4e5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ --- -title: AI Learns To Drive — TiGa interface +title: AI Learns To Drive — GPU lab sdk: gradio sdk_version: 6.29.1 python_version: "3.10" @@ -8,16 +8,8 @@ hf_oauth: true hf_oauth_expiration_minutes: 60 --- -Protected CPU/ZeroGPU integration candidate. Custom interface with Gradio Server on port 7860. -Simulator: https://github.com/Zorro909/driving_sim_rs -Hosting adaptation and UI: TiGa https://github.com/seeker-cyber-maker/driving_sim_rs -CPU is the default; GPU requests explicitly use ZeroGPU. No automatic fallback. 28 built-in editor-format replica tracks; no bundled networks. See TRACK_LIBRARY.md for provenance and limitations. -One server process; no persistent storage. See DATA_POLICY.md for retention limits. -Not yet verified in the hosted Linux runtime; do not publish as a stable release. +Protected CPU/ZeroGPU training beta using Zorro909/driving_sim_rs at commit b90b8af06ffc4ea4e4fd703becd1107a48b8e5e0. Open the public app at https://tiga-rce-ald-tao-gpu.hf.space/; the source repository remains restricted. CPU runs anonymously. GPU is explicitly selected and subject to available HF allowance; optional HF sign-in uses the official OAuth flow and needs no personal token entry. -Expanded runner UI beta candidate: configurable training, settings-only browser -recipes, bounded sequential batches, browser-local network creation/editing. -Track rendering and star evaluation are not connected. This new package is -not hosted-qualified. See BETA_TESTING.md and STORAGE_BOUNDARY.md. +The selected track map previews tile layout and asphalt/dirt/ice directly from the selected file. Twenty-eight reconstructed editor-format tracks are included. Settings-only recipes and batches save in the visitor's browser. Formula steering/acceleration networks within the current worker limits are supported. -Optional HF OAuth: openid/profile only. CPU remains anonymous. Visitor tokens live in browser memory; queue identity is checked before GPU dispatch. No personal token entry, server session store, or automatic CPU fallback. +Application-controlled inputs and intermediate files are temporary. Outputs are held in memory for at most five minutes and deleted sooner after verified download by default; a single-run visitor may explicitly keep the result until that five-minute deadline. This is not a provider-wide zero-data-retention guarantee. Read DATA_POLICY.md and TRACK_LIBRARY.md. Upstream code and license: https://github.com/Zorro909/driving_sim_rs. Open upstream PRs 19 and 21 are not included. diff --git a/RELEASE_MANIFEST.json b/RELEASE_MANIFEST.json index eafaeb95336eae10c9a25de745873c65949b440e..bf92d9d25d0dc88b097a85fb77dab2318b7a21b6 100644 --- a/RELEASE_MANIFEST.json +++ b/RELEASE_MANIFEST.json @@ -1,21 +1,25 @@ { "backend": "cpu+zerogpu", "files": { - "BETA_TESTING.md": "d511965b9d675a092571f8815348da991ef1ef6be3766266c1f98ff393fd3a4d", - "DATA_POLICY.md": "1434ac5420a611b35c69da90bbbb70abb088754fdab0354a51f0f892b4abe224", - "FRONTEND_SOURCE.json": "35f4b3eb2e268d4fd11aa36feb1220267c683395e2e1b0bf2ff535a945ac16f8", + ".gitattributes": "c2ad16718fc51c888d32efc88c8f6e23ebe5fcf6f48e2954f896ba9770f24d72", + "BETA_TESTING.md": "b3c8ca006e67bea3511a9e3e8cc976991a57a14fed93085739842d5e312818b4", + "BINARY_PROVENANCE.json": "02760a067747114299a6b79c3de2cd88e42395f43c37ce34c0b1678d6c445524", + "DATA_POLICY.md": "aaf42a8d3f2b82f4410fa2477bd5c09779c265baadda41ce567ad44bd23fea73", + "FRONTEND_SOURCE.json": "0b9d9983a6feb93ae56ea95c037d7f744ade111824617785a0cd9f7e2f61308e", "GODOT_LICENSE": "fb2b3bb82c60f1e76841ba2122eb902f8e39c151476fe21ce10c9a05d1045304", "IMPORTER_BUILD_RECEIPT.json": "4ba5c533cc617105478e4d85c83f456a7dfa68b1388049624619871ba9e42e9d", "LICENSE": "53d5851b6440feeae36f72a5f57fcab37858c48b7084765ece4b0c54b9a28ed8", - "README.md": "5157c28039004afb7a6ef40a5f5bc27ba431f16efd7d1d2482e9bdf34373f8fe", - "STORAGE_BOUNDARY.md": "cf6f4b6824f0780766a5c3a44e372e6e9b1cf7d73428cc14770af7ed5ade5d45", + "README.md": "478d7eef40681c0657a0e03628576ea275f7db197974de62ee8ed3f20bf2c8f4", + "STORAGE_BOUNDARY.md": "0cda17053a343c2c83055e5a7e1191f1567199622a6eea422b48e11ec70e702e", "TRACK_LIBRARY.md": "7e4829c5e0933bfb317c21d7441ad0ba2ee00ab7f9f1635549452d504aa0777b", + "UPSTREAM.json": "f613a978cab5fe7824b26c620c784c38711a984cf2ecc4490ac8229e01957c55", "app.py": "704f789a41846b0a5bc1d3f6f5305d7ac5a6b1719ff77c23585b89fd3e68d55d", "app_release.py": "7482559c667a665fd3cb6aa8af6c78422a822b515e14589db8306f7fd4860a8b", "assets/formula_template.json": "80f745df82bb7be45af52785fd582460bcddc63c5a4e1d8375d85b3122db4c6b", - "bin/altd-sim": "2f2f2144ae14b0ef599ca8510f95712da00243c6875881491d384b394e7499f7", + "assets/track-atlas.json": "d97c8900e3e5fb17734b5bd61d1f4f4b196d9be30af9848e2e273ae7e4079ae4", + "bin/altd-sim": "0135ecc3d435a9d327be7a92e048cfb5c5291a02ef1e0dddd6ead1cc19b31166", "bin/extract_saved_track": "3a7733c45b4301a786fcbfe92660a1c484749272da5f608d62f34878bd4ff31a", - "bin/libaltd_gpu.so": "279d6353b4c691686d6aeeae6abfd4a82b959e9b604f079a6c45f9823b48a87a", + "bin/libaltd_gpu.so": "0064e747543251bab90cb966f8df00b010716a92cc75910229d42c9b24c1dac9", "builtin_tracks.py": "2ecc2b1d00fe15701b0271d71e1e68d2e7e59c596330879494055f9b2a99c242", "builtin_tracks/catalog.json": "65006e70a87a5a53e3738166b0895bc4db9f99767dd891d917b68029abb4e80a", "builtin_tracks/tracks/Replica A01 Beginner.track": "ca5749537450430794e2eade2e0ac72fa141a4f225e13e2f8ef842abb948ff15", @@ -46,15 +50,16 @@ "builtin_tracks/tracks/Replica R08 Demolition Derby.track": "6f11a9ffbcb477c9d19964566b1b612d2a94675605246e93e21f27bbbdb357ef", "builtin_tracks/tracks/Replica R09 Eau Noir.track": "3ec0a78478cca1a4f0d2d98ac81f40783928adf271b70f0c32d295a6da2cdaa9", "builtin_tracks/tracks/Replica The Champions Trial.track": "a3bfb76422affc774b44c739b0636d08acb901fa7615b9adcb52c069f79834e1", - "client.js": "628ca4edec49c45302bbcbbc7c35f640442a7670105cdb42d8e21c59f1db1ff2", - "delivery.py": "4f5a4e45df92e5cbfdef6eb449ede9ba26217fe24da376fafddae5cbbb73847a", + "client.js": "90a682cf27f4b8abc2de792fa90f6a32d859ca9918b7e92386b2c4d9366dd638", + "delivery.py": "07453999c3831a16e8b1fcc2b4ae5fb28beb7d3952a3fae8de65d16eb6f11e5b", "gpu_tickets.py": "c32cb5de751d49d3e1cd970c7d6328a42ebe71602dd9fd3788c872eef42611da", "hf-auth.js": "b59691ce2c517f193a5d5141538ebe61c961bb163cefa1fcf09b414c388984c1", "hf_identity.py": "6993d0238ada835606dbdc3275c17a3fe5a5cac5ad2156ab78230acf2d051eba", - "index.html": "24fd848cd38d84d84236558e1d617d79554ae462317b3bea68b11c74d2802076", + "index.html": "d329965febb49db4939093ec0c512474db109a7b3a24e97cbabb18f97674757b", "requirements.txt": "9b532747e51bd052e82090ece860d6dafe027407f4512dc74272c148ea104c22", - "server.py": "dd9d2e709ea61b6a3e3942be8377f0cad12509d4df3dbe0baa930523c0a3120c", - "track-library.js": "d19e2a58442e07d0d5029fbdfae1461106b2c3fe91dcb7b3091e304fc700811a", + "server.py": "44eee069364f1a5d952ed316d30e27908111394ce53d776ccf0fb8099c6aee81", + "track-library.js": "351ec4c026b49420ce963f4310cb000d9e987fd28e6dac5ecd0176509340f6e6", + "track-map.js": "ebf0ffe889983b677060c785edd6398afb53c9e680e1ea7f13614a6a16712ac7", "training_config.py": "2b9768876fdc60b16afb45b98023c6c31666e30f7c56f1292f6071775e6da0c5", "vendor/gradio-client/LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", "vendor/gradio-client/dist/index.min.js": "fdd776db8633354f118fe2908c980592283968737e6b91dc3aa86d648da5c3fd", @@ -64,7 +69,6 @@ "vendor/hf-oauth/package.json": "fafd4d8891023a1961546a7d3bc25cd24f64923a2ebfc449fe6d516a2949bda8", "worker.py": "934fb941a7f73a82c9bbcff5f3290103d4dd55e66542e71d56ae8fe7101f0e57" }, - "hosted_verified": false, - "interface": "runner-beta", - "upstream_revision": "d8ab74c25df70b6a86ed101080e33d5fb785ea6f" + "interface": "single-engine-runner-with-map", + "upstream_revision": "b90b8af06ffc4ea4e4fd703becd1107a48b8e5e0" } diff --git a/STORAGE_BOUNDARY.md b/STORAGE_BOUNDARY.md index 2bb053060257e458c5f2fedf8158e25d5e5f6316..80fad8a2e169e2240b09424ef83a8029488e9cb8 100644 --- a/STORAGE_BOUNDARY.md +++ b/STORAGE_BOUNDARY.md @@ -11,7 +11,7 @@ same settings contract is used independently of the selected storage policy. | Selected built-in track | Synchronized selectors and one verified Blob | Page memory across runs; no cookie, recipe or localStorage persistence | | Worker inputs / intermediate files | Temporary directory | Removed before successful result publication | | GPU pending inputs | Opaque single-use ticket plus memory store | Dispatch, fixed 120-second expiry, or shutdown | -| Single-run output | Memory result store and random job key | Receipt ACK in automatic mode, manual delete, fixed 30-minute expiry, or shutdown | +| Single-run output | Memory result store and random job key | Default receipt ACK; opt-in repeat download until fixed five-minute expiry; manual delete or shutdown | | Batch intermediates | Same job contract, always automatic | Verified size/hash and deletion ACK before advancement | | Received candidate / receipt | Browser page memory | Explicit discard, replacement, page reload/close; explicit export to disk | | Optional HF identity / token | Browser page memory, separate from job/recipe storage | Sign out, expiry, or page reload/close; no app auth cookie or server session database | diff --git a/UPSTREAM.json b/UPSTREAM.json index 583e45778531436db1dea08a0b6c0c4ed222abe6..d31f4ad72d91306a10fdf53a2d101727da43fc98 100644 --- a/UPSTREAM.json +++ b/UPSTREAM.json @@ -1,10 +1,9 @@ { "repository": "https://github.com/Zorro909/driving_sim_rs", - "commit": "d8ab74c25df70b6a86ed101080e33d5fb785ea6f", - "previous_space_commit": "a3b395b0b608fb7caa2081348be153feabc38a69", + "commit": "b90b8af06ffc4ea4e4fd703becd1107a48b8e5e0", + "github_actions_run": 38203558700, "math_profile": "proton", "role": "gpu", - "gpu_runtime_verified": false, "open_prs_not_included": [ 19, 21 diff --git a/ZEROGPU_MANIFEST.json b/ZEROGPU_MANIFEST.json deleted file mode 100644 index f7b5d2bd5ec729f49299e786f5561d1b2d9206c9..0000000000000000000000000000000000000000 --- a/ZEROGPU_MANIFEST.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "README.md": "cd5ad5b4e2dd5230ecc0eb3083ea40e90c3665b5a9529a4286fbead2ed37bacb", - "app.py": "5d7e2fe152b0846f73a8383ef9e87c60925b4f7384221a4b04f639c5bdeb1e81", - "probe_core.py": "97a36ad8cf8efec09d84bb4b2c1ee6794658ceedfd643cd9849ff3c6d85aca9d", - "requirements.txt": "d4a97517b799a94b3370ea2e11aee3e41304aa12b77bab9abecd0b37e961acaf", - "BINARY_PROVENANCE.json": "f2223c1c6e2bbf8f6101b46e11563f4d866f99094c529cd5584f3228309e6551", - "bin/altd-sim": "2f2f2144ae14b0ef599ca8510f95712da00243c6875881491d384b394e7499f7", - "bin/libaltd_gpu.so": "279d6353b4c691686d6aeeae6abfd4a82b959e9b604f079a6c45f9823b48a87a", - "training_smoke.py": "91e278b8fdf1a025d2ce455f5a4e55b1751691be2689fadda92955fc6dcbb508" -} diff --git a/assets/track-atlas.json b/assets/track-atlas.json new file mode 100644 index 0000000000000000000000000000000000000000..b3974a057f0442c41f33c451296e086d8815f3f6 --- /dev/null +++ b/assets/track-atlas.json @@ -0,0 +1 @@ +{"blocks":{"101:0":{"connections":[{"index":1,"side":2,"type":0},{"index":0,"side":0,"type":0}],"surface":1},"101:1":{"connections":[{"index":2,"side":0,"type":0},{"index":1,"side":3,"type":0}],"surface":1},"101:2":{"connections":[{"index":1,"side":2,"type":0},{"index":0,"side":1,"type":0}],"surface":1},"101:3":{"connections":[{"index":0,"side":2,"type":0},{"index":1,"side":0,"type":0}],"surface":1},"101:4":{"connections":[{"index":1,"side":3,"type":0},{"index":2,"side":1,"type":0}],"surface":1},"101:5":{"connections":[{"index":2,"side":2,"type":0},{"index":1,"side":1,"type":0}],"surface":1},"101:6":{"connections":[{"index":1,"side":0,"type":0},{"index":0,"side":3,"type":0}],"surface":1},"101:7":{"connections":[{"index":1,"side":1,"type":0},{"index":2,"side":3,"type":0}],"surface":1},"102:0":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":0,"type":0}],"surface":1},"102:1":{"connections":[{"index":1,"side":0,"type":0},{"index":1,"side":3,"type":0}],"surface":1},"102:2":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":1,"type":0}],"surface":1},"102:3":{"connections":[{"index":1,"side":1,"type":0},{"index":1,"side":3,"type":0}],"surface":1},"103:0":{"connections":[{"index":0,"side":2,"type":0},{"index":0,"side":0,"type":0}],"surface":1},"103:1":{"connections":[{"index":2,"side":0,"type":0},{"index":0,"side":3,"type":0}],"surface":1},"103:2":{"connections":[{"index":2,"side":1,"type":0},{"index":2,"side":3,"type":0}],"surface":1},"103:3":{"connections":[{"index":2,"side":2,"type":0},{"index":0,"side":1,"type":0}],"surface":1},"104:0":{"connections":[{"index":1,"side":2,"type":0},{"index":2,"side":0,"type":0}],"surface":1},"104:1":{"connections":[{"index":0,"side":0,"type":0},{"index":1,"side":3,"type":0}],"surface":1},"104:2":{"connections":[{"index":1,"side":2,"type":0},{"index":2,"side":1,"type":0}],"surface":1},"104:3":{"connections":[{"index":1,"side":0,"type":0},{"index":2,"side":2,"type":0}],"surface":1},"104:4":{"connections":[{"index":0,"side":1,"type":0},{"index":1,"side":3,"type":0}],"surface":1},"104:5":{"connections":[{"index":0,"side":2,"type":0},{"index":1,"side":1,"type":0}],"surface":1},"104:6":{"connections":[{"index":1,"side":0,"type":0},{"index":2,"side":3,"type":0}],"surface":1},"104:7":{"connections":[{"index":1,"side":1,"type":0},{"index":0,"side":3,"type":0}],"surface":1},"105:0":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":3,"type":0}],"surface":1},"105:1":{"connections":[{"index":1,"side":0,"type":0},{"index":1,"side":1,"type":0}],"surface":1},"106:0":{"connections":[{"index":1,"side":2,"type":0},{"index":0,"side":3,"type":0}],"surface":1},"106:1":{"connections":[{"index":0,"side":2,"type":0},{"index":1,"side":3,"type":0}],"surface":1},"106:2":{"connections":[{"index":1,"side":2,"type":0},{"index":2,"side":3,"type":0}],"surface":1},"106:3":{"connections":[{"index":1,"side":0,"type":0},{"index":0,"side":1,"type":0}],"surface":1},"106:4":{"connections":[{"index":2,"side":2,"type":0},{"index":1,"side":3,"type":0}],"surface":1},"106:5":{"connections":[{"index":0,"side":0,"type":0},{"index":1,"side":1,"type":0}],"surface":1},"106:6":{"connections":[{"index":1,"side":0,"type":0},{"index":2,"side":1,"type":0}],"surface":1},"106:7":{"connections":[{"index":1,"side":1,"type":0},{"index":2,"side":0,"type":0}],"surface":1},"107:0":{"connections":[{"index":0,"side":2,"type":0},{"index":0,"side":1,"type":0}],"surface":1},"107:1":{"connections":[{"index":0,"side":3,"type":0},{"index":2,"side":1,"type":0}],"surface":1},"107:2":{"connections":[{"index":0,"side":0,"type":0},{"index":2,"side":2,"type":0}],"surface":1},"107:3":{"connections":[{"index":0,"side":0,"type":0},{"index":0,"side":3,"type":0}],"surface":1},"107:4":{"connections":[{"index":2,"side":0,"type":0},{"index":2,"side":3,"type":0}],"surface":1},"107:5":{"connections":[{"index":0,"side":1,"type":0},{"index":2,"side":3,"type":0}],"surface":1},"107:6":{"connections":[{"index":0,"side":2,"type":0},{"index":2,"side":0,"type":0}],"surface":1},"107:7":{"connections":[{"index":2,"side":2,"type":0},{"index":2,"side":1,"type":0}],"surface":1},"108:0":{"connections":[{"index":0,"side":2,"type":0},{"index":2,"side":1,"type":0}],"surface":1},"108:1":{"connections":[{"index":2,"side":2,"type":0},{"index":2,"side":0,"type":0}],"surface":1},"108:2":{"connections":[{"index":0,"side":0,"type":0},{"index":2,"side":3,"type":0}],"surface":1},"108:3":{"connections":[{"index":0,"side":1,"type":0},{"index":0,"side":3,"type":0}],"surface":1},"109:0":{"connections":[{"index":0,"side":2,"type":0},{"index":2,"side":3,"type":0}],"surface":1},"109:1":{"connections":[{"index":2,"side":2,"type":0},{"index":0,"side":3,"type":0}],"surface":1},"109:2":{"connections":[{"index":0,"side":1,"type":0},{"index":2,"side":0,"type":0}],"surface":1},"109:3":{"connections":[{"index":0,"side":0,"type":0},{"index":2,"side":1,"type":0}],"surface":1},"10:0":{"connections":[{"index":0,"side":2,"type":0},{"index":0,"side":3,"type":0}],"surface":0},"10:1":{"connections":[{"index":2,"side":2,"type":0},{"index":2,"side":3,"type":0}],"surface":0},"10:2":{"connections":[{"index":0,"side":0,"type":0},{"index":0,"side":1,"type":0}],"surface":0},"10:3":{"connections":[{"index":2,"side":0,"type":0},{"index":2,"side":1,"type":0}],"surface":0},"110:0":{"connections":[{"index":0,"side":2,"type":0},{"index":0,"side":3,"type":0}],"surface":1},"110:1":{"connections":[{"index":2,"side":2,"type":0},{"index":2,"side":3,"type":0}],"surface":1},"110:2":{"connections":[{"index":0,"side":0,"type":0},{"index":0,"side":1,"type":0}],"surface":1},"110:3":{"connections":[{"index":2,"side":0,"type":0},{"index":2,"side":1,"type":0}],"surface":1},"111:0":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":3,"type":0}],"surface":1},"111:1":{"connections":[{"index":1,"side":0,"type":0},{"index":1,"side":1,"type":0}],"surface":1},"112:0":{"connections":[{"index":1,"side":2,"type":2},{"index":1,"side":3,"type":2}],"surface":1},"112:1":{"connections":[{"index":1,"side":0,"type":2},{"index":1,"side":1,"type":2}],"surface":1},"113:0":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":3,"type":2}],"surface":1},"113:1":{"connections":[{"index":1,"side":0,"type":0},{"index":1,"side":1,"type":2}],"surface":1},"113:2":{"connections":[{"index":1,"side":2,"type":2},{"index":1,"side":3,"type":0}],"surface":1},"113:3":{"connections":[{"index":1,"side":0,"type":2},{"index":1,"side":1,"type":0}],"surface":1},"114:0":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":3,"type":0}],"surface":1},"114:1":{"connections":[{"index":1,"side":0,"type":0},{"index":1,"side":1,"type":0}],"surface":1},"115:0":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":3,"type":0}],"surface":1},"115:1":{"connections":[{"index":1,"side":0,"type":0},{"index":1,"side":1,"type":0}],"surface":1},"116:0":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":3,"type":0}],"surface":1},"116:1":{"connections":[{"index":1,"side":0,"type":0},{"index":1,"side":1,"type":0}],"surface":1},"117:0":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":3,"type":0}],"surface":1},"117:1":{"connections":[{"index":1,"side":0,"type":0},{"index":1,"side":1,"type":0}],"surface":1},"118:0":{"connections":[{"index":0,"side":2,"type":0},{"index":0,"side":3,"type":0}],"surface":1},"118:1":{"connections":[{"index":2,"side":2,"type":0},{"index":2,"side":3,"type":0}],"surface":1},"118:2":{"connections":[{"index":0,"side":0,"type":0},{"index":0,"side":1,"type":0}],"surface":1},"118:3":{"connections":[{"index":2,"side":0,"type":0},{"index":2,"side":1,"type":0}],"surface":1},"119:0":{"connections":[{"index":0,"side":2,"type":0},{"index":0,"side":3,"type":0}],"surface":1},"119:1":{"connections":[{"index":2,"side":2,"type":0},{"index":2,"side":3,"type":0}],"surface":1},"119:2":{"connections":[{"index":0,"side":0,"type":0},{"index":0,"side":1,"type":0}],"surface":1},"119:3":{"connections":[{"index":2,"side":0,"type":0},{"index":2,"side":1,"type":0}],"surface":1},"11:0":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":3,"type":0}],"surface":0},"11:1":{"connections":[{"index":1,"side":0,"type":0},{"index":1,"side":1,"type":0}],"surface":0},"120:0":{"connections":[{"index":0,"side":2,"type":0},{"index":0,"side":3,"type":0}],"surface":1},"120:1":{"connections":[{"index":2,"side":2,"type":0},{"index":2,"side":3,"type":0}],"surface":1},"120:2":{"connections":[{"index":0,"side":0,"type":0},{"index":0,"side":1,"type":0}],"surface":1},"120:3":{"connections":[{"index":2,"side":0,"type":0},{"index":2,"side":1,"type":0}],"surface":1},"121:0":{"connections":[{"index":1,"side":1,"type":1},{"index":1,"side":3,"type":0}],"surface":1},"121:1":{"connections":[{"index":1,"side":2,"type":1},{"index":1,"side":1,"type":0}],"surface":1},"121:2":{"connections":[{"index":1,"side":0,"type":1},{"index":1,"side":2,"type":0}],"surface":1},"121:3":{"connections":[{"index":1,"side":3,"type":1},{"index":1,"side":0,"type":0}],"surface":1},"121:4":{"connections":[{"index":1,"side":0,"type":1},{"index":1,"side":3,"type":0}],"surface":1},"121:5":{"connections":[{"index":1,"side":2,"type":1},{"index":1,"side":0,"type":0}],"surface":1},"121:6":{"connections":[{"index":1,"side":1,"type":1},{"index":1,"side":2,"type":0}],"surface":1},"121:7":{"connections":[{"index":1,"side":3,"type":1},{"index":1,"side":1,"type":0}],"surface":1},"122:0":{"connections":[{"index":1,"side":2,"type":1},{"index":1,"side":0,"type":1}],"surface":1},"122:1":{"connections":[{"index":1,"side":0,"type":1},{"index":1,"side":3,"type":1}],"surface":1},"122:2":{"connections":[{"index":1,"side":1,"type":1},{"index":1,"side":2,"type":1}],"surface":1},"122:3":{"connections":[{"index":1,"side":3,"type":1},{"index":1,"side":1,"type":1}],"surface":1},"123:0":{"connections":[{"index":0,"side":2,"type":0},{"index":0,"side":3,"type":0}],"surface":1},"123:1":{"connections":[{"index":2,"side":2,"type":0},{"index":2,"side":3,"type":0}],"surface":1},"123:2":{"connections":[{"index":0,"side":0,"type":0},{"index":0,"side":1,"type":0}],"surface":1},"123:3":{"connections":[{"index":2,"side":0,"type":0},{"index":2,"side":1,"type":0}],"surface":1},"124:0":{"connections":[{"index":1,"side":2,"type":4},{"index":0,"side":0,"type":3}],"surface":1},"124:1":{"connections":[{"index":2,"side":0,"type":3},{"index":1,"side":3,"type":4}],"surface":1},"124:2":{"connections":[{"index":1,"side":2,"type":4},{"index":0,"side":1,"type":3}],"surface":1},"124:3":{"connections":[{"index":0,"side":2,"type":3},{"index":1,"side":0,"type":4}],"surface":1},"124:4":{"connections":[{"index":1,"side":3,"type":4},{"index":2,"side":1,"type":3}],"surface":1},"124:5":{"connections":[{"index":2,"side":2,"type":3},{"index":1,"side":1,"type":4}],"surface":1},"124:6":{"connections":[{"index":1,"side":0,"type":4},{"index":0,"side":3,"type":3}],"surface":1},"124:7":{"connections":[{"index":1,"side":1,"type":4},{"index":2,"side":3,"type":3}],"surface":1},"125:0":{"connections":[{"index":1,"side":2,"type":4},{"index":0,"side":0,"type":0}],"surface":1},"125:1":{"connections":[{"index":2,"side":0,"type":0},{"index":1,"side":3,"type":4}],"surface":1},"125:2":{"connections":[{"index":1,"side":2,"type":4},{"index":0,"side":1,"type":0}],"surface":1},"125:3":{"connections":[{"index":0,"side":2,"type":0},{"index":1,"side":0,"type":4}],"surface":1},"125:4":{"connections":[{"index":1,"side":3,"type":4},{"index":2,"side":1,"type":0}],"surface":1},"125:5":{"connections":[{"index":2,"side":2,"type":0},{"index":1,"side":1,"type":4}],"surface":1},"125:6":{"connections":[{"index":1,"side":0,"type":4},{"index":0,"side":3,"type":0}],"surface":1},"125:7":{"connections":[{"index":1,"side":1,"type":4},{"index":2,"side":3,"type":0}],"surface":1},"126:0":{"connections":[{"index":1,"side":2,"type":3},{"index":0,"side":3,"type":3}],"surface":1},"126:1":{"connections":[{"index":0,"side":2,"type":3},{"index":1,"side":3,"type":3}],"surface":1},"126:2":{"connections":[{"index":1,"side":2,"type":3},{"index":2,"side":3,"type":3}],"surface":1},"126:3":{"connections":[{"index":1,"side":0,"type":3},{"index":0,"side":1,"type":3}],"surface":1},"126:4":{"connections":[{"index":2,"side":2,"type":3},{"index":1,"side":3,"type":3}],"surface":1},"126:5":{"connections":[{"index":0,"side":0,"type":3},{"index":1,"side":1,"type":3}],"surface":1},"126:6":{"connections":[{"index":1,"side":0,"type":3},{"index":2,"side":1,"type":3}],"surface":1},"126:7":{"connections":[{"index":1,"side":1,"type":3},{"index":2,"side":0,"type":3}],"surface":1},"127:0":{"connections":[{"index":1,"side":2,"type":0},{"index":0,"side":3,"type":3}],"surface":1},"127:1":{"connections":[{"index":0,"side":2,"type":3},{"index":1,"side":3,"type":0}],"surface":1},"127:2":{"connections":[{"index":1,"side":2,"type":0},{"index":2,"side":3,"type":3}],"surface":1},"127:3":{"connections":[{"index":1,"side":0,"type":0},{"index":0,"side":1,"type":3}],"surface":1},"127:4":{"connections":[{"index":2,"side":2,"type":3},{"index":1,"side":3,"type":0}],"surface":1},"127:5":{"connections":[{"index":0,"side":0,"type":3},{"index":1,"side":1,"type":0}],"surface":1},"127:6":{"connections":[{"index":1,"side":0,"type":0},{"index":2,"side":1,"type":3}],"surface":1},"127:7":{"connections":[{"index":1,"side":1,"type":0},{"index":2,"side":0,"type":3}],"surface":1},"128:0":{"connections":[{"index":1,"side":2,"type":3},{"index":0,"side":3,"type":0}],"surface":1},"128:1":{"connections":[{"index":0,"side":2,"type":0},{"index":1,"side":3,"type":3}],"surface":1},"128:2":{"connections":[{"index":1,"side":2,"type":3},{"index":2,"side":3,"type":0}],"surface":1},"128:3":{"connections":[{"index":1,"side":0,"type":3},{"index":0,"side":1,"type":0}],"surface":1},"128:4":{"connections":[{"index":2,"side":2,"type":0},{"index":1,"side":3,"type":3}],"surface":1},"128:5":{"connections":[{"index":0,"side":0,"type":0},{"index":1,"side":1,"type":3}],"surface":1},"128:6":{"connections":[{"index":1,"side":0,"type":3},{"index":2,"side":1,"type":0}],"surface":1},"128:7":{"connections":[{"index":1,"side":1,"type":3},{"index":2,"side":0,"type":0}],"surface":1},"12:0":{"connections":[{"index":1,"side":2,"type":2},{"index":1,"side":3,"type":2}],"surface":0},"12:1":{"connections":[{"index":1,"side":0,"type":2},{"index":1,"side":1,"type":2}],"surface":0},"13:0":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":3,"type":2}],"surface":0},"13:1":{"connections":[{"index":1,"side":0,"type":0},{"index":1,"side":1,"type":2}],"surface":0},"13:2":{"connections":[{"index":1,"side":2,"type":2},{"index":1,"side":3,"type":0}],"surface":0},"13:3":{"connections":[{"index":1,"side":0,"type":2},{"index":1,"side":1,"type":0}],"surface":0},"14:0":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":3,"type":0}],"surface":0},"14:1":{"connections":[{"index":1,"side":0,"type":0},{"index":1,"side":1,"type":0}],"surface":0},"15:0":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":3,"type":0}],"surface":0},"15:1":{"connections":[{"index":1,"side":0,"type":0},{"index":1,"side":1,"type":0}],"surface":0},"16:0":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":3,"type":0}],"surface":0},"16:1":{"connections":[{"index":1,"side":0,"type":0},{"index":1,"side":1,"type":0}],"surface":0},"17:0":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":3,"type":0}],"surface":0},"17:1":{"connections":[{"index":1,"side":0,"type":0},{"index":1,"side":1,"type":0}],"surface":0},"18:0":{"connections":[{"index":0,"side":2,"type":0},{"index":0,"side":3,"type":0}],"surface":0},"18:1":{"connections":[{"index":2,"side":2,"type":0},{"index":2,"side":3,"type":0}],"surface":0},"18:2":{"connections":[{"index":0,"side":0,"type":0},{"index":0,"side":1,"type":0}],"surface":0},"18:3":{"connections":[{"index":2,"side":0,"type":0},{"index":2,"side":1,"type":0}],"surface":0},"19:0":{"connections":[{"index":0,"side":2,"type":0},{"index":0,"side":3,"type":0}],"surface":0},"19:1":{"connections":[{"index":2,"side":2,"type":0},{"index":2,"side":3,"type":0}],"surface":0},"19:2":{"connections":[{"index":0,"side":0,"type":0},{"index":0,"side":1,"type":0}],"surface":0},"19:3":{"connections":[{"index":2,"side":0,"type":0},{"index":2,"side":1,"type":0}],"surface":0},"1:0":{"connections":[{"index":1,"side":2,"type":0},{"index":0,"side":0,"type":0}],"surface":0},"1:1":{"connections":[{"index":2,"side":0,"type":0},{"index":1,"side":3,"type":0}],"surface":0},"1:2":{"connections":[{"index":1,"side":2,"type":0},{"index":0,"side":1,"type":0}],"surface":0},"1:3":{"connections":[{"index":0,"side":2,"type":0},{"index":1,"side":0,"type":0}],"surface":0},"1:4":{"connections":[{"index":1,"side":3,"type":0},{"index":2,"side":1,"type":0}],"surface":0},"1:5":{"connections":[{"index":2,"side":2,"type":0},{"index":1,"side":1,"type":0}],"surface":0},"1:6":{"connections":[{"index":1,"side":0,"type":0},{"index":0,"side":3,"type":0}],"surface":0},"1:7":{"connections":[{"index":1,"side":1,"type":0},{"index":2,"side":3,"type":0}],"surface":0},"201:0":{"connections":[{"index":1,"side":2,"type":0},{"index":0,"side":0,"type":0}],"surface":2},"201:1":{"connections":[{"index":2,"side":0,"type":0},{"index":1,"side":3,"type":0}],"surface":2},"201:2":{"connections":[{"index":1,"side":2,"type":0},{"index":0,"side":1,"type":0}],"surface":2},"201:3":{"connections":[{"index":0,"side":2,"type":0},{"index":1,"side":0,"type":0}],"surface":2},"201:4":{"connections":[{"index":1,"side":3,"type":0},{"index":2,"side":1,"type":0}],"surface":2},"201:5":{"connections":[{"index":2,"side":2,"type":0},{"index":1,"side":1,"type":0}],"surface":2},"201:6":{"connections":[{"index":1,"side":0,"type":0},{"index":0,"side":3,"type":0}],"surface":2},"201:7":{"connections":[{"index":1,"side":1,"type":0},{"index":2,"side":3,"type":0}],"surface":2},"202:0":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":0,"type":0}],"surface":2},"202:1":{"connections":[{"index":1,"side":0,"type":0},{"index":1,"side":3,"type":0}],"surface":2},"202:2":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":1,"type":0}],"surface":2},"202:3":{"connections":[{"index":1,"side":1,"type":0},{"index":1,"side":3,"type":0}],"surface":2},"203:0":{"connections":[{"index":0,"side":2,"type":0},{"index":0,"side":0,"type":0}],"surface":2},"203:1":{"connections":[{"index":2,"side":0,"type":0},{"index":0,"side":3,"type":0}],"surface":2},"203:2":{"connections":[{"index":2,"side":1,"type":0},{"index":2,"side":3,"type":0}],"surface":2},"203:3":{"connections":[{"index":2,"side":2,"type":0},{"index":0,"side":1,"type":0}],"surface":2},"204:0":{"connections":[{"index":1,"side":2,"type":0},{"index":2,"side":0,"type":0}],"surface":2},"204:1":{"connections":[{"index":0,"side":0,"type":0},{"index":1,"side":3,"type":0}],"surface":2},"204:2":{"connections":[{"index":1,"side":2,"type":0},{"index":2,"side":1,"type":0}],"surface":2},"204:3":{"connections":[{"index":1,"side":0,"type":0},{"index":2,"side":2,"type":0}],"surface":2},"204:4":{"connections":[{"index":0,"side":1,"type":0},{"index":1,"side":3,"type":0}],"surface":2},"204:5":{"connections":[{"index":0,"side":2,"type":0},{"index":1,"side":1,"type":0}],"surface":2},"204:6":{"connections":[{"index":1,"side":0,"type":0},{"index":2,"side":3,"type":0}],"surface":2},"204:7":{"connections":[{"index":1,"side":1,"type":0},{"index":0,"side":3,"type":0}],"surface":2},"205:0":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":3,"type":0}],"surface":2},"205:1":{"connections":[{"index":1,"side":0,"type":0},{"index":1,"side":1,"type":0}],"surface":2},"206:0":{"connections":[{"index":1,"side":2,"type":0},{"index":0,"side":3,"type":0}],"surface":2},"206:1":{"connections":[{"index":0,"side":2,"type":0},{"index":1,"side":3,"type":0}],"surface":2},"206:2":{"connections":[{"index":1,"side":2,"type":0},{"index":2,"side":3,"type":0}],"surface":2},"206:3":{"connections":[{"index":1,"side":0,"type":0},{"index":0,"side":1,"type":0}],"surface":2},"206:4":{"connections":[{"index":2,"side":2,"type":0},{"index":1,"side":3,"type":0}],"surface":2},"206:5":{"connections":[{"index":0,"side":0,"type":0},{"index":1,"side":1,"type":0}],"surface":2},"206:6":{"connections":[{"index":1,"side":0,"type":0},{"index":2,"side":1,"type":0}],"surface":2},"206:7":{"connections":[{"index":1,"side":1,"type":0},{"index":2,"side":0,"type":0}],"surface":2},"207:0":{"connections":[{"index":0,"side":2,"type":0},{"index":0,"side":1,"type":0}],"surface":2},"207:1":{"connections":[{"index":0,"side":3,"type":0},{"index":2,"side":1,"type":0}],"surface":2},"207:2":{"connections":[{"index":0,"side":0,"type":0},{"index":2,"side":2,"type":0}],"surface":2},"207:3":{"connections":[{"index":0,"side":0,"type":0},{"index":0,"side":3,"type":0}],"surface":2},"207:4":{"connections":[{"index":2,"side":0,"type":0},{"index":2,"side":3,"type":0}],"surface":2},"207:5":{"connections":[{"index":0,"side":1,"type":0},{"index":2,"side":3,"type":0}],"surface":2},"207:6":{"connections":[{"index":0,"side":2,"type":0},{"index":2,"side":0,"type":0}],"surface":2},"207:7":{"connections":[{"index":2,"side":2,"type":0},{"index":2,"side":1,"type":0}],"surface":2},"208:0":{"connections":[{"index":0,"side":2,"type":0},{"index":2,"side":1,"type":0}],"surface":2},"208:1":{"connections":[{"index":2,"side":2,"type":0},{"index":2,"side":0,"type":0}],"surface":2},"208:2":{"connections":[{"index":0,"side":0,"type":0},{"index":2,"side":3,"type":0}],"surface":2},"208:3":{"connections":[{"index":0,"side":1,"type":0},{"index":0,"side":3,"type":0}],"surface":2},"209:0":{"connections":[{"index":0,"side":2,"type":0},{"index":2,"side":3,"type":0}],"surface":2},"209:1":{"connections":[{"index":2,"side":2,"type":0},{"index":0,"side":3,"type":0}],"surface":2},"209:2":{"connections":[{"index":0,"side":1,"type":0},{"index":2,"side":0,"type":0}],"surface":2},"209:3":{"connections":[{"index":0,"side":0,"type":0},{"index":2,"side":1,"type":0}],"surface":2},"20:0":{"connections":[{"index":0,"side":2,"type":0},{"index":0,"side":3,"type":0}],"surface":0},"20:1":{"connections":[{"index":2,"side":2,"type":0},{"index":2,"side":3,"type":0}],"surface":0},"20:2":{"connections":[{"index":0,"side":0,"type":0},{"index":0,"side":1,"type":0}],"surface":0},"20:3":{"connections":[{"index":2,"side":0,"type":0},{"index":2,"side":1,"type":0}],"surface":0},"210:0":{"connections":[{"index":0,"side":2,"type":0},{"index":0,"side":3,"type":0}],"surface":2},"210:1":{"connections":[{"index":2,"side":2,"type":0},{"index":2,"side":3,"type":0}],"surface":2},"210:2":{"connections":[{"index":0,"side":0,"type":0},{"index":0,"side":1,"type":0}],"surface":2},"210:3":{"connections":[{"index":2,"side":0,"type":0},{"index":2,"side":1,"type":0}],"surface":2},"211:0":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":3,"type":0}],"surface":2},"211:1":{"connections":[{"index":1,"side":0,"type":0},{"index":1,"side":1,"type":0}],"surface":2},"212:0":{"connections":[{"index":1,"side":2,"type":2},{"index":1,"side":3,"type":2}],"surface":2},"212:1":{"connections":[{"index":1,"side":0,"type":2},{"index":1,"side":1,"type":2}],"surface":2},"213:0":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":3,"type":2}],"surface":2},"213:1":{"connections":[{"index":1,"side":0,"type":0},{"index":1,"side":1,"type":2}],"surface":2},"213:2":{"connections":[{"index":1,"side":2,"type":2},{"index":1,"side":3,"type":0}],"surface":2},"213:3":{"connections":[{"index":1,"side":0,"type":2},{"index":1,"side":1,"type":0}],"surface":2},"214:0":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":3,"type":0}],"surface":2},"214:1":{"connections":[{"index":1,"side":0,"type":0},{"index":1,"side":1,"type":0}],"surface":2},"215:0":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":3,"type":0}],"surface":2},"215:1":{"connections":[{"index":1,"side":0,"type":0},{"index":1,"side":1,"type":0}],"surface":2},"216:0":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":3,"type":0}],"surface":2},"216:1":{"connections":[{"index":1,"side":0,"type":0},{"index":1,"side":1,"type":0}],"surface":2},"217:0":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":3,"type":0}],"surface":2},"217:1":{"connections":[{"index":1,"side":0,"type":0},{"index":1,"side":1,"type":0}],"surface":2},"218:0":{"connections":[{"index":0,"side":2,"type":0},{"index":0,"side":3,"type":0}],"surface":2},"218:1":{"connections":[{"index":2,"side":2,"type":0},{"index":2,"side":3,"type":0}],"surface":2},"218:2":{"connections":[{"index":0,"side":0,"type":0},{"index":0,"side":1,"type":0}],"surface":2},"218:3":{"connections":[{"index":2,"side":0,"type":0},{"index":2,"side":1,"type":0}],"surface":2},"219:0":{"connections":[{"index":0,"side":2,"type":0},{"index":0,"side":3,"type":0}],"surface":2},"219:1":{"connections":[{"index":2,"side":2,"type":0},{"index":2,"side":3,"type":0}],"surface":2},"219:2":{"connections":[{"index":0,"side":0,"type":0},{"index":0,"side":1,"type":0}],"surface":2},"219:3":{"connections":[{"index":2,"side":0,"type":0},{"index":2,"side":1,"type":0}],"surface":2},"21:0":{"connections":[{"index":1,"side":1,"type":1},{"index":1,"side":3,"type":0}],"surface":0},"21:1":{"connections":[{"index":1,"side":2,"type":1},{"index":1,"side":1,"type":0}],"surface":0},"21:2":{"connections":[{"index":1,"side":0,"type":1},{"index":1,"side":2,"type":0}],"surface":0},"21:3":{"connections":[{"index":1,"side":3,"type":1},{"index":1,"side":0,"type":0}],"surface":0},"21:4":{"connections":[{"index":1,"side":0,"type":1},{"index":1,"side":3,"type":0}],"surface":0},"21:5":{"connections":[{"index":1,"side":2,"type":1},{"index":1,"side":0,"type":0}],"surface":0},"21:6":{"connections":[{"index":1,"side":1,"type":1},{"index":1,"side":2,"type":0}],"surface":0},"21:7":{"connections":[{"index":1,"side":3,"type":1},{"index":1,"side":1,"type":0}],"surface":0},"220:0":{"connections":[{"index":0,"side":2,"type":0},{"index":0,"side":3,"type":0}],"surface":2},"220:1":{"connections":[{"index":2,"side":2,"type":0},{"index":2,"side":3,"type":0}],"surface":2},"220:2":{"connections":[{"index":0,"side":0,"type":0},{"index":0,"side":1,"type":0}],"surface":2},"220:3":{"connections":[{"index":2,"side":0,"type":0},{"index":2,"side":1,"type":0}],"surface":2},"221:0":{"connections":[{"index":1,"side":1,"type":1},{"index":1,"side":3,"type":0}],"surface":2},"221:1":{"connections":[{"index":1,"side":2,"type":1},{"index":1,"side":1,"type":0}],"surface":2},"221:2":{"connections":[{"index":1,"side":0,"type":1},{"index":1,"side":2,"type":0}],"surface":2},"221:3":{"connections":[{"index":1,"side":3,"type":1},{"index":1,"side":0,"type":0}],"surface":2},"221:4":{"connections":[{"index":1,"side":0,"type":1},{"index":1,"side":3,"type":0}],"surface":2},"221:5":{"connections":[{"index":1,"side":2,"type":1},{"index":1,"side":0,"type":0}],"surface":2},"221:6":{"connections":[{"index":1,"side":1,"type":1},{"index":1,"side":2,"type":0}],"surface":2},"221:7":{"connections":[{"index":1,"side":3,"type":1},{"index":1,"side":1,"type":0}],"surface":2},"222:0":{"connections":[{"index":1,"side":2,"type":1},{"index":1,"side":0,"type":1}],"surface":2},"222:1":{"connections":[{"index":1,"side":0,"type":1},{"index":1,"side":3,"type":1}],"surface":2},"222:2":{"connections":[{"index":1,"side":1,"type":1},{"index":1,"side":2,"type":1}],"surface":2},"222:3":{"connections":[{"index":1,"side":3,"type":1},{"index":1,"side":1,"type":1}],"surface":2},"223:0":{"connections":[{"index":0,"side":2,"type":0},{"index":0,"side":3,"type":0}],"surface":2},"223:1":{"connections":[{"index":2,"side":2,"type":0},{"index":2,"side":3,"type":0}],"surface":2},"223:2":{"connections":[{"index":0,"side":0,"type":0},{"index":0,"side":1,"type":0}],"surface":2},"223:3":{"connections":[{"index":2,"side":0,"type":0},{"index":2,"side":1,"type":0}],"surface":2},"224:0":{"connections":[{"index":1,"side":2,"type":4},{"index":0,"side":0,"type":3}],"surface":2},"224:1":{"connections":[{"index":2,"side":0,"type":3},{"index":1,"side":3,"type":4}],"surface":2},"224:2":{"connections":[{"index":1,"side":2,"type":4},{"index":0,"side":1,"type":3}],"surface":2},"224:3":{"connections":[{"index":0,"side":2,"type":3},{"index":1,"side":0,"type":4}],"surface":2},"224:4":{"connections":[{"index":1,"side":3,"type":4},{"index":2,"side":1,"type":3}],"surface":2},"224:5":{"connections":[{"index":2,"side":2,"type":3},{"index":1,"side":1,"type":4}],"surface":2},"224:6":{"connections":[{"index":1,"side":0,"type":4},{"index":0,"side":3,"type":3}],"surface":2},"224:7":{"connections":[{"index":1,"side":1,"type":4},{"index":2,"side":3,"type":3}],"surface":2},"225:0":{"connections":[{"index":1,"side":2,"type":4},{"index":0,"side":0,"type":0}],"surface":2},"225:1":{"connections":[{"index":2,"side":0,"type":0},{"index":1,"side":3,"type":4}],"surface":2},"225:2":{"connections":[{"index":1,"side":2,"type":4},{"index":0,"side":1,"type":0}],"surface":2},"225:3":{"connections":[{"index":0,"side":2,"type":0},{"index":1,"side":0,"type":4}],"surface":2},"225:4":{"connections":[{"index":1,"side":3,"type":4},{"index":2,"side":1,"type":0}],"surface":2},"225:5":{"connections":[{"index":2,"side":2,"type":0},{"index":1,"side":1,"type":4}],"surface":2},"225:6":{"connections":[{"index":1,"side":0,"type":4},{"index":0,"side":3,"type":0}],"surface":2},"225:7":{"connections":[{"index":1,"side":1,"type":4},{"index":2,"side":3,"type":0}],"surface":2},"226:0":{"connections":[{"index":1,"side":2,"type":3},{"index":0,"side":3,"type":3}],"surface":2},"226:1":{"connections":[{"index":0,"side":2,"type":3},{"index":1,"side":3,"type":3}],"surface":2},"226:2":{"connections":[{"index":1,"side":2,"type":3},{"index":2,"side":3,"type":3}],"surface":2},"226:3":{"connections":[{"index":1,"side":0,"type":3},{"index":0,"side":1,"type":3}],"surface":2},"226:4":{"connections":[{"index":2,"side":2,"type":3},{"index":1,"side":3,"type":3}],"surface":2},"226:5":{"connections":[{"index":0,"side":0,"type":3},{"index":1,"side":1,"type":3}],"surface":2},"226:6":{"connections":[{"index":1,"side":0,"type":3},{"index":2,"side":1,"type":3}],"surface":2},"226:7":{"connections":[{"index":1,"side":1,"type":3},{"index":2,"side":0,"type":3}],"surface":2},"227:0":{"connections":[{"index":1,"side":2,"type":0},{"index":0,"side":3,"type":3}],"surface":2},"227:1":{"connections":[{"index":0,"side":2,"type":3},{"index":1,"side":3,"type":0}],"surface":2},"227:2":{"connections":[{"index":1,"side":2,"type":0},{"index":2,"side":3,"type":3}],"surface":2},"227:3":{"connections":[{"index":1,"side":0,"type":0},{"index":0,"side":1,"type":3}],"surface":2},"227:4":{"connections":[{"index":2,"side":2,"type":3},{"index":1,"side":3,"type":0}],"surface":2},"227:5":{"connections":[{"index":0,"side":0,"type":3},{"index":1,"side":1,"type":0}],"surface":2},"227:6":{"connections":[{"index":1,"side":0,"type":0},{"index":2,"side":1,"type":3}],"surface":2},"227:7":{"connections":[{"index":1,"side":1,"type":0},{"index":2,"side":0,"type":3}],"surface":2},"228:0":{"connections":[{"index":1,"side":2,"type":3},{"index":0,"side":3,"type":0}],"surface":2},"228:1":{"connections":[{"index":0,"side":2,"type":0},{"index":1,"side":3,"type":3}],"surface":2},"228:2":{"connections":[{"index":1,"side":2,"type":3},{"index":2,"side":3,"type":0}],"surface":2},"228:3":{"connections":[{"index":1,"side":0,"type":3},{"index":0,"side":1,"type":0}],"surface":2},"228:4":{"connections":[{"index":2,"side":2,"type":0},{"index":1,"side":3,"type":3}],"surface":2},"228:5":{"connections":[{"index":0,"side":0,"type":0},{"index":1,"side":1,"type":3}],"surface":2},"228:6":{"connections":[{"index":1,"side":0,"type":3},{"index":2,"side":1,"type":0}],"surface":2},"228:7":{"connections":[{"index":1,"side":1,"type":3},{"index":2,"side":0,"type":0}],"surface":2},"22:0":{"connections":[{"index":1,"side":2,"type":1},{"index":1,"side":0,"type":1}],"surface":0},"22:1":{"connections":[{"index":1,"side":0,"type":1},{"index":1,"side":3,"type":1}],"surface":0},"22:2":{"connections":[{"index":1,"side":1,"type":1},{"index":1,"side":2,"type":1}],"surface":0},"22:3":{"connections":[{"index":1,"side":3,"type":1},{"index":1,"side":1,"type":1}],"surface":0},"23:0":{"connections":[{"index":0,"side":2,"type":0},{"index":0,"side":3,"type":0}],"surface":0},"23:1":{"connections":[{"index":2,"side":2,"type":0},{"index":2,"side":3,"type":0}],"surface":0},"23:2":{"connections":[{"index":0,"side":0,"type":0},{"index":0,"side":1,"type":0}],"surface":0},"23:3":{"connections":[{"index":2,"side":0,"type":0},{"index":2,"side":1,"type":0}],"surface":0},"24:0":{"connections":[{"index":1,"side":2,"type":4},{"index":0,"side":0,"type":3}],"surface":0},"24:1":{"connections":[{"index":2,"side":0,"type":3},{"index":1,"side":3,"type":4}],"surface":0},"24:2":{"connections":[{"index":1,"side":2,"type":4},{"index":0,"side":1,"type":3}],"surface":0},"24:3":{"connections":[{"index":0,"side":2,"type":3},{"index":1,"side":0,"type":4}],"surface":0},"24:4":{"connections":[{"index":1,"side":3,"type":4},{"index":2,"side":1,"type":3}],"surface":0},"24:5":{"connections":[{"index":2,"side":2,"type":3},{"index":1,"side":1,"type":4}],"surface":0},"24:6":{"connections":[{"index":1,"side":0,"type":4},{"index":0,"side":3,"type":3}],"surface":0},"24:7":{"connections":[{"index":1,"side":1,"type":4},{"index":2,"side":3,"type":3}],"surface":0},"25:0":{"connections":[{"index":1,"side":2,"type":4},{"index":0,"side":0,"type":0}],"surface":0},"25:1":{"connections":[{"index":2,"side":0,"type":0},{"index":1,"side":3,"type":4}],"surface":0},"25:2":{"connections":[{"index":1,"side":2,"type":4},{"index":0,"side":1,"type":0}],"surface":0},"25:3":{"connections":[{"index":0,"side":2,"type":0},{"index":1,"side":0,"type":4}],"surface":0},"25:4":{"connections":[{"index":1,"side":3,"type":4},{"index":2,"side":1,"type":0}],"surface":0},"25:5":{"connections":[{"index":2,"side":2,"type":0},{"index":1,"side":1,"type":4}],"surface":0},"25:6":{"connections":[{"index":1,"side":0,"type":4},{"index":0,"side":3,"type":0}],"surface":0},"25:7":{"connections":[{"index":1,"side":1,"type":4},{"index":2,"side":3,"type":0}],"surface":0},"26:0":{"connections":[{"index":1,"side":2,"type":3},{"index":0,"side":3,"type":3}],"surface":0},"26:1":{"connections":[{"index":0,"side":2,"type":3},{"index":1,"side":3,"type":3}],"surface":0},"26:2":{"connections":[{"index":1,"side":2,"type":3},{"index":2,"side":3,"type":3}],"surface":0},"26:3":{"connections":[{"index":1,"side":0,"type":3},{"index":0,"side":1,"type":3}],"surface":0},"26:4":{"connections":[{"index":2,"side":2,"type":3},{"index":1,"side":3,"type":3}],"surface":0},"26:5":{"connections":[{"index":0,"side":0,"type":3},{"index":1,"side":1,"type":3}],"surface":0},"26:6":{"connections":[{"index":1,"side":0,"type":3},{"index":2,"side":1,"type":3}],"surface":0},"26:7":{"connections":[{"index":1,"side":1,"type":3},{"index":2,"side":0,"type":3}],"surface":0},"27:0":{"connections":[{"index":1,"side":2,"type":0},{"index":0,"side":3,"type":3}],"surface":0},"27:1":{"connections":[{"index":0,"side":2,"type":3},{"index":1,"side":3,"type":0}],"surface":0},"27:2":{"connections":[{"index":1,"side":2,"type":0},{"index":2,"side":3,"type":3}],"surface":0},"27:3":{"connections":[{"index":1,"side":0,"type":0},{"index":0,"side":1,"type":3}],"surface":0},"27:4":{"connections":[{"index":2,"side":2,"type":3},{"index":1,"side":3,"type":0}],"surface":0},"27:5":{"connections":[{"index":0,"side":0,"type":3},{"index":1,"side":1,"type":0}],"surface":0},"27:6":{"connections":[{"index":1,"side":0,"type":0},{"index":2,"side":1,"type":3}],"surface":0},"27:7":{"connections":[{"index":1,"side":1,"type":0},{"index":2,"side":0,"type":3}],"surface":0},"28:0":{"connections":[{"index":1,"side":2,"type":3},{"index":0,"side":3,"type":0}],"surface":0},"28:1":{"connections":[{"index":0,"side":2,"type":0},{"index":1,"side":3,"type":3}],"surface":0},"28:2":{"connections":[{"index":1,"side":2,"type":3},{"index":2,"side":3,"type":0}],"surface":0},"28:3":{"connections":[{"index":1,"side":0,"type":3},{"index":0,"side":1,"type":0}],"surface":0},"28:4":{"connections":[{"index":2,"side":2,"type":0},{"index":1,"side":3,"type":3}],"surface":0},"28:5":{"connections":[{"index":0,"side":0,"type":0},{"index":1,"side":1,"type":3}],"surface":0},"28:6":{"connections":[{"index":1,"side":0,"type":3},{"index":2,"side":1,"type":0}],"surface":0},"28:7":{"connections":[{"index":1,"side":1,"type":3},{"index":2,"side":0,"type":0}],"surface":0},"2:0":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":0,"type":0}],"surface":0},"2:1":{"connections":[{"index":1,"side":0,"type":0},{"index":1,"side":3,"type":0}],"surface":0},"2:2":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":1,"type":0}],"surface":0},"2:3":{"connections":[{"index":1,"side":1,"type":0},{"index":1,"side":3,"type":0}],"surface":0},"3:0":{"connections":[{"index":0,"side":2,"type":0},{"index":0,"side":0,"type":0}],"surface":0},"3:1":{"connections":[{"index":2,"side":0,"type":0},{"index":0,"side":3,"type":0}],"surface":0},"3:2":{"connections":[{"index":2,"side":1,"type":0},{"index":2,"side":3,"type":0}],"surface":0},"3:3":{"connections":[{"index":2,"side":2,"type":0},{"index":0,"side":1,"type":0}],"surface":0},"4:0":{"connections":[{"index":1,"side":2,"type":0},{"index":2,"side":0,"type":0}],"surface":0},"4:1":{"connections":[{"index":0,"side":0,"type":0},{"index":1,"side":3,"type":0}],"surface":0},"4:2":{"connections":[{"index":1,"side":2,"type":0},{"index":2,"side":1,"type":0}],"surface":0},"4:3":{"connections":[{"index":1,"side":0,"type":0},{"index":2,"side":2,"type":0}],"surface":0},"4:4":{"connections":[{"index":0,"side":1,"type":0},{"index":1,"side":3,"type":0}],"surface":0},"4:5":{"connections":[{"index":0,"side":2,"type":0},{"index":1,"side":1,"type":0}],"surface":0},"4:6":{"connections":[{"index":1,"side":0,"type":0},{"index":2,"side":3,"type":0}],"surface":0},"4:7":{"connections":[{"index":1,"side":1,"type":0},{"index":0,"side":3,"type":0}],"surface":0},"5:0":{"connections":[{"index":1,"side":2,"type":0},{"index":1,"side":3,"type":0}],"surface":0},"5:1":{"connections":[{"index":1,"side":0,"type":0},{"index":1,"side":1,"type":0}],"surface":0},"6:0":{"connections":[{"index":1,"side":2,"type":0},{"index":0,"side":3,"type":0}],"surface":0},"6:1":{"connections":[{"index":0,"side":2,"type":0},{"index":1,"side":3,"type":0}],"surface":0},"6:2":{"connections":[{"index":1,"side":2,"type":0},{"index":2,"side":3,"type":0}],"surface":0},"6:3":{"connections":[{"index":1,"side":0,"type":0},{"index":0,"side":1,"type":0}],"surface":0},"6:4":{"connections":[{"index":2,"side":2,"type":0},{"index":1,"side":3,"type":0}],"surface":0},"6:5":{"connections":[{"index":0,"side":0,"type":0},{"index":1,"side":1,"type":0}],"surface":0},"6:6":{"connections":[{"index":1,"side":0,"type":0},{"index":2,"side":1,"type":0}],"surface":0},"6:7":{"connections":[{"index":1,"side":1,"type":0},{"index":2,"side":0,"type":0}],"surface":0},"7:0":{"connections":[{"index":0,"side":2,"type":0},{"index":0,"side":1,"type":0}],"surface":0},"7:1":{"connections":[{"index":0,"side":3,"type":0},{"index":2,"side":1,"type":0}],"surface":0},"7:2":{"connections":[{"index":0,"side":0,"type":0},{"index":2,"side":2,"type":0}],"surface":0},"7:3":{"connections":[{"index":0,"side":0,"type":0},{"index":0,"side":3,"type":0}],"surface":0},"7:4":{"connections":[{"index":2,"side":0,"type":0},{"index":2,"side":3,"type":0}],"surface":0},"7:5":{"connections":[{"index":0,"side":1,"type":0},{"index":2,"side":3,"type":0}],"surface":0},"7:6":{"connections":[{"index":0,"side":2,"type":0},{"index":2,"side":0,"type":0}],"surface":0},"7:7":{"connections":[{"index":2,"side":2,"type":0},{"index":2,"side":1,"type":0}],"surface":0},"8:0":{"connections":[{"index":0,"side":2,"type":0},{"index":2,"side":1,"type":0}],"surface":0},"8:1":{"connections":[{"index":2,"side":2,"type":0},{"index":2,"side":0,"type":0}],"surface":0},"8:2":{"connections":[{"index":0,"side":0,"type":0},{"index":2,"side":3,"type":0}],"surface":0},"8:3":{"connections":[{"index":0,"side":1,"type":0},{"index":0,"side":3,"type":0}],"surface":0},"9:0":{"connections":[{"index":0,"side":2,"type":0},{"index":2,"side":3,"type":0}],"surface":0},"9:1":{"connections":[{"index":2,"side":2,"type":0},{"index":0,"side":3,"type":0}],"surface":0},"9:2":{"connections":[{"index":0,"side":1,"type":0},{"index":2,"side":0,"type":0}],"surface":0},"9:3":{"connections":[{"index":0,"side":0,"type":0},{"index":2,"side":1,"type":0}],"surface":0}},"schema":"ald-track-atlas-preview-v1","source":"Zorro909/driving_sim_rs src/data/track_catalog.json at d8ab74c25df70b6a86ed101080e33d5fb785ea6f"} diff --git a/backend_policy.py b/backend_policy.py deleted file mode 100644 index a73fb2477afd14c21f80e040c9357d30da325b14..0000000000000000000000000000000000000000 --- a/backend_policy.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Deployment identity and fail-closed GPU selection, independent of UI labels.""" -import os -import json -import subprocess - -UPSTREAM = 'd8ab74c25df70b6a86ed101080e33d5fb785ea6f' - - -def backend_args(config, binary, scratch): - args = ['--math-profile', 'proton'] - if os.environ.get('ALD_REQUIRE_GPU') != '1': - return args, [] - if not scratch: - raise ValueError('GPU worker currently supports positive-mutation training only; replay stays on the CPU Space') - check = subprocess.run([str(binary), 'gpu-info'], capture_output=True, text=True, timeout=30) - if check.returncode: - raise RuntimeError('GPU preflight failed; CPU fallback is disabled: ' + check.stderr[-1500:]) - info=json.loads(check.stdout) - if not info.get('device') or info.get('kernels') != 'ok' or info.get('layout') != 'ok': - raise RuntimeError('GPU device and kernel acceptance missing; CPU fallback is disabled') - return args, ['--gpu'] diff --git a/batch_runner.py b/batch_runner.py deleted file mode 100644 index d650dba61535d9f2669ee2de7fb8f4a0733575f4..0000000000000000000000000000000000000000 --- a/batch_runner.py +++ /dev/null @@ -1,42 +0,0 @@ -"""Serial finite batches, frozen recipes, explicit parent lineage, no auto-resume.""" -import base64,copy,hashlib,json,threading,time,uuid -from pathlib import Path -class Batches: - def __init__(self,root,store,parse,make_job,execute): - self.root=Path(root);self.root.mkdir(exist_ok=True);self.store=store;self.parse=parse;self.make_job=make_job;self.execute=execute;self.items={};self.guard=threading.RLock() - for p in sorted(self.root.glob('*/status.json')): - b=json.loads(p.read_text());b['cancel']=threading.Event() - if b['state'] in ('queued','running'):b['state']='interrupted';self.save(b) - self.items[b['id']]=b - def public(self,b):return {k:v for k,v in b.items() if k!='cancel'} - def save(self,b): - with self.guard: - f=self.root/b['id']/'status.json';tmp=f.with_suffix('.tmp');tmp.write_text(json.dumps(self.public(b),indent=2));tmp.replace(f) - def active(self):return any(b['state'] in ('queued','running') for b in self.items.values()) - def start(self,slot,data): - template,_=self.parse(data);manifest=self.store.resolve(slot) - id='batch-'+time.strftime('%Y%m%d-%H%M%S')+'-'+uuid.uuid4().hex[:6];d=self.root/id;d.mkdir();(d/'input.evolution').write_bytes(data) - manifest['input_sha256']=hashlib.sha256(data).hexdigest();(d/'manifest.json').write_text(json.dumps(manifest,indent=2)) - b={'id':id,'name':manifest['name'],'slot':slot,'state':'queued','step':0,'total':len(manifest['steps']),'carry':manifest['carry'],'manifest':manifest,'jobs':[],'current_job':None,'cancel':threading.Event()};self.items[id]=b;self.save(b) - threading.Thread(target=self.worker,args=(b,data),daemon=True).start();return self.public(b) - def worker(self,b,original): - current=original - try: - b['state']='running';self.save(b) - for i,step in enumerate(b['manifest']['steps']): - if b['cancel'].is_set():raise InterruptedError('Batch stopped') - parent=current if b['carry'] else original - job=self.make_job(copy.deepcopy(step['config']),parent,b['id']);b['current_job']=job['id'];b['step']=i+1;b['jobs'].append({'id':job['id'],'run_slot':step['run_slot'],'input_sha256':hashlib.sha256(parent).hexdigest()});self.save(b) - if b['cancel'].is_set():job['cancel'].set() - self.execute(job) - if job['state']=='stopped' or b['cancel'].is_set():raise InterruptedError('Batch stopped') - if job['state']!='complete':raise RuntimeError(f'Run {step["run_slot"]} failed: {job.get("error",job["state"])}') - current=(Path(job['output_dir'])/'candidate.evolution').read_bytes();b['last_candidate']=str(Path(job['output_dir'])/'candidate.evolution');self.save(b) - b['state']='complete' - except InterruptedError:b['state']='stopped' - except Exception as e:b['state']='failed';b['error']=str(e) - finally:self.save(b) - def stop(self,id,jobs): - b=self.items[id];b['cancel'].set();j=jobs.get(b['current_job']) - if j:j['cancel'].set() - return self.public(b) diff --git a/bin/altd-sim b/bin/altd-sim index cf62c3b2e639bd7414ef9a7a94cbbb7c0d64fb64..d6d840cead7e0171a489cf9139961d9c7975c0b0 100644 --- a/bin/altd-sim +++ b/bin/altd-sim @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f2f2144ae14b0ef599ca8510f95712da00243c6875881491d384b394e7499f7 -size 13911800 +oid sha256:0135ecc3d435a9d327be7a92e048cfb5c5291a02ef1e0dddd6ead1cc19b31166 +size 15774792 diff --git a/bin/libaltd_gpu.so b/bin/libaltd_gpu.so index da6440318a0e1a9b3a41bee7d639a2b69a5a5e09..55ca64346f612b4cc1ec7b200e5f9ca35161caa4 100644 --- a/bin/libaltd_gpu.so +++ b/bin/libaltd_gpu.so @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:279d6353b4c691686d6aeeae6abfd4a82b959e9b604f079a6c45f9823b48a87a -size 3933424 +oid sha256:0064e747543251bab90cb966f8df00b010716a92cc75910229d42c9b24c1dac9 +size 12291032 diff --git a/client.js b/client.js index 031129b6e35f16c55f8b2200a6b40872560d9c82..0f360f2879d91cf7b6cbca52ed36575979c59b6c 100644 --- a/client.js +++ b/client.js @@ -14,6 +14,7 @@ let batchReceipt = null; const hostedStandalone = location.hostname.endsWith('.hf.space') && window.self === window.top; const say = text => { $('status').textContent = text; }; const trackLibrary=window.TrackLibrary?.mount({document,fetch,digest:sha}); +window.TrackMap?.mount({document,fetch,picker:trackLibrary}); const trackSource=()=>trackLibrary?trackLibrary.file():Promise.resolve($('track').files[0]); function buttons() { trackLibrary?.setBusy(busy || !!job); @@ -24,7 +25,7 @@ function buttons() { $('backend').disabled = busy || !!job || !capabilitiesReady; $('download').disabled = busy || !job; $('erase').disabled = busy || !job; - $('automatic').disabled = busy || !!job; + $('keepServer').disabled = busy || !!job; $('saveLocal').disabled = busy || !localResult; $('forgetLocal').disabled = busy || !localResult; $('track').disabled=busy || !!job; @@ -122,7 +123,7 @@ $('run').onclick = async () => { busy=true; buttons(); say(window.getTrainingSettings?'Processing the training run…':'Processing the bounded test…'); try { const trackInfo=trackLibrary?.info(); - const body={track:await encoded(await trackSource()),network:await encoded(networkSource()),automatic:$('automatic').checked,backend:$('backend').value}; + const body={track:await encoded(await trackSource()),network:await encoded(networkSource()),automatic:!$('keepServer').checked,backend:$('backend').value}; if(window.getTrainingSettings)body.settings=window.getTrainingSettings(); job=await submit(body);localResult=null; clearInterval(progress); @@ -153,7 +154,7 @@ $('run').onclick = async () => { $('runSummary').textContent='Elapsed: '+((performance.now()-started)/1000).toFixed(1)+' s\nNo downloadable result was received. A connection error does not prove the server stopped.'; say(error.message); } - finally {clearInterval(progress);busy=false; buttons();} + finally {clearInterval(progress);busy=false;$('keepServer').checked=false;buttons();} }; $('download').onclick = async () => { busy=true; buttons(); @@ -163,7 +164,7 @@ $('download').onclick = async () => { localResult=new Blob([bytes]); saveLocal(); const acknowledgment=await acknowledge(job,receipt); if (acknowledgment.deleted) { $('runState').textContent='COMPLETE — result received; server copy deleted'; job=null; say('Result received by browser. Server result deleted. Save your local download; server retrieval is no longer available.'); } - else say('Result received by browser. Verify your saved file, then select Delete now; repeat downloads remain available until expiry.'); + else say('Result received by browser. The server copy is available for another download until its five-minute deadline; use Delete now to remove it sooner.'); } catch (error) { say(error.message+' If receipt acknowledgment failed, use Delete now; automatic expiry still applies.'); } finally {busy=false; buttons();} }; @@ -251,7 +252,9 @@ async function connect(){ if(capabilities.cpu!==true)throw new Error('CPU unavailable'); gpuTransport=capabilities.gpu_transport??null; $('gpuOption').disabled=!capabilities.gpu; - $('backendNote').textContent=capabilities.gpu?(hostedStandalone?'CPU uses no GPU allowance. Optional HF sign-in identifies account GPU requests; without sign-in, direct requests may use the limited anonymous allowance. No automatic switching.':'CPU uses no GPU allowance. GPU is subject to HF quota through the official client; optional HF sign-in explicitly identifies the caller. No automatic switching.'):'CPU available. GPU is not connected on this deployment; no GPU allowance is needed for CPU.'; + $('backendNote').textContent=capabilities.gpu?(hostedStandalone?'CPU is anonymous. GPU may use a limited anonymous allowance or your optional HF account allowance.':'CPU is anonymous. GPU requires an available HF allowance.'):'CPU training is ready; no sign-in or GPU allowance needed.'; + $('gpuAccount').hidden=!capabilities.gpu; + $('computeBlock').hidden=!capabilities.gpu; capabilitiesReady=true; buttons(); say('Connected. Choose your track and network, then run the test.'); } catch(error) { diff --git a/cloud-contract.json b/cloud-contract.json deleted file mode 100644 index 1c480f1696197ed66a931c0c32ac4437f93d4dcd..0000000000000000000000000000000000000000 --- a/cloud-contract.json +++ /dev/null @@ -1 +0,0 @@ -{"sensors": [{"$type": "raycast", "Length": 200.00003, "Degrees": -90}, {"$type": "raycast", "Length": 719.61523, "Degrees": -30}, {"$type": "raycast", "Length": 800, "Degrees": 0}, {"$type": "raycast", "Length": 719.61523, "Degrees": 30}, {"$type": "raycast", "Length": 200.00003, "Degrees": 90}, {"$type": "speed"}]} \ No newline at end of file diff --git a/cloud_core.py b/cloud_core.py deleted file mode 100644 index 0e58b6a6aa189acc89abebc93577201d6c1268c1..0000000000000000000000000000000000000000 --- a/cloud_core.py +++ /dev/null @@ -1,5 +0,0 @@ -import json -from pathlib import Path -ENTRY=json.loads((Path(__file__).parent/'cloud-contract.json').read_text()) -def network(v): - return {'weights':[[v[i*4:(i+1)*4] for i in range(6)],[v[28+i*2:28+(i+1)*2] for i in range(4)]],'biases':[v[24:28],v[36:38]]} diff --git a/cloud_probe.py b/cloud_probe.py deleted file mode 100644 index 5a7ec1338e52b988e419034c8499dff0a30a1bae..0000000000000000000000000000000000000000 --- a/cloud_probe.py +++ /dev/null @@ -1,63 +0,0 @@ -"""Fixed synthetic CPU/CUDA smoke test; no uploads or retained model artifacts.""" -import gzip -import hashlib -import json -import os -from pathlib import Path -import subprocess -import tempfile -import time - -ROOT = Path(__file__).resolve().parent - -def run_probe(gpu=False): - from probe_core import verify_artifacts, qualify - verify_artifacts() - qualification = qualify() if gpu else None - if gpu and not qualification['qualified_for_next_test']: - return {'status':'qualification_failed','qualification':qualification} - binary=ROOT/'bin/altd-sim' - binary.chmod(0o755) - network={'shape':[1,2],'weights':[[[0.0,0.0]]],'biases':[[0.0,1.0]], - 'inputs':['speed'],'outputs':['Steering','Acceleration']} - model={'sensor_layout':{'names':['speed'],'sensors':[{'$type':'speed'}]}} - settings={'population':4,'selection_algorithm':'best','selection_size':1, - 'crossover':'none','mutation_rate':0.01,'adaptive_mutation':False,'weight_decay':0, - 'preserve_parents':'on_custom','preserve_parents_size':1, - 'rewards':[{'metric':'total_score','type':'default','weight':1}]} - report={'backend_requested':'cuda' if gpu else 'cpu','population':4,'generations':1, - 'ticks':60,'seed':42,'qualification':qualification,'speedup_verified':False} - with tempfile.TemporaryDirectory(prefix='tiga-probe-') as directory: - work=Path(directory) - for name,value in [('network',network),('model',model),('settings',settings)]: - (work/(name+'.json')).write_text(json.dumps(value)) - command=[str(binary),'--math-profile','proton','--threads','1','train-scratch'] - if gpu: command+=['--gpu'] - command+=['--scene',str(ROOT/'tracks/A01 Beginner.scene.json'), - '--spawn-trace',str(ROOT/'tracks/A01 Beginner.spawn.json'), - '--network',str(work/'network.json'),'--init-network',str(work/'network.json'), - '--model',str(work/'model.json'),'--settings',str(work/'settings.json'), - '--shape','1,2','--population','4','--generations','1','--ticks','60', - '--seed','42','--batch-count','2','--out-dir',str(work/'out'), - '--mutation-start','.01','--mutation-end','.01','--schedule','linear','--save-final-candidate'] - # Preserve only native-loader/device plumbing, never provider credentials. - keys=('PATH','LD_LIBRARY_PATH','LD_PRELOAD','CUDA_VISIBLE_DEVICES','NVIDIA_VISIBLE_DEVICES') - env={k:os.environ[k] for k in keys if k in os.environ} - env.update(HOME=directory,TMPDIR=directory,ALTD_GPU_LIB=str(ROOT/'bin/libaltd_gpu.so')) - started=time.perf_counter() - try: - result=subprocess.run(command,cwd=work,env=env,capture_output=True,text=True,timeout=30) - report.update(returncode=result.returncode,wall_seconds=time.perf_counter()-started, - stderr=result.stderr[-4000:],stdout=result.stdout[-4000:]) - candidate=work/'out/candidate.json' - report['status']='failed' - if result.returncode==0 and candidate.is_file(): - value=json.loads(candidate.read_text()) - report.update(status='candidate_created',shape=value['shape'], - candidate_sha256=hashlib.sha256(candidate.read_bytes()).hexdigest(), - history=[json.loads(line) for line in (work/'out/log.jsonl').read_text().splitlines()]) - except subprocess.TimeoutExpired: - report.update(status='timeout',wall_seconds=time.perf_counter()-started) - report['temporary_directory_removed']=not work.exists() - report['candidate_retained']=False - return report diff --git a/delivery.py b/delivery.py index bf2b8ae7ac2ca9e053e299b0eeeb2f901da67dc9..333384418378fb504510a494e2d1b68661750b81 100644 --- a/delivery.py +++ b/delivery.py @@ -50,9 +50,9 @@ class ResultStore: if len(self.jobs)>=16 or self.retained_bytes+len(data)>self.max_bytes: raise CapacityExceeded() ident, key = secrets.token_urlsafe(24), secrets.token_urlsafe(32) - expires = self.wall_clock()+1800 + expires = self.wall_clock()+300 self.jobs[ident] = dict(key=key, data=bytearray(data), automatic=automatic, - deadline=self.clock()+1800, expires_at=expires, delivered=False, receipt=None) + deadline=self.clock()+300, expires_at=expires, delivered=False, receipt=None) return dict(id=ident,key=key,expires_at=expires,automatic=automatic, sha256=hashlib.sha256(data).hexdigest(),size=len(data)) diff --git a/demo.evolution b/demo.evolution deleted file mode 100644 index 9baf45b790f151419fbaad7adec8c63476cf929a..0000000000000000000000000000000000000000 Binary files a/demo.evolution and /dev/null differ diff --git a/github-simulator/source/Cargo.lock b/github-simulator/source/Cargo.lock deleted file mode 100644 index 95beadb8a3daa1a2b1cdda3da40ea04b6490d84d..0000000000000000000000000000000000000000 --- a/github-simulator/source/Cargo.lock +++ /dev/null @@ -1,792 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "altd_sim" -version = "1.1.0" -dependencies = [ - "clap", - "console_error_panic_hook", - "js-sys", - "libc", - "libloading", - "rayon", - "serde", - "serde_json", - "sha2", - "tungstenite", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-bindgen-rayon", - "web-sys", - "windows-sys", -] - -[[package]] -name = "anstream" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" - -[[package]] -name = "anstyle-parse" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" -dependencies = [ - "anstyle", - "once_cell_polyfill", - "windows-sys", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-buffer" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" -dependencies = [ - "hybrid-array", -] - -[[package]] -name = "bumpalo" -version = "3.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" - -[[package]] -name = "bytes" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" - -[[package]] -name = "cfg-if" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" - -[[package]] -name = "chacha20" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" -dependencies = [ - "cfg-if", - "cpufeatures 0.3.1", - "rand_core", -] - -[[package]] -name = "clap" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa8876b300ab35ba921adea3dfd70157a46249b33f95c9084ae5709785478946" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0797fb7aeb1406c84efac526901f7ec3ead2124f946b494e72879d4b54704d" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9c751b79415d4e559e3d1fcf128e09e720eb673a06d26cf6f392d37d75b66e0" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c133bc6a41be0d194c306b5506d15e6feeea7b1d6604bd3f8310dfb2ca96486" - -[[package]] -name = "colorchoice" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" - -[[package]] -name = "console_error_panic_hook" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] - -[[package]] -name = "const-oid" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" - -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "cpufeatures" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" -dependencies = [ - "libc", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98b0cc327b5bc766e7fda9c9260cc0fa81b43a8e240440422dff70788e3f9ef1" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622f3fc73690be383c7214310406f28a90e6edeadc3cea882f9d71e495b9711a" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6" - -[[package]] -name = "crypto-common" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "crypto-common" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" -dependencies = [ - "hybrid-array", -] - -[[package]] -name = "data-encoding" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer 0.10.4", - "crypto-common 0.1.7", -] - -[[package]] -name = "digest" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" -dependencies = [ - "block-buffer 0.12.1", - "const-oid", - "crypto-common 0.2.2", -] - -[[package]] -name = "either" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "futures-core" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" - -[[package]] -name = "futures-task" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" - -[[package]] -name = "futures-util" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" -dependencies = [ - "futures-core", - "futures-task", - "pin-project-lite", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "rand_core", -] - -[[package]] -name = "hashbrown" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "http" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" -dependencies = [ - "bytes", - "itoa", -] - -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "hybrid-array" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27f864f10dfb56725ce5ce5472bc52252c8f93a4ab86327122cebf62c5f59a17" -dependencies = [ - "typenum", -] - -[[package]] -name = "indexmap" -version = "2.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" -dependencies = [ - "equivalent", - "hashbrown", -] - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "js-sys" -version = "0.3.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5" -dependencies = [ - "cfg-if", - "futures-util", - "wasm-bindgen", -] - -[[package]] -name = "libc" -version = "0.2.189" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" - -[[package]] -name = "libloading" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" -dependencies = [ - "cfg-if", - "windows-link", -] - -[[package]] -name = "log" -version = "0.4.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" - -[[package]] -name = "memchr" -version = "2.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "once_cell_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" - -[[package]] -name = "pin-project-lite" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" - -[[package]] -name = "proc-macro2" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" - -[[package]] -name = "rand" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65c9fb96cbc91e3478eaae79a69fcd3f1ae4ad052e471fe6732fff548984b4af" -dependencies = [ - "chacha20", - "getrandom", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" - -[[package]] -name = "rayon" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" -dependencies = [ - "either", - "rayon-core", - "wasm_sync", -] - -[[package]] -name = "rayon-core" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", - "wasm_sync", -] - -[[package]] -name = "rustversion" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" - -[[package]] -name = "serde" -version = "1.0.229" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.229" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.229" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.151" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" -dependencies = [ - "indexmap", - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "sha1" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" -dependencies = [ - "cfg-if", - "cpufeatures 0.3.1", - "digest 0.11.3", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", - "digest 0.10.7", -] - -[[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "syn" -version = "3.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "thiserror" -version = "2.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio" -version = "1.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" -dependencies = [ - "pin-project-lite", -] - -[[package]] -name = "tungstenite" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48ac77174b19c110a50ab2128b24215ac9cb40e0e12e093fb602d175c569d22" -dependencies = [ - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand", - "sha1", - "thiserror", -] - -[[package]] -name = "typenum" -version = "1.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" - -[[package]] -name = "unicode-ident" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "wasm-bindgen" -version = "0.2.129" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cbab34de2d982e9b48e18d216d04c4a6f641066ff19ffb699980f591ee3610e" -dependencies = [ - "js-sys", - "tokio", - "wasm-bindgen", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.129" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.129" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-rayon" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a16c60a56c81e4dc3b9c43d76ba5633e1c0278211d59a9cb07d61b6cd1c6583" -dependencies = [ - "crossbeam-channel", - "js-sys", - "rayon", - "wasm-bindgen", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.129" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "wasm_sync" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff360cade7fec41ff0e9d2cda57fe58258c5f16def0e21302394659e6bbb0ea" -dependencies = [ - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "web-sys" -version = "0.3.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88261b9deccee56594c11a3460c462c41f58d148598fe70ad77070126a68aba4" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "zmij" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/github-simulator/source/Cargo.toml b/github-simulator/source/Cargo.toml deleted file mode 100644 index 2fdf412d02b017abce90017a3c0246010f791d0b..0000000000000000000000000000000000000000 --- a/github-simulator/source/Cargo.toml +++ /dev/null @@ -1,77 +0,0 @@ -[package] -name = "altd_sim" -version = "1.1.0" -edition = "2021" -description = "CPU game-fidelity simulator and trainer for AI Learns To Drive" -license = "MIT" -repository = "https://github.com/Zorro909/driving_sim_rs" -readme = "README.md" -keywords = ["simulation", "driving", "training", "neural-network", "wasm"] -categories = ["simulation", "science", "command-line-utilities"] -rust-version = "1.93" -exclude = ["tests/fixtures/", "wasm/test/"] - -[lib] -# Keep native test artifacts distinct. wasm/build.sh selects cdylib explicitly. -crate-type = ["rlib"] - -[features] -default = ["cli", "server"] -# The `altd-sim` command line (native only). -cli = ["dep:clap"] -# `altd-sim serve`: the WebSocket server Drive Lab can train on (native only). -server = ["cli", "dep:tungstenite"] -# The WebAssembly library in src/wasm: JavaScript bindings and the WebGPU raycaster. -wasm = ["dep:wasm-bindgen", "dep:js-sys", "dep:wasm-bindgen-futures", "dep:web-sys", "dep:console_error_panic_hook"] -wasm-threads = ["wasm", "dep:wasm-bindgen-rayon"] -# Browser integration diagnostics, excluded from distributed packages. -wasm-thread-test = ["wasm"] - -[dependencies] -clap = { version = "4", features = ["derive"], optional = true } -rayon = "1" -serde = { version = "1", features = ["derive"] } -# float_roundtrip parses JSON floats exactly like Python's float(). -serde_json = { version = "1", features = ["float_roundtrip", "preserve_order"] } -wasm-bindgen = { version = "0.2", optional = true } -js-sys = { version = "0.3", optional = true } -wasm-bindgen-futures = { version = "0.4", optional = true } -console_error_panic_hook = { version = "0.1", optional = true } -web-sys = { version = "0.3", optional = true, features = [ - "Window", "Navigator", "WorkerGlobalScope", "WorkerNavigator", - "Gpu", "GpuAdapter", "GpuDevice", "GpuQueue", - "GpuBuffer", "GpuBufferDescriptor", "GpuBufferBinding", - "GpuShaderModule", "GpuShaderModuleDescriptor", "GpuCompilationInfo", "GpuCompilationMessage", "GpuCompilationMessageType", - "GpuComputePipeline", "GpuComputePipelineDescriptor", "GpuProgrammableStage", "GpuPipelineLayout", - "GpuBindGroup", "GpuBindGroupDescriptor", "GpuBindGroupEntry", "GpuBindGroupLayout", - "GpuCommandEncoder", "GpuComputePassEncoder", "GpuCommandBuffer", - "gpu_buffer_usage", "gpu_map_mode", -] } - -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] -# Runtime loading of the HIP simulator (src/gpu/hip.rs). -libloading = "0.9" -sha2 = "0.10" -# Synchronous WebSockets for `altd-sim serve`, without TLS. -tungstenite = { version = "0.30", default-features = false, features = ["handshake"], optional = true } - -# Process statistics in the CLI (src/bin/altd-sim/platform.rs). -[target.'cfg(unix)'.dependencies] -libc = "0.2" - -[target.'cfg(windows)'.dependencies] -windows-sys = { version = "0.61", features = ["Wdk_System_SystemServices", "Win32_Foundation", "Win32_System_ProcessStatus", "Win32_System_SystemInformation", "Win32_System_Threading"] } - -[target.'cfg(target_arch = "wasm32")'.dependencies] -wasm-bindgen-rayon = { version = "=1.3.0", optional = true, features = ["no-bundler"] } - -[profile.release] -codegen-units = 1 -lto = "fat" -panic = "abort" -debug = "line-tables-only" - -[[bin]] -name = "altd-sim" -path = "src/bin/altd-sim/main.rs" -required-features = ["cli"] diff --git a/github-simulator/source/GODOT_LICENSE b/github-simulator/source/GODOT_LICENSE deleted file mode 100644 index 5c16da20885052753b65be6a1790efc77ec0e9fd..0000000000000000000000000000000000000000 --- a/github-simulator/source/GODOT_LICENSE +++ /dev/null @@ -1,43 +0,0 @@ -Godot Engine source adaptations in src/physics/broadphase.rs - -Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). -Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Modified tree-balancing algorithm, originally Randy Gaul qu3e: -/** - * @file q3DynamicAABBTree.h - * @author Randy Gaul - * @date 10/10/2014 - * Copyright (c) 2014 Randy Gaul http://www.randygaul.net - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not - * be misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - diff --git a/github-simulator/source/LICENSE b/github-simulator/source/LICENSE deleted file mode 100644 index f2f0c87fc193019801b4184387e475ccda4c9412..0000000000000000000000000000000000000000 --- a/github-simulator/source/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2026 Zorro909 - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/github-simulator/source/README.md b/github-simulator/source/README.md deleted file mode 100644 index 9e9cdc31b3a0100371771839f52953a5b0b3ed05..0000000000000000000000000000000000000000 --- a/github-simulator/source/README.md +++ /dev/null @@ -1,183 +0,0 @@ -# altd-sim - -[![CI](https://github.com/Zorro909/driving_sim_rs/actions/workflows/ci.yml/badge.svg)](https://github.com/Zorro909/driving_sim_rs/actions/workflows/ci.yml) - -`altd-sim` is a Rust vehicle simulator and neural-network trainer for AI Learns To Drive. It runs headless on the CPU, through a runtime-loaded AMD HIP library, or in WebAssembly. It reproduces the game's floating-point arithmetic, contact ordering, sensors and evolution behavior against captured regression fixtures. Exactness claims apply to the tested inputs; [fidelity](docs/fidelity.md) records the limits. - -The repository includes vehicle and sensor templates, tile resources, fixed generated tracks and self-contained tests. Campaign tracks are not distributed. - -## Getting started - -This guide sets up `altd-sim serve`, the local simulator for [Drive Lab](https://drivinglab.jectrum.de). Drive Lab keeps your tracks, runs and downloads in the browser; the server drives the cars on your CPU threads or an AMD GPU. That removes the browser's limits on cars and network size and its eight-thread cap. - -### 1. Download - -Download the archive for your system from [Releases](https://github.com/Zorro909/driving_sim_rs/releases). Tagged versions are stable; the `nightly` pre-release is rebuilt from every push to `main` and keeps the same file names. `SHA256SUMS` lists the archive checksums. - -| System | Archive | Training on | -| --- | --- | --- | -| Linux x86_64 | `altd-sim--x86_64-unknown-linux-gnu.tar.gz` | CPU, or AMD GPU through HIP | -| Windows x86_64 | `altd-sim--x86_64-pc-windows-msvc.zip` | CPU | -| macOS, Apple silicon | `altd-sim--aarch64-apple-darwin.tar.gz` | CPU | - -Unpack it and open a terminal in the unpacked folder: - -```sh -tar -xzf altd-sim-nightly-x86_64-unknown-linux-gnu.tar.gz -cd altd-sim-nightly-x86_64-unknown-linux-gnu -./altd-sim --version -``` - -On Windows, extract the `.zip`, then open PowerShell in the folder and use `.\altd-sim.exe` wherever this guide says `./altd-sim`. macOS binaries are unsigned, so first remove the download quarantine with `xattr -d com.apple.quarantine altd-sim`. Apple silicon uses the portable trigonometry described in [fidelity](docs/fidelity.md). - -### 2. Start the server - -```sh -./altd-sim serve -``` - -```text -altd-sim serve: listening on ws://127.0.0.1:47800/v1 - allowed origins: https://drivinglab.jectrum.de - CPU threads: 16 - HIP: available (AMD Radeon RX 7900 XTX) -``` - -Keep the terminal open while you train; Ctrl+C stops the server. It listens only on `127.0.0.1`, so the browser must run on the same computer. - -### 3. Connect Drive Lab - -1. In Drive Lab, open **New run**. Under **Compute**, enable **Use a local altd-sim**. The port field defaults to 47800. -2. If the browser asks to allow access to devices on your local network, allow it. -3. The status shows the connection, the CPU thread count and the GPU, if HIP is available. Choose **Native CPU** or **Native GPU (HIP)** and start the run. - -Each browser connection gets its own session. Only one session at a time can use the GPU; another one asking for HIP trains on CPU and says why. If the server stops during training, the run stops and keeps its last saved generation. Start the server again and resume the run. - -### Options - -| Option | Use it to | -| --- | --- | -| `--threads N` | Limit the CPU threads, for example to keep the computer responsive. The default is every logical CPU the process may use. | -| `--math-profile PROFILE` | Reproduce `proton`, `win10-fma3` or `win11-fma3` arithmetic. The default follows the server's operating system; clients can select a profile for each session. | -| `--port N` | Listen on another port when 47800 is taken. Set the same port in Drive Lab. | -| `--allow-origin ORIGIN` | Allow another Drive Lab address, such as a development copy: `--allow-origin http://127.0.0.1:5173`. It replaces the default; repeat the option to allow several. | - -```sh -./altd-sim serve --threads 8 --port 47801 -``` - -All builds include all three math profiles. Windows 11 24H2 and newer default to `win11-fma3`; earlier Windows releases use `win10-fma3`; Linux and macOS use `proton`. To train on Linux for a game running on current Windows, for example, pass `--math-profile win11-fma3`. Checkpoints retain their profile. [Math profiles](math/README.md) describes the kernels and captured reference data. - -### Training on an AMD GPU - -The Linux archive contains `libaltd_gpu.so` next to `altd-sim`. It needs a ROCm 7.x runtime and one of the [covered GPU architectures](gpu/README.md). `./altd-sim gpu-info` checks that the library loads and prints `"layout": "ok"`; with a GPU present it also runs one kernel and prints the device, and fails if the library has no code for that GPU; the server's `HIP:` line shows the device or why HIP is unavailable. - -A HIP session drives its first 12 ticks on both the GPU and the CPU and compares them. If they differ, or the network or track needs something HIP does not support, the session trains on CPU and Drive Lab shows the reason. Native CPU and HIP produce the same results. Browser training can differ from both in the last digits of a few values ([issue #3](https://github.com/Zorro909/driving_sim_rs/issues/3)), so a run moved between the browser and the server can develop differently from then on. - -NVIDIA GPUs use the same backend: build the library with `gpu/build-cuda.sh` (Linux) or `gpu/build-cuda.ps1` (Windows) as described in the [GPU guide](gpu/README.md#nvidia-cuda). The release archives do not include it. - -### Troubleshooting - -- **`cannot listen on 127.0.0.1:47800: Address already in use`**: another server is already running, or another program uses the port. Stop it or choose another `--port`. -- **Drive Lab cannot connect**: check that the server is running and that both use the same port. A Drive Lab address other than `https://drivinglab.jectrum.de` must be allowed with `--allow-origin`, including its scheme and port. If you denied local network access, allow it again in the browser's site settings. -- **`HIP: unavailable`**: the line gives the reason. `./altd-sim gpu-info` reports library problems, and [GPU setup](gpu/README.md) covers ROCm and other GPU architectures. - -[Server documentation](docs/server.md) describes the protocol and its security checks for other clients. - -### Training without Drive Lab - -The CLI can also train on its own. This example trains Rally networks on generated tracks and writes them to `runs/example`: - -```sh -./altd-sim train-scratch --scene assets/scenes/rally_template.json --track-mode random \ - --random-track-settings assets/random_track_settings.json \ - --population 256 --generations 3 --ticks 600 --out-dir runs/example -``` - -Add `--gpu` to train on an AMD GPU. `./altd-sim --help` lists every command, and the [CLI reference](docs/cli.md) describes their inputs and outputs. - -## Build from source - -Rust 1.94 or newer is the tested native toolchain. Build from the repository root. `--offline` works when dependencies are already cached; omit it on the first build if needed. - -```sh -cargo build --release --offline -python3 tools/benchmark.py --population 1024 --ticks 600 --repeats 5 \ - --report target/benchmark.json - -target/release/altd-sim --threads 8 train-scratch \ - --scene assets/scenes/rally_template.json --track-mode random \ - --random-track-settings assets/random_track_settings.json \ - --population 256 --generations 3 --ticks 600 --seed 1729 \ - --eliminate-on-wall --idle-eliminate --save-final-candidate \ - --out-dir runs/example -``` - -The training command starts Xavier networks and generates a reproducible track each generation. Increase the population, generations and tick limit for longer runs. Supply the same scene and track settings when resuming, with `--resume` and a higher `--generations` target. - -`.cargo/config.toml` uses `target-cpu=native` for x86 local builds, including the published CPU measurements. For a portable x86_64 binary, override it explicitly: - -```sh -RUSTFLAGS='-C target-cpu=x86-64' cargo build --release --offline -``` - -AVX2 network and geometry paths use runtime detection and retain scalar fallbacks. Exact native trigonometry uses x87 on x86; other targets use the portable implementation described in the fidelity notes. - -## Other backends - -AMD GPU builds need ROCm and `hipcc`. The default architecture is `gfx1100`, for the RX 7900 XTX. - -```sh -gpu/build.sh -# Add --gpu to the train-scratch command above. -``` - -For other AMD GPUs, set `GPU_ARCH` to one or more architectures when building. Rust builds do not require ROCm. See [GPU setup](gpu/README.md) for validation and profiling. - -The default build also includes a local WebSocket server for native CPU or HIP sessions: - -```sh -target/release/altd-sim --threads 8 serve -``` - -[Getting started](#getting-started) explains its options and how to connect Drive Lab; [server documentation](docs/server.md) covers the protocol, security checks and checkpoint resume. - -WebAssembly builds need the wasm32 target and the matching `wasm-bindgen` CLI: - -```sh -rustup target add wasm32-unknown-unknown -cargo install wasm-bindgen-cli --version "$(wasm/bindgen-version.sh)" --locked -wasm/build.sh -``` - -The JavaScript library includes CPU simulation, a threaded CPU package and optional WebGPU backends. See [WASM setup](wasm/README.md) for initialization, browser requirements and tests. Expanded native/WASM comparisons have known last-bit differences; serial and threaded WASM are compared separately. - -## CLI and output - -`target/release/altd-sim --help` and `target/release/altd-sim COMMAND --help` show arguments. `bench` measures a fixed population; `train` evolves an exported network; `train-scratch` starts Xavier networks and supports checkpoints, generated-track batches and stop conditions; `evaluate` runs a frozen candidate against a hash-validated suite; `serve` exposes sessions over a loopback WebSocket. The `compare-*` commands compare user-supplied captures with the simulator. Scenes and recorded traces are explicit inputs. [CLI reference](docs/cli.md) covers defaults, resume behavior and report formats. - -A `train-scratch` run writes: - -| File | Contents | -| --- | --- | -| `run.json`, `log.jsonl`, `progress.json` | Effective options, completed generations and current progress | -| `checkpoint.json`, `checkpoint_gNNNNN.bin` | Generation-boundary population and RNG state | -| `best.json`, `best_laps/` | Networks that set a new best lap | -| `candidate.json` | Final fitness leader when `--save-final-candidate` is enabled | -| `tracks/` | Generated track geometry for replay | - -## Code and tests - -`src/math/` contains runtime-compatible arithmetic and vectors. `src/physics/` implements vehicles and contacts, while `src/track/` owns scene geometry, spatial queries and generated tracks. `src/nn/` provides scalar and AVX2 inference. `src/training/` owns populations, evolution, RNGs, evaluation and embedding sessions. `src/gpu/hip.rs` loads HIP; `src/gpu/simulation.rs` shares device layouts with `src/wasm/`, which supplies JavaScript and WebGPU bindings. The CLI entrypoint is `src/bin/altd-sim/main.rs`, beside its argument, comparison, benchmark and checkpoint modules. Rust imports follow these groups, such as `altd_sim::physics::car::Car` and `altd_sim::training::session::Session`. See [design notes](docs/design.md). - -```sh -cargo test --release --offline -cargo test --offline -cargo build --offline --target wasm32-unknown-unknown --no-default-features --features wasm -``` - -Tests retain self-contained native captures and label generated golden data as current-code regressions. Browser integration checks live in `wasm/test/`; HIP equivalence checks use `gpu_check`. The exhaustive trigonometry scan and hardware-dependent tests are opt-in. See [performance](docs/performance.md), [GPU internals](docs/gpu.md) and [WASM fidelity](docs/wasm.md). - -GitHub Actions checks formatting, native and WASM Clippy, native release and debug tests, Windows and macOS Clippy and release tests, the HIP library build and layout, rustdoc, Rust 1.93 compatibility, and WASM packages with CPU browser comparisons. The release workflow builds and smoke-tests the archives, then publishes tags and nightly builds. Its native jobs override `target-cpu=native` with `x86-64` and include the flags in their cache keys, so cached builds can move between runner CPUs. Serial/threaded browser tests retain the documented expanded native differences. HIP equivalence and hardware WebGPU checks run separately because the hosted runners have no GPU. - -The project uses the [MIT license](LICENSE). Godot-derived code retains the [Godot notice](GODOT_LICENSE), and numerical modules retain their source notices. diff --git a/github-simulator/source/assets/models/formula.json b/github-simulator/source/assets/models/formula.json deleted file mode 100644 index 4bc2fce7b0d8074ccf12ba872575decc45a5da94..0000000000000000000000000000000000000000 --- a/github-simulator/source/assets/models/formula.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "vehicle_type": "formula", - "vision": [ - { - "angle": -135, - "length": 624.2640380859375 - }, - { - "angle": -90, - "length": 200.00003051757812 - }, - { - "angle": -60, - "length": 499.9999694824219 - }, - { - "angle": -35, - "length": 691.4912109375 - }, - { - "angle": -20, - "length": 763.8155517578125 - }, - { - "angle": -10, - "length": 790.8846435546875 - }, - { - "angle": 0, - "length": 800 - }, - { - "angle": 10, - "length": 790.8846435546875 - }, - { - "angle": 20, - "length": 763.8155517578125 - }, - { - "angle": 35, - "length": 691.4912109375 - }, - { - "angle": 60, - "length": 499.9999694824219 - }, - { - "angle": 90, - "length": 200.00003051757812 - }, - { - "angle": 135, - "length": 624.2640380859375 - } - ], - "sensors": [ - "angular_velocity", - "boost_capacity", - "wheel_angle", - "grip", - "velocity_front", - "velocity_side", - "track_curvature" - ], - "outputs": [ - "steering", - "acceleration", - "handbrake", - "brake", - "boost" - ] -} diff --git a/github-simulator/source/assets/models/rally.json b/github-simulator/source/assets/models/rally.json deleted file mode 100644 index fe3d4a23fd2bfeffd9b93bedc16fd7d5c4ee0f81..0000000000000000000000000000000000000000 --- a/github-simulator/source/assets/models/rally.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "vehicle_type": "rally", - "vision": [ - { - "angle": -135, - "length": 624.2640380859375 - }, - { - "angle": -90, - "length": 200.00003051757812 - }, - { - "angle": -60, - "length": 499.9999694824219 - }, - { - "angle": -35, - "length": 691.4912109375 - }, - { - "angle": -20, - "length": 763.8155517578125 - }, - { - "angle": -10, - "length": 790.8846435546875 - }, - { - "angle": 0, - "length": 800 - }, - { - "angle": 10, - "length": 790.8846435546875 - }, - { - "angle": 20, - "length": 763.8155517578125 - }, - { - "angle": 35, - "length": 691.4912109375 - }, - { - "angle": 60, - "length": 499.9999694824219 - }, - { - "angle": 90, - "length": 200.00003051757812 - }, - { - "angle": 135, - "length": 624.2640380859375 - } - ], - "sensors": [ - "angular_velocity", - "boost_capacity", - "correct_direction", - "grip", - "velocity_front", - "velocity_side", - "track_curvature" - ], - "outputs": [ - "steering", - "acceleration", - "handbrake", - "brake", - "boost" - ], - "fidelity_provenance": "Sensor lengths retain the captured single-precision values." -} diff --git a/github-simulator/source/assets/networks/formula.json b/github-simulator/source/assets/networks/formula.json deleted file mode 100644 index e50819157b43abd77417e2e7bdffa2d199733c9b..0000000000000000000000000000000000000000 --- a/github-simulator/source/assets/networks/formula.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "inputs": [ - "↑ -135°", - "↑ -90°", - "↑ -60°", - "↑ -35°", - "↑ -20°", - "↑ -10°", - "↑ 0°", - "↑ 10°", - "↑ 20°", - "↑ 35°", - "↑ 60°", - "↑ 90°", - "↑ 135°", - "AngVel", - "Boost", - "Wheel", - "Grip", - "VelF", - "VelS", - "Curve" - ], - "outputs": [ - "Steering", - "Acceleration", - "Handbrake", - "Brake", - "Boost" - ] -} diff --git a/github-simulator/source/assets/networks/rally.json b/github-simulator/source/assets/networks/rally.json deleted file mode 100644 index 04e47ee2568eb00151e81a2196459cc0de6366f0..0000000000000000000000000000000000000000 --- a/github-simulator/source/assets/networks/rally.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "inputs": [ - "↑ -135°", - "↑ -90°", - "↑ -60°", - "↑ -35°", - "↑ -20°", - "↑ -10°", - "↑ 0°", - "↑ 10°", - "↑ 20°", - "↑ 35°", - "↑ 60°", - "↑ 90°", - "↑ 135°", - "AngVel", - "Boost", - "Dir", - "Grip", - "VelF", - "VelS", - "Curve" - ], - "outputs": [ - "Steering", - "Acceleration", - "Handbrake", - "Brake", - "Boost" - ] -} diff --git a/github-simulator/source/assets/random_track_settings.json b/github-simulator/source/assets/random_track_settings.json deleted file mode 100644 index 93e40c9d68c13f345fa4659d4be5f9ab047a813b..0000000000000000000000000000000000000000 --- a/github-simulator/source/assets/random_track_settings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "length": { "min": 12, "max": 40 }, - "allow_double": [false, true], - "surfaces": { "pool": [0, 1, 2], "count": [1, 2, 3] }, - "distribution": [0, 1], - "start": null -} diff --git a/github-simulator/source/assets/scenes/formula_template.json b/github-simulator/source/assets/scenes/formula_template.json deleted file mode 100644 index 9470c003c3a08f274e89526c27574f3cd20c93f5..0000000000000000000000000000000000000000 --- a/github-simulator/source/assets/scenes/formula_template.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "vehicle": { - "type": "Formula", - "mass": 2, - "inertia_property": 0, - "inertia_server": 1038.1666259765625, - "shape_size": [ - 73, - 30 - ], - "shape_position": [ - 0, - 0 - ], - "shape_rotation": 1.5708, - "gravity_scale": 1, - "linear_damp": 0, - "angular_damp": 0, - "max_contacts_reported": 4, - "friction": 0.95, - "bounce": 0.1, - "grip": 2, - "steering_speed": 0.4000000059604645, - "center_steering": true, - "air_resistance": 0.10000000149011612, - "max_velocity": 1200, - "surfaces": { - "asphalt": { - "grip": 1, - "power": 1, - "steering": 1 - }, - "dirt": { - "grip": 0.8, - "power": 0.5, - "steering": 1 - }, - "ice": { - "grip": 0.3, - "power": 0.5, - "steering": 1 - } - }, - "wheels": [ - { - "position": [ - -11, - -17 - ], - "steering": true, - "max_angle_deg": 35, - "power": 0, - "brake_power": 3.5, - "handbrake_power": 0 - }, - { - "position": [ - 11, - -17 - ], - "steering": true, - "max_angle_deg": 35, - "power": 0, - "brake_power": 3.5, - "handbrake_power": 0 - }, - { - "position": [ - -13, - 29 - ], - "steering": false, - "max_angle_deg": 0, - "power": 6, - "brake_power": 3, - "handbrake_power": 3 - }, - { - "position": [ - 13, - 29 - ], - "steering": false, - "max_angle_deg": 0, - "power": 6, - "brake_power": 3, - "handbrake_power": 3 - } - ] - }, - "physics": { - "real_precision": "single", - "contact_recycle_radius": 1, - "contact_max_separation": 1.5, - "contact_max_allowed_penetration": 0.3, - "contact_default_bias": 0.8, - "solver_iterations": 16 - }, - "track": { - "name": "Generated track template", - "tiles": [], - "path": [], - "walls": [], - "physics_shapes": [] - }, - "reset_position": [ - 0, - 0 - ], - "reset_rotation": 0 -} diff --git a/github-simulator/source/assets/scenes/rally_template.json b/github-simulator/source/assets/scenes/rally_template.json deleted file mode 100644 index f0e855630916471974d01d02dcc1b7571d176f7a..0000000000000000000000000000000000000000 --- a/github-simulator/source/assets/scenes/rally_template.json +++ /dev/null @@ -1,112 +0,0 @@ -{ - "vehicle": { - "type": "Rally", - "mass": 3, - "inertia_property": 0, - "inertia_server": 1171.25, - "shape_size": [ - 62, - 29 - ], - "shape_position": [ - 0, - 0 - ], - "shape_rotation": 1.5708, - "gravity_scale": 1, - "linear_damp": 0, - "angular_damp": 0, - "max_contacts_reported": 4, - "continuous_cd": "Disabled", - "friction": 0.95, - "bounce": 0.1, - "grip": 2.25, - "steering_speed": 0.4000000059604645, - "center_steering": true, - "air_resistance": 0.15, - "max_velocity": 1000, - "surfaces": { - "asphalt": { - "grip": 1, - "power": 0.77, - "steering": 1 - }, - "dirt": { - "grip": 0.9, - "power": 0.82, - "steering": 1 - }, - "ice": { - "grip": 0.42, - "power": 0.6, - "steering": 1 - } - }, - "wheels": [ - { - "position": [ - -14, - -24 - ], - "steering": true, - "max_angle_deg": 35, - "power": 2.5, - "brake_power": 3.5, - "handbrake_power": 0 - }, - { - "position": [ - 14, - -24 - ], - "steering": true, - "max_angle_deg": 35, - "power": 2.5, - "brake_power": 3.5, - "handbrake_power": 0 - }, - { - "position": [ - -14, - 24 - ], - "steering": false, - "max_angle_deg": 0, - "power": 3.5, - "brake_power": 0, - "handbrake_power": 0 - }, - { - "position": [ - 14, - 24 - ], - "steering": false, - "max_angle_deg": 0, - "power": 3.5, - "brake_power": 0, - "handbrake_power": 0 - } - ] - }, - "physics": { - "real_precision": "single", - "contact_recycle_radius": 1, - "contact_max_separation": 1.5, - "contact_max_allowed_penetration": 0.3, - "contact_default_bias": 0.8, - "solver_iterations": 16 - }, - "track": { - "name": "Generated track template", - "tiles": [], - "path": [], - "walls": [], - "physics_shapes": [] - }, - "reset_position": [ - 0, - 0 - ], - "reset_rotation": 0 -} diff --git a/github-simulator/source/assets/scenes/snowmobile_template.json b/github-simulator/source/assets/scenes/snowmobile_template.json deleted file mode 100644 index 26316e46526c1ab8a4008e29ee7e3a7222e3d53d..0000000000000000000000000000000000000000 --- a/github-simulator/source/assets/scenes/snowmobile_template.json +++ /dev/null @@ -1,123 +0,0 @@ -{ - "vehicle": { - "type": "Snowmobile", - "mass": 1.5, - "inertia_property": 0, - "inertia_server": 552.625732421875, - "shape_size": [ - 65, - 14.0002 - ], - "shape_position": [ - -0.000114441, - 0.499986 - ], - "shape_rotation": 1.5708, - "gravity_scale": 1, - "linear_damp": 0, - "angular_damp": 0, - "max_contacts_reported": 4, - "continuous_cd": "Disabled", - "friction": 0.95, - "bounce": 0.1, - "grip": 2, - "steering_speed": 0.2, - "center_steering": true, - "air_resistance": 0.3, - "max_velocity": 600, - "surfaces": { - "asphalt": { - "grip": 0.9, - "power": 0.9, - "steering": 0.25 - }, - "dirt": { - "grip": 0.7, - "power": 0.8, - "steering": 0.7 - }, - "ice": { - "grip": 0.4, - "power": 0.8, - "steering": 0.7 - } - }, - "wheels": [ - { - "position": [ - -10, - -25 - ], - "steering": true, - "max_angle_deg": 35, - "power": 0, - "brake_power": 0, - "handbrake_power": 0 - }, - { - "position": [ - 10, - -25 - ], - "steering": true, - "max_angle_deg": 35, - "power": 0, - "brake_power": 0, - "handbrake_power": 0 - }, - { - "position": [ - 0, - 28 - ], - "steering": false, - "max_angle_deg": 0, - "power": 1.3, - "brake_power": 1.5, - "handbrake_power": 0 - }, - { - "position": [ - 0, - 22 - ], - "steering": false, - "max_angle_deg": 0, - "power": 1.3, - "brake_power": 1.5, - "handbrake_power": 0 - }, - { - "position": [ - 0, - 16 - ], - "steering": false, - "max_angle_deg": 0, - "power": 1.3, - "brake_power": 1.5, - "handbrake_power": 0 - } - ] - }, - "physics": { - "real_precision": "single", - "contact_recycle_radius": 1, - "contact_max_separation": 1.5, - "contact_max_allowed_penetration": 0.3, - "contact_default_bias": 0.8, - "solver_iterations": 16 - }, - "track": { - "name": "Generated track template", - "tiles": [], - "path": [], - "walls": [], - "physics_shapes": [] - }, - "reset_position": [ - 0, - 0 - ], - "reset_rotation": 0 -} diff --git a/github-simulator/source/assets/scenes/truck_template.json b/github-simulator/source/assets/scenes/truck_template.json deleted file mode 100644 index 3847b8ff0654afe85a51e329dc693635bd947e3a..0000000000000000000000000000000000000000 --- a/github-simulator/source/assets/scenes/truck_template.json +++ /dev/null @@ -1,112 +0,0 @@ -{ - "vehicle": { - "type": "Truck", - "mass": 8, - "inertia_property": 0, - "inertia_server": 11059.5, - "shape_size": [ - 124.5, - 33 - ], - "shape_position": [ - 0, - 1.25 - ], - "shape_rotation": 1.5708, - "gravity_scale": 1, - "linear_damp": 0, - "angular_damp": 0, - "max_contacts_reported": 4, - "continuous_cd": "Disabled", - "friction": 0.95, - "bounce": 0.1, - "grip": 12, - "steering_speed": 0.15, - "center_steering": true, - "air_resistance": 0.4, - "max_velocity": 600, - "surfaces": { - "asphalt": { - "grip": 1, - "power": 0.85, - "steering": 1 - }, - "dirt": { - "grip": 0.65, - "power": 0.8, - "steering": 1 - }, - "ice": { - "grip": 0.15, - "power": 0.7, - "steering": 1 - } - }, - "wheels": [ - { - "position": [ - -17, - -51 - ], - "steering": true, - "max_angle_deg": 22, - "power": 0, - "brake_power": 3.5, - "handbrake_power": 0 - }, - { - "position": [ - 17, - -51 - ], - "steering": true, - "max_angle_deg": 22, - "power": 0, - "brake_power": 3.5, - "handbrake_power": 0 - }, - { - "position": [ - -17, - 46 - ], - "steering": false, - "max_angle_deg": 0, - "power": 6, - "brake_power": 3, - "handbrake_power": 3 - }, - { - "position": [ - 17, - 46 - ], - "steering": false, - "max_angle_deg": 0, - "power": 6, - "brake_power": 3, - "handbrake_power": 3 - } - ] - }, - "physics": { - "real_precision": "single", - "contact_recycle_radius": 1, - "contact_max_separation": 1.5, - "contact_max_allowed_penetration": 0.3, - "contact_default_bias": 0.8, - "solver_iterations": 16 - }, - "track": { - "name": "Generated track template", - "tiles": [], - "path": [], - "walls": [], - "physics_shapes": [] - }, - "reset_position": [ - 0, - 0 - ], - "reset_rotation": 0 -} diff --git a/github-simulator/source/docs/cli.md b/github-simulator/source/docs/cli.md deleted file mode 100644 index e8144891dd2b9d550189fb25e60a99d8c1558194..0000000000000000000000000000000000000000 --- a/github-simulator/source/docs/cli.md +++ /dev/null @@ -1,115 +0,0 @@ -# CLI reference - -Run commands from the repository root or an unpacked release archive; both contain `assets/`. `altd-sim --version` identifies the build, including nightly packages. `--threads N` sets the Rayon pool; the default respects CPU affinity. `--mode independent|lockstep` selects native scheduling. Both are global flags. Each command's `--help` is the complete argument reference. - -`--math-profile proton|win10-fma3|win11-fma3` is also global and selects the game's C runtime arithmetic independently of the CPU or GPU backend. Every build includes all three profiles. Omitted profiles use `proton` on Linux/macOS, `win10-fma3` on Windows before build 26100, and `win11-fma3` on Windows build 26100 and newer. For example, `altd-sim --math-profile win11-fma3 train-scratch ...` trains for current Windows on any supported host. [Math profiles](../math/README.md) describes the version boundaries and implementation. - -## Commands and inputs - -| Command | Required inputs and purpose | -| --- | --- | -| `bench` | `--scene`, `--network` with weights, `--spawn-trace`; fixed-population timing and optional state dumps | -| `train` | `--scene`, weighted `--network`, `--model`, `--output`; evolution of an existing network | -| `train-scratch` | `--scene`; Xavier initialization, checkpoints, generated batches and candidate export | -| `evaluate` | `--network`, `--model`, `--suite`, `--report`; evaluate a frozen candidate without evolution | -| `serve` | No input files; expose CPU or HIP sessions over a loopback WebSocket | -| `compare-trace` | Positional scene, trace and report; replay controls from a recorded trace | -| `compare-one-step` | Positional scene, trace and report; restore each recorded frame before stepping | -| `compare-closed-loop` | Positional scene, trace, network, model and report; network-driven trajectory comparison | -| `compare-network` | Positional network, trace and report; compare inference with recorded controls | -| `compare-sensors` | Positional scene, trace, model, trajectory and report; compare sensor observations | -| `compare-score` | Positional scene, trace and report; compare lap/score statistics | -| `gpu-info` | Optional `--library`; load the GPU library used by `--gpu`, check its struct layouts and exports and, when a GPU is present, run one kernel on it | - -Scene and capture paths have no campaign defaults. `bench` and `train-scratch` default their sensor model to `assets/models/rally.json`. Scratch training defaults its sensor/control names to `assets/networks/rally.json`; these templates have no trained weights. The defaults are compiled into the binary, and `run.json` records them as `builtin:`. Formula runs should explicitly use the Formula network/model assets and scene template. - -A fixed track requires `--spawn-trace` for `train` and `train-scratch`. The trace's first frame supplies the initial pose. Random mode needs only a geometry-free scene template; generated geometry supplies its reset pose. `--spawn-trace` is ignored in random mode. `bench` always requires its explicit spawn trace and supports `--spawn-index`. - -```sh -target/release/altd-sim --threads 8 bench \ - --scene wasm/test/scenarios/rally_mixed.scene.json \ - --spawn-trace wasm/test/scenarios/rally_mixed.spawn.json \ - --network wasm/test/scenarios/benchmark.network.json \ - --population 1024 --ticks 600 - -cargo run --release --offline --example extract_saved_track -- \ - tests/fixtures/generated_saved_track.json example \ - assets/scenes/rally_template.json target/imported-scene.json -``` - -The importer takes a saved-track export, a track name, a scene template and an output scene. Its bundled input is generated regression data. `tools/benchmark.py` runs the public generated-track bench repeatedly and keeps every report. - -## Local server - -```sh -target/release/altd-sim --threads 8 serve --port 47800 -target/release/altd-sim serve --allow-origin http://127.0.0.1:5173 -``` - -`serve` binds `127.0.0.1` only and defaults to port 47800. Repeat `--allow-origin` to replace the default `https://drivinglab.jectrum.de` allowlist. The shared Rayon pool uses the global `--threads` flag. The server prints its address, allowed origins, thread count and HIP availability. It is included by the default `server` feature. See [the server protocol](server.md) for handshake checks, session operations, binary frames and resume behavior. Drive Lab client integration is a separate change. - -## Scratch training - -Defaults are 8,192 cars, 50,000 target generations, a 5,400-tick time limit, seed 1, two inference batches and network shape `20,16,16,16,16,12,12,8,5`. The output directory defaults to `runs/example`. Pass short explicit limits for a smoke run. `--init-network` uses supplied weights; `--init-population` imports a compatible checkpoint into a new run directory. - -Evolution starts with tournament selection of 20 parents, no crossover, four preserved parents, adaptive mutation and no weight decay. Mutation decays geometrically from 0.4 to 0.0125; `--schedule linear` changes that schedule. `--settings FILE` overrides evolution defaults except requested population and scheduled mutation. It accepts a plain object or a nested `settings` object. Explicit elimination switches and `--reward` take precedence over the file. - -Elimination defaults off. `--eliminate-on-wall` deactivates cars after native wall contact. `--idle-eliminate` deactivates cars after more than 80 consecutive statistics updates with insufficient forward progress. The corresponding `--no-*` switches disable them. Passive physics and pending contacts continue for inactive cars. - -`--reward distance` selects on `total_score`; `--reward best-lap-time` selects on reciprocal best completed lap time. Without an explicit reward, settings-file rewards remain, with distance as the fallback. Cars without a completed lap have no best-lap signal. - -## Generated track batches - -```sh -target/release/altd-sim --threads 8 train-scratch \ - --scene assets/scenes/rally_template.json --track-mode random \ - --random-track-settings assets/random_track_settings.json \ - --tracks-per-generation 3 --track-buffer-size 8 \ - --population 256 --generations 3 --ticks 600 --seed 1729 \ - --save-final-candidate --out-dir runs/example -``` - -Each unchanged population evaluates the same track batch, with fresh spawn and a separate tick limit on each track. Selection uses the mean of normalized per-track reward ranks. Reproduction and the mutation schedule advance once after the complete batch. Fixed mode allows one track only. Queue depth changes preparation capacity, not the RNG stream. - -Without a settings file, track lengths range from 12 to 40 even tiles, all block types are enabled, and one, two or three distinct surfaces are sampled with equal probability into consecutive sections. `assets/random_track_settings.json` is an explicit variant that also samples double-block choices and random/section surface distribution. - -| Track field | Values | -| --- | --- | -| `length` | Fixed even count, or inclusive `{"min": 12, "max": 40}` range; supported grid capacity 4 to 76 | -| `allow_double` | Boolean or list of boolean choices | -| `surfaces` | Fixed distinct IDs, list of sets, or `{"pool": [0,1,2], "count": [1,2,3]}` | -| `distribution` | `0` random per tile, `1` consecutive sections, or choices | -| `start` | `null` for a fresh position, or fixed grid `[x,y]` | - -Surface IDs are asphalt 0, dirt 1 and ice 2. Failed generation retries are bounded; invalid or impossible settings fail rather than silently changing a fixed requested length. Track seeds depend on run seed, generation and batch slot independently of reproduction RNG, worker count and backend. - -## Outputs, resume and stopping - -`run.json` stores effective options from the latest invocation. `log.jsonl` has one completed-generation row, with per-track statistics for random batches. `progress.json` publishes current batch progress atomically. Generated track exports live under `tracks/`. `best.json` and `best_laps/` retain the fastest individual lap, even when selection uses another reward. - -Checkpoints are saved every `--checkpoint-every` generations, default 100, and after normal termination. Zero disables them. `checkpoint.json` identifies a binary population file and stores shape, population, generation and RNG state. `--resume` restores the latest complete generation boundary and removes replayed log/geometry entries. It cannot preserve every mid-tick native contact cache. - -```sh -target/release/altd-sim train-scratch --resume \ - --scene assets/scenes/rally_template.json --track-mode random \ - --random-track-settings assets/random_track_settings.json \ - --population 256 --generations 6 --ticks 600 --out-dir runs/example -``` - -`--generations` is the total target, including saved generations. Supply desired options again: omitted options use CLI defaults, except track seed/count retained in checkpoint metadata. An explicit `--tracks-per-generation` replaces the saved count. Preserve scene and track settings to reproduce the original sequence. Saved networks/RNG take precedence over a new `--seed` or `--init-network` during resume. - -`train-scratch --resume` also retains the checkpoint's math profile; an explicit different `--math-profile` is rejected. Checkpoints predating profiles use `proton`. New `run.json` and checkpoint metadata record `math_profile` so moving a run to another host does not change its arithmetic. - -Shape must match every checkpoint layer; matching parameter counts alone is insufficient. Population reductions keep the first networks, while expansions repeat them in order. Mutation uses the newly supplied schedule at the restored generation. Earlier checkpoints without shape/population recover those fields from adjacent `run.json` before updating current options. - -`--save-final-candidate` writes the complete-batch fitness leader before breeding, with lowest-index tie breaking. It preserves exact weights, ordered names, generation and reward metadata without consuming RNG. A completed checkpoint confirms its filename/hash/generation. A candidate without that confirmation is not a completed-stage result. - -`--stop-score-above`, `--stop-lap-below`, `--stop-lapped-percent` and `--stop-plateau` stop after a qualifying complete batch. Lap targets require a car to lap every track. Plateau counters restart per invocation, using `--plateau-metric lap|score`. A `stop_request` file in the run directory stops after the current batch; a stale file is removed at startup. SIGTERM does not write a final checkpoint. Negative thresholds use `=`, for example `--stop-score-above=-100`. - -## Frozen evaluation - -`evaluate` reads a version-1 suite containing `vehicle`, `options` and distinct `tracks`. Every track specifies relative scene/spawn filenames, their SHA-256 hashes and a finite tick limit. Options specify CPU backend, inference batch count and elimination. The evaluator validates finite network parameters, shape, ordered sensor/control names, vehicle compatibility and input hashes. - -A suite may set `options.math_profile` to a supported profile. An explicit CLI profile must agree with it. Without either, evaluation detects the host's profile; the report records the effective `math_profile`. - -Each track gets fresh state, contacts, controls, sensors and statistics. The evaluator never breeds or modifies training checkpoints/logs. Its atomic report records candidate/suite/simulator hashes, per-track score, progress, lap completion, optional best lap, collisions and executed ticks. An interrupted run does not publish a successful report. Existing tests build fully contained suites from fixed generated tracks. diff --git a/github-simulator/source/docs/design.md b/github-simulator/source/docs/design.md deleted file mode 100644 index 79e93ee14595e8bf099a88cdd041a1d181c66903..0000000000000000000000000000000000000000 --- a/github-simulator/source/docs/design.md +++ /dev/null @@ -1,59 +0,0 @@ -# Design notes - -The simulator keeps arithmetic and execution ordering visible. A small algebraic rewrite can change rounding, contact choices or evolution, so numeric code and scheduling need stronger regression evidence than ordinary I/O changes. - -## Modules and ownership - -| Directory | Modules | Responsibility | -| --- | --- | --- | -| `src/math/` | `godot_math`, `native_math`, `managed_trig`, `double_math`, `pymath`, `vec2` | Captured rounding, runtime arithmetic and vectors | -| `src/physics/` | `car`, `collision`, `simulation`, `broadphase`, `trace_state` | Forces, passive bodies, contacts, population physics and recorded-state reconstruction | -| `src/track/` | `world`, `curve`, `bsp`, `segment_grid`, `path_segments`, `random_track`, `training_tracks` | Scene loading, geometry queries, tile generation and bounded preparation queue | -| `src/nn/` | `network`, `network_simd` | Flat parameters and scalar/AVX2 inference | -| `src/training/` | `agent`, `lineage`, `sensors`, `stats`, `runner`, `evolution`, `batch_evaluation`, `game_random`, `pyrandom` | Populations, statistics, ordered execution, reproduction and RNG backends | -| `src/training/` | `session`, `evaluation` | Embedding, generation checkpoints and evaluation reports | -| `src/gpu/` | `hip`, `simulation` | Runtime-loaded HIP C API, shared layouts and GPU windows | -| `src/wasm/` | `gpu`, `sim` | JavaScript bindings, WebGPU rays and full simulation | - -Library imports follow the directory groups. For example, `physics::car::Car` and `track::world::World` describe their roles directly. The training scheduler types remain available from `training`, and embedding sessions live in `training::session`. HIP loading uses `gpu::hip`; portable GPU layouts live in `gpu::simulation`. - -The CLI entrypoint is `src/bin/altd-sim/main.rs`. Its sibling modules use Cargo's standard binary layout: `args` defines the command contract, `comparisons` produces recorded-trace reports, `bench` and `train` run their workloads, and `scratch` owns run configuration, stopping rules, checkpoint I/O and execution. Scratch parameter checkpoints and Session lineage checkpoints remain separate formats. - -The GPU validation example uses private modules in `examples/gpu_check/` for math, rays, simulation, evolution and benchmarks. Shared fixture setup and mismatch reporting stay outside the checked numerical implementations. - -CPU runners own populations and checkpoint serialization. HIP retains uploaded networks and geometry between windows; CPU evolution installs offspring and preserves the CPU/GPU resume format. WebGPU full simulation mirrors state in browser buffers and commits complete windows back to the Session. A failed window leaves the last committed state available for CPU continuation. - -## Execution modes - -Independent mode advances each car through a window, keeping its state and network near the CPU. Lockstep advances the population one tick at a time. Scenes requesting native shared broadphase require tick-major execution to preserve population contact ordering. Generated CLI training disables that shared broadphase so CPU and GPU use independent car physics. - -Statistics run before driving. Inference uses eight original index-based batches, so compacting inactive work must preserve each car's batch identity. Eliminated cars stop driving and scoring, but passive physics and pending contact accounting continue. The generation limit is checked on the statistics callback, so actual callback counts can slightly exceed a requested tick cap. - -Raycaster windows keep physics and inference on the CPU and replace only ray queries. HIP and full WebGPU windows run those phases on-device. These paths have distinct scheduling and memory costs; shared arithmetic names do not make their loop structure interchangeable. - -## Reproducible tracks and checkpoints - -Track seeds depend on the run seed, generation and batch slot. Their RNG stream is separate from network reproduction. Bounded queue depth and worker timing therefore do not affect which tracks a generation receives. Each batch shares a track across its population and breeds once after all tracks. - -Scratch CLI checkpoints save the next generation's population and RNG state at generation boundaries. Session checkpoints can encode parent lineage instead of every child network while retaining exact reconstruction. Those formats have different consumers and should be treated as separate contracts. Neither promises to serialize every mid-tick native contact cache. - -## Numerical constraints - -Keep float32/float64 transitions, reduction order, runtime transcendental behavior and equal-distance query decisions explicit. Disable FMA contraction in HIP builds. Preserve cached Gaussian draws and both captured game RNG streams. Generate shared numeric tables from their bit patterns and check the generated header before building; GPU builds must not rewrite source files. - -Performance changes need workload medians as well as exact state, checkpoint and RNG comparisons. Profiling paths alter launch behavior, so final timing should use production settings. [Fidelity](fidelity.md) and [performance](performance.md) describe the evidence and its boundaries. - -## Formatting and lint checks - -`cargo fmt --all -- --check` uses the repository's 120-column width. Native checks use `cargo clippy --offline --all-targets -- -D warnings`. Browser bindings use `cargo clippy --offline --target wasm32-unknown-unknown --no-default-features --features wasm -- -D warnings`. Scoped lint allowances explain preserved runtime constants, nonfinite comparisons, indexed numerical loops and packed GPU layouts. They do not relax warnings for unrelated code. - -## Optimizers - -`TrainingRunner` evaluates a whole population, scores it with `reward_values` (ranks within the generation), and hands the cars and scores to an `Optimizer` (`src/training/optimizer.rs`), which returns the next population. `settings.algorithm` chooses one; the runner replaces its optimizer when the name changes, and the new one continues from the cars it is next given. - -- `"ga"` is the default: selection, crossover, mutation and decay as in `evolution.rs`. Its random draws, and so every GA checkpoint and test, are unchanged. -- `"ars"` (`src/training/ars.rs`) is Augmented Random Search V2-t with an elite pool. It keeps a search point `theta`; the population is the elites, `theta`, and antithetic probes `theta ± nu·d`, and the scores steer one step `alpha/(b·nu)·Σ(r+ − r−)·d` over the best `top_frac` of the pairs. The step is skipped when the scores are all alike. Elites are the best distinct cars of the last population, old elites included; they are re-run unchanged every generation, so ranks from different generations are never compared. Settings live under `settings.ars` (`nu`, `alpha`, `top_frac`, `elite_count`, `max_weight`), and `population` must be at least `elite_count + 3`. - -Rewards stay rank based for every algorithm. ARS therefore behaves like rank-based OpenAI-ES with top-b pair selection rather than reference ARS on raw magnitudes. - -Session checkpoints record how a population was made, so each optimizer has its own format: `ALTDCKP2` for GA parent lineage and `ALTDCKP3` for ARS, which stores `theta`, the elite pool, the sampling settings and the generator before the directions were drawn. Restoring one resamples the population and the directions exactly. A full population checkpoint (`ALTDCKP1`, JSON checkpoints, scratch CLI checkpoints) carries no search state, so ARS continues from the best restored car. A changed population size does the same. diff --git a/github-simulator/source/docs/fidelity.md b/github-simulator/source/docs/fidelity.md deleted file mode 100644 index f18dbc940a5eff682649f92cf7eab82e20c18735..0000000000000000000000000000000000000000 --- a/github-simulator/source/docs/fidelity.md +++ /dev/null @@ -1,37 +0,0 @@ -# Fidelity and regression coverage - -The simulator targets the captured behavior of AI Learns To Drive. Physics mixes float32 state with float64 calculations, and callbacks, contact caches and inference batches have observable ordering. Matching formulas alone does not reproduce those results. - -Numerical implementations retain operation order, intermediate precision, rounding boundaries, tie-breaking and RNG consumption. Scalar and AVX2 inference accumulate in the same order. HIP builds disable multiply-add contraction and require correctly rounded float32 division and square root. Spatial acceleration preserves traversal and first-equal-result decisions. A change to any of these rules needs exact comparisons before performance measurements. - -## What the tests establish - -The game captures in `tests/fixtures/` exercise native vehicle transitions, population lifecycle, contact/reset behavior, runtime math, sensors, statistics and training. Their track geometry comes from synthetic cases or generated tracks. Campaign tracks are not distributed. Short recorded vehicle-state samples retain positions, contact points and scalar sensor outputs, with collider exports removed. They are finite oracle samples. Older traces sometimes omit body matrices or contact caches, so restoring their visible state cannot prove complete native state reconstruction. - -Packed-game captures use Godot 4.3 build 77dcf97d8 and Windows .NET 8.0.2 where the fixture records that setup. The capture harnesses and raw logs are not distributed. Fixture provenance names the runtime and harness without presenting unavailable paths as public tooling. Packed-sine lookup tables were extracted from Windows .NET 8.0.2 coreclr.dll; their source hash remains in the table header, and the extraction script is not distributed. - -Generated-track game captures cover parked TileMaps, native wall insertion order, single-vehicle and population contacts, live resizing, redraws, coalesced creation, Truck and Snowmobile behavior, terrain grip and ordered/path sensors. Their `recipe` fields select a fixed generator seed, generation and slot using the public settings and vehicle template. Tests rebuild the geometry and apply explicit scene overrides; output states, curve samples and sensor values come from the game. These captures are separate from simulator-generated goldens. - -Generated-track goldens record the simulator's current results for controlled contacts, restored states, scoring, population redraw and Session checkpoints. They detect regressions; they are not independent game captures. Fixtures and their origins are documented in [the fixture README](../tests/fixtures/README.md). Differential tests compare accelerated queries with brute force, and CLI tests cover settings, checkpoints, resume, generated batches and evaluation. - -A historical diagnostic comparison against a captured campaign run matched 1,393 of 1,398 one-step frames exactly. The remaining five differed in angular velocity, rotation or velocity. Network inference matched all 1,398 frames with zero error. These results predate the public generated-track replacements and describe that capture, not every scene or closed-loop run. The campaign run and its geometry are not public inputs. - -## Platform limits - -On x86, native `engine_sin_cos` uses x87 `FSINCOS`. Its large-argument reduction preserves the extended pi constant across x87 precision modes. Tests cover 24, 53 and 64-bit precision and check that the caller's control word is unchanged. The portable fdlibm-based implementation is validated against x87 over float32 arguments with absolute value at most 16; the regular suite samples the domain and an ignored exhaustive test scans it. Larger arguments are not covered by that claim. - -Nonfinite float32 trig outputs retain the captured NaN signs and payloads on every target. Scalar and engine routines quiet NaNs. Managed `SinCos` preserves the sine input bits and clears the sign while quieting cosine. Both infinities produce the captured x86 indefinite NaN, `0xffc00000`. - -The distance-from-wall sensor also returns the x86 indefinite for zero divided by zero and infinity divided by infinity, including float32 conversion boundaries. Quotients from NaN operands retain their payloads. - -Seeded Python Xavier tests keep the captured parameter checksum on Linux and Windows. On macOS, `cargo test --lib nn::network::tests::xavier_matches_python -- --exact` requires `python3` and compares every parameter bit, the MT state and the cached Gaussian draw with CPython on that host. This checks host parity without assuming libSystem and glibc round identically. - -Seeded initialization uses host float64 transcendental functions. Equal RNG draws can therefore produce different parameter bits across native hosts. Session regressions compare both execution modes with the legacy `TrainingRunner` on each host and require exact checkpoint replay. Stored Session snapshot and checkpoint goldens remain exact comparisons on their capture platform, Linux x86_64 GNU. - -Native and WASM use different host implementations for some float64 transcendental operations. The public generated Formula state scenario matched 2,773 native values, while expanded snapshots found 2,285 native differences per scheduling mode: 703 controls, 405 metrics and 1,177 checkpoint bytes. Serial and threaded WASM had the same native differences and matched each other in all 253,341 checked values per mode, including the optional score aggregates. A passing state-only comparison does not imply exact expanded native parity. Counts depend on the scenario and checkpoint layout. - -The RX 7900 XTX WebGPU raycaster matched 4,000 random rays, 4,000 nearest-wall points and 119,743 in-simulation rays in that baseline. WGSL floating-point behavior, subnormal handling and NaN payloads still vary by device. Runtime verification counts discrepancies without replacing GPU results. See [WASM notes](wasm.md) for the comparison layers. - -## Checking a change - -Run native tests in debug and release, regenerate observations to temporary outputs, and compare exact float bits, RNG state and checkpoint bytes. Exclude only named timing and path metadata when comparing reports. Keep generated references separate from captured oracle data. Measure throughput only after those checks pass. diff --git a/github-simulator/source/docs/gpu.md b/github-simulator/source/docs/gpu.md deleted file mode 100644 index deed2f709fbd098053cb11a35a26b1f5b2cec6e0..0000000000000000000000000000000000000000 --- a/github-simulator/source/docs/gpu.md +++ /dev/null @@ -1,31 +0,0 @@ -# HIP implementation notes - -The HIP library runs statistics, sensing, network inference and vehicle physics for independent cars. It also supports GPU turnover through the runner API. Population mean and novelty run on the GPU with reductions in the original order; that turnover path keeps offspring weights uploaded for the next generation. Rust owns resets and checkpoints. - -The same sources build for NVIDIA GPUs with `nvcc` (`gpu/build-cuda.*`, `gpu/sim/compat.h`); this page applies to both. NVIDIA warps are 32 lanes and use the `*_sync` warp operations with a full mask, so every lane of a wave must reach `reserve`, `append` and the active-count ballot, as on AMD. NVIDIA arithmetic returns its canonical NaN where the CPU returns a NaN operand, so those results are built explicitly on both vendors (`altd_nan_operand`, `altd_invalid`). - -## HIP sessions - -Native `SessionOptions` accepts `backend: "hip"` as well as the default `"cpu"`. The same option is available to [local server](server.md) clients. WASM sessions reject HIP. The process opens the HIP library once and allows one HIP session at a time; a session that cannot use HIP reports CPU through `backend()` and a reason through `backend_note()`. - -HIP sessions drive through the existing GPU window and generation paths, then read car and agent state back into the CPU runner. Readers, metrics and checkpoints therefore use the same runner state as CPU sessions. The simulator grows when a restored or started population exceeds its capacity, and track replacement uploads a new GPU world. - -Before the first window after a start or restore, a session compares 12 CPU and HIP ticks exactly from a backed-up state. It restores that state before real driving. Track replacement also requires a new comparison. A mismatch or verification error switches the session to CPU; a later HIP window error returns an error without retrying. - -Session turnover uses CPU `next_generation_traced`, which retains the parent records needed by format-2 checkpoints. The next HIP window uploads the new networks. Start, restore and network replacement also invalidate the uploaded network cache. This differs from the runner's GPU turnover path, which does not produce those parent records. - -Native CPU and HIP sessions share checkpoint formats with WASM. Native and WASM results have known last-bit differences; switching engines preserves the checkpoint but may change later training. - -## Kernels and validation - -Kernels compact live work into eight original inference batches. Only due batches run sensing and inference. A separate physics list retains inactive cars until they settle exactly. Windows replay a HIP graph over the 24-tick statistics/inference period, with direct launches for remainders. Compatible windows reuse the graph; changing track, schedule, shape, population or output mapping invalidates it. - -The split physics path separates collision phases and preserves ordered per-car contact generation. `ALTD_GPU_SPLIT=0` selects the fused alternative. `ALTD_GPU_GRAPH=0` bypasses graphs. These are differential validation options, not promises of better performance. - -The network forward path uses double parameters and adds products in input order. This can dominate at large populations on GPUs with low double-precision throughput. Host buffers retain mutation noise and upload packing space. A 262,144-car population with 1,661 parameters requires about 3.24 GiB for that host double buffer alone; device memory, car state and checkpoint populations are additional. - -`gpu::simulation` mirrors C layouts explicitly and exports/imports canonical car and agent state. The loader resolves the shared library at runtime, so ordinary CPU builds do not link ROCm. Rebuild the Rust executable and library together when their C API or layouts change. - -Historical CPU/HIP checks cover primitive math, queries, sensors, inference, physics, turnover, batch wrapping, uneven populations, simulator reuse and resumed training. They compare flags, exact values, RNG state and population bytes. These finite checks do not establish exactness for every scene or device. Run the generated-input checks on your own GPU using [the GPU guide](../gpu/README.md). [Performance notes](performance.md) retain measured results and timing boundaries. - -On Linux, `python3 gpu/test_constructor_cleanup.py` checks the built library's constructor failure paths without allocating GPU memory. It requires a C++17 compiler and the ROCm runtime. A temporary HIP shim injects allocation and copy failures, then checks that device buffers, host buffers, events and streams are released. The ROCm CI job runs this check without a GPU. diff --git a/github-simulator/source/docs/math-profiles.html b/github-simulator/source/docs/math-profiles.html deleted file mode 100644 index d6d08b19bc763f5648e699bfdfcbb0edc752ec0c..0000000000000000000000000000000000000000 --- a/github-simulator/source/docs/math-profiles.html +++ /dev/null @@ -1,415 +0,0 @@ - - - - -Math profiles: supporting Windows UCRT variants on every backend - - - -
- -

Math profiles

-

Historical design draft for issue #7, based on code at e96a2e8 and the 56-build UCRT survey. The original proposal and diagrams below are retained as design history.

- -
-Implemented profiles -

The implementation supports proton, win10-fma3 and win11-fma3. Every native/WASM/GPU build includes all three. Defaults detect the host's operating system and version; resumed runs retain their saved profile. SSE2 variants are outside the supported scope.

-

The new Windows kernels share restricted C source in math/kernels/ucrt.h, translated to Rust/WASM and WGSL and included by HIP/CUDA. The established musl and AVX2 ports remain. WebGPU pipeline creation specializes the generated module for the session's profile. Current kernel, fixture and verification documentation describes the implemented architecture.

-
- -

Original proposal

- -
-TL;DR -
    -
  • Make the platform's C runtime a runtime parameter (MathProfile) instead of a fact baked into every backend. Five profiles cover everything we measured: Proton and four Windows ones.
  • -
  • Write each math variant once, in the restricted C subset that gpu/sim/*.h already uses, and generate the Rust, AVX2, HIP and WGSL versions from it. This extends the existing gen-sim.py / gen_tables.py pipeline instead of adding a new one.
  • -
  • Use exception tables where exhaustive proof is possible (sinf/cosf: every float32 can be checked, as engine_exceptions.h already does for x87). Use algorithm ports for the rest (atan2f, exp, log, pow, tanh).
  • -
  • Verify each (profile × backend) pair against golden vectors captured from genuine DLLs under Wine. Only the output bits are stored; the DLLs are not.
  • -
  • The default stays proton, so existing goldens, checkpoints and behaviour don't change until a user chooses another profile.
  • -
-
- -

1. What has to be supported (measured)

-

Results of the 56-build survey: genuine x64 ucrtbase.dll builds signed 2020–2026, each tested with FMA3 on and off. Each cell shows which implementation a profile needs. A different colour means a different implementation, i.e. not bit-identical.

- - - - - - - - - - - -
Function (who uses it)protonwin10-fma3
Win10, Win11 ≤23H2
win10-sse2win11-fma3
Win11 24H2+
win11-sse2impls
sinf (ray directions, vision)muslucrt·Fucrt·Sucrt·Fucrt·S3
cosf (ray directions, vision)muslucrt·Fucrt·Sucrt·Fucrt·S3
atan2f (rotation, sensors, slip, tracks)muslucrtucrtucrtucrt2
exp (tyre grip)muslA·FSB·FS4
log (Box-Muller, evolution; CPU only)muslucrtucrtucrtucrt2
pow (input easing, novelty)muslA·FA·SB·FB·S†5
tanh (network activation)muslAAB·FA3
Total implementationstoday: 7 (one per function) · needed: 2222
-

A = Windows 10 / Win11 ≤23H2 behaviour, B = Win11 24H2+ behaviour, F = FMA3 path, S = SSE2 path. Profile-independent and unchanged: x87 engine_sin_cos, .NET MathF.SinCos (managed_sin_cos). †B·F and B·S pow agree on all game-range inputs and differ on 10 of 4M wide inputs.

- -
-
-

Where each function runs today

- - - - - - - -
CPU scalarAVX2HIPWebGPU
sinf / cosf✓–✓✓
atan2f✓–✓✓
exp, pow✓–✓✓
log✓–––
tanh✓✓ tanh4✓✓
-

WASM CPU compiles the same Rust as native scalar. log only runs in reproduction, which stays on the CPU in HIP and WebGPU sessions.

-
-
-

Why a naive port doesn't scale

-

Hand-porting 22 implementations to every backend would mean 22 Rust + 20 HIP + 3 AVX2 bit-exact ports, each needing review, plus 22 generated WGSL functions to validate. Today 7 Rust + 6 HIP + 1 AVX2 are maintained by hand.

-

The FMA3 variants also need explicit fused multiply-adds. HIP builds deliberately use -ffp-contract=off, and WGSL has no f64 at all (f64.wgsl is software binary64). So "just enable FMA" isn't an option anywhere.

-
-
- -

2. Current state

-
- - - - - - - - SPECIFICATION (one fixed platform: Proton / musl) - BACKEND IMPLEMENTATIONS - CALLERS (hard-wired) - - - - Rust scalar = the reference - src/math/native_math.rs — sinf, cosf, atan2f - src/math/double_math.rs — exp, log, pow - src/nn/network.rs — game_tanh / game_expm1 - src/math/double_math_tables.rs - Free functions; no notion of "which platform". - Also compiled for WASM CPU. - - - - AVX2 src/nn/network_simd.rs - tanh4 / expm1_tanh — hand port of game_tanh - - - - HIP gpu/sim/math.h - native_sin/cos/atan2, dexp, dpow, game_tanh - hand port; -ffp-contract=off - - - - gpu/gen_tables.py → double_tables.h - bit-copied from Rust tables - - - - WebGPU src/wasm/sim/generated.wgsl - wasm/gen-sim.py transpiles gpu/sim/*.h (pycparser) - doubles via f64.wgsl (software binary64) - - - - - - - - gen - - - - 21 Rust call sites - physics/car.rs, training/*.rs, - track/curve.rs, random_track.rs … - call native_math::atan2(...) etc. - gpu/sim step/sensors/stats/track.h - call native_* / dexp / dpow - → no way to choose a platform - - - - Limits for issue #7 - • One implementation per function, compiled in. - • Rust and HIP are two hand-maintained sources; - AVX2 is a third for tanh. - • FMA is forbidden everywhere (contract=off); - no explicit fma primitive in Rust/HIP/WGSL code. - • Golden fixtures describe Proton only. - • Adding Windows naïvely = 22 × (Rust + HIP) - + 3 AVX2 bit-exact hand ports. - - - hand-written port - generated - source of truth - -
-

Rust is the reference; HIP is a second hand-written copy; WGSL is generated from HIP; AVX2 has a third copy of tanh. Each backend's math is fixed when it is compiled.

- -

3. Proposed architecture

-
- - - - - - - - - ① EVIDENCE (dev-only, Wine) - ② SINGLE SOURCE - ③ GENERATED BACKENDS - ④ RUNTIME SELECTION - - - - UCRT reference harness - tools/ucrt-ref (from ucrt_harness.c) - genuine DLL × FMA3 on/off, under Wine - Outputs (committed): - • golden vectors per profile - tests/fixtures/math_profiles/*.bin - • exhaustive sinf/cosf scans - → exception tables - • build → profile map (profiles.toml) - DLLs are never committed; - only versions, hashes and output bits. - - - - math/kernels/ (restricted C subset) - the subset gen-sim.py already parses - - musl/ sinf cosf atan2f exp log pow tanh - ucrt/ atan2f log - ucrt/ exp_{a_fma,b_fma,sse2} - ucrt/ pow_{a_fma,a_sse2,b_fma,b_sse2} - ucrt/ tanh_{a,b_fma} sincos = musl + table - Primitives every backend must provide - add/sub/mul/div (correctly rounded), - fma() explicit and exact, bits casts, select() - profiles.toml - profile → variant per function (the matrix in §1) - + the UCRT builds each profile stands for - - - - wasm/gen-math.py - reuses gen-sim.py's pycparser front end; - 4 emitters; CI runs it with --check - - - - - - - - - Rust scalar (+ WASM CPU) - src/math/platform/generated.rs - fma → f64::mul_add (always exact) - - - AVX2 (tanh variants only) - emitted from branch-free kernels - fma → _mm256_fmadd_pd / scalar fallback - - - HIP - gpu/sim/platform_math.h (#include) - fma → __fma_rn; contract stays off - - - WGSL, one module per profile - src/wasm/sim/math_<profile>.wgsl - fma → new f64_fma in f64.wgsl, - f32 fma via exact f64 product - - - - - - - - - MathProfile - proton | win10-fma3 | - win10-sse2 | win11-fma3 | - win11-sse2 - SessionOptions - mathProfile (default proton) - Checkpoint header - records profile; resume - rejects a mismatch - Rust - Math{profile} passed to - world/runner; 21 sites call - m.atan2(), m.exp() … - HIP - wave-uniform profile arg - WebGPU - concat chosen module only - - - - - - - - ⑤ Verification matrix (every profile × every backend, bit-exact) - - - Math level - Golden vectors vs. Rust scalar - and AVX2 (cargo test). - HIP math_kernel op tests - gain a profile argument. - WGSL: wasm/test/exact.js - over the same vectors. - - - Exhaustive (f32 unary) - All 2³² inputs for sinf/cosf - per variant: musl + table - == genuine DLL. Ignored test, - runs under Wine (~45 s/pair), - like examples/ - gpu_engine_scan.rs. - - - Simulation level - Existing CPU↔HIP↔WebGPU - parity tests, parameterised - by profile. proton goldens - must stay byte-identical - (refactor guard). - - - Game level (optional) - Windows player records a trace - via the game's MCP server; the - sim replays it under each profile. - The matching profile confirms the - setup end to end and can - auto-detect a user's profile. - - - -
-

One source per variant, four generated backends, and the profile chosen at run time. The Wine harness that produced the survey becomes the permanent oracle.

- -

4. Current vs proposed

- - - - - - - - - - - - - -
ConcernCurrentProposed
Platform choiceFixed at compile time: Proton/musl onlyMathProfile chosen per session and recorded in checkpoints; default proton
Sources of truthRust (reference) + HIP (hand copy) + AVX2 tanh (hand copy)One C-subset kernel per variant; Rust, AVX2, HIP and WGSL are generated
Cost of one new variantUp to 3 bit-exact hand ports + regenerate WGSL + new fixtures1 kernel + 1 line in profiles.toml + regenerated golden vectors
FMAForbidden everywhere (-ffp-contract=off); no fma primitiveStill no implicit contraction; an explicit fma() primitive with an exact implementation on each backend
sinf / cosfmusl portmusl port + per-variant exception table (6–51k entries), proven over every float32
atan2f, exp, log, pow, tanhmusl portsmusl + UCRT algorithm ports (can't be proven exhaustively; validated with golden vectors)
HIP selectionn/aProfile kernel argument (uniform per launch, so no divergence); template instantiation only if profiling shows register pressure
WebGPU selectionn/aAssemble the shader with only the chosen math_<profile>.wgsl, keeping compile cost flat
Track generationUses atan2f (random_track.rs, curve.rs)Uses the profile too, so generated geometry matches the player's platform
FixturesProton captures onlyProton captures unchanged + per-profile golden vectors from genuine DLLs (outputs only)
Unaffectedx87 engine_sin_cos, .NET managed_sin_cos, all ordinary arithmetic, RNG streams, and pyrandom's host libm (separate CPython-compat concern)
- -

5. Implementation strategy per function

- - - - - - -
FunctionApproachWhyProof level
sinf, cosfmusl kernel + sorted exception table + branch-free binary search (existing engine_sin_cos pattern)Only 6,254–50,872 of 2³² inputs differ, all 1 ULP; the table is generated from the scanExhaustive
atan2fPort the UCRT algorithm16% of inputs differ, too many for a table, and there are 2 argumentsGolden vectors
tanhPort both UCRT variants in branch-free form (also feeds AVX2)About 10% of inputs differ; it's the hottest path (inference)Golden vectors
exp, pow, logPort UCRT algorithms; FMA and SSE2 variants differ only where fma() is usedDouble-precision inputs can't be enumeratedGolden vectors
- -

6. Phased rollout

-
    -
  1. Phase 0: spike (decides feasibility). Recover the algorithms behind UCRT atan2f and tanh, the two highest-impact functions, and reproduce the golden vectors bit-exactly in a throwaway Rust prototype. Turn ucrt_harness.c and the 56-build survey into tools/ucrt-ref. Exit criterion: 0 differences on golden vectors for both functions on both paths.
  2. -
  3. Phase 1: plumbing, no behaviour change. Add MathProfile, SessionOptions.mathProfile and the checkpoint field; route the 21 Rust call sites and the HIP/WGSL call sites through a profile-aware layer that still only knows proton. All existing goldens stay byte-identical.
  4. -
  5. Phase 2: single source, no behaviour change. Move the musl kernels into math/kernels/, write gen-math.py with Rust/AVX2/HIP/WGSL emitters, and replace the hand ports with generated code. Guard: exhaustive f32 checks and fixture checks against the old code.
  6. -
  7. Phase 3: Windows variants. Add them in order of impact: atan2f, tanh, exp, pow, log, then the sin/cos tables. Add f64_fma to f64.wgsl and the scalar fma fallback for CPUs without FMA.
  8. -
  9. Phase 4: parity matrix. Parameterise CPU↔HIP↔WebGPU parity and session regressions by profile; run the full set for proton and one Windows profile in CI, all profiles nightly.
  10. -
  11. Phase 5: user-facing. Profile picker in Drive Lab and the CLI (--math-profile), with a short "which one am I?" helper (Windows version, FMA3 support); optional detection from a recorded game trace.
  12. -
- -

7. Risks and open questions

-
-
-

Deriving the UCRT algorithms largest risk

-

For atan2f, exp, log, pow and tanh we need Microsoft's actual algorithm, not just its outputs. Studying the binary may conflict with Microsoft's licence terms, and interoperability exceptions vary by jurisdiction, so this needs a decision before Phase 0. Black-box fitting against golden vectors is a fallback, but it may not converge to bit-exactness. One unverified hypothesis: the x64 CRT's transcendentals may share ancestry with AMD's open-source libm. If so, that would be a clean starting point to test against the vectors.

-
-
-

Coverage the survey didn't test

-
    -
  • All results came from one CPU (Zen 4 with AVX-512). It's unknown whether newer builds use AVX-512 paths that CPUs without AVX-512 wouldn't take.
  • -
  • FMA3 vs SSE2 was forced with _set_FMA3_enable, not observed on a CPU without FMA3.
  • -
  • Windows on ARM (x64 emulation) wasn't tested.
  • -
  • Profiles may need splitting if any of these show differences; profiles.toml makes that a data change.
  • -
-
-
-

Performance

-
    -
  • FMA3 profiles on targets without hardware FMA: f64::mul_add on WASM and pre-Haswell CPUs, and f64_fma in WGSL soft-float, are much slower than a multiply plus an add. This mainly affects tanh in inference.
  • -
  • The per-call profile match in Rust should be close to free (well predicted); switch to monomorphisation only if benchmarks show otherwise.
  • -
  • HIP code size grows with the number of inlined variants. Watch register pressure and occupancy.
  • -
-
-
-

Scope and maintenance

-
    -
  • About 3× more kernels (22 vs 7), but each is written once.
  • -
  • Golden vectors grow per profile: keep about 64k inputs per function per profile, so a few MB total.
  • -
  • A future UCRT update can add a behaviour group. The survey script reruns in about 2 minutes against Winbindex, so it can become a periodic check.
  • -
  • An end-to-end game confirmation still needs a Windows player's trace (Steam blocks running outside Steam).
  • -
-
-
- -

Sources: code at e96a2e8 (src/math, src/nn, gpu/sim, wasm/gen-sim.py, src/wasm/sim); UCRT survey data in ~/altd-wine/versions/ (sweep.json, groupcmp_*.txt, exhaustive_*.txt).

-
- - diff --git a/github-simulator/source/docs/performance.md b/github-simulator/source/docs/performance.md deleted file mode 100644 index 7797107f23f1352f79a9f8ad65ffd252109aeeb2..0000000000000000000000000000000000000000 --- a/github-simulator/source/docs/performance.md +++ /dev/null @@ -1,72 +0,0 @@ -# Performance measurements - -All timings below describe the stated hardware and inputs. Generated-track benchmarks in this tree provide a repeatable public workload; historical campaign/checkpoint measurements are retained for context and cannot be reproduced from public assets alone. - -## Native CPU baseline - -Measured on an AMD Ryzen 9 7950X, Linux x86_64, rustc 1.94 and LLVM 21.1.8, with local `target-cpu=native`. Each workload had an untimed warmup and seven measured samples. Worker processes used eight distinct physical cores. Desktop activity and a concurrent GPU training process remained active. - -The fixed bench workloads used 1,024 cars, 3,600 timed ticks, two warmup ticks, a fixed seed/network/model and independent execution. The generated scene used seed 1729, generation zero, slot zero. The random batch used 512 cars, three generations, three tracks per generation, 3,600 ticks per track and eight workers. Random timing includes simulation, turnover and track installation waits. Throughput uses the requested car-tick count; elimination can reduce actual executed work. - -| Workload | Workers | Median seconds | Requested car-ticks/s | -| --- | ---: | ---: | ---: | -| Captured campaign scene | 1 | 7.474565 | 493,193 | -| Captured campaign scene | 8 | 0.987717 | 3,732,242 | -| Generated scene | 1 | 7.508362 | 490,973 | -| Generated scene | 8 | 1.004230 | 3,670,872 | -| Random training batch | 8 | 4.673350 | 3,549,660 | - -An identical-source repeat changed the medians by -0.15%, -0.95%, +0.06%, -2.33% and -0.37%, respectively. This measures noise; it is not a speedup claim. Public benchmark recipes use different generated geometry and Xavier weights, so their numbers need their own baseline. - -```sh -cargo build --release --offline -python3 tools/benchmark.py --population 1024 --ticks 3600 --repeats 7 \ - --threads 1 8 --report target/cpu-benchmark.json -``` - -The runner works from any directory, keeps every JSON sample and reports medians. `--scene`, `--spawn-trace`, `--network`, `--model`, `--modes` and `--binary` select another workload. It measures the CLI simulation interval; process launch, loading and the initial population are outside that interval. - -## Historical HIP measurements - -AMD Radeon RX 7900 XTX, gfx1100, HIP 7.1.52802, ROCm clang 20 and Ryzen 9 7950X with eight Rayon workers. Inputs were a captured Rally campaign scene and a frozen 8,192-network checkpoint, repeated for larger populations. Shape was `20,16,16,16,16,12,12,8,5`, with 1,661 double parameters per car and two inference batches. The desktop remained active. - -Full generation medians after one warmup and three measured runs were 2.094 seconds at 8,192 cars, 5.715 at 32,768 and 40.315 at 262,144. Wall and idle elimination were disabled; the 5,400-tick limit ended at statistics callback 5,406. Timers included packing, transfers, simulation and state import, and excluded reset, hashing, reproduction and checkpoint I/O. A later kernel investigation recorded production wall times of about 1.86, 4.93 and 36.2 seconds with unchanged digests, but did not record the same repeated-median protocol. Treat those later values as diagnostic observations. - -Separate turnover optimization measurements used three consecutive generations with elimination enabled, one warmup and three timed samples. Complete-generation medians changed from 0.386 to 0.267 seconds at 8,192 cars, 1.154 to 0.790 at 32,768 and 9.663 to 6.336 at 262,144. They include turnover after the third generation and exclude loading, hashing and checkpoint I/O. All 36 before/after generation pairs matched result digests and executed ticks. This workload ended early and must not be compared as a full 5,400-tick run. - -Use [GPU tooling](../gpu/README.md) for fresh measurements on generated inputs. A GPU performance gate requires an otherwise idle GPU; these historical results do not establish throughput under concurrent training load. - -## Historical browser measurements - -Ryzen 9 7950X, Chromium 153.0.8010.12 on Linux, RX 7900 XTX with RADV 26.2.2. A Formula scene used shape `20,16,5`, two inference batches and no elimination. Four 180-tick generations ran per population; the first was warmup and the median of the next three is shown. Complete time includes evolution and coordinator yields. This was one sweep. - -| Cars | Serial complete ms | Eight-worker complete ms | WebGPU with eight workers ms | -| --- | ---: | ---: | ---: | -| 32 | 12.7 | 4.9 | 365.2 | -| 256 | 100.4 | 18.3 | 361.5 | -| 1,024 | 399.5 | 66.1 | 189.0 | -| 4,096 | 1,657.3 | 263.8 | 647.7 | - -At 4,096 cars, eight-worker CPU advancement took 228.9 ms versus 1,577.7 ms serial. Module/pool startup was 14.1 ms serial and 94.5 ms with eight workers. Peak observed linear memory in the largest case was 97.6 MiB serial and 113.7 MiB with eight workers, excluding JavaScript workers, GPU allocations and any separate preview module. These inputs are historical; rerun the [public browser harness](../wasm/README.md) to establish results for the generated scenarios and another browser/device. - -## Firefox completion waits — 2026-10-06 - -Firefox 157 on the local Linux/RX 7900 XTX system reproduced the 30 ticks/s limit. In the public Formula benchmark, six-tick calls took about 200 ms at both 32 and 1,024 cars. At 32 cars, the 120 measured ticks spent 3,975 ms in advancement while GPU timestamps covered only 192 ms. Setting GPU load to 1 retained the same limit, ruling out the configured idle budget as its cause. - -The runtime now prompts completion delivery with empty queue submissions every 4 ms while awaiting queued work or mapped readback. It retains the original promises and command order, cancelling the timer after completion, device loss or a submission exception. The earlier workaround was absent from this source tree. The Windows/Proton kernel sources are unchanged by this fix. - -| Workload | Cars | Before ticks/s | After ticks/s | -| --- | ---: | ---: | ---: | -| Formula, fixed six-tick windows | 32 | 30.19 | 688.07 | -| Formula, fixed six-tick windows | 1,024 | 30.27 | 259.63 | -| Normally served app, Rally, adaptive windows | 512 | 29.98 | 249.53 | - -Formula used the public default scenario, shape `20,16,5`, Proton, disabled elimination and 120 warmup ticks, with 120 measured ticks at 32 cars and 60 at 1,024 cars. The benchmark records per-pass GPU timestamps. The app used its Test Loop fixture, default shape `20,16,16,12,8,5`, one inference batch, eight CPU workers, Proton, load 0.85 and disabled elimination. App values are medians of the reported rates during a four-second observation; whole-observation rates were 29.88 and 258.92 ticks/s. The final served run used the rebuilt assets without package or runtime overrides. Compilation and startup are excluded. These are diagnostic before/after observations, not repeated benchmark medians. - -A separate app comparison completed three generations and 558 ticks with each runtime, using identical rebuilt WASM packages and changing only the JavaScript completion waits. Checkpoint bytes, every population network, terminal car states, summaries and stable generation logs matched exactly. The completed outcome SHA-256 was `7086f0ce5b7f555d7bc2d7a4ef5c2db321d71ebb3c21dd93eaa9324ffa02eedc`. Time through the third turnover changed from 18,688 ms to 1,405 ms; the adaptive pump used 90 calls before and 37 after. - -Two additional runs through normally served assets completed the same 558 ticks in 2,339 and 2,385 ms, with identical outcomes and stable logs. A prior diagnostic run with package routing reported 1,143 ticks/s; the normal served measurements above provide the conservative result. One initial instrumented run stalled while waiting for its generation outcome. Its detailed phase was not captured, and the stall did not recur in the three subsequent completed comparisons; its cause remains unresolved. - -Three alternating Chromium 153 comparisons kept the WASM, shader, Win11 profile, 1,024 active cars and 120-tick bulk windows identical, changing only the completion-wait runtime. Median wall time was 355.4 ms before and 355.0 ms after. Background GPU activity was present before browser launches, so these observations do not measure isolated peak throughput. - -Both serial and threaded production packages were rebuilt. Full Firefox simulation comparisons passed for all three profiles: 184,558 checks, zero mismatches. The Node completion-wait regression suite passes 16 tests against the fixed runtime; the frozen original runtime fails nine of them. See [the browser commands](../wasm/README.md) to repeat the Formula workload or run the regression suite without a GPU. diff --git a/github-simulator/source/docs/reviews/2026-10-05-native-server-review.md b/github-simulator/source/docs/reviews/2026-10-05-native-server-review.md deleted file mode 100644 index b79afed333c6267c1844e3b997fde0527d7d3ae1..0000000000000000000000000000000000000000 --- a/github-simulator/source/docs/reviews/2026-10-05-native-server-review.md +++ /dev/null @@ -1,51 +0,0 @@ -# Native server implementation review - -Date: 2026-10-05 - -Two independent reviewers used GPT-6.1-sol at ultra effort. The native comparison starts at `origin/main`, commit `0852de991c6e6e2d9868f74346bb0ac9d3260774`. The companion webapp comparison starts at `aebb7ab` and ends at `e99e65541cb502302a32ac2d1bdc4d53b9aee0a9`, branch `t3code/native-simulator-client`. The webapp repository has no configured remote. - -The spec is [the approved native server design](../superpowers/specs/2026-10-05-native-server-design.md). Standards sources include `docs/design.md` and the code-review skill's code-smell baseline. The reviewers checked the implementation, then reviewed the fixes below. - -## Standards - -The reviewer found no hard coding-standard violations or actionable code smells. The correctness review found that an ordinary HIP window error could panic and abort the release server. The new Session path returns errors from world creation, simulator creation, world replacement, network upload, state transfers and window execution. Unsupported scenes and networks fall back during initialization or verification. - -The follow-up review found leaked resources after partial GPU constructor failures. Constructors now retain ownership before copying and release partial allocations before returning null. World cleanup excludes borrowed host pointers; simulator cleanup leaves its borrowed World intact. - -The final review reports no remaining correctness findings, hard coding-standard violations or actionable code smells. - -## Spec - -The reviewer found that a hidden layer wider than HIP's 16 inference lanes could abort the server rather than fall back. The HIP path now validates its network limits before upload; verification restores the initial runner state before CPU fallback. Real-socket regressions cover a 32-wide hidden layer and a scene without physics shapes. - -The device-name export was incorrectly mandatory. Older compatible libraries now probe device availability through the legacy math function and report the loaded library path. A real-socket HIP training test passed with a library that lacks the name export. - -The worker now ignores queued pause and resume commands after a run fails or finishes. A deterministic browser test injects an ordinary native RPC error, checks the saved failed state, and resumes from its checkpoint. Removing the guards makes that test fail. - -The final review confirms the constructor cleanup and fault-injection coverage, with no remaining high-confidence spec findings. - -## Initial validation - -- Rust debug and release suites: 173 passed and five existing tests ignored in each. -- Real-socket server suite: ten passed, including CPU parity, binary/JSON checkpoints, invalid requests, Origin/Host checks, HIP claim release and unsupported HIP fallback. -- Actual RX 7900 XTX Session checks passed for CPU/HIP parity, restart, restore, track replacement, population growth and fallback. -- Constructor cleanup passed all 38 World and 21 simulator injected failures, plus successful and null destruction. The same harness detects a leak in the previous library. ROCm CI runs it without a GPU. -- Native and WASM Clippy, Rust 1.93 all-target compilation, formatting and rustdoc passed. -- Serial and threaded WASM builds passed. CPU reference comparison checked 2,773 values with zero mismatches. -- Webapp build, TypeScript, 31 unit tests, immutable asset publication and formatting passed. -- Full browser suite: 45 passed, five conditional tests skipped. All ten native integration cases passed. -- Hardware WebGPU training, partial-window CPU fallback and contact/reproduction parity checks passed. - -Remaining findings: Standards 0; Spec 0. Native/WASM last-bit differences remain tracked in [issue #3](https://github.com/Zorro909/driving_sim_rs/issues/3). - -## Size limits and platform follow-up - -The user requested removal of all size constraints for local simulator connections. The server now has no configured WebSocket frame or message ceiling. The companion frontend change is commit `432e7da` on `t3code/native-simulator-client`. Native settings bypass browser network, population, stage, file and checkpoint limits while retaining valid values and shared checkpoint field widths. Unsupported HIP shapes continue on native CPU. - -Large checkpoints, run metadata and imported tracks use 32 MiB IndexedDB chunks, with replacement and deletion in one transaction. Database version 4 closes older connections before writing the new format. Review found and fixed the unchunked track path, oversized plan edits after browser fallback, and incompatible readers in old tabs. Startup failures with no training history can restart when the local simulator returns. - -Both reviewers rereviewed these fixes with no remaining findings. The frontend build, TypeScript, formatting, 35 unit tests and immutable asset checks passed. The full browser suite passed 52 tests with five expected skips, including all 13 native cases and four storage cases. Storage regressions round-trip checkpoints, metadata and tracks above 127 MiB and check rollback, replacement, deletion and schema migration. - -Platform CI exposed x87 precision-dependent large-angle reduction on Windows, a host-specific Xavier checksum on macOS, and ARM default NaN signs. The fixes preserve captured bits rather than relax comparisons. The macOS Xavier oracle compares every parameter and RNG bit with same-host CPython. Session tests retain exact Linux GNU goldens and compare both execution modes with the legacy Runner on every host, including exact checkpoint replay. - -The final native release suite passed 176 tests with five existing tests ignored. All 35 vehicle tests passed under AArch64 QEMU after reproducing the macOS distance-sensor failure. Debug/release sensor checks and Darwin Clippy passed. Fresh serial/threaded WASM builds passed, with 2,773 CPU reference values and zero mismatches. The rebuilt frontend passed asset checks and all 13 native integration cases again. diff --git a/github-simulator/source/docs/server.md b/github-simulator/source/docs/server.md deleted file mode 100644 index 1f345bef25938e87c0b8fb679f962fc27b7c8f3f..0000000000000000000000000000000000000000 --- a/github-simulator/source/docs/server.md +++ /dev/null @@ -1,102 +0,0 @@ -# Local simulator server - -`altd-sim serve` exposes native `Session` simulation over a WebSocket. A client can use Rayon CPU threads or request the AMD HIP backend. The server implements the protocol for Drive Lab's planned native client; the webapp integration is a separate change. - -The server owns one session per connection. Clients own run storage, checkpoints, logs, stage changes and mutation schedules. Closing the socket drops its session. - -## Starting the server - -The default Cargo features include `server`. CPU use needs no ROCm installation. Build the HIP library separately using [the GPU guide](../gpu/README.md). - -```sh -cargo build --release --offline -target/release/altd-sim --threads 8 serve -target/release/altd-sim serve --port 47800 \ - --allow-origin http://127.0.0.1:5173 -``` - -| Flag | Default and effect | -| --- | --- | -| `--port N` | `47800`; binds only `127.0.0.1:N` | -| `--allow-origin ORIGIN` | `https://drivinglab.jectrum.de`; repeat to allow several origins, replacing the default list | -| `--threads N` | Global flag; sizes the Rayon pool shared by all CPU sessions, with CPU affinity as the default | -| `--math-profile PROFILE` | Global flag; the math profile of sessions whose options name none. Without it, the server detects the profile of its own machine | - -Allowed origins use `http://host[:port]` or `https://host[:port]`, with an ASCII hostname or IP address. Startup normalizes scheme and host case, compresses IPv6 addresses, removes a trailing slash and omits default ports. Paths, credentials, query strings and fragments are rejected. Startup prints the listening URL, allowed origins, CPU thread count, HIP availability and the default math profile. - -## Connection checks - -Connect to `ws://127.0.0.1:47800/v1`, substituting the configured port. The handshake returns HTTP 403 unless all checks pass: - -- The request path is `/v1`. -- `Origin` is present and exactly matches an allowed origin. Different schemes or ports do not match; `null` is rejected. -- `Host` names `127.0.0.1` or `localhost` with the listening port. Other hostnames are rejected to prevent DNS rebinding. - -There is no LAN binding, TLS or authentication token. These checks restrict browser callers; a local program can supply its own headers. At most 64 connections (including incomplete handshakes) may be active. Excess sockets are closed before spawning a worker. Handshake socket reads/writes time out after 5 seconds; established socket reads/writes after 10 minutes of blocked I/O. Long-running generation computation is not subject to those socket timeouts. Connection-worker spawn failures reject that connection while retaining the accept loop. Requests run in order on a dedicated connection thread, with no per-request timeout. Incoming frames and assembled messages are each limited to 64 MiB, including binary checkpoint headers/payloads. Equal limits allow a browser to send an admissible message in one frame: JavaScript WebSocket.send cannot select continuation framing. Larger messages must be split into protocol-level operations; fragmenting a message does not bypass its assembled limit. A rejected oversized frame/message closes only that connection. A generation may take minutes. - -## Protocol 1 - -The first frame is a JSON text greeting: - -```json -{"type":"hello","protocol":1,"version":"1.1.0","threads":8,"carStateStride":14,"carStateFields":["x","y","..."],"hip":{"available":true,"device":"AMD Radeon RX 7900 XTX","platform":"HIP"},"mathProfile":"proton","mathProfiles":["proton","win10-fma3","win11-fma3"]} -``` - -Read `carStateStride` and `carStateFields` from the greeting rather than hard-coding them. The example's fields are abbreviated. `platform` is `"HIP"` for the AMD library and `"CUDA"` for the NVIDIA one. When HIP is unavailable, `hip` instead contains `{"available":false,"reason":"..."}`. Availability means the process opened the library and device; a particular session can still fall back to CPU. `mathProfile` is the default math profile and `mathProfiles` lists every profile a session can choose; [math profiles](../math/README.md) documents their runtime variants. - -Requests use an unsigned integer `id`, an operation name and optional `args`: - -```json -{"id":7,"op":"advance","args":{"ticks":180,"stopWhenInactive":true}} -``` - -A successful reply contains the same `id`, its result in `ok` and the current `state`. An error reply contains `id` and `error`, without `state`. - -```json -{"id":7,"ok":180,"state":{"started":true,"generation":0,"tick":180,"population":256,"activeCount":256,"checkpointGeneration":0,"backend":"cpu","backendNote":null,"mathProfile":"proton"}} -``` - -`checkpointGeneration` identifies the restorable generation boundary, or is `null` before starting. It need not describe the current tick. `backend` reports what actually runs; `backendNote` explains a CPU fallback. `mathProfile` is the session's math profile. Clients can queue requests and match replies by `id`. - -### Operations - -| Operation | `args` | `ok` | -| --- | --- | --- | -| `create` | `scene`, `network`, `model` as JSON strings; optional `options` as a SessionOptions object | `{backend, backendNote, mathProfile}` | -| `start` | None; seeds training from the `create` network export | `null` | -| `startWithShape` | `shape` as an array of unsigned 32-bit integers | `null` | -| `restoreCheckpointBytes` | Binary payload, no arguments required | `null` | -| `restoreCheckpointJson` | `json` as a checkpoint JSON string | `null` | -| `advance` | `ticks`, `stopWhenInactive` | Executed ticks | -| `advanceGeneration` | `timeLimitTicks` | Executed ticks | -| `nextGeneration` | None | `{preservedCount, rewards}` | -| `setEvolutionSettings` | `json` as an evolution-settings JSON string; `algorithm` is `"ga"` or `"ars"` (see design notes) | `null` | -| `replaceTrack` | `scene` as a scene JSON string | `null` | -| `generationSummary` | None | `{bestIndex, bestScore, lapped, active, lapIndex, lapTime}`, optional `averageScore` and `worstScore` | -| `networkJson` | `index` | Network JSON string | -| `carStates` | None | Number of `f64` values, with a binary payload | -| `checkpointBytes` | None | `null`, with a binary payload | - -Call `create` once, then start or restore before advancing. `options.backend` accepts `"cpu"`, the default, or `"hip"`. `options.mathProfile` accepts `"proton"`, `"win10-fma3"` or `"win11-fma3"` and defaults to the server's profile; checkpoints restore only into sessions of the profile they were made with. Other SessionOptions fields retain their regular defaults and unknown fields are rejected. A second successful `create` on the same connection is an error. Unknown operations and malformed JSON return errors while leaving the connection usable. - -`start` uses the export's `weights` and `biases` as the seed, matching `train-scratch --init-network` and the browser simulator. GA breeds mutations of that seed; ARS samples around it as its initial search point. Without weights, `start` draws a Xavier seed from `shape` (or `summary.shape`). Without either weights or a shape, use `startWithShape`. Malformed weighted exports return an error before starting and leave the connection usable. - -### Binary frames - -`restoreCheckpointBytes` requests and `carStates` or `checkpointBytes` replies use a binary WebSocket frame: - -```text -[u32 little-endian JSON byte length][UTF-8 JSON header][raw payload] -``` - -The header has the same request or reply shape as a text frame. `carStates` payloads contain little-endian `f64` values, grouped by the greeting's stride and field order. Checkpoint payloads use the shared Session checkpoint format. All other operations use text frames. Invalid binary framing closes the connection. - -## HIP fallback and resume - -Only one session can hold the HIP device per process. Another HIP request runs on CPU with `backendNote: "another session is using the GPU"`. Library/device errors, native broadphase tracks and unsupported path sensors also cause CPU fallback with a reason. HIP requires physics shapes and at least two path points. Networks support up to 12 layers, 32 inputs and 16 neurons in each later layer; wider CPU networks fall back during verification. - -Before a HIP session's first driving window after a start or restore, it compares 12 ticks against CPU from the same state, then restores that state. A mismatch or verification error drops HIP and runs the requested window on CPU. Track replacement uploads the new world and repeats verification before driving. Read `state.backend` after advancing because it may change from the `create` result. - -HIP cars and agents stay on the device between windows; an `advance` or `advanceGeneration` reply reads back only the active car count. `generationSummary`, `carStates`, turnover, restores and track replacement read the population back into the CPU runner first; checkpoints are saved from the generation boundary and need no readback. Turnover breeds on CPU to retain the parent records in format-2 checkpoints, then uploads the new networks and cars on the next HIP window. HIP libraries without `altd_gpu_sim_active_count` read the population back after every window. Checkpoints can move between native CPU, HIP and WASM sessions. Native and WASM training have known last-bit differences, so continued training may diverge after switching engines. - -HIP errors after startup verification return request errors without retrying. The client should stop the run and resume from its last saved checkpoint. A closed connection loses the server's session; reconnect, create a new session and restore a saved checkpoint. Release builds use `panic = "abort"`, so a panic terminates the server and closes every connection. diff --git a/github-simulator/source/docs/superpowers/specs/2026-10-05-native-server-design.md b/github-simulator/source/docs/superpowers/specs/2026-10-05-native-server-design.md deleted file mode 100644 index 29c95ce241c8c768d1205e8577efc8991184c319..0000000000000000000000000000000000000000 --- a/github-simulator/source/docs/superpowers/specs/2026-10-05-native-server-design.md +++ /dev/null @@ -1,322 +0,0 @@ -# Native simulator server for Drive Lab - -Date: 2026-10-05 -Status: implemented and reviewed - -## Goal - -Drive Lab (`driving_webapp_public`) trains in the browser on WASM, optionally with the WebGPU raycaster, and that stays the default. Users who also run the native `altd-sim` binary should be able to train faster with it: on native CPU threads without the browser's 8-thread cap and WASM overhead, or on an AMD GPU through the existing HIP simulator. - -The browser talks to the native binary over a WebSocket on `127.0.0.1`. By default only `https://drivinglab.jectrum.de` may connect; a CLI argument replaces that list. - -### Success criteria - -- With `altd-sim serve` running and the setting enabled, a user can create a run with backend "Native CPU" or "Native GPU (HIP)". It trains, saves checkpoints and logs to IndexedDB, and pauses, resumes and changes stages exactly as WASM runs do. -- A native run's checkpoint resumes on WASM and the other way round. The format is shared; results are not bit-identical, see "Known limitation". -- No request reaches the server from an origin that is not allowed, or with a `Host` other than the loopback address and port. -- When the server is missing, unreachable or crashes, the user gets a clear message and the run falls back or resumes as the existing WebGPU and WASM-trap paths do. -- A HIP session matches a native CPU session exactly in car state, as the existing HIP checks require, or falls back to CPU with a reason. - -### Non-goals - -- The server does not own runs, storage or the stage, plateau and mutation-schedule logic; those stay in the webapp's coordinator worker. -- No remote or LAN access, TLS or authentication tokens. -- No HIP version of the parent-recording turnover (see "Turnover"); that is a later optimization. -- WASM/native bit-exactness is tracked separately in issue #3. - -## Architecture - -``` -Page (main thread) Coordinator worker altd-sim serve (127.0.0.1) - ├─ setting + status probe ──► (hello only, then close) ──────────► ├─ handshake: Origin + Host check - └─ TrainingWorker ──────────► simulation.worker.ts ├─ one thread per connection - ├─ training loop (unchanged) └─ one Session per connection - ├─ Engine interface ├─ backend: cpu (rayon) - │ ├─ WasmEngine (Simulation, WebGPU) └─ backend: hip (libaltd_gpu.so) - │ └─ RemoteEngine ── WebSocket ──► - └─ IndexedDB (runs, logs, checkpoints) -``` - -The server works as a remote `Simulation`. The worker keeps its training loop and swaps the local `Simulation` for an object that sends the same calls over the socket. - -## Part 1: HIP backend in `Session` (`driving_sim_rs`) - -### Native access to Session methods - -Some `Session` items exist only in WASM builds and must also be compiled for the server: - -- `options` field (`src/training/session.rs:76`) -- `set_evolution_settings` (`:313`) -- `replace_track` (`:653`) -- the `network` field the WASM build keeps - -Their cfg changes from "wasm" to "wasm or `server` feature". The server module lives in the library (`src/server/`) so it can use the `pub(crate)` methods directly. - -### Backend option - -`SessionOptions` gains `backend: "cpu" | "hip"` (camelCase JSON, default `"cpu"`). `deny_unknown_fields` stays. - -- On wasm32, `"hip"` is an error: "the HIP backend is only available in the native simulator". -- On native, `Session` gains `hip: Option`: - -```rust -struct HipState { - world: GpuWorld<'static>, - sim: GpuSim<'static>, -} -static GPU: OnceLock> = OnceLock::new(); // Gpu::open(None): ALTD_GPU_LIB or the default path -``` - -The `Gpu` handle is opened once per process and never dropped, so a `Session` can own `'static` GPU handles without self-borrowing. - -### When HIP is refused - -`Session::new` builds `HipState` only when all of these hold. Otherwise it keeps `hip = None` and records a reason that the server reports, and the session runs on CPU. - -- `GPU` opened successfully. -- The track does not use the native broadphase (`runner.physics` would be `Some`); `advance_window_gpu` rejects it. -- `runner.gpu_sim(...)` succeeds. It already rejects path sensors without `track.curve`. - -Population-dependent capacity is handled on the first window (below), so construction does not need the population. - -### Routing - -| Session call | CPU | HIP | -|---|---|---| -| `advance(ticks, stop)` | `runner.advance` | `runner.advance_window_gpu(sim, world, ticks, stop, None)` | -| `advance_generation(limit)` | `runner.advance_generation` | `runner.advance_generation_gpu(sim, world, limit)` | -| `next_generation()` | `next_generation_traced` | the same CPU path (see "Turnover") | -| `replace_track(scene)` | as today | as today, then `GpuWorld::new(gpu, &runner.world)` and `sim.set_world(&world)` | -| `start*`, `restore_checkpoint*`, `set_network_json` | as today | as today, then `sim.network_tag = None` | - -Each HIP window reads car and agent state back into `runner.agents`, so `car_states`, `generation_summary`, `network_json`, `metrics`, `active_count` and checkpoints read the runner as they do now. - -If the population is larger than `GpuSim`'s capacity, `GpuSim` is rebuilt with the new population before the window. - -### Turnover - -`next_generation_gpu` breeds on the GPU but does not produce the parent record (`Lineage`) that format 2 checkpoints store. A HIP session therefore breeds through `next_generation_traced` on the CPU, the same call CPU sessions make. The next window uploads the new networks because `network_tag` no longer matches. Breeding is small next to a generation of driving. A HIP turnover that also records parents can come later if profiling shows it matters. - -### Startup check - -A HIP session checks itself before its first real window, following `GpuSimulation::verify` in `src/wasm/sim/mod.rs:186-235`: - -1. Back up `runner.agents`, `tick` and `batch_index`. -2. Advance 12 ticks on the CPU and record the canonical state (`canonical_state`, moved from `src/wasm/sim` into shared native and WASM code). -3. Restore the backup, run 12 ticks on HIP, and compare canonical states exactly. -4. Restore the backup again. - -On a mismatch or HIP error, the session drops `HipState` and continues on CPU with the reason "HIP verification differs from the CPU simulator" (or the error). It runs once per session, on the first `advance` or `advance_generation` after `start*` or `restore*`. - -`Session` exposes `backend() -> "cpu" | "hip"` and `backend_note() -> Option`, so hosts can report what actually runs. - -### Errors - -HIP failures after the check, such as a failed window, are returned as `Err(String)` and not retried. The webapp fails the run, and resuming continues from the last checkpoint. - -## Part 2: `altd-sim serve` - -### Crate layout - -- Feature `server = ["cli", "dep:tungstenite"]`, added to `default`. `tungstenite` is the synchronous WebSocket crate without TLS features, so there is no async runtime. -- `src/server/mod.rs`: listener, handshake checks and the per-connection loop. -- `src/server/protocol.rs`: message types and frame encoding. -- `src/server/dispatch.rs`: operations to `Session` calls. -- `src/bin/altd-sim`: a `Serve` subcommand that parses flags and calls `altd_sim::server::run(config)`. - -### Flags - -``` -altd-sim [--threads N] serve [--port 47800] [--allow-origin ORIGIN]... -``` - -- `--port`: TCP port, default `47800`. The server binds `127.0.0.1:` only. -- `--allow-origin`: repeatable. When given, the list replaces the default `https://drivinglab.jectrum.de`. Values are normalized as `scheme://host[:port]`, and invalid values are rejected at startup. Local development uses `--allow-origin http://127.0.0.1:5173`. -- `--threads`: the existing global flag; it sizes the rayon pool all CPU sessions share. - -On startup the server prints the address, allowed origins, thread count and HIP availability. - -### Handshake checks - -`tungstenite::accept_hdr` with a callback that rejects with HTTP 403 and a short plain-text reason unless all hold: - -- The path is `/v1`. -- `Origin` is present and equals an allowed origin exactly (scheme, host and port). `null` is rejected. -- `Host` is `127.0.0.1:` or `localhost:`. - -### Greeting - -Sent as the first text frame after the handshake: - -```json -{"type":"hello","protocol":1,"version":"0.1.0","threads":32, - "carStateStride":N,"carStateFields":["..."], - "hip":{"available":true,"device":"..."} | {"available":false,"reason":"..."}} -``` - -Checking HIP availability calls `GPU.get_or_init` once per process. `device` comes from the HIP library if it exposes a name; otherwise it is the library path. - -### Requests and replies - -- Request (text frame): `{"id":7,"op":"advance","args":{"ticks":180,"stopWhenInactive":true}}` -- Success: `{"id":7,"ok":,"state":State}` -- Failure: `{"id":7,"error":""}` -- `State` is `{"started","generation","tick","population","activeCount","checkpointGeneration","backend","backendNote"}`. - -Requests are handled strictly in order on the connection's thread. Clients may send the next request before the reply arrives and match replies by `id`. - -### Binary frames - -Used by `restoreCheckpointBytes` (request), and by `checkpointBytes` and `carStates` (replies): - -``` -[u32 little-endian: JSON length][JSON header][raw payload] -``` - -The JSON header has the same shape as a text frame. `carStates` returns little-endian `f64` values. All other messages are text frames. - -### Operations - -| op | args | ok | -|---|---|---| -| `create` | `scene`, `network`, `model` (JSON strings), `options` (SessionOptions object, including `backend`) | `{backend, backendNote}` | -| `startWithShape` | `shape: number[]` | `null` | -| `restoreCheckpointBytes` | binary payload | `null` | -| `restoreCheckpointJson` | `json: string` | `null` | -| `advance` | `ticks`, `stopWhenInactive` | executed ticks | -| `advanceGeneration` | `timeLimitTicks` | executed ticks | -| `nextGeneration` | none | `{preservedCount, rewards: number[]}` | -| `setEvolutionSettings` | `json: string` | `null` | -| `replaceTrack` | `scene: string` | `null` | -| `generationSummary` | none | `{bestIndex, bestScore, lapped, active, lapIndex, lapTime}` | -| `networkJson` | `index` | string | -| `carStates` | none | binary `f64` payload | -| `checkpointBytes` | none | binary payload | - -Rules: - -- Any op before `create`, or a second `create`, is an error. -- Only one HIP session may exist per process at a time. A second `create` with `backend: "hip"` falls back to CPU with the note "another session is using the GPU". -- Unknown ops and malformed JSON are errors and leave the session usable. Malformed binary framing closes the connection. -- The session is dropped when the socket closes. - -### Panics and timeouts - -The release profile keeps `panic = "abort"`, so a panic ends the server. The client sees the socket close (see "Disconnect" below), and a session that panicked part-way would not be trustworthy anyway. There is no per-request timeout, because `advanceGeneration` can take minutes on large populations. - -## Part 3: webapp client (`driving_webapp_public`) - -### Setting and status - -- In `#compute-choices` (`src/views.ts` `computeChoices`, wired in `src/main.ts` `bindCompute`), above the backend radios: - - a "Use a local altd-sim" checkbox and a port input; - - help text with the command `altd-sim serve` and the note that the browser may ask once for local network access. -- The setting is stored in `localStorage` (`drivelab.native` = `{enabled, port}`) and is per machine. -- When enabled, the page opens a WebSocket to `ws://127.0.0.1:/v1`, reads `hello`, closes it, and shows one of: - - "Connected: altd-sim 0.1.0 · 32 threads · HIP: " - - "Not reachable on port N" - - "Update altd-sim: protocol N is not supported" - - It probes again when the setting changes and whenever the compute choices are drawn. The page timeout is 3 s. -- New radios: - - "Native CPU · N threads": enabled when connected. - - "Native GPU (HIP) · device": enabled when `hip.available`, otherwise disabled with the server's reason. - - Both carry the note "Results differ slightly from in-browser training", linking to issue #3. -- When the selected native backend becomes unavailable while the user is editing a draft, the draft switches to `"cpu"`, as WebGPU does in `main.ts:115`. - -### Domain and protocol - -- `Setup.backend` becomes `"cpu" | "webgpu" | "native-cpu" | "native-hip"`. -- `validateSetup` accepts the new values; run summaries and the "Computing on" line show "Native CPU, N threads" and "Native GPU (HIP), ". -- `StartCommand` gains `native?: { port: number }`, set by the page from the setting. -- The `engine` event's `backend` gains `"native-cpu" | "native-hip"` and an optional `device`. - -### Engine interface (`src/engine.ts`) - -```ts -interface Engine { - readonly started: boolean; readonly generation: number; readonly tick: number; - readonly population: number; readonly activeCount: number; - readonly checkpointGeneration: number | undefined; - startWithShape(shape: Uint32Array): Promise; - restoreCheckpointBytes(bytes: Uint8Array): Promise; - restoreCheckpointJson(json: string): Promise; - advance(ticks: number, stopWhenInactive: boolean): Promise; - advanceGeneration(timeLimitTicks: number): Promise; - nextGeneration(): Promise; - setEvolutionSettings(json: string): Promise; - replaceTrack(sceneJson: string): Promise; - generationSummary(): Promise; - networkJson(index: number): Promise; - carStates(): Promise; - checkpointBytes(): Promise; - free(): void; -} -``` - -- `WasmEngine` wraps `lib.Simulation` and holds the existing WebGPU logic (`prepareGpu`, `dropGpu`, the GPU-to-CPU fallback in `advance`). -- `RemoteEngine` opens the socket, checks `hello.protocol === 1`, sends `create`, and updates the cached getters from every reply's `state`. Its getters are synchronous because they return those cached values. -- `simulation.worker.ts` replaces `sim` and `gpu` with one `engine: Engine` and adds `await` to the calls that become asynchronous. The training loop, storage queue, stage logic and messages are otherwise unchanged. -- The WASM library still loads for every backend, because `trackScene` and `randomTrackScene` run in the worker. - -### Fallback and disconnect - -- **Server unreachable at `buildSession`, `hello` rejected, or `create` failing:** the run uses `WasmEngine` on CPU and emits an `engine` event with a note, for example "The local altd-sim could not be reached on port 47800, so this run trains in the browser. ". -- **HIP refused or failing its check:** the server reports `backend: "cpu"` with `backendNote`, and the worker emits `engine` `native-cpu` with that note. -- **Disconnect during a run:** `RemoteEngine` rejects all pending and future calls with "The native simulator closed the connection. Resuming continues from the last saved checkpoint." The worker treats this like a WASM trap: it sets `trapped`, so `publishRun(true)` skips `checkpointBytes`, marks the run failed, stores it, and reports the error. Resuming goes through the same start path, including the fallback above. - -### Content Security Policy - -Add `ws://127.0.0.1:*` to `connect-src` in `public/_headers` and in `vite.config.ts`. `deploy/deploy.sh` reads `_headers`, and `scripts/serve-test.mjs` reads the same policy. COOP/COEP do not apply to WebSocket connections. - -## Known limitation - -WASM and native CPU are not bit-exact (issue #3). Native CPU and HIP are expected to be exact with each other, and the startup check enforces it. Cross-engine resume works through the shared checkpoint format, but continued training diverges. The UI says so next to the native choices. - -## Testing - -### `driving_sim_rs` - -- **Unit:** - - origin normalization and matching: exact, different port, different scheme, trailing slash, `null`, missing; - - Host matching; - - binary frame encode and decode, including truncated frames; - - `SessionOptions` with `backend` (`"hip"` rejected on wasm by a cfg test). -- **Integration (`tests/server.rs`):** start the server in-process on port 0 and connect with a `tungstenite` client. - - A disallowed origin and a wrong Host get 403. - - `hello` contents. - - `create → startWithShape → advance → advanceGeneration → nextGeneration → generationSummary → carStates → checkpointBytes → restoreCheckpointBytes` matches a direct `Session` with the same inputs bit for bit. - - Ops before `create` and a second `create` return errors. - - A dropped socket frees the session. -- **HIP (skipped when `Gpu::open` fails):** - - a CPU and a HIP `Session` agree in canonical state through several `advance` windows, `advanceGeneration` and two turnovers; - - a checkpoint saved under HIP restores under CPU, and the other way round; - - fallback reasons for native broadphase and a missing curve; - - a second concurrent HIP session falls back to CPU. -- Existing tests: `cargo test`, the WASM parity harness, and a WASM build that confirms the cfg changes do not leak native-only code. - -### `driving_webapp_public` - -- Domain tests: `validateSetup` with the new backends. -- Playwright (`tests/native.spec.ts`): build `altd-sim` from `SIM_ROOT` and start `altd-sim serve --port --allow-origin http://127.0.0.1:`. - - The setting shows "Connected". - - A two-generation Native CPU run stores progress and a checkpoint, with the correct engine event. - - Setting enabled with no server: the run falls back to CPU with a note. - - Killing the server mid-run fails the run with the disconnect message; restarting the server and resuming continues from the checkpoint. - - CSP permits the socket. - - The HIP case is skipped unless the server reports HIP available. - -## Documentation - -- `docs/server.md`: purpose, flags, security model, the protocol (greeting, ops, frames) and fallback behavior. -- `docs/cli.md`: the `serve` subcommand. -- `docs/gpu.md`: HIP through `Session`, and CPU turnover with network re-upload. -- Webapp `README.md`: the setting, the CSP change and native testing. - -## Delivery order - -1. HIP backend in `Session`, with its tests. Independently useful; no server needed. -2. `altd-sim serve` and the protocol, with integration tests (CPU, then HIP). -3. Webapp client, CSP and Playwright tests. - -Each phase lands with green tests before the next starts. diff --git a/github-simulator/source/docs/wasm.md b/github-simulator/source/docs/wasm.md deleted file mode 100644 index 5936dae2a5dcf38164d1f7f5a24cd9ed20033ebe..0000000000000000000000000000000000000000 --- a/github-simulator/source/docs/wasm.md +++ /dev/null @@ -1,27 +0,0 @@ -# WASM and WebGPU fidelity notes - -The serial and threaded packages compile the same Session and TrainingRunner code. The threaded package rebuilds Rust's standard library with atomics and uses `wasm-bindgen-rayon`; it requires an initialized worker pool before constructing worlds or invoking Rayon. Both packages preserve the existing arithmetic and RNG algorithms. - -Three comparisons answer different questions: - -- Native versus WASM state/reference checks catch portability differences in the exported fields. -- Serial versus threaded WASM checks cover states, sensors, controls, metrics, rewards, summaries and binary checkpoints, including exchange between the two packages. -- WebGPU versus WASM checks validate query or full-window device results against the WASM CPU implementation. - -Expanded native parity is incomplete. On the public generated Formula scenario, serial and threaded WASM matched each other in 253,341 values per mode, including the optional score aggregates, but both differed from native in 2,285 values per mode. Those differences comprised 703 controls, 405 metrics and 1,177 checkpoint bytes. The original captured-scene baseline had 2,532 native differences with the same exact serial/threaded equality. Counts depend on the scenario, build and checkpoint representation. `STRICT_NATIVE=1` makes the browser test fail on any expanded native difference. - -## Device arithmetic - -The WebGPU raycaster keeps simulation on the CPU and casts rays or nearest-wall queries on-device. Explicit float32 rounding boundaries prevent fusion and reassociation. Division corrects a hardware estimate using an exact integer remainder, with an integer-loop fallback; square root and software binary64 division use the same principle. Full `GpuSimulation` also implements binary64 arithmetic in integer WGSL operations and keeps network accumulation ordered. - -Historical RX 7900 XTX checks with RADV 26.2.2 matched 65,536 rays and 65,536 nearest-wall points per scenario, and millions of in-simulation rays across Formula and three Rally scenes. A newer baseline repeated a smaller 4,000-query corpus and 119,743 in-simulation rays with zero differences. The public replacements contain four distinct generated tracks and native current-code references, not game-oracle captures. - -WGSL permits device-dependent subnormal and NaN behavior. Raw division fixtures check signed zero, subnormal boundaries, infinities and broad exponent coverage; NaN payloads are treated as equivalent in that arithmetic diagnostic. Simulation comparisons retain exact values and count differences without substituting CPU results. Results on one GPU do not validate every browser adapter. - -## Runtime and memory - -Threaded WASM uses imported shared memory and a 2 GiB growth ceiling. This is a maximum, not an initial allocation. Workers need stack and TLS memory; an application's separately loaded preview module has its own memory. Shared-memory packages require cross-origin isolation and complete generated helper snippets. - -A host must await GPU windows before mutating the Session. A failed full-GPU window keeps the last committed WASM state so it can continue on CPU. Paused physics and native shared broadphase are unsupported by full WebGPU. The raycaster has one readback per tick; full simulation batches bounded windows, which changes the population size at which device work can pay off. - -[The WASM guide](../wasm/README.md) contains build commands, initialization and the browser harness. [Performance notes](performance.md) distinguish CPU worker scaling, startup and WebGPU measurements. diff --git a/github-simulator/source/gpu/README.md b/github-simulator/source/gpu/README.md deleted file mode 100644 index e5a0c96ba03bebcddfcbc9fe4424b53c2ce7e2bf..0000000000000000000000000000000000000000 --- a/github-simulator/source/gpu/README.md +++ /dev/null @@ -1,105 +0,0 @@ -# GPU backends (AMD HIP, NVIDIA CUDA) - -The GPU library runs whole driving generations on an AMD GPU (HIP) or an NVIDIA GPU (CUDA, see below). Captured CPU/HIP checks compare simulation values, population parameters, RNG state and checkpoint bytes exactly. Rust owns evolution and checkpoint I/O; both backends can resume the same generation-boundary checkpoints. See [fidelity](../docs/fidelity.md) for the scope of that evidence. - -## Build and use - -Linux release archives include `libaltd_gpu.so` beside `altd-sim`, built with ROCm 7.1 for the targets in `release-targets`. They need a ROCm 7.x runtime that provides `libamdhip64.so.7`, but no compiler. `altd-sim gpu-info` reports the loaded library. To build the library yourself, install ROCm with `hipcc`, then build from the repository root: - -```sh -gpu/build.sh -cargo build --release --offline - -target/release/altd-sim --threads 8 train-scratch --gpu \ - --scene assets/scenes/rally_template.json --track-mode random \ - --random-track-settings assets/random_track_settings.json \ - --population 256 --generations 3 --ticks 600 --seed 1729 \ - --eliminate-on-wall --idle-eliminate --out-dir runs/gpu-example -``` - -`OUT` overrides the library output directory, default `target/gpu`. `GPU_ARCH` lists one or more architectures separated by commas or spaces, default `gfx1100` for RX 7900 XTX; `rocminfo` reports a GPU's name. `GPU_ARCH="$(cat gpu/release-targets)"` builds the release library for every consumer target. - -`--gpu` and `gpu-info` load `ALTD_GPU_LIB` when set. Otherwise they try `libaltd_gpu.so` beside the executable, then `target/gpu` of the checkout that built the binary. Load errors list each attempted path. - -| Targets | GPUs | -| --- | --- | -| gfx803, gfx900, gfx902, gfx906, gfx909, gfx90c | Polaris and Vega: RX 470-590, Vega 56/64, Radeon VII, Vega APUs | -| gfx1010-gfx1013 | RDNA1: RX 5500-5700 XT | -| gfx1030-gfx1036 | RDNA2: RX 6400-6950 XT, Steam Deck, Radeon 610M/680M | -| gfx1100-gfx1103, gfx1150-gfx1153 | RDNA3 and RDNA3.5: RX 7600-7900 XTX, Radeon 780M/890M, Strix Halo | -| gfx1200, gfx1201 | RDNA4: RX 9060/9070 | - -The ROCm 7.1 runtime officially supports fewer consumer GPUs than this list; the remaining targets are best effort. The captured CPU/HIP equivalence checks have run on gfx1100 only. The script verifies the checked-in double tables against their Rust bit patterns without rewriting tracked files. `python3 gpu/gen_tables.py --output PATH` writes a header explicitly; `--check` validates it. - -The compiler flags `-ffp-contract=off` and `-fhip-fp32-correctly-rounded-divide-sqrt` preserve separate multiply/add rounding and correctly rounded float32 division/square root. Explicit `fma` calls in Windows kernels still fuse and round once. Keep these flags when changing architecture. The native crate loads the library at runtime, so CPU-only builds do not require ROCm. - -## Math profiles - -Each library contains `proton`, `win10-fma3` and `win11-fma3`; a build selects GPU architectures, while each simulation selects its math profile. `--math-profile win11-fma3` works with `--gpu` on Linux, for example. Without that flag the CLI detects its host profile; resumed scratch runs retain the saved profile. Server sessions use `options.mathProfile`, defaulting to the server's profile. [Math profiles](../math/README.md) documents the shared Windows kernels and the existing musl implementations. - -## NVIDIA (CUDA) - -The same sources build for NVIDIA GPUs with `nvcc`; `sim/compat.h` maps the HIP runtime calls and warp helpers to CUDA. HIP and CUDA use the same library ABI and checkpoint formats: `--gpu`, `gpu-info` and `backend: "hip"` use whichever library is loaded. Math profiles require simulator ABI 10, so rebuild older libraries. Install the CUDA toolkit (13.x tested) and a host compiler (Visual Studio Build Tools on Windows, gcc or clang on Linux), then build from the repository root: - -```sh -gpu/build-cuda.sh # Linux, writes target/gpu/libaltd_gpu.so -gpu/build-cuda.ps1 # Windows, writes target/gpu/altd_gpu.dll -``` - -`OUT` overrides the output directory. `CUDA_ARCH` defaults to `native`, which builds for the GPUs of the build machine. Without a GPU, nvcc 13.4 warns and builds `sm_75` code and PTX instead, which newer GPUs compile at load time. To build for other GPUs, or several, list `sm_` targets separated by commas or spaces; `nvidia-smi --query-gpu=compute_cap --format=csv` reports the number, so 8.6 is `sm_86`. A list also embeds PTX for its newest target, which the driver compiles at load time for a newer GPU. CUDA 13 builds for Turing (`sm_75`, RTX 20 series) and newer only; older GPUs need a CUDA 12 toolkit, which is untested. Both scripts check the double tables with `gpu/gen_tables.py --check` (`python3` on Linux, the `py` launcher or `python` on Windows). The binary loads `ALTD_GPU_LIB`, then the library beside the executable, then `target/gpu`, as on AMD. `altd-sim gpu-info` runs one kernel when a GPU is present, so a library built for another GPU fails there with HIP error 209. - -nvcc rejects a Visual Studio newer than the toolkit supports. `gpu/build-cuda.ps1 -AllowUnsupportedCompiler` (or `CUDA_ALLOW_UNSUPPORTED_COMPILER=1`) builds anyway with a warning; NVIDIA has not validated that combination, so run the checks below before trusting the library. - -`-fmad=false` is the CUDA counterpart of `-ffp-contract=off`; keep it, and keep nvcc's default correctly rounded float32 division and square root (no `-use_fast_math`, no `-ftz=true`). NVIDIA arithmetic also replaces every NaN result with its canonical NaN, so the places where the CPU returns a NaN operand or the x86 default NaN call `altd_nan_operand` and `altd_invalid` from `sim/compat.h`, which build those bits explicitly on both vendors. - -All three math profiles passed primitive and simulation checks on an RTX 3060 Ti (sm_86, Linux, CUDA 13.0); see the [verification report](../math/verification.md). Earlier Proton baseline checks passed on an RTX 4070 Laptop (sm_89, Windows, CUDA 13.4), and on Linux on an RTX 2070 (sm_75, driver 580) and an RTX 3060 (sm_86, driver 595) with `build-cuda.sh` libraries from CUDA 13.4: `native`, and `sm_75,sm_86`. They also passed with a CUDA 13.0 `sm_75` library run through its PTX (`CUDA_FORCE_PTX_JIT=1`). On those three GPUs the baseline `train-scratch --gpu` checkpoints were byte-identical to the CPU's. The driver compiles PTX only from a toolkit no newer than itself, so PTX for newer GPUs needs a driver at least as new as the CUDA toolkit that built the library. Consumer NVIDIA GPUs run float64 at 1/64 of their float32 rate, which bounds the network forward pass. A Windows GPU that drives a display kills kernels that run for more than a couple of seconds; the windowed launches stay well below that in the checks above. - -## Execution model - -GPU driving uses tick-major execution for independent cars. Native shared broadphase and paused windows are unsupported. Generated CLI tracks disable shared broadphase. Track preparation runs on a CPU producer with a bounded queue; `--track-buffer-size` sets its depth, default eight. Population/network buffers stay allocated while each fresh track replaces geometry. - -## Validation and measurements - -```sh -cargo build --release --offline --example gpu_check -target/release/examples/gpu_check --help -target/release/examples/gpu_check schedules -``` - -The default check fixture is a fixed generated Rally track and seeded Xavier networks. `ALTD_GPU_SCENE`, `ALTD_GPU_SPAWN`, `ALTD_GPU_NETWORK`, `ALTD_GPU_MODEL` and `ALTD_GPU_CKPT` select explicit inputs or a saved population. No captured campaign scene is required. `gpu_check --help` lists primitive, query, sensing, inference, physics and generation checks. Exhaustive parts can take minutes. - -`gpu_check math` compares primitives under all three profiles with the CPU. `ALTD_GPU_MATH` selects the simulation fixture's profile, default `proton`. Run complete simulation checks for each profile on the library's actual GPU: - -```sh -for profile in proton win10-fma3 win11-fma3; do - ALTD_GPU_MATH="$profile" ALTD_GPU_LIB=target/gpu/libaltd_gpu.so \ - target/release/examples/gpu_check \ - rays sensors infer step stats window schedules reuse novelty turnover -done -``` - -Run benchmarks on an otherwise idle GPU: - -```sh -RAYON_NUM_THREADS=8 python3 gpu/benchmark.py \ - --library target/gpu/libaltd_gpu.so --cars 8192 32768 \ - --samples 5 --warmups 1 --no-elimination --output target/gpu-fixed.json -RAYON_NUM_THREADS=8 python3 gpu/benchmark.py --training \ - --library target/gpu/libaltd_gpu.so --cars 8192 --samples 5 \ - --output target/gpu-training.json -``` - -The runner keeps every sample and validates result digests. `--checkpoint DIR` selects a saved population; omitting it uses the fixed Xavier recipe. `--scene`, `--spawn-trace`, `--network`, `--model` and `--binary` override inputs. Fixed-window timing excludes fixture construction, reset, hashing and reproduction; training timing includes consecutive generation turnover. Elimination can finish early, so inspect executed ticks before comparing results. - -`gpu/profile.sh` collects phase timings, optional rocprofv3 traces/counters and diagnostic kernel builds. `--only 1,2`, `--sizes "8192 32768"`, `--checkpoint DIR` and `--output target/profile` select the work. Profiling changes execution behavior; final timing should run with profiling disabled. [Performance notes](../docs/performance.md) retain measured hardware results. - -| Variable | Effect | -| --- | --- | -| `ALTD_GPU_LIB` | Runtime shared-library path | -| `ALTD_GPU_MATH` | Math profile of `gpu_check` simulation fixtures | -| `ALTD_GPU_GRAPH=0` | Direct launches instead of HIP graph replay | -| `ALTD_GPU_SPLIT=0` | Fused instead of split physics | -| `ALTD_GPU_PROFILE=1` | Device phase timings; `host` reports host/wall time only | -| `ALTD_TRAIN_PROFILE=1` | CPU reproduction, installation and transfer timings | - -[Implementation notes](../docs/gpu.md) explain batching, graph lifetime, exact reduction order and memory costs. `gpu_engine_scan` is an exhaustive native/GPU trigonometry developer tool; it requires an explicit thread count and output header, and is not part of the regular test suite. diff --git a/github-simulator/source/gpu/benchmark.py b/github-simulator/source/gpu/benchmark.py deleted file mode 100644 index 787fdd919b2d847faf8c1da647de99bae49c9e54..0000000000000000000000000000000000000000 --- a/github-simulator/source/gpu/benchmark.py +++ /dev/null @@ -1,117 +0,0 @@ -#!/usr/bin/env python3 -"""Benchmark identical GPU generations; build examples/gpu_check first.""" -import argparse -import hashlib -import json -import os -from pathlib import Path -import statistics -import subprocess - - -def main(): - parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument("--library", type=Path, required=True) - parser.add_argument("--cars", type=int, nargs="+", default=[8192, 32768, 262144]) - parser.add_argument("--ticks", type=int, default=5400) - parser.add_argument("--samples", type=int, default=3) - parser.add_argument("--warmups", type=int, default=1) - parser.add_argument("--no-elimination", action="store_true") - parser.add_argument("--training", action="store_true", help="include turnover across consecutive generations") - parser.add_argument("--generations", type=int, default=3) - parser.add_argument("--binary", type=Path, help="gpu_check executable (also supports saved baselines)") - parser.add_argument("--checkpoint", type=Path, help="saved train-scratch population; otherwise use the fixed generated fixture") - parser.add_argument("--scene", type=Path, help="explicit scene instead of the generated track") - parser.add_argument("--spawn-trace", type=Path, help="spawn trace for an explicit scene") - parser.add_argument("--network", type=Path, help="sensor/control names") - parser.add_argument("--model", type=Path, help="vehicle sensor model") - parser.add_argument("--output", type=Path, required=True) - args = parser.parse_args() - for key, variable in [("checkpoint", "ALTD_GPU_CKPT"), ("scene", "ALTD_GPU_SCENE"), - ("spawn_trace", "ALTD_GPU_SPAWN"), ("network", "ALTD_GPU_NETWORK"), - ("model", "ALTD_GPU_MODEL")]: - value = getattr(args, key) - if value is not None: - os.environ[variable] = str(value.resolve()) - root = Path(__file__).resolve().parent.parent - library = args.library.resolve() - binary = args.binary.resolve() if args.binary else root / "target/release/examples/gpu_check" - if args.samples < 1 or args.warmups < 0 or args.generations < 1: - parser.error("samples and generations must be positive; warmups must be nonnegative") - if args.training and args.no_elimination: - parser.error("the training benchmark uses the fixture's elimination settings") - report = { - "benchmark": "training" if args.training else "fixed_generation", - "binary": str(binary), - "binary_sha256": hashlib.sha256(binary.read_bytes()).hexdigest(), - "library": str(library), - "library_sha256": hashlib.sha256(library.read_bytes()).hexdigest(), - "requested_ticks": args.ticks, - "rayon_threads": os.environ.get("RAYON_NUM_THREADS", "default"), - "graph": os.environ.get("ALTD_GPU_GRAPH", "default"), - "split": os.environ.get("ALTD_GPU_SPLIT", "default"), - "profile": os.environ.get("ALTD_GPU_PROFILE", "off"), - "training_profile": os.environ.get("ALTD_TRAIN_PROFILE", "off"), - "generations": args.generations if args.training else 1, - "samples": args.samples, - "warmups": args.warmups, - "eliminate": not args.no_elimination, - "results": [], - } - report["fixture_inputs"] = {key: os.environ[key] for key in [ - "ALTD_GPU_CKPT", "ALTD_GPU_SCENE", "ALTD_GPU_SPAWN", "ALTD_GPU_NETWORK", "ALTD_GPU_MODEL" - ] if key in os.environ} - if "ALTD_GPU_CKPT" in os.environ: - checkpoint = Path(os.environ["ALTD_GPU_CKPT"]) - metadata = json.loads((checkpoint / "checkpoint.json").read_text()) - report["checkpoint_sha256"] = hashlib.sha256((checkpoint / metadata["population_file"]).read_bytes()).hexdigest() - else: - report["population_recipe"] = {"initialization": "Xavier", "seed": 1, "shape": [20,16,16,16,16,12,12,8,5]} - for cars in args.cars: - env = dict(os.environ, ALTD_GPU_LIB=str(library), ALTD_GPU_BENCH_CARS=str(cars), - ALTD_GPU_BENCH_TICKS=str(args.ticks), ALTD_GPU_BENCH_SAMPLES=str(args.samples), - ALTD_GPU_BENCH_WARMUPS=str(args.warmups), - ALTD_GPU_BENCH_GENERATIONS=str(args.generations), - ALTD_GPU_BENCH_ELIMINATE=str(int(not args.no_elimination))) - command = [str(binary), "benchtrain" if args.training else "benchfixed"] - records = [] - for sample in range(args.warmups + args.samples if args.training else 1): - trial = [] - with subprocess.Popen(command, cwd=root, env=env, stdout=subprocess.PIPE, text=True) as process: - for line in process.stdout: - print(line, end="", flush=True) - if line.startswith("{"): - record = json.loads(line) - if args.training: - record.update(sample=sample, warmup=sample < args.warmups) - trial.append(record) - if process.wait(): - raise SystemExit(f"benchmark failed for {cars} cars") - if args.training and [r["generation"] for r in trial] != list(range(args.generations)): - raise SystemExit("missing training generations") - records.extend(trial) - if args.training: - generations = [] - for generation in range(args.generations): - runs = [r for r in records if r["generation"] == generation] - timed = [r for r in runs if not r["warmup"]] - if len(timed) != args.samples or len({r["digest"] for r in runs}) != 1: - raise SystemExit("missing samples or inconsistent training results") - generations.append({"generation": generation, "digest": runs[0]["digest"], **{ - "median_" + key: statistics.median(r[key] for r in timed) - for key in ["simulate_seconds", "turnover_seconds", "total_seconds"]}}) - timed = [sum(r["total_seconds"] for r in records if r["sample"] == sample) / args.generations - for sample in range(args.warmups, args.warmups + args.samples)] - result = {"cars": cars, "median_seconds": statistics.median(timed), "generations": generations, "runs": records} - else: - timed = [r["seconds"] for r in records if not r["warmup"]] - if len(timed) != args.samples or len({r["digest"] for r in records}) != 1: - raise SystemExit("missing samples or inconsistent results") - result = {"cars": cars, "median_seconds": statistics.median(timed), "runs": records} - report["results"].append(result) - args.output.parent.mkdir(parents=True, exist_ok=True) - args.output.write_text(json.dumps(report, indent=2) + "\n") - - -if __name__ == "__main__": - main() diff --git a/github-simulator/source/gpu/build-cuda.ps1 b/github-simulator/source/gpu/build-cuda.ps1 deleted file mode 100644 index 5dac335da98bcc0c07b73dfb7bb58592a507e211..0000000000000000000000000000000000000000 --- a/github-simulator/source/gpu/build-cuda.ps1 +++ /dev/null @@ -1,55 +0,0 @@ -# Build altd_gpu.dll (the GPU simulator parts) for NVIDIA GPUs into $OUT. -# CUDA_ARCH is native (the default: the GPUs of this machine; without one nvcc warns and uses -# sm_75) or one or more sm_ targets separated by commas or spaces. A list also embeds PTX for its -# newest target, which the driver compiles at load time for newer GPUs. -# -fmad=false: nvcc otherwise fuses a*b+c into FMA, which rounds once. Keep the default -# -prec-div=true -prec-sqrt=true -ftz=false and never use -use_fast_math. -# -AllowUnsupportedCompiler (or CUDA_ALLOW_UNSUPPORTED_COMPILER=1) passes -allow-unsupported-compiler -# to nvcc for a Visual Studio newer than the CUDA toolkit supports. NVIDIA has not validated that -# combination; run the CPU/GPU checks in gpu/README.md before trusting the result. -param( - [string]$Out = $(if ($env:OUT) { $env:OUT } else { Join-Path $PSScriptRoot "..\target\gpu" }), - [string]$Arch = $(if ($env:CUDA_ARCH) { $env:CUDA_ARCH } else { "native" }), - [switch]$AllowUnsupportedCompiler = ($env:CUDA_ALLOW_UNSUPPORTED_COMPILER -eq "1") -) -$ErrorActionPreference = "Stop" -New-Item -ItemType Directory -Force $Out | Out-Null -$Out = (Resolve-Path $Out).Path - -# The py launcher first: python3 on Windows is often the Microsoft Store placeholder. -$tables = Join-Path $PSScriptRoot "gen_tables.py" -if (Get-Command py -ErrorAction SilentlyContinue) { py -3 $tables --check } else { python $tables --check } -if ($LASTEXITCODE -ne 0) { throw "gen_tables.py --check failed" } - -if ($Arch -eq "native") { - $archFlags = "-arch=native" -} else { - $targets = @($Arch -split '[,\s]+' | Where-Object { $_ }) - if (-not $targets) { throw "CUDA_ARCH lists no targets" } - $bad = @($targets | Where-Object { $_ -notmatch '^sm_\d+$' }) - if ($bad) { throw "CUDA_ARCH target $($bad[0]) is not sm_NN (or the whole value native)" } - $numbers = @($targets | ForEach-Object { [int]($_ -replace '^sm_', '') }) - $newest = ($numbers | Measure-Object -Maximum).Maximum - $archFlags = (@($numbers | ForEach-Object { "-gencode=arch=compute_$_,code=sm_$_" }) + - "-gencode=arch=compute_$newest,code=compute_$newest") -join " " -} - -$unsupported = "" -if ($AllowUnsupportedCompiler) { - Write-Warning "building with -allow-unsupported-compiler: NVIDIA has not validated this Visual Studio with this CUDA toolkit" - $unsupported = "-allow-unsupported-compiler" -} - -# nvcc needs the MSVC host compiler environment; load it unless cl.exe is already on PATH. -$src = Join-Path $PSScriptRoot "sim\altd_gpu.hip" -$dll = Join-Path $Out "altd_gpu.dll" -$prefix = "" -if (-not (Get-Command cl -ErrorAction SilentlyContinue)) { - $vw = Join-Path ([Environment]::GetFolderPath("ProgramFilesX86")) "Microsoft Visual Studio\Installer\vswhere.exe" - $vs = & $vw -products * -latest -property installationPath - $prefix = "`"$(Join-Path $vs 'VC\Auxiliary\Build\vcvars64.bat')`" >nul && " -} -$cmd = "${prefix}nvcc -x cu -O3 -std=c++20 -fmad=false $unsupported $archFlags -shared -Xcompiler /MD -o `"$dll`" `"$src`"" -cmd /c $cmd -if ($LASTEXITCODE -ne 0) { throw "nvcc failed" } -Write-Host "built $dll for $Arch" diff --git a/github-simulator/source/gpu/build-cuda.sh b/github-simulator/source/gpu/build-cuda.sh deleted file mode 100644 index 84d1347b8ffeee2111ec15832735c9ecc6386951..0000000000000000000000000000000000000000 --- a/github-simulator/source/gpu/build-cuda.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash -# Build libaltd_gpu.so (the GPU simulator parts) for NVIDIA GPUs into $OUT. -# CUDA_ARCH is native (the default: the GPUs of this machine; without one nvcc warns and uses -# sm_75) or one or more sm_ targets separated by commas or spaces. A list also embeds PTX for its -# newest target, which the driver compiles at load time for newer GPUs. -# -fmad=false: nvcc otherwise fuses a*b+c into FMA, which rounds once. Keep the default -# -prec-div=true -prec-sqrt=true -ftz=false and never use -use_fast_math. -set -euo pipefail -HERE="$(cd "$(dirname "$0")" && pwd)" -OUT="${OUT:-$HERE/../target/gpu}" -mkdir -p "$OUT" -python3 "$HERE/gen_tables.py" --check -CUDA_ARCH="${CUDA_ARCH:-native}" -ARCH_FLAGS=() -if [ "$CUDA_ARCH" = native ]; then - ARCH_FLAGS=("-arch=native") -else - newest=0 - for arch in ${CUDA_ARCH//,/ }; do - if ! [[ "$arch" =~ ^sm_[0-9]+$ ]]; then - echo "CUDA_ARCH target $arch is not sm_NN (or the whole value native)" >&2 - exit 1 - fi - ARCH_FLAGS+=("-gencode=arch=compute_${arch#sm_},code=$arch") - if [ "${arch#sm_}" -gt "$newest" ]; then newest="${arch#sm_}"; fi - done - if [ "${#ARCH_FLAGS[@]}" -eq 0 ]; then - echo "CUDA_ARCH lists no targets" >&2 - exit 1 - fi - ARCH_FLAGS+=("-gencode=arch=compute_$newest,code=compute_$newest") -fi -nice -n 19 nvcc -x cu -O3 -std=c++20 -fmad=false "${ARCH_FLAGS[@]}" -shared -Xcompiler -fPIC \ - -o "$OUT/libaltd_gpu.so" "$HERE/sim/altd_gpu.hip" -echo "built $OUT/libaltd_gpu.so for ${CUDA_ARCH}" >&2 diff --git a/github-simulator/source/gpu/build.sh b/github-simulator/source/gpu/build.sh deleted file mode 100644 index acbf0f6a4e990cfcab6b3e3973221c4e313b2548..0000000000000000000000000000000000000000 --- a/github-simulator/source/gpu/build.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bash -# Build libaltd_gpu.so (the GPU simulator parts) into $OUT. -# GPU_ARCH lists one or more offload targets separated by commas or spaces. -# -ffp-contract=off: hipcc otherwise fuses a*b+c into FMA, which rounds once. -set -euo pipefail -HERE="$(cd "$(dirname "$0")" && pwd)" -OUT="${OUT:-$HERE/../target/gpu}" -mkdir -p "$OUT" -python3 "$HERE/gen_tables.py" --check -GPU_ARCH="${GPU_ARCH:-gfx1100}" -ARCH_FLAGS=() -for arch in ${GPU_ARCH//,/ }; do - ARCH_FLAGS+=("--offload-arch=$arch") -done -if [ "${#ARCH_FLAGS[@]}" -eq 0 ]; then - echo "GPU_ARCH lists no targets" >&2 - exit 1 -fi -nice -n 19 hipcc -O3 -ffp-contract=off "${ARCH_FLAGS[@]}" -fPIC -shared -std=c++20 -fhip-fp32-correctly-rounded-divide-sqrt \ - -o "$OUT/libaltd_gpu.so" "$HERE/sim/altd_gpu.hip" -lamdhip64 -echo "built $OUT/libaltd_gpu.so for ${ARCH_FLAGS[*]#--offload-arch=}" >&2 diff --git a/github-simulator/source/gpu/gen_tables.py b/github-simulator/source/gpu/gen_tables.py deleted file mode 100644 index b1c369dc782bc4b1951f45508310b4340aba26b8..0000000000000000000000000000000000000000 --- a/github-simulator/source/gpu/gen_tables.py +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env python3 -"""Translate src/math/double_math_tables.rs into gpu/sim/double_tables.h. - -Only bit patterns are copied, so the GPU tables equal the CPU ones exactly. -""" -import argparse -from pathlib import Path -import re -import sys - -HERE = Path(__file__).resolve().parent -source = (HERE.parent / "src/math/double_math_tables.rs").read_text() -out = [ - "// Generated by gpu/gen_tables.py from src/math/double_math_tables.rs. Do not edit.", - "#pragma once", - "#include ", -] - - -def c_type(rust): - dims = [] - while rust.startswith("["): - inner, count = re.fullmatch(r"\[(.*);(\d+)\]", rust).groups() - dims.insert(0, count) - rust = inner - base = {"f64": "double", "u64": "uint64_t"}[rust] - return base, "".join(f"[{d}]" for d in reversed(dims)) - - -def c_value(rust, top_level=True): - """Keep the header layout independent of Rust source formatting.""" - rust = re.sub(r"\s+", "", rust) - if rust.startswith("["): - values = [] - start = 1 - depth = 0 - for index, char in enumerate(rust[1:-1], start=1): - if char in "[(": - depth += 1 - elif char in "])": - depth -= 1 - elif char == "," and depth == 0: - values.append(c_value(rust[start:index], False)) - start = index + 1 - if start < len(rust) - 1: - values.append(c_value(rust[start:-1], False)) - separator = ",\n" if top_level else "," - edge = "\n" if top_level else "" - return "{" + edge + separator.join(values) + edge + "}" - if match := re.fullmatch(r"f64::from_bits\((0x[0-9a-f]+)\)", rust): - return f"__builtin_bit_cast(double, (uint64_t){match[1]}ull)" - if re.fullmatch(r"0x[0-9a-f]+|0", rust): - return rust + "ull" - raise ValueError(f"unsupported table value: {rust}") - - -for name, rust_type, value in re.findall(r"pub(?:\([^)]*\))? const (\w+): ([^=]+?) = (.*?);\n", source, re.S): - base, dims = c_type(rust_type.replace(" ", "")) - value = c_value(value) - out.append(f"__device__ constexpr {base} {name}{dims} = {value};") - -parser = argparse.ArgumentParser(description=__doc__) -parser.add_argument("--output", type=Path, default=HERE / "sim/double_tables.h") -parser.add_argument("--check", action="store_true", help="check the selected header without writing it") -args = parser.parse_args() -generated = "\n".join(out) + "\n" -if args.check: - if not args.output.is_file() or args.output.read_text() != generated: - parser.exit(1, f"{args.output} is stale; run python3 gpu/gen_tables.py\n") - print(f"verified {args.output}", file=sys.stderr) -else: - args.output.parent.mkdir(parents=True, exist_ok=True) - args.output.write_text(generated) - print(f"wrote {args.output}", file=sys.stderr) diff --git a/github-simulator/source/gpu/profile.sh b/github-simulator/source/gpu/profile.sh deleted file mode 100644 index 0c8026c1c14d238d42917f615410b330566a3617..0000000000000000000000000000000000000000 --- a/github-simulator/source/gpu/profile.sh +++ /dev/null @@ -1,225 +0,0 @@ -#!/usr/bin/env bash -# Interactive profiling wizard for the GPU simulator. Collects, into one -# directory, everything needed to see where a generation's time goes: -# -# 1 built-in per-phase GPU time (ALTD_GPU_PROFILE=1) and split/fused wall clock -# 2 per-kernel durations with rocprofv3 (kernel trace + stats) -# 3 hardware counters of the physics/sensor/forward kernels with rocprofv3 -# 4 step-section cycle counts (a -DALTD_STEP_TIMING library) and kernel resources -# 5 forward-pass attribution: diagnostic libraries with parts of the network -# kernel removed (-DALTD_FORWARD_DIAG), and the scalar tanh for an A/B -# -# Every step is optional and failures are recorded, not fatal. Run from anywhere: -# gpu/profile.sh # asks before each level -# gpu/profile.sh --yes # accept every default without asking -# gpu/profile.sh --only 1,2 # run only these levels, without asking -# gpu/profile.sh --sizes "8192 32768" # populations (default: 8192 32768 262144) -# gpu/profile.sh --checkpoint runs/example # optional saved population -# gpu/profile.sh --output target/profile # results directory -# -set -u -HERE="$(cd "$(dirname "$0")" && pwd)" -REPO="$(cd "$HERE/.." && pwd)" -cd "$REPO" - -YES=0; ONLY=""; SIZES="8192 32768 262144"; PROFILE_OUT="" -while [ $# -gt 0 ]; do - case "$1" in - --yes|-y) YES=1 ;; - --only) ONLY="$2"; shift ;; - --sizes) SIZES="$2"; shift ;; - --checkpoint) export ALTD_GPU_CKPT="$2"; shift ;; - --output) PROFILE_OUT="$2"; shift ;; - -h|--help) sed -n '2,19p' "$0"; exit 0 ;; - *) echo "unknown option $1" >&2; exit 2 ;; - esac - shift -done - -OUT="${PROFILE_OUT:-$REPO/prof-$(date +%Y%m%d-%H%M)}" -mkdir -p "$OUT" -OUT="$(cd "$OUT" && pwd)" -LOG="$OUT/README.txt" -CHECK="target/release/examples/gpu_check" -LIB="${ALTD_GPU_LIB:-$REPO/target/gpu/libaltd_gpu.so}" -export RAYON_NUM_THREADS="${RAYON_NUM_THREADS:-8}" -# Keep the integrated GPU (the Ryzen's gfx1036) out of every run. -export HIP_VISIBLE_DEVICES="${HIP_VISIBLE_DEVICES:-0}" ROCR_VISIBLE_DEVICES="${ROCR_VISIBLE_DEVICES:-0}" - -bold() { printf '\033[1m%s\033[0m\n' "$*"; } -note() { printf '%s\n' "$*" | tee -a "$LOG"; } -ask() { # ask "question" default(y|n) -> returns 0 for yes - local q=$1 d=$2 a - if [ "$YES" = 1 ]; then a=$d; else - if [ "$d" = y ]; then read -r -p "$q [Y/n] " a : runs, times, records the exit code; stdout+stderr to $OUT/.txt -run_logged() { - local name=$1; shift - local start=$SECONDS - printf ' %-44s' "$name" - "$@" > "$OUT/$name.txt" 2>&1 - local rc=$? - local secs=$((SECONDS - start)) - if [ $rc -eq 0 ]; then echo "ok (${secs}s)"; else echo "FAILED rc=$rc (${secs}s), see $name.txt"; fi - echo "$name: rc=$rc seconds=$secs :: $*" >> "$LOG" - return $rc -} -bench_env() { # bench_env cars eliminate [extra VAR=val ...] - local cars=$1 elim=$2; shift 2 - echo env ALTD_GPU_BENCH_CARS="$cars" ALTD_GPU_BENCH_ELIMINATE="$elim" ALTD_GPU_BENCH_WARMUPS=0 ALTD_GPU_BENCH_SAMPLES=1 "$@" -} - -bold "GPU profiling wizard, output in $OUT" -{ - echo "altd GPU profiling run $(date -Iseconds)" - echo "repo: $REPO git: $(git rev-parse --short HEAD 2>/dev/null) $(git status --short 2>/dev/null | wc -l) modified files" - echo "library: $LIB"; echo "checkpoint: ${ALTD_GPU_CKPT:-generated Xavier population}"; echo "sizes: $SIZES rayon threads: $RAYON_NUM_THREADS" - echo -} > "$LOG" - -# ---- preflight ------------------------------------------------------------- -echo; bold "Preflight" -missing=0 -for tool in cargo hipcc rocminfo python3; do - if command -v "$tool" >/dev/null; then echo " $tool: $(command -v "$tool")"; else echo " $tool: MISSING"; missing=1; fi -done -if command -v rocprofv3 >/dev/null; then HAVE_ROCPROF=1; echo " rocprofv3: $(command -v rocprofv3)"; else HAVE_ROCPROF=0; echo " rocprofv3: missing (levels 2 and 3 will be skipped)"; fi -[ $missing = 1 ] && { echo "install the missing tools first"; exit 1; } -if [ -n "${ALTD_GPU_CKPT:-}" ] && [ ! -f "$ALTD_GPU_CKPT/checkpoint.json" ]; then - echo " checkpoint: $ALTD_GPU_CKPT/checkpoint.json not found"; exit 1 -fi -echo " checkpoint: ${ALTD_GPU_CKPT:-generated Xavier population}" -{ rocminfo | grep -E "^\s*(Name|Marketing Name|Compute Unit|Max Clock Freq|Wavefront Size):" ; } > "$OUT/device.txt" 2>&1 -{ hipcc --version; echo; rocminfo | grep -m1 -i "ROCk\|version"; [ $HAVE_ROCPROF = 1 ] && rocprofv3 --version; } > "$OUT/toolchain.txt" 2>&1 -grep -m1 "Marketing Name" "$OUT/device.txt" | sed 's/^\s*/ /' -if [ ! -f "$LIB" ] || [ "$(find gpu/sim math/kernels gpu/build.sh -type f -newer "$LIB" | wc -l)" -gt 0 ]; then - if ask "GPU library missing or older than its sources or math kernels. Build it now with gpu/build.sh?" y; then run_logged build_library gpu/build.sh || exit 1; fi -fi -sha256sum "$LIB" >> "$LOG" -if [ ! -x "$CHECK" ] || [ "$(find src examples -newer "$CHECK" -name '*.rs' | wc -l)" -gt 0 ]; then - run_logged build_examples cargo build --release --offline --example gpu_check || exit 1 -fi -if ask "Run the exactness checks first (gpu_check math novelty turnover step, a few minutes)?" y; then - run_logged exactness_check "$CHECK" math novelty turnover step - grep -E "ALL OK|FAILURES|mismatch" "$OUT/exactness_check.txt" | tail -3 | sed 's/^/ /' -fi -echo " Close games and other GPU load now; the step kernels are latency sensitive." -ask "Continue?" y || exit 0 - -# ---- level 1: built-in phases --------------------------------------------- -if want_level 1 "built-in per-phase GPU time and split/fused wall clock"; then - note "level 1: ALTD_GPU_PROFILE=1 prints GPU seconds per phase and per 600 ticks; graphs are disabled in this mode" - for cars in $SIZES; do for elim in 0 1; do - run_logged "phases_${cars}_elim${elim}" $(bench_env "$cars" "$elim" ALTD_GPU_PROFILE=1) "$CHECK" benchfixed - grep -h "altd_gpu window:" "$OUT/phases_${cars}_elim${elim}.txt" | head -2 | sed 's/^/ /' - done; done - for cars in $SIZES; do [ "$cars" -le 32768 ] || continue - run_logged "phases_${cars}_elim1_fused" $(bench_env "$cars" 1 ALTD_GPU_PROFILE=1 ALTD_GPU_SPLIT=0) "$CHECK" benchfixed - done - note "level 1b: production-mode wall clock, split vs fused step (graph on, no profiling)" - : > "$OUT/split_vs_fused.jsonl" - for split in 1 0; do for cars in $SIZES; do [ "$cars" -le 32768 ] || continue; for elim in 0 1; do - run_logged "wall_split${split}_${cars}_elim${elim}" env ALTD_GPU_SPLIT=$split ALTD_GPU_BENCH_CARS=$cars ALTD_GPU_BENCH_ELIMINATE=$elim ALTD_GPU_BENCH_WARMUPS=1 ALTD_GPU_BENCH_SAMPLES=2 "$CHECK" benchfixed - grep '^{' "$OUT/wall_split${split}_${cars}_elim${elim}.txt" | sed "s/^{/{\"split\":$split,/" >> "$OUT/split_vs_fused.jsonl" - done; done; done -fi - -# ---- level 2: rocprofv3 kernel trace --------------------------------------- -if [ $HAVE_ROCPROF = 1 ] && want_level 2 "per-kernel durations with rocprofv3 (kernel trace + stats)"; then - note "level 2: rocprofv3 --kernel-trace --stats per run; graph mode unless kernels do not appear individually" - for cars in $SIZES; do for elim in 0 1; do - dir="$OUT/trace_${cars}_elim${elim}" - run_logged "trace_${cars}_elim${elim}" $(bench_env "$cars" "$elim") rocprofv3 --kernel-trace --stats --output-format csv -d "$dir" -o run -- "$CHECK" benchfixed - stats=$(find "$dir" -name '*kernel_stats.csv' 2>/dev/null | head -1) - if [ -z "$stats" ] || ! grep -q "w_step\|step_kernel" "$stats"; then - note " graph launches were not traced as kernels; repeating with ALTD_GPU_GRAPH=0" - run_logged "trace_${cars}_elim${elim}_direct" $(bench_env "$cars" "$elim" ALTD_GPU_GRAPH=0) rocprofv3 --kernel-trace --stats --output-format csv -d "${dir}_direct" -o run -- "$CHECK" benchfixed - stats=$(find "${dir}_direct" -name '*kernel_stats.csv' 2>/dev/null | head -1) - fi - [ -n "$stats" ] && { echo " top kernels by total time:"; sort -t, -k3 -g -r "$stats" 2>/dev/null | head -6 | cut -d, -f1-4 | sed 's/^/ /'; } - done; done -fi - -# ---- level 3: hardware counters ------------------------------------------- -if [ $HAVE_ROCPROF = 1 ] && want_level 3 "hardware counters of the step, sensor and forward kernels"; then - note "level 3: rocprofv3 --pmc passes over 300 ticks with direct launches" - rocprofv3 --list-avail > "$OUT/counters_available.txt" 2>&1 - # Candidate passes; names absent from this GPU's list are dropped, empty passes skipped. - passes=( - "SQ_WAVES SQ_BUSY_CYCLES SQ_WAVE_CYCLES GRBM_GUI_ACTIVE" - "SQ_INSTS_VALU SQ_INSTS_SALU SQ_INSTS_LDS SQ_INSTS_SMEM" - "SQ_INSTS_VMEM_RD SQ_INSTS_VMEM_WR SQ_INST_LEVEL_VMEM SQ_WAIT_INST_ANY" - "SQ_ACTIVE_INST_VALU SQ_ACTIVE_INST_VMEM SQ_ACTIVE_INST_LDS SQ_INST_LEVEL_LDS" - "GL2C_HIT GL2C_MISS TCP_TOTAL_CACHE_ACCESSES TCP_TCC_READ_REQ" - "SQ_LEVEL_WAVES SQ_INSTS_BRANCH SQ_INSTS_FLAT SQ_INST_CYCLES_VMEM_RD" - "SQ_INSTS_VALU_TRANS_F64 SQ_INSTS_VALU_FMA_F64 SQ_INSTS_VALU_ADD_F64 SQ_INSTS_VALU_MUL_F64" - ) - : > "$OUT/pmc.txt"; dropped="" - for pass in "${passes[@]}"; do - keep="" - for c in $pass; do if grep -qw "$c" "$OUT/counters_available.txt"; then keep="$keep $c"; else dropped="$dropped $c"; fi; done - [ -n "$keep" ] && echo "pmc:$keep" >> "$OUT/pmc.txt" - done - note " counter passes:"; sed 's/^/ /' "$OUT/pmc.txt" | tee -a "$LOG" - [ -n "$dropped" ] && note " not available on this GPU, dropped:$dropped" - for cars in $SIZES; do [ "$cars" = 32768 ] && continue - run_logged "counters_${cars}" $(bench_env "$cars" 0 ALTD_GPU_GRAPH=0 ALTD_GPU_BENCH_TICKS=300) rocprofv3 -i "$OUT/pmc.txt" --kernel-include-regex "w_step|sat_kernel|w_sensor|w_forward|w_stats|step_kernel" --output-format csv -d "$OUT/counters_${cars}" -o run -- "$CHECK" benchfixed - done -fi - -# ---- level 4: step sections and kernel resources --------------------------- -if want_level 4 "step-section cycle counts (timing build) and kernel resource usage"; then - note "level 4: a second library built with -DALTD_STEP_TIMING (atomics slow the step; only the ratios matter)" - FLAGS=(-O3 -ffp-contract=off --offload-arch="${GPU_ARCH:-gfx1100}" -fPIC -shared -std=c++20 -fhip-fp32-correctly-rounded-divide-sqrt) - TLIB="$REPO/target/gpu/libaltd_gpu_timing.so" - python3 gpu/gen_tables.py --check || exit 1 - if run_logged build_timing_library hipcc "${FLAGS[@]}" -DALTD_STEP_TIMING -o "$TLIB" gpu/sim/altd_gpu.hip -lamdhip64; then - : > "$OUT/step_sections.txt" - for cars in $SIZES; do [ "$cars" = 32768 ] && continue; for elim in 0 1; do - run_logged "sections_${cars}_elim${elim}" $(bench_env "$cars" "$elim" ALTD_GPU_LIB="$TLIB" ALTD_GPU_PROFILE=1) "$CHECK" benchfixed - { echo "== $cars cars, eliminate=$elim"; grep -E "step sections|altd_gpu window:" "$OUT/sections_${cars}_elim${elim}.txt"; } >> "$OUT/step_sections.txt" - done; done - fi - note " kernel resource usage (VGPRs, SGPRs, scratch, LDS, occupancy) of the production flags" - run_logged kernel_resources hipcc "${FLAGS[@]}" -Rpass-analysis=kernel-resource-usage -o "$OUT/.tmp_resources.so" gpu/sim/altd_gpu.hip -lamdhip64 - rm -f "$OUT/.tmp_resources.so" -fi - -# ---- level 5: forward-pass attribution ------------------------------------- -if want_level 5 "forward-pass attribution (diagnostic builds; their results are wrong by design)"; then - note "level 5: forward seconds of a 32K full run per variant. diag1 = no activation, diag2 = no weight traffic, diag3 = activation only, diag4 = layer loop only, diag5 = diag4 without layer barriers, branchy = scalar tanh, cumode = -mcumode build" - FLAGS=(-O3 -ffp-contract=off --offload-arch="${GPU_ARCH:-gfx1100}" -fPIC -shared -std=c++20 -fhip-fp32-correctly-rounded-divide-sqrt) - python3 gpu/gen_tables.py --check || exit 1 - : > "$OUT/forward_attribution.txt" - for variant in "current:" "cumode:-mcumode" "branchy:-DALTD_TANH_BRANCHY" "diag1:-DALTD_FORWARD_DIAG=1" "diag2:-DALTD_FORWARD_DIAG=2" "diag3:-DALTD_FORWARD_DIAG=3" "diag4:-DALTD_FORWARD_DIAG=4" "diag5:-DALTD_FORWARD_DIAG=5"; do - name=${variant%%:*}; define=${variant#*:} - DLIB="$REPO/target/gpu/libaltd_gpu_$name.so" - if [ "$name" = current ]; then DLIB="$LIB"; else - run_logged "build_$name" hipcc "${FLAGS[@]}" $define -o "$DLIB" gpu/sim/altd_gpu.hip -lamdhip64 || continue - fi - run_logged "forward_$name" $(bench_env 32768 0 ALTD_GPU_LIB="$DLIB" ALTD_GPU_PROFILE=1) "$CHECK" benchfixed - line=$(grep -h "altd_gpu window:" "$OUT/forward_$name.txt" | head -1) - echo "$name: $line" >> "$OUT/forward_attribution.txt"; echo " $name: ${line#*cars: }" - done -fi - -# ---- pack ------------------------------------------------------------------- -echo; bold "Done" -TAR="$OUT.tar.gz" -tar czf "$TAR" -C "$(dirname "$OUT")" "$(basename "$OUT")" -echo " results: $OUT" -echo " archive: $TAR ($(du -h "$TAR" | cut -f1)), send this file" -grep -c "rc=0" "$LOG" | sed 's/^/ steps succeeded: /' -grep "rc=[1-9]" "$LOG" | sed 's/^/ failed: /' -exit 0 diff --git a/github-simulator/source/gpu/release-targets b/github-simulator/source/gpu/release-targets deleted file mode 100644 index acb7f31ffef7e14533c7e46b8694b69074a6f114..0000000000000000000000000000000000000000 --- a/github-simulator/source/gpu/release-targets +++ /dev/null @@ -1,27 +0,0 @@ -gfx803 -gfx900 -gfx902 -gfx906 -gfx909 -gfx90c -gfx1010 -gfx1011 -gfx1012 -gfx1013 -gfx1030 -gfx1031 -gfx1032 -gfx1033 -gfx1034 -gfx1035 -gfx1036 -gfx1100 -gfx1101 -gfx1102 -gfx1103 -gfx1150 -gfx1151 -gfx1152 -gfx1153 -gfx1200 -gfx1201 diff --git a/github-simulator/source/gpu/sim/altd_gpu.hip b/github-simulator/source/gpu/sim/altd_gpu.hip deleted file mode 100644 index 9e1853d8e29e957b12e709c9215ec70e49fa7648..0000000000000000000000000000000000000000 --- a/github-simulator/source/gpu/sim/altd_gpu.hip +++ /dev/null @@ -1,1384 +0,0 @@ -#include -#include -#include -#include -#include -#include -// libaltd_gpu.so: GPU parts of the fidelity simulator behind a C ABI. -// Rust loads it with dlopen (src/gpu/hip.rs); examples/gpu_check/main.rs compares -// every part bit for bit with the CPU code. -#include "compat.h" -#include -#include -#include -#include "math.h" -#include "rays.h" -#include "sensors.h" -#include "step.h" -#include "stats.h" -#ifdef _WIN32 -// For GetThreadTimes. After the other headers: windows.h defines `near` and `far`, which the -// simulator headers use as variable names. -#ifndef NOMINMAX -#define NOMINMAX -#endif -#define WIN32_LEAN_AND_MEAN -#include -#undef near -#undef far -#endif - -using namespace altd; - -#define CHECK(call) do { hipError_t e_ = (call); if (e_ != hipSuccess) { \ - fprintf(stderr, "altd_gpu %s:%d %s\n", __FILE__, __LINE__, hipGetErrorString(e_)); return (int)e_; } } while (0) - -namespace { - -enum MathOp : int { - OP_NATIVE_SIN = 0, // f32 -> f32 - OP_NATIVE_COS = 1, // f32 -> f32 - OP_NATIVE_ATAN2 = 2, // (f32 y, f32 x) -> f32 - OP_MANAGED_SINCOS = 3, // f32 -> (f32, f32) - OP_DEXP = 4, // f64 -> f64 - OP_DPOW = 5, // (f64, f64) -> f64 - OP_ENGINE_SINCOS = 6, // f32 -> (f32, f32), with exceptions - OP_ENGINE_RAW = 7, // f32 -> (f32, f32), without exceptions - OP_GAME_TANH = 8, // f64 -> f64 -}; -// altd_gpu_math's op carries a math profile above these bits; it selects the -// variant of the sin, cos, atan2, exp, pow and tanh ops. -constexpr int OP_MASK = 0xff, OP_PROFILE_SHIFT = 8; - -__global__ void math_kernel(int op, int n, const void* in, void* out, uint32_t* err) { - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i >= n) return; - uint32_t e = 0, math = (uint32_t)op >> OP_PROFILE_SHIFT; - const float* fi = (const float*)in; - const double* di = (const double*)in; - float* fo = (float*)out; - double* dout = (double*)out; - switch (op & OP_MASK) { - case OP_NATIVE_SIN: fo[i] = profile_sin(math, fi[i], e); break; - case OP_NATIVE_COS: fo[i] = profile_cos(math, fi[i], e); break; - case OP_NATIVE_ATAN2: fo[i] = profile_atan2(math, fi[2 * i], fi[2 * i + 1]); break; - case OP_MANAGED_SINCOS: managed_sin_cos(fi[i], fo[2 * i], fo[2 * i + 1], e); break; - case OP_DEXP: dout[i] = profile_exp(math, di[i]); break; - case OP_DPOW: dout[i] = profile_pow(math, di[2 * i], di[2 * i + 1]); break; - case OP_ENGINE_SINCOS: engine_sin_cos(fi[i], fo[2 * i], fo[2 * i + 1], e); break; - case OP_ENGINE_RAW: engine_sin_cos_raw(fi[i], fo[2 * i], fo[2 * i + 1]); break; - case OP_GAME_TANH: dout[i] = profile_tanh(math, di[i]); break; - } - err[i] = e; -} - -// Engine sin/cos of the float with bits `first + i` (wrapping), for the -// exhaustive x87 scan. Output: interleaved sine and cosine bits. -__global__ void engine_scan_kernel(uint32_t first, uint32_t n, uint32_t* out) { - uint32_t i = blockIdx.x * blockDim.x + threadIdx.x; - if (i >= n) return; - float s, c; - engine_sin_cos_raw(ffrom(first + i), s, c); - out[2 * (size_t)i] = fbits(s); - out[2 * (size_t)i + 1] = fbits(c); -} - -size_t in_size(int op) { - switch (op & OP_MASK) { - case OP_NATIVE_ATAN2: return 8; - case OP_DEXP: case OP_GAME_TANH: return 8; - case OP_DPOW: return 16; - default: return 4; - } -} -size_t out_size(int op) { - switch (op & OP_MASK) { - case OP_MANAGED_SINCOS: case OP_ENGINE_SINCOS: case OP_ENGINE_RAW: return 8; - case OP_DEXP: case OP_DPOW: case OP_GAME_TANH: return 8; - default: return 4; - } -} - -// Query kinds of altd_gpu_query. -enum QueryOp : int { - Q_RAYCAST = 0, // in (sx, sy, ex, ey) -> out (hx, hy, hit, 0) - Q_CLOSEST = 1, // in (px, py, -, -) -> out (cx, cy, 1, 0) -}; - -__global__ void query_kernel(World w, int op, int n, const float4* in, float4* out) { - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i >= n) return; - float4 q = in[i]; - float2 r = make_float2(0.0f, 0.0f); - bool ok = true; - if (op == Q_RAYCAST) ok = raycast(w, make_float2(q.x, q.y), make_float2(q.z, q.w), r); - else r = closest_wall_point(w, make_float2(q.x, q.y)); - out[i] = make_float4(r.x, r.y, ok ? 1.0f : 0.0f, 0.0f); -} - -template -int upload(const T* host, size_t count, const T*& device) { - T* d = nullptr; - device = nullptr; - if (count) { - CHECK(hipMalloc(&d, sizeof(T) * count)); - // The owner must retain this allocation if copying fails. - device = d; - CHECK(hipMemcpy(d, host, sizeof(T) * count, hipMemcpyHostToDevice)); - } - return 0; -} - -int upload_grid(const NearGrid& host, NearGrid& device) { - size_t cells = host.start ? (size_t)host.nx * host.ny : 0; - size_t items = cells ? host.start[cells] : 0; - return upload(host.start, cells ? cells + 1 : 0, device.start) || upload(host.items, items, device.items); -} - -// ---- simulation ---- - -// Network layer widths, and for each control (Agent::controls order: -// acceleration, steering, brake, handbrake, boost) the output neuron that -// sets it: the last one with that name, or -1 for 0.0. -struct NetShape { - uint32_t layers, width[MAX_LAYERS]; - int32_t control_src[5]; -}; - -} // namespace - -// Sim and WindowArgs stay outside the anonymous namespace: g++ (nvcc's host compiler) gives the -// extern "C" functions that take them internal linkage otherwise, and leaves them out of the library. -struct WindowArgs { - uint64_t start_tick, ticks; - uint32_t start_batch, batches_per_tick, stats_phase, stop_when_inactive; - uint32_t eliminate_on_wall, eliminate_when_idle, has_time_limit, pad; - double time_limit; -}; - -namespace { - -struct WindowState { - uint32_t live_count[2][8], step_count[2]; - uint32_t stopped; - uint32_t items; // SAT items of the split step (cleared by its end kernel) - uint64_t executed; // the stopping tick's k - uint64_t base; // k before the current period; kernels add their offset -}; - -uint32_t live_total(const WindowState& st, uint32_t p) { - uint32_t count = 0; - for (uint32_t b = 0; b < 8; b++) count += st.live_count[p][b]; - return count; -} - -} // namespace - -struct Sim { - World world; // device pointers, owned by the world handle - VehicleDesc vehicle; - Sensors sensors; - uint32_t capacity, n; - Car* cars; - Agent* agents; - float* here; // sensor path offset per car - double* inputs; // n * sensors.count, car-major - NetShape shape; - size_t stride; // parameters per network - double* params; // capacity * stride, one network per car - uint32_t network_count; - double* mean; - double* novelty; - // altd_gpu_sim_window: work lists, settling, device state, stop polling. - uint32_t* live[2]; - uint32_t* steps[2]; - uint32_t* settled_at; - Car* shadow; - WindowState* state; - // The split step: carried state per car, SAT items (car * MAX_PAIRS + pair) and their pushes. - StepCarry* carry; - uint32_t* items; - Vec2* pushes; - uint32_t* host_stopped; // pinned - uint32_t* active; // altd_gpu_sim_active_count's result - hipEvent_t chunk_done[4]; - hipStream_t stream; // blocking, so ordered with the null stream - hipGraphExec_t graph_exec; - WindowArgs graph_args; - uint32_t graph_n; -}; - -namespace { - -// Kernel arguments captured by the period graph. The requested window length -// only affects the host's replay count and is not part of the captured graph. -bool same_schedule(const WindowArgs& a, const WindowArgs& b) { - return a.start_tick == b.start_tick && a.start_batch == b.start_batch - && a.batches_per_tick == b.batches_per_tick && a.stats_phase == b.stats_phase - && a.stop_when_inactive == b.stop_when_inactive && a.eliminate_on_wall == b.eliminate_on_wall - && a.eliminate_when_idle == b.eliminate_when_idle && a.has_time_limit == b.has_time_limit - && a.time_limit == b.time_limit; -} - -// Pass 1 of the sensor read: the path offset (a nearest-segment search) per reading car. -__global__ void path_offset_kernel(World w, ReadMask m, Car* cars, uint32_t n, float* here) { - uint32_t i = blockIdx.x * blockDim.x + threadIdx.x; - if (i >= n || !reads(m, cars[i], i)) return; - here[i] = sensor_path_offset(w, cars[i]); -} - -// Pass 2: one thread per (sensor, car), sensor-major so a wave reads one sensor kind. -__global__ void sensor_kernel(World w, VehicleDesc v, Sensors s, ReadMask m, Car* cars, uint32_t n, - const float* here, double* inputs) { - uint32_t t = blockIdx.x * blockDim.x + threadIdx.x; - uint32_t k = t / n, i = t % n; - if (k >= s.count || !reads(m, cars[i], i)) return; - uint32_t err = 0; - inputs[(size_t)i * s.count + k] = sensor_value(w, v, cars[i], s.s[k], s.needs_path ? here[i] : 0.0f, err); - if (err) atomicOr(&cars[i].error, err); -} - -// `Network::forward_into` for the reading cars, then Controls(**outputs). -// A block holds CARS_PER_BLOCK cars. Within a layer of `cols` outputs, thread -// t computes output t % cols of the block's car t / cols, so the block's -// CARS_PER_BLOCK * cols outputs occupy the first threads and the waves past -// them skip the layer entirely. With one car per LANES threads, a 12-, 8- or -// 5-wide layer would leave 4, 8 or 11 idle lanes in every wave, and idle -// lanes still cost the double-precision issue slots that bound this kernel. -// Each output adds its products in input order, then the bias, as the CPU does. -constexpr int LANES = 16; -constexpr int CARS_PER_BLOCK = 16; -// ALTD_FORWARD_DIAG (timing builds only, results are wrong): 1 skips the -// activation, 2 reads one weight row for every input (no weight traffic), -// 3 skips the products and applies the activation to an input, 4 skips both -// (the layer loop's own cost), 5 additionally drops the per-layer barriers. -#ifndef ALTD_FORWARD_DIAG -#define ALTD_FORWARD_DIAG 0 -#endif -#if ALTD_FORWARD_DIAG == 5 -#define FORWARD_LAYER_BARRIER() ((void)0) -#else -#define FORWARD_LAYER_BARRIER() __syncthreads() -#endif -// Weight rows are loaded ROW_CHUNK at a time, one chunk ahead of the products -// that use them, and a layer's first chunk and bias during the previous -// layer's activation. Every row's weights are a fresh cache line streamed -// from memory, so without the lookahead each row waits for its own load. -constexpr uint32_t ROW_CHUNK = 8; - -// Rows first .. first + ROW_CHUNK - 1 of output j (zero past `rows` or when -// not `active`; nothing is read there). -__device__ inline void load_rows(double (&w)[ROW_CHUNK], const double* __restrict__ matrix, uint32_t rows, uint32_t cols, uint32_t j, - uint32_t first, bool active) { -#pragma unroll - for (uint32_t u = 0; u < ROW_CHUNK; u++) { - bool have = active && first + u < rows; -#if ALTD_FORWARD_DIAG == 2 - w[u] = have ? matrix[j] : 0.0; -#else - w[u] = have ? matrix[(size_t)(first + u) * cols + j] : 0.0; -#endif - } -} - -// Every thread of the block must call it (barriers); `car` and `live` are the -// caller's slot (threadIdx.x / LANES). -__device__ inline void forward_car(uint32_t math, const NetShape& shape, const double* __restrict__ params, size_t stride, bool live, - uint32_t car, const double* __restrict__ inputs, Agent* agents) { - // Two activation buffers: a layer reads one and writes the other, so one - // barrier per layer orders the writes before the next layer's reads (the - // previous barrier already ordered this layer's reads before those writes). - // Row stride of MAX_WIDTH + 1 doubles: the cars of one wave read the same - // column at the same time, and rows 256 bytes apart would share LDS banks. - __shared__ double buffers[2][CARS_PER_BLOCK][MAX_WIDTH + 1]; - __shared__ uint32_t slot_car[CARS_PER_BLOCK]; - __shared__ uint32_t slot_live[CARS_PER_BLOCK]; - uint32_t t = threadIdx.x, slot = t / LANES, lane = t % LANES; - uint32_t n_in = shape.width[0]; - if (lane == 0) { - slot_car[slot] = car; - slot_live[slot] = live; - } - for (uint32_t i = lane; live && i < n_in; i += LANES) buffers[0][slot][i] = inputs[(size_t)car * n_in + i]; - __syncthreads(); - uint32_t current = 0; // the buffer holding this layer's inputs - // This thread's assignment for the current layer: car slot s, output j. - uint32_t rows = shape.width[0], cols = shape.width[1]; - uint32_t s = t / cols, j = t % cols; - bool active = t < CARS_PER_BLOCK * cols && slot_live[s]; - const double* matrix = active ? params + (size_t)slot_car[s] * stride : params; - double w[ROW_CHUNK]; - load_rows(w, matrix, rows, cols, j, 0, active); - size_t position = 0; - for (uint32_t layer = 0; layer + 1 < shape.layers; layer++) { - double (&values)[CARS_PER_BLOCK][MAX_WIDTH + 1] = buffers[current]; - double acc = 0.0, bias = 0.0, out = 0.0; - if (active) { - bias = matrix[(size_t)rows * cols + j]; -#if ALTD_FORWARD_DIAG == 3 || ALTD_FORWARD_DIAG >= 4 - acc = values[s][j < rows ? j : 0]; -#else - for (uint32_t i = 0; i < rows; i += ROW_CHUNK) { - double next[ROW_CHUNK]; - load_rows(next, matrix, rows, cols, j, i + ROW_CHUNK, true); -#pragma unroll - for (uint32_t u = 0; u < ROW_CHUNK; u++) - if (i + u < rows) acc += values[s][i + u] * w[u]; -#pragma unroll - for (uint32_t u = 0; u < ROW_CHUNK; u++) w[u] = next[u]; - } -#endif - } - // The next layer's assignment and first rows, issued before this - // layer's activation so their latency overlaps it. - bool has_next = layer + 2 < shape.layers; - uint32_t rows_n = cols, cols_n = has_next ? shape.width[layer + 2] : 1; - uint32_t s_n = t / cols_n, j_n = t % cols_n; - bool active_n = has_next && t < CARS_PER_BLOCK * cols_n && slot_live[s_n]; - size_t position_n = position + (size_t)(rows + 1) * cols; - const double* matrix_n = active_n ? params + (size_t)slot_car[s_n] * stride + position_n : params; - load_rows(w, matrix_n, rows_n, cols_n, j_n, 0, active_n); - if (active) { -#if ALTD_FORWARD_DIAG == 1 || ALTD_FORWARD_DIAG >= 4 - out = acc + bias; -#else - out = profile_tanh(math, acc + bias); -#endif - } - if (active) buffers[current ^ 1][s][j] = out; - FORWARD_LAYER_BARRIER(); - current ^= 1; - rows = rows_n; cols = cols_n; s = s_n; j = j_n; active = active_n; matrix = matrix_n; position = position_n; - } - if (live && lane < 5) { - int32_t src = shape.control_src[lane]; - agents[car].controls[lane] = src >= 0 ? buffers[current][slot][src] : 0.0; - } -} - -__global__ void __launch_bounds__(LANES * CARS_PER_BLOCK) - forward_kernel(uint32_t math, NetShape shape, const double* __restrict__ params, size_t stride, ReadMask m, const Car* cars, - uint32_t n, const double* __restrict__ inputs, Agent* agents) { - uint32_t car = blockIdx.x * CARS_PER_BLOCK + threadIdx.x / LANES; - bool live = car < n && reads(m, cars[car], car); - forward_car(math, shape, params, stride, live, car, inputs, agents); -} - -// The agent's side of a step of car i: a reported wall contact sets -// AGENT_PENDING_CONTACT; a car deactivated by the step records `tick` -// (Schedule::drive_agent). -__device__ inline void record(const Car& car, Agent& agent, bool was_active, bool report, uint64_t tick) { - uint32_t flags = agent.flags | (report ? AGENT_PENDING_CONTACT : 0u); - if (was_active && !(car.flags & CAR_ACTIVE)) { - agent.deactivated_at = tick; - flags |= AGENT_HAS_DEACTIVATED; - } - agent.flags = flags; -} - -// `Car::step` (drive) or `Car::passive_step` of car i with the agent's controls. -__device__ inline void step_car(const World& w, const VehicleDesc& v, Car& car, Agent& agent, bool drive, bool eliminate_on_wall, - uint64_t tick) { - bool was_active = car.flags & CAR_ACTIVE; - bool report = car_step(w, v, car, agent.controls, drive, eliminate_on_wall); - record(car, agent, was_active, report, tick); -} - -constexpr uint32_t STEP_THREADS = 64; - -// Bound all physics kernels to their actual block size so the solver -// contact-index array reserves LDS for STEP_THREADS threads, rather than 1024. -__global__ void __launch_bounds__(STEP_THREADS) step_kernel(World w, VehicleDesc v, Car* cars, Agent* agents, uint32_t n, uint32_t drive, uint32_t eliminate_on_wall, - uint64_t tick) { - uint32_t i = blockIdx.x * blockDim.x + threadIdx.x; - if (i >= n) return; - step_car(w, v, cars[i], agents[i], drive != 0, eliminate_on_wall != 0, tick); -} - -// ---- the split step ---- -// -// `step_car` in three kernels, so the SAT tests of all broad pairs run side -// by side instead of in a loop per car: `step_begin` per car reserves one -// SAT item per broad pair (consecutive from the carry's base), the SAT kernel -// runs one item per thread, and `step_end` per car reads the pushes. The -// work per SAT thread varies only with its pair's axes, not with the pair -// counts of the other cars of the wave. - -// Reserves `count` consecutive slots of `*total` per lane, one atomic per -// wave; returns the lane's first slot. Every lane must call it. -__device__ inline uint32_t reserve(uint32_t count, uint32_t* total) { - uint32_t lane = altd_lane_id(), prefix = count; // inclusive scan over the wave - for (uint32_t o = 1; o < (uint32_t)warpSize; o <<= 1) { - uint32_t x = altd_shfl_up(prefix, o); - prefix += lane >= o ? x : 0u; - } - uint32_t sum = altd_shfl(prefix, warpSize - 1), base = 0; - if (lane == 0 && sum) base = atomicAdd(total, sum); - return altd_shfl(base, 0) + prefix - count; -} - -// Every lane must call it. -__device__ inline void begin_car(const World& w, const VehicleDesc& v, Car* cars, Agent* agents, bool valid, uint32_t i, bool drive, - StepCarry* carry, uint32_t* items, uint32_t* item_count) { - StepCarry k{}; - uint32_t count = 0; - if (valid) { - k = step_begin(w, v, cars[i], agents[i].controls, drive); - count = cars[i].pair_count; - } - uint32_t base = reserve(count, item_count); - if (!valid) return; - k.base = base; - carry[i] = k; - for (uint32_t c = 0; c < count; c++) items[base + c] = i * MAX_PAIRS + c; -} - -// The SAT items, grid-stride (the grid is sized for the cars, not the items). -__global__ void __launch_bounds__(STEP_THREADS) sat_kernel(World w, VehicleDesc v, Car* cars, const uint32_t* items, const WindowState* st, Vec2* pushes) { - uint32_t total = st->items; - for (uint32_t t = blockIdx.x * blockDim.x + threadIdx.x; t < total; t += gridDim.x * blockDim.x) { - uint32_t item = items[t]; - pushes[t] = sat_pair(w, v, cars[item / MAX_PAIRS], item % MAX_PAIRS); - } -} - -__device__ inline bool end_car(const World& w, const VehicleDesc& v, Car& car, Agent& agent, const StepCarry& k, const Vec2* pushes, - bool drive, bool eliminate_on_wall, uint64_t tick) { - bool report = step_end(w, v, car, k, [&](uint32_t ci) { return pushes[k.base + ci]; }, drive, eliminate_on_wall); - record(car, agent, k.was_active, report, tick); - return report; -} - -__global__ void __launch_bounds__(STEP_THREADS) step_begin_kernel(World w, VehicleDesc v, Car* cars, Agent* agents, uint32_t n, uint32_t drive, StepCarry* carry, - uint32_t* items, WindowState* st) { - uint32_t i = blockIdx.x * blockDim.x + threadIdx.x; - begin_car(w, v, cars, agents, i < n, i, drive != 0, carry, items, &st->items); -} - -__global__ void __launch_bounds__(STEP_THREADS) step_end_kernel(World w, VehicleDesc v, Car* cars, Agent* agents, uint32_t n, uint32_t drive, uint32_t eliminate_on_wall, - uint64_t tick, const StepCarry* carry, const Vec2* pushes, WindowState* st) { - uint32_t i = blockIdx.x * blockDim.x + threadIdx.x; - if (i == 0) st->items = 0; // the SAT kernel is done with them - if (i >= n) return; - end_car(w, v, cars[i], agents[i], carry[i], pushes, drive != 0, eliminate_on_wall != 0, tick); -} - -// Counts the cars with CAR_ACTIVE, one atomic per wave. -__global__ void active_count_kernel(const Car* cars, uint32_t n, uint32_t* count) { - uint32_t i = blockIdx.x * blockDim.x + threadIdx.x; - uint64_t mask = altd_ballot(i < n && (cars[i].flags & CAR_ACTIVE)); - if (mask && altd_lane_id() == (uint32_t)__ffsll((unsigned long long)mask) - 1) atomicAdd(count, (uint32_t)__popcll(mask)); -} - -uint32_t blocks(size_t n, uint32_t size = 256) { return (uint32_t)((n + size - 1) / size); } - -// ALTD_GPU_SPLIT=0: the step in one kernel (car_step) instead of the split step. -bool split_step() { - static const char* env = getenv("ALTD_GPU_SPLIT"); - static const bool split = !(env && strcmp(env, "0") == 0); - return split; -} - -// Host clocks of ALTD_GPU_PROFILE, in seconds. A negative thread CPU time is not printed. -double wall_seconds() { return std::chrono::duration(std::chrono::steady_clock::now().time_since_epoch()).count(); } -double thread_cpu_seconds() { -#ifdef _WIN32 - FILETIME created, exited, kernel, user; // 100 ns units - if (!GetThreadTimes(GetCurrentThread(), &created, &exited, &kernel, &user)) return -1.0; - ULARGE_INTEGER k, u; - k.LowPart = kernel.dwLowDateTime, k.HighPart = kernel.dwHighDateTime; - u.LowPart = user.dwLowDateTime, u.HighPart = user.dwHighDateTime; - return 1e-7 * (double)(k.QuadPart + u.QuadPart); -#else - timespec t; - clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t); - return (double)t.tv_sec + 1e-9 * (double)t.tv_nsec; -#endif -} - -// The step kernels need ~300 MB of scratch at full occupancy, over the HSA -// runtime's single-dispatch limit, so by default every launch allocates and -// frees it (0.2-1.7 ms per launch). Keeping the scratch avoids that. Runs at -// load, before the first HIP call initialises HSA; an explicit setting wins. -#if !defined(__CUDACC__) -__attribute__((constructor)) void keep_scratch() { setenv("HSA_NO_SCRATCH_RECLAIM", "1", 0); } -#endif - -} // namespace - -extern "C" { - -ALTD_API void altd_gpu_world_free(World* w); -ALTD_API void altd_gpu_sim_free(Sim* s); - -// Copies the track data to the device; returns an opaque handle or null. -// `desc` holds host pointers and has the layout of World. -ALTD_API World* altd_gpu_world_create(const World* desc) { - if (desc->ray_depth > (uint32_t)MAX_RAY_DEPTH) { - fprintf(stderr, "altd_gpu: BSP depth %u exceeds MAX_RAY_DEPTH %d\n", desc->ray_depth, MAX_RAY_DEPTH); - return nullptr; - } - World* w = new (std::nothrow) World(*desc); - if (!w) return nullptr; - // desc contains borrowed host pointers. Cleanup may only free pointers - // assigned by successful device allocations, including a failed copy. - w->ray_nodes = nullptr; - w->ray_walls = nullptr; - w->surfaces = nullptr; - w->tiles = nullptr; - w->path_segments = nullptr; - w->path_offsets = nullptr; - w->path_grid.start = nullptr; - w->path_grid.items = nullptr; - w->curve_position = nullptr; - w->curve_offsets = nullptr; - w->curve_forwards = nullptr; - w->curve_grid.start = nullptr; - w->curve_grid.items = nullptr; - w->shapes = nullptr; - w->shape_local = nullptr; - w->shape_normals = nullptr; - w->shape_grid.start = nullptr; - w->shape_grid.items = nullptr; - w->path_xy = nullptr; - int bad = upload(desc->ray_nodes, desc->ray_node_count, w->ray_nodes) - || upload(desc->ray_walls, desc->ray_wall_count, w->ray_walls) - || upload(desc->surfaces, desc->surface_count, w->surfaces) - || upload(desc->tiles, (size_t)(desc->tile_nx * desc->tile_ny), w->tiles) - || upload(desc->path_segments, desc->path_points ? desc->path_points - 1 : 0, w->path_segments) - || upload(desc->path_offsets, desc->path_points, w->path_offsets) - || upload_grid(desc->path_grid, w->path_grid) - || upload(desc->curve_position, desc->curve_points, w->curve_position) - || upload(desc->curve_offsets, desc->curve_points, w->curve_offsets) - || upload(desc->curve_forwards, desc->curve_points, w->curve_forwards) - || upload_grid(desc->curve_grid, w->curve_grid) - || upload(desc->shapes, desc->shape_count, w->shapes) - || upload(desc->shape_local, desc->shape_point_count, w->shape_local) - || upload(desc->shape_normals, desc->shape_point_count, w->shape_normals) - || upload_grid(desc->shape_grid, w->shape_grid) - || upload(desc->path_xy, (size_t)desc->path_points * 2, w->path_xy); - if (bad) { - fprintf(stderr, "altd_gpu: world upload failed\n"); - altd_gpu_world_free(w); - return nullptr; - } - return w; -} - -ALTD_API void altd_gpu_world_free(World* w) { - if (!w) return; - for (const void* p : {(const void*)w->ray_nodes, (const void*)w->ray_walls, (const void*)w->surfaces, - (const void*)w->tiles, (const void*)w->path_segments, (const void*)w->path_offsets, - (const void*)w->path_grid.start, (const void*)w->path_grid.items, - (const void*)w->curve_position, (const void*)w->curve_offsets, (const void*)w->curve_forwards, - (const void*)w->curve_grid.start, (const void*)w->curve_grid.items, - (const void*)w->shapes, (const void*)w->shape_local, (const void*)w->shape_normals, - (const void*)w->shape_grid.start, (const void*)w->shape_grid.items, (const void*)w->path_xy}) - if (p) (void)hipFree((void*)p); - delete w; -} - -// Runs `n` BSP queries (see QueryOp) on host arrays. -ALTD_API int altd_gpu_query(const World* w, int op, int n, const float4* in, float4* out) { - float4 *d_in, *d_out; - CHECK(hipMalloc(&d_in, sizeof(float4) * n)); - CHECK(hipMalloc(&d_out, sizeof(float4) * n)); - CHECK(hipMemcpy(d_in, in, sizeof(float4) * n, hipMemcpyHostToDevice)); - query_kernel<<<(n + 255) / 256, 256>>>(*w, op, n, d_in, d_out); - CHECK(hipGetLastError()); - CHECK(hipMemcpy(out, d_out, sizeof(float4) * n, hipMemcpyDeviceToHost)); - CHECK(hipFree(d_in)); - CHECK(hipFree(d_out)); - return 0; -} - - -// Struct sizes and a few offsets, for the Rust layout check. -ALTD_API int altd_gpu_layout(uint64_t* out, int n) { - uint64_t v[] = {sizeof(World), sizeof(Car), sizeof(Contact), sizeof(Agent), sizeof(VehicleDesc), sizeof(SensorDesc), - sizeof(WheelDesc), sizeof(ShapeDev), sizeof(TileCell), sizeof(PathSeg), sizeof(NearGrid), - offsetof(World, tiles), offsetof(World, curve_grid), offsetof(World, shape_grid), - offsetof(Car, tick), offsetof(Car, pairs), offsetof(Car, contacts), offsetof(Agent, recent), - offsetof(VehicleDesc, steering_speed), offsetof(VehicleDesc, gravity), offsetof(World, math_profile)}; - int count = (int)(sizeof(v) / sizeof(v[0])); - for (int i = 0; i < n && i < count; i++) out[i] = v[i]; - return count; -} - -ALTD_API Sim* altd_gpu_sim_create(const World* world, const VehicleDesc* vehicle, const SensorDesc* sensors, uint32_t sensor_count, - uint32_t capacity) { - if (sensor_count > (uint32_t)MAX_SENSORS || vehicle->wheel_count > (uint32_t)MAX_WHEELS) return nullptr; - Sim* s = new (std::nothrow) Sim{}; - if (!s) return nullptr; - s->world = *world; - s->vehicle = *vehicle; - s->sensors.count = sensor_count; - for (uint32_t k = 0; k < sensor_count; k++) { - s->sensors.s[k] = sensors[k]; - s->sensors.needs_path |= sensors[k].kind == S_CORRECT_DIRECTION || sensors[k].kind == S_TRACK_CURVATURE; - } - s->capacity = capacity; - if (hipMalloc(&s->cars, sizeof(Car) * capacity) || hipMalloc(&s->agents, sizeof(Agent) * capacity) - || hipMalloc(&s->here, sizeof(float) * capacity) || hipMalloc(&s->inputs, sizeof(double) * capacity * MAX_SENSORS) - || hipMalloc(&s->live[0], sizeof(uint32_t) * ((capacity + 7) / 8) * 8) || hipMalloc(&s->live[1], sizeof(uint32_t) * ((capacity + 7) / 8) * 8) - || hipMalloc(&s->steps[0], sizeof(uint32_t) * capacity) || hipMalloc(&s->steps[1], sizeof(uint32_t) * capacity) - || hipMalloc(&s->settled_at, sizeof(uint32_t) * capacity) || hipMalloc(&s->shadow, sizeof(Car) * capacity) - || hipMalloc(&s->carry, sizeof(StepCarry) * capacity) || hipMalloc(&s->items, sizeof(uint32_t) * capacity * MAX_PAIRS) - || hipMalloc(&s->pushes, sizeof(Vec2) * capacity * MAX_PAIRS) - || hipMalloc(&s->novelty, sizeof(double) * capacity) - || hipMalloc(&s->state, sizeof(WindowState)) || hipHostMalloc(&s->host_stopped, sizeof(uint32_t) * 4) - || hipMalloc(&s->active, sizeof(uint32_t)) - || hipEventCreateWithFlags(&s->chunk_done[0], hipEventDisableTiming) || hipEventCreateWithFlags(&s->chunk_done[1], hipEventDisableTiming) - || hipEventCreateWithFlags(&s->chunk_done[2], hipEventDisableTiming) || hipEventCreateWithFlags(&s->chunk_done[3], hipEventDisableTiming) - || hipStreamCreate(&s->stream)) { - fprintf(stderr, "altd_gpu: sim allocation failed\n"); - altd_gpu_sim_free(s); - return nullptr; - } - return s; -} - -struct RefreshedSensors { Sensors sensors; uint32_t error; }; - -// Ray endpoints belong to the world's math profile. Recompute once per profile -// switch, retaining the precomputed endpoints in the per-car sensing kernels. -__global__ void refresh_ray_offsets(Sensors sensors, uint32_t math, RefreshedSensors* out) { - uint32_t error = 0; - for (uint32_t i = 0; i < sensors.count; ++i) { - SensorDesc& sensor = sensors.s[i]; - if (sensor.kind != S_RAYCAST) continue; - float angle = (sensor.a - 90.0f) * (PI_F / 180.0f); - sensor.offset = Vec2{profile_cos(math, angle, error) * sensor.b, - profile_sin(math, angle, error) * sensor.b}; - } - out->sensors = sensors; - out->error = error; -} - -// Graph kernels capture world pointers by value. Invalidate the graph before -// releasing the old track, while retaining all population allocations. -ALTD_API int altd_gpu_sim_set_world(Sim* s, const World* world) { - CHECK(hipStreamSynchronize(s->stream)); - Sensors sensors = s->sensors; - if (s->world.math_profile != world->math_profile) { - struct DeviceBuffer { - RefreshedSensors* value = nullptr; - ~DeviceBuffer() { if (value) (void)hipFree(value); } - } buffer; - CHECK(hipMalloc(&buffer.value, sizeof(RefreshedSensors))); - refresh_ray_offsets<<<1, 1, 0, s->stream>>>(sensors, world->math_profile, buffer.value); - CHECK(hipGetLastError()); - CHECK(hipStreamSynchronize(s->stream)); - RefreshedSensors refreshed{}; - CHECK(hipMemcpy(&refreshed, buffer.value, sizeof(refreshed), hipMemcpyDeviceToHost)); - if (refreshed.error) return -static_cast(refreshed.error); - sensors = refreshed.sensors; - } - if (s->graph_exec) { - CHECK(hipGraphExecDestroy(s->graph_exec)); - s->graph_exec = nullptr; - } - s->sensors = sensors; - s->world = *world; - return 0; -} - -ALTD_API void altd_gpu_sim_free(Sim* s) { - if (!s) return; - if (s->graph_exec) (void)hipGraphExecDestroy(s->graph_exec); - for (void* p : {(void*)s->cars, (void*)s->agents, (void*)s->here, (void*)s->inputs, (void*)s->params, (void*)s->live[0], - (void*)s->live[1], (void*)s->steps[0], (void*)s->steps[1], (void*)s->settled_at, (void*)s->shadow, (void*)s->state, - (void*)s->carry, (void*)s->items, (void*)s->pushes, (void*)s->mean, (void*)s->novelty, (void*)s->active}) - if (p) (void)hipFree(p); - if (s->host_stopped) (void)hipHostFree(s->host_stopped); - for (hipEvent_t e : s->chunk_done) - if (e) (void)hipEventDestroy(e); - if (s->stream) (void)hipStreamDestroy(s->stream); - delete s; -} - -// Uploads `n` cars and (if not null) agents. -ALTD_API int altd_gpu_sim_upload(Sim* s, uint32_t n, const Car* cars, const Agent* agents) { - if (n > s->capacity) return -1; - s->n = n; - CHECK(hipMemcpy(s->cars, cars, sizeof(Car) * n, hipMemcpyHostToDevice)); - if (agents) CHECK(hipMemcpy(s->agents, agents, sizeof(Agent) * n, hipMemcpyHostToDevice)); - return 0; -} - -ALTD_API int altd_gpu_sim_upload_agents(Sim* s, uint32_t n, const Agent* agents) { - if (n != s->n) return -1; - CHECK(hipMemcpy(s->agents, agents, sizeof(Agent) * n, hipMemcpyHostToDevice)); - return 0; -} - -// The number of uploaded cars that are active, without reading them back. -ALTD_API int altd_gpu_sim_active_count(Sim* s, uint32_t* out) { - CHECK(hipMemsetAsync(s->active, 0, sizeof(uint32_t), s->stream)); - if (s->n) active_count_kernel<<n), 256, 0, s->stream>>>(s->cars, s->n, s->active); - CHECK(hipGetLastError()); - CHECK(hipMemcpyAsync(out, s->active, sizeof(uint32_t), hipMemcpyDeviceToHost, s->stream)); - CHECK(hipStreamSynchronize(s->stream)); - return 0; -} - -ALTD_API int altd_gpu_sim_download(Sim* s, Car* cars, Agent* agents) { - CHECK(hipDeviceSynchronize()); - if (cars) CHECK(hipMemcpy(cars, s->cars, sizeof(Car) * s->n, hipMemcpyDeviceToHost)); - if (agents) CHECK(hipMemcpy(agents, s->agents, sizeof(Agent) * s->n, hipMemcpyDeviceToHost)); - return 0; -} - -// Reads the sensors of the cars selected by `mask`; if `inputs` is not null, -// copies all n * sensor_count values back (unread cars keep stale values). -ALTD_API int altd_gpu_sim_sensors(Sim* s, ReadMask mask, double* inputs) { - if (s->n == 0) return 0; - if (s->sensors.needs_path) path_offset_kernel<<n, 64), 64>>>(s->world, mask, s->cars, s->n, s->here); - sensor_kernel<<n * s->sensors.count, 64), 64>>>(s->world, s->vehicle, s->sensors, mask, s->cars, s->n, - s->here, s->inputs); - CHECK(hipGetLastError()); - if (inputs) - CHECK(hipMemcpy(inputs, s->inputs, sizeof(double) * s->n * s->sensors.count, hipMemcpyDeviceToHost)); - return 0; -} - -// Installs one network per car: `layers` widths, then n * parameter_count(widths) -// f64 parameters (Network::params, car-major). `control_src` as in NetShape. -ALTD_API int altd_gpu_sim_networks(Sim* s, uint32_t layers, const uint32_t* width, uint32_t n, const double* params, - const int32_t* control_src) { - if (layers < 2 || layers > (uint32_t)MAX_LAYERS || n > s->capacity || width[0] != s->sensors.count) return -1; - NetShape shape{}; - shape.layers = layers; - size_t stride = 0; - for (uint32_t l = 0; l < layers; l++) { - if (width[l] == 0 || width[l] > (uint32_t)MAX_WIDTH || (l > 0 && width[l] > (uint32_t)LANES)) return -1; - shape.width[l] = width[l]; - if (l > 0) stride += (size_t)(width[l - 1] + 1) * width[l]; - } - for (int c = 0; c < 5; c++) { - if (control_src[c] >= (int32_t)width[layers - 1]) return -1; - shape.control_src[c] = control_src[c]; - } - // The graph embeds the network shape and parameter pointer. Parameter - // contents may change in place without invalidating it. - if (s->graph_exec && memcmp(&shape, &s->shape, sizeof shape) != 0) { - CHECK(hipGraphExecDestroy(s->graph_exec)); - s->graph_exec = nullptr; - } - if (stride != s->stride || !s->params) { - if (s->params) CHECK(hipFree(s->params)); - if (s->mean) CHECK(hipFree(s->mean)); - s->params = nullptr; - s->mean = nullptr; - CHECK(hipMalloc(&s->params, sizeof(double) * stride * s->capacity)); - CHECK(hipMalloc(&s->mean, sizeof(double) * stride)); - } - s->shape = shape; - s->stride = stride; - CHECK(hipMemcpy(s->params, params, sizeof(double) * stride * n, hipMemcpyHostToDevice)); - s->network_count = n; - return 0; -} - -// One thread per parameter preserves the CPU's left-to-right population sum. -// Only `stride` such threads exist (a few dozen waves), so with one load in -// flight per add the population's 8 bytes x n x stride stream far below the -// memory bandwidth. Each block therefore stages MEAN_ROWS networks' slice of -// its MEAN_COLS parameters in LDS with all MEAN_THREADS threads loading -// (coalesced rows, many loads in flight), then the MEAN_COLS summing threads -// add the staged rows in ascending network order, exactly as before. -constexpr uint32_t MEAN_COLS = 64, MEAN_ROWS = 64, MEAN_THREADS = 256; - -__global__ void __launch_bounds__(MEAN_THREADS) -population_mean_kernel(const double* params, size_t stride, uint32_t n, double* mean) { - __shared__ double tile[MEAN_ROWS][MEAN_COLS + 1]; - uint32_t t = threadIdx.x; - size_t col0 = (size_t)blockIdx.x * MEAN_COLS; - bool sums = t < MEAN_COLS && col0 + t < stride; - double sum = 0.0; - for (uint32_t row0 = 0; row0 < n; row0 += MEAN_ROWS) { - uint32_t rows = min(MEAN_ROWS, n - row0); - for (uint32_t e = t; e < rows * MEAN_COLS; e += MEAN_THREADS) { - uint32_t r = e / MEAN_COLS, c = e % MEAN_COLS; - if (col0 + c < stride) tile[r][c] = params[(size_t)(row0 + r) * stride + col0 + c]; - } - __syncthreads(); - if (sums) - for (uint32_t r = 0; r < rows; r++) sum += tile[r][t]; - __syncthreads(); - } - if (sums) mean[col0 + t] = sum / (double)n; -} - -// Parallelize the expensive pow calls, then add their results in the exact -// parameter order used by TrainingRunner::install. pow(x, 2) is the game's -// routine and must not be replaced by x*x or a parallel sum. -__global__ void __launch_bounds__(64) -population_novelty_kernel(uint32_t math, const double* params, size_t stride, const double* mean, double* novelty) { - __shared__ double terms[64]; - size_t car = blockIdx.x; - uint32_t lane = threadIdx.x; - double sum = 0.0; - for (size_t base = 0; base < stride; base += 64) { - size_t j = base + lane; - if (j < stride) terms[lane] = profile_pow(math, params[car * stride + j] - mean[j], 2.0); - __syncthreads(); - if (lane == 0) - for (uint32_t k = 0; k < 64 && base + k < stride; k++) sum += terms[k]; - __syncthreads(); - } - if (lane == 0) novelty[car] = sqrt(sum); -} - -ALTD_API int altd_gpu_sim_novelty(Sim* s, uint32_t n, double* novelty) { - if (n != s->network_count || !s->params) return -1; - if (n == 0) return 0; - population_mean_kernel<<stride, MEAN_COLS), MEAN_THREADS, 0, s->stream>>>(s->params, s->stride, n, s->mean); - population_novelty_kernel<<stream>>>(s->world.math_profile, s->params, s->stride, s->mean, s->novelty); - CHECK(hipGetLastError()); - CHECK(hipMemcpyAsync(novelty, s->novelty, sizeof(double) * n, hipMemcpyDeviceToHost, s->stream)); - CHECK(hipStreamSynchronize(s->stream)); - return 0; -} - -// Sensors and network for the cars in `mask`: sets their Agent::controls. -ALTD_API int altd_gpu_sim_infer(Sim* s, ReadMask mask) { - if (!s->params) return -1; - if (s->n == 0) return 0; - int status = altd_gpu_sim_sensors(s, mask, nullptr); - if (status) return status; - forward_kernel<<n, CARS_PER_BLOCK), LANES * CARS_PER_BLOCK>>>(s->world.math_profile, s->shape, s->params, s->stride, mask, s->cars, s->n, - s->inputs, s->agents); - CHECK(hipGetLastError()); - return 0; -} - -// Counts the cars still active afterwards into `active` if not null. -__global__ void stats_kernel(World w, Car* cars, Agent* agents, uint32_t n, uint64_t tick, uint64_t stats_tick, - uint32_t eliminate_when_idle, uint32_t* active) { - uint32_t i = blockIdx.x * blockDim.x + threadIdx.x; - if (i >= n) return; - uint32_t err = 0; - update_stats(w, cars[i], agents[i], tick, stats_tick, eliminate_when_idle != 0, err); - cars[i].error |= err; - if (active && (cars[i].flags & CAR_ACTIVE)) atomicAdd(active, 1u); -} - -// One physics tick for all cars (see step_kernel). Needs physics shapes -// (the shapeless resolve_walls path is not ported). -ALTD_API int altd_gpu_sim_step(Sim* s, uint32_t drive, uint32_t eliminate_on_wall, uint64_t tick) { - if (s->world.shape_count == 0) return -2; - if (s->n == 0) return 0; - if (!split_step()) { - step_kernel<<n, STEP_THREADS), STEP_THREADS>>>(s->world, s->vehicle, s->cars, s->agents, s->n, drive, eliminate_on_wall, tick); - CHECK(hipGetLastError()); - return 0; - } - CHECK(hipMemset(s->state, 0, sizeof(WindowState))); - step_begin_kernel<<n, STEP_THREADS), STEP_THREADS>>>(s->world, s->vehicle, s->cars, s->agents, s->n, drive, s->carry, s->items, s->state); - sat_kernel<<n, STEP_THREADS), STEP_THREADS>>>(s->world, s->vehicle, s->cars, s->items, s->state, s->pushes); - step_end_kernel<<n, STEP_THREADS), STEP_THREADS>>>(s->world, s->vehicle, s->cars, s->agents, s->n, drive, eliminate_on_wall, tick, s->carry, - s->pushes, s->state); - CHECK(hipGetLastError()); - return 0; -} - -// Schedule::update_stats for all cars on a statistics tick. -ALTD_API int altd_gpu_sim_stats(Sim* s, uint64_t tick, uint64_t stats_tick, uint32_t eliminate_when_idle) { - if (s->world.path_points < 2) return -2; // Track::closest_path needs a path - if (s->n == 0) return 0; - stats_kernel<<n, 64), 64>>>(s->world, s->cars, s->agents, s->n, tick, stats_tick, eliminate_when_idle, nullptr); - CHECK(hipGetLastError()); - return 0; -} - -// ---- whole windows ---- -// -// TrainingRunner::advance_window (not paused, no native broadphase), in the -// tick-major order: per tick the statistics (on statistics ticks, followed by -// the stop test), then the batch's sensors + forward, then the physics step. -// -// The host only enqueues: the stop test runs on the device and every later -// kernel of the window returns at once. Kernels walk compact work lists, -// double-buffered and rebuilt on statistics ticks: -// - live: the cars that may read sensors or change statistics (active, or -// with a pending contact at the window start); after each statistics -// update exactly the active cars. Cars deactivated by a wall stay listed -// until the next rebuild and are filtered by their active flag. Each live -// list has eight segments, retaining the cars' original inference batches. -// - step: the cars still stepped. An inactive car whose step leaves every -// field but `tick` bitwise unchanged is settled: `car_step` is a pure -// function of that state for inactive cars (controls, agent and tick are -// not read), so all its later steps repeat it. Settled cars leave the list -// and get the skipped `tick` increments when the window ends. -constexpr uint32_t NOT_SETTLED = 0xffffffffu; - -// Appends `value` where `take`, one atomic per wave; every lane must call it. -__device__ inline void append(bool take, uint32_t value, uint32_t* list, uint32_t* count) { - uint64_t mask = altd_ballot(take); - if (!mask) return; - uint32_t lane = altd_lane_id(); - uint32_t leader = (uint32_t)__ffsll((unsigned long long)mask) - 1; - uint32_t base = 0; - if (lane == leader) base = atomicAdd(count, (uint32_t)__popcll(mask)); - base = altd_shfl(base, leader); - if (take) list[base + (uint32_t)__popcll(mask & ((1ull << lane) - 1))] = value; -} - -// The Car state `car_step` reads: everything but `tick`, and the used pairs and contacts. -__device__ inline void snapshot(const Car& c, Car& s) { - altd_memcpy(&s, &c, offsetof(Car, pairs)); - altd_memcpy(s.pairs, c.pairs, sizeof(uint32_t) * c.pair_count); - altd_memcpy(s.axes, c.axes, sizeof(Vec2) * c.pair_count); - altd_memcpy(s.contacts, c.contacts, sizeof(Contact) * c.contact_count); -} - -__device__ inline bool same_words(const void* a, const void* b, size_t bytes) { - uint32_t differ = 0; - for (size_t k = 0; k < bytes; k += 4) { - uint32_t x, y; - altd_memcpy(&x, (const char*)a + k, 4); - altd_memcpy(&y, (const char*)b + k, 4); - differ |= x ^ y; - } - return differ == 0; -} - -__device__ inline bool same_state(const Car& s, const Car& c) { - if (s.pair_count != c.pair_count || s.contact_count != c.contact_count) return false; - return same_words(&s, &c, offsetof(Car, tick)) - && same_words((const char*)&s + offsetof(Car, tick) + 8, (const char*)&c + offsetof(Car, tick) + 8, - offsetof(Car, pairs) - offsetof(Car, tick) - 8) - && same_words(s.pairs, c.pairs, sizeof(uint32_t) * c.pair_count) - && same_words(s.axes, c.axes, sizeof(Vec2) * c.pair_count) - && same_words(s.contacts, c.contacts, sizeof(Contact) * c.contact_count); -} - -// Clears HAS_DEACTIVATED and builds the first live and step lists (index 0). -__global__ void w_start_kernel(Car* cars, Agent* agents, uint32_t n, uint32_t* live, uint32_t* steps, uint32_t* settled_at, - WindowState* st) { - uint32_t batch_size = (n + 7) / 8, batch = blockIdx.y; - uint32_t j = blockIdx.x * blockDim.x + threadIdx.x; - uint32_t i = batch * batch_size + j; - bool valid = j < batch_size && i < n, listed = false; - if (valid) { - uint32_t flags = agents[i].flags & ~AGENT_HAS_DEACTIVATED; - agents[i].flags = flags; - settled_at[i] = NOT_SETTLED; - listed = (cars[i].flags & CAR_ACTIVE) || (flags & AGENT_PENDING_CONTACT); - } - append(listed, i, live + batch * batch_size, &st->live_count[0][batch]); - append(valid, i, steps, &st->step_count[0]); -} - -// Statistics of the live cars in list p; the still active ones form list 1 - p. -__global__ void w_stats_kernel(World w, Car* cars, Agent* agents, const uint32_t* in, uint32_t* out, WindowState* st, uint32_t p, uint32_t batch_size, - uint64_t start_tick, uint32_t off, uint64_t first, uint32_t eliminate_when_idle) { - uint32_t j = blockIdx.x * blockDim.x + threadIdx.x; - uint32_t batch = blockIdx.y; - bool valid = !st->stopped && j < st->live_count[p][batch], keep = false; - uint32_t i = valid ? in[batch * batch_size + j] : 0; - if (valid) { - uint32_t err = 0; - uint64_t tick = start_tick + st->base + off; - update_stats(w, cars[i], agents[i], tick, tick - first, eliminate_when_idle != 0, err); - cars[i].error |= err; - keep = cars[i].flags & CAR_ACTIVE; - } - append(keep, i, out + batch * batch_size, &st->live_count[1 - p][batch]); -} - -// The stop test after the statistics of tick k (live list 1 - p holds the -// active cars); clears the counts the next rebuilds append to. -__global__ void w_stop_kernel(WindowState* st, uint32_t p, uint64_t start_tick, uint32_t off, uint64_t first, - uint32_t has_time_limit, double time_limit, uint32_t stop_when_inactive) { - if (st->stopped) return; - uint64_t k = st->base + off; - bool time_stop = has_time_limit && (double)((start_tick + k - first) / 6) * 0.1 >= time_limit; - uint32_t live = 0; - for (uint32_t b = 0; b < 8; b++) live += st->live_count[1 - p][b]; - if (time_stop || (stop_when_inactive && live == 0)) { - st->stopped = 1; - st->executed = k; - return; - } - for (uint32_t b = 0; b < 8; b++) st->live_count[p][b] = 0; - st->step_count[1 - p] = 0; -} - -// The live lists retain original batch identity. Each y slice handles one -// scheduled batch; z slices of the sensor grid handle individual sensors. -__global__ void w_path_offset_kernel(World w, ReadMask m, Car* cars, const uint32_t* list, const WindowState* st, uint32_t p, - float* here) { - uint32_t batch = (m.batch_index + blockIdx.y) % 8; - uint32_t j = blockIdx.x * blockDim.x + threadIdx.x; - if (st->stopped || j >= st->live_count[p][batch]) return; - uint32_t i = list[batch * m.batch_size + j]; - if (!(cars[i].flags & CAR_ACTIVE)) return; - here[i] = sensor_path_offset(w, cars[i]); -} - -__global__ void w_sensor_kernel(World w, VehicleDesc v, Sensors s, ReadMask m, Car* cars, const uint32_t* list, - const WindowState* st, uint32_t p, const float* here, double* inputs) { - uint32_t batch = (m.batch_index + blockIdx.y) % 8; - uint32_t j = blockIdx.x * blockDim.x + threadIdx.x, k = blockIdx.z; - if (st->stopped || j >= st->live_count[p][batch]) return; - uint32_t i = list[batch * m.batch_size + j]; - if (!(cars[i].flags & CAR_ACTIVE)) return; - uint32_t err = 0; - inputs[(size_t)i * s.count + k] = sensor_value(w, v, cars[i], s.s[k], s.needs_path ? here[i] : 0.0f, err); - if (err) atomicOr(&cars[i].error, err); -} - -__global__ void __launch_bounds__(LANES * CARS_PER_BLOCK) -w_forward_kernel(uint32_t math, NetShape shape, const double* __restrict__ params, size_t stride, ReadMask m, const Car* cars, - const uint32_t* list, const WindowState* st, uint32_t p, const double* __restrict__ inputs, Agent* agents) { - uint32_t batch = (m.batch_index + blockIdx.y) % 8; - uint32_t count = st->live_count[p][batch]; - if (st->stopped || blockIdx.x * CARS_PER_BLOCK >= count) return; // uniform per block - uint32_t j = blockIdx.x * CARS_PER_BLOCK + threadIdx.x / LANES; - uint32_t car = j < count ? list[batch * m.batch_size + j] : 0; - bool live = j < count && (cars[car].flags & CAR_ACTIVE); - forward_car(math, shape, params, stride, live, car, inputs, agents); -} - -// Steps the cars of step list q. With `out`, inactive cars are checked for -// settling and the others form step list 1 - q. -__global__ void __launch_bounds__(STEP_THREADS) w_step_kernel(World w, VehicleDesc v, Car* cars, Agent* agents, const uint32_t* in, uint32_t* out, WindowState* st, - uint32_t q, uint32_t* settled_at, Car* shadow, uint32_t eliminate_on_wall, uint64_t start_tick, - uint32_t off) { - uint32_t j = blockIdx.x * blockDim.x + threadIdx.x; - bool valid = !st->stopped && j < st->step_count[q], keep = valid; - uint32_t i = valid ? in[j] : 0; - if (valid) { - uint64_t k = st->base + off; // the step index: one step per tick - uint64_t tick = start_tick + k; - uint32_t step_index = (uint32_t)k; - Car& car = cars[i]; - bool check = out && !(car.flags & CAR_ACTIVE); - if (check) snapshot(car, shadow[i]); - step_car(w, v, car, agents[i], true, eliminate_on_wall != 0, tick); - if (check && same_state(shadow[i], car)) { - settled_at[i] = step_index; - keep = false; - } - } - if (out) append(keep, i, out, &st->step_count[1 - q]); -} - -// w_step_kernel split (see sat_kernel): begin, then the SAT items, then end. -__global__ void __launch_bounds__(STEP_THREADS) w_step_begin_kernel(World w, VehicleDesc v, Car* cars, Agent* agents, const uint32_t* in, WindowState* st, uint32_t q, - uint32_t settle, Car* shadow, StepCarry* carry, uint32_t* items) { - uint32_t j = blockIdx.x * blockDim.x + threadIdx.x; - if (st->stopped) return; // uniform - bool valid = j < st->step_count[q]; - uint32_t i = valid ? in[j] : 0; - if (valid && settle && !(cars[i].flags & CAR_ACTIVE)) snapshot(cars[i], shadow[i]); - begin_car(w, v, cars, agents, valid, i, true, carry, items, &st->items); -} - -__global__ void __launch_bounds__(STEP_THREADS) w_step_end_kernel(World w, VehicleDesc v, Car* cars, Agent* agents, const uint32_t* in, uint32_t* out, WindowState* st, - uint32_t q, uint32_t* settled_at, const Car* shadow, uint32_t eliminate_on_wall, uint64_t start_tick, - uint32_t off, const StepCarry* carry, const Vec2* pushes) { - uint32_t j = blockIdx.x * blockDim.x + threadIdx.x; - if (j == 0) st->items = 0; // the SAT kernel is done with them - bool valid = !st->stopped && j < st->step_count[q], keep = valid; - uint32_t i = valid ? in[j] : 0; - if (valid) { - uint64_t k = st->base + off; - const StepCarry& c = carry[i]; - bool check = out && !c.was_active; - end_car(w, v, cars[i], agents[i], c, pushes, true, eliminate_on_wall != 0, start_tick + k); - if (check && same_state(shadow[i], cars[i])) { - settled_at[i] = (uint32_t)k; - keep = false; - } - } - if (out) append(keep, i, out, &st->step_count[1 - q]); -} - -__global__ void w_advance_kernel(WindowState* st, uint32_t by) { st->base += by; } - -// Adds the skipped steps to the settled cars' tick. -__global__ void w_finish_kernel(Car* cars, uint32_t n, const uint32_t* settled_at, const WindowState* st, uint64_t ticks) { - uint32_t i = blockIdx.x * blockDim.x + threadIdx.x; - if (i >= n || settled_at[i] == NOT_SETTLED) return; - uint64_t steps = st->stopped ? st->executed - 1 : ticks; - cars[i].tick += steps - settled_at[i]; -} - -ALTD_API int altd_gpu_sim_window(Sim* s, const WindowArgs* a, uint64_t* executed, uint32_t* transition_without_drive) { - *executed = 0; - *transition_without_drive = 0; - if (a->ticks == 0) return 0; - if (!s->params || s->world.shape_count == 0 || s->world.path_points < 2) return -2; - uint32_t n = s->n; - hipStream_t stream = s->stream; - // ALTD_GPU_PROFILE=1: GPU time per phase (event pairs around direct launches). - // ALTD_GPU_PROFILE=host: only the host thread's CPU time and the wall time. - // ALTD_GPU_GRAPH=0: direct launches instead of the period graph. - static const char* profile_env = getenv("ALTD_GPU_PROFILE"); - static const bool profile = profile_env && strcmp(profile_env, "host") != 0; - static const char* graph_env = getenv("ALTD_GPU_GRAPH"); - static const bool use_graph = !profile && !(graph_env && strcmp(graph_env, "0") == 0); - const double cpu0 = profile_env ? thread_cpu_seconds() : 0, wall0 = profile_env ? wall_seconds() : 0; - double spent[4] = {0, 0, 0, 0}; // stats, sensors, forward, step - struct Timed { int slot; uint64_t k; hipEvent_t start, stop; }; - std::vector timed; - uint64_t enqueued = 0; // ticks enqueued so far (profile timeline) - auto phase = [&](int slot, auto&& launch) -> int { - if (!profile) { launch(); return 0; } - Timed t{slot, enqueued, nullptr, nullptr}; - CHECK(hipEventCreate(&t.start)); - CHECK(hipEventCreate(&t.stop)); - CHECK(hipEventRecord(t.start, stream)); - launch(); - CHECK(hipEventRecord(t.stop, stream)); - timed.push_back(t); - return 0; - }; - WindowState* st = s->state; - CHECK(hipMemsetAsync(st, 0, sizeof(WindowState), stream)); - if (n) w_start_kernel<<>>(s->cars, s->agents, n, s->live[0], s->steps[0], s->settled_at, st); - uint64_t first = a->stats_phase == 0 ? 6 : a->stats_phase; - // The time limit stops the window at a known tick; the host runs up to it. - uint64_t limit = a->ticks; - if (a->has_time_limit) - for (uint64_t k = 1; k <= a->ticks; k++) { - uint64_t tick = a->start_tick + k; - if (tick % 6 == a->stats_phase && (double)((tick - first) / 6) * 0.1 >= a->time_limit) { - limit = k; - break; - } - } - // Tick k = base + off of a period of PERIOD ticks: the statistics ticks, - // read batches and list parity depend only on off, so a period is one graph. - constexpr uint32_t PERIOD = 24; // lcm of the statistics (6) and batch (8) cycles - static_assert(PERIOD % 6 == 0 && PERIOD % 8 == 0, "period"); - auto enqueue_tick = [&](uint32_t off, uint32_t& p) { - enqueued++; - if (profile && enqueued % 600 == 1) { // list sizes along the window - WindowState now; - (void)hipMemcpyAsync(&now, st, sizeof now, hipMemcpyDeviceToHost, stream); // no CHECK: this lambda returns void - (void)hipStreamSynchronize(stream); - fprintf(stderr, " tick %5llu: live %u/%u, steps %u/%u\n", (unsigned long long)enqueued, live_total(now, 0), live_total(now, 1), - now.step_count[0], now.step_count[1]); - } - uint64_t tick_mod = a->start_tick + off; - uint32_t batch_index = (uint32_t)((a->start_batch + ((off - 1) % 8) * a->batches_per_tick) % 8); - bool stats = tick_mod % 6 == a->stats_phase; - if (stats) { - if (n) phase(0, [&] { - w_stats_kernel<<>>(s->world, s->cars, s->agents, s->live[p], s->live[1 - p], st, p, (n + 7) / 8, - a->start_tick, off, first, a->eliminate_when_idle); - w_stop_kernel<<<1, 1, 0, stream>>>(st, p, a->start_tick, off, first, a->has_time_limit, a->time_limit, - a->stop_when_inactive); - }); - else w_stop_kernel<<<1, 1, 0, stream>>>(st, p, a->start_tick, off, first, a->has_time_limit, a->time_limit, - a->stop_when_inactive); - p = 1 - p; - } - if (n) { - ReadMask mask{(uint32_t)((n + 7) / 8), batch_index, a->batches_per_tick, 1}; - phase(1, [&] { - if (s->sensors.needs_path) - w_path_offset_kernel<<>>(s->world, mask, s->cars, s->live[p], st, p, s->here); - w_sensor_kernel<<sensors.count), 64, 0, stream>>>(s->world, s->vehicle, s->sensors, mask, - s->cars, s->live[p], st, p, s->here, s->inputs); - }); - phase(2, [&] { - w_forward_kernel<<>>( - s->world.math_profile, s->shape, s->params, s->stride, mask, s->cars, s->live[p], st, p, s->inputs, s->agents); - }); - phase(3, [&] { - // On statistics ticks list 1 - p is read and list p rebuilt. - uint32_t q = stats ? 1 - p : p; - uint32_t* out = stats ? s->steps[p] : nullptr; - if (!split_step()) { - w_step_kernel<<>>(s->world, s->vehicle, s->cars, s->agents, s->steps[q], out, st, q, - s->settled_at, s->shadow, a->eliminate_on_wall, a->start_tick, off); - return; - } - w_step_begin_kernel<<>>(s->world, s->vehicle, s->cars, s->agents, s->steps[q], st, q, - out != nullptr, s->shadow, s->carry, s->items); - sat_kernel<<>>(s->world, s->vehicle, s->cars, s->items, st, s->pushes); - w_step_end_kernel<<>>(s->world, s->vehicle, s->cars, s->agents, s->steps[q], out, st, q, - s->settled_at, s->shadow, a->eliminate_on_wall, a->start_tick, off, - s->carry, s->pushes); - }); - } - }; - auto enqueue_period = [&] { - uint32_t p = 0; - for (uint32_t off = 1; off <= PERIOD; off++) enqueue_tick(off, p); - w_advance_kernel<<<1, 1, 0, stream>>>(st, PERIOD); - }; - uint64_t periods = limit / PERIOD, rest = limit % PERIOD; - hipGraphExec_t exec = nullptr; - if (use_graph && periods > 1) { - if (s->graph_exec && (s->graph_n != n || !same_schedule(s->graph_args, *a))) { - CHECK(hipGraphExecDestroy(s->graph_exec)); - s->graph_exec = nullptr; - } - if (!s->graph_exec) { - hipGraph_t graph; - CHECK(hipStreamBeginCapture(stream, hipStreamCaptureModeThreadLocal)); - enqueue_period(); - CHECK(hipStreamEndCapture(stream, &graph)); - CHECK(hipGraphInstantiate(&s->graph_exec, graph, nullptr, nullptr, 0)); - CHECK(hipGraphDestroy(graph)); - s->graph_n = n; - s->graph_args = *a; - } - exec = s->graph_exec; - } - // Polls the device stop flag every POLL periods, keeping DEPTH polls queued. - constexpr uint64_t POLL = 2; - constexpr int DEPTH = 3; - uint32_t* seen = s->host_stopped; // pinned, one slot per queued poll - for (int c = 0; c < DEPTH; c++) seen[c] = 0; - int polls = 0; - bool host_stop = false; - double launch_wall = 0, wait_wall = 0; // host profile: time in launches and in polls - for (uint64_t m = 0; m < periods && !host_stop; m++) { - double t0 = profile_env ? wall_seconds() : 0; - if (exec) CHECK(hipGraphLaunch(exec, stream)); - else enqueue_period(); - if (profile_env) launch_wall += wall_seconds() - t0; - CHECK(hipGetLastError()); - if (a->stop_when_inactive && (m + 1) % POLL == 0) { - int slot = polls % DEPTH; - CHECK(hipMemcpyAsync(&seen[slot], &st->stopped, sizeof(uint32_t), hipMemcpyDeviceToHost, stream)); - CHECK(hipEventRecord(s->chunk_done[slot], stream)); - polls++; - if (polls >= DEPTH) { // wait for the oldest queued poll - int oldest = polls % DEPTH; - double t1 = profile_env ? wall_seconds() : 0; - CHECK(hipEventSynchronize(s->chunk_done[oldest])); - if (profile_env) wait_wall += wall_seconds() - t1; - host_stop = seen[oldest] != 0; - } - } - } - if (!host_stop) { - uint32_t p = 0; - for (uint32_t off = 1; off <= rest; off++) enqueue_tick(off, p); - } - if (n) w_finish_kernel<<>>(s->cars, n, s->settled_at, st, a->ticks); - CHECK(hipGetLastError()); - WindowState result; - CHECK(hipMemcpyAsync(&result, st, sizeof result, hipMemcpyDeviceToHost, stream)); - CHECK(hipStreamSynchronize(stream)); - *executed = result.stopped ? result.executed : limit; - *transition_without_drive = result.stopped; - constexpr uint64_t SPAN = 600; // profile timeline bucket, ticks - std::vector line(4 * (limit / SPAN + 1), 0.0); - for (const Timed& t : timed) { - float ms = 0.0f; - CHECK(hipEventElapsedTime(&ms, t.start, t.stop)); - spent[t.slot] += ms * 1e-3; - line[4 * ((t.k - 1) / SPAN) + t.slot] += ms * 1e-3; - CHECK(hipEventDestroy(t.start)); - CHECK(hipEventDestroy(t.stop)); - } - if (profile) - fprintf(stderr, "altd_gpu window: %llu ticks, %u cars: stats %.3f s, sensors %.3f s, forward %.3f s, step %.3f s\n", - (unsigned long long)*executed, n, spent[0], spent[1], spent[2], spent[3]); - if (profile) - for (size_t b = 0; 4 * b < line.size(); b++) - fprintf(stderr, " ticks %5zu..: stats %.3f sensors %.3f forward %.3f step %.3f\n", b * SPAN + 1, line[4 * b], line[4 * b + 1], - line[4 * b + 2], line[4 * b + 3]); -#ifdef ALTD_STEP_TIMING - { - unsigned long long c[16]; - int rate_khz = 0; - CHECK(hipMemcpyFromSymbol(c, HIP_SYMBOL(g_step_cycles), sizeof c)); - CHECK(altd_wall_clock_khz(&rate_khz)); - const char* names[6] = {"drive", "validate", "contacts+merge", "active", "solver", "finish"}; - double us = 1e3 / (double)rate_khz, steps = (double)(c[8] ? c[8] : 1); - fprintf(stderr, "altd_gpu step sections (%llu car steps, %llu solving, %llu pair updates, %llu full scans), us per car step:", - c[8], c[9], c[7], c[6]); - for (int k = 0; k < 6; k++) fprintf(stderr, " %s %.2f", names[k], (double)c[k] * us / steps); - fprintf(stderr, " (pairs %.2f, merge %.2f)", (double)c[10] * us / steps, (double)c[11] * us / steps); - fprintf(stderr, "; SAT pairs %.2f per step, axes %.2f per pair, separated %.2f, wall points %.2f", - (double)c[12] / steps, (double)c[13] / (double)(c[12] ? c[12] : 1), (double)c[14] / (double)(c[12] ? c[12] : 1), - (double)c[15] / (double)(c[12] ? c[12] : 1)); - fprintf(stderr, "\n"); - unsigned long long zero[16] = {}; - CHECK(hipMemcpyToSymbol(HIP_SYMBOL(g_step_cycles), zero, sizeof zero)); - } -#endif - if (profile_env) { - double cpu = cpu0 >= 0 ? thread_cpu_seconds() - cpu0 : -1.0, wall = wall_seconds() - wall0; - if (cpu >= 0) fprintf(stderr, "altd_gpu window: host thread cpu %.3f s of %.3f s wall", cpu, wall); - else fprintf(stderr, "altd_gpu window: %.3f s wall", wall); - fprintf(stderr, " (%s): launches %.3f s, polls %.3f s\n", exec ? "graph" : "direct", launch_wall, wait_wall); - } - return 0; -} - -ALTD_API int altd_gpu_version() { return 10; } - -// The runtime the library was built for: "HIP" (AMD) or "CUDA" (NVIDIA). -ALTD_API const char* altd_gpu_platform() { return ALTD_PLATFORM; } - -// Writes the current device's name, NUL-terminated and truncated to `len` -// bytes; returns the HIP error, so it also tells whether a device is usable. -ALTD_API int altd_gpu_device_name(char* out, int len) { - if (len <= 0) return (int)hipErrorInvalidValue; - int device = 0; - hipError_t e = hipGetDevice(&device); - if (e != hipSuccess) return (int)e; - hipDeviceProp_t prop; - e = hipGetDeviceProperties(&prop, device); - if (e != hipSuccess) return (int)e; - snprintf(out, (size_t)len, "%s", prop.name); - return 0; -} - -// Whether the library has kernels for the current device, without launching one: ROCm 7.1 crashes -// in the launch instead of returning an error when the library was built for another GPU_ARCH. -// Returns the HIP error, hipErrorNoBinaryForGpu when the kernels are missing. -ALTD_API int altd_gpu_probe() { - hipFuncAttributes attributes; - hipError_t e = hipFuncGetAttributes(&attributes, (const void*)math_kernel); -#if !defined(__CUDACC__) - if (e == hipErrorInvalidKernelFile) return (int)hipErrorNoBinaryForGpu; // HIP's answer for a missing code object -#endif - return (int)e; -} - -// Runs one math primitive over `n` host inputs; `err` gets the per-element error bits. -ALTD_API int altd_gpu_math(int op, int n, const void* in, void* out, uint32_t* err) { - void *d_in, *d_out; - uint32_t* d_err; - CHECK(hipMalloc(&d_in, in_size(op) * n)); - CHECK(hipMalloc(&d_out, out_size(op) * n)); - CHECK(hipMalloc(&d_err, 4 * (size_t)n)); - CHECK(hipMemcpy(d_in, in, in_size(op) * n, hipMemcpyHostToDevice)); - math_kernel<<<(n + 255) / 256, 256>>>(op, n, d_in, d_out, d_err); - CHECK(hipGetLastError()); - CHECK(hipMemcpy(out, d_out, out_size(op) * n, hipMemcpyDeviceToHost)); - CHECK(hipMemcpy(err, d_err, 4 * (size_t)n, hipMemcpyDeviceToHost)); - CHECK(hipFree(d_in)); - CHECK(hipFree(d_out)); - CHECK(hipFree(d_err)); - return 0; -} - -// Raw engine sin/cos for floats with bits first .. first + n - 1. -ALTD_API int altd_gpu_engine_scan(uint32_t first, uint32_t n, uint32_t* out) { - static uint32_t* d_out = nullptr; - static uint32_t capacity = 0; - if (n > capacity) { - if (d_out) CHECK(hipFree(d_out)); - CHECK(hipMalloc(&d_out, 8 * (size_t)n)); - capacity = n; - } - engine_scan_kernel<<<(n + 255) / 256, 256>>>(first, n, d_out); - CHECK(hipGetLastError()); - CHECK(hipMemcpy(out, d_out, 8 * (size_t)n, hipMemcpyDeviceToHost)); - return 0; -} - -} // extern "C" diff --git a/github-simulator/source/gpu/sim/compat.h b/github-simulator/source/gpu/sim/compat.h deleted file mode 100644 index fb8d3105bb28adbc3180debd8dafd3af98c522c1..0000000000000000000000000000000000000000 --- a/github-simulator/source/gpu/sim/compat.h +++ /dev/null @@ -1,135 +0,0 @@ -// One source for AMD (hipcc) and NVIDIA (nvcc -x cu). Under HIP this header only includes the -// runtime and forwards the warp helpers to the HIP builtins; under CUDA it maps the hip* runtime -// names the simulator uses to their cuda* equivalents. -// -// ALTD_PLATFORM names the compiler the library was built with ("HIP" or "CUDA"). -// -// Warp helpers: kernels call altd_lane_id/altd_ballot/altd_shfl*. On AMD a wave is warpSize -// (32 or 64) lanes and __ballot returns a 64-bit mask. On NVIDIA a warp is 32 lanes and the -// *_sync forms with a full mask are required; callers already have every lane of the wave call them. -#pragma once - -// Marks the C ABI functions of the library. A Windows DLL exports nothing unless told to. -#if defined(_WIN32) -#define ALTD_API __declspec(dllexport) -#else -#define ALTD_API -#endif - -#if defined(__CUDACC__) - -#include -#include -#include - -#define ALTD_PLATFORM "CUDA" - -#define hipError_t cudaError_t -#define hipSuccess cudaSuccess -#define hipErrorInvalidValue cudaErrorInvalidValue -#define hipGetErrorString cudaGetErrorString -#define hipGetLastError cudaGetLastError -#define hipGetDevice cudaGetDevice -#define hipDeviceSynchronize cudaDeviceSynchronize -#define hipDeviceProp_t cudaDeviceProp -#define hipFuncAttributes cudaFuncAttributes -#define hipFuncGetAttributes cudaFuncGetAttributes -#define hipGetDeviceProperties cudaGetDeviceProperties - -#define hipMalloc cudaMalloc -#define hipFree cudaFree -#define hipHostMalloc(ptr, size) cudaMallocHost(ptr, size) -#define hipHostFree cudaFreeHost -#define hipMemcpy cudaMemcpy -#define hipMemcpyAsync cudaMemcpyAsync -#define hipMemcpyHostToDevice cudaMemcpyHostToDevice -#define hipMemcpyDeviceToHost cudaMemcpyDeviceToHost -#define hipMemset cudaMemset -#define hipMemsetAsync cudaMemsetAsync -#define hipMemcpyToSymbol cudaMemcpyToSymbol -#define hipMemcpyFromSymbol cudaMemcpyFromSymbol -#define HIP_SYMBOL(symbol) (symbol) - -#define hipStream_t cudaStream_t -#define hipStreamCreate cudaStreamCreate -#define hipStreamDestroy cudaStreamDestroy -#define hipStreamSynchronize cudaStreamSynchronize -#define hipStreamBeginCapture cudaStreamBeginCapture -#define hipStreamEndCapture cudaStreamEndCapture -#define hipStreamCaptureModeThreadLocal cudaStreamCaptureModeThreadLocal - -#define hipEvent_t cudaEvent_t -#define hipEventCreate cudaEventCreate -#define hipEventCreateWithFlags cudaEventCreateWithFlags -#define hipEventDisableTiming cudaEventDisableTiming -#define hipEventRecord cudaEventRecord -#define hipEventSynchronize cudaEventSynchronize -#define hipEventElapsedTime cudaEventElapsedTime -#define hipEventDestroy cudaEventDestroy - -#define hipGraph_t cudaGraph_t -#define hipGraphExec_t cudaGraphExec_t -#define hipGraphLaunch cudaGraphLaunch -#define hipGraphDestroy cudaGraphDestroy -#define hipGraphExecDestroy cudaGraphExecDestroy -// HIP's (exec, graph, error node, log buffer, buffer size) form; CUDA 12+ takes flags instead. -static inline cudaError_t hipGraphInstantiate(cudaGraphExec_t* exec, cudaGraph_t graph, void*, void*, size_t) { - return cudaGraphInstantiate(exec, graph, 0ull); -} - -// ALTD_STEP_TIMING: clock64() counts SM cycles at a varying clock, so the step sections read -// the nanosecond %globaltimer instead (updated about once a microsecond, so only sums are useful). -__device__ inline uint64_t wall_clock64() { - uint64_t t; - asm volatile("mov.u64 %0, %%globaltimer;" : "=l"(t)); - return t; -} -inline cudaError_t altd_wall_clock_khz(int* khz) { - *khz = 1000000; - return cudaSuccess; -} - -// nvcc's front end does not know clang's __builtin_memcpy. -__device__ inline void altd_memcpy(void* dst, const void* src, size_t bytes) { memcpy(dst, src, bytes); } - -__device__ inline uint32_t altd_lane_id() { - uint32_t lane; - asm volatile("mov.u32 %0, %%laneid;" : "=r"(lane)); - return lane; -} -__device__ inline uint64_t altd_ballot(bool predicate) { return (uint64_t)__ballot_sync(0xffffffffu, predicate); } -__device__ inline uint32_t altd_shfl(uint32_t value, uint32_t lane) { return __shfl_sync(0xffffffffu, value, lane); } -__device__ inline uint32_t altd_shfl_up(uint32_t value, uint32_t delta) { return __shfl_up_sync(0xffffffffu, value, delta); } - -#else - -#include -#include - -#define ALTD_PLATFORM "HIP" - -inline hipError_t altd_wall_clock_khz(int* khz) { return hipDeviceGetAttribute(khz, hipDeviceAttributeWallClockRate, 0); } - -// HIP's device memcpy is a byte loop; the builtin lets clang copy whole words. -__device__ inline void altd_memcpy(void* dst, const void* src, size_t bytes) { __builtin_memcpy(dst, src, bytes); } - -__device__ inline uint32_t altd_lane_id() { return __lane_id(); } -__device__ inline uint64_t altd_ballot(bool predicate) { return __ballot(predicate); } -__device__ inline uint32_t altd_shfl(uint32_t value, uint32_t lane) { return (uint32_t)__shfl((int)value, (int)lane); } -__device__ inline uint32_t altd_shfl_up(uint32_t value, uint32_t delta) { return (uint32_t)__shfl_up((int)value, delta); } - -#endif - -// NaN results with the CPU's bits. x86 SSE returns the first NaN operand with its quiet bit set, -// and the default NaN 0xffc00000 (0xfff8000000000000) for an invalid operation such as inf - inf. -// NVIDIA arithmetic instead returns its canonical NaN, and which NaN plain `a + b` returns on AMD -// depends on the operand order the compiler picks, so both vendors build the bits explicitly. -// altd_nan_operand(a, b): the result of an add, multiply or divide when a or b is a NaN. -// altd_invalid(x): the result of x - x for a NaN or infinite x. -__device__ inline float altd_nan_operand(float a, float b) { - return __uint_as_float(__float_as_uint(isnan(a) ? a : b) | 0x00400000u); -} -__device__ inline double altd_nan_operand(double a, double b) { - return __longlong_as_double(__double_as_longlong(isnan(a) ? a : b) | 0x0008000000000000ll); -} -__device__ inline float altd_invalid(float x) { return isnan(x) ? altd_nan_operand(x, x) : __uint_as_float(0xffc00000u); } diff --git a/github-simulator/source/gpu/sim/double_tables.h b/github-simulator/source/gpu/sim/double_tables.h deleted file mode 100644 index b7275341b64f1690d9a4fd1301c10d90c90c32ab..0000000000000000000000000000000000000000 --- a/github-simulator/source/gpu/sim/double_tables.h +++ /dev/null @@ -1,691 +0,0 @@ -// Generated by gpu/gen_tables.py from src/math/double_math_tables.rs. Do not edit. -#pragma once -#include -__device__ constexpr double INV_LN2_N = __builtin_bit_cast(double, (uint64_t)0x40671547652b82feull); -__device__ constexpr double NEG_LN2_HI_N = __builtin_bit_cast(double, (uint64_t)0xbf762e42fefa0000ull); -__device__ constexpr double NEG_LN2_LO_N = __builtin_bit_cast(double, (uint64_t)0xbd0cf79abc9e3b3aull); -__device__ constexpr double EXP_POLY[4] = { -__builtin_bit_cast(double, (uint64_t)0x3fdffffffffffdbdull), -__builtin_bit_cast(double, (uint64_t)0x3fc555555555543cull), -__builtin_bit_cast(double, (uint64_t)0x3fa55555cf172b91ull), -__builtin_bit_cast(double, (uint64_t)0x3f81111167a4d017ull) -}; -__device__ constexpr uint64_t EXP_TAB[256] = { -0x0ull, -0x3ff0000000000000ull, -0x3c9b3b4f1a88bf6eull, -0x3feff63da9fb3335ull, -0xbc7160139cd8dc5dull, -0x3fefec9a3e778061ull, -0xbc905e7a108766d1ull, -0x3fefe315e86e7f85ull, -0x3c8cd2523567f613ull, -0x3fefd9b0d3158574ull, -0xbc8bce8023f98efaull, -0x3fefd06b29ddf6deull, -0x3c60f74e61e6c861ull, -0x3fefc74518759bc8ull, -0x3c90a3e45b33d399ull, -0x3fefbe3ecac6f383ull, -0x3c979aa65d837b6dull, -0x3fefb5586cf9890full, -0x3c8eb51a92fdeffcull, -0x3fefac922b7247f7ull, -0x3c3ebe3d702f9cd1ull, -0x3fefa3ec32d3d1a2ull, -0xbc6a033489906e0bull, -0x3fef9b66affed31bull, -0xbc9556522a2fbd0eull, -0x3fef9301d0125b51ull, -0xbc5080ef8c4eea55ull, -0x3fef8abdc06c31ccull, -0xbc91c923b9d5f416ull, -0x3fef829aaea92de0ull, -0x3c80d3e3e95c55afull, -0x3fef7a98c8a58e51ull, -0xbc801b15eaa59348ull, -0x3fef72b83c7d517bull, -0xbc8f1ff055de323dull, -0x3fef6af9388c8deaull, -0x3c8b898c3f1353bfull, -0x3fef635beb6fcb75ull, -0xbc96d99c7611eb26ull, -0x3fef5be084045cd4ull, -0x3c9aecf73e3a2f60ull, -0x3fef54873168b9aaull, -0xbc8fe782cb86389dull, -0x3fef4d5022fcd91dull, -0x3c8a6f4144a6c38dull, -0x3fef463b88628cd6ull, -0x3c807a05b0e4047dull, -0x3fef3f49917ddc96ull, -0x3c968efde3a8a894ull, -0x3fef387a6e756238ull, -0x3c875e18f274487dull, -0x3fef31ce4fb2a63full, -0x3c80472b981fe7f2ull, -0x3fef2b4565e27cddull, -0xbc96b87b3f71085eull, -0x3fef24dfe1f56381ull, -0x3c82f7e16d09ab31ull, -0x3fef1e9df51fdee1ull, -0xbc3d219b1a6fbffaull, -0x3fef187fd0dad990ull, -0x3c8b3782720c0ab4ull, -0x3fef1285a6e4030bull, -0x3c6e149289cecb8full, -0x3fef0cafa93e2f56ull, -0x3c834d754db0abb6ull, -0x3fef06fe0a31b715ull, -0x3c864201e2ac744cull, -0x3fef0170fc4cd831ull, -0x3c8fdd395dd3f84aull, -0x3feefc08b26416ffull, -0xbc86a3803b8e5b04ull, -0x3feef6c55f929ff1ull, -0xbc924aedcc4b5068ull, -0x3feef1a7373aa9cbull, -0xbc9907f81b512d8eull, -0x3feeecae6d05d866ull, -0xbc71d1e83e9436d2ull, -0x3feee7db34e59ff7ull, -0xbc991919b3ce1b15ull, -0x3feee32dc313a8e5ull, -0x3c859f48a72a4c6dull, -0x3feedea64c123422ull, -0xbc9312607a28698aull, -0x3feeda4504ac801cull, -0xbc58a78f4817895bull, -0x3feed60a21f72e2aull, -0xbc7c2c9b67499a1bull, -0x3feed1f5d950a897ull, -0x3c4363ed60c2ac11ull, -0x3feece086061892dull, -0x3c9666093b0664efull, -0x3feeca41ed1d0057ull, -0x3c6ecce1daa10379ull, -0x3feec6a2b5c13cd0ull, -0x3c93ff8e3f0f1230ull, -0x3feec32af0d7d3deull, -0x3c7690cebb7aafb0ull, -0x3feebfdad5362a27ull, -0x3c931dbdeb54e077ull, -0x3feebcb299fddd0dull, -0xbc8f94340071a38eull, -0x3feeb9b2769d2ca7ull, -0xbc87deccdc93a349ull, -0x3feeb6daa2cf6642ull, -0xbc78dec6bd0f385full, -0x3feeb42b569d4f82ull, -0xbc861246ec7b5cf6ull, -0x3feeb1a4ca5d920full, -0x3c93350518fdd78eull, -0x3feeaf4736b527daull, -0x3c7b98b72f8a9b05ull, -0x3feead12d497c7fdull, -0x3c9063e1e21c5409ull, -0x3feeab07dd485429ull, -0x3c34c7855019c6eaull, -0x3feea9268a5946b7ull, -0x3c9432e62b64c035ull, -0x3feea76f15ad2148ull, -0xbc8ce44a6199769full, -0x3feea5e1b976dc09ull, -0xbc8c33c53bef4da8ull, -0x3feea47eb03a5585ull, -0xbc845378892be9aeull, -0x3feea34634ccc320ull, -0xbc93cedd78565858ull, -0x3feea23882552225ull, -0x3c5710aa807e1964ull, -0x3feea155d44ca973ull, -0xbc93b3efbf5e2228ull, -0x3feea09e667f3bcdull, -0xbc6a12ad8734b982ull, -0x3feea012750bdabfull, -0xbc6367efb86da9eeull, -0x3fee9fb23c651a2full, -0xbc80dc3d54e08851ull, -0x3fee9f7df9519484ull, -0xbc781f647e5a3ecfull, -0x3fee9f75e8ec5f74ull, -0xbc86ee4ac08b7db0ull, -0x3fee9f9a48a58174ull, -0xbc8619321e55e68aull, -0x3fee9feb564267c9ull, -0x3c909ccb5e09d4d3ull, -0x3feea0694fde5d3full, -0xbc7b32dcb94da51dull, -0x3feea11473eb0187ull, -0x3c94ecfd5467c06bull, -0x3feea1ed0130c132ull, -0x3c65ebe1abd66c55ull, -0x3feea2f336cf4e62ull, -0xbc88a1c52fb3cf42ull, -0x3feea427543e1a12ull, -0xbc9369b6f13b3734ull, -0x3feea589994cce13ull, -0xbc805e843a19ff1eull, -0x3feea71a4623c7adull, -0xbc94d450d872576eull, -0x3feea8d99b4492edull, -0x3c90ad675b0e8a00ull, -0x3feeaac7d98a6699ull, -0x3c8db72fc1f0eab4ull, -0x3feeace5422aa0dbull, -0xbc65b6609cc5e7ffull, -0x3feeaf3216b5448cull, -0x3c7bf68359f35f44ull, -0x3feeb1ae99157736ull, -0xbc93091fa71e3d83ull, -0x3feeb45b0b91ffc6ull, -0xbc5da9b88b6c1e29ull, -0x3feeb737b0cdc5e5ull, -0xbc6c23f97c90b959ull, -0x3feeba44cbc8520full, -0xbc92434322f4f9aaull, -0x3feebd829fde4e50ull, -0xbc85ca6cd7668e4bull, -0x3feec0f170ca07baull, -0x3c71affc2b91ce27ull, -0x3feec49182a3f090ull, -0x3c6dd235e10a73bbull, -0x3feec86319e32323ull, -0xbc87c50422622263ull, -0x3feecc667b5de565ull, -0x3c8b1c86e3e231d5ull, -0x3feed09bec4a2d33ull, -0xbc91bbd1d3bcbb15ull, -0x3feed503b23e255dull, -0x3c90cc319cee31d2ull, -0x3feed99e1330b358ull, -0x3c8469846e735ab3ull, -0x3feede6b5579fdbfull, -0xbc82dfcd978e9db4ull, -0x3feee36bbfd3f37aull, -0x3c8c1a7792cb3387ull, -0x3feee89f995ad3adull, -0xbc907b8f4ad1d9faull, -0x3feeee07298db666ull, -0xbc55c3d956dcaebaull, -0x3feef3a2b84f15fbull, -0xbc90a40e3da6f640ull, -0x3feef9728de5593aull, -0xbc68d6f438ad9334ull, -0x3feeff76f2fb5e47ull, -0xbc91eee26b588a35ull, -0x3fef05b030a1064aull, -0x3c74ffd70a5fddcdull, -0x3fef0c1e904bc1d2ull, -0xbc91bdfbfa9298acull, -0x3fef12c25bd71e09ull, -0x3c736eae30af0cb3ull, -0x3fef199bdd85529cull, -0x3c8ee3325c9ffd94ull, -0x3fef20ab5fffd07aull, -0x3c84e08fd10959acull, -0x3fef27f12e57d14bull, -0x3c63cdaf384e1a67ull, -0x3fef2f6d9406e7b5ull, -0x3c676b2c6c921968ull, -0x3fef3720dcef9069ull, -0xbc808a1883ccb5d2ull, -0x3fef3f0b555dc3faull, -0xbc8fad5d3ffffa6full, -0x3fef472d4a07897cull, -0xbc900dae3875a949ull, -0x3fef4f87080d89f2ull, -0x3c74a385a63d07a7ull, -0x3fef5818dcfba487ull, -0xbc82919e2040220full, -0x3fef60e316c98398ull, -0x3c8e5a50d5c192acull, -0x3fef69e603db3285ull, -0x3c843a59ac016b4bull, -0x3fef7321f301b460ull, -0xbc82d52107b43e1full, -0x3fef7c97337b9b5full, -0xbc892ab93b470dc9ull, -0x3fef864614f5a129ull, -0x3c74b604603a88d3ull, -0x3fef902ee78b3ff6ull, -0x3c83c5ec519d7271ull, -0x3fef9a51fbc74c83ull, -0xbc8ff7128fd391f0ull, -0x3fefa4afa2a490daull, -0xbc8dae98e223747dull, -0x3fefaf482d8e67f1ull, -0x3c8ec3bc41aa2008ull, -0x3fefba1bee615a27ull, -0x3c842b94c3a9eb32ull, -0x3fefc52b376bba97ull, -0x3c8a64a931d185eeull, -0x3fefd0765b6e4540ull, -0xbc8e37bae43be3edull, -0x3fefdbfdad9cbe14ull, -0x3c77893b4d91cd9dull, -0x3fefe7c1819e90d8ull, -0x3c5305c14160cc89ull, -0x3feff3c22b8f71f1ull -}; -__device__ constexpr double LN2_HI = __builtin_bit_cast(double, (uint64_t)0x3fe62e42fefa3800ull); -__device__ constexpr double LN2_LO = __builtin_bit_cast(double, (uint64_t)0x3d2ef35793c76730ull); -__device__ constexpr double POW_POLY[7] = { -__builtin_bit_cast(double, (uint64_t)0xbfe0000000000000ull), -__builtin_bit_cast(double, (uint64_t)0xbfe5555555555560ull), -__builtin_bit_cast(double, (uint64_t)0x3fe0000000000006ull), -__builtin_bit_cast(double, (uint64_t)0x3fe999999959554eull), -__builtin_bit_cast(double, (uint64_t)0xbfe555555529a47aull), -__builtin_bit_cast(double, (uint64_t)0xbff2495b9b4845e9ull), -__builtin_bit_cast(double, (uint64_t)0x3ff0002b8b263fc3ull) -}; -__device__ constexpr double POW_TAB[128][3] = { -{__builtin_bit_cast(double, (uint64_t)0x3ff6a00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfd62c82f2b9c800ull),__builtin_bit_cast(double, (uint64_t)0x3cfab42428375680ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff6800000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfd5d1bdbf580800ull),__builtin_bit_cast(double, (uint64_t)0xbd1ca508d8e0f720ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff6600000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfd5767717455800ull),__builtin_bit_cast(double, (uint64_t)0xbd2362a4d5b6506dull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff6400000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfd51aad872df800ull),__builtin_bit_cast(double, (uint64_t)0xbce684e49eb067d5ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff6200000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfd4be5f95777800ull),__builtin_bit_cast(double, (uint64_t)0xbd041b6993293ee0ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff6000000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfd4618bc21c6000ull),__builtin_bit_cast(double, (uint64_t)0x3d13d82f484c84ccull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff5e00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfd404308686a800ull),__builtin_bit_cast(double, (uint64_t)0x3cdc42f3ed820b3aull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff5c00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfd3a64c55694800ull),__builtin_bit_cast(double, (uint64_t)0x3d20b1c686519460ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff5a00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfd347dd9a988000ull),__builtin_bit_cast(double, (uint64_t)0x3d25594dd4c58092ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff5800000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfd2e8e2bae12000ull),__builtin_bit_cast(double, (uint64_t)0x3d267b1e99b72bd8ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff5600000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfd2895a13de8800ull),__builtin_bit_cast(double, (uint64_t)0x3d15ca14b6cfb03full)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff5600000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfd2895a13de8800ull),__builtin_bit_cast(double, (uint64_t)0x3d15ca14b6cfb03full)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff5400000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfd22941fbcf7800ull),__builtin_bit_cast(double, (uint64_t)0xbd165a242853da76ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff5200000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfd1c898c1699800ull),__builtin_bit_cast(double, (uint64_t)0xbd1fafbc68e75404ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff5000000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfd1675cababa800ull),__builtin_bit_cast(double, (uint64_t)0x3d1f1fc63382a8f0ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff4e00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfd1058bf9ae4800ull),__builtin_bit_cast(double, (uint64_t)0xbd26a8c4fd055a66ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff4c00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfd0a324e2739000ull),__builtin_bit_cast(double, (uint64_t)0xbd0c6bee7ef4030eull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff4a00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfd0402594b4d000ull),__builtin_bit_cast(double, (uint64_t)0xbcf036b89ef42d7full)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff4a00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfd0402594b4d000ull),__builtin_bit_cast(double, (uint64_t)0xbcf036b89ef42d7full)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff4800000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfcfb9186d5e4000ull),__builtin_bit_cast(double, (uint64_t)0x3d0d572aab993c87ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff4600000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfcef0adcbdc6000ull),__builtin_bit_cast(double, (uint64_t)0x3d2b26b79c86af24ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff4400000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfce27076e2af000ull),__builtin_bit_cast(double, (uint64_t)0xbd172f4f543fff10ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff4200000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfcd5c216b4fc000ull),__builtin_bit_cast(double, (uint64_t)0x3d21ba91bbca681bull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff4000000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfcc8ff7c79aa000ull),__builtin_bit_cast(double, (uint64_t)0x3d27794f689f8434ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff4000000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfcc8ff7c79aa000ull),__builtin_bit_cast(double, (uint64_t)0x3d27794f689f8434ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff3e00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfcbc286742d9000ull),__builtin_bit_cast(double, (uint64_t)0x3d194eb0318bb78full)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff3c00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfcaf3c94e80c000ull),__builtin_bit_cast(double, (uint64_t)0x3cba4e633fcd9066ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff3a00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfca23bc1fe2b000ull),__builtin_bit_cast(double, (uint64_t)0xbd258c64dc46c1eaull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff3a00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfca23bc1fe2b000ull),__builtin_bit_cast(double, (uint64_t)0xbd258c64dc46c1eaull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff3800000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfc9525a9cf45000ull),__builtin_bit_cast(double, (uint64_t)0xbd2ad1d904c1d4e3ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff3600000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfc87fa06520d000ull),__builtin_bit_cast(double, (uint64_t)0x3d2bbdbf7fdbfa09ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff3400000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfc7ab890210e000ull),__builtin_bit_cast(double, (uint64_t)0x3d2bdb9072534a58ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff3400000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfc7ab890210e000ull),__builtin_bit_cast(double, (uint64_t)0x3d2bdb9072534a58ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff3200000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfc6d60fe719d000ull),__builtin_bit_cast(double, (uint64_t)0xbd10e46aa3b2e266ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff3000000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfc5ff3070a79000ull),__builtin_bit_cast(double, (uint64_t)0xbd1e9e439f105039ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff3000000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfc5ff3070a79000ull),__builtin_bit_cast(double, (uint64_t)0xbd1e9e439f105039ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff2e00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfc526e5e3a1b000ull),__builtin_bit_cast(double, (uint64_t)0xbd20de8b90075b8full)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff2c00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfc44d2b6ccb8000ull),__builtin_bit_cast(double, (uint64_t)0x3d170cc16135783cull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff2c00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfc44d2b6ccb8000ull),__builtin_bit_cast(double, (uint64_t)0x3d170cc16135783cull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff2a00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfc371fc201e9000ull),__builtin_bit_cast(double, (uint64_t)0x3cf178864d27543aull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff2800000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfc29552f81ff000ull),__builtin_bit_cast(double, (uint64_t)0xbd248d301771c408ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff2600000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfc1b72ad52f6000ull),__builtin_bit_cast(double, (uint64_t)0xbd2e80a41811a396ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff2600000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfc1b72ad52f6000ull),__builtin_bit_cast(double, (uint64_t)0xbd2e80a41811a396ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff2400000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfc0d77e7cd09000ull),__builtin_bit_cast(double, (uint64_t)0x3d0a699688e85bf4ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff2400000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfc0d77e7cd09000ull),__builtin_bit_cast(double, (uint64_t)0x3d0a699688e85bf4ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff2200000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfbfec9131dbe000ull),__builtin_bit_cast(double, (uint64_t)0xbd2575545ca333f2ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff2000000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfbe27076e2b0000ull),__builtin_bit_cast(double, (uint64_t)0x3d2a342c2af0003cull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff2000000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfbe27076e2b0000ull),__builtin_bit_cast(double, (uint64_t)0x3d2a342c2af0003cull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff1e00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfbc5e548f5bc000ull),__builtin_bit_cast(double, (uint64_t)0xbd1d0c57585fbe06ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff1c00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfba926d3a4ae000ull),__builtin_bit_cast(double, (uint64_t)0x3d253935e85baac8ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff1c00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfba926d3a4ae000ull),__builtin_bit_cast(double, (uint64_t)0x3d253935e85baac8ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff1a00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfb8c345d631a000ull),__builtin_bit_cast(double, (uint64_t)0x3d137c294d2f5668ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff1a00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfb8c345d631a000ull),__builtin_bit_cast(double, (uint64_t)0x3d137c294d2f5668ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff1800000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfb6f0d28ae56000ull),__builtin_bit_cast(double, (uint64_t)0xbd269737c93373daull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff1600000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfb51b073f062000ull),__builtin_bit_cast(double, (uint64_t)0x3d1f025b61c65e57ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff1600000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfb51b073f062000ull),__builtin_bit_cast(double, (uint64_t)0x3d1f025b61c65e57ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff1400000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfb341d7961be000ull),__builtin_bit_cast(double, (uint64_t)0x3d2c5edaccf913dfull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff1400000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfb341d7961be000ull),__builtin_bit_cast(double, (uint64_t)0x3d2c5edaccf913dfull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff1200000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfb16536eea38000ull),__builtin_bit_cast(double, (uint64_t)0x3d147c5e768fa309ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff1000000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfaf0a30c0118000ull),__builtin_bit_cast(double, (uint64_t)0x3d2d599e83368e91ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff1000000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfaf0a30c0118000ull),__builtin_bit_cast(double, (uint64_t)0x3d2d599e83368e91ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff0e00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfab42dd71198000ull),__builtin_bit_cast(double, (uint64_t)0x3d1c827ae5d6704cull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff0e00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfab42dd71198000ull),__builtin_bit_cast(double, (uint64_t)0x3d1c827ae5d6704cull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff0c00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfa77458f632c000ull),__builtin_bit_cast(double, (uint64_t)0xbd2cfc4634f2a1eeull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff0c00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfa77458f632c000ull),__builtin_bit_cast(double, (uint64_t)0xbd2cfc4634f2a1eeull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff0a00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfa39e87b9fec000ull),__builtin_bit_cast(double, (uint64_t)0x3cf502b7f526feaaull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff0a00000000000ull),__builtin_bit_cast(double, (uint64_t)0xbfa39e87b9fec000ull),__builtin_bit_cast(double, (uint64_t)0x3cf502b7f526feaaull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff0800000000000ull),__builtin_bit_cast(double, (uint64_t)0xbf9f829b0e780000ull),__builtin_bit_cast(double, (uint64_t)0xbd2980267c7e09e4ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff0800000000000ull),__builtin_bit_cast(double, (uint64_t)0xbf9f829b0e780000ull),__builtin_bit_cast(double, (uint64_t)0xbd2980267c7e09e4ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff0600000000000ull),__builtin_bit_cast(double, (uint64_t)0xbf97b91b07d58000ull),__builtin_bit_cast(double, (uint64_t)0xbd288d5493faa639ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff0400000000000ull),__builtin_bit_cast(double, (uint64_t)0xbf8fc0a8b0fc0000ull),__builtin_bit_cast(double, (uint64_t)0xbcdf1e7cf6d3a69cull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff0400000000000ull),__builtin_bit_cast(double, (uint64_t)0xbf8fc0a8b0fc0000ull),__builtin_bit_cast(double, (uint64_t)0xbcdf1e7cf6d3a69cull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff0200000000000ull),__builtin_bit_cast(double, (uint64_t)0xbf7fe02a6b100000ull),__builtin_bit_cast(double, (uint64_t)0xbd19e23f0dda40e4ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff0200000000000ull),__builtin_bit_cast(double, (uint64_t)0xbf7fe02a6b100000ull),__builtin_bit_cast(double, (uint64_t)0xbd19e23f0dda40e4ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff0000000000000ull),__builtin_bit_cast(double, (uint64_t)0x0000000000000000ull),__builtin_bit_cast(double, (uint64_t)0x0000000000000000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff0000000000000ull),__builtin_bit_cast(double, (uint64_t)0x0000000000000000ull),__builtin_bit_cast(double, (uint64_t)0x0000000000000000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fefc00000000000ull),__builtin_bit_cast(double, (uint64_t)0x3f80101575890000ull),__builtin_bit_cast(double, (uint64_t)0xbd10c76b999d2be8ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fef800000000000ull),__builtin_bit_cast(double, (uint64_t)0x3f90205658938000ull),__builtin_bit_cast(double, (uint64_t)0xbd23dc5b06e2f7d2ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fef400000000000ull),__builtin_bit_cast(double, (uint64_t)0x3f98492528c90000ull),__builtin_bit_cast(double, (uint64_t)0xbd2aa0ba325a0c34ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fef000000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fa0415d89e74000ull),__builtin_bit_cast(double, (uint64_t)0x3d0111c05cf1d753ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feec00000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fa466aed42e0000ull),__builtin_bit_cast(double, (uint64_t)0xbd2c167375bdfd28ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fee800000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fa894aa149fc000ull),__builtin_bit_cast(double, (uint64_t)0xbd197995d05a267dull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fee400000000000ull),__builtin_bit_cast(double, (uint64_t)0x3faccb73cdddc000ull),__builtin_bit_cast(double, (uint64_t)0xbd1a68f247d82807ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fee200000000000ull),__builtin_bit_cast(double, (uint64_t)0x3faeea31c006c000ull),__builtin_bit_cast(double, (uint64_t)0xbd0e113e4fc93b7bull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fede00000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fb1973bd1466000ull),__builtin_bit_cast(double, (uint64_t)0xbd25325d560d9e9bull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feda00000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fb3bdf5a7d1e000ull),__builtin_bit_cast(double, (uint64_t)0x3d2cc85ea5db4ed7ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fed600000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fb5e95a4d97a000ull),__builtin_bit_cast(double, (uint64_t)0xbd2c69063c5d1d1eull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fed400000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fb700d30aeac000ull),__builtin_bit_cast(double, (uint64_t)0x3cec1e8da99ded32ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fed000000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fb9335e5d594000ull),__builtin_bit_cast(double, (uint64_t)0x3d23115c3abd47daull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fecc00000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fbb6ac88dad6000ull),__builtin_bit_cast(double, (uint64_t)0xbd1390802bf768e5ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feca00000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fbc885801bc4000ull),__builtin_bit_cast(double, (uint64_t)0x3d2646d1c65aacd3ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fec600000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fbec739830a2000ull),__builtin_bit_cast(double, (uint64_t)0xbd2dc068afe645e0ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fec400000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fbfe89139dbe000ull),__builtin_bit_cast(double, (uint64_t)0xbd2534d64fa10afdull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fec000000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fc1178e8227e000ull),__builtin_bit_cast(double, (uint64_t)0x3d21ef78ce2d07f2ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3febe00000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fc1aa2b7e23f000ull),__builtin_bit_cast(double, (uint64_t)0x3d2ca78e44389934ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feba00000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fc2d1610c868000ull),__builtin_bit_cast(double, (uint64_t)0x3d039d6ccb81b4a1ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feb800000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fc365fcb0159000ull),__builtin_bit_cast(double, (uint64_t)0x3cc62fa8234b7289ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feb400000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fc4913d8333b000ull),__builtin_bit_cast(double, (uint64_t)0x3d25837954fdb678ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feb200000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fc527e5e4a1b000ull),__builtin_bit_cast(double, (uint64_t)0x3d2633e8e5697dc7ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feae00000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fc6574ebe8c1000ull),__builtin_bit_cast(double, (uint64_t)0x3d19cf8b2c3c2e78ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feac00000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fc6f0128b757000ull),__builtin_bit_cast(double, (uint64_t)0xbd25118de59c21e1ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feaa00000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fc7898d85445000ull),__builtin_bit_cast(double, (uint64_t)0xbd1c661070914305ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fea600000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fc8beafeb390000ull),__builtin_bit_cast(double, (uint64_t)0xbd073d54aae92cd1ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fea400000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fc95a5adcf70000ull),__builtin_bit_cast(double, (uint64_t)0x3d07f22858a0ff6full)}, -{__builtin_bit_cast(double, (uint64_t)0x3fea000000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fca93ed3c8ae000ull),__builtin_bit_cast(double, (uint64_t)0xbd28724350562169ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe9e00000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fcb31d8575bd000ull),__builtin_bit_cast(double, (uint64_t)0xbd0c358d4eace1aaull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe9c00000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fcbd087383be000ull),__builtin_bit_cast(double, (uint64_t)0xbd2d4bc4595412b6ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe9a00000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fcc6ffbc6f01000ull),__builtin_bit_cast(double, (uint64_t)0xbcf1ec72c5962bd2ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe9600000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fcdb13db0d49000ull),__builtin_bit_cast(double, (uint64_t)0xbd2aff2af715b035ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe9400000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fce530effe71000ull),__builtin_bit_cast(double, (uint64_t)0x3cc212276041f430ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe9200000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fcef5ade4dd0000ull),__builtin_bit_cast(double, (uint64_t)0xbcca211565bb8e11ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe9000000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fcf991c6cb3b000ull),__builtin_bit_cast(double, (uint64_t)0x3d1bcbecca0cdf30ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe8c00000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fd07138604d5800ull),__builtin_bit_cast(double, (uint64_t)0x3cf89cdb16ed4e91ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe8a00000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fd0c42d67616000ull),__builtin_bit_cast(double, (uint64_t)0x3d27188b163ceae9ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe8800000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fd1178e8227e800ull),__builtin_bit_cast(double, (uint64_t)0xbd2c210e63a5f01cull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe8600000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fd16b5ccbacf800ull),__builtin_bit_cast(double, (uint64_t)0x3d2b9acdf7a51681ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe8400000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fd1bf99635a6800ull),__builtin_bit_cast(double, (uint64_t)0x3d2ca6ed5147bdb7ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe8200000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fd214456d0eb800ull),__builtin_bit_cast(double, (uint64_t)0x3d0a87deba46baeaull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe7e00000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fd2bef07cdc9000ull),__builtin_bit_cast(double, (uint64_t)0x3d2a9cfa4a5004f4ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe7c00000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fd314f1e1d36000ull),__builtin_bit_cast(double, (uint64_t)0xbd28e27ad3213cb8ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe7a00000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fd36b6776be1000ull),__builtin_bit_cast(double, (uint64_t)0x3d116ecdb0f177c8ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe7800000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fd3c25277333000ull),__builtin_bit_cast(double, (uint64_t)0x3d183b54b606bd5cull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe7600000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fd419b423d5e800ull),__builtin_bit_cast(double, (uint64_t)0x3d08e436ec90e09dull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe7400000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fd4718dc271c800ull),__builtin_bit_cast(double, (uint64_t)0xbd2f27ce0967d675ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe7200000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fd4c9e09e173000ull),__builtin_bit_cast(double, (uint64_t)0xbd2e20891b0ad8a4ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe7000000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fd522ae0738a000ull),__builtin_bit_cast(double, (uint64_t)0x3d2ebe708164c759ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe6e00000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fd57bf753c8d000ull),__builtin_bit_cast(double, (uint64_t)0x3d1fadedee5d40efull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe6c00000000000ull),__builtin_bit_cast(double, (uint64_t)0x3fd5d5bddf596000ull),__builtin_bit_cast(double, (uint64_t)0xbd0a0b2a08a465dcull)} -}; -__device__ constexpr double LOG_POLY[5] = { -__builtin_bit_cast(double, (uint64_t)0xbfe0000000000001ull), -__builtin_bit_cast(double, (uint64_t)0x3fd555555551305bull), -__builtin_bit_cast(double, (uint64_t)0xbfcfffffffeb4590ull), -__builtin_bit_cast(double, (uint64_t)0x3fc999b324f10111ull), -__builtin_bit_cast(double, (uint64_t)0xbfc55575e506c89full) -}; -__device__ constexpr double LOG_NEAR_POLY[11] = { -__builtin_bit_cast(double, (uint64_t)0xbfe0000000000000ull), -__builtin_bit_cast(double, (uint64_t)0x3fd5555555555577ull), -__builtin_bit_cast(double, (uint64_t)0xbfcffffffffffdcbull), -__builtin_bit_cast(double, (uint64_t)0x3fc999999995dd0cull), -__builtin_bit_cast(double, (uint64_t)0xbfc55555556745a7ull), -__builtin_bit_cast(double, (uint64_t)0x3fc24924a344de30ull), -__builtin_bit_cast(double, (uint64_t)0xbfbfffffa4423d65ull), -__builtin_bit_cast(double, (uint64_t)0x3fbc7184282ad6caull), -__builtin_bit_cast(double, (uint64_t)0xbfb999eb43b068ffull), -__builtin_bit_cast(double, (uint64_t)0x3fb78182f7afd085ull), -__builtin_bit_cast(double, (uint64_t)0xbfb5521375d145cdull) -}; -__device__ constexpr double LOG_TAB[128][2] = { -{__builtin_bit_cast(double, (uint64_t)0x3ff734f0c3e0de9full),__builtin_bit_cast(double, (uint64_t)0xbfd7cc7f79e69000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff713786a2ce91full),__builtin_bit_cast(double, (uint64_t)0xbfd76feec20d0000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff6f26008fab5a0ull),__builtin_bit_cast(double, (uint64_t)0xbfd713e31351e000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff6d1a61f138c7dull),__builtin_bit_cast(double, (uint64_t)0xbfd6b85b38287800ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff6b1490bc5b4d1ull),__builtin_bit_cast(double, (uint64_t)0xbfd65d5590807800ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff69147332f0cbaull),__builtin_bit_cast(double, (uint64_t)0xbfd602d076180000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff6719f18224223ull),__builtin_bit_cast(double, (uint64_t)0xbfd5a8ca86909000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff6524f99a51ed9ull),__builtin_bit_cast(double, (uint64_t)0xbfd54f4356035000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff63356aa8f24c4ull),__builtin_bit_cast(double, (uint64_t)0xbfd4f637c36b4000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff614b36b9ddc14ull),__builtin_bit_cast(double, (uint64_t)0xbfd49da7fda85000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff5f66452c65c4cull),__builtin_bit_cast(double, (uint64_t)0xbfd445923989a800ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff5d867b5912c4full),__builtin_bit_cast(double, (uint64_t)0xbfd3edf439b0b800ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff5babccb5b90deull),__builtin_bit_cast(double, (uint64_t)0xbfd396ce448f7000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff59d61f2d91a78ull),__builtin_bit_cast(double, (uint64_t)0xbfd3401e17bda000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff5805612465687ull),__builtin_bit_cast(double, (uint64_t)0xbfd2e9e2ef468000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff56397cee76bd3ull),__builtin_bit_cast(double, (uint64_t)0xbfd2941b3830e000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff54725e2a77f93ull),__builtin_bit_cast(double, (uint64_t)0xbfd23ec58cda8800ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff52aff42064583ull),__builtin_bit_cast(double, (uint64_t)0xbfd1e9e129279000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff50f22dbb2bddfull),__builtin_bit_cast(double, (uint64_t)0xbfd1956d2b48f800ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff4f38f4734ded7ull),__builtin_bit_cast(double, (uint64_t)0xbfd141679ab9f800ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff4d843cfde2840ull),__builtin_bit_cast(double, (uint64_t)0xbfd0edd094ef9800ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff4bd3ec078a3c8ull),__builtin_bit_cast(double, (uint64_t)0xbfd09aa518db1000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff4a27fc3e0258aull),__builtin_bit_cast(double, (uint64_t)0xbfd047e65263b800ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff4880524d48434ull),__builtin_bit_cast(double, (uint64_t)0xbfcfeb224586f000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff46dce1b192d0bull),__builtin_bit_cast(double, (uint64_t)0xbfcf474a7517b000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff453d9d3391854ull),__builtin_bit_cast(double, (uint64_t)0xbfcea4443d103000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff43a2744b4845aull),__builtin_bit_cast(double, (uint64_t)0xbfce020d44e9b000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff420b54115f8fbull),__builtin_bit_cast(double, (uint64_t)0xbfcd60a22977f000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff40782da3ef4b1ull),__builtin_bit_cast(double, (uint64_t)0xbfccc00104959000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff3ee8f5d57fe8full),__builtin_bit_cast(double, (uint64_t)0xbfcc202956891000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff3d5d9a00b4ce9ull),__builtin_bit_cast(double, (uint64_t)0xbfcb81178d811000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff3bd60c010c12bull),__builtin_bit_cast(double, (uint64_t)0xbfcae2c9ccd3d000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff3a5242b75dab8ull),__builtin_bit_cast(double, (uint64_t)0xbfca45402e129000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff38d22cd9fd002ull),__builtin_bit_cast(double, (uint64_t)0xbfc9a877681df000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff3755bc5847a1cull),__builtin_bit_cast(double, (uint64_t)0xbfc90c6d69483000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff35dce49ad36e2ull),__builtin_bit_cast(double, (uint64_t)0xbfc87120a645c000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff34679984dd440ull),__builtin_bit_cast(double, (uint64_t)0xbfc7d68fb4143000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff32f5cceffcb24ull),__builtin_bit_cast(double, (uint64_t)0xbfc73cb83c627000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff3187775a10d49ull),__builtin_bit_cast(double, (uint64_t)0xbfc6a39a9b376000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff301c8373e3990ull),__builtin_bit_cast(double, (uint64_t)0xbfc60b3154b7a000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff2eb4ebb95f841ull),__builtin_bit_cast(double, (uint64_t)0xbfc5737d76243000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff2d50a0219a9d1ull),__builtin_bit_cast(double, (uint64_t)0xbfc4dc7b8fc23000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff2bef9a8b7fd2aull),__builtin_bit_cast(double, (uint64_t)0xbfc4462c51d20000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff2a91c7a0c1babull),__builtin_bit_cast(double, (uint64_t)0xbfc3b08abc830000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff293726014b530ull),__builtin_bit_cast(double, (uint64_t)0xbfc31b996b490000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff27dfa5757a1f5ull),__builtin_bit_cast(double, (uint64_t)0xbfc2875490a44000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff268b39b1d3bbfull),__builtin_bit_cast(double, (uint64_t)0xbfc1f3b9f879a000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff2539d838ff5bdull),__builtin_bit_cast(double, (uint64_t)0xbfc160c8252ca000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff23eb7aac9083bull),__builtin_bit_cast(double, (uint64_t)0xbfc0ce7f57f72000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff22a012ba940b6ull),__builtin_bit_cast(double, (uint64_t)0xbfc03cdc49fea000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff2157996cc4132ull),__builtin_bit_cast(double, (uint64_t)0xbfbf57bdbc4b8000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff201201dd2fc9bull),__builtin_bit_cast(double, (uint64_t)0xbfbe370896404000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff1ecf4494d480bull),__builtin_bit_cast(double, (uint64_t)0xbfbd17983ef94000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff1d8f5528f6569ull),__builtin_bit_cast(double, (uint64_t)0xbfbbf9674ed8a000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff1c52311577e7cull),__builtin_bit_cast(double, (uint64_t)0xbfbadc79202f6000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff1b17c74cb26e9ull),__builtin_bit_cast(double, (uint64_t)0xbfb9c0c3e7288000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff19e010c2c1ab6ull),__builtin_bit_cast(double, (uint64_t)0xbfb8a646b372c000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff18ab07bb670bdull),__builtin_bit_cast(double, (uint64_t)0xbfb78d01b3ac0000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff1778a25efbcb6ull),__builtin_bit_cast(double, (uint64_t)0xbfb674f145380000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff1648d354c31daull),__builtin_bit_cast(double, (uint64_t)0xbfb55e0e6d878000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff151b990275fddull),__builtin_bit_cast(double, (uint64_t)0xbfb4485cdea1e000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff13f0ea432d24cull),__builtin_bit_cast(double, (uint64_t)0xbfb333d94d6aa000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff12c8b7210f9daull),__builtin_bit_cast(double, (uint64_t)0xbfb22079f8c56000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff11a3028ecb531ull),__builtin_bit_cast(double, (uint64_t)0xbfb10e4698622000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff107fbda8434afull),__builtin_bit_cast(double, (uint64_t)0xbfaffa6c6ad20000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff0f5ee0f4e6bb3ull),__builtin_bit_cast(double, (uint64_t)0xbfadda8d4a774000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff0e4065d2a9fceull),__builtin_bit_cast(double, (uint64_t)0xbfabbcece4850000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff0d244632ca521ull),__builtin_bit_cast(double, (uint64_t)0xbfa9a1894012c000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff0c0a77ce2981aull),__builtin_bit_cast(double, (uint64_t)0xbfa788583302c000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff0af2f83c636d1ull),__builtin_bit_cast(double, (uint64_t)0xbfa5715e67d68000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff09ddb98a01339ull),__builtin_bit_cast(double, (uint64_t)0xbfa35c8a49658000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff08cabaf52e7dfull),__builtin_bit_cast(double, (uint64_t)0xbfa149e364154000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff07b9f2f4e28fbull),__builtin_bit_cast(double, (uint64_t)0xbf9e72c082eb8000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff06ab58c358f19ull),__builtin_bit_cast(double, (uint64_t)0xbf9a55f152528000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff059eea5ecf92cull),__builtin_bit_cast(double, (uint64_t)0xbf963d62cf818000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff04949cdd12c90ull),__builtin_bit_cast(double, (uint64_t)0xbf9228fb8caa0000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff038c6c6f0ada9ull),__builtin_bit_cast(double, (uint64_t)0xbf8c317b20f90000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff02865137932a9ull),__builtin_bit_cast(double, (uint64_t)0xbf8419355daa0000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff0182427ea7348ull),__builtin_bit_cast(double, (uint64_t)0xbf781203c2ec0000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff008040614b195ull),__builtin_bit_cast(double, (uint64_t)0xbf60040979240000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fefe01ff726fa1aull),__builtin_bit_cast(double, (uint64_t)0x3f6feff384900000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fefa11cc261ea74ull),__builtin_bit_cast(double, (uint64_t)0x3f87dc41353d0000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fef6310b081992eull),__builtin_bit_cast(double, (uint64_t)0x3f93cea3c4c28000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fef25f63ceeadcdull),__builtin_bit_cast(double, (uint64_t)0x3f9b9fc114890000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feee9c8039113e7ull),__builtin_bit_cast(double, (uint64_t)0x3fa1b0d8ce110000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feeae8078cbb1abull),__builtin_bit_cast(double, (uint64_t)0x3fa58a5bd001c000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fee741aa29d0c9bull),__builtin_bit_cast(double, (uint64_t)0x3fa95c8340d88000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fee3a91830a99b5ull),__builtin_bit_cast(double, (uint64_t)0x3fad276aef578000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fee01e009609a56ull),__builtin_bit_cast(double, (uint64_t)0x3fb07598e598c000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fedca01e577bb98ull),__builtin_bit_cast(double, (uint64_t)0x3fb253f5e30d2000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fed92f20b7c9103ull),__builtin_bit_cast(double, (uint64_t)0x3fb42edd8b380000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fed5cac66fb5cceull),__builtin_bit_cast(double, (uint64_t)0x3fb606598757c000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fed272caa5ede9dull),__builtin_bit_cast(double, (uint64_t)0x3fb7da76356a0000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fecf26e3e6b2ccdull),__builtin_bit_cast(double, (uint64_t)0x3fb9ab434e1c6000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fecbe6da2a77902ull),__builtin_bit_cast(double, (uint64_t)0x3fbb78c7bb0d6000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fec8b266d37086dull),__builtin_bit_cast(double, (uint64_t)0x3fbd431332e72000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fec5894bd5d5804ull),__builtin_bit_cast(double, (uint64_t)0x3fbf0a3171de6000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fec26b533bb9f8cull),__builtin_bit_cast(double, (uint64_t)0x3fc067152b914000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3febf583eeece73full),__builtin_bit_cast(double, (uint64_t)0x3fc147858292b000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3febc4fd75db96c1ull),__builtin_bit_cast(double, (uint64_t)0x3fc2266ecdca3000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feb951e0c864a28ull),__builtin_bit_cast(double, (uint64_t)0x3fc303d7a6c55000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feb65e2c5ef3e2cull),__builtin_bit_cast(double, (uint64_t)0x3fc3dfc33c331000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feb374867c9888bull),__builtin_bit_cast(double, (uint64_t)0x3fc4ba366b7a8000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feb094b211d304aull),__builtin_bit_cast(double, (uint64_t)0x3fc5933928d1f000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feadbe885f2ef7eull),__builtin_bit_cast(double, (uint64_t)0x3fc66acd2418f000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feaaf1d31603da2ull),__builtin_bit_cast(double, (uint64_t)0x3fc740f8ec669000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fea82e63fd358a7ull),__builtin_bit_cast(double, (uint64_t)0x3fc815c0f51af000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fea5740ef09738bull),__builtin_bit_cast(double, (uint64_t)0x3fc8e92954f68000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fea2c2a90ab4b27ull),__builtin_bit_cast(double, (uint64_t)0x3fc9bb3602f84000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fea01a01393f2d1ull),__builtin_bit_cast(double, (uint64_t)0x3fca8bed1c2c0000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe9d79f24db3c1bull),__builtin_bit_cast(double, (uint64_t)0x3fcb5b515c01d000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe9ae2505c7b190ull),__builtin_bit_cast(double, (uint64_t)0x3fcc2967ccbcc000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe9852ef297ce2full),__builtin_bit_cast(double, (uint64_t)0x3fccf635d5486000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe95cbaeea44b75ull),__builtin_bit_cast(double, (uint64_t)0x3fcdc1bd3446c000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe934c69de74838ull),__builtin_bit_cast(double, (uint64_t)0x3fce8c01b8cfe000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe90d4f2f6752e6ull),__builtin_bit_cast(double, (uint64_t)0x3fcf5509c0179000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe8e6528effd79dull),__builtin_bit_cast(double, (uint64_t)0x3fd00e6c121fb800ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe8bfce9fcc007cull),__builtin_bit_cast(double, (uint64_t)0x3fd071b80e93d000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe899c0dabec30eull),__builtin_bit_cast(double, (uint64_t)0x3fd0d46b9e867000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe87427aa2317fbull),__builtin_bit_cast(double, (uint64_t)0x3fd13687334bd000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe84f00acb39a08ull),__builtin_bit_cast(double, (uint64_t)0x3fd1980d67234800ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe82a49e8653e55ull),__builtin_bit_cast(double, (uint64_t)0x3fd1f8ffe0cc8000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe8060195f40260ull),__builtin_bit_cast(double, (uint64_t)0x3fd2595fd7636800ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe7e22563e0a329ull),__builtin_bit_cast(double, (uint64_t)0x3fd2b9300914a800ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe7beb377dcb5adull),__builtin_bit_cast(double, (uint64_t)0x3fd3187210436000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe79baa679725c2ull),__builtin_bit_cast(double, (uint64_t)0x3fd377266dec1800ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe77907f2170657ull),__builtin_bit_cast(double, (uint64_t)0x3fd3d54ffbaf3000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe756cadbd6130cull),__builtin_bit_cast(double, (uint64_t)0x3fd432eee32fe000ull)} -}; -__device__ constexpr double LOG_TAB2[128][2] = { -{__builtin_bit_cast(double, (uint64_t)0x3fe61000014fb66bull),__builtin_bit_cast(double, (uint64_t)0x3c7e026c91425b3cull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe63000034db495ull),__builtin_bit_cast(double, (uint64_t)0x3c8dbfea48005d41ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe650000d94d478ull),__builtin_bit_cast(double, (uint64_t)0x3c8e7fa786d6a5b7ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe67000074e6fadull),__builtin_bit_cast(double, (uint64_t)0x3c61fcea6b54254cull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe68ffffedf0faeull),__builtin_bit_cast(double, (uint64_t)0xbc7c7e274c590efdull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe6b0000763c5bcull),__builtin_bit_cast(double, (uint64_t)0xbc8ac16848dcda01ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe6d0001e5cc1f6ull),__builtin_bit_cast(double, (uint64_t)0x3c833f1c9d499311ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe6efffeb05f63eull),__builtin_bit_cast(double, (uint64_t)0xbc7e80041ae22d53ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe710000e869780ull),__builtin_bit_cast(double, (uint64_t)0x3c7bff6671097952ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe72ffffc67e912ull),__builtin_bit_cast(double, (uint64_t)0x3c8c00e226bd8724ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe74fffdf81116aull),__builtin_bit_cast(double, (uint64_t)0xbc6e02916ef101d2ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe770000f679c90ull),__builtin_bit_cast(double, (uint64_t)0xbc67fc71cd549c74ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe78ffffa7ec835ull),__builtin_bit_cast(double, (uint64_t)0x3c81bec19ef50483ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe7affffe20c2e6ull),__builtin_bit_cast(double, (uint64_t)0xbc707e1729cc6465ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe7cfffed3fc900ull),__builtin_bit_cast(double, (uint64_t)0xbc808072087b8b1cull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe7efffe9261a76ull),__builtin_bit_cast(double, (uint64_t)0x3c8dc0286d9df9aeull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe81000049ca3e8ull),__builtin_bit_cast(double, (uint64_t)0x3c897fd251e54c33ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe8300017932c8full),__builtin_bit_cast(double, (uint64_t)0xbc8afee9b630f381ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe850000633739cull),__builtin_bit_cast(double, (uint64_t)0x3c89bfbf6b6535bcull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe87000204289c6ull),__builtin_bit_cast(double, (uint64_t)0xbc8bbf65f3117b75ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe88fffebf57904ull),__builtin_bit_cast(double, (uint64_t)0xbc89006ea23dcb57ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe8b00022bc04dfull),__builtin_bit_cast(double, (uint64_t)0xbc7d00df38e04b0aull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe8cfffe50c1b8aull),__builtin_bit_cast(double, (uint64_t)0xbc88007146ff9f05ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe8effffc918e43ull),__builtin_bit_cast(double, (uint64_t)0x3c83817bd07a7038ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe910001efa5fc7ull),__builtin_bit_cast(double, (uint64_t)0x3c893e9176dfb403ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe9300013467bb9ull),__builtin_bit_cast(double, (uint64_t)0x3c7f804e4b980276ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe94fffe6ee076full),__builtin_bit_cast(double, (uint64_t)0xbc8f7ef0d9ff622eull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe96fffde3c12d1ull),__builtin_bit_cast(double, (uint64_t)0xbc7082aa962638baull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe98ffff4458a0dull),__builtin_bit_cast(double, (uint64_t)0xbc87801b9164a8efull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe9afffdd982e3eull),__builtin_bit_cast(double, (uint64_t)0xbc8740e08a5a9337ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe9cfffed49fb66ull),__builtin_bit_cast(double, (uint64_t)0x3c3fce08c19be000ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fe9f00020f19c51ull),__builtin_bit_cast(double, (uint64_t)0xbc8a3faa27885b0aull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fea10001145b006ull),__builtin_bit_cast(double, (uint64_t)0x3c74ff489958da56ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fea300007bbf6faull),__builtin_bit_cast(double, (uint64_t)0x3c8cbeab8a2b6d18ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fea500010971d79ull),__builtin_bit_cast(double, (uint64_t)0x3c88fecadd787930ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fea70001df52e48ull),__builtin_bit_cast(double, (uint64_t)0xbc8f41763dd8abdbull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fea90001c593352ull),__builtin_bit_cast(double, (uint64_t)0xbc8ebf0284c27612ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feab0002a4f3e4bull),__builtin_bit_cast(double, (uint64_t)0xbc69fd043cff3f5full)}, -{__builtin_bit_cast(double, (uint64_t)0x3feacfffd7ae1ed1ull),__builtin_bit_cast(double, (uint64_t)0xbc823ee7129070b4ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feaefffee510478ull),__builtin_bit_cast(double, (uint64_t)0x3c6a063ee00edea3ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feb0fffdb650d5bull),__builtin_bit_cast(double, (uint64_t)0x3c5a06c8381f0ab9ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feb2ffffeaaca57ull),__builtin_bit_cast(double, (uint64_t)0xbc79011e74233c1dull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feb4fffd995badcull),__builtin_bit_cast(double, (uint64_t)0xbc79ff1068862a9full)}, -{__builtin_bit_cast(double, (uint64_t)0x3feb7000249e659cull),__builtin_bit_cast(double, (uint64_t)0x3c8aff45d0864f3eull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feb8ffff9871640ull),__builtin_bit_cast(double, (uint64_t)0x3c7cfe7796c2c3f9ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3febafffd204cb4full),__builtin_bit_cast(double, (uint64_t)0xbc63ff27eef22bc4ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3febcfffd2415c45ull),__builtin_bit_cast(double, (uint64_t)0xbc6cffb7ee3bea21ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3febeffff86309dfull),__builtin_bit_cast(double, (uint64_t)0xbc814103972e0b5cull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fec0fffe1b57653ull),__builtin_bit_cast(double, (uint64_t)0x3c8bc16494b76a19ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fec2ffff1fa57e3ull),__builtin_bit_cast(double, (uint64_t)0xbc64feef8d30c6edull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fec4fffdcbfe424ull),__builtin_bit_cast(double, (uint64_t)0xbc843f68bcec4775ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fec6fffed54b9f7ull),__builtin_bit_cast(double, (uint64_t)0x3c847ea3f053e0ecull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fec8fffeb998fd5ull),__builtin_bit_cast(double, (uint64_t)0x3c7383068df992f1ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fecb0002125219aull),__builtin_bit_cast(double, (uint64_t)0xbc68fd8e64180e04ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feccfffdd94469cull),__builtin_bit_cast(double, (uint64_t)0x3c8e7ebe1cc7ea72ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fecefffeafdc476ull),__builtin_bit_cast(double, (uint64_t)0x3c8ebe39ad9f88feull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fed1000169af82bull),__builtin_bit_cast(double, (uint64_t)0x3c757d91a8b95a71ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fed30000d0ff71dull),__builtin_bit_cast(double, (uint64_t)0x3c89c1906970c7daull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fed4fffea790fc4ull),__builtin_bit_cast(double, (uint64_t)0xbc580e37c558fe0cull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fed70002edc87e5ull),__builtin_bit_cast(double, (uint64_t)0xbc7f80d64dc10f44ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fed900021dc82aaull),__builtin_bit_cast(double, (uint64_t)0xbc747c8f94fd5c5cull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fedafffd86b0283ull),__builtin_bit_cast(double, (uint64_t)0x3c8c7f1dc521617eull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fedd000296c4739ull),__builtin_bit_cast(double, (uint64_t)0x3c88019eb2ffb153ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fedefffe54490f5ull),__builtin_bit_cast(double, (uint64_t)0x3c6e00d2c652cc89ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fee0fffcdabf694ull),__builtin_bit_cast(double, (uint64_t)0xbc7f8340202d69d2ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fee2fffdb52c8ddull),__builtin_bit_cast(double, (uint64_t)0x3c7b00c1ca1b0864ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fee4ffff24216efull),__builtin_bit_cast(double, (uint64_t)0x3c72ffa8b094ab51ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fee6fffe88a5e11ull),__builtin_bit_cast(double, (uint64_t)0xbc57f673b1efbe59ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fee9000119eff0dull),__builtin_bit_cast(double, (uint64_t)0xbc84808d5e0bc801ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feeafffdfa51744ull),__builtin_bit_cast(double, (uint64_t)0x3c780006d54320b5ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feed0001a127fa1ull),__builtin_bit_cast(double, (uint64_t)0xbc5002f860565c92ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3feef00007babcc4ull),__builtin_bit_cast(double, (uint64_t)0xbc8540445d35e611ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fef0ffff57a8d02ull),__builtin_bit_cast(double, (uint64_t)0xbc4ffb3139ef9105ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fef30001ee58ac7ull),__builtin_bit_cast(double, (uint64_t)0x3c8a81acf2731155ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fef4ffff5823494ull),__builtin_bit_cast(double, (uint64_t)0x3c8a3f41d4d7c743ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fef6ffffca94c6bull),__builtin_bit_cast(double, (uint64_t)0xbc6202f41c987875ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fef8fffe1f9c441ull),__builtin_bit_cast(double, (uint64_t)0x3c777dd1f477e74bull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fefafffd2e0e37eull),__builtin_bit_cast(double, (uint64_t)0xbc6f01199a7ca331ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fefd0001c77e49eull),__builtin_bit_cast(double, (uint64_t)0x3c7181ee4bceacb1ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3fefeffff7e0c331ull),__builtin_bit_cast(double, (uint64_t)0xbc6e05370170875aull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff00ffff465606eull),__builtin_bit_cast(double, (uint64_t)0xbc8a7ead491c0adaull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff02ffff3867a58ull),__builtin_bit_cast(double, (uint64_t)0xbc977f69c3fcb2e0ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff04ffffdfc0d17ull),__builtin_bit_cast(double, (uint64_t)0x3c97bffe34cb945bull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff0700003cd4d82ull),__builtin_bit_cast(double, (uint64_t)0x3c820083c0e456cbull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff08ffff9f2cbe8ull),__builtin_bit_cast(double, (uint64_t)0xbc6dffdfbe37751aull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff0b000010cda65ull),__builtin_bit_cast(double, (uint64_t)0xbc913f7faee626ebull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff0d00001a4d338ull),__builtin_bit_cast(double, (uint64_t)0x3c807dfa79489ff7ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff0effffadafdfdull),__builtin_bit_cast(double, (uint64_t)0xbc77040570d66bc0ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff110000bbafd96ull),__builtin_bit_cast(double, (uint64_t)0x3c8e80d4846d0b62ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff12ffffae5f45dull),__builtin_bit_cast(double, (uint64_t)0x3c9dbffa64fd36efull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff150000dd59ad9ull),__builtin_bit_cast(double, (uint64_t)0x3c9a0077701250aeull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff170000f21559aull),__builtin_bit_cast(double, (uint64_t)0x3c8dfdf9e2e3deeeull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff18ffffc275426ull),__builtin_bit_cast(double, (uint64_t)0x3c910030dc3b7273ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff1b000123d3c59ull),__builtin_bit_cast(double, (uint64_t)0x3c997f7980030188ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff1cffff8299eb7ull),__builtin_bit_cast(double, (uint64_t)0xbc65f932ab9f8c67ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff1effff48ad400ull),__builtin_bit_cast(double, (uint64_t)0x3c937fbf9da75bebull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff210000c8b86a4ull),__builtin_bit_cast(double, (uint64_t)0x3c9f806b91fd5b22ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff2300003854303ull),__builtin_bit_cast(double, (uint64_t)0x3c93ffc2eb9fbf33ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff24fffffbcf684ull),__builtin_bit_cast(double, (uint64_t)0x3c7601e77e2e2e72ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff26ffff52921d9ull),__builtin_bit_cast(double, (uint64_t)0x3c7ffcbb767f0c61ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff2900014933a3cull),__builtin_bit_cast(double, (uint64_t)0xbc7202ca3c02412bull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff2b00014556313ull),__builtin_bit_cast(double, (uint64_t)0xbc92808233f21f02ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff2cfffebfe523bull),__builtin_bit_cast(double, (uint64_t)0xbc88ff7e384fdcf2ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff2f0000bb8ad96ull),__builtin_bit_cast(double, (uint64_t)0xbc85ff51503041c5ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff30ffffb7ae2afull),__builtin_bit_cast(double, (uint64_t)0xbc810071885e289dull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff32ffffeac5f7full),__builtin_bit_cast(double, (uint64_t)0xbc91ff5d3fb7b715ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff350000ca66756ull),__builtin_bit_cast(double, (uint64_t)0x3c957f82228b82bdull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff3700011fbf721ull),__builtin_bit_cast(double, (uint64_t)0x3c8000bac40dd5ccull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff38ffff9592fb9ull),__builtin_bit_cast(double, (uint64_t)0xbc943f9d2db2a751ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff3b00004ddd242ull),__builtin_bit_cast(double, (uint64_t)0x3c857f6b707638e1ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff3cffff5b2c957ull),__builtin_bit_cast(double, (uint64_t)0x3c7a023a10bf1231ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff3efffeab0b418ull),__builtin_bit_cast(double, (uint64_t)0x3c987f6d66b152b0ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff410001532aff4ull),__builtin_bit_cast(double, (uint64_t)0x3c67f8375f198524ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff4300017478b29ull),__builtin_bit_cast(double, (uint64_t)0x3c8301e672dc5143ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff44fffe795b463ull),__builtin_bit_cast(double, (uint64_t)0x3c89ff69b8b2895aull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff46fffe80475e0ull),__builtin_bit_cast(double, (uint64_t)0xbc95c0b19bc2f254ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff48fffef6fc1e7ull),__builtin_bit_cast(double, (uint64_t)0x3c9b4009f23a2a72ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff4afffe5bea704ull),__builtin_bit_cast(double, (uint64_t)0xbc94ffb7bf0d7d45ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff4d000171027deull),__builtin_bit_cast(double, (uint64_t)0xbc99c06471dc6a3dull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff4f0000ff03ee2ull),__builtin_bit_cast(double, (uint64_t)0x3c977f890b85531cull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff5100012dc4bd1ull),__builtin_bit_cast(double, (uint64_t)0x3c6004657166a436ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff530001605277aull),__builtin_bit_cast(double, (uint64_t)0xbc96bfcece233209ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff54fffecdb704cull),__builtin_bit_cast(double, (uint64_t)0xbc8902720505a1d7ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff56fffef5f54a9ull),__builtin_bit_cast(double, (uint64_t)0x3c9bbfe60ec96412ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff5900017e61012ull),__builtin_bit_cast(double, (uint64_t)0x3c887ec581afef90ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff5b00003c93e92ull),__builtin_bit_cast(double, (uint64_t)0xbc9f41080abf0cc0ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff5d0001d4919bcull),__builtin_bit_cast(double, (uint64_t)0xbc98812afb254729ull)}, -{__builtin_bit_cast(double, (uint64_t)0x3ff5efffe7b87a89ull),__builtin_bit_cast(double, (uint64_t)0xbc947eb780ed6904ull)} -}; diff --git a/github-simulator/source/gpu/sim/engine_exceptions.h b/github-simulator/source/gpu/sim/engine_exceptions.h deleted file mode 100644 index 6464c08d58f8692a0b9a77bc38b5b396dba3f653..0000000000000000000000000000000000000000 --- a/github-simulator/source/gpu/sim/engine_exceptions.h +++ /dev/null @@ -1,9 +0,0 @@ -// Generated by examples/gpu_engine_scan.rs: every float with |x| <= 16 whose -// engine_sin_cos_raw result differs from x87 FSINCOS. Keys: input bits, sorted. -#pragma once -#include -constexpr int ENGINE_EXCEPTION_COUNT = 0; -constexpr int ENGINE_EXCEPTION_STEP = 0; -__device__ constexpr uint32_t ENGINE_EXCEPTIONS[1][3] = { - {0, 0, 0}, -}; diff --git a/github-simulator/source/gpu/sim/math.h b/github-simulator/source/gpu/sim/math.h deleted file mode 100644 index a14634eedd502d98edefe7c6d863b1fa69db9f3b..0000000000000000000000000000000000000000 --- a/github-simulator/source/gpu/sim/math.h +++ /dev/null @@ -1,540 +0,0 @@ -// Bit-exact device ports of the CPU math in src/math/native_math.rs, -// src/math/godot_math.rs (managed_sin_cos), src/math/double_math.rs and src/nn/network.rs. -// Build with -ffp-contract=off: a fused multiply-add rounds once and breaks -// bit equality. Arguments outside the ported domains set an `err` bit instead -// of taking the rare large-argument paths of the CPU code. -#pragma once -#include "compat.h" -#include -#include "double_tables.h" -#include "engine_exceptions.h" - -namespace altd { - -enum : uint32_t { - ERR_MANAGED_LARGE = 1u << 0, // managed_sin_cos |x| > 10000 (sine_large not ported) - ERR_NATIVE_LARGE = 1u << 1, // musl sinf/cosf |x| >= 0x4dc90fdb (Payne-Hanek not ported) - ERR_ENGINE_DOMAIN = 1u << 2, // engine_sin_cos |x| > ENGINE_DOMAIN (not validated against x87) -}; - -__device__ __host__ inline uint32_t fbits(float x) { return __builtin_bit_cast(uint32_t, x); } -__device__ __host__ inline float ffrom(uint32_t b) { return __builtin_bit_cast(float, b); } -__device__ __host__ inline uint64_t dbits(double x) { return __builtin_bit_cast(uint64_t, x); } -__device__ __host__ inline double dfrom(uint64_t b) { return __builtin_bit_cast(double, b); } - -// ---- musl sinf/cosf (native_math.rs) ------------------------------------ - -__device__ inline float sin_kernel(double x) { - double z = x * x; - const double s1 = -0.16666666666666666; - if (x > -7.8175831586122513e-3 && x < 7.8175831586122513e-3) return (float)(x * (1.0 + s1 * z)); - double w = z * z; - double r = -0.0001984126984126984 + z * 2.7557319223985893e-6; - double s = z * x; - return (float)((x + s * (s1 + z * 0.008333333333333333)) + s * w * r); -} -__device__ inline float cos_kernel(double x) { - double z = x * x; - if (x > -7.816314697265625e-3 && x < 7.816314697265625e-3) return (float)(1.0 - 0.5 * z); - return (float)(1.0 + z * (-0.5 + z * (0.041666666666666664 + z * (-0.001388888888888889 - + z * (2.48015873015873e-5 + z * -2.755731922398589e-7))))); -} -// Medium reduction; the large path is flagged. -__device__ inline int reduce_medium(float x, double& y, uint32_t& err) { - if ((fbits(x) & 0x7fffffffu) >= 0x4dc90fdbu) err |= ERR_NATIVE_LARGE; - const double toint = 1.5 / 2.220446049250313e-16; - double n = ((double)x * 6.36619772367581382433e-1 + toint) - toint; - double xd = (double)x; - y = xd - n * 1.57079631090164184570 - n * 1.58932547735281966916e-8; - const double quarter = (double)0.7853981852531433f; // f32 FRAC_PI_4 - if (y < -quarter) { n -= 1.0; y = xd - n * 1.57079631090164184570 - n * 1.58932547735281966916e-8; } - else if (y > quarter) { n += 1.0; y = xd - n * 1.57079631090164184570 - n * 1.58932547735281966916e-8; } - return (int)n; -} -__device__ inline float native_sin(float x, uint32_t& err) { - uint32_t bits = fbits(x) & 0x7fffffffu; - bool negative = signbit(x); - double a = (double)x; - const double p = 1.5707963267948966; - if (bits <= 0x3f490fdau) return bits < 0x39800000u ? x : sin_kernel(a); - if (bits <= 0x407b53d1u) { - if (bits <= 0x4016cbe3u) return negative ? -cos_kernel(a + p) : cos_kernel(a - p); - return sin_kernel(negative ? -(a + 2.0 * p) : -(a - 2.0 * p)); - } - if (bits <= 0x40e231d5u) { - if (bits <= 0x40afeddfu) return negative ? cos_kernel(a + 3.0 * p) : -cos_kernel(a - 3.0 * p); - return sin_kernel(negative ? a + 4.0 * p : a - 4.0 * p); - } - if (!isfinite(x)) return altd_invalid(x); - double y; - int n = reduce_medium(x, y, err); - switch (n & 3) { case 0: return sin_kernel(y); case 1: return cos_kernel(y); case 2: return sin_kernel(-y); default: return -cos_kernel(y); } -} -__device__ inline float native_cos(float x, uint32_t& err) { - uint32_t bits = fbits(x) & 0x7fffffffu; - bool negative = signbit(x); - double a = (double)x; - const double p = 1.5707963267948966; - if (bits <= 0x3f490fdau) return bits < 0x39800000u ? 1.0f : cos_kernel(a); - if (bits <= 0x407b53d1u) { - if (bits > 0x4016cbe3u) return -cos_kernel(negative ? a + 2.0 * p : a - 2.0 * p); - return sin_kernel(negative ? a + p : p - a); - } - if (bits <= 0x40e231d5u) { - if (bits > 0x40afeddfu) return cos_kernel(negative ? a + 4.0 * p : a - 4.0 * p); - return sin_kernel(negative ? -a - 3.0 * p : a - 3.0 * p); - } - if (!isfinite(x)) return altd_invalid(x); - double y; - int n = reduce_medium(x, y, err); - switch (n & 3) { case 0: return cos_kernel(y); case 1: return sin_kernel(-y); case 2: return -cos_kernel(y); default: return sin_kernel(y); } -} - -__device__ inline float native_atan(float x) { - bool negative = signbit(x); - uint32_t bits = fbits(x) & 0x7fffffffu; - const float high[4] = {ffrom(0x3eed6338u), ffrom(0x3f490fdau), ffrom(0x3f7b985eu), ffrom(0x3fc90fdau)}; - const float low[4] = {ffrom(0x31ac3769u), ffrom(0x33222168u), ffrom(0x33140fb4u), ffrom(0x33a22168u)}; - if (bits >= 0x4c800000u) return isnan(x) ? x : copysignf(high[3], x); - int id; - if (bits < 0x3ee00000u) { - if (bits < 0x39800000u) return x; - id = -1; - } else { - x = fabsf(x); - if (bits < 0x3f980000u) { - if (bits < 0x3f300000u) { id = 0; x = (2.0f * x - 1.0f) / (2.0f + x); } - else { id = 1; x = (x - 1.0f) / (x + 1.0f); } - } else if (bits < 0x401c0000u) { id = 2; x = (x - 1.5f) / (1.0f + 1.5f * x); } - else { id = 3; x = -1.0f / x; } - } - float z = x * x; - float w = z * z; - float s1 = z * (3.3333328366e-01f + w * (1.4253635705e-01f + w * 6.1687607318e-02f)); - float s2 = w * (-1.9999158382e-01f + w * -1.0648017377e-01f); - if (id < 0) return x - x * (s1 + s2); - float r = high[id] - ((x * (s1 + s2) - low[id]) - x); - return negative ? -r : r; -} -__device__ inline float native_atan2(float y, float x) { - const float pi = 3.14159274101257324f; - const float pi_lo = ffrom(0xb3bbbd2eu); - if (isnan(x) || isnan(y)) return altd_nan_operand(y, x); // the CPU's `x + y` returns y when both are NaN - uint32_t ix = fbits(x), iy = fbits(y); - if (ix == 0x3f800000u) return native_atan(y); - uint32_t m = ((iy >> 31) & 1u) | ((ix >> 30) & 2u); - ix &= 0x7fffffffu; iy &= 0x7fffffffu; - if (iy == 0) return m <= 1 ? y : (m == 2 ? pi : -pi); - if (ix == 0) return copysignf(pi / 2.0f, y); - if (ix == 0x7f800000u) { - if (iy == 0x7f800000u) { - const float v[4] = {pi / 4.0f, -pi / 4.0f, 3.0f * pi / 4.0f, -3.0f * pi / 4.0f}; - return v[m]; - } - const float v[4] = {0.0f, -0.0f, pi, -pi}; - return v[m]; - } - if (ix + (26u << 23) < iy || iy == 0x7f800000u) return copysignf(pi / 2.0f, y); - float z = ((m & 2) && iy + (26u << 23) < ix) ? 0.0f : native_atan(fabsf(y / x)); - switch (m) { case 0: return z; case 1: return -z; case 2: return pi - (z - pi_lo); default: return (z - pi_lo) - pi; } -} - -// ---- .NET MathF.SinCos (godot_math.rs managed_sin_cos) -------------------- - -// Straight-line for |value| <= 10000; larger arguments are flagged. -__device__ inline float packed_sine(float value, uint32_t& err) { - float a = fabsf(value); - if (!(a <= 10000.0f)) err |= ERR_MANAGED_LARGE; - float rounded = a * ffrom(0x3ea2f983u) + ffrom(0x4b400000u); - float n = rounded - ffrom(0x4b400000u); - float r = a - n * ffrom(0x40490000u); - r -= n * ffrom(0x3a7da000u); - r -= n * ffrom(0x34222000u); - r -= n * ffrom(0x2cb4611au); - float square = r * r; - r = ffrom(fbits(r) ^ (fbits(rounded) << 31)); - float p = ffrom(0x362edef8u) * square; - p += ffrom(0xb94fb7ffu); - p *= square; - p += ffrom(0x3c088766u); - p *= square; - p += ffrom(0xbe2aaaa6u); - float result = r + (square * p) * r; - return ffrom(fbits(result) ^ (fbits(value) & 0x80000000u)); -} -__device__ inline void managed_sin_cos(float angle, float& s, float& c, uint32_t& err) { - if (isnan(angle)) { s = angle; c = ffrom((fbits(angle) & 0x7fffffffu) | 0x00400000u); return; } - float a = fabsf(angle); - if (!(a >= ffrom(0x39000000u))) { s = angle; c = 1.0f - a * a * 0.5f; return; } - s = packed_sine(angle, err); - c = packed_sine(a + 1.57079637050628662f, err); -} - -// ---- x87 FSINCOS (native_math.rs engine_sin_cos) -------------------------- -// fdlibm kernels in f64 after a Cody-Waite reduction; for float inputs up to -// ENGINE_DOMAIN the product n * PIO2_1 and the first subtraction are exact. -// The rounded float results equal x87 except for the inputs in -// engine_exceptions.h, found by an exhaustive scan (examples/gpu_engine_scan.rs). - -__device__ inline void engine_sin_cos_raw(float xf, float& s, float& c) { - double x = (double)xf; - double n = rint(x * 6.36619772367581382433e-01); - double y = (x - n * 1.57079632673412561417e+00) - n * 6.07710050650619224932e-11; - y = n == 0.0 ? x : y; // keeps -0 - double z = y * y, w = z * z; - double r = 8.33333333332248946124e-03 + z * (-1.98412698298579493134e-04 + z * 2.75573137070700676789e-06) - + z * w * (-2.50507602534068634195e-08 + z * 1.58969099521155010221e-10); - // fdlibm returns tiny arguments unchanged, which also keeps the sign of -0. - double sy = fabs(y) < 0x1p-27 ? y : y + z * y * (-1.66666666666666324348e-01 + z * r); - double rc = z * (4.16666666666666019037e-02 + z * (-1.38888888888741095749e-03 + z * 2.48015872894767294178e-05)) - + w * w * (-2.75573143513906633035e-07 + z * (2.08757232129817482790e-09 + z * -1.13596475577881948265e-11)); - double hz = 0.5 * z, one_minus = 1.0 - hz; - double cy = one_minus + (((1.0 - one_minus) - hz) + z * rc); - int q = (int)n & 3; - double sv = (q & 1) ? cy : sy, cv = (q & 1) ? sy : cy; - sv = (q & 2) ? -sv : sv; - cv = ((q + 1) & 2) ? -cv : cv; - s = (float)sv; - c = (float)cv; -} - -constexpr float ENGINE_DOMAIN = 16.0f; - -// Branch-free binary search over the sorted exception keys. -__device__ inline void engine_sin_cos(float x, float& s, float& c, uint32_t& err) { - if (!(fabsf(x) <= ENGINE_DOMAIN)) err |= ERR_ENGINE_DOMAIN; - engine_sin_cos_raw(x, s, c); - if (ENGINE_EXCEPTION_COUNT == 0) return; - uint32_t key = fbits(x); - int lo = 0; - for (int step = ENGINE_EXCEPTION_STEP; step > 0; step >>= 1) { - int probe = lo + step; - lo = (probe < ENGINE_EXCEPTION_COUNT && ENGINE_EXCEPTIONS[probe][0] <= key) ? probe : lo; - } - bool hit = ENGINE_EXCEPTIONS[lo][0] == key; - s = hit ? ffrom(ENGINE_EXCEPTIONS[lo][1]) : s; - c = hit ? ffrom(ENGINE_EXCEPTIONS[lo][2]) : c; -} - -// ---- musl/Arm double exp and pow (double_math.rs) ------------------------- - -__device__ inline double exp_special(double tmp, uint64_t bits, uint64_t k) { - if ((k & 0x80000000ull) == 0) { - bits -= 1009ull << 52; - double scale = dfrom(bits); - return dfrom(2032ull << 52) * (scale + scale * tmp); - } - bits += 1022ull << 52; - double scale = dfrom(bits); - double y = scale + scale * tmp; - if (fabs(y) < 1.0) { - double one = y < 0.0 ? -1.0 : 1.0; - double lo = scale - y + scale * tmp; - double hi = one + y; - lo = one - hi + y + lo; - y = (hi + lo) - one; - if (y == 0.0) y = dfrom(bits & (1ull << 63)); - } - return 2.2250738585072014e-308 * y; -} -__device__ inline double exp_inner(double x, double xtail, bool negative) { - uint64_t exponent = (dbits(x) >> 52) & 0x7ff; - if (exponent < 969) return negative ? -(1.0 + x) : 1.0 + x; - if (exponent >= 1033) { - double y = signbit(x) ? 0.0 : INFINITY; - return negative ? -y : y; - } - double kd = round(INV_LN2_N * x); - uint64_t ki = (uint64_t)(int64_t)kd; - double r = (x + kd * NEG_LN2_HI_N + kd * NEG_LN2_LO_N) + xtail; - int idx = 2 * (int)(ki % 128); - uint64_t bias = negative ? (0x800ull << 7) : 0; - uint64_t top = (ki + bias) << 45; - double tail = dfrom(EXP_TAB[idx]); - uint64_t bits = EXP_TAB[idx + 1] + top; - double r2 = r * r; - double tmp = tail + r + r2 * (EXP_POLY[0] + r * EXP_POLY[1]) + r2 * r2 * (EXP_POLY[2] + r * EXP_POLY[3]); - if (exponent >= 1032) return exp_special(tmp, bits, ki); - double scale = dfrom(bits); - return scale + scale * tmp; -} -__device__ inline double dexp(double x) { - if (isnan(x)) return x; - return exp_inner(x, 0.0, false); -} -__device__ inline void pow_log(uint64_t ix, double& out_hi, double& out_lo) { - uint64_t tmp = ix - 0x3fe6955500000000ull; - int i = (int)((tmp >> 45) % 128); - double kd = (double)((int64_t)tmp >> 52); - uint64_t iz = ix - (tmp & (0xfffull << 52)); - double z = dfrom(iz); - double invc = POW_TAB[i][0], logc = POW_TAB[i][1], logctail = POW_TAB[i][2]; - double zhi = dfrom((iz + (1ull << 31)) & (~0ull << 32)); - double zlo = z - zhi; - double rhi = zhi * invc - 1.0; - double rlo = zlo * invc; - double r = rhi + rlo; - double t1 = kd * LN2_HI + logc; - double t2 = t1 + r; - double lo1 = kd * LN2_LO + logctail; - double lo2 = t1 - t2 + r; - const double* a = POW_POLY; - double ar = a[0] * r; - double ar2 = r * ar; - double ar3 = r * ar2; - double arhi = a[0] * rhi; - double arhi2 = rhi * arhi; - double hi = t2 + arhi2; - double lo3 = rlo * (ar + arhi); - double lo4 = t2 - hi + arhi2; - double p = ar3 * (a[1] + r * a[2] + ar2 * (a[3] + r * a[4] + ar2 * (a[5] + r * a[6]))); - double lo = lo1 + lo2 + lo3 + lo4 + p; - double y = hi + lo; - out_hi = y; - out_lo = hi - y + lo; -} -__device__ inline uint32_t integer_kind(uint64_t bits) { - uint64_t e = (bits >> 52) & 0x7ff; - if (e < 1023) return 0; - if (e > 1075) return 2; - uint64_t bit = 1ull << (1075 - e); - return (bits & (bit - 1)) ? 0 : ((bits & bit) ? 1 : 2); -} -__device__ inline double dpow(double x, double y) { - // pow(1, y) is 1 for every y, NaN included, as the paths below also give; - // answering first skips the logarithm, e.g. for godot_ease(0, curve). - if (dbits(x) == dbits(1.0)) return 1.0; - const uint64_t SIGN = 1ull << 63; - uint64_t ix = dbits(x), iy = dbits(y); - uint32_t topx = (uint32_t)(ix >> 52), topy = (uint32_t)(iy >> 52); - bool negative = false; - if (topx - 1u >= 0x7feu || (topy & 0x7ffu) - 0x3beu >= 0x80u) { - if (y == 0.0 || !isfinite(y)) { - if (y == 0.0) return 1.0; - if (x == 1.0) return 1.0; - if (isnan(x) || isnan(y)) return altd_nan_operand(x, y); - if (fabs(x) == 1.0) return 1.0; - if ((fabs(x) < 1.0) == !signbit(y)) return 0.0; - return y * y; - } - if (x == 0.0 || !isfinite(x)) { - double x2 = isnan(x) ? altd_nan_operand(x, x) : x * x; - if (signbit(x) && integer_kind(iy) == 1) x2 = dfrom(dbits(x2) ^ (1ull << 63)); // -x2, NaN included - return signbit(y) && !isnan(x2) ? 1.0 / x2 : x2; - } - if (signbit(x)) { - uint32_t kind = integer_kind(iy); - if (kind == 0) return dfrom(0xfff8000000000000ull); - negative = kind == 1; - ix &= ~SIGN; topx &= 0x7ff; - } - if ((topy & 0x7ffu) - 0x3beu >= 0x80u) { - if (ix == dbits(1.0)) return 1.0; - if ((topy & 0x7ffu) < 0x3beu) return ix > dbits(1.0) ? 1.0 + y : 1.0 - y; - return (ix > dbits(1.0)) == (topy < 0x800u) ? INFINITY : 0.0; - } - if (topx == 0) ix = (dbits(x * 4503599627370496.0) & ~SIGN) - (52ull << 52); - } - double hi, lo; - pow_log(ix, hi, lo); - double yhi = dfrom(iy & (~0ull << 27)); - double ylo = y - yhi; - double lhi = dfrom(dbits(hi) & (~0ull << 27)); - double llo = hi - lhi + lo; - double ehi = yhi * lhi; - double elo = ylo * lhi + y * llo; - return exp_inner(ehi, elo, negative); -} - -// ---- Network activation (network.rs game_expm1 / game_tanh) --------------- - -__device__ inline double game_expm1(double x) { - uint32_t high = (uint32_t)(dbits(x) >> 32) & 0x7fffffffu; - bool negative = signbit(x); - if (high >= 0x4043687au) { - if (isnan(x)) return x; - if (isinf(x)) return negative ? -1.0 : x; - if (negative) return -1.0; - if (x > 7.09782712893383973096e2) return INFINITY; - } - int k; double c; - if (high > 0x3fd62e42u) { - double hi, lo; - if (high < 0x3ff0a2b2u) { - if (!negative) { hi = x - 6.93147180369123816490e-1; lo = 1.90821492927058770002e-10; k = 1; } - else { hi = x + 6.93147180369123816490e-1; lo = -1.90821492927058770002e-10; k = -1; } - } else { - k = (int)(1.44269504088896338700 * x + (negative ? -0.5 : 0.5)); - hi = x - (double)k * 6.93147180369123816490e-1; - lo = (double)k * 1.90821492927058770002e-10; - } - x = hi - lo; c = (hi - x) - lo; - } else if (high < 0x3c900000u) { - return x; - } else { k = 0; c = 0.0; } - double hfx = 0.5 * x; - double hxs = x * hfx; - double r1 = 1.0 + hxs * (-3.33333333333331316428e-2 + hxs * (1.58730158725481460165e-3 + hxs * (-7.93650757867487942473e-5 - + hxs * (4.00821782732936239552e-6 + hxs * (-2.01099218183624371326e-7))))); - double t = 3.0 - r1 * hfx; - double e = hxs * ((r1 - t) / (6.0 - x * t)); - if (k == 0) return x - (x * e - hxs); - e = x * (e - c) - c; - e -= hxs; - if (k == -1) return 0.5 * (x - e) - 0.5; - if (k == 1) return x < -0.25 ? -2.0 * (e - (x + 0.5)) : 1.0 + 2.0 * (x - e); - double twopk = dfrom((uint64_t)(0x3ff + k) << 52); - if (k < 0 || k > 56) { - double y = x - e + 1.0; - return (k == 1024 ? y * 2.0 * dfrom(0x7fe0000000000000ull) : y * twopk) - 1.0; - } - double inverse = dfrom((uint64_t)(0x3ff - k) << 52); - return k < 20 ? (x - e + (1.0 - inverse)) * twopk : (x - (e + inverse) + 1.0) * twopk; -} -// The scalar tanh, one branch per range. A wave whose lanes fall in different -// ranges executes every taken branch in turn, and the three expm1 call sites -// are inlined separately, so the double-precision work (1/32 rate on RDNA3) -// runs up to three times per layer. Kept for comparison: -DALTD_TANH_BRANCHY. -__device__ inline double game_tanh_branchy(double value) { - double x = fabs(value); - uint32_t high = (uint32_t)(dbits(x) >> 32); - double t; - if (high > 0x3fe193eau) { - if (high > 0x40340000u) t = 1.0 - 0.0 / x; - else { double u = game_expm1(2.0 * x); t = 1.0 - 2.0 / (u + 2.0); } - } else if (high > 0x3fd058aeu) { - double u = game_expm1(2.0 * x); t = u / (u + 2.0); - } else if (high >= 0x00100000u) { - double u = game_expm1(-2.0 * x); t = -u / (u + 2.0); - } else t = x; - return signbit(value) ? -t : t; -} - -// ---- branch-free game_tanh ------------------------------------------------ -// One expm1 evaluation per lane on a selected argument, then selects, so the -// lanes of a wave never serialize the tanh ranges or the expm1 cases. Each -// lane's selected expression is exactly what the scalar code evaluates on -// that path; unselected values are computed and dropped. This is the scalar -// form of `tanh4`/`expm1_tanh` in src/nn/network_simd.rs, whose CPU tests -// compare it with the scalar functions bit for bit over every branch, -// threshold and special value. - -// 2^k by exponent-field arithmetic, as the scalar code. -__device__ inline double pow2_of(int32_t k) { - return dfrom((uint64_t)(int64_t)(k + 0x3ff) << 52); -} - -// `game_expm1` for the arguments `game_tanh` selects: finite, below 709.78 -// when positive, so the overflow block of the scalar code never returns early. -// The integer k is kept as an integer for its comparisons and powers of two -// (the scalar code's `k as f64` only enters the two reduction products). -__device__ inline double expm1_lane(double x) { - uint32_t h = (uint32_t)(dbits(x) >> 32) & 0x7fffffffu; - bool negative = signbit(x); - bool reduce = h > 0x3fd62e42u, tiny = h < 0x3c900000u, near = h < 0x3ff0a2b2u; - // k = +-1 near ln 2, else trunc(x / ln 2 +- 0.5); 0 without reduction. - double rounded = 1.44269504088896338700 * x + (negative ? -0.5 : 0.5); - int32_t ki = reduce ? (near ? (negative ? -1 : 1) : __double2int_rz(rounded)) : 0; - double k = (double)ki; - // For k = +-1, x - k*ln2_hi and k*ln2_lo equal the scalar x -+ ln2_hi, +-ln2_lo exactly. - double hi = x - k * 6.93147180369123816490e-1; - double lo = k * 1.90821492927058770002e-10; - double xr = reduce ? hi - lo : x; - double c = reduce ? (hi - xr) - lo : 0.0; - double hfx = 0.5 * xr; - double hxs = xr * hfx; - double p = hxs * -2.01099218183624371326e-7; - p = hxs * (4.00821782732936239552e-6 + p); - p = hxs * (-7.93650757867487942473e-5 + p); - p = hxs * (1.58730158725481460165e-3 + p); - p = hxs * (-3.33333333333331316428e-2 + p); - double r1 = 1.0 + p; - double t = 3.0 - r1 * hfx; - double e = hxs * ((r1 - t) / (6.0 - xr * t)); - double k0 = xr - (xr * e - hxs); - e = (xr * (e - c) - c) - hxs; - double xme = xr - e; - double km1 = 0.5 * xme - 0.5; - double k1 = xr < -0.25 ? -2.0 * (e - (xr + 0.5)) : 1.0 + 2.0 * xme; - double twopk = pow2_of(ki), inverse = pow2_of(-ki); - // k < 0 || k > 56 (k == 1024 needs x > 709, outside the domain). - bool far = ki < 0 || ki > 56; - double far_value = (xme + 1.0) * twopk - 1.0; - double below20 = (xme + (1.0 - inverse)) * twopk; - double from20 = ((xr - (e + inverse)) + 1.0) * twopk; - double general = far ? far_value : (ki < 20 ? below20 : from20); - double result = ki == 0 ? k0 : (ki == -1 ? km1 : (ki == 1 ? k1 : general)); - return tiny ? x : result; -} - -__device__ inline double game_tanh(double value) { -#ifdef ALTD_TANH_BRANCHY - return game_tanh_branchy(value); -#else - double x = fabs(value); - uint32_t h = (uint32_t)(dbits(x) >> 32); - // Above 0x40340000 (|x| > 20, infinities, NaN) the scalar path stays a - // branch: NaN must keep its payload, and a NaN routed through the selects - // below can lose it on this hardware (fabs folded into the 32-bit select - // as a float source modifier, which quiets or canonicalizes under IEEE - // mode). Finite lanes never take it during inference. - if (h > 0x40340000u) { - double t = 1.0 - 0.0 / x; - return signbit(value) ? -t : t; - } - bool big = h > 0x3fe193eau, mid = h > 0x3fd058aeu, normal = h > 0x000fffffu; - double t = expm1_lane(mid ? 2.0 * x : -2.0 * x); - double d = t + 2.0; - // The range's own division: 2 / (t + 2) for the large range, t / (t + 2) - // otherwise; the small range's (-t) / (t + 2) is the exact negation. - double q = (big ? 2.0 : t) / d; - double result = big ? 1.0 - q : (mid ? q : (normal ? -q : x)); - return signbit(value) ? -result : result; -#endif -} - -// ---- math profiles (crate::math::profile) ---------------------------------- -// The Windows UCRT functions of math/kernels/ucrt.h. World::math_profile picks a -// profile for the whole simulation, so the branches below never diverge. - -#define ALTD_MATH_FN __device__ inline -#define ALTD_MATH_TABLE __device__ constexpr -#define ALTD_MATH_CONST constexpr -#include "../../math/kernels/ucrt.h" - -// MathProfile::index. -enum : uint32_t { MATH_PROTON = 0, MATH_WIN10_FMA3 = 1, MATH_WIN11_FMA3 = 2 }; - -// sinf and cosf are musl's with the Windows exceptions. Inputs beyond -// WIN_TRIG_LIMIT are flagged by the musl reduction already. -__device__ inline float profile_sin(uint32_t math, float x, uint32_t& err) { - float musl = native_sin(x, err); - return math == MATH_PROTON ? musl : win_sinf_fix(x, musl); -} -__device__ inline float profile_cos(uint32_t math, float x, uint32_t& err) { - float musl = native_cos(x, err); - return math == MATH_PROTON ? musl : win_cosf_fix(x, musl); -} -// The profile is uniform, so these branch rather than select (WGSL's select -// evaluates both operands). -__device__ inline float profile_atan2(uint32_t math, float y, float x) { - if (math == MATH_PROTON) return native_atan2(y, x); - return win_atan2f(y, x); -} -__device__ inline double profile_exp(uint32_t math, double x) { - if (math == MATH_PROTON) return dexp(x); - if (math == MATH_WIN10_FMA3) return win10_exp(x); - return win11_exp(x); -} -__device__ inline double profile_pow(uint32_t math, double x, double y) { - if (math == MATH_PROTON) return dpow(x, y); - if (math == MATH_WIN10_FMA3) return win10_pow(x, y); - return win11_pow(x, y); -} -__device__ inline double profile_tanh(uint32_t math, double x) { - if (math == MATH_PROTON) return game_tanh(x); - if (math == MATH_WIN10_FMA3) return win10_tanh(x); - return win11_tanh(x); -} - -} // namespace altd diff --git a/github-simulator/source/gpu/sim/rays.h b/github-simulator/source/gpu/sim/rays.h deleted file mode 100644 index 0c9d217fb645fbcda19df26042f9e9e572badfd2..0000000000000000000000000000000000000000 --- a/github-simulator/source/gpu/sim/rays.h +++ /dev/null @@ -1,118 +0,0 @@ -// BSP queries of src/track/bsp.rs with explicit stacks instead of recursion: -// RayTree::raycast and Node::closest, which share the RayTree arena. -#pragma once -#include "world.h" -#include "math.h" - -namespace altd { - -// `Bounds::beside`: box (cx, cy, hx, hy) lies beyond `margin` on one side of the ray's line. -__device__ inline bool beside(float4 box, float2 start, float2 b, float margin) { - float cross = b.x * (box.y - start.y) - b.y * (box.x - start.x); - return fabsf(cross) > fabsf(b.x) * (box.w + margin) + fabsf(b.y) * (box.z + margin); -} - -// `classify`: side of p relative to the wall (origin, direction). -__device__ inline int classify(float4 wall, float2 p) { - float cross = wall.z * (p.y - wall.y) - wall.w * (p.x - wall.x); - return cross > 0.0001f ? 1 : (cross < -0.0001f ? -1 : 0); -} - -// `godot_math::ray_wall_hit` -__device__ inline bool ray_wall_hit(float2 start, float2 b, float4 wall, float2& hit) { - float denominator = wall.z * b.y - b.x * wall.w; - float dx = wall.x - start.x, dy = wall.y - start.y; - float ray_fraction = ((0.0f - wall.w) * dx + wall.z * dy) / denominator; - float wall_fraction = (b.x * dy - b.y * dx) / denominator; - hit = make_float2(wall.x + wall.z * wall_fraction, wall.y + wall.w * wall_fraction); - return !(fabsf(denominator) < 1e-6f) && ray_fraction >= 0.0f && ray_fraction <= 1.0f - && wall_fraction >= 0.0f && wall_fraction <= 1.0f; -} - -// `RayTree::raycast`. The recursion "near subtree, else own walls, else the -// far subtree if the ray crosses the partition" becomes a descent loop that -// pushes each entered node, and a pop that tests the node's own walls and -// then continues into its far child. -__device__ inline bool raycast(const World& w, float2 start, float2 end, float2& hit) { - float2 b = make_float2(end.x - start.x, end.y - start.y); - float scale = w.ray_magnitude + fabsf(start.x) + fabsf(start.y); - float margin = fabsf(b.x) + fabsf(b.y) > 1e-10f ? 1e-3f + 2e-5f * scale : INFINITY; - uint32_t stack[MAX_RAY_DEPTH]; - int depth = 0; - uint32_t current = w.ray_node_count ? 0 : NONE; - for (;;) { - while (current != NONE) { - const RayNode& node = w.ray_nodes[current]; - if (beside(node.subtree, start, b, margin)) break; - float4 partition = w.ray_walls[node.first]; - int a = classify(partition, start), e = classify(partition, end); - bool front_first = a == 1 || (a == 0 && e == 1); - // Low bit: the ray crosses the partition, so the far side follows. - stack[depth++] = current << 1 | (a != e); - current = front_first ? node.front : node.back; - } - if (depth == 0) return false; - uint32_t entry = stack[--depth]; - const RayNode& node = w.ray_nodes[entry >> 1]; - if (!beside(node.own, start, b, margin)) { - float distance = 3.40282347e38f; - bool found = false; - for (uint32_t i = node.first; i < node.last; i++) { - float2 p; - bool ok = ray_wall_hit(start, b, w.ray_walls[i], p); - float dx = p.x - start.x, dy = p.y - start.y; - float d2 = dx * dx + dy * dy; - bool better = ok && d2 < distance; - distance = better ? d2 : distance; - hit = better ? p : hit; - found |= better; - } - if (found) return true; - } - float4 partition = w.ray_walls[node.first]; - bool front_first = classify(partition, start) == 1 - || (classify(partition, start) == 0 && classify(partition, end) == 1); - current = (entry & 1) ? (front_first ? node.back : node.front) : NONE; - } -} - -// `Node::closest`: own walls, then the near subtree, then the far subtree -// when the partition line is closer than the best wall so far. -__device__ inline float2 closest_wall_point(const World& w, float2 p) { - float distance = INFINITY; - float2 closest = make_float2(0.0f, 0.0f); - uint32_t stack[MAX_RAY_DEPTH]; - int depth = 0; - uint32_t current = w.ray_node_count ? 0 : NONE; - for (;;) { - while (current != NONE) { - const RayNode& node = w.ray_nodes[current]; - for (uint32_t i = node.first; i < node.last; i++) { - float4 wall = w.ray_walls[i]; - float len2 = wall.z * wall.z + wall.w * wall.w; - float t = (p.x * wall.z + p.y * wall.w) - (wall.x * wall.z + wall.y * wall.w); - t = t < 0.0f ? 0.0f : (t > len2 ? len2 : t); // f32::clamp - t = t / len2; - float2 candidate = len2 == 0.0f ? make_float2(wall.x, wall.y) - : make_float2(wall.x + wall.z * t, wall.y + wall.w * t); - float dx = p.x - candidate.x, dy = p.y - candidate.y; - float d2 = dx * dx + dy * dy; - bool better = d2 < distance; - distance = better ? d2 : distance; - closest = better ? candidate : closest; - } - stack[depth++] = current; - current = classify(w.ray_walls[node.first], p) >= 0 ? node.front : node.back; - } - if (depth == 0) return closest; - const RayNode& node = w.ray_nodes[stack[--depth]]; - float4 partition = w.ray_walls[node.first]; - float t = ((p.x - partition.x) * partition.z + (p.y - partition.y) * partition.w) - / (partition.z * partition.z + partition.w * partition.w); - float dx = p.x - (partition.x + partition.z * t), dy = p.y - (partition.y + partition.w * t); - bool far = dx * dx + dy * dy < distance; - current = far ? (classify(partition, p) >= 0 ? node.back : node.front) : NONE; - } -} - -} // namespace altd diff --git a/github-simulator/source/gpu/sim/sensors.h b/github-simulator/source/gpu/sim/sensors.h deleted file mode 100644 index 55fac1c0d517fad63de46cd8f5ec21e91497e9c1..0000000000000000000000000000000000000000 --- a/github-simulator/source/gpu/sim/sensors.h +++ /dev/null @@ -1,98 +0,0 @@ -// `Car::sensor` (src/physics/car.rs) for every sensor kind, reading the GPU car state. -#pragma once -#include "rays.h" -#include "track.h" - -namespace altd { - -constexpr float PI_F = 3.14159265358979323846f; - -struct Sensors { - uint32_t count, needs_path; // needs_path: some sensor reads the path offset - SensorDesc s[MAX_SENSORS]; -}; - -// Cars whose controls update this tick (Schedule::update_controls). -struct ReadMask { - uint32_t batch_size, batch_index, batches_per_tick, require_active; -}; - -__device__ inline bool reads(const ReadMask& m, const Car& car, uint32_t index) { - uint32_t batch = index / m.batch_size; - return (batch + 8 - m.batch_index) % 8 < m.batches_per_tick && (!m.require_active || (car.flags & CAR_ACTIVE)); -} - -__device__ inline Vec2 actual_velocity(const Car& c) { return (c.flags & CAR_PENDING_VELOCITY) ? Vec2{0.0f, 0.0f} : c.velocity; } -__device__ inline float actual_angular(const Car& c) { return (c.flags & CAR_PENDING_VELOCITY) ? 0.0f : c.angular_velocity; } -__device__ inline Vec2 actual_acceleration(const Car& c) { return (c.flags & CAR_PENDING_VELOCITY) ? c.reset_acceleration : c.acceleration; } -__device__ inline Vec2 cached_right(const Car& c) { return (c.flags & CAR_RESET_RIGHT) ? Vec2{0.0f, 0.0f} : normalized(c.basis_x); } -// `Car::cached_front`: cached_right rotated by -PI/2, whose managed sine and -// cosine are constant (godot_math::QUARTER_TURN_BACK). -__device__ inline Vec2 cached_front(const Car& c) { - Vec2 r = cached_right(c); - float s = ffrom(0xbf7fffffu), co = ffrom(0xb3bbbd2eu); - return Vec2{r.x * co - r.y * s, r.x * s + r.y * co}; -} - -// `Car::sensor_path_offset` (actual position equals position: no transform reset is pending). -__device__ inline float sensor_path_offset(const World& w, const Car& car) { - return curve_closest_offset(w, path_sensor_point(w, car.position)); -} - -// `Car::sensor`. `here` is sensor_path_offset for the path sensors. -__device__ inline double sensor_value(const World& w, const VehicleDesc& v, const Car& car, const SensorDesc& s, - float here, uint32_t& err) { - switch (s.kind) { - case S_RAYCAST: { - Vec2 local = s.offset; // `Car::ray_local`, precomputed on the host (with_ray_offset) - Vec2 end = transform_point(car.position, car.basis_x, car.basis_y, local); - float2 hit; - if (!raycast(w, make_float2(car.position.x, car.position.y), make_float2(end.x, end.y), hit)) return 0.0; - if (hit.x == end.x && hit.y == end.y) return 0.0; - return (double)(1.0f - length(Vec2{hit.x, hit.y} - car.position) / s.b); - } - case S_DISTANCE_FROM_WALL: { - float2 wall = closest_wall_point(w, make_float2(car.position.x, car.position.y)); - return (double)rclamp(length(car.position - Vec2{wall.x, wall.y}) / s.a, 0.0f, 1.0f); - } - case S_SPEED: return (double)rclamp(length(car.velocity) / v.max_velocity_f, 0.0f, 1.0f); - case S_VELOCITY_FRONT: - return signed_sensor(dot(actual_velocity(car), cached_front(car)), v.max_velocity_f); - case S_VELOCITY_SIDE: - return signed_sensor(dot(actual_velocity(car), cached_right(car)), v.max_velocity_f / 2.0f); - case S_ACCELERATION_FRONT: - return signed_sensor(dot(actual_acceleration(car), cached_front(car)), s.a); - case S_ACCELERATION_SIDE: - return signed_sensor(dot(actual_acceleration(car), cached_right(car)), s.a); - case S_ANGULAR_VELOCITY: return signed_sensor(actual_angular(car), 2.0f); - case S_WHEEL_ANGLE: { - uint32_t i = 0; - while (i + 1 < v.wheel_count && !v.wheels[i].steering) i++; - return signed_sensor(car.wheel_angle[i], 45.0f); - } - case S_BOOST_CAPACITY: return py_clamp((double)car.boost_energy, 0.0, 1.0); - case S_GRIP: { - Vec2 p = car.position + rotated(s.offset, car.rotation, err); - return (double)w.surfaces[vehicle_surface(w, p)].grip; - } - case S_CORRECT_DIRECTION: { - if (w.path_points < 2) return 0.0; - Vec2 tangent = normalized(curve_direction(w, here, err)); - Vec2 forward = normalized(cached_front(car)); - return (double)rclamp(dot(forward, tangent), -1.0f, 1.0f); - } - case S_TRACK_CURVATURE: { - if (w.path_points < 2) return 0.0; - Vec2 tangent = normalized(curve_direction(w, here, err)); - if (w.curve_length <= 0.0f) return 0.0; - float speed = rclamp(length(actual_velocity(car)) / v.max_velocity_f, 0.0f, 1.0f); - float lo = rmax(s.a, 0.0f), hi = rmax(s.b, lo); - float offset = fmodf(here + (lo + (hi - lo) * speed), w.curve_length); - Vec2 ahead = normalized(curve_direction(w, offset, err)); - return (double)rclamp(fabsf(profile_atan2(w.math_profile, cross(tangent, ahead), dot(tangent, ahead))) / PI_F, 0.0f, 1.0f); - } - } - return 0.0; -} - -} // namespace altd diff --git a/github-simulator/source/gpu/sim/state.h b/github-simulator/source/gpu/sim/state.h deleted file mode 100644 index f4b06baa3b91cc62dd0e4e2b0016b890238143af..0000000000000000000000000000000000000000 --- a/github-simulator/source/gpu/sim/state.h +++ /dev/null @@ -1,144 +0,0 @@ -// Host/device layouts of the simulation state, mirrored field by field by -// #[repr(C)] structs in src/gpu/simulation.rs. Every car value the CPU keeps as f64 is -// float32-representable (the exporter checks), so cars store f32. -#pragma once -#include - -namespace altd { - -constexpr int MAX_WHEELS = 4; -constexpr int MAX_PAIRS = 16; // broad-phase pairs per car -constexpr int MAX_CONTACTS = 32; // retained pair contacts per car (two per pair) -constexpr int MAX_SHAPE_POINTS = 16; // points per physics shape -constexpr int MAX_SENSORS = 32; -constexpr int MAX_OUTPUTS = 8; -constexpr int MAX_LAYERS = 12; -constexpr int MAX_WIDTH = 32; -constexpr int RECENT = 10; // TrainingStats.recent_score_diffs capacity - -// Per-car error bits; the math bits of math.h (1, 2, 4) are included. -enum CarError : uint32_t { - ERR_PAIRS = 8, // more than MAX_PAIRS broad-phase pairs - ERR_CONTACTS = 16, // more than MAX_CONTACTS retained contacts - ERR_PATH_NAN = 32, // NaN path offset (the CPU panics) -}; - -struct Vec2 { float x, y; }; - -enum ContactFlags : uint32_t { CONTACT_WALL_FIRST = 1, CONTACT_USED = 2 }; - -// collision::Contact -struct Contact { - Vec2 normal, point, wall_point, local_point, wall_local_point; - float depth; - uint32_t shape, flags; - float normal_mass, tangent_mass, bias, bounce, friction; - float acc_normal, acc_tangent, acc_bias, acc_bias_center; -}; - -enum CarFlags : uint32_t { - CAR_ACTIVE = 1, - CAR_RESET_RIGHT = 2, - CAR_PENDING_VELOCITY = 4, // pending_velocity_reset = Some((0, 0), 0) - CAR_HAS_SHAPE = 8, // scratch.shape_aabb is Some - CAR_HAS_LEAF = 16, // scratch.leaf_aabb is Some - CAR_PAIR_CHECK = 32, // scratch.pair_check_pending -}; - -// car::Car with its CollisionScratch. `contacts` are the retained -// previous_contacts; `axes[i]` is separating_axes[pairs[i]]. -struct Car { - Vec2 position, velocity, acceleration, reset_acceleration, basis_x, basis_y; - float rotation, transform_angle, angular_velocity, boost_energy; - uint32_t flags, collision_count; - uint64_t tick; - float wheel_angle[MAX_WHEELS]; - Vec2 wheel_previous[MAX_WHEELS]; - uint32_t wheel_surface[MAX_WHEELS]; // index into TrackDesc::surfaces - uint32_t wheel_has_previous; // bit per wheel - uint32_t pair_count, contact_count, error; - Vec2 shape_position, shape_size; // scratch.shape_aabb (FloatRect) - Vec2 leaf_min, leaf_max; // scratch.leaf_aabb - uint32_t pairs[MAX_PAIRS]; - Vec2 axes[MAX_PAIRS]; - Contact contacts[MAX_CONTACTS]; -}; - -enum AgentFlags : uint32_t { - AGENT_HAS_PREVIOUS_OFFSET = 1, - AGENT_WENT_BACKWARDS = 2, - AGENT_HAS_LAP_CROSSING = 4, - AGENT_PENDING_CONTACT = 8, - AGENT_HAS_BEST_LAP = 16, - AGENT_HAS_ALL_LAPS = 32, - AGENT_HAS_DEACTIVATED = 64, -}; - -// training::TrainingAgent without its network and car: controls, -// TrainingStats with its ScoreTracker, and the window bookkeeping. -struct Agent { - double controls[5]; // acceleration, steering, brake, handbrake, boost - double previous_offset, tracker_score, lap_crossing_fraction; - uint64_t lap_count; - double network_novelty; - uint64_t update_count, collision_count; - double total_score, total_speed, total_abs_steering, total_actual_distance, total_drifting_amount, - total_momentum_change_frequency, total_throttle_change_frequency, total_braking_frequency, - total_distance_from_wall, total_distance_from_center; - double best_lap_time; - uint64_t idle_ticks; - double previous_speed, previous_throttle, previous_score; - uint64_t previous_lap_count, lap_start_tick, drift_ticks; - double current_slip_angle_degrees, continuous_drift_time, max_continuous_drift_time, all_laps_time_sum; - double recent[RECENT]; // ring buffer, oldest at recent_start - uint32_t recent_start, recent_len; - uint64_t deactivated_at; - uint32_t flags, pad; -}; - -// world::WheelConfig with the conversions step_internal applies. -struct WheelDesc { - Vec2 position; // F2::from(local position) - uint32_t steering; - uint32_t handbrake_off; // handbrake_power.abs() < 0.00001 - float power, brake_power, handbrake_power, brake_max; // as f32; brake_max = max(brake, handbrake) as f32 - double max_angle_deg; -}; - -// world::VehicleConfig and the car's wheel initialization. -struct VehicleDesc { - WheelDesc wheels[MAX_WHEELS]; - uint32_t wheel_count, custom_integrator, center_steering, solver_iterations; - uint32_t max_contacts_reported, pad0; - double steering_speed, max_velocity, dt, contact_max_separation; - float grip, air_resistance, mass, inertia; - float linear_damp, angular_damp, dt_f, max_velocity_f; - Vec2 gravity, shape_basis_x, shape_basis_y, shape_size, shape_half, shape_position, center_of_mass; - float friction, bounce, contact_bias, allowed_penetration; - float recycle_radius, max_separation_f, pad1, pad2; -}; - -enum SensorKind : uint32_t { - S_RAYCAST = 0, // a = degrees, b = length - S_DISTANCE_FROM_WALL, // a = max distance - S_SPEED, - S_VELOCITY_FRONT, - S_VELOCITY_SIDE, - S_ACCELERATION_FRONT, // a = max acceleration - S_ACCELERATION_SIDE, // a = max acceleration - S_ANGULAR_VELOCITY, - S_WHEEL_ANGLE, - S_BOOST_CAPACITY, - S_GRIP, // offset - S_CORRECT_DIRECTION, - S_TRACK_CURVATURE, // a = min lookahead, b = max lookahead -}; - -struct SensorDesc { - uint32_t kind; - float a, b; - Vec2 offset; - uint32_t pad; -}; - -} // namespace altd diff --git a/github-simulator/source/gpu/sim/stats.h b/github-simulator/source/gpu/sim/stats.h deleted file mode 100644 index 5577a1d06a0b20bd8cf046274fb6580523212421..0000000000000000000000000000000000000000 --- a/github-simulator/source/gpu/sim/stats.h +++ /dev/null @@ -1,144 +0,0 @@ -// training::ScoreTracker and TrainingStats::update (src/training/stats.rs) on the -// GPU agent state, and Schedule::update_stats. -#pragma once -#include "rays.h" -#include "track.h" - -namespace altd { - -__device__ inline double path_length(const World& w) { return w.path_points ? w.path_offsets[w.path_points - 1] : 0.0; } - -// `Track::path_position` (path_points >= 2). -__device__ inline Vec2 path_position(const World& w, double offset, uint32_t& err) { - if (w.curve_points) return curve_position(w, (float)offset, err); - offset = py_clamp(offset, 0.0, path_length(w)); - uint32_t lo = 0, hi = w.path_points; // partition_point(x <= offset) - while (lo < hi) { - uint32_t mid = (lo + hi) / 2; - if (w.path_offsets[mid] <= offset) lo = mid + 1; else hi = mid; - } - uint32_t index = min(w.path_points - 2, lo ? lo - 1 : 0); - double start = w.path_offsets[index], end = w.path_offsets[index + 1]; - double fraction = end > start ? (offset - start) / (end - start) : 0.0; - const double* a = w.path_xy + 2 * index; - return Vec2{(float)(a[0] + (a[2] - a[0]) * fraction), (float)(a[1] + (a[3] - a[1]) * fraction)}; -} - -// `ScoreTracker::update` -__device__ inline void score_update(const World& w, Agent& a, Vec2 position, uint32_t& err) { - a.flags &= ~AGENT_HAS_LAP_CROSSING; - a.lap_crossing_fraction = 0.0; // None exports as 0.0 - double length = path_length(w); - if (length <= 0.0) return; - if (!(a.flags & AGENT_HAS_PREVIOUS_OFFSET)) { - double offset = closest_path(w, position, false, 0.0, err).offset; - a.tracker_score += (offset > length / 2.0 ? offset - length : offset) * (5.0 / 384.0); - a.flags = offset > length / 2.0 ? a.flags | AGENT_WENT_BACKWARDS : a.flags & ~AGENT_WENT_BACKWARDS; - a.previous_offset = offset; - a.flags |= AGENT_HAS_PREVIOUS_OFFSET; - return; - } - double previous = a.previous_offset; - double offset = closest_path(w, position, true, previous, err).offset; - double change = offset - previous; - if (change > length / 2.0) change -= length; - else if (change < -length / 2.0) change += length; - a.tracker_score += change * (5.0 / 384.0); - double crossing = previous + change; - if (crossing < 0.0) { - a.flags |= AGENT_WENT_BACKWARDS; - } else if (crossing >= length) { - if (!(a.flags & AGENT_WENT_BACKWARDS)) { - a.lap_count++; - a.lap_crossing_fraction = (length - previous) / (length - previous + offset); - a.flags |= AGENT_HAS_LAP_CROSSING; - } - a.flags &= ~AGENT_WENT_BACKWARDS; - } - a.previous_offset = offset; -} - -// `TrainingStats::update` -__device__ inline void stats_update(const World& w, const Car& car, Agent& a, uint64_t tick, uint32_t& err) { - if (a.flags & AGENT_PENDING_CONTACT) a.collision_count++; - if (!(car.flags & CAR_ACTIVE)) return; - a.update_count++; - double speed = (double)length(car.velocity); - bool initializing = !(a.flags & AGENT_HAS_PREVIOUS_OFFSET); - score_update(w, a, car.position, err); - double score = a.tracker_score; - double diff = initializing ? 0.0 : score - a.previous_score; - if (a.recent_len == RECENT) { - a.recent[a.recent_start] = diff; - a.recent_start = (a.recent_start + 1) % RECENT; - } else { - a.recent[(a.recent_start + a.recent_len) % RECENT] = diff; - a.recent_len++; - } - double sum = 0.0; - for (uint32_t k = 0; k < a.recent_len; k++) sum += a.recent[(a.recent_start + k) % RECENT]; - a.idle_ticks = sum / (double)a.recent_len < 0.1 ? a.idle_ticks + 1 : 0; - a.total_score = score; - a.previous_score = score; - a.total_speed += speed; - a.total_abs_steering += fabs(py_clamp(a.controls[1], -1.0, 1.0)); - a.total_actual_distance += speed * 0.1; - a.total_momentum_change_frequency += fabs(speed - a.previous_speed); - double throttle = py_clamp(a.controls[0], -1.0, 1.0); - a.total_throttle_change_frequency += fabs(throttle - a.previous_throttle); - if (a.controls[2] > 0.0001 || a.controls[3] > 0.0001) a.total_braking_frequency += 0.1; - a.previous_speed = speed; - a.previous_throttle = throttle; - float2 wall = closest_wall_point(w, make_float2(car.position.x, car.position.y)); - a.total_distance_from_wall += (double)length(Vec2{wall.x, wall.y} - car.position); - if (w.path_points >= 2) { - Vec2 center = path_position(w, (a.flags & AGENT_HAS_PREVIOUS_OFFSET) ? a.previous_offset : 0.0, err); - a.total_distance_from_center += (double)length(center - car.position); - } - Vec2 right = normalized(car.basis_x); - float front = dot(car.velocity, rotated(right, -1.57079632679489661923f, err)); - float side = dot(car.velocity, right); - a.current_slip_angle_degrees = (double)(profile_atan2(w.math_profile, side, front) * 57.29578f); - double slip = fabs(a.current_slip_angle_degrees); - a.drift_ticks = (speed > 50.0 && slip >= 15.0 && slip <= 60.0) ? a.drift_ticks + 1 : 0; - if (a.drift_ticks >= 3) { - a.total_drifting_amount += 0.1; - a.continuous_drift_time += 0.1; - a.max_continuous_drift_time = fmax(a.max_continuous_drift_time, a.continuous_drift_time); - } - if (a.drift_ticks == 0) a.continuous_drift_time = 0.0; - if (a.lap_count > a.previous_lap_count) { - double fraction = (a.flags & AGENT_HAS_LAP_CROSSING) ? a.lap_crossing_fraction : 1.0; - fraction = fraction < 0.0 ? 0.0 : (fraction > 1.0 ? 1.0 : fraction); // f64::clamp - double elapsed = (double)(tick / 6) * 0.1 - (double)(a.lap_start_tick / 6) * 0.1; - double lap_time = rint((elapsed - 0.1 + fraction * 0.1) * 1000.0) / 1000.0; - double current = (a.flags & AGENT_HAS_BEST_LAP) ? a.best_lap_time : lap_time; - a.best_lap_time = py_min(current, lap_time); - a.all_laps_time_sum = ((a.flags & AGENT_HAS_ALL_LAPS) ? a.all_laps_time_sum : 0.0) + lap_time; - a.flags |= AGENT_HAS_BEST_LAP | AGENT_HAS_ALL_LAPS; - a.lap_start_tick = tick; - a.previous_lap_count = a.lap_count; - } -} - -// `Car::deactivate`: velocity reset to zero at the next callback. -__device__ inline void car_deactivate(Car& car) { - car.flags = (car.flags & ~CAR_ACTIVE) | CAR_PENDING_VELOCITY; - car.reset_acceleration = car.acceleration; -} - -// `Schedule::update_stats` on a statistics tick (tick % 6 == stats_phase); -// `stats_tick` is tick - first_stats_tick. -__device__ inline void update_stats(const World& w, Car& car, Agent& a, uint64_t tick, uint64_t stats_tick, - bool eliminate_when_idle, uint32_t& err) { - bool was_active = car.flags & CAR_ACTIVE; - stats_update(w, car, a, stats_tick, err); - a.flags &= ~AGENT_PENDING_CONTACT; - if ((car.flags & CAR_ACTIVE) && eliminate_when_idle && a.idle_ticks > 80) car_deactivate(car); - if (was_active && !(car.flags & CAR_ACTIVE)) { - a.deactivated_at = tick; - a.flags |= AGENT_HAS_DEACTIVATED; - } -} - -} // namespace altd diff --git a/github-simulator/source/gpu/sim/step.h b/github-simulator/source/gpu/sim/step.h deleted file mode 100644 index 93868e0685739e0c4b0ebc2923d44a958e54d2a9..0000000000000000000000000000000000000000 --- a/github-simulator/source/gpu/sim/step.h +++ /dev/null @@ -1,743 +0,0 @@ -// `Car::step` (src/physics/car.rs) with its wall collision (src/physics/collision.rs) for -// tracks with physics shapes and no native shared broadphase: one thread per -// car. Every f64 expression of the CPU code whose operands are float32 values -// and whose result is rounded to float32 at once (f32r, dot_f32, ...) is a -// plain float operation here: +, -, *, / and sqrt rounded through double -// equal the float operation. -#pragma once -#include "track.h" - -namespace altd { - -#ifdef ALTD_STEP_TIMING -// Diagnostic build: summed per-thread cycles of the car_step sections. -__device__ unsigned long long g_step_cycles[16]; -#define STEP_MARK(var) uint64_t var = wall_clock64() -#define STEP_SECTION(k, var) do { uint64_t _now = wall_clock64(); atomicAdd(&g_step_cycles[k], _now - var); var = _now; } while (0) -#define STEP_COUNT(k) atomicAdd(&g_step_cycles[k], 1ull) -#define STEP_ADD(k, x) atomicAdd(&g_step_cycles[k], (unsigned long long)(x)) -#else -#define STEP_MARK(var) -#define STEP_SECTION(k, var) -#define STEP_COUNT(k) -#define STEP_ADD(k, x) -#endif - -struct Controls { double acceleration, steering, brake, handbrake, boost; }; - -// `Controls::normalized` -__device__ inline Controls normalized(const double* c) { - return Controls{py_clamp(c[0], -1.0, 1.0), py_clamp(c[1], -1.0, 1.0), py_clamp(c[2], -1.0, 1.0), - py_clamp(c[3], 0.0, 1.0), py_clamp(c[4], 0.0, 1.0)}; -} - -// Vector2 == Vector2 (so -0 equals 0). -__device__ inline bool is_zero(Vec2 a) { return a.x == 0.0f && a.y == 0.0f; } - -// ---- vehicle ---- - -// `godot_ease` -__device__ inline double godot_ease(uint32_t math, double value, double curve) { - return 1.0 - profile_pow(math, 1.0 - py_clamp(value, 0.0, 1.0), 1.0 / curve); -} - -// `godot_math::combine` -__device__ inline Vec2 combine(Vec2 brake, Vec2 drive, float brake_max, float drive_max) { - float n = length(brake); - if (n < 0.0001f || brake_max <= 0.0f) return brake + drive; - float allowed = n + drive_max * (1.0f - rmin(n / brake_max, 1.0f)); - Vec2 result = brake + drive; - Vec2 axis = normalized(brake); - float projection = dot(result, axis); - return projection > allowed ? result - axis * (projection - allowed) : result; -} - -// `Car::set_transform_angle` -__device__ inline void set_transform_angle(uint32_t math, Car& car, float angle, uint32_t& err) { - float s, c; - engine_sin_cos(angle, s, c, err); - car.transform_angle = angle; - car.basis_x = Vec2{c, s}; - car.basis_y = Vec2{-s, c}; - car.rotation = profile_atan2(math, s, c); -} - -// ---- CollisionScratch::update_shape ---- - -__device__ inline void update_shape(const VehicleDesc& v, Car& car) { - Vec2 bx = car.basis_x, by = car.basis_y; - Vec2 x = bx * v.shape_basis_x.x + by * v.shape_basis_x.y; - Vec2 y = bx * v.shape_basis_y.x + by * v.shape_basis_y.y; - Vec2 size = v.shape_size; - Vec2 shape_origin = bx * v.shape_position.x + by * v.shape_position.y + car.position; - Vec2 pos = x * (-size.x * 0.5f) + y * (-size.y * 0.5f) + shape_origin; - Vec2 dx = x * size.x, dy = y * size.y; - // FloatRect::expand from an empty rect at pos, keeping (position, size). - Vec2 rp = pos, rs = Vec2{0.0f, 0.0f}; - auto expand_rect = [&](Vec2 p) { - Vec2 e = rp + rs; - Vec2 b = Vec2{rmin(rp.x, p.x), rmin(rp.y, p.y)}; - Vec2 f = Vec2{rmax(e.x, p.x), rmax(e.y, p.y)}; - rp = b; - rs = f - b; - }; - expand_rect(pos + dx); - expand_rect(pos + dy); - expand_rect((pos + dx) + dy); - float margin = (car.flags & CAR_HAS_SHAPE) - ? (float)((double)(car.shape_size.x + car.shape_size.y) * 0.5 * 0.05) : 0.0f; - rp = rp - Vec2{margin, margin}; - rs = rs + Vec2{margin * 2.0f, margin * 2.0f}; - car.shape_position = rp; - car.shape_size = rs; - car.flags |= CAR_HAS_SHAPE; - Vec2 raw_min = rp, raw_max = rp + rs; - if (!(car.flags & CAR_HAS_LEAF)) { - // item_add inserts the unexpanded box. - car.leaf_min = raw_min; - car.leaf_max = raw_max; - car.flags |= CAR_HAS_LEAF | CAR_PAIR_CHECK; - return; - } - float threshold = (float)((double)0.1f * 2.0 * 2.0 * (double)1.1f); - float density = (float)((double)car.pair_count * (1.0 / 9.0)); - float grow = 0.1f * (1.0f - rmin(density, 1.0f)); - Vec2 old_size = car.leaf_max - car.leaf_min; - Vec2 rt_max = car.leaf_min + old_size; // roundtrip_rect - bool encloses = raw_min.x >= car.leaf_min.x && raw_min.y >= car.leaf_min.y && raw_max.x <= rt_max.x && raw_max.y <= rt_max.y; - if (encloses && (old_size.x + old_size.y) - (rs.x + rs.y) < threshold) return; - car.leaf_min = raw_min - Vec2{grow, grow}; - car.leaf_max = raw_max + Vec2{grow, grow}; - car.flags |= CAR_PAIR_CHECK; -} - -// ---- SAT contacts (find_contacts_with_basis) ---- - -struct Support { Vec2 p[2]; uint32_t n; }; - -__device__ inline Vec2 project_on_line(Vec2 point, Vec2 e0, Vec2 e1) { - Vec2 direction = e1 - e0, offset = point - e0; - float ls = dot(direction, direction); - if (ls < 1e-20f) return e0; - float fraction = dot(direction, offset) / ls; - return e0 + direction * fraction; -} - -__device__ inline Support rectangle_supports(const Vec2* car, Vec2 direction, Vec2 bx, Vec2 by) { - Vec2 local = normalized(Vec2{dot(bx, direction), dot(by, direction)}); - if ((double)fabsf(local.x) > 0.99998) - return local.x > 0.0f ? Support{{car[2], car[1]}, 2} : Support{{car[3], car[0]}, 2}; - if ((double)fabsf(local.y) > 0.99998) - return local.y > 0.0f ? Support{{car[2], car[3]}, 2} : Support{{car[1], car[0]}, 2}; - if (local.x < 0.0f) return Support{{local.y < 0.0f ? car[0] : car[3], Vec2{0, 0}}, 1}; - return Support{{local.y < 0.0f ? car[1] : car[2], Vec2{0, 0}}, 1}; -} - -// The loop returns at the first edge facing `direction`, after updating the -// maximum up to it: that maximum is only used without such an edge. -__device__ inline Support polygon_supports(const World& w, const ShapeDev& shape, Vec2 wall_offset, Vec2 direction) { - direction = normalized(direction); - const Vec2* local = w.shape_local + shape.first; - const Vec2* normals = w.shape_normals + shape.first; - float maximum = -INFINITY; - uint32_t best = 0, edge = NONE, n = shape.count; - // Unrolled for constant indices; leaving at n skips the predicated no-op - // iterations (most track shapes have 3 or 4 points). -#pragma unroll - for (uint32_t i = 0; i < MAX_SHAPE_POINTS; i++) { - if (i >= n) break; - float projection = dot(local[i], direction); - bool higher = projection > maximum; - maximum = higher ? projection : maximum; - best = higher ? i : best; - edge = edge == NONE && (double)dot(normals[i], direction) > 0.99998 ? i : edge; - } - if (edge != NONE) return Support{{local[edge] + wall_offset, local[edge + 1 == n ? 0 : edge + 1] + wall_offset}, 2}; - return Support{{local[best] + wall_offset, Vec2{0, 0}}, 1}; -} - -// `contact_pairs`: (car point, wall point) pairs. -__device__ inline uint32_t contact_pairs(const Support& a, const Support& b, Vec2 push, Vec2 (*out)[2]) { - if (a.n == 1 && b.n == 1) { out[0][0] = a.p[0]; out[0][1] = b.p[0]; return 1; } - if (a.n == 1) { out[0][0] = a.p[0]; out[0][1] = project_on_line(a.p[0], b.p[0], b.p[1]); return 1; } - if (b.n == 1) { out[0][0] = project_on_line(b.p[0], a.p[0], a.p[1]); out[0][1] = b.p[0]; return 1; } - Vec2 tangent = Vec2{push.y, -push.x}; - float distance_a = dot(push, a.p[0]), distance_b = dot(push, b.p[0]); - float key[4] = {dot(a.p[0], tangent), dot(a.p[1], tangent), dot(b.p[0], tangent), dot(b.p[1], tangent)}; - uint32_t order[4] = {0, 1, 2, 3}; // 0, 1: car points; 2, 3: wall points - for (int i = 1; i < 4; i++) - for (int j = i; j > 0 && key[order[j]] < key[order[j - 1]]; j--) { - uint32_t t = order[j]; order[j] = order[j - 1]; order[j - 1] = t; - } - uint32_t count = 0; - for (int k = 1; k < 3; k++) { - uint32_t o = order[k]; - bool from_car = o < 2; - Vec2 point = from_car ? a.p[o] : b.p[o - 2]; - float distance = dot(push, point) - (from_car ? distance_b : distance_a); - Vec2 other = point - push * distance; - Vec2 p0 = from_car ? point : other, p1 = from_car ? other : point; - if ((double)dot(push, p0) <= (double)dot(push, p1) - 1e-5) { - out[count][0] = p0; - out[count][1] = p1; - count++; - } - } - return count; -} - -__device__ inline bool shape_overlaps(const ShapeDev& s, double left, double right, double top, double bottom) { - return !(s.max_x < left || s.min_x > right || s.max_y < top || s.min_y > bottom); -} - -// Broad pairs update (pair_check_pending, no shared broadphase): retain the -// overlapping pairs with their separating axes, then append overlapping -// shapes in ascending index order. -__device__ inline void update_pairs(const World& w, Car& car, double left, double right, double top, double bottom) { - uint32_t kept = 0; - for (uint32_t i = 0; i < car.pair_count; i++) { - uint32_t index = car.pairs[i]; - if (shape_overlaps(w.shapes[index], left, right, top, bottom)) { - car.pairs[kept] = index; - car.axes[kept] = car.axes[i]; - kept++; - } - } - uint32_t retained = kept; - auto visit = [&](uint32_t index) { - if (!shape_overlaps(w.shapes[index], left, right, top, bottom)) return; - for (uint32_t j = 0; j < retained; j++) - if (car.pairs[j] == index) return; - if (kept == MAX_PAIRS) { car.error |= ERR_PAIRS; return; } - car.pairs[kept] = index; - car.axes[kept] = Vec2{0.0f, 0.0f}; - kept++; - }; - // Cell lists hold every shape whose box, grown by one unit, meets the - // cell grown by shape_margin: complete for queries inside that region. - const NearGrid& g = w.shape_grid; - double fx = floor((left - g.x0) / g.cell), fy = floor((top - g.y0) / g.cell); - bool inside = fx >= 0.0 && fy >= 0.0 && fx < (double)g.nx && fy < (double)g.ny - && right < g.x0 + (fx + 1.0) * g.cell + w.shape_margin && bottom < g.y0 + (fy + 1.0) * g.cell + w.shape_margin; - if (inside) { - uint32_t cell = (uint32_t)fy * g.nx + (uint32_t)fx; - for (uint32_t k = g.start[cell], e = g.start[cell + 1]; k < e; k++) visit(g.items[k]); - } else { - STEP_COUNT(6); - for (uint32_t i = 0; i < w.shape_count; i++) visit(i); - } - STEP_COUNT(7); - car.pair_count = kept; - car.flags &= ~CAR_PAIR_CHECK; -} - -// The car side of `find_contacts_with_basis`: the collision shape's basis, -// its corners relative to the car origin and the car's SAT axes. -struct CarFrame { Vec2 bx, by, basis_x, basis_y, local[4], axis_x, axis_y; }; - -__device__ inline CarFrame car_frame(const VehicleDesc& v, const Car& car) { - CarFrame f; - f.bx = car.basis_x; - f.by = car.basis_y; - f.basis_x = f.bx * v.shape_basis_x.x + f.by * v.shape_basis_x.y; - f.basis_y = f.bx * v.shape_basis_y.x + f.by * v.shape_basis_y.y; - const float sxs[4] = {-1.0f, 1.0f, 1.0f, -1.0f}, sys[4] = {-1.0f, -1.0f, 1.0f, 1.0f}; -#pragma unroll - for (int i = 0; i < 4; i++) { - float x = sxs[i] * v.shape_half.x, y = sys[i] * v.shape_half.y; - f.local[i] = Vec2{f.basis_x.x * x + f.basis_y.x * y, f.basis_x.y * x + f.basis_y.y * y}; - } - f.axis_x = normalized(f.basis_x); - f.axis_y = normalized(f.basis_y); - return f; -} - -// One broad pair, relative to the pair origin: the wall's origin if the wall -// comes first, else the car's. -struct PairFrame { Vec2 origin, car_offset, wall_offset, car[4]; bool wall_first; }; - -__device__ inline PairFrame pair_frame(const VehicleDesc& v, const Car& car, const CarFrame& f, const ShapeDev& shape) { - PairFrame p; - p.wall_first = shape.wall_first != 0; - p.origin = p.wall_first ? shape.origin : car.position; - p.car_offset = car.position - p.origin; - p.wall_offset = shape.origin - p.origin; - Vec2 shape_origin = f.bx * v.shape_position.x + f.by * v.shape_position.y + p.car_offset; -#pragma unroll - for (int i = 0; i < 4; i++) p.car[i] = f.local[i] + shape_origin; - return p; -} - -// The SAT test of broad pair ci of `find_contacts_with_basis`. It reads and -// writes only the pair's cached axis, so the pairs are independent. Returns -// the push axis of the least penetration, or zero if the shapes are separated -// (caching the separating axis) or no axis qualifies. -__device__ inline Vec2 sat_pair(const World& w, const VehicleDesc& v, Car& car, uint32_t ci) { - CarFrame f = car_frame(v, car); - const ShapeDev& shape = w.shapes[car.pairs[ci]]; - PairFrame p = pair_frame(v, car, f, shape); - uint32_t n = shape.count; - const Vec2* local = w.shape_local + shape.first; - Vec2 wall[MAX_SHAPE_POINTS]; // registers: constant indices only -#pragma unroll - for (uint32_t i = 0; i < MAX_SHAPE_POINTS; i++) wall[i] = i < n ? local[i] + p.wall_offset : Vec2{0.0f, 0.0f}; - // Axes: the cached separating axis (if any), the car axes, the wall edge normals. - Vec2& separating = car.axes[ci]; - Vec2 cached = separating; - uint32_t axis_count = 3 + n; - float best_depth = 1e15f; - Vec2 push = Vec2{0.0f, 0.0f}; - STEP_COUNT(12); - STEP_ADD(15, n); - for (uint32_t k = is_zero(cached) ? 1 : 0; k < axis_count; k++) { - STEP_COUNT(13); - Vec2 axis; - if (k >= 3) { - uint32_t e = k - 3; - Vec2 en = normalized((local[e + 1 == n ? 0 : e + 1] + p.wall_offset) - (local[e] + p.wall_offset)); - axis = Vec2{en.y, -en.x}; - } else { - axis = k == 0 ? cached : k == 1 ? f.axis_x : f.axis_y; - } - if ((double)fabsf(axis.x) < 1e-5 && (double)fabsf(axis.y) < 1e-5) axis = Vec2{0.0f, 1.0f}; - float min_a = INFINITY, max_a = -INFINITY, min_b = INFINITY, max_b = -INFINITY; -#pragma unroll - for (int i = 0; i < 4; i++) { - float d = dot(p.car[i], axis); - min_a = d < min_a ? d : min_a; - max_a = d > max_a ? d : max_a; - } -#pragma unroll - for (uint32_t i = 0; i < MAX_SHAPE_POINTS; i++) { - if (i >= n) break; // constant indices keep `wall` in registers; iterations past n did nothing - float d = dot(wall[i], axis); - min_b = d < min_b ? d : min_b; - max_b = d > max_b ? d : max_b; - } - float width_a = (max_a - min_a) * 0.5f, center_a = (min_a + max_a) * 0.5f; - float dmin = (min_b - width_a) - center_a, dmax = (max_b + width_a) - center_a; - if (dmin > 0.0f || dmax < 0.0f) { - STEP_COUNT(14); - separating = axis; - return Vec2{0.0f, 0.0f}; - } - bool use_max = dmax < fabsf(dmin); - float depth = use_max ? dmax : fabsf(dmin); - if (depth < best_depth) { - best_depth = depth; - push = use_max ? axis : -axis; - } - } - if (!is_zero(push)) separating = Vec2{0.0f, 0.0f}; - return push; -} - -// The contacts of broad pair ci for its (nonzero) push axis, `add`ed in -// order; returns how many. -template -__device__ inline uint32_t pair_contacts(const World& w, const VehicleDesc& v, const Car& car, const CarFrame& f, uint32_t ci, - Vec2 push, Add&& add) { - uint32_t shape_index = car.pairs[ci]; - const ShapeDev& shape = w.shapes[shape_index]; - PairFrame p = pair_frame(v, car, f, shape); - Support support_car = rectangle_supports(p.car, -push, f.basis_x, f.basis_y); - Support support_wall = polygon_supports(w, shape, p.wall_offset, push); - Vec2 pairs[2][2]; - uint32_t made = contact_pairs(support_car, support_wall, push, pairs), added = 0; - for (uint32_t k = 0; k < made; k++) { - Vec2 car_point = pairs[k][0], wall_point = pairs[k][1]; - Vec2 delta = p.wall_first ? wall_point - car_point : car_point - wall_point; - float depth = sqrtf(dot(delta, delta)); - if (!(depth > 0.0f)) continue; - Vec2 normal = Vec2{delta.x / depth, delta.y / depth}; - if (!p.wall_first) normal = -normal; - Vec2 relative = car_point - p.car_offset; - Vec2 local = Vec2{dot(f.bx, relative), dot(f.by, relative)}; - Vec2 global_a = (f.bx * local.x + f.by * local.y) + p.car_offset; - Vec2 wall_local = wall_point - p.wall_offset; - Vec2 global_b = wall_local + p.wall_offset; - Contact c{}; - c.normal = normal; - c.point = global_a + p.origin; - c.wall_point = global_b + p.origin; - c.local_point = local; - c.wall_local_point = wall_local; - c.depth = dot(global_b - global_a, normal); - c.shape = shape_index; - c.flags = (p.wall_first ? CONTACT_WALL_FIRST : 0u) | CONTACT_USED; - add(c); - added++; - } - return added; -} - -// ---- solve_wall_contacts ---- - -struct SolveResult { Vec2 velocity, bias_velocity; float angular, bias_angular; bool collided; }; - -// `refresh_contact`; returns the separation vector of the validation. -__device__ inline Vec2 refresh_contact(const World& w, Contact& c, Vec2 position, Vec2 bx, Vec2 by) { - const ShapeDev& shape = w.shapes[c.shape]; - Vec2 origin = (c.flags & CONTACT_WALL_FIRST) ? shape.origin : position; - Vec2 car_offset = position - origin, wall_offset = shape.origin - origin; - Vec2 car = (bx * c.local_point.x + by * c.local_point.y) + car_offset; - Vec2 wall = c.wall_local_point + wall_offset; - c.depth = dot(wall - car, c.normal); - c.point = car + origin; - c.wall_point = wall + origin; - return (wall - c.normal * c.depth) - car; -} - -__device__ inline void remove_contact(Car& car, uint32_t index) { - for (uint32_t j = index; j + 1 < car.contact_count; j++) car.contacts[j] = car.contacts[j + 1]; - car.contact_count--; -} - -// Lever arm of `contact_arm`. -__device__ inline Vec2 contact_arm(const World& w, const Contact& c, Vec2 position, Vec2 bx, Vec2 by, Vec2 center) { - Vec2 local = bx * c.local_point.x + by * c.local_point.y; - Vec2 offset = (c.flags & CONTACT_WALL_FIRST) ? position - w.shapes[c.shape].origin : Vec2{0.0f, 0.0f}; - return ((local + offset) - center) - offset; -} - -// The start of `solve_wall_contacts`: validates the retained contacts, then -// updates the broad pairs (`find_contacts_with_basis` before its SAT tests) -// and keeps the contacts of the paired shapes. -__device__ inline void prepare_contacts(const World& w, const VehicleDesc& v, Car& car) { - STEP_MARK(ts); - Vec2 position = car.position, bx = car.basis_x, by = car.basis_y; - float max_sep2 = v.max_separation_f * v.max_separation_f; - for (uint32_t i = 0; i < car.contact_count;) { - Contact& c = car.contacts[i]; - Vec2 separation = refresh_contact(w, c, position, bx, by); - if (!(c.flags & CONTACT_USED) || (double)c.depth < -v.contact_max_separation || dot(separation, separation) > max_sep2) { - uint32_t last = i; - for (uint32_t j = car.contact_count; j-- > i;) - if (car.contacts[j].shape == c.shape) { last = j; break; } - car.contacts[i] = car.contacts[last]; - remove_contact(car, last); - } else { - c.flags &= ~CONTACT_USED; - i++; - } - } - STEP_SECTION(1, ts); - if (!(car.flags & CAR_HAS_SHAPE)) update_shape(v, car); - // leaf.roundtrip_rect() - Vec2 qmin = car.leaf_min, qmax = car.leaf_min + (car.leaf_max - car.leaf_min); - if (car.flags & CAR_PAIR_CHECK) update_pairs(w, car, (double)qmin.x, (double)qmax.x, (double)qmin.y, (double)qmax.y); - STEP_SECTION(10, ts); - // previous.retain(in broad_pairs) - uint32_t kept = 0; - for (uint32_t i = 0; i < car.contact_count; i++) { - bool paired = false; - for (uint32_t j = 0; j < car.pair_count; j++) paired |= car.pairs[j] == car.contacts[i].shape; - if (paired) car.contacts[kept++] = car.contacts[i]; - } - car.contact_count = kept; -} - -// `merge` of one fresh contact: finding reads no contacts, so merging each -// as it is found matches merging the collected list in order. -__device__ inline void merge_contact(const VehicleDesc& v, Car& car, const Contact& nw) { - float radius2 = v.recycle_radius * v.recycle_radius; - int32_t found = -1; - uint32_t same = 0; - for (uint32_t i = 0; i < car.contact_count; i++) { - const Contact& old = car.contacts[i]; - if (old.shape != nw.shape) continue; - same++; - Vec2 a = old.local_point - nw.local_point, b = old.wall_local_point - nw.wall_local_point; - if (found < 0 && dot(a, a) < radius2 && dot(b, b) < radius2) found = (int32_t)i; - } - if (found >= 0) { - Contact& old = car.contacts[found]; - Contact r = nw; - r.acc_normal = old.acc_normal; - r.acc_tangent = old.acc_tangent; - r.acc_bias = old.acc_bias; - r.acc_bias_center = old.acc_bias_center; - old = r; - } else if (same < 2) { - if (car.contact_count == MAX_CONTACTS) car.error |= ERR_CONTACTS; - else car.contacts[car.contact_count++] = nw; - } else { - int32_t least = -1; - float depth = nw.depth; - for (uint32_t i = 0; i < car.contact_count; i++) - if (car.contacts[i].shape == nw.shape && car.contacts[i].depth < depth) { depth = car.contacts[i].depth; least = (int32_t)i; } - if (least >= 0) car.contacts[least] = nw; - } -} - -// The rest of `solve_wall_contacts` once this tick's contacts are merged; -// `made_contacts` has a bit per broad pair that made contacts. -__device__ inline SolveResult solve_contacts(const World& w, const VehicleDesc& v, Car& car, uint32_t made_contacts, Vec2 velocity, - float angular, Vec2 initial_velocity, float initial_angular) { - STEP_MARK(ts); - Vec2 position = car.position, bx = car.basis_x, by = car.basis_y; - // Active contacts in broad-pair order: positive depth, shape collided this tick. - uint8_t active[MAX_CONTACTS]; - uint32_t active_count = 0; - for (uint32_t p = 0; p < car.pair_count; p++) { - uint32_t shape = car.pairs[p]; - bool collided = false; - for (uint32_t q = 0; q < car.pair_count; q++) collided |= ((made_contacts >> q) & 1) && car.pairs[q] == shape; - if (!collided) continue; - for (uint32_t i = 0; i < car.contact_count; i++) - if (car.contacts[i].shape == shape && car.contacts[i].depth > 0.0f) active[active_count++] = (uint8_t)i; - } - STEP_SECTION(3, ts); - if (active_count == 0) return SolveResult{velocity, Vec2{0.0f, 0.0f}, angular, 0.0f, false}; - STEP_COUNT(9); - float inv_mass = 1.0f / v.mass, inv_inertia = 1.0f / v.inertia, inv_dt = 1.0f / v.dt_f; - Vec2 vel = velocity; - float wv = angular; - Vec2 center = bx * v.center_of_mass.x + by * v.center_of_mass.y; - for (uint32_t k = 0; k < active_count; k++) { - Contact& c = car.contacts[active[k]]; - const ShapeDev& shape = w.shapes[c.shape]; - Vec2 arm = contact_arm(w, c, position, bx, by, center); - Vec2 n = c.normal, t = Vec2{-n.y, n.x}; - float rn = dot(arm, n), rt = dot(arm, t); - c.normal_mass = 1.0f / (inv_mass + inv_inertia * (dot(arm, arm) - rn * rn)); - c.tangent_mass = 1.0f / (inv_mass + inv_inertia * (dot(arm, arm) - rt * rt)); - c.bias = -v.contact_bias * inv_dt * rmin(-c.depth + v.allowed_penetration, 0.0f); - Vec2 rotational = Vec2{-arm.y * initial_angular, arm.x * initial_angular}; - c.bounce = rclamp(v.bounce + shape.bounce, 0.0f, 1.0f); - if (c.bounce != 0.0f) c.bounce *= dot(initial_velocity + rotational, n); - c.friction = fabsf(rmin(v.friction, shape.friction)); - Vec2 impulse = n * c.acc_normal + t * c.acc_tangent; - vel = vel + impulse * inv_mass; - wv += cross((arm + center) - center, impulse) * inv_inertia; - } - Vec2 bv = Vec2{0.0f, 0.0f}; - float bw = 0.0f; - float bias_limit = (float)(0.39269908169872414 / (double)v.dt_f); - for (uint32_t iteration = 0; iteration < v.solver_iterations; iteration++) { - for (uint32_t k = 0; k < active_count; k++) { - Contact& c = car.contacts[active[k]]; - Vec2 arm = contact_arm(w, c, position, bx, by, center); - Vec2 impulse_arm = (arm + center) - center; - Vec2 n = c.normal, t = Vec2{-n.y, n.x}; - Vec2 rotational = Vec2{-arm.y, arm.x}; - Vec2 at_contact = vel + rotational * wv; - float normal_speed = dot(at_contact, n), tangent_speed = dot(at_contact, t); - float biased = dot(bv + rotational * bw, n); - float bias_impulse = (c.bias - biased) * c.normal_mass; - float previous_bias = c.acc_bias; - c.acc_bias = rmax(previous_bias + bias_impulse, 0.0f); - Vec2 impulse = n * (c.acc_bias - previous_bias); - bv = bv + impulse * inv_mass; - bw = rclamp(bw + cross(impulse_arm, impulse) * inv_inertia, -bias_limit, bias_limit); - biased = dot(bv + rotational * bw, n); - if (fabsf(c.bias - biased) > 0.001f) { - float center_impulse = (c.bias - biased) / inv_mass; - float previous_center = c.acc_bias_center; - c.acc_bias_center = rmax(previous_center + center_impulse, 0.0f); - bv = bv + (n * (c.acc_bias_center - previous_center)) * inv_mass; - } - float normal_impulse = -(normal_speed + c.bounce) * c.normal_mass; - float previous_normal = c.acc_normal; - c.acc_normal = rmax(previous_normal + normal_impulse, 0.0f); - float friction_limit = c.friction * c.acc_normal; - float tangent_impulse = -tangent_speed * c.tangent_mass; - float previous_tangent = c.acc_tangent; - c.acc_tangent = rclamp(previous_tangent + tangent_impulse, -friction_limit, friction_limit); - impulse = n * (c.acc_normal - previous_normal) + t * (c.acc_tangent - previous_tangent); - vel = vel + impulse * inv_mass; - wv += cross(impulse_arm, impulse) * inv_inertia; - } - } - STEP_SECTION(4, ts); - // Reports are capped at max_contacts_reported, so any active contact collides unless the cap is 0. - return SolveResult{vel, bv, wv, bw, v.max_contacts_reported > 0}; -} - -// ---- Car::step ---- -// -// In parts: `step_begin` (driving, contact validation, broad pairs), then -// `sat_pair` for every broad pair, then `step_end` (contacts, merge, solve, -// integration, finish_callback). `car_step` runs them in a row. - -// A step's state between its parts. -struct StepCarry { - Vec2 old_velocity, velocity, previous_velocity; - float angular, previous_angular; - uint32_t was_active; - uint32_t base; // first SAT item of the car (the split kernels) -}; - -// `Car::step` (drive) or `Car::passive_step` up to the SAT tests. -__device__ inline StepCarry step_begin(const World& w, const VehicleDesc& v, Car& car, const double* raw, bool drive) { - STEP_MARK(tc); - STEP_COUNT(8); - uint32_t err = 0; - bool was_active = car.flags & CAR_ACTIVE; - Controls control = normalized(raw); - Vec2 impulse = Vec2{0.0f, 0.0f}; - float torque = 0.0f; - Vec2 old_velocity = car.velocity; - bool driving = was_active && drive; - if (driving) { - float boost = (float)control.boost, energy = car.boost_energy, strength = 0.0f; - if (boost > 0.0f && energy > 0.0f) { - energy = rmax(energy - boost * 0.01f, 0.0f); - strength = boost * 0.5f; - } else if (boost <= 0.0f) { - energy = rmin(energy + 0.002f, 1.0f); - } - car.boost_energy = energy; - float drive_amount = (float)(control.acceleration * (control.acceleration > 0.0 ? 1.0 + (double)strength : 1.0)); - double steering_multiplier = 1.0 / (0.002 * (double)length(car.velocity) + 1.0); - Vec2 body_x = car.basis_x, body_y = car.basis_y; - // godot_ease(handbrake, 0.3) takes one of two inputs per step (the - // control, or 0.0 for wheels without handbrake power), so each pow - // is evaluated at most once and shared by the wheels with that input. - double ease[2]; - bool eased[2] = {false, false}; - for (uint32_t i = 0; i < v.wheel_count; i++) { - const WheelDesc& spec = v.wheels[i]; - Vec2 position = transform_point(car.position, body_x, body_y, spec.position); - Vec2 offset = position - car.position; - Vec2 wheel_velocity = position - ((car.wheel_has_previous >> i & 1) ? car.wheel_previous[i] : position); - car.wheel_previous[i] = position; - car.wheel_has_previous |= 1u << i; - float angle = car.wheel_angle[i] * (PI_F / 180.0f); - float s, c; - managed_sin_cos(angle, s, c, err); - Vec2 forward = -normalized(body_x * -s + body_y * c); - Vec2 right = normalized(body_x * c + body_y * s); - float extra = angle * (w.surfaces[car.wheel_surface[i]].steering - 1.0f); - if (extra != 0.0f) { - forward = rotated(forward, extra, err); - right = rotated(right, extra, err); - } - uint32_t surface_index = vehicle_surface(w, position); - const SurfaceDev& surface = w.surfaces[surface_index]; - car.wheel_surface[i] = surface_index; - Vec2 direction = normalized(wheel_velocity); - Vec2 braking = limit(-direction * ((float)control.handbrake * spec.handbrake_power) - + -direction * ((float)control.brake * spec.brake_power), spec.brake_max); - Vec2 drive_force = forward * (drive_amount * spec.power); - Vec2 wheel_drive = combine(braking, drive_force, spec.brake_max, spec.power) * surface.power; - impulse = impulse + wheel_drive; - torque += cross(offset, wheel_drive); - float lateral_speed = dot(wheel_velocity, right); - uint32_t off = spec.handbrake_off ? 1 : 0; - if (!eased[off]) { - ease[off] = godot_ease(w.math_profile, off ? 0.0 : control.handbrake, 0.3); - eased[off] = true; - } - double lateral_grip = 0.20000000298023224 - + (0.8 + (0.1 - 0.8) * ease[off]) - / (1.0 + profile_exp(w.math_profile, ((double)length(wheel_velocity) - 450.0) * 0.00800000037997961)); - float effective_grip = (float)((double)v.grip * lateral_grip); - Vec2 lateral = right * (-effective_grip * lateral_speed) * surface.grip; - impulse = impulse + lateral; - torque += cross(offset, lateral); - if (spec.steering) { - double current = (double)car.wheel_angle[i]; - double target = (steering_multiplier * control.steering) * spec.max_angle_deg; - if (control.steering == 0.0 && !v.center_steering) target = current; - car.wheel_angle[i] = (float)(current + (target - current) * (v.steering_speed * steering_multiplier)); - } - } - impulse = impulse + (old_velocity * 0.005f) * (-v.air_resistance); - } - Vec2 velocity = old_velocity + impulse * (1.0f / v.mass); - if (driving && (double)length(old_velocity) > v.max_velocity) velocity = normalized(old_velocity) * v.max_velocity_f; - Vec2 previous_velocity = velocity; - float previous_angular = car.angular_velocity + torque * (1.0f / v.inertia); - float angular = previous_angular; - if (!v.custom_integrator) { - Vec2 zero = Vec2{0.0f, 0.0f}; - velocity = velocity * rmax(1.0f - v.linear_damp * v.dt_f, 0.0f) - + (((v.gravity * v.mass + zero) + zero) * (1.0f / v.mass)) * v.dt_f; - angular = previous_angular * rmax(1.0f - v.angular_damp * v.dt_f, 0.0f); - } - STEP_SECTION(0, tc); - car.error |= err; - prepare_contacts(w, v, car); - return StepCarry{old_velocity, velocity, previous_velocity, angular, previous_angular, was_active, 0}; -} - -// The rest of the step with `push_of(ci)`, the result of `sat_pair` for -// broad pair ci. Returns the reported wall contact. -template -__device__ inline bool step_end(const World& w, const VehicleDesc& v, Car& car, const StepCarry& k, PushOf&& push_of, bool drive, - bool eliminate_on_wall) { - STEP_MARK(tc); - uint32_t err = 0; - static_assert(MAX_PAIRS <= 32, "pair mask"); - // The car frame (two normalizations) is only needed once a pair pushes; - // most steps have no penetrating pair. The basis does not change here. - CarFrame f; - bool framed = false; - uint32_t made_contacts = 0; - for (uint32_t ci = 0; ci < car.pair_count; ci++) { - Vec2 push = push_of(ci); - if (is_zero(push)) continue; - if (!framed) { - f = car_frame(v, car); - framed = true; - } - uint32_t made = pair_contacts(w, v, car, f, ci, push, [&](const Contact& c) { - STEP_MARK(tm); - merge_contact(v, car, c); - STEP_SECTION(11, tm); - }); - made_contacts |= made ? 1u << ci : 0u; - } - STEP_SECTION(2, tc); - SolveResult r = solve_contacts(w, v, car, made_contacts, k.velocity, k.angular, k.previous_velocity, k.previous_angular); - STEP_MARK(tr); - car.velocity = r.velocity; - car.angular_velocity = r.angular; - car.position = Vec2{car.position.x + (r.velocity.x + r.bias_velocity.x) * v.dt_f, - car.position.y + (r.velocity.y + r.bias_velocity.y) * v.dt_f}; - float angle_delta = (r.angular + r.bias_angular) * v.dt_f; - // apply_center_of_mass_displacement - Vec2 center = car.basis_x * v.center_of_mass.x + car.basis_y * v.center_of_mass.y; - if ((double)dot(center, center) > 1e-5 * 1e-5) { - float s, c; - engine_sin_cos(angle_delta, s, c, err); - Vec2 turned = Vec2{center.x * c - center.y * s, center.x * s + center.y * c}; - car.position = car.position + (center - turned); - } - set_transform_angle(w.math_profile, car, car.rotation + angle_delta, err); - car.tick += drive ? 1 : 0; - update_shape(v, car); - - // finish_callback (no pending transform reset; frozen cars are not modeled) - bool pending_velocity = car.flags & CAR_PENDING_VELOCITY; - bool report = r.collided && k.was_active && !pending_velocity; - if (report) { - car.collision_count++; - if (eliminate_on_wall) { - // set_active(false): reset_velocity((0, 0), 0) - car.flags &= ~CAR_ACTIVE; - car.reset_acceleration = car.acceleration; - pending_velocity = true; - } - } - if (pending_velocity) { - car.velocity = Vec2{0.0f, 0.0f}; - car.angular_velocity = 0.0f; - } - car.flags &= ~(CAR_PENDING_VELOCITY | CAR_RESET_RIGHT); - car.acceleration = car.velocity - k.old_velocity; - car.error |= err; - STEP_SECTION(5, tr); - return report; -} - -// `Car::step` (drive) or `Car::passive_step`, then `finish_callback`. -// Returns the reported wall contact. -__device__ inline bool car_step(const World& w, const VehicleDesc& v, Car& car, const double* raw, bool drive, - bool eliminate_on_wall) { - StepCarry k = step_begin(w, v, car, raw, drive); - return step_end(w, v, car, k, [&](uint32_t ci) { return sat_pair(w, v, car, ci); }, drive, eliminate_on_wall); -} - -} // namespace altd diff --git a/github-simulator/source/gpu/sim/track.h b/github-simulator/source/gpu/sim/track.h deleted file mode 100644 index fc2f21fa21785d38b4b2b9df11ba7ed66d777d04..0000000000000000000000000000000000000000 --- a/github-simulator/source/gpu/sim/track.h +++ /dev/null @@ -1,187 +0,0 @@ -// Track queries of src/track/world.rs and src/track/curve.rs: tile surfaces, the path -// sensor point, Curve2D sampling and closest offsets, and closest_path. -#pragma once -#include -#include "vec.h" -#include "world.h" - -namespace altd { - -// `TileTable::get`; null outside the table. -__device__ inline const TileCell* tile_at(const World& w, Vec2 p) { - int64_t x = sat_i64(floor((double)p.x / 768.0)) - w.tile_x0; - int64_t y = sat_i64(floor((double)p.y / 768.0)) - w.tile_y0; - if (x < 0 || y < 0 || x >= w.tile_nx || y >= w.tile_ny) return nullptr; - return &w.tiles[y * w.tile_nx + x]; -} - -// `Track::vehicle_surface`, as an index into w.surfaces. -__device__ inline uint32_t vehicle_surface(const World& w, Vec2 p) { - const TileCell* t = tile_at(w, p); - return t ? t->surface : w.default_surface; -} - -// `Track::path_sensor_point` -__device__ inline Vec2 path_sensor_point(const World& w, Vec2 p) { - const TileCell* t = tile_at(w, p); - if (!t || !t->connected) return p; - Vec2 a = t->first; - Vec2 v = t->last - a; - float n = dot(v, v); - Vec2 closest = n == 0.0f ? a : a + v * (rclamp(dot(p, v) - dot(a, v), 0.0f, n) / n); - return closest + (p - closest) * 0.5f; -} - -// Cell of `g` holding q, or NONE (outside or not finite). -__device__ inline uint32_t grid_cell(const NearGrid& g, Vec2 q) { - double fx = floor(((double)q.x - g.x0) / g.cell), fy = floor(((double)q.y - g.y0) / g.cell); - if (!(fx >= 0.0 && fy >= 0.0 && fx < (double)g.nx && fy < (double)g.ny)) return NONE; - return (uint32_t)fy * g.nx + (uint32_t)fx; -} - -// ---- Curve2D ---- - -// `Curve::interval` -__device__ inline uint32_t curve_interval(const World& w, float offset, float& t, uint32_t& err) { - if (isnan(offset)) err |= ERR_PATH_NAN; - offset = rclamp(offset, 0.0f, w.curve_length); - uint32_t start = 0, end = w.curve_points, index = (start + end) / 2; - while (start < index) { - if (offset <= w.curve_offsets[index]) end = index; else start = index; - index = (start + end) / 2; - } - index = min(index, w.curve_points - 2); // only a NaN offset (flagged) gets here - float span = w.curve_offsets[index + 1] - w.curve_offsets[index]; - t = span < FLT_EPSILON ? 0.5f : (offset - w.curve_offsets[index]) / span; - return index; -} - -// `Curve::direction` -__device__ inline Vec2 curve_direction(const World& w, float offset, uint32_t& err) { - float t; - uint32_t i = curve_interval(w, offset, t, err); - Vec2 a = w.curve_forwards[i], b = w.curve_forwards[i + 1]; - float a2 = dot(a, a), b2 = dot(b, b); - if (a2 == 0.0f || b2 == 0.0f) return normalized(a + (b - a) * t); - float len = sqrtf(a2); - float result_length = len + (sqrtf(b2) - len) * t; - float angle = profile_atan2(w.math_profile, cross(a, b), dot(a, b)) * t; - float s, c; - engine_sin_cos(angle, s, c, err); - return normalized(Vec2{a.x * c - a.y * s, a.x * s + a.y * c} * (result_length / len)); -} - -// `Curve::position` -__device__ inline Vec2 curve_position(const World& w, float offset, uint32_t& err) { - float t; - uint32_t i = curve_interval(w, offset, t, err); - Vec2 a = w.curve_position[i]; - return a + (w.curve_position[i + 1] - a) * t; -} - -// `Curve::project`: (offset, squared distance) of q on segment i. -__device__ inline float curve_project(const World& w, Vec2 q, uint32_t i, float& d2) { - float span = w.curve_offsets[i + 1] - w.curve_offsets[i]; - Vec2 origin = w.curve_position[i]; - Vec2 e = w.curve_position[i + 1] - origin; - Vec2 direction = Vec2{e.x / span, e.y / span}; - float d = rclamp(dot(q - origin, direction), 0.0f, span); - Vec2 delta = (origin + direction * d) - q; - d2 = dot(delta, delta); - return w.curve_offsets[i] + d; -} - -// `Curve::closest_offset`: segment 0 first (a NaN there sticks), then strict -// improvements in ascending order, over a candidate superset of the nearest. -__device__ inline float curve_closest_offset(const World& w, Vec2 q) { - float nearest = 0.0f, distance = -1.0f; - uint32_t count = w.curve_points - 1; - if (w.curve_points < 2) return nearest; - auto visit = [&](uint32_t i) { - float d2; - float offset = curve_project(w, q, i, d2); - if (distance < 0.0f || d2 < distance) { nearest = offset; distance = d2; } - }; - visit(0); - uint32_t cell = grid_cell(w.curve_grid, q); - if (cell != NONE) { - for (uint32_t k = w.curve_grid.start[cell], e = w.curve_grid.start[cell + 1]; k < e; k++) { - uint32_t i = w.curve_grid.items[k]; - if (i > 0) visit(i); - } - } else { - for (uint32_t i = 1; i < count; i++) visit(i); - } - return nearest; -} - -// ---- baked path ---- - -struct PathHit { double offset; Vec2 near, tangent; }; - -// `PathSegments::project` -__device__ inline float path_project(const PathSeg& s, Vec2 p, float& fraction, Vec2& near) { - Vec2 start = Vec2{s.x, s.y}, delta = Vec2{s.dx, s.dy}; - fraction = s.len2 > 0.0f ? rclamp(dot(p - start, delta) / s.len2, 0.0f, 1.0f) : 0.0f; - near = start + delta * fraction; - Vec2 diff = p - near; - return dot(diff, diff); -} - -__device__ inline float path_posmod(float x, float length) { - float r = fmodf(x, length); - return r < 0.0f ? r + length : r; -} - -// `binary_search_by(partial_cmp).unwrap_or_else(|i| i.saturating_sub(1))` -// over strictly increasing path offsets. -__device__ inline int64_t path_index(const World& w, float offset, uint32_t& err) { - double v = (double)offset; - if (isnan(v)) { err |= ERR_PATH_NAN; return 0; } - uint32_t lo = 0, hi = w.path_points; // first index with offsets[i] >= v - while (lo < hi) { - uint32_t mid = (lo + hi) / 2; - if (w.path_offsets[mid] < v) lo = mid + 1; else hi = mid; - } - if (lo < w.path_points && w.path_offsets[lo] == v) return lo; - return lo > 0 ? lo - 1 : 0; -} - -// `Track::closest_path(point, previous)`; `has_previous` selects the window. -__device__ inline PathHit closest_path(const World& w, Vec2 point, bool has_previous, double previous, uint32_t& err) { - int64_t count = w.path_points - 1; - float length = (float)w.path_offsets[count]; - int64_t from = 0, to = count; - if (has_previous && length > 900.0f) { - float prev = (float)previous; - int64_t center = path_index(w, path_posmod(prev, length), err); - from = path_index(w, path_posmod(prev - 450.0f, length), err); - to = path_index(w, path_posmod(prev + 450.0f, length), err) + 1; - if (from > center) from -= count; - if (to < center) to += count; - } - float distance = FLT_MAX; - int64_t nearest = -1; - auto visit = [&](int64_t i) { - float fraction; - Vec2 near; - float d2 = path_project(w.path_segments[i], point, fraction, near); - if (d2 < distance) { distance = d2; nearest = i; } - }; - uint32_t cell = (from == 0 && to == count) ? grid_cell(w.path_grid, point) : NONE; - if (cell != NONE) { - for (uint32_t k = w.path_grid.start[cell], e = w.path_grid.start[cell + 1]; k < e; k++) visit(w.path_grid.items[k]); - } else { - for (int64_t i = from; i < to; i++) visit(((i % count) + count) % count); - } - if (nearest < 0) return PathHit{0.0, Vec2{0.0f, 0.0f}, Vec2{0.0f, 0.0f}}; - const PathSeg& s = w.path_segments[nearest]; - float fraction; - Vec2 near; - path_project(s, point, fraction, near); - float begin = (float)w.path_offsets[nearest], end = (float)w.path_offsets[nearest + 1]; - double offset = length > 0.0f ? (double)path_posmod(begin + (end - begin) * fraction, length) : 0.0; - return PathHit{offset, near, normalized(Vec2{s.dx, s.dy})}; -} - -} // namespace altd diff --git a/github-simulator/source/gpu/sim/vec.h b/github-simulator/source/gpu/sim/vec.h deleted file mode 100644 index 7a33deb43f963679337b9b999d0db20faa623ada..0000000000000000000000000000000000000000 --- a/github-simulator/source/gpu/sim/vec.h +++ /dev/null @@ -1,70 +0,0 @@ -// godot_math::F2 and the scalar helpers with the CPU's exact semantics. -// Build with -ffp-contract=off: every operation rounds on its own. -#pragma once -#include "compat.h" -#include "math.h" -#include "state.h" - -namespace altd { - -__device__ inline Vec2 v2(float x, float y) { return Vec2{x, y}; } -__device__ inline Vec2 operator+(Vec2 a, Vec2 b) { return Vec2{a.x + b.x, a.y + b.y}; } -__device__ inline Vec2 operator-(Vec2 a, Vec2 b) { return Vec2{a.x - b.x, a.y - b.y}; } -__device__ inline Vec2 operator*(Vec2 a, float s) { return Vec2{a.x * s, a.y * s}; } -__device__ inline Vec2 operator-(Vec2 a) { return Vec2{-a.x, -a.y}; } -__device__ inline float dot(Vec2 a, Vec2 b) { return a.x * b.x + a.y * b.y; } -__device__ inline float cross(Vec2 a, Vec2 b) { return a.x * b.y - a.y * b.x; } -// sqrtf is correctly rounded only with -fhip-fp32-correctly-rounded-divide-sqrt -// (gpu/build.sh); __fsqrt_rn is NOT (ROCm lowers it to the approximate -// instruction: ~15% of results differ by an ulp). -__device__ inline float length(Vec2 a) { return sqrtf(dot(a, a)); } -__device__ inline bool same_bits(Vec2 a, Vec2 b) { return fbits(a.x) == fbits(b.x) && fbits(a.y) == fbits(b.y); } - -__device__ inline Vec2 normalized(Vec2 a) { - float n = length(a); - return n == 0.0f ? Vec2{0.0f, 0.0f} : Vec2{a.x / n, a.y / n}; -} - -// Godot C# Vector2.LimitLength divides before multiplying. -__device__ inline Vec2 limit(Vec2 a, float max) { - float n = length(a); - return (n > max && n > 0.0f) ? Vec2{a.x / n, a.y / n} * max : a; -} - -__device__ inline Vec2 rotated(Vec2 a, float angle, uint32_t& err) { - float s, c; - managed_sin_cos(angle, s, c, err); - return Vec2{a.x * c - a.y * s, a.x * s + a.y * c}; -} - -// Rust f32::max / f32::min as lowered on x86 (a NaN operand yields the other). -__device__ inline float rmax(float a, float b) { return isnan(a) ? b : (b > a ? b : a); } -__device__ inline float rmin(float a, float b) { return isnan(a) ? b : (b < a ? b : a); } -// Rust f32::clamp: NaN passes through. -__device__ inline float rclamp(float x, float lo, float hi) { return x < lo ? lo : (x > hi ? hi : x); } -// pymath::py_max / py_min / clamp (f64). -__device__ inline double py_max(double a, double b) { return b > a ? b : a; } -__device__ inline double py_min(double a, double b) { return b < a ? b : a; } -__device__ inline double py_clamp(double v, double lo, double hi) { return py_min(hi, py_max(lo, v)); } - -// Rust `as i64` from f64 saturates and maps NaN to 0. -__device__ inline int64_t sat_i64(double x) { - if (isnan(x)) return 0; - if (x >= 9223372036854775807.0) return INT64_MAX; - if (x <= -9223372036854775808.0) return INT64_MIN; - return (int64_t)x; -} - -// `godot_math::transform_point`: products before the origin. -__device__ inline Vec2 transform_point(Vec2 position, Vec2 x, Vec2 y, Vec2 p) { - return (x * p.x + y * p.y) + position; -} - -// `godot_math::signed_sensor`: Mathf.Remap(value, -max, max, -1, 1), then Clamp. -__device__ inline double signed_sensor(float value, float maximum) { - float from = 0.0f - maximum; - float weight = (value - from) / (maximum - from); - return (double)rclamp(-1.0f + 2.0f * weight, -1.0f, 1.0f); -} - -} // namespace altd diff --git a/github-simulator/source/gpu/sim/world.h b/github-simulator/source/gpu/sim/world.h deleted file mode 100644 index d3c577d70e825fe76c0e780fee4fbc2a811d5e3f..0000000000000000000000000000000000000000 --- a/github-simulator/source/gpu/sim/world.h +++ /dev/null @@ -1,79 +0,0 @@ -// Static track data on the device. `World` is filled by Rust (src/gpu/simulation.rs -// mirrors it) with host pointers and copied field by field to the device by -// altd_gpu_world_create. -#pragma once -#include -#include "state.h" - -namespace altd { - -constexpr uint32_t NONE = 0xffffffffu; -constexpr int MAX_RAY_DEPTH = 48; - -struct RayNode { uint32_t first, last, front, back; float4 own, subtree; }; - -// world::Surface, with the `as f32` every user applies. -struct SurfaceDev { float grip, power, steering, pad; }; - -// One 768-pixel tile: surface index (vehicle_surface) and the path-sensor -// connection line (first, last) if `connected`. -struct TileCell { uint32_t surface, connected; Vec2 first, last; }; - -// path_segments::PathSegments, one row per segment. -struct PathSeg { float x, y, dx, dy, len2, pad0, pad1, pad2; }; - -// world::PhysicsShape. -struct ShapeDev { - Vec2 origin; // origin_f32 - float friction, bounce; - double min_x, min_y, max_x, max_y; - uint32_t first, count, wall_first, pad; // points first .. first + count of shape_local/shape_normals -}; - -// Cell lists of the segments that can be nearest to any point of the cell -// (gpu::simulation::near_grid). Points outside the grid use the full scan. -struct NearGrid { - double x0, y0, cell; - uint32_t nx, ny; - const uint32_t* start; // nx * ny + 1 offsets into items - const uint32_t* items; // ascending segment indices per cell -}; - -struct World { - // BSP raycaster (bsp::RayTree) - uint32_t ray_node_count, ray_wall_count, ray_depth; - float ray_magnitude; - const RayNode* ray_nodes; - const float4* ray_walls; - // vehicle surfaces by tile - uint32_t surface_count, default_surface; // default: vehicle.surface("asphalt") - const SurfaceDev* surfaces; - int64_t tile_x0, tile_y0, tile_nx, tile_ny; - const TileCell* tiles; - // baked path (Track::closest_path) - uint32_t path_points; - uint32_t math_profile; // MathProfile::index (math.h MATH_*) - const PathSeg* path_segments; - const double* path_offsets; - NearGrid path_grid; - // Curve2D (curve::Curve) - uint32_t curve_points; - float curve_length; - const Vec2* curve_position; - const float* curve_offsets; - const Vec2* curve_forwards; - NearGrid curve_grid; - // physics shapes - uint32_t shape_count, shape_point_count; - const ShapeDev* shapes; - const Vec2* shape_local; - const Vec2* shape_normals; - // Shapes per cell whose box (grown by 1) meets the cell grown by - // shape_margin, ascending (gpu::simulation::shape_grid). - NearGrid shape_grid; - double shape_margin; - // Track::path as (x, y) pairs, path_points of them (Track::path_position). - const double* path_xy; -}; - -} // namespace altd diff --git a/github-simulator/source/gpu/test_constructor_cleanup.py b/github-simulator/source/gpu/test_constructor_cleanup.py deleted file mode 100644 index ce414cc92027e57df967b40eb7788d4be3c58f6b..0000000000000000000000000000000000000000 --- a/github-simulator/source/gpu/test_constructor_cleanup.py +++ /dev/null @@ -1,332 +0,0 @@ -#!/usr/bin/env python3 -"""Check HIP constructor cleanup without allocating GPU memory. - -Usage: python3 gpu/test_constructor_cleanup.py [path/to/libaltd_gpu.so] - -Requires Linux, a C++17 compiler, and the ROCm runtime needed to load the -library's GPU registration symbols. A temporary LD_PRELOAD shim replaces HIP -memory, event, and stream calls with ordinary host allocations. No kernels run. -""" - -import argparse -import os -from pathlib import Path -import shlex -import subprocess -import sys -import tempfile - - -SHIM = r""" -#include -#include -#include -#include -#include - -namespace { -enum Operation { DEVICE_ALLOC, COPY, HOST_ALLOC, EVENT_CREATE, STREAM_CREATE, OPERATIONS }; -enum Resource { DEVICE, HOST, EVENT, STREAM, RESOURCES }; -struct Allocation { Resource kind; size_t bytes; }; -std::unordered_map allocations; -std::array calls{}; -std::array outstanding{}; -int failure_operation = -1; -size_t failure_call = 0; -size_t invalid_calls = 0; -constexpr int SUCCESS = 0; -constexpr int FAILURE = 2; - -bool fail(Operation operation) { - ++calls[operation]; - return failure_operation == operation && calls[operation] == failure_call; -} - -int allocate(void** pointer, size_t bytes, Operation operation, Resource resource) { - if (fail(operation)) return FAILURE; - void* memory = std::malloc(bytes ? bytes : 1); - if (!memory) std::abort(); - allocations.emplace(memory, Allocation{resource, bytes}); - ++outstanding[resource]; - *pointer = memory; - return SUCCESS; -} - -int release(void* pointer, Resource resource) { - if (!pointer) return SUCCESS; - auto allocation = allocations.find(pointer); - if (allocation == allocations.end() || allocation->second.kind != resource) { - ++invalid_calls; - return FAILURE; - } - --outstanding[resource]; - allocations.erase(allocation); - std::free(pointer); - return SUCCESS; -} -} - -extern "C" { -// These Linux C ABI signatures avoid a dependency on ROCm development headers. -int hipMalloc(void** pointer, size_t bytes) { - return allocate(pointer, bytes, DEVICE_ALLOC, DEVICE); -} -int hipMemcpy(void* destination, const void* source, size_t bytes, int kind) { - if (fail(COPY)) return FAILURE; - auto allocation = allocations.find(destination); - if (kind != 1 || allocation == allocations.end() - || allocation->second.kind != DEVICE || allocation->second.bytes < bytes - || (!source && bytes)) { - ++invalid_calls; - return FAILURE; - } - if (bytes) std::memcpy(destination, source, bytes); - return SUCCESS; -} -int hipHostMalloc(void** pointer, size_t bytes, unsigned int) { - return allocate(pointer, bytes, HOST_ALLOC, HOST); -} -int hipEventCreateWithFlags(void** event, unsigned int) { - return allocate(event, 1, EVENT_CREATE, EVENT); -} -int hipStreamCreate(void** stream) { - return allocate(stream, 1, STREAM_CREATE, STREAM); -} -int hipFree(void* pointer) { return release(pointer, DEVICE); } -int hipHostFree(void* pointer) { return release(pointer, HOST); } -int hipEventDestroy(void* event) { return release(event, EVENT); } -int hipStreamDestroy(void* stream) { return release(stream, STREAM); } -const char* hipGetErrorString(int) { return "injected HIP constructor test failure"; } - -void cleanup_test_reset(int operation, size_t call) { - calls.fill(0); - failure_operation = operation; - failure_call = call; -} -size_t cleanup_test_calls(int operation) { return calls.at(operation); } -size_t cleanup_test_outstanding(int resource) { return outstanding.at(resource); } -size_t cleanup_test_invalid_calls() { return invalid_calls; } -} -""" - - -RUNNER = r""" -#include -#include -#include -#include -#include -#include - -struct alignas(16) float4 { float x, y, z, w; }; -#include "world.h" - -namespace { -enum Operation { DEVICE_ALLOC, COPY, HOST_ALLOC, EVENT_CREATE, STREAM_CREATE, OPERATIONS }; -constexpr int RESOURCES = 4; -const char* operation_names[] = { - "hipMalloc", "hipMemcpy", "hipHostMalloc", "hipEventCreateWithFlags", "hipStreamCreate" -}; -const char* resource_names[] = {"device", "host", "event", "stream"}; - -[[noreturn]] void fail(const char* message) { - std::fprintf(stderr, "constructor cleanup test: %s\n", message); - std::exit(1); -} - -template T symbol(void* library, const char* name) { - dlerror(); - void* address = dlsym(library, name); - if (const char* error = dlerror()) fail(error); - return reinterpret_cast(address); -} - -// Every World upload has a nonzero count and a distinct host pointer. The -// copied grid descriptors must release their uploaded starts and items too. -struct Fixture { - altd::RayNode ray_nodes[1]{}; - float4 ray_walls[1]{}; - altd::SurfaceDev surfaces[1]{}; - altd::TileCell tiles[1]{}; - altd::PathSeg path_segments[1]{}; - double path_offsets[2]{}; - uint32_t path_start[2]{0, 1}, path_items[1]{}; - altd::Vec2 curve_position[2]{}; - float curve_offsets[2]{}; - altd::Vec2 curve_forwards[2]{}; - uint32_t curve_start[2]{0, 1}, curve_items[1]{}; - altd::ShapeDev shapes[1]{}; - altd::Vec2 shape_local[1]{}, shape_normals[1]{}; - uint32_t shape_start[2]{0, 1}, shape_items[1]{}; - double path_xy[4]{}; - altd::World world{}; - - Fixture() { - world.ray_node_count = world.ray_wall_count = world.ray_depth = 1; - world.ray_nodes = ray_nodes; - world.ray_walls = ray_walls; - world.surface_count = 1; - world.surfaces = surfaces; - world.tile_nx = world.tile_ny = 1; - world.tiles = tiles; - world.path_points = 2; - world.path_segments = path_segments; - world.path_offsets = path_offsets; - world.path_grid = {0, 0, 1, 1, 1, path_start, path_items}; - world.curve_points = 2; - world.curve_position = curve_position; - world.curve_offsets = curve_offsets; - world.curve_forwards = curve_forwards; - world.curve_grid = {0, 0, 1, 1, 1, curve_start, curve_items}; - world.shape_count = world.shape_point_count = 1; - world.shapes = shapes; - world.shape_local = shape_local; - world.shape_normals = shape_normals; - world.shape_grid = {0, 0, 1, 1, 1, shape_start, shape_items}; - world.path_xy = path_xy; - } -}; -} - -int main(int argc, char** argv) { - if (argc != 2) fail("expected library path"); - void* library = dlopen(argv[1], RTLD_NOW | RTLD_LOCAL); - if (!library) fail(dlerror()); - auto world_create = symbol(library, "altd_gpu_world_create"); - auto world_free = symbol(library, "altd_gpu_world_free"); - auto sim_create = symbol(library, "altd_gpu_sim_create"); - auto sim_free = symbol(library, "altd_gpu_sim_free"); - auto reset = symbol(RTLD_DEFAULT, "cleanup_test_reset"); - auto calls = symbol(RTLD_DEFAULT, "cleanup_test_calls"); - auto outstanding = symbol(RTLD_DEFAULT, "cleanup_test_outstanding"); - auto invalid_calls = symbol(RTLD_DEFAULT, "cleanup_test_invalid_calls"); - - auto check_resources = [&](const std::array& expected, const char* context) { - if (invalid_calls()) fail("constructor cleanup freed an unowned pointer or made an invalid copy"); - for (int resource = 0; resource < RESOURCES; ++resource) { - size_t count = outstanding(resource); - if (count != expected[resource]) { - std::fprintf(stderr, "%s: %zu outstanding %s resources, expected %zu\n", - context, count, resource_names[resource], expected[resource]); - fail("resource leak"); - } - } - }; - const std::array none{}; - check_resources(none, "before constructors"); - world_free(nullptr); - sim_free(nullptr); - check_resources(none, "null destructors"); - - Fixture fixture; - reset(-1, 0); - altd::World* world = world_create(&fixture.world); - if (!world) fail("successful world constructor returned null"); - std::array world_calls{}; - for (int operation = 0; operation < OPERATIONS; ++operation) world_calls[operation] = calls(operation); - if (world_calls[DEVICE_ALLOC] != 19 || world_calls[COPY] != 19) - fail("world fixture did not exercise all 19 uploads through the fake runtime"); - world_free(world); - check_resources(none, "successful world destruction"); - - size_t world_failures = 0; - for (int operation = 0; operation < OPERATIONS; ++operation) { - for (size_t call = 1; call <= world_calls[operation]; ++call) { - reset(operation, call); - world = world_create(&fixture.world); - if (world) fail("failed world constructor returned a handle"); - if (calls(operation) != call) fail("world failure injection was not reached"); - char context[128]; - std::snprintf(context, sizeof context, "world %s failure %zu", operation_names[operation], call); - check_resources(none, context); - ++world_failures; - } - } - - reset(-1, 0); - world = world_create(&fixture.world); - if (!world) fail("sim test world constructor returned null"); - std::array world_resources{}; - for (int resource = 0; resource < RESOURCES; ++resource) world_resources[resource] = outstanding(resource); - altd::VehicleDesc vehicle{}; - reset(-1, 0); - void* sim = sim_create(world, &vehicle, nullptr, 0, 2); - if (!sim) fail("successful sim constructor returned null"); - std::array sim_calls{}; - for (int operation = 0; operation < OPERATIONS; ++operation) sim_calls[operation] = calls(operation); - if (sim_calls[DEVICE_ALLOC] != 16 || sim_calls[HOST_ALLOC] != 1 - || sim_calls[EVENT_CREATE] != 4 || sim_calls[STREAM_CREATE] != 1) - fail("sim fixture did not exercise all allocations through the fake runtime"); - sim_free(sim); - check_resources(world_resources, "successful sim destruction"); - - size_t sim_failures = 0; - for (int operation = 0; operation < OPERATIONS; ++operation) { - for (size_t call = 1; call <= sim_calls[operation]; ++call) { - reset(operation, call); - sim = sim_create(world, &vehicle, nullptr, 0, 2); - if (sim) fail("failed sim constructor returned a handle"); - if (calls(operation) != call) fail("sim failure injection was not reached"); - char context[128]; - std::snprintf(context, sizeof context, "sim %s failure %zu", operation_names[operation], call); - check_resources(world_resources, context); - ++sim_failures; - } - } - reset(-1, 0); - world_free(world); - world_free(nullptr); - sim_free(nullptr); - check_resources(none, "final world destruction and null destructors"); - dlclose(library); - check_resources(none, "library unload"); - std::printf("HIP constructor cleanup passed: %zu world failures, %zu sim failures, successful and null destructors\n", - world_failures, sim_failures); -} -""" - - -def main() -> None: - parser = argparse.ArgumentParser(description=__doc__) - gpu_dir = Path(__file__).resolve().parent - parser.add_argument( - "library", nargs="?", type=Path, default=gpu_dir.parent / "target/gpu/libaltd_gpu.so" - ) - args = parser.parse_args() - if not sys.platform.startswith("linux"): - parser.error("this test requires Linux LD_PRELOAD") - library = args.library.resolve() - if not library.is_file(): - parser.error(f"library does not exist: {library}; build it with gpu/build.sh") - compiler = shlex.split(os.environ.get("CXX", "c++")) - with tempfile.TemporaryDirectory(prefix="altd-constructor-cleanup-") as directory: - temporary = Path(directory) - shim_source = temporary / "fake_hip.cpp" - runner_source = temporary / "constructor_cleanup.cpp" - shim_library = temporary / "fake_hip.so" - runner = temporary / "constructor_cleanup" - shim_source.write_text(SHIM) - runner_source.write_text(RUNNER) - common = [*compiler, "-std=c++17", "-O0", "-Wall", "-Wextra", "-Werror"] - subprocess.run( - [*common, "-fPIC", "-shared", str(shim_source), "-o", str(shim_library)], check=True - ) - subprocess.run( - [*common, "-I", str(gpu_dir / "sim"), str(runner_source), "-ldl", "-o", str(runner)], - check=True, - ) - environment = os.environ.copy() - environment["LD_PRELOAD"] = str(shim_library) - result = subprocess.run( - [str(runner), str(library)], env=environment, text=True, capture_output=True - ) - if result.stdout: - print(result.stdout, end="") - if result.returncode: - print(result.stderr, end="", file=sys.stderr) - raise SystemExit(result.returncode) - - -if __name__ == "__main__": - main() diff --git a/github-simulator/source/math/README.md b/github-simulator/source/math/README.md deleted file mode 100644 index c38c60618103be978e3b0013f0f02aac8c318e2d..0000000000000000000000000000000000000000 --- a/github-simulator/source/math/README.md +++ /dev/null @@ -1,71 +0,0 @@ -# Math profiles and reference data - -The profile selects the C runtime arithmetic seen by the game. It is independent of the simulator's host, scheduling mode and CPU/GPU backend. All builds include every profile: - -| Profile | Game runtime reproduced | -| --- | --- | -| `proton` | Wine/Proton's musl-based UCRT math | -| `win10-fma3` | The x64 Microsoft UCRT FMA3 path used by Windows 10 and Windows 11 through 23H2 | -| `win11-fma3` | The x64 Microsoft UCRT FMA3 path used by Windows 11 24H2, build 26100, and newer | - -These names describe the arithmetic being reproduced. A Linux, macOS, ARM or WASM build can select either Windows profile without loading a Windows DLL or requiring the host to execute x86 FMA3 instructions. Native defaults detect the operating system/build; browsers use their platform hints. An explicit profile overrides detection. SSE2 UCRT variants are outside the supported profiles. - -## Kernel sources - -`kernels/ucrt.h` and `kernels/ucrt_tables.h` are the common source for the new Windows kernels: - -- `tools/ucrt/gen-math.py` translates restricted C to `src/math/ucrt.rs` for native x86/ARM and WASM CPU execution. -- `gpu/sim/math.h` includes the same C functions and tables for HIP/CUDA. -- `wasm/gen-sim.py` translates the kernels to `src/wasm/sim/generated.wgsl`. Shader creation specializes each pipeline for the session's profile and removes unused functions/tables. - -The shared functions cover `atan2f`, `exp`, `pow`, `tanh` and `log`. `sinf`/`cosf` start with the established musl implementation and apply sorted tables of every input where the Microsoft FMA3 result differs. The GPU's large-input trig restriction remains; the Rust implementation also includes the wide exception tables. WGSL prepares profiled sensor offsets on the WASM CPU, and evolution's `log` runs on the CPU, so those functions/tables are omitted from the WGSL module. - -The Proton kernels retain their existing Rust and GPU ports. AVX2 network accumulation remains ordered with separate multiplication/addition; Proton uses its existing vectorized tanh, while Windows profiles apply their generated scalar tanh to each accumulated output. This change gives the Windows kernels a common source without rewriting the established musl and managed/Godot trig implementations. - -Each explicit `fma` rounds once: Rust uses `f64::mul_add`, HIP/CUDA use double `fma`, and WGSL uses the integer-based software binary64 `f64_fma`. All other multiplications/additions preserve separate rounding. Keep `-ffp-contract=off` for HIP/C checks and `-fmad=false` for CUDA; neither flag disables an explicit `fma`. The kernel header documents its restricted types, casts, operation order and NaN bit construction. - -Check generated code without modifying it: - -```sh -python3 -m pip install -r wasm/requirements-sim.txt -python3 tools/ucrt/gen-math.py --check -python3 gpu/gen_tables.py --check -python3 wasm/gen-sim.py --check -``` - -Ordinary Rust/WASM builds consume committed generated sources and do not require Python or a Windows DLL. To regenerate Rust or WGSL after editing kernels, run the corresponding generator without `--check` and review its diff. - -## Captured oracle fixtures - -`fixtures/*.bin` contains results from genuine Microsoft UCRT DLLs executed under Wine, alongside Wine's builtin results. These are captured function oracles, separate from native current-code browser/session regression fixtures. - -The capture compared FMA3-enabled Microsoft builds in two observed groups: G01, represented by `ucrtbase.dll` 10.0.22621.7510, and G02, represented by 10.0.26100.9444. The binaries, complete raw runs and DLL capture harness are external research inputs, not repository assets. `tools/ucrt/tables.py` pins the representative DLL hashes/version and extracts the committed constants. `tools/ucrt/scan_sincos.c` scans all 2³² float32 bit patterns, recording exceptions against Wine's builtin functions. - -`tools/ucrt/fixtures.py` combines game-range and wide-range captures. It selects up to 2,000 differing rows plus 500 evenly spread rows, deduplicates them, and includes exhaustive sine/cosine scan samples. Current files contain 2,500 rows each, except `cosf.bin`, which has 2,499 after deduplication. - -There is no file header. Each little-endian row contains input words followed by output words for `proton`, `win10-fma3`, `win11-fma3` in that order: - -| Files | Word width | Input words | Words per row | -| --- | ---: | ---: | ---: | -| `sinf.bin`, `cosf.bin` | u32 | 1 | 4 | -| `atan2f.bin` | u32 | 2, ordered y then x | 5 | -| `exp.bin`, `log.bin`, `tanh.bin` | u64 | 1 | 4 | -| `pow.bin` | u64 | 2, ordered base then exponent | 5 | - -With the external capture directory and scans available, regeneration is explicit: - -```sh -python3 -I tools/ucrt/tables.py --win10 /path/to/g01/ucrtbase.dll \ - --win11 /path/to/g02/ucrtbase.dll --scan /path/to/scans -python3 -I tools/ucrt/fixtures.py /path/to/altd-wine --scan /path/to/scans -``` - -Fixture generation also needs NumPy. Do not replace oracle outputs with simulator-generated values to make a comparison pass. `tools/ucrt/check_kernels.c` compares the shared C/C++ kernels directly with the full external captures; its header contains compile/run commands. - -## Verification scope - -`cargo test math::profile` compares each primitive bit-for-bit with all three fixture columns. `gpu_check math` compares HIP/CUDA with the Rust implementations for every profile, and `ALTD_GPU_MATH` selects the simulation checks' profile. Browser CPU comparisons must generate a native reference using the same explicit `options.mathProfile`; full WebGPU comparisons use the WASM CPU in the page. Naga validates the full modules and all three profiles' specialized entry points without requiring a GPU. - -`PAGE=math-profiles.html GPU=hardware node wasm/test/run.mjs` checks WebGPU `atan2f`, `exp`, `pow` and `tanh` directly against the captured oracle columns, including signed zeros and subnormal inputs, rather than relying on a driving scenario to reach those cases. `GPU=software` runs the same 30,000 comparisons on SwiftShader; CI runs these fixture checks and the software binary64 tests without requiring physical GPU hardware. - -Primitive fixtures and successful tests on one adapter do not establish equality for every device, argument or complete game run. Existing host-dependent Python initialization and managed/Godot trig limits still apply. [Fidelity](../docs/fidelity.md), [the WASM guide](../wasm/README.md) and [GPU checks](../gpu/README.md) describe those boundaries. diff --git a/github-simulator/source/math/fixtures/atan2f.bin b/github-simulator/source/math/fixtures/atan2f.bin deleted file mode 100644 index 0b08896300d28d8fcc4de0ca05e71aa26fa74d66..0000000000000000000000000000000000000000 --- a/github-simulator/source/math/fixtures/atan2f.bin +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9ecf9251878ddcb789a43119f1f060e24025db3e3485cccdbcb1f4366cb68ed2 -size 50000 diff --git a/github-simulator/source/math/fixtures/cosf.bin b/github-simulator/source/math/fixtures/cosf.bin deleted file mode 100644 index 9ae3514f55453008e2e32fa6ea7d69926e7200ad..0000000000000000000000000000000000000000 --- a/github-simulator/source/math/fixtures/cosf.bin +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c84017673fdbe2a29f03e8ae4f410292da3ee93f5f74d91e169053a04c6273d6 -size 39984 diff --git a/github-simulator/source/math/fixtures/exp.bin b/github-simulator/source/math/fixtures/exp.bin deleted file mode 100644 index 12854c3ce5ce8739f47e1aec157ac7981a9ad16b..0000000000000000000000000000000000000000 --- a/github-simulator/source/math/fixtures/exp.bin +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b0b063fc5bba32294b399f7305718a15797f4df65ba81cfbc785264e11126a9a -size 80000 diff --git a/github-simulator/source/math/fixtures/log.bin b/github-simulator/source/math/fixtures/log.bin deleted file mode 100644 index d230f290fb607524dd5a89c831555c242b9057cc..0000000000000000000000000000000000000000 --- a/github-simulator/source/math/fixtures/log.bin +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9ef1de3473a7b1d8a2416eee3f2263a88eea073ad9c2af53bb751bca7e45813c -size 80000 diff --git a/github-simulator/source/math/fixtures/pow.bin b/github-simulator/source/math/fixtures/pow.bin deleted file mode 100644 index 9373a18c2494e5ea777e12a86fc2ada1de844d52..0000000000000000000000000000000000000000 --- a/github-simulator/source/math/fixtures/pow.bin +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d2475d6b050e7b842f868c093591b4095509cd2e1350ba8ebb06df64452bf3cc -size 100000 diff --git a/github-simulator/source/math/fixtures/sinf.bin b/github-simulator/source/math/fixtures/sinf.bin deleted file mode 100644 index 7450b380e7943365eb7a456d3813017226188c7a..0000000000000000000000000000000000000000 --- a/github-simulator/source/math/fixtures/sinf.bin +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bae316adc7a9ceb4a61a8375a7eb94004d9e9058e5563cc81e14c9ffe911a104 -size 40000 diff --git a/github-simulator/source/math/fixtures/tanh.bin b/github-simulator/source/math/fixtures/tanh.bin deleted file mode 100644 index f384ef36ee107b9c3f54a8aa4ec71cc4cae630ff..0000000000000000000000000000000000000000 --- a/github-simulator/source/math/fixtures/tanh.bin +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b0f438bcf53c685c622158e0ab2f47b9a2c714e8c35cb8b90da1e539a53c7e2c -size 80000 diff --git a/github-simulator/source/math/kernels/ucrt.h b/github-simulator/source/math/kernels/ucrt.h deleted file mode 100644 index 0c29f3f3ffbfd0c1db24d9f990c977b72f9644cf..0000000000000000000000000000000000000000 --- a/github-simulator/source/math/kernels/ucrt.h +++ /dev/null @@ -1,606 +0,0 @@ -// The float math of the Windows UCRT (ucrtbase.dll, FMA3 code paths), bit for bit. -// -// win10_*: ucrtbase 10.0.22621 (Windows 10, and Windows 11 up to 23H2). -// win11_*: ucrtbase 10.0.26100 (Windows 11 24H2 and later). -// win_*: the same in both. -// -// One source for every backend: tools/ucrt/gen-math.py translates it to Rust for the CPU -// (x86, ARM and wasm), gpu/sim/math.h includes it for HIP and CUDA, and wasm/gen-sim.py -// translates it to WGSL. It is therefore restricted C: -// - The operands of an operator have one type, except shift counts and literals; every other -// conversion is a cast. -// - A fused multiply-add is an explicit fma(). Nothing else may be contracted, so C and HIP/CUDA -// builds use -ffp-contract=off. -// - No pointers, references, recursion, macros, or side effects inside expressions. -// - NaN results are built from bits, because GPU arithmetic does not return x86's NaNs. -// - Doubles are only converted to integers when the value is in range. -// -// The includer defines ALTD_MATH_FN (function specifiers), ALTD_MATH_TABLE (table specifiers) -// and ALTD_MATH_CONST (scalar constant specifiers). It also provides bool, the types, -// fma, and the bit casts fbits/ffrom (float) and dbits/dfrom (double). -#pragma once -#include "ucrt_tables.h" - -ALTD_MATH_FN bool win_isnan(double x) { return (dbits(x) & 0x7fffffffffffffffull) > 0x7ff0000000000000ull; } - -// x86's NaN result of an operation on NaN operands: the first NaN, quieted. -ALTD_MATH_FN double win_quiet(double x) { return dfrom(dbits(x) | 0x0008000000000000ull); } -ALTD_MATH_FN double win_nan_operand(double a, double b) { return win_isnan(a) ? win_quiet(a) : win_quiet(b); } - -// ---- atan2f --------------------------------------------------------------------------------- - -ALTD_MATH_FN float win_atan2f(float yf, float xf) { - // The x operand's NaN first. - if ((fbits(xf) & 0x7fffffffu) > 0x7f800000u) return ffrom(fbits(xf) | 0x00400000u); - if ((fbits(yf) & 0x7fffffffu) > 0x7f800000u) return ffrom(fbits(yf) | 0x00400000u); - double x = (double)xf; - double y = (double)yf; - uint64_t xb = dbits(x); - uint64_t yb = dbits(y); - int32_t de = (int32_t)((yb >> 52) & 0x7ffull) - (int32_t)((xb >> 52) & 0x7ffull); - uint64_t ax = xb & 0x7fffffffffffffffull; - uint64_t ay = yb & 0x7fffffffffffffffull; - bool xneg = (xb >> 63) != 0ull; - bool yneg = (yb >> 63) != 0ull; - float pi = ffrom(0x40490fdbu); - float half_pi = ffrom(0x3fc90fdbu); - if (ay == 0ull) { - if (xneg) return yneg ? -pi : pi; - return yf; - } - if (ax == 0ull && yneg) return -half_pi; - if (de > 26) return yneg ? -half_pi : half_pi; - if (de < -13 && !xneg) { - // The DLL scales quotients below 2^-126 by 2^100 and back, which rounds the same. - if (de < -150) return yneg ? ffrom(0x80000000u) : ffrom(0u); - return (float)(y / x); - } - if (de < -26 && xneg) return yneg ? -pi : pi; - if (ay == 0x7ff0000000000000ull && ax == 0x7ff0000000000000ull) { - if (xneg) return yneg ? ffrom(0xc016cbe4u) : ffrom(0x4016cbe4u); - return yneg ? ffrom(0xbf490fdbu) : ffrom(0x3f490fdbu); - } - double sx = xneg ? -x : x; - double sy = yneg ? -y : y; - bool swap = sy > sx; - double num = swap ? sx : sy; - double den = swap ? sy : sx; - double t = num / den; - double r; - if (t > 0.0625) { - int32_t n = (int32_t)(t * 256.0 + 0.5); - double nd = (double)n; - double u = (num * 256.0 - nd * den) / (nd * num + den * 256.0); - double hi = u + dfrom(UCRT_ATAN2F_ATAN256[n - 16]); - double u3 = u * u; - u3 = u3 * u; - u3 = u3 * 0.33333333333224097; - r = hi - u3; - } else if (t < 0.0001) { - r = t; - } else { - double t2 = t * t; - double p = 0.19999999999393223 - t2 * 0.1428571356180717; - p = p * t2; - double t3 = t2 * t; - double q = 0.3333333333333317 - p; - q = q * t3; - r = t - q; - } - if (swap) r = 1.5707963267948966 - r; - if (xneg) r = 3.141592653589793 - r; - if (yneg) r = -r; - return (float)r; -} - -// ---- exp ------------------------------------------------------------------------------------ - -// exp of finite x in [-744.0346068132731, 709.782712893384]. -ALTD_MATH_FN double win_exp_finite(double x) { - if ((dbits(x) & 0x7fffffffffffffffull) <= 0x3e50000000000000ull) return x + 1.0; - int32_t n = (int32_t)(x * 92.33248261689366); - double nd = (double)n; - double r = nd * -2.5728046223276688e-14 + fma(nd, -0.010830424696223417, x); - int32_t j = n & 63; - int32_t k = n >> 6; - double p = fma(0.001388888888888889, r, 0.008333333333333333); - p = fma(p, r, 0.041666666666666664); - p = fma(p, r, 0.16666666666666666); - p = fma(p, r, 0.5); - double e = fma(r * r, p, r); - double t = e * dfrom(UCRT_EXP_MUL[j]) + dfrom(UCRT_EXP_LO[j]); - double res = t + dfrom(UCRT_EXP_HI[j]); - if (k > -1022 || (k == -1022 && res >= 1.0)) return dfrom(dbits(res) + ((uint64_t)(int64_t)k << 52)); - return res * dfrom(1ull << (uint32_t)((k + 1074) & 63)); -} - -ALTD_MATH_FN double win10_exp(double x) { - uint64_t xb = dbits(x); - if ((xb & 0x7fffffffffffffffull) >= 0x7ff0000000000000ull) { - if (xb == 0x7ff0000000000000ull) return x; - if (xb == 0xfff0000000000000ull) return 0.0; - return win_quiet(x); - } - if (x > 709.782712893384) return dfrom(0x7ff0000000000000ull); - if (x < -744.0346068132731) return 0.0; - return win_exp_finite(x); -} - -// As win10_exp, but the smallest subnormal below -744.0346068132731 down to -745.1332191019411. -ALTD_MATH_FN double win11_exp(double x) { - uint64_t xb = dbits(x); - if ((xb & 0x7fffffffffffffffull) >= 0x7ff0000000000000ull) { - if (xb == 0x7ff0000000000000ull) return x; - if (xb == 0xfff0000000000000ull) return 0.0; - return win_quiet(x); - } - if (x > 709.782712893384) return dfrom(0x7ff0000000000000ull); - if (x < -744.0346068132731) return x >= -745.1332191019411 ? dfrom(1ull) : 0.0; - return win_exp_finite(x); -} - -// ---- tanh ----------------------------------------------------------------------------------- - -// The polynomial coefficients of |x| <= 1, for |x| below and from 0.9. -ALTD_MATH_FN double win_tanh_k(int32_t i, bool up) { - double v = 0.0; - if (i == 0) v = up ? -0.00016559704390354995 : -0.0002000476210719095; - if (i == 1) v = up ? 1.154758789961434e-08 : 1.4207792637883471e-08; - if (i == 2) v = up ? 0.00017307605012622596 : 0.00020911402625291644; - if (i == 3) v = up ? 0.016735877546189656 : 0.020156216602693764; - if (i == 4) v = up ? 0.014617304728873168 : 0.017601634900304468; - if (i == 5) v = up ? 0.3172045589772944 : 0.3816414142883289; - if (i == 6) v = up ? 0.2277938706590883 : 0.27403042465617977; - if (i == 7) v = up ? 0.6833816119772959 : 0.8220912739685393; - return v; -} - -// 2^k as the product of two normal powers of two, for 1 + 2^k * m. -ALTD_MATH_FN double win_tanh_scale1(int32_t k) { return dfrom((uint64_t)(int64_t)(((k + (k < 0 ? 1 : 0)) >> 1) + 1023) << 52); } -ALTD_MATH_FN double win_tanh_scale2(int32_t k) { return dfrom((uint64_t)(int64_t)(k - ((k + (k < 0 ? 1 : 0)) >> 1) + 1023) << 52); } - -// win10 tanh rounds every operation. -ALTD_MATH_FN double win10_tanh(double x) { - uint64_t xb = dbits(x); - uint64_t ab = xb & 0x7fffffffffffffffull; - if (ab < 0x3e30000000000000ull) return x; - if (ab > 0x7ff0000000000000ull) return win_quiet(x); - bool neg = ab != xb; - double a = dfrom(ab); - if (a > 20.0) return neg ? -1.0 : 1.0; - double res; - if (a > 1.0) { - double a2 = a + a; - int32_t n = (int32_t)(a2 * 46.16624130844683 + 0.5); - int32_t j = n & 31; - double nd = (double)n; - double hi = dfrom(UCRT_TANH_HI[j]); - double lo = dfrom(UCRT_TANH_LO[j]); - int32_t k = (n - j) >> 5; - double r6 = a2 - nd * 0.021660849335603416; - double r1 = (double)(-n) * 5.689487495325456e-11; - double r = r1 + r6; - double p = r * 0.001388894908637772 + 0.008333367984342196; - p = p * r + 0.04166666666622608; - p = p * r + 0.16666666666526087; - p = p * r; - p = p + 0.5; - p = p * (r * r); - p = p + r1; - p = p + r6; - p = p * (lo + hi); - p = p + lo; - double t = (hi + p) * win_tanh_scale1(k); - t = t * win_tanh_scale2(k); - t = t + 1.0; - res = 1.0 - 2.0 / t; - } else { - bool up = a >= 0.9; - double x2 = a * a; - double x3 = x2 * a; - double p = win_tanh_k(0, up) - x2 * win_tanh_k(1, up); - p = p * x2; - p = p - win_tanh_k(4, up); - p = p * x2; - p = p - win_tanh_k(6, up); - p = p * x3; - double q = x2 * win_tanh_k(2, up) + win_tanh_k(3, up); - q = q * x2; - q = q + win_tanh_k(5, up); - q = q * x2; - q = q + win_tanh_k(7, up); - res = p / q + a; - } - return neg ? -res : res; -} - -// win11 tanh fuses the polynomials. -ALTD_MATH_FN double win11_tanh(double x) { - uint64_t xb = dbits(x); - uint64_t ab = xb & 0x7fffffffffffffffull; - if (ab < 0x3e30000000000000ull) return x; - if (ab > 0x7ff0000000000000ull) return win_quiet(x); - bool neg = ab != xb; - double a = dfrom(ab); - if (a > 20.0) return neg ? -1.0 : 1.0; - double res; - if (a > 1.0) { - double a2 = a + a; - int32_t n = (int32_t)(a2 * 46.16624130844683 + 0.5); - int32_t j = n & 31; - double nd = (double)n; - double hi = dfrom(UCRT_TANH_HI[j]); - double lo = dfrom(UCRT_TANH_LO[j]); - int32_t k = (n - j) >> 5; - double r6 = fma(-nd, 0.021660849335603416, a2); - double r1 = (double)(-n) * 5.689487495325456e-11; - double r = r1 + r6; - double p = fma(0.001388894908637772, r, 0.008333367984342196); - p = fma(p, r, 0.04166666666622608); - p = fma(p, r, 0.16666666666526087); - p = fma(p, r, 0.5); - double m = fma(r * r, p, r1) + r6; - m = fma(hi + lo, m, lo); - double t = (hi + m) * win_tanh_scale1(k); - t = fma(t, win_tanh_scale2(k), 1.0); - res = 1.0 - 2.0 / t; - } else { - bool up = a >= 0.9; - double x2 = a * a; - double x3 = x2 * a; - double p = fma(-win_tanh_k(1, up), x2, win_tanh_k(0, up)); - p = fma(p, x2, -win_tanh_k(4, up)); - p = fma(p, x2, -win_tanh_k(6, up)); - p = p * x3; - double q = fma(win_tanh_k(2, up), x2, win_tanh_k(3, up)); - q = fma(q, x2, win_tanh_k(5, up)); - q = fma(q, x2, win_tanh_k(7, up)); - res = p / q + a; - } - return neg ? -res : res; -} - -// ---- pow ------------------------------------------------------------------------------------ - -// 0: y is not an integer, 1: an odd integer, 2: an even integer. -ALTD_MATH_FN int32_t win_integer_kind(uint64_t yb) { - uint64_t e = (yb >> 52) & 0x7ffull; - if (e < 1023ull) return 0; - if (e > 1075ull) return 2; - uint64_t bit = 1ull << (uint32_t)(1075ull - e); - if ((yb & (bit - 1ull)) != 0ull) return 0; - return (yb & bit) != 0ull ? 1 : 2; -} - -// win10 pow of finite x > 0 and finite y with 2^-63 <= |y| <= 2^63. -ALTD_MATH_FN double win10_pow_core(double x, double y) { - uint64_t xb = dbits(x); - uint64_t yb = dbits(y); - if (yb == 0x3ff0000000000000ull) return x; - if (xb == 0x3ff0000000000000ull) return 1.0; - uint64_t mb = xb; - int32_t ex = (int32_t)(xb >> 52) - 1023; - uint64_t mant = xb & 0x000fffffffffffffull; - if (ex == -1023) { - mb = dbits(dfrom(mant | 0x3ff0000000000000ull) - 1.0); - mant = mb & 0x000fffffffffffffull; - ex = (int32_t)(mb >> 52) - 2045; - } - double e = (double)ex; - uint64_t f1 = (mb & 0x000ff00000000000ull) + ((mb & 0x0000080000000000ull) << 1); - int32_t j = (int32_t)(f1 >> 44); - double d = dfrom(f1 | 0x3fe0000000000000ull) - dfrom(mant | 0x3fe0000000000000ull); - double hiv = d * dfrom(UCRT_POW_T1[j]); - double lov = d * dfrom(UCRT_POW_T2[j]); - double z = hiv + lov; - double err = hiv - z; - double zz = z * z; - err = err + lov; - double p = fma(0.1428571428571429, z, 0.16666666666666666); - p = fma(p, z, 0.2); - p = fma(p, z, 0.25); - p = fma(p, z, 0.3333333333333333); - p = fma(p, z, 0.5); - p = fma(p, zz, err); - double s5 = fma(e, 5.7699990475432854e-08, -p); - double s3 = s5 + dfrom(UCRT_POW_T4[j]); - double s1 = s3; - s3 = s3 - z; - double lg = fma(e, 0.6931471228599548, dfrom(UCRT_POW_T3[j])); - double s7 = lg; - lg = lg + s3; - double lg_hi = dfrom(dbits(lg) & 0xfffffffff8000000ull); - double zs = z + s3; - s7 = s7 - lg; - s1 = s1 - zs; - s7 = s7 + s3; - s7 = s7 + s1; - s7 = s7 + (lg - lg_hi); - double y_hi = dfrom(yb & 0xfffffffff8000000ull); - double y_lo = y - y_hi; - double a3 = y_lo * s7; - a3 = a3 + y_lo * lg_hi; - a3 = a3 + s7 * y_hi; - double a6 = lg_hi * y_hi; - double prod_hi = a6 + a3; - double prod_lo = a6 - prod_hi; - prod_lo = prod_lo + a3; - double w = prod_hi * 92.33248261689366; - if (w > 65536.0) return dfrom(0x7ff0000000000000ull); - if (w < -68800.0) return 0.0; - // Round to nearest even: |w| <= 68800 is far below 2^51. - int32_t n = (int32_t)(w + 6755399441055744.0 - 6755399441055744.0); - double nd = (double)n; - double r0 = fma(-nd, 0.010830424260348082, prod_hi); - int32_t jj = n & 63; - int32_t k = (n - jj) >> 6; - double r = r0 + nd * -4.359010638708991e-10; - r = r + prod_lo; - double q = fma(0.001388888888888889, r, 0.008333333333333333); - q = fma(q, r, 0.041666666666666664); - q = fma(q, r, 0.16666666666666666); - q = fma(q, r, 0.5); - q = fma(q, r, 1.0); - q = q * r; - double lo = q * dfrom(UCRT_EXP_LO[jj]); - double u = q * dfrom(UCRT_EXP_HI[jj]); - lo = lo + dfrom(UCRT_EXP_LO[jj]); - u = u + lo; - u = u + dfrom(UCRT_EXP_HI[jj]); - uint64_t scale = (uint64_t)(int64_t)(k + 1023) << 52; - if (k <= -1022) { - if (k == -1022 && u >= 1.0) return u * dfrom(scale); - if ((int64_t)dbits(prod_hi) > (int64_t)0xc0874046dfefd9d0ull) return dfrom(1ull); - int32_t sh = k + 1074 > 0 ? k + 1074 : 0; - return u * dfrom(1ull << (uint32_t)(sh & 63)); - } - if (k + 1023 == 2047) { - if (u >= 1.0) return dfrom(0x7ff0000000000000ull); - return dfrom(dbits(u) | 0x7fe0000000000000ull); - } - return u * dfrom(scale); -} - -ALTD_MATH_FN double win10_pow(double x, double y) { - uint64_t xb = dbits(x); - uint64_t yb = dbits(y); - bool xnan = win_isnan(x); - bool ynan = win_isnan(y); - // The NaN operand, or of two the first unless only it has the sign bit set. - double qnan = (xnan && (!ynan || (xb >> 63) == 0ull || (yb >> 63) != 0ull)) ? win_quiet(x) : win_quiet(y); - // A signalling NaN is invalid even where C99 gives 1. - if ((xnan && (xb & 0x0008000000000000ull) == 0ull) || (ynan && (yb & 0x0008000000000000ull) == 0ull)) return qnan; - if ((yb & 0x7fffffffffffffffull) == 0ull || xb == 0x3ff0000000000000ull) return 1.0; - if (xnan || ynan) return qnan; - bool xneg = (xb >> 63) != 0ull; - uint64_t axb = xb & 0x7fffffffffffffffull; - bool yinf = (yb & 0x7fffffffffffffffull) == 0x7ff0000000000000ull; - int32_t kind = win_integer_kind(yb); - // pow(-1, y) is 1 for infinite y and for every even y. - if (axb == 0x3ff0000000000000ull && (yinf || kind == 2)) return 1.0; - bool ypos = (yb >> 63) == 0ull; - if (yinf) return (axb < 0x3ff0000000000000ull) == ypos ? 0.0 : dfrom(0x7ff0000000000000ull); - uint64_t sign = xneg && kind == 1 ? 0x8000000000000000ull : 0ull; - if (axb == 0ull) return dfrom((ypos ? 0ull : 0x7ff0000000000000ull) | sign); - if (axb == 0x7ff0000000000000ull) return dfrom((ypos ? 0x7ff0000000000000ull : 0ull) | sign); - if (xneg && kind == 0) return dfrom(0xfff8000000000000ull); - uint64_t ye = yb & 0x7ff0000000000000ull; - // |y| beyond 2^63 is an even integer. - if (ye > 0x43e0000000000000ull) return (axb < 0x3ff0000000000000ull) == ypos ? 0.0 : dfrom(0x7ff0000000000000ull); - if (ye < 0x3c00000000000000ull) return dfrom(dbits(y + 1.0) | sign); - return dfrom(dbits(win10_pow_core(dfrom(axb), y)) | sign); -} - -// win11 pow is Arm's optimized-routines pow (N = 128 log, N = 256 exp) with FMA, plus two -// shortcuts: pow(x, 1) = x and pow(+-1, y) = +-1. -ALTD_MATH_FN double win11_pow(double x, double y) { - uint64_t ix = dbits(x); - uint64_t iy = dbits(y); - uint32_t topx = (uint32_t)(ix >> 52); - uint32_t topy = (uint32_t)(iy >> 52); - uint64_t sign_bias = 0ull; - if (topx - 1u >= 0x7feu || (topy & 0x7ffu) - 0x3beu >= 0x80u) { - // Special y: zero, infinite or NaN. - if (iy * 2ull - 1ull >= 0xffdfffffffffffffull) { - if (iy * 2ull == 0ull) return (ix ^ 0x0008000000000000ull) * 2ull > 0xfff0000000000000ull ? win_nan_operand(x, y) : 1.0; - if (ix == 0x3ff0000000000000ull) return (iy ^ 0x0008000000000000ull) * 2ull > 0xfff0000000000000ull ? win_nan_operand(x, y) : 1.0; - if (ix * 2ull > 0xffe0000000000000ull || iy * 2ull > 0xffe0000000000000ull) return win_nan_operand(x, y); - if (ix * 2ull == 0x7fe0000000000000ull) return 1.0; - if ((ix * 2ull < 0x7fe0000000000000ull) == ((iy >> 63) == 0ull)) return 0.0; - return y * y; - } - // Special x: zero, infinite or NaN. - if (ix * 2ull - 1ull >= 0xffdfffffffffffffull) { - uint64_t x2 = win_isnan(x) ? dbits(win_quiet(x)) : dbits(x * x); - uint64_t neg = 0ull; - if ((ix >> 63) != 0ull && win_integer_kind(iy) == 1) neg = 0x8000000000000000ull; - x2 = x2 ^ neg; - if ((iy >> 63) == 0ull || win_isnan(x)) return dfrom(x2); - if (ix * 2ull == 0ull) return dfrom(0x7ff0000000000000ull | neg); - return 1.0 / dfrom(x2); - } - // Finite x < 0: y must be an integer. - if ((ix >> 63) != 0ull) { - int32_t kind = win_integer_kind(iy); - if (kind == 0) return dfrom(0xfff8000000000000ull); - if (kind == 1) sign_bias = 0x80000ull; - ix = ix & 0x7fffffffffffffffull; - topx = topx & 0x7ffu; - } - // |y| outside [2^-65, 2^63]. - if ((topy & 0x7ffu) - 0x3beu >= 0x80u) { - if (ix == 0x3ff0000000000000ull) return 1.0; - if ((topy & 0x7ffu) < 0x3beu) return ix > 0x3ff0000000000000ull ? y + 1.0 : 1.0 - y; - return (ix > 0x3ff0000000000000ull) == (topy < 0x800u) ? dfrom(0x7ff0000000000000ull) : 0.0; - } - // Subnormal x. - if (topx == 0u) ix = (dbits(x * 4503599627370496.0) & 0x7fffffffffffffffull) - 0x0340000000000000ull; - } - if (iy == 0x3ff0000000000000ull) return x; - if (ix == 0x3ff0000000000000ull) return sign_bias != 0ull ? -1.0 : 1.0; - - // log(x) as hi + lo. - uint64_t tmp = ix - 0x3fe6955500000000ull; - int32_t i = (int32_t)((tmp >> 45) & 127ull); - double kd = (double)(int32_t)((int64_t)tmp >> 52); - double z = dfrom(ix - (tmp & 0xfff0000000000000ull)); - double r = fma(dfrom(UCRT_POW11_INVC[i]), z, -1.0); - double t1 = kd * 0.6931471805598903 + dfrom(UCRT_POW11_LOGC[i]); - double ar = r * -0.5; - double ar2 = ar * r; - double lo3 = fma(ar, r, -ar2); - double lo1 = kd * 5.497923018708371e-14 + dfrom(UCRT_POW11_LOGCTAIL[i]); - double t2 = t1 + r; - double lo2 = t1 - t2 + r; - double lo = lo3 + (lo1 + lo2); - double hi = ar2 + t2; - double lo4 = t2 - hi + ar2; - lo = lo + lo4; - double p = (0.7999999995323976 - r * 0.6666666663487739) + (r * 1.0000415263675542 - 1.142909628459501) * ar2; - p = p * ar2 + (r * 0.5000000000000007 - 0.6666666666666679); - p = p * (ar2 * r); - lo = lo + p; - double lhi = lo + hi; - double ltail = hi - lhi + lo; - - // exp(y * log(x)) with the tail ehi + elo. - double ehi = lhi * y; - double elo = fma(y, lhi, -ehi) + ltail * y; - uint32_t abstop = (uint32_t)(dbits(ehi) >> 52) & 0x7ffu; - if (abstop - 0x3c9u >= 0x3fu) { - if (abstop - 0x3c9u >= 0x80000000u) { - double one = ehi + 1.0; - return sign_bias != 0ull ? -one : one; - } - if (abstop >= 0x409u) { - uint64_t s = sign_bias != 0ull ? 0x8000000000000000ull : 0ull; - return dfrom(((dbits(ehi) >> 63) != 0ull ? 0ull : 0x7ff0000000000000ull) | s); - } - abstop = 0u; - } - double kd2 = ehi * 369.3299304675746 + 103079215104.5; - uint64_t ki = dbits(kd2) >> 16; - double kn = (double)(int32_t)(uint32_t)ki; - double er = ehi - kn * 0.0027076061741126978 + kn * 5.0411407304988844e-14 + elo; - int32_t idx = 2 * (int32_t)(ki & 255ull); - uint64_t sbits = UCRT_POW11_EXP[idx + 1] + ((ki + sign_bias) << 44); - double er2 = er * er; - double e = er2 * (er * 0.16666666666665886 + 0.49999999999996786) + (er + dfrom(UCRT_POW11_EXP[idx])); - e = e + er2 * er2 * (er * 0.008333335853047663 + 0.04166668084093659); - if (abstop != 0u) return e * dfrom(sbits) + dfrom(sbits); - // The result may be subnormal or overflow. - if ((ki & 0x80000000ull) == 0ull) { - double scale = dfrom(sbits - 0x3f10000000000000ull); - return (e * scale + scale) * 5.486124068793689e+303; - } - uint64_t sb = sbits + 0x3fe0000000000000ull; - double scale = dfrom(sb); - double se = scale * e; - double v = scale + se; - if ((dbits(v) & 0x7fffffffffffffffull) < 0x3ff0000000000000ull) { - double one = v < 0.0 ? -1.0 : 1.0; - double h = one + v; - double l = (scale - v + se) + (one - h + v); - v = l + h - one; - if (v == 0.0) v = dfrom(sb & 0x8000000000000000ull); - } - return v * 2.2250738585072014e-308; -} - -#ifndef ALTD_MATH_WGSL -// ---- log (CPU only) ------------------------------------------------------------------------- - -// AMD's libm log, the same in both builds. -ALTD_MATH_FN double win_log(double x) { - uint64_t ux = dbits(x); - if ((ux & 0x7ff0000000000000ull) == 0x7ff0000000000000ull) { - if (ux == 0x7ff0000000000000ull) return x; - if (ux == 0xfff0000000000000ull) return dfrom(0xfff8000000000000ull); - return win_quiet(x); - } - if (!(x > 0.0)) return x == 0.0 ? dfrom(0xfff0000000000000ull) : dfrom(0xfff8000000000000ull); - double xm1 = x - 1.0; - if ((dbits(xm1) & 0x7fffffffffffffffull) < 0x3fb0000000000000ull) { - double u = xm1 / (2.0 + xm1); - double ru = xm1 * u; - double u2 = u + u; - double v = u2 * u2; - double a = fma(0.012500000003771751, v, 0.08333333333333179); - double b = fma(0.0004348877777076146, v, 0.0022321399879194482); - double w = v * u2; - a = a * w; - double w3 = w * w * u2; - a = fma(b, w3, a) - ru; - return xm1 + a; - } - int32_t ex = (int32_t)(ux >> 52) - 1023; - uint64_t mb = ux; - if (ex == -1023) { - mb = dbits(dfrom((ux & 0x000fffffffffffffull) | 0x3ff0000000000000ull) - 1.0); - ex = (int32_t)(mb >> 52) - 2045; - } - double e = (double)ex; - uint64_t f1 = (mb & 0x000ff00000000000ull) + ((mb & 0x0000080000000000ull) << 1); - int32_t j = (int32_t)(f1 >> 44); - double u = (dfrom(f1 | 0x3fe0000000000000ull) - dfrom((mb & 0x000fffffffffffffull) | 0x3fe0000000000000ull)) * dfrom(UCRT_LOG_INV[j]); - double u2 = u * u; - double p3 = fma(0.16666666666666666, u, 0.2); - p3 = fma(p3, u, 0.25); - double s = fma(fma(0.3333333333333333, u, 0.5), u2, u); - s = fma(p3, u2 * u2, s); - double x5 = fma(5.7699990475432854e-08, e, -s); - return fma(e, 0.6931471228599548, dfrom(UCRT_LOG_LEAD[j])) + (dfrom(UCRT_LOG_TAIL[j]) + x5); -} - -// ---- sinf, cosf (CPU, HIP and CUDA) --------------------------------------------------------- - -// The genuine functions are musl's (src/math/native_math.rs, gpu/sim/math.h) except for the -// inputs in the exception tables. `musl` is musl's result for x. -ALTD_MATH_CONST uint32_t WIN_TRIG_LIMIT = 0x4dc90fdbu; - -// For |x| < WIN_TRIG_LIMIT. -ALTD_MATH_FN float win_sinf_fix(float x, float musl) { - uint32_t key = fbits(x) & 0x7fffffffu; - int32_t lo = 0; - for (int32_t step = UCRT_SINF_STEP; step > 0; step = step >> 1) { - int32_t probe = lo + step; - if (probe < UCRT_SINF_COUNT && UCRT_SINF_KEYS[probe] <= key) lo = probe; - } - if (UCRT_SINF_KEYS[lo] != key) return musl; - return ffrom(UCRT_SINF_VALUES[lo] ^ (fbits(x) & 0x80000000u)); -} -ALTD_MATH_FN float win_cosf_fix(float x, float musl) { - uint32_t key = fbits(x) & 0x7fffffffu; - int32_t lo = 0; - for (int32_t step = UCRT_COSF_STEP; step > 0; step = step >> 1) { - int32_t probe = lo + step; - if (probe < UCRT_COSF_COUNT && UCRT_COSF_KEYS[probe] <= key) lo = probe; - } - if (UCRT_COSF_KEYS[lo] != key) return musl; - return ffrom(UCRT_COSF_VALUES[lo]); -} - -// Every x. -ALTD_MATH_FN float win_sinf_fix_all(float x, float musl) { - uint32_t key = fbits(x) & 0x7fffffffu; - if (key < WIN_TRIG_LIMIT) return win_sinf_fix(x, musl); - int32_t lo = 0; - for (int32_t step = UCRT_SINF_WIDE_STEP; step > 0; step = step >> 1) { - int32_t probe = lo + step; - if (probe < UCRT_SINF_WIDE_COUNT && UCRT_SINF_WIDE_KEYS[probe] <= key) lo = probe; - } - if (UCRT_SINF_WIDE_KEYS[lo] != key) return musl; - return ffrom(UCRT_SINF_WIDE_VALUES[lo] ^ (fbits(x) & 0x80000000u)); -} -ALTD_MATH_FN float win_cosf_fix_all(float x, float musl) { - uint32_t key = fbits(x) & 0x7fffffffu; - if (key < WIN_TRIG_LIMIT) return win_cosf_fix(x, musl); - int32_t lo = 0; - for (int32_t step = UCRT_COSF_WIDE_STEP; step > 0; step = step >> 1) { - int32_t probe = lo + step; - if (probe < UCRT_COSF_WIDE_COUNT && UCRT_COSF_WIDE_KEYS[probe] <= key) lo = probe; - } - if (UCRT_COSF_WIDE_KEYS[lo] != key) return musl; - return ffrom(UCRT_COSF_WIDE_VALUES[lo]); -} -#endif diff --git a/github-simulator/source/math/kernels/ucrt_tables.h b/github-simulator/source/math/kernels/ucrt_tables.h deleted file mode 100644 index 5986ddac9b4d25d24f6c50996c0b8e16bba1c7c8..0000000000000000000000000000000000000000 --- a/github-simulator/source/math/kernels/ucrt_tables.h +++ /dev/null @@ -1,3030 +0,0 @@ -// Generated by tools/ucrt/tables.py from ucrtbase.dll 10.0.22621.7510 and 10.0.26100.9444. Do not edit. -// Bit patterns; math/kernels/ucrt.h documents their use. -#pragma once -// atan2f: atan(n / 256) for n = 16..256 -ALTD_MATH_TABLE uint64_t UCRT_ATAN2F_ATAN256[241] = { - 0x3faff55bb72cfde9ull, 0x3fb0f99ea71d52a6ull, 0x3fb1f86dbf082d58ull, 0x3fb2f719318a4a9aull, - 0x3fb3f59f0e7c559dull, 0x3fb4f3fd677292fbull, 0x3fb5f2324fd2d7b2ull, 0x3fb6f03bdcea4b0cull, - 0x3fb7ee182602f10eull, 0x3fb8ebc54478fb28ull, 0x3fb9e94153cfdcf1ull, 0x3fbae68a71c722b8ull, - 0x3fbbe39ebe6f07c3ull, 0x3fbce07c5c3cca32ull, 0x3fbddd21701eba6eull, 0x3fbed98c2190043aull, - 0x3fbfd5ba9aac2f6dull, 0x3fc068d584212b3dull, 0x3fc0e6adccf40881ull, 0x3fc1646541060850ull, - 0x3fc1e1fafb043726ull, 0x3fc25f6e171a535cull, 0x3fc2dcbdb2fba1ffull, 0x3fc359e8edeb99a3ull, - 0x3fc3d6eee8c6626cull, 0x3fc453cec6092a9eull, 0x3fc4d087a9da4f17ull, 0x3fc54d18ba11570aull, - 0x3fc5c9811e3ec269ull, 0x3fc645bfffb3aa73ull, 0x3fc6c1d4898933d8ull, 0x3fc73dbde8a7d201ull, - 0x3fc7b97b4bce5b02ull, 0x3fc8350be398ebc7ull, 0x3fc8b06ee2879c28ull, 0x3fc92ba37d050271ull, - 0x3fc9a6a8e96c8626ull, 0x3fca217e601081a5ull, 0x3fca9c231b403279ull, 0x3fcb1696574d780bull, - 0x3fcb90d7529260a2ull, 0x3fcc0ae54d768466ull, 0x3fcc84bf8a742e6dull, 0x3fccfe654e1d5395ull, - 0x3fcd77d5df205736ull, 0x3fcdf110864c9d9dull, 0x3fce6a148e96ec4dull, 0x3fcee2e1451d980cull, - 0x3fcf5b75f92c80ddull, 0x3fcfd3d1fc40dbe4ull, 0x3fd025fa510665b5ull, 0x3fd061eea03d6290ull, - 0x3fd09dc597d86362ull, 0x3fd0d97ee509acb3ull, 0x3fd1151a362431c9ull, 0x3fd150973a9ce546ull, - 0x3fd18bf5a30bf178ull, 0x3fd1c735212dd883ull, 0x3fd2025567e47c95ull, 0x3fd23d562b381041ull, - 0x3fd278372057ef45ull, 0x3fd2b2f7fd9b5fe2ull, 0x3fd2ed987a823cfeull, 0x3fd328184fb58951ull, - 0x3fd362773707ebcbull, 0x3fd39cb4eb76157bull, 0x3fd3d6d129271134ull, 0x3fd410cbad6c7d32ull, - 0x3fd44aa436c2af09ull, 0x3fd4845a84d0c21bull, 0x3fd4bdee586890e6ull, 0x3fd4f75f73869978ull, - 0x3fd530ad9951cd49ull, 0x3fd569d88e1b4cd7ull, 0x3fd5a2e0175e0f4eull, 0x3fd5dbc3fbbe768dull, - 0x3fd614840309cfe1ull, 0x3fd64d1ff635c1c5ull, 0x3fd685979f5fa6fdull, 0x3fd6bdeac9cbd76cull, - 0x3fd6f61941e4def0ull, 0x3fd72e22d53aa2a9ull, 0x3fd7660752817501ull, 0x3fd79dc6899118d1ull, - 0x3fd7d5604b63b3f7ull, 0x3fd80cd46a14b1d0ull, 0x3fd84422b8df95d7ull, 0x3fd87b4b0c1ebedbull, - 0x3fd8b24d394a1b25ull, 0x3fd8e92916f5cde8ull, 0x3fd91fde7cd0c662ull, 0x3fd9566d43a34907ull, - 0x3fd98cd5454d6b18ull, 0x3fd9c3165cc58107ull, 0x3fd9f93066168001ull, 0x3fda2f233e5e530bull, - 0x3fda64eec3cc23fcull, 0x3fda9a92d59e98cfull, 0x3fdad00f5422058bull, 0x3fdb056420ae9343ull, - 0x3fdb3a911da65c6cull, 0x3fdb6f962e737efbull, 0x3fdba473378624a5ull, 0x3fdbd9281e528191ull, - 0x3fdc0db4c94ec9efull, 0x3fdc42191ff11eb6ull, 0x3fdc76550aad71f8ull, 0x3fdcaa6872f3631bull, - 0x3fdcde53432c1350ull, 0x3fdd121566b7f2adull, 0x3fdd45aec9ec862bull, 0x3fdd791f5a1226f4ull, - 0x3fddac670561bb4full, 0x3fdddf85bb026974ull, 0x3fde127b6b0744afull, 0x3fde4548066cf51aull, - 0x3fde77eb7f175a34ull, 0x3fdeaa65c7cf28c4ull, 0x3fdedcb6d43f8434ull, 0x3fdf0ede98f393cfull, - 0x3fdf40dd0b541417ull, 0x3fdf72b221a4e495ull, 0x3fdfa45dd3029258ull, 0x3fdfd5e0175fdf83ull, - 0x3fe0039c73c1a40bull, 0x3fe01c341e82422dull, 0x3fe034b709250488ull, 0x3fe04d25314342e5ull, - 0x3fe0657e94db30cfull, 0x3fe07dc3324e9b38ull, 0x3fe095f30861a58full, 0x3fe0ae0e1639866cull, - 0x3fe0c6145b5b43daull, 0x3fe0de05d7aa6f7cull, 0x3fe0f5e28b67e295ull, 0x3fe10daa77307a0dull, - 0x3fe1255d9bfbd2a8ull, 0x3fe13cfbfb1b056eull, 0x3fe1548596376469ull, 0x3fe16bfa6f5137e1ull, - 0x3fe1835a88be7c13ull, 0x3fe19aa5e5299f99ull, 0x3fe1b1dc87904284ull, 0x3fe1c8fe7341f64full, - 0x3fe1e00babdefeb3ull, 0x3fe1f7043557138aull, 0x3fe20de813e823b1ull, 0x3fe224b74c1d192aull, - 0x3fe23b71e2cc9e6aull, 0x3fe25217dd17e501ull, 0x3fe268a940696da6ull, 0x3fe27f261273d1b3ull, - 0x3fe2958e59308e30ull, 0x3fe2abe21aded073ull, 0x3fe2c2215e024465ull, 0x3fe2d84c2961e48bull, - 0x3fe2ee628406cbcaull, 0x3fe30464753b090aull, 0x3fe31a52048874beull, 0x3fe3302b39b78856ull, - 0x3fe345f01cce37bbull, 0x3fe35ba0b60eccceull, 0x3fe3713d0df6c503ull, 0x3fe386c52d3db11eull, - 0x3fe39c391cd41719ull, 0x3fe3b198e5e2564aull, 0x3fe3c6e491c78dc4ull, 0x3fe3dc1c2a188504ull, - 0x3fe3f13fb89e96f4ull, 0x3fe4064f47569f48ull, 0x3fe41b4ae06fea41ull, 0x3fe430328e4b26d5ull, - 0x3fe445065b795b55ull, 0x3fe459c652badc7full, 0x3fe46e727efe4715ull, 0x3fe4830aeb5f7bfdull, - 0x3fe4978fa3269ee1ull, 0x3fe4ac00b1c71762ull, 0x3fe4c05e22de94e4ull, 0x3fe4d4a8023414e8ull, - 0x3fe4e8de5bb6ec04ull, 0x3fe4fd013b7dd17eull, 0x3fe51110adc5ed81ull, 0x3fe5250cbef1e9faull, - 0x3fe538f57b89061eull, 0x3fe54ccaf0362c8full, 0x3fe5608d29c70c34ull, 0x3fe5743c352b33b9ull, - 0x3fe587d81f732fbaull, 0x3fe59b60f5cfab9dull, 0x3fe5aed6c5909517ull, 0x3fe5c2399c244260ull, - 0x3fe5d58987169b18ull, 0x3fe5e8c6941043cfull, 0x3fe5fbf0d0d5cc49ull, 0x3fe60f084b46e05eull, - 0x3fe6220d115d7b8dull, 0x3fe634ff312d1f3bull, 0x3fe647deb8e20b8full, 0x3fe65aabb6c07b02ull, - 0x3fe66d663923e086ull, 0x3fe6800e4e7e2857ull, 0x3fe692a40556fb6aull, 0x3fe6a5276c4b0575ull, - 0x3fe6b798920b3d98ull, 0x3fe6c9f7855c3198ull, 0x3fe6dc44551553aeull, 0x3fe6ee7f10204aefull, - 0x3fe700a7c5784633ull, 0x3fe712be84295198ull, 0x3fe724c35b4fae7bull, 0x3fe736b65a172dffull, - 0x3fe748978fba8e0full, 0x3fe75a670b82d8d8ull, 0x3fe76c24dcc6c6c0ull, 0x3fe77dd112ea22c7ull, - 0x3fe78f6bbd5d315eull, 0x3fe7a0f4eb9c19a2ull, 0x3fe7b26cad2e50fdull, 0x3fe7c3d311a6092bull, - 0x3fe7d528289fa093ull, 0x3fe7e66c01c114fdull, 0x3fe7f79eacb97898ull, 0x3fe808c03940694aull, - 0x3fe819d0b7158a4cull, 0x3fe82ad036000005ull, 0x3fe83bbec5cdee22ull, 0x3fe84c9c7653f7eaull, - 0x3fe85d69576cc2c5ull, 0x3fe86e2578f87ae5ull, 0x3fe87ed0eadc5a2aull, 0x3fe88f6bbd023118ull, - 0x3fe89ff5ff57f1f7ull, 0x3fe8b06fc1cf3dfeull, 0x3fe8c0d9145cf49dull, 0x3fe8d13206f8c4caull, - 0x3fe8e17aa99cc05dull, 0x3fe8f1b30c44f167ull, 0x3fe901db3eeef187ull, 0x3fe911f35199833bull, - 0x3fe921fb54442d18ull, -}; -// tanh: 2^(j/32) split in high and low parts -ALTD_MATH_TABLE uint64_t UCRT_TANH_HI[32] = { - 0x3ff0000000000000ull, 0x3ff059b0d0000000ull, 0x3ff0b55860000000ull, 0x3ff11301d0000000ull, - 0x3ff172b830000000ull, 0x3ff1d48730000000ull, 0x3ff2387a60000000ull, 0x3ff29e9df0000000ull, - 0x3ff306fe00000000ull, 0x3ff371a730000000ull, 0x3ff3dea640000000ull, 0x3ff44e0860000000ull, - 0x3ff4bfdad0000000ull, 0x3ff5342b50000000ull, 0x3ff5ab07d0000000ull, 0x3ff6247eb0000000ull, - 0x3ff6a09e60000000ull, 0x3ff71f75e0000000ull, 0x3ff7a11470000000ull, 0x3ff8258990000000ull, - 0x3ff8ace540000000ull, 0x3ff93737b0000000ull, 0x3ff9c49180000000ull, 0x3ffa5503b0000000ull, - 0x3ffae89f90000000ull, 0x3ffb7f76f0000000ull, 0x3ffc199bd0000000ull, 0x3ffcb720d0000000ull, - 0x3ffd5818d0000000ull, 0x3ffdfc9730000000ull, 0x3ffea4afa0000000ull, 0x3fff507650000000ull, -}; -ALTD_MATH_TABLE uint64_t UCRT_TANH_LO[32] = { - 0x0000000000000000ull, 0x3e48ac2ba1d73e2aull, 0x3e69f3121ec53172ull, 0x3df25b50a4ebbf1bull, - 0x3e68faa2f5b9bef9ull, 0x3e368b9aa7805b80ull, 0x3e6ceac470cd83f6ull, 0x3e547f7b84b09745ull, - 0x3e64636e2a5bd1abull, 0x3e5ceaa72a9c5154ull, 0x3e682468446b6824ull, 0x3e18624b40c4dbd0ull, - 0x3e54d8a89c750e5eull, 0x3e5a753e077c2a0full, 0x3e6a90a852b19260ull, 0x3e0d2ac258f87d03ull, - 0x3e59fcef32422cbfull, 0x3e61d8bee7ba46e2ull, 0x3e4f580c36bea881ull, 0x3e62999c25159f11ull, - 0x3e415506dadd3e2aull, 0x3e29b8bc9e8a0388ull, 0x3e451f8480e3e236ull, 0x3e41f12ae45a1224ull, - 0x3e62b5a75abd0e6aull, 0x3e47daf237553d84ull, 0x3e6b0aa538444196ull, 0x3e69df20d22a0798ull, - 0x3e69f7490e4bb40bull, 0x3e4bdcdaf5cb4656ull, 0x3e452486cc2c7b9dull, 0x3e66dc8a80ce9f09ull, -}; -// exp and the win10 pow: 2^(j/64) as a multiplier and low and high parts -ALTD_MATH_TABLE uint64_t UCRT_EXP_MUL[64] = { - 0x3ff0000000000000ull, 0x3ff02c9a3e778061ull, 0x3ff059b0d3158574ull, 0x3ff0874518759bc8ull, - 0x3ff0b5586cf9890full, 0x3ff0e3ec32d3d1a2ull, 0x3ff11301d0125b51ull, 0x3ff1429aaea92de0ull, - 0x3ff172b83c7d517bull, 0x3ff1a35beb6fcb75ull, 0x3ff1d4873168b9aaull, 0x3ff2063b88628cd6ull, - 0x3ff2387a6e756238ull, 0x3ff26b4565e27cddull, 0x3ff29e9df51fdee1ull, 0x3ff2d285a6e4030bull, - 0x3ff306fe0a31b715ull, 0x3ff33c08b26416ffull, 0x3ff371a7373aa9cbull, 0x3ff3a7db34e59ff7ull, - 0x3ff3dea64c123422ull, 0x3ff4160a21f72e2aull, 0x3ff44e086061892dull, 0x3ff486a2b5c13cd0ull, - 0x3ff4bfdad5362a27ull, 0x3ff4f9b2769d2ca7ull, 0x3ff5342b569d4f82ull, 0x3ff56f4736b527daull, - 0x3ff5ab07dd485429ull, 0x3ff5e76f15ad2148ull, 0x3ff6247eb03a5585ull, 0x3ff6623882552225ull, - 0x3ff6a09e667f3bcdull, 0x3ff6dfb23c651a2full, 0x3ff71f75e8ec5f74ull, 0x3ff75feb564267c9ull, - 0x3ff7a11473eb0187ull, 0x3ff7e2f336cf4e62ull, 0x3ff82589994cce13ull, 0x3ff868d99b4492edull, - 0x3ff8ace5422aa0dbull, 0x3ff8f1ae99157736ull, 0x3ff93737b0cdc5e5ull, 0x3ff97d829fde4e50ull, - 0x3ff9c49182a3f090ull, 0x3ffa0c667b5de565ull, 0x3ffa5503b23e255dull, 0x3ffa9e6b5579fdbfull, - 0x3ffae89f995ad3adull, 0x3ffb33a2b84f15fbull, 0x3ffb7f76f2fb5e47ull, 0x3ffbcc1e904bc1d2ull, - 0x3ffc199bdd85529cull, 0x3ffc67f12e57d14bull, 0x3ffcb720dcef9069ull, 0x3ffd072d4a07897cull, - 0x3ffd5818dcfba487ull, 0x3ffda9e603db3285ull, 0x3ffdfc97337b9b5full, 0x3ffe502ee78b3ff6ull, - 0x3ffea4afa2a490daull, 0x3ffefa1bee615a27ull, 0x3fff50765b6e4540ull, 0x3fffa7c1819e90d8ull, -}; -ALTD_MATH_TABLE uint64_t UCRT_EXP_LO[64] = { - 0x0000000000000000ull, 0x3e6cef00c1dcdef9ull, 0x3e48ac2ba1d73e2aull, 0x3e60eb37901186beull, - 0x3e69f3121ec53172ull, 0x3e469e8d10103a17ull, 0x3df25b50a4ebbf1aull, 0x3e6d525bbf668203ull, - 0x3e68faa2f5b9bef9ull, 0x3e66df96ea796d31ull, 0x3e368b9aa7805b80ull, 0x3e60c519ac771dd6ull, - 0x3e6ceac470cd83f5ull, 0x3e5789f37495e99cull, 0x3e547f7b84b09745ull, 0x3e5b900c2d002475ull, - 0x3e64636e2a5bd1abull, 0x3e4320b7fa64e430ull, 0x3e5ceaa72a9c5154ull, 0x3e53967fdba86f24ull, - 0x3e682468446b6824ull, 0x3e3f72e29f84325bull, 0x3e18624b40c4dbd0ull, 0x3e5704f3404f068eull, - 0x3e54d8a89c750e5eull, 0x3e5a74b29ab4cf62ull, 0x3e5a753e077c2a0full, 0x3e5ad49f699bb2c0ull, - 0x3e6a90a852b19260ull, 0x3e56b48521ba6f93ull, 0x3e0d2ac258f87d03ull, 0x3e42a91124893ecfull, - 0x3e59fcef32422cbeull, 0x3e68ca345de441c5ull, 0x3e61d8bee7ba46e1ull, 0x3e59099f22fdba6aull, - 0x3e4f580c36bea881ull, 0x3e5b3d398841740aull, 0x3e62999c25159f11ull, 0x3e668925d901c83bull, - 0x3e415506dadd3e2aull, 0x3e622aee6c57304eull, 0x3e29b8bc9e8a0387ull, 0x3e6fbc9c9f173d24ull, - 0x3e451f8480e3e235ull, 0x3e66bbcac96535b5ull, 0x3e41f12ae45a1224ull, 0x3e55e7f6fd0fac90ull, - 0x3e62b5a75abd0e69ull, 0x3e609e2bf5ed7fa1ull, 0x3e47daf237553d84ull, 0x3e12f074891ee83dull, - 0x3e6b0aa538444196ull, 0x3e6cafa29694426full, 0x3e69df20d22a0797ull, 0x3e640f12f71a1e45ull, - 0x3e69f7490e4bb40bull, 0x3e4ed9942b84600dull, 0x3e4bdcdaf5cb4656ull, 0x3e5e2cffd89cf44cull, - 0x3e452486cc2c7b9dull, 0x3e6cc2b44eee3fa4ull, 0x3e66dc8a80ce9f09ull, 0x3e39e90d82e90a7eull, -}; -ALTD_MATH_TABLE uint64_t UCRT_EXP_HI[64] = { - 0x3ff0000000000000ull, 0x3ff02c9a30000000ull, 0x3ff059b0d0000000ull, 0x3ff0874510000000ull, - 0x3ff0b55860000000ull, 0x3ff0e3ec30000000ull, 0x3ff11301d0000000ull, 0x3ff1429aa0000000ull, - 0x3ff172b830000000ull, 0x3ff1a35be0000000ull, 0x3ff1d48730000000ull, 0x3ff2063b80000000ull, - 0x3ff2387a60000000ull, 0x3ff26b4560000000ull, 0x3ff29e9df0000000ull, 0x3ff2d285a0000000ull, - 0x3ff306fe00000000ull, 0x3ff33c08b0000000ull, 0x3ff371a730000000ull, 0x3ff3a7db30000000ull, - 0x3ff3dea640000000ull, 0x3ff4160a20000000ull, 0x3ff44e0860000000ull, 0x3ff486a2b0000000ull, - 0x3ff4bfdad0000000ull, 0x3ff4f9b270000000ull, 0x3ff5342b50000000ull, 0x3ff56f4730000000ull, - 0x3ff5ab07d0000000ull, 0x3ff5e76f10000000ull, 0x3ff6247eb0000000ull, 0x3ff6623880000000ull, - 0x3ff6a09e60000000ull, 0x3ff6dfb230000000ull, 0x3ff71f75e0000000ull, 0x3ff75feb50000000ull, - 0x3ff7a11470000000ull, 0x3ff7e2f330000000ull, 0x3ff8258990000000ull, 0x3ff868d990000000ull, - 0x3ff8ace540000000ull, 0x3ff8f1ae90000000ull, 0x3ff93737b0000000ull, 0x3ff97d8290000000ull, - 0x3ff9c49180000000ull, 0x3ffa0c6670000000ull, 0x3ffa5503b0000000ull, 0x3ffa9e6b50000000ull, - 0x3ffae89f90000000ull, 0x3ffb33a2b0000000ull, 0x3ffb7f76f0000000ull, 0x3ffbcc1e90000000ull, - 0x3ffc199bd0000000ull, 0x3ffc67f120000000ull, 0x3ffcb720d0000000ull, 0x3ffd072d40000000ull, - 0x3ffd5818d0000000ull, 0x3ffda9e600000000ull, 0x3ffdfc9730000000ull, 0x3ffe502ee0000000ull, - 0x3ffea4afa0000000ull, 0x3ffefa1be0000000ull, 0x3fff507650000000ull, 0x3fffa7c180000000ull, -}; -// win10 pow: logarithm tables over 257 mantissa intervals -ALTD_MATH_TABLE uint64_t UCRT_POW_T1[257] = { - 0x4000000000000000ull, 0x3fffe00000000000ull, 0x3fffc00000000000ull, 0x3fffa00000000000ull, - 0x3fff800000000000ull, 0x3fff600000000000ull, 0x3fff400000000000ull, 0x3fff200000000000ull, - 0x3fff000000000000ull, 0x3ffee00000000000ull, 0x3ffec00000000000ull, 0x3ffea00000000000ull, - 0x3ffe900000000000ull, 0x3ffe700000000000ull, 0x3ffe500000000000ull, 0x3ffe300000000000ull, - 0x3ffe100000000000ull, 0x3ffe000000000000ull, 0x3ffde00000000000ull, 0x3ffdc00000000000ull, - 0x3ffda00000000000ull, 0x3ffd900000000000ull, 0x3ffd700000000000ull, 0x3ffd500000000000ull, - 0x3ffd400000000000ull, 0x3ffd200000000000ull, 0x3ffd000000000000ull, 0x3ffcf00000000000ull, - 0x3ffcd00000000000ull, 0x3ffcb00000000000ull, 0x3ffca00000000000ull, 0x3ffc800000000000ull, - 0x3ffc700000000000ull, 0x3ffc500000000000ull, 0x3ffc300000000000ull, 0x3ffc200000000000ull, - 0x3ffc000000000000ull, 0x3ffbf00000000000ull, 0x3ffbd00000000000ull, 0x3ffbc00000000000ull, - 0x3ffba00000000000ull, 0x3ffb900000000000ull, 0x3ffb700000000000ull, 0x3ffb600000000000ull, - 0x3ffb400000000000ull, 0x3ffb300000000000ull, 0x3ffb200000000000ull, 0x3ffb000000000000ull, - 0x3ffaf00000000000ull, 0x3ffad00000000000ull, 0x3ffac00000000000ull, 0x3ffaa00000000000ull, - 0x3ffa900000000000ull, 0x3ffa800000000000ull, 0x3ffa600000000000ull, 0x3ffa500000000000ull, - 0x3ffa400000000000ull, 0x3ffa200000000000ull, 0x3ffa100000000000ull, 0x3ffa000000000000ull, - 0x3ff9e00000000000ull, 0x3ff9d00000000000ull, 0x3ff9c00000000000ull, 0x3ff9a00000000000ull, - 0x3ff9900000000000ull, 0x3ff9800000000000ull, 0x3ff9700000000000ull, 0x3ff9500000000000ull, - 0x3ff9400000000000ull, 0x3ff9300000000000ull, 0x3ff9200000000000ull, 0x3ff9000000000000ull, - 0x3ff8f00000000000ull, 0x3ff8e00000000000ull, 0x3ff8d00000000000ull, 0x3ff8b00000000000ull, - 0x3ff8a00000000000ull, 0x3ff8900000000000ull, 0x3ff8800000000000ull, 0x3ff8700000000000ull, - 0x3ff8600000000000ull, 0x3ff8400000000000ull, 0x3ff8300000000000ull, 0x3ff8200000000000ull, - 0x3ff8100000000000ull, 0x3ff8000000000000ull, 0x3ff7f00000000000ull, 0x3ff7e00000000000ull, - 0x3ff7d00000000000ull, 0x3ff7b00000000000ull, 0x3ff7a00000000000ull, 0x3ff7900000000000ull, - 0x3ff7800000000000ull, 0x3ff7700000000000ull, 0x3ff7600000000000ull, 0x3ff7500000000000ull, - 0x3ff7400000000000ull, 0x3ff7300000000000ull, 0x3ff7200000000000ull, 0x3ff7100000000000ull, - 0x3ff7000000000000ull, 0x3ff6f00000000000ull, 0x3ff6e00000000000ull, 0x3ff6d00000000000ull, - 0x3ff6c00000000000ull, 0x3ff6b00000000000ull, 0x3ff6a00000000000ull, 0x3ff6900000000000ull, - 0x3ff6800000000000ull, 0x3ff6700000000000ull, 0x3ff6600000000000ull, 0x3ff6500000000000ull, - 0x3ff6400000000000ull, 0x3ff6300000000000ull, 0x3ff6200000000000ull, 0x3ff6100000000000ull, - 0x3ff6000000000000ull, 0x3ff5f00000000000ull, 0x3ff5e00000000000ull, 0x3ff5d00000000000ull, - 0x3ff5c00000000000ull, 0x3ff5b00000000000ull, 0x3ff5a00000000000ull, 0x3ff5900000000000ull, - 0x3ff5800000000000ull, 0x3ff5800000000000ull, 0x3ff5700000000000ull, 0x3ff5600000000000ull, - 0x3ff5500000000000ull, 0x3ff5400000000000ull, 0x3ff5300000000000ull, 0x3ff5200000000000ull, - 0x3ff5100000000000ull, 0x3ff5000000000000ull, 0x3ff5000000000000ull, 0x3ff4f00000000000ull, - 0x3ff4e00000000000ull, 0x3ff4d00000000000ull, 0x3ff4c00000000000ull, 0x3ff4b00000000000ull, - 0x3ff4a00000000000ull, 0x3ff4a00000000000ull, 0x3ff4900000000000ull, 0x3ff4800000000000ull, - 0x3ff4700000000000ull, 0x3ff4600000000000ull, 0x3ff4600000000000ull, 0x3ff4500000000000ull, - 0x3ff4400000000000ull, 0x3ff4300000000000ull, 0x3ff4200000000000ull, 0x3ff4200000000000ull, - 0x3ff4100000000000ull, 0x3ff4000000000000ull, 0x3ff3f00000000000ull, 0x3ff3e00000000000ull, - 0x3ff3e00000000000ull, 0x3ff3d00000000000ull, 0x3ff3c00000000000ull, 0x3ff3b00000000000ull, - 0x3ff3b00000000000ull, 0x3ff3a00000000000ull, 0x3ff3900000000000ull, 0x3ff3800000000000ull, - 0x3ff3800000000000ull, 0x3ff3700000000000ull, 0x3ff3600000000000ull, 0x3ff3500000000000ull, - 0x3ff3500000000000ull, 0x3ff3400000000000ull, 0x3ff3300000000000ull, 0x3ff3200000000000ull, - 0x3ff3200000000000ull, 0x3ff3100000000000ull, 0x3ff3000000000000ull, 0x3ff3000000000000ull, - 0x3ff2f00000000000ull, 0x3ff2e00000000000ull, 0x3ff2e00000000000ull, 0x3ff2d00000000000ull, - 0x3ff2c00000000000ull, 0x3ff2b00000000000ull, 0x3ff2b00000000000ull, 0x3ff2a00000000000ull, - 0x3ff2900000000000ull, 0x3ff2900000000000ull, 0x3ff2800000000000ull, 0x3ff2700000000000ull, - 0x3ff2700000000000ull, 0x3ff2600000000000ull, 0x3ff2500000000000ull, 0x3ff2500000000000ull, - 0x3ff2400000000000ull, 0x3ff2300000000000ull, 0x3ff2300000000000ull, 0x3ff2200000000000ull, - 0x3ff2100000000000ull, 0x3ff2100000000000ull, 0x3ff2000000000000ull, 0x3ff2000000000000ull, - 0x3ff1f00000000000ull, 0x3ff1e00000000000ull, 0x3ff1e00000000000ull, 0x3ff1d00000000000ull, - 0x3ff1c00000000000ull, 0x3ff1c00000000000ull, 0x3ff1b00000000000ull, 0x3ff1b00000000000ull, - 0x3ff1a00000000000ull, 0x3ff1900000000000ull, 0x3ff1900000000000ull, 0x3ff1800000000000ull, - 0x3ff1800000000000ull, 0x3ff1700000000000ull, 0x3ff1600000000000ull, 0x3ff1600000000000ull, - 0x3ff1500000000000ull, 0x3ff1500000000000ull, 0x3ff1400000000000ull, 0x3ff1300000000000ull, - 0x3ff1300000000000ull, 0x3ff1200000000000ull, 0x3ff1200000000000ull, 0x3ff1100000000000ull, - 0x3ff1100000000000ull, 0x3ff1000000000000ull, 0x3ff0f00000000000ull, 0x3ff0f00000000000ull, - 0x3ff0e00000000000ull, 0x3ff0e00000000000ull, 0x3ff0d00000000000ull, 0x3ff0d00000000000ull, - 0x3ff0c00000000000ull, 0x3ff0c00000000000ull, 0x3ff0b00000000000ull, 0x3ff0a00000000000ull, - 0x3ff0a00000000000ull, 0x3ff0900000000000ull, 0x3ff0900000000000ull, 0x3ff0800000000000ull, - 0x3ff0800000000000ull, 0x3ff0700000000000ull, 0x3ff0700000000000ull, 0x3ff0600000000000ull, - 0x3ff0600000000000ull, 0x3ff0500000000000ull, 0x3ff0500000000000ull, 0x3ff0400000000000ull, - 0x3ff0400000000000ull, 0x3ff0300000000000ull, 0x3ff0300000000000ull, 0x3ff0200000000000ull, - 0x3ff0200000000000ull, 0x3ff0100000000000ull, 0x3ff0100000000000ull, 0x3ff0000000000000ull, - 0x3ff0000000000000ull, -}; -ALTD_MATH_TABLE uint64_t UCRT_POW_T2[257] = { - 0x0000000000000000ull, 0x3effe01fe01fe020ull, 0x3f1fc07f01fc07f0ull, 0x3f31caa01fa11caaull, - 0x3f3f81f81f81f820ull, 0x3f48856506ddaba6ull, 0x3f5196792909c560ull, 0x3f57d9108c2ad433ull, - 0x3f5f07c1f07c1f08ull, 0x3f638ff08b1c03ddull, 0x3f680f6603d980f6ull, 0x3f6d00f57403d5d0ull, - 0x3f331abf0b7672a0ull, 0x3f506a965d43919bull, 0x3f5ceb240795ceb2ull, 0x3f6522f3b834e67full, - 0x3f6c3c3c3c3c3c3cull, 0x3f3e01e01e01e01eull, 0x3f575b8fe21a291cull, 0x3f6403b9403b9404ull, - 0x3f6cc0ed7303b5ccull, 0x3f479118f3fc4da2ull, 0x3f5ed952e0b0ce46ull, 0x3f695900eae56404ull, - 0x3f3d41d41d41d41dull, 0x3f5cb28ff16c69aeull, 0x3f696b1edd80e866ull, 0x3f4372e225fe30d9ull, - 0x3f60ad12073615a2ull, 0x3f6cdb2c0397cdb3ull, 0x3f52cc157b864407ull, 0x3f664cb5f7148404ull, - 0x3f3c71c71c71c71cull, 0x3f6129a21a930b84ull, 0x3f6f1e0387f1e038ull, 0x3f5ad4e4ba80709bull, - 0x3f6c0e070381c0e0ull, 0x3f560fba1a362bb0ull, 0x3f6a5713280dee96ull, 0x3f53f59620f9ece9ull, - 0x3f69f22983759f23ull, 0x3f5478ac63fc8d5cull, 0x3f6ad87bb4671656ull, 0x3f578b8efbb8148cull, - 0x3f6d0369d0369d03ull, 0x3f5d212b601b3748ull, 0x3f0b2036406c80d9ull, 0x3f629663b24547d1ull, - 0x3f4435e50d79435eull, 0x3f67d0ff2920bc03ull, 0x3f55c06b15c06b16ull, 0x3f6e3a5f0fd7f954ull, - 0x3f61dec0d4c77b03ull, 0x3f473289870ac52eull, 0x3f6a034da034da03ull, 0x3f5d041da2292856ull, - 0x3f3a41a41a41a41aull, 0x3f68550f8a39409dull, 0x3f5b4fe5e92c0686ull, 0x3f3a01a01a01a01aull, - 0x3f691d2a2067b23aull, 0x3f5e7c5dada0b4e5ull, 0x3f468a7725080ce1ull, 0x3f6c49d4aa21b490ull, - 0x3f63333333333333ull, 0x3f54bc363b03fccfull, 0x3f2c9f01970e4f81ull, 0x3f697617c6ef5b25ull, - 0x3f6161f9add3c0caull, 0x3f5319fe6cb39806ull, 0x3f2f693a1c451ab3ull, 0x3f6a9e240321a9e2ull, - 0x3f63831f3831f383ull, 0x3f5949ebc4dcfc1cull, 0x3f480c6980c6980cull, 0x3f6f9d00c5fe7403ull, - 0x3f69721ed7e75347ull, 0x3f6381ec0313381full, 0x3f5b97c2aec12653ull, 0x3f509ef3024ae3baull, - 0x3f38618618618618ull, 0x3f6e0184f00c2780ull, 0x3f692ef5657dba52ull, 0x3f64940305494030ull, - 0x3f60303030303030ull, 0x3f58060180601806ull, 0x3f5017f405fd017full, 0x3f412a8ad278e8ddull, - 0x3f17d05f417d05f4ull, 0x3f6d67245c02f7d6ull, 0x3f6a4411c1d986a9ull, 0x3f6754d76c7316dfull, - 0x3f649902f149902full, 0x3f621023358c1a68ull, 0x3f5f7390d2a6c406ull, 0x3f5b2b0805d5b2b1ull, - 0x3f5745d1745d1746ull, 0x3f53c31507fa32c4ull, 0x3f50a1fd1b7af017ull, 0x3f4bc36ce3e0453aull, - 0x3f4702e05c0b8170ull, 0x3f4300b79300b793ull, 0x3f3f76b4337c6cb1ull, 0x3f3a62681c860fb0ull, - 0x3f36c16c16c16c17ull, 0x3f3490aa31a3cfc7ull, 0x3f33cd153729043eull, 0x3f3473a88d0bfd2eull, - 0x3f36816816816817ull, 0x3f39f36016719f36ull, 0x3f3ec6a5122f9016ull, 0x3f427c29da5519cfull, - 0x3f4642c8590b2164ull, 0x3f4ab5c45606f00bull, 0x3f4fd3b80b11fd3cull, 0x3f52cda0c6ba4eaaull, - 0x3f56058160581606ull, 0x3f5990d0a4b7ef87ull, 0x3f5d6ee340579d6full, 0x3f60cf87d9c54a69ull, - 0x3f6310572620ae4cull, 0x3f65798c8ff522a2ull, 0x3f680ad602b580adull, 0x3f6ac3e24799546full, - 0x3f6da46102b1da46ull, 0x3f15805601580560ull, 0x3f3ed3c506b39a23ull, 0x3f4cbdd3e2970f60ull, - 0x3f55555555555555ull, 0x3f5c979aee0bf805ull, 0x3f621291e81fd58eull, 0x3f65fead500a9580ull, - 0x3f6a0fd5c5f02a3aull, 0x3f6e45c223898adcull, 0x3f35015015015015ull, 0x3f4c7b16ea64d422ull, - 0x3f57829cbc14e5e1ull, 0x3f60877db8589720ull, 0x3f65710e4b5edceaull, 0x3f6a7dbb4d1fc1c8ull, - 0x3f6fad40a57eb503ull, 0x3f43fd6bb00a5140ull, 0x3f54e78ecb419ba9ull, 0x3f600a44029100a4ull, - 0x3f65c28f5c28f5c3ull, 0x3f6b9c68b2c0cc4aull, 0x3f2978feb9f34381ull, 0x3f4ecf163bb6500aull, - 0x3f5be1958b67ebb9ull, 0x3f644e6157dc9a3bull, 0x3f6acc4baa3f0ddfull, 0x3f26a4cbcb2a247bull, - 0x3f50505050505050ull, 0x3f5e0b4439959819ull, 0x3f66027f6027f602ull, 0x3f6d1e854b5e0db4ull, - 0x3f4165e7254813e2ull, 0x3f576646a9d716efull, 0x3f632b48f757ce88ull, 0x3f6ac1b24652a906ull, - 0x3f33b13b13b13b14ull, 0x3f5490e1eb208984ull, 0x3f62385830fec66eull, 0x3f6a45a6cc111b7eull, - 0x3f33813813813814ull, 0x3f556f472517b708ull, 0x3f631be7bc0e8f2aull, 0x3f6b9cbf3e55f044ull, - 0x3f40e7d95bc609a9ull, 0x3f59e6b3804d19e7ull, 0x3f65c8b6af7963c2ull, 0x3f6eb9dad43bf402ull, - 0x3f4f1a515885fb37ull, 0x3f60eeb1d3d76c02ull, 0x3f6a320261a32026ull, 0x3f3c82ac40260390ull, - 0x3f5a12f684bda12full, 0x3f669d43fda2962cull, 0x3f02e025c04b8097ull, 0x3f542804b542804bull, - 0x3f63f69b02593f6aull, 0x3f6df31cb46e21faull, 0x3f5012b404ad012bull, 0x3f623925e7820a7full, - 0x3f6c8253c8253c82ull, 0x3f4b92ddc02526e5ull, 0x3f61602511602511ull, 0x3f6bf471439c9adfull, - 0x3f4a85c40939a85cull, 0x3f6166f9ac024d16ull, 0x3f6c44e10125e227ull, 0x3f4cebf48bbd90e5ull, - 0x3f62492492492492ull, 0x3f6d6f2e2ec0b673ull, 0x3f5159e26af37c05ull, 0x3f64024540245402ull, - 0x3f6f6f0243f6f024ull, 0x3f55e60121579805ull, 0x3f668e18cf81b10full, 0x3f32012012012012ull, - 0x3f5c11f7047dc11full, 0x3f69e878ff70985eull, 0x3f4779d9fdc3a219ull, 0x3f61eace5c957907ull, - 0x3f6e0d5b450239e1ull, 0x3f548bf073816367ull, 0x3f6694808dda5202ull, 0x3f37c67f2bae2b21ull, - 0x3f5ee58469ee5847ull, 0x3f6c0233c0233c02ull, 0x3f514e02328a7012ull, 0x3f6561072057b573ull, - 0x3f31811811811812ull, 0x3f5e28646f5a1060ull, 0x3f6c0d1284e6f1d7ull, 0x3f523543f0c80459ull, - 0x3f663cbeea4e1a09ull, 0x3f3b9a3fdd5c8cb8ull, 0x3f60be1c159a76d2ull, 0x3f6e1d1a688e4838ull, - 0x3f572044d72044d7ull, 0x3f691713db81577bull, 0x3f4ac73ae9819b50ull, 0x3f6460334e904cf6ull, - 0x3f31111111111111ull, 0x3f5feef80441fef0ull, 0x3f6de021fde021feull, 0x3f57b7eacc9686a0ull, - 0x3f69ead7cd391fbcull, 0x3f50195609804390ull, 0x3f6641511e8d2b32ull, 0x3f4222b1acf1ce96ull, - 0x3f62e29f79b47582ull, 0x3f24f0d1682e11cdull, 0x3f5f9bb096771e4dull, 0x3f6e5ee45dd96ae2ull, - 0x3f5a0429a0429a04ull, 0x3f6bb74d5f06c021ull, 0x3f54fce404254fceull, 0x3f695766eacbc402ull, - 0x3f50842108421084ull, 0x3f673e5371d5c338ull, 0x3f4930523fbe3368ull, 0x3f656b38f225f6c4ull, - 0x3f426e978d4fdf3bull, 0x3f63dd40e4eb0cc6ull, 0x3f397f7d73404146ull, 0x3f6293982cc98af1ull, - 0x3f30410410410410ull, 0x3f618d6f048ff7e4ull, 0x3f2236a3ebc349deull, 0x3f60c9f8ee53d18cull, - 0x3f10204081020408ull, 0x3f60486ca2f46ea6ull, 0x3ef0101010101010ull, 0x3f60080402010080ull, - 0x0000000000000000ull, -}; -ALTD_MATH_TABLE uint64_t UCRT_POW_T3[257] = { - 0x0000000000000000ull, 0x3f6ff00aa0000000ull, 0x3f7fe02a60000000ull, 0x3f87dc4750000000ull, - 0x3f8fc0a8b0000000ull, 0x3f93cea440000000ull, 0x3f97b91b00000000ull, 0x3f9b9fc020000000ull, - 0x3f9f829b00000000ull, 0x3fa1b0d980000000ull, 0x3fa39e87b0000000ull, 0x3fa58a5ba0000000ull, - 0x3fa77458f0000000ull, 0x3fa95c8300000000ull, 0x3fab42dd70000000ull, 0x3fad276b80000000ull, - 0x3faf0a30c0000000ull, 0x3fb0759830000000ull, 0x3fb16536e0000000ull, 0x3fb253f620000000ull, - 0x3fb341d790000000ull, 0x3fb42edcb0000000ull, 0x3fb51b0730000000ull, 0x3fb60658a0000000ull, - 0x3fb6f0d280000000ull, 0x3fb7da7660000000ull, 0x3fb8c345d0000000ull, 0x3fb9ab4240000000ull, - 0x3fba926d30000000ull, 0x3fbb78c820000000ull, 0x3fbc5e5480000000ull, 0x3fbd4313d0000000ull, - 0x3fbe270760000000ull, 0x3fbf0a30c0000000ull, 0x3fbfec9130000000ull, 0x3fc0671510000000ull, - 0x3fc0d77e70000000ull, 0x3fc1478580000000ull, 0x3fc1b72ad0000000ull, 0x3fc2266f10000000ull, - 0x3fc29552f0000000ull, 0x3fc303d710000000ull, 0x3fc371fc20000000ull, 0x3fc3dfc2b0000000ull, - 0x3fc44d2b60000000ull, 0x3fc4ba36f0000000ull, 0x3fc526e5e0000000ull, 0x3fc59338d0000000ull, - 0x3fc5ff3070000000ull, 0x3fc66acd40000000ull, 0x3fc6d60fe0000000ull, 0x3fc740f8f0000000ull, - 0x3fc7ab8900000000ull, 0x3fc815c0a0000000ull, 0x3fc87fa060000000ull, 0x3fc8e928d0000000ull, - 0x3fc9525a90000000ull, 0x3fc9bb3620000000ull, 0x3fca23bc10000000ull, 0x3fca8becf0000000ull, - 0x3fcaf3c940000000ull, 0x3fcb5b5190000000ull, 0x3fcbc28670000000ull, 0x3fcc296850000000ull, - 0x3fcc8ff7c0000000ull, 0x3fccf63540000000ull, 0x3fcd5c2160000000ull, 0x3fcdc1bca0000000ull, - 0x3fce270760000000ull, 0x3fce8c0250000000ull, 0x3fcef0adc0000000ull, 0x3fcf550a50000000ull, - 0x3fcfb91860000000ull, 0x3fd00e6c40000000ull, 0x3fd0402590000000ull, 0x3fd071b850000000ull, - 0x3fd0a324e0000000ull, 0x3fd0d46b50000000ull, 0x3fd1058bf0000000ull, 0x3fd1368700000000ull, - 0x3fd1675ca0000000ull, 0x3fd1980d20000000ull, 0x3fd1c898c0000000ull, 0x3fd1f8ff90000000ull, - 0x3fd22941f0000000ull, 0x3fd2596010000000ull, 0x3fd2895a10000000ull, 0x3fd2b93030000000ull, - 0x3fd2e8e2b0000000ull, 0x3fd31871c0000000ull, 0x3fd347dd90000000ull, 0x3fd3772660000000ull, - 0x3fd3a64c50000000ull, 0x3fd3d54fa0000000ull, 0x3fd4043080000000ull, 0x3fd432ef20000000ull, - 0x3fd4618bc0000000ull, 0x3fd4900680000000ull, 0x3fd4be5f90000000ull, 0x3fd4ec9730000000ull, - 0x3fd51aad80000000ull, 0x3fd548a2c0000000ull, 0x3fd5767710000000ull, 0x3fd5a42ab0000000ull, - 0x3fd5d1bdb0000000ull, 0x3fd5ff3070000000ull, 0x3fd62c82f0000000ull, 0x3fd659b570000000ull, - 0x3fd686c810000000ull, 0x3fd6b3bb20000000ull, 0x3fd6e08ea0000000ull, 0x3fd70d42e0000000ull, - 0x3fd739d7f0000000ull, 0x3fd7664e10000000ull, 0x3fd792a550000000ull, 0x3fd7bede00000000ull, - 0x3fd7eaf830000000ull, 0x3fd816f410000000ull, 0x3fd842d1d0000000ull, 0x3fd86e9190000000ull, - 0x3fd89a3380000000ull, 0x3fd8c5b7c0000000ull, 0x3fd8f11e80000000ull, 0x3fd91c67e0000000ull, - 0x3fd9479410000000ull, 0x3fd972a340000000ull, 0x3fd99d9580000000ull, 0x3fd9c86b00000000ull, - 0x3fd9f323e0000000ull, 0x3fda1dc060000000ull, 0x3fda484090000000ull, 0x3fda72a490000000ull, - 0x3fda9cec90000000ull, 0x3fdac718c0000000ull, 0x3fdaf12930000000ull, 0x3fdb1b1e00000000ull, - 0x3fdb44f770000000ull, 0x3fdb6eb590000000ull, 0x3fdb985890000000ull, 0x3fdbc1e080000000ull, - 0x3fdbeb4d90000000ull, 0x3fdc149ff0000000ull, 0x3fdc3dd7a0000000ull, 0x3fdc66f4e0000000ull, - 0x3fdc8ff7c0000000ull, 0x3fdcb8e070000000ull, 0x3fdce1af00000000ull, 0x3fdd0a63a0000000ull, - 0x3fdd32fe70000000ull, 0x3fdd5b7f90000000ull, 0x3fdd83e720000000ull, 0x3fddac3530000000ull, - 0x3fddd46a00000000ull, 0x3fddfc8590000000ull, 0x3fde248810000000ull, 0x3fde4c71a0000000ull, - 0x3fde744260000000ull, 0x3fde9bfa60000000ull, 0x3fdec399d0000000ull, 0x3fdeeb20c0000000ull, - 0x3fdf128f50000000ull, 0x3fdf39e5b0000000ull, 0x3fdf6123f0000000ull, 0x3fdf884a30000000ull, - 0x3fdfaf5880000000ull, 0x3fdfd64f20000000ull, 0x3fdffd2e00000000ull, 0x3fe011fab0000000ull, - 0x3fe02552a0000000ull, 0x3fe0389ee0000000ull, 0x3fe04bdf90000000ull, 0x3fe05f14b0000000ull, - 0x3fe0723e50000000ull, 0x3fe0855c80000000ull, 0x3fe0986f40000000ull, 0x3fe0ab76b0000000ull, - 0x3fe0be72e0000000ull, 0x3fe0d163c0000000ull, 0x3fe0e44980000000ull, 0x3fe0f72410000000ull, - 0x3fe109f390000000ull, 0x3fe11cb810000000ull, 0x3fe12f7190000000ull, 0x3fe1422020000000ull, - 0x3fe154c3d0000000ull, 0x3fe1675ca0000000ull, 0x3fe179eab0000000ull, 0x3fe18c6e00000000ull, - 0x3fe19ee6b0000000ull, 0x3fe1b154b0000000ull, 0x3fe1c3b810000000ull, 0x3fe1d610f0000000ull, - 0x3fe1e85f50000000ull, 0x3fe1faa340000000ull, 0x3fe20cdcd0000000ull, 0x3fe21f0bf0000000ull, - 0x3fe23130d0000000ull, 0x3fe2434b60000000ull, 0x3fe2555bc0000000ull, 0x3fe2676200000000ull, - 0x3fe2795e10000000ull, 0x3fe28b5000000000ull, 0x3fe29d37f0000000ull, 0x3fe2af15f0000000ull, - 0x3fe2c0e9e0000000ull, 0x3fe2d2b400000000ull, 0x3fe2e47430000000ull, 0x3fe2f62a90000000ull, - 0x3fe307d730000000ull, 0x3fe3197a00000000ull, 0x3fe32b1330000000ull, 0x3fe33ca2b0000000ull, - 0x3fe34e2890000000ull, 0x3fe35fa4e0000000ull, 0x3fe37117b0000000ull, 0x3fe38280f0000000ull, - 0x3fe393e0d0000000ull, 0x3fe3a53730000000ull, 0x3fe3b68440000000ull, 0x3fe3c7c7f0000000ull, - 0x3fe3d90260000000ull, 0x3fe3ea3390000000ull, 0x3fe3fb5b80000000ull, 0x3fe40c7a40000000ull, - 0x3fe41d8fe0000000ull, 0x3fe42e9c60000000ull, 0x3fe43f9fe0000000ull, 0x3fe4509a50000000ull, - 0x3fe4618bc0000000ull, 0x3fe4727430000000ull, 0x3fe48353d0000000ull, 0x3fe4942a80000000ull, - 0x3fe4a4f850000000ull, 0x3fe4b5bd60000000ull, 0x3fe4c679a0000000ull, 0x3fe4d72d30000000ull, - 0x3fe4e7d810000000ull, 0x3fe4f87a30000000ull, 0x3fe50913c0000000ull, 0x3fe519a4c0000000ull, - 0x3fe52a2d20000000ull, 0x3fe53aad00000000ull, 0x3fe54b2460000000ull, 0x3fe55b9350000000ull, - 0x3fe56bf9d0000000ull, 0x3fe57c57f0000000ull, 0x3fe58cadb0000000ull, 0x3fe59cfb20000000ull, - 0x3fe5ad4040000000ull, 0x3fe5bd7d30000000ull, 0x3fe5cdb1d0000000ull, 0x3fe5ddde50000000ull, - 0x3fe5ee02a0000000ull, 0x3fe5fe1ed0000000ull, 0x3fe60e32f0000000ull, 0x3fe61e3ef0000000ull, - 0x3fe62e42e0000000ull, -}; -ALTD_MATH_TABLE uint64_t UCRT_POW_T4[257] = { - 0x0000000000000000ull, 0x3db5885e0250435aull, 0x3de620cf11f86ed2ull, 0x3dff0214edba4a25ull, - 0x3dbf807c79f3db4eull, 0x3dea352ba779a52bull, 0x3dff56c46aa49fd5ull, 0x3dfebe465fef5196ull, - 0x3e0cf0660099f1f8ull, 0x3e1247b2ff85945dull, 0x3e13fd7abf5202b6ull, 0x3e1f91c9a918d51eull, - 0x3e08cb73f118d3caull, 0x3e1d91c7d6fad074ull, 0x3de1971bec28d14cull, 0x3e15b616a423c78aull, - 0x3da162a6617cc971ull, 0x3e166391c4c06d29ull, 0x3e2d46f5c1d0c4b8ull, 0x3e2e14282df1f6d3ull, - 0x3e186f47424a660dull, 0x3e2d4c8de077753eull, 0x3e2e0c307ed24f1cull, 0x3e226ea18763bdd3ull, - 0x3e25cad69737c933ull, 0x3e2af62599088901ull, 0x3e18c66c83d6b2d0ull, 0x3e1880ceb36fb30full, - 0x3e2495aac6ca17a4ull, 0x3e2761db4210878cull, 0x3e2eb78e862bac2full, 0x3e19b2cd75790dd9ull, - 0x3e2c55e5cbd3d50full, 0x3db162a6617cc971ull, 0x3dfdbeabaaa2e519ull, 0x3e1652cb7150c647ull, - 0x3e39a11cb2cd2ee2ull, 0x3e219d0ab1a28813ull, 0x3e24bd9e80a41811ull, 0x3e3214b596faa3dfull, - 0x3e303fea46980bb8ull, 0x3e31c8ffa5fd28c7ull, 0x3dce8f743bcd96c5ull, 0x3dfd98c5395315c6ull, - 0x3e3996fa3ccfa7b2ull, 0x3e1cd2af2ad13037ull, 0x3e1d0da1bd17200eull, 0x3e3330410ba68b75ull, - 0x3df4f27a790e7c41ull, 0x3e13956a86f6ff1bull, 0x3e2c6748723551d9ull, 0x3e2500de9326cdfcull, - 0x3e1086c848df1b59ull, 0x3e04357ead6836ffull, 0x3e24832442408024ull, 0x3e3d10da8154b13dull, - 0x3e39e8ad68ec8260ull, 0x3e3cfbf706abaf18ull, 0x3e3fc56ac6326e23ull, 0x3e39105e3185cf21ull, - 0x3e3d017fe5b19cc0ull, 0x3e3d1f6b48dd13feull, 0x3e20b63358a7e73aull, 0x3e263063028c211cull, - 0x3e2e6a6886b09760ull, 0x3e3c138bb891cd03ull, 0x3e369f7722b7221aull, 0x3df57d8fac1a628cull, - 0x3e3c55e5cbd3d50full, 0x3e1552d2ff48fe2eull, 0x3e37b8b26ca431bcull, 0x3e292decdc1c5f6dull, - 0x3e3abc7c551aaa8cull, 0x3e36b540731a354bull, 0x3e32d341036b89efull, 0x3e4f9ab21a3a2e0full, - 0x3e239c871afb9fbdull, 0x3e3e6add2c81f640ull, 0x3e435c95aa313f41ull, 0x3e249d4582f6cc53ull, - 0x3e47574c1c07398full, 0x3e4ba846dece9e8dull, 0x3e16999fafbc68e7ull, 0x3e4c9145e51b0103ull, - 0x3e479ef2cb44850aull, 0x3e0beec73de11275ull, 0x3e2ef4351af5a498ull, 0x3e45713a493b4a50ull, - 0x3e45c23a61385992ull, 0x3e42a88309f57299ull, 0x3e4530faa9ac8aceull, 0x3e25fec2d792a758ull, - 0x3e35a517a71cbcd7ull, 0x3e3707dc3e1cd9a3ull, 0x3e3a1a9f8ef43049ull, 0x3e4409d0276b3674ull, - 0x3e20e2f613e85bd9ull, 0x3df0027433001e5full, 0x3e35dde2836d3265ull, 0x3e2300134d7aaf04ull, - 0x3e3cb7e0b42724f5ull, 0x3e2d6e93167e6308ull, 0x3e3d1569b1526adbull, 0x3e0e99fc338a1a41ull, - 0x3e4eb01394a11b1cull, 0x3e04f27a790e7c41ull, 0x3e25ce3ca97b7af9ull, 0x3e281f0f940ed857ull, - 0x3e4d36295d88857cull, 0x3e21aca1ec4af526ull, 0x3e445743c7182726ull, 0x3e23c491aead337eull, - 0x3e3aef401a738931ull, 0x3e21cede76092a29ull, 0x3e4fba8f44f82bb4ull, 0x3e446f5f7f3c3e1aull, - 0x3e47055f86c9674bull, 0x3e4b41a92b6b6e1aull, 0x3e443d162e927628ull, 0x3e4466174013f9b1ull, - 0x3e3b05096ad69c62ull, 0x3e40b169150faa58ull, 0x3e3cd98b1df85da7ull, 0x3e468b507b0f8fa8ull, - 0x3e48422df57499baull, 0x3e11351586970274ull, 0x3e117e08acba92eeull, 0x3e26e04314dd0229ull, - 0x3e497f3097e56d1aull, 0x3e3356e655901286ull, 0x3e0cb761457f94d6ull, 0x3e39af67a85a9dacull, - 0x3e453410931a909full, 0x3e22c587206058f5ull, 0x3e223bc358899c22ull, 0x3e4d7bf8b6d223cbull, - 0x3e47991ec5197ddbull, 0x3e4a79e6bb3a9219ull, 0x3e3a4c43ed663ec5ull, 0x3e461b5a1484f438ull, - 0x3e4b4e36f7ef0c3aull, 0x3e115f026acd0d1bull, 0x3e3f36b535cecf05ull, 0x3e2ffb7fbf3eb5c6ull, - 0x3e3e6a6886b09760ull, 0x3e3135eb27f5bbc3ull, 0x3e470be7d6f6fa57ull, 0x3e4ce43cc84ab338ull, - 0x3e4c01d7aac3bd91ull, 0x3e45c58d07961060ull, 0x3e3628bcf941456eull, 0x3e4c58b2a8461cd2ull, - 0x3e33071282fb989aull, 0x3e420dab6a80f09cull, 0x3e44f8d84c397b1eull, 0x3e40d0ee08599e48ull, - 0x3e1d68787e37da36ull, 0x3e366187d591bafcull, 0x3e22346600bae772ull, 0x3e390377d0d61b8eull, - 0x3e4f5e0dd966b907ull, 0x3e49023cb79a00e2ull, 0x3e44e05158c28ad8ull, 0x3e3bfa7b08b18ae4ull, - 0x3e4ef1e63db35f67ull, 0x3e0ec2ae39493d4full, 0x3e40afe930ab2fa0ull, 0x3e225ff8a1810dd4ull, - 0x3e469743fb1a71a5ull, 0x3e5f9cc676785571ull, 0x3e5b524da4cbf982ull, 0x3e5a4c8b381535b8ull, - 0x3e5839be809caf2cull, 0x3e50968a1cb82c13ull, 0x3e5eae6a41723fb5ull, 0x3e5d9c29a380a4dbull, - 0x3e4094aa0ada625eull, 0x3e5973ad6fc108caull, 0x3e4747322fdbab97ull, 0x3e593692fa9d4221ull, - 0x3e5c5a992dfbc7d9ull, 0x3e4e1f33e102387aull, 0x3e464fbef14c048cull, 0x3e4490f513ca5e3bull, - 0x3e37a6af4d4c799dull, 0x3e57574c1c07398full, 0x3e57b133417f8c1cull, 0x3e5feb9e0c176514ull, - 0x3e419f25bb3172f7ull, 0x3e45f68a7bbfb852ull, 0x3e5ee278497929f1ull, 0x3e5ccee006109d58ull, - 0x3e5ce081a07bd8b3ull, 0x3e570e12981817b8ull, 0x3e292ab6d93503d0ull, 0x3e58cb7dd7c3b61eull, - 0x3e4efafd0a0b78daull, 0x3e5e907267c4288eull, 0x3e5d31ef96780875ull, 0x3e23430dfcd2ad50ull, - 0x3e344d88d75bc1f9ull, 0x3e5bec0f055e04fcull, 0x3e5d85611590b9adull, 0x3df320568e583229ull, - 0x3e5a891d1772f538ull, 0x3e22edc9dabba74dull, 0x3e4b9009a1015086ull, 0x3e52a12a8c5b1a19ull, - 0x3e3a7885f0fdac85ull, 0x3e5f4ffcd43ac691ull, 0x3e52243ae2640aadull, 0x3e546513299035d3ull, - 0x3e5b39c3a62dd725ull, 0x3e5ba6dd40049f51ull, 0x3e451d1ed7177409ull, 0x3e5cb0f2fd7f5216ull, - 0x3e3ab150cd4e2213ull, 0x3e5cfd7bf3193844ull, 0x3e53fff8455f1dbdull, 0x3e5fee640b905fc9ull, - 0x3e54e2adf548084cull, 0x3e3b597adc1ecdd2ull, 0x3e4345bd096d3a75ull, 0x3e5101b9d2453c8bull, - 0x3e508ce55cc8c979ull, 0x3e5bbf017e595f71ull, 0x3e37ce733bd393dcull, 0x3e233bb0a503f8a1ull, - 0x3e30e2f613e85bd9ull, 0x3e5e67555a635b3cull, 0x3e2ea88df73d5e8bull, 0x3e3d17e03bda18a8ull, - 0x3e5b607d76044f7eull, 0x3e52adc4e71bc2fcull, 0x3e5f99dc7362d1d9ull, 0x3e5473fa008e6a6aull, - 0x3e2b75bb09cb0985ull, 0x3e5ea04dd10b9abaull, 0x3e5802d0d6979674ull, 0x3e174688ccd99094ull, - 0x3e496f16abb9df22ull, 0x3e46e66df2aa374full, 0x3e4e66525ea4550aull, 0x3e42d02f34f20cbdull, - 0x3e46cfce65047188ull, 0x3e39b78c842d58b8ull, 0x3e4735e624c24bc9ull, 0x3e47eba1f7dd1adfull, - 0x3e586b3e59f65355ull, 0x3e1ce38e637f1b4dull, 0x3e58d82ec919edc7ull, 0x3e4c52648ddcfa37ull, - 0x3e52482ceae1ac12ull, 0x3e55a312311aba4full, 0x3e411e236329f225ull, 0x3e5b48c8cd2f246cull, - 0x3e6efa39ef35793cull, -}; -// win11 pow (ARM optimized-routines pow): log table, N = 128 -ALTD_MATH_TABLE uint64_t UCRT_POW11_INVC[128] = { - 0x3ff6a00000000000ull, 0x3ff6800000000000ull, 0x3ff6600000000000ull, 0x3ff6400000000000ull, - 0x3ff6200000000000ull, 0x3ff6000000000000ull, 0x3ff5e00000000000ull, 0x3ff5c00000000000ull, - 0x3ff5a00000000000ull, 0x3ff5800000000000ull, 0x3ff5600000000000ull, 0x3ff5600000000000ull, - 0x3ff5400000000000ull, 0x3ff5200000000000ull, 0x3ff5000000000000ull, 0x3ff4e00000000000ull, - 0x3ff4c00000000000ull, 0x3ff4a00000000000ull, 0x3ff4a00000000000ull, 0x3ff4800000000000ull, - 0x3ff4600000000000ull, 0x3ff4400000000000ull, 0x3ff4200000000000ull, 0x3ff4000000000000ull, - 0x3ff4000000000000ull, 0x3ff3e00000000000ull, 0x3ff3c00000000000ull, 0x3ff3a00000000000ull, - 0x3ff3a00000000000ull, 0x3ff3800000000000ull, 0x3ff3600000000000ull, 0x3ff3400000000000ull, - 0x3ff3400000000000ull, 0x3ff3200000000000ull, 0x3ff3000000000000ull, 0x3ff3000000000000ull, - 0x3ff2e00000000000ull, 0x3ff2c00000000000ull, 0x3ff2c00000000000ull, 0x3ff2a00000000000ull, - 0x3ff2800000000000ull, 0x3ff2600000000000ull, 0x3ff2600000000000ull, 0x3ff2400000000000ull, - 0x3ff2400000000000ull, 0x3ff2200000000000ull, 0x3ff2000000000000ull, 0x3ff2000000000000ull, - 0x3ff1e00000000000ull, 0x3ff1c00000000000ull, 0x3ff1c00000000000ull, 0x3ff1a00000000000ull, - 0x3ff1a00000000000ull, 0x3ff1800000000000ull, 0x3ff1600000000000ull, 0x3ff1600000000000ull, - 0x3ff1400000000000ull, 0x3ff1400000000000ull, 0x3ff1200000000000ull, 0x3ff1000000000000ull, - 0x3ff1000000000000ull, 0x3ff0e00000000000ull, 0x3ff0e00000000000ull, 0x3ff0c00000000000ull, - 0x3ff0c00000000000ull, 0x3ff0a00000000000ull, 0x3ff0a00000000000ull, 0x3ff0800000000000ull, - 0x3ff0800000000000ull, 0x3ff0600000000000ull, 0x3ff0400000000000ull, 0x3ff0400000000000ull, - 0x3ff0200000000000ull, 0x3ff0200000000000ull, 0x3ff0000000000000ull, 0x3ff0000000000000ull, - 0x3fefc00000000000ull, 0x3fef800000000000ull, 0x3fef400000000000ull, 0x3fef000000000000ull, - 0x3feec00000000000ull, 0x3fee800000000000ull, 0x3fee400000000000ull, 0x3fee200000000000ull, - 0x3fede00000000000ull, 0x3feda00000000000ull, 0x3fed600000000000ull, 0x3fed400000000000ull, - 0x3fed000000000000ull, 0x3fecc00000000000ull, 0x3feca00000000000ull, 0x3fec600000000000ull, - 0x3fec400000000000ull, 0x3fec000000000000ull, 0x3febe00000000000ull, 0x3feba00000000000ull, - 0x3feb800000000000ull, 0x3feb400000000000ull, 0x3feb200000000000ull, 0x3feae00000000000ull, - 0x3feac00000000000ull, 0x3feaa00000000000ull, 0x3fea600000000000ull, 0x3fea400000000000ull, - 0x3fea000000000000ull, 0x3fe9e00000000000ull, 0x3fe9c00000000000ull, 0x3fe9a00000000000ull, - 0x3fe9600000000000ull, 0x3fe9400000000000ull, 0x3fe9200000000000ull, 0x3fe9000000000000ull, - 0x3fe8c00000000000ull, 0x3fe8a00000000000ull, 0x3fe8800000000000ull, 0x3fe8600000000000ull, - 0x3fe8400000000000ull, 0x3fe8200000000000ull, 0x3fe7e00000000000ull, 0x3fe7c00000000000ull, - 0x3fe7a00000000000ull, 0x3fe7800000000000ull, 0x3fe7600000000000ull, 0x3fe7400000000000ull, - 0x3fe7200000000000ull, 0x3fe7000000000000ull, 0x3fe6e00000000000ull, 0x3fe6c00000000000ull, -}; -ALTD_MATH_TABLE uint64_t UCRT_POW11_LOGC[128] = { - 0xbfd62c82f2b9c800ull, 0xbfd5d1bdbf580800ull, 0xbfd5767717455800ull, 0xbfd51aad872df800ull, - 0xbfd4be5f95777800ull, 0xbfd4618bc21c6000ull, 0xbfd404308686a800ull, 0xbfd3a64c55694800ull, - 0xbfd347dd9a988000ull, 0xbfd2e8e2bae12000ull, 0xbfd2895a13de8800ull, 0xbfd2895a13de8800ull, - 0xbfd22941fbcf7800ull, 0xbfd1c898c1699800ull, 0xbfd1675cababa800ull, 0xbfd1058bf9ae4800ull, - 0xbfd0a324e2739000ull, 0xbfd0402594b4d000ull, 0xbfd0402594b4d000ull, 0xbfcfb9186d5e4000ull, - 0xbfcef0adcbdc6000ull, 0xbfce27076e2af000ull, 0xbfcd5c216b4fc000ull, 0xbfcc8ff7c79aa000ull, - 0xbfcc8ff7c79aa000ull, 0xbfcbc286742d9000ull, 0xbfcaf3c94e80c000ull, 0xbfca23bc1fe2b000ull, - 0xbfca23bc1fe2b000ull, 0xbfc9525a9cf45000ull, 0xbfc87fa06520d000ull, 0xbfc7ab890210e000ull, - 0xbfc7ab890210e000ull, 0xbfc6d60fe719d000ull, 0xbfc5ff3070a79000ull, 0xbfc5ff3070a79000ull, - 0xbfc526e5e3a1b000ull, 0xbfc44d2b6ccb8000ull, 0xbfc44d2b6ccb8000ull, 0xbfc371fc201e9000ull, - 0xbfc29552f81ff000ull, 0xbfc1b72ad52f6000ull, 0xbfc1b72ad52f6000ull, 0xbfc0d77e7cd09000ull, - 0xbfc0d77e7cd09000ull, 0xbfbfec9131dbe000ull, 0xbfbe27076e2b0000ull, 0xbfbe27076e2b0000ull, - 0xbfbc5e548f5bc000ull, 0xbfba926d3a4ae000ull, 0xbfba926d3a4ae000ull, 0xbfb8c345d631a000ull, - 0xbfb8c345d631a000ull, 0xbfb6f0d28ae56000ull, 0xbfb51b073f062000ull, 0xbfb51b073f062000ull, - 0xbfb341d7961be000ull, 0xbfb341d7961be000ull, 0xbfb16536eea38000ull, 0xbfaf0a30c0118000ull, - 0xbfaf0a30c0118000ull, 0xbfab42dd71198000ull, 0xbfab42dd71198000ull, 0xbfa77458f632c000ull, - 0xbfa77458f632c000ull, 0xbfa39e87b9fec000ull, 0xbfa39e87b9fec000ull, 0xbf9f829b0e780000ull, - 0xbf9f829b0e780000ull, 0xbf97b91b07d58000ull, 0xbf8fc0a8b0fc0000ull, 0xbf8fc0a8b0fc0000ull, - 0xbf7fe02a6b100000ull, 0xbf7fe02a6b100000ull, 0x0000000000000000ull, 0x0000000000000000ull, - 0x3f80101575890000ull, 0x3f90205658938000ull, 0x3f98492528c90000ull, 0x3fa0415d89e74000ull, - 0x3fa466aed42e0000ull, 0x3fa894aa149fc000ull, 0x3faccb73cdddc000ull, 0x3faeea31c006c000ull, - 0x3fb1973bd1466000ull, 0x3fb3bdf5a7d1e000ull, 0x3fb5e95a4d97a000ull, 0x3fb700d30aeac000ull, - 0x3fb9335e5d594000ull, 0x3fbb6ac88dad6000ull, 0x3fbc885801bc4000ull, 0x3fbec739830a2000ull, - 0x3fbfe89139dbe000ull, 0x3fc1178e8227e000ull, 0x3fc1aa2b7e23f000ull, 0x3fc2d1610c868000ull, - 0x3fc365fcb0159000ull, 0x3fc4913d8333b000ull, 0x3fc527e5e4a1b000ull, 0x3fc6574ebe8c1000ull, - 0x3fc6f0128b757000ull, 0x3fc7898d85445000ull, 0x3fc8beafeb390000ull, 0x3fc95a5adcf70000ull, - 0x3fca93ed3c8ae000ull, 0x3fcb31d8575bd000ull, 0x3fcbd087383be000ull, 0x3fcc6ffbc6f01000ull, - 0x3fcdb13db0d49000ull, 0x3fce530effe71000ull, 0x3fcef5ade4dd0000ull, 0x3fcf991c6cb3b000ull, - 0x3fd07138604d5800ull, 0x3fd0c42d67616000ull, 0x3fd1178e8227e800ull, 0x3fd16b5ccbacf800ull, - 0x3fd1bf99635a6800ull, 0x3fd214456d0eb800ull, 0x3fd2bef07cdc9000ull, 0x3fd314f1e1d36000ull, - 0x3fd36b6776be1000ull, 0x3fd3c25277333000ull, 0x3fd419b423d5e800ull, 0x3fd4718dc271c800ull, - 0x3fd4c9e09e173000ull, 0x3fd522ae0738a000ull, 0x3fd57bf753c8d000ull, 0x3fd5d5bddf596000ull, -}; -ALTD_MATH_TABLE uint64_t UCRT_POW11_LOGCTAIL[128] = { - 0x3cfab42428375680ull, 0xbd1ca508d8e0f720ull, 0xbd2362a4d5b6506dull, 0xbce684e49eb067d5ull, - 0xbd041b6993293ee0ull, 0x3d13d82f484c84ccull, 0x3cdc42f3ed820b3aull, 0x3d20b1c686519460ull, - 0x3d25594dd4c58092ull, 0x3d267b1e99b72bd8ull, 0x3d15ca14b6cfb03full, 0x3d15ca14b6cfb03full, - 0xbd165a242853da76ull, 0xbd1fafbc68e75404ull, 0x3d1f1fc63382a8f0ull, 0xbd26a8c4fd055a66ull, - 0xbd0c6bee7ef4030eull, 0xbcf036b89ef42d7full, 0xbcf036b89ef42d7full, 0x3d0d572aab993c87ull, - 0x3d2b26b79c86af24ull, 0xbd172f4f543fff10ull, 0x3d21ba91bbca681bull, 0x3d27794f689f8434ull, - 0x3d27794f689f8434ull, 0x3d194eb0318bb78full, 0x3cba4e633fcd9066ull, 0xbd258c64dc46c1eaull, - 0xbd258c64dc46c1eaull, 0xbd2ad1d904c1d4e3ull, 0x3d2bbdbf7fdbfa09ull, 0x3d2bdb9072534a58ull, - 0x3d2bdb9072534a58ull, 0xbd10e46aa3b2e266ull, 0xbd1e9e439f105039ull, 0xbd1e9e439f105039ull, - 0xbd20de8b90075b8full, 0x3d170cc16135783cull, 0x3d170cc16135783cull, 0x3cf178864d27543aull, - 0xbd248d301771c408ull, 0xbd2e80a41811a396ull, 0xbd2e80a41811a396ull, 0x3d0a699688e85bf4ull, - 0x3d0a699688e85bf4ull, 0xbd2575545ca333f2ull, 0x3d2a342c2af0003cull, 0x3d2a342c2af0003cull, - 0xbd1d0c57585fbe06ull, 0x3d253935e85baac8ull, 0x3d253935e85baac8ull, 0x3d137c294d2f5668ull, - 0x3d137c294d2f5668ull, 0xbd269737c93373daull, 0x3d1f025b61c65e57ull, 0x3d1f025b61c65e57ull, - 0x3d2c5edaccf913dfull, 0x3d2c5edaccf913dfull, 0x3d147c5e768fa309ull, 0x3d2d599e83368e91ull, - 0x3d2d599e83368e91ull, 0x3d1c827ae5d6704cull, 0x3d1c827ae5d6704cull, 0xbd2cfc4634f2a1eeull, - 0xbd2cfc4634f2a1eeull, 0x3cf502b7f526feaaull, 0x3cf502b7f526feaaull, 0xbd2980267c7e09e4ull, - 0xbd2980267c7e09e4ull, 0xbd288d5493faa639ull, 0xbcdf1e7cf6d3a69cull, 0xbcdf1e7cf6d3a69cull, - 0xbd19e23f0dda40e4ull, 0xbd19e23f0dda40e4ull, 0x0000000000000000ull, 0x0000000000000000ull, - 0xbd10c76b999d2be8ull, 0xbd23dc5b06e2f7d2ull, 0xbd2aa0ba325a0c34ull, 0x3d0111c05cf1d753ull, - 0xbd2c167375bdfd28ull, 0xbd197995d05a267dull, 0xbd1a68f247d82807ull, 0xbd0e113e4fc93b7bull, - 0xbd25325d560d9e9bull, 0x3d2cc85ea5db4ed7ull, 0xbd2c69063c5d1d1eull, 0x3cec1e8da99ded32ull, - 0x3d23115c3abd47daull, 0xbd1390802bf768e5ull, 0x3d2646d1c65aacd3ull, 0xbd2dc068afe645e0ull, - 0xbd2534d64fa10afdull, 0x3d21ef78ce2d07f2ull, 0x3d2ca78e44389934ull, 0x3d039d6ccb81b4a1ull, - 0x3cc62fa8234b7289ull, 0x3d25837954fdb678ull, 0x3d2633e8e5697dc7ull, 0x3d19cf8b2c3c2e78ull, - 0xbd25118de59c21e1ull, 0xbd1c661070914305ull, 0xbd073d54aae92cd1ull, 0x3d07f22858a0ff6full, - 0xbd28724350562169ull, 0xbd0c358d4eace1aaull, 0xbd2d4bc4595412b6ull, 0xbcf1ec72c5962bd2ull, - 0xbd2aff2af715b035ull, 0x3cc212276041f430ull, 0xbcca211565bb8e11ull, 0x3d1bcbecca0cdf30ull, - 0x3cf89cdb16ed4e91ull, 0x3d27188b163ceae9ull, 0xbd2c210e63a5f01cull, 0x3d2b9acdf7a51681ull, - 0x3d2ca6ed5147bdb7ull, 0x3d0a87deba46baeaull, 0x3d2a9cfa4a5004f4ull, 0xbd28e27ad3213cb8ull, - 0x3d116ecdb0f177c8ull, 0x3d183b54b606bd5cull, 0x3d08e436ec90e09dull, 0xbd2f27ce0967d675ull, - 0xbd2e20891b0ad8a4ull, 0x3d2ebe708164c759ull, 0x3d1fadedee5d40efull, 0xbd0a0b2a08a465dcull, -}; -// win11 pow: exp table, N = 256, (tail, scale bits) pairs -ALTD_MATH_TABLE uint64_t UCRT_POW11_EXP[512] = { - 0x0000000000000000ull, 0x3ff0000000000000ull, 0xbc84e82fc61851acull, 0x3feffb1afa5abcbfull, - 0x3c9b3b4f1a88bf6eull, 0x3feff63da9fb3335ull, 0xbc82985dd8521d32ull, 0x3feff168143b0281ull, - 0xbc7160139cd8dc5dull, 0x3fefec9a3e778061ull, 0x3c651e617061bfbdull, 0x3fefe7d42e11bbccull, - 0xbc905e7a108766d1ull, 0x3fefe315e86e7f85ull, 0x3c845fad437fa426ull, 0x3fefde5f72f654b1ull, - 0x3c8cd2523567f613ull, 0x3fefd9b0d3158574ull, 0xbc954529642b232full, 0x3fefd50a0e3c1f89ull, - 0xbc8bce8023f98efaull, 0x3fefd06b29ddf6deull, 0x3c8293708ef5c32eull, 0x3fefcbd42b72a836ull, - 0x3c60f74e61e6c861ull, 0x3fefc74518759bc8ull, 0xbc95b9280905b2a4ull, 0x3fefc2bdf66607e0ull, - 0x3c90a3e45b33d399ull, 0x3fefbe3ecac6f383ull, 0x3c84f31f32c4b7e7ull, 0x3fefb9c79b1f3919ull, - 0x3c979aa65d837b6dull, 0x3fefb5586cf9890full, 0x3c9407fb30d06420ull, 0x3fefb0f145e46c85ull, - 0x3c8eb51a92fdeffcull, 0x3fefac922b7247f7ull, 0xbc9a5d04b3b9911bull, 0x3fefa83b23395decull, - 0x3c3ebe3d702f9cd1ull, 0x3fefa3ec32d3d1a2ull, 0xbc937a01f0739546ull, 0x3fef9fa55fdfa9c5ull, - 0xbc6a033489906e0bull, 0x3fef9b66affed31bull, 0x3c8b8268b04ef0a5ull, 0x3fef973028d7233eull, - 0xbc9556522a2fbd0eull, 0x3fef9301d0125b51ull, 0xbc9ac46e44a2ebccull, 0x3fef8edbab5e2ab6ull, - 0xbc5080ef8c4eea55ull, 0x3fef8abdc06c31ccull, 0xbc65704e90c9f860ull, 0x3fef86a814f204abull, - 0xbc91c923b9d5f416ull, 0x3fef829aaea92de0ull, 0xbc897cea57e46280ull, 0x3fef7e95934f312eull, - 0x3c80d3e3e95c55afull, 0x3fef7a98c8a58e51ull, 0x3c56f01429e2b9d2ull, 0x3fef76a45471c3c2ull, - 0xbc801b15eaa59348ull, 0x3fef72b83c7d517bull, 0x3c6e653b2459034bull, 0x3fef6ed48695bbc0ull, - 0xbc8f1ff055de323dull, 0x3fef6af9388c8deaull, 0x3c92cc7ea345b7dcull, 0x3fef672658375d2full, - 0x3c8b898c3f1353bfull, 0x3fef635beb6fcb75ull, 0x3c957bfb2876ea9eull, 0x3fef5f99f8138a1cull, - 0xbc96d99c7611eb26ull, 0x3fef5be084045cd4ull, 0x3c8cdc1873af2155ull, 0x3fef582f95281c6bull, - 0x3c9aecf73e3a2f60ull, 0x3fef54873168b9aaull, 0xbc9493684653a131ull, 0x3fef50e75eb44027ull, - 0xbc8fe782cb86389dull, 0x3fef4d5022fcd91dull, 0xbc98e2899077520aull, 0x3fef49c18438ce4dull, - 0x3c8a6f4144a6c38dull, 0x3fef463b88628cd6ull, 0x3c9120fcd4f59273ull, 0x3fef42be3578a819ull, - 0x3c807a05b0e4047dull, 0x3fef3f49917ddc96ull, 0x3c89b788c188c9b8ull, 0x3fef3bdda27912d1ull, - 0x3c968efde3a8a894ull, 0x3fef387a6e756238ull, 0x3c877afbca90ef84ull, 0x3fef351ffb82140aull, - 0x3c875e18f274487dull, 0x3fef31ce4fb2a63full, 0x3c91512f082876eeull, 0x3fef2e85711ece75ull, - 0x3c80472b981fe7f2ull, 0x3fef2b4565e27cddull, 0x3c9a02f0c7d75ec6ull, 0x3fef280e341ddf29ull, - 0xbc96b87b3f71085eull, 0x3fef24dfe1f56381ull, 0xbc803297e78260bfull, 0x3fef21ba7591bb70ull, - 0x3c82f7e16d09ab31ull, 0x3fef1e9df51fdee1ull, 0xbc95b77e5ccd9fbfull, 0x3fef1b8a66d10f13ull, - 0xbc3d219b1a6fbffaull, 0x3fef187fd0dad990ull, 0xbc91e75c40b4251eull, 0x3fef157e39771b2full, - 0x3c8b3782720c0ab4ull, 0x3fef1285a6e4030bull, 0x3c98a911f1f7785aull, 0x3fef0f961f641589ull, - 0x3c6e149289cecb8full, 0x3fef0cafa93e2f56ull, 0xbc61e7c998db7dbbull, 0x3fef09d24abd886bull, - 0x3c834d754db0abb6ull, 0x3fef06fe0a31b715ull, 0x3c85425c11faadf4ull, 0x3fef0432edeeb2fdull, - 0x3c864201e2ac744cull, 0x3fef0170fc4cd831ull, 0xbc979517a03e2847ull, 0x3feefeb83ba8ea32ull, - 0x3c8fdd395dd3f84aull, 0x3feefc08b26416ffull, 0xbc800e2a46da4beeull, 0x3feef96266e3fa2dull, - 0xbc86a3803b8e5b04ull, 0x3feef6c55f929ff1ull, 0xbc87430803972b34ull, 0x3feef431a2de883bull, - 0xbc924aedcc4b5068ull, 0x3feef1a7373aa9cbull, 0xbc954de30ae02d94ull, 0x3feeef26231e754aull, - 0xbc9907f81b512d8eull, 0x3feeecae6d05d866ull, 0xbc94f2487e1c03ecull, 0x3feeea401b7140efull, - 0xbc71d1e83e9436d2ull, 0x3feee7db34e59ff7ull, 0x3c914a5432fcb2f4ull, 0x3feee57fbfec6cf4ull, - 0xbc991919b3ce1b15ull, 0x3feee32dc313a8e5ull, 0x3c79c3bba5562a2full, 0x3feee0e544ede173ull, - 0x3c859f48a72a4c6dull, 0x3feedea64c123422ull, 0xbc85a71612e21658ull, 0x3feedc70df1c5175ull, - 0xbc9312607a28698aull, 0x3feeda4504ac801cull, 0x3c86421f6f1d24d6ull, 0x3feed822c367a024ull, - 0xbc58a78f4817895bull, 0x3feed60a21f72e2aull, 0xbc9348a6815fce65ull, 0x3feed3fb2709468aull, - 0xbc7c2c9b67499a1bull, 0x3feed1f5d950a897ull, 0x3c835c43984d9871ull, 0x3feecffa3f84b9d4ull, - 0x3c4363ed60c2ac11ull, 0x3feece086061892dull, 0xbc632afc8d9473a0ull, 0x3feecc2042a7d232ull, - 0x3c9666093b0664efull, 0x3feeca41ed1d0057ull, 0xbc95fc5e44de020eull, 0x3feec86d668b3237ull, - 0x3c6ecce1daa10379ull, 0x3feec6a2b5c13cd0ull, 0xbc7ea0148327c42full, 0x3feec4e1e192aed2ull, - 0x3c93ff8e3f0f1230ull, 0x3feec32af0d7d3deull, 0xbc7a843ad1a88022ull, 0x3feec17dea6db7d7ull, - 0x3c7690cebb7aafb0ull, 0x3feebfdad5362a27ull, 0x3c892ca3bf144e63ull, 0x3feebe41b817c114ull, - 0x3c931dbdeb54e077ull, 0x3feebcb299fddd0dull, 0xbc902c99b04aa8b0ull, 0x3feebb2d81d8abffull, - 0xbc8f94340071a38eull, 0x3feeb9b2769d2ca7ull, 0x3c73e34f67e67118ull, 0x3feeb8417f4531eeull, - 0xbc87deccdc93a349ull, 0x3feeb6daa2cf6642ull, 0xbc75a3b1197ba0f0ull, 0x3feeb57de83f4eefull, - 0xbc78dec6bd0f385full, 0x3feeb42b569d4f82ull, 0x3c81bd2888075068ull, 0x3feeb2e2f4f6ad27ull, - 0xbc861246ec7b5cf6ull, 0x3feeb1a4ca5d920full, 0xbc896be8ae89ef8full, 0x3feeb070dde910d2ull, - 0x3c93350518fdd78eull, 0x3feeaf4736b527daull, 0xbc88e6ac90348602ull, 0x3feeae27dbe2c4cfull, - 0x3c7b98b72f8a9b05ull, 0x3feead12d497c7fdull, 0xbc91af7f1365c3acull, 0x3feeac0827ff07ccull, - 0x3c9063e1e21c5409ull, 0x3feeab07dd485429ull, 0xbc943a3540d1898aull, 0x3feeaa11fba87a03ull, - 0x3c34c7855019c6eaull, 0x3feea9268a5946b7ull, 0xbc951f58ddaa8090ull, 0x3feea84590998b93ull, - 0x3c9432e62b64c035ull, 0x3feea76f15ad2148ull, 0xbc82e1648e50a17cull, 0x3feea6a320dceb71ull, - 0xbc8ce44a6199769full, 0x3feea5e1b976dc09ull, 0x3c95f30eda98a575ull, 0x3feea52ae6cdf6f4ull, - 0xbc8c33c53bef4da8ull, 0x3feea47eb03a5585ull, 0x3c917ecda8a72159ull, 0x3feea3dd1d1929fdull, - 0xbc845378892be9aeull, 0x3feea34634ccc320ull, 0xbc9345f3cee1ae6eull, 0x3feea2b9febc8fb7ull, - 0xbc93cedd78565858ull, 0x3feea23882552225ull, 0xbc85c33fdf910406ull, 0x3feea1c1c70833f6ull, - 0x3c5710aa807e1964ull, 0x3feea155d44ca973ull, 0x3c81079ab5789604ull, 0x3feea0f4b19e9538ull, - 0xbc93b3efbf5e2228ull, 0x3feea09e667f3bcdull, 0x3c727df161cd7778ull, 0x3feea052fa75173eull, - 0xbc6a12ad8734b982ull, 0x3feea012750bdabfull, 0x3c93f9924a05b767ull, 0x3fee9fdcddd47645ull, - 0xbc6367efb86da9eeull, 0x3fee9fb23c651a2full, 0xbc87557939a8b5efull, 0x3fee9f9298593ae5ull, - 0xbc80dc3d54e08851ull, 0x3fee9f7df9519484ull, 0x3c51ed2f56fa9d1aull, 0x3fee9f7466f42e87ull, - 0xbc781f647e5a3ecfull, 0x3fee9f75e8ec5f74ull, 0xbc88e67a9006c909ull, 0x3fee9f8286ead08aull, - 0xbc86ee4ac08b7db0ull, 0x3fee9f9a48a58174ull, 0x3c86597566977ac8ull, 0x3fee9fbd35d7cbfdull, - 0xbc8619321e55e68aull, 0x3fee9feb564267c9ull, 0x3c92c0b7028a5c3aull, 0x3feea024b1ab6e09ull, - 0x3c909ccb5e09d4d3ull, 0x3feea0694fde5d3full, 0x3c8a30faf49cc78cull, 0x3feea0b938ac1cf6ull, - 0xbc7b32dcb94da51dull, 0x3feea11473eb0187ull, 0xbc92dad3519d7b5bull, 0x3feea17b0976cfdbull, - 0x3c94ecfd5467c06bull, 0x3feea1ed0130c132ull, 0x3c87d51410fd15c2ull, 0x3feea26a62ff86f0ull, - 0x3c65ebe1abd66c55ull, 0x3feea2f336cf4e62ull, 0xbc760a3629969871ull, 0x3feea3878491c491ull, - 0xbc88a1c52fb3cf42ull, 0x3feea427543e1a12ull, 0x3c8b18c6e3fdef5dull, 0x3feea4d2add106d9ull, - 0xbc9369b6f13b3734ull, 0x3feea589994cce13ull, 0x3c90ec1ddcb1390aull, 0x3feea64c1eb941f7ull, - 0xbc805e843a19ff1eull, 0x3feea71a4623c7adull, 0xbc522cea4f3afa1eull, 0x3feea7f4179f5b21ull, - 0xbc94d450d872576eull, 0x3feea8d99b4492edull, 0x3c7c88549b958471ull, 0x3feea9cad931a436ull, - 0x3c90ad675b0e8a00ull, 0x3feeaac7d98a6699ull, 0x3c931143962f7877ull, 0x3feeabd0a478580full, - 0x3c8db72fc1f0eab4ull, 0x3feeace5422aa0dbull, 0x3c93e9e96f112479ull, 0x3feeae05bad61778ull, - 0xbc65b6609cc5e7ffull, 0x3feeaf3216b5448cull, 0xbc8dac42a4a38df0ull, 0x3feeb06a5e0866d9ull, - 0x3c7bf68359f35f44ull, 0x3feeb1ae99157736ull, 0x3c8b99dd98b1ed84ull, 0x3feeb2fed0282c8aull, - 0xbc93091fa71e3d83ull, 0x3feeb45b0b91ffc6ull, 0xbc7885ad50cbb750ull, 0x3feeb5c353aa2fe2ull, - 0xbc5da9b88b6c1e29ull, 0x3feeb737b0cdc5e5ull, 0xbc82d5e85f3e0301ull, 0x3feeb8b82b5f98e5ull, - 0xbc6c23f97c90b959ull, 0x3feeba44cbc8520full, 0xbc51669428996971ull, 0x3feebbdd9a7670b3ull, - 0xbc92434322f4f9aaull, 0x3feebd829fde4e50ull, 0x3c71f2b2c1c4c014ull, 0x3feebf33e47a22a2ull, - 0xbc85ca6cd7668e4bull, 0x3feec0f170ca07baull, 0xbc9294f304f166b6ull, 0x3feec2bb4d53fe0dull, - 0x3c71affc2b91ce27ull, 0x3feec49182a3f090ull, 0xbc8a1e58414c07d3ull, 0x3feec674194bb8d5ull, - 0x3c6dd235e10a73bbull, 0x3feec86319e32323ull, 0xbc79740b58a20091ull, 0x3feeca5e8d07f29eull, - 0xbc87c50422622263ull, 0x3feecc667b5de565ull, 0x3c9165830a2b96c2ull, 0x3feece7aed8eb8bbull, - 0x3c8b1c86e3e231d5ull, 0x3feed09bec4a2d33ull, 0xbc903d5cbe27874bull, 0x3feed2c980460ad8ull, - 0xbc91bbd1d3bcbb15ull, 0x3feed503b23e255dull, 0x3c5986178980fce0ull, 0x3feed74a8af46052ull, - 0x3c90cc319cee31d2ull, 0x3feed99e1330b358ull, 0xbc89472975b1f2a5ull, 0x3feedbfe53c12e59ull, - 0x3c8469846e735ab3ull, 0x3feede6b5579fdbfull, 0x3c7d8157a34b7e7full, 0x3feee0e521356ebaull, - 0xbc82dfcd978e9db4ull, 0x3feee36bbfd3f37aull, 0x3c8c8a4e231ebb7dull, 0x3feee5ff3a3c2774ull, - 0x3c8c1a7792cb3387ull, 0x3feee89f995ad3adull, 0xbc888c8d11a142e5ull, 0x3feeeb4ce622f2ffull, - 0xbc907b8f4ad1d9faull, 0x3feeee07298db666ull, 0x3c889c2ea41433c7ull, 0x3feef0ce6c9a8952ull, - 0xbc55c3d956dcaebaull, 0x3feef3a2b84f15fbull, 0xbc7274aedac8ff80ull, 0x3feef68415b749b1ull, - 0xbc90a40e3da6f640ull, 0x3feef9728de5593aull, 0x3c85c620ce76df06ull, 0x3feefc6e29f1c52aull, - 0xbc68d6f438ad9334ull, 0x3feeff76f2fb5e47ull, 0xbc8fda52e1b51e41ull, 0x3fef028cf22749e4ull, - 0xbc91eee26b588a35ull, 0x3fef05b030a1064aull, 0xbc32141a7b3e2cd8ull, 0x3fef08e0b79a6f1full, - 0x3c74ffd70a5fddcdull, 0x3fef0c1e904bc1d2ull, 0xbc302899507554e5ull, 0x3fef0f69c3f3a207ull, - 0xbc91bdfbfa9298acull, 0x3fef12c25bd71e09ull, 0xbc80dda2d4c0010cull, 0x3fef16286141b33dull, - 0x3c736eae30af0cb3ull, 0x3fef199bdd85529cull, 0xbc8a007daadf8d68ull, 0x3fef1d1cd9fa652cull, - 0x3c8ee3325c9ffd94ull, 0x3fef20ab5fffd07aull, 0x3c836909391181d3ull, 0x3fef244778fafb22ull, - 0x3c84e08fd10959acull, 0x3fef27f12e57d14bull, 0xbc811cd7dbdf9547ull, 0x3fef2ba88988c933ull, - 0x3c63cdaf384e1a67ull, 0x3fef2f6d9406e7b5ull, 0xbc7ac28b7bef6621ull, 0x3fef33405751c4dbull, - 0x3c676b2c6c921968ull, 0x3fef3720dcef9069ull, 0xbc7030587207b9e1ull, 0x3fef3b0f2e6d1675ull, - 0xbc808a1883ccb5d2ull, 0x3fef3f0b555dc3faull, 0xbc8cc734592af7fcull, 0x3fef43155b5bab74ull, - 0xbc8fad5d3ffffa6full, 0x3fef472d4a07897cull, 0x3c87752a44f587e8ull, 0x3fef4b532b08c968ull, - 0xbc900dae3875a949ull, 0x3fef4f87080d89f2ull, 0x3c85b66fefeef52eull, 0x3fef53c8eacaa1d6ull, - 0x3c74a385a63d07a7ull, 0x3fef5818dcfba487ull, 0x3c5159d9d908a96eull, 0x3fef5c76e862e6d3ull, - 0xbc82919e2040220full, 0x3fef60e316c98398ull, 0x3c8c254d16117a68ull, 0x3fef655d71ff6075ull, - 0x3c8e5a50d5c192acull, 0x3fef69e603db3285ull, 0xbc8d8c329fbd0e03ull, 0x3fef6e7cd63a8315ull, - 0x3c843a59ac016b4bull, 0x3fef7321f301b460ull, 0xbc8ea6e6fbd5f2a6ull, 0x3fef77d5641c0658ull, - 0xbc82d52107b43e1full, 0x3fef7c97337b9b5full, 0xbc63e8e3eab2cbb4ull, 0x3fef81676b197d17ull, - 0xbc892ab93b470dc9ull, 0x3fef864614f5a129ull, 0xbc8b7966cd0d2cd9ull, 0x3fef8b333b16ee12ull, - 0x3c74b604603a88d3ull, 0x3fef902ee78b3ff6ull, 0xbc776caa4c2ff1cfull, 0x3fef953924676d76ull, - 0x3c83c5ec519d7271ull, 0x3fef9a51fbc74c83ull, 0xbc81d5fc525d9940ull, 0x3fef9f7977cdb740ull, - 0xbc8ff7128fd391f0ull, 0x3fefa4afa2a490daull, 0x3c855cd8aaea3d21ull, 0x3fefa9f4867cca6eull, - 0xbc8dae98e223747dull, 0x3fefaf482d8e67f1ull, 0x3c8269947c2bed4aull, 0x3fefb4aaa2188510ull, - 0x3c8ec3bc41aa2008ull, 0x3fefba1bee615a27ull, 0xbc83b6137e9afe9eull, 0x3fefbf9c1cb6412aull, - 0x3c842b94c3a9eb32ull, 0x3fefc52b376bba97ull, 0xbc69fa74878ba7c7ull, 0x3fefcac948dd7274ull, - 0x3c8a64a931d185eeull, 0x3fefd0765b6e4540ull, 0x3c901f3a75ee0efeull, 0x3fefd632798844f8ull, - 0xbc8e37bae43be3edull, 0x3fefdbfdad9cbe14ull, 0xbc516a9ce6ed84faull, 0x3fefe1d802243c89ull, - 0x3c77893b4d91cd9dull, 0x3fefe7c1819e90d8ull, 0xbc699c7db2effc76ull, 0x3fefedba3692d514ull, - 0x3c5305c14160cc89ull, 0x3feff3c22b8f71f1ull, 0x3c64b458677f9840ull, 0x3feff9d96b2a23d9ull, -}; -#ifndef ALTD_MATH_WGSL -ALTD_MATH_CONST int UCRT_SINF_COUNT = 786; -ALTD_MATH_CONST int UCRT_SINF_STEP = 512; -ALTD_MATH_CONST int UCRT_SINF_WIDE_COUNT = 2536; -ALTD_MATH_CONST int UCRT_SINF_WIDE_STEP = 2048; -ALTD_MATH_CONST int UCRT_COSF_COUNT = 703; -ALTD_MATH_CONST int UCRT_COSF_STEP = 512; -ALTD_MATH_CONST int UCRT_COSF_WIDE_COUNT = 2424; -ALTD_MATH_CONST int UCRT_COSF_WIDE_STEP = 2048; -// log (both groups): 1/F, and log(F) in lead and tail parts, over 257 intervals -ALTD_MATH_TABLE uint64_t UCRT_LOG_INV[257] = { - 0x4000000000000000ull, 0x3fffe01fe01fe020ull, 0x3fffc07f01fc07f0ull, 0x3fffa11caa01fa12ull, - 0x3fff81f81f81f820ull, 0x3fff6310aca0dbb5ull, 0x3fff44659e4a4271ull, 0x3fff25f644230ab5ull, - 0x3fff07c1f07c1f08ull, 0x3ffee9c7f8458e02ull, 0x3ffecc07b301ecc0ull, 0x3ffeae807aba01ebull, - 0x3ffe9131abf0b767ull, 0x3ffe741aa59750e4ull, 0x3ffe573ac901e574ull, 0x3ffe3a9179dc1a73ull, - 0x3ffe1e1e1e1e1e1eull, 0x3ffe01e01e01e01eull, 0x3ffde5d6e3f8868aull, 0x3ffdca01dca01dcaull, - 0x3ffdae6076b981dbull, 0x3ffd92f2231e7f8aull, 0x3ffd77b654b82c34ull, 0x3ffd5cac807572b2ull, - 0x3ffd41d41d41d41dull, 0x3ffd272ca3fc5b1aull, 0x3ffd0cb58f6ec074ull, 0x3ffcf26e5c44bfc6ull, - 0x3ffcd85689039b0bull, 0x3ffcbe6d9601cbe7ull, 0x3ffca4b3055ee191ull, 0x3ffc8b265afb8a42ull, - 0x3ffc71c71c71c71cull, 0x3ffc5894d10d4986ull, 0x3ffc3f8f01c3f8f0ull, 0x3ffc26b5392ea01cull, - 0x3ffc0e070381c0e0ull, 0x3ffbf583ee868d8bull, 0x3ffbdd2b899406f7ull, 0x3ffbc4fd65883e7bull, - 0x3ffbacf914c1bad0ull, 0x3ffb951e2b18ff23ull, 0x3ffb7d6c3dda338bull, 0x3ffb65e2e3beee05ull, - 0x3ffb4e81b4e81b4full, 0x3ffb37484ad806ceull, 0x3ffb2036406c80d9ull, 0x3ffb094b31d922a4ull, - 0x3ffaf286bca1af28ull, 0x3ffadbe87f94905eull, 0x3ffac5701ac5701bull, 0x3ffaaf1d2f87ebfdull, - 0x3ffa98ef606a63beull, 0x3ffa82e65130e159ull, 0x3ffa6d01a6d01a6dull, 0x3ffa574107688a4aull, - 0x3ffa41a41a41a41aull, 0x3ffa2c2a87c51ca0ull, 0x3ffa16d3f97a4b02ull, 0x3ffa01a01a01a01aull, - 0x3ff9ec8e951033d9ull, 0x3ff9d79f176b682dull, 0x3ff9c2d14ee4a102ull, 0x3ff9ae24ea5510daull, - 0x3ff999999999999aull, 0x3ff9852f0d8ec0ffull, 0x3ff970e4f80cb872ull, 0x3ff95cbb0be377aeull, - 0x3ff948b0fcd6e9e0ull, 0x3ff934c67f9b2ce6ull, 0x3ff920fb49d0e229ull, 0x3ff90d4f120190d5ull, - 0x3ff8f9c18f9c18faull, 0x3ff8e6527af1373full, 0x3ff8d3018d3018d3ull, 0x3ff8bfce8062ff3aull, - 0x3ff8acb90f6bf3aaull, 0x3ff899c0f601899cull, 0x3ff886e5f0abb04aull, 0x3ff87427bcc092b9ull, - 0x3ff8618618618618ull, 0x3ff84f00c2780614ull, 0x3ff83c977ab2beddull, 0x3ff82a4a0182a4a0ull, - 0x3ff8181818181818ull, 0x3ff8060180601806ull, 0x3ff7f405fd017f40ull, 0x3ff7e225515a4f1dull, - 0x3ff7d05f417d05f4ull, 0x3ff7beb3922e017cull, 0x3ff7ad2208e0ecc3ull, 0x3ff79baa6bb6398bull, - 0x3ff78a4c8178a4c8ull, 0x3ff77908119ac60dull, 0x3ff767dce434a9b1ull, 0x3ff756cac201756dull, - 0x3ff745d1745d1746ull, 0x3ff734f0c541fe8dull, 0x3ff724287f46debcull, 0x3ff713786d9c7c09ull, - 0x3ff702e05c0b8170ull, 0x3ff6f26016f26017ull, 0x3ff6e1f76b4337c7ull, 0x3ff6d1a62681c861ull, - 0x3ff6c16c16c16c17ull, 0x3ff6b1490aa31a3dull, 0x3ff6a13cd1537290ull, 0x3ff691473a88d0c0ull, - 0x3ff6816816816817ull, 0x3ff6719f3601671aull, 0x3ff661ec6a5122f9ull, 0x3ff6524f853b4aa3ull, - 0x3ff642c8590b2164ull, 0x3ff63356b88ac0deull, 0x3ff623fa77016240ull, 0x3ff614b36831ae94ull, - 0x3ff6058160581606ull, 0x3ff5f66434292dfcull, 0x3ff5e75bb8d015e7ull, 0x3ff5d867c3ece2a5ull, - 0x3ff5c9882b931057ull, 0x3ff5babcc647fa91ull, 0x3ff5ac056b015ac0ull, 0x3ff59d61f123ccaaull, - 0x3ff58ed2308158edull, 0x3ff5805601580560ull, 0x3ff571ed3c506b3aull, 0x3ff56397ba7c52e2ull, - 0x3ff5555555555555ull, 0x3ff54725e6bb82feull, 0x3ff5390948f40febull, 0x3ff52aff56a8054bull, - 0x3ff51d07eae2f815ull, 0x3ff50f22e111c4c5ull, 0x3ff5015015015015ull, 0x3ff4f38f62dd4c9bull, - 0x3ff4e5e0a72f0539ull, 0x3ff4d843bedc2c4cull, 0x3ff4cab88725af6eull, 0x3ff4bd3edda68fe1ull, - 0x3ff4afd6a052bf5bull, 0x3ff4a27fad76014aull, 0x3ff49539e3b2d067ull, 0x3ff4880522014880ull, - 0x3ff47ae147ae147bull, 0x3ff46dce34596066ull, 0x3ff460cbc7f5cf9aull, 0x3ff453d9e2c776caull, - 0x3ff446f86562d9fbull, 0x3ff43a2730abee4dull, 0x3ff42d6625d51f87ull, 0x3ff420b5265e5951ull, - 0x3ff4141414141414ull, 0x3ff40782d10e6566ull, 0x3ff3fb013fb013fbull, 0x3ff3ee8f42a5af07ull, - 0x3ff3e22cbce4a902ull, 0x3ff3d5d991aa75c6ull, 0x3ff3c995a47babe7ull, 0x3ff3bd60d9232955ull, - 0x3ff3b13b13b13b14ull, 0x3ff3a524387ac822ull, 0x3ff3991c2c187f63ull, 0x3ff38d22d366088eull, - 0x3ff3813813813814ull, 0x3ff3755bd1c945eeull, 0x3ff3698df3de0748ull, 0x3ff35dce5f9f2af8ull, - 0x3ff3521cfb2b78c1ull, 0x3ff34679ace01346ull, 0x3ff33ae45b57bcb2ull, 0x3ff32f5ced6a1dfaull, - 0x3ff323e34a2b10bfull, 0x3ff3187758e9ebb6ull, 0x3ff30d190130d190ull, 0x3ff301c82ac40260ull, - 0x3ff2f684bda12f68ull, 0x3ff2eb4ea1fed14bull, 0x3ff2e025c04b8097ull, 0x3ff2d50a012d50a0ull, - 0x3ff2c9fb4d812ca0ull, 0x3ff2bef98e5a3711ull, 0x3ff2b404ad012b40ull, 0x3ff2a91c92f3c105ull, - 0x3ff29e4129e4129eull, 0x3ff293725bb804a5ull, 0x3ff288b01288b013ull, 0x3ff27dfa38a1ce4dull, - 0x3ff27350b8812735ull, 0x3ff268b37cd60127ull, 0x3ff25e22708092f1ull, 0x3ff2539d7e9177b2ull, - 0x3ff2492492492492ull, 0x3ff23eb79717605bull, 0x3ff23456789abcdfull, 0x3ff22a0122a0122aull, - 0x3ff21fb78121fb78ull, 0x3ff21579804855e6ull, 0x3ff20b470c67c0d9ull, 0x3ff2012012012012ull, - 0x3ff1f7047dc11f70ull, 0x3ff1ecf43c7fb84cull, 0x3ff1e2ef3b3fb874ull, 0x3ff1d8f5672e4abdull, - 0x3ff1cf06ada2811dull, 0x3ff1c522fc1ce059ull, 0x3ff1bb4a4046ed29ull, 0x3ff1b17c67f2bae3ull, - 0x3ff1a7b9611a7b96ull, 0x3ff19e0119e0119eull, 0x3ff19453808ca29cull, 0x3ff18ab083902bdbull, - 0x3ff1811811811812ull, 0x3ff1778a191bd684ull, 0x3ff16e0689427379ull, 0x3ff1648d50fc3201ull, - 0x3ff15b1e5f75270dull, 0x3ff151b9a3fdd5c9ull, 0x3ff1485f0e0acd3bull, 0x3ff13f0e8d344724ull, - 0x3ff135c81135c811ull, 0x3ff12c8b89edc0acull, 0x3ff12358e75d3033ull, 0x3ff11a3019a74826ull, - 0x3ff1111111111111ull, 0x3ff107fbbe011080ull, 0x3ff0fef010fef011ull, 0x3ff0f5edfab325a2ull, - 0x3ff0ecf56be69c90ull, 0x3ff0e40655826011ull, 0x3ff0db20a88f4696ull, 0x3ff0d24456359e3aull, - 0x3ff0c9714fbcda3bull, 0x3ff0c0a7868b4171ull, 0x3ff0b7e6ec259dc8ull, 0x3ff0af2f722eecb5ull, - 0x3ff0a6810a6810a7ull, 0x3ff09ddba6af8360ull, 0x3ff0953f39010954ull, 0x3ff08cabb37565e2ull, - 0x3ff0842108421084ull, 0x3ff07b9f29b8eae2ull, 0x3ff073260a47f7c6ull, 0x3ff06ab59c7912fbull, - 0x3ff0624dd2f1a9fcull, 0x3ff059eea0727586ull, 0x3ff05197f7d73404ull, 0x3ff04949cc1664c5ull, - 0x3ff0410410410410ull, 0x3ff038c6b78247fcull, 0x3ff03091b51f5e1aull, 0x3ff02864fc7729e9ull, - 0x3ff0204081020408ull, 0x3ff0182436517a37ull, 0x3ff0101010101010ull, 0x3ff0080402010080ull, - 0x3ff0000000000000ull, -}; -ALTD_MATH_TABLE uint64_t UCRT_LOG_LEAD[257] = { - 0x0000000000000000ull, 0x3f6ff00aa0000000ull, 0x3f7fe02a60000000ull, 0x3f87dc4750000000ull, - 0x3f8fc0a8b0000000ull, 0x3f93cea440000000ull, 0x3f97b91b00000000ull, 0x3f9b9fc020000000ull, - 0x3f9f829b00000000ull, 0x3fa1b0d980000000ull, 0x3fa39e87b0000000ull, 0x3fa58a5ba0000000ull, - 0x3fa77458f0000000ull, 0x3fa95c8300000000ull, 0x3fab42dd70000000ull, 0x3fad276b80000000ull, - 0x3faf0a30c0000000ull, 0x3fb0759830000000ull, 0x3fb16536e0000000ull, 0x3fb253f620000000ull, - 0x3fb341d790000000ull, 0x3fb42edcb0000000ull, 0x3fb51b0730000000ull, 0x3fb60658a0000000ull, - 0x3fb6f0d280000000ull, 0x3fb7da7660000000ull, 0x3fb8c345d0000000ull, 0x3fb9ab4240000000ull, - 0x3fba926d30000000ull, 0x3fbb78c820000000ull, 0x3fbc5e5480000000ull, 0x3fbd4313d0000000ull, - 0x3fbe270760000000ull, 0x3fbf0a30c0000000ull, 0x3fbfec9130000000ull, 0x3fc0671510000000ull, - 0x3fc0d77e70000000ull, 0x3fc1478580000000ull, 0x3fc1b72ad0000000ull, 0x3fc2266f10000000ull, - 0x3fc29552f0000000ull, 0x3fc303d710000000ull, 0x3fc371fc20000000ull, 0x3fc3dfc2b0000000ull, - 0x3fc44d2b60000000ull, 0x3fc4ba36f0000000ull, 0x3fc526e5e0000000ull, 0x3fc59338d0000000ull, - 0x3fc5ff3070000000ull, 0x3fc66acd40000000ull, 0x3fc6d60fe0000000ull, 0x3fc740f8f0000000ull, - 0x3fc7ab8900000000ull, 0x3fc815c0a0000000ull, 0x3fc87fa060000000ull, 0x3fc8e928d0000000ull, - 0x3fc9525a90000000ull, 0x3fc9bb3620000000ull, 0x3fca23bc10000000ull, 0x3fca8becf0000000ull, - 0x3fcaf3c940000000ull, 0x3fcb5b5190000000ull, 0x3fcbc28670000000ull, 0x3fcc296850000000ull, - 0x3fcc8ff7c0000000ull, 0x3fccf63540000000ull, 0x3fcd5c2160000000ull, 0x3fcdc1bca0000000ull, - 0x3fce270760000000ull, 0x3fce8c0250000000ull, 0x3fcef0adc0000000ull, 0x3fcf550a50000000ull, - 0x3fcfb91860000000ull, 0x3fd00e6c40000000ull, 0x3fd0402590000000ull, 0x3fd071b850000000ull, - 0x3fd0a324e0000000ull, 0x3fd0d46b50000000ull, 0x3fd1058bf0000000ull, 0x3fd1368700000000ull, - 0x3fd1675ca0000000ull, 0x3fd1980d20000000ull, 0x3fd1c898c0000000ull, 0x3fd1f8ff90000000ull, - 0x3fd22941f0000000ull, 0x3fd2596010000000ull, 0x3fd2895a10000000ull, 0x3fd2b93030000000ull, - 0x3fd2e8e2b0000000ull, 0x3fd31871c0000000ull, 0x3fd347dd90000000ull, 0x3fd3772660000000ull, - 0x3fd3a64c50000000ull, 0x3fd3d54fa0000000ull, 0x3fd4043080000000ull, 0x3fd432ef20000000ull, - 0x3fd4618bc0000000ull, 0x3fd4900680000000ull, 0x3fd4be5f90000000ull, 0x3fd4ec9730000000ull, - 0x3fd51aad80000000ull, 0x3fd548a2c0000000ull, 0x3fd5767710000000ull, 0x3fd5a42ab0000000ull, - 0x3fd5d1bdb0000000ull, 0x3fd5ff3070000000ull, 0x3fd62c82f0000000ull, 0x3fd659b570000000ull, - 0x3fd686c810000000ull, 0x3fd6b3bb20000000ull, 0x3fd6e08ea0000000ull, 0x3fd70d42e0000000ull, - 0x3fd739d7f0000000ull, 0x3fd7664e10000000ull, 0x3fd792a550000000ull, 0x3fd7bede00000000ull, - 0x3fd7eaf830000000ull, 0x3fd816f410000000ull, 0x3fd842d1d0000000ull, 0x3fd86e9190000000ull, - 0x3fd89a3380000000ull, 0x3fd8c5b7c0000000ull, 0x3fd8f11e80000000ull, 0x3fd91c67e0000000ull, - 0x3fd9479410000000ull, 0x3fd972a340000000ull, 0x3fd99d9580000000ull, 0x3fd9c86b00000000ull, - 0x3fd9f323e0000000ull, 0x3fda1dc060000000ull, 0x3fda484090000000ull, 0x3fda72a490000000ull, - 0x3fda9cec90000000ull, 0x3fdac718c0000000ull, 0x3fdaf12930000000ull, 0x3fdb1b1e00000000ull, - 0x3fdb44f770000000ull, 0x3fdb6eb590000000ull, 0x3fdb985890000000ull, 0x3fdbc1e080000000ull, - 0x3fdbeb4d90000000ull, 0x3fdc149ff0000000ull, 0x3fdc3dd7a0000000ull, 0x3fdc66f4e0000000ull, - 0x3fdc8ff7c0000000ull, 0x3fdcb8e070000000ull, 0x3fdce1af00000000ull, 0x3fdd0a63a0000000ull, - 0x3fdd32fe70000000ull, 0x3fdd5b7f90000000ull, 0x3fdd83e720000000ull, 0x3fddac3530000000ull, - 0x3fddd46a00000000ull, 0x3fddfc8590000000ull, 0x3fde248810000000ull, 0x3fde4c71a0000000ull, - 0x3fde744260000000ull, 0x3fde9bfa60000000ull, 0x3fdec399d0000000ull, 0x3fdeeb20c0000000ull, - 0x3fdf128f50000000ull, 0x3fdf39e5b0000000ull, 0x3fdf6123f0000000ull, 0x3fdf884a30000000ull, - 0x3fdfaf5880000000ull, 0x3fdfd64f20000000ull, 0x3fdffd2e00000000ull, 0x3fe011fab0000000ull, - 0x3fe02552a0000000ull, 0x3fe0389ee0000000ull, 0x3fe04bdf90000000ull, 0x3fe05f14b0000000ull, - 0x3fe0723e50000000ull, 0x3fe0855c80000000ull, 0x3fe0986f40000000ull, 0x3fe0ab76b0000000ull, - 0x3fe0be72e0000000ull, 0x3fe0d163c0000000ull, 0x3fe0e44980000000ull, 0x3fe0f72410000000ull, - 0x3fe109f390000000ull, 0x3fe11cb810000000ull, 0x3fe12f7190000000ull, 0x3fe1422020000000ull, - 0x3fe154c3d0000000ull, 0x3fe1675ca0000000ull, 0x3fe179eab0000000ull, 0x3fe18c6e00000000ull, - 0x3fe19ee6b0000000ull, 0x3fe1b154b0000000ull, 0x3fe1c3b810000000ull, 0x3fe1d610f0000000ull, - 0x3fe1e85f50000000ull, 0x3fe1faa340000000ull, 0x3fe20cdcd0000000ull, 0x3fe21f0bf0000000ull, - 0x3fe23130d0000000ull, 0x3fe2434b60000000ull, 0x3fe2555bc0000000ull, 0x3fe2676200000000ull, - 0x3fe2795e10000000ull, 0x3fe28b5000000000ull, 0x3fe29d37f0000000ull, 0x3fe2af15f0000000ull, - 0x3fe2c0e9e0000000ull, 0x3fe2d2b400000000ull, 0x3fe2e47430000000ull, 0x3fe2f62a90000000ull, - 0x3fe307d730000000ull, 0x3fe3197a00000000ull, 0x3fe32b1330000000ull, 0x3fe33ca2b0000000ull, - 0x3fe34e2890000000ull, 0x3fe35fa4e0000000ull, 0x3fe37117b0000000ull, 0x3fe38280f0000000ull, - 0x3fe393e0d0000000ull, 0x3fe3a53730000000ull, 0x3fe3b68440000000ull, 0x3fe3c7c7f0000000ull, - 0x3fe3d90260000000ull, 0x3fe3ea3390000000ull, 0x3fe3fb5b80000000ull, 0x3fe40c7a40000000ull, - 0x3fe41d8fe0000000ull, 0x3fe42e9c60000000ull, 0x3fe43f9fe0000000ull, 0x3fe4509a50000000ull, - 0x3fe4618bc0000000ull, 0x3fe4727430000000ull, 0x3fe48353d0000000ull, 0x3fe4942a80000000ull, - 0x3fe4a4f850000000ull, 0x3fe4b5bd60000000ull, 0x3fe4c679a0000000ull, 0x3fe4d72d30000000ull, - 0x3fe4e7d810000000ull, 0x3fe4f87a30000000ull, 0x3fe50913c0000000ull, 0x3fe519a4c0000000ull, - 0x3fe52a2d20000000ull, 0x3fe53aad00000000ull, 0x3fe54b2460000000ull, 0x3fe55b9350000000ull, - 0x3fe56bf9d0000000ull, 0x3fe57c57f0000000ull, 0x3fe58cadb0000000ull, 0x3fe59cfb20000000ull, - 0x3fe5ad4040000000ull, 0x3fe5bd7d30000000ull, 0x3fe5cdb1d0000000ull, 0x3fe5ddde50000000ull, - 0x3fe5ee02a0000000ull, 0x3fe5fe1ed0000000ull, 0x3fe60e32f0000000ull, 0x3fe61e3ef0000000ull, - 0x3fe62e42e0000000ull, -}; -ALTD_MATH_TABLE uint64_t UCRT_LOG_TAIL[257] = { - 0x0000000000000000ull, 0x3db5885e0250435aull, 0x3de620cf11f86ed2ull, 0x3dff0214edba4a25ull, - 0x3dbf807c79f3db4eull, 0x3dea352ba779a52bull, 0x3dff56c46aa49fd5ull, 0x3dfebe465fef5196ull, - 0x3e0cf0660099f1f8ull, 0x3e1247b2ff85945dull, 0x3e13fd7abf5202b6ull, 0x3e1f91c9a918d51eull, - 0x3e08cb73f118d3caull, 0x3e1d91c7d6fad074ull, 0x3de1971bec28d14cull, 0x3e15b616a423c78aull, - 0x3da162a6617cc971ull, 0x3e166391c4c06d29ull, 0x3e2d46f5c1d0c4b8ull, 0x3e2e14282df1f6d3ull, - 0x3e186f47424a660dull, 0x3e2d4c8de077753eull, 0x3e2e0c307ed24f1cull, 0x3e226ea18763bdd3ull, - 0x3e25cad69737c933ull, 0x3e2af62599088901ull, 0x3e18c66c83d6b2d0ull, 0x3e1880ceb36fb30full, - 0x3e2495aac6ca17a4ull, 0x3e2761db4210878cull, 0x3e2eb78e862bac2full, 0x3e19b2cd75790dd9ull, - 0x3e2c55e5cbd3d50full, 0x3db162a6617cc971ull, 0x3dfdbeabaaa2e519ull, 0x3e1652cb7150c647ull, - 0x3e39a11cb2cd2ee2ull, 0x3e219d0ab1a28813ull, 0x3e24bd9e80a41811ull, 0x3e3214b596faa3dfull, - 0x3e303fea46980bb8ull, 0x3e31c8ffa5fd28c7ull, 0x3dce8f743bcd96c5ull, 0x3dfd98c5395315c6ull, - 0x3e3996fa3ccfa7b2ull, 0x3e1cd2af2ad13037ull, 0x3e1d0da1bd17200eull, 0x3e3330410ba68b75ull, - 0x3df4f27a790e7c41ull, 0x3e13956a86f6ff1bull, 0x3e2c6748723551d9ull, 0x3e2500de9326cdfcull, - 0x3e1086c848df1b59ull, 0x3e04357ead6836ffull, 0x3e24832442408024ull, 0x3e3d10da8154b13dull, - 0x3e39e8ad68ec8260ull, 0x3e3cfbf706abaf18ull, 0x3e3fc56ac6326e23ull, 0x3e39105e3185cf21ull, - 0x3e3d017fe5b19cc0ull, 0x3e3d1f6b48dd13feull, 0x3e20b63358a7e73aull, 0x3e263063028c211cull, - 0x3e2e6a6886b09760ull, 0x3e3c138bb891cd03ull, 0x3e369f7722b7221aull, 0x3df57d8fac1a628cull, - 0x3e3c55e5cbd3d50full, 0x3e1552d2ff48fe2eull, 0x3e37b8b26ca431bcull, 0x3e292decdc1c5f6dull, - 0x3e3abc7c551aaa8cull, 0x3e36b540731a354bull, 0x3e32d341036b89efull, 0x3e4f9ab21a3a2e0full, - 0x3e239c871afb9fbdull, 0x3e3e6add2c81f640ull, 0x3e435c95aa313f41ull, 0x3e249d4582f6cc53ull, - 0x3e47574c1c07398full, 0x3e4ba846dece9e8dull, 0x3e16999fafbc68e7ull, 0x3e4c9145e51b0103ull, - 0x3e479ef2cb44850aull, 0x3e0beec73de11275ull, 0x3e2ef4351af5a498ull, 0x3e45713a493b4a50ull, - 0x3e45c23a61385992ull, 0x3e42a88309f57299ull, 0x3e4530faa9ac8aceull, 0x3e25fec2d792a758ull, - 0x3e35a517a71cbcd7ull, 0x3e3707dc3e1cd9a3ull, 0x3e3a1a9f8ef43049ull, 0x3e4409d0276b3674ull, - 0x3e20e2f613e85bd9ull, 0x3df0027433001e5full, 0x3e35dde2836d3265ull, 0x3e2300134d7aaf04ull, - 0x3e3cb7e0b42724f5ull, 0x3e2d6e93167e6308ull, 0x3e3d1569b1526adbull, 0x3e0e99fc338a1a41ull, - 0x3e4eb01394a11b1cull, 0x3e04f27a790e7c41ull, 0x3e25ce3ca97b7af9ull, 0x3e281f0f940ed857ull, - 0x3e4d36295d88857cull, 0x3e21aca1ec4af526ull, 0x3e445743c7182726ull, 0x3e23c491aead337eull, - 0x3e3aef401a738931ull, 0x3e21cede76092a29ull, 0x3e4fba8f44f82bb4ull, 0x3e446f5f7f3c3e1aull, - 0x3e47055f86c9674bull, 0x3e4b41a92b6b6e1aull, 0x3e443d162e927628ull, 0x3e4466174013f9b1ull, - 0x3e3b05096ad69c62ull, 0x3e40b169150faa58ull, 0x3e3cd98b1df85da7ull, 0x3e468b507b0f8fa8ull, - 0x3e48422df57499baull, 0x3e11351586970274ull, 0x3e117e08acba92eeull, 0x3e26e04314dd0229ull, - 0x3e497f3097e56d1aull, 0x3e3356e655901286ull, 0x3e0cb761457f94d6ull, 0x3e39af67a85a9dacull, - 0x3e453410931a909full, 0x3e22c587206058f5ull, 0x3e223bc358899c22ull, 0x3e4d7bf8b6d223cbull, - 0x3e47991ec5197ddbull, 0x3e4a79e6bb3a9219ull, 0x3e3a4c43ed663ec5ull, 0x3e461b5a1484f438ull, - 0x3e4b4e36f7ef0c3aull, 0x3e115f026acd0d1bull, 0x3e3f36b535cecf05ull, 0x3e2ffb7fbf3eb5c6ull, - 0x3e3e6a6886b09760ull, 0x3e3135eb27f5bbc3ull, 0x3e470be7d6f6fa57ull, 0x3e4ce43cc84ab338ull, - 0x3e4c01d7aac3bd91ull, 0x3e45c58d07961060ull, 0x3e3628bcf941456eull, 0x3e4c58b2a8461cd2ull, - 0x3e33071282fb989aull, 0x3e420dab6a80f09cull, 0x3e44f8d84c397b1eull, 0x3e40d0ee08599e48ull, - 0x3e1d68787e37da36ull, 0x3e366187d591bafcull, 0x3e22346600bae772ull, 0x3e390377d0d61b8eull, - 0x3e4f5e0dd966b907ull, 0x3e49023cb79a00e2ull, 0x3e44e05158c28ad8ull, 0x3e3bfa7b08b18ae4ull, - 0x3e4ef1e63db35f67ull, 0x3e0ec2ae39493d4full, 0x3e40afe930ab2fa0ull, 0x3e225ff8a1810dd4ull, - 0x3e469743fb1a71a5ull, 0x3e5f9cc676785571ull, 0x3e5b524da4cbf982ull, 0x3e5a4c8b381535b8ull, - 0x3e5839be809caf2cull, 0x3e50968a1cb82c13ull, 0x3e5eae6a41723fb5ull, 0x3e5d9c29a380a4dbull, - 0x3e4094aa0ada625eull, 0x3e5973ad6fc108caull, 0x3e4747322fdbab97ull, 0x3e593692fa9d4221ull, - 0x3e5c5a992dfbc7d9ull, 0x3e4e1f33e102387aull, 0x3e464fbef14c048cull, 0x3e4490f513ca5e3bull, - 0x3e37a6af4d4c799dull, 0x3e57574c1c07398full, 0x3e57b133417f8c1cull, 0x3e5feb9e0c176514ull, - 0x3e419f25bb3172f7ull, 0x3e45f68a7bbfb852ull, 0x3e5ee278497929f1ull, 0x3e5ccee006109d58ull, - 0x3e5ce081a07bd8b3ull, 0x3e570e12981817b8ull, 0x3e292ab6d93503d0ull, 0x3e58cb7dd7c3b61eull, - 0x3e4efafd0a0b78daull, 0x3e5e907267c4288eull, 0x3e5d31ef96780875ull, 0x3e23430dfcd2ad50ull, - 0x3e344d88d75bc1f9ull, 0x3e5bec0f055e04fcull, 0x3e5d85611590b9adull, 0x3df320568e583229ull, - 0x3e5a891d1772f538ull, 0x3e22edc9dabba74dull, 0x3e4b9009a1015086ull, 0x3e52a12a8c5b1a19ull, - 0x3e3a7885f0fdac85ull, 0x3e5f4ffcd43ac691ull, 0x3e52243ae2640aadull, 0x3e546513299035d3ull, - 0x3e5b39c3a62dd725ull, 0x3e5ba6dd40049f51ull, 0x3e451d1ed7177409ull, 0x3e5cb0f2fd7f5216ull, - 0x3e3ab150cd4e2213ull, 0x3e5cfd7bf3193844ull, 0x3e53fff8455f1dbdull, 0x3e5fee640b905fc9ull, - 0x3e54e2adf548084cull, 0x3e3b597adc1ecdd2ull, 0x3e4345bd096d3a75ull, 0x3e5101b9d2453c8bull, - 0x3e508ce55cc8c979ull, 0x3e5bbf017e595f71ull, 0x3e37ce733bd393dcull, 0x3e233bb0a503f8a1ull, - 0x3e30e2f613e85bd9ull, 0x3e5e67555a635b3cull, 0x3e2ea88df73d5e8bull, 0x3e3d17e03bda18a8ull, - 0x3e5b607d76044f7eull, 0x3e52adc4e71bc2fcull, 0x3e5f99dc7362d1d9ull, 0x3e5473fa008e6a6aull, - 0x3e2b75bb09cb0985ull, 0x3e5ea04dd10b9abaull, 0x3e5802d0d6979674ull, 0x3e174688ccd99094ull, - 0x3e496f16abb9df22ull, 0x3e46e66df2aa374full, 0x3e4e66525ea4550aull, 0x3e42d02f34f20cbdull, - 0x3e46cfce65047188ull, 0x3e39b78c842d58b8ull, 0x3e4735e624c24bc9ull, 0x3e47eba1f7dd1adfull, - 0x3e586b3e59f65355ull, 0x3e1ce38e637f1b4dull, 0x3e58d82ec919edc7ull, 0x3e4c52648ddcfa37ull, - 0x3e52482ceae1ac12ull, 0x3e55a312311aba4full, 0x3e411e236329f225ull, 0x3e5b48c8cd2f246cull, - 0x3e6efa39ef35793cull, -}; -// sinf: musl's result is wrong by 1 ulp for these |x| bits; values are the bits of sinf(|x|), negated for negative x. |x| < 0x4dc90fdb -ALTD_MATH_TABLE uint32_t UCRT_SINF_KEYS[786] = { - 0x3fc810bbu, 0x3fc8127eu, 0x3fc81508u, 0x3fc818a1u, 0x3fc81a30u, 0x3fc81d99u, - 0x3fc82041u, 0x3fc820cau, 0x3fc82406u, 0x3fc82794u, 0x3fc82868u, 0x3fc829cbu, - 0x3fc82aa1u, 0x3fc82f24u, 0x3fc82f6du, 0x3fc82fb6u, 0x3fc831b8u, 0x3fc8332au, - 0x3fc836acu, 0x3fc83dddu, 0x3fc83f16u, 0x3fc84002u, 0x3fc840efu, 0x3fc84737u, - 0x3fc84db3u, 0x3fc8588du, 0x3fc86718u, 0x3fc86aeeu, 0x3fc89977u, 0x3fc972cfu, - 0x3fc97418u, 0x3fc989f8u, 0x3fc9957bu, 0x3fc9a0f6u, 0x3fc9aac6u, 0x3fc9ac02u, - 0x3fc9b338u, 0x3fc9b779u, 0x3fc9b95fu, 0x3fc9ba80u, 0x3fc9bd1bu, 0x3fc9cfb0u, - 0x3fc9d3fau, 0x3fc9d4f4u, 0x3fc9d59au, 0x3fc9db58u, 0x3fc9dc49u, 0x3fc9dce9u, - 0x3fc9de77u, 0x3fc9e13cu, 0x3fc9ec41u, 0x3fc9ee47u, 0x3fc9f5a3u, 0x3fc9f794u, - 0x3fc9f9c7u, 0x3fc9fa0du, 0x3fca0041u, 0x3fca0151u, 0x3fca0437u, 0x3fca047au, - 0x3fca05c8u, 0x3fca09e9u, 0x3fca0a6cu, 0x3fca0b71u, 0x3fca0bb2u, 0x3fca0c34u, - 0x3fca0c75u, 0x3fca0df9u, 0x3fca0ebau, 0x404892a4u, 0x40489b07u, 0x40495eeeu, - 0x4049816fu, 0x40498b57u, 0x40498e3au, 0x40968c0cu, 0x40968c9du, 0x40968d1fu, - 0x40968d50u, 0x40968e58u, 0x40968e8au, 0x40969123u, 0x409695d1u, 0x40969a0du, - 0x40969b45u, 0x40969bc4u, 0x40969c04u, 0x40969f09u, 0x4096f047u, 0x4096fc98u, - 0x4096fcadu, 0x4096fe9bu, 0x409701beu, 0x409701d1u, 0x409701e4u, 0x409705e4u, - 0x4097063cu, 0x40970741u, 0x40970797u, 0x409709e4u, 0x40970a05u, 0x40970bdcu, - 0x40c8d0d3u, 0x40c9474du, 0x40fb146au, 0x40fb14fcu, 0x40fb1688u, 0x40fb1730u, - 0x40fb17fcu, 0x40fb19aeu, 0x40fb2752u, 0x40fb2aafu, 0x40fb34a4u, 0x40fb7d3eu, - 0x40fb7ef2u, 0x40fb7f39u, 0x40fb84eeu, 0x40fb8b10u, 0x40fb8ceau, 0x40fb8d67u, - 0x40fb90b6u, 0x40fb9369u, 0x4116e0abu, 0x4116e82au, 0x412fcf11u, 0x412fcfcau, - 0x412fd14au, 0x412fd192u, 0x412fd6beu, 0x412fd904u, 0x4130079fu, 0x4130081fu, - 0x4148f347u, 0x41492a67u, 0x41492ce3u, 0x416211f2u, 0x41621514u, 0x4162175cu, - 0x41621a47u, 0x41621b3bu, 0x41623e72u, 0x41624dffu, 0x41624f25u, 0x417b379eu, - 0x417b70e8u, 0x418a2d12u, 0x418a3030u, 0x418a430au, 0x418a4916u, 0x418a4928u, - 0x41a34d18u, 0x41a34dc6u, 0x41a34f9bu, 0x41a350efu, 0x41afde6fu, 0x41afe160u, - 0x41bc6eefu, 0x41bc6fa8u, 0x41bc7017u, 0x41bc70cdu, 0x41bc70edu, 0x41bc71c2u, - 0x41bc8a0eu, 0x41bc8c7eu, 0x41c902ceu, 0x41d5920eu, 0x41d593a5u, 0x41d59472u, - 0x41d5ad81u, 0x41d5b071u, 0x41eeb412u, 0x41eeb53cu, 0x41eed0bfu, 0x41eed260u, - 0x41fb44a0u, 0x41fb47deu, 0x4203f94du, 0x4203f9e0u, 0x42107c64u, 0x42107cdfu, - 0x42108a64u, 0x421d0d1au, 0x421d1a89u, 0x42235534u, 0x42299df7u, 0x42299e04u, - 0x4229ac6du, 0x42363112u, 0x423c770bu, 0x4242bfa2u, 0x4242bfdfu, 0x4242c032u, - 0x4242ce3fu, 0x4242cedfu, 0x424f516du, 0x424f5f04u, 0x425bef43u, 0x425bf113u, - 0x425bf140u, 0x42622a27u, 0x42687272u, 0x42687317u, 0x426873c0u, 0x4268812au, - 0x426ec97du, 0x42751327u, 0x427b4c5au, 0x428d5b62u, 0x428d6295u, 0x42907f8du, - 0x4296cf20u, 0x42acc7efu, 0x42b31540u, 0x42b63a11u, 0x42b95e93u, 0x42bf9fddu, - 0x42bfa58du, 0x42c5ef2fu, 0x42cc308du, 0x42cf54f5u, 0x42d8c196u, 0x42df10e7u, - 0x42eba259u, 0x42eba26eu, 0x42f1e35au, 0x42f82c84u, 0x42fe7439u, 0x42fe7bfcu, - 0x430585e9u, 0x43058636u, 0x430bceaau, 0x430ef311u, 0x431537e4u, 0x4315385cu, - 0x43153bc0u, 0x43185c0cu, 0x43185efau, 0x431d1646u, 0x431ea872u, 0x4321c8d4u, - 0x4324f0d3u, 0x4328153du, 0x4333140cu, 0x4337ca74u, 0x43413717u, 0x43477bd8u, - 0x43540da3u, 0x43573436u, 0x4357343au, 0x435d7a02u, 0x435d7c84u, 0x43609e9du, - 0x4360a18fu, 0x4360a1b4u, 0x436554b1u, 0x436a0b1au, 0x437056d2u, 0x43737760u, - 0x437ce741u, 0x437ce761u, 0x437ce781u, 0x437ce7a1u, 0x437ce7c1u, 0x437ce7e1u, - 0x4380cd49u, 0x43819662u, 0x438972bau, 0x438972d0u, 0x438bcde4u, 0x438c9517u, - 0x4392df67u, 0x439471b0u, 0x439795dbu, 0x43a35c17u, 0x43ac0187u, 0x43ad91b8u, - 0x43ad93a8u, 0x43b0b7ebu, 0x43b180e7u, 0x43b24811u, 0x43b249ffu, 0x43b56c6du, - 0x43b6fe9cu, 0x43b6fea0u, 0x43b890e8u, 0x43ba22d6u, 0x43bbb553u, 0x43bd46f2u, - 0x43c77cd5u, 0x43d40de4u, 0x43db1f5fu, 0x43db1f7eu, 0x43dd7a82u, 0x43e2fbd3u, - 0x43e61fd1u, 0x43e6e737u, 0x43ec66b5u, 0x43edf8cbu, 0x43f8f80eu, 0x43f8f94cu, - 0x43fc1c24u, 0x440e8c48u, 0x44173006u, 0x44209cc3u, 0x442d2ea7u, 0x44350857u, - 0x4439bf82u, 0x443a88adu, 0x443a88b3u, 0x443dac15u, 0x44432c49u, 0x444a3de6u, - 0x444d6225u, 0x44566979u, 0x4456ce11u, 0x4457fc6cu, 0x4459292fu, 0x445abb4eu, - 0x4460a030u, 0x4461ccebu, 0x44648d6au, 0x446942feu, 0x446a7097u, 0x446b398fu, - 0x446c02a8u, 0x446c0390u, 0x446ff0e2u, 0x4473de31u, 0x447e7795u, 0x44810031u, - 0x44835bd6u, 0x4488447au, 0x4489a3e7u, 0x44911a6du, 0x44956c47u, 0x44a13433u, - 0x44b66866u, 0x44b6686bu, 0x44b668deu, 0x44b7faf6u, 0x44b98cabu, 0x44bf70a4u, - 0x44ca0b69u, 0x44d377b1u, 0x44d69c60u, 0x44d8930cu, 0x44dc1bdbu, 0x44dfa431u, - 0x44e94327u, 0x44f022e5u, 0x450a08c1u, 0x450d7829u, 0x45106a24u, 0x45214d02u, - 0x45214d2eu, 0x45295a12u, 0x452b50c1u, 0x452e74c3u, 0x452fd4a9u, 0x4533f438u, - 0x453718a2u, 0x453dde9au, 0x454c4d2cu, 0x455879ceu, 0x456218ceu, 0x45653cdau, - 0x456f0e50u, 0x4573ab91u, 0x457c1cf7u, 0x457edcb8u, 0x45888308u, 0x45896537u, - 0x458fadd2u, 0x45905daau, 0x4596d86au, 0x4597ae08u, 0x459a9371u, 0x459af814u, - 0x45a76fd4u, 0x45ad53cbu, 0x45aeb3a7u, 0x45ba239eu, 0x45be1d9bu, 0x45bee692u, - 0x45c2e06fu, 0x45c46619u, 0x45c4661au, 0x45c4661bu, 0x45c4661cu, 0x45cee74bu, - 0x45d80889u, 0x45da1853u, 0x45dbaa76u, 0x45dd6ed8u, 0x45df00f8u, 0x45e1c0afu, - 0x45f07a9cu, 0x45fafbeau, 0x45ff66c5u, 0x46032f99u, 0x4604b536u, 0x46073618u, - 0x460fb414u, 0x46124e0bu, 0x46166dabu, 0x4617434du, 0x4619854cu, 0x461b49b1u, - 0x461bd3f7u, 0x461ce86fu, 0x461ef82cu, 0x46224eadu, 0x46225b4bu, 0x4625f0a4u, - 0x46294728u, 0x4629c4c3u, 0x462b8934u, 0x462ccfedu, 0x4636ec8du, 0x463a81e5u, - 0x463f44e3u, 0x4640ca72u, 0x4658a5b2u, 0x465a5d78u, 0x465b4601u, 0x4663ca39u, - 0x466ba4d7u, 0x466f7904u, 0x466f7910u, 0x467ac347u, 0x467b40f1u, 0x467fd19cu, - 0x46852fb1u, 0x46866cfeu, 0x468cbef1u, 0x46960c3eu, 0x4696ee76u, 0x469a4e66u, - 0x469ca672u, 0x469dcdc1u, 0x46ab40e9u, 0x46abfd6fu, 0x46adc1d3u, 0x46b1bbb3u, - 0x46b8881eu, 0x46bcecd5u, 0x46c13864u, 0x46d40548u, 0x46d5cffcu, 0x46df94b1u, - 0x46dfc0a6u, 0x46ee28e0u, 0x46f6f890u, 0x46fca3f4u, 0x47022303u, 0x4703472eu, - 0x47069db5u, 0x470a864eu, 0x471c4ce4u, 0x4722d75fu, 0x47333c8au, 0x47382b72u, - 0x473873b7u, 0x473b10e0u, 0x474ad2aeu, 0x474ff6feu, 0x47511cbfu, 0x475340f4u, - 0x476420a2u, 0x47672576u, 0x476b83e2u, 0x476bf1d3u, 0x476d8d63u, 0x47796506u, - 0x477d2ca2u, 0x4787d580u, 0x47886ddeu, 0x478a15fdu, 0x478cdf20u, 0x478e38b3u, - 0x4798ca67u, 0x4799f991u, 0x479bc04eu, 0x47a13e2bu, 0x47a36d5fu, 0x47a505c9u, - 0x47a9ac73u, 0x47ac70dfu, 0x47b751e5u, 0x47b8213du, 0x47bc5ea9u, 0x47bdb1f5u, - 0x47bee1e8u, 0x47c01e6cu, 0x47c92058u, 0x47cb10b9u, 0x47cb34d8u, 0x47cc7eb6u, - 0x47ccaf68u, 0x47cfdd14u, 0x47d29683u, 0x47e5c0e5u, 0x47e60d14u, 0x47f1816du, - 0x47f2448au, 0x47f37d21u, 0x47f4cbb4u, 0x47f80dcdu, 0x47faca5fu, 0x480003ebu, - 0x4816a75fu, 0x481bf48au, 0x4822a0c7u, 0x482cb0e2u, 0x482da2cau, 0x482fc4a4u, - 0x48301bd1u, 0x483536b8u, 0x483c0c96u, 0x483ce625u, 0x4840e7dfu, 0x4840fb1eu, - 0x48492250u, 0x484f15fcu, 0x486c5aafu, 0x486d4197u, 0x48738a16u, 0x4874acaeu, - 0x4875d97du, 0x48769b7cu, 0x4879ebb6u, 0x488923b1u, 0x489b35e2u, 0x489d70deu, - 0x48a0bfebu, 0x48a5b51fu, 0x48a83fa1u, 0x48ae67bau, 0x48b27e45u, 0x48bc606eu, - 0x48bfeb2cu, 0x48cb9b25u, 0x48d60980u, 0x48d843b3u, 0x48da321bu, 0x48ddef1du, - 0x48e1df2cu, 0x48eab068u, 0x48f281ccu, 0x48f5319cu, 0x48fb10ddu, 0x48fb48a0u, - 0x48fb6938u, 0x48fba160u, 0x48fcd63cu, 0x49057582u, 0x490f60b3u, 0x4910358bu, - 0x49108db4u, 0x4915ae7fu, 0x491c1fa3u, 0x49223770u, 0x4924a1beu, 0x493185fcu, - 0x493c4fa4u, 0x4942e389u, 0x4947573fu, 0x494933b0u, 0x494a76e1u, 0x495099adu, - 0x4961b66eu, 0x49623286u, 0x4964e903u, 0x496b819eu, 0x4971a8d5u, 0x497c6765u, - 0x497e45e6u, 0x497f3d1au, 0x49824860u, 0x498c3a25u, 0x498c4232u, 0x498e6b69u, - 0x498eda01u, 0x49901a98u, 0x499719e4u, 0x499e2de7u, 0x49a4de12u, 0x49a78977u, - 0x49a80af5u, 0x49a9ad93u, 0x49af2f2bu, 0x49b8da4du, 0x49c02849u, 0x49c6eea4u, - 0x49ca9c23u, 0x49cab6d7u, 0x49d06fe7u, 0x49d46f21u, 0x49e927f4u, 0x49eef442u, - 0x49f25e29u, 0x49f473b1u, 0x49fccdf0u, 0x4a026803u, 0x4a0e545du, 0x4a1221abu, - 0x4a131f5au, 0x4a1b3285u, 0x4a1c9343u, 0x4a22b42bu, 0x4a2b447cu, 0x4a30c538u, - 0x4a368b1eu, 0x4a3ca940u, 0x4a3d7c08u, 0x4a3f69b4u, 0x4a4297f7u, 0x4a42f9d2u, - 0x4a4b5011u, 0x4a503380u, 0x4a51597cu, 0x4a5349c2u, 0x4a562bd6u, 0x4a563ebcu, - 0x4a673598u, 0x4a687b63u, 0x4a6d862au, 0x4a7600bcu, 0x4a789357u, 0x4a7ae775u, - 0x4a7edc1bu, 0x4a882dd3u, 0x4a8c74d1u, 0x4a8dfa66u, 0x4a92a2fau, 0x4a99984fu, - 0x4aa29b4bu, 0x4aaaecddu, 0x4aaffaacu, 0x4ab39469u, 0x4ab63248u, 0x4ab65656u, - 0x4abcb200u, 0x4abdae0au, 0x4ac4ac35u, 0x4ac4d043u, 0x4ac8d1ebu, 0x4ada79adu, - 0x4ae2ee2cu, 0x4ae7b663u, 0x4af71d19u, 0x4af7faf9u, 0x4b005d01u, 0x4b008c27u, - 0x4b035ffau, 0x4b0d03b1u, 0x4b20489bu, 0x4b233ef8u, 0x4b2ab439u, 0x4b2beeecu, - 0x4b2d7cd3u, 0x4b35e06cu, 0x4b36896eu, 0x4b4b2e7cu, 0x4b4c178au, 0x4b63f84bu, - 0x4b6ca10eu, 0x4b72d747u, 0x4b7870f9u, 0x4b7a400du, 0x4b7f0b0au, 0x4b7f7019u, - 0x4b86250cu, 0x4b94c1a4u, 0x4b9d1da2u, 0x4b9f341bu, 0x4ba01e6bu, 0x4ba26e6bu, - 0x4ba353d0u, 0x4bb24657u, 0x4bb88441u, 0x4bb8a4c7u, 0x4bbd17bbu, 0x4bbf5e06u, - 0x4bc6f8a2u, 0x4bcdca15u, 0x4bd0424cu, 0x4bd1e929u, 0x4bd60159u, 0x4bdda85au, - 0x4bf13989u, 0x4bf4fbb4u, 0x4bfb1f3au, 0x4c05e17au, 0x4c0ba822u, 0x4c0e7862u, - 0x4c1423efu, 0x4c24b66fu, 0x4c2575c2u, 0x4c26dc1eu, 0x4c2c0fc8u, 0x4c30d7ffu, - 0x4c37501cu, 0x4c3b5fefu, 0x4c3dea22u, 0x4c40b639u, 0x4c4d22b6u, 0x4c548f79u, - 0x4c6b1be5u, 0x4c6c0d19u, 0x4c7d2ae6u, 0x4c835981u, 0x4c8552dbu, 0x4c8935a2u, - 0x4c8dfcf7u, 0x4c904cccu, 0x4c92d760u, 0x4c97b32cu, 0x4ca3ba99u, 0x4cac1529u, - 0x4cad98afu, 0x4cb3ed6au, 0x4cb8fb78u, 0x4cbbe63cu, 0x4cbcfb28u, 0x4cbf5a69u, - 0x4cc02bc3u, 0x4cc8b1a6u, 0x4ccf143fu, 0x4cd701f9u, 0x4cd7bf3du, 0x4cda6e35u, - 0x4cdf355fu, 0x4cdf9f6fu, 0x4ce5926cu, 0x4ce92362u, 0x4cf4b153u, 0x4cfde962u, - 0x4d041be0u, 0x4d0dd240u, 0x4d0e10d2u, 0x4d0ee548u, 0x4d135c90u, 0x4d141111u, - 0x4d14c4b0u, 0x4d16c8f7u, 0x4d19ae2fu, 0x4d20e53eu, 0x4d21f9a9u, 0x4d24e708u, - 0x4d2572e1u, 0x4d2d3548u, 0x4d3209cfu, 0x4d353641u, 0x4d3683ddu, 0x4d36e3c7u, - 0x4d3d0150u, 0x4d3e1881u, 0x4d461153u, 0x4d545d7du, 0x4d57f29cu, 0x4d5fe8a8u, - 0x4d667694u, 0x4d6b5a87u, 0x4d6e1309u, 0x4d6ff608u, 0x4d718da4u, 0x4d772536u, - 0x4d77e8ddu, 0x4d7e5cb6u, 0x4d810c95u, 0x4d8418ccu, 0x4d864b57u, 0x4d898046u, - 0x4d9072b6u, 0x4d90ade6u, 0x4d91ff5du, 0x4d952069u, 0x4da14c5du, 0x4da27d28u, - 0x4da840a9u, 0x4db29c1eu, 0x4db446b3u, 0x4dbac382u, 0x4dc6898fu, 0x4dc889d8u, -}; -ALTD_MATH_TABLE uint32_t UCRT_SINF_VALUES[786] = { - 0x3f7ffe04u, 0x3f7ffe0bu, 0x3f7ffe15u, 0x3f7ffe23u, 0x3f7ffe29u, 0x3f7ffe36u, - 0x3f7ffe40u, 0x3f7ffe42u, 0x3f7ffe4eu, 0x3f7ffe5bu, 0x3f7ffe5eu, 0x3f7ffe63u, - 0x3f7ffe66u, 0x3f7ffe76u, 0x3f7ffe77u, 0x3f7ffe78u, 0x3f7ffe7fu, 0x3f7ffe84u, - 0x3f7ffe90u, 0x3f7ffea8u, 0x3f7ffeacu, 0x3f7ffeafu, 0x3f7ffeb2u, 0x3f7ffec6u, - 0x3f7ffedau, 0x3f7ffefau, 0x3f7fff22u, 0x3f7fff2cu, 0x3f7fff93u, 0x3f7fffb4u, - 0x3f7fffb2u, 0x3f7fff8cu, 0x3f7fff75u, 0x3f7fff5cu, 0x3f7fff45u, 0x3f7fff42u, - 0x3f7fff30u, 0x3f7fff25u, 0x3f7fff20u, 0x3f7fff1du, 0x3f7fff16u, 0x3f7ffee1u, - 0x3f7ffed4u, 0x3f7ffed1u, 0x3f7ffecfu, 0x3f7ffebdu, 0x3f7ffebau, 0x3f7ffeb8u, - 0x3f7ffeb3u, 0x3f7ffeaau, 0x3f7ffe85u, 0x3f7ffe7eu, 0x3f7ffe64u, 0x3f7ffe5du, - 0x3f7ffe55u, 0x3f7ffe54u, 0x3f7ffe3du, 0x3f7ffe39u, 0x3f7ffe2eu, 0x3f7ffe2du, - 0x3f7ffe28u, 0x3f7ffe18u, 0x3f7ffe16u, 0x3f7ffe12u, 0x3f7ffe11u, 0x3f7ffe0fu, - 0x3f7ffe0eu, 0x3f7ffe08u, 0x3f7ffe05u, 0x3bfa6ca5u, 0x3be9a6c3u, 0xbb9e2694u, - 0xbbe32845u, 0xbbf6f823u, 0xbbfcbe18u, 0xbf7ffe03u, 0xbf7ffe0cu, 0xbf7ffe14u, - 0xbf7ffe17u, 0xbf7ffe27u, 0xbf7ffe2au, 0xbf7ffe51u, 0xbf7ffe93u, 0xbf7ffecau, - 0xbf7ffed9u, 0xbf7ffedfu, 0xbf7ffee2u, 0xbf7fff05u, 0xbf7fff5bu, 0xbf7ffed8u, - 0xbf7ffed7u, 0xbf7ffebfu, 0xbf7ffe96u, 0xbf7ffe95u, 0xbf7ffe94u, 0xbf7ffe5cu, - 0xbf7ffe57u, 0xbf7ffe48u, 0xbf7ffe43u, 0xbf7ffe20u, 0xbf7ffe1eu, 0xbf7ffe01u, - 0xbbfc1de6u, 0x3bddc909u, 0x3f7ffe0au, 0x3f7ffe13u, 0x3f7ffe2bu, 0x3f7ffe35u, - 0x3f7ffe41u, 0x3f7ffe5au, 0x3f7fff09u, 0x3f7fff2du, 0x3f7fff87u, 0x3f7fff2au, - 0x3f7fff18u, 0x3f7fff15u, 0x3f7ffed3u, 0x3f7ffe83u, 0x3f7ffe69u, 0x3f7ffe62u, - 0x3f7ffe31u, 0x3f7ffe07u, 0xbba63805u, 0xbbe22fbeu, 0xbf7ffe26u, 0xbf7ffe3cu, - 0xbf7ffe68u, 0xbf7ffe70u, 0xbf7ffef5u, 0xbf7fff27u, 0xbf7ffeb5u, 0xbf7ffea8u, - 0xbbe49c98u, 0x3bd4628eu, 0x3be84270u, 0x3f7ffe04u, 0x3f7ffe63u, 0x3f7ffea2u, - 0x3f7ffeebu, 0x3f7fff01u, 0x3f7fffb1u, 0x3f7ffe74u, 0x3f7ffe53u, 0x3be199e1u, - 0xbbe8b52bu, 0xbf7ffe82u, 0xbf7fff1bu, 0xbf7fff7cu, 0xbf7ffe6eu, 0xbf7ffe6au, - 0x3f7ffe0eu, 0x3f7ffe38u, 0x3f7ffea0u, 0x3f7ffee3u, 0x3bf70445u, 0x3bc7f48du, - 0xbf7ffe05u, 0xbf7ffe32u, 0xbf7ffe4cu, 0xbf7ffe75u, 0xbf7ffe7cu, 0xbf7ffea9u, - 0xbf7fff06u, 0xbf7ffe8du, 0xbbd0c9c6u, 0x3f7ffe4bu, 0x3f7ffea4u, 0x3f7ffecdu, - 0x3f7ffec0u, 0x3f7ffe1au, 0xbf7ffe4du, 0xbf7ffe8fu, 0xbf7ffe7du, 0xbf7ffe1du, - 0xbbf31419u, 0xbbbf3464u, 0x3f7ffe84u, 0x3f7ffe42u, 0xbf7ffe78u, 0xbf7ffeacu, - 0xbf7ffe79u, 0x3f7ffe58u, 0x3f7ffed2u, 0x3bf5b3e1u, 0xbf7ffe49u, 0xbf7ffe4fu, - 0xbf7ffe73u, 0x3f7fff20u, 0x3bfa3e61u, 0xbf7ffe23u, 0xbf7ffe40u, 0xbf7ffe66u, - 0xbf7ffe85u, 0xbf7ffe3du, 0x3f7ffe81u, 0x3f7ffe9du, 0xbf7ffee8u, 0xbf7ffe22u, - 0xbf7ffe0cu, 0xbbf5de36u, 0x3f7ffe0fu, 0x3f7ffe5du, 0x3f7ffea6u, 0x3f7ffe8bu, - 0xbbd52b8du, 0xbf7ffe16u, 0xbbeee8cbu, 0x3f7ffe3bu, 0x3f7ffe87u, 0x3bf6088bu, - 0x3bcf0140u, 0xbf7fffa5u, 0x3f7ffec7u, 0xbbecb6beu, 0xbf7ffe0bu, 0x3f7ffeafu, - 0x3f7fff42u, 0xbf7ffe67u, 0x3f7ffe6du, 0x3bd91d65u, 0x3f7ffe77u, 0xbf7ffea1u, - 0xbf7ffe3au, 0xbf7ffe26u, 0x3f7ffe45u, 0xbf7ffed6u, 0x3f7ffe28u, 0x3f7ffe15u, - 0x3f7fff0cu, 0x3f7ffe96u, 0x3f7ffea8u, 0xbf7ffe64u, 0xbf7ffe3eu, 0xbf7fff03u, - 0xbf7ffe09u, 0x3f7ffe11u, 0x3f7fff8au, 0x3bf1981cu, 0x3f7ffe21u, 0xbf7ffe24u, - 0x3f7ffe59u, 0xbf7ffe08u, 0xbbf6a335u, 0x3f7ffe14u, 0xbf7ffe48u, 0xbf7ffe39u, - 0xbf7fff6au, 0x3f7fff3bu, 0x3f7fff36u, 0x3f7ffef1u, 0x3f7fff73u, 0xbf7fff66u, - 0xbf7ffe4cu, 0xbf7ffe05u, 0x3bb2303eu, 0x3f7fff16u, 0x3f7ffe41u, 0xbf7ffe57u, - 0x3f7fff24u, 0x3f7ffef8u, 0x3f7ffec8u, 0x3f7ffe94u, 0x3f7ffe5cu, 0x3f7ffe20u, - 0xbbdf0f6du, 0x3f7ffe9bu, 0xbf7ffe6cu, 0xbf7ffe1au, 0xbbddfad6u, 0xbf7ffe05u, - 0xbf7ffea7u, 0x3f7ffe12u, 0x3f7ffe5fu, 0xbbcaa367u, 0xbf7ffe24u, 0x3f7ffe21u, - 0x3f7ffe1fu, 0x3f7ffe11u, 0xbbe7fc60u, 0xbf7ffe09u, 0xbf7ffe3eu, 0xbf7ffe74u, - 0x3f7ffea8u, 0x3f7ffeb5u, 0xbf7fff2au, 0x3f7ffe96u, 0xbf7fff8cu, 0x3f7ffe15u, - 0x3be5ec62u, 0x3bd4969eu, 0xbf7ffe5bu, 0xbf7ffec4u, 0x3bf4d604u, 0x3f7ffe49u, - 0x3f7fff1au, 0x3bfe158cu, 0x3f7ffe42u, 0xbf7ffe1du, 0x3f7fff56u, 0x3f7fff1eu, - 0x3f7ffee9u, 0xbf7ffe51u, 0x3f7ffebcu, 0x3f7ffeb4u, 0x3f7ffe07u, 0x3f7ffe7du, - 0x3f7ffef5u, 0xbf7ffe41u, 0xbf7ffe13u, 0xbf7ffee2u, 0x3f7ffec0u, 0xbf7ffe5fu, - 0xbf7ffe62u, 0x3bde4d5cu, 0xbf7ffee6u, 0xbbc8475bu, 0x3f7ffe74u, 0x3f7ffe6fu, - 0x3bf37d2bu, 0xbf7ffeceu, 0xbbc8f316u, 0x3bcd4bdau, 0x3f7ffebfu, 0xbf7ffe16u, - 0x3f7ffe5bu, 0x3f7ffe5du, 0xbf7ffe49u, 0x3f7ffe4bu, 0xbbf769cbu, 0x3f7ffe4du, - 0x3f7ffe56u, 0xbbed4bf2u, 0x3f7ffeb6u, 0xbf7ffe3fu, 0x3f7ffe14u, 0x3f7ffe30u, - 0x3f7ffe3du, 0x3f7ffe85u, 0x3f7ffe40u, 0x3f7ffeacu, 0x3f7ffe8du, 0xbf7ffea9u, - 0x3f7ffe54u, 0x3f7ffe43u, 0x3f7ffeb0u, 0xbf7ffe75u, 0x3f7ffe02u, 0xbf7ffecfu, - 0xbbf697cbu, 0xbf7ffe99u, 0xbbf538f7u, 0x3f7ffe29u, 0xbf7ffe1bu, 0xbf7fff0eu, - 0xbf7fff0fu, 0x3f7ffeedu, 0x3f7ffe25u, 0x3f7ffe2du, 0xbf7fff32u, 0x3f7ffed3u, - 0x3f7fff5eu, 0x3bf5b442u, 0x3f7fff80u, 0x3f7ffe72u, 0xbf7ffeaeu, 0xbf7ffebeu, - 0xbf7fff05u, 0xbbd247bau, 0x3baed56bu, 0x3bfaeb45u, 0x3f7ffef2u, 0xbf7ffe5eu, - 0xbf7ffe89u, 0x3f7fff7bu, 0x3f7fff10u, 0x3bdcf88au, 0x3f7ffe2eu, 0x3f7ffeeau, - 0xbf7ffe08u, 0xbf7ffe0eu, 0xbf7ffe1eu, 0xbbf680f7u, 0x3f7ffe9du, 0x3f7ffef7u, - 0x3bec85c4u, 0x3f7ffec8u, 0x3f7ffe94u, 0x3f7ffe5cu, 0x3f7ffe20u, 0xbf7fff26u, - 0x3f7ffe0au, 0xbf7ffe22u, 0xbf7ffed8u, 0xbf7ffe81u, 0xbf7ffe91u, 0xbf7ffe76u, - 0xbf7fff2cu, 0x3f7ffe17u, 0xbf7ffe5au, 0x3f7ffe6bu, 0xbf7ffe66u, 0x3f7ffea1u, - 0xbf7ffe61u, 0x3f7ffec2u, 0x3f7ffeb3u, 0xbf7ffe31u, 0xbf7ffe7cu, 0xbf7fff02u, - 0x3f7fff12u, 0x3f7ffe2bu, 0x3f7fff3cu, 0x3f7ffe52u, 0xbf7ffeffu, 0x3f7ffebfu, - 0x3f7ffe7du, 0x3f7ffe07u, 0x3f7fff19u, 0x3f7fff56u, 0x3f7ffe3eu, 0xbf7ffe15u, - 0x3f7ffedcu, 0xbf7ffec0u, 0xbf7fff4bu, 0x3f7ffe0bu, 0xbbd80980u, 0x3f7fff2bu, - 0x3f7ffefcu, 0x3f7ffee1u, 0x3f7ffee0u, 0x3f7ffe84u, 0x3f7ffe8eu, 0xbf7ffe06u, - 0x3f7ffe79u, 0x3be11cf6u, 0x3f7ffe18u, 0xbf7ffefau, 0xbf7ffea4u, 0xbbcb5846u, - 0x3f7ffe23u, 0xbf7ffe67u, 0xbf7ffe14u, 0xbf7ffeb6u, 0xbf7ffe6fu, 0xbf7ffea8u, - 0x3f7ffee9u, 0xbf7ffe97u, 0x3f7ffe54u, 0x3f7ffe27u, 0xbf7ffe04u, 0xbf7ffe32u, - 0x3f7fff1du, 0xbf7ffe7bu, 0x3f7fff20u, 0xbf7ffe33u, 0x3f7ffe1fu, 0xbf7ffe0cu, - 0xbf7ffea7u, 0x3bdf45adu, 0x3f7fff6cu, 0xbf7ffe38u, 0xbf7ffe21u, 0xbf7ffea5u, - 0x3f7ffea2u, 0xbf7ffe37u, 0xbf7ffe59u, 0x3f7ffe0du, 0x3bee6fd7u, 0x3f7ffeaau, - 0xbf7fff86u, 0xbf7ffe2au, 0xbf7ffe35u, 0x3f7ffe6du, 0xbf7ffe6eu, 0x3f7ffe84u, - 0x3f7ffefcu, 0xbf7ffe39u, 0x3f7ffe2bu, 0x3f7fff48u, 0x3f7fff34u, 0x3f7ffe73u, - 0xbbc546fbu, 0x3bdd9e64u, 0xbf7ffe09u, 0x3f7ffe92u, 0x3f7ffe3cu, 0x3f7ffe89u, - 0x3f7ffe1eu, 0xbf7ffe6bu, 0x3f7fff84u, 0x3f7ffe95u, 0xbf7fffa2u, 0xbf7ffeabu, - 0xbbd98f8cu, 0x3f7ffe69u, 0x3f7ffe42u, 0x3f7ffe49u, 0xbf7ffe4du, 0xbf7ffe43u, - 0x3f7ffecfu, 0x3f7ffe44u, 0x3f7ffe4au, 0xbbcd13e6u, 0x3f7ffeb2u, 0xbf498823u, - 0xbf7fff50u, 0x3f7ffe3du, 0xbf7fff9cu, 0x3f7ffec3u, 0x3f7fff97u, 0x3f7ffed1u, - 0xbf7ffeb1u, 0xbf7ffec9u, 0x3f7fff3fu, 0x3f7ffe79u, 0x3f7ffe18u, 0xbf7ffe67u, - 0x3f7ffe8bu, 0x3f7ffe2cu, 0x3f7ffe53u, 0xbf7ffecdu, 0xbf7ffe0fu, 0x3bfacce6u, - 0xbf7ffea6u, 0xbf7fff6eu, 0xbf7ffe55u, 0xbf7fff81u, 0xbf7ffe9cu, 0xbf7ffe07u, - 0x3f7fffa1u, 0xbf7ffe1au, 0xbf7ffebeu, 0xbbd29457u, 0xbf7fff0au, 0xbf7fff47u, - 0x3f7fff5eu, 0x3f7ffe76u, 0xbbf2dc97u, 0x3f7ffe99u, 0xbf7ffedfu, 0x3f7ffef4u, - 0xbf7ffeeeu, 0x3f7ffe2eu, 0x3f7ffeb9u, 0x3f7ffe2du, 0xbf7ffe11u, 0x3f7ffea0u, - 0xbf7ffecbu, 0xbf7ffef5u, 0xbbf93b3au, 0xbf7ffe55u, 0xbf7ffe31u, 0xbf7fff31u, - 0x3f7ffe52u, 0xbf7fff36u, 0x3bf499ffu, 0xbf7ffe0fu, 0xbf7ffed3u, 0xbf7ffe20u, - 0x3f7fff2cu, 0x3f7ffed1u, 0xbf7ffe43u, 0xbf7ffe09u, 0x3f7ffe2bu, 0x3f7ffe78u, - 0xbf7ffe27u, 0xbf7ffeaau, 0x3bdfdea2u, 0x3f7fff1au, 0x3f7fff69u, 0x3f7fff55u, - 0xbf7ffe8bu, 0xbf7ffe80u, 0x3f7ffecdu, 0x3f7fffa7u, 0xbbeaf373u, 0xbf7ffea0u, - 0x3f7ffe11u, 0xbf7ffe2du, 0x3f7ffe98u, 0xbf7ffe23u, 0xbf7ffe76u, 0x3f7ffe47u, - 0xbb4d283fu, 0x3f7ffe3fu, 0x3f7ffe07u, 0xbf7ffe53u, 0xbf7ffeb6u, 0x3f7fff3eu, - 0xbf7ffeddu, 0xbf7ffe69u, 0xbf7ffeefu, 0x3bf4f8b3u, 0x3f7ffea3u, 0x3f7ffef6u, - 0x3f7ffe5bu, 0x3f7ffec7u, 0xbf7ffe86u, 0x3beeb7d6u, 0x3f7ffed7u, 0xbf7fff46u, - 0x3beee707u, 0xbf7ffe31u, 0xbf7ffefdu, 0xbf7ffe8eu, 0xbf7ffe0au, 0xbf7ffe74u, - 0xbf7ffe0eu, 0xbf7ffe24u, 0xbf7ffe15u, 0x3bc2bee4u, 0x3f7fff8au, 0xbbe5074cu, - 0x3f7ffec1u, 0xbf7ffe30u, 0xbf7ffeb4u, 0x3f7fff93u, 0x3f7ffe2fu, 0xbbf5f135u, - 0x3f7ffeb3u, 0x3f7ffe7cu, 0xbf7fff1du, 0xbf7ffe6du, 0xbf7ffedau, 0x3f7ffe48u, - 0x3f7ffe28u, 0xbf7ffe5eu, 0xbbf3d100u, 0xbf7ffe40u, 0x3f7ffed8u, 0x3f7ffe56u, - 0x3beca8fbu, 0x3f7ffe4au, 0x3f7ffe33u, 0xbf7ffe1bu, 0xbf7fff10u, 0xbf7ffeb8u, - 0x3f7ffe0bu, 0x3bdbdc84u, 0xbf7ffe3bu, 0x3f7fff43u, 0xbf7ffe6bu, 0x3f7ffe3cu, - 0xbf7ffe3du, 0x3f7ffe19u, 0x3f7ffe34u, 0xbf7ffe02u, 0x3f7ffe1cu, 0xbbea33cdu, - 0xbf7ffe25u, 0x3f7ffe21u, 0x3be8edf1u, 0x3b99e8c4u, 0xbf7ffe70u, 0xbf7ffedeu, - 0x3bb1c302u, 0xbf7ffe41u, 0xbf7ffeb7u, 0x3f7ffe2bu, 0x3f7ffe2cu, 0x3f7fff06u, - 0x3f7fff18u, 0x3f7ffe0fu, 0x3f7ffeb1u, 0xbf7ffea0u, 0x3f7ffe31u, 0x3f7ffe20u, - 0x3f7ffebfu, 0xbf7ffe63u, 0xbf7ffea4u, 0x3f7ffe14u, 0x3f7ffe04u, 0xbf7ffe0du, - 0x3f7ffe66u, 0x3f7fff90u, 0xbbadd51eu, 0x3f7ffea7u, 0xbf7ffe08u, 0x3f7ffe6cu, - 0xbbaa9cd2u, 0x3be4f24bu, 0x3f7fff32u, 0x3be20f1bu, 0xbf7ffecdu, 0x3f7ffe43u, - 0x3f7ffe99u, 0x3f7ffeccu, 0xbf7fff24u, 0xbf7fff5bu, 0xbf7ffeedu, 0xbf7fffd0u, - 0xbf7ffe03u, 0x3f7fff0eu, 0xbbf020eau, 0x3f7ffe94u, 0xbf7ffe6du, 0x3f7ffe1au, - 0x3f7ffe07u, 0x3f7ffe54u, 0x3f7ffecau, 0xbf7ffe65u, 0x3f7ffeb8u, 0x3f7ffed2u, - 0xbf7fff19u, 0xbf7ffe51u, 0xbf7ffe5bu, 0x3f7ffe80u, 0x3f7fff66u, 0x3f7ffe32u, - 0xbf7ffe06u, 0xbf7ffe8du, 0x3f7ffec0u, 0x3f7ffea9u, 0x3f7ffe64u, 0x3f7ffe89u, - 0x3bcc33f5u, 0xbf7fff28u, 0x3f7ffe67u, 0xbbf5c839u, 0xbf7ffe38u, 0xbf7ffe53u, - 0xbf7ffe55u, 0xbf7ffee6u, 0x3ba9ba68u, 0xbf7ffe85u, 0x3f7ffe5eu, 0x3f7ffe15u, - 0x3f7ffe8eu, 0xbf7ffe0au, 0xbf7ffe8cu, 0xbf7ffe13u, 0xbf7ffe23u, 0xbf7fffafu, - 0x3f7fff0bu, 0x3f7fff4au, 0x3f7fff15u, 0x3f7ffee4u, 0x3f7fff47u, 0xbf7ffe2au, - 0xbf7ffec5u, 0x3f7fff1eu, 0xbf7ffe0du, 0xbf7ffe73u, 0x3f7ffe2cu, 0xbbd96693u, - 0x3f7ffec7u, 0xbf7fff14u, 0x3f7ffea1u, 0x3f7ffed5u, 0xbf7ffe50u, 0xbf7ffef3u, - 0x3f7ffe17u, 0x3f7ffe63u, 0xbf7ffe57u, 0xbf7ffe9du, 0x3f7ffe29u, 0x3c000de3u, - 0xbf7ffe21u, 0x3f7ffe02u, 0x3f7ffed7u, 0x3f7ffe46u, 0x3f7ffe75u, 0x3f7ffed4u, - 0x3f7ffea2u, 0x3f7fff6du, 0xbf7ffe28u, 0x3f7ffe3bu, 0x3f7ffe25u, 0x3bb23314u, - 0x3bc9ce01u, 0xbf7ffea7u, 0x3f7ffecdu, 0x3f7ffef0u, 0x3f7ffe18u, 0x3f7fff0du, - 0x3f7ffe4bu, 0xbf7ffe35u, 0xbbfa1b6bu, 0x3f7ffed8u, 0xbbdb09deu, 0xbf7ffe6au, - 0xbf7ffe56u, 0x3f7fff0cu, 0xbf7ffe1cu, 0x3beb827au, 0xbf7fff10u, 0xbf7ffe0cu, -}; -// sinf, |x| >= 0x4dc90fdb (CPU only) -ALTD_MATH_TABLE uint32_t UCRT_SINF_WIDE_KEYS[2536] = { - 0x4dcd12b6u, 0x4dce4a93u, 0x4ddb264bu, 0x4de1fb46u, 0x4de5f386u, 0x4df02536u, - 0x4df1accau, 0x4df21f09u, 0x4df5b58bu, 0x4df6e564u, 0x4df9074bu, 0x4dfae621u, - 0x4dfeac1fu, 0x4e0c62dfu, 0x4e138c08u, 0x4e15acdau, 0x4e15df1cu, 0x4e16ea6eu, - 0x4e18d40eu, 0x4e1add97u, 0x4e1dd903u, 0x4e24d63du, 0x4e26ffafu, 0x4e28e901u, - 0x4e2a99beu, 0x4e2d253cu, 0x4e2f9d50u, 0x4e304732u, 0x4e3db133u, 0x4e4405eeu, - 0x4e4d583bu, 0x4e525a1cu, 0x4e587104u, 0x4e592a52u, 0x4e5c2dc2u, 0x4e65c65bu, - 0x4e71dc46u, 0x4e73678eu, 0x4e7f9939u, 0x4e81b3d8u, 0x4e8801a2u, 0x4e8e007eu, - 0x4e8f4cdeu, 0x4e93ad7du, 0x4e941ca4u, 0x4e961510u, 0x4e973158u, 0x4e9d30a9u, - 0x4ea3b957u, 0x4eac59beu, 0x4eaf230fu, 0x4eb05a02u, 0x4ebf3188u, 0x4ec1418bu, - 0x4ec812fdu, 0x4ecb49ebu, 0x4ecd37b4u, 0x4ed1cae3u, 0x4edd49bau, 0x4edf10f9u, - 0x4ee83893u, 0x4eeabfe8u, 0x4eeba9eeu, 0x4ef31e2cu, 0x4ef36cf3u, 0x4f021e87u, - 0x4f032539u, 0x4f084f55u, 0x4f121255u, 0x4f129a4eu, 0x4f14765du, 0x4f1ecb0cu, - 0x4f1fbb3cu, 0x4f3379cfu, 0x4f33bd2eu, 0x4f38f6dcu, 0x4f398815u, 0x4f3d4571u, - 0x4f456af9u, 0x4f4b8681u, 0x4f4f7043u, 0x4f57664fu, 0x4f5bc3d8u, 0x4f5ccf2au, - 0x4f5d4b1du, 0x4f60d7e9u, 0x4f629d27u, 0x4f653a39u, 0x4f669d6bu, 0x4f6cc6d3u, - 0x4f6cce39u, 0x4f6dac87u, 0x4f7b3dd8u, 0x4f7ea892u, 0x4f86fab5u, 0x4f9871b2u, - 0x4f9f616fu, 0x4fb837c6u, 0x4fc662a3u, 0x4fca5268u, 0x4fca69d7u, 0x4fcb5151u, - 0x4fce54fcu, 0x4fcf1138u, 0x4fd5ee3au, 0x4fe6cf4au, 0x4fe6eb0au, 0x4fe7c1cau, - 0x4febc20eu, 0x4fec45b6u, 0x4ff8dcf9u, 0x4fff1f33u, 0x5002231du, 0x50072573u, - 0x500a72bbu, 0x5011faa1u, 0x5017f216u, 0x5022ce6fu, 0x502ac8eau, 0x502cb11du, - 0x5031b24bu, 0x5041c703u, 0x50508437u, 0x505d8e99u, 0x506bf127u, 0x50757e9eu, - 0x5077ec2bu, 0x50789841u, 0x507b7d52u, 0x5087f758u, 0x50962d67u, 0x5097d49fu, - 0x50989b88u, 0x50a0bc21u, 0x50a714ddu, 0x50a9d38bu, 0x50af97e5u, 0x50affc5fu, - 0x50b22c6eu, 0x50c82e33u, 0x50ca1255u, 0x50caa4b6u, 0x50d695f0u, 0x50ead1e3u, - 0x50eb8eb3u, 0x50f23d30u, 0x50fbd7f3u, 0x5108b8e8u, 0x510a9e32u, 0x5117d75eu, - 0x511b263du, 0x5124e6f9u, 0x51292cd3u, 0x512cb9e9u, 0x5133a403u, 0x513679ecu, - 0x514468f8u, 0x5144f1aau, 0x5147ad05u, 0x5149a0e3u, 0x5154ead1u, 0x5156f884u, - 0x51669265u, 0x516e552au, 0x51849d6du, 0x51894694u, 0x518f20bcu, 0x51a467bcu, - 0x51a467e1u, 0x51a85134u, 0x51b44e43u, 0x51bc69a8u, 0x51bcb320u, 0x51cecbbcu, - 0x51d54be3u, 0x51d98dfbu, 0x51da2cb0u, 0x51e67f90u, 0x51efd42eu, 0x51f0131eu, - 0x51f9d18au, 0x51fe6dd9u, 0x52077e8fu, 0x520a2f03u, 0x520aa7f9u, 0x521442f6u, - 0x52166b27u, 0x521803cbu, 0x521d2ea4u, 0x52209c67u, 0x52242830u, 0x52248448u, - 0x522c1b61u, 0x522cf6dbu, 0x5232ba47u, 0x5234aeb9u, 0x523536fcu, 0x52370462u, - 0x523d74a8u, 0x523ebb3du, 0x52408f59u, 0x52457ebau, 0x52481fe1u, 0x5248279au, - 0x524c2612u, 0x524d5c32u, 0x5256fd9bu, 0x525bb1ceu, 0x525de06bu, 0x52643feau, - 0x526aac66u, 0x526b35acu, 0x526d5693u, 0x527e1edbu, 0x527f89e2u, 0x528238eau, - 0x528618a3u, 0x528d1cf9u, 0x528dcec5u, 0x528ed792u, 0x528ef9acu, 0x5295b387u, - 0x52981e07u, 0x52a33cacu, 0x52a80de2u, 0x52a9edcbu, 0x52aba67fu, 0x52b4200eu, - 0x52bec3b3u, 0x52c46d2du, 0x52d05b5au, 0x52d15ee7u, 0x52d31b8au, 0x52da30edu, - 0x52da52e2u, 0x52da6025u, 0x52dc8360u, 0x52e0205bu, 0x52e29d39u, 0x52e312f9u, - 0x52f06ecbu, 0x52f83484u, 0x52fe87c7u, 0x530234afu, 0x5304a0cau, 0x530977f9u, - 0x530adb47u, 0x5315b627u, 0x5319149du, 0x531949a9u, 0x531d81b4u, 0x5322a3f2u, - 0x532433fdu, 0x5328af37u, 0x5328f8f5u, 0x5338a88bu, 0x5338b204u, 0x533beb05u, - 0x533fe2a6u, 0x5345720bu, 0x534e6730u, 0x536b1632u, 0x536bc3eau, 0x537ab656u, - 0x537e331cu, 0x537f3be9u, 0x538dd164u, 0x538f1202u, 0x5390ebf0u, 0x53912efau, - 0x539b6f60u, 0x539cd4ddu, 0x53a20a57u, 0x53acb4b0u, 0x53af8559u, 0x53d3e722u, - 0x53d51e22u, 0x53da2544u, 0x53dab5fbu, 0x53dd2445u, 0x53dec55du, 0x53ee165eu, - 0x53fb36bau, 0x53fe9a4au, 0x54037206u, 0x5407a27du, 0x540841ecu, 0x54340495u, - 0x543ee110u, 0x54406585u, 0x54440612u, 0x5444ad28u, 0x544646e4u, 0x5448072cu, - 0x545352d9u, 0x54542a64u, 0x5455bc54u, 0x54565f8du, 0x54566f25u, 0x545d7fa5u, - 0x54644d66u, 0x546fcd8au, 0x5470abc9u, 0x5474c22au, 0x54754783u, 0x54766c4bu, - 0x548106fau, 0x548c4bf3u, 0x54919eb8u, 0x5498f484u, 0x549d7c50u, 0x54a48922u, - 0x54a82c04u, 0x54b8f38fu, 0x54c13d51u, 0x54c20464u, 0x54c3817du, 0x54c8cfd0u, - 0x54d08a18u, 0x54d1a82cu, 0x54d21458u, 0x54d3edcdu, 0x54d56a76u, 0x54d6866du, - 0x54d7ff4cu, 0x54e1bfd0u, 0x54e3d5a4u, 0x54e5b659u, 0x54f75e08u, 0x54fa3eb9u, - 0x54fc4fabu, 0x5500bfd7u, 0x5500c74fu, 0x5508389du, 0x5508cb71u, 0x5510bc4du, - 0x5514c646u, 0x551794e0u, 0x551f386cu, 0x55208c88u, 0x553361e3u, 0x55339d8cu, - 0x55347a10u, 0x554c2d58u, 0x555042bdu, 0x555d4be4u, 0x555f2923u, 0x556235d7u, - 0x556a9116u, 0x556c4d94u, 0x5579b751u, 0x557c9977u, 0x5581b52du, 0x5583b72fu, - 0x55884062u, 0x558c55e3u, 0x55920303u, 0x5592b8a7u, 0x5593d683u, 0x5594f66eu, - 0x55958d44u, 0x55a13c68u, 0x55a22466u, 0x55a4a210u, 0x55a5c7c6u, 0x55a71dc7u, - 0x55aa77a1u, 0x55ac666bu, 0x55ad02f0u, 0x55b0edb9u, 0x55b6d4c7u, 0x55b9280du, - 0x55b9717eu, 0x55bbc678u, 0x55c0ae78u, 0x55c9cae3u, 0x55c9d850u, 0x55ca520cu, - 0x55cf2110u, 0x55cf721cu, 0x55d328ecu, 0x55d5c564u, 0x55d5d309u, 0x55d917b8u, - 0x55d9c97du, 0x55ddb632u, 0x55e706ceu, 0x55eca271u, 0x55ecebf7u, 0x55ee43ebu, - 0x55ef2f89u, 0x55ef7cf5u, 0x55f24fa6u, 0x55f8935du, 0x55fd52d0u, 0x5601d8d7u, - 0x560c77afu, 0x5610f939u, 0x561a9aefu, 0x5622bee3u, 0x5626e892u, 0x562c3cdau, - 0x56387737u, 0x56392323u, 0x5643cf76u, 0x5659dfa5u, 0x565db357u, 0x56643320u, - 0x5669875au, 0x56735761u, 0x5675006fu, 0x567ac0dcu, 0x567fce8eu, 0x568340c2u, - 0x56880eadu, 0x56949d1bu, 0x569c4bc2u, 0x569e9fe2u, 0x56a90338u, 0x56aa46e2u, - 0x56aa83c0u, 0x56aae367u, 0x56afbee2u, 0x56b1a3e6u, 0x56b6c203u, 0x56b7a509u, - 0x56bd96c5u, 0x56c3e256u, 0x56c71f4eu, 0x56c7203du, 0x56d8f466u, 0x56dbfe2au, - 0x56dd6a6fu, 0x56ddd98bu, 0x56e180cau, 0x56e31610u, 0x56e50a15u, 0x56e5cfa2u, - 0x56e8470du, 0x56e851a6u, 0x56e88934u, 0x56f5e308u, 0x56f9f86du, 0x56fc4d01u, - 0x570c7c3cu, 0x57160a90u, 0x571681a4u, 0x571c066au, 0x571f7339u, 0x572bd24bu, - 0x572ffa0eu, 0x57377b45u, 0x573b482fu, 0x57405d9fu, 0x5744e123u, 0x5746639eu, - 0x5746c152u, 0x57516855u, 0x5757e245u, 0x575bb19du, 0x575c7998u, 0x5765dcf1u, - 0x57664eefu, 0x57680423u, 0x576c872cu, 0x576d79aeu, 0x5770c339u, 0x57751e29u, - 0x5777032du, 0x57855312u, 0x57869d02u, 0x57896adeu, 0x578a9796u, 0x578f4c28u, - 0x5791bd80u, 0x579381cbu, 0x5799cfa6u, 0x579b63fdu, 0x579d68e1u, 0x579da544u, - 0x579f7d00u, 0x57a4fde7u, 0x57a86373u, 0x57b9badau, 0x57bb9c5du, 0x57be5726u, - 0x57bfcd0eu, 0x57c3e4dau, 0x57cf33f8u, 0x57d2b54bu, 0x57e36989u, 0x57e5139bu, - 0x57ef0c36u, 0x57ef44fbu, 0x57f5d1e1u, 0x57fbf5f1u, 0x58034febu, 0x58037d54u, - 0x5805cb7au, 0x58123df2u, 0x5813a957u, 0x5813e239u, 0x581b06cfu, 0x581c8ddeu, - 0x5822f780u, 0x58240feeu, 0x5827a8aeu, 0x582a783cu, 0x582ae956u, 0x582d3bf3u, - 0x5832218cu, 0x58388580u, 0x5838a29bu, 0x583c12aau, 0x5857d2beu, 0x58630dafu, - 0x5864c4cfu, 0x5867fa4au, 0x5871c57cu, 0x587b9525u, 0x587dfd14u, 0x587ec94cu, - 0x58853ebfu, 0x589251b8u, 0x589768e5u, 0x58aa2d34u, 0x58bb7dc8u, 0x58be957eu, - 0x58c2b554u, 0x58c4fff9u, 0x58c8b5aeu, 0x58c962d6u, 0x58ca25c2u, 0x58cf98e6u, - 0x58d36369u, 0x58d46b89u, 0x58d7c91du, 0x58d86f98u, 0x58dc58b9u, 0x58ddc0fbu, - 0x58df2314u, 0x58e15c17u, 0x58f1a931u, 0x58f3a8a8u, 0x58f728a7u, 0x5911ba4au, - 0x5914e848u, 0x5915c156u, 0x591f6d6fu, 0x59279a25u, 0x593406c9u, 0x593b97e6u, - 0x593e3a44u, 0x5943847fu, 0x5945d049u, 0x59471675u, 0x594955c7u, 0x594bdff2u, - 0x5962a5a8u, 0x596c5513u, 0x5971ea27u, 0x59786912u, 0x597cfeeau, 0x59816d77u, - 0x59835a3fu, 0x5986bf21u, 0x5989bb31u, 0x59966fb1u, 0x5996d466u, 0x599cef88u, - 0x599ea856u, 0x59ac933du, 0x59b1d74fu, 0x59b32a48u, 0x59b51348u, 0x59b997d3u, - 0x59bed554u, 0x59c04229u, 0x59c4b06cu, 0x59c657edu, 0x59ca463du, 0x59cc9b6fu, - 0x59d258f6u, 0x59d2ddd1u, 0x59dd1a72u, 0x59de6643u, 0x59e0d93au, 0x59ecf6b4u, - 0x59edad15u, 0x59efe297u, 0x59f2f27bu, 0x59f8260bu, 0x5a00f6e5u, 0x5a17f74eu, - 0x5a1939dfu, 0x5a1cd112u, 0x5a1d6cabu, 0x5a1fb9b6u, 0x5a245695u, 0x5a26a082u, - 0x5a2ab7dcu, 0x5a2cfac2u, 0x5a2eafa7u, 0x5a333672u, 0x5a371191u, 0x5a424e4cu, - 0x5a46296bu, 0x5a482cbdu, 0x5a49827fu, 0x5a49a383u, 0x5a5347feu, 0x5a55eb7cu, - 0x5a66744fu, 0x5a6f2be1u, 0x5a701fd1u, 0x5a7404a6u, 0x5a78c990u, 0x5a8e8c6cu, - 0x5a9e3134u, 0x5a9e59dcu, 0x5aa2c8d6u, 0x5aac85b2u, 0x5ab095a3u, 0x5ab8de2du, - 0x5ab9fd8eu, 0x5abc5c9du, 0x5abe9cd4u, 0x5abfa40fu, 0x5ac14f5fu, 0x5aca4630u, - 0x5aca879bu, 0x5acc6c15u, 0x5acf833bu, 0x5ad062a6u, 0x5ad2b762u, 0x5ad6bbacu, - 0x5ad97807u, 0x5ae084ccu, 0x5ae1d728u, 0x5ae629beu, 0x5ae63254u, 0x5ae86cbeu, - 0x5ae9f0f5u, 0x5aea83b0u, 0x5af6000cu, 0x5afb262bu, 0x5b04da14u, 0x5b0e54a0u, - 0x5b1f5283u, 0x5b210dc5u, 0x5b2bc80cu, 0x5b2bfee6u, 0x5b3b7984u, 0x5b3eadcfu, - 0x5b41465du, 0x5b480135u, 0x5b48169fu, 0x5b4e4215u, 0x5b531c14u, 0x5b5961dcu, - 0x5b5b417bu, 0x5b64d786u, 0x5b7ef7e6u, 0x5b84d398u, 0x5b88b86du, 0x5b8d554cu, - 0x5b8f8b6bu, 0x5b91c2a0u, 0x5b971f75u, 0x5b9adba2u, 0x5b9d0e68u, 0x5ba15783u, - 0x5ba1802bu, 0x5ba63ecfu, 0x5baa72f9u, 0x5bb01eaau, 0x5bb893aeu, 0x5bbba1b3u, - 0x5bbd3c15u, 0x5bc2399eu, 0x5bc682a7u, 0x5bd80e03u, 0x5bd91f49u, 0x5bd99100u, - 0x5bdd6852u, 0x5beb77d6u, 0x5bebcafdu, 0x5bfa0f2au, 0x5bfc9cdfu, 0x5bff4b98u, - 0x5bffd9e4u, 0x5c0be078u, 0x5c10f063u, 0x5c123277u, 0x5c12a245u, 0x5c225655u, - 0x5c263374u, 0x5c2b6b61u, 0x5c3741a3u, 0x5c386539u, 0x5c43ae72u, 0x5c44347du, - 0x5c55e375u, 0x5c58fa6au, 0x5c5cfd99u, 0x5c6178e9u, 0x5c6c6d00u, 0x5c6e3708u, - 0x5c6f2ecau, 0x5c752933u, 0x5c76a291u, 0x5c8260f4u, 0x5c8904f2u, 0x5ca80381u, - 0x5cadf5eau, 0x5cb4fc60u, 0x5cb6c435u, 0x5cbce0dcu, 0x5cc354eau, 0x5cc511e6u, - 0x5cc9915bu, 0x5cce1499u, 0x5cd22ccbu, 0x5cd87b01u, 0x5cde32b3u, 0x5ce45e99u, - 0x5ce48096u, 0x5ce69a58u, 0x5cf01e01u, 0x5cf076ecu, 0x5cf899b0u, 0x5d0073f6u, - 0x5d12612du, 0x5d152468u, 0x5d18cfa5u, 0x5d1eaf59u, 0x5d20276bu, 0x5d2b7b74u, - 0x5d2d9d24u, 0x5d344cbdu, 0x5d34561cu, 0x5d351858u, 0x5d3edba8u, 0x5d456ce8u, - 0x5d45c772u, 0x5d45e0dbu, 0x5d502313u, 0x5d5ab884u, 0x5d5c8565u, 0x5d5dc44du, - 0x5d672227u, 0x5d67bf5fu, 0x5d69f9dbu, 0x5d6c36ddu, 0x5d6dca94u, 0x5d7210d6u, - 0x5d78a7d1u, 0x5d7be9ebu, 0x5d7d8bf1u, 0x5d80e63cu, 0x5d846f57u, 0x5d87ae20u, - 0x5d89dd3du, 0x5d9969c8u, 0x5d9afd07u, 0x5d9d9c09u, 0x5d9f91e5u, 0x5da4c68au, - 0x5dacf468u, 0x5dad1730u, 0x5daedceeu, 0x5daf0e2au, 0x5db13595u, 0x5db8fa75u, - 0x5dbaf0f7u, 0x5dc30ef0u, 0x5dc5552cu, 0x5dcf6a72u, 0x5dd515e3u, 0x5ddb4c21u, - 0x5dddfb80u, 0x5de29a88u, 0x5de3bc10u, 0x5de56fdbu, 0x5df11742u, 0x5df69532u, - 0x5dfb97d0u, 0x5dfdadd7u, 0x5dff3699u, 0x5e0bab71u, 0x5e13ea60u, 0x5e1821f2u, - 0x5e1a580du, 0x5e1d880fu, 0x5e20882fu, 0x5e333b21u, 0x5e337172u, 0x5e3413f4u, - 0x5e346c60u, 0x5e34ca8eu, 0x5e3871ebu, 0x5e38a617u, 0x5e3a335fu, 0x5e3b9e6eu, - 0x5e435a06u, 0x5e45cc52u, 0x5e4a1566u, 0x5e4db1ecu, 0x5e4ec48fu, 0x5e4f1532u, - 0x5e5274f8u, 0x5e5e3965u, 0x5e5ffbf7u, 0x5e610f83u, 0x5e62145fu, 0x5e639e99u, - 0x5e64445eu, 0x5e775a04u, 0x5e786b2fu, 0x5e7873e1u, 0x5e80b7b4u, 0x5e90081du, - 0x5e9099acu, 0x5e91e436u, 0x5e92b2a3u, 0x5e967adfu, 0x5e9c6e05u, 0x5ea170c1u, - 0x5ea2d01fu, 0x5eaf14e4u, 0x5eb29a44u, 0x5eb5ef33u, 0x5ebefd78u, 0x5ec0dc63u, - 0x5ecd7c57u, 0x5ecedd5au, 0x5ed206b1u, 0x5ed574fau, 0x5ee2fcf1u, 0x5ee3378cu, - 0x5ee7b597u, 0x5ee87146u, 0x5ee954d2u, 0x5eec1bb1u, 0x5ef39c1fu, 0x5ef92593u, - 0x5f0d7722u, 0x5f14aab7u, 0x5f1e7b1au, 0x5f2dbaccu, 0x5f307d61u, 0x5f386a04u, - 0x5f4d2f3eu, 0x5f548648u, 0x5f5caf6bu, 0x5f5d29e3u, 0x5f5d61bbu, 0x5f5ea1fdu, - 0x5f66c9c6u, 0x5f744260u, 0x5f76f953u, 0x5f7840deu, 0x5f79a1d2u, 0x5f80b9aau, - 0x5f86a886u, 0x5f92c8fcu, 0x5f93663cu, 0x5f9afedbu, 0x5f9c99bdu, 0x5fa4693au, - 0x5fb2384au, 0x5fb8bde8u, 0x5fb9b8a9u, 0x5fbab194u, 0x5fbb2a65u, 0x5fd13806u, - 0x5fdbc476u, 0x5fdc24d8u, 0x5fddb427u, 0x5fe1c409u, 0x5fe20e7fu, 0x5fe3a303u, - 0x5fe68570u, 0x5fe993b5u, 0x5fe9a0c0u, 0x5ff1a6fbu, 0x5ff493c1u, 0x5ff4f3c5u, - 0x5ff62ccdu, 0x5ff6728cu, 0x5ffb05c7u, 0x5ffbd791u, 0x5ffd254bu, 0x601554e3u, - 0x6019244au, 0x6019725du, 0x601ae1f4u, 0x601ee953u, 0x601f489bu, 0x602ac65eu, - 0x602dfd1du, 0x602e71e3u, 0x60371cf6u, 0x603876d0u, 0x6038a701u, 0x604bfd22u, - 0x604df8f3u, 0x604fb5b2u, 0x604fc501u, 0x6051a8a2u, 0x60544f5bu, 0x605cdb62u, - 0x606493c9u, 0x6066ed89u, 0x606759dcu, 0x606d5216u, 0x6076905eu, 0x60772945u, - 0x60776f04u, 0x607bcde4u, 0x608034d1u, 0x608154ddu, 0x6083d0a9u, 0x609ec6c2u, - 0x60a24001u, 0x60a69eb2u, 0x60a75637u, 0x60b1bbe4u, 0x60b95d35u, 0x60bd505fu, - 0x60bd6250u, 0x60be9c72u, 0x60bea30fu, 0x60bf70deu, 0x60c5d085u, 0x60caae65u, - 0x60cc540eu, 0x60d0ae66u, 0x60d10e0cu, 0x60d18ef2u, 0x60d55bb7u, 0x60df66e3u, - 0x60df7d2du, 0x60e16e3fu, 0x60ee8ae7u, 0x60f6d2b7u, 0x61001027u, 0x6100e0cfu, - 0x610b4289u, 0x610e9f9du, 0x6110f721u, 0x6111645fu, 0x611635c9u, 0x611a0e48u, - 0x611c62c4u, 0x611d5663u, 0x61244e5cu, 0x6127adb8u, 0x6135505bu, 0x61384d0du, - 0x613bf923u, 0x613e2aa8u, 0x613e2e41u, 0x6141c9e6u, 0x6149acfbu, 0x6151ba39u, - 0x61565f07u, 0x6157faadu, 0x615c185au, 0x616302e6u, 0x6163e18cu, 0x6165d6f7u, - 0x616830eeu, 0x616fb3a1u, 0x61779f39u, 0x617b5492u, 0x617ff960u, 0x618887ccu, - 0x618ce5ecu, 0x618d734cu, 0x618db694u, 0x618dc068u, 0x618e46c9u, 0x6194b58cu, - 0x61a3b9ffu, 0x61aacd30u, 0x61b24b8au, 0x61b3296cu, 0x61ba12a7u, 0x61bc9dc2u, - 0x61c6d8ecu, 0x61c72d07u, 0x61c97234u, 0x61cdd5cfu, 0x61ce880au, 0x61d629bbu, - 0x61d98309u, 0x61dd19f5u, 0x61e0a283u, 0x61e303a8u, 0x61e4421fu, 0x61e96897u, - 0x61ef45a1u, 0x61f27ce9u, 0x61f759abu, 0x61fd49ccu, 0x6200ab53u, 0x6201d016u, - 0x620533c9u, 0x6207a744u, 0x620e7180u, 0x6215fc23u, 0x62161e89u, 0x623c9ee5u, - 0x624b0f8cu, 0x624f0e0au, 0x624f4e7du, 0x624fbe30u, 0x6250534fu, 0x625c895fu, - 0x6265b705u, 0x62673fc3u, 0x6268cfe0u, 0x626ca79fu, 0x62702b13u, 0x62706ac4u, - 0x6273b15cu, 0x62753d51u, 0x62783ebcu, 0x627d6b0fu, 0x6284ae5au, 0x628545d6u, - 0x628baa33u, 0x628e250du, 0x629378e0u, 0x629e49a2u, 0x62a2520cu, 0x62a668ebu, - 0x62a6ca41u, 0x62ad67b2u, 0x62b2c2e4u, 0x62b8ddecu, 0x62b96055u, 0x62c2b821u, - 0x62ca02b2u, 0x62d60982u, 0x62e0b1eau, 0x62f1e023u, 0x62f29655u, 0x62f474b1u, - 0x62f5005du, 0x62fc0897u, 0x63063ee4u, 0x6310e170u, 0x63168d3eu, 0x63281782u, - 0x632edfc2u, 0x6330de0fu, 0x6331d0bcu, 0x633fab83u, 0x633fbfbcu, 0x63432841u, - 0x6349f18bu, 0x634ac423u, 0x634dd31eu, 0x635b327au, 0x6368d33bu, 0x63692e3cu, - 0x636c5bc9u, 0x636c9bc3u, 0x6378a508u, 0x6380af2du, 0x6384163bu, 0x638f9a29u, - 0x63937953u, 0x6396751fu, 0x63995f33u, 0x63a8c729u, 0x63a9a230u, 0x63b40d0du, - 0x63bcb7a1u, 0x63c16014u, 0x63c20f48u, 0x63c35de8u, 0x63c6a57eu, 0x63cf9967u, - 0x63d4a14cu, 0x63deb88du, 0x63e0bc49u, 0x63e449c1u, 0x63f4475bu, 0x63f5df5cu, - 0x63f8a973u, 0x64013ce4u, 0x6401b76fu, 0x64049c1au, 0x6407e0bcu, 0x640978bau, - 0x640d033bu, 0x640dbb4bu, 0x640fd922u, 0x64162e3fu, 0x641ddf49u, 0x641e9cc2u, - 0x6429452au, 0x642febf6u, 0x6434156du, 0x643e109du, 0x643f6a15u, 0x644b061cu, - 0x64536eb4u, 0x645ce6fcu, 0x646362f3u, 0x646703a0u, 0x646bdec0u, 0x646cf844u, - 0x646f33a0u, 0x646fa846u, 0x647088b0u, 0x6471a61du, 0x6474865au, 0x64784293u, - 0x647d8af7u, 0x647f391bu, 0x648189eau, 0x6486daaeu, 0x648c6ce3u, 0x64997499u, - 0x649d15bfu, 0x64a1375bu, 0x64a5bed3u, 0x64a60047u, 0x64a703e3u, 0x64af4032u, - 0x64af806du, 0x64b659e9u, 0x64bbe1c8u, 0x64bc243au, 0x64bd5a07u, 0x64c18c1cu, - 0x64c5912bu, 0x64cec350u, 0x64cf06fbu, 0x64cf0da0u, 0x64d574b0u, 0x64d71587u, - 0x64dbed37u, 0x64deb8c8u, 0x64e224c9u, 0x64ed2fa0u, 0x64ed5506u, 0x64efb062u, - 0x65008202u, 0x650cd654u, 0x650ec2b7u, 0x6515c781u, 0x651defe4u, 0x652cf8a6u, - 0x652f5b64u, 0x653044e8u, 0x6532e8dcu, 0x6535c720u, 0x653b1c90u, 0x653e865au, - 0x6558ed9bu, 0x655ba366u, 0x6560d291u, 0x6567566cu, 0x6567c6a1u, 0x656997f1u, - 0x6569c80du, 0x656baee5u, 0x656c19b1u, 0x65700ea1u, 0x657716bfu, 0x6584f55eu, - 0x65904655u, 0x6590a339u, 0x6592c834u, 0x659ce70fu, 0x659dfe29u, 0x65a0e52au, - 0x65a4f677u, 0x65ae5e2fu, 0x65b1e3b8u, 0x65b2b5fcu, 0x65ba893au, 0x65c2478eu, - 0x65c99a6au, 0x65ca17b9u, 0x65d12a6bu, 0x65d18b00u, 0x65db5afeu, 0x65f054b3u, - 0x65f628bcu, 0x65fdd0acu, 0x65fe4318u, 0x6609b4bbu, 0x660f4dbeu, 0x660f5508u, - 0x66185518u, 0x661a1396u, 0x662100d1u, 0x6622ee7eu, 0x6623210fu, 0x6624c178u, - 0x6626952fu, 0x6626f575u, 0x6629617du, 0x662990acu, 0x662bd0b0u, 0x662c8afeu, - 0x662cba2du, 0x662d2b6eu, 0x662d8f2eu, 0x663f3f16u, 0x6641af8cu, 0x66435a33u, - 0x6643a5fdu, 0x6645b22du, 0x6649a32eu, 0x664af128u, 0x66585799u, 0x666468aeu, - 0x66650861u, 0x666f15d3u, 0x667f9331u, 0x6682876eu, 0x66830d62u, 0x6683bac1u, - 0x668a3559u, 0x669a6f7au, 0x669ac39au, 0x66a133f4u, 0x66a61ecfu, 0x66a73744u, - 0x66a8818du, 0x66bab58du, 0x66baed06u, 0x66c6a1bdu, 0x66c73f70u, 0x66c84970u, - 0x66c87d0du, 0x66cecb6au, 0x66d06089u, 0x66d1aa15u, 0x66d5d4d2u, 0x66dd4c38u, - 0x66e0cb8au, 0x66e2f512u, 0x66e41f5eu, 0x66eab59du, 0x66ead7d1u, 0x66ee6036u, - 0x66f0a12eu, 0x66f62272u, 0x67081f28u, 0x67117603u, 0x671669b4u, 0x671ab1bdu, - 0x6725ccecu, 0x67361a5eu, 0x673a13ecu, 0x673c71c8u, 0x673f9624u, 0x6743e934u, - 0x6751bcafu, 0x675a5229u, 0x675aae13u, 0x675c7512u, 0x675f0879u, 0x6760543cu, - 0x6762822cu, 0x676b82bcu, 0x6770af9du, 0x67714349u, 0x6775ccdeu, 0x6777671cu, - 0x677fe07bu, 0x6783ad4cu, 0x678d19acu, 0x678f471cu, 0x6795b776u, 0x679d5c1du, - 0x67a69df6u, 0x67b40b00u, 0x67b71efcu, 0x67c28effu, 0x67c7b316u, 0x67c7e4f6u, - 0x67c97205u, 0x67ced8d9u, 0x67d39642u, 0x67e5106bu, 0x67ef5993u, 0x67f1b056u, - 0x67f659b1u, 0x67fbcc43u, 0x67fe2380u, 0x680337b2u, 0x68049a54u, 0x68051ba9u, - 0x680ced0eu, 0x680e6ad4u, 0x681305a3u, 0x6815ac95u, 0x6819c3fbu, 0x681a57a7u, - 0x681b103eu, 0x681e96c6u, 0x6820be77u, 0x6824eaa5u, 0x682abb58u, 0x6834c8b9u, - 0x68390567u, 0x683ccf89u, 0x683ff36bu, 0x6842ee3au, 0x684a98c0u, 0x684e8750u, - 0x684f9e85u, 0x68505508u, 0x6852c267u, 0x6855ee3cu, 0x685748ebu, 0x685801a2u, - 0x685a169fu, 0x68606a3eu, 0x6865ffa7u, 0x68691f61u, 0x686aa343u, 0x686de5d4u, - 0x687351cdu, 0x687a643fu, 0x687b4a31u, 0x687d63b3u, 0x68804bdcu, 0x6885a8acu, - 0x6891a488u, 0x68950f22u, 0x68971cd9u, 0x689802abu, 0x689b06c4u, 0x689eb3eeu, - 0x68a05d58u, 0x68a0c7f1u, 0x68a35c4fu, 0x68a39fe9u, 0x68a77c62u, 0x68a9e47fu, - 0x68afe173u, 0x68b8d3f4u, 0x68c1e0fcu, 0x68c6de91u, 0x68c72b75u, 0x68c86a46u, - 0x68c97811u, 0x68d47e95u, 0x68d664b0u, 0x68daf066u, 0x68ddef5du, 0x68de4e98u, - 0x68df26d8u, 0x68e6784fu, 0x68f32b3bu, 0x68f407c3u, 0x68fd974au, 0x69060d7eu, - 0x690d4c41u, 0x69106e0fu, 0x691bc9c7u, 0x6923b640u, 0x6926b8d2u, 0x69281d90u, - 0x69298b1bu, 0x69373600u, 0x6939ba4bu, 0x693f271eu, 0x694521eeu, 0x695ed323u, - 0x696319b0u, 0x696617a5u, 0x696f2e8cu, 0x6972bbf5u, 0x6972dd45u, 0x6974b0bcu, - 0x6975890cu, 0x69786b60u, 0x697dac8fu, 0x698a2b81u, 0x698e2defu, 0x6992cc5du, - 0x6993ea5fu, 0x69944878u, 0x69949fd8u, 0x69a5b241u, 0x69aace85u, 0x69abee12u, - 0x69ae0d73u, 0x69b1767eu, 0x69b7041cu, 0x69c019f9u, 0x69c1701bu, 0x69c49482u, - 0x69c69216u, 0x69caf966u, 0x69cd6193u, 0x69cedfc6u, 0x69dba884u, 0x69dd26b3u, - 0x69de14d5u, 0x69e358a1u, 0x69e42e58u, 0x69e4769fu, 0x69e7e02fu, 0x69e9957cu, - 0x69f1686cu, 0x69f417dau, 0x69f81491u, 0x69f8b4b5u, 0x69fe1f7cu, 0x69ff1e46u, - 0x6a0292cbu, 0x6a081d4bu, 0x6a0877c5u, 0x6a0bfd67u, 0x6a1c9ce7u, 0x6a2d4d90u, - 0x6a3370f0u, 0x6a33842fu, 0x6a3c9ca5u, 0x6a3cc853u, 0x6a3ea17fu, 0x6a4060adu, - 0x6a45ce11u, 0x6a4894deu, 0x6a48c198u, 0x6a58726bu, 0x6a5ad4dfu, 0x6a63b637u, - 0x6a68f7ebu, 0x6a6c33bbu, 0x6a6e4380u, 0x6a758b9fu, 0x6a772453u, 0x6a80789du, - 0x6a826379u, 0x6a848469u, 0x6a864208u, 0x6a893de0u, 0x6a94ad5bu, 0x6a9d6f7fu, - 0x6aa6bcffu, 0x6aaa92b8u, 0x6aadedb9u, 0x6ab0188au, 0x6ab02f6au, 0x6ab05db1u, - 0x6ab1e4b7u, 0x6ab3dd18u, 0x6abde11bu, 0x6ac20d24u, 0x6ac76a67u, 0x6acb9a15u, - 0x6ad28287u, 0x6ad45cbfu, 0x6ad64f68u, 0x6ad8b0d2u, 0x6ad8da6au, 0x6add8eb0u, - 0x6ae51f9bu, 0x6ae6cf2fu, 0x6ae78957u, 0x6af1a4bfu, 0x6af2796cu, 0x6af6cae4u, - 0x6af87f27u, 0x6af9ea9fu, 0x6b173b3au, 0x6b216e8eu, 0x6b23bbb1u, 0x6b25add4u, - 0x6b264395u, 0x6b26910fu, 0x6b27765fu, 0x6b2eb30du, 0x6b2f5972u, 0x6b33f693u, - 0x6b3bd8dfu, 0x6b3f1b2du, 0x6b433c08u, 0x6b4448dcu, 0x6b458dd8u, 0x6b59ac7du, - 0x6b622723u, 0x6b693ee6u, 0x6b69e336u, 0x6b6bbe36u, 0x6b7175b1u, 0x6b769e6fu, - 0x6b78ab18u, 0x6b82c59eu, 0x6b89fc73u, 0x6b8e2793u, 0x6b94a1eau, 0x6b996f88u, - 0x6b9af8e5u, 0x6ba36925u, 0x6ba732e7u, 0x6baa3f64u, 0x6baa7854u, 0x6baafd71u, - 0x6babaec1u, 0x6bade4c0u, 0x6bb01ddeu, 0x6bb75982u, 0x6bb9c6e2u, 0x6bba3e7au, - 0x6bc3600au, 0x6bc5e6e4u, 0x6bc649b0u, 0x6bc93b65u, 0x6bcd73e9u, 0x6bcdf5c6u, - 0x6bceaf04u, 0x6bd323d9u, 0x6bd5b72eu, 0x6bd79ea9u, 0x6be0a639u, 0x6be7bfceu, - 0x6c0d2d45u, 0x6c1fe7bau, 0x6c27b08bu, 0x6c2b7327u, 0x6c2f47f6u, 0x6c31f0beu, - 0x6c36d4b8u, 0x6c3c387bu, 0x6c3f67dfu, 0x6c42999au, 0x6c467657u, 0x6c4d39deu, - 0x6c594d23u, 0x6c5d1f9bu, 0x6c6d1f15u, 0x6c6eca81u, 0x6c7181e9u, 0x6c78e11bu, - 0x6c7a4164u, 0x6c81d973u, 0x6c86ab9eu, 0x6c8a63c3u, 0x6c8dee26u, 0x6c96d207u, - 0x6c98ede5u, 0x6c9a90acu, 0x6c9bd95du, 0x6ca8a510u, 0x6cae9cdbu, 0x6cc0e8b0u, - 0x6cc17035u, 0x6cc47fe1u, 0x6cc67432u, 0x6ccada80u, 0x6cd491c0u, 0x6cd4f256u, - 0x6cdaae23u, 0x6cdc0325u, 0x6cdf4fd1u, 0x6ce948bfu, 0x6ceada3cu, 0x6ceb5a58u, - 0x6cf7df96u, 0x6cfddb58u, 0x6d004229u, 0x6d03ee82u, 0x6d0bd2c9u, 0x6d0f2d98u, - 0x6d16d358u, 0x6d1c37ebu, 0x6d1eaa01u, 0x6d23d99bu, 0x6d2dee31u, 0x6d2e6b58u, - 0x6d368088u, 0x6d3fb264u, 0x6d42956du, 0x6d4e1561u, 0x6d4f5ab9u, 0x6d53935eu, - 0x6d53a988u, 0x6d6152dfu, 0x6d62f8cdu, 0x6d6488e0u, 0x6d6b327cu, 0x6d6cfe57u, - 0x6d76fa85u, 0x6d8568ecu, 0x6d86e3f2u, 0x6d87e19du, 0x6d8999f2u, 0x6d8b6ff7u, - 0x6d8ca8b3u, 0x6d9281feu, 0x6d9ad706u, 0x6db56fa6u, 0x6db741e8u, 0x6dbb44abu, - 0x6dbfb75fu, 0x6dc0865fu, 0x6dc2c055u, 0x6dc32257u, 0x6dc6c8feu, 0x6dcb2fe6u, - 0x6dd45116u, 0x6dd452b6u, 0x6de30535u, 0x6de8b56cu, 0x6defa823u, 0x6df40b14u, - 0x6dfe779bu, 0x6e033a59u, 0x6e061e81u, 0x6e0f6e0du, 0x6e0ff40du, 0x6e10ce56u, - 0x6e1217d9u, 0x6e1b8b97u, 0x6e1c232eu, 0x6e1cb806u, 0x6e1cc27fu, 0x6e2b59a4u, - 0x6e2c6aa0u, 0x6e37b170u, 0x6e3b8366u, 0x6e3c680eu, 0x6e3c7aa9u, 0x6e406a01u, - 0x6e412a91u, 0x6e41bad6u, 0x6e425594u, 0x6e483f57u, 0x6e515ad5u, 0x6e57c346u, - 0x6e5a9507u, 0x6e64a076u, 0x6e729864u, 0x6e7d4f09u, 0x6e812cf8u, 0x6e828169u, - 0x6e8b4f61u, 0x6e8c3541u, 0x6e8cb31fu, 0x6e9342f6u, 0x6e9795feu, 0x6ea31f80u, - 0x6eab4dd9u, 0x6eae8f08u, 0x6eb02d2fu, 0x6eb55a25u, 0x6ebf0d88u, 0x6ec45bd7u, - 0x6ec74ef1u, 0x6ecfb528u, 0x6eda07c2u, 0x6edd841du, 0x6ee452e3u, 0x6eefd8fdu, - 0x6ef83b4au, 0x6efb53c1u, 0x6f0c15b6u, 0x6f11b91eu, 0x6f1a853bu, 0x6f1d2a4du, - 0x6f1fb3d8u, 0x6f246a9du, 0x6f2860b8u, 0x6f2d1abeu, 0x6f2edacdu, 0x6f32a090u, - 0x6f371657u, 0x6f37d170u, 0x6f3b2be3u, 0x6f3ea42du, 0x6f3ef76bu, 0x6f410359u, - 0x6f4c3b2au, 0x6f4f8abcu, 0x6f622282u, 0x6f6d0924u, 0x6f70cf1bu, 0x6f76724fu, - 0x6f7d0a3bu, 0x6f8339b3u, 0x6f88ad04u, 0x6f8ac870u, 0x6f91f8e7u, 0x6f926d64u, - 0x6fa06dfdu, 0x6fa330e6u, 0x6fa57060u, 0x6fa58de9u, 0x6fa9e459u, 0x6faff210u, - 0x6fb0e1b6u, 0x6fb15398u, 0x6fb3b36au, 0x6fbefb48u, 0x6fc5d0fbu, 0x6fc9fae3u, - 0x6fd0f665u, 0x6fd97307u, 0x6fda1f21u, 0x6fe2c817u, 0x6fe430f1u, 0x6fee2242u, - 0x6ff471c4u, 0x6ff66bfeu, 0x6ff692dbu, 0x7005700du, 0x7011a8cbu, 0x70159d0bu, - 0x701fe490u, 0x70214d6au, 0x7024d042u, 0x702825d2u, 0x702fededu, 0x703567c3u, - 0x703e7bfcu, 0x70439deau, 0x70490088u, 0x7049cab2u, 0x704a479cu, 0x704bb076u, - 0x705150afu, 0x7051b7dcu, 0x7051d14fu, 0x705fc7a0u, 0x706040d4u, 0x7061dc94u, - 0x70655a55u, 0x7069eeb3u, 0x706e432eu, 0x706ef79bu, 0x707041ebu, 0x70782890u, - 0x709012d9u, 0x709a8047u, 0x70a5ab5cu, 0x70aef4d0u, 0x70b2d9cbu, 0x70c5b388u, - 0x70c7ada8u, 0x70d3a69du, 0x70d4678du, 0x70d4d619u, 0x70d732c9u, 0x70dd10f0u, - 0x70e50754u, 0x70e5c7cau, 0x70e837e2u, 0x70eb45b6u, 0x70ef307fu, 0x70ef8c4du, - 0x70f0187cu, 0x70f742dbu, 0x71008f7bu, 0x7101fa30u, 0x7107652eu, 0x710ac173u, - 0x710b972cu, 0x7110a867u, 0x7112229eu, 0x7112ebbcu, 0x7114ea61u, 0x7119aafbu, - 0x711a3480u, 0x711b16f9u, 0x7123b4cdu, 0x7129b4a2u, 0x712bda24u, 0x712fe209u, - 0x7135d1cau, 0x71400a77u, 0x7142e105u, 0x7143dfb3u, 0x7145e43eu, 0x71477f84u, - 0x71499ea6u, 0x71569cfeu, 0x71648852u, 0x716f34dfu, 0x71788090u, 0x7179b385u, - 0x71831bf9u, 0x718aec1eu, 0x718e20f4u, 0x719824a3u, 0x719d983du, 0x719f1a8au, - 0x719f8e15u, 0x71a15c04u, 0x71ae93bau, 0x71af40a3u, 0x71b2a2ceu, 0x71b67990u, - 0x71b788e4u, 0x71bcadacu, 0x71c6dff9u, 0x71c8bbd8u, 0x71cbbf5au, 0x71cdcccau, - 0x71d941e3u, 0x71dbb12cu, 0x71e13385u, 0x71ed6a69u, 0x71eebd00u, 0x7203c925u, - 0x7205f92au, 0x7208069au, 0x720c8cb2u, 0x721199eeu, 0x721220c3u, 0x72154729u, - 0x7215e0c2u, 0x72177590u, 0x721979a1u, 0x72215a66u, 0x7224fac3u, 0x72292867u, - 0x72384486u, 0x7239c1bcu, 0x723c8e96u, 0x723fe46fu, 0x7240ee1au, 0x72426ce8u, - 0x72430575u, 0x724447f2u, 0x72484890u, 0x724b2e57u, 0x724f8cc3u, 0x72501f6au, - 0x72505c41u, 0x725804bau, 0x725fc4cbu, 0x726adcd9u, 0x72718bbbu, 0x72738a6cu, - 0x727a3ef1u, 0x72826a7cu, 0x728d5b21u, 0x728f2074u, 0x72925774u, 0x72962009u, - 0x7298a6eau, 0x72a4f96bu, 0x72a5a292u, 0x72a64e26u, 0x72a8e4d8u, 0x72ad5760u, - 0x72b608b8u, 0x72b6a547u, 0x72b8f4a8u, 0x72bd52d4u, 0x72bd7635u, 0x72c67c42u, - 0x72ca61c9u, 0x72cfda3du, 0x72f4b445u, 0x72f94896u, 0x72fd3e77u, 0x7300e8dbu, - 0x7301263eu, 0x7304b07eu, 0x7306b93du, 0x7308e24au, 0x730e8d24u, 0x7311227eu, - 0x7315b6cfu, 0x73181fefu, 0x73190478u, 0x731a3ca7u, 0x7320cba4u, 0x73233cceu, - 0x7323b1f4u, 0x73240e93u, 0x7325b75au, 0x732af179u, 0x73349a84u, 0x7339f6f5u, - 0x73473896u, 0x73492e08u, 0x7351ade9u, 0x73553737u, 0x735fffa4u, 0x736225beu, - 0x7365817au, 0x736b7a57u, 0x7375b606u, 0x7376ee9bu, 0x737b2fd2u, 0x7384d132u, - 0x738d6dc2u, 0x738f6525u, 0x73944644u, 0x7394e4b4u, 0x739bc108u, 0x739c4757u, - 0x73a63131u, 0x73a8b440u, 0x73aaa95cu, 0x73abd96eu, 0x73acffebu, 0x73adb073u, - 0x73af1904u, 0x73ba1a69u, 0x73c85607u, 0x73d448f6u, 0x73dd926fu, 0x73dde477u, - 0x73e1cd9du, 0x73ec90adu, 0x73f25aa0u, 0x73fc069eu, 0x73fcdd5au, 0x73fe8a8cu, - 0x73ffdb42u, 0x740006ffu, 0x7400e29fu, 0x74153078u, 0x74169b73u, 0x741c2c3bu, - 0x741d44d8u, 0x741dca1du, 0x7420bef9u, 0x7422abbdu, 0x74259230u, 0x74262771u, - 0x742933f5u, 0x7429e55cu, 0x742b776au, 0x742d9e0du, 0x74300f37u, 0x7430aa5eu, - 0x743597d2u, 0x743721bbu, 0x7438bb42u, 0x743ae696u, 0x744080c0u, 0x7443bac6u, - 0x745047d2u, 0x74570e67u, 0x745b299du, 0x74628d75u, 0x746f5fd6u, 0x747365abu, - 0x747592e8u, 0x747c18aeu, 0x74805427u, 0x7482a20cu, 0x7485a609u, 0x7486adcbu, - 0x7488a8a2u, 0x749a54d4u, 0x74a07addu, 0x74a3689eu, 0x74ac8f82u, 0x74acff3bu, - 0x74b45a07u, 0x74b52c1au, 0x74ba1b77u, 0x74bc6cd4u, 0x74bdc908u, 0x74c2978eu, - 0x74c5a35eu, 0x74ce3050u, 0x74ddaa98u, 0x74e2a00au, 0x74e2d58eu, 0x74e41efeu, - 0x74eb8515u, 0x74ebc3d0u, 0x74ec77ffu, 0x74efd5a4u, 0x74f4fb8fu, 0x74f5973fu, - 0x74fcdac1u, 0x74fd8777u, 0x74fe0c91u, 0x75031c5eu, 0x7507d0a7u, 0x7514f801u, - 0x751554c9u, 0x7519191fu, 0x751afa98u, 0x7520aad3u, 0x7523d562u, 0x752b6b42u, - 0x752b91d4u, 0x752d80dbu, 0x752e2ef5u, 0x7532063au, 0x7535b851u, 0x75496e21u, - 0x754e9439u, 0x7550196fu, 0x7561ec08u, 0x756300d3u, 0x75651429u, 0x756e1a0bu, - 0x756e63b7u, 0x7578ed3eu, 0x7580e196u, 0x75951852u, 0x7595800du, 0x759ba591u, - 0x75a13286u, 0x75a85cd8u, 0x75af0cd7u, 0x75b37db6u, 0x75b6d893u, 0x75be750du, - 0x75bee813u, 0x75bf3a6fu, 0x75c91b3fu, 0x75ca1b03u, 0x75d0dd14u, 0x75d51f8fu, - 0x75d68819u, 0x75d7ed82u, 0x75e3c341u, 0x75e8e587u, 0x75ee84e7u, 0x75eea42eu, - 0x75f01ecau, 0x75fd3fb5u, 0x75fd648bu, 0x76005e7du, 0x76054d81u, 0x7608d2c4u, - 0x76095e4au, 0x760b9009u, 0x760c182cu, 0x761b0305u, 0x7622cde3u, 0x7624ff49u, - 0x762e7a47u, 0x7632b43eu, 0x763794c0u, 0x763ad335u, 0x763b23d4u, 0x763c1bf3u, - 0x763ded9cu, 0x7641b9c3u, 0x7644c50cu, 0x76459ba2u, 0x76459ffau, 0x7655e02au, - 0x765d20b9u, 0x765e207du, 0x765e64c6u, 0x765ea4b7u, 0x765fa47bu, 0x7665798cu, - 0x76695079u, 0x766ac04fu, 0x766ce83du, 0x76797829u, 0x767d7523u, 0x768c98cbu, - 0x769b3773u, 0x76a08a1du, 0x76a129cbu, 0x76b4031cu, 0x76bae552u, 0x76bd1312u, - 0x76cab1f6u, 0x76cff4bau, 0x76d02240u, 0x76d854c3u, 0x76d85974u, 0x76e4a8bdu, - 0x76e8f977u, 0x76ed0814u, 0x76ef0171u, 0x76fc75adu, 0x76fd549au, 0x770a499eu, - 0x771089a1u, 0x7711c9afu, 0x77172b29u, 0x77187472u, 0x77193dd9u, 0x771bc98eu, - 0x7721554cu, 0x772974b2u, 0x772f428du, 0x772f7b48u, 0x773c9cc4u, 0x77443455u, - 0x7752593bu, 0x775488ceu, 0x7756ffecu, 0x775a86b4u, 0x77620782u, 0x77676acfu, - 0x77682bb8u, 0x77689026u, 0x776c6bcfu, 0x7771b0bfu, 0x77736cbbu, 0x777cce02u, - 0x778d6ac9u, 0x77967acau, 0x779794ecu, 0x779c01a5u, 0x779c8328u, 0x77a95af8u, - 0x77a9aa44u, 0x77ae3aefu, 0x77b65572u, 0x77b78037u, 0x77b7f2eau, 0x77bd3949u, - 0x77bdb7a3u, 0x77c05c2eu, 0x77c14194u, 0x77c3bd0au, 0x77c432cdu, 0x77cc998cu, - 0x77d07483u, 0x77d0823du, 0x77d3fe5bu, 0x77d43092u, 0x77d4dce4u, 0x77d8727cu, - 0x77d88bf6u, 0x77d8ccfdu, 0x77dc7ac7u, 0x77e40388u, 0x77f5888du, 0x77fe04f9u, - 0x77fe3363u, 0x7805a269u, 0x780abb74u, 0x780b7e70u, 0x780c80ebu, 0x780cafc0u, - 0x781704ebu, 0x7823f603u, 0x78249385u, 0x7828bc0eu, 0x78327ae6u, 0x783efea0u, - 0x783fe000u, 0x7841c2a5u, 0x78466723u, 0x784bed01u, 0x784f0e0cu, 0x7855227cu, - 0x78553249u, 0x7855713du, 0x7859289bu, 0x786ea78du, 0x7876950eu, 0x7877c07eu, - 0x7879b9cdu, 0x787d5a81u, 0x788a0791u, 0x7893c0a2u, 0x7893e558u, 0x789923a4u, - 0x7899fc9fu, 0x789af224u, 0x789bacbbu, 0x789e5c29u, 0x789f922au, 0x78aa435eu, - 0x78b336f5u, 0x78b7c6dcu, 0x78b7e8f4u, 0x78bb2186u, 0x78bd0e6au, 0x78c3d203u, - 0x78c929b0u, 0x78ca3c83u, 0x78ca9beeu, 0x78cc8fafu, 0x78ccd205u, 0x78ccf243u, - 0x78d10109u, 0x78d229dbu, 0x78d4ffb0u, 0x78dadb45u, 0x78e7b5fcu, 0x78ed712au, - 0x78f1c26fu, 0x78f2471bu, 0x78f691bcu, 0x78f95b4fu, 0x7908aa44u, 0x7908fbccu, - 0x790c9b08u, 0x790ee993u, 0x7911d09du, 0x7915cee2u, 0x79182e79u, 0x79189bcfu, - 0x79207264u, 0x7920a49bu, 0x792668dau, 0x7926b8eau, 0x79270d39u, 0x79362ac4u, - 0x7937d819u, 0x793a17d4u, 0x793dd24bu, 0x794062cau, 0x794275dcu, 0x7945cdbfu, - 0x7948e526u, 0x79494845u, 0x7959b88cu, 0x795c52e0u, 0x795daeadu, 0x795e734au, - 0x79605274u, 0x79675caau, 0x79683cdcu, 0x7968b815u, 0x79742386u, 0x797917b2u, - 0x797b4864u, 0x797e1d75u, 0x79812e4cu, 0x7989dddcu, 0x79900bbdu, 0x7990e1f1u, - 0x799681e4u, 0x7996ce9au, 0x79983f58u, 0x799b434au, 0x799c54cau, 0x79a2d5dcu, - 0x79a54bafu, 0x79b03330u, 0x79b4a7e0u, 0x79b4fa24u, 0x79bc8e05u, 0x79bf5709u, - 0x79c09414u, 0x79c0d16bu, 0x79c2df49u, 0x79d085fdu, 0x79d37295u, 0x79dc065bu, - 0x79dee748u, 0x79e4ffdau, 0x79f203dcu, 0x79ff33c7u, 0x7a018680u, 0x7a024ecfu, - 0x7a036a20u, 0x7a17b3a0u, 0x7a192197u, 0x7a198280u, 0x7a1a7362u, 0x7a21081au, - 0x7a21fad8u, 0x7a28f812u, 0x7a296d5du, 0x7a3053fbu, 0x7a37e7abu, 0x7a432ec8u, - 0x7a471791u, 0x7a47728cu, 0x7a4f7b87u, 0x7a62dbbeu, 0x7a658339u, 0x7a6a7c37u, - 0x7a722449u, 0x7a7762c2u, 0x7a78ea4bu, 0x7a79826cu, 0x7a7e4169u, 0x7a8cf631u, - 0x7a8db803u, 0x7a977471u, 0x7a9afb94u, 0x7a9df969u, 0x7aa6151bu, 0x7aa876bbu, - 0x7aafc66au, 0x7ab18f8bu, 0x7abcf156u, 0x7ac0b6ebu, 0x7ac3510fu, 0x7ac9cbd6u, - 0x7acc7b4au, 0x7ad6e9f9u, 0x7ae004d2u, 0x7ae07c86u, 0x7ae70ed5u, 0x7aecbeb6u, - 0x7aed46b8u, 0x7af6887bu, 0x7af81504u, 0x7afac2fcu, 0x7afc3477u, 0x7b13b511u, - 0x7b174e8cu, 0x7b18362bu, 0x7b1c5ca9u, 0x7b209492u, 0x7b246ee8u, 0x7b2f7203u, - 0x7b389c6du, 0x7b44d52au, 0x7b50c71eu, 0x7b531fc2u, 0x7b55c197u, 0x7b5a8ff6u, - 0x7b6e9429u, 0x7b76cdb0u, 0x7b793d95u, 0x7b7aecf4u, 0x7b8198b5u, 0x7b940727u, - 0x7b9fef55u, 0x7ba3796fu, 0x7ba4c1a4u, 0x7bac38bfu, 0x7bbba083u, 0x7bbfd2ddu, - 0x7bc84b35u, 0x7bd126c7u, 0x7be10da8u, 0x7be6cf82u, 0x7be8b80bu, 0x7bedd49cu, - 0x7bee0a29u, 0x7bee21c9u, 0x7bfdfc34u, 0x7c031f56u, 0x7c0e1eecu, 0x7c12ace7u, - 0x7c1407d3u, 0x7c14dcbbu, 0x7c1bdc3au, 0x7c1d32e9u, 0x7c1fa9afu, 0x7c20973cu, - 0x7c214750u, 0x7c298fb6u, 0x7c29c63cu, 0x7c34353eu, 0x7c38a6a4u, 0x7c3b24fdu, - 0x7c3c77b6u, 0x7c44d6c3u, 0x7c4535bau, 0x7c52183eu, 0x7c53a3ceu, 0x7c5b2aa9u, - 0x7c61e6d3u, 0x7c681eebu, 0x7c6d0840u, 0x7c74d666u, 0x7c7b3643u, 0x7c7ecbdau, - 0x7c814597u, 0x7c89fb62u, 0x7c8c829au, 0x7c907ac5u, 0x7c9909c7u, 0x7c9c3b2eu, - 0x7ca0bda9u, 0x7cadc062u, 0x7cae4c57u, 0x7cb02babu, 0x7cb2d414u, 0x7cb879f9u, - 0x7cbe947du, 0x7cbed82bu, 0x7cd21cd7u, 0x7cd929d1u, 0x7cda58bbu, 0x7ce00098u, - 0x7ce18e76u, 0x7ce27470u, 0x7ce3383du, 0x7ce9174cu, 0x7cec2c74u, 0x7cf07fa0u, - 0x7d01f7a9u, 0x7d029045u, 0x7d081a91u, 0x7d0996aeu, 0x7d137a64u, 0x7d14fc19u, - 0x7d155f50u, 0x7d18123au, 0x7d1c6b51u, 0x7d207395u, 0x7d252463u, 0x7d26c257u, - 0x7d297e20u, 0x7d2cde83u, 0x7d31a4acu, 0x7d37e69fu, 0x7d3d48d6u, 0x7d4af079u, - 0x7d4f3488u, 0x7d53307bu, 0x7d57b80du, 0x7d5a42e2u, 0x7d5e28fcu, 0x7d635039u, - 0x7d63a382u, 0x7d69e912u, 0x7d766f46u, 0x7d7a023fu, 0x7d7e81c6u, 0x7d7ef07du, - 0x7d953320u, 0x7d958d64u, 0x7d963e4fu, 0x7d9bab49u, 0x7d9d366fu, 0x7d9e7e26u, - 0x7d9eabfbu, 0x7da29fe0u, 0x7da7fb5bu, 0x7daa5567u, 0x7dae09e4u, 0x7db4febau, - 0x7db98a14u, 0x7dbd7003u, 0x7dca911eu, 0x7dcf9a4cu, 0x7dd19511u, 0x7dd79a3du, - 0x7ddb83c9u, 0x7dde61e7u, 0x7de6ab1bu, 0x7deb6b06u, 0x7dedaa75u, 0x7df1c12du, - 0x7df521e3u, 0x7df8127cu, 0x7dfe321au, 0x7dff4b6au, 0x7e00954bu, 0x7e00986au, - 0x7e00c6fcu, 0x7e02bbc2u, 0x7e03becfu, 0x7e083450u, 0x7e0bd237u, 0x7e104ad7u, - 0x7e1265ceu, 0x7e1ad37au, 0x7e1e669eu, 0x7e206ed1u, 0x7e209f12u, 0x7e215db4u, - 0x7e216934u, 0x7e2472c5u, 0x7e3078a2u, 0x7e33971au, 0x7e34f68eu, 0x7e371d05u, - 0x7e3f28bfu, 0x7e4051f3u, 0x7e53eacdu, 0x7e577df1u, 0x7e5eca56u, 0x7e666a0eu, - 0x7e6f513eu, 0x7e721345u, 0x7e739a50u, 0x7e7401cdu, 0x7e7894d5u, 0x7e84e1c0u, - 0x7e946aa8u, 0x7e974529u, 0x7e9fa8c5u, 0x7ea144f6u, 0x7ea232ddu, 0x7ea25bafu, - 0x7ea80382u, 0x7eafeac4u, 0x7eb32eabu, 0x7ebb51b3u, 0x7ebfe5ecu, 0x7ec81ecdu, - 0x7ed01b6fu, 0x7ed443a1u, 0x7ed48dd1u, 0x7edc874fu, 0x7edd1025u, 0x7ee0026du, - 0x7ee33868u, 0x7ee3a01fu, 0x7ee5b26cu, 0x7ef030fau, 0x7ef24b6eu, 0x7ef2b58cu, - 0x7f060c08u, 0x7f13e9dcu, 0x7f178880u, 0x7f1c49a1u, 0x7f28d815u, 0x7f2d7b14u, - 0x7f33a10du, 0x7f340c5cu, 0x7f34b8f7u, 0x7f3645e9u, 0x7f3e5dc8u, 0x7f430e57u, - 0x7f45d05eu, 0x7f4931ccu, 0x7f4a25f1u, 0x7f4bf0f3u, 0x7f4dcf28u, 0x7f523d74u, - 0x7f543611u, 0x7f5830a8u, 0x7f62b0e5u, 0x7f6dbf3fu, 0x7f786dcfu, 0x7f789ec8u, - 0x7f7a5658u, 0x7f7ac89du, 0x7f7c5b32u, 0x7f7d2593u, -}; -ALTD_MATH_TABLE uint32_t UCRT_SINF_WIDE_VALUES[2536] = { - 0x3f7ffe06u, 0xbf7ffef7u, 0xbf7fff03u, 0xbf7ffe1fu, 0xbf7ffe9bu, 0x3f7ffe0fu, - 0xbf7ffeebu, 0x3bfc3c8bu, 0x3bc2a43au, 0x3f7ffeb3u, 0x3f7fffa3u, 0x3f7ffe9au, - 0x3f7ffe54u, 0xbf7fff11u, 0x3f7fff20u, 0xbf7ffe80u, 0xbf7ffe16u, 0xbf7ffe32u, - 0x3f7ffe14u, 0x3b5a1c57u, 0x3f7ffe1bu, 0xbf7fff25u, 0xbf7fff88u, 0xbf7ffeeeu, - 0xbf7fff07u, 0x3f7ffe58u, 0xbf7ffe3eu, 0x3f7ffe05u, 0x3f7ffe9fu, 0x3f7fff6du, - 0x3f7ffed7u, 0xbf7ffe57u, 0x3f7ffea1u, 0xbf7fff14u, 0xbbeec976u, 0xbf7ffe0au, - 0xbbddc72fu, 0xbf7fff19u, 0x3f7ffe30u, 0xbbf7703du, 0xbf7ffe89u, 0xbf7ffee4u, - 0x3f7ffe2au, 0x3f7ffe50u, 0xbf7ffe17u, 0x3f7ffe9du, 0xbf7ffe02u, 0x3f7ffe28u, - 0x3f7ffe7eu, 0x3f7ffe0bu, 0x3f7fff18u, 0x3f7ffe3fu, 0xbf7ffe54u, 0xbf7ffe43u, - 0x3f7fff03u, 0x3f7ffef7u, 0x3f7fff05u, 0x3f7ffe1cu, 0xbf7ffe18u, 0x3f7ffea7u, - 0xbf7ffe1eu, 0xbf7ffed5u, 0x3f7ffe95u, 0x3f7ffe85u, 0xbb8e69a3u, 0x3f7ffeb8u, - 0xbf7fff52u, 0xbf7ffe0du, 0x3f7ffed8u, 0x3bc195a7u, 0xbf7ffe56u, 0x3f7ffe9au, - 0xbf7ffebeu, 0x3f7ffe69u, 0x3f7ffe29u, 0xbbf110cfu, 0xbf7ffee9u, 0x3f7fffbau, - 0x3f7ffe0au, 0xbf7ffed7u, 0xbf7ffe9fu, 0xbf7ffe1bu, 0x3f7fff11u, 0x3f7ffebbu, - 0x3bf73dcau, 0xbf7ffe0fu, 0x3f7ffe1fu, 0xbf7ffe06u, 0x3f7ffe82u, 0x3f7ffe35u, - 0xbf7ffe4bu, 0xbf7fff0du, 0xbbd9f7c4u, 0xbbc9336au, 0x3f7ffe25u, 0xbbb6bb96u, - 0x3f7ffe30u, 0xbf7ffe31u, 0x3f7ffe7bu, 0x3f7ffedau, 0xbf7ffe1cu, 0x3bdcef60u, - 0xbf7ffe81u, 0x3f7fff5cu, 0xbf7ffe7eu, 0x3f7ffec3u, 0x3f7fff95u, 0xbf7ffed1u, - 0xbf7ffe14u, 0xbf7fff7eu, 0x3f7ffe84u, 0xbf7ffec6u, 0x3f7ffe63u, 0x3f7ffeb3u, - 0x3f7ffe05u, 0xbf7ffe53u, 0xbf7ffe98u, 0xbf7ffe7du, 0x3be5e894u, 0x3f7ffeaau, - 0xbf7ffe09u, 0x3f7ffe55u, 0xbbed977bu, 0x3f7ffe36u, 0xbbe88825u, 0x3f7ffe16u, - 0xbbe0357du, 0x3f7ffe0cu, 0x3f7ffe2fu, 0x3bc0ac45u, 0x3f7ffeb0u, 0xbf7ffe66u, - 0x3f7fff80u, 0x3f7ffe8cu, 0x3f7ffe8cu, 0x3f7fffc7u, 0x3f7ffe94u, 0xbf7ffe66u, - 0xbbed9468u, 0x3bf030f7u, 0x3f7ffe2fu, 0x3f7ffe77u, 0x3f7ffe10u, 0xbf7ffe88u, - 0x3f7fff9du, 0xbf7fff67u, 0xbf7ffe98u, 0xbf7fffb9u, 0x3f7ffedau, 0x3f7ffeb5u, - 0x3bfcdfe4u, 0xbf7ffe08u, 0x3f7fff58u, 0xbf7ffe04u, 0xbbe1bc20u, 0x3f7ffeeeu, - 0x3f7ffef3u, 0xbb8aee20u, 0xbf7fff2fu, 0xbbd35746u, 0xbf7ffeeau, 0xbf7fff1cu, - 0x3bff62aau, 0x3b9e9841u, 0xbf7ffe77u, 0xbf7ffee3u, 0x3f7fff1bu, 0x3f7ffe08u, - 0xbf7ffe6cu, 0x3f7ffe04u, 0x3f7ffe3bu, 0x3f7ffeeau, 0xbf7ffe9bu, 0xbf7ffeb9u, - 0x3f7fff65u, 0x3f7ffe10u, 0x3bdb9027u, 0x3f7ffec8u, 0x3f7ffeb5u, 0x3f7ffe49u, - 0xbf7ffee5u, 0x3f7ffef4u, 0xbf7ffe1bu, 0x3f7ffe58u, 0x3f7ffeccu, 0xbf7ffe77u, - 0x3f7fff02u, 0x3f7ffea6u, 0xbf7ffe74u, 0xbf7ffefdu, 0xbf7ffe6cu, 0xbf7ffe12u, - 0x3f7ffe3bu, 0xbf7ffe91u, 0x3f7ffee4u, 0x3f7ffea1u, 0x3f7ffe3fu, 0xbf7ffed7u, - 0xbf7ffeb2u, 0x3f7ffe10u, 0x3f7fffa7u, 0xbf7fff48u, 0x3f7ffe5cu, 0xbf7fff13u, - 0x3f7ffe87u, 0xbf7ffee2u, 0xbf7ffebdu, 0x3f7ffe2au, 0x3f7ffe89u, 0x3ba4b555u, - 0xbf7ffe40u, 0xbf7fff38u, 0xbf7ffe8du, 0xbf7ffeb7u, 0xbf7ffe03u, 0xbf7ffe0bu, - 0xbf7fff03u, 0x3f7ffe4du, 0xbf7ffe48u, 0xbf7ffe38u, 0x3f7ffec4u, 0xbf7ffe1bu, - 0xbbf00f93u, 0xbf7ffe84u, 0xbf7ffecfu, 0x3f7ffeecu, 0x3bc3ffddu, 0x3f7fff20u, - 0x3f7ffe68u, 0xbf7ffe90u, 0x3bc0c79du, 0xbf7fff99u, 0xbbda04eau, 0x3f7fff09u, - 0xbf7ffe30u, 0x3be41a7cu, 0xbf7ffeacu, 0x3bf4b54cu, 0x3f7fff26u, 0xbf7ffef6u, - 0xbf7ffeadu, 0xbf7fff81u, 0xbf7ffe2eu, 0xbf7ffe71u, 0xbf7ffe9eu, 0x3f7fff04u, - 0x3f7ffe15u, 0xbf7ffef1u, 0xbf7fffa2u, 0xbf7ffe4eu, 0x3f7ffe14u, 0xbf7ffe41u, - 0x3bf5694cu, 0xbf7fff0cu, 0x3f7ffe9au, 0xbf7ffe42u, 0x3f7ffe92u, 0x3f7ffe05u, - 0xbf7fff95u, 0x3bcc3a51u, 0x3f7ffedfu, 0x3f7ffe21u, 0x3f7ffee9u, 0xbf7ffe45u, - 0xbf7ffe34u, 0xbf7ffe13u, 0xbf7ffed9u, 0x3f7fff3bu, 0xbf7fff05u, 0x3f7fff69u, - 0xbf7ffea8u, 0xbf7ffea2u, 0xbf7ffe0fu, 0x3f7ffe2cu, 0x3f7ffe81u, 0x3f7ffe27u, - 0xbf7ffeebu, 0x3f7ffe4bu, 0xbf7ffef0u, 0xbf7ffeb6u, 0x3f7fff6eu, 0x3f7ffea5u, - 0xbf7ffe43u, 0x3bd04094u, 0x3f7ffe7cu, 0x3f7ffe55u, 0xbf7ffe8eu, 0x3f7ffe49u, - 0x3f7fff1cu, 0xbbc72941u, 0xbf7ffe69u, 0x3bca72cbu, 0xbf7ffe3bu, 0xbf7ffe5cu, - 0x3f7ffe0eu, 0x3f7ffe1cu, 0x3f7ffe03u, 0xbf7ffe5bu, 0x3f7fff24u, 0xbbf38016u, - 0x3f7fffafu, 0x3f7ffe31u, 0x3f7ffe63u, 0xbf7ffe3cu, 0xbf7ffe1du, 0x3f7ffed8u, - 0xbf7ffe80u, 0xbf7ffe98u, 0xbf7ffe3du, 0xbf7fff2du, 0x3bd83c32u, 0x3f7ffe3au, - 0xbf7ffe56u, 0xbf7ffe08u, 0xbf7ffed7u, 0xbf7fff4au, 0xbf7ffe03u, 0x3f7fff3du, - 0xbf7ffe31u, 0xbf7fff46u, 0xbbbf1bc3u, 0xbf7fff42u, 0xbf7fff2eu, 0x3f7ffe23u, - 0x3f7ffefau, 0xbf7ffe5au, 0xbf7ffe1eu, 0x3f7fff18u, 0x3f7ffec2u, 0x3f7ffe1au, - 0xbf7ffe46u, 0x3f7ffe3eu, 0xbf7ffe02u, 0x3f7ffecau, 0x3f7ffe08u, 0x3f7fff3eu, - 0x3bb39f5du, 0xbf7ffe23u, 0xbf7ffeedu, 0x3f7fff50u, 0x3f7ffe71u, 0xbbea5032u, - 0x3f7ffebcu, 0xbf7ffe65u, 0x3f7ffe88u, 0xbf7fff0cu, 0x3f7ffe15u, 0x3f7fff20u, - 0x3f7fff75u, 0x3f7ffe38u, 0x3f7ffe70u, 0x3f7ffe75u, 0x3f7ffe97u, 0x3f7ffe20u, - 0xbf7ffe29u, 0xbf7ffee5u, 0xbf7ffe28u, 0x3f7ffe0du, 0x3f7ffe7bu, 0xbf7ffe8au, - 0xbf7ffe93u, 0xbf7fff13u, 0x3f7ffe04u, 0x3f7ffe40u, 0xbf7ffe68u, 0xbf7fff39u, - 0x3f7ffe6au, 0xbf7ffec0u, 0xbf7ffeb4u, 0x3f7fff57u, 0x3f7ffe2du, 0xbf7ffeccu, - 0xbbf80187u, 0xbf7fff01u, 0x3f7ffe62u, 0x3f7fff5eu, 0xbf7ffe45u, 0xbbf6e5c1u, - 0xbf7ffe84u, 0x3f7ffec4u, 0x3f7ffeb2u, 0xbf7ffe4au, 0x3f7ffe9eu, 0xbf7fff3cu, - 0x3b8d05cdu, 0x3f7ffe0fu, 0xbf7ffedeu, 0xbf7fff8cu, 0xbf7ffe41u, 0x3f7ffe52u, - 0x3f7ffe21u, 0x3f7ffe36u, 0x3bf57880u, 0x3f7ffec7u, 0xbf7fff61u, 0x3f7ffeabu, - 0xbf7ffe48u, 0x3f7ffe7au, 0x3f7ffe42u, 0xbbeb190au, 0xbf7ffeceu, 0xbf7ffe35u, - 0x3f7ffef6u, 0xbf7ffe4du, 0x3f7fff6bu, 0x3f7ffe92u, 0xbf7fff0bu, 0xbf7ffea5u, - 0xbf7ffe50u, 0x3f7ffe2au, 0x3f7ffee2u, 0xbf7ffe11u, 0xbbed437au, 0x3f7fffc9u, - 0xbf7ffe96u, 0x3f7fff0du, 0xbbd88640u, 0x3bf57f85u, 0xbf7ffea1u, 0x3f7ffe76u, - 0xbf7ffe37u, 0x3f7ffea9u, 0xbf7ffea7u, 0xbf7ffe5fu, 0xbf7fff41u, 0x3f7ffe75u, - 0xbf7fff10u, 0xbf7ffe0cu, 0xbf7ffe02u, 0x3f7ffe3eu, 0x3f7ffeafu, 0xbf7fff4eu, - 0x3f7ffe3bu, 0xbf7ffe1cu, 0xbf7ffe3au, 0xbbfa6bb1u, 0x3f7ffec1u, 0xbbfa3e62u, - 0x3f7ffeb9u, 0xbf7ffe08u, 0xbbfa1113u, 0x3f7fffa6u, 0x3f7ffeeeu, 0x3bef8f4du, - 0x3f7ffe7fu, 0x3f7ffef8u, 0xbbe7c8c3u, 0xbf7ffe82u, 0x3f7ffe5bu, 0x3f7ffe8du, - 0x3f7ffe1fu, 0xbf7ffeecu, 0xbf7ffeb6u, 0xbf7ffe2du, 0xbf7ffe0du, 0xbf7ffec3u, - 0xbf7ffe20u, 0x3f7ffef3u, 0x3f7ffed5u, 0xbf7ffe4bu, 0xbf7ffe56u, 0x3f7ffe74u, - 0x3f7ffe4eu, 0xbf7ffee7u, 0x3f7ffe6au, 0x3f7ffeb1u, 0x3f7ffe6fu, 0x3f7ffe21u, - 0x3f7ffeb5u, 0x3f7ffe63u, 0x3f7ffe79u, 0x3f7ffe27u, 0xbf7ffe60u, 0x3f7fffa9u, - 0xbf7ffe22u, 0xbbd21bfdu, 0xbf7fff00u, 0xbf7ffecfu, 0xbf7ffe61u, 0xbf7fff66u, - 0x3f7fff42u, 0xbf7ffe98u, 0xbf7ffe15u, 0xbbedab17u, 0x3f7ffe1bu, 0xbf7ffef2u, - 0xbf7ffe49u, 0xbf7ffe42u, 0xbf7ffec9u, 0x3f7fff1au, 0x3f7ffe5eu, 0x3f7ffe0fu, - 0x3f7ffe8fu, 0xbf7ffe57u, 0xbbfbcae0u, 0x3f7fff2au, 0x3f7ffe0du, 0x3f7ffed0u, - 0x3be251fdu, 0xbf7ffe78u, 0x3f7ffe2eu, 0xbf7ffe06u, 0x3f7ffe2bu, 0x3f7ffe99u, - 0xbf7fff17u, 0xbf7ffe1au, 0xbf7ffe01u, 0xbf7ffe16u, 0xbbfb5f07u, 0xbf7ffe67u, - 0xbf7ffe4du, 0xbf7ffe68u, 0x3f7ffe04u, 0xbf7ffe31u, 0x3f7ffe5cu, 0xbf7ffe85u, - 0xbf7ffeaau, 0xbf7fff26u, 0xbf7ffeffu, 0x3f7ffe17u, 0x3f7ffe0au, 0xbf7ffe19u, - 0x3f7ffed3u, 0xbf7ffe93u, 0x3f7ffe25u, 0xbf7fff7eu, 0x3f7ffe30u, 0xbf7ffe5au, - 0x3f7ffe5du, 0xbf7ffe50u, 0xbbd56139u, 0x3f7fff20u, 0x3f7ffe3eu, 0x3f7ffe53u, - 0x3f7ffebfu, 0x3f7ffe9du, 0x3f7fff46u, 0xbf7ffe2cu, 0x3f7ffe36u, 0x3f7fff9eu, - 0x3f7ffe6cu, 0xbf7fff36u, 0xbf7ffe38u, 0xbf7ffea8u, 0xbf7ffef9u, 0x3f7ffe1bu, - 0x3f7fff23u, 0xbf7ffecfu, 0xbf7ffe82u, 0xbf7ffe45u, 0x3f7ffe59u, 0xbbd19a6cu, - 0x3f7ffe15u, 0xbf7fff1au, 0xbf7ffe0fu, 0x3f7ffe57u, 0xbf7ffec8u, 0xbf7ffe3fu, - 0xbf7ffe55u, 0xbf7ffe39u, 0xbf7ffe07u, 0x3f7ffe77u, 0xbbf44b89u, 0x3f7ffe81u, - 0x3ba7e0d0u, 0xbbedacd7u, 0xbf7ffe17u, 0xbf7ffe7eu, 0x3f7ffe87u, 0x3f7ffe42u, - 0xbbd9f987u, 0xbf7ffe4bu, 0xbf7ffe60u, 0xbf7fff84u, 0xbf7ffef2u, 0x3f7ffe0du, - 0xbf7ffe67u, 0x3f7ffe04u, 0xbf7ffef1u, 0xbbe9dc88u, 0x3f7ffe7du, 0xbf7fff5bu, - 0xbbac6e95u, 0xbf7ffeadu, 0xbf7ffe2fu, 0xbf7ffe52u, 0x3f7ffe1du, 0x3f7ffe69u, - 0xbf7fff5du, 0xbf7fff0cu, 0xbf7ffee4u, 0x3bb67716u, 0xbf7ffe9fu, 0xbbf7bac9u, - 0x3f7ffed6u, 0x3f7ffe8bu, 0x3f7ffe31u, 0x3f7fff33u, 0xbf7ffe88u, 0x3f7ffe0eu, - 0x3f7ffe74u, 0x3f7ffe76u, 0xbf7ffe62u, 0xbf7fff3eu, 0x3bfc4d34u, 0x3be10bddu, - 0xbf7ffef5u, 0x3f7ffe4cu, 0xbf7ffe73u, 0xbf7ffe97u, 0x3f7ffe18u, 0xbf7ffe8du, - 0xbf7ffe90u, 0x3bddaa29u, 0xbf7ffe72u, 0x3f7fff0eu, 0x3f7ffe80u, 0x3f7ffe34u, - 0xbbdde376u, 0xbf7ffe7bu, 0x3bcaa789u, 0x3f7ffe02u, 0x3bfe9254u, 0x3f7ffe14u, - 0x3bc6eb5eu, 0xbf7ffe85u, 0x3f7ffe2bu, 0xbf7ffe9bu, 0x3f7ffe27u, 0xbf7ffe58u, - 0xbbfa2daeu, 0x3f7ffeceu, 0xbf7ffe07u, 0xbf7ffe48u, 0xbf7ffe46u, 0x3f7ffe5fu, - 0x3f7fff2fu, 0x3bd2d1e0u, 0xbf7ffe5au, 0x3f7fff2cu, 0xbf7ffeffu, 0xbf7ffe16u, - 0xbbb3cf6au, 0xbf7ffec9u, 0x3b9a7248u, 0xbf7fffd5u, 0xbf7ffecbu, 0xbf7ffe2eu, - 0x3f7fff68u, 0x3f7ffe6eu, 0xbf7ffe23u, 0x3bf38d31u, 0x3bf5de58u, 0x3f7fffadu, - 0x3f7ffe4eu, 0x3f7ffed2u, 0x3f7ffe86u, 0xbf7fff29u, 0xbf7ffe1cu, 0x3f7fff0au, - 0x3f7ffec6u, 0xbf7ffe3du, 0xbf7ffe17u, 0xbf7ffed9u, 0xbf7ffebfu, 0xbf7ffe01u, - 0xbf7fff36u, 0x3f7ffe0cu, 0x3f7ffe79u, 0x3f7fff06u, 0x3f7fff11u, 0x3f7ffe6fu, - 0x3f7ffe5bu, 0xbf7ffe3cu, 0x3f7ffee5u, 0x3f7ffe7au, 0xbf7ffe33u, 0xbbeaaa16u, - 0x3bd9ecebu, 0xbf7ffe84u, 0xbb936a78u, 0x3f7ffe1bu, 0x3f7ffe6cu, 0x3f7ffe5du, - 0x3b8c8feeu, 0x3f7fff95u, 0x3f7ffee4u, 0xbf7ffe4au, 0x3f7ffeecu, 0xbf7ffe7cu, - 0x3f7ffe35u, 0xbf7ffef8u, 0xbbab964du, 0xbf7ffe85u, 0x3f7ffe14u, 0x3f7ffe69u, - 0xbf7ffe55u, 0x3f7ffe13u, 0x3f7ffea0u, 0x3bfcdb89u, 0x3f7ffe07u, 0x3f7ffe12u, - 0xbf7fff54u, 0xbf7ffe6bu, 0x3f7ffe11u, 0xbf7fff1cu, 0x3be44bbcu, 0xbf7ffe50u, - 0xbf7ffe78u, 0xbf7fffa6u, 0xbf7ffe39u, 0xbf7ffe44u, 0xbf7ffe04u, 0x3f7fff15u, - 0xbbe2a6fau, 0x3bb8f837u, 0xbf7ffe48u, 0xbf7ffe73u, 0x3bfc8794u, 0x3f7ffee8u, - 0x3bf6d31du, 0x3f7ffeb4u, 0x3f7fffa9u, 0xbf7ffe71u, 0xbf7ffe4bu, 0x3f7ffe52u, - 0x3bd5d480u, 0xbf7ffe5cu, 0x3f7fff19u, 0xbf7ffeddu, 0x3bced20au, 0x3bf7dff1u, - 0x3bdcba03u, 0xbf7ffe65u, 0x3f7ffe22u, 0x3f7fff3au, 0x3f7ffe0bu, 0x3f7ffebdu, - 0x3f7ffe49u, 0xbbda7672u, 0x3f7ffe21u, 0x3be85f4du, 0x3f7fff0du, 0xbf7ffec2u, - 0x3f7ffe0eu, 0x3f7ffe76u, 0x3f7ffee4u, 0x3f7ffe79u, 0xbf7ffebfu, 0xbf7ffe4cu, - 0x3f7ffe8eu, 0xbf7ffee0u, 0xbf7fff7au, 0xbf7fff00u, 0x3f7ffe68u, 0xbf7ffe34u, - 0x3f7ffec7u, 0x3f7ffe3du, 0x3f7ffe17u, 0xbf7ffe5bu, 0x3f7ffe3bu, 0xbf7ffe63u, - 0xbbf58ef9u, 0xbf7ffe3fu, 0x3f7ffe86u, 0xbf7ffe16u, 0xbf7ffe8du, 0x3f7ffe95u, - 0x3f7ffe10u, 0xbf7ffe05u, 0x3f7fff7eu, 0x3f7fff05u, 0x3f7ffe51u, 0xbf7ffe0au, - 0xbf7ffe72u, 0xbf7ffe83u, 0xbf7ffe38u, 0x3f7ffedcu, 0xbf7ffe12u, 0x3f7fff49u, - 0x3f7ffe4au, 0xbf7ffe7au, 0x3f7fff20u, 0x3f7fff47u, 0xbf7fff71u, 0xbbfe7d40u, - 0xbbdd7083u, 0x3f7fff26u, 0xbf7fff3bu, 0x3f7ffef2u, 0xbf7ffed5u, 0x3f7ffeafu, - 0xbf7fff65u, 0xbf7ffe37u, 0x3f7ffea2u, 0x3f7fff6fu, 0x3f7ffe99u, 0x3bddfdffu, - 0x3f7ffe08u, 0x3f7ffedeu, 0xbf7ffea5u, 0x3f7ffe89u, 0xbf7fff04u, 0xbf7ffee3u, - 0x3f7ffe8fu, 0xbf7ffe48u, 0x3b58fb95u, 0xbf7ffe78u, 0x3bf4e0d9u, 0xbf7ffe6bu, - 0x3bb65e28u, 0x3f7ffe58u, 0x3f7fff32u, 0x3f7ffe01u, 0xbf7ffe9au, 0xbbf3bec0u, - 0xbf7fff0fu, 0x3f7fff1fu, 0x3f7ffe4bu, 0x3bee07a3u, 0xbbcc0743u, 0xbf7ffe25u, - 0xbf7ffe94u, 0x3f7ffe57u, 0xbf7ffe1du, 0xbf7ffefeu, 0xbf7ffea1u, 0x3f7ffea6u, - 0x3be159d2u, 0xbf7ffe13u, 0xbf7ffe81u, 0xbf7fff1eu, 0xbf7ffe2au, 0xbf7fff6au, - 0xbf7ffe27u, 0x3f7ffe36u, 0x3f7ffe22u, 0xbbfa8967u, 0x3f7ffe96u, 0x3f7ffe88u, - 0xbf7fff1du, 0x3f7ffef7u, 0xbf7fff2du, 0xbf7ffe0du, 0xbf7fff4eu, 0xbf7ffe0bu, - 0xbf7ffe33u, 0x3f7ffe2fu, 0x3f7ffea3u, 0xbf7fff45u, 0xbf7ffe71u, 0x3bbf1e61u, - 0xbf7ffe16u, 0x3f7ffe0eu, 0x3f7ffe1au, 0xbf7ffe50u, 0x3f7ffee2u, 0x3f7ffeceu, - 0x3f7ffe73u, 0xbf7ffe86u, 0xbf7ffec3u, 0x3f7ffe72u, 0xbf7ffed4u, 0xbf7ffea0u, - 0xbf7ffe1fu, 0xbf7ffe75u, 0xbf7ffeabu, 0x3f7ffe80u, 0x3f7ffe11u, 0xbbddb2d6u, - 0xbf7ffe0fu, 0xbf7fff2au, 0xbf7ffe09u, 0x3f7ffe9du, 0x3bac459du, 0xbf7ffe90u, - 0x3f7ffe2bu, 0x3f7ffefau, 0x3f7ffe5eu, 0x3f7ffecfu, 0xbbf15981u, 0x3f7ffe66u, - 0x3f7ffe21u, 0xbf7ffe64u, 0xbf7ffe26u, 0xbb8e7c00u, 0xbf7ffe37u, 0x3f7ffea2u, - 0x3f7ffeecu, 0xbbe6fd68u, 0xbf7ffec4u, 0x3f7ffee1u, 0xbf7ffe9au, 0xbf7ffe2du, - 0xbf7ffe7du, 0x3f7fffdbu, 0xbf7ffedbu, 0x3f7ffe3cu, 0x3f7ffeedu, 0x3f7ffe1au, - 0x3bea2ddbu, 0x3f7ffe0au, 0xbf7fff5du, 0x3f7fff14u, 0x3f7ffe4eu, 0x3f7ffebbu, - 0xbf7ffe02u, 0x3f7ffe57u, 0xbbe23eccu, 0x3f7fff41u, 0xbbec9e18u, 0x3f7ffe61u, - 0x3bf1427cu, 0xbf7ffea9u, 0xbf7ffe10u, 0x3bec3f9eu, 0x3f7ffe20u, 0x3f7ffe6du, - 0xbf7fff64u, 0xbf7ffe36u, 0xbf7ffeeeu, 0xbf7fff12u, 0x3f7ffef0u, 0xbf7ffe84u, - 0x3f7ffe58u, 0x3f7ffe95u, 0x3f7ffe68u, 0x3f7ffe6cu, 0x3f7ffe54u, 0xbf7ffe93u, - 0xbf7ffe24u, 0xbf7ffe1eu, 0x3f7ffee7u, 0x3f7ffe45u, 0xbf7ffe44u, 0xbf7ffe43u, - 0x3f7ffea4u, 0x3f7ffe15u, 0x3f7ffe46u, 0xbf7ffe1bu, 0x3f7ffe4cu, 0xbf7ffe5bu, - 0xbf7ffeacu, 0x3bd400dfu, 0xbf7ffe22u, 0xbf7ffebeu, 0xbf7ffe3bu, 0x3f7ffe4fu, - 0xbf7ffecbu, 0xbf7ffe1cu, 0x3f7ffe4bu, 0x3f7ffe32u, 0xbf7fff07u, 0x3f7ffea7u, - 0x3f7ffe8du, 0xbf7ffe74u, 0x3f7ffea1u, 0xbbf1cec9u, 0xbbf48cd7u, 0xbbc5e1c3u, - 0x3f7ffe06u, 0x3f7ffe19u, 0x3bcb2f4bu, 0xbf7ffe01u, 0xbf7ffed7u, 0x3f7ffe23u, - 0x3f7fff01u, 0xbf7ffe53u, 0x3be4d584u, 0x3f7ffe8au, 0x3f7ffeb2u, 0xbf7ffe2cu, - 0x3f7ffec6u, 0x3f7ffe2eu, 0x3f7ffe17u, 0xbf7ffe37u, 0xbf7ffef2u, 0xbf7ffe33u, - 0x3f7ffe5du, 0xbf7fff36u, 0x3f7ffe83u, 0x3f7ffeb8u, 0x3f7ffe59u, 0xbf7ffe72u, - 0xbf7fff15u, 0xbf7ffe78u, 0xbf7ffe7du, 0xbf7ffe4du, 0x3f7ffe56u, 0x3f7ffe35u, - 0xbf7ffe08u, 0xbf7ffec9u, 0xbf7fff3du, 0x3f7ffe87u, 0xbbe89d7cu, 0xbf7ffec0u, - 0x3bcca667u, 0xbf7ffe85u, 0xbf7ffe9eu, 0x3f7ffee3u, 0xbf7ffe20u, 0xbf7ffe12u, - 0xbf7ffe68u, 0x3f7ffe47u, 0xbf7fff62u, 0x3f7ffe5bu, 0x3f7ffe96u, 0x3f7fffc8u, - 0xbf7ffe89u, 0x3be7b30bu, 0xbf7ffe2au, 0x3f7ffeadu, 0x3f7fffb7u, 0xbf7ffe04u, - 0x3f7ffebau, 0x3f7fff31u, 0x3f7ffeaeu, 0xbf7ffe34u, 0xbf7ffe51u, 0xbf7ffe8eu, - 0x3f7ffeedu, 0x3f7ffe77u, 0xbf7ffe49u, 0xbf7fff3bu, 0xbbdadc39u, 0xbbe565d7u, - 0x3f7ffea0u, 0x3f7ffe0fu, 0xbf7ffec7u, 0x3f7fff2eu, 0xbb9d99d6u, 0xbf7ffe13u, - 0x3f7ffe8cu, 0xbf7ffe3au, 0x3f7ffe1du, 0xbbe1ce37u, 0x3f7fff2eu, 0x3f7ffe0fu, - 0xbf7ffe98u, 0x3f7ffe77u, 0xbf7ffe8eu, 0xbf7ffe51u, 0x3f7ffeaeu, 0xbf7ffe04u, - 0x3f7ffe80u, 0x3f7ffeadu, 0x3f7ffe96u, 0x3f7ffe35u, 0x3f7ffe9du, 0xbf7ffe7du, - 0x3f7ffee2u, 0xbf7ffe44u, 0x3f7ffea1u, 0x3f7fff0bu, 0xbf7fff02u, 0xbf7ffe35u, - 0x3f7fff3au, 0x3f7ffe4eu, 0x3f7ffe89u, 0x3f7ffe04u, 0x3f7fff6du, 0xbf7ffeb3u, - 0xbf7ffeedu, 0x3ba8c65cu, 0xbf7ffe0fu, 0xbf7ffe8cu, 0xbf7fff22u, 0x3f7ffedfu, - 0xbf7ffe6bu, 0x3f7ffe91u, 0x3f7ffea2u, 0xbf7ffe5bu, 0x3f7ffe08u, 0x3f7ffe26u, - 0xbf7ffe24u, 0x3f7ffe19u, 0x3f7ffe83u, 0x3f7ffe7eu, 0xbbecf39du, 0xbf7ffe3au, - 0xbf7ffefeu, 0x3f7ffec3u, 0x3f7ffe36u, 0x3f7ffe1bu, 0xbf7ffe23u, 0xbf7ffe43u, - 0x3f7ffe11u, 0x3f7ffea8u, 0xbf7ffe62u, 0xbf7ffe40u, 0x3f7fff60u, 0xbf7ffe2bu, - 0x3f7ffe6du, 0xbf7ffe06u, 0x3f7ffe74u, 0xbf7fff29u, 0x3f7fff18u, 0xbbc3e499u, - 0x3f7ffe38u, 0xbf7fff7fu, 0x3f7ffe50u, 0x3bf65c80u, 0x3f7ffec5u, 0x3f7ffed1u, - 0x3f7fff64u, 0x3f7ffe54u, 0xbf7fff03u, 0xbf7ffe52u, 0x3f7fff2cu, 0x3f7ffe39u, - 0xbf7ffe48u, 0x3f7ffe07u, 0xbf7ffe99u, 0x3f7ffe2fu, 0xbf7ffe93u, 0xbf7ffe2cu, - 0x3ba4b050u, 0xbf7ffe10u, 0x3f7ffe4au, 0x3bf11da4u, 0xbf7ffecfu, 0x3f7ffef4u, - 0x3f7ffe03u, 0xbf7ffee0u, 0xbf7ffe5fu, 0x3f7ffe57u, 0xbf7ffed4u, 0xbf7ffe87u, - 0xbf7ffe82u, 0x3f7ffe02u, 0x3f7fff15u, 0xbf7ffe16u, 0x3bf2e8dfu, 0x3bd65dfdu, - 0xbf7ffe2du, 0x3f7ffe22u, 0x3f7ffe01u, 0xbf7ffe8au, 0xbbfe1326u, 0xbf7ffe3cu, - 0xbf7ffe3eu, 0xbbf25073u, 0x3f7fff00u, 0x3be4ad19u, 0xbf7ffe60u, 0xbf7ffe9bu, - 0x3f7ffef0u, 0xbf7ffe21u, 0x3f7ffe12u, 0xbf7fffdcu, 0xbf7ffeabu, 0xbf7ffe05u, - 0x3f7fff53u, 0xbf7ffe24u, 0x3f7ffe08u, 0x3f7ffea6u, 0xbf7ffe78u, 0x3f7ffe8cu, - 0x3f7ffe5bu, 0xbf7ffe33u, 0x3f7fff3du, 0x3f7ffe3au, 0xbf7ffea8u, 0x3f7fff1du, - 0xbf7fff08u, 0x3bcd847au, 0x3f7fff32u, 0x3f7ffe0bu, 0x3f7ffe7cu, 0xbf7ffeb0u, - 0x3bcfb3b5u, 0xbf7ffeeeu, 0xbf7ffe1au, 0x3bfc2109u, 0xbf7fff56u, 0xbf7fff6au, - 0xbf7fff8bu, 0x3f7ffee4u, 0x3f7ffe17u, 0x3f7ffe5eu, 0xbbe92ebeu, 0x3bed8182u, - 0xbf7fff3eu, 0xbbed0251u, 0x3f7ffeb8u, 0xbf7ffe79u, 0xbf7ffe14u, 0xbf7ffe53u, - 0xbbfaa408u, 0x3f7ffe0eu, 0xbf7ffe3fu, 0x3f7ffec0u, 0xbf7ffe21u, 0xbf7fff6eu, - 0x3f7ffe2fu, 0x3f7ffe80u, 0x3f7ffe77u, 0xbf7ffe30u, 0xbbf9940eu, 0x3f7fff4fu, - 0x3f7ffe31u, 0x3f7ffe70u, 0xbf7ffe01u, 0x3f7ffeecu, 0x3f7ffee9u, 0xbf7ffed7u, - 0x3f7ffe2eu, 0xbf7ffe32u, 0x3f7ffec4u, 0xbf7ffe6du, 0xbbfa786au, 0xbf7ffe83u, - 0x3f7ffe24u, 0xbf7ffe4du, 0x3bdb83d9u, 0xbbc2b509u, 0xbb49201bu, 0xbf7ffe0bu, - 0xbf7ffe68u, 0x3f7ffe45u, 0x3f7ffef4u, 0xbf7ffe99u, 0xbf7ffe34u, 0x3f7fff03u, - 0x3f7ffe52u, 0xbf7ffe0au, 0x3f7ffe8eu, 0xbbe825cfu, 0xbbf7c66au, 0xbf7ffe43u, - 0x3f7ffe94u, 0x3f7ffeb3u, 0xbf7fff4bu, 0xbf7fff37u, 0x3f7ffe28u, 0x3f7ffe66u, - 0x3f7fff73u, 0x3bfacfa6u, 0x3f7ffe97u, 0xbf7ffef9u, 0x3f7fff82u, 0x3f7ffe95u, - 0x3f7ffe43u, 0xbf7fff15u, 0x3f7ffed4u, 0x3f7ffe10u, 0xbf7ffe11u, 0xbf7fff29u, - 0xbf7ffecfu, 0xbf7fffbfu, 0xbf7ffe2du, 0xbf7ffe6au, 0xbf7ffe8bu, 0x3f7ffe35u, - 0x3f7ffe40u, 0xbf7ffea5u, 0x3f7ffe47u, 0x3ba72e47u, 0x3f7ffe7bu, 0xbf7ffe40u, - 0x3f7fff92u, 0x3f7ffed0u, 0x3f7ffe48u, 0x3be65d36u, 0x3f7ffeafu, 0x3f7ffe91u, - 0x3f7ffeabu, 0xbbf7390bu, 0x3f7ffe1bu, 0xbf7ffe48u, 0xbf7ffe8au, 0xbf7fff1cu, - 0xbbe7eac0u, 0x3f7ffe23u, 0xbf7fff13u, 0xbf7fff0fu, 0x3f7ffeebu, 0xbf7ffe65u, - 0xbf7ffee6u, 0xbbf7735du, 0xbf7ffe0fu, 0xbf7ffe87u, 0xbf7ffeefu, 0x3f7ffe72u, - 0xbf7ffe18u, 0xbf7ffe73u, 0xbbf21174u, 0x3f7ffe8cu, 0xbf7fff06u, 0xbf7ffe2au, - 0x3bf254e1u, 0xbbf28696u, 0x3bdf9950u, 0xbf7fff2du, 0x3f7ffef8u, 0x3f7ffe67u, - 0x3f7ffe46u, 0xbf7ffe82u, 0xbf7ffe49u, 0x3be340a8u, 0xbf7ffe13u, 0xbf7ffee8u, - 0x3f7ffedfu, 0xbb9261d1u, 0x3f7ffebeu, 0xbf7ffe1eu, 0xbf7ffe06u, 0xbf7ffe1du, - 0xbf7ffe39u, 0xbbe08706u, 0x3f7ffe3bu, 0xbf7ffeb2u, 0xbf7ffe9du, 0xbbeca964u, - 0x3f7ffe0au, 0x3f7ffeccu, 0x3f7ffeacu, 0xbf7ffeb4u, 0x3f7ffef7u, 0xbf7fff1bu, - 0x3f7ffe4au, 0x3beeb199u, 0x3f7ffe24u, 0xbbcd9765u, 0xbbe8eff6u, 0xbf7fff59u, - 0x3f7fff04u, 0x3ba21ea9u, 0xbf7ffee2u, 0x3f7ffee5u, 0xbbfede76u, 0xbf7ffe19u, - 0xbf7ffe8du, 0x3f7fff56u, 0x3bd00e77u, 0xbf7ffe07u, 0x3f7ffea6u, 0x3f7ffe74u, - 0x3f7fff41u, 0x3b933220u, 0xbf7ffe0eu, 0xbf7ffee4u, 0xbf7ffe3cu, 0x3f7ffe90u, - 0x3f7ffe69u, 0x3f7ffe38u, 0x3f7ffea4u, 0xbf7ffe44u, 0xbf7ffebcu, 0xbf7fff1au, - 0xbf7ffef2u, 0x3f7ffee7u, 0x3f7ffe28u, 0xbf7ffe83u, 0x3f7ffeecu, 0x3f7ffe2fu, - 0xbf7ffe79u, 0x3f7ffe43u, 0xbbdb0f54u, 0x3f7ffe8bu, 0x3f7ffe4eu, 0xbf7ffe75u, - 0x3f7fff4du, 0x3bfaf3d2u, 0xbf7fff77u, 0xbf7ffe37u, 0x3bb848abu, 0x3f7ffe50u, - 0xbf7ffe51u, 0xbf7ffe0cu, 0xbf7ffe53u, 0x3f7ffe41u, 0xbf7ffe85u, 0xbbf078d8u, - 0x3f7ffe84u, 0x3f7ffe26u, 0xbf7ffe16u, 0xbf7ffeabu, 0x3f7ffe2du, 0xbf7ffef1u, - 0xbbdfa7d4u, 0x3f7fff68u, 0xbb92e285u, 0x3f7ffe36u, 0xbf7fff0bu, 0xbf7ffe55u, - 0x3f7ffe08u, 0x3bf9852au, 0x3bf4c50du, 0xbf7fffdbu, 0xbf7ffe37u, 0x3bfbb92cu, - 0x3f7ffe3du, 0xbf7ffe9au, 0x3f7ffe1fu, 0xbf7fff97u, 0xbf7fff6du, 0x3f7ffea9u, - 0x3f7ffe03u, 0xbf7ffe14u, 0x3f7ffefeu, 0xbf7ffe8cu, 0x3f7fffb7u, 0xbf7ffeebu, - 0xbf7ffed0u, 0x3f7ffed7u, 0x3f7fff4fu, 0x3f7ffe66u, 0x3f7ffe46u, 0xbbe745e2u, - 0x3bc0ba1bu, 0xbf7fff55u, 0x3f7ffe30u, 0xbf7ffe0au, 0xbf7ffe45u, 0x3f7ffe88u, - 0xbbe0719cu, 0x3f7ffe44u, 0xbf7ffeeeu, 0xbf7fff87u, 0x3f7ffe70u, 0xbf7ffe60u, - 0x3f7ffeb5u, 0x3f7ffec7u, 0xbf7ffe89u, 0x3f7ffe5bu, 0x3f7ffe1bu, 0xbf7ffe7du, - 0x3b9c6b64u, 0xbf7ffef4u, 0xbf7fff56u, 0xbbdf86fdu, 0xbf7fff04u, 0x3bfabbb5u, - 0x3bd09fe8u, 0xbf7ffe23u, 0x3f7fff1cu, 0xbb87e470u, 0x3f7ffee5u, 0xbf7ffebbu, - 0xbf7ffeb1u, 0xbf7fff35u, 0x3bf3d461u, 0x3f7ffe08u, 0xbbeb56e6u, 0xbf7fff15u, - 0xbf7ffefau, 0xbf7ffe57u, 0x3f7ffe78u, 0x3bac09f5u, 0x3f7ffe64u, 0x3f7ffe49u, - 0x3f7ffef9u, 0x3bde62c7u, 0xbf7ffe41u, 0x3f7ffe7eu, 0x3f7fff0cu, 0xbf7ffe26u, - 0x3f7ffe11u, 0x3be80f27u, 0x3beb8ca9u, 0xbf7ffea4u, 0x3f7ffe39u, 0x3bf6a218u, - 0xbf7ffe30u, 0xbf7fff8cu, 0x3f7ffe38u, 0xbf7ffe83u, 0xbf7ffe79u, 0x3f7ffec9u, - 0xbf7fff14u, 0x3f7ffeedu, 0xbf7ffe8au, 0xbf7ffe6au, 0xbf7ffe33u, 0x3f7ffea7u, - 0x3f7ffed2u, 0x3bd08987u, 0x3f7ffec2u, 0x3f7fff8eu, 0x3f7ffedbu, 0xbf7ffe11u, - 0x3f7ffeb3u, 0xbf7ffe40u, 0x3f7ffee0u, 0xbf7ffe7cu, 0x3f7fffc8u, 0x3b9e5d6fu, - 0x3f7ffe09u, 0xbbebfcedu, 0x3f7ffe0cu, 0xbf7fff53u, 0x3f7ffe6au, 0xbba3857du, - 0xbf7fff07u, 0x3f7fff61u, 0x3f7ffe07u, 0xbbf3d202u, 0xbf7ffec1u, 0xbf7ffebau, - 0xbf7ffe10u, 0x3f7ffeadu, 0x3bd05cc5u, 0x3f7ffe68u, 0xbbab1860u, 0x3f7ffef3u, - 0xbf7ffe63u, 0x3f7ffe2bu, 0x3f7ffe1eu, 0x3f7ffe42u, 0xbf7ffec5u, 0xbf7ffe54u, - 0xbf7ffe3fu, 0x3f7ffe2eu, 0xbf7ffe4cu, 0xbf7ffe3eu, 0xbf7ffe20u, 0xbf7ffe6cu, - 0x3f7ffe37u, 0x3f7ffeb8u, 0x3f7ffeabu, 0xbf7ffe28u, 0xbf7ffe29u, 0x3bf3cfa3u, - 0xbf7ffe3cu, 0xbf7fff2fu, 0xbf7ffe92u, 0x3f7ffeccu, 0xbf7ffe56u, 0x3f7ffe03u, - 0x3f7ffe02u, 0x3f7ffeefu, 0x3f7ffe43u, 0xbf7ffe19u, 0x3f7ffe16u, 0xbf7ffe32u, - 0x3f7ffe8eu, 0x3f7ffe35u, 0x3bcbf4eau, 0xbf7ffea5u, 0xbf7ffedfu, 0xbf7fff3au, - 0xbf7ffeebu, 0x3f7ffe52u, 0xbf7ffee5u, 0xbf7ffe08u, 0xbbe38cc1u, 0x3f7ffe95u, - 0x3f7ffe44u, 0xbf7fff6bu, 0xbf7ffe0eu, 0x3f7fff0au, 0xbf7ffebfu, 0x3f7ffe80u, - 0x3f7ffe06u, 0xbf7ffe8fu, 0x3f7ffe62u, 0x3bf6bd27u, 0xbf7ffed3u, 0xbf7fff27u, - 0x3f7ffe58u, 0xbf7fff21u, 0xbbd5feacu, 0xbf7ffec1u, 0x3f7ffedbu, 0x3f7fff1fu, - 0xbbdeeb4cu, 0xbf7ffe46u, 0x3bb6c12fu, 0x3f7ffe49u, 0xbf7fff7fu, 0x3f7ffe9au, - 0xbbcef121u, 0x3f7ffe40u, 0xbf7ffe09u, 0x3f7ffebau, 0xbf7ffe7fu, 0xbf7ffe12u, - 0x3f7ffe6du, 0xbf7ffe4au, 0x3f7fff0bu, 0xbf7fff18u, 0xbf7ffed0u, 0x3f7ffeb5u, - 0x3f7ffe15u, 0xbbfe5120u, 0x3f7fffcbu, 0xbf7ffeccu, 0x3f7ffe29u, 0x3f7ffe3fu, - 0x3f7ffe54u, 0xbf7ffea7u, 0x3f7ffea4u, 0x3bebddafu, 0xbf7fff02u, 0xbf7ffee3u, - 0x3f7fff73u, 0xbba70e96u, 0xbf7ffe47u, 0x3f7ffe8bu, 0x3bf462c6u, 0x3f7ffe1cu, - 0x3f7ffe2fu, 0xbf7ffed6u, 0x3f7fff80u, 0x3f7ffe51u, 0x3f7ffe01u, 0x3f7fff33u, - 0xbf7fff28u, 0x3f7ffe4du, 0x3f7ffe48u, 0x3bee8b49u, 0xbf7ffe81u, 0x3f7ffe08u, - 0xbf7ffe0fu, 0xbf7ffefdu, 0xbf7ffe8au, 0x3f7ffe57u, 0x3f7fff22u, 0xbf7ffe4cu, - 0xbf7ffe19u, 0x3f7fff04u, 0x3f7ffea3u, 0x3f7ffefau, 0xbf7ffe79u, 0x3f7ffefeu, - 0xbf7ffe30u, 0x3f7fff2eu, 0x3f7ffe25u, 0x3f7ffeffu, 0xbf7fff43u, 0xbbf92707u, - 0x3f7ffe64u, 0xbf7ffe2cu, 0xbbdfe2d3u, 0x3f7ffea9u, 0x3f7fff42u, 0x3f7ffee1u, - 0xbf7ffe1du, 0x3f7ffea8u, 0x3f7fff29u, 0xbf7fff45u, 0x3f7ffe05u, 0xbf7ffeb1u, - 0x3f7ffe61u, 0xbbcd996cu, 0xbba2a8b3u, 0xbf7ffe3cu, 0x3f7fff16u, 0x3beb098eu, - 0xbf7ffe80u, 0x3f7ffe98u, 0x3f7ffe22u, 0x3be9641cu, 0x3f7ffebbu, 0x3f7ffe04u, - 0xbbef8f39u, 0x3be84b23u, 0xbf7fff34u, 0xbf7ffeebu, 0x3f7ffe6cu, 0xbf7ffe9cu, - 0x3f7fff84u, 0x3f7ffe2au, 0x3f7ffea4u, 0x3f7ffea1u, 0x3bfc6cb1u, 0x3f7fff9bu, - 0x3be90856u, 0xbf7ffe5eu, 0x3f7ffe17u, 0x3f7ffe86u, 0x3bebf1cdu, 0x3bf9995bu, - 0xbf7ffe39u, 0x3f7ffe74u, 0xbf7ffe24u, 0x3f7ffe33u, 0x3f7ffe2bu, 0x3f7ffe76u, - 0xbbebc991u, 0xbf7fff0au, 0x3f7ffe10u, 0x3f7ffeefu, 0x3bffb4c4u, 0x3f7ffe62u, - 0xbf7ffe3du, 0x3f7ffee6u, 0x3f7ffea6u, 0x3f7ffe0au, 0x3f7ffe8cu, 0x3f7ffe26u, - 0x3f7ffe26u, 0x3f7ffe0au, 0x3f7ffea6u, 0xbbfe582fu, 0x3f7ffee6u, 0xbf7ffe89u, - 0x3f7ffe76u, 0x3f7ffe33u, 0xbf7fffddu, 0xbf7ffe24u, 0xbf7ffe39u, 0x3bebb573u, - 0x3f7ffe86u, 0x3b46d0b3u, 0x3f7ffe17u, 0xbbb6ace9u, 0x3f7ffe2au, 0x3f7ffe6cu, - 0x3f7ffe04u, 0x3f7fff16u, 0xbf7fff05u, 0xbf7ffe2cu, 0x3f7ffe64u, 0xbbc5ad19u, - 0x3bfbe916u, 0x3f7ffedbu, 0xbf7ffec1u, 0x3bea5802u, 0xbf7ffe1bu, 0xbf7ffeb9u, - 0xbf7ffe17u, 0xbf7ffe86u, 0x3bff2e59u, 0xbf7ffe33u, 0x3f7ffe3du, 0xbf7ffea6u, - 0xbf7ffe0au, 0xbf7fff53u, 0xbf7fff5du, 0xbbcf4628u, 0x3bb84c63u, 0x3bd6802eu, - 0xbf7fff74u, 0xbbcc968cu, 0x3f7ffe39u, 0xbf7ffe2au, 0x3f7ffef8u, 0xbf7ffe6cu, - 0xbf7ffe04u, 0x3f7fff05u, 0x3f7ffe2cu, 0xbf7ffe64u, 0x3f7fff44u, 0x3f7ffec1u, - 0x3f7ffe9du, 0x3f7ffe0au, 0x3f7ffe33u, 0xbf7ffe97u, 0x3f7fff1eu, 0xbf7ffe1bu, - 0xbf7ffeb9u, 0x3f7fff1au, 0xbf7fff50u, 0x3f7ffe3du, 0xbf7fff22u, 0x3f7ffe8bu, - 0x3f7fff3eu, 0x3f7ffe39u, 0xbf7ffe59u, 0xbf7ffeedu, 0xbbe564c7u, 0xbf7ffe04u, - 0x3f7fff2cu, 0xbf7ffe0fu, 0xbf7ffe05u, 0xbf7ffeb2u, 0xbf7ffe24u, 0x3f7ffe03u, - 0x3f7ffe2fu, 0x3f7fff4bu, 0xbf7ffe7eu, 0x3be7988cu, 0x3f7fff38u, 0xbbfee936u, - 0x3f7ffeadu, 0xbf7ffe51u, 0xbf7ffe09u, 0x3bf0f468u, 0x3f7ffe8du, 0xbf7ffec5u, - 0xbf7ffe38u, 0xbf7ffe69u, 0x3f7ffe7fu, 0xbf7ffe23u, 0x3f7fff69u, 0x3f7fff30u, - 0x3f7fff0fu, 0x3f7fff11u, 0x3bb77e72u, 0xbbe2e5e0u, 0xbf7ffe0fu, 0x3f7ffe9au, - 0xbf7ffe06u, 0x3bb3c213u, 0x3bf908ffu, 0xbbd7060au, 0x3f7ffec8u, 0x3f7ffe0bu, - 0xbf7ffe1au, 0xbf7ffe35u, 0xbf7ffeaeu, 0x3f7fff06u, 0xbf7fff17u, 0x3f7ffe4bu, - 0x3f7ffe0eu, 0xbf7ffe4cu, 0x3f7ffe56u, 0x3bd75eaeu, 0xbf7ffe54u, 0x3f7ffe3au, - 0x3f7ffeb1u, 0xbf7fff19u, 0xbf7ffe93u, 0xbbec8655u, 0x3f7fff1bu, 0x3f7ffe22u, - 0x3f7ffe80u, 0x3f7ffeafu, 0x3f7ffe12u, 0xbf7ffef4u, 0xbbd6a30du, 0xbf7fff1cu, - 0xbf7ffe65u, 0xbf7fff76u, 0xbf7ffe6au, 0xbbce823du, 0xbf7ffe9bu, 0xbbb969ecu, - 0x3f7ffe19u, 0x3f7ffe44u, 0x3f7fffa6u, 0xbf7ffe2du, 0xbf7ffe1fu, 0xbbd3f1c1u, - 0x3bc9d942u, 0xbf7ffebeu, 0xbf7ffe88u, 0x3f7fff25u, 0x3f7fff33u, 0x3f7ffe62u, - 0x3f7ffe5cu, 0xbf7ffe10u, 0x3f7ffeefu, 0xbf7fffb0u, 0x3f7fff6eu, 0xbf7ffebbu, - 0x3bd9b609u, 0x3f7ffedcu, 0xbf7ffe4eu, 0xbf7ffe58u, 0x3f7ffe41u, 0x3f7ffec2u, - 0x3f7ffe5du, 0xbf7ffe9cu, 0xbf7ffec0u, 0xbf7ffe91u, 0x3f7ffe77u, 0x3bddf623u, - 0x3f7ffe92u, 0x3bbad1a9u, 0xbf7ffe11u, 0x3bf5b6d0u, 0x3f7ffe1cu, 0x3f7ffe7fu, - 0xbf7ffe09u, 0xbf7ffe34u, 0x3bf02e4eu, 0x3bf1fe0cu, 0xbf7fff89u, 0x3f7ffe08u, - 0x3f7ffe79u, 0x3becb273u, 0x3f7fff00u, 0x3f7ffe15u, 0x3f7ffefeu, 0xbf7ffe5eu, - 0xbf7ffe6bu, 0xbf7ffe84u, 0xbbe83dbfu, 0x3f7ffe1du, 0x3f7ffecdu, 0x3f7ffe8fu, - 0xbf7fff6cu, 0x3be97458u, 0xbf7ffe4fu, 0x3ba434d6u, 0x3bd580d7u, 0x3f7fff46u, - 0x3f7ffe18u, 0xbbaeedafu, 0x3f7ffeddu, 0xbf7ffe27u, 0xbf7ffe3du, 0xbf7ffe9du, - 0x3f7ffe89u, 0x3f7ffe0au, 0xbf7ffe59u, 0x3f7ffe7bu, 0x3f7ffe8eu, 0xbbdb4a93u, - 0xbbfda153u, 0xbf7fff48u, 0x3f7ffe1eu, 0x3f7ffe5cu, 0xbf7ffe6au, 0xbf7fff1cu, - 0xbbf09242u, 0x3f7fff30u, 0xbbe3533fu, 0xbf7ffe1bu, 0x3f7ffec1u, 0xbf7ffe17u, - 0xbf7ffe04u, 0x3f7fff17u, 0xbf7fff25u, 0x3f7ffec4u, 0xbf7ffeacu, 0xbf7fff4du, - 0x3f7ffe51u, 0x3f7fff01u, 0x3f7ffe40u, 0x3f7ffe0cu, 0x3f7ffe96u, 0x3f7ffe8au, - 0x3f7ffe7au, 0x3bff9c78u, 0xbbf7b166u, 0x3f7fff79u, 0x3f7ffed9u, 0xbf7ffec2u, - 0x3f7ffe4eu, 0xbbf9c9beu, 0xbbc6f806u, 0x3f7ffebeu, 0x3f7ffe65u, 0xbf7ffe12u, - 0xbf7ffe0eu, 0x3f7ffe35u, 0xbf7ffef2u, 0xbf7ffe86u, 0xbf7fff5fu, 0x3f7ffe8bu, - 0x3f7ffed7u, 0x3bbb151eu, 0x3f7ffed6u, 0x3f7ffe9fu, 0xbf7ffe78u, 0x3f7fff56u, - 0xbb99e210u, 0xbf7ffe68u, 0x3f7ffeabu, 0x3f7ffe06u, 0x3f7fffa0u, 0x3f7fff71u, - 0xbbe69c7cu, 0xbf7ffe9bu, 0xbf7ffed0u, 0xbbe4f8fcu, 0x3f7ffe2eu, 0xbf7ffea1u, - 0x3f7ffe95u, 0x3f7ffea0u, 0x3f7ffe38u, 0x3f7ffe71u, 0x3f7fff0eu, 0x3f7ffe0fu, - 0x3f7ffee7u, 0xbf7ffe26u, 0x3f7ffeaau, 0xbf7ffe6cu, 0x3f7ffeedu, 0xbf7ffe0du, - 0x3f7fff45u, 0x3f7ffeb1u, 0xbbd676f2u, 0x3f7ffebdu, 0xbf7ffe31u, 0x3f7ffe87u, - 0xbf7ffe57u, 0xbf7fff31u, 0x3f7ffe60u, 0xbf7ffedau, 0x3f7fff69u, 0x3f7fff2cu, - 0xbf7ffe83u, 0xbf7ffe2bu, 0xbf7ffea4u, 0xbf7fffa3u, 0xbf7ffe4au, 0xbbea11e9u, - 0x3f7ffe16u, 0xbf7fffa5u, 0xbf7ffe31u, 0x3f7ffe3au, 0xbf7fff0du, 0xbf7ffe84u, - 0xbf7ffe6bu, 0x3f7ffeedu, 0xbf7ffe6cu, 0x3f7ffe44u, 0x3f7fff24u, 0x3f7ffeaeu, - 0x3b9e5cfdu, 0x3f7ffeb7u, 0x3f7ffe35u, 0x3f7ffe65u, 0x3f7ffe4eu, 0x3f7ffe40u, - 0xbf7ffec9u, 0xbf7ffe6au, 0xbf7ffec6u, 0x3f7fffe0u, 0x3f7ffe75u, 0x3f7fff1eu, - 0xbbfec9a7u, 0xbf7ffe95u, 0xbf7ffe71u, 0x3f7ffe26u, 0x3f7ffe0du, 0x3f7ffe82u, - 0x3f7ffe31u, 0xbf7ffe87u, 0x3f7ffe10u, 0x3f7ffe4au, 0xbf7ffe16u, 0xbbfe6222u, - 0xbf7ffe3au, 0x3f7fff23u, 0x3f7ffe7fu, 0xbf7fff34u, 0xbf7ffe32u, 0xbbf914c0u, - 0x3f7ffea2u, 0xbf7ffe40u, 0x3f7ffec9u, 0x3f7fff51u, 0xbf7ffe52u, 0xbf7ffe01u, - 0x3f7ffe1au, 0x3f7ffee9u, 0xbbb65715u, 0x3f7ffe8fu, 0xbf7ffed3u, 0x3f7fff22u, - 0x3f7ffe22u, 0x3f7ffe44u, 0xbbfaadfeu, 0x3f7ffe69u, 0xbf7fff2du, 0x3f7ffe36u, - 0xbf7ffe23u, 0xbf7ffedeu, 0xbf7fff54u, 0x3f7fff63u, 0xbf7ffeceu, 0x3f7fff4eu, - 0x3f7ffe9du, 0x3f7ffefbu, 0x3f7ffe5eu, 0xbf7ffe79u, 0xbf7ffedcu, 0xbf7fff8au, - 0xbf7ffe43u, 0x3f7ffe10u, 0xbbd4c5c2u, 0xbf7fff2fu, 0xbf7ffe32u, 0xbf7ffee4u, - 0xbf7fff08u, 0x3f7ffeecu, 0x3f7ffe45u, 0x3f7ffe49u, 0x3f7ffed4u, 0x3f7ffe9eu, - 0x3f7ffe91u, 0x3f7ffef1u, 0x3f7ffe14u, 0xbf7fff09u, 0xbf7ffe37u, 0x3f7ffe7au, - 0x3f7ffe46u, 0xbf7ffea3u, 0x3f7ffe07u, 0xbf7ffef4u, 0xbf7ffec2u, 0x3f7ffee2u, - 0x3f7ffe1bu, 0x3f7ffe05u, 0xbf7ffea5u, 0x3bee572au, 0x3f7ffeb8u, 0xbf7ffe2fu, - 0xbf7ffe42u, 0xbf7ffe59u, 0xbf7fff5au, 0xbf7ffee1u, 0x3f7ffe3bu, 0xbf7ffe9au, - 0x3f7fff37u, 0x3f7ffeddu, 0xbf7ffef6u, 0x3f7ffe90u, 0xbf7ffe04u, 0x3f7ffe0bu, - 0xbf7ffe8cu, 0xbf7ffe83u, 0x3f7ffe38u, 0x3f7ffee5u, 0xbf7ffe8au, 0xbf7ffe0fu, - 0xbf7fff04u, 0xbf7ffe29u, 0xbf7ffe4du, 0x3f7ffe1cu, 0xbf7ffec1u, 0xbf7ffea9u, - 0x3f7ffe09u, 0x3f7ffe15u, 0xbf7ffe39u, 0xbf7ffe06u, 0x3f7ffe0eu, 0x3f7ffe48u, - 0x3b92c549u, 0xbf7ffe12u, 0xbf7ffe02u, 0xbf7ffea8u, 0xbf7ffe93u, 0x3f7ffe3eu, - 0x3f7ffe30u, 0x3f7fff02u, 0xbf7ffe2au, 0xbf7ffe34u, 0xbf7ffe8eu, 0xbf7ffe55u, - 0x3f7fff0fu, 0x3f7ffe6du, 0x3f7ffe10u, 0x3f7ffe22u, 0x3f7fff58u, 0xbf7fff67u, - 0x3f7ffe19u, 0xbf7ffee9u, 0xbf7ffe9du, 0x3f7ffe43u, 0x3f7fff2fu, 0x3bfd8c33u, - 0xbf7ffe6eu, 0xbf7fffaau, 0xbf7fff4au, 0x3f7ffedbu, 0x3f7ffe0au, 0xbf7ffe58u, - 0xbbebdb89u, 0x3be6f5c5u, 0x3f7ffe24u, 0xbf7ffeb5u, 0xbbf6761au, 0x3f7ffe3du, - 0xbf7ffecdu, 0x3f7ffe92u, 0x3f7ffe2du, 0xbf7ffe44u, 0x3bd9a77bu, 0x3f7ffe4au, - 0xbf7ffec3u, 0x3f7ffe35u, 0x3f7ffeb0u, 0x3f7ffe1fu, 0xbf7fff6fu, 0x3f7ffe1eu, - 0xbf7ffea4u, 0x3f7ffe5cu, 0x3f7fff36u, 0xbf7ffe98u, 0x3f7fff10u, 0xbf7ffea6u, - 0x3f7ffec9u, 0xbf7ffea0u, 0x3f7ffe6cu, 0x3f7ffe40u, 0xbf7ffe7eu, 0xbf7ffe21u, - 0x3f7ffe0cu, 0x3f7ffe18u, 0xbf7fffd1u, 0x3f7ffe80u, 0xbf7ffe94u, 0xbf7ffe74u, - 0xbbd487e1u, 0xbbe1d69eu, 0xbf7ffed7u, 0xbf7ffe27u, 0x3befb386u, 0x3f7ffe73u, - 0x3f7ffe65u, 0x3f7ffe3au, 0xbf7fff13u, 0xbf7ffe32u, 0xbf7fff56u, 0x3f7fff85u, - 0xbf7ffef9u, 0x3f7ffe1bu, 0xbf7ffebeu, 0xbf7ffe01u, 0xbf7ffeb7u, 0x3ba9621eu, - 0xbbfc2106u, 0xbbe72770u, 0xbf7ffe0bu, 0xbf7ffe38u, 0xbf7ffee5u, 0x3f7ffec6u, - 0xbf7ffe6bu, 0x3f7ffe69u, 0xbf7ffe08u, 0xbf7fff97u, 0x3f7ffe99u, 0xbf7ffe57u, - 0x3f7ffe97u, 0x3f7ffe6fu, 0xbbd2c5bfu, 0xbf7ffeeau, 0xbf7ffe46u, 0xbf7ffe77u, - 0xbf7ffe20u, 0xbbe46cccu, 0x3f7ffe0au, 0xbbf50605u, 0x3f7ffebdu, 0xbf7ffea7u, - 0x3f7ffe3eu, 0xbf7ffe25u, 0x3f7ffe2eu, 0x3f7ffe6au, 0x3f7ffe0fu, 0x3f7ffe04u, - 0x3f7ffe02u, 0xbf7fff91u, 0x3f7ffe87u, 0xbb76ed70u, 0xbf7ffe13u, 0x3f7ffe15u, - 0xbbec7374u, 0x3bea4291u, 0xbf7ffe45u, 0x3f7ffee4u, 0x3bea9840u, 0x3f7ffe67u, - 0xbbe18a78u, 0xbf7ffe5du, 0x3f7fff71u, 0xbf7fff19u, 0xbf7ffe9eu, 0x3bb6cfedu, - 0x3f7ffed5u, 0xbbd15f79u, 0x3f7ffedfu, 0xbf7ffef4u, 0xbbebb0e6u, 0x3f7fff6bu, - 0x3f7ffeb2u, 0xbf7ffe30u, 0xbf7ffe19u, 0x3bec062cu, 0x3f7ffed9u, 0xbf7ffe61u, - 0xbf7ffe68u, 0xbbf62489u, 0x3f7ffe07u, 0x3f7fff27u, 0xbf7fff2du, 0xbf7ffe1du, - 0xbf7ffee6u, 0xbf7ffe63u, 0xbbdb8429u, 0x3f7ffe39u, 0x3f7ffee9u, 0x3f7ffecdu, - 0x3f7ffe7du, 0xbf7ffec0u, 0xbf7ffec7u, 0x3f7ffe70u, 0x3f7ffe8du, 0x3f7fff22u, - 0x3f7ffe79u, 0xbbf3fddfu, 0x3f7ffec5u, 0x3f7ffe4cu, 0x3f7ffe8au, 0xbf7ffe3cu, - 0x3f7ffe75u, 0xbf7ffeaau, 0x3f7ffef2u, 0xbf7ffe8bu, 0x3bd99a3du, 0xbf7ffe23u, - 0x3f7ffe17u, 0xbf7ffe06u, 0x3f7fff3bu, 0xbf7ffe8eu, 0xbf7ffe98u, 0xbf7ffeb7u, - 0xbf7ffefau, 0x3f7ffe1fu, 0x3bd872cfu, 0xbb9e61e1u, 0x3f7ffe22u, 0xbf7ffeddu, - 0xbf7ffe21u, 0x3f7ffe54u, 0x3f7ffe78u, 0xbf7fff08u, 0x3f7ffe0eu, 0xbf7ffe36u, - 0x3f7ffe0eu, 0x3f7ffe54u, 0xbf7ffe03u, 0xbbe2cdb1u, 0x3f7ffe4eu, 0xbf7ffe83u, - 0x3f7ffeaeu, 0x3f7ffe17u, 0x3f7ffe3fu, 0xbf7ffe5cu, 0xbf7fffc0u, 0xbbf66fe3u, - 0x3f7fff5au, 0x3f7ffe9cu, 0x3bf0548fu, 0x3f7ffea1u, 0xbf7ffea5u, 0xbf7ffe6du, - 0xbf7fff23u, 0x3f7ffe92u, 0xbf7ffef8u, 0xbf7ffe5eu, 0x3f7ffe6bu, 0x3f7ffe37u, - 0x3f7ffe60u, 0xbf7fff40u, 0xbbfadb1fu, 0x3f7ffe36u, 0x3f7ffe34u, 0x3f7ffe04u, - 0xbf7fffa8u, 0xbf7ffe01u, 0x3f7ffe09u, 0x3beea03du, 0x3f7ffe11u, 0x3f7ffe85u, - 0xbf7ffe16u, 0x3f7ffef6u, 0xbf7fff0fu, 0x3f7ffe08u, 0xbf7ffe7eu, 0xbf7ffe25u, - 0xbf7ffe4au, 0xbf7ffe42u, 0xbf7ffe90u, 0x3f7fff2eu, 0xbf7ffe20u, 0x3f7ffe9fu, - 0xbbf7c017u, 0x3f7ffe4fu, 0x3f7ffe1au, 0x3f7ffebeu, 0xbf7ffe41u, 0xbf7fff1cu, - 0x3f7ffe95u, 0xbf7ffea4u, 0x3f7ffedeu, 0x3f7ffe12u, 0x3bf6c5ffu, 0xbf7ffeb1u, - 0x3bf45a2eu, 0xbf7ffee7u, 0x3bbe3228u, 0x3f7ffe3bu, 0x3f7ffe93u, 0xbf7ffe38u, - 0xbf7ffe35u, 0xbf7ffedcu, 0x3f7ffe61u, 0x3f7ffe2eu, 0x3f7fff90u, 0x3f7ffe79u, - 0x3bc90183u, 0xbbd51dadu, 0x3f7ffef3u, 0xbf7ffe47u, 0x3f7ffe6eu, 0xbf7ffe59u, - 0xbb7bc8eau, 0xbf7ffe02u, 0x3f7ffe06u, 0x3f7ffe3du, 0x3f7ffe5au, 0xbf7fff1au, - 0x3f7ffed6u, 0xbf7ffe10u, 0x3f7ffeafu, 0x3f7ffe0au, 0xbf7ffe7du, 0xbf7ffe7bu, - 0x3f7ffe88u, 0x3f7ffe76u, 0xbf7ffe1eu, 0x3f7ffe69u, 0x3f7fff00u, 0xbf7ffef5u, - 0x3f7ffe60u, 0xbbceff63u, 0xbf7fff82u, 0xbf7ffe63u, 0xbf7ffe8eu, 0x3bfd8798u, - 0x3f7fff32u, 0xbf7ffe1du, 0x3f7ffe74u, 0x3f7ffe03u, 0x3f7fffe6u, 0x3f7ffef4u, - 0x3bdde463u, 0xbf7ffe40u, 0x3f7fff61u, 0xbbcbe2adu, 0xbf7fff28u, 0x3f7fff79u, - 0xbf7fffc9u, 0xbf7ffee0u, 0x3f7fff4du, 0xbf7ffec0u, 0xbf7ffe39u, 0x3f7ffe89u, - 0x3f7ffe24u, 0xbf7fff88u, 0x3f7fff25u, 0x3f7ffe5bu, 0xbf7fff09u, 0xbf7ffe7au, - 0x3f7ffe9au, 0xbf7ffee5u, 0x3f7ffeddu, 0x3f7ffe26u, 0xbf7ffe45u, 0xbf7ffeccu, - 0xbf7ffea0u, 0xbbbc8740u, 0x3bc58d0du, 0x3f7ffe04u, 0xbf7ffeb6u, 0xbf7ffec8u, - 0x3f7ffe2fu, 0xbf7ffe52u, 0xbf7ffe4bu, 0xbf7ffe0fu, 0x3f7ffe0cu, 0x3f7ffe5du, - 0x3f7ffe56u, 0x3f7ffe84u, 0x3f7fff42u, 0xbbfa14ecu, 0xbf7ffe6fu, 0x3f7ffe1cu, - 0x3f7ffe2au, 0xbf7ffe21u, 0x3b75924fu, 0x3f7ffe51u, 0xbf7ffe62u, 0xbf7ffec4u, - 0x3bee073bu, 0xbf7fff55u, 0x3f7fff27u, 0x3f7ffea8u, 0x3f7ffe03u, 0x3bf7037fu, - 0x3f7ffe69u, 0x3f7ffe76u, 0xbf7ffe7bu, 0xbf7ffe7du, 0xbbe3b95fu, 0xbf7ffeb1u, - 0x3f7ffe7eu, 0x3f7ffea4u, 0xbf7ffe06u, 0x3f7fff1au, 0xbf7ffeafu, 0xbf7ffecdu, - 0x3f7ffef5u, 0xbf7ffebau, 0x3bdaccf3u, 0x3f7fff75u, 0x3f7ffe44u, 0x3bf43126u, - 0x3f7fff11u, 0xbb8b4592u, 0x3f7ffed7u, 0xbf7ffe53u, 0xbf7ffe11u, 0x3f7ffea0u, - 0xbb6fbcf7u, 0xbf7ffeddu, 0x3f7ffee5u, 0xbbf049cau, 0x3f7ffef9u, 0x3f7ffec0u, - 0xbf7ffe3du, 0x3f7ffe94u, 0x3f7ffe07u, 0xbf7ffe63u, 0xbf7fff09u, 0xbbf9d466u, - 0x3f7ffea3u, 0x3f7ffebdu, 0x3f7ffe4fu, 0xbbf53d51u, 0xbf7fff12u, 0xbf7ffed0u, - 0xbf7ffea1u, 0x3f7ffe71u, 0x3f7ffe0du, 0x3f7ffe0au, 0xbf7ffe02u, 0x3f7fff8fu, - 0xbf7ffef8u, 0xbf7ffe34u, 0x3f7fff83u, 0x3f7fff9cu, 0xbf7ffea6u, 0x3f7ffe3cu, - 0xbf7ffe0cu, 0x3f7ffefbu, 0x3f7ffe4bu, 0x3f7ffeb6u, -}; -// cosf: musl's result is wrong by 1 ulp for these |x| bits; values are result bits. |x| < 0x4dc90fdb -ALTD_MATH_TABLE uint32_t UCRT_COSF_KEYS[703] = { - 0x3fc81e28u, 0x3fc82d15u, 0x3fc84caeu, 0x3fc855b1u, 0x3fc8bc8du, 0x3fc98e69u, - 0x3fc9c9c5u, 0x3fc9e926u, 0x3fc9fa78u, 0x3fca0286u, 0x3fca07f7u, 0x3fca087fu, - 0x3fca0c27u, 0x4048910cu, 0x40489292u, 0x40489315u, 0x4048945fu, 0x404894e4u, - 0x404895ceu, 0x40489698u, 0x4048a2f8u, 0x4048a57fu, 0x4048aa95u, 0x4048ac7fu, - 0x4048b148u, 0x4048b2a5u, 0x4048b2d1u, 0x4048ba58u, 0x4048c2cfu, 0x40496aceu, - 0x40496d68u, 0x40496debu, 0x40497778u, 0x40498134u, 0x4049820cu, 0x4049862du, - 0x404986fcu, 0x40498763u, 0x4049893fu, 0x40498ab0u, 0x40498d44u, 0x40498f0au, - 0x40968fb6u, 0x409709c0u, 0x40c8d38eu, 0x40c8d39fu, 0x40c8d3b0u, 0x40c8d6b0u, - 0x40c8d931u, 0x40c8da60u, 0x40c8df7bu, 0x40c8e03bu, 0x40c8e3cfu, 0x40c8eecfu, - 0x40c9367bu, 0x40c938e4u, 0x40c93bcfu, 0x40c94481u, 0x40c94697u, 0x40c946cfu, - 0x40c94ef1u, 0x40fb25a1u, 0x40fb8efcu, 0x40fb911au, 0x40fb9182u, 0x40fb924eu, - 0x4116acc3u, 0x4116b10au, 0x412fd42cu, 0x41300c5cu, 0x4148f557u, 0x4148f611u, - 0x4148fa1bu, 0x41492c3au, 0x417b343eu, 0x417b3535u, 0x417b368bu, 0x417b3961u, - 0x417b3a8au, 0x417b5b19u, 0x417b6fdfu, 0x418a2b82u, 0x4196bcafu, 0x4196bd83u, - 0x41afde74u, 0x41afdf71u, 0x41afe2b4u, 0x41afe3c9u, 0x41aff8c5u, 0x41aff97bu, - 0x41affc18u, 0x41affdadu, 0x41bc71bau, 0x41bc8c52u, 0x41c90119u, 0x41e221e8u, - 0x41e2228cu, 0x41e22310u, 0x41e22594u, 0x41e227b3u, 0x41e23bb9u, 0x41e23fa1u, - 0x41fb4507u, 0x41fb4514u, 0x41fb45cfu, 0x41fb48bdu, 0x420a337fu, 0x420a416au, - 0x420a4285u, 0x4216c3efu, 0x4216c6ecu, 0x421d0efau, 0x422356ebu, 0x42236374u, - 0x422fe763u, 0x422ff5bcu, 0x423c846au, 0x423c8609u, 0x424915f9u, 0x4255990eu, - 0x4255997eu, 0x42559afau, 0x426229e3u, 0x42622c69u, 0x426873f5u, 0x426ebb68u, - 0x426ebe17u, 0x4275129eu, 0x427512feu, 0x4275131cu, 0x427b5ad7u, 0x4283f040u, - 0x428712c7u, 0x42871790u, 0x4296c8b1u, 0x4296cfadu, 0x4299ecbau, 0x42a9a22bu, - 0x42b63293u, 0x42b63979u, 0x42b639b1u, 0x42b63a3au, 0x42bc7afeu, 0x42bc7b15u, - 0x42c913cau, 0x42cf5462u, 0x42cf5c1au, 0x42d59d94u, 0x42dbe5c0u, 0x42e22e8eu, - 0x42e22e9eu, 0x42e87743u, 0x42e877beu, 0x42e87df7u, 0x42eebf76u, 0x42eec010u, - 0x42f50ecbu, 0x42fb4fe6u, 0x42fb57a9u, 0x430714bau, 0x431081a6u, 0x43235b51u, - 0x432ccb94u, 0x432fec70u, 0x4334a2a0u, 0x43363840u, 0x433c80ceu, 0x4342c901u, - 0x43527e70u, 0x43541070u, 0x4358c6d8u, 0x435be779u, 0x435f0ba5u, 0x436557fcu, - 0x43687c4eu, 0x436b9cccu, 0x436d2ed9u, 0x437509b4u, 0x4378306fu, 0x4379bfb2u, - 0x437e772fu, 0x4380cdafu, 0x4380ceb1u, 0x438715c0u, 0x438715fdu, 0x43908445u, - 0x439795bfu, 0x43985ed9u, 0x439b817eu, 0x439dde3du, 0x43a35dd6u, 0x43a4ee49u, - 0x43a9a515u, 0x43b4a35eu, 0x43baebcfu, 0x43c458a8u, 0x43c5ec81u, 0x43d0eb62u, - 0x43d27d97u, 0x43d8c48cu, 0x43df0e84u, 0x43e55578u, 0x43e55714u, 0x43eec3c0u, - 0x43f82e92u, 0x43fce6b9u, 0x43fce6c9u, 0x43fce6d9u, 0x43fce6e9u, 0x440004a7u, - 0x4410832du, 0x441926abu, 0x441ea606u, 0x44203827u, 0x44229362u, 0x4429a5c7u, - 0x442d932cu, 0x4438920cu, 0x443bb63au, 0x444390dau, 0x4445869cu, 0x445b20bbu, - 0x445be8e0u, 0x446428fau, 0x4467b1c4u, 0x446f8c47u, 0x447ce66du, 0x447f40a1u, - 0x448004e8u, 0x448584aau, 0x44893f59u, 0x44940c6bu, 0x44af2491u, 0x44b6ccebu, - 0x44b82d41u, 0x44ccfd63u, 0x44d27c62u, 0x44d2e0eau, 0x44d34572u, 0x44d53c21u, - 0x44d98e64u, 0x44dabbedu, 0x44ea0ca0u, 0x450069bau, 0x4503f24bu, 0x45056b49u, - 0x450971f6u, 0x450a08bfu, 0x4516cbd0u, 0x451c7db5u, 0x451caff4u, 0x451e7424u, - 0x451ed8a9u, 0x4521fd19u, 0x4526b34bu, 0x4526b37fu, 0x45392877u, 0x453be831u, - 0x4544be19u, 0x454fbcc9u, 0x4555a0f3u, 0x455d4917u, 0x45626435u, 0x456ad5a3u, - 0x456ec2b9u, 0x4574422cu, 0x457606bau, 0x457638cdu, 0x4576e8bcu, 0x45771b3bu, - 0x457b53f0u, 0x458d7838u, 0x4596cbd5u, 0x4596cbf3u, 0x45a0066eu, 0x45a38f17u, - 0x45a3da7cu, 0x45a9d795u, 0x45ab69b7u, 0x45abce5cu, 0x45b98cf1u, 0x45bc1a64u, - 0x45bf70e2u, 0x45c0142au, 0x45c61defu, 0x45cc4d30u, 0x45ccb1bbu, 0x45cf3f47u, - 0x45d00838u, 0x45d55580u, 0x45d5ec4cu, 0x45dfd6b3u, 0x45e0221bu, 0x45e10431u, - 0x45e34659u, 0x45edc78fu, 0x45f054fbu, 0x45f2b018u, 0x45f82fa1u, 0x45fc815du, - 0x45fdc816u, 0x46012c6du, 0x460dd037u, 0x46133692u, 0x461adeeeu, 0x46259ee9u, - 0x4629fd51u, 0x462a942bu, 0x462c9763u, 0x463aff9eu, 0x463c5922u, 0x464df230u, - 0x46561e88u, 0x465a63d0u, 0x465bef99u, 0x46651745u, 0x4665ae14u, 0x4668f7f8u, - 0x4676b68eu, 0x46770e92u, 0x467ee2dbu, 0x468ee4b1u, 0x468f16f5u, 0x46913fe8u, - 0x4691d06bu, 0x46a107f7u, 0x46a38bfeu, 0x46a3bb24u, 0x46a43f10u, 0x46b7bf0eu, - 0x46be8212u, 0x46c87912u, 0x46d0ea7du, 0x46dae170u, 0x46e391afu, 0x46e6771fu, - 0x46ea3eb5u, 0x46f19bb0u, 0x46f26b08u, 0x46f60060u, 0x46f6457fu, 0x46fda26cu, - 0x46ffa5acu, 0x470ade45u, 0x470e834fu, 0x4711f619u, 0x471e451du, 0x47220671u, - 0x47261fbcu, 0x47290526u, 0x472f5d5au, 0x473497afu, 0x47368030u, 0x47370bfdu, - 0x473b31d9u, 0x473e8538u, 0x4742e3a4u, 0x4744729cu, 0x4744f9b6u, 0x474583f1u, - 0x474a3404u, 0x474d0050u, 0x474db9a7u, 0x4753ccbeu, 0x4756799cu, 0x4757d4c1u, - 0x475eefc0u, 0x47714d21u, 0x47732dcbu, 0x477464d0u, 0x477adf96u, 0x47816819u, - 0x47873b90u, 0x478a50e5u, 0x47920a85u, 0x479c6608u, 0x47a0fe8du, 0x47aa5ebcu, - 0x47b0bec9u, 0x47b14a96u, 0x47becfd6u, 0x47c4d726u, 0x47ca75feu, 0x47d03768u, - 0x47e2e67bu, 0x47e986efu, 0x47ed648bu, 0x47efa9bbu, 0x47fa8478u, 0x47fd1db5u, - 0x47fe0889u, 0x47ffc838u, 0x4805f217u, 0x48092671u, 0x48125cfdu, 0x4816ac79u, - 0x4816ec18u, 0x48197fd4u, 0x481b1f4du, 0x481bc761u, 0x48239aeeu, 0x482b7bd5u, - 0x482fd135u, 0x48416aa4u, 0x4842c36eu, 0x4842d0c9u, 0x484a1889u, 0x484a7c47u, - 0x484ad5d0u, 0x48521f23u, 0x48527719u, 0x485a2361u, 0x48630e88u, 0x4864a55eu, - 0x4864b126u, 0x48658b7eu, 0x486598d8u, 0x486c65adu, 0x486d3da9u, 0x486f580du, - 0x4871a063u, 0x487993bfu, 0x488109d9u, 0x488a9342u, 0x488b445du, 0x488d5fefu, - 0x488e36bdu, 0x4891c5cdu, 0x4892214du, 0x489682d9u, 0x489bb09au, 0x489c5524u, - 0x48a3e03du, 0x48a54694u, 0x48a8527au, 0x48adb735u, 0x48ae1a5du, 0x48aedc8eu, - 0x48b175c9u, 0x48baf9b3u, 0x48bcf60cu, 0x48c1c300u, 0x48c579b9u, 0x48cc170bu, - 0x48ccb4e8u, 0x48d31ff6u, 0x48d5550eu, 0x48def0ecu, 0x48e2a518u, 0x48e3b7c9u, - 0x48e51bf7u, 0x48e873a8u, 0x48f28ec2u, 0x48f3b2edu, 0x48f7f21du, 0x490127b1u, - 0x49114e9eu, 0x491b05e0u, 0x491d60c4u, 0x491f8627u, 0x492b4d7eu, 0x493318e5u, - 0x493a3979u, 0x493eb910u, 0x4941ddb4u, 0x494252bau, 0x494e09c5u, 0x49547a52u, - 0x4957f916u, 0x495a46b9u, 0x495ac73cu, 0x495b5a98u, 0x4961540fu, 0x49639d15u, - 0x4968359cu, 0x4968430fu, 0x4971857du, 0x497297e3u, 0x4976b5cbu, 0x4977c00bu, - 0x497b880cu, 0x497c756fu, 0x497f7529u, 0x498890a0u, 0x4988d330u, 0x498f3a37u, - 0x498f7cc7u, 0x4992577eu, 0x49991aceu, 0x499d570cu, 0x499e122bu, 0x49a0b56au, - 0x49b203a6u, 0x49ba18fau, 0x49bd8a96u, 0x49c0f585u, 0x49c39230u, 0x49d25102u, - 0x49d37f24u, 0x49d93d29u, 0x49db5030u, 0x49dd7e82u, 0x49e7a817u, 0x49eb33f6u, - 0x49ee9d6cu, 0x49f11deau, 0x49f7261bu, 0x49fbdf6cu, 0x49fe3b97u, 0x49ff89f9u, - 0x4a096fa1u, 0x4a0b15fau, 0x4a15bd91u, 0x4a15f74bu, 0x4a20a2f5u, 0x4a2ddb56u, - 0x4a32ca61u, 0x4a34c2f9u, 0x4a3658aeu, 0x4a3c3135u, 0x4a435050u, 0x4a44686eu, - 0x4a467cc2u, 0x4a49a763u, 0x4a4ab9c9u, 0x4a52149bu, 0x4a596868u, 0x4a5de5bdu, - 0x4a60b8a6u, 0x4a65220du, 0x4a66df9eu, 0x4a6d5dddu, 0x4a769704u, 0x4a7b74e7u, - 0x4a7b88d5u, 0x4a7d3c17u, 0x4a86ff59u, 0x4a8fc6b4u, 0x4a9a82aau, 0x4aa1c30du, - 0x4aa3b206u, 0x4aa5d438u, 0x4aac9aebu, 0x4ab2b3c3u, 0x4ab30739u, 0x4ab6d5d4u, - 0x4ab73c72u, 0x4aba3dbbu, 0x4ac5e585u, 0x4acbedcfu, 0x4acde959u, 0x4ace3b40u, - 0x4acfcd0eu, 0x4ad0ec05u, 0x4ad587dcu, 0x4ad6154eu, 0x4ad75cbeu, 0x4ad7b57eu, - 0x4adaa0c3u, 0x4ae29143u, 0x4ae2c450u, 0x4ae89814u, 0x4aeeed11u, 0x4af5600cu, - 0x4afd9bb0u, 0x4b0b9896u, 0x4b1b997bu, 0x4b1ba1cdu, 0x4b202774u, 0x4b2e6034u, - 0x4b2f8604u, 0x4b331d53u, 0x4b37d383u, 0x4b395213u, 0x4b395d41u, 0x4b3aa898u, - 0x4b3b5329u, 0x4b489926u, 0x4b48b0b9u, 0x4b54aa1du, 0x4b554f17u, 0x4b5a27f2u, - 0x4b69579eu, 0x4b6a477au, 0x4b6e0c6bu, 0x4b73e019u, 0x4b7ed637u, 0x4b7ee578u, - 0x4b7f5cd0u, 0x4b8707ccu, 0x4b90189bu, 0x4b92fa41u, 0x4b9cca63u, 0x4b9e68eeu, - 0x4b9fab47u, 0x4bb00b2fu, 0x4bb12fbdu, 0x4bb1ac75u, 0x4bbb7303u, 0x4bc40183u, - 0x4bc6e04du, 0x4bcbcb2fu, 0x4bd2fc5cu, 0x4bd6e295u, 0x4bdbf9e2u, 0x4be05a0du, - 0x4beb472du, 0x4beb759cu, 0x4bf4c656u, 0x4bf56cb3u, 0x4bfddc10u, 0x4c01e149u, - 0x4c027bd7u, 0x4c07a204u, 0x4c116f6bu, 0x4c117231u, 0x4c176855u, 0x4c224363u, - 0x4c37cef9u, 0x4c43a817u, 0x4c47484eu, 0x4c4f4972u, 0x4c54dd77u, 0x4c55c18fu, - 0x4c5a55c0u, 0x4c5d249du, 0x4c6321b0u, 0x4c655175u, 0x4c670a4eu, 0x4c690ec0u, - 0x4c743404u, 0x4c7a0400u, 0x4c7e8ab4u, 0x4c8ed913u, 0x4c913058u, 0x4c9bab6cu, - 0x4ca0ee01u, 0x4cb1f491u, 0x4cb386a1u, 0x4cb93346u, 0x4cbbe131u, 0x4cbdf1c8u, - 0x4cc247f3u, 0x4cc4a46eu, 0x4cc50bb8u, 0x4cc72767u, 0x4ccb2e31u, 0x4cd94adfu, - 0x4ce12483u, 0x4ce14c03u, 0x4cea9828u, 0x4ceb71d4u, 0x4cec089au, 0x4cecb69du, - 0x4cf355d9u, 0x4cf5db98u, 0x4cf7d7b8u, 0x4d037a9eu, 0x4d0a920eu, 0x4d0aeab3u, - 0x4d0b1fbbu, 0x4d0d3399u, 0x4d117cc8u, 0x4d125ac8u, 0x4d15b063u, 0x4d22e06cu, - 0x4d2916a5u, 0x4d2ef770u, 0x4d336bf2u, 0x4d343994u, 0x4d3efb46u, 0x4d430455u, - 0x4d460e8du, 0x4d466b5bu, 0x4d4ddb70u, 0x4d5049c7u, 0x4d54503bu, 0x4d549d01u, - 0x4d58a457u, 0x4d6133efu, 0x4d633172u, 0x4d65f7fdu, 0x4d671091u, 0x4d6916bcu, - 0x4d6b87d9u, 0x4d709c65u, 0x4d722133u, 0x4d73311fu, 0x4d7c7946u, 0x4d7e0f7fu, - 0x4d7f131bu, 0x4d83ed2bu, 0x4d901b74u, 0x4d98b50fu, 0x4da0cdceu, 0x4da684f7u, - 0x4da73bf8u, 0x4db27f22u, 0x4db5d35au, 0x4db90583u, 0x4dc1b03bu, 0x4dc6f4b4u, - 0x4dc70f38u, -}; -ALTD_MATH_TABLE uint32_t UCRT_COSF_VALUES[703] = { - 0x3bf1b213u, 0x3be2c52cu, 0x3bc32c57u, 0x3bba2961u, 0x3b269b39u, 0xbb7d1c93u, - 0xbbb9ea1du, 0xbbd94af6u, 0xbbea9cdbu, 0xbbf2aacdu, 0xbbf81bc3u, 0xbbf8a3c2u, - 0xbbfc4bbbu, 0xbf7ffe0au, 0xbf7ffe16u, 0xbf7ffe1au, 0xbf7ffe24u, 0xbf7ffe28u, - 0xbf7ffe2fu, 0xbf7ffe35u, 0xbf7ffe8eu, 0xbf7ffe9fu, 0xbf7ffec0u, 0xbf7ffeccu, - 0xbf7ffee9u, 0xbf7ffef1u, 0xbf7ffef2u, 0xbf7fff1cu, 0xbf7fff47u, 0xbf7ffefeu, - 0xbf7ffeefu, 0xbf7ffeecu, 0xbf7ffeb1u, 0xbf7ffe6fu, 0xbf7ffe69u, 0xbf7ffe4bu, - 0xbf7ffe45u, 0xbf7ffe42u, 0xbf7ffe34u, 0xbf7ffe29u, 0xbf7ffe15u, 0xbf7ffe07u, - 0xbbf0b759u, 0x3bf76f80u, 0x3f7ffe3au, 0x3f7ffe3bu, 0x3f7ffe3cu, 0x3f7ffe68u, - 0x3f7ffe8bu, 0x3f7ffe9bu, 0x3f7ffedcu, 0x3f7ffee5u, 0x3f7fff0eu, 0x3f7fff78u, - 0x3f7fff46u, 0x3f7fff2eu, 0x3f7fff0fu, 0x3f7ffea6u, 0x3f7ffe8au, 0x3f7ffe87u, - 0x3f7ffe0fu, 0x3bb8c0ebu, 0xbbecaa4fu, 0xbbf52240u, 0xbbf6c23du, 0xbbf9f237u, - 0xbf7ffe1cu, 0xbf7ffe98u, 0xbbcd9a17u, 0x3bf3e4feu, 0x3f7ffea1u, 0x3f7ffeb4u, - 0x3f7fff14u, 0x3f7ffe6eu, 0xbf7ffe0eu, 0xbf7ffe2cu, 0xbf7ffe54u, 0xbf7ffea3u, - 0xbf7ffec1u, 0xbf7fffe6u, 0xbf7ffe77u, 0xbbf64460u, 0x3f7ffe32u, 0x3f7ffe63u, - 0xbf7ffe25u, 0xbf7ffe60u, 0xbf7fff07u, 0xbf7fff35u, 0xbf7fff13u, 0xbf7ffef3u, - 0xbf7ffe6cu, 0xbf7ffe0du, 0xbbd22f22u, 0x3bd7501bu, 0x3f7ffe4du, 0xbf7ffe05u, - 0xbf7ffe2du, 0xbf7ffe4cu, 0xbf7ffed4u, 0xbf7fff33u, 0xbf7fff3du, 0xbf7ffe84u, - 0x3f7ffe4bu, 0x3f7ffe4eu, 0x3f7ffe78u, 0x3f7fff0bu, 0xbf7ffe4au, 0xbf7ffeadu, - 0xbf7ffe30u, 0x3f7ffe06u, 0x3f7fff3bu, 0x3bad19a1u, 0xbf7ffee4u, 0xbf7ffea7u, - 0x3f7ffeb0u, 0x3f7ffe12u, 0xbf7fff0au, 0xbf7ffe65u, 0x3f7ffed5u, 0xbf7ffe1bu, - 0xbf7ffe50u, 0xbf7ffeedu, 0x3f7ffe07u, 0x3f7fff15u, 0x3bcbf919u, 0xbf7ffe48u, - 0xbf7fff4du, 0x3be970ccu, 0x3bf570b7u, 0x3bf930b0u, 0x3f7ffe76u, 0xbf7fff6cu, - 0xbbf7f8a1u, 0x3ae91b0au, 0x3f7ffeb9u, 0x3f7ffe36u, 0x3bda589fu, 0xbf7ffeb7u, - 0xbf7ffe34u, 0xbf7ffeccu, 0xbf7ffe9fu, 0xbf7ffe24u, 0x3f7ffe21u, 0x3f7ffe37u, - 0x3f7ffe11u, 0xbf7ffe09u, 0xbf7ffe3eu, 0x3f7ffeabu, 0xbf7ffe64u, 0x3f7ffea8u, - 0x3f7ffeb5u, 0xbf7ffed3u, 0xbf7fff2au, 0xbf7ffe5au, 0x3f7ffe96u, 0x3f7fff0cu, - 0xbf7ffe7fu, 0x3f7ffe15u, 0x3f7ffe28u, 0xbf7ffe26u, 0x3f7ffe7au, 0x3f7ffec7u, - 0xbf7ffe18u, 0x3f7ffef9u, 0xbbcf367bu, 0x3f7ffe3bu, 0x3f7ffe1eu, 0x3f7ffea6u, - 0xbf7ffe4fu, 0x3bdbb415u, 0xbf7ffe78u, 0x3f7ffe42u, 0xbf7ffe1du, 0xbf7ffe32u, - 0x3f7ffe0eu, 0xbf7ffe6au, 0xbbed751au, 0x3f7ffeb1u, 0xbf7fffa1u, 0xbbffca09u, - 0xbf7fff9du, 0x3f7fff8eu, 0x3f7fff6eu, 0x3f7ffe60u, 0x3f7fff1fu, 0x3f7ffe79u, - 0xbbcb2b70u, 0xbf7ffe9du, 0xbf7ffed1u, 0xbbca5660u, 0x3f7ffe2eu, 0xbf7ffef7u, - 0x3f7fffd4u, 0xbf7ffe77u, 0xbf7ffe45u, 0xbf7ffea3u, 0x3f7ffe64u, 0xbf7ffe59u, - 0x3f7ffe08u, 0x3f7fff68u, 0x3f7ffe48u, 0x3f7fff3fu, 0x3f7ffe06u, 0x3f7ffe4cu, - 0x3f7ffe01u, 0xbf7ffec8u, 0xbf7ffe94u, 0xbf7ffe5cu, 0xbf7ffe20u, 0xbf7ffe70u, - 0x3f7fff9eu, 0xbf7ffeabu, 0x3f7ffe1cu, 0x3f7ffe26u, 0xbf7ffe7au, 0x3f7ffeb8u, - 0xbf7ffe1eu, 0xbf7ffe0eu, 0xbf7ffe8eu, 0xbf7ffe84u, 0xbbee9406u, 0xbf7ffe5eu, - 0x3f7ffe45u, 0xbbf8cd6eu, 0xbf7ffe08u, 0xbf7ffecbu, 0x3f7ffe20u, 0xbf7ffe04u, - 0x3f7ffedeu, 0x3f7fff69u, 0xbbe5f836u, 0xbf7ffe18u, 0x3f7ffe19u, 0xbbfc1afdu, - 0xbf7ffe4au, 0x3f7ffe69u, 0x3f7ffe88u, 0xbf7ffe89u, 0x3f7ffe8au, 0xbf7ffee8u, - 0x3f7ffe38u, 0xbf7ffefcu, 0x3f7fff14u, 0x3f7ffeb2u, 0x3f7ffe6bu, 0xbbe1a23bu, - 0x3f7ffe0fu, 0xbf7ffe66u, 0x3f7fff39u, 0xbf7ffe3cu, 0x3f7ffec5u, 0xbf7ffea0u, - 0xbf7ffe4eu, 0xbf7fff4au, 0xbf7ffeaeu, 0xbf7ffeafu, 0xbf7ffe50u, 0xbf7ffe03u, - 0x3f7fff18u, 0x3f7ffed7u, 0x3f7ffe9cu, 0xbf7ffe7cu, 0xbf7ffe92u, 0x3f7ffe2bu, - 0x3f7ffe9eu, 0x3f7fff01u, 0xbf7fff3eu, 0x3f7ffe55u, 0xbbe0e65bu, 0xbbf76354u, - 0x3f7ffe29u, 0xbf7ffe13u, 0x3f7ffe40u, 0x3f7ffe3du, 0x3f7ffe11u, 0x3f7ffe56u, - 0xbf7ffe1du, 0x3f7ffe24u, 0x3f7ffea7u, 0x3f7ffe05u, 0x3f7ffe21u, 0x3f7ffe41u, - 0x3f7fff49u, 0x3bb4d7f5u, 0x3f7ffe0cu, 0xbf7ffedfu, 0xbf7fff60u, 0xbf7ffe1fu, - 0xbf7ffe2au, 0xbf7ffe8cu, 0xbf7ffe86u, 0x3f7ffec3u, 0xbf7ffe53u, 0x3f7ffea5u, - 0xbf7ffea2u, 0x3f7ffe33u, 0x3f7fff8bu, 0x3f7fff13u, 0x3f7ffe37u, 0x3f7ffeddu, - 0xbf7ffeb7u, 0x3bb0349cu, 0xbf7ffe2du, 0xbf7ffebdu, 0xbf7ffe1cu, 0x3f7ffecbu, - 0xbf7ffeb8u, 0xbf7ffe26u, 0x3f7ffe70u, 0x3be81839u, 0xbf7ffe01u, 0xbba73806u, - 0x3f7ffef4u, 0xbf7ffe34u, 0x3bd404d3u, 0xbf7fff59u, 0xbf7ffe28u, 0x3f7ffe99u, - 0x3f7ffed2u, 0xbf7ffe82u, 0x3bc42ac1u, 0x3f7ffe85u, 0x3f7ffe8du, 0x3f7ffe02u, - 0xbf7ffe62u, 0xbf7ffe68u, 0xbbd6bde5u, 0xbf7ffeeeu, 0x3f7ffe7eu, 0x3bc67c6du, - 0x3f7ffe63u, 0x3f7ffeadu, 0x3f7ffe2cu, 0x3f7fff54u, 0x3f7fff16u, 0x3f7fff00u, - 0x3f7ffe80u, 0x3f7ffef3u, 0xbf7fff30u, 0xbf7fff63u, 0x3f7ffe3au, 0x3f7ffe23u, - 0x3f7ffe76u, 0x3f7ffe5eu, 0xbf7ffe72u, 0x3f7fff0fu, 0xbf7ffe51u, 0xbf7ffea9u, - 0xbf7fff0eu, 0xbf7ffe50u, 0x3f7ffe2eu, 0x3f7ffec8u, 0xbbfa9a48u, 0x3f7ffe0au, - 0x3f7ffe8au, 0xbf7ffe4eu, 0xbf7ffe10u, 0x3f7ffee4u, 0xbf7ffe47u, 0xbf7ffe16u, - 0xbf7ffe9bu, 0xbf7ffe4bu, 0x3f7ffe71u, 0xbf7fff0au, 0xbf7fff09u, 0xbbe1baa8u, - 0xbbe74b03u, 0xbf7ffe30u, 0x3f7ffe5fu, 0xbf7fff1eu, 0xbf7ffe3fu, 0x3f7ffeb9u, - 0xbbce11deu, 0x3f7ffe2du, 0xbf7ffe11u, 0xbf7ffe3bu, 0x3f7ffea0u, 0x3f7ffe56u, - 0xbf7ffecbu, 0x3f7ffeb8u, 0xbbb9cb3au, 0x3f7fff53u, 0x3f7ffe7au, 0x3f7ffe36u, - 0xbf7ffebeu, 0xbf7ffe1au, 0xbf7ffe07u, 0xbf7ffe9cu, 0x3f7ffe3au, 0x3b9a6edeu, - 0xbf7ffe55u, 0x3f7ffec4u, 0xbba69ae3u, 0x3f7ffe17u, 0xbf7ffe1cu, 0x3f7ffe05u, - 0xbf7ffe1du, 0xbf7ffe46u, 0x3f7fff4du, 0x3f7ffe60u, 0xbf7ffe40u, 0xbf7ffe82u, - 0x3be99d43u, 0x3f7fff43u, 0xbf7ffe12u, 0x3f7ffe57u, 0xbf7ffe08u, 0x3f7ffeeau, - 0x3f7ffe88u, 0xbf7ffe19u, 0xbf7ffe65u, 0x3f7fff1bu, 0xbf7ffef0u, 0xbf7ffe6cu, - 0x3f7ffee3u, 0x3f7ffea8u, 0xbf7ffefbu, 0xbbd1fd08u, 0x3be1b545u, 0x3f7ffec3u, - 0x3f7ffe3du, 0xbbebde62u, 0x3f7ffe4au, 0xbf7ffe6bu, 0x3f7ffe1eu, 0x3f7ffe92u, - 0xbf7ffe5du, 0xbf7fffdeu, 0x3f7ffe73u, 0x3f7ffe84u, 0x3f7ffe0du, 0xbf7ffe59u, - 0x3bfd45a1u, 0xbf7ffe0cu, 0xbf7ffe04u, 0xbf7fff87u, 0xbbc2feb3u, 0x3f7ffe0bu, - 0x3f7ffea1u, 0xbf7ffe7du, 0xbb6ec74au, 0xbf7ffe54u, 0x3f7ffe33u, 0x3f7ffe21u, - 0x3f7ffea5u, 0x3f7ffe35u, 0xbf7ffefcu, 0xbf7ffe89u, 0x3bcc1790u, 0xbf7ffe95u, - 0x3f7ffeabu, 0xbf7ffe49u, 0xbf7ffe3du, 0xbf7ffec3u, 0x3be1cad1u, 0x3f7ffe19u, - 0xbf7fff1fu, 0xbbabe180u, 0xbf7fff2fu, 0xbf7ffeb8u, 0x3f7fff0eu, 0xbbf6e3aau, - 0xbf7ffe02u, 0x3f7fff5fu, 0x3f7ffe10u, 0x3f7ffe9bu, 0x3f7fff1cu, 0x3f7fff4cu, - 0xbf7ffe17u, 0x3f7ffe1cu, 0xbbf6088cu, 0xbf7ffe05u, 0x3bcce7abu, 0x3f7ffe97u, - 0x3f7ffe08u, 0x3b7bc4cbu, 0xbf7ffec3u, 0x3be19fb9u, 0x3f7fff07u, 0x3f7fff27u, - 0x3f7ffef7u, 0xbf7ffe1bu, 0xbf7fff12u, 0x3f7ffe9eu, 0x3f7ffe8au, 0xbf7ffe13u, - 0xbf7ffe2au, 0x3f7ffe92u, 0xbf7ffe6cu, 0xbf7ffe12u, 0xbf7ffe5cu, 0xbf7ffe01u, - 0xbf7fffd5u, 0xbf7ffe06u, 0xbf7ffeecu, 0xbf7ffe64u, 0x3f7ffefeu, 0x3f7ffe6fu, - 0x3bb51a74u, 0xbf7ffe5du, 0x3f7ffe84u, 0xbf7ffee2u, 0x3b7126f7u, 0x3f7ffe83u, - 0xbf7ffe95u, 0xbf7ffe5fu, 0x3f7ffe5au, 0x3f7ffebdu, 0x3f7ffe1cu, 0x3f7ffe68u, - 0xbf7ffe1fu, 0xbf7ffef8u, 0xbf7ffe66u, 0xbf7ffed6u, 0x3f7fff5au, 0x3f7ffe3au, - 0xbbf8a06eu, 0x3f7ffedcu, 0xbf7ffe38u, 0x3f7ffe36u, 0x3f7ffebau, 0xbbfe4f9eu, - 0xbf7ffebbu, 0xbf7fff3bu, 0xbf7ffe04u, 0xbbd1d175u, 0xbf7ffe45u, 0xbf7ffe14u, - 0x3f7ffe3eu, 0x3f7ffea4u, 0xbf7ffe61u, 0x3f7fff2eu, 0xbf7ffe7fu, 0x3f7ffeadu, - 0x3f7ffe63u, 0xbf7fff32u, 0x3ba0cbc1u, 0x3f7ffe62u, 0x3f7fff8du, 0x3f7ffef4u, - 0xbf7fff47u, 0xbf7ffe1au, 0xbf7ffe7bu, 0xbbebc031u, 0x3f7ffe67u, 0xbf7fff65u, - 0x3f7fff0au, 0xbb8059f5u, 0x3f7ffeaau, 0x3f7fffa1u, 0xbf7fffdau, 0x3f7ffe2eu, - 0xbf7ffedfu, 0xbf7ffe50u, 0x3f7ffe18u, 0xbf7ffec9u, 0x3f7ffeb2u, 0x3f7ffe44u, - 0xbbd6b02bu, 0x3f7ffe9cu, 0x3f7ffebeu, 0x3f7ffe11u, 0xbf7fff02u, 0x3f7ffe26u, - 0xbf7ffe9au, 0xbf7ffe6eu, 0x3f7ffe2au, 0x3f7ffe13u, 0x3f7ffeb5u, 0xbf7ffee8u, - 0xbf7fff00u, 0xbf7ffef9u, 0x3f7fff17u, 0x3f7fff73u, 0xbf7fff30u, 0xbf7fffa3u, - 0x3f7ffe60u, 0xbf7ffe1eu, 0x3f7ffe5du, 0x3bdf16b1u, 0x3f7ffe16u, 0x3f7ffe4bu, - 0x3f7ffe9fu, 0x3bee9390u, 0x3f7ffec5u, 0xbf7ffef1u, 0x3f7ffe8fu, 0xbf7ffeb3u, - 0x3f7fff23u, 0xbf7ffe43u, 0x3f7ffe76u, 0x3f7ffe27u, 0x3f7ffe52u, 0x3f7ffe78u, - 0x3bc7e4d7u, 0xbf7fff95u, 0x3f7ffe2fu, 0x3f7fff3du, 0xbf7ffe90u, 0xbf7fff63u, - 0xbf7ffe04u, 0xbf7ffec3u, 0x3f7ffeebu, 0xbf7ffe93u, 0x3f7ffee5u, 0xbf7ffe31u, - 0xbbad81d7u, 0xbf7ffe0fu, 0xbf7ffe2bu, 0x3f7ffee0u, 0x3bdf9cc7u, 0x3f7ffec6u, - 0xbf7ffe67u, 0x3bc00afdu, 0xbbc5a127u, 0xbbbdba3cu, 0x3f7ffe30u, 0xbf7ffe22u, - 0xbbf39e21u, 0xbf7ffe1du, 0x3f7ffe9bu, 0x3f7fff11u, 0x3f7fff29u, 0xbf7ffedbu, - 0x3f7ffe4du, 0xbf7ffe8eu, 0x3beb8c23u, 0x3f7ffe4fu, 0x3f7ffe1fu, 0xbf7ffe6fu, - 0xbf7ffe92u, 0x3bb5b961u, 0xbf7ffe97u, 0x3f7fff03u, 0xbf7fff12u, 0x3f7ffe07u, - 0xbf7fff20u, 0x3f7ffe80u, 0x3f7ffe16u, 0xbf7ffecfu, 0x3f7ffe32u, 0xbf7fff3bu, - 0x3f7ffef7u, 0x3f7ffe88u, 0x3f7ffea5u, 0xbf7ffe06u, 0xbf7ffe14u, 0x3f7ffe0cu, - 0x3f7fff05u, 0x3f7fff10u, 0xbf7ffe39u, 0xbf7fff36u, 0x3f7fff1au, 0x3f7ffe3fu, - 0xbf7ffe1bu, 0x3f7ffe3cu, 0x3f7ffe4cu, 0x3f7ffe20u, 0xbbcca980u, 0xbf7ffec8u, - 0xbf7ffe77u, 0x3f7ffed3u, 0xbf7ffe5du, 0x3f7fff4cu, 0xbba61100u, 0x3f7ffe87u, - 0x3f7ffe96u, 0xbf7fff13u, 0x3f7ffe74u, 0x3be96970u, 0x3f7fff70u, 0xbf7ffe12u, - 0x3be2bf79u, 0xbf7ffef8u, 0x3f7ffe5cu, 0xbf7ffee3u, 0xbf7ffe2fu, 0x3f7ffe42u, - 0xbf7ffeb6u, 0xbf7ffe2du, 0xbf7ffe11u, 0xbf7ffe62u, 0x3f7ffe45u, 0x3f7ffe4eu, - 0xbf7ffe8au, 0x3bb6e58eu, 0xbf7ffe26u, 0x3f7ffe34u, 0xbf7ffe76u, 0xbf7ffe52u, - 0xbf7fff0fu, 0x3f7ffeb1u, 0xbbc3b4fcu, 0x3f7ffe03u, 0xbbc8dd97u, 0x3f7ffe49u, - 0x3f7ffe5fu, 0xbf7ffe3au, 0x3f7ffe47u, 0x3f7ffe10u, 0xbf7fff47u, 0xbf7fff4au, - 0xbf7fff0bu, -}; -// cosf, |x| >= 0x4dc90fdb (CPU only) -ALTD_MATH_TABLE uint32_t UCRT_COSF_WIDE_KEYS[2424] = { - 0x4dcb1780u, 0x4dd37664u, 0x4ddd87d9u, 0x4ddf3733u, 0x4ddfd138u, 0x4de53839u, - 0x4de74b9eu, 0x4dea010cu, 0x4df1d86bu, 0x4df67a3fu, 0x4df9c298u, 0x4dff4a70u, - 0x4e0828cau, 0x4e0855ceu, 0x4e09c5a4u, 0x4e0b4ceau, 0x4e0c2583u, 0x4e0fd978u, - 0x4e13e59bu, 0x4e14f0edu, 0x4e175995u, 0x4e1a4e87u, 0x4e24bc2eu, 0x4e2b66c8u, - 0x4e2f6dadu, 0x4e311128u, 0x4e3e636bu, 0x4e3f17c9u, 0x4e3f58b0u, 0x4e425443u, - 0x4e468f94u, 0x4e48b37au, 0x4e49c881u, 0x4e4a9fb9u, 0x4e4aca1eu, 0x4e4d41b9u, - 0x4e599728u, 0x4e5c14a1u, 0x4e5c21e3u, 0x4e664802u, 0x4e67b627u, 0x4e75dc15u, - 0x4e7871c1u, 0x4e870fe8u, 0x4e8c28e7u, 0x4e958d3fu, 0x4e971397u, 0x4e9a01e8u, - 0x4e9c505au, 0x4e9cdf43u, 0x4ea19772u, 0x4eaee314u, 0x4eaf17ceu, 0x4eb1a978u, - 0x4eb58359u, 0x4ebaf660u, 0x4ebf2ce8u, 0x4ec13e00u, 0x4ec26675u, 0x4ec2b3d9u, - 0x4ec49a3cu, 0x4ec70ec3u, 0x4ecb2828u, 0x4ed09b2fu, 0x4ed53b52u, 0x4ed8af4cu, - 0x4edf4d67u, 0x4ee6fd27u, 0x4ee7992bu, 0x4eee6d3cu, 0x4ef521d9u, 0x4effaacbu, - 0x4f085ae5u, 0x4f0aa507u, 0x4f0da15fu, 0x4f152068u, 0x4f1b1f43u, 0x4f30c5ecu, - 0x4f33f812u, 0x4f352777u, 0x4f37c527u, 0x4f3b2b41u, 0x4f3c63e6u, 0x4f4ada75u, - 0x4f4af347u, 0x4f4e477fu, 0x4f53fb95u, 0x4f5848eeu, 0x4f59e329u, 0x4f629887u, - 0x4f6b1127u, 0x4f6ec7bau, 0x4f6f6c60u, 0x4f6fa994u, 0x4f704b24u, 0x4f74ae89u, - 0x4f772db3u, 0x4f78b432u, 0x4f82660fu, 0x4f8a963au, 0x4f975141u, 0x4f98a758u, - 0x4f9be9fdu, 0x4fa2966fu, 0x4fa393a6u, 0x4fadc355u, 0x4fb2be31u, 0x4fbe37f1u, - 0x4fbef1ddu, 0x4fbfa4d9u, 0x4fc35642u, 0x4fd484cau, 0x4fd5ba81u, 0x4fda9761u, - 0x4fdaaf95u, 0x4ff03c7fu, 0x4ff9a226u, 0x4ffa4d94u, 0x4ffff569u, 0x5001b43bu, - 0x500609fbu, 0x5009df81u, 0x500dd2c8u, 0x50161eb3u, 0x50183b1fu, 0x501b1fe1u, - 0x5025028fu, 0x502fdee8u, 0x5035d65du, 0x503d5e43u, 0x5045ade1u, 0x5051ae23u, - 0x5053f019u, 0x50546959u, 0x505cd9e1u, 0x50604862u, 0x5060a680u, 0x5062a7cau, - 0x506863c7u, 0x506bb51bu, 0x507a6749u, 0x5088e1c0u, 0x508db39fu, 0x50a215b0u, - 0x50ad49d0u, 0x50bf60fau, 0x50bfff7au, 0x50c4f160u, 0x50d5b70eu, 0x50d67d19u, - 0x50d77dbeu, 0x50df1864u, 0x50e4e971u, 0x50efe18au, 0x510f53e7u, 0x5110d394u, - 0x511d0af3u, 0x51257eaeu, 0x5129c032u, 0x5129f26fu, 0x512d3caeu, 0x5136fd6au, - 0x513c5e52u, 0x51475d31u, 0x514a560au, 0x514b75ddu, 0x514fe039u, 0x515ac58du, - 0x515c121fu, 0x5162bf74u, 0x5168595fu, 0x516b99aau, 0x517a8d67u, 0x517c0796u, - 0x517f1075u, 0x518acfc6u, 0x518d996bu, 0x51902964u, 0x5191c977u, 0x5193b508u, - 0x51999e33u, 0x51a079b9u, 0x51a1f3c3u, 0x51a53e3cu, 0x51a6ed77u, 0x51a79195u, - 0x51ad9132u, 0x51b18013u, 0x51b5a2d8u, 0x51b76341u, 0x51bd51d5u, 0x51bf5706u, - 0x51c416e9u, 0x51d17c7au, 0x51e236c2u, 0x51e4c074u, 0x51ed07dfu, 0x51f3c4f0u, - 0x51f4e454u, 0x51f7b07eu, 0x5200266eu, 0x5207e390u, 0x52100bcdu, 0x5210f625u, - 0x52177badu, 0x521ba286u, 0x521d50beu, 0x521e86deu, 0x521ead7bu, 0x521ec359u, - 0x52226ee4u, 0x5222ee21u, 0x52230c71u, 0x52238e48u, 0x522d2089u, 0x522eba30u, - 0x52309d2au, 0x523382aau, 0x5236c2f5u, 0x5242e0eeu, 0x52467c04u, 0x5248782fu, - 0x52489635u, 0x524c523du, 0x52565af7u, 0x5260e92du, 0x526512cdu, 0x526a4df6u, - 0x526dc303u, 0x52714ef1u, 0x527d75cbu, 0x527e3a89u, 0x5282d23au, 0x528849deu, - 0x528ac235u, 0x528f5c9fu, 0x52942792u, 0x529966f4u, 0x52a92543u, 0x52ab9057u, - 0x52b01556u, 0x52b077b5u, 0x52b72d32u, 0x52b828bcu, 0x52c3f5f7u, 0x52c6a2c6u, - 0x52c8b460u, 0x52ce9563u, 0x52da6025u, 0x52ea3f18u, 0x52eb265fu, 0x52ec4b97u, - 0x52ed832du, 0x52f0bbbfu, 0x52f0d30fu, 0x52f1a32bu, 0x52f74fb6u, 0x52f87309u, - 0x52fbb71eu, 0x52fd78b7u, 0x5304763cu, 0x530f438fu, 0x5313e028u, 0x5315b6e2u, - 0x531dca6du, 0x53256d07u, 0x5329db48u, 0x5329e4c1u, 0x532ad4a3u, 0x532b3accu, - 0x532c2d27u, 0x5334d7aeu, 0x5338802cu, 0x53399457u, 0x5339c38fu, 0x533bfca8u, - 0x533c0dffu, 0x533d640au, 0x533e7a64u, 0x53450b98u, 0x534943a3u, 0x53503965u, - 0x53598caeu, 0x535dec82u, 0x535fa122u, 0x535fb643u, 0x5368c11fu, 0x5369b929u, - 0x536a0b80u, 0x53769be0u, 0x53797b67u, 0x537ab940u, 0x53880514u, 0x5388777bu, - 0x539cb61du, 0x53a367d0u, 0x53a4460fu, 0x53a57b74u, 0x53b6c673u, 0x53c5cbacu, - 0x53cf448fu, 0x53cf63e4u, 0x53d6c57eu, 0x53de0aadu, 0x53dec7b2u, 0x53fb7874u, - 0x53fc4d15u, 0x540003c0u, 0x54036c1fu, 0x541214cdu, 0x5414a5a1u, 0x5418301au, - 0x541c1ed7u, 0x541f8c05u, 0x54208b21u, 0x5424571cu, 0x542efdabu, 0x543776cau, - 0x544296e4u, 0x5445b410u, 0x5445b5f5u, 0x54475b0fu, 0x54563eb0u, 0x545a4943u, - 0x5460878au, 0x54740bb4u, 0x5477b4b5u, 0x547c7caau, 0x547e9610u, 0x5485d84cu, - 0x548653f4u, 0x54906924u, 0x54927911u, 0x5495fb16u, 0x549d98d7u, 0x54a49bf8u, - 0x54a4a78eu, 0x54a4bcd5u, 0x54a6f56bu, 0x54ac7972u, 0x54b69aa8u, 0x54bd4b1eu, - 0x54c3b986u, 0x54c45fa0u, 0x54caa6f0u, 0x54d77862u, 0x54d7ee5bu, 0x54e0f949u, - 0x54ee3808u, 0x54ee41b9u, 0x54f0a8a7u, 0x54f424e1u, 0x54f60807u, 0x54fd9c17u, - 0x55007df3u, 0x55028da8u, 0x5503d055u, 0x55079a25u, 0x550eb2c5u, 0x5515ff18u, - 0x551657feu, 0x551a7ec4u, 0x5529d72fu, 0x552cf91cu, 0x552e0020u, 0x552fc44eu, - 0x553290f5u, 0x5533a775u, 0x55375293u, 0x553cac98u, 0x555326d6u, 0x5555bfcau, - 0x555c3d92u, 0x555d6cb3u, 0x556a6093u, 0x557e2f3fu, 0x55800a17u, 0x5586d157u, - 0x55953821u, 0x55a8caa6u, 0x55ba746bu, 0x55c015eeu, 0x55c794beu, 0x55cbdc3eu, - 0x55cc7507u, 0x55cf4fddu, 0x55e172d4u, 0x55f2f1dau, 0x55f87094u, 0x55fb2856u, - 0x5604c5abu, 0x5608d166u, 0x560a4836u, 0x56123726u, 0x5612884eu, 0x5614f675u, - 0x56194ab2u, 0x561aa0acu, 0x561d5094u, 0x561df7b9u, 0x562a806au, 0x562c4d37u, - 0x564019c6u, 0x564f6d5cu, 0x565e0962u, 0x565ed446u, 0x56637899u, 0x566f5fe2u, - 0x5673ef03u, 0x5676800fu, 0x567d3208u, 0x567e1bc8u, 0x56813f3bu, 0x56822248u, - 0x56835b59u, 0x56843f55u, 0x56847c33u, 0x5690b980u, 0x5694ac97u, 0x5696baa3u, - 0x56990f3eu, 0x569927e9u, 0x569e17a7u, 0x56a1ddc9u, 0x56a2c5b2u, 0x56a5a7d1u, - 0x56ab75ceu, 0x56ac21c1u, 0x56b18b42u, 0x56b77250u, 0x56c4a315u, 0x56ccedbfu, - 0x56d2157fu, 0x56d644f9u, 0x56dd4cefu, 0x56dd51cbu, 0x56e1ffcfu, 0x56edf612u, - 0x56f2401cu, 0x56f9bfe2u, 0x56fb96e2u, 0x56fe9777u, 0x5702efdbu, 0x57097698u, - 0x57127f93u, 0x57152f00u, 0x57176c6fu, 0x5717d448u, 0x57187d60u, 0x57208464u, - 0x572377f2u, 0x5727a7a6u, 0x572c1b41u, 0x572d1822u, 0x572e65f1u, 0x5736e0edu, - 0x573e1d88u, 0x57492755u, 0x574b4e46u, 0x574c3bf3u, 0x574dd2b8u, 0x5754c114u, - 0x575e911bu, 0x5769df3cu, 0x576c45bau, 0x577342cfu, 0x57773d29u, 0x577af871u, - 0x577bdee2u, 0x5787da08u, 0x57891370u, 0x5789723eu, 0x57898e05u, 0x578edb3du, - 0x5796f651u, 0x579f8d4eu, 0x57a19c57u, 0x57a32f3du, 0x57ad2d28u, 0x57b47656u, - 0x57b4938eu, 0x57b4ee60u, 0x57ba53c1u, 0x57c5cf11u, 0x57c9463fu, 0x57cd3cf4u, - 0x57d64d15u, 0x57e049acu, 0x57e9861cu, 0x57ea1c5bu, 0x57ea6606u, 0x57eb4307u, - 0x57ed3230u, 0x57ef8f9cu, 0x57f3b8edu, 0x57f49250u, 0x57fe5494u, 0x57ff1780u, - 0x58018208u, 0x58057277u, 0x580c6e79u, 0x580ddf8cu, 0x581065eau, 0x5812b931u, - 0x5813e90cu, 0x58165f1cu, 0x581d6f68u, 0x5826426au, 0x582c6cb5u, 0x582d1257u, - 0x583c21f0u, 0x583f8afdu, 0x58454b4du, 0x584e7e54u, 0x584eb905u, 0x58503a66u, - 0x58541764u, 0x58576ecfu, 0x585b6509u, 0x585b6963u, 0x586806e8u, 0x58702acdu, - 0x58781df7u, 0x58782772u, 0x5878ec8bu, 0x587a48d9u, 0x5883c684u, 0x588db7a2u, - 0x588e318du, 0x588f00cbu, 0x58923e6du, 0x589b15c9u, 0x58a2fb27u, 0x58a6a8e4u, - 0x58a8f05du, 0x58b44814u, 0x58b50130u, 0x58b56bfbu, 0x58b6bd1cu, 0x58b7b2f0u, - 0x58b92205u, 0x58bb0caeu, 0x58bea81fu, 0x58c5656bu, 0x58c62e5au, 0x58d11220u, - 0x58d3c40fu, 0x58d9e1d9u, 0x58de4135u, 0x58dfcee8u, 0x58f983c0u, 0x58fa7f39u, - 0x58fbfaf5u, 0x58fd4b6cu, 0x5901fc77u, 0x5903bb0bu, 0x590ab6b8u, 0x5915ed16u, - 0x5917526eu, 0x5917fc15u, 0x592015d5u, 0x5926c513u, 0x5932ffeau, 0x5935e2bau, - 0x59467161u, 0x59529856u, 0x595aee9fu, 0x59659c05u, 0x5966dcb2u, 0x59778bc6u, - 0x5979d9bdu, 0x597c4339u, 0x597e0b9du, 0x5983ddfau, 0x5986687au, 0x598b287cu, - 0x59a06398u, 0x59b4753bu, 0x59b9ab94u, 0x59c84b67u, 0x59cafa2au, 0x59cc39b7u, - 0x59d4b9c2u, 0x59d5235fu, 0x59d5257eu, 0x59d97c38u, 0x59ec0d79u, 0x59ecb08fu, - 0x59ed5a36u, 0x59ee268au, 0x59f0f09eu, 0x59f3aafbu, 0x59f8f3f5u, 0x5a042723u, - 0x5a08ab7au, 0x5a0d65afu, 0x5a0ef990u, 0x5a142435u, 0x5a14f4adu, 0x5a1b5b86u, - 0x5a1f2121u, 0x5a202777u, 0x5a213b31u, 0x5a2573cau, 0x5a2b95b8u, 0x5a3a391fu, - 0x5a3fdb55u, 0x5a405b7cu, 0x5a43dc9bu, 0x5a475897u, 0x5a494077u, 0x5a4edb6bu, - 0x5a5d18a8u, 0x5a604354u, 0x5a637f81u, 0x5a63f7a2u, 0x5a756d99u, 0x5a76e45fu, - 0x5a7c7668u, 0x5a85fad3u, 0x5a8a7878u, 0x5a8de0eeu, 0x5a8e3719u, 0x5a93dd9au, - 0x5a9f0157u, 0x5a9f52a7u, 0x5aa63e20u, 0x5aa8e200u, 0x5aab1bcdu, 0x5aaf85ecu, - 0x5ab54359u, 0x5acacca7u, 0x5ad262acu, 0x5ad8f6b3u, 0x5adc1881u, 0x5af11317u, - 0x5af639d3u, 0x5af69bfau, 0x5af6d962u, 0x5afb87b5u, 0x5b034756u, 0x5b0eba44u, - 0x5b13a562u, 0x5b16a3b8u, 0x5b1f6b22u, 0x5b2201b5u, 0x5b23c4bfu, 0x5b281dbfu, - 0x5b290b8du, 0x5b3ae144u, 0x5b3ccea9u, 0x5b4164a5u, 0x5b4311a8u, 0x5b472199u, - 0x5b502a6eu, 0x5b513215u, 0x5b522233u, 0x5b5365c0u, 0x5b57c664u, 0x5b58a344u, - 0x5b58a3e1u, 0x5b5a8de0u, 0x5b5dbeaeu, 0x5b62e6ecu, 0x5b644b59u, 0x5b71deb4u, - 0x5b7821b3u, 0x5b79bb78u, 0x5b7e4677u, 0x5b822454u, 0x5b8550d7u, 0x5b868c78u, - 0x5b8c6ce4u, 0x5b90b873u, 0x5b953338u, 0x5b999f2eu, 0x5b9db108u, 0x5ba5ef68u, - 0x5ba62c64u, 0x5bad1fc9u, 0x5bbd6ba0u, 0x5bbf482eu, 0x5bc47914u, 0x5bc61931u, - 0x5bc6f6e4u, 0x5bcadda2u, 0x5bd3a66au, 0x5bda460bu, 0x5bdba743u, 0x5bdce814u, - 0x5bddcb82u, 0x5be1efc7u, 0x5be33098u, 0x5bfff551u, 0x5c0797f1u, 0x5c1be014u, - 0x5c1d08bfu, 0x5c2e422eu, 0x5c33f751u, 0x5c36f93au, 0x5c3a7d65u, 0x5c3ae5a1u, - 0x5c509e20u, 0x5c57a16au, 0x5c586520u, 0x5c5a5afcu, 0x5c6ea642u, 0x5c71d0dcu, - 0x5c83d860u, 0x5c929d39u, 0x5c947906u, 0x5c95c218u, 0x5c96f822u, 0x5ca03aa1u, - 0x5ca04335u, 0x5caec2d1u, 0x5cb38f1eu, 0x5cc2cedfu, 0x5cc8ff3du, 0x5cca48ecu, - 0x5ccca193u, 0x5ccd4107u, 0x5cd3d430u, 0x5cd49c4cu, 0x5cd573a7u, 0x5cd97abau, - 0x5ce2cbdbu, 0x5ce9201fu, 0x5cedc0e2u, 0x5d07c238u, 0x5d102b29u, 0x5d11bca4u, - 0x5d15e71cu, 0x5d1b3341u, 0x5d1df09cu, 0x5d288052u, 0x5d357da9u, 0x5d3b2b7bu, - 0x5d42169fu, 0x5d45b858u, 0x5d48cb4du, 0x5d4bdb60u, 0x5d5021a2u, 0x5d52631cu, - 0x5d7318a2u, 0x5d736902u, 0x5d742f88u, 0x5d75d0e8u, 0x5d7fb381u, 0x5d82fd61u, - 0x5d84729au, 0x5d8c288eu, 0x5d8c4b56u, 0x5d8e8200u, 0x5d8fca47u, 0x5d90ae2du, - 0x5d91e33eu, 0x5d95447cu, 0x5d9566f1u, 0x5d9a614eu, 0x5d9fbd05u, 0x5da802dbu, - 0x5da84cb5u, 0x5db061a2u, 0x5db4d529u, 0x5db8988cu, 0x5dbb3d6eu, 0x5dc15ea4u, - 0x5dc22d2fu, 0x5dc5850eu, 0x5dc85a0eu, 0x5dca5090u, 0x5dcd03e6u, 0x5dcefff5u, - 0x5dd60bf8u, 0x5dd91d7cu, 0x5ddf3eb2u, 0x5de20319u, 0x5df38d0du, 0x5dfadfbeu, - 0x5dfdbaf1u, 0x5e01d688u, 0x5e05d196u, 0x5e0912c0u, 0x5e09eb75u, 0x5e0e7540u, - 0x5e14370cu, 0x5e154837u, 0x5e156336u, 0x5e21262bu, 0x5e21d4a2u, 0x5e3ce82du, - 0x5e40aed3u, 0x5e51ee04u, 0x5e66f5adu, 0x5e6c3a3eu, 0x5e705c40u, 0x5e780023u, - 0x5e80737eu, 0x5e81daf0u, 0x5e875188u, 0x5e8e1b3eu, 0x5e913e44u, 0x5e94069bu, - 0x5e9b8f9du, 0x5e9bd009u, 0x5e9d0293u, 0x5ea0e63fu, 0x5ead30b7u, 0x5eaf90b6u, - 0x5eb45694u, 0x5eb5861fu, 0x5ebe2fa9u, 0x5ebee7bbu, 0x5ec13141u, 0x5ec1cdc5u, - 0x5ed788fau, 0x5ed85270u, 0x5ee3ec01u, 0x5ee8ad68u, 0x5eeb942eu, 0x5efdc704u, - 0x5efddaabu, 0x5f0d848bu, 0x5f163999u, 0x5f176f04u, 0x5f23a312u, 0x5f27d725u, - 0x5f2d7422u, 0x5f37f3e5u, 0x5f3980b1u, 0x5f3ce2fau, 0x5f3d9a50u, 0x5f40bd47u, - 0x5f412b61u, 0x5f44a25cu, 0x5f485ef6u, 0x5f4a481au, 0x5f508313u, 0x5f51ac2fu, - 0x5f51b135u, 0x5f53e0a5u, 0x5f61bd4du, 0x5f671971u, 0x5f72dc08u, 0x5f75ff5du, - 0x5f792865u, 0x5f84d26du, 0x5f87779eu, 0x5f97af30u, 0x5f9af1a1u, 0x5fa0983bu, - 0x5fa3a8d4u, 0x5fae9323u, 0x5fbd3856u, 0x5fbd4590u, 0x5fc986f8u, 0x5fca7e6bu, - 0x5fcb58e7u, 0x5fd050aeu, 0x5fd59ce6u, 0x5fd791ccu, 0x5fd9e3d5u, 0x5fdd5f87u, - 0x5fe282c8u, 0x5fe5d244u, 0x5fe69b5cu, 0x5fe81da0u, 0x5fe9a548u, 0x5fe9bfbcu, - 0x5ff86067u, 0x5ffaabc3u, 0x5ffbd4dfu, 0x60019b8fu, 0x6002fa20u, 0x600486ddu, - 0x6005d749u, 0x600b036bu, 0x600be784u, 0x601b32d8u, 0x601c3eafu, 0x601e3327u, - 0x602066c1u, 0x6024353du, 0x60290e37u, 0x602b12bau, 0x60305d4bu, 0x603a5d52u, - 0x603ce929u, 0x60442532u, 0x6051e3c9u, 0x6055c746u, 0x6055e48bu, 0x605791fbu, - 0x60635397u, 0x6064d55eu, 0x6068b629u, 0x607035dcu, 0x60818eb3u, 0x608b2f43u, - 0x6099f5b6u, 0x609cdcabu, 0x60b3df3cu, 0x60b5110au, 0x60b7612du, 0x60b77fcbu, - 0x60b8050au, 0x60bee382u, 0x60cc1552u, 0x60d9cb6eu, 0x60dbeacbu, 0x60de0606u, - 0x60df156au, 0x60e07525u, 0x60e0c7c0u, 0x60e1d2cbu, 0x60e352dbu, 0x60f92e65u, - 0x60fd84c2u, 0x6101ebafu, 0x6109161du, 0x610e7212u, 0x61194ff7u, 0x611e07adu, - 0x6123235eu, 0x612b8759u, 0x612dd201u, 0x612f290au, 0x613233bau, 0x613558deu, - 0x6143777du, 0x6147b790u, 0x614f4e7cu, 0x61517fd2u, 0x61564524u, 0x6160deceu, - 0x61671f42u, 0x616ac54cu, 0x616c8f0eu, 0x616c9a33u, 0x616d2a68u, 0x61710c8cu, - 0x6178ee50u, 0x6181b26au, 0x6188fc69u, 0x618aae8cu, 0x618befd8u, 0x619dcb31u, - 0x61a16889u, 0x61a292c7u, 0x61a3722du, 0x61a3f313u, 0x61bb3586u, 0x61c8d15du, - 0x61dfd93au, 0x61e0216cu, 0x61e5fba1u, 0x61ea081bu, 0x61f6cc4bu, 0x61fde4c6u, - 0x61fe44cau, 0x61fefacbu, 0x6205a836u, 0x620cd122u, 0x6214117eu, 0x62204a03u, - 0x62207e2du, 0x6221c0cdu, 0x622964c3u, 0x6229bbb4u, 0x622ee684u, 0x6230b652u, - 0x62327a99u, 0x6233a324u, 0x6235f2b6u, 0x623a1cacu, 0x623d7110u, 0x62438274u, - 0x6243fe40u, 0x6249d690u, 0x624b4ae5u, 0x6253168cu, 0x6259e42fu, 0x625a0fa7u, - 0x6262326fu, 0x6266fb58u, 0x62756bcfu, 0x6282386au, 0x628372e9u, 0x6283c581u, - 0x628ce9b4u, 0x629b9f88u, 0x62afee5cu, 0x62b387bcu, 0x62b4157cu, 0x62c7ca47u, - 0x62cc5532u, 0x62cd2304u, 0x62d0c87du, 0x62d0e292u, 0x62d1fe12u, 0x62df6f93u, - 0x62e17661u, 0x62e18e62u, 0x62e2de9du, 0x62e91145u, 0x62f23a32u, 0x62ff3e75u, - 0x62ffdd07u, 0x63017301u, 0x6301d775u, 0x63185185u, 0x631de81cu, 0x631e7fb0u, - 0x632def1du, 0x6338a355u, 0x633d2270u, 0x63435683u, 0x63538bc6u, 0x6359b9f1u, - 0x63610c8au, 0x6364e24du, 0x6366f5ddu, 0x636ddca3u, 0x637c0334u, 0x637d4787u, - 0x637f9d86u, 0x6382813au, 0x638530bdu, 0x638daa94u, 0x639189beu, 0x639439c0u, - 0x6398fc48u, 0x639ff73bu, 0x63a69a94u, 0x63a7baf2u, 0x63aad6bbu, 0x63b16084u, - 0x63b59454u, 0x63b5d15au, 0x63b62012u, 0x63bcfa89u, 0x63c04df5u, 0x63cff082u, - 0x63d05367u, 0x63d1c8f6u, 0x63d9b317u, 0x63dc231fu, 0x63ecb455u, 0x63ed3a31u, - 0x63f0f6efu, 0x63f0f9e3u, 0x63f3b82au, 0x6404da9au, 0x64088f74u, 0x641840c5u, - 0x641b13b8u, 0x641bbdfcu, 0x6428692bu, 0x642956e2u, 0x6429cb82u, 0x642b0503u, - 0x642e082eu, 0x642eee80u, 0x642f9165u, 0x643bff91u, 0x644c9e14u, 0x644ffa56u, - 0x64542c30u, 0x6456c805u, 0x645b013eu, 0x64624409u, 0x64653582u, 0x64692ec1u, - 0x646bc03du, 0x646d7e1du, 0x6471f7cfu, 0x647dc11du, 0x6481b206u, 0x64862513u, - 0x6487500eu, 0x6493042eu, 0x649601f3u, 0x64982a5eu, 0x6498ed02u, 0x64993eb4u, - 0x64a17c80u, 0x64aa79f9u, 0x64ad509du, 0x64b1b233u, 0x64ba8420u, 0x64bacc39u, - 0x64c3a0d9u, 0x64c44aa4u, 0x64c55b49u, 0x64c58890u, 0x64c5b947u, 0x64d50301u, - 0x64d51478u, 0x64dbc695u, 0x64de4719u, 0x64e01bdcu, 0x64ea9681u, 0x64f8eed3u, - 0x65012642u, 0x6503d938u, 0x65060a03u, 0x650875dbu, 0x6508d202u, 0x650a0b61u, - 0x650b4697u, 0x650e06f6u, 0x6515735au, 0x651b30ffu, 0x6523acccu, 0x6523c40cu, - 0x65284ed8u, 0x65291a39u, 0x652a5a7bu, 0x652ec0dcu, 0x65324322u, 0x65340045u, - 0x6556e704u, 0x6557cb1fu, 0x65594848u, 0x655b869bu, 0x655bcf71u, 0x655e2d83u, - 0x6564dcacu, 0x656851c7u, 0x657691bfu, 0x6585c19bu, 0x658655ecu, 0x658aa17bu, - 0x658dbf7bu, 0x65926568u, 0x65a70070u, 0x65b87d0au, 0x65bc8d9au, 0x65c3a5c6u, - 0x65c46652u, 0x65c769eeu, 0x65c99cc0u, 0x65cf9164u, 0x65cfefc2u, 0x65d3d29du, - 0x65d69e0fu, 0x65d87c8au, 0x65e1324au, 0x65e46828u, 0x65e712e0u, 0x65ed9111u, - 0x65f92f4cu, 0x65fd2fceu, 0x65fd6153u, 0x66056d2cu, 0x66062edcu, 0x6613954du, - 0x66154aefu, 0x661a36beu, 0x661e1d4au, 0x661e9643u, 0x66204615u, 0x662206e9u, - 0x6625e613u, 0x662a5387u, 0x662d41eau, 0x662d87ccu, 0x662fb154u, 0x663beb91u, - 0x66402531u, 0x6643e3d8u, 0x6644f7dfu, 0x664bcde1u, 0x664e7707u, 0x665d1764u, - 0x665d3b61u, 0x665ed5e2u, 0x6667d2c7u, 0x6667e35bu, 0x6667ff39u, 0x666a1a15u, - 0x666ebc1au, 0x6671edbau, 0x667513c1u, 0x667735ffu, 0x6678d1abu, 0x667bc758u, - 0x6683bda9u, 0x66862167u, 0x669dd893u, 0x66a4b052u, 0x66a61be7u, 0x66aa1b94u, - 0x66ac967fu, 0x66af48efu, 0x66b373acu, 0x66b82af6u, 0x66be6a64u, 0x66c1be38u, - 0x66c85477u, 0x66c876abu, 0x66c9986au, 0x66ce0c34u, 0x66cfc738u, 0x66d3040au, - 0x66e187ccu, 0x66e35470u, 0x66e3fc61u, 0x66e7373fu, 0x66eaaa96u, 0x66eddd2au, - 0x66efc469u, 0x66f209cfu, 0x66fe6e10u, 0x6703dc3eu, 0x67053921u, 0x67058c41u, - 0x670860aeu, 0x6709812au, 0x67194fbbu, 0x671afd4au, 0x67212873u, 0x67279ac1u, - 0x673bf361u, 0x67408c1cu, 0x67474af1u, 0x67497d80u, 0x674b447fu, 0x674dd7e6u, - 0x67501ec0u, 0x6750c6b7u, 0x675519c7u, 0x675a5229u, 0x676bdea6u, 0x676c80feu, - 0x67769170u, 0x677cb34fu, 0x678baca6u, 0x67957a18u, 0x6797ddd0u, 0x679cdd39u, - 0x679e4657u, 0x679fcefeu, 0x679fe82bu, 0x67a1a69du, 0x67ac1955u, 0x67b24bd4u, - 0x67b35b36u, 0x67b57c6bu, 0x67b76895u, 0x67bf844du, 0x67c37c67u, 0x68069313u, - 0x6809bf45u, 0x681a38f8u, 0x681b53d8u, 0x681de67fu, 0x681f9f12u, 0x68260371u, - 0x682a6b46u, 0x682c8b04u, 0x682fcf2cu, 0x6830bd4eu, 0x6831cdeau, 0x6841d996u, - 0x684210d8u, 0x68456811u, 0x68481b3eu, 0x6853121cu, 0x685652f6u, 0x685a936fu, - 0x685c3c1cu, 0x685fc247u, 0x686b0743u, 0x686dc2fdu, 0x686e2f4du, 0x68745c6au, - 0x687656e0u, 0x6876d239u, 0x68778e7bu, 0x687a6653u, 0x687f7274u, 0x68886a35u, - 0x688dbd1eu, 0x6890a961u, 0x689d4b20u, 0x68a15a93u, 0x68ba1e13u, 0x68bd224cu, - 0x68c14e5au, 0x68c17981u, 0x68c77133u, 0x68d33998u, 0x68da82afu, 0x68ddbe0au, - 0x68e47693u, 0x68ef344bu, 0x68f21dddu, 0x68f3180au, 0x68fce6f3u, 0x68ff95d8u, - 0x69077132u, 0x690adde0u, 0x690bd6f3u, 0x6911aff6u, 0x69161b66u, 0x69167f46u, - 0x6918c521u, 0x691ac3bfu, 0x691cee86u, 0x692cb196u, 0x69303528u, 0x69360da6u, - 0x69365c21u, 0x693bb96eu, 0x693c77b4u, 0x693f8ea1u, 0x69401fb4u, 0x694ca02bu, - 0x6954967bu, 0x69549badu, 0x6956bc95u, 0x6958021fu, 0x6958b798u, 0x6965a7d2u, - 0x69671351u, 0x696713d6u, 0x6969be93u, 0x69709b05u, 0x697f1781u, 0x6982bbdcu, - 0x6984e06fu, 0x698526a6u, 0x6986ffd0u, 0x69947e8cu, 0x699a2b5eu, 0x699ba991u, - 0x699e4e0au, 0x699f0383u, 0x69a29df9u, 0x69a3fa57u, 0x69abb565u, 0x69ac35d0u, - 0x69b8295cu, 0x69bf9d2du, 0x69c4dee1u, 0x69c7ffa1u, 0x69dd0567u, 0x69ea3c65u, - 0x69efc234u, 0x69efe723u, 0x69f33d76u, 0x6a13d699u, 0x6a1c65c9u, 0x6a21680du, - 0x6a2d829cu, 0x6a2f0258u, 0x6a30db03u, 0x6a3540c4u, 0x6a3b79f8u, 0x6a3e99b2u, - 0x6a426481u, 0x6a474471u, 0x6a4db760u, 0x6a56a320u, 0x6a58996au, 0x6a63c6ddu, - 0x6a6a7de7u, 0x6a723230u, 0x6a755dddu, 0x6a7bc132u, 0x6a7d7e4eu, 0x6a80736au, - 0x6a88f93eu, 0x6a89c16fu, 0x6a8c830au, 0x6a90ca1eu, 0x6a90f75bu, 0x6a92ba2cu, - 0x6a94eccau, 0x6a9a7c7du, 0x6a9b09ecu, 0x6a9ef57cu, 0x6aa7a912u, 0x6aac0c38u, - 0x6aae4d66u, 0x6abd4284u, 0x6abe9abeu, 0x6ac5293au, 0x6ad54d7fu, 0x6ade7ac1u, - 0x6aeeff37u, 0x6af3fbc4u, 0x6af6502du, 0x6af721bau, 0x6af733edu, 0x6af907e9u, - 0x6b02e1d3u, 0x6b0475d9u, 0x6b103a15u, 0x6b12d855u, 0x6b1a4988u, 0x6b243d6du, - 0x6b26859fu, 0x6b2d128du, 0x6b2e7ecbu, 0x6b3510ecu, 0x6b380e55u, 0x6b38e8b9u, - 0x6b4ac397u, 0x6b4bf6a3u, 0x6b542411u, 0x6b583bd3u, 0x6b5c3461u, 0x6b5d00bbu, - 0x6b605793u, 0x6b60f6b0u, 0x6b6195cdu, 0x6b6234eau, 0x6b64b373u, 0x6b67548fu, - 0x6b6b875bu, 0x6b6bc922u, 0x6b82da49u, 0x6b87880eu, 0x6b88ae5du, 0x6b8e63c3u, - 0x6b8e82b3u, 0x6b942285u, 0x6b9b7933u, 0x6b9c1aa7u, 0x6ba0c05du, 0x6ba6c107u, - 0x6ba9445fu, 0x6bb2c8fdu, 0x6bb802e4u, 0x6bbf0660u, 0x6bc3b0c4u, 0x6bc620bdu, - 0x6bcb0e77u, 0x6bcff07eu, 0x6bd2488cu, 0x6bd70c28u, 0x6bd9ed7cu, 0x6be2794bu, - 0x6be3a233u, 0x6be8b06du, 0x6becf038u, 0x6bedb931u, 0x6bedefcau, 0x6bf1edceu, - 0x6bf45b2eu, 0x6bf9a4d0u, 0x6c0fb007u, 0x6c1f131fu, 0x6c20e2f1u, 0x6c2d10dcu, - 0x6c3867a3u, 0x6c3b106bu, 0x6c3ca29fu, 0x6c3f3146u, 0x6c41050fu, 0x6c4567e3u, - 0x6c52fd80u, 0x6c534b7fu, 0x6c5878a9u, 0x6c58bcc7u, 0x6c622bf2u, 0x6c6ec157u, - 0x6c7055e2u, 0x6c70fa32u, 0x6c759284u, 0x6c7cb726u, 0x6c7de0d6u, 0x6c7f45cdu, - 0x6c81ca67u, 0x6c839ee7u, 0x6c873138u, 0x6c87d3aeu, 0x6c8a028fu, 0x6c9615a2u, - 0x6ca60bf2u, 0x6ca8b3eau, 0x6cb27a36u, 0x6cb63cd2u, 0x6cb6c9bcu, 0x6cc4d632u, - 0x6cce84b4u, 0x6ccf4e6cu, 0x6cd1ec1fu, 0x6cd2f182u, 0x6cd87a7bu, 0x6cde8ea5u, - 0x6ce11199u, 0x6ce35f30u, 0x6cee754bu, 0x6cf198cau, 0x6cf27315u, 0x6cf4f2e2u, - 0x6cfbe7d7u, 0x6d078f28u, 0x6d0a5cbeu, 0x6d0bbceeu, 0x6d0ef2a0u, 0x6d17d35au, - 0x6d225214u, 0x6d2e9ba3u, 0x6d346442u, 0x6d38dc25u, 0x6d41495fu, 0x6d429a83u, - 0x6d44a42eu, 0x6d4b5210u, 0x6d556dddu, 0x6d58adedu, 0x6d616958u, 0x6d699414u, - 0x6d6a3777u, 0x6d6a9e0cu, 0x6d6bf000u, 0x6d6bf71eu, 0x6d6e00e2u, 0x6d701e47u, - 0x6d76b478u, 0x6d7c8c8bu, 0x6d7e66bbu, 0x6d821e7cu, 0x6d84900fu, 0x6d84f03du, - 0x6d86dfc7u, 0x6d966934u, 0x6d99e8eau, 0x6d9ab45bu, 0x6da19f3du, 0x6da763feu, - 0x6da7c42cu, 0x6daaafdau, 0x6dad5c31u, 0x6daf0d34u, 0x6db5d397u, 0x6db81513u, - 0x6dbcde18u, 0x6dbd2096u, 0x6dbdcef3u, 0x6dc40b10u, 0x6dcdfa21u, 0x6dcef7ccu, - 0x6dd649fau, 0x6de36e55u, 0x6de5eb31u, 0x6df82813u, 0x6df84ee9u, 0x6dfaff37u, - 0x6e0134f3u, 0x6e01b953u, 0x6e088ffau, 0x6e09203fu, 0x6e0c9973u, 0x6e145fbdu, - 0x6e193118u, 0x6e1c6a59u, 0x6e1e657au, 0x6e2310cfu, 0x6e28ce24u, 0x6e2b97ddu, - 0x6e3e817bu, 0x6e45eb5au, 0x6e507db3u, 0x6e63491fu, 0x6e6c6860u, 0x6e7a4988u, - 0x6e83d9aau, 0x6e85f9b3u, 0x6e8c3cadu, 0x6e8e3b6au, 0x6e917450u, 0x6e92f5b1u, - 0x6e95c6f0u, 0x6e95e8b1u, 0x6e99470eu, 0x6e9e861du, 0x6ea02c59u, 0x6ea2bb1au, - 0x6eac671eu, 0x6eb03837u, 0x6eb414dau, 0x6eb465d5u, 0x6eb66151u, 0x6ebbab75u, - 0x6ec1a0c2u, 0x6ec55ee5u, 0x6ec73ccbu, 0x6ed00a4eu, 0x6edb511eu, 0x6ede10b9u, - 0x6ee79001u, 0x6ee98eb1u, 0x6ef828c9u, 0x6efc5326u, 0x6f0cba64u, 0x6f11e3b1u, - 0x6f14ead9u, 0x6f1a3781u, 0x6f282e85u, 0x6f2df346u, 0x6f3927c9u, 0x6f47c73eu, - 0x6f4928e0u, 0x6f4bd1e9u, 0x6f4ea39du, 0x6f5fd221u, 0x6f62d608u, 0x6f71c32au, - 0x6f7319b7u, 0x6f736771u, 0x6f86d087u, 0x6f885341u, 0x6f939ed5u, 0x6f9529d5u, - 0x6f989d75u, 0x6fa2c4a2u, 0x6fa427ebu, 0x6fb36113u, 0x6fcec03fu, 0x6fcf1471u, - 0x6fd00e45u, 0x6fd44c89u, 0x6fda7514u, 0x6fdc3139u, 0x6fdd60cfu, 0x6ff8184au, - 0x6ffaa3b0u, 0x6ffb6440u, 0x6ffe35dau, 0x70067234u, 0x700f3f38u, 0x701b65fcu, - 0x701fc7d4u, 0x704108c3u, 0x705e4721u, 0x705eb110u, 0x7064695bu, 0x7064e645u, - 0x70670567u, 0x7067c21au, 0x706b8f8au, 0x707d6e05u, 0x70805511u, 0x70888fe8u, - 0x7098a138u, 0x709d505du, 0x70a987c7u, 0x70aa6311u, 0x70b47e95u, 0x70b58b94u, - 0x70c70d5au, 0x70d533cfu, 0x70e530b6u, 0x70e9240fu, 0x70ef13d0u, 0x70ef5245u, - 0x70fa8832u, 0x70fdb547u, 0x7100c2f3u, 0x71043a86u, 0x710ab016u, 0x710d3cddu, - 0x71138e22u, 0x7115df73u, 0x71182dc5u, 0x711c4569u, 0x711dd738u, 0x712301a9u, - 0x712e661cu, 0x71306b8eu, 0x7132fe3bu, 0x7136cfa9u, 0x713c5bf3u, 0x713ca335u, - 0x713d09abu, 0x7143981cu, 0x7148f9bbu, 0x714af4ffu, 0x714de7e7u, 0x714e8bebu, - 0x715f5077u, 0x7160d6cfu, 0x71635913u, 0x716bfba9u, 0x716e828au, 0x7174e2efu, - 0x7178aef1u, 0x717b5d04u, 0x718a8d38u, 0x718b0f82u, 0x7198b003u, 0x719a49abu, - 0x719b2b18u, 0x719fd00fu, 0x71a59ca9u, 0x71a61bc3u, 0x71a83430u, 0x71a9eec3u, - 0x71ac6ccbu, 0x71b2cf48u, 0x71bc5e9du, 0x71bc5f6cu, 0x71c17644u, 0x71c242cfu, - 0x71c9cb69u, 0x71cda15cu, 0x71cdad28u, 0x71d52fdcu, 0x71d69e53u, 0x71dd6b82u, - 0x71e2ca3au, 0x71e7cc6du, 0x71ee6df1u, 0x71f02a6bu, 0x71f560e5u, 0x71f781eeu, - 0x71f9816eu, 0x71fa189au, 0x71fd17e0u, 0x72012147u, 0x7201272du, 0x7201bd4du, - 0x7205d82du, 0x72066e4du, 0x721195e9u, 0x721219cbu, 0x72129a34u, 0x721b1811u, - 0x7224dcb9u, 0x722759afu, 0x723248b6u, 0x723a5644u, 0x72457320u, 0x7259c8fbu, - 0x725e2bf8u, 0x72615732u, 0x7266c0e7u, 0x726b361cu, 0x726d71edu, 0x726ff262u, - 0x727a7c11u, 0x72829c59u, 0x7285a9cfu, 0x7285dbefu, 0x728e269au, 0x728f1a05u, - 0x72906044u, 0x7297840cu, 0x72a20169u, 0x72a33e46u, 0x72ab4ca0u, 0x72aeabd8u, - 0x72b0d5b1u, 0x72bb29c7u, 0x72be7c64u, 0x72c37a9bu, 0x72c4a3a2u, 0x72c9892fu, - 0x72d2678du, 0x72d5b378u, 0x72d791feu, 0x72daa4d1u, 0x72e1210au, 0x72e58ce0u, - 0x72edde80u, 0x72f039f3u, 0x72f67998u, 0x72f8a9e0u, 0x72f91a78u, 0x7301c55au, - 0x7308ce51u, 0x7315478fu, 0x7316143du, 0x731d08b2u, 0x731e8cbdu, 0x73367100u, - 0x73381266u, 0x73402c46u, 0x7350000bu, 0x7352fc35u, 0x735ad668u, 0x7362fec1u, - 0x73646e6du, 0x73722198u, 0x73730c2au, 0x737f6b00u, 0x737fc77cu, 0x73838128u, - 0x738d30f8u, 0x738e9dd4u, 0x73981b1bu, 0x739a54a5u, 0x739b8d3au, 0x739cbd7fu, - 0x73a11984u, 0x73a63f44u, 0x73a6a96du, 0x73a71554u, 0x73b152c1u, 0x73b4636du, - 0x73bdb93bu, 0x73c28796u, 0x73c36099u, 0x73c77caeu, 0x73c8f3cbu, 0x73cbc859u, - 0x73cfccf9u, 0x73cfd8a2u, 0x73d06abdu, 0x73d0d5f8u, 0x73d27590u, 0x73d3c11cu, - 0x73d64a67u, 0x73ef6136u, 0x73eff6aau, 0x73f2fdc1u, 0x73f8d43cu, 0x7407bf9fu, - 0x740820efu, 0x740cf23du, 0x7411e64bu, 0x74139ba2u, 0x7414e24au, 0x74157c92u, - 0x741819b3u, 0x741a46f0u, 0x741ab00fu, 0x741d38aeu, 0x741ff116u, 0x7420dac9u, - 0x742493d0u, 0x742e728au, 0x743016b0u, 0x7430cadfu, 0x74316799u, 0x74344238u, - 0x74355d47u, 0x743ecc48u, 0x7451f982u, 0x7457d504u, 0x74642116u, 0x7478db01u, - 0x747cb751u, 0x7485b794u, 0x748676e3u, 0x7486bf56u, 0x748a2632u, 0x748c1138u, - 0x748e5908u, 0x7493cd7fu, 0x74b007beu, 0x74b41030u, 0x74b6a9aau, 0x74b772b1u, - 0x74b99c47u, 0x74badfcdu, 0x74bdc2f3u, 0x74c7d9ceu, 0x74cb32c2u, 0x74cfceb7u, - 0x74e09f1eu, 0x74e2384fu, 0x74e3ee00u, 0x74e5751du, 0x74e72c32u, 0x74fdd25cu, - 0x7502211eu, 0x7505987bu, 0x75174aefu, 0x7518bb78u, 0x7518cf46u, 0x751c15d0u, - 0x75260401u, 0x75325d47u, 0x753398a4u, 0x75346180u, 0x7534ec55u, 0x75372161u, - 0x753a01ecu, 0x754905b4u, 0x75498995u, 0x7556701eu, 0x75605fb3u, 0x7563406bu, - 0x756cf6d5u, 0x757c6eafu, 0x75807eb8u, 0x7583857du, 0x7589c8e3u, 0x758a1557u, - 0x758d38c7u, 0x758e199du, 0x758e6ac2u, 0x7593400fu, 0x7598be13u, 0x75ae7bc1u, - 0x75b2fd7bu, 0x75be5a77u, 0x75c8feedu, 0x75c9acaeu, 0x75ce110au, 0x75d934b0u, - 0x75dd4b08u, 0x75e0a8dau, 0x75e7c146u, 0x75eea37cu, 0x75fa60e7u, 0x76063162u, - 0x760b4f50u, 0x760bcb49u, 0x760e1e91u, 0x760e8c77u, 0x76121f74u, 0x76132c99u, - 0x7618ea8eu, 0x761913bcu, 0x7623ee3bu, 0x762b1e1cu, 0x76315b9au, 0x76342856u, - 0x76390254u, 0x76403ae5u, 0x76404153u, 0x7642b2aau, 0x76462b11u, 0x765bc364u, - 0x765d7090u, 0x765f1ac8u, 0x765fcb24u, 0x7664d07cu, 0x766633e6u, 0x7673ff9eu, - 0x76745c67u, 0x7678fa73u, 0x76831ef9u, 0x768a330du, 0x768c2477u, 0x768cf8ecu, - 0x769eb6fau, 0x769ff0e8u, 0x76a19685u, 0x76a95edeu, 0x76adc766u, 0x76b7d164u, - 0x76b8acabu, 0x76ba5aedu, 0x76bbf498u, 0x76c18e10u, 0x76c792d3u, 0x76cbe38du, - 0x76d832d6u, 0x76d9728bu, 0x76dc6b53u, 0x76dea9c1u, 0x76e06a0au, 0x76e87028u, - 0x76e92982u, 0x76ea347bu, 0x76ea9659u, 0x76ec5a48u, 0x76ed867cu, 0x76f02842u, - 0x76f4541bu, 0x76fe7877u, 0x7700c3a8u, 0x770c04ddu, 0x77152eb1u, 0x77179fafu, - 0x771809e4u, 0x771a9708u, 0x7728cabeu, 0x772a3d03u, 0x773272d3u, 0x77358f66u, - 0x7748a2e1u, 0x774e090cu, 0x774fb691u, 0x77501affu, 0x775433bbu, 0x775480dbu, - 0x77554422u, 0x77589ed3u, 0x775953d5u, 0x775c0234u, 0x775c0ae4u, 0x775cb620u, - 0x7761eb83u, 0x77625139u, 0x7763f136u, 0x776a43fdu, 0x7775a35du, 0x777a7ad6u, - 0x7780a8bfu, 0x7784f53au, 0x778982d5u, 0x778a51eau, 0x77982100u, 0x779b67d7u, - 0x779da9aeu, 0x77a16d25u, 0x77a3322au, 0x77a5fb4bu, 0x77b3b39eu, 0x77b467e3u, - 0x77c2ddddu, 0x77c84324u, 0x77ca0998u, 0x77ca3157u, 0x77d6e5a7u, 0x77db4744u, - 0x77dfd87au, 0x77e1d4d9u, 0x77e258fau, 0x77e333e8u, 0x77e4333au, 0x77e80142u, - 0x77ee2e4fu, 0x77f7a28cu, 0x77fc0c35u, 0x78061c32u, 0x7807fb23u, 0x781097cdu, - 0x781763cbu, 0x782500aau, 0x782d5f04u, 0x782ef3e5u, 0x7833741fu, 0x7839e543u, - 0x783ea4fcu, 0x783ea825u, 0x784bbacau, 0x784d06cau, 0x784e4ec4u, 0x784f174eu, - 0x785da24au, 0x786cfed9u, 0x786d537au, 0x7873d2e3u, 0x7875bdedu, 0x787730efu, - 0x78774dcbu, 0x7879dd6du, 0x7879f140u, 0x787b69e9u, 0x787d8adeu, 0x78890216u, - 0x788b92ceu, 0x78962e55u, 0x789d87cfu, 0x78a63649u, 0x78aa22f7u, 0x78aa4271u, - 0x78b1a804u, 0x78bcde0du, 0x78c4346eu, 0x78c5053du, 0x78c847d5u, 0x78c95920u, - 0x78ccd408u, 0x78d08ca5u, 0x78d13ee7u, 0x78d151cdu, 0x78dd5c07u, 0x78e4c48au, - 0x78e6dedbu, 0x78e74639u, 0x78e7571cu, 0x78ec053eu, 0x78ec524eu, 0x78ed9dfcu, - 0x78f98abfu, 0x78ff33f4u, 0x790276a4u, 0x7905b023u, 0x790af6c4u, 0x7911b5bcu, - 0x79163797u, 0x79168211u, 0x791691a5u, 0x791a567cu, 0x791d3d24u, 0x79216c28u, - 0x7924f3ddu, 0x79251c3fu, 0x793d690bu, 0x793dedb7u, 0x79435080u, 0x794617aeu, - 0x7948dc3eu, 0x794efa00u, 0x7952a95bu, 0x795e5aa5u, 0x79706e6cu, 0x797adbd2u, - 0x7980fe20u, 0x798b8514u, 0x798df029u, 0x7991480cu, 0x799cd945u, 0x799dec9fu, - 0x799e7a02u, 0x799e8903u, 0x79a2032fu, 0x79a8183au, 0x79b27bfcu, 0x79b3c6bdu, - 0x79c155e6u, 0x79c7f5beu, 0x79c9e008u, 0x79cb07c4u, 0x79cf6166u, 0x79d1b10fu, - 0x79d1bda7u, 0x79d4a74bu, 0x79d684ccu, 0x79d84ac6u, 0x79da5906u, 0x79db97ebu, - 0x79ddda69u, 0x79e274b8u, 0x79e5db3eu, 0x79e5ffbfu, 0x79e8fca7u, 0x79ed9e2du, - 0x79f1cd31u, 0x79f207eeu, 0x79f7a048u, 0x79faa6a3u, 0x7a07866du, 0x7a091a30u, - 0x7a0b190du, 0x7a1597dbu, 0x7a16d33bu, 0x7a16dad4u, 0x7a1ab763u, 0x7a1dd663u, - 0x7a210c5du, 0x7a23c5d3u, 0x7a2abd7du, 0x7a2ec713u, 0x7a318321u, 0x7a3863d1u, - 0x7a3d9fe3u, 0x7a41adbcu, 0x7a43fbb8u, 0x7a4b2715u, 0x7a583bc5u, 0x7a5e0683u, - 0x7a68fd34u, 0x7a6bf900u, 0x7a6cdfb1u, 0x7a6e8393u, 0x7a71e636u, 0x7a791d9eu, - 0x7a7a7733u, 0x7a7d0698u, 0x7a852823u, 0x7a8b428fu, 0x7a8e22eeu, 0x7a90900bu, - 0x7a933f7fu, 0x7a956fd5u, 0x7a9bd902u, 0x7a9c546au, 0x7a9d3e72u, 0x7ab4949au, - 0x7ab6f63au, 0x7abb1cb8u, 0x7ac048dau, 0x7acca589u, 0x7acf5352u, 0x7ad1980au, - 0x7ad19a73u, 0x7ae04a1fu, 0x7aeb9d76u, 0x7aed2fbeu, 0x7aefa8b7u, 0x7af9520fu, - 0x7b0643a4u, 0x7b07e8d2u, 0x7b0945bcu, 0x7b0a3832u, 0x7b0bc11eu, 0x7b14d033u, - 0x7b17857du, 0x7b17e666u, 0x7b1a2aefu, 0x7b1e5fe1u, 0x7b1e8b3du, 0x7b2474beu, - 0x7b2b89b0u, 0x7b34cef8u, 0x7b374f5bu, 0x7b383654u, 0x7b38412bu, 0x7b3fc166u, - 0x7b4155e7u, 0x7b42e066u, 0x7b4639f4u, 0x7b46aa3eu, 0x7b4a578du, 0x7b5130a5u, - 0x7b54df28u, 0x7b65351fu, 0x7b673c9au, 0x7b7016c8u, 0x7b713791u, 0x7b7a3745u, - 0x7b7d825fu, 0x7b83a6b8u, 0x7b860ac1u, 0x7b8b2752u, 0x7b93c4d1u, 0x7b94e4f4u, - 0x7b964ff3u, 0x7b999e4bu, 0x7ba71bdbu, 0x7ba91eb4u, 0x7bab30eeu, 0x7baeb136u, - 0x7bb25792u, 0x7bb76ce9u, 0x7bb97fd5u, 0x7bc36676u, 0x7bca1cb8u, 0x7bce245cu, - 0x7bd07118u, 0x7bd1f1d1u, 0x7bd44574u, 0x7bd550e2u, 0x7bdd6c41u, 0x7bdec634u, - 0x7be33b37u, 0x7be756deu, 0x7bef74d5u, 0x7bf20836u, 0x7bf9ac05u, 0x7bfa4869u, - 0x7bfc9c0cu, 0x7bfd2368u, 0x7bfefa86u, 0x7c052183u, 0x7c076b48u, 0x7c0c15d8u, - 0x7c0cec65u, 0x7c1c4410u, 0x7c244770u, 0x7c2e6a3eu, 0x7c2f04f7u, 0x7c3065d1u, - 0x7c3436e3u, 0x7c361a83u, 0x7c3b97aau, 0x7c3e9b0eu, 0x7c42e072u, 0x7c46310fu, - 0x7c46ec53u, 0x7c4c7940u, 0x7c4e7387u, 0x7c4eacb1u, 0x7c51e456u, 0x7c5da023u, - 0x7c5de76eu, 0x7c5df7e0u, 0x7c60d08eu, 0x7c61027eu, 0x7c616175u, 0x7c621189u, - 0x7c625b78u, 0x7c650580u, 0x7c660d51u, 0x7c66cc8bu, 0x7c6904e5u, 0x7c6ffbdeu, - 0x7c741ebfu, 0x7c7489d9u, 0x7c793469u, 0x7c7c12b2u, 0x7c7f258fu, 0x7c7fbcabu, - 0x7c811d82u, 0x7c8359d2u, 0x7c87a082u, 0x7c8fccf9u, 0x7c95c209u, 0x7c961e09u, - 0x7c9f59dbu, 0x7ca0a40bu, 0x7ca168d4u, 0x7ca4592eu, 0x7cae26e3u, 0x7cae6a91u, - 0x7cb1bfc7u, 0x7cb3122au, 0x7cbe81c3u, 0x7cc6c97du, 0x7cc77f7fu, 0x7ccf2348u, - 0x7cd96a38u, 0x7cda7f2bu, 0x7cdf779au, 0x7cef5c36u, 0x7cfba3fbu, 0x7cff4fefu, - 0x7d03d700u, 0x7d0a1c65u, 0x7d1f77c2u, 0x7d21f90fu, 0x7d26bee5u, 0x7d2ca017u, - 0x7d3364a2u, 0x7d36d571u, 0x7d455cd0u, 0x7d4d3c64u, 0x7d4dacc0u, 0x7d4f59a9u, - 0x7d518073u, 0x7d52b793u, 0x7d531783u, 0x7d647740u, 0x7d773d19u, 0x7d78a52du, - 0x7d796071u, 0x7d7a4fc8u, 0x7d7d65c1u, 0x7d8c87b1u, 0x7d8faf12u, 0x7d928d30u, - 0x7d96ab4du, 0x7d9c0b8cu, 0x7dafc555u, 0x7db2cd2du, 0x7db36f79u, 0x7db5fc32u, - 0x7dc9110eu, 0x7dcaa07au, 0x7dce3df7u, 0x7dd12ff6u, 0x7de6b4b7u, 0x7ded94dbu, - 0x7df13ffbu, 0x7dfcf980u, 0x7e016b36u, 0x7e0183efu, 0x7e03621fu, 0x7e0736ceu, - 0x7e07b3dbu, 0x7e0f28ddu, 0x7e1b6e57u, 0x7e24c08du, 0x7e25b4b2u, 0x7e29caadu, - 0x7e2a47bau, 0x7e2e7befu, 0x7e2ee04du, 0x7e2fca58u, 0x7e303a36u, 0x7e383be0u, - 0x7e396741u, 0x7e3c9c84u, 0x7e3ca113u, 0x7e430f2cu, 0x7e4a84e1u, 0x7e4d4d26u, - 0x7e517b1du, 0x7e5fde6eu, 0x7e688505u, 0x7e689d01u, 0x7e845fe0u, 0x7e8a4716u, - 0x7e8c96d8u, 0x7e96db10u, 0x7e971f7bu, 0x7ea58021u, 0x7ead2787u, 0x7ead8d16u, - 0x7eadd181u, 0x7eae522bu, 0x7eb9e31du, 0x7ec07c74u, 0x7ec1cb95u, 0x7ec4fcc2u, - 0x7eccf080u, 0x7ed1132cu, 0x7ed927cfu, 0x7edbdbeau, 0x7ee043b9u, 0x7ee0fae7u, - 0x7ee1e5afu, 0x7ee7b64fu, 0x7ee9bb68u, 0x7efa04d6u, 0x7f10c320u, 0x7f11c382u, - 0x7f13b529u, 0x7f17c371u, 0x7f189cefu, 0x7f19dbbdu, 0x7f1d477fu, 0x7f1deddcu, - 0x7f1ea0b5u, 0x7f203669u, 0x7f3349cbu, 0x7f3fd429u, 0x7f402607u, 0x7f4834e5u, - 0x7f48e3a8u, 0x7f4b8c3eu, 0x7f50595du, 0x7f56a3b6u, 0x7f5a061bu, 0x7f5d730eu, - 0x7f5dba98u, 0x7f5e4d83u, 0x7f6b1faau, 0x7f70ad15u, 0x7f790474u, 0x7f7c725eu, -}; -ALTD_MATH_TABLE uint32_t UCRT_COSF_WIDE_VALUES[2424] = { - 0x3f7ffe8cu, 0x3f7ffe55u, 0xbf7ffe64u, 0xbf7ffec0u, 0xbbc984d6u, 0xbf7fff66u, - 0xbbe39c5bu, 0x3f7ffe65u, 0x3f7fff24u, 0xbf7ffe66u, 0xbf7ffe2bu, 0x3bb8a2e1u, - 0xbf7ffe6du, 0x3f7ffe1au, 0x3f7ffecau, 0xbf7ffe5bu, 0xbf3504f2u, 0x3f7ffea9u, - 0xbf7ffe38u, 0xbf7ffe53u, 0x3f7ffe15u, 0xbf7ffe13u, 0xbf7ffe41u, 0xbf7ffe01u, - 0xbf7ffe4au, 0xbf7ffe0eu, 0xbf7ffe90u, 0x3f7fff0fu, 0x3f7ffe52u, 0x3f7ffe26u, - 0x3f7ffe2du, 0x3f7ffeb6u, 0x3f7ffe2fu, 0x3beef249u, 0x3f7ffee3u, 0x3f7ffef8u, - 0x3b93df58u, 0x3f7ffe77u, 0x3f7ffec8u, 0xbf7ffe88u, 0x3f7ffecfu, 0x3be34790u, - 0x3bdd59beu, 0x3f7ffe92u, 0xbf7fff2eu, 0x3f7fff71u, 0xbf7ffe9cu, 0xbf7ffe42u, - 0xbf7ffe45u, 0x3f7ffe8au, 0x3f7ffefdu, 0x3f7ffe68u, 0x3f7ffef9u, 0xbf7ffe23u, - 0xbf7ffe09u, 0x3f7ffe94u, 0xbf7ffe22u, 0xbf7ffe66u, 0x3f7fff24u, 0xbbd8723bu, - 0xbf7fffbeu, 0xbf7ffec0u, 0xbf7ffe8eu, 0x3f7ffe10u, 0x3f7ffe49u, 0x3f7ffe03u, - 0xbf7ffe76u, 0x3f7fff70u, 0x3f7fff6bu, 0x3f7ffe20u, 0x3f7ffe07u, 0x3f7ffe2eu, - 0x3f7ffef1u, 0x3f7ffe74u, 0xbf7ffe11u, 0x3bfe4af1u, 0xbf7ffe64u, 0x3f7ffe62u, - 0xbbec42cau, 0xbf7fff48u, 0xbf7fff1au, 0x3f7ffe97u, 0x3bf48ba8u, 0xbbf9ecb5u, - 0x3f7ffe8bu, 0x3f7ffe26u, 0x3be884a2u, 0xbf7ffe13u, 0xbf7ffe38u, 0xbbddae92u, - 0x3f7fff33u, 0x3bc947a9u, 0x3bdf1847u, 0x3f7ffe6eu, 0x3f7ffe4eu, 0x3f7ffe87u, - 0x3f7ffe3cu, 0x3f7ffea5u, 0xbf7fff3bu, 0xbf7ffe3au, 0x3f7fff0fu, 0x3f7ffe2fu, - 0xbf7fff7au, 0xbf7fffe5u, 0xbbf22fd6u, 0x3bdddfccu, 0x3f7fff08u, 0x3f7ffed9u, - 0x3f7ffe2eu, 0xbf7ffe99u, 0x3bdffa9au, 0x3f7ffe36u, 0x3f7ffeceu, 0xbf7ffed3u, - 0x3f7ffe83u, 0x3f7ffe65u, 0x3be2003cu, 0xbbee713au, 0x3f7ffe6bu, 0xbf7ffe5du, - 0x3f7ffe55u, 0xbba0d5c6u, 0xbf7ffe88u, 0xbf7ffe09u, 0x3f7fff46u, 0x3f7ffeaau, - 0xbf7ffe7du, 0xbf7ffe98u, 0xbf7ffe53u, 0x3f7ffe05u, 0x3f7ffe63u, 0xbf7fff7eu, - 0xbf7ffed1u, 0x3f7ffec3u, 0xbf7ffe7eu, 0x3f7fff5cu, 0xbf7ffe81u, 0x3f7ffedau, - 0xbbc6d2aeu, 0xbf7ffe31u, 0x3be9d497u, 0xbf7fff35u, 0x3bd74c83u, 0x3b515e2du, - 0xbf7ffe9du, 0xbf7fff11u, 0x3b9b0b54u, 0xbf7fffcbu, 0x3bff43f9u, 0xbf7ffedau, - 0x3f7ffe81u, 0xbbe3e073u, 0x3f7ffef3u, 0x3f7ffe98u, 0xbf7ffe16u, 0xbf7ffe2fu, - 0xbf7ffeb0u, 0xbf7ffe8cu, 0xbf7ffe94u, 0x3f7ffe66u, 0x3bf2dafeu, 0xbf7ffe2fu, - 0x3bfc307du, 0x3f7ffe88u, 0xbbabdf55u, 0xbbc94889u, 0x3f7ffe98u, 0x3f7ffe11u, - 0x3f7ffe81u, 0x3f7fff1fu, 0x3f7fff35u, 0x3f7fff3fu, 0x3f7ffe9du, 0xbf7ffe7au, - 0xbf7ffe19u, 0x3be9c065u, 0xbbd8aa10u, 0xbf7ffecbu, 0xbbd04ee8u, 0xbf7ffe1fu, - 0x3f7ffe16u, 0x3f7ffeb0u, 0xbf7ffe06u, 0x3bea000eu, 0xbf7ffe66u, 0x3f7ffec9u, - 0x3bf86506u, 0x3f7fff71u, 0xbf7ffe88u, 0x3b983c73u, 0x3f7ffe26u, 0xbf7ffe11u, - 0x3b9d025cu, 0x3f7ffe19u, 0xbf7ffe27u, 0x3f7fffddu, 0xbf7ffe2cu, 0x3f7ffe0fu, - 0xbf7ffe5eu, 0x3bf5ffbcu, 0x3f7ffe1eu, 0x3f7fff23u, 0x3f7fff50u, 0xbf7ffe7au, - 0x3bebfe48u, 0x3f7fff0du, 0xbf7ffeb8u, 0x3f7ffe57u, 0xbf7fffe9u, 0x3f7ffe16u, - 0x3f7ffe0au, 0xbf7ffe06u, 0xbf7ffe4bu, 0x3f7ffea2u, 0x3f7ffee0u, 0x3f7ffea8u, - 0x3f7ffe26u, 0x3f7ffe5fu, 0x3f7ffe97u, 0x3f7ffe5au, 0x3f7ffe95u, 0xbf7ffe2cu, - 0xbf7fff41u, 0xbbe78eb4u, 0xbf7ffedbu, 0x3f7ffe39u, 0xbf7ffeedu, 0x3f7ffee8u, - 0xbf7ffe1fu, 0xbf7ffe3du, 0x3f7ffe75u, 0x3f7ffe4fu, 0xbf7fff0au, 0xbf7ffe96u, - 0x3f7ffe93u, 0x3bea7a8du, 0x3f7ffe85u, 0xbf7fffb3u, 0x3bd5be25u, 0x3f7ffec1u, - 0x3f7ffe65u, 0x3f7ffe8au, 0x3f7ffeceu, 0x3bda130fu, 0xbf7ffefeu, 0x3f7ffe13u, - 0x3f7ffe34u, 0x3f7ffec6u, 0x3f7ffe6au, 0x3f7ffef7u, 0xbf7ffed4u, 0xbbe18e63u, - 0xbf7fff56u, 0xbf7ffe60u, 0xbf7ffe82u, 0x3f7ffe01u, 0x3f7fff12u, 0xbf7ffec2u, - 0x3f7ffe44u, 0xbf7ffefau, 0x3f7ffeafu, 0x3f7ffe6du, 0x3f7ffe43u, 0xbf7fff7cu, - 0x3f7fffaau, 0xbbf5a568u, 0xbf7ffe92u, 0x3f7ffe42u, 0xbf7ffe0cu, 0xbf7ffe7cu, - 0xbf7fff68u, 0x3f7fff98u, 0xbf7ffed5u, 0xbf7ffe9au, 0x3f7fff9bu, 0xbf7ffe55u, - 0x3f7ffe5du, 0xbf7ffe62u, 0x3f7ffe8eu, 0xbf7ffe14u, 0x3f7ffe4eu, 0x3f7ffe09u, - 0x3f7fffadu, 0x3f7ffe9eu, 0xbf7ffe4au, 0x3f7ffe47u, 0x3f7ffe9cu, 0xbf7fff32u, - 0xbbfbcb18u, 0xbf7fff72u, 0x3f7ffe35u, 0x3f7fff86u, 0xbf7fff83u, 0x3f7ffe84u, - 0x3f7ffe12u, 0x3f7ffef2u, 0xbf7ffedeu, 0x3f7ffe9bu, 0xbf7ffeb5u, 0xbf7ffe6cu, - 0xbf7ffef9u, 0x3f7fff0bu, 0x3f7ffe69u, 0x3f7fff5du, 0x3f7ffe5cu, 0x3f7ffe5bu, - 0xbf7ffe2du, 0x3f7ffe14u, 0xbf7fff17u, 0xbf7ffe86u, 0x3f7fff62u, 0xbf7ffe34u, - 0x3f7ffe3cu, 0xbf7ffed9u, 0x3bd6843fu, 0x3f7ffe79u, 0x3f7ffe2cu, 0xbf7fff11u, - 0x3f7ffe27u, 0x3f7ffe06u, 0x3f7ffe4bu, 0x3ba7410du, 0xbf7ffe43u, 0xbf7fff79u, - 0xbf7ffe24u, 0xbb9664b9u, 0x3f7ffe56u, 0x3b8fd5d6u, 0xbf7ffe2eu, 0xbf7ffe1bu, - 0x3f7ffe49u, 0xbf7fff5bu, 0x3f7ffe50u, 0x3f7ffe0eu, 0xbbdb1032u, 0xbf7ffe7eu, - 0x3f7ffe63u, 0x3f7ffeaau, 0xbf7ffe1du, 0xbf7ffe60u, 0xbf7ffeb8u, 0x3f7ffeddu, - 0xbbfd2394u, 0x3f7fff08u, 0x3f7ffeefu, 0xbf7fff54u, 0x3f7ffe2eu, 0xbf7ffe2au, - 0x3f7ffe87u, 0xbf7ffee2u, 0x3f7ffe4du, 0x3bf7f77au, 0xbf7ffef6u, 0xbbb37986u, - 0x3f7fff88u, 0x3f7ffe82u, 0xbbb19c80u, 0xbf7ffe57u, 0x3f7ffe8fu, 0x3f7ffe59u, - 0x3f7ffea4u, 0x3f7ffe78u, 0xbbe83b1fu, 0x3f7ffe9cu, 0xbf7ffebeu, 0x3f7ffe5du, - 0x3f7ffe6du, 0x3f7ffe44u, 0x3f7ffe19u, 0xbf7ffe1fu, 0x3f7ffeeau, 0xbf7ffe0bu, - 0x3f7ffe9au, 0x3f7ffe05u, 0xbf7ffe0au, 0x3f7ffea6u, 0x3f7fffe7u, 0xbf7fff78u, - 0x3f7ffe8bu, 0x3f7ffe36u, 0xbbe0ad1du, 0xbf7ffeb3u, 0xbbf8cfdeu, 0xbf7ffe2bu, - 0x3f7ffe07u, 0xbe8dbae5u, 0x3f7ffe2du, 0x3f7ffefdu, 0xbf7ffe9fu, 0x3f7ffe32u, - 0xbf7fff03u, 0x3f7ffec3u, 0x3f7ffe20u, 0xbf7ffe91u, 0xbf7ffeb7u, 0x3f7ffe64u, - 0x3b8a2421u, 0x3bcfb11cu, 0x3be20d0eu, 0xbf7ffe3fu, 0x3be25235u, 0x3f7ffe73u, - 0x3f7ffe08u, 0x3bd48859u, 0x3f7ffe4bu, 0xbf7ffeafu, 0xbf7ffe69u, 0xbf7ffed7u, - 0x3f7fff18u, 0x3f7ffe1au, 0x3f7ffe54u, 0xbf7ffe51u, 0x3f7ffe02u, 0xbf7ffe83u, - 0xbf7ffe74u, 0xbf7ffe4eu, 0x3f7ffe0cu, 0xbf7ffe75u, 0xbbd773f2u, 0x3f7ffe5fu, - 0x3bf1f877u, 0xbf7ffe6fu, 0xbf7ffe76u, 0xbf7ffe21u, 0xbf7ffe81u, 0xbf7ffeb5u, - 0xbf7ffe77u, 0xbf7ffec5u, 0xbf7fff07u, 0xbf7ffea6u, 0x3f7ffecdu, 0xbf7ffe63u, - 0xbf7ffe79u, 0x3f7ffea5u, 0xbf7ffe27u, 0x3f7ffeebu, 0x3f7ffe60u, 0x3b94d5c9u, - 0x3f7ffe35u, 0xbf7fff49u, 0xbbe38024u, 0x3f7ffe22u, 0xbbfdef08u, 0x3f7ffe4au, - 0x3f7ffe30u, 0xbf7ffe47u, 0x3f7ffe65u, 0xbf7ffe72u, 0xbf7ffe58u, 0xbf7ffe86u, - 0xbf7ffe7cu, 0x3bcf1332u, 0x3bffd2b8u, 0x3f7ffef5u, 0xbf7ffe94u, 0xbf7ffe93u, - 0x3f7ffec4u, 0xbbc42fe0u, 0x3f7ffe95u, 0x3f7ffe5du, 0x3f7ffea4u, 0xbf7ffed8u, - 0xbf7ffe50u, 0x3f7ffe8bu, 0x3f7fff5au, 0x3bd7b327u, 0xbbecc2c2u, 0xbf7fff27u, - 0x3f7fff09u, 0x3f7fff25u, 0xbbf97ae4u, 0x3f7ffe3bu, 0xbf7ffe1cu, 0x3f7ffe09u, - 0xbf7ffe4cu, 0xbf7ffed1u, 0xbf7ffe34u, 0xbf7ffe14u, 0xbf7ffec6u, 0xbf7ffe7bu, - 0x3f7ffe28u, 0x3f7ffee5u, 0xbf7ffefau, 0x3f7ffe97u, 0xbf7ffeccu, 0x3f7ffec7u, - 0xbbed4342u, 0x3f7ffeaeu, 0xbf7ffe90u, 0xbf7ffe26u, 0x3f7fff8au, 0xbf7ffe25u, - 0xbf7fff12u, 0x3bd01e4bu, 0x3f7ffeacu, 0x3f7ffe8eu, 0xbf7ffe4fu, 0x3f7fff76u, - 0xbf7ffe07u, 0xbf7ffe2au, 0x3f7fff5fu, 0x3f7ffee9u, 0x3f7ffe29u, 0xbf7ffec2u, - 0xbf7fff1cu, 0x3f7ffefeu, 0xbf7ffe48u, 0x3f7ffe05u, 0x3bdfe958u, 0x3f7ffe13u, - 0xbf7ffe24u, 0x3f7fff7fu, 0xbf7ffefdu, 0xbf7fff0au, 0xbf7ffedcu, 0x3f7fff40u, - 0xbf7fff15u, 0x3f7ffe0eu, 0x3f7ffe6du, 0x3f7ffe44u, 0xbf7ffe46u, 0x3f7ffe0bu, - 0x3bd9860du, 0x3f7ffe89u, 0x3f7ffee3u, 0x3f7fff81u, 0xbf7ffe12u, 0xbf7ffe4bu, - 0x3f7ffed5u, 0x3f7ffef3u, 0xbf7ffeecu, 0xbf7ffe3au, 0xbb5e506cu, 0xbf7fff13u, - 0xbbef8ea5u, 0x3f7ffe5fu, 0x3b99bb23u, 0x3f7ffe0cu, 0xbf7ffeafu, 0x3f7ffe08u, - 0xbf7ffe91u, 0xbbf57223u, 0xbf7ffe8du, 0xbbe17857u, 0x3f7ffee7u, 0xbbf3500eu, - 0x3f7ffe18u, 0x3f7ffedau, 0xbf7ffe10u, 0xbf7ffe3cu, 0x3f7ffecau, 0xbbd17bfau, - 0xbf7ffe71u, 0xbf7ffee0u, 0xbf7ffeabu, 0x3f7ffe3du, 0xbf7ffeacu, 0xbbed4262u, - 0xbf7ffe09u, 0x3f7ffe1cu, 0x3f7ffe8cu, 0x3bf43cd7u, 0xbf7ffe5au, 0xbf7ffe95u, - 0x3f7ffe26u, 0xbf7ffe19u, 0x3f7ffe64u, 0x3f7fff29u, 0xbf7ffef4u, 0x3f7ffe02u, - 0x3f7ffe11u, 0xbbfbaa13u, 0x3bf74d30u, 0x3f7ffe5bu, 0x3f7ffe6fu, 0x3f7ffe0bu, - 0xbf7fff19u, 0xbf7ffe7bu, 0x3f7fff8du, 0x3f7ffee3u, 0x3f7fff11u, 0x3bb402dcu, - 0xbbf2b471u, 0x3bf90742u, 0x3f7ffeebu, 0x3f7ffe2du, 0x3f7ffe0fu, 0xbf7ffe57u, - 0xbf7ffe33u, 0x3f7fff06u, 0x3baa59b5u, 0x3f7ffe34u, 0xbf7ffecbu, 0xbbbff7f6u, - 0xbbf5a5ebu, 0xbf7ffe10u, 0x3f7ffedfu, 0xbba81630u, 0x3f7ffe79u, 0xbf7ffe7fu, - 0xbf7fff7du, 0x3f7ffe29u, 0xbf7ffe4fu, 0x3becc792u, 0x3f7ffe94u, 0x3f7fff14u, - 0x3f7ffe0cu, 0x3f7ffec3u, 0xbf7ffe84u, 0xbbfed837u, 0x3bf67a62u, 0xbf7ffef9u, - 0x3f7ffed3u, 0x3f7ffe5cu, 0xbf7ffe1au, 0x3f7ffe99u, 0xbf7ffe2au, 0x3f7ffe78u, - 0xbf7fff6cu, 0x3f7ffe38u, 0x3f7ffe20u, 0x3f7ffeb1u, 0xbbf9bc67u, 0xbf7ffe52u, - 0xbf7ffe60u, 0x3f7ffe6au, 0xbf7fff9au, 0xbbb1496bu, 0x3f7ffe81u, 0xbf7ffe03u, - 0x3f7ffea2u, 0xbf7ffe37u, 0xbf7ffe64u, 0x3f7fff6du, 0x3bee7b73u, 0x3f7ffe7eu, - 0xbf7fff94u, 0x3f7ffe4bu, 0x3bec020cu, 0xbf7ffe04u, 0xbf7ffe26u, 0xbf7ffe13u, - 0x3f7ffe43u, 0xbf7ffebeu, 0xbf7ffeaeu, 0x3bf3b63au, 0xbf7ffe05u, 0x3f7ffe10u, - 0x3f7ffe71u, 0xbf7ffe41u, 0x3f7ffe40u, 0xbbf10103u, 0xbf7ffe9au, 0x3f7ffea1u, - 0xbf7ffe57u, 0xbbca0194u, 0x3f7ffe45u, 0xbbcc697cu, 0xbbf63a43u, 0x3f7ffebbu, - 0xbf7ffe0au, 0xbf7ffed0u, 0xbf7fff44u, 0x3f7fff20u, 0xbf7ffe09u, 0xbf7ffe12u, - 0x3f7ffec1u, 0xbbf35a1bu, 0xbf7ffefcu, 0x3f7ffe01u, 0x3f7ffe32u, 0xbf7ffe96u, - 0x3f7ffea4u, 0x3f7ffe4cu, 0xbf7ffe88u, 0xbf7ffe9fu, 0x3bc56621u, 0xbf7ffea8u, - 0x3f7ffe8cu, 0x3f7ffe2cu, 0xbf7ffe31u, 0xbf7ffeb7u, 0x3bd6542eu, 0x3f7fff18u, - 0xbbdeb4fdu, 0xbf7ffedau, 0x3f7ffedcu, 0x3f7ffeefu, 0xbf7ffe8eu, 0x3f7ffe58u, - 0x3f7ffe39u, 0xbf7ffe53u, 0x3f7ffe19u, 0xbf7ffe1fu, 0x3f7ffe6du, 0xbf7ffeb0u, - 0xbf7fff2au, 0x3f7ffefdu, 0xbc000b2du, 0x3f7fff9cu, 0xbf7ffe38u, 0xbf7ffebau, - 0x3f7ffe0cu, 0x3f7ffee1u, 0xbf7ffe66u, 0x3f7fff1du, 0x3f7ffe1eu, 0xbf7ffef7u, - 0x3bccdfb9u, 0xbf7fff75u, 0xbf7ffeb3u, 0xbf7ffe67u, 0xbf7ffe55u, 0xbbec213cu, - 0xbf7ffec4u, 0x3bfe58a5u, 0x3f7ffe0du, 0x3f7ffe24u, 0x3f7fff22u, 0x3f7ffe1au, - 0x3bdf1975u, 0xbbe77a43u, 0x3f7fff29u, 0xbf7ffed2u, 0xbf7ffe4eu, 0xbf7ffe6eu, - 0xbb90870bu, 0xbf7fff2bu, 0xbf7ffe22u, 0xbf7ffe5eu, 0xbf7ffe36u, 0xbf7ffecdu, - 0x3afd496au, 0x3f7ffe27u, 0x3f7ffe18u, 0x3f7ffef4u, 0xbf7fff97u, 0x3f7ffeadu, - 0x3f7ffe0bu, 0xbf7ffe2bu, 0xbbc4b51bu, 0xbf7ffe68u, 0x3f7ffe34u, 0x3f7ffe5fu, - 0x3f7ffe03u, 0xbf7ffea7u, 0x3f7ffe64u, 0x3f7ffe26u, 0xbf7ffe4cu, 0xbf7ffe97u, - 0xbf7ffeefu, 0x3f7ffeb5u, 0xbf7ffeacu, 0xbf7ffe60u, 0x3f7ffe7eu, 0xbf7ffeccu, - 0xbf7ffe61u, 0xbf7ffe62u, 0x3f7ffe3bu, 0x3f7fff83u, 0xbf7ffec3u, 0x3f7ffe0au, - 0xbf7fff08u, 0x3f7ffea9u, 0xbf7ffe92u, 0xbf7fff3fu, 0xbf7ffe8au, 0x3bfd3459u, - 0x3f7ffe51u, 0xbf7fff0eu, 0x3f7ffe69u, 0x3f7ffe14u, 0xbf7ffe4au, 0x3f7fff50u, - 0xbf7ffe1cu, 0xbf7ffe5au, 0x3bef98bbu, 0x3f7ffe23u, 0xbf7ffea0u, 0xbf7ffe9eu, - 0xbf7ffe4fu, 0xbf7ffe82u, 0x3f7ffe90u, 0x3f7ffe40u, 0xbf7fff3cu, 0xbbe9efbau, - 0xbf7fff67u, 0xbf7ffeb2u, 0xbf7ffe6au, 0x3f7ffe59u, 0xbf7ffeecu, 0x3f7ffe08u, - 0xbf7fff5au, 0x3bd44aaau, 0x3f7ffec9u, 0x3b922b34u, 0x3f7fff33u, 0x3bf2e930u, - 0x3f7ffe4bu, 0xbf7fff28u, 0x3f7fff4fu, 0x3f7ffe32u, 0xbf7ffe3eu, 0x3f7ffe30u, - 0x3f7fffc1u, 0xbbfe242au, 0xbf7ffe9du, 0x3f7ffe09u, 0xbf7ffe39u, 0x3f7ffebau, - 0x3f7ffe47u, 0xbf7ffefbu, 0x3f7ffe8bu, 0x3f7ffe0fu, 0xbf7ffe17u, 0xbf7ffec7u, - 0x3f7ffe6eu, 0x3f7ffe04u, 0x3b8a6d81u, 0xbf7ffe19u, 0xbbffd0d9u, 0x3f7ffe8eu, - 0xbf7ffee0u, 0xbf7ffeb7u, 0xbf7fff52u, 0xbf7ffe06u, 0x3f7ffe24u, 0x3f7fff31u, - 0x3f7ffe1eu, 0x3f7ffeaeu, 0xbbe80dddu, 0x3f7ffefcu, 0xbf7ffeb1u, 0x3f7ffeb9u, - 0x3f7ffe4fu, 0xbf7ffe23u, 0xbf7ffe12u, 0x3f7ffe92u, 0x3f7ffe97u, 0x3f7ffea7u, - 0xbf7ffe03u, 0xbf7ffe34u, 0x3f7ffed0u, 0x3bc34b51u, 0xbf7ffe7eu, 0x3beb8f44u, - 0xbf7ffe51u, 0x3f7ffe9cu, 0xbf7fff01u, 0xbf7ffe3au, 0x3f7ffedfu, 0x3bf05bb0u, - 0x3f7ffe0bu, 0x3f7ffe27u, 0xbbdb6c98u, 0x3f7ffe1du, 0xbf7ffe8fu, 0x3f7ffee3u, - 0xbf7ffedeu, 0xbf7fff4au, 0x3f7ffe38u, 0xbba701b6u, 0x3f7ffe5bu, 0xbf7fff21u, - 0xbf7ffef6u, 0x3f7ffe18u, 0xbf7ffe65u, 0xbf7ffec2u, 0x3f7ffeb5u, 0xbf7fff57u, - 0x3f7ffe99u, 0xbf7ffe48u, 0xbf7ffe6bu, 0x3f7ffe2cu, 0xbf7ffe25u, 0xbf7ffea1u, - 0x3f7ffe41u, 0xbf7ffec6u, 0x3f7ffe55u, 0x3f7ffe8cu, 0xbbead006u, 0xbf7fff18u, - 0x3f7ffe74u, 0xbf7ffe8du, 0xbf7ffe2eu, 0x3f7ffeddu, 0xbf7fff47u, 0x3f7ffee5u, - 0x3f7fff07u, 0xbf7ffeb3u, 0xbf7ffe4bu, 0x3f7fff04u, 0xbbfe2287u, 0x3f7ffe37u, - 0x3bfd32b6u, 0x3f7ffe79u, 0x3f7ffecbu, 0xbbdc708bu, 0x3f7ffe42u, 0x3f7ffe03u, - 0xbf7fff99u, 0xbf7ffebfu, 0xbf7ffe13u, 0xbf7fff23u, 0x3f7ffed9u, 0x3f7ffe7cu, - 0xbf7ffee8u, 0x3f7ffed6u, 0x3f7ffe07u, 0xbbcb3213u, 0xbf7ffea5u, 0x3f7ffe5au, - 0x3f7ffe62u, 0xbf7ffee6u, 0xbf7ffe5cu, 0xbf7ffe0cu, 0xbf7ffe64u, 0x3f7ffe28u, - 0x3f7ffef8u, 0xbf7ffe71u, 0x3f7fff1fu, 0x3f7fff2eu, 0xbbf9425du, 0x3f7ffe29u, - 0xbf7ffec7u, 0xbf7ffe6au, 0x3f7ffe30u, 0x3f7ffe0fu, 0xbf7ffe98u, 0x3f7ffea0u, - 0x3bff55c6u, 0x3be91a57u, 0xbf7ffe9bu, 0xbf7fff3bu, 0xbf7ffe1fu, 0xbf7ffe49u, - 0x3f7ffe77u, 0xbf7ffe2du, 0x3f7ffeb4u, 0x3f7ffe52u, 0x3f7ffe3cu, 0x3f7ffe0au, - 0xbf7ffe02u, 0xbf7fff9bu, 0xbf7ffe8eu, 0x3f7ffe3eu, 0x3f7ffe82u, 0xbf7fffb6u, - 0xbf7ffe51u, 0xbf7ffe34u, 0x3f7ffeaeu, 0x3f7fff31u, 0xbf7ffe0bu, 0xbf7ffe04u, - 0xbf7ffe9au, 0xbf7fff39u, 0x3f7ffe80u, 0x3bb6b63au, 0x3bfa486eu, 0x3f7ffe0cu, - 0x3f7ffe64u, 0xbf7ffea2u, 0x3f7ffe3fu, 0xbf7ffebdu, 0x3f7ffe31u, 0xbf7ffe0du, - 0x3f7ffe22u, 0x3be124dcu, 0xbf7ffe68u, 0x3b86e4c4u, 0xbf7ffe20u, 0xbf7ffe9eu, - 0xbf7ffe85u, 0xbf7ffe1au, 0xbf7ffec0u, 0x3f7ffe87u, 0xbf7ffec9u, 0x3bf95600u, - 0x3f7ffe56u, 0x3beca99au, 0x3f7ffe57u, 0xbf7fff48u, 0x3f7ffe59u, 0xbf7fff95u, - 0xbf7ffe37u, 0x3f7ffec6u, 0x3bfba495u, 0xbf7fff54u, 0x3f7ffe46u, 0xbf7fff75u, - 0x3f7ffe21u, 0xbf7ffe6cu, 0x3f7ffe37u, 0xbf7fff20u, 0xbf7ffe59u, 0x3f7ffe85u, - 0x3f7ffe68u, 0xbf7ffe47u, 0xbf7ffe22u, 0x3f7ffe0du, 0x3f7fff84u, 0x3f7fff19u, - 0xbf7ffe64u, 0xbf7ffe0cu, 0xbf7ffef1u, 0x3f7ffe9au, 0x3f7ffe0bu, 0x3f7ffe28u, - 0xbf7ffecau, 0xbf7ffe0au, 0x3f7ffe2du, 0xbbd204f0u, 0x3f7ffe1fu, 0x3f7ffe1cu, - 0x3f7ffe15u, 0xbf7ffe22u, 0x3f7ffe85u, 0xbf7ffe57u, 0x3beef07fu, 0xbf7ffe86u, - 0x3f7ffed8u, 0xbf7ffe01u, 0x3f7ffe8au, 0x3f7ffe5fu, 0x3f7ffe31u, 0x3f7ffee0u, - 0x3f7ffe3fu, 0xbf7fff04u, 0xbb64d34du, 0x3f7ffe27u, 0xbf7ffe25u, 0x3f7ffe17u, - 0xbf7ffe1eu, 0x3f7ffe3bu, 0xbf7ffe9eu, 0x3f7ffe75u, 0x3f7ffe58u, 0x3f7ffe46u, - 0x3bfd9050u, 0x3f7ffe70u, 0x3f7fff0au, 0x3bf33e84u, 0xbf7ffeebu, 0xbf7ffee6u, - 0x3f7ffe28u, 0x3f7ffe90u, 0xbf7ffe69u, 0x3f7ffe09u, 0xbf7ffebeu, 0x3f7ffe4cu, - 0x3bf75321u, 0xbf7ffe0eu, 0x3f7ffe9cu, 0xbf7ffe97u, 0x3f7fff05u, 0xbf7ffe4fu, - 0x3f7ffe5eu, 0xbf7ffe1au, 0xbbec00bfu, 0x3bccd7a1u, 0xbf7ffed6u, 0xbf7ffe30u, - 0xbf7fff77u, 0x3f7fff83u, 0xbf7ffe73u, 0x3f7ffe15u, 0xbf7ffeb1u, 0x3f7fff7eu, - 0x3f7fff26u, 0x3f7ffe44u, 0x3f7fff4au, 0xbf7fff50u, 0x3f7ffe6eu, 0x3f7ffef5u, - 0x3f7ffee9u, 0xbf7ffe63u, 0x3f7ffe81u, 0x3bd26f27u, 0xbf7ffe91u, 0xbbf75c90u, - 0xbf7ffe04u, 0xbf7fff8du, 0x3f7fff0eu, 0x3be9a462u, 0xbf7ffebau, 0xbf7fff2bu, - 0x3f7ffeb8u, 0x3be98f8du, 0x3f7ffe96u, 0xbf7fff14u, 0x3f7ffe3au, 0xbbd3bad9u, - 0xbf7ffe26u, 0xbf7fff51u, 0x3f7ffee1u, 0xbf7ffe33u, 0xbf7ffe72u, 0xbbeb85b8u, - 0x3f7ffef7u, 0x3f7ffee5u, 0x3bf31c16u, 0xbf7fff35u, 0xbf7ffe8fu, 0xbf7ffe66u, - 0x3f7ffe0eu, 0x3f7ffeffu, 0xbf7ffeafu, 0x3f7ffedeu, 0xbbed7a28u, 0x3f7fff42u, - 0xbf7ffe3bu, 0x3bfa8e39u, 0x3bdcb876u, 0xbf7ffe05u, 0xbf7ffe21u, 0x3becfaf7u, - 0xbf7ffe60u, 0xbbe0c164u, 0xbf7fff19u, 0x3bca6a98u, 0xbf7ffe3cu, 0x3f7fffdeu, - 0x3f7ffe4au, 0xbf7ffe2cu, 0x3f7ffe2fu, 0x3f7ffeb5u, 0x3f7ffe07u, 0xbf7ffe48u, - 0xbf7ffefbu, 0x3f7ffe41u, 0xbf7fff49u, 0xbbfc64e8u, 0xbf7fff6cu, 0x3f7ffe2au, - 0x3f7fff06u, 0xbb95adf0u, 0x3f7ffe93u, 0xbf7ffe01u, 0xbbc54106u, 0x3f7ffe2eu, - 0xbf7ffe32u, 0xbbf6fc82u, 0xbf7ffe6du, 0xbf7ffe83u, 0x3f7ffe24u, 0x3f7fff76u, - 0xbf7fff2eu, 0x3f7ffe78u, 0xbf7ffe5bu, 0xbf7ffe3au, 0x3f7ffea8u, 0x3f7ffeaau, - 0x3f7fff30u, 0xbf7ffe0bu, 0x3bed88dcu, 0xbbed09abu, 0xbf7ffe99u, 0xbf7ffe34u, - 0xbf7ffeb4u, 0x3f7ffe98u, 0x3f7ffe79u, 0xbbd57f26u, 0x3f7ffe52u, 0x3f7ffe14u, - 0x3f7ffe53u, 0xbf7ffe0au, 0xbf7fff39u, 0xbf7ffec0u, 0xbf7fff52u, 0xbf7fff59u, - 0xbba015b5u, 0xbf7ffe77u, 0x3f7ffeacu, 0x3bef6cddu, 0x3f7ffe30u, 0xbf7ffe31u, - 0x3f7fff04u, 0xbf7ffe70u, 0xbbe0cda6u, 0xbf7ffee9u, 0x3f7ffe63u, 0x3bc6b76fu, - 0xbf7ffeadu, 0x3f7ffe4du, 0xbbfeaf76u, 0x3f7ffeccu, 0x3f7ffe5au, 0xbbdb72afu, - 0x3f7ffe28u, 0x3bfc173du, 0x3f7ffe92u, 0x3bd0b61bu, 0x3f7ffe43u, 0xbf7fff0du, - 0xbf7ffe94u, 0x3f7ffe10u, 0xbf7ffe11u, 0xbbd52fbau, 0xbf7ffecfu, 0xbf7ffe2du, - 0xbf7ffe6au, 0xbf7ffe8bu, 0xbf7ffe66u, 0xbf7fff14u, 0x3f7fff0eu, 0x3f7ffef3u, - 0x3f7ffeedu, 0x3f7ffee3u, 0x3f7ffe96u, 0xbbafee0bu, 0xbf7ffe95u, 0x3f7ffe47u, - 0xbf7fff47u, 0xbf7fff7du, 0xbf7fff01u, 0x3beda121u, 0x3f7ffe7bu, 0xbf7fff24u, - 0xbf7ffe35u, 0xbf7ffe40u, 0xbf7ffe47u, 0x3f7fff8fu, 0xbf7fff7bu, 0x3f7ffea0u, - 0x3f7ffedcu, 0x3f7ffeabu, 0xbf7ffed0u, 0x3f7ffe1bu, 0xbf7ffe48u, 0xbf7ffe8au, - 0x3bd912feu, 0xbf7ffeafu, 0xbf7ffe91u, 0x3f7ffe23u, 0xbf7ffe1bu, 0xbf7fff2fu, - 0x3f7ffebfu, 0xbf7ffe23u, 0x3f7ffe16u, 0xbf7ffec1u, 0x3f7fff5cu, 0xbf7ffe16u, - 0x3b8bda1fu, 0xbf7ffe26u, 0xbf7ffe7eu, 0xbf7ffe20u, 0xbf7fff16u, 0xbf7ffe3fu, - 0xbf7fff21u, 0xbbd9711eu, 0x3f7ffee0u, 0xbf7fff67u, 0x3f7ffe3fu, 0xbf7ffe84u, - 0xbf7ffe0du, 0xbf7ffe56u, 0x3f7ffe04u, 0xbf7ffe41u, 0x3f7ffe57u, 0x3f7ffe55u, - 0x3f7ffe59u, 0x3f7ffe27u, 0x3f7fff54u, 0x3f7ffec3u, 0xbf7ffe54u, 0x3f7ffe2eu, - 0x3f7ffee9u, 0xbf7ffe21u, 0xbf7fff6au, 0xbbda3f55u, 0x3f7ffe01u, 0xbf7ffe52u, - 0xbf7ffe8eu, 0x3f7ffecbu, 0x3f7ffe0cu, 0x3f7ffe09u, 0xbf7ffe5cu, 0xbf7ffec5u, - 0xbbc1c89du, 0x3f7fff44u, 0x3f7ffe51u, 0xbf7fff49u, 0xbf7ffebau, 0x3f7ffe42u, - 0xbf7fffe8u, 0x3f7fff6bu, 0xbf7fff46u, 0x3f7ffe4fu, 0xbf7ffea1u, 0x3f7fff7du, - 0xbf7ffe10u, 0x3f7ffe94u, 0xbf7ffe98u, 0x3f7ffe70u, 0xbf7ffe96u, 0xbf7ffe86u, - 0x3be299a1u, 0xbf7ffe4cu, 0x3f7ffe22u, 0x3ba2f2b9u, 0xbbe7fe7fu, 0x3bd227b7u, - 0x3f7ffe4bu, 0xbf7ffed5u, 0x3f7ffe07u, 0x3f7ffee2u, 0xbf7ffe45u, 0xbf7ffe24u, - 0x3bf75fddu, 0x3be7d701u, 0xbf7ffe0au, 0xbf7fff4au, 0x3bff6911u, 0x3f7ffeceu, - 0x3f7ffe18u, 0x3f7ffe65u, 0xbf7ffe23u, 0xbf7ffe48u, 0x3bf1b345u, 0x3f7ffe3au, - 0x3f7ffe7bu, 0xbf7fff24u, 0x3f7ffea0u, 0x3f7ffedcu, 0xbf7ffed0u, 0xbf7ffe8cu, - 0x3f7fff1du, 0x3ba51de2u, 0x3bfd0591u, 0xbf7ffe1eu, 0x3f7ffe4au, 0x3f7ffee5u, - 0x3f7ffe03u, 0xbbb94db3u, 0x3f7ffea9u, 0xbf7ffebbu, 0xbf7fff30u, 0xbf7ffe92u, - 0x3f7ffe1fu, 0x3f7ffe3du, 0x3f7ffe12u, 0xbf7ffed3u, 0x3f7ffe05u, 0x3f7fff42u, - 0x3f7ffe08u, 0x3f7fff2bu, 0xbf7fff2fu, 0xbbecfd5cu, 0xbbdd1971u, 0x3f7fff50u, - 0x3b8bb087u, 0x3f7ffe50u, 0x3bf25d8fu, 0xbf7ffe73u, 0x3be1409fu, 0x3f7ffe49u, - 0xbf7ffe69u, 0x3bee0fdbu, 0x3f7fff47u, 0x3f7ffe75u, 0xbf7ffe15u, 0xbf7ffe52u, - 0xbf7ffe41u, 0xbf7ffe26u, 0x3f7fffcau, 0x3f7ffe55u, 0xbf7ffe21u, 0xbf7ffe1fu, - 0xbf7fff2au, 0x3f7ffe14u, 0x3f7ffe39u, 0x3f7ffeebu, 0xbf7ffe95u, 0xbf7ffe66u, - 0xbf7ffe0eu, 0xbf7ffe44u, 0x3f7ffe60u, 0xbf7ffec7u, 0x3f7ffe89u, 0xbf7ffe5bu, - 0xbf7fff69u, 0x3bce3316u, 0xbf7ffe1bu, 0x3f7ffe5au, 0xbf7ffe61u, 0x3f7fff04u, - 0x3f7ffe81u, 0xbf7fffa7u, 0xbf7ffe96u, 0xbf7ffe00u, 0x3f7fff6bu, 0x3f7fff3au, - 0xbf7ffe9cu, 0x3f7ffe1cu, 0xbf7ffe9fu, 0x3f7ffe1au, 0x3f7ffe15u, 0x3f7fff3du, - 0x3f7ffe21u, 0xbf7fff51u, 0x3f7fff43u, 0xbf7ffeffu, 0x3f7ffe16u, 0xbf7fff2du, - 0xbb9fbac0u, 0x3bff60fau, 0x3f7ffe6fu, 0x3f7ffe36u, 0xbf7ffe25u, 0xbf7ffe94u, - 0x3bf73b3cu, 0x3f7ffe97u, 0x3f7fff25u, 0xbf7ffe48u, 0x3f7ffe4au, 0xbf7ffecau, - 0xbf7ffe5fu, 0x3f7ffe5eu, 0xbf7ffe5du, 0x3f7ffe5cu, 0xbf7ffef1u, 0xbbfab41bu, - 0xbbc2c133u, 0x3f7ffe8bu, 0xbf7ffe17u, 0x3f7ffea3u, 0x3f7ffee1u, 0xbf7ffe2cu, - 0x3f7ffe6bu, 0x3f7ffeb2u, 0x3f7ffeeau, 0xbf7ffea1u, 0xbf7ffe1du, 0xbbac4d26u, - 0x3f7ffeecu, 0x3f7ffea8u, 0x3f7ffe06u, 0x3f7ffef5u, 0x3f7ffe62u, 0x3f7fff29u, - 0xbf7ffe56u, 0xbf7ffeeeu, 0xbf7fff05u, 0x3f7fff32u, 0x3f7fff76u, 0xbf7ffe28u, - 0xbf7ffe50u, 0xbf7ffe3du, 0xbf7ffeceu, 0x3f7ffe0au, 0x3f7ffe45u, 0xbf7ffe20u, - 0xbf7ffe3eu, 0x3be00e9au, 0x3f7ffe99u, 0x3f7ffe0du, 0x3f7fff22u, 0xbf7fff54u, - 0xbf7ffe53u, 0x3f7ffe38u, 0x3f7fff3cu, 0x3f7fff16u, 0x3bf886c3u, 0x3f7fff20u, - 0x3f7ffe26u, 0x3f7ffe41u, 0x3f7ffe57u, 0x3f7ffefau, 0xbf7ffe46u, 0x3f7ffefeu, - 0x3f7ffe88u, 0x3f7ffe70u, 0xbf7ffe23u, 0x3f7ffe49u, 0x3f7ffef9u, 0x3f7ffe11u, - 0xbf7fff8cu, 0xbf7ffe6au, 0x3f7ffe8du, 0x3bfb0378u, 0x3f7ffe10u, 0xbf7ffecfu, - 0x3f7fff0eu, 0xbbf504d7u, 0x3f7ffe85u, 0x3f7ffecdu, 0x3f7fff03u, 0xbbf8cf6eu, - 0xbf7fff25u, 0x3bcae6fbu, 0x3f7ffe94u, 0x3bb9fed3u, 0x3f7fff51u, 0x3f7ffe61u, - 0x3f7ffed7u, 0xbf7ffe75u, 0x3f7ffe69u, 0x3f7ffe0eu, 0x3bf709dcu, 0x3f7ffe96u, - 0x3be62157u, 0xbbf76a3du, 0xbf7ffee4u, 0x3f7ffe72u, 0x3f7ffe8cu, 0xbf7ffefbu, - 0xbf7ffe90u, 0x3be5eb60u, 0xbf7ffe40u, 0xbf7ffeb6u, 0x3f7ffe7fu, 0x3f7ffe6eu, - 0x3f7ffe12u, 0x3f7ffe0au, 0x3f7fff48u, 0xbf7ffe17u, 0x3f7ffe1cu, 0xbf7ffed5u, - 0x3be301ffu, 0xbf7fff81u, 0x3f7ffe9fu, 0xbf7ffe15u, 0x3f7ffe5au, 0xbf7fff89u, - 0x3f7fff1du, 0xbf7ffea1u, 0x3f7fff4cu, 0xbbd19303u, 0xbf7ffe54u, 0x3f7ffe82u, - 0x3f7fff9du, 0xbf7ffe91u, 0x3f7ffebdu, 0x3f7ffe51u, 0x3f7ffe47u, 0xbf7ffe2fu, - 0x3f7ffe5fu, 0xbf7ffe21u, 0x3f7fff4du, 0x3f7fff56u, 0x3f7ffe5du, 0xbf7ffec6u, - 0x3f7ffea6u, 0x3f7ffe2du, 0x3b864477u, 0xbf7fff91u, 0x3f7ffe57u, 0xbf7ffe7cu, - 0x3f7fff37u, 0x3f7fff01u, 0xbf7ffe08u, 0xbf7ffe20u, 0xbf7ffe3eu, 0x3f7ffe99u, - 0xbf7ffe7du, 0x3f7ffe41u, 0x3f7ffe92u, 0xbf7ffe03u, 0x3f7ffe19u, 0xbf7ffe6fu, - 0xbf7ffe0du, 0xbf7ffeb3u, 0x3bfa1017u, 0x3bbbf406u, 0x3f7ffee6u, 0x3b75e843u, - 0xbf7ffec2u, 0xbf7ffe93u, 0x3f7ffe31u, 0x3f7ffe06u, 0x3f7ffeefu, 0x3f7ffe10u, - 0xbf7fff0au, 0xbf7ffec0u, 0xbbebe7beu, 0x3f7ffe76u, 0x3bf80d44u, 0xbf7ffe13u, - 0x3f7ffe2bu, 0xbf7ffe63u, 0x3f7ffe33u, 0xbf7ffe87u, 0x3f7fff9cu, 0xbf7ffe24u, - 0xbbfccf4fu, 0x3f7ffe43u, 0xbf7ffe39u, 0xbbe8b69fu, 0x3f7ffe53u, 0xbf7ffe14u, - 0x3bed7eb2u, 0x3bebd3a0u, 0x3f7ffe02u, 0x3f7ffe86u, 0x3f7ffe17u, 0xbbb2531fu, - 0xbf7fff1bu, 0x3f7ffe4au, 0xbf7ffe5eu, 0x3bfd02d2u, 0x3f7ffea4u, 0x3f7ffe2au, - 0xbbe05ccdu, 0x3f7ffe6cu, 0x3f7ffe04u, 0x3f7ffebbu, 0x3f7fff16u, 0x3bdac638u, - 0x3f7fff29u, 0xbf7ffe2cu, 0x3f7ffe64u, 0x3be71c74u, 0x3f7ffe01u, 0x3bec65c0u, - 0x3f7ffedbu, 0xbf7ffec1u, 0x3f7fffb0u, 0xbf7ffe01u, 0x3bdb4d71u, 0xbf7fff29u, - 0x3bc57d97u, 0xbf7ffebbu, 0xbf7fff85u, 0xbf7ffea4u, 0x3bf34a64u, 0xbf7ffe74u, - 0x3f7ffe1fu, 0xbf7ffe2bu, 0xbf7ffe10u, 0xbf7ffeefu, 0xbf7ffe62u, 0xbf7ffe26u, - 0xbf7ffeabu, 0xbf7ffe26u, 0xbf7ffe8cu, 0x3f7ffe0du, 0xbf7fff90u, 0xbf7ffebfu, - 0xbbba5879u, 0x3f7ffe01u, 0xbf7fff48u, 0xbbed33e1u, 0x3f7ffe74u, 0xbf7ffe1fu, - 0x3f7ffe2bu, 0x3f7ffe5cu, 0x3f7ffe62u, 0x3f7ffe9fu, 0xbf7ffe48u, 0xbf7ffe95u, - 0x3f7ffe20u, 0x3f7fff25u, 0x3f7ffe7au, 0x3f7ffe6eu, 0xbf7ffe88u, 0x3be18f99u, - 0x3f7ffe52u, 0x3f7ffe68u, 0xbf7ffe4eu, 0xbf7ffe29u, 0x3f7ffe74u, 0xbf7ffe1fu, - 0x3bf46abbu, 0x3f7ffe8au, 0xbf7ffe58u, 0x3f7fffbfu, 0x3bf0474bu, 0x3bf952a0u, - 0x3f7ffe4fu, 0xbf7ffed1u, 0xbf7ffe10u, 0x3f7fffa9u, 0xbf7ffe21u, 0x3f7ffe1eu, - 0x3f7ffe30u, 0xbf7fff46u, 0x3ba8de0eu, 0x3f7ffe5cu, 0x3f7ffe90u, 0x3f7ffed5u, - 0xbf7ffe48u, 0xbf7fff54u, 0xbbd37a4du, 0xbbf5db83u, 0xbf7fff0du, 0x3f7ffe83u, - 0xbf7fffacu, 0x3bfb6608u, 0xbf7ffee0u, 0x3bf463fau, 0x3f7ffe3cu, 0xbf7fff13u, - 0xbf7ffe2du, 0xbf7ffebdu, 0xbbd52a3au, 0x3f7ffe31u, 0x3f7ffe96u, 0x3f7ffe16u, - 0xbf7ffe66u, 0xbf7ffea7u, 0xbf7ffe1du, 0x3f7ffe0cu, 0x3f7ffecfu, 0xbbe75d5bu, - 0xbf7ffe3fu, 0xbf7ffe78u, 0xbf7fff28u, 0x3f7ffe4du, 0xbbd1d44au, 0xbf7ffe18u, - 0x3f7ffe6bu, 0x3f7ffe13u, 0x3f7ffe63u, 0x3f7ffe49u, 0x3bd83a7cu, 0xbf7ffe87u, - 0xbf7ffe2eu, 0xbf7ffe43u, 0x3f7ffe5eu, 0xbf7ffe98u, 0xbf7ffe36u, 0xbbc32099u, - 0x3f7ffe5fu, 0xbf7fff09u, 0xbf7ffe14u, 0xbf7ffe6du, 0xbf7ffeb5u, 0xbf7ffea8u, - 0xbf7ffe46u, 0xbf7ffe37u, 0xbf7ffe42u, 0x3bab9830u, 0x3f7ffef3u, 0xbf7ffe07u, - 0xbf7ffe50u, 0x3bbbf8e1u, 0xbf7ffe11u, 0x3f7ffe67u, 0x3f7fff58u, 0xbf7ffee9u, - 0x3f7ffe64u, 0x3f7ffe39u, 0x3f7ffedbu, 0x3f7ffee6u, 0x3f7fff16u, 0xbf7ffe6cu, - 0x3f7fff75u, 0x3f7fff12u, 0x3bd43491u, 0x3f7fff0eu, 0xbbcb8eb7u, 0x3f7ffe05u, - 0xbf7ffe03u, 0x3f7ffe23u, 0x3f7ffe08u, 0xbbffa04du, 0x3f7ffe53u, 0x3f7ffeebu, - 0xbbf83111u, 0xbf7ffea5u, 0xbf7ffeccu, 0xbbe2066au, 0x3f7fff47u, 0xbbe51b69u, - 0xbf7ffe52u, 0x3bd76dc1u, 0xbf7ffe3bu, 0xbf7ffe0du, 0xbf7ffe68u, 0xbf7ffea4u, - 0xbf7ffe2bu, 0xbf7ffe01u, 0x3f7ffe48u, 0xbf7ffe83u, 0xbf7ffe6eu, 0xbf7ffe61u, - 0x3f7fff26u, 0xbbd49b26u, 0xbf7ffe56u, 0xbbd74f9bu, 0x3f7ffe06u, 0x3f7fff2cu, - 0xbf7ffe7eu, 0xbf7ffe36u, 0x3f7ffed4u, 0x3bf33edau, 0x3f7fff62u, 0xbf7fff4fu, - 0xbbe8c65du, 0x3f7ffe16u, 0xbf7ffe73u, 0x3f7ffe60u, 0xbf7ffeb0u, 0xbf7ffe57u, - 0x3bccd71au, 0xbbe7abadu, 0xbf7ffe32u, 0x3f7ffe50u, 0x3f7ffe07u, 0x3f7ffe42u, - 0x3f7ffe87u, 0xbf7ffe31u, 0xbf7fff02u, 0x3f7ffebdu, 0xbf7ffe20u, 0xbf7ffe30u, - 0x3f7fff49u, 0xbf7ffe2fu, 0x3f7fff92u, 0xbf7fff1au, 0xbbdbb3a5u, 0xbf7ffedbu, - 0xbf7fff5au, 0x3f7ffecau, 0x3f7ffeceu, 0x3f7ffe0bu, 0x3bd73175u, 0x3f7ffe3au, - 0x3f7ffe85u, 0xbf7ffe7du, 0x3f7ffe47u, 0x3befea18u, 0x3f7ffeb1u, 0xbf7ffef7u, - 0xbf7ffec8u, 0xbf7ffea3u, 0x3bc2f979u, 0xbf7ffe0du, 0xbf7ffe84u, 0xbf7ffe6bu, - 0xbf7fff9bu, 0x3bfc2824u, 0x3f7ffe08u, 0x3f7ffeedu, 0xbf7ffe6cu, 0x3f7ffe39u, - 0x3f7ffeaau, 0xbf7ffe26u, 0x3f7ffee7u, 0xbbdfc7b3u, 0xbf7ffe21u, 0x3bff0f44u, - 0x3f7ffe13u, 0x3f7ffe0fu, 0x3f7ffe71u, 0x3f7ffe38u, 0x3f7ffea0u, 0xbf7fff38u, - 0x3bc3d4f5u, 0xbf7ffea1u, 0x3f7ffeaeu, 0x3f7ffe2eu, 0x3f7fff44u, 0xbf7ffed0u, - 0xbf7ffe9bu, 0x3f7ffeb7u, 0x3f7ffed6u, 0x3f7ffe8bu, 0xbf7ffef2u, 0x3f7ffe35u, - 0x3f7ffe65u, 0x3bdf36c6u, 0x3f7ffe4eu, 0xbf7fff28u, 0x3f7fff85u, 0x3f7ffe40u, - 0xbf7ffe04u, 0x3f7fff30u, 0x3bfcb1f6u, 0xbf7ffe6au, 0x3f7ffe5cu, 0x3bf7499eu, - 0xbf7ffee5u, 0x3f7ffeadu, 0xbbfb626au, 0xbf7ffe1eu, 0xbf7ffe5cu, 0x3f7ffe04u, - 0xbf7ffe8au, 0xbf7ffe8bu, 0xbf7ffed7u, 0xbf7ffed6u, 0xbf7ffe9fu, 0xbf7fff56u, - 0xbbf6a04fu, 0xbf7ffe2eu, 0x3f7ffea1u, 0xbf7ffe38u, 0xbf7ffee7u, 0xbf7ffeaau, - 0xbf7fff06u, 0xbf7ffe60u, 0xbf7fff05u, 0xbf7ffe53u, 0x3f7ffea4u, 0x3f7ffe83u, - 0x3f7ffe8cu, 0x3f7ffe98u, 0x3f7ffe57u, 0xbf7ffe0bu, 0xbf7ffe1du, 0x3f7ffe72u, - 0xbf7ffed8u, 0x3f7ffe04u, 0xbf7ffeadu, 0x3bf3ed98u, 0xbf7fff2eu, 0x3f7ffe8au, - 0xbf7ffe3bu, 0x3f7ffe9fu, 0xbf7fff4fu, 0xbf7fff36u, 0xbf7ffe90u, 0xbf7ffe1fu, - 0x3f7ffef9u, 0x3bd58e0fu, 0xbbf7061du, 0xbf7ffe27u, 0x3f7ffe0fu, 0x3f7ffec0u, - 0xbf7ffec5u, 0x3f7ffe76u, 0x3f7ffedbu, 0x3f7fff3eu, 0x3f7ffe5bu, 0xbf7ffe67u, - 0xbf7fff40u, 0xbf7ffe58u, 0x3f7ffe41u, 0x3f7ffe89u, 0x3f7ffe51u, 0x3f7ffe42u, - 0x3f7ffe72u, 0xbf7ffed5u, 0x3f7ffe64u, 0xbba2183fu, 0xbf7ffe20u, 0xbf7fff10u, - 0x3f7ffec7u, 0xbf7fff8fu, 0x3f7fff94u, 0x3f7ffe0au, 0x3be862fdu, 0xbf7ffe05u, - 0xbbd25d2eu, 0x3f7ffe17u, 0xbf7fffcau, 0x3f7ffe29u, 0x3f7ffe4du, 0x3f7fff46u, - 0x3f7ffe5du, 0xbbe61c1du, 0x3f7ffeb9u, 0x3f7ffe8du, 0xbf7ffe46u, 0xbf7ffe7au, - 0x3f7ffe37u, 0x3f7fff29u, 0xbf7ffef7u, 0xbf7ffeb0u, 0xbf7ffe14u, 0xbf7fff38u, - 0x3bb68fbbu, 0xbbe215fdu, 0x3f7ffeabu, 0x3f7ffebeu, 0x3be34866u, 0xbf7fff6cu, - 0xbbf49698u, 0x3f7ffe7du, 0xbf7ffeebu, 0x3bf560ccu, 0xbf7fff03u, 0xbf7ffe0eu, - 0x3f7ffefau, 0x3f7fff4cu, 0x3f7ffe23u, 0x3f7ffe02u, 0x3f7fff25u, 0x3f7ffebau, - 0xbf7ffe30u, 0x3f7fff39u, 0x3f7ffe2au, 0x3f7ffe34u, 0xbf7fff60u, 0xbbe968bdu, - 0x3f7ffe78u, 0x3f7ffe55u, 0xbf7ffe6du, 0x3f7ffe32u, 0x3f7ffeb7u, 0x3f7fff34u, - 0x3f7ffe87u, 0xbbdb4667u, 0x3f7ffe71u, 0x3f7ffe6au, 0xbf7ffeaeu, 0xbf7ffe01u, - 0xbf7ffedau, 0x3bccc2d0u, 0x3bd875e9u, 0xbf7ffe3au, 0x3f7ffeacu, 0x3f7ffe9eu, - 0xbf7ffe73u, 0xbf7ffe4bu, 0xbf7fffb1u, 0xbf7ffe3cu, 0x3f7fff5fu, 0x3f7ffe1bu, - 0xbf7ffe08u, 0x3f7ffe70u, 0x3f7ffe62u, 0x3f7fff19u, 0xbbf9dcacu, 0xbf7ffec7u, - 0x3f7ffe0bu, 0xbbbc90aau, 0x3f7ffe38u, 0x3f7fff47u, 0xbbeb7d28u, 0x3f7ffe27u, - 0xbf7fff77u, 0x3f7ffed0u, 0x3f7ffe9bu, 0xbf7ffec0u, 0x3f7ffe3fu, 0xbf7ffe80u, - 0xbf7ffe0cu, 0xbf7ffedcu, 0x3f7ffe26u, 0xbf7ffec6u, 0xbf7ffe40u, 0xbf7ffe6cu, - 0x3f7fff66u, 0xbf7ffe69u, 0xbbd33ba8u, 0x3befda70u, 0x3f7ffe63u, 0xbbb96f75u, - 0xbf7ffef5u, 0x3f7ffe98u, 0xbf7ffe53u, 0xbf7ffe5cu, 0x3f7ffea4u, 0x3f7fff28u, - 0xbbf0f31fu, 0xbf7ffe97u, 0xbbc104a1u, 0xbf7ffe6fu, 0xbf7ffe4fu, 0xbf7ffe07u, - 0x3f7ffeabu, 0xbf7ffe35u, 0x3f7ffe44u, 0xbbd6362bu, 0xbf7ffe92u, 0x3f7ffecdu, - 0xbf7ffe24u, 0xbbec5822u, 0x3f7ffee0u, 0x3f7fff1au, 0x3f7fff93u, 0x3f7ffe6eu, - 0xbf7ffe2cu, 0x3bcf9ae3u, 0x3f7fff22u, 0x3bc8bbffu, 0x3f7ffe79u, 0x3f7ffe12u, - 0xbf7ffe0eu, 0x3f7ffe39u, 0x3f7ffeeeu, 0xbbf583e3u, 0xbf7fff92u, 0x3f7fff55u, - 0xbf7ffe90u, 0xbbd38c55u, 0xbf7ffec4u, 0x3f7ffe19u, 0xbf7ffe2au, 0x3f7fff14u, - 0x3f7ffe3bu, 0x3bca97afu, 0xbbff5d44u, 0xbf7ffe76u, 0xbf7ffe29u, 0x3f7ffe0eu, - 0xbf7ffe34u, 0xbbaa7ba5u, 0xbf7ffeb5u, 0xbf7ffefcu, 0xbf7fff8eu, 0xbf7ffe44u, - 0xbf7ffec3u, 0x3f7ffe7cu, 0xbf7fffb9u, 0xbf7ffe7du, 0x3f7fff01u, 0xbf7ffe56u, - 0xbb786da6u, 0x3f7fff0bu, 0x3f7ffef1u, 0x3f7ffe78u, 0xbbdbb4fdu, 0x3f7ffee8u, - 0x3f7fff17u, 0xbf7ffef8u, 0x3f7ffe8fu, 0x3f7ffe53u, 0x3f7ffe54u, 0x3f7fff29u, - 0xbbef5095u, 0xbbf1d52au, 0x3f7ffebcu, 0x3f7fff32u, 0xbf7ffe10u, 0x3f7ffe06u, - 0xbbc05684u, 0x3f7ffe64u, 0xbf7ffeddu, 0x3f7ffe2fu, 0xbf7ffe49u, 0xbb8ef147u, - 0xbf7fff0eu, 0xbf7ffe59u, 0x3b963693u, 0xbf7fff04u, 0x3f7ffe48u, 0xbf7fff23u, - 0xbf7ffefdu, 0x3f7ffe41u, 0xbf7ffe47u, 0xbf7ffe5eu, 0x3f7ffe96u, 0x3f7ffe57u, - 0x3f7ffe08u, 0xbbd6d576u, 0xbf7ffe1bu, 0xbf7ffefeu, 0xbf7ffec8u, 0xbf7ffeb6u, - 0xbbf5be44u, 0xbf7ffe16u, 0xbf7ffe5fu, 0x3f7ffe85u, 0xbf7ffe88u, 0x3be3d2d6u, - 0x3f7ffe11u, 0x3f7ffe22u, 0xbf7fff73u, 0x3f7fff49u, 0x3f7ffe73u, 0xbf7ffe72u, - 0xbbeab9c7u, 0xbf7ffe98u, 0x3f7ffe6fu, 0x3f7ffeb0u, 0xbbfe5a6fu, 0xbf7ffe8eu, - 0x3f7ffe09u, 0xbf7ffee1u, 0xbf7ffedbu, 0xbf7fff62u, 0xbbf3ae61u, 0x3f7ffe17u, - 0xbf7ffe23u, 0x3f7ffe3fu, 0x3f7ffef3u, 0xbf7ffe8bu, 0xbb947064u, 0xbf7ffe3cu, - 0x3f7ffe8au, 0x3f7ffe9bu, 0x3f7ffeceu, 0x3f7ffee3u, 0x3f7ffe79u, 0xbf7ffe89u, - 0xbf7fff65u, 0x3f7ffe70u, 0x3f7ffe39u, 0x3bfc12f2u, 0xbf7fff6du, 0xbf7ffe63u, - 0xbf7ffe61u, 0x3f7ffed9u, 0xbf7ffe3bu, 0x3bf21694u, 0x3f7ffefcu, 0x3bf1ab59u, - 0x3f7ffe28u, 0x3f7ffee4u, 0x3f7ffe15u, 0x3f7ffe2eu, 0x3f7ffed8u, 0xbf7ffe05u, - 0xbf7ffe58u, 0xbf7ffe0cu, 0x3f7ffef9u, 0x3bfd94e6u, 0xbf7ffe3au, 0xbf7ffe6au, - 0xbf7ffe02u, 0xbf7ffe15u, 0xbf7ffe94u, 0xbf7ffe27u, 0x3f7fffdbu, 0xbf7fff02u, - 0x3be4cda4u, 0x3ba6866au, 0xbf7ffe39u, 0xbf7ffe56u, 0x3be5c7a0u, 0xbf7ffe24u, - 0xbf7ffe4cu, 0x3f7ffeaau, 0x3f7ffeedu, 0x3f7fff15u, 0xbf7fff81u, 0xbf7ffe1fu, - 0xbf7ffe22u, 0x3f7fff44u, 0x3f7fff1fu, 0xbf7ffea3u, 0x3f7ffe82u, 0x3f7fff07u, - 0xbf7fff3eu, 0xbf7ffeebu, 0x3bfee7a8u, 0xbf7ffe12u, 0xbf7fff19u, 0x3f7ffe0fu, - 0xbbbaa8e9u, 0xbf7ffe0bu, 0x3f7ffe6cu, 0xbbe47087u, 0xbf7ffe97u, 0xbf7ffebdu, - 0xbbd313f2u, 0x3bfff543u, 0x3f7ffeadu, 0xbf7fff3fu, 0xbf7fff3du, 0xbf7ffe28u, - 0x3f7ffe86u, 0xbf7ffe2du, 0x3f7fff20u, 0xbf7fff74u, 0x3f7ffeb4u, 0xbf7ffe9du, - 0xbbc51e53u, 0xbf7ffe8fu, 0x3f7fff5du, 0x3f7ffe4bu, 0x3f7ffe20u, 0xbf7ffee2u, - 0xbf7ffeb5u, 0xbf7ffe07u, 0x3f7fff11u, 0x3f7ffe52u, 0xbf7ffe84u, 0x3f7ffe4au, - 0xbbd79d51u, 0x3f7ffe5fu, 0xbf7ffe08u, 0xbf7ffe96u, 0xbf7ffe53u, 0x3f7ffe10u, - 0xbf7ffe2fu, 0x3f7ffe49u, 0x3f7ffec8u, 0x3f7ffeb6u, 0x3f7ffe16u, 0xbf7ffe85u, - 0xbf7fffa3u, 0x3f7ffe72u, 0xbf7ffeb0u, 0xbf7ffe09u, 0x3f7ffe65u, 0xbf7ffe04u, - 0xbf7ffecdu, 0xbf7ffe34u, 0x3f7ffe19u, 0xbf7fff42u, 0xbf7ffe0au, 0xbf7ffe99u, - 0xbf7ffe43u, 0x3f7ffe3eu, 0x3f7ffe5eu, 0x3f7ffe6fu, 0xbf7ffe1cu, 0xbf7ffe2au, - 0x3ba36c51u, 0x3f7ffea0u, 0x3f7ffe14u, 0x3f7ffe67u, 0x3f7ffeccu, 0x3f7ffe21u, - 0xbbb68309u, 0x3f7ffef7u, 0x3f7ffe45u, 0xbf7fff66u, 0xbf7ffe26u, 0x3f7ffeaeu, - 0x3f7ffe78u, 0xbf7ffeddu, 0xbf7ffedbu, 0xbf7fff1eu, 0x3f7ffee5u, 0x3baf7ce0u, - 0xbf7ffe4eu, 0xbbecce7eu, 0xbf7ffe41u, 0x3f7ffe29u, 0xbf7ffe66u, 0x3bfd1b99u, - 0xbf7ffe46u, 0xbf7fff3au, 0xbf7fff04u, 0x3f7ffed8u, 0x3f7ffe28u, 0x3bcb2818u, - 0xbf7ffe3cu, 0x3f7ffe0bu, 0x3f7ffe8cu, 0xbf7ffed9u, 0x3f7ffe05u, 0x3f7fff36u, - 0x3f7ffe27u, 0x3be23979u, 0xbf7ffeeeu, 0xbf7ffe13u, 0x3f7ffe73u, 0xbf7ffe1bu, - 0x3f7fff0au, 0x3f7ffe2bu, 0xbf7ffe70u, 0x3f7ffe6du, 0x3f7fffcfu, 0xbbc25a53u, - 0xbbfffd73u, 0x3f7ffe8du, 0x3f7ffe48u, 0x3f7ffe4du, 0xbf7fff90u, 0x3f7ffedcu, - 0xbf7ffe3bu, 0xbf7ffe95u, 0x3f7ffe20u, 0xbf7fff2eu, 0xbf7ffe08u, 0xbf7ffe37u, - 0xbbef1443u, 0x3f7ffeb2u, 0x3f7ffe07u, 0x3beb0b78u, 0xbf7ffe38u, 0x3f7ffe2eu, - 0x3becb5d6u, 0xbbd2fbafu, 0x3be5546au, 0xbf7ffefdu, 0xbf7ffe4cu, 0x3f7ffe33u, - 0xbf7fff17u, 0x3f7ffe18u, 0x3bc1ee22u, 0xbf7ffe75u, 0x3f7ffe68u, 0x3f7ffe83u, - 0xbf7ffeb3u, 0x3f7ffefcu, 0xbf7ffe8cu, 0xbf7ffe0bu, 0xbf7ffe67u, 0x3bb5a58eu, - 0xbf7ffeaau, 0xbf7ffe3eu, 0xbf7ffe5eu, 0x3f7fff3au, 0x3f7ffe31u, 0xbf7ffe05u, - 0xbf7ffe27u, 0x3f7ffeeeu, 0x3f7ffe54u, 0x3f7ffe2cu, 0xbbf08a74u, 0xbf7ffef0u, - 0x3f7ffe12u, 0xbf7fff57u, 0xbf7fff29u, 0x3f7ffe4au, 0x3be87507u, 0xbf7ffe3bu, - 0x3f7ffedcu, 0x3bfb26e7u, 0x3f7ffe48u, 0xbf7fff6cu, 0x3f7ffeb4u, 0x3f7ffe25u, - 0x3f7ffe5cu, 0xbf7ffe42u, 0xbf7ffe41u, 0xbf7ffe46u, 0xbf7ffea9u, 0xbf7fff78u, - 0xbf7fff5bu, 0xbf7ffe13u, 0xbf7ffe1eu, 0x3f7ffe87u, 0xbf7ffe23u, 0x3f7ffeedu, - 0xbf7ffe97u, 0x3f7ffe66u, 0xbf7ffe64u, 0x3f7ffe58u, 0x3f7fff59u, 0x3f7fff1eu, - 0x3f7fff2cu, 0xbf7ffe04u, 0x3f7ffec5u, 0x3f7fff33u, 0x3f7fff0bu, 0xbf7ffe08u, -}; -#endif diff --git a/github-simulator/source/math/verification.md b/github-simulator/source/math/verification.md deleted file mode 100644 index 3ee34abedc4fd1b0d136cb5176b997d2a69426ee..0000000000000000000000000000000000000000 --- a/github-simulator/source/math/verification.md +++ /dev/null @@ -1,34 +0,0 @@ -# Math profile verification — 2026-10-06 - -This implementation extends the NVIDIA support merged in [PR #11](https://github.com/Zorro909/driving_sim_rs/pull/11) with portable Proton and Windows UCRT math profiles. All three profiles remain available in a single native library and WASM package. WebGPU specializes the shared generated kernel source for a session before compiling its pipelines. - -Checkpoint validation and population imports preserve the selected profile. Asynchronous browser initialization completes default-profile detection before resolving. WebGPU specializes profile dispatch and prunes unused tables; shader sign/selection operations preserve subnormal and negative-zero bits. No profile-specific copies of the kernel source were introduced. - -| Check | Result | -| --- | --- | -| Linux x86_64 release suite | 190 passed, zero failures; six existing optional/exhaustive tests ignored | -| Debug suite and strict native clippy | Passed | -| ARM64 Linux musl under QEMU | All seven primitive fixture functions and the profile/RNG/checkpoint continuation matrix passed | -| Native/WASM CPU | All three profiles: 2,773 values each, zero mismatches | -| Browser automatic detection | Eight cases passed for both serial and threaded packages; Windows 10, Windows 11 before/after 24H2, missing/denied/invalid hints, and synchronous followed by asynchronous initialization | -| Serial/threaded worker simulation | Two actual Rayon workers; 253,341 values per scheduling mode, zero serial/threaded mismatches | -| HIP, AMD gfx1100 | Rebuilt library; all-profile math checks passed, with 4,194,304 base inputs per primitive/profile and trig exception keys; Windows simulation/inference/evolution checks passed | -| CUDA, RTX 3060 Ti | 79,304,139 primitive comparisons across profiles and 5,781,863 simulation comparisons per profile, zero mismatches | -| Full WebGPU, AMD RDNA 3 | Formula and mixed Rally scenarios for every profile: 410,858 total comparisons, zero mismatches | -| Captured WGSL primitive fixtures | 30,000 raw-bit comparisons across all profiles on both AMD and SwiftShader, zero mismatches; includes atan2f, exp, pow, tanh | -| WGSL binary64 | 200,000 cases for each of 22 operations, including fused multiply-add and cancellation; zero mismatches on SwiftShader | -| Generated code and shader validation | Rust/table/shader generators current; complete shaders and all 21 specialized pipelines pass Naga 30.0.1 and SPIR-V translation | -| Build/lint compatibility | MSRV 1.93, Windows GNU and ARM macOS/Linux cross-checks; strict serial/diagnostic/threaded WASM clippy, documentation, formatting, workflow parsing passed | - -Final serial and threaded distribution packages are in `wasm/pkg/` and `wasm/pkg-threads/`, with matching source fingerprints. Diagnostic packages and browser reports are under `target/wasm-thread-test/`, `target/math-profile-debug/`, and `target/math-profile-test/`. - -CUDA used a single all-profile Linux library with CUDA 13.0.88 and driver 580.173.02. The temporary GPU instance was destroyed after verification. Local logs, the library, source snapshot and reproduction script are preserved in `target/cuda-verification/`; build artifacts and raw execution logs are excluded from the repository. - -Known boundaries remain explicit: - -- Full simulator pipeline compilation stalls/fails on the tested SwiftShader browser backend, including the unmodified PR #11 forward shader. Full simulation was verified on the AMD hardware adapter; all profile primitive fixtures and binary64 operations passed on SwiftShader. -- Expanded native/WASM observations retain the documented host-math differences in Python initialization: 2,285 differences per mode in this fixture. Serial and threaded WASM match each other exactly. See [fidelity notes](../docs/fidelity.md). -- GPU trig inputs outside the supported range are reported as fallback flags and are excluded from exact device comparisons. CUDA recorded 4,590,875 such flags alongside its primitive checks. -- ARM runtime verification used QEMU. Windows/macOS native runtime execution and Windows CUDA DLL compilation were not exercised locally. Windows browsers without version hints use the documented `win11-fma3` fallback. - -See [math sources and fixture provenance](README.md), [WASM commands](../wasm/README.md), and [GPU commands](../gpu/README.md) to reproduce the checks. diff --git a/github-simulator/source/src/bin/altd-sim/args.rs b/github-simulator/source/src/bin/altd-sim/args.rs deleted file mode 100644 index 7272932fd33cc22f8a6502fb37815eb0a1ced1ed..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/bin/altd-sim/args.rs +++ /dev/null @@ -1,408 +0,0 @@ -//! Argument parsing and values shared by the CLI commands. - -use super::io::load; -use altd_sim::math::profile::MathProfile; -use altd_sim::track::training_tracks::RandomTrainingTrackSettings; -use altd_sim::training::evolution::RewardSpec; -use altd_sim::training::Mode; -use clap::{Args, Parser, Subcommand, ValueEnum}; -use serde_json::Value; -use std::path::PathBuf; - -#[derive(Parser)] -#[command( - name = "altd-sim", - about = "CPU game-fidelity simulator and trainer for AI Learns To Drive", - version = super::platform::VERSION -)] -pub(super) struct Cli { - /// Worker threads (default: all logical CPUs in the affinity mask). - #[arg(long, global = true)] - pub(super) threads: Option, - /// Training execution: each car runs a whole window independently, or all - /// cars advance one tick at a time like `TrainingRunner.step`. - #[arg(long, global = true, value_enum, default_value_t = ModeArg::Independent)] - pub(super) mode: ModeArg, - /// The C runtime math to reproduce: proton (Linux and macOS through - /// Proton), win10-fma3 (Windows 10, Windows 11 up to 23H2) or win11-fma3 - /// (Windows 11 24H2 and later). Default: this machine's; train-scratch - /// --resume and --init-population keep the checkpoint's. - #[arg(long, global = true, value_name = "PROFILE")] - pub(super) math_profile: Option, - #[command(subcommand)] - pub(super) command: Command, -} - -#[derive(Clone, Copy, ValueEnum)] -pub(super) enum ModeArg { - Independent, - Lockstep, -} - -#[derive(Clone, Copy, PartialEq, ValueEnum)] -pub(super) enum TrackMode { - Fixed, - Random, -} - -#[derive(Args)] -pub(super) struct TrackOptions { - /// Use the scene's fixed track or a fresh CPU-generated track each generation. - #[arg(long, value_enum, default_value_t = TrackMode::Fixed)] - pub(super) track_mode: TrackMode, - /// JSON TrackFactory settings with fixed values, min/max lengths, or choices. - #[arg(long)] - pub(super) random_track_settings: Option, - /// Number of prepared tracks queued by the CPU producer, including with --gpu. - #[arg(long, default_value_t = 8)] - pub(super) track_buffer_size: usize, - /// Evaluate the unchanged population on this many random tracks before breeding. - /// Resume restores the checkpoint value when omitted. - #[arg(long)] - pub(super) tracks_per_generation: Option, -} - -impl TrackOptions { - pub(super) fn count(&self, checkpoint: Option<&Value>) -> usize { - let count = self.tracks_per_generation.unwrap_or_else(|| { - checkpoint - .and_then(|value| value["tracks_per_generation"].as_u64()) - .unwrap_or(1) as usize - }); - assert!(count > 0, "--tracks-per-generation must be positive"); - assert!( - self.track_mode == TrackMode::Random || count == 1, - "--tracks-per-generation above 1 requires --track-mode random" - ); - count - } - pub(super) fn settings(&self) -> Option { - if self.track_mode == TrackMode::Fixed { - assert!( - self.random_track_settings.is_none(), - "--random-track-settings requires --track-mode random" - ); - return None; - } - let settings: RandomTrainingTrackSettings = self - .random_track_settings - .as_deref() - .map(|path| serde_json::from_value(load(path)).unwrap_or_else(|e| panic!("{}: {e}", path.display()))) - .unwrap_or_default(); - settings - .validate() - .unwrap_or_else(|e| panic!("--random-track-settings: {e}")); - assert!(self.track_buffer_size > 0, "--track-buffer-size must be positive"); - Some(settings) - } -} - -impl From for Mode { - fn from(mode: ModeArg) -> Mode { - match mode { - ModeArg::Independent => Mode::Independent, - ModeArg::Lockstep => Mode::Lockstep, - } - } -} - -/// Mutation rate interpolation between `--mutation-start` and `--mutation-end`. -#[derive(Clone, Copy, ValueEnum, PartialEq)] -pub(super) enum Schedule { - /// Constant ratio per generation (equal time per halving). - Geometric, - Linear, -} - -#[derive(Clone, Copy, ValueEnum)] -pub(super) enum ScratchReward { - /// Distance along the track (total_score). - Distance, - /// Faster completed laps rank higher (best_lap_performance). - BestLapTime, -} - -impl ScratchReward { - pub(super) fn spec(self) -> RewardSpec { - let metric = match self { - Self::Distance => "total_score", - Self::BestLapTime => "best_lap_performance", - }; - RewardSpec { - metric: metric.into(), - weight: 100, - kind: "default".into(), - } - } -} - -/// Metric watched by `--stop-plateau`. -#[derive(Clone, Copy, ValueEnum, PartialEq)] -pub(super) enum PlateauMetric { - /// Fastest completed lap of a generation. - Lap, - /// Best total_score of a generation. - Score, -} - -#[derive(Subcommand)] -pub(super) enum Command { - /// Time training ticks and reproduction for a population. - Bench { - #[arg(long)] - scene: PathBuf, - #[arg(long)] - network: PathBuf, - /// Vehicle model JSON (default: the built-in copy of assets/models/rally.json). - #[arg(long)] - model: Option, - #[arg(long)] - spawn_trace: PathBuf, - #[arg(long, default_value_t = 1000)] - population: usize, - #[arg(long, default_value_t = 60)] - ticks: u64, - #[arg(long, default_value_t = 2)] - warmup_ticks: u64, - #[arg(long, default_value_t = 7)] - seed: i64, - #[arg(long, default_value_t = 0)] - spawn_index: usize, - #[arg(long, default_value_t = 2)] - batch_count: usize, - #[arg(long)] - report: Option, - /// Write every car's final state, metrics, and the next generation's - /// networks for deterministic comparisons. - #[arg(long)] - dump_state: Option, - }, - /// Run and evolve imported networks for several generations. - Train { - #[command(flatten)] - tracks: TrackOptions, - #[arg(long)] - scene: PathBuf, - #[arg(long)] - network: PathBuf, - #[arg(long)] - model: PathBuf, - #[arg(long, required_if_eq("track_mode", "fixed"))] - spawn_trace: Option, - #[arg(long)] - output: PathBuf, - #[arg(long)] - settings: Option, - #[arg(long)] - population: Option, - #[arg(long, default_value_t = 2)] - generations: usize, - #[arg(long)] - ticks: Option, - #[arg(long, default_value_t = 7)] - seed: i64, - /// Exact game RNG state JSON containing independent decisions and normals streams. - #[arg(long)] - game_rng_state: Option, - #[arg(long, default_value_t = 2)] - batch_count: usize, - #[arg(long, overrides_with = "no_eliminate_on_wall")] - eliminate_on_wall: bool, - #[arg(long)] - no_eliminate_on_wall: bool, - #[arg(long, overrides_with = "no_idle_eliminate")] - idle_eliminate: bool, - #[arg(long)] - no_idle_eliminate: bool, - }, - /// Train a network from a random Xavier initialization with a decaying - /// mutation rate, logging lap times each generation and saving the network - /// behind every new best lap. Defaults to Rally sensor and output metadata. - TrainScratch { - #[command(flatten)] - tracks: TrackOptions, - #[arg(long)] - scene: PathBuf, - /// Trace whose first frame is the spawn pose. - #[arg(long, required_if_eq("track_mode", "fixed"))] - spawn_trace: Option, - /// Network export supplying the input sensor and output control names; its weights are unused - /// (default: the built-in copy of assets/networks/rally.json). - #[arg(long)] - network: Option, - /// Vehicle model JSON (default: the built-in copy of assets/models/rally.json). - #[arg(long)] - model: Option, - /// Run directory: log, best-lap networks, and checkpoints. - #[arg(long, default_value = "runs/example")] - out_dir: PathBuf, - #[arg(long, value_delimiter = ',', default_value = "20,16,16,16,16,12,12,8,5")] - shape: Vec, - #[arg(long, default_value_t = 8192)] - population: usize, - #[arg(long, default_value_t = 50000)] - generations: usize, - /// Simulated ticks per generation (60 per second). - #[arg(long, default_value_t = 5400)] - ticks: u64, - /// Mutation rate of generation 0; adaptive mutation then scales it by network shape. - #[arg(long, default_value_t = 0.4)] - mutation_start: f64, - /// Mutation rate of the last generation. - #[arg(long, default_value_t = 0.0125)] - mutation_end: f64, - #[arg(long, value_enum, default_value_t = Schedule::Geometric)] - schedule: Schedule, - /// Evolution settings JSON overriding the generalist defaults (population and mutation rate excluded). - #[arg(long)] - settings: Option, - /// Selection reward. Overrides settings-file rewards; otherwise defaults to distance. - #[arg(long, value_enum)] - reward: Option, - #[arg(long, default_value_t = 1)] - seed: i64, - /// Exact game RNG state JSON containing independent decisions and normals streams. - #[arg(long)] - game_rng_state: Option, - /// Seed the first generation from this network export instead of a random Xavier network. - #[arg(long)] - init_network: Option, - /// Continue another run's population: the checkpoint.json of a run with the same shape, - /// e.g. from another simulator. Takes its networks, generation (mutation - /// schedule), RNG and math profile; best laps start over. - #[arg(long, conflicts_with = "init_network")] - init_population: Option, - #[arg(long, default_value_t = 2)] - batch_count: usize, - /// Deactivate cars on wall contact. Overrides the settings file. - #[arg(long, overrides_with = "no_eliminate_on_wall")] - eliminate_on_wall: bool, - #[arg(long, overrides_with = "eliminate_on_wall")] - no_eliminate_on_wall: bool, - /// Deactivate cars after sustained lack of forward progress. Overrides the settings file. - #[arg(long, overrides_with = "no_idle_eliminate")] - idle_eliminate: bool, - #[arg(long, overrides_with = "idle_eliminate")] - no_idle_eliminate: bool, - /// Write a resumable checkpoint every N generations and when training ends (0 disables). - #[arg(long, default_value_t = 100)] - checkpoint_every: usize, - /// Export the final complete batch fitness leader before reproduction. - #[arg(long)] - save_final_candidate: bool, - /// Stop after a generation whose best total_score reaches this value. - #[arg(long)] - stop_score_above: Option, - /// Stop after a generation whose fastest lap is at most this many seconds. - #[arg(long)] - stop_lap_below: Option, - /// Stop after a generation in which at least this percentage of cars completed a lap. - #[arg(long)] - stop_lapped_percent: Option, - /// Stop once --plateau-metric has not improved for this many generations of this invocation. - #[arg(long)] - stop_plateau: Option, - #[arg(long, value_enum, default_value_t = PlateauMetric::Lap)] - plateau_metric: PlateauMetric, - /// Continue from the checkpoint in --out-dir, allowing new settings with the same network shape. - #[arg(long)] - resume: bool, - /// Simulate the generations on the GPU (gpu/sim, bit-exact with the CPU). - /// `gpu-info` shows which library this loads. - #[arg(long)] - gpu: bool, - }, - /// Evaluate frozen weights on an immutable shared suite without breeding. - Evaluate { - #[arg(long)] - network: PathBuf, - #[arg(long)] - model: PathBuf, - #[arg(long)] - suite: PathBuf, - #[arg(long)] - report: PathBuf, - }, - /// Replay recorded controls and compare the resulting trajectory. - CompareTrace { - scene: PathBuf, - trace: PathBuf, - report: PathBuf, - #[arg(long, default_value_t = 1)] - start_index: usize, - #[arg(long)] - end_index: Option, - }, - /// Compare one simulated transition from each recorded state. - CompareOneStep { - scene: PathBuf, - trace: PathBuf, - report: PathBuf, - }, - /// Compare network inference and physics together against a recorded episode. - CompareClosedLoop { - scene: PathBuf, - trace: PathBuf, - network: PathBuf, - model: PathBuf, - report: PathBuf, - #[arg(long, default_value_t = 2)] - batch_count: usize, - #[arg(long)] - new_vehicle: bool, - }, - /// Compare network inference on recorded sensors. - CompareNetwork { - network: PathBuf, - trace: PathBuf, - report: PathBuf, - #[arg(long, default_value_t = 2)] - first_tick: usize, - }, - /// Compare sensors on recorded states. - CompareSensors { - scene: PathBuf, - trace: PathBuf, - model: PathBuf, - trajectory_report: PathBuf, - sensor_report: PathBuf, - #[arg(long)] - all_frames: bool, - }, - /// Compare path scores on recorded positions. - CompareScore { - scene: PathBuf, - trace: PathBuf, - report: PathBuf, - }, - /// Serve simulations to Drive Lab over a WebSocket on 127.0.0.1 - /// (docs/server.md). - #[cfg(feature = "server")] - Serve { - /// TCP port on 127.0.0.1. - #[arg(long, default_value_t = altd_sim::server::DEFAULT_PORT)] - port: u16, - /// A web origin (scheme://host[:port]) allowed to connect; repeat for - /// several. Replaces the default https://drivinglab.jectrum.de. - #[arg(long = "allow-origin", value_name = "ORIGIN", value_parser = altd_sim::server::protocol::normalize_origin)] - allow_origin: Vec, - }, - /// Load the GPU simulator library used by --gpu and check that it matches this binary. - /// Needs the GPU runtime (ROCm on AMD, the driver on NVIDIA) but no GPU; with a GPU it also - /// runs one kernel, which fails when the library was built for another GPU. - GpuInfo { - /// Library path (default: ALTD_GPU_LIB, then beside the executable, then target/gpu). - #[arg(long)] - library: Option, - }, -} - -pub(super) fn flag(yes: bool, no: bool) -> Option { - if yes { - Some(true) - } else if no { - Some(false) - } else { - None - } -} diff --git a/github-simulator/source/src/bin/altd-sim/bench.rs b/github-simulator/source/src/bin/altd-sim/bench.rs deleted file mode 100644 index 4847ff05e655a96838cea9864bd983cc6ecd4684..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/bin/altd-sim/bench.rs +++ /dev/null @@ -1,174 +0,0 @@ -//! Population timing and optional before-turnover state dumps. - -use super::inputs::{car_from_frame, frames, num, output_names, recorded_controls}; -use super::io::write_report; -use super::platform::{peak_rss_kib, platform_json, process_cpu_seconds}; -use altd_sim::math::profile::MathProfile; -use altd_sim::nn::network::Network; -use altd_sim::track::world::{vector, World}; -use altd_sim::training::evolution::{EvolutionSettings, METRIC_NAMES}; -use altd_sim::training::pyrandom::PyRandom; -use altd_sim::training::{Mode, SensorLayout, TrainingAgent, TrainingRunner}; -use serde_json::{json, Map, Value}; -use std::path::Path; -use std::sync::Arc; -use std::time::Instant; - -fn agent_state(agent: &TrainingAgent) -> Value { - let car = &agent.car; - let metrics: Map = METRIC_NAMES - .iter() - .zip(agent.stats.metrics()) - .map(|(n, v)| (n.to_string(), json!(v))) - .collect(); - json!({ - "position": [car.position.x, car.position.y], - "velocity": [car.velocity.x, car.velocity.y], - "rotation": car.rotation, - "angular_velocity": car.angular_velocity, - "boost_energy": car.boost_energy, - "active": car.active, - "collision_count": car.collision_count, - "update_count": agent.stats.update_count, - "metrics": metrics, - }) -} - -#[allow(clippy::too_many_arguments)] -pub(super) fn bench( - scene: &Value, - network_data: &Value, - model: &Value, - spawn_trace: &Value, - population: usize, - ticks: u64, - warmup_ticks: u64, - seed: i64, - batch_count: usize, - spawn_index: usize, - mode: Mode, - threads: usize, - dump_state: Option<&Path>, - math: MathProfile, -) -> Value { - assert!(population >= 1 && ticks >= 1, "population and ticks must be positive"); - let spawn_frames = frames(spawn_trace); - assert!(spawn_index < spawn_frames.len(), "spawn index is outside the trace"); - let settings = EvolutionSettings { - population, - selection_size: 14, - mutation_rate: 0.05, - adaptive_mutation: true, - weight_decay: 0.0001, - preserve_parents: "on_custom".into(), - preserve_parents_size: 7, - ..EvolutionSettings::default() - }; - let load_started = Instant::now(); - let world = Arc::new(World::from_scene_with(scene, math)); - let world_seconds = load_started.elapsed().as_secs_f64(); - let spawn = &spawn_frames[spawn_index]; - let mut runner = TrainingRunner::new( - world.clone(), - vector(&spawn["position"]), - num(&spawn["rotation"]), - SensorLayout::from_exports(network_data, model, math), - &output_names(network_data), - settings.clone(), - PyRandom::new(seed), - batch_count, - 0, - false, - false, - ); - runner.mode = mode; - let seed_network = Network::from_game_export(network_data); - - let started = Instant::now(); - runner.start(&seed_network); - assert_eq!( - runner.agents.len(), - population, - "generation size differs from requested population" - ); - if spawn_index > 0 { - let template = car_from_frame(&world, spawn, Some(&spawn_frames[spawn_index - 1])); - let controls = recorded_controls(spawn); - for agent in &mut runner.agents { - agent.car.velocity = template.velocity; - agent.car.angular_velocity = template.angular_velocity; - agent.car.boost_energy = template.boost_energy; - agent.controls = controls; - for (wheel, source) in agent.car.wheels.iter_mut().zip(&template.wheels) { - *wheel = *source; - } - } - runner.tick = spawn_index as u64; - runner.batch_index = (spawn_index * runner.batches_per_tick()) % 8; - } - let initialization_seconds = started.elapsed().as_secs_f64(); - - let warmup_started = Instant::now(); - runner.advance(warmup_ticks, false); - let warmup_seconds = warmup_started.elapsed().as_secs_f64(); - - let cpu_started = process_cpu_seconds(); - let timed_started = Instant::now(); - runner.advance(ticks, false); - let tick_seconds = timed_started.elapsed().as_secs_f64(); - let tick_cpu_seconds = process_cpu_seconds() - cpu_started; - let active_after_ticks = runner.agents.iter().filter(|a| a.car.active).count(); - let contacts_after_ticks: u64 = runner.agents.iter().map(|a| a.car.collision_count).sum(); - let agents_state: Option> = dump_state.map(|_| runner.agents.iter().map(agent_state).collect()); - - let turnover_started = Instant::now(); - runner.next_generation(); - let turnover_seconds = turnover_started.elapsed().as_secs_f64(); - assert_eq!( - runner.agents.len(), - population, - "reproduction did not preserve population size" - ); - if let (Some(path), Some(agents)) = (dump_state, agents_state) { - let networks: Vec<&Vec> = runner.agents.iter().map(|a| &a.network.params).collect(); - write_report(path, &json!({"agents": agents, "next_networks": networks})); - } - - let simulated_seconds = ticks as f64 / 60.0; - let complete_generation_seconds = (warmup_ticks + ticks) as f64 / 60.0; - let total_wall_seconds = initialization_seconds + warmup_seconds + tick_seconds + turnover_seconds; - let mut result = json!({ - "population": population, - "timed_ticks": ticks, - "warmup_ticks": warmup_ticks, - "batch_count": batch_count, - "seed": seed, - "spawn_index": spawn_index, - "settings": settings.to_json(), - "physics_shapes": world.track.shapes.len(), - "network_shape": seed_network.shape, - "network_parameters": seed_network.params.len(), - "active_after_ticks": active_after_ticks, - "contacts_after_ticks": contacts_after_ticks, - "world_load_seconds": world_seconds, - "initialization_seconds": initialization_seconds, - "warmup_seconds": warmup_seconds, - "timed_wall_seconds": tick_seconds, - "timed_cpu_seconds": tick_cpu_seconds, - "turnover_seconds": turnover_seconds, - "simulated_seconds": simulated_seconds, - "realtime_multiplier": simulated_seconds / tick_seconds, - "wall_seconds_per_simulated_second": tick_seconds / simulated_seconds, - "car_ticks_per_wall_second": (population as u64 * ticks) as f64 / tick_seconds, - "complete_generation_simulated_seconds": complete_generation_seconds, - "complete_generation_wall_seconds": total_wall_seconds, - "complete_generation_realtime_multiplier": complete_generation_seconds / total_wall_seconds, - "peak_rss_kib": peak_rss_kib(), - "mode": match mode { Mode::Independent => "independent", Mode::Lockstep => "lockstep" }, - }); - let map = result.as_object_mut().unwrap(); - for (k, v) in platform_json(threads).as_object().unwrap() { - map.insert(k.clone(), v.clone()); - } - result -} diff --git a/github-simulator/source/src/bin/altd-sim/comparisons.rs b/github-simulator/source/src/bin/altd-sim/comparisons.rs deleted file mode 100644 index d531cfacbb27a092ddefd74d3202b4af6e26f789..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/bin/altd-sim/comparisons.rs +++ /dev/null @@ -1,613 +0,0 @@ -//! Recorded trajectory, controller, sensor, and score comparisons. - -use super::inputs::{car_from_frame, frames, num, output_names, recorded_controls}; -use altd_sim::math::profile::MathProfile; -use altd_sim::math::pymath::{py_max, py_min, py_pow, py_remainder, py_sum}; -use altd_sim::nn::network::Network; -use altd_sim::physics::car::{Sensor, SensorScratch, DT}; -use altd_sim::track::world::{vector, World}; -use altd_sim::training::evolution::EvolutionSettings; -use altd_sim::training::pyrandom::PyRandom; -use altd_sim::training::{Mode, ScoreTracker, SensorLayout, TrainingRunner}; -use serde_json::{json, Map, Value}; -use std::f64::consts::PI; -use std::sync::Arc; - -/// Output names in `{name.lower(): value}` dict order (first position, last value). -fn recorded_outputs(frame: &Value) -> Vec<(String, f64)> { - let mut outputs: Vec<(String, f64)> = Vec::new(); - for item in frame["outputs"].as_array().expect("outputs") { - let name = item["name"].as_str().unwrap().to_lowercase(); - let value = num(&item["value"]); - match outputs.iter_mut().find(|(n, _)| *n == name) { - Some(slot) => slot.1 = value, - None => outputs.push((name, value)), - } - } - outputs -} - -/// Python `max(values)`: the first maximum wins. -fn py_max_of(values: impl IntoIterator) -> f64 { - let mut iter = values.into_iter(); - let first = iter.next().expect("max() of an empty sequence"); - iter.fold(first, py_max) -} - -fn rotation_error(a: f64, b: f64) -> f64 { - py_remainder(a - b, 2.0 * PI).abs() -} - -fn opt_tick(value: Option) -> Value { - value.map_or(Value::Null, |v| json!(v)) -} - -/// `{name: {"max": max(row[name]), "rmse": sqrt(sum(row[name] ** 2) / n)}}`. -fn summary(rows: &[Value], fields: &[&str]) -> Value { - if rows.is_empty() { - return json!({}); - } - let mut out = Map::new(); - for &field in fields { - let values: Vec = rows.iter().map(|row| num(&row[field])).collect(); - let rmse = (py_sum(values.iter().map(|&v| py_pow(v, 2.0))) / values.len() as f64).sqrt(); - out.insert( - field.into(), - json!({"max": py_max_of(values.iter().copied()), "rmse": rmse}), - ); - } - Value::Object(out) -} - -pub(super) fn compare_trace(world: &World, trace: &Value, start_index: usize, end_index: Option) -> Value { - const LIMITS: [(&str, f64); 3] = [ - ("position_error_px", 0.05), - ("velocity_error_px_s", 1.0), - ("rotation_error_rad", 0.001), - ]; - let frames = frames(trace); - let first = &frames[start_index]; - let previous = if start_index > 0 { - Some(&frames[start_index - 1]) - } else { - None - }; - let mut car = car_from_frame(world, first, previous); - let baseline = first["collision_count"].as_i64().expect("collision_count"); - let end_index = end_index.unwrap_or(frames.len()); - assert!( - start_index < end_index && end_index <= frames.len(), - "start_index and end_index must delimit trace frames" - ); - let mut rows = Vec::new(); - for (index, expected) in frames.iter().enumerate().take(end_index).skip(start_index + 1) { - let contact = car.step(world, &recorded_controls(expected), DT, false); - rows.push(json!({ - "tick": index, - "position_error_px": (car.position - vector(&expected["position"])).length(), - "velocity_error_px_s": (car.velocity - vector(&expected["velocity"])).length(), - "rotation_error_rad": rotation_error(car.rotation, num(&expected["rotation"])), - "game_contact_count": expected["collision_count"].as_i64().unwrap() - baseline, - "python_contact": contact, - "python_position": [car.position.x, car.position.y], - "game_position": expected["position"], - })); - } - let tick_of = |row: &Value| row["tick"].as_u64().unwrap() as usize; - let game_increase = rows - .iter() - .find(|r| r["game_contact_count"].as_i64().unwrap() > 0) - .map(tick_of); - let first_contact = rows.iter().find(|r| r["python_contact"] == true).map(tick_of); - let divergence = rows - .iter() - .find(|r| LIMITS.iter().any(|(name, limit)| num(&r[*name]) > *limit)) - .map(tick_of); - let agreement: Vec = rows - .iter() - .filter(|r| divergence.is_none_or(|stop| tick_of(r) < stop)) - .cloned() - .collect(); - let fields = ["position_error_px", "velocity_error_px_s", "rotation_error_rad"]; - json!({ - "start_index": start_index, - "end_index": end_index, - "game_first_collision_count_increase_tick": opt_tick(game_increase), - "python_first_contact_tick": opt_tick(first_contact), - "first_large_divergence_tick": opt_tick(divergence), - "agreement_limits": LIMITS.iter().map(|(n, l)| ((*n).to_string(), json!(l))).collect::>(), - "agreement_prefix_frames": agreement.len(), - "agreement_prefix": summary(&agreement, &fields), - "full_trace": summary(&rows, &fields), - "frames": rows, - }) -} - -pub(super) fn compare_one_step(world: &World, trace: &Value) -> Value { - let frames = frames(trace); - let has_contact_data = frames[0].get("physics_contacts").is_some(); - let mut rows = Vec::new(); - for index in 2..frames.len() { - let (previous, state, expected) = (&frames[index - 2], &frames[index - 1], &frames[index]); - let mut car = altd_sim::physics::trace_state::car_from_frame( - world, - state, - Some(previous), - index.checked_sub(3).map(|i| &frames[i]), - ); - let contact = car.step(world, &recorded_controls(expected), DT, false); - let game_contact = if has_contact_data { - json!(match &expected["physics_contacts"] { - Value::Array(items) => !items.is_empty(), - Value::Null => false, - Value::Bool(b) => *b, - Value::Object(map) => !map.is_empty(), - other => num(other) != 0.0, - }) - } else { - Value::Null - }; - let mut bit_mismatches = Vec::new(); - let mut check = |name: &str, actual: f64, reference: &Value| { - let expected = num(reference) as f32; - if (actual as f32).to_bits() != expected.to_bits() { - bit_mismatches.push(json!({"field": name, - "actual": actual, "expected": expected as f64, - "actual_bits": format!("{:08x}", (actual as f32).to_bits()), - "expected_bits": format!("{:08x}", expected.to_bits())})); - } - }; - check("position.x", car.position.x, &expected["position"][0]); - check("position.y", car.position.y, &expected["position"][1]); - check("velocity.x", car.velocity.x, &expected["velocity"][0]); - check("velocity.y", car.velocity.y, &expected["velocity"][1]); - check("rotation", car.rotation, &expected["rotation"]); - check("angular_velocity", car.angular_velocity, &expected["angular_velocity"]); - check("boost_energy", car.boost_energy, &expected["boost_energy"]); - for (i, wheel) in car.wheels.iter().enumerate() { - check( - &format!("wheel_angles.{i}"), - wheel.angle_deg, - &expected["wheel_angles"][i], - ); - } - rows.push(json!({ - "tick": index, - "position_error_px": (car.position - vector(&expected["position"])).length(), - "velocity_error_px_s": (car.velocity - vector(&expected["velocity"])).length(), - "rotation_error_rad": rotation_error(car.rotation, num(&expected["rotation"])), - "angular_velocity_error_rad_s": (car.angular_velocity - num(&expected["angular_velocity"])).abs(), - "python_contact": contact, - "game_contact": game_contact, - "bit_mismatches": bit_mismatches, - })); - } - let fields = [ - "position_error_px", - "velocity_error_px_s", - "rotation_error_rad", - "angular_velocity_error_rad_s", - ]; - let pick = |want: bool| -> Vec { rows.iter().filter(|r| r["game_contact"] == want).cloned().collect() }; - let mut worst = Map::new(); - for field in fields { - let mut best = &rows[0]; - for row in &rows[1..] { - if num(&row[field]) > num(&best[field]) { - best = row; - } - } - worst.insert(field.into(), best["tick"].clone()); - } - json!({ - "frames": rows.len(), - "bit_exact_frames": rows.iter().filter(|r| r["bit_mismatches"].as_array().unwrap().is_empty()).count(), - "bit_mismatch_counts": ({ - let mut counts = std::collections::BTreeMap::::new(); - for row in &rows { - for mismatch in row["bit_mismatches"].as_array().unwrap() { - *counts.entry(mismatch["field"].as_str().unwrap().to_owned()).or_default() += 1; - } - } - counts - }), - "all": summary(&rows, &fields), - "contacts": summary(&pick(true), &fields), - "noncontacts": summary(&pick(false), &fields), - "contact_mismatches": rows.iter() - .filter(|r| !r["game_contact"].is_null() && r["game_contact"] != r["python_contact"]) - .map(|r| r["tick"].clone()).collect::>(), - "worst": worst, - "rows": rows, - }) -} - -/// Settings and runner shared by closed-loop comparisons. -fn closed_loop_runner( - world: &Arc, - first: &Value, - network_data: &Value, - model: &Value, - batch_count: usize, - stats_phase: u64, - mode: Mode, -) -> TrainingRunner { - let settings = EvolutionSettings { - population: 1, - selection_size: 1, - mutation_rate: 0.0, - weight_decay: 0.0, - preserve_parents: "on_custom".into(), - preserve_parents_size: 1, - ..EvolutionSettings::default() - }; - let mut runner = TrainingRunner::new( - world.clone(), - vector(&first["position"]), - num(&first["rotation"]), - SensorLayout::from_exports(network_data, model, world.math), - &output_names(network_data), - settings, - PyRandom::new(0), - batch_count, - stats_phase, - false, - false, - ); - runner.mode = mode; - runner -} - -pub(super) fn compare_closed_loop( - world: &Arc, - trace: &Value, - network_data: &Value, - model: &Value, - batch_count: usize, - reused: bool, - mode: Mode, -) -> Value { - let frames = frames(trace); - let first = &frames[0]; - let score_change_ticks: Vec = (1..frames.len()) - .filter(|&i| frames[i]["score"] != frames[i - 1]["score"]) - .collect(); - // Counter(...).most_common(1): highest count, first-seen residue on ties. - let mut counts: Vec<(usize, usize)> = Vec::new(); - for &tick in &score_change_ticks { - match counts.iter_mut().find(|(r, _)| *r == tick % 6) { - Some(entry) => entry.1 += 1, - None => counts.push((tick % 6, 1)), - } - } - let stats_phase = counts - .iter() - .fold(None, |best: Option<(usize, usize)>, &(r, c)| match best { - Some((_, bc)) if bc >= c => best, - _ => Some((r, c)), - }); - let stats_phase = stats_phase.map_or(0, |(r, _)| r) as u64; - let valid: std::collections::HashSet = score_change_ticks.iter().skip(2).copied().collect(); - let mut runner = closed_loop_runner(world, first, network_data, model, batch_count, stats_phase, mode); - runner.start(&Network::from_game_export(network_data)); - if reused || first.get("reset_transform").and_then(Value::as_bool) == Some(true) { - runner.agents[0].car.reset( - world, - vector(&first["position"]), - num(first.get("reset_rotation").unwrap_or(&first["rotation"])), - true, - ); - } - // RecordTrace resets the vehicle, not the running inference batch cursor. - // Recover its phase from the first observed output update. - let inference_phase = frames - .windows(2) - .position(|f| f[0]["outputs"] != f[1]["outputs"]) - .unwrap_or(0) - % batch_count; - runner.batch_index = (8 - (inference_phase * runner.batches_per_tick()) % 8) % 8; - runner.agents[0].controls = recorded_controls(first); - let mut rows = Vec::new(); - for (index, expected) in frames.iter().enumerate().skip(1) { - runner.step(); - let agent = &runner.agents[0]; - let car = &agent.car; - let outputs = recorded_outputs(expected); - let expected_effective = recorded_controls(expected).normalized(); - let effective = agent.controls.normalized(); - let mut row = json!({ - "tick": index, - "position_error_px": (car.position - vector(&expected["position"])).length(), - "velocity_error_px_s": (car.velocity - vector(&expected["velocity"])).length(), - "rotation_error_rad": rotation_error(car.rotation, num(&expected["rotation"])), - "output_error": py_max_of(outputs.iter().map(|(n, v)| (agent.controls.get(n) - v).abs())), - "effective_output_error": py_max_of(outputs.iter().map(|(n, _)| (effective.get(n) - expected_effective.get(n)).abs())), - }); - if valid.contains(&index) { - row["score_absolute_error"] = json!((agent.stats.total_score - num(&expected["score"])).abs()); - } - rows.push(row); - } - let fields = [ - "position_error_px", - "velocity_error_px_s", - "rotation_error_rad", - "output_error", - "effective_output_error", - ]; - let score_summary = |rows: &[Value]| -> Value { - let values: Vec = rows - .iter() - .filter_map(|r| r.get("score_absolute_error").map(num)) - .collect(); - if values.is_empty() { - return json!({}); - } - let rmse = (py_sum(values.iter().map(|v| v * v)) / values.len() as f64).sqrt(); - json!({"updates": values.len(), "max": py_max_of(values.iter().copied()), "rmse": rmse}) - }; - let first_over = |limit: f64| { - opt_tick( - rows.iter() - .find(|r| num(&r["position_error_px"]) > limit) - .map(|r| r["tick"].as_u64().unwrap() as usize), - ) - }; - let head = |n: usize| &rows[..rows.len().min(n)]; - json!({ - "frames": rows.len(), - "batch_count": batch_count, - "inference_phase": inference_phase, - "score_update_phase": stats_phase, - "first_position_error_over_0_05_px": first_over(0.05), - "first_position_error_over_1_px": first_over(1.0), - "first_position_error_over_10_px": first_over(10.0), - "first_half_second": summary(head(30), &fields), - "first_200_frames": summary(head(200), &fields), - "first_500_frames": summary(head(500), &fields), - "full": summary(&rows, &fields), - "score_first_500_frames": score_summary(head(500)), - "score_full": score_summary(&rows), - "rows": rows, - }) -} - -pub(super) fn compare_network(network_data: &Value, trace: &Value, first_tick: usize, math: MathProfile) -> Value { - let network = Network::from_game_export(network_data); - let frames = frames(trace); - let input_names: Vec<&str> = network_data["inputs"] - .as_array() - .unwrap() - .iter() - .map(|v| v.as_str().unwrap()) - .collect(); - let output_names = output_names(network_data); - let mut per_output: Vec> = vec![Vec::new(); output_names.len()]; - let mut worst = (0.0, Value::Null); - for frame in frames.iter().skip(first_tick) { - let sensors = frame["sensors"].as_array().unwrap(); - let outputs = frame["outputs"].as_array().unwrap(); - assert!( - sensors - .iter() - .map(|s| s["name"].as_str().unwrap()) - .eq(input_names.iter().copied()), - "trace sensor order differs from live network" - ); - assert!( - outputs - .iter() - .map(|s| s["name"].as_str().unwrap()) - .eq(output_names.iter().map(String::as_str)), - "trace output order differs from live network" - ); - let inputs: Vec = sensors.iter().map(|s| num(&s["value"])).collect(); - let predicted = network.forward(&inputs, math); - for (j, (value, actual)) in predicted.iter().zip(outputs).enumerate() { - let error = (value - num(&actual["value"])).abs(); - per_output[j].push(error); - if error > worst.0 { - worst = (error, frame["tick"].clone()); - } - } - } - // Python dict: duplicate output names share one error list. - let mut summaries = Map::new(); - for (name, errors) in output_names.iter().zip(&per_output) { - let rmse = (py_sum(errors.iter().map(|e| e * e)) / errors.len() as f64).sqrt(); - summaries.insert( - name.clone(), - json!({"max": py_max_of(errors.iter().copied()), "rmse": rmse}), - ); - } - json!({ - "network_shape": network.shape, - "compared_frames": frames.len() as i64 - first_tick as i64, - "max_error": worst.0, - "worst_tick": worst.1, - "outputs": summaries, - }) -} - -pub(super) fn compare_sensors( - world: &World, - trace: &Value, - model: &Value, - trajectory: &Value, - all_frames: bool, -) -> Value { - const ALL_KINDS: [(&str, &str); 8] = [ - ("AngVel", "angularVelocity"), - ("Boost", "boostCapacity"), - ("Dir", "correctDirection"), - ("Grip", "grip"), - ("VelF", "velocityFront"), - ("VelS", "velocitySide"), - ("Curve", "trackCurvature"), - ("Wheel", "wheelAngle"), - ]; - let frames = frames(trace); - // Only the sensors this trace recorded (rally vs. formula layouts differ). - let has_sensor = |name: &str| { - frames.iter().any(|f| { - f["sensors"] - .as_array() - .is_some_and(|s| s.iter().any(|i| i["name"] == name)) - }) - }; - let kinds: Vec<(&str, &str)> = ALL_KINDS.iter().copied().filter(|(n, _)| has_sensor(n)).collect(); - let source_index = |tick: usize, parity: usize| if tick % 2 == parity { tick - 1 } else { tick - 2 }; - let actual_of = |frame: &Value, name: &str| -> f64 { - // {item["name"]: item["value"]}: the last duplicate wins. - frame["sensors"] - .as_array() - .unwrap() - .iter() - .rev() - .find(|i| i["name"] == name) - .map(|i| num(&i["value"])) - .unwrap_or_else(|| panic!("missing sensor {name}")) - }; - let first_tick = 2.max(trajectory["start_index"].as_u64().unwrap() as usize + 1); - let stop_tick = if all_frames { - frames.len() - } else { - let end = trajectory - .get("end_index") - .map_or(frames.len(), |v| v.as_u64().unwrap() as usize); - match trajectory["first_large_divergence_tick"].as_u64() { - Some(d) => (d as usize).min(end), - None => end, - } - }; - let residual = |parity: usize| { - let mut error = 0.0; - for tick in 3.max(first_tick)..stop_tick.min(first_tick + 40) { - let sample = &frames[source_index(tick, parity)]; - let angular = py_max(-1.0, py_min(1.0, num(&sample["angular_velocity"]) / 2.0)); - error += py_pow(angular - actual_of(&frames[tick], "AngVel"), 2.0); - error += py_pow(num(&sample["boost_energy"]) - actual_of(&frames[tick], "Boost"), 2.0); - } - error - }; - let parity = if residual(1) < residual(0) { 1 } else { 0 }; - let mut vision: Vec<(String, f64, f64)> = model["vision"] - .as_array() - .unwrap() - .iter() - .map(|item| (item["angle"].to_string(), num(&item["angle"]), num(&item["length"]))) - .collect(); - if vision.is_empty() { - // model.json without vision: rays come from the trace sensor names, lengths from the game formula. - for item in frames - .iter() - .find(|f| f["sensors"].as_array().is_some_and(|s| !s.is_empty())) - .map_or(&[][..], |f| f["sensors"].as_array().unwrap()) - { - if let Some(label) = item["name"] - .as_str() - .and_then(|n| n.strip_prefix("↑ ")) - .and_then(|n| n.strip_suffix('°')) - { - let degrees: f64 = label.parse().unwrap(); - vision.push(( - label.to_string(), - degrees, - altd_sim::training::vision_length(degrees as f32, world.math) as f64, - )); - } - } - } - let mut errors: Vec<(String, Vec)> = kinds.iter().map(|(n, _)| (n.to_string(), Vec::new())).collect(); - for (label, _, _) in &vision { - let key = format!("vision_{label}"); - if !errors.iter().any(|(n, _)| *n == key) { - errors.push((key, Vec::new())); - } - } - let push = |errors: &mut Vec<(String, Vec)>, name: &str, value: f64| { - errors.iter_mut().find(|(n, _)| n == name).unwrap().1.push(value); - }; - let mut scratch = SensorScratch::default(); - let mut ray_mismatches = Vec::new(); - for tick in first_tick..stop_tick { - let index = source_index(tick, parity); - let car = altd_sim::physics::trace_state::car_from_frame( - world, - &frames[index], - index.checked_sub(1).map(|i| &frames[i]), - index.checked_sub(2).map(|i| &frames[i]), - ); - scratch.invalidate(); - for &(name, kind) in &kinds { - let value = car.sensor(world, Sensor::by_name(kind), &mut scratch) - actual_of(&frames[tick], name); - push(&mut errors, name, value); - } - for (label, degrees, length) in &vision { - let predicted = car.sensor( - world, - Sensor::Raycast { - degrees: *degrees, - length: *length, - }, - &mut scratch, - ); - let expected = actual_of(&frames[tick], &format!("↑ {label}°")); - let value = predicted - expected; - if value != 0.0 { - ray_mismatches.push(json!({"tick":tick,"source_index":index,"degrees":degrees, - "length":length,"actual":predicted,"expected":expected})); - } - push(&mut errors, &format!("vision_{label}"), value); - } - } - let summarize = |errors: &[f64]| -> Value { - if errors.is_empty() { - return Value::Null; - } - let rmse = (py_sum(errors.iter().map(|e| e * e)) / errors.len() as f64).sqrt(); - json!({"samples": errors.len(), "nonzero_errors":errors.iter().filter(|e| **e != 0.0).count(), "rmse": rmse, "max": py_max_of(errors.iter().map(|e| e.abs()))}) - }; - let vision_errors: Vec = errors - .iter() - .filter(|(n, _)| n.starts_with("vision_")) - .flat_map(|(_, v)| v.iter().copied()) - .collect(); - json!({ - "first_tick": first_tick, - "stop_tick_exclusive": stop_tick, - "sensor_update_parity": parity, - "sample_state_rule": "tick-1 on update ticks; tick-2 on held ticks", - "vision_all": summarize(&vision_errors), - "ray_mismatches":ray_mismatches, - "sensors": errors.iter().map(|(n, v)| (n.clone(), summarize(v))).collect::>(), - }) -} - -pub(super) fn compare_score(world: &World, trace: &Value) -> Value { - let frames = frames(trace); - let mut score = ScoreTracker::default(); - let mut rows = Vec::new(); - for index in 1..frames.len() { - if frames[index]["score"].as_f64() == frames[index - 1]["score"].as_f64() { - continue; - } - score.update(&world.track, vector(&frames[index - 1]["position"])); - let game = num(&frames[index]["score"]); - rows.push(json!({ - "tick": index, - "python_score": score.total_score, - "game_score": frames[index]["score"], - "absolute_error": (score.total_score - game).abs(), - })); - } - let stable = &rows[2.min(rows.len())..]; - let errors: Vec = stable.iter().map(|r| num(&r["absolute_error"])).collect(); - json!({ - "updates": rows.len(), - "warmup_updates_excluded": rows.len() - stable.len(), - "max_error": py_max_of(errors.iter().copied()), - "rmse": (py_sum(errors.iter().map(|&e| py_pow(e, 2.0))) / errors.len() as f64).sqrt(), - "rows": rows, - }) -} diff --git a/github-simulator/source/src/bin/altd-sim/inputs.rs b/github-simulator/source/src/bin/altd-sim/inputs.rs deleted file mode 100644 index b0aa25ccd754269a4353818e3834105cfca56433..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/bin/altd-sim/inputs.rs +++ /dev/null @@ -1,51 +0,0 @@ -//! Read the ordered values in exported traces, networks, and settings. - -use altd_sim::physics::car::{Car, Controls}; -use altd_sim::track::world::World; -use serde_json::Value; - -pub(super) fn num(value: &Value) -> f64 { - value - .as_f64() - .unwrap_or_else(|| panic!("expected a number, got {value}")) -} - -pub(super) fn frames(trace: &Value) -> &Vec { - trace["frames"].as_array().expect("trace frames") -} - -/// `Controls(**{item["name"].lower(): item["value"]})`. -pub(super) fn recorded_controls(frame: &Value) -> Controls { - let mut controls = Controls::default(); - for item in frame["outputs"].as_array().expect("outputs") { - controls.set(item["name"].as_str().unwrap(), num(&item["value"])); - } - controls -} - -/// Car state from a recorded frame plus the wheel history of the frame before. -pub(super) fn car_from_frame(world: &World, state: &Value, previous: Option<&Value>) -> Car { - altd_sim::physics::trace_state::car_from_frame(world, state, previous, None) -} - -pub(super) fn output_names(network_data: &Value) -> Vec { - network_data["outputs"] - .as_array() - .expect("outputs") - .iter() - .map(|v| v.as_str().unwrap().to_string()) - .collect() -} - -/// CLI switches override the game settings; omitted switches default to false. -pub(super) fn elimination_options(settings: &Value, wall: Option, idle: Option) -> (bool, bool) { - let truthy = |key: &str| { - settings - .get(key) - .is_some_and(|v| v.as_bool().unwrap_or_else(|| num(v) != 0.0)) - }; - ( - wall.unwrap_or_else(|| truthy("eliminate")), - idle.unwrap_or_else(|| truthy("idle_eliminate")), - ) -} diff --git a/github-simulator/source/src/bin/altd-sim/io.rs b/github-simulator/source/src/bin/altd-sim/io.rs deleted file mode 100644 index 00a1e4910f3bfd4534ab9d34fce09b2247ef6ea9..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/bin/altd-sim/io.rs +++ /dev/null @@ -1,91 +0,0 @@ -//! JSON reports and atomic run-file writes. - -use serde_json::Value; -use std::path::Path; - -pub(super) fn load(path: &Path) -> Value { - let text = std::fs::read_to_string(path).unwrap_or_else(|e| panic!("{}: {e}", path.display())); - serde_json::from_str(&text).unwrap_or_else(|e| panic!("{}: {e}", path.display())) -} - -/// A default input compiled into the binary, so packaged CLIs work outside the source checkout. -pub(super) struct Builtin { - /// Repository path of the file, recorded in run.json as `builtin:`. - path: &'static str, - text: &'static str, -} - -pub(super) const RALLY_NETWORK: Builtin = Builtin { - path: "assets/networks/rally.json", - text: include_str!("../../../assets/networks/rally.json"), -}; -pub(super) const RALLY_MODEL: Builtin = Builtin { - path: "assets/models/rally.json", - text: include_str!("../../../assets/models/rally.json"), -}; - -/// Loads `path`, or `builtin` when the option was omitted. -pub(super) fn load_or(path: Option<&Path>, builtin: &Builtin) -> Value { - match path { - Some(path) => load(path), - None => serde_json::from_str(builtin.text).expect("built-in input"), - } -} - -/// The run.json record of an input loaded by [`load_or`]. -pub(super) fn describe_input(path: Option<&Path>, builtin: &Builtin) -> Value { - match path { - Some(path) => serde_json::json!(path), - None => format!("builtin:{}", builtin.path).into(), - } -} - -pub(super) fn write_report(path: &Path, value: &Value) { - if let Some(parent) = path.parent() { - if !parent.as_os_str().is_empty() { - std::fs::create_dir_all(parent).expect("create report directory"); - } - } - std::fs::write(path, serde_json::to_string_pretty(value).unwrap() + "\n") - .unwrap_or_else(|e| panic!("{}: {e}", path.display())); -} - -/// Print a report without its bulky per-row key. -pub(super) fn print_without(value: &Value, skip: &str) { - let mut map = value.as_object().unwrap().clone(); - map.shift_remove(skip); - println!("{}", serde_json::to_string_pretty(&Value::Object(map)).unwrap()); -} - -pub(super) fn write_atomic(path: &Path, bytes: &[u8]) { - let tmp = path.with_extension("tmp"); - std::fs::write(&tmp, bytes).unwrap_or_else(|e| panic!("{}: {e}", tmp.display())); - std::fs::rename(&tmp, path).unwrap_or_else(|e| panic!("{}: {e}", path.display())); -} - -pub(super) fn write_atomic_report(path: &Path, value: &Value) { - write_atomic(path, (serde_json::to_string_pretty(value).unwrap() + "\n").as_bytes()); -} - -pub(super) fn load_optional(path: &Path) -> Option { - path.exists().then(|| load(path)) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn builtin_inputs_match_the_repository_assets() { - let root = Path::new(env!("CARGO_MANIFEST_DIR")); - for builtin in [&RALLY_NETWORK, &RALLY_MODEL] { - assert_eq!(load_or(None, builtin), load(&root.join(builtin.path))); - assert_eq!(describe_input(None, builtin), format!("builtin:{}", builtin.path)); - } - let explicit = root.join(RALLY_MODEL.path); - assert_eq!( - describe_input(Some(&explicit), &RALLY_MODEL), - serde_json::json!(explicit) - ); - } -} diff --git a/github-simulator/source/src/bin/altd-sim/main.rs b/github-simulator/source/src/bin/altd-sim/main.rs deleted file mode 100644 index e3426acb29790e1dff181aabe59c8f58965da3d1..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/bin/altd-sim/main.rs +++ /dev/null @@ -1,371 +0,0 @@ -//! CPU simulator CLI, training checkpoints, and recorded-game comparisons. -//! -//! Reports compare trajectories, controller outputs, sensors, and path scores. -//! Simulation arithmetic follows the original game runtime. - -mod args; -mod bench; -mod comparisons; -mod inputs; -mod io; -mod platform; -mod scratch; -mod train; - -use args::{flag, Cli, Command, TrackMode}; -use bench::bench; -use comparisons::{ - compare_closed_loop, compare_network, compare_one_step, compare_score, compare_sensors, compare_trace, -}; -use io::{load, load_or, print_without, write_report, RALLY_MODEL}; -use scratch::{train_scratch, ScratchConfig, StopRules}; -use train::train; - -use altd_sim::math::profile::MathProfile; -use altd_sim::track::world::World; -use altd_sim::training::Mode; -use clap::Parser; -use serde_json::{json, Value}; -use std::sync::Arc; - -fn main() { - let cli = Cli::parse(); - let mut pool = rayon::ThreadPoolBuilder::new(); - if let Some(threads) = cli.threads { - pool = pool.num_threads(threads); - } - pool.build_global().expect("thread pool"); - let threads = rayon::current_num_threads(); - let mode: Mode = cli.mode.into(); - let math = cli.math_profile.unwrap_or_else(MathProfile::detect); - match cli.command { - Command::Bench { - scene, - network, - model, - spawn_trace, - population, - ticks, - warmup_ticks, - seed, - spawn_index, - batch_count, - report, - dump_state, - } => { - let result = bench( - &load(&scene), - &load(&network), - &load_or(model.as_deref(), &RALLY_MODEL), - &load(&spawn_trace), - population, - ticks, - warmup_ticks, - seed, - batch_count, - spawn_index, - mode, - threads, - dump_state.as_deref(), - math, - ); - if let Some(report) = report { - write_report(&report, &result); - } - println!("{}", serde_json::to_string_pretty(&result).unwrap()); - } - Command::Train { - tracks, - scene, - network, - model, - spawn_trace, - output, - settings, - population, - generations, - ticks, - seed, - batch_count, - eliminate_on_wall, - no_eliminate_on_wall, - idle_eliminate, - no_idle_eliminate, - game_rng_state, - } => { - if tracks.track_mode == TrackMode::Fixed && spawn_trace.is_none() { - clap::Error::raw( - clap::error::ErrorKind::MissingRequiredArgument, - "--spawn-trace is required with --track-mode fixed", - ) - .exit(); - } - let settings = settings.as_deref().map(load); - let spawn_data = if tracks.track_mode == TrackMode::Fixed { - load(spawn_trace.as_deref().unwrap()) - } else { - Value::Null - }; - let (result, timing) = train( - &load(&scene), - &load(&network), - &load(&model), - &spawn_data, - settings.as_ref(), - population, - generations, - ticks, - seed, - batch_count, - flag(eliminate_on_wall, no_eliminate_on_wall), - flag(idle_eliminate, no_idle_eliminate), - mode, - threads, - game_rng_state.as_deref().map(load).as_ref(), - &tracks, - math, - ); - write_report(&output, &result); - let history = result["history"].as_array().unwrap(); - println!( - "{}", - serde_json::to_string_pretty(&json!({ - "generations": history.len(), - "best_score": result["best_score"], - "last_generation": history.last(), - "timing": timing, - })) - .unwrap() - ); - } - Command::TrainScratch { - tracks, - scene, - spawn_trace, - network, - model, - out_dir, - shape, - population, - generations, - ticks, - mutation_start, - mutation_end, - schedule, - settings, - reward, - seed, - init_network, - init_population, - game_rng_state, - batch_count, - checkpoint_every, - save_final_candidate, - resume, - eliminate_on_wall, - no_eliminate_on_wall, - idle_eliminate, - no_idle_eliminate, - gpu, - stop_score_above, - stop_lap_below, - stop_lapped_percent, - stop_plateau, - plateau_metric, - } => { - let stop = StopRules { - score_above: stop_score_above, - lap_below: stop_lap_below, - lapped_percent: stop_lapped_percent, - plateau: stop_plateau, - plateau_metric, - }; - let config = ScratchConfig { - tracks, - scene, - spawn_trace, - network, - model, - out_dir, - shape, - population, - generations, - ticks, - mutation_start, - mutation_end, - schedule, - settings, - reward, - seed, - init_network, - init_population, - game_rng_state, - batch_count, - checkpoint_every, - save_final_candidate, - resume, - stop, - eliminate_on_wall: flag(eliminate_on_wall, no_eliminate_on_wall), - idle_eliminate: flag(idle_eliminate, no_idle_eliminate), - gpu, - math_profile: cli.math_profile, - }; - train_scratch(&config, mode, threads); - } - Command::Evaluate { - network, - model, - suite, - report, - } => match altd_sim::training::evaluation::evaluate(&network, &model, &suite, &report, cli.math_profile) { - Ok(result) => println!("{}", serde_json::to_string_pretty(&result).unwrap()), - Err(error) => { - eprintln!("evaluation: {error}"); - std::process::exit(1); - } - }, - Command::CompareTrace { - scene, - trace, - report, - start_index, - end_index, - } => { - let world = World::from_scene_with(&load(&scene), math); - let result = compare_trace(&world, &load(&trace), start_index, end_index); - write_report(&report, &result); - print_without(&result, "frames"); - } - Command::CompareOneStep { scene, trace, report } => { - let world = World::from_scene_with(&load(&scene), math); - let result = compare_one_step(&world, &load(&trace)); - write_report(&report, &result); - print_without(&result, "rows"); - } - Command::CompareClosedLoop { - scene, - trace, - network, - model, - report, - batch_count, - new_vehicle, - } => { - let world = Arc::new(World::from_scene_with(&load(&scene), math)); - let result = compare_closed_loop( - &world, - &load(&trace), - &load(&network), - &load(&model), - batch_count, - !new_vehicle, - mode, - ); - write_report(&report, &result); - print_without(&result, "rows"); - } - Command::CompareNetwork { - network, - trace, - report, - first_tick, - } => { - let result = compare_network(&load(&network), &load(&trace), first_tick, math); - write_report(&report, &result); - print_without(&result, "outputs"); - } - Command::CompareSensors { - scene, - trace, - model, - trajectory_report, - sensor_report, - all_frames, - } => { - let world = World::from_scene_with(&load(&scene), math); - let result = compare_sensors( - &world, - &load(&trace), - &load(&model), - &load(&trajectory_report), - all_frames, - ); - write_report(&sensor_report, &result); - print_without(&result, "sensors"); - } - Command::CompareScore { scene, trace, report } => { - let world = World::from_scene_with(&load(&scene), math); - let result = compare_score(&world, &load(&trace)); - write_report(&report, &result); - print_without(&result, "rows"); - } - #[cfg(feature = "server")] - Command::Serve { port, allow_origin } => serve(port, allow_origin, threads, cli.math_profile), - Command::GpuInfo { library } => { - let gpu = altd_sim::gpu::hip::Gpu::open(library.as_deref()).unwrap_or_else(|error| { - eprintln!("gpu-info: {error}"); - std::process::exit(1); - }); - gpu.check_layout(); - let mut info = json!({ - "version": platform::VERSION, - "library": gpu.path(), - "platform": gpu.platform(), - "layout": "ok", - }); - // Without a device only the layout is checked. With one, a library - // that has no kernels for it fails here. - let mut usable = true; - match gpu.device_name() { - Ok(name) => { - info["device"] = json!(name); - let kernels = gpu.probe_kernels(); - usable = kernels.is_ok(); - info["kernels"] = json!(kernels.err().unwrap_or_else(|| "ok".to_string())); - } - Err(reason) => { - info["device"] = Value::Null; - info["reason"] = json!(reason); - } - } - println!("{}", serde_json::to_string_pretty(&info).unwrap()); - if !usable { - std::process::exit(1); - } - } - } -} - -#[cfg(feature = "server")] -fn serve(port: u16, allow_origin: Vec, threads: usize, math_profile: Option) { - use altd_sim::server::{Config, Server, DEFAULT_ORIGIN, PATH}; - let origins = if allow_origin.is_empty() { - vec![DEFAULT_ORIGIN.to_string()] - } else { - allow_origin - }; - let server = Server::bind(Config { - port, - origins: origins.clone(), - math_profile, - }) - .unwrap_or_else(|e| { - eprintln!("altd-sim serve: cannot listen on 127.0.0.1:{port}: {e}"); - std::process::exit(1); - }); - let address = server.local_addr().expect("listener address"); - let hip = server.hip_status(); - let hip = match hip["available"].as_bool() { - Some(true) => format!("available ({})", hip["device"].as_str().unwrap_or("?")), - _ => format!("unavailable ({})", hip["reason"].as_str().unwrap_or("?")), - }; - eprintln!("altd-sim serve: listening on ws://{address}{PATH}"); - eprintln!(" allowed origins: {}", origins.join(", ")); - eprintln!(" CPU threads: {threads}"); - eprintln!(" HIP: {hip}"); - eprintln!(" default math profile: {}", server.math_profile()); - if let Err(e) = server.run() { - eprintln!("altd-sim serve: {e}"); - std::process::exit(1); - } -} diff --git a/github-simulator/source/src/bin/altd-sim/platform.rs b/github-simulator/source/src/bin/altd-sim/platform.rs deleted file mode 100644 index ca6574ac72ca39bbe30a6e4e9d514ae22331e519..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/bin/altd-sim/platform.rs +++ /dev/null @@ -1,71 +0,0 @@ -//! Process statistics included in benchmark and training reports. - -use serde_json::{json, Value}; - -/// `altd-sim --version`: the package version, or the CI build version of nightly packages. -pub(super) const VERSION: &str = altd_sim::VERSION; - -#[cfg(unix)] -pub(super) fn process_cpu_seconds() -> f64 { - let mut ts = libc::timespec { tv_sec: 0, tv_nsec: 0 }; - unsafe { libc::clock_gettime(libc::CLOCK_PROCESS_CPUTIME_ID, &mut ts) }; - ts.tv_sec as f64 + ts.tv_nsec as f64 * 1e-9 -} - -#[cfg(unix)] -pub(super) fn peak_rss_kib() -> i64 { - let mut usage: libc::rusage = unsafe { std::mem::zeroed() }; - unsafe { libc::getrusage(libc::RUSAGE_SELF, &mut usage) }; - // macOS reports bytes, Linux KiB. - if cfg!(target_os = "macos") { - usage.ru_maxrss / 1024 - } else { - usage.ru_maxrss - } -} - -#[cfg(unix)] -fn logical_cpus() -> i64 { - unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } -} - -#[cfg(windows)] -pub(super) fn process_cpu_seconds() -> f64 { - use windows_sys::Win32::Foundation::FILETIME; - use windows_sys::Win32::System::Threading::{GetCurrentProcess, GetProcessTimes}; - let mut times = [FILETIME { - dwLowDateTime: 0, - dwHighDateTime: 0, - }; 4]; - let [created, exited, kernel, user] = &mut times; - unsafe { GetProcessTimes(GetCurrentProcess(), created, exited, kernel, user) }; - // FILETIME counts 100 ns intervals. - let ticks = |t: &FILETIME| ((t.dwHighDateTime as u64) << 32 | t.dwLowDateTime as u64) as f64; - (ticks(kernel) + ticks(user)) * 1e-7 -} - -#[cfg(windows)] -pub(super) fn peak_rss_kib() -> i64 { - use windows_sys::Win32::System::ProcessStatus::{GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS}; - use windows_sys::Win32::System::Threading::GetCurrentProcess; - let mut counters: PROCESS_MEMORY_COUNTERS = unsafe { std::mem::zeroed() }; - let size = std::mem::size_of::() as u32; - unsafe { GetProcessMemoryInfo(GetCurrentProcess(), &mut counters, size) }; - (counters.PeakWorkingSetSize / 1024) as i64 -} - -#[cfg(windows)] -fn logical_cpus() -> i64 { - use windows_sys::Win32::System::Threading::{GetActiveProcessorCount, ALL_PROCESSOR_GROUPS}; - unsafe { GetActiveProcessorCount(ALL_PROCESSOR_GROUPS) as i64 } -} - -pub(super) fn platform_json(threads: usize) -> Value { - json!({ - "implementation": format!("rust altd_sim {VERSION}"), - "platform": format!("{}-{}", std::env::consts::OS, std::env::consts::ARCH), - "logical_cpus": logical_cpus(), - "affinity_cpus": std::thread::available_parallelism().map(|n| n.get()).ok(), - "threads": threads, - }) -} diff --git a/github-simulator/source/src/bin/altd-sim/scratch/checkpoint.rs b/github-simulator/source/src/bin/altd-sim/scratch/checkpoint.rs deleted file mode 100644 index 041ce79ac3f29e0e93a0dbdeed509eef670c5008..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/bin/altd-sim/scratch/checkpoint.rs +++ /dev/null @@ -1,91 +0,0 @@ -//! Scratch population checkpoints use ordered little-endian f64 parameters. - -use super::{scheduled_rate, ScratchConfig}; -use crate::io::{load, load_optional, write_atomic, write_atomic_report}; -use altd_sim::nn::network::{parameter_count, Network}; -use altd_sim::training::TrainingRunner; -use serde_json::{json, Value}; -use std::path::Path; - -/// Install a checkpoint's population, RNG and mutation rate; returns its generation. -pub(super) fn install_checkpoint( - runner: &mut TrainingRunner, - config: &ScratchConfig, - meta: &mut Value, - dir: &Path, -) -> u64 { - let generation = meta["generation"].as_u64().unwrap(); - let file = dir.join(meta["population_file"].as_str().unwrap()); - let bytes = std::fs::read(&file).unwrap_or_else(|e| panic!("{}: {e}", file.display())); - // Checkpoints without shape/population fields read them from the adjacent run.json. - // Copy those fields into the metadata before a resume can replace run.json. - if meta.get("shape").is_none() || meta.get("population").is_none() { - let saved = load(&dir.join("run.json")); - if meta.get("shape").is_none() { - meta["shape"] = saved["shape"].clone(); - } - if meta.get("population").is_none() { - meta["population"] = saved["population"].clone(); - } - } - let saved_shape: Vec = serde_json::from_value(meta["shape"].clone()).expect("checkpoint network shape"); - assert_eq!( - saved_shape, config.shape, - "checkpoint network shape differs from --shape" - ); - let saved_population = meta["population"].as_u64().expect("checkpoint population") as usize; - assert!(saved_population > 0, "checkpoint population must be positive"); - let size = parameter_count(&saved_shape); - let expected_bytes = saved_population - .checked_mul(size) - .and_then(|n| n.checked_mul(8)) - .expect("checkpoint population size overflow"); - assert_eq!( - bytes.len(), - expected_bytes, - "checkpoint population file size does not match its shape and population" - ); - let values: Vec = bytes - .chunks_exact(8) - .map(|b| f64::from_le_bytes(b.try_into().unwrap())) - .collect(); - // Keep checkpoint order and RNG state. Expansion repeats saved networks; - // subsequent reproduction uses the requested population and settings. - let networks: Vec = (0..config.population) - .map(|i| { - let start = (i % saved_population) * size; - Network::from_vector(&saved_shape, values[start..start + size].to_vec()) - }) - .collect(); - runner.rng = altd_sim::training::TrainingRandom::from_json(&meta["rng"]); - runner.settings.mutation_rate = scheduled_rate(config, generation as usize); - runner.resume(&networks, generation); - generation -} - -/// `checkpoint_gNNNNN.bin`: every network's parameters as little-endian f64. -pub(super) fn write_checkpoint(dir: &Path, runner: &TrainingRunner, state: Value) { - let generation = runner.generation; - let name = format!("checkpoint_g{generation:05}.bin"); - let mut bytes = Vec::with_capacity(runner.agents.len() * runner.agents[0].network.params.len() * 8); - for agent in &runner.agents { - for value in &agent.network.params { - bytes.extend_from_slice(&value.to_le_bytes()); - } - } - write_atomic(&dir.join(&name), &bytes); - let mut meta = state; - meta["generation"] = json!(generation); - meta["population_file"] = json!(name); - meta["shape"] = json!(runner.agents[0].network.shape); - meta["population"] = json!(runner.agents.len()); - meta["rng"] = runner.rng.to_json(); - meta["math_profile"] = json!(runner.world.math); - let previous = load_optional(&dir.join("checkpoint.json")); - write_atomic_report(&dir.join("checkpoint.json"), &meta); - if let Some(old) = previous.as_ref().and_then(|p| p["population_file"].as_str()) { - if old != name { - let _ = std::fs::remove_file(dir.join(old)); - } - } -} diff --git a/github-simulator/source/src/bin/altd-sim/scratch/mod.rs b/github-simulator/source/src/bin/altd-sim/scratch/mod.rs deleted file mode 100644 index 55517302f4368169cda627aca3446e3e4ac2c39d..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/bin/altd-sim/scratch/mod.rs +++ /dev/null @@ -1,61 +0,0 @@ -//! Scratch training configuration, checkpoints, and stopping rules. - -mod checkpoint; -mod runner; -mod stop; - -pub(super) use runner::train_scratch; -pub(super) use stop::StopRules; - -use super::args::{Schedule, ScratchReward, TrackOptions}; -use altd_sim::math::profile::MathProfile; -use std::path::PathBuf; - -#[cfg(test)] -mod tests; - -pub(super) struct ScratchConfig { - pub(super) tracks: TrackOptions, - pub(super) scene: PathBuf, - pub(super) spawn_trace: Option, - pub(super) network: Option, - pub(super) model: Option, - pub(super) out_dir: PathBuf, - pub(super) shape: Vec, - pub(super) population: usize, - pub(super) generations: usize, - pub(super) ticks: u64, - pub(super) mutation_start: f64, - pub(super) mutation_end: f64, - pub(super) schedule: Schedule, - pub(super) settings: Option, - pub(super) reward: Option, - pub(super) seed: i64, - pub(super) init_network: Option, - pub(super) init_population: Option, - pub(super) game_rng_state: Option, - pub(super) batch_count: usize, - pub(super) checkpoint_every: usize, - pub(super) save_final_candidate: bool, - pub(super) stop: StopRules, - pub(super) eliminate_on_wall: Option, - pub(super) idle_eliminate: Option, - pub(super) resume: bool, - pub(super) gpu: bool, - /// `--math-profile`; a resumed run keeps its own. - pub(super) math_profile: Option, -} - -/// Mutation rate used to create generation `generation`. The final checkpoint's -/// population, bred after the last generation, keeps the end rate. -fn scheduled_rate(config: &ScratchConfig, generation: usize) -> f64 { - let (start, end) = (config.mutation_start, config.mutation_end); - if config.generations < 2 { - return start; - } - let t = (generation as f64 / (config.generations - 1) as f64).min(1.0); - match config.schedule { - Schedule::Geometric => start * (end / start).powf(t), - Schedule::Linear => start + (end - start) * t, - } -} diff --git a/github-simulator/source/src/bin/altd-sim/scratch/runner.rs b/github-simulator/source/src/bin/altd-sim/scratch/runner.rs deleted file mode 100644 index e699fdab7a80e8618558bbb4dcf3675dde0c53d7..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/bin/altd-sim/scratch/runner.rs +++ /dev/null @@ -1,616 +0,0 @@ -//! Scratch generation loop, run artifacts, and CPU/GPU track activation. - -use super::checkpoint::{install_checkpoint, write_checkpoint}; -use super::stop::StopState; -use super::{scheduled_rate, ScratchConfig}; -use crate::args::Schedule; -use crate::inputs::{elimination_options, frames, num, output_names}; -use crate::io::{ - describe_input, load, load_optional, load_or, write_atomic, write_atomic_report, RALLY_MODEL, RALLY_NETWORK, -}; -use altd_sim::math::profile::MathProfile; -use altd_sim::math::pymath::py_sum; -use altd_sim::nn::network::Network; -use altd_sim::track::training_tracks::TrainingTrackBuffer; -use altd_sim::track::world::{vector, World}; -use altd_sim::training::batch_evaluation::BatchEvaluation; -use altd_sim::training::evolution::EvolutionSettings; -use altd_sim::training::pyrandom::PyRandom; -use altd_sim::training::{Mode, SensorLayout, TrainingRandom, TrainingRunner}; -use serde_json::{json, Value}; -use std::io::Write; -use std::path::Path; -use std::sync::Arc; -use std::time::Instant; - -fn activate_prepared_track<'a>( - runner: &mut TrainingRunner, - mut track: altd_sim::track::training_tracks::PreparedTrainingTrack, - gpu: Option<&'a altd_sim::gpu::hip::Gpu>, - sim: &mut Option>, - world: &mut Option>, -) -> altd_sim::track::training_tracks::PreparedTrainingTrack { - runner.replace_track(track.world.clone(), track.position, track.rotation); - if let (Some(gpu), Some(sim)) = (gpu, sim.as_mut()) { - let next = altd_sim::gpu::hip::GpuWorld::from_prepared( - gpu, - track.gpu_world.take().expect("buffered GPU track arrays"), - ); - sim.set_world(&next); - *world = Some(next); - } - track -} - -fn write_progress(dir: &Path, generation: usize, track_index: usize, completed: usize, count: usize) { - let progress = json!({"pid": std::process::id(), "generation": generation, - "track_index": track_index, "tracks_completed": completed, - "tracks_per_generation": count, "evaluating": completed < count}); - write_atomic(&dir.join("progress.json"), progress.to_string().as_bytes()); -} - -pub(super) fn final_candidate_leader(fitness: &[f64]) -> usize { - (0..fitness.len()).fold(0, |best, i| if fitness[i] > fitness[best] { i } else { best }) -} - -pub(crate) fn train_scratch(config: &ScratchConfig, mode: Mode, threads: usize) { - let scene = load(&config.scene); - let network_data = load_or(config.network.as_deref(), &RALLY_NETWORK); - let model = load_or(config.model.as_deref(), &RALLY_MODEL); - let track_settings = config.tracks.settings(); - let shape = &config.shape; - let inputs = network_data["inputs"].as_array().expect("inputs").len(); - let outputs = output_names(&network_data); - assert!( - shape.len() >= 2 - && shape.iter().all(|&n| n > 0) - && shape[0] == inputs - && shape[shape.len() - 1] == outputs.len(), - "shape {shape:?} must start with the {inputs} sensor inputs and end with the {} outputs", - outputs.len() - ); - assert!( - config.population >= 1 && config.generations >= 1 && config.ticks >= 1, - "population, generations, and ticks must be positive" - ); - assert!( - config.mutation_start > 0.0 && config.mutation_end > 0.0, - "mutation rates must be positive" - ); - - // Generalist Rally settings, optionally overridden. - let mut settings = EvolutionSettings::from_mcp(&json!({ - "selection_algorithm": "tournament", - "selection_size": 20, - "crossover": "none", - "adaptive_mutation": true, - "weight_decay": 0.0, - "preserve_parents": "on_custom", - "preserve_parents_size": 4, - "rewards": [{"metric": "total_score", "weight": 100, "type": "default"}], - })); - let settings_data = config.settings.as_deref().map(load).unwrap_or_else(|| json!({})); - let settings_data = settings_data.get("settings").unwrap_or(&settings_data); - let (eliminate_on_wall, idle_eliminate) = - elimination_options(settings_data, config.eliminate_on_wall, config.idle_eliminate); - if config.settings.is_some() { - let data = settings_data; - let mut merged = settings.to_json(); - for (k, v) in data.as_object().expect("settings object") { - merged[k] = v.clone(); - } - settings = EvolutionSettings::from_mcp(&merged); - } - if let Some(reward) = config.reward { - settings.rewards = vec![reward.spec()]; - } - settings.population = config.population; - settings.mutation_rate = scheduled_rate(config, 0); - settings - .validate_algorithm() - .unwrap_or_else(|e| panic!("settings: {e}")); - - let dir = &config.out_dir; - let best_dir = dir.join("best_laps"); - std::fs::create_dir_all(&best_dir).unwrap_or_else(|e| panic!("{}: {e}", best_dir.display())); - let mut run = json!({ - "simulator": "game-fidelity-cpu-v1", - "eliminate_on_wall": eliminate_on_wall, "idle_eliminate": idle_eliminate, - "scene": config.scene, "track": scene["track"]["name"], "spawn_trace": config.spawn_trace, - "network_template": describe_input(config.network.as_deref(), &RALLY_NETWORK), - "model": describe_input(config.model.as_deref(), &RALLY_MODEL), "shape": shape, - "population": config.population, "generations": config.generations, "ticks": config.ticks, - "mutation_start": config.mutation_start, "mutation_end": config.mutation_end, - "schedule": match config.schedule { Schedule::Geometric => "geometric", Schedule::Linear => "linear" }, - "seed": config.seed, "init_network": config.init_network, "batch_count": config.batch_count, "settings": settings.to_json(), - "stop": config.stop.to_json(), - "track_mode": if track_settings.is_some() { "random" } else { "fixed" }, - "random_track_settings": track_settings, - "track_buffer_size": config.tracks.track_buffer_size, - }); - if track_settings.is_some() { - run["track"] = json!("random"); - run["spawn_trace"] = Value::Null; - } - if let Some(path) = &config.game_rng_state { - run["game_rng_state"] = load(path); - } - if let Some(path) = &config.init_population { - run["init_population"] = json!(path); - } - let checkpoint = load_optional(&dir.join("checkpoint.json")); - let initial_population_meta = config.init_population.as_deref().map(load); - if config.resume { - assert!( - checkpoint.is_some(), - "--resume: no checkpoint.json in {}", - dir.display() - ); - } else { - assert!( - !dir.join("run.json").exists(), - "{} already holds a run; pass --resume or choose another --out-dir", - dir.display() - ); - } - - // A request left over from an earlier invocation must not end this one. - let stop_request_path = dir.join("stop_request"); - let _ = std::fs::remove_file(&stop_request_path); - - let track_count = config - .tracks - .count(if config.resume { checkpoint.as_ref() } else { None }); - let restored_meta = if config.resume { - checkpoint.as_ref() - } else { - initial_population_meta.as_ref() - }; - // Resumes and population imports keep the checkpoint's math; checkpoints - // from before profiles were Proton's. - let math = match restored_meta { - Some(meta) => { - let saved = match meta.get("math_profile") { - None => MathProfile::Proton, - Some(name) => name - .as_str() - .and_then(MathProfile::parse) - .expect("checkpoint math profile"), - }; - if let Some(asked) = config.math_profile { - assert_eq!(asked, saved, "--math-profile {asked} differs from the run's {saved}"); - } - saved - } - None => config.math_profile.unwrap_or_else(MathProfile::detect), - }; - run["math_profile"] = json!(math); - run["tracks_per_generation"] = json!(track_count); - run["track_seed_version"] = json!(2); - let started = Instant::now(); - let starting_generation = restored_meta.and_then(|m| m["generation"].as_u64()).unwrap_or(0); - let track_seed = if config.resume { - checkpoint - .as_ref() - .and_then(|m| m["random_tracks"]["seed"].as_i64()) - .unwrap_or(config.seed) - } else { - config.seed - }; - run["random_track_seed"] = track_settings.as_ref().map_or(Value::Null, |_| json!(track_seed)); - let track_buffer = track_settings.as_ref().map(|settings| { - TrainingTrackBuffer::new_batched( - scene.clone(), - settings.clone(), - track_seed, - starting_generation, - config.tracks.track_buffer_size, - config.gpu, - track_count, - math, - ) - .unwrap_or_else(|e| panic!("random tracks: {e}")) - }); - let mut current_track = track_buffer - .as_ref() - .map(|b| b.next_track().unwrap_or_else(|e| panic!("random tracks: {e}"))); - let (world, position, rotation) = match ¤t_track { - Some(track) => (track.world.clone(), track.position, track.rotation), - None => { - let path = config - .spawn_trace - .as_deref() - .expect("--spawn-trace is required for fixed tracks"); - let spawn_trace = load(path); - let spawn = &frames(&spawn_trace)[0]; - ( - Arc::new(World::from_scene_with(&scene, math)), - vector(&spawn["position"]), - num(&spawn["rotation"]), - ) - } - }; - let restoring = config.resume || config.init_population.is_some(); - let mut rng: TrainingRandom = match &config.game_rng_state { - Some(path) => altd_sim::training::game_random::GameRandom::from_json(&load(path)).into(), - None => PyRandom::new(config.seed).into(), - }; - let seed_network = if restoring { - None - } else { - let network = match &config.init_network { - Some(path) => Network::from_game_export(&load(path)), - None => rng.xavier(shape, math), - }; - assert_eq!(&network.shape, shape, "--init-network shape differs from --shape"); - Some(network) - }; - let mut runner = TrainingRunner::new( - world, - position, - rotation, - SensorLayout::from_exports(&network_data, &model, math), - &outputs, - settings, - rng, - config.batch_count, - 0, - eliminate_on_wall, - idle_eliminate, - ); - runner.mode = mode; - let gpu = config - .gpu - .then(|| altd_sim::gpu::hip::Gpu::open(None).unwrap_or_else(|e| panic!("--gpu: {e}"))); - let mut gpu_world = gpu.as_ref().map(|g| match current_track.as_mut() { - Some(track) => { - altd_sim::gpu::hip::GpuWorld::from_prepared(g, track.gpu_world.take().expect("buffered GPU track arrays")) - } - None => altd_sim::gpu::hip::GpuWorld::new(g, &runner.world), - }); - let mut gpu_sim = gpu_world.as_ref().map(|w| { - runner - .gpu_sim(w, config.population) - .unwrap_or_else(|e| panic!("--gpu: {e}")) - }); - - let log_path = dir.join("log.jsonl"); - let mut best_lap = f64::INFINITY; - let mut best_lap_generation: Option = None; - let mut best_network_file = Value::Null; - let first_generation = if let Some(mut meta) = checkpoint.filter(|_| config.resume) { - let generation = install_checkpoint(&mut runner, config, &mut meta, dir); - // Persist restored shape/population fields before run.json adopts the new options. - write_atomic_report(&dir.join("checkpoint.json"), &meta); - best_lap = meta["best_lap_s"].as_f64().unwrap_or(f64::INFINITY); - best_lap_generation = meta["best_lap_generation"].as_u64(); - // Drop log lines of generations the checkpoint will redo. - let kept: String = std::fs::read_to_string(&log_path) - .unwrap_or_default() - .lines() - .filter(|line| { - serde_json::from_str::(line).is_ok_and(|v| v["generation"].as_u64() < Some(generation)) - }) - .map(|line| format!("{line}\n")) - .collect(); - write_atomic(&log_path, kept.as_bytes()); - best_network_file = meta["best_network_file"].clone(); - if best_network_file.is_null() { - best_network_file = kept - .lines() - .filter_map(|line| serde_json::from_str::(line).ok()) - .filter_map(|row| row["saved"].as_str().map(str::to_owned)) - .next_back() - .map_or(Value::Null, |name| json!(name)); - } - if let Some(name) = best_network_file.as_str() { - if let Ok(bytes) = std::fs::read(dir.join(name)) { - write_atomic(&dir.join("best.json"), &bytes); - } else { - let _ = std::fs::remove_file(dir.join("best.json")); - } - } else { - let _ = std::fs::remove_file(dir.join("best.json")); - } - for artifact_dir in ["tracks", "best_laps"] { - if let Ok(files) = std::fs::read_dir(dir.join(artifact_dir)) { - for file in files.flatten() { - let name = file.file_name().to_string_lossy().into_owned(); - let prefix = name.strip_prefix('g').and_then(|name| name.split(['_', '.']).next()); - if prefix - .and_then(|value| value.parse::().ok()) - .is_some_and(|value| value >= generation) - { - let _ = std::fs::remove_file(file.path()); - } - } - } - } - println!("resumed {} at generation {generation}", dir.display()); - generation as usize - } else if let Some(path) = &config.init_population { - let mut meta = initial_population_meta.unwrap(); - let generation = install_checkpoint(&mut runner, config, &mut meta, path.parent().unwrap_or(Path::new("."))); - std::fs::write(&log_path, "").expect("log file"); - println!( - "continuing the population of {} at generation {generation}", - path.display() - ); - generation as usize - } else { - runner.start(seed_network.as_ref().unwrap()); - std::fs::write(&log_path, "").expect("log file"); - 0 - }; - write_atomic_report(&dir.join("run.json"), &run); - assert_eq!( - runner.agents.len(), - config.population, - "generation size differs from requested population" - ); - let mut log = std::fs::OpenOptions::new() - .append(true) - .open(&log_path) - .expect("log file"); - println!( - "{} on {}: population {}, {} generations x {:.1} s, mutation {} -> {} ({}), {}, setup {:.2} s", - dir.display(), - run["track"].as_str().unwrap_or("?"), - config.population, - config.generations, - config.ticks as f64 / 60.0, - config.mutation_start, - config.mutation_end, - run["schedule"].as_str().unwrap(), - if config.gpu { - "gpu".to_string() - } else { - format!("{threads} threads") - }, - started.elapsed().as_secs_f64() - ); - - let fmt_lap = |lap: Option| lap.map_or(" - ".to_string(), |t| format!("{t:7.2}")); - let mut stop_state = StopState { - best_lap: None, - best_score: f64::NEG_INFINITY, - stale: 0, - }; - for generation in first_generation..config.generations { - let generation_started = Instant::now(); - let rate = runner.settings.mutation_rate; - let mut batch = BatchEvaluation::new(config.population); - let mut track_reports = Vec::new(); - let mut simulated_ticks = 0u64; - let mut active_cars = 0usize; - let mut lap_time_sum = 0.0; - let mut lap_count = 0usize; - let mut median_lap = None; - let mut saved = Value::Null; - let mut generation_best: Option = None; - for track_index in 0..track_count { - if track_index > 0 { - let track = track_buffer - .as_ref() - .unwrap() - .next_track() - .unwrap_or_else(|e| panic!("random tracks: {e}")); - current_track = Some(activate_prepared_track( - &mut runner, - track, - gpu.as_ref(), - &mut gpu_sim, - &mut gpu_world, - )); - runner.reset_evaluation(); - } - write_progress(dir, generation, track_index, track_index, track_count); - let track_file = current_track.as_ref().map(|track| { - assert_eq!((track.generation, track.track_index), (generation as u64, track_index)); - let name = if track_count == 1 { - format!("tracks/g{generation:05}.track.json") - } else { - format!("tracks/g{generation:05}_t{track_index:03}.track.json") - }; - std::fs::create_dir_all(dir.join("tracks")).expect("track directory"); - write_atomic( - &dir.join(&name), - (serde_json::to_string(&track.track).unwrap() + "\n").as_bytes(), - ); - name - }); - let track_config = current_track.as_ref().map(|track| track.track.config.clone()); - match (&mut gpu_sim, &gpu_world) { - (Some(sim), Some(world)) => { - runner - .advance_generation_gpu(sim, world, config.ticks) - .unwrap_or_else(|e| panic!("--gpu: {e}")); - } - _ => { - runner.advance_generation(config.ticks); - } - } - simulated_ticks += runner.tick; - let active = runner.agents.iter().filter(|agent| agent.car.active).count(); - active_cars += active; - let laps: Vec> = runner - .agents - .iter() - .map(|a| a.stats.best_lap_time.filter(|&t| t > 0.0)) - .collect(); - let scores: Vec = runner.agents.iter().map(|a| a.stats.total_score).collect(); - let lapped: Vec = laps.iter().flatten().copied().collect(); - let leader = - (0..laps.len()) - .filter(|&i| laps[i].is_some()) - .fold(None, |best: Option, i| match best { - Some(b) if (laps[b], -scores[b]) <= (laps[i], -scores[i]) => Some(b), - _ => Some(i), - }); - let track_best = leader.map(|i| laps[i].unwrap()); - if let Some(lap) = track_best { - generation_best = Some(generation_best.map_or(lap, |best| best.min(lap))); - } - batch.record(&runner.results(), &runner.settings.rewards); - track_reports.push(json!({"track_index": track_index, "track_file": track_file, "track_config": track_config, - "simulated_ticks": runner.tick, "active_cars": active, "lapped_cars": lapped.len(), "best_lap_s": track_best, - "best_score": scores.iter().copied().fold(f64::NEG_INFINITY, f64::max), - "mean_score": py_sum(scores.iter().copied()) / scores.len() as f64})); - lap_time_sum += py_sum(lapped.iter().copied()); - lap_count += lapped.len(); - if track_count == 1 && !lapped.is_empty() { - let mut sorted = lapped; - sorted.sort_by(f64::total_cmp); - median_lap = Some(sorted[sorted.len() / 2]); - } - if let (Some(i), Some(lap)) = (leader, track_best) { - if lap < best_lap { - best_lap = lap; - best_lap_generation = Some(generation as u64); - let mut export = runner.agents[i].network.to_json(); - export["inputs"] = network_data["inputs"].clone(); - export["outputs"] = network_data["outputs"].clone(); - export["training"] = json!({ - "generation": generation, "car": i, "inference_batch": i / config.population.div_ceil(8), - "reused_vehicle": generation > 0, "best_lap_s": lap, "total_score": scores[i], - "mutation_rate": rate, "track": run["track"], "seed": config.seed, - "track_file": track_file, "track_config": track_config, - "track_index": track_index, "tracks_per_generation": track_count, - }); - let text = serde_json::to_string_pretty(&export).unwrap() + "\n"; - let name = if track_count == 1 { - format!("g{generation:05}_{lap:.2}s.json") - } else { - format!("g{generation:05}_t{track_index:03}_{lap:.2}s.json") - }; - write_atomic(&best_dir.join(&name), text.as_bytes()); - write_atomic(&dir.join("best.json"), text.as_bytes()); - saved = json!(format!("best_laps/{name}")); - best_network_file = saved.clone(); - } - } - write_progress(dir, generation, track_index, track_index + 1, track_count); - } - let simulate_seconds = generation_started.elapsed().as_secs_f64(); - let summary = batch.finish(); - let best_score = summary.mean_scores.iter().copied().fold(f64::NEG_INFINITY, f64::max); - let mean_score = py_sum(summary.mean_scores.iter().copied()) / config.population as f64; - let mean_lap = (lap_count > 0).then(|| lap_time_sum / lap_count as f64); - let batch_lap = if track_count == 1 { - generation_best - } else { - summary.best_mean_lap_s - }; - let lapped_count = summary.lapped_all_tracks; - let stop_request = stop_request_path.exists(); - let mut stop_reason = config.stop.check( - &mut stop_state, - generation, - batch_lap, - best_score, - lapped_count, - config.population, - stop_request, - ); - if stop_request { - let _ = std::fs::remove_file(&stop_request_path); - } - if stop_reason.is_none() && generation + 1 == config.generations { - stop_reason = Some(json!({"condition": "generations", "generation": generation, - "value": generation + 1, "threshold": config.generations})); - } - let last = stop_reason.is_some(); - let final_checkpoint = last && config.checkpoint_every > 0; - let candidate = if last && config.save_final_candidate { - let leader = final_candidate_leader(&summary.fitness); - let mut export = runner.agents[leader].network.to_json(); - export["schema_version"] = json!(1); - export["inputs"] = network_data["inputs"].clone(); - export["outputs"] = network_data["outputs"].clone(); - export["training"] = json!({"generation": generation, "car": leader, - "selection_rule": "complete-batch-fitness-lowest-index-tie", "fitness": summary.fitness[leader], - "rewards": runner.settings.to_json()["rewards"], "total_score": summary.mean_scores[leader], - "metric_totals": summary.metric_totals[leader], "update_count": summary.update_counts[leader], - "lapped_tracks": summary.lap_counts[leader], "tracks_per_generation": track_count, - "best_lap_s": if track_count == 1 { runner.agents[leader].stats.best_lap_time } else { None }, - "mutation_rate": rate, "seed": config.seed}); - let bytes = (serde_json::to_string_pretty(&export).unwrap() + "\n").into_bytes(); - write_atomic(&dir.join("candidate.json"), &bytes); - json!({"file": "candidate.json", "sha256": altd_sim::training::evaluation::sha256(&bytes), "generation": generation}) - } else { - Value::Null - }; - let turnover_started = Instant::now(); - if !last || final_checkpoint { - runner.settings.mutation_rate = scheduled_rate(config, generation + 1); - if let Some(buffer) = &track_buffer { - let track = buffer.next_track().unwrap_or_else(|e| panic!("random tracks: {e}")); - current_track = Some(activate_prepared_track( - &mut runner, - track, - gpu.as_ref(), - &mut gpu_sim, - &mut gpu_world, - )); - } - if let Some(sim) = &mut gpu_sim { - if track_count == 1 { - runner.next_generation_gpu(sim) - } else { - runner.next_generation_gpu_with_fitness(sim, summary.fitness.clone()) - } - .unwrap_or_else(|e| panic!("--gpu: {e}")); - } else if track_count == 1 { - runner.next_generation(); - } else { - runner.next_generation_with_fitness(summary.fitness.clone()); - } - } - let turnover_seconds = turnover_started.elapsed().as_secs_f64(); - let wall = generation_started.elapsed().as_secs_f64(); - let record = json!({ - "schema_version": 2, "generation": generation, "tracks_per_generation": track_count, - "tracks": track_reports, "track_file": track_reports[0]["track_file"], "track_config": track_reports[0]["track_config"], - "mutation_rate": rate, "best_lap_s": generation_best, "best_batch_mean_lap_s": batch_lap, - "mean_best_lap_s": mean_lap, "median_best_lap_s": median_lap, - "lapped_cars": lapped_count, "lapped_all_tracks": lapped_count, - "best_score": best_score, "best_mean_score": best_score, "mean_score": mean_score, - "best_fitness": summary.fitness.iter().copied().fold(f64::NEG_INFINITY, f64::max), - "mean_fitness": py_sum(summary.fitness.iter().copied()) / config.population as f64, - "total_metric_values": (0..14).map(|metric| py_sum(summary.metric_totals.iter().map(|row| row[metric]))).collect::>(), - "total_update_count": summary.update_counts.iter().sum::(), - "all_time_best_lap_s": best_lap.is_finite().then_some(best_lap), "all_time_best_lap_generation": best_lap_generation, - "saved": saved, "simulate_seconds": simulate_seconds, "simulated_ticks": simulated_ticks, - "active_cars": active_cars / track_count, "turnover_seconds": turnover_seconds, - "car_seconds_per_second": simulated_ticks as f64 / 60.0 * config.population as f64 / wall, - }); - writeln!(log, "{record}").and_then(|_| log.flush()).expect("write log"); - println!("gen {generation:5} tracks {track_count} mut {rate:.5} best lap {} batch lap {} lapped {lapped_count}/{} score {best_score:.1}/{mean_score:.1} {wall:.2} s", - fmt_lap(generation_best), fmt_lap(batch_lap), config.population); - if final_checkpoint || (!last && config.checkpoint_every > 0 && (generation + 1) % config.checkpoint_every == 0) - { - write_checkpoint( - dir, - &runner, - json!({ - "best_lap_s": best_lap.is_finite().then_some(best_lap), "best_lap_generation": best_lap_generation, - "best_network_file": best_network_file, "tracks_per_generation": track_count, "track_seed_version": 2, - "random_tracks": track_settings.as_ref().map(|settings| json!({"seed": track_seed, "settings": settings})), - "stop_reason": if last { stop_reason.clone().unwrap() } else { Value::Null }, - "candidate": candidate, - }), - ); - } - if let Some(reason) = &stop_reason { - println!("stopped: {reason}"); - break; - } - } - let _ = std::fs::remove_file(dir.join("progress.json")); - println!( - "done: best lap {} (generation {}), {:.1} h", - fmt_lap(best_lap.is_finite().then_some(best_lap)), - best_lap_generation.map_or("-".into(), |g| g.to_string()), - started.elapsed().as_secs_f64() / 3600.0 - ); -} diff --git a/github-simulator/source/src/bin/altd-sim/scratch/stop.rs b/github-simulator/source/src/bin/altd-sim/scratch/stop.rs deleted file mode 100644 index f358cb9a86a8192f5f2317e11e2224499a88131d..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/bin/altd-sim/scratch/stop.rs +++ /dev/null @@ -1,85 +0,0 @@ -//! Early-stop conditions and plateau state for one invocation. - -use crate::args::PlateauMetric; -use serde_json::{json, Value}; - -/// Early-stop rules of one train-scratch invocation; any rule ends the run. -#[derive(Clone, Copy)] -pub(crate) struct StopRules { - pub(crate) score_above: Option, - pub(crate) lap_below: Option, - pub(crate) lapped_percent: Option, - pub(crate) plateau: Option, - pub(crate) plateau_metric: PlateauMetric, -} - -/// Plateau bookkeeping; counts start with the invocation, not the run. -pub(super) struct StopState { - pub(super) best_lap: Option, - pub(super) best_score: f64, - pub(super) stale: usize, -} - -impl StopRules { - pub(super) fn to_json(self) -> Value { - json!({ - "score_above": self.score_above, "lap_below": self.lap_below, - "lapped_percent": self.lapped_percent, "plateau": self.plateau, - "plateau_metric": match self.plateau_metric { PlateauMetric::Lap => "lap", PlateauMetric::Score => "score" }, - }) - } - - /// Stop reason after `generation`, if a rule fired. Checked in a fixed order. - // Keep the stop inputs explicit and evaluate rules in their recorded order. - #[allow(clippy::too_many_arguments)] - pub(super) fn check( - &self, - state: &mut StopState, - generation: usize, - lap: Option, - score: f64, - lapped: usize, - population: usize, - stop_request: bool, - ) -> Option { - let improved = match self.plateau_metric { - PlateauMetric::Lap => lap.is_some_and(|t| state.best_lap.is_none_or(|best| t < best)), - PlateauMetric::Score => score > state.best_score, - }; - if let Some(t) = lap { - state.best_lap = Some(state.best_lap.map_or(t, |best| best.min(t))); - } - state.best_score = state.best_score.max(score); - state.stale = if improved { 0 } else { state.stale + 1 }; - let percent = 100.0 * lapped as f64 / population as f64; - let reason = |condition: &str, value: Value, threshold: Value| { - Some(json!({"condition": condition, "generation": generation, "value": value, "threshold": threshold})) - }; - if stop_request { - return reason("stop_request", Value::Null, Value::Null); - } - if let (Some(limit), Some(t)) = (self.lap_below, lap) { - if t <= limit { - return reason("lap_below", json!(t), json!(limit)); - } - } - if let Some(limit) = self.score_above { - if score >= limit { - return reason("score_above", json!(score), json!(limit)); - } - } - if let Some(limit) = self.lapped_percent { - if percent >= limit { - return reason("lapped_percent", json!(percent), json!(limit)); - } - } - if let Some(limit) = self.plateau { - if state.stale >= limit { - let mut stop = reason("plateau", json!(state.stale), json!(limit)).unwrap(); - stop["metric"] = self.to_json()["plateau_metric"].clone(); - return Some(stop); - } - } - None - } -} diff --git a/github-simulator/source/src/bin/altd-sim/scratch/tests.rs b/github-simulator/source/src/bin/altd-sim/scratch/tests.rs deleted file mode 100644 index e2a81d5558df1376b695a97631091ac1b12dd035..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/bin/altd-sim/scratch/tests.rs +++ /dev/null @@ -1,161 +0,0 @@ -use super::runner::final_candidate_leader; -use super::stop::{StopRules, StopState}; -use crate::args::{PlateauMetric, ScratchReward}; -use altd_sim::nn::network::Network; -use altd_sim::training::batch_evaluation::BatchEvaluation; -use altd_sim::training::evolution::{reproduce, reward_values, AgentResult, EvolutionSettings}; -use altd_sim::training::pyrandom::PyRandom; -use altd_sim::training::TrainingStats; -use serde_json::json; - -#[test] -fn final_candidate_selection_breaks_ties_by_population_index() { - assert_eq!(final_candidate_leader(&[1.0, 3.0, 3.0, 2.0]), 1); - assert_eq!(final_candidate_leader(&[0.0, 0.0, 0.0]), 0); -} - -#[test] -fn reward_choice_changes_the_preserved_parent() { - // The distance leader has no completed lap; the fastest lap belongs to - // the car with the least distance. Exercise real stats and reproduction. - let networks: Vec<_> = (0..3) - .map(|i| Network::from_vector(&[1, 1], vec![i as f64, 0.0])) - .collect(); - let stats: Vec<_> = [(1000.0, None), (800.0, Some(60.0)), (600.0, Some(40.0))] - .into_iter() - .map(|(distance, lap)| { - let mut stats = TrainingStats::new(0.0); - stats.total_score = distance; - stats.best_lap_time = lap; - stats.update_count = 100; - stats - }) - .collect(); - let agents: Vec<_> = networks - .iter() - .zip(&stats) - .map(|(network, stats)| AgentResult { - network, - metrics: stats.metrics(), - update_count: stats.update_count, - }) - .collect(); - for (reward, expected) in [(ScratchReward::Distance, 0), (ScratchReward::BestLapTime, 2)] { - let settings = EvolutionSettings { - population: 1, - selection_size: 1, - preserve_parents: "on_custom".into(), - preserve_parents_size: 1, - rewards: vec![reward.spec()], - ..Default::default() - }; - let result = reproduce(&agents, &settings, &mut PyRandom::new(1)); - assert_eq!(result.preserved_count, 1); - assert_eq!(result.networks[0].params, networks[expected].params); - } - let rewards = reward_values(&agents, &[ScratchReward::BestLapTime.spec()]); - assert!(rewards[2] > rewards[1]); - assert!(rewards[2] > rewards[0]); -} - -#[test] -fn batch_stop_conditions_require_one_driver_to_succeed_across_all_tracks() { - let network = Network::from_vector(&[1, 1], vec![0.0; 2]); - let mut batch = BatchEvaluation::new(2); - for track in 0..2 { - let results: Vec<_> = (0..2) - .map(|i| { - let mut metrics = [Some(0.0); 14]; - metrics[0] = Some(if i == track { 100.0 } else { 0.0 }); - metrics[12] = if i == track { Some(0.1) } else { None }; - AgentResult { - network: &network, - metrics, - update_count: 10, - } - }) - .collect(); - batch.record(&results, &[ScratchReward::Distance.spec()]); - } - let summary = batch.finish(); - let rules = StopRules { - score_above: Some(75.0), - lap_below: Some(15.0), - lapped_percent: Some(50.0), - plateau: None, - plateau_metric: PlateauMetric::Lap, - }; - let mut state = StopState { - best_lap: None, - best_score: f64::NEG_INFINITY, - stale: 0, - }; - assert_eq!( - rules.check( - &mut state, - 0, - summary.best_mean_lap_s, - summary.mean_scores.into_iter().fold(f64::NEG_INFINITY, f64::max), - summary.lapped_all_tracks, - 2, - false - ), - None - ); -} - -#[test] -fn stop_rules_use_generation_laps_and_fixed_order() { - let rules = StopRules { - score_above: Some(50.0), - lap_below: Some(40.0), - lapped_percent: Some(25.0), - plateau: Some(2), - plateau_metric: PlateauMetric::Lap, - }; - let mut state = StopState { - best_lap: None, - best_score: f64::NEG_INFINITY, - stale: 0, - }; - assert_eq!(rules.check(&mut state, 0, Some(41.0), 10.0, 1, 10, false), None); - // The all-time best lap does not satisfy lap_below; a later slower generation must not stop. - let reason = rules.check(&mut state, 1, Some(40.0), 60.0, 5, 10, false).unwrap(); - assert_eq!( - reason, - json!({"condition": "lap_below", "generation": 1, "value": 40.0, "threshold": 40.0}) - ); - assert_eq!( - rules.check(&mut state, 2, Some(41.0), 60.0, 3, 10, true).unwrap()["condition"], - "stop_request" - ); - assert_eq!( - rules.check(&mut state, 3, None, 60.0, 0, 10, false).unwrap()["condition"], - "score_above" - ); - let rules = StopRules { - score_above: None, - lap_below: None, - ..rules - }; - assert_eq!( - rules.check(&mut state, 4, Some(45.0), 0.0, 3, 10, false).unwrap()["condition"], - "lapped_percent" - ); - // Slower laps and lapless generations are stale; state.stale is 5 by now. - let rules = StopRules { - lapped_percent: None, - ..rules - }; - let reason = rules.check(&mut state, 5, Some(39.0), 0.0, 1, 10, false); - assert_eq!(reason, None, "a faster lap resets the plateau"); - assert_eq!( - rules.check(&mut state, 6, Some(39.0), 0.0, 1, 10, false), - None, - "ties do not improve" - ); - assert_eq!( - rules.check(&mut state, 7, None, 0.0, 0, 10, false).unwrap()["metric"], - "lap" - ); -} diff --git a/github-simulator/source/src/bin/altd-sim/train.rs b/github-simulator/source/src/bin/altd-sim/train.rs deleted file mode 100644 index 688ba19fea3b639a6dd32cb594b75f30f7fca290..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/bin/altd-sim/train.rs +++ /dev/null @@ -1,206 +0,0 @@ -//! Training an imported network and returning the generation history. - -use super::args::TrackOptions; -use super::inputs::{elimination_options, frames, num, output_names}; -use super::platform::platform_json; -use altd_sim::math::profile::MathProfile; -use altd_sim::math::pymath::py_sum; -use altd_sim::nn::network::Network; -use altd_sim::track::training_tracks::TrainingTrackBuffer; -use altd_sim::track::world::{vector, World}; -use altd_sim::training::batch_evaluation::BatchEvaluation; -use altd_sim::training::evolution::EvolutionSettings; -use altd_sim::training::pyrandom::PyRandom; -use altd_sim::training::{Mode, SensorLayout, TrainingRandom, TrainingRunner}; -use serde_json::{json, Value}; -use std::sync::Arc; -use std::time::Instant; - -#[allow(clippy::too_many_arguments)] -pub(super) fn train( - scene: &Value, - network_data: &Value, - model: &Value, - spawn_trace: &Value, - settings_file: Option<&Value>, - population: Option, - generations: usize, - ticks: Option, - seed: i64, - batch_count: usize, - eliminate_on_wall: Option, - idle_eliminate: Option, - mode: Mode, - threads: usize, - game_rng_state: Option<&Value>, - tracks: &TrackOptions, - math: MathProfile, -) -> (Value, Value) { - let empty = json!({}); - let settings_data = settings_file.unwrap_or(&empty); - let settings_data = settings_data.get("settings").unwrap_or(settings_data); - let mut settings = EvolutionSettings::from_mcp(settings_data); - let population = population.unwrap_or(if settings_file.is_some() { - settings.population - } else { - 8 - }); - let ticks = ticks.unwrap_or_else(|| { - settings_data - .get("time_limit_s") - .map_or(300, |v| (num(v) * 60.0).round_ties_even() as u64) - }); - let (eliminate_on_wall, idle_eliminate) = elimination_options(settings_data, eliminate_on_wall, idle_eliminate); - assert!( - population >= 1 && generations >= 1 && ticks >= 1, - "population, generations, and ticks must be positive" - ); - settings.population = population; - settings - .validate_algorithm() - .unwrap_or_else(|e| panic!("settings: {e}")); - let started = Instant::now(); - let track_settings = tracks.settings(); - let track_count = tracks.count(None); - let track_buffer = track_settings.as_ref().map(|settings| { - TrainingTrackBuffer::new_batched( - scene.clone(), - settings.clone(), - seed, - 0, - tracks.track_buffer_size, - false, - track_count, - math, - ) - .unwrap_or_else(|e| panic!("random tracks: {e}")) - }); - let mut current_track = track_buffer - .as_ref() - .map(|b| b.next_track().unwrap_or_else(|e| panic!("random tracks: {e}"))); - let (world, position, rotation) = match ¤t_track { - Some(track) => (track.world.clone(), track.position, track.rotation), - None => { - let spawn = &frames(spawn_trace)[0]; - ( - Arc::new(World::from_scene_with(scene, math)), - vector(&spawn["position"]), - num(&spawn["rotation"]), - ) - } - }; - let world_seconds = started.elapsed().as_secs_f64(); - let seed_network = Network::from_game_export(network_data); - let mut runner = TrainingRunner::new( - world, - position, - rotation, - SensorLayout::from_exports(network_data, model, math), - &output_names(network_data), - settings.clone(), - game_rng_state.map_or_else( - || TrainingRandom::from(PyRandom::new(seed)), - |v| altd_sim::training::game_random::GameRandom::from_json(v).into(), - ), - batch_count, - 0, - eliminate_on_wall, - idle_eliminate, - ); - runner.mode = mode; - runner.start(&seed_network); - let mut history = Vec::new(); - let mut best_score = f64::NEG_INFINITY; - let mut best_network = seed_network.clone(); - let mut simulated_ticks = 0u64; - let mut simulate_seconds = 0.0; - for generation in 0..generations { - let mut batch = BatchEvaluation::new(population); - let mut track_reports = Vec::new(); - let mut generation_ticks = 0; - for track_index in 0..track_count { - if track_index > 0 { - let track = track_buffer - .as_ref() - .unwrap() - .next_track() - .unwrap_or_else(|e| panic!("random tracks: {e}")); - runner.replace_track(track.world.clone(), track.position, track.rotation); - runner.reset_evaluation(); - current_track = Some(track); - } - if let Some(track) = ¤t_track { - assert_eq!((track.generation, track.track_index), (generation as u64, track_index)); - } - let tick_started = Instant::now(); - runner.advance_generation(ticks); - simulate_seconds += tick_started.elapsed().as_secs_f64(); - simulated_ticks += runner.tick; - generation_ticks += runner.tick; - batch.record(&runner.results(), &settings.rewards); - track_reports.push(json!({"track_index": track_index, "ticks": runner.tick, - "track": current_track.as_ref().map(|t| &t.track)})); - } - let summary = batch.finish(); - let first_max = - |values: &[f64]| (1..values.len()).fold(0, |best, i| if values[i] > values[best] { i } else { best }); - let leader = first_max(&summary.fitness); - let best_raw = first_max(&summary.mean_scores); - if summary.mean_scores[best_raw] > best_score { - best_score = summary.mean_scores[best_raw]; - best_network = runner.agents[best_raw].network.clone(); - } - history.push(json!({ - "generation": generation, "ticks": generation_ticks, "tracks_per_generation": track_count, - "best_reward": summary.fitness[leader], "best_score": summary.mean_scores[best_raw], - "mean_score": py_sum(summary.mean_scores.iter().copied()) / population as f64, - "total_collision_updates": py_sum(summary.metric_totals.iter().map(|r| r[11])), - "lapped_all_tracks": summary.lapped_all_tracks, "best_batch_mean_lap_s": summary.best_mean_lap_s, - "track": track_reports[0]["track"], "tracks": track_reports, - })); - if generation + 1 < generations { - if let Some(buffer) = &track_buffer { - let track = buffer.next_track().unwrap_or_else(|e| panic!("random tracks: {e}")); - runner.replace_track(track.world.clone(), track.position, track.rotation); - current_track = Some(track); - } - if track_count == 1 { - runner.next_generation(); - } else { - runner.next_generation_with_fitness(summary.fitness); - } - } - } - let total_seconds = started.elapsed().as_secs_f64(); - let mut best = best_network.to_json(); - best["inputs"] = network_data["inputs"].clone(); - best["outputs"] = network_data["outputs"].clone(); - let output = json!({ - "seed": seed, - "math_profile": math, - "settings": settings.to_json(), - "ticks_limit": ticks, "tracks_per_generation": track_count, "track_seed_version": 2, - "eliminate_on_wall": eliminate_on_wall, - "idle_eliminate": idle_eliminate, - "history": history, - "best_score": best_score, - "best_network": best, - "track_mode": if track_settings.is_some() { "random" } else { "fixed" }, - "random_track_settings": track_settings, - }); - let simulated_seconds = simulated_ticks as f64 / 60.0; - let mut timing = json!({ - "world_load_seconds": world_seconds, - "simulation_wall_seconds": simulate_seconds, - "total_wall_seconds": total_seconds, - "simulated_seconds_per_car": simulated_seconds, - "realtime_multiplier": simulated_seconds / simulate_seconds, - "complete_realtime_multiplier": simulated_seconds / total_seconds, - "population": population, - "mode": match mode { Mode::Independent => "independent", Mode::Lockstep => "lockstep" }, - }); - for (k, v) in platform_json(threads).as_object().unwrap() { - timing[k] = v.clone(); - } - (output, timing) -} diff --git a/github-simulator/source/src/data/tilemap_initial_cells.json b/github-simulator/source/src/data/tilemap_initial_cells.json deleted file mode 100644 index f75c80085a9f60edcbda5ef0289d7c5b17e57cca..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/data/tilemap_initial_cells.json +++ /dev/null @@ -1 +0,0 @@ -[[0,0],[0,1],[0,2],[0,3],[0,4],[0,5],[0,6],[1,0],[2,0],[3,0],[4,0],[5,0],[6,0],[7,0],[8,0],[9,0],[10,0]] \ No newline at end of file diff --git a/github-simulator/source/src/data/track_catalog.json b/github-simulator/source/src/data/track_catalog.json deleted file mode 100644 index 0452580fb9c80e283e1d50e65259e4958047f4b7..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/data/track_catalog.json +++ /dev/null @@ -1 +0,0 @@ -{"hashcode_seed":0,"bounds":[0,0,11,7],"blocks":[{"atlas":1,"alternative":0,"surface":0,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":0,"index":0,"type":0}]},{"atlas":1,"alternative":1,"surface":0,"type":0,"connections":[{"side":0,"index":2,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":1,"alternative":2,"surface":0,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":1,"alternative":3,"surface":0,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":0,"index":1,"type":0}]},{"atlas":1,"alternative":4,"surface":0,"type":0,"connections":[{"side":3,"index":1,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":1,"alternative":5,"surface":0,"type":0,"connections":[{"side":2,"index":2,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":1,"alternative":6,"surface":0,"type":0,"connections":[{"side":0,"index":1,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":1,"alternative":7,"surface":0,"type":0,"connections":[{"side":1,"index":1,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":2,"alternative":0,"surface":0,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":0,"index":1,"type":0}]},{"atlas":2,"alternative":1,"surface":0,"type":0,"connections":[{"side":0,"index":1,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":2,"alternative":2,"surface":0,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":2,"alternative":3,"surface":0,"type":0,"connections":[{"side":1,"index":1,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":3,"alternative":0,"surface":0,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":0,"index":0,"type":0}]},{"atlas":3,"alternative":1,"surface":0,"type":0,"connections":[{"side":0,"index":2,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":3,"alternative":2,"surface":0,"type":0,"connections":[{"side":1,"index":2,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":3,"alternative":3,"surface":0,"type":0,"connections":[{"side":2,"index":2,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":4,"alternative":0,"surface":0,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":0,"index":2,"type":0}]},{"atlas":4,"alternative":1,"surface":0,"type":0,"connections":[{"side":0,"index":0,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":4,"alternative":2,"surface":0,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":4,"alternative":3,"surface":0,"type":0,"connections":[{"side":0,"index":1,"type":0},{"side":2,"index":2,"type":0}]},{"atlas":4,"alternative":4,"surface":0,"type":0,"connections":[{"side":1,"index":0,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":4,"alternative":5,"surface":0,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":4,"alternative":6,"surface":0,"type":0,"connections":[{"side":0,"index":1,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":4,"alternative":7,"surface":0,"type":0,"connections":[{"side":1,"index":1,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":5,"alternative":0,"surface":0,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":5,"alternative":1,"surface":0,"type":0,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":6,"alternative":0,"surface":0,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":6,"alternative":1,"surface":0,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":6,"alternative":2,"surface":0,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":6,"alternative":3,"surface":0,"type":0,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":6,"alternative":4,"surface":0,"type":0,"connections":[{"side":2,"index":2,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":6,"alternative":5,"surface":0,"type":0,"connections":[{"side":0,"index":0,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":6,"alternative":6,"surface":0,"type":0,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":6,"alternative":7,"surface":0,"type":0,"connections":[{"side":1,"index":1,"type":0},{"side":0,"index":2,"type":0}]},{"atlas":7,"alternative":0,"surface":0,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":7,"alternative":1,"surface":0,"type":0,"connections":[{"side":3,"index":0,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":7,"alternative":2,"surface":0,"type":0,"connections":[{"side":0,"index":0,"type":0},{"side":2,"index":2,"type":0}]},{"atlas":7,"alternative":3,"surface":0,"type":0,"connections":[{"side":0,"index":0,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":7,"alternative":4,"surface":0,"type":0,"connections":[{"side":0,"index":2,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":7,"alternative":5,"surface":0,"type":0,"connections":[{"side":1,"index":0,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":7,"alternative":6,"surface":0,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":0,"index":2,"type":0}]},{"atlas":7,"alternative":7,"surface":0,"type":0,"connections":[{"side":2,"index":2,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":8,"alternative":0,"surface":0,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":8,"alternative":1,"surface":0,"type":0,"connections":[{"side":2,"index":2,"type":0},{"side":0,"index":2,"type":0}]},{"atlas":8,"alternative":2,"surface":0,"type":0,"connections":[{"side":0,"index":0,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":8,"alternative":3,"surface":0,"type":0,"connections":[{"side":1,"index":0,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":9,"alternative":0,"surface":0,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":9,"alternative":1,"surface":0,"type":0,"connections":[{"side":2,"index":2,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":9,"alternative":2,"surface":0,"type":0,"connections":[{"side":1,"index":0,"type":0},{"side":0,"index":2,"type":0}]},{"atlas":9,"alternative":3,"surface":0,"type":0,"connections":[{"side":0,"index":0,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":10,"alternative":0,"surface":0,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":10,"alternative":1,"surface":0,"type":0,"connections":[{"side":2,"index":2,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":10,"alternative":2,"surface":0,"type":0,"connections":[{"side":0,"index":0,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":10,"alternative":3,"surface":0,"type":0,"connections":[{"side":0,"index":2,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":21,"alternative":0,"surface":0,"type":5,"connections":[{"side":1,"index":1,"type":1},{"side":3,"index":1,"type":0}]},{"atlas":21,"alternative":1,"surface":0,"type":5,"connections":[{"side":2,"index":1,"type":1},{"side":1,"index":1,"type":0}]},{"atlas":21,"alternative":2,"surface":0,"type":5,"connections":[{"side":0,"index":1,"type":1},{"side":2,"index":1,"type":0}]},{"atlas":21,"alternative":3,"surface":0,"type":5,"connections":[{"side":3,"index":1,"type":1},{"side":0,"index":1,"type":0}]},{"atlas":21,"alternative":4,"surface":0,"type":5,"connections":[{"side":0,"index":1,"type":1},{"side":3,"index":1,"type":0}]},{"atlas":21,"alternative":5,"surface":0,"type":5,"connections":[{"side":2,"index":1,"type":1},{"side":0,"index":1,"type":0}]},{"atlas":21,"alternative":6,"surface":0,"type":5,"connections":[{"side":1,"index":1,"type":1},{"side":2,"index":1,"type":0}]},{"atlas":21,"alternative":7,"surface":0,"type":5,"connections":[{"side":3,"index":1,"type":1},{"side":1,"index":1,"type":0}]},{"atlas":22,"alternative":0,"surface":0,"type":5,"connections":[{"side":2,"index":1,"type":1},{"side":0,"index":1,"type":1}]},{"atlas":22,"alternative":1,"surface":0,"type":5,"connections":[{"side":0,"index":1,"type":1},{"side":3,"index":1,"type":1}]},{"atlas":22,"alternative":2,"surface":0,"type":5,"connections":[{"side":1,"index":1,"type":1},{"side":2,"index":1,"type":1}]},{"atlas":22,"alternative":3,"surface":0,"type":5,"connections":[{"side":3,"index":1,"type":1},{"side":1,"index":1,"type":1}]},{"atlas":11,"alternative":0,"surface":0,"type":1,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":11,"alternative":1,"surface":0,"type":1,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":12,"alternative":0,"surface":0,"type":2,"connections":[{"side":2,"index":1,"type":2},{"side":3,"index":1,"type":2}]},{"atlas":12,"alternative":1,"surface":0,"type":2,"connections":[{"side":0,"index":1,"type":2},{"side":1,"index":1,"type":2}]},{"atlas":13,"alternative":0,"surface":0,"type":2,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":1,"type":2}]},{"atlas":13,"alternative":1,"surface":0,"type":2,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":1,"type":2}]},{"atlas":13,"alternative":2,"surface":0,"type":2,"connections":[{"side":2,"index":1,"type":2},{"side":3,"index":1,"type":0}]},{"atlas":13,"alternative":3,"surface":0,"type":2,"connections":[{"side":0,"index":1,"type":2},{"side":1,"index":1,"type":0}]},{"atlas":14,"alternative":0,"surface":0,"type":3,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":14,"alternative":1,"surface":0,"type":3,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":15,"alternative":0,"surface":0,"type":3,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":15,"alternative":1,"surface":0,"type":3,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":16,"alternative":0,"surface":0,"type":1,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":16,"alternative":1,"surface":0,"type":1,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":17,"alternative":0,"surface":0,"type":4,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":17,"alternative":1,"surface":0,"type":4,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":18,"alternative":0,"surface":0,"type":1,"connections":[{"side":2,"index":0,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":18,"alternative":1,"surface":0,"type":1,"connections":[{"side":2,"index":2,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":18,"alternative":2,"surface":0,"type":1,"connections":[{"side":0,"index":0,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":18,"alternative":3,"surface":0,"type":1,"connections":[{"side":0,"index":2,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":19,"alternative":0,"surface":0,"type":3,"connections":[{"side":2,"index":0,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":19,"alternative":1,"surface":0,"type":3,"connections":[{"side":2,"index":2,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":19,"alternative":2,"surface":0,"type":3,"connections":[{"side":0,"index":0,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":19,"alternative":3,"surface":0,"type":3,"connections":[{"side":0,"index":2,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":20,"alternative":0,"surface":0,"type":3,"connections":[{"side":2,"index":0,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":20,"alternative":1,"surface":0,"type":3,"connections":[{"side":2,"index":2,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":20,"alternative":2,"surface":0,"type":3,"connections":[{"side":0,"index":0,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":20,"alternative":3,"surface":0,"type":3,"connections":[{"side":0,"index":2,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":23,"alternative":0,"surface":0,"type":1,"connections":[{"side":2,"index":0,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":23,"alternative":1,"surface":0,"type":1,"connections":[{"side":2,"index":2,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":23,"alternative":2,"surface":0,"type":1,"connections":[{"side":0,"index":0,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":23,"alternative":3,"surface":0,"type":1,"connections":[{"side":0,"index":2,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":24,"alternative":0,"surface":0,"type":5,"connections":[{"side":2,"index":1,"type":4},{"side":0,"index":0,"type":3}]},{"atlas":24,"alternative":1,"surface":0,"type":5,"connections":[{"side":0,"index":2,"type":3},{"side":3,"index":1,"type":4}]},{"atlas":24,"alternative":2,"surface":0,"type":5,"connections":[{"side":2,"index":1,"type":4},{"side":1,"index":0,"type":3}]},{"atlas":24,"alternative":3,"surface":0,"type":5,"connections":[{"side":2,"index":0,"type":3},{"side":0,"index":1,"type":4}]},{"atlas":24,"alternative":4,"surface":0,"type":5,"connections":[{"side":3,"index":1,"type":4},{"side":1,"index":2,"type":3}]},{"atlas":24,"alternative":5,"surface":0,"type":5,"connections":[{"side":2,"index":2,"type":3},{"side":1,"index":1,"type":4}]},{"atlas":24,"alternative":6,"surface":0,"type":5,"connections":[{"side":0,"index":1,"type":4},{"side":3,"index":0,"type":3}]},{"atlas":24,"alternative":7,"surface":0,"type":5,"connections":[{"side":1,"index":1,"type":4},{"side":3,"index":2,"type":3}]},{"atlas":25,"alternative":0,"surface":0,"type":5,"connections":[{"side":2,"index":1,"type":4},{"side":0,"index":0,"type":0}]},{"atlas":25,"alternative":1,"surface":0,"type":5,"connections":[{"side":0,"index":2,"type":0},{"side":3,"index":1,"type":4}]},{"atlas":25,"alternative":2,"surface":0,"type":5,"connections":[{"side":2,"index":1,"type":4},{"side":1,"index":0,"type":0}]},{"atlas":25,"alternative":3,"surface":0,"type":5,"connections":[{"side":2,"index":0,"type":0},{"side":0,"index":1,"type":4}]},{"atlas":25,"alternative":4,"surface":0,"type":5,"connections":[{"side":3,"index":1,"type":4},{"side":1,"index":2,"type":0}]},{"atlas":25,"alternative":5,"surface":0,"type":5,"connections":[{"side":2,"index":2,"type":0},{"side":1,"index":1,"type":4}]},{"atlas":25,"alternative":6,"surface":0,"type":5,"connections":[{"side":0,"index":1,"type":4},{"side":3,"index":0,"type":0}]},{"atlas":25,"alternative":7,"surface":0,"type":5,"connections":[{"side":1,"index":1,"type":4},{"side":3,"index":2,"type":0}]},{"atlas":26,"alternative":0,"surface":0,"type":5,"connections":[{"side":2,"index":1,"type":3},{"side":3,"index":0,"type":3}]},{"atlas":26,"alternative":1,"surface":0,"type":5,"connections":[{"side":2,"index":0,"type":3},{"side":3,"index":1,"type":3}]},{"atlas":26,"alternative":2,"surface":0,"type":5,"connections":[{"side":2,"index":1,"type":3},{"side":3,"index":2,"type":3}]},{"atlas":26,"alternative":3,"surface":0,"type":5,"connections":[{"side":0,"index":1,"type":3},{"side":1,"index":0,"type":3}]},{"atlas":26,"alternative":4,"surface":0,"type":5,"connections":[{"side":2,"index":2,"type":3},{"side":3,"index":1,"type":3}]},{"atlas":26,"alternative":5,"surface":0,"type":5,"connections":[{"side":0,"index":0,"type":3},{"side":1,"index":1,"type":3}]},{"atlas":26,"alternative":6,"surface":0,"type":5,"connections":[{"side":0,"index":1,"type":3},{"side":1,"index":2,"type":3}]},{"atlas":26,"alternative":7,"surface":0,"type":5,"connections":[{"side":1,"index":1,"type":3},{"side":0,"index":2,"type":3}]},{"atlas":27,"alternative":0,"surface":0,"type":5,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":0,"type":3}]},{"atlas":27,"alternative":1,"surface":0,"type":5,"connections":[{"side":2,"index":0,"type":3},{"side":3,"index":1,"type":0}]},{"atlas":27,"alternative":2,"surface":0,"type":5,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":2,"type":3}]},{"atlas":27,"alternative":3,"surface":0,"type":5,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":0,"type":3}]},{"atlas":27,"alternative":4,"surface":0,"type":5,"connections":[{"side":2,"index":2,"type":3},{"side":3,"index":1,"type":0}]},{"atlas":27,"alternative":5,"surface":0,"type":5,"connections":[{"side":0,"index":0,"type":3},{"side":1,"index":1,"type":0}]},{"atlas":27,"alternative":6,"surface":0,"type":5,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":2,"type":3}]},{"atlas":27,"alternative":7,"surface":0,"type":5,"connections":[{"side":1,"index":1,"type":0},{"side":0,"index":2,"type":3}]},{"atlas":28,"alternative":0,"surface":0,"type":5,"connections":[{"side":2,"index":1,"type":3},{"side":3,"index":0,"type":0}]},{"atlas":28,"alternative":1,"surface":0,"type":5,"connections":[{"side":2,"index":0,"type":0},{"side":3,"index":1,"type":3}]},{"atlas":28,"alternative":2,"surface":0,"type":5,"connections":[{"side":2,"index":1,"type":3},{"side":3,"index":2,"type":0}]},{"atlas":28,"alternative":3,"surface":0,"type":5,"connections":[{"side":0,"index":1,"type":3},{"side":1,"index":0,"type":0}]},{"atlas":28,"alternative":4,"surface":0,"type":5,"connections":[{"side":2,"index":2,"type":0},{"side":3,"index":1,"type":3}]},{"atlas":28,"alternative":5,"surface":0,"type":5,"connections":[{"side":0,"index":0,"type":0},{"side":1,"index":1,"type":3}]},{"atlas":28,"alternative":6,"surface":0,"type":5,"connections":[{"side":0,"index":1,"type":3},{"side":1,"index":2,"type":0}]},{"atlas":28,"alternative":7,"surface":0,"type":5,"connections":[{"side":1,"index":1,"type":3},{"side":0,"index":2,"type":0}]},{"atlas":101,"alternative":0,"surface":1,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":0,"index":0,"type":0}]},{"atlas":101,"alternative":1,"surface":1,"type":0,"connections":[{"side":0,"index":2,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":101,"alternative":2,"surface":1,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":101,"alternative":3,"surface":1,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":0,"index":1,"type":0}]},{"atlas":101,"alternative":4,"surface":1,"type":0,"connections":[{"side":3,"index":1,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":101,"alternative":5,"surface":1,"type":0,"connections":[{"side":2,"index":2,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":101,"alternative":6,"surface":1,"type":0,"connections":[{"side":0,"index":1,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":101,"alternative":7,"surface":1,"type":0,"connections":[{"side":1,"index":1,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":102,"alternative":0,"surface":1,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":0,"index":1,"type":0}]},{"atlas":102,"alternative":1,"surface":1,"type":0,"connections":[{"side":0,"index":1,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":102,"alternative":2,"surface":1,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":102,"alternative":3,"surface":1,"type":0,"connections":[{"side":1,"index":1,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":103,"alternative":0,"surface":1,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":0,"index":0,"type":0}]},{"atlas":103,"alternative":1,"surface":1,"type":0,"connections":[{"side":0,"index":2,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":103,"alternative":2,"surface":1,"type":0,"connections":[{"side":1,"index":2,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":103,"alternative":3,"surface":1,"type":0,"connections":[{"side":2,"index":2,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":104,"alternative":0,"surface":1,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":0,"index":2,"type":0}]},{"atlas":104,"alternative":1,"surface":1,"type":0,"connections":[{"side":0,"index":0,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":104,"alternative":2,"surface":1,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":104,"alternative":3,"surface":1,"type":0,"connections":[{"side":0,"index":1,"type":0},{"side":2,"index":2,"type":0}]},{"atlas":104,"alternative":4,"surface":1,"type":0,"connections":[{"side":1,"index":0,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":104,"alternative":5,"surface":1,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":104,"alternative":6,"surface":1,"type":0,"connections":[{"side":0,"index":1,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":104,"alternative":7,"surface":1,"type":0,"connections":[{"side":1,"index":1,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":105,"alternative":0,"surface":1,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":105,"alternative":1,"surface":1,"type":0,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":106,"alternative":0,"surface":1,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":106,"alternative":1,"surface":1,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":106,"alternative":2,"surface":1,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":106,"alternative":3,"surface":1,"type":0,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":106,"alternative":4,"surface":1,"type":0,"connections":[{"side":2,"index":2,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":106,"alternative":5,"surface":1,"type":0,"connections":[{"side":0,"index":0,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":106,"alternative":6,"surface":1,"type":0,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":106,"alternative":7,"surface":1,"type":0,"connections":[{"side":1,"index":1,"type":0},{"side":0,"index":2,"type":0}]},{"atlas":107,"alternative":0,"surface":1,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":107,"alternative":1,"surface":1,"type":0,"connections":[{"side":3,"index":0,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":107,"alternative":2,"surface":1,"type":0,"connections":[{"side":0,"index":0,"type":0},{"side":2,"index":2,"type":0}]},{"atlas":107,"alternative":3,"surface":1,"type":0,"connections":[{"side":0,"index":0,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":107,"alternative":4,"surface":1,"type":0,"connections":[{"side":0,"index":2,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":107,"alternative":5,"surface":1,"type":0,"connections":[{"side":1,"index":0,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":107,"alternative":6,"surface":1,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":0,"index":2,"type":0}]},{"atlas":107,"alternative":7,"surface":1,"type":0,"connections":[{"side":2,"index":2,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":108,"alternative":0,"surface":1,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":108,"alternative":1,"surface":1,"type":0,"connections":[{"side":2,"index":2,"type":0},{"side":0,"index":2,"type":0}]},{"atlas":108,"alternative":2,"surface":1,"type":0,"connections":[{"side":0,"index":0,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":108,"alternative":3,"surface":1,"type":0,"connections":[{"side":1,"index":0,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":109,"alternative":0,"surface":1,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":109,"alternative":1,"surface":1,"type":0,"connections":[{"side":2,"index":2,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":109,"alternative":2,"surface":1,"type":0,"connections":[{"side":1,"index":0,"type":0},{"side":0,"index":2,"type":0}]},{"atlas":109,"alternative":3,"surface":1,"type":0,"connections":[{"side":0,"index":0,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":110,"alternative":0,"surface":1,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":110,"alternative":1,"surface":1,"type":0,"connections":[{"side":2,"index":2,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":110,"alternative":2,"surface":1,"type":0,"connections":[{"side":0,"index":0,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":110,"alternative":3,"surface":1,"type":0,"connections":[{"side":0,"index":2,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":121,"alternative":0,"surface":1,"type":5,"connections":[{"side":1,"index":1,"type":1},{"side":3,"index":1,"type":0}]},{"atlas":121,"alternative":1,"surface":1,"type":5,"connections":[{"side":2,"index":1,"type":1},{"side":1,"index":1,"type":0}]},{"atlas":121,"alternative":2,"surface":1,"type":5,"connections":[{"side":0,"index":1,"type":1},{"side":2,"index":1,"type":0}]},{"atlas":121,"alternative":3,"surface":1,"type":5,"connections":[{"side":3,"index":1,"type":1},{"side":0,"index":1,"type":0}]},{"atlas":121,"alternative":4,"surface":1,"type":5,"connections":[{"side":0,"index":1,"type":1},{"side":3,"index":1,"type":0}]},{"atlas":121,"alternative":5,"surface":1,"type":5,"connections":[{"side":2,"index":1,"type":1},{"side":0,"index":1,"type":0}]},{"atlas":121,"alternative":6,"surface":1,"type":5,"connections":[{"side":1,"index":1,"type":1},{"side":2,"index":1,"type":0}]},{"atlas":121,"alternative":7,"surface":1,"type":5,"connections":[{"side":3,"index":1,"type":1},{"side":1,"index":1,"type":0}]},{"atlas":122,"alternative":0,"surface":1,"type":5,"connections":[{"side":2,"index":1,"type":1},{"side":0,"index":1,"type":1}]},{"atlas":122,"alternative":1,"surface":1,"type":5,"connections":[{"side":0,"index":1,"type":1},{"side":3,"index":1,"type":1}]},{"atlas":122,"alternative":2,"surface":1,"type":5,"connections":[{"side":1,"index":1,"type":1},{"side":2,"index":1,"type":1}]},{"atlas":122,"alternative":3,"surface":1,"type":5,"connections":[{"side":3,"index":1,"type":1},{"side":1,"index":1,"type":1}]},{"atlas":111,"alternative":0,"surface":1,"type":1,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":111,"alternative":1,"surface":1,"type":1,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":112,"alternative":0,"surface":1,"type":2,"connections":[{"side":2,"index":1,"type":2},{"side":3,"index":1,"type":2}]},{"atlas":112,"alternative":1,"surface":1,"type":2,"connections":[{"side":0,"index":1,"type":2},{"side":1,"index":1,"type":2}]},{"atlas":113,"alternative":0,"surface":1,"type":2,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":1,"type":2}]},{"atlas":113,"alternative":1,"surface":1,"type":2,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":1,"type":2}]},{"atlas":113,"alternative":2,"surface":1,"type":2,"connections":[{"side":2,"index":1,"type":2},{"side":3,"index":1,"type":0}]},{"atlas":113,"alternative":3,"surface":1,"type":2,"connections":[{"side":0,"index":1,"type":2},{"side":1,"index":1,"type":0}]},{"atlas":114,"alternative":0,"surface":1,"type":3,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":114,"alternative":1,"surface":1,"type":3,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":115,"alternative":0,"surface":1,"type":3,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":115,"alternative":1,"surface":1,"type":3,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":116,"alternative":0,"surface":1,"type":1,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":116,"alternative":1,"surface":1,"type":1,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":117,"alternative":0,"surface":1,"type":4,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":117,"alternative":1,"surface":1,"type":4,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":118,"alternative":0,"surface":1,"type":1,"connections":[{"side":2,"index":0,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":118,"alternative":1,"surface":1,"type":1,"connections":[{"side":2,"index":2,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":118,"alternative":2,"surface":1,"type":1,"connections":[{"side":0,"index":0,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":118,"alternative":3,"surface":1,"type":1,"connections":[{"side":0,"index":2,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":119,"alternative":0,"surface":1,"type":3,"connections":[{"side":2,"index":0,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":119,"alternative":1,"surface":1,"type":3,"connections":[{"side":2,"index":2,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":119,"alternative":2,"surface":1,"type":3,"connections":[{"side":0,"index":0,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":119,"alternative":3,"surface":1,"type":3,"connections":[{"side":0,"index":2,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":120,"alternative":0,"surface":1,"type":3,"connections":[{"side":2,"index":0,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":120,"alternative":1,"surface":1,"type":3,"connections":[{"side":2,"index":2,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":120,"alternative":2,"surface":1,"type":3,"connections":[{"side":0,"index":0,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":120,"alternative":3,"surface":1,"type":3,"connections":[{"side":0,"index":2,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":123,"alternative":0,"surface":1,"type":1,"connections":[{"side":2,"index":0,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":123,"alternative":1,"surface":1,"type":1,"connections":[{"side":2,"index":2,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":123,"alternative":2,"surface":1,"type":1,"connections":[{"side":0,"index":0,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":123,"alternative":3,"surface":1,"type":1,"connections":[{"side":0,"index":2,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":124,"alternative":0,"surface":1,"type":5,"connections":[{"side":2,"index":1,"type":4},{"side":0,"index":0,"type":3}]},{"atlas":124,"alternative":1,"surface":1,"type":5,"connections":[{"side":0,"index":2,"type":3},{"side":3,"index":1,"type":4}]},{"atlas":124,"alternative":2,"surface":1,"type":5,"connections":[{"side":2,"index":1,"type":4},{"side":1,"index":0,"type":3}]},{"atlas":124,"alternative":3,"surface":1,"type":5,"connections":[{"side":2,"index":0,"type":3},{"side":0,"index":1,"type":4}]},{"atlas":124,"alternative":4,"surface":1,"type":5,"connections":[{"side":3,"index":1,"type":4},{"side":1,"index":2,"type":3}]},{"atlas":124,"alternative":5,"surface":1,"type":5,"connections":[{"side":2,"index":2,"type":3},{"side":1,"index":1,"type":4}]},{"atlas":124,"alternative":6,"surface":1,"type":5,"connections":[{"side":0,"index":1,"type":4},{"side":3,"index":0,"type":3}]},{"atlas":124,"alternative":7,"surface":1,"type":5,"connections":[{"side":1,"index":1,"type":4},{"side":3,"index":2,"type":3}]},{"atlas":125,"alternative":0,"surface":1,"type":5,"connections":[{"side":2,"index":1,"type":4},{"side":0,"index":0,"type":0}]},{"atlas":125,"alternative":1,"surface":1,"type":5,"connections":[{"side":0,"index":2,"type":0},{"side":3,"index":1,"type":4}]},{"atlas":125,"alternative":2,"surface":1,"type":5,"connections":[{"side":2,"index":1,"type":4},{"side":1,"index":0,"type":0}]},{"atlas":125,"alternative":3,"surface":1,"type":5,"connections":[{"side":2,"index":0,"type":0},{"side":0,"index":1,"type":4}]},{"atlas":125,"alternative":4,"surface":1,"type":5,"connections":[{"side":3,"index":1,"type":4},{"side":1,"index":2,"type":0}]},{"atlas":125,"alternative":5,"surface":1,"type":5,"connections":[{"side":2,"index":2,"type":0},{"side":1,"index":1,"type":4}]},{"atlas":125,"alternative":6,"surface":1,"type":5,"connections":[{"side":0,"index":1,"type":4},{"side":3,"index":0,"type":0}]},{"atlas":125,"alternative":7,"surface":1,"type":5,"connections":[{"side":1,"index":1,"type":4},{"side":3,"index":2,"type":0}]},{"atlas":126,"alternative":0,"surface":1,"type":5,"connections":[{"side":2,"index":1,"type":3},{"side":3,"index":0,"type":3}]},{"atlas":126,"alternative":1,"surface":1,"type":5,"connections":[{"side":2,"index":0,"type":3},{"side":3,"index":1,"type":3}]},{"atlas":126,"alternative":2,"surface":1,"type":5,"connections":[{"side":2,"index":1,"type":3},{"side":3,"index":2,"type":3}]},{"atlas":126,"alternative":3,"surface":1,"type":5,"connections":[{"side":0,"index":1,"type":3},{"side":1,"index":0,"type":3}]},{"atlas":126,"alternative":4,"surface":1,"type":5,"connections":[{"side":2,"index":2,"type":3},{"side":3,"index":1,"type":3}]},{"atlas":126,"alternative":5,"surface":1,"type":5,"connections":[{"side":0,"index":0,"type":3},{"side":1,"index":1,"type":3}]},{"atlas":126,"alternative":6,"surface":1,"type":5,"connections":[{"side":0,"index":1,"type":3},{"side":1,"index":2,"type":3}]},{"atlas":126,"alternative":7,"surface":1,"type":5,"connections":[{"side":1,"index":1,"type":3},{"side":0,"index":2,"type":3}]},{"atlas":127,"alternative":0,"surface":1,"type":5,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":0,"type":3}]},{"atlas":127,"alternative":1,"surface":1,"type":5,"connections":[{"side":2,"index":0,"type":3},{"side":3,"index":1,"type":0}]},{"atlas":127,"alternative":2,"surface":1,"type":5,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":2,"type":3}]},{"atlas":127,"alternative":3,"surface":1,"type":5,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":0,"type":3}]},{"atlas":127,"alternative":4,"surface":1,"type":5,"connections":[{"side":2,"index":2,"type":3},{"side":3,"index":1,"type":0}]},{"atlas":127,"alternative":5,"surface":1,"type":5,"connections":[{"side":0,"index":0,"type":3},{"side":1,"index":1,"type":0}]},{"atlas":127,"alternative":6,"surface":1,"type":5,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":2,"type":3}]},{"atlas":127,"alternative":7,"surface":1,"type":5,"connections":[{"side":1,"index":1,"type":0},{"side":0,"index":2,"type":3}]},{"atlas":128,"alternative":0,"surface":1,"type":5,"connections":[{"side":2,"index":1,"type":3},{"side":3,"index":0,"type":0}]},{"atlas":128,"alternative":1,"surface":1,"type":5,"connections":[{"side":2,"index":0,"type":0},{"side":3,"index":1,"type":3}]},{"atlas":128,"alternative":2,"surface":1,"type":5,"connections":[{"side":2,"index":1,"type":3},{"side":3,"index":2,"type":0}]},{"atlas":128,"alternative":3,"surface":1,"type":5,"connections":[{"side":0,"index":1,"type":3},{"side":1,"index":0,"type":0}]},{"atlas":128,"alternative":4,"surface":1,"type":5,"connections":[{"side":2,"index":2,"type":0},{"side":3,"index":1,"type":3}]},{"atlas":128,"alternative":5,"surface":1,"type":5,"connections":[{"side":0,"index":0,"type":0},{"side":1,"index":1,"type":3}]},{"atlas":128,"alternative":6,"surface":1,"type":5,"connections":[{"side":0,"index":1,"type":3},{"side":1,"index":2,"type":0}]},{"atlas":128,"alternative":7,"surface":1,"type":5,"connections":[{"side":1,"index":1,"type":3},{"side":0,"index":2,"type":0}]},{"atlas":201,"alternative":0,"surface":2,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":0,"index":0,"type":0}]},{"atlas":201,"alternative":1,"surface":2,"type":0,"connections":[{"side":0,"index":2,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":201,"alternative":2,"surface":2,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":201,"alternative":3,"surface":2,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":0,"index":1,"type":0}]},{"atlas":201,"alternative":4,"surface":2,"type":0,"connections":[{"side":3,"index":1,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":201,"alternative":5,"surface":2,"type":0,"connections":[{"side":2,"index":2,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":201,"alternative":6,"surface":2,"type":0,"connections":[{"side":0,"index":1,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":201,"alternative":7,"surface":2,"type":0,"connections":[{"side":1,"index":1,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":202,"alternative":0,"surface":2,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":0,"index":1,"type":0}]},{"atlas":202,"alternative":1,"surface":2,"type":0,"connections":[{"side":0,"index":1,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":202,"alternative":2,"surface":2,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":202,"alternative":3,"surface":2,"type":0,"connections":[{"side":1,"index":1,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":203,"alternative":0,"surface":2,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":0,"index":0,"type":0}]},{"atlas":203,"alternative":1,"surface":2,"type":0,"connections":[{"side":0,"index":2,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":203,"alternative":2,"surface":2,"type":0,"connections":[{"side":1,"index":2,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":203,"alternative":3,"surface":2,"type":0,"connections":[{"side":2,"index":2,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":204,"alternative":0,"surface":2,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":0,"index":2,"type":0}]},{"atlas":204,"alternative":1,"surface":2,"type":0,"connections":[{"side":0,"index":0,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":204,"alternative":2,"surface":2,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":204,"alternative":3,"surface":2,"type":0,"connections":[{"side":0,"index":1,"type":0},{"side":2,"index":2,"type":0}]},{"atlas":204,"alternative":4,"surface":2,"type":0,"connections":[{"side":1,"index":0,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":204,"alternative":5,"surface":2,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":204,"alternative":6,"surface":2,"type":0,"connections":[{"side":0,"index":1,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":204,"alternative":7,"surface":2,"type":0,"connections":[{"side":1,"index":1,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":205,"alternative":0,"surface":2,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":205,"alternative":1,"surface":2,"type":0,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":206,"alternative":0,"surface":2,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":206,"alternative":1,"surface":2,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":206,"alternative":2,"surface":2,"type":0,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":206,"alternative":3,"surface":2,"type":0,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":206,"alternative":4,"surface":2,"type":0,"connections":[{"side":2,"index":2,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":206,"alternative":5,"surface":2,"type":0,"connections":[{"side":0,"index":0,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":206,"alternative":6,"surface":2,"type":0,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":206,"alternative":7,"surface":2,"type":0,"connections":[{"side":1,"index":1,"type":0},{"side":0,"index":2,"type":0}]},{"atlas":207,"alternative":0,"surface":2,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":207,"alternative":1,"surface":2,"type":0,"connections":[{"side":3,"index":0,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":207,"alternative":2,"surface":2,"type":0,"connections":[{"side":0,"index":0,"type":0},{"side":2,"index":2,"type":0}]},{"atlas":207,"alternative":3,"surface":2,"type":0,"connections":[{"side":0,"index":0,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":207,"alternative":4,"surface":2,"type":0,"connections":[{"side":0,"index":2,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":207,"alternative":5,"surface":2,"type":0,"connections":[{"side":1,"index":0,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":207,"alternative":6,"surface":2,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":0,"index":2,"type":0}]},{"atlas":207,"alternative":7,"surface":2,"type":0,"connections":[{"side":2,"index":2,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":208,"alternative":0,"surface":2,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":208,"alternative":1,"surface":2,"type":0,"connections":[{"side":2,"index":2,"type":0},{"side":0,"index":2,"type":0}]},{"atlas":208,"alternative":2,"surface":2,"type":0,"connections":[{"side":0,"index":0,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":208,"alternative":3,"surface":2,"type":0,"connections":[{"side":1,"index":0,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":209,"alternative":0,"surface":2,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":209,"alternative":1,"surface":2,"type":0,"connections":[{"side":2,"index":2,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":209,"alternative":2,"surface":2,"type":0,"connections":[{"side":1,"index":0,"type":0},{"side":0,"index":2,"type":0}]},{"atlas":209,"alternative":3,"surface":2,"type":0,"connections":[{"side":0,"index":0,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":210,"alternative":0,"surface":2,"type":0,"connections":[{"side":2,"index":0,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":210,"alternative":1,"surface":2,"type":0,"connections":[{"side":2,"index":2,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":210,"alternative":2,"surface":2,"type":0,"connections":[{"side":0,"index":0,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":210,"alternative":3,"surface":2,"type":0,"connections":[{"side":0,"index":2,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":221,"alternative":0,"surface":2,"type":5,"connections":[{"side":1,"index":1,"type":1},{"side":3,"index":1,"type":0}]},{"atlas":221,"alternative":1,"surface":2,"type":5,"connections":[{"side":2,"index":1,"type":1},{"side":1,"index":1,"type":0}]},{"atlas":221,"alternative":2,"surface":2,"type":5,"connections":[{"side":0,"index":1,"type":1},{"side":2,"index":1,"type":0}]},{"atlas":221,"alternative":3,"surface":2,"type":5,"connections":[{"side":3,"index":1,"type":1},{"side":0,"index":1,"type":0}]},{"atlas":221,"alternative":4,"surface":2,"type":5,"connections":[{"side":0,"index":1,"type":1},{"side":3,"index":1,"type":0}]},{"atlas":221,"alternative":5,"surface":2,"type":5,"connections":[{"side":2,"index":1,"type":1},{"side":0,"index":1,"type":0}]},{"atlas":221,"alternative":6,"surface":2,"type":5,"connections":[{"side":1,"index":1,"type":1},{"side":2,"index":1,"type":0}]},{"atlas":221,"alternative":7,"surface":2,"type":5,"connections":[{"side":3,"index":1,"type":1},{"side":1,"index":1,"type":0}]},{"atlas":222,"alternative":0,"surface":2,"type":5,"connections":[{"side":2,"index":1,"type":1},{"side":0,"index":1,"type":1}]},{"atlas":222,"alternative":1,"surface":2,"type":5,"connections":[{"side":0,"index":1,"type":1},{"side":3,"index":1,"type":1}]},{"atlas":222,"alternative":2,"surface":2,"type":5,"connections":[{"side":1,"index":1,"type":1},{"side":2,"index":1,"type":1}]},{"atlas":222,"alternative":3,"surface":2,"type":5,"connections":[{"side":3,"index":1,"type":1},{"side":1,"index":1,"type":1}]},{"atlas":211,"alternative":0,"surface":2,"type":1,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":211,"alternative":1,"surface":2,"type":1,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":212,"alternative":0,"surface":2,"type":2,"connections":[{"side":2,"index":1,"type":2},{"side":3,"index":1,"type":2}]},{"atlas":212,"alternative":1,"surface":2,"type":2,"connections":[{"side":0,"index":1,"type":2},{"side":1,"index":1,"type":2}]},{"atlas":213,"alternative":0,"surface":2,"type":2,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":1,"type":2}]},{"atlas":213,"alternative":1,"surface":2,"type":2,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":1,"type":2}]},{"atlas":213,"alternative":2,"surface":2,"type":2,"connections":[{"side":2,"index":1,"type":2},{"side":3,"index":1,"type":0}]},{"atlas":213,"alternative":3,"surface":2,"type":2,"connections":[{"side":0,"index":1,"type":2},{"side":1,"index":1,"type":0}]},{"atlas":214,"alternative":0,"surface":2,"type":3,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":214,"alternative":1,"surface":2,"type":3,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":215,"alternative":0,"surface":2,"type":3,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":215,"alternative":1,"surface":2,"type":3,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":216,"alternative":0,"surface":2,"type":1,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":216,"alternative":1,"surface":2,"type":1,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":217,"alternative":0,"surface":2,"type":4,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":1,"type":0}]},{"atlas":217,"alternative":1,"surface":2,"type":4,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":1,"type":0}]},{"atlas":218,"alternative":0,"surface":2,"type":1,"connections":[{"side":2,"index":0,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":218,"alternative":1,"surface":2,"type":1,"connections":[{"side":2,"index":2,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":218,"alternative":2,"surface":2,"type":1,"connections":[{"side":0,"index":0,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":218,"alternative":3,"surface":2,"type":1,"connections":[{"side":0,"index":2,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":219,"alternative":0,"surface":2,"type":3,"connections":[{"side":2,"index":0,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":219,"alternative":1,"surface":2,"type":3,"connections":[{"side":2,"index":2,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":219,"alternative":2,"surface":2,"type":3,"connections":[{"side":0,"index":0,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":219,"alternative":3,"surface":2,"type":3,"connections":[{"side":0,"index":2,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":220,"alternative":0,"surface":2,"type":3,"connections":[{"side":2,"index":0,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":220,"alternative":1,"surface":2,"type":3,"connections":[{"side":2,"index":2,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":220,"alternative":2,"surface":2,"type":3,"connections":[{"side":0,"index":0,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":220,"alternative":3,"surface":2,"type":3,"connections":[{"side":0,"index":2,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":223,"alternative":0,"surface":2,"type":1,"connections":[{"side":2,"index":0,"type":0},{"side":3,"index":0,"type":0}]},{"atlas":223,"alternative":1,"surface":2,"type":1,"connections":[{"side":2,"index":2,"type":0},{"side":3,"index":2,"type":0}]},{"atlas":223,"alternative":2,"surface":2,"type":1,"connections":[{"side":0,"index":0,"type":0},{"side":1,"index":0,"type":0}]},{"atlas":223,"alternative":3,"surface":2,"type":1,"connections":[{"side":0,"index":2,"type":0},{"side":1,"index":2,"type":0}]},{"atlas":224,"alternative":0,"surface":2,"type":5,"connections":[{"side":2,"index":1,"type":4},{"side":0,"index":0,"type":3}]},{"atlas":224,"alternative":1,"surface":2,"type":5,"connections":[{"side":0,"index":2,"type":3},{"side":3,"index":1,"type":4}]},{"atlas":224,"alternative":2,"surface":2,"type":5,"connections":[{"side":2,"index":1,"type":4},{"side":1,"index":0,"type":3}]},{"atlas":224,"alternative":3,"surface":2,"type":5,"connections":[{"side":2,"index":0,"type":3},{"side":0,"index":1,"type":4}]},{"atlas":224,"alternative":4,"surface":2,"type":5,"connections":[{"side":3,"index":1,"type":4},{"side":1,"index":2,"type":3}]},{"atlas":224,"alternative":5,"surface":2,"type":5,"connections":[{"side":2,"index":2,"type":3},{"side":1,"index":1,"type":4}]},{"atlas":224,"alternative":6,"surface":2,"type":5,"connections":[{"side":0,"index":1,"type":4},{"side":3,"index":0,"type":3}]},{"atlas":224,"alternative":7,"surface":2,"type":5,"connections":[{"side":1,"index":1,"type":4},{"side":3,"index":2,"type":3}]},{"atlas":225,"alternative":0,"surface":2,"type":5,"connections":[{"side":2,"index":1,"type":4},{"side":0,"index":0,"type":0}]},{"atlas":225,"alternative":1,"surface":2,"type":5,"connections":[{"side":0,"index":2,"type":0},{"side":3,"index":1,"type":4}]},{"atlas":225,"alternative":2,"surface":2,"type":5,"connections":[{"side":2,"index":1,"type":4},{"side":1,"index":0,"type":0}]},{"atlas":225,"alternative":3,"surface":2,"type":5,"connections":[{"side":2,"index":0,"type":0},{"side":0,"index":1,"type":4}]},{"atlas":225,"alternative":4,"surface":2,"type":5,"connections":[{"side":3,"index":1,"type":4},{"side":1,"index":2,"type":0}]},{"atlas":225,"alternative":5,"surface":2,"type":5,"connections":[{"side":2,"index":2,"type":0},{"side":1,"index":1,"type":4}]},{"atlas":225,"alternative":6,"surface":2,"type":5,"connections":[{"side":0,"index":1,"type":4},{"side":3,"index":0,"type":0}]},{"atlas":225,"alternative":7,"surface":2,"type":5,"connections":[{"side":1,"index":1,"type":4},{"side":3,"index":2,"type":0}]},{"atlas":226,"alternative":0,"surface":2,"type":5,"connections":[{"side":2,"index":1,"type":3},{"side":3,"index":0,"type":3}]},{"atlas":226,"alternative":1,"surface":2,"type":5,"connections":[{"side":2,"index":0,"type":3},{"side":3,"index":1,"type":3}]},{"atlas":226,"alternative":2,"surface":2,"type":5,"connections":[{"side":2,"index":1,"type":3},{"side":3,"index":2,"type":3}]},{"atlas":226,"alternative":3,"surface":2,"type":5,"connections":[{"side":0,"index":1,"type":3},{"side":1,"index":0,"type":3}]},{"atlas":226,"alternative":4,"surface":2,"type":5,"connections":[{"side":2,"index":2,"type":3},{"side":3,"index":1,"type":3}]},{"atlas":226,"alternative":5,"surface":2,"type":5,"connections":[{"side":0,"index":0,"type":3},{"side":1,"index":1,"type":3}]},{"atlas":226,"alternative":6,"surface":2,"type":5,"connections":[{"side":0,"index":1,"type":3},{"side":1,"index":2,"type":3}]},{"atlas":226,"alternative":7,"surface":2,"type":5,"connections":[{"side":1,"index":1,"type":3},{"side":0,"index":2,"type":3}]},{"atlas":227,"alternative":0,"surface":2,"type":5,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":0,"type":3}]},{"atlas":227,"alternative":1,"surface":2,"type":5,"connections":[{"side":2,"index":0,"type":3},{"side":3,"index":1,"type":0}]},{"atlas":227,"alternative":2,"surface":2,"type":5,"connections":[{"side":2,"index":1,"type":0},{"side":3,"index":2,"type":3}]},{"atlas":227,"alternative":3,"surface":2,"type":5,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":0,"type":3}]},{"atlas":227,"alternative":4,"surface":2,"type":5,"connections":[{"side":2,"index":2,"type":3},{"side":3,"index":1,"type":0}]},{"atlas":227,"alternative":5,"surface":2,"type":5,"connections":[{"side":0,"index":0,"type":3},{"side":1,"index":1,"type":0}]},{"atlas":227,"alternative":6,"surface":2,"type":5,"connections":[{"side":0,"index":1,"type":0},{"side":1,"index":2,"type":3}]},{"atlas":227,"alternative":7,"surface":2,"type":5,"connections":[{"side":1,"index":1,"type":0},{"side":0,"index":2,"type":3}]},{"atlas":228,"alternative":0,"surface":2,"type":5,"connections":[{"side":2,"index":1,"type":3},{"side":3,"index":0,"type":0}]},{"atlas":228,"alternative":1,"surface":2,"type":5,"connections":[{"side":2,"index":0,"type":0},{"side":3,"index":1,"type":3}]},{"atlas":228,"alternative":2,"surface":2,"type":5,"connections":[{"side":2,"index":1,"type":3},{"side":3,"index":2,"type":0}]},{"atlas":228,"alternative":3,"surface":2,"type":5,"connections":[{"side":0,"index":1,"type":3},{"side":1,"index":0,"type":0}]},{"atlas":228,"alternative":4,"surface":2,"type":5,"connections":[{"side":2,"index":2,"type":0},{"side":3,"index":1,"type":3}]},{"atlas":228,"alternative":5,"surface":2,"type":5,"connections":[{"side":0,"index":0,"type":0},{"side":1,"index":1,"type":3}]},{"atlas":228,"alternative":6,"surface":2,"type":5,"connections":[{"side":0,"index":1,"type":3},{"side":1,"index":2,"type":0}]},{"atlas":228,"alternative":7,"surface":2,"type":5,"connections":[{"side":1,"index":1,"type":3},{"side":0,"index":2,"type":0}]}]} \ No newline at end of file diff --git a/github-simulator/source/src/data/track_resources.json b/github-simulator/source/src/data/track_resources.json deleted file mode 100644 index 538f218dfe2af979a8c300ecba1902e10bf45d89..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/data/track_resources.json +++ /dev/null @@ -1 +0,0 @@ -[{"polygons":[[[-352,-384],[-352,-208],[-368,-208],[-368,-128],[-384,-112],[-384,-384]],[[-160,-384],[-160,-224],[-160,-80],[-192,16],[-256,80],[-320,96],[-384,96],[-384,384],[384,384],[384,-384]]],"shapes":[[[-368,-128],[-384,-112],[-384,-384],[-368,-208]],[[-368,-208],[-384,-384],[-352,-384],[-352,-208]],[[-160,-384],[384,-384],[384,384],[-160,-224]],[[-160,-80],[-160,-224],[384,384]],[[-192,16],[-160,-80],[384,384]],[[-192,16],[384,384],[-384,384],[-256,80]],[[-320,96],[-256,80],[-384,384]],[[-320,96],[-384,384],[-384,96]]]},{"polygons":[[[352,-384],[352,-208],[368,-208],[368,-128],[384,-112],[384,-384]],[[160,-384],[160,-224],[160,-80],[192,16],[256,80],[320,96],[384,96],[384,384],[-384,384],[-384,-384]]],"shapes":[[[384,-384],[384,-112],[368,-128],[368,-208]],[[352,-384],[384,-384],[368,-208],[352,-208]],[[-384,-384],[160,-384],[160,-224],[-384,384]],[[-384,384],[160,-224],[160,-80]],[[-384,384],[160,-80],[192,16]],[[384,384],[-384,384],[192,16],[256,80]],[[384,384],[256,80],[320,96]],[[384,384],[320,96],[384,96]]]},{"polygons":[[[-352,384],[-352,208],[-368,208],[-368,128],[-384,112],[-384,384]],[[-160,384],[-160,224],[-160,80],[-192,-16],[-256,-80],[-320,-96],[-384,-96],[-384,-384],[384,-384],[384,384]]],"shapes":[[[-384,384],[-384,112],[-368,128],[-368,208]],[[-352,384],[-384,384],[-368,208],[-352,208]],[[384,384],[-160,384],[-160,224],[384,-384]],[[384,-384],[-160,224],[-160,80]],[[384,-384],[-160,80],[-192,-16]],[[-384,-384],[384,-384],[-192,-16],[-256,-80]],[[-384,-384],[-256,-80],[-320,-96]],[[-384,-384],[-320,-96],[-384,-96]]]},{"polygons":[[[-384,-352],[-208,-352],[-208,-368],[-128,-368],[-112,-384],[-384,-384]],[[-384,-160],[-224,-160],[-80,-160],[16,-192],[80,-256],[96,-320],[96,-384],[384,-384],[384,384],[-384,384]]],"shapes":[[[-384,-384],[-112,-384],[-128,-368],[-208,-368]],[[-384,-352],[-384,-384],[-208,-368],[-208,-352]],[[-384,384],[-384,-160],[-224,-160],[384,384]],[[384,384],[-224,-160],[-80,-160]],[[384,384],[-80,-160],[16,-192]],[[384,-384],[384,384],[16,-192],[80,-256]],[[384,-384],[80,-256],[96,-320]],[[384,-384],[96,-320],[96,-384]]]},{"polygons":[[[352,384],[352,208],[368,208],[368,128],[384,112],[384,384]],[[160,384],[160,224],[160,80],[192,-16],[256,-80],[320,-96],[384,-96],[384,-384],[-384,-384],[-384,384]]],"shapes":[[[368,128],[384,112],[384,384],[368,208]],[[368,208],[384,384],[352,384],[352,208]],[[160,384],[-384,384],[-384,-384],[160,224]],[[160,80],[160,224],[-384,-384]],[[192,-16],[160,80],[-384,-384]],[[192,-16],[-384,-384],[384,-384],[256,-80]],[[320,-96],[256,-80],[384,-384]],[[320,-96],[384,-384],[384,-96]]]},{"polygons":[[[-384,352],[-208,352],[-208,368],[-128,368],[-112,384],[-384,384]],[[-384,160],[-224,160],[-80,160],[16,192],[80,256],[96,320],[96,384],[384,384],[384,-384],[-384,-384]]],"shapes":[[[-128,368],[-112,384],[-384,384],[-208,368]],[[-208,368],[-384,384],[-384,352],[-208,352]],[[-384,160],[-384,-384],[384,-384],[-224,160]],[[-80,160],[-224,160],[384,-384]],[[16,192],[-80,160],[384,-384]],[[16,192],[384,-384],[384,384],[80,256]],[[96,320],[80,256],[384,384]],[[96,320],[384,384],[96,384]]]},{"polygons":[[[384,-352],[208,-352],[208,-368],[128,-368],[112,-384],[384,-384]],[[384,-160],[224,-160],[80,-160],[-16,-192],[-80,-256],[-96,-320],[-96,-384],[-384,-384],[-384,384],[384,384]]],"shapes":[[[128,-368],[112,-384],[384,-384],[208,-368]],[[208,-368],[384,-384],[384,-352],[208,-352]],[[384,-160],[384,384],[-384,384],[224,-160]],[[80,-160],[224,-160],[-384,384]],[[-16,-192],[80,-160],[-384,384]],[[-16,-192],[-384,384],[-384,-384],[-80,-256]],[[-96,-320],[-80,-256],[-384,-384]],[[-96,-320],[-384,-384],[-96,-384]]]},{"polygons":[[[384,352],[208,352],[208,368],[128,368],[112,384],[384,384]],[[384,160],[224,160],[80,160],[-16,192],[-80,256],[-96,320],[-96,384],[-384,384],[-384,-384],[384,-384]]],"shapes":[[[384,384],[112,384],[128,368],[208,368]],[[384,352],[384,384],[208,368],[208,352]],[[384,-384],[384,160],[224,160],[-384,-384]],[[-384,-384],[224,160],[80,160]],[[-384,-384],[80,160],[-16,192]],[[-384,384],[-384,-384],[-16,192],[-80,256]],[[-384,384],[-80,256],[-96,320]],[[-384,384],[-96,320],[-96,384]]]},{"polygons":[[[-384,96],[-288,96],[-176,80],[-96,48],[0,-16],[32,-64],[64,-128],[96,-240],[96,-384],[384,-384],[384,384],[-384,384]],[[-384,-112],[-240,-128],[-192,-144],[-160,-160],[-144,-192],[-128,-240],[-112,-336],[-112,-384],[-384,-384]]],"shapes":[[[-384,384],[-384,96],[-288,96],[384,384]],[[384,384],[-288,96],[-176,80]],[[384,384],[-176,80],[-96,48]],[[384,384],[-96,48],[0,-16]],[[384,-384],[384,384],[0,-16],[32,-64]],[[384,-384],[32,-64],[64,-128]],[[384,-384],[64,-128],[96,-240]],[[384,-384],[96,-240],[96,-384]],[[-384,-384],[-112,-384],[-112,-336],[-128,-240],[-144,-192],[-160,-160],[-192,-144],[-240,-128],[-384,-112]]]},{"polygons":[[[-96,-384],[-96,-288],[-80,-176],[-48,-96],[16,0],[64,32],[128,64],[240,96],[384,96],[384,384],[-384,384],[-384,-384]],[[112,-384],[128,-240],[144,-192],[160,-160],[192,-144],[240,-128],[336,-112],[384,-112],[384,-384]]],"shapes":[[[-384,-384],[-96,-384],[-96,-288],[-384,384]],[[-384,384],[-96,-288],[-80,-176]],[[-384,384],[-80,-176],[-48,-96]],[[-384,384],[-48,-96],[16,0]],[[384,384],[-384,384],[16,0],[64,32]],[[384,384],[64,32],[128,64]],[[384,384],[128,64],[240,96]],[[384,384],[240,96],[384,96]],[[384,-384],[384,-112],[336,-112],[240,-128],[192,-144],[160,-160],[144,-192],[128,-240],[112,-384]]]},{"polygons":[[[96,384],[96,288],[80,176],[48,96],[-16,0],[-64,-32],[-128,-64],[-240,-96],[-384,-96],[-384,-384],[384,-384],[384,384]],[[-112,384],[-128,240],[-144,192],[-160,160],[-192,144],[-240,128],[-336,112],[-384,112],[-384,384]]],"shapes":[[[384,384],[96,384],[96,288],[384,-384]],[[384,-384],[96,288],[80,176]],[[384,-384],[80,176],[48,96]],[[384,-384],[48,96],[-16,0]],[[-384,-384],[384,-384],[-16,0],[-64,-32]],[[-384,-384],[-64,-32],[-128,-64]],[[-384,-384],[-128,-64],[-240,-96]],[[-384,-384],[-240,-96],[-384,-96]],[[-384,384],[-384,112],[-336,112],[-240,128],[-192,144],[-160,160],[-144,192],[-128,240],[-112,384]]]},{"polygons":[[[384,-96],[288,-96],[176,-80],[96,-48],[0,16],[-32,64],[-64,128],[-96,240],[-96,384],[-384,384],[-384,-384],[384,-384]],[[384,112],[240,128],[192,144],[160,160],[144,192],[128,240],[112,336],[112,384],[384,384]]],"shapes":[[[384,-384],[384,-96],[288,-96],[-384,-384]],[[-384,-384],[288,-96],[176,-80]],[[-384,-384],[176,-80],[96,-48]],[[-384,-384],[96,-48],[0,16]],[[-384,384],[-384,-384],[0,16],[-32,64]],[[-384,384],[-32,64],[-64,128]],[[-384,384],[-64,128],[-96,240]],[[-384,384],[-96,240],[-96,384]],[[384,384],[112,384],[112,336],[128,240],[144,192],[160,160],[192,144],[240,128],[384,112]]]},{"polygons":[[[-384,-368],[-368,-368],[-368,-384],[-384,-384]],[[-160,-384],[-160,-288],[-176,-240],[-240,-176],[-304,-160],[-384,-160],[-384,384],[384,384],[384,-384]]],"shapes":[[[-384,-384],[-368,-384],[-368,-368],[-384,-368]],[[-160,-384],[384,-384],[384,384],[-160,-288]],[[-176,-240],[-160,-288],[384,384]],[[-176,-240],[384,384],[-384,384],[-240,-176]],[[-304,-160],[-240,-176],[-384,384]],[[-304,-160],[-384,384],[-384,-160]]]},{"polygons":[[[368,-384],[368,-368],[384,-368],[384,-384]],[[384,-160],[288,-160],[240,-176],[176,-240],[160,-304],[160,-384],[-384,-384],[-384,384],[384,384]]],"shapes":[[[384,-384],[384,-368],[368,-368],[368,-384]],[[384,-160],[384,384],[-384,384],[288,-160]],[[240,-176],[288,-160],[-384,384]],[[240,-176],[-384,384],[-384,-384],[176,-240]],[[160,-304],[176,-240],[-384,-384]],[[160,-304],[-384,-384],[160,-384]]]},{"polygons":[[[384,368],[368,368],[368,384],[384,384]],[[160,384],[160,288],[176,240],[240,176],[304,160],[384,160],[384,-384],[-384,-384],[-384,384]]],"shapes":[[[384,384],[368,384],[368,368],[384,368]],[[160,384],[-384,384],[-384,-384],[160,288]],[[176,240],[160,288],[-384,-384]],[[176,240],[-384,-384],[384,-384],[240,176]],[[304,160],[240,176],[384,-384]],[[304,160],[384,-384],[384,160]]]},{"polygons":[[[-368,384],[-368,368],[-384,368],[-384,384]],[[-384,160],[-288,160],[-240,176],[-176,240],[-160,304],[-160,384],[384,384],[384,-384],[-384,-384]]],"shapes":[[[-384,384],[-384,368],[-368,368],[-368,384]],[[-384,160],[-384,-384],[384,-384],[-288,160]],[[-240,176],[-288,160],[384,-384]],[[-240,176],[384,-384],[384,384],[-176,240]],[[-160,304],[-176,240],[384,384]],[[-160,304],[384,384],[-160,384]]]},{"polygons":[[[-384,-96],[-128,-96],[-20.5,-101.5],[-21.5,-120.5],[63,-129.5],[112,-144],[128,-160],[139,-230],[159,-229.5],[160,-384],[-384,-384]],[[-384,95],[-99.5,92.5],[107,74.5],[212.5,41.5],[291.5,-32],[336,-130],[349.5,-263],[350.5,-384],[384,-384],[384,384],[-384,384]]],"shapes":[[[160,-384],[159,-229.5],[139,-230],[-384,-384]],[[-128,-96],[-384,-96],[-384,-384],[139,-230],[128,-160],[-21.5,-120.5]],[[-21.5,-120.5],[-20.5,-101.5],[-128,-96]],[[63,-129.5],[-21.5,-120.5],[128,-160],[112,-144]],[[350.5,-384],[384,-384],[384,384],[349.5,-263]],[[336,-130],[349.5,-263],[384,384]],[[291.5,-32],[336,-130],[384,384]],[[212.5,41.5],[291.5,-32],[384,384]],[[212.5,41.5],[384,384],[-384,384],[107,74.5]],[[-99.5,92.5],[107,74.5],[-384,384]],[[-384,384],[-384,95],[-99.5,92.5]]]},{"polygons":[[[384,-96],[128,-96],[20.5,-101.5],[21.5,-120.5],[-63,-129.5],[-112,-144],[-128,-160],[-139,-230],[-159,-229.5],[-160,-384],[384,-384]],[[384,95],[99.5,92.5],[-107,74.5],[-212.5,41.5],[-291.5,-32],[-336,-130],[-349.5,-263],[-350.5,-384],[-384,-384],[-384,384],[384,384]]],"shapes":[[[-139,-230],[-159,-229.5],[-160,-384],[384,-384]],[[-128,-160],[-139,-230],[384,-384],[384,-96],[128,-96],[21.5,-120.5]],[[128,-96],[20.5,-101.5],[21.5,-120.5]],[[-128,-160],[21.5,-120.5],[-63,-129.5],[-112,-144]],[[384,95],[384,384],[-384,384],[99.5,92.5]],[[-107,74.5],[99.5,92.5],[-384,384]],[[-212.5,41.5],[-107,74.5],[-384,384]],[[-384,384],[-384,-384],[-350.5,-384],[-349.5,-263]],[[-384,384],[-349.5,-263],[-336,-130]],[[-384,384],[-336,-130],[-291.5,-32]],[[-212.5,41.5],[-384,384],[-291.5,-32]]]},{"polygons":[[[-384,96],[-128,96],[-20.5,101.5],[-21.5,120.5],[63,129.5],[112,144],[128,160],[139,230],[159,229.5],[160,384],[-384,384]],[[-384,-95],[-99.5,-92.5],[107,-74.5],[212.5,-41.5],[291.5,32],[336,130],[349.5,263],[350.5,384],[384,384],[384,-384],[-384,-384]]],"shapes":[[[139,230],[159,229.5],[160,384],[-384,384]],[[128,160],[139,230],[-384,384],[-384,96],[-128,96],[-21.5,120.5]],[[-128,96],[-20.5,101.5],[-21.5,120.5]],[[128,160],[-21.5,120.5],[63,129.5],[112,144]],[[-384,-95],[-384,-384],[384,-384],[-99.5,-92.5]],[[107,-74.5],[-99.5,-92.5],[384,-384]],[[212.5,-41.5],[107,-74.5],[384,-384]],[[384,-384],[384,384],[350.5,384],[349.5,263]],[[384,-384],[349.5,263],[336,130]],[[384,-384],[336,130],[291.5,32]],[[212.5,-41.5],[384,-384],[291.5,32]]]},{"polygons":[[[-96,-384],[-96,-128],[-101.5,-20.5],[-120.5,-21.5],[-129.5,63],[-144,112],[-160,128],[-230,139],[-229.5,159],[-384,160],[-384,-384]],[[95,-384],[92.5,-99.5],[74.5,107],[41.5,212.5],[-32,291.5],[-130,336],[-263,349.5],[-384,350.5],[-384,384],[384,384],[384,-384]]],"shapes":[[[-230,139],[-229.5,159],[-384,160],[-384,-384]],[[-160,128],[-230,139],[-384,-384],[-96,-384],[-96,-128],[-120.5,-21.5]],[[-96,-128],[-101.5,-20.5],[-120.5,-21.5]],[[-160,128],[-120.5,-21.5],[-129.5,63],[-144,112]],[[95,-384],[384,-384],[384,384],[92.5,-99.5]],[[74.5,107],[92.5,-99.5],[384,384]],[[41.5,212.5],[74.5,107],[384,384]],[[384,384],[-384,384],[-384,350.5],[-263,349.5]],[[384,384],[-263,349.5],[-130,336]],[[384,384],[-130,336],[-32,291.5]],[[41.5,212.5],[384,384],[-32,291.5]]]},{"polygons":[[[384,96],[128,96],[20.5,101.5],[21.5,120.5],[-63,129.5],[-112,144],[-128,160],[-139,230],[-159,229.5],[-160,384],[384,384]],[[384,-95],[99.5,-92.5],[-107,-74.5],[-212.5,-41.5],[-291.5,32],[-336,130],[-349.5,263],[-350.5,384],[-384,384],[-384,-384],[384,-384]]],"shapes":[[[-160,384],[-159,229.5],[-139,230],[384,384]],[[128,96],[384,96],[384,384],[-139,230],[-128,160],[21.5,120.5]],[[21.5,120.5],[20.5,101.5],[128,96]],[[-63,129.5],[21.5,120.5],[-128,160],[-112,144]],[[-350.5,384],[-384,384],[-384,-384],[-349.5,263]],[[-336,130],[-349.5,263],[-384,-384]],[[-291.5,32],[-336,130],[-384,-384]],[[-212.5,-41.5],[-291.5,32],[-384,-384]],[[-212.5,-41.5],[-384,-384],[384,-384],[-107,-74.5]],[[99.5,-92.5],[-107,-74.5],[384,-384]],[[384,-384],[384,-95],[99.5,-92.5]]]},{"polygons":[[[-96,384],[-96,128],[-101.5,20.5],[-120.5,21.5],[-129.5,-63],[-144,-112],[-160,-128],[-230,-139],[-229.5,-159],[-384,-160],[-384,384]],[[95,384],[92.5,99.5],[74.5,-107],[41.5,-212.5],[-32,-291.5],[-130,-336],[-263,-349.5],[-384,-350.5],[-384,-384],[384,-384],[384,384]]],"shapes":[[[-384,-160],[-229.5,-159],[-230,-139],[-384,384]],[[-96,128],[-96,384],[-384,384],[-230,-139],[-160,-128],[-120.5,21.5]],[[-120.5,21.5],[-101.5,20.5],[-96,128]],[[-129.5,-63],[-120.5,21.5],[-160,-128],[-144,-112]],[[-384,-350.5],[-384,-384],[384,-384],[-263,-349.5]],[[-130,-336],[-263,-349.5],[384,-384]],[[-32,-291.5],[-130,-336],[384,-384]],[[41.5,-212.5],[-32,-291.5],[384,-384]],[[41.5,-212.5],[384,-384],[384,384],[74.5,-107]],[[92.5,99.5],[74.5,-107],[384,384]],[[384,384],[95,384],[92.5,99.5]]]},{"polygons":[[[96,-384],[96,-128],[101.5,-20.5],[120.5,-21.5],[129.5,63],[144,112],[160,128],[230,139],[229.5,159],[384,160],[384,-384]],[[-95,-384],[-92.5,-99.5],[-74.5,107],[-41.5,212.5],[32,291.5],[130,336],[263,349.5],[384,350.5],[384,384],[-384,384],[-384,-384]]],"shapes":[[[384,160],[229.5,159],[230,139],[384,-384]],[[96,-128],[96,-384],[384,-384],[230,139],[160,128],[120.5,-21.5]],[[120.5,-21.5],[101.5,-20.5],[96,-128]],[[129.5,63],[120.5,-21.5],[160,128],[144,112]],[[384,350.5],[384,384],[-384,384],[263,349.5]],[[130,336],[263,349.5],[-384,384]],[[32,291.5],[130,336],[-384,384]],[[-41.5,212.5],[32,291.5],[-384,384]],[[-41.5,212.5],[-384,384],[-384,-384],[-74.5,107]],[[-92.5,-99.5],[-74.5,107],[-384,-384]],[[-384,-384],[-95,-384],[-92.5,-99.5]]]},{"polygons":[[[96,384],[96,128],[101.5,20.5],[120.5,21.5],[129.5,-63],[144,-112],[160,-128],[230,-139],[229.5,-159],[384,-160],[384,384]],[[-95,384],[-92.5,99.5],[-74.5,-107],[-41.5,-212.5],[32,-291.5],[130,-336],[263,-349.5],[384,-350.5],[384,-384],[-384,-384],[-384,384]]],"shapes":[[[230,-139],[229.5,-159],[384,-160],[384,384]],[[160,-128],[230,-139],[384,384],[96,384],[96,128],[120.5,21.5]],[[96,128],[101.5,20.5],[120.5,21.5]],[[160,-128],[120.5,21.5],[129.5,-63],[144,-112]],[[-95,384],[-384,384],[-384,-384],[-92.5,99.5]],[[-74.5,-107],[-92.5,99.5],[-384,-384]],[[-41.5,-212.5],[-74.5,-107],[-384,-384]],[[-384,-384],[384,-384],[384,-350.5],[263,-349.5]],[[-384,-384],[263,-349.5],[130,-336]],[[-384,-384],[130,-336],[32,-291.5]],[[-41.5,-212.5],[-384,-384],[32,-291.5]]]},{"polygons":[[[-384,-96],[384,-96],[384,-384],[-384,-384]],[[-384,96],[384,96],[384,384],[-384,384]]],"shapes":[[[-384,-384],[384,-384],[384,-96],[-384,-96]],[[-384,96],[384,96],[384,384],[-384,384]]]},{"polygons":[[[96,-384],[96,384],[384,384],[384,-384]],[[-96,-384],[-96,384],[-384,384],[-384,-384]]],"shapes":[[[384,-384],[384,384],[96,384],[96,-384]],[[-96,-384],[-96,384],[-384,384],[-384,-384]]]},{"polygons":[[[-384,-96],[-384,-384],[384,-384],[384,-352],[274,-348],[140.5,-318.5],[16,-256],[-104.5,-173.5],[-230,-111.5]],[[-384,96],[-215,81.5],[-79,33.5],[49.5,-47],[175,-121.5],[291,-152],[384,-160],[384,384],[-384,384]]],"shapes":[[[-230,-111.5],[-384,-96],[-384,-384],[16,-256],[-104.5,-173.5]],[[-384,-384],[384,-384],[384,-352],[274,-348]],[[-384,-384],[274,-348],[140.5,-318.5]],[[-384,-384],[140.5,-318.5],[16,-256]],[[49.5,-47],[175,-121.5],[291,-152],[384,-160],[384,384],[-384,384],[-79,33.5]],[[-215,81.5],[-79,33.5],[-384,384]],[[-384,384],[-384,96],[-215,81.5]]]},{"polygons":[[[384,-96],[384,-384],[-384,-384],[-384,-352],[-274,-348],[-140.5,-318.5],[-16,-256],[104.5,-173.5],[230,-111.5]],[[384,96],[215,81.5],[79,33.5],[-49.5,-47],[-175,-121.5],[-291,-152],[-384,-160],[-384,384],[384,384]]],"shapes":[[[-16,-256],[384,-384],[384,-96],[230,-111.5],[104.5,-173.5]],[[-384,-352],[-384,-384],[384,-384],[-274,-348]],[[-140.5,-318.5],[-274,-348],[384,-384]],[[-16,-256],[-140.5,-318.5],[384,-384]],[[384,384],[-384,384],[-384,-160],[-291,-152],[-175,-121.5],[-49.5,-47],[79,33.5]],[[384,384],[79,33.5],[215,81.5]],[[384,96],[384,384],[215,81.5]]]},{"polygons":[[[-384,96],[-384,384],[384,384],[384,352],[274,348],[140.5,318.5],[16,256],[-104.5,173.5],[-230,111.5]],[[-384,-96],[-215,-81.5],[-79,-33.5],[49.5,47],[175,121.5],[291,152],[384,160],[384,-384],[-384,-384]]],"shapes":[[[16,256],[-384,384],[-384,96],[-230,111.5],[-104.5,173.5]],[[384,352],[384,384],[-384,384],[274,348]],[[140.5,318.5],[274,348],[-384,384]],[[16,256],[140.5,318.5],[-384,384]],[[-384,-384],[384,-384],[384,160],[291,152],[175,121.5],[49.5,47],[-79,-33.5]],[[-384,-384],[-79,-33.5],[-215,-81.5]],[[-384,-96],[-384,-384],[-215,-81.5]]]},{"polygons":[[[-96,-384],[-384,-384],[-384,384],[-352,384],[-348,274],[-318.5,140.5],[-256,16],[-173.5,-104.5],[-111.5,-230]],[[96,-384],[81.5,-215],[33.5,-79],[-47,49.5],[-121.5,175],[-152,291],[-160,384],[384,384],[384,-384]]],"shapes":[[[-256,16],[-384,-384],[-96,-384],[-111.5,-230],[-173.5,-104.5]],[[-352,384],[-384,384],[-384,-384],[-348,274]],[[-318.5,140.5],[-348,274],[-384,-384]],[[-256,16],[-318.5,140.5],[-384,-384]],[[384,-384],[384,384],[-160,384],[-152,291],[-121.5,175],[-47,49.5],[33.5,-79]],[[384,-384],[33.5,-79],[81.5,-215]],[[96,-384],[384,-384],[81.5,-215]]]},{"polygons":[[[384,96],[384,384],[-384,384],[-384,352],[-274,348],[-140.5,318.5],[-16,256],[104.5,173.5],[230,111.5]],[[384,-96],[215,-81.5],[79,-33.5],[-49.5,47],[-175,121.5],[-291,152],[-384,160],[-384,-384],[384,-384]]],"shapes":[[[230,111.5],[384,96],[384,384],[-16,256],[104.5,173.5]],[[384,384],[-384,384],[-384,352],[-274,348]],[[384,384],[-274,348],[-140.5,318.5]],[[384,384],[-140.5,318.5],[-16,256]],[[-49.5,47],[-175,121.5],[-291,152],[-384,160],[-384,-384],[384,-384],[79,-33.5]],[[215,-81.5],[79,-33.5],[384,-384]],[[384,-384],[384,-96],[215,-81.5]]]},{"polygons":[[[-96,384],[-384,384],[-384,-384],[-352,-384],[-348,-274],[-318.5,-140.5],[-256,-16],[-173.5,104.5],[-111.5,230]],[[96,384],[81.5,215],[33.5,79],[-47,-49.5],[-121.5,-175],[-152,-291],[-160,-384],[384,-384],[384,384]]],"shapes":[[[-111.5,230],[-96,384],[-384,384],[-256,-16],[-173.5,104.5]],[[-384,384],[-384,-384],[-352,-384],[-348,-274]],[[-384,384],[-348,-274],[-318.5,-140.5]],[[-384,384],[-318.5,-140.5],[-256,-16]],[[-47,-49.5],[-121.5,-175],[-152,-291],[-160,-384],[384,-384],[384,384],[33.5,79]],[[81.5,215],[33.5,79],[384,384]],[[384,384],[96,384],[81.5,215]]]},{"polygons":[[[96,-384],[384,-384],[384,384],[352,384],[348,274],[318.5,140.5],[256,16],[173.5,-104.5],[111.5,-230]],[[-96,-384],[-81.5,-215],[-33.5,-79],[47,49.5],[121.5,175],[152,291],[160,384],[-384,384],[-384,-384]]],"shapes":[[[111.5,-230],[96,-384],[384,-384],[256,16],[173.5,-104.5]],[[384,-384],[384,384],[352,384],[348,274]],[[384,-384],[348,274],[318.5,140.5]],[[384,-384],[318.5,140.5],[256,16]],[[47,49.5],[121.5,175],[152,291],[160,384],[-384,384],[-384,-384],[-33.5,-79]],[[-81.5,-215],[-33.5,-79],[-384,-384]],[[-384,-384],[-96,-384],[-81.5,-215]]]},{"polygons":[[[96,384],[384,384],[384,-384],[352,-384],[348,-274],[318.5,-140.5],[256,-16],[173.5,104.5],[111.5,230]],[[-96,384],[-81.5,215],[-33.5,79],[47,-49.5],[121.5,-175],[152,-291],[160,-384],[-384,-384],[-384,384]]],"shapes":[[[256,-16],[384,384],[96,384],[111.5,230],[173.5,104.5]],[[352,-384],[384,-384],[384,384],[348,-274]],[[318.5,-140.5],[348,-274],[384,384]],[[256,-16],[318.5,-140.5],[384,384]],[[-384,384],[-384,-384],[160,-384],[152,-291],[121.5,-175],[47,-49.5],[-33.5,79]],[[-384,384],[-33.5,79],[-81.5,215]],[[-96,384],[-384,384],[-81.5,215]]]},{"polygons":[[[-384,-352],[-320,-352],[-248.5,-327.5],[-194,-265],[-170,-159],[-161.5,65.5],[-160,384],[384,384],[384,-384],[-384,-384]],[[-384,-140],[-376.5,-134.5],[-375.5,-118.5],[-357,-118],[-352.5,77.5],[-352,384],[-384,384]]],"shapes":[[[-384,-352],[-384,-384],[384,-384],[-320,-352]],[[-248.5,-327.5],[-320,-352],[384,-384]],[[-194,-265],[-248.5,-327.5],[384,-384],[384,384]],[[-170,-159],[-194,-265],[384,384]],[[-161.5,65.5],[-170,-159],[384,384]],[[-161.5,65.5],[384,384],[-160,384]],[[-384,384],[-384,-140],[-376.5,-134.5],[-375.5,-118.5],[-352,384]],[[-352.5,77.5],[-352,384],[-375.5,-118.5],[-357,-118]]]},{"polygons":[[[384,-352],[320,-352],[248.5,-327.5],[194,-265],[170,-159],[161.5,65.5],[160,384],[-384,384],[-384,-384],[384,-384]],[[384,-140],[376.5,-134.5],[375.5,-118.5],[357,-118],[352.5,77.5],[352,384],[384,384]]],"shapes":[[[384,-384],[384,-352],[320,-352],[-384,-384]],[[-384,-384],[320,-352],[248.5,-327.5]],[[-384,-384],[248.5,-327.5],[194,-265],[-384,384]],[[-384,384],[194,-265],[170,-159]],[[-384,384],[170,-159],[161.5,65.5]],[[-384,384],[161.5,65.5],[160,384]],[[375.5,-118.5],[376.5,-134.5],[384,-140],[384,384],[352,384]],[[375.5,-118.5],[352,384],[352.5,77.5],[357,-118]]]},{"polygons":[[[-384,352],[-320,352],[-248.5,327.5],[-194,265],[-170,159],[-161.5,-65.5],[-160,-384],[384,-384],[384,384],[-384,384]],[[-384,140],[-376.5,134.5],[-375.5,118.5],[-357,118],[-352.5,-77.5],[-352,-384],[-384,-384]]],"shapes":[[[-384,384],[-384,352],[-320,352],[384,384]],[[384,384],[-320,352],[-248.5,327.5]],[[384,384],[-248.5,327.5],[-194,265],[384,-384]],[[384,-384],[-194,265],[-170,159]],[[384,-384],[-170,159],[-161.5,-65.5]],[[384,-384],[-161.5,-65.5],[-160,-384]],[[-375.5,118.5],[-376.5,134.5],[-384,140],[-384,-384],[-352,-384]],[[-375.5,118.5],[-352,-384],[-352.5,-77.5],[-357,118]]]},{"polygons":[[[-352,-384],[-352,-320],[-327.5,-248.5],[-265,-194],[-159,-170],[65.5,-161.5],[384,-160],[384,384],[-384,384],[-384,-384]],[[-140,-384],[-134.5,-376.5],[-118.5,-375.5],[-118,-357],[77.5,-352.5],[384,-352],[384,-384]]],"shapes":[[[-384,-384],[-352,-384],[-352,-320],[-384,384]],[[-384,384],[-352,-320],[-327.5,-248.5]],[[-384,384],[-327.5,-248.5],[-265,-194],[384,384]],[[384,384],[-265,-194],[-159,-170]],[[384,384],[-159,-170],[65.5,-161.5]],[[384,384],[65.5,-161.5],[384,-160]],[[-118.5,-375.5],[-134.5,-376.5],[-140,-384],[384,-384],[384,-352]],[[-118.5,-375.5],[384,-352],[77.5,-352.5],[-118,-357]]]},{"polygons":[[[384,352],[320,352],[248.5,327.5],[194,265],[170,159],[161.5,-65.5],[160,-384],[-384,-384],[-384,384],[384,384]],[[384,140],[376.5,134.5],[375.5,118.5],[357,118],[352.5,-77.5],[352,-384],[384,-384]]],"shapes":[[[384,352],[384,384],[-384,384],[320,352]],[[248.5,327.5],[320,352],[-384,384]],[[194,265],[248.5,327.5],[-384,384],[-384,-384]],[[170,159],[194,265],[-384,-384]],[[161.5,-65.5],[170,159],[-384,-384]],[[161.5,-65.5],[-384,-384],[160,-384]],[[384,-384],[384,140],[376.5,134.5],[375.5,118.5],[352,-384]],[[352.5,-77.5],[352,-384],[375.5,118.5],[357,118]]]},{"polygons":[[[-352,384],[-352,320],[-327.5,248.5],[-265,194],[-159,170],[65.5,161.5],[384,160],[384,-384],[-384,-384],[-384,384]],[[-140,384],[-134.5,376.5],[-118.5,375.5],[-118,357],[77.5,352.5],[384,352],[384,384]]],"shapes":[[[-352,384],[-384,384],[-384,-384],[-352,320]],[[-327.5,248.5],[-352,320],[-384,-384]],[[-265,194],[-327.5,248.5],[-384,-384],[384,-384]],[[-159,170],[-265,194],[384,-384]],[[65.5,161.5],[-159,170],[384,-384]],[[65.5,161.5],[384,-384],[384,160]],[[384,384],[-140,384],[-134.5,376.5],[-118.5,375.5],[384,352]],[[77.5,352.5],[384,352],[-118.5,375.5],[-118,357]]]},{"polygons":[[[352,-384],[352,-320],[327.5,-248.5],[265,-194],[159,-170],[-65.5,-161.5],[-384,-160],[-384,384],[384,384],[384,-384]],[[140,-384],[134.5,-376.5],[118.5,-375.5],[118,-357],[-77.5,-352.5],[-384,-352],[-384,-384]]],"shapes":[[[352,-384],[384,-384],[384,384],[352,-320]],[[327.5,-248.5],[352,-320],[384,384]],[[265,-194],[327.5,-248.5],[384,384],[-384,384]],[[159,-170],[265,-194],[-384,384]],[[-65.5,-161.5],[159,-170],[-384,384]],[[-65.5,-161.5],[-384,384],[-384,-160]],[[-384,-384],[140,-384],[134.5,-376.5],[118.5,-375.5],[-384,-352]],[[-77.5,-352.5],[-384,-352],[118.5,-375.5],[118,-357]]]},{"polygons":[[[352,384],[352,320],[327.5,248.5],[265,194],[159,170],[-65.5,161.5],[-384,160],[-384,-384],[384,-384],[384,384]],[[140,384],[134.5,376.5],[118.5,375.5],[118,357],[-77.5,352.5],[-384,352],[-384,384]]],"shapes":[[[384,384],[352,384],[352,320],[384,-384]],[[384,-384],[352,320],[327.5,248.5]],[[384,-384],[327.5,248.5],[265,194],[-384,-384]],[[-384,-384],[265,194],[159,170]],[[-384,-384],[159,170],[-65.5,161.5]],[[-384,-384],[-65.5,161.5],[-384,160]],[[118.5,375.5],[134.5,376.5],[140,384],[-384,384],[-384,352]],[[118.5,375.5],[-384,352],[-77.5,352.5],[118,357]]]},{"polygons":[[[-384,-384],[384,-384],[384,384],[352,384],[351,273],[313,80],[218.5,-109],[76,-242.5],[-96,-320],[-288,-352],[-384,-352]],[[-384,-138],[-384,384],[139,384],[140.5,318],[113,165.5],[50,31],[-61,-67.5],[-224,-128]]],"shapes":[[[-384,-352],[-384,-384],[384,-384],[-288,-352]],[[-96,-320],[-288,-352],[384,-384]],[[76,-242.5],[-96,-320],[384,-384]],[[218.5,-109],[76,-242.5],[384,-384]],[[384,-384],[384,384],[352,384],[351,273]],[[384,-384],[351,273],[313,80]],[[218.5,-109],[384,-384],[313,80]],[[-224,-128],[-61,-67.5],[50,31],[113,165.5],[140.5,318],[139,384],[-384,384],[-384,-138]]]},{"polygons":[[[384,-384],[384,384],[-384,384],[-384,352],[-273,351],[-80,313],[109,218.5],[242.5,76],[320,-96],[352,-288],[352,-384]],[[138,-384],[-384,-384],[-384,139],[-318,140.5],[-165.5,113],[-31,50],[67.5,-61],[128,-224]]],"shapes":[[[352,-384],[384,-384],[384,384],[352,-288]],[[320,-96],[352,-288],[384,384]],[[242.5,76],[320,-96],[384,384]],[[109,218.5],[242.5,76],[384,384]],[[384,384],[-384,384],[-384,352],[-273,351]],[[384,384],[-273,351],[-80,313]],[[109,218.5],[384,384],[-80,313]],[[128,-224],[67.5,-61],[-31,50],[-165.5,113],[-318,140.5],[-384,139],[-384,-384],[138,-384]]]},{"polygons":[[[384,384],[-384,384],[-384,-384],[-352,-384],[-351,-273],[-313,-80],[-218.5,109],[-76,242.5],[96,320],[288,352],[384,352]],[[384,138],[384,-384],[-139,-384],[-140.5,-318],[-113,-165.5],[-50,-31],[61,67.5],[224,128]]],"shapes":[[[384,352],[384,384],[-384,384],[288,352]],[[96,320],[288,352],[-384,384]],[[-76,242.5],[96,320],[-384,384]],[[-218.5,109],[-76,242.5],[-384,384]],[[-384,384],[-384,-384],[-352,-384],[-351,-273]],[[-384,384],[-351,-273],[-313,-80]],[[-218.5,109],[-384,384],[-313,-80]],[[224,128],[61,67.5],[-50,-31],[-113,-165.5],[-140.5,-318],[-139,-384],[384,-384],[384,138]]]},{"polygons":[[[-384,384],[-384,-384],[384,-384],[384,-352],[273,-351],[80,-313],[-109,-218.5],[-242.5,-76],[-320,96],[-352,288],[-352,384]],[[-138,384],[384,384],[384,-139],[318,-140.5],[165.5,-113],[31,-50],[-67.5,61],[-128,224]]],"shapes":[[[-352,384],[-384,384],[-384,-384],[-352,288]],[[-320,96],[-352,288],[-384,-384]],[[-242.5,-76],[-320,96],[-384,-384]],[[-109,-218.5],[-242.5,-76],[-384,-384]],[[-384,-384],[384,-384],[384,-352],[273,-351]],[[-384,-384],[273,-351],[80,-313]],[[-109,-218.5],[-384,-384],[80,-313]],[[-128,224],[-67.5,61],[31,-50],[165.5,-113],[318,-140.5],[384,-139],[384,384],[-138,384]]]},{"polygons":[[[-384,-352],[-352,-352],[-193.5,-315],[-59,-224],[74.5,-64.5],[93.5,-70.5],[163,19.5],[204.5,65.5],[288,123.5],[349,138],[384,140.5],[384,-384],[-384,-384]],[[-384,-140.5],[-307,-132.5],[-243,-102],[-163.5,-26],[-92.5,71],[-74.5,60],[19.5,178],[137.5,284],[260,338.5],[384,352],[384,384],[-384,384]]],"shapes":[[[384,-384],[384,140.5],[349,138],[288,123.5],[204.5,65.5],[163,19.5],[93.5,-70.5]],[[-384,-352],[-384,-384],[384,-384],[-352,-352]],[[-193.5,-315],[-352,-352],[384,-384]],[[-59,-224],[-193.5,-315],[384,-384]],[[-59,-224],[384,-384],[93.5,-70.5],[74.5,-64.5]],[[-384,384],[-384,-140.5],[-307,-132.5],[-243,-102],[-163.5,-26],[-92.5,71]],[[384,352],[384,384],[-384,384],[260,338.5]],[[137.5,284],[260,338.5],[-384,384]],[[19.5,178],[137.5,284],[-384,384]],[[19.5,178],[-384,384],[-92.5,71],[-74.5,60]]]},{"polygons":[[[384,-352],[352,-352],[193.5,-315],[59,-224],[-74.5,-64.5],[-93.5,-70.5],[-163,19.5],[-204.5,65.5],[-288,123.5],[-349,138],[-384,140.5],[-384,-384],[384,-384]],[[384,-140.5],[307,-132.5],[243,-102],[163.5,-26],[92.5,71],[74.5,60],[-19.5,178],[-137.5,284],[-260,338.5],[-384,352],[-384,384],[384,384]]],"shapes":[[[-163,19.5],[-204.5,65.5],[-288,123.5],[-349,138],[-384,140.5],[-384,-384],[-93.5,-70.5]],[[384,-384],[384,-352],[352,-352],[-384,-384]],[[-384,-384],[352,-352],[193.5,-315]],[[-384,-384],[193.5,-315],[59,-224]],[[-93.5,-70.5],[-384,-384],[59,-224],[-74.5,-64.5]],[[163.5,-26],[243,-102],[307,-132.5],[384,-140.5],[384,384],[92.5,71]],[[384,384],[-384,384],[-384,352],[-260,338.5]],[[384,384],[-260,338.5],[-137.5,284]],[[384,384],[-137.5,284],[-19.5,178]],[[92.5,71],[384,384],[-19.5,178],[74.5,60]]]},{"polygons":[[[352,-384],[352,-352],[315,-193.5],[224,-59],[64.5,74.5],[70.5,93.5],[-19.5,163],[-65.5,204.5],[-123.5,288],[-138,349],[-140.5,384],[384,384],[384,-384]],[[140.5,-384],[132.5,-307],[102,-243],[26,-163.5],[-71,-92.5],[-60,-74.5],[-178,19.5],[-284,137.5],[-338.5,260],[-352,384],[-384,384],[-384,-384]]],"shapes":[[[384,384],[-140.5,384],[-138,349],[-123.5,288],[-65.5,204.5],[-19.5,163],[70.5,93.5]],[[352,-384],[384,-384],[384,384],[352,-352]],[[315,-193.5],[352,-352],[384,384]],[[224,-59],[315,-193.5],[384,384]],[[224,-59],[384,384],[70.5,93.5],[64.5,74.5]],[[-384,-384],[140.5,-384],[132.5,-307],[102,-243],[26,-163.5],[-71,-92.5]],[[-352,384],[-384,384],[-384,-384],[-338.5,260]],[[-284,137.5],[-338.5,260],[-384,-384]],[[-178,19.5],[-284,137.5],[-384,-384]],[[-178,19.5],[-384,-384],[-71,-92.5],[-60,-74.5]]]},{"polygons":[[[-352,-384],[-352,-352],[-315,-193.5],[-224,-59],[-64.5,74.5],[-70.5,93.5],[19.5,163],[65.5,204.5],[123.5,288],[138,349],[140.5,384],[-384,384],[-384,-384]],[[-140.5,-384],[-132.5,-307],[-102,-243],[-26,-163.5],[71,-92.5],[60,-74.5],[178,19.5],[284,137.5],[338.5,260],[352,384],[384,384],[384,-384]]],"shapes":[[[19.5,163],[65.5,204.5],[123.5,288],[138,349],[140.5,384],[-384,384],[-70.5,93.5]],[[-384,-384],[-352,-384],[-352,-352],[-384,384]],[[-384,384],[-352,-352],[-315,-193.5]],[[-384,384],[-315,-193.5],[-224,-59]],[[-70.5,93.5],[-384,384],[-224,-59],[-64.5,74.5]],[[-26,-163.5],[-102,-243],[-132.5,-307],[-140.5,-384],[384,-384],[71,-92.5]],[[384,-384],[384,384],[352,384],[338.5,260]],[[384,-384],[338.5,260],[284,137.5]],[[384,-384],[284,137.5],[178,19.5]],[[71,-92.5],[384,-384],[178,19.5],[60,-74.5]]]},{"polygons":[[[-384,-352],[-384,-384],[384,-384],[384,-352]],[[-384,-160],[-384,384],[384,384],[384,-160]]],"shapes":[[[-384,-352],[-384,-384],[384,-384],[384,-352]],[[384,-160],[384,384],[-384,384],[-384,-160]]]},{"polygons":[[[384,352],[384,384],[-384,384],[-384,352]],[[384,160],[384,-384],[-384,-384],[-384,160]]],"shapes":[[[384,352],[384,384],[-384,384],[-384,352]],[[-384,160],[-384,-384],[384,-384],[384,160]]]},{"polygons":[[[-352,384],[-384,384],[-384,-384],[-352,-384]],[[-160,384],[384,384],[384,-384],[-160,-384]]],"shapes":[[[-352,384],[-384,384],[-384,-384],[-352,-384]],[[-160,-384],[384,-384],[384,384],[-160,384]]]},{"polygons":[[[352,-384],[384,-384],[384,384],[352,384]],[[160,-384],[-384,-384],[-384,384],[160,384]]],"shapes":[[[352,-384],[384,-384],[384,384],[352,384]],[[160,384],[-384,384],[-384,-384],[160,-384]]]},{"polygons":[[[360,136],[346.95098876953125,124.73200225830078],[240,272],[132,384],[384,384],[384,112]],[[-132,384],[48,192],[252,-48],[300,-84],[348,-96],[384,-96],[384,-384],[-384,-384],[-384,384]]],"shapes":[[[360,136],[384,112],[384,384],[240,272]],[[384,384],[132,384],[240,272]],[[360,136],[240,272],[346.95098876953125,124.73200225830078]],[[48,192],[-132,384],[-384,384],[-384,-384],[384,-384],[252,-48]],[[300,-84],[252,-48],[384,-384]],[[348,-96],[300,-84],[384,-384]],[[348,-96],[384,-384],[384,-96]]]},{"polygons":[[[-136,360],[-124.73200225830078,346.95098876953125],[-272,240],[-384,132],[-384,384],[-112,384]],[[-384,-132],[-192,48],[48,252],[84,300],[96,348],[96,384],[384,384],[384,-384],[-384,-384]]],"shapes":[[[-136,360],[-112,384],[-384,384],[-272,240]],[[-384,384],[-384,132],[-272,240]],[[-136,360],[-272,240],[-124.73200225830078,346.95098876953125]],[[-192,48],[-384,-132],[-384,-384],[384,-384],[384,384],[48,252]],[[84,300],[48,252],[384,384]],[[96,348],[84,300],[384,384]],[[96,348],[384,384],[96,384]]]},{"polygons":[[[-360,-136],[-346.95098876953125,-124.73200225830078],[-240,-272],[-132,-384],[-384,-384],[-384,-112]],[[132,-384],[-48,-192],[-252,48],[-300,84],[-348,96],[-384,96],[-384,384],[384,384],[384,-384]]],"shapes":[[[-360,-136],[-384,-112],[-384,-384],[-240,-272]],[[-384,-384],[-132,-384],[-240,-272]],[[-360,-136],[-240,-272],[-346.95098876953125,-124.73200225830078]],[[-48,-192],[132,-384],[384,-384],[384,384],[-384,384],[-252,48]],[[-300,84],[-252,48],[-384,384]],[[-348,96],[-300,84],[-384,384]],[[-348,96],[-384,384],[-384,96]]]},{"polygons":[[[136,-360],[124.73200225830078,-346.95098876953125],[272,-240],[384,-132],[384,-384],[112,-384]],[[384,132],[192,-48],[-48,-252],[-84,-300],[-96,-348],[-96,-384],[-384,-384],[-384,384],[384,384]]],"shapes":[[[136,-360],[112,-384],[384,-384],[272,-240]],[[384,-384],[384,-132],[272,-240]],[[136,-360],[272,-240],[124.73200225830078,-346.95098876953125]],[[192,-48],[384,132],[384,384],[-384,384],[-384,-384],[-48,-252]],[[-84,-300],[-48,-252],[-384,-384]],[[-96,-348],[-84,-300],[-384,-384]],[[-96,-348],[-384,-384],[-96,-384]]]},{"polygons":[[[360,-136],[346.95098876953125,-124.73200225830078],[240,-272],[132,-384],[384,-384],[384,-112]],[[-132,-384],[48,-192],[252,48],[300,84],[348,96],[384,96],[384,384],[-384,384],[-384,-384]]],"shapes":[[[384,-384],[384,-112],[360,-136],[240,-272]],[[240,-272],[132,-384],[384,-384]],[[240,-272],[360,-136],[346.95098876953125,-124.73200225830078]],[[348,96],[384,96],[384,384],[-384,384]],[[300,84],[348,96],[-384,384]],[[252,48],[300,84],[-384,384],[-384,-384]],[[48,-192],[252,48],[-384,-384],[-132,-384]]]},{"polygons":[[[-136,-360],[-124.73200225830078,-346.95098876953125],[-272,-240],[-384,-132],[-384,-384],[-112,-384]],[[-384,132],[-192,-48],[48,-252],[84,-300],[96,-348],[96,-384],[384,-384],[384,384],[-384,384]]],"shapes":[[[-384,-384],[-112,-384],[-136,-360],[-272,-240]],[[-272,-240],[-384,-132],[-384,-384]],[[-272,-240],[-136,-360],[-124.73200225830078,-346.95098876953125]],[[96,-348],[96,-384],[384,-384],[384,384]],[[84,-300],[96,-348],[384,384]],[[48,-252],[84,-300],[384,384],[-384,384]],[[-192,-48],[48,-252],[-384,384],[-384,132]]]},{"polygons":[[[-360,136],[-346.95098876953125,124.73200225830078],[-240,272],[-132,384],[-384,384],[-384,112]],[[132,384],[-48,192],[-252,-48],[-300,-84],[-348,-96],[-384,-96],[-384,-384],[384,-384],[384,384]]],"shapes":[[[-384,384],[-384,112],[-360,136],[-240,272]],[[-240,272],[-132,384],[-384,384]],[[-240,272],[-360,136],[-346.95098876953125,124.73200225830078]],[[-348,-96],[-384,-96],[-384,-384],[384,-384]],[[-300,-84],[-348,-96],[384,-384]],[[-252,-48],[-300,-84],[384,-384],[384,384]],[[-48,192],[-252,-48],[384,384],[132,384]]]},{"polygons":[[[136,360],[124.73200225830078,346.95098876953125],[272,240],[384,132],[384,384],[112,384]],[[384,-132],[192,48],[-48,252],[-84,300],[-96,348],[-96,384],[-384,384],[-384,-384],[384,-384]]],"shapes":[[[384,384],[112,384],[136,360],[272,240]],[[272,240],[384,132],[384,384]],[[272,240],[136,360],[124.73200225830078,346.95098876953125]],[[-96,348],[-96,384],[-384,384],[-384,-384]],[[-84,300],[-96,348],[-384,-384]],[[-48,252],[-84,300],[-384,-384],[384,-384]],[[192,48],[-48,252],[384,-384],[384,-132]]]},{"polygons":[[[-384,-132],[-132,-384],[-384,-384]],[[132,-384],[-384,132],[-384,384],[384,384],[384,-384]]],"shapes":[[[-384,-384],[-132,-384],[-384,-132]],[[384,-384],[384,384],[-384,384],[-384,132],[132,-384]]]},{"polygons":[[[132,-384],[384,-132],[384,-384]],[[384,132],[-132,-384],[-384,-384],[-384,384],[384,384]]],"shapes":[[[384,-384],[384,-132],[132,-384]],[[384,384],[-384,384],[-384,-384],[-132,-384],[384,132]]]},{"polygons":[[[-132,384],[-384,132],[-384,384]],[[-384,-132],[132,384],[384,384],[384,-384],[-384,-384]]],"shapes":[[[-384,384],[-384,132],[-132,384]],[[-384,-384],[384,-384],[384,384],[132,384],[-384,-132]]]},{"polygons":[[[384,132],[132,384],[384,384]],[[-132,384],[384,-132],[384,-384],[-384,-384],[-384,384]]],"shapes":[[[384,384],[132,384],[384,132]],[[-384,384],[-384,-384],[384,-384],[384,-132],[-132,384]]]},{"polygons":[[[-384,96],[384,96],[384,384],[-384,384]],[[-384,-96],[384,-96],[384,-384],[-384,-384]],[[16,8],[16,-8],[8,-16],[-8,-16],[-16,-8],[-16,8],[-8,16],[8,16]]],"shapes":[[[-384,96],[384,96],[384,384],[-384,384]],[[-384,-384],[384,-384],[384,-96],[-384,-96]],[[8,16],[-8,16],[-16,8],[-16,-8],[-8,-16],[8,-16],[16,-8],[16,8]]]},{"polygons":[[[96,384],[96,-384],[384,-384],[384,384]],[[-96,384],[-96,-384],[-384,-384],[-384,384]],[[8,-16],[-8,-16],[-16,-8],[-16,8],[-8,16],[8,16],[16,8],[16,-8]]],"shapes":[[[96,384],[96,-384],[384,-384],[384,384]],[[-384,384],[-384,-384],[-96,-384],[-96,384]],[[16,-8],[16,8],[8,16],[-8,16],[-16,8],[-16,-8],[-8,-16],[8,-16]]]},{"polygons":[[[384,-352],[-384,-352],[-384,-384],[384,-384]],[[384,-160],[-384,-160],[-384,160],[384,160]],[[384,352],[384,384],[-384,384],[-384,352]]],"shapes":[[[384,-352],[-384,-352],[-384,-384],[384,-384]],[[384,160],[-384,160],[-384,-160],[384,-160]],[[384,352],[384,384],[-384,384],[-384,352]]]},{"polygons":[[[352,384],[352,-384],[384,-384],[384,384]],[[160,384],[160,-384],[-160,-384],[-160,384]],[[-352,384],[-384,384],[-384,-384],[-352,-384]]],"shapes":[[[352,384],[352,-384],[384,-384],[384,384]],[[-160,384],[-160,-384],[160,-384],[160,384]],[[-352,384],[-384,384],[-384,-384],[-352,-384]]]},{"polygons":[[[-384,-96],[-336,-96],[-216,-120],[-104,-176],[48,-272],[160,-320],[272,-344],[384,-352],[384,-384],[-384,-384]],[[-384,96],[-336,96],[-216,120],[-112,168],[-8,240],[112,304],[256,344],[384,352],[384,384],[-384,384]],[[384,-160],[344,-160],[240,-144],[108,-84],[108,-60],[16,0],[108,60],[108,84],[240,144],[324,156],[384,160]]],"shapes":[[[-384,-384],[384,-384],[384,-352],[272,-344]],[[-384,-384],[272,-344],[160,-320]],[[-384,-384],[160,-320],[48,-272]],[[-384,-96],[-384,-384],[48,-272],[-104,-176],[-216,-120],[-336,-96]],[[-8,240],[-384,384],[-384,96],[-336,96],[-216,120],[-112,168]],[[384,352],[384,384],[-384,384],[256,344]],[[112,304],[256,344],[-384,384]],[[-8,240],[112,304],[-384,384]],[[108,60],[16,0],[108,-60],[240,-144]],[[240,-144],[344,-160],[384,-160],[384,160],[324,156],[240,144],[108,84],[108,60]],[[240,-144],[108,-60],[108,-84]]]},{"polygons":[[[96,-384],[96,-336],[120,-216],[176,-104],[272,48],[320,160],[344,272],[352,384],[384,384],[384,-384]],[[-96,-384],[-96,-336],[-120,-216],[-168,-112],[-240,-8],[-304,112],[-344,256],[-352,384],[-384,384],[-384,-384]],[[160,384],[160,344],[144,240],[84,108],[60,108],[0,16],[-60,108],[-84,108],[-144,240],[-156,324],[-160,384]]],"shapes":[[[384,-384],[384,384],[352,384],[344,272]],[[384,-384],[344,272],[320,160]],[[384,-384],[320,160],[272,48]],[[96,-384],[384,-384],[272,48],[176,-104],[120,-216],[96,-336]],[[-240,-8],[-384,-384],[-96,-384],[-96,-336],[-120,-216],[-168,-112]],[[-352,384],[-384,384],[-384,-384],[-344,256]],[[-304,112],[-344,256],[-384,-384]],[[-240,-8],[-304,112],[-384,-384]],[[-60,108],[0,16],[60,108],[144,240]],[[144,240],[160,344],[160,384],[-160,384],[-156,324],[-144,240],[-84,108],[-60,108]],[[144,240],[60,108],[84,108]]]},{"polygons":[[[384,96],[336,96],[216,120],[104,176],[-48,272],[-160,320],[-272,344],[-384,352],[-384,384],[384,384]],[[384,-96],[336,-96],[216,-120],[112,-168],[8,-240],[-112,-304],[-256,-344],[-384,-352],[-384,-384],[384,-384]],[[-384,160],[-344,160],[-240,144],[-108,84],[-108,60],[-16,0],[-108,-60],[-108,-84],[-240,-144],[-324,-156],[-384,-160]]],"shapes":[[[384,384],[-384,384],[-384,352],[-272,344]],[[384,384],[-272,344],[-160,320]],[[384,384],[-160,320],[-48,272]],[[384,96],[384,384],[-48,272],[104,176],[216,120],[336,96]],[[8,-240],[384,-384],[384,-96],[336,-96],[216,-120],[112,-168]],[[-384,-352],[-384,-384],[384,-384],[-256,-344]],[[-112,-304],[-256,-344],[384,-384]],[[8,-240],[-112,-304],[384,-384]],[[-108,-60],[-16,0],[-108,60],[-240,144]],[[-240,144],[-344,160],[-384,160],[-384,-160],[-324,-156],[-240,-144],[-108,-84],[-108,-60]],[[-240,144],[-108,60],[-108,84]]]},{"polygons":[[[-96,384],[-96,336],[-120,216],[-176,104],[-272,-48],[-320,-160],[-344,-272],[-352,-384],[-384,-384],[-384,384]],[[96,384],[96,336],[120,216],[168,112],[240,8],[304,-112],[344,-256],[352,-384],[384,-384],[384,384]],[[-160,-384],[-160,-344],[-144,-240],[-84,-108],[-60,-108],[0,-16],[60,-108],[84,-108],[144,-240],[156,-324],[160,-384]]],"shapes":[[[-384,384],[-384,-384],[-352,-384],[-344,-272]],[[-384,384],[-344,-272],[-320,-160]],[[-384,384],[-320,-160],[-272,-48]],[[-96,384],[-384,384],[-272,-48],[-176,104],[-120,216],[-96,336]],[[240,8],[384,384],[96,384],[96,336],[120,216],[168,112]],[[352,-384],[384,-384],[384,384],[344,-256]],[[304,-112],[344,-256],[384,384]],[[240,8],[304,-112],[384,384]],[[60,-108],[0,-16],[-60,-108],[-144,-240]],[[-144,-240],[-160,-344],[-160,-384],[160,-384],[156,-324],[144,-240],[84,-108],[60,-108]],[[-144,-240],[-60,-108],[-84,-108]]]},{"polygons":[[[384,-96],[216,-96],[108,-48],[-108,-48],[-216,-96],[-384,-96],[-384,-384],[384,-384]],[[384,96],[384,384],[-384,384],[-384,96],[-214.70700073242188,96],[-108,48],[108,48],[216,96]]],"shapes":[[[384,-384],[384,-96],[216,-96],[-384,-384]],[[-384,-384],[216,-96],[108,-48],[-108,-48],[-216,-96]],[[-384,-384],[-216,-96],[-384,-96]],[[216,96],[384,96],[384,384],[-384,384]],[[-108,48],[108,48],[216,96],[-384,384],[-214.70700073242188,96]],[[-214.70700073242188,96],[-384,384],[-384,96]]]},{"polygons":[[[-96,-384],[-96,-216],[-48,-108],[-48,108],[-96,216],[-96,384],[-384,384],[-384,-384]],[[96,-384],[384,-384],[384,384],[96,384],[96,214.70700073242188],[48,108],[48,-108],[96,-216]]],"shapes":[[[-384,-384],[-96,-384],[-96,-216],[-384,384]],[[-384,384],[-96,-216],[-48,-108],[-48,108],[-96,216]],[[-384,384],[-96,216],[-96,384]],[[96,-216],[96,-384],[384,-384],[384,384]],[[48,108],[48,-108],[96,-216],[384,384],[96,214.70700073242188]],[[96,214.70700073242188],[384,384],[96,384]]]},{"polygons":[[[-384,96],[-72,96],[-12,48],[12,48],[72,96],[216,96],[384,96],[384,384],[-384,384]],[[-384,-96],[-384,-384],[384,-384],[384,-96],[72,-96],[12,-48],[-12,-48],[-72,-96]]],"shapes":[[[-384,384],[-384,96],[-72,96],[384,384]],[[384,384],[-72,96],[-12,48],[12,48],[72,96]],[[384,384],[72,96],[216,96]],[[384,384],[216,96],[384,96]],[[-72,-96],[-384,-96],[-384,-384],[384,-384]],[[12,-48],[-12,-48],[-72,-96],[384,-384],[72,-96]],[[72,-96],[384,-384],[384,-96]]]},{"polygons":[[[96,384],[96,72],[48,12],[48,-12],[96,-72],[96,-216],[96,-384],[384,-384],[384,384]],[[-96,384],[-384,384],[-384,-384],[-96,-384],[-96,-72],[-48,-12],[-48,12],[-96,72]]],"shapes":[[[384,384],[96,384],[96,72],[384,-384]],[[384,-384],[96,72],[48,12],[48,-12],[96,-72]],[[384,-384],[96,-72],[96,-216]],[[384,-384],[96,-216],[96,-384]],[[-96,72],[-96,384],[-384,384],[-384,-384]],[[-48,-12],[-48,12],[-96,72],[-384,-384],[-96,-72]],[[-96,-72],[-384,-384],[-96,-384]]]},{"polygons":[[[-384,96],[384,96],[384,384],[-384,384]],[[-384,-96],[384,-96],[384,-384],[-384,-384]],[[-136,16],[-120,16],[-112,8],[-112,-8],[-120,-16],[-136,-16],[-144,-8],[-144,8]],[[120,16],[136,16],[144,8],[144,-8],[136,-16],[120,-16],[112,-8],[112,8]]],"shapes":[[[-384,96],[384,96],[384,384],[-384,384]],[[-384,-384],[384,-384],[384,-96],[-384,-96]],[[-144,8],[-144,-8],[-136,-16],[-120,-16],[-112,-8],[-112,8],[-120,16],[-136,16]],[[112,8],[112,-8],[120,-16],[136,-16],[144,-8],[144,8],[136,16],[120,16]]]},{"polygons":[[[-96,-384],[-96,384],[-384,384],[-384,-384]],[[96,-384],[96,384],[384,384],[384,-384]],[[-16,-136],[-16,-120],[-8,-112],[8,-112],[16,-120],[16,-136],[8,-144],[-8,-144]],[[-16,120],[-16,136],[-8,144],[8,144],[16,136],[16,120],[8,112],[-8,112]]],"shapes":[[[-96,-384],[-96,384],[-384,384],[-384,-384]],[[384,-384],[384,384],[96,384],[96,-384]],[[-8,-144],[8,-144],[16,-136],[16,-120],[8,-112],[-8,-112],[-16,-120],[-16,-136]],[[-8,112],[8,112],[16,120],[16,136],[8,144],[-8,144],[-16,136],[-16,120]]]},{"polygons":[[[-384,-96],[-300,-96],[-216,-144],[216,-144],[300,-96],[384,-96],[384,-384],[-384,-384]],[[-384,96],[-300,96],[-228,144],[216,144],[312,96],[384,96],[384,384],[-384,384]]],"shapes":[[[-384,-96],[-384,-384],[384,-384],[-216,-144]],[[384,-384],[384,-96],[300,-96],[216,-144]],[[384,-384],[216,-144],[-216,-144]],[[-384,-96],[-216,-144],[-300,-96]],[[-384,384],[-384,96],[-300,96],[-228,144]],[[384,384],[-384,384],[-228,144],[216,144]],[[384,96],[384,384],[216,144],[312,96]]]},{"polygons":[[[-96,384],[-96,300],[-144,216],[-144,-216],[-96,-300],[-96,-384],[-384,-384],[-384,384]],[[96,384],[96,300],[144,228],[144,-216],[96,-312],[96,-384],[384,-384],[384,384]]],"shapes":[[[-96,384],[-384,384],[-384,-384],[-144,216]],[[-384,-384],[-96,-384],[-96,-300],[-144,-216]],[[-384,-384],[-144,-216],[-144,216]],[[-96,384],[-144,216],[-96,300]],[[384,384],[96,384],[96,300],[144,228]],[[384,-384],[384,384],[144,228],[144,-216]],[[96,-384],[384,-384],[144,-216],[96,-312]]]},{"polygons":[[[-384,-352],[-384,-384],[384,-384],[384,-352]],[[-384,-160],[384,-160],[384,384],[-384,384]],[[-8,-240],[8,-240],[16,-248],[16,-264],[8,-272],[-8,-272],[-16,-264],[-16,-248],[-8,-240]]],"shapes":[[[-384,-352],[-384,-384],[384,-384],[384,-352]],[[-384,-160],[384,-160],[384,384],[-384,384]],[[-8,-240],[-16,-248],[-16,-264],[-8,-272],[8,-272],[16,-264],[16,-248],[8,-240],[-8,-240]]]},{"polygons":[[[384,352],[384,384],[-384,384],[-384,352]],[[384,160],[-384,160],[-384,-384],[384,-384]],[[8,240],[-8,240],[-16,248],[-16,264],[-8,272],[8,272],[16,264],[16,248],[8,240]]],"shapes":[[[384,352],[384,384],[-384,384],[-384,352]],[[384,160],[-384,160],[-384,-384],[384,-384]],[[8,240],[16,248],[16,264],[8,272],[-8,272],[-16,264],[-16,248],[-8,240],[8,240]]]},{"polygons":[[[-352,384],[-384,384],[-384,-384],[-352,-384]],[[-160,384],[-160,-384],[384,-384],[384,384]],[[-240,8],[-240,-8],[-248,-16],[-264,-16],[-272,-8],[-272,8],[-264,16],[-248,16]]],"shapes":[[[-352,384],[-384,384],[-384,-384],[-352,-384]],[[-160,384],[-160,-384],[384,-384],[384,384]],[[-248,16],[-264,16],[-272,8],[-272,-8],[-264,-16],[-248,-16],[-240,-8],[-240,8]]]},{"polygons":[[[352,-384],[384,-384],[384,384],[352,384]],[[160,-384],[160,384],[-384,384],[-384,-384]],[[240,-8],[240,8],[248,16],[264,16],[272,8],[272,-8],[264,-16],[248,-16]]],"shapes":[[[352,-384],[384,-384],[384,384],[352,384]],[[160,-384],[160,384],[-384,384],[-384,-384]],[[248,-16],[264,-16],[272,-8],[272,8],[264,16],[248,16],[240,8],[240,-8]]]},{"polygons":[[[-384,-352],[-208,-352],[-112,-304],[112,-304],[208,-352],[384,-352],[384,-384],[-384,-384]],[[-384,-160],[-208,-160],[-112,-208],[112,-208],[208,-160],[384,-160],[384,384],[-384,384]]],"shapes":[[[-384,-352],[-384,-384],[384,-384],[-208,-352]],[[-112,-304],[-208,-352],[384,-384],[208,-352]],[[384,-384],[384,-352],[208,-352]],[[-112,-304],[208,-352],[112,-304]],[[-384,384],[-384,-160],[-208,-160],[384,384]],[[384,384],[-208,-160],[-112,-208],[112,-208],[208,-160]],[[384,384],[208,-160],[384,-160]]]},{"polygons":[[[384,352],[208,352],[112,304],[-112,304],[-208,352],[-384,352],[-384,384],[384,384]],[[384,160],[208,160],[112,208],[-112,208],[-208,160],[-384,160],[-384,-384],[384,-384]]],"shapes":[[[384,352],[384,384],[-384,384],[208,352]],[[112,304],[208,352],[-384,384],[-208,352]],[[-384,384],[-384,352],[-208,352]],[[112,304],[-208,352],[-112,304]],[[384,-384],[384,160],[208,160],[-384,-384]],[[-384,-384],[208,160],[112,208],[-112,208],[-208,160]],[[-384,-384],[-208,160],[-384,160]]]},{"polygons":[[[-352,384],[-352,208],[-304,112],[-304,-112],[-352,-208],[-352,-384],[-384,-384],[-384,384]],[[-160,384],[-160,208],[-208,112],[-208,-112],[-160,-208],[-160,-384],[384,-384],[384,384]]],"shapes":[[[-352,384],[-384,384],[-384,-384],[-352,208]],[[-304,112],[-352,208],[-384,-384],[-352,-208]],[[-384,-384],[-352,-384],[-352,-208]],[[-304,112],[-352,-208],[-304,-112]],[[384,384],[-160,384],[-160,208],[384,-384]],[[384,-384],[-160,208],[-208,112],[-208,-112],[-160,-208]],[[384,-384],[-160,-208],[-160,-384]]]},{"polygons":[[[352,-384],[352,-208],[304,-112],[304,112],[352,208],[352,384],[384,384],[384,-384]],[[160,-384],[160,-208],[208,-112],[208,112],[160,208],[160,384],[-384,384],[-384,-384]]],"shapes":[[[352,-384],[384,-384],[384,384],[352,-208]],[[304,-112],[352,-208],[384,384],[352,208]],[[384,384],[352,384],[352,208]],[[304,-112],[352,208],[304,112]],[[-384,-384],[160,-384],[160,-208],[-384,384]],[[-384,384],[160,-208],[208,-112],[208,112],[160,208]],[[-384,384],[160,208],[160,384]]]},{"polygons":[[[-384,-352],[-64,-352],[-8,-304],[8,-304],[72,-352],[384,-352],[384,-384],[-384,-384]],[[-384,-160],[-72,-160],[-8,-208],[8,-208],[72,-160],[384,-160],[384,384],[-384,384]]],"shapes":[[[-384,-352],[-384,-384],[384,-384],[-64,-352]],[[384,-384],[384,-352],[72,-352]],[[-64,-352],[384,-384],[72,-352]],[[-64,-352],[72,-352],[8,-304],[-8,-304]],[[-384,384],[-384,-160],[-72,-160],[384,384]],[[384,384],[-72,-160],[-8,-208],[8,-208],[72,-160]],[[384,384],[72,-160],[384,-160]]]},{"polygons":[[[384,352],[64,352],[8,304],[-8,304],[-72,352],[-384,352],[-384,384],[384,384]],[[384,160],[72,160],[8,208],[-8,208],[-72,160],[-384,160],[-384,-384],[384,-384]]],"shapes":[[[384,352],[384,384],[-384,384],[64,352]],[[-384,384],[-384,352],[-72,352]],[[64,352],[-384,384],[-72,352]],[[64,352],[-72,352],[-8,304],[8,304]],[[384,-384],[384,160],[72,160],[-384,-384]],[[-384,-384],[72,160],[8,208],[-8,208],[-72,160]],[[-384,-384],[-72,160],[-384,160]]]},{"polygons":[[[-352,384],[-352,64],[-304,8],[-304,-8],[-352,-72],[-352,-384],[-384,-384],[-384,384]],[[-160,384],[-160,72],[-208,8],[-208,-8],[-160,-72],[-160,-384],[384,-384],[384,384]]],"shapes":[[[-352,384],[-384,384],[-384,-384],[-352,64]],[[-384,-384],[-352,-384],[-352,-72]],[[-352,64],[-384,-384],[-352,-72]],[[-352,64],[-352,-72],[-304,-8],[-304,8]],[[384,384],[-160,384],[-160,72],[384,-384]],[[384,-384],[-160,72],[-208,8],[-208,-8],[-160,-72]],[[384,-384],[-160,-72],[-160,-384]]]},{"polygons":[[[352,-384],[352,-64],[304,-8],[304,8],[352,72],[352,384],[384,384],[384,-384]],[[160,-384],[160,-72],[208,-8],[208,8],[160,72],[160,384],[-384,384],[-384,-384]]],"shapes":[[[352,-384],[384,-384],[384,384],[352,-64]],[[384,384],[352,384],[352,72]],[[352,-64],[384,384],[352,72]],[[352,-64],[352,72],[304,8],[304,-8]],[[-384,-384],[160,-384],[160,-72],[-384,384]],[[-384,384],[160,-72],[208,-8],[208,8],[160,72]],[[-384,384],[160,72],[160,384]]]},{"polygons":[[[-384,-352],[-384,-384],[384,-384],[384,-352]],[[-384,-160],[384,-160],[384,384],[-384,384]],[[136,-272],[120,-272],[112,-264],[112,-248],[120,-240],[136,-240],[144,-248],[144,-264]],[[-120,-272],[-136,-272],[-144,-264],[-144,-248],[-136,-240],[-120,-240],[-112,-248],[-112,-264]]],"shapes":[[[-384,-352],[-384,-384],[384,-384],[384,-352]],[[-384,-160],[384,-160],[384,384],[-384,384]],[[144,-264],[144,-248],[136,-240],[120,-240],[112,-248],[112,-264],[120,-272],[136,-272]],[[-112,-264],[-112,-248],[-120,-240],[-136,-240],[-144,-248],[-144,-264],[-136,-272],[-120,-272]]]},{"polygons":[[[384,352],[384,384],[-384,384],[-384,352]],[[384,160],[-384,160],[-384,-384],[384,-384]],[[-136,272],[-120,272],[-112,264],[-112,248],[-120,240],[-136,240],[-144,248],[-144,264]],[[120,272],[136,272],[144,264],[144,248],[136,240],[120,240],[112,248],[112,264]]],"shapes":[[[384,352],[384,384],[-384,384],[-384,352]],[[384,160],[-384,160],[-384,-384],[384,-384]],[[-144,264],[-144,248],[-136,240],[-120,240],[-112,248],[-112,264],[-120,272],[-136,272]],[[112,264],[112,248],[120,240],[136,240],[144,248],[144,264],[136,272],[120,272]]]},{"polygons":[[[-352,384],[-384,384],[-384,-384],[-352,-384]],[[-160,384],[-160,-384],[384,-384],[384,384]],[[-272,-136],[-272,-120],[-264,-112],[-248,-112],[-240,-120],[-240,-136],[-248,-144],[-264,-144]],[[-272,120],[-272,136],[-264,144],[-248,144],[-240,136],[-240,120],[-248,112],[-264,112]]],"shapes":[[[-352,384],[-384,384],[-384,-384],[-352,-384]],[[-160,384],[-160,-384],[384,-384],[384,384]],[[-264,-144],[-248,-144],[-240,-136],[-240,-120],[-248,-112],[-264,-112],[-272,-120],[-272,-136]],[[-264,112],[-248,112],[-240,120],[-240,136],[-248,144],[-264,144],[-272,136],[-272,120]]]},{"polygons":[[[352,-384],[384,-384],[384,384],[352,384]],[[160,-384],[160,384],[-384,384],[-384,-384]],[[272,136],[272,120],[264,112],[248,112],[240,120],[240,136],[248,144],[264,144]],[[272,-120],[272,-136],[264,-144],[248,-144],[240,-136],[240,-120],[248,-112],[264,-112]]],"shapes":[[[352,-384],[384,-384],[384,384],[352,384]],[[160,-384],[160,384],[-384,384],[-384,-384]],[[264,144],[248,144],[240,136],[240,120],[248,112],[264,112],[272,120],[272,136]],[[264,-112],[248,-112],[240,-120],[240,-136],[248,-144],[264,-144],[272,-136],[272,-120]]]},{"polygons":[[[-355.75,-384],[-384,-298.5],[-384,-384]],[[-156.16099548339844,-384],[-384,299.4530029296875],[-384,384],[384,384],[384,-384]]],"shapes":[[[-355.75,-384],[-384,-298.5],[-384,-384]],[[384,-384],[384,384],[-384,384],[-384,299.4530029296875],[-156.16099548339844,-384]]]},{"polygons":[[[355.75,-384],[384,-298.5],[384,-384]],[[156.16099548339844,-384],[384,299.4530029296875],[384,384],[-384,384],[-384,-384]]],"shapes":[[[384,-384],[384,-298.5],[355.75,-384]],[[156.16099548339844,-384],[384,299.4530029296875],[384,384],[-384,384],[-384,-384]]]},{"polygons":[[[-355.75,384],[-384,298.5],[-384,384]],[[-156.16099548339844,384],[-384,-299.4530029296875],[-384,-384],[384,-384],[384,384]]],"shapes":[[[-384,384],[-384,298.5],[-355.75,384]],[[-156.16099548339844,384],[-384,-299.4530029296875],[-384,-384],[384,-384],[384,384]]]},{"polygons":[[[-384,-355.75],[-298.5,-384],[-384,-384]],[[-384,-156.16099548339844],[299.4530029296875,-384],[384,-384],[384,384],[-384,384]]],"shapes":[[[-384,-384],[-298.5,-384],[-384,-355.75]],[[-384,-156.16099548339844],[299.4530029296875,-384],[384,-384],[384,384],[-384,384]]]},{"polygons":[[[355.75,384],[384,298.5],[384,384]],[[156.16099548339844,384],[384,-299.4530029296875],[384,-384],[-384,-384],[-384,384]]],"shapes":[[[355.75,384],[384,298.5],[384,384]],[[-384,384],[-384,-384],[384,-384],[384,-299.4530029296875],[156.16099548339844,384]]]},{"polygons":[[[-384,355.75],[-298.5,384],[-384,384]],[[-384,156.16099548339844],[299.4530029296875,384],[384,384],[384,-384],[-384,-384]]],"shapes":[[[-384,355.75],[-298.5,384],[-384,384]],[[-384,-384],[384,-384],[384,384],[299.4530029296875,384],[-384,156.16099548339844]]]},{"polygons":[[[384,-355.75],[298.5,-384],[384,-384]],[[384,-156.16099548339844],[-299.4530029296875,-384],[-384,-384],[-384,384],[384,384]]],"shapes":[[[384,-355.75],[298.5,-384],[384,-384]],[[384,384],[-384,384],[-384,-384],[-299.4530029296875,-384],[384,-156.16099548339844]]]},{"polygons":[[[384,355.75],[298.5,384],[384,384]],[[384,156.16099548339844],[-299.4530029296875,384],[-384,384],[-384,-384],[384,-384]]],"shapes":[[[384,384],[298.5,384],[384,355.75]],[[384,156.16099548339844],[-299.4530029296875,384],[-384,384],[-384,-384],[384,-384]]]},{"polygons":[[[-350.7309875488281,-384],[-354.02099609375,-373.9549865722656],[-371.39300537109375,-336.98199462890625],[-384,-298.5],[-384,-384]],[[-160.375,-384],[-183.49400329589844,-298.39898681640625],[-273.2829895019531,-21.38159942626953],[-384,299.4530029296875],[-384,384],[384,384],[384,-384]]],"shapes":[[[-371.39300537109375,-336.98199462890625],[-384,-298.5],[-384,-384]],[[-354.02099609375,-373.9549865722656],[-371.39300537109375,-336.98199462890625],[-384,-384],[-350.7309875488281,-384]],[[-160.375,-384],[384,-384],[384,384],[-183.49400329589844,-298.39898681640625]],[[-183.49400329589844,-298.39898681640625],[384,384],[-384,384],[-273.2829895019531,-21.38159942626953]],[[-273.2829895019531,-21.38159942626953],[-384,384],[-384,299.4530029296875]]]},{"polygons":[[[350.7309875488281,-384],[354.02099609375,-373.9549865722656],[371.39300537109375,-336.98199462890625],[384,-298.5],[384,-384]],[[160.375,-384],[183.49400329589844,-298.39898681640625],[273.2829895019531,-21.38159942626953],[384,299.4530029296875],[384,384],[-384,384],[-384,-384]]],"shapes":[[[384,-384],[384,-298.5],[371.39300537109375,-336.98199462890625]],[[384,-384],[371.39300537109375,-336.98199462890625],[354.02099609375,-373.9549865722656],[350.7309875488281,-384]],[[273.2829895019531,-21.38159942626953],[384,299.4530029296875],[384,384],[-384,384],[-384,-384]],[[183.49400329589844,-298.39898681640625],[273.2829895019531,-21.38159942626953],[-384,-384]],[[-384,-384],[160.375,-384],[183.49400329589844,-298.39898681640625]]]},{"polygons":[[[-350.7309875488281,384],[-354.02099609375,373.9549865722656],[-371.39300537109375,336.98199462890625],[-384,298.5],[-384,384]],[[-160.375,384],[-183.49400329589844,298.39898681640625],[-273.2829895019531,21.38159942626953],[-384,-299.4530029296875],[-384,-384],[384,-384],[384,384]]],"shapes":[[[-384,384],[-384,298.5],[-371.39300537109375,336.98199462890625]],[[-384,384],[-371.39300537109375,336.98199462890625],[-354.02099609375,373.9549865722656],[-350.7309875488281,384]],[[-273.2829895019531,21.38159942626953],[-384,-299.4530029296875],[-384,-384],[384,-384],[384,384]],[[-183.49400329589844,298.39898681640625],[-273.2829895019531,21.38159942626953],[384,384]],[[384,384],[-160.375,384],[-183.49400329589844,298.39898681640625]]]},{"polygons":[[[-384,-350.7309875488281],[-373.9549865722656,-354.02099609375],[-336.98199462890625,-371.39300537109375],[-298.5,-384],[-384,-384]],[[-384,-160.375],[-298.39898681640625,-183.49400329589844],[-21.38159942626953,-273.2829895019531],[299.4530029296875,-384],[384,-384],[384,384],[-384,384]]],"shapes":[[[-384,-384],[-298.5,-384],[-336.98199462890625,-371.39300537109375]],[[-384,-384],[-336.98199462890625,-371.39300537109375],[-373.9549865722656,-354.02099609375],[-384,-350.7309875488281]],[[-21.38159942626953,-273.2829895019531],[299.4530029296875,-384],[384,-384],[384,384],[-384,384]],[[-298.39898681640625,-183.49400329589844],[-21.38159942626953,-273.2829895019531],[-384,384]],[[-384,384],[-384,-160.375],[-298.39898681640625,-183.49400329589844]]]},{"polygons":[[[350.7309875488281,384],[354.02099609375,373.9549865722656],[371.39300537109375,336.98199462890625],[384,298.5],[384,384]],[[160.375,384],[183.49400329589844,298.39898681640625],[273.2829895019531,21.38159942626953],[384,-299.4530029296875],[384,-384],[-384,-384],[-384,384]]],"shapes":[[[371.39300537109375,336.98199462890625],[384,298.5],[384,384]],[[354.02099609375,373.9549865722656],[371.39300537109375,336.98199462890625],[384,384],[350.7309875488281,384]],[[160.375,384],[-384,384],[-384,-384],[183.49400329589844,298.39898681640625]],[[183.49400329589844,298.39898681640625],[-384,-384],[384,-384],[273.2829895019531,21.38159942626953]],[[273.2829895019531,21.38159942626953],[384,-384],[384,-299.4530029296875]]]},{"polygons":[[[-384,350.7309875488281],[-373.9549865722656,354.02099609375],[-336.98199462890625,371.39300537109375],[-298.5,384],[-384,384]],[[-384,160.375],[-298.39898681640625,183.49400329589844],[-21.38159942626953,273.2829895019531],[299.4530029296875,384],[384,384],[384,-384],[-384,-384]]],"shapes":[[[-336.98199462890625,371.39300537109375],[-298.5,384],[-384,384]],[[-373.9549865722656,354.02099609375],[-336.98199462890625,371.39300537109375],[-384,384],[-384,350.7309875488281]],[[-384,160.375],[-384,-384],[384,-384],[-298.39898681640625,183.49400329589844]],[[-298.39898681640625,183.49400329589844],[384,-384],[384,384],[-21.38159942626953,273.2829895019531]],[[-21.38159942626953,273.2829895019531],[384,384],[299.4530029296875,384]]]},{"polygons":[[[384,-350.7309875488281],[373.9549865722656,-354.02099609375],[336.98199462890625,-371.39300537109375],[298.5,-384],[384,-384]],[[384,-160.375],[298.39898681640625,-183.49400329589844],[21.38159942626953,-273.2829895019531],[-299.4530029296875,-384],[-384,-384],[-384,384],[384,384]]],"shapes":[[[336.98199462890625,-371.39300537109375],[298.5,-384],[384,-384]],[[373.9549865722656,-354.02099609375],[336.98199462890625,-371.39300537109375],[384,-384],[384,-350.7309875488281]],[[384,-160.375],[384,384],[-384,384],[298.39898681640625,-183.49400329589844]],[[298.39898681640625,-183.49400329589844],[-384,384],[-384,-384],[21.38159942626953,-273.2829895019531]],[[21.38159942626953,-273.2829895019531],[-384,-384],[-299.4530029296875,-384]]]},{"polygons":[[[384,350.7309875488281],[373.9549865722656,354.02099609375],[336.98199462890625,371.39300537109375],[298.5,384],[384,384]],[[384,160.375],[298.39898681640625,183.49400329589844],[21.38159942626953,273.2829895019531],[-299.4530029296875,384],[-384,384],[-384,-384],[384,-384]]],"shapes":[[[384,384],[298.5,384],[336.98199462890625,371.39300537109375]],[[384,384],[336.98199462890625,371.39300537109375],[373.9549865722656,354.02099609375],[384,350.7309875488281]],[[21.38159942626953,273.2829895019531],[-299.4530029296875,384],[-384,384],[-384,-384],[384,-384]],[[298.39898681640625,183.49400329589844],[21.38159942626953,273.2829895019531],[384,-384]],[[384,-384],[384,160.375],[298.39898681640625,183.49400329589844]]]},{"polygons":[[[-384,-100.16400146484375],[-384,-384],[384,-384],[384,-357.18798828125]],[[-384,100.16400146484375],[384,-155.60000610351562],[384,384],[-384,384]]],"shapes":[[[-384,-100.16400146484375],[-384,-384],[384,-384],[384,-357.18798828125]],[[-384,100.16400146484375],[384,-155.60000610351562],[384,384],[-384,384]]]},{"polygons":[[[384,-100.16400146484375],[384,-384],[-384,-384],[-384,-357.18798828125]],[[384,100.16400146484375],[-384,-155.60000610351562],[-384,384],[384,384]]],"shapes":[[[-384,-357.18798828125],[-384,-384],[384,-384],[384,-100.16400146484375]],[[384,384],[-384,384],[-384,-155.60000610351562],[384,100.16400146484375]]]},{"polygons":[[[-384,100.16400146484375],[-384,384],[384,384],[384,357.18798828125]],[[-384,-100.16400146484375],[384,155.60000610351562],[384,-384],[-384,-384]]],"shapes":[[[384,357.18798828125],[384,384],[-384,384],[-384,100.16400146484375]],[[-384,-384],[384,-384],[384,155.60000610351562],[-384,-100.16400146484375]]]},{"polygons":[[[-100.16400146484375,-384],[-384,-384],[-384,384],[-357.18798828125,384]],[[100.16400146484375,-384],[-155.60000610351562,384],[384,384],[384,-384]]],"shapes":[[[-357.18798828125,384],[-384,384],[-384,-384],[-100.16400146484375,-384]],[[384,-384],[384,384],[-155.60000610351562,384],[100.16400146484375,-384]]]},{"polygons":[[[384,100.16400146484375],[384,384],[-384,384],[-384,357.18798828125]],[[384,-100.16400146484375],[-384,155.60000610351562],[-384,-384],[384,-384]]],"shapes":[[[384,100.16400146484375],[384,384],[-384,384],[-384,357.18798828125]],[[384,-100.16400146484375],[-384,155.60000610351562],[-384,-384],[384,-384]]]},{"polygons":[[[-100.16400146484375,384],[-384,384],[-384,-384],[-357.18798828125,-384]],[[100.16400146484375,384],[-155.60000610351562,-384],[384,-384],[384,384]]],"shapes":[[[-100.16400146484375,384],[-384,384],[-384,-384],[-357.18798828125,-384]],[[100.16400146484375,384],[-155.60000610351562,-384],[384,-384],[384,384]]]},{"polygons":[[[100.16400146484375,-384],[384,-384],[384,384],[357.18798828125,384]],[[-100.16400146484375,-384],[155.60000610351562,384],[-384,384],[-384,-384]]],"shapes":[[[100.16400146484375,-384],[384,-384],[384,384],[357.18798828125,384]],[[-100.16400146484375,-384],[155.60000610351562,384],[-384,384],[-384,-384]]]},{"polygons":[[[100.16400146484375,384],[384,384],[384,-384],[357.18798828125,-384]],[[-100.16400146484375,384],[155.60000610351562,-384],[-384,-384],[-384,384]]],"shapes":[[[357.18798828125,-384],[384,-384],[384,384],[100.16400146484375,384]],[[-384,384],[-384,-384],[155.60000610351562,-384],[-100.16400146484375,384]]]},{"polygons":[[[-384,-95.4092025756836],[-384,-384],[384,-384],[384,-357.18798828125],[-205.9720001220703,-133.572998046875],[-323.8299865722656,-102.14399719238281]],[[-384,96.53170013427734],[-271.07501220703125,85.30709838867188],[-128.52200317382812,46.02090072631836],[204.8489990234375,-89.7968978881836],[384,-155.60000610351562],[384,384],[-384,384]]],"shapes":[[[-384,-95.4092025756836],[-384,-384],[384,-384],[384,-357.18798828125],[-205.9720001220703,-133.572998046875],[-323.8299865722656,-102.14399719238281]],[[204.8489990234375,-89.7968978881836],[384,-155.60000610351562],[384,384],[-384,384],[-128.52200317382812,46.02090072631836]],[[-271.07501220703125,85.30709838867188],[-128.52200317382812,46.02090072631836],[-384,384]],[[-384,384],[-384,96.53170013427734],[-271.07501220703125,85.30709838867188]]]},{"polygons":[[[384,-95.4092025756836],[384,-384],[-384,-384],[-384,-357.18798828125],[205.9720001220703,-133.572998046875],[323.8299865722656,-102.14399719238281]],[[384,96.53170013427734],[271.07501220703125,85.30709838867188],[128.52200317382812,46.02090072631836],[-204.8489990234375,-89.7968978881836],[-384,-155.60000610351562],[-384,384],[384,384]]],"shapes":[[[323.8299865722656,-102.14399719238281],[205.9720001220703,-133.572998046875],[-384,-357.18798828125],[-384,-384],[384,-384],[384,-95.4092025756836]],[[384,384],[-384,384],[-384,-155.60000610351562],[-204.8489990234375,-89.7968978881836],[128.52200317382812,46.02090072631836]],[[384,384],[128.52200317382812,46.02090072631836],[271.07501220703125,85.30709838867188]],[[384,96.53170013427734],[384,384],[271.07501220703125,85.30709838867188]]]},{"polygons":[[[-384,95.4092025756836],[-384,384],[384,384],[384,357.18798828125],[-205.9720001220703,133.572998046875],[-323.8299865722656,102.14399719238281]],[[-384,-96.53170013427734],[-271.07501220703125,-85.30709838867188],[-128.52200317382812,-46.02090072631836],[204.8489990234375,89.7968978881836],[384,155.60000610351562],[384,-384],[-384,-384]]],"shapes":[[[-323.8299865722656,102.14399719238281],[-205.9720001220703,133.572998046875],[384,357.18798828125],[384,384],[-384,384],[-384,95.4092025756836]],[[-384,-384],[384,-384],[384,155.60000610351562],[204.8489990234375,89.7968978881836],[-128.52200317382812,-46.02090072631836]],[[-384,-384],[-128.52200317382812,-46.02090072631836],[-271.07501220703125,-85.30709838867188]],[[-384,-96.53170013427734],[-384,-384],[-271.07501220703125,-85.30709838867188]]]},{"polygons":[[[-95.4092025756836,-384],[-384,-384],[-384,384],[-357.18798828125,384],[-133.572998046875,-205.9720001220703],[-102.14399719238281,-323.8299865722656]],[[96.53170013427734,-384],[85.30709838867188,-271.07501220703125],[46.02090072631836,-128.52200317382812],[-89.7968978881836,204.8489990234375],[-155.60000610351562,384],[384,384],[384,-384]]],"shapes":[[[-102.14399719238281,-323.8299865722656],[-133.572998046875,-205.9720001220703],[-357.18798828125,384],[-384,384],[-384,-384],[-95.4092025756836,-384]],[[384,-384],[384,384],[-155.60000610351562,384],[-89.7968978881836,204.8489990234375],[46.02090072631836,-128.52200317382812]],[[384,-384],[46.02090072631836,-128.52200317382812],[85.30709838867188,-271.07501220703125]],[[96.53170013427734,-384],[384,-384],[85.30709838867188,-271.07501220703125]]]},{"polygons":[[[384,95.4092025756836],[384,384],[-384,384],[-384,357.18798828125],[205.9720001220703,133.572998046875],[323.8299865722656,102.14399719238281]],[[384,-96.53170013427734],[271.07501220703125,-85.30709838867188],[128.52200317382812,-46.02090072631836],[-204.8489990234375,89.7968978881836],[-384,155.60000610351562],[-384,-384],[384,-384]]],"shapes":[[[384,95.4092025756836],[384,384],[-384,384],[-384,357.18798828125],[205.9720001220703,133.572998046875],[323.8299865722656,102.14399719238281]],[[-204.8489990234375,89.7968978881836],[-384,155.60000610351562],[-384,-384],[384,-384],[128.52200317382812,-46.02090072631836]],[[271.07501220703125,-85.30709838867188],[128.52200317382812,-46.02090072631836],[384,-384]],[[384,-384],[384,-96.53170013427734],[271.07501220703125,-85.30709838867188]]]},{"polygons":[[[-95.4092025756836,384],[-384,384],[-384,-384],[-357.18798828125,-384],[-133.572998046875,205.9720001220703],[-102.14399719238281,323.8299865722656]],[[96.53170013427734,384],[85.30709838867188,271.07501220703125],[46.02090072631836,128.52200317382812],[-89.7968978881836,-204.8489990234375],[-155.60000610351562,-384],[384,-384],[384,384]]],"shapes":[[[-95.4092025756836,384],[-384,384],[-384,-384],[-357.18798828125,-384],[-133.572998046875,205.9720001220703],[-102.14399719238281,323.8299865722656]],[[-89.7968978881836,-204.8489990234375],[-155.60000610351562,-384],[384,-384],[384,384],[46.02090072631836,128.52200317382812]],[[85.30709838867188,271.07501220703125],[46.02090072631836,128.52200317382812],[384,384]],[[384,384],[96.53170013427734,384],[85.30709838867188,271.07501220703125]]]},{"polygons":[[[95.4092025756836,-384],[384,-384],[384,384],[357.18798828125,384],[133.572998046875,-205.9720001220703],[102.14399719238281,-323.8299865722656]],[[-96.53170013427734,-384],[-85.30709838867188,-271.07501220703125],[-46.02090072631836,-128.52200317382812],[89.7968978881836,204.8489990234375],[155.60000610351562,384],[-384,384],[-384,-384]]],"shapes":[[[95.4092025756836,-384],[384,-384],[384,384],[357.18798828125,384],[133.572998046875,-205.9720001220703],[102.14399719238281,-323.8299865722656]],[[89.7968978881836,204.8489990234375],[155.60000610351562,384],[-384,384],[-384,-384],[-46.02090072631836,-128.52200317382812]],[[-85.30709838867188,-271.07501220703125],[-46.02090072631836,-128.52200317382812],[-384,-384]],[[-384,-384],[-96.53170013427734,-384],[-85.30709838867188,-271.07501220703125]]]},{"polygons":[[[95.4092025756836,384],[384,384],[384,-384],[357.18798828125,-384],[133.572998046875,205.9720001220703],[102.14399719238281,323.8299865722656]],[[-96.53170013427734,384],[-85.30709838867188,271.07501220703125],[-46.02090072631836,128.52200317382812],[89.7968978881836,-204.8489990234375],[155.60000610351562,-384],[-384,-384],[-384,384]]],"shapes":[[[102.14399719238281,323.8299865722656],[133.572998046875,205.9720001220703],[357.18798828125,-384],[384,-384],[384,384],[95.4092025756836,384]],[[-384,384],[-384,-384],[155.60000610351562,-384],[89.7968978881836,-204.8489990234375],[-46.02090072631836,128.52200317382812]],[[-384,384],[-46.02090072631836,128.52200317382812],[-85.30709838867188,271.07501220703125]],[[-96.53170013427734,384],[-384,384],[-85.30709838867188,271.07501220703125]]]},{"polygons":[[[384,-351.2550048828125],[384,-384],[-384,-384],[-384,-100.05599975585938],[85.55989837646484,-287.0849914550781],[215.76100158691406,-327.5790100097656],[332.7510070800781,-350.12298583984375]],[[-384,99.61849975585938],[-384,384],[384,384],[384,-161.12100219726562],[310.4779968261719,-152.343994140625],[166.1529998779297,-107.79900360107422],[5.790830135345459,-47.21760177612305]]],"shapes":[[[85.55989837646484,-287.0849914550781],[-384,-100.05599975585938],[-384,-384]],[[85.55989837646484,-287.0849914550781],[-384,-384],[384,-384],[215.76100158691406,-327.5790100097656]],[[332.7510070800781,-350.12298583984375],[215.76100158691406,-327.5790100097656],[384,-384]],[[384,-351.2550048828125],[332.7510070800781,-350.12298583984375],[384,-384]],[[166.1529998779297,-107.79900360107422],[310.4779968261719,-152.343994140625],[384,-161.12100219726562],[384,384],[-384,384],[5.790830135345459,-47.21760177612305]],[[-384,99.61849975585938],[5.790830135345459,-47.21760177612305],[-384,384]]]},{"polygons":[[[-384,-351.2550048828125],[-384,-384],[384,-384],[384,-100.05599975585938],[-85.55989837646484,-287.0849914550781],[-215.76100158691406,-327.5790100097656],[-332.7510070800781,-350.12298583984375]],[[384,99.61849975585938],[384,384],[-384,384],[-384,-161.12100219726562],[-310.4779968261719,-152.343994140625],[-166.1529998779297,-107.79900360107422],[-5.790830135345459,-47.21760177612305]]],"shapes":[[[384,-384],[384,-100.05599975585938],[-85.55989837646484,-287.0849914550781]],[[-384,-384],[384,-384],[-85.55989837646484,-287.0849914550781],[-215.76100158691406,-327.5790100097656]],[[-384,-384],[-215.76100158691406,-327.5790100097656],[-332.7510070800781,-350.12298583984375]],[[-332.7510070800781,-350.12298583984375],[-384,-351.2550048828125],[-384,-384]],[[384,384],[-384,384],[-384,-161.12100219726562],[-310.4779968261719,-152.343994140625],[-166.1529998779297,-107.79900360107422],[-5.790830135345459,-47.21760177612305]],[[-5.790830135345459,-47.21760177612305],[384,99.61849975585938],[384,384]]]},{"polygons":[[[384,351.2550048828125],[384,384],[-384,384],[-384,100.05599975585938],[85.55989837646484,287.0849914550781],[215.76100158691406,327.5790100097656],[332.7510070800781,350.12298583984375]],[[-384,-99.61849975585938],[-384,-384],[384,-384],[384,161.12100219726562],[310.4779968261719,152.343994140625],[166.1529998779297,107.79900360107422],[5.790830135345459,47.21760177612305]]],"shapes":[[[-384,384],[-384,100.05599975585938],[85.55989837646484,287.0849914550781]],[[384,384],[-384,384],[85.55989837646484,287.0849914550781],[215.76100158691406,327.5790100097656]],[[384,384],[215.76100158691406,327.5790100097656],[332.7510070800781,350.12298583984375]],[[332.7510070800781,350.12298583984375],[384,351.2550048828125],[384,384]],[[-384,-384],[384,-384],[384,161.12100219726562],[310.4779968261719,152.343994140625],[166.1529998779297,107.79900360107422],[5.790830135345459,47.21760177612305]],[[5.790830135345459,47.21760177612305],[-384,-99.61849975585938],[-384,-384]]]},{"polygons":[[[-351.2550048828125,384],[-384,384],[-384,-384],[-100.05599975585938,-384],[-287.0849914550781,85.55989837646484],[-327.5790100097656,215.76100158691406],[-350.12298583984375,332.7510070800781]],[[99.61849975585938,-384],[384,-384],[384,384],[-161.12100219726562,384],[-152.343994140625,310.4779968261719],[-107.79900360107422,166.1529998779297],[-47.21760177612305,5.790830135345459]]],"shapes":[[[-384,-384],[-100.05599975585938,-384],[-287.0849914550781,85.55989837646484]],[[-384,384],[-384,-384],[-287.0849914550781,85.55989837646484],[-327.5790100097656,215.76100158691406]],[[-384,384],[-327.5790100097656,215.76100158691406],[-350.12298583984375,332.7510070800781]],[[-350.12298583984375,332.7510070800781],[-351.2550048828125,384],[-384,384]],[[384,-384],[384,384],[-161.12100219726562,384],[-152.343994140625,310.4779968261719],[-107.79900360107422,166.1529998779297],[-47.21760177612305,5.790830135345459]],[[-47.21760177612305,5.790830135345459],[99.61849975585938,-384],[384,-384]]]},{"polygons":[[[-384,351.2550048828125],[-384,384],[384,384],[384,100.05599975585938],[-85.55989837646484,287.0849914550781],[-215.76100158691406,327.5790100097656],[-332.7510070800781,350.12298583984375]],[[384,-99.61849975585938],[384,-384],[-384,-384],[-384,161.12100219726562],[-310.4779968261719,152.343994140625],[-166.1529998779297,107.79900360107422],[-5.790830135345459,47.21760177612305]]],"shapes":[[[-85.55989837646484,287.0849914550781],[384,100.05599975585938],[384,384]],[[-85.55989837646484,287.0849914550781],[384,384],[-384,384],[-215.76100158691406,327.5790100097656]],[[-332.7510070800781,350.12298583984375],[-215.76100158691406,327.5790100097656],[-384,384]],[[-384,351.2550048828125],[-332.7510070800781,350.12298583984375],[-384,384]],[[-166.1529998779297,107.79900360107422],[-310.4779968261719,152.343994140625],[-384,161.12100219726562],[-384,-384],[384,-384],[-5.790830135345459,47.21760177612305]],[[384,-99.61849975585938],[-5.790830135345459,47.21760177612305],[384,-384]]]},{"polygons":[[[-351.2550048828125,-384],[-384,-384],[-384,384],[-100.05599975585938,384],[-287.0849914550781,-85.55989837646484],[-327.5790100097656,-215.76100158691406],[-350.12298583984375,-332.7510070800781]],[[99.61849975585938,384],[384,384],[384,-384],[-161.12100219726562,-384],[-152.343994140625,-310.4779968261719],[-107.79900360107422,-166.1529998779297],[-47.21760177612305,-5.790830135345459]]],"shapes":[[[-287.0849914550781,-85.55989837646484],[-100.05599975585938,384],[-384,384]],[[-287.0849914550781,-85.55989837646484],[-384,384],[-384,-384],[-327.5790100097656,-215.76100158691406]],[[-350.12298583984375,-332.7510070800781],[-327.5790100097656,-215.76100158691406],[-384,-384]],[[-351.2550048828125,-384],[-350.12298583984375,-332.7510070800781],[-384,-384]],[[-107.79900360107422,-166.1529998779297],[-152.343994140625,-310.4779968261719],[-161.12100219726562,-384],[384,-384],[384,384],[-47.21760177612305,-5.790830135345459]],[[99.61849975585938,384],[-47.21760177612305,-5.790830135345459],[384,384]]]},{"polygons":[[[351.2550048828125,384],[384,384],[384,-384],[100.05599975585938,-384],[287.0849914550781,85.55989837646484],[327.5790100097656,215.76100158691406],[350.12298583984375,332.7510070800781]],[[-99.61849975585938,-384],[-384,-384],[-384,384],[161.12100219726562,384],[152.343994140625,310.4779968261719],[107.79900360107422,166.1529998779297],[47.21760177612305,5.790830135345459]]],"shapes":[[[287.0849914550781,85.55989837646484],[100.05599975585938,-384],[384,-384]],[[287.0849914550781,85.55989837646484],[384,-384],[384,384],[327.5790100097656,215.76100158691406]],[[350.12298583984375,332.7510070800781],[327.5790100097656,215.76100158691406],[384,384]],[[351.2550048828125,384],[350.12298583984375,332.7510070800781],[384,384]],[[107.79900360107422,166.1529998779297],[152.343994140625,310.4779968261719],[161.12100219726562,384],[-384,384],[-384,-384],[47.21760177612305,5.790830135345459]],[[-99.61849975585938,-384],[47.21760177612305,5.790830135345459],[-384,-384]]]},{"polygons":[[[351.2550048828125,-384],[384,-384],[384,384],[100.05599975585938,384],[287.0849914550781,-85.55989837646484],[327.5790100097656,-215.76100158691406],[350.12298583984375,-332.7510070800781]],[[-99.61849975585938,384],[-384,384],[-384,-384],[161.12100219726562,-384],[152.343994140625,-310.4779968261719],[107.79900360107422,-166.1529998779297],[47.21760177612305,-5.790830135345459]]],"shapes":[[[384,384],[100.05599975585938,384],[287.0849914550781,-85.55989837646484]],[[384,-384],[384,384],[287.0849914550781,-85.55989837646484],[327.5790100097656,-215.76100158691406]],[[384,-384],[327.5790100097656,-215.76100158691406],[350.12298583984375,-332.7510070800781]],[[350.12298583984375,-332.7510070800781],[351.2550048828125,-384],[384,-384]],[[-384,384],[-384,-384],[161.12100219726562,-384],[152.343994140625,-310.4779968261719],[107.79900360107422,-166.1529998779297],[47.21760177612305,-5.790830135345459]],[[47.21760177612305,-5.790830135345459],[-99.61849975585938,384],[-384,384]]]},{"polygons":[[[-352,-384],[-352,-208],[-368,-208],[-368,-128],[-384,-112],[-384,-384]],[[-160,-384],[-160,-224],[-160,-80],[-192,16],[-256,80],[-320,96],[-384,96],[-384,384],[384,384],[384,-384]]],"shapes":[[[-368,-128],[-384,-112],[-384,-384],[-368,-208]],[[-368,-208],[-384,-384],[-352,-384],[-352,-208]],[[-160,-384],[384,-384],[384,384],[-160,-224]],[[-160,-80],[-160,-224],[384,384]],[[-192,16],[-160,-80],[384,384]],[[-192,16],[384,384],[-384,384],[-256,80]],[[-320,96],[-256,80],[-384,384]],[[-320,96],[-384,384],[-384,96]]]},{"polygons":[[[352,-384],[352,-208],[368,-208],[368,-128],[384,-112],[384,-384]],[[160,-384],[160,-224],[160,-80],[192,16],[256,80],[320,96],[384,96],[384,384],[-384,384],[-384,-384]]],"shapes":[[[384,-384],[384,-112],[368,-128],[368,-208]],[[352,-384],[384,-384],[368,-208],[352,-208]],[[-384,-384],[160,-384],[160,-224],[-384,384]],[[-384,384],[160,-224],[160,-80]],[[-384,384],[160,-80],[192,16]],[[384,384],[-384,384],[192,16],[256,80]],[[384,384],[256,80],[320,96]],[[384,384],[320,96],[384,96]]]},{"polygons":[[[-352,384],[-352,208],[-368,208],[-368,128],[-384,112],[-384,384]],[[-160,384],[-160,224],[-160,80],[-192,-16],[-256,-80],[-320,-96],[-384,-96],[-384,-384],[384,-384],[384,384]]],"shapes":[[[-384,384],[-384,112],[-368,128],[-368,208]],[[-352,384],[-384,384],[-368,208],[-352,208]],[[384,384],[-160,384],[-160,224],[384,-384]],[[384,-384],[-160,224],[-160,80]],[[384,-384],[-160,80],[-192,-16]],[[-384,-384],[384,-384],[-192,-16],[-256,-80]],[[-384,-384],[-256,-80],[-320,-96]],[[-384,-384],[-320,-96],[-384,-96]]]},{"polygons":[[[-384,-352],[-208,-352],[-208,-368],[-128,-368],[-112,-384],[-384,-384]],[[-384,-160],[-224,-160],[-80,-160],[16,-192],[80,-256],[96,-320],[96,-384],[384,-384],[384,384],[-384,384]]],"shapes":[[[-384,-384],[-112,-384],[-128,-368],[-208,-368]],[[-384,-352],[-384,-384],[-208,-368],[-208,-352]],[[-384,384],[-384,-160],[-224,-160],[384,384]],[[384,384],[-224,-160],[-80,-160]],[[384,384],[-80,-160],[16,-192]],[[384,-384],[384,384],[16,-192],[80,-256]],[[384,-384],[80,-256],[96,-320]],[[384,-384],[96,-320],[96,-384]]]},{"polygons":[[[352,384],[352,208],[368,208],[368,128],[384,112],[384,384]],[[160,384],[160,224],[160,80],[192,-16],[256,-80],[320,-96],[384,-96],[384,-384],[-384,-384],[-384,384]]],"shapes":[[[368,128],[384,112],[384,384],[368,208]],[[368,208],[384,384],[352,384],[352,208]],[[160,384],[-384,384],[-384,-384],[160,224]],[[160,80],[160,224],[-384,-384]],[[192,-16],[160,80],[-384,-384]],[[192,-16],[-384,-384],[384,-384],[256,-80]],[[320,-96],[256,-80],[384,-384]],[[320,-96],[384,-384],[384,-96]]]},{"polygons":[[[-384,352],[-208,352],[-208,368],[-128,368],[-112,384],[-384,384]],[[-384,160],[-224,160],[-80,160],[16,192],[80,256],[96,320],[96,384],[384,384],[384,-384],[-384,-384]]],"shapes":[[[-128,368],[-112,384],[-384,384],[-208,368]],[[-208,368],[-384,384],[-384,352],[-208,352]],[[-384,160],[-384,-384],[384,-384],[-224,160]],[[-80,160],[-224,160],[384,-384]],[[16,192],[-80,160],[384,-384]],[[16,192],[384,-384],[384,384],[80,256]],[[96,320],[80,256],[384,384]],[[96,320],[384,384],[96,384]]]},{"polygons":[[[384,-352],[208,-352],[208,-368],[128,-368],[112,-384],[384,-384]],[[384,-160],[224,-160],[80,-160],[-16,-192],[-80,-256],[-96,-320],[-96,-384],[-384,-384],[-384,384],[384,384]]],"shapes":[[[128,-368],[112,-384],[384,-384],[208,-368]],[[208,-368],[384,-384],[384,-352],[208,-352]],[[384,-160],[384,384],[-384,384],[224,-160]],[[80,-160],[224,-160],[-384,384]],[[-16,-192],[80,-160],[-384,384]],[[-16,-192],[-384,384],[-384,-384],[-80,-256]],[[-96,-320],[-80,-256],[-384,-384]],[[-96,-320],[-384,-384],[-96,-384]]]},{"polygons":[[[384,352],[208,352],[208,368],[128,368],[112,384],[384,384]],[[384,160],[224,160],[80,160],[-16,192],[-80,256],[-96,320],[-96,384],[-384,384],[-384,-384],[384,-384]]],"shapes":[[[384,384],[112,384],[128,368],[208,368]],[[384,352],[384,384],[208,368],[208,352]],[[384,-384],[384,160],[224,160],[-384,-384]],[[-384,-384],[224,160],[80,160]],[[-384,-384],[80,160],[-16,192]],[[-384,384],[-384,-384],[-16,192],[-80,256]],[[-384,384],[-80,256],[-96,320]],[[-384,384],[-96,320],[-96,384]]]},{"polygons":[[[-384,96],[-288,96],[-176,80],[-96,48],[0,-16],[32,-64],[64,-128],[96,-240],[96,-384],[384,-384],[384,384],[-384,384]],[[-384,-112],[-240,-128],[-192,-144],[-160,-160],[-144,-192],[-128,-240],[-112,-336],[-112,-384],[-384,-384]]],"shapes":[[[-384,384],[-384,96],[-288,96],[384,384]],[[384,384],[-288,96],[-176,80]],[[384,384],[-176,80],[-96,48]],[[384,384],[-96,48],[0,-16]],[[384,-384],[384,384],[0,-16],[32,-64]],[[384,-384],[32,-64],[64,-128]],[[384,-384],[64,-128],[96,-240]],[[384,-384],[96,-240],[96,-384]],[[-384,-384],[-112,-384],[-112,-336],[-128,-240],[-144,-192],[-160,-160],[-192,-144],[-240,-128],[-384,-112]]]},{"polygons":[[[-96,-384],[-96,-288],[-80,-176],[-48,-96],[16,0],[64,32],[128,64],[240,96],[384,96],[384,384],[-384,384],[-384,-384]],[[112,-384],[128,-240],[144,-192],[160,-160],[192,-144],[240,-128],[336,-112],[384,-112],[384,-384]]],"shapes":[[[-384,-384],[-96,-384],[-96,-288],[-384,384]],[[-384,384],[-96,-288],[-80,-176]],[[-384,384],[-80,-176],[-48,-96]],[[-384,384],[-48,-96],[16,0]],[[384,384],[-384,384],[16,0],[64,32]],[[384,384],[64,32],[128,64]],[[384,384],[128,64],[240,96]],[[384,384],[240,96],[384,96]],[[384,-384],[384,-112],[336,-112],[240,-128],[192,-144],[160,-160],[144,-192],[128,-240],[112,-384]]]},{"polygons":[[[96,384],[96,288],[80,176],[48,96],[-16,0],[-64,-32],[-128,-64],[-240,-96],[-384,-96],[-384,-384],[384,-384],[384,384]],[[-112,384],[-128,240],[-144,192],[-160,160],[-192,144],[-240,128],[-336,112],[-384,112],[-384,384]]],"shapes":[[[384,384],[96,384],[96,288],[384,-384]],[[384,-384],[96,288],[80,176]],[[384,-384],[80,176],[48,96]],[[384,-384],[48,96],[-16,0]],[[-384,-384],[384,-384],[-16,0],[-64,-32]],[[-384,-384],[-64,-32],[-128,-64]],[[-384,-384],[-128,-64],[-240,-96]],[[-384,-384],[-240,-96],[-384,-96]],[[-384,384],[-384,112],[-336,112],[-240,128],[-192,144],[-160,160],[-144,192],[-128,240],[-112,384]]]},{"polygons":[[[384,-96],[288,-96],[176,-80],[96,-48],[0,16],[-32,64],[-64,128],[-96,240],[-96,384],[-384,384],[-384,-384],[384,-384]],[[384,112],[240,128],[192,144],[160,160],[144,192],[128,240],[112,336],[112,384],[384,384]]],"shapes":[[[384,-384],[384,-96],[288,-96],[-384,-384]],[[-384,-384],[288,-96],[176,-80]],[[-384,-384],[176,-80],[96,-48]],[[-384,-384],[96,-48],[0,16]],[[-384,384],[-384,-384],[0,16],[-32,64]],[[-384,384],[-32,64],[-64,128]],[[-384,384],[-64,128],[-96,240]],[[-384,384],[-96,240],[-96,384]],[[384,384],[112,384],[112,336],[128,240],[144,192],[160,160],[192,144],[240,128],[384,112]]]},{"polygons":[[[-384,-368],[-368,-368],[-368,-384],[-384,-384]],[[-160,-384],[-160,-288],[-176,-240],[-240,-176],[-304,-160],[-384,-160],[-384,384],[384,384],[384,-384]]],"shapes":[[[-384,-384],[-368,-384],[-368,-368],[-384,-368]],[[-160,-384],[384,-384],[384,384],[-160,-288]],[[-176,-240],[-160,-288],[384,384]],[[-176,-240],[384,384],[-384,384],[-240,-176]],[[-304,-160],[-240,-176],[-384,384]],[[-304,-160],[-384,384],[-384,-160]]]},{"polygons":[[[368,-384],[368,-368],[384,-368],[384,-384]],[[384,-160],[288,-160],[240,-176],[176,-240],[160,-304],[160,-384],[-384,-384],[-384,384],[384,384]]],"shapes":[[[384,-384],[384,-368],[368,-368],[368,-384]],[[384,-160],[384,384],[-384,384],[288,-160]],[[240,-176],[288,-160],[-384,384]],[[240,-176],[-384,384],[-384,-384],[176,-240]],[[160,-304],[176,-240],[-384,-384]],[[160,-304],[-384,-384],[160,-384]]]},{"polygons":[[[384,368],[368,368],[368,384],[384,384]],[[160,384],[160,288],[176,240],[240,176],[304,160],[384,160],[384,-384],[-384,-384],[-384,384]]],"shapes":[[[384,384],[368,384],[368,368],[384,368]],[[160,384],[-384,384],[-384,-384],[160,288]],[[176,240],[160,288],[-384,-384]],[[176,240],[-384,-384],[384,-384],[240,176]],[[304,160],[240,176],[384,-384]],[[304,160],[384,-384],[384,160]]]},{"polygons":[[[-368,384],[-368,368],[-384,368],[-384,384]],[[-384,160],[-288,160],[-240,176],[-176,240],[-160,304],[-160,384],[384,384],[384,-384],[-384,-384]]],"shapes":[[[-384,384],[-384,368],[-368,368],[-368,384]],[[-384,160],[-384,-384],[384,-384],[-288,160]],[[-240,176],[-288,160],[384,-384]],[[-240,176],[384,-384],[384,384],[-176,240]],[[-160,304],[-176,240],[384,384]],[[-160,304],[384,384],[-160,384]]]},{"polygons":[[[-384,-96],[-128,-96],[-20.5,-101.5],[-21.5,-120.5],[63,-129.5],[112,-144],[128,-160],[139,-230],[159,-229.5],[160,-384],[-384,-384]],[[-384,95],[-99.5,92.5],[107,74.5],[212.5,41.5],[291.5,-32],[336,-130],[349.5,-263],[350.5,-384],[384,-384],[384,384],[-384,384]]],"shapes":[[[160,-384],[159,-229.5],[139,-230],[-384,-384]],[[-128,-96],[-384,-96],[-384,-384],[139,-230],[128,-160],[-21.5,-120.5]],[[-21.5,-120.5],[-20.5,-101.5],[-128,-96]],[[63,-129.5],[-21.5,-120.5],[128,-160],[112,-144]],[[350.5,-384],[384,-384],[384,384],[349.5,-263]],[[336,-130],[349.5,-263],[384,384]],[[291.5,-32],[336,-130],[384,384]],[[212.5,41.5],[291.5,-32],[384,384]],[[212.5,41.5],[384,384],[-384,384],[107,74.5]],[[-99.5,92.5],[107,74.5],[-384,384]],[[-384,384],[-384,95],[-99.5,92.5]]]},{"polygons":[[[384,-96],[128,-96],[20.5,-101.5],[21.5,-120.5],[-63,-129.5],[-112,-144],[-128,-160],[-139,-230],[-159,-229.5],[-160,-384],[384,-384]],[[384,95],[99.5,92.5],[-107,74.5],[-212.5,41.5],[-291.5,-32],[-336,-130],[-349.5,-263],[-350.5,-384],[-384,-384],[-384,384],[384,384]]],"shapes":[[[-139,-230],[-159,-229.5],[-160,-384],[384,-384]],[[-128,-160],[-139,-230],[384,-384],[384,-96],[128,-96],[21.5,-120.5]],[[128,-96],[20.5,-101.5],[21.5,-120.5]],[[-128,-160],[21.5,-120.5],[-63,-129.5],[-112,-144]],[[384,95],[384,384],[-384,384],[99.5,92.5]],[[-107,74.5],[99.5,92.5],[-384,384]],[[-212.5,41.5],[-107,74.5],[-384,384]],[[-384,384],[-384,-384],[-350.5,-384],[-349.5,-263]],[[-384,384],[-349.5,-263],[-336,-130]],[[-384,384],[-336,-130],[-291.5,-32]],[[-212.5,41.5],[-384,384],[-291.5,-32]]]},{"polygons":[[[-384,96],[-128,96],[-20.5,101.5],[-21.5,120.5],[63,129.5],[112,144],[128,160],[139,230],[159,229.5],[160,384],[-384,384]],[[-384,-95],[-99.5,-92.5],[107,-74.5],[212.5,-41.5],[291.5,32],[336,130],[349.5,263],[350.5,384],[384,384],[384,-384],[-384,-384]]],"shapes":[[[139,230],[159,229.5],[160,384],[-384,384]],[[128,160],[139,230],[-384,384],[-384,96],[-128,96],[-21.5,120.5]],[[-128,96],[-20.5,101.5],[-21.5,120.5]],[[128,160],[-21.5,120.5],[63,129.5],[112,144]],[[-384,-95],[-384,-384],[384,-384],[-99.5,-92.5]],[[107,-74.5],[-99.5,-92.5],[384,-384]],[[212.5,-41.5],[107,-74.5],[384,-384]],[[384,-384],[384,384],[350.5,384],[349.5,263]],[[384,-384],[349.5,263],[336,130]],[[384,-384],[336,130],[291.5,32]],[[212.5,-41.5],[384,-384],[291.5,32]]]},{"polygons":[[[-96,-384],[-96,-128],[-101.5,-20.5],[-120.5,-21.5],[-129.5,63],[-144,112],[-160,128],[-230,139],[-229.5,159],[-384,160],[-384,-384]],[[95,-384],[92.5,-99.5],[74.5,107],[41.5,212.5],[-32,291.5],[-130,336],[-263,349.5],[-384,350.5],[-384,384],[384,384],[384,-384]]],"shapes":[[[-230,139],[-229.5,159],[-384,160],[-384,-384]],[[-160,128],[-230,139],[-384,-384],[-96,-384],[-96,-128],[-120.5,-21.5]],[[-96,-128],[-101.5,-20.5],[-120.5,-21.5]],[[-160,128],[-120.5,-21.5],[-129.5,63],[-144,112]],[[95,-384],[384,-384],[384,384],[92.5,-99.5]],[[74.5,107],[92.5,-99.5],[384,384]],[[41.5,212.5],[74.5,107],[384,384]],[[384,384],[-384,384],[-384,350.5],[-263,349.5]],[[384,384],[-263,349.5],[-130,336]],[[384,384],[-130,336],[-32,291.5]],[[41.5,212.5],[384,384],[-32,291.5]]]},{"polygons":[[[384,96],[128,96],[20.5,101.5],[21.5,120.5],[-63,129.5],[-112,144],[-128,160],[-139,230],[-159,229.5],[-160,384],[384,384]],[[384,-95],[99.5,-92.5],[-107,-74.5],[-212.5,-41.5],[-291.5,32],[-336,130],[-349.5,263],[-350.5,384],[-384,384],[-384,-384],[384,-384]]],"shapes":[[[-160,384],[-159,229.5],[-139,230],[384,384]],[[128,96],[384,96],[384,384],[-139,230],[-128,160],[21.5,120.5]],[[21.5,120.5],[20.5,101.5],[128,96]],[[-63,129.5],[21.5,120.5],[-128,160],[-112,144]],[[-350.5,384],[-384,384],[-384,-384],[-349.5,263]],[[-336,130],[-349.5,263],[-384,-384]],[[-291.5,32],[-336,130],[-384,-384]],[[-212.5,-41.5],[-291.5,32],[-384,-384]],[[-212.5,-41.5],[-384,-384],[384,-384],[-107,-74.5]],[[99.5,-92.5],[-107,-74.5],[384,-384]],[[384,-384],[384,-95],[99.5,-92.5]]]},{"polygons":[[[-96,384],[-96,128],[-101.5,20.5],[-120.5,21.5],[-129.5,-63],[-144,-112],[-160,-128],[-230,-139],[-229.5,-159],[-384,-160],[-384,384]],[[95,384],[92.5,99.5],[74.5,-107],[41.5,-212.5],[-32,-291.5],[-130,-336],[-263,-349.5],[-384,-350.5],[-384,-384],[384,-384],[384,384]]],"shapes":[[[-384,-160],[-229.5,-159],[-230,-139],[-384,384]],[[-96,128],[-96,384],[-384,384],[-230,-139],[-160,-128],[-120.5,21.5]],[[-120.5,21.5],[-101.5,20.5],[-96,128]],[[-129.5,-63],[-120.5,21.5],[-160,-128],[-144,-112]],[[-384,-350.5],[-384,-384],[384,-384],[-263,-349.5]],[[-130,-336],[-263,-349.5],[384,-384]],[[-32,-291.5],[-130,-336],[384,-384]],[[41.5,-212.5],[-32,-291.5],[384,-384]],[[41.5,-212.5],[384,-384],[384,384],[74.5,-107]],[[92.5,99.5],[74.5,-107],[384,384]],[[384,384],[95,384],[92.5,99.5]]]},{"polygons":[[[96,-384],[96,-128],[101.5,-20.5],[120.5,-21.5],[129.5,63],[144,112],[160,128],[230,139],[229.5,159],[384,160],[384,-384]],[[-95,-384],[-92.5,-99.5],[-74.5,107],[-41.5,212.5],[32,291.5],[130,336],[263,349.5],[384,350.5],[384,384],[-384,384],[-384,-384]]],"shapes":[[[384,160],[229.5,159],[230,139],[384,-384]],[[96,-128],[96,-384],[384,-384],[230,139],[160,128],[120.5,-21.5]],[[120.5,-21.5],[101.5,-20.5],[96,-128]],[[129.5,63],[120.5,-21.5],[160,128],[144,112]],[[384,350.5],[384,384],[-384,384],[263,349.5]],[[130,336],[263,349.5],[-384,384]],[[32,291.5],[130,336],[-384,384]],[[-41.5,212.5],[32,291.5],[-384,384]],[[-41.5,212.5],[-384,384],[-384,-384],[-74.5,107]],[[-92.5,-99.5],[-74.5,107],[-384,-384]],[[-384,-384],[-95,-384],[-92.5,-99.5]]]},{"polygons":[[[96,384],[96,128],[101.5,20.5],[120.5,21.5],[129.5,-63],[144,-112],[160,-128],[230,-139],[229.5,-159],[384,-160],[384,384]],[[-95,384],[-92.5,99.5],[-74.5,-107],[-41.5,-212.5],[32,-291.5],[130,-336],[263,-349.5],[384,-350.5],[384,-384],[-384,-384],[-384,384]]],"shapes":[[[230,-139],[229.5,-159],[384,-160],[384,384]],[[160,-128],[230,-139],[384,384],[96,384],[96,128],[120.5,21.5]],[[96,128],[101.5,20.5],[120.5,21.5]],[[160,-128],[120.5,21.5],[129.5,-63],[144,-112]],[[-95,384],[-384,384],[-384,-384],[-92.5,99.5]],[[-74.5,-107],[-92.5,99.5],[-384,-384]],[[-41.5,-212.5],[-74.5,-107],[-384,-384]],[[-384,-384],[384,-384],[384,-350.5],[263,-349.5]],[[-384,-384],[263,-349.5],[130,-336]],[[-384,-384],[130,-336],[32,-291.5]],[[-41.5,-212.5],[-384,-384],[32,-291.5]]]},{"polygons":[[[-384,-96],[384,-96],[384,-384],[-384,-384]],[[-384,96],[384,96],[384,384],[-384,384]]],"shapes":[[[-384,-384],[384,-384],[384,-96],[-384,-96]],[[-384,96],[384,96],[384,384],[-384,384]]]},{"polygons":[[[96,-384],[96,384],[384,384],[384,-384]],[[-96,-384],[-96,384],[-384,384],[-384,-384]]],"shapes":[[[384,-384],[384,384],[96,384],[96,-384]],[[-96,-384],[-96,384],[-384,384],[-384,-384]]]},{"polygons":[[[-384,-96],[-384,-384],[384,-384],[384,-352],[274,-348],[140.5,-318.5],[16,-256],[-104.5,-173.5],[-230,-111.5]],[[-384,96],[-215,81.5],[-79,33.5],[49.5,-47],[175,-121.5],[291,-152],[384,-160],[384,384],[-384,384]]],"shapes":[[[-230,-111.5],[-384,-96],[-384,-384],[16,-256],[-104.5,-173.5]],[[-384,-384],[384,-384],[384,-352],[274,-348]],[[-384,-384],[274,-348],[140.5,-318.5]],[[-384,-384],[140.5,-318.5],[16,-256]],[[49.5,-47],[175,-121.5],[291,-152],[384,-160],[384,384],[-384,384],[-79,33.5]],[[-215,81.5],[-79,33.5],[-384,384]],[[-384,384],[-384,96],[-215,81.5]]]},{"polygons":[[[384,-96],[384,-384],[-384,-384],[-384,-352],[-274,-348],[-140.5,-318.5],[-16,-256],[104.5,-173.5],[230,-111.5]],[[384,96],[215,81.5],[79,33.5],[-49.5,-47],[-175,-121.5],[-291,-152],[-384,-160],[-384,384],[384,384]]],"shapes":[[[-16,-256],[384,-384],[384,-96],[230,-111.5],[104.5,-173.5]],[[-384,-352],[-384,-384],[384,-384],[-274,-348]],[[-140.5,-318.5],[-274,-348],[384,-384]],[[-16,-256],[-140.5,-318.5],[384,-384]],[[384,384],[-384,384],[-384,-160],[-291,-152],[-175,-121.5],[-49.5,-47],[79,33.5]],[[384,384],[79,33.5],[215,81.5]],[[384,96],[384,384],[215,81.5]]]},{"polygons":[[[-384,96],[-384,384],[384,384],[384,352],[274,348],[140.5,318.5],[16,256],[-104.5,173.5],[-230,111.5]],[[-384,-96],[-215,-81.5],[-79,-33.5],[49.5,47],[175,121.5],[291,152],[384,160],[384,-384],[-384,-384]]],"shapes":[[[16,256],[-384,384],[-384,96],[-230,111.5],[-104.5,173.5]],[[384,352],[384,384],[-384,384],[274,348]],[[140.5,318.5],[274,348],[-384,384]],[[16,256],[140.5,318.5],[-384,384]],[[-384,-384],[384,-384],[384,160],[291,152],[175,121.5],[49.5,47],[-79,-33.5]],[[-384,-384],[-79,-33.5],[-215,-81.5]],[[-384,-96],[-384,-384],[-215,-81.5]]]},{"polygons":[[[-96,-384],[-384,-384],[-384,384],[-352,384],[-348,274],[-318.5,140.5],[-256,16],[-173.5,-104.5],[-111.5,-230]],[[96,-384],[81.5,-215],[33.5,-79],[-47,49.5],[-121.5,175],[-152,291],[-160,384],[384,384],[384,-384]]],"shapes":[[[-256,16],[-384,-384],[-96,-384],[-111.5,-230],[-173.5,-104.5]],[[-352,384],[-384,384],[-384,-384],[-348,274]],[[-318.5,140.5],[-348,274],[-384,-384]],[[-256,16],[-318.5,140.5],[-384,-384]],[[384,-384],[384,384],[-160,384],[-152,291],[-121.5,175],[-47,49.5],[33.5,-79]],[[384,-384],[33.5,-79],[81.5,-215]],[[96,-384],[384,-384],[81.5,-215]]]},{"polygons":[[[384,96],[384,384],[-384,384],[-384,352],[-274,348],[-140.5,318.5],[-16,256],[104.5,173.5],[230,111.5]],[[384,-96],[215,-81.5],[79,-33.5],[-49.5,47],[-175,121.5],[-291,152],[-384,160],[-384,-384],[384,-384]]],"shapes":[[[230,111.5],[384,96],[384,384],[-16,256],[104.5,173.5]],[[384,384],[-384,384],[-384,352],[-274,348]],[[384,384],[-274,348],[-140.5,318.5]],[[384,384],[-140.5,318.5],[-16,256]],[[-49.5,47],[-175,121.5],[-291,152],[-384,160],[-384,-384],[384,-384],[79,-33.5]],[[215,-81.5],[79,-33.5],[384,-384]],[[384,-384],[384,-96],[215,-81.5]]]},{"polygons":[[[-96,384],[-384,384],[-384,-384],[-352,-384],[-348,-274],[-318.5,-140.5],[-256,-16],[-173.5,104.5],[-111.5,230]],[[96,384],[81.5,215],[33.5,79],[-47,-49.5],[-121.5,-175],[-152,-291],[-160,-384],[384,-384],[384,384]]],"shapes":[[[-111.5,230],[-96,384],[-384,384],[-256,-16],[-173.5,104.5]],[[-384,384],[-384,-384],[-352,-384],[-348,-274]],[[-384,384],[-348,-274],[-318.5,-140.5]],[[-384,384],[-318.5,-140.5],[-256,-16]],[[-47,-49.5],[-121.5,-175],[-152,-291],[-160,-384],[384,-384],[384,384],[33.5,79]],[[81.5,215],[33.5,79],[384,384]],[[384,384],[96,384],[81.5,215]]]},{"polygons":[[[96,-384],[384,-384],[384,384],[352,384],[348,274],[318.5,140.5],[256,16],[173.5,-104.5],[111.5,-230]],[[-96,-384],[-81.5,-215],[-33.5,-79],[47,49.5],[121.5,175],[152,291],[160,384],[-384,384],[-384,-384]]],"shapes":[[[111.5,-230],[96,-384],[384,-384],[256,16],[173.5,-104.5]],[[384,-384],[384,384],[352,384],[348,274]],[[384,-384],[348,274],[318.5,140.5]],[[384,-384],[318.5,140.5],[256,16]],[[47,49.5],[121.5,175],[152,291],[160,384],[-384,384],[-384,-384],[-33.5,-79]],[[-81.5,-215],[-33.5,-79],[-384,-384]],[[-384,-384],[-96,-384],[-81.5,-215]]]},{"polygons":[[[96,384],[384,384],[384,-384],[352,-384],[348,-274],[318.5,-140.5],[256,-16],[173.5,104.5],[111.5,230]],[[-96,384],[-81.5,215],[-33.5,79],[47,-49.5],[121.5,-175],[152,-291],[160,-384],[-384,-384],[-384,384]]],"shapes":[[[256,-16],[384,384],[96,384],[111.5,230],[173.5,104.5]],[[352,-384],[384,-384],[384,384],[348,-274]],[[318.5,-140.5],[348,-274],[384,384]],[[256,-16],[318.5,-140.5],[384,384]],[[-384,384],[-384,-384],[160,-384],[152,-291],[121.5,-175],[47,-49.5],[-33.5,79]],[[-384,384],[-33.5,79],[-81.5,215]],[[-96,384],[-384,384],[-81.5,215]]]},{"polygons":[[[-384,-352],[-320,-352],[-248.5,-327.5],[-194,-265],[-170,-159],[-161.5,65.5],[-160,384],[384,384],[384,-384],[-384,-384]],[[-384,-140],[-376.5,-134.5],[-375.5,-118.5],[-357,-118],[-352.5,77.5],[-352,384],[-384,384]]],"shapes":[[[-384,-352],[-384,-384],[384,-384],[-320,-352]],[[-248.5,-327.5],[-320,-352],[384,-384]],[[-194,-265],[-248.5,-327.5],[384,-384],[384,384]],[[-170,-159],[-194,-265],[384,384]],[[-161.5,65.5],[-170,-159],[384,384]],[[-161.5,65.5],[384,384],[-160,384]],[[-384,384],[-384,-140],[-376.5,-134.5],[-375.5,-118.5],[-352,384]],[[-352.5,77.5],[-352,384],[-375.5,-118.5],[-357,-118]]]},{"polygons":[[[384,-352],[320,-352],[248.5,-327.5],[194,-265],[170,-159],[161.5,65.5],[160,384],[-384,384],[-384,-384],[384,-384]],[[384,-140],[376.5,-134.5],[375.5,-118.5],[357,-118],[352.5,77.5],[352,384],[384,384]]],"shapes":[[[384,-384],[384,-352],[320,-352],[-384,-384]],[[-384,-384],[320,-352],[248.5,-327.5]],[[-384,-384],[248.5,-327.5],[194,-265],[-384,384]],[[-384,384],[194,-265],[170,-159]],[[-384,384],[170,-159],[161.5,65.5]],[[-384,384],[161.5,65.5],[160,384]],[[375.5,-118.5],[376.5,-134.5],[384,-140],[384,384],[352,384]],[[375.5,-118.5],[352,384],[352.5,77.5],[357,-118]]]},{"polygons":[[[-384,352],[-320,352],[-248.5,327.5],[-194,265],[-170,159],[-161.5,-65.5],[-160,-384],[384,-384],[384,384],[-384,384]],[[-384,140],[-376.5,134.5],[-375.5,118.5],[-357,118],[-352.5,-77.5],[-352,-384],[-384,-384]]],"shapes":[[[-384,384],[-384,352],[-320,352],[384,384]],[[384,384],[-320,352],[-248.5,327.5]],[[384,384],[-248.5,327.5],[-194,265],[384,-384]],[[384,-384],[-194,265],[-170,159]],[[384,-384],[-170,159],[-161.5,-65.5]],[[384,-384],[-161.5,-65.5],[-160,-384]],[[-375.5,118.5],[-376.5,134.5],[-384,140],[-384,-384],[-352,-384]],[[-375.5,118.5],[-352,-384],[-352.5,-77.5],[-357,118]]]},{"polygons":[[[-352,-384],[-352,-320],[-327.5,-248.5],[-265,-194],[-159,-170],[65.5,-161.5],[384,-160],[384,384],[-384,384],[-384,-384]],[[-140,-384],[-134.5,-376.5],[-118.5,-375.5],[-118,-357],[77.5,-352.5],[384,-352],[384,-384]]],"shapes":[[[-384,-384],[-352,-384],[-352,-320],[-384,384]],[[-384,384],[-352,-320],[-327.5,-248.5]],[[-384,384],[-327.5,-248.5],[-265,-194],[384,384]],[[384,384],[-265,-194],[-159,-170]],[[384,384],[-159,-170],[65.5,-161.5]],[[384,384],[65.5,-161.5],[384,-160]],[[-118.5,-375.5],[-134.5,-376.5],[-140,-384],[384,-384],[384,-352]],[[-118.5,-375.5],[384,-352],[77.5,-352.5],[-118,-357]]]},{"polygons":[[[384,352],[320,352],[248.5,327.5],[194,265],[170,159],[161.5,-65.5],[160,-384],[-384,-384],[-384,384],[384,384]],[[384,140],[376.5,134.5],[375.5,118.5],[357,118],[352.5,-77.5],[352,-384],[384,-384]]],"shapes":[[[384,352],[384,384],[-384,384],[320,352]],[[248.5,327.5],[320,352],[-384,384]],[[194,265],[248.5,327.5],[-384,384],[-384,-384]],[[170,159],[194,265],[-384,-384]],[[161.5,-65.5],[170,159],[-384,-384]],[[161.5,-65.5],[-384,-384],[160,-384]],[[384,-384],[384,140],[376.5,134.5],[375.5,118.5],[352,-384]],[[352.5,-77.5],[352,-384],[375.5,118.5],[357,118]]]},{"polygons":[[[-352,384],[-352,320],[-327.5,248.5],[-265,194],[-159,170],[65.5,161.5],[384,160],[384,-384],[-384,-384],[-384,384]],[[-140,384],[-134.5,376.5],[-118.5,375.5],[-118,357],[77.5,352.5],[384,352],[384,384]]],"shapes":[[[-352,384],[-384,384],[-384,-384],[-352,320]],[[-327.5,248.5],[-352,320],[-384,-384]],[[-265,194],[-327.5,248.5],[-384,-384],[384,-384]],[[-159,170],[-265,194],[384,-384]],[[65.5,161.5],[-159,170],[384,-384]],[[65.5,161.5],[384,-384],[384,160]],[[384,384],[-140,384],[-134.5,376.5],[-118.5,375.5],[384,352]],[[77.5,352.5],[384,352],[-118.5,375.5],[-118,357]]]},{"polygons":[[[352,-384],[352,-320],[327.5,-248.5],[265,-194],[159,-170],[-65.5,-161.5],[-384,-160],[-384,384],[384,384],[384,-384]],[[140,-384],[134.5,-376.5],[118.5,-375.5],[118,-357],[-77.5,-352.5],[-384,-352],[-384,-384]]],"shapes":[[[352,-384],[384,-384],[384,384],[352,-320]],[[327.5,-248.5],[352,-320],[384,384]],[[265,-194],[327.5,-248.5],[384,384],[-384,384]],[[159,-170],[265,-194],[-384,384]],[[-65.5,-161.5],[159,-170],[-384,384]],[[-65.5,-161.5],[-384,384],[-384,-160]],[[-384,-384],[140,-384],[134.5,-376.5],[118.5,-375.5],[-384,-352]],[[-77.5,-352.5],[-384,-352],[118.5,-375.5],[118,-357]]]},{"polygons":[[[352,384],[352,320],[327.5,248.5],[265,194],[159,170],[-65.5,161.5],[-384,160],[-384,-384],[384,-384],[384,384]],[[140,384],[134.5,376.5],[118.5,375.5],[118,357],[-77.5,352.5],[-384,352],[-384,384]]],"shapes":[[[384,384],[352,384],[352,320],[384,-384]],[[384,-384],[352,320],[327.5,248.5]],[[384,-384],[327.5,248.5],[265,194],[-384,-384]],[[-384,-384],[265,194],[159,170]],[[-384,-384],[159,170],[-65.5,161.5]],[[-384,-384],[-65.5,161.5],[-384,160]],[[118.5,375.5],[134.5,376.5],[140,384],[-384,384],[-384,352]],[[118.5,375.5],[-384,352],[-77.5,352.5],[118,357]]]},{"polygons":[[[-384,-384],[384,-384],[384,384],[352,384],[351,273],[313,80],[218.5,-109],[76,-242.5],[-96,-320],[-288,-352],[-384,-352]],[[-384,-138],[-384,384],[139,384],[140.5,318],[113,165.5],[50,31],[-61,-67.5],[-224,-128]]],"shapes":[[[-384,-352],[-384,-384],[384,-384],[-288,-352]],[[-96,-320],[-288,-352],[384,-384]],[[76,-242.5],[-96,-320],[384,-384]],[[218.5,-109],[76,-242.5],[384,-384]],[[384,-384],[384,384],[352,384],[351,273]],[[384,-384],[351,273],[313,80]],[[218.5,-109],[384,-384],[313,80]],[[-224,-128],[-61,-67.5],[50,31],[113,165.5],[140.5,318],[139,384],[-384,384],[-384,-138]]]},{"polygons":[[[384,-384],[384,384],[-384,384],[-384,352],[-273,351],[-80,313],[109,218.5],[242.5,76],[320,-96],[352,-288],[352,-384]],[[138,-384],[-384,-384],[-384,139],[-318,140.5],[-165.5,113],[-31,50],[67.5,-61],[128,-224]]],"shapes":[[[352,-384],[384,-384],[384,384],[352,-288]],[[320,-96],[352,-288],[384,384]],[[242.5,76],[320,-96],[384,384]],[[109,218.5],[242.5,76],[384,384]],[[384,384],[-384,384],[-384,352],[-273,351]],[[384,384],[-273,351],[-80,313]],[[109,218.5],[384,384],[-80,313]],[[128,-224],[67.5,-61],[-31,50],[-165.5,113],[-318,140.5],[-384,139],[-384,-384],[138,-384]]]},{"polygons":[[[384,384],[-384,384],[-384,-384],[-352,-384],[-351,-273],[-313,-80],[-218.5,109],[-76,242.5],[96,320],[288,352],[384,352]],[[384,138],[384,-384],[-139,-384],[-140.5,-318],[-113,-165.5],[-50,-31],[61,67.5],[224,128]]],"shapes":[[[384,352],[384,384],[-384,384],[288,352]],[[96,320],[288,352],[-384,384]],[[-76,242.5],[96,320],[-384,384]],[[-218.5,109],[-76,242.5],[-384,384]],[[-384,384],[-384,-384],[-352,-384],[-351,-273]],[[-384,384],[-351,-273],[-313,-80]],[[-218.5,109],[-384,384],[-313,-80]],[[224,128],[61,67.5],[-50,-31],[-113,-165.5],[-140.5,-318],[-139,-384],[384,-384],[384,138]]]},{"polygons":[[[-384,384],[-384,-384],[384,-384],[384,-352],[273,-351],[80,-313],[-109,-218.5],[-242.5,-76],[-320,96],[-352,288],[-352,384]],[[-138,384],[384,384],[384,-139],[318,-140.5],[165.5,-113],[31,-50],[-67.5,61],[-128,224]]],"shapes":[[[-352,384],[-384,384],[-384,-384],[-352,288]],[[-320,96],[-352,288],[-384,-384]],[[-242.5,-76],[-320,96],[-384,-384]],[[-109,-218.5],[-242.5,-76],[-384,-384]],[[-384,-384],[384,-384],[384,-352],[273,-351]],[[-384,-384],[273,-351],[80,-313]],[[-109,-218.5],[-384,-384],[80,-313]],[[-128,224],[-67.5,61],[31,-50],[165.5,-113],[318,-140.5],[384,-139],[384,384],[-138,384]]]},{"polygons":[[[-384,-352],[-352,-352],[-193.5,-315],[-59,-224],[74.5,-64.5],[93.5,-70.5],[163,19.5],[204.5,65.5],[288,123.5],[349,138],[384,140.5],[384,-384],[-384,-384]],[[-384,-140.5],[-307,-132.5],[-243,-102],[-163.5,-26],[-92.5,71],[-74.5,60],[19.5,178],[137.5,284],[260,338.5],[384,352],[384,384],[-384,384]]],"shapes":[[[384,-384],[384,140.5],[349,138],[288,123.5],[204.5,65.5],[163,19.5],[93.5,-70.5]],[[-384,-352],[-384,-384],[384,-384],[-352,-352]],[[-193.5,-315],[-352,-352],[384,-384]],[[-59,-224],[-193.5,-315],[384,-384]],[[-59,-224],[384,-384],[93.5,-70.5],[74.5,-64.5]],[[-384,384],[-384,-140.5],[-307,-132.5],[-243,-102],[-163.5,-26],[-92.5,71]],[[384,352],[384,384],[-384,384],[260,338.5]],[[137.5,284],[260,338.5],[-384,384]],[[19.5,178],[137.5,284],[-384,384]],[[19.5,178],[-384,384],[-92.5,71],[-74.5,60]]]},{"polygons":[[[384,-352],[352,-352],[193.5,-315],[59,-224],[-74.5,-64.5],[-93.5,-70.5],[-163,19.5],[-204.5,65.5],[-288,123.5],[-349,138],[-384,140.5],[-384,-384],[384,-384]],[[384,-140.5],[307,-132.5],[243,-102],[163.5,-26],[92.5,71],[74.5,60],[-19.5,178],[-137.5,284],[-260,338.5],[-384,352],[-384,384],[384,384]]],"shapes":[[[-163,19.5],[-204.5,65.5],[-288,123.5],[-349,138],[-384,140.5],[-384,-384],[-93.5,-70.5]],[[384,-384],[384,-352],[352,-352],[-384,-384]],[[-384,-384],[352,-352],[193.5,-315]],[[-384,-384],[193.5,-315],[59,-224]],[[-93.5,-70.5],[-384,-384],[59,-224],[-74.5,-64.5]],[[163.5,-26],[243,-102],[307,-132.5],[384,-140.5],[384,384],[92.5,71]],[[384,384],[-384,384],[-384,352],[-260,338.5]],[[384,384],[-260,338.5],[-137.5,284]],[[384,384],[-137.5,284],[-19.5,178]],[[92.5,71],[384,384],[-19.5,178],[74.5,60]]]},{"polygons":[[[352,-384],[352,-352],[315,-193.5],[224,-59],[64.5,74.5],[70.5,93.5],[-19.5,163],[-65.5,204.5],[-123.5,288],[-138,349],[-140.5,384],[384,384],[384,-384]],[[140.5,-384],[132.5,-307],[102,-243],[26,-163.5],[-71,-92.5],[-60,-74.5],[-178,19.5],[-284,137.5],[-338.5,260],[-352,384],[-384,384],[-384,-384]]],"shapes":[[[384,384],[-140.5,384],[-138,349],[-123.5,288],[-65.5,204.5],[-19.5,163],[70.5,93.5]],[[352,-384],[384,-384],[384,384],[352,-352]],[[315,-193.5],[352,-352],[384,384]],[[224,-59],[315,-193.5],[384,384]],[[224,-59],[384,384],[70.5,93.5],[64.5,74.5]],[[-384,-384],[140.5,-384],[132.5,-307],[102,-243],[26,-163.5],[-71,-92.5]],[[-352,384],[-384,384],[-384,-384],[-338.5,260]],[[-284,137.5],[-338.5,260],[-384,-384]],[[-178,19.5],[-284,137.5],[-384,-384]],[[-178,19.5],[-384,-384],[-71,-92.5],[-60,-74.5]]]},{"polygons":[[[-352,-384],[-352,-352],[-315,-193.5],[-224,-59],[-64.5,74.5],[-70.5,93.5],[19.5,163],[65.5,204.5],[123.5,288],[138,349],[140.5,384],[-384,384],[-384,-384]],[[-140.5,-384],[-132.5,-307],[-102,-243],[-26,-163.5],[71,-92.5],[60,-74.5],[178,19.5],[284,137.5],[338.5,260],[352,384],[384,384],[384,-384]]],"shapes":[[[19.5,163],[65.5,204.5],[123.5,288],[138,349],[140.5,384],[-384,384],[-70.5,93.5]],[[-384,-384],[-352,-384],[-352,-352],[-384,384]],[[-384,384],[-352,-352],[-315,-193.5]],[[-384,384],[-315,-193.5],[-224,-59]],[[-70.5,93.5],[-384,384],[-224,-59],[-64.5,74.5]],[[-26,-163.5],[-102,-243],[-132.5,-307],[-140.5,-384],[384,-384],[71,-92.5]],[[384,-384],[384,384],[352,384],[338.5,260]],[[384,-384],[338.5,260],[284,137.5]],[[384,-384],[284,137.5],[178,19.5]],[[71,-92.5],[384,-384],[178,19.5],[60,-74.5]]]},{"polygons":[[[-384,-352],[-384,-384],[384,-384],[384,-352]],[[-384,-160],[-384,384],[384,384],[384,-160]]],"shapes":[[[-384,-352],[-384,-384],[384,-384],[384,-352]],[[384,-160],[384,384],[-384,384],[-384,-160]]]},{"polygons":[[[384,352],[384,384],[-384,384],[-384,352]],[[384,160],[384,-384],[-384,-384],[-384,160]]],"shapes":[[[384,352],[384,384],[-384,384],[-384,352]],[[-384,160],[-384,-384],[384,-384],[384,160]]]},{"polygons":[[[-352,384],[-384,384],[-384,-384],[-352,-384]],[[-160,384],[384,384],[384,-384],[-160,-384]]],"shapes":[[[-352,384],[-384,384],[-384,-384],[-352,-384]],[[-160,-384],[384,-384],[384,384],[-160,384]]]},{"polygons":[[[352,-384],[384,-384],[384,384],[352,384]],[[160,-384],[-384,-384],[-384,384],[160,384]]],"shapes":[[[352,-384],[384,-384],[384,384],[352,384]],[[160,384],[-384,384],[-384,-384],[160,-384]]]},{"polygons":[[[360,136],[346.95098876953125,124.73200225830078],[240,272],[132,384],[384,384],[384,112]],[[-132,384],[48,192],[252,-48],[300,-84],[348,-96],[384,-96],[384,-384],[-384,-384],[-384,384]]],"shapes":[[[360,136],[384,112],[384,384],[240,272]],[[384,384],[132,384],[240,272]],[[360,136],[240,272],[346.95098876953125,124.73200225830078]],[[48,192],[-132,384],[-384,384],[-384,-384],[384,-384],[252,-48]],[[300,-84],[252,-48],[384,-384]],[[348,-96],[300,-84],[384,-384]],[[348,-96],[384,-384],[384,-96]]]},{"polygons":[[[-136,360],[-124.73200225830078,346.95098876953125],[-272,240],[-384,132],[-384,384],[-112,384]],[[-384,-132],[-192,48],[48,252],[84,300],[96,348],[96,384],[384,384],[384,-384],[-384,-384]]],"shapes":[[[-136,360],[-112,384],[-384,384],[-272,240]],[[-384,384],[-384,132],[-272,240]],[[-136,360],[-272,240],[-124.73200225830078,346.95098876953125]],[[-192,48],[-384,-132],[-384,-384],[384,-384],[384,384],[48,252]],[[84,300],[48,252],[384,384]],[[96,348],[84,300],[384,384]],[[96,348],[384,384],[96,384]]]},{"polygons":[[[-360,-136],[-346.95098876953125,-124.73200225830078],[-240,-272],[-132,-384],[-384,-384],[-384,-112]],[[132,-384],[-48,-192],[-252,48],[-300,84],[-348,96],[-384,96],[-384,384],[384,384],[384,-384]]],"shapes":[[[-360,-136],[-384,-112],[-384,-384],[-240,-272]],[[-384,-384],[-132,-384],[-240,-272]],[[-360,-136],[-240,-272],[-346.95098876953125,-124.73200225830078]],[[-48,-192],[132,-384],[384,-384],[384,384],[-384,384],[-252,48]],[[-300,84],[-252,48],[-384,384]],[[-348,96],[-300,84],[-384,384]],[[-348,96],[-384,384],[-384,96]]]},{"polygons":[[[136,-360],[124.73200225830078,-346.95098876953125],[272,-240],[384,-132],[384,-384],[112,-384]],[[384,132],[192,-48],[-48,-252],[-84,-300],[-96,-348],[-96,-384],[-384,-384],[-384,384],[384,384]]],"shapes":[[[136,-360],[112,-384],[384,-384],[272,-240]],[[384,-384],[384,-132],[272,-240]],[[136,-360],[272,-240],[124.73200225830078,-346.95098876953125]],[[192,-48],[384,132],[384,384],[-384,384],[-384,-384],[-48,-252]],[[-84,-300],[-48,-252],[-384,-384]],[[-96,-348],[-84,-300],[-384,-384]],[[-96,-348],[-384,-384],[-96,-384]]]},{"polygons":[[[360,-136],[346.95098876953125,-124.73200225830078],[240,-272],[132,-384],[384,-384],[384,-112]],[[-132,-384],[48,-192],[252,48],[300,84],[348,96],[384,96],[384,384],[-384,384],[-384,-384]]],"shapes":[[[384,-384],[384,-112],[360,-136],[240,-272]],[[240,-272],[132,-384],[384,-384]],[[240,-272],[360,-136],[346.95098876953125,-124.73200225830078]],[[348,96],[384,96],[384,384],[-384,384]],[[300,84],[348,96],[-384,384]],[[252,48],[300,84],[-384,384],[-384,-384]],[[48,-192],[252,48],[-384,-384],[-132,-384]]]},{"polygons":[[[-136,-360],[-124.73200225830078,-346.95098876953125],[-272,-240],[-384,-132],[-384,-384],[-112,-384]],[[-384,132],[-192,-48],[48,-252],[84,-300],[96,-348],[96,-384],[384,-384],[384,384],[-384,384]]],"shapes":[[[-384,-384],[-112,-384],[-136,-360],[-272,-240]],[[-272,-240],[-384,-132],[-384,-384]],[[-272,-240],[-136,-360],[-124.73200225830078,-346.95098876953125]],[[96,-348],[96,-384],[384,-384],[384,384]],[[84,-300],[96,-348],[384,384]],[[48,-252],[84,-300],[384,384],[-384,384]],[[-192,-48],[48,-252],[-384,384],[-384,132]]]},{"polygons":[[[-360,136],[-346.95098876953125,124.73200225830078],[-240,272],[-132,384],[-384,384],[-384,112]],[[132,384],[-48,192],[-252,-48],[-300,-84],[-348,-96],[-384,-96],[-384,-384],[384,-384],[384,384]]],"shapes":[[[-384,384],[-384,112],[-360,136],[-240,272]],[[-240,272],[-132,384],[-384,384]],[[-240,272],[-360,136],[-346.95098876953125,124.73200225830078]],[[-348,-96],[-384,-96],[-384,-384],[384,-384]],[[-300,-84],[-348,-96],[384,-384]],[[-252,-48],[-300,-84],[384,-384],[384,384]],[[-48,192],[-252,-48],[384,384],[132,384]]]},{"polygons":[[[136,360],[124.73200225830078,346.95098876953125],[272,240],[384,132],[384,384],[112,384]],[[384,-132],[192,48],[-48,252],[-84,300],[-96,348],[-96,384],[-384,384],[-384,-384],[384,-384]]],"shapes":[[[384,384],[112,384],[136,360],[272,240]],[[272,240],[384,132],[384,384]],[[272,240],[136,360],[124.73200225830078,346.95098876953125]],[[-96,348],[-96,384],[-384,384],[-384,-384]],[[-84,300],[-96,348],[-384,-384]],[[-48,252],[-84,300],[-384,-384],[384,-384]],[[192,48],[-48,252],[384,-384],[384,-132]]]},{"polygons":[[[-384,-132],[-132,-384],[-384,-384]],[[132,-384],[-384,132],[-384,384],[384,384],[384,-384]]],"shapes":[[[-384,-384],[-132,-384],[-384,-132]],[[384,-384],[384,384],[-384,384],[-384,132],[132,-384]]]},{"polygons":[[[132,-384],[384,-132],[384,-384]],[[384,132],[-132,-384],[-384,-384],[-384,384],[384,384]]],"shapes":[[[384,-384],[384,-132],[132,-384]],[[384,384],[-384,384],[-384,-384],[-132,-384],[384,132]]]},{"polygons":[[[-132,384],[-384,132],[-384,384]],[[-384,-132],[132,384],[384,384],[384,-384],[-384,-384]]],"shapes":[[[-384,384],[-384,132],[-132,384]],[[-384,-384],[384,-384],[384,384],[132,384],[-384,-132]]]},{"polygons":[[[384,132],[132,384],[384,384]],[[-132,384],[384,-132],[384,-384],[-384,-384],[-384,384]]],"shapes":[[[384,384],[132,384],[384,132]],[[-384,384],[-384,-384],[384,-384],[384,-132],[-132,384]]]},{"polygons":[[[384,-96],[-384,-96],[-384,-384],[384,-384]],[[384,96],[-384,96],[-384,384],[384,384]],[[-16,-8],[-16,8],[-8,16],[8,16],[16,8],[16,-8],[8,-16],[-8,-16]]],"shapes":[[[384,-96],[-384,-96],[-384,-384],[384,-384]],[[384,384],[-384,384],[-384,96],[384,96]],[[-8,-16],[8,-16],[16,-8],[16,8],[8,16],[-8,16],[-16,8],[-16,-8]]]},{"polygons":[[[96,384],[96,-384],[384,-384],[384,384]],[[-96,384],[-96,-384],[-384,-384],[-384,384]],[[8,-16],[-8,-16],[-16,-8],[-16,8],[-8,16],[8,16],[16,8],[16,-8]]],"shapes":[[[96,384],[96,-384],[384,-384],[384,384]],[[-384,384],[-384,-384],[-96,-384],[-96,384]],[[16,-8],[16,8],[8,16],[-8,16],[-16,8],[-16,-8],[-8,-16],[8,-16]]]},{"polygons":[[[384,-352],[-384,-352],[-384,-384],[384,-384]],[[384,-160],[-384,-160],[-384,160],[384,160]],[[384,352],[384,384],[-384,384],[-384,352]]],"shapes":[[[384,-352],[-384,-352],[-384,-384],[384,-384]],[[384,160],[-384,160],[-384,-160],[384,-160]],[[384,352],[384,384],[-384,384],[-384,352]]]},{"polygons":[[[-352,-384],[-352,384],[-384,384],[-384,-384]],[[-160,-384],[-160,384],[160,384],[160,-384]],[[352,-384],[384,-384],[384,384],[352,384]]],"shapes":[[[-352,-384],[-352,384],[-384,384],[-384,-384]],[[160,-384],[160,384],[-160,384],[-160,-384]],[[352,-384],[384,-384],[384,384],[352,384]]]},{"polygons":[[[-384,-96],[-336,-96],[-216,-120],[-104,-176],[48,-272],[160,-320],[272,-344],[384,-352],[384,-384],[-384,-384]],[[-384,96],[-336,96],[-216,120],[-112,168],[-8,240],[112,304],[256,344],[384,352],[384,384],[-384,384]],[[384,-160],[344,-160],[240,-144],[108,-84],[108,-60],[16,0],[108,60],[108,84],[240,144],[324,156],[384,160]]],"shapes":[[[-384,-384],[384,-384],[384,-352],[272,-344]],[[-384,-384],[272,-344],[160,-320]],[[-384,-384],[160,-320],[48,-272]],[[-384,-96],[-384,-384],[48,-272],[-104,-176],[-216,-120],[-336,-96]],[[-8,240],[-384,384],[-384,96],[-336,96],[-216,120],[-112,168]],[[384,352],[384,384],[-384,384],[256,344]],[[112,304],[256,344],[-384,384]],[[-8,240],[112,304],[-384,384]],[[108,60],[16,0],[108,-60],[240,-144]],[[240,-144],[344,-160],[384,-160],[384,160],[324,156],[240,144],[108,84],[108,60]],[[240,-144],[108,-60],[108,-84]]]},{"polygons":[[[96,-384],[96,-336],[120,-216],[176,-104],[272,48],[320,160],[344,272],[352,384],[384,384],[384,-384]],[[-96,-384],[-96,-336],[-120,-216],[-168,-112],[-240,-8],[-304,112],[-344,256],[-352,384],[-384,384],[-384,-384]],[[160,384],[160,344],[144,240],[84,108],[60,108],[0,16],[-60,108],[-84,108],[-144,240],[-156,324],[-160,384]]],"shapes":[[[384,-384],[384,384],[352,384],[344,272]],[[384,-384],[344,272],[320,160]],[[384,-384],[320,160],[272,48]],[[96,-384],[384,-384],[272,48],[176,-104],[120,-216],[96,-336]],[[-240,-8],[-384,-384],[-96,-384],[-96,-336],[-120,-216],[-168,-112]],[[-352,384],[-384,384],[-384,-384],[-344,256]],[[-304,112],[-344,256],[-384,-384]],[[-240,-8],[-304,112],[-384,-384]],[[-60,108],[0,16],[60,108],[144,240]],[[144,240],[160,344],[160,384],[-160,384],[-156,324],[-144,240],[-84,108],[-60,108]],[[144,240],[60,108],[84,108]]]},{"polygons":[[[384,96],[336,96],[216,120],[104,176],[-48,272],[-160,320],[-272,344],[-384,352],[-384,384],[384,384]],[[384,-96],[336,-96],[216,-120],[112,-168],[8,-240],[-112,-304],[-256,-344],[-384,-352],[-384,-384],[384,-384]],[[-384,160],[-344,160],[-240,144],[-108,84],[-108,60],[-16,0],[-108,-60],[-108,-84],[-240,-144],[-324,-156],[-384,-160]]],"shapes":[[[384,384],[-384,384],[-384,352],[-272,344]],[[384,384],[-272,344],[-160,320]],[[384,384],[-160,320],[-48,272]],[[384,96],[384,384],[-48,272],[104,176],[216,120],[336,96]],[[8,-240],[384,-384],[384,-96],[336,-96],[216,-120],[112,-168]],[[-384,-352],[-384,-384],[384,-384],[-256,-344]],[[-112,-304],[-256,-344],[384,-384]],[[8,-240],[-112,-304],[384,-384]],[[-108,-60],[-16,0],[-108,60],[-240,144]],[[-240,144],[-344,160],[-384,160],[-384,-160],[-324,-156],[-240,-144],[-108,-84],[-108,-60]],[[-240,144],[-108,60],[-108,84]]]},{"polygons":[[[-96,384],[-96,336],[-120,216],[-176,104],[-272,-48],[-320,-160],[-344,-272],[-352,-384],[-384,-384],[-384,384]],[[96,384],[96,336],[120,216],[168,112],[240,8],[304,-112],[344,-256],[352,-384],[384,-384],[384,384]],[[-160,-384],[-160,-344],[-144,-240],[-84,-108],[-60,-108],[0,-16],[60,-108],[84,-108],[144,-240],[156,-324],[160,-384]]],"shapes":[[[-384,384],[-384,-384],[-352,-384],[-344,-272]],[[-384,384],[-344,-272],[-320,-160]],[[-384,384],[-320,-160],[-272,-48]],[[-96,384],[-384,384],[-272,-48],[-176,104],[-120,216],[-96,336]],[[240,8],[384,384],[96,384],[96,336],[120,216],[168,112]],[[352,-384],[384,-384],[384,384],[344,-256]],[[304,-112],[344,-256],[384,384]],[[240,8],[304,-112],[384,384]],[[60,-108],[0,-16],[-60,-108],[-144,-240]],[[-144,-240],[-160,-344],[-160,-384],[160,-384],[156,-324],[144,-240],[84,-108],[60,-108]],[[-144,-240],[-60,-108],[-84,-108]]]},{"polygons":[[[-384,96],[-216,96],[-108,48],[108,48],[216,96],[384,96],[384,384],[-384,384]],[[-384,-96],[-384,-384],[384,-384],[384,-96],[214.70700073242188,-96],[108,-48],[-108,-48],[-216,-96]]],"shapes":[[[-384,384],[-384,96],[-216,96],[384,384]],[[384,384],[-216,96],[-108,48],[108,48],[216,96]],[[384,384],[216,96],[384,96]],[[-216,-96],[-384,-96],[-384,-384],[384,-384]],[[108,-48],[-108,-48],[-216,-96],[384,-384],[214.70700073242188,-96]],[[214.70700073242188,-96],[384,-384],[384,-96]]]},{"polygons":[[[96,384],[96,216],[48,108],[48,-108],[96,-216],[96,-384],[384,-384],[384,384]],[[-96,384],[-384,384],[-384,-384],[-96,-384],[-96,-214.70700073242188],[-48,-108],[-48,108],[-96,216]]],"shapes":[[[384,384],[96,384],[96,216],[384,-384]],[[384,-384],[96,216],[48,108],[48,-108],[96,-216]],[[384,-384],[96,-216],[96,-384]],[[-96,216],[-96,384],[-384,384],[-384,-384]],[[-48,-108],[-48,108],[-96,216],[-384,-384],[-96,-214.70700073242188]],[[-96,-214.70700073242188],[-384,-384],[-96,-384]]]},{"polygons":[[[384,-96],[72,-96],[12,-48],[-12,-48],[-72,-96],[-216,-96],[-384,-96],[-384,-384],[384,-384]],[[384,96],[384,384],[-384,384],[-384,96],[-72,96],[-12,48],[12,48],[72,96]]],"shapes":[[[384,-384],[384,-96],[72,-96],[-384,-384]],[[-384,-384],[72,-96],[12,-48],[-12,-48],[-72,-96]],[[-384,-384],[-72,-96],[-216,-96]],[[-384,-384],[-216,-96],[-384,-96]],[[72,96],[384,96],[384,384],[-384,384]],[[-12,48],[12,48],[72,96],[-384,384],[-72,96]],[[-72,96],[-384,384],[-384,96]]]},{"polygons":[[[96,384],[96,72],[48,12],[48,-12],[96,-72],[96,-216],[96,-384],[384,-384],[384,384]],[[-96,384],[-384,384],[-384,-384],[-96,-384],[-96,-72],[-48,-12],[-48,12],[-96,72]]],"shapes":[[[384,384],[96,384],[96,72],[384,-384]],[[384,-384],[96,72],[48,12],[48,-12],[96,-72]],[[384,-384],[96,-72],[96,-216]],[[384,-384],[96,-216],[96,-384]],[[-96,72],[-96,384],[-384,384],[-384,-384]],[[-48,-12],[-48,12],[-96,72],[-384,-384],[-96,-72]],[[-96,-72],[-384,-384],[-96,-384]]]},{"polygons":[[[384,-96],[-384,-96],[-384,-384],[384,-384]],[[384,96],[-384,96],[-384,384],[384,384]],[[136,-16],[120,-16],[112,-8],[112,8],[120,16],[136,16],[144,8],[144,-8]],[[-120,-16],[-136,-16],[-144,-8],[-144,8],[-136,16],[-120,16],[-112,8],[-112,-8]]],"shapes":[[[384,-96],[-384,-96],[-384,-384],[384,-384]],[[384,384],[-384,384],[-384,96],[384,96]],[[144,-8],[144,8],[136,16],[120,16],[112,8],[112,-8],[120,-16],[136,-16]],[[-112,-8],[-112,8],[-120,16],[-136,16],[-144,8],[-144,-8],[-136,-16],[-120,-16]]]},{"polygons":[[[96,384],[96,-384],[384,-384],[384,384]],[[-96,384],[-96,-384],[-384,-384],[-384,384]],[[16,136],[16,120],[8,112],[-8,112],[-16,120],[-16,136],[-8,144],[8,144]],[[16,-120],[16,-136],[8,-144],[-8,-144],[-16,-136],[-16,-120],[-8,-112],[8,-112]]],"shapes":[[[96,384],[96,-384],[384,-384],[384,384]],[[-384,384],[-384,-384],[-96,-384],[-96,384]],[[8,144],[-8,144],[-16,136],[-16,120],[-8,112],[8,112],[16,120],[16,136]],[[8,-112],[-8,-112],[-16,-120],[-16,-136],[-8,-144],[8,-144],[16,-136],[16,-120]]]},{"polygons":[[[384,96],[300,96],[216,144],[-216,144],[-300,96],[-384,96],[-384,384],[384,384]],[[384,-96],[300,-96],[228,-144],[-216,-144],[-312,-96],[-384,-96],[-384,-384],[384,-384]]],"shapes":[[[384,96],[384,384],[-384,384],[216,144]],[[-384,384],[-384,96],[-300,96],[-216,144]],[[-384,384],[-216,144],[216,144]],[[384,96],[216,144],[300,96]],[[384,-384],[384,-96],[300,-96],[228,-144]],[[-384,-384],[384,-384],[228,-144],[-216,-144]],[[-384,-96],[-384,-384],[-216,-144],[-312,-96]]]},{"polygons":[[[96,-384],[96,-300],[144,-216],[144,216],[96,300],[96,384],[384,384],[384,-384]],[[-96,-384],[-96,-300],[-144,-228],[-144,216],[-96,312],[-96,384],[-384,384],[-384,-384]]],"shapes":[[[96,-384],[384,-384],[384,384],[144,-216]],[[384,384],[96,384],[96,300],[144,216]],[[384,384],[144,216],[144,-216]],[[96,-384],[144,-216],[96,-300]],[[-384,-384],[-96,-384],[-96,-300],[-144,-228]],[[-384,384],[-384,-384],[-144,-228],[-144,216]],[[-96,384],[-384,384],[-144,216],[-96,312]]]},{"polygons":[[[-384,-352],[-384,-384],[384,-384],[384,-352]],[[-384,-160],[384,-160],[384,384],[-384,384]],[[-8,-240],[8,-240],[16,-248],[16,-264],[8,-272],[-8,-272],[-16,-264],[-16,-248],[-8,-240]]],"shapes":[[[-384,-352],[-384,-384],[384,-384],[384,-352]],[[-384,-160],[384,-160],[384,384],[-384,384]],[[-8,-240],[-16,-248],[-16,-264],[-8,-272],[8,-272],[16,-264],[16,-248],[8,-240],[-8,-240]]]},{"polygons":[[[384,352],[384,384],[-384,384],[-384,352]],[[384,160],[-384,160],[-384,-384],[384,-384]],[[8,240],[-8,240],[-16,248],[-16,264],[-8,272],[8,272],[16,264],[16,248],[8,240]]],"shapes":[[[384,352],[384,384],[-384,384],[-384,352]],[[384,160],[-384,160],[-384,-384],[384,-384]],[[8,240],[16,248],[16,264],[8,272],[-8,272],[-16,264],[-16,248],[-8,240],[8,240]]]},{"polygons":[[[-352,384],[-384,384],[-384,-384],[-352,-384]],[[-160,384],[-160,-384],[384,-384],[384,384]],[[-240,8],[-240,-8],[-248,-16],[-264,-16],[-272,-8],[-272,8],[-264,16],[-248,16],[-240,8]]],"shapes":[[[-352,384],[-384,384],[-384,-384],[-352,-384]],[[-160,384],[-160,-384],[384,-384],[384,384]],[[-240,8],[-248,16],[-264,16],[-272,8],[-272,-8],[-264,-16],[-248,-16],[-240,-8],[-240,8]]]},{"polygons":[[[352,-384],[384,-384],[384,384],[352,384]],[[160,-384],[160,384],[-384,384],[-384,-384]],[[240,-8],[240,8],[248,16],[264,16],[272,8],[272,-8],[264,-16],[248,-16],[240,-8]]],"shapes":[[[352,-384],[384,-384],[384,384],[352,384]],[[160,-384],[160,384],[-384,384],[-384,-384]],[[240,-8],[248,-16],[264,-16],[272,-8],[272,8],[264,16],[248,16],[240,8],[240,-8]]]},{"polygons":[[[-384,-352],[-208,-352],[-112,-304],[112,-304],[208,-352],[384,-352],[384,-384],[-384,-384]],[[-384,-160],[-208,-160],[-112,-208],[112,-208],[208,-160],[384,-160],[384,384],[-384,384]]],"shapes":[[[-384,-352],[-384,-384],[384,-384],[-208,-352]],[[-112,-304],[-208,-352],[384,-384],[208,-352]],[[384,-384],[384,-352],[208,-352]],[[-112,-304],[208,-352],[112,-304]],[[-384,384],[-384,-160],[-208,-160],[384,384]],[[384,384],[-208,-160],[-112,-208],[112,-208],[208,-160]],[[384,384],[208,-160],[384,-160]]]},{"polygons":[[[384,352],[208,352],[112,304],[-112,304],[-208,352],[-384,352],[-384,384],[384,384]],[[384,160],[208,160],[112,208],[-112,208],[-208,160],[-384,160],[-384,-384],[384,-384]]],"shapes":[[[384,352],[384,384],[-384,384],[208,352]],[[112,304],[208,352],[-384,384],[-208,352]],[[-384,384],[-384,352],[-208,352]],[[112,304],[-208,352],[-112,304]],[[384,-384],[384,160],[208,160],[-384,-384]],[[-384,-384],[208,160],[112,208],[-112,208],[-208,160]],[[-384,-384],[-208,160],[-384,160]]]},{"polygons":[[[-352,384],[-352,208],[-304,112],[-304,-112],[-352,-208],[-352,-384],[-384,-384],[-384,384]],[[-160,384],[-160,208],[-208,112],[-208,-112],[-160,-208],[-160,-384],[384,-384],[384,384]]],"shapes":[[[-352,384],[-384,384],[-384,-384],[-352,208]],[[-304,112],[-352,208],[-384,-384],[-352,-208]],[[-384,-384],[-352,-384],[-352,-208]],[[-304,112],[-352,-208],[-304,-112]],[[384,384],[-160,384],[-160,208],[384,-384]],[[384,-384],[-160,208],[-208,112],[-208,-112],[-160,-208]],[[384,-384],[-160,-208],[-160,-384]]]},{"polygons":[[[352,-384],[352,-208],[304,-112],[304,112],[352,208],[352,384],[384,384],[384,-384]],[[160,-384],[160,-208],[208,-112],[208,112],[160,208],[160,384],[-384,384],[-384,-384]]],"shapes":[[[352,-384],[384,-384],[384,384],[352,-208]],[[304,-112],[352,-208],[384,384],[352,208]],[[384,384],[352,384],[352,208]],[[304,-112],[352,208],[304,112]],[[-384,-384],[160,-384],[160,-208],[-384,384]],[[-384,384],[160,-208],[208,-112],[208,112],[160,208]],[[-384,384],[160,208],[160,384]]]},{"polygons":[[[-384,-352],[-64,-352],[-8,-304],[8,-304],[72,-352],[384,-352],[384,-384],[-384,-384]],[[-384,-160],[-72,-160],[-8,-208],[8,-208],[72,-160],[384,-160],[384,384],[-384,384]]],"shapes":[[[-384,-352],[-384,-384],[384,-384],[-64,-352]],[[384,-384],[384,-352],[72,-352]],[[-64,-352],[384,-384],[72,-352]],[[-64,-352],[72,-352],[8,-304],[-8,-304]],[[-384,384],[-384,-160],[-72,-160],[384,384]],[[384,384],[-72,-160],[-8,-208],[8,-208],[72,-160]],[[384,384],[72,-160],[384,-160]]]},{"polygons":[[[384,352],[64,352],[8,304],[-8,304],[-72,352],[-384,352],[-384,384],[384,384]],[[384,160],[72,160],[8,208],[-8,208],[-72,160],[-384,160],[-384,-384],[384,-384]]],"shapes":[[[384,352],[384,384],[-384,384],[64,352]],[[-384,384],[-384,352],[-72,352]],[[64,352],[-384,384],[-72,352]],[[64,352],[-72,352],[-8,304],[8,304]],[[384,-384],[384,160],[72,160],[-384,-384]],[[-384,-384],[72,160],[8,208],[-8,208],[-72,160]],[[-384,-384],[-72,160],[-384,160]]]},{"polygons":[[[-352,384],[-352,64],[-304,8],[-304,-8],[-352,-72],[-352,-384],[-384,-384],[-384,384]],[[-160,384],[-160,72],[-208,8],[-208,-8],[-160,-72],[-160,-384],[384,-384],[384,384]]],"shapes":[[[-352,384],[-384,384],[-384,-384],[-352,64]],[[-384,-384],[-352,-384],[-352,-72]],[[-352,64],[-384,-384],[-352,-72]],[[-352,64],[-352,-72],[-304,-8],[-304,8]],[[384,384],[-160,384],[-160,72],[384,-384]],[[384,-384],[-160,72],[-208,8],[-208,-8],[-160,-72]],[[384,-384],[-160,-72],[-160,-384]]]},{"polygons":[[[352,-384],[352,-64],[304,-8],[304,8],[352,72],[352,384],[384,384],[384,-384]],[[160,-384],[160,-72],[208,-8],[208,8],[160,72],[160,384],[-384,384],[-384,-384]]],"shapes":[[[352,-384],[384,-384],[384,384],[352,-64]],[[384,384],[352,384],[352,72]],[[352,-64],[384,384],[352,72]],[[352,-64],[352,72],[304,8],[304,-8]],[[-384,-384],[160,-384],[160,-72],[-384,384]],[[-384,384],[160,-72],[208,-8],[208,8],[160,72]],[[-384,384],[160,72],[160,384]]]},{"polygons":[[[-384,-352],[-384,-384],[384,-384],[384,-352]],[[-384,-160],[384,-160],[384,384],[-384,384]],[[136,-272],[120,-272],[112,-264],[112,-248],[120,-240],[136,-240],[144,-248],[144,-264]],[[-120,-272],[-136,-272],[-144,-264],[-144,-248],[-136,-240],[-120,-240],[-112,-248],[-112,-264]]],"shapes":[[[-384,-352],[-384,-384],[384,-384],[384,-352]],[[-384,-160],[384,-160],[384,384],[-384,384]],[[144,-264],[144,-248],[136,-240],[120,-240],[112,-248],[112,-264],[120,-272],[136,-272]],[[-112,-264],[-112,-248],[-120,-240],[-136,-240],[-144,-248],[-144,-264],[-136,-272],[-120,-272]]]},{"polygons":[[[384,352],[384,384],[-384,384],[-384,352]],[[384,160],[-384,160],[-384,-384],[384,-384]],[[-136,272],[-120,272],[-112,264],[-112,248],[-120,240],[-136,240],[-144,248],[-144,264]],[[120,272],[136,272],[144,264],[144,248],[136,240],[120,240],[112,248],[112,264]]],"shapes":[[[384,352],[384,384],[-384,384],[-384,352]],[[384,160],[-384,160],[-384,-384],[384,-384]],[[-144,264],[-144,248],[-136,240],[-120,240],[-112,248],[-112,264],[-120,272],[-136,272]],[[112,264],[112,248],[120,240],[136,240],[144,248],[144,264],[136,272],[120,272]]]},{"polygons":[[[-352,384],[-384,384],[-384,-384],[-352,-384]],[[-160,384],[-160,-384],[384,-384],[384,384]],[[-272,-136],[-272,-120],[-264,-112],[-248,-112],[-240,-120],[-240,-136],[-248,-144],[-264,-144]],[[-272,120],[-272,136],[-264,144],[-248,144],[-240,136],[-240,120],[-248,112],[-264,112]]],"shapes":[[[-352,384],[-384,384],[-384,-384],[-352,-384]],[[-160,384],[-160,-384],[384,-384],[384,384]],[[-264,-144],[-248,-144],[-240,-136],[-240,-120],[-248,-112],[-264,-112],[-272,-120],[-272,-136]],[[-264,112],[-248,112],[-240,120],[-240,136],[-248,144],[-264,144],[-272,136],[-272,120]]]},{"polygons":[[[352,-384],[384,-384],[384,384],[352,384]],[[160,-384],[160,384],[-384,384],[-384,-384]],[[272,136],[272,120],[264,112],[248,112],[240,120],[240,136],[248,144],[264,144]],[[272,-120],[272,-136],[264,-144],[248,-144],[240,-136],[240,-120],[248,-112],[264,-112]]],"shapes":[[[352,-384],[384,-384],[384,384],[352,384]],[[160,-384],[160,384],[-384,384],[-384,-384]],[[264,144],[248,144],[240,136],[240,120],[248,112],[264,112],[272,120],[272,136]],[[264,-112],[248,-112],[240,-120],[240,-136],[248,-144],[264,-144],[272,-136],[272,-120]]]},{"polygons":[[[-355.75,-384],[-384,-298.5],[-384,-384]],[[-156.16099548339844,-384],[-384,299.4530029296875],[-384,384],[384,384],[384,-384]]],"shapes":[[[-355.75,-384],[-384,-298.5],[-384,-384]],[[384,-384],[384,384],[-384,384],[-384,299.4530029296875],[-156.16099548339844,-384]]]},{"polygons":[[[355.75,-384],[384,-298.5],[384,-384]],[[156.16099548339844,-384],[384,299.4530029296875],[384,384],[-384,384],[-384,-384]]],"shapes":[[[384,-384],[384,-298.5],[355.75,-384]],[[156.16099548339844,-384],[384,299.4530029296875],[384,384],[-384,384],[-384,-384]]]},{"polygons":[[[-355.75,384],[-384,298.5],[-384,384]],[[-156.16099548339844,384],[-384,-299.4530029296875],[-384,-384],[384,-384],[384,384]]],"shapes":[[[-384,384],[-384,298.5],[-355.75,384]],[[-156.16099548339844,384],[-384,-299.4530029296875],[-384,-384],[384,-384],[384,384]]]},{"polygons":[[[-384,-355.75],[-298.5,-384],[-384,-384]],[[-384,-156.16099548339844],[299.4530029296875,-384],[384,-384],[384,384],[-384,384]]],"shapes":[[[-384,-384],[-298.5,-384],[-384,-355.75]],[[-384,-156.16099548339844],[299.4530029296875,-384],[384,-384],[384,384],[-384,384]]]},{"polygons":[[[355.75,384],[384,298.5],[384,384]],[[156.16099548339844,384],[384,-299.4530029296875],[384,-384],[-384,-384],[-384,384]]],"shapes":[[[355.75,384],[384,298.5],[384,384]],[[-384,384],[-384,-384],[384,-384],[384,-299.4530029296875],[156.16099548339844,384]]]},{"polygons":[[[-384,355.75],[-298.5,384],[-384,384]],[[-384,156.16099548339844],[299.4530029296875,384],[384,384],[384,-384],[-384,-384]]],"shapes":[[[-384,355.75],[-298.5,384],[-384,384]],[[-384,-384],[384,-384],[384,384],[299.4530029296875,384],[-384,156.16099548339844]]]},{"polygons":[[[384,-355.75],[298.5,-384],[384,-384]],[[384,-156.16099548339844],[-299.4530029296875,-384],[-384,-384],[-384,384],[384,384]]],"shapes":[[[384,-355.75],[298.5,-384],[384,-384]],[[384,384],[-384,384],[-384,-384],[-299.4530029296875,-384],[384,-156.16099548339844]]]},{"polygons":[[[384,355.75],[298.5,384],[384,384]],[[384,156.16099548339844],[-299.4530029296875,384],[-384,384],[-384,-384],[384,-384]]],"shapes":[[[384,384],[298.5,384],[384,355.75]],[[384,156.16099548339844],[-299.4530029296875,384],[-384,384],[-384,-384],[384,-384]]]},{"polygons":[[[-350.7309875488281,-384],[-354.02099609375,-373.9549865722656],[-371.39300537109375,-336.98199462890625],[-384,-298.5],[-384,-384]],[[-160.375,-384],[-183.49400329589844,-298.39898681640625],[-273.2829895019531,-21.38159942626953],[-384,299.4530029296875],[-384,384],[384,384],[384,-384]]],"shapes":[[[-371.39300537109375,-336.98199462890625],[-384,-298.5],[-384,-384]],[[-354.02099609375,-373.9549865722656],[-371.39300537109375,-336.98199462890625],[-384,-384],[-350.7309875488281,-384]],[[-160.375,-384],[384,-384],[384,384],[-183.49400329589844,-298.39898681640625]],[[-183.49400329589844,-298.39898681640625],[384,384],[-384,384],[-273.2829895019531,-21.38159942626953]],[[-273.2829895019531,-21.38159942626953],[-384,384],[-384,299.4530029296875]]]},{"polygons":[[[350.7309875488281,-384],[354.02099609375,-373.9549865722656],[371.39300537109375,-336.98199462890625],[384,-298.5],[384,-384]],[[160.375,-384],[183.49400329589844,-298.39898681640625],[273.2829895019531,-21.38159942626953],[384,299.4530029296875],[384,384],[-384,384],[-384,-384]]],"shapes":[[[384,-384],[384,-298.5],[371.39300537109375,-336.98199462890625]],[[384,-384],[371.39300537109375,-336.98199462890625],[354.02099609375,-373.9549865722656],[350.7309875488281,-384]],[[273.2829895019531,-21.38159942626953],[384,299.4530029296875],[384,384],[-384,384],[-384,-384]],[[183.49400329589844,-298.39898681640625],[273.2829895019531,-21.38159942626953],[-384,-384]],[[-384,-384],[160.375,-384],[183.49400329589844,-298.39898681640625]]]},{"polygons":[[[-350.7309875488281,384],[-354.02099609375,373.9549865722656],[-371.39300537109375,336.98199462890625],[-384,298.5],[-384,384]],[[-160.375,384],[-183.49400329589844,298.39898681640625],[-273.2829895019531,21.38159942626953],[-384,-299.4530029296875],[-384,-384],[384,-384],[384,384]]],"shapes":[[[-384,384],[-384,298.5],[-371.39300537109375,336.98199462890625]],[[-384,384],[-371.39300537109375,336.98199462890625],[-354.02099609375,373.9549865722656],[-350.7309875488281,384]],[[-273.2829895019531,21.38159942626953],[-384,-299.4530029296875],[-384,-384],[384,-384],[384,384]],[[-183.49400329589844,298.39898681640625],[-273.2829895019531,21.38159942626953],[384,384]],[[384,384],[-160.375,384],[-183.49400329589844,298.39898681640625]]]},{"polygons":[[[-384,-350.7309875488281],[-373.9549865722656,-354.02099609375],[-336.98199462890625,-371.39300537109375],[-298.5,-384],[-384,-384]],[[-384,-160.375],[-298.39898681640625,-183.49400329589844],[-21.38159942626953,-273.2829895019531],[299.4530029296875,-384],[384,-384],[384,384],[-384,384]]],"shapes":[[[-384,-384],[-298.5,-384],[-336.98199462890625,-371.39300537109375]],[[-384,-384],[-336.98199462890625,-371.39300537109375],[-373.9549865722656,-354.02099609375],[-384,-350.7309875488281]],[[-21.38159942626953,-273.2829895019531],[299.4530029296875,-384],[384,-384],[384,384],[-384,384]],[[-298.39898681640625,-183.49400329589844],[-21.38159942626953,-273.2829895019531],[-384,384]],[[-384,384],[-384,-160.375],[-298.39898681640625,-183.49400329589844]]]},{"polygons":[[[350.7309875488281,384],[354.02099609375,373.9549865722656],[371.39300537109375,336.98199462890625],[384,298.5],[384,384]],[[160.375,384],[183.49400329589844,298.39898681640625],[273.2829895019531,21.38159942626953],[384,-299.4530029296875],[384,-384],[-384,-384],[-384,384]]],"shapes":[[[371.39300537109375,336.98199462890625],[384,298.5],[384,384]],[[354.02099609375,373.9549865722656],[371.39300537109375,336.98199462890625],[384,384],[350.7309875488281,384]],[[160.375,384],[-384,384],[-384,-384],[183.49400329589844,298.39898681640625]],[[183.49400329589844,298.39898681640625],[-384,-384],[384,-384],[273.2829895019531,21.38159942626953]],[[273.2829895019531,21.38159942626953],[384,-384],[384,-299.4530029296875]]]},{"polygons":[[[-384,350.7309875488281],[-373.9549865722656,354.02099609375],[-336.98199462890625,371.39300537109375],[-298.5,384],[-384,384]],[[-384,160.375],[-298.39898681640625,183.49400329589844],[-21.38159942626953,273.2829895019531],[299.4530029296875,384],[384,384],[384,-384],[-384,-384]]],"shapes":[[[-336.98199462890625,371.39300537109375],[-298.5,384],[-384,384]],[[-373.9549865722656,354.02099609375],[-336.98199462890625,371.39300537109375],[-384,384],[-384,350.7309875488281]],[[-384,160.375],[-384,-384],[384,-384],[-298.39898681640625,183.49400329589844]],[[-298.39898681640625,183.49400329589844],[384,-384],[384,384],[-21.38159942626953,273.2829895019531]],[[-21.38159942626953,273.2829895019531],[384,384],[299.4530029296875,384]]]},{"polygons":[[[384,-350.7309875488281],[373.9549865722656,-354.02099609375],[336.98199462890625,-371.39300537109375],[298.5,-384],[384,-384]],[[384,-160.375],[298.39898681640625,-183.49400329589844],[21.38159942626953,-273.2829895019531],[-299.4530029296875,-384],[-384,-384],[-384,384],[384,384]]],"shapes":[[[336.98199462890625,-371.39300537109375],[298.5,-384],[384,-384]],[[373.9549865722656,-354.02099609375],[336.98199462890625,-371.39300537109375],[384,-384],[384,-350.7309875488281]],[[384,-160.375],[384,384],[-384,384],[298.39898681640625,-183.49400329589844]],[[298.39898681640625,-183.49400329589844],[-384,384],[-384,-384],[21.38159942626953,-273.2829895019531]],[[21.38159942626953,-273.2829895019531],[-384,-384],[-299.4530029296875,-384]]]},{"polygons":[[[384,350.7309875488281],[373.9549865722656,354.02099609375],[336.98199462890625,371.39300537109375],[298.5,384],[384,384]],[[384,160.375],[298.39898681640625,183.49400329589844],[21.38159942626953,273.2829895019531],[-299.4530029296875,384],[-384,384],[-384,-384],[384,-384]]],"shapes":[[[384,384],[298.5,384],[336.98199462890625,371.39300537109375]],[[384,384],[336.98199462890625,371.39300537109375],[373.9549865722656,354.02099609375],[384,350.7309875488281]],[[21.38159942626953,273.2829895019531],[-299.4530029296875,384],[-384,384],[-384,-384],[384,-384]],[[298.39898681640625,183.49400329589844],[21.38159942626953,273.2829895019531],[384,-384]],[[384,-384],[384,160.375],[298.39898681640625,183.49400329589844]]]},{"polygons":[[[-384,-100.16400146484375],[-384,-384],[384,-384],[384,-357.18798828125]],[[-384,100.16400146484375],[384,-155.60000610351562],[384,384],[-384,384]]],"shapes":[[[-384,-100.16400146484375],[-384,-384],[384,-384],[384,-357.18798828125]],[[-384,100.16400146484375],[384,-155.60000610351562],[384,384],[-384,384]]]},{"polygons":[[[384,-100.16400146484375],[384,-384],[-384,-384],[-384,-357.18798828125]],[[384,100.16400146484375],[-384,-155.60000610351562],[-384,384],[384,384]]],"shapes":[[[-384,-357.18798828125],[-384,-384],[384,-384],[384,-100.16400146484375]],[[384,384],[-384,384],[-384,-155.60000610351562],[384,100.16400146484375]]]},{"polygons":[[[-384,100.16400146484375],[-384,384],[384,384],[384,357.18798828125]],[[-384,-100.16400146484375],[384,155.60000610351562],[384,-384],[-384,-384]]],"shapes":[[[384,357.18798828125],[384,384],[-384,384],[-384,100.16400146484375]],[[-384,-384],[384,-384],[384,155.60000610351562],[-384,-100.16400146484375]]]},{"polygons":[[[-100.16400146484375,-384],[-384,-384],[-384,384],[-357.18798828125,384]],[[100.16400146484375,-384],[-155.60000610351562,384],[384,384],[384,-384]]],"shapes":[[[-357.18798828125,384],[-384,384],[-384,-384],[-100.16400146484375,-384]],[[384,-384],[384,384],[-155.60000610351562,384],[100.16400146484375,-384]]]},{"polygons":[[[384,100.16400146484375],[384,384],[-384,384],[-384,357.18798828125]],[[384,-100.16400146484375],[-384,155.60000610351562],[-384,-384],[384,-384]]],"shapes":[[[384,100.16400146484375],[384,384],[-384,384],[-384,357.18798828125]],[[384,-100.16400146484375],[-384,155.60000610351562],[-384,-384],[384,-384]]]},{"polygons":[[[-100.16400146484375,384],[-384,384],[-384,-384],[-357.18798828125,-384]],[[100.16400146484375,384],[-155.60000610351562,-384],[384,-384],[384,384]]],"shapes":[[[-100.16400146484375,384],[-384,384],[-384,-384],[-357.18798828125,-384]],[[100.16400146484375,384],[-155.60000610351562,-384],[384,-384],[384,384]]]},{"polygons":[[[100.16400146484375,-384],[384,-384],[384,384],[357.18798828125,384]],[[-100.16400146484375,-384],[155.60000610351562,384],[-384,384],[-384,-384]]],"shapes":[[[100.16400146484375,-384],[384,-384],[384,384],[357.18798828125,384]],[[-100.16400146484375,-384],[155.60000610351562,384],[-384,384],[-384,-384]]]},{"polygons":[[[100.16400146484375,384],[384,384],[384,-384],[357.18798828125,-384]],[[-100.16400146484375,384],[155.60000610351562,-384],[-384,-384],[-384,384]]],"shapes":[[[357.18798828125,-384],[384,-384],[384,384],[100.16400146484375,384]],[[-384,384],[-384,-384],[155.60000610351562,-384],[-100.16400146484375,384]]]},{"polygons":[[[-384,-95.4092025756836],[-384,-384],[384,-384],[384,-357.18798828125],[-205.9720001220703,-133.572998046875],[-323.8299865722656,-102.14399719238281]],[[-384,96.53170013427734],[-271.07501220703125,85.30709838867188],[-128.52200317382812,46.02090072631836],[204.8489990234375,-89.7968978881836],[384,-155.60000610351562],[384,384],[-384,384]]],"shapes":[[[-384,-95.4092025756836],[-384,-384],[384,-384],[384,-357.18798828125],[-205.9720001220703,-133.572998046875],[-323.8299865722656,-102.14399719238281]],[[204.8489990234375,-89.7968978881836],[384,-155.60000610351562],[384,384],[-384,384],[-128.52200317382812,46.02090072631836]],[[-271.07501220703125,85.30709838867188],[-128.52200317382812,46.02090072631836],[-384,384]],[[-384,384],[-384,96.53170013427734],[-271.07501220703125,85.30709838867188]]]},{"polygons":[[[384,-95.4092025756836],[384,-384],[-384,-384],[-384,-357.18798828125],[205.9720001220703,-133.572998046875],[323.8299865722656,-102.14399719238281]],[[384,96.53170013427734],[271.07501220703125,85.30709838867188],[128.52200317382812,46.02090072631836],[-204.8489990234375,-89.7968978881836],[-384,-155.60000610351562],[-384,384],[384,384]]],"shapes":[[[323.8299865722656,-102.14399719238281],[205.9720001220703,-133.572998046875],[-384,-357.18798828125],[-384,-384],[384,-384],[384,-95.4092025756836]],[[384,384],[-384,384],[-384,-155.60000610351562],[-204.8489990234375,-89.7968978881836],[128.52200317382812,46.02090072631836]],[[384,384],[128.52200317382812,46.02090072631836],[271.07501220703125,85.30709838867188]],[[384,96.53170013427734],[384,384],[271.07501220703125,85.30709838867188]]]},{"polygons":[[[-384,95.4092025756836],[-384,384],[384,384],[384,357.18798828125],[-205.9720001220703,133.572998046875],[-323.8299865722656,102.14399719238281]],[[-384,-96.53170013427734],[-271.07501220703125,-85.30709838867188],[-128.52200317382812,-46.02090072631836],[204.8489990234375,89.7968978881836],[384,155.60000610351562],[384,-384],[-384,-384]]],"shapes":[[[-323.8299865722656,102.14399719238281],[-205.9720001220703,133.572998046875],[384,357.18798828125],[384,384],[-384,384],[-384,95.4092025756836]],[[-384,-384],[384,-384],[384,155.60000610351562],[204.8489990234375,89.7968978881836],[-128.52200317382812,-46.02090072631836]],[[-384,-384],[-128.52200317382812,-46.02090072631836],[-271.07501220703125,-85.30709838867188]],[[-384,-96.53170013427734],[-384,-384],[-271.07501220703125,-85.30709838867188]]]},{"polygons":[[[-95.4092025756836,-384],[-384,-384],[-384,384],[-357.18798828125,384],[-133.572998046875,-205.9720001220703],[-102.14399719238281,-323.8299865722656]],[[96.53170013427734,-384],[85.30709838867188,-271.07501220703125],[46.02090072631836,-128.52200317382812],[-89.7968978881836,204.8489990234375],[-155.60000610351562,384],[384,384],[384,-384]]],"shapes":[[[-102.14399719238281,-323.8299865722656],[-133.572998046875,-205.9720001220703],[-357.18798828125,384],[-384,384],[-384,-384],[-95.4092025756836,-384]],[[384,-384],[384,384],[-155.60000610351562,384],[-89.7968978881836,204.8489990234375],[46.02090072631836,-128.52200317382812]],[[384,-384],[46.02090072631836,-128.52200317382812],[85.30709838867188,-271.07501220703125]],[[96.53170013427734,-384],[384,-384],[85.30709838867188,-271.07501220703125]]]},{"polygons":[[[384,95.4092025756836],[384,384],[-384,384],[-384,357.18798828125],[205.9720001220703,133.572998046875],[323.8299865722656,102.14399719238281]],[[384,-96.53170013427734],[271.07501220703125,-85.30709838867188],[128.52200317382812,-46.02090072631836],[-204.8489990234375,89.7968978881836],[-384,155.60000610351562],[-384,-384],[384,-384]]],"shapes":[[[384,95.4092025756836],[384,384],[-384,384],[-384,357.18798828125],[205.9720001220703,133.572998046875],[323.8299865722656,102.14399719238281]],[[-204.8489990234375,89.7968978881836],[-384,155.60000610351562],[-384,-384],[384,-384],[128.52200317382812,-46.02090072631836]],[[271.07501220703125,-85.30709838867188],[128.52200317382812,-46.02090072631836],[384,-384]],[[384,-384],[384,-96.53170013427734],[271.07501220703125,-85.30709838867188]]]},{"polygons":[[[-95.4092025756836,384],[-384,384],[-384,-384],[-357.18798828125,-384],[-133.572998046875,205.9720001220703],[-102.14399719238281,323.8299865722656]],[[96.53170013427734,384],[85.30709838867188,271.07501220703125],[46.02090072631836,128.52200317382812],[-89.7968978881836,-204.8489990234375],[-155.60000610351562,-384],[384,-384],[384,384]]],"shapes":[[[-95.4092025756836,384],[-384,384],[-384,-384],[-357.18798828125,-384],[-133.572998046875,205.9720001220703],[-102.14399719238281,323.8299865722656]],[[-89.7968978881836,-204.8489990234375],[-155.60000610351562,-384],[384,-384],[384,384],[46.02090072631836,128.52200317382812]],[[85.30709838867188,271.07501220703125],[46.02090072631836,128.52200317382812],[384,384]],[[384,384],[96.53170013427734,384],[85.30709838867188,271.07501220703125]]]},{"polygons":[[[95.4092025756836,-384],[384,-384],[384,384],[357.18798828125,384],[133.572998046875,-205.9720001220703],[102.14399719238281,-323.8299865722656]],[[-96.53170013427734,-384],[-85.30709838867188,-271.07501220703125],[-46.02090072631836,-128.52200317382812],[89.7968978881836,204.8489990234375],[155.60000610351562,384],[-384,384],[-384,-384]]],"shapes":[[[95.4092025756836,-384],[384,-384],[384,384],[357.18798828125,384],[133.572998046875,-205.9720001220703],[102.14399719238281,-323.8299865722656]],[[89.7968978881836,204.8489990234375],[155.60000610351562,384],[-384,384],[-384,-384],[-46.02090072631836,-128.52200317382812]],[[-85.30709838867188,-271.07501220703125],[-46.02090072631836,-128.52200317382812],[-384,-384]],[[-384,-384],[-96.53170013427734,-384],[-85.30709838867188,-271.07501220703125]]]},{"polygons":[[[95.4092025756836,384],[384,384],[384,-384],[357.18798828125,-384],[133.572998046875,205.9720001220703],[102.14399719238281,323.8299865722656]],[[-96.53170013427734,384],[-85.30709838867188,271.07501220703125],[-46.02090072631836,128.52200317382812],[89.7968978881836,-204.8489990234375],[155.60000610351562,-384],[-384,-384],[-384,384]]],"shapes":[[[102.14399719238281,323.8299865722656],[133.572998046875,205.9720001220703],[357.18798828125,-384],[384,-384],[384,384],[95.4092025756836,384]],[[-384,384],[-384,-384],[155.60000610351562,-384],[89.7968978881836,-204.8489990234375],[-46.02090072631836,128.52200317382812]],[[-384,384],[-46.02090072631836,128.52200317382812],[-85.30709838867188,271.07501220703125]],[[-96.53170013427734,384],[-384,384],[-85.30709838867188,271.07501220703125]]]},{"polygons":[[[384,-351.2550048828125],[384,-384],[-384,-384],[-384,-100.05599975585938],[85.55989837646484,-287.0849914550781],[215.76100158691406,-327.5790100097656],[332.7510070800781,-350.12298583984375]],[[-384,99.61849975585938],[-384,384],[384,384],[384,-161.12100219726562],[310.4779968261719,-152.343994140625],[166.1529998779297,-107.79900360107422],[5.790830135345459,-47.21760177612305]]],"shapes":[[[85.55989837646484,-287.0849914550781],[-384,-100.05599975585938],[-384,-384]],[[85.55989837646484,-287.0849914550781],[-384,-384],[384,-384],[215.76100158691406,-327.5790100097656]],[[332.7510070800781,-350.12298583984375],[215.76100158691406,-327.5790100097656],[384,-384]],[[384,-351.2550048828125],[332.7510070800781,-350.12298583984375],[384,-384]],[[166.1529998779297,-107.79900360107422],[310.4779968261719,-152.343994140625],[384,-161.12100219726562],[384,384],[-384,384],[5.790830135345459,-47.21760177612305]],[[-384,99.61849975585938],[5.790830135345459,-47.21760177612305],[-384,384]]]},{"polygons":[[[-384,-351.2550048828125],[-384,-384],[384,-384],[384,-100.05599975585938],[-85.55989837646484,-287.0849914550781],[-215.76100158691406,-327.5790100097656],[-332.7510070800781,-350.12298583984375]],[[384,99.61849975585938],[384,384],[-384,384],[-384,-161.12100219726562],[-310.4779968261719,-152.343994140625],[-166.1529998779297,-107.79900360107422],[-5.790830135345459,-47.21760177612305]]],"shapes":[[[384,-384],[384,-100.05599975585938],[-85.55989837646484,-287.0849914550781]],[[-384,-384],[384,-384],[-85.55989837646484,-287.0849914550781],[-215.76100158691406,-327.5790100097656]],[[-384,-384],[-215.76100158691406,-327.5790100097656],[-332.7510070800781,-350.12298583984375]],[[-332.7510070800781,-350.12298583984375],[-384,-351.2550048828125],[-384,-384]],[[384,384],[-384,384],[-384,-161.12100219726562],[-310.4779968261719,-152.343994140625],[-166.1529998779297,-107.79900360107422],[-5.790830135345459,-47.21760177612305]],[[-5.790830135345459,-47.21760177612305],[384,99.61849975585938],[384,384]]]},{"polygons":[[[384,351.2550048828125],[384,384],[-384,384],[-384,100.05599975585938],[85.55989837646484,287.0849914550781],[215.76100158691406,327.5790100097656],[332.7510070800781,350.12298583984375]],[[-384,-99.61849975585938],[-384,-384],[384,-384],[384,161.12100219726562],[310.4779968261719,152.343994140625],[166.1529998779297,107.79900360107422],[5.790830135345459,47.21760177612305]]],"shapes":[[[-384,384],[-384,100.05599975585938],[85.55989837646484,287.0849914550781]],[[384,384],[-384,384],[85.55989837646484,287.0849914550781],[215.76100158691406,327.5790100097656]],[[384,384],[215.76100158691406,327.5790100097656],[332.7510070800781,350.12298583984375]],[[332.7510070800781,350.12298583984375],[384,351.2550048828125],[384,384]],[[-384,-384],[384,-384],[384,161.12100219726562],[310.4779968261719,152.343994140625],[166.1529998779297,107.79900360107422],[5.790830135345459,47.21760177612305]],[[5.790830135345459,47.21760177612305],[-384,-99.61849975585938],[-384,-384]]]},{"polygons":[[[-351.2550048828125,384],[-384,384],[-384,-384],[-100.05599975585938,-384],[-287.0849914550781,85.55989837646484],[-327.5790100097656,215.76100158691406],[-350.12298583984375,332.7510070800781]],[[99.61849975585938,-384],[384,-384],[384,384],[-161.12100219726562,384],[-152.343994140625,310.4779968261719],[-107.79900360107422,166.1529998779297],[-47.21760177612305,5.790830135345459]]],"shapes":[[[-384,-384],[-100.05599975585938,-384],[-287.0849914550781,85.55989837646484]],[[-384,384],[-384,-384],[-287.0849914550781,85.55989837646484],[-327.5790100097656,215.76100158691406]],[[-384,384],[-327.5790100097656,215.76100158691406],[-350.12298583984375,332.7510070800781]],[[-350.12298583984375,332.7510070800781],[-351.2550048828125,384],[-384,384]],[[384,-384],[384,384],[-161.12100219726562,384],[-152.343994140625,310.4779968261719],[-107.79900360107422,166.1529998779297],[-47.21760177612305,5.790830135345459]],[[-47.21760177612305,5.790830135345459],[99.61849975585938,-384],[384,-384]]]},{"polygons":[[[-384,351.2550048828125],[-384,384],[384,384],[384,100.05599975585938],[-85.55989837646484,287.0849914550781],[-215.76100158691406,327.5790100097656],[-332.7510070800781,350.12298583984375]],[[384,-99.61849975585938],[384,-384],[-384,-384],[-384,161.12100219726562],[-310.4779968261719,152.343994140625],[-166.1529998779297,107.79900360107422],[-5.790830135345459,47.21760177612305]]],"shapes":[[[-85.55989837646484,287.0849914550781],[384,100.05599975585938],[384,384]],[[-85.55989837646484,287.0849914550781],[384,384],[-384,384],[-215.76100158691406,327.5790100097656]],[[-332.7510070800781,350.12298583984375],[-215.76100158691406,327.5790100097656],[-384,384]],[[-384,351.2550048828125],[-332.7510070800781,350.12298583984375],[-384,384]],[[-166.1529998779297,107.79900360107422],[-310.4779968261719,152.343994140625],[-384,161.12100219726562],[-384,-384],[384,-384],[-5.790830135345459,47.21760177612305]],[[384,-99.61849975585938],[-5.790830135345459,47.21760177612305],[384,-384]]]},{"polygons":[[[-351.2550048828125,-384],[-384,-384],[-384,384],[-100.05599975585938,384],[-287.0849914550781,-85.55989837646484],[-327.5790100097656,-215.76100158691406],[-350.12298583984375,-332.7510070800781]],[[99.61849975585938,384],[384,384],[384,-384],[-161.12100219726562,-384],[-152.343994140625,-310.4779968261719],[-107.79900360107422,-166.1529998779297],[-47.21760177612305,-5.790830135345459]]],"shapes":[[[-287.0849914550781,-85.55989837646484],[-100.05599975585938,384],[-384,384]],[[-287.0849914550781,-85.55989837646484],[-384,384],[-384,-384],[-327.5790100097656,-215.76100158691406]],[[-350.12298583984375,-332.7510070800781],[-327.5790100097656,-215.76100158691406],[-384,-384]],[[-351.2550048828125,-384],[-350.12298583984375,-332.7510070800781],[-384,-384]],[[-107.79900360107422,-166.1529998779297],[-152.343994140625,-310.4779968261719],[-161.12100219726562,-384],[384,-384],[384,384],[-47.21760177612305,-5.790830135345459]],[[99.61849975585938,384],[-47.21760177612305,-5.790830135345459],[384,384]]]},{"polygons":[[[351.2550048828125,384],[384,384],[384,-384],[100.05599975585938,-384],[287.0849914550781,85.55989837646484],[327.5790100097656,215.76100158691406],[350.12298583984375,332.7510070800781]],[[-99.61849975585938,-384],[-384,-384],[-384,384],[161.12100219726562,384],[152.343994140625,310.4779968261719],[107.79900360107422,166.1529998779297],[47.21760177612305,5.790830135345459]]],"shapes":[[[287.0849914550781,85.55989837646484],[100.05599975585938,-384],[384,-384]],[[287.0849914550781,85.55989837646484],[384,-384],[384,384],[327.5790100097656,215.76100158691406]],[[350.12298583984375,332.7510070800781],[327.5790100097656,215.76100158691406],[384,384]],[[351.2550048828125,384],[350.12298583984375,332.7510070800781],[384,384]],[[107.79900360107422,166.1529998779297],[152.343994140625,310.4779968261719],[161.12100219726562,384],[-384,384],[-384,-384],[47.21760177612305,5.790830135345459]],[[-99.61849975585938,-384],[47.21760177612305,5.790830135345459],[-384,-384]]]},{"polygons":[[[351.2550048828125,-384],[384,-384],[384,384],[100.05599975585938,384],[287.0849914550781,-85.55989837646484],[327.5790100097656,-215.76100158691406],[350.12298583984375,-332.7510070800781]],[[-99.61849975585938,384],[-384,384],[-384,-384],[161.12100219726562,-384],[152.343994140625,-310.4779968261719],[107.79900360107422,-166.1529998779297],[47.21760177612305,-5.790830135345459]]],"shapes":[[[384,384],[100.05599975585938,384],[287.0849914550781,-85.55989837646484]],[[384,-384],[384,384],[287.0849914550781,-85.55989837646484],[327.5790100097656,-215.76100158691406]],[[384,-384],[327.5790100097656,-215.76100158691406],[350.12298583984375,-332.7510070800781]],[[350.12298583984375,-332.7510070800781],[351.2550048828125,-384],[384,-384]],[[-384,384],[-384,-384],[161.12100219726562,-384],[152.343994140625,-310.4779968261719],[107.79900360107422,-166.1529998779297],[47.21760177612305,-5.790830135345459]],[[47.21760177612305,-5.790830135345459],[-99.61849975585938,384],[-384,384]]]},{"polygons":[[[-352,-384],[-352,-208],[-368,-208],[-368,-128],[-384,-112],[-384,-384]],[[-160,-384],[-160,-224],[-160,-80],[-192,16],[-256,80],[-320,96],[-384,96],[-384,384],[384,384],[384,-384]]],"shapes":[[[-368,-128],[-384,-112],[-384,-384],[-368,-208]],[[-368,-208],[-384,-384],[-352,-384],[-352,-208]],[[-160,-384],[384,-384],[384,384],[-160,-224]],[[-160,-80],[-160,-224],[384,384]],[[-192,16],[-160,-80],[384,384]],[[-192,16],[384,384],[-384,384],[-256,80]],[[-320,96],[-256,80],[-384,384]],[[-320,96],[-384,384],[-384,96]]]},{"polygons":[[[352,-384],[352,-208],[368,-208],[368,-128],[384,-112],[384,-384]],[[160,-384],[160,-224],[160,-80],[192,16],[256,80],[320,96],[384,96],[384,384],[-384,384],[-384,-384]]],"shapes":[[[384,-384],[384,-112],[368,-128],[368,-208]],[[352,-384],[384,-384],[368,-208],[352,-208]],[[-384,-384],[160,-384],[160,-224],[-384,384]],[[-384,384],[160,-224],[160,-80]],[[-384,384],[160,-80],[192,16]],[[384,384],[-384,384],[192,16],[256,80]],[[384,384],[256,80],[320,96]],[[384,384],[320,96],[384,96]]]},{"polygons":[[[-352,384],[-352,208],[-368,208],[-368,128],[-384,112],[-384,384]],[[-160,384],[-160,224],[-160,80],[-192,-16],[-256,-80],[-320,-96],[-384,-96],[-384,-384],[384,-384],[384,384]]],"shapes":[[[-384,384],[-384,112],[-368,128],[-368,208]],[[-352,384],[-384,384],[-368,208],[-352,208]],[[384,384],[-160,384],[-160,224],[384,-384]],[[384,-384],[-160,224],[-160,80]],[[384,-384],[-160,80],[-192,-16]],[[-384,-384],[384,-384],[-192,-16],[-256,-80]],[[-384,-384],[-256,-80],[-320,-96]],[[-384,-384],[-320,-96],[-384,-96]]]},{"polygons":[[[-384,-352],[-208,-352],[-208,-368],[-128,-368],[-112,-384],[-384,-384]],[[-384,-160],[-224,-160],[-80,-160],[16,-192],[80,-256],[96,-320],[96,-384],[384,-384],[384,384],[-384,384]]],"shapes":[[[-384,-384],[-112,-384],[-128,-368],[-208,-368]],[[-384,-352],[-384,-384],[-208,-368],[-208,-352]],[[-384,384],[-384,-160],[-224,-160],[384,384]],[[384,384],[-224,-160],[-80,-160]],[[384,384],[-80,-160],[16,-192]],[[384,-384],[384,384],[16,-192],[80,-256]],[[384,-384],[80,-256],[96,-320]],[[384,-384],[96,-320],[96,-384]]]},{"polygons":[[[352,384],[352,208],[368,208],[368,128],[384,112],[384,384]],[[160,384],[160,224],[160,80],[192,-16],[256,-80],[320,-96],[384,-96],[384,-384],[-384,-384],[-384,384]]],"shapes":[[[368,128],[384,112],[384,384],[368,208]],[[368,208],[384,384],[352,384],[352,208]],[[160,384],[-384,384],[-384,-384],[160,224]],[[160,80],[160,224],[-384,-384]],[[192,-16],[160,80],[-384,-384]],[[192,-16],[-384,-384],[384,-384],[256,-80]],[[320,-96],[256,-80],[384,-384]],[[320,-96],[384,-384],[384,-96]]]},{"polygons":[[[-384,352],[-208,352],[-208,368],[-128,368],[-112,384],[-384,384]],[[-384,160],[-224,160],[-80,160],[16,192],[80,256],[96,320],[96,384],[384,384],[384,-384],[-384,-384]]],"shapes":[[[-128,368],[-112,384],[-384,384],[-208,368]],[[-208,368],[-384,384],[-384,352],[-208,352]],[[-384,160],[-384,-384],[384,-384],[-224,160]],[[-80,160],[-224,160],[384,-384]],[[16,192],[-80,160],[384,-384]],[[16,192],[384,-384],[384,384],[80,256]],[[96,320],[80,256],[384,384]],[[96,320],[384,384],[96,384]]]},{"polygons":[[[384,-352],[208,-352],[208,-368],[128,-368],[112,-384],[384,-384]],[[384,-160],[224,-160],[80,-160],[-16,-192],[-80,-256],[-96,-320],[-96,-384],[-384,-384],[-384,384],[384,384]]],"shapes":[[[128,-368],[112,-384],[384,-384],[208,-368]],[[208,-368],[384,-384],[384,-352],[208,-352]],[[384,-160],[384,384],[-384,384],[224,-160]],[[80,-160],[224,-160],[-384,384]],[[-16,-192],[80,-160],[-384,384]],[[-16,-192],[-384,384],[-384,-384],[-80,-256]],[[-96,-320],[-80,-256],[-384,-384]],[[-96,-320],[-384,-384],[-96,-384]]]},{"polygons":[[[384,352],[208,352],[208,368],[128,368],[112,384],[384,384]],[[384,160],[224,160],[80,160],[-16,192],[-80,256],[-96,320],[-96,384],[-384,384],[-384,-384],[384,-384]]],"shapes":[[[384,384],[112,384],[128,368],[208,368]],[[384,352],[384,384],[208,368],[208,352]],[[384,-384],[384,160],[224,160],[-384,-384]],[[-384,-384],[224,160],[80,160]],[[-384,-384],[80,160],[-16,192]],[[-384,384],[-384,-384],[-16,192],[-80,256]],[[-384,384],[-80,256],[-96,320]],[[-384,384],[-96,320],[-96,384]]]},{"polygons":[[[-384,96],[-288,96],[-176,80],[-96,48],[0,-16],[32,-64],[64,-128],[96,-240],[96,-384],[384,-384],[384,384],[-384,384]],[[-384,-112],[-240,-128],[-192,-144],[-160,-160],[-144,-192],[-128,-240],[-112,-336],[-112,-384],[-384,-384]]],"shapes":[[[-384,384],[-384,96],[-288,96],[384,384]],[[384,384],[-288,96],[-176,80]],[[384,384],[-176,80],[-96,48]],[[384,384],[-96,48],[0,-16]],[[384,-384],[384,384],[0,-16],[32,-64]],[[384,-384],[32,-64],[64,-128]],[[384,-384],[64,-128],[96,-240]],[[384,-384],[96,-240],[96,-384]],[[-384,-384],[-112,-384],[-112,-336],[-128,-240],[-144,-192],[-160,-160],[-192,-144],[-240,-128],[-384,-112]]]},{"polygons":[[[-96,-384],[-96,-288],[-80,-176],[-48,-96],[16,0],[64,32],[128,64],[240,96],[384,96],[384,384],[-384,384],[-384,-384]],[[112,-384],[128,-240],[144,-192],[160,-160],[192,-144],[240,-128],[336,-112],[384,-112],[384,-384]]],"shapes":[[[-384,-384],[-96,-384],[-96,-288],[-384,384]],[[-384,384],[-96,-288],[-80,-176]],[[-384,384],[-80,-176],[-48,-96]],[[-384,384],[-48,-96],[16,0]],[[384,384],[-384,384],[16,0],[64,32]],[[384,384],[64,32],[128,64]],[[384,384],[128,64],[240,96]],[[384,384],[240,96],[384,96]],[[384,-384],[384,-112],[336,-112],[240,-128],[192,-144],[160,-160],[144,-192],[128,-240],[112,-384]]]},{"polygons":[[[96,384],[96,288],[80,176],[48,96],[-16,0],[-64,-32],[-128,-64],[-240,-96],[-384,-96],[-384,-384],[384,-384],[384,384]],[[-112,384],[-128,240],[-144,192],[-160,160],[-192,144],[-240,128],[-336,112],[-384,112],[-384,384]]],"shapes":[[[384,384],[96,384],[96,288],[384,-384]],[[384,-384],[96,288],[80,176]],[[384,-384],[80,176],[48,96]],[[384,-384],[48,96],[-16,0]],[[-384,-384],[384,-384],[-16,0],[-64,-32]],[[-384,-384],[-64,-32],[-128,-64]],[[-384,-384],[-128,-64],[-240,-96]],[[-384,-384],[-240,-96],[-384,-96]],[[-384,384],[-384,112],[-336,112],[-240,128],[-192,144],[-160,160],[-144,192],[-128,240],[-112,384]]]},{"polygons":[[[384,-96],[288,-96],[176,-80],[96,-48],[0,16],[-32,64],[-64,128],[-96,240],[-96,384],[-384,384],[-384,-384],[384,-384]],[[384,112],[240,128],[192,144],[160,160],[144,192],[128,240],[112,336],[112,384],[384,384]]],"shapes":[[[384,-384],[384,-96],[288,-96],[-384,-384]],[[-384,-384],[288,-96],[176,-80]],[[-384,-384],[176,-80],[96,-48]],[[-384,-384],[96,-48],[0,16]],[[-384,384],[-384,-384],[0,16],[-32,64]],[[-384,384],[-32,64],[-64,128]],[[-384,384],[-64,128],[-96,240]],[[-384,384],[-96,240],[-96,384]],[[384,384],[112,384],[112,336],[128,240],[144,192],[160,160],[192,144],[240,128],[384,112]]]},{"polygons":[[[-384,-368],[-368,-368],[-368,-384],[-384,-384]],[[-160,-384],[-160,-288],[-176,-240],[-240,-176],[-304,-160],[-384,-160],[-384,384],[384,384],[384,-384]]],"shapes":[[[-384,-384],[-368,-384],[-368,-368],[-384,-368]],[[-160,-384],[384,-384],[384,384],[-160,-288]],[[-176,-240],[-160,-288],[384,384]],[[-176,-240],[384,384],[-384,384],[-240,-176]],[[-304,-160],[-240,-176],[-384,384]],[[-304,-160],[-384,384],[-384,-160]]]},{"polygons":[[[368,-384],[368,-368],[384,-368],[384,-384]],[[384,-160],[288,-160],[240,-176],[176,-240],[160,-304],[160,-384],[-384,-384],[-384,384],[384,384]]],"shapes":[[[384,-384],[384,-368],[368,-368],[368,-384]],[[384,-160],[384,384],[-384,384],[288,-160]],[[240,-176],[288,-160],[-384,384]],[[240,-176],[-384,384],[-384,-384],[176,-240]],[[160,-304],[176,-240],[-384,-384]],[[160,-304],[-384,-384],[160,-384]]]},{"polygons":[[[384,368],[368,368],[368,384],[384,384]],[[160,384],[160,288],[176,240],[240,176],[304,160],[384,160],[384,-384],[-384,-384],[-384,384]]],"shapes":[[[384,384],[368,384],[368,368],[384,368]],[[160,384],[-384,384],[-384,-384],[160,288]],[[176,240],[160,288],[-384,-384]],[[176,240],[-384,-384],[384,-384],[240,176]],[[304,160],[240,176],[384,-384]],[[304,160],[384,-384],[384,160]]]},{"polygons":[[[-368,384],[-368,368],[-384,368],[-384,384]],[[-384,160],[-288,160],[-240,176],[-176,240],[-160,304],[-160,384],[384,384],[384,-384],[-384,-384]]],"shapes":[[[-384,384],[-384,368],[-368,368],[-368,384]],[[-384,160],[-384,-384],[384,-384],[-288,160]],[[-240,176],[-288,160],[384,-384]],[[-240,176],[384,-384],[384,384],[-176,240]],[[-160,304],[-176,240],[384,384]],[[-160,304],[384,384],[-160,384]]]},{"polygons":[[[-384,-96],[-128,-96],[-20.5,-101.5],[-21.5,-120.5],[63,-129.5],[112,-144],[128,-160],[139,-230],[159,-229.5],[160,-384],[-384,-384]],[[-384,95],[-99.5,92.5],[107,74.5],[212.5,41.5],[291.5,-32],[336,-130],[349.5,-263],[350.5,-384],[384,-384],[384,384],[-384,384]]],"shapes":[[[160,-384],[159,-229.5],[139,-230],[-384,-384]],[[-128,-96],[-384,-96],[-384,-384],[139,-230],[128,-160],[-21.5,-120.5]],[[-21.5,-120.5],[-20.5,-101.5],[-128,-96]],[[63,-129.5],[-21.5,-120.5],[128,-160],[112,-144]],[[350.5,-384],[384,-384],[384,384],[349.5,-263]],[[336,-130],[349.5,-263],[384,384]],[[291.5,-32],[336,-130],[384,384]],[[212.5,41.5],[291.5,-32],[384,384]],[[212.5,41.5],[384,384],[-384,384],[107,74.5]],[[-99.5,92.5],[107,74.5],[-384,384]],[[-384,384],[-384,95],[-99.5,92.5]]]},{"polygons":[[[384,-96],[128,-96],[20.5,-101.5],[21.5,-120.5],[-63,-129.5],[-112,-144],[-128,-160],[-139,-230],[-159,-229.5],[-160,-384],[384,-384]],[[384,95],[99.5,92.5],[-107,74.5],[-212.5,41.5],[-291.5,-32],[-336,-130],[-349.5,-263],[-350.5,-384],[-384,-384],[-384,384],[384,384]]],"shapes":[[[-139,-230],[-159,-229.5],[-160,-384],[384,-384]],[[-128,-160],[-139,-230],[384,-384],[384,-96],[128,-96],[21.5,-120.5]],[[128,-96],[20.5,-101.5],[21.5,-120.5]],[[-128,-160],[21.5,-120.5],[-63,-129.5],[-112,-144]],[[384,95],[384,384],[-384,384],[99.5,92.5]],[[-107,74.5],[99.5,92.5],[-384,384]],[[-212.5,41.5],[-107,74.5],[-384,384]],[[-384,384],[-384,-384],[-350.5,-384],[-349.5,-263]],[[-384,384],[-349.5,-263],[-336,-130]],[[-384,384],[-336,-130],[-291.5,-32]],[[-212.5,41.5],[-384,384],[-291.5,-32]]]},{"polygons":[[[-384,96],[-128,96],[-20.5,101.5],[-21.5,120.5],[63,129.5],[112,144],[128,160],[139,230],[159,229.5],[160,384],[-384,384]],[[-384,-95],[-99.5,-92.5],[107,-74.5],[212.5,-41.5],[291.5,32],[336,130],[349.5,263],[350.5,384],[384,384],[384,-384],[-384,-384]]],"shapes":[[[139,230],[159,229.5],[160,384],[-384,384]],[[128,160],[139,230],[-384,384],[-384,96],[-128,96],[-21.5,120.5]],[[-128,96],[-20.5,101.5],[-21.5,120.5]],[[128,160],[-21.5,120.5],[63,129.5],[112,144]],[[-384,-95],[-384,-384],[384,-384],[-99.5,-92.5]],[[107,-74.5],[-99.5,-92.5],[384,-384]],[[212.5,-41.5],[107,-74.5],[384,-384]],[[384,-384],[384,384],[350.5,384],[349.5,263]],[[384,-384],[349.5,263],[336,130]],[[384,-384],[336,130],[291.5,32]],[[212.5,-41.5],[384,-384],[291.5,32]]]},{"polygons":[[[-96,-384],[-96,-128],[-101.5,-20.5],[-120.5,-21.5],[-129.5,63],[-144,112],[-160,128],[-230,139],[-229.5,159],[-384,160],[-384,-384]],[[95,-384],[92.5,-99.5],[74.5,107],[41.5,212.5],[-32,291.5],[-130,336],[-263,349.5],[-384,350.5],[-384,384],[384,384],[384,-384]]],"shapes":[[[-230,139],[-229.5,159],[-384,160],[-384,-384]],[[-160,128],[-230,139],[-384,-384],[-96,-384],[-96,-128],[-120.5,-21.5]],[[-96,-128],[-101.5,-20.5],[-120.5,-21.5]],[[-160,128],[-120.5,-21.5],[-129.5,63],[-144,112]],[[95,-384],[384,-384],[384,384],[92.5,-99.5]],[[74.5,107],[92.5,-99.5],[384,384]],[[41.5,212.5],[74.5,107],[384,384]],[[384,384],[-384,384],[-384,350.5],[-263,349.5]],[[384,384],[-263,349.5],[-130,336]],[[384,384],[-130,336],[-32,291.5]],[[41.5,212.5],[384,384],[-32,291.5]]]},{"polygons":[[[384,96],[128,96],[20.5,101.5],[21.5,120.5],[-63,129.5],[-112,144],[-128,160],[-139,230],[-159,229.5],[-160,384],[384,384]],[[384,-95],[99.5,-92.5],[-107,-74.5],[-212.5,-41.5],[-291.5,32],[-336,130],[-349.5,263],[-350.5,384],[-384,384],[-384,-384],[384,-384]]],"shapes":[[[-160,384],[-159,229.5],[-139,230],[384,384]],[[128,96],[384,96],[384,384],[-139,230],[-128,160],[21.5,120.5]],[[21.5,120.5],[20.5,101.5],[128,96]],[[-63,129.5],[21.5,120.5],[-128,160],[-112,144]],[[-350.5,384],[-384,384],[-384,-384],[-349.5,263]],[[-336,130],[-349.5,263],[-384,-384]],[[-291.5,32],[-336,130],[-384,-384]],[[-212.5,-41.5],[-291.5,32],[-384,-384]],[[-212.5,-41.5],[-384,-384],[384,-384],[-107,-74.5]],[[99.5,-92.5],[-107,-74.5],[384,-384]],[[384,-384],[384,-95],[99.5,-92.5]]]},{"polygons":[[[-96,384],[-96,128],[-101.5,20.5],[-120.5,21.5],[-129.5,-63],[-144,-112],[-160,-128],[-230,-139],[-229.5,-159],[-384,-160],[-384,384]],[[95,384],[92.5,99.5],[74.5,-107],[41.5,-212.5],[-32,-291.5],[-130,-336],[-263,-349.5],[-384,-350.5],[-384,-384],[384,-384],[384,384]]],"shapes":[[[-384,-160],[-229.5,-159],[-230,-139],[-384,384]],[[-96,128],[-96,384],[-384,384],[-230,-139],[-160,-128],[-120.5,21.5]],[[-120.5,21.5],[-101.5,20.5],[-96,128]],[[-129.5,-63],[-120.5,21.5],[-160,-128],[-144,-112]],[[-384,-350.5],[-384,-384],[384,-384],[-263,-349.5]],[[-130,-336],[-263,-349.5],[384,-384]],[[-32,-291.5],[-130,-336],[384,-384]],[[41.5,-212.5],[-32,-291.5],[384,-384]],[[41.5,-212.5],[384,-384],[384,384],[74.5,-107]],[[92.5,99.5],[74.5,-107],[384,384]],[[384,384],[95,384],[92.5,99.5]]]},{"polygons":[[[96,-384],[96,-128],[101.5,-20.5],[120.5,-21.5],[129.5,63],[144,112],[160,128],[230,139],[229.5,159],[384,160],[384,-384]],[[-95,-384],[-92.5,-99.5],[-74.5,107],[-41.5,212.5],[32,291.5],[130,336],[263,349.5],[384,350.5],[384,384],[-384,384],[-384,-384]]],"shapes":[[[384,160],[229.5,159],[230,139],[384,-384]],[[96,-128],[96,-384],[384,-384],[230,139],[160,128],[120.5,-21.5]],[[120.5,-21.5],[101.5,-20.5],[96,-128]],[[129.5,63],[120.5,-21.5],[160,128],[144,112]],[[384,350.5],[384,384],[-384,384],[263,349.5]],[[130,336],[263,349.5],[-384,384]],[[32,291.5],[130,336],[-384,384]],[[-41.5,212.5],[32,291.5],[-384,384]],[[-41.5,212.5],[-384,384],[-384,-384],[-74.5,107]],[[-92.5,-99.5],[-74.5,107],[-384,-384]],[[-384,-384],[-95,-384],[-92.5,-99.5]]]},{"polygons":[[[96,384],[96,128],[101.5,20.5],[120.5,21.5],[129.5,-63],[144,-112],[160,-128],[230,-139],[229.5,-159],[384,-160],[384,384]],[[-95,384],[-92.5,99.5],[-74.5,-107],[-41.5,-212.5],[32,-291.5],[130,-336],[263,-349.5],[384,-350.5],[384,-384],[-384,-384],[-384,384]]],"shapes":[[[230,-139],[229.5,-159],[384,-160],[384,384]],[[160,-128],[230,-139],[384,384],[96,384],[96,128],[120.5,21.5]],[[96,128],[101.5,20.5],[120.5,21.5]],[[160,-128],[120.5,21.5],[129.5,-63],[144,-112]],[[-95,384],[-384,384],[-384,-384],[-92.5,99.5]],[[-74.5,-107],[-92.5,99.5],[-384,-384]],[[-41.5,-212.5],[-74.5,-107],[-384,-384]],[[-384,-384],[384,-384],[384,-350.5],[263,-349.5]],[[-384,-384],[263,-349.5],[130,-336]],[[-384,-384],[130,-336],[32,-291.5]],[[-41.5,-212.5],[-384,-384],[32,-291.5]]]},{"polygons":[[[-384,-96],[384,-96],[384,-384],[-384,-384]],[[-384,96],[384,96],[384,384],[-384,384]]],"shapes":[[[-384,-384],[384,-384],[384,-96],[-384,-96]],[[-384,96],[384,96],[384,384],[-384,384]]]},{"polygons":[[[96,-384],[96,384],[384,384],[384,-384]],[[-96,-384],[-96,384],[-384,384],[-384,-384]]],"shapes":[[[384,-384],[384,384],[96,384],[96,-384]],[[-96,-384],[-96,384],[-384,384],[-384,-384]]]},{"polygons":[[[-384,-96],[-384,-384],[384,-384],[384,-352],[274,-348],[140.5,-318.5],[16,-256],[-104.5,-173.5],[-230,-111.5]],[[-384,96],[-215,81.5],[-79,33.5],[49.5,-47],[175,-121.5],[291,-152],[384,-160],[384,384],[-384,384]]],"shapes":[[[-230,-111.5],[-384,-96],[-384,-384],[16,-256],[-104.5,-173.5]],[[-384,-384],[384,-384],[384,-352],[274,-348]],[[-384,-384],[274,-348],[140.5,-318.5]],[[-384,-384],[140.5,-318.5],[16,-256]],[[49.5,-47],[175,-121.5],[291,-152],[384,-160],[384,384],[-384,384],[-79,33.5]],[[-215,81.5],[-79,33.5],[-384,384]],[[-384,384],[-384,96],[-215,81.5]]]},{"polygons":[[[384,-96],[384,-384],[-384,-384],[-384,-352],[-274,-348],[-140.5,-318.5],[-16,-256],[104.5,-173.5],[230,-111.5]],[[384,96],[215,81.5],[79,33.5],[-49.5,-47],[-175,-121.5],[-291,-152],[-384,-160],[-384,384],[384,384]]],"shapes":[[[-16,-256],[384,-384],[384,-96],[230,-111.5],[104.5,-173.5]],[[-384,-352],[-384,-384],[384,-384],[-274,-348]],[[-140.5,-318.5],[-274,-348],[384,-384]],[[-16,-256],[-140.5,-318.5],[384,-384]],[[384,384],[-384,384],[-384,-160],[-291,-152],[-175,-121.5],[-49.5,-47],[79,33.5]],[[384,384],[79,33.5],[215,81.5]],[[384,96],[384,384],[215,81.5]]]},{"polygons":[[[-384,96],[-384,384],[384,384],[384,352],[274,348],[140.5,318.5],[16,256],[-104.5,173.5],[-230,111.5]],[[-384,-96],[-215,-81.5],[-79,-33.5],[49.5,47],[175,121.5],[291,152],[384,160],[384,-384],[-384,-384]]],"shapes":[[[16,256],[-384,384],[-384,96],[-230,111.5],[-104.5,173.5]],[[384,352],[384,384],[-384,384],[274,348]],[[140.5,318.5],[274,348],[-384,384]],[[16,256],[140.5,318.5],[-384,384]],[[-384,-384],[384,-384],[384,160],[291,152],[175,121.5],[49.5,47],[-79,-33.5]],[[-384,-384],[-79,-33.5],[-215,-81.5]],[[-384,-96],[-384,-384],[-215,-81.5]]]},{"polygons":[[[-96,-384],[-384,-384],[-384,384],[-352,384],[-348,274],[-318.5,140.5],[-256,16],[-173.5,-104.5],[-111.5,-230]],[[96,-384],[81.5,-215],[33.5,-79],[-47,49.5],[-121.5,175],[-152,291],[-160,384],[384,384],[384,-384]]],"shapes":[[[-256,16],[-384,-384],[-96,-384],[-111.5,-230],[-173.5,-104.5]],[[-352,384],[-384,384],[-384,-384],[-348,274]],[[-318.5,140.5],[-348,274],[-384,-384]],[[-256,16],[-318.5,140.5],[-384,-384]],[[384,-384],[384,384],[-160,384],[-152,291],[-121.5,175],[-47,49.5],[33.5,-79]],[[384,-384],[33.5,-79],[81.5,-215]],[[96,-384],[384,-384],[81.5,-215]]]},{"polygons":[[[384,96],[384,384],[-384,384],[-384,352],[-274,348],[-140.5,318.5],[-16,256],[104.5,173.5],[230,111.5]],[[384,-96],[215,-81.5],[79,-33.5],[-49.5,47],[-175,121.5],[-291,152],[-384,160],[-384,-384],[384,-384]]],"shapes":[[[230,111.5],[384,96],[384,384],[-16,256],[104.5,173.5]],[[384,384],[-384,384],[-384,352],[-274,348]],[[384,384],[-274,348],[-140.5,318.5]],[[384,384],[-140.5,318.5],[-16,256]],[[-49.5,47],[-175,121.5],[-291,152],[-384,160],[-384,-384],[384,-384],[79,-33.5]],[[215,-81.5],[79,-33.5],[384,-384]],[[384,-384],[384,-96],[215,-81.5]]]},{"polygons":[[[-96,384],[-384,384],[-384,-384],[-352,-384],[-348,-274],[-318.5,-140.5],[-256,-16],[-173.5,104.5],[-111.5,230]],[[96,384],[81.5,215],[33.5,79],[-47,-49.5],[-121.5,-175],[-152,-291],[-160,-384],[384,-384],[384,384]]],"shapes":[[[-111.5,230],[-96,384],[-384,384],[-256,-16],[-173.5,104.5]],[[-384,384],[-384,-384],[-352,-384],[-348,-274]],[[-384,384],[-348,-274],[-318.5,-140.5]],[[-384,384],[-318.5,-140.5],[-256,-16]],[[-47,-49.5],[-121.5,-175],[-152,-291],[-160,-384],[384,-384],[384,384],[33.5,79]],[[81.5,215],[33.5,79],[384,384]],[[384,384],[96,384],[81.5,215]]]},{"polygons":[[[96,-384],[384,-384],[384,384],[352,384],[348,274],[318.5,140.5],[256,16],[173.5,-104.5],[111.5,-230]],[[-96,-384],[-81.5,-215],[-33.5,-79],[47,49.5],[121.5,175],[152,291],[160,384],[-384,384],[-384,-384]]],"shapes":[[[111.5,-230],[96,-384],[384,-384],[256,16],[173.5,-104.5]],[[384,-384],[384,384],[352,384],[348,274]],[[384,-384],[348,274],[318.5,140.5]],[[384,-384],[318.5,140.5],[256,16]],[[47,49.5],[121.5,175],[152,291],[160,384],[-384,384],[-384,-384],[-33.5,-79]],[[-81.5,-215],[-33.5,-79],[-384,-384]],[[-384,-384],[-96,-384],[-81.5,-215]]]},{"polygons":[[[96,384],[384,384],[384,-384],[352,-384],[348,-274],[318.5,-140.5],[256,-16],[173.5,104.5],[111.5,230]],[[-96,384],[-81.5,215],[-33.5,79],[47,-49.5],[121.5,-175],[152,-291],[160,-384],[-384,-384],[-384,384]]],"shapes":[[[256,-16],[384,384],[96,384],[111.5,230],[173.5,104.5]],[[352,-384],[384,-384],[384,384],[348,-274]],[[318.5,-140.5],[348,-274],[384,384]],[[256,-16],[318.5,-140.5],[384,384]],[[-384,384],[-384,-384],[160,-384],[152,-291],[121.5,-175],[47,-49.5],[-33.5,79]],[[-384,384],[-33.5,79],[-81.5,215]],[[-96,384],[-384,384],[-81.5,215]]]},{"polygons":[[[-384,-352],[-320,-352],[-248.5,-327.5],[-194,-265],[-170,-159],[-161.5,65.5],[-160,384],[384,384],[384,-384],[-384,-384]],[[-384,-140],[-376.5,-134.5],[-375.5,-118.5],[-357,-118],[-352.5,77.5],[-352,384],[-384,384]]],"shapes":[[[-384,-352],[-384,-384],[384,-384],[-320,-352]],[[-248.5,-327.5],[-320,-352],[384,-384]],[[-194,-265],[-248.5,-327.5],[384,-384],[384,384]],[[-170,-159],[-194,-265],[384,384]],[[-161.5,65.5],[-170,-159],[384,384]],[[-161.5,65.5],[384,384],[-160,384]],[[-384,384],[-384,-140],[-376.5,-134.5],[-375.5,-118.5],[-352,384]],[[-352.5,77.5],[-352,384],[-375.5,-118.5],[-357,-118]]]},{"polygons":[[[384,-352],[320,-352],[248.5,-327.5],[194,-265],[170,-159],[161.5,65.5],[160,384],[-384,384],[-384,-384],[384,-384]],[[384,-140],[376.5,-134.5],[375.5,-118.5],[357,-118],[352.5,77.5],[352,384],[384,384]]],"shapes":[[[384,-384],[384,-352],[320,-352],[-384,-384]],[[-384,-384],[320,-352],[248.5,-327.5]],[[-384,-384],[248.5,-327.5],[194,-265],[-384,384]],[[-384,384],[194,-265],[170,-159]],[[-384,384],[170,-159],[161.5,65.5]],[[-384,384],[161.5,65.5],[160,384]],[[375.5,-118.5],[376.5,-134.5],[384,-140],[384,384],[352,384]],[[375.5,-118.5],[352,384],[352.5,77.5],[357,-118]]]},{"polygons":[[[-384,352],[-320,352],[-248.5,327.5],[-194,265],[-170,159],[-161.5,-65.5],[-160,-384],[384,-384],[384,384],[-384,384]],[[-384,140],[-376.5,134.5],[-375.5,118.5],[-357,118],[-352.5,-77.5],[-352,-384],[-384,-384]]],"shapes":[[[-384,384],[-384,352],[-320,352],[384,384]],[[384,384],[-320,352],[-248.5,327.5]],[[384,384],[-248.5,327.5],[-194,265],[384,-384]],[[384,-384],[-194,265],[-170,159]],[[384,-384],[-170,159],[-161.5,-65.5]],[[384,-384],[-161.5,-65.5],[-160,-384]],[[-375.5,118.5],[-376.5,134.5],[-384,140],[-384,-384],[-352,-384]],[[-375.5,118.5],[-352,-384],[-352.5,-77.5],[-357,118]]]},{"polygons":[[[-352,-384],[-352,-320],[-327.5,-248.5],[-265,-194],[-159,-170],[65.5,-161.5],[384,-160],[384,384],[-384,384],[-384,-384]],[[-140,-384],[-134.5,-376.5],[-118.5,-375.5],[-118,-357],[77.5,-352.5],[384,-352],[384,-384]]],"shapes":[[[-384,-384],[-352,-384],[-352,-320],[-384,384]],[[-384,384],[-352,-320],[-327.5,-248.5]],[[-384,384],[-327.5,-248.5],[-265,-194],[384,384]],[[384,384],[-265,-194],[-159,-170]],[[384,384],[-159,-170],[65.5,-161.5]],[[384,384],[65.5,-161.5],[384,-160]],[[-118.5,-375.5],[-134.5,-376.5],[-140,-384],[384,-384],[384,-352]],[[-118.5,-375.5],[384,-352],[77.5,-352.5],[-118,-357]]]},{"polygons":[[[384,352],[320,352],[248.5,327.5],[194,265],[170,159],[161.5,-65.5],[160,-384],[-384,-384],[-384,384],[384,384]],[[384,140],[376.5,134.5],[375.5,118.5],[357,118],[352.5,-77.5],[352,-384],[384,-384]]],"shapes":[[[384,352],[384,384],[-384,384],[320,352]],[[248.5,327.5],[320,352],[-384,384]],[[194,265],[248.5,327.5],[-384,384],[-384,-384]],[[170,159],[194,265],[-384,-384]],[[161.5,-65.5],[170,159],[-384,-384]],[[161.5,-65.5],[-384,-384],[160,-384]],[[384,-384],[384,140],[376.5,134.5],[375.5,118.5],[352,-384]],[[352.5,-77.5],[352,-384],[375.5,118.5],[357,118]]]},{"polygons":[[[-352,384],[-352,320],[-327.5,248.5],[-265,194],[-159,170],[65.5,161.5],[384,160],[384,-384],[-384,-384],[-384,384]],[[-140,384],[-134.5,376.5],[-118.5,375.5],[-118,357],[77.5,352.5],[384,352],[384,384]]],"shapes":[[[-352,384],[-384,384],[-384,-384],[-352,320]],[[-327.5,248.5],[-352,320],[-384,-384]],[[-265,194],[-327.5,248.5],[-384,-384],[384,-384]],[[-159,170],[-265,194],[384,-384]],[[65.5,161.5],[-159,170],[384,-384]],[[65.5,161.5],[384,-384],[384,160]],[[384,384],[-140,384],[-134.5,376.5],[-118.5,375.5],[384,352]],[[77.5,352.5],[384,352],[-118.5,375.5],[-118,357]]]},{"polygons":[[[352,-384],[352,-320],[327.5,-248.5],[265,-194],[159,-170],[-65.5,-161.5],[-384,-160],[-384,384],[384,384],[384,-384]],[[140,-384],[134.5,-376.5],[118.5,-375.5],[118,-357],[-77.5,-352.5],[-384,-352],[-384,-384]]],"shapes":[[[352,-384],[384,-384],[384,384],[352,-320]],[[327.5,-248.5],[352,-320],[384,384]],[[265,-194],[327.5,-248.5],[384,384],[-384,384]],[[159,-170],[265,-194],[-384,384]],[[-65.5,-161.5],[159,-170],[-384,384]],[[-65.5,-161.5],[-384,384],[-384,-160]],[[-384,-384],[140,-384],[134.5,-376.5],[118.5,-375.5],[-384,-352]],[[-77.5,-352.5],[-384,-352],[118.5,-375.5],[118,-357]]]},{"polygons":[[[352,384],[352,320],[327.5,248.5],[265,194],[159,170],[-65.5,161.5],[-384,160],[-384,-384],[384,-384],[384,384]],[[140,384],[134.5,376.5],[118.5,375.5],[118,357],[-77.5,352.5],[-384,352],[-384,384]]],"shapes":[[[384,384],[352,384],[352,320],[384,-384]],[[384,-384],[352,320],[327.5,248.5]],[[384,-384],[327.5,248.5],[265,194],[-384,-384]],[[-384,-384],[265,194],[159,170]],[[-384,-384],[159,170],[-65.5,161.5]],[[-384,-384],[-65.5,161.5],[-384,160]],[[118.5,375.5],[134.5,376.5],[140,384],[-384,384],[-384,352]],[[118.5,375.5],[-384,352],[-77.5,352.5],[118,357]]]},{"polygons":[[[-384,-384],[384,-384],[384,384],[352,384],[351,273],[313,80],[218.5,-109],[76,-242.5],[-96,-320],[-288,-352],[-384,-352]],[[-384,-138],[-384,384],[139,384],[140.5,318],[113,165.5],[50,31],[-61,-67.5],[-224,-128]]],"shapes":[[[-384,-352],[-384,-384],[384,-384],[-288,-352]],[[-96,-320],[-288,-352],[384,-384]],[[76,-242.5],[-96,-320],[384,-384]],[[218.5,-109],[76,-242.5],[384,-384]],[[384,-384],[384,384],[352,384],[351,273]],[[384,-384],[351,273],[313,80]],[[218.5,-109],[384,-384],[313,80]],[[-224,-128],[-61,-67.5],[50,31],[113,165.5],[140.5,318],[139,384],[-384,384],[-384,-138]]]},{"polygons":[[[384,-384],[384,384],[-384,384],[-384,352],[-273,351],[-80,313],[109,218.5],[242.5,76],[320,-96],[352,-288],[352,-384]],[[138,-384],[-384,-384],[-384,139],[-318,140.5],[-165.5,113],[-31,50],[67.5,-61],[128,-224]]],"shapes":[[[352,-384],[384,-384],[384,384],[352,-288]],[[320,-96],[352,-288],[384,384]],[[242.5,76],[320,-96],[384,384]],[[109,218.5],[242.5,76],[384,384]],[[384,384],[-384,384],[-384,352],[-273,351]],[[384,384],[-273,351],[-80,313]],[[109,218.5],[384,384],[-80,313]],[[128,-224],[67.5,-61],[-31,50],[-165.5,113],[-318,140.5],[-384,139],[-384,-384],[138,-384]]]},{"polygons":[[[384,384],[-384,384],[-384,-384],[-352,-384],[-351,-273],[-313,-80],[-218.5,109],[-76,242.5],[96,320],[288,352],[384,352]],[[384,138],[384,-384],[-139,-384],[-140.5,-318],[-113,-165.5],[-50,-31],[61,67.5],[224,128]]],"shapes":[[[384,352],[384,384],[-384,384],[288,352]],[[96,320],[288,352],[-384,384]],[[-76,242.5],[96,320],[-384,384]],[[-218.5,109],[-76,242.5],[-384,384]],[[-384,384],[-384,-384],[-352,-384],[-351,-273]],[[-384,384],[-351,-273],[-313,-80]],[[-218.5,109],[-384,384],[-313,-80]],[[224,128],[61,67.5],[-50,-31],[-113,-165.5],[-140.5,-318],[-139,-384],[384,-384],[384,138]]]},{"polygons":[[[-384,384],[-384,-384],[384,-384],[384,-352],[273,-351],[80,-313],[-109,-218.5],[-242.5,-76],[-320,96],[-352,288],[-352,384]],[[-138,384],[384,384],[384,-139],[318,-140.5],[165.5,-113],[31,-50],[-67.5,61],[-128,224]]],"shapes":[[[-352,384],[-384,384],[-384,-384],[-352,288]],[[-320,96],[-352,288],[-384,-384]],[[-242.5,-76],[-320,96],[-384,-384]],[[-109,-218.5],[-242.5,-76],[-384,-384]],[[-384,-384],[384,-384],[384,-352],[273,-351]],[[-384,-384],[273,-351],[80,-313]],[[-109,-218.5],[-384,-384],[80,-313]],[[-128,224],[-67.5,61],[31,-50],[165.5,-113],[318,-140.5],[384,-139],[384,384],[-138,384]]]},{"polygons":[[[-384,-352],[-352,-352],[-193.5,-315],[-59,-224],[74.5,-64.5],[93.5,-70.5],[163,19.5],[204.5,65.5],[288,123.5],[349,138],[384,140.5],[384,-384],[-384,-384]],[[-384,-140.5],[-307,-132.5],[-243,-102],[-163.5,-26],[-92.5,71],[-74.5,60],[19.5,178],[137.5,284],[260,338.5],[384,352],[384,384],[-384,384]]],"shapes":[[[384,-384],[384,140.5],[349,138],[288,123.5],[204.5,65.5],[163,19.5],[93.5,-70.5]],[[-384,-352],[-384,-384],[384,-384],[-352,-352]],[[-193.5,-315],[-352,-352],[384,-384]],[[-59,-224],[-193.5,-315],[384,-384]],[[-59,-224],[384,-384],[93.5,-70.5],[74.5,-64.5]],[[-384,384],[-384,-140.5],[-307,-132.5],[-243,-102],[-163.5,-26],[-92.5,71]],[[384,352],[384,384],[-384,384],[260,338.5]],[[137.5,284],[260,338.5],[-384,384]],[[19.5,178],[137.5,284],[-384,384]],[[19.5,178],[-384,384],[-92.5,71],[-74.5,60]]]},{"polygons":[[[384,-352],[352,-352],[193.5,-315],[59,-224],[-74.5,-64.5],[-93.5,-70.5],[-163,19.5],[-204.5,65.5],[-288,123.5],[-349,138],[-384,140.5],[-384,-384],[384,-384]],[[384,-140.5],[307,-132.5],[243,-102],[163.5,-26],[92.5,71],[74.5,60],[-19.5,178],[-137.5,284],[-260,338.5],[-384,352],[-384,384],[384,384]]],"shapes":[[[-163,19.5],[-204.5,65.5],[-288,123.5],[-349,138],[-384,140.5],[-384,-384],[-93.5,-70.5]],[[384,-384],[384,-352],[352,-352],[-384,-384]],[[-384,-384],[352,-352],[193.5,-315]],[[-384,-384],[193.5,-315],[59,-224]],[[-93.5,-70.5],[-384,-384],[59,-224],[-74.5,-64.5]],[[163.5,-26],[243,-102],[307,-132.5],[384,-140.5],[384,384],[92.5,71]],[[384,384],[-384,384],[-384,352],[-260,338.5]],[[384,384],[-260,338.5],[-137.5,284]],[[384,384],[-137.5,284],[-19.5,178]],[[92.5,71],[384,384],[-19.5,178],[74.5,60]]]},{"polygons":[[[352,-384],[352,-352],[315,-193.5],[224,-59],[64.5,74.5],[70.5,93.5],[-19.5,163],[-65.5,204.5],[-123.5,288],[-138,349],[-140.5,384],[384,384],[384,-384]],[[140.5,-384],[132.5,-307],[102,-243],[26,-163.5],[-71,-92.5],[-60,-74.5],[-178,19.5],[-284,137.5],[-338.5,260],[-352,384],[-384,384],[-384,-384]]],"shapes":[[[384,384],[-140.5,384],[-138,349],[-123.5,288],[-65.5,204.5],[-19.5,163],[70.5,93.5]],[[352,-384],[384,-384],[384,384],[352,-352]],[[315,-193.5],[352,-352],[384,384]],[[224,-59],[315,-193.5],[384,384]],[[224,-59],[384,384],[70.5,93.5],[64.5,74.5]],[[-384,-384],[140.5,-384],[132.5,-307],[102,-243],[26,-163.5],[-71,-92.5]],[[-352,384],[-384,384],[-384,-384],[-338.5,260]],[[-284,137.5],[-338.5,260],[-384,-384]],[[-178,19.5],[-284,137.5],[-384,-384]],[[-178,19.5],[-384,-384],[-71,-92.5],[-60,-74.5]]]},{"polygons":[[[-352,-384],[-352,-352],[-315,-193.5],[-224,-59],[-64.5,74.5],[-70.5,93.5],[19.5,163],[65.5,204.5],[123.5,288],[138,349],[140.5,384],[-384,384],[-384,-384]],[[-140.5,-384],[-132.5,-307],[-102,-243],[-26,-163.5],[71,-92.5],[60,-74.5],[178,19.5],[284,137.5],[338.5,260],[352,384],[384,384],[384,-384]]],"shapes":[[[19.5,163],[65.5,204.5],[123.5,288],[138,349],[140.5,384],[-384,384],[-70.5,93.5]],[[-384,-384],[-352,-384],[-352,-352],[-384,384]],[[-384,384],[-352,-352],[-315,-193.5]],[[-384,384],[-315,-193.5],[-224,-59]],[[-70.5,93.5],[-384,384],[-224,-59],[-64.5,74.5]],[[-26,-163.5],[-102,-243],[-132.5,-307],[-140.5,-384],[384,-384],[71,-92.5]],[[384,-384],[384,384],[352,384],[338.5,260]],[[384,-384],[338.5,260],[284,137.5]],[[384,-384],[284,137.5],[178,19.5]],[[71,-92.5],[384,-384],[178,19.5],[60,-74.5]]]},{"polygons":[[[-384,-352],[-384,-384],[384,-384],[384,-352]],[[-384,-160],[-384,384],[384,384],[384,-160]]],"shapes":[[[-384,-352],[-384,-384],[384,-384],[384,-352]],[[384,-160],[384,384],[-384,384],[-384,-160]]]},{"polygons":[[[384,352],[384,384],[-384,384],[-384,352]],[[384,160],[384,-384],[-384,-384],[-384,160]]],"shapes":[[[384,352],[384,384],[-384,384],[-384,352]],[[-384,160],[-384,-384],[384,-384],[384,160]]]},{"polygons":[[[-352,384],[-384,384],[-384,-384],[-352,-384]],[[-160,384],[384,384],[384,-384],[-160,-384]]],"shapes":[[[-352,384],[-384,384],[-384,-384],[-352,-384]],[[-160,-384],[384,-384],[384,384],[-160,384]]]},{"polygons":[[[352,-384],[384,-384],[384,384],[352,384]],[[160,-384],[-384,-384],[-384,384],[160,384]]],"shapes":[[[352,-384],[384,-384],[384,384],[352,384]],[[160,384],[-384,384],[-384,-384],[160,-384]]]},{"polygons":[[[360,136],[346.95098876953125,124.73200225830078],[240,272],[132,384],[384,384],[384,112]],[[-132,384],[48,192],[252,-48],[300,-84],[348,-96],[384,-96],[384,-384],[-384,-384],[-384,384]]],"shapes":[[[360,136],[384,112],[384,384],[240,272]],[[384,384],[132,384],[240,272]],[[360,136],[240,272],[346.95098876953125,124.73200225830078]],[[48,192],[-132,384],[-384,384],[-384,-384],[384,-384],[252,-48]],[[300,-84],[252,-48],[384,-384]],[[348,-96],[300,-84],[384,-384]],[[348,-96],[384,-384],[384,-96]]]},{"polygons":[[[-136,360],[-124.73200225830078,346.95098876953125],[-272,240],[-384,132],[-384,384],[-112,384]],[[-384,-132],[-192,48],[48,252],[84,300],[96,348],[96,384],[384,384],[384,-384],[-384,-384]]],"shapes":[[[-136,360],[-112,384],[-384,384],[-272,240]],[[-384,384],[-384,132],[-272,240]],[[-136,360],[-272,240],[-124.73200225830078,346.95098876953125]],[[-192,48],[-384,-132],[-384,-384],[384,-384],[384,384],[48,252]],[[84,300],[48,252],[384,384]],[[96,348],[84,300],[384,384]],[[96,348],[384,384],[96,384]]]},{"polygons":[[[-360,-136],[-346.95098876953125,-124.73200225830078],[-240,-272],[-132,-384],[-384,-384],[-384,-112]],[[132,-384],[-48,-192],[-252,48],[-300,84],[-348,96],[-384,96],[-384,384],[384,384],[384,-384]]],"shapes":[[[-360,-136],[-384,-112],[-384,-384],[-240,-272]],[[-384,-384],[-132,-384],[-240,-272]],[[-360,-136],[-240,-272],[-346.95098876953125,-124.73200225830078]],[[-48,-192],[132,-384],[384,-384],[384,384],[-384,384],[-252,48]],[[-300,84],[-252,48],[-384,384]],[[-348,96],[-300,84],[-384,384]],[[-348,96],[-384,384],[-384,96]]]},{"polygons":[[[136,-360],[124.73200225830078,-346.95098876953125],[272,-240],[384,-132],[384,-384],[112,-384]],[[384,132],[192,-48],[-48,-252],[-84,-300],[-96,-348],[-96,-384],[-384,-384],[-384,384],[384,384]]],"shapes":[[[136,-360],[112,-384],[384,-384],[272,-240]],[[384,-384],[384,-132],[272,-240]],[[136,-360],[272,-240],[124.73200225830078,-346.95098876953125]],[[192,-48],[384,132],[384,384],[-384,384],[-384,-384],[-48,-252]],[[-84,-300],[-48,-252],[-384,-384]],[[-96,-348],[-84,-300],[-384,-384]],[[-96,-348],[-384,-384],[-96,-384]]]},{"polygons":[[[360,-136],[346.95098876953125,-124.73200225830078],[240,-272],[132,-384],[384,-384],[384,-112]],[[-132,-384],[48,-192],[252,48],[300,84],[348,96],[384,96],[384,384],[-384,384],[-384,-384]]],"shapes":[[[384,-384],[384,-112],[360,-136],[240,-272]],[[240,-272],[132,-384],[384,-384]],[[240,-272],[360,-136],[346.95098876953125,-124.73200225830078]],[[348,96],[384,96],[384,384],[-384,384]],[[300,84],[348,96],[-384,384]],[[252,48],[300,84],[-384,384],[-384,-384]],[[48,-192],[252,48],[-384,-384],[-132,-384]]]},{"polygons":[[[-136,-360],[-124.73200225830078,-346.95098876953125],[-272,-240],[-384,-132],[-384,-384],[-112,-384]],[[-384,132],[-192,-48],[48,-252],[84,-300],[96,-348],[96,-384],[384,-384],[384,384],[-384,384]]],"shapes":[[[-384,-384],[-112,-384],[-136,-360],[-272,-240]],[[-272,-240],[-384,-132],[-384,-384]],[[-272,-240],[-136,-360],[-124.73200225830078,-346.95098876953125]],[[96,-348],[96,-384],[384,-384],[384,384]],[[84,-300],[96,-348],[384,384]],[[48,-252],[84,-300],[384,384],[-384,384]],[[-192,-48],[48,-252],[-384,384],[-384,132]]]},{"polygons":[[[-360,136],[-346.95098876953125,124.73200225830078],[-240,272],[-132,384],[-384,384],[-384,112]],[[132,384],[-48,192],[-252,-48],[-300,-84],[-348,-96],[-384,-96],[-384,-384],[384,-384],[384,384]]],"shapes":[[[-384,384],[-384,112],[-360,136],[-240,272]],[[-240,272],[-132,384],[-384,384]],[[-240,272],[-360,136],[-346.95098876953125,124.73200225830078]],[[-348,-96],[-384,-96],[-384,-384],[384,-384]],[[-300,-84],[-348,-96],[384,-384]],[[-252,-48],[-300,-84],[384,-384],[384,384]],[[-48,192],[-252,-48],[384,384],[132,384]]]},{"polygons":[[[136,360],[124.73200225830078,346.95098876953125],[272,240],[384,132],[384,384],[112,384]],[[384,-132],[192,48],[-48,252],[-84,300],[-96,348],[-96,384],[-384,384],[-384,-384],[384,-384]]],"shapes":[[[384,384],[112,384],[136,360],[272,240]],[[272,240],[384,132],[384,384]],[[272,240],[136,360],[124.73200225830078,346.95098876953125]],[[-96,348],[-96,384],[-384,384],[-384,-384]],[[-84,300],[-96,348],[-384,-384]],[[-48,252],[-84,300],[-384,-384],[384,-384]],[[192,48],[-48,252],[384,-384],[384,-132]]]},{"polygons":[[[-384,-132],[-132,-384],[-384,-384]],[[132,-384],[-384,132],[-384,384],[384,384],[384,-384]]],"shapes":[[[-384,-384],[-132,-384],[-384,-132]],[[384,-384],[384,384],[-384,384],[-384,132],[132,-384]]]},{"polygons":[[[132,-384],[384,-132],[384,-384]],[[384,132],[-132,-384],[-384,-384],[-384,384],[384,384]]],"shapes":[[[384,-384],[384,-132],[132,-384]],[[384,384],[-384,384],[-384,-384],[-132,-384],[384,132]]]},{"polygons":[[[-132,384],[-384,132],[-384,384]],[[-384,-132],[132,384],[384,384],[384,-384],[-384,-384]]],"shapes":[[[-384,384],[-384,132],[-132,384]],[[-384,-384],[384,-384],[384,384],[132,384],[-384,-132]]]},{"polygons":[[[384,132],[132,384],[384,384]],[[-132,384],[384,-132],[384,-384],[-384,-384],[-384,384]]],"shapes":[[[384,384],[132,384],[384,132]],[[-384,384],[-384,-384],[384,-384],[384,-132],[-132,384]]]},{"polygons":[[[384,-96],[-384,-96],[-384,-384],[384,-384]],[[384,96],[-384,96],[-384,384],[384,384]],[[-16,-8],[-16,8],[-8,16],[8,16],[16,8],[16,-8],[8,-16],[-8,-16]]],"shapes":[[[384,-96],[-384,-96],[-384,-384],[384,-384]],[[384,384],[-384,384],[-384,96],[384,96]],[[-8,-16],[8,-16],[16,-8],[16,8],[8,16],[-8,16],[-16,8],[-16,-8]]]},{"polygons":[[[-96,-384],[-96,384],[-384,384],[-384,-384]],[[96,-384],[96,384],[384,384],[384,-384]],[[-8,16],[8,16],[16,8],[16,-8],[8,-16],[-8,-16],[-16,-8],[-16,8]]],"shapes":[[[-96,-384],[-96,384],[-384,384],[-384,-384]],[[384,-384],[384,384],[96,384],[96,-384]],[[-16,8],[-16,-8],[-8,-16],[8,-16],[16,-8],[16,8],[8,16],[-8,16]]]},{"polygons":[[[384,-352],[-384,-352],[-384,-384],[384,-384]],[[384,-160],[-384,-160],[-384,160],[384,160]],[[384,352],[384,384],[-384,384],[-384,352]]],"shapes":[[[384,-352],[-384,-352],[-384,-384],[384,-384]],[[384,160],[-384,160],[-384,-160],[384,-160]],[[384,352],[384,384],[-384,384],[-384,352]]]},{"polygons":[[[-352,-384],[-352,384],[-384,384],[-384,-384]],[[-160,-384],[-160,384],[160,384],[160,-384]],[[352,-384],[384,-384],[384,384],[352,384]]],"shapes":[[[-352,-384],[-352,384],[-384,384],[-384,-384]],[[160,-384],[160,384],[-160,384],[-160,-384]],[[352,-384],[384,-384],[384,384],[352,384]]]},{"polygons":[[[-384,-96],[-336,-96],[-216,-120],[-104,-176],[48,-272],[160,-320],[272,-344],[384,-352],[384,-384],[-384,-384]],[[-384,96],[-336,96],[-216,120],[-112,168],[-8,240],[112,304],[256,344],[384,352],[384,384],[-384,384]],[[384,-160],[344,-160],[240,-144],[108,-84],[108,-60],[16,0],[108,60],[108,84],[240,144],[324,156],[384,160]]],"shapes":[[[-384,-384],[384,-384],[384,-352],[272,-344]],[[-384,-384],[272,-344],[160,-320]],[[-384,-384],[160,-320],[48,-272]],[[-384,-96],[-384,-384],[48,-272],[-104,-176],[-216,-120],[-336,-96]],[[-8,240],[-384,384],[-384,96],[-336,96],[-216,120],[-112,168]],[[384,352],[384,384],[-384,384],[256,344]],[[112,304],[256,344],[-384,384]],[[-8,240],[112,304],[-384,384]],[[108,60],[16,0],[108,-60],[240,-144]],[[240,-144],[344,-160],[384,-160],[384,160],[324,156],[240,144],[108,84],[108,60]],[[240,-144],[108,-60],[108,-84]]]},{"polygons":[[[96,-384],[96,-336],[120,-216],[176,-104],[272,48],[320,160],[344,272],[352,384],[384,384],[384,-384]],[[-96,-384],[-96,-336],[-120,-216],[-168,-112],[-240,-8],[-304,112],[-344,256],[-352,384],[-384,384],[-384,-384]],[[160,384],[160,344],[144,240],[84,108],[60,108],[0,16],[-60,108],[-84,108],[-144,240],[-156,324],[-160,384]]],"shapes":[[[384,-384],[384,384],[352,384],[344,272]],[[384,-384],[344,272],[320,160]],[[384,-384],[320,160],[272,48]],[[96,-384],[384,-384],[272,48],[176,-104],[120,-216],[96,-336]],[[-240,-8],[-384,-384],[-96,-384],[-96,-336],[-120,-216],[-168,-112]],[[-352,384],[-384,384],[-384,-384],[-344,256]],[[-304,112],[-344,256],[-384,-384]],[[-240,-8],[-304,112],[-384,-384]],[[-60,108],[0,16],[60,108],[144,240]],[[144,240],[160,344],[160,384],[-160,384],[-156,324],[-144,240],[-84,108],[-60,108]],[[144,240],[60,108],[84,108]]]},{"polygons":[[[384,96],[336,96],[216,120],[104,176],[-48,272],[-160,320],[-272,344],[-384,352],[-384,384],[384,384]],[[384,-96],[336,-96],[216,-120],[112,-168],[8,-240],[-112,-304],[-256,-344],[-384,-352],[-384,-384],[384,-384]],[[-384,160],[-344,160],[-240,144],[-108,84],[-108,60],[-16,0],[-108,-60],[-108,-84],[-240,-144],[-324,-156],[-384,-160]]],"shapes":[[[384,384],[-384,384],[-384,352],[-272,344]],[[384,384],[-272,344],[-160,320]],[[384,384],[-160,320],[-48,272]],[[384,96],[384,384],[-48,272],[104,176],[216,120],[336,96]],[[8,-240],[384,-384],[384,-96],[336,-96],[216,-120],[112,-168]],[[-384,-352],[-384,-384],[384,-384],[-256,-344]],[[-112,-304],[-256,-344],[384,-384]],[[8,-240],[-112,-304],[384,-384]],[[-108,-60],[-16,0],[-108,60],[-240,144]],[[-240,144],[-344,160],[-384,160],[-384,-160],[-324,-156],[-240,-144],[-108,-84],[-108,-60]],[[-240,144],[-108,60],[-108,84]]]},{"polygons":[[[-96,384],[-96,336],[-120,216],[-176,104],[-272,-48],[-320,-160],[-344,-272],[-352,-384],[-384,-384],[-384,384]],[[96,384],[96,336],[120,216],[168,112],[240,8],[304,-112],[344,-256],[352,-384],[384,-384],[384,384]],[[-160,-384],[-160,-344],[-144,-240],[-84,-108],[-60,-108],[0,-16],[60,-108],[84,-108],[144,-240],[156,-324],[160,-384]]],"shapes":[[[-384,384],[-384,-384],[-352,-384],[-344,-272]],[[-384,384],[-344,-272],[-320,-160]],[[-384,384],[-320,-160],[-272,-48]],[[-96,384],[-384,384],[-272,-48],[-176,104],[-120,216],[-96,336]],[[240,8],[384,384],[96,384],[96,336],[120,216],[168,112]],[[352,-384],[384,-384],[384,384],[344,-256]],[[304,-112],[344,-256],[384,384]],[[240,8],[304,-112],[384,384]],[[60,-108],[0,-16],[-60,-108],[-144,-240]],[[-144,-240],[-160,-344],[-160,-384],[160,-384],[156,-324],[144,-240],[84,-108],[60,-108]],[[-144,-240],[-60,-108],[-84,-108]]]},{"polygons":[[[384,-96],[216,-96],[108,-48],[-108,-48],[-216,-96],[-384,-96],[-384,-384],[384,-384]],[[384,96],[384,384],[-384,384],[-384,96],[-214.70700073242188,96],[-108,48],[108,48],[216,96]]],"shapes":[[[384,-384],[384,-96],[216,-96],[-384,-384]],[[-384,-384],[216,-96],[108,-48],[-108,-48],[-216,-96]],[[-384,-384],[-216,-96],[-384,-96]],[[216,96],[384,96],[384,384],[-384,384]],[[-108,48],[108,48],[216,96],[-384,384],[-214.70700073242188,96]],[[-214.70700073242188,96],[-384,384],[-384,96]]]},{"polygons":[[[-96,-384],[-96,-216],[-48,-108],[-48,108],[-96,216],[-96,384],[-384,384],[-384,-384]],[[96,-384],[384,-384],[384,384],[96,384],[96,214.70700073242188],[48,108],[48,-108],[96,-216]]],"shapes":[[[-384,-384],[-96,-384],[-96,-216],[-384,384]],[[-384,384],[-96,-216],[-48,-108],[-48,108],[-96,216]],[[-384,384],[-96,216],[-96,384]],[[96,-216],[96,-384],[384,-384],[384,384]],[[48,108],[48,-108],[96,-216],[384,384],[96,214.70700073242188]],[[96,214.70700073242188],[384,384],[96,384]]]},{"polygons":[[[384,-96],[72,-96],[12,-48],[-12,-48],[-72,-96],[-216,-96],[-384,-96],[-384,-384],[384,-384]],[[384,96],[384,384],[-384,384],[-384,96],[-72,96],[-12,48],[12,48],[72,96]]],"shapes":[[[384,-384],[384,-96],[72,-96],[-384,-384]],[[-384,-384],[72,-96],[12,-48],[-12,-48],[-72,-96]],[[-384,-384],[-72,-96],[-216,-96]],[[-384,-384],[-216,-96],[-384,-96]],[[72,96],[384,96],[384,384],[-384,384]],[[-12,48],[12,48],[72,96],[-384,384],[-72,96]],[[-72,96],[-384,384],[-384,96]]]},{"polygons":[[[-96,-384],[-96,-72],[-48,-12],[-48,12],[-96,72],[-96,216],[-96,384],[-384,384],[-384,-384]],[[96,-384],[384,-384],[384,384],[96,384],[96,72],[48,12],[48,-12],[96,-72]]],"shapes":[[[-384,-384],[-96,-384],[-96,-72],[-384,384]],[[-384,384],[-96,-72],[-48,-12],[-48,12],[-96,72]],[[-384,384],[-96,72],[-96,216]],[[-384,384],[-96,216],[-96,384]],[[96,-72],[96,-384],[384,-384],[384,384]],[[48,12],[48,-12],[96,-72],[384,384],[96,72]],[[96,72],[384,384],[96,384]]]},{"polygons":[[[-384,96],[384,96],[384,384],[-384,384]],[[-384,-96],[384,-96],[384,-384],[-384,-384]],[[-136,16],[-120,16],[-112,8],[-112,-8],[-120,-16],[-136,-16],[-144,-8],[-144,8]],[[120,16],[136,16],[144,8],[144,-8],[136,-16],[120,-16],[112,-8],[112,8]]],"shapes":[[[-384,96],[384,96],[384,384],[-384,384]],[[-384,-384],[384,-384],[384,-96],[-384,-96]],[[-144,8],[-144,-8],[-136,-16],[-120,-16],[-112,-8],[-112,8],[-120,16],[-136,16]],[[112,8],[112,-8],[120,-16],[136,-16],[144,-8],[144,8],[136,16],[120,16]]]},{"polygons":[[[-96,-384],[-96,384],[-384,384],[-384,-384]],[[96,-384],[96,384],[384,384],[384,-384]],[[-16,-136],[-16,-120],[-8,-112],[8,-112],[16,-120],[16,-136],[8,-144],[-8,-144]],[[-16,120],[-16,136],[-8,144],[8,144],[16,136],[16,120],[8,112],[-8,112]]],"shapes":[[[-96,-384],[-96,384],[-384,384],[-384,-384]],[[384,-384],[384,384],[96,384],[96,-384]],[[-8,-144],[8,-144],[16,-136],[16,-120],[8,-112],[-8,-112],[-16,-120],[-16,-136]],[[-8,112],[8,112],[16,120],[16,136],[8,144],[-8,144],[-16,136],[-16,120]]]},{"polygons":[[[-384,-96],[-300,-96],[-216,-144],[216,-144],[300,-96],[384,-96],[384,-384],[-384,-384]],[[-384,96],[-300,96],[-228,144],[216,144],[312,96],[384,96],[384,384],[-384,384]]],"shapes":[[[-384,-96],[-384,-384],[384,-384],[-216,-144]],[[384,-384],[384,-96],[300,-96],[216,-144]],[[384,-384],[216,-144],[-216,-144]],[[-384,-96],[-216,-144],[-300,-96]],[[-384,384],[-384,96],[-300,96],[-228,144]],[[384,384],[-384,384],[-228,144],[216,144]],[[384,96],[384,384],[216,144],[312,96]]]},{"polygons":[[[-96,384],[-96,300],[-144,216],[-144,-216],[-96,-300],[-96,-384],[-384,-384],[-384,384]],[[96,384],[96,300],[144,228],[144,-216],[96,-312],[96,-384],[384,-384],[384,384]]],"shapes":[[[-96,384],[-384,384],[-384,-384],[-144,216]],[[-384,-384],[-96,-384],[-96,-300],[-144,-216]],[[-384,-384],[-144,-216],[-144,216]],[[-96,384],[-144,216],[-96,300]],[[384,384],[96,384],[96,300],[144,228]],[[384,-384],[384,384],[144,228],[144,-216]],[[96,-384],[384,-384],[144,-216],[96,-312]]]},{"polygons":[[[-384,-352],[-384,-384],[384,-384],[384,-352]],[[-384,-160],[384,-160],[384,384],[-384,384]],[[-8,-240],[8,-240],[16,-248],[16,-264],[8,-272],[-8,-272],[-16,-264],[-16,-248],[-8,-240]]],"shapes":[[[-384,-352],[-384,-384],[384,-384],[384,-352]],[[-384,-160],[384,-160],[384,384],[-384,384]],[[-8,-240],[-16,-248],[-16,-264],[-8,-272],[8,-272],[16,-264],[16,-248],[8,-240],[-8,-240]]]},{"polygons":[[[384,352],[384,384],[-384,384],[-384,352]],[[384,160],[-384,160],[-384,-384],[384,-384]],[[8,240],[-8,240],[-16,248],[-16,264],[-8,272],[8,272],[16,264],[16,248],[8,240]]],"shapes":[[[384,352],[384,384],[-384,384],[-384,352]],[[384,160],[-384,160],[-384,-384],[384,-384]],[[8,240],[16,248],[16,264],[8,272],[-8,272],[-16,264],[-16,248],[-8,240],[8,240]]]},{"polygons":[[[-352,384],[-384,384],[-384,-384],[-352,-384]],[[-160,384],[-160,-384],[384,-384],[384,384]],[[-240,8],[-240,-8],[-248,-16],[-264,-16],[-272,-8],[-272,8],[-264,16],[-248,16],[-240,8]]],"shapes":[[[-352,384],[-384,384],[-384,-384],[-352,-384]],[[-160,384],[-160,-384],[384,-384],[384,384]],[[-240,8],[-248,16],[-264,16],[-272,8],[-272,-8],[-264,-16],[-248,-16],[-240,-8],[-240,8]]]},{"polygons":[[[352,-384],[384,-384],[384,384],[352,384]],[[160,-384],[160,384],[-384,384],[-384,-384]],[[240,-8],[240,8],[248,16],[264,16],[272,8],[272,-8],[264,-16],[248,-16],[240,-8]]],"shapes":[[[352,-384],[384,-384],[384,384],[352,384]],[[160,-384],[160,384],[-384,384],[-384,-384]],[[240,-8],[248,-16],[264,-16],[272,-8],[272,8],[264,16],[248,16],[240,8],[240,-8]]]},{"polygons":[[[-384,-352],[-208,-352],[-112,-304],[112,-304],[208,-352],[384,-352],[384,-384],[-384,-384]],[[-384,-160],[-208,-160],[-112,-208],[112,-208],[208,-160],[384,-160],[384,384],[-384,384]]],"shapes":[[[-384,-352],[-384,-384],[384,-384],[-208,-352]],[[-112,-304],[-208,-352],[384,-384],[208,-352]],[[384,-384],[384,-352],[208,-352]],[[-112,-304],[208,-352],[112,-304]],[[-384,384],[-384,-160],[-208,-160],[384,384]],[[384,384],[-208,-160],[-112,-208],[112,-208],[208,-160]],[[384,384],[208,-160],[384,-160]]]},{"polygons":[[[384,352],[208,352],[112,304],[-112,304],[-208,352],[-384,352],[-384,384],[384,384]],[[384,160],[208,160],[112,208],[-112,208],[-208,160],[-384,160],[-384,-384],[384,-384]]],"shapes":[[[384,352],[384,384],[-384,384],[208,352]],[[112,304],[208,352],[-384,384],[-208,352]],[[-384,384],[-384,352],[-208,352]],[[112,304],[-208,352],[-112,304]],[[384,-384],[384,160],[208,160],[-384,-384]],[[-384,-384],[208,160],[112,208],[-112,208],[-208,160]],[[-384,-384],[-208,160],[-384,160]]]},{"polygons":[[[-352,384],[-352,208],[-304,112],[-304,-112],[-352,-208],[-352,-384],[-384,-384],[-384,384]],[[-160,384],[-160,208],[-208,112],[-208,-112],[-160,-208],[-160,-384],[384,-384],[384,384]]],"shapes":[[[-352,384],[-384,384],[-384,-384],[-352,208]],[[-304,112],[-352,208],[-384,-384],[-352,-208]],[[-384,-384],[-352,-384],[-352,-208]],[[-304,112],[-352,-208],[-304,-112]],[[384,384],[-160,384],[-160,208],[384,-384]],[[384,-384],[-160,208],[-208,112],[-208,-112],[-160,-208]],[[384,-384],[-160,-208],[-160,-384]]]},{"polygons":[[[352,-384],[352,-208],[304,-112],[304,112],[352,208],[352,384],[384,384],[384,-384]],[[160,-384],[160,-208],[208,-112],[208,112],[160,208],[160,384],[-384,384],[-384,-384]]],"shapes":[[[352,-384],[384,-384],[384,384],[352,-208]],[[304,-112],[352,-208],[384,384],[352,208]],[[384,384],[352,384],[352,208]],[[304,-112],[352,208],[304,112]],[[-384,-384],[160,-384],[160,-208],[-384,384]],[[-384,384],[160,-208],[208,-112],[208,112],[160,208]],[[-384,384],[160,208],[160,384]]]},{"polygons":[[[-384,-352],[-64,-352],[-8,-304],[8,-304],[72,-352],[384,-352],[384,-384],[-384,-384]],[[-384,-160],[-72,-160],[-8,-208],[8,-208],[72,-160],[384,-160],[384,384],[-384,384]]],"shapes":[[[-384,-352],[-384,-384],[384,-384],[-64,-352]],[[384,-384],[384,-352],[72,-352]],[[-64,-352],[384,-384],[72,-352]],[[-64,-352],[72,-352],[8,-304],[-8,-304]],[[-384,384],[-384,-160],[-72,-160],[384,384]],[[384,384],[-72,-160],[-8,-208],[8,-208],[72,-160]],[[384,384],[72,-160],[384,-160]]]},{"polygons":[[[384,352],[64,352],[8,304],[-8,304],[-72,352],[-384,352],[-384,384],[384,384]],[[384,160],[72,160],[8,208],[-8,208],[-72,160],[-384,160],[-384,-384],[384,-384]]],"shapes":[[[384,352],[384,384],[-384,384],[64,352]],[[-384,384],[-384,352],[-72,352]],[[64,352],[-384,384],[-72,352]],[[64,352],[-72,352],[-8,304],[8,304]],[[384,-384],[384,160],[72,160],[-384,-384]],[[-384,-384],[72,160],[8,208],[-8,208],[-72,160]],[[-384,-384],[-72,160],[-384,160]]]},{"polygons":[[[-352,384],[-352,64],[-304,8],[-304,-8],[-352,-72],[-352,-384],[-384,-384],[-384,384]],[[-160,384],[-160,72],[-208,8],[-208,-8],[-160,-72],[-160,-384],[384,-384],[384,384]]],"shapes":[[[-352,384],[-384,384],[-384,-384],[-352,64]],[[-384,-384],[-352,-384],[-352,-72]],[[-352,64],[-384,-384],[-352,-72]],[[-352,64],[-352,-72],[-304,-8],[-304,8]],[[384,384],[-160,384],[-160,72],[384,-384]],[[384,-384],[-160,72],[-208,8],[-208,-8],[-160,-72]],[[384,-384],[-160,-72],[-160,-384]]]},{"polygons":[[[352,-384],[352,-64],[304,-8],[304,8],[352,72],[352,384],[384,384],[384,-384]],[[160,-384],[160,-72],[208,-8],[208,8],[160,72],[160,384],[-384,384],[-384,-384]]],"shapes":[[[352,-384],[384,-384],[384,384],[352,-64]],[[384,384],[352,384],[352,72]],[[352,-64],[384,384],[352,72]],[[352,-64],[352,72],[304,8],[304,-8]],[[-384,-384],[160,-384],[160,-72],[-384,384]],[[-384,384],[160,-72],[208,-8],[208,8],[160,72]],[[-384,384],[160,72],[160,384]]]},{"polygons":[[[-384,-352],[-384,-384],[384,-384],[384,-352]],[[-384,-160],[384,-160],[384,384],[-384,384]],[[136,-272],[120,-272],[112,-264],[112,-248],[120,-240],[136,-240],[144,-248],[144,-264]],[[-120,-272],[-136,-272],[-144,-264],[-144,-248],[-136,-240],[-120,-240],[-112,-248],[-112,-264]]],"shapes":[[[-384,-352],[-384,-384],[384,-384],[384,-352]],[[-384,-160],[384,-160],[384,384],[-384,384]],[[144,-264],[144,-248],[136,-240],[120,-240],[112,-248],[112,-264],[120,-272],[136,-272]],[[-112,-264],[-112,-248],[-120,-240],[-136,-240],[-144,-248],[-144,-264],[-136,-272],[-120,-272]]]},{"polygons":[[[384,352],[384,384],[-384,384],[-384,352]],[[384,160],[-384,160],[-384,-384],[384,-384]],[[-136,272],[-120,272],[-112,264],[-112,248],[-120,240],[-136,240],[-144,248],[-144,264]],[[120,272],[136,272],[144,264],[144,248],[136,240],[120,240],[112,248],[112,264]]],"shapes":[[[384,352],[384,384],[-384,384],[-384,352]],[[384,160],[-384,160],[-384,-384],[384,-384]],[[-144,264],[-144,248],[-136,240],[-120,240],[-112,248],[-112,264],[-120,272],[-136,272]],[[112,264],[112,248],[120,240],[136,240],[144,248],[144,264],[136,272],[120,272]]]},{"polygons":[[[-352,384],[-384,384],[-384,-384],[-352,-384]],[[-160,384],[-160,-384],[384,-384],[384,384]],[[-272,-136],[-272,-120],[-264,-112],[-248,-112],[-240,-120],[-240,-136],[-248,-144],[-264,-144]],[[-272,120],[-272,136],[-264,144],[-248,144],[-240,136],[-240,120],[-248,112],[-264,112]]],"shapes":[[[-352,384],[-384,384],[-384,-384],[-352,-384]],[[-160,384],[-160,-384],[384,-384],[384,384]],[[-264,-144],[-248,-144],[-240,-136],[-240,-120],[-248,-112],[-264,-112],[-272,-120],[-272,-136]],[[-264,112],[-248,112],[-240,120],[-240,136],[-248,144],[-264,144],[-272,136],[-272,120]]]},{"polygons":[[[352,-384],[384,-384],[384,384],[352,384]],[[160,-384],[160,384],[-384,384],[-384,-384]],[[272,136],[272,120],[264,112],[248,112],[240,120],[240,136],[248,144],[264,144]],[[272,-120],[272,-136],[264,-144],[248,-144],[240,-136],[240,-120],[248,-112],[264,-112]]],"shapes":[[[352,-384],[384,-384],[384,384],[352,384]],[[160,-384],[160,384],[-384,384],[-384,-384]],[[264,144],[248,144],[240,136],[240,120],[248,112],[264,112],[272,120],[272,136]],[[264,-112],[248,-112],[240,-120],[240,-136],[248,-144],[264,-144],[272,-136],[272,-120]]]},{"polygons":[[[-355.75,-384],[-384,-298.5],[-384,-384]],[[-156.16099548339844,-384],[-384,299.4530029296875],[-384,384],[384,384],[384,-384]]],"shapes":[[[-355.75,-384],[-384,-298.5],[-384,-384]],[[384,-384],[384,384],[-384,384],[-384,299.4530029296875],[-156.16099548339844,-384]]]},{"polygons":[[[355.75,-384],[384,-298.5],[384,-384]],[[156.16099548339844,-384],[384,299.4530029296875],[384,384],[-384,384],[-384,-384]]],"shapes":[[[384,-384],[384,-298.5],[355.75,-384]],[[156.16099548339844,-384],[384,299.4530029296875],[384,384],[-384,384],[-384,-384]]]},{"polygons":[[[-355.75,384],[-384,298.5],[-384,384]],[[-156.16099548339844,384],[-384,-299.4530029296875],[-384,-384],[384,-384],[384,384]]],"shapes":[[[-384,384],[-384,298.5],[-355.75,384]],[[-156.16099548339844,384],[-384,-299.4530029296875],[-384,-384],[384,-384],[384,384]]]},{"polygons":[[[-384,-355.75],[-298.5,-384],[-384,-384]],[[-384,-156.16099548339844],[299.4530029296875,-384],[384,-384],[384,384],[-384,384]]],"shapes":[[[-384,-384],[-298.5,-384],[-384,-355.75]],[[-384,-156.16099548339844],[299.4530029296875,-384],[384,-384],[384,384],[-384,384]]]},{"polygons":[[[355.75,384],[384,298.5],[384,384]],[[156.16099548339844,384],[384,-299.4530029296875],[384,-384],[-384,-384],[-384,384]]],"shapes":[[[355.75,384],[384,298.5],[384,384]],[[-384,384],[-384,-384],[384,-384],[384,-299.4530029296875],[156.16099548339844,384]]]},{"polygons":[[[-384,355.75],[-298.5,384],[-384,384]],[[-384,156.16099548339844],[299.4530029296875,384],[384,384],[384,-384],[-384,-384]]],"shapes":[[[-384,355.75],[-298.5,384],[-384,384]],[[-384,-384],[384,-384],[384,384],[299.4530029296875,384],[-384,156.16099548339844]]]},{"polygons":[[[384,-355.75],[298.5,-384],[384,-384]],[[384,-156.16099548339844],[-299.4530029296875,-384],[-384,-384],[-384,384],[384,384]]],"shapes":[[[384,-355.75],[298.5,-384],[384,-384]],[[384,384],[-384,384],[-384,-384],[-299.4530029296875,-384],[384,-156.16099548339844]]]},{"polygons":[[[384,355.75],[298.5,384],[384,384]],[[384,156.16099548339844],[-299.4530029296875,384],[-384,384],[-384,-384],[384,-384]]],"shapes":[[[384,384],[298.5,384],[384,355.75]],[[384,156.16099548339844],[-299.4530029296875,384],[-384,384],[-384,-384],[384,-384]]]},{"polygons":[[[-350.7309875488281,-384],[-354.02099609375,-373.9549865722656],[-371.39300537109375,-336.98199462890625],[-384,-298.5],[-384,-384]],[[-160.375,-384],[-183.49400329589844,-298.39898681640625],[-273.2829895019531,-21.38159942626953],[-384,299.4530029296875],[-384,384],[384,384],[384,-384]]],"shapes":[[[-371.39300537109375,-336.98199462890625],[-384,-298.5],[-384,-384]],[[-354.02099609375,-373.9549865722656],[-371.39300537109375,-336.98199462890625],[-384,-384],[-350.7309875488281,-384]],[[-160.375,-384],[384,-384],[384,384],[-183.49400329589844,-298.39898681640625]],[[-183.49400329589844,-298.39898681640625],[384,384],[-384,384],[-273.2829895019531,-21.38159942626953]],[[-273.2829895019531,-21.38159942626953],[-384,384],[-384,299.4530029296875]]]},{"polygons":[[[350.7309875488281,-384],[354.02099609375,-373.9549865722656],[371.39300537109375,-336.98199462890625],[384,-298.5],[384,-384]],[[160.375,-384],[183.49400329589844,-298.39898681640625],[273.2829895019531,-21.38159942626953],[384,299.4530029296875],[384,384],[-384,384],[-384,-384]]],"shapes":[[[384,-384],[384,-298.5],[371.39300537109375,-336.98199462890625]],[[384,-384],[371.39300537109375,-336.98199462890625],[354.02099609375,-373.9549865722656],[350.7309875488281,-384]],[[273.2829895019531,-21.38159942626953],[384,299.4530029296875],[384,384],[-384,384],[-384,-384]],[[183.49400329589844,-298.39898681640625],[273.2829895019531,-21.38159942626953],[-384,-384]],[[-384,-384],[160.375,-384],[183.49400329589844,-298.39898681640625]]]},{"polygons":[[[-350.7309875488281,384],[-354.02099609375,373.9549865722656],[-371.39300537109375,336.98199462890625],[-384,298.5],[-384,384]],[[-160.375,384],[-183.49400329589844,298.39898681640625],[-273.2829895019531,21.38159942626953],[-384,-299.4530029296875],[-384,-384],[384,-384],[384,384]]],"shapes":[[[-384,384],[-384,298.5],[-371.39300537109375,336.98199462890625]],[[-384,384],[-371.39300537109375,336.98199462890625],[-354.02099609375,373.9549865722656],[-350.7309875488281,384]],[[-273.2829895019531,21.38159942626953],[-384,-299.4530029296875],[-384,-384],[384,-384],[384,384]],[[-183.49400329589844,298.39898681640625],[-273.2829895019531,21.38159942626953],[384,384]],[[384,384],[-160.375,384],[-183.49400329589844,298.39898681640625]]]},{"polygons":[[[-384,-350.7309875488281],[-373.9549865722656,-354.02099609375],[-336.98199462890625,-371.39300537109375],[-298.5,-384],[-384,-384]],[[-384,-160.375],[-298.39898681640625,-183.49400329589844],[-21.38159942626953,-273.2829895019531],[299.4530029296875,-384],[384,-384],[384,384],[-384,384]]],"shapes":[[[-384,-384],[-298.5,-384],[-336.98199462890625,-371.39300537109375]],[[-384,-384],[-336.98199462890625,-371.39300537109375],[-373.9549865722656,-354.02099609375],[-384,-350.7309875488281]],[[-21.38159942626953,-273.2829895019531],[299.4530029296875,-384],[384,-384],[384,384],[-384,384]],[[-298.39898681640625,-183.49400329589844],[-21.38159942626953,-273.2829895019531],[-384,384]],[[-384,384],[-384,-160.375],[-298.39898681640625,-183.49400329589844]]]},{"polygons":[[[350.7309875488281,384],[354.02099609375,373.9549865722656],[371.39300537109375,336.98199462890625],[384,298.5],[384,384]],[[160.375,384],[183.49400329589844,298.39898681640625],[273.2829895019531,21.38159942626953],[384,-299.4530029296875],[384,-384],[-384,-384],[-384,384]]],"shapes":[[[371.39300537109375,336.98199462890625],[384,298.5],[384,384]],[[354.02099609375,373.9549865722656],[371.39300537109375,336.98199462890625],[384,384],[350.7309875488281,384]],[[160.375,384],[-384,384],[-384,-384],[183.49400329589844,298.39898681640625]],[[183.49400329589844,298.39898681640625],[-384,-384],[384,-384],[273.2829895019531,21.38159942626953]],[[273.2829895019531,21.38159942626953],[384,-384],[384,-299.4530029296875]]]},{"polygons":[[[-384,350.7309875488281],[-373.9549865722656,354.02099609375],[-336.98199462890625,371.39300537109375],[-298.5,384],[-384,384]],[[-384,160.375],[-298.39898681640625,183.49400329589844],[-21.38159942626953,273.2829895019531],[299.4530029296875,384],[384,384],[384,-384],[-384,-384]]],"shapes":[[[-336.98199462890625,371.39300537109375],[-298.5,384],[-384,384]],[[-373.9549865722656,354.02099609375],[-336.98199462890625,371.39300537109375],[-384,384],[-384,350.7309875488281]],[[-384,160.375],[-384,-384],[384,-384],[-298.39898681640625,183.49400329589844]],[[-298.39898681640625,183.49400329589844],[384,-384],[384,384],[-21.38159942626953,273.2829895019531]],[[-21.38159942626953,273.2829895019531],[384,384],[299.4530029296875,384]]]},{"polygons":[[[384,-350.7309875488281],[373.9549865722656,-354.02099609375],[336.98199462890625,-371.39300537109375],[298.5,-384],[384,-384]],[[384,-160.375],[298.39898681640625,-183.49400329589844],[21.38159942626953,-273.2829895019531],[-299.4530029296875,-384],[-384,-384],[-384,384],[384,384]]],"shapes":[[[336.98199462890625,-371.39300537109375],[298.5,-384],[384,-384]],[[373.9549865722656,-354.02099609375],[336.98199462890625,-371.39300537109375],[384,-384],[384,-350.7309875488281]],[[384,-160.375],[384,384],[-384,384],[298.39898681640625,-183.49400329589844]],[[298.39898681640625,-183.49400329589844],[-384,384],[-384,-384],[21.38159942626953,-273.2829895019531]],[[21.38159942626953,-273.2829895019531],[-384,-384],[-299.4530029296875,-384]]]},{"polygons":[[[384,350.7309875488281],[373.9549865722656,354.02099609375],[336.98199462890625,371.39300537109375],[298.5,384],[384,384]],[[384,160.375],[298.39898681640625,183.49400329589844],[21.38159942626953,273.2829895019531],[-299.4530029296875,384],[-384,384],[-384,-384],[384,-384]]],"shapes":[[[384,384],[298.5,384],[336.98199462890625,371.39300537109375]],[[384,384],[336.98199462890625,371.39300537109375],[373.9549865722656,354.02099609375],[384,350.7309875488281]],[[21.38159942626953,273.2829895019531],[-299.4530029296875,384],[-384,384],[-384,-384],[384,-384]],[[298.39898681640625,183.49400329589844],[21.38159942626953,273.2829895019531],[384,-384]],[[384,-384],[384,160.375],[298.39898681640625,183.49400329589844]]]},{"polygons":[[[-384,-100.16400146484375],[-384,-384],[384,-384],[384,-357.18798828125]],[[-384,100.16400146484375],[384,-155.60000610351562],[384,384],[-384,384]]],"shapes":[[[-384,-100.16400146484375],[-384,-384],[384,-384],[384,-357.18798828125]],[[-384,100.16400146484375],[384,-155.60000610351562],[384,384],[-384,384]]]},{"polygons":[[[384,-100.16400146484375],[384,-384],[-384,-384],[-384,-357.18798828125]],[[384,100.16400146484375],[-384,-155.60000610351562],[-384,384],[384,384]]],"shapes":[[[-384,-357.18798828125],[-384,-384],[384,-384],[384,-100.16400146484375]],[[384,384],[-384,384],[-384,-155.60000610351562],[384,100.16400146484375]]]},{"polygons":[[[-384,100.16400146484375],[-384,384],[384,384],[384,357.18798828125]],[[-384,-100.16400146484375],[384,155.60000610351562],[384,-384],[-384,-384]]],"shapes":[[[384,357.18798828125],[384,384],[-384,384],[-384,100.16400146484375]],[[-384,-384],[384,-384],[384,155.60000610351562],[-384,-100.16400146484375]]]},{"polygons":[[[-100.16400146484375,-384],[-384,-384],[-384,384],[-357.18798828125,384]],[[100.16400146484375,-384],[-155.60000610351562,384],[384,384],[384,-384]]],"shapes":[[[-357.18798828125,384],[-384,384],[-384,-384],[-100.16400146484375,-384]],[[384,-384],[384,384],[-155.60000610351562,384],[100.16400146484375,-384]]]},{"polygons":[[[384,100.16400146484375],[384,384],[-384,384],[-384,357.18798828125]],[[384,-100.16400146484375],[-384,155.60000610351562],[-384,-384],[384,-384]]],"shapes":[[[384,100.16400146484375],[384,384],[-384,384],[-384,357.18798828125]],[[384,-100.16400146484375],[-384,155.60000610351562],[-384,-384],[384,-384]]]},{"polygons":[[[-100.16400146484375,384],[-384,384],[-384,-384],[-357.18798828125,-384]],[[100.16400146484375,384],[-155.60000610351562,-384],[384,-384],[384,384]]],"shapes":[[[-100.16400146484375,384],[-384,384],[-384,-384],[-357.18798828125,-384]],[[100.16400146484375,384],[-155.60000610351562,-384],[384,-384],[384,384]]]},{"polygons":[[[100.16400146484375,-384],[384,-384],[384,384],[357.18798828125,384]],[[-100.16400146484375,-384],[155.60000610351562,384],[-384,384],[-384,-384]]],"shapes":[[[100.16400146484375,-384],[384,-384],[384,384],[357.18798828125,384]],[[-100.16400146484375,-384],[155.60000610351562,384],[-384,384],[-384,-384]]]},{"polygons":[[[100.16400146484375,384],[384,384],[384,-384],[357.18798828125,-384]],[[-100.16400146484375,384],[155.60000610351562,-384],[-384,-384],[-384,384]]],"shapes":[[[357.18798828125,-384],[384,-384],[384,384],[100.16400146484375,384]],[[-384,384],[-384,-384],[155.60000610351562,-384],[-100.16400146484375,384]]]},{"polygons":[[[-384,-95.4092025756836],[-384,-384],[384,-384],[384,-357.18798828125],[-205.9720001220703,-133.572998046875],[-323.8299865722656,-102.14399719238281]],[[-384,96.53170013427734],[-271.07501220703125,85.30709838867188],[-128.52200317382812,46.02090072631836],[204.8489990234375,-89.7968978881836],[384,-155.60000610351562],[384,384],[-384,384]]],"shapes":[[[-384,-95.4092025756836],[-384,-384],[384,-384],[384,-357.18798828125],[-205.9720001220703,-133.572998046875],[-323.8299865722656,-102.14399719238281]],[[204.8489990234375,-89.7968978881836],[384,-155.60000610351562],[384,384],[-384,384],[-128.52200317382812,46.02090072631836]],[[-271.07501220703125,85.30709838867188],[-128.52200317382812,46.02090072631836],[-384,384]],[[-384,384],[-384,96.53170013427734],[-271.07501220703125,85.30709838867188]]]},{"polygons":[[[384,-95.4092025756836],[384,-384],[-384,-384],[-384,-357.18798828125],[205.9720001220703,-133.572998046875],[323.8299865722656,-102.14399719238281]],[[384,96.53170013427734],[271.07501220703125,85.30709838867188],[128.52200317382812,46.02090072631836],[-204.8489990234375,-89.7968978881836],[-384,-155.60000610351562],[-384,384],[384,384]]],"shapes":[[[323.8299865722656,-102.14399719238281],[205.9720001220703,-133.572998046875],[-384,-357.18798828125],[-384,-384],[384,-384],[384,-95.4092025756836]],[[384,384],[-384,384],[-384,-155.60000610351562],[-204.8489990234375,-89.7968978881836],[128.52200317382812,46.02090072631836]],[[384,384],[128.52200317382812,46.02090072631836],[271.07501220703125,85.30709838867188]],[[384,96.53170013427734],[384,384],[271.07501220703125,85.30709838867188]]]},{"polygons":[[[-384,95.4092025756836],[-384,384],[384,384],[384,357.18798828125],[-205.9720001220703,133.572998046875],[-323.8299865722656,102.14399719238281]],[[-384,-96.53170013427734],[-271.07501220703125,-85.30709838867188],[-128.52200317382812,-46.02090072631836],[204.8489990234375,89.7968978881836],[384,155.60000610351562],[384,-384],[-384,-384]]],"shapes":[[[-323.8299865722656,102.14399719238281],[-205.9720001220703,133.572998046875],[384,357.18798828125],[384,384],[-384,384],[-384,95.4092025756836]],[[-384,-384],[384,-384],[384,155.60000610351562],[204.8489990234375,89.7968978881836],[-128.52200317382812,-46.02090072631836]],[[-384,-384],[-128.52200317382812,-46.02090072631836],[-271.07501220703125,-85.30709838867188]],[[-384,-96.53170013427734],[-384,-384],[-271.07501220703125,-85.30709838867188]]]},{"polygons":[[[-95.4092025756836,-384],[-384,-384],[-384,384],[-357.18798828125,384],[-133.572998046875,-205.9720001220703],[-102.14399719238281,-323.8299865722656]],[[96.53170013427734,-384],[85.30709838867188,-271.07501220703125],[46.02090072631836,-128.52200317382812],[-89.7968978881836,204.8489990234375],[-155.60000610351562,384],[384,384],[384,-384]]],"shapes":[[[-102.14399719238281,-323.8299865722656],[-133.572998046875,-205.9720001220703],[-357.18798828125,384],[-384,384],[-384,-384],[-95.4092025756836,-384]],[[384,-384],[384,384],[-155.60000610351562,384],[-89.7968978881836,204.8489990234375],[46.02090072631836,-128.52200317382812]],[[384,-384],[46.02090072631836,-128.52200317382812],[85.30709838867188,-271.07501220703125]],[[96.53170013427734,-384],[384,-384],[85.30709838867188,-271.07501220703125]]]},{"polygons":[[[384,95.4092025756836],[384,384],[-384,384],[-384,357.18798828125],[205.9720001220703,133.572998046875],[323.8299865722656,102.14399719238281]],[[384,-96.53170013427734],[271.07501220703125,-85.30709838867188],[128.52200317382812,-46.02090072631836],[-204.8489990234375,89.7968978881836],[-384,155.60000610351562],[-384,-384],[384,-384]]],"shapes":[[[384,95.4092025756836],[384,384],[-384,384],[-384,357.18798828125],[205.9720001220703,133.572998046875],[323.8299865722656,102.14399719238281]],[[-204.8489990234375,89.7968978881836],[-384,155.60000610351562],[-384,-384],[384,-384],[128.52200317382812,-46.02090072631836]],[[271.07501220703125,-85.30709838867188],[128.52200317382812,-46.02090072631836],[384,-384]],[[384,-384],[384,-96.53170013427734],[271.07501220703125,-85.30709838867188]]]},{"polygons":[[[-95.4092025756836,384],[-384,384],[-384,-384],[-357.18798828125,-384],[-133.572998046875,205.9720001220703],[-102.14399719238281,323.8299865722656]],[[96.53170013427734,384],[85.30709838867188,271.07501220703125],[46.02090072631836,128.52200317382812],[-89.7968978881836,-204.8489990234375],[-155.60000610351562,-384],[384,-384],[384,384]]],"shapes":[[[-95.4092025756836,384],[-384,384],[-384,-384],[-357.18798828125,-384],[-133.572998046875,205.9720001220703],[-102.14399719238281,323.8299865722656]],[[-89.7968978881836,-204.8489990234375],[-155.60000610351562,-384],[384,-384],[384,384],[46.02090072631836,128.52200317382812]],[[85.30709838867188,271.07501220703125],[46.02090072631836,128.52200317382812],[384,384]],[[384,384],[96.53170013427734,384],[85.30709838867188,271.07501220703125]]]},{"polygons":[[[95.4092025756836,-384],[384,-384],[384,384],[357.18798828125,384],[133.572998046875,-205.9720001220703],[102.14399719238281,-323.8299865722656]],[[-96.53170013427734,-384],[-85.30709838867188,-271.07501220703125],[-46.02090072631836,-128.52200317382812],[89.7968978881836,204.8489990234375],[155.60000610351562,384],[-384,384],[-384,-384]]],"shapes":[[[95.4092025756836,-384],[384,-384],[384,384],[357.18798828125,384],[133.572998046875,-205.9720001220703],[102.14399719238281,-323.8299865722656]],[[89.7968978881836,204.8489990234375],[155.60000610351562,384],[-384,384],[-384,-384],[-46.02090072631836,-128.52200317382812]],[[-85.30709838867188,-271.07501220703125],[-46.02090072631836,-128.52200317382812],[-384,-384]],[[-384,-384],[-96.53170013427734,-384],[-85.30709838867188,-271.07501220703125]]]},{"polygons":[[[95.4092025756836,384],[384,384],[384,-384],[357.18798828125,-384],[133.572998046875,205.9720001220703],[102.14399719238281,323.8299865722656]],[[-96.53170013427734,384],[-85.30709838867188,271.07501220703125],[-46.02090072631836,128.52200317382812],[89.7968978881836,-204.8489990234375],[155.60000610351562,-384],[-384,-384],[-384,384]]],"shapes":[[[102.14399719238281,323.8299865722656],[133.572998046875,205.9720001220703],[357.18798828125,-384],[384,-384],[384,384],[95.4092025756836,384]],[[-384,384],[-384,-384],[155.60000610351562,-384],[89.7968978881836,-204.8489990234375],[-46.02090072631836,128.52200317382812]],[[-384,384],[-46.02090072631836,128.52200317382812],[-85.30709838867188,271.07501220703125]],[[-96.53170013427734,384],[-384,384],[-85.30709838867188,271.07501220703125]]]},{"polygons":[[[384,-351.2550048828125],[384,-384],[-384,-384],[-384,-100.05599975585938],[85.55989837646484,-287.0849914550781],[215.76100158691406,-327.5790100097656],[332.7510070800781,-350.12298583984375]],[[-384,99.61849975585938],[-384,384],[384,384],[384,-161.12100219726562],[310.4779968261719,-152.343994140625],[166.1529998779297,-107.79900360107422],[5.790830135345459,-47.21760177612305]]],"shapes":[[[85.55989837646484,-287.0849914550781],[-384,-100.05599975585938],[-384,-384]],[[85.55989837646484,-287.0849914550781],[-384,-384],[384,-384],[215.76100158691406,-327.5790100097656]],[[332.7510070800781,-350.12298583984375],[215.76100158691406,-327.5790100097656],[384,-384]],[[384,-351.2550048828125],[332.7510070800781,-350.12298583984375],[384,-384]],[[166.1529998779297,-107.79900360107422],[310.4779968261719,-152.343994140625],[384,-161.12100219726562],[384,384],[-384,384],[5.790830135345459,-47.21760177612305]],[[-384,99.61849975585938],[5.790830135345459,-47.21760177612305],[-384,384]]]},{"polygons":[[[-384,-351.2550048828125],[-384,-384],[384,-384],[384,-100.05599975585938],[-85.55989837646484,-287.0849914550781],[-215.76100158691406,-327.5790100097656],[-332.7510070800781,-350.12298583984375]],[[384,99.61849975585938],[384,384],[-384,384],[-384,-161.12100219726562],[-310.4779968261719,-152.343994140625],[-166.1529998779297,-107.79900360107422],[-5.790830135345459,-47.21760177612305]]],"shapes":[[[384,-384],[384,-100.05599975585938],[-85.55989837646484,-287.0849914550781]],[[-384,-384],[384,-384],[-85.55989837646484,-287.0849914550781],[-215.76100158691406,-327.5790100097656]],[[-384,-384],[-215.76100158691406,-327.5790100097656],[-332.7510070800781,-350.12298583984375]],[[-332.7510070800781,-350.12298583984375],[-384,-351.2550048828125],[-384,-384]],[[384,384],[-384,384],[-384,-161.12100219726562],[-310.4779968261719,-152.343994140625],[-166.1529998779297,-107.79900360107422],[-5.790830135345459,-47.21760177612305]],[[-5.790830135345459,-47.21760177612305],[384,99.61849975585938],[384,384]]]},{"polygons":[[[384,351.2550048828125],[384,384],[-384,384],[-384,100.05599975585938],[85.55989837646484,287.0849914550781],[215.76100158691406,327.5790100097656],[332.7510070800781,350.12298583984375]],[[-384,-99.61849975585938],[-384,-384],[384,-384],[384,161.12100219726562],[310.4779968261719,152.343994140625],[166.1529998779297,107.79900360107422],[5.790830135345459,47.21760177612305]]],"shapes":[[[-384,384],[-384,100.05599975585938],[85.55989837646484,287.0849914550781]],[[384,384],[-384,384],[85.55989837646484,287.0849914550781],[215.76100158691406,327.5790100097656]],[[384,384],[215.76100158691406,327.5790100097656],[332.7510070800781,350.12298583984375]],[[332.7510070800781,350.12298583984375],[384,351.2550048828125],[384,384]],[[-384,-384],[384,-384],[384,161.12100219726562],[310.4779968261719,152.343994140625],[166.1529998779297,107.79900360107422],[5.790830135345459,47.21760177612305]],[[5.790830135345459,47.21760177612305],[-384,-99.61849975585938],[-384,-384]]]},{"polygons":[[[-351.2550048828125,384],[-384,384],[-384,-384],[-100.05599975585938,-384],[-287.0849914550781,85.55989837646484],[-327.5790100097656,215.76100158691406],[-350.12298583984375,332.7510070800781]],[[99.61849975585938,-384],[384,-384],[384,384],[-161.12100219726562,384],[-152.343994140625,310.4779968261719],[-107.79900360107422,166.1529998779297],[-47.21760177612305,5.790830135345459]]],"shapes":[[[-384,-384],[-100.05599975585938,-384],[-287.0849914550781,85.55989837646484]],[[-384,384],[-384,-384],[-287.0849914550781,85.55989837646484],[-327.5790100097656,215.76100158691406]],[[-384,384],[-327.5790100097656,215.76100158691406],[-350.12298583984375,332.7510070800781]],[[-350.12298583984375,332.7510070800781],[-351.2550048828125,384],[-384,384]],[[384,-384],[384,384],[-161.12100219726562,384],[-152.343994140625,310.4779968261719],[-107.79900360107422,166.1529998779297],[-47.21760177612305,5.790830135345459]],[[-47.21760177612305,5.790830135345459],[99.61849975585938,-384],[384,-384]]]},{"polygons":[[[-384,351.2550048828125],[-384,384],[384,384],[384,100.05599975585938],[-85.55989837646484,287.0849914550781],[-215.76100158691406,327.5790100097656],[-332.7510070800781,350.12298583984375]],[[384,-99.61849975585938],[384,-384],[-384,-384],[-384,161.12100219726562],[-310.4779968261719,152.343994140625],[-166.1529998779297,107.79900360107422],[-5.790830135345459,47.21760177612305]]],"shapes":[[[-85.55989837646484,287.0849914550781],[384,100.05599975585938],[384,384]],[[-85.55989837646484,287.0849914550781],[384,384],[-384,384],[-215.76100158691406,327.5790100097656]],[[-332.7510070800781,350.12298583984375],[-215.76100158691406,327.5790100097656],[-384,384]],[[-384,351.2550048828125],[-332.7510070800781,350.12298583984375],[-384,384]],[[-166.1529998779297,107.79900360107422],[-310.4779968261719,152.343994140625],[-384,161.12100219726562],[-384,-384],[384,-384],[-5.790830135345459,47.21760177612305]],[[384,-99.61849975585938],[-5.790830135345459,47.21760177612305],[384,-384]]]},{"polygons":[[[-351.2550048828125,-384],[-384,-384],[-384,384],[-100.05599975585938,384],[-287.0849914550781,-85.55989837646484],[-327.5790100097656,-215.76100158691406],[-350.12298583984375,-332.7510070800781]],[[99.61849975585938,384],[384,384],[384,-384],[-161.12100219726562,-384],[-152.343994140625,-310.4779968261719],[-107.79900360107422,-166.1529998779297],[-47.21760177612305,-5.790830135345459]]],"shapes":[[[-287.0849914550781,-85.55989837646484],[-100.05599975585938,384],[-384,384]],[[-287.0849914550781,-85.55989837646484],[-384,384],[-384,-384],[-327.5790100097656,-215.76100158691406]],[[-350.12298583984375,-332.7510070800781],[-327.5790100097656,-215.76100158691406],[-384,-384]],[[-351.2550048828125,-384],[-350.12298583984375,-332.7510070800781],[-384,-384]],[[-107.79900360107422,-166.1529998779297],[-152.343994140625,-310.4779968261719],[-161.12100219726562,-384],[384,-384],[384,384],[-47.21760177612305,-5.790830135345459]],[[99.61849975585938,384],[-47.21760177612305,-5.790830135345459],[384,384]]]},{"polygons":[[[351.2550048828125,384],[384,384],[384,-384],[100.05599975585938,-384],[287.0849914550781,85.55989837646484],[327.5790100097656,215.76100158691406],[350.12298583984375,332.7510070800781]],[[-99.61849975585938,-384],[-384,-384],[-384,384],[161.12100219726562,384],[152.343994140625,310.4779968261719],[107.79900360107422,166.1529998779297],[47.21760177612305,5.790830135345459]]],"shapes":[[[287.0849914550781,85.55989837646484],[100.05599975585938,-384],[384,-384]],[[287.0849914550781,85.55989837646484],[384,-384],[384,384],[327.5790100097656,215.76100158691406]],[[350.12298583984375,332.7510070800781],[327.5790100097656,215.76100158691406],[384,384]],[[351.2550048828125,384],[350.12298583984375,332.7510070800781],[384,384]],[[107.79900360107422,166.1529998779297],[152.343994140625,310.4779968261719],[161.12100219726562,384],[-384,384],[-384,-384],[47.21760177612305,5.790830135345459]],[[-99.61849975585938,-384],[47.21760177612305,5.790830135345459],[-384,-384]]]},{"polygons":[[[351.2550048828125,-384],[384,-384],[384,384],[100.05599975585938,384],[287.0849914550781,-85.55989837646484],[327.5790100097656,-215.76100158691406],[350.12298583984375,-332.7510070800781]],[[-99.61849975585938,384],[-384,384],[-384,-384],[161.12100219726562,-384],[152.343994140625,-310.4779968261719],[107.79900360107422,-166.1529998779297],[47.21760177612305,-5.790830135345459]]],"shapes":[[[384,384],[100.05599975585938,384],[287.0849914550781,-85.55989837646484]],[[384,-384],[384,384],[287.0849914550781,-85.55989837646484],[327.5790100097656,-215.76100158691406]],[[384,-384],[327.5790100097656,-215.76100158691406],[350.12298583984375,-332.7510070800781]],[[350.12298583984375,-332.7510070800781],[351.2550048828125,-384],[384,-384]],[[-384,384],[-384,-384],[161.12100219726562,-384],[152.343994140625,-310.4779968261719],[107.79900360107422,-166.1529998779297],[47.21760177612305,-5.790830135345459]],[[47.21760177612305,-5.790830135345459],[-99.61849975585938,384],[-384,384]]]}] \ No newline at end of file diff --git a/github-simulator/source/src/gpu/hip.rs b/github-simulator/source/src/gpu/hip.rs deleted file mode 100644 index a896ecc8c3294e422e645b565947a4df29970524..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/gpu/hip.rs +++ /dev/null @@ -1,479 +0,0 @@ -//! Runtime loader for the HIP simulator parts in gpu/ (libaltd_gpu.so). -//! Loaded at runtime so the crate builds and runs without ROCm. -use crate::math::profile::MathProfile; -use std::ffi::{c_void, CStr}; -use std::path::{Path, PathBuf}; - -/// Library file name, both in release archives and in gpu/build.sh output. -pub fn library_name() -> String { - format!( - "{}altd_gpu{}", - std::env::consts::DLL_PREFIX, - std::env::consts::DLL_SUFFIX - ) -} - -/// Paths [`Gpu::open`] tries, in order. An explicit path or ALTD_GPU_LIB is the -/// only candidate; otherwise the library beside the executable (release -/// archives), then gpu/build.sh output in this source checkout. -pub fn library_candidates(explicit: Option<&Path>, env: Option, exe_dir: Option<&Path>) -> Vec { - if let Some(path) = explicit.map(Path::to_path_buf).or(env) { - return vec![path]; - } - let name = library_name(); - let checkout = Path::new(env!("CARGO_MANIFEST_DIR")).join("target/gpu").join(&name); - exe_dir - .map(|dir| dir.join(&name)) - .into_iter() - .chain([checkout]) - .collect() -} - -/// Explains a HIP status returned by the library. -pub(crate) fn status_message(status: i32) -> String { - let hint = match status { - 35 => " (hipErrorInsufficientDriver: the GPU driver is missing or older than the runtime)", - 100 => " (hipErrorNoDevice: no supported GPU; check `rocminfo` and /dev/kfd permissions on AMD, `nvidia-smi` on NVIDIA)", - 209 => { - " (hipErrorNoBinaryForGpu: the library has no kernels for this GPU; \ - rebuild it with GPU_ARCH set to the gfx name `rocminfo` reports, or CUDA_ARCH set to your sm_ target on NVIDIA)" - } - _ => "", - }; - format!("HIP error {status}{hint}") -} - -/// Math primitive selectors of `altd_gpu_math` (gpu/sim/altd_gpu.hip). -#[repr(i32)] -#[derive(Clone, Copy, Debug)] -pub enum MathOp { - NativeSin = 0, - NativeCos = 1, - NativeAtan2 = 2, - ManagedSinCos = 3, - Exp = 4, - Pow = 5, - EngineSinCos = 6, - GameTanh = 8, -} - -mod math_element { - pub trait Sealed {} - impl Sealed for f32 {} - impl Sealed for f64 {} - impl Sealed for [f32; 2] {} - impl Sealed for [f64; 2] {} -} - -/// Native math buffer elements. Sealed to padding-free types for which every -/// output bit pattern is valid; arbitrary `Copy` types are not FFI-safe. -/// -/// ```compile_fail -/// use altd_sim::gpu::hip::{Gpu, MathOp}; -/// fn invalid(gpu: &Gpu) { -/// gpu.math(MathOp::NativeSin, &[0u8], &mut [false]); -/// } -/// ``` -pub trait MathElement: math_element::Sealed + Copy + 'static {} -impl MathElement for f32 {} -impl MathElement for f64 {} -impl MathElement for [f32; 2] {} -impl MathElement for [f64; 2] {} - -fn validate_math_buffers( - op: MathOp, - inputs: usize, - outputs: usize, -) -> Result { - use std::any::TypeId; - let (input, output) = match op { - MathOp::NativeSin | MathOp::NativeCos => (TypeId::of::(), TypeId::of::()), - MathOp::NativeAtan2 => (TypeId::of::<[f32; 2]>(), TypeId::of::()), - MathOp::ManagedSinCos | MathOp::EngineSinCos => (TypeId::of::(), TypeId::of::<[f32; 2]>()), - MathOp::Exp | MathOp::GameTanh => (TypeId::of::(), TypeId::of::()), - MathOp::Pow => (TypeId::of::<[f64; 2]>(), TypeId::of::()), - }; - if TypeId::of::() != input || TypeId::of::() != output { - return Err("math buffer types do not match the selected operation"); - } - if inputs != outputs { - return Err("math input and output lengths differ"); - } - i32::try_from(inputs).map_err(|_| "math batch too large") -} - -pub struct Gpu { - library: libloading::Library, - path: PathBuf, -} - -impl Gpu { - /// Opens the first loadable path of [`library_candidates`] for `path`, - /// ALTD_GPU_LIB, and the running executable. - pub fn open(path: Option<&Path>) -> Result { - let env = std::env::var_os("ALTD_GPU_LIB").map(PathBuf::from); - let exe = std::env::current_exe().ok(); - let candidates = library_candidates(path, env, exe.as_deref().and_then(Path::parent)); - let mut errors = Vec::new(); - for candidate in candidates { - match unsafe { load(&candidate) } { - Ok(library) => { - return Ok(Gpu { - library, - path: candidate, - }) - } - Err(error) => { - // libloading keeps the dlerror/LoadLibrary text in `source`. - let detail = std::error::Error::source(&error).map_or(error.to_string(), ToString::to_string); - let path = candidate.display().to_string(); - let mut message = if detail.contains(&path) { - detail - } else { - format!("{path}: {detail}") - }; - if message.contains("amdhip64") { - message += " (install a ROCm 7.x runtime that provides libamdhip64)"; - } - errors.push(message); - } - } - } - Err(format!( - "cannot load the GPU simulator library; build it with gpu/build.sh (AMD) or gpu/build-cuda.ps1 / gpu/build-cuda.sh (NVIDIA), or set ALTD_GPU_LIB\n {}", - errors.join("\n ") - )) - } - - /// Path of the loaded library. - pub fn path(&self) -> &Path { - &self.path - } - - /// Function pointer `name` of type `F` (an `unsafe extern "C" fn`). - pub(crate) fn symbol(&self, name: &str) -> F { - self.try_symbol(name).unwrap_or_else(|| { - panic!( - "{} lacks {name}; rebuild it with gpu/build.sh or gpu/build-cuda.*", - self.path.display() - ) - }) - } - - /// `symbol`, or `None` when the library lacks `name`. - pub(crate) fn try_symbol(&self, name: &str) -> Option { - assert_eq!(std::mem::size_of::(), std::mem::size_of::<*mut c_void>()); - unsafe { self.library.get::(name.as_bytes()) }.ok().map(|s| *s) - } - - /// A required function pointer, reporting incompatible libraries without panicking. - pub(crate) fn required_symbol(&self, name: &str) -> Result { - self.try_symbol(name).ok_or_else(|| { - format!( - "{} lacks {name}; rebuild it with gpu/build.sh or gpu/build-cuda.*", - self.path.display() - ) - }) - } - - /// The runtime the library was built for: "HIP" (AMD) or "CUDA" (NVIDIA). - /// Libraries without `altd_gpu_platform` predate the CUDA build and are HIP. - pub fn platform(&self) -> String { - let f: Option *const std::ffi::c_char> = self.try_symbol("altd_gpu_platform"); - f.map_or_else( - || "HIP".to_string(), - |f| unsafe { CStr::from_ptr(f()) }.to_string_lossy().into_owned(), - ) - } - - /// The device name, or the library path for older libraries. The legacy - /// math entry point probes device availability when no name is exported. - pub fn device_name(&self) -> Result { - let f: Option i32> = - self.try_symbol("altd_gpu_device_name"); - let Some(f) = f else { - self.probe_kernels()?; - return Ok(self.path.display().to_string()); - }; - let mut name = [0 as std::ffi::c_char; 256]; - let status = unsafe { f(name.as_mut_ptr(), name.len() as i32) }; - if status != 0 { - return Err(format!("no usable HIP device ({})", status_message(status))); - } - Ok(unsafe { CStr::from_ptr(name.as_ptr()) }.to_string_lossy().into_owned()) - } - - /// Runs one small kernel. A device can be present while the library has no - /// code for it (built for another GPU_ARCH or CUDA_ARCH); this reports that - /// before a simulation needs the device. - pub fn probe_kernels(&self) -> Result<(), String> { - // Asks before launching where the library can: a ROCm 7.1 launch without - // kernels for the device crashes the process. - let check: Option i32> = self.try_symbol("altd_gpu_probe"); - if let Some(check) = check { - let status = unsafe { check() }; - if status != 0 { - return Err(format!("no usable HIP device ({})", status_message(status))); - } - } - let probe: unsafe extern "C" fn(i32, i32, *const c_void, *mut c_void, *mut u32) -> i32 = self - .try_symbol("altd_gpu_math") - .ok_or("the HIP library lacks the device probe entry points")?; - let input = 0.0f32; - let mut output = 0.0f32; - let mut error = 0u32; - let status = unsafe { - probe( - MathOp::NativeSin as i32, - 1, - std::ptr::from_ref(&input).cast(), - std::ptr::from_mut(&mut output).cast(), - &mut error, - ) - }; - if status != 0 { - return Err(format!("no usable HIP device ({})", status_message(status))); - } - Ok(()) - } - - pub(crate) fn check(status: i32, what: &str) { - assert_eq!(status, 0, "GPU {what} failed with {}", status_message(status)); - } - - /// Panics unless the library's device structs match the Rust mirrors. - /// Needs no GPU: the library only reports host-side sizes and offsets. - pub fn check_layout(&self) { - crate::gpu::simulation::check_layout(self); - } - - /// Runs one math primitive. `input` and `output` hold `n` elements of the - /// op's input and output layout (see gpu/sim/altd_gpu.hip); returns the error bits. - pub fn math(&self, op: MathOp, input: &[I], output: &mut [O]) -> Vec { - self.profile_math(MathProfile::Proton, op, input, output) - } - - /// [`Gpu::math`] in `profile`'s variant of the sin, cos, atan2, exp, pow - /// and tanh ops. - pub fn profile_math( - &self, - profile: MathProfile, - op: MathOp, - input: &[I], - output: &mut [O], - ) -> Vec { - // Validate before symbol resolution, allocation, or any native call. - let n = validate_math_buffers::(op, input.len(), output.len()).expect("invalid math buffers"); - if n == 0 { - return Vec::new(); - } - let f: unsafe extern "C" fn(i32, i32, *const c_void, *mut c_void, *mut u32) -> i32 = - self.symbol("altd_gpu_math"); - let mut err = vec![0u32; input.len()]; - Self::check( - unsafe { - f( - op as i32 | (profile.index() as i32) << 8, - n, - input.as_ptr().cast(), - output.as_mut_ptr().cast(), - err.as_mut_ptr(), - ) - }, - "math", - ); - err - } - - /// Raw engine sine and cosine bits for the floats with bits `first..first + n`. - pub fn engine_scan(&self, first: u32, out: &mut [[u32; 2]]) { - let f: unsafe extern "C" fn(u32, u32, *mut [u32; 2]) -> i32 = self.symbol("altd_gpu_engine_scan"); - Self::check(unsafe { f(first, out.len() as u32, out.as_mut_ptr()) }, "engine scan"); - } -} - -/// Opens `path`; on Unix with RTLD_NOW, so unresolved symbols fail at load time. -unsafe fn load(path: &Path) -> Result { - #[cfg(unix)] - { - use libloading::os::unix::{Library, RTLD_LOCAL, RTLD_NOW}; - unsafe { Library::open(Some(path), RTLD_NOW | RTLD_LOCAL) }.map(Into::into) - } - #[cfg(not(unix))] - unsafe { - libloading::Library::new(path) - } -} - -pub(crate) use crate::gpu::simulation::RayNode; - -/// Query selectors of `altd_gpu_query`. -#[repr(i32)] -#[derive(Clone, Copy, Debug)] -pub enum Query { - /// `(sx, sy, ex, ey)` -> `(hx, hy, hit, 0)` - Raycast = 0, - /// `(px, py, _, _)` -> `(cx, cy, 1, 0)` - ClosestWall = 1, -} - -/// Device copy of a `World`. -pub struct GpuWorld<'a> { - gpu: &'a Gpu, - handle: *mut c_void, - free: unsafe extern "C" fn(*mut c_void), - /// The exported track (surface table, nearest-segment grids). - pub arrays: crate::gpu::simulation::TrackArrays, -} - -impl Drop for GpuWorld<'_> { - fn drop(&mut self) { - unsafe { (self.free)(self.handle) }; - } -} - -/// Owned host track arrays. Preparing these calls no GPU APIs and can run on -/// the CPU producer thread before a device is opened or a track is uploaded. -pub struct PreparedGpuWorld { - arrays: crate::gpu::simulation::TrackArrays, - rays: (Vec, Vec<[f32; 4]>, f32, usize), -} - -impl PreparedGpuWorld { - pub(crate) fn new(world: &crate::track::world::World) -> Result { - let track = &world.track; - if track.raycaster_present() == Some(false) { - return Err("GPU port expects a BSP raycaster".into()); - } - let (nodes, walls, magnitude, depth) = track.ray_tree().ok_or("GPU port needs the BSP RayTree")?.gpu_arrays(); - let nodes = nodes - .into_iter() - .map(|(links, own, subtree)| RayNode { links, own, subtree }) - .collect(); - Ok(Self { - arrays: crate::gpu::simulation::TrackArrays::new(world)?, - rays: (nodes, walls, magnitude, depth), - }) - } -} - -impl<'a> GpuWorld<'a> { - pub fn new(gpu: &'a Gpu, world: &crate::track::world::World) -> GpuWorld<'a> { - Self::from_prepared( - gpu, - PreparedGpuWorld::new(world).unwrap_or_else(|e| panic!("GPU track export: {e}")), - ) - } - - /// Upload CPU-prepared geometry without rebuilding spatial query arrays. - pub fn from_prepared(gpu: &'a Gpu, prepared: PreparedGpuWorld) -> GpuWorld<'a> { - Self::try_from_prepared(gpu, prepared).unwrap_or_else(|e| panic!("{e}")) - } - - /// `from_prepared`, returning unsupported geometry, missing symbols and upload failures. - pub fn try_from_prepared(gpu: &'a Gpu, prepared: PreparedGpuWorld) -> Result, String> { - crate::gpu::simulation::layout_matches(gpu)?; - let PreparedGpuWorld { arrays, rays } = prepared; - arrays.validate_counts(&rays)?; - let desc = arrays.desc(&rays); - let create: unsafe extern "C" fn(*const crate::gpu::simulation::WorldDesc) -> *mut c_void = - gpu.required_symbol("altd_gpu_world_create")?; - let free = gpu.required_symbol("altd_gpu_world_free")?; - // The library copies every array; `rays` only has to outlive the call. - let handle = unsafe { create(&desc) }; - if handle.is_null() { - return Err("altd_gpu_world_create failed to upload the track".into()); - } - Ok(GpuWorld { - gpu, - handle, - free, - arrays, - }) - } - - pub(crate) fn gpu(&self) -> &'a Gpu { - self.gpu - } - - pub(crate) fn handle(&self) -> *const c_void { - self.handle - } - - /// Runs one BSP query per element of `input`. - pub fn query(&self, op: Query, input: &[[f32; 4]]) -> Vec<[f32; 4]> { - let f: unsafe extern "C" fn(*mut c_void, i32, i32, *const [f32; 4], *mut [f32; 4]) -> i32 = - self.gpu.symbol("altd_gpu_query"); - let mut out = vec![[0f32; 4]; input.len()]; - let n = i32::try_from(input.len()).expect("batch too large"); - Gpu::check( - unsafe { f(self.handle, op as i32, n, input.as_ptr(), out.as_mut_ptr()) }, - "query", - ); - out - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn explicit_library_paths_are_the_only_candidate() { - let exe = Path::new("/opt/altd"); - let explicit = library_candidates(Some(Path::new("a.so")), Some("b.so".into()), Some(exe)); - assert_eq!(explicit, [PathBuf::from("a.so")]); - assert_eq!( - library_candidates(None, Some("b.so".into()), Some(exe)), - [PathBuf::from("b.so")] - ); - } - - #[test] - fn default_library_search_prefers_the_executable_directory() { - let name = library_name(); - let checkout = Path::new(env!("CARGO_MANIFEST_DIR")).join("target/gpu").join(&name); - let found = library_candidates(None, None, Some(Path::new("/opt/altd"))); - assert_eq!(found, [Path::new("/opt/altd").join(&name), checkout.clone()]); - assert_eq!(library_candidates(None, None, None), [checkout]); - } - - #[test] - fn missing_libraries_list_every_attempt() { - let error = Gpu::open(Some(Path::new("/nonexistent/libaltd_gpu.so"))).err().unwrap(); - assert!(error.contains("/nonexistent/libaltd_gpu.so"), "{error}"); - assert!(error.contains("gpu/build.sh"), "{error}"); - } -} - -#[cfg(test)] -mod math_buffer_tests { - use super::*; - - #[test] - fn all_operation_layouts_are_accepted_without_loading_gpu() { - for op in [MathOp::NativeSin, MathOp::NativeCos] { - assert_eq!(validate_math_buffers::(op, 3, 3), Ok(3)); - } - assert_eq!(validate_math_buffers::<[f32; 2], f32>(MathOp::NativeAtan2, 3, 3), Ok(3)); - for op in [MathOp::ManagedSinCos, MathOp::EngineSinCos] { - assert_eq!(validate_math_buffers::(op, 3, 3), Ok(3)); - } - for op in [MathOp::Exp, MathOp::GameTanh] { - assert_eq!(validate_math_buffers::(op, 3, 3), Ok(3)); - } - assert_eq!(validate_math_buffers::<[f64; 2], f64>(MathOp::Pow, 3, 3), Ok(3)); - } - - #[test] - fn undersized_and_same_width_wrong_types_are_rejected() { - assert!(validate_math_buffers::(MathOp::Pow, 1, 1).is_err()); - assert!(validate_math_buffers::(MathOp::Exp, 1, 1).is_err()); - assert!(validate_math_buffers::(MathOp::NativeAtan2, 1, 1).is_err()); - assert!(validate_math_buffers::(MathOp::NativeSin, 1, 2).is_err()); - assert!( - validate_math_buffers::(MathOp::NativeSin, i32::MAX as usize + 1, i32::MAX as usize + 1).is_err() - ); - assert_eq!(validate_math_buffers::(MathOp::NativeSin, 0, 0), Ok(0)); - } -} diff --git a/github-simulator/source/src/gpu/mod.rs b/github-simulator/source/src/gpu/mod.rs deleted file mode 100644 index 92b18a933dffe3eeaec16f978a2b965ca643a980..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/gpu/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -//! GPU state layouts, exports, and native HIP execution. - -/// Runtime-loaded HIP library and device geometry. -#[cfg(not(target_arch = "wasm32"))] -pub mod hip; -/// Shared GPU layouts and simulation windows. -pub mod simulation; diff --git a/github-simulator/source/src/gpu/simulation.rs b/github-simulator/source/src/gpu/simulation.rs deleted file mode 100644 index 91038e60608c5e9aa8c28388a223fb8732c59ba5..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/gpu/simulation.rs +++ /dev/null @@ -1,1746 +0,0 @@ -//! Host side of the GPU simulator: #[repr(C)] mirrors of gpu/sim/state.h and -//! gpu/sim/world.h, the exporters that fill them from the CPU structures, and -//! the handle that runs the simulation kernels (libaltd_gpu.so). -#[cfg(not(target_arch = "wasm32"))] -use crate::gpu::hip::{Gpu, GpuWorld}; -use crate::math::godot_math::F2; -use crate::math::profile::MathProfile; -use crate::math::vec2::V2; -use crate::physics::car::{Sensor, DT}; -use crate::track::world::{Surface, VehicleConfig, World, ASPHALT}; -use rayon::prelude::*; -#[cfg(not(target_arch = "wasm32"))] -use std::ffi::c_void; - -/// `RayNode` of gpu/sim/world.h. -#[repr(C, align(16))] -#[derive(Clone, Copy)] -pub(crate) struct RayNode { - pub(crate) links: [u32; 4], - pub(crate) own: [f32; 4], - pub(crate) subtree: [f32; 4], -} - -pub(crate) type Vec2 = [f32; 2]; - -pub(crate) const MAX_WHEELS: usize = 4; -pub(crate) const MAX_PAIRS: usize = 16; -pub(crate) const MAX_CONTACTS: usize = 32; -pub(crate) const MAX_SHAPE_POINTS: usize = 16; -#[cfg(not(target_arch = "wasm32"))] -pub(crate) const MAX_SENSORS: usize = 32; -#[cfg(not(target_arch = "wasm32"))] -const MAX_LAYERS: usize = 12; -#[cfg(not(target_arch = "wasm32"))] -const MAX_WIDTH: usize = 32; -#[cfg(not(target_arch = "wasm32"))] -const LANES: usize = 16; -pub const RECENT: usize = 10; - -pub(crate) const CONTACT_WALL_FIRST: u32 = 1; -pub(crate) const CONTACT_USED: u32 = 2; - -pub(crate) const CAR_ACTIVE: u32 = 1; -pub(crate) const CAR_RESET_RIGHT: u32 = 2; -pub(crate) const CAR_PENDING_VELOCITY: u32 = 4; -pub(crate) const CAR_HAS_SHAPE: u32 = 8; -pub(crate) const CAR_HAS_LEAF: u32 = 16; -pub(crate) const CAR_PAIR_CHECK: u32 = 32; - -#[repr(C)] -#[derive(Clone, Copy, Debug, Default)] -pub struct GpuContact { - pub(crate) normal: Vec2, - pub(crate) point: Vec2, - pub(crate) wall_point: Vec2, - pub(crate) local_point: Vec2, - pub(crate) wall_local_point: Vec2, - pub(crate) depth: f32, - pub(crate) shape: u32, - pub(crate) flags: u32, - pub(crate) normal_mass: f32, - pub(crate) tangent_mass: f32, - pub(crate) bias: f32, - pub(crate) bounce: f32, - pub(crate) friction: f32, - pub(crate) acc_normal: f32, - pub(crate) acc_tangent: f32, - pub(crate) acc_bias: f32, - pub(crate) acc_bias_center: f32, -} - -#[repr(C)] -#[derive(Clone, Copy, Debug)] -pub struct GpuCar { - pub(crate) position: Vec2, - pub(crate) velocity: Vec2, - pub(crate) acceleration: Vec2, - pub(crate) reset_acceleration: Vec2, - pub(crate) basis_x: Vec2, - pub(crate) basis_y: Vec2, - pub(crate) rotation: f32, - pub(crate) transform_angle: f32, - pub(crate) angular_velocity: f32, - pub(crate) boost_energy: f32, - pub flags: u32, - pub(crate) collision_count: u32, - pub tick: u64, - pub(crate) wheel_angle: [f32; MAX_WHEELS], - pub(crate) wheel_previous: [Vec2; MAX_WHEELS], - pub(crate) wheel_surface: [u32; MAX_WHEELS], - pub(crate) wheel_has_previous: u32, - pub pair_count: u32, - pub contact_count: u32, - pub error: u32, - pub(crate) shape_position: Vec2, - pub(crate) shape_size: Vec2, - pub(crate) leaf_min: Vec2, - pub(crate) leaf_max: Vec2, - pub pairs: [u32; MAX_PAIRS], - pub axes: [Vec2; MAX_PAIRS], - pub contacts: [GpuContact; MAX_CONTACTS], -} - -impl GpuCar { - pub(crate) fn zeroed() -> GpuCar { - // SAFETY: plain old data; all-zero bits are a valid value. - unsafe { std::mem::zeroed() } - } -} - -pub(crate) const AGENT_HAS_PREVIOUS_OFFSET: u32 = 1; -pub(crate) const AGENT_WENT_BACKWARDS: u32 = 2; -pub(crate) const AGENT_HAS_LAP_CROSSING: u32 = 4; -pub const AGENT_PENDING_CONTACT: u32 = 8; -pub(crate) const AGENT_HAS_BEST_LAP: u32 = 16; -pub(crate) const AGENT_HAS_ALL_LAPS: u32 = 32; -pub(crate) const AGENT_HAS_DEACTIVATED: u32 = 64; - -#[repr(C)] -#[derive(Clone, Copy, Debug, Default)] -pub struct GpuAgent { - pub controls: [f64; 5], - pub previous_offset: f64, - pub tracker_score: f64, - pub lap_crossing_fraction: f64, - pub lap_count: u64, - pub network_novelty: f64, - pub update_count: u64, - pub collision_count: u64, - pub total_score: f64, - pub total_speed: f64, - pub total_abs_steering: f64, - pub total_actual_distance: f64, - pub total_drifting_amount: f64, - pub total_momentum_change_frequency: f64, - pub total_throttle_change_frequency: f64, - pub total_braking_frequency: f64, - pub total_distance_from_wall: f64, - pub total_distance_from_center: f64, - pub best_lap_time: f64, - pub idle_ticks: u64, - pub previous_speed: f64, - pub previous_throttle: f64, - pub previous_score: f64, - pub previous_lap_count: u64, - pub lap_start_tick: u64, - pub drift_ticks: u64, - pub current_slip_angle_degrees: f64, - pub continuous_drift_time: f64, - pub max_continuous_drift_time: f64, - pub all_laps_time_sum: f64, - pub recent: [f64; RECENT], - pub recent_start: u32, - pub recent_len: u32, - pub deactivated_at: u64, - pub flags: u32, - pub pad: u32, -} - -#[repr(C)] -#[derive(Clone, Copy, Debug, Default)] -pub(crate) struct WheelDesc { - pub(crate) position: Vec2, - pub(crate) steering: u32, - pub(crate) handbrake_off: u32, - pub(crate) power: f32, - pub(crate) brake_power: f32, - pub(crate) handbrake_power: f32, - pub(crate) brake_max: f32, - pub(crate) max_angle_deg: f64, -} - -#[repr(C)] -#[derive(Clone, Copy, Debug, Default)] -pub struct VehicleDesc { - pub(crate) wheels: [WheelDesc; MAX_WHEELS], - pub(crate) wheel_count: u32, - pub(crate) custom_integrator: u32, - pub(crate) center_steering: u32, - pub(crate) solver_iterations: u32, - pub(crate) max_contacts_reported: u32, - pub(crate) pad0: u32, - pub(crate) steering_speed: f64, - pub(crate) max_velocity: f64, - pub(crate) dt: f64, - pub(crate) contact_max_separation: f64, - pub(crate) grip: f32, - pub(crate) air_resistance: f32, - pub(crate) mass: f32, - pub(crate) inertia: f32, - pub(crate) linear_damp: f32, - pub(crate) angular_damp: f32, - pub(crate) dt_f: f32, - pub(crate) max_velocity_f: f32, - pub(crate) gravity: Vec2, - pub(crate) shape_basis_x: Vec2, - pub(crate) shape_basis_y: Vec2, - pub(crate) shape_size: Vec2, - pub(crate) shape_half: Vec2, - pub(crate) shape_position: Vec2, - pub(crate) center_of_mass: Vec2, - pub(crate) friction: f32, - pub(crate) bounce: f32, - pub(crate) contact_bias: f32, - pub(crate) allowed_penetration: f32, - pub(crate) recycle_radius: f32, - pub(crate) max_separation_f: f32, - pub(crate) pad1: f32, - pub(crate) pad2: f32, -} - -#[repr(C)] -#[derive(Clone, Copy, Debug, Default)] -pub struct SensorDesc { - pub(crate) kind: u32, - pub(crate) a: f32, - pub(crate) b: f32, - pub(crate) offset: Vec2, - pub(crate) pad: u32, -} - -#[repr(C)] -#[derive(Clone, Copy, Debug, Default)] -pub(crate) struct TileCell { - pub(crate) surface: u32, - pub(crate) connected: u32, - pub(crate) first: Vec2, - pub(crate) last: Vec2, -} - -#[repr(C)] -#[derive(Clone, Copy, Debug, Default)] -pub(crate) struct ShapeDev { - pub(crate) origin: Vec2, - pub(crate) friction: f32, - pub(crate) bounce: f32, - pub(crate) min_x: f64, - pub(crate) min_y: f64, - pub(crate) max_x: f64, - pub(crate) max_y: f64, - pub(crate) first: u32, - pub(crate) count: u32, - pub(crate) wall_first: u32, - pub(crate) pad: u32, -} - -#[repr(C)] -#[derive(Clone, Copy, Debug)] -pub(crate) struct NearGridDesc { - pub(crate) x0: f64, - pub(crate) y0: f64, - pub(crate) cell: f64, - pub(crate) nx: u32, - pub(crate) ny: u32, - pub(crate) start: *const u32, - pub(crate) items: *const u32, -} - -/// `World` of gpu/sim/world.h with host pointers. -#[repr(C)] -#[derive(Clone, Copy, Debug)] -pub(crate) struct WorldDesc { - pub(crate) ray_node_count: u32, - pub(crate) ray_wall_count: u32, - pub(crate) ray_depth: u32, - pub(crate) ray_magnitude: f32, - pub(crate) ray_nodes: *const RayNode, - pub(crate) ray_walls: *const [f32; 4], - pub(crate) surface_count: u32, - pub(crate) default_surface: u32, - pub(crate) surfaces: *const [f32; 4], - pub(crate) tile_x0: i64, - pub(crate) tile_y0: i64, - pub(crate) tile_nx: i64, - pub(crate) tile_ny: i64, - pub(crate) tiles: *const TileCell, - pub(crate) path_points: u32, - /// `MathProfile::index`. - pub(crate) math_profile: u32, - pub(crate) path_segments: *const [f32; 8], - pub(crate) path_offsets: *const f64, - pub(crate) path_grid: NearGridDesc, - pub(crate) curve_points: u32, - pub(crate) curve_length: f32, - pub(crate) curve_position: *const Vec2, - pub(crate) curve_offsets: *const f32, - pub(crate) curve_forwards: *const Vec2, - pub(crate) curve_grid: NearGridDesc, - pub(crate) shape_count: u32, - pub(crate) shape_point_count: u32, - pub(crate) shapes: *const ShapeDev, - pub(crate) shape_local: *const Vec2, - pub(crate) shape_normals: *const Vec2, - pub(crate) shape_grid: NearGridDesc, - pub(crate) shape_margin: f64, - pub(crate) path_xy: *const f64, -} - -/// The little-endian words of `values`. The GPU mirrors hold only numbers -/// and explicit, initialized padding, so every byte is defined. -pub(crate) fn words(values: &[T]) -> Vec { - assert_eq!(std::mem::size_of::() % 4, 0); - let bytes = unsafe { std::slice::from_raw_parts(values.as_ptr().cast::(), std::mem::size_of_val(values)) }; - bytes - .chunks_exact(4) - .map(|v| u32::from_le_bytes(v.try_into().unwrap())) - .collect() -} - -/// Checks the Rust mirrors against the library's struct layout. -#[cfg(not(target_arch = "wasm32"))] -pub(crate) fn check_layout(gpu: &Gpu) { - if let Err(e) = layout_matches(gpu) { - panic!("{e}"); - } -} - -/// `check_layout` as an error instead of a panic. -#[cfg(not(target_arch = "wasm32"))] -pub(crate) fn layout_matches(gpu: &Gpu) -> Result<(), String> { - use std::mem::{offset_of, size_of}; - let f: unsafe extern "C" fn(*mut u64, i32) -> i32 = gpu - .try_symbol("altd_gpu_layout") - .ok_or("libaltd_gpu.so lacks altd_gpu_layout; rebuild it with gpu/build.sh or gpu/build-cuda.*")?; - let mut got = [0u64; 32]; - let count = unsafe { f(got.as_mut_ptr(), got.len() as i32) } as usize; - let want = [ - size_of::(), - size_of::(), - size_of::(), - size_of::(), - size_of::(), - size_of::(), - size_of::(), - size_of::(), - size_of::(), - size_of::<[f32; 8]>(), - size_of::(), - offset_of!(WorldDesc, tiles), - offset_of!(WorldDesc, curve_grid), - offset_of!(WorldDesc, shape_grid), - offset_of!(GpuCar, tick), - offset_of!(GpuCar, pairs), - offset_of!(GpuCar, contacts), - offset_of!(GpuAgent, recent), - offset_of!(VehicleDesc, steering_speed), - offset_of!(VehicleDesc, gravity), - offset_of!(WorldDesc, math_profile), - ]; - if count != want.len() { - return Err("libaltd_gpu.so layout table differs; rebuild it with gpu/build.sh or gpu/build-cuda.*".into()); - } - for (i, (&g, &w)) in got.iter().zip(&want).enumerate() { - if g != w as u64 { - return Err(format!("GPU struct layout entry {i} differs (library {g}, Rust {w})")); - } - } - // A compiler can drop exports without failing the build, so check them up front. - for name in SIMULATOR_EXPORTS { - gpu.required_symbol::(name)?; - } - Ok(()) -} - -/// The entry points `GpuWorld` and `GpuSim` call. -#[cfg(not(target_arch = "wasm32"))] -const SIMULATOR_EXPORTS: [&str; 16] = [ - "altd_gpu_world_create", - "altd_gpu_world_free", - "altd_gpu_sim_create", - "altd_gpu_sim_set_world", - "altd_gpu_sim_free", - "altd_gpu_sim_upload", - "altd_gpu_sim_upload_agents", - "altd_gpu_sim_active_count", - "altd_gpu_sim_download", - "altd_gpu_sim_sensors", - "altd_gpu_sim_networks", - "altd_gpu_sim_novelty", - "altd_gpu_sim_infer", - "altd_gpu_sim_step", - "altd_gpu_sim_stats", - "altd_gpu_sim_window", -]; - -// ---- exactness helpers ---- - -pub(crate) fn exact(v: f64, what: &str) -> Result { - let f = v as f32; - if (f as f64).to_bits() == v.to_bits() || v.is_nan() { - Ok(f) - } else { - Err(format!("{what} = {v:e} is not float32-exact")) - } -} - -pub(crate) fn exact2(v: V2, what: &str) -> Result { - Ok([exact(v.x, what)?, exact(v.y, what)?]) -} - -pub(crate) fn f2(v: F2) -> Vec2 { - [v.x, v.y] -} - -pub(crate) fn v2(v: Vec2) -> V2 { - V2::new(v[0] as f64, v[1] as f64) -} - -// ---- surfaces ---- - -/// Vehicle surfaces by GPU index: 0 is `ASPHALT` (the missing-name default), -/// then the configured surfaces in name order. -pub struct SurfaceTable { - pub(crate) names: Vec, - pub(crate) values: Vec, -} - -impl SurfaceTable { - pub fn new(vehicle: &VehicleConfig) -> SurfaceTable { - let mut names: Vec = vehicle.surfaces.keys().cloned().collect(); - names.sort(); - let mut values = vec![ASPHALT]; - values.extend(names.iter().map(|n| vehicle.surfaces[n])); - SurfaceTable { names, values } - } - - /// Index of `vehicle.surface(name)`: the first one holding its value, as - /// `by_value` (the CPU keeps surfaces by value, e.g. a configured - /// "asphalt" equal to the default). - pub(crate) fn by_name(&self, name: &str) -> u32 { - let named = self.names.iter().position(|n| n == name).map_or(0, |i| i + 1); - self.values - .iter() - .position(|&s| s == self.values[named]) - .unwrap_or(named) as u32 - } - - /// First index holding `surface`. - pub(crate) fn by_value(&self, surface: Surface) -> Result { - self.values - .iter() - .position(|&s| s == surface) - .map(|i| i as u32) - .ok_or_else(|| format!("unknown surface {surface:?}")) - } -} - -// ---- nearest-segment candidate grid ---- - -/// Cell lists for exact nearest-segment searches on the GPU. For every -/// point of a cell (grown by one unit against index rounding), the list -/// holds, in ascending order, every segment whose distance can come within -/// the float32 slack of the nearest one; see `near_grid`. -pub struct NearGrid { - pub x0: f64, - pub y0: f64, - pub(crate) cell: f64, - pub nx: u32, - pub ny: u32, - pub start: Vec, - pub items: Vec, -} - -fn segment_distance(p: [f64; 2], a: [f64; 2], b: [f64; 2]) -> f64 { - let d = [b[0] - a[0], b[1] - a[1]]; - let l2 = d[0] * d[0] + d[1] * d[1]; - let t = if l2 > 0.0 { - (((p[0] - a[0]) * d[0] + (p[1] - a[1]) * d[1]) / l2).clamp(0.0, 1.0) - } else { - 0.0 - }; - let q = [a[0] + d[0] * t - p[0], a[1] + d[1] * t - p[1]]; - (q[0] * q[0] + q[1] * q[1]).sqrt() -} - -fn box_distance(a: [f64; 4], b: [f64; 4]) -> f64 { - let dx = (a[0] - b[2]).max(b[0] - a[2]).max(0.0); - let dy = (a[1] - b[3]).max(b[1] - a[3]).max(0.0); - (dx * dx + dy * dy).sqrt() -} - -/// Index for preparing GPU grids. Bounds only prune candidates; leaves -/// retain the original distance calculation and segment indices. -struct SegmentBoxTree { - bounds: [f64; 4], - indices: Vec, - children: Option<[Box; 2]>, -} - -impl SegmentBoxTree { - fn new(boxes: &[[f64; 4]], mut indices: Vec) -> Self { - let bounds = indices.iter().fold( - [f64::INFINITY, f64::INFINITY, f64::NEG_INFINITY, f64::NEG_INFINITY], - |b, &i| { - let a = boxes[i]; - [b[0].min(a[0]), b[1].min(a[1]), b[2].max(a[2]), b[3].max(a[3])] - }, - ); - let children = if indices.len() > 16 { - let axis = usize::from(bounds[3] - bounds[1] > bounds[2] - bounds[0]); - let middle = indices.len() / 2; - indices.select_nth_unstable_by(middle, |&a, &b| { - (boxes[a][axis] + boxes[a][axis + 2]).total_cmp(&(boxes[b][axis] + boxes[b][axis + 2])) - }); - let right = indices.split_off(middle); - let left = std::mem::take(&mut indices); - Some([Box::new(Self::new(boxes, left)), Box::new(Self::new(boxes, right))]) - } else { - None - }; - Self { - bounds, - indices, - children, - } - } - - fn visit(&self, query: [f64; 4], limit: &mut f64, visitor: &mut impl FnMut(usize, &mut f64)) { - if box_distance(self.bounds, query) > *limit + 1e-12 * (1.0 + *limit) { - return; - } - if let Some(children) = &self.children { - let first = usize::from(box_distance(children[1].bounds, query) < box_distance(children[0].bounds, query)); - children[first].visit(query, limit, visitor); - children[first ^ 1].visit(query, limit, visitor); - } else { - for &i in &self.indices { - visitor(i, limit); - } - } - } -} - -/// Builds the grid over the segments' bounding box grown by `margin`. -/// For a cell box C, `bound = min_s max_{corner c} |c - s|` bounds the -/// nearest distance of every point of C (distance to a segment is convex); -/// segment s is listed if the distance from C to its bounding box is at most -/// `bound` plus the slack. The GPU's float32 distances differ from the exact -/// ones by far less than the slack `1 + 1e-4 * magnitude` (as in -/// segment_grid), so no segment that can tie or beat the nearest is dropped. -pub fn near_grid(segments: &[([f64; 2], [f64; 2])], cell: f64, margin: f64) -> Option { - if segments.is_empty() - || segments - .iter() - .any(|(a, b)| !(a[0].is_finite() && a[1].is_finite() && b[0].is_finite() && b[1].is_finite())) - { - return None; - } - let boxes: Vec<[f64; 4]> = segments - .iter() - .map(|(a, b)| [a[0].min(b[0]), a[1].min(b[1]), a[0].max(b[0]), a[1].max(b[1])]) - .collect(); - let index = SegmentBoxTree::new(&boxes, (0..segments.len()).collect()); - let lo = boxes - .iter() - .fold([f64::INFINITY; 2], |l, b| [l[0].min(b[0]), l[1].min(b[1])]); - let hi = boxes - .iter() - .fold([f64::NEG_INFINITY; 2], |h, b| [h[0].max(b[2]), h[1].max(b[3])]); - let (x0, y0) = (lo[0] - margin, lo[1] - margin); - let nx = ((hi[0] + margin - x0) / cell).ceil() as u32; - let ny = ((hi[1] + margin - y0) / cell).ceil() as u32; - let magnitude = [x0, y0, x0 + nx as f64 * cell, y0 + ny as f64 * cell] - .iter() - .fold(0.0f64, |m, v| m.max(v.abs())); - let slack = 1.0 + magnitude * 1e-4; - let lists: Vec> = (0..nx as usize * ny as usize) - .into_par_iter() - .map(|c| { - let (cx, cy) = ((c % nx as usize) as f64, (c / nx as usize) as f64); - let bx = [ - x0 + cx * cell - 1.0, - y0 + cy * cell - 1.0, - x0 + (cx + 1.0) * cell + 1.0, - y0 + (cy + 1.0) * cell + 1.0, - ]; - let corners = [[bx[0], bx[1]], [bx[2], bx[1]], [bx[0], bx[3]], [bx[2], bx[3]]]; - // Distance to a segment's box from the cell center is a lower - // bound on its greatest corner distance. Visit nearby boxes - // first, then prune boxes that cannot improve the bound. - let center = [(bx[0] + bx[2]) * 0.5, (bx[1] + bx[3]) * 0.5]; - let mut bound = f64::INFINITY; - index.visit( - [center[0], center[1], center[0], center[1]], - &mut bound, - &mut |i, bound| { - let (a, b) = segments[i]; - let farthest = corners - .iter() - .map(|&p| segment_distance(p, a, b)) - .fold(0.0f64, f64::max); - *bound = bound.min(farthest); - }, - ); - let mut limit = bound * (1.0 + 1e-4) + slack; - let mut list = Vec::new(); - index.visit(bx, &mut limit, &mut |i, limit| { - if box_distance(boxes[i], bx) <= *limit { - list.push(i as u32); - } - }); - list.sort_unstable(); - list - }) - .collect(); - let mut start = Vec::with_capacity(lists.len() + 1); - let mut items = Vec::new(); - start.push(0u32); - for list in &lists { - items.extend_from_slice(list); - start.push(items.len() as u32); - } - Some(NearGrid { - x0, - y0, - cell, - nx, - ny, - start, - items, - }) -} - -pub(crate) const SHAPE_CELL: f64 = 256.0; -pub(crate) const SHAPE_MARGIN: f64 = 128.0; - -/// Physics shape lists for the broad-pair update (gpu/sim/step.h -/// update_pairs): shape i is listed, ascending, in every cell that its box -/// grown by one unit meets after growing the cell by `margin`. A query box -/// starting in a cell and ending before its far edges plus `margin` finds -/// every overlapping shape there; other queries scan all shapes. -pub fn shape_grid(boxes: &[([f64; 2], [f64; 2])], cell: f64, margin: f64) -> Option { - if boxes.is_empty() - || boxes - .iter() - .any(|(a, b)| !(a[0].is_finite() && a[1].is_finite() && b[0].is_finite() && b[1].is_finite())) - { - return None; - } - let lo = boxes - .iter() - .fold([f64::INFINITY; 2], |l, (a, _)| [l[0].min(a[0]), l[1].min(a[1])]); - let hi = boxes - .iter() - .fold([f64::NEG_INFINITY; 2], |h, (_, b)| [h[0].max(b[0]), h[1].max(b[1])]); - let (x0, y0) = (lo[0] - margin, lo[1] - margin); - let nx = ((hi[0] + margin - x0) / cell).ceil().max(1.0) as u32; - let ny = ((hi[1] + margin - y0) / cell).ceil().max(1.0) as u32; - let padded: Vec<_> = boxes - .iter() - .map(|(a, b)| [a[0] - 1.0, a[1] - 1.0, b[0] + 1.0, b[1] + 1.0]) - .collect(); - let index = SegmentBoxTree::new(&padded, (0..boxes.len()).collect()); - let mut start = vec![0u32]; - let mut items = Vec::new(); - for cy in 0..ny { - for cx in 0..nx { - let c = [ - x0 + cx as f64 * cell - margin, - y0 + cy as f64 * cell - margin, - x0 + (cx + 1) as f64 * cell + margin, - y0 + (cy + 1) as f64 * cell + margin, - ]; - let row_start = items.len(); - index.visit(c, &mut 0.0, &mut |i, _| { - let (a, b) = boxes[i]; - if a[0] - 1.0 <= c[2] && b[0] + 1.0 >= c[0] && a[1] - 1.0 <= c[3] && b[1] + 1.0 >= c[1] { - items.push(i as u32); - } - }); - items[row_start..].sort_unstable(); - start.push(items.len() as u32); - } - } - Some(NearGrid { - x0, - y0, - cell, - nx, - ny, - start, - items, - }) -} - -impl NearGrid { - fn desc(grid: Option<&NearGrid>) -> NearGridDesc { - match grid { - Some(g) => NearGridDesc { - x0: g.x0, - y0: g.y0, - cell: g.cell, - nx: g.nx, - ny: g.ny, - start: g.start.as_ptr(), - items: g.items.as_ptr(), - }, - None => NearGridDesc { - x0: 0.0, - y0: 0.0, - cell: 1.0, - nx: 0, - ny: 0, - start: std::ptr::null(), - items: std::ptr::null(), - }, - } - } - - /// (cells, mean list length, longest list) - pub fn stats(&self) -> (usize, f64, usize) { - let cells = self.start.len() - 1; - let longest = self.start.windows(2).map(|w| (w[1] - w[0]) as usize).max().unwrap_or(0); - (cells, self.items.len() as f64 / cells.max(1) as f64, longest) - } -} - -pub(crate) const GRID_CELL: f64 = 64.0; -pub(crate) const GRID_MARGIN: f64 = 1024.0; - -// ---- track export ---- - -/// Owned host arrays behind a `WorldDesc`. -pub struct TrackArrays { - math: MathProfile, - pub surfaces: SurfaceTable, - surface_rows: Vec<[f32; 4]>, - default_surface: u32, - tiles: (i64, i64, i64, i64, Vec), - path_rows: Vec<[f32; 8]>, - path_offsets: Vec, - path_xy: Vec, - pub path_grid: Option, - curve_position: Vec, - curve_offsets: Vec, - curve_forwards: Vec, - curve_length: f32, - pub curve_grid: Option, - shapes: Vec, - shape_local: Vec, - shape_normals: Vec, - pub shape_grid: Option, -} - -impl TrackArrays { - pub(crate) fn new(world: &World) -> Result { - let track = &world.track; - let surfaces = SurfaceTable::new(&world.vehicle); - let surface_rows = surfaces - .values - .iter() - .map(|s| [s.grip as f32, s.power as f32, s.steering as f32, 0.0]) - .collect(); - let default_surface = surfaces.by_name("asphalt"); - let (x0, y0, nx, ny, cells) = track.gpu_tiles(); - let tiles = cells - .into_iter() - .map(|(name, connection)| { - let surface = name.map_or(default_surface, |n| surfaces.by_name(&n)); - let (connected, first, last) = match connection { - Some((a, b)) => (1, f2(F2::from(a)), f2(F2::from(b))), - None => (0, [0.0; 2], [0.0; 2]), - }; - TileCell { - surface, - connected, - first, - last, - } - }) - .collect(); - let path_rows: Vec<[f32; 8]> = track - .gpu_path_rows() - .into_iter() - .map(|r| [r[0], r[1], r[2], r[3], r[4], 0.0, 0.0, 0.0]) - .collect(); - let path_offsets = track.path_offsets.clone(); - if track.path.len() != path_offsets.len() { - return Err("path points and offsets differ in length".into()); - } - let path_xy = track.path.iter().flat_map(|p| [p.x, p.y]).collect(); - if path_offsets - .windows(2) - .any(|w| w[1].partial_cmp(&w[0]) != Some(std::cmp::Ordering::Greater)) - { - return Err("GPU closest_path needs strictly increasing path offsets".into()); - } - let path_grid = near_grid( - &path_rows - .iter() - .map(|r| ([r[0] as f64, r[1] as f64], [(r[0] + r[2]) as f64, (r[1] + r[3]) as f64])) - .collect::>(), - GRID_CELL, - GRID_MARGIN, - ); - let (curve_position, curve_offsets, curve_forwards, curve_length, curve_grid) = match &track.curve { - Some(c) => { - let points: Vec = c.points.iter().map(|&p| f2(p)).collect(); - let segments: Vec<_> = points - .windows(2) - .map(|w| ([w[0][0] as f64, w[0][1] as f64], [w[1][0] as f64, w[1][1] as f64])) - .collect(); - ( - points, - c.offsets.clone(), - c.forwards.iter().map(|&p| f2(p)).collect(), - c.length(), - near_grid(&segments, GRID_CELL, GRID_MARGIN), - ) - } - None => (Vec::new(), Vec::new(), Vec::new(), 0.0, None), - }; - let mut shape_local = Vec::new(); - let mut shape_normals = Vec::new(); - let mut shapes = Vec::new(); - for s in &track.shapes { - if s.local.len() > MAX_SHAPE_POINTS { - return Err(format!("shape with {} points exceeds MAX_SHAPE_POINTS", s.local.len())); - } - let first = shape_local.len() as u32; - for (p, n) in s.local.iter().zip(&s.local_normals) { - shape_local.push(exact2(*p, "shape local point")?); - shape_normals.push(exact2(*n, "shape normal")?); - } - shapes.push(ShapeDev { - origin: exact2(s.origin_f32, "shape origin")?, - friction: s.friction as f32, - bounce: s.bounce as f32, - min_x: s.min.x, - min_y: s.min.y, - max_x: s.max.x, - max_y: s.max.y, - first, - count: s.local.len() as u32, - wall_first: s.wall_first as u32, - pad: 0, - }); - } - let shape_boxes: Vec<_> = track - .shapes - .iter() - .map(|s| ([s.min.x, s.min.y], [s.max.x, s.max.y])) - .collect(); - let shape_grid = shape_grid(&shape_boxes, SHAPE_CELL, SHAPE_MARGIN); - Ok(TrackArrays { - math: world.math, - surfaces, - surface_rows, - default_surface, - tiles: (x0, y0, nx, ny, tiles), - path_rows, - path_offsets, - path_xy, - path_grid, - curve_position, - curve_offsets, - curve_forwards, - curve_length, - curve_grid, - shapes, - shape_local, - shape_normals, - shape_grid, - }) - } - - /// Checks every count represented by a u32 in the native C descriptor. - #[cfg(not(target_arch = "wasm32"))] - pub(crate) fn validate_counts(&self, rays: &(Vec, Vec<[f32; 4]>, f32, usize)) -> Result<(), String> { - for (what, count) in [ - ("BSP nodes", rays.0.len()), - ("BSP walls", rays.1.len()), - ("BSP depth", rays.3), - ("surfaces", self.surface_rows.len()), - ("path points", self.path_offsets.len()), - ("curve points", self.curve_position.len()), - ("physics shapes", self.shapes.len()), - ("physics shape points", self.shape_local.len()), - ] { - u32::try_from(count).map_err(|_| format!("too many {what} for the HIP simulator"))?; - } - // gpu/sim/world.h fixes the BSP traversal stack at 48 entries. - if rays.3 > 48 { - return Err("BSP depth exceeds the HIP simulator's maximum of 48".into()); - } - for grid in [&self.path_grid, &self.curve_grid, &self.shape_grid] - .into_iter() - .flatten() - { - u32::try_from(grid.items.len()).map_err(|_| "too many grid entries for the HIP simulator")?; - let cells = (grid.nx as usize) - .checked_mul(grid.ny as usize) - .and_then(|n| n.checked_add(1)) - .ok_or("HIP grid cell count overflow")?; - if grid.start.len() != cells { - return Err("HIP grid dimensions do not match its offsets".into()); - } - } - Ok(()) - } - - /// The descriptor; `rays` are the BSP arrays of `bsp::RayTree::gpu_arrays`. - pub(crate) fn desc(&self, rays: &(Vec, Vec<[f32; 4]>, f32, usize)) -> WorldDesc { - let (x0, y0, nx, ny, tiles) = &self.tiles; - WorldDesc { - ray_node_count: rays.0.len() as u32, - ray_wall_count: rays.1.len() as u32, - ray_depth: rays.3 as u32, - ray_magnitude: rays.2, - ray_nodes: rays.0.as_ptr(), - ray_walls: rays.1.as_ptr(), - surface_count: self.surface_rows.len() as u32, - default_surface: self.default_surface, - surfaces: self.surface_rows.as_ptr(), - tile_x0: *x0, - tile_y0: *y0, - tile_nx: *nx, - tile_ny: *ny, - tiles: tiles.as_ptr(), - path_points: self.path_offsets.len() as u32, - math_profile: self.math.index(), - path_segments: self.path_rows.as_ptr(), - path_offsets: self.path_offsets.as_ptr(), - path_grid: NearGrid::desc(self.path_grid.as_ref()), - curve_points: self.curve_position.len() as u32, - curve_length: self.curve_length, - curve_position: self.curve_position.as_ptr(), - curve_offsets: self.curve_offsets.as_ptr(), - curve_forwards: self.curve_forwards.as_ptr(), - curve_grid: NearGrid::desc(self.curve_grid.as_ref()), - shape_count: self.shapes.len() as u32, - shape_point_count: self.shape_local.len() as u32, - shapes: self.shapes.as_ptr(), - shape_local: self.shape_local.as_ptr(), - shape_normals: self.shape_normals.as_ptr(), - shape_grid: NearGrid::desc(self.shape_grid.as_ref()), - shape_margin: SHAPE_MARGIN, - path_xy: self.path_xy.as_ptr(), - } - } -} - -// ---- vehicle and sensors ---- - -pub fn vehicle_desc(cfg: &VehicleConfig) -> Result { - if cfg.wheels.len() > MAX_WHEELS { - return Err(format!("{} wheels exceed MAX_WHEELS", cfg.wheels.len())); - } - let mut d = VehicleDesc::default(); - for (w, spec) in d.wheels.iter_mut().zip(&cfg.wheels) { - *w = WheelDesc { - position: f2(F2::from(spec.position)), - steering: spec.steering as u32, - handbrake_off: (spec.handbrake_power.abs() < 0.00001) as u32, - power: spec.power as f32, - brake_power: spec.brake_power as f32, - handbrake_power: spec.handbrake_power as f32, - brake_max: spec.brake_power.max(spec.handbrake_power) as f32, - max_angle_deg: spec.max_angle_deg, - }; - } - d.wheel_count = cfg.wheels.len() as u32; - d.custom_integrator = cfg.custom_integrator as u32; - d.center_steering = cfg.center_steering as u32; - d.solver_iterations = cfg.solver_iterations as u32; - d.max_contacts_reported = cfg.max_contacts_reported as u32; - d.steering_speed = cfg.steering_speed; - d.max_velocity = cfg.max_velocity; - d.dt = DT; - d.contact_max_separation = cfg.contact_max_separation; - d.grip = cfg.grip as f32; - d.air_resistance = cfg.air_resistance as f32; - d.mass = cfg.mass as f32; - d.inertia = cfg.inertia as f32; - d.linear_damp = cfg.linear_damp as f32; - d.angular_damp = cfg.angular_damp as f32; - d.dt_f = DT as f32; - d.max_velocity_f = cfg.max_velocity as f32; - d.gravity = f2(F2::from(cfg.gravity)); - d.shape_basis_x = f2(cfg.shape_basis.0); - d.shape_basis_y = f2(cfg.shape_basis.1); - d.shape_size = f2(F2::from(cfg.shape_size)); - d.shape_half = [(cfg.shape_size.x / 2.0) as f32, (cfg.shape_size.y / 2.0) as f32]; - d.shape_position = f2(F2::from(cfg.shape_position)); - d.center_of_mass = f2(F2::from(cfg.center_of_mass)); - d.friction = cfg.friction as f32; - d.bounce = cfg.bounce as f32; - d.contact_bias = cfg.contact_bias as f32; - d.allowed_penetration = cfg.allowed_penetration as f32; - d.recycle_radius = cfg.contact_recycle_radius as f32; - d.max_separation_f = cfg.contact_max_separation as f32; - Ok(d) -} - -pub fn sensor_desc(sensor: &Sensor) -> SensorDesc { - let d = |kind: u32, a: f64, b: f64| SensorDesc { - kind, - a: a as f32, - b: b as f32, - ..Default::default() - }; - match *sensor { - Sensor::Raycast { degrees, length } => { - assert!(length > 0.0, "ray length must be positive"); - d(0, degrees, length) - } - Sensor::DistanceFromWall { max_distance } => d(1, max_distance, 0.0), - Sensor::Speed => d(2, 0.0, 0.0), - Sensor::VelocityFront => d(3, 0.0, 0.0), - Sensor::VelocitySide => d(4, 0.0, 0.0), - Sensor::AccelerationFront { max_acceleration } => d(5, max_acceleration, 0.0), - Sensor::AccelerationSide { max_acceleration } => d(6, max_acceleration, 0.0), - Sensor::AngularVelocity => d(7, 0.0, 0.0), - Sensor::WheelAngle => d(8, 0.0, 0.0), - Sensor::BoostCapacity => d(9, 0.0, 0.0), - Sensor::Grip { offset } => SensorDesc { - kind: 10, - offset: f2(F2::from(offset)), - ..Default::default() - }, - Sensor::CorrectDirection => d(11, 0.0, 0.0), - Sensor::TrackCurvature { - min_lookahead, - max_lookahead, - } => d(12, min_lookahead, max_lookahead), - } -} - -/// `desc` with a ray sensor's local end point (`Car::ray_local`) in `offset`: -/// the kernels read it instead of evaluating `math`'s sine and cosine per car. -pub(crate) fn with_ray_offset(mut desc: SensorDesc, math: MathProfile) -> SensorDesc { - if desc.kind == 0 { - let angle = (desc.a - 90.0f32) * (std::f32::consts::PI / 180.0); - desc.offset = [math.cos(angle) * desc.b, math.sin(angle) * desc.b]; - } - desc -} - -// ---- simulation handle ---- - -/// `ReadMask` of gpu/sim/sensors.h: the cars whose controls update this tick. -#[repr(C)] -#[derive(Clone, Copy, Debug)] -pub struct ReadMask { - pub(crate) batch_size: u32, - pub(crate) batch_index: u32, - pub(crate) batches_per_tick: u32, - pub(crate) require_active: u32, -} - -impl ReadMask { - /// Every car, active or not. - pub const ALL: ReadMask = ReadMask { - batch_size: u32::MAX, - batch_index: 0, - batches_per_tick: 8, - require_active: 0, - }; -} - -/// GPU control order of `GpuAgent::controls`. -pub(crate) const CONTROL_NAMES: [&str; 5] = ["acceleration", "steering", "brake", "handbrake", "boost"]; - -/// For each control, the last output neuron with its name (-1: none, 0.0), -/// as `Controls(**dict(zip(names, outputs)))` assigns them. -pub fn control_sources(outputs: &[String]) -> [i32; 5] { - CONTROL_NAMES.map(|c| { - outputs - .iter() - .rposition(|o| o.to_lowercase() == c) - .map_or(-1, |j| j as i32) - }) -} - -impl ReadMask { - /// `Schedule::update_controls` for `cars` cars at `batch_index`. - pub fn training(cars: usize, batch_index: usize, batches_per_tick: usize) -> ReadMask { - ReadMask { - batch_size: cars.div_ceil(8).max(1) as u32, - batch_index: batch_index as u32, - batches_per_tick: batches_per_tick as u32, - require_active: 1, - } - } -} - -#[cfg(not(target_arch = "wasm32"))] -pub struct GpuSim<'a> { - gpu: &'a Gpu, - handle: *mut c_void, - free: unsafe extern "C" fn(*mut c_void), - capacity: usize, - pub(crate) sensor_count: usize, - pub(crate) cars: usize, - /// Caller-chosen tag of the uploaded networks (e.g. the generation), so - /// unchanged networks are not uploaded again. - pub network_tag: Option, - network_count: usize, - parameter_buffer: Vec, - /// Host staging buffers for car and agent state, retained between windows - /// so their pages stay mapped and no per-window allocation is needed. - car_buffer: Vec, - agent_buffer: Vec, -} - -/// Arguments of `altd_gpu_sim_window` (gpu/sim/altd_gpu.hip). -#[repr(C)] -#[derive(Clone, Copy, Debug, Default)] -#[cfg(not(target_arch = "wasm32"))] -pub(crate) struct WindowArgs { - pub(crate) start_tick: u64, - pub(crate) ticks: u64, - pub(crate) start_batch: u32, - pub(crate) batches_per_tick: u32, - pub(crate) stats_phase: u32, - pub(crate) stop_when_inactive: u32, - pub(crate) eliminate_on_wall: u32, - pub(crate) eliminate_when_idle: u32, - pub(crate) has_time_limit: u32, - pub(crate) pad: u32, - pub(crate) time_limit: f64, -} - -#[cfg(not(target_arch = "wasm32"))] -impl Drop for GpuSim<'_> { - fn drop(&mut self) { - unsafe { (self.free)(self.handle) }; - } -} - -#[cfg(not(target_arch = "wasm32"))] -fn check(status: i32, what: &str) { - assert_eq!( - status, - 0, - "GPU {what} failed with {}", - crate::gpu::hip::status_message(status) - ); -} - -#[cfg(not(target_arch = "wasm32"))] -fn try_check(status: i32, what: &str) -> Result<(), String> { - if status == 0 { - Ok(()) - } else { - Err(format!( - "GPU {what} failed with {}", - crate::gpu::hip::status_message(status) - )) - } -} - -#[cfg(not(target_arch = "wasm32"))] -fn network_size(shape: &[usize], sensor_count: usize, control_src: [i32; 5]) -> Result { - if !(2..=MAX_LAYERS).contains(&shape.len()) { - return Err(format!("the HIP simulator needs 2..{MAX_LAYERS} network layers")); - } - if shape[0] != sensor_count { - return Err(format!( - "the HIP network has {} inputs but the simulator has {sensor_count} sensors", - shape[0] - )); - } - for (layer, &width) in shape.iter().enumerate() { - let limit = if layer == 0 { MAX_WIDTH } else { LANES }; - if width == 0 || width > limit { - return Err(format!( - "HIP network layer {layer} has width {width}; supported widths are 1..{limit}" - )); - } - } - let outputs = *shape.last().unwrap(); - if control_src.iter().any(|&index| index < -1 || index >= outputs as i32) { - return Err("HIP control mapping is outside the network output layer".into()); - } - shape.windows(2).try_fold(0usize, |size, layer| { - layer[0] - .checked_add(1) - .and_then(|rows| rows.checked_mul(layer[1])) - .and_then(|count| size.checked_add(count)) - .ok_or_else(|| "HIP network parameter count overflow".to_string()) - }) -} - -#[cfg(not(target_arch = "wasm32"))] -impl<'a> GpuSim<'a> { - pub fn new(world: &GpuWorld<'a>, vehicle: &VehicleDesc, sensors: &[SensorDesc], capacity: usize) -> GpuSim<'a> { - Self::try_new(world, vehicle, sensors, capacity).unwrap_or_else(|e| panic!("{e}")) - } - - /// `new`, reporting invalid capacities, unsupported sensors and allocation failures. - pub fn try_new( - world: &GpuWorld<'a>, - vehicle: &VehicleDesc, - sensors: &[SensorDesc], - capacity: usize, - ) -> Result, String> { - let gpu = world.gpu(); - if sensors.len() > MAX_SENSORS { - return Err(format!( - "{} sensors exceed the HIP maximum of {MAX_SENSORS}", - sensors.len() - )); - } - if vehicle.wheel_count as usize > MAX_WHEELS { - return Err(format!( - "{} wheels exceed the HIP maximum of {MAX_WHEELS}", - vehicle.wheel_count - )); - } - let count = u32::try_from(capacity).map_err(|_| "population exceeds the HIP maximum of 4294967295")?; - if count == 0 || count > u32::MAX - 7 { - return Err("HIP population capacity must be positive and leave room for eight inference batches".into()); - } - for size in [ - std::mem::size_of::(), - std::mem::size_of::(), - MAX_SENSORS * std::mem::size_of::(), - ] { - if capacity - .checked_mul(size) - .is_none_or(|bytes| bytes > isize::MAX as usize) - { - return Err("HIP population capacity exceeds the native address space".into()); - } - } - let create: unsafe extern "C" fn( - *const c_void, - *const VehicleDesc, - *const SensorDesc, - u32, - u32, - ) -> *mut c_void = gpu.required_symbol("altd_gpu_sim_create")?; - let free = gpu.required_symbol("altd_gpu_sim_free")?; - let sensors: Vec<_> = sensors - .iter() - .map(|&desc| with_ray_offset(desc, world.arrays.math)) - .collect(); - let handle = unsafe { create(world.handle(), vehicle, sensors.as_ptr(), sensors.len() as u32, count) }; - if handle.is_null() { - return Err("altd_gpu_sim_create failed to allocate the HIP simulator".into()); - } - Ok(GpuSim { - gpu, - handle, - free, - capacity, - sensor_count: sensors.len(), - cars: 0, - network_tag: None, - network_count: 0, - parameter_buffer: Vec::new(), - car_buffer: Vec::new(), - agent_buffer: Vec::new(), - }) - } - - /// Switch uploaded tracks while retaining population and network buffers. - /// Keep `world` alive until the next switch or until this simulator is dropped. - pub fn set_world(&mut self, world: &GpuWorld<'a>) { - self.try_set_world(world).unwrap_or_else(|e| panic!("{e}")); - } - - /// `set_world`, reporting incompatible handles and HIP failures. - pub fn try_set_world(&mut self, world: &GpuWorld<'a>) -> Result<(), String> { - if !std::ptr::eq(self.gpu, world.gpu()) { - return Err("GPU world belongs to another device handle".into()); - } - let f: unsafe extern "C" fn(*mut c_void, *const c_void) -> i32 = - self.gpu.required_symbol("altd_gpu_sim_set_world")?; - try_check(unsafe { f(self.handle, world.handle()) }, "set_world") - } - - /// The retained host state buffers (empty on first use); return them with - /// `return_state_buffers` so the next window reuses the allocations. - pub(crate) fn take_state_buffers(&mut self) -> (Vec, Vec) { - ( - std::mem::take(&mut self.car_buffer), - std::mem::take(&mut self.agent_buffer), - ) - } - pub(crate) fn return_state_buffers(&mut self, cars: Vec, agents: Vec) { - self.car_buffer = cars; - self.agent_buffer = agents; - } - - pub fn upload(&mut self, cars: &[GpuCar], agents: Option<&[GpuAgent]>) { - self.try_upload(cars, agents).unwrap_or_else(|e| panic!("{e}")); - } - - /// `upload`, reporting invalid state counts and HIP failures. - pub fn try_upload(&mut self, cars: &[GpuCar], agents: Option<&[GpuAgent]>) -> Result<(), String> { - if cars.len() > self.capacity { - return Err("car population exceeds the HIP simulator capacity".into()); - } - let count = u32::try_from(cars.len()).map_err(|_| "too many cars for the HIP simulator")?; - let f: unsafe extern "C" fn(*mut c_void, u32, *const GpuCar, *const GpuAgent) -> i32 = - self.gpu.required_symbol("altd_gpu_sim_upload")?; - if let Some(a) = agents { - if a.len() != cars.len() { - return Err("HIP car and agent counts differ".into()); - } - } - try_check( - unsafe { - f( - self.handle, - count, - cars.as_ptr(), - agents.map_or(std::ptr::null(), |a| a.as_ptr()), - ) - }, - "upload", - )?; - self.cars = cars.len(); - Ok(()) - } - - /// A training window over the uploaded cars (`TrainingRunner::advance_window` - /// order); returns (executed ticks, transition_without_drive). - pub(crate) fn try_window(&self, args: &WindowArgs) -> Result<(u64, bool), String> { - let f: unsafe extern "C" fn(*mut c_void, *const WindowArgs, *mut u64, *mut u32) -> i32 = - self.gpu.required_symbol("altd_gpu_sim_window")?; - let (mut executed, mut transition) = (0u64, 0u32); - try_check( - unsafe { f(self.handle, args, &mut executed, &mut transition) }, - "window", - )?; - if executed > args.ticks || transition > 1 || executed < transition as u64 { - return Err("HIP window returned invalid tick counters".into()); - } - Ok((executed, transition != 0)) - } - - /// Replaces the agents of the uploaded cars. - pub fn upload_agents(&mut self, agents: &[GpuAgent]) { - let f: unsafe extern "C" fn(*mut c_void, u32, *const GpuAgent) -> i32 = - self.gpu.symbol("altd_gpu_sim_upload_agents"); - check( - unsafe { f(self.handle, agents.len() as u32, agents.as_ptr()) }, - "upload_agents", - ); - } - - /// The number of active uploaded cars, counted on the device; `None` when - /// the library predates `altd_gpu_sim_active_count`. - pub(crate) fn try_active_count(&self) -> Result, String> { - let Some(f) = self - .gpu - .try_symbol:: i32>("altd_gpu_sim_active_count") - else { - return Ok(None); - }; - let mut count = 0u32; - try_check(unsafe { f(self.handle, &mut count) }, "active_count")?; - if count as usize > self.cars { - return Err("HIP active count exceeds the uploaded cars".into()); - } - Ok(Some(count as usize)) - } - - pub fn download(&self, cars: Option<&mut Vec>, agents: Option<&mut Vec>) { - self.try_download(cars, agents).unwrap_or_else(|e| panic!("{e}")); - } - - /// `download`, reporting allocation, missing-symbol and HIP failures. - pub fn try_download( - &self, - cars: Option<&mut Vec>, - agents: Option<&mut Vec>, - ) -> Result<(), String> { - let f: unsafe extern "C" fn(*mut c_void, *mut GpuCar, *mut GpuAgent) -> i32 = - self.gpu.required_symbol("altd_gpu_sim_download")?; - let cars = cars - .map(|c| { - c.try_reserve(self.cars.saturating_sub(c.len())) - .map_err(|e| format!("HIP car readback allocation failed: {e}"))?; - c.resize(self.cars, GpuCar::zeroed()); - Ok::<_, String>(c.as_mut_ptr()) - }) - .transpose()?; - let agents = agents - .map(|a| { - a.try_reserve(self.cars.saturating_sub(a.len())) - .map_err(|e| format!("HIP agent readback allocation failed: {e}"))?; - a.resize(self.cars, GpuAgent::default()); - Ok::<_, String>(a.as_mut_ptr()) - }) - .transpose()?; - try_check( - unsafe { - f( - self.handle, - cars.unwrap_or(std::ptr::null_mut()), - agents.unwrap_or(std::ptr::null_mut()), - ) - }, - "download", - ) - } - - /// One network per car: `params` holds `networks` flat parameter vectors of `shape`. - pub fn networks(&mut self, shape: &[usize], params: &[f64], control_src: [i32; 5]) { - self.try_networks(shape, params, control_src) - .unwrap_or_else(|e| panic!("{e}")); - } - - /// `networks`, validating the HIP kernel limits before upload and allocation. - pub fn try_networks(&mut self, shape: &[usize], params: &[f64], control_src: [i32; 5]) -> Result<(), String> { - let size = network_size(shape, self.sensor_count, control_src)?; - if params.is_empty() || !params.len().is_multiple_of(size) { - return Err("HIP network parameters do not contain complete networks".into()); - } - let count = params.len() / size; - if count > self.capacity { - return Err("network population exceeds the HIP simulator capacity".into()); - } - let count = u32::try_from(count).map_err(|_| "too many networks for the HIP simulator")?; - let f: unsafe extern "C" fn(*mut c_void, u32, *const u32, u32, *const f64, *const i32) -> i32 = - self.gpu.required_symbol("altd_gpu_sim_networks")?; - let widths: Vec = shape.iter().map(|&w| w as u32).collect(); - let status = unsafe { - f( - self.handle, - widths.len() as u32, - widths.as_ptr(), - count, - params.as_ptr(), - control_src.as_ptr(), - ) - }; - try_check(status, "networks")?; - self.network_count = params.len() / size; - Ok(()) - } - - pub(crate) fn network_count(&self) -> usize { - self.network_count - } - - pub(crate) fn take_parameter_buffer(&mut self) -> Vec { - std::mem::take(&mut self.parameter_buffer) - } - pub(crate) fn return_parameter_buffer(&mut self, buffer: Vec) { - self.parameter_buffer = buffer; - } - - /// Reuse a flat upload buffer, filling disjoint networks in parallel. - pub fn upload_networks( - &mut self, - networks: &[crate::nn::network::Network], - control_src: [i32; 5], - ) -> Result<(), String> { - self.upload_network_refs(&networks.iter().collect::>(), control_src) - } - - pub(crate) fn upload_agent_networks( - &mut self, - agents: &[crate::training::TrainingAgent], - control_src: [i32; 5], - ) -> Result<(), String> { - self.upload_network_refs(&agents.iter().map(|a| &a.network).collect::>(), control_src) - } - - fn upload_network_refs( - &mut self, - networks: &[&crate::nn::network::Network], - control_src: [i32; 5], - ) -> Result<(), String> { - use rayon::prelude::*; - let first = networks.first().ok_or("no networks")?; - let size = network_size(&first.shape, self.sensor_count, control_src)?; - if networks.len() > self.capacity { - return Err("network population exceeds the HIP simulator capacity".into()); - } - if networks - .iter() - .any(|n| n.shape != first.shape || n.params.len() != size) - { - return Err("networks of different shapes or invalid parameter counts".into()); - } - let mut profile = crate::training::training_profile::Profile::new("network_upload"); - let mut params = std::mem::take(&mut self.parameter_buffer); - let length = networks - .len() - .checked_mul(size) - .ok_or("network parameter count overflow")?; - params - .try_reserve(length.saturating_sub(params.len())) - .map_err(|e| format!("HIP network upload allocation failed: {e}"))?; - params.resize(length, 0.0); - params - .par_chunks_mut(size) - .zip(networks.par_iter()) - .for_each(|(out, network)| out.copy_from_slice(&network.params)); - profile.mark("pack"); - let result = self.try_networks(&first.shape, ¶ms, control_src); - profile.mark("upload"); - self.parameter_buffer = params; - result - } - - /// Exact population novelty for the currently uploaded networks. - pub fn novelty(&self) -> Vec { - self.try_novelty().unwrap_or_else(|e| panic!("{e}")) - } - - /// `novelty`, reporting allocation, missing-symbol and HIP failures. - pub fn try_novelty(&self) -> Result, String> { - let f: unsafe extern "C" fn(*mut c_void, u32, *mut f64) -> i32 = - self.gpu.required_symbol("altd_gpu_sim_novelty")?; - let mut out = Vec::new(); - out.try_reserve(self.network_count) - .map_err(|e| format!("HIP novelty allocation failed: {e}"))?; - out.resize(self.network_count, 0.0); - try_check( - unsafe { f(self.handle, self.network_count as u32, out.as_mut_ptr()) }, - "population novelty", - )?; - Ok(out) - } - - /// Sensors and network forward for the cars in `mask` (sets their controls). - pub fn infer(&self, mask: ReadMask) { - let f: unsafe extern "C" fn(*mut c_void, ReadMask) -> i32 = self.gpu.symbol("altd_gpu_sim_infer"); - check(unsafe { f(self.handle, mask) }, "infer"); - } - - /// One physics tick for every car with its agent's controls (`Car::step` - /// if `drive`, else `Car::passive_step`); a reported wall contact sets - /// AGENT_PENDING_CONTACT. - /// A car the step deactivates records `tick` in `deactivated_at`. - pub fn step(&self, drive: bool, eliminate_on_wall: bool, tick: u64) { - let f: unsafe extern "C" fn(*mut c_void, u32, u32, u64) -> i32 = self.gpu.symbol("altd_gpu_sim_step"); - check( - unsafe { f(self.handle, drive as u32, eliminate_on_wall as u32, tick) }, - "step", - ); - } - - /// `Schedule::update_stats` for every car at statistics tick `tick`; - /// `stats_tick` is `tick - first_stats_tick`. - pub fn stats(&self, tick: u64, stats_tick: u64, eliminate_when_idle: bool) { - let f: unsafe extern "C" fn(*mut c_void, u64, u64, u32) -> i32 = self.gpu.symbol("altd_gpu_sim_stats"); - check( - unsafe { f(self.handle, tick, stats_tick, eliminate_when_idle as u32) }, - "stats", - ); - } - - /// Reads the sensors of the cars in `mask`; returns all cars' inputs, car-major. - pub fn sensors(&self, mask: ReadMask) -> Vec { - let f: unsafe extern "C" fn(*mut c_void, ReadMask, *mut f64) -> i32 = self.gpu.symbol("altd_gpu_sim_sensors"); - let mut out = vec![0.0; self.cars * self.sensor_count]; - check(unsafe { f(self.handle, mask, out.as_mut_ptr()) }, "sensors"); - out - } -} - -// ---- agents ---- - -/// The GPU agent state of a training agent (controls, statistics, window -/// bookkeeping); errors on states the GPU port does not model. -pub fn agent_export(a: &crate::training::TrainingAgent) -> Result { - let s = &a.stats; - let t = &s.score; - if s.recent_score_diffs.len() > RECENT { - return Err("more than RECENT score diffs".into()); - } - let c = &a.controls; - let mut g = GpuAgent { - controls: [c.acceleration, c.steering, c.brake, c.handbrake, c.boost], - previous_offset: t.previous_offset.unwrap_or(0.0), - tracker_score: t.total_score, - lap_crossing_fraction: t.lap_crossing_fraction.unwrap_or(0.0), - lap_count: t.lap_count, - network_novelty: s.network_novelty, - update_count: s.update_count, - collision_count: s.collision_count, - total_score: s.total_score, - total_speed: s.total_speed, - total_abs_steering: s.total_abs_steering, - total_actual_distance: s.total_actual_distance, - total_drifting_amount: s.total_drifting_amount, - total_momentum_change_frequency: s.total_momentum_change_frequency, - total_throttle_change_frequency: s.total_throttle_change_frequency, - total_braking_frequency: s.total_braking_frequency, - total_distance_from_wall: s.total_distance_from_wall, - total_distance_from_center: s.total_distance_from_center, - best_lap_time: s.best_lap_time.unwrap_or(0.0), - idle_ticks: s.idle_ticks, - previous_speed: s.previous_speed, - previous_throttle: s.previous_throttle, - previous_score: s.previous_score, - previous_lap_count: s.previous_lap_count, - lap_start_tick: s.lap_start_tick, - drift_ticks: s.drift_ticks, - current_slip_angle_degrees: s.current_slip_angle_degrees, - continuous_drift_time: s.continuous_drift_time, - max_continuous_drift_time: s.max_continuous_drift_time, - all_laps_time_sum: s.all_laps_time_sum.unwrap_or(0.0), - recent: [0.0; RECENT], - recent_start: 0, - recent_len: s.recent_score_diffs.len() as u32, - deactivated_at: a.deactivated_at.unwrap_or(0), - flags: 0, - pad: 0, - }; - for (slot, &d) in g.recent.iter_mut().zip(&s.recent_score_diffs) { - *slot = d; - } - for (on, bit) in [ - (t.previous_offset.is_some(), AGENT_HAS_PREVIOUS_OFFSET), - (t.went_backwards, AGENT_WENT_BACKWARDS), - (t.lap_crossing_fraction.is_some(), AGENT_HAS_LAP_CROSSING), - (a.pending_contact, AGENT_PENDING_CONTACT), - (s.best_lap_time.is_some(), AGENT_HAS_BEST_LAP), - (s.all_laps_time_sum.is_some(), AGENT_HAS_ALL_LAPS), - (a.deactivated_at.is_some(), AGENT_HAS_DEACTIVATED), - ] { - g.flags |= if on { bit } else { 0 }; - } - Ok(g) -} - -/// The inverse of `agent_export` (the network and car are left alone). -pub fn agent_import(g: &GpuAgent, a: &mut crate::training::TrainingAgent) { - let on = |bit: u32| g.flags & bit != 0; - let [acceleration, steering, brake, handbrake, boost] = g.controls; - a.controls = crate::physics::car::Controls { - acceleration, - steering, - brake, - handbrake, - boost, - }; - a.pending_contact = on(AGENT_PENDING_CONTACT); - a.deactivated_at = on(AGENT_HAS_DEACTIVATED).then_some(g.deactivated_at); - let s = &mut a.stats; - s.score = crate::training::ScoreTracker { - previous_offset: on(AGENT_HAS_PREVIOUS_OFFSET).then_some(g.previous_offset), - total_score: g.tracker_score, - lap_count: g.lap_count, - went_backwards: on(AGENT_WENT_BACKWARDS), - lap_crossing_fraction: on(AGENT_HAS_LAP_CROSSING).then_some(g.lap_crossing_fraction), - }; - s.network_novelty = g.network_novelty; - s.update_count = g.update_count; - s.collision_count = g.collision_count; - s.total_score = g.total_score; - s.total_speed = g.total_speed; - s.total_abs_steering = g.total_abs_steering; - s.total_actual_distance = g.total_actual_distance; - s.total_drifting_amount = g.total_drifting_amount; - s.total_momentum_change_frequency = g.total_momentum_change_frequency; - s.total_throttle_change_frequency = g.total_throttle_change_frequency; - s.total_braking_frequency = g.total_braking_frequency; - s.total_distance_from_wall = g.total_distance_from_wall; - s.total_distance_from_center = g.total_distance_from_center; - s.best_lap_time = on(AGENT_HAS_BEST_LAP).then_some(g.best_lap_time); - s.idle_ticks = g.idle_ticks; - s.previous_speed = g.previous_speed; - s.previous_throttle = g.previous_throttle; - s.previous_score = g.previous_score; - s.previous_lap_count = g.previous_lap_count; - s.lap_start_tick = g.lap_start_tick; - s.drift_ticks = g.drift_ticks; - s.current_slip_angle_degrees = g.current_slip_angle_degrees; - s.continuous_drift_time = g.continuous_drift_time; - s.max_continuous_drift_time = g.max_continuous_drift_time; - s.all_laps_time_sum = on(AGENT_HAS_ALL_LAPS).then_some(g.all_laps_time_sum); - s.recent_score_diffs.clear(); - s.recent_score_diffs - .extend((0..g.recent_len as usize).map(|k| g.recent[(g.recent_start as usize + k) % RECENT])); -} - -#[cfg(all(test, not(target_arch = "wasm32")))] -mod native_validation_tests { - use super::*; - - #[test] - fn network_limits_match_the_hip_forward_kernel() { - let controls = [0, 1, 2, 3, 4]; - assert_eq!(network_size(&[20, 16, 5], 20, controls).unwrap(), 421); - assert!(network_size(&[32, 16, 5], 32, controls).is_ok()); - assert!(network_size(&[20, 32, 5], 20, controls) - .unwrap_err() - .contains("width 32")); - assert!(network_size(&[20, 5, 17], 20, controls).unwrap_err().contains("1..16")); - assert!(network_size(&[33, 5], 33, controls).unwrap_err().contains("1..32")); - assert!(network_size(&[20, 0, 5], 20, controls).is_err()); - assert!(network_size(&[20], 20, controls).is_err()); - assert!(network_size(&[20; MAX_LAYERS + 1], 20, controls) - .unwrap_err() - .contains("network layers")); - assert!(network_size(&[20, 5], 19, controls).unwrap_err().contains("sensors")); - assert!(network_size(&[20, 5], 20, [0, 1, 2, 3, 5]) - .unwrap_err() - .contains("control mapping")); - assert!(network_size(&[20, 5], 20, [-2; 5]).is_err()); - assert!(network_size(&[20, 5], 20, [-1; 5]).is_ok()); - } - - #[test] - fn a_hip_window_error_returns_without_panicking() { - let gpu = match crate::training::session::hip_device() { - Ok((gpu, _)) => gpu, - Err(reason) => { - eprintln!("skipping HIP window error check: {reason}"); - return; - } - }; - let template = serde_json::from_str(include_str!("../../assets/scenes/formula_template.json")).unwrap(); - let settings = serde_json::from_str(include_str!("../../assets/random_track_settings.json")).unwrap(); - let (_, scene) = crate::track::training_tracks::training_scene_at( - &template, - &settings, - None, - 1729, - 0, - 0, - crate::math::profile::MathProfile::Proton, - ) - .unwrap(); - let world = World::from_scene(&scene); - let prepared = crate::gpu::hip::PreparedGpuWorld::new(&world).unwrap(); - let uploaded = GpuWorld::try_from_prepared(gpu, prepared).unwrap(); - let mut sim = GpuSim::try_new( - &uploaded, - &vehicle_desc(&world.vehicle).unwrap(), - &[sensor_desc(&Sensor::Speed)], - 1, - ) - .unwrap(); - // An empty simulator has no uploaded networks, which the C API rejects. - let error = sim - .try_window(&WindowArgs { - ticks: 1, - ..Default::default() - }) - .unwrap_err(); - assert!(error.contains("window") && error.contains("-2"), "{error}"); - let error = sim.try_networks(&[1, 32, 5], &[], [0, 1, 2, 3, 4]).unwrap_err(); - assert!(error.contains("width 32"), "{error}"); - } -} diff --git a/github-simulator/source/src/lib.rs b/github-simulator/source/src/lib.rs deleted file mode 100644 index a27f519ad4bc0e7186de89b466f642b275ca661c..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/lib.rs +++ /dev/null @@ -1,26 +0,0 @@ -//! Bit-exact driving physics, generated tracks, and neural-network training. - -/// Package version, or the CI build version of a nightly release. -pub const VERSION: &str = match option_env!("ALTD_BUILD_VERSION") { - Some(version) => version, - None => env!("CARGO_PKG_VERSION"), -}; - -/// GPU state layouts and the native HIP loader. -pub mod gpu; -/// Runtime-compatible arithmetic and vector types. -pub mod math; -/// Neural-network parameters and inference. -pub mod nn; -/// Vehicle dynamics, contacts, and recorded-state reconstruction. -pub mod physics; -/// `altd-sim serve`: the WebSocket server Drive Lab trains on. -#[cfg(all(feature = "server", not(target_arch = "wasm32")))] -pub mod server; -/// Scene geometry, spatial queries, and generated tracks. -pub mod track; -/// Population execution, evolution, evaluation, and embedding sessions. -pub mod training; -/// JavaScript bindings and WebGPU execution. -#[cfg(all(target_arch = "wasm32", feature = "wasm"))] -pub mod wasm; diff --git a/github-simulator/source/src/math/double_math.rs b/github-simulator/source/src/math/double_math.rs deleted file mode 100644 index 8d9b958e5a09a7b16049ea3b0277205595ba42cb..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/math/double_math.rs +++ /dev/null @@ -1,229 +0,0 @@ -//! Double math from the shipped Proton 11 UCRT (musl/Arm, non-FMA path). -//! Copyright (c) 2018, Arm Limited. SPDX-License-Identifier: MIT -//! Arithmetic order, C round (ties away), and unsigned wrap are intentional. -use crate::math::double_math_tables::*; -const SIGN: u64 = 1 << 63; -const INF: u64 = 0x7ff0000000000000; -const NEG_NAN: f64 = f64::from_bits(0xfff8000000000000); -// Keep the compensated sum grouping visible; reassociation changes rounding. -#[allow(clippy::assign_op_pattern)] -fn special(tmp: f64, mut bits: u64, k: u64) -> f64 { - if k & 0x80000000 == 0 { - bits = bits.wrapping_sub(1009u64 << 52); - let scale = f64::from_bits(bits); - return f64::from_bits(2032u64 << 52) * (scale + scale * tmp); - } - bits = bits.wrapping_add(1022u64 << 52); - let scale = f64::from_bits(bits); - let mut y = scale + scale * tmp; - if y.abs() < 1.0 { - let one = if y < 0.0 { -1.0 } else { 1.0 }; - let mut lo = scale - y + scale * tmp; - let hi = one + y; - lo = one - hi + y + lo; - y = (hi + lo) - one; - if y == 0.0 { - y = f64::from_bits(bits & SIGN); - } - } - f64::MIN_POSITIVE * y -} -fn exp_inner(x: f64, xtail: f64, negative: bool) -> f64 { - let exponent = (x.to_bits() >> 52) & 0x7ff; - if exponent < 969 { - return if negative { -(1.0 + x) } else { 1.0 + x }; - } - if exponent >= 1033 { - let y = if x.is_sign_negative() { 0.0 } else { f64::INFINITY }; - return if negative { -y } else { y }; - } - let kd = (INV_LN2_N * x).round(); - let ki = kd as i64 as u64; - let r = (x + kd * NEG_LN2_HI_N + kd * NEG_LN2_LO_N) + xtail; - let idx = 2 * (ki as usize % 128); - let bias = if negative { 0x800u64 << 7 } else { 0 }; - let top = ki.wrapping_add(bias) << 45; - let tail = f64::from_bits(EXP_TAB[idx]); - let bits = EXP_TAB[idx + 1].wrapping_add(top); - let r2 = r * r; - let c = EXP_POLY; - let tmp = tail + r + r2 * (c[0] + r * c[1]) + r2 * r2 * (c[2] + r * c[3]); - if exponent >= 1032 { - return special(tmp, bits, ki); - } - let scale = f64::from_bits(bits); - scale + scale * tmp -} -pub fn exp(x: f64) -> f64 { - if x.is_nan() { - return x; - } - exp_inner(x, 0.0, false) -} -fn pow_log(ix: u64) -> (f64, f64) { - let tmp = ix.wrapping_sub(0x3fe6955500000000); - let i = ((tmp >> 45) % 128) as usize; - let kd = ((tmp as i64) >> 52) as f64; - let iz = ix.wrapping_sub(tmp & (0xfffu64 << 52)); - let z = f64::from_bits(iz); - let [invc, logc, logctail] = POW_TAB[i]; - let zhi = f64::from_bits(iz.wrapping_add(1 << 31) & (u64::MAX << 32)); - let zlo = z - zhi; - let rhi = zhi * invc - 1.0; - let rlo = zlo * invc; - let r = rhi + rlo; - let t1 = kd * LN2_HI + logc; - let t2 = t1 + r; - let lo1 = kd * LN2_LO + logctail; - let lo2 = t1 - t2 + r; - let a = POW_POLY; - let ar = a[0] * r; - let ar2 = r * ar; - let ar3 = r * ar2; - let arhi = a[0] * rhi; - let arhi2 = rhi * arhi; - let hi = t2 + arhi2; - let lo3 = rlo * (ar + arhi); - let lo4 = t2 - hi + arhi2; - let p = ar3 * (a[1] + r * a[2] + ar2 * (a[3] + r * a[4] + ar2 * (a[5] + r * a[6]))); - let lo = lo1 + lo2 + lo3 + lo4 + p; - let y = hi + lo; - (y, hi - y + lo) -} -fn integer_kind(bits: u64) -> u32 { - let e = (bits >> 52) & 0x7ff; - if e < 1023 { - return 0; - } - if e > 1075 { - return 2; - } - let bit = 1u64 << (1075 - e); - if bits & (bit - 1) != 0 { - 0 - } else if bits & bit != 0 { - 1 - } else { - 2 - } -} -pub fn pow(x: f64, y: f64) -> f64 { - let mut ix = x.to_bits(); - let iy = y.to_bits(); - let mut topx = (ix >> 52) as u32; - let topy = (iy >> 52) as u32; - let mut negative = false; - if topx.wrapping_sub(1) >= 0x7fe || (topy & 0x7ff).wrapping_sub(0x3be) >= 0x80 { - if y == 0.0 || !y.is_finite() { - if y == 0.0 { - return 1.0; - } - if x == 1.0 { - return 1.0; - } - if x.is_nan() || y.is_nan() { - return x + y; - } - if x.abs() == 1.0 { - return 1.0; - } - if (x.abs() < 1.0) != y.is_sign_negative() { - return 0.0; - } - return y * y; - } - if x == 0.0 || !x.is_finite() { - let mut x2 = x * x; - if x.is_sign_negative() && integer_kind(iy) == 1 { - x2 = -x2; - } - return if y.is_sign_negative() { 1.0 / x2 } else { x2 }; - } - if x.is_sign_negative() { - let kind = integer_kind(iy); - if kind == 0 { - return NEG_NAN; - } - negative = kind == 1; - ix &= !SIGN; - topx &= 0x7ff; - } - if (topy & 0x7ff).wrapping_sub(0x3be) >= 0x80 { - if ix == 1.0f64.to_bits() { - return 1.0; - } - if topy & 0x7ff < 0x3be { - return if ix > 1.0f64.to_bits() { 1.0 + y } else { 1.0 - y }; - } - return if (ix > 1.0f64.to_bits()) == (topy < 0x800) { - f64::INFINITY - } else { - 0.0 - }; - } - if topx == 0 { - ix = ((x * 4503599627370496.0).to_bits() & !SIGN).wrapping_sub(52u64 << 52); - } - } - let (hi, lo) = pow_log(ix); - let yhi = f64::from_bits(iy & (u64::MAX << 27)); - let ylo = y - yhi; - let lhi = f64::from_bits(hi.to_bits() & (u64::MAX << 27)); - let llo = hi - lhi + lo; - let ehi = yhi * lhi; - let elo = ylo * lhi + y * llo; - exp_inner(ehi, elo, negative) -} -pub fn log(x: f64) -> f64 { - let mut ix = x.to_bits(); - if ix.wrapping_sub(0.9375f64.to_bits()) < (1.064697265625f64.to_bits() - 0.9375f64.to_bits()) { - if x == 1.0 { - return 0.0; - } - let r = x - 1.0; - let r2 = r * r; - let r3 = r * r2; - let b = LOG_NEAR_POLY; - let mut y = r3 - * (b[1] - + r * b[2] - + r2 * b[3] - + r3 * (b[4] + r * b[5] + r2 * b[6] + r3 * (b[7] + r * b[8] + r2 * b[9] + r3 * b[10]))); - let w = r * 134217728.0; - let rhi = r + w - w; - let rlo = r - rhi; - let w = rhi * rhi * b[0]; - let hi = r + w; - let mut lo = r - hi + w; - lo += b[0] * rlo * (rhi + r); - y += lo; - y += hi; - return y; - } - let top = (ix >> 48) as u32; - if top.wrapping_sub(0x10) >= 0x7fe0 { - if x == 0.0 { - return f64::NEG_INFINITY; - } - if ix == INF || x.is_nan() { - return x; - } - if x.is_sign_negative() { - return NEG_NAN; - } - ix = (x * 4503599627370496.0).to_bits().wrapping_sub(52u64 << 52); - } - let tmp = ix.wrapping_sub(0x3fe6000000000000); - let i = ((tmp >> 45) % 128) as usize; - let kd = ((tmp as i64) >> 52) as f64; - let iz = ix.wrapping_sub(tmp & (0xfffu64 << 52)); - let [invc, logc] = LOG_TAB[i]; - let z = f64::from_bits(iz); - let r = (z - LOG_TAB2[i][0] - LOG_TAB2[i][1]) * invc; - let w = kd * LN2_HI + logc; - let hi = w + r; - let lo = w - hi + r + kd * LN2_LO; - let r2 = r * r; - let a = LOG_POLY; - lo + r2 * a[0] + r * r2 * (a[1] + r * a[2] + r2 * (a[3] + r * a[4])) + hi -} diff --git a/github-simulator/source/src/math/double_math_tables.rs b/github-simulator/source/src/math/double_math_tables.rs deleted file mode 100644 index 81dff44bd7966db34e7b2afe028efe0c0b83b539..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/math/double_math_tables.rs +++ /dev/null @@ -1,1202 +0,0 @@ -// Generated by scripts/double_math_tables.py from Proton 11 musl. -// Copyright (c) 2018, Arm Limited. SPDX-License-Identifier: MIT -pub(crate) const INV_LN2_N: f64 = f64::from_bits(0x40671547652b82fe); -pub(crate) const NEG_LN2_HI_N: f64 = f64::from_bits(0xbf762e42fefa0000); -pub(crate) const NEG_LN2_LO_N: f64 = f64::from_bits(0xbd0cf79abc9e3b3a); -pub(crate) const EXP_POLY: [f64; 4] = [ - f64::from_bits(0x3fdffffffffffdbd), - f64::from_bits(0x3fc555555555543c), - f64::from_bits(0x3fa55555cf172b91), - f64::from_bits(0x3f81111167a4d017), -]; -pub(crate) const EXP_TAB: [u64; 256] = [ - 0x0, - 0x3ff0000000000000, - 0x3c9b3b4f1a88bf6e, - 0x3feff63da9fb3335, - 0xbc7160139cd8dc5d, - 0x3fefec9a3e778061, - 0xbc905e7a108766d1, - 0x3fefe315e86e7f85, - 0x3c8cd2523567f613, - 0x3fefd9b0d3158574, - 0xbc8bce8023f98efa, - 0x3fefd06b29ddf6de, - 0x3c60f74e61e6c861, - 0x3fefc74518759bc8, - 0x3c90a3e45b33d399, - 0x3fefbe3ecac6f383, - 0x3c979aa65d837b6d, - 0x3fefb5586cf9890f, - 0x3c8eb51a92fdeffc, - 0x3fefac922b7247f7, - 0x3c3ebe3d702f9cd1, - 0x3fefa3ec32d3d1a2, - 0xbc6a033489906e0b, - 0x3fef9b66affed31b, - 0xbc9556522a2fbd0e, - 0x3fef9301d0125b51, - 0xbc5080ef8c4eea55, - 0x3fef8abdc06c31cc, - 0xbc91c923b9d5f416, - 0x3fef829aaea92de0, - 0x3c80d3e3e95c55af, - 0x3fef7a98c8a58e51, - 0xbc801b15eaa59348, - 0x3fef72b83c7d517b, - 0xbc8f1ff055de323d, - 0x3fef6af9388c8dea, - 0x3c8b898c3f1353bf, - 0x3fef635beb6fcb75, - 0xbc96d99c7611eb26, - 0x3fef5be084045cd4, - 0x3c9aecf73e3a2f60, - 0x3fef54873168b9aa, - 0xbc8fe782cb86389d, - 0x3fef4d5022fcd91d, - 0x3c8a6f4144a6c38d, - 0x3fef463b88628cd6, - 0x3c807a05b0e4047d, - 0x3fef3f49917ddc96, - 0x3c968efde3a8a894, - 0x3fef387a6e756238, - 0x3c875e18f274487d, - 0x3fef31ce4fb2a63f, - 0x3c80472b981fe7f2, - 0x3fef2b4565e27cdd, - 0xbc96b87b3f71085e, - 0x3fef24dfe1f56381, - 0x3c82f7e16d09ab31, - 0x3fef1e9df51fdee1, - 0xbc3d219b1a6fbffa, - 0x3fef187fd0dad990, - 0x3c8b3782720c0ab4, - 0x3fef1285a6e4030b, - 0x3c6e149289cecb8f, - 0x3fef0cafa93e2f56, - 0x3c834d754db0abb6, - 0x3fef06fe0a31b715, - 0x3c864201e2ac744c, - 0x3fef0170fc4cd831, - 0x3c8fdd395dd3f84a, - 0x3feefc08b26416ff, - 0xbc86a3803b8e5b04, - 0x3feef6c55f929ff1, - 0xbc924aedcc4b5068, - 0x3feef1a7373aa9cb, - 0xbc9907f81b512d8e, - 0x3feeecae6d05d866, - 0xbc71d1e83e9436d2, - 0x3feee7db34e59ff7, - 0xbc991919b3ce1b15, - 0x3feee32dc313a8e5, - 0x3c859f48a72a4c6d, - 0x3feedea64c123422, - 0xbc9312607a28698a, - 0x3feeda4504ac801c, - 0xbc58a78f4817895b, - 0x3feed60a21f72e2a, - 0xbc7c2c9b67499a1b, - 0x3feed1f5d950a897, - 0x3c4363ed60c2ac11, - 0x3feece086061892d, - 0x3c9666093b0664ef, - 0x3feeca41ed1d0057, - 0x3c6ecce1daa10379, - 0x3feec6a2b5c13cd0, - 0x3c93ff8e3f0f1230, - 0x3feec32af0d7d3de, - 0x3c7690cebb7aafb0, - 0x3feebfdad5362a27, - 0x3c931dbdeb54e077, - 0x3feebcb299fddd0d, - 0xbc8f94340071a38e, - 0x3feeb9b2769d2ca7, - 0xbc87deccdc93a349, - 0x3feeb6daa2cf6642, - 0xbc78dec6bd0f385f, - 0x3feeb42b569d4f82, - 0xbc861246ec7b5cf6, - 0x3feeb1a4ca5d920f, - 0x3c93350518fdd78e, - 0x3feeaf4736b527da, - 0x3c7b98b72f8a9b05, - 0x3feead12d497c7fd, - 0x3c9063e1e21c5409, - 0x3feeab07dd485429, - 0x3c34c7855019c6ea, - 0x3feea9268a5946b7, - 0x3c9432e62b64c035, - 0x3feea76f15ad2148, - 0xbc8ce44a6199769f, - 0x3feea5e1b976dc09, - 0xbc8c33c53bef4da8, - 0x3feea47eb03a5585, - 0xbc845378892be9ae, - 0x3feea34634ccc320, - 0xbc93cedd78565858, - 0x3feea23882552225, - 0x3c5710aa807e1964, - 0x3feea155d44ca973, - 0xbc93b3efbf5e2228, - 0x3feea09e667f3bcd, - 0xbc6a12ad8734b982, - 0x3feea012750bdabf, - 0xbc6367efb86da9ee, - 0x3fee9fb23c651a2f, - 0xbc80dc3d54e08851, - 0x3fee9f7df9519484, - 0xbc781f647e5a3ecf, - 0x3fee9f75e8ec5f74, - 0xbc86ee4ac08b7db0, - 0x3fee9f9a48a58174, - 0xbc8619321e55e68a, - 0x3fee9feb564267c9, - 0x3c909ccb5e09d4d3, - 0x3feea0694fde5d3f, - 0xbc7b32dcb94da51d, - 0x3feea11473eb0187, - 0x3c94ecfd5467c06b, - 0x3feea1ed0130c132, - 0x3c65ebe1abd66c55, - 0x3feea2f336cf4e62, - 0xbc88a1c52fb3cf42, - 0x3feea427543e1a12, - 0xbc9369b6f13b3734, - 0x3feea589994cce13, - 0xbc805e843a19ff1e, - 0x3feea71a4623c7ad, - 0xbc94d450d872576e, - 0x3feea8d99b4492ed, - 0x3c90ad675b0e8a00, - 0x3feeaac7d98a6699, - 0x3c8db72fc1f0eab4, - 0x3feeace5422aa0db, - 0xbc65b6609cc5e7ff, - 0x3feeaf3216b5448c, - 0x3c7bf68359f35f44, - 0x3feeb1ae99157736, - 0xbc93091fa71e3d83, - 0x3feeb45b0b91ffc6, - 0xbc5da9b88b6c1e29, - 0x3feeb737b0cdc5e5, - 0xbc6c23f97c90b959, - 0x3feeba44cbc8520f, - 0xbc92434322f4f9aa, - 0x3feebd829fde4e50, - 0xbc85ca6cd7668e4b, - 0x3feec0f170ca07ba, - 0x3c71affc2b91ce27, - 0x3feec49182a3f090, - 0x3c6dd235e10a73bb, - 0x3feec86319e32323, - 0xbc87c50422622263, - 0x3feecc667b5de565, - 0x3c8b1c86e3e231d5, - 0x3feed09bec4a2d33, - 0xbc91bbd1d3bcbb15, - 0x3feed503b23e255d, - 0x3c90cc319cee31d2, - 0x3feed99e1330b358, - 0x3c8469846e735ab3, - 0x3feede6b5579fdbf, - 0xbc82dfcd978e9db4, - 0x3feee36bbfd3f37a, - 0x3c8c1a7792cb3387, - 0x3feee89f995ad3ad, - 0xbc907b8f4ad1d9fa, - 0x3feeee07298db666, - 0xbc55c3d956dcaeba, - 0x3feef3a2b84f15fb, - 0xbc90a40e3da6f640, - 0x3feef9728de5593a, - 0xbc68d6f438ad9334, - 0x3feeff76f2fb5e47, - 0xbc91eee26b588a35, - 0x3fef05b030a1064a, - 0x3c74ffd70a5fddcd, - 0x3fef0c1e904bc1d2, - 0xbc91bdfbfa9298ac, - 0x3fef12c25bd71e09, - 0x3c736eae30af0cb3, - 0x3fef199bdd85529c, - 0x3c8ee3325c9ffd94, - 0x3fef20ab5fffd07a, - 0x3c84e08fd10959ac, - 0x3fef27f12e57d14b, - 0x3c63cdaf384e1a67, - 0x3fef2f6d9406e7b5, - 0x3c676b2c6c921968, - 0x3fef3720dcef9069, - 0xbc808a1883ccb5d2, - 0x3fef3f0b555dc3fa, - 0xbc8fad5d3ffffa6f, - 0x3fef472d4a07897c, - 0xbc900dae3875a949, - 0x3fef4f87080d89f2, - 0x3c74a385a63d07a7, - 0x3fef5818dcfba487, - 0xbc82919e2040220f, - 0x3fef60e316c98398, - 0x3c8e5a50d5c192ac, - 0x3fef69e603db3285, - 0x3c843a59ac016b4b, - 0x3fef7321f301b460, - 0xbc82d52107b43e1f, - 0x3fef7c97337b9b5f, - 0xbc892ab93b470dc9, - 0x3fef864614f5a129, - 0x3c74b604603a88d3, - 0x3fef902ee78b3ff6, - 0x3c83c5ec519d7271, - 0x3fef9a51fbc74c83, - 0xbc8ff7128fd391f0, - 0x3fefa4afa2a490da, - 0xbc8dae98e223747d, - 0x3fefaf482d8e67f1, - 0x3c8ec3bc41aa2008, - 0x3fefba1bee615a27, - 0x3c842b94c3a9eb32, - 0x3fefc52b376bba97, - 0x3c8a64a931d185ee, - 0x3fefd0765b6e4540, - 0xbc8e37bae43be3ed, - 0x3fefdbfdad9cbe14, - 0x3c77893b4d91cd9d, - 0x3fefe7c1819e90d8, - 0x3c5305c14160cc89, - 0x3feff3c22b8f71f1, -]; -pub(crate) const LN2_HI: f64 = f64::from_bits(0x3fe62e42fefa3800); -pub(crate) const LN2_LO: f64 = f64::from_bits(0x3d2ef35793c76730); -pub(crate) const POW_POLY: [f64; 7] = [ - f64::from_bits(0xbfe0000000000000), - f64::from_bits(0xbfe5555555555560), - f64::from_bits(0x3fe0000000000006), - f64::from_bits(0x3fe999999959554e), - f64::from_bits(0xbfe555555529a47a), - f64::from_bits(0xbff2495b9b4845e9), - f64::from_bits(0x3ff0002b8b263fc3), -]; -pub(crate) const POW_TAB: [[f64; 3]; 128] = [ - [ - f64::from_bits(0x3ff6a00000000000), - f64::from_bits(0xbfd62c82f2b9c800), - f64::from_bits(0x3cfab42428375680), - ], - [ - f64::from_bits(0x3ff6800000000000), - f64::from_bits(0xbfd5d1bdbf580800), - f64::from_bits(0xbd1ca508d8e0f720), - ], - [ - f64::from_bits(0x3ff6600000000000), - f64::from_bits(0xbfd5767717455800), - f64::from_bits(0xbd2362a4d5b6506d), - ], - [ - f64::from_bits(0x3ff6400000000000), - f64::from_bits(0xbfd51aad872df800), - f64::from_bits(0xbce684e49eb067d5), - ], - [ - f64::from_bits(0x3ff6200000000000), - f64::from_bits(0xbfd4be5f95777800), - f64::from_bits(0xbd041b6993293ee0), - ], - [ - f64::from_bits(0x3ff6000000000000), - f64::from_bits(0xbfd4618bc21c6000), - f64::from_bits(0x3d13d82f484c84cc), - ], - [ - f64::from_bits(0x3ff5e00000000000), - f64::from_bits(0xbfd404308686a800), - f64::from_bits(0x3cdc42f3ed820b3a), - ], - [ - f64::from_bits(0x3ff5c00000000000), - f64::from_bits(0xbfd3a64c55694800), - f64::from_bits(0x3d20b1c686519460), - ], - [ - f64::from_bits(0x3ff5a00000000000), - f64::from_bits(0xbfd347dd9a988000), - f64::from_bits(0x3d25594dd4c58092), - ], - [ - f64::from_bits(0x3ff5800000000000), - f64::from_bits(0xbfd2e8e2bae12000), - f64::from_bits(0x3d267b1e99b72bd8), - ], - [ - f64::from_bits(0x3ff5600000000000), - f64::from_bits(0xbfd2895a13de8800), - f64::from_bits(0x3d15ca14b6cfb03f), - ], - [ - f64::from_bits(0x3ff5600000000000), - f64::from_bits(0xbfd2895a13de8800), - f64::from_bits(0x3d15ca14b6cfb03f), - ], - [ - f64::from_bits(0x3ff5400000000000), - f64::from_bits(0xbfd22941fbcf7800), - f64::from_bits(0xbd165a242853da76), - ], - [ - f64::from_bits(0x3ff5200000000000), - f64::from_bits(0xbfd1c898c1699800), - f64::from_bits(0xbd1fafbc68e75404), - ], - [ - f64::from_bits(0x3ff5000000000000), - f64::from_bits(0xbfd1675cababa800), - f64::from_bits(0x3d1f1fc63382a8f0), - ], - [ - f64::from_bits(0x3ff4e00000000000), - f64::from_bits(0xbfd1058bf9ae4800), - f64::from_bits(0xbd26a8c4fd055a66), - ], - [ - f64::from_bits(0x3ff4c00000000000), - f64::from_bits(0xbfd0a324e2739000), - f64::from_bits(0xbd0c6bee7ef4030e), - ], - [ - f64::from_bits(0x3ff4a00000000000), - f64::from_bits(0xbfd0402594b4d000), - f64::from_bits(0xbcf036b89ef42d7f), - ], - [ - f64::from_bits(0x3ff4a00000000000), - f64::from_bits(0xbfd0402594b4d000), - f64::from_bits(0xbcf036b89ef42d7f), - ], - [ - f64::from_bits(0x3ff4800000000000), - f64::from_bits(0xbfcfb9186d5e4000), - f64::from_bits(0x3d0d572aab993c87), - ], - [ - f64::from_bits(0x3ff4600000000000), - f64::from_bits(0xbfcef0adcbdc6000), - f64::from_bits(0x3d2b26b79c86af24), - ], - [ - f64::from_bits(0x3ff4400000000000), - f64::from_bits(0xbfce27076e2af000), - f64::from_bits(0xbd172f4f543fff10), - ], - [ - f64::from_bits(0x3ff4200000000000), - f64::from_bits(0xbfcd5c216b4fc000), - f64::from_bits(0x3d21ba91bbca681b), - ], - [ - f64::from_bits(0x3ff4000000000000), - f64::from_bits(0xbfcc8ff7c79aa000), - f64::from_bits(0x3d27794f689f8434), - ], - [ - f64::from_bits(0x3ff4000000000000), - f64::from_bits(0xbfcc8ff7c79aa000), - f64::from_bits(0x3d27794f689f8434), - ], - [ - f64::from_bits(0x3ff3e00000000000), - f64::from_bits(0xbfcbc286742d9000), - f64::from_bits(0x3d194eb0318bb78f), - ], - [ - f64::from_bits(0x3ff3c00000000000), - f64::from_bits(0xbfcaf3c94e80c000), - f64::from_bits(0x3cba4e633fcd9066), - ], - [ - f64::from_bits(0x3ff3a00000000000), - f64::from_bits(0xbfca23bc1fe2b000), - f64::from_bits(0xbd258c64dc46c1ea), - ], - [ - f64::from_bits(0x3ff3a00000000000), - f64::from_bits(0xbfca23bc1fe2b000), - f64::from_bits(0xbd258c64dc46c1ea), - ], - [ - f64::from_bits(0x3ff3800000000000), - f64::from_bits(0xbfc9525a9cf45000), - f64::from_bits(0xbd2ad1d904c1d4e3), - ], - [ - f64::from_bits(0x3ff3600000000000), - f64::from_bits(0xbfc87fa06520d000), - f64::from_bits(0x3d2bbdbf7fdbfa09), - ], - [ - f64::from_bits(0x3ff3400000000000), - f64::from_bits(0xbfc7ab890210e000), - f64::from_bits(0x3d2bdb9072534a58), - ], - [ - f64::from_bits(0x3ff3400000000000), - f64::from_bits(0xbfc7ab890210e000), - f64::from_bits(0x3d2bdb9072534a58), - ], - [ - f64::from_bits(0x3ff3200000000000), - f64::from_bits(0xbfc6d60fe719d000), - f64::from_bits(0xbd10e46aa3b2e266), - ], - [ - f64::from_bits(0x3ff3000000000000), - f64::from_bits(0xbfc5ff3070a79000), - f64::from_bits(0xbd1e9e439f105039), - ], - [ - f64::from_bits(0x3ff3000000000000), - f64::from_bits(0xbfc5ff3070a79000), - f64::from_bits(0xbd1e9e439f105039), - ], - [ - f64::from_bits(0x3ff2e00000000000), - f64::from_bits(0xbfc526e5e3a1b000), - f64::from_bits(0xbd20de8b90075b8f), - ], - [ - f64::from_bits(0x3ff2c00000000000), - f64::from_bits(0xbfc44d2b6ccb8000), - f64::from_bits(0x3d170cc16135783c), - ], - [ - f64::from_bits(0x3ff2c00000000000), - f64::from_bits(0xbfc44d2b6ccb8000), - f64::from_bits(0x3d170cc16135783c), - ], - [ - f64::from_bits(0x3ff2a00000000000), - f64::from_bits(0xbfc371fc201e9000), - f64::from_bits(0x3cf178864d27543a), - ], - [ - f64::from_bits(0x3ff2800000000000), - f64::from_bits(0xbfc29552f81ff000), - f64::from_bits(0xbd248d301771c408), - ], - [ - f64::from_bits(0x3ff2600000000000), - f64::from_bits(0xbfc1b72ad52f6000), - f64::from_bits(0xbd2e80a41811a396), - ], - [ - f64::from_bits(0x3ff2600000000000), - f64::from_bits(0xbfc1b72ad52f6000), - f64::from_bits(0xbd2e80a41811a396), - ], - [ - f64::from_bits(0x3ff2400000000000), - f64::from_bits(0xbfc0d77e7cd09000), - f64::from_bits(0x3d0a699688e85bf4), - ], - [ - f64::from_bits(0x3ff2400000000000), - f64::from_bits(0xbfc0d77e7cd09000), - f64::from_bits(0x3d0a699688e85bf4), - ], - [ - f64::from_bits(0x3ff2200000000000), - f64::from_bits(0xbfbfec9131dbe000), - f64::from_bits(0xbd2575545ca333f2), - ], - [ - f64::from_bits(0x3ff2000000000000), - f64::from_bits(0xbfbe27076e2b0000), - f64::from_bits(0x3d2a342c2af0003c), - ], - [ - f64::from_bits(0x3ff2000000000000), - f64::from_bits(0xbfbe27076e2b0000), - f64::from_bits(0x3d2a342c2af0003c), - ], - [ - f64::from_bits(0x3ff1e00000000000), - f64::from_bits(0xbfbc5e548f5bc000), - f64::from_bits(0xbd1d0c57585fbe06), - ], - [ - f64::from_bits(0x3ff1c00000000000), - f64::from_bits(0xbfba926d3a4ae000), - f64::from_bits(0x3d253935e85baac8), - ], - [ - f64::from_bits(0x3ff1c00000000000), - f64::from_bits(0xbfba926d3a4ae000), - f64::from_bits(0x3d253935e85baac8), - ], - [ - f64::from_bits(0x3ff1a00000000000), - f64::from_bits(0xbfb8c345d631a000), - f64::from_bits(0x3d137c294d2f5668), - ], - [ - f64::from_bits(0x3ff1a00000000000), - f64::from_bits(0xbfb8c345d631a000), - f64::from_bits(0x3d137c294d2f5668), - ], - [ - f64::from_bits(0x3ff1800000000000), - f64::from_bits(0xbfb6f0d28ae56000), - f64::from_bits(0xbd269737c93373da), - ], - [ - f64::from_bits(0x3ff1600000000000), - f64::from_bits(0xbfb51b073f062000), - f64::from_bits(0x3d1f025b61c65e57), - ], - [ - f64::from_bits(0x3ff1600000000000), - f64::from_bits(0xbfb51b073f062000), - f64::from_bits(0x3d1f025b61c65e57), - ], - [ - f64::from_bits(0x3ff1400000000000), - f64::from_bits(0xbfb341d7961be000), - f64::from_bits(0x3d2c5edaccf913df), - ], - [ - f64::from_bits(0x3ff1400000000000), - f64::from_bits(0xbfb341d7961be000), - f64::from_bits(0x3d2c5edaccf913df), - ], - [ - f64::from_bits(0x3ff1200000000000), - f64::from_bits(0xbfb16536eea38000), - f64::from_bits(0x3d147c5e768fa309), - ], - [ - f64::from_bits(0x3ff1000000000000), - f64::from_bits(0xbfaf0a30c0118000), - f64::from_bits(0x3d2d599e83368e91), - ], - [ - f64::from_bits(0x3ff1000000000000), - f64::from_bits(0xbfaf0a30c0118000), - f64::from_bits(0x3d2d599e83368e91), - ], - [ - f64::from_bits(0x3ff0e00000000000), - f64::from_bits(0xbfab42dd71198000), - f64::from_bits(0x3d1c827ae5d6704c), - ], - [ - f64::from_bits(0x3ff0e00000000000), - f64::from_bits(0xbfab42dd71198000), - f64::from_bits(0x3d1c827ae5d6704c), - ], - [ - f64::from_bits(0x3ff0c00000000000), - f64::from_bits(0xbfa77458f632c000), - f64::from_bits(0xbd2cfc4634f2a1ee), - ], - [ - f64::from_bits(0x3ff0c00000000000), - f64::from_bits(0xbfa77458f632c000), - f64::from_bits(0xbd2cfc4634f2a1ee), - ], - [ - f64::from_bits(0x3ff0a00000000000), - f64::from_bits(0xbfa39e87b9fec000), - f64::from_bits(0x3cf502b7f526feaa), - ], - [ - f64::from_bits(0x3ff0a00000000000), - f64::from_bits(0xbfa39e87b9fec000), - f64::from_bits(0x3cf502b7f526feaa), - ], - [ - f64::from_bits(0x3ff0800000000000), - f64::from_bits(0xbf9f829b0e780000), - f64::from_bits(0xbd2980267c7e09e4), - ], - [ - f64::from_bits(0x3ff0800000000000), - f64::from_bits(0xbf9f829b0e780000), - f64::from_bits(0xbd2980267c7e09e4), - ], - [ - f64::from_bits(0x3ff0600000000000), - f64::from_bits(0xbf97b91b07d58000), - f64::from_bits(0xbd288d5493faa639), - ], - [ - f64::from_bits(0x3ff0400000000000), - f64::from_bits(0xbf8fc0a8b0fc0000), - f64::from_bits(0xbcdf1e7cf6d3a69c), - ], - [ - f64::from_bits(0x3ff0400000000000), - f64::from_bits(0xbf8fc0a8b0fc0000), - f64::from_bits(0xbcdf1e7cf6d3a69c), - ], - [ - f64::from_bits(0x3ff0200000000000), - f64::from_bits(0xbf7fe02a6b100000), - f64::from_bits(0xbd19e23f0dda40e4), - ], - [ - f64::from_bits(0x3ff0200000000000), - f64::from_bits(0xbf7fe02a6b100000), - f64::from_bits(0xbd19e23f0dda40e4), - ], - [ - f64::from_bits(0x3ff0000000000000), - f64::from_bits(0x0000000000000000), - f64::from_bits(0x0000000000000000), - ], - [ - f64::from_bits(0x3ff0000000000000), - f64::from_bits(0x0000000000000000), - f64::from_bits(0x0000000000000000), - ], - [ - f64::from_bits(0x3fefc00000000000), - f64::from_bits(0x3f80101575890000), - f64::from_bits(0xbd10c76b999d2be8), - ], - [ - f64::from_bits(0x3fef800000000000), - f64::from_bits(0x3f90205658938000), - f64::from_bits(0xbd23dc5b06e2f7d2), - ], - [ - f64::from_bits(0x3fef400000000000), - f64::from_bits(0x3f98492528c90000), - f64::from_bits(0xbd2aa0ba325a0c34), - ], - [ - f64::from_bits(0x3fef000000000000), - f64::from_bits(0x3fa0415d89e74000), - f64::from_bits(0x3d0111c05cf1d753), - ], - [ - f64::from_bits(0x3feec00000000000), - f64::from_bits(0x3fa466aed42e0000), - f64::from_bits(0xbd2c167375bdfd28), - ], - [ - f64::from_bits(0x3fee800000000000), - f64::from_bits(0x3fa894aa149fc000), - f64::from_bits(0xbd197995d05a267d), - ], - [ - f64::from_bits(0x3fee400000000000), - f64::from_bits(0x3faccb73cdddc000), - f64::from_bits(0xbd1a68f247d82807), - ], - [ - f64::from_bits(0x3fee200000000000), - f64::from_bits(0x3faeea31c006c000), - f64::from_bits(0xbd0e113e4fc93b7b), - ], - [ - f64::from_bits(0x3fede00000000000), - f64::from_bits(0x3fb1973bd1466000), - f64::from_bits(0xbd25325d560d9e9b), - ], - [ - f64::from_bits(0x3feda00000000000), - f64::from_bits(0x3fb3bdf5a7d1e000), - f64::from_bits(0x3d2cc85ea5db4ed7), - ], - [ - f64::from_bits(0x3fed600000000000), - f64::from_bits(0x3fb5e95a4d97a000), - f64::from_bits(0xbd2c69063c5d1d1e), - ], - [ - f64::from_bits(0x3fed400000000000), - f64::from_bits(0x3fb700d30aeac000), - f64::from_bits(0x3cec1e8da99ded32), - ], - [ - f64::from_bits(0x3fed000000000000), - f64::from_bits(0x3fb9335e5d594000), - f64::from_bits(0x3d23115c3abd47da), - ], - [ - f64::from_bits(0x3fecc00000000000), - f64::from_bits(0x3fbb6ac88dad6000), - f64::from_bits(0xbd1390802bf768e5), - ], - [ - f64::from_bits(0x3feca00000000000), - f64::from_bits(0x3fbc885801bc4000), - f64::from_bits(0x3d2646d1c65aacd3), - ], - [ - f64::from_bits(0x3fec600000000000), - f64::from_bits(0x3fbec739830a2000), - f64::from_bits(0xbd2dc068afe645e0), - ], - [ - f64::from_bits(0x3fec400000000000), - f64::from_bits(0x3fbfe89139dbe000), - f64::from_bits(0xbd2534d64fa10afd), - ], - [ - f64::from_bits(0x3fec000000000000), - f64::from_bits(0x3fc1178e8227e000), - f64::from_bits(0x3d21ef78ce2d07f2), - ], - [ - f64::from_bits(0x3febe00000000000), - f64::from_bits(0x3fc1aa2b7e23f000), - f64::from_bits(0x3d2ca78e44389934), - ], - [ - f64::from_bits(0x3feba00000000000), - f64::from_bits(0x3fc2d1610c868000), - f64::from_bits(0x3d039d6ccb81b4a1), - ], - [ - f64::from_bits(0x3feb800000000000), - f64::from_bits(0x3fc365fcb0159000), - f64::from_bits(0x3cc62fa8234b7289), - ], - [ - f64::from_bits(0x3feb400000000000), - f64::from_bits(0x3fc4913d8333b000), - f64::from_bits(0x3d25837954fdb678), - ], - [ - f64::from_bits(0x3feb200000000000), - f64::from_bits(0x3fc527e5e4a1b000), - f64::from_bits(0x3d2633e8e5697dc7), - ], - [ - f64::from_bits(0x3feae00000000000), - f64::from_bits(0x3fc6574ebe8c1000), - f64::from_bits(0x3d19cf8b2c3c2e78), - ], - [ - f64::from_bits(0x3feac00000000000), - f64::from_bits(0x3fc6f0128b757000), - f64::from_bits(0xbd25118de59c21e1), - ], - [ - f64::from_bits(0x3feaa00000000000), - f64::from_bits(0x3fc7898d85445000), - f64::from_bits(0xbd1c661070914305), - ], - [ - f64::from_bits(0x3fea600000000000), - f64::from_bits(0x3fc8beafeb390000), - f64::from_bits(0xbd073d54aae92cd1), - ], - [ - f64::from_bits(0x3fea400000000000), - f64::from_bits(0x3fc95a5adcf70000), - f64::from_bits(0x3d07f22858a0ff6f), - ], - [ - f64::from_bits(0x3fea000000000000), - f64::from_bits(0x3fca93ed3c8ae000), - f64::from_bits(0xbd28724350562169), - ], - [ - f64::from_bits(0x3fe9e00000000000), - f64::from_bits(0x3fcb31d8575bd000), - f64::from_bits(0xbd0c358d4eace1aa), - ], - [ - f64::from_bits(0x3fe9c00000000000), - f64::from_bits(0x3fcbd087383be000), - f64::from_bits(0xbd2d4bc4595412b6), - ], - [ - f64::from_bits(0x3fe9a00000000000), - f64::from_bits(0x3fcc6ffbc6f01000), - f64::from_bits(0xbcf1ec72c5962bd2), - ], - [ - f64::from_bits(0x3fe9600000000000), - f64::from_bits(0x3fcdb13db0d49000), - f64::from_bits(0xbd2aff2af715b035), - ], - [ - f64::from_bits(0x3fe9400000000000), - f64::from_bits(0x3fce530effe71000), - f64::from_bits(0x3cc212276041f430), - ], - [ - f64::from_bits(0x3fe9200000000000), - f64::from_bits(0x3fcef5ade4dd0000), - f64::from_bits(0xbcca211565bb8e11), - ], - [ - f64::from_bits(0x3fe9000000000000), - f64::from_bits(0x3fcf991c6cb3b000), - f64::from_bits(0x3d1bcbecca0cdf30), - ], - [ - f64::from_bits(0x3fe8c00000000000), - f64::from_bits(0x3fd07138604d5800), - f64::from_bits(0x3cf89cdb16ed4e91), - ], - [ - f64::from_bits(0x3fe8a00000000000), - f64::from_bits(0x3fd0c42d67616000), - f64::from_bits(0x3d27188b163ceae9), - ], - [ - f64::from_bits(0x3fe8800000000000), - f64::from_bits(0x3fd1178e8227e800), - f64::from_bits(0xbd2c210e63a5f01c), - ], - [ - f64::from_bits(0x3fe8600000000000), - f64::from_bits(0x3fd16b5ccbacf800), - f64::from_bits(0x3d2b9acdf7a51681), - ], - [ - f64::from_bits(0x3fe8400000000000), - f64::from_bits(0x3fd1bf99635a6800), - f64::from_bits(0x3d2ca6ed5147bdb7), - ], - [ - f64::from_bits(0x3fe8200000000000), - f64::from_bits(0x3fd214456d0eb800), - f64::from_bits(0x3d0a87deba46baea), - ], - [ - f64::from_bits(0x3fe7e00000000000), - f64::from_bits(0x3fd2bef07cdc9000), - f64::from_bits(0x3d2a9cfa4a5004f4), - ], - [ - f64::from_bits(0x3fe7c00000000000), - f64::from_bits(0x3fd314f1e1d36000), - f64::from_bits(0xbd28e27ad3213cb8), - ], - [ - f64::from_bits(0x3fe7a00000000000), - f64::from_bits(0x3fd36b6776be1000), - f64::from_bits(0x3d116ecdb0f177c8), - ], - [ - f64::from_bits(0x3fe7800000000000), - f64::from_bits(0x3fd3c25277333000), - f64::from_bits(0x3d183b54b606bd5c), - ], - [ - f64::from_bits(0x3fe7600000000000), - f64::from_bits(0x3fd419b423d5e800), - f64::from_bits(0x3d08e436ec90e09d), - ], - [ - f64::from_bits(0x3fe7400000000000), - f64::from_bits(0x3fd4718dc271c800), - f64::from_bits(0xbd2f27ce0967d675), - ], - [ - f64::from_bits(0x3fe7200000000000), - f64::from_bits(0x3fd4c9e09e173000), - f64::from_bits(0xbd2e20891b0ad8a4), - ], - [ - f64::from_bits(0x3fe7000000000000), - f64::from_bits(0x3fd522ae0738a000), - f64::from_bits(0x3d2ebe708164c759), - ], - [ - f64::from_bits(0x3fe6e00000000000), - f64::from_bits(0x3fd57bf753c8d000), - f64::from_bits(0x3d1fadedee5d40ef), - ], - [ - f64::from_bits(0x3fe6c00000000000), - f64::from_bits(0x3fd5d5bddf596000), - f64::from_bits(0xbd0a0b2a08a465dc), - ], -]; -pub(crate) const LOG_POLY: [f64; 5] = [ - f64::from_bits(0xbfe0000000000001), - f64::from_bits(0x3fd555555551305b), - f64::from_bits(0xbfcfffffffeb4590), - f64::from_bits(0x3fc999b324f10111), - f64::from_bits(0xbfc55575e506c89f), -]; -pub(crate) const LOG_NEAR_POLY: [f64; 11] = [ - f64::from_bits(0xbfe0000000000000), - f64::from_bits(0x3fd5555555555577), - f64::from_bits(0xbfcffffffffffdcb), - f64::from_bits(0x3fc999999995dd0c), - f64::from_bits(0xbfc55555556745a7), - f64::from_bits(0x3fc24924a344de30), - f64::from_bits(0xbfbfffffa4423d65), - f64::from_bits(0x3fbc7184282ad6ca), - f64::from_bits(0xbfb999eb43b068ff), - f64::from_bits(0x3fb78182f7afd085), - f64::from_bits(0xbfb5521375d145cd), -]; -pub(crate) const LOG_TAB: [[f64; 2]; 128] = [ - [f64::from_bits(0x3ff734f0c3e0de9f), f64::from_bits(0xbfd7cc7f79e69000)], - [f64::from_bits(0x3ff713786a2ce91f), f64::from_bits(0xbfd76feec20d0000)], - [f64::from_bits(0x3ff6f26008fab5a0), f64::from_bits(0xbfd713e31351e000)], - [f64::from_bits(0x3ff6d1a61f138c7d), f64::from_bits(0xbfd6b85b38287800)], - [f64::from_bits(0x3ff6b1490bc5b4d1), f64::from_bits(0xbfd65d5590807800)], - [f64::from_bits(0x3ff69147332f0cba), f64::from_bits(0xbfd602d076180000)], - [f64::from_bits(0x3ff6719f18224223), f64::from_bits(0xbfd5a8ca86909000)], - [f64::from_bits(0x3ff6524f99a51ed9), f64::from_bits(0xbfd54f4356035000)], - [f64::from_bits(0x3ff63356aa8f24c4), f64::from_bits(0xbfd4f637c36b4000)], - [f64::from_bits(0x3ff614b36b9ddc14), f64::from_bits(0xbfd49da7fda85000)], - [f64::from_bits(0x3ff5f66452c65c4c), f64::from_bits(0xbfd445923989a800)], - [f64::from_bits(0x3ff5d867b5912c4f), f64::from_bits(0xbfd3edf439b0b800)], - [f64::from_bits(0x3ff5babccb5b90de), f64::from_bits(0xbfd396ce448f7000)], - [f64::from_bits(0x3ff59d61f2d91a78), f64::from_bits(0xbfd3401e17bda000)], - [f64::from_bits(0x3ff5805612465687), f64::from_bits(0xbfd2e9e2ef468000)], - [f64::from_bits(0x3ff56397cee76bd3), f64::from_bits(0xbfd2941b3830e000)], - [f64::from_bits(0x3ff54725e2a77f93), f64::from_bits(0xbfd23ec58cda8800)], - [f64::from_bits(0x3ff52aff42064583), f64::from_bits(0xbfd1e9e129279000)], - [f64::from_bits(0x3ff50f22dbb2bddf), f64::from_bits(0xbfd1956d2b48f800)], - [f64::from_bits(0x3ff4f38f4734ded7), f64::from_bits(0xbfd141679ab9f800)], - [f64::from_bits(0x3ff4d843cfde2840), f64::from_bits(0xbfd0edd094ef9800)], - [f64::from_bits(0x3ff4bd3ec078a3c8), f64::from_bits(0xbfd09aa518db1000)], - [f64::from_bits(0x3ff4a27fc3e0258a), f64::from_bits(0xbfd047e65263b800)], - [f64::from_bits(0x3ff4880524d48434), f64::from_bits(0xbfcfeb224586f000)], - [f64::from_bits(0x3ff46dce1b192d0b), f64::from_bits(0xbfcf474a7517b000)], - [f64::from_bits(0x3ff453d9d3391854), f64::from_bits(0xbfcea4443d103000)], - [f64::from_bits(0x3ff43a2744b4845a), f64::from_bits(0xbfce020d44e9b000)], - [f64::from_bits(0x3ff420b54115f8fb), f64::from_bits(0xbfcd60a22977f000)], - [f64::from_bits(0x3ff40782da3ef4b1), f64::from_bits(0xbfccc00104959000)], - [f64::from_bits(0x3ff3ee8f5d57fe8f), f64::from_bits(0xbfcc202956891000)], - [f64::from_bits(0x3ff3d5d9a00b4ce9), f64::from_bits(0xbfcb81178d811000)], - [f64::from_bits(0x3ff3bd60c010c12b), f64::from_bits(0xbfcae2c9ccd3d000)], - [f64::from_bits(0x3ff3a5242b75dab8), f64::from_bits(0xbfca45402e129000)], - [f64::from_bits(0x3ff38d22cd9fd002), f64::from_bits(0xbfc9a877681df000)], - [f64::from_bits(0x3ff3755bc5847a1c), f64::from_bits(0xbfc90c6d69483000)], - [f64::from_bits(0x3ff35dce49ad36e2), f64::from_bits(0xbfc87120a645c000)], - [f64::from_bits(0x3ff34679984dd440), f64::from_bits(0xbfc7d68fb4143000)], - [f64::from_bits(0x3ff32f5cceffcb24), f64::from_bits(0xbfc73cb83c627000)], - [f64::from_bits(0x3ff3187775a10d49), f64::from_bits(0xbfc6a39a9b376000)], - [f64::from_bits(0x3ff301c8373e3990), f64::from_bits(0xbfc60b3154b7a000)], - [f64::from_bits(0x3ff2eb4ebb95f841), f64::from_bits(0xbfc5737d76243000)], - [f64::from_bits(0x3ff2d50a0219a9d1), f64::from_bits(0xbfc4dc7b8fc23000)], - [f64::from_bits(0x3ff2bef9a8b7fd2a), f64::from_bits(0xbfc4462c51d20000)], - [f64::from_bits(0x3ff2a91c7a0c1bab), f64::from_bits(0xbfc3b08abc830000)], - [f64::from_bits(0x3ff293726014b530), f64::from_bits(0xbfc31b996b490000)], - [f64::from_bits(0x3ff27dfa5757a1f5), f64::from_bits(0xbfc2875490a44000)], - [f64::from_bits(0x3ff268b39b1d3bbf), f64::from_bits(0xbfc1f3b9f879a000)], - [f64::from_bits(0x3ff2539d838ff5bd), f64::from_bits(0xbfc160c8252ca000)], - [f64::from_bits(0x3ff23eb7aac9083b), f64::from_bits(0xbfc0ce7f57f72000)], - [f64::from_bits(0x3ff22a012ba940b6), f64::from_bits(0xbfc03cdc49fea000)], - [f64::from_bits(0x3ff2157996cc4132), f64::from_bits(0xbfbf57bdbc4b8000)], - [f64::from_bits(0x3ff201201dd2fc9b), f64::from_bits(0xbfbe370896404000)], - [f64::from_bits(0x3ff1ecf4494d480b), f64::from_bits(0xbfbd17983ef94000)], - [f64::from_bits(0x3ff1d8f5528f6569), f64::from_bits(0xbfbbf9674ed8a000)], - [f64::from_bits(0x3ff1c52311577e7c), f64::from_bits(0xbfbadc79202f6000)], - [f64::from_bits(0x3ff1b17c74cb26e9), f64::from_bits(0xbfb9c0c3e7288000)], - [f64::from_bits(0x3ff19e010c2c1ab6), f64::from_bits(0xbfb8a646b372c000)], - [f64::from_bits(0x3ff18ab07bb670bd), f64::from_bits(0xbfb78d01b3ac0000)], - [f64::from_bits(0x3ff1778a25efbcb6), f64::from_bits(0xbfb674f145380000)], - [f64::from_bits(0x3ff1648d354c31da), f64::from_bits(0xbfb55e0e6d878000)], - [f64::from_bits(0x3ff151b990275fdd), f64::from_bits(0xbfb4485cdea1e000)], - [f64::from_bits(0x3ff13f0ea432d24c), f64::from_bits(0xbfb333d94d6aa000)], - [f64::from_bits(0x3ff12c8b7210f9da), f64::from_bits(0xbfb22079f8c56000)], - [f64::from_bits(0x3ff11a3028ecb531), f64::from_bits(0xbfb10e4698622000)], - [f64::from_bits(0x3ff107fbda8434af), f64::from_bits(0xbfaffa6c6ad20000)], - [f64::from_bits(0x3ff0f5ee0f4e6bb3), f64::from_bits(0xbfadda8d4a774000)], - [f64::from_bits(0x3ff0e4065d2a9fce), f64::from_bits(0xbfabbcece4850000)], - [f64::from_bits(0x3ff0d244632ca521), f64::from_bits(0xbfa9a1894012c000)], - [f64::from_bits(0x3ff0c0a77ce2981a), f64::from_bits(0xbfa788583302c000)], - [f64::from_bits(0x3ff0af2f83c636d1), f64::from_bits(0xbfa5715e67d68000)], - [f64::from_bits(0x3ff09ddb98a01339), f64::from_bits(0xbfa35c8a49658000)], - [f64::from_bits(0x3ff08cabaf52e7df), f64::from_bits(0xbfa149e364154000)], - [f64::from_bits(0x3ff07b9f2f4e28fb), f64::from_bits(0xbf9e72c082eb8000)], - [f64::from_bits(0x3ff06ab58c358f19), f64::from_bits(0xbf9a55f152528000)], - [f64::from_bits(0x3ff059eea5ecf92c), f64::from_bits(0xbf963d62cf818000)], - [f64::from_bits(0x3ff04949cdd12c90), f64::from_bits(0xbf9228fb8caa0000)], - [f64::from_bits(0x3ff038c6c6f0ada9), f64::from_bits(0xbf8c317b20f90000)], - [f64::from_bits(0x3ff02865137932a9), f64::from_bits(0xbf8419355daa0000)], - [f64::from_bits(0x3ff0182427ea7348), f64::from_bits(0xbf781203c2ec0000)], - [f64::from_bits(0x3ff008040614b195), f64::from_bits(0xbf60040979240000)], - [f64::from_bits(0x3fefe01ff726fa1a), f64::from_bits(0x3f6feff384900000)], - [f64::from_bits(0x3fefa11cc261ea74), f64::from_bits(0x3f87dc41353d0000)], - [f64::from_bits(0x3fef6310b081992e), f64::from_bits(0x3f93cea3c4c28000)], - [f64::from_bits(0x3fef25f63ceeadcd), f64::from_bits(0x3f9b9fc114890000)], - [f64::from_bits(0x3feee9c8039113e7), f64::from_bits(0x3fa1b0d8ce110000)], - [f64::from_bits(0x3feeae8078cbb1ab), f64::from_bits(0x3fa58a5bd001c000)], - [f64::from_bits(0x3fee741aa29d0c9b), f64::from_bits(0x3fa95c8340d88000)], - [f64::from_bits(0x3fee3a91830a99b5), f64::from_bits(0x3fad276aef578000)], - [f64::from_bits(0x3fee01e009609a56), f64::from_bits(0x3fb07598e598c000)], - [f64::from_bits(0x3fedca01e577bb98), f64::from_bits(0x3fb253f5e30d2000)], - [f64::from_bits(0x3fed92f20b7c9103), f64::from_bits(0x3fb42edd8b380000)], - [f64::from_bits(0x3fed5cac66fb5cce), f64::from_bits(0x3fb606598757c000)], - [f64::from_bits(0x3fed272caa5ede9d), f64::from_bits(0x3fb7da76356a0000)], - [f64::from_bits(0x3fecf26e3e6b2ccd), f64::from_bits(0x3fb9ab434e1c6000)], - [f64::from_bits(0x3fecbe6da2a77902), f64::from_bits(0x3fbb78c7bb0d6000)], - [f64::from_bits(0x3fec8b266d37086d), f64::from_bits(0x3fbd431332e72000)], - [f64::from_bits(0x3fec5894bd5d5804), f64::from_bits(0x3fbf0a3171de6000)], - [f64::from_bits(0x3fec26b533bb9f8c), f64::from_bits(0x3fc067152b914000)], - [f64::from_bits(0x3febf583eeece73f), f64::from_bits(0x3fc147858292b000)], - [f64::from_bits(0x3febc4fd75db96c1), f64::from_bits(0x3fc2266ecdca3000)], - [f64::from_bits(0x3feb951e0c864a28), f64::from_bits(0x3fc303d7a6c55000)], - [f64::from_bits(0x3feb65e2c5ef3e2c), f64::from_bits(0x3fc3dfc33c331000)], - [f64::from_bits(0x3feb374867c9888b), f64::from_bits(0x3fc4ba366b7a8000)], - [f64::from_bits(0x3feb094b211d304a), f64::from_bits(0x3fc5933928d1f000)], - [f64::from_bits(0x3feadbe885f2ef7e), f64::from_bits(0x3fc66acd2418f000)], - [f64::from_bits(0x3feaaf1d31603da2), f64::from_bits(0x3fc740f8ec669000)], - [f64::from_bits(0x3fea82e63fd358a7), f64::from_bits(0x3fc815c0f51af000)], - [f64::from_bits(0x3fea5740ef09738b), f64::from_bits(0x3fc8e92954f68000)], - [f64::from_bits(0x3fea2c2a90ab4b27), f64::from_bits(0x3fc9bb3602f84000)], - [f64::from_bits(0x3fea01a01393f2d1), f64::from_bits(0x3fca8bed1c2c0000)], - [f64::from_bits(0x3fe9d79f24db3c1b), f64::from_bits(0x3fcb5b515c01d000)], - [f64::from_bits(0x3fe9ae2505c7b190), f64::from_bits(0x3fcc2967ccbcc000)], - [f64::from_bits(0x3fe9852ef297ce2f), f64::from_bits(0x3fccf635d5486000)], - [f64::from_bits(0x3fe95cbaeea44b75), f64::from_bits(0x3fcdc1bd3446c000)], - [f64::from_bits(0x3fe934c69de74838), f64::from_bits(0x3fce8c01b8cfe000)], - [f64::from_bits(0x3fe90d4f2f6752e6), f64::from_bits(0x3fcf5509c0179000)], - [f64::from_bits(0x3fe8e6528effd79d), f64::from_bits(0x3fd00e6c121fb800)], - [f64::from_bits(0x3fe8bfce9fcc007c), f64::from_bits(0x3fd071b80e93d000)], - [f64::from_bits(0x3fe899c0dabec30e), f64::from_bits(0x3fd0d46b9e867000)], - [f64::from_bits(0x3fe87427aa2317fb), f64::from_bits(0x3fd13687334bd000)], - [f64::from_bits(0x3fe84f00acb39a08), f64::from_bits(0x3fd1980d67234800)], - [f64::from_bits(0x3fe82a49e8653e55), f64::from_bits(0x3fd1f8ffe0cc8000)], - [f64::from_bits(0x3fe8060195f40260), f64::from_bits(0x3fd2595fd7636800)], - [f64::from_bits(0x3fe7e22563e0a329), f64::from_bits(0x3fd2b9300914a800)], - [f64::from_bits(0x3fe7beb377dcb5ad), f64::from_bits(0x3fd3187210436000)], - [f64::from_bits(0x3fe79baa679725c2), f64::from_bits(0x3fd377266dec1800)], - [f64::from_bits(0x3fe77907f2170657), f64::from_bits(0x3fd3d54ffbaf3000)], - [f64::from_bits(0x3fe756cadbd6130c), f64::from_bits(0x3fd432eee32fe000)], -]; -pub(crate) const LOG_TAB2: [[f64; 2]; 128] = [ - [f64::from_bits(0x3fe61000014fb66b), f64::from_bits(0x3c7e026c91425b3c)], - [f64::from_bits(0x3fe63000034db495), f64::from_bits(0x3c8dbfea48005d41)], - [f64::from_bits(0x3fe650000d94d478), f64::from_bits(0x3c8e7fa786d6a5b7)], - [f64::from_bits(0x3fe67000074e6fad), f64::from_bits(0x3c61fcea6b54254c)], - [f64::from_bits(0x3fe68ffffedf0fae), f64::from_bits(0xbc7c7e274c590efd)], - [f64::from_bits(0x3fe6b0000763c5bc), f64::from_bits(0xbc8ac16848dcda01)], - [f64::from_bits(0x3fe6d0001e5cc1f6), f64::from_bits(0x3c833f1c9d499311)], - [f64::from_bits(0x3fe6efffeb05f63e), f64::from_bits(0xbc7e80041ae22d53)], - [f64::from_bits(0x3fe710000e869780), f64::from_bits(0x3c7bff6671097952)], - [f64::from_bits(0x3fe72ffffc67e912), f64::from_bits(0x3c8c00e226bd8724)], - [f64::from_bits(0x3fe74fffdf81116a), f64::from_bits(0xbc6e02916ef101d2)], - [f64::from_bits(0x3fe770000f679c90), f64::from_bits(0xbc67fc71cd549c74)], - [f64::from_bits(0x3fe78ffffa7ec835), f64::from_bits(0x3c81bec19ef50483)], - [f64::from_bits(0x3fe7affffe20c2e6), f64::from_bits(0xbc707e1729cc6465)], - [f64::from_bits(0x3fe7cfffed3fc900), f64::from_bits(0xbc808072087b8b1c)], - [f64::from_bits(0x3fe7efffe9261a76), f64::from_bits(0x3c8dc0286d9df9ae)], - [f64::from_bits(0x3fe81000049ca3e8), f64::from_bits(0x3c897fd251e54c33)], - [f64::from_bits(0x3fe8300017932c8f), f64::from_bits(0xbc8afee9b630f381)], - [f64::from_bits(0x3fe850000633739c), f64::from_bits(0x3c89bfbf6b6535bc)], - [f64::from_bits(0x3fe87000204289c6), f64::from_bits(0xbc8bbf65f3117b75)], - [f64::from_bits(0x3fe88fffebf57904), f64::from_bits(0xbc89006ea23dcb57)], - [f64::from_bits(0x3fe8b00022bc04df), f64::from_bits(0xbc7d00df38e04b0a)], - [f64::from_bits(0x3fe8cfffe50c1b8a), f64::from_bits(0xbc88007146ff9f05)], - [f64::from_bits(0x3fe8effffc918e43), f64::from_bits(0x3c83817bd07a7038)], - [f64::from_bits(0x3fe910001efa5fc7), f64::from_bits(0x3c893e9176dfb403)], - [f64::from_bits(0x3fe9300013467bb9), f64::from_bits(0x3c7f804e4b980276)], - [f64::from_bits(0x3fe94fffe6ee076f), f64::from_bits(0xbc8f7ef0d9ff622e)], - [f64::from_bits(0x3fe96fffde3c12d1), f64::from_bits(0xbc7082aa962638ba)], - [f64::from_bits(0x3fe98ffff4458a0d), f64::from_bits(0xbc87801b9164a8ef)], - [f64::from_bits(0x3fe9afffdd982e3e), f64::from_bits(0xbc8740e08a5a9337)], - [f64::from_bits(0x3fe9cfffed49fb66), f64::from_bits(0x3c3fce08c19be000)], - [f64::from_bits(0x3fe9f00020f19c51), f64::from_bits(0xbc8a3faa27885b0a)], - [f64::from_bits(0x3fea10001145b006), f64::from_bits(0x3c74ff489958da56)], - [f64::from_bits(0x3fea300007bbf6fa), f64::from_bits(0x3c8cbeab8a2b6d18)], - [f64::from_bits(0x3fea500010971d79), f64::from_bits(0x3c88fecadd787930)], - [f64::from_bits(0x3fea70001df52e48), f64::from_bits(0xbc8f41763dd8abdb)], - [f64::from_bits(0x3fea90001c593352), f64::from_bits(0xbc8ebf0284c27612)], - [f64::from_bits(0x3feab0002a4f3e4b), f64::from_bits(0xbc69fd043cff3f5f)], - [f64::from_bits(0x3feacfffd7ae1ed1), f64::from_bits(0xbc823ee7129070b4)], - [f64::from_bits(0x3feaefffee510478), f64::from_bits(0x3c6a063ee00edea3)], - [f64::from_bits(0x3feb0fffdb650d5b), f64::from_bits(0x3c5a06c8381f0ab9)], - [f64::from_bits(0x3feb2ffffeaaca57), f64::from_bits(0xbc79011e74233c1d)], - [f64::from_bits(0x3feb4fffd995badc), f64::from_bits(0xbc79ff1068862a9f)], - [f64::from_bits(0x3feb7000249e659c), f64::from_bits(0x3c8aff45d0864f3e)], - [f64::from_bits(0x3feb8ffff9871640), f64::from_bits(0x3c7cfe7796c2c3f9)], - [f64::from_bits(0x3febafffd204cb4f), f64::from_bits(0xbc63ff27eef22bc4)], - [f64::from_bits(0x3febcfffd2415c45), f64::from_bits(0xbc6cffb7ee3bea21)], - [f64::from_bits(0x3febeffff86309df), f64::from_bits(0xbc814103972e0b5c)], - [f64::from_bits(0x3fec0fffe1b57653), f64::from_bits(0x3c8bc16494b76a19)], - [f64::from_bits(0x3fec2ffff1fa57e3), f64::from_bits(0xbc64feef8d30c6ed)], - [f64::from_bits(0x3fec4fffdcbfe424), f64::from_bits(0xbc843f68bcec4775)], - [f64::from_bits(0x3fec6fffed54b9f7), f64::from_bits(0x3c847ea3f053e0ec)], - [f64::from_bits(0x3fec8fffeb998fd5), f64::from_bits(0x3c7383068df992f1)], - [f64::from_bits(0x3fecb0002125219a), f64::from_bits(0xbc68fd8e64180e04)], - [f64::from_bits(0x3feccfffdd94469c), f64::from_bits(0x3c8e7ebe1cc7ea72)], - [f64::from_bits(0x3fecefffeafdc476), f64::from_bits(0x3c8ebe39ad9f88fe)], - [f64::from_bits(0x3fed1000169af82b), f64::from_bits(0x3c757d91a8b95a71)], - [f64::from_bits(0x3fed30000d0ff71d), f64::from_bits(0x3c89c1906970c7da)], - [f64::from_bits(0x3fed4fffea790fc4), f64::from_bits(0xbc580e37c558fe0c)], - [f64::from_bits(0x3fed70002edc87e5), f64::from_bits(0xbc7f80d64dc10f44)], - [f64::from_bits(0x3fed900021dc82aa), f64::from_bits(0xbc747c8f94fd5c5c)], - [f64::from_bits(0x3fedafffd86b0283), f64::from_bits(0x3c8c7f1dc521617e)], - [f64::from_bits(0x3fedd000296c4739), f64::from_bits(0x3c88019eb2ffb153)], - [f64::from_bits(0x3fedefffe54490f5), f64::from_bits(0x3c6e00d2c652cc89)], - [f64::from_bits(0x3fee0fffcdabf694), f64::from_bits(0xbc7f8340202d69d2)], - [f64::from_bits(0x3fee2fffdb52c8dd), f64::from_bits(0x3c7b00c1ca1b0864)], - [f64::from_bits(0x3fee4ffff24216ef), f64::from_bits(0x3c72ffa8b094ab51)], - [f64::from_bits(0x3fee6fffe88a5e11), f64::from_bits(0xbc57f673b1efbe59)], - [f64::from_bits(0x3fee9000119eff0d), f64::from_bits(0xbc84808d5e0bc801)], - [f64::from_bits(0x3feeafffdfa51744), f64::from_bits(0x3c780006d54320b5)], - [f64::from_bits(0x3feed0001a127fa1), f64::from_bits(0xbc5002f860565c92)], - [f64::from_bits(0x3feef00007babcc4), f64::from_bits(0xbc8540445d35e611)], - [f64::from_bits(0x3fef0ffff57a8d02), f64::from_bits(0xbc4ffb3139ef9105)], - [f64::from_bits(0x3fef30001ee58ac7), f64::from_bits(0x3c8a81acf2731155)], - [f64::from_bits(0x3fef4ffff5823494), f64::from_bits(0x3c8a3f41d4d7c743)], - [f64::from_bits(0x3fef6ffffca94c6b), f64::from_bits(0xbc6202f41c987875)], - [f64::from_bits(0x3fef8fffe1f9c441), f64::from_bits(0x3c777dd1f477e74b)], - [f64::from_bits(0x3fefafffd2e0e37e), f64::from_bits(0xbc6f01199a7ca331)], - [f64::from_bits(0x3fefd0001c77e49e), f64::from_bits(0x3c7181ee4bceacb1)], - [f64::from_bits(0x3fefeffff7e0c331), f64::from_bits(0xbc6e05370170875a)], - [f64::from_bits(0x3ff00ffff465606e), f64::from_bits(0xbc8a7ead491c0ada)], - [f64::from_bits(0x3ff02ffff3867a58), f64::from_bits(0xbc977f69c3fcb2e0)], - [f64::from_bits(0x3ff04ffffdfc0d17), f64::from_bits(0x3c97bffe34cb945b)], - [f64::from_bits(0x3ff0700003cd4d82), f64::from_bits(0x3c820083c0e456cb)], - [f64::from_bits(0x3ff08ffff9f2cbe8), f64::from_bits(0xbc6dffdfbe37751a)], - [f64::from_bits(0x3ff0b000010cda65), f64::from_bits(0xbc913f7faee626eb)], - [f64::from_bits(0x3ff0d00001a4d338), f64::from_bits(0x3c807dfa79489ff7)], - [f64::from_bits(0x3ff0effffadafdfd), f64::from_bits(0xbc77040570d66bc0)], - [f64::from_bits(0x3ff110000bbafd96), f64::from_bits(0x3c8e80d4846d0b62)], - [f64::from_bits(0x3ff12ffffae5f45d), f64::from_bits(0x3c9dbffa64fd36ef)], - [f64::from_bits(0x3ff150000dd59ad9), f64::from_bits(0x3c9a0077701250ae)], - [f64::from_bits(0x3ff170000f21559a), f64::from_bits(0x3c8dfdf9e2e3deee)], - [f64::from_bits(0x3ff18ffffc275426), f64::from_bits(0x3c910030dc3b7273)], - [f64::from_bits(0x3ff1b000123d3c59), f64::from_bits(0x3c997f7980030188)], - [f64::from_bits(0x3ff1cffff8299eb7), f64::from_bits(0xbc65f932ab9f8c67)], - [f64::from_bits(0x3ff1effff48ad400), f64::from_bits(0x3c937fbf9da75beb)], - [f64::from_bits(0x3ff210000c8b86a4), f64::from_bits(0x3c9f806b91fd5b22)], - [f64::from_bits(0x3ff2300003854303), f64::from_bits(0x3c93ffc2eb9fbf33)], - [f64::from_bits(0x3ff24fffffbcf684), f64::from_bits(0x3c7601e77e2e2e72)], - [f64::from_bits(0x3ff26ffff52921d9), f64::from_bits(0x3c7ffcbb767f0c61)], - [f64::from_bits(0x3ff2900014933a3c), f64::from_bits(0xbc7202ca3c02412b)], - [f64::from_bits(0x3ff2b00014556313), f64::from_bits(0xbc92808233f21f02)], - [f64::from_bits(0x3ff2cfffebfe523b), f64::from_bits(0xbc88ff7e384fdcf2)], - [f64::from_bits(0x3ff2f0000bb8ad96), f64::from_bits(0xbc85ff51503041c5)], - [f64::from_bits(0x3ff30ffffb7ae2af), f64::from_bits(0xbc810071885e289d)], - [f64::from_bits(0x3ff32ffffeac5f7f), f64::from_bits(0xbc91ff5d3fb7b715)], - [f64::from_bits(0x3ff350000ca66756), f64::from_bits(0x3c957f82228b82bd)], - [f64::from_bits(0x3ff3700011fbf721), f64::from_bits(0x3c8000bac40dd5cc)], - [f64::from_bits(0x3ff38ffff9592fb9), f64::from_bits(0xbc943f9d2db2a751)], - [f64::from_bits(0x3ff3b00004ddd242), f64::from_bits(0x3c857f6b707638e1)], - [f64::from_bits(0x3ff3cffff5b2c957), f64::from_bits(0x3c7a023a10bf1231)], - [f64::from_bits(0x3ff3efffeab0b418), f64::from_bits(0x3c987f6d66b152b0)], - [f64::from_bits(0x3ff410001532aff4), f64::from_bits(0x3c67f8375f198524)], - [f64::from_bits(0x3ff4300017478b29), f64::from_bits(0x3c8301e672dc5143)], - [f64::from_bits(0x3ff44fffe795b463), f64::from_bits(0x3c89ff69b8b2895a)], - [f64::from_bits(0x3ff46fffe80475e0), f64::from_bits(0xbc95c0b19bc2f254)], - [f64::from_bits(0x3ff48fffef6fc1e7), f64::from_bits(0x3c9b4009f23a2a72)], - [f64::from_bits(0x3ff4afffe5bea704), f64::from_bits(0xbc94ffb7bf0d7d45)], - [f64::from_bits(0x3ff4d000171027de), f64::from_bits(0xbc99c06471dc6a3d)], - [f64::from_bits(0x3ff4f0000ff03ee2), f64::from_bits(0x3c977f890b85531c)], - [f64::from_bits(0x3ff5100012dc4bd1), f64::from_bits(0x3c6004657166a436)], - [f64::from_bits(0x3ff530001605277a), f64::from_bits(0xbc96bfcece233209)], - [f64::from_bits(0x3ff54fffecdb704c), f64::from_bits(0xbc8902720505a1d7)], - [f64::from_bits(0x3ff56fffef5f54a9), f64::from_bits(0x3c9bbfe60ec96412)], - [f64::from_bits(0x3ff5900017e61012), f64::from_bits(0x3c887ec581afef90)], - [f64::from_bits(0x3ff5b00003c93e92), f64::from_bits(0xbc9f41080abf0cc0)], - [f64::from_bits(0x3ff5d0001d4919bc), f64::from_bits(0xbc98812afb254729)], - [f64::from_bits(0x3ff5efffe7b87a89), f64::from_bits(0xbc947eb780ed6904)], -]; diff --git a/github-simulator/source/src/math/float_reduction.rs b/github-simulator/source/src/math/float_reduction.rs deleted file mode 100644 index 7eeea878c1845126cce7d9dd2493f5829daa7efb..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/math/float_reduction.rs +++ /dev/null @@ -1,146 +0,0 @@ -//! Wine/musl __rem_pio2_large, specialized to one float mantissa and prec=0. -//! Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. -//! Developed at SunSoft, a Sun Microsystems, Inc. business. -//! Permission to use, copy, modify, and distribute this software is freely -//! granted, provided that this notice is preserved. - -const TWO_OVER_PI: [i32; 66] = [ - 0xA2F983, 0x6E4E44, 0x1529FC, 0x2757D1, 0xF534DD, 0xC0DB62, 0x95993C, 0x439041, 0xFE5163, 0xABDEBB, 0xC561B7, - 0x246E3A, 0x424DD2, 0xE00649, 0x2EEA09, 0xD1921C, 0xFE1DEB, 0x1CB129, 0xA73EE8, 0x8235F5, 0x2EBB44, 0x84E99C, - 0x7026B4, 0x5F7E41, 0x3991D6, 0x398353, 0x39F49C, 0x845F8B, 0xBDF928, 0x3B1FF8, 0x97FFDE, 0x05980F, 0xEF2F11, - 0x8B5A0A, 0x6D1F6D, 0x367ECF, 0x27CB09, 0xB74F46, 0x3F669E, 0x5FEA2D, 0x7527BA, 0xC7EBE5, 0xF17B3D, 0x0739F7, - 0x8A5292, 0xEA6BFB, 0x5FB11F, 0x8D5D08, 0x560330, 0x46FC7B, 0x6BABF0, 0xCFBC20, 0x9AF436, 0x1DA9E3, 0x91615E, - 0xE61B08, 0x659985, 0x5F14A0, 0x68408D, 0xFFD880, 0x4D7327, 0x310606, 0x1556CA, 0x73A8C9, 0x60E27B, 0xC08C6B, -]; -// These split coefficients retain the original reduction rounding. -#[allow(clippy::excessive_precision)] -const PI_OVER_TWO: [f64; 4] = [ - 1.57079625129699707031, - 7.54978941586159635335e-8, - 5.39030252995776476554e-15, - 3.28200341580791294123e-22, -]; -fn power_two(exponent: i32) -> f64 { - f64::from_bits(((1023 + exponent) as u64) << 52) -} - -pub(crate) fn reduce(x: f32) -> (i32, f64) { - let bits = x.to_bits(); - let magnitude = bits & 0x7fff_ffff; - let exponent = (magnitude >> 23) as i32 - 150; - let mantissa = f32::from_bits(magnitude - ((exponent as u32) << 23)) as f64; - let (n, y) = reduce_mantissa(mantissa, exponent); - if bits >> 31 != 0 { - (-n, -y) - } else { - (n, y) - } -} - -fn reduce_mantissa(x: f64, exponent: i32) -> (i32, f64) { - let start = ((exponent - 3) / 24).max(0) as usize; - let mut q0 = exponent - 24 * (start as i32 + 1); - let mut q = [0.0; 20]; - let mut iq = [0i32; 20]; - let mut fq = [0.0; 20]; - for i in 0..=3 { - q[i] = x * TWO_OVER_PI[start + i] as f64; - } - let mut end = 3usize; - let (n, ih, mut z) = loop { - let mut z = q[end]; - for i in 0..end { - let whole = (z / 16777216.0) as i32 as f64; - iq[i] = (z - 16777216.0 * whole) as i32; - z = q[end - i - 1] + whole; - } - z *= power_two(q0); - z -= 8.0 * (z * 0.125).floor(); - let mut n = z as i32; - z -= n as f64; - let mut ih = 0; - if q0 > 0 { - let high = iq[end - 1] >> (24 - q0); - n += high; - iq[end - 1] -= high << (24 - q0); - ih = iq[end - 1] >> (23 - q0); - } else if q0 == 0 { - ih = iq[end - 1] >> 23; - } else if z >= 0.5 { - ih = 2; - } - if ih > 0 { - n += 1; - let mut carry = false; - for chunk in &mut iq[..end] { - if !carry { - if *chunk != 0 { - carry = true; - *chunk = 0x1000000 - *chunk; - } - } else { - *chunk = 0xffffff - *chunk; - } - } - if q0 == 1 { - iq[end - 1] &= 0x7fffff; - } else if q0 == 2 { - iq[end - 1] &= 0x3fffff; - } - if ih == 2 { - z = 1.0 - z; - if carry { - z -= power_two(q0); - } - } - } - if z == 0.0 && iq[3..end].iter().all(|&v| v == 0) { - let mut extra = 1; - while iq[3 - extra] == 0 { - extra += 1; - } - for i in end + 1..=end + extra { - q[i] = x * TWO_OVER_PI[start + i] as f64; - } - end += extra; - continue; - } - break (n, ih, z); - }; - if z == 0.0 { - end -= 1; - q0 -= 24; - while iq[end] == 0 { - end -= 1; - q0 -= 24; - } - } else { - z *= power_two(-q0); - if z >= 16777216.0 { - let whole = (z / 16777216.0) as i32; - iq[end] = (z - 16777216.0 * whole as f64) as i32; - end += 1; - q0 += 24; - iq[end] = whole; - } else { - iq[end] = z as i32; - } - } - let mut scale = power_two(q0); - for i in (0..=end).rev() { - q[i] = scale * iq[i] as f64; - scale *= 1.0 / 16777216.0; - } - for i in (0..=end).rev() { - let mut sum = 0.0; - for k in 0..=3.min(end - i) { - sum += PI_OVER_TWO[k] * q[i + k]; - } - fq[end - i] = sum; - } - let mut y = 0.0; - for i in (0..=end).rev() { - y += fq[i]; - } - (n & 7, if ih == 0 { y } else { -y }) -} diff --git a/github-simulator/source/src/math/godot_math.rs b/github-simulator/source/src/math/godot_math.rs deleted file mode 100644 index 695051e40bd4659d67500a5d2fbda5db7e3af6eb..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/math/godot_math.rs +++ /dev/null @@ -1,231 +0,0 @@ -//! Float32 operations at the Godot Vector2/Transform2D boundary. -//! Networks and the game's explicitly double-valued scalars keep using f64. -use crate::math::vec2::V2; -use std::ops::{Add, Mul, Neg, Sub}; - -#[derive(Clone, Copy, Debug, Default)] -pub struct F2 { - pub x: f32, - pub y: f32, -} -impl From for F2 { - fn from(v: V2) -> Self { - Self { - x: v.x as f32, - y: v.y as f32, - } - } -} -impl From for V2 { - fn from(v: F2) -> Self { - Self::new(v.x as f64, v.y as f64) - } -} -impl Add for F2 { - type Output = Self; - fn add(self, v: Self) -> Self { - Self { - x: self.x + v.x, - y: self.y + v.y, - } - } -} -impl Sub for F2 { - type Output = Self; - fn sub(self, v: Self) -> Self { - Self { - x: self.x - v.x, - y: self.y - v.y, - } - } -} -impl Mul for F2 { - type Output = Self; - fn mul(self, s: f32) -> Self { - Self { - x: self.x * s, - y: self.y * s, - } - } -} -impl Neg for F2 { - type Output = Self; - fn neg(self) -> Self { - Self { x: -self.x, y: -self.y } - } -} -impl F2 { - pub fn dot(self, v: Self) -> f32 { - self.x * v.x + self.y * v.y - } - pub(crate) fn cross(self, v: Self) -> f32 { - self.x * v.y - self.y * v.x - } - pub(crate) fn length(self) -> f32 { - self.dot(self).sqrt() - } - pub fn normalized(self) -> Self { - let n = self.length(); - if n == 0.0 { - Self::default() - } else { - Self { - x: self.x / n, - y: self.y / n, - } - } - } - pub(crate) fn limit(self, max: f32) -> Self { - let n = self.length(); - if n > max && n > 0.0 { - // Godot C# Vector2.LimitLength divides before multiplying. - Self { - x: self.x / n, - y: self.y / n, - } * max - } else { - self - } - } - pub(crate) fn rotated(self, angle: f32) -> Self { - self.rotated_sin_cos(managed_sin_cos(angle)) - } - /// `rotated` by the angle whose `managed_sin_cos` is `(s, c)`. - #[inline] - pub(crate) fn rotated_sin_cos(self, (s, c): (f32, f32)) -> Self { - Self { - x: self.x * c - self.y * s, - y: self.x * s + self.y * c, - } - } -} - -/// Windows .NET 8.0.2 MathF.SinCos, as used by the game's managed transforms. -/// COMSingle::SinCos uses the compiler's packed sine routine, including a -/// rounded pi/2 shift for cosine. Native Godot transforms use separate sin/cos. -/// Constants and operation order are from the shipped coreclr.dll, RVA 169f90 -/// and 16b620. No fused multiply-adds; large lanes use the original tables. -// Negated float comparisons reproduce the managed branch behavior, including NaNs. -#[allow(clippy::neg_cmp_op_on_partial_ord)] -pub fn managed_sin_cos(angle: f32) -> (f32, f32) { - // COMISS takes the wrapper's tiny branch for NaNs. Sine retains the input - // payload; cosine quiets the absolute-value payload through multiplication. - if angle.is_nan() { - return (angle, f32::from_bits((angle.to_bits() & 0x7fffffff) | 0x00400000)); - } - if !(angle.abs() >= f32::from_bits(0x39000000)) { - return (angle, 1.0 - angle.abs() * angle.abs() * 0.5); - } - fn packed_sine(value: f32) -> f32 { - let abs = value.abs(); - if !(abs <= 10000.0) { - return crate::math::managed_trig::sine_large(value); - } - let rounded = abs * f32::from_bits(0x3ea2f983) + f32::from_bits(0x4b400000); - let n = rounded - f32::from_bits(0x4b400000); - let mut r = abs - n * f32::from_bits(0x40490000); - r -= n * f32::from_bits(0x3a7da000); - r -= n * f32::from_bits(0x34222000); - r -= n * f32::from_bits(0x2cb4611a); - let square = r * r; - r = f32::from_bits(r.to_bits() ^ (rounded.to_bits() << 31)); - let mut p = f32::from_bits(0x362edef8) * square; - p += f32::from_bits(0xb94fb7ff); - p *= square; - p += f32::from_bits(0x3c088766); - p *= square; - p += f32::from_bits(0xbe2aaaa6); - let result = r + (square * p) * r; - f32::from_bits(result.to_bits() ^ (value.to_bits() & 0x80000000)) - } - ( - packed_sine(angle), - packed_sine(angle.abs() + std::f32::consts::FRAC_PI_2), - ) -} - -/// `managed_sin_cos(-PI / 2)`, the quarter turn from a body's right to its front. -pub(crate) const QUARTER_TURN_BACK: (f32, f32) = (f32::from_bits(0xbf7f_ffff), f32::from_bits(0xb3bb_bd2e)); - -pub fn basis(rotation: f64) -> (F2, F2) { - let (s, c) = crate::math::native_math::engine_sin_cos(rotation as f32); - (F2 { x: c, y: s }, F2 { x: -s, y: c }) -} - -pub(crate) fn transform_point(position: V2, (x, y): (F2, F2), local: V2) -> V2 { - let p = F2::from(local); - // Transform2D operator * evaluates the two products before the origin. - (x * p.x + y * p.y + F2::from(position)).into() -} - -pub(crate) fn combine(brake: F2, drive: F2, brake_max: f32, drive_max: f32) -> F2 { - let n = brake.length(); - if n < 0.0001 || brake_max <= 0.0 { - return brake + drive; - } - let allowed = n + drive_max * (1.0 - (n / brake_max).min(1.0)); - let result = brake + drive; - let axis = brake.normalized(); - let projection = result.dot(axis); - if projection > allowed { - result - axis * (projection - allowed) - } else { - result - } -} - -/// C# Mathf.Remap(value, -maximum, maximum, -1f, 1f), then Clamp. -/// Keeping the InverseLerp/Lerp operations preserves their float rounding. -pub(crate) fn signed_sensor(value: f32, maximum: f32) -> f64 { - let from = 0.0f32 - maximum; - let weight = (value - from) / (maximum - from); - (-1.0f32 + 2.0f32 * weight).clamp(-1.0, 1.0) as f64 -} - -/// BspTreeRaycaster.TryGetIntersection(wall, ray). The scene export already -/// contains BSP-split wall segments. The returned point lies on the wall. -pub(crate) fn ray_intersection(start: V2, end: V2, wall_start: V2, wall_end: V2) -> Option { - let (start, end) = (F2::from(start), F2::from(end)); - let (ws, we) = (F2::from(wall_start), F2::from(wall_end)); - ray_wall_hit(start, end - start, ws, we - ws).map(V2::from) -} - -/// `ray_intersection` with the ray `start` and `b = end - start` and the wall -/// `ws` and `a = we - ws` already in float32. -#[inline] -pub(crate) fn ray_wall_hit(start: F2, b: F2, ws: F2, a: F2) -> Option { - let denominator = a.x * b.y - b.x * a.y; - if denominator.abs() < 1e-6 { - return None; - } - let delta = ws - start; - let ray_fraction = ((0.0 - a.y) * delta.x + a.x * delta.y) / denominator; - let wall_fraction = (b.x * delta.y - b.y * delta.x) / denominator; - if !(0.0..=1.0).contains(&ray_fraction) || !(0.0..=1.0).contains(&wall_fraction) { - return None; - } - Some(ws + a * wall_fraction) -} - -/// Vehicle.Reset constructs a managed transform; BodySetState orthonormalizes it. -pub(crate) fn reset_basis(angle: f32) -> (F2, F2) { - let (s, c) = managed_sin_cos(angle); - let x = F2 { x: c, y: s }.normalized(); - let y = F2 { x: -s, y: c }; - (x, (y - x * x.dot(y)).normalized()) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn quarter_turn_back_is_managed_sin_cos() { - let (s, c) = managed_sin_cos(-std::f32::consts::FRAC_PI_2); - assert_eq!( - (s.to_bits(), c.to_bits()), - (QUARTER_TURN_BACK.0.to_bits(), QUARTER_TURN_BACK.1.to_bits()) - ); - assert_eq!(std::f32::consts::FRAC_PI_2, std::f32::consts::PI / 2.0); - } -} diff --git a/github-simulator/source/src/math/managed_sine_tables.rs b/github-simulator/source/src/math/managed_sine_tables.rs deleted file mode 100644 index 9d693b57f71a12788cd526db38cb9fe2d0c2c512..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/math/managed_sine_tables.rs +++ /dev/null @@ -1,519 +0,0 @@ -// Numeric tables extracted from Windows .NET 8.0.2 coreclr.dll, SSE4.1 packed sine. -// The extraction script is not distributed. -// Reference SHA256: 11f53a07c5cde93a0ab9e2505377905d85f1583cd5542dabd6713a4c5c2ba63b -pub(super) const EXPONENTS: [[u32; 3]; 256] = [ - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000000], - [0x00000000, 0x00000000, 0x00000001], - [0x00000000, 0x00000000, 0x00000002], - [0x00000000, 0x00000000, 0x00000005], - [0x00000000, 0x00000000, 0x0000000a], - [0x00000000, 0x00000000, 0x00000014], - [0x00000000, 0x00000000, 0x00000028], - [0x00000000, 0x00000000, 0x00000051], - [0x00000000, 0x00000000, 0x000000a2], - [0x00000000, 0x00000000, 0x00000145], - [0x00000000, 0x00000000, 0x0000028b], - [0x00000000, 0x00000000, 0x00000517], - [0x00000000, 0x00000000, 0x00000a2f], - [0x00000000, 0x00000000, 0x0000145f], - [0x00000000, 0x00000000, 0x000028be], - [0x00000000, 0x00000000, 0x0000517c], - [0x00000000, 0x00000000, 0x0000a2f9], - [0x00000000, 0x00000000, 0x000145f3], - [0x00000000, 0x00000000, 0x00028be6], - [0x00000000, 0x00000000, 0x000517cc], - [0x00000000, 0x00000000, 0x000a2f98], - [0x00000000, 0x00000000, 0x00145f30], - [0x00000000, 0x00000000, 0x0028be60], - [0x00000000, 0x00000000, 0x00517cc1], - [0x00000000, 0x00000000, 0x00a2f983], - [0x00000000, 0x00000000, 0x0145f306], - [0x00000000, 0x00000000, 0x028be60d], - [0x00000000, 0x00000000, 0x0517cc1b], - [0x00000000, 0x00000000, 0x0a2f9836], - [0x00000000, 0x00000000, 0x145f306d], - [0x00000000, 0x00000000, 0x28be60db], - [0x00000000, 0x00000000, 0x517cc1b7], - [0x00000000, 0x00000000, 0xa2f9836e], - [0x00000000, 0x00000001, 0x45f306dc], - [0x00000000, 0x00000002, 0x8be60db9], - [0x00000000, 0x00000005, 0x17cc1b72], - [0x00000000, 0x0000000a, 0x2f9836e4], - [0x00000000, 0x00000014, 0x5f306dc9], - [0x00000000, 0x00000028, 0xbe60db93], - [0x00000000, 0x00000051, 0x7cc1b727], - [0x00000000, 0x000000a2, 0xf9836e4e], - [0x00000000, 0x00000145, 0xf306dc9c], - [0x00000000, 0x0000028b, 0xe60db939], - [0x00000000, 0x00000517, 0xcc1b7272], - [0x00000000, 0x00000a2f, 0x9836e4e4], - [0x00000000, 0x0000145f, 0x306dc9c8], - [0x00000000, 0x000028be, 0x60db9391], - [0x00000000, 0x0000517c, 0xc1b72722], - [0x00000000, 0x0000a2f9, 0x836e4e44], - [0x00000000, 0x000145f3, 0x06dc9c88], - [0x00000000, 0x00028be6, 0x0db93910], - [0x00000000, 0x000517cc, 0x1b727220], - [0x00000000, 0x000a2f98, 0x36e4e441], - [0x00000000, 0x00145f30, 0x6dc9c882], - [0x00000000, 0x0028be60, 0xdb939105], - [0x00000000, 0x00517cc1, 0xb727220a], - [0x00000000, 0x00a2f983, 0x6e4e4415], - [0x00000000, 0x0145f306, 0xdc9c882a], - [0x00000000, 0x028be60d, 0xb9391054], - [0x00000000, 0x0517cc1b, 0x727220a9], - [0x00000000, 0x0a2f9836, 0xe4e44152], - [0x00000000, 0x145f306d, 0xc9c882a5], - [0x00000000, 0x28be60db, 0x9391054a], - [0x00000000, 0x517cc1b7, 0x27220a94], - [0x00000000, 0xa2f9836e, 0x4e441529], - [0x00000001, 0x45f306dc, 0x9c882a53], - [0x00000002, 0x8be60db9, 0x391054a7], - [0x00000005, 0x17cc1b72, 0x7220a94f], - [0x0000000a, 0x2f9836e4, 0xe441529f], - [0x00000014, 0x5f306dc9, 0xc882a53f], - [0x00000028, 0xbe60db93, 0x91054a7f], - [0x00000051, 0x7cc1b727, 0x220a94fe], - [0x000000a2, 0xf9836e4e, 0x441529fc], - [0x00000145, 0xf306dc9c, 0x882a53f8], - [0x0000028b, 0xe60db939, 0x1054a7f0], - [0x00000517, 0xcc1b7272, 0x20a94fe1], - [0x00000a2f, 0x9836e4e4, 0x41529fc2], - [0x0000145f, 0x306dc9c8, 0x82a53f84], - [0x000028be, 0x60db9391, 0x054a7f09], - [0x0000517c, 0xc1b72722, 0x0a94fe13], - [0x0000a2f9, 0x836e4e44, 0x1529fc27], - [0x000145f3, 0x06dc9c88, 0x2a53f84e], - [0x00028be6, 0x0db93910, 0x54a7f09d], - [0x000517cc, 0x1b727220, 0xa94fe13a], - [0x000a2f98, 0x36e4e441, 0x529fc275], - [0x00145f30, 0x6dc9c882, 0xa53f84ea], - [0x0028be60, 0xdb939105, 0x4a7f09d5], - [0x00517cc1, 0xb727220a, 0x94fe13ab], - [0x00a2f983, 0x6e4e4415, 0x29fc2757], - [0x0145f306, 0xdc9c882a, 0x53f84eaf], - [0x028be60d, 0xb9391054, 0xa7f09d5f], - [0x0517cc1b, 0x727220a9, 0x4fe13abe], - [0x0a2f9836, 0xe4e44152, 0x9fc2757d], - [0x145f306d, 0xc9c882a5, 0x3f84eafa], - [0x28be60db, 0x9391054a, 0x7f09d5f4], - [0x517cc1b7, 0x27220a94, 0xfe13abe8], - [0xa2f9836e, 0x4e441529, 0xfc2757d1], - [0x45f306dc, 0x9c882a53, 0xf84eafa3], - [0x8be60db9, 0x391054a7, 0xf09d5f47], - [0x17cc1b72, 0x7220a94f, 0xe13abe8f], - [0x2f9836e4, 0xe441529f, 0xc2757d1f], - [0x5f306dc9, 0xc882a53f, 0x84eafa3e], - [0xbe60db93, 0x91054a7f, 0x09d5f47d], - [0x7cc1b727, 0x220a94fe, 0x13abe8fa], - [0xf9836e4e, 0x441529fc, 0x2757d1f5], - [0xf306dc9c, 0x882a53f8, 0x4eafa3ea], - [0xe60db939, 0x1054a7f0, 0x9d5f47d4], - [0xcc1b7272, 0x20a94fe1, 0x3abe8fa9], - [0x9836e4e4, 0x41529fc2, 0x757d1f53], - [0x306dc9c8, 0x82a53f84, 0xeafa3ea6], - [0x60db9391, 0x054a7f09, 0xd5f47d4d], - [0xc1b72722, 0x0a94fe13, 0xabe8fa9a], - [0x836e4e44, 0x1529fc27, 0x57d1f534], - [0x06dc9c88, 0x2a53f84e, 0xafa3ea69], - [0x0db93910, 0x54a7f09d, 0x5f47d4d3], - [0x1b727220, 0xa94fe13a, 0xbe8fa9a6], - [0x36e4e441, 0x529fc275, 0x7d1f534d], - [0x6dc9c882, 0xa53f84ea, 0xfa3ea69b], - [0xdb939105, 0x4a7f09d5, 0xf47d4d37], - [0xb727220a, 0x94fe13ab, 0xe8fa9a6e], - [0x6e4e4415, 0x29fc2757, 0xd1f534dd], - [0xdc9c882a, 0x53f84eaf, 0xa3ea69bb], - [0xb9391054, 0xa7f09d5f, 0x47d4d377], - [0x727220a9, 0x4fe13abe, 0x8fa9a6ee], - [0xe4e44152, 0x9fc2757d, 0x1f534ddc], - [0xc9c882a5, 0x3f84eafa, 0x3ea69bb8], - [0x9391054a, 0x7f09d5f4, 0x7d4d3770], - [0x27220a94, 0xfe13abe8, 0xfa9a6ee0], - [0x4e441529, 0xfc2757d1, 0xf534ddc0], - [0x9c882a53, 0xf84eafa3, 0xea69bb81], - [0x391054a7, 0xf09d5f47, 0xd4d37703], - [0x7220a94f, 0xe13abe8f, 0xa9a6ee06], - [0xe441529f, 0xc2757d1f, 0x534ddc0d], - [0xc882a53f, 0x84eafa3e, 0xa69bb81b], - [0x91054a7f, 0x09d5f47d, 0x4d377036], - [0x220a94fe, 0x13abe8fa, 0x9a6ee06d], - [0x441529fc, 0x2757d1f5, 0x34ddc0db], - [0x882a53f8, 0x4eafa3ea, 0x69bb81b6], - [0x1054a7f0, 0x9d5f47d4, 0xd377036d], - [0x20a94fe1, 0x3abe8fa9, 0xa6ee06db], - [0x41529fc2, 0x757d1f53, 0x4ddc0db6], - [0x82a53f84, 0xeafa3ea6, 0x9bb81b6c], - [0x054a7f09, 0xd5f47d4d, 0x377036d8], - [0x0a94fe13, 0xabe8fa9a, 0x6ee06db1], - [0x1529fc27, 0x57d1f534, 0xddc0db62], - [0x2a53f84e, 0xafa3ea69, 0xbb81b6c5], - [0x54a7f09d, 0x5f47d4d3, 0x77036d8a], - [0xa94fe13a, 0xbe8fa9a6, 0xee06db14], - [0x529fc275, 0x7d1f534d, 0xdc0db629], - [0xa53f84ea, 0xfa3ea69b, 0xb81b6c52], - [0x4a7f09d5, 0xf47d4d37, 0x7036d8a5], - [0x94fe13ab, 0xe8fa9a6e, 0xe06db14a], - [0x29fc2757, 0xd1f534dd, 0xc0db6295], - [0x53f84eaf, 0xa3ea69bb, 0x81b6c52b], - [0xa7f09d5f, 0x47d4d377, 0x036d8a56], - [0x4fe13abe, 0x8fa9a6ee, 0x06db14ac], - [0x9fc2757d, 0x1f534ddc, 0x0db62959], - [0x3f84eafa, 0x3ea69bb8, 0x1b6c52b3], - [0x7f09d5f4, 0x7d4d3770, 0x36d8a566], - [0xfe13abe8, 0xfa9a6ee0, 0x6db14acc], - [0xfc2757d1, 0xf534ddc0, 0xdb629599], - [0xf84eafa3, 0xea69bb81, 0xb6c52b32], - [0xf09d5f47, 0xd4d37703, 0x6d8a5664], - [0xe13abe8f, 0xa9a6ee06, 0xdb14acc9], - [0xc2757d1f, 0x534ddc0d, 0xb6295993], - [0x84eafa3e, 0xa69bb81b, 0x6c52b327], - [0x09d5f47d, 0x4d377036, 0xd8a5664f], - [0x13abe8fa, 0x9a6ee06d, 0xb14acc9e], - [0x2757d1f5, 0x34ddc0db, 0x6295993c], - [0x4eafa3ea, 0x69bb81b6, 0xc52b3278], - [0x9d5f47d4, 0xd377036d, 0x8a5664f1], - [0x3abe8fa9, 0xa6ee06db, 0x14acc9e2], - [0x757d1f53, 0x4ddc0db6, 0x295993c4], - [0xeafa3ea6, 0x9bb81b6c, 0x52b32788], - [0xd5f47d4d, 0x377036d8, 0xa5664f10], - [0xabe8fa9a, 0x6ee06db1, 0x4acc9e21], - [0x57d1f534, 0xddc0db62, 0x95993c43], - [0xafa3ea69, 0xbb81b6c5, 0x2b327887], - [0x5f47d4d3, 0x77036d8a, 0x5664f10e], - [0xbe8fa9a6, 0xee06db14, 0xacc9e21c], - [0x7d1f534d, 0xdc0db629, 0x5993c439], - [0xfa3ea69b, 0xb81b6c52, 0xb3278872], - [0xf47d4d37, 0x7036d8a5, 0x664f10e4], - [0xe8fa9a6e, 0xe06db14a, 0xcc9e21c8], - [0xd1f534dd, 0xc0db6295, 0x993c4390], - [0xa3ea69bb, 0x81b6c52b, 0x32788720], - [0x47d4d377, 0x036d8a56, 0x64f10e41], - [0x8fa9a6ee, 0x06db14ac, 0xc9e21c82], - [0x1f534ddc, 0x0db62959, 0x93c43904], - [0x3ea69bb8, 0x1b6c52b3, 0x27887208], - [0x7d4d3770, 0x36d8a566, 0x4f10e410], - [0xfa9a6ee0, 0x6db14acc, 0x9e21c820], - [0xf534ddc0, 0xdb629599, 0x3c439041], - [0xea69bb81, 0xb6c52b32, 0x78872083], - [0xd4d37703, 0x6d8a5664, 0xf10e4107], - [0xa9a6ee06, 0xdb14acc9, 0xe21c820f], - [0x534ddc0d, 0xb6295993, 0xc439041f], - [0xa69bb81b, 0x6c52b327, 0x8872083f], - [0x4d377036, 0xd8a5664f, 0x10e4107f], - [0x9a6ee06d, 0xb14acc9e, 0x21c820ff], -]; -pub(super) const TRIG: [[u32; 4]; 256] = [ - [0x00000000, 0x00000000, 0x00000000, 0x3f800000], - [0xb99de7df, 0x3cc90ab0, 0xb005c998, 0x3f800000], - [0xba9de1c8, 0x3d48fb30, 0xb0ef227f, 0x3f800000], - [0xbb319298, 0x3d96a905, 0xb1531e61, 0x3f800000], - [0xbb9dc971, 0x3dc8bd36, 0xb07592f5, 0x3f800000], - [0xbbf66e3c, 0x3dfab273, 0xb11568cf, 0x3f800000], - [0xbc315502, 0x3e164083, 0x31e8e614, 0x3f800000], - [0xbc71360b, 0x3e2f10a2, 0x311167f9, 0x3f800000], - [0xbc9d6830, 0x3e47c5c2, 0xb0e5967d, 0x3f800000], - [0xbcc70c54, 0x3e605c13, 0x31a7e4f6, 0x3f800000], - [0xbcf58104, 0x3e78cfcc, 0xb11bd41d, 0x3f800000], - [0xbd145f8c, 0x3e888e93, 0x312c7d9e, 0x3f800000], - [0xbd305f55, 0x3e94a031, 0x326d59f0, 0x3f800000], - [0xbd4ebb8a, 0x3ea09ae5, 0xb23e89a0, 0x3f800000], - [0xbd6f6f7e, 0x3eac7cd4, 0xb2254e02, 0x3f800000], - [0xbd893b12, 0x3eb8442a, 0xb2705ba6, 0x3f800000], - [0xbd9be50c, 0x3ec3ef15, 0x31d5d52c, 0x3f800000], - [0xbdafb2cc, 0x3ecf7bca, 0x316a3b63, 0x3f800000], - [0xbdc4a143, 0x3edae880, 0x321e15cc, 0x3f800000], - [0xbddaad38, 0x3ee63375, 0xb1d9c774, 0x3f800000], - [0xbdf1d344, 0x3ef15aea, 0xb1ff2139, 0x3f800000], - [0xbe0507ea, 0x3efc5d27, 0xb180eca9, 0x3f800000], - [0xbe11af97, 0x3f039c3d, 0xb25ba002, 0x3f800000], - [0xbe1edeb5, 0x3f08f59b, 0xb2be4b4e, 0x3f800000], - [0xbe2c933b, 0x3f0e39da, 0xb24a32e7, 0x3f800000], - [0xbe3acb0c, 0x3f13682a, 0x32cdd12e, 0x3f800000], - [0xbe4983f7, 0x3f187fc0, 0xb1c7a3f3, 0x3f800000], - [0xbe58bbb7, 0x3f1d7fd1, 0x3292050c, 0x3f800000], - [0xbe686ff3, 0x3f226799, 0x322123bb, 0x3f800000], - [0xbe789e3f, 0x3f273656, 0xb2038343, 0x3f800000], - [0xbe84a20e, 0x3f2beb4a, 0xb2b73136, 0x3f800000], - [0xbe8d2f7d, 0x3f3085bb, 0xb2ae2d32, 0x3f800000], - [0xbe95f61a, 0x3f3504f3, 0x324fe77a, 0x3f800000], - [0x3e4216eb, 0x3f396842, 0xb2810007, 0x3f000000], - [0x3e2fad27, 0x3f3daef9, 0x319aabec, 0x3f000000], - [0x3e1cd957, 0x3f41d870, 0x32bff977, 0x3f000000], - [0x3e099e65, 0x3f45e403, 0x32b15174, 0x3f000000], - [0x3debfe8a, 0x3f49d112, 0x32992640, 0x3f000000], - [0x3dc3fdff, 0x3f4d9f02, 0x327e70e8, 0x3f000000], - [0x3d9b4153, 0x3f514d3d, 0x300c4f04, 0x3f000000], - [0x3d639d9d, 0x3f54db31, 0x3290ea1a, 0x3f000000], - [0x3d0f59aa, 0x3f584853, 0xb27d5fc0, 0x3f000000], - [0x3c670f32, 0x3f5b941a, 0x32232dc8, 0x3f000000], - [0xbbe8b648, 0x3f5ebe05, 0x32c6f953, 0x3f000000], - [0xbcea5164, 0x3f61c598, 0xb2e7f425, 0x3f000000], - [0xbd4e645a, 0x3f64aa59, 0x311a08fa, 0x3f000000], - [0xbd945dff, 0x3f676bd8, 0xb2bc3389, 0x3f000000], - [0xbdc210d8, 0x3f6a09a7, 0xb2eb236c, 0x3f000000], - [0xbdf043ab, 0x3f6c835e, 0x32f328d4, 0x3f000000], - [0xbe0f77ad, 0x3f6ed89e, 0xb29333dc, 0x3f000000], - [0x3db1f34f, 0x3f710908, 0x321ed0dd, 0x3e800000], - [0x3d826b93, 0x3f731447, 0x32c48e11, 0x3e800000], - [0x3d25018c, 0x3f74fa0b, 0xb2939d22, 0x3e800000], - [0x3c88e931, 0x3f76ba07, 0x326d092c, 0x3e800000], - [0xbbe60685, 0x3f7853f8, 0xb20db9e5, 0x3e800000], - [0xbcfd1f65, 0x3f79c79d, 0x32c64e59, 0x3e800000], - [0xbd60e8f8, 0x3f7b14be, 0x32ff75cb, 0x3e800000], - [0x3d3c4289, 0x3f7c3b28, 0xb231d68b, 0x3e000000], - [0x3cb2041c, 0x3f7d3aac, 0xb0f75ae9, 0x3e000000], - [0xbb29b1a9, 0x3f7e1324, 0xb2f1e603, 0x3e000000], - [0xbcdd0b28, 0x3f7ec46d, 0x31f44949, 0x3e000000], - [0x3c354825, 0x3f7f4e6d, 0x32d01884, 0x3d800000], - [0xbc5c1342, 0x3f7fb10f, 0x31de5b5f, 0x3d800000], - [0xbbdbd541, 0x3f7fec43, 0x3084cd0d, 0x3d000000], - [0x00000000, 0x3f800000, 0x00000000, 0x00000000], - [0x3bdbd541, 0x3f7fec43, 0x3084cd0d, 0xbd000000], - [0x3c5c1342, 0x3f7fb10f, 0x31de5b5f, 0xbd800000], - [0xbc354825, 0x3f7f4e6d, 0x32d01884, 0xbd800000], - [0x3cdd0b28, 0x3f7ec46d, 0x31f44949, 0xbe000000], - [0x3b29b1a9, 0x3f7e1324, 0xb2f1e603, 0xbe000000], - [0xbcb2041c, 0x3f7d3aac, 0xb0f75ae9, 0xbe000000], - [0xbd3c4289, 0x3f7c3b28, 0xb231d68b, 0xbe000000], - [0x3d60e8f8, 0x3f7b14be, 0x32ff75cb, 0xbe800000], - [0x3cfd1f65, 0x3f79c79d, 0x32c64e59, 0xbe800000], - [0x3be60685, 0x3f7853f8, 0xb20db9e5, 0xbe800000], - [0xbc88e931, 0x3f76ba07, 0x326d092c, 0xbe800000], - [0xbd25018c, 0x3f74fa0b, 0xb2939d22, 0xbe800000], - [0xbd826b93, 0x3f731447, 0x32c48e11, 0xbe800000], - [0xbdb1f34f, 0x3f710908, 0x321ed0dd, 0xbe800000], - [0x3e0f77ad, 0x3f6ed89e, 0xb29333dc, 0xbf000000], - [0x3df043ab, 0x3f6c835e, 0x32f328d4, 0xbf000000], - [0x3dc210d8, 0x3f6a09a7, 0xb2eb236c, 0xbf000000], - [0x3d945dff, 0x3f676bd8, 0xb2bc3389, 0xbf000000], - [0x3d4e645a, 0x3f64aa59, 0x311a08fa, 0xbf000000], - [0x3cea5164, 0x3f61c598, 0xb2e7f425, 0xbf000000], - [0x3be8b648, 0x3f5ebe05, 0x32c6f953, 0xbf000000], - [0xbc670f32, 0x3f5b941a, 0x32232dc8, 0xbf000000], - [0xbd0f59aa, 0x3f584853, 0xb27d5fc0, 0xbf000000], - [0xbd639d9d, 0x3f54db31, 0x3290ea1a, 0xbf000000], - [0xbd9b4153, 0x3f514d3d, 0x300c4f04, 0xbf000000], - [0xbdc3fdff, 0x3f4d9f02, 0x327e70e8, 0xbf000000], - [0xbdebfe8a, 0x3f49d112, 0x32992640, 0xbf000000], - [0xbe099e65, 0x3f45e403, 0x32b15174, 0xbf000000], - [0xbe1cd957, 0x3f41d870, 0x32bff977, 0xbf000000], - [0xbe2fad27, 0x3f3daef9, 0x319aabec, 0xbf000000], - [0xbe4216eb, 0x3f396842, 0xb2810007, 0xbf000000], - [0x3e95f61a, 0x3f3504f3, 0x324fe77a, 0xbf800000], - [0x3e8d2f7d, 0x3f3085bb, 0xb2ae2d32, 0xbf800000], - [0x3e84a20e, 0x3f2beb4a, 0xb2b73136, 0xbf800000], - [0x3e789e3f, 0x3f273656, 0xb2038343, 0xbf800000], - [0x3e686ff3, 0x3f226799, 0x322123bb, 0xbf800000], - [0x3e58bbb7, 0x3f1d7fd1, 0x3292050c, 0xbf800000], - [0x3e4983f7, 0x3f187fc0, 0xb1c7a3f3, 0xbf800000], - [0x3e3acb0c, 0x3f13682a, 0x32cdd12e, 0xbf800000], - [0x3e2c933b, 0x3f0e39da, 0xb24a32e7, 0xbf800000], - [0x3e1edeb5, 0x3f08f59b, 0xb2be4b4e, 0xbf800000], - [0x3e11af97, 0x3f039c3d, 0xb25ba002, 0xbf800000], - [0x3e0507ea, 0x3efc5d27, 0xb180eca9, 0xbf800000], - [0x3df1d344, 0x3ef15aea, 0xb1ff2139, 0xbf800000], - [0x3ddaad38, 0x3ee63375, 0xb1d9c774, 0xbf800000], - [0x3dc4a143, 0x3edae880, 0x321e15cc, 0xbf800000], - [0x3dafb2cc, 0x3ecf7bca, 0x316a3b63, 0xbf800000], - [0x3d9be50c, 0x3ec3ef15, 0x31d5d52c, 0xbf800000], - [0x3d893b12, 0x3eb8442a, 0xb2705ba6, 0xbf800000], - [0x3d6f6f7e, 0x3eac7cd4, 0xb2254e02, 0xbf800000], - [0x3d4ebb8a, 0x3ea09ae5, 0xb23e89a0, 0xbf800000], - [0x3d305f55, 0x3e94a031, 0x326d59f0, 0xbf800000], - [0x3d145f8c, 0x3e888e93, 0x312c7d9e, 0xbf800000], - [0x3cf58104, 0x3e78cfcc, 0xb11bd41d, 0xbf800000], - [0x3cc70c54, 0x3e605c13, 0x31a7e4f6, 0xbf800000], - [0x3c9d6830, 0x3e47c5c2, 0xb0e5967d, 0xbf800000], - [0x3c71360b, 0x3e2f10a2, 0x311167f9, 0xbf800000], - [0x3c315502, 0x3e164083, 0x31e8e614, 0xbf800000], - [0x3bf66e3c, 0x3dfab273, 0xb11568cf, 0xbf800000], - [0x3b9dc971, 0x3dc8bd36, 0xb07592f5, 0xbf800000], - [0x3b319298, 0x3d96a905, 0xb1531e61, 0xbf800000], - [0x3a9de1c8, 0x3d48fb30, 0xb0ef227f, 0xbf800000], - [0x399de7df, 0x3cc90ab0, 0xb005c998, 0xbf800000], - [0x00000000, 0x00000000, 0x00000000, 0xbf800000], - [0x399de7df, 0xbcc90ab0, 0x3005c998, 0xbf800000], - [0x3a9de1c8, 0xbd48fb30, 0x30ef227f, 0xbf800000], - [0x3b319298, 0xbd96a905, 0x31531e61, 0xbf800000], - [0x3b9dc971, 0xbdc8bd36, 0x307592f5, 0xbf800000], - [0x3bf66e3c, 0xbdfab273, 0x311568cf, 0xbf800000], - [0x3c315502, 0xbe164083, 0xb1e8e614, 0xbf800000], - [0x3c71360b, 0xbe2f10a2, 0xb11167f9, 0xbf800000], - [0x3c9d6830, 0xbe47c5c2, 0x30e5967d, 0xbf800000], - [0x3cc70c54, 0xbe605c13, 0xb1a7e4f6, 0xbf800000], - [0x3cf58104, 0xbe78cfcc, 0x311bd41d, 0xbf800000], - [0x3d145f8c, 0xbe888e93, 0xb12c7d9e, 0xbf800000], - [0x3d305f55, 0xbe94a031, 0xb26d59f0, 0xbf800000], - [0x3d4ebb8a, 0xbea09ae5, 0x323e89a0, 0xbf800000], - [0x3d6f6f7e, 0xbeac7cd4, 0x32254e02, 0xbf800000], - [0x3d893b12, 0xbeb8442a, 0x32705ba6, 0xbf800000], - [0x3d9be50c, 0xbec3ef15, 0xb1d5d52c, 0xbf800000], - [0x3dafb2cc, 0xbecf7bca, 0xb16a3b63, 0xbf800000], - [0x3dc4a143, 0xbedae880, 0xb21e15cc, 0xbf800000], - [0x3ddaad38, 0xbee63375, 0x31d9c774, 0xbf800000], - [0x3df1d344, 0xbef15aea, 0x31ff2139, 0xbf800000], - [0x3e0507ea, 0xbefc5d27, 0x3180eca9, 0xbf800000], - [0x3e11af97, 0xbf039c3d, 0x325ba002, 0xbf800000], - [0x3e1edeb5, 0xbf08f59b, 0x32be4b4e, 0xbf800000], - [0x3e2c933b, 0xbf0e39da, 0x324a32e7, 0xbf800000], - [0x3e3acb0c, 0xbf13682a, 0xb2cdd12e, 0xbf800000], - [0x3e4983f7, 0xbf187fc0, 0x31c7a3f3, 0xbf800000], - [0x3e58bbb7, 0xbf1d7fd1, 0xb292050c, 0xbf800000], - [0x3e686ff3, 0xbf226799, 0xb22123bb, 0xbf800000], - [0x3e789e3f, 0xbf273656, 0x32038343, 0xbf800000], - [0x3e84a20e, 0xbf2beb4a, 0x32b73136, 0xbf800000], - [0x3e8d2f7d, 0xbf3085bb, 0x32ae2d32, 0xbf800000], - [0x3e95f61a, 0xbf3504f3, 0xb24fe77a, 0xbf800000], - [0xbe4216eb, 0xbf396842, 0x32810007, 0xbf000000], - [0xbe2fad27, 0xbf3daef9, 0xb19aabec, 0xbf000000], - [0xbe1cd957, 0xbf41d870, 0xb2bff977, 0xbf000000], - [0xbe099e65, 0xbf45e403, 0xb2b15174, 0xbf000000], - [0xbdebfe8a, 0xbf49d112, 0xb2992640, 0xbf000000], - [0xbdc3fdff, 0xbf4d9f02, 0xb27e70e8, 0xbf000000], - [0xbd9b4153, 0xbf514d3d, 0xb00c4f04, 0xbf000000], - [0xbd639d9d, 0xbf54db31, 0xb290ea1a, 0xbf000000], - [0xbd0f59aa, 0xbf584853, 0x327d5fc0, 0xbf000000], - [0xbc670f32, 0xbf5b941a, 0xb2232dc8, 0xbf000000], - [0x3be8b648, 0xbf5ebe05, 0xb2c6f953, 0xbf000000], - [0x3cea5164, 0xbf61c598, 0x32e7f425, 0xbf000000], - [0x3d4e645a, 0xbf64aa59, 0xb11a08fa, 0xbf000000], - [0x3d945dff, 0xbf676bd8, 0x32bc3389, 0xbf000000], - [0x3dc210d8, 0xbf6a09a7, 0x32eb236c, 0xbf000000], - [0x3df043ab, 0xbf6c835e, 0xb2f328d4, 0xbf000000], - [0x3e0f77ad, 0xbf6ed89e, 0x329333dc, 0xbf000000], - [0xbdb1f34f, 0xbf710908, 0xb21ed0dd, 0xbe800000], - [0xbd826b93, 0xbf731447, 0xb2c48e11, 0xbe800000], - [0xbd25018c, 0xbf74fa0b, 0x32939d22, 0xbe800000], - [0xbc88e931, 0xbf76ba07, 0xb26d092c, 0xbe800000], - [0x3be60685, 0xbf7853f8, 0x320db9e5, 0xbe800000], - [0x3cfd1f65, 0xbf79c79d, 0xb2c64e59, 0xbe800000], - [0x3d60e8f8, 0xbf7b14be, 0xb2ff75cb, 0xbe800000], - [0xbd3c4289, 0xbf7c3b28, 0x3231d68b, 0xbe000000], - [0xbcb2041c, 0xbf7d3aac, 0x30f75ae9, 0xbe000000], - [0x3b29b1a9, 0xbf7e1324, 0x32f1e603, 0xbe000000], - [0x3cdd0b28, 0xbf7ec46d, 0xb1f44949, 0xbe000000], - [0xbc354825, 0xbf7f4e6d, 0xb2d01884, 0xbd800000], - [0x3c5c1342, 0xbf7fb10f, 0xb1de5b5f, 0xbd800000], - [0x3bdbd541, 0xbf7fec43, 0xb084cd0d, 0xbd000000], - [0x00000000, 0xbf800000, 0x00000000, 0x00000000], - [0xbbdbd541, 0xbf7fec43, 0xb084cd0d, 0x3d000000], - [0xbc5c1342, 0xbf7fb10f, 0xb1de5b5f, 0x3d800000], - [0x3c354825, 0xbf7f4e6d, 0xb2d01884, 0x3d800000], - [0xbcdd0b28, 0xbf7ec46d, 0xb1f44949, 0x3e000000], - [0xbb29b1a9, 0xbf7e1324, 0x32f1e603, 0x3e000000], - [0x3cb2041c, 0xbf7d3aac, 0x30f75ae9, 0x3e000000], - [0x3d3c4289, 0xbf7c3b28, 0x3231d68b, 0x3e000000], - [0xbd60e8f8, 0xbf7b14be, 0xb2ff75cb, 0x3e800000], - [0xbcfd1f65, 0xbf79c79d, 0xb2c64e59, 0x3e800000], - [0xbbe60685, 0xbf7853f8, 0x320db9e5, 0x3e800000], - [0x3c88e931, 0xbf76ba07, 0xb26d092c, 0x3e800000], - [0x3d25018c, 0xbf74fa0b, 0x32939d22, 0x3e800000], - [0x3d826b93, 0xbf731447, 0xb2c48e11, 0x3e800000], - [0x3db1f34f, 0xbf710908, 0xb21ed0dd, 0x3e800000], - [0xbe0f77ad, 0xbf6ed89e, 0x329333dc, 0x3f000000], - [0xbdf043ab, 0xbf6c835e, 0xb2f328d4, 0x3f000000], - [0xbdc210d8, 0xbf6a09a7, 0x32eb236c, 0x3f000000], - [0xbd945dff, 0xbf676bd8, 0x32bc3389, 0x3f000000], - [0xbd4e645a, 0xbf64aa59, 0xb11a08fa, 0x3f000000], - [0xbcea5164, 0xbf61c598, 0x32e7f425, 0x3f000000], - [0xbbe8b648, 0xbf5ebe05, 0xb2c6f953, 0x3f000000], - [0x3c670f32, 0xbf5b941a, 0xb2232dc8, 0x3f000000], - [0x3d0f59aa, 0xbf584853, 0x327d5fc0, 0x3f000000], - [0x3d639d9d, 0xbf54db31, 0xb290ea1a, 0x3f000000], - [0x3d9b4153, 0xbf514d3d, 0xb00c4f04, 0x3f000000], - [0x3dc3fdff, 0xbf4d9f02, 0xb27e70e8, 0x3f000000], - [0x3debfe8a, 0xbf49d112, 0xb2992640, 0x3f000000], - [0x3e099e65, 0xbf45e403, 0xb2b15174, 0x3f000000], - [0x3e1cd957, 0xbf41d870, 0xb2bff977, 0x3f000000], - [0x3e2fad27, 0xbf3daef9, 0xb19aabec, 0x3f000000], - [0x3e4216eb, 0xbf396842, 0x32810007, 0x3f000000], - [0xbe95f61a, 0xbf3504f3, 0xb24fe77a, 0x3f800000], - [0xbe8d2f7d, 0xbf3085bb, 0x32ae2d32, 0x3f800000], - [0xbe84a20e, 0xbf2beb4a, 0x32b73136, 0x3f800000], - [0xbe789e3f, 0xbf273656, 0x32038343, 0x3f800000], - [0xbe686ff3, 0xbf226799, 0xb22123bb, 0x3f800000], - [0xbe58bbb7, 0xbf1d7fd1, 0xb292050c, 0x3f800000], - [0xbe4983f7, 0xbf187fc0, 0x31c7a3f3, 0x3f800000], - [0xbe3acb0c, 0xbf13682a, 0xb2cdd12e, 0x3f800000], - [0xbe2c933b, 0xbf0e39da, 0x324a32e7, 0x3f800000], - [0xbe1edeb5, 0xbf08f59b, 0x32be4b4e, 0x3f800000], - [0xbe11af97, 0xbf039c3d, 0x325ba002, 0x3f800000], - [0xbe0507ea, 0xbefc5d27, 0x3180eca9, 0x3f800000], - [0xbdf1d344, 0xbef15aea, 0x31ff2139, 0x3f800000], - [0xbddaad38, 0xbee63375, 0x31d9c774, 0x3f800000], - [0xbdc4a143, 0xbedae880, 0xb21e15cc, 0x3f800000], - [0xbdafb2cc, 0xbecf7bca, 0xb16a3b63, 0x3f800000], - [0xbd9be50c, 0xbec3ef15, 0xb1d5d52c, 0x3f800000], - [0xbd893b12, 0xbeb8442a, 0x32705ba6, 0x3f800000], - [0xbd6f6f7e, 0xbeac7cd4, 0x32254e02, 0x3f800000], - [0xbd4ebb8a, 0xbea09ae5, 0x323e89a0, 0x3f800000], - [0xbd305f55, 0xbe94a031, 0xb26d59f0, 0x3f800000], - [0xbd145f8c, 0xbe888e93, 0xb12c7d9e, 0x3f800000], - [0xbcf58104, 0xbe78cfcc, 0x311bd41d, 0x3f800000], - [0xbcc70c54, 0xbe605c13, 0xb1a7e4f6, 0x3f800000], - [0xbc9d6830, 0xbe47c5c2, 0x30e5967d, 0x3f800000], - [0xbc71360b, 0xbe2f10a2, 0xb11167f9, 0x3f800000], - [0xbc315502, 0xbe164083, 0xb1e8e614, 0x3f800000], - [0xbbf66e3c, 0xbdfab273, 0x311568cf, 0x3f800000], - [0xbb9dc971, 0xbdc8bd36, 0x307592f5, 0x3f800000], - [0xbb319298, 0xbd96a905, 0x31531e61, 0x3f800000], - [0xba9de1c8, 0xbd48fb30, 0x30ef227f, 0x3f800000], - [0xb99de7df, 0xbcc90ab0, 0x3005c998, 0x3f800000], -]; diff --git a/github-simulator/source/src/math/managed_trig.rs b/github-simulator/source/src/math/managed_trig.rs deleted file mode 100644 index 3d5b379889f01d4a52f71032124f4cb22faf3996..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/math/managed_trig.rs +++ /dev/null @@ -1,72 +0,0 @@ -//! Scalar transcription of the shipped .NET SSE4.1 large packed-sine lane. -//! Reference coreclr.dll RVAs 16b82b..16bd86; tables at 4de300 and 4def00. -use super::managed_sine_tables as tables; -use std::num::Wrapping as W; -fn f(bits: u32) -> f32 { - f32::from_bits(bits) -} - -pub(crate) fn sine_large(value: f32) -> f32 { - if !value.is_finite() { - return super::native_math::nonfinite_trig_result(value); - } - let bits = value.to_bits(); - let sign = bits & 0x80000000; - let exponent = ((bits >> 23) & 255) as usize; - let m = (bits & 0x007fffff) + 0x00800000; - let [t0, t1, t2] = tables::EXPONENTS[exponent]; - let (l, h) = (W(m & 65535), W(m >> 16)); - let (a, b, c, d, e, g) = ( - W(t2 & 65535), - W(t2 >> 16), - W(t1 & 65535), - W(t1 >> 16), - W(t0 & 65535), - W(t0 >> 16), - ); - let mask = W(65535); - let w = ((l * c) & mask) + h * b + ((l * b) >> 16) + ((h * a) >> 16); - let v = (w >> 16) + ((l * d) & mask) + h * c + ((l * c) >> 16); - let u = (v >> 16) + ((l * e) & mask) + h * d + ((l * d) >> 16); - let q = (u >> 16) + ((l * g) & mask) + h * e + ((l * e) >> 16); - let hi = ((q << 16) + (u & mask)).0; - let lo = ((v << 16) + (w & mask)).0; - let a0 = f((hi >> 9) | (0x3f800000 ^ sign)); - let rounded = a0 + f(0x47400000); - let coarse = rounded - f(0x47400000); - let r0 = a0 - coarse; - let mut r1 = f(((hi & 511) << 14) | (lo >> 18) | (0x34000000 ^ sign)) - f(0x34000000 ^ sign); - let mut r2 = f(((lo & 0x3ffff) << 5) | (0x28800000 ^ sign)) - f(0x28800000 ^ sign); - let r = r0 + r1; - let rh0 = f(r.to_bits() & 0xfffff000); - let rl0 = r - rh0; - r1 += r0 - r; - r2 += r1; - let main = rh0 * f(0x40c91000); - let mut tail = rl0 * f(0x40c91000); - let mut small = r2 * f(0x40c90fdb); - tail += rh0 * f(0xb795777a); - small += rl0 * f(0xb795777a); - tail += small; - let rh = tail + main; - let rl = tail + (main - rh); - let [cd, sh, sl, ch] = tables::TRIG[(rounded.to_bits() & 255) as usize].map(f); - let z = rh * rh; - let cp = f(0x3d2aaa7c) * z + f(0xbf000000); - let mut sp = f(0x3c08885c) * z + f(0xbe2aaaab); - sp *= z; - sp *= rh; - let term = rh * ch; - let s0 = sh + term; - let err0 = (sh - s0) + term; - let term = rh * cd; - let s1 = s0 + term; - let err1 = (s0 - s1) + term; - let mut err = err0 + err1; - let adjusted_cos = (cd + ch) - rh * sh; - err += sp * adjusted_cos; - let mut tail = sl + rl * adjusted_cos; - tail += sh * (z * cp); - tail += err; - s1 + tail -} diff --git a/github-simulator/source/src/math/mod.rs b/github-simulator/source/src/math/mod.rs deleted file mode 100644 index a1542dcb80a70674664563c8d9f211f08dd4d6a5..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/math/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! Runtime-compatible scalar arithmetic, tables, and vectors. - -pub mod double_math; -mod double_math_tables; -mod float_reduction; -pub mod godot_math; -mod managed_sine_tables; -mod managed_trig; -pub mod native_math; -pub mod profile; -pub mod pymath; -pub mod ucrt; -pub mod vec2; diff --git a/github-simulator/source/src/math/native_math.rs b/github-simulator/source/src/math/native_math.rs deleted file mode 100644 index a4513eec59cc1d9454e91380c3c5c126850f6bd0..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/math/native_math.rs +++ /dev/null @@ -1,484 +0,0 @@ -//! Float math used by the game's Proton UCRT, which incorporates musl. -//! -//! atan2f/atanf/sinf/cosf adapted from Wine proton_11.0 libs/musl/src/math. -//! Conversion to float by Ian Lance Taylor, Cygnus Support. -//! Trigonometric kernels optimized by Bruce D. Evans. -//! Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. -//! Developed at SunPro, a Sun Microsystems, Inc. business. -//! Permission to use, copy, modify, and distribute this software is freely -//! granted, provided that this notice is preserved. - -// Preserve the musl polynomial and reduction literals exactly as transcribed. -#![allow(clippy::excessive_precision, clippy::approx_constant)] - -fn sin_kernel(x: f64) -> f32 { - let z = x * x; - let s1 = -0.16666666666666666; - if x > -7.8175831586122513e-3 && x < 7.8175831586122513e-3 { - return (x * (1.0 + s1 * z)) as f32; - } - let w = z * z; - let r = -0.0001984126984126984 + z * 2.7557319223985893e-6; - let s = z * x; - ((x + s * (s1 + z * 0.008333333333333333)) + s * w * r) as f32 -} -fn cos_kernel(x: f64) -> f32 { - let z = x * x; - if x > -7.816314697265625e-3 && x < 7.816314697265625e-3 { - return (1.0 - 0.5 * z) as f32; - } - (1.0 + z - * (-0.5 - + z * (0.041666666666666664 - + z * (-0.001388888888888889 + z * (2.48015873015873e-5 + z * -2.755731922398589e-7))))) as f32 -} -fn reduce(x: f32) -> (i32, f64) { - if (x.to_bits() & 0x7fffffff) >= 0x4dc90fdb { - return crate::math::float_reduction::reduce(x); - } - let toint = 1.5 / f64::EPSILON; - let mut n = (x as f64 * 6.36619772367581382433e-1 + toint) - toint; - let rem = |n| x as f64 - n * 1.57079631090164184570 - n * 1.58932547735281966916e-8; - let mut y = rem(n); - if y < -(std::f32::consts::FRAC_PI_4 as f64) { - n -= 1.0; - y = rem(n); - } else if y > std::f32::consts::FRAC_PI_4 as f64 { - n += 1.0; - y = rem(n); - } - (n as i32, y) -} -// Match x86 invalid-operation results and quiet NaNs without losing their bits. -pub(crate) fn nonfinite_trig_result(value: f32) -> f32 { - let bits = value.to_bits(); - f32::from_bits(if bits & 0x7fff_ffff == 0x7f80_0000 { - 0xffc0_0000 - } else { - bits | 0x0040_0000 - }) -} - -pub fn sin(x: f32) -> f32 { - let bits = x.to_bits() & 0x7fffffff; - let negative = x.is_sign_negative(); - let a = x as f64; - let p = std::f64::consts::FRAC_PI_2; - if bits <= 0x3f490fda { - return if bits < 0x39800000 { x } else { sin_kernel(a) }; - } - if bits <= 0x407b53d1 { - if bits <= 0x4016cbe3 { - return if negative { - -cos_kernel(a + p) - } else { - cos_kernel(a - p) - }; - } - return sin_kernel(if negative { -(a + 2.0 * p) } else { -(a - 2.0 * p) }); - } - if bits <= 0x40e231d5 { - if bits <= 0x40afeddf { - return if negative { - cos_kernel(a + 3.0 * p) - } else { - -cos_kernel(a - 3.0 * p) - }; - } - return sin_kernel(if negative { a + 4.0 * p } else { a - 4.0 * p }); - } - if !x.is_finite() { - return nonfinite_trig_result(x); - } - let (n, y) = reduce(x); - match n & 3 { - 0 => sin_kernel(y), - 1 => cos_kernel(y), - 2 => sin_kernel(-y), - _ => -cos_kernel(y), - } -} -pub fn cos(x: f32) -> f32 { - let bits = x.to_bits() & 0x7fffffff; - let negative = x.is_sign_negative(); - let a = x as f64; - let p = std::f64::consts::FRAC_PI_2; - if bits <= 0x3f490fda { - return if bits < 0x39800000 { 1.0 } else { cos_kernel(a) }; - } - if bits <= 0x407b53d1 { - if bits > 0x4016cbe3 { - return -cos_kernel(if negative { a + 2.0 * p } else { a - 2.0 * p }); - } - return sin_kernel(if negative { a + p } else { p - a }); - } - if bits <= 0x40e231d5 { - if bits > 0x40afeddf { - return cos_kernel(if negative { a + 4.0 * p } else { a - 4.0 * p }); - } - return sin_kernel(if negative { -a - 3.0 * p } else { a - 3.0 * p }); - } - if !x.is_finite() { - return nonfinite_trig_result(x); - } - let (n, y) = reduce(x); - match n & 3 { - 0 => cos_kernel(y), - 1 => sin_kernel(-y), - 2 => -cos_kernel(y), - _ => sin_kernel(y), - } -} - -fn atan(mut x: f32) -> f32 { - let negative = x.is_sign_negative(); - let bits = x.to_bits() & 0x7fff_ffff; - let high = [0x3eed6338, 0x3f490fda, 0x3f7b985e, 0x3fc90fda].map(f32::from_bits); - let low = [0x31ac3769, 0x33222168, 0x33140fb4, 0x33a22168].map(f32::from_bits); - if bits >= 0x4c800000 { - return if x.is_nan() { x } else { high[3].copysign(x) }; - } - let id; - if bits < 0x3ee00000 { - if bits < 0x39800000 { - return x; - } - id = None; - } else { - x = x.abs(); - if bits < 0x3f980000 { - if bits < 0x3f300000 { - id = Some(0); - x = (2.0 * x - 1.0) / (2.0 + x); - } else { - id = Some(1); - x = (x - 1.0) / (x + 1.0); - } - } else if bits < 0x401c0000 { - id = Some(2); - x = (x - 1.5) / (1.0 + 1.5 * x); - } else { - id = Some(3); - x = -1.0 / x; - } - } - let a = [ - 3.3333328366e-01f32, - -1.9999158382e-01, - 1.4253635705e-01, - -1.0648017377e-01, - 6.1687607318e-02, - ]; - let z = x * x; - let w = z * z; - let s1 = z * (a[0] + w * (a[2] + w * a[4])); - let s2 = w * (a[1] + w * a[3]); - match id { - None => x - x * (s1 + s2), - Some(i) => { - let z = high[i] - ((x * (s1 + s2) - low[i]) - x); - if negative { - -z - } else { - z - } - } - } -} - -pub fn atan2(y: f32, x: f32) -> f32 { - let pi = std::f32::consts::PI; - let pi_lo = f32::from_bits(0xb3bbbd2e); - if x.is_nan() || y.is_nan() { - return x + y; - } - let (mut ix, mut iy) = (x.to_bits(), y.to_bits()); - if ix == 0x3f800000 { - return atan(y); - } - let m = ((iy >> 31) & 1) | ((ix >> 30) & 2); - ix &= 0x7fffffff; - iy &= 0x7fffffff; - if iy == 0 { - return match m { - 0 | 1 => y, - 2 => pi, - _ => -pi, - }; - } - if ix == 0 { - return (pi / 2.0).copysign(y); - } - if ix == 0x7f800000 { - return if iy == 0x7f800000 { - match m { - 0 => pi / 4.0, - 1 => -pi / 4.0, - 2 => 3.0 * pi / 4.0, - _ => -3.0 * pi / 4.0, - } - } else { - match m { - 0 => 0.0, - 1 => -0.0, - 2 => pi, - _ => -pi, - } - }; - } - if ix + (26 << 23) < iy || iy == 0x7f800000 { - return (pi / 2.0).copysign(y); - } - let z = if m & 2 != 0 && iy + (26 << 23) < ix { - 0.0 - } else { - atan((y / x).abs()) - }; - match m { - 0 => z, - 1 => -z, - 2 => pi - (z - pi_lo), - _ => (z - pi_lo) - pi, - } -} - -/// The shipped x86 Godot compiler combines adjacent sin/cos calls into an x87 -/// helper. Its internal pi approximation is observable for large arguments. -/// Managed MathF scalar and packed routines above use different algorithms. -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -pub fn engine_sin_cos(x: f32) -> (f32, f32) { - let mut sine = 0.0f32; - let mut cosine = 0.0f32; - // The x87 stack is balanced on both paths. FSINCOS leaves its operand on - // range failure; FPREM1 reduces against the same extended FLDPI constant. - // FSCALE doubles pi without rounding it to the caller's x87 precision. - unsafe { - core::arch::asm!( - "fld dword ptr [{input}]", - "fsincos", - "fnstsw ax", - "test ax, 0x400", - "jz 3f", - "fldpi", - "fld1", - "fxch st(1)", - "fscale", - "fstp st(1)", - "fxch st(1)", - "2:", - "fprem1", - "fnstsw ax", - "test ax, 0x400", - "jnz 2b", - "fstp st(1)", - "fsincos", - "3:", - "fstp dword ptr [{cosine}]", - "fstp dword ptr [{sine}]", - input=in(reg) &x,sine=in(reg) &mut sine,cosine=in(reg) &mut cosine, - out("ax") _,out("st(0)") _,out("st(1)") _,out("st(2)") _,options(nostack), - ); - } - (sine, cosine) -} -#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] -pub fn engine_sin_cos(x: f32) -> (f32, f32) { - engine_sin_cos_portable(x) -} - -/// Inputs up to this magnitude have been compared exhaustively with x87 `FSINCOS`. -#[cfg(all(test, any(target_arch = "x86", target_arch = "x86_64")))] -const ENGINE_PORTABLE_DOMAIN: f32 = 16.0; - -/// `engine_sin_cos` without the x87 instruction set (the WebAssembly library -/// and other non-x86 targets): the fdlibm kernels in f64 after a Cody-Waite -/// reduction, as gpu/sim/math.h `engine_sin_cos_raw`. The float results -/// equal x87 `FSINCOS` for every float with |x| <= `ENGINE_PORTABLE_DOMAIN` -/// (examples/gpu_engine_scan.rs found no exception, and -/// `portable_engine_sin_cos_matches_x87` in this module rechecks the domain -/// natively). Larger finite arguments use the same reduction, whose products -/// stay exact for |x| below 2^20; those results have not been compared. -/// FSINCOS leaves NaNs quieted and turns infinities into the x87 indefinite. -// The literals are the fdlibm constants as written (2/pi is the rounded value). -#[allow(clippy::approx_constant, clippy::excessive_precision)] -#[cfg(any(test, not(any(target_arch = "x86", target_arch = "x86_64"))))] -pub(crate) fn engine_sin_cos_portable(xf: f32) -> (f32, f32) { - if xf.is_nan() { - let quiet = f32::from_bits(xf.to_bits() | 0x0040_0000); - return (quiet, quiet); - } - if xf.is_infinite() { - let indefinite = f32::from_bits(0xffc0_0000); - return (indefinite, indefinite); - } - let x = xf as f64; - let n = (x * 6.36619772367581382433e-01).round_ties_even(); - // Keeps -0 (and every tiny argument) unchanged when no reduction happens. - let y = if n == 0.0 { - x - } else { - (x - n * 1.57079632673412561417e+00) - n * 6.07710050650619224932e-11 - }; - let z = y * y; - let w = z * z; - let r = 8.33333333332248946124e-03 - + z * (-1.98412698298579493134e-04 + z * 2.75573137070700676789e-06) - + z * w * (-2.50507602534068634195e-08 + z * 1.58969099521155010221e-10); - // fdlibm returns tiny arguments unchanged, which also keeps the sign of -0. - let sy = if y.abs() < f64::from_bits(0x3e40_0000_0000_0000) { - y - } else { - y + z * y * (-1.66666666666666324348e-01 + z * r) - }; - let rc = z * (4.16666666666666019037e-02 + z * (-1.38888888888741095749e-03 + z * 2.48015872894767294178e-05)) - + w * w * (-2.75573143513906633035e-07 + z * (2.08757232129817482790e-09 + z * -1.13596475577881948265e-11)); - let hz = 0.5 * z; - let one_minus = 1.0 - hz; - let cy = one_minus + (((1.0 - one_minus) - hz) + z * rc); - let q = (n as i64) & 3; - let (mut sv, mut cv) = if q & 1 != 0 { (cy, sy) } else { (sy, cy) }; - if q & 2 != 0 { - sv = -sv; - } - if (q + 1) & 2 != 0 { - cv = -cv; - } - (sv as f32, cv as f32) -} - -pub fn engine_sin(x: f32) -> f32 { - engine_sin_cos(x).0 -} -pub fn engine_cos(x: f32) -> f32 { - engine_sin_cos(x).1 -} - -/// Comparisons with x87 `FSINCOS`, which only x86 provides. -#[cfg(all(test, any(target_arch = "x86", target_arch = "x86_64")))] -mod engine_tests { - use super::*; - - #[test] - fn large_engine_arguments_match_the_capture_at_every_x87_precision() { - fn control_word() -> u16 { - let mut word = 0u16; - unsafe { - core::arch::asm!("fnstcw word ptr [{word}]", word = in(reg) &mut word, options(nostack)); - } - word - } - struct RestoreControlWord(u16); - impl Drop for RestoreControlWord { - fn drop(&mut self) { - unsafe { - core::arch::asm!("fldcw word ptr [{word}]", word = in(reg) &self.0, options(nostack)); - } - } - } - - let original = control_word(); - let _restore = RestoreControlWord(original); - // Captured results at the FSINCOS range boundary and the largest float. - let rows = [ - (0x5eff_ffff, 0x3e54_4316, 0xbf7a_7091), - (0x5f00_0000, 0x3f7c_c47f, 0x3e22_3675), - (0x7f7f_ffff, 0x3f7a_128f, 0xbe5b_1472), - ]; - for precision in [0x0000, 0x0200, 0x0300] { - let word = (original & !0x0300) | precision; - unsafe { - core::arch::asm!("fldcw word ptr [{word}]", word = in(reg) &word, options(nostack)); - } - for (input, sine, cosine) in rows { - let actual = engine_sin_cos(f32::from_bits(input)); - assert_eq!( - (actual.0.to_bits(), actual.1.to_bits()), - (sine, cosine), - "input {input:08x}, x87 control word {word:04x}" - ); - assert_eq!( - control_word(), - word, - "engine math changed the caller's x87 control word" - ); - } - } - } - - fn check(bits: u32) -> Option<(u32, u32, u32, u32, u32)> { - let x = f32::from_bits(bits); - let (s, c) = engine_sin_cos(x); - let (ps, pc) = engine_sin_cos_portable(x); - (s.to_bits() != ps.to_bits() || c.to_bits() != pc.to_bits()).then_some(( - bits, - s.to_bits(), - c.to_bits(), - ps.to_bits(), - pc.to_bits(), - )) - } - - /// Every 61st float with |x| <= 16 plus the domain edges and specials, - /// against the x87 instruction (`cargo test -- --ignored` runs the whole domain). - #[test] - fn portable_engine_sin_cos_matches_x87() { - let limit = ENGINE_PORTABLE_DOMAIN.to_bits(); - let mut mismatches = Vec::new(); - for sign in [0u32, 0x8000_0000] { - for bits in (0..=limit).step_by(61).chain([ - limit, - limit - 1, - 0x3fc9_0fdb, - 0x4049_0fdb, - 0x40c9_0fdb, - 0x3f49_0fdb, - 1, - 0, - ]) { - mismatches.extend(check(sign | bits)); - if mismatches.len() > 8 { - break; - } - } - } - assert!( - mismatches.is_empty(), - "portable engine trig differs from x87: {mismatches:x?}" - ); - for bits in [0x7fc0_0000u32, 0xffc0_0000, 0x7f80_0000, 0xff80_0000, 0x7f80_0001] { - let (s, c) = engine_sin_cos(f32::from_bits(bits)); - let (ps, pc) = engine_sin_cos_portable(f32::from_bits(bits)); - assert!(s.is_nan() && c.is_nan() && ps.is_nan() && pc.is_nan()); - } - } - - #[test] - #[ignore = "exhaustive: about two billion evaluations"] - fn portable_engine_sin_cos_matches_x87_exhaustively() { - let limit = ENGINE_PORTABLE_DOMAIN.to_bits(); - let threads = std::thread::available_parallelism().map_or(4, |n| n.get()); - let per = (limit as usize + 1).div_ceil(threads) as u32; - let mismatches: Vec<_> = std::thread::scope(|scope| { - let handles: Vec<_> = (0..threads as u32) - .map(|t| { - scope.spawn(move || { - let mut found = Vec::new(); - for sign in [0u32, 0x8000_0000] { - for bits in (t * per)..((t + 1) * per).min(limit + 1) { - found.extend(check(sign | bits)); - } - } - found - }) - }) - .collect(); - handles.into_iter().flat_map(|h| h.join().unwrap()).collect() - }); - assert!( - mismatches.is_empty(), - "{} mismatches, first {:x?}", - mismatches.len(), - &mismatches[..mismatches.len().min(8)] - ); - } -} diff --git a/github-simulator/source/src/math/profile.rs b/github-simulator/source/src/math/profile.rs deleted file mode 100644 index 34947cf8be1b4a1d6ceb71e173671bb10ebab14b..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/math/profile.rs +++ /dev/null @@ -1,274 +0,0 @@ -//! The C runtime math the game sees, which depends on where it runs. -//! -//! Under Proton the game's ucrtbase is Wine's, which forwards to musl -//! (native_math.rs, double_math.rs). On Windows it is Microsoft's, whose FMA3 -//! code paths changed in Windows 11 24H2 (build 26100). The Windows functions -//! are generated from math/kernels/ucrt.h (ucrt.rs); sinf and cosf are musl's -//! with a table of the inputs where Microsoft's result differs. -use crate::math::{double_math, native_math, ucrt}; - -/// Which C runtime math a session reproduces. The value is part of a run: a -/// network trained under one profile can drive differently under another. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "kebab-case")] -pub enum MathProfile { - /// Linux and macOS through Proton: Wine's ucrtbase, which is musl. - #[default] - Proton, - /// Windows 10, and Windows 11 up to 23H2 (build 22631), on FMA3 hardware. - Win10Fma3, - /// Windows 11 24H2 (build 26100) and later on FMA3 hardware. - Win11Fma3, -} - -impl MathProfile { - pub const ALL: [MathProfile; 3] = [MathProfile::Proton, MathProfile::Win10Fma3, MathProfile::Win11Fma3]; - - /// The name of the profile in options, checkpoints and on the command line. - pub fn name(self) -> &'static str { - match self { - MathProfile::Proton => "proton", - MathProfile::Win10Fma3 => "win10-fma3", - MathProfile::Win11Fma3 => "win11-fma3", - } - } - - pub fn parse(name: &str) -> Option { - MathProfile::ALL.into_iter().find(|p| p.name() == name) - } - - /// The number the GPU simulators read (World::math_profile, the WGSL shader). - pub fn index(self) -> u32 { - self as u32 - } - - /// The profile of a Windows build number. - pub fn for_windows_build(build: u32) -> MathProfile { - if build >= 26100 { - MathProfile::Win11Fma3 - } else { - MathProfile::Win10Fma3 - } - } - - /// The profile the game would use on this machine: Windows by its build, - /// everything else Proton. In a browser, the platform's - /// (wasm::math_profile). - pub fn detect() -> MathProfile { - #[cfg(all(target_arch = "wasm32", feature = "wasm"))] - return crate::wasm::math_profile::browser_math_profile(); - #[cfg(windows)] - if let Some(build) = windows_build() { - return MathProfile::for_windows_build(build); - } - #[cfg(not(all(target_arch = "wasm32", feature = "wasm")))] - MathProfile::Proton - } - - pub fn sin(self, x: f32) -> f32 { - let musl = native_math::sin(x); - match self { - MathProfile::Proton => musl, - MathProfile::Win10Fma3 | MathProfile::Win11Fma3 => ucrt::win_sinf_fix_all(x, musl), - } - } - - pub fn cos(self, x: f32) -> f32 { - let musl = native_math::cos(x); - match self { - MathProfile::Proton => musl, - MathProfile::Win10Fma3 | MathProfile::Win11Fma3 => ucrt::win_cosf_fix_all(x, musl), - } - } - - pub fn atan2(self, y: f32, x: f32) -> f32 { - match self { - MathProfile::Proton => native_math::atan2(y, x), - MathProfile::Win10Fma3 | MathProfile::Win11Fma3 => ucrt::win_atan2f(y, x), - } - } - - pub fn exp(self, x: f64) -> f64 { - match self { - MathProfile::Proton => double_math::exp(x), - MathProfile::Win10Fma3 => ucrt::win10_exp(x), - MathProfile::Win11Fma3 => ucrt::win11_exp(x), - } - } - - pub fn pow(self, x: f64, y: f64) -> f64 { - match self { - MathProfile::Proton => double_math::pow(x, y), - MathProfile::Win10Fma3 => ucrt::win10_pow(x, y), - MathProfile::Win11Fma3 => ucrt::win11_pow(x, y), - } - } - - pub fn log(self, x: f64) -> f64 { - match self { - MathProfile::Proton => double_math::log(x), - MathProfile::Win10Fma3 | MathProfile::Win11Fma3 => ucrt::win_log(x), - } - } - - /// .NET Math.Tanh, which calls the C runtime's tanh. - pub fn tanh(self, x: f64) -> f64 { - match self { - MathProfile::Proton => crate::nn::network::game_tanh(x), - MathProfile::Win10Fma3 => ucrt::win10_tanh(x), - MathProfile::Win11Fma3 => ucrt::win11_tanh(x), - } - } -} - -impl std::fmt::Display for MathProfile { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(self.name()) - } -} - -impl std::str::FromStr for MathProfile { - type Err = String; - - fn from_str(name: &str) -> Result { - MathProfile::parse(name).ok_or_else(|| { - let names: Vec<_> = MathProfile::ALL.iter().map(|p| p.name()).collect(); - format!("unknown math profile {name:?} (expected {})", names.join(", ")) - }) - } -} - -/// The real build number: GetVersionEx reports 9200 to unmanifested programs. -#[cfg(windows)] -fn windows_build() -> Option { - use windows_sys::Wdk::System::SystemServices::RtlGetVersion; - use windows_sys::Win32::System::SystemInformation::OSVERSIONINFOW; - let mut info = OSVERSIONINFOW { - dwOSVersionInfoSize: std::mem::size_of::() as u32, - ..Default::default() - }; - // SAFETY: info is a writable OSVERSIONINFOW whose size field is set. - let status = unsafe { RtlGetVersion(&mut info) }; - (status == 0).then_some(info.dwBuildNumber) -} - -#[cfg(test)] -mod tests { - use super::*; - - /// math/fixtures/.bin rows: input words, then proton, win10-fma3 and - /// win11-fma3 result bits (tools/ucrt/fixtures.py). - fn rows(name: &str, arity: usize) -> Vec> { - let path = format!("{}/math/fixtures/{name}.bin", env!("CARGO_MANIFEST_DIR")); - let bytes = std::fs::read(&path).unwrap_or_else(|e| panic!("{path}: {e}")); - let words: Vec = bytes - .chunks_exact(W) - .map(|c| c.iter().rev().fold(0u64, |a, &b| (a << 8) | b as u64)) - .collect(); - let rows: Vec> = words.chunks_exact(arity + 3).map(<[u64]>::to_vec).collect(); - assert!(rows.len() > 100, "{path}"); - rows - } - - fn check(name: &str, arity: usize, width: usize, f: impl Fn(MathProfile, &[u64]) -> u64) { - let rows = if width == 4 { - rows::<4>(name, arity) - } else { - rows::<8>(name, arity) - }; - for (column, profile) in MathProfile::ALL.into_iter().enumerate() { - let bad: Vec<_> = rows - .iter() - .filter(|row| f(profile, &row[..arity]) != row[arity + column]) - .map(|row| { - format!( - "{:x?} = {:#x}, want {:#x}", - &row[..arity], - f(profile, &row[..arity]), - row[arity + column] - ) - }) - .collect(); - assert!( - bad.is_empty(), - "{name} {profile}: {} of {} rows differ, e.g. {:?}", - bad.len(), - rows.len(), - &bad[..bad.len().min(4)] - ); - } - } - - fn f32_of(bits: u64) -> f32 { - f32::from_bits(bits as u32) - } - - #[test] - fn sin_matches_fixtures() { - check("sinf", 1, 4, |p, x| p.sin(f32_of(x[0])).to_bits() as u64); - } - - #[test] - fn cos_matches_fixtures() { - check("cosf", 1, 4, |p, x| p.cos(f32_of(x[0])).to_bits() as u64); - } - - #[test] - fn atan2_matches_fixtures() { - check("atan2f", 2, 4, |p, x| { - p.atan2(f32_of(x[0]), f32_of(x[1])).to_bits() as u64 - }); - } - - #[test] - fn exp_matches_fixtures() { - check("exp", 1, 8, |p, x| p.exp(f64::from_bits(x[0])).to_bits()); - } - - #[test] - fn log_matches_fixtures() { - check("log", 1, 8, |p, x| p.log(f64::from_bits(x[0])).to_bits()); - } - - #[test] - fn tanh_matches_fixtures() { - check("tanh", 1, 8, |p, x| p.tanh(f64::from_bits(x[0])).to_bits()); - } - - #[test] - fn pow_matches_fixtures() { - check("pow", 2, 8, |p, x| { - p.pow(f64::from_bits(x[0]), f64::from_bits(x[1])).to_bits() - }); - } - - #[test] - fn names_round_trip() { - for p in MathProfile::ALL { - assert_eq!(p.name().parse::(), Ok(p)); - assert_eq!(serde_json::to_value(p).unwrap(), serde_json::json!(p.name())); - } - assert_eq!(MathProfile::for_windows_build(22631), MathProfile::Win10Fma3); - assert_eq!(MathProfile::for_windows_build(26100), MathProfile::Win11Fma3); - } - - /// Every float through sinf and cosf against the scans' tables is too slow - /// for every run: cargo test --release -- --ignored exhaustive - #[test] - #[ignore] - fn exhaustive_sin_cos_tables_are_sorted_and_signed() { - let mut fixed = 0u64; - for bits in 0..=u32::MAX { - let x = f32::from_bits(bits); - for p in [MathProfile::Win10Fma3, MathProfile::Win11Fma3] { - fixed += (p.sin(x).to_bits() != native_math::sin(x).to_bits()) as u64; - fixed += (p.cos(x).to_bits() != native_math::cos(x).to_bits()) as u64; - } - } - // Both profiles, both signs: the four tables' rows. - let keys = 2 - * 2 - * (ucrt::UCRT_SINF_COUNT + ucrt::UCRT_SINF_WIDE_COUNT + ucrt::UCRT_COSF_COUNT + ucrt::UCRT_COSF_WIDE_COUNT); - assert_eq!(fixed, keys as u64); - } -} diff --git a/github-simulator/source/src/math/pymath.rs b/github-simulator/source/src/math/pymath.rs deleted file mode 100644 index db0104b01c2d48dd19a5d6db496a4b52b3fa3967..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/math/pymath.rs +++ /dev/null @@ -1,229 +0,0 @@ -//! Arithmetic that reproduces CPython's float semantics bit for bit. -//! -//! The reference arithmetic follows CPython, so each helper here -//! mirrors a specific CPython behaviour: `math.hypot` has its own correctly -//! rounded algorithm, `x ** 2` calls libm `pow` (not `x * x`), `sum()` uses -//! Neumaier compensation since 3.12, `%` is floored, and `min`/`max` keep the -//! first argument on ties. - -use std::hint::black_box; - -extern "C" { - fn pow(x: f64, y: f64) -> f64; - fn remainder(x: f64, y: f64) -> f64; -} - -/// `math.remainder(x, y)`; the IEEE remainder is exact, so libm matches CPython. -#[inline] -pub fn py_remainder(x: f64, y: f64) -> f64 { - unsafe { remainder(x, y) } -} - -/// Round to float32 and back, like `ctypes.c_float(value).value`. -#[inline(always)] -pub(crate) fn f32r(x: f64) -> f64 { - x as f32 as f64 -} - -/// Python `x ** y` for floats. `black_box` stops LLVM from turning `pow(x, 2)` -/// into `x * x`, which differs from glibc `pow` in the last bit. -#[inline] -pub fn py_pow(x: f64, y: f64) -> f64 { - unsafe { pow(x, black_box(y)) } -} - -/// Python `max(a, b)`: the first argument wins unless `b > a`. -#[inline(always)] -pub fn py_max(a: f64, b: f64) -> f64 { - if b > a { - b - } else { - a - } -} - -/// Python `min(a, b)`: the first argument wins unless `b < a`. -#[inline(always)] -pub fn py_min(a: f64, b: f64) -> f64 { - if b < a { - b - } else { - a - } -} - -/// Clamp as `min(high, max(low, value))`, preserving comparison order. -#[inline(always)] -pub(crate) fn clamp(value: f64, low: f64, high: f64) -> f64 { - py_min(high, py_max(low, value)) -} - -/// Python float `a % b` (CPython `float_rem`). -#[inline] -pub(crate) fn py_mod(a: f64, b: f64) -> f64 { - let m = a % b; - if m != 0.0 { - if (b < 0.0) != (m < 0.0) { - m + b - } else { - m - } - } else { - 0.0f64.copysign(b) - } -} - -/// Python `sum(iterable)` over floats: Neumaier compensated summation. -#[inline] -pub fn py_sum>(values: I) -> f64 { - let mut sum = NeumaierSum::default(); - for value in values { - sum.add(value); - } - sum.value() -} - -#[derive(Clone, Copy, Default)] -pub(crate) struct NeumaierSum { - total: f64, - compensation: f64, -} - -impl NeumaierSum { - #[inline(always)] - pub(crate) fn add(&mut self, x: f64) { - let t = self.total + x; - if self.total.abs() >= x.abs() { - self.compensation += (self.total - t) + x; - } else { - self.compensation += (x - t) + self.total; - } - self.total = t; - } - - #[inline(always)] - pub(crate) fn value(self) -> f64 { - if self.compensation != 0.0 && self.compensation.is_finite() { - self.total + self.compensation - } else { - self.total - } - } -} - -#[inline(always)] -fn dl_mul(x: f64, y: f64) -> (f64, f64) { - let z = x * y; - (z, x.mul_add(y, -z)) -} - -#[inline(always)] -fn dl_fast_sum(a: f64, b: f64) -> (f64, f64) { - let x = a + b; - (x, (a - x) + b) -} - -/// Exponent returned by C `frexp`. -fn frexp_exponent(x: f64) -> i32 { - let bits = x.to_bits(); - let biased = ((bits >> 52) & 0x7ff) as i32; - if biased == 0 { - frexp_exponent(x * f64::from_bits(0x4350_0000_0000_0000)) - 54 // 2**54 - } else { - biased - 1022 - } -} - -/// C `ldexp(1.0, exponent)` for the range `vector_norm` needs. -fn power_of_two(exponent: i32) -> f64 { - if exponent >= -1022 { - f64::from_bits(((exponent + 1023) as u64) << 52) - } else { - f64::from_bits(1u64 << (exponent + 1074)) - } -} - -fn vector_norm2(a: f64, b: f64, max: f64, found_nan: bool) -> f64 { - if max.is_infinite() { - return max; - } - if found_nan { - return f64::NAN; - } - if max == 0.0 { - return max; - } - let max_e = frexp_exponent(max); - if max_e < -1023 { - let m = f64::MIN_POSITIVE; - return m * vector_norm2(a / m, b / m, max / m, found_nan); - } - let scale = power_of_two(-max_e); - let (mut csum, mut frac1, mut frac2) = (1.0f64, 0.0f64, 0.0f64); - for x in [a, b] { - let x = x * scale; - let (hi, lo) = dl_mul(x, x); - let (sum_hi, sum_lo) = dl_fast_sum(csum, hi); - csum = sum_hi; - frac1 += lo; - frac2 += sum_lo; - } - let mut h = (csum - 1.0 + (frac1 + frac2)).sqrt(); - let (hi, lo) = dl_mul(-h, h); - let (sum_hi, sum_lo) = dl_fast_sum(csum, hi); - csum = sum_hi; - frac1 += lo; - frac2 += sum_lo; - let x = csum - 1.0 + (frac1 + frac2); - h += x / (2.0 * h); - h / scale -} - -/// CPython `math.hypot(x, y)` (`vector_norm` in Modules/mathmodule.c). -#[inline] -pub(crate) fn hypot(x: f64, y: f64) -> f64 { - let (ax, ay) = (x.abs(), y.abs()); - let found_nan = ax.is_nan() || ay.is_nan(); - let mut max = 0.0; - if ax > max { - max = ax; - } - if ay > max { - max = ay; - } - vector_norm2(ax, ay, max, found_nan) -} - -/// Python `math.floor(x)` for track tile coordinates. -#[inline(always)] -pub(crate) fn floor_i64(x: f64) -> i64 { - x.floor() as i64 -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn floored_modulo_matches_python() { - assert_eq!(py_mod(-1.0, 5.0), 4.0); - assert_eq!(py_mod(1.0, -5.0), -4.0); - assert_eq!(py_mod(7.5, 5.0), 2.5); - assert!(py_mod(0.0, -5.0).is_sign_negative()); - } - - #[test] - fn hypot_basic_values() { - assert_eq!(hypot(3.0, 4.0), 5.0); - assert_eq!(hypot(0.0, 0.0), 0.0); - assert_eq!(hypot(-3.0, 0.0), 3.0); - assert_eq!(hypot(f64::INFINITY, f64::NAN), f64::INFINITY); - assert!(hypot(1e-310, 1e-310) > 0.0); - } - - #[test] - fn compensated_sum_recovers_cancelled_terms() { - assert_eq!(py_sum([1e16, 1.0, -1e16]), 1.0); - assert_eq!(py_sum([0.1; 10]), 1.0); - } -} diff --git a/github-simulator/source/src/math/ucrt.rs b/github-simulator/source/src/math/ucrt.rs deleted file mode 100644 index 37ce6ae0234144093b0f89b7c76ead417cec9251..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/math/ucrt.rs +++ /dev/null @@ -1,3325 +0,0 @@ -// Generated from math/kernels/ucrt.h by tools/ucrt/gen-math.py. Do not edit. -//! The float math of the Windows UCRT (FMA3 code paths), bit for bit: see math/kernels/ucrt.h. -#![allow(clippy::all, clippy::pedantic)] - -#[rustfmt::skip] -pub static UCRT_ATAN2F_ATAN256: [u64; 241] = [ - 0x3faff55bb72cfde9u64, 0x3fb0f99ea71d52a6u64, 0x3fb1f86dbf082d58u64, 0x3fb2f719318a4a9au64, - 0x3fb3f59f0e7c559du64, 0x3fb4f3fd677292fbu64, 0x3fb5f2324fd2d7b2u64, 0x3fb6f03bdcea4b0cu64, - 0x3fb7ee182602f10eu64, 0x3fb8ebc54478fb28u64, 0x3fb9e94153cfdcf1u64, 0x3fbae68a71c722b8u64, - 0x3fbbe39ebe6f07c3u64, 0x3fbce07c5c3cca32u64, 0x3fbddd21701eba6eu64, 0x3fbed98c2190043au64, - 0x3fbfd5ba9aac2f6du64, 0x3fc068d584212b3du64, 0x3fc0e6adccf40881u64, 0x3fc1646541060850u64, - 0x3fc1e1fafb043726u64, 0x3fc25f6e171a535cu64, 0x3fc2dcbdb2fba1ffu64, 0x3fc359e8edeb99a3u64, - 0x3fc3d6eee8c6626cu64, 0x3fc453cec6092a9eu64, 0x3fc4d087a9da4f17u64, 0x3fc54d18ba11570au64, - 0x3fc5c9811e3ec269u64, 0x3fc645bfffb3aa73u64, 0x3fc6c1d4898933d8u64, 0x3fc73dbde8a7d201u64, - 0x3fc7b97b4bce5b02u64, 0x3fc8350be398ebc7u64, 0x3fc8b06ee2879c28u64, 0x3fc92ba37d050271u64, - 0x3fc9a6a8e96c8626u64, 0x3fca217e601081a5u64, 0x3fca9c231b403279u64, 0x3fcb1696574d780bu64, - 0x3fcb90d7529260a2u64, 0x3fcc0ae54d768466u64, 0x3fcc84bf8a742e6du64, 0x3fccfe654e1d5395u64, - 0x3fcd77d5df205736u64, 0x3fcdf110864c9d9du64, 0x3fce6a148e96ec4du64, 0x3fcee2e1451d980cu64, - 0x3fcf5b75f92c80ddu64, 0x3fcfd3d1fc40dbe4u64, 0x3fd025fa510665b5u64, 0x3fd061eea03d6290u64, - 0x3fd09dc597d86362u64, 0x3fd0d97ee509acb3u64, 0x3fd1151a362431c9u64, 0x3fd150973a9ce546u64, - 0x3fd18bf5a30bf178u64, 0x3fd1c735212dd883u64, 0x3fd2025567e47c95u64, 0x3fd23d562b381041u64, - 0x3fd278372057ef45u64, 0x3fd2b2f7fd9b5fe2u64, 0x3fd2ed987a823cfeu64, 0x3fd328184fb58951u64, - 0x3fd362773707ebcbu64, 0x3fd39cb4eb76157bu64, 0x3fd3d6d129271134u64, 0x3fd410cbad6c7d32u64, - 0x3fd44aa436c2af09u64, 0x3fd4845a84d0c21bu64, 0x3fd4bdee586890e6u64, 0x3fd4f75f73869978u64, - 0x3fd530ad9951cd49u64, 0x3fd569d88e1b4cd7u64, 0x3fd5a2e0175e0f4eu64, 0x3fd5dbc3fbbe768du64, - 0x3fd614840309cfe1u64, 0x3fd64d1ff635c1c5u64, 0x3fd685979f5fa6fdu64, 0x3fd6bdeac9cbd76cu64, - 0x3fd6f61941e4def0u64, 0x3fd72e22d53aa2a9u64, 0x3fd7660752817501u64, 0x3fd79dc6899118d1u64, - 0x3fd7d5604b63b3f7u64, 0x3fd80cd46a14b1d0u64, 0x3fd84422b8df95d7u64, 0x3fd87b4b0c1ebedbu64, - 0x3fd8b24d394a1b25u64, 0x3fd8e92916f5cde8u64, 0x3fd91fde7cd0c662u64, 0x3fd9566d43a34907u64, - 0x3fd98cd5454d6b18u64, 0x3fd9c3165cc58107u64, 0x3fd9f93066168001u64, 0x3fda2f233e5e530bu64, - 0x3fda64eec3cc23fcu64, 0x3fda9a92d59e98cfu64, 0x3fdad00f5422058bu64, 0x3fdb056420ae9343u64, - 0x3fdb3a911da65c6cu64, 0x3fdb6f962e737efbu64, 0x3fdba473378624a5u64, 0x3fdbd9281e528191u64, - 0x3fdc0db4c94ec9efu64, 0x3fdc42191ff11eb6u64, 0x3fdc76550aad71f8u64, 0x3fdcaa6872f3631bu64, - 0x3fdcde53432c1350u64, 0x3fdd121566b7f2adu64, 0x3fdd45aec9ec862bu64, 0x3fdd791f5a1226f4u64, - 0x3fddac670561bb4fu64, 0x3fdddf85bb026974u64, 0x3fde127b6b0744afu64, 0x3fde4548066cf51au64, - 0x3fde77eb7f175a34u64, 0x3fdeaa65c7cf28c4u64, 0x3fdedcb6d43f8434u64, 0x3fdf0ede98f393cfu64, - 0x3fdf40dd0b541417u64, 0x3fdf72b221a4e495u64, 0x3fdfa45dd3029258u64, 0x3fdfd5e0175fdf83u64, - 0x3fe0039c73c1a40bu64, 0x3fe01c341e82422du64, 0x3fe034b709250488u64, 0x3fe04d25314342e5u64, - 0x3fe0657e94db30cfu64, 0x3fe07dc3324e9b38u64, 0x3fe095f30861a58fu64, 0x3fe0ae0e1639866cu64, - 0x3fe0c6145b5b43dau64, 0x3fe0de05d7aa6f7cu64, 0x3fe0f5e28b67e295u64, 0x3fe10daa77307a0du64, - 0x3fe1255d9bfbd2a8u64, 0x3fe13cfbfb1b056eu64, 0x3fe1548596376469u64, 0x3fe16bfa6f5137e1u64, - 0x3fe1835a88be7c13u64, 0x3fe19aa5e5299f99u64, 0x3fe1b1dc87904284u64, 0x3fe1c8fe7341f64fu64, - 0x3fe1e00babdefeb3u64, 0x3fe1f7043557138au64, 0x3fe20de813e823b1u64, 0x3fe224b74c1d192au64, - 0x3fe23b71e2cc9e6au64, 0x3fe25217dd17e501u64, 0x3fe268a940696da6u64, 0x3fe27f261273d1b3u64, - 0x3fe2958e59308e30u64, 0x3fe2abe21aded073u64, 0x3fe2c2215e024465u64, 0x3fe2d84c2961e48bu64, - 0x3fe2ee628406cbcau64, 0x3fe30464753b090au64, 0x3fe31a52048874beu64, 0x3fe3302b39b78856u64, - 0x3fe345f01cce37bbu64, 0x3fe35ba0b60eccceu64, 0x3fe3713d0df6c503u64, 0x3fe386c52d3db11eu64, - 0x3fe39c391cd41719u64, 0x3fe3b198e5e2564au64, 0x3fe3c6e491c78dc4u64, 0x3fe3dc1c2a188504u64, - 0x3fe3f13fb89e96f4u64, 0x3fe4064f47569f48u64, 0x3fe41b4ae06fea41u64, 0x3fe430328e4b26d5u64, - 0x3fe445065b795b55u64, 0x3fe459c652badc7fu64, 0x3fe46e727efe4715u64, 0x3fe4830aeb5f7bfdu64, - 0x3fe4978fa3269ee1u64, 0x3fe4ac00b1c71762u64, 0x3fe4c05e22de94e4u64, 0x3fe4d4a8023414e8u64, - 0x3fe4e8de5bb6ec04u64, 0x3fe4fd013b7dd17eu64, 0x3fe51110adc5ed81u64, 0x3fe5250cbef1e9fau64, - 0x3fe538f57b89061eu64, 0x3fe54ccaf0362c8fu64, 0x3fe5608d29c70c34u64, 0x3fe5743c352b33b9u64, - 0x3fe587d81f732fbau64, 0x3fe59b60f5cfab9du64, 0x3fe5aed6c5909517u64, 0x3fe5c2399c244260u64, - 0x3fe5d58987169b18u64, 0x3fe5e8c6941043cfu64, 0x3fe5fbf0d0d5cc49u64, 0x3fe60f084b46e05eu64, - 0x3fe6220d115d7b8du64, 0x3fe634ff312d1f3bu64, 0x3fe647deb8e20b8fu64, 0x3fe65aabb6c07b02u64, - 0x3fe66d663923e086u64, 0x3fe6800e4e7e2857u64, 0x3fe692a40556fb6au64, 0x3fe6a5276c4b0575u64, - 0x3fe6b798920b3d98u64, 0x3fe6c9f7855c3198u64, 0x3fe6dc44551553aeu64, 0x3fe6ee7f10204aefu64, - 0x3fe700a7c5784633u64, 0x3fe712be84295198u64, 0x3fe724c35b4fae7bu64, 0x3fe736b65a172dffu64, - 0x3fe748978fba8e0fu64, 0x3fe75a670b82d8d8u64, 0x3fe76c24dcc6c6c0u64, 0x3fe77dd112ea22c7u64, - 0x3fe78f6bbd5d315eu64, 0x3fe7a0f4eb9c19a2u64, 0x3fe7b26cad2e50fdu64, 0x3fe7c3d311a6092bu64, - 0x3fe7d528289fa093u64, 0x3fe7e66c01c114fdu64, 0x3fe7f79eacb97898u64, 0x3fe808c03940694au64, - 0x3fe819d0b7158a4cu64, 0x3fe82ad036000005u64, 0x3fe83bbec5cdee22u64, 0x3fe84c9c7653f7eau64, - 0x3fe85d69576cc2c5u64, 0x3fe86e2578f87ae5u64, 0x3fe87ed0eadc5a2au64, 0x3fe88f6bbd023118u64, - 0x3fe89ff5ff57f1f7u64, 0x3fe8b06fc1cf3dfeu64, 0x3fe8c0d9145cf49du64, 0x3fe8d13206f8c4cau64, - 0x3fe8e17aa99cc05du64, 0x3fe8f1b30c44f167u64, 0x3fe901db3eeef187u64, 0x3fe911f35199833bu64, - 0x3fe921fb54442d18u64, -]; -#[rustfmt::skip] -pub static UCRT_TANH_HI: [u64; 32] = [ - 0x3ff0000000000000u64, 0x3ff059b0d0000000u64, 0x3ff0b55860000000u64, 0x3ff11301d0000000u64, - 0x3ff172b830000000u64, 0x3ff1d48730000000u64, 0x3ff2387a60000000u64, 0x3ff29e9df0000000u64, - 0x3ff306fe00000000u64, 0x3ff371a730000000u64, 0x3ff3dea640000000u64, 0x3ff44e0860000000u64, - 0x3ff4bfdad0000000u64, 0x3ff5342b50000000u64, 0x3ff5ab07d0000000u64, 0x3ff6247eb0000000u64, - 0x3ff6a09e60000000u64, 0x3ff71f75e0000000u64, 0x3ff7a11470000000u64, 0x3ff8258990000000u64, - 0x3ff8ace540000000u64, 0x3ff93737b0000000u64, 0x3ff9c49180000000u64, 0x3ffa5503b0000000u64, - 0x3ffae89f90000000u64, 0x3ffb7f76f0000000u64, 0x3ffc199bd0000000u64, 0x3ffcb720d0000000u64, - 0x3ffd5818d0000000u64, 0x3ffdfc9730000000u64, 0x3ffea4afa0000000u64, 0x3fff507650000000u64, -]; -#[rustfmt::skip] -pub static UCRT_TANH_LO: [u64; 32] = [ - 0x0000000000000000u64, 0x3e48ac2ba1d73e2au64, 0x3e69f3121ec53172u64, 0x3df25b50a4ebbf1bu64, - 0x3e68faa2f5b9bef9u64, 0x3e368b9aa7805b80u64, 0x3e6ceac470cd83f6u64, 0x3e547f7b84b09745u64, - 0x3e64636e2a5bd1abu64, 0x3e5ceaa72a9c5154u64, 0x3e682468446b6824u64, 0x3e18624b40c4dbd0u64, - 0x3e54d8a89c750e5eu64, 0x3e5a753e077c2a0fu64, 0x3e6a90a852b19260u64, 0x3e0d2ac258f87d03u64, - 0x3e59fcef32422cbfu64, 0x3e61d8bee7ba46e2u64, 0x3e4f580c36bea881u64, 0x3e62999c25159f11u64, - 0x3e415506dadd3e2au64, 0x3e29b8bc9e8a0388u64, 0x3e451f8480e3e236u64, 0x3e41f12ae45a1224u64, - 0x3e62b5a75abd0e6au64, 0x3e47daf237553d84u64, 0x3e6b0aa538444196u64, 0x3e69df20d22a0798u64, - 0x3e69f7490e4bb40bu64, 0x3e4bdcdaf5cb4656u64, 0x3e452486cc2c7b9du64, 0x3e66dc8a80ce9f09u64, -]; -#[rustfmt::skip] -pub static UCRT_EXP_MUL: [u64; 64] = [ - 0x3ff0000000000000u64, 0x3ff02c9a3e778061u64, 0x3ff059b0d3158574u64, 0x3ff0874518759bc8u64, - 0x3ff0b5586cf9890fu64, 0x3ff0e3ec32d3d1a2u64, 0x3ff11301d0125b51u64, 0x3ff1429aaea92de0u64, - 0x3ff172b83c7d517bu64, 0x3ff1a35beb6fcb75u64, 0x3ff1d4873168b9aau64, 0x3ff2063b88628cd6u64, - 0x3ff2387a6e756238u64, 0x3ff26b4565e27cddu64, 0x3ff29e9df51fdee1u64, 0x3ff2d285a6e4030bu64, - 0x3ff306fe0a31b715u64, 0x3ff33c08b26416ffu64, 0x3ff371a7373aa9cbu64, 0x3ff3a7db34e59ff7u64, - 0x3ff3dea64c123422u64, 0x3ff4160a21f72e2au64, 0x3ff44e086061892du64, 0x3ff486a2b5c13cd0u64, - 0x3ff4bfdad5362a27u64, 0x3ff4f9b2769d2ca7u64, 0x3ff5342b569d4f82u64, 0x3ff56f4736b527dau64, - 0x3ff5ab07dd485429u64, 0x3ff5e76f15ad2148u64, 0x3ff6247eb03a5585u64, 0x3ff6623882552225u64, - 0x3ff6a09e667f3bcdu64, 0x3ff6dfb23c651a2fu64, 0x3ff71f75e8ec5f74u64, 0x3ff75feb564267c9u64, - 0x3ff7a11473eb0187u64, 0x3ff7e2f336cf4e62u64, 0x3ff82589994cce13u64, 0x3ff868d99b4492edu64, - 0x3ff8ace5422aa0dbu64, 0x3ff8f1ae99157736u64, 0x3ff93737b0cdc5e5u64, 0x3ff97d829fde4e50u64, - 0x3ff9c49182a3f090u64, 0x3ffa0c667b5de565u64, 0x3ffa5503b23e255du64, 0x3ffa9e6b5579fdbfu64, - 0x3ffae89f995ad3adu64, 0x3ffb33a2b84f15fbu64, 0x3ffb7f76f2fb5e47u64, 0x3ffbcc1e904bc1d2u64, - 0x3ffc199bdd85529cu64, 0x3ffc67f12e57d14bu64, 0x3ffcb720dcef9069u64, 0x3ffd072d4a07897cu64, - 0x3ffd5818dcfba487u64, 0x3ffda9e603db3285u64, 0x3ffdfc97337b9b5fu64, 0x3ffe502ee78b3ff6u64, - 0x3ffea4afa2a490dau64, 0x3ffefa1bee615a27u64, 0x3fff50765b6e4540u64, 0x3fffa7c1819e90d8u64, -]; -#[rustfmt::skip] -pub static UCRT_EXP_LO: [u64; 64] = [ - 0x0000000000000000u64, 0x3e6cef00c1dcdef9u64, 0x3e48ac2ba1d73e2au64, 0x3e60eb37901186beu64, - 0x3e69f3121ec53172u64, 0x3e469e8d10103a17u64, 0x3df25b50a4ebbf1au64, 0x3e6d525bbf668203u64, - 0x3e68faa2f5b9bef9u64, 0x3e66df96ea796d31u64, 0x3e368b9aa7805b80u64, 0x3e60c519ac771dd6u64, - 0x3e6ceac470cd83f5u64, 0x3e5789f37495e99cu64, 0x3e547f7b84b09745u64, 0x3e5b900c2d002475u64, - 0x3e64636e2a5bd1abu64, 0x3e4320b7fa64e430u64, 0x3e5ceaa72a9c5154u64, 0x3e53967fdba86f24u64, - 0x3e682468446b6824u64, 0x3e3f72e29f84325bu64, 0x3e18624b40c4dbd0u64, 0x3e5704f3404f068eu64, - 0x3e54d8a89c750e5eu64, 0x3e5a74b29ab4cf62u64, 0x3e5a753e077c2a0fu64, 0x3e5ad49f699bb2c0u64, - 0x3e6a90a852b19260u64, 0x3e56b48521ba6f93u64, 0x3e0d2ac258f87d03u64, 0x3e42a91124893ecfu64, - 0x3e59fcef32422cbeu64, 0x3e68ca345de441c5u64, 0x3e61d8bee7ba46e1u64, 0x3e59099f22fdba6au64, - 0x3e4f580c36bea881u64, 0x3e5b3d398841740au64, 0x3e62999c25159f11u64, 0x3e668925d901c83bu64, - 0x3e415506dadd3e2au64, 0x3e622aee6c57304eu64, 0x3e29b8bc9e8a0387u64, 0x3e6fbc9c9f173d24u64, - 0x3e451f8480e3e235u64, 0x3e66bbcac96535b5u64, 0x3e41f12ae45a1224u64, 0x3e55e7f6fd0fac90u64, - 0x3e62b5a75abd0e69u64, 0x3e609e2bf5ed7fa1u64, 0x3e47daf237553d84u64, 0x3e12f074891ee83du64, - 0x3e6b0aa538444196u64, 0x3e6cafa29694426fu64, 0x3e69df20d22a0797u64, 0x3e640f12f71a1e45u64, - 0x3e69f7490e4bb40bu64, 0x3e4ed9942b84600du64, 0x3e4bdcdaf5cb4656u64, 0x3e5e2cffd89cf44cu64, - 0x3e452486cc2c7b9du64, 0x3e6cc2b44eee3fa4u64, 0x3e66dc8a80ce9f09u64, 0x3e39e90d82e90a7eu64, -]; -#[rustfmt::skip] -pub static UCRT_EXP_HI: [u64; 64] = [ - 0x3ff0000000000000u64, 0x3ff02c9a30000000u64, 0x3ff059b0d0000000u64, 0x3ff0874510000000u64, - 0x3ff0b55860000000u64, 0x3ff0e3ec30000000u64, 0x3ff11301d0000000u64, 0x3ff1429aa0000000u64, - 0x3ff172b830000000u64, 0x3ff1a35be0000000u64, 0x3ff1d48730000000u64, 0x3ff2063b80000000u64, - 0x3ff2387a60000000u64, 0x3ff26b4560000000u64, 0x3ff29e9df0000000u64, 0x3ff2d285a0000000u64, - 0x3ff306fe00000000u64, 0x3ff33c08b0000000u64, 0x3ff371a730000000u64, 0x3ff3a7db30000000u64, - 0x3ff3dea640000000u64, 0x3ff4160a20000000u64, 0x3ff44e0860000000u64, 0x3ff486a2b0000000u64, - 0x3ff4bfdad0000000u64, 0x3ff4f9b270000000u64, 0x3ff5342b50000000u64, 0x3ff56f4730000000u64, - 0x3ff5ab07d0000000u64, 0x3ff5e76f10000000u64, 0x3ff6247eb0000000u64, 0x3ff6623880000000u64, - 0x3ff6a09e60000000u64, 0x3ff6dfb230000000u64, 0x3ff71f75e0000000u64, 0x3ff75feb50000000u64, - 0x3ff7a11470000000u64, 0x3ff7e2f330000000u64, 0x3ff8258990000000u64, 0x3ff868d990000000u64, - 0x3ff8ace540000000u64, 0x3ff8f1ae90000000u64, 0x3ff93737b0000000u64, 0x3ff97d8290000000u64, - 0x3ff9c49180000000u64, 0x3ffa0c6670000000u64, 0x3ffa5503b0000000u64, 0x3ffa9e6b50000000u64, - 0x3ffae89f90000000u64, 0x3ffb33a2b0000000u64, 0x3ffb7f76f0000000u64, 0x3ffbcc1e90000000u64, - 0x3ffc199bd0000000u64, 0x3ffc67f120000000u64, 0x3ffcb720d0000000u64, 0x3ffd072d40000000u64, - 0x3ffd5818d0000000u64, 0x3ffda9e600000000u64, 0x3ffdfc9730000000u64, 0x3ffe502ee0000000u64, - 0x3ffea4afa0000000u64, 0x3ffefa1be0000000u64, 0x3fff507650000000u64, 0x3fffa7c180000000u64, -]; -#[rustfmt::skip] -pub static UCRT_POW_T1: [u64; 257] = [ - 0x4000000000000000u64, 0x3fffe00000000000u64, 0x3fffc00000000000u64, 0x3fffa00000000000u64, - 0x3fff800000000000u64, 0x3fff600000000000u64, 0x3fff400000000000u64, 0x3fff200000000000u64, - 0x3fff000000000000u64, 0x3ffee00000000000u64, 0x3ffec00000000000u64, 0x3ffea00000000000u64, - 0x3ffe900000000000u64, 0x3ffe700000000000u64, 0x3ffe500000000000u64, 0x3ffe300000000000u64, - 0x3ffe100000000000u64, 0x3ffe000000000000u64, 0x3ffde00000000000u64, 0x3ffdc00000000000u64, - 0x3ffda00000000000u64, 0x3ffd900000000000u64, 0x3ffd700000000000u64, 0x3ffd500000000000u64, - 0x3ffd400000000000u64, 0x3ffd200000000000u64, 0x3ffd000000000000u64, 0x3ffcf00000000000u64, - 0x3ffcd00000000000u64, 0x3ffcb00000000000u64, 0x3ffca00000000000u64, 0x3ffc800000000000u64, - 0x3ffc700000000000u64, 0x3ffc500000000000u64, 0x3ffc300000000000u64, 0x3ffc200000000000u64, - 0x3ffc000000000000u64, 0x3ffbf00000000000u64, 0x3ffbd00000000000u64, 0x3ffbc00000000000u64, - 0x3ffba00000000000u64, 0x3ffb900000000000u64, 0x3ffb700000000000u64, 0x3ffb600000000000u64, - 0x3ffb400000000000u64, 0x3ffb300000000000u64, 0x3ffb200000000000u64, 0x3ffb000000000000u64, - 0x3ffaf00000000000u64, 0x3ffad00000000000u64, 0x3ffac00000000000u64, 0x3ffaa00000000000u64, - 0x3ffa900000000000u64, 0x3ffa800000000000u64, 0x3ffa600000000000u64, 0x3ffa500000000000u64, - 0x3ffa400000000000u64, 0x3ffa200000000000u64, 0x3ffa100000000000u64, 0x3ffa000000000000u64, - 0x3ff9e00000000000u64, 0x3ff9d00000000000u64, 0x3ff9c00000000000u64, 0x3ff9a00000000000u64, - 0x3ff9900000000000u64, 0x3ff9800000000000u64, 0x3ff9700000000000u64, 0x3ff9500000000000u64, - 0x3ff9400000000000u64, 0x3ff9300000000000u64, 0x3ff9200000000000u64, 0x3ff9000000000000u64, - 0x3ff8f00000000000u64, 0x3ff8e00000000000u64, 0x3ff8d00000000000u64, 0x3ff8b00000000000u64, - 0x3ff8a00000000000u64, 0x3ff8900000000000u64, 0x3ff8800000000000u64, 0x3ff8700000000000u64, - 0x3ff8600000000000u64, 0x3ff8400000000000u64, 0x3ff8300000000000u64, 0x3ff8200000000000u64, - 0x3ff8100000000000u64, 0x3ff8000000000000u64, 0x3ff7f00000000000u64, 0x3ff7e00000000000u64, - 0x3ff7d00000000000u64, 0x3ff7b00000000000u64, 0x3ff7a00000000000u64, 0x3ff7900000000000u64, - 0x3ff7800000000000u64, 0x3ff7700000000000u64, 0x3ff7600000000000u64, 0x3ff7500000000000u64, - 0x3ff7400000000000u64, 0x3ff7300000000000u64, 0x3ff7200000000000u64, 0x3ff7100000000000u64, - 0x3ff7000000000000u64, 0x3ff6f00000000000u64, 0x3ff6e00000000000u64, 0x3ff6d00000000000u64, - 0x3ff6c00000000000u64, 0x3ff6b00000000000u64, 0x3ff6a00000000000u64, 0x3ff6900000000000u64, - 0x3ff6800000000000u64, 0x3ff6700000000000u64, 0x3ff6600000000000u64, 0x3ff6500000000000u64, - 0x3ff6400000000000u64, 0x3ff6300000000000u64, 0x3ff6200000000000u64, 0x3ff6100000000000u64, - 0x3ff6000000000000u64, 0x3ff5f00000000000u64, 0x3ff5e00000000000u64, 0x3ff5d00000000000u64, - 0x3ff5c00000000000u64, 0x3ff5b00000000000u64, 0x3ff5a00000000000u64, 0x3ff5900000000000u64, - 0x3ff5800000000000u64, 0x3ff5800000000000u64, 0x3ff5700000000000u64, 0x3ff5600000000000u64, - 0x3ff5500000000000u64, 0x3ff5400000000000u64, 0x3ff5300000000000u64, 0x3ff5200000000000u64, - 0x3ff5100000000000u64, 0x3ff5000000000000u64, 0x3ff5000000000000u64, 0x3ff4f00000000000u64, - 0x3ff4e00000000000u64, 0x3ff4d00000000000u64, 0x3ff4c00000000000u64, 0x3ff4b00000000000u64, - 0x3ff4a00000000000u64, 0x3ff4a00000000000u64, 0x3ff4900000000000u64, 0x3ff4800000000000u64, - 0x3ff4700000000000u64, 0x3ff4600000000000u64, 0x3ff4600000000000u64, 0x3ff4500000000000u64, - 0x3ff4400000000000u64, 0x3ff4300000000000u64, 0x3ff4200000000000u64, 0x3ff4200000000000u64, - 0x3ff4100000000000u64, 0x3ff4000000000000u64, 0x3ff3f00000000000u64, 0x3ff3e00000000000u64, - 0x3ff3e00000000000u64, 0x3ff3d00000000000u64, 0x3ff3c00000000000u64, 0x3ff3b00000000000u64, - 0x3ff3b00000000000u64, 0x3ff3a00000000000u64, 0x3ff3900000000000u64, 0x3ff3800000000000u64, - 0x3ff3800000000000u64, 0x3ff3700000000000u64, 0x3ff3600000000000u64, 0x3ff3500000000000u64, - 0x3ff3500000000000u64, 0x3ff3400000000000u64, 0x3ff3300000000000u64, 0x3ff3200000000000u64, - 0x3ff3200000000000u64, 0x3ff3100000000000u64, 0x3ff3000000000000u64, 0x3ff3000000000000u64, - 0x3ff2f00000000000u64, 0x3ff2e00000000000u64, 0x3ff2e00000000000u64, 0x3ff2d00000000000u64, - 0x3ff2c00000000000u64, 0x3ff2b00000000000u64, 0x3ff2b00000000000u64, 0x3ff2a00000000000u64, - 0x3ff2900000000000u64, 0x3ff2900000000000u64, 0x3ff2800000000000u64, 0x3ff2700000000000u64, - 0x3ff2700000000000u64, 0x3ff2600000000000u64, 0x3ff2500000000000u64, 0x3ff2500000000000u64, - 0x3ff2400000000000u64, 0x3ff2300000000000u64, 0x3ff2300000000000u64, 0x3ff2200000000000u64, - 0x3ff2100000000000u64, 0x3ff2100000000000u64, 0x3ff2000000000000u64, 0x3ff2000000000000u64, - 0x3ff1f00000000000u64, 0x3ff1e00000000000u64, 0x3ff1e00000000000u64, 0x3ff1d00000000000u64, - 0x3ff1c00000000000u64, 0x3ff1c00000000000u64, 0x3ff1b00000000000u64, 0x3ff1b00000000000u64, - 0x3ff1a00000000000u64, 0x3ff1900000000000u64, 0x3ff1900000000000u64, 0x3ff1800000000000u64, - 0x3ff1800000000000u64, 0x3ff1700000000000u64, 0x3ff1600000000000u64, 0x3ff1600000000000u64, - 0x3ff1500000000000u64, 0x3ff1500000000000u64, 0x3ff1400000000000u64, 0x3ff1300000000000u64, - 0x3ff1300000000000u64, 0x3ff1200000000000u64, 0x3ff1200000000000u64, 0x3ff1100000000000u64, - 0x3ff1100000000000u64, 0x3ff1000000000000u64, 0x3ff0f00000000000u64, 0x3ff0f00000000000u64, - 0x3ff0e00000000000u64, 0x3ff0e00000000000u64, 0x3ff0d00000000000u64, 0x3ff0d00000000000u64, - 0x3ff0c00000000000u64, 0x3ff0c00000000000u64, 0x3ff0b00000000000u64, 0x3ff0a00000000000u64, - 0x3ff0a00000000000u64, 0x3ff0900000000000u64, 0x3ff0900000000000u64, 0x3ff0800000000000u64, - 0x3ff0800000000000u64, 0x3ff0700000000000u64, 0x3ff0700000000000u64, 0x3ff0600000000000u64, - 0x3ff0600000000000u64, 0x3ff0500000000000u64, 0x3ff0500000000000u64, 0x3ff0400000000000u64, - 0x3ff0400000000000u64, 0x3ff0300000000000u64, 0x3ff0300000000000u64, 0x3ff0200000000000u64, - 0x3ff0200000000000u64, 0x3ff0100000000000u64, 0x3ff0100000000000u64, 0x3ff0000000000000u64, - 0x3ff0000000000000u64, -]; -#[rustfmt::skip] -pub static UCRT_POW_T2: [u64; 257] = [ - 0x0000000000000000u64, 0x3effe01fe01fe020u64, 0x3f1fc07f01fc07f0u64, 0x3f31caa01fa11caau64, - 0x3f3f81f81f81f820u64, 0x3f48856506ddaba6u64, 0x3f5196792909c560u64, 0x3f57d9108c2ad433u64, - 0x3f5f07c1f07c1f08u64, 0x3f638ff08b1c03ddu64, 0x3f680f6603d980f6u64, 0x3f6d00f57403d5d0u64, - 0x3f331abf0b7672a0u64, 0x3f506a965d43919bu64, 0x3f5ceb240795ceb2u64, 0x3f6522f3b834e67fu64, - 0x3f6c3c3c3c3c3c3cu64, 0x3f3e01e01e01e01eu64, 0x3f575b8fe21a291cu64, 0x3f6403b9403b9404u64, - 0x3f6cc0ed7303b5ccu64, 0x3f479118f3fc4da2u64, 0x3f5ed952e0b0ce46u64, 0x3f695900eae56404u64, - 0x3f3d41d41d41d41du64, 0x3f5cb28ff16c69aeu64, 0x3f696b1edd80e866u64, 0x3f4372e225fe30d9u64, - 0x3f60ad12073615a2u64, 0x3f6cdb2c0397cdb3u64, 0x3f52cc157b864407u64, 0x3f664cb5f7148404u64, - 0x3f3c71c71c71c71cu64, 0x3f6129a21a930b84u64, 0x3f6f1e0387f1e038u64, 0x3f5ad4e4ba80709bu64, - 0x3f6c0e070381c0e0u64, 0x3f560fba1a362bb0u64, 0x3f6a5713280dee96u64, 0x3f53f59620f9ece9u64, - 0x3f69f22983759f23u64, 0x3f5478ac63fc8d5cu64, 0x3f6ad87bb4671656u64, 0x3f578b8efbb8148cu64, - 0x3f6d0369d0369d03u64, 0x3f5d212b601b3748u64, 0x3f0b2036406c80d9u64, 0x3f629663b24547d1u64, - 0x3f4435e50d79435eu64, 0x3f67d0ff2920bc03u64, 0x3f55c06b15c06b16u64, 0x3f6e3a5f0fd7f954u64, - 0x3f61dec0d4c77b03u64, 0x3f473289870ac52eu64, 0x3f6a034da034da03u64, 0x3f5d041da2292856u64, - 0x3f3a41a41a41a41au64, 0x3f68550f8a39409du64, 0x3f5b4fe5e92c0686u64, 0x3f3a01a01a01a01au64, - 0x3f691d2a2067b23au64, 0x3f5e7c5dada0b4e5u64, 0x3f468a7725080ce1u64, 0x3f6c49d4aa21b490u64, - 0x3f63333333333333u64, 0x3f54bc363b03fccfu64, 0x3f2c9f01970e4f81u64, 0x3f697617c6ef5b25u64, - 0x3f6161f9add3c0cau64, 0x3f5319fe6cb39806u64, 0x3f2f693a1c451ab3u64, 0x3f6a9e240321a9e2u64, - 0x3f63831f3831f383u64, 0x3f5949ebc4dcfc1cu64, 0x3f480c6980c6980cu64, 0x3f6f9d00c5fe7403u64, - 0x3f69721ed7e75347u64, 0x3f6381ec0313381fu64, 0x3f5b97c2aec12653u64, 0x3f509ef3024ae3bau64, - 0x3f38618618618618u64, 0x3f6e0184f00c2780u64, 0x3f692ef5657dba52u64, 0x3f64940305494030u64, - 0x3f60303030303030u64, 0x3f58060180601806u64, 0x3f5017f405fd017fu64, 0x3f412a8ad278e8ddu64, - 0x3f17d05f417d05f4u64, 0x3f6d67245c02f7d6u64, 0x3f6a4411c1d986a9u64, 0x3f6754d76c7316dfu64, - 0x3f649902f149902fu64, 0x3f621023358c1a68u64, 0x3f5f7390d2a6c406u64, 0x3f5b2b0805d5b2b1u64, - 0x3f5745d1745d1746u64, 0x3f53c31507fa32c4u64, 0x3f50a1fd1b7af017u64, 0x3f4bc36ce3e0453au64, - 0x3f4702e05c0b8170u64, 0x3f4300b79300b793u64, 0x3f3f76b4337c6cb1u64, 0x3f3a62681c860fb0u64, - 0x3f36c16c16c16c17u64, 0x3f3490aa31a3cfc7u64, 0x3f33cd153729043eu64, 0x3f3473a88d0bfd2eu64, - 0x3f36816816816817u64, 0x3f39f36016719f36u64, 0x3f3ec6a5122f9016u64, 0x3f427c29da5519cfu64, - 0x3f4642c8590b2164u64, 0x3f4ab5c45606f00bu64, 0x3f4fd3b80b11fd3cu64, 0x3f52cda0c6ba4eaau64, - 0x3f56058160581606u64, 0x3f5990d0a4b7ef87u64, 0x3f5d6ee340579d6fu64, 0x3f60cf87d9c54a69u64, - 0x3f6310572620ae4cu64, 0x3f65798c8ff522a2u64, 0x3f680ad602b580adu64, 0x3f6ac3e24799546fu64, - 0x3f6da46102b1da46u64, 0x3f15805601580560u64, 0x3f3ed3c506b39a23u64, 0x3f4cbdd3e2970f60u64, - 0x3f55555555555555u64, 0x3f5c979aee0bf805u64, 0x3f621291e81fd58eu64, 0x3f65fead500a9580u64, - 0x3f6a0fd5c5f02a3au64, 0x3f6e45c223898adcu64, 0x3f35015015015015u64, 0x3f4c7b16ea64d422u64, - 0x3f57829cbc14e5e1u64, 0x3f60877db8589720u64, 0x3f65710e4b5edceau64, 0x3f6a7dbb4d1fc1c8u64, - 0x3f6fad40a57eb503u64, 0x3f43fd6bb00a5140u64, 0x3f54e78ecb419ba9u64, 0x3f600a44029100a4u64, - 0x3f65c28f5c28f5c3u64, 0x3f6b9c68b2c0cc4au64, 0x3f2978feb9f34381u64, 0x3f4ecf163bb6500au64, - 0x3f5be1958b67ebb9u64, 0x3f644e6157dc9a3bu64, 0x3f6acc4baa3f0ddfu64, 0x3f26a4cbcb2a247bu64, - 0x3f50505050505050u64, 0x3f5e0b4439959819u64, 0x3f66027f6027f602u64, 0x3f6d1e854b5e0db4u64, - 0x3f4165e7254813e2u64, 0x3f576646a9d716efu64, 0x3f632b48f757ce88u64, 0x3f6ac1b24652a906u64, - 0x3f33b13b13b13b14u64, 0x3f5490e1eb208984u64, 0x3f62385830fec66eu64, 0x3f6a45a6cc111b7eu64, - 0x3f33813813813814u64, 0x3f556f472517b708u64, 0x3f631be7bc0e8f2au64, 0x3f6b9cbf3e55f044u64, - 0x3f40e7d95bc609a9u64, 0x3f59e6b3804d19e7u64, 0x3f65c8b6af7963c2u64, 0x3f6eb9dad43bf402u64, - 0x3f4f1a515885fb37u64, 0x3f60eeb1d3d76c02u64, 0x3f6a320261a32026u64, 0x3f3c82ac40260390u64, - 0x3f5a12f684bda12fu64, 0x3f669d43fda2962cu64, 0x3f02e025c04b8097u64, 0x3f542804b542804bu64, - 0x3f63f69b02593f6au64, 0x3f6df31cb46e21fau64, 0x3f5012b404ad012bu64, 0x3f623925e7820a7fu64, - 0x3f6c8253c8253c82u64, 0x3f4b92ddc02526e5u64, 0x3f61602511602511u64, 0x3f6bf471439c9adfu64, - 0x3f4a85c40939a85cu64, 0x3f6166f9ac024d16u64, 0x3f6c44e10125e227u64, 0x3f4cebf48bbd90e5u64, - 0x3f62492492492492u64, 0x3f6d6f2e2ec0b673u64, 0x3f5159e26af37c05u64, 0x3f64024540245402u64, - 0x3f6f6f0243f6f024u64, 0x3f55e60121579805u64, 0x3f668e18cf81b10fu64, 0x3f32012012012012u64, - 0x3f5c11f7047dc11fu64, 0x3f69e878ff70985eu64, 0x3f4779d9fdc3a219u64, 0x3f61eace5c957907u64, - 0x3f6e0d5b450239e1u64, 0x3f548bf073816367u64, 0x3f6694808dda5202u64, 0x3f37c67f2bae2b21u64, - 0x3f5ee58469ee5847u64, 0x3f6c0233c0233c02u64, 0x3f514e02328a7012u64, 0x3f6561072057b573u64, - 0x3f31811811811812u64, 0x3f5e28646f5a1060u64, 0x3f6c0d1284e6f1d7u64, 0x3f523543f0c80459u64, - 0x3f663cbeea4e1a09u64, 0x3f3b9a3fdd5c8cb8u64, 0x3f60be1c159a76d2u64, 0x3f6e1d1a688e4838u64, - 0x3f572044d72044d7u64, 0x3f691713db81577bu64, 0x3f4ac73ae9819b50u64, 0x3f6460334e904cf6u64, - 0x3f31111111111111u64, 0x3f5feef80441fef0u64, 0x3f6de021fde021feu64, 0x3f57b7eacc9686a0u64, - 0x3f69ead7cd391fbcu64, 0x3f50195609804390u64, 0x3f6641511e8d2b32u64, 0x3f4222b1acf1ce96u64, - 0x3f62e29f79b47582u64, 0x3f24f0d1682e11cdu64, 0x3f5f9bb096771e4du64, 0x3f6e5ee45dd96ae2u64, - 0x3f5a0429a0429a04u64, 0x3f6bb74d5f06c021u64, 0x3f54fce404254fceu64, 0x3f695766eacbc402u64, - 0x3f50842108421084u64, 0x3f673e5371d5c338u64, 0x3f4930523fbe3368u64, 0x3f656b38f225f6c4u64, - 0x3f426e978d4fdf3bu64, 0x3f63dd40e4eb0cc6u64, 0x3f397f7d73404146u64, 0x3f6293982cc98af1u64, - 0x3f30410410410410u64, 0x3f618d6f048ff7e4u64, 0x3f2236a3ebc349deu64, 0x3f60c9f8ee53d18cu64, - 0x3f10204081020408u64, 0x3f60486ca2f46ea6u64, 0x3ef0101010101010u64, 0x3f60080402010080u64, - 0x0000000000000000u64, -]; -#[rustfmt::skip] -pub static UCRT_POW_T3: [u64; 257] = [ - 0x0000000000000000u64, 0x3f6ff00aa0000000u64, 0x3f7fe02a60000000u64, 0x3f87dc4750000000u64, - 0x3f8fc0a8b0000000u64, 0x3f93cea440000000u64, 0x3f97b91b00000000u64, 0x3f9b9fc020000000u64, - 0x3f9f829b00000000u64, 0x3fa1b0d980000000u64, 0x3fa39e87b0000000u64, 0x3fa58a5ba0000000u64, - 0x3fa77458f0000000u64, 0x3fa95c8300000000u64, 0x3fab42dd70000000u64, 0x3fad276b80000000u64, - 0x3faf0a30c0000000u64, 0x3fb0759830000000u64, 0x3fb16536e0000000u64, 0x3fb253f620000000u64, - 0x3fb341d790000000u64, 0x3fb42edcb0000000u64, 0x3fb51b0730000000u64, 0x3fb60658a0000000u64, - 0x3fb6f0d280000000u64, 0x3fb7da7660000000u64, 0x3fb8c345d0000000u64, 0x3fb9ab4240000000u64, - 0x3fba926d30000000u64, 0x3fbb78c820000000u64, 0x3fbc5e5480000000u64, 0x3fbd4313d0000000u64, - 0x3fbe270760000000u64, 0x3fbf0a30c0000000u64, 0x3fbfec9130000000u64, 0x3fc0671510000000u64, - 0x3fc0d77e70000000u64, 0x3fc1478580000000u64, 0x3fc1b72ad0000000u64, 0x3fc2266f10000000u64, - 0x3fc29552f0000000u64, 0x3fc303d710000000u64, 0x3fc371fc20000000u64, 0x3fc3dfc2b0000000u64, - 0x3fc44d2b60000000u64, 0x3fc4ba36f0000000u64, 0x3fc526e5e0000000u64, 0x3fc59338d0000000u64, - 0x3fc5ff3070000000u64, 0x3fc66acd40000000u64, 0x3fc6d60fe0000000u64, 0x3fc740f8f0000000u64, - 0x3fc7ab8900000000u64, 0x3fc815c0a0000000u64, 0x3fc87fa060000000u64, 0x3fc8e928d0000000u64, - 0x3fc9525a90000000u64, 0x3fc9bb3620000000u64, 0x3fca23bc10000000u64, 0x3fca8becf0000000u64, - 0x3fcaf3c940000000u64, 0x3fcb5b5190000000u64, 0x3fcbc28670000000u64, 0x3fcc296850000000u64, - 0x3fcc8ff7c0000000u64, 0x3fccf63540000000u64, 0x3fcd5c2160000000u64, 0x3fcdc1bca0000000u64, - 0x3fce270760000000u64, 0x3fce8c0250000000u64, 0x3fcef0adc0000000u64, 0x3fcf550a50000000u64, - 0x3fcfb91860000000u64, 0x3fd00e6c40000000u64, 0x3fd0402590000000u64, 0x3fd071b850000000u64, - 0x3fd0a324e0000000u64, 0x3fd0d46b50000000u64, 0x3fd1058bf0000000u64, 0x3fd1368700000000u64, - 0x3fd1675ca0000000u64, 0x3fd1980d20000000u64, 0x3fd1c898c0000000u64, 0x3fd1f8ff90000000u64, - 0x3fd22941f0000000u64, 0x3fd2596010000000u64, 0x3fd2895a10000000u64, 0x3fd2b93030000000u64, - 0x3fd2e8e2b0000000u64, 0x3fd31871c0000000u64, 0x3fd347dd90000000u64, 0x3fd3772660000000u64, - 0x3fd3a64c50000000u64, 0x3fd3d54fa0000000u64, 0x3fd4043080000000u64, 0x3fd432ef20000000u64, - 0x3fd4618bc0000000u64, 0x3fd4900680000000u64, 0x3fd4be5f90000000u64, 0x3fd4ec9730000000u64, - 0x3fd51aad80000000u64, 0x3fd548a2c0000000u64, 0x3fd5767710000000u64, 0x3fd5a42ab0000000u64, - 0x3fd5d1bdb0000000u64, 0x3fd5ff3070000000u64, 0x3fd62c82f0000000u64, 0x3fd659b570000000u64, - 0x3fd686c810000000u64, 0x3fd6b3bb20000000u64, 0x3fd6e08ea0000000u64, 0x3fd70d42e0000000u64, - 0x3fd739d7f0000000u64, 0x3fd7664e10000000u64, 0x3fd792a550000000u64, 0x3fd7bede00000000u64, - 0x3fd7eaf830000000u64, 0x3fd816f410000000u64, 0x3fd842d1d0000000u64, 0x3fd86e9190000000u64, - 0x3fd89a3380000000u64, 0x3fd8c5b7c0000000u64, 0x3fd8f11e80000000u64, 0x3fd91c67e0000000u64, - 0x3fd9479410000000u64, 0x3fd972a340000000u64, 0x3fd99d9580000000u64, 0x3fd9c86b00000000u64, - 0x3fd9f323e0000000u64, 0x3fda1dc060000000u64, 0x3fda484090000000u64, 0x3fda72a490000000u64, - 0x3fda9cec90000000u64, 0x3fdac718c0000000u64, 0x3fdaf12930000000u64, 0x3fdb1b1e00000000u64, - 0x3fdb44f770000000u64, 0x3fdb6eb590000000u64, 0x3fdb985890000000u64, 0x3fdbc1e080000000u64, - 0x3fdbeb4d90000000u64, 0x3fdc149ff0000000u64, 0x3fdc3dd7a0000000u64, 0x3fdc66f4e0000000u64, - 0x3fdc8ff7c0000000u64, 0x3fdcb8e070000000u64, 0x3fdce1af00000000u64, 0x3fdd0a63a0000000u64, - 0x3fdd32fe70000000u64, 0x3fdd5b7f90000000u64, 0x3fdd83e720000000u64, 0x3fddac3530000000u64, - 0x3fddd46a00000000u64, 0x3fddfc8590000000u64, 0x3fde248810000000u64, 0x3fde4c71a0000000u64, - 0x3fde744260000000u64, 0x3fde9bfa60000000u64, 0x3fdec399d0000000u64, 0x3fdeeb20c0000000u64, - 0x3fdf128f50000000u64, 0x3fdf39e5b0000000u64, 0x3fdf6123f0000000u64, 0x3fdf884a30000000u64, - 0x3fdfaf5880000000u64, 0x3fdfd64f20000000u64, 0x3fdffd2e00000000u64, 0x3fe011fab0000000u64, - 0x3fe02552a0000000u64, 0x3fe0389ee0000000u64, 0x3fe04bdf90000000u64, 0x3fe05f14b0000000u64, - 0x3fe0723e50000000u64, 0x3fe0855c80000000u64, 0x3fe0986f40000000u64, 0x3fe0ab76b0000000u64, - 0x3fe0be72e0000000u64, 0x3fe0d163c0000000u64, 0x3fe0e44980000000u64, 0x3fe0f72410000000u64, - 0x3fe109f390000000u64, 0x3fe11cb810000000u64, 0x3fe12f7190000000u64, 0x3fe1422020000000u64, - 0x3fe154c3d0000000u64, 0x3fe1675ca0000000u64, 0x3fe179eab0000000u64, 0x3fe18c6e00000000u64, - 0x3fe19ee6b0000000u64, 0x3fe1b154b0000000u64, 0x3fe1c3b810000000u64, 0x3fe1d610f0000000u64, - 0x3fe1e85f50000000u64, 0x3fe1faa340000000u64, 0x3fe20cdcd0000000u64, 0x3fe21f0bf0000000u64, - 0x3fe23130d0000000u64, 0x3fe2434b60000000u64, 0x3fe2555bc0000000u64, 0x3fe2676200000000u64, - 0x3fe2795e10000000u64, 0x3fe28b5000000000u64, 0x3fe29d37f0000000u64, 0x3fe2af15f0000000u64, - 0x3fe2c0e9e0000000u64, 0x3fe2d2b400000000u64, 0x3fe2e47430000000u64, 0x3fe2f62a90000000u64, - 0x3fe307d730000000u64, 0x3fe3197a00000000u64, 0x3fe32b1330000000u64, 0x3fe33ca2b0000000u64, - 0x3fe34e2890000000u64, 0x3fe35fa4e0000000u64, 0x3fe37117b0000000u64, 0x3fe38280f0000000u64, - 0x3fe393e0d0000000u64, 0x3fe3a53730000000u64, 0x3fe3b68440000000u64, 0x3fe3c7c7f0000000u64, - 0x3fe3d90260000000u64, 0x3fe3ea3390000000u64, 0x3fe3fb5b80000000u64, 0x3fe40c7a40000000u64, - 0x3fe41d8fe0000000u64, 0x3fe42e9c60000000u64, 0x3fe43f9fe0000000u64, 0x3fe4509a50000000u64, - 0x3fe4618bc0000000u64, 0x3fe4727430000000u64, 0x3fe48353d0000000u64, 0x3fe4942a80000000u64, - 0x3fe4a4f850000000u64, 0x3fe4b5bd60000000u64, 0x3fe4c679a0000000u64, 0x3fe4d72d30000000u64, - 0x3fe4e7d810000000u64, 0x3fe4f87a30000000u64, 0x3fe50913c0000000u64, 0x3fe519a4c0000000u64, - 0x3fe52a2d20000000u64, 0x3fe53aad00000000u64, 0x3fe54b2460000000u64, 0x3fe55b9350000000u64, - 0x3fe56bf9d0000000u64, 0x3fe57c57f0000000u64, 0x3fe58cadb0000000u64, 0x3fe59cfb20000000u64, - 0x3fe5ad4040000000u64, 0x3fe5bd7d30000000u64, 0x3fe5cdb1d0000000u64, 0x3fe5ddde50000000u64, - 0x3fe5ee02a0000000u64, 0x3fe5fe1ed0000000u64, 0x3fe60e32f0000000u64, 0x3fe61e3ef0000000u64, - 0x3fe62e42e0000000u64, -]; -#[rustfmt::skip] -pub static UCRT_POW_T4: [u64; 257] = [ - 0x0000000000000000u64, 0x3db5885e0250435au64, 0x3de620cf11f86ed2u64, 0x3dff0214edba4a25u64, - 0x3dbf807c79f3db4eu64, 0x3dea352ba779a52bu64, 0x3dff56c46aa49fd5u64, 0x3dfebe465fef5196u64, - 0x3e0cf0660099f1f8u64, 0x3e1247b2ff85945du64, 0x3e13fd7abf5202b6u64, 0x3e1f91c9a918d51eu64, - 0x3e08cb73f118d3cau64, 0x3e1d91c7d6fad074u64, 0x3de1971bec28d14cu64, 0x3e15b616a423c78au64, - 0x3da162a6617cc971u64, 0x3e166391c4c06d29u64, 0x3e2d46f5c1d0c4b8u64, 0x3e2e14282df1f6d3u64, - 0x3e186f47424a660du64, 0x3e2d4c8de077753eu64, 0x3e2e0c307ed24f1cu64, 0x3e226ea18763bdd3u64, - 0x3e25cad69737c933u64, 0x3e2af62599088901u64, 0x3e18c66c83d6b2d0u64, 0x3e1880ceb36fb30fu64, - 0x3e2495aac6ca17a4u64, 0x3e2761db4210878cu64, 0x3e2eb78e862bac2fu64, 0x3e19b2cd75790dd9u64, - 0x3e2c55e5cbd3d50fu64, 0x3db162a6617cc971u64, 0x3dfdbeabaaa2e519u64, 0x3e1652cb7150c647u64, - 0x3e39a11cb2cd2ee2u64, 0x3e219d0ab1a28813u64, 0x3e24bd9e80a41811u64, 0x3e3214b596faa3dfu64, - 0x3e303fea46980bb8u64, 0x3e31c8ffa5fd28c7u64, 0x3dce8f743bcd96c5u64, 0x3dfd98c5395315c6u64, - 0x3e3996fa3ccfa7b2u64, 0x3e1cd2af2ad13037u64, 0x3e1d0da1bd17200eu64, 0x3e3330410ba68b75u64, - 0x3df4f27a790e7c41u64, 0x3e13956a86f6ff1bu64, 0x3e2c6748723551d9u64, 0x3e2500de9326cdfcu64, - 0x3e1086c848df1b59u64, 0x3e04357ead6836ffu64, 0x3e24832442408024u64, 0x3e3d10da8154b13du64, - 0x3e39e8ad68ec8260u64, 0x3e3cfbf706abaf18u64, 0x3e3fc56ac6326e23u64, 0x3e39105e3185cf21u64, - 0x3e3d017fe5b19cc0u64, 0x3e3d1f6b48dd13feu64, 0x3e20b63358a7e73au64, 0x3e263063028c211cu64, - 0x3e2e6a6886b09760u64, 0x3e3c138bb891cd03u64, 0x3e369f7722b7221au64, 0x3df57d8fac1a628cu64, - 0x3e3c55e5cbd3d50fu64, 0x3e1552d2ff48fe2eu64, 0x3e37b8b26ca431bcu64, 0x3e292decdc1c5f6du64, - 0x3e3abc7c551aaa8cu64, 0x3e36b540731a354bu64, 0x3e32d341036b89efu64, 0x3e4f9ab21a3a2e0fu64, - 0x3e239c871afb9fbdu64, 0x3e3e6add2c81f640u64, 0x3e435c95aa313f41u64, 0x3e249d4582f6cc53u64, - 0x3e47574c1c07398fu64, 0x3e4ba846dece9e8du64, 0x3e16999fafbc68e7u64, 0x3e4c9145e51b0103u64, - 0x3e479ef2cb44850au64, 0x3e0beec73de11275u64, 0x3e2ef4351af5a498u64, 0x3e45713a493b4a50u64, - 0x3e45c23a61385992u64, 0x3e42a88309f57299u64, 0x3e4530faa9ac8aceu64, 0x3e25fec2d792a758u64, - 0x3e35a517a71cbcd7u64, 0x3e3707dc3e1cd9a3u64, 0x3e3a1a9f8ef43049u64, 0x3e4409d0276b3674u64, - 0x3e20e2f613e85bd9u64, 0x3df0027433001e5fu64, 0x3e35dde2836d3265u64, 0x3e2300134d7aaf04u64, - 0x3e3cb7e0b42724f5u64, 0x3e2d6e93167e6308u64, 0x3e3d1569b1526adbu64, 0x3e0e99fc338a1a41u64, - 0x3e4eb01394a11b1cu64, 0x3e04f27a790e7c41u64, 0x3e25ce3ca97b7af9u64, 0x3e281f0f940ed857u64, - 0x3e4d36295d88857cu64, 0x3e21aca1ec4af526u64, 0x3e445743c7182726u64, 0x3e23c491aead337eu64, - 0x3e3aef401a738931u64, 0x3e21cede76092a29u64, 0x3e4fba8f44f82bb4u64, 0x3e446f5f7f3c3e1au64, - 0x3e47055f86c9674bu64, 0x3e4b41a92b6b6e1au64, 0x3e443d162e927628u64, 0x3e4466174013f9b1u64, - 0x3e3b05096ad69c62u64, 0x3e40b169150faa58u64, 0x3e3cd98b1df85da7u64, 0x3e468b507b0f8fa8u64, - 0x3e48422df57499bau64, 0x3e11351586970274u64, 0x3e117e08acba92eeu64, 0x3e26e04314dd0229u64, - 0x3e497f3097e56d1au64, 0x3e3356e655901286u64, 0x3e0cb761457f94d6u64, 0x3e39af67a85a9dacu64, - 0x3e453410931a909fu64, 0x3e22c587206058f5u64, 0x3e223bc358899c22u64, 0x3e4d7bf8b6d223cbu64, - 0x3e47991ec5197ddbu64, 0x3e4a79e6bb3a9219u64, 0x3e3a4c43ed663ec5u64, 0x3e461b5a1484f438u64, - 0x3e4b4e36f7ef0c3au64, 0x3e115f026acd0d1bu64, 0x3e3f36b535cecf05u64, 0x3e2ffb7fbf3eb5c6u64, - 0x3e3e6a6886b09760u64, 0x3e3135eb27f5bbc3u64, 0x3e470be7d6f6fa57u64, 0x3e4ce43cc84ab338u64, - 0x3e4c01d7aac3bd91u64, 0x3e45c58d07961060u64, 0x3e3628bcf941456eu64, 0x3e4c58b2a8461cd2u64, - 0x3e33071282fb989au64, 0x3e420dab6a80f09cu64, 0x3e44f8d84c397b1eu64, 0x3e40d0ee08599e48u64, - 0x3e1d68787e37da36u64, 0x3e366187d591bafcu64, 0x3e22346600bae772u64, 0x3e390377d0d61b8eu64, - 0x3e4f5e0dd966b907u64, 0x3e49023cb79a00e2u64, 0x3e44e05158c28ad8u64, 0x3e3bfa7b08b18ae4u64, - 0x3e4ef1e63db35f67u64, 0x3e0ec2ae39493d4fu64, 0x3e40afe930ab2fa0u64, 0x3e225ff8a1810dd4u64, - 0x3e469743fb1a71a5u64, 0x3e5f9cc676785571u64, 0x3e5b524da4cbf982u64, 0x3e5a4c8b381535b8u64, - 0x3e5839be809caf2cu64, 0x3e50968a1cb82c13u64, 0x3e5eae6a41723fb5u64, 0x3e5d9c29a380a4dbu64, - 0x3e4094aa0ada625eu64, 0x3e5973ad6fc108cau64, 0x3e4747322fdbab97u64, 0x3e593692fa9d4221u64, - 0x3e5c5a992dfbc7d9u64, 0x3e4e1f33e102387au64, 0x3e464fbef14c048cu64, 0x3e4490f513ca5e3bu64, - 0x3e37a6af4d4c799du64, 0x3e57574c1c07398fu64, 0x3e57b133417f8c1cu64, 0x3e5feb9e0c176514u64, - 0x3e419f25bb3172f7u64, 0x3e45f68a7bbfb852u64, 0x3e5ee278497929f1u64, 0x3e5ccee006109d58u64, - 0x3e5ce081a07bd8b3u64, 0x3e570e12981817b8u64, 0x3e292ab6d93503d0u64, 0x3e58cb7dd7c3b61eu64, - 0x3e4efafd0a0b78dau64, 0x3e5e907267c4288eu64, 0x3e5d31ef96780875u64, 0x3e23430dfcd2ad50u64, - 0x3e344d88d75bc1f9u64, 0x3e5bec0f055e04fcu64, 0x3e5d85611590b9adu64, 0x3df320568e583229u64, - 0x3e5a891d1772f538u64, 0x3e22edc9dabba74du64, 0x3e4b9009a1015086u64, 0x3e52a12a8c5b1a19u64, - 0x3e3a7885f0fdac85u64, 0x3e5f4ffcd43ac691u64, 0x3e52243ae2640aadu64, 0x3e546513299035d3u64, - 0x3e5b39c3a62dd725u64, 0x3e5ba6dd40049f51u64, 0x3e451d1ed7177409u64, 0x3e5cb0f2fd7f5216u64, - 0x3e3ab150cd4e2213u64, 0x3e5cfd7bf3193844u64, 0x3e53fff8455f1dbdu64, 0x3e5fee640b905fc9u64, - 0x3e54e2adf548084cu64, 0x3e3b597adc1ecdd2u64, 0x3e4345bd096d3a75u64, 0x3e5101b9d2453c8bu64, - 0x3e508ce55cc8c979u64, 0x3e5bbf017e595f71u64, 0x3e37ce733bd393dcu64, 0x3e233bb0a503f8a1u64, - 0x3e30e2f613e85bd9u64, 0x3e5e67555a635b3cu64, 0x3e2ea88df73d5e8bu64, 0x3e3d17e03bda18a8u64, - 0x3e5b607d76044f7eu64, 0x3e52adc4e71bc2fcu64, 0x3e5f99dc7362d1d9u64, 0x3e5473fa008e6a6au64, - 0x3e2b75bb09cb0985u64, 0x3e5ea04dd10b9abau64, 0x3e5802d0d6979674u64, 0x3e174688ccd99094u64, - 0x3e496f16abb9df22u64, 0x3e46e66df2aa374fu64, 0x3e4e66525ea4550au64, 0x3e42d02f34f20cbdu64, - 0x3e46cfce65047188u64, 0x3e39b78c842d58b8u64, 0x3e4735e624c24bc9u64, 0x3e47eba1f7dd1adfu64, - 0x3e586b3e59f65355u64, 0x3e1ce38e637f1b4du64, 0x3e58d82ec919edc7u64, 0x3e4c52648ddcfa37u64, - 0x3e52482ceae1ac12u64, 0x3e55a312311aba4fu64, 0x3e411e236329f225u64, 0x3e5b48c8cd2f246cu64, - 0x3e6efa39ef35793cu64, -]; -#[rustfmt::skip] -pub static UCRT_POW11_INVC: [u64; 128] = [ - 0x3ff6a00000000000u64, 0x3ff6800000000000u64, 0x3ff6600000000000u64, 0x3ff6400000000000u64, - 0x3ff6200000000000u64, 0x3ff6000000000000u64, 0x3ff5e00000000000u64, 0x3ff5c00000000000u64, - 0x3ff5a00000000000u64, 0x3ff5800000000000u64, 0x3ff5600000000000u64, 0x3ff5600000000000u64, - 0x3ff5400000000000u64, 0x3ff5200000000000u64, 0x3ff5000000000000u64, 0x3ff4e00000000000u64, - 0x3ff4c00000000000u64, 0x3ff4a00000000000u64, 0x3ff4a00000000000u64, 0x3ff4800000000000u64, - 0x3ff4600000000000u64, 0x3ff4400000000000u64, 0x3ff4200000000000u64, 0x3ff4000000000000u64, - 0x3ff4000000000000u64, 0x3ff3e00000000000u64, 0x3ff3c00000000000u64, 0x3ff3a00000000000u64, - 0x3ff3a00000000000u64, 0x3ff3800000000000u64, 0x3ff3600000000000u64, 0x3ff3400000000000u64, - 0x3ff3400000000000u64, 0x3ff3200000000000u64, 0x3ff3000000000000u64, 0x3ff3000000000000u64, - 0x3ff2e00000000000u64, 0x3ff2c00000000000u64, 0x3ff2c00000000000u64, 0x3ff2a00000000000u64, - 0x3ff2800000000000u64, 0x3ff2600000000000u64, 0x3ff2600000000000u64, 0x3ff2400000000000u64, - 0x3ff2400000000000u64, 0x3ff2200000000000u64, 0x3ff2000000000000u64, 0x3ff2000000000000u64, - 0x3ff1e00000000000u64, 0x3ff1c00000000000u64, 0x3ff1c00000000000u64, 0x3ff1a00000000000u64, - 0x3ff1a00000000000u64, 0x3ff1800000000000u64, 0x3ff1600000000000u64, 0x3ff1600000000000u64, - 0x3ff1400000000000u64, 0x3ff1400000000000u64, 0x3ff1200000000000u64, 0x3ff1000000000000u64, - 0x3ff1000000000000u64, 0x3ff0e00000000000u64, 0x3ff0e00000000000u64, 0x3ff0c00000000000u64, - 0x3ff0c00000000000u64, 0x3ff0a00000000000u64, 0x3ff0a00000000000u64, 0x3ff0800000000000u64, - 0x3ff0800000000000u64, 0x3ff0600000000000u64, 0x3ff0400000000000u64, 0x3ff0400000000000u64, - 0x3ff0200000000000u64, 0x3ff0200000000000u64, 0x3ff0000000000000u64, 0x3ff0000000000000u64, - 0x3fefc00000000000u64, 0x3fef800000000000u64, 0x3fef400000000000u64, 0x3fef000000000000u64, - 0x3feec00000000000u64, 0x3fee800000000000u64, 0x3fee400000000000u64, 0x3fee200000000000u64, - 0x3fede00000000000u64, 0x3feda00000000000u64, 0x3fed600000000000u64, 0x3fed400000000000u64, - 0x3fed000000000000u64, 0x3fecc00000000000u64, 0x3feca00000000000u64, 0x3fec600000000000u64, - 0x3fec400000000000u64, 0x3fec000000000000u64, 0x3febe00000000000u64, 0x3feba00000000000u64, - 0x3feb800000000000u64, 0x3feb400000000000u64, 0x3feb200000000000u64, 0x3feae00000000000u64, - 0x3feac00000000000u64, 0x3feaa00000000000u64, 0x3fea600000000000u64, 0x3fea400000000000u64, - 0x3fea000000000000u64, 0x3fe9e00000000000u64, 0x3fe9c00000000000u64, 0x3fe9a00000000000u64, - 0x3fe9600000000000u64, 0x3fe9400000000000u64, 0x3fe9200000000000u64, 0x3fe9000000000000u64, - 0x3fe8c00000000000u64, 0x3fe8a00000000000u64, 0x3fe8800000000000u64, 0x3fe8600000000000u64, - 0x3fe8400000000000u64, 0x3fe8200000000000u64, 0x3fe7e00000000000u64, 0x3fe7c00000000000u64, - 0x3fe7a00000000000u64, 0x3fe7800000000000u64, 0x3fe7600000000000u64, 0x3fe7400000000000u64, - 0x3fe7200000000000u64, 0x3fe7000000000000u64, 0x3fe6e00000000000u64, 0x3fe6c00000000000u64, -]; -#[rustfmt::skip] -pub static UCRT_POW11_LOGC: [u64; 128] = [ - 0xbfd62c82f2b9c800u64, 0xbfd5d1bdbf580800u64, 0xbfd5767717455800u64, 0xbfd51aad872df800u64, - 0xbfd4be5f95777800u64, 0xbfd4618bc21c6000u64, 0xbfd404308686a800u64, 0xbfd3a64c55694800u64, - 0xbfd347dd9a988000u64, 0xbfd2e8e2bae12000u64, 0xbfd2895a13de8800u64, 0xbfd2895a13de8800u64, - 0xbfd22941fbcf7800u64, 0xbfd1c898c1699800u64, 0xbfd1675cababa800u64, 0xbfd1058bf9ae4800u64, - 0xbfd0a324e2739000u64, 0xbfd0402594b4d000u64, 0xbfd0402594b4d000u64, 0xbfcfb9186d5e4000u64, - 0xbfcef0adcbdc6000u64, 0xbfce27076e2af000u64, 0xbfcd5c216b4fc000u64, 0xbfcc8ff7c79aa000u64, - 0xbfcc8ff7c79aa000u64, 0xbfcbc286742d9000u64, 0xbfcaf3c94e80c000u64, 0xbfca23bc1fe2b000u64, - 0xbfca23bc1fe2b000u64, 0xbfc9525a9cf45000u64, 0xbfc87fa06520d000u64, 0xbfc7ab890210e000u64, - 0xbfc7ab890210e000u64, 0xbfc6d60fe719d000u64, 0xbfc5ff3070a79000u64, 0xbfc5ff3070a79000u64, - 0xbfc526e5e3a1b000u64, 0xbfc44d2b6ccb8000u64, 0xbfc44d2b6ccb8000u64, 0xbfc371fc201e9000u64, - 0xbfc29552f81ff000u64, 0xbfc1b72ad52f6000u64, 0xbfc1b72ad52f6000u64, 0xbfc0d77e7cd09000u64, - 0xbfc0d77e7cd09000u64, 0xbfbfec9131dbe000u64, 0xbfbe27076e2b0000u64, 0xbfbe27076e2b0000u64, - 0xbfbc5e548f5bc000u64, 0xbfba926d3a4ae000u64, 0xbfba926d3a4ae000u64, 0xbfb8c345d631a000u64, - 0xbfb8c345d631a000u64, 0xbfb6f0d28ae56000u64, 0xbfb51b073f062000u64, 0xbfb51b073f062000u64, - 0xbfb341d7961be000u64, 0xbfb341d7961be000u64, 0xbfb16536eea38000u64, 0xbfaf0a30c0118000u64, - 0xbfaf0a30c0118000u64, 0xbfab42dd71198000u64, 0xbfab42dd71198000u64, 0xbfa77458f632c000u64, - 0xbfa77458f632c000u64, 0xbfa39e87b9fec000u64, 0xbfa39e87b9fec000u64, 0xbf9f829b0e780000u64, - 0xbf9f829b0e780000u64, 0xbf97b91b07d58000u64, 0xbf8fc0a8b0fc0000u64, 0xbf8fc0a8b0fc0000u64, - 0xbf7fe02a6b100000u64, 0xbf7fe02a6b100000u64, 0x0000000000000000u64, 0x0000000000000000u64, - 0x3f80101575890000u64, 0x3f90205658938000u64, 0x3f98492528c90000u64, 0x3fa0415d89e74000u64, - 0x3fa466aed42e0000u64, 0x3fa894aa149fc000u64, 0x3faccb73cdddc000u64, 0x3faeea31c006c000u64, - 0x3fb1973bd1466000u64, 0x3fb3bdf5a7d1e000u64, 0x3fb5e95a4d97a000u64, 0x3fb700d30aeac000u64, - 0x3fb9335e5d594000u64, 0x3fbb6ac88dad6000u64, 0x3fbc885801bc4000u64, 0x3fbec739830a2000u64, - 0x3fbfe89139dbe000u64, 0x3fc1178e8227e000u64, 0x3fc1aa2b7e23f000u64, 0x3fc2d1610c868000u64, - 0x3fc365fcb0159000u64, 0x3fc4913d8333b000u64, 0x3fc527e5e4a1b000u64, 0x3fc6574ebe8c1000u64, - 0x3fc6f0128b757000u64, 0x3fc7898d85445000u64, 0x3fc8beafeb390000u64, 0x3fc95a5adcf70000u64, - 0x3fca93ed3c8ae000u64, 0x3fcb31d8575bd000u64, 0x3fcbd087383be000u64, 0x3fcc6ffbc6f01000u64, - 0x3fcdb13db0d49000u64, 0x3fce530effe71000u64, 0x3fcef5ade4dd0000u64, 0x3fcf991c6cb3b000u64, - 0x3fd07138604d5800u64, 0x3fd0c42d67616000u64, 0x3fd1178e8227e800u64, 0x3fd16b5ccbacf800u64, - 0x3fd1bf99635a6800u64, 0x3fd214456d0eb800u64, 0x3fd2bef07cdc9000u64, 0x3fd314f1e1d36000u64, - 0x3fd36b6776be1000u64, 0x3fd3c25277333000u64, 0x3fd419b423d5e800u64, 0x3fd4718dc271c800u64, - 0x3fd4c9e09e173000u64, 0x3fd522ae0738a000u64, 0x3fd57bf753c8d000u64, 0x3fd5d5bddf596000u64, -]; -#[rustfmt::skip] -pub static UCRT_POW11_LOGCTAIL: [u64; 128] = [ - 0x3cfab42428375680u64, 0xbd1ca508d8e0f720u64, 0xbd2362a4d5b6506du64, 0xbce684e49eb067d5u64, - 0xbd041b6993293ee0u64, 0x3d13d82f484c84ccu64, 0x3cdc42f3ed820b3au64, 0x3d20b1c686519460u64, - 0x3d25594dd4c58092u64, 0x3d267b1e99b72bd8u64, 0x3d15ca14b6cfb03fu64, 0x3d15ca14b6cfb03fu64, - 0xbd165a242853da76u64, 0xbd1fafbc68e75404u64, 0x3d1f1fc63382a8f0u64, 0xbd26a8c4fd055a66u64, - 0xbd0c6bee7ef4030eu64, 0xbcf036b89ef42d7fu64, 0xbcf036b89ef42d7fu64, 0x3d0d572aab993c87u64, - 0x3d2b26b79c86af24u64, 0xbd172f4f543fff10u64, 0x3d21ba91bbca681bu64, 0x3d27794f689f8434u64, - 0x3d27794f689f8434u64, 0x3d194eb0318bb78fu64, 0x3cba4e633fcd9066u64, 0xbd258c64dc46c1eau64, - 0xbd258c64dc46c1eau64, 0xbd2ad1d904c1d4e3u64, 0x3d2bbdbf7fdbfa09u64, 0x3d2bdb9072534a58u64, - 0x3d2bdb9072534a58u64, 0xbd10e46aa3b2e266u64, 0xbd1e9e439f105039u64, 0xbd1e9e439f105039u64, - 0xbd20de8b90075b8fu64, 0x3d170cc16135783cu64, 0x3d170cc16135783cu64, 0x3cf178864d27543au64, - 0xbd248d301771c408u64, 0xbd2e80a41811a396u64, 0xbd2e80a41811a396u64, 0x3d0a699688e85bf4u64, - 0x3d0a699688e85bf4u64, 0xbd2575545ca333f2u64, 0x3d2a342c2af0003cu64, 0x3d2a342c2af0003cu64, - 0xbd1d0c57585fbe06u64, 0x3d253935e85baac8u64, 0x3d253935e85baac8u64, 0x3d137c294d2f5668u64, - 0x3d137c294d2f5668u64, 0xbd269737c93373dau64, 0x3d1f025b61c65e57u64, 0x3d1f025b61c65e57u64, - 0x3d2c5edaccf913dfu64, 0x3d2c5edaccf913dfu64, 0x3d147c5e768fa309u64, 0x3d2d599e83368e91u64, - 0x3d2d599e83368e91u64, 0x3d1c827ae5d6704cu64, 0x3d1c827ae5d6704cu64, 0xbd2cfc4634f2a1eeu64, - 0xbd2cfc4634f2a1eeu64, 0x3cf502b7f526feaau64, 0x3cf502b7f526feaau64, 0xbd2980267c7e09e4u64, - 0xbd2980267c7e09e4u64, 0xbd288d5493faa639u64, 0xbcdf1e7cf6d3a69cu64, 0xbcdf1e7cf6d3a69cu64, - 0xbd19e23f0dda40e4u64, 0xbd19e23f0dda40e4u64, 0x0000000000000000u64, 0x0000000000000000u64, - 0xbd10c76b999d2be8u64, 0xbd23dc5b06e2f7d2u64, 0xbd2aa0ba325a0c34u64, 0x3d0111c05cf1d753u64, - 0xbd2c167375bdfd28u64, 0xbd197995d05a267du64, 0xbd1a68f247d82807u64, 0xbd0e113e4fc93b7bu64, - 0xbd25325d560d9e9bu64, 0x3d2cc85ea5db4ed7u64, 0xbd2c69063c5d1d1eu64, 0x3cec1e8da99ded32u64, - 0x3d23115c3abd47dau64, 0xbd1390802bf768e5u64, 0x3d2646d1c65aacd3u64, 0xbd2dc068afe645e0u64, - 0xbd2534d64fa10afdu64, 0x3d21ef78ce2d07f2u64, 0x3d2ca78e44389934u64, 0x3d039d6ccb81b4a1u64, - 0x3cc62fa8234b7289u64, 0x3d25837954fdb678u64, 0x3d2633e8e5697dc7u64, 0x3d19cf8b2c3c2e78u64, - 0xbd25118de59c21e1u64, 0xbd1c661070914305u64, 0xbd073d54aae92cd1u64, 0x3d07f22858a0ff6fu64, - 0xbd28724350562169u64, 0xbd0c358d4eace1aau64, 0xbd2d4bc4595412b6u64, 0xbcf1ec72c5962bd2u64, - 0xbd2aff2af715b035u64, 0x3cc212276041f430u64, 0xbcca211565bb8e11u64, 0x3d1bcbecca0cdf30u64, - 0x3cf89cdb16ed4e91u64, 0x3d27188b163ceae9u64, 0xbd2c210e63a5f01cu64, 0x3d2b9acdf7a51681u64, - 0x3d2ca6ed5147bdb7u64, 0x3d0a87deba46baeau64, 0x3d2a9cfa4a5004f4u64, 0xbd28e27ad3213cb8u64, - 0x3d116ecdb0f177c8u64, 0x3d183b54b606bd5cu64, 0x3d08e436ec90e09du64, 0xbd2f27ce0967d675u64, - 0xbd2e20891b0ad8a4u64, 0x3d2ebe708164c759u64, 0x3d1fadedee5d40efu64, 0xbd0a0b2a08a465dcu64, -]; -#[rustfmt::skip] -pub static UCRT_POW11_EXP: [u64; 512] = [ - 0x0000000000000000u64, 0x3ff0000000000000u64, 0xbc84e82fc61851acu64, 0x3feffb1afa5abcbfu64, - 0x3c9b3b4f1a88bf6eu64, 0x3feff63da9fb3335u64, 0xbc82985dd8521d32u64, 0x3feff168143b0281u64, - 0xbc7160139cd8dc5du64, 0x3fefec9a3e778061u64, 0x3c651e617061bfbdu64, 0x3fefe7d42e11bbccu64, - 0xbc905e7a108766d1u64, 0x3fefe315e86e7f85u64, 0x3c845fad437fa426u64, 0x3fefde5f72f654b1u64, - 0x3c8cd2523567f613u64, 0x3fefd9b0d3158574u64, 0xbc954529642b232fu64, 0x3fefd50a0e3c1f89u64, - 0xbc8bce8023f98efau64, 0x3fefd06b29ddf6deu64, 0x3c8293708ef5c32eu64, 0x3fefcbd42b72a836u64, - 0x3c60f74e61e6c861u64, 0x3fefc74518759bc8u64, 0xbc95b9280905b2a4u64, 0x3fefc2bdf66607e0u64, - 0x3c90a3e45b33d399u64, 0x3fefbe3ecac6f383u64, 0x3c84f31f32c4b7e7u64, 0x3fefb9c79b1f3919u64, - 0x3c979aa65d837b6du64, 0x3fefb5586cf9890fu64, 0x3c9407fb30d06420u64, 0x3fefb0f145e46c85u64, - 0x3c8eb51a92fdeffcu64, 0x3fefac922b7247f7u64, 0xbc9a5d04b3b9911bu64, 0x3fefa83b23395decu64, - 0x3c3ebe3d702f9cd1u64, 0x3fefa3ec32d3d1a2u64, 0xbc937a01f0739546u64, 0x3fef9fa55fdfa9c5u64, - 0xbc6a033489906e0bu64, 0x3fef9b66affed31bu64, 0x3c8b8268b04ef0a5u64, 0x3fef973028d7233eu64, - 0xbc9556522a2fbd0eu64, 0x3fef9301d0125b51u64, 0xbc9ac46e44a2ebccu64, 0x3fef8edbab5e2ab6u64, - 0xbc5080ef8c4eea55u64, 0x3fef8abdc06c31ccu64, 0xbc65704e90c9f860u64, 0x3fef86a814f204abu64, - 0xbc91c923b9d5f416u64, 0x3fef829aaea92de0u64, 0xbc897cea57e46280u64, 0x3fef7e95934f312eu64, - 0x3c80d3e3e95c55afu64, 0x3fef7a98c8a58e51u64, 0x3c56f01429e2b9d2u64, 0x3fef76a45471c3c2u64, - 0xbc801b15eaa59348u64, 0x3fef72b83c7d517bu64, 0x3c6e653b2459034bu64, 0x3fef6ed48695bbc0u64, - 0xbc8f1ff055de323du64, 0x3fef6af9388c8deau64, 0x3c92cc7ea345b7dcu64, 0x3fef672658375d2fu64, - 0x3c8b898c3f1353bfu64, 0x3fef635beb6fcb75u64, 0x3c957bfb2876ea9eu64, 0x3fef5f99f8138a1cu64, - 0xbc96d99c7611eb26u64, 0x3fef5be084045cd4u64, 0x3c8cdc1873af2155u64, 0x3fef582f95281c6bu64, - 0x3c9aecf73e3a2f60u64, 0x3fef54873168b9aau64, 0xbc9493684653a131u64, 0x3fef50e75eb44027u64, - 0xbc8fe782cb86389du64, 0x3fef4d5022fcd91du64, 0xbc98e2899077520au64, 0x3fef49c18438ce4du64, - 0x3c8a6f4144a6c38du64, 0x3fef463b88628cd6u64, 0x3c9120fcd4f59273u64, 0x3fef42be3578a819u64, - 0x3c807a05b0e4047du64, 0x3fef3f49917ddc96u64, 0x3c89b788c188c9b8u64, 0x3fef3bdda27912d1u64, - 0x3c968efde3a8a894u64, 0x3fef387a6e756238u64, 0x3c877afbca90ef84u64, 0x3fef351ffb82140au64, - 0x3c875e18f274487du64, 0x3fef31ce4fb2a63fu64, 0x3c91512f082876eeu64, 0x3fef2e85711ece75u64, - 0x3c80472b981fe7f2u64, 0x3fef2b4565e27cddu64, 0x3c9a02f0c7d75ec6u64, 0x3fef280e341ddf29u64, - 0xbc96b87b3f71085eu64, 0x3fef24dfe1f56381u64, 0xbc803297e78260bfu64, 0x3fef21ba7591bb70u64, - 0x3c82f7e16d09ab31u64, 0x3fef1e9df51fdee1u64, 0xbc95b77e5ccd9fbfu64, 0x3fef1b8a66d10f13u64, - 0xbc3d219b1a6fbffau64, 0x3fef187fd0dad990u64, 0xbc91e75c40b4251eu64, 0x3fef157e39771b2fu64, - 0x3c8b3782720c0ab4u64, 0x3fef1285a6e4030bu64, 0x3c98a911f1f7785au64, 0x3fef0f961f641589u64, - 0x3c6e149289cecb8fu64, 0x3fef0cafa93e2f56u64, 0xbc61e7c998db7dbbu64, 0x3fef09d24abd886bu64, - 0x3c834d754db0abb6u64, 0x3fef06fe0a31b715u64, 0x3c85425c11faadf4u64, 0x3fef0432edeeb2fdu64, - 0x3c864201e2ac744cu64, 0x3fef0170fc4cd831u64, 0xbc979517a03e2847u64, 0x3feefeb83ba8ea32u64, - 0x3c8fdd395dd3f84au64, 0x3feefc08b26416ffu64, 0xbc800e2a46da4beeu64, 0x3feef96266e3fa2du64, - 0xbc86a3803b8e5b04u64, 0x3feef6c55f929ff1u64, 0xbc87430803972b34u64, 0x3feef431a2de883bu64, - 0xbc924aedcc4b5068u64, 0x3feef1a7373aa9cbu64, 0xbc954de30ae02d94u64, 0x3feeef26231e754au64, - 0xbc9907f81b512d8eu64, 0x3feeecae6d05d866u64, 0xbc94f2487e1c03ecu64, 0x3feeea401b7140efu64, - 0xbc71d1e83e9436d2u64, 0x3feee7db34e59ff7u64, 0x3c914a5432fcb2f4u64, 0x3feee57fbfec6cf4u64, - 0xbc991919b3ce1b15u64, 0x3feee32dc313a8e5u64, 0x3c79c3bba5562a2fu64, 0x3feee0e544ede173u64, - 0x3c859f48a72a4c6du64, 0x3feedea64c123422u64, 0xbc85a71612e21658u64, 0x3feedc70df1c5175u64, - 0xbc9312607a28698au64, 0x3feeda4504ac801cu64, 0x3c86421f6f1d24d6u64, 0x3feed822c367a024u64, - 0xbc58a78f4817895bu64, 0x3feed60a21f72e2au64, 0xbc9348a6815fce65u64, 0x3feed3fb2709468au64, - 0xbc7c2c9b67499a1bu64, 0x3feed1f5d950a897u64, 0x3c835c43984d9871u64, 0x3feecffa3f84b9d4u64, - 0x3c4363ed60c2ac11u64, 0x3feece086061892du64, 0xbc632afc8d9473a0u64, 0x3feecc2042a7d232u64, - 0x3c9666093b0664efu64, 0x3feeca41ed1d0057u64, 0xbc95fc5e44de020eu64, 0x3feec86d668b3237u64, - 0x3c6ecce1daa10379u64, 0x3feec6a2b5c13cd0u64, 0xbc7ea0148327c42fu64, 0x3feec4e1e192aed2u64, - 0x3c93ff8e3f0f1230u64, 0x3feec32af0d7d3deu64, 0xbc7a843ad1a88022u64, 0x3feec17dea6db7d7u64, - 0x3c7690cebb7aafb0u64, 0x3feebfdad5362a27u64, 0x3c892ca3bf144e63u64, 0x3feebe41b817c114u64, - 0x3c931dbdeb54e077u64, 0x3feebcb299fddd0du64, 0xbc902c99b04aa8b0u64, 0x3feebb2d81d8abffu64, - 0xbc8f94340071a38eu64, 0x3feeb9b2769d2ca7u64, 0x3c73e34f67e67118u64, 0x3feeb8417f4531eeu64, - 0xbc87deccdc93a349u64, 0x3feeb6daa2cf6642u64, 0xbc75a3b1197ba0f0u64, 0x3feeb57de83f4eefu64, - 0xbc78dec6bd0f385fu64, 0x3feeb42b569d4f82u64, 0x3c81bd2888075068u64, 0x3feeb2e2f4f6ad27u64, - 0xbc861246ec7b5cf6u64, 0x3feeb1a4ca5d920fu64, 0xbc896be8ae89ef8fu64, 0x3feeb070dde910d2u64, - 0x3c93350518fdd78eu64, 0x3feeaf4736b527dau64, 0xbc88e6ac90348602u64, 0x3feeae27dbe2c4cfu64, - 0x3c7b98b72f8a9b05u64, 0x3feead12d497c7fdu64, 0xbc91af7f1365c3acu64, 0x3feeac0827ff07ccu64, - 0x3c9063e1e21c5409u64, 0x3feeab07dd485429u64, 0xbc943a3540d1898au64, 0x3feeaa11fba87a03u64, - 0x3c34c7855019c6eau64, 0x3feea9268a5946b7u64, 0xbc951f58ddaa8090u64, 0x3feea84590998b93u64, - 0x3c9432e62b64c035u64, 0x3feea76f15ad2148u64, 0xbc82e1648e50a17cu64, 0x3feea6a320dceb71u64, - 0xbc8ce44a6199769fu64, 0x3feea5e1b976dc09u64, 0x3c95f30eda98a575u64, 0x3feea52ae6cdf6f4u64, - 0xbc8c33c53bef4da8u64, 0x3feea47eb03a5585u64, 0x3c917ecda8a72159u64, 0x3feea3dd1d1929fdu64, - 0xbc845378892be9aeu64, 0x3feea34634ccc320u64, 0xbc9345f3cee1ae6eu64, 0x3feea2b9febc8fb7u64, - 0xbc93cedd78565858u64, 0x3feea23882552225u64, 0xbc85c33fdf910406u64, 0x3feea1c1c70833f6u64, - 0x3c5710aa807e1964u64, 0x3feea155d44ca973u64, 0x3c81079ab5789604u64, 0x3feea0f4b19e9538u64, - 0xbc93b3efbf5e2228u64, 0x3feea09e667f3bcdu64, 0x3c727df161cd7778u64, 0x3feea052fa75173eu64, - 0xbc6a12ad8734b982u64, 0x3feea012750bdabfu64, 0x3c93f9924a05b767u64, 0x3fee9fdcddd47645u64, - 0xbc6367efb86da9eeu64, 0x3fee9fb23c651a2fu64, 0xbc87557939a8b5efu64, 0x3fee9f9298593ae5u64, - 0xbc80dc3d54e08851u64, 0x3fee9f7df9519484u64, 0x3c51ed2f56fa9d1au64, 0x3fee9f7466f42e87u64, - 0xbc781f647e5a3ecfu64, 0x3fee9f75e8ec5f74u64, 0xbc88e67a9006c909u64, 0x3fee9f8286ead08au64, - 0xbc86ee4ac08b7db0u64, 0x3fee9f9a48a58174u64, 0x3c86597566977ac8u64, 0x3fee9fbd35d7cbfdu64, - 0xbc8619321e55e68au64, 0x3fee9feb564267c9u64, 0x3c92c0b7028a5c3au64, 0x3feea024b1ab6e09u64, - 0x3c909ccb5e09d4d3u64, 0x3feea0694fde5d3fu64, 0x3c8a30faf49cc78cu64, 0x3feea0b938ac1cf6u64, - 0xbc7b32dcb94da51du64, 0x3feea11473eb0187u64, 0xbc92dad3519d7b5bu64, 0x3feea17b0976cfdbu64, - 0x3c94ecfd5467c06bu64, 0x3feea1ed0130c132u64, 0x3c87d51410fd15c2u64, 0x3feea26a62ff86f0u64, - 0x3c65ebe1abd66c55u64, 0x3feea2f336cf4e62u64, 0xbc760a3629969871u64, 0x3feea3878491c491u64, - 0xbc88a1c52fb3cf42u64, 0x3feea427543e1a12u64, 0x3c8b18c6e3fdef5du64, 0x3feea4d2add106d9u64, - 0xbc9369b6f13b3734u64, 0x3feea589994cce13u64, 0x3c90ec1ddcb1390au64, 0x3feea64c1eb941f7u64, - 0xbc805e843a19ff1eu64, 0x3feea71a4623c7adu64, 0xbc522cea4f3afa1eu64, 0x3feea7f4179f5b21u64, - 0xbc94d450d872576eu64, 0x3feea8d99b4492edu64, 0x3c7c88549b958471u64, 0x3feea9cad931a436u64, - 0x3c90ad675b0e8a00u64, 0x3feeaac7d98a6699u64, 0x3c931143962f7877u64, 0x3feeabd0a478580fu64, - 0x3c8db72fc1f0eab4u64, 0x3feeace5422aa0dbu64, 0x3c93e9e96f112479u64, 0x3feeae05bad61778u64, - 0xbc65b6609cc5e7ffu64, 0x3feeaf3216b5448cu64, 0xbc8dac42a4a38df0u64, 0x3feeb06a5e0866d9u64, - 0x3c7bf68359f35f44u64, 0x3feeb1ae99157736u64, 0x3c8b99dd98b1ed84u64, 0x3feeb2fed0282c8au64, - 0xbc93091fa71e3d83u64, 0x3feeb45b0b91ffc6u64, 0xbc7885ad50cbb750u64, 0x3feeb5c353aa2fe2u64, - 0xbc5da9b88b6c1e29u64, 0x3feeb737b0cdc5e5u64, 0xbc82d5e85f3e0301u64, 0x3feeb8b82b5f98e5u64, - 0xbc6c23f97c90b959u64, 0x3feeba44cbc8520fu64, 0xbc51669428996971u64, 0x3feebbdd9a7670b3u64, - 0xbc92434322f4f9aau64, 0x3feebd829fde4e50u64, 0x3c71f2b2c1c4c014u64, 0x3feebf33e47a22a2u64, - 0xbc85ca6cd7668e4bu64, 0x3feec0f170ca07bau64, 0xbc9294f304f166b6u64, 0x3feec2bb4d53fe0du64, - 0x3c71affc2b91ce27u64, 0x3feec49182a3f090u64, 0xbc8a1e58414c07d3u64, 0x3feec674194bb8d5u64, - 0x3c6dd235e10a73bbu64, 0x3feec86319e32323u64, 0xbc79740b58a20091u64, 0x3feeca5e8d07f29eu64, - 0xbc87c50422622263u64, 0x3feecc667b5de565u64, 0x3c9165830a2b96c2u64, 0x3feece7aed8eb8bbu64, - 0x3c8b1c86e3e231d5u64, 0x3feed09bec4a2d33u64, 0xbc903d5cbe27874bu64, 0x3feed2c980460ad8u64, - 0xbc91bbd1d3bcbb15u64, 0x3feed503b23e255du64, 0x3c5986178980fce0u64, 0x3feed74a8af46052u64, - 0x3c90cc319cee31d2u64, 0x3feed99e1330b358u64, 0xbc89472975b1f2a5u64, 0x3feedbfe53c12e59u64, - 0x3c8469846e735ab3u64, 0x3feede6b5579fdbfu64, 0x3c7d8157a34b7e7fu64, 0x3feee0e521356ebau64, - 0xbc82dfcd978e9db4u64, 0x3feee36bbfd3f37au64, 0x3c8c8a4e231ebb7du64, 0x3feee5ff3a3c2774u64, - 0x3c8c1a7792cb3387u64, 0x3feee89f995ad3adu64, 0xbc888c8d11a142e5u64, 0x3feeeb4ce622f2ffu64, - 0xbc907b8f4ad1d9fau64, 0x3feeee07298db666u64, 0x3c889c2ea41433c7u64, 0x3feef0ce6c9a8952u64, - 0xbc55c3d956dcaebau64, 0x3feef3a2b84f15fbu64, 0xbc7274aedac8ff80u64, 0x3feef68415b749b1u64, - 0xbc90a40e3da6f640u64, 0x3feef9728de5593au64, 0x3c85c620ce76df06u64, 0x3feefc6e29f1c52au64, - 0xbc68d6f438ad9334u64, 0x3feeff76f2fb5e47u64, 0xbc8fda52e1b51e41u64, 0x3fef028cf22749e4u64, - 0xbc91eee26b588a35u64, 0x3fef05b030a1064au64, 0xbc32141a7b3e2cd8u64, 0x3fef08e0b79a6f1fu64, - 0x3c74ffd70a5fddcdu64, 0x3fef0c1e904bc1d2u64, 0xbc302899507554e5u64, 0x3fef0f69c3f3a207u64, - 0xbc91bdfbfa9298acu64, 0x3fef12c25bd71e09u64, 0xbc80dda2d4c0010cu64, 0x3fef16286141b33du64, - 0x3c736eae30af0cb3u64, 0x3fef199bdd85529cu64, 0xbc8a007daadf8d68u64, 0x3fef1d1cd9fa652cu64, - 0x3c8ee3325c9ffd94u64, 0x3fef20ab5fffd07au64, 0x3c836909391181d3u64, 0x3fef244778fafb22u64, - 0x3c84e08fd10959acu64, 0x3fef27f12e57d14bu64, 0xbc811cd7dbdf9547u64, 0x3fef2ba88988c933u64, - 0x3c63cdaf384e1a67u64, 0x3fef2f6d9406e7b5u64, 0xbc7ac28b7bef6621u64, 0x3fef33405751c4dbu64, - 0x3c676b2c6c921968u64, 0x3fef3720dcef9069u64, 0xbc7030587207b9e1u64, 0x3fef3b0f2e6d1675u64, - 0xbc808a1883ccb5d2u64, 0x3fef3f0b555dc3fau64, 0xbc8cc734592af7fcu64, 0x3fef43155b5bab74u64, - 0xbc8fad5d3ffffa6fu64, 0x3fef472d4a07897cu64, 0x3c87752a44f587e8u64, 0x3fef4b532b08c968u64, - 0xbc900dae3875a949u64, 0x3fef4f87080d89f2u64, 0x3c85b66fefeef52eu64, 0x3fef53c8eacaa1d6u64, - 0x3c74a385a63d07a7u64, 0x3fef5818dcfba487u64, 0x3c5159d9d908a96eu64, 0x3fef5c76e862e6d3u64, - 0xbc82919e2040220fu64, 0x3fef60e316c98398u64, 0x3c8c254d16117a68u64, 0x3fef655d71ff6075u64, - 0x3c8e5a50d5c192acu64, 0x3fef69e603db3285u64, 0xbc8d8c329fbd0e03u64, 0x3fef6e7cd63a8315u64, - 0x3c843a59ac016b4bu64, 0x3fef7321f301b460u64, 0xbc8ea6e6fbd5f2a6u64, 0x3fef77d5641c0658u64, - 0xbc82d52107b43e1fu64, 0x3fef7c97337b9b5fu64, 0xbc63e8e3eab2cbb4u64, 0x3fef81676b197d17u64, - 0xbc892ab93b470dc9u64, 0x3fef864614f5a129u64, 0xbc8b7966cd0d2cd9u64, 0x3fef8b333b16ee12u64, - 0x3c74b604603a88d3u64, 0x3fef902ee78b3ff6u64, 0xbc776caa4c2ff1cfu64, 0x3fef953924676d76u64, - 0x3c83c5ec519d7271u64, 0x3fef9a51fbc74c83u64, 0xbc81d5fc525d9940u64, 0x3fef9f7977cdb740u64, - 0xbc8ff7128fd391f0u64, 0x3fefa4afa2a490dau64, 0x3c855cd8aaea3d21u64, 0x3fefa9f4867cca6eu64, - 0xbc8dae98e223747du64, 0x3fefaf482d8e67f1u64, 0x3c8269947c2bed4au64, 0x3fefb4aaa2188510u64, - 0x3c8ec3bc41aa2008u64, 0x3fefba1bee615a27u64, 0xbc83b6137e9afe9eu64, 0x3fefbf9c1cb6412au64, - 0x3c842b94c3a9eb32u64, 0x3fefc52b376bba97u64, 0xbc69fa74878ba7c7u64, 0x3fefcac948dd7274u64, - 0x3c8a64a931d185eeu64, 0x3fefd0765b6e4540u64, 0x3c901f3a75ee0efeu64, 0x3fefd632798844f8u64, - 0xbc8e37bae43be3edu64, 0x3fefdbfdad9cbe14u64, 0xbc516a9ce6ed84fau64, 0x3fefe1d802243c89u64, - 0x3c77893b4d91cd9du64, 0x3fefe7c1819e90d8u64, 0xbc699c7db2effc76u64, 0x3fefedba3692d514u64, - 0x3c5305c14160cc89u64, 0x3feff3c22b8f71f1u64, 0x3c64b458677f9840u64, 0x3feff9d96b2a23d9u64, -]; -pub const UCRT_SINF_COUNT: i32 = 786i32; -pub const UCRT_SINF_STEP: i32 = 512i32; -pub const UCRT_SINF_WIDE_COUNT: i32 = 2536i32; -pub const UCRT_SINF_WIDE_STEP: i32 = 2048i32; -pub const UCRT_COSF_COUNT: i32 = 703i32; -pub const UCRT_COSF_STEP: i32 = 512i32; -pub const UCRT_COSF_WIDE_COUNT: i32 = 2424i32; -pub const UCRT_COSF_WIDE_STEP: i32 = 2048i32; -#[rustfmt::skip] -pub static UCRT_LOG_INV: [u64; 257] = [ - 0x4000000000000000u64, 0x3fffe01fe01fe020u64, 0x3fffc07f01fc07f0u64, 0x3fffa11caa01fa12u64, - 0x3fff81f81f81f820u64, 0x3fff6310aca0dbb5u64, 0x3fff44659e4a4271u64, 0x3fff25f644230ab5u64, - 0x3fff07c1f07c1f08u64, 0x3ffee9c7f8458e02u64, 0x3ffecc07b301ecc0u64, 0x3ffeae807aba01ebu64, - 0x3ffe9131abf0b767u64, 0x3ffe741aa59750e4u64, 0x3ffe573ac901e574u64, 0x3ffe3a9179dc1a73u64, - 0x3ffe1e1e1e1e1e1eu64, 0x3ffe01e01e01e01eu64, 0x3ffde5d6e3f8868au64, 0x3ffdca01dca01dcau64, - 0x3ffdae6076b981dbu64, 0x3ffd92f2231e7f8au64, 0x3ffd77b654b82c34u64, 0x3ffd5cac807572b2u64, - 0x3ffd41d41d41d41du64, 0x3ffd272ca3fc5b1au64, 0x3ffd0cb58f6ec074u64, 0x3ffcf26e5c44bfc6u64, - 0x3ffcd85689039b0bu64, 0x3ffcbe6d9601cbe7u64, 0x3ffca4b3055ee191u64, 0x3ffc8b265afb8a42u64, - 0x3ffc71c71c71c71cu64, 0x3ffc5894d10d4986u64, 0x3ffc3f8f01c3f8f0u64, 0x3ffc26b5392ea01cu64, - 0x3ffc0e070381c0e0u64, 0x3ffbf583ee868d8bu64, 0x3ffbdd2b899406f7u64, 0x3ffbc4fd65883e7bu64, - 0x3ffbacf914c1bad0u64, 0x3ffb951e2b18ff23u64, 0x3ffb7d6c3dda338bu64, 0x3ffb65e2e3beee05u64, - 0x3ffb4e81b4e81b4fu64, 0x3ffb37484ad806ceu64, 0x3ffb2036406c80d9u64, 0x3ffb094b31d922a4u64, - 0x3ffaf286bca1af28u64, 0x3ffadbe87f94905eu64, 0x3ffac5701ac5701bu64, 0x3ffaaf1d2f87ebfdu64, - 0x3ffa98ef606a63beu64, 0x3ffa82e65130e159u64, 0x3ffa6d01a6d01a6du64, 0x3ffa574107688a4au64, - 0x3ffa41a41a41a41au64, 0x3ffa2c2a87c51ca0u64, 0x3ffa16d3f97a4b02u64, 0x3ffa01a01a01a01au64, - 0x3ff9ec8e951033d9u64, 0x3ff9d79f176b682du64, 0x3ff9c2d14ee4a102u64, 0x3ff9ae24ea5510dau64, - 0x3ff999999999999au64, 0x3ff9852f0d8ec0ffu64, 0x3ff970e4f80cb872u64, 0x3ff95cbb0be377aeu64, - 0x3ff948b0fcd6e9e0u64, 0x3ff934c67f9b2ce6u64, 0x3ff920fb49d0e229u64, 0x3ff90d4f120190d5u64, - 0x3ff8f9c18f9c18fau64, 0x3ff8e6527af1373fu64, 0x3ff8d3018d3018d3u64, 0x3ff8bfce8062ff3au64, - 0x3ff8acb90f6bf3aau64, 0x3ff899c0f601899cu64, 0x3ff886e5f0abb04au64, 0x3ff87427bcc092b9u64, - 0x3ff8618618618618u64, 0x3ff84f00c2780614u64, 0x3ff83c977ab2beddu64, 0x3ff82a4a0182a4a0u64, - 0x3ff8181818181818u64, 0x3ff8060180601806u64, 0x3ff7f405fd017f40u64, 0x3ff7e225515a4f1du64, - 0x3ff7d05f417d05f4u64, 0x3ff7beb3922e017cu64, 0x3ff7ad2208e0ecc3u64, 0x3ff79baa6bb6398bu64, - 0x3ff78a4c8178a4c8u64, 0x3ff77908119ac60du64, 0x3ff767dce434a9b1u64, 0x3ff756cac201756du64, - 0x3ff745d1745d1746u64, 0x3ff734f0c541fe8du64, 0x3ff724287f46debcu64, 0x3ff713786d9c7c09u64, - 0x3ff702e05c0b8170u64, 0x3ff6f26016f26017u64, 0x3ff6e1f76b4337c7u64, 0x3ff6d1a62681c861u64, - 0x3ff6c16c16c16c17u64, 0x3ff6b1490aa31a3du64, 0x3ff6a13cd1537290u64, 0x3ff691473a88d0c0u64, - 0x3ff6816816816817u64, 0x3ff6719f3601671au64, 0x3ff661ec6a5122f9u64, 0x3ff6524f853b4aa3u64, - 0x3ff642c8590b2164u64, 0x3ff63356b88ac0deu64, 0x3ff623fa77016240u64, 0x3ff614b36831ae94u64, - 0x3ff6058160581606u64, 0x3ff5f66434292dfcu64, 0x3ff5e75bb8d015e7u64, 0x3ff5d867c3ece2a5u64, - 0x3ff5c9882b931057u64, 0x3ff5babcc647fa91u64, 0x3ff5ac056b015ac0u64, 0x3ff59d61f123ccaau64, - 0x3ff58ed2308158edu64, 0x3ff5805601580560u64, 0x3ff571ed3c506b3au64, 0x3ff56397ba7c52e2u64, - 0x3ff5555555555555u64, 0x3ff54725e6bb82feu64, 0x3ff5390948f40febu64, 0x3ff52aff56a8054bu64, - 0x3ff51d07eae2f815u64, 0x3ff50f22e111c4c5u64, 0x3ff5015015015015u64, 0x3ff4f38f62dd4c9bu64, - 0x3ff4e5e0a72f0539u64, 0x3ff4d843bedc2c4cu64, 0x3ff4cab88725af6eu64, 0x3ff4bd3edda68fe1u64, - 0x3ff4afd6a052bf5bu64, 0x3ff4a27fad76014au64, 0x3ff49539e3b2d067u64, 0x3ff4880522014880u64, - 0x3ff47ae147ae147bu64, 0x3ff46dce34596066u64, 0x3ff460cbc7f5cf9au64, 0x3ff453d9e2c776cau64, - 0x3ff446f86562d9fbu64, 0x3ff43a2730abee4du64, 0x3ff42d6625d51f87u64, 0x3ff420b5265e5951u64, - 0x3ff4141414141414u64, 0x3ff40782d10e6566u64, 0x3ff3fb013fb013fbu64, 0x3ff3ee8f42a5af07u64, - 0x3ff3e22cbce4a902u64, 0x3ff3d5d991aa75c6u64, 0x3ff3c995a47babe7u64, 0x3ff3bd60d9232955u64, - 0x3ff3b13b13b13b14u64, 0x3ff3a524387ac822u64, 0x3ff3991c2c187f63u64, 0x3ff38d22d366088eu64, - 0x3ff3813813813814u64, 0x3ff3755bd1c945eeu64, 0x3ff3698df3de0748u64, 0x3ff35dce5f9f2af8u64, - 0x3ff3521cfb2b78c1u64, 0x3ff34679ace01346u64, 0x3ff33ae45b57bcb2u64, 0x3ff32f5ced6a1dfau64, - 0x3ff323e34a2b10bfu64, 0x3ff3187758e9ebb6u64, 0x3ff30d190130d190u64, 0x3ff301c82ac40260u64, - 0x3ff2f684bda12f68u64, 0x3ff2eb4ea1fed14bu64, 0x3ff2e025c04b8097u64, 0x3ff2d50a012d50a0u64, - 0x3ff2c9fb4d812ca0u64, 0x3ff2bef98e5a3711u64, 0x3ff2b404ad012b40u64, 0x3ff2a91c92f3c105u64, - 0x3ff29e4129e4129eu64, 0x3ff293725bb804a5u64, 0x3ff288b01288b013u64, 0x3ff27dfa38a1ce4du64, - 0x3ff27350b8812735u64, 0x3ff268b37cd60127u64, 0x3ff25e22708092f1u64, 0x3ff2539d7e9177b2u64, - 0x3ff2492492492492u64, 0x3ff23eb79717605bu64, 0x3ff23456789abcdfu64, 0x3ff22a0122a0122au64, - 0x3ff21fb78121fb78u64, 0x3ff21579804855e6u64, 0x3ff20b470c67c0d9u64, 0x3ff2012012012012u64, - 0x3ff1f7047dc11f70u64, 0x3ff1ecf43c7fb84cu64, 0x3ff1e2ef3b3fb874u64, 0x3ff1d8f5672e4abdu64, - 0x3ff1cf06ada2811du64, 0x3ff1c522fc1ce059u64, 0x3ff1bb4a4046ed29u64, 0x3ff1b17c67f2bae3u64, - 0x3ff1a7b9611a7b96u64, 0x3ff19e0119e0119eu64, 0x3ff19453808ca29cu64, 0x3ff18ab083902bdbu64, - 0x3ff1811811811812u64, 0x3ff1778a191bd684u64, 0x3ff16e0689427379u64, 0x3ff1648d50fc3201u64, - 0x3ff15b1e5f75270du64, 0x3ff151b9a3fdd5c9u64, 0x3ff1485f0e0acd3bu64, 0x3ff13f0e8d344724u64, - 0x3ff135c81135c811u64, 0x3ff12c8b89edc0acu64, 0x3ff12358e75d3033u64, 0x3ff11a3019a74826u64, - 0x3ff1111111111111u64, 0x3ff107fbbe011080u64, 0x3ff0fef010fef011u64, 0x3ff0f5edfab325a2u64, - 0x3ff0ecf56be69c90u64, 0x3ff0e40655826011u64, 0x3ff0db20a88f4696u64, 0x3ff0d24456359e3au64, - 0x3ff0c9714fbcda3bu64, 0x3ff0c0a7868b4171u64, 0x3ff0b7e6ec259dc8u64, 0x3ff0af2f722eecb5u64, - 0x3ff0a6810a6810a7u64, 0x3ff09ddba6af8360u64, 0x3ff0953f39010954u64, 0x3ff08cabb37565e2u64, - 0x3ff0842108421084u64, 0x3ff07b9f29b8eae2u64, 0x3ff073260a47f7c6u64, 0x3ff06ab59c7912fbu64, - 0x3ff0624dd2f1a9fcu64, 0x3ff059eea0727586u64, 0x3ff05197f7d73404u64, 0x3ff04949cc1664c5u64, - 0x3ff0410410410410u64, 0x3ff038c6b78247fcu64, 0x3ff03091b51f5e1au64, 0x3ff02864fc7729e9u64, - 0x3ff0204081020408u64, 0x3ff0182436517a37u64, 0x3ff0101010101010u64, 0x3ff0080402010080u64, - 0x3ff0000000000000u64, -]; -#[rustfmt::skip] -pub static UCRT_LOG_LEAD: [u64; 257] = [ - 0x0000000000000000u64, 0x3f6ff00aa0000000u64, 0x3f7fe02a60000000u64, 0x3f87dc4750000000u64, - 0x3f8fc0a8b0000000u64, 0x3f93cea440000000u64, 0x3f97b91b00000000u64, 0x3f9b9fc020000000u64, - 0x3f9f829b00000000u64, 0x3fa1b0d980000000u64, 0x3fa39e87b0000000u64, 0x3fa58a5ba0000000u64, - 0x3fa77458f0000000u64, 0x3fa95c8300000000u64, 0x3fab42dd70000000u64, 0x3fad276b80000000u64, - 0x3faf0a30c0000000u64, 0x3fb0759830000000u64, 0x3fb16536e0000000u64, 0x3fb253f620000000u64, - 0x3fb341d790000000u64, 0x3fb42edcb0000000u64, 0x3fb51b0730000000u64, 0x3fb60658a0000000u64, - 0x3fb6f0d280000000u64, 0x3fb7da7660000000u64, 0x3fb8c345d0000000u64, 0x3fb9ab4240000000u64, - 0x3fba926d30000000u64, 0x3fbb78c820000000u64, 0x3fbc5e5480000000u64, 0x3fbd4313d0000000u64, - 0x3fbe270760000000u64, 0x3fbf0a30c0000000u64, 0x3fbfec9130000000u64, 0x3fc0671510000000u64, - 0x3fc0d77e70000000u64, 0x3fc1478580000000u64, 0x3fc1b72ad0000000u64, 0x3fc2266f10000000u64, - 0x3fc29552f0000000u64, 0x3fc303d710000000u64, 0x3fc371fc20000000u64, 0x3fc3dfc2b0000000u64, - 0x3fc44d2b60000000u64, 0x3fc4ba36f0000000u64, 0x3fc526e5e0000000u64, 0x3fc59338d0000000u64, - 0x3fc5ff3070000000u64, 0x3fc66acd40000000u64, 0x3fc6d60fe0000000u64, 0x3fc740f8f0000000u64, - 0x3fc7ab8900000000u64, 0x3fc815c0a0000000u64, 0x3fc87fa060000000u64, 0x3fc8e928d0000000u64, - 0x3fc9525a90000000u64, 0x3fc9bb3620000000u64, 0x3fca23bc10000000u64, 0x3fca8becf0000000u64, - 0x3fcaf3c940000000u64, 0x3fcb5b5190000000u64, 0x3fcbc28670000000u64, 0x3fcc296850000000u64, - 0x3fcc8ff7c0000000u64, 0x3fccf63540000000u64, 0x3fcd5c2160000000u64, 0x3fcdc1bca0000000u64, - 0x3fce270760000000u64, 0x3fce8c0250000000u64, 0x3fcef0adc0000000u64, 0x3fcf550a50000000u64, - 0x3fcfb91860000000u64, 0x3fd00e6c40000000u64, 0x3fd0402590000000u64, 0x3fd071b850000000u64, - 0x3fd0a324e0000000u64, 0x3fd0d46b50000000u64, 0x3fd1058bf0000000u64, 0x3fd1368700000000u64, - 0x3fd1675ca0000000u64, 0x3fd1980d20000000u64, 0x3fd1c898c0000000u64, 0x3fd1f8ff90000000u64, - 0x3fd22941f0000000u64, 0x3fd2596010000000u64, 0x3fd2895a10000000u64, 0x3fd2b93030000000u64, - 0x3fd2e8e2b0000000u64, 0x3fd31871c0000000u64, 0x3fd347dd90000000u64, 0x3fd3772660000000u64, - 0x3fd3a64c50000000u64, 0x3fd3d54fa0000000u64, 0x3fd4043080000000u64, 0x3fd432ef20000000u64, - 0x3fd4618bc0000000u64, 0x3fd4900680000000u64, 0x3fd4be5f90000000u64, 0x3fd4ec9730000000u64, - 0x3fd51aad80000000u64, 0x3fd548a2c0000000u64, 0x3fd5767710000000u64, 0x3fd5a42ab0000000u64, - 0x3fd5d1bdb0000000u64, 0x3fd5ff3070000000u64, 0x3fd62c82f0000000u64, 0x3fd659b570000000u64, - 0x3fd686c810000000u64, 0x3fd6b3bb20000000u64, 0x3fd6e08ea0000000u64, 0x3fd70d42e0000000u64, - 0x3fd739d7f0000000u64, 0x3fd7664e10000000u64, 0x3fd792a550000000u64, 0x3fd7bede00000000u64, - 0x3fd7eaf830000000u64, 0x3fd816f410000000u64, 0x3fd842d1d0000000u64, 0x3fd86e9190000000u64, - 0x3fd89a3380000000u64, 0x3fd8c5b7c0000000u64, 0x3fd8f11e80000000u64, 0x3fd91c67e0000000u64, - 0x3fd9479410000000u64, 0x3fd972a340000000u64, 0x3fd99d9580000000u64, 0x3fd9c86b00000000u64, - 0x3fd9f323e0000000u64, 0x3fda1dc060000000u64, 0x3fda484090000000u64, 0x3fda72a490000000u64, - 0x3fda9cec90000000u64, 0x3fdac718c0000000u64, 0x3fdaf12930000000u64, 0x3fdb1b1e00000000u64, - 0x3fdb44f770000000u64, 0x3fdb6eb590000000u64, 0x3fdb985890000000u64, 0x3fdbc1e080000000u64, - 0x3fdbeb4d90000000u64, 0x3fdc149ff0000000u64, 0x3fdc3dd7a0000000u64, 0x3fdc66f4e0000000u64, - 0x3fdc8ff7c0000000u64, 0x3fdcb8e070000000u64, 0x3fdce1af00000000u64, 0x3fdd0a63a0000000u64, - 0x3fdd32fe70000000u64, 0x3fdd5b7f90000000u64, 0x3fdd83e720000000u64, 0x3fddac3530000000u64, - 0x3fddd46a00000000u64, 0x3fddfc8590000000u64, 0x3fde248810000000u64, 0x3fde4c71a0000000u64, - 0x3fde744260000000u64, 0x3fde9bfa60000000u64, 0x3fdec399d0000000u64, 0x3fdeeb20c0000000u64, - 0x3fdf128f50000000u64, 0x3fdf39e5b0000000u64, 0x3fdf6123f0000000u64, 0x3fdf884a30000000u64, - 0x3fdfaf5880000000u64, 0x3fdfd64f20000000u64, 0x3fdffd2e00000000u64, 0x3fe011fab0000000u64, - 0x3fe02552a0000000u64, 0x3fe0389ee0000000u64, 0x3fe04bdf90000000u64, 0x3fe05f14b0000000u64, - 0x3fe0723e50000000u64, 0x3fe0855c80000000u64, 0x3fe0986f40000000u64, 0x3fe0ab76b0000000u64, - 0x3fe0be72e0000000u64, 0x3fe0d163c0000000u64, 0x3fe0e44980000000u64, 0x3fe0f72410000000u64, - 0x3fe109f390000000u64, 0x3fe11cb810000000u64, 0x3fe12f7190000000u64, 0x3fe1422020000000u64, - 0x3fe154c3d0000000u64, 0x3fe1675ca0000000u64, 0x3fe179eab0000000u64, 0x3fe18c6e00000000u64, - 0x3fe19ee6b0000000u64, 0x3fe1b154b0000000u64, 0x3fe1c3b810000000u64, 0x3fe1d610f0000000u64, - 0x3fe1e85f50000000u64, 0x3fe1faa340000000u64, 0x3fe20cdcd0000000u64, 0x3fe21f0bf0000000u64, - 0x3fe23130d0000000u64, 0x3fe2434b60000000u64, 0x3fe2555bc0000000u64, 0x3fe2676200000000u64, - 0x3fe2795e10000000u64, 0x3fe28b5000000000u64, 0x3fe29d37f0000000u64, 0x3fe2af15f0000000u64, - 0x3fe2c0e9e0000000u64, 0x3fe2d2b400000000u64, 0x3fe2e47430000000u64, 0x3fe2f62a90000000u64, - 0x3fe307d730000000u64, 0x3fe3197a00000000u64, 0x3fe32b1330000000u64, 0x3fe33ca2b0000000u64, - 0x3fe34e2890000000u64, 0x3fe35fa4e0000000u64, 0x3fe37117b0000000u64, 0x3fe38280f0000000u64, - 0x3fe393e0d0000000u64, 0x3fe3a53730000000u64, 0x3fe3b68440000000u64, 0x3fe3c7c7f0000000u64, - 0x3fe3d90260000000u64, 0x3fe3ea3390000000u64, 0x3fe3fb5b80000000u64, 0x3fe40c7a40000000u64, - 0x3fe41d8fe0000000u64, 0x3fe42e9c60000000u64, 0x3fe43f9fe0000000u64, 0x3fe4509a50000000u64, - 0x3fe4618bc0000000u64, 0x3fe4727430000000u64, 0x3fe48353d0000000u64, 0x3fe4942a80000000u64, - 0x3fe4a4f850000000u64, 0x3fe4b5bd60000000u64, 0x3fe4c679a0000000u64, 0x3fe4d72d30000000u64, - 0x3fe4e7d810000000u64, 0x3fe4f87a30000000u64, 0x3fe50913c0000000u64, 0x3fe519a4c0000000u64, - 0x3fe52a2d20000000u64, 0x3fe53aad00000000u64, 0x3fe54b2460000000u64, 0x3fe55b9350000000u64, - 0x3fe56bf9d0000000u64, 0x3fe57c57f0000000u64, 0x3fe58cadb0000000u64, 0x3fe59cfb20000000u64, - 0x3fe5ad4040000000u64, 0x3fe5bd7d30000000u64, 0x3fe5cdb1d0000000u64, 0x3fe5ddde50000000u64, - 0x3fe5ee02a0000000u64, 0x3fe5fe1ed0000000u64, 0x3fe60e32f0000000u64, 0x3fe61e3ef0000000u64, - 0x3fe62e42e0000000u64, -]; -#[rustfmt::skip] -pub static UCRT_LOG_TAIL: [u64; 257] = [ - 0x0000000000000000u64, 0x3db5885e0250435au64, 0x3de620cf11f86ed2u64, 0x3dff0214edba4a25u64, - 0x3dbf807c79f3db4eu64, 0x3dea352ba779a52bu64, 0x3dff56c46aa49fd5u64, 0x3dfebe465fef5196u64, - 0x3e0cf0660099f1f8u64, 0x3e1247b2ff85945du64, 0x3e13fd7abf5202b6u64, 0x3e1f91c9a918d51eu64, - 0x3e08cb73f118d3cau64, 0x3e1d91c7d6fad074u64, 0x3de1971bec28d14cu64, 0x3e15b616a423c78au64, - 0x3da162a6617cc971u64, 0x3e166391c4c06d29u64, 0x3e2d46f5c1d0c4b8u64, 0x3e2e14282df1f6d3u64, - 0x3e186f47424a660du64, 0x3e2d4c8de077753eu64, 0x3e2e0c307ed24f1cu64, 0x3e226ea18763bdd3u64, - 0x3e25cad69737c933u64, 0x3e2af62599088901u64, 0x3e18c66c83d6b2d0u64, 0x3e1880ceb36fb30fu64, - 0x3e2495aac6ca17a4u64, 0x3e2761db4210878cu64, 0x3e2eb78e862bac2fu64, 0x3e19b2cd75790dd9u64, - 0x3e2c55e5cbd3d50fu64, 0x3db162a6617cc971u64, 0x3dfdbeabaaa2e519u64, 0x3e1652cb7150c647u64, - 0x3e39a11cb2cd2ee2u64, 0x3e219d0ab1a28813u64, 0x3e24bd9e80a41811u64, 0x3e3214b596faa3dfu64, - 0x3e303fea46980bb8u64, 0x3e31c8ffa5fd28c7u64, 0x3dce8f743bcd96c5u64, 0x3dfd98c5395315c6u64, - 0x3e3996fa3ccfa7b2u64, 0x3e1cd2af2ad13037u64, 0x3e1d0da1bd17200eu64, 0x3e3330410ba68b75u64, - 0x3df4f27a790e7c41u64, 0x3e13956a86f6ff1bu64, 0x3e2c6748723551d9u64, 0x3e2500de9326cdfcu64, - 0x3e1086c848df1b59u64, 0x3e04357ead6836ffu64, 0x3e24832442408024u64, 0x3e3d10da8154b13du64, - 0x3e39e8ad68ec8260u64, 0x3e3cfbf706abaf18u64, 0x3e3fc56ac6326e23u64, 0x3e39105e3185cf21u64, - 0x3e3d017fe5b19cc0u64, 0x3e3d1f6b48dd13feu64, 0x3e20b63358a7e73au64, 0x3e263063028c211cu64, - 0x3e2e6a6886b09760u64, 0x3e3c138bb891cd03u64, 0x3e369f7722b7221au64, 0x3df57d8fac1a628cu64, - 0x3e3c55e5cbd3d50fu64, 0x3e1552d2ff48fe2eu64, 0x3e37b8b26ca431bcu64, 0x3e292decdc1c5f6du64, - 0x3e3abc7c551aaa8cu64, 0x3e36b540731a354bu64, 0x3e32d341036b89efu64, 0x3e4f9ab21a3a2e0fu64, - 0x3e239c871afb9fbdu64, 0x3e3e6add2c81f640u64, 0x3e435c95aa313f41u64, 0x3e249d4582f6cc53u64, - 0x3e47574c1c07398fu64, 0x3e4ba846dece9e8du64, 0x3e16999fafbc68e7u64, 0x3e4c9145e51b0103u64, - 0x3e479ef2cb44850au64, 0x3e0beec73de11275u64, 0x3e2ef4351af5a498u64, 0x3e45713a493b4a50u64, - 0x3e45c23a61385992u64, 0x3e42a88309f57299u64, 0x3e4530faa9ac8aceu64, 0x3e25fec2d792a758u64, - 0x3e35a517a71cbcd7u64, 0x3e3707dc3e1cd9a3u64, 0x3e3a1a9f8ef43049u64, 0x3e4409d0276b3674u64, - 0x3e20e2f613e85bd9u64, 0x3df0027433001e5fu64, 0x3e35dde2836d3265u64, 0x3e2300134d7aaf04u64, - 0x3e3cb7e0b42724f5u64, 0x3e2d6e93167e6308u64, 0x3e3d1569b1526adbu64, 0x3e0e99fc338a1a41u64, - 0x3e4eb01394a11b1cu64, 0x3e04f27a790e7c41u64, 0x3e25ce3ca97b7af9u64, 0x3e281f0f940ed857u64, - 0x3e4d36295d88857cu64, 0x3e21aca1ec4af526u64, 0x3e445743c7182726u64, 0x3e23c491aead337eu64, - 0x3e3aef401a738931u64, 0x3e21cede76092a29u64, 0x3e4fba8f44f82bb4u64, 0x3e446f5f7f3c3e1au64, - 0x3e47055f86c9674bu64, 0x3e4b41a92b6b6e1au64, 0x3e443d162e927628u64, 0x3e4466174013f9b1u64, - 0x3e3b05096ad69c62u64, 0x3e40b169150faa58u64, 0x3e3cd98b1df85da7u64, 0x3e468b507b0f8fa8u64, - 0x3e48422df57499bau64, 0x3e11351586970274u64, 0x3e117e08acba92eeu64, 0x3e26e04314dd0229u64, - 0x3e497f3097e56d1au64, 0x3e3356e655901286u64, 0x3e0cb761457f94d6u64, 0x3e39af67a85a9dacu64, - 0x3e453410931a909fu64, 0x3e22c587206058f5u64, 0x3e223bc358899c22u64, 0x3e4d7bf8b6d223cbu64, - 0x3e47991ec5197ddbu64, 0x3e4a79e6bb3a9219u64, 0x3e3a4c43ed663ec5u64, 0x3e461b5a1484f438u64, - 0x3e4b4e36f7ef0c3au64, 0x3e115f026acd0d1bu64, 0x3e3f36b535cecf05u64, 0x3e2ffb7fbf3eb5c6u64, - 0x3e3e6a6886b09760u64, 0x3e3135eb27f5bbc3u64, 0x3e470be7d6f6fa57u64, 0x3e4ce43cc84ab338u64, - 0x3e4c01d7aac3bd91u64, 0x3e45c58d07961060u64, 0x3e3628bcf941456eu64, 0x3e4c58b2a8461cd2u64, - 0x3e33071282fb989au64, 0x3e420dab6a80f09cu64, 0x3e44f8d84c397b1eu64, 0x3e40d0ee08599e48u64, - 0x3e1d68787e37da36u64, 0x3e366187d591bafcu64, 0x3e22346600bae772u64, 0x3e390377d0d61b8eu64, - 0x3e4f5e0dd966b907u64, 0x3e49023cb79a00e2u64, 0x3e44e05158c28ad8u64, 0x3e3bfa7b08b18ae4u64, - 0x3e4ef1e63db35f67u64, 0x3e0ec2ae39493d4fu64, 0x3e40afe930ab2fa0u64, 0x3e225ff8a1810dd4u64, - 0x3e469743fb1a71a5u64, 0x3e5f9cc676785571u64, 0x3e5b524da4cbf982u64, 0x3e5a4c8b381535b8u64, - 0x3e5839be809caf2cu64, 0x3e50968a1cb82c13u64, 0x3e5eae6a41723fb5u64, 0x3e5d9c29a380a4dbu64, - 0x3e4094aa0ada625eu64, 0x3e5973ad6fc108cau64, 0x3e4747322fdbab97u64, 0x3e593692fa9d4221u64, - 0x3e5c5a992dfbc7d9u64, 0x3e4e1f33e102387au64, 0x3e464fbef14c048cu64, 0x3e4490f513ca5e3bu64, - 0x3e37a6af4d4c799du64, 0x3e57574c1c07398fu64, 0x3e57b133417f8c1cu64, 0x3e5feb9e0c176514u64, - 0x3e419f25bb3172f7u64, 0x3e45f68a7bbfb852u64, 0x3e5ee278497929f1u64, 0x3e5ccee006109d58u64, - 0x3e5ce081a07bd8b3u64, 0x3e570e12981817b8u64, 0x3e292ab6d93503d0u64, 0x3e58cb7dd7c3b61eu64, - 0x3e4efafd0a0b78dau64, 0x3e5e907267c4288eu64, 0x3e5d31ef96780875u64, 0x3e23430dfcd2ad50u64, - 0x3e344d88d75bc1f9u64, 0x3e5bec0f055e04fcu64, 0x3e5d85611590b9adu64, 0x3df320568e583229u64, - 0x3e5a891d1772f538u64, 0x3e22edc9dabba74du64, 0x3e4b9009a1015086u64, 0x3e52a12a8c5b1a19u64, - 0x3e3a7885f0fdac85u64, 0x3e5f4ffcd43ac691u64, 0x3e52243ae2640aadu64, 0x3e546513299035d3u64, - 0x3e5b39c3a62dd725u64, 0x3e5ba6dd40049f51u64, 0x3e451d1ed7177409u64, 0x3e5cb0f2fd7f5216u64, - 0x3e3ab150cd4e2213u64, 0x3e5cfd7bf3193844u64, 0x3e53fff8455f1dbdu64, 0x3e5fee640b905fc9u64, - 0x3e54e2adf548084cu64, 0x3e3b597adc1ecdd2u64, 0x3e4345bd096d3a75u64, 0x3e5101b9d2453c8bu64, - 0x3e508ce55cc8c979u64, 0x3e5bbf017e595f71u64, 0x3e37ce733bd393dcu64, 0x3e233bb0a503f8a1u64, - 0x3e30e2f613e85bd9u64, 0x3e5e67555a635b3cu64, 0x3e2ea88df73d5e8bu64, 0x3e3d17e03bda18a8u64, - 0x3e5b607d76044f7eu64, 0x3e52adc4e71bc2fcu64, 0x3e5f99dc7362d1d9u64, 0x3e5473fa008e6a6au64, - 0x3e2b75bb09cb0985u64, 0x3e5ea04dd10b9abau64, 0x3e5802d0d6979674u64, 0x3e174688ccd99094u64, - 0x3e496f16abb9df22u64, 0x3e46e66df2aa374fu64, 0x3e4e66525ea4550au64, 0x3e42d02f34f20cbdu64, - 0x3e46cfce65047188u64, 0x3e39b78c842d58b8u64, 0x3e4735e624c24bc9u64, 0x3e47eba1f7dd1adfu64, - 0x3e586b3e59f65355u64, 0x3e1ce38e637f1b4du64, 0x3e58d82ec919edc7u64, 0x3e4c52648ddcfa37u64, - 0x3e52482ceae1ac12u64, 0x3e55a312311aba4fu64, 0x3e411e236329f225u64, 0x3e5b48c8cd2f246cu64, - 0x3e6efa39ef35793cu64, -]; -#[rustfmt::skip] -pub static UCRT_SINF_KEYS: [u32; 786] = [ - 0x3fc810bbu32, 0x3fc8127eu32, 0x3fc81508u32, 0x3fc818a1u32, 0x3fc81a30u32, 0x3fc81d99u32, 0x3fc82041u32, 0x3fc820cau32, - 0x3fc82406u32, 0x3fc82794u32, 0x3fc82868u32, 0x3fc829cbu32, 0x3fc82aa1u32, 0x3fc82f24u32, 0x3fc82f6du32, 0x3fc82fb6u32, - 0x3fc831b8u32, 0x3fc8332au32, 0x3fc836acu32, 0x3fc83dddu32, 0x3fc83f16u32, 0x3fc84002u32, 0x3fc840efu32, 0x3fc84737u32, - 0x3fc84db3u32, 0x3fc8588du32, 0x3fc86718u32, 0x3fc86aeeu32, 0x3fc89977u32, 0x3fc972cfu32, 0x3fc97418u32, 0x3fc989f8u32, - 0x3fc9957bu32, 0x3fc9a0f6u32, 0x3fc9aac6u32, 0x3fc9ac02u32, 0x3fc9b338u32, 0x3fc9b779u32, 0x3fc9b95fu32, 0x3fc9ba80u32, - 0x3fc9bd1bu32, 0x3fc9cfb0u32, 0x3fc9d3fau32, 0x3fc9d4f4u32, 0x3fc9d59au32, 0x3fc9db58u32, 0x3fc9dc49u32, 0x3fc9dce9u32, - 0x3fc9de77u32, 0x3fc9e13cu32, 0x3fc9ec41u32, 0x3fc9ee47u32, 0x3fc9f5a3u32, 0x3fc9f794u32, 0x3fc9f9c7u32, 0x3fc9fa0du32, - 0x3fca0041u32, 0x3fca0151u32, 0x3fca0437u32, 0x3fca047au32, 0x3fca05c8u32, 0x3fca09e9u32, 0x3fca0a6cu32, 0x3fca0b71u32, - 0x3fca0bb2u32, 0x3fca0c34u32, 0x3fca0c75u32, 0x3fca0df9u32, 0x3fca0ebau32, 0x404892a4u32, 0x40489b07u32, 0x40495eeeu32, - 0x4049816fu32, 0x40498b57u32, 0x40498e3au32, 0x40968c0cu32, 0x40968c9du32, 0x40968d1fu32, 0x40968d50u32, 0x40968e58u32, - 0x40968e8au32, 0x40969123u32, 0x409695d1u32, 0x40969a0du32, 0x40969b45u32, 0x40969bc4u32, 0x40969c04u32, 0x40969f09u32, - 0x4096f047u32, 0x4096fc98u32, 0x4096fcadu32, 0x4096fe9bu32, 0x409701beu32, 0x409701d1u32, 0x409701e4u32, 0x409705e4u32, - 0x4097063cu32, 0x40970741u32, 0x40970797u32, 0x409709e4u32, 0x40970a05u32, 0x40970bdcu32, 0x40c8d0d3u32, 0x40c9474du32, - 0x40fb146au32, 0x40fb14fcu32, 0x40fb1688u32, 0x40fb1730u32, 0x40fb17fcu32, 0x40fb19aeu32, 0x40fb2752u32, 0x40fb2aafu32, - 0x40fb34a4u32, 0x40fb7d3eu32, 0x40fb7ef2u32, 0x40fb7f39u32, 0x40fb84eeu32, 0x40fb8b10u32, 0x40fb8ceau32, 0x40fb8d67u32, - 0x40fb90b6u32, 0x40fb9369u32, 0x4116e0abu32, 0x4116e82au32, 0x412fcf11u32, 0x412fcfcau32, 0x412fd14au32, 0x412fd192u32, - 0x412fd6beu32, 0x412fd904u32, 0x4130079fu32, 0x4130081fu32, 0x4148f347u32, 0x41492a67u32, 0x41492ce3u32, 0x416211f2u32, - 0x41621514u32, 0x4162175cu32, 0x41621a47u32, 0x41621b3bu32, 0x41623e72u32, 0x41624dffu32, 0x41624f25u32, 0x417b379eu32, - 0x417b70e8u32, 0x418a2d12u32, 0x418a3030u32, 0x418a430au32, 0x418a4916u32, 0x418a4928u32, 0x41a34d18u32, 0x41a34dc6u32, - 0x41a34f9bu32, 0x41a350efu32, 0x41afde6fu32, 0x41afe160u32, 0x41bc6eefu32, 0x41bc6fa8u32, 0x41bc7017u32, 0x41bc70cdu32, - 0x41bc70edu32, 0x41bc71c2u32, 0x41bc8a0eu32, 0x41bc8c7eu32, 0x41c902ceu32, 0x41d5920eu32, 0x41d593a5u32, 0x41d59472u32, - 0x41d5ad81u32, 0x41d5b071u32, 0x41eeb412u32, 0x41eeb53cu32, 0x41eed0bfu32, 0x41eed260u32, 0x41fb44a0u32, 0x41fb47deu32, - 0x4203f94du32, 0x4203f9e0u32, 0x42107c64u32, 0x42107cdfu32, 0x42108a64u32, 0x421d0d1au32, 0x421d1a89u32, 0x42235534u32, - 0x42299df7u32, 0x42299e04u32, 0x4229ac6du32, 0x42363112u32, 0x423c770bu32, 0x4242bfa2u32, 0x4242bfdfu32, 0x4242c032u32, - 0x4242ce3fu32, 0x4242cedfu32, 0x424f516du32, 0x424f5f04u32, 0x425bef43u32, 0x425bf113u32, 0x425bf140u32, 0x42622a27u32, - 0x42687272u32, 0x42687317u32, 0x426873c0u32, 0x4268812au32, 0x426ec97du32, 0x42751327u32, 0x427b4c5au32, 0x428d5b62u32, - 0x428d6295u32, 0x42907f8du32, 0x4296cf20u32, 0x42acc7efu32, 0x42b31540u32, 0x42b63a11u32, 0x42b95e93u32, 0x42bf9fddu32, - 0x42bfa58du32, 0x42c5ef2fu32, 0x42cc308du32, 0x42cf54f5u32, 0x42d8c196u32, 0x42df10e7u32, 0x42eba259u32, 0x42eba26eu32, - 0x42f1e35au32, 0x42f82c84u32, 0x42fe7439u32, 0x42fe7bfcu32, 0x430585e9u32, 0x43058636u32, 0x430bceaau32, 0x430ef311u32, - 0x431537e4u32, 0x4315385cu32, 0x43153bc0u32, 0x43185c0cu32, 0x43185efau32, 0x431d1646u32, 0x431ea872u32, 0x4321c8d4u32, - 0x4324f0d3u32, 0x4328153du32, 0x4333140cu32, 0x4337ca74u32, 0x43413717u32, 0x43477bd8u32, 0x43540da3u32, 0x43573436u32, - 0x4357343au32, 0x435d7a02u32, 0x435d7c84u32, 0x43609e9du32, 0x4360a18fu32, 0x4360a1b4u32, 0x436554b1u32, 0x436a0b1au32, - 0x437056d2u32, 0x43737760u32, 0x437ce741u32, 0x437ce761u32, 0x437ce781u32, 0x437ce7a1u32, 0x437ce7c1u32, 0x437ce7e1u32, - 0x4380cd49u32, 0x43819662u32, 0x438972bau32, 0x438972d0u32, 0x438bcde4u32, 0x438c9517u32, 0x4392df67u32, 0x439471b0u32, - 0x439795dbu32, 0x43a35c17u32, 0x43ac0187u32, 0x43ad91b8u32, 0x43ad93a8u32, 0x43b0b7ebu32, 0x43b180e7u32, 0x43b24811u32, - 0x43b249ffu32, 0x43b56c6du32, 0x43b6fe9cu32, 0x43b6fea0u32, 0x43b890e8u32, 0x43ba22d6u32, 0x43bbb553u32, 0x43bd46f2u32, - 0x43c77cd5u32, 0x43d40de4u32, 0x43db1f5fu32, 0x43db1f7eu32, 0x43dd7a82u32, 0x43e2fbd3u32, 0x43e61fd1u32, 0x43e6e737u32, - 0x43ec66b5u32, 0x43edf8cbu32, 0x43f8f80eu32, 0x43f8f94cu32, 0x43fc1c24u32, 0x440e8c48u32, 0x44173006u32, 0x44209cc3u32, - 0x442d2ea7u32, 0x44350857u32, 0x4439bf82u32, 0x443a88adu32, 0x443a88b3u32, 0x443dac15u32, 0x44432c49u32, 0x444a3de6u32, - 0x444d6225u32, 0x44566979u32, 0x4456ce11u32, 0x4457fc6cu32, 0x4459292fu32, 0x445abb4eu32, 0x4460a030u32, 0x4461ccebu32, - 0x44648d6au32, 0x446942feu32, 0x446a7097u32, 0x446b398fu32, 0x446c02a8u32, 0x446c0390u32, 0x446ff0e2u32, 0x4473de31u32, - 0x447e7795u32, 0x44810031u32, 0x44835bd6u32, 0x4488447au32, 0x4489a3e7u32, 0x44911a6du32, 0x44956c47u32, 0x44a13433u32, - 0x44b66866u32, 0x44b6686bu32, 0x44b668deu32, 0x44b7faf6u32, 0x44b98cabu32, 0x44bf70a4u32, 0x44ca0b69u32, 0x44d377b1u32, - 0x44d69c60u32, 0x44d8930cu32, 0x44dc1bdbu32, 0x44dfa431u32, 0x44e94327u32, 0x44f022e5u32, 0x450a08c1u32, 0x450d7829u32, - 0x45106a24u32, 0x45214d02u32, 0x45214d2eu32, 0x45295a12u32, 0x452b50c1u32, 0x452e74c3u32, 0x452fd4a9u32, 0x4533f438u32, - 0x453718a2u32, 0x453dde9au32, 0x454c4d2cu32, 0x455879ceu32, 0x456218ceu32, 0x45653cdau32, 0x456f0e50u32, 0x4573ab91u32, - 0x457c1cf7u32, 0x457edcb8u32, 0x45888308u32, 0x45896537u32, 0x458fadd2u32, 0x45905daau32, 0x4596d86au32, 0x4597ae08u32, - 0x459a9371u32, 0x459af814u32, 0x45a76fd4u32, 0x45ad53cbu32, 0x45aeb3a7u32, 0x45ba239eu32, 0x45be1d9bu32, 0x45bee692u32, - 0x45c2e06fu32, 0x45c46619u32, 0x45c4661au32, 0x45c4661bu32, 0x45c4661cu32, 0x45cee74bu32, 0x45d80889u32, 0x45da1853u32, - 0x45dbaa76u32, 0x45dd6ed8u32, 0x45df00f8u32, 0x45e1c0afu32, 0x45f07a9cu32, 0x45fafbeau32, 0x45ff66c5u32, 0x46032f99u32, - 0x4604b536u32, 0x46073618u32, 0x460fb414u32, 0x46124e0bu32, 0x46166dabu32, 0x4617434du32, 0x4619854cu32, 0x461b49b1u32, - 0x461bd3f7u32, 0x461ce86fu32, 0x461ef82cu32, 0x46224eadu32, 0x46225b4bu32, 0x4625f0a4u32, 0x46294728u32, 0x4629c4c3u32, - 0x462b8934u32, 0x462ccfedu32, 0x4636ec8du32, 0x463a81e5u32, 0x463f44e3u32, 0x4640ca72u32, 0x4658a5b2u32, 0x465a5d78u32, - 0x465b4601u32, 0x4663ca39u32, 0x466ba4d7u32, 0x466f7904u32, 0x466f7910u32, 0x467ac347u32, 0x467b40f1u32, 0x467fd19cu32, - 0x46852fb1u32, 0x46866cfeu32, 0x468cbef1u32, 0x46960c3eu32, 0x4696ee76u32, 0x469a4e66u32, 0x469ca672u32, 0x469dcdc1u32, - 0x46ab40e9u32, 0x46abfd6fu32, 0x46adc1d3u32, 0x46b1bbb3u32, 0x46b8881eu32, 0x46bcecd5u32, 0x46c13864u32, 0x46d40548u32, - 0x46d5cffcu32, 0x46df94b1u32, 0x46dfc0a6u32, 0x46ee28e0u32, 0x46f6f890u32, 0x46fca3f4u32, 0x47022303u32, 0x4703472eu32, - 0x47069db5u32, 0x470a864eu32, 0x471c4ce4u32, 0x4722d75fu32, 0x47333c8au32, 0x47382b72u32, 0x473873b7u32, 0x473b10e0u32, - 0x474ad2aeu32, 0x474ff6feu32, 0x47511cbfu32, 0x475340f4u32, 0x476420a2u32, 0x47672576u32, 0x476b83e2u32, 0x476bf1d3u32, - 0x476d8d63u32, 0x47796506u32, 0x477d2ca2u32, 0x4787d580u32, 0x47886ddeu32, 0x478a15fdu32, 0x478cdf20u32, 0x478e38b3u32, - 0x4798ca67u32, 0x4799f991u32, 0x479bc04eu32, 0x47a13e2bu32, 0x47a36d5fu32, 0x47a505c9u32, 0x47a9ac73u32, 0x47ac70dfu32, - 0x47b751e5u32, 0x47b8213du32, 0x47bc5ea9u32, 0x47bdb1f5u32, 0x47bee1e8u32, 0x47c01e6cu32, 0x47c92058u32, 0x47cb10b9u32, - 0x47cb34d8u32, 0x47cc7eb6u32, 0x47ccaf68u32, 0x47cfdd14u32, 0x47d29683u32, 0x47e5c0e5u32, 0x47e60d14u32, 0x47f1816du32, - 0x47f2448au32, 0x47f37d21u32, 0x47f4cbb4u32, 0x47f80dcdu32, 0x47faca5fu32, 0x480003ebu32, 0x4816a75fu32, 0x481bf48au32, - 0x4822a0c7u32, 0x482cb0e2u32, 0x482da2cau32, 0x482fc4a4u32, 0x48301bd1u32, 0x483536b8u32, 0x483c0c96u32, 0x483ce625u32, - 0x4840e7dfu32, 0x4840fb1eu32, 0x48492250u32, 0x484f15fcu32, 0x486c5aafu32, 0x486d4197u32, 0x48738a16u32, 0x4874acaeu32, - 0x4875d97du32, 0x48769b7cu32, 0x4879ebb6u32, 0x488923b1u32, 0x489b35e2u32, 0x489d70deu32, 0x48a0bfebu32, 0x48a5b51fu32, - 0x48a83fa1u32, 0x48ae67bau32, 0x48b27e45u32, 0x48bc606eu32, 0x48bfeb2cu32, 0x48cb9b25u32, 0x48d60980u32, 0x48d843b3u32, - 0x48da321bu32, 0x48ddef1du32, 0x48e1df2cu32, 0x48eab068u32, 0x48f281ccu32, 0x48f5319cu32, 0x48fb10ddu32, 0x48fb48a0u32, - 0x48fb6938u32, 0x48fba160u32, 0x48fcd63cu32, 0x49057582u32, 0x490f60b3u32, 0x4910358bu32, 0x49108db4u32, 0x4915ae7fu32, - 0x491c1fa3u32, 0x49223770u32, 0x4924a1beu32, 0x493185fcu32, 0x493c4fa4u32, 0x4942e389u32, 0x4947573fu32, 0x494933b0u32, - 0x494a76e1u32, 0x495099adu32, 0x4961b66eu32, 0x49623286u32, 0x4964e903u32, 0x496b819eu32, 0x4971a8d5u32, 0x497c6765u32, - 0x497e45e6u32, 0x497f3d1au32, 0x49824860u32, 0x498c3a25u32, 0x498c4232u32, 0x498e6b69u32, 0x498eda01u32, 0x49901a98u32, - 0x499719e4u32, 0x499e2de7u32, 0x49a4de12u32, 0x49a78977u32, 0x49a80af5u32, 0x49a9ad93u32, 0x49af2f2bu32, 0x49b8da4du32, - 0x49c02849u32, 0x49c6eea4u32, 0x49ca9c23u32, 0x49cab6d7u32, 0x49d06fe7u32, 0x49d46f21u32, 0x49e927f4u32, 0x49eef442u32, - 0x49f25e29u32, 0x49f473b1u32, 0x49fccdf0u32, 0x4a026803u32, 0x4a0e545du32, 0x4a1221abu32, 0x4a131f5au32, 0x4a1b3285u32, - 0x4a1c9343u32, 0x4a22b42bu32, 0x4a2b447cu32, 0x4a30c538u32, 0x4a368b1eu32, 0x4a3ca940u32, 0x4a3d7c08u32, 0x4a3f69b4u32, - 0x4a4297f7u32, 0x4a42f9d2u32, 0x4a4b5011u32, 0x4a503380u32, 0x4a51597cu32, 0x4a5349c2u32, 0x4a562bd6u32, 0x4a563ebcu32, - 0x4a673598u32, 0x4a687b63u32, 0x4a6d862au32, 0x4a7600bcu32, 0x4a789357u32, 0x4a7ae775u32, 0x4a7edc1bu32, 0x4a882dd3u32, - 0x4a8c74d1u32, 0x4a8dfa66u32, 0x4a92a2fau32, 0x4a99984fu32, 0x4aa29b4bu32, 0x4aaaecddu32, 0x4aaffaacu32, 0x4ab39469u32, - 0x4ab63248u32, 0x4ab65656u32, 0x4abcb200u32, 0x4abdae0au32, 0x4ac4ac35u32, 0x4ac4d043u32, 0x4ac8d1ebu32, 0x4ada79adu32, - 0x4ae2ee2cu32, 0x4ae7b663u32, 0x4af71d19u32, 0x4af7faf9u32, 0x4b005d01u32, 0x4b008c27u32, 0x4b035ffau32, 0x4b0d03b1u32, - 0x4b20489bu32, 0x4b233ef8u32, 0x4b2ab439u32, 0x4b2beeecu32, 0x4b2d7cd3u32, 0x4b35e06cu32, 0x4b36896eu32, 0x4b4b2e7cu32, - 0x4b4c178au32, 0x4b63f84bu32, 0x4b6ca10eu32, 0x4b72d747u32, 0x4b7870f9u32, 0x4b7a400du32, 0x4b7f0b0au32, 0x4b7f7019u32, - 0x4b86250cu32, 0x4b94c1a4u32, 0x4b9d1da2u32, 0x4b9f341bu32, 0x4ba01e6bu32, 0x4ba26e6bu32, 0x4ba353d0u32, 0x4bb24657u32, - 0x4bb88441u32, 0x4bb8a4c7u32, 0x4bbd17bbu32, 0x4bbf5e06u32, 0x4bc6f8a2u32, 0x4bcdca15u32, 0x4bd0424cu32, 0x4bd1e929u32, - 0x4bd60159u32, 0x4bdda85au32, 0x4bf13989u32, 0x4bf4fbb4u32, 0x4bfb1f3au32, 0x4c05e17au32, 0x4c0ba822u32, 0x4c0e7862u32, - 0x4c1423efu32, 0x4c24b66fu32, 0x4c2575c2u32, 0x4c26dc1eu32, 0x4c2c0fc8u32, 0x4c30d7ffu32, 0x4c37501cu32, 0x4c3b5fefu32, - 0x4c3dea22u32, 0x4c40b639u32, 0x4c4d22b6u32, 0x4c548f79u32, 0x4c6b1be5u32, 0x4c6c0d19u32, 0x4c7d2ae6u32, 0x4c835981u32, - 0x4c8552dbu32, 0x4c8935a2u32, 0x4c8dfcf7u32, 0x4c904cccu32, 0x4c92d760u32, 0x4c97b32cu32, 0x4ca3ba99u32, 0x4cac1529u32, - 0x4cad98afu32, 0x4cb3ed6au32, 0x4cb8fb78u32, 0x4cbbe63cu32, 0x4cbcfb28u32, 0x4cbf5a69u32, 0x4cc02bc3u32, 0x4cc8b1a6u32, - 0x4ccf143fu32, 0x4cd701f9u32, 0x4cd7bf3du32, 0x4cda6e35u32, 0x4cdf355fu32, 0x4cdf9f6fu32, 0x4ce5926cu32, 0x4ce92362u32, - 0x4cf4b153u32, 0x4cfde962u32, 0x4d041be0u32, 0x4d0dd240u32, 0x4d0e10d2u32, 0x4d0ee548u32, 0x4d135c90u32, 0x4d141111u32, - 0x4d14c4b0u32, 0x4d16c8f7u32, 0x4d19ae2fu32, 0x4d20e53eu32, 0x4d21f9a9u32, 0x4d24e708u32, 0x4d2572e1u32, 0x4d2d3548u32, - 0x4d3209cfu32, 0x4d353641u32, 0x4d3683ddu32, 0x4d36e3c7u32, 0x4d3d0150u32, 0x4d3e1881u32, 0x4d461153u32, 0x4d545d7du32, - 0x4d57f29cu32, 0x4d5fe8a8u32, 0x4d667694u32, 0x4d6b5a87u32, 0x4d6e1309u32, 0x4d6ff608u32, 0x4d718da4u32, 0x4d772536u32, - 0x4d77e8ddu32, 0x4d7e5cb6u32, 0x4d810c95u32, 0x4d8418ccu32, 0x4d864b57u32, 0x4d898046u32, 0x4d9072b6u32, 0x4d90ade6u32, - 0x4d91ff5du32, 0x4d952069u32, 0x4da14c5du32, 0x4da27d28u32, 0x4da840a9u32, 0x4db29c1eu32, 0x4db446b3u32, 0x4dbac382u32, - 0x4dc6898fu32, 0x4dc889d8u32, -]; -#[rustfmt::skip] -pub static UCRT_SINF_VALUES: [u32; 786] = [ - 0x3f7ffe04u32, 0x3f7ffe0bu32, 0x3f7ffe15u32, 0x3f7ffe23u32, 0x3f7ffe29u32, 0x3f7ffe36u32, 0x3f7ffe40u32, 0x3f7ffe42u32, - 0x3f7ffe4eu32, 0x3f7ffe5bu32, 0x3f7ffe5eu32, 0x3f7ffe63u32, 0x3f7ffe66u32, 0x3f7ffe76u32, 0x3f7ffe77u32, 0x3f7ffe78u32, - 0x3f7ffe7fu32, 0x3f7ffe84u32, 0x3f7ffe90u32, 0x3f7ffea8u32, 0x3f7ffeacu32, 0x3f7ffeafu32, 0x3f7ffeb2u32, 0x3f7ffec6u32, - 0x3f7ffedau32, 0x3f7ffefau32, 0x3f7fff22u32, 0x3f7fff2cu32, 0x3f7fff93u32, 0x3f7fffb4u32, 0x3f7fffb2u32, 0x3f7fff8cu32, - 0x3f7fff75u32, 0x3f7fff5cu32, 0x3f7fff45u32, 0x3f7fff42u32, 0x3f7fff30u32, 0x3f7fff25u32, 0x3f7fff20u32, 0x3f7fff1du32, - 0x3f7fff16u32, 0x3f7ffee1u32, 0x3f7ffed4u32, 0x3f7ffed1u32, 0x3f7ffecfu32, 0x3f7ffebdu32, 0x3f7ffebau32, 0x3f7ffeb8u32, - 0x3f7ffeb3u32, 0x3f7ffeaau32, 0x3f7ffe85u32, 0x3f7ffe7eu32, 0x3f7ffe64u32, 0x3f7ffe5du32, 0x3f7ffe55u32, 0x3f7ffe54u32, - 0x3f7ffe3du32, 0x3f7ffe39u32, 0x3f7ffe2eu32, 0x3f7ffe2du32, 0x3f7ffe28u32, 0x3f7ffe18u32, 0x3f7ffe16u32, 0x3f7ffe12u32, - 0x3f7ffe11u32, 0x3f7ffe0fu32, 0x3f7ffe0eu32, 0x3f7ffe08u32, 0x3f7ffe05u32, 0x3bfa6ca5u32, 0x3be9a6c3u32, 0xbb9e2694u32, - 0xbbe32845u32, 0xbbf6f823u32, 0xbbfcbe18u32, 0xbf7ffe03u32, 0xbf7ffe0cu32, 0xbf7ffe14u32, 0xbf7ffe17u32, 0xbf7ffe27u32, - 0xbf7ffe2au32, 0xbf7ffe51u32, 0xbf7ffe93u32, 0xbf7ffecau32, 0xbf7ffed9u32, 0xbf7ffedfu32, 0xbf7ffee2u32, 0xbf7fff05u32, - 0xbf7fff5bu32, 0xbf7ffed8u32, 0xbf7ffed7u32, 0xbf7ffebfu32, 0xbf7ffe96u32, 0xbf7ffe95u32, 0xbf7ffe94u32, 0xbf7ffe5cu32, - 0xbf7ffe57u32, 0xbf7ffe48u32, 0xbf7ffe43u32, 0xbf7ffe20u32, 0xbf7ffe1eu32, 0xbf7ffe01u32, 0xbbfc1de6u32, 0x3bddc909u32, - 0x3f7ffe0au32, 0x3f7ffe13u32, 0x3f7ffe2bu32, 0x3f7ffe35u32, 0x3f7ffe41u32, 0x3f7ffe5au32, 0x3f7fff09u32, 0x3f7fff2du32, - 0x3f7fff87u32, 0x3f7fff2au32, 0x3f7fff18u32, 0x3f7fff15u32, 0x3f7ffed3u32, 0x3f7ffe83u32, 0x3f7ffe69u32, 0x3f7ffe62u32, - 0x3f7ffe31u32, 0x3f7ffe07u32, 0xbba63805u32, 0xbbe22fbeu32, 0xbf7ffe26u32, 0xbf7ffe3cu32, 0xbf7ffe68u32, 0xbf7ffe70u32, - 0xbf7ffef5u32, 0xbf7fff27u32, 0xbf7ffeb5u32, 0xbf7ffea8u32, 0xbbe49c98u32, 0x3bd4628eu32, 0x3be84270u32, 0x3f7ffe04u32, - 0x3f7ffe63u32, 0x3f7ffea2u32, 0x3f7ffeebu32, 0x3f7fff01u32, 0x3f7fffb1u32, 0x3f7ffe74u32, 0x3f7ffe53u32, 0x3be199e1u32, - 0xbbe8b52bu32, 0xbf7ffe82u32, 0xbf7fff1bu32, 0xbf7fff7cu32, 0xbf7ffe6eu32, 0xbf7ffe6au32, 0x3f7ffe0eu32, 0x3f7ffe38u32, - 0x3f7ffea0u32, 0x3f7ffee3u32, 0x3bf70445u32, 0x3bc7f48du32, 0xbf7ffe05u32, 0xbf7ffe32u32, 0xbf7ffe4cu32, 0xbf7ffe75u32, - 0xbf7ffe7cu32, 0xbf7ffea9u32, 0xbf7fff06u32, 0xbf7ffe8du32, 0xbbd0c9c6u32, 0x3f7ffe4bu32, 0x3f7ffea4u32, 0x3f7ffecdu32, - 0x3f7ffec0u32, 0x3f7ffe1au32, 0xbf7ffe4du32, 0xbf7ffe8fu32, 0xbf7ffe7du32, 0xbf7ffe1du32, 0xbbf31419u32, 0xbbbf3464u32, - 0x3f7ffe84u32, 0x3f7ffe42u32, 0xbf7ffe78u32, 0xbf7ffeacu32, 0xbf7ffe79u32, 0x3f7ffe58u32, 0x3f7ffed2u32, 0x3bf5b3e1u32, - 0xbf7ffe49u32, 0xbf7ffe4fu32, 0xbf7ffe73u32, 0x3f7fff20u32, 0x3bfa3e61u32, 0xbf7ffe23u32, 0xbf7ffe40u32, 0xbf7ffe66u32, - 0xbf7ffe85u32, 0xbf7ffe3du32, 0x3f7ffe81u32, 0x3f7ffe9du32, 0xbf7ffee8u32, 0xbf7ffe22u32, 0xbf7ffe0cu32, 0xbbf5de36u32, - 0x3f7ffe0fu32, 0x3f7ffe5du32, 0x3f7ffea6u32, 0x3f7ffe8bu32, 0xbbd52b8du32, 0xbf7ffe16u32, 0xbbeee8cbu32, 0x3f7ffe3bu32, - 0x3f7ffe87u32, 0x3bf6088bu32, 0x3bcf0140u32, 0xbf7fffa5u32, 0x3f7ffec7u32, 0xbbecb6beu32, 0xbf7ffe0bu32, 0x3f7ffeafu32, - 0x3f7fff42u32, 0xbf7ffe67u32, 0x3f7ffe6du32, 0x3bd91d65u32, 0x3f7ffe77u32, 0xbf7ffea1u32, 0xbf7ffe3au32, 0xbf7ffe26u32, - 0x3f7ffe45u32, 0xbf7ffed6u32, 0x3f7ffe28u32, 0x3f7ffe15u32, 0x3f7fff0cu32, 0x3f7ffe96u32, 0x3f7ffea8u32, 0xbf7ffe64u32, - 0xbf7ffe3eu32, 0xbf7fff03u32, 0xbf7ffe09u32, 0x3f7ffe11u32, 0x3f7fff8au32, 0x3bf1981cu32, 0x3f7ffe21u32, 0xbf7ffe24u32, - 0x3f7ffe59u32, 0xbf7ffe08u32, 0xbbf6a335u32, 0x3f7ffe14u32, 0xbf7ffe48u32, 0xbf7ffe39u32, 0xbf7fff6au32, 0x3f7fff3bu32, - 0x3f7fff36u32, 0x3f7ffef1u32, 0x3f7fff73u32, 0xbf7fff66u32, 0xbf7ffe4cu32, 0xbf7ffe05u32, 0x3bb2303eu32, 0x3f7fff16u32, - 0x3f7ffe41u32, 0xbf7ffe57u32, 0x3f7fff24u32, 0x3f7ffef8u32, 0x3f7ffec8u32, 0x3f7ffe94u32, 0x3f7ffe5cu32, 0x3f7ffe20u32, - 0xbbdf0f6du32, 0x3f7ffe9bu32, 0xbf7ffe6cu32, 0xbf7ffe1au32, 0xbbddfad6u32, 0xbf7ffe05u32, 0xbf7ffea7u32, 0x3f7ffe12u32, - 0x3f7ffe5fu32, 0xbbcaa367u32, 0xbf7ffe24u32, 0x3f7ffe21u32, 0x3f7ffe1fu32, 0x3f7ffe11u32, 0xbbe7fc60u32, 0xbf7ffe09u32, - 0xbf7ffe3eu32, 0xbf7ffe74u32, 0x3f7ffea8u32, 0x3f7ffeb5u32, 0xbf7fff2au32, 0x3f7ffe96u32, 0xbf7fff8cu32, 0x3f7ffe15u32, - 0x3be5ec62u32, 0x3bd4969eu32, 0xbf7ffe5bu32, 0xbf7ffec4u32, 0x3bf4d604u32, 0x3f7ffe49u32, 0x3f7fff1au32, 0x3bfe158cu32, - 0x3f7ffe42u32, 0xbf7ffe1du32, 0x3f7fff56u32, 0x3f7fff1eu32, 0x3f7ffee9u32, 0xbf7ffe51u32, 0x3f7ffebcu32, 0x3f7ffeb4u32, - 0x3f7ffe07u32, 0x3f7ffe7du32, 0x3f7ffef5u32, 0xbf7ffe41u32, 0xbf7ffe13u32, 0xbf7ffee2u32, 0x3f7ffec0u32, 0xbf7ffe5fu32, - 0xbf7ffe62u32, 0x3bde4d5cu32, 0xbf7ffee6u32, 0xbbc8475bu32, 0x3f7ffe74u32, 0x3f7ffe6fu32, 0x3bf37d2bu32, 0xbf7ffeceu32, - 0xbbc8f316u32, 0x3bcd4bdau32, 0x3f7ffebfu32, 0xbf7ffe16u32, 0x3f7ffe5bu32, 0x3f7ffe5du32, 0xbf7ffe49u32, 0x3f7ffe4bu32, - 0xbbf769cbu32, 0x3f7ffe4du32, 0x3f7ffe56u32, 0xbbed4bf2u32, 0x3f7ffeb6u32, 0xbf7ffe3fu32, 0x3f7ffe14u32, 0x3f7ffe30u32, - 0x3f7ffe3du32, 0x3f7ffe85u32, 0x3f7ffe40u32, 0x3f7ffeacu32, 0x3f7ffe8du32, 0xbf7ffea9u32, 0x3f7ffe54u32, 0x3f7ffe43u32, - 0x3f7ffeb0u32, 0xbf7ffe75u32, 0x3f7ffe02u32, 0xbf7ffecfu32, 0xbbf697cbu32, 0xbf7ffe99u32, 0xbbf538f7u32, 0x3f7ffe29u32, - 0xbf7ffe1bu32, 0xbf7fff0eu32, 0xbf7fff0fu32, 0x3f7ffeedu32, 0x3f7ffe25u32, 0x3f7ffe2du32, 0xbf7fff32u32, 0x3f7ffed3u32, - 0x3f7fff5eu32, 0x3bf5b442u32, 0x3f7fff80u32, 0x3f7ffe72u32, 0xbf7ffeaeu32, 0xbf7ffebeu32, 0xbf7fff05u32, 0xbbd247bau32, - 0x3baed56bu32, 0x3bfaeb45u32, 0x3f7ffef2u32, 0xbf7ffe5eu32, 0xbf7ffe89u32, 0x3f7fff7bu32, 0x3f7fff10u32, 0x3bdcf88au32, - 0x3f7ffe2eu32, 0x3f7ffeeau32, 0xbf7ffe08u32, 0xbf7ffe0eu32, 0xbf7ffe1eu32, 0xbbf680f7u32, 0x3f7ffe9du32, 0x3f7ffef7u32, - 0x3bec85c4u32, 0x3f7ffec8u32, 0x3f7ffe94u32, 0x3f7ffe5cu32, 0x3f7ffe20u32, 0xbf7fff26u32, 0x3f7ffe0au32, 0xbf7ffe22u32, - 0xbf7ffed8u32, 0xbf7ffe81u32, 0xbf7ffe91u32, 0xbf7ffe76u32, 0xbf7fff2cu32, 0x3f7ffe17u32, 0xbf7ffe5au32, 0x3f7ffe6bu32, - 0xbf7ffe66u32, 0x3f7ffea1u32, 0xbf7ffe61u32, 0x3f7ffec2u32, 0x3f7ffeb3u32, 0xbf7ffe31u32, 0xbf7ffe7cu32, 0xbf7fff02u32, - 0x3f7fff12u32, 0x3f7ffe2bu32, 0x3f7fff3cu32, 0x3f7ffe52u32, 0xbf7ffeffu32, 0x3f7ffebfu32, 0x3f7ffe7du32, 0x3f7ffe07u32, - 0x3f7fff19u32, 0x3f7fff56u32, 0x3f7ffe3eu32, 0xbf7ffe15u32, 0x3f7ffedcu32, 0xbf7ffec0u32, 0xbf7fff4bu32, 0x3f7ffe0bu32, - 0xbbd80980u32, 0x3f7fff2bu32, 0x3f7ffefcu32, 0x3f7ffee1u32, 0x3f7ffee0u32, 0x3f7ffe84u32, 0x3f7ffe8eu32, 0xbf7ffe06u32, - 0x3f7ffe79u32, 0x3be11cf6u32, 0x3f7ffe18u32, 0xbf7ffefau32, 0xbf7ffea4u32, 0xbbcb5846u32, 0x3f7ffe23u32, 0xbf7ffe67u32, - 0xbf7ffe14u32, 0xbf7ffeb6u32, 0xbf7ffe6fu32, 0xbf7ffea8u32, 0x3f7ffee9u32, 0xbf7ffe97u32, 0x3f7ffe54u32, 0x3f7ffe27u32, - 0xbf7ffe04u32, 0xbf7ffe32u32, 0x3f7fff1du32, 0xbf7ffe7bu32, 0x3f7fff20u32, 0xbf7ffe33u32, 0x3f7ffe1fu32, 0xbf7ffe0cu32, - 0xbf7ffea7u32, 0x3bdf45adu32, 0x3f7fff6cu32, 0xbf7ffe38u32, 0xbf7ffe21u32, 0xbf7ffea5u32, 0x3f7ffea2u32, 0xbf7ffe37u32, - 0xbf7ffe59u32, 0x3f7ffe0du32, 0x3bee6fd7u32, 0x3f7ffeaau32, 0xbf7fff86u32, 0xbf7ffe2au32, 0xbf7ffe35u32, 0x3f7ffe6du32, - 0xbf7ffe6eu32, 0x3f7ffe84u32, 0x3f7ffefcu32, 0xbf7ffe39u32, 0x3f7ffe2bu32, 0x3f7fff48u32, 0x3f7fff34u32, 0x3f7ffe73u32, - 0xbbc546fbu32, 0x3bdd9e64u32, 0xbf7ffe09u32, 0x3f7ffe92u32, 0x3f7ffe3cu32, 0x3f7ffe89u32, 0x3f7ffe1eu32, 0xbf7ffe6bu32, - 0x3f7fff84u32, 0x3f7ffe95u32, 0xbf7fffa2u32, 0xbf7ffeabu32, 0xbbd98f8cu32, 0x3f7ffe69u32, 0x3f7ffe42u32, 0x3f7ffe49u32, - 0xbf7ffe4du32, 0xbf7ffe43u32, 0x3f7ffecfu32, 0x3f7ffe44u32, 0x3f7ffe4au32, 0xbbcd13e6u32, 0x3f7ffeb2u32, 0xbf498823u32, - 0xbf7fff50u32, 0x3f7ffe3du32, 0xbf7fff9cu32, 0x3f7ffec3u32, 0x3f7fff97u32, 0x3f7ffed1u32, 0xbf7ffeb1u32, 0xbf7ffec9u32, - 0x3f7fff3fu32, 0x3f7ffe79u32, 0x3f7ffe18u32, 0xbf7ffe67u32, 0x3f7ffe8bu32, 0x3f7ffe2cu32, 0x3f7ffe53u32, 0xbf7ffecdu32, - 0xbf7ffe0fu32, 0x3bfacce6u32, 0xbf7ffea6u32, 0xbf7fff6eu32, 0xbf7ffe55u32, 0xbf7fff81u32, 0xbf7ffe9cu32, 0xbf7ffe07u32, - 0x3f7fffa1u32, 0xbf7ffe1au32, 0xbf7ffebeu32, 0xbbd29457u32, 0xbf7fff0au32, 0xbf7fff47u32, 0x3f7fff5eu32, 0x3f7ffe76u32, - 0xbbf2dc97u32, 0x3f7ffe99u32, 0xbf7ffedfu32, 0x3f7ffef4u32, 0xbf7ffeeeu32, 0x3f7ffe2eu32, 0x3f7ffeb9u32, 0x3f7ffe2du32, - 0xbf7ffe11u32, 0x3f7ffea0u32, 0xbf7ffecbu32, 0xbf7ffef5u32, 0xbbf93b3au32, 0xbf7ffe55u32, 0xbf7ffe31u32, 0xbf7fff31u32, - 0x3f7ffe52u32, 0xbf7fff36u32, 0x3bf499ffu32, 0xbf7ffe0fu32, 0xbf7ffed3u32, 0xbf7ffe20u32, 0x3f7fff2cu32, 0x3f7ffed1u32, - 0xbf7ffe43u32, 0xbf7ffe09u32, 0x3f7ffe2bu32, 0x3f7ffe78u32, 0xbf7ffe27u32, 0xbf7ffeaau32, 0x3bdfdea2u32, 0x3f7fff1au32, - 0x3f7fff69u32, 0x3f7fff55u32, 0xbf7ffe8bu32, 0xbf7ffe80u32, 0x3f7ffecdu32, 0x3f7fffa7u32, 0xbbeaf373u32, 0xbf7ffea0u32, - 0x3f7ffe11u32, 0xbf7ffe2du32, 0x3f7ffe98u32, 0xbf7ffe23u32, 0xbf7ffe76u32, 0x3f7ffe47u32, 0xbb4d283fu32, 0x3f7ffe3fu32, - 0x3f7ffe07u32, 0xbf7ffe53u32, 0xbf7ffeb6u32, 0x3f7fff3eu32, 0xbf7ffeddu32, 0xbf7ffe69u32, 0xbf7ffeefu32, 0x3bf4f8b3u32, - 0x3f7ffea3u32, 0x3f7ffef6u32, 0x3f7ffe5bu32, 0x3f7ffec7u32, 0xbf7ffe86u32, 0x3beeb7d6u32, 0x3f7ffed7u32, 0xbf7fff46u32, - 0x3beee707u32, 0xbf7ffe31u32, 0xbf7ffefdu32, 0xbf7ffe8eu32, 0xbf7ffe0au32, 0xbf7ffe74u32, 0xbf7ffe0eu32, 0xbf7ffe24u32, - 0xbf7ffe15u32, 0x3bc2bee4u32, 0x3f7fff8au32, 0xbbe5074cu32, 0x3f7ffec1u32, 0xbf7ffe30u32, 0xbf7ffeb4u32, 0x3f7fff93u32, - 0x3f7ffe2fu32, 0xbbf5f135u32, 0x3f7ffeb3u32, 0x3f7ffe7cu32, 0xbf7fff1du32, 0xbf7ffe6du32, 0xbf7ffedau32, 0x3f7ffe48u32, - 0x3f7ffe28u32, 0xbf7ffe5eu32, 0xbbf3d100u32, 0xbf7ffe40u32, 0x3f7ffed8u32, 0x3f7ffe56u32, 0x3beca8fbu32, 0x3f7ffe4au32, - 0x3f7ffe33u32, 0xbf7ffe1bu32, 0xbf7fff10u32, 0xbf7ffeb8u32, 0x3f7ffe0bu32, 0x3bdbdc84u32, 0xbf7ffe3bu32, 0x3f7fff43u32, - 0xbf7ffe6bu32, 0x3f7ffe3cu32, 0xbf7ffe3du32, 0x3f7ffe19u32, 0x3f7ffe34u32, 0xbf7ffe02u32, 0x3f7ffe1cu32, 0xbbea33cdu32, - 0xbf7ffe25u32, 0x3f7ffe21u32, 0x3be8edf1u32, 0x3b99e8c4u32, 0xbf7ffe70u32, 0xbf7ffedeu32, 0x3bb1c302u32, 0xbf7ffe41u32, - 0xbf7ffeb7u32, 0x3f7ffe2bu32, 0x3f7ffe2cu32, 0x3f7fff06u32, 0x3f7fff18u32, 0x3f7ffe0fu32, 0x3f7ffeb1u32, 0xbf7ffea0u32, - 0x3f7ffe31u32, 0x3f7ffe20u32, 0x3f7ffebfu32, 0xbf7ffe63u32, 0xbf7ffea4u32, 0x3f7ffe14u32, 0x3f7ffe04u32, 0xbf7ffe0du32, - 0x3f7ffe66u32, 0x3f7fff90u32, 0xbbadd51eu32, 0x3f7ffea7u32, 0xbf7ffe08u32, 0x3f7ffe6cu32, 0xbbaa9cd2u32, 0x3be4f24bu32, - 0x3f7fff32u32, 0x3be20f1bu32, 0xbf7ffecdu32, 0x3f7ffe43u32, 0x3f7ffe99u32, 0x3f7ffeccu32, 0xbf7fff24u32, 0xbf7fff5bu32, - 0xbf7ffeedu32, 0xbf7fffd0u32, 0xbf7ffe03u32, 0x3f7fff0eu32, 0xbbf020eau32, 0x3f7ffe94u32, 0xbf7ffe6du32, 0x3f7ffe1au32, - 0x3f7ffe07u32, 0x3f7ffe54u32, 0x3f7ffecau32, 0xbf7ffe65u32, 0x3f7ffeb8u32, 0x3f7ffed2u32, 0xbf7fff19u32, 0xbf7ffe51u32, - 0xbf7ffe5bu32, 0x3f7ffe80u32, 0x3f7fff66u32, 0x3f7ffe32u32, 0xbf7ffe06u32, 0xbf7ffe8du32, 0x3f7ffec0u32, 0x3f7ffea9u32, - 0x3f7ffe64u32, 0x3f7ffe89u32, 0x3bcc33f5u32, 0xbf7fff28u32, 0x3f7ffe67u32, 0xbbf5c839u32, 0xbf7ffe38u32, 0xbf7ffe53u32, - 0xbf7ffe55u32, 0xbf7ffee6u32, 0x3ba9ba68u32, 0xbf7ffe85u32, 0x3f7ffe5eu32, 0x3f7ffe15u32, 0x3f7ffe8eu32, 0xbf7ffe0au32, - 0xbf7ffe8cu32, 0xbf7ffe13u32, 0xbf7ffe23u32, 0xbf7fffafu32, 0x3f7fff0bu32, 0x3f7fff4au32, 0x3f7fff15u32, 0x3f7ffee4u32, - 0x3f7fff47u32, 0xbf7ffe2au32, 0xbf7ffec5u32, 0x3f7fff1eu32, 0xbf7ffe0du32, 0xbf7ffe73u32, 0x3f7ffe2cu32, 0xbbd96693u32, - 0x3f7ffec7u32, 0xbf7fff14u32, 0x3f7ffea1u32, 0x3f7ffed5u32, 0xbf7ffe50u32, 0xbf7ffef3u32, 0x3f7ffe17u32, 0x3f7ffe63u32, - 0xbf7ffe57u32, 0xbf7ffe9du32, 0x3f7ffe29u32, 0x3c000de3u32, 0xbf7ffe21u32, 0x3f7ffe02u32, 0x3f7ffed7u32, 0x3f7ffe46u32, - 0x3f7ffe75u32, 0x3f7ffed4u32, 0x3f7ffea2u32, 0x3f7fff6du32, 0xbf7ffe28u32, 0x3f7ffe3bu32, 0x3f7ffe25u32, 0x3bb23314u32, - 0x3bc9ce01u32, 0xbf7ffea7u32, 0x3f7ffecdu32, 0x3f7ffef0u32, 0x3f7ffe18u32, 0x3f7fff0du32, 0x3f7ffe4bu32, 0xbf7ffe35u32, - 0xbbfa1b6bu32, 0x3f7ffed8u32, 0xbbdb09deu32, 0xbf7ffe6au32, 0xbf7ffe56u32, 0x3f7fff0cu32, 0xbf7ffe1cu32, 0x3beb827au32, - 0xbf7fff10u32, 0xbf7ffe0cu32, -]; -#[rustfmt::skip] -pub static UCRT_SINF_WIDE_KEYS: [u32; 2536] = [ - 0x4dcd12b6u32, 0x4dce4a93u32, 0x4ddb264bu32, 0x4de1fb46u32, 0x4de5f386u32, 0x4df02536u32, 0x4df1accau32, 0x4df21f09u32, - 0x4df5b58bu32, 0x4df6e564u32, 0x4df9074bu32, 0x4dfae621u32, 0x4dfeac1fu32, 0x4e0c62dfu32, 0x4e138c08u32, 0x4e15acdau32, - 0x4e15df1cu32, 0x4e16ea6eu32, 0x4e18d40eu32, 0x4e1add97u32, 0x4e1dd903u32, 0x4e24d63du32, 0x4e26ffafu32, 0x4e28e901u32, - 0x4e2a99beu32, 0x4e2d253cu32, 0x4e2f9d50u32, 0x4e304732u32, 0x4e3db133u32, 0x4e4405eeu32, 0x4e4d583bu32, 0x4e525a1cu32, - 0x4e587104u32, 0x4e592a52u32, 0x4e5c2dc2u32, 0x4e65c65bu32, 0x4e71dc46u32, 0x4e73678eu32, 0x4e7f9939u32, 0x4e81b3d8u32, - 0x4e8801a2u32, 0x4e8e007eu32, 0x4e8f4cdeu32, 0x4e93ad7du32, 0x4e941ca4u32, 0x4e961510u32, 0x4e973158u32, 0x4e9d30a9u32, - 0x4ea3b957u32, 0x4eac59beu32, 0x4eaf230fu32, 0x4eb05a02u32, 0x4ebf3188u32, 0x4ec1418bu32, 0x4ec812fdu32, 0x4ecb49ebu32, - 0x4ecd37b4u32, 0x4ed1cae3u32, 0x4edd49bau32, 0x4edf10f9u32, 0x4ee83893u32, 0x4eeabfe8u32, 0x4eeba9eeu32, 0x4ef31e2cu32, - 0x4ef36cf3u32, 0x4f021e87u32, 0x4f032539u32, 0x4f084f55u32, 0x4f121255u32, 0x4f129a4eu32, 0x4f14765du32, 0x4f1ecb0cu32, - 0x4f1fbb3cu32, 0x4f3379cfu32, 0x4f33bd2eu32, 0x4f38f6dcu32, 0x4f398815u32, 0x4f3d4571u32, 0x4f456af9u32, 0x4f4b8681u32, - 0x4f4f7043u32, 0x4f57664fu32, 0x4f5bc3d8u32, 0x4f5ccf2au32, 0x4f5d4b1du32, 0x4f60d7e9u32, 0x4f629d27u32, 0x4f653a39u32, - 0x4f669d6bu32, 0x4f6cc6d3u32, 0x4f6cce39u32, 0x4f6dac87u32, 0x4f7b3dd8u32, 0x4f7ea892u32, 0x4f86fab5u32, 0x4f9871b2u32, - 0x4f9f616fu32, 0x4fb837c6u32, 0x4fc662a3u32, 0x4fca5268u32, 0x4fca69d7u32, 0x4fcb5151u32, 0x4fce54fcu32, 0x4fcf1138u32, - 0x4fd5ee3au32, 0x4fe6cf4au32, 0x4fe6eb0au32, 0x4fe7c1cau32, 0x4febc20eu32, 0x4fec45b6u32, 0x4ff8dcf9u32, 0x4fff1f33u32, - 0x5002231du32, 0x50072573u32, 0x500a72bbu32, 0x5011faa1u32, 0x5017f216u32, 0x5022ce6fu32, 0x502ac8eau32, 0x502cb11du32, - 0x5031b24bu32, 0x5041c703u32, 0x50508437u32, 0x505d8e99u32, 0x506bf127u32, 0x50757e9eu32, 0x5077ec2bu32, 0x50789841u32, - 0x507b7d52u32, 0x5087f758u32, 0x50962d67u32, 0x5097d49fu32, 0x50989b88u32, 0x50a0bc21u32, 0x50a714ddu32, 0x50a9d38bu32, - 0x50af97e5u32, 0x50affc5fu32, 0x50b22c6eu32, 0x50c82e33u32, 0x50ca1255u32, 0x50caa4b6u32, 0x50d695f0u32, 0x50ead1e3u32, - 0x50eb8eb3u32, 0x50f23d30u32, 0x50fbd7f3u32, 0x5108b8e8u32, 0x510a9e32u32, 0x5117d75eu32, 0x511b263du32, 0x5124e6f9u32, - 0x51292cd3u32, 0x512cb9e9u32, 0x5133a403u32, 0x513679ecu32, 0x514468f8u32, 0x5144f1aau32, 0x5147ad05u32, 0x5149a0e3u32, - 0x5154ead1u32, 0x5156f884u32, 0x51669265u32, 0x516e552au32, 0x51849d6du32, 0x51894694u32, 0x518f20bcu32, 0x51a467bcu32, - 0x51a467e1u32, 0x51a85134u32, 0x51b44e43u32, 0x51bc69a8u32, 0x51bcb320u32, 0x51cecbbcu32, 0x51d54be3u32, 0x51d98dfbu32, - 0x51da2cb0u32, 0x51e67f90u32, 0x51efd42eu32, 0x51f0131eu32, 0x51f9d18au32, 0x51fe6dd9u32, 0x52077e8fu32, 0x520a2f03u32, - 0x520aa7f9u32, 0x521442f6u32, 0x52166b27u32, 0x521803cbu32, 0x521d2ea4u32, 0x52209c67u32, 0x52242830u32, 0x52248448u32, - 0x522c1b61u32, 0x522cf6dbu32, 0x5232ba47u32, 0x5234aeb9u32, 0x523536fcu32, 0x52370462u32, 0x523d74a8u32, 0x523ebb3du32, - 0x52408f59u32, 0x52457ebau32, 0x52481fe1u32, 0x5248279au32, 0x524c2612u32, 0x524d5c32u32, 0x5256fd9bu32, 0x525bb1ceu32, - 0x525de06bu32, 0x52643feau32, 0x526aac66u32, 0x526b35acu32, 0x526d5693u32, 0x527e1edbu32, 0x527f89e2u32, 0x528238eau32, - 0x528618a3u32, 0x528d1cf9u32, 0x528dcec5u32, 0x528ed792u32, 0x528ef9acu32, 0x5295b387u32, 0x52981e07u32, 0x52a33cacu32, - 0x52a80de2u32, 0x52a9edcbu32, 0x52aba67fu32, 0x52b4200eu32, 0x52bec3b3u32, 0x52c46d2du32, 0x52d05b5au32, 0x52d15ee7u32, - 0x52d31b8au32, 0x52da30edu32, 0x52da52e2u32, 0x52da6025u32, 0x52dc8360u32, 0x52e0205bu32, 0x52e29d39u32, 0x52e312f9u32, - 0x52f06ecbu32, 0x52f83484u32, 0x52fe87c7u32, 0x530234afu32, 0x5304a0cau32, 0x530977f9u32, 0x530adb47u32, 0x5315b627u32, - 0x5319149du32, 0x531949a9u32, 0x531d81b4u32, 0x5322a3f2u32, 0x532433fdu32, 0x5328af37u32, 0x5328f8f5u32, 0x5338a88bu32, - 0x5338b204u32, 0x533beb05u32, 0x533fe2a6u32, 0x5345720bu32, 0x534e6730u32, 0x536b1632u32, 0x536bc3eau32, 0x537ab656u32, - 0x537e331cu32, 0x537f3be9u32, 0x538dd164u32, 0x538f1202u32, 0x5390ebf0u32, 0x53912efau32, 0x539b6f60u32, 0x539cd4ddu32, - 0x53a20a57u32, 0x53acb4b0u32, 0x53af8559u32, 0x53d3e722u32, 0x53d51e22u32, 0x53da2544u32, 0x53dab5fbu32, 0x53dd2445u32, - 0x53dec55du32, 0x53ee165eu32, 0x53fb36bau32, 0x53fe9a4au32, 0x54037206u32, 0x5407a27du32, 0x540841ecu32, 0x54340495u32, - 0x543ee110u32, 0x54406585u32, 0x54440612u32, 0x5444ad28u32, 0x544646e4u32, 0x5448072cu32, 0x545352d9u32, 0x54542a64u32, - 0x5455bc54u32, 0x54565f8du32, 0x54566f25u32, 0x545d7fa5u32, 0x54644d66u32, 0x546fcd8au32, 0x5470abc9u32, 0x5474c22au32, - 0x54754783u32, 0x54766c4bu32, 0x548106fau32, 0x548c4bf3u32, 0x54919eb8u32, 0x5498f484u32, 0x549d7c50u32, 0x54a48922u32, - 0x54a82c04u32, 0x54b8f38fu32, 0x54c13d51u32, 0x54c20464u32, 0x54c3817du32, 0x54c8cfd0u32, 0x54d08a18u32, 0x54d1a82cu32, - 0x54d21458u32, 0x54d3edcdu32, 0x54d56a76u32, 0x54d6866du32, 0x54d7ff4cu32, 0x54e1bfd0u32, 0x54e3d5a4u32, 0x54e5b659u32, - 0x54f75e08u32, 0x54fa3eb9u32, 0x54fc4fabu32, 0x5500bfd7u32, 0x5500c74fu32, 0x5508389du32, 0x5508cb71u32, 0x5510bc4du32, - 0x5514c646u32, 0x551794e0u32, 0x551f386cu32, 0x55208c88u32, 0x553361e3u32, 0x55339d8cu32, 0x55347a10u32, 0x554c2d58u32, - 0x555042bdu32, 0x555d4be4u32, 0x555f2923u32, 0x556235d7u32, 0x556a9116u32, 0x556c4d94u32, 0x5579b751u32, 0x557c9977u32, - 0x5581b52du32, 0x5583b72fu32, 0x55884062u32, 0x558c55e3u32, 0x55920303u32, 0x5592b8a7u32, 0x5593d683u32, 0x5594f66eu32, - 0x55958d44u32, 0x55a13c68u32, 0x55a22466u32, 0x55a4a210u32, 0x55a5c7c6u32, 0x55a71dc7u32, 0x55aa77a1u32, 0x55ac666bu32, - 0x55ad02f0u32, 0x55b0edb9u32, 0x55b6d4c7u32, 0x55b9280du32, 0x55b9717eu32, 0x55bbc678u32, 0x55c0ae78u32, 0x55c9cae3u32, - 0x55c9d850u32, 0x55ca520cu32, 0x55cf2110u32, 0x55cf721cu32, 0x55d328ecu32, 0x55d5c564u32, 0x55d5d309u32, 0x55d917b8u32, - 0x55d9c97du32, 0x55ddb632u32, 0x55e706ceu32, 0x55eca271u32, 0x55ecebf7u32, 0x55ee43ebu32, 0x55ef2f89u32, 0x55ef7cf5u32, - 0x55f24fa6u32, 0x55f8935du32, 0x55fd52d0u32, 0x5601d8d7u32, 0x560c77afu32, 0x5610f939u32, 0x561a9aefu32, 0x5622bee3u32, - 0x5626e892u32, 0x562c3cdau32, 0x56387737u32, 0x56392323u32, 0x5643cf76u32, 0x5659dfa5u32, 0x565db357u32, 0x56643320u32, - 0x5669875au32, 0x56735761u32, 0x5675006fu32, 0x567ac0dcu32, 0x567fce8eu32, 0x568340c2u32, 0x56880eadu32, 0x56949d1bu32, - 0x569c4bc2u32, 0x569e9fe2u32, 0x56a90338u32, 0x56aa46e2u32, 0x56aa83c0u32, 0x56aae367u32, 0x56afbee2u32, 0x56b1a3e6u32, - 0x56b6c203u32, 0x56b7a509u32, 0x56bd96c5u32, 0x56c3e256u32, 0x56c71f4eu32, 0x56c7203du32, 0x56d8f466u32, 0x56dbfe2au32, - 0x56dd6a6fu32, 0x56ddd98bu32, 0x56e180cau32, 0x56e31610u32, 0x56e50a15u32, 0x56e5cfa2u32, 0x56e8470du32, 0x56e851a6u32, - 0x56e88934u32, 0x56f5e308u32, 0x56f9f86du32, 0x56fc4d01u32, 0x570c7c3cu32, 0x57160a90u32, 0x571681a4u32, 0x571c066au32, - 0x571f7339u32, 0x572bd24bu32, 0x572ffa0eu32, 0x57377b45u32, 0x573b482fu32, 0x57405d9fu32, 0x5744e123u32, 0x5746639eu32, - 0x5746c152u32, 0x57516855u32, 0x5757e245u32, 0x575bb19du32, 0x575c7998u32, 0x5765dcf1u32, 0x57664eefu32, 0x57680423u32, - 0x576c872cu32, 0x576d79aeu32, 0x5770c339u32, 0x57751e29u32, 0x5777032du32, 0x57855312u32, 0x57869d02u32, 0x57896adeu32, - 0x578a9796u32, 0x578f4c28u32, 0x5791bd80u32, 0x579381cbu32, 0x5799cfa6u32, 0x579b63fdu32, 0x579d68e1u32, 0x579da544u32, - 0x579f7d00u32, 0x57a4fde7u32, 0x57a86373u32, 0x57b9badau32, 0x57bb9c5du32, 0x57be5726u32, 0x57bfcd0eu32, 0x57c3e4dau32, - 0x57cf33f8u32, 0x57d2b54bu32, 0x57e36989u32, 0x57e5139bu32, 0x57ef0c36u32, 0x57ef44fbu32, 0x57f5d1e1u32, 0x57fbf5f1u32, - 0x58034febu32, 0x58037d54u32, 0x5805cb7au32, 0x58123df2u32, 0x5813a957u32, 0x5813e239u32, 0x581b06cfu32, 0x581c8ddeu32, - 0x5822f780u32, 0x58240feeu32, 0x5827a8aeu32, 0x582a783cu32, 0x582ae956u32, 0x582d3bf3u32, 0x5832218cu32, 0x58388580u32, - 0x5838a29bu32, 0x583c12aau32, 0x5857d2beu32, 0x58630dafu32, 0x5864c4cfu32, 0x5867fa4au32, 0x5871c57cu32, 0x587b9525u32, - 0x587dfd14u32, 0x587ec94cu32, 0x58853ebfu32, 0x589251b8u32, 0x589768e5u32, 0x58aa2d34u32, 0x58bb7dc8u32, 0x58be957eu32, - 0x58c2b554u32, 0x58c4fff9u32, 0x58c8b5aeu32, 0x58c962d6u32, 0x58ca25c2u32, 0x58cf98e6u32, 0x58d36369u32, 0x58d46b89u32, - 0x58d7c91du32, 0x58d86f98u32, 0x58dc58b9u32, 0x58ddc0fbu32, 0x58df2314u32, 0x58e15c17u32, 0x58f1a931u32, 0x58f3a8a8u32, - 0x58f728a7u32, 0x5911ba4au32, 0x5914e848u32, 0x5915c156u32, 0x591f6d6fu32, 0x59279a25u32, 0x593406c9u32, 0x593b97e6u32, - 0x593e3a44u32, 0x5943847fu32, 0x5945d049u32, 0x59471675u32, 0x594955c7u32, 0x594bdff2u32, 0x5962a5a8u32, 0x596c5513u32, - 0x5971ea27u32, 0x59786912u32, 0x597cfeeau32, 0x59816d77u32, 0x59835a3fu32, 0x5986bf21u32, 0x5989bb31u32, 0x59966fb1u32, - 0x5996d466u32, 0x599cef88u32, 0x599ea856u32, 0x59ac933du32, 0x59b1d74fu32, 0x59b32a48u32, 0x59b51348u32, 0x59b997d3u32, - 0x59bed554u32, 0x59c04229u32, 0x59c4b06cu32, 0x59c657edu32, 0x59ca463du32, 0x59cc9b6fu32, 0x59d258f6u32, 0x59d2ddd1u32, - 0x59dd1a72u32, 0x59de6643u32, 0x59e0d93au32, 0x59ecf6b4u32, 0x59edad15u32, 0x59efe297u32, 0x59f2f27bu32, 0x59f8260bu32, - 0x5a00f6e5u32, 0x5a17f74eu32, 0x5a1939dfu32, 0x5a1cd112u32, 0x5a1d6cabu32, 0x5a1fb9b6u32, 0x5a245695u32, 0x5a26a082u32, - 0x5a2ab7dcu32, 0x5a2cfac2u32, 0x5a2eafa7u32, 0x5a333672u32, 0x5a371191u32, 0x5a424e4cu32, 0x5a46296bu32, 0x5a482cbdu32, - 0x5a49827fu32, 0x5a49a383u32, 0x5a5347feu32, 0x5a55eb7cu32, 0x5a66744fu32, 0x5a6f2be1u32, 0x5a701fd1u32, 0x5a7404a6u32, - 0x5a78c990u32, 0x5a8e8c6cu32, 0x5a9e3134u32, 0x5a9e59dcu32, 0x5aa2c8d6u32, 0x5aac85b2u32, 0x5ab095a3u32, 0x5ab8de2du32, - 0x5ab9fd8eu32, 0x5abc5c9du32, 0x5abe9cd4u32, 0x5abfa40fu32, 0x5ac14f5fu32, 0x5aca4630u32, 0x5aca879bu32, 0x5acc6c15u32, - 0x5acf833bu32, 0x5ad062a6u32, 0x5ad2b762u32, 0x5ad6bbacu32, 0x5ad97807u32, 0x5ae084ccu32, 0x5ae1d728u32, 0x5ae629beu32, - 0x5ae63254u32, 0x5ae86cbeu32, 0x5ae9f0f5u32, 0x5aea83b0u32, 0x5af6000cu32, 0x5afb262bu32, 0x5b04da14u32, 0x5b0e54a0u32, - 0x5b1f5283u32, 0x5b210dc5u32, 0x5b2bc80cu32, 0x5b2bfee6u32, 0x5b3b7984u32, 0x5b3eadcfu32, 0x5b41465du32, 0x5b480135u32, - 0x5b48169fu32, 0x5b4e4215u32, 0x5b531c14u32, 0x5b5961dcu32, 0x5b5b417bu32, 0x5b64d786u32, 0x5b7ef7e6u32, 0x5b84d398u32, - 0x5b88b86du32, 0x5b8d554cu32, 0x5b8f8b6bu32, 0x5b91c2a0u32, 0x5b971f75u32, 0x5b9adba2u32, 0x5b9d0e68u32, 0x5ba15783u32, - 0x5ba1802bu32, 0x5ba63ecfu32, 0x5baa72f9u32, 0x5bb01eaau32, 0x5bb893aeu32, 0x5bbba1b3u32, 0x5bbd3c15u32, 0x5bc2399eu32, - 0x5bc682a7u32, 0x5bd80e03u32, 0x5bd91f49u32, 0x5bd99100u32, 0x5bdd6852u32, 0x5beb77d6u32, 0x5bebcafdu32, 0x5bfa0f2au32, - 0x5bfc9cdfu32, 0x5bff4b98u32, 0x5bffd9e4u32, 0x5c0be078u32, 0x5c10f063u32, 0x5c123277u32, 0x5c12a245u32, 0x5c225655u32, - 0x5c263374u32, 0x5c2b6b61u32, 0x5c3741a3u32, 0x5c386539u32, 0x5c43ae72u32, 0x5c44347du32, 0x5c55e375u32, 0x5c58fa6au32, - 0x5c5cfd99u32, 0x5c6178e9u32, 0x5c6c6d00u32, 0x5c6e3708u32, 0x5c6f2ecau32, 0x5c752933u32, 0x5c76a291u32, 0x5c8260f4u32, - 0x5c8904f2u32, 0x5ca80381u32, 0x5cadf5eau32, 0x5cb4fc60u32, 0x5cb6c435u32, 0x5cbce0dcu32, 0x5cc354eau32, 0x5cc511e6u32, - 0x5cc9915bu32, 0x5cce1499u32, 0x5cd22ccbu32, 0x5cd87b01u32, 0x5cde32b3u32, 0x5ce45e99u32, 0x5ce48096u32, 0x5ce69a58u32, - 0x5cf01e01u32, 0x5cf076ecu32, 0x5cf899b0u32, 0x5d0073f6u32, 0x5d12612du32, 0x5d152468u32, 0x5d18cfa5u32, 0x5d1eaf59u32, - 0x5d20276bu32, 0x5d2b7b74u32, 0x5d2d9d24u32, 0x5d344cbdu32, 0x5d34561cu32, 0x5d351858u32, 0x5d3edba8u32, 0x5d456ce8u32, - 0x5d45c772u32, 0x5d45e0dbu32, 0x5d502313u32, 0x5d5ab884u32, 0x5d5c8565u32, 0x5d5dc44du32, 0x5d672227u32, 0x5d67bf5fu32, - 0x5d69f9dbu32, 0x5d6c36ddu32, 0x5d6dca94u32, 0x5d7210d6u32, 0x5d78a7d1u32, 0x5d7be9ebu32, 0x5d7d8bf1u32, 0x5d80e63cu32, - 0x5d846f57u32, 0x5d87ae20u32, 0x5d89dd3du32, 0x5d9969c8u32, 0x5d9afd07u32, 0x5d9d9c09u32, 0x5d9f91e5u32, 0x5da4c68au32, - 0x5dacf468u32, 0x5dad1730u32, 0x5daedceeu32, 0x5daf0e2au32, 0x5db13595u32, 0x5db8fa75u32, 0x5dbaf0f7u32, 0x5dc30ef0u32, - 0x5dc5552cu32, 0x5dcf6a72u32, 0x5dd515e3u32, 0x5ddb4c21u32, 0x5dddfb80u32, 0x5de29a88u32, 0x5de3bc10u32, 0x5de56fdbu32, - 0x5df11742u32, 0x5df69532u32, 0x5dfb97d0u32, 0x5dfdadd7u32, 0x5dff3699u32, 0x5e0bab71u32, 0x5e13ea60u32, 0x5e1821f2u32, - 0x5e1a580du32, 0x5e1d880fu32, 0x5e20882fu32, 0x5e333b21u32, 0x5e337172u32, 0x5e3413f4u32, 0x5e346c60u32, 0x5e34ca8eu32, - 0x5e3871ebu32, 0x5e38a617u32, 0x5e3a335fu32, 0x5e3b9e6eu32, 0x5e435a06u32, 0x5e45cc52u32, 0x5e4a1566u32, 0x5e4db1ecu32, - 0x5e4ec48fu32, 0x5e4f1532u32, 0x5e5274f8u32, 0x5e5e3965u32, 0x5e5ffbf7u32, 0x5e610f83u32, 0x5e62145fu32, 0x5e639e99u32, - 0x5e64445eu32, 0x5e775a04u32, 0x5e786b2fu32, 0x5e7873e1u32, 0x5e80b7b4u32, 0x5e90081du32, 0x5e9099acu32, 0x5e91e436u32, - 0x5e92b2a3u32, 0x5e967adfu32, 0x5e9c6e05u32, 0x5ea170c1u32, 0x5ea2d01fu32, 0x5eaf14e4u32, 0x5eb29a44u32, 0x5eb5ef33u32, - 0x5ebefd78u32, 0x5ec0dc63u32, 0x5ecd7c57u32, 0x5ecedd5au32, 0x5ed206b1u32, 0x5ed574fau32, 0x5ee2fcf1u32, 0x5ee3378cu32, - 0x5ee7b597u32, 0x5ee87146u32, 0x5ee954d2u32, 0x5eec1bb1u32, 0x5ef39c1fu32, 0x5ef92593u32, 0x5f0d7722u32, 0x5f14aab7u32, - 0x5f1e7b1au32, 0x5f2dbaccu32, 0x5f307d61u32, 0x5f386a04u32, 0x5f4d2f3eu32, 0x5f548648u32, 0x5f5caf6bu32, 0x5f5d29e3u32, - 0x5f5d61bbu32, 0x5f5ea1fdu32, 0x5f66c9c6u32, 0x5f744260u32, 0x5f76f953u32, 0x5f7840deu32, 0x5f79a1d2u32, 0x5f80b9aau32, - 0x5f86a886u32, 0x5f92c8fcu32, 0x5f93663cu32, 0x5f9afedbu32, 0x5f9c99bdu32, 0x5fa4693au32, 0x5fb2384au32, 0x5fb8bde8u32, - 0x5fb9b8a9u32, 0x5fbab194u32, 0x5fbb2a65u32, 0x5fd13806u32, 0x5fdbc476u32, 0x5fdc24d8u32, 0x5fddb427u32, 0x5fe1c409u32, - 0x5fe20e7fu32, 0x5fe3a303u32, 0x5fe68570u32, 0x5fe993b5u32, 0x5fe9a0c0u32, 0x5ff1a6fbu32, 0x5ff493c1u32, 0x5ff4f3c5u32, - 0x5ff62ccdu32, 0x5ff6728cu32, 0x5ffb05c7u32, 0x5ffbd791u32, 0x5ffd254bu32, 0x601554e3u32, 0x6019244au32, 0x6019725du32, - 0x601ae1f4u32, 0x601ee953u32, 0x601f489bu32, 0x602ac65eu32, 0x602dfd1du32, 0x602e71e3u32, 0x60371cf6u32, 0x603876d0u32, - 0x6038a701u32, 0x604bfd22u32, 0x604df8f3u32, 0x604fb5b2u32, 0x604fc501u32, 0x6051a8a2u32, 0x60544f5bu32, 0x605cdb62u32, - 0x606493c9u32, 0x6066ed89u32, 0x606759dcu32, 0x606d5216u32, 0x6076905eu32, 0x60772945u32, 0x60776f04u32, 0x607bcde4u32, - 0x608034d1u32, 0x608154ddu32, 0x6083d0a9u32, 0x609ec6c2u32, 0x60a24001u32, 0x60a69eb2u32, 0x60a75637u32, 0x60b1bbe4u32, - 0x60b95d35u32, 0x60bd505fu32, 0x60bd6250u32, 0x60be9c72u32, 0x60bea30fu32, 0x60bf70deu32, 0x60c5d085u32, 0x60caae65u32, - 0x60cc540eu32, 0x60d0ae66u32, 0x60d10e0cu32, 0x60d18ef2u32, 0x60d55bb7u32, 0x60df66e3u32, 0x60df7d2du32, 0x60e16e3fu32, - 0x60ee8ae7u32, 0x60f6d2b7u32, 0x61001027u32, 0x6100e0cfu32, 0x610b4289u32, 0x610e9f9du32, 0x6110f721u32, 0x6111645fu32, - 0x611635c9u32, 0x611a0e48u32, 0x611c62c4u32, 0x611d5663u32, 0x61244e5cu32, 0x6127adb8u32, 0x6135505bu32, 0x61384d0du32, - 0x613bf923u32, 0x613e2aa8u32, 0x613e2e41u32, 0x6141c9e6u32, 0x6149acfbu32, 0x6151ba39u32, 0x61565f07u32, 0x6157faadu32, - 0x615c185au32, 0x616302e6u32, 0x6163e18cu32, 0x6165d6f7u32, 0x616830eeu32, 0x616fb3a1u32, 0x61779f39u32, 0x617b5492u32, - 0x617ff960u32, 0x618887ccu32, 0x618ce5ecu32, 0x618d734cu32, 0x618db694u32, 0x618dc068u32, 0x618e46c9u32, 0x6194b58cu32, - 0x61a3b9ffu32, 0x61aacd30u32, 0x61b24b8au32, 0x61b3296cu32, 0x61ba12a7u32, 0x61bc9dc2u32, 0x61c6d8ecu32, 0x61c72d07u32, - 0x61c97234u32, 0x61cdd5cfu32, 0x61ce880au32, 0x61d629bbu32, 0x61d98309u32, 0x61dd19f5u32, 0x61e0a283u32, 0x61e303a8u32, - 0x61e4421fu32, 0x61e96897u32, 0x61ef45a1u32, 0x61f27ce9u32, 0x61f759abu32, 0x61fd49ccu32, 0x6200ab53u32, 0x6201d016u32, - 0x620533c9u32, 0x6207a744u32, 0x620e7180u32, 0x6215fc23u32, 0x62161e89u32, 0x623c9ee5u32, 0x624b0f8cu32, 0x624f0e0au32, - 0x624f4e7du32, 0x624fbe30u32, 0x6250534fu32, 0x625c895fu32, 0x6265b705u32, 0x62673fc3u32, 0x6268cfe0u32, 0x626ca79fu32, - 0x62702b13u32, 0x62706ac4u32, 0x6273b15cu32, 0x62753d51u32, 0x62783ebcu32, 0x627d6b0fu32, 0x6284ae5au32, 0x628545d6u32, - 0x628baa33u32, 0x628e250du32, 0x629378e0u32, 0x629e49a2u32, 0x62a2520cu32, 0x62a668ebu32, 0x62a6ca41u32, 0x62ad67b2u32, - 0x62b2c2e4u32, 0x62b8ddecu32, 0x62b96055u32, 0x62c2b821u32, 0x62ca02b2u32, 0x62d60982u32, 0x62e0b1eau32, 0x62f1e023u32, - 0x62f29655u32, 0x62f474b1u32, 0x62f5005du32, 0x62fc0897u32, 0x63063ee4u32, 0x6310e170u32, 0x63168d3eu32, 0x63281782u32, - 0x632edfc2u32, 0x6330de0fu32, 0x6331d0bcu32, 0x633fab83u32, 0x633fbfbcu32, 0x63432841u32, 0x6349f18bu32, 0x634ac423u32, - 0x634dd31eu32, 0x635b327au32, 0x6368d33bu32, 0x63692e3cu32, 0x636c5bc9u32, 0x636c9bc3u32, 0x6378a508u32, 0x6380af2du32, - 0x6384163bu32, 0x638f9a29u32, 0x63937953u32, 0x6396751fu32, 0x63995f33u32, 0x63a8c729u32, 0x63a9a230u32, 0x63b40d0du32, - 0x63bcb7a1u32, 0x63c16014u32, 0x63c20f48u32, 0x63c35de8u32, 0x63c6a57eu32, 0x63cf9967u32, 0x63d4a14cu32, 0x63deb88du32, - 0x63e0bc49u32, 0x63e449c1u32, 0x63f4475bu32, 0x63f5df5cu32, 0x63f8a973u32, 0x64013ce4u32, 0x6401b76fu32, 0x64049c1au32, - 0x6407e0bcu32, 0x640978bau32, 0x640d033bu32, 0x640dbb4bu32, 0x640fd922u32, 0x64162e3fu32, 0x641ddf49u32, 0x641e9cc2u32, - 0x6429452au32, 0x642febf6u32, 0x6434156du32, 0x643e109du32, 0x643f6a15u32, 0x644b061cu32, 0x64536eb4u32, 0x645ce6fcu32, - 0x646362f3u32, 0x646703a0u32, 0x646bdec0u32, 0x646cf844u32, 0x646f33a0u32, 0x646fa846u32, 0x647088b0u32, 0x6471a61du32, - 0x6474865au32, 0x64784293u32, 0x647d8af7u32, 0x647f391bu32, 0x648189eau32, 0x6486daaeu32, 0x648c6ce3u32, 0x64997499u32, - 0x649d15bfu32, 0x64a1375bu32, 0x64a5bed3u32, 0x64a60047u32, 0x64a703e3u32, 0x64af4032u32, 0x64af806du32, 0x64b659e9u32, - 0x64bbe1c8u32, 0x64bc243au32, 0x64bd5a07u32, 0x64c18c1cu32, 0x64c5912bu32, 0x64cec350u32, 0x64cf06fbu32, 0x64cf0da0u32, - 0x64d574b0u32, 0x64d71587u32, 0x64dbed37u32, 0x64deb8c8u32, 0x64e224c9u32, 0x64ed2fa0u32, 0x64ed5506u32, 0x64efb062u32, - 0x65008202u32, 0x650cd654u32, 0x650ec2b7u32, 0x6515c781u32, 0x651defe4u32, 0x652cf8a6u32, 0x652f5b64u32, 0x653044e8u32, - 0x6532e8dcu32, 0x6535c720u32, 0x653b1c90u32, 0x653e865au32, 0x6558ed9bu32, 0x655ba366u32, 0x6560d291u32, 0x6567566cu32, - 0x6567c6a1u32, 0x656997f1u32, 0x6569c80du32, 0x656baee5u32, 0x656c19b1u32, 0x65700ea1u32, 0x657716bfu32, 0x6584f55eu32, - 0x65904655u32, 0x6590a339u32, 0x6592c834u32, 0x659ce70fu32, 0x659dfe29u32, 0x65a0e52au32, 0x65a4f677u32, 0x65ae5e2fu32, - 0x65b1e3b8u32, 0x65b2b5fcu32, 0x65ba893au32, 0x65c2478eu32, 0x65c99a6au32, 0x65ca17b9u32, 0x65d12a6bu32, 0x65d18b00u32, - 0x65db5afeu32, 0x65f054b3u32, 0x65f628bcu32, 0x65fdd0acu32, 0x65fe4318u32, 0x6609b4bbu32, 0x660f4dbeu32, 0x660f5508u32, - 0x66185518u32, 0x661a1396u32, 0x662100d1u32, 0x6622ee7eu32, 0x6623210fu32, 0x6624c178u32, 0x6626952fu32, 0x6626f575u32, - 0x6629617du32, 0x662990acu32, 0x662bd0b0u32, 0x662c8afeu32, 0x662cba2du32, 0x662d2b6eu32, 0x662d8f2eu32, 0x663f3f16u32, - 0x6641af8cu32, 0x66435a33u32, 0x6643a5fdu32, 0x6645b22du32, 0x6649a32eu32, 0x664af128u32, 0x66585799u32, 0x666468aeu32, - 0x66650861u32, 0x666f15d3u32, 0x667f9331u32, 0x6682876eu32, 0x66830d62u32, 0x6683bac1u32, 0x668a3559u32, 0x669a6f7au32, - 0x669ac39au32, 0x66a133f4u32, 0x66a61ecfu32, 0x66a73744u32, 0x66a8818du32, 0x66bab58du32, 0x66baed06u32, 0x66c6a1bdu32, - 0x66c73f70u32, 0x66c84970u32, 0x66c87d0du32, 0x66cecb6au32, 0x66d06089u32, 0x66d1aa15u32, 0x66d5d4d2u32, 0x66dd4c38u32, - 0x66e0cb8au32, 0x66e2f512u32, 0x66e41f5eu32, 0x66eab59du32, 0x66ead7d1u32, 0x66ee6036u32, 0x66f0a12eu32, 0x66f62272u32, - 0x67081f28u32, 0x67117603u32, 0x671669b4u32, 0x671ab1bdu32, 0x6725ccecu32, 0x67361a5eu32, 0x673a13ecu32, 0x673c71c8u32, - 0x673f9624u32, 0x6743e934u32, 0x6751bcafu32, 0x675a5229u32, 0x675aae13u32, 0x675c7512u32, 0x675f0879u32, 0x6760543cu32, - 0x6762822cu32, 0x676b82bcu32, 0x6770af9du32, 0x67714349u32, 0x6775ccdeu32, 0x6777671cu32, 0x677fe07bu32, 0x6783ad4cu32, - 0x678d19acu32, 0x678f471cu32, 0x6795b776u32, 0x679d5c1du32, 0x67a69df6u32, 0x67b40b00u32, 0x67b71efcu32, 0x67c28effu32, - 0x67c7b316u32, 0x67c7e4f6u32, 0x67c97205u32, 0x67ced8d9u32, 0x67d39642u32, 0x67e5106bu32, 0x67ef5993u32, 0x67f1b056u32, - 0x67f659b1u32, 0x67fbcc43u32, 0x67fe2380u32, 0x680337b2u32, 0x68049a54u32, 0x68051ba9u32, 0x680ced0eu32, 0x680e6ad4u32, - 0x681305a3u32, 0x6815ac95u32, 0x6819c3fbu32, 0x681a57a7u32, 0x681b103eu32, 0x681e96c6u32, 0x6820be77u32, 0x6824eaa5u32, - 0x682abb58u32, 0x6834c8b9u32, 0x68390567u32, 0x683ccf89u32, 0x683ff36bu32, 0x6842ee3au32, 0x684a98c0u32, 0x684e8750u32, - 0x684f9e85u32, 0x68505508u32, 0x6852c267u32, 0x6855ee3cu32, 0x685748ebu32, 0x685801a2u32, 0x685a169fu32, 0x68606a3eu32, - 0x6865ffa7u32, 0x68691f61u32, 0x686aa343u32, 0x686de5d4u32, 0x687351cdu32, 0x687a643fu32, 0x687b4a31u32, 0x687d63b3u32, - 0x68804bdcu32, 0x6885a8acu32, 0x6891a488u32, 0x68950f22u32, 0x68971cd9u32, 0x689802abu32, 0x689b06c4u32, 0x689eb3eeu32, - 0x68a05d58u32, 0x68a0c7f1u32, 0x68a35c4fu32, 0x68a39fe9u32, 0x68a77c62u32, 0x68a9e47fu32, 0x68afe173u32, 0x68b8d3f4u32, - 0x68c1e0fcu32, 0x68c6de91u32, 0x68c72b75u32, 0x68c86a46u32, 0x68c97811u32, 0x68d47e95u32, 0x68d664b0u32, 0x68daf066u32, - 0x68ddef5du32, 0x68de4e98u32, 0x68df26d8u32, 0x68e6784fu32, 0x68f32b3bu32, 0x68f407c3u32, 0x68fd974au32, 0x69060d7eu32, - 0x690d4c41u32, 0x69106e0fu32, 0x691bc9c7u32, 0x6923b640u32, 0x6926b8d2u32, 0x69281d90u32, 0x69298b1bu32, 0x69373600u32, - 0x6939ba4bu32, 0x693f271eu32, 0x694521eeu32, 0x695ed323u32, 0x696319b0u32, 0x696617a5u32, 0x696f2e8cu32, 0x6972bbf5u32, - 0x6972dd45u32, 0x6974b0bcu32, 0x6975890cu32, 0x69786b60u32, 0x697dac8fu32, 0x698a2b81u32, 0x698e2defu32, 0x6992cc5du32, - 0x6993ea5fu32, 0x69944878u32, 0x69949fd8u32, 0x69a5b241u32, 0x69aace85u32, 0x69abee12u32, 0x69ae0d73u32, 0x69b1767eu32, - 0x69b7041cu32, 0x69c019f9u32, 0x69c1701bu32, 0x69c49482u32, 0x69c69216u32, 0x69caf966u32, 0x69cd6193u32, 0x69cedfc6u32, - 0x69dba884u32, 0x69dd26b3u32, 0x69de14d5u32, 0x69e358a1u32, 0x69e42e58u32, 0x69e4769fu32, 0x69e7e02fu32, 0x69e9957cu32, - 0x69f1686cu32, 0x69f417dau32, 0x69f81491u32, 0x69f8b4b5u32, 0x69fe1f7cu32, 0x69ff1e46u32, 0x6a0292cbu32, 0x6a081d4bu32, - 0x6a0877c5u32, 0x6a0bfd67u32, 0x6a1c9ce7u32, 0x6a2d4d90u32, 0x6a3370f0u32, 0x6a33842fu32, 0x6a3c9ca5u32, 0x6a3cc853u32, - 0x6a3ea17fu32, 0x6a4060adu32, 0x6a45ce11u32, 0x6a4894deu32, 0x6a48c198u32, 0x6a58726bu32, 0x6a5ad4dfu32, 0x6a63b637u32, - 0x6a68f7ebu32, 0x6a6c33bbu32, 0x6a6e4380u32, 0x6a758b9fu32, 0x6a772453u32, 0x6a80789du32, 0x6a826379u32, 0x6a848469u32, - 0x6a864208u32, 0x6a893de0u32, 0x6a94ad5bu32, 0x6a9d6f7fu32, 0x6aa6bcffu32, 0x6aaa92b8u32, 0x6aadedb9u32, 0x6ab0188au32, - 0x6ab02f6au32, 0x6ab05db1u32, 0x6ab1e4b7u32, 0x6ab3dd18u32, 0x6abde11bu32, 0x6ac20d24u32, 0x6ac76a67u32, 0x6acb9a15u32, - 0x6ad28287u32, 0x6ad45cbfu32, 0x6ad64f68u32, 0x6ad8b0d2u32, 0x6ad8da6au32, 0x6add8eb0u32, 0x6ae51f9bu32, 0x6ae6cf2fu32, - 0x6ae78957u32, 0x6af1a4bfu32, 0x6af2796cu32, 0x6af6cae4u32, 0x6af87f27u32, 0x6af9ea9fu32, 0x6b173b3au32, 0x6b216e8eu32, - 0x6b23bbb1u32, 0x6b25add4u32, 0x6b264395u32, 0x6b26910fu32, 0x6b27765fu32, 0x6b2eb30du32, 0x6b2f5972u32, 0x6b33f693u32, - 0x6b3bd8dfu32, 0x6b3f1b2du32, 0x6b433c08u32, 0x6b4448dcu32, 0x6b458dd8u32, 0x6b59ac7du32, 0x6b622723u32, 0x6b693ee6u32, - 0x6b69e336u32, 0x6b6bbe36u32, 0x6b7175b1u32, 0x6b769e6fu32, 0x6b78ab18u32, 0x6b82c59eu32, 0x6b89fc73u32, 0x6b8e2793u32, - 0x6b94a1eau32, 0x6b996f88u32, 0x6b9af8e5u32, 0x6ba36925u32, 0x6ba732e7u32, 0x6baa3f64u32, 0x6baa7854u32, 0x6baafd71u32, - 0x6babaec1u32, 0x6bade4c0u32, 0x6bb01ddeu32, 0x6bb75982u32, 0x6bb9c6e2u32, 0x6bba3e7au32, 0x6bc3600au32, 0x6bc5e6e4u32, - 0x6bc649b0u32, 0x6bc93b65u32, 0x6bcd73e9u32, 0x6bcdf5c6u32, 0x6bceaf04u32, 0x6bd323d9u32, 0x6bd5b72eu32, 0x6bd79ea9u32, - 0x6be0a639u32, 0x6be7bfceu32, 0x6c0d2d45u32, 0x6c1fe7bau32, 0x6c27b08bu32, 0x6c2b7327u32, 0x6c2f47f6u32, 0x6c31f0beu32, - 0x6c36d4b8u32, 0x6c3c387bu32, 0x6c3f67dfu32, 0x6c42999au32, 0x6c467657u32, 0x6c4d39deu32, 0x6c594d23u32, 0x6c5d1f9bu32, - 0x6c6d1f15u32, 0x6c6eca81u32, 0x6c7181e9u32, 0x6c78e11bu32, 0x6c7a4164u32, 0x6c81d973u32, 0x6c86ab9eu32, 0x6c8a63c3u32, - 0x6c8dee26u32, 0x6c96d207u32, 0x6c98ede5u32, 0x6c9a90acu32, 0x6c9bd95du32, 0x6ca8a510u32, 0x6cae9cdbu32, 0x6cc0e8b0u32, - 0x6cc17035u32, 0x6cc47fe1u32, 0x6cc67432u32, 0x6ccada80u32, 0x6cd491c0u32, 0x6cd4f256u32, 0x6cdaae23u32, 0x6cdc0325u32, - 0x6cdf4fd1u32, 0x6ce948bfu32, 0x6ceada3cu32, 0x6ceb5a58u32, 0x6cf7df96u32, 0x6cfddb58u32, 0x6d004229u32, 0x6d03ee82u32, - 0x6d0bd2c9u32, 0x6d0f2d98u32, 0x6d16d358u32, 0x6d1c37ebu32, 0x6d1eaa01u32, 0x6d23d99bu32, 0x6d2dee31u32, 0x6d2e6b58u32, - 0x6d368088u32, 0x6d3fb264u32, 0x6d42956du32, 0x6d4e1561u32, 0x6d4f5ab9u32, 0x6d53935eu32, 0x6d53a988u32, 0x6d6152dfu32, - 0x6d62f8cdu32, 0x6d6488e0u32, 0x6d6b327cu32, 0x6d6cfe57u32, 0x6d76fa85u32, 0x6d8568ecu32, 0x6d86e3f2u32, 0x6d87e19du32, - 0x6d8999f2u32, 0x6d8b6ff7u32, 0x6d8ca8b3u32, 0x6d9281feu32, 0x6d9ad706u32, 0x6db56fa6u32, 0x6db741e8u32, 0x6dbb44abu32, - 0x6dbfb75fu32, 0x6dc0865fu32, 0x6dc2c055u32, 0x6dc32257u32, 0x6dc6c8feu32, 0x6dcb2fe6u32, 0x6dd45116u32, 0x6dd452b6u32, - 0x6de30535u32, 0x6de8b56cu32, 0x6defa823u32, 0x6df40b14u32, 0x6dfe779bu32, 0x6e033a59u32, 0x6e061e81u32, 0x6e0f6e0du32, - 0x6e0ff40du32, 0x6e10ce56u32, 0x6e1217d9u32, 0x6e1b8b97u32, 0x6e1c232eu32, 0x6e1cb806u32, 0x6e1cc27fu32, 0x6e2b59a4u32, - 0x6e2c6aa0u32, 0x6e37b170u32, 0x6e3b8366u32, 0x6e3c680eu32, 0x6e3c7aa9u32, 0x6e406a01u32, 0x6e412a91u32, 0x6e41bad6u32, - 0x6e425594u32, 0x6e483f57u32, 0x6e515ad5u32, 0x6e57c346u32, 0x6e5a9507u32, 0x6e64a076u32, 0x6e729864u32, 0x6e7d4f09u32, - 0x6e812cf8u32, 0x6e828169u32, 0x6e8b4f61u32, 0x6e8c3541u32, 0x6e8cb31fu32, 0x6e9342f6u32, 0x6e9795feu32, 0x6ea31f80u32, - 0x6eab4dd9u32, 0x6eae8f08u32, 0x6eb02d2fu32, 0x6eb55a25u32, 0x6ebf0d88u32, 0x6ec45bd7u32, 0x6ec74ef1u32, 0x6ecfb528u32, - 0x6eda07c2u32, 0x6edd841du32, 0x6ee452e3u32, 0x6eefd8fdu32, 0x6ef83b4au32, 0x6efb53c1u32, 0x6f0c15b6u32, 0x6f11b91eu32, - 0x6f1a853bu32, 0x6f1d2a4du32, 0x6f1fb3d8u32, 0x6f246a9du32, 0x6f2860b8u32, 0x6f2d1abeu32, 0x6f2edacdu32, 0x6f32a090u32, - 0x6f371657u32, 0x6f37d170u32, 0x6f3b2be3u32, 0x6f3ea42du32, 0x6f3ef76bu32, 0x6f410359u32, 0x6f4c3b2au32, 0x6f4f8abcu32, - 0x6f622282u32, 0x6f6d0924u32, 0x6f70cf1bu32, 0x6f76724fu32, 0x6f7d0a3bu32, 0x6f8339b3u32, 0x6f88ad04u32, 0x6f8ac870u32, - 0x6f91f8e7u32, 0x6f926d64u32, 0x6fa06dfdu32, 0x6fa330e6u32, 0x6fa57060u32, 0x6fa58de9u32, 0x6fa9e459u32, 0x6faff210u32, - 0x6fb0e1b6u32, 0x6fb15398u32, 0x6fb3b36au32, 0x6fbefb48u32, 0x6fc5d0fbu32, 0x6fc9fae3u32, 0x6fd0f665u32, 0x6fd97307u32, - 0x6fda1f21u32, 0x6fe2c817u32, 0x6fe430f1u32, 0x6fee2242u32, 0x6ff471c4u32, 0x6ff66bfeu32, 0x6ff692dbu32, 0x7005700du32, - 0x7011a8cbu32, 0x70159d0bu32, 0x701fe490u32, 0x70214d6au32, 0x7024d042u32, 0x702825d2u32, 0x702fededu32, 0x703567c3u32, - 0x703e7bfcu32, 0x70439deau32, 0x70490088u32, 0x7049cab2u32, 0x704a479cu32, 0x704bb076u32, 0x705150afu32, 0x7051b7dcu32, - 0x7051d14fu32, 0x705fc7a0u32, 0x706040d4u32, 0x7061dc94u32, 0x70655a55u32, 0x7069eeb3u32, 0x706e432eu32, 0x706ef79bu32, - 0x707041ebu32, 0x70782890u32, 0x709012d9u32, 0x709a8047u32, 0x70a5ab5cu32, 0x70aef4d0u32, 0x70b2d9cbu32, 0x70c5b388u32, - 0x70c7ada8u32, 0x70d3a69du32, 0x70d4678du32, 0x70d4d619u32, 0x70d732c9u32, 0x70dd10f0u32, 0x70e50754u32, 0x70e5c7cau32, - 0x70e837e2u32, 0x70eb45b6u32, 0x70ef307fu32, 0x70ef8c4du32, 0x70f0187cu32, 0x70f742dbu32, 0x71008f7bu32, 0x7101fa30u32, - 0x7107652eu32, 0x710ac173u32, 0x710b972cu32, 0x7110a867u32, 0x7112229eu32, 0x7112ebbcu32, 0x7114ea61u32, 0x7119aafbu32, - 0x711a3480u32, 0x711b16f9u32, 0x7123b4cdu32, 0x7129b4a2u32, 0x712bda24u32, 0x712fe209u32, 0x7135d1cau32, 0x71400a77u32, - 0x7142e105u32, 0x7143dfb3u32, 0x7145e43eu32, 0x71477f84u32, 0x71499ea6u32, 0x71569cfeu32, 0x71648852u32, 0x716f34dfu32, - 0x71788090u32, 0x7179b385u32, 0x71831bf9u32, 0x718aec1eu32, 0x718e20f4u32, 0x719824a3u32, 0x719d983du32, 0x719f1a8au32, - 0x719f8e15u32, 0x71a15c04u32, 0x71ae93bau32, 0x71af40a3u32, 0x71b2a2ceu32, 0x71b67990u32, 0x71b788e4u32, 0x71bcadacu32, - 0x71c6dff9u32, 0x71c8bbd8u32, 0x71cbbf5au32, 0x71cdcccau32, 0x71d941e3u32, 0x71dbb12cu32, 0x71e13385u32, 0x71ed6a69u32, - 0x71eebd00u32, 0x7203c925u32, 0x7205f92au32, 0x7208069au32, 0x720c8cb2u32, 0x721199eeu32, 0x721220c3u32, 0x72154729u32, - 0x7215e0c2u32, 0x72177590u32, 0x721979a1u32, 0x72215a66u32, 0x7224fac3u32, 0x72292867u32, 0x72384486u32, 0x7239c1bcu32, - 0x723c8e96u32, 0x723fe46fu32, 0x7240ee1au32, 0x72426ce8u32, 0x72430575u32, 0x724447f2u32, 0x72484890u32, 0x724b2e57u32, - 0x724f8cc3u32, 0x72501f6au32, 0x72505c41u32, 0x725804bau32, 0x725fc4cbu32, 0x726adcd9u32, 0x72718bbbu32, 0x72738a6cu32, - 0x727a3ef1u32, 0x72826a7cu32, 0x728d5b21u32, 0x728f2074u32, 0x72925774u32, 0x72962009u32, 0x7298a6eau32, 0x72a4f96bu32, - 0x72a5a292u32, 0x72a64e26u32, 0x72a8e4d8u32, 0x72ad5760u32, 0x72b608b8u32, 0x72b6a547u32, 0x72b8f4a8u32, 0x72bd52d4u32, - 0x72bd7635u32, 0x72c67c42u32, 0x72ca61c9u32, 0x72cfda3du32, 0x72f4b445u32, 0x72f94896u32, 0x72fd3e77u32, 0x7300e8dbu32, - 0x7301263eu32, 0x7304b07eu32, 0x7306b93du32, 0x7308e24au32, 0x730e8d24u32, 0x7311227eu32, 0x7315b6cfu32, 0x73181fefu32, - 0x73190478u32, 0x731a3ca7u32, 0x7320cba4u32, 0x73233cceu32, 0x7323b1f4u32, 0x73240e93u32, 0x7325b75au32, 0x732af179u32, - 0x73349a84u32, 0x7339f6f5u32, 0x73473896u32, 0x73492e08u32, 0x7351ade9u32, 0x73553737u32, 0x735fffa4u32, 0x736225beu32, - 0x7365817au32, 0x736b7a57u32, 0x7375b606u32, 0x7376ee9bu32, 0x737b2fd2u32, 0x7384d132u32, 0x738d6dc2u32, 0x738f6525u32, - 0x73944644u32, 0x7394e4b4u32, 0x739bc108u32, 0x739c4757u32, 0x73a63131u32, 0x73a8b440u32, 0x73aaa95cu32, 0x73abd96eu32, - 0x73acffebu32, 0x73adb073u32, 0x73af1904u32, 0x73ba1a69u32, 0x73c85607u32, 0x73d448f6u32, 0x73dd926fu32, 0x73dde477u32, - 0x73e1cd9du32, 0x73ec90adu32, 0x73f25aa0u32, 0x73fc069eu32, 0x73fcdd5au32, 0x73fe8a8cu32, 0x73ffdb42u32, 0x740006ffu32, - 0x7400e29fu32, 0x74153078u32, 0x74169b73u32, 0x741c2c3bu32, 0x741d44d8u32, 0x741dca1du32, 0x7420bef9u32, 0x7422abbdu32, - 0x74259230u32, 0x74262771u32, 0x742933f5u32, 0x7429e55cu32, 0x742b776au32, 0x742d9e0du32, 0x74300f37u32, 0x7430aa5eu32, - 0x743597d2u32, 0x743721bbu32, 0x7438bb42u32, 0x743ae696u32, 0x744080c0u32, 0x7443bac6u32, 0x745047d2u32, 0x74570e67u32, - 0x745b299du32, 0x74628d75u32, 0x746f5fd6u32, 0x747365abu32, 0x747592e8u32, 0x747c18aeu32, 0x74805427u32, 0x7482a20cu32, - 0x7485a609u32, 0x7486adcbu32, 0x7488a8a2u32, 0x749a54d4u32, 0x74a07addu32, 0x74a3689eu32, 0x74ac8f82u32, 0x74acff3bu32, - 0x74b45a07u32, 0x74b52c1au32, 0x74ba1b77u32, 0x74bc6cd4u32, 0x74bdc908u32, 0x74c2978eu32, 0x74c5a35eu32, 0x74ce3050u32, - 0x74ddaa98u32, 0x74e2a00au32, 0x74e2d58eu32, 0x74e41efeu32, 0x74eb8515u32, 0x74ebc3d0u32, 0x74ec77ffu32, 0x74efd5a4u32, - 0x74f4fb8fu32, 0x74f5973fu32, 0x74fcdac1u32, 0x74fd8777u32, 0x74fe0c91u32, 0x75031c5eu32, 0x7507d0a7u32, 0x7514f801u32, - 0x751554c9u32, 0x7519191fu32, 0x751afa98u32, 0x7520aad3u32, 0x7523d562u32, 0x752b6b42u32, 0x752b91d4u32, 0x752d80dbu32, - 0x752e2ef5u32, 0x7532063au32, 0x7535b851u32, 0x75496e21u32, 0x754e9439u32, 0x7550196fu32, 0x7561ec08u32, 0x756300d3u32, - 0x75651429u32, 0x756e1a0bu32, 0x756e63b7u32, 0x7578ed3eu32, 0x7580e196u32, 0x75951852u32, 0x7595800du32, 0x759ba591u32, - 0x75a13286u32, 0x75a85cd8u32, 0x75af0cd7u32, 0x75b37db6u32, 0x75b6d893u32, 0x75be750du32, 0x75bee813u32, 0x75bf3a6fu32, - 0x75c91b3fu32, 0x75ca1b03u32, 0x75d0dd14u32, 0x75d51f8fu32, 0x75d68819u32, 0x75d7ed82u32, 0x75e3c341u32, 0x75e8e587u32, - 0x75ee84e7u32, 0x75eea42eu32, 0x75f01ecau32, 0x75fd3fb5u32, 0x75fd648bu32, 0x76005e7du32, 0x76054d81u32, 0x7608d2c4u32, - 0x76095e4au32, 0x760b9009u32, 0x760c182cu32, 0x761b0305u32, 0x7622cde3u32, 0x7624ff49u32, 0x762e7a47u32, 0x7632b43eu32, - 0x763794c0u32, 0x763ad335u32, 0x763b23d4u32, 0x763c1bf3u32, 0x763ded9cu32, 0x7641b9c3u32, 0x7644c50cu32, 0x76459ba2u32, - 0x76459ffau32, 0x7655e02au32, 0x765d20b9u32, 0x765e207du32, 0x765e64c6u32, 0x765ea4b7u32, 0x765fa47bu32, 0x7665798cu32, - 0x76695079u32, 0x766ac04fu32, 0x766ce83du32, 0x76797829u32, 0x767d7523u32, 0x768c98cbu32, 0x769b3773u32, 0x76a08a1du32, - 0x76a129cbu32, 0x76b4031cu32, 0x76bae552u32, 0x76bd1312u32, 0x76cab1f6u32, 0x76cff4bau32, 0x76d02240u32, 0x76d854c3u32, - 0x76d85974u32, 0x76e4a8bdu32, 0x76e8f977u32, 0x76ed0814u32, 0x76ef0171u32, 0x76fc75adu32, 0x76fd549au32, 0x770a499eu32, - 0x771089a1u32, 0x7711c9afu32, 0x77172b29u32, 0x77187472u32, 0x77193dd9u32, 0x771bc98eu32, 0x7721554cu32, 0x772974b2u32, - 0x772f428du32, 0x772f7b48u32, 0x773c9cc4u32, 0x77443455u32, 0x7752593bu32, 0x775488ceu32, 0x7756ffecu32, 0x775a86b4u32, - 0x77620782u32, 0x77676acfu32, 0x77682bb8u32, 0x77689026u32, 0x776c6bcfu32, 0x7771b0bfu32, 0x77736cbbu32, 0x777cce02u32, - 0x778d6ac9u32, 0x77967acau32, 0x779794ecu32, 0x779c01a5u32, 0x779c8328u32, 0x77a95af8u32, 0x77a9aa44u32, 0x77ae3aefu32, - 0x77b65572u32, 0x77b78037u32, 0x77b7f2eau32, 0x77bd3949u32, 0x77bdb7a3u32, 0x77c05c2eu32, 0x77c14194u32, 0x77c3bd0au32, - 0x77c432cdu32, 0x77cc998cu32, 0x77d07483u32, 0x77d0823du32, 0x77d3fe5bu32, 0x77d43092u32, 0x77d4dce4u32, 0x77d8727cu32, - 0x77d88bf6u32, 0x77d8ccfdu32, 0x77dc7ac7u32, 0x77e40388u32, 0x77f5888du32, 0x77fe04f9u32, 0x77fe3363u32, 0x7805a269u32, - 0x780abb74u32, 0x780b7e70u32, 0x780c80ebu32, 0x780cafc0u32, 0x781704ebu32, 0x7823f603u32, 0x78249385u32, 0x7828bc0eu32, - 0x78327ae6u32, 0x783efea0u32, 0x783fe000u32, 0x7841c2a5u32, 0x78466723u32, 0x784bed01u32, 0x784f0e0cu32, 0x7855227cu32, - 0x78553249u32, 0x7855713du32, 0x7859289bu32, 0x786ea78du32, 0x7876950eu32, 0x7877c07eu32, 0x7879b9cdu32, 0x787d5a81u32, - 0x788a0791u32, 0x7893c0a2u32, 0x7893e558u32, 0x789923a4u32, 0x7899fc9fu32, 0x789af224u32, 0x789bacbbu32, 0x789e5c29u32, - 0x789f922au32, 0x78aa435eu32, 0x78b336f5u32, 0x78b7c6dcu32, 0x78b7e8f4u32, 0x78bb2186u32, 0x78bd0e6au32, 0x78c3d203u32, - 0x78c929b0u32, 0x78ca3c83u32, 0x78ca9beeu32, 0x78cc8fafu32, 0x78ccd205u32, 0x78ccf243u32, 0x78d10109u32, 0x78d229dbu32, - 0x78d4ffb0u32, 0x78dadb45u32, 0x78e7b5fcu32, 0x78ed712au32, 0x78f1c26fu32, 0x78f2471bu32, 0x78f691bcu32, 0x78f95b4fu32, - 0x7908aa44u32, 0x7908fbccu32, 0x790c9b08u32, 0x790ee993u32, 0x7911d09du32, 0x7915cee2u32, 0x79182e79u32, 0x79189bcfu32, - 0x79207264u32, 0x7920a49bu32, 0x792668dau32, 0x7926b8eau32, 0x79270d39u32, 0x79362ac4u32, 0x7937d819u32, 0x793a17d4u32, - 0x793dd24bu32, 0x794062cau32, 0x794275dcu32, 0x7945cdbfu32, 0x7948e526u32, 0x79494845u32, 0x7959b88cu32, 0x795c52e0u32, - 0x795daeadu32, 0x795e734au32, 0x79605274u32, 0x79675caau32, 0x79683cdcu32, 0x7968b815u32, 0x79742386u32, 0x797917b2u32, - 0x797b4864u32, 0x797e1d75u32, 0x79812e4cu32, 0x7989dddcu32, 0x79900bbdu32, 0x7990e1f1u32, 0x799681e4u32, 0x7996ce9au32, - 0x79983f58u32, 0x799b434au32, 0x799c54cau32, 0x79a2d5dcu32, 0x79a54bafu32, 0x79b03330u32, 0x79b4a7e0u32, 0x79b4fa24u32, - 0x79bc8e05u32, 0x79bf5709u32, 0x79c09414u32, 0x79c0d16bu32, 0x79c2df49u32, 0x79d085fdu32, 0x79d37295u32, 0x79dc065bu32, - 0x79dee748u32, 0x79e4ffdau32, 0x79f203dcu32, 0x79ff33c7u32, 0x7a018680u32, 0x7a024ecfu32, 0x7a036a20u32, 0x7a17b3a0u32, - 0x7a192197u32, 0x7a198280u32, 0x7a1a7362u32, 0x7a21081au32, 0x7a21fad8u32, 0x7a28f812u32, 0x7a296d5du32, 0x7a3053fbu32, - 0x7a37e7abu32, 0x7a432ec8u32, 0x7a471791u32, 0x7a47728cu32, 0x7a4f7b87u32, 0x7a62dbbeu32, 0x7a658339u32, 0x7a6a7c37u32, - 0x7a722449u32, 0x7a7762c2u32, 0x7a78ea4bu32, 0x7a79826cu32, 0x7a7e4169u32, 0x7a8cf631u32, 0x7a8db803u32, 0x7a977471u32, - 0x7a9afb94u32, 0x7a9df969u32, 0x7aa6151bu32, 0x7aa876bbu32, 0x7aafc66au32, 0x7ab18f8bu32, 0x7abcf156u32, 0x7ac0b6ebu32, - 0x7ac3510fu32, 0x7ac9cbd6u32, 0x7acc7b4au32, 0x7ad6e9f9u32, 0x7ae004d2u32, 0x7ae07c86u32, 0x7ae70ed5u32, 0x7aecbeb6u32, - 0x7aed46b8u32, 0x7af6887bu32, 0x7af81504u32, 0x7afac2fcu32, 0x7afc3477u32, 0x7b13b511u32, 0x7b174e8cu32, 0x7b18362bu32, - 0x7b1c5ca9u32, 0x7b209492u32, 0x7b246ee8u32, 0x7b2f7203u32, 0x7b389c6du32, 0x7b44d52au32, 0x7b50c71eu32, 0x7b531fc2u32, - 0x7b55c197u32, 0x7b5a8ff6u32, 0x7b6e9429u32, 0x7b76cdb0u32, 0x7b793d95u32, 0x7b7aecf4u32, 0x7b8198b5u32, 0x7b940727u32, - 0x7b9fef55u32, 0x7ba3796fu32, 0x7ba4c1a4u32, 0x7bac38bfu32, 0x7bbba083u32, 0x7bbfd2ddu32, 0x7bc84b35u32, 0x7bd126c7u32, - 0x7be10da8u32, 0x7be6cf82u32, 0x7be8b80bu32, 0x7bedd49cu32, 0x7bee0a29u32, 0x7bee21c9u32, 0x7bfdfc34u32, 0x7c031f56u32, - 0x7c0e1eecu32, 0x7c12ace7u32, 0x7c1407d3u32, 0x7c14dcbbu32, 0x7c1bdc3au32, 0x7c1d32e9u32, 0x7c1fa9afu32, 0x7c20973cu32, - 0x7c214750u32, 0x7c298fb6u32, 0x7c29c63cu32, 0x7c34353eu32, 0x7c38a6a4u32, 0x7c3b24fdu32, 0x7c3c77b6u32, 0x7c44d6c3u32, - 0x7c4535bau32, 0x7c52183eu32, 0x7c53a3ceu32, 0x7c5b2aa9u32, 0x7c61e6d3u32, 0x7c681eebu32, 0x7c6d0840u32, 0x7c74d666u32, - 0x7c7b3643u32, 0x7c7ecbdau32, 0x7c814597u32, 0x7c89fb62u32, 0x7c8c829au32, 0x7c907ac5u32, 0x7c9909c7u32, 0x7c9c3b2eu32, - 0x7ca0bda9u32, 0x7cadc062u32, 0x7cae4c57u32, 0x7cb02babu32, 0x7cb2d414u32, 0x7cb879f9u32, 0x7cbe947du32, 0x7cbed82bu32, - 0x7cd21cd7u32, 0x7cd929d1u32, 0x7cda58bbu32, 0x7ce00098u32, 0x7ce18e76u32, 0x7ce27470u32, 0x7ce3383du32, 0x7ce9174cu32, - 0x7cec2c74u32, 0x7cf07fa0u32, 0x7d01f7a9u32, 0x7d029045u32, 0x7d081a91u32, 0x7d0996aeu32, 0x7d137a64u32, 0x7d14fc19u32, - 0x7d155f50u32, 0x7d18123au32, 0x7d1c6b51u32, 0x7d207395u32, 0x7d252463u32, 0x7d26c257u32, 0x7d297e20u32, 0x7d2cde83u32, - 0x7d31a4acu32, 0x7d37e69fu32, 0x7d3d48d6u32, 0x7d4af079u32, 0x7d4f3488u32, 0x7d53307bu32, 0x7d57b80du32, 0x7d5a42e2u32, - 0x7d5e28fcu32, 0x7d635039u32, 0x7d63a382u32, 0x7d69e912u32, 0x7d766f46u32, 0x7d7a023fu32, 0x7d7e81c6u32, 0x7d7ef07du32, - 0x7d953320u32, 0x7d958d64u32, 0x7d963e4fu32, 0x7d9bab49u32, 0x7d9d366fu32, 0x7d9e7e26u32, 0x7d9eabfbu32, 0x7da29fe0u32, - 0x7da7fb5bu32, 0x7daa5567u32, 0x7dae09e4u32, 0x7db4febau32, 0x7db98a14u32, 0x7dbd7003u32, 0x7dca911eu32, 0x7dcf9a4cu32, - 0x7dd19511u32, 0x7dd79a3du32, 0x7ddb83c9u32, 0x7dde61e7u32, 0x7de6ab1bu32, 0x7deb6b06u32, 0x7dedaa75u32, 0x7df1c12du32, - 0x7df521e3u32, 0x7df8127cu32, 0x7dfe321au32, 0x7dff4b6au32, 0x7e00954bu32, 0x7e00986au32, 0x7e00c6fcu32, 0x7e02bbc2u32, - 0x7e03becfu32, 0x7e083450u32, 0x7e0bd237u32, 0x7e104ad7u32, 0x7e1265ceu32, 0x7e1ad37au32, 0x7e1e669eu32, 0x7e206ed1u32, - 0x7e209f12u32, 0x7e215db4u32, 0x7e216934u32, 0x7e2472c5u32, 0x7e3078a2u32, 0x7e33971au32, 0x7e34f68eu32, 0x7e371d05u32, - 0x7e3f28bfu32, 0x7e4051f3u32, 0x7e53eacdu32, 0x7e577df1u32, 0x7e5eca56u32, 0x7e666a0eu32, 0x7e6f513eu32, 0x7e721345u32, - 0x7e739a50u32, 0x7e7401cdu32, 0x7e7894d5u32, 0x7e84e1c0u32, 0x7e946aa8u32, 0x7e974529u32, 0x7e9fa8c5u32, 0x7ea144f6u32, - 0x7ea232ddu32, 0x7ea25bafu32, 0x7ea80382u32, 0x7eafeac4u32, 0x7eb32eabu32, 0x7ebb51b3u32, 0x7ebfe5ecu32, 0x7ec81ecdu32, - 0x7ed01b6fu32, 0x7ed443a1u32, 0x7ed48dd1u32, 0x7edc874fu32, 0x7edd1025u32, 0x7ee0026du32, 0x7ee33868u32, 0x7ee3a01fu32, - 0x7ee5b26cu32, 0x7ef030fau32, 0x7ef24b6eu32, 0x7ef2b58cu32, 0x7f060c08u32, 0x7f13e9dcu32, 0x7f178880u32, 0x7f1c49a1u32, - 0x7f28d815u32, 0x7f2d7b14u32, 0x7f33a10du32, 0x7f340c5cu32, 0x7f34b8f7u32, 0x7f3645e9u32, 0x7f3e5dc8u32, 0x7f430e57u32, - 0x7f45d05eu32, 0x7f4931ccu32, 0x7f4a25f1u32, 0x7f4bf0f3u32, 0x7f4dcf28u32, 0x7f523d74u32, 0x7f543611u32, 0x7f5830a8u32, - 0x7f62b0e5u32, 0x7f6dbf3fu32, 0x7f786dcfu32, 0x7f789ec8u32, 0x7f7a5658u32, 0x7f7ac89du32, 0x7f7c5b32u32, 0x7f7d2593u32, -]; -#[rustfmt::skip] -pub static UCRT_SINF_WIDE_VALUES: [u32; 2536] = [ - 0x3f7ffe06u32, 0xbf7ffef7u32, 0xbf7fff03u32, 0xbf7ffe1fu32, 0xbf7ffe9bu32, 0x3f7ffe0fu32, 0xbf7ffeebu32, 0x3bfc3c8bu32, - 0x3bc2a43au32, 0x3f7ffeb3u32, 0x3f7fffa3u32, 0x3f7ffe9au32, 0x3f7ffe54u32, 0xbf7fff11u32, 0x3f7fff20u32, 0xbf7ffe80u32, - 0xbf7ffe16u32, 0xbf7ffe32u32, 0x3f7ffe14u32, 0x3b5a1c57u32, 0x3f7ffe1bu32, 0xbf7fff25u32, 0xbf7fff88u32, 0xbf7ffeeeu32, - 0xbf7fff07u32, 0x3f7ffe58u32, 0xbf7ffe3eu32, 0x3f7ffe05u32, 0x3f7ffe9fu32, 0x3f7fff6du32, 0x3f7ffed7u32, 0xbf7ffe57u32, - 0x3f7ffea1u32, 0xbf7fff14u32, 0xbbeec976u32, 0xbf7ffe0au32, 0xbbddc72fu32, 0xbf7fff19u32, 0x3f7ffe30u32, 0xbbf7703du32, - 0xbf7ffe89u32, 0xbf7ffee4u32, 0x3f7ffe2au32, 0x3f7ffe50u32, 0xbf7ffe17u32, 0x3f7ffe9du32, 0xbf7ffe02u32, 0x3f7ffe28u32, - 0x3f7ffe7eu32, 0x3f7ffe0bu32, 0x3f7fff18u32, 0x3f7ffe3fu32, 0xbf7ffe54u32, 0xbf7ffe43u32, 0x3f7fff03u32, 0x3f7ffef7u32, - 0x3f7fff05u32, 0x3f7ffe1cu32, 0xbf7ffe18u32, 0x3f7ffea7u32, 0xbf7ffe1eu32, 0xbf7ffed5u32, 0x3f7ffe95u32, 0x3f7ffe85u32, - 0xbb8e69a3u32, 0x3f7ffeb8u32, 0xbf7fff52u32, 0xbf7ffe0du32, 0x3f7ffed8u32, 0x3bc195a7u32, 0xbf7ffe56u32, 0x3f7ffe9au32, - 0xbf7ffebeu32, 0x3f7ffe69u32, 0x3f7ffe29u32, 0xbbf110cfu32, 0xbf7ffee9u32, 0x3f7fffbau32, 0x3f7ffe0au32, 0xbf7ffed7u32, - 0xbf7ffe9fu32, 0xbf7ffe1bu32, 0x3f7fff11u32, 0x3f7ffebbu32, 0x3bf73dcau32, 0xbf7ffe0fu32, 0x3f7ffe1fu32, 0xbf7ffe06u32, - 0x3f7ffe82u32, 0x3f7ffe35u32, 0xbf7ffe4bu32, 0xbf7fff0du32, 0xbbd9f7c4u32, 0xbbc9336au32, 0x3f7ffe25u32, 0xbbb6bb96u32, - 0x3f7ffe30u32, 0xbf7ffe31u32, 0x3f7ffe7bu32, 0x3f7ffedau32, 0xbf7ffe1cu32, 0x3bdcef60u32, 0xbf7ffe81u32, 0x3f7fff5cu32, - 0xbf7ffe7eu32, 0x3f7ffec3u32, 0x3f7fff95u32, 0xbf7ffed1u32, 0xbf7ffe14u32, 0xbf7fff7eu32, 0x3f7ffe84u32, 0xbf7ffec6u32, - 0x3f7ffe63u32, 0x3f7ffeb3u32, 0x3f7ffe05u32, 0xbf7ffe53u32, 0xbf7ffe98u32, 0xbf7ffe7du32, 0x3be5e894u32, 0x3f7ffeaau32, - 0xbf7ffe09u32, 0x3f7ffe55u32, 0xbbed977bu32, 0x3f7ffe36u32, 0xbbe88825u32, 0x3f7ffe16u32, 0xbbe0357du32, 0x3f7ffe0cu32, - 0x3f7ffe2fu32, 0x3bc0ac45u32, 0x3f7ffeb0u32, 0xbf7ffe66u32, 0x3f7fff80u32, 0x3f7ffe8cu32, 0x3f7ffe8cu32, 0x3f7fffc7u32, - 0x3f7ffe94u32, 0xbf7ffe66u32, 0xbbed9468u32, 0x3bf030f7u32, 0x3f7ffe2fu32, 0x3f7ffe77u32, 0x3f7ffe10u32, 0xbf7ffe88u32, - 0x3f7fff9du32, 0xbf7fff67u32, 0xbf7ffe98u32, 0xbf7fffb9u32, 0x3f7ffedau32, 0x3f7ffeb5u32, 0x3bfcdfe4u32, 0xbf7ffe08u32, - 0x3f7fff58u32, 0xbf7ffe04u32, 0xbbe1bc20u32, 0x3f7ffeeeu32, 0x3f7ffef3u32, 0xbb8aee20u32, 0xbf7fff2fu32, 0xbbd35746u32, - 0xbf7ffeeau32, 0xbf7fff1cu32, 0x3bff62aau32, 0x3b9e9841u32, 0xbf7ffe77u32, 0xbf7ffee3u32, 0x3f7fff1bu32, 0x3f7ffe08u32, - 0xbf7ffe6cu32, 0x3f7ffe04u32, 0x3f7ffe3bu32, 0x3f7ffeeau32, 0xbf7ffe9bu32, 0xbf7ffeb9u32, 0x3f7fff65u32, 0x3f7ffe10u32, - 0x3bdb9027u32, 0x3f7ffec8u32, 0x3f7ffeb5u32, 0x3f7ffe49u32, 0xbf7ffee5u32, 0x3f7ffef4u32, 0xbf7ffe1bu32, 0x3f7ffe58u32, - 0x3f7ffeccu32, 0xbf7ffe77u32, 0x3f7fff02u32, 0x3f7ffea6u32, 0xbf7ffe74u32, 0xbf7ffefdu32, 0xbf7ffe6cu32, 0xbf7ffe12u32, - 0x3f7ffe3bu32, 0xbf7ffe91u32, 0x3f7ffee4u32, 0x3f7ffea1u32, 0x3f7ffe3fu32, 0xbf7ffed7u32, 0xbf7ffeb2u32, 0x3f7ffe10u32, - 0x3f7fffa7u32, 0xbf7fff48u32, 0x3f7ffe5cu32, 0xbf7fff13u32, 0x3f7ffe87u32, 0xbf7ffee2u32, 0xbf7ffebdu32, 0x3f7ffe2au32, - 0x3f7ffe89u32, 0x3ba4b555u32, 0xbf7ffe40u32, 0xbf7fff38u32, 0xbf7ffe8du32, 0xbf7ffeb7u32, 0xbf7ffe03u32, 0xbf7ffe0bu32, - 0xbf7fff03u32, 0x3f7ffe4du32, 0xbf7ffe48u32, 0xbf7ffe38u32, 0x3f7ffec4u32, 0xbf7ffe1bu32, 0xbbf00f93u32, 0xbf7ffe84u32, - 0xbf7ffecfu32, 0x3f7ffeecu32, 0x3bc3ffddu32, 0x3f7fff20u32, 0x3f7ffe68u32, 0xbf7ffe90u32, 0x3bc0c79du32, 0xbf7fff99u32, - 0xbbda04eau32, 0x3f7fff09u32, 0xbf7ffe30u32, 0x3be41a7cu32, 0xbf7ffeacu32, 0x3bf4b54cu32, 0x3f7fff26u32, 0xbf7ffef6u32, - 0xbf7ffeadu32, 0xbf7fff81u32, 0xbf7ffe2eu32, 0xbf7ffe71u32, 0xbf7ffe9eu32, 0x3f7fff04u32, 0x3f7ffe15u32, 0xbf7ffef1u32, - 0xbf7fffa2u32, 0xbf7ffe4eu32, 0x3f7ffe14u32, 0xbf7ffe41u32, 0x3bf5694cu32, 0xbf7fff0cu32, 0x3f7ffe9au32, 0xbf7ffe42u32, - 0x3f7ffe92u32, 0x3f7ffe05u32, 0xbf7fff95u32, 0x3bcc3a51u32, 0x3f7ffedfu32, 0x3f7ffe21u32, 0x3f7ffee9u32, 0xbf7ffe45u32, - 0xbf7ffe34u32, 0xbf7ffe13u32, 0xbf7ffed9u32, 0x3f7fff3bu32, 0xbf7fff05u32, 0x3f7fff69u32, 0xbf7ffea8u32, 0xbf7ffea2u32, - 0xbf7ffe0fu32, 0x3f7ffe2cu32, 0x3f7ffe81u32, 0x3f7ffe27u32, 0xbf7ffeebu32, 0x3f7ffe4bu32, 0xbf7ffef0u32, 0xbf7ffeb6u32, - 0x3f7fff6eu32, 0x3f7ffea5u32, 0xbf7ffe43u32, 0x3bd04094u32, 0x3f7ffe7cu32, 0x3f7ffe55u32, 0xbf7ffe8eu32, 0x3f7ffe49u32, - 0x3f7fff1cu32, 0xbbc72941u32, 0xbf7ffe69u32, 0x3bca72cbu32, 0xbf7ffe3bu32, 0xbf7ffe5cu32, 0x3f7ffe0eu32, 0x3f7ffe1cu32, - 0x3f7ffe03u32, 0xbf7ffe5bu32, 0x3f7fff24u32, 0xbbf38016u32, 0x3f7fffafu32, 0x3f7ffe31u32, 0x3f7ffe63u32, 0xbf7ffe3cu32, - 0xbf7ffe1du32, 0x3f7ffed8u32, 0xbf7ffe80u32, 0xbf7ffe98u32, 0xbf7ffe3du32, 0xbf7fff2du32, 0x3bd83c32u32, 0x3f7ffe3au32, - 0xbf7ffe56u32, 0xbf7ffe08u32, 0xbf7ffed7u32, 0xbf7fff4au32, 0xbf7ffe03u32, 0x3f7fff3du32, 0xbf7ffe31u32, 0xbf7fff46u32, - 0xbbbf1bc3u32, 0xbf7fff42u32, 0xbf7fff2eu32, 0x3f7ffe23u32, 0x3f7ffefau32, 0xbf7ffe5au32, 0xbf7ffe1eu32, 0x3f7fff18u32, - 0x3f7ffec2u32, 0x3f7ffe1au32, 0xbf7ffe46u32, 0x3f7ffe3eu32, 0xbf7ffe02u32, 0x3f7ffecau32, 0x3f7ffe08u32, 0x3f7fff3eu32, - 0x3bb39f5du32, 0xbf7ffe23u32, 0xbf7ffeedu32, 0x3f7fff50u32, 0x3f7ffe71u32, 0xbbea5032u32, 0x3f7ffebcu32, 0xbf7ffe65u32, - 0x3f7ffe88u32, 0xbf7fff0cu32, 0x3f7ffe15u32, 0x3f7fff20u32, 0x3f7fff75u32, 0x3f7ffe38u32, 0x3f7ffe70u32, 0x3f7ffe75u32, - 0x3f7ffe97u32, 0x3f7ffe20u32, 0xbf7ffe29u32, 0xbf7ffee5u32, 0xbf7ffe28u32, 0x3f7ffe0du32, 0x3f7ffe7bu32, 0xbf7ffe8au32, - 0xbf7ffe93u32, 0xbf7fff13u32, 0x3f7ffe04u32, 0x3f7ffe40u32, 0xbf7ffe68u32, 0xbf7fff39u32, 0x3f7ffe6au32, 0xbf7ffec0u32, - 0xbf7ffeb4u32, 0x3f7fff57u32, 0x3f7ffe2du32, 0xbf7ffeccu32, 0xbbf80187u32, 0xbf7fff01u32, 0x3f7ffe62u32, 0x3f7fff5eu32, - 0xbf7ffe45u32, 0xbbf6e5c1u32, 0xbf7ffe84u32, 0x3f7ffec4u32, 0x3f7ffeb2u32, 0xbf7ffe4au32, 0x3f7ffe9eu32, 0xbf7fff3cu32, - 0x3b8d05cdu32, 0x3f7ffe0fu32, 0xbf7ffedeu32, 0xbf7fff8cu32, 0xbf7ffe41u32, 0x3f7ffe52u32, 0x3f7ffe21u32, 0x3f7ffe36u32, - 0x3bf57880u32, 0x3f7ffec7u32, 0xbf7fff61u32, 0x3f7ffeabu32, 0xbf7ffe48u32, 0x3f7ffe7au32, 0x3f7ffe42u32, 0xbbeb190au32, - 0xbf7ffeceu32, 0xbf7ffe35u32, 0x3f7ffef6u32, 0xbf7ffe4du32, 0x3f7fff6bu32, 0x3f7ffe92u32, 0xbf7fff0bu32, 0xbf7ffea5u32, - 0xbf7ffe50u32, 0x3f7ffe2au32, 0x3f7ffee2u32, 0xbf7ffe11u32, 0xbbed437au32, 0x3f7fffc9u32, 0xbf7ffe96u32, 0x3f7fff0du32, - 0xbbd88640u32, 0x3bf57f85u32, 0xbf7ffea1u32, 0x3f7ffe76u32, 0xbf7ffe37u32, 0x3f7ffea9u32, 0xbf7ffea7u32, 0xbf7ffe5fu32, - 0xbf7fff41u32, 0x3f7ffe75u32, 0xbf7fff10u32, 0xbf7ffe0cu32, 0xbf7ffe02u32, 0x3f7ffe3eu32, 0x3f7ffeafu32, 0xbf7fff4eu32, - 0x3f7ffe3bu32, 0xbf7ffe1cu32, 0xbf7ffe3au32, 0xbbfa6bb1u32, 0x3f7ffec1u32, 0xbbfa3e62u32, 0x3f7ffeb9u32, 0xbf7ffe08u32, - 0xbbfa1113u32, 0x3f7fffa6u32, 0x3f7ffeeeu32, 0x3bef8f4du32, 0x3f7ffe7fu32, 0x3f7ffef8u32, 0xbbe7c8c3u32, 0xbf7ffe82u32, - 0x3f7ffe5bu32, 0x3f7ffe8du32, 0x3f7ffe1fu32, 0xbf7ffeecu32, 0xbf7ffeb6u32, 0xbf7ffe2du32, 0xbf7ffe0du32, 0xbf7ffec3u32, - 0xbf7ffe20u32, 0x3f7ffef3u32, 0x3f7ffed5u32, 0xbf7ffe4bu32, 0xbf7ffe56u32, 0x3f7ffe74u32, 0x3f7ffe4eu32, 0xbf7ffee7u32, - 0x3f7ffe6au32, 0x3f7ffeb1u32, 0x3f7ffe6fu32, 0x3f7ffe21u32, 0x3f7ffeb5u32, 0x3f7ffe63u32, 0x3f7ffe79u32, 0x3f7ffe27u32, - 0xbf7ffe60u32, 0x3f7fffa9u32, 0xbf7ffe22u32, 0xbbd21bfdu32, 0xbf7fff00u32, 0xbf7ffecfu32, 0xbf7ffe61u32, 0xbf7fff66u32, - 0x3f7fff42u32, 0xbf7ffe98u32, 0xbf7ffe15u32, 0xbbedab17u32, 0x3f7ffe1bu32, 0xbf7ffef2u32, 0xbf7ffe49u32, 0xbf7ffe42u32, - 0xbf7ffec9u32, 0x3f7fff1au32, 0x3f7ffe5eu32, 0x3f7ffe0fu32, 0x3f7ffe8fu32, 0xbf7ffe57u32, 0xbbfbcae0u32, 0x3f7fff2au32, - 0x3f7ffe0du32, 0x3f7ffed0u32, 0x3be251fdu32, 0xbf7ffe78u32, 0x3f7ffe2eu32, 0xbf7ffe06u32, 0x3f7ffe2bu32, 0x3f7ffe99u32, - 0xbf7fff17u32, 0xbf7ffe1au32, 0xbf7ffe01u32, 0xbf7ffe16u32, 0xbbfb5f07u32, 0xbf7ffe67u32, 0xbf7ffe4du32, 0xbf7ffe68u32, - 0x3f7ffe04u32, 0xbf7ffe31u32, 0x3f7ffe5cu32, 0xbf7ffe85u32, 0xbf7ffeaau32, 0xbf7fff26u32, 0xbf7ffeffu32, 0x3f7ffe17u32, - 0x3f7ffe0au32, 0xbf7ffe19u32, 0x3f7ffed3u32, 0xbf7ffe93u32, 0x3f7ffe25u32, 0xbf7fff7eu32, 0x3f7ffe30u32, 0xbf7ffe5au32, - 0x3f7ffe5du32, 0xbf7ffe50u32, 0xbbd56139u32, 0x3f7fff20u32, 0x3f7ffe3eu32, 0x3f7ffe53u32, 0x3f7ffebfu32, 0x3f7ffe9du32, - 0x3f7fff46u32, 0xbf7ffe2cu32, 0x3f7ffe36u32, 0x3f7fff9eu32, 0x3f7ffe6cu32, 0xbf7fff36u32, 0xbf7ffe38u32, 0xbf7ffea8u32, - 0xbf7ffef9u32, 0x3f7ffe1bu32, 0x3f7fff23u32, 0xbf7ffecfu32, 0xbf7ffe82u32, 0xbf7ffe45u32, 0x3f7ffe59u32, 0xbbd19a6cu32, - 0x3f7ffe15u32, 0xbf7fff1au32, 0xbf7ffe0fu32, 0x3f7ffe57u32, 0xbf7ffec8u32, 0xbf7ffe3fu32, 0xbf7ffe55u32, 0xbf7ffe39u32, - 0xbf7ffe07u32, 0x3f7ffe77u32, 0xbbf44b89u32, 0x3f7ffe81u32, 0x3ba7e0d0u32, 0xbbedacd7u32, 0xbf7ffe17u32, 0xbf7ffe7eu32, - 0x3f7ffe87u32, 0x3f7ffe42u32, 0xbbd9f987u32, 0xbf7ffe4bu32, 0xbf7ffe60u32, 0xbf7fff84u32, 0xbf7ffef2u32, 0x3f7ffe0du32, - 0xbf7ffe67u32, 0x3f7ffe04u32, 0xbf7ffef1u32, 0xbbe9dc88u32, 0x3f7ffe7du32, 0xbf7fff5bu32, 0xbbac6e95u32, 0xbf7ffeadu32, - 0xbf7ffe2fu32, 0xbf7ffe52u32, 0x3f7ffe1du32, 0x3f7ffe69u32, 0xbf7fff5du32, 0xbf7fff0cu32, 0xbf7ffee4u32, 0x3bb67716u32, - 0xbf7ffe9fu32, 0xbbf7bac9u32, 0x3f7ffed6u32, 0x3f7ffe8bu32, 0x3f7ffe31u32, 0x3f7fff33u32, 0xbf7ffe88u32, 0x3f7ffe0eu32, - 0x3f7ffe74u32, 0x3f7ffe76u32, 0xbf7ffe62u32, 0xbf7fff3eu32, 0x3bfc4d34u32, 0x3be10bddu32, 0xbf7ffef5u32, 0x3f7ffe4cu32, - 0xbf7ffe73u32, 0xbf7ffe97u32, 0x3f7ffe18u32, 0xbf7ffe8du32, 0xbf7ffe90u32, 0x3bddaa29u32, 0xbf7ffe72u32, 0x3f7fff0eu32, - 0x3f7ffe80u32, 0x3f7ffe34u32, 0xbbdde376u32, 0xbf7ffe7bu32, 0x3bcaa789u32, 0x3f7ffe02u32, 0x3bfe9254u32, 0x3f7ffe14u32, - 0x3bc6eb5eu32, 0xbf7ffe85u32, 0x3f7ffe2bu32, 0xbf7ffe9bu32, 0x3f7ffe27u32, 0xbf7ffe58u32, 0xbbfa2daeu32, 0x3f7ffeceu32, - 0xbf7ffe07u32, 0xbf7ffe48u32, 0xbf7ffe46u32, 0x3f7ffe5fu32, 0x3f7fff2fu32, 0x3bd2d1e0u32, 0xbf7ffe5au32, 0x3f7fff2cu32, - 0xbf7ffeffu32, 0xbf7ffe16u32, 0xbbb3cf6au32, 0xbf7ffec9u32, 0x3b9a7248u32, 0xbf7fffd5u32, 0xbf7ffecbu32, 0xbf7ffe2eu32, - 0x3f7fff68u32, 0x3f7ffe6eu32, 0xbf7ffe23u32, 0x3bf38d31u32, 0x3bf5de58u32, 0x3f7fffadu32, 0x3f7ffe4eu32, 0x3f7ffed2u32, - 0x3f7ffe86u32, 0xbf7fff29u32, 0xbf7ffe1cu32, 0x3f7fff0au32, 0x3f7ffec6u32, 0xbf7ffe3du32, 0xbf7ffe17u32, 0xbf7ffed9u32, - 0xbf7ffebfu32, 0xbf7ffe01u32, 0xbf7fff36u32, 0x3f7ffe0cu32, 0x3f7ffe79u32, 0x3f7fff06u32, 0x3f7fff11u32, 0x3f7ffe6fu32, - 0x3f7ffe5bu32, 0xbf7ffe3cu32, 0x3f7ffee5u32, 0x3f7ffe7au32, 0xbf7ffe33u32, 0xbbeaaa16u32, 0x3bd9ecebu32, 0xbf7ffe84u32, - 0xbb936a78u32, 0x3f7ffe1bu32, 0x3f7ffe6cu32, 0x3f7ffe5du32, 0x3b8c8feeu32, 0x3f7fff95u32, 0x3f7ffee4u32, 0xbf7ffe4au32, - 0x3f7ffeecu32, 0xbf7ffe7cu32, 0x3f7ffe35u32, 0xbf7ffef8u32, 0xbbab964du32, 0xbf7ffe85u32, 0x3f7ffe14u32, 0x3f7ffe69u32, - 0xbf7ffe55u32, 0x3f7ffe13u32, 0x3f7ffea0u32, 0x3bfcdb89u32, 0x3f7ffe07u32, 0x3f7ffe12u32, 0xbf7fff54u32, 0xbf7ffe6bu32, - 0x3f7ffe11u32, 0xbf7fff1cu32, 0x3be44bbcu32, 0xbf7ffe50u32, 0xbf7ffe78u32, 0xbf7fffa6u32, 0xbf7ffe39u32, 0xbf7ffe44u32, - 0xbf7ffe04u32, 0x3f7fff15u32, 0xbbe2a6fau32, 0x3bb8f837u32, 0xbf7ffe48u32, 0xbf7ffe73u32, 0x3bfc8794u32, 0x3f7ffee8u32, - 0x3bf6d31du32, 0x3f7ffeb4u32, 0x3f7fffa9u32, 0xbf7ffe71u32, 0xbf7ffe4bu32, 0x3f7ffe52u32, 0x3bd5d480u32, 0xbf7ffe5cu32, - 0x3f7fff19u32, 0xbf7ffeddu32, 0x3bced20au32, 0x3bf7dff1u32, 0x3bdcba03u32, 0xbf7ffe65u32, 0x3f7ffe22u32, 0x3f7fff3au32, - 0x3f7ffe0bu32, 0x3f7ffebdu32, 0x3f7ffe49u32, 0xbbda7672u32, 0x3f7ffe21u32, 0x3be85f4du32, 0x3f7fff0du32, 0xbf7ffec2u32, - 0x3f7ffe0eu32, 0x3f7ffe76u32, 0x3f7ffee4u32, 0x3f7ffe79u32, 0xbf7ffebfu32, 0xbf7ffe4cu32, 0x3f7ffe8eu32, 0xbf7ffee0u32, - 0xbf7fff7au32, 0xbf7fff00u32, 0x3f7ffe68u32, 0xbf7ffe34u32, 0x3f7ffec7u32, 0x3f7ffe3du32, 0x3f7ffe17u32, 0xbf7ffe5bu32, - 0x3f7ffe3bu32, 0xbf7ffe63u32, 0xbbf58ef9u32, 0xbf7ffe3fu32, 0x3f7ffe86u32, 0xbf7ffe16u32, 0xbf7ffe8du32, 0x3f7ffe95u32, - 0x3f7ffe10u32, 0xbf7ffe05u32, 0x3f7fff7eu32, 0x3f7fff05u32, 0x3f7ffe51u32, 0xbf7ffe0au32, 0xbf7ffe72u32, 0xbf7ffe83u32, - 0xbf7ffe38u32, 0x3f7ffedcu32, 0xbf7ffe12u32, 0x3f7fff49u32, 0x3f7ffe4au32, 0xbf7ffe7au32, 0x3f7fff20u32, 0x3f7fff47u32, - 0xbf7fff71u32, 0xbbfe7d40u32, 0xbbdd7083u32, 0x3f7fff26u32, 0xbf7fff3bu32, 0x3f7ffef2u32, 0xbf7ffed5u32, 0x3f7ffeafu32, - 0xbf7fff65u32, 0xbf7ffe37u32, 0x3f7ffea2u32, 0x3f7fff6fu32, 0x3f7ffe99u32, 0x3bddfdffu32, 0x3f7ffe08u32, 0x3f7ffedeu32, - 0xbf7ffea5u32, 0x3f7ffe89u32, 0xbf7fff04u32, 0xbf7ffee3u32, 0x3f7ffe8fu32, 0xbf7ffe48u32, 0x3b58fb95u32, 0xbf7ffe78u32, - 0x3bf4e0d9u32, 0xbf7ffe6bu32, 0x3bb65e28u32, 0x3f7ffe58u32, 0x3f7fff32u32, 0x3f7ffe01u32, 0xbf7ffe9au32, 0xbbf3bec0u32, - 0xbf7fff0fu32, 0x3f7fff1fu32, 0x3f7ffe4bu32, 0x3bee07a3u32, 0xbbcc0743u32, 0xbf7ffe25u32, 0xbf7ffe94u32, 0x3f7ffe57u32, - 0xbf7ffe1du32, 0xbf7ffefeu32, 0xbf7ffea1u32, 0x3f7ffea6u32, 0x3be159d2u32, 0xbf7ffe13u32, 0xbf7ffe81u32, 0xbf7fff1eu32, - 0xbf7ffe2au32, 0xbf7fff6au32, 0xbf7ffe27u32, 0x3f7ffe36u32, 0x3f7ffe22u32, 0xbbfa8967u32, 0x3f7ffe96u32, 0x3f7ffe88u32, - 0xbf7fff1du32, 0x3f7ffef7u32, 0xbf7fff2du32, 0xbf7ffe0du32, 0xbf7fff4eu32, 0xbf7ffe0bu32, 0xbf7ffe33u32, 0x3f7ffe2fu32, - 0x3f7ffea3u32, 0xbf7fff45u32, 0xbf7ffe71u32, 0x3bbf1e61u32, 0xbf7ffe16u32, 0x3f7ffe0eu32, 0x3f7ffe1au32, 0xbf7ffe50u32, - 0x3f7ffee2u32, 0x3f7ffeceu32, 0x3f7ffe73u32, 0xbf7ffe86u32, 0xbf7ffec3u32, 0x3f7ffe72u32, 0xbf7ffed4u32, 0xbf7ffea0u32, - 0xbf7ffe1fu32, 0xbf7ffe75u32, 0xbf7ffeabu32, 0x3f7ffe80u32, 0x3f7ffe11u32, 0xbbddb2d6u32, 0xbf7ffe0fu32, 0xbf7fff2au32, - 0xbf7ffe09u32, 0x3f7ffe9du32, 0x3bac459du32, 0xbf7ffe90u32, 0x3f7ffe2bu32, 0x3f7ffefau32, 0x3f7ffe5eu32, 0x3f7ffecfu32, - 0xbbf15981u32, 0x3f7ffe66u32, 0x3f7ffe21u32, 0xbf7ffe64u32, 0xbf7ffe26u32, 0xbb8e7c00u32, 0xbf7ffe37u32, 0x3f7ffea2u32, - 0x3f7ffeecu32, 0xbbe6fd68u32, 0xbf7ffec4u32, 0x3f7ffee1u32, 0xbf7ffe9au32, 0xbf7ffe2du32, 0xbf7ffe7du32, 0x3f7fffdbu32, - 0xbf7ffedbu32, 0x3f7ffe3cu32, 0x3f7ffeedu32, 0x3f7ffe1au32, 0x3bea2ddbu32, 0x3f7ffe0au32, 0xbf7fff5du32, 0x3f7fff14u32, - 0x3f7ffe4eu32, 0x3f7ffebbu32, 0xbf7ffe02u32, 0x3f7ffe57u32, 0xbbe23eccu32, 0x3f7fff41u32, 0xbbec9e18u32, 0x3f7ffe61u32, - 0x3bf1427cu32, 0xbf7ffea9u32, 0xbf7ffe10u32, 0x3bec3f9eu32, 0x3f7ffe20u32, 0x3f7ffe6du32, 0xbf7fff64u32, 0xbf7ffe36u32, - 0xbf7ffeeeu32, 0xbf7fff12u32, 0x3f7ffef0u32, 0xbf7ffe84u32, 0x3f7ffe58u32, 0x3f7ffe95u32, 0x3f7ffe68u32, 0x3f7ffe6cu32, - 0x3f7ffe54u32, 0xbf7ffe93u32, 0xbf7ffe24u32, 0xbf7ffe1eu32, 0x3f7ffee7u32, 0x3f7ffe45u32, 0xbf7ffe44u32, 0xbf7ffe43u32, - 0x3f7ffea4u32, 0x3f7ffe15u32, 0x3f7ffe46u32, 0xbf7ffe1bu32, 0x3f7ffe4cu32, 0xbf7ffe5bu32, 0xbf7ffeacu32, 0x3bd400dfu32, - 0xbf7ffe22u32, 0xbf7ffebeu32, 0xbf7ffe3bu32, 0x3f7ffe4fu32, 0xbf7ffecbu32, 0xbf7ffe1cu32, 0x3f7ffe4bu32, 0x3f7ffe32u32, - 0xbf7fff07u32, 0x3f7ffea7u32, 0x3f7ffe8du32, 0xbf7ffe74u32, 0x3f7ffea1u32, 0xbbf1cec9u32, 0xbbf48cd7u32, 0xbbc5e1c3u32, - 0x3f7ffe06u32, 0x3f7ffe19u32, 0x3bcb2f4bu32, 0xbf7ffe01u32, 0xbf7ffed7u32, 0x3f7ffe23u32, 0x3f7fff01u32, 0xbf7ffe53u32, - 0x3be4d584u32, 0x3f7ffe8au32, 0x3f7ffeb2u32, 0xbf7ffe2cu32, 0x3f7ffec6u32, 0x3f7ffe2eu32, 0x3f7ffe17u32, 0xbf7ffe37u32, - 0xbf7ffef2u32, 0xbf7ffe33u32, 0x3f7ffe5du32, 0xbf7fff36u32, 0x3f7ffe83u32, 0x3f7ffeb8u32, 0x3f7ffe59u32, 0xbf7ffe72u32, - 0xbf7fff15u32, 0xbf7ffe78u32, 0xbf7ffe7du32, 0xbf7ffe4du32, 0x3f7ffe56u32, 0x3f7ffe35u32, 0xbf7ffe08u32, 0xbf7ffec9u32, - 0xbf7fff3du32, 0x3f7ffe87u32, 0xbbe89d7cu32, 0xbf7ffec0u32, 0x3bcca667u32, 0xbf7ffe85u32, 0xbf7ffe9eu32, 0x3f7ffee3u32, - 0xbf7ffe20u32, 0xbf7ffe12u32, 0xbf7ffe68u32, 0x3f7ffe47u32, 0xbf7fff62u32, 0x3f7ffe5bu32, 0x3f7ffe96u32, 0x3f7fffc8u32, - 0xbf7ffe89u32, 0x3be7b30bu32, 0xbf7ffe2au32, 0x3f7ffeadu32, 0x3f7fffb7u32, 0xbf7ffe04u32, 0x3f7ffebau32, 0x3f7fff31u32, - 0x3f7ffeaeu32, 0xbf7ffe34u32, 0xbf7ffe51u32, 0xbf7ffe8eu32, 0x3f7ffeedu32, 0x3f7ffe77u32, 0xbf7ffe49u32, 0xbf7fff3bu32, - 0xbbdadc39u32, 0xbbe565d7u32, 0x3f7ffea0u32, 0x3f7ffe0fu32, 0xbf7ffec7u32, 0x3f7fff2eu32, 0xbb9d99d6u32, 0xbf7ffe13u32, - 0x3f7ffe8cu32, 0xbf7ffe3au32, 0x3f7ffe1du32, 0xbbe1ce37u32, 0x3f7fff2eu32, 0x3f7ffe0fu32, 0xbf7ffe98u32, 0x3f7ffe77u32, - 0xbf7ffe8eu32, 0xbf7ffe51u32, 0x3f7ffeaeu32, 0xbf7ffe04u32, 0x3f7ffe80u32, 0x3f7ffeadu32, 0x3f7ffe96u32, 0x3f7ffe35u32, - 0x3f7ffe9du32, 0xbf7ffe7du32, 0x3f7ffee2u32, 0xbf7ffe44u32, 0x3f7ffea1u32, 0x3f7fff0bu32, 0xbf7fff02u32, 0xbf7ffe35u32, - 0x3f7fff3au32, 0x3f7ffe4eu32, 0x3f7ffe89u32, 0x3f7ffe04u32, 0x3f7fff6du32, 0xbf7ffeb3u32, 0xbf7ffeedu32, 0x3ba8c65cu32, - 0xbf7ffe0fu32, 0xbf7ffe8cu32, 0xbf7fff22u32, 0x3f7ffedfu32, 0xbf7ffe6bu32, 0x3f7ffe91u32, 0x3f7ffea2u32, 0xbf7ffe5bu32, - 0x3f7ffe08u32, 0x3f7ffe26u32, 0xbf7ffe24u32, 0x3f7ffe19u32, 0x3f7ffe83u32, 0x3f7ffe7eu32, 0xbbecf39du32, 0xbf7ffe3au32, - 0xbf7ffefeu32, 0x3f7ffec3u32, 0x3f7ffe36u32, 0x3f7ffe1bu32, 0xbf7ffe23u32, 0xbf7ffe43u32, 0x3f7ffe11u32, 0x3f7ffea8u32, - 0xbf7ffe62u32, 0xbf7ffe40u32, 0x3f7fff60u32, 0xbf7ffe2bu32, 0x3f7ffe6du32, 0xbf7ffe06u32, 0x3f7ffe74u32, 0xbf7fff29u32, - 0x3f7fff18u32, 0xbbc3e499u32, 0x3f7ffe38u32, 0xbf7fff7fu32, 0x3f7ffe50u32, 0x3bf65c80u32, 0x3f7ffec5u32, 0x3f7ffed1u32, - 0x3f7fff64u32, 0x3f7ffe54u32, 0xbf7fff03u32, 0xbf7ffe52u32, 0x3f7fff2cu32, 0x3f7ffe39u32, 0xbf7ffe48u32, 0x3f7ffe07u32, - 0xbf7ffe99u32, 0x3f7ffe2fu32, 0xbf7ffe93u32, 0xbf7ffe2cu32, 0x3ba4b050u32, 0xbf7ffe10u32, 0x3f7ffe4au32, 0x3bf11da4u32, - 0xbf7ffecfu32, 0x3f7ffef4u32, 0x3f7ffe03u32, 0xbf7ffee0u32, 0xbf7ffe5fu32, 0x3f7ffe57u32, 0xbf7ffed4u32, 0xbf7ffe87u32, - 0xbf7ffe82u32, 0x3f7ffe02u32, 0x3f7fff15u32, 0xbf7ffe16u32, 0x3bf2e8dfu32, 0x3bd65dfdu32, 0xbf7ffe2du32, 0x3f7ffe22u32, - 0x3f7ffe01u32, 0xbf7ffe8au32, 0xbbfe1326u32, 0xbf7ffe3cu32, 0xbf7ffe3eu32, 0xbbf25073u32, 0x3f7fff00u32, 0x3be4ad19u32, - 0xbf7ffe60u32, 0xbf7ffe9bu32, 0x3f7ffef0u32, 0xbf7ffe21u32, 0x3f7ffe12u32, 0xbf7fffdcu32, 0xbf7ffeabu32, 0xbf7ffe05u32, - 0x3f7fff53u32, 0xbf7ffe24u32, 0x3f7ffe08u32, 0x3f7ffea6u32, 0xbf7ffe78u32, 0x3f7ffe8cu32, 0x3f7ffe5bu32, 0xbf7ffe33u32, - 0x3f7fff3du32, 0x3f7ffe3au32, 0xbf7ffea8u32, 0x3f7fff1du32, 0xbf7fff08u32, 0x3bcd847au32, 0x3f7fff32u32, 0x3f7ffe0bu32, - 0x3f7ffe7cu32, 0xbf7ffeb0u32, 0x3bcfb3b5u32, 0xbf7ffeeeu32, 0xbf7ffe1au32, 0x3bfc2109u32, 0xbf7fff56u32, 0xbf7fff6au32, - 0xbf7fff8bu32, 0x3f7ffee4u32, 0x3f7ffe17u32, 0x3f7ffe5eu32, 0xbbe92ebeu32, 0x3bed8182u32, 0xbf7fff3eu32, 0xbbed0251u32, - 0x3f7ffeb8u32, 0xbf7ffe79u32, 0xbf7ffe14u32, 0xbf7ffe53u32, 0xbbfaa408u32, 0x3f7ffe0eu32, 0xbf7ffe3fu32, 0x3f7ffec0u32, - 0xbf7ffe21u32, 0xbf7fff6eu32, 0x3f7ffe2fu32, 0x3f7ffe80u32, 0x3f7ffe77u32, 0xbf7ffe30u32, 0xbbf9940eu32, 0x3f7fff4fu32, - 0x3f7ffe31u32, 0x3f7ffe70u32, 0xbf7ffe01u32, 0x3f7ffeecu32, 0x3f7ffee9u32, 0xbf7ffed7u32, 0x3f7ffe2eu32, 0xbf7ffe32u32, - 0x3f7ffec4u32, 0xbf7ffe6du32, 0xbbfa786au32, 0xbf7ffe83u32, 0x3f7ffe24u32, 0xbf7ffe4du32, 0x3bdb83d9u32, 0xbbc2b509u32, - 0xbb49201bu32, 0xbf7ffe0bu32, 0xbf7ffe68u32, 0x3f7ffe45u32, 0x3f7ffef4u32, 0xbf7ffe99u32, 0xbf7ffe34u32, 0x3f7fff03u32, - 0x3f7ffe52u32, 0xbf7ffe0au32, 0x3f7ffe8eu32, 0xbbe825cfu32, 0xbbf7c66au32, 0xbf7ffe43u32, 0x3f7ffe94u32, 0x3f7ffeb3u32, - 0xbf7fff4bu32, 0xbf7fff37u32, 0x3f7ffe28u32, 0x3f7ffe66u32, 0x3f7fff73u32, 0x3bfacfa6u32, 0x3f7ffe97u32, 0xbf7ffef9u32, - 0x3f7fff82u32, 0x3f7ffe95u32, 0x3f7ffe43u32, 0xbf7fff15u32, 0x3f7ffed4u32, 0x3f7ffe10u32, 0xbf7ffe11u32, 0xbf7fff29u32, - 0xbf7ffecfu32, 0xbf7fffbfu32, 0xbf7ffe2du32, 0xbf7ffe6au32, 0xbf7ffe8bu32, 0x3f7ffe35u32, 0x3f7ffe40u32, 0xbf7ffea5u32, - 0x3f7ffe47u32, 0x3ba72e47u32, 0x3f7ffe7bu32, 0xbf7ffe40u32, 0x3f7fff92u32, 0x3f7ffed0u32, 0x3f7ffe48u32, 0x3be65d36u32, - 0x3f7ffeafu32, 0x3f7ffe91u32, 0x3f7ffeabu32, 0xbbf7390bu32, 0x3f7ffe1bu32, 0xbf7ffe48u32, 0xbf7ffe8au32, 0xbf7fff1cu32, - 0xbbe7eac0u32, 0x3f7ffe23u32, 0xbf7fff13u32, 0xbf7fff0fu32, 0x3f7ffeebu32, 0xbf7ffe65u32, 0xbf7ffee6u32, 0xbbf7735du32, - 0xbf7ffe0fu32, 0xbf7ffe87u32, 0xbf7ffeefu32, 0x3f7ffe72u32, 0xbf7ffe18u32, 0xbf7ffe73u32, 0xbbf21174u32, 0x3f7ffe8cu32, - 0xbf7fff06u32, 0xbf7ffe2au32, 0x3bf254e1u32, 0xbbf28696u32, 0x3bdf9950u32, 0xbf7fff2du32, 0x3f7ffef8u32, 0x3f7ffe67u32, - 0x3f7ffe46u32, 0xbf7ffe82u32, 0xbf7ffe49u32, 0x3be340a8u32, 0xbf7ffe13u32, 0xbf7ffee8u32, 0x3f7ffedfu32, 0xbb9261d1u32, - 0x3f7ffebeu32, 0xbf7ffe1eu32, 0xbf7ffe06u32, 0xbf7ffe1du32, 0xbf7ffe39u32, 0xbbe08706u32, 0x3f7ffe3bu32, 0xbf7ffeb2u32, - 0xbf7ffe9du32, 0xbbeca964u32, 0x3f7ffe0au32, 0x3f7ffeccu32, 0x3f7ffeacu32, 0xbf7ffeb4u32, 0x3f7ffef7u32, 0xbf7fff1bu32, - 0x3f7ffe4au32, 0x3beeb199u32, 0x3f7ffe24u32, 0xbbcd9765u32, 0xbbe8eff6u32, 0xbf7fff59u32, 0x3f7fff04u32, 0x3ba21ea9u32, - 0xbf7ffee2u32, 0x3f7ffee5u32, 0xbbfede76u32, 0xbf7ffe19u32, 0xbf7ffe8du32, 0x3f7fff56u32, 0x3bd00e77u32, 0xbf7ffe07u32, - 0x3f7ffea6u32, 0x3f7ffe74u32, 0x3f7fff41u32, 0x3b933220u32, 0xbf7ffe0eu32, 0xbf7ffee4u32, 0xbf7ffe3cu32, 0x3f7ffe90u32, - 0x3f7ffe69u32, 0x3f7ffe38u32, 0x3f7ffea4u32, 0xbf7ffe44u32, 0xbf7ffebcu32, 0xbf7fff1au32, 0xbf7ffef2u32, 0x3f7ffee7u32, - 0x3f7ffe28u32, 0xbf7ffe83u32, 0x3f7ffeecu32, 0x3f7ffe2fu32, 0xbf7ffe79u32, 0x3f7ffe43u32, 0xbbdb0f54u32, 0x3f7ffe8bu32, - 0x3f7ffe4eu32, 0xbf7ffe75u32, 0x3f7fff4du32, 0x3bfaf3d2u32, 0xbf7fff77u32, 0xbf7ffe37u32, 0x3bb848abu32, 0x3f7ffe50u32, - 0xbf7ffe51u32, 0xbf7ffe0cu32, 0xbf7ffe53u32, 0x3f7ffe41u32, 0xbf7ffe85u32, 0xbbf078d8u32, 0x3f7ffe84u32, 0x3f7ffe26u32, - 0xbf7ffe16u32, 0xbf7ffeabu32, 0x3f7ffe2du32, 0xbf7ffef1u32, 0xbbdfa7d4u32, 0x3f7fff68u32, 0xbb92e285u32, 0x3f7ffe36u32, - 0xbf7fff0bu32, 0xbf7ffe55u32, 0x3f7ffe08u32, 0x3bf9852au32, 0x3bf4c50du32, 0xbf7fffdbu32, 0xbf7ffe37u32, 0x3bfbb92cu32, - 0x3f7ffe3du32, 0xbf7ffe9au32, 0x3f7ffe1fu32, 0xbf7fff97u32, 0xbf7fff6du32, 0x3f7ffea9u32, 0x3f7ffe03u32, 0xbf7ffe14u32, - 0x3f7ffefeu32, 0xbf7ffe8cu32, 0x3f7fffb7u32, 0xbf7ffeebu32, 0xbf7ffed0u32, 0x3f7ffed7u32, 0x3f7fff4fu32, 0x3f7ffe66u32, - 0x3f7ffe46u32, 0xbbe745e2u32, 0x3bc0ba1bu32, 0xbf7fff55u32, 0x3f7ffe30u32, 0xbf7ffe0au32, 0xbf7ffe45u32, 0x3f7ffe88u32, - 0xbbe0719cu32, 0x3f7ffe44u32, 0xbf7ffeeeu32, 0xbf7fff87u32, 0x3f7ffe70u32, 0xbf7ffe60u32, 0x3f7ffeb5u32, 0x3f7ffec7u32, - 0xbf7ffe89u32, 0x3f7ffe5bu32, 0x3f7ffe1bu32, 0xbf7ffe7du32, 0x3b9c6b64u32, 0xbf7ffef4u32, 0xbf7fff56u32, 0xbbdf86fdu32, - 0xbf7fff04u32, 0x3bfabbb5u32, 0x3bd09fe8u32, 0xbf7ffe23u32, 0x3f7fff1cu32, 0xbb87e470u32, 0x3f7ffee5u32, 0xbf7ffebbu32, - 0xbf7ffeb1u32, 0xbf7fff35u32, 0x3bf3d461u32, 0x3f7ffe08u32, 0xbbeb56e6u32, 0xbf7fff15u32, 0xbf7ffefau32, 0xbf7ffe57u32, - 0x3f7ffe78u32, 0x3bac09f5u32, 0x3f7ffe64u32, 0x3f7ffe49u32, 0x3f7ffef9u32, 0x3bde62c7u32, 0xbf7ffe41u32, 0x3f7ffe7eu32, - 0x3f7fff0cu32, 0xbf7ffe26u32, 0x3f7ffe11u32, 0x3be80f27u32, 0x3beb8ca9u32, 0xbf7ffea4u32, 0x3f7ffe39u32, 0x3bf6a218u32, - 0xbf7ffe30u32, 0xbf7fff8cu32, 0x3f7ffe38u32, 0xbf7ffe83u32, 0xbf7ffe79u32, 0x3f7ffec9u32, 0xbf7fff14u32, 0x3f7ffeedu32, - 0xbf7ffe8au32, 0xbf7ffe6au32, 0xbf7ffe33u32, 0x3f7ffea7u32, 0x3f7ffed2u32, 0x3bd08987u32, 0x3f7ffec2u32, 0x3f7fff8eu32, - 0x3f7ffedbu32, 0xbf7ffe11u32, 0x3f7ffeb3u32, 0xbf7ffe40u32, 0x3f7ffee0u32, 0xbf7ffe7cu32, 0x3f7fffc8u32, 0x3b9e5d6fu32, - 0x3f7ffe09u32, 0xbbebfcedu32, 0x3f7ffe0cu32, 0xbf7fff53u32, 0x3f7ffe6au32, 0xbba3857du32, 0xbf7fff07u32, 0x3f7fff61u32, - 0x3f7ffe07u32, 0xbbf3d202u32, 0xbf7ffec1u32, 0xbf7ffebau32, 0xbf7ffe10u32, 0x3f7ffeadu32, 0x3bd05cc5u32, 0x3f7ffe68u32, - 0xbbab1860u32, 0x3f7ffef3u32, 0xbf7ffe63u32, 0x3f7ffe2bu32, 0x3f7ffe1eu32, 0x3f7ffe42u32, 0xbf7ffec5u32, 0xbf7ffe54u32, - 0xbf7ffe3fu32, 0x3f7ffe2eu32, 0xbf7ffe4cu32, 0xbf7ffe3eu32, 0xbf7ffe20u32, 0xbf7ffe6cu32, 0x3f7ffe37u32, 0x3f7ffeb8u32, - 0x3f7ffeabu32, 0xbf7ffe28u32, 0xbf7ffe29u32, 0x3bf3cfa3u32, 0xbf7ffe3cu32, 0xbf7fff2fu32, 0xbf7ffe92u32, 0x3f7ffeccu32, - 0xbf7ffe56u32, 0x3f7ffe03u32, 0x3f7ffe02u32, 0x3f7ffeefu32, 0x3f7ffe43u32, 0xbf7ffe19u32, 0x3f7ffe16u32, 0xbf7ffe32u32, - 0x3f7ffe8eu32, 0x3f7ffe35u32, 0x3bcbf4eau32, 0xbf7ffea5u32, 0xbf7ffedfu32, 0xbf7fff3au32, 0xbf7ffeebu32, 0x3f7ffe52u32, - 0xbf7ffee5u32, 0xbf7ffe08u32, 0xbbe38cc1u32, 0x3f7ffe95u32, 0x3f7ffe44u32, 0xbf7fff6bu32, 0xbf7ffe0eu32, 0x3f7fff0au32, - 0xbf7ffebfu32, 0x3f7ffe80u32, 0x3f7ffe06u32, 0xbf7ffe8fu32, 0x3f7ffe62u32, 0x3bf6bd27u32, 0xbf7ffed3u32, 0xbf7fff27u32, - 0x3f7ffe58u32, 0xbf7fff21u32, 0xbbd5feacu32, 0xbf7ffec1u32, 0x3f7ffedbu32, 0x3f7fff1fu32, 0xbbdeeb4cu32, 0xbf7ffe46u32, - 0x3bb6c12fu32, 0x3f7ffe49u32, 0xbf7fff7fu32, 0x3f7ffe9au32, 0xbbcef121u32, 0x3f7ffe40u32, 0xbf7ffe09u32, 0x3f7ffebau32, - 0xbf7ffe7fu32, 0xbf7ffe12u32, 0x3f7ffe6du32, 0xbf7ffe4au32, 0x3f7fff0bu32, 0xbf7fff18u32, 0xbf7ffed0u32, 0x3f7ffeb5u32, - 0x3f7ffe15u32, 0xbbfe5120u32, 0x3f7fffcbu32, 0xbf7ffeccu32, 0x3f7ffe29u32, 0x3f7ffe3fu32, 0x3f7ffe54u32, 0xbf7ffea7u32, - 0x3f7ffea4u32, 0x3bebddafu32, 0xbf7fff02u32, 0xbf7ffee3u32, 0x3f7fff73u32, 0xbba70e96u32, 0xbf7ffe47u32, 0x3f7ffe8bu32, - 0x3bf462c6u32, 0x3f7ffe1cu32, 0x3f7ffe2fu32, 0xbf7ffed6u32, 0x3f7fff80u32, 0x3f7ffe51u32, 0x3f7ffe01u32, 0x3f7fff33u32, - 0xbf7fff28u32, 0x3f7ffe4du32, 0x3f7ffe48u32, 0x3bee8b49u32, 0xbf7ffe81u32, 0x3f7ffe08u32, 0xbf7ffe0fu32, 0xbf7ffefdu32, - 0xbf7ffe8au32, 0x3f7ffe57u32, 0x3f7fff22u32, 0xbf7ffe4cu32, 0xbf7ffe19u32, 0x3f7fff04u32, 0x3f7ffea3u32, 0x3f7ffefau32, - 0xbf7ffe79u32, 0x3f7ffefeu32, 0xbf7ffe30u32, 0x3f7fff2eu32, 0x3f7ffe25u32, 0x3f7ffeffu32, 0xbf7fff43u32, 0xbbf92707u32, - 0x3f7ffe64u32, 0xbf7ffe2cu32, 0xbbdfe2d3u32, 0x3f7ffea9u32, 0x3f7fff42u32, 0x3f7ffee1u32, 0xbf7ffe1du32, 0x3f7ffea8u32, - 0x3f7fff29u32, 0xbf7fff45u32, 0x3f7ffe05u32, 0xbf7ffeb1u32, 0x3f7ffe61u32, 0xbbcd996cu32, 0xbba2a8b3u32, 0xbf7ffe3cu32, - 0x3f7fff16u32, 0x3beb098eu32, 0xbf7ffe80u32, 0x3f7ffe98u32, 0x3f7ffe22u32, 0x3be9641cu32, 0x3f7ffebbu32, 0x3f7ffe04u32, - 0xbbef8f39u32, 0x3be84b23u32, 0xbf7fff34u32, 0xbf7ffeebu32, 0x3f7ffe6cu32, 0xbf7ffe9cu32, 0x3f7fff84u32, 0x3f7ffe2au32, - 0x3f7ffea4u32, 0x3f7ffea1u32, 0x3bfc6cb1u32, 0x3f7fff9bu32, 0x3be90856u32, 0xbf7ffe5eu32, 0x3f7ffe17u32, 0x3f7ffe86u32, - 0x3bebf1cdu32, 0x3bf9995bu32, 0xbf7ffe39u32, 0x3f7ffe74u32, 0xbf7ffe24u32, 0x3f7ffe33u32, 0x3f7ffe2bu32, 0x3f7ffe76u32, - 0xbbebc991u32, 0xbf7fff0au32, 0x3f7ffe10u32, 0x3f7ffeefu32, 0x3bffb4c4u32, 0x3f7ffe62u32, 0xbf7ffe3du32, 0x3f7ffee6u32, - 0x3f7ffea6u32, 0x3f7ffe0au32, 0x3f7ffe8cu32, 0x3f7ffe26u32, 0x3f7ffe26u32, 0x3f7ffe0au32, 0x3f7ffea6u32, 0xbbfe582fu32, - 0x3f7ffee6u32, 0xbf7ffe89u32, 0x3f7ffe76u32, 0x3f7ffe33u32, 0xbf7fffddu32, 0xbf7ffe24u32, 0xbf7ffe39u32, 0x3bebb573u32, - 0x3f7ffe86u32, 0x3b46d0b3u32, 0x3f7ffe17u32, 0xbbb6ace9u32, 0x3f7ffe2au32, 0x3f7ffe6cu32, 0x3f7ffe04u32, 0x3f7fff16u32, - 0xbf7fff05u32, 0xbf7ffe2cu32, 0x3f7ffe64u32, 0xbbc5ad19u32, 0x3bfbe916u32, 0x3f7ffedbu32, 0xbf7ffec1u32, 0x3bea5802u32, - 0xbf7ffe1bu32, 0xbf7ffeb9u32, 0xbf7ffe17u32, 0xbf7ffe86u32, 0x3bff2e59u32, 0xbf7ffe33u32, 0x3f7ffe3du32, 0xbf7ffea6u32, - 0xbf7ffe0au32, 0xbf7fff53u32, 0xbf7fff5du32, 0xbbcf4628u32, 0x3bb84c63u32, 0x3bd6802eu32, 0xbf7fff74u32, 0xbbcc968cu32, - 0x3f7ffe39u32, 0xbf7ffe2au32, 0x3f7ffef8u32, 0xbf7ffe6cu32, 0xbf7ffe04u32, 0x3f7fff05u32, 0x3f7ffe2cu32, 0xbf7ffe64u32, - 0x3f7fff44u32, 0x3f7ffec1u32, 0x3f7ffe9du32, 0x3f7ffe0au32, 0x3f7ffe33u32, 0xbf7ffe97u32, 0x3f7fff1eu32, 0xbf7ffe1bu32, - 0xbf7ffeb9u32, 0x3f7fff1au32, 0xbf7fff50u32, 0x3f7ffe3du32, 0xbf7fff22u32, 0x3f7ffe8bu32, 0x3f7fff3eu32, 0x3f7ffe39u32, - 0xbf7ffe59u32, 0xbf7ffeedu32, 0xbbe564c7u32, 0xbf7ffe04u32, 0x3f7fff2cu32, 0xbf7ffe0fu32, 0xbf7ffe05u32, 0xbf7ffeb2u32, - 0xbf7ffe24u32, 0x3f7ffe03u32, 0x3f7ffe2fu32, 0x3f7fff4bu32, 0xbf7ffe7eu32, 0x3be7988cu32, 0x3f7fff38u32, 0xbbfee936u32, - 0x3f7ffeadu32, 0xbf7ffe51u32, 0xbf7ffe09u32, 0x3bf0f468u32, 0x3f7ffe8du32, 0xbf7ffec5u32, 0xbf7ffe38u32, 0xbf7ffe69u32, - 0x3f7ffe7fu32, 0xbf7ffe23u32, 0x3f7fff69u32, 0x3f7fff30u32, 0x3f7fff0fu32, 0x3f7fff11u32, 0x3bb77e72u32, 0xbbe2e5e0u32, - 0xbf7ffe0fu32, 0x3f7ffe9au32, 0xbf7ffe06u32, 0x3bb3c213u32, 0x3bf908ffu32, 0xbbd7060au32, 0x3f7ffec8u32, 0x3f7ffe0bu32, - 0xbf7ffe1au32, 0xbf7ffe35u32, 0xbf7ffeaeu32, 0x3f7fff06u32, 0xbf7fff17u32, 0x3f7ffe4bu32, 0x3f7ffe0eu32, 0xbf7ffe4cu32, - 0x3f7ffe56u32, 0x3bd75eaeu32, 0xbf7ffe54u32, 0x3f7ffe3au32, 0x3f7ffeb1u32, 0xbf7fff19u32, 0xbf7ffe93u32, 0xbbec8655u32, - 0x3f7fff1bu32, 0x3f7ffe22u32, 0x3f7ffe80u32, 0x3f7ffeafu32, 0x3f7ffe12u32, 0xbf7ffef4u32, 0xbbd6a30du32, 0xbf7fff1cu32, - 0xbf7ffe65u32, 0xbf7fff76u32, 0xbf7ffe6au32, 0xbbce823du32, 0xbf7ffe9bu32, 0xbbb969ecu32, 0x3f7ffe19u32, 0x3f7ffe44u32, - 0x3f7fffa6u32, 0xbf7ffe2du32, 0xbf7ffe1fu32, 0xbbd3f1c1u32, 0x3bc9d942u32, 0xbf7ffebeu32, 0xbf7ffe88u32, 0x3f7fff25u32, - 0x3f7fff33u32, 0x3f7ffe62u32, 0x3f7ffe5cu32, 0xbf7ffe10u32, 0x3f7ffeefu32, 0xbf7fffb0u32, 0x3f7fff6eu32, 0xbf7ffebbu32, - 0x3bd9b609u32, 0x3f7ffedcu32, 0xbf7ffe4eu32, 0xbf7ffe58u32, 0x3f7ffe41u32, 0x3f7ffec2u32, 0x3f7ffe5du32, 0xbf7ffe9cu32, - 0xbf7ffec0u32, 0xbf7ffe91u32, 0x3f7ffe77u32, 0x3bddf623u32, 0x3f7ffe92u32, 0x3bbad1a9u32, 0xbf7ffe11u32, 0x3bf5b6d0u32, - 0x3f7ffe1cu32, 0x3f7ffe7fu32, 0xbf7ffe09u32, 0xbf7ffe34u32, 0x3bf02e4eu32, 0x3bf1fe0cu32, 0xbf7fff89u32, 0x3f7ffe08u32, - 0x3f7ffe79u32, 0x3becb273u32, 0x3f7fff00u32, 0x3f7ffe15u32, 0x3f7ffefeu32, 0xbf7ffe5eu32, 0xbf7ffe6bu32, 0xbf7ffe84u32, - 0xbbe83dbfu32, 0x3f7ffe1du32, 0x3f7ffecdu32, 0x3f7ffe8fu32, 0xbf7fff6cu32, 0x3be97458u32, 0xbf7ffe4fu32, 0x3ba434d6u32, - 0x3bd580d7u32, 0x3f7fff46u32, 0x3f7ffe18u32, 0xbbaeedafu32, 0x3f7ffeddu32, 0xbf7ffe27u32, 0xbf7ffe3du32, 0xbf7ffe9du32, - 0x3f7ffe89u32, 0x3f7ffe0au32, 0xbf7ffe59u32, 0x3f7ffe7bu32, 0x3f7ffe8eu32, 0xbbdb4a93u32, 0xbbfda153u32, 0xbf7fff48u32, - 0x3f7ffe1eu32, 0x3f7ffe5cu32, 0xbf7ffe6au32, 0xbf7fff1cu32, 0xbbf09242u32, 0x3f7fff30u32, 0xbbe3533fu32, 0xbf7ffe1bu32, - 0x3f7ffec1u32, 0xbf7ffe17u32, 0xbf7ffe04u32, 0x3f7fff17u32, 0xbf7fff25u32, 0x3f7ffec4u32, 0xbf7ffeacu32, 0xbf7fff4du32, - 0x3f7ffe51u32, 0x3f7fff01u32, 0x3f7ffe40u32, 0x3f7ffe0cu32, 0x3f7ffe96u32, 0x3f7ffe8au32, 0x3f7ffe7au32, 0x3bff9c78u32, - 0xbbf7b166u32, 0x3f7fff79u32, 0x3f7ffed9u32, 0xbf7ffec2u32, 0x3f7ffe4eu32, 0xbbf9c9beu32, 0xbbc6f806u32, 0x3f7ffebeu32, - 0x3f7ffe65u32, 0xbf7ffe12u32, 0xbf7ffe0eu32, 0x3f7ffe35u32, 0xbf7ffef2u32, 0xbf7ffe86u32, 0xbf7fff5fu32, 0x3f7ffe8bu32, - 0x3f7ffed7u32, 0x3bbb151eu32, 0x3f7ffed6u32, 0x3f7ffe9fu32, 0xbf7ffe78u32, 0x3f7fff56u32, 0xbb99e210u32, 0xbf7ffe68u32, - 0x3f7ffeabu32, 0x3f7ffe06u32, 0x3f7fffa0u32, 0x3f7fff71u32, 0xbbe69c7cu32, 0xbf7ffe9bu32, 0xbf7ffed0u32, 0xbbe4f8fcu32, - 0x3f7ffe2eu32, 0xbf7ffea1u32, 0x3f7ffe95u32, 0x3f7ffea0u32, 0x3f7ffe38u32, 0x3f7ffe71u32, 0x3f7fff0eu32, 0x3f7ffe0fu32, - 0x3f7ffee7u32, 0xbf7ffe26u32, 0x3f7ffeaau32, 0xbf7ffe6cu32, 0x3f7ffeedu32, 0xbf7ffe0du32, 0x3f7fff45u32, 0x3f7ffeb1u32, - 0xbbd676f2u32, 0x3f7ffebdu32, 0xbf7ffe31u32, 0x3f7ffe87u32, 0xbf7ffe57u32, 0xbf7fff31u32, 0x3f7ffe60u32, 0xbf7ffedau32, - 0x3f7fff69u32, 0x3f7fff2cu32, 0xbf7ffe83u32, 0xbf7ffe2bu32, 0xbf7ffea4u32, 0xbf7fffa3u32, 0xbf7ffe4au32, 0xbbea11e9u32, - 0x3f7ffe16u32, 0xbf7fffa5u32, 0xbf7ffe31u32, 0x3f7ffe3au32, 0xbf7fff0du32, 0xbf7ffe84u32, 0xbf7ffe6bu32, 0x3f7ffeedu32, - 0xbf7ffe6cu32, 0x3f7ffe44u32, 0x3f7fff24u32, 0x3f7ffeaeu32, 0x3b9e5cfdu32, 0x3f7ffeb7u32, 0x3f7ffe35u32, 0x3f7ffe65u32, - 0x3f7ffe4eu32, 0x3f7ffe40u32, 0xbf7ffec9u32, 0xbf7ffe6au32, 0xbf7ffec6u32, 0x3f7fffe0u32, 0x3f7ffe75u32, 0x3f7fff1eu32, - 0xbbfec9a7u32, 0xbf7ffe95u32, 0xbf7ffe71u32, 0x3f7ffe26u32, 0x3f7ffe0du32, 0x3f7ffe82u32, 0x3f7ffe31u32, 0xbf7ffe87u32, - 0x3f7ffe10u32, 0x3f7ffe4au32, 0xbf7ffe16u32, 0xbbfe6222u32, 0xbf7ffe3au32, 0x3f7fff23u32, 0x3f7ffe7fu32, 0xbf7fff34u32, - 0xbf7ffe32u32, 0xbbf914c0u32, 0x3f7ffea2u32, 0xbf7ffe40u32, 0x3f7ffec9u32, 0x3f7fff51u32, 0xbf7ffe52u32, 0xbf7ffe01u32, - 0x3f7ffe1au32, 0x3f7ffee9u32, 0xbbb65715u32, 0x3f7ffe8fu32, 0xbf7ffed3u32, 0x3f7fff22u32, 0x3f7ffe22u32, 0x3f7ffe44u32, - 0xbbfaadfeu32, 0x3f7ffe69u32, 0xbf7fff2du32, 0x3f7ffe36u32, 0xbf7ffe23u32, 0xbf7ffedeu32, 0xbf7fff54u32, 0x3f7fff63u32, - 0xbf7ffeceu32, 0x3f7fff4eu32, 0x3f7ffe9du32, 0x3f7ffefbu32, 0x3f7ffe5eu32, 0xbf7ffe79u32, 0xbf7ffedcu32, 0xbf7fff8au32, - 0xbf7ffe43u32, 0x3f7ffe10u32, 0xbbd4c5c2u32, 0xbf7fff2fu32, 0xbf7ffe32u32, 0xbf7ffee4u32, 0xbf7fff08u32, 0x3f7ffeecu32, - 0x3f7ffe45u32, 0x3f7ffe49u32, 0x3f7ffed4u32, 0x3f7ffe9eu32, 0x3f7ffe91u32, 0x3f7ffef1u32, 0x3f7ffe14u32, 0xbf7fff09u32, - 0xbf7ffe37u32, 0x3f7ffe7au32, 0x3f7ffe46u32, 0xbf7ffea3u32, 0x3f7ffe07u32, 0xbf7ffef4u32, 0xbf7ffec2u32, 0x3f7ffee2u32, - 0x3f7ffe1bu32, 0x3f7ffe05u32, 0xbf7ffea5u32, 0x3bee572au32, 0x3f7ffeb8u32, 0xbf7ffe2fu32, 0xbf7ffe42u32, 0xbf7ffe59u32, - 0xbf7fff5au32, 0xbf7ffee1u32, 0x3f7ffe3bu32, 0xbf7ffe9au32, 0x3f7fff37u32, 0x3f7ffeddu32, 0xbf7ffef6u32, 0x3f7ffe90u32, - 0xbf7ffe04u32, 0x3f7ffe0bu32, 0xbf7ffe8cu32, 0xbf7ffe83u32, 0x3f7ffe38u32, 0x3f7ffee5u32, 0xbf7ffe8au32, 0xbf7ffe0fu32, - 0xbf7fff04u32, 0xbf7ffe29u32, 0xbf7ffe4du32, 0x3f7ffe1cu32, 0xbf7ffec1u32, 0xbf7ffea9u32, 0x3f7ffe09u32, 0x3f7ffe15u32, - 0xbf7ffe39u32, 0xbf7ffe06u32, 0x3f7ffe0eu32, 0x3f7ffe48u32, 0x3b92c549u32, 0xbf7ffe12u32, 0xbf7ffe02u32, 0xbf7ffea8u32, - 0xbf7ffe93u32, 0x3f7ffe3eu32, 0x3f7ffe30u32, 0x3f7fff02u32, 0xbf7ffe2au32, 0xbf7ffe34u32, 0xbf7ffe8eu32, 0xbf7ffe55u32, - 0x3f7fff0fu32, 0x3f7ffe6du32, 0x3f7ffe10u32, 0x3f7ffe22u32, 0x3f7fff58u32, 0xbf7fff67u32, 0x3f7ffe19u32, 0xbf7ffee9u32, - 0xbf7ffe9du32, 0x3f7ffe43u32, 0x3f7fff2fu32, 0x3bfd8c33u32, 0xbf7ffe6eu32, 0xbf7fffaau32, 0xbf7fff4au32, 0x3f7ffedbu32, - 0x3f7ffe0au32, 0xbf7ffe58u32, 0xbbebdb89u32, 0x3be6f5c5u32, 0x3f7ffe24u32, 0xbf7ffeb5u32, 0xbbf6761au32, 0x3f7ffe3du32, - 0xbf7ffecdu32, 0x3f7ffe92u32, 0x3f7ffe2du32, 0xbf7ffe44u32, 0x3bd9a77bu32, 0x3f7ffe4au32, 0xbf7ffec3u32, 0x3f7ffe35u32, - 0x3f7ffeb0u32, 0x3f7ffe1fu32, 0xbf7fff6fu32, 0x3f7ffe1eu32, 0xbf7ffea4u32, 0x3f7ffe5cu32, 0x3f7fff36u32, 0xbf7ffe98u32, - 0x3f7fff10u32, 0xbf7ffea6u32, 0x3f7ffec9u32, 0xbf7ffea0u32, 0x3f7ffe6cu32, 0x3f7ffe40u32, 0xbf7ffe7eu32, 0xbf7ffe21u32, - 0x3f7ffe0cu32, 0x3f7ffe18u32, 0xbf7fffd1u32, 0x3f7ffe80u32, 0xbf7ffe94u32, 0xbf7ffe74u32, 0xbbd487e1u32, 0xbbe1d69eu32, - 0xbf7ffed7u32, 0xbf7ffe27u32, 0x3befb386u32, 0x3f7ffe73u32, 0x3f7ffe65u32, 0x3f7ffe3au32, 0xbf7fff13u32, 0xbf7ffe32u32, - 0xbf7fff56u32, 0x3f7fff85u32, 0xbf7ffef9u32, 0x3f7ffe1bu32, 0xbf7ffebeu32, 0xbf7ffe01u32, 0xbf7ffeb7u32, 0x3ba9621eu32, - 0xbbfc2106u32, 0xbbe72770u32, 0xbf7ffe0bu32, 0xbf7ffe38u32, 0xbf7ffee5u32, 0x3f7ffec6u32, 0xbf7ffe6bu32, 0x3f7ffe69u32, - 0xbf7ffe08u32, 0xbf7fff97u32, 0x3f7ffe99u32, 0xbf7ffe57u32, 0x3f7ffe97u32, 0x3f7ffe6fu32, 0xbbd2c5bfu32, 0xbf7ffeeau32, - 0xbf7ffe46u32, 0xbf7ffe77u32, 0xbf7ffe20u32, 0xbbe46cccu32, 0x3f7ffe0au32, 0xbbf50605u32, 0x3f7ffebdu32, 0xbf7ffea7u32, - 0x3f7ffe3eu32, 0xbf7ffe25u32, 0x3f7ffe2eu32, 0x3f7ffe6au32, 0x3f7ffe0fu32, 0x3f7ffe04u32, 0x3f7ffe02u32, 0xbf7fff91u32, - 0x3f7ffe87u32, 0xbb76ed70u32, 0xbf7ffe13u32, 0x3f7ffe15u32, 0xbbec7374u32, 0x3bea4291u32, 0xbf7ffe45u32, 0x3f7ffee4u32, - 0x3bea9840u32, 0x3f7ffe67u32, 0xbbe18a78u32, 0xbf7ffe5du32, 0x3f7fff71u32, 0xbf7fff19u32, 0xbf7ffe9eu32, 0x3bb6cfedu32, - 0x3f7ffed5u32, 0xbbd15f79u32, 0x3f7ffedfu32, 0xbf7ffef4u32, 0xbbebb0e6u32, 0x3f7fff6bu32, 0x3f7ffeb2u32, 0xbf7ffe30u32, - 0xbf7ffe19u32, 0x3bec062cu32, 0x3f7ffed9u32, 0xbf7ffe61u32, 0xbf7ffe68u32, 0xbbf62489u32, 0x3f7ffe07u32, 0x3f7fff27u32, - 0xbf7fff2du32, 0xbf7ffe1du32, 0xbf7ffee6u32, 0xbf7ffe63u32, 0xbbdb8429u32, 0x3f7ffe39u32, 0x3f7ffee9u32, 0x3f7ffecdu32, - 0x3f7ffe7du32, 0xbf7ffec0u32, 0xbf7ffec7u32, 0x3f7ffe70u32, 0x3f7ffe8du32, 0x3f7fff22u32, 0x3f7ffe79u32, 0xbbf3fddfu32, - 0x3f7ffec5u32, 0x3f7ffe4cu32, 0x3f7ffe8au32, 0xbf7ffe3cu32, 0x3f7ffe75u32, 0xbf7ffeaau32, 0x3f7ffef2u32, 0xbf7ffe8bu32, - 0x3bd99a3du32, 0xbf7ffe23u32, 0x3f7ffe17u32, 0xbf7ffe06u32, 0x3f7fff3bu32, 0xbf7ffe8eu32, 0xbf7ffe98u32, 0xbf7ffeb7u32, - 0xbf7ffefau32, 0x3f7ffe1fu32, 0x3bd872cfu32, 0xbb9e61e1u32, 0x3f7ffe22u32, 0xbf7ffeddu32, 0xbf7ffe21u32, 0x3f7ffe54u32, - 0x3f7ffe78u32, 0xbf7fff08u32, 0x3f7ffe0eu32, 0xbf7ffe36u32, 0x3f7ffe0eu32, 0x3f7ffe54u32, 0xbf7ffe03u32, 0xbbe2cdb1u32, - 0x3f7ffe4eu32, 0xbf7ffe83u32, 0x3f7ffeaeu32, 0x3f7ffe17u32, 0x3f7ffe3fu32, 0xbf7ffe5cu32, 0xbf7fffc0u32, 0xbbf66fe3u32, - 0x3f7fff5au32, 0x3f7ffe9cu32, 0x3bf0548fu32, 0x3f7ffea1u32, 0xbf7ffea5u32, 0xbf7ffe6du32, 0xbf7fff23u32, 0x3f7ffe92u32, - 0xbf7ffef8u32, 0xbf7ffe5eu32, 0x3f7ffe6bu32, 0x3f7ffe37u32, 0x3f7ffe60u32, 0xbf7fff40u32, 0xbbfadb1fu32, 0x3f7ffe36u32, - 0x3f7ffe34u32, 0x3f7ffe04u32, 0xbf7fffa8u32, 0xbf7ffe01u32, 0x3f7ffe09u32, 0x3beea03du32, 0x3f7ffe11u32, 0x3f7ffe85u32, - 0xbf7ffe16u32, 0x3f7ffef6u32, 0xbf7fff0fu32, 0x3f7ffe08u32, 0xbf7ffe7eu32, 0xbf7ffe25u32, 0xbf7ffe4au32, 0xbf7ffe42u32, - 0xbf7ffe90u32, 0x3f7fff2eu32, 0xbf7ffe20u32, 0x3f7ffe9fu32, 0xbbf7c017u32, 0x3f7ffe4fu32, 0x3f7ffe1au32, 0x3f7ffebeu32, - 0xbf7ffe41u32, 0xbf7fff1cu32, 0x3f7ffe95u32, 0xbf7ffea4u32, 0x3f7ffedeu32, 0x3f7ffe12u32, 0x3bf6c5ffu32, 0xbf7ffeb1u32, - 0x3bf45a2eu32, 0xbf7ffee7u32, 0x3bbe3228u32, 0x3f7ffe3bu32, 0x3f7ffe93u32, 0xbf7ffe38u32, 0xbf7ffe35u32, 0xbf7ffedcu32, - 0x3f7ffe61u32, 0x3f7ffe2eu32, 0x3f7fff90u32, 0x3f7ffe79u32, 0x3bc90183u32, 0xbbd51dadu32, 0x3f7ffef3u32, 0xbf7ffe47u32, - 0x3f7ffe6eu32, 0xbf7ffe59u32, 0xbb7bc8eau32, 0xbf7ffe02u32, 0x3f7ffe06u32, 0x3f7ffe3du32, 0x3f7ffe5au32, 0xbf7fff1au32, - 0x3f7ffed6u32, 0xbf7ffe10u32, 0x3f7ffeafu32, 0x3f7ffe0au32, 0xbf7ffe7du32, 0xbf7ffe7bu32, 0x3f7ffe88u32, 0x3f7ffe76u32, - 0xbf7ffe1eu32, 0x3f7ffe69u32, 0x3f7fff00u32, 0xbf7ffef5u32, 0x3f7ffe60u32, 0xbbceff63u32, 0xbf7fff82u32, 0xbf7ffe63u32, - 0xbf7ffe8eu32, 0x3bfd8798u32, 0x3f7fff32u32, 0xbf7ffe1du32, 0x3f7ffe74u32, 0x3f7ffe03u32, 0x3f7fffe6u32, 0x3f7ffef4u32, - 0x3bdde463u32, 0xbf7ffe40u32, 0x3f7fff61u32, 0xbbcbe2adu32, 0xbf7fff28u32, 0x3f7fff79u32, 0xbf7fffc9u32, 0xbf7ffee0u32, - 0x3f7fff4du32, 0xbf7ffec0u32, 0xbf7ffe39u32, 0x3f7ffe89u32, 0x3f7ffe24u32, 0xbf7fff88u32, 0x3f7fff25u32, 0x3f7ffe5bu32, - 0xbf7fff09u32, 0xbf7ffe7au32, 0x3f7ffe9au32, 0xbf7ffee5u32, 0x3f7ffeddu32, 0x3f7ffe26u32, 0xbf7ffe45u32, 0xbf7ffeccu32, - 0xbf7ffea0u32, 0xbbbc8740u32, 0x3bc58d0du32, 0x3f7ffe04u32, 0xbf7ffeb6u32, 0xbf7ffec8u32, 0x3f7ffe2fu32, 0xbf7ffe52u32, - 0xbf7ffe4bu32, 0xbf7ffe0fu32, 0x3f7ffe0cu32, 0x3f7ffe5du32, 0x3f7ffe56u32, 0x3f7ffe84u32, 0x3f7fff42u32, 0xbbfa14ecu32, - 0xbf7ffe6fu32, 0x3f7ffe1cu32, 0x3f7ffe2au32, 0xbf7ffe21u32, 0x3b75924fu32, 0x3f7ffe51u32, 0xbf7ffe62u32, 0xbf7ffec4u32, - 0x3bee073bu32, 0xbf7fff55u32, 0x3f7fff27u32, 0x3f7ffea8u32, 0x3f7ffe03u32, 0x3bf7037fu32, 0x3f7ffe69u32, 0x3f7ffe76u32, - 0xbf7ffe7bu32, 0xbf7ffe7du32, 0xbbe3b95fu32, 0xbf7ffeb1u32, 0x3f7ffe7eu32, 0x3f7ffea4u32, 0xbf7ffe06u32, 0x3f7fff1au32, - 0xbf7ffeafu32, 0xbf7ffecdu32, 0x3f7ffef5u32, 0xbf7ffebau32, 0x3bdaccf3u32, 0x3f7fff75u32, 0x3f7ffe44u32, 0x3bf43126u32, - 0x3f7fff11u32, 0xbb8b4592u32, 0x3f7ffed7u32, 0xbf7ffe53u32, 0xbf7ffe11u32, 0x3f7ffea0u32, 0xbb6fbcf7u32, 0xbf7ffeddu32, - 0x3f7ffee5u32, 0xbbf049cau32, 0x3f7ffef9u32, 0x3f7ffec0u32, 0xbf7ffe3du32, 0x3f7ffe94u32, 0x3f7ffe07u32, 0xbf7ffe63u32, - 0xbf7fff09u32, 0xbbf9d466u32, 0x3f7ffea3u32, 0x3f7ffebdu32, 0x3f7ffe4fu32, 0xbbf53d51u32, 0xbf7fff12u32, 0xbf7ffed0u32, - 0xbf7ffea1u32, 0x3f7ffe71u32, 0x3f7ffe0du32, 0x3f7ffe0au32, 0xbf7ffe02u32, 0x3f7fff8fu32, 0xbf7ffef8u32, 0xbf7ffe34u32, - 0x3f7fff83u32, 0x3f7fff9cu32, 0xbf7ffea6u32, 0x3f7ffe3cu32, 0xbf7ffe0cu32, 0x3f7ffefbu32, 0x3f7ffe4bu32, 0x3f7ffeb6u32, -]; -#[rustfmt::skip] -pub static UCRT_COSF_KEYS: [u32; 703] = [ - 0x3fc81e28u32, 0x3fc82d15u32, 0x3fc84caeu32, 0x3fc855b1u32, 0x3fc8bc8du32, 0x3fc98e69u32, 0x3fc9c9c5u32, 0x3fc9e926u32, - 0x3fc9fa78u32, 0x3fca0286u32, 0x3fca07f7u32, 0x3fca087fu32, 0x3fca0c27u32, 0x4048910cu32, 0x40489292u32, 0x40489315u32, - 0x4048945fu32, 0x404894e4u32, 0x404895ceu32, 0x40489698u32, 0x4048a2f8u32, 0x4048a57fu32, 0x4048aa95u32, 0x4048ac7fu32, - 0x4048b148u32, 0x4048b2a5u32, 0x4048b2d1u32, 0x4048ba58u32, 0x4048c2cfu32, 0x40496aceu32, 0x40496d68u32, 0x40496debu32, - 0x40497778u32, 0x40498134u32, 0x4049820cu32, 0x4049862du32, 0x404986fcu32, 0x40498763u32, 0x4049893fu32, 0x40498ab0u32, - 0x40498d44u32, 0x40498f0au32, 0x40968fb6u32, 0x409709c0u32, 0x40c8d38eu32, 0x40c8d39fu32, 0x40c8d3b0u32, 0x40c8d6b0u32, - 0x40c8d931u32, 0x40c8da60u32, 0x40c8df7bu32, 0x40c8e03bu32, 0x40c8e3cfu32, 0x40c8eecfu32, 0x40c9367bu32, 0x40c938e4u32, - 0x40c93bcfu32, 0x40c94481u32, 0x40c94697u32, 0x40c946cfu32, 0x40c94ef1u32, 0x40fb25a1u32, 0x40fb8efcu32, 0x40fb911au32, - 0x40fb9182u32, 0x40fb924eu32, 0x4116acc3u32, 0x4116b10au32, 0x412fd42cu32, 0x41300c5cu32, 0x4148f557u32, 0x4148f611u32, - 0x4148fa1bu32, 0x41492c3au32, 0x417b343eu32, 0x417b3535u32, 0x417b368bu32, 0x417b3961u32, 0x417b3a8au32, 0x417b5b19u32, - 0x417b6fdfu32, 0x418a2b82u32, 0x4196bcafu32, 0x4196bd83u32, 0x41afde74u32, 0x41afdf71u32, 0x41afe2b4u32, 0x41afe3c9u32, - 0x41aff8c5u32, 0x41aff97bu32, 0x41affc18u32, 0x41affdadu32, 0x41bc71bau32, 0x41bc8c52u32, 0x41c90119u32, 0x41e221e8u32, - 0x41e2228cu32, 0x41e22310u32, 0x41e22594u32, 0x41e227b3u32, 0x41e23bb9u32, 0x41e23fa1u32, 0x41fb4507u32, 0x41fb4514u32, - 0x41fb45cfu32, 0x41fb48bdu32, 0x420a337fu32, 0x420a416au32, 0x420a4285u32, 0x4216c3efu32, 0x4216c6ecu32, 0x421d0efau32, - 0x422356ebu32, 0x42236374u32, 0x422fe763u32, 0x422ff5bcu32, 0x423c846au32, 0x423c8609u32, 0x424915f9u32, 0x4255990eu32, - 0x4255997eu32, 0x42559afau32, 0x426229e3u32, 0x42622c69u32, 0x426873f5u32, 0x426ebb68u32, 0x426ebe17u32, 0x4275129eu32, - 0x427512feu32, 0x4275131cu32, 0x427b5ad7u32, 0x4283f040u32, 0x428712c7u32, 0x42871790u32, 0x4296c8b1u32, 0x4296cfadu32, - 0x4299ecbau32, 0x42a9a22bu32, 0x42b63293u32, 0x42b63979u32, 0x42b639b1u32, 0x42b63a3au32, 0x42bc7afeu32, 0x42bc7b15u32, - 0x42c913cau32, 0x42cf5462u32, 0x42cf5c1au32, 0x42d59d94u32, 0x42dbe5c0u32, 0x42e22e8eu32, 0x42e22e9eu32, 0x42e87743u32, - 0x42e877beu32, 0x42e87df7u32, 0x42eebf76u32, 0x42eec010u32, 0x42f50ecbu32, 0x42fb4fe6u32, 0x42fb57a9u32, 0x430714bau32, - 0x431081a6u32, 0x43235b51u32, 0x432ccb94u32, 0x432fec70u32, 0x4334a2a0u32, 0x43363840u32, 0x433c80ceu32, 0x4342c901u32, - 0x43527e70u32, 0x43541070u32, 0x4358c6d8u32, 0x435be779u32, 0x435f0ba5u32, 0x436557fcu32, 0x43687c4eu32, 0x436b9cccu32, - 0x436d2ed9u32, 0x437509b4u32, 0x4378306fu32, 0x4379bfb2u32, 0x437e772fu32, 0x4380cdafu32, 0x4380ceb1u32, 0x438715c0u32, - 0x438715fdu32, 0x43908445u32, 0x439795bfu32, 0x43985ed9u32, 0x439b817eu32, 0x439dde3du32, 0x43a35dd6u32, 0x43a4ee49u32, - 0x43a9a515u32, 0x43b4a35eu32, 0x43baebcfu32, 0x43c458a8u32, 0x43c5ec81u32, 0x43d0eb62u32, 0x43d27d97u32, 0x43d8c48cu32, - 0x43df0e84u32, 0x43e55578u32, 0x43e55714u32, 0x43eec3c0u32, 0x43f82e92u32, 0x43fce6b9u32, 0x43fce6c9u32, 0x43fce6d9u32, - 0x43fce6e9u32, 0x440004a7u32, 0x4410832du32, 0x441926abu32, 0x441ea606u32, 0x44203827u32, 0x44229362u32, 0x4429a5c7u32, - 0x442d932cu32, 0x4438920cu32, 0x443bb63au32, 0x444390dau32, 0x4445869cu32, 0x445b20bbu32, 0x445be8e0u32, 0x446428fau32, - 0x4467b1c4u32, 0x446f8c47u32, 0x447ce66du32, 0x447f40a1u32, 0x448004e8u32, 0x448584aau32, 0x44893f59u32, 0x44940c6bu32, - 0x44af2491u32, 0x44b6ccebu32, 0x44b82d41u32, 0x44ccfd63u32, 0x44d27c62u32, 0x44d2e0eau32, 0x44d34572u32, 0x44d53c21u32, - 0x44d98e64u32, 0x44dabbedu32, 0x44ea0ca0u32, 0x450069bau32, 0x4503f24bu32, 0x45056b49u32, 0x450971f6u32, 0x450a08bfu32, - 0x4516cbd0u32, 0x451c7db5u32, 0x451caff4u32, 0x451e7424u32, 0x451ed8a9u32, 0x4521fd19u32, 0x4526b34bu32, 0x4526b37fu32, - 0x45392877u32, 0x453be831u32, 0x4544be19u32, 0x454fbcc9u32, 0x4555a0f3u32, 0x455d4917u32, 0x45626435u32, 0x456ad5a3u32, - 0x456ec2b9u32, 0x4574422cu32, 0x457606bau32, 0x457638cdu32, 0x4576e8bcu32, 0x45771b3bu32, 0x457b53f0u32, 0x458d7838u32, - 0x4596cbd5u32, 0x4596cbf3u32, 0x45a0066eu32, 0x45a38f17u32, 0x45a3da7cu32, 0x45a9d795u32, 0x45ab69b7u32, 0x45abce5cu32, - 0x45b98cf1u32, 0x45bc1a64u32, 0x45bf70e2u32, 0x45c0142au32, 0x45c61defu32, 0x45cc4d30u32, 0x45ccb1bbu32, 0x45cf3f47u32, - 0x45d00838u32, 0x45d55580u32, 0x45d5ec4cu32, 0x45dfd6b3u32, 0x45e0221bu32, 0x45e10431u32, 0x45e34659u32, 0x45edc78fu32, - 0x45f054fbu32, 0x45f2b018u32, 0x45f82fa1u32, 0x45fc815du32, 0x45fdc816u32, 0x46012c6du32, 0x460dd037u32, 0x46133692u32, - 0x461adeeeu32, 0x46259ee9u32, 0x4629fd51u32, 0x462a942bu32, 0x462c9763u32, 0x463aff9eu32, 0x463c5922u32, 0x464df230u32, - 0x46561e88u32, 0x465a63d0u32, 0x465bef99u32, 0x46651745u32, 0x4665ae14u32, 0x4668f7f8u32, 0x4676b68eu32, 0x46770e92u32, - 0x467ee2dbu32, 0x468ee4b1u32, 0x468f16f5u32, 0x46913fe8u32, 0x4691d06bu32, 0x46a107f7u32, 0x46a38bfeu32, 0x46a3bb24u32, - 0x46a43f10u32, 0x46b7bf0eu32, 0x46be8212u32, 0x46c87912u32, 0x46d0ea7du32, 0x46dae170u32, 0x46e391afu32, 0x46e6771fu32, - 0x46ea3eb5u32, 0x46f19bb0u32, 0x46f26b08u32, 0x46f60060u32, 0x46f6457fu32, 0x46fda26cu32, 0x46ffa5acu32, 0x470ade45u32, - 0x470e834fu32, 0x4711f619u32, 0x471e451du32, 0x47220671u32, 0x47261fbcu32, 0x47290526u32, 0x472f5d5au32, 0x473497afu32, - 0x47368030u32, 0x47370bfdu32, 0x473b31d9u32, 0x473e8538u32, 0x4742e3a4u32, 0x4744729cu32, 0x4744f9b6u32, 0x474583f1u32, - 0x474a3404u32, 0x474d0050u32, 0x474db9a7u32, 0x4753ccbeu32, 0x4756799cu32, 0x4757d4c1u32, 0x475eefc0u32, 0x47714d21u32, - 0x47732dcbu32, 0x477464d0u32, 0x477adf96u32, 0x47816819u32, 0x47873b90u32, 0x478a50e5u32, 0x47920a85u32, 0x479c6608u32, - 0x47a0fe8du32, 0x47aa5ebcu32, 0x47b0bec9u32, 0x47b14a96u32, 0x47becfd6u32, 0x47c4d726u32, 0x47ca75feu32, 0x47d03768u32, - 0x47e2e67bu32, 0x47e986efu32, 0x47ed648bu32, 0x47efa9bbu32, 0x47fa8478u32, 0x47fd1db5u32, 0x47fe0889u32, 0x47ffc838u32, - 0x4805f217u32, 0x48092671u32, 0x48125cfdu32, 0x4816ac79u32, 0x4816ec18u32, 0x48197fd4u32, 0x481b1f4du32, 0x481bc761u32, - 0x48239aeeu32, 0x482b7bd5u32, 0x482fd135u32, 0x48416aa4u32, 0x4842c36eu32, 0x4842d0c9u32, 0x484a1889u32, 0x484a7c47u32, - 0x484ad5d0u32, 0x48521f23u32, 0x48527719u32, 0x485a2361u32, 0x48630e88u32, 0x4864a55eu32, 0x4864b126u32, 0x48658b7eu32, - 0x486598d8u32, 0x486c65adu32, 0x486d3da9u32, 0x486f580du32, 0x4871a063u32, 0x487993bfu32, 0x488109d9u32, 0x488a9342u32, - 0x488b445du32, 0x488d5fefu32, 0x488e36bdu32, 0x4891c5cdu32, 0x4892214du32, 0x489682d9u32, 0x489bb09au32, 0x489c5524u32, - 0x48a3e03du32, 0x48a54694u32, 0x48a8527au32, 0x48adb735u32, 0x48ae1a5du32, 0x48aedc8eu32, 0x48b175c9u32, 0x48baf9b3u32, - 0x48bcf60cu32, 0x48c1c300u32, 0x48c579b9u32, 0x48cc170bu32, 0x48ccb4e8u32, 0x48d31ff6u32, 0x48d5550eu32, 0x48def0ecu32, - 0x48e2a518u32, 0x48e3b7c9u32, 0x48e51bf7u32, 0x48e873a8u32, 0x48f28ec2u32, 0x48f3b2edu32, 0x48f7f21du32, 0x490127b1u32, - 0x49114e9eu32, 0x491b05e0u32, 0x491d60c4u32, 0x491f8627u32, 0x492b4d7eu32, 0x493318e5u32, 0x493a3979u32, 0x493eb910u32, - 0x4941ddb4u32, 0x494252bau32, 0x494e09c5u32, 0x49547a52u32, 0x4957f916u32, 0x495a46b9u32, 0x495ac73cu32, 0x495b5a98u32, - 0x4961540fu32, 0x49639d15u32, 0x4968359cu32, 0x4968430fu32, 0x4971857du32, 0x497297e3u32, 0x4976b5cbu32, 0x4977c00bu32, - 0x497b880cu32, 0x497c756fu32, 0x497f7529u32, 0x498890a0u32, 0x4988d330u32, 0x498f3a37u32, 0x498f7cc7u32, 0x4992577eu32, - 0x49991aceu32, 0x499d570cu32, 0x499e122bu32, 0x49a0b56au32, 0x49b203a6u32, 0x49ba18fau32, 0x49bd8a96u32, 0x49c0f585u32, - 0x49c39230u32, 0x49d25102u32, 0x49d37f24u32, 0x49d93d29u32, 0x49db5030u32, 0x49dd7e82u32, 0x49e7a817u32, 0x49eb33f6u32, - 0x49ee9d6cu32, 0x49f11deau32, 0x49f7261bu32, 0x49fbdf6cu32, 0x49fe3b97u32, 0x49ff89f9u32, 0x4a096fa1u32, 0x4a0b15fau32, - 0x4a15bd91u32, 0x4a15f74bu32, 0x4a20a2f5u32, 0x4a2ddb56u32, 0x4a32ca61u32, 0x4a34c2f9u32, 0x4a3658aeu32, 0x4a3c3135u32, - 0x4a435050u32, 0x4a44686eu32, 0x4a467cc2u32, 0x4a49a763u32, 0x4a4ab9c9u32, 0x4a52149bu32, 0x4a596868u32, 0x4a5de5bdu32, - 0x4a60b8a6u32, 0x4a65220du32, 0x4a66df9eu32, 0x4a6d5dddu32, 0x4a769704u32, 0x4a7b74e7u32, 0x4a7b88d5u32, 0x4a7d3c17u32, - 0x4a86ff59u32, 0x4a8fc6b4u32, 0x4a9a82aau32, 0x4aa1c30du32, 0x4aa3b206u32, 0x4aa5d438u32, 0x4aac9aebu32, 0x4ab2b3c3u32, - 0x4ab30739u32, 0x4ab6d5d4u32, 0x4ab73c72u32, 0x4aba3dbbu32, 0x4ac5e585u32, 0x4acbedcfu32, 0x4acde959u32, 0x4ace3b40u32, - 0x4acfcd0eu32, 0x4ad0ec05u32, 0x4ad587dcu32, 0x4ad6154eu32, 0x4ad75cbeu32, 0x4ad7b57eu32, 0x4adaa0c3u32, 0x4ae29143u32, - 0x4ae2c450u32, 0x4ae89814u32, 0x4aeeed11u32, 0x4af5600cu32, 0x4afd9bb0u32, 0x4b0b9896u32, 0x4b1b997bu32, 0x4b1ba1cdu32, - 0x4b202774u32, 0x4b2e6034u32, 0x4b2f8604u32, 0x4b331d53u32, 0x4b37d383u32, 0x4b395213u32, 0x4b395d41u32, 0x4b3aa898u32, - 0x4b3b5329u32, 0x4b489926u32, 0x4b48b0b9u32, 0x4b54aa1du32, 0x4b554f17u32, 0x4b5a27f2u32, 0x4b69579eu32, 0x4b6a477au32, - 0x4b6e0c6bu32, 0x4b73e019u32, 0x4b7ed637u32, 0x4b7ee578u32, 0x4b7f5cd0u32, 0x4b8707ccu32, 0x4b90189bu32, 0x4b92fa41u32, - 0x4b9cca63u32, 0x4b9e68eeu32, 0x4b9fab47u32, 0x4bb00b2fu32, 0x4bb12fbdu32, 0x4bb1ac75u32, 0x4bbb7303u32, 0x4bc40183u32, - 0x4bc6e04du32, 0x4bcbcb2fu32, 0x4bd2fc5cu32, 0x4bd6e295u32, 0x4bdbf9e2u32, 0x4be05a0du32, 0x4beb472du32, 0x4beb759cu32, - 0x4bf4c656u32, 0x4bf56cb3u32, 0x4bfddc10u32, 0x4c01e149u32, 0x4c027bd7u32, 0x4c07a204u32, 0x4c116f6bu32, 0x4c117231u32, - 0x4c176855u32, 0x4c224363u32, 0x4c37cef9u32, 0x4c43a817u32, 0x4c47484eu32, 0x4c4f4972u32, 0x4c54dd77u32, 0x4c55c18fu32, - 0x4c5a55c0u32, 0x4c5d249du32, 0x4c6321b0u32, 0x4c655175u32, 0x4c670a4eu32, 0x4c690ec0u32, 0x4c743404u32, 0x4c7a0400u32, - 0x4c7e8ab4u32, 0x4c8ed913u32, 0x4c913058u32, 0x4c9bab6cu32, 0x4ca0ee01u32, 0x4cb1f491u32, 0x4cb386a1u32, 0x4cb93346u32, - 0x4cbbe131u32, 0x4cbdf1c8u32, 0x4cc247f3u32, 0x4cc4a46eu32, 0x4cc50bb8u32, 0x4cc72767u32, 0x4ccb2e31u32, 0x4cd94adfu32, - 0x4ce12483u32, 0x4ce14c03u32, 0x4cea9828u32, 0x4ceb71d4u32, 0x4cec089au32, 0x4cecb69du32, 0x4cf355d9u32, 0x4cf5db98u32, - 0x4cf7d7b8u32, 0x4d037a9eu32, 0x4d0a920eu32, 0x4d0aeab3u32, 0x4d0b1fbbu32, 0x4d0d3399u32, 0x4d117cc8u32, 0x4d125ac8u32, - 0x4d15b063u32, 0x4d22e06cu32, 0x4d2916a5u32, 0x4d2ef770u32, 0x4d336bf2u32, 0x4d343994u32, 0x4d3efb46u32, 0x4d430455u32, - 0x4d460e8du32, 0x4d466b5bu32, 0x4d4ddb70u32, 0x4d5049c7u32, 0x4d54503bu32, 0x4d549d01u32, 0x4d58a457u32, 0x4d6133efu32, - 0x4d633172u32, 0x4d65f7fdu32, 0x4d671091u32, 0x4d6916bcu32, 0x4d6b87d9u32, 0x4d709c65u32, 0x4d722133u32, 0x4d73311fu32, - 0x4d7c7946u32, 0x4d7e0f7fu32, 0x4d7f131bu32, 0x4d83ed2bu32, 0x4d901b74u32, 0x4d98b50fu32, 0x4da0cdceu32, 0x4da684f7u32, - 0x4da73bf8u32, 0x4db27f22u32, 0x4db5d35au32, 0x4db90583u32, 0x4dc1b03bu32, 0x4dc6f4b4u32, 0x4dc70f38u32, -]; -#[rustfmt::skip] -pub static UCRT_COSF_VALUES: [u32; 703] = [ - 0x3bf1b213u32, 0x3be2c52cu32, 0x3bc32c57u32, 0x3bba2961u32, 0x3b269b39u32, 0xbb7d1c93u32, 0xbbb9ea1du32, 0xbbd94af6u32, - 0xbbea9cdbu32, 0xbbf2aacdu32, 0xbbf81bc3u32, 0xbbf8a3c2u32, 0xbbfc4bbbu32, 0xbf7ffe0au32, 0xbf7ffe16u32, 0xbf7ffe1au32, - 0xbf7ffe24u32, 0xbf7ffe28u32, 0xbf7ffe2fu32, 0xbf7ffe35u32, 0xbf7ffe8eu32, 0xbf7ffe9fu32, 0xbf7ffec0u32, 0xbf7ffeccu32, - 0xbf7ffee9u32, 0xbf7ffef1u32, 0xbf7ffef2u32, 0xbf7fff1cu32, 0xbf7fff47u32, 0xbf7ffefeu32, 0xbf7ffeefu32, 0xbf7ffeecu32, - 0xbf7ffeb1u32, 0xbf7ffe6fu32, 0xbf7ffe69u32, 0xbf7ffe4bu32, 0xbf7ffe45u32, 0xbf7ffe42u32, 0xbf7ffe34u32, 0xbf7ffe29u32, - 0xbf7ffe15u32, 0xbf7ffe07u32, 0xbbf0b759u32, 0x3bf76f80u32, 0x3f7ffe3au32, 0x3f7ffe3bu32, 0x3f7ffe3cu32, 0x3f7ffe68u32, - 0x3f7ffe8bu32, 0x3f7ffe9bu32, 0x3f7ffedcu32, 0x3f7ffee5u32, 0x3f7fff0eu32, 0x3f7fff78u32, 0x3f7fff46u32, 0x3f7fff2eu32, - 0x3f7fff0fu32, 0x3f7ffea6u32, 0x3f7ffe8au32, 0x3f7ffe87u32, 0x3f7ffe0fu32, 0x3bb8c0ebu32, 0xbbecaa4fu32, 0xbbf52240u32, - 0xbbf6c23du32, 0xbbf9f237u32, 0xbf7ffe1cu32, 0xbf7ffe98u32, 0xbbcd9a17u32, 0x3bf3e4feu32, 0x3f7ffea1u32, 0x3f7ffeb4u32, - 0x3f7fff14u32, 0x3f7ffe6eu32, 0xbf7ffe0eu32, 0xbf7ffe2cu32, 0xbf7ffe54u32, 0xbf7ffea3u32, 0xbf7ffec1u32, 0xbf7fffe6u32, - 0xbf7ffe77u32, 0xbbf64460u32, 0x3f7ffe32u32, 0x3f7ffe63u32, 0xbf7ffe25u32, 0xbf7ffe60u32, 0xbf7fff07u32, 0xbf7fff35u32, - 0xbf7fff13u32, 0xbf7ffef3u32, 0xbf7ffe6cu32, 0xbf7ffe0du32, 0xbbd22f22u32, 0x3bd7501bu32, 0x3f7ffe4du32, 0xbf7ffe05u32, - 0xbf7ffe2du32, 0xbf7ffe4cu32, 0xbf7ffed4u32, 0xbf7fff33u32, 0xbf7fff3du32, 0xbf7ffe84u32, 0x3f7ffe4bu32, 0x3f7ffe4eu32, - 0x3f7ffe78u32, 0x3f7fff0bu32, 0xbf7ffe4au32, 0xbf7ffeadu32, 0xbf7ffe30u32, 0x3f7ffe06u32, 0x3f7fff3bu32, 0x3bad19a1u32, - 0xbf7ffee4u32, 0xbf7ffea7u32, 0x3f7ffeb0u32, 0x3f7ffe12u32, 0xbf7fff0au32, 0xbf7ffe65u32, 0x3f7ffed5u32, 0xbf7ffe1bu32, - 0xbf7ffe50u32, 0xbf7ffeedu32, 0x3f7ffe07u32, 0x3f7fff15u32, 0x3bcbf919u32, 0xbf7ffe48u32, 0xbf7fff4du32, 0x3be970ccu32, - 0x3bf570b7u32, 0x3bf930b0u32, 0x3f7ffe76u32, 0xbf7fff6cu32, 0xbbf7f8a1u32, 0x3ae91b0au32, 0x3f7ffeb9u32, 0x3f7ffe36u32, - 0x3bda589fu32, 0xbf7ffeb7u32, 0xbf7ffe34u32, 0xbf7ffeccu32, 0xbf7ffe9fu32, 0xbf7ffe24u32, 0x3f7ffe21u32, 0x3f7ffe37u32, - 0x3f7ffe11u32, 0xbf7ffe09u32, 0xbf7ffe3eu32, 0x3f7ffeabu32, 0xbf7ffe64u32, 0x3f7ffea8u32, 0x3f7ffeb5u32, 0xbf7ffed3u32, - 0xbf7fff2au32, 0xbf7ffe5au32, 0x3f7ffe96u32, 0x3f7fff0cu32, 0xbf7ffe7fu32, 0x3f7ffe15u32, 0x3f7ffe28u32, 0xbf7ffe26u32, - 0x3f7ffe7au32, 0x3f7ffec7u32, 0xbf7ffe18u32, 0x3f7ffef9u32, 0xbbcf367bu32, 0x3f7ffe3bu32, 0x3f7ffe1eu32, 0x3f7ffea6u32, - 0xbf7ffe4fu32, 0x3bdbb415u32, 0xbf7ffe78u32, 0x3f7ffe42u32, 0xbf7ffe1du32, 0xbf7ffe32u32, 0x3f7ffe0eu32, 0xbf7ffe6au32, - 0xbbed751au32, 0x3f7ffeb1u32, 0xbf7fffa1u32, 0xbbffca09u32, 0xbf7fff9du32, 0x3f7fff8eu32, 0x3f7fff6eu32, 0x3f7ffe60u32, - 0x3f7fff1fu32, 0x3f7ffe79u32, 0xbbcb2b70u32, 0xbf7ffe9du32, 0xbf7ffed1u32, 0xbbca5660u32, 0x3f7ffe2eu32, 0xbf7ffef7u32, - 0x3f7fffd4u32, 0xbf7ffe77u32, 0xbf7ffe45u32, 0xbf7ffea3u32, 0x3f7ffe64u32, 0xbf7ffe59u32, 0x3f7ffe08u32, 0x3f7fff68u32, - 0x3f7ffe48u32, 0x3f7fff3fu32, 0x3f7ffe06u32, 0x3f7ffe4cu32, 0x3f7ffe01u32, 0xbf7ffec8u32, 0xbf7ffe94u32, 0xbf7ffe5cu32, - 0xbf7ffe20u32, 0xbf7ffe70u32, 0x3f7fff9eu32, 0xbf7ffeabu32, 0x3f7ffe1cu32, 0x3f7ffe26u32, 0xbf7ffe7au32, 0x3f7ffeb8u32, - 0xbf7ffe1eu32, 0xbf7ffe0eu32, 0xbf7ffe8eu32, 0xbf7ffe84u32, 0xbbee9406u32, 0xbf7ffe5eu32, 0x3f7ffe45u32, 0xbbf8cd6eu32, - 0xbf7ffe08u32, 0xbf7ffecbu32, 0x3f7ffe20u32, 0xbf7ffe04u32, 0x3f7ffedeu32, 0x3f7fff69u32, 0xbbe5f836u32, 0xbf7ffe18u32, - 0x3f7ffe19u32, 0xbbfc1afdu32, 0xbf7ffe4au32, 0x3f7ffe69u32, 0x3f7ffe88u32, 0xbf7ffe89u32, 0x3f7ffe8au32, 0xbf7ffee8u32, - 0x3f7ffe38u32, 0xbf7ffefcu32, 0x3f7fff14u32, 0x3f7ffeb2u32, 0x3f7ffe6bu32, 0xbbe1a23bu32, 0x3f7ffe0fu32, 0xbf7ffe66u32, - 0x3f7fff39u32, 0xbf7ffe3cu32, 0x3f7ffec5u32, 0xbf7ffea0u32, 0xbf7ffe4eu32, 0xbf7fff4au32, 0xbf7ffeaeu32, 0xbf7ffeafu32, - 0xbf7ffe50u32, 0xbf7ffe03u32, 0x3f7fff18u32, 0x3f7ffed7u32, 0x3f7ffe9cu32, 0xbf7ffe7cu32, 0xbf7ffe92u32, 0x3f7ffe2bu32, - 0x3f7ffe9eu32, 0x3f7fff01u32, 0xbf7fff3eu32, 0x3f7ffe55u32, 0xbbe0e65bu32, 0xbbf76354u32, 0x3f7ffe29u32, 0xbf7ffe13u32, - 0x3f7ffe40u32, 0x3f7ffe3du32, 0x3f7ffe11u32, 0x3f7ffe56u32, 0xbf7ffe1du32, 0x3f7ffe24u32, 0x3f7ffea7u32, 0x3f7ffe05u32, - 0x3f7ffe21u32, 0x3f7ffe41u32, 0x3f7fff49u32, 0x3bb4d7f5u32, 0x3f7ffe0cu32, 0xbf7ffedfu32, 0xbf7fff60u32, 0xbf7ffe1fu32, - 0xbf7ffe2au32, 0xbf7ffe8cu32, 0xbf7ffe86u32, 0x3f7ffec3u32, 0xbf7ffe53u32, 0x3f7ffea5u32, 0xbf7ffea2u32, 0x3f7ffe33u32, - 0x3f7fff8bu32, 0x3f7fff13u32, 0x3f7ffe37u32, 0x3f7ffeddu32, 0xbf7ffeb7u32, 0x3bb0349cu32, 0xbf7ffe2du32, 0xbf7ffebdu32, - 0xbf7ffe1cu32, 0x3f7ffecbu32, 0xbf7ffeb8u32, 0xbf7ffe26u32, 0x3f7ffe70u32, 0x3be81839u32, 0xbf7ffe01u32, 0xbba73806u32, - 0x3f7ffef4u32, 0xbf7ffe34u32, 0x3bd404d3u32, 0xbf7fff59u32, 0xbf7ffe28u32, 0x3f7ffe99u32, 0x3f7ffed2u32, 0xbf7ffe82u32, - 0x3bc42ac1u32, 0x3f7ffe85u32, 0x3f7ffe8du32, 0x3f7ffe02u32, 0xbf7ffe62u32, 0xbf7ffe68u32, 0xbbd6bde5u32, 0xbf7ffeeeu32, - 0x3f7ffe7eu32, 0x3bc67c6du32, 0x3f7ffe63u32, 0x3f7ffeadu32, 0x3f7ffe2cu32, 0x3f7fff54u32, 0x3f7fff16u32, 0x3f7fff00u32, - 0x3f7ffe80u32, 0x3f7ffef3u32, 0xbf7fff30u32, 0xbf7fff63u32, 0x3f7ffe3au32, 0x3f7ffe23u32, 0x3f7ffe76u32, 0x3f7ffe5eu32, - 0xbf7ffe72u32, 0x3f7fff0fu32, 0xbf7ffe51u32, 0xbf7ffea9u32, 0xbf7fff0eu32, 0xbf7ffe50u32, 0x3f7ffe2eu32, 0x3f7ffec8u32, - 0xbbfa9a48u32, 0x3f7ffe0au32, 0x3f7ffe8au32, 0xbf7ffe4eu32, 0xbf7ffe10u32, 0x3f7ffee4u32, 0xbf7ffe47u32, 0xbf7ffe16u32, - 0xbf7ffe9bu32, 0xbf7ffe4bu32, 0x3f7ffe71u32, 0xbf7fff0au32, 0xbf7fff09u32, 0xbbe1baa8u32, 0xbbe74b03u32, 0xbf7ffe30u32, - 0x3f7ffe5fu32, 0xbf7fff1eu32, 0xbf7ffe3fu32, 0x3f7ffeb9u32, 0xbbce11deu32, 0x3f7ffe2du32, 0xbf7ffe11u32, 0xbf7ffe3bu32, - 0x3f7ffea0u32, 0x3f7ffe56u32, 0xbf7ffecbu32, 0x3f7ffeb8u32, 0xbbb9cb3au32, 0x3f7fff53u32, 0x3f7ffe7au32, 0x3f7ffe36u32, - 0xbf7ffebeu32, 0xbf7ffe1au32, 0xbf7ffe07u32, 0xbf7ffe9cu32, 0x3f7ffe3au32, 0x3b9a6edeu32, 0xbf7ffe55u32, 0x3f7ffec4u32, - 0xbba69ae3u32, 0x3f7ffe17u32, 0xbf7ffe1cu32, 0x3f7ffe05u32, 0xbf7ffe1du32, 0xbf7ffe46u32, 0x3f7fff4du32, 0x3f7ffe60u32, - 0xbf7ffe40u32, 0xbf7ffe82u32, 0x3be99d43u32, 0x3f7fff43u32, 0xbf7ffe12u32, 0x3f7ffe57u32, 0xbf7ffe08u32, 0x3f7ffeeau32, - 0x3f7ffe88u32, 0xbf7ffe19u32, 0xbf7ffe65u32, 0x3f7fff1bu32, 0xbf7ffef0u32, 0xbf7ffe6cu32, 0x3f7ffee3u32, 0x3f7ffea8u32, - 0xbf7ffefbu32, 0xbbd1fd08u32, 0x3be1b545u32, 0x3f7ffec3u32, 0x3f7ffe3du32, 0xbbebde62u32, 0x3f7ffe4au32, 0xbf7ffe6bu32, - 0x3f7ffe1eu32, 0x3f7ffe92u32, 0xbf7ffe5du32, 0xbf7fffdeu32, 0x3f7ffe73u32, 0x3f7ffe84u32, 0x3f7ffe0du32, 0xbf7ffe59u32, - 0x3bfd45a1u32, 0xbf7ffe0cu32, 0xbf7ffe04u32, 0xbf7fff87u32, 0xbbc2feb3u32, 0x3f7ffe0bu32, 0x3f7ffea1u32, 0xbf7ffe7du32, - 0xbb6ec74au32, 0xbf7ffe54u32, 0x3f7ffe33u32, 0x3f7ffe21u32, 0x3f7ffea5u32, 0x3f7ffe35u32, 0xbf7ffefcu32, 0xbf7ffe89u32, - 0x3bcc1790u32, 0xbf7ffe95u32, 0x3f7ffeabu32, 0xbf7ffe49u32, 0xbf7ffe3du32, 0xbf7ffec3u32, 0x3be1cad1u32, 0x3f7ffe19u32, - 0xbf7fff1fu32, 0xbbabe180u32, 0xbf7fff2fu32, 0xbf7ffeb8u32, 0x3f7fff0eu32, 0xbbf6e3aau32, 0xbf7ffe02u32, 0x3f7fff5fu32, - 0x3f7ffe10u32, 0x3f7ffe9bu32, 0x3f7fff1cu32, 0x3f7fff4cu32, 0xbf7ffe17u32, 0x3f7ffe1cu32, 0xbbf6088cu32, 0xbf7ffe05u32, - 0x3bcce7abu32, 0x3f7ffe97u32, 0x3f7ffe08u32, 0x3b7bc4cbu32, 0xbf7ffec3u32, 0x3be19fb9u32, 0x3f7fff07u32, 0x3f7fff27u32, - 0x3f7ffef7u32, 0xbf7ffe1bu32, 0xbf7fff12u32, 0x3f7ffe9eu32, 0x3f7ffe8au32, 0xbf7ffe13u32, 0xbf7ffe2au32, 0x3f7ffe92u32, - 0xbf7ffe6cu32, 0xbf7ffe12u32, 0xbf7ffe5cu32, 0xbf7ffe01u32, 0xbf7fffd5u32, 0xbf7ffe06u32, 0xbf7ffeecu32, 0xbf7ffe64u32, - 0x3f7ffefeu32, 0x3f7ffe6fu32, 0x3bb51a74u32, 0xbf7ffe5du32, 0x3f7ffe84u32, 0xbf7ffee2u32, 0x3b7126f7u32, 0x3f7ffe83u32, - 0xbf7ffe95u32, 0xbf7ffe5fu32, 0x3f7ffe5au32, 0x3f7ffebdu32, 0x3f7ffe1cu32, 0x3f7ffe68u32, 0xbf7ffe1fu32, 0xbf7ffef8u32, - 0xbf7ffe66u32, 0xbf7ffed6u32, 0x3f7fff5au32, 0x3f7ffe3au32, 0xbbf8a06eu32, 0x3f7ffedcu32, 0xbf7ffe38u32, 0x3f7ffe36u32, - 0x3f7ffebau32, 0xbbfe4f9eu32, 0xbf7ffebbu32, 0xbf7fff3bu32, 0xbf7ffe04u32, 0xbbd1d175u32, 0xbf7ffe45u32, 0xbf7ffe14u32, - 0x3f7ffe3eu32, 0x3f7ffea4u32, 0xbf7ffe61u32, 0x3f7fff2eu32, 0xbf7ffe7fu32, 0x3f7ffeadu32, 0x3f7ffe63u32, 0xbf7fff32u32, - 0x3ba0cbc1u32, 0x3f7ffe62u32, 0x3f7fff8du32, 0x3f7ffef4u32, 0xbf7fff47u32, 0xbf7ffe1au32, 0xbf7ffe7bu32, 0xbbebc031u32, - 0x3f7ffe67u32, 0xbf7fff65u32, 0x3f7fff0au32, 0xbb8059f5u32, 0x3f7ffeaau32, 0x3f7fffa1u32, 0xbf7fffdau32, 0x3f7ffe2eu32, - 0xbf7ffedfu32, 0xbf7ffe50u32, 0x3f7ffe18u32, 0xbf7ffec9u32, 0x3f7ffeb2u32, 0x3f7ffe44u32, 0xbbd6b02bu32, 0x3f7ffe9cu32, - 0x3f7ffebeu32, 0x3f7ffe11u32, 0xbf7fff02u32, 0x3f7ffe26u32, 0xbf7ffe9au32, 0xbf7ffe6eu32, 0x3f7ffe2au32, 0x3f7ffe13u32, - 0x3f7ffeb5u32, 0xbf7ffee8u32, 0xbf7fff00u32, 0xbf7ffef9u32, 0x3f7fff17u32, 0x3f7fff73u32, 0xbf7fff30u32, 0xbf7fffa3u32, - 0x3f7ffe60u32, 0xbf7ffe1eu32, 0x3f7ffe5du32, 0x3bdf16b1u32, 0x3f7ffe16u32, 0x3f7ffe4bu32, 0x3f7ffe9fu32, 0x3bee9390u32, - 0x3f7ffec5u32, 0xbf7ffef1u32, 0x3f7ffe8fu32, 0xbf7ffeb3u32, 0x3f7fff23u32, 0xbf7ffe43u32, 0x3f7ffe76u32, 0x3f7ffe27u32, - 0x3f7ffe52u32, 0x3f7ffe78u32, 0x3bc7e4d7u32, 0xbf7fff95u32, 0x3f7ffe2fu32, 0x3f7fff3du32, 0xbf7ffe90u32, 0xbf7fff63u32, - 0xbf7ffe04u32, 0xbf7ffec3u32, 0x3f7ffeebu32, 0xbf7ffe93u32, 0x3f7ffee5u32, 0xbf7ffe31u32, 0xbbad81d7u32, 0xbf7ffe0fu32, - 0xbf7ffe2bu32, 0x3f7ffee0u32, 0x3bdf9cc7u32, 0x3f7ffec6u32, 0xbf7ffe67u32, 0x3bc00afdu32, 0xbbc5a127u32, 0xbbbdba3cu32, - 0x3f7ffe30u32, 0xbf7ffe22u32, 0xbbf39e21u32, 0xbf7ffe1du32, 0x3f7ffe9bu32, 0x3f7fff11u32, 0x3f7fff29u32, 0xbf7ffedbu32, - 0x3f7ffe4du32, 0xbf7ffe8eu32, 0x3beb8c23u32, 0x3f7ffe4fu32, 0x3f7ffe1fu32, 0xbf7ffe6fu32, 0xbf7ffe92u32, 0x3bb5b961u32, - 0xbf7ffe97u32, 0x3f7fff03u32, 0xbf7fff12u32, 0x3f7ffe07u32, 0xbf7fff20u32, 0x3f7ffe80u32, 0x3f7ffe16u32, 0xbf7ffecfu32, - 0x3f7ffe32u32, 0xbf7fff3bu32, 0x3f7ffef7u32, 0x3f7ffe88u32, 0x3f7ffea5u32, 0xbf7ffe06u32, 0xbf7ffe14u32, 0x3f7ffe0cu32, - 0x3f7fff05u32, 0x3f7fff10u32, 0xbf7ffe39u32, 0xbf7fff36u32, 0x3f7fff1au32, 0x3f7ffe3fu32, 0xbf7ffe1bu32, 0x3f7ffe3cu32, - 0x3f7ffe4cu32, 0x3f7ffe20u32, 0xbbcca980u32, 0xbf7ffec8u32, 0xbf7ffe77u32, 0x3f7ffed3u32, 0xbf7ffe5du32, 0x3f7fff4cu32, - 0xbba61100u32, 0x3f7ffe87u32, 0x3f7ffe96u32, 0xbf7fff13u32, 0x3f7ffe74u32, 0x3be96970u32, 0x3f7fff70u32, 0xbf7ffe12u32, - 0x3be2bf79u32, 0xbf7ffef8u32, 0x3f7ffe5cu32, 0xbf7ffee3u32, 0xbf7ffe2fu32, 0x3f7ffe42u32, 0xbf7ffeb6u32, 0xbf7ffe2du32, - 0xbf7ffe11u32, 0xbf7ffe62u32, 0x3f7ffe45u32, 0x3f7ffe4eu32, 0xbf7ffe8au32, 0x3bb6e58eu32, 0xbf7ffe26u32, 0x3f7ffe34u32, - 0xbf7ffe76u32, 0xbf7ffe52u32, 0xbf7fff0fu32, 0x3f7ffeb1u32, 0xbbc3b4fcu32, 0x3f7ffe03u32, 0xbbc8dd97u32, 0x3f7ffe49u32, - 0x3f7ffe5fu32, 0xbf7ffe3au32, 0x3f7ffe47u32, 0x3f7ffe10u32, 0xbf7fff47u32, 0xbf7fff4au32, 0xbf7fff0bu32, -]; -#[rustfmt::skip] -pub static UCRT_COSF_WIDE_KEYS: [u32; 2424] = [ - 0x4dcb1780u32, 0x4dd37664u32, 0x4ddd87d9u32, 0x4ddf3733u32, 0x4ddfd138u32, 0x4de53839u32, 0x4de74b9eu32, 0x4dea010cu32, - 0x4df1d86bu32, 0x4df67a3fu32, 0x4df9c298u32, 0x4dff4a70u32, 0x4e0828cau32, 0x4e0855ceu32, 0x4e09c5a4u32, 0x4e0b4ceau32, - 0x4e0c2583u32, 0x4e0fd978u32, 0x4e13e59bu32, 0x4e14f0edu32, 0x4e175995u32, 0x4e1a4e87u32, 0x4e24bc2eu32, 0x4e2b66c8u32, - 0x4e2f6dadu32, 0x4e311128u32, 0x4e3e636bu32, 0x4e3f17c9u32, 0x4e3f58b0u32, 0x4e425443u32, 0x4e468f94u32, 0x4e48b37au32, - 0x4e49c881u32, 0x4e4a9fb9u32, 0x4e4aca1eu32, 0x4e4d41b9u32, 0x4e599728u32, 0x4e5c14a1u32, 0x4e5c21e3u32, 0x4e664802u32, - 0x4e67b627u32, 0x4e75dc15u32, 0x4e7871c1u32, 0x4e870fe8u32, 0x4e8c28e7u32, 0x4e958d3fu32, 0x4e971397u32, 0x4e9a01e8u32, - 0x4e9c505au32, 0x4e9cdf43u32, 0x4ea19772u32, 0x4eaee314u32, 0x4eaf17ceu32, 0x4eb1a978u32, 0x4eb58359u32, 0x4ebaf660u32, - 0x4ebf2ce8u32, 0x4ec13e00u32, 0x4ec26675u32, 0x4ec2b3d9u32, 0x4ec49a3cu32, 0x4ec70ec3u32, 0x4ecb2828u32, 0x4ed09b2fu32, - 0x4ed53b52u32, 0x4ed8af4cu32, 0x4edf4d67u32, 0x4ee6fd27u32, 0x4ee7992bu32, 0x4eee6d3cu32, 0x4ef521d9u32, 0x4effaacbu32, - 0x4f085ae5u32, 0x4f0aa507u32, 0x4f0da15fu32, 0x4f152068u32, 0x4f1b1f43u32, 0x4f30c5ecu32, 0x4f33f812u32, 0x4f352777u32, - 0x4f37c527u32, 0x4f3b2b41u32, 0x4f3c63e6u32, 0x4f4ada75u32, 0x4f4af347u32, 0x4f4e477fu32, 0x4f53fb95u32, 0x4f5848eeu32, - 0x4f59e329u32, 0x4f629887u32, 0x4f6b1127u32, 0x4f6ec7bau32, 0x4f6f6c60u32, 0x4f6fa994u32, 0x4f704b24u32, 0x4f74ae89u32, - 0x4f772db3u32, 0x4f78b432u32, 0x4f82660fu32, 0x4f8a963au32, 0x4f975141u32, 0x4f98a758u32, 0x4f9be9fdu32, 0x4fa2966fu32, - 0x4fa393a6u32, 0x4fadc355u32, 0x4fb2be31u32, 0x4fbe37f1u32, 0x4fbef1ddu32, 0x4fbfa4d9u32, 0x4fc35642u32, 0x4fd484cau32, - 0x4fd5ba81u32, 0x4fda9761u32, 0x4fdaaf95u32, 0x4ff03c7fu32, 0x4ff9a226u32, 0x4ffa4d94u32, 0x4ffff569u32, 0x5001b43bu32, - 0x500609fbu32, 0x5009df81u32, 0x500dd2c8u32, 0x50161eb3u32, 0x50183b1fu32, 0x501b1fe1u32, 0x5025028fu32, 0x502fdee8u32, - 0x5035d65du32, 0x503d5e43u32, 0x5045ade1u32, 0x5051ae23u32, 0x5053f019u32, 0x50546959u32, 0x505cd9e1u32, 0x50604862u32, - 0x5060a680u32, 0x5062a7cau32, 0x506863c7u32, 0x506bb51bu32, 0x507a6749u32, 0x5088e1c0u32, 0x508db39fu32, 0x50a215b0u32, - 0x50ad49d0u32, 0x50bf60fau32, 0x50bfff7au32, 0x50c4f160u32, 0x50d5b70eu32, 0x50d67d19u32, 0x50d77dbeu32, 0x50df1864u32, - 0x50e4e971u32, 0x50efe18au32, 0x510f53e7u32, 0x5110d394u32, 0x511d0af3u32, 0x51257eaeu32, 0x5129c032u32, 0x5129f26fu32, - 0x512d3caeu32, 0x5136fd6au32, 0x513c5e52u32, 0x51475d31u32, 0x514a560au32, 0x514b75ddu32, 0x514fe039u32, 0x515ac58du32, - 0x515c121fu32, 0x5162bf74u32, 0x5168595fu32, 0x516b99aau32, 0x517a8d67u32, 0x517c0796u32, 0x517f1075u32, 0x518acfc6u32, - 0x518d996bu32, 0x51902964u32, 0x5191c977u32, 0x5193b508u32, 0x51999e33u32, 0x51a079b9u32, 0x51a1f3c3u32, 0x51a53e3cu32, - 0x51a6ed77u32, 0x51a79195u32, 0x51ad9132u32, 0x51b18013u32, 0x51b5a2d8u32, 0x51b76341u32, 0x51bd51d5u32, 0x51bf5706u32, - 0x51c416e9u32, 0x51d17c7au32, 0x51e236c2u32, 0x51e4c074u32, 0x51ed07dfu32, 0x51f3c4f0u32, 0x51f4e454u32, 0x51f7b07eu32, - 0x5200266eu32, 0x5207e390u32, 0x52100bcdu32, 0x5210f625u32, 0x52177badu32, 0x521ba286u32, 0x521d50beu32, 0x521e86deu32, - 0x521ead7bu32, 0x521ec359u32, 0x52226ee4u32, 0x5222ee21u32, 0x52230c71u32, 0x52238e48u32, 0x522d2089u32, 0x522eba30u32, - 0x52309d2au32, 0x523382aau32, 0x5236c2f5u32, 0x5242e0eeu32, 0x52467c04u32, 0x5248782fu32, 0x52489635u32, 0x524c523du32, - 0x52565af7u32, 0x5260e92du32, 0x526512cdu32, 0x526a4df6u32, 0x526dc303u32, 0x52714ef1u32, 0x527d75cbu32, 0x527e3a89u32, - 0x5282d23au32, 0x528849deu32, 0x528ac235u32, 0x528f5c9fu32, 0x52942792u32, 0x529966f4u32, 0x52a92543u32, 0x52ab9057u32, - 0x52b01556u32, 0x52b077b5u32, 0x52b72d32u32, 0x52b828bcu32, 0x52c3f5f7u32, 0x52c6a2c6u32, 0x52c8b460u32, 0x52ce9563u32, - 0x52da6025u32, 0x52ea3f18u32, 0x52eb265fu32, 0x52ec4b97u32, 0x52ed832du32, 0x52f0bbbfu32, 0x52f0d30fu32, 0x52f1a32bu32, - 0x52f74fb6u32, 0x52f87309u32, 0x52fbb71eu32, 0x52fd78b7u32, 0x5304763cu32, 0x530f438fu32, 0x5313e028u32, 0x5315b6e2u32, - 0x531dca6du32, 0x53256d07u32, 0x5329db48u32, 0x5329e4c1u32, 0x532ad4a3u32, 0x532b3accu32, 0x532c2d27u32, 0x5334d7aeu32, - 0x5338802cu32, 0x53399457u32, 0x5339c38fu32, 0x533bfca8u32, 0x533c0dffu32, 0x533d640au32, 0x533e7a64u32, 0x53450b98u32, - 0x534943a3u32, 0x53503965u32, 0x53598caeu32, 0x535dec82u32, 0x535fa122u32, 0x535fb643u32, 0x5368c11fu32, 0x5369b929u32, - 0x536a0b80u32, 0x53769be0u32, 0x53797b67u32, 0x537ab940u32, 0x53880514u32, 0x5388777bu32, 0x539cb61du32, 0x53a367d0u32, - 0x53a4460fu32, 0x53a57b74u32, 0x53b6c673u32, 0x53c5cbacu32, 0x53cf448fu32, 0x53cf63e4u32, 0x53d6c57eu32, 0x53de0aadu32, - 0x53dec7b2u32, 0x53fb7874u32, 0x53fc4d15u32, 0x540003c0u32, 0x54036c1fu32, 0x541214cdu32, 0x5414a5a1u32, 0x5418301au32, - 0x541c1ed7u32, 0x541f8c05u32, 0x54208b21u32, 0x5424571cu32, 0x542efdabu32, 0x543776cau32, 0x544296e4u32, 0x5445b410u32, - 0x5445b5f5u32, 0x54475b0fu32, 0x54563eb0u32, 0x545a4943u32, 0x5460878au32, 0x54740bb4u32, 0x5477b4b5u32, 0x547c7caau32, - 0x547e9610u32, 0x5485d84cu32, 0x548653f4u32, 0x54906924u32, 0x54927911u32, 0x5495fb16u32, 0x549d98d7u32, 0x54a49bf8u32, - 0x54a4a78eu32, 0x54a4bcd5u32, 0x54a6f56bu32, 0x54ac7972u32, 0x54b69aa8u32, 0x54bd4b1eu32, 0x54c3b986u32, 0x54c45fa0u32, - 0x54caa6f0u32, 0x54d77862u32, 0x54d7ee5bu32, 0x54e0f949u32, 0x54ee3808u32, 0x54ee41b9u32, 0x54f0a8a7u32, 0x54f424e1u32, - 0x54f60807u32, 0x54fd9c17u32, 0x55007df3u32, 0x55028da8u32, 0x5503d055u32, 0x55079a25u32, 0x550eb2c5u32, 0x5515ff18u32, - 0x551657feu32, 0x551a7ec4u32, 0x5529d72fu32, 0x552cf91cu32, 0x552e0020u32, 0x552fc44eu32, 0x553290f5u32, 0x5533a775u32, - 0x55375293u32, 0x553cac98u32, 0x555326d6u32, 0x5555bfcau32, 0x555c3d92u32, 0x555d6cb3u32, 0x556a6093u32, 0x557e2f3fu32, - 0x55800a17u32, 0x5586d157u32, 0x55953821u32, 0x55a8caa6u32, 0x55ba746bu32, 0x55c015eeu32, 0x55c794beu32, 0x55cbdc3eu32, - 0x55cc7507u32, 0x55cf4fddu32, 0x55e172d4u32, 0x55f2f1dau32, 0x55f87094u32, 0x55fb2856u32, 0x5604c5abu32, 0x5608d166u32, - 0x560a4836u32, 0x56123726u32, 0x5612884eu32, 0x5614f675u32, 0x56194ab2u32, 0x561aa0acu32, 0x561d5094u32, 0x561df7b9u32, - 0x562a806au32, 0x562c4d37u32, 0x564019c6u32, 0x564f6d5cu32, 0x565e0962u32, 0x565ed446u32, 0x56637899u32, 0x566f5fe2u32, - 0x5673ef03u32, 0x5676800fu32, 0x567d3208u32, 0x567e1bc8u32, 0x56813f3bu32, 0x56822248u32, 0x56835b59u32, 0x56843f55u32, - 0x56847c33u32, 0x5690b980u32, 0x5694ac97u32, 0x5696baa3u32, 0x56990f3eu32, 0x569927e9u32, 0x569e17a7u32, 0x56a1ddc9u32, - 0x56a2c5b2u32, 0x56a5a7d1u32, 0x56ab75ceu32, 0x56ac21c1u32, 0x56b18b42u32, 0x56b77250u32, 0x56c4a315u32, 0x56ccedbfu32, - 0x56d2157fu32, 0x56d644f9u32, 0x56dd4cefu32, 0x56dd51cbu32, 0x56e1ffcfu32, 0x56edf612u32, 0x56f2401cu32, 0x56f9bfe2u32, - 0x56fb96e2u32, 0x56fe9777u32, 0x5702efdbu32, 0x57097698u32, 0x57127f93u32, 0x57152f00u32, 0x57176c6fu32, 0x5717d448u32, - 0x57187d60u32, 0x57208464u32, 0x572377f2u32, 0x5727a7a6u32, 0x572c1b41u32, 0x572d1822u32, 0x572e65f1u32, 0x5736e0edu32, - 0x573e1d88u32, 0x57492755u32, 0x574b4e46u32, 0x574c3bf3u32, 0x574dd2b8u32, 0x5754c114u32, 0x575e911bu32, 0x5769df3cu32, - 0x576c45bau32, 0x577342cfu32, 0x57773d29u32, 0x577af871u32, 0x577bdee2u32, 0x5787da08u32, 0x57891370u32, 0x5789723eu32, - 0x57898e05u32, 0x578edb3du32, 0x5796f651u32, 0x579f8d4eu32, 0x57a19c57u32, 0x57a32f3du32, 0x57ad2d28u32, 0x57b47656u32, - 0x57b4938eu32, 0x57b4ee60u32, 0x57ba53c1u32, 0x57c5cf11u32, 0x57c9463fu32, 0x57cd3cf4u32, 0x57d64d15u32, 0x57e049acu32, - 0x57e9861cu32, 0x57ea1c5bu32, 0x57ea6606u32, 0x57eb4307u32, 0x57ed3230u32, 0x57ef8f9cu32, 0x57f3b8edu32, 0x57f49250u32, - 0x57fe5494u32, 0x57ff1780u32, 0x58018208u32, 0x58057277u32, 0x580c6e79u32, 0x580ddf8cu32, 0x581065eau32, 0x5812b931u32, - 0x5813e90cu32, 0x58165f1cu32, 0x581d6f68u32, 0x5826426au32, 0x582c6cb5u32, 0x582d1257u32, 0x583c21f0u32, 0x583f8afdu32, - 0x58454b4du32, 0x584e7e54u32, 0x584eb905u32, 0x58503a66u32, 0x58541764u32, 0x58576ecfu32, 0x585b6509u32, 0x585b6963u32, - 0x586806e8u32, 0x58702acdu32, 0x58781df7u32, 0x58782772u32, 0x5878ec8bu32, 0x587a48d9u32, 0x5883c684u32, 0x588db7a2u32, - 0x588e318du32, 0x588f00cbu32, 0x58923e6du32, 0x589b15c9u32, 0x58a2fb27u32, 0x58a6a8e4u32, 0x58a8f05du32, 0x58b44814u32, - 0x58b50130u32, 0x58b56bfbu32, 0x58b6bd1cu32, 0x58b7b2f0u32, 0x58b92205u32, 0x58bb0caeu32, 0x58bea81fu32, 0x58c5656bu32, - 0x58c62e5au32, 0x58d11220u32, 0x58d3c40fu32, 0x58d9e1d9u32, 0x58de4135u32, 0x58dfcee8u32, 0x58f983c0u32, 0x58fa7f39u32, - 0x58fbfaf5u32, 0x58fd4b6cu32, 0x5901fc77u32, 0x5903bb0bu32, 0x590ab6b8u32, 0x5915ed16u32, 0x5917526eu32, 0x5917fc15u32, - 0x592015d5u32, 0x5926c513u32, 0x5932ffeau32, 0x5935e2bau32, 0x59467161u32, 0x59529856u32, 0x595aee9fu32, 0x59659c05u32, - 0x5966dcb2u32, 0x59778bc6u32, 0x5979d9bdu32, 0x597c4339u32, 0x597e0b9du32, 0x5983ddfau32, 0x5986687au32, 0x598b287cu32, - 0x59a06398u32, 0x59b4753bu32, 0x59b9ab94u32, 0x59c84b67u32, 0x59cafa2au32, 0x59cc39b7u32, 0x59d4b9c2u32, 0x59d5235fu32, - 0x59d5257eu32, 0x59d97c38u32, 0x59ec0d79u32, 0x59ecb08fu32, 0x59ed5a36u32, 0x59ee268au32, 0x59f0f09eu32, 0x59f3aafbu32, - 0x59f8f3f5u32, 0x5a042723u32, 0x5a08ab7au32, 0x5a0d65afu32, 0x5a0ef990u32, 0x5a142435u32, 0x5a14f4adu32, 0x5a1b5b86u32, - 0x5a1f2121u32, 0x5a202777u32, 0x5a213b31u32, 0x5a2573cau32, 0x5a2b95b8u32, 0x5a3a391fu32, 0x5a3fdb55u32, 0x5a405b7cu32, - 0x5a43dc9bu32, 0x5a475897u32, 0x5a494077u32, 0x5a4edb6bu32, 0x5a5d18a8u32, 0x5a604354u32, 0x5a637f81u32, 0x5a63f7a2u32, - 0x5a756d99u32, 0x5a76e45fu32, 0x5a7c7668u32, 0x5a85fad3u32, 0x5a8a7878u32, 0x5a8de0eeu32, 0x5a8e3719u32, 0x5a93dd9au32, - 0x5a9f0157u32, 0x5a9f52a7u32, 0x5aa63e20u32, 0x5aa8e200u32, 0x5aab1bcdu32, 0x5aaf85ecu32, 0x5ab54359u32, 0x5acacca7u32, - 0x5ad262acu32, 0x5ad8f6b3u32, 0x5adc1881u32, 0x5af11317u32, 0x5af639d3u32, 0x5af69bfau32, 0x5af6d962u32, 0x5afb87b5u32, - 0x5b034756u32, 0x5b0eba44u32, 0x5b13a562u32, 0x5b16a3b8u32, 0x5b1f6b22u32, 0x5b2201b5u32, 0x5b23c4bfu32, 0x5b281dbfu32, - 0x5b290b8du32, 0x5b3ae144u32, 0x5b3ccea9u32, 0x5b4164a5u32, 0x5b4311a8u32, 0x5b472199u32, 0x5b502a6eu32, 0x5b513215u32, - 0x5b522233u32, 0x5b5365c0u32, 0x5b57c664u32, 0x5b58a344u32, 0x5b58a3e1u32, 0x5b5a8de0u32, 0x5b5dbeaeu32, 0x5b62e6ecu32, - 0x5b644b59u32, 0x5b71deb4u32, 0x5b7821b3u32, 0x5b79bb78u32, 0x5b7e4677u32, 0x5b822454u32, 0x5b8550d7u32, 0x5b868c78u32, - 0x5b8c6ce4u32, 0x5b90b873u32, 0x5b953338u32, 0x5b999f2eu32, 0x5b9db108u32, 0x5ba5ef68u32, 0x5ba62c64u32, 0x5bad1fc9u32, - 0x5bbd6ba0u32, 0x5bbf482eu32, 0x5bc47914u32, 0x5bc61931u32, 0x5bc6f6e4u32, 0x5bcadda2u32, 0x5bd3a66au32, 0x5bda460bu32, - 0x5bdba743u32, 0x5bdce814u32, 0x5bddcb82u32, 0x5be1efc7u32, 0x5be33098u32, 0x5bfff551u32, 0x5c0797f1u32, 0x5c1be014u32, - 0x5c1d08bfu32, 0x5c2e422eu32, 0x5c33f751u32, 0x5c36f93au32, 0x5c3a7d65u32, 0x5c3ae5a1u32, 0x5c509e20u32, 0x5c57a16au32, - 0x5c586520u32, 0x5c5a5afcu32, 0x5c6ea642u32, 0x5c71d0dcu32, 0x5c83d860u32, 0x5c929d39u32, 0x5c947906u32, 0x5c95c218u32, - 0x5c96f822u32, 0x5ca03aa1u32, 0x5ca04335u32, 0x5caec2d1u32, 0x5cb38f1eu32, 0x5cc2cedfu32, 0x5cc8ff3du32, 0x5cca48ecu32, - 0x5ccca193u32, 0x5ccd4107u32, 0x5cd3d430u32, 0x5cd49c4cu32, 0x5cd573a7u32, 0x5cd97abau32, 0x5ce2cbdbu32, 0x5ce9201fu32, - 0x5cedc0e2u32, 0x5d07c238u32, 0x5d102b29u32, 0x5d11bca4u32, 0x5d15e71cu32, 0x5d1b3341u32, 0x5d1df09cu32, 0x5d288052u32, - 0x5d357da9u32, 0x5d3b2b7bu32, 0x5d42169fu32, 0x5d45b858u32, 0x5d48cb4du32, 0x5d4bdb60u32, 0x5d5021a2u32, 0x5d52631cu32, - 0x5d7318a2u32, 0x5d736902u32, 0x5d742f88u32, 0x5d75d0e8u32, 0x5d7fb381u32, 0x5d82fd61u32, 0x5d84729au32, 0x5d8c288eu32, - 0x5d8c4b56u32, 0x5d8e8200u32, 0x5d8fca47u32, 0x5d90ae2du32, 0x5d91e33eu32, 0x5d95447cu32, 0x5d9566f1u32, 0x5d9a614eu32, - 0x5d9fbd05u32, 0x5da802dbu32, 0x5da84cb5u32, 0x5db061a2u32, 0x5db4d529u32, 0x5db8988cu32, 0x5dbb3d6eu32, 0x5dc15ea4u32, - 0x5dc22d2fu32, 0x5dc5850eu32, 0x5dc85a0eu32, 0x5dca5090u32, 0x5dcd03e6u32, 0x5dcefff5u32, 0x5dd60bf8u32, 0x5dd91d7cu32, - 0x5ddf3eb2u32, 0x5de20319u32, 0x5df38d0du32, 0x5dfadfbeu32, 0x5dfdbaf1u32, 0x5e01d688u32, 0x5e05d196u32, 0x5e0912c0u32, - 0x5e09eb75u32, 0x5e0e7540u32, 0x5e14370cu32, 0x5e154837u32, 0x5e156336u32, 0x5e21262bu32, 0x5e21d4a2u32, 0x5e3ce82du32, - 0x5e40aed3u32, 0x5e51ee04u32, 0x5e66f5adu32, 0x5e6c3a3eu32, 0x5e705c40u32, 0x5e780023u32, 0x5e80737eu32, 0x5e81daf0u32, - 0x5e875188u32, 0x5e8e1b3eu32, 0x5e913e44u32, 0x5e94069bu32, 0x5e9b8f9du32, 0x5e9bd009u32, 0x5e9d0293u32, 0x5ea0e63fu32, - 0x5ead30b7u32, 0x5eaf90b6u32, 0x5eb45694u32, 0x5eb5861fu32, 0x5ebe2fa9u32, 0x5ebee7bbu32, 0x5ec13141u32, 0x5ec1cdc5u32, - 0x5ed788fau32, 0x5ed85270u32, 0x5ee3ec01u32, 0x5ee8ad68u32, 0x5eeb942eu32, 0x5efdc704u32, 0x5efddaabu32, 0x5f0d848bu32, - 0x5f163999u32, 0x5f176f04u32, 0x5f23a312u32, 0x5f27d725u32, 0x5f2d7422u32, 0x5f37f3e5u32, 0x5f3980b1u32, 0x5f3ce2fau32, - 0x5f3d9a50u32, 0x5f40bd47u32, 0x5f412b61u32, 0x5f44a25cu32, 0x5f485ef6u32, 0x5f4a481au32, 0x5f508313u32, 0x5f51ac2fu32, - 0x5f51b135u32, 0x5f53e0a5u32, 0x5f61bd4du32, 0x5f671971u32, 0x5f72dc08u32, 0x5f75ff5du32, 0x5f792865u32, 0x5f84d26du32, - 0x5f87779eu32, 0x5f97af30u32, 0x5f9af1a1u32, 0x5fa0983bu32, 0x5fa3a8d4u32, 0x5fae9323u32, 0x5fbd3856u32, 0x5fbd4590u32, - 0x5fc986f8u32, 0x5fca7e6bu32, 0x5fcb58e7u32, 0x5fd050aeu32, 0x5fd59ce6u32, 0x5fd791ccu32, 0x5fd9e3d5u32, 0x5fdd5f87u32, - 0x5fe282c8u32, 0x5fe5d244u32, 0x5fe69b5cu32, 0x5fe81da0u32, 0x5fe9a548u32, 0x5fe9bfbcu32, 0x5ff86067u32, 0x5ffaabc3u32, - 0x5ffbd4dfu32, 0x60019b8fu32, 0x6002fa20u32, 0x600486ddu32, 0x6005d749u32, 0x600b036bu32, 0x600be784u32, 0x601b32d8u32, - 0x601c3eafu32, 0x601e3327u32, 0x602066c1u32, 0x6024353du32, 0x60290e37u32, 0x602b12bau32, 0x60305d4bu32, 0x603a5d52u32, - 0x603ce929u32, 0x60442532u32, 0x6051e3c9u32, 0x6055c746u32, 0x6055e48bu32, 0x605791fbu32, 0x60635397u32, 0x6064d55eu32, - 0x6068b629u32, 0x607035dcu32, 0x60818eb3u32, 0x608b2f43u32, 0x6099f5b6u32, 0x609cdcabu32, 0x60b3df3cu32, 0x60b5110au32, - 0x60b7612du32, 0x60b77fcbu32, 0x60b8050au32, 0x60bee382u32, 0x60cc1552u32, 0x60d9cb6eu32, 0x60dbeacbu32, 0x60de0606u32, - 0x60df156au32, 0x60e07525u32, 0x60e0c7c0u32, 0x60e1d2cbu32, 0x60e352dbu32, 0x60f92e65u32, 0x60fd84c2u32, 0x6101ebafu32, - 0x6109161du32, 0x610e7212u32, 0x61194ff7u32, 0x611e07adu32, 0x6123235eu32, 0x612b8759u32, 0x612dd201u32, 0x612f290au32, - 0x613233bau32, 0x613558deu32, 0x6143777du32, 0x6147b790u32, 0x614f4e7cu32, 0x61517fd2u32, 0x61564524u32, 0x6160deceu32, - 0x61671f42u32, 0x616ac54cu32, 0x616c8f0eu32, 0x616c9a33u32, 0x616d2a68u32, 0x61710c8cu32, 0x6178ee50u32, 0x6181b26au32, - 0x6188fc69u32, 0x618aae8cu32, 0x618befd8u32, 0x619dcb31u32, 0x61a16889u32, 0x61a292c7u32, 0x61a3722du32, 0x61a3f313u32, - 0x61bb3586u32, 0x61c8d15du32, 0x61dfd93au32, 0x61e0216cu32, 0x61e5fba1u32, 0x61ea081bu32, 0x61f6cc4bu32, 0x61fde4c6u32, - 0x61fe44cau32, 0x61fefacbu32, 0x6205a836u32, 0x620cd122u32, 0x6214117eu32, 0x62204a03u32, 0x62207e2du32, 0x6221c0cdu32, - 0x622964c3u32, 0x6229bbb4u32, 0x622ee684u32, 0x6230b652u32, 0x62327a99u32, 0x6233a324u32, 0x6235f2b6u32, 0x623a1cacu32, - 0x623d7110u32, 0x62438274u32, 0x6243fe40u32, 0x6249d690u32, 0x624b4ae5u32, 0x6253168cu32, 0x6259e42fu32, 0x625a0fa7u32, - 0x6262326fu32, 0x6266fb58u32, 0x62756bcfu32, 0x6282386au32, 0x628372e9u32, 0x6283c581u32, 0x628ce9b4u32, 0x629b9f88u32, - 0x62afee5cu32, 0x62b387bcu32, 0x62b4157cu32, 0x62c7ca47u32, 0x62cc5532u32, 0x62cd2304u32, 0x62d0c87du32, 0x62d0e292u32, - 0x62d1fe12u32, 0x62df6f93u32, 0x62e17661u32, 0x62e18e62u32, 0x62e2de9du32, 0x62e91145u32, 0x62f23a32u32, 0x62ff3e75u32, - 0x62ffdd07u32, 0x63017301u32, 0x6301d775u32, 0x63185185u32, 0x631de81cu32, 0x631e7fb0u32, 0x632def1du32, 0x6338a355u32, - 0x633d2270u32, 0x63435683u32, 0x63538bc6u32, 0x6359b9f1u32, 0x63610c8au32, 0x6364e24du32, 0x6366f5ddu32, 0x636ddca3u32, - 0x637c0334u32, 0x637d4787u32, 0x637f9d86u32, 0x6382813au32, 0x638530bdu32, 0x638daa94u32, 0x639189beu32, 0x639439c0u32, - 0x6398fc48u32, 0x639ff73bu32, 0x63a69a94u32, 0x63a7baf2u32, 0x63aad6bbu32, 0x63b16084u32, 0x63b59454u32, 0x63b5d15au32, - 0x63b62012u32, 0x63bcfa89u32, 0x63c04df5u32, 0x63cff082u32, 0x63d05367u32, 0x63d1c8f6u32, 0x63d9b317u32, 0x63dc231fu32, - 0x63ecb455u32, 0x63ed3a31u32, 0x63f0f6efu32, 0x63f0f9e3u32, 0x63f3b82au32, 0x6404da9au32, 0x64088f74u32, 0x641840c5u32, - 0x641b13b8u32, 0x641bbdfcu32, 0x6428692bu32, 0x642956e2u32, 0x6429cb82u32, 0x642b0503u32, 0x642e082eu32, 0x642eee80u32, - 0x642f9165u32, 0x643bff91u32, 0x644c9e14u32, 0x644ffa56u32, 0x64542c30u32, 0x6456c805u32, 0x645b013eu32, 0x64624409u32, - 0x64653582u32, 0x64692ec1u32, 0x646bc03du32, 0x646d7e1du32, 0x6471f7cfu32, 0x647dc11du32, 0x6481b206u32, 0x64862513u32, - 0x6487500eu32, 0x6493042eu32, 0x649601f3u32, 0x64982a5eu32, 0x6498ed02u32, 0x64993eb4u32, 0x64a17c80u32, 0x64aa79f9u32, - 0x64ad509du32, 0x64b1b233u32, 0x64ba8420u32, 0x64bacc39u32, 0x64c3a0d9u32, 0x64c44aa4u32, 0x64c55b49u32, 0x64c58890u32, - 0x64c5b947u32, 0x64d50301u32, 0x64d51478u32, 0x64dbc695u32, 0x64de4719u32, 0x64e01bdcu32, 0x64ea9681u32, 0x64f8eed3u32, - 0x65012642u32, 0x6503d938u32, 0x65060a03u32, 0x650875dbu32, 0x6508d202u32, 0x650a0b61u32, 0x650b4697u32, 0x650e06f6u32, - 0x6515735au32, 0x651b30ffu32, 0x6523acccu32, 0x6523c40cu32, 0x65284ed8u32, 0x65291a39u32, 0x652a5a7bu32, 0x652ec0dcu32, - 0x65324322u32, 0x65340045u32, 0x6556e704u32, 0x6557cb1fu32, 0x65594848u32, 0x655b869bu32, 0x655bcf71u32, 0x655e2d83u32, - 0x6564dcacu32, 0x656851c7u32, 0x657691bfu32, 0x6585c19bu32, 0x658655ecu32, 0x658aa17bu32, 0x658dbf7bu32, 0x65926568u32, - 0x65a70070u32, 0x65b87d0au32, 0x65bc8d9au32, 0x65c3a5c6u32, 0x65c46652u32, 0x65c769eeu32, 0x65c99cc0u32, 0x65cf9164u32, - 0x65cfefc2u32, 0x65d3d29du32, 0x65d69e0fu32, 0x65d87c8au32, 0x65e1324au32, 0x65e46828u32, 0x65e712e0u32, 0x65ed9111u32, - 0x65f92f4cu32, 0x65fd2fceu32, 0x65fd6153u32, 0x66056d2cu32, 0x66062edcu32, 0x6613954du32, 0x66154aefu32, 0x661a36beu32, - 0x661e1d4au32, 0x661e9643u32, 0x66204615u32, 0x662206e9u32, 0x6625e613u32, 0x662a5387u32, 0x662d41eau32, 0x662d87ccu32, - 0x662fb154u32, 0x663beb91u32, 0x66402531u32, 0x6643e3d8u32, 0x6644f7dfu32, 0x664bcde1u32, 0x664e7707u32, 0x665d1764u32, - 0x665d3b61u32, 0x665ed5e2u32, 0x6667d2c7u32, 0x6667e35bu32, 0x6667ff39u32, 0x666a1a15u32, 0x666ebc1au32, 0x6671edbau32, - 0x667513c1u32, 0x667735ffu32, 0x6678d1abu32, 0x667bc758u32, 0x6683bda9u32, 0x66862167u32, 0x669dd893u32, 0x66a4b052u32, - 0x66a61be7u32, 0x66aa1b94u32, 0x66ac967fu32, 0x66af48efu32, 0x66b373acu32, 0x66b82af6u32, 0x66be6a64u32, 0x66c1be38u32, - 0x66c85477u32, 0x66c876abu32, 0x66c9986au32, 0x66ce0c34u32, 0x66cfc738u32, 0x66d3040au32, 0x66e187ccu32, 0x66e35470u32, - 0x66e3fc61u32, 0x66e7373fu32, 0x66eaaa96u32, 0x66eddd2au32, 0x66efc469u32, 0x66f209cfu32, 0x66fe6e10u32, 0x6703dc3eu32, - 0x67053921u32, 0x67058c41u32, 0x670860aeu32, 0x6709812au32, 0x67194fbbu32, 0x671afd4au32, 0x67212873u32, 0x67279ac1u32, - 0x673bf361u32, 0x67408c1cu32, 0x67474af1u32, 0x67497d80u32, 0x674b447fu32, 0x674dd7e6u32, 0x67501ec0u32, 0x6750c6b7u32, - 0x675519c7u32, 0x675a5229u32, 0x676bdea6u32, 0x676c80feu32, 0x67769170u32, 0x677cb34fu32, 0x678baca6u32, 0x67957a18u32, - 0x6797ddd0u32, 0x679cdd39u32, 0x679e4657u32, 0x679fcefeu32, 0x679fe82bu32, 0x67a1a69du32, 0x67ac1955u32, 0x67b24bd4u32, - 0x67b35b36u32, 0x67b57c6bu32, 0x67b76895u32, 0x67bf844du32, 0x67c37c67u32, 0x68069313u32, 0x6809bf45u32, 0x681a38f8u32, - 0x681b53d8u32, 0x681de67fu32, 0x681f9f12u32, 0x68260371u32, 0x682a6b46u32, 0x682c8b04u32, 0x682fcf2cu32, 0x6830bd4eu32, - 0x6831cdeau32, 0x6841d996u32, 0x684210d8u32, 0x68456811u32, 0x68481b3eu32, 0x6853121cu32, 0x685652f6u32, 0x685a936fu32, - 0x685c3c1cu32, 0x685fc247u32, 0x686b0743u32, 0x686dc2fdu32, 0x686e2f4du32, 0x68745c6au32, 0x687656e0u32, 0x6876d239u32, - 0x68778e7bu32, 0x687a6653u32, 0x687f7274u32, 0x68886a35u32, 0x688dbd1eu32, 0x6890a961u32, 0x689d4b20u32, 0x68a15a93u32, - 0x68ba1e13u32, 0x68bd224cu32, 0x68c14e5au32, 0x68c17981u32, 0x68c77133u32, 0x68d33998u32, 0x68da82afu32, 0x68ddbe0au32, - 0x68e47693u32, 0x68ef344bu32, 0x68f21dddu32, 0x68f3180au32, 0x68fce6f3u32, 0x68ff95d8u32, 0x69077132u32, 0x690adde0u32, - 0x690bd6f3u32, 0x6911aff6u32, 0x69161b66u32, 0x69167f46u32, 0x6918c521u32, 0x691ac3bfu32, 0x691cee86u32, 0x692cb196u32, - 0x69303528u32, 0x69360da6u32, 0x69365c21u32, 0x693bb96eu32, 0x693c77b4u32, 0x693f8ea1u32, 0x69401fb4u32, 0x694ca02bu32, - 0x6954967bu32, 0x69549badu32, 0x6956bc95u32, 0x6958021fu32, 0x6958b798u32, 0x6965a7d2u32, 0x69671351u32, 0x696713d6u32, - 0x6969be93u32, 0x69709b05u32, 0x697f1781u32, 0x6982bbdcu32, 0x6984e06fu32, 0x698526a6u32, 0x6986ffd0u32, 0x69947e8cu32, - 0x699a2b5eu32, 0x699ba991u32, 0x699e4e0au32, 0x699f0383u32, 0x69a29df9u32, 0x69a3fa57u32, 0x69abb565u32, 0x69ac35d0u32, - 0x69b8295cu32, 0x69bf9d2du32, 0x69c4dee1u32, 0x69c7ffa1u32, 0x69dd0567u32, 0x69ea3c65u32, 0x69efc234u32, 0x69efe723u32, - 0x69f33d76u32, 0x6a13d699u32, 0x6a1c65c9u32, 0x6a21680du32, 0x6a2d829cu32, 0x6a2f0258u32, 0x6a30db03u32, 0x6a3540c4u32, - 0x6a3b79f8u32, 0x6a3e99b2u32, 0x6a426481u32, 0x6a474471u32, 0x6a4db760u32, 0x6a56a320u32, 0x6a58996au32, 0x6a63c6ddu32, - 0x6a6a7de7u32, 0x6a723230u32, 0x6a755dddu32, 0x6a7bc132u32, 0x6a7d7e4eu32, 0x6a80736au32, 0x6a88f93eu32, 0x6a89c16fu32, - 0x6a8c830au32, 0x6a90ca1eu32, 0x6a90f75bu32, 0x6a92ba2cu32, 0x6a94eccau32, 0x6a9a7c7du32, 0x6a9b09ecu32, 0x6a9ef57cu32, - 0x6aa7a912u32, 0x6aac0c38u32, 0x6aae4d66u32, 0x6abd4284u32, 0x6abe9abeu32, 0x6ac5293au32, 0x6ad54d7fu32, 0x6ade7ac1u32, - 0x6aeeff37u32, 0x6af3fbc4u32, 0x6af6502du32, 0x6af721bau32, 0x6af733edu32, 0x6af907e9u32, 0x6b02e1d3u32, 0x6b0475d9u32, - 0x6b103a15u32, 0x6b12d855u32, 0x6b1a4988u32, 0x6b243d6du32, 0x6b26859fu32, 0x6b2d128du32, 0x6b2e7ecbu32, 0x6b3510ecu32, - 0x6b380e55u32, 0x6b38e8b9u32, 0x6b4ac397u32, 0x6b4bf6a3u32, 0x6b542411u32, 0x6b583bd3u32, 0x6b5c3461u32, 0x6b5d00bbu32, - 0x6b605793u32, 0x6b60f6b0u32, 0x6b6195cdu32, 0x6b6234eau32, 0x6b64b373u32, 0x6b67548fu32, 0x6b6b875bu32, 0x6b6bc922u32, - 0x6b82da49u32, 0x6b87880eu32, 0x6b88ae5du32, 0x6b8e63c3u32, 0x6b8e82b3u32, 0x6b942285u32, 0x6b9b7933u32, 0x6b9c1aa7u32, - 0x6ba0c05du32, 0x6ba6c107u32, 0x6ba9445fu32, 0x6bb2c8fdu32, 0x6bb802e4u32, 0x6bbf0660u32, 0x6bc3b0c4u32, 0x6bc620bdu32, - 0x6bcb0e77u32, 0x6bcff07eu32, 0x6bd2488cu32, 0x6bd70c28u32, 0x6bd9ed7cu32, 0x6be2794bu32, 0x6be3a233u32, 0x6be8b06du32, - 0x6becf038u32, 0x6bedb931u32, 0x6bedefcau32, 0x6bf1edceu32, 0x6bf45b2eu32, 0x6bf9a4d0u32, 0x6c0fb007u32, 0x6c1f131fu32, - 0x6c20e2f1u32, 0x6c2d10dcu32, 0x6c3867a3u32, 0x6c3b106bu32, 0x6c3ca29fu32, 0x6c3f3146u32, 0x6c41050fu32, 0x6c4567e3u32, - 0x6c52fd80u32, 0x6c534b7fu32, 0x6c5878a9u32, 0x6c58bcc7u32, 0x6c622bf2u32, 0x6c6ec157u32, 0x6c7055e2u32, 0x6c70fa32u32, - 0x6c759284u32, 0x6c7cb726u32, 0x6c7de0d6u32, 0x6c7f45cdu32, 0x6c81ca67u32, 0x6c839ee7u32, 0x6c873138u32, 0x6c87d3aeu32, - 0x6c8a028fu32, 0x6c9615a2u32, 0x6ca60bf2u32, 0x6ca8b3eau32, 0x6cb27a36u32, 0x6cb63cd2u32, 0x6cb6c9bcu32, 0x6cc4d632u32, - 0x6cce84b4u32, 0x6ccf4e6cu32, 0x6cd1ec1fu32, 0x6cd2f182u32, 0x6cd87a7bu32, 0x6cde8ea5u32, 0x6ce11199u32, 0x6ce35f30u32, - 0x6cee754bu32, 0x6cf198cau32, 0x6cf27315u32, 0x6cf4f2e2u32, 0x6cfbe7d7u32, 0x6d078f28u32, 0x6d0a5cbeu32, 0x6d0bbceeu32, - 0x6d0ef2a0u32, 0x6d17d35au32, 0x6d225214u32, 0x6d2e9ba3u32, 0x6d346442u32, 0x6d38dc25u32, 0x6d41495fu32, 0x6d429a83u32, - 0x6d44a42eu32, 0x6d4b5210u32, 0x6d556dddu32, 0x6d58adedu32, 0x6d616958u32, 0x6d699414u32, 0x6d6a3777u32, 0x6d6a9e0cu32, - 0x6d6bf000u32, 0x6d6bf71eu32, 0x6d6e00e2u32, 0x6d701e47u32, 0x6d76b478u32, 0x6d7c8c8bu32, 0x6d7e66bbu32, 0x6d821e7cu32, - 0x6d84900fu32, 0x6d84f03du32, 0x6d86dfc7u32, 0x6d966934u32, 0x6d99e8eau32, 0x6d9ab45bu32, 0x6da19f3du32, 0x6da763feu32, - 0x6da7c42cu32, 0x6daaafdau32, 0x6dad5c31u32, 0x6daf0d34u32, 0x6db5d397u32, 0x6db81513u32, 0x6dbcde18u32, 0x6dbd2096u32, - 0x6dbdcef3u32, 0x6dc40b10u32, 0x6dcdfa21u32, 0x6dcef7ccu32, 0x6dd649fau32, 0x6de36e55u32, 0x6de5eb31u32, 0x6df82813u32, - 0x6df84ee9u32, 0x6dfaff37u32, 0x6e0134f3u32, 0x6e01b953u32, 0x6e088ffau32, 0x6e09203fu32, 0x6e0c9973u32, 0x6e145fbdu32, - 0x6e193118u32, 0x6e1c6a59u32, 0x6e1e657au32, 0x6e2310cfu32, 0x6e28ce24u32, 0x6e2b97ddu32, 0x6e3e817bu32, 0x6e45eb5au32, - 0x6e507db3u32, 0x6e63491fu32, 0x6e6c6860u32, 0x6e7a4988u32, 0x6e83d9aau32, 0x6e85f9b3u32, 0x6e8c3cadu32, 0x6e8e3b6au32, - 0x6e917450u32, 0x6e92f5b1u32, 0x6e95c6f0u32, 0x6e95e8b1u32, 0x6e99470eu32, 0x6e9e861du32, 0x6ea02c59u32, 0x6ea2bb1au32, - 0x6eac671eu32, 0x6eb03837u32, 0x6eb414dau32, 0x6eb465d5u32, 0x6eb66151u32, 0x6ebbab75u32, 0x6ec1a0c2u32, 0x6ec55ee5u32, - 0x6ec73ccbu32, 0x6ed00a4eu32, 0x6edb511eu32, 0x6ede10b9u32, 0x6ee79001u32, 0x6ee98eb1u32, 0x6ef828c9u32, 0x6efc5326u32, - 0x6f0cba64u32, 0x6f11e3b1u32, 0x6f14ead9u32, 0x6f1a3781u32, 0x6f282e85u32, 0x6f2df346u32, 0x6f3927c9u32, 0x6f47c73eu32, - 0x6f4928e0u32, 0x6f4bd1e9u32, 0x6f4ea39du32, 0x6f5fd221u32, 0x6f62d608u32, 0x6f71c32au32, 0x6f7319b7u32, 0x6f736771u32, - 0x6f86d087u32, 0x6f885341u32, 0x6f939ed5u32, 0x6f9529d5u32, 0x6f989d75u32, 0x6fa2c4a2u32, 0x6fa427ebu32, 0x6fb36113u32, - 0x6fcec03fu32, 0x6fcf1471u32, 0x6fd00e45u32, 0x6fd44c89u32, 0x6fda7514u32, 0x6fdc3139u32, 0x6fdd60cfu32, 0x6ff8184au32, - 0x6ffaa3b0u32, 0x6ffb6440u32, 0x6ffe35dau32, 0x70067234u32, 0x700f3f38u32, 0x701b65fcu32, 0x701fc7d4u32, 0x704108c3u32, - 0x705e4721u32, 0x705eb110u32, 0x7064695bu32, 0x7064e645u32, 0x70670567u32, 0x7067c21au32, 0x706b8f8au32, 0x707d6e05u32, - 0x70805511u32, 0x70888fe8u32, 0x7098a138u32, 0x709d505du32, 0x70a987c7u32, 0x70aa6311u32, 0x70b47e95u32, 0x70b58b94u32, - 0x70c70d5au32, 0x70d533cfu32, 0x70e530b6u32, 0x70e9240fu32, 0x70ef13d0u32, 0x70ef5245u32, 0x70fa8832u32, 0x70fdb547u32, - 0x7100c2f3u32, 0x71043a86u32, 0x710ab016u32, 0x710d3cddu32, 0x71138e22u32, 0x7115df73u32, 0x71182dc5u32, 0x711c4569u32, - 0x711dd738u32, 0x712301a9u32, 0x712e661cu32, 0x71306b8eu32, 0x7132fe3bu32, 0x7136cfa9u32, 0x713c5bf3u32, 0x713ca335u32, - 0x713d09abu32, 0x7143981cu32, 0x7148f9bbu32, 0x714af4ffu32, 0x714de7e7u32, 0x714e8bebu32, 0x715f5077u32, 0x7160d6cfu32, - 0x71635913u32, 0x716bfba9u32, 0x716e828au32, 0x7174e2efu32, 0x7178aef1u32, 0x717b5d04u32, 0x718a8d38u32, 0x718b0f82u32, - 0x7198b003u32, 0x719a49abu32, 0x719b2b18u32, 0x719fd00fu32, 0x71a59ca9u32, 0x71a61bc3u32, 0x71a83430u32, 0x71a9eec3u32, - 0x71ac6ccbu32, 0x71b2cf48u32, 0x71bc5e9du32, 0x71bc5f6cu32, 0x71c17644u32, 0x71c242cfu32, 0x71c9cb69u32, 0x71cda15cu32, - 0x71cdad28u32, 0x71d52fdcu32, 0x71d69e53u32, 0x71dd6b82u32, 0x71e2ca3au32, 0x71e7cc6du32, 0x71ee6df1u32, 0x71f02a6bu32, - 0x71f560e5u32, 0x71f781eeu32, 0x71f9816eu32, 0x71fa189au32, 0x71fd17e0u32, 0x72012147u32, 0x7201272du32, 0x7201bd4du32, - 0x7205d82du32, 0x72066e4du32, 0x721195e9u32, 0x721219cbu32, 0x72129a34u32, 0x721b1811u32, 0x7224dcb9u32, 0x722759afu32, - 0x723248b6u32, 0x723a5644u32, 0x72457320u32, 0x7259c8fbu32, 0x725e2bf8u32, 0x72615732u32, 0x7266c0e7u32, 0x726b361cu32, - 0x726d71edu32, 0x726ff262u32, 0x727a7c11u32, 0x72829c59u32, 0x7285a9cfu32, 0x7285dbefu32, 0x728e269au32, 0x728f1a05u32, - 0x72906044u32, 0x7297840cu32, 0x72a20169u32, 0x72a33e46u32, 0x72ab4ca0u32, 0x72aeabd8u32, 0x72b0d5b1u32, 0x72bb29c7u32, - 0x72be7c64u32, 0x72c37a9bu32, 0x72c4a3a2u32, 0x72c9892fu32, 0x72d2678du32, 0x72d5b378u32, 0x72d791feu32, 0x72daa4d1u32, - 0x72e1210au32, 0x72e58ce0u32, 0x72edde80u32, 0x72f039f3u32, 0x72f67998u32, 0x72f8a9e0u32, 0x72f91a78u32, 0x7301c55au32, - 0x7308ce51u32, 0x7315478fu32, 0x7316143du32, 0x731d08b2u32, 0x731e8cbdu32, 0x73367100u32, 0x73381266u32, 0x73402c46u32, - 0x7350000bu32, 0x7352fc35u32, 0x735ad668u32, 0x7362fec1u32, 0x73646e6du32, 0x73722198u32, 0x73730c2au32, 0x737f6b00u32, - 0x737fc77cu32, 0x73838128u32, 0x738d30f8u32, 0x738e9dd4u32, 0x73981b1bu32, 0x739a54a5u32, 0x739b8d3au32, 0x739cbd7fu32, - 0x73a11984u32, 0x73a63f44u32, 0x73a6a96du32, 0x73a71554u32, 0x73b152c1u32, 0x73b4636du32, 0x73bdb93bu32, 0x73c28796u32, - 0x73c36099u32, 0x73c77caeu32, 0x73c8f3cbu32, 0x73cbc859u32, 0x73cfccf9u32, 0x73cfd8a2u32, 0x73d06abdu32, 0x73d0d5f8u32, - 0x73d27590u32, 0x73d3c11cu32, 0x73d64a67u32, 0x73ef6136u32, 0x73eff6aau32, 0x73f2fdc1u32, 0x73f8d43cu32, 0x7407bf9fu32, - 0x740820efu32, 0x740cf23du32, 0x7411e64bu32, 0x74139ba2u32, 0x7414e24au32, 0x74157c92u32, 0x741819b3u32, 0x741a46f0u32, - 0x741ab00fu32, 0x741d38aeu32, 0x741ff116u32, 0x7420dac9u32, 0x742493d0u32, 0x742e728au32, 0x743016b0u32, 0x7430cadfu32, - 0x74316799u32, 0x74344238u32, 0x74355d47u32, 0x743ecc48u32, 0x7451f982u32, 0x7457d504u32, 0x74642116u32, 0x7478db01u32, - 0x747cb751u32, 0x7485b794u32, 0x748676e3u32, 0x7486bf56u32, 0x748a2632u32, 0x748c1138u32, 0x748e5908u32, 0x7493cd7fu32, - 0x74b007beu32, 0x74b41030u32, 0x74b6a9aau32, 0x74b772b1u32, 0x74b99c47u32, 0x74badfcdu32, 0x74bdc2f3u32, 0x74c7d9ceu32, - 0x74cb32c2u32, 0x74cfceb7u32, 0x74e09f1eu32, 0x74e2384fu32, 0x74e3ee00u32, 0x74e5751du32, 0x74e72c32u32, 0x74fdd25cu32, - 0x7502211eu32, 0x7505987bu32, 0x75174aefu32, 0x7518bb78u32, 0x7518cf46u32, 0x751c15d0u32, 0x75260401u32, 0x75325d47u32, - 0x753398a4u32, 0x75346180u32, 0x7534ec55u32, 0x75372161u32, 0x753a01ecu32, 0x754905b4u32, 0x75498995u32, 0x7556701eu32, - 0x75605fb3u32, 0x7563406bu32, 0x756cf6d5u32, 0x757c6eafu32, 0x75807eb8u32, 0x7583857du32, 0x7589c8e3u32, 0x758a1557u32, - 0x758d38c7u32, 0x758e199du32, 0x758e6ac2u32, 0x7593400fu32, 0x7598be13u32, 0x75ae7bc1u32, 0x75b2fd7bu32, 0x75be5a77u32, - 0x75c8feedu32, 0x75c9acaeu32, 0x75ce110au32, 0x75d934b0u32, 0x75dd4b08u32, 0x75e0a8dau32, 0x75e7c146u32, 0x75eea37cu32, - 0x75fa60e7u32, 0x76063162u32, 0x760b4f50u32, 0x760bcb49u32, 0x760e1e91u32, 0x760e8c77u32, 0x76121f74u32, 0x76132c99u32, - 0x7618ea8eu32, 0x761913bcu32, 0x7623ee3bu32, 0x762b1e1cu32, 0x76315b9au32, 0x76342856u32, 0x76390254u32, 0x76403ae5u32, - 0x76404153u32, 0x7642b2aau32, 0x76462b11u32, 0x765bc364u32, 0x765d7090u32, 0x765f1ac8u32, 0x765fcb24u32, 0x7664d07cu32, - 0x766633e6u32, 0x7673ff9eu32, 0x76745c67u32, 0x7678fa73u32, 0x76831ef9u32, 0x768a330du32, 0x768c2477u32, 0x768cf8ecu32, - 0x769eb6fau32, 0x769ff0e8u32, 0x76a19685u32, 0x76a95edeu32, 0x76adc766u32, 0x76b7d164u32, 0x76b8acabu32, 0x76ba5aedu32, - 0x76bbf498u32, 0x76c18e10u32, 0x76c792d3u32, 0x76cbe38du32, 0x76d832d6u32, 0x76d9728bu32, 0x76dc6b53u32, 0x76dea9c1u32, - 0x76e06a0au32, 0x76e87028u32, 0x76e92982u32, 0x76ea347bu32, 0x76ea9659u32, 0x76ec5a48u32, 0x76ed867cu32, 0x76f02842u32, - 0x76f4541bu32, 0x76fe7877u32, 0x7700c3a8u32, 0x770c04ddu32, 0x77152eb1u32, 0x77179fafu32, 0x771809e4u32, 0x771a9708u32, - 0x7728cabeu32, 0x772a3d03u32, 0x773272d3u32, 0x77358f66u32, 0x7748a2e1u32, 0x774e090cu32, 0x774fb691u32, 0x77501affu32, - 0x775433bbu32, 0x775480dbu32, 0x77554422u32, 0x77589ed3u32, 0x775953d5u32, 0x775c0234u32, 0x775c0ae4u32, 0x775cb620u32, - 0x7761eb83u32, 0x77625139u32, 0x7763f136u32, 0x776a43fdu32, 0x7775a35du32, 0x777a7ad6u32, 0x7780a8bfu32, 0x7784f53au32, - 0x778982d5u32, 0x778a51eau32, 0x77982100u32, 0x779b67d7u32, 0x779da9aeu32, 0x77a16d25u32, 0x77a3322au32, 0x77a5fb4bu32, - 0x77b3b39eu32, 0x77b467e3u32, 0x77c2ddddu32, 0x77c84324u32, 0x77ca0998u32, 0x77ca3157u32, 0x77d6e5a7u32, 0x77db4744u32, - 0x77dfd87au32, 0x77e1d4d9u32, 0x77e258fau32, 0x77e333e8u32, 0x77e4333au32, 0x77e80142u32, 0x77ee2e4fu32, 0x77f7a28cu32, - 0x77fc0c35u32, 0x78061c32u32, 0x7807fb23u32, 0x781097cdu32, 0x781763cbu32, 0x782500aau32, 0x782d5f04u32, 0x782ef3e5u32, - 0x7833741fu32, 0x7839e543u32, 0x783ea4fcu32, 0x783ea825u32, 0x784bbacau32, 0x784d06cau32, 0x784e4ec4u32, 0x784f174eu32, - 0x785da24au32, 0x786cfed9u32, 0x786d537au32, 0x7873d2e3u32, 0x7875bdedu32, 0x787730efu32, 0x78774dcbu32, 0x7879dd6du32, - 0x7879f140u32, 0x787b69e9u32, 0x787d8adeu32, 0x78890216u32, 0x788b92ceu32, 0x78962e55u32, 0x789d87cfu32, 0x78a63649u32, - 0x78aa22f7u32, 0x78aa4271u32, 0x78b1a804u32, 0x78bcde0du32, 0x78c4346eu32, 0x78c5053du32, 0x78c847d5u32, 0x78c95920u32, - 0x78ccd408u32, 0x78d08ca5u32, 0x78d13ee7u32, 0x78d151cdu32, 0x78dd5c07u32, 0x78e4c48au32, 0x78e6dedbu32, 0x78e74639u32, - 0x78e7571cu32, 0x78ec053eu32, 0x78ec524eu32, 0x78ed9dfcu32, 0x78f98abfu32, 0x78ff33f4u32, 0x790276a4u32, 0x7905b023u32, - 0x790af6c4u32, 0x7911b5bcu32, 0x79163797u32, 0x79168211u32, 0x791691a5u32, 0x791a567cu32, 0x791d3d24u32, 0x79216c28u32, - 0x7924f3ddu32, 0x79251c3fu32, 0x793d690bu32, 0x793dedb7u32, 0x79435080u32, 0x794617aeu32, 0x7948dc3eu32, 0x794efa00u32, - 0x7952a95bu32, 0x795e5aa5u32, 0x79706e6cu32, 0x797adbd2u32, 0x7980fe20u32, 0x798b8514u32, 0x798df029u32, 0x7991480cu32, - 0x799cd945u32, 0x799dec9fu32, 0x799e7a02u32, 0x799e8903u32, 0x79a2032fu32, 0x79a8183au32, 0x79b27bfcu32, 0x79b3c6bdu32, - 0x79c155e6u32, 0x79c7f5beu32, 0x79c9e008u32, 0x79cb07c4u32, 0x79cf6166u32, 0x79d1b10fu32, 0x79d1bda7u32, 0x79d4a74bu32, - 0x79d684ccu32, 0x79d84ac6u32, 0x79da5906u32, 0x79db97ebu32, 0x79ddda69u32, 0x79e274b8u32, 0x79e5db3eu32, 0x79e5ffbfu32, - 0x79e8fca7u32, 0x79ed9e2du32, 0x79f1cd31u32, 0x79f207eeu32, 0x79f7a048u32, 0x79faa6a3u32, 0x7a07866du32, 0x7a091a30u32, - 0x7a0b190du32, 0x7a1597dbu32, 0x7a16d33bu32, 0x7a16dad4u32, 0x7a1ab763u32, 0x7a1dd663u32, 0x7a210c5du32, 0x7a23c5d3u32, - 0x7a2abd7du32, 0x7a2ec713u32, 0x7a318321u32, 0x7a3863d1u32, 0x7a3d9fe3u32, 0x7a41adbcu32, 0x7a43fbb8u32, 0x7a4b2715u32, - 0x7a583bc5u32, 0x7a5e0683u32, 0x7a68fd34u32, 0x7a6bf900u32, 0x7a6cdfb1u32, 0x7a6e8393u32, 0x7a71e636u32, 0x7a791d9eu32, - 0x7a7a7733u32, 0x7a7d0698u32, 0x7a852823u32, 0x7a8b428fu32, 0x7a8e22eeu32, 0x7a90900bu32, 0x7a933f7fu32, 0x7a956fd5u32, - 0x7a9bd902u32, 0x7a9c546au32, 0x7a9d3e72u32, 0x7ab4949au32, 0x7ab6f63au32, 0x7abb1cb8u32, 0x7ac048dau32, 0x7acca589u32, - 0x7acf5352u32, 0x7ad1980au32, 0x7ad19a73u32, 0x7ae04a1fu32, 0x7aeb9d76u32, 0x7aed2fbeu32, 0x7aefa8b7u32, 0x7af9520fu32, - 0x7b0643a4u32, 0x7b07e8d2u32, 0x7b0945bcu32, 0x7b0a3832u32, 0x7b0bc11eu32, 0x7b14d033u32, 0x7b17857du32, 0x7b17e666u32, - 0x7b1a2aefu32, 0x7b1e5fe1u32, 0x7b1e8b3du32, 0x7b2474beu32, 0x7b2b89b0u32, 0x7b34cef8u32, 0x7b374f5bu32, 0x7b383654u32, - 0x7b38412bu32, 0x7b3fc166u32, 0x7b4155e7u32, 0x7b42e066u32, 0x7b4639f4u32, 0x7b46aa3eu32, 0x7b4a578du32, 0x7b5130a5u32, - 0x7b54df28u32, 0x7b65351fu32, 0x7b673c9au32, 0x7b7016c8u32, 0x7b713791u32, 0x7b7a3745u32, 0x7b7d825fu32, 0x7b83a6b8u32, - 0x7b860ac1u32, 0x7b8b2752u32, 0x7b93c4d1u32, 0x7b94e4f4u32, 0x7b964ff3u32, 0x7b999e4bu32, 0x7ba71bdbu32, 0x7ba91eb4u32, - 0x7bab30eeu32, 0x7baeb136u32, 0x7bb25792u32, 0x7bb76ce9u32, 0x7bb97fd5u32, 0x7bc36676u32, 0x7bca1cb8u32, 0x7bce245cu32, - 0x7bd07118u32, 0x7bd1f1d1u32, 0x7bd44574u32, 0x7bd550e2u32, 0x7bdd6c41u32, 0x7bdec634u32, 0x7be33b37u32, 0x7be756deu32, - 0x7bef74d5u32, 0x7bf20836u32, 0x7bf9ac05u32, 0x7bfa4869u32, 0x7bfc9c0cu32, 0x7bfd2368u32, 0x7bfefa86u32, 0x7c052183u32, - 0x7c076b48u32, 0x7c0c15d8u32, 0x7c0cec65u32, 0x7c1c4410u32, 0x7c244770u32, 0x7c2e6a3eu32, 0x7c2f04f7u32, 0x7c3065d1u32, - 0x7c3436e3u32, 0x7c361a83u32, 0x7c3b97aau32, 0x7c3e9b0eu32, 0x7c42e072u32, 0x7c46310fu32, 0x7c46ec53u32, 0x7c4c7940u32, - 0x7c4e7387u32, 0x7c4eacb1u32, 0x7c51e456u32, 0x7c5da023u32, 0x7c5de76eu32, 0x7c5df7e0u32, 0x7c60d08eu32, 0x7c61027eu32, - 0x7c616175u32, 0x7c621189u32, 0x7c625b78u32, 0x7c650580u32, 0x7c660d51u32, 0x7c66cc8bu32, 0x7c6904e5u32, 0x7c6ffbdeu32, - 0x7c741ebfu32, 0x7c7489d9u32, 0x7c793469u32, 0x7c7c12b2u32, 0x7c7f258fu32, 0x7c7fbcabu32, 0x7c811d82u32, 0x7c8359d2u32, - 0x7c87a082u32, 0x7c8fccf9u32, 0x7c95c209u32, 0x7c961e09u32, 0x7c9f59dbu32, 0x7ca0a40bu32, 0x7ca168d4u32, 0x7ca4592eu32, - 0x7cae26e3u32, 0x7cae6a91u32, 0x7cb1bfc7u32, 0x7cb3122au32, 0x7cbe81c3u32, 0x7cc6c97du32, 0x7cc77f7fu32, 0x7ccf2348u32, - 0x7cd96a38u32, 0x7cda7f2bu32, 0x7cdf779au32, 0x7cef5c36u32, 0x7cfba3fbu32, 0x7cff4fefu32, 0x7d03d700u32, 0x7d0a1c65u32, - 0x7d1f77c2u32, 0x7d21f90fu32, 0x7d26bee5u32, 0x7d2ca017u32, 0x7d3364a2u32, 0x7d36d571u32, 0x7d455cd0u32, 0x7d4d3c64u32, - 0x7d4dacc0u32, 0x7d4f59a9u32, 0x7d518073u32, 0x7d52b793u32, 0x7d531783u32, 0x7d647740u32, 0x7d773d19u32, 0x7d78a52du32, - 0x7d796071u32, 0x7d7a4fc8u32, 0x7d7d65c1u32, 0x7d8c87b1u32, 0x7d8faf12u32, 0x7d928d30u32, 0x7d96ab4du32, 0x7d9c0b8cu32, - 0x7dafc555u32, 0x7db2cd2du32, 0x7db36f79u32, 0x7db5fc32u32, 0x7dc9110eu32, 0x7dcaa07au32, 0x7dce3df7u32, 0x7dd12ff6u32, - 0x7de6b4b7u32, 0x7ded94dbu32, 0x7df13ffbu32, 0x7dfcf980u32, 0x7e016b36u32, 0x7e0183efu32, 0x7e03621fu32, 0x7e0736ceu32, - 0x7e07b3dbu32, 0x7e0f28ddu32, 0x7e1b6e57u32, 0x7e24c08du32, 0x7e25b4b2u32, 0x7e29caadu32, 0x7e2a47bau32, 0x7e2e7befu32, - 0x7e2ee04du32, 0x7e2fca58u32, 0x7e303a36u32, 0x7e383be0u32, 0x7e396741u32, 0x7e3c9c84u32, 0x7e3ca113u32, 0x7e430f2cu32, - 0x7e4a84e1u32, 0x7e4d4d26u32, 0x7e517b1du32, 0x7e5fde6eu32, 0x7e688505u32, 0x7e689d01u32, 0x7e845fe0u32, 0x7e8a4716u32, - 0x7e8c96d8u32, 0x7e96db10u32, 0x7e971f7bu32, 0x7ea58021u32, 0x7ead2787u32, 0x7ead8d16u32, 0x7eadd181u32, 0x7eae522bu32, - 0x7eb9e31du32, 0x7ec07c74u32, 0x7ec1cb95u32, 0x7ec4fcc2u32, 0x7eccf080u32, 0x7ed1132cu32, 0x7ed927cfu32, 0x7edbdbeau32, - 0x7ee043b9u32, 0x7ee0fae7u32, 0x7ee1e5afu32, 0x7ee7b64fu32, 0x7ee9bb68u32, 0x7efa04d6u32, 0x7f10c320u32, 0x7f11c382u32, - 0x7f13b529u32, 0x7f17c371u32, 0x7f189cefu32, 0x7f19dbbdu32, 0x7f1d477fu32, 0x7f1deddcu32, 0x7f1ea0b5u32, 0x7f203669u32, - 0x7f3349cbu32, 0x7f3fd429u32, 0x7f402607u32, 0x7f4834e5u32, 0x7f48e3a8u32, 0x7f4b8c3eu32, 0x7f50595du32, 0x7f56a3b6u32, - 0x7f5a061bu32, 0x7f5d730eu32, 0x7f5dba98u32, 0x7f5e4d83u32, 0x7f6b1faau32, 0x7f70ad15u32, 0x7f790474u32, 0x7f7c725eu32, -]; -#[rustfmt::skip] -pub static UCRT_COSF_WIDE_VALUES: [u32; 2424] = [ - 0x3f7ffe8cu32, 0x3f7ffe55u32, 0xbf7ffe64u32, 0xbf7ffec0u32, 0xbbc984d6u32, 0xbf7fff66u32, 0xbbe39c5bu32, 0x3f7ffe65u32, - 0x3f7fff24u32, 0xbf7ffe66u32, 0xbf7ffe2bu32, 0x3bb8a2e1u32, 0xbf7ffe6du32, 0x3f7ffe1au32, 0x3f7ffecau32, 0xbf7ffe5bu32, - 0xbf3504f2u32, 0x3f7ffea9u32, 0xbf7ffe38u32, 0xbf7ffe53u32, 0x3f7ffe15u32, 0xbf7ffe13u32, 0xbf7ffe41u32, 0xbf7ffe01u32, - 0xbf7ffe4au32, 0xbf7ffe0eu32, 0xbf7ffe90u32, 0x3f7fff0fu32, 0x3f7ffe52u32, 0x3f7ffe26u32, 0x3f7ffe2du32, 0x3f7ffeb6u32, - 0x3f7ffe2fu32, 0x3beef249u32, 0x3f7ffee3u32, 0x3f7ffef8u32, 0x3b93df58u32, 0x3f7ffe77u32, 0x3f7ffec8u32, 0xbf7ffe88u32, - 0x3f7ffecfu32, 0x3be34790u32, 0x3bdd59beu32, 0x3f7ffe92u32, 0xbf7fff2eu32, 0x3f7fff71u32, 0xbf7ffe9cu32, 0xbf7ffe42u32, - 0xbf7ffe45u32, 0x3f7ffe8au32, 0x3f7ffefdu32, 0x3f7ffe68u32, 0x3f7ffef9u32, 0xbf7ffe23u32, 0xbf7ffe09u32, 0x3f7ffe94u32, - 0xbf7ffe22u32, 0xbf7ffe66u32, 0x3f7fff24u32, 0xbbd8723bu32, 0xbf7fffbeu32, 0xbf7ffec0u32, 0xbf7ffe8eu32, 0x3f7ffe10u32, - 0x3f7ffe49u32, 0x3f7ffe03u32, 0xbf7ffe76u32, 0x3f7fff70u32, 0x3f7fff6bu32, 0x3f7ffe20u32, 0x3f7ffe07u32, 0x3f7ffe2eu32, - 0x3f7ffef1u32, 0x3f7ffe74u32, 0xbf7ffe11u32, 0x3bfe4af1u32, 0xbf7ffe64u32, 0x3f7ffe62u32, 0xbbec42cau32, 0xbf7fff48u32, - 0xbf7fff1au32, 0x3f7ffe97u32, 0x3bf48ba8u32, 0xbbf9ecb5u32, 0x3f7ffe8bu32, 0x3f7ffe26u32, 0x3be884a2u32, 0xbf7ffe13u32, - 0xbf7ffe38u32, 0xbbddae92u32, 0x3f7fff33u32, 0x3bc947a9u32, 0x3bdf1847u32, 0x3f7ffe6eu32, 0x3f7ffe4eu32, 0x3f7ffe87u32, - 0x3f7ffe3cu32, 0x3f7ffea5u32, 0xbf7fff3bu32, 0xbf7ffe3au32, 0x3f7fff0fu32, 0x3f7ffe2fu32, 0xbf7fff7au32, 0xbf7fffe5u32, - 0xbbf22fd6u32, 0x3bdddfccu32, 0x3f7fff08u32, 0x3f7ffed9u32, 0x3f7ffe2eu32, 0xbf7ffe99u32, 0x3bdffa9au32, 0x3f7ffe36u32, - 0x3f7ffeceu32, 0xbf7ffed3u32, 0x3f7ffe83u32, 0x3f7ffe65u32, 0x3be2003cu32, 0xbbee713au32, 0x3f7ffe6bu32, 0xbf7ffe5du32, - 0x3f7ffe55u32, 0xbba0d5c6u32, 0xbf7ffe88u32, 0xbf7ffe09u32, 0x3f7fff46u32, 0x3f7ffeaau32, 0xbf7ffe7du32, 0xbf7ffe98u32, - 0xbf7ffe53u32, 0x3f7ffe05u32, 0x3f7ffe63u32, 0xbf7fff7eu32, 0xbf7ffed1u32, 0x3f7ffec3u32, 0xbf7ffe7eu32, 0x3f7fff5cu32, - 0xbf7ffe81u32, 0x3f7ffedau32, 0xbbc6d2aeu32, 0xbf7ffe31u32, 0x3be9d497u32, 0xbf7fff35u32, 0x3bd74c83u32, 0x3b515e2du32, - 0xbf7ffe9du32, 0xbf7fff11u32, 0x3b9b0b54u32, 0xbf7fffcbu32, 0x3bff43f9u32, 0xbf7ffedau32, 0x3f7ffe81u32, 0xbbe3e073u32, - 0x3f7ffef3u32, 0x3f7ffe98u32, 0xbf7ffe16u32, 0xbf7ffe2fu32, 0xbf7ffeb0u32, 0xbf7ffe8cu32, 0xbf7ffe94u32, 0x3f7ffe66u32, - 0x3bf2dafeu32, 0xbf7ffe2fu32, 0x3bfc307du32, 0x3f7ffe88u32, 0xbbabdf55u32, 0xbbc94889u32, 0x3f7ffe98u32, 0x3f7ffe11u32, - 0x3f7ffe81u32, 0x3f7fff1fu32, 0x3f7fff35u32, 0x3f7fff3fu32, 0x3f7ffe9du32, 0xbf7ffe7au32, 0xbf7ffe19u32, 0x3be9c065u32, - 0xbbd8aa10u32, 0xbf7ffecbu32, 0xbbd04ee8u32, 0xbf7ffe1fu32, 0x3f7ffe16u32, 0x3f7ffeb0u32, 0xbf7ffe06u32, 0x3bea000eu32, - 0xbf7ffe66u32, 0x3f7ffec9u32, 0x3bf86506u32, 0x3f7fff71u32, 0xbf7ffe88u32, 0x3b983c73u32, 0x3f7ffe26u32, 0xbf7ffe11u32, - 0x3b9d025cu32, 0x3f7ffe19u32, 0xbf7ffe27u32, 0x3f7fffddu32, 0xbf7ffe2cu32, 0x3f7ffe0fu32, 0xbf7ffe5eu32, 0x3bf5ffbcu32, - 0x3f7ffe1eu32, 0x3f7fff23u32, 0x3f7fff50u32, 0xbf7ffe7au32, 0x3bebfe48u32, 0x3f7fff0du32, 0xbf7ffeb8u32, 0x3f7ffe57u32, - 0xbf7fffe9u32, 0x3f7ffe16u32, 0x3f7ffe0au32, 0xbf7ffe06u32, 0xbf7ffe4bu32, 0x3f7ffea2u32, 0x3f7ffee0u32, 0x3f7ffea8u32, - 0x3f7ffe26u32, 0x3f7ffe5fu32, 0x3f7ffe97u32, 0x3f7ffe5au32, 0x3f7ffe95u32, 0xbf7ffe2cu32, 0xbf7fff41u32, 0xbbe78eb4u32, - 0xbf7ffedbu32, 0x3f7ffe39u32, 0xbf7ffeedu32, 0x3f7ffee8u32, 0xbf7ffe1fu32, 0xbf7ffe3du32, 0x3f7ffe75u32, 0x3f7ffe4fu32, - 0xbf7fff0au32, 0xbf7ffe96u32, 0x3f7ffe93u32, 0x3bea7a8du32, 0x3f7ffe85u32, 0xbf7fffb3u32, 0x3bd5be25u32, 0x3f7ffec1u32, - 0x3f7ffe65u32, 0x3f7ffe8au32, 0x3f7ffeceu32, 0x3bda130fu32, 0xbf7ffefeu32, 0x3f7ffe13u32, 0x3f7ffe34u32, 0x3f7ffec6u32, - 0x3f7ffe6au32, 0x3f7ffef7u32, 0xbf7ffed4u32, 0xbbe18e63u32, 0xbf7fff56u32, 0xbf7ffe60u32, 0xbf7ffe82u32, 0x3f7ffe01u32, - 0x3f7fff12u32, 0xbf7ffec2u32, 0x3f7ffe44u32, 0xbf7ffefau32, 0x3f7ffeafu32, 0x3f7ffe6du32, 0x3f7ffe43u32, 0xbf7fff7cu32, - 0x3f7fffaau32, 0xbbf5a568u32, 0xbf7ffe92u32, 0x3f7ffe42u32, 0xbf7ffe0cu32, 0xbf7ffe7cu32, 0xbf7fff68u32, 0x3f7fff98u32, - 0xbf7ffed5u32, 0xbf7ffe9au32, 0x3f7fff9bu32, 0xbf7ffe55u32, 0x3f7ffe5du32, 0xbf7ffe62u32, 0x3f7ffe8eu32, 0xbf7ffe14u32, - 0x3f7ffe4eu32, 0x3f7ffe09u32, 0x3f7fffadu32, 0x3f7ffe9eu32, 0xbf7ffe4au32, 0x3f7ffe47u32, 0x3f7ffe9cu32, 0xbf7fff32u32, - 0xbbfbcb18u32, 0xbf7fff72u32, 0x3f7ffe35u32, 0x3f7fff86u32, 0xbf7fff83u32, 0x3f7ffe84u32, 0x3f7ffe12u32, 0x3f7ffef2u32, - 0xbf7ffedeu32, 0x3f7ffe9bu32, 0xbf7ffeb5u32, 0xbf7ffe6cu32, 0xbf7ffef9u32, 0x3f7fff0bu32, 0x3f7ffe69u32, 0x3f7fff5du32, - 0x3f7ffe5cu32, 0x3f7ffe5bu32, 0xbf7ffe2du32, 0x3f7ffe14u32, 0xbf7fff17u32, 0xbf7ffe86u32, 0x3f7fff62u32, 0xbf7ffe34u32, - 0x3f7ffe3cu32, 0xbf7ffed9u32, 0x3bd6843fu32, 0x3f7ffe79u32, 0x3f7ffe2cu32, 0xbf7fff11u32, 0x3f7ffe27u32, 0x3f7ffe06u32, - 0x3f7ffe4bu32, 0x3ba7410du32, 0xbf7ffe43u32, 0xbf7fff79u32, 0xbf7ffe24u32, 0xbb9664b9u32, 0x3f7ffe56u32, 0x3b8fd5d6u32, - 0xbf7ffe2eu32, 0xbf7ffe1bu32, 0x3f7ffe49u32, 0xbf7fff5bu32, 0x3f7ffe50u32, 0x3f7ffe0eu32, 0xbbdb1032u32, 0xbf7ffe7eu32, - 0x3f7ffe63u32, 0x3f7ffeaau32, 0xbf7ffe1du32, 0xbf7ffe60u32, 0xbf7ffeb8u32, 0x3f7ffeddu32, 0xbbfd2394u32, 0x3f7fff08u32, - 0x3f7ffeefu32, 0xbf7fff54u32, 0x3f7ffe2eu32, 0xbf7ffe2au32, 0x3f7ffe87u32, 0xbf7ffee2u32, 0x3f7ffe4du32, 0x3bf7f77au32, - 0xbf7ffef6u32, 0xbbb37986u32, 0x3f7fff88u32, 0x3f7ffe82u32, 0xbbb19c80u32, 0xbf7ffe57u32, 0x3f7ffe8fu32, 0x3f7ffe59u32, - 0x3f7ffea4u32, 0x3f7ffe78u32, 0xbbe83b1fu32, 0x3f7ffe9cu32, 0xbf7ffebeu32, 0x3f7ffe5du32, 0x3f7ffe6du32, 0x3f7ffe44u32, - 0x3f7ffe19u32, 0xbf7ffe1fu32, 0x3f7ffeeau32, 0xbf7ffe0bu32, 0x3f7ffe9au32, 0x3f7ffe05u32, 0xbf7ffe0au32, 0x3f7ffea6u32, - 0x3f7fffe7u32, 0xbf7fff78u32, 0x3f7ffe8bu32, 0x3f7ffe36u32, 0xbbe0ad1du32, 0xbf7ffeb3u32, 0xbbf8cfdeu32, 0xbf7ffe2bu32, - 0x3f7ffe07u32, 0xbe8dbae5u32, 0x3f7ffe2du32, 0x3f7ffefdu32, 0xbf7ffe9fu32, 0x3f7ffe32u32, 0xbf7fff03u32, 0x3f7ffec3u32, - 0x3f7ffe20u32, 0xbf7ffe91u32, 0xbf7ffeb7u32, 0x3f7ffe64u32, 0x3b8a2421u32, 0x3bcfb11cu32, 0x3be20d0eu32, 0xbf7ffe3fu32, - 0x3be25235u32, 0x3f7ffe73u32, 0x3f7ffe08u32, 0x3bd48859u32, 0x3f7ffe4bu32, 0xbf7ffeafu32, 0xbf7ffe69u32, 0xbf7ffed7u32, - 0x3f7fff18u32, 0x3f7ffe1au32, 0x3f7ffe54u32, 0xbf7ffe51u32, 0x3f7ffe02u32, 0xbf7ffe83u32, 0xbf7ffe74u32, 0xbf7ffe4eu32, - 0x3f7ffe0cu32, 0xbf7ffe75u32, 0xbbd773f2u32, 0x3f7ffe5fu32, 0x3bf1f877u32, 0xbf7ffe6fu32, 0xbf7ffe76u32, 0xbf7ffe21u32, - 0xbf7ffe81u32, 0xbf7ffeb5u32, 0xbf7ffe77u32, 0xbf7ffec5u32, 0xbf7fff07u32, 0xbf7ffea6u32, 0x3f7ffecdu32, 0xbf7ffe63u32, - 0xbf7ffe79u32, 0x3f7ffea5u32, 0xbf7ffe27u32, 0x3f7ffeebu32, 0x3f7ffe60u32, 0x3b94d5c9u32, 0x3f7ffe35u32, 0xbf7fff49u32, - 0xbbe38024u32, 0x3f7ffe22u32, 0xbbfdef08u32, 0x3f7ffe4au32, 0x3f7ffe30u32, 0xbf7ffe47u32, 0x3f7ffe65u32, 0xbf7ffe72u32, - 0xbf7ffe58u32, 0xbf7ffe86u32, 0xbf7ffe7cu32, 0x3bcf1332u32, 0x3bffd2b8u32, 0x3f7ffef5u32, 0xbf7ffe94u32, 0xbf7ffe93u32, - 0x3f7ffec4u32, 0xbbc42fe0u32, 0x3f7ffe95u32, 0x3f7ffe5du32, 0x3f7ffea4u32, 0xbf7ffed8u32, 0xbf7ffe50u32, 0x3f7ffe8bu32, - 0x3f7fff5au32, 0x3bd7b327u32, 0xbbecc2c2u32, 0xbf7fff27u32, 0x3f7fff09u32, 0x3f7fff25u32, 0xbbf97ae4u32, 0x3f7ffe3bu32, - 0xbf7ffe1cu32, 0x3f7ffe09u32, 0xbf7ffe4cu32, 0xbf7ffed1u32, 0xbf7ffe34u32, 0xbf7ffe14u32, 0xbf7ffec6u32, 0xbf7ffe7bu32, - 0x3f7ffe28u32, 0x3f7ffee5u32, 0xbf7ffefau32, 0x3f7ffe97u32, 0xbf7ffeccu32, 0x3f7ffec7u32, 0xbbed4342u32, 0x3f7ffeaeu32, - 0xbf7ffe90u32, 0xbf7ffe26u32, 0x3f7fff8au32, 0xbf7ffe25u32, 0xbf7fff12u32, 0x3bd01e4bu32, 0x3f7ffeacu32, 0x3f7ffe8eu32, - 0xbf7ffe4fu32, 0x3f7fff76u32, 0xbf7ffe07u32, 0xbf7ffe2au32, 0x3f7fff5fu32, 0x3f7ffee9u32, 0x3f7ffe29u32, 0xbf7ffec2u32, - 0xbf7fff1cu32, 0x3f7ffefeu32, 0xbf7ffe48u32, 0x3f7ffe05u32, 0x3bdfe958u32, 0x3f7ffe13u32, 0xbf7ffe24u32, 0x3f7fff7fu32, - 0xbf7ffefdu32, 0xbf7fff0au32, 0xbf7ffedcu32, 0x3f7fff40u32, 0xbf7fff15u32, 0x3f7ffe0eu32, 0x3f7ffe6du32, 0x3f7ffe44u32, - 0xbf7ffe46u32, 0x3f7ffe0bu32, 0x3bd9860du32, 0x3f7ffe89u32, 0x3f7ffee3u32, 0x3f7fff81u32, 0xbf7ffe12u32, 0xbf7ffe4bu32, - 0x3f7ffed5u32, 0x3f7ffef3u32, 0xbf7ffeecu32, 0xbf7ffe3au32, 0xbb5e506cu32, 0xbf7fff13u32, 0xbbef8ea5u32, 0x3f7ffe5fu32, - 0x3b99bb23u32, 0x3f7ffe0cu32, 0xbf7ffeafu32, 0x3f7ffe08u32, 0xbf7ffe91u32, 0xbbf57223u32, 0xbf7ffe8du32, 0xbbe17857u32, - 0x3f7ffee7u32, 0xbbf3500eu32, 0x3f7ffe18u32, 0x3f7ffedau32, 0xbf7ffe10u32, 0xbf7ffe3cu32, 0x3f7ffecau32, 0xbbd17bfau32, - 0xbf7ffe71u32, 0xbf7ffee0u32, 0xbf7ffeabu32, 0x3f7ffe3du32, 0xbf7ffeacu32, 0xbbed4262u32, 0xbf7ffe09u32, 0x3f7ffe1cu32, - 0x3f7ffe8cu32, 0x3bf43cd7u32, 0xbf7ffe5au32, 0xbf7ffe95u32, 0x3f7ffe26u32, 0xbf7ffe19u32, 0x3f7ffe64u32, 0x3f7fff29u32, - 0xbf7ffef4u32, 0x3f7ffe02u32, 0x3f7ffe11u32, 0xbbfbaa13u32, 0x3bf74d30u32, 0x3f7ffe5bu32, 0x3f7ffe6fu32, 0x3f7ffe0bu32, - 0xbf7fff19u32, 0xbf7ffe7bu32, 0x3f7fff8du32, 0x3f7ffee3u32, 0x3f7fff11u32, 0x3bb402dcu32, 0xbbf2b471u32, 0x3bf90742u32, - 0x3f7ffeebu32, 0x3f7ffe2du32, 0x3f7ffe0fu32, 0xbf7ffe57u32, 0xbf7ffe33u32, 0x3f7fff06u32, 0x3baa59b5u32, 0x3f7ffe34u32, - 0xbf7ffecbu32, 0xbbbff7f6u32, 0xbbf5a5ebu32, 0xbf7ffe10u32, 0x3f7ffedfu32, 0xbba81630u32, 0x3f7ffe79u32, 0xbf7ffe7fu32, - 0xbf7fff7du32, 0x3f7ffe29u32, 0xbf7ffe4fu32, 0x3becc792u32, 0x3f7ffe94u32, 0x3f7fff14u32, 0x3f7ffe0cu32, 0x3f7ffec3u32, - 0xbf7ffe84u32, 0xbbfed837u32, 0x3bf67a62u32, 0xbf7ffef9u32, 0x3f7ffed3u32, 0x3f7ffe5cu32, 0xbf7ffe1au32, 0x3f7ffe99u32, - 0xbf7ffe2au32, 0x3f7ffe78u32, 0xbf7fff6cu32, 0x3f7ffe38u32, 0x3f7ffe20u32, 0x3f7ffeb1u32, 0xbbf9bc67u32, 0xbf7ffe52u32, - 0xbf7ffe60u32, 0x3f7ffe6au32, 0xbf7fff9au32, 0xbbb1496bu32, 0x3f7ffe81u32, 0xbf7ffe03u32, 0x3f7ffea2u32, 0xbf7ffe37u32, - 0xbf7ffe64u32, 0x3f7fff6du32, 0x3bee7b73u32, 0x3f7ffe7eu32, 0xbf7fff94u32, 0x3f7ffe4bu32, 0x3bec020cu32, 0xbf7ffe04u32, - 0xbf7ffe26u32, 0xbf7ffe13u32, 0x3f7ffe43u32, 0xbf7ffebeu32, 0xbf7ffeaeu32, 0x3bf3b63au32, 0xbf7ffe05u32, 0x3f7ffe10u32, - 0x3f7ffe71u32, 0xbf7ffe41u32, 0x3f7ffe40u32, 0xbbf10103u32, 0xbf7ffe9au32, 0x3f7ffea1u32, 0xbf7ffe57u32, 0xbbca0194u32, - 0x3f7ffe45u32, 0xbbcc697cu32, 0xbbf63a43u32, 0x3f7ffebbu32, 0xbf7ffe0au32, 0xbf7ffed0u32, 0xbf7fff44u32, 0x3f7fff20u32, - 0xbf7ffe09u32, 0xbf7ffe12u32, 0x3f7ffec1u32, 0xbbf35a1bu32, 0xbf7ffefcu32, 0x3f7ffe01u32, 0x3f7ffe32u32, 0xbf7ffe96u32, - 0x3f7ffea4u32, 0x3f7ffe4cu32, 0xbf7ffe88u32, 0xbf7ffe9fu32, 0x3bc56621u32, 0xbf7ffea8u32, 0x3f7ffe8cu32, 0x3f7ffe2cu32, - 0xbf7ffe31u32, 0xbf7ffeb7u32, 0x3bd6542eu32, 0x3f7fff18u32, 0xbbdeb4fdu32, 0xbf7ffedau32, 0x3f7ffedcu32, 0x3f7ffeefu32, - 0xbf7ffe8eu32, 0x3f7ffe58u32, 0x3f7ffe39u32, 0xbf7ffe53u32, 0x3f7ffe19u32, 0xbf7ffe1fu32, 0x3f7ffe6du32, 0xbf7ffeb0u32, - 0xbf7fff2au32, 0x3f7ffefdu32, 0xbc000b2du32, 0x3f7fff9cu32, 0xbf7ffe38u32, 0xbf7ffebau32, 0x3f7ffe0cu32, 0x3f7ffee1u32, - 0xbf7ffe66u32, 0x3f7fff1du32, 0x3f7ffe1eu32, 0xbf7ffef7u32, 0x3bccdfb9u32, 0xbf7fff75u32, 0xbf7ffeb3u32, 0xbf7ffe67u32, - 0xbf7ffe55u32, 0xbbec213cu32, 0xbf7ffec4u32, 0x3bfe58a5u32, 0x3f7ffe0du32, 0x3f7ffe24u32, 0x3f7fff22u32, 0x3f7ffe1au32, - 0x3bdf1975u32, 0xbbe77a43u32, 0x3f7fff29u32, 0xbf7ffed2u32, 0xbf7ffe4eu32, 0xbf7ffe6eu32, 0xbb90870bu32, 0xbf7fff2bu32, - 0xbf7ffe22u32, 0xbf7ffe5eu32, 0xbf7ffe36u32, 0xbf7ffecdu32, 0x3afd496au32, 0x3f7ffe27u32, 0x3f7ffe18u32, 0x3f7ffef4u32, - 0xbf7fff97u32, 0x3f7ffeadu32, 0x3f7ffe0bu32, 0xbf7ffe2bu32, 0xbbc4b51bu32, 0xbf7ffe68u32, 0x3f7ffe34u32, 0x3f7ffe5fu32, - 0x3f7ffe03u32, 0xbf7ffea7u32, 0x3f7ffe64u32, 0x3f7ffe26u32, 0xbf7ffe4cu32, 0xbf7ffe97u32, 0xbf7ffeefu32, 0x3f7ffeb5u32, - 0xbf7ffeacu32, 0xbf7ffe60u32, 0x3f7ffe7eu32, 0xbf7ffeccu32, 0xbf7ffe61u32, 0xbf7ffe62u32, 0x3f7ffe3bu32, 0x3f7fff83u32, - 0xbf7ffec3u32, 0x3f7ffe0au32, 0xbf7fff08u32, 0x3f7ffea9u32, 0xbf7ffe92u32, 0xbf7fff3fu32, 0xbf7ffe8au32, 0x3bfd3459u32, - 0x3f7ffe51u32, 0xbf7fff0eu32, 0x3f7ffe69u32, 0x3f7ffe14u32, 0xbf7ffe4au32, 0x3f7fff50u32, 0xbf7ffe1cu32, 0xbf7ffe5au32, - 0x3bef98bbu32, 0x3f7ffe23u32, 0xbf7ffea0u32, 0xbf7ffe9eu32, 0xbf7ffe4fu32, 0xbf7ffe82u32, 0x3f7ffe90u32, 0x3f7ffe40u32, - 0xbf7fff3cu32, 0xbbe9efbau32, 0xbf7fff67u32, 0xbf7ffeb2u32, 0xbf7ffe6au32, 0x3f7ffe59u32, 0xbf7ffeecu32, 0x3f7ffe08u32, - 0xbf7fff5au32, 0x3bd44aaau32, 0x3f7ffec9u32, 0x3b922b34u32, 0x3f7fff33u32, 0x3bf2e930u32, 0x3f7ffe4bu32, 0xbf7fff28u32, - 0x3f7fff4fu32, 0x3f7ffe32u32, 0xbf7ffe3eu32, 0x3f7ffe30u32, 0x3f7fffc1u32, 0xbbfe242au32, 0xbf7ffe9du32, 0x3f7ffe09u32, - 0xbf7ffe39u32, 0x3f7ffebau32, 0x3f7ffe47u32, 0xbf7ffefbu32, 0x3f7ffe8bu32, 0x3f7ffe0fu32, 0xbf7ffe17u32, 0xbf7ffec7u32, - 0x3f7ffe6eu32, 0x3f7ffe04u32, 0x3b8a6d81u32, 0xbf7ffe19u32, 0xbbffd0d9u32, 0x3f7ffe8eu32, 0xbf7ffee0u32, 0xbf7ffeb7u32, - 0xbf7fff52u32, 0xbf7ffe06u32, 0x3f7ffe24u32, 0x3f7fff31u32, 0x3f7ffe1eu32, 0x3f7ffeaeu32, 0xbbe80dddu32, 0x3f7ffefcu32, - 0xbf7ffeb1u32, 0x3f7ffeb9u32, 0x3f7ffe4fu32, 0xbf7ffe23u32, 0xbf7ffe12u32, 0x3f7ffe92u32, 0x3f7ffe97u32, 0x3f7ffea7u32, - 0xbf7ffe03u32, 0xbf7ffe34u32, 0x3f7ffed0u32, 0x3bc34b51u32, 0xbf7ffe7eu32, 0x3beb8f44u32, 0xbf7ffe51u32, 0x3f7ffe9cu32, - 0xbf7fff01u32, 0xbf7ffe3au32, 0x3f7ffedfu32, 0x3bf05bb0u32, 0x3f7ffe0bu32, 0x3f7ffe27u32, 0xbbdb6c98u32, 0x3f7ffe1du32, - 0xbf7ffe8fu32, 0x3f7ffee3u32, 0xbf7ffedeu32, 0xbf7fff4au32, 0x3f7ffe38u32, 0xbba701b6u32, 0x3f7ffe5bu32, 0xbf7fff21u32, - 0xbf7ffef6u32, 0x3f7ffe18u32, 0xbf7ffe65u32, 0xbf7ffec2u32, 0x3f7ffeb5u32, 0xbf7fff57u32, 0x3f7ffe99u32, 0xbf7ffe48u32, - 0xbf7ffe6bu32, 0x3f7ffe2cu32, 0xbf7ffe25u32, 0xbf7ffea1u32, 0x3f7ffe41u32, 0xbf7ffec6u32, 0x3f7ffe55u32, 0x3f7ffe8cu32, - 0xbbead006u32, 0xbf7fff18u32, 0x3f7ffe74u32, 0xbf7ffe8du32, 0xbf7ffe2eu32, 0x3f7ffeddu32, 0xbf7fff47u32, 0x3f7ffee5u32, - 0x3f7fff07u32, 0xbf7ffeb3u32, 0xbf7ffe4bu32, 0x3f7fff04u32, 0xbbfe2287u32, 0x3f7ffe37u32, 0x3bfd32b6u32, 0x3f7ffe79u32, - 0x3f7ffecbu32, 0xbbdc708bu32, 0x3f7ffe42u32, 0x3f7ffe03u32, 0xbf7fff99u32, 0xbf7ffebfu32, 0xbf7ffe13u32, 0xbf7fff23u32, - 0x3f7ffed9u32, 0x3f7ffe7cu32, 0xbf7ffee8u32, 0x3f7ffed6u32, 0x3f7ffe07u32, 0xbbcb3213u32, 0xbf7ffea5u32, 0x3f7ffe5au32, - 0x3f7ffe62u32, 0xbf7ffee6u32, 0xbf7ffe5cu32, 0xbf7ffe0cu32, 0xbf7ffe64u32, 0x3f7ffe28u32, 0x3f7ffef8u32, 0xbf7ffe71u32, - 0x3f7fff1fu32, 0x3f7fff2eu32, 0xbbf9425du32, 0x3f7ffe29u32, 0xbf7ffec7u32, 0xbf7ffe6au32, 0x3f7ffe30u32, 0x3f7ffe0fu32, - 0xbf7ffe98u32, 0x3f7ffea0u32, 0x3bff55c6u32, 0x3be91a57u32, 0xbf7ffe9bu32, 0xbf7fff3bu32, 0xbf7ffe1fu32, 0xbf7ffe49u32, - 0x3f7ffe77u32, 0xbf7ffe2du32, 0x3f7ffeb4u32, 0x3f7ffe52u32, 0x3f7ffe3cu32, 0x3f7ffe0au32, 0xbf7ffe02u32, 0xbf7fff9bu32, - 0xbf7ffe8eu32, 0x3f7ffe3eu32, 0x3f7ffe82u32, 0xbf7fffb6u32, 0xbf7ffe51u32, 0xbf7ffe34u32, 0x3f7ffeaeu32, 0x3f7fff31u32, - 0xbf7ffe0bu32, 0xbf7ffe04u32, 0xbf7ffe9au32, 0xbf7fff39u32, 0x3f7ffe80u32, 0x3bb6b63au32, 0x3bfa486eu32, 0x3f7ffe0cu32, - 0x3f7ffe64u32, 0xbf7ffea2u32, 0x3f7ffe3fu32, 0xbf7ffebdu32, 0x3f7ffe31u32, 0xbf7ffe0du32, 0x3f7ffe22u32, 0x3be124dcu32, - 0xbf7ffe68u32, 0x3b86e4c4u32, 0xbf7ffe20u32, 0xbf7ffe9eu32, 0xbf7ffe85u32, 0xbf7ffe1au32, 0xbf7ffec0u32, 0x3f7ffe87u32, - 0xbf7ffec9u32, 0x3bf95600u32, 0x3f7ffe56u32, 0x3beca99au32, 0x3f7ffe57u32, 0xbf7fff48u32, 0x3f7ffe59u32, 0xbf7fff95u32, - 0xbf7ffe37u32, 0x3f7ffec6u32, 0x3bfba495u32, 0xbf7fff54u32, 0x3f7ffe46u32, 0xbf7fff75u32, 0x3f7ffe21u32, 0xbf7ffe6cu32, - 0x3f7ffe37u32, 0xbf7fff20u32, 0xbf7ffe59u32, 0x3f7ffe85u32, 0x3f7ffe68u32, 0xbf7ffe47u32, 0xbf7ffe22u32, 0x3f7ffe0du32, - 0x3f7fff84u32, 0x3f7fff19u32, 0xbf7ffe64u32, 0xbf7ffe0cu32, 0xbf7ffef1u32, 0x3f7ffe9au32, 0x3f7ffe0bu32, 0x3f7ffe28u32, - 0xbf7ffecau32, 0xbf7ffe0au32, 0x3f7ffe2du32, 0xbbd204f0u32, 0x3f7ffe1fu32, 0x3f7ffe1cu32, 0x3f7ffe15u32, 0xbf7ffe22u32, - 0x3f7ffe85u32, 0xbf7ffe57u32, 0x3beef07fu32, 0xbf7ffe86u32, 0x3f7ffed8u32, 0xbf7ffe01u32, 0x3f7ffe8au32, 0x3f7ffe5fu32, - 0x3f7ffe31u32, 0x3f7ffee0u32, 0x3f7ffe3fu32, 0xbf7fff04u32, 0xbb64d34du32, 0x3f7ffe27u32, 0xbf7ffe25u32, 0x3f7ffe17u32, - 0xbf7ffe1eu32, 0x3f7ffe3bu32, 0xbf7ffe9eu32, 0x3f7ffe75u32, 0x3f7ffe58u32, 0x3f7ffe46u32, 0x3bfd9050u32, 0x3f7ffe70u32, - 0x3f7fff0au32, 0x3bf33e84u32, 0xbf7ffeebu32, 0xbf7ffee6u32, 0x3f7ffe28u32, 0x3f7ffe90u32, 0xbf7ffe69u32, 0x3f7ffe09u32, - 0xbf7ffebeu32, 0x3f7ffe4cu32, 0x3bf75321u32, 0xbf7ffe0eu32, 0x3f7ffe9cu32, 0xbf7ffe97u32, 0x3f7fff05u32, 0xbf7ffe4fu32, - 0x3f7ffe5eu32, 0xbf7ffe1au32, 0xbbec00bfu32, 0x3bccd7a1u32, 0xbf7ffed6u32, 0xbf7ffe30u32, 0xbf7fff77u32, 0x3f7fff83u32, - 0xbf7ffe73u32, 0x3f7ffe15u32, 0xbf7ffeb1u32, 0x3f7fff7eu32, 0x3f7fff26u32, 0x3f7ffe44u32, 0x3f7fff4au32, 0xbf7fff50u32, - 0x3f7ffe6eu32, 0x3f7ffef5u32, 0x3f7ffee9u32, 0xbf7ffe63u32, 0x3f7ffe81u32, 0x3bd26f27u32, 0xbf7ffe91u32, 0xbbf75c90u32, - 0xbf7ffe04u32, 0xbf7fff8du32, 0x3f7fff0eu32, 0x3be9a462u32, 0xbf7ffebau32, 0xbf7fff2bu32, 0x3f7ffeb8u32, 0x3be98f8du32, - 0x3f7ffe96u32, 0xbf7fff14u32, 0x3f7ffe3au32, 0xbbd3bad9u32, 0xbf7ffe26u32, 0xbf7fff51u32, 0x3f7ffee1u32, 0xbf7ffe33u32, - 0xbf7ffe72u32, 0xbbeb85b8u32, 0x3f7ffef7u32, 0x3f7ffee5u32, 0x3bf31c16u32, 0xbf7fff35u32, 0xbf7ffe8fu32, 0xbf7ffe66u32, - 0x3f7ffe0eu32, 0x3f7ffeffu32, 0xbf7ffeafu32, 0x3f7ffedeu32, 0xbbed7a28u32, 0x3f7fff42u32, 0xbf7ffe3bu32, 0x3bfa8e39u32, - 0x3bdcb876u32, 0xbf7ffe05u32, 0xbf7ffe21u32, 0x3becfaf7u32, 0xbf7ffe60u32, 0xbbe0c164u32, 0xbf7fff19u32, 0x3bca6a98u32, - 0xbf7ffe3cu32, 0x3f7fffdeu32, 0x3f7ffe4au32, 0xbf7ffe2cu32, 0x3f7ffe2fu32, 0x3f7ffeb5u32, 0x3f7ffe07u32, 0xbf7ffe48u32, - 0xbf7ffefbu32, 0x3f7ffe41u32, 0xbf7fff49u32, 0xbbfc64e8u32, 0xbf7fff6cu32, 0x3f7ffe2au32, 0x3f7fff06u32, 0xbb95adf0u32, - 0x3f7ffe93u32, 0xbf7ffe01u32, 0xbbc54106u32, 0x3f7ffe2eu32, 0xbf7ffe32u32, 0xbbf6fc82u32, 0xbf7ffe6du32, 0xbf7ffe83u32, - 0x3f7ffe24u32, 0x3f7fff76u32, 0xbf7fff2eu32, 0x3f7ffe78u32, 0xbf7ffe5bu32, 0xbf7ffe3au32, 0x3f7ffea8u32, 0x3f7ffeaau32, - 0x3f7fff30u32, 0xbf7ffe0bu32, 0x3bed88dcu32, 0xbbed09abu32, 0xbf7ffe99u32, 0xbf7ffe34u32, 0xbf7ffeb4u32, 0x3f7ffe98u32, - 0x3f7ffe79u32, 0xbbd57f26u32, 0x3f7ffe52u32, 0x3f7ffe14u32, 0x3f7ffe53u32, 0xbf7ffe0au32, 0xbf7fff39u32, 0xbf7ffec0u32, - 0xbf7fff52u32, 0xbf7fff59u32, 0xbba015b5u32, 0xbf7ffe77u32, 0x3f7ffeacu32, 0x3bef6cddu32, 0x3f7ffe30u32, 0xbf7ffe31u32, - 0x3f7fff04u32, 0xbf7ffe70u32, 0xbbe0cda6u32, 0xbf7ffee9u32, 0x3f7ffe63u32, 0x3bc6b76fu32, 0xbf7ffeadu32, 0x3f7ffe4du32, - 0xbbfeaf76u32, 0x3f7ffeccu32, 0x3f7ffe5au32, 0xbbdb72afu32, 0x3f7ffe28u32, 0x3bfc173du32, 0x3f7ffe92u32, 0x3bd0b61bu32, - 0x3f7ffe43u32, 0xbf7fff0du32, 0xbf7ffe94u32, 0x3f7ffe10u32, 0xbf7ffe11u32, 0xbbd52fbau32, 0xbf7ffecfu32, 0xbf7ffe2du32, - 0xbf7ffe6au32, 0xbf7ffe8bu32, 0xbf7ffe66u32, 0xbf7fff14u32, 0x3f7fff0eu32, 0x3f7ffef3u32, 0x3f7ffeedu32, 0x3f7ffee3u32, - 0x3f7ffe96u32, 0xbbafee0bu32, 0xbf7ffe95u32, 0x3f7ffe47u32, 0xbf7fff47u32, 0xbf7fff7du32, 0xbf7fff01u32, 0x3beda121u32, - 0x3f7ffe7bu32, 0xbf7fff24u32, 0xbf7ffe35u32, 0xbf7ffe40u32, 0xbf7ffe47u32, 0x3f7fff8fu32, 0xbf7fff7bu32, 0x3f7ffea0u32, - 0x3f7ffedcu32, 0x3f7ffeabu32, 0xbf7ffed0u32, 0x3f7ffe1bu32, 0xbf7ffe48u32, 0xbf7ffe8au32, 0x3bd912feu32, 0xbf7ffeafu32, - 0xbf7ffe91u32, 0x3f7ffe23u32, 0xbf7ffe1bu32, 0xbf7fff2fu32, 0x3f7ffebfu32, 0xbf7ffe23u32, 0x3f7ffe16u32, 0xbf7ffec1u32, - 0x3f7fff5cu32, 0xbf7ffe16u32, 0x3b8bda1fu32, 0xbf7ffe26u32, 0xbf7ffe7eu32, 0xbf7ffe20u32, 0xbf7fff16u32, 0xbf7ffe3fu32, - 0xbf7fff21u32, 0xbbd9711eu32, 0x3f7ffee0u32, 0xbf7fff67u32, 0x3f7ffe3fu32, 0xbf7ffe84u32, 0xbf7ffe0du32, 0xbf7ffe56u32, - 0x3f7ffe04u32, 0xbf7ffe41u32, 0x3f7ffe57u32, 0x3f7ffe55u32, 0x3f7ffe59u32, 0x3f7ffe27u32, 0x3f7fff54u32, 0x3f7ffec3u32, - 0xbf7ffe54u32, 0x3f7ffe2eu32, 0x3f7ffee9u32, 0xbf7ffe21u32, 0xbf7fff6au32, 0xbbda3f55u32, 0x3f7ffe01u32, 0xbf7ffe52u32, - 0xbf7ffe8eu32, 0x3f7ffecbu32, 0x3f7ffe0cu32, 0x3f7ffe09u32, 0xbf7ffe5cu32, 0xbf7ffec5u32, 0xbbc1c89du32, 0x3f7fff44u32, - 0x3f7ffe51u32, 0xbf7fff49u32, 0xbf7ffebau32, 0x3f7ffe42u32, 0xbf7fffe8u32, 0x3f7fff6bu32, 0xbf7fff46u32, 0x3f7ffe4fu32, - 0xbf7ffea1u32, 0x3f7fff7du32, 0xbf7ffe10u32, 0x3f7ffe94u32, 0xbf7ffe98u32, 0x3f7ffe70u32, 0xbf7ffe96u32, 0xbf7ffe86u32, - 0x3be299a1u32, 0xbf7ffe4cu32, 0x3f7ffe22u32, 0x3ba2f2b9u32, 0xbbe7fe7fu32, 0x3bd227b7u32, 0x3f7ffe4bu32, 0xbf7ffed5u32, - 0x3f7ffe07u32, 0x3f7ffee2u32, 0xbf7ffe45u32, 0xbf7ffe24u32, 0x3bf75fddu32, 0x3be7d701u32, 0xbf7ffe0au32, 0xbf7fff4au32, - 0x3bff6911u32, 0x3f7ffeceu32, 0x3f7ffe18u32, 0x3f7ffe65u32, 0xbf7ffe23u32, 0xbf7ffe48u32, 0x3bf1b345u32, 0x3f7ffe3au32, - 0x3f7ffe7bu32, 0xbf7fff24u32, 0x3f7ffea0u32, 0x3f7ffedcu32, 0xbf7ffed0u32, 0xbf7ffe8cu32, 0x3f7fff1du32, 0x3ba51de2u32, - 0x3bfd0591u32, 0xbf7ffe1eu32, 0x3f7ffe4au32, 0x3f7ffee5u32, 0x3f7ffe03u32, 0xbbb94db3u32, 0x3f7ffea9u32, 0xbf7ffebbu32, - 0xbf7fff30u32, 0xbf7ffe92u32, 0x3f7ffe1fu32, 0x3f7ffe3du32, 0x3f7ffe12u32, 0xbf7ffed3u32, 0x3f7ffe05u32, 0x3f7fff42u32, - 0x3f7ffe08u32, 0x3f7fff2bu32, 0xbf7fff2fu32, 0xbbecfd5cu32, 0xbbdd1971u32, 0x3f7fff50u32, 0x3b8bb087u32, 0x3f7ffe50u32, - 0x3bf25d8fu32, 0xbf7ffe73u32, 0x3be1409fu32, 0x3f7ffe49u32, 0xbf7ffe69u32, 0x3bee0fdbu32, 0x3f7fff47u32, 0x3f7ffe75u32, - 0xbf7ffe15u32, 0xbf7ffe52u32, 0xbf7ffe41u32, 0xbf7ffe26u32, 0x3f7fffcau32, 0x3f7ffe55u32, 0xbf7ffe21u32, 0xbf7ffe1fu32, - 0xbf7fff2au32, 0x3f7ffe14u32, 0x3f7ffe39u32, 0x3f7ffeebu32, 0xbf7ffe95u32, 0xbf7ffe66u32, 0xbf7ffe0eu32, 0xbf7ffe44u32, - 0x3f7ffe60u32, 0xbf7ffec7u32, 0x3f7ffe89u32, 0xbf7ffe5bu32, 0xbf7fff69u32, 0x3bce3316u32, 0xbf7ffe1bu32, 0x3f7ffe5au32, - 0xbf7ffe61u32, 0x3f7fff04u32, 0x3f7ffe81u32, 0xbf7fffa7u32, 0xbf7ffe96u32, 0xbf7ffe00u32, 0x3f7fff6bu32, 0x3f7fff3au32, - 0xbf7ffe9cu32, 0x3f7ffe1cu32, 0xbf7ffe9fu32, 0x3f7ffe1au32, 0x3f7ffe15u32, 0x3f7fff3du32, 0x3f7ffe21u32, 0xbf7fff51u32, - 0x3f7fff43u32, 0xbf7ffeffu32, 0x3f7ffe16u32, 0xbf7fff2du32, 0xbb9fbac0u32, 0x3bff60fau32, 0x3f7ffe6fu32, 0x3f7ffe36u32, - 0xbf7ffe25u32, 0xbf7ffe94u32, 0x3bf73b3cu32, 0x3f7ffe97u32, 0x3f7fff25u32, 0xbf7ffe48u32, 0x3f7ffe4au32, 0xbf7ffecau32, - 0xbf7ffe5fu32, 0x3f7ffe5eu32, 0xbf7ffe5du32, 0x3f7ffe5cu32, 0xbf7ffef1u32, 0xbbfab41bu32, 0xbbc2c133u32, 0x3f7ffe8bu32, - 0xbf7ffe17u32, 0x3f7ffea3u32, 0x3f7ffee1u32, 0xbf7ffe2cu32, 0x3f7ffe6bu32, 0x3f7ffeb2u32, 0x3f7ffeeau32, 0xbf7ffea1u32, - 0xbf7ffe1du32, 0xbbac4d26u32, 0x3f7ffeecu32, 0x3f7ffea8u32, 0x3f7ffe06u32, 0x3f7ffef5u32, 0x3f7ffe62u32, 0x3f7fff29u32, - 0xbf7ffe56u32, 0xbf7ffeeeu32, 0xbf7fff05u32, 0x3f7fff32u32, 0x3f7fff76u32, 0xbf7ffe28u32, 0xbf7ffe50u32, 0xbf7ffe3du32, - 0xbf7ffeceu32, 0x3f7ffe0au32, 0x3f7ffe45u32, 0xbf7ffe20u32, 0xbf7ffe3eu32, 0x3be00e9au32, 0x3f7ffe99u32, 0x3f7ffe0du32, - 0x3f7fff22u32, 0xbf7fff54u32, 0xbf7ffe53u32, 0x3f7ffe38u32, 0x3f7fff3cu32, 0x3f7fff16u32, 0x3bf886c3u32, 0x3f7fff20u32, - 0x3f7ffe26u32, 0x3f7ffe41u32, 0x3f7ffe57u32, 0x3f7ffefau32, 0xbf7ffe46u32, 0x3f7ffefeu32, 0x3f7ffe88u32, 0x3f7ffe70u32, - 0xbf7ffe23u32, 0x3f7ffe49u32, 0x3f7ffef9u32, 0x3f7ffe11u32, 0xbf7fff8cu32, 0xbf7ffe6au32, 0x3f7ffe8du32, 0x3bfb0378u32, - 0x3f7ffe10u32, 0xbf7ffecfu32, 0x3f7fff0eu32, 0xbbf504d7u32, 0x3f7ffe85u32, 0x3f7ffecdu32, 0x3f7fff03u32, 0xbbf8cf6eu32, - 0xbf7fff25u32, 0x3bcae6fbu32, 0x3f7ffe94u32, 0x3bb9fed3u32, 0x3f7fff51u32, 0x3f7ffe61u32, 0x3f7ffed7u32, 0xbf7ffe75u32, - 0x3f7ffe69u32, 0x3f7ffe0eu32, 0x3bf709dcu32, 0x3f7ffe96u32, 0x3be62157u32, 0xbbf76a3du32, 0xbf7ffee4u32, 0x3f7ffe72u32, - 0x3f7ffe8cu32, 0xbf7ffefbu32, 0xbf7ffe90u32, 0x3be5eb60u32, 0xbf7ffe40u32, 0xbf7ffeb6u32, 0x3f7ffe7fu32, 0x3f7ffe6eu32, - 0x3f7ffe12u32, 0x3f7ffe0au32, 0x3f7fff48u32, 0xbf7ffe17u32, 0x3f7ffe1cu32, 0xbf7ffed5u32, 0x3be301ffu32, 0xbf7fff81u32, - 0x3f7ffe9fu32, 0xbf7ffe15u32, 0x3f7ffe5au32, 0xbf7fff89u32, 0x3f7fff1du32, 0xbf7ffea1u32, 0x3f7fff4cu32, 0xbbd19303u32, - 0xbf7ffe54u32, 0x3f7ffe82u32, 0x3f7fff9du32, 0xbf7ffe91u32, 0x3f7ffebdu32, 0x3f7ffe51u32, 0x3f7ffe47u32, 0xbf7ffe2fu32, - 0x3f7ffe5fu32, 0xbf7ffe21u32, 0x3f7fff4du32, 0x3f7fff56u32, 0x3f7ffe5du32, 0xbf7ffec6u32, 0x3f7ffea6u32, 0x3f7ffe2du32, - 0x3b864477u32, 0xbf7fff91u32, 0x3f7ffe57u32, 0xbf7ffe7cu32, 0x3f7fff37u32, 0x3f7fff01u32, 0xbf7ffe08u32, 0xbf7ffe20u32, - 0xbf7ffe3eu32, 0x3f7ffe99u32, 0xbf7ffe7du32, 0x3f7ffe41u32, 0x3f7ffe92u32, 0xbf7ffe03u32, 0x3f7ffe19u32, 0xbf7ffe6fu32, - 0xbf7ffe0du32, 0xbf7ffeb3u32, 0x3bfa1017u32, 0x3bbbf406u32, 0x3f7ffee6u32, 0x3b75e843u32, 0xbf7ffec2u32, 0xbf7ffe93u32, - 0x3f7ffe31u32, 0x3f7ffe06u32, 0x3f7ffeefu32, 0x3f7ffe10u32, 0xbf7fff0au32, 0xbf7ffec0u32, 0xbbebe7beu32, 0x3f7ffe76u32, - 0x3bf80d44u32, 0xbf7ffe13u32, 0x3f7ffe2bu32, 0xbf7ffe63u32, 0x3f7ffe33u32, 0xbf7ffe87u32, 0x3f7fff9cu32, 0xbf7ffe24u32, - 0xbbfccf4fu32, 0x3f7ffe43u32, 0xbf7ffe39u32, 0xbbe8b69fu32, 0x3f7ffe53u32, 0xbf7ffe14u32, 0x3bed7eb2u32, 0x3bebd3a0u32, - 0x3f7ffe02u32, 0x3f7ffe86u32, 0x3f7ffe17u32, 0xbbb2531fu32, 0xbf7fff1bu32, 0x3f7ffe4au32, 0xbf7ffe5eu32, 0x3bfd02d2u32, - 0x3f7ffea4u32, 0x3f7ffe2au32, 0xbbe05ccdu32, 0x3f7ffe6cu32, 0x3f7ffe04u32, 0x3f7ffebbu32, 0x3f7fff16u32, 0x3bdac638u32, - 0x3f7fff29u32, 0xbf7ffe2cu32, 0x3f7ffe64u32, 0x3be71c74u32, 0x3f7ffe01u32, 0x3bec65c0u32, 0x3f7ffedbu32, 0xbf7ffec1u32, - 0x3f7fffb0u32, 0xbf7ffe01u32, 0x3bdb4d71u32, 0xbf7fff29u32, 0x3bc57d97u32, 0xbf7ffebbu32, 0xbf7fff85u32, 0xbf7ffea4u32, - 0x3bf34a64u32, 0xbf7ffe74u32, 0x3f7ffe1fu32, 0xbf7ffe2bu32, 0xbf7ffe10u32, 0xbf7ffeefu32, 0xbf7ffe62u32, 0xbf7ffe26u32, - 0xbf7ffeabu32, 0xbf7ffe26u32, 0xbf7ffe8cu32, 0x3f7ffe0du32, 0xbf7fff90u32, 0xbf7ffebfu32, 0xbbba5879u32, 0x3f7ffe01u32, - 0xbf7fff48u32, 0xbbed33e1u32, 0x3f7ffe74u32, 0xbf7ffe1fu32, 0x3f7ffe2bu32, 0x3f7ffe5cu32, 0x3f7ffe62u32, 0x3f7ffe9fu32, - 0xbf7ffe48u32, 0xbf7ffe95u32, 0x3f7ffe20u32, 0x3f7fff25u32, 0x3f7ffe7au32, 0x3f7ffe6eu32, 0xbf7ffe88u32, 0x3be18f99u32, - 0x3f7ffe52u32, 0x3f7ffe68u32, 0xbf7ffe4eu32, 0xbf7ffe29u32, 0x3f7ffe74u32, 0xbf7ffe1fu32, 0x3bf46abbu32, 0x3f7ffe8au32, - 0xbf7ffe58u32, 0x3f7fffbfu32, 0x3bf0474bu32, 0x3bf952a0u32, 0x3f7ffe4fu32, 0xbf7ffed1u32, 0xbf7ffe10u32, 0x3f7fffa9u32, - 0xbf7ffe21u32, 0x3f7ffe1eu32, 0x3f7ffe30u32, 0xbf7fff46u32, 0x3ba8de0eu32, 0x3f7ffe5cu32, 0x3f7ffe90u32, 0x3f7ffed5u32, - 0xbf7ffe48u32, 0xbf7fff54u32, 0xbbd37a4du32, 0xbbf5db83u32, 0xbf7fff0du32, 0x3f7ffe83u32, 0xbf7fffacu32, 0x3bfb6608u32, - 0xbf7ffee0u32, 0x3bf463fau32, 0x3f7ffe3cu32, 0xbf7fff13u32, 0xbf7ffe2du32, 0xbf7ffebdu32, 0xbbd52a3au32, 0x3f7ffe31u32, - 0x3f7ffe96u32, 0x3f7ffe16u32, 0xbf7ffe66u32, 0xbf7ffea7u32, 0xbf7ffe1du32, 0x3f7ffe0cu32, 0x3f7ffecfu32, 0xbbe75d5bu32, - 0xbf7ffe3fu32, 0xbf7ffe78u32, 0xbf7fff28u32, 0x3f7ffe4du32, 0xbbd1d44au32, 0xbf7ffe18u32, 0x3f7ffe6bu32, 0x3f7ffe13u32, - 0x3f7ffe63u32, 0x3f7ffe49u32, 0x3bd83a7cu32, 0xbf7ffe87u32, 0xbf7ffe2eu32, 0xbf7ffe43u32, 0x3f7ffe5eu32, 0xbf7ffe98u32, - 0xbf7ffe36u32, 0xbbc32099u32, 0x3f7ffe5fu32, 0xbf7fff09u32, 0xbf7ffe14u32, 0xbf7ffe6du32, 0xbf7ffeb5u32, 0xbf7ffea8u32, - 0xbf7ffe46u32, 0xbf7ffe37u32, 0xbf7ffe42u32, 0x3bab9830u32, 0x3f7ffef3u32, 0xbf7ffe07u32, 0xbf7ffe50u32, 0x3bbbf8e1u32, - 0xbf7ffe11u32, 0x3f7ffe67u32, 0x3f7fff58u32, 0xbf7ffee9u32, 0x3f7ffe64u32, 0x3f7ffe39u32, 0x3f7ffedbu32, 0x3f7ffee6u32, - 0x3f7fff16u32, 0xbf7ffe6cu32, 0x3f7fff75u32, 0x3f7fff12u32, 0x3bd43491u32, 0x3f7fff0eu32, 0xbbcb8eb7u32, 0x3f7ffe05u32, - 0xbf7ffe03u32, 0x3f7ffe23u32, 0x3f7ffe08u32, 0xbbffa04du32, 0x3f7ffe53u32, 0x3f7ffeebu32, 0xbbf83111u32, 0xbf7ffea5u32, - 0xbf7ffeccu32, 0xbbe2066au32, 0x3f7fff47u32, 0xbbe51b69u32, 0xbf7ffe52u32, 0x3bd76dc1u32, 0xbf7ffe3bu32, 0xbf7ffe0du32, - 0xbf7ffe68u32, 0xbf7ffea4u32, 0xbf7ffe2bu32, 0xbf7ffe01u32, 0x3f7ffe48u32, 0xbf7ffe83u32, 0xbf7ffe6eu32, 0xbf7ffe61u32, - 0x3f7fff26u32, 0xbbd49b26u32, 0xbf7ffe56u32, 0xbbd74f9bu32, 0x3f7ffe06u32, 0x3f7fff2cu32, 0xbf7ffe7eu32, 0xbf7ffe36u32, - 0x3f7ffed4u32, 0x3bf33edau32, 0x3f7fff62u32, 0xbf7fff4fu32, 0xbbe8c65du32, 0x3f7ffe16u32, 0xbf7ffe73u32, 0x3f7ffe60u32, - 0xbf7ffeb0u32, 0xbf7ffe57u32, 0x3bccd71au32, 0xbbe7abadu32, 0xbf7ffe32u32, 0x3f7ffe50u32, 0x3f7ffe07u32, 0x3f7ffe42u32, - 0x3f7ffe87u32, 0xbf7ffe31u32, 0xbf7fff02u32, 0x3f7ffebdu32, 0xbf7ffe20u32, 0xbf7ffe30u32, 0x3f7fff49u32, 0xbf7ffe2fu32, - 0x3f7fff92u32, 0xbf7fff1au32, 0xbbdbb3a5u32, 0xbf7ffedbu32, 0xbf7fff5au32, 0x3f7ffecau32, 0x3f7ffeceu32, 0x3f7ffe0bu32, - 0x3bd73175u32, 0x3f7ffe3au32, 0x3f7ffe85u32, 0xbf7ffe7du32, 0x3f7ffe47u32, 0x3befea18u32, 0x3f7ffeb1u32, 0xbf7ffef7u32, - 0xbf7ffec8u32, 0xbf7ffea3u32, 0x3bc2f979u32, 0xbf7ffe0du32, 0xbf7ffe84u32, 0xbf7ffe6bu32, 0xbf7fff9bu32, 0x3bfc2824u32, - 0x3f7ffe08u32, 0x3f7ffeedu32, 0xbf7ffe6cu32, 0x3f7ffe39u32, 0x3f7ffeaau32, 0xbf7ffe26u32, 0x3f7ffee7u32, 0xbbdfc7b3u32, - 0xbf7ffe21u32, 0x3bff0f44u32, 0x3f7ffe13u32, 0x3f7ffe0fu32, 0x3f7ffe71u32, 0x3f7ffe38u32, 0x3f7ffea0u32, 0xbf7fff38u32, - 0x3bc3d4f5u32, 0xbf7ffea1u32, 0x3f7ffeaeu32, 0x3f7ffe2eu32, 0x3f7fff44u32, 0xbf7ffed0u32, 0xbf7ffe9bu32, 0x3f7ffeb7u32, - 0x3f7ffed6u32, 0x3f7ffe8bu32, 0xbf7ffef2u32, 0x3f7ffe35u32, 0x3f7ffe65u32, 0x3bdf36c6u32, 0x3f7ffe4eu32, 0xbf7fff28u32, - 0x3f7fff85u32, 0x3f7ffe40u32, 0xbf7ffe04u32, 0x3f7fff30u32, 0x3bfcb1f6u32, 0xbf7ffe6au32, 0x3f7ffe5cu32, 0x3bf7499eu32, - 0xbf7ffee5u32, 0x3f7ffeadu32, 0xbbfb626au32, 0xbf7ffe1eu32, 0xbf7ffe5cu32, 0x3f7ffe04u32, 0xbf7ffe8au32, 0xbf7ffe8bu32, - 0xbf7ffed7u32, 0xbf7ffed6u32, 0xbf7ffe9fu32, 0xbf7fff56u32, 0xbbf6a04fu32, 0xbf7ffe2eu32, 0x3f7ffea1u32, 0xbf7ffe38u32, - 0xbf7ffee7u32, 0xbf7ffeaau32, 0xbf7fff06u32, 0xbf7ffe60u32, 0xbf7fff05u32, 0xbf7ffe53u32, 0x3f7ffea4u32, 0x3f7ffe83u32, - 0x3f7ffe8cu32, 0x3f7ffe98u32, 0x3f7ffe57u32, 0xbf7ffe0bu32, 0xbf7ffe1du32, 0x3f7ffe72u32, 0xbf7ffed8u32, 0x3f7ffe04u32, - 0xbf7ffeadu32, 0x3bf3ed98u32, 0xbf7fff2eu32, 0x3f7ffe8au32, 0xbf7ffe3bu32, 0x3f7ffe9fu32, 0xbf7fff4fu32, 0xbf7fff36u32, - 0xbf7ffe90u32, 0xbf7ffe1fu32, 0x3f7ffef9u32, 0x3bd58e0fu32, 0xbbf7061du32, 0xbf7ffe27u32, 0x3f7ffe0fu32, 0x3f7ffec0u32, - 0xbf7ffec5u32, 0x3f7ffe76u32, 0x3f7ffedbu32, 0x3f7fff3eu32, 0x3f7ffe5bu32, 0xbf7ffe67u32, 0xbf7fff40u32, 0xbf7ffe58u32, - 0x3f7ffe41u32, 0x3f7ffe89u32, 0x3f7ffe51u32, 0x3f7ffe42u32, 0x3f7ffe72u32, 0xbf7ffed5u32, 0x3f7ffe64u32, 0xbba2183fu32, - 0xbf7ffe20u32, 0xbf7fff10u32, 0x3f7ffec7u32, 0xbf7fff8fu32, 0x3f7fff94u32, 0x3f7ffe0au32, 0x3be862fdu32, 0xbf7ffe05u32, - 0xbbd25d2eu32, 0x3f7ffe17u32, 0xbf7fffcau32, 0x3f7ffe29u32, 0x3f7ffe4du32, 0x3f7fff46u32, 0x3f7ffe5du32, 0xbbe61c1du32, - 0x3f7ffeb9u32, 0x3f7ffe8du32, 0xbf7ffe46u32, 0xbf7ffe7au32, 0x3f7ffe37u32, 0x3f7fff29u32, 0xbf7ffef7u32, 0xbf7ffeb0u32, - 0xbf7ffe14u32, 0xbf7fff38u32, 0x3bb68fbbu32, 0xbbe215fdu32, 0x3f7ffeabu32, 0x3f7ffebeu32, 0x3be34866u32, 0xbf7fff6cu32, - 0xbbf49698u32, 0x3f7ffe7du32, 0xbf7ffeebu32, 0x3bf560ccu32, 0xbf7fff03u32, 0xbf7ffe0eu32, 0x3f7ffefau32, 0x3f7fff4cu32, - 0x3f7ffe23u32, 0x3f7ffe02u32, 0x3f7fff25u32, 0x3f7ffebau32, 0xbf7ffe30u32, 0x3f7fff39u32, 0x3f7ffe2au32, 0x3f7ffe34u32, - 0xbf7fff60u32, 0xbbe968bdu32, 0x3f7ffe78u32, 0x3f7ffe55u32, 0xbf7ffe6du32, 0x3f7ffe32u32, 0x3f7ffeb7u32, 0x3f7fff34u32, - 0x3f7ffe87u32, 0xbbdb4667u32, 0x3f7ffe71u32, 0x3f7ffe6au32, 0xbf7ffeaeu32, 0xbf7ffe01u32, 0xbf7ffedau32, 0x3bccc2d0u32, - 0x3bd875e9u32, 0xbf7ffe3au32, 0x3f7ffeacu32, 0x3f7ffe9eu32, 0xbf7ffe73u32, 0xbf7ffe4bu32, 0xbf7fffb1u32, 0xbf7ffe3cu32, - 0x3f7fff5fu32, 0x3f7ffe1bu32, 0xbf7ffe08u32, 0x3f7ffe70u32, 0x3f7ffe62u32, 0x3f7fff19u32, 0xbbf9dcacu32, 0xbf7ffec7u32, - 0x3f7ffe0bu32, 0xbbbc90aau32, 0x3f7ffe38u32, 0x3f7fff47u32, 0xbbeb7d28u32, 0x3f7ffe27u32, 0xbf7fff77u32, 0x3f7ffed0u32, - 0x3f7ffe9bu32, 0xbf7ffec0u32, 0x3f7ffe3fu32, 0xbf7ffe80u32, 0xbf7ffe0cu32, 0xbf7ffedcu32, 0x3f7ffe26u32, 0xbf7ffec6u32, - 0xbf7ffe40u32, 0xbf7ffe6cu32, 0x3f7fff66u32, 0xbf7ffe69u32, 0xbbd33ba8u32, 0x3befda70u32, 0x3f7ffe63u32, 0xbbb96f75u32, - 0xbf7ffef5u32, 0x3f7ffe98u32, 0xbf7ffe53u32, 0xbf7ffe5cu32, 0x3f7ffea4u32, 0x3f7fff28u32, 0xbbf0f31fu32, 0xbf7ffe97u32, - 0xbbc104a1u32, 0xbf7ffe6fu32, 0xbf7ffe4fu32, 0xbf7ffe07u32, 0x3f7ffeabu32, 0xbf7ffe35u32, 0x3f7ffe44u32, 0xbbd6362bu32, - 0xbf7ffe92u32, 0x3f7ffecdu32, 0xbf7ffe24u32, 0xbbec5822u32, 0x3f7ffee0u32, 0x3f7fff1au32, 0x3f7fff93u32, 0x3f7ffe6eu32, - 0xbf7ffe2cu32, 0x3bcf9ae3u32, 0x3f7fff22u32, 0x3bc8bbffu32, 0x3f7ffe79u32, 0x3f7ffe12u32, 0xbf7ffe0eu32, 0x3f7ffe39u32, - 0x3f7ffeeeu32, 0xbbf583e3u32, 0xbf7fff92u32, 0x3f7fff55u32, 0xbf7ffe90u32, 0xbbd38c55u32, 0xbf7ffec4u32, 0x3f7ffe19u32, - 0xbf7ffe2au32, 0x3f7fff14u32, 0x3f7ffe3bu32, 0x3bca97afu32, 0xbbff5d44u32, 0xbf7ffe76u32, 0xbf7ffe29u32, 0x3f7ffe0eu32, - 0xbf7ffe34u32, 0xbbaa7ba5u32, 0xbf7ffeb5u32, 0xbf7ffefcu32, 0xbf7fff8eu32, 0xbf7ffe44u32, 0xbf7ffec3u32, 0x3f7ffe7cu32, - 0xbf7fffb9u32, 0xbf7ffe7du32, 0x3f7fff01u32, 0xbf7ffe56u32, 0xbb786da6u32, 0x3f7fff0bu32, 0x3f7ffef1u32, 0x3f7ffe78u32, - 0xbbdbb4fdu32, 0x3f7ffee8u32, 0x3f7fff17u32, 0xbf7ffef8u32, 0x3f7ffe8fu32, 0x3f7ffe53u32, 0x3f7ffe54u32, 0x3f7fff29u32, - 0xbbef5095u32, 0xbbf1d52au32, 0x3f7ffebcu32, 0x3f7fff32u32, 0xbf7ffe10u32, 0x3f7ffe06u32, 0xbbc05684u32, 0x3f7ffe64u32, - 0xbf7ffeddu32, 0x3f7ffe2fu32, 0xbf7ffe49u32, 0xbb8ef147u32, 0xbf7fff0eu32, 0xbf7ffe59u32, 0x3b963693u32, 0xbf7fff04u32, - 0x3f7ffe48u32, 0xbf7fff23u32, 0xbf7ffefdu32, 0x3f7ffe41u32, 0xbf7ffe47u32, 0xbf7ffe5eu32, 0x3f7ffe96u32, 0x3f7ffe57u32, - 0x3f7ffe08u32, 0xbbd6d576u32, 0xbf7ffe1bu32, 0xbf7ffefeu32, 0xbf7ffec8u32, 0xbf7ffeb6u32, 0xbbf5be44u32, 0xbf7ffe16u32, - 0xbf7ffe5fu32, 0x3f7ffe85u32, 0xbf7ffe88u32, 0x3be3d2d6u32, 0x3f7ffe11u32, 0x3f7ffe22u32, 0xbf7fff73u32, 0x3f7fff49u32, - 0x3f7ffe73u32, 0xbf7ffe72u32, 0xbbeab9c7u32, 0xbf7ffe98u32, 0x3f7ffe6fu32, 0x3f7ffeb0u32, 0xbbfe5a6fu32, 0xbf7ffe8eu32, - 0x3f7ffe09u32, 0xbf7ffee1u32, 0xbf7ffedbu32, 0xbf7fff62u32, 0xbbf3ae61u32, 0x3f7ffe17u32, 0xbf7ffe23u32, 0x3f7ffe3fu32, - 0x3f7ffef3u32, 0xbf7ffe8bu32, 0xbb947064u32, 0xbf7ffe3cu32, 0x3f7ffe8au32, 0x3f7ffe9bu32, 0x3f7ffeceu32, 0x3f7ffee3u32, - 0x3f7ffe79u32, 0xbf7ffe89u32, 0xbf7fff65u32, 0x3f7ffe70u32, 0x3f7ffe39u32, 0x3bfc12f2u32, 0xbf7fff6du32, 0xbf7ffe63u32, - 0xbf7ffe61u32, 0x3f7ffed9u32, 0xbf7ffe3bu32, 0x3bf21694u32, 0x3f7ffefcu32, 0x3bf1ab59u32, 0x3f7ffe28u32, 0x3f7ffee4u32, - 0x3f7ffe15u32, 0x3f7ffe2eu32, 0x3f7ffed8u32, 0xbf7ffe05u32, 0xbf7ffe58u32, 0xbf7ffe0cu32, 0x3f7ffef9u32, 0x3bfd94e6u32, - 0xbf7ffe3au32, 0xbf7ffe6au32, 0xbf7ffe02u32, 0xbf7ffe15u32, 0xbf7ffe94u32, 0xbf7ffe27u32, 0x3f7fffdbu32, 0xbf7fff02u32, - 0x3be4cda4u32, 0x3ba6866au32, 0xbf7ffe39u32, 0xbf7ffe56u32, 0x3be5c7a0u32, 0xbf7ffe24u32, 0xbf7ffe4cu32, 0x3f7ffeaau32, - 0x3f7ffeedu32, 0x3f7fff15u32, 0xbf7fff81u32, 0xbf7ffe1fu32, 0xbf7ffe22u32, 0x3f7fff44u32, 0x3f7fff1fu32, 0xbf7ffea3u32, - 0x3f7ffe82u32, 0x3f7fff07u32, 0xbf7fff3eu32, 0xbf7ffeebu32, 0x3bfee7a8u32, 0xbf7ffe12u32, 0xbf7fff19u32, 0x3f7ffe0fu32, - 0xbbbaa8e9u32, 0xbf7ffe0bu32, 0x3f7ffe6cu32, 0xbbe47087u32, 0xbf7ffe97u32, 0xbf7ffebdu32, 0xbbd313f2u32, 0x3bfff543u32, - 0x3f7ffeadu32, 0xbf7fff3fu32, 0xbf7fff3du32, 0xbf7ffe28u32, 0x3f7ffe86u32, 0xbf7ffe2du32, 0x3f7fff20u32, 0xbf7fff74u32, - 0x3f7ffeb4u32, 0xbf7ffe9du32, 0xbbc51e53u32, 0xbf7ffe8fu32, 0x3f7fff5du32, 0x3f7ffe4bu32, 0x3f7ffe20u32, 0xbf7ffee2u32, - 0xbf7ffeb5u32, 0xbf7ffe07u32, 0x3f7fff11u32, 0x3f7ffe52u32, 0xbf7ffe84u32, 0x3f7ffe4au32, 0xbbd79d51u32, 0x3f7ffe5fu32, - 0xbf7ffe08u32, 0xbf7ffe96u32, 0xbf7ffe53u32, 0x3f7ffe10u32, 0xbf7ffe2fu32, 0x3f7ffe49u32, 0x3f7ffec8u32, 0x3f7ffeb6u32, - 0x3f7ffe16u32, 0xbf7ffe85u32, 0xbf7fffa3u32, 0x3f7ffe72u32, 0xbf7ffeb0u32, 0xbf7ffe09u32, 0x3f7ffe65u32, 0xbf7ffe04u32, - 0xbf7ffecdu32, 0xbf7ffe34u32, 0x3f7ffe19u32, 0xbf7fff42u32, 0xbf7ffe0au32, 0xbf7ffe99u32, 0xbf7ffe43u32, 0x3f7ffe3eu32, - 0x3f7ffe5eu32, 0x3f7ffe6fu32, 0xbf7ffe1cu32, 0xbf7ffe2au32, 0x3ba36c51u32, 0x3f7ffea0u32, 0x3f7ffe14u32, 0x3f7ffe67u32, - 0x3f7ffeccu32, 0x3f7ffe21u32, 0xbbb68309u32, 0x3f7ffef7u32, 0x3f7ffe45u32, 0xbf7fff66u32, 0xbf7ffe26u32, 0x3f7ffeaeu32, - 0x3f7ffe78u32, 0xbf7ffeddu32, 0xbf7ffedbu32, 0xbf7fff1eu32, 0x3f7ffee5u32, 0x3baf7ce0u32, 0xbf7ffe4eu32, 0xbbecce7eu32, - 0xbf7ffe41u32, 0x3f7ffe29u32, 0xbf7ffe66u32, 0x3bfd1b99u32, 0xbf7ffe46u32, 0xbf7fff3au32, 0xbf7fff04u32, 0x3f7ffed8u32, - 0x3f7ffe28u32, 0x3bcb2818u32, 0xbf7ffe3cu32, 0x3f7ffe0bu32, 0x3f7ffe8cu32, 0xbf7ffed9u32, 0x3f7ffe05u32, 0x3f7fff36u32, - 0x3f7ffe27u32, 0x3be23979u32, 0xbf7ffeeeu32, 0xbf7ffe13u32, 0x3f7ffe73u32, 0xbf7ffe1bu32, 0x3f7fff0au32, 0x3f7ffe2bu32, - 0xbf7ffe70u32, 0x3f7ffe6du32, 0x3f7fffcfu32, 0xbbc25a53u32, 0xbbfffd73u32, 0x3f7ffe8du32, 0x3f7ffe48u32, 0x3f7ffe4du32, - 0xbf7fff90u32, 0x3f7ffedcu32, 0xbf7ffe3bu32, 0xbf7ffe95u32, 0x3f7ffe20u32, 0xbf7fff2eu32, 0xbf7ffe08u32, 0xbf7ffe37u32, - 0xbbef1443u32, 0x3f7ffeb2u32, 0x3f7ffe07u32, 0x3beb0b78u32, 0xbf7ffe38u32, 0x3f7ffe2eu32, 0x3becb5d6u32, 0xbbd2fbafu32, - 0x3be5546au32, 0xbf7ffefdu32, 0xbf7ffe4cu32, 0x3f7ffe33u32, 0xbf7fff17u32, 0x3f7ffe18u32, 0x3bc1ee22u32, 0xbf7ffe75u32, - 0x3f7ffe68u32, 0x3f7ffe83u32, 0xbf7ffeb3u32, 0x3f7ffefcu32, 0xbf7ffe8cu32, 0xbf7ffe0bu32, 0xbf7ffe67u32, 0x3bb5a58eu32, - 0xbf7ffeaau32, 0xbf7ffe3eu32, 0xbf7ffe5eu32, 0x3f7fff3au32, 0x3f7ffe31u32, 0xbf7ffe05u32, 0xbf7ffe27u32, 0x3f7ffeeeu32, - 0x3f7ffe54u32, 0x3f7ffe2cu32, 0xbbf08a74u32, 0xbf7ffef0u32, 0x3f7ffe12u32, 0xbf7fff57u32, 0xbf7fff29u32, 0x3f7ffe4au32, - 0x3be87507u32, 0xbf7ffe3bu32, 0x3f7ffedcu32, 0x3bfb26e7u32, 0x3f7ffe48u32, 0xbf7fff6cu32, 0x3f7ffeb4u32, 0x3f7ffe25u32, - 0x3f7ffe5cu32, 0xbf7ffe42u32, 0xbf7ffe41u32, 0xbf7ffe46u32, 0xbf7ffea9u32, 0xbf7fff78u32, 0xbf7fff5bu32, 0xbf7ffe13u32, - 0xbf7ffe1eu32, 0x3f7ffe87u32, 0xbf7ffe23u32, 0x3f7ffeedu32, 0xbf7ffe97u32, 0x3f7ffe66u32, 0xbf7ffe64u32, 0x3f7ffe58u32, - 0x3f7fff59u32, 0x3f7fff1eu32, 0x3f7fff2cu32, 0xbf7ffe04u32, 0x3f7ffec5u32, 0x3f7fff33u32, 0x3f7fff0bu32, 0xbf7ffe08u32, -]; -#[inline] -pub fn win_isnan(x: f64) -> bool { - return x.to_bits() & 0x7fffffffffffffffu64 > 0x7ff0000000000000u64; -} -#[inline] -pub fn win_quiet(x: f64) -> f64 { - return f64::from_bits(x.to_bits() | 0x0008000000000000u64); -} -#[inline] -pub fn win_nan_operand(a: f64, b: f64) -> f64 { - return if win_isnan(a) { win_quiet(a) } else { win_quiet(b) }; -} -#[inline] -pub fn win_atan2f(yf: f32, xf: f32) -> f32 { - if xf.to_bits() & 0x7fffffffu32 > 0x7f800000u32 { - return f32::from_bits(xf.to_bits() | 0x00400000u32); - } - if yf.to_bits() & 0x7fffffffu32 > 0x7f800000u32 { - return f32::from_bits(yf.to_bits() | 0x00400000u32); - } - let x: f64 = xf as f64; - let y: f64 = yf as f64; - let xb: u64 = x.to_bits(); - let yb: u64 = y.to_bits(); - let de: i32 = ((yb >> 52u32 & 0x7ffu64) as i32).wrapping_sub((xb >> 52u32 & 0x7ffu64) as i32); - let ax: u64 = xb & 0x7fffffffffffffffu64; - let ay: u64 = yb & 0x7fffffffffffffffu64; - let xneg: bool = xb >> 63u32 != 0u64; - let yneg: bool = yb >> 63u32 != 0u64; - let pi: f32 = f32::from_bits(0x40490fdbu32); - let half_pi: f32 = f32::from_bits(0x3fc90fdbu32); - if ay == 0u64 { - if xneg { - return if yneg { -pi } else { pi }; - } - return yf; - } - if ax == 0u64 && yneg { - return -half_pi; - } - if de > 26i32 { - return if yneg { -half_pi } else { half_pi }; - } - if de < -13i32 && !xneg { - if de < -150i32 { - return if yneg { - f32::from_bits(0x80000000u32) - } else { - f32::from_bits(0u32) - }; - } - return (y / x) as f32; - } - if de < -26i32 && xneg { - return if yneg { -pi } else { pi }; - } - if ay == 0x7ff0000000000000u64 && ax == 0x7ff0000000000000u64 { - if xneg { - return if yneg { - f32::from_bits(0xc016cbe4u32) - } else { - f32::from_bits(0x4016cbe4u32) - }; - } - return if yneg { - f32::from_bits(0xbf490fdbu32) - } else { - f32::from_bits(0x3f490fdbu32) - }; - } - let sx: f64 = if xneg { -x } else { x }; - let sy: f64 = if yneg { -y } else { y }; - let swap: bool = sy > sx; - let num: f64 = if swap { sx } else { sy }; - let den: f64 = if swap { sy } else { sx }; - let t: f64 = num / den; - let mut r: f64; - if t > 0.0625 { - let n: i32 = (t * 256.0 + 0.5) as i32; - let nd: f64 = n as f64; - let u: f64 = (num * 256.0 - nd * den) / (nd * num + den * 256.0); - let hi: f64 = u + f64::from_bits(UCRT_ATAN2F_ATAN256[n.wrapping_sub(16i32) as usize]); - let mut u3: f64 = u * u; - u3 = u3 * u; - u3 = u3 * 0.33333333333224097; - r = hi - u3; - } else if t < 0.0001 { - r = t; - } else { - let t2: f64 = t * t; - let mut p: f64 = 0.19999999999393223 - t2 * 0.1428571356180717; - p = p * t2; - let t3: f64 = t2 * t; - let mut q: f64 = 0.3333333333333317 - p; - q = q * t3; - r = t - q; - } - if swap { - r = 1.5707963267948966 - r; - } - if xneg { - r = 3.141592653589793 - r; - } - if yneg { - r = -r; - } - return r as f32; -} -#[inline] -pub fn win_exp_finite(x: f64) -> f64 { - if (x.to_bits() & 0x7fffffffffffffffu64) <= 0x3e50000000000000u64 { - return x + 1.0; - } - let n: i32 = (x * 92.33248261689366) as i32; - let nd: f64 = n as f64; - let r: f64 = nd * -2.5728046223276688e-14 + nd.mul_add(-0.010830424696223417, x); - let j: i32 = n & 63i32; - let k: i32 = n >> 6u32; - let mut p: f64 = 0.001388888888888889_f64.mul_add(r, 0.008333333333333333); - p = p.mul_add(r, 0.041666666666666664); - p = p.mul_add(r, 0.16666666666666666); - p = p.mul_add(r, 0.5); - let e: f64 = (r * r).mul_add(p, r); - let t: f64 = e * f64::from_bits(UCRT_EXP_MUL[j as usize]) + f64::from_bits(UCRT_EXP_LO[j as usize]); - let res: f64 = t + f64::from_bits(UCRT_EXP_HI[j as usize]); - if k > -1022i32 || k == -1022i32 && res >= 1.0 { - return f64::from_bits(res.to_bits().wrapping_add((k as i64 as u64) << 52u32)); - } - return res * f64::from_bits(1u64 << (k.wrapping_add(1074i32) & 63i32) as u32); -} -#[inline] -pub fn win10_exp(x: f64) -> f64 { - let xb: u64 = x.to_bits(); - if xb & 0x7fffffffffffffffu64 >= 0x7ff0000000000000u64 { - if xb == 0x7ff0000000000000u64 { - return x; - } - if xb == 0xfff0000000000000u64 { - return 0.0; - } - return win_quiet(x); - } - if x > 709.782712893384 { - return f64::from_bits(0x7ff0000000000000u64); - } - if x < -744.0346068132731 { - return 0.0; - } - return win_exp_finite(x); -} -#[inline] -pub fn win11_exp(x: f64) -> f64 { - let xb: u64 = x.to_bits(); - if xb & 0x7fffffffffffffffu64 >= 0x7ff0000000000000u64 { - if xb == 0x7ff0000000000000u64 { - return x; - } - if xb == 0xfff0000000000000u64 { - return 0.0; - } - return win_quiet(x); - } - if x > 709.782712893384 { - return f64::from_bits(0x7ff0000000000000u64); - } - if x < -744.0346068132731 { - return if x >= -745.1332191019411 { - f64::from_bits(1u64) - } else { - 0.0 - }; - } - return win_exp_finite(x); -} -#[inline] -pub fn win_tanh_k(i: i32, up: bool) -> f64 { - let mut v: f64 = 0.0; - if i == 0i32 { - v = if up { - -0.00016559704390354995 - } else { - -0.0002000476210719095 - }; - } - if i == 1i32 { - v = if up { - 1.154758789961434e-08 - } else { - 1.4207792637883471e-08 - }; - } - if i == 2i32 { - v = if up { - 0.00017307605012622596 - } else { - 0.00020911402625291644 - }; - } - if i == 3i32 { - v = if up { 0.016735877546189656 } else { 0.020156216602693764 }; - } - if i == 4i32 { - v = if up { 0.014617304728873168 } else { 0.017601634900304468 }; - } - if i == 5i32 { - v = if up { 0.3172045589772944 } else { 0.3816414142883289 }; - } - if i == 6i32 { - v = if up { 0.2277938706590883 } else { 0.27403042465617977 }; - } - if i == 7i32 { - v = if up { 0.6833816119772959 } else { 0.8220912739685393 }; - } - return v; -} -#[inline] -pub fn win_tanh_scale1(k: i32) -> f64 { - return f64::from_bits( - ((k.wrapping_add(if k < 0i32 { 1i32 } else { 0i32 }) >> 1u32).wrapping_add(1023i32) as i64 as u64) << 52u32, - ); -} -#[inline] -pub fn win_tanh_scale2(k: i32) -> f64 { - return f64::from_bits( - (k.wrapping_sub(k.wrapping_add(if k < 0i32 { 1i32 } else { 0i32 }) >> 1u32) - .wrapping_add(1023i32) as i64 as u64) - << 52u32, - ); -} -#[inline] -pub fn win10_tanh(x: f64) -> f64 { - let xb: u64 = x.to_bits(); - let ab: u64 = xb & 0x7fffffffffffffffu64; - if ab < 0x3e30000000000000u64 { - return x; - } - if ab > 0x7ff0000000000000u64 { - return win_quiet(x); - } - let neg: bool = ab != xb; - let a: f64 = f64::from_bits(ab); - if a > 20.0 { - return if neg { -1.0 } else { 1.0 }; - } - let res: f64; - if a > 1.0 { - let a2: f64 = a + a; - let n: i32 = (a2 * 46.16624130844683 + 0.5) as i32; - let j: i32 = n & 31i32; - let nd: f64 = n as f64; - let hi: f64 = f64::from_bits(UCRT_TANH_HI[j as usize]); - let lo: f64 = f64::from_bits(UCRT_TANH_LO[j as usize]); - let k: i32 = n.wrapping_sub(j) >> 5u32; - let r6: f64 = a2 - nd * 0.021660849335603416; - let r1: f64 = n.wrapping_neg() as f64 * 5.689487495325456e-11; - let r: f64 = r1 + r6; - let mut p: f64 = r * 0.001388894908637772 + 0.008333367984342196; - p = p * r + 0.04166666666622608; - p = p * r + 0.16666666666526087; - p = p * r; - p = p + 0.5; - p = p * (r * r); - p = p + r1; - p = p + r6; - p = p * (lo + hi); - p = p + lo; - let mut t: f64 = (hi + p) * win_tanh_scale1(k); - t = t * win_tanh_scale2(k); - t = t + 1.0; - res = 1.0 - 2.0 / t; - } else { - let up: bool = a >= 0.9; - let x2: f64 = a * a; - let x3: f64 = x2 * a; - let mut p: f64 = win_tanh_k(0i32, up) - x2 * win_tanh_k(1i32, up); - p = p * x2; - p = p - win_tanh_k(4i32, up); - p = p * x2; - p = p - win_tanh_k(6i32, up); - p = p * x3; - let mut q: f64 = x2 * win_tanh_k(2i32, up) + win_tanh_k(3i32, up); - q = q * x2; - q = q + win_tanh_k(5i32, up); - q = q * x2; - q = q + win_tanh_k(7i32, up); - res = p / q + a; - } - return if neg { -res } else { res }; -} -#[inline] -pub fn win11_tanh(x: f64) -> f64 { - let xb: u64 = x.to_bits(); - let ab: u64 = xb & 0x7fffffffffffffffu64; - if ab < 0x3e30000000000000u64 { - return x; - } - if ab > 0x7ff0000000000000u64 { - return win_quiet(x); - } - let neg: bool = ab != xb; - let a: f64 = f64::from_bits(ab); - if a > 20.0 { - return if neg { -1.0 } else { 1.0 }; - } - let res: f64; - if a > 1.0 { - let a2: f64 = a + a; - let n: i32 = (a2 * 46.16624130844683 + 0.5) as i32; - let j: i32 = n & 31i32; - let nd: f64 = n as f64; - let hi: f64 = f64::from_bits(UCRT_TANH_HI[j as usize]); - let lo: f64 = f64::from_bits(UCRT_TANH_LO[j as usize]); - let k: i32 = n.wrapping_sub(j) >> 5u32; - let r6: f64 = (-nd).mul_add(0.021660849335603416, a2); - let r1: f64 = n.wrapping_neg() as f64 * 5.689487495325456e-11; - let r: f64 = r1 + r6; - let mut p: f64 = 0.001388894908637772_f64.mul_add(r, 0.008333367984342196); - p = p.mul_add(r, 0.04166666666622608); - p = p.mul_add(r, 0.16666666666526087); - p = p.mul_add(r, 0.5); - let mut m: f64 = (r * r).mul_add(p, r1) + r6; - m = (hi + lo).mul_add(m, lo); - let mut t: f64 = (hi + m) * win_tanh_scale1(k); - t = t.mul_add(win_tanh_scale2(k), 1.0); - res = 1.0 - 2.0 / t; - } else { - let up: bool = a >= 0.9; - let x2: f64 = a * a; - let x3: f64 = x2 * a; - let mut p: f64 = (-win_tanh_k(1i32, up)).mul_add(x2, win_tanh_k(0i32, up)); - p = p.mul_add(x2, -win_tanh_k(4i32, up)); - p = p.mul_add(x2, -win_tanh_k(6i32, up)); - p = p * x3; - let mut q: f64 = win_tanh_k(2i32, up).mul_add(x2, win_tanh_k(3i32, up)); - q = q.mul_add(x2, win_tanh_k(5i32, up)); - q = q.mul_add(x2, win_tanh_k(7i32, up)); - res = p / q + a; - } - return if neg { -res } else { res }; -} -#[inline] -pub fn win_integer_kind(yb: u64) -> i32 { - let e: u64 = yb >> 52u32 & 0x7ffu64; - if e < 1023u64 { - return 0i32; - } - if e > 1075u64 { - return 2i32; - } - let bit: u64 = 1u64 << 1075u64.wrapping_sub(e) as u32; - if yb & bit.wrapping_sub(1u64) != 0u64 { - return 0i32; - } - return if yb & bit != 0u64 { 1i32 } else { 2i32 }; -} -#[inline] -pub fn win10_pow_core(x: f64, y: f64) -> f64 { - let xb: u64 = x.to_bits(); - let yb: u64 = y.to_bits(); - if yb == 0x3ff0000000000000u64 { - return x; - } - if xb == 0x3ff0000000000000u64 { - return 1.0; - } - let mut mb: u64 = xb; - let mut ex: i32 = ((xb >> 52u32) as i32).wrapping_sub(1023i32); - let mut mant: u64 = xb & 0x000fffffffffffffu64; - if ex == -1023i32 { - mb = (f64::from_bits(mant | 0x3ff0000000000000u64) - 1.0).to_bits(); - mant = mb & 0x000fffffffffffffu64; - ex = ((mb >> 52u32) as i32).wrapping_sub(2045i32); - } - let e: f64 = ex as f64; - let f1: u64 = (mb & 0x000ff00000000000u64).wrapping_add((mb & 0x0000080000000000u64) << 1u32); - let j: i32 = (f1 >> 44u32) as i32; - let d: f64 = f64::from_bits(f1 | 0x3fe0000000000000u64) - f64::from_bits(mant | 0x3fe0000000000000u64); - let hiv: f64 = d * f64::from_bits(UCRT_POW_T1[j as usize]); - let lov: f64 = d * f64::from_bits(UCRT_POW_T2[j as usize]); - let z: f64 = hiv + lov; - let mut err: f64 = hiv - z; - let zz: f64 = z * z; - err = err + lov; - let mut p: f64 = 0.1428571428571429_f64.mul_add(z, 0.16666666666666666); - p = p.mul_add(z, 0.2); - p = p.mul_add(z, 0.25); - p = p.mul_add(z, 0.3333333333333333); - p = p.mul_add(z, 0.5); - p = p.mul_add(zz, err); - let s5: f64 = e.mul_add(5.7699990475432854e-08, -p); - let mut s3: f64 = s5 + f64::from_bits(UCRT_POW_T4[j as usize]); - let mut s1: f64 = s3; - s3 = s3 - z; - let mut lg: f64 = e.mul_add(0.6931471228599548, f64::from_bits(UCRT_POW_T3[j as usize])); - let mut s7: f64 = lg; - lg = lg + s3; - let lg_hi: f64 = f64::from_bits(lg.to_bits() & 0xfffffffff8000000u64); - let zs: f64 = z + s3; - s7 = s7 - lg; - s1 = s1 - zs; - s7 = s7 + s3; - s7 = s7 + s1; - s7 = s7 + (lg - lg_hi); - let y_hi: f64 = f64::from_bits(yb & 0xfffffffff8000000u64); - let y_lo: f64 = y - y_hi; - let mut a3: f64 = y_lo * s7; - a3 = a3 + y_lo * lg_hi; - a3 = a3 + s7 * y_hi; - let a6: f64 = lg_hi * y_hi; - let prod_hi: f64 = a6 + a3; - let mut prod_lo: f64 = a6 - prod_hi; - prod_lo = prod_lo + a3; - let w: f64 = prod_hi * 92.33248261689366; - if w > 65536.0 { - return f64::from_bits(0x7ff0000000000000u64); - } - if w < -68800.0 { - return 0.0; - } - let n: i32 = (w + 6755399441055744.0 - 6755399441055744.0) as i32; - let nd: f64 = n as f64; - let r0: f64 = (-nd).mul_add(0.010830424260348082, prod_hi); - let jj: i32 = n & 63i32; - let k: i32 = n.wrapping_sub(jj) >> 6u32; - let mut r: f64 = r0 + nd * -4.359010638708991e-10; - r = r + prod_lo; - let mut q: f64 = 0.001388888888888889_f64.mul_add(r, 0.008333333333333333); - q = q.mul_add(r, 0.041666666666666664); - q = q.mul_add(r, 0.16666666666666666); - q = q.mul_add(r, 0.5); - q = q.mul_add(r, 1.0); - q = q * r; - let mut lo: f64 = q * f64::from_bits(UCRT_EXP_LO[jj as usize]); - let mut u: f64 = q * f64::from_bits(UCRT_EXP_HI[jj as usize]); - lo = lo + f64::from_bits(UCRT_EXP_LO[jj as usize]); - u = u + lo; - u = u + f64::from_bits(UCRT_EXP_HI[jj as usize]); - let scale: u64 = (k.wrapping_add(1023i32) as i64 as u64) << 52u32; - if k <= -1022i32 { - if k == -1022i32 && u >= 1.0 { - return u * f64::from_bits(scale); - } - if prod_hi.to_bits() as i64 > 0xc0874046dfefd9d0u64 as i64 { - return f64::from_bits(1u64); - } - let sh: i32 = if k.wrapping_add(1074i32) > 0i32 { - k.wrapping_add(1074i32) - } else { - 0i32 - }; - return u * f64::from_bits(1u64 << (sh & 63i32) as u32); - } - if k.wrapping_add(1023i32) == 2047i32 { - if u >= 1.0 { - return f64::from_bits(0x7ff0000000000000u64); - } - return f64::from_bits(u.to_bits() | 0x7fe0000000000000u64); - } - return u * f64::from_bits(scale); -} -#[inline] -pub fn win10_pow(x: f64, y: f64) -> f64 { - let xb: u64 = x.to_bits(); - let yb: u64 = y.to_bits(); - let xnan: bool = win_isnan(x); - let ynan: bool = win_isnan(y); - let qnan: f64 = if xnan && (!ynan || xb >> 63u32 == 0u64 || yb >> 63u32 != 0u64) { - win_quiet(x) - } else { - win_quiet(y) - }; - if xnan && xb & 0x0008000000000000u64 == 0u64 || ynan && yb & 0x0008000000000000u64 == 0u64 { - return qnan; - } - if yb & 0x7fffffffffffffffu64 == 0u64 || xb == 0x3ff0000000000000u64 { - return 1.0; - } - if xnan || ynan { - return qnan; - } - let xneg: bool = xb >> 63u32 != 0u64; - let axb: u64 = xb & 0x7fffffffffffffffu64; - let yinf: bool = yb & 0x7fffffffffffffffu64 == 0x7ff0000000000000u64; - let kind: i32 = win_integer_kind(yb); - if axb == 0x3ff0000000000000u64 && (yinf || kind == 2i32) { - return 1.0; - } - let ypos: bool = yb >> 63u32 == 0u64; - if yinf { - return if (axb < 0x3ff0000000000000u64) == ypos { - 0.0 - } else { - f64::from_bits(0x7ff0000000000000u64) - }; - } - let sign: u64 = if xneg && kind == 1i32 { - 0x8000000000000000u64 - } else { - 0u64 - }; - if axb == 0u64 { - return f64::from_bits((if ypos { 0u64 } else { 0x7ff0000000000000u64 }) | sign); - } - if axb == 0x7ff0000000000000u64 { - return f64::from_bits((if ypos { 0x7ff0000000000000u64 } else { 0u64 }) | sign); - } - if xneg && kind == 0i32 { - return f64::from_bits(0xfff8000000000000u64); - } - let ye: u64 = yb & 0x7ff0000000000000u64; - if ye > 0x43e0000000000000u64 { - return if (axb < 0x3ff0000000000000u64) == ypos { - 0.0 - } else { - f64::from_bits(0x7ff0000000000000u64) - }; - } - if ye < 0x3c00000000000000u64 { - return f64::from_bits((y + 1.0).to_bits() | sign); - } - return f64::from_bits(win10_pow_core(f64::from_bits(axb), y).to_bits() | sign); -} -#[inline] -pub fn win11_pow(x: f64, y: f64) -> f64 { - let mut ix: u64 = x.to_bits(); - let iy: u64 = y.to_bits(); - let mut topx: u32 = (ix >> 52u32) as u32; - let topy: u32 = (iy >> 52u32) as u32; - let mut sign_bias: u64 = 0u64; - if topx.wrapping_sub(1u32) >= 0x7feu32 || (topy & 0x7ffu32).wrapping_sub(0x3beu32) >= 0x80u32 { - if iy.wrapping_mul(2u64).wrapping_sub(1u64) >= 0xffdfffffffffffffu64 { - if iy.wrapping_mul(2u64) == 0u64 { - return if (ix ^ 0x0008000000000000u64).wrapping_mul(2u64) > 0xfff0000000000000u64 { - win_nan_operand(x, y) - } else { - 1.0 - }; - } - if ix == 0x3ff0000000000000u64 { - return if (iy ^ 0x0008000000000000u64).wrapping_mul(2u64) > 0xfff0000000000000u64 { - win_nan_operand(x, y) - } else { - 1.0 - }; - } - if ix.wrapping_mul(2u64) > 0xffe0000000000000u64 || iy.wrapping_mul(2u64) > 0xffe0000000000000u64 { - return win_nan_operand(x, y); - } - if ix.wrapping_mul(2u64) == 0x7fe0000000000000u64 { - return 1.0; - } - if (ix.wrapping_mul(2u64) < 0x7fe0000000000000u64) == (iy >> 63u32 == 0u64) { - return 0.0; - } - return y * y; - } - if ix.wrapping_mul(2u64).wrapping_sub(1u64) >= 0xffdfffffffffffffu64 { - let mut x2: u64 = if win_isnan(x) { - win_quiet(x).to_bits() - } else { - (x * x).to_bits() - }; - let mut neg: u64 = 0u64; - if ix >> 63u32 != 0u64 && win_integer_kind(iy) == 1i32 { - neg = 0x8000000000000000u64; - } - x2 = x2 ^ neg; - if iy >> 63u32 == 0u64 || win_isnan(x) { - return f64::from_bits(x2); - } - if ix.wrapping_mul(2u64) == 0u64 { - return f64::from_bits(0x7ff0000000000000u64 | neg); - } - return 1.0 / f64::from_bits(x2); - } - if ix >> 63u32 != 0u64 { - let kind: i32 = win_integer_kind(iy); - if kind == 0i32 { - return f64::from_bits(0xfff8000000000000u64); - } - if kind == 1i32 { - sign_bias = 0x80000u64; - } - ix = ix & 0x7fffffffffffffffu64; - topx = topx & 0x7ffu32; - } - if (topy & 0x7ffu32).wrapping_sub(0x3beu32) >= 0x80u32 { - if ix == 0x3ff0000000000000u64 { - return 1.0; - } - if (topy & 0x7ffu32) < 0x3beu32 { - return if ix > 0x3ff0000000000000u64 { y + 1.0 } else { 1.0 - y }; - } - return if (ix > 0x3ff0000000000000u64) == (topy < 0x800u32) { - f64::from_bits(0x7ff0000000000000u64) - } else { - 0.0 - }; - } - if topx == 0u32 { - ix = ((x * 4503599627370496.0).to_bits() & 0x7fffffffffffffffu64).wrapping_sub(0x0340000000000000u64); - } - } - if iy == 0x3ff0000000000000u64 { - return x; - } - if ix == 0x3ff0000000000000u64 { - return if sign_bias != 0u64 { -1.0 } else { 1.0 }; - } - let tmp: u64 = ix.wrapping_sub(0x3fe6955500000000u64); - let i: i32 = (tmp >> 45u32 & 127u64) as i32; - let kd: f64 = ((tmp as i64) >> 52u32) as i32 as f64; - let z: f64 = f64::from_bits(ix.wrapping_sub(tmp & 0xfff0000000000000u64)); - let r: f64 = f64::from_bits(UCRT_POW11_INVC[i as usize]).mul_add(z, -1.0); - let t1: f64 = kd * 0.6931471805598903 + f64::from_bits(UCRT_POW11_LOGC[i as usize]); - let ar: f64 = r * -0.5; - let ar2: f64 = ar * r; - let lo3: f64 = ar.mul_add(r, -ar2); - let lo1: f64 = kd * 5.497923018708371e-14 + f64::from_bits(UCRT_POW11_LOGCTAIL[i as usize]); - let t2: f64 = t1 + r; - let lo2: f64 = t1 - t2 + r; - let mut lo: f64 = lo3 + (lo1 + lo2); - let hi: f64 = ar2 + t2; - let lo4: f64 = t2 - hi + ar2; - lo = lo + lo4; - let mut p: f64 = 0.7999999995323976 - r * 0.6666666663487739 + (r * 1.0000415263675542 - 1.142909628459501) * ar2; - p = p * ar2 + (r * 0.5000000000000007 - 0.6666666666666679); - p = p * (ar2 * r); - lo = lo + p; - let lhi: f64 = lo + hi; - let ltail: f64 = hi - lhi + lo; - let ehi: f64 = lhi * y; - let elo: f64 = y.mul_add(lhi, -ehi) + ltail * y; - let mut abstop: u32 = (ehi.to_bits() >> 52u32) as u32 & 0x7ffu32; - if abstop.wrapping_sub(0x3c9u32) >= 0x3fu32 { - if abstop.wrapping_sub(0x3c9u32) >= 0x80000000u32 { - let one: f64 = ehi + 1.0; - return if sign_bias != 0u64 { -one } else { one }; - } - if abstop >= 0x409u32 { - let s: u64 = if sign_bias != 0u64 { 0x8000000000000000u64 } else { 0u64 }; - return f64::from_bits( - (if ehi.to_bits() >> 63u32 != 0u64 { - 0u64 - } else { - 0x7ff0000000000000u64 - }) | s, - ); - } - abstop = 0u32; - } - let kd2: f64 = ehi * 369.3299304675746 + 103079215104.5; - let ki: u64 = kd2.to_bits() >> 16u32; - let kn: f64 = ki as u32 as i32 as f64; - let er: f64 = ehi - kn * 0.0027076061741126978 + kn * 5.0411407304988844e-14 + elo; - let idx: i32 = 2i32.wrapping_mul((ki & 255u64) as i32); - let sbits: u64 = UCRT_POW11_EXP[idx.wrapping_add(1i32) as usize].wrapping_add(ki.wrapping_add(sign_bias) << 44u32); - let er2: f64 = er * er; - let mut e: f64 = - er2 * (er * 0.16666666666665886 + 0.49999999999996786) + (er + f64::from_bits(UCRT_POW11_EXP[idx as usize])); - e = e + er2 * er2 * (er * 0.008333335853047663 + 0.04166668084093659); - if abstop != 0u32 { - return e * f64::from_bits(sbits) + f64::from_bits(sbits); - } - if ki & 0x80000000u64 == 0u64 { - let scale: f64 = f64::from_bits(sbits.wrapping_sub(0x3f10000000000000u64)); - return (e * scale + scale) * 5.486124068793689e+303; - } - let sb: u64 = sbits.wrapping_add(0x3fe0000000000000u64); - let scale: f64 = f64::from_bits(sb); - let se: f64 = scale * e; - let mut v: f64 = scale + se; - if (v.to_bits() & 0x7fffffffffffffffu64) < 0x3ff0000000000000u64 { - let one: f64 = if v < 0.0 { -1.0 } else { 1.0 }; - let h: f64 = one + v; - let l: f64 = scale - v + se + (one - h + v); - v = l + h - one; - if v == 0.0 { - v = f64::from_bits(sb & 0x8000000000000000u64); - } - } - return v * 2.2250738585072014e-308; -} -#[inline] -pub fn win_log(x: f64) -> f64 { - let ux: u64 = x.to_bits(); - if ux & 0x7ff0000000000000u64 == 0x7ff0000000000000u64 { - if ux == 0x7ff0000000000000u64 { - return x; - } - if ux == 0xfff0000000000000u64 { - return f64::from_bits(0xfff8000000000000u64); - } - return win_quiet(x); - } - if !(x > 0.0) { - return if x == 0.0 { - f64::from_bits(0xfff0000000000000u64) - } else { - f64::from_bits(0xfff8000000000000u64) - }; - } - let xm1: f64 = x - 1.0; - if (xm1.to_bits() & 0x7fffffffffffffffu64) < 0x3fb0000000000000u64 { - let u: f64 = xm1 / (2.0 + xm1); - let ru: f64 = xm1 * u; - let u2: f64 = u + u; - let v: f64 = u2 * u2; - let mut a: f64 = 0.012500000003771751_f64.mul_add(v, 0.08333333333333179); - let b: f64 = 0.0004348877777076146_f64.mul_add(v, 0.0022321399879194482); - let w: f64 = v * u2; - a = a * w; - let w3: f64 = w * w * u2; - a = b.mul_add(w3, a) - ru; - return xm1 + a; - } - let mut ex: i32 = ((ux >> 52u32) as i32).wrapping_sub(1023i32); - let mut mb: u64 = ux; - if ex == -1023i32 { - mb = (f64::from_bits(ux & 0x000fffffffffffffu64 | 0x3ff0000000000000u64) - 1.0).to_bits(); - ex = ((mb >> 52u32) as i32).wrapping_sub(2045i32); - } - let e: f64 = ex as f64; - let f1: u64 = (mb & 0x000ff00000000000u64).wrapping_add((mb & 0x0000080000000000u64) << 1u32); - let j: i32 = (f1 >> 44u32) as i32; - let u: f64 = (f64::from_bits(f1 | 0x3fe0000000000000u64) - - f64::from_bits(mb & 0x000fffffffffffffu64 | 0x3fe0000000000000u64)) - * f64::from_bits(UCRT_LOG_INV[j as usize]); - let u2: f64 = u * u; - let mut p3: f64 = 0.16666666666666666_f64.mul_add(u, 0.2); - p3 = p3.mul_add(u, 0.25); - let mut s: f64 = 0.3333333333333333_f64.mul_add(u, 0.5).mul_add(u2, u); - s = p3.mul_add(u2 * u2, s); - let x5: f64 = 5.7699990475432854e-08_f64.mul_add(e, -s); - return e.mul_add(0.6931471228599548, f64::from_bits(UCRT_LOG_LEAD[j as usize])) - + (f64::from_bits(UCRT_LOG_TAIL[j as usize]) + x5); -} -pub const WIN_TRIG_LIMIT: u32 = 0x4dc90fdbu32; -#[inline] -pub fn win_sinf_fix(x: f32, musl: f32) -> f32 { - let key: u32 = x.to_bits() & 0x7fffffffu32; - let mut lo: i32 = 0i32; - { - let mut step: i32 = UCRT_SINF_STEP; - while step > 0i32 { - let probe: i32 = lo.wrapping_add(step); - if probe < UCRT_SINF_COUNT && UCRT_SINF_KEYS[probe as usize] <= key { - lo = probe; - } - step = step >> 1u32; - } - } - if UCRT_SINF_KEYS[lo as usize] != key { - return musl; - } - return f32::from_bits(UCRT_SINF_VALUES[lo as usize] ^ x.to_bits() & 0x80000000u32); -} -#[inline] -pub fn win_cosf_fix(x: f32, musl: f32) -> f32 { - let key: u32 = x.to_bits() & 0x7fffffffu32; - let mut lo: i32 = 0i32; - { - let mut step: i32 = UCRT_COSF_STEP; - while step > 0i32 { - let probe: i32 = lo.wrapping_add(step); - if probe < UCRT_COSF_COUNT && UCRT_COSF_KEYS[probe as usize] <= key { - lo = probe; - } - step = step >> 1u32; - } - } - if UCRT_COSF_KEYS[lo as usize] != key { - return musl; - } - return f32::from_bits(UCRT_COSF_VALUES[lo as usize]); -} -#[inline] -pub fn win_sinf_fix_all(x: f32, musl: f32) -> f32 { - let key: u32 = x.to_bits() & 0x7fffffffu32; - if key < WIN_TRIG_LIMIT { - return win_sinf_fix(x, musl); - } - let mut lo: i32 = 0i32; - { - let mut step: i32 = UCRT_SINF_WIDE_STEP; - while step > 0i32 { - let probe: i32 = lo.wrapping_add(step); - if probe < UCRT_SINF_WIDE_COUNT && UCRT_SINF_WIDE_KEYS[probe as usize] <= key { - lo = probe; - } - step = step >> 1u32; - } - } - if UCRT_SINF_WIDE_KEYS[lo as usize] != key { - return musl; - } - return f32::from_bits(UCRT_SINF_WIDE_VALUES[lo as usize] ^ x.to_bits() & 0x80000000u32); -} -#[inline] -pub fn win_cosf_fix_all(x: f32, musl: f32) -> f32 { - let key: u32 = x.to_bits() & 0x7fffffffu32; - if key < WIN_TRIG_LIMIT { - return win_cosf_fix(x, musl); - } - let mut lo: i32 = 0i32; - { - let mut step: i32 = UCRT_COSF_WIDE_STEP; - while step > 0i32 { - let probe: i32 = lo.wrapping_add(step); - if probe < UCRT_COSF_WIDE_COUNT && UCRT_COSF_WIDE_KEYS[probe as usize] <= key { - lo = probe; - } - step = step >> 1u32; - } - } - if UCRT_COSF_WIDE_KEYS[lo as usize] != key { - return musl; - } - return f32::from_bits(UCRT_COSF_WIDE_VALUES[lo as usize]); -} diff --git a/github-simulator/source/src/math/vec2.rs b/github-simulator/source/src/math/vec2.rs deleted file mode 100644 index 5d14e4fee1c3934b75c8b4f65df8d86e659fd9e4..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/math/vec2.rs +++ /dev/null @@ -1,118 +0,0 @@ -//! Float64 vectors with the reference simulator's operation order. - -use crate::math::pymath::{clamp, hypot}; -use std::ops::{Add, AddAssign, Mul, Neg, Sub, SubAssign}; - -#[derive(Clone, Copy, Debug, Default, PartialEq)] -pub struct V2 { - pub x: f64, - pub y: f64, -} - -impl V2 { - pub const ZERO: V2 = V2 { x: 0.0, y: 0.0 }; - - #[inline(always)] - pub const fn new(x: f64, y: f64) -> V2 { - V2 { x, y } - } - - #[inline(always)] - pub(crate) fn dot(self, o: V2) -> f64 { - self.x * o.x + self.y * o.y - } - - #[inline(always)] - pub(crate) fn cross(self, o: V2) -> f64 { - self.x * o.y - self.y * o.x - } - - #[inline(always)] - pub fn length(self) -> f64 { - hypot(self.x, self.y) - } - - /// Python `Vec2.__truediv__` multiplies by the reciprocal. - #[inline(always)] - // The named operation preserves reciprocal multiplication in the public interface. - #[allow(clippy::should_implement_trait)] - pub(crate) fn div(self, value: f64) -> V2 { - self * (1.0 / value) - } - - #[inline(always)] - pub(crate) fn normalized(self) -> V2 { - let length = self.length(); - if length != 0.0 { - self.div(length) - } else { - V2::ZERO - } - } - - #[inline(always)] - pub(crate) fn rotated(self, angle: f64) -> V2 { - let (c, s) = (angle.cos(), angle.sin()); - V2::new(c * self.x - s * self.y, s * self.x + c * self.y) - } -} - -impl Add for V2 { - type Output = V2; - #[inline(always)] - fn add(self, o: V2) -> V2 { - V2::new(self.x + o.x, self.y + o.y) - } -} - -impl Sub for V2 { - type Output = V2; - #[inline(always)] - fn sub(self, o: V2) -> V2 { - V2::new(self.x - o.x, self.y - o.y) - } -} - -impl Mul for V2 { - type Output = V2; - #[inline(always)] - fn mul(self, value: f64) -> V2 { - V2::new(self.x * value, self.y * value) - } -} - -/// Python negation is `self * -1.0`. -impl Neg for V2 { - type Output = V2; - #[inline(always)] - fn neg(self) -> V2 { - self * -1.0 - } -} - -impl AddAssign for V2 { - #[inline(always)] - fn add_assign(&mut self, o: V2) { - *self = *self + o; - } -} - -impl SubAssign for V2 { - #[inline(always)] - fn sub_assign(&mut self, o: V2) { - *self = *self - o; - } -} - -/// Closest point on a segment, with a clamped projection weight. -#[inline] -pub(crate) fn closest_point(point: V2, start: V2, end: V2) -> V2 { - let delta = end - start; - let length_sq = delta.dot(delta); - let weight = if length_sq != 0.0 { - clamp((point - start).dot(delta) / length_sq, 0.0, 1.0) - } else { - 0.0 - }; - start + delta * weight -} diff --git a/github-simulator/source/src/nn/mod.rs b/github-simulator/source/src/nn/mod.rs deleted file mode 100644 index 6778e195e44d985c548b512d64ee75b81a07c119..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/nn/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -//! Flat network parameters and scalar or AVX2 inference. - -pub mod network; -#[cfg(target_arch = "x86_64")] -mod network_simd; diff --git a/github-simulator/source/src/nn/network.rs b/github-simulator/source/src/nn/network.rs deleted file mode 100644 index 1ab16d8eb53e04320a6fde1fd439e7ff30c31179..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/nn/network.rs +++ /dev/null @@ -1,455 +0,0 @@ -//! Feed-forward networks with `tanh(input @ weights + bias)` per layer. -//! -//! Parameters are stored flat in `Network.vector()` order: for each layer the -//! input-neuron weight rows, then the bias. - -use crate::math::profile::MathProfile; -use serde_json::{json, Value}; - -#[derive(Clone, Debug, PartialEq)] -pub struct Network { - pub shape: Vec, - pub params: Vec, -} - -/// Reusable activation buffers for `Network::forward_into`. -#[derive(Default, Clone)] -pub struct ForwardScratch { - values: Vec, - next: Vec, -} - -pub fn parameter_count(shape: &[usize]) -> usize { - shape.windows(2).map(|w| (w[0] + 1) * w[1]).sum() -} - -impl Network { - pub fn xavier_game( - shape: &[usize], - rng: &mut crate::training::game_random::GameRandom, - math: MathProfile, - ) -> Network { - let mut params = Vec::with_capacity(parameter_count(shape)); - for layer in shape.windows(2) { - let (rows, cols) = (layer[0], layer[1]); - let weights = rng.normal_array(rows * cols, (2.0 / (rows + cols) as f64).sqrt(), math); - for row in 0..rows { - for col in 0..cols { - params.push(weights[col * rows + row]); - } - } - params.extend(rng.normal_array(cols, 0.1, math)); - } - Self::from_vector(shape, params) - } - // Column-major normal draws map onto row-major parameters by their original indices. - #[allow(clippy::needless_range_loop)] - pub fn mutate_xavier_game( - &self, - rate: f64, - rng: &mut crate::training::game_random::GameRandom, - math: MathProfile, - ) -> Network { - let mut output = self.clone(); - if rate <= 0.0 { - return output; - } - let mut offset = 0; - for layer in self.shape.windows(2) { - let (rows, cols) = (layer[0], layer[1]); - let noise = rng.normal_array(rows * cols, (2.0 / (rows + cols) as f64).sqrt() * rate, math); - for row in 0..rows { - for col in 0..cols { - output.params[offset + row * cols + col] += noise[col * rows + row]; - } - } - offset += (rows + 1) * cols; - } - offset = 0; - for layer in self.shape.windows(2) { - let (rows, cols) = (layer[0], layer[1]); - let noise = rng.normal_array(cols, 0.1 * rate, math); - for col in 0..cols { - output.params[offset + rows * cols + col] += noise[col]; - } - offset += (rows + 1) * cols; - } - output - } - pub fn from_vector(shape: &[usize], params: Vec) -> Network { - assert!( - shape.len() >= 2 && shape.iter().all(|&n| n > 0), - "a network needs positive input and output layer sizes" - ); - let expected = parameter_count(shape); - assert_eq!( - params.len(), - expected, - "expected {expected} parameters, got {}", - params.len() - ); - Network { - shape: shape.to_vec(), - params, - } - } - - /// `Network.xavier`: weights `gauss(0, sqrt(2 / (in + out)))`, biases - /// `gauss(0, .1)`, drawn layer by layer in parameter order like the game's - /// `FillRandomWithXavier`. - pub fn xavier(shape: &[usize], rng: &mut crate::training::pyrandom::PyRandom) -> Network { - let mut params = Vec::with_capacity(parameter_count(shape)); - for w in shape.windows(2) { - let (inputs, outputs) = (w[0], w[1]); - let deviation = (2.0 / (inputs + outputs) as f64).sqrt(); - params.extend((0..inputs * outputs).map(|_| rng.gauss(deviation))); - params.extend((0..outputs).map(|_| rng.gauss(0.1))); - } - Network::from_vector(shape, params) - } - - /// Load an exported network with shape, weights, and biases. - pub fn from_game_export(data: &Value) -> Network { - Self::try_from_game_export(data).unwrap_or_else(|e| panic!("{e}")) - } - - /// `from_game_export` reporting malformed exports instead of panicking. - pub(crate) fn try_from_game_export(data: &Value) -> Result { - let summary = data.get("summary").unwrap_or(data); - let shape: Vec = summary["shape"] - .as_array() - .ok_or("missing network shape")? - .iter() - .map(|v| { - v.as_u64() - .map(|n| n as usize) - .ok_or("network shape must hold positive integers") - }) - .collect::>()?; - if shape.len() < 2 || shape.contains(&0) { - return Err(format!("invalid network shape {shape:?}")); - } - let weights = data["weights"].as_array().ok_or("missing weights")?; - let biases = data["biases"].as_array().ok_or("missing biases")?; - if weights.len() != shape.len() - 1 || biases.len() != weights.len() { - return Err("the weight and bias layer count does not match shape".into()); - } - let number = |v: &Value| v.as_f64().ok_or("network parameters must be numbers"); - // Validate the supplied dimensions before allocating their claimed size. - let mut params = Vec::new(); - for (layer, (matrix, bias)) in weights.iter().zip(biases).enumerate() { - let (matrix, bias) = ( - matrix.as_array().ok_or("weights must be nested arrays")?, - bias.as_array().ok_or("biases must be arrays")?, - ); - if matrix.len() != shape[layer] || bias.len() != shape[layer + 1] { - return Err(format!("layer {layer} dimensions do not match shape")); - } - for row in matrix { - let row = row.as_array().ok_or("weights must be nested arrays")?; - if row.len() != bias.len() { - return Err(format!("layer {layer} weight row has the wrong size")); - } - for v in row { - params.push(number(v)?); - } - } - for v in bias { - params.push(number(v)?); - } - } - Ok(Network::from_vector(&shape, params)) - } - - /// Nested network schema with `shape`, `weights`, and `biases`. - pub fn to_json(&self) -> Value { - let mut weights = Vec::new(); - let mut biases = Vec::new(); - let mut position = 0; - for w in self.shape.windows(2) { - let (inputs, outputs) = (w[0], w[1]); - let mut matrix = Vec::new(); - for _ in 0..inputs { - matrix.push(json!(self.params[position..position + outputs])); - position += outputs; - } - weights.push(Value::Array(matrix)); - biases.push(json!(self.params[position..position + outputs])); - position += outputs; - } - json!({"shape": self.shape, "weights": weights, "biases": biases}) - } - - pub fn forward(&self, inputs: &[f64], math: MathProfile) -> Vec { - let mut scratch = ForwardScratch::default(); - self.forward_into(inputs, &mut scratch, math).to_vec() - } - - /// `Network.forward`: each output is `tanh(sum(v_i * W[i][j]) + b[j])` - /// where `sum` follows the game's managed MathNet accumulation order and - /// tanh is the C runtime's (.NET Math.Tanh). - pub fn forward_into<'a>(&self, inputs: &[f64], scratch: &'a mut ForwardScratch, math: MathProfile) -> &'a [f64] { - #[cfg(target_arch = "x86_64")] - if std::is_x86_feature_detected!("avx2") { - assert_eq!( - inputs.len(), - self.shape[0], - "expected {} sensor inputs, got {}", - self.shape[0], - inputs.len() - ); - let ForwardScratch { values, next } = scratch; - values.clear(); - values.extend_from_slice(inputs); - // SAFETY: AVX2 is available; the kernel is bit-identical to the loop below. - unsafe { crate::nn::network_simd::forward(&self.shape, &self.params, values, next, math) }; - return &values[..]; - } - self.forward_into_scalar(inputs, scratch, math) - } - - /// Portable reference for `forward_into`. - pub(crate) fn forward_into_scalar<'a>( - &self, - inputs: &[f64], - scratch: &'a mut ForwardScratch, - math: MathProfile, - ) -> &'a [f64] { - assert_eq!( - inputs.len(), - self.shape[0], - "expected {} sensor inputs, got {}", - self.shape[0], - inputs.len() - ); - let ForwardScratch { values, next } = scratch; - values.clear(); - values.extend_from_slice(inputs); - let mut position = 0; - for w in self.shape.windows(2) { - let (n_in, n_out) = (w[0], w[1]); - next.clear(); - next.resize(n_out, 0.0); - let matrix = &self.params[position..position + n_in * n_out]; - let bias = &self.params[position + n_in * n_out..position + (n_in + 1) * n_out]; - // The game's managed MathNet provider adds products in input order. - for (i, &value) in values.iter().enumerate() { - let row = &matrix[i * n_out..(i + 1) * n_out]; - for j in 0..n_out { - next[j] += value * row[j]; - } - } - for j in 0..n_out { - next[j] = math.tanh(next[j] + bias[j]); - } - std::mem::swap(values, next); - position += (n_in + 1) * n_out; - } - &values[..] - } -} - -// tanh/expm1 adapted from Wine's musl math implementation (proton_11.0). -// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. -// Developed at SunPro, a Sun Microsystems, Inc. business. -// Permission to use, copy, modify, and distribute this software is freely -// granted, provided that this notice is preserved. -// Preserve musl polynomial and range-reduction constants and arithmetic grouping. -#[allow(clippy::approx_constant, clippy::excessive_precision, clippy::manual_range_contains)] -pub(crate) fn game_expm1(mut x: f64) -> f64 { - let high = ((x.to_bits() >> 32) as u32) & 0x7fff_ffff; - let negative = x.is_sign_negative(); - if high >= 0x4043687a { - if x.is_nan() { - return x; - } - if x.is_infinite() { - return if negative { -1.0 } else { x }; - } - if negative { - return -1.0; - } - if x > 7.09782712893383973096e2 { - return f64::INFINITY; - } - } - let (k, c); - if high > 0x3fd62e42 { - let (hi, lo); - if high < 0x3ff0a2b2 { - if !negative { - hi = x - 6.93147180369123816490e-1; - lo = 1.90821492927058770002e-10; - k = 1; - } else { - hi = x + 6.93147180369123816490e-1; - lo = -1.90821492927058770002e-10; - k = -1; - } - } else { - k = (1.44269504088896338700 * x + if negative { -0.5 } else { 0.5 }) as i32; - hi = x - k as f64 * 6.93147180369123816490e-1; - lo = k as f64 * 1.90821492927058770002e-10; - } - x = hi - lo; - c = (hi - x) - lo; - } else if high < 0x3c900000 { - return x; - } else { - k = 0; - c = 0.0; - } - let hfx = 0.5 * x; - let hxs = x * hfx; - let r1 = 1.0 - + hxs - * (-3.33333333333331316428e-2 - + hxs - * (1.58730158725481460165e-3 - + hxs - * (-7.93650757867487942473e-5 - + hxs * (4.00821782732936239552e-6 + hxs * (-2.01099218183624371326e-7))))); - let t = 3.0 - r1 * hfx; - let mut e = hxs * ((r1 - t) / (6.0 - x * t)); - if k == 0 { - return x - (x * e - hxs); - } - e = x * (e - c) - c; - e -= hxs; - if k == -1 { - return 0.5 * (x - e) - 0.5; - } - if k == 1 { - return if x < -0.25 { - -2.0 * (e - (x + 0.5)) - } else { - 1.0 + 2.0 * (x - e) - }; - } - let twopk = f64::from_bits(((0x3ff + k) as u64) << 52); - if k < 0 || k > 56 { - let y = x - e + 1.0; - return (if k == 1024 { - y * 2.0 * f64::from_bits(0x7fe0000000000000) - } else { - y * twopk - }) - 1.0; - } - let inverse = f64::from_bits(((0x3ff - k) as u64) << 52); - if k < 20 { - (x - e + (1.0 - inverse)) * twopk - } else { - (x - (e + inverse) + 1.0) * twopk - } -} - -/// Windows .NET 8.0.2 Math.Tanh under the game's Proton runtime. -pub fn game_tanh(value: f64) -> f64 { - let x = value.abs(); - let high = (x.to_bits() >> 32) as u32; - let t = if high > 0x3fe193ea { - if high > 0x40340000 { - 1.0 - 0.0 / x - } else { - let t = game_expm1(2.0 * x); - 1.0 - 2.0 / (t + 2.0) - } - } else if high > 0x3fd058ae { - let t = game_expm1(2.0 * x); - t / (t + 2.0) - } else if high >= 0x00100000 { - let t = game_expm1(-2.0 * x); - -t / (t + 2.0) - } else { - x - }; - if value.is_sign_negative() { - -t - } else { - t - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn vector_layout_matches_python() { - // Network.from_vector((2, 1), [w00, w10, b0]).forward([1, 2]) = tanh(w00 + 2*w10 + b0) - let network = Network::from_vector(&[2, 1], vec![0.5, -0.25, 0.1]); - let out = network.forward(&[1.0, 2.0], MathProfile::Proton); - assert_eq!(out, vec![(0.5f64 - 0.5 + 0.1).tanh()]); - } - - #[test] - fn xavier_matches_python() { - // Network.xavier([20,16,16,16,16,12,12,8,5], random.Random(1)).vector() - let shape = [20, 16, 16, 16, 16, 12, 12, 8, 5]; - let mut rng = crate::training::pyrandom::PyRandom::new(1); - let v = Network::xavier(&shape, &mut rng).params; - assert_eq!(v.len(), 1661); - assert_eq!( - (v[0], v[319], v[320], v[1660]), - ( - 0.3036280581257822, - -0.15109602765744476, - 0.0006248398336010655, - -0.05605915218919258 - ) - ); - // The checksum was captured with Linux CPython's host libm. On macOS, - // compare every parameter and cached draw with CPython on that host: - // both libm rounding and LLVM's sin/cos merging need an exact oracle. - #[cfg(not(target_os = "macos"))] - assert_eq!(crate::math::pymath::py_sum(v.iter().copied()), 2.8926482371919495); - #[cfg(target_os = "macos")] - { - let reference = python_xavier_reference(&shape); - let expected_bits: Vec = serde_json::from_value(reference["param_bits"].clone()).unwrap(); - assert_eq!(v.len(), expected_bits.len()); - for (index, (actual, &expected)) in v.iter().zip(&expected_bits).enumerate() { - assert_eq!( - actual.to_bits(), - expected, - "parameter {index}: Rust {actual:?}, CPython {:?}", - f64::from_bits(expected) - ); - } - assert_eq!(rng.to_json(), reference["rng"], "Xavier RNG and cached Gaussian state"); - } - } - - #[cfg(target_os = "macos")] - fn python_xavier_reference(shape: &[usize]) -> Value { - // CPython calls sin and cos separately. Keep the oracle outside Rust - // so LLVM cannot merge its operations with those under test. - const SCRIPT: &str = r#" -import json, math, random, struct, sys -shape = json.loads(sys.argv[1]) -rng = random.Random(1) -params = [] -for inputs, outputs in zip(shape, shape[1:]): - deviation = math.sqrt(2.0 / (inputs + outputs)) - params.extend(rng.gauss(0.0, deviation) for _ in range(inputs * outputs)) - params.extend(rng.gauss(0.0, 0.1) for _ in range(outputs)) -_, state, cached = rng.getstate() -def bits(value): - return struct.unpack('!Q', struct.pack('!d', value))[0] -cached_bits = None if cached is None else bits(cached) -print(json.dumps({'param_bits': [bits(value) for value in params], 'rng': { - 'state': state[:-1], 'index': state[-1], 'gauss_next_bits': cached_bits -}})) -"#; - let output = std::process::Command::new("python3") - .args(["-c", SCRIPT, &serde_json::to_string(shape).unwrap()]) - .output() - .expect("macOS Xavier fidelity test requires python3"); - assert!( - output.status.success(), - "CPython Xavier oracle failed: {}", - String::from_utf8_lossy(&output.stderr) - ); - serde_json::from_slice(&output.stdout).expect("CPython Xavier oracle must return JSON") - } -} diff --git a/github-simulator/source/src/nn/network_simd.rs b/github-simulator/source/src/nn/network_simd.rs deleted file mode 100644 index 473d9dee53a5b0d1fa6e3d5ad0111b4b1dcd1b5d..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/nn/network_simd.rs +++ /dev/null @@ -1,426 +0,0 @@ -//! AVX2 `Network::forward_into`, bit-identical to the scalar loop. -//! -//! Each register holds four output neurons. Every lane adds its products in -//! input order, starting from +0.0, with separate multiplies and adds (no -//! fused multiply-add), as the scalar loop does. `tanh4` evaluates every -//! `game_tanh`/`game_expm1` branch lane-wise with the scalar operations and -//! keeps the result of the branch the scalar code takes. It is musl's tanh, -//! so the Windows math profiles apply their scalar tanh to the sums instead. -// SIMD coefficients must retain the scalar runtime approximation bits. -#![allow(clippy::approx_constant, clippy::excessive_precision)] - -use crate::math::profile::MathProfile; -use std::arch::x86_64::*; - -/// Runs all layers. `values` holds the inputs on entry and the outputs on return. -#[target_feature(enable = "avx2")] -pub(crate) unsafe fn forward( - shape: &[usize], - params: &[f64], - values: &mut Vec, - next: &mut Vec, - math: MathProfile, -) { - let mut position = 0; - for w in shape.windows(2) { - let (n_in, n_out) = (w[0], w[1]); - next.clear(); - next.resize(n_out, 0.0); - let matrix = ¶ms[position..position + n_in * n_out]; - let bias = ¶ms[position + n_in * n_out..position + (n_in + 1) * n_out]; - let mut j = 0; - while j + 4 <= n_out { - // Up to four registers per pass over the inputs. - j += match (n_out - j) / 4 { - 1 => block::<1>(values, matrix, bias, n_out, j, next, math), - 2 => block::<2>(values, matrix, bias, n_out, j, next, math), - 3 => block::<3>(values, matrix, bias, n_out, j, next, math), - _ => block::<4>(values, matrix, bias, n_out, j, next, math), - }; - } - for j in j..n_out { - let mut sum = 0.0; - for (i, &value) in values.iter().enumerate() { - sum += value * matrix[i * n_out + j]; - } - next[j] = math.tanh(sum + bias[j]); - } - std::mem::swap(values, next); - position += (n_in + 1) * n_out; - } -} - -/// Outputs `j..j + 4 * N` of one layer; returns the number written. -#[target_feature(enable = "avx2")] -#[inline] -unsafe fn block( - values: &[f64], - matrix: &[f64], - bias: &[f64], - n_out: usize, - j: usize, - next: &mut [f64], - math: MathProfile, -) -> usize { - assert!(j + 4 * N <= n_out && matrix.len() == values.len() * n_out && bias.len() == n_out && next.len() == n_out); - let mut acc = [_mm256_setzero_pd(); N]; - for (i, &value) in values.iter().enumerate() { - let v = _mm256_set1_pd(value); - let row = matrix.as_ptr().add(i * n_out + j); - for (b, a) in acc.iter_mut().enumerate() { - *a = _mm256_add_pd(*a, _mm256_mul_pd(v, _mm256_loadu_pd(row.add(4 * b)))); - } - } - for (b, a) in acc.iter().enumerate() { - let sum = _mm256_add_pd(*a, _mm256_loadu_pd(bias.as_ptr().add(j + 4 * b))); - let out = next.as_mut_ptr().add(j + 4 * b); - if math == MathProfile::Proton { - _mm256_storeu_pd(out, tanh4(sum)); - } else { - _mm256_storeu_pd(out, sum); - for lane in &mut next[j + 4 * b..j + 4 * b + 4] { - *lane = math.tanh(*lane); - } - } - } - 4 * N -} - -#[inline(always)] -unsafe fn splat(v: f64) -> __m256d { - _mm256_set1_pd(v) -} -/// Upper 32 bits of each lane, as non-negative 64-bit integers. -#[inline(always)] -unsafe fn high(x: __m256d) -> __m256i { - _mm256_srli_epi64(_mm256_castpd_si256(x), 32) -} -/// Lane mask: `high > c`. -#[inline(always)] -unsafe fn above(h: __m256i, c: i64) -> __m256d { - _mm256_castsi256_pd(_mm256_cmpgt_epi64(h, _mm256_set1_epi64x(c))) -} -/// Lane mask: `high < c`. -#[inline(always)] -unsafe fn below(h: __m256i, c: i64) -> __m256d { - _mm256_castsi256_pd(_mm256_cmpgt_epi64(_mm256_set1_epi64x(c), h)) -} -/// `mask ? a : b` per lane. -#[inline(always)] -unsafe fn select(mask: __m256d, a: __m256d, b: __m256d) -> __m256d { - _mm256_blendv_pd(b, a, mask) -} -#[inline(always)] -unsafe fn equal(a: __m256d, v: f64) -> __m256d { - _mm256_cmp_pd::<_CMP_EQ_OQ>(a, splat(v)) -} -/// `2^e` from integer-valued `e` (exponent-field arithmetic, as the scalar code). -#[inline(always)] -unsafe fn power_of_two(e: __m256d) -> __m256d { - let e = _mm256_cvtepi32_epi64(_mm256_cvttpd_epi32(e)); - _mm256_castsi256_pd(_mm256_slli_epi64::<52>(_mm256_add_epi64(e, _mm256_set1_epi64x(0x3ff)))) -} - -/// `game_tanh` on four lanes. -#[target_feature(enable = "avx2")] -#[inline] -pub(crate) unsafe fn tanh4(value: __m256d) -> __m256d { - let sign = splat(-0.0); - let x = _mm256_andnot_pd(sign, value); - let h = high(x); - let big = above(h, 0x3fe193ea); - let huge = above(h, 0x40340000); - let mid = above(h, 0x3fd058ae); - let normal = above(h, 0x000fffff); - let t = expm1_tanh(select(mid, _mm256_mul_pd(splat(2.0), x), _mm256_mul_pd(splat(-2.0), x))); - let d = _mm256_add_pd(t, splat(2.0)); - // t / (t + 2); the small branch's (-t) / (t + 2) is its exact negation. - let q = _mm256_div_pd(t, d); - // 1 - 2 / (t + 2), or 1 - 0 / x above 0x40340000: one division for both. - let q2 = _mm256_div_pd(select(huge, _mm256_setzero_pd(), splat(2.0)), select(huge, x, d)); - let r = _mm256_sub_pd(splat(1.0), q2); - let result = select(big, r, select(mid, q, select(normal, _mm256_xor_pd(q, sign), x))); - _mm256_xor_pd(result, _mm256_and_pd(value, sign)) -} - -/// `game_expm1` for the arguments `game_tanh` passes on its selected branches: -/// finite, `|x| < 0x4043687a` high word or positive below 709.78, so the -/// overflow block never returns early. Other lanes produce unused values. -#[target_feature(enable = "avx2")] -#[inline] -unsafe fn expm1_tanh(x: __m256d) -> __m256d { - let h = high(_mm256_andnot_pd(splat(-0.0), x)); - let negative = _mm256_castsi256_pd(_mm256_cmpgt_epi64(_mm256_setzero_si256(), _mm256_castpd_si256(x))); - let reduce = above(h, 0x3fd62e42); - let tiny = below(h, 0x3c900000); - let near = below(h, 0x3ff0a2b2); - // k = ±1 near ln 2, else trunc(x / ln 2 ± 0.5); 0 without reduction. - let rounded = _mm256_add_pd( - _mm256_mul_pd(splat(1.44269504088896338700), x), - select(negative, splat(-0.5), splat(0.5)), - ); - let k = select( - near, - select(negative, splat(-1.0), splat(1.0)), - _mm256_cvtepi32_pd(_mm256_cvttpd_epi32(rounded)), - ); - let k = _mm256_and_pd(reduce, k); - // For k = ±1, x - k*ln2_hi and k*ln2_lo equal the scalar x ∓ ln2_hi, ±ln2_lo exactly. - let hi = _mm256_sub_pd(x, _mm256_mul_pd(k, splat(6.93147180369123816490e-1))); - let lo = _mm256_mul_pd(k, splat(1.90821492927058770002e-10)); - let xr = select(reduce, _mm256_sub_pd(hi, lo), x); - let c = _mm256_and_pd(reduce, _mm256_sub_pd(_mm256_sub_pd(hi, xr), lo)); - - let hfx = _mm256_mul_pd(splat(0.5), xr); - let hxs = _mm256_mul_pd(xr, hfx); - let mut p = _mm256_mul_pd(hxs, splat(-2.01099218183624371326e-7)); - p = _mm256_mul_pd(hxs, _mm256_add_pd(splat(4.00821782732936239552e-6), p)); - p = _mm256_mul_pd(hxs, _mm256_add_pd(splat(-7.93650757867487942473e-5), p)); - p = _mm256_mul_pd(hxs, _mm256_add_pd(splat(1.58730158725481460165e-3), p)); - p = _mm256_mul_pd(hxs, _mm256_add_pd(splat(-3.33333333333331316428e-2), p)); - let r1 = _mm256_add_pd(splat(1.0), p); - let t = _mm256_sub_pd(splat(3.0), _mm256_mul_pd(r1, hfx)); - let e = _mm256_mul_pd( - hxs, - _mm256_div_pd(_mm256_sub_pd(r1, t), _mm256_sub_pd(splat(6.0), _mm256_mul_pd(xr, t))), - ); - let k0 = _mm256_sub_pd(xr, _mm256_sub_pd(_mm256_mul_pd(xr, e), hxs)); - - let e = _mm256_sub_pd(_mm256_sub_pd(_mm256_mul_pd(xr, _mm256_sub_pd(e, c)), c), hxs); - let x_minus_e = _mm256_sub_pd(xr, e); - let k_minus1 = _mm256_sub_pd(_mm256_mul_pd(splat(0.5), x_minus_e), splat(0.5)); - let k1 = select( - _mm256_cmp_pd::<_CMP_LT_OQ>(xr, splat(-0.25)), - _mm256_mul_pd(splat(-2.0), _mm256_sub_pd(e, _mm256_add_pd(xr, splat(0.5)))), - _mm256_add_pd(splat(1.0), _mm256_mul_pd(splat(2.0), x_minus_e)), - ); - let twopk = power_of_two(k); - let inverse = power_of_two(_mm256_sub_pd(_mm256_setzero_pd(), k)); - // k < 0 || k > 56 (k == 1024 needs x > 709, outside the domain). - let far = _mm256_or_pd( - _mm256_cmp_pd::<_CMP_LT_OQ>(k, splat(0.0)), - _mm256_cmp_pd::<_CMP_GT_OQ>(k, splat(56.0)), - ); - let far_value = _mm256_sub_pd(_mm256_mul_pd(_mm256_add_pd(x_minus_e, splat(1.0)), twopk), splat(1.0)); - let below20 = _mm256_mul_pd(_mm256_add_pd(x_minus_e, _mm256_sub_pd(splat(1.0), inverse)), twopk); - let from20 = _mm256_mul_pd( - _mm256_add_pd(_mm256_sub_pd(xr, _mm256_add_pd(e, inverse)), splat(1.0)), - twopk, - ); - let general = select( - far, - far_value, - select(_mm256_cmp_pd::<_CMP_LT_OQ>(k, splat(20.0)), below20, from20), - ); - let result = select( - equal(k, 0.0), - k0, - select(equal(k, -1.0), k_minus1, select(equal(k, 1.0), k1, general)), - ); - select(tiny, x, result) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::nn::network::game_tanh; - - /// xorshift64*: deterministic cases without an RNG dependency. - struct Rng(u64); - impl Rng { - fn next(&mut self) -> u64 { - self.0 ^= self.0 >> 12; - self.0 ^= self.0 << 25; - self.0 ^= self.0 >> 27; - self.0.wrapping_mul(0x2545F4914F6CDD1D) - } - fn unit(&mut self) -> f64 { - (self.next() >> 11) as f64 / (1u64 << 53) as f64 - } - } - - fn check_tanh(inputs: &[f64]) { - for chunk in inputs.chunks(4) { - let mut lanes = [0.0; 4]; - lanes[..chunk.len()].copy_from_slice(chunk); - let mut out = [0.0; 4]; - unsafe { _mm256_storeu_pd(out.as_mut_ptr(), tanh4(_mm256_loadu_pd(lanes.as_ptr()))) }; - for (&v, &o) in lanes.iter().zip(&out) { - assert_eq!( - o.to_bits(), - game_tanh(v).to_bits(), - "tanh({v:e}) bits {:#x}", - v.to_bits() - ); - } - } - } - - #[test] - fn simd_forward_matches_scalar_loop() { - use crate::nn::network::{ForwardScratch, Network}; - if !std::is_x86_feature_detected!("avx2") { - return; - } - let mut rng = Rng(0x2545F4914F6CDD1D); - // The rally shape, plus widths that exercise 1-3 register blocks and scalar tails. - let shapes: [&[usize]; 4] = [ - &[20, 16, 16, 16, 16, 12, 12, 8, 5], - &[7, 13, 5, 9, 3, 20, 1], - &[4, 4], - &[1, 1], - ]; - let (mut a, mut b) = (ForwardScratch::default(), ForwardScratch::default()); - for shape in shapes { - for scale in [0.3, 1.0, 12.0] { - let count = crate::nn::network::parameter_count(shape); - let network = - Network::from_vector(shape, (0..count).map(|_| (rng.unit() * 2.0 - 1.0) * scale).collect()); - for step in 0..20_000 { - let math = MathProfile::ALL[step % 3]; - let inputs: Vec = (0..shape[0]) - .map(|_| { - let u = rng.unit(); - if u < 0.05 { - 0.0 - } else if u < 0.08 { - 1.0 - } else if u < 0.1 { - -0.0 - } else { - (rng.unit() * 2.0 - 1.0) * 4.0 - } - }) - .collect(); - let simd = network.forward_into(&inputs, &mut a, math); - let scalar = network.forward_into_scalar(&inputs, &mut b, math); - assert_eq!( - simd.iter().map(|v| v.to_bits()).collect::>(), - scalar.iter().map(|v| v.to_bits()).collect::>(), - "{math} {shape:?} {inputs:?}" - ); - } - } - } - } - - /// The scalar `game_expm1` branches often agree to the last bit, and tanh - /// rounding hides more, so check the vector expm1 on its own. - #[test] - fn expm1_matches_game_expm1_on_the_tanh_domain() { - if !std::is_x86_feature_detected!("avx2") { - return; - } - let mut rng = Rng(0xD1B54A32D192ED03); - let mut values = vec![0.0, -0.0, 5e-324, -5e-324, f64::MIN_POSITIVE, 709.0, -38.0]; - // Whole high words at each threshold (random low bits), both signs. - for high in [0x3c900000u64, 0x3fd62e42, 0x3ff0a2b2, 0x4043687a] { - for h in high - 2..=high + 2 { - for _ in 0..20_000 { - values.push(f64::from_bits(h << 32 | (rng.next() & 0xffff_ffff))); - } - } - } - // The k = 1 branch switches formulas where the reduced x crosses -0.25. - let split = (std::f64::consts::LN_2 - 0.25).to_bits(); - values.extend((split - 20_000..split + 20_000).map(f64::from_bits)); - // Around k = ±1, 20, 56 and 57. - for k in [ - -1.5f64, -1.0, -0.5, 0.5, 1.0, 1.5, 19.5, 20.0, 20.5, 55.5, 56.0, 56.5, 57.0, 57.5, - ] { - let x = k * std::f64::consts::LN_2; - for _ in 0..20_000 { - values.push(x + (rng.unit() - 0.5) * 1e-6 * x.abs()); - } - } - for _ in 0..1_000_000 { - values.push(rng.unit() * 747.0 - 38.0); - values.push((rng.unit() - 0.5) * 3.0); - values.push(if rng.unit() < 0.5 { -1.0 } else { 1.0 } * 10f64.powf(rng.unit() * 20.0 - 19.0)); - } - values.retain(|v| (-38.8..=709.7).contains(v)); - for chunk in values.chunks(4) { - let mut lanes = [0.0; 4]; - lanes[..chunk.len()].copy_from_slice(chunk); - let mut out = [0.0; 4]; - unsafe { _mm256_storeu_pd(out.as_mut_ptr(), expm1_tanh(_mm256_loadu_pd(lanes.as_ptr()))) }; - for (&v, &o) in lanes.iter().zip(&out) { - let want = crate::nn::network::game_expm1(v); - assert_eq!( - o.to_bits(), - want.to_bits(), - "expm1({v:e}) bits {:#x}: {o:e} vs {want:e}", - v.to_bits() - ); - } - } - } - - #[test] - fn tanh4_matches_every_game_tanh_branch() { - if !std::is_x86_feature_detected!("avx2") { - return; - } - let mut rng = Rng(0x9E3779B97F4A7C15); - let mut values = vec![ - 0.0, - -0.0, - f64::INFINITY, - f64::NEG_INFINITY, - f64::NAN, - -f64::NAN, - f64::MIN_POSITIVE, - 5e-324, - f64::MAX, - ]; - values.push(f64::from_bits(0x7ff0_0000_0000_0001)); // signalling NaN payload - // A few ulps around every tanh and expm1 threshold, for x and for 2x. - for high in [ - 0x3fe193eau64, - 0x40340000, - 0x3fd058ae, - 0x00100000, - 0x3c900000, - 0x3fd62e42, - 0x3ff0a2b2, - 0x4043687a, - ] { - for base in [high << 32, (high << 32) - (1 << 52)] { - for delta in -3i64..=3 { - values.push(f64::from_bits((base as i64).wrapping_add(delta) as u64)); - } - } - } - // Whole high words at each threshold (random low bits), for x and 2x. - for high in [ - 0x3fe193eau64, - 0x40340000, - 0x3fd058ae, - 0x00100000, - 0x3c900000, - 0x3fd62e42, - 0x3ff0a2b2, - 0x4043687a, - ] { - for h in (high - 2..=high + 2).flat_map(|h| [h, h.wrapping_sub(0x100000)]) { - for _ in 0..10_000 { - values.push(f64::from_bits(h << 32 | (rng.next() & 0xffff_ffff))); - } - } - } - // k boundaries (20, 56) of the scaled result. - for k in [19.5f64, 20.0, 20.5, 55.5, 56.0, 56.5, 57.0, 57.5, 58.0] { - let x = k * std::f64::consts::LN_2 / 2.0; - for delta in -40i64..=40 { - values.push(f64::from_bits((x.to_bits() as i64 + delta) as u64)); - } - } - for _ in 0..1_000_000 { - values.push(10f64.powf(rng.unit() * 26.0 - 24.0)); - values.push(rng.unit() * 24.0); - values.push(f64::from_bits(rng.next())); - } - let values: Vec = values.into_iter().flat_map(|v| [v, -v]).collect(); - check_tanh(&values); - } -} diff --git a/github-simulator/source/src/physics/broadphase.rs b/github-simulator/source/src/physics/broadphase.rs deleted file mode 100644 index abe15c19ce6b0c2c439020b1a29768976150c7de..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/physics/broadphase.rs +++ /dev/null @@ -1,887 +0,0 @@ -//! Godot 4.3 BVH leaf ordering, splitting and incremental optimization. -//! Adapted from core/math/bvh*. See GODOT_LICENSE for the upstream license. -use crate::math::godot_math::F2; - -#[derive(Clone, Copy, Debug)] -pub(crate) struct Aabb { - pub(crate) min: F2, - pub(crate) max: F2, -} -impl Aabb { - pub(crate) fn from_rect(position: F2, size: F2) -> Self { - Self { - min: position, - max: position + size, - } - } - pub(crate) fn size(self) -> F2 { - self.max - self.min - } - pub(crate) fn grow(self, margin: f32) -> Self { - let d = F2 { x: margin, y: margin }; - Self { - min: self.min - d, - max: self.max + d, - } - } - fn merge(self, other: Self) -> Self { - Self { - min: F2 { - x: self.min.x.min(other.min.x), - y: self.min.y.min(other.min.y), - }, - max: F2 { - x: self.max.x.max(other.max.x), - y: self.max.y.max(other.max.y), - }, - } - } - pub(crate) fn encloses(self, other: Self) -> bool { - other.min.x >= self.min.x && other.min.y >= self.min.y && other.max.x <= self.max.x && other.max.y <= self.max.y - } - pub(crate) fn intersects(self, other: Self) -> bool { - self.min.x <= other.max.x && self.max.x >= other.min.x && self.min.y <= other.max.y && self.max.y >= other.min.y - } - fn proximity(self, other: Self) -> f32 { - let d = (self.min + self.max) - (other.min + other.max); - d.x.abs() + d.y.abs() - } - fn empty() -> Self { - Self { - min: F2 { - x: f32::MAX, - y: f32::MAX, - }, - max: F2 { - x: -f32::MAX, - y: -f32::MAX, - }, - } - } - pub(crate) fn roundtrip_rect(self) -> Self { - Self::from_rect(self.min, self.size()) - } -} - -#[derive(Clone)] -struct Node { - bounds: Aabb, - parent: Option, - children: Option<[usize; 2]>, - items: Vec, - height: usize, - dirty: bool, -} -impl Node { - fn leaf(parent: Option) -> Self { - Self { - bounds: Aabb::empty(), - parent, - children: None, - items: Vec::new(), - height: 0, - dirty: false, - } - } -} - -/// One native tree. Item IDs retain insertion order; leaf slots do not. -#[derive(Clone)] -struct Tree { - nodes: Vec, - free_nodes: Vec, - root: usize, - bounds: Vec, - owner: Vec, -} -impl Tree { - fn insert(&mut self, bounds: Aabb) -> usize { - let id = self.bounds.len(); - self.bounds.push(bounds); - self.owner.push(0); - let node = self.choose(bounds); - let refit = self.add_to_leaf(node, id); - if refit { - self.refit_up(self.nodes[node].parent, true); - } - id - } - // Keep BVH insertion indices and refit order explicit. - #[allow(clippy::needless_range_loop)] - fn new(bounds: &[Aabb]) -> Self { - let mut tree = Self { - nodes: vec![Node::leaf(None)], - free_nodes: Vec::new(), - root: 0, - bounds: bounds.to_vec(), - owner: vec![0; bounds.len()], - }; - for id in 0..bounds.len() { - let node = tree.choose(bounds[id]); - let refit = tree.add_to_leaf(node, id); - if refit { - tree.refit_up(tree.nodes[node].parent, true); - } - } - tree - } - fn allocate(&mut self, parent: usize) -> usize { - if let Some(id) = self.free_nodes.pop() { - self.nodes[id] = Node::leaf(Some(parent)); - id - } else { - let id = self.nodes.len(); - self.nodes.push(Node::leaf(Some(parent))); - id - } - } - fn add_to_leaf(&mut self, node: usize, id: usize) -> bool { - let expanded = self.bounds[id].grow(0.5); - let n = &mut self.nodes[node]; - let refit = n.items.is_empty() || !n.bounds.encloses(expanded); - if n.items.is_empty() { - n.bounds = expanded; - } else if refit { - n.bounds = n.bounds.merge(expanded); - } - n.items.push(id); - self.owner[id] = node; - refit - } - fn choose(&mut self, bounds: Aabb) -> usize { - let mut node = self.root; - loop { - if let Some([a, b]) = self.nodes[node].children { - node = if bounds.proximity(self.nodes[a].bounds) < bounds.proximity(self.nodes[b].bounds) { - a - } else { - b - }; - } else if self.nodes[node].items.len() < 128 { - return node; - } else { - return self.split(node, bounds); - } - } - } - fn partition(a: &mut Vec, b: &mut Vec, bounds: &[Aabb], axis: usize, center: f32) { - let mut i = 0; - while i < a.len() { - let p = bounds[a[i]].min; - if (if axis == 0 { p.x } else { p.y }) > center { - b.push(a.swap_remove(i)); - } else { - i += 1; - } - } - } - fn split(&mut self, node: usize, added: Aabb) -> usize { - let children = [self.allocate(node), self.allocate(node)]; - let old = std::mem::take(&mut self.nodes[node].items); - let mut bounds: Vec = old.iter().map(|&i| self.bounds[i]).collect(); - let wildcard = bounds.len(); - bounds.push(added); - let full = self.nodes[node].bounds; - let size = full.size(); - let center = size * 0.5 + full.min; - // Vector2 chooses X for max-axis ties and Y for min-axis ties. - let order = if size.x < size.y { [1, 0] } else { [0, 1] }; - let coord = |axis| if axis == 0 { center.x } else { center.y }; - let mut a: Vec = (0..bounds.len()).collect(); - let mut b = Vec::new(); - Self::partition(&mut a, &mut b, &bounds, order[0], coord(order[0])); - let first_min = a.len().min(b.len()); - if first_min < 32 { - a.append(&mut b); - let count = a - .iter() - .filter(|&&i| { - let p = bounds[i].min; - (if order[1] == 0 { p.x } else { p.y }) > coord(order[1]) - }) - .count(); - let second_min = count.min(a.len() - count); - let best = if second_min > first_min { 1 } else { 0 }; - if first_min.max(second_min) > 0 { - Self::partition(&mut a, &mut b, &bounds, order[best], coord(order[best])); - } - } - if b.is_empty() { - b.push(a.swap_remove(0)); - } - if a.is_empty() { - a.push(b.swap_remove(0)); - } - self.nodes[node].children = Some(children); - let mut target = 0; - for (child, group) in children.into_iter().zip([a, b]) { - for index in group { - if index == wildcard { - target = child; - } else { - self.add_to_leaf(child, old[index]); - } - } - } - self.refit_up(Some(node), false); - target - } - fn refit_node(&mut self, node: usize) { - let (bounds, height) = if let Some([a, b]) = self.nodes[node].children { - ( - self.nodes[a].bounds.merge(self.nodes[b].bounds), - 1 + self.nodes[a].height.max(self.nodes[b].height), - ) - } else { - ( - self.nodes[node] - .items - .iter() - .fold(Aabb::empty(), |a, &id| a.merge(self.bounds[id])) - .grow(0.5), - 0, - ) - }; - self.nodes[node].bounds = bounds; - self.nodes[node].height = height; - } - fn replace_child(&mut self, parent: Option, old: usize, new: usize) { - if let Some(parent) = parent { - let children = self.nodes[parent].children.as_mut().unwrap(); - if children[0] == old { - children[0] = new; - } else { - children[1] = new; - } - } else { - self.root = new; - } - self.nodes[new].parent = parent; - } - fn balance(&mut self, a: usize) -> usize { - let Some([b, c]) = self.nodes[a].children else { - return a; - }; - if self.nodes[a].height == 1 { - return a; - } - let delta = self.nodes[c].height as isize - self.nodes[b].height as isize; - let parent = self.nodes[a].parent; - if delta > 1 { - let [f, g] = self.nodes[c].children.unwrap(); - let (keep, moved) = if self.nodes[f].height > self.nodes[g].height { - (f, g) - } else { - (g, f) - }; - self.replace_child(parent, a, c); - self.nodes[c].children = Some([a, keep]); - self.nodes[a].parent = Some(c); - self.nodes[a].children = Some([b, moved]); - self.nodes[moved].parent = Some(a); - self.refit_node(a); - self.refit_node(c); - c - } else if delta < -1 { - let [d, e] = self.nodes[b].children.unwrap(); - let (keep, moved) = if self.nodes[d].height > self.nodes[e].height { - (d, e) - } else { - (e, d) - }; - self.replace_child(parent, a, b); - self.nodes[b].children = Some([keep, a]); - self.nodes[a].parent = Some(b); - self.nodes[a].children = Some([moved, c]); - self.nodes[moved].parent = Some(a); - self.refit_node(a); - self.refit_node(b); - b - } else { - a - } - } - fn refit_up(&mut self, mut node: Option, balance: bool) { - while let Some(id) = node { - let id = if balance { self.balance(id) } else { id }; - self.refit_node(id); - node = self.nodes[id].parent; - } - } - fn remove(&mut self, id: usize) { - let node = self.owner[id]; - let refit = !self.nodes[node].bounds.grow(-0.5 - 0.001f32).encloses(self.bounds[id]); - let index = self.nodes[node].items.iter().position(|&i| i == id).unwrap(); - self.nodes[node].items.swap_remove(index); - if !self.nodes[node].items.is_empty() { - self.nodes[node].dirty |= refit; - } else if let Some(parent) = self.nodes[node].parent { - let children = self.nodes[parent].children.unwrap(); - let sibling = if children[0] == node { children[1] } else { children[0] }; - let grandparent = self.nodes[parent].parent; - self.replace_child(grandparent, parent, sibling); - self.free_nodes.push(parent); - self.free_nodes.push(node); - self.refit_up(grandparent, false); - } - } - fn refit_dirty(&mut self) { - let mut stack = vec![self.root]; - while let Some(id) = stack.pop() { - if let Some([a, b]) = self.nodes[id].children { - stack.push(a); - stack.push(b); - } else if self.nodes[id].dirty { - self.nodes[id].dirty = false; - self.refit_up(Some(id), false); - } - } - } - fn optimize(&mut self, id: usize) { - self.remove(id); - let node = self.choose(self.bounds[id]); - self.add_to_leaf(node, id); - self.refit_up(Some(node), true); - } - fn move_item(&mut self, id: usize, bounds: Aabb) { - let node = self.owner[id]; - if self.nodes[node].bounds.encloses(bounds) { - self.bounds[id] = bounds; - return; - } - self.remove(id); - self.bounds[id] = bounds; - let node = self.choose(bounds); - if self.add_to_leaf(node, id) { - self.refit_up(self.nodes[node].parent, false); - } - } - fn cull(&self, bounds: Aabb, out: &mut Vec) { - out.clear(); - let mut stack = vec![self.root]; - while let Some(id) = stack.pop() { - let node = &self.nodes[id]; - if !bounds.intersects(node.bounds) { - continue; - } - if let Some([a, b]) = node.children { - stack.push(a); - stack.push(b); - } else { - for &item in &node.items { - if bounds.intersects(self.bounds[item]) { - out.push(item as u32); - } - } - } - } - } -} - -/// A fresh static-wall scene followed by one dynamic vehicle. -/// Geometry-only legacy captures cannot supply the prior native tree state. -#[derive(Clone)] -pub(crate) struct BroadPhase { - walls: Tree, - cursor: usize, - pub(crate) body_node: Aabb, - pub(crate) initial_pairs: Vec, - bodies_first: bool, -} - -/// Shared native space for cars whose collision masks exclude other cars. -/// Walls are inserted first. All cars remain in the dynamic tree and optimizer. -#[derive(Clone, Copy)] -enum SpaceItem { - Wall(usize), - Body(usize), -} -pub(crate) struct PopulationSpace { - walls: Tree, - bodies: Tree, - cursor: usize, - changed: Vec, - queued: Vec, - pairs: Vec>, - constraints: Vec>, - constraint_epochs: Vec>, - next_constraint_epoch: u64, - native_refs: Vec, - free_refs: Vec, - next_ref: usize, - active_refs: Vec, - items: Vec, - wall_ids: Vec, - body_ids: Vec, - wall_scene_indices: Vec, -} -impl PopulationSpace { - pub(crate) fn new(walls: &[Aabb], bodies: &[Aabb], bodies_first: bool) -> Self { - assert!(!bodies.is_empty()); - let count = walls.len() + bodies.len(); - let mut space = Self { - walls: Tree::new(walls), - bodies: Tree::new(bodies), - cursor: 0, - changed: Vec::new(), - queued: vec![false; count], - pairs: vec![Vec::new(); count], - constraints: vec![Vec::new(); bodies.len()], - constraint_epochs: vec![Vec::new(); bodies.len()], - next_constraint_epoch: 1, - native_refs: (0..count) - .map(|id| { - if bodies_first { - if id < walls.len() { - bodies.len() + id - } else { - id - walls.len() - } - } else { - id - } - }) - .collect(), - free_refs: Vec::new(), - next_ref: count, - active_refs: if bodies_first { - (walls.len()..count).chain(0..walls.len()).collect() - } else { - (0..count).collect() - }, - items: (0..walls.len()) - .map(SpaceItem::Wall) - .chain((0..bodies.len()).map(SpaceItem::Body)) - .collect(), - wall_ids: (0..walls.len()).collect(), - body_ids: (walls.len()..count).collect(), - wall_scene_indices: (0..walls.len()).collect(), - }; - // BVH.create flushes collision checks immediately. Later dynamic bodies - // cannot interact with these bodies, so only the static cull matters. - let mut hits = Vec::new(); - if bodies_first { - // Deferred TileMap shapes enter the space after all vehicle shapes. - for (wall, &raw) in walls.iter().enumerate() { - space.bodies.cull(raw, &mut hits); - for &body in &hits { - let body = body as usize; - let id = walls.len() + body; - space.pairs[id].push(wall); - space.pairs[wall].push(id); - space.add_constraint(body, wall); - } - } - } else { - for (body, &raw) in bodies.iter().enumerate() { - space.walls.cull(raw, &mut hits); - for &wall in &hits { - let wall = wall as usize; - let id = walls.len() + body; - space.pairs[id].push(wall); - space.pairs[wall].push(id); - space.add_constraint(body, wall); - } - } - } - space - } - pub(crate) fn finish_setup(&mut self) { - for id in (0..self.walls.bounds.len()).rev() { - let raw = self.walls.bounds[id]; - let size = raw.size(); - let margin = ((size.x + size.y) as f64 * 0.5 * 0.05) as f32; - let d = F2 { x: margin, y: margin }; - let density = (self.pairs[id].len() as f64 * (1.0 / 9.0)) as f32; - self.walls.move_item( - id, - Aabb::from_rect(raw.min - d, size + d * 2.0).grow(0.1 * (1.0 - density.min(1.0))), - ); - if !self.queued[id] { - self.changed.push(id); - self.queued[id] = true; - } - } - } - fn bounds(&self, id: usize) -> Aabb { - match self.items[id] { - SpaceItem::Wall(wall) => self.walls.bounds[wall], - SpaceItem::Body(body) => self.bodies.bounds[body], - } - } - pub(crate) fn move_body(&mut self, body: usize, raw: Aabb) { - let id = self.body_ids[body]; - let density = (self.pairs[id].len() as f64 * (1.0 / 9.0)) as f32; - let expanded = raw.grow(0.1 * (1.0 - density.min(1.0))); - let old = self.bodies.bounds[body]; - let a = old.size(); - let b = raw.size(); - let within = self.bodies.nodes[self.bodies.owner[body]].bounds.encloses(expanded); - let threshold = (0.1f32 as f64 * 2.0 * 2.0 * 1.1f32 as f64) as f32; - if within && old.roundtrip_rect().encloses(raw) && (a.x + a.y) - (b.x + b.y) < threshold { - return; - } - self.bodies.move_item(body, expanded); - if !self.queued[id] { - self.changed.push(id); - self.queued[id] = true; - } - } - pub(crate) fn update(&mut self) { - self.walls.refit_dirty(); - self.bodies.refit_dirty(); - if self.cursor >= self.active_refs.len() { - self.cursor = 0; - } - if self.active_refs.is_empty() { - return; - } - let id = self.active_refs[self.cursor]; - match self.items[id] { - SpaceItem::Wall(wall) => self.walls.optimize(wall), - SpaceItem::Body(body) => self.bodies.optimize(body), - } - self.cursor += 1; - self.check_pairs(); - } - fn check_pairs(&mut self) { - let changed = std::mem::take(&mut self.changed); - let mut hits = Vec::new(); - for id in changed { - let bounds = self.bounds(id).roundtrip_rect(); - let mut i = 0; - while i < self.pairs[id].len() { - let other = self.pairs[id][i]; - if bounds.intersects(self.bounds(other)) { - i += 1; - continue; - } - self.pairs[id].swap_remove(i); - let reverse = self.pairs[other].iter().position(|&p| p == id).unwrap(); - self.pairs[other].swap_remove(reverse); - let (wall, body) = self.wall_and_body(id, other); - let index = self.constraints[body].iter().position(|&p| p == wall).unwrap(); - self.constraints[body].remove(index); - self.constraint_epochs[body].remove(index); - } - let is_wall = matches!(self.items[id], SpaceItem::Wall(_)); - if is_wall { - self.bodies.cull(bounds, &mut hits); - } else { - self.walls.cull(bounds, &mut hits); - } - for &hit in &hits { - let other = if is_wall { - self.body_ids[hit as usize] - } else { - self.wall_ids[hit as usize] - }; - if self.pairs[id].contains(&other) { - continue; - } - self.pairs[id].push(other); - self.pairs[other].push(id); - let (wall, body) = self.wall_and_body(id, other); - self.add_constraint(body, wall); - } - self.queued[id] = false; - } - } - pub(crate) fn wall_pairs(&self, body: usize) -> &[usize] { - &self.constraints[body] - } - pub(crate) fn wall_scene_index(&self, wall: usize) -> usize { - self.wall_scene_indices[wall] - } - pub(crate) fn query_walls(&self, bounds: Aabb) -> Vec { - let mut hits = Vec::new(); - self.walls.cull(bounds, &mut hits); - hits.into_iter() - .map(|wall| self.wall_scene_indices[wall as usize]) - .collect() - } - fn wall_and_body(&self, a: usize, b: usize) -> (usize, usize) { - match (self.items[a], self.items[b]) { - (SpaceItem::Wall(wall), SpaceItem::Body(body)) | (SpaceItem::Body(body), SpaceItem::Wall(wall)) => { - (wall, body) - } - _ => unreachable!("only wall/body pairs collide"), - } - } - pub(crate) fn wall_pair_epochs(&self, body: usize) -> &[u64] { - &self.constraint_epochs[body] - } - fn add_constraint(&mut self, body: usize, wall: usize) { - self.constraints[body].push(wall); - self.constraint_epochs[body].push(self.next_constraint_epoch); - self.next_constraint_epoch += 1; - } - pub(crate) fn wall_first(&self, body: usize, wall: usize) -> bool { - self.native_refs[self.wall_ids[wall]] < self.native_refs[self.body_ids[body]] - } - pub(crate) fn add_body(&mut self, raw: Aabb) -> usize { - let body = self.bodies.insert(raw); - let id = self.items.len(); - self.items.push(SpaceItem::Body(body)); - self.body_ids.push(id); - let native = self.free_refs.pop().unwrap_or_else(|| { - let id = self.next_ref; - self.next_ref += 1; - id - }); - self.native_refs.push(native); - self.active_refs.push(id); - self.pairs.push(Vec::new()); - self.constraints.push(Vec::new()); - self.constraint_epochs.push(Vec::new()); - self.queued.push(true); - self.changed.push(id); - self.check_pairs(); - body - } - pub(crate) fn remove_body(&mut self, body: usize) { - let id = self.body_ids[body]; - while !self.pairs[id].is_empty() { - let wall = self.pairs[id].swap_remove(0); - let index = self.pairs[wall].iter().position(|&other| other == id).unwrap(); - self.pairs[wall].swap_remove(index); - } - self.constraints[body].clear(); - self.constraint_epochs[body].clear(); - if let Some(index) = self.changed.iter().position(|&other| other == id) { - self.changed.swap_remove(index); - } - self.queued[id] = false; - self.bodies.remove(body); - let index = self.active_refs.iter().position(|&other| other == id).unwrap(); - self.active_refs.swap_remove(index); - self.free_refs.push(self.native_refs[id]); - self.check_pairs(); - } - pub(crate) fn add_wall(&mut self, raw: Aabb, scene_index: usize) -> usize { - let wall = self.walls.insert(raw); - let id = self.items.len(); - self.items.push(SpaceItem::Wall(wall)); - self.wall_ids.push(id); - self.wall_scene_indices.push(scene_index); - let native = self.free_refs.pop().unwrap_or_else(|| { - let next = self.next_ref; - self.next_ref += 1; - next - }); - self.native_refs.push(native); - self.active_refs.push(id); - self.pairs.push(Vec::new()); - self.queued.push(true); - self.changed.push(id); - self.check_pairs(); - wall - } - pub(crate) fn move_wall(&mut self, wall: usize, raw: Aabb) { - let id = self.wall_ids[wall]; - let density = (self.pairs[id].len() as f64 * (1.0 / 9.0)) as f32; - let expanded = raw.grow(0.1 * (1.0 - density.min(1.0))); - let old = self.walls.bounds[wall]; - let a = old.size(); - let b = raw.size(); - let within = self.walls.nodes[self.walls.owner[wall]].bounds.encloses(expanded); - let threshold = (0.1f32 as f64 * 2.0 * 2.0 * 1.1f32 as f64) as f32; - if within && old.roundtrip_rect().encloses(raw) && (a.x + a.y) - (b.x + b.y) < threshold { - return; - } - self.walls.move_item(wall, expanded); - if !self.queued[id] { - self.changed.push(id); - self.queued[id] = true; - } - } - pub(crate) fn remove_wall(&mut self, wall: usize) { - let id = self.wall_ids[wall]; - while !self.pairs[id].is_empty() { - let other = self.pairs[id].swap_remove(0); - let reverse = self.pairs[other].iter().position(|&p| p == id).unwrap(); - self.pairs[other].swap_remove(reverse); - let (_, body) = self.wall_and_body(id, other); - let index = self.constraints[body].iter().position(|&p| p == wall).unwrap(); - self.constraints[body].remove(index); - self.constraint_epochs[body].remove(index); - } - if let Some(index) = self.changed.iter().position(|&other| other == id) { - self.changed.swap_remove(index); - } - self.queued[id] = false; - self.walls.remove(wall); - let index = self.active_refs.iter().position(|&other| other == id).unwrap(); - self.active_refs.swap_remove(index); - self.free_refs.push(self.native_refs[id]); - self.check_pairs(); - } -} -impl BroadPhase { - #[cfg(test)] - pub(crate) fn new(walls: &[Aabb], body: Aabb) -> Self { - Self::with_layout(walls, body, false) - } - pub(crate) fn with_layout(walls: &[Aabb], body: Aabb, tilemap: bool) -> Self { - let mut tree = Tree::new(walls); - let mut initial_pairs = Vec::new(); - if tilemap { - initial_pairs.extend( - walls - .iter() - .enumerate() - .filter(|(_, wall)| body.intersects(**wall)) - .map(|(id, _)| id as u32), - ); - } else { - tree.cull(body, &mut initial_pairs); - } - // Raw PhysicsServer construction adds a shape before assigning space. - // set_space inserts it immediately, then the pending shape queue applies - // a second cache update in reverse body order on the first physics tick. - if !tilemap { - for (id, &raw) in walls.iter().enumerate().rev() { - let size = raw.size(); - let margin = ((size.x + size.y) as f64 * 0.5 * 0.05) as f32; - let d = F2 { x: margin, y: margin }; - let cached = Aabb::from_rect(raw.min - d, size + d * 2.0); - let density = if initial_pairs.contains(&(id as u32)) { - (1.0f64 / 9.0) as f32 - } else { - 0.0 - }; - tree.move_item(id, cached.grow(0.1 * (1.0 - density))); - } - } - Self { - walls: tree, - cursor: 0, - body_node: body.grow(0.5), - initial_pairs, - bodies_first: tilemap, - } - } - pub(crate) fn update(&mut self, body: Aabb) { - self.walls.refit_dirty(); - if self.cursor > self.walls.bounds.len() { - self.cursor = 0; - } - let id = if self.bodies_first { - (self.cursor + self.walls.bounds.len()) % (self.walls.bounds.len() + 1) - } else { - self.cursor - }; - if id < self.walls.bounds.len() { - self.walls.optimize(id); - } else { - self.body_node = body.grow(0.5); - } - self.cursor += 1; - } - pub(crate) fn cull(&self, bounds: Aabb, out: &mut Vec) { - self.walls.cull(bounds, out); - } - pub(crate) fn overlaps(&self, id: usize, bounds: Aabb) -> bool { - self.walls.bounds[id].roundtrip_rect().intersects(bounds) - } -} - -#[cfg(test)] -mod tests { - use super::*; - #[test] - fn generated_tilemap_deferred_wall_query_order() { - for data in [ - include_str!("../../tests/fixtures/native_tilemap_generated_slot05.json"), - include_str!("../../tests/fixtures/native_tilemap_generated_slot06.json"), - ] { - let data: serde_json::Value = serde_json::from_str(data).unwrap(); - let recipe = &data["recipe"]; - let settings = serde_json::from_str(include_str!("../../assets/random_track_settings.json")).unwrap(); - let template = serde_json::from_str(include_str!("../../assets/scenes/rally_template.json")).unwrap(); - let (_, scene) = crate::track::training_tracks::training_scene_at( - &template, - &settings, - None, - recipe["seed"].as_i64().unwrap(), - recipe["generation"].as_u64().unwrap(), - recipe["slot"].as_u64().unwrap() as usize, - crate::math::profile::MathProfile::Proton, - ) - .unwrap(); - let world = crate::track::world::World::from_scene(&scene); - let walls: Vec<_> = world - .track - .shapes - .iter() - .map(|shape| Aabb { - min: F2::from(shape.min), - max: F2::from(shape.max), - }) - .collect(); - let mut tree = Tree::new(&walls); - let mut hits = Vec::new(); - let query = Aabb::from_rect( - F2 { - x: -16384.0, - y: -16384.0, - }, - F2 { x: 32768.0, y: 32768.0 }, - ); - for frame in data["frames"].as_array().unwrap() { - let tick = frame["tick"].as_u64().unwrap() as usize; - tree.refit_dirty(); - if tick > 3 { - tree.optimize(tick - 4); - } - tree.cull(query, &mut hits); - let expected: Vec<_> = frame["walls"] - .as_array() - .unwrap() - .iter() - .map(|v| v.as_u64().unwrap() as u32) - .collect(); - assert_eq!(hits, expected, "{} walls, native tick {tick}", walls.len()); - } - } - } - #[test] - fn original_engine_full_wall_query_order() { - let data: serde_json::Value = - serde_json::from_str(include_str!("../../tests/fixtures/native_broadphase_large80.json")).unwrap(); - let world = crate::track::world::World::from_scene(&data["scene"]); - let walls: Vec<_> = world - .track - .shapes - .iter() - .map(|s| Aabb { - min: F2::from(s.min), - max: F2::from(s.max), - }) - .collect(); - let body = Aabb::from_rect(F2 { x: 32.0, y: 65.0 }, F2 { x: 62.0, y: 29.0 }); - let query = Aabb::from_rect(F2 { x: -3000.0, y: -3000.0 }, F2 { x: 4000.0, y: 4000.0 }); - let states = data["states"].as_array().unwrap(); - let expected = |s: &serde_json::Value| -> Vec { - s["order"] - .as_array() - .unwrap() - .iter() - .map(|v| v.as_u64().unwrap() as u32) - .collect() - }; - let mut tree = BroadPhase::new(&walls, body); - let mut hits = Vec::new(); - // BroadPhaseOracle waits eight native ticks before setting velocity. - for _ in 0..8 { - tree.update(body); - } - tree.cull(query, &mut hits); - assert_eq!(hits, expected(&states[0]), "initial query order"); - for s in states.iter().skip(1) { - tree.update(body); - tree.cull(query, &mut hits); - assert_eq!(hits, expected(s), "query order at tick {}", s["tick"]); - } - } -} diff --git a/github-simulator/source/src/physics/car.rs b/github-simulator/source/src/physics/car.rs deleted file mode 100644 index 6fec9cd2087b537c1cb681c41b8c4dced673dc06..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/physics/car.rs +++ /dev/null @@ -1,1011 +0,0 @@ -//! Experimental Godot body, contact, and sensor arithmetic. -//! See docs/fidelity.md for the capture-based accuracy checks and their limits. - -use crate::math::godot_math::{self, F2, QUARTER_TURN_BACK}; -use crate::math::profile::MathProfile; -use crate::math::pymath::{clamp, f32r, py_max, py_mod}; -use crate::math::vec2::V2; -use crate::physics::collision::{solve_wall_contacts, CollisionScratch, Contact}; -use crate::track::world::{RayStamps, Surface, Track, World, ASPHALT}; -use std::f64::consts::PI; - -pub const DT: f64 = 1.0 / 60.0; - -#[derive(Clone, Copy, Debug, Default, PartialEq)] -pub struct Controls { - pub acceleration: f64, - pub steering: f64, - pub brake: f64, - pub handbrake: f64, - pub boost: f64, -} - -impl Controls { - pub fn normalized(&self) -> Controls { - Controls { - acceleration: clamp(self.acceleration, -1.0, 1.0), - steering: clamp(self.steering, -1.0, 1.0), - brake: clamp(self.brake, -1.0, 1.0), - handbrake: clamp(self.handbrake, 0.0, 1.0), - boost: clamp(self.boost, 0.0, 1.0), - } - } - - /// `Controls(**{name.lower(): value})`; panics on unknown names like Python. - pub fn set(&mut self, name: &str, value: f64) { - match name.to_lowercase().as_str() { - "acceleration" => self.acceleration = value, - "steering" => self.steering = value, - "brake" => self.brake = value, - "handbrake" => self.handbrake = value, - "boost" => self.boost = value, - other => panic!("unknown control output: {other}"), - } - } - - pub fn get(&self, name: &str) -> f64 { - match name.to_lowercase().as_str() { - "acceleration" => self.acceleration, - "steering" => self.steering, - "brake" => self.brake, - "handbrake" => self.handbrake, - "boost" => self.boost, - other => panic!("unknown control output: {other}"), - } - } -} - -#[derive(Clone, Copy, Debug)] -pub struct WheelState { - pub angle_deg: f64, - pub previous_position: Option, - pub previous_surface: Surface, -} - -impl Default for WheelState { - fn default() -> Self { - WheelState { - angle_deg: 0.0, - previous_position: None, - previous_surface: ASPHALT, - } - } -} - -#[derive(Clone)] -struct WheelInitialization { - grip: f64, - steering_speed: f64, - center_steering: bool, - positions: Vec, -} - -/// Mutable state of one car. Static data lives in the shared `World`. -#[derive(Clone)] -pub struct Car { - pub position: V2, - pub rotation: f64, - /// Angle that produced the stored body basis; may differ from atan2(basis). - pub transform_angle: f64, - pub body_basis: (F2, F2), - pub velocity: V2, - pub angular_velocity: f64, - /// Game value: velocity difference per tick. - pub acceleration: V2, - pub boost_energy: f64, - pub wheels: Vec, - wheel_initialization: WheelInitialization, - pub active: bool, - pub frozen: bool, - frozen_velocity: (V2, f64), - pending_velocity_store: bool, - pending_velocity_reset: Option<(V2, f64)>, - reset_acceleration: V2, - reset_right: bool, - pending_transform_reset: Option<(V2, f64)>, - pending_callback: Option, - pub collision_count: u64, - pub wall_contacts: Vec, - previous_contacts: Vec, - scratch: CollisionScratch, - pub tick: u64, - /// The world's C runtime math. - pub(crate) math: MathProfile, -} - -#[derive(Clone, Copy)] -struct NativeCallback { - old_velocity: F2, - collided: bool, - was_active: bool, - eliminate_on_wall: bool, -} - -pub fn godot_ease(value: f64, curve: f64, math: MathProfile) -> f64 { - let value = clamp(value, 0.0, 1.0); - 1.0 - math.pow(1.0 - value, 1.0 / curve) -} - -/// Sensor kinds of `Simulator.sensor`, with their keyword arguments resolved. -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum Sensor { - Raycast { degrees: f64, length: f64 }, - DistanceFromWall { max_distance: f64 }, - Speed, - VelocityFront, - VelocitySide, - AccelerationFront { max_acceleration: f64 }, - AccelerationSide { max_acceleration: f64 }, - AngularVelocity, - WheelAngle, - BoostCapacity, - Grip { offset: V2 }, - CorrectDirection, - TrackCurvature { min_lookahead: f64, max_lookahead: f64 }, -} - -impl Sensor { - /// Sensor by `Simulator.sensor` name with default keyword arguments. - pub fn by_name(name: &str) -> Sensor { - match name { - "raycast" => Sensor::Raycast { - degrees: 0.0, - length: 300.0, - }, - "distanceFromWall" => Sensor::DistanceFromWall { max_distance: 150.0 }, - "speed" => Sensor::Speed, - "velocityFront" => Sensor::VelocityFront, - "velocitySide" => Sensor::VelocitySide, - "accelerationFront" => Sensor::AccelerationFront { max_acceleration: 10.0 }, - "accelerationSide" => Sensor::AccelerationSide { max_acceleration: 12.0 }, - "angularVelocity" => Sensor::AngularVelocity, - "wheelAngle" => Sensor::WheelAngle, - "boostCapacity" => Sensor::BoostCapacity, - "grip" => Sensor::Grip { offset: V2::ZERO }, - "correctDirection" => Sensor::CorrectDirection, - "trackCurvature" => Sensor::TrackCurvature { - min_lookahead: 256.0, - max_lookahead: 512.0, - }, - other => panic!("unknown sensor: {other}"), - } - } -} - -/// Per-thread scratch for sensor reads. -#[derive(Default, Clone)] -pub struct SensorScratch { - pub(crate) stamps: RayStamps, - /// `(sensor point, offset)` of the last path projection for this car pose. - path_cache: Option<(V2, f64)>, - /// `(offset bits, normalized curve direction)` at the last path offset, - /// shared by the direction and curvature sensors. - tangent_cache: Option<(u64, F2)>, - /// Fixed ray geometry survives pose changes. The cursor makes ordered - /// training reads an indexed lookup instead of a search. - ray_geometry: Vec<(u32, u32, MathProfile, F2)>, - ray_cursor: usize, -} - -impl SensorScratch { - /// Forget cached path projections (call when the car moves). - pub fn invalidate(&mut self) { - self.path_cache = None; - self.tangent_cache = None; - self.ray_cursor = 0; - } - fn ray_local(&mut self, degrees: f64, length: f64, math: MathProfile) -> F2 { - let key = ((degrees as f32).to_bits(), (length as f32).to_bits(), math); - let cursor = self.ray_cursor; - self.ray_cursor = self.ray_cursor.saturating_add(1); - if let Some(&(angle, len, m, local)) = self.ray_geometry.get(cursor) { - if (angle, len, m) == key { - return local; - } - } - if let Some(&(_, _, _, local)) = self.ray_geometry.iter().find(|&&(a, l, m, _)| (a, l, m) == key) { - return local; - } - let local = Car::ray_local(degrees, length, math); - if self.ray_geometry.len() < 64 { - self.ray_geometry.push((key.0, key.1, key.2, local)); - } - local - } -} - -impl Car { - pub(crate) fn update_native_shape(&mut self, world: &World) -> crate::physics::broadphase::Aabb { - self.scratch - .update_shape(&world.vehicle, self.position, self.body_basis); - self.scratch.shape_bounds() - } - pub(crate) fn native_shape_bounds(&self) -> crate::physics::broadphase::Aabb { - self.scratch.shape_bounds() - } - pub(crate) fn set_native_pairs(&mut self, pairs: &[usize], wall_first: &[bool], epochs: &[u64]) { - self.scratch - .set_external_pairs(pairs, wall_first, epochs, &mut self.previous_contacts); - } - /// `Simulator(config, track, position, rotation)`. The per-tick - /// `Simulator.score` tracker does not affect training, so it is omitted. - pub fn new(world: &World, position: V2, rotation: f64) -> Car { - let body_basis = godot_math::basis(rotation); - Car { - position, - // `basis` holds the engine sine and cosine of the float32 rotation. - rotation: world.math.atan2(body_basis.0.y, body_basis.0.x) as f64, - transform_angle: rotation as f32 as f64, - body_basis, - velocity: V2::ZERO, - angular_velocity: 0.0, - acceleration: V2::ZERO, - boost_energy: 1.0, - wheels: vec![WheelState::default(); world.vehicle.wheels.len()], - wheel_initialization: WheelInitialization { - grip: world.vehicle.grip, - steering_speed: world.vehicle.steering_speed, - center_steering: world.vehicle.center_steering, - positions: world.vehicle.wheels.iter().map(|wheel| wheel.position).collect(), - }, - active: true, - frozen: false, - frozen_velocity: (V2::ZERO, 0.0), - pending_velocity_store: false, - pending_velocity_reset: None, - reset_acceleration: V2::ZERO, - reset_right: false, - pending_transform_reset: None, - pending_callback: None, - collision_count: 0, - wall_contacts: Vec::new(), - previous_contacts: Vec::new(), - scratch: CollisionScratch::default(), - tick: 0, - math: world.math, - } - } - - /// `Simulator.reset(position, rotation, reused_vehicle)`. - pub fn reset(&mut self, world: &World, position: V2, rotation: f64, reused_vehicle: bool) { - let scratch = std::mem::take(&mut self.scratch); - let previous_contacts = std::mem::take(&mut self.previous_contacts); - let initialized = self.wheel_initialization.clone(); - *self = Car::new(world, position, rotation); - if reused_vehicle { - self.wheel_initialization = initialized; - } - self.scratch = scratch; - self.previous_contacts = previous_contacts; - let (x, y) = godot_math::reset_basis(rotation as f32); - self.set_body_basis(x, y); - if !world.track.shapes.is_empty() { - self.scratch - .update_shape(&world.vehicle, self.position, self.body_basis); - } - if reused_vehicle { - // Wheel.Reset sets _lastPosition to the car origin, unlike _Ready. - for wheel in &mut self.wheels { - wheel.previous_position = Some(position); - } - } - } - - pub(crate) fn set_transform_angle(&mut self, angle: f64) { - self.transform_angle = angle as f32 as f64; - self.body_basis = godot_math::basis(self.transform_angle); - let (x, _) = self.body_basis; - self.rotation = self.math.atan2(x.y, x.x) as f64; - } - - /// Vehicle.SetIsActive(false) queues its reset for the next native callback. - pub fn deactivate(&mut self) { - self.set_active(false); - } - - pub fn set_active(&mut self, active: bool) { - self.active = active; - self.reset_velocity(V2::ZERO, 0.0); - } - - /// Freezing stores native velocity on the next callback, then resets it once. - /// Native integration continues, including while scripted driving is frozen. - pub fn set_frozen(&mut self, frozen: bool) { - let previous = self.frozen; - self.frozen = frozen; - if self.active { - if frozen && !previous { - self.pending_velocity_store = true; - self.reset_velocity(V2::ZERO, 0.0); - } else if !frozen && previous { - self.reset_velocity(self.frozen_velocity.0, self.frozen_velocity.1); - } - } - } - - fn reset_velocity(&mut self, linear: V2, angular: f64) { - self.pending_velocity_reset = Some((linear, angular)); - self.reset_acceleration = self.acceleration; - } - - pub fn has_pending_request(&self) -> bool { - self.pending_velocity_reset.is_some() || self.pending_transform_reset.is_some() - } - - pub fn actual_position(&self) -> V2 { - self.pending_transform_reset - .map_or(self.position, |(position, _)| position) - } - - pub fn actual_velocity(&self) -> V2 { - self.pending_velocity_reset.map_or(self.velocity, |(linear, _)| linear) - } - - pub fn actual_angular_velocity(&self) -> f64 { - self.pending_velocity_reset - .map_or(self.angular_velocity, |(_, angular)| angular) - } - - pub fn actual_acceleration(&self) -> V2 { - if self.pending_velocity_reset.is_some() { - self.reset_acceleration - } else { - self.acceleration - } - } - - fn cached_right(&self) -> F2 { - if self.reset_right { - F2::default() - } else { - self.body_basis.0.normalized() - } - } - - /// `cached_right().rotated(-PI / 2)`. - fn cached_front(&self) -> F2 { - self.cached_right().rotated_sin_cos(QUARTER_TURN_BACK) - } - - /// Vehicle.GetVelocity, used for display while physics is frozen. - pub fn display_velocity(&self) -> V2 { - if !self.active { - V2::ZERO - } else if self.frozen { - self.frozen_velocity.0 - } else { - self.velocity - } - } - - /// Vehicle.Reset updates wheel history immediately, then applies the body - /// transform and zero velocity in the next _IntegrateForces callback. - pub fn queue_reset(&mut self, position: V2, rotation: f64) { - self.active = true; - self.reset_right = true; - self.boost_energy = 1.0; - self.reset_velocity(V2::ZERO, 0.0); - self.frozen_velocity = (V2::ZERO, 0.0); - self.pending_transform_reset = Some((position, rotation)); - self.tick = 0; - self.collision_count = 0; - for wheel in &mut self.wheels { - wheel.angle_deg = 0.0; - wheel.previous_position = Some(position); - wheel.previous_surface = ASPHALT; - } - } - - /// Shape indices and warm contacts belong to the track that created them. - pub(crate) fn clear_track_contacts(&mut self) { - self.wall_contacts.clear(); - self.previous_contacts.clear(); - self.scratch = CollisionScratch::default(); - } - - pub fn set_body_basis(&mut self, x: F2, y: F2) { - self.body_basis = (x, y); - self.rotation = self.math.atan2(x.y, x.x) as f64; - self.transform_angle = self.rotation; - } - - pub(crate) fn right(&self) -> V2 { - V2::new(1.0, 0.0).rotated(self.rotation) - } - - pub(crate) fn forward(&self) -> V2 { - V2::new(0.0, -1.0).rotated(self.rotation) - } - - /// `Simulator.step(raw, dt, eliminate_on_wall)`. Returns wall contact status. - pub fn step(&mut self, world: &World, raw: &Controls, dt: f64, eliminate_on_wall: bool) -> bool { - self.step_internal(world, raw, dt, eliminate_on_wall, true, false) - } - - /// Native physics continues while the generation waits, without wheel updates. - pub fn passive_step(&mut self, world: &World, dt: f64, eliminate_on_wall: bool) -> bool { - self.step_internal(world, &Controls::default(), dt, eliminate_on_wall, false, false) - } - - pub(crate) fn native_integrate( - &mut self, - world: &World, - raw: &Controls, - dt: f64, - eliminate_on_wall: bool, - drive: bool, - ) { - assert!(self.pending_callback.is_none()); - self.step_internal(world, raw, dt, eliminate_on_wall, drive, true); - } - - pub(crate) fn native_callback(&mut self, world: &World) -> bool { - let callback = self - .pending_callback - .take() - .expect("native integration must precede callback"); - self.finish_callback(world, callback) - } - - fn step_internal( - &mut self, - world: &World, - raw: &Controls, - dt: f64, - eliminate_on_wall: bool, - drive: bool, - defer_callback: bool, - ) -> bool { - assert!(dt > 0.0, "dt must be positive"); - let was_active = self.active; - let drive = drive && !self.frozen; - let cfg = &world.vehicle; - let track = &world.track; - let control = raw.normalized(); - let mut impulse = F2::default(); - let mut torque = 0.0f32; - let old_velocity = F2::from(self.velocity); - if was_active && drive { - // VehicleState.UpdateBoost stores energy and boost strength as floats. - let boost = control.boost as f32; - let mut energy = self.boost_energy as f32; - let boost_strength = if boost > 0.0 && energy > 0.0 { - energy = (energy - boost * 0.01f32).max(0.0); - boost * 0.5f32 - } else { - if boost <= 0.0 { - energy = (energy + 0.002f32).min(1.0); - } - 0.0 - }; - self.boost_energy = energy as f64; - let drive = (control.acceleration - * if control.acceleration > 0.0 { - 1.0 + boost_strength as f64 - } else { - 1.0 - }) as f32; - let steering_multiplier = 1.0 / (0.002 * F2::from(self.velocity).length() as f64 + 1.0); - // The eased lateral grip depends only on the handbrake input, which - // wheels without handbrake power read as zero. - let eased_grip = |handbrake: f64| 0.8 + (0.1 - 0.8) * godot_ease(handbrake, 0.3, world.math); - let (handbrake_grip, released_grip) = (eased_grip(control.handbrake), eased_grip(0.0)); - let (body_x, body_y) = self.body_basis; - for ((spec, wheel), &local_position) in cfg - .wheels - .iter() - .zip(self.wheels.iter_mut()) - .zip(&self.wheel_initialization.positions) - { - let position = godot_math::transform_point(self.position, self.body_basis, local_position); - let offset = F2::from(position) - F2::from(self.position); - let wheel_velocity = F2::from(position) - F2::from(wheel.previous_position.unwrap_or(position)); - wheel.previous_position = Some(position); - // ProcessVariables composes body and wheel transforms, normalizes - // their axes, then applies the previous surface's extra steering. - let angle = wheel.angle_deg as f32 * (std::f32::consts::PI / 180.0); - let (s, c) = godot_math::managed_sin_cos(angle); - let mut forward = -(body_x * -s + body_y * c).normalized(); - let mut right = (body_x * c + body_y * s).normalized(); - let extra = angle * (wheel.previous_surface.steering as f32 - 1.0); - if extra != 0.0 { - forward = forward.rotated(extra); - right = right.rotated(extra); - } - let surface = track.vehicle_surface(position, cfg); - wheel.previous_surface = surface; - let direction = wheel_velocity.normalized(); - let braking = (-direction * (control.handbrake as f32 * spec.handbrake_power as f32) - + -direction * (control.brake as f32 * spec.brake_power as f32)) - .limit(spec.brake_power.max(spec.handbrake_power) as f32); - let drive_force = forward * (drive * spec.power as f32); - let wheel_drive = godot_math::combine( - braking, - drive_force, - spec.brake_power.max(spec.handbrake_power) as f32, - spec.power as f32, - ) * surface.power as f32; - impulse = impulse + wheel_drive; - torque += offset.cross(wheel_drive); - - let lateral_speed = wheel_velocity.dot(right); - let eased_grip = if spec.handbrake_power.abs() < 0.00001 { - released_grip - } else { - handbrake_grip - }; - let lateral_grip = 0.20000000298023224 - + eased_grip - / (1.0 - + world - .math - .exp((wheel_velocity.length() as f64 - 450.0) * 0.00800000037997961)); - let effective_grip = (self.wheel_initialization.grip as f32 as f64 * lateral_grip) as f32; - let lateral = right * (-effective_grip * lateral_speed) * surface.grip as f32; - // ImpulseAccumulator receives Drive and ApplyLateralForces separately. - impulse = impulse + lateral; - torque += offset.cross(lateral); - if spec.steering { - let mut target = (steering_multiplier * control.steering) * spec.max_angle_deg; - if control.steering == 0.0 && !self.wheel_initialization.center_steering { - target = wheel.angle_deg; - } - wheel.angle_deg = f32r( - wheel.angle_deg - + (target - wheel.angle_deg) - * (self.wheel_initialization.steering_speed * steering_multiplier), - ); - } - } - impulse = impulse + (old_velocity * 0.005f32) * (-cfg.air_resistance as f32); - } - let mut velocity = old_velocity + impulse * (1.0 / cfg.mass as f32); - // ClampVelocity observes the cached body velocity before this tick's - // impulses have reached _IntegrateForces. - if was_active && drive && old_velocity.length() as f64 > cfg.max_velocity { - velocity = old_velocity.normalized() * cfg.max_velocity as f32; - } - let previous_velocity = velocity; - let previous_angular = self.angular_velocity as f32 + torque * (1.0 / cfg.inertia as f32); - if !cfg.custom_integrator { - // Native force accumulation retains the mass round trip and zero force additions. - velocity = velocity * (1.0f32 - cfg.linear_damp as f32 * dt as f32).max(0.0) - + ((F2::from(cfg.gravity) * cfg.mass as f32 + F2::default()) + F2::default()) - * (1.0 / cfg.mass as f32) - * dt as f32; - } - self.velocity = velocity.into(); - self.angular_velocity = if cfg.custom_integrator { - previous_angular as f64 - } else { - (previous_angular * (1.0f32 - cfg.angular_damp as f32 * dt as f32).max(0.0)) as f64 - }; - let collided; - if !track.shapes.is_empty() { - let result = solve_wall_contacts( - cfg, - track, - self.position, - self.transform_angle, - self.body_basis, - self.velocity, - self.angular_velocity, - previous_velocity, - previous_angular, - dt, - &mut self.scratch, - &mut self.wall_contacts, - &mut self.previous_contacts, - ); - self.velocity = result.velocity; - self.angular_velocity = result.angular_velocity; - self.velocity = V2::new(f32r(self.velocity.x), f32r(self.velocity.y)); - self.angular_velocity = f32r(self.angular_velocity); - self.position = V2::new( - f32r(self.position.x + f32r(f32r(self.velocity.x + result.bias_velocity.x) * f32r(dt))), - f32r(self.position.y + f32r(f32r(self.velocity.y + result.bias_velocity.y) * f32r(dt))), - ); - let angle_delta = f32r(f32r(self.angular_velocity + result.bias_angular) * f32r(dt)); - self.apply_center_of_mass_displacement(cfg, angle_delta as f32); - self.set_transform_angle(f32r(self.rotation + angle_delta)); - collided = result.collided; - } else { - let old_position = self.position; - self.position = (F2::from(self.position) + F2::from(self.velocity) * dt as f32).into(); - let angle_delta = f32r(self.angular_velocity * f32r(dt)); - self.apply_center_of_mass_displacement(cfg, angle_delta as f32); - self.set_transform_angle(f32r(self.rotation + angle_delta)); - collided = self.resolve_walls(world, old_position); - } - if drive { - self.tick += 1; - } - if !track.shapes.is_empty() || self.scratch.has_shape() { - self.scratch.update_shape(cfg, self.position, self.body_basis); - } - let callback = NativeCallback { - old_velocity, - collided, - was_active, - eliminate_on_wall, - }; - if defer_callback { - self.pending_callback = Some(callback); - return false; - } - self.finish_callback(world, callback) - } - - fn finish_callback(&mut self, world: &World, callback: NativeCallback) -> bool { - let NativeCallback { - old_velocity, - collided, - was_active, - eliminate_on_wall, - } = callback; - let cfg = &world.vehicle; - let track = &world.track; - let report_collision = - collided && was_active && self.pending_velocity_reset.is_none() && self.pending_transform_reset.is_none(); - if report_collision { - self.collision_count += 1; - if eliminate_on_wall { - self.set_active(false); - } - } - if self.pending_velocity_store { - self.frozen_velocity = (self.velocity, self.angular_velocity); - self.pending_velocity_store = false; - } - if let Some((linear, angular)) = self.pending_velocity_reset.take() { - self.velocity = linear; - self.angular_velocity = angular; - } - if let Some((position, rotation)) = self.pending_transform_reset.take() { - self.position = position; - let (x, y) = godot_math::reset_basis(rotation as f32); - self.set_body_basis(x, y); - if !track.shapes.is_empty() || self.scratch.has_shape() { - self.scratch.update_shape(cfg, self.position, self.body_basis); - } - } - self.reset_right = false; - self.acceleration = (F2::from(self.velocity) - old_velocity).into(); - report_collision - } - - fn apply_center_of_mass_displacement(&mut self, cfg: &crate::track::world::VehicleConfig, angle_delta: f32) { - let center = self.body_basis.0 * cfg.center_of_mass.x as f32 + self.body_basis.1 * cfg.center_of_mass.y as f32; - if center.dot(center) as f64 > 1e-5f64 * 1e-5f64 { - let (s, c) = crate::math::native_math::engine_sin_cos(angle_delta); - let rotated = F2 { - x: center.x * c - center.y * s, - y: center.x * s + center.y * c, - }; - self.position = (F2::from(self.position) + (center - rotated)).into(); - } - } - - /// `Simulator._resolve_walls`: prototype rectangle versus wall segments. - fn resolve_walls(&mut self, world: &World, old_position: V2) -> bool { - let cfg = &world.vehicle; - let mut contact = false; - for wall in &world.track.walls { - let tangent = (wall.end - wall.start).normalized(); - if tangent.length() == 0.0 { - continue; - } - let normal = V2::new(-tangent.y, tangent.x); - let old_side = (old_position - wall.start).dot(normal); - let side = (self.position - wall.start).dot(normal); - let toward_car = normal * if old_side >= 0.0 { 1.0 } else { -1.0 }; - let (right, forward) = (self.right(), self.forward()); - let support = - toward_car.dot(right).abs() * cfg.half_width + toward_car.dot(forward).abs() * cfg.half_length; - let along = (self.position - wall.start).dot(tangent); - let length = (wall.end - wall.start).length(); - let extent = tangent.dot(right).abs() * cfg.half_width + tangent.dot(forward).abs() * cfg.half_length; - if -extent > along || along > length + extent { - continue; - } - let signed_distance = if old_side >= 0.0 { side } else { -side }; - if signed_distance >= support - || old_side * side < 0.0 && old_side.abs() > support + (self.position - old_position).length() - { - continue; - } - let penetration = support - signed_distance; - self.position += toward_car * py_max(0.0, penetration); - let normal_speed = self.velocity.dot(toward_car); - if normal_speed < 0.0 { - self.velocity -= toward_car * ((1.0 + cfg.bounce) * normal_speed); - } - contact = true; - } - contact - } - - /// The world-space end of the ray sensor of `degrees` and `length`, and - /// the float32 length. The ray runs from `position` to it. - #[cfg(any(test, all(target_arch = "wasm32", feature = "wasm")))] - pub(crate) fn ray_end(&self, degrees: f64, length: f64) -> (V2, f32) { - let local = Self::ray_local(degrees, length, self.math); - ( - godot_math::transform_point(self.position, self.body_basis, local.into()), - length as f32, - ) - } - fn ray_local(degrees: f64, length: f64, math: MathProfile) -> F2 { - assert!(length > 0.0, "ray length must be positive"); - let length = length as f32; - let angle = (degrees as f32 - 90.0f32) * (std::f32::consts::PI / 180.0); - F2 { - x: math.cos(angle) * length, - y: math.sin(angle) * length, - } - } - - /// The ray sensor reading for `hit`, the track's raycast from `start` to - /// `end` (`ray_end`), which an external raycaster may have computed. - pub(crate) fn ray_value(start: V2, end: V2, length: f32, hit: Option) -> f64 { - match hit { - None => 0.0, - Some(hit) if hit == end => 0.0, - Some(hit) => (1.0f32 - (F2::from(hit) - F2::from(start)).length() / length) as f64, - } - } - - /// Offset along the path of the tile-connection sensor point, cached per pose. - fn sensor_path_offset(&self, track: &Track, scratch: &mut SensorScratch) -> f64 { - let point = track.path_sensor_point(self.actual_position()); - if let Some((cached_point, offset)) = scratch.path_cache { - if cached_point.x.to_bits() == point.x.to_bits() && cached_point.y.to_bits() == point.y.to_bits() { - return offset; - } - } - let offset = track.curve.as_ref().map_or_else( - || track.closest_path(point, None).offset, - |c| c.closest_offset(point.into()) as f64, - ); - scratch.path_cache = Some((point, offset)); - offset - } - - /// `Simulator.sensor(name, **kwargs)`. - pub fn sensor(&self, world: &World, sensor: Sensor, scratch: &mut SensorScratch) -> f64 { - let cfg = &world.vehicle; - let track = &world.track; - match sensor { - Sensor::Raycast { degrees, length } => { - let local = scratch.ray_local(degrees, length, world.math); - let end = godot_math::transform_point(self.position, self.body_basis, local.into()); - let length = length as f32; - Self::ray_value( - self.position, - end, - length, - track.raycast(self.position, end, &mut scratch.stamps), - ) - } - Sensor::DistanceFromWall { max_distance } => { - let position = self.actual_position(); - match track.closest_wall(position) { - None => 1.0, - Some(wall) => { - let distance = (F2::from(position) - F2::from(wall)).length(); - let maximum = max_distance as f32; - // x86 division produces a negative indefinite for 0/0 - // and infinity/infinity. Keep NaN operand payloads. - let fraction = if (distance == 0.0 && maximum == 0.0) - || (distance.is_infinite() && maximum.is_infinite()) - { - f32::from_bits(0xffc0_0000) - } else { - distance / maximum - }; - fraction.clamp(0.0, 1.0) as f64 - } - } - } - Sensor::Speed => (F2::from(self.velocity).length() / cfg.max_velocity as f32).clamp(0.0, 1.0) as f64, - Sensor::VelocityFront => godot_math::signed_sensor( - F2::from(self.actual_velocity()).dot(self.cached_front()), - cfg.max_velocity as f32, - ), - Sensor::VelocitySide => { - let right = self.cached_right(); - godot_math::signed_sensor( - F2::from(self.actual_velocity()).dot(right), - cfg.max_velocity as f32 / 2.0, - ) - } - Sensor::AccelerationFront { max_acceleration } => godot_math::signed_sensor( - F2::from(self.actual_acceleration()).dot(self.cached_front()), - max_acceleration as f32, - ), - Sensor::AccelerationSide { max_acceleration } => godot_math::signed_sensor( - F2::from(self.actual_acceleration()).dot(self.cached_right()), - max_acceleration as f32, - ), - Sensor::AngularVelocity => godot_math::signed_sensor(self.actual_angular_velocity() as f32, 2.0), - Sensor::WheelAngle => { - let angle = cfg - .wheels - .iter() - .zip(&self.wheels) - .find(|(spec, _)| spec.steering) - .expect("wheel angle sensor requires a steering wheel") - .1 - .angle_deg; - godot_math::signed_sensor(angle as f32, 45.0) - } - Sensor::BoostCapacity => clamp(self.boost_energy, 0.0, 1.0), - Sensor::Grip { offset } => { - track - .vehicle_surface( - (F2::from(self.position) + F2::from(offset).rotated(self.rotation as f32)).into(), - cfg, - ) - .grip as f32 as f64 - } - Sensor::CorrectDirection | Sensor::TrackCurvature { .. } => { - if track.path.len() < 2 { - return 0.0; - } - let here = self.sensor_path_offset(track, scratch); - if let Some(curve) = &track.curve { - let tangent = match scratch.tangent_cache { - Some((bits, tangent)) if bits == here.to_bits() => tangent, - _ => { - let tangent = curve.direction(here as f32).normalized(); - scratch.tangent_cache = Some((here.to_bits(), tangent)); - tangent - } - }; - let Sensor::TrackCurvature { - min_lookahead, - max_lookahead, - } = sensor - else { - let forward = self.cached_front().normalized(); - return forward.dot(tangent).clamp(-1.0, 1.0) as f64; - }; - if curve.length() <= 0.0 { - return 0.0; - } - let speed = (F2::from(self.actual_velocity()).length() / cfg.max_velocity as f32).clamp(0.0, 1.0); - let min = (min_lookahead as f32).max(0.0); - let max = (max_lookahead as f32).max(min); - let lookahead = min + (max - min) * speed; - let offset = (here as f32 + lookahead) % curve.length(); - let ahead = curve.direction(offset).normalized(); - return (world.math.atan2(tangent.cross(ahead), tangent.dot(ahead)).abs() / std::f32::consts::PI) - .clamp(0.0, 1.0) as f64; - } - let tangent = track.path_direction(here); - let Sensor::TrackCurvature { - min_lookahead, - max_lookahead, - } = sensor - else { - return clamp(self.forward().dot(tangent), -1.0, 1.0); - }; - let path_length = track.path_length(); - if path_length <= 0.0 { - return 0.0; - } - let lookahead = - min_lookahead + (max_lookahead - min_lookahead) * self.sensor(world, Sensor::Speed, scratch); - let target_offset = py_mod(here + lookahead, path_length); - let ahead = track.path_direction(target_offset); - tangent.cross(ahead).atan2(tangent.dot(ahead)).abs() / PI - } - } - } -} - -impl Car { - /// The GPU car state (`gpu::simulation::GpuCar`) with its collision scratch; - /// errors on states the GPU port does not model. - pub fn gpu_export( - &self, - vehicle: &crate::track::world::VehicleConfig, - surfaces: &crate::gpu::simulation::SurfaceTable, - ) -> Result { - use crate::gpu::simulation::*; - let init = &self.wheel_initialization; - if init.grip != vehicle.grip - || init.steering_speed != vehicle.steering_speed - || init.center_steering != vehicle.center_steering - || !init.positions.iter().eq(vehicle.wheels.iter().map(|w| &w.position)) - { - return Err("wheel initialization differs from the vehicle config".into()); - } - if self.frozen || self.pending_velocity_store { - return Err("frozen cars are not supported".into()); - } - if self.pending_transform_reset.is_some() || self.pending_callback.is_some() { - return Err("pending transform reset or native callback".into()); - } - if self.wheels.len() > MAX_WHEELS { - return Err(format!("{} wheels exceed MAX_WHEELS", self.wheels.len())); - } - let mut c = GpuCar::zeroed(); - c.position = exact2(self.position, "position")?; - c.velocity = exact2(self.velocity, "velocity")?; - c.acceleration = exact2(self.acceleration, "acceleration")?; - c.reset_acceleration = exact2(self.reset_acceleration, "reset_acceleration")?; - c.basis_x = f2(self.body_basis.0); - c.basis_y = f2(self.body_basis.1); - c.rotation = exact(self.rotation, "rotation")?; - c.transform_angle = exact(self.transform_angle, "transform_angle")?; - c.angular_velocity = exact(self.angular_velocity, "angular_velocity")?; - c.boost_energy = exact(self.boost_energy, "boost_energy")?; - c.flags = if self.active { CAR_ACTIVE } else { 0 } | if self.reset_right { CAR_RESET_RIGHT } else { 0 }; - match self.pending_velocity_reset { - None => {} - Some((linear, angular)) if linear == V2::ZERO && angular == 0.0 => c.flags |= CAR_PENDING_VELOCITY, - Some(_) => return Err("pending non-zero velocity reset".into()), - } - c.collision_count = u32::try_from(self.collision_count).map_err(|_| "collision count overflows u32")?; - c.tick = self.tick; - for (i, wheel) in self.wheels.iter().enumerate() { - c.wheel_angle[i] = exact(wheel.angle_deg, "wheel angle")?; - if let Some(p) = wheel.previous_position { - // Only ever read through F2::from. - c.wheel_previous[i] = f2(F2::from(p)); - c.wheel_has_previous |= 1 << i; - } - c.wheel_surface[i] = surfaces.by_value(wheel.previous_surface)?; - } - self.scratch.gpu_export(&mut c, &self.previous_contacts)?; - Ok(c) - } - - /// Sets the state exported by `gpu_export` from a GPU car. `wall_contacts` - /// (a per-step report) is cleared. - pub fn gpu_import( - &mut self, - c: &crate::gpu::simulation::GpuCar, - surfaces: &crate::gpu::simulation::SurfaceTable, - ) -> Result<(), String> { - use crate::gpu::simulation::*; - if c.error != 0 { - return Err(format!("GPU error bits {:#x}", c.error)); - } - self.position = v2(c.position); - self.velocity = v2(c.velocity); - self.acceleration = v2(c.acceleration); - self.reset_acceleration = v2(c.reset_acceleration); - self.body_basis = ( - F2 { - x: c.basis_x[0], - y: c.basis_x[1], - }, - F2 { - x: c.basis_y[0], - y: c.basis_y[1], - }, - ); - self.rotation = c.rotation as f64; - self.transform_angle = c.transform_angle as f64; - self.angular_velocity = c.angular_velocity as f64; - self.boost_energy = c.boost_energy as f64; - self.active = c.flags & CAR_ACTIVE != 0; - self.reset_right = c.flags & CAR_RESET_RIGHT != 0; - self.pending_velocity_reset = (c.flags & CAR_PENDING_VELOCITY != 0).then_some((V2::ZERO, 0.0)); - self.collision_count = c.collision_count as u64; - self.tick = c.tick; - for (i, wheel) in self.wheels.iter_mut().enumerate() { - wheel.angle_deg = c.wheel_angle[i] as f64; - wheel.previous_position = (c.wheel_has_previous >> i & 1 != 0).then(|| v2(c.wheel_previous[i])); - wheel.previous_surface = *surfaces - .values - .get(c.wheel_surface[i] as usize) - .ok_or("wheel surface index out of range")?; - } - self.wall_contacts.clear(); - self.scratch.gpu_import(c, &mut self.previous_contacts); - Ok(()) - } -} diff --git a/github-simulator/source/src/physics/collision.rs b/github-simulator/source/src/physics/collision.rs deleted file mode 100644 index 1f080f2ca482b68918bb8ac8019b6d0906370026..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/physics/collision.rs +++ /dev/null @@ -1,882 +0,0 @@ -//! Car rectangle against static convex tile shapes using Godot 4.3 float32 -//! SAT and the sequential impulse solver. - -use crate::math::godot_math::F2; -use crate::math::pymath::{f32r, py_max, py_min}; -use crate::math::vec2::V2; -use crate::physics::broadphase::{Aabb, BroadPhase}; -use crate::track::world::{PhysicsShape, Track, VehicleConfig}; - -const SUPPORT_EDGE_DOT: f64 = 0.99998; -const CMP_EPSILON: f64 = 1e-5; -// This captured float literal is part of the contact solver contract. -#[allow(clippy::approx_constant)] -const MAX_BIAS_ROTATION: f64 = 0.39269908169872414; - -#[inline(always)] -fn dot_f32(a: V2, b: V2) -> f64 { - f32r(f32r(a.x * b.x) + f32r(a.y * b.y)) -} - -#[inline(always)] -fn normalize_f32(v: V2) -> V2 { - let length = f32r(dot_f32(v, v).sqrt()); - if length != 0.0 { - V2::new(f32r(v.x / length), f32r(v.y / length)) - } else { - V2::ZERO - } -} - -#[derive(Clone, Copy, Debug, Default)] -pub struct Contact { - /// Points from the static shape toward the car. - pub normal: V2, - /// Point on the car. - pub point: V2, - pub depth: f64, - pub shape_index: usize, - pub wall_first: bool, - pub wall_point: V2, - pub local_point: V2, - pub wall_local_point: V2, - pub used: bool, - pub normal_mass: f32, - pub tangent_mass: f32, - pub bias: f32, - pub bounce: f32, - pub friction: f32, - pub acc_normal: f32, - pub acc_tangent: f32, - pub acc_bias: f32, - pub acc_bias_center: f32, -} - -/// Up to two support points in their contact order. -#[derive(Clone, Copy)] -struct Support { - points: [V2; 2], - len: usize, -} - -impl Support { - fn one(a: V2) -> Support { - Support { - points: [a, V2::ZERO], - len: 1, - } - } - fn two(a: V2, b: V2) -> Support { - Support { points: [a, b], len: 2 } - } - fn slice(&self) -> &[V2] { - &self.points[..self.len] - } -} - -fn project_on_line(point: V2, edge: &[V2]) -> V2 { - let direction = V2::new(f32r(edge[1].x - edge[0].x), f32r(edge[1].y - edge[0].y)); - let offset = V2::new(f32r(point.x - edge[0].x), f32r(point.y - edge[0].y)); - let length_squared = dot_f32(direction, direction); - if length_squared < 1e-20f32 as f64 { - return edge[0]; - } - let fraction = f32r(dot_f32(direction, offset) / length_squared); - V2::new( - f32r(edge[0].x + f32r(direction.x * fraction)), - f32r(edge[0].y + f32r(direction.y * fraction)), - ) -} - -fn rectangle_supports(car: &[V2; 4], direction: V2, basis_x: V2, basis_y: V2) -> Support { - let local = normalize_f32(V2::new(dot_f32(basis_x, direction), dot_f32(basis_y, direction))); - if local.x.abs() > SUPPORT_EDGE_DOT { - return if local.x > 0.0 { - Support::two(car[2], car[1]) - } else { - Support::two(car[3], car[0]) - }; - } - if local.y.abs() > SUPPORT_EDGE_DOT { - return if local.y > 0.0 { - Support::two(car[2], car[3]) - } else { - Support::two(car[1], car[0]) - }; - } - if local.x < 0.0 { - return Support::one(if local.y < 0.0 { car[0] } else { car[3] }); - } - Support::one(if local.y < 0.0 { car[1] } else { car[2] }) -} - -fn polygon_supports(wall: &[V2], shape: &PhysicsShape, direction: V2) -> Support { - let direction = normalize_f32(direction); - let mut maximum = f64::NEG_INFINITY; - let mut best = 0; - let n = shape.local.len(); - for i in 0..n { - let projection = dot_f32(shape.local[i], direction); - if projection > maximum { - maximum = projection; - best = i; - } - if dot_f32(shape.local_normals[i], direction) > SUPPORT_EDGE_DOT { - return Support::two(wall[i], wall[(i + 1) % wall.len()]); - } - } - Support::one(wall[best]) -} - -fn contact_pairs(a: &[V2], b: &[V2], push: V2, out: &mut [(V2, V2); 2]) -> usize { - if a.len() == 1 && b.len() == 1 { - out[0] = (a[0], b[0]); - return 1; - } - if a.len() == 1 { - out[0] = (a[0], project_on_line(a[0], b)); - return 1; - } - if b.len() == 1 { - out[0] = (project_on_line(b[0], a), b[0]); - return 1; - } - // Godot Vector2::orthogonal is (y, -x). This also fixes the order of - // the two edge contacts, which is observable in solver roundoff. - let tangent = V2::new(push.y, -push.x); - let distance_a = dot_f32(push, a[0]); - let distance_b = dot_f32(push, b[0]); - // Stable sort of 4 entries by projection, car points first. - let mut ordered = [ - (dot_f32(a[0], tangent), true, a[0]), - (dot_f32(a[1], tangent), true, a[1]), - (dot_f32(b[0], tangent), false, b[0]), - (dot_f32(b[1], tangent), false, b[1]), - ]; - for i in 1..4 { - let mut j = i; - while j > 0 && ordered[j].0 < ordered[j - 1].0 { - ordered.swap(j, j - 1); - j -= 1; - } - } - let mut count = 0; - for &(_, from_car, point) in &ordered[1..3] { - let pair = if from_car { - let distance = f32r(dot_f32(push, point) - distance_b); - let other = V2::new( - f32r(point.x - f32r(push.x * distance)), - f32r(point.y - f32r(push.y * distance)), - ); - (point, other) - } else { - let distance = f32r(dot_f32(push, point) - distance_a); - let other = V2::new( - f32r(point.x - f32r(push.x * distance)), - f32r(point.y - f32r(push.y * distance)), - ); - (other, point) - }; - if dot_f32(push, pair.0) <= dot_f32(push, pair.1) - CMP_EPSILON { - out[count] = pair; - count += 1; - } - } - count -} - -/// Scratch buffers reused across ticks by one car. -#[derive(Default, Clone)] -pub(crate) struct CollisionScratch { - candidates: Vec, - wall: Vec, - axes: Vec, - active_indices: Vec, - collided_shapes: Vec, - broad_pairs: Vec, - shape_aabb: Option, - leaf_aabb: Option, - broadphase: Option, - pair_check_pending: bool, - external_pairs: bool, - pair_orientation: Vec, - pair_epochs: Vec, - separating_axes: std::collections::HashMap, -} - -#[derive(Clone, Copy)] -struct FloatRect { - position: F2, - size: F2, -} -impl FloatRect { - fn end(self) -> F2 { - self.position + self.size - } - fn grow(mut self, margin: f32) -> Self { - self.position = self.position - F2 { x: margin, y: margin }; - self.size = self.size - + F2 { - x: margin * 2.0, - y: margin * 2.0, - }; - self - } - fn expand(&mut self, p: F2) { - let end = self.end(); - let begin = F2 { - x: self.position.x.min(p.x), - y: self.position.y.min(p.y), - }; - let end = F2 { - x: end.x.max(p.x), - y: end.y.max(p.y), - }; - self.position = begin; - self.size = end - begin; - } -} -impl CollisionScratch { - pub(crate) fn has_shape(&self) -> bool { - self.shape_aabb.is_some() - } - pub(crate) fn shape_bounds(&self) -> Aabb { - let r = self.shape_aabb.expect("native shape initialized"); - Aabb::from_rect(r.position, r.size) - } - pub(crate) fn set_external_pairs( - &mut self, - pairs: &[usize], - wall_first: &[bool], - epochs: &[u64], - previous: &mut Vec, - ) { - let retained = |wall: usize| { - pairs.iter().position(|&p| p == wall).is_some_and(|i| { - self.broad_pairs - .iter() - .position(|&p| p == wall) - .is_some_and(|j| epochs[i] == self.pair_epochs[j]) - }) - }; - previous.retain(|c| retained(c.shape_index)); - self.separating_axes.retain(|&wall, _| retained(wall)); - self.external_pairs = true; - self.broad_pairs.clear(); - self.broad_pairs.extend_from_slice(pairs); - self.pair_orientation.clear(); - self.pair_orientation.extend_from_slice(wall_first); - self.pair_epochs.clear(); - self.pair_epochs.extend_from_slice(epochs); - } - /// Native _update_shapes runs when a transform changes, including resets; - /// pair processing happens later in the broad-phase update. - pub(crate) fn update_shape(&mut self, cfg: &VehicleConfig, position: V2, body: (F2, F2)) { - let (sx, sy) = cfg.shape_basis; - let x = body.0 * sx.x + body.1 * sx.y; - let y = body.0 * sy.x + body.1 * sy.y; - let size = F2::from(cfg.shape_size); - let shape_origin = - body.0 * cfg.shape_position.x as f32 + body.1 * cfg.shape_position.y as f32 + F2::from(position); - let pos = x * (-size.x * 0.5) + y * (-size.y * 0.5) + shape_origin; - let dx = x * size.x; - let dy = y * size.y; - let mut rect = FloatRect { - position: pos, - size: F2::default(), - }; - rect.expand(pos + dx); - rect.expand(pos + dy); - rect.expand((pos + dx) + dy); - let margin = self - .shape_aabb - .map_or(0.0, |old| ((old.size.x + old.size.y) as f64 * 0.5 * 0.05) as f32); - rect = rect.grow(margin); - self.shape_aabb = Some(rect); - let Some(old) = self.leaf_aabb else { - // item_add inserts the unexpanded box; the following move is a no-op. - self.leaf_aabb = Some(Aabb::from_rect(rect.position, rect.size)); - self.pair_check_pending = true; - return; - }; - let threshold = (0.1f32 as f64 * 2.0 * 2.0 * 1.1f32 as f64) as f32; - let density = (self.broad_pairs.len() as f64 * (1.0 / 9.0)) as f32; - let raw = Aabb::from_rect(rect.position, rect.size); - let expanded = raw.grow(0.1f32 * (1.0 - density.min(1.0))); - let within_node = self.broadphase.as_ref().is_none_or(|b| b.body_node.encloses(expanded)); - let old_size = old.size(); - if within_node - && old.roundtrip_rect().encloses(raw) - && (old_size.x + old_size.y) - (rect.size.x + rect.size.y) < threshold - { - return; - } - if !within_node { - self.broadphase.as_mut().unwrap().body_node = expanded.grow(0.5); - } - self.leaf_aabb = Some(expanded); - self.pair_check_pending = true; - } -} - -fn find_contacts_with_basis( - cfg: &VehicleConfig, - track: &Track, - position: V2, - _rotation: f64, - body_basis: (F2, F2), - scratch: &mut CollisionScratch, - contacts: &mut Vec, -) { - contacts.clear(); - let (sx, sy) = cfg.shape_basis; - let basis_x = V2::from(body_basis.0 * sx.x + body_basis.1 * sx.y); - let basis_y = V2::from(body_basis.0 * sy.x + body_basis.1 * sy.y); - let half_x = f32r(cfg.shape_size.x / 2.0); - let half_y = f32r(cfg.shape_size.y / 2.0); - - if scratch.shape_aabb.is_none() { - scratch.update_shape(cfg, position, body_basis); - } - let leaf = scratch.leaf_aabb.unwrap(); - if !scratch.external_pairs && track.native_broadphase && scratch.broadphase.is_none() { - let walls: Vec = track - .shapes - .iter() - .map(|s| Aabb { - min: F2::from(s.min), - max: F2::from(s.max), - }) - .collect(); - let broadphase = BroadPhase::with_layout(&walls, leaf, track.native_tilemap); - scratch.broad_pairs = broadphase.initial_pairs.iter().map(|&id| id as usize).collect(); - scratch.broadphase = Some(broadphase); - } - if let Some(b) = scratch.broadphase.as_mut() { - b.update(leaf); - } - let query = leaf.roundtrip_rect(); - let (left, right, top, bottom) = ( - query.min.x as f64, - query.max.x as f64, - query.min.y as f64, - query.max.y as f64, - ); - let car_origin = V2::new(f32r(position.x), f32r(position.y)); - - let mut car_local = [V2::ZERO; 4]; - for (i, (sx, sy)) in [(-1.0, -1.0), (1.0, -1.0), (1.0, 1.0), (-1.0, 1.0)] - .into_iter() - .enumerate() - { - let (x, y) = (f32r(sx * half_x), f32r(sy * half_y)); - car_local[i] = V2::new( - f32r(f32r(basis_x.x * x) + f32r(basis_y.x * y)), - f32r(f32r(basis_x.y * x) + f32r(basis_y.y * y)), - ); - } - let car_axes = [normalize_f32(basis_x), normalize_f32(basis_y)]; - - if scratch.pair_check_pending && !scratch.external_pairs { - if let Some(b) = scratch.broadphase.as_ref() { - b.cull(query, &mut scratch.candidates); - } else { - track.shape_candidates(V2::new(left, top), V2::new(right, bottom), &mut scratch.candidates); - } - // Godot's body constraint list preserves broadphase pair creation order. - // A pair can exist for many frames before its convex shapes actually touch. - let overlaps = |index: usize| { - if let Some(b) = scratch.broadphase.as_ref() { - return b.overlaps(index, query); - } - let shape = &track.shapes[index]; - !(shape.max.x < left || shape.min.x > right || shape.max.y < top || shape.min.y > bottom) - }; - scratch.broad_pairs.retain(|&index| overlaps(index)); - scratch - .separating_axes - .retain(|index, _| scratch.broad_pairs.contains(index)); - for &index in &scratch.candidates { - let index = index as usize; - if overlaps(index) && !scratch.broad_pairs.contains(&index) { - scratch.broad_pairs.push(index); - } - } - scratch.pair_check_pending = false; - } - for ci in 0..scratch.broad_pairs.len() { - let shape_index = scratch.broad_pairs[ci]; - let shape = &track.shapes[shape_index]; - let wall_first = if scratch.external_pairs { - scratch.pair_orientation[ci] - } else { - shape.wall_first - }; - let wall_origin = shape.origin_f32; - // BodyPair uses the origin of the body with the lower BVH handle. - let pair_origin = if wall_first { wall_origin } else { car_origin }; - let car_offset = F2::from(car_origin) - F2::from(pair_origin); - let wall_offset = F2::from(wall_origin) - F2::from(pair_origin); - let shape_origin = - body_basis.0 * cfg.shape_position.x as f32 + body_basis.1 * cfg.shape_position.y as f32 + car_offset; - let car = car_local.map(|p| V2::from(F2::from(p) + shape_origin)); - let wall = &mut scratch.wall; - wall.clear(); - wall.extend(shape.local.iter().map(|p| V2::from(F2::from(*p) + wall_offset))); - let axes = &mut scratch.axes; - axes.clear(); - let separating_axis = scratch.separating_axes.entry(shape_index).or_insert(V2::ZERO); - if *separating_axis != V2::ZERO { - axes.push(*separating_axis); - } - axes.extend_from_slice(&car_axes); - let n = wall.len(); - for i in 0..n { - let edge = V2::new( - f32r(wall[(i + 1) % n].x - wall[i].x), - f32r(wall[(i + 1) % n].y - wall[i].y), - ); - let edge_normalized = normalize_f32(edge); - let axis = V2::new(edge_normalized.y, -edge_normalized.x); - axes.push(axis); - } - let mut best_depth = 1e15f32 as f64; - let mut push = V2::ZERO; - let mut separated = false; - for &axis in axes.iter() { - let axis = if axis.x.abs() < CMP_EPSILON && axis.y.abs() < CMP_EPSILON { - V2::new(0.0, 1.0) - } else { - axis - }; - let (mut min_a, mut max_a) = (f64::INFINITY, f64::NEG_INFINITY); - for &p in &car { - let d = dot_f32(p, axis); - min_a = py_min(min_a, d); - max_a = py_max(max_a, d); - } - let (mut min_b, mut max_b) = (f64::INFINITY, f64::NEG_INFINITY); - for &p in wall.iter() { - let d = dot_f32(p, axis); - min_b = py_min(min_b, d); - max_b = py_max(max_b, d); - } - let width_a = f32r(f32r(max_a - min_a) * 0.5); - let center_a = f32r(f32r(min_a + max_a) * 0.5); - let dmin = f32r(f32r(min_b - width_a) - center_a); - let dmax = f32r(f32r(max_b + width_a) - center_a); - if dmin > 0.0 || dmax < 0.0 { - *separating_axis = axis; - separated = true; - break; - } - let (depth, candidate) = if dmax < dmin.abs() { - (dmax, axis) - } else { - (dmin.abs(), -axis) - }; - if depth < best_depth { - best_depth = depth; - push = candidate; - } - } - if separated || push == V2::ZERO { - continue; - } - let support_car = rectangle_supports(&car, -push, basis_x, basis_y); - let support_wall = polygon_supports(wall, shape, push); - let mut pairs = [(V2::ZERO, V2::ZERO); 2]; - let count = contact_pairs(support_car.slice(), support_wall.slice(), push, &mut pairs); - *separating_axis = V2::ZERO; - for &(car_point, wall_point) in &pairs[..count] { - // The pair stores normalize(A - B), then reports its negation to A. - // Reversing subtraction algebraically loses native negative zeros. - let (a, b) = if wall_first { - (wall_point, car_point) - } else { - (car_point, wall_point) - }; - let delta = V2::new(f32r(a.x - b.x), f32r(a.y - b.y)); - let depth = f32r(dot_f32(delta, delta).sqrt()); - if depth > 0.0 { - let mut normal = V2::new(f32r(delta.x / depth), f32r(delta.y / depth)); - if !wall_first { - normal = -normal; - } - // BodyPair stores local points, then transforms them again in - // pre_solve. The two float32 round trips affect the lever arm. - let (x, y) = body_basis; - let relative = F2::from(car_point) - car_offset; - let local = F2 { - x: x.dot(relative), - y: y.dot(relative), - }; - let global_a = (x * local.x + y * local.y) + car_offset; - let global_b = (F2::from(wall_point) - wall_offset) + wall_offset; - let depth = (global_b - global_a).dot(normal.into()) as f64; - let point = V2::from(global_a + F2::from(pair_origin)); - let other = V2::from(global_b + F2::from(pair_origin)); - contacts.push(Contact { - normal, - point, - depth, - shape_index, - wall_first, - wall_point: other, - local_point: local.into(), - wall_local_point: (F2::from(wall_point) - wall_offset).into(), - used: true, - ..Contact::default() - }); - } - } - } -} - -fn refresh_contact(c: &mut Contact, track: &Track, position: V2, (x, y): (F2, F2)) { - let shape = &track.shapes[c.shape_index]; - let origin = if c.wall_first { shape.origin_f32 } else { position }; - let car_offset = F2::from(position) - F2::from(origin); - let wall_offset = F2::from(shape.origin_f32) - F2::from(origin); - let car = (x * c.local_point.x as f32 + y * c.local_point.y as f32) + car_offset; - let wall = F2::from(c.wall_local_point) + wall_offset; - c.depth = (wall - car).dot(c.normal.into()) as f64; - c.point = V2::from(car + F2::from(origin)); - c.wall_point = V2::from(wall + F2::from(origin)); -} - -pub(crate) struct SolveResult { - pub(crate) velocity: V2, - pub(crate) angular_velocity: f64, - pub(crate) bias_velocity: V2, - pub(crate) bias_angular: f64, - pub(crate) collided: bool, -} - -/// Resolve every persistent pair contact, then return the native capped reports. -#[allow(clippy::too_many_arguments)] -pub(crate) fn solve_wall_contacts( - cfg: &VehicleConfig, - track: &Track, - position: V2, - rotation: f64, - body_basis: (F2, F2), - velocity: V2, - angular_velocity: f64, - initial_velocity: F2, - initial_angular: f32, - dt: f64, - scratch: &mut CollisionScratch, - contacts: &mut Vec, - previous: &mut Vec, -) -> SolveResult { - // Godot retains two local contacts per pair, including points that are - // temporarily inactive. Validate before adding this tick's SAT contacts. - let mut i = 0; - while i < previous.len() { - refresh_contact(&mut previous[i], track, position, body_basis); - let c = &previous[i]; - let pair_origin = if c.wall_first { - track.shapes[c.shape_index].origin_f32 - } else { - position - }; - let car_offset = F2::from(position) - F2::from(pair_origin); - let car_point = body_basis.0 * c.local_point.x as f32 + body_basis.1 * c.local_point.y as f32 + car_offset; - let wall_offset = F2::from(track.shapes[c.shape_index].origin_f32) - F2::from(pair_origin); - let wall_point = F2::from(c.wall_local_point) + wall_offset; - let separation = wall_point - F2::from(c.normal) * c.depth as f32 - car_point; - if !c.used - || c.depth < -cfg.contact_max_separation - || separation.dot(separation) > (cfg.contact_max_separation as f32).powi(2) - { - let shape = c.shape_index; - let last = previous.iter().rposition(|p| p.shape_index == shape).unwrap(); - previous[i] = previous[last]; - previous.remove(last); - } else { - previous[i].used = false; - i += 1; - } - } - find_contacts_with_basis(cfg, track, position, rotation, body_basis, scratch, contacts); - previous.retain(|c| scratch.broad_pairs.contains(&c.shape_index)); - scratch.collided_shapes.clear(); - for new in contacts.iter() { - if !scratch.collided_shapes.contains(&new.shape_index) { - scratch.collided_shapes.push(new.shape_index); - } - let radius2 = (cfg.contact_recycle_radius as f32).powi(2); - if let Some(old) = previous.iter_mut().find(|old| { - let a = F2::from(old.local_point) - F2::from(new.local_point); - let b = F2::from(old.wall_local_point) - F2::from(new.wall_local_point); - old.shape_index == new.shape_index && a.dot(a) < radius2 && b.dot(b) < radius2 - }) { - let mut replacement = *new; - replacement.acc_normal = old.acc_normal; - replacement.acc_tangent = old.acc_tangent; - replacement.acc_bias = old.acc_bias; - replacement.acc_bias_center = old.acc_bias_center; - *old = replacement; - } else if previous.iter().filter(|c| c.shape_index == new.shape_index).count() < 2 { - previous.push(*new); - } else { - let mut least = None; - let mut depth = new.depth; - for (i, c) in previous - .iter() - .enumerate() - .filter(|(_, c)| c.shape_index == new.shape_index) - { - if c.depth < depth { - depth = c.depth; - least = Some(i); - } - } - if let Some(i) = least { - previous[i] = *new; - } - } - } - contacts.clear(); - scratch.active_indices.clear(); - for &shape in &scratch.broad_pairs { - for (i, c) in previous.iter().enumerate().filter(|(_, c)| c.shape_index == shape) { - if c.depth > 0.0 && scratch.collided_shapes.contains(&c.shape_index) { - contacts.push(*c); - scratch.active_indices.push(i); - } - } - } - if contacts.is_empty() { - return SolveResult { - velocity, - angular_velocity, - bias_velocity: V2::ZERO, - bias_angular: 0.0, - collided: false, - }; - } - let inv_mass = 1.0f32 / cfg.mass as f32; - let inv_inertia = 1.0f32 / cfg.inertia as f32; - let inv_dt = 1.0f32 / dt as f32; - let mut v = F2::from(velocity); - let mut w = angular_velocity as f32; - let center = body_basis.0 * cfg.center_of_mass.x as f32 + body_basis.1 * cfg.center_of_mass.y as f32; - let contact_arm = |contact: &Contact| { - let local = body_basis.0 * contact.local_point.x as f32 + body_basis.1 * contact.local_point.y as f32; - let offset = if contact.wall_first { - F2::from(position) - F2::from(track.shapes[contact.shape_index].origin_f32) - } else { - F2::default() - }; - ((local + offset) - center) - offset - }; - for contact in contacts.iter_mut() { - let arm = contact_arm(contact); - let n = F2::from(contact.normal); - let t = F2 { x: -n.y, y: n.x }; - // GodotBodyPair2D::pre_solve uses squared length minus squared dot, - // not the algebraically equivalent squared cross product. - let rn = arm.dot(n); - let rt = arm.dot(t); - contact.normal_mass = 1.0 / (inv_mass + inv_inertia * (arm.dot(arm) - rn * rn)); - contact.tangent_mass = 1.0 / (inv_mass + inv_inertia * (arm.dot(arm) - rt * rt)); - contact.bias = - -(cfg.contact_bias as f32) * inv_dt * (-(contact.depth as f32) + cfg.allowed_penetration as f32).min(0.0); - let rotational = F2 { - x: -arm.y * initial_angular, - y: arm.x * initial_angular, - }; - contact.bounce = (cfg.bounce as f32 + track.shapes[contact.shape_index].bounce as f32).clamp(0.0, 1.0); - if contact.bounce != 0.0 { - contact.bounce *= (initial_velocity + rotational).dot(n); - } - contact.friction = (cfg.friction as f32) - .min(track.shapes[contact.shape_index].friction as f32) - .abs(); - let impulse = n * contact.acc_normal + t * contact.acc_tangent; - v = v + impulse * inv_mass; - w += ((arm + center) - center).cross(impulse) * inv_inertia; - } - let mut bv = F2::default(); - let mut bw = 0.0f32; - let bias_limit = (MAX_BIAS_ROTATION / dt as f32 as f64) as f32; - for _ in 0..cfg.solver_iterations { - for contact in contacts.iter_mut() { - let arm = contact_arm(contact); - let impulse_arm = (arm + center) - center; - let n = F2::from(contact.normal); - let t = F2 { x: -n.y, y: n.x }; - let rotational = F2 { x: -arm.y, y: arm.x }; - let velocity_at_contact = v + rotational * w; - let normal_speed = velocity_at_contact.dot(n); - let tangent_speed = velocity_at_contact.dot(t); - let biased_normal_speed = (bv + rotational * bw).dot(n); - let bias_impulse = (contact.bias - biased_normal_speed) * contact.normal_mass; - let previous_bias = contact.acc_bias; - contact.acc_bias = (previous_bias + bias_impulse).max(0.0); - let impulse = n * (contact.acc_bias - previous_bias); - bv = bv + impulse * inv_mass; - bw = (bw + impulse_arm.cross(impulse) * inv_inertia).clamp(-bias_limit, bias_limit); - let biased_normal_speed = (bv + rotational * bw).dot(n); - if (contact.bias - biased_normal_speed).abs() > 0.001 { - let center_impulse = (contact.bias - biased_normal_speed) / inv_mass; - let previous_center = contact.acc_bias_center; - contact.acc_bias_center = (previous_center + center_impulse).max(0.0); - bv = bv + (n * (contact.acc_bias_center - previous_center)) * inv_mass; - } - let normal_impulse = -(normal_speed + contact.bounce) * contact.normal_mass; - let previous_normal = contact.acc_normal; - contact.acc_normal = (previous_normal + normal_impulse).max(0.0); - let friction_limit = contact.friction * contact.acc_normal; - let tangent_impulse = -tangent_speed * contact.tangent_mass; - let previous_tangent = contact.acc_tangent; - contact.acc_tangent = (previous_tangent + tangent_impulse).clamp(-friction_limit, friction_limit); - let impulse = n * (contact.acc_normal - previous_normal) + t * (contact.acc_tangent - previous_tangent); - v = v + impulse * inv_mass; - w += impulse_arm.cross(impulse) * inv_inertia; - } - } - for (c, &i) in contacts.iter().zip(&scratch.active_indices) { - previous[i] = *c; - } - // Reporting is independent of solving and of the two-contact pair cache. - // Godot replaces the first shallowest slot only for a strictly deeper hit. - let limit = cfg.max_contacts_reported; - if limit == 0 { - contacts.clear(); - } else if contacts.len() > limit { - for incoming in limit..contacts.len() { - let mut shallowest = 0; - for slot in 1..limit { - if contacts[slot].depth < contacts[shallowest].depth { - shallowest = slot; - } - } - if contacts[shallowest].depth < contacts[incoming].depth { - contacts[shallowest] = contacts[incoming]; - } - } - contacts.truncate(limit); - } - SolveResult { - velocity: v.into(), - angular_velocity: w as f64, - bias_velocity: bv.into(), - bias_angular: bw as f64, - collided: !contacts.is_empty(), - } -} - -impl CollisionScratch { - /// Fills the scratch fields of a GPU car (gpu/sim/state.h) and its - /// retained contacts; errors on states the GPU port does not model. - pub(crate) fn gpu_export( - &self, - c: &mut crate::gpu::simulation::GpuCar, - previous: &[Contact], - ) -> Result<(), String> { - use crate::gpu::simulation::*; - if self.broadphase.is_some() || self.external_pairs { - return Err("native broadphase pairs are not supported".into()); - } - if self.broad_pairs.len() > MAX_PAIRS || previous.len() > MAX_CONTACTS { - return Err(format!( - "{} pairs / {} contacts exceed the GPU limits", - self.broad_pairs.len(), - previous.len() - )); - } - if self.separating_axes.keys().any(|k| !self.broad_pairs.contains(k)) { - return Err("separating axis without a broad pair".into()); - } - c.pair_count = self.broad_pairs.len() as u32; - for (i, &index) in self.broad_pairs.iter().enumerate() { - c.pairs[i] = index as u32; - c.axes[i] = exact2( - self.separating_axes.get(&index).copied().unwrap_or(V2::ZERO), - "separating axis", - )?; - } - if let Some(r) = self.shape_aabb { - c.flags |= CAR_HAS_SHAPE; - c.shape_position = f2(r.position); - c.shape_size = f2(r.size); - } - if let Some(a) = self.leaf_aabb { - c.flags |= CAR_HAS_LEAF; - c.leaf_min = f2(a.min); - c.leaf_max = f2(a.max); - } - if self.pair_check_pending { - c.flags |= CAR_PAIR_CHECK; - } - c.contact_count = previous.len() as u32; - for (g, p) in c.contacts.iter_mut().zip(previous) { - *g = GpuContact { - normal: exact2(p.normal, "contact normal")?, - point: exact2(p.point, "contact point")?, - wall_point: exact2(p.wall_point, "contact wall point")?, - local_point: exact2(p.local_point, "contact local point")?, - wall_local_point: exact2(p.wall_local_point, "contact wall local point")?, - depth: exact(p.depth, "contact depth")?, - shape: p.shape_index as u32, - flags: if p.wall_first { CONTACT_WALL_FIRST } else { 0 } | if p.used { CONTACT_USED } else { 0 }, - normal_mass: p.normal_mass, - tangent_mass: p.tangent_mass, - bias: p.bias, - bounce: p.bounce, - friction: p.friction, - acc_normal: p.acc_normal, - acc_tangent: p.acc_tangent, - acc_bias: p.acc_bias, - acc_bias_center: p.acc_bias_center, - }; - } - Ok(()) - } - - /// The inverse of `gpu_export`. - pub(crate) fn gpu_import(&mut self, c: &crate::gpu::simulation::GpuCar, previous: &mut Vec) { - use crate::gpu::simulation::*; - let n = c.pair_count as usize; - self.broad_pairs = c.pairs[..n].iter().map(|&i| i as usize).collect(); - self.separating_axes = c.pairs[..n] - .iter() - .zip(&c.axes) - .map(|(&i, &a)| (i as usize, v2(a))) - .collect(); - let fv = |v: Vec2| F2 { x: v[0], y: v[1] }; - self.shape_aabb = (c.flags & CAR_HAS_SHAPE != 0).then(|| FloatRect { - position: fv(c.shape_position), - size: fv(c.shape_size), - }); - self.leaf_aabb = (c.flags & CAR_HAS_LEAF != 0).then(|| Aabb { - min: fv(c.leaf_min), - max: fv(c.leaf_max), - }); - self.pair_check_pending = c.flags & CAR_PAIR_CHECK != 0; - previous.clear(); - previous.extend(c.contacts[..c.contact_count as usize].iter().map(|g| Contact { - normal: v2(g.normal), - point: v2(g.point), - depth: g.depth as f64, - shape_index: g.shape as usize, - wall_first: g.flags & CONTACT_WALL_FIRST != 0, - wall_point: v2(g.wall_point), - local_point: v2(g.local_point), - wall_local_point: v2(g.wall_local_point), - used: g.flags & CONTACT_USED != 0, - normal_mass: g.normal_mass, - tangent_mass: g.tangent_mass, - bias: g.bias, - bounce: g.bounce, - friction: g.friction, - acc_normal: g.acc_normal, - acc_tangent: g.acc_tangent, - acc_bias: g.acc_bias, - acc_bias_center: g.acc_bias_center, - })); - } -} diff --git a/github-simulator/source/src/physics/mod.rs b/github-simulator/source/src/physics/mod.rs deleted file mode 100644 index 3a67f87b46a298c34a0516029d46057500567ab7..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/physics/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -//! Vehicle dynamics and ordered contact solving. - -mod broadphase; -pub mod car; -pub mod collision; -pub mod simulation; -pub mod trace_state; diff --git a/github-simulator/source/src/physics/simulation.rs b/github-simulator/source/src/physics/simulation.rs deleted file mode 100644 index 0bf36952dbe2672280ad1c625db9fa30ff9d1ad9..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/physics/simulation.rs +++ /dev/null @@ -1,299 +0,0 @@ -//! Shared native broad phase for a controlled population with car collisions off. -//! Construction follows raw static bodies first, then cars, as in the oracle. -use crate::{ - math::godot_math::F2, - physics::broadphase::{Aabb, PopulationSpace}, - physics::car::{Car, Controls, DT}, - track::world::World, -}; -use rayon::prelude::*; -use std::sync::Arc; - -pub struct NativePopulation { - pub cars: Vec, - world: Arc, - physics: SharedPhysics, -} -enum PendingShapeUpdate { - Car(usize, Aabb), - WallCell(Vec<(usize, Aabb)>), -} -pub(crate) struct SharedPhysics { - space: PopulationSpace, - body_handles: Vec, - wall_handles: Vec, - cell_order: Vec<(i64, i64)>, - pending_redraw: Option>, - pending_shapes: Vec, - pending_deletions: Vec, -} -impl SharedPhysics { - // Body handles and pending native shape updates share these population indices. - #[allow(clippy::needless_range_loop)] - pub(crate) fn resize(&mut self, world: &World, cars: &mut [&mut Car], retained: usize) { - assert!(retained <= cars.len() && retained <= self.body_handles.len()); - let old = std::mem::take(&mut self.body_handles); - self.body_handles.extend_from_slice(&old[..retained]); - for car in cars.iter_mut().skip(retained) { - let raw = car.update_native_shape(world); - self.body_handles.push(self.space.add_body(raw)); - } - // A body joins the pending list once at its first shape edit. The - // native server drains that list from its head (reverse creation). - for i in retained..cars.len() { - let raw = cars[i].update_native_shape(world); - self.pending_shapes - .push(PendingShapeUpdate::Car(self.body_handles[i], raw)); - } - self.pending_deletions.extend_from_slice(&old[retained..]); - } - pub(crate) fn new(world: &World, cars: &mut [&mut Car]) -> Self { - let walls: Vec<_> = world - .track - .shapes - .iter() - .map(|s| Aabb { - min: F2::from(s.min), - max: F2::from(s.max), - }) - .collect(); - let body_handles: Vec<_> = (0..cars.len()) - .map(|i| { - if world.track.native_body_order_reverse { - cars.len() - 1 - i - } else { - i - } - }) - .collect(); - let mut bodies = vec![ - Aabb { - min: F2::default(), - max: F2::default() - }; - cars.len() - ]; - for (i, car) in cars.iter_mut().enumerate() { - bodies[body_handles[i]] = car.update_native_shape(world); - } - let mut space = PopulationSpace::new(&walls, &bodies, world.track.native_tilemap); - for (i, car) in cars.iter_mut().enumerate().rev() { - let raw = car.update_native_shape(world); - space.move_body(body_handles[i], raw); - } - if !world.track.native_tilemap { - space.finish_setup(); - } - Self { - space, - body_handles, - wall_handles: (0..walls.len()).collect(), - cell_order: world.track.native_cell_order.clone(), - pending_redraw: None, - pending_shapes: Vec::new(), - pending_deletions: Vec::new(), - } - } - pub(crate) fn queue_tilemap_redraw(&mut self, old: Arc) { - assert!( - self.pending_redraw.is_none(), - "coalesce track edits before queuing native redraw" - ); - self.pending_redraw = Some(old); - } - /// TileMap redraw keeps cell bodies and the BVH allocator, but replaces shapes. - fn redraw_tilemap(&mut self, old: &World, new: &World) { - assert!(old.track.native_tilemap && new.track.native_tilemap); - assert!(old - .track - .shapes - .iter() - .chain(&new.track.shapes) - .all(|s| s.tile.is_some())); - let old_handles = std::mem::replace(&mut self.wall_handles, vec![usize::MAX; new.track.shapes.len()]); - let mut dirty = self.cell_order.clone(); - for &cell in &new.track.native_tile_order { - if !dirty.contains(&cell) { - dirty.push(cell); - } - } - for &cell in dirty.iter().rev() { - let old_indices: Vec<_> = old - .track - .shapes - .iter() - .enumerate() - .filter_map(|(i, s)| (s.tile == Some(cell)).then_some(i)) - .collect(); - let new_indices: Vec<_> = new - .track - .shapes - .iter() - .enumerate() - .filter_map(|(i, s)| (s.tile == Some(cell)).then_some(i)) - .collect(); - if new_indices.is_empty() { - if !old_indices.is_empty() { - // PhysicsServer.free drains every pending object's shapes first. - self.flush_shape_updates(); - for i in old_indices { - self.space.remove_wall(old_handles[i]); - } - } - continue; - } - let mut cache: Vec<_> = old_indices - .iter() - .map(|&i| { - let s = &old.track.shapes[i]; - (F2::from(s.min), F2::from(s.max) - F2::from(s.min)) - }) - .collect(); - // Setting transform, collision layer and mask updates old shapes. - for _ in 0..3 { - for (n, &i) in old_indices.iter().enumerate() { - let s = &old.track.shapes[i]; - let old_size = cache[n].1; - let margin = ((old_size.x + old_size.y) as f64 * 0.5 * 0.05) as f32; - let d = F2 { x: margin, y: margin }; - let position = F2::from(s.min) - d; - let size = (F2::from(s.max) - F2::from(s.min)) + d * 2.0; - cache[n] = (position, size); - self.space.move_wall(old_handles[i], Aabb::from_rect(position, size)); - } - } - // remove_shape(0) unregisters all following shapes in index order. - for i in old_indices { - self.space.remove_wall(old_handles[i]); - } - self.pending_shapes.push(PendingShapeUpdate::WallCell( - new_indices - .into_iter() - .map(|i| { - let s = &new.track.shapes[i]; - ( - i, - Aabb { - min: F2::from(s.min), - max: F2::from(s.max), - }, - ) - }) - .collect(), - )); - } - self.cell_order = dirty - .into_iter() - .filter(|c| new.track.native_tile_order.contains(c)) - .collect(); - } - fn flush_shape_updates(&mut self) { - while let Some(update) = self.pending_shapes.pop() { - match update { - PendingShapeUpdate::Car(body, raw) => self.space.move_body(body, raw), - PendingShapeUpdate::WallCell(shapes) => { - for (i, raw) in shapes { - self.wall_handles[i] = self.space.add_wall(raw, i); - } - } - } - } - } - fn process_deferred(&mut self, world: &World) { - if let Some(old) = self.pending_redraw.take() { - self.redraw_tilemap(&old, world); - } - for body in std::mem::take(&mut self.pending_deletions) { - // QueueFree first exits the tree and unregisters the body. Its - // destructor then flushes every pending shape before freeing it. - self.space.remove_body(body); - self.pending_shapes - .retain(|u| !matches!(u,PendingShapeUpdate::Car(id,_) if *id==body)); - self.flush_shape_updates(); - } - self.flush_shape_updates(); - assert!(self.wall_handles.iter().all(|&h| h != usize::MAX)); - } - pub(crate) fn advance( - &mut self, - world: &World, - cars: &mut [&mut Car], - controls: &[Controls], - eliminate_on_wall: bool, - drive: bool, - ) -> Vec { - assert_eq!(controls.len(), cars.len()); - assert_eq!( - self.body_handles.len(), - cars.len(), - "native population size cannot change without updating body allocation state" - ); - self.process_deferred(world); - self.space.update(); - // Wall pairs change only in `update`, so they are fixed for this tick - // and cars integrate independently. Body moves only touch the bodies - // tree; they replay in the native list order afterwards. - for (i, car) in cars.iter_mut().enumerate() { - let handle = self.body_handles[i]; - let pairs = self.space.wall_pairs(handle); - let orientation: Vec<_> = pairs.iter().map(|&wall| self.space.wall_first(handle, wall)).collect(); - let shape_indices: Vec<_> = pairs.iter().map(|&wall| self.space.wall_scene_index(wall)).collect(); - car.set_native_pairs(&shape_indices, &orientation, self.space.wall_pair_epochs(handle)); - } - cars.par_iter_mut() - .zip(controls) - .for_each(|(car, c)| car.native_integrate(world, c, DT, eliminate_on_wall, drive)); - for (i, car) in cars.iter().enumerate().rev() { - self.space.move_body(self.body_handles[i], car.native_shape_bounds()); - } - // Native integration and script state callbacks traverse opposite lists. - let contacts: Vec = cars.par_iter_mut().map(|car| car.native_callback(world)).collect(); - for (i, car) in cars.iter().enumerate() { - self.space.move_body(self.body_handles[i], car.native_shape_bounds()); - } - contacts - } -} -impl NativePopulation { - pub fn query_wall_candidates(&self, min: crate::math::vec2::V2, max: crate::math::vec2::V2) -> Vec { - self.physics.space.query_walls(Aabb { - min: F2::from(min), - max: F2::from(max), - }) - } - pub fn redraw_tilemap(&mut self, world: Arc) { - self.physics.queue_tilemap_redraw(self.world.clone()); - self.world = world; - } - pub fn extend_cars(&mut self, cars: impl IntoIterator) { - let retained = self.cars.len(); - self.cars.extend(cars); - self.physics - .resize(&self.world, &mut self.cars.iter_mut().collect::>(), retained); - } - pub fn truncate(&mut self, count: usize) { - assert!(count <= self.cars.len()); - self.cars.truncate(count); - self.physics - .resize(&self.world, &mut self.cars.iter_mut().collect::>(), count); - } - pub fn new(world: Arc, mut cars: Vec) -> Self { - let physics = SharedPhysics::new(&world, &mut cars.iter_mut().collect::>()); - Self { cars, world, physics } - } - pub fn step(&mut self, controls: &[Controls], eliminate_on_wall: bool) { - self.advance(controls, eliminate_on_wall, true); - } - pub fn passive_step(&mut self, eliminate_on_wall: bool) { - self.advance(&vec![Controls::default(); self.cars.len()], eliminate_on_wall, false); - } - fn advance(&mut self, controls: &[Controls], eliminate_on_wall: bool, drive: bool) { - self.physics.advance( - &self.world, - &mut self.cars.iter_mut().collect::>(), - controls, - eliminate_on_wall, - drive, - ); - } -} diff --git a/github-simulator/source/src/physics/trace_state.rs b/github-simulator/source/src/physics/trace_state.rs deleted file mode 100644 index 7ed92e3b679086c474ef36efc2908b3d02085c52..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/physics/trace_state.rs +++ /dev/null @@ -1,85 +0,0 @@ -//! Restore exported float32 state and, when justified, its integration angle. -use crate::{ - math::godot_math::{self, F2}, - math::vec2::V2, - physics::car::{Car, DT}, - track::world::World, -}; -use serde_json::Value; -fn f(v: &Value) -> f64 { - v.as_f64().expect("trace number") as f32 as f64 -} -fn v(v: &Value) -> V2 { - V2::new(f(&v[0]), f(&v[1])) -} - -pub(crate) fn transform_angle(world: &World, state: &Value, previous: Option<&Value>) -> f64 { - let recorded = f(&state["rotation"]); - let Some(previous) = previous else { - return recorded; - }; - let Some(contacts) = state.get("physics_contacts").and_then(Value::as_array) else { - return recorded; - }; - // Without penetration bias, integrate_velocities used the preceding angle - // and this frame's angular velocity. Contact points are world-space floats; - // allow two coordinate ULPs when deciding whether depth is below the limit. - let no_bias = contacts.iter().all(|c| { - let a = v(&c["local_position"]); - let b = v(&c["collider_position"]); - let scale = a.x.abs().max(a.y.abs()).max(b.x.abs()).max(b.y.abs()) as f32; - let ulp = f32::from_bits(scale.to_bits() + 1) - scale; - (a - b).length() + 2.0 * (ulp as f64) < world.vehicle.allowed_penetration - }); - if !no_bias { - return recorded; - } - let angle = f(&previous["rotation"]) as f32 + f(&state["angular_velocity"]) as f32 * DT as f32; - let (sine, cosine) = crate::math::native_math::engine_sin_cos(angle); - let extracted = world.math.atan2(sine, cosine); - // Reject resets/discontinuities. Two float32 ULPs account for angle - // extraction by the native engine versus the managed trace exporter. - let ulp = f32::from_bits((recorded as f32).abs().to_bits() + 1) - (recorded as f32).abs(); - let error = (extracted as f64 - recorded) - .sin() - .atan2((extracted as f64 - recorded).cos()) - .abs(); - if error <= 2.0 * ulp as f64 { - angle as f64 - } else { - recorded - } -} - -pub(crate) fn body_basis(world: &World, state: &Value, previous: Option<&Value>) -> (F2, F2) { - if let Some(basis) = state.get("body_basis") { - return (v(&basis[0]).into(), v(&basis[1]).into()); - } - if state.get("reset_transform").and_then(Value::as_bool) == Some(true) { - return godot_math::reset_basis(f(state.get("reset_rotation").unwrap_or(&state["rotation"])) as f32); - } - godot_math::basis(transform_angle(world, state, previous)) -} - -pub fn car_from_frame(world: &World, state: &Value, previous: Option<&Value>, older: Option<&Value>) -> Car { - let mut car = Car::new(world, v(&state["position"]), f(&state["rotation"])); - car.set_transform_angle(transform_angle(world, state, previous)); - let (x, y) = body_basis(world, state, previous); - car.body_basis = (x, y); - car.velocity = v(&state["velocity"]); - car.angular_velocity = f(&state["angular_velocity"]); - car.boost_energy = f(&state["boost_energy"]); - for (i, (spec, wheel)) in world.vehicle.wheels.iter().zip(&mut car.wheels).enumerate() { - wheel.angle_deg = f(&state["wheel_angles"][i]); - if let Some(previous) = previous { - let position = godot_math::transform_point( - v(&previous["position"]), - body_basis(world, previous, older), - spec.position, - ); - wheel.previous_position = Some(position); - wheel.previous_surface = world.track.surface(position); - } - } - car -} diff --git a/github-simulator/source/src/server/dispatch.rs b/github-simulator/source/src/server/dispatch.rs deleted file mode 100644 index a59243c439944202c74fcabb164b78282300a6f6..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/server/dispatch.rs +++ /dev/null @@ -1,196 +0,0 @@ -//! Operations of `altd-sim serve` on one connection's `Session`. - -use crate::math::profile::MathProfile; -use crate::training::session::{Backend, Session, SessionOptions}; -use serde_json::{json, Value}; - -/// A reply: a text frame, or a binary frame with a payload. -#[derive(Debug)] -pub enum Reply { - Text(Value), - Binary(Value, Vec), -} - -/// The session of one connection; `create` installs it. -pub struct Connection { - session: Option, - /// The math profile of sessions whose options name none. - math: MathProfile, -} - -fn arg<'a>(args: &'a Value, name: &str) -> Result<&'a Value, String> { - args.get(name).ok_or_else(|| format!("missing argument {name}")) -} - -fn string_arg<'a>(args: &'a Value, name: &str) -> Result<&'a str, String> { - arg(args, name)? - .as_str() - .ok_or_else(|| format!("argument {name} must be a string")) -} - -fn u64_arg(args: &Value, name: &str) -> Result { - arg(args, name)? - .as_u64() - .ok_or_else(|| format!("argument {name} must be a non-negative integer")) -} - -fn bool_arg(args: &Value, name: &str) -> Result { - arg(args, name)? - .as_bool() - .ok_or_else(|| format!("argument {name} must be a boolean")) -} - -fn parse(text: &str, what: &str) -> Result { - serde_json::from_str(text).map_err(|e| format!("invalid {what} JSON: {e}")) -} - -fn backend_name(backend: Backend) -> &'static str { - match backend { - Backend::Cpu => "cpu", - Backend::Hip => "hip", - } -} - -impl Connection { - pub fn new(math: MathProfile) -> Connection { - Connection { session: None, math } - } - - /// Handles one request. `payload` is the binary payload of a binary - /// request frame. Errors leave the connection usable. - pub fn handle(&mut self, id: u64, op: &str, args: &Value, payload: Option<&[u8]>) -> Reply { - match self.run(op, args, payload) { - Ok(Reply::Text(ok)) => Reply::Text(json!({"id": id, "ok": ok, "state": self.state()})), - Ok(Reply::Binary(ok, bytes)) => Reply::Binary(json!({"id": id, "ok": ok, "state": self.state()}), bytes), - Err(error) => Reply::Text(json!({"id": id, "error": error})), - } - } - - /// The session state every successful reply carries. - fn state(&self) -> Value { - let Some(s) = &self.session else { - return Value::Null; - }; - json!({ - "started": s.started(), - "generation": s.runner.generation, - "tick": s.runner.tick, - "population": s.runner.agents.len(), - "activeCount": s.active_count(), - "checkpointGeneration": s.boundary().map(|(g, _)| g), - "backend": backend_name(s.backend()), - "backendNote": s.backend_note(), - "mathProfile": s.math_profile(), - }) - } - - fn session(&mut self) -> Result<&mut Session, String> { - self.session - .as_mut() - .ok_or_else(|| "create a session first".to_string()) - } - - fn run(&mut self, op: &str, args: &Value, payload: Option<&[u8]>) -> Result { - if payload.is_some() && op != "restoreCheckpointBytes" { - return Err(format!("{op} takes no binary payload")); - } - let ok = |v: Value| Ok(Reply::Text(v)); - match op { - "create" => { - if self.session.is_some() { - return Err("the connection already has a session".into()); - } - let mut options: SessionOptions = match args.get("options") { - None | Some(Value::Null) => SessionOptions::default(), - Some(o) => { - serde_json::from_value(o.clone()).map_err(|e| format!("invalid session options: {e}"))? - } - }; - options.math_profile.get_or_insert(self.math); - let session = Session::new( - &parse(string_arg(args, "scene")?, "scene")?, - &parse(string_arg(args, "network")?, "network")?, - &parse(string_arg(args, "model")?, "model")?, - options, - )?; - let reply = json!({ - "backend": backend_name(session.backend()), - "backendNote": session.backend_note(), - "mathProfile": session.math_profile(), - }); - self.session = Some(session); - ok(reply) - } - "start" => { - self.session()?.start()?; - ok(Value::Null) - } - "startWithShape" => { - let shape = arg(args, "shape")? - .as_array() - .ok_or("argument shape must be an array")? - .iter() - .map(|v| { - v.as_u64() - .and_then(|n| u32::try_from(n).ok()) - .map(|n| n as usize) - .ok_or("shape entries must be 32-bit non-negative integers") - }) - .collect::, _>>()?; - self.session()?.start_with_shape(&shape)?; - ok(Value::Null) - } - "restoreCheckpointBytes" => { - let bytes = payload.ok_or("restoreCheckpointBytes needs a binary frame")?; - self.session()?.restore_checkpoint_bytes(bytes)?; - ok(Value::Null) - } - "restoreCheckpointJson" => { - let checkpoint = parse(string_arg(args, "json")?, "checkpoint")?; - self.session()?.restore_checkpoint(&checkpoint)?; - ok(Value::Null) - } - "advance" => { - let (ticks, stop) = (u64_arg(args, "ticks")?, bool_arg(args, "stopWhenInactive")?); - ok(json!(self.session()?.advance(ticks, stop)?)) - } - "advanceGeneration" => { - let limit = u64_arg(args, "timeLimitTicks")?; - ok(json!(self.session()?.advance_generation(limit)?)) - } - "nextGeneration" => { - let (preserved, rewards) = self.session()?.next_generation()?; - ok(json!({"preservedCount": preserved, "rewards": rewards})) - } - "setEvolutionSettings" => { - let text = string_arg(args, "json")?; - self.session()?.set_evolution_settings(text)?; - ok(Value::Null) - } - "replaceTrack" => { - let scene = parse(string_arg(args, "scene")?, "scene")?; - self.session()?.replace_track(&scene)?; - ok(Value::Null) - } - "generationSummary" => { - let session = self.session()?; - session.sync()?; - ok(serde_json::to_value(session.generation_summary()).unwrap()) - } - "networkJson" => { - let index = u64_arg(args, "index")?; - let index = usize::try_from(index).map_err(|_| format!("car {index} is outside the population"))?; - ok(Value::String(self.session()?.network_json(index)?)) - } - "carStates" => { - let session = self.session()?; - session.sync()?; - let states = session.car_states(); - let bytes = states.iter().flat_map(|v| v.to_le_bytes()).collect(); - Ok(Reply::Binary(json!(states.len()), bytes)) - } - "checkpointBytes" => Ok(Reply::Binary(Value::Null, self.session()?.checkpoint_bytes()?)), - other => Err(format!("unknown operation {other:?}")), - } - } -} diff --git a/github-simulator/source/src/server/mod.rs b/github-simulator/source/src/server/mod.rs deleted file mode 100644 index 3dfc1d9cf0458a79e4f190ddb4531bb393f38280..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/server/mod.rs +++ /dev/null @@ -1,343 +0,0 @@ -//! `altd-sim serve`: a WebSocket server on the loopback address that Drive -//! Lab uses as a remote simulation (docs/server.md). Each connection gets a -//! thread and one `Session`; requests run in order. - -pub mod dispatch; -pub mod protocol; - -use crate::math::profile::MathProfile; -use dispatch::{Connection, Reply}; -use protocol::{decode_binary, encode_binary, host_allowed, normalize_origin, origin_allowed, Request, PROTOCOL}; -use serde_json::{json, Value}; -use std::io::{Read, Write}; -use std::net::{Ipv4Addr, SocketAddr, TcpListener, TcpStream}; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::Arc; -use std::time::{Duration, Instant}; -use tungstenite::handshake::server::{ErrorResponse, Request as HttpRequest, Response as HttpResponse}; -use tungstenite::http::StatusCode; -use tungstenite::protocol::WebSocketConfig; -use tungstenite::{Message, WebSocket}; - -/// The port Drive Lab connects to unless the user changes it. -pub const DEFAULT_PORT: u16 = 47800; -/// The only origin allowed unless `--allow-origin` replaces it. -pub const DEFAULT_ORIGIN: &str = "https://drivinglab.jectrum.de"; -/// The WebSocket endpoint. -pub const PATH: &str = "/v1"; -/// Finite transport ceilings independent of browser continuation framing. -pub const MAX_MESSAGE_BYTES: usize = 64 * 1024 * 1024; -pub const MAX_FRAME_BYTES: usize = MAX_MESSAGE_BYTES; -/// Includes sockets that have not yet completed the handshake. -pub const MAX_CONNECTIONS: usize = 64; -pub const HANDSHAKE_TIMEOUT: Duration = Duration::from_secs(5); -/// Applies to socket I/O, not computation between requests. -pub const IDLE_TIMEOUT: Duration = Duration::from_secs(600); - -struct ConnectionPermit(Arc); - -impl ConnectionPermit { - fn acquire(active: &Arc) -> Option { - active - .fetch_update(Ordering::AcqRel, Ordering::Acquire, |n| { - (n < MAX_CONNECTIONS).then_some(n + 1) - }) - .ok() - .map(|_| Self(active.clone())) - } -} - -impl Drop for ConnectionPermit { - fn drop(&mut self) { - self.0.fetch_sub(1, Ordering::AcqRel); - } -} - -// A deadline across all handshake I/O prevents drip-fed bytes from resetting -// a per-read timeout forever. Established sockets retain their idle timeout. -struct ConnectionStream { - stream: TcpStream, - handshake_deadline: Option, -} - -impl ConnectionStream { - fn timeout(&self) -> std::io::Result { - match self.handshake_deadline { - Some(deadline) => deadline - .checked_duration_since(Instant::now()) - .filter(|remaining| !remaining.is_zero()) - .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::TimedOut, "handshake deadline")), - None => Ok(IDLE_TIMEOUT), - } - } -} - -impl Read for ConnectionStream { - fn read(&mut self, buffer: &mut [u8]) -> std::io::Result { - self.stream.set_read_timeout(Some(self.timeout()?))?; - self.stream.read(buffer) - } -} - -impl Write for ConnectionStream { - fn write(&mut self, buffer: &[u8]) -> std::io::Result { - self.stream.set_write_timeout(Some(self.timeout()?))?; - self.stream.write(buffer) - } - fn flush(&mut self) -> std::io::Result<()> { - self.stream.flush() - } -} - -pub struct Config { - /// TCP port on 127.0.0.1; 0 picks a free one. - pub port: u16, - /// Allowed HTTP(S) origins; `bind` validates and normalizes them. - pub origins: Vec, - /// The math profile of sessions whose options name none; None detects it. - pub math_profile: Option, -} - -/// A bound server; `run` accepts connections. -pub struct Server { - listener: TcpListener, - origins: Arc>, - math: MathProfile, -} - -/// HIP availability as `hello` reports it. -fn hip_status() -> Value { - match crate::training::session::hip_device() { - Ok((gpu, device)) => json!({"available": true, "device": device, "platform": gpu.platform()}), - Err(reason) => json!({"available": false, "reason": reason}), - } -} - -fn hello(math: MathProfile) -> Value { - json!({ - "type": "hello", - "protocol": PROTOCOL, - "version": crate::VERSION, - "threads": rayon::current_num_threads(), - "carStateStride": crate::training::CAR_STATE_STRIDE, - "carStateFields": crate::training::CAR_STATE_FIELDS, - "hip": hip_status(), - "mathProfile": math, - "mathProfiles": MathProfile::ALL, - }) -} - -fn reject(reason: &str) -> ErrorResponse { - let mut response = ErrorResponse::new(Some(format!("{reason}\n"))); - *response.status_mut() = StatusCode::FORBIDDEN; - response -} - -/// The handshake checks of docs/server.md: path, `Origin` and `Host`. -fn check_request(request: &HttpRequest, origins: &[String], port: u16) -> Result<(), &'static str> { - if request.uri().path() != PATH { - return Err("unknown path"); - } - let header = |name| request.headers().get(name).and_then(|v| v.to_str().ok()); - match header("origin") { - Some(origin) if origin_allowed(origin, origins) => {} - _ => return Err("origin not allowed (see altd-sim serve --allow-origin)"), - } - match header("host") { - Some(host) if host_allowed(host, port) => Ok(()), - _ => Err("host not allowed"), - } -} - -impl Server { - pub fn bind(config: Config) -> std::io::Result { - let origins = config - .origins - .iter() - .map(|origin| normalize_origin(origin)) - .collect::, _>>() - .map_err(|reason| std::io::Error::new(std::io::ErrorKind::InvalidInput, reason))?; - let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, config.port))?; - Ok(Server { - listener, - origins: Arc::new(origins), - math: config.math_profile.unwrap_or_else(MathProfile::detect), - }) - } - - pub fn local_addr(&self) -> std::io::Result { - self.listener.local_addr() - } - - /// The math profile of sessions whose options name none. - pub fn math_profile(&self) -> MathProfile { - self.math - } - - /// Whether HIP sessions can run, as `hello` reports it. - pub fn hip_status(&self) -> Value { - hip_status() - } - - /// Accepts connections until the listener fails. - pub fn run(self) -> std::io::Result<()> { - let port = self.local_addr()?.port(); - let active = Arc::new(AtomicUsize::new(0)); - for stream in self.listener.incoming() { - let stream = match stream { - Ok(s) => s, - Err(e) => { - eprintln!("altd-sim serve: accept failed: {e}"); - continue; - } - }; - let Some(permit) = ConnectionPermit::acquire(&active) else { - drop(stream); - continue; - }; - let (origins, math) = (self.origins.clone(), self.math); - if let Err(error) = std::thread::Builder::new() - .name("altd-sim-connection".into()) - .spawn(move || { - let _permit = permit; - serve_connection(stream, &origins, port, math); - }) - { - // Dropping the failed spawn's closure releases its permit/socket. - eprintln!("altd-sim serve: connection spawn failed: {error}"); - } - } - Ok(()) - } -} - -fn serve_connection(stream: TcpStream, origins: &[String], port: u16, math: MathProfile) { - let peer = stream.peer_addr().map_or_else(|_| "?".into(), |a| a.to_string()); - let _ = stream.set_nodelay(true); - if stream.set_read_timeout(Some(HANDSHAKE_TIMEOUT)).is_err() - || stream.set_write_timeout(Some(HANDSHAKE_TIMEOUT)).is_err() - { - return; - } - let mut origin = String::new(); - // tungstenite's handshake callback requires an unboxed HTTP response. - #[allow(clippy::result_large_err)] - let callback = |request: &HttpRequest, response: HttpResponse| { - origin = request - .headers() - .get("origin") - .and_then(|v| v.to_str().ok()) - .unwrap_or("none") - .to_owned(); - match check_request(request, origins, port) { - Ok(()) => Ok(response), - Err(reason) => { - eprintln!("altd-sim serve: rejected {peer} (origin {origin}): {reason}"); - Err(reject(reason)) - } - } - }; - // Check frames and assembled messages before application deserialization. - let config = WebSocketConfig::default() - .max_message_size(Some(MAX_MESSAGE_BYTES)) - .max_frame_size(Some(MAX_FRAME_BYTES)); - let stream = ConnectionStream { - stream, - handshake_deadline: Some(Instant::now() + HANDSHAKE_TIMEOUT), - }; - let mut socket = match tungstenite::accept_hdr_with_config(stream, callback, Some(config)) { - Ok(s) => s, - Err(_) => return, - }; - socket.get_mut().handshake_deadline = None; - eprintln!("altd-sim serve: connected {peer} (origin {origin})"); - match converse(&mut socket, math) { - Ok(()) => eprintln!("altd-sim serve: {peer} disconnected"), - Err(e) => eprintln!("altd-sim serve: {peer} closed: {e}"), - } -} - -fn send(socket: &mut WebSocket, reply: Reply) -> tungstenite::Result<()> { - match reply { - Reply::Text(v) => socket.send(Message::text(v.to_string())), - Reply::Binary(header, payload) => socket.send(Message::binary(encode_binary(&header, &payload))), - } -} - -/// Greets the client and answers its requests until it closes. The -/// session drops with the connection. -fn converse(socket: &mut WebSocket, math: MathProfile) -> Result<(), String> { - let mut connection = Connection::new(math); - socket - .send(Message::text(hello(math).to_string())) - .map_err(|e| e.to_string())?; - loop { - let message = match socket.read() { - Ok(m) => m, - Err(tungstenite::Error::ConnectionClosed | tungstenite::Error::AlreadyClosed) => return Ok(()), - Err(e) => return Err(e.to_string()), - }; - let reply = match message { - Message::Text(text) => match serde_json::from_str::(&text) { - Ok(r) => connection.handle(r.id, &r.op, &r.args, None), - Err(e) => { - // Keep the id when there is one, so the client can match the error. - let id = serde_json::from_str::(&text) - .ok() - .and_then(|v| v.get("id").cloned()) - .unwrap_or(Value::Null); - Reply::Text(json!({"id": id, "error": format!("invalid request: {e}")})) - } - }, - Message::Binary(frame) => { - let request = decode_binary(&frame).and_then(|(header, payload)| { - serde_json::from_value::(header) - .map(|r| (r, payload)) - .map_err(|e| format!("invalid binary frame header: {e}")) - }); - match request { - Ok((r, payload)) => connection.handle(r.id, &r.op, &r.args, Some(payload)), - Err(e) => { - let _ = socket.close(None); - return Err(e); - } - } - } - Message::Close(_) => continue, - Message::Ping(_) | Message::Pong(_) | Message::Frame(_) => continue, - }; - send(socket, reply).map_err(|e| e.to_string())?; - } -} - -#[cfg(test)] -mod connection_limit_tests { - use super::*; - - #[test] - fn overload_does_not_consume_a_permit_and_drop_releases_it() { - let active = Arc::new(AtomicUsize::new(0)); - let mut permits: Vec<_> = (0..MAX_CONNECTIONS) - .map(|_| ConnectionPermit::acquire(&active).unwrap()) - .collect(); - assert!(ConnectionPermit::acquire(&active).is_none()); - assert_eq!(active.load(Ordering::Acquire), MAX_CONNECTIONS); - permits.pop(); - assert!(ConnectionPermit::acquire(&active).is_some()); - drop(permits); - assert_eq!(active.load(Ordering::Acquire), 0); - } - - #[test] - fn rejected_or_incomplete_handshake_drops_its_socket() { - use std::io::Read; - let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).unwrap(); - let mut client = TcpStream::connect(listener.local_addr().unwrap()).unwrap(); - client.set_read_timeout(Some(Duration::from_secs(10))).unwrap(); - let stream = listener.accept().unwrap().0; - let worker = std::thread::spawn(move || serve_connection(stream, &[], 0, MathProfile::Proton)); - let mut byte = [0]; - assert_eq!(client.read(&mut byte).unwrap(), 0); - worker.join().unwrap(); - } -} diff --git a/github-simulator/source/src/server/protocol.rs b/github-simulator/source/src/server/protocol.rs deleted file mode 100644 index a4a9838f5280749c07a954cbf7b81256bccacc83..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/server/protocol.rs +++ /dev/null @@ -1,237 +0,0 @@ -//! Messages of `altd-sim serve` (docs/server.md): origin and host checks, -//! requests, and the binary frame layout. - -use serde_json::Value; -use std::net::Ipv6Addr; - -/// The protocol version `hello` announces. -pub const PROTOCOL: u32 = 1; - -/// Normalizes an allowed origin given on the command line to the form -/// browsers send: lowercase `scheme://host[:port]` with the scheme's default -/// port left out. Only `http` and `https` origins without a path are valid. -pub fn normalize_origin(text: &str) -> Result { - let invalid = |why: &str| format!("invalid origin {text:?}: {why} (expected scheme://host[:port])"); - let (scheme, rest) = text.split_once("://").ok_or_else(|| invalid("no scheme"))?; - let scheme = scheme.to_ascii_lowercase(); - let default_port = match scheme.as_str() { - "http" => "80", - "https" => "443", - _ => return Err(invalid("the scheme must be http or https")), - }; - let authority = rest.strip_suffix('/').unwrap_or(rest); - if authority.is_empty() { - return Err(invalid("no host")); - } - if authority.contains(['/', '?', '#', '@']) || authority.chars().any(|c| c.is_whitespace() || c.is_control()) { - return Err(invalid("only scheme, host and port are allowed")); - } - // An IPv6 host is bracketed; the port follows the closing bracket. - let (host, port) = match authority.strip_prefix('[') { - Some(inner) => { - let (host, after) = inner.split_once(']').ok_or_else(|| invalid("unclosed IPv6 bracket"))?; - let host = host.parse::().map_err(|_| invalid("invalid IPv6 host"))?; - let port = match after { - "" => None, - p => Some( - p.strip_prefix(':') - .ok_or_else(|| invalid("garbage after the IPv6 host"))?, - ), - }; - // Browsers serialize mapped IPv4 addresses as hexadecimal hextets. - let host = if host.to_ipv4_mapped().is_some() { - let segments = host.segments(); - format!("::ffff:{:x}:{:x}", segments[6], segments[7]) - } else { - host.to_string() - }; - (format!("[{host}]"), port) - } - None => match authority.rsplit_once(':') { - Some((host, port)) => (host.to_ascii_lowercase(), Some(port)), - None => (authority.to_ascii_lowercase(), None), - }, - }; - if host.is_empty() - || !host.starts_with('[') - && !host - .bytes() - .all(|c| c.is_ascii_alphanumeric() || matches!(c, b'.' | b'-' | b'_')) - { - return Err(invalid("invalid host; use an ASCII hostname or IP address")); - } - let port = match port { - None => None, - Some(p) => { - if p.is_empty() || !p.bytes().all(|c| c.is_ascii_digit()) { - return Err(invalid("the port must be a number up to 65535")); - } - let n: u16 = p - .parse() - .map_err(|_| invalid("the port must be a number up to 65535"))?; - (n.to_string() != default_port).then(|| n.to_string()) - } - }; - Ok(match port { - Some(port) => format!("{scheme}://{host}:{port}"), - None => format!("{scheme}://{host}"), - }) -} - -/// Whether an `Origin` header value is allowed: it must equal an allowed -/// origin exactly. Opaque origins (`null`) never are. -pub fn origin_allowed(origin: &str, allowed: &[String]) -> bool { - origin != "null" && allowed.iter().any(|a| a == origin) -} - -/// Whether a `Host` header names this server: the loopback address or -/// `localhost` with the listening port. Rejecting other names blocks DNS -/// rebinding. -pub fn host_allowed(host: &str, port: u16) -> bool { - let Some((name, p)) = host.rsplit_once(':') else { - return false; - }; - p == port.to_string() && (name == "127.0.0.1" || name.eq_ignore_ascii_case("localhost")) -} - -/// A request: `{"id": 7, "op": "advance", "args": {...}}`. -#[derive(Debug, serde::Deserialize)] -#[serde(deny_unknown_fields)] -pub struct Request { - pub id: u64, - pub op: String, - #[serde(default)] - pub args: Value, -} - -/// A binary frame: a little-endian u32 JSON length, the JSON header and the -/// raw payload. -pub fn encode_binary(header: &Value, payload: &[u8]) -> Vec { - let json = header.to_string(); - let mut out = Vec::with_capacity(4 + json.len() + payload.len()); - out.extend_from_slice(&(json.len() as u32).to_le_bytes()); - out.extend_from_slice(json.as_bytes()); - out.extend_from_slice(payload); - out -} - -/// Splits a binary frame into its JSON header and payload. -pub fn decode_binary(frame: &[u8]) -> Result<(Value, &[u8]), String> { - let length = frame - .get(..4) - .map(|b| u32::from_le_bytes(b.try_into().unwrap()) as usize) - .ok_or("binary frame shorter than its length prefix")?; - let json = frame - .get(4..4usize.saturating_add(length)) - .filter(|_| length <= frame.len() - 4) - .ok_or("binary frame shorter than its JSON header")?; - let header = serde_json::from_slice(json).map_err(|e| format!("invalid binary frame header: {e}"))?; - Ok((header, &frame[4 + length..])) -} - -#[cfg(test)] -mod tests { - use super::*; - use serde_json::json; - - #[test] - fn origins_normalize_to_the_browser_form() { - for (given, want) in [ - ("https://drivinglab.jectrum.de", "https://drivinglab.jectrum.de"), - ("https://drivinglab.jectrum.de/", "https://drivinglab.jectrum.de"), - ("HTTPS://DrivingLab.Jectrum.de:443", "https://drivinglab.jectrum.de"), - ("http://127.0.0.1:5173", "http://127.0.0.1:5173"), - ("http://localhost:80", "http://localhost"), - ("https://example.com:8443", "https://example.com:8443"), - ("http://[::1]:5173", "http://[::1]:5173"), - ("http://[0:0:0:0:0:0:0:1]:5173", "http://[::1]:5173"), - ("http://[::ffff:192.0.2.1]:5173", "http://[::ffff:c000:201]:5173"), - ] { - assert_eq!(normalize_origin(given).as_deref(), Ok(want), "{given}"); - } - for bad in [ - "drivinglab.jectrum.de", - "ftp://example.com", - "https://", - "https://example.com/path", - "https://example.com?q", - "https://user@example.com", - "https://example.com:99999", - "https://example.com:port", - "null", - "http://[::1", - "http://[localhost]:5173", - "http://[::1%eth0]:5173", - "http://local\\host:5173", - "http://example.com:+80", - "https://example.com:", - "https://example.com//", - ] { - assert!(normalize_origin(bad).is_err(), "{bad}"); - } - } - - #[test] - fn origins_match_exactly() { - let allowed = vec![normalize_origin("https://drivinglab.jectrum.de").unwrap()]; - assert!(origin_allowed("https://drivinglab.jectrum.de", &allowed)); - for other in [ - "http://drivinglab.jectrum.de", - "https://drivinglab.jectrum.de:8443", - "https://drivinglab.jectrum.de/", - "https://evil.drivinglab.jectrum.de", - "https://drivinglab.jectrum.de.evil.com", - "null", - "", - ] { - assert!(!origin_allowed(other, &allowed), "{other}"); - } - assert!(!origin_allowed("null", &["null".to_string()])); - } - - #[test] - fn hosts_must_name_the_loopback_port() { - assert!(host_allowed("127.0.0.1:47800", 47800)); - assert!(host_allowed("localhost:47800", 47800)); - assert!(host_allowed("LocalHost:47800", 47800)); - for bad in [ - "127.0.0.1:47801", - "127.0.0.1", - "localhost", - "evil.example:47800", - "127.0.0.2:47800", - "[::1]:47800", - "localhost:+47800", - "localhost:047800", - "", - ] { - assert!(!host_allowed(bad, 47800), "{bad}"); - } - } - - #[test] - fn binary_frames_round_trip_and_reject_truncation() { - let header = json!({"id": 3, "op": "restoreCheckpointBytes"}); - let frame = encode_binary(&header, b"payload"); - let (h, payload) = decode_binary(&frame).unwrap(); - assert_eq!((h, payload), (header.clone(), &b"payload"[..])); - let empty = encode_binary(&header, b""); - assert_eq!(decode_binary(&empty).unwrap().1, b""); - assert!(decode_binary(&frame[..3]).is_err()); - assert!(decode_binary(&frame[..10]).is_err()); - let mut huge = frame.clone(); - huge[..4].copy_from_slice(&u32::MAX.to_le_bytes()); - assert!(decode_binary(&huge).is_err()); - let mut garbage = frame; - garbage[4] = b'x'; - assert!(decode_binary(&garbage).is_err()); - } - - #[test] - fn requests_parse_with_optional_args() { - let r: Request = serde_json::from_str(r#"{"id":1,"op":"nextGeneration"}"#).unwrap(); - assert_eq!((r.id, r.op.as_str(), r.args), (1, "nextGeneration", Value::Null)); - assert!(serde_json::from_str::(r#"{"op":"x"}"#).is_err()); - assert!(serde_json::from_str::(r#"{"id":1,"op":"x","extra":1}"#).is_err()); - } -} diff --git a/github-simulator/source/src/track/bsp.rs b/github-simulator/source/src/track/bsp.rs deleted file mode 100644 index 117ad8cf2d255def128be35eb499aa98bb69f7ee..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/track/bsp.rs +++ /dev/null @@ -1,448 +0,0 @@ -//! Game BspTreeRaycaster construction and traversal, using managed float order. -use crate::{ - math::godot_math::{ray_intersection, ray_wall_hit, F2}, - math::vec2::V2, - track::world::Wall, -}; -use serde_json::Value; - -pub struct Node { - partition: Wall, - segments: Vec, - front: Option>, - back: Option>, -} -fn classify(p: V2, wall: Wall) -> i8 { - let a = F2::from(wall.end) - F2::from(wall.start); - let b = F2::from(p) - F2::from(wall.start); - let cross = a.cross(b); - if cross > 0.0001 { - 1 - } else if cross < -0.0001 { - -1 - } else { - 0 - } -} -fn sides(wall: Wall, partition: Wall) -> (i8, i8) { - (classify(wall.start, partition), classify(wall.end, partition)) -} -impl Node { - pub fn from_polygons(polygons: &[Value]) -> Option> { - let mut segments = Vec::new(); - for polygon in polygons { - let points: Vec = polygon["points"] - .as_array() - .unwrap() - .iter() - .map(|p| { - V2::new( - p[0].as_f64().unwrap() as f32 as f64, - p[1].as_f64().unwrap() as f32 as f64, - ) - }) - .collect(); - for i in 0..points.len() { - let wall = Wall { - start: points[i], - end: points[(i + 1) % points.len()], - }; - if wall.start != wall.end { - segments.push(wall); - } - } - } - Self::build(segments) - } - fn build(walls: Vec) -> Option> { - if walls.is_empty() { - return None; - } - let mut best = i32::MAX; - let mut partition = walls[0]; - for &candidate in &walls { - let (mut front, mut back, mut split) = (0i32, 0i32, 0i32); - for &wall in &walls { - if wall == candidate { - continue; - } - let (a, b) = sides(wall, candidate); - if a == 0 && b == 0 { - continue; - } - if a >= 0 && b >= 0 { - front += 1; - } else if a <= 0 && b <= 0 { - back += 1; - } else { - split += 1; - } - } - let score = (front - back).abs() + split * 10; - if score < best { - best = score; - partition = candidate; - } - } - let (mut front, mut back, mut segments) = (Vec::new(), Vec::new(), Vec::new()); - for wall in walls { - if wall == partition { - continue; - } - let (a, b) = sides(wall, partition); - if a == 0 && b == 0 { - segments.push(wall); - } else if a >= 0 && b >= 0 { - front.push(wall); - } else if a <= 0 && b <= 0 { - back.push(wall); - } else { - let start = F2::from(partition.start); - let delta = F2::from(partition.end) - start; - let other = F2::from(wall.end) - F2::from(wall.start); - let denominator = delta.x * other.y - other.x * delta.y; - let d = start - F2::from(wall.start); - let intersection: V2 = if denominator.abs() < 1e-6 { - V2::ZERO - } else { - (start + delta * ((other.x * d.y - other.y * d.x) / denominator)).into() - }; - if a == 1 { - front.push(Wall { - start: wall.start, - end: intersection, - }); - back.push(Wall { - start: intersection, - end: wall.end, - }); - } else { - front.push(Wall { - start: wall.end, - end: intersection, - }); - back.push(Wall { - start: intersection, - end: wall.start, - }); - } - } - } - Some(Box::new(Node { - partition, - segments, - front: Self::build(front), - back: Self::build(back), - })) - } - pub fn collect(&self, out: &mut Vec) { - out.push(self.partition); - out.extend(&self.segments); - if let Some(node) = &self.front { - node.collect(out); - } - if let Some(node) = &self.back { - node.collect(out); - } - } - pub(crate) fn closest(&self, point: V2) -> V2 { - let mut distance = f64::MAX; - let mut closest = F2::default(); - self.closest_recursive(point, &mut distance, &mut closest); - closest.into() - } - fn closest_recursive(&self, point: V2, distance: &mut f64, closest: &mut F2) { - let p = F2::from(point); - for wall in std::iter::once(&self.partition).chain(&self.segments) { - let a = F2::from(wall.start); - let delta = F2::from(wall.end) - a; - let len2 = delta.dot(delta); - let candidate = if len2 == 0.0 { - a - } else { - a + delta * ((p.dot(delta) - a.dot(delta)).clamp(0.0, len2) / len2) - }; - let d = p - candidate; - if (d.dot(d) as f64) < *distance { - *distance = d.dot(d) as f64; - *closest = candidate; - } - } - let (near, far) = if classify(point, self.partition) >= 0 { - (&self.front, &self.back) - } else { - (&self.back, &self.front) - }; - if let Some(n) = near { - n.closest_recursive(point, distance, closest); - } - let a = F2::from(self.partition.start); - let delta = F2::from(self.partition.end) - a; - let t = (p - a).dot(delta) / delta.dot(delta); - let d = p - (a + delta * t); - if (d.dot(d) as f64) < *distance { - if let Some(n) = far { - n.closest_recursive(point, distance, closest); - } - } - } - pub fn raycast(&self, start: V2, end: V2) -> Option { - let a = classify(start, self.partition); - let b = classify(end, self.partition); - let (near, far) = if a == 1 || a == 0 && b == 1 { - (&self.front, &self.back) - } else { - (&self.back, &self.front) - }; - if let Some(hit) = near.as_ref().and_then(|n| n.raycast(start, end)) { - return Some(hit); - } - let mut nearest = None; - let mut distance = f32::MAX; - for wall in std::iter::once(&self.partition).chain(&self.segments) { - if let Some(p) = ray_intersection(start, end, wall.start, wall.end) { - let d = F2::from(p) - F2::from(start); - let d2 = d.dot(d); - if d2 < distance { - distance = d2; - nearest = Some(p); - } - } - } - nearest.or_else(|| { - if a != b { - far.as_ref().and_then(|n| n.raycast(start, end)) - } else { - None - } - }) - } -} - -const NONE: u32 = u32::MAX; - -/// `Node` for raycasts: one arena, float32 wall geometry, and box tests that -/// skip exactly the walls `ray_intersection` rejects for being well beside the ray. -pub struct RayTree { - nodes: Vec, - /// Wall start and `end - start`; each node's partition, then its segments. - walls: Vec<(F2, F2)>, - /// Bound on `|x| + |y|` over wall endpoints. - magnitude: f32, -} -struct RayNode { - first: u32, - last: u32, - front: u32, - back: u32, - /// The node's own walls, and those of its whole subtree. - own: Bounds, - subtree: Bounds, -} -/// Box center and half extents, covering its wall endpoints. -#[derive(Clone, Copy)] -struct Bounds { - cx: f32, - cy: f32, - hx: f32, - hy: f32, -} -struct Ray { - start: F2, - end: F2, - b: F2, - margin: f32, -} - -impl Bounds { - fn new(lo: (f64, f64), hi: (f64, f64)) -> Bounds { - // Round the center, then widen the half extent to still cover the walls. - let half = |lo: f64, hi: f64| { - let center = ((lo + hi) * 0.5) as f32; - let half = (hi - center as f64).max(center as f64 - lo); - let mut rounded = half as f32; - if (rounded as f64) < half { - rounded = f32::from_bits(rounded.to_bits() + 1); - } - (center, rounded) - }; - let ((cx, hx), (cy, hy)) = (half(lo.0, hi.0), half(lo.1, hi.1)); - Bounds { cx, cy, hx, hy } - } - - /// True when the box lies beyond `margin` from the ray's infinite line, - /// entirely on one side. `|cross(b, p - start)|` is `|b|` times the - /// distance of `p`; over the box it varies by `|b.x| hy + |b.y| hx`, and - /// `|b.x| + |b.y| >= |b|`. - #[inline] - fn beside(&self, ray: &Ray) -> bool { - let b = ray.b; - let cross = b.x * (self.cy - ray.start.y) - b.y * (self.cx - ray.start.x); - cross.abs() > b.x.abs() * (self.hy + ray.margin) + b.y.abs() * (self.hx + ray.margin) - } -} - -impl RayTree { - pub fn new(root: &Node) -> RayTree { - let mut tree = RayTree { - nodes: Vec::new(), - walls: Vec::new(), - magnitude: 0.0, - }; - tree.push(root); - tree - } - - /// Appends `node` and its subtree in preorder; returns its index and the - /// subtree's wall extent. - fn push(&mut self, node: &Node) -> (u32, (f64, f64), (f64, f64)) { - let index = self.nodes.len() as u32; - let first = self.walls.len() as u32; - let (mut lo, mut hi) = ((f64::INFINITY, f64::INFINITY), (f64::NEG_INFINITY, f64::NEG_INFINITY)); - for wall in std::iter::once(&node.partition).chain(&node.segments) { - let (start, end) = (F2::from(wall.start), F2::from(wall.end)); - self.walls.push((start, end - start)); - for p in [start, end] { - let (x, y) = (p.x as f64, p.y as f64); - lo = (lo.0.min(x), lo.1.min(y)); - hi = (hi.0.max(x), hi.1.max(y)); - self.magnitude = self.magnitude.max((x.abs() + y.abs()) as f32 * (1.0 + f32::EPSILON)); - } - } - let own = Bounds::new(lo, hi); - self.nodes.push(RayNode { - first, - last: self.walls.len() as u32, - front: NONE, - back: NONE, - own, - subtree: own, - }); - for (child, front) in [(&node.front, true), (&node.back, false)] { - let Some(child) = child else { continue }; - let (child, child_lo, child_hi) = self.push(child); - lo = (lo.0.min(child_lo.0), lo.1.min(child_lo.1)); - hi = (hi.0.max(child_hi.0), hi.1.max(child_hi.1)); - let node = &mut self.nodes[index as usize]; - if front { - node.front = child - } else { - node.back = child - } - } - self.nodes[index as usize].subtree = Bounds::new(lo, hi); - (index, lo, hi) - } - - /// Flat arrays for GPU queries (gpu/sim/rays.h): per node `[first, last, - /// front, back]` and the own and subtree boxes as `[cx, cy, hx, hy]`; per - /// wall `[start.x, start.y, delta.x, delta.y]`; the coordinate bound; and - /// the tree depth (nodes on the longest root-to-leaf path). - // The tuple matches the existing packed GPU node/wall layout. - #[allow(clippy::type_complexity)] - pub(crate) fn gpu_arrays(&self) -> (Vec<([u32; 4], [f32; 4], [f32; 4])>, Vec<[f32; 4]>, f32, usize) { - let nodes: Vec<_> = self - .nodes - .iter() - .map(|n| { - let b = |b: &Bounds| [b.cx, b.cy, b.hx, b.hy]; - ([n.first, n.last, n.front, n.back], b(&n.own), b(&n.subtree)) - }) - .collect(); - let walls = self.walls.iter().map(|(s, d)| [s.x, s.y, d.x, d.y]).collect(); - fn depth(nodes: &[RayNode], i: u32) -> usize { - if i == NONE { - return 0; - } - let n = &nodes[i as usize]; - 1 + depth(nodes, n.front).max(depth(nodes, n.back)) - } - let d = if self.nodes.is_empty() { - 0 - } else { - depth(&self.nodes, 0) - }; - (nodes, walls, self.magnitude, d) - } - - /// `Node::raycast`. - /// - /// Skipping is exact: a wall is hit only if the float32 wall fraction - /// `N / D` lies in [0, 1], where `N = cross(b, ws - start)` and - /// `D = cross(a, b)`. `N` and `N - D` are `|b|` times the signed distances - /// of the wall's endpoints from the ray's line. With both of one sign and - /// at least `|b| M` in size, either `D` has the opposite sign (fraction - /// below 0) or `|N| - |D| >= |b| M` (fraction above 1). With unit - /// roundoff `u = 2^-24` and coordinates bounded by `C` (walls) and `S` - /// (ray start), float32 errs by under `12u |b| (C + S)` in `N` and - /// `8u |a| |b|` in `D`, and the quotient rounds to 1 only within `2u |D|` - /// of it; `M > 32u (C + S)` therefore rejects every such wall. `beside` - /// errs by under `10u |b| (C + S + margin)`, so `margin` keeps `M` about - /// tenfold above that bound. - pub fn raycast(&self, start: V2, end: V2) -> Option { - let (start, end) = (F2::from(start), F2::from(end)); - let b = end - start; - let scale = self.magnitude + start.x.abs() + start.y.abs(); - // Tiny rays would underflow the bound; they are never skipped. - let margin = if b.x.abs() + b.y.abs() > 1e-10 { - 1e-3 + 2e-5 * scale - } else { - f32::INFINITY - }; - if self.nodes.is_empty() { - return None; - } - self.cast(0, &Ray { start, end, b, margin }).map(V2::from) - } - - fn cast(&self, index: u32, ray: &Ray) -> Option { - let node = &self.nodes[index as usize]; - if node.subtree.beside(ray) { - return None; - } - let (origin, direction) = self.walls[node.first as usize]; - // `classify` - let side = |p: F2| { - let cross = direction.cross(p - origin); - if cross > 0.0001 { - 1 - } else if cross < -0.0001 { - -1 - } else { - 0 - } - }; - let (a, b) = (side(ray.start), side(ray.end)); - let (near, far) = if a == 1 || a == 0 && b == 1 { - (node.front, node.back) - } else { - (node.back, node.front) - }; - if near != NONE { - if let Some(hit) = self.cast(near, ray) { - return Some(hit); - } - } - let mut nearest = None; - if !node.own.beside(ray) { - let mut distance = f32::MAX; - for &(ws, wa) in &self.walls[node.first as usize..node.last as usize] { - if let Some(p) = ray_wall_hit(ray.start, ray.b, ws, wa) { - let d = p - ray.start; - let d2 = d.dot(d); - if d2 < distance { - distance = d2; - nearest = Some(p); - } - } - } - } - nearest.or_else(|| { - if a != b && far != NONE { - self.cast(far, ray) - } else { - None - } - }) - } -} diff --git a/github-simulator/source/src/track/curve.rs b/github-simulator/source/src/track/curve.rs deleted file mode 100644 index cfcd2268dbf840a41b44913b7c9a8ebbae02fafa..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/track/curve.rs +++ /dev/null @@ -1,195 +0,0 @@ -//! Godot 4.3 Curve2D baking and linear sampling, preserving float operation order. -//! Adapted from Godot Engine scene/resources/curve.cpp and core/math/vector2.h. -//! Copyright (c) 2014-present Godot Engine contributors; MIT license. -use crate::math::godot_math::F2; -use crate::math::native_math; -use crate::math::profile::MathProfile; -use serde_json::Value; -#[derive(Clone, Debug)] -pub struct Curve { - pub points: Vec, - pub offsets: Vec, - pub(crate) forwards: Vec, - grid: Option, - math: MathProfile, -} -fn divide(p: F2, n: f32) -> F2 { - F2 { x: p.x / n, y: p.y / n } -} -fn bezier([a, b, c, d]: [F2; 4], t: f32) -> F2 { - let omt = 1.0 - t; - let omt2 = omt * omt; - let omt3 = omt2 * omt; - let t2 = t * t; - let t3 = t2 * t; - a * omt3 + b * omt2 * t * 3.0 + c * omt * t2 * 3.0 + d * t3 -} -fn approximate(a: f32, b: f32) -> bool { - a == b || (a - b).abs() < (1e-5 * a.abs()).max(1e-5) -} -fn tangent([a, b, c, d]: [F2; 4], t: f32) -> F2 { - if (t.abs() < 1e-5 && approximate(a.x, b.x) && approximate(a.y, b.y)) - || ((t - 1.0).abs() < 1e-5 && approximate(c.x, d.x) && approximate(c.y, d.y)) - { - return (d - a).normalized(); - } - let omt = 1.0 - t; - let omt2 = omt * omt; - let t2 = t * t; - ((b - a) * 3.0 * omt2 + (c - b) * 6.0 * omt * t + (d - c) * 3.0 * t2).normalized() -} -fn subdivide(p: [F2; 4], begin: f32, end: f32, depth: u32, interval: f32, out: &mut Vec<(f32, F2)>) { - if (bezier(p, begin) - bezier(p, end)).length() > interval && depth < 10 { - let mid = (begin + end) * 0.5; - subdivide(p, begin, mid, depth + 1, interval, out); - out.push((mid, bezier(p, mid))); - subdivide(p, mid, end, depth + 1, interval, out); - } -} -pub fn json_vector(v: &Value) -> F2 { - if v.is_array() { - F2 { - x: v[0].as_f64().unwrap() as f32, - y: v[1].as_f64().unwrap() as f32, - } - } else { - F2 { - x: v["x"].as_f64().unwrap() as f32, - y: v["y"].as_f64().unwrap() as f32, - } - } -} -impl Curve { - pub fn from_json(data: &Value, math: MathProfile) -> Self { - let controls = data["points"].as_array().unwrap(); - let interval = data["bake_interval"].as_f64().unwrap() as f32; - let mut curve = Self { - points: Vec::new(), - offsets: Vec::new(), - forwards: Vec::new(), - grid: None, - math, - }; - for (i, pair) in controls.windows(2).enumerate() { - let a = json_vector(&pair[0]["position"]); - let d = json_vector(&pair[1]["position"]); - let p = [ - a, - a + json_vector(&pair[0]["outgoing"]), - d + json_vector(&pair[1]["incoming"]), - d, - ]; - if i == 0 { - curve.points.push(a); - curve.forwards.push(tangent(p, 0.0)); - } - let mut mid = Vec::new(); - subdivide(p, 0.0, 1.0, 0, interval, &mut mid); - for (t, pos) in mid { - curve.points.push(pos); - curve.forwards.push(tangent(p, t)); - } - curve.points.push(d); - curve.forwards.push(tangent(p, 1.0)); - } - let mut distance = 0.0; - curve.offsets.push(distance); - for p in curve.points.windows(2) { - distance += (p[0] - p[1]).length(); - curve.offsets.push(distance); - } - curve.grid = crate::track::segment_grid::SegmentGrid::new(&curve.points); - curve - } - pub fn length(&self) -> f32 { - *self.offsets.last().unwrap_or(&0.0) - } - fn interval(&self, offset: f32) -> (usize, f32) { - let offset = offset.clamp(0.0, self.length()); - let (mut start, mut end) = (0, self.points.len()); - let mut index = (start + end) / 2; - while start < index { - if offset <= self.offsets[index] { - end = index; - } else { - start = index; - } - index = (start + end) / 2; - } - let span = self.offsets[index + 1] - self.offsets[index]; - ( - index, - if span < f32::EPSILON { - 0.5 - } else { - (offset - self.offsets[index]) / span - }, - ) - } - pub fn direction(&self, offset: f32) -> F2 { - let (i, t) = self.interval(offset); - let a = self.forwards[i]; - let b = self.forwards[i + 1]; - let a2 = a.dot(a); - let b2 = b.dot(b); - if a2 == 0.0 || b2 == 0.0 { - return (a + (b - a) * t).normalized(); - } - let length = a2.sqrt(); - let result_length = length + (b2.sqrt() - length) * t; - let angle = self.math.atan2(a.cross(b), a.dot(b)) * t; - let (s, c) = native_math::engine_sin_cos(angle); - (F2 { - x: a.x * c - a.y * s, - y: a.x * s + a.y * c, - } * (result_length / length)) - .normalized() - } - pub(crate) fn position(&self, offset: f32) -> F2 { - let (i, t) = self.interval(offset); - self.points[i] + (self.points[i + 1] - self.points[i]) * t - } - fn project(&self, query: F2, i: usize) -> (f32, f32) { - let span = self.offsets[i + 1] - self.offsets[i]; - let origin = self.points[i]; - let direction = divide(self.points[i + 1] - origin, span); - let d = (query - origin).dot(direction).clamp(0.0, span); - let projection = origin + direction * d; - let delta = projection - query; - (self.offsets[i] + d, delta.dot(delta)) - } - pub fn closest_offset(&self, query: F2) -> f32 { - let (mut nearest, mut distance) = (0.0, -1.0); - let mut visit = |i: usize| { - let (offset, dist) = self.project(query, i); - if distance < 0.0 || dist < distance { - nearest = offset; - distance = dist; - } - }; - // Segment 0 is always accepted first (a NaN there sticks), then the exact - // candidate subset in ascending order; see segment_grid. - let count = self.points.len() - 1; - if count > 0 { - if let Some(grid) = &self.grid { - visit(0); - // Revisiting segment 0 in the fallback cannot change the result. - if grid.scan( - query, - |i| self.project(query, i).1, - |i| { - if i > 0 { - visit(i) - } - }, - ) { - return nearest; - } - } - } - for i in 0..count { - visit(i); - } - nearest - } -} diff --git a/github-simulator/source/src/track/mod.rs b/github-simulator/source/src/track/mod.rs deleted file mode 100644 index 33ae2324dd07ff9591fd2f5043157ebad1d9fc3b..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/track/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -//! Scene geometry, acceleration structures, and generated tracks. - -pub mod bsp; -pub mod curve; -mod path_segments; -pub mod random_track; -mod segment_grid; -pub mod training_tracks; -pub mod world; diff --git a/github-simulator/source/src/track/path_segments.rs b/github-simulator/source/src/track/path_segments.rs deleted file mode 100644 index 0ed4ee667ff062ba8739ebff3c801ef99e06a226..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/track/path_segments.rs +++ /dev/null @@ -1,203 +0,0 @@ -//! Baked path segments for `Track::closest_path`, stored as f32 columns. -//! Start, delta and squared length are the values TrackManager's projection -//! computes per query; precomputing them performs the same operations. -use crate::{math::godot_math::F2, math::vec2::V2}; -use std::ops::Range; - -#[derive(Default)] -pub(crate) struct PathSegments { - x: Vec, - y: Vec, - dx: Vec, - dy: Vec, - len2: Vec, -} - -/// One projection: squared distance, delta, clamped fraction and nearest point. -pub(crate) struct Projection { - pub(crate) d2: f32, - pub(crate) delta: F2, - pub(crate) fraction: f32, - pub(crate) near: F2, -} - -impl PathSegments { - pub(crate) fn new(path: &[V2]) -> PathSegments { - let mut segments = PathSegments::default(); - for pair in path.windows(2) { - let start = F2::from(pair[0]); - let delta = F2::from(pair[1]) - start; - segments.x.push(start.x); - segments.y.push(start.y); - segments.dx.push(delta.x); - segments.dy.push(delta.y); - segments.len2.push(delta.dot(delta)); - } - segments - } - - /// `[x, y, dx, dy, len2]` per segment (GPU export). - pub(crate) fn rows(&self) -> Vec<[f32; 5]> { - (0..self.len2.len()) - .map(|i| [self.x[i], self.y[i], self.dx[i], self.dy[i], self.len2[i]]) - .collect() - } - - pub(crate) fn project(&self, point: F2, i: usize) -> Projection { - let start = F2 { - x: self.x[i], - y: self.y[i], - }; - let delta = F2 { - x: self.dx[i], - y: self.dy[i], - }; - let len2 = self.len2[i]; - let fraction = if len2 > 0.0 { - ((point - start).dot(delta) / len2).clamp(0.0, 1.0) - } else { - 0.0 - }; - let near = start + delta * fraction; - let diff = point - near; - Projection { - d2: diff.dot(diff), - delta, - fraction, - near, - } - } - - /// Visits `range` in order: a squared distance below `distance` replaces - /// it and records the segment in `nearest`, so ties keep the first segment. - pub(crate) fn scan(&self, point: F2, range: Range, distance: &mut f32, nearest: &mut Option) { - assert!(range.end <= self.len2.len()); - #[cfg(target_arch = "x86_64")] - if std::is_x86_feature_detected!("avx2") { - unsafe { self.scan_avx2(point, range, distance, nearest) }; - return; - } - self.scan_scalar(point, range, distance, nearest); - } - - fn scan_scalar(&self, point: F2, range: Range, distance: &mut f32, nearest: &mut Option) { - for i in range { - let d2 = self.project(point, i).d2; - if d2 < *distance { - *distance = d2; - *nearest = Some(i); - } - } - } - - /// Eight segments per step with `project`'s operations; lanes are then - /// compared in order, so the selected segment is the scalar one. - #[cfg(target_arch = "x86_64")] - #[target_feature(enable = "avx2")] - unsafe fn scan_avx2(&self, point: F2, range: Range, distance: &mut f32, nearest: &mut Option) { - use std::arch::x86_64::*; - let (px, py) = (_mm256_set1_ps(point.x), _mm256_set1_ps(point.y)); - let (zero, one) = (_mm256_setzero_ps(), _mm256_set1_ps(1.0)); - let mut i = range.start; - while i + 8 <= range.end { - let load = |column: &[f32]| _mm256_loadu_ps(column.as_ptr().add(i)); - let (sx, sy, dx, dy, len2) = ( - load(&self.x), - load(&self.y), - load(&self.dx), - load(&self.dy), - load(&self.len2), - ); - let dot = _mm256_add_ps( - _mm256_mul_ps(_mm256_sub_ps(px, sx), dx), - _mm256_mul_ps(_mm256_sub_ps(py, sy), dy), - ); - let q = _mm256_div_ps(dot, len2); - // f32::clamp: NaN stays NaN. - let q = _mm256_blendv_ps(q, zero, _mm256_cmp_ps::<_CMP_LT_OQ>(q, zero)); - let q = _mm256_blendv_ps(q, one, _mm256_cmp_ps::<_CMP_GT_OQ>(q, one)); - let fraction = _mm256_blendv_ps(zero, q, _mm256_cmp_ps::<_CMP_GT_OQ>(len2, zero)); - let ex = _mm256_sub_ps(px, _mm256_add_ps(sx, _mm256_mul_ps(dx, fraction))); - let ey = _mm256_sub_ps(py, _mm256_add_ps(sy, _mm256_mul_ps(dy, fraction))); - let d2 = _mm256_add_ps(_mm256_mul_ps(ex, ex), _mm256_mul_ps(ey, ey)); - if _mm256_movemask_ps(_mm256_cmp_ps::<_CMP_LT_OQ>(d2, _mm256_set1_ps(*distance))) != 0 { - let mut lanes = [0.0f32; 8]; - _mm256_storeu_ps(lanes.as_mut_ptr(), d2); - for (lane, &d2) in lanes.iter().enumerate() { - if d2 < *distance { - *distance = d2; - *nearest = Some(i + lane); - } - } - } - i += 8; - } - self.scan_scalar(point, i..range.end, distance, nearest); - } -} - -#[cfg(all(test, target_arch = "x86_64"))] -mod tests { - use super::*; - - fn random(state: &mut u64) -> f64 { - *state ^= *state << 13; - *state ^= *state >> 7; - *state ^= *state << 17; - (*state >> 11) as f64 / (1u64 << 53) as f64 - } - - #[test] - fn avx2_scan_matches_scalar_scan() { - if !std::is_x86_feature_detected!("avx2") { - return; - } - let mut state = 0x243F6A8885A308D3u64; - for case in 0..400 { - // Repeated points (zero-length segments), repeated segments (equal - // distances) and a wide range of scales. - let scale = [1e-3, 1.0, 64.0, 5000.0, 3e7][case % 5]; - let mut path: Vec = Vec::new(); - for _ in 0..(case % 97 + 2) { - match (random(&mut state) * 6.0) as u32 { - 0 if !path.is_empty() => path.push(*path.last().unwrap()), - 1 if path.len() >= 2 => { - let n = path.len(); - path.extend([path[n - 2], path[n - 1]]); - } - _ => path.push(V2::new( - (random(&mut state) - 0.5) * scale, - (random(&mut state) - 0.5) * scale, - )), - } - } - if path.len() < 2 { - path.push(path[0]); - } - let segments = PathSegments::new(&path); - let count = path.len() - 1; - for _ in 0..200 { - let point = F2 { - x: ((random(&mut state) - 0.5) * scale * 1.5) as f32, - y: ((random(&mut state) - 0.5) * scale * 1.5) as f32, - }; - let point = if random(&mut state) < 0.1 { - F2::from(path[(random(&mut state) * path.len() as f64) as usize]) - } else { - point - }; - let a = (random(&mut state) * count as f64) as usize; - let b = a + ((random(&mut state) * (count - a + 1) as f64) as usize).min(count - a); - let initial = if random(&mut state) < 0.2 { - (random(&mut state) * scale * scale) as f32 - } else { - f32::MAX - }; - let (mut d1, mut n1, mut d2, mut n2) = (initial, None, initial, None); - segments.scan_scalar(point, a..b, &mut d1, &mut n1); - unsafe { segments.scan_avx2(point, a..b, &mut d2, &mut n2) }; - assert_eq!((d1.to_bits(), n1), (d2.to_bits(), n2), "case {case} range {a}..{b}"); - } - } - } -} diff --git a/github-simulator/source/src/track/random_track.rs b/github-simulator/source/src/track/random_track.rs deleted file mode 100644 index 61e57ee77cb6a87a061d6c1a4d9c48b8db518c1d..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/track/random_track.rs +++ /dev/null @@ -1,894 +0,0 @@ -//! Original TrackFactory path search, surface assignment and block selection. -//! Each generation takes its own .NET Xoshiro state, independent of reproduction. -use crate::math::profile::MathProfile; -use crate::training::game_random::GameRandom; -use crate::{math::godot_math::F2, track::curve::Curve}; -use serde::{Deserialize, Serialize}; -use serde_json::{json, Value}; -use std::{ - collections::{HashMap, HashSet}, - sync::OnceLock, -}; - -type Position = [i32; 2]; -const NEIGHBORS: [Position; 4] = [[0, -1], [0, 1], [-1, 0], [1, 0]]; - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct RandomTrackConfig { - pub length: i32, - pub(crate) allow_double: bool, - pub(crate) start: Option, - pub(crate) start_direction: Option, - pub(crate) surfaces: Option>, - /// Original SurfaceDistribution: Random = 0, Sections = 1. - pub(crate) distribution: usize, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct Connection { - pub side: usize, - pub index: i32, - #[serde(rename = "type")] - pub kind: usize, -} - -#[derive(Clone, Debug, Deserialize)] -pub struct Block { - pub atlas: i32, - pub alternative: i32, - pub(crate) surface: usize, - #[serde(rename = "type")] - pub(crate) kind: usize, - pub connections: Vec, -} - -#[derive(Serialize, Debug, PartialEq, Eq)] -struct Lookup { - surface: usize, - previous: usize, - next: usize, - simple: Vec, - all: Vec, -} - -/// The game's tile set as `data/track_catalog.json` holds it. Only what the -/// simulator reads is kept; block candidates are derived (`TrackGenerator`). -#[derive(Deserialize)] -struct Catalog { - hashcode_seed: u32, - bounds: [i32; 4], - blocks: Vec, -} - -fn catalog() -> &'static Catalog { - static CATALOG: OnceLock = OnceLock::new(); - CATALOG.get_or_init(|| serde_json::from_str(include_str!("../data/track_catalog.json")).unwrap()) -} - -/// .NET 8 HashCode.Combine(int,int), with the process-wide random seed. -/// .NET Foundation, MIT; xxHash32 constants and mixing from Yann Collet. -pub fn combine_hash(seed: u32, a: i32, b: i32) -> i32 { - let mut h = seed.wrapping_add(374761393).wrapping_add(8); - for v in [a, b] { - h = h - .wrapping_add((v as u32).wrapping_mul(3266489917)) - .rotate_left(17) - .wrapping_mul(668265263); - } - h ^= h >> 15; - h = h.wrapping_mul(2246822519); - h ^= h >> 13; - h = h.wrapping_mul(3266489917); - (h ^ (h >> 16)) as i32 -} -pub fn connection_set_hash(seed: u32, connections: &[Connection]) -> i32 { - let mut hashes: Vec = connections - .iter() - .map(|c| combine_hash(seed, c.side as i32, c.index)) - .collect(); - hashes.sort(); // original orders signed ints, including negative hashes - hashes.into_iter().fold(0, |h, c| combine_hash(seed, h, c)) -} - -/// Lookup tables for one original runtime process. Different connection sets -/// with equal integer hashes intentionally share a group in the original game. -pub struct TrackGenerator { - lookups: Vec, -} -impl TrackGenerator { - pub fn new(hashcode_seed: u32) -> Self { - let mut groups: HashMap> = HashMap::new(); - for (i, b) in blocks().iter().enumerate() { - groups - .entry(connection_set_hash(hashcode_seed, &b.connections)) - .or_default() - .push(i); - } - let mut lookups = Vec::new(); - for surface in 0..3 { - for previous in 0..4 { - for next in 0..4 { - if previous == next { - continue; - } - let query = |a, b| { - connection_set_hash( - hashcode_seed, - &[ - Connection { - side: previous, - index: a, - kind: 0, - }, - Connection { - side: next, - index: b, - kind: 0, - }, - ], - ) - }; - let simple = groups - .get(&query(1, 1)) - .into_iter() - .flatten() - .copied() - .filter(|&i| blocks()[i].surface == surface && blocks()[i].kind == 0) - .collect(); - let mut all = Vec::new(); - // Type combinations have identical hashes; repeating each pair 25 - // times cannot add another candidate after the first occurrence. - for a in 0..3 { - for b in 0..3 { - for &i in groups.get(&query(a, b)).into_iter().flatten() { - if blocks()[i].surface == surface && !all.contains(&i) { - all.push(i); - } - } - } - } - lookups.push(Lookup { - surface, - previous, - next, - simple, - all, - }); - } - } - } - Self { lookups } - } - pub fn lookup_json(&self) -> Value { - serde_json::to_value(&self.lookups).unwrap() - } - pub fn generate( - &self, - config: &mut RandomTrackConfig, - state: &mut [u64; 4], - ) -> Result { - generate_with_lookups(config, state, &self.lookups) - } -} - -/// Catalog and candidate order exported from the unchanged original game DLL. -pub(crate) fn default_hashcode_seed() -> u32 { - catalog().hashcode_seed -} - -pub fn blocks() -> &'static [Block] { - &catalog().blocks -} - -/// Tile grid bounds [x, y, width, height] from the original game catalog. -pub(crate) fn bounds() -> [i32; 4] { - catalog().bounds -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct GeneratedTile { - pub position: Position, - pub block: usize, -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct GeneratedTrack { - pub name: String, - pub config: RandomTrackConfig, - pub start: Position, - pub connection: Connection, - /// Dictionary insertion order is the closed path's traversal order. - pub tiles: Vec, -} - -/// The geometry of each block, in `blocks()` order: wall polygons for the -/// rays and convex collision shapes, relative to the tile centre. -#[derive(Deserialize)] -struct TileResource { - polygons: Vec>, - shapes: Vec>, -} -/// Every tile's physics material in the game's tile set. -const TILE_FRICTION: f64 = 1.0; -const TILE_BOUNCE: f64 = 0.0; - -fn resources() -> &'static [TileResource] { - static RESOURCES: OnceLock> = OnceLock::new(); - RESOURCES.get_or_init(|| serde_json::from_str(include_str!("../data/track_resources.json")).unwrap()) -} - -impl Connection { - fn position(self, tile: Position) -> F2 { - let offsets = [128, 384, 640]; - let offset = offsets[self.index as usize]; - let [x, y] = match self.side { - 0 => [offset, 0], - 1 => [offset, 768], - 2 => [0, offset], - 3 => [768, offset], - _ => unreachable!(), - }; - F2 { - x: (x + tile[0] * 768) as f32, - y: (y + tile[1] * 768) as f32, - } - } - fn normal(self) -> F2 { - // The original negates Vector2I before converting to Vector2. - F2 { - x: -NEIGHBORS[self.side][0] as f32, - y: -NEIGHBORS[self.side][1] as f32, - } - } -} - -fn tangent_parameters(base: F2, delta: F2, own: usize, other: usize, math: MathProfile) -> (F2, f32) { - let smoothness = if matches!(own, 0 | 2 | 100) && matches!(other, 1 | 3 | 4) { - 0.2 - } else { - 0.5 - }; - let angle = match own { - 1 => std::f32::consts::PI / 4.0, - 3 => math.atan2(128.0, 384.0), - 4 => math.atan2(384.0, 128.0), - _ => return (base, smoothness), - }; - let cross = base.cross(delta); - let sign = if cross > 0.0 { - 1.0 - } else if cross < 0.0 { - -1.0 - } else { - 0.0 - }; - (base.rotated(sign * angle), smoothness) -} - -fn vector_json(value: F2) -> Value { - json!([value.x as f64, value.y as f64]) -} - -impl GeneratedTrack { - /// Original TrackUtils.GetPathCurve control points, before native baking. - pub fn curve_controls(&self, math: MathProfile) -> Value { - let mut position = self.start; - let mut connection = self.connection; - let mut points = Vec::new(); - loop { - let tile = self - .tiles - .iter() - .find(|tile| tile.position == position) - .expect("connected generated track"); - points.push((connection.position(position), connection.normal(), connection.kind)); - let outgoing = blocks()[tile.block] - .connections - .iter() - .find(|&&c| c != connection) - .unwrap(); - position = add(position, NEIGHBORS[outgoing.side]); - connection = Connection { - side: outgoing.side ^ 1, - ..*outgoing - }; - if position == self.start { - break; - } - assert!( - points.len() < self.tiles.len(), - "generated path must return to its start" - ); - } - let mut controls = Vec::with_capacity(points.len() + 1); - for i in 0..points.len() { - let (position, normal, kind) = points[i]; - let previous = points[(i + points.len() - 1) % points.len()]; - let next = points[(i + 1) % points.len()]; - let incoming_distance = (position - previous.0).length(); - let outgoing_distance = (position - next.0).length(); - let (outgoing, out_smoothness) = tangent_parameters(normal, next.0 - position, kind, next.2, math); - let (incoming, in_smoothness) = tangent_parameters(-normal, previous.0 - position, kind, previous.2, math); - controls.push(json!({ - "position":vector_json(position), - "incoming":vector_json((incoming * incoming_distance) * in_smoothness), - "outgoing":vector_json((outgoing * outgoing_distance) * out_smoothness), - })); - } - controls.push(controls[0].clone()); - json!({"bake_interval":5.0,"points":controls}) - } - - /// Build the complete scene consumed by World using original TileSet resources. - /// Vehicle and space settings come from the supplied scene template. - pub fn to_scene(&self, template: &Value, math: MathProfile) -> Value { - let controls = self.curve_controls(math); - let curve = Curve::from_json(&controls, math); - let mut tiles = Vec::new(); - let mut polygons = Vec::new(); - let mut shapes = Vec::new(); - for tile in &self.tiles { - let block = &blocks()[tile.block]; - let resource = &resources()[tile.block]; - let origin = F2 { - x: (tile.position[0] * 768 + 384) as f32, - y: (tile.position[1] * 768 + 384) as f32, - }; - tiles.push(json!({"coords":tile.position,"surface":(["asphalt","dirt","ice"][block.surface]), - "connections":block.connections.iter().map(|c|vector_json(c.position(tile.position))).collect::>()})); - for polygon in &resource.polygons { - polygons.push( - json!({"points":polygon.iter().map(|p|vector_json(F2{x:p[0],y:p[1]}+origin)).collect::>()}), - ); - } - } - let initial_cells: Vec = - serde_json::from_str(include_str!("../data/tilemap_initial_cells.json")).unwrap(); - let mut cell_order: Vec = initial_cells - .into_iter() - .filter(|p| self.tiles.iter().any(|t| t.position == *p)) - .collect(); - for tile in &self.tiles { - if !cell_order.contains(&tile.position) { - cell_order.push(tile.position); - } - } - // The packed TileMap already contains cells before TrackManager.Init. - // Surviving coordinates keep their resident order. Dirty cells are - // processed last-in-first-out; shape order - // within each cell follows the native polygon decomposition order. - for position in cell_order.iter().rev() { - let tile = self.tiles.iter().find(|t| t.position == *position).unwrap(); - let resource = &resources()[tile.block]; - let origin = F2 { - x: (tile.position[0] * 768 + 384) as f32, - y: (tile.position[1] * 768 + 384) as f32, - }; - for shape in &resource.shapes { - shapes.push( - json!({"tile":tile.position,"origin":vector_json(origin),"local_points":shape, - "points":shape.iter().map(|p|vector_json(F2{x:p[0],y:p[1]}+origin)).collect::>(), - "friction":TILE_FRICTION,"bounce":TILE_BOUNCE,"wall_first":false}), - ); - } - } - let mut walls = Vec::new(); - if let Some(tree) = crate::track::bsp::Node::from_polygons(&polygons) { - tree.collect(&mut walls); - } - let direction = curve.direction(0.0); - let rotation = math.atan2(direction.y, direction.x) + std::f32::consts::PI / 2.0; - let mut scene = template.clone(); - scene["reset_position"] = vector_json(curve.position(0.0)); - scene["reset_rotation"] = json!(rotation as f64); - scene["track"] = json!({"name":self.name,"tile_size":[768,768],"tile_map_position":[0,0], - "native_creation":"tilemap","native_broadphase":true,"raycaster_present":true, - "native_cell_order":cell_order, - "tiles":tiles,"polygons":polygons,"physics_shapes":shapes, - "walls":walls.iter().map(|w|json!([vector_json(w.start.into()),vector_json(w.end.into())])).collect::>(), - "curve":controls,"path":curve.points.iter().copied().map(vector_json).collect::>(), - "path_forward":curve.forwards.iter().copied().map(vector_json).collect::>()}); - scene - } -} - -/// Advances the supplied independent track RNG state, including on failure. -/// A successful direct attempt pins `config.start`; fallback changes a new config. -pub fn generate(config: &mut RandomTrackConfig, state: &mut [u64; 4]) -> Result { - static DEFAULT: OnceLock = OnceLock::new(); - DEFAULT - .get_or_init(|| TrackGenerator::new(default_hashcode_seed())) - .generate(config, state) -} -fn generate_with_lookups( - config: &mut RandomTrackConfig, - state: &mut [u64; 4], - lookups: &[Lookup], -) -> Result { - let mut random = GameRandom::new(*state, 0); - let result = generate_internal(config, &mut random, &mut 0, lookups); - *state = random.decision_state; - result -} - -fn add(a: Position, b: Position) -> Position { - [a[0] + b[0], a[1] + b[1]] -} -fn side(from: Position, to: Position) -> usize { - NEIGHBORS - .iter() - .position(|&d| add(from, d) == to) - .expect("cardinal path neighbor") -} -fn neighbors(position: Position) -> impl Iterator { - NEIGHBORS.into_iter().map(move |delta| add(position, delta)) -} -fn in_bounds(position: Position) -> bool { - let [x, y, width, height] = catalog().bounds; - position[0] >= x && position[1] >= y && position[0] < x + width && position[1] < y + height -} -fn shuffle(items: &mut [T], random: &mut GameRandom) { - for i in (1..items.len()).rev() { - items.swap(i, random.randrange(i + 1)); - } -} - -fn generate_internal( - config: &mut RandomTrackConfig, - random: &mut GameRandom, - attempts: &mut usize, - lookups: &[Lookup], -) -> Result { - for _ in 0..50 { - if *attempts >= 200 { - break; - } - *attempts += 1; - let start = config.start.unwrap_or_else(|| { - let [x, y, width, height] = catalog().bounds; - [ - x + random.randrange(width as usize) as i32, - y + random.randrange(height as usize) as i32, - ] - }); - let mut path = vec![start]; - let mut visited = HashSet::from([start]); - if !build_path(&mut path, &mut visited, start, config.length, random, &mut 0) { - continue; - } - let surfaces = assign_surfaces(&path, config, random); - let Some(selected) = assign_blocks(&path, &surfaces, config.allow_double, random, lookups) else { - continue; - }; - let direction = side(path[0], path[1]); - let connection = first_connection(selected[0], direction); - if !finish_compatible(connection) || !complete(&path, &selected) { - continue; - } - config.start.get_or_insert(start); - config.start_direction.get_or_insert(direction); - return Ok(GeneratedTrack { - name: format!( - "Random ({}, {})", - if config.allow_double { "All" } else { "Simple" }, - config.length - ), - config: config.clone(), - start, - connection, - tiles: path - .into_iter() - .zip(selected) - .map(|(position, block)| GeneratedTile { position, block }) - .collect(), - }); - } - if config.length > 4 && *attempts < 200 { - let mut shorter = config.clone(); - shorter.length -= 2; - return generate_internal(&mut shorter, random, attempts, lookups); - } - Err("Failed to generate a random track") -} - -fn build_path( - path: &mut Vec, - visited: &mut HashSet, - start: Position, - length: i32, - random: &mut GameRandom, - calls: &mut usize, -) -> bool { - *calls += 1; - if *calls > 5000 { - return false; - } - let current = *path.last().unwrap(); - if path.len() as i32 == length { - return (start[0] - current[0]).abs() + (start[1] - current[1]).abs() == 1; - } - let mut next: Vec<_> = neighbors(current) - .filter(|p| in_bounds(*p) && !visited.contains(p)) - .collect(); - shuffle(&mut next, random); - for position in next { - path.push(position); - visited.insert(position); - if build_path(path, visited, start, length, random, calls) { - return true; - } - path.pop(); - visited.remove(&position); - } - false -} - -fn assign_surfaces(path: &[Position], config: &RandomTrackConfig, random: &mut GameRandom) -> Vec { - let surfaces = config - .surfaces - .as_deref() - .filter(|s| !s.is_empty()) - .unwrap_or(&[0, 1, 2]); - if surfaces.len() == 1 || config.distribution == 0 { - // Random.Next(1) still consumes a draw in the original runtime. - return path - .iter() - .map(|_| surfaces[random.randrange(surfaces.len())]) - .collect(); - } - let mut result = Vec::with_capacity(path.len()); - for (i, &surface) in surfaces.iter().enumerate() { - result.extend(std::iter::repeat_n( - surface, - path.len() / surfaces.len() + usize::from(i < path.len() % surfaces.len()), - )); - } - result -} - -fn candidates(lookups: &[Lookup], previous: usize, next: usize, surface: usize, all: bool) -> Vec { - let lookup = lookups - .iter() - .find(|row| row.previous == previous && row.next == next && row.surface == surface); - lookup.map_or_else(Vec::new, |row| if all { row.all.clone() } else { row.simple.clone() }) -} -fn compatible(a: usize, b: usize, touching_side: usize) -> bool { - blocks()[a] - .connections - .iter() - .filter(|c| c.side == touching_side) - .all(|c| blocks()[b].connections.contains(&Connection { side: c.side ^ 1, ..*c })) -} -fn compatible_both(a: usize, b: usize, touching_side: usize) -> bool { - compatible(a, b, touching_side) && compatible(b, a, touching_side ^ 1) -} -fn empty_compatible(block: usize, touching_side: usize) -> bool { - blocks()[block].connections.iter().all(|c| c.side != touching_side) -} -fn empty_neighbors_compatible(position: Position, block: usize, path: &[Position]) -> bool { - neighbors(position) - .filter(|p| in_bounds(*p) && !path.contains(p)) - .all(|p| empty_compatible(block, side(position, p))) -} -fn first_connection(block: usize, side: usize) -> Connection { - blocks()[block] - .connections - .iter() - .find(|c| c.side == side) - .copied() - .unwrap_or(Connection { - side: 0, - index: 0, - kind: 0, - }) -} -fn finish_compatible(connection: Connection) -> bool { - connection.kind == 0 || connection.kind == 100 -} - -fn assign_blocks( - path: &[Position], - surfaces: &[usize], - all: bool, - random: &mut GameRandom, - lookups: &[Lookup], -) -> Option> { - let mut selected = vec![0; path.len()]; - if all { - return assign_all(path, surfaces, &mut selected, 0, random, &mut 0, lookups).then_some(selected); - } - for i in 0..path.len() { - let previous = side(path[i], path[(i + path.len() - 1) % path.len()]); - let next = side(path[i], path[(i + 1) % path.len()]); - let choices = candidates(lookups, previous, next, surfaces[i], false); - if choices.is_empty() { - return None; - } - let block = choices[random.randrange(choices.len())]; - if !empty_neighbors_compatible(path[i], block, path) { - return None; - } - selected[i] = block; - } - (0..path.len()) - .all(|i| { - let next = (i + 1) % path.len(); - compatible_both(selected[i], selected[next], side(path[i], path[next])) - }) - .then_some(selected) -} - -fn assign_all( - path: &[Position], - surfaces: &[usize], - selected: &mut [usize], - index: usize, - random: &mut GameRandom, - calls: &mut usize, - lookups: &[Lookup], -) -> bool { - *calls += 1; - if *calls > 5000 { - return false; - } - let count = path.len(); - if index == count { - return compatible_both(selected[count - 1], selected[0], side(path[count - 1], path[0])); - } - let previous = side(path[index], path[(index + count - 1) % count]); - let next = side(path[index], path[(index + 1) % count]); - let mut choices = candidates(lookups, previous, next, surfaces[index], true); - shuffle(&mut choices, random); - for block in choices { - if *calls > 5000 { - return false; - } - if index > 0 && !compatible_both(selected[index - 1], block, previous ^ 1) { - continue; - } - if index == 0 && !finish_compatible(first_connection(block, next)) { - continue; - } - if index == count - 1 && !compatible_both(block, selected[0], next) { - continue; - } - if !empty_neighbors_compatible(path[index], block, path) { - continue; - } - selected[index] = block; - if assign_all(path, surfaces, selected, index + 1, random, calls, lookups) { - return true; - } - } - false -} - -fn complete(path: &[Position], selected: &[usize]) -> bool { - path.len() >= 4 - && path.iter().enumerate().all(|(i, &position)| { - neighbors(position) - .enumerate() - .all(|(side, neighbor)| match path.iter().position(|p| *p == neighbor) { - Some(j) => compatible(selected[i], selected[j], side), - None => empty_compatible(selected[i], side), - }) - }) -} - -/// The game's track TileMap sits at this global position; physics shapes -/// follow it, while ray walls and the path are absolute. -pub(crate) const GAME_TILE_MAP_POSITION: [i32; 2] = [3, 0]; - -fn saved_coords(value: &Value, what: &str) -> Result { - let text = value.as_str().ok_or_else(|| format!("{what} must be a string"))?; - let values = text - .strip_prefix("Vector2i(") - .and_then(|value| value.strip_suffix(')')) - .map(|inner| { - inner - .split(',') - .map(|v| v.trim().parse::()) - .collect::, _>>() - }) - .and_then(Result::ok) - .filter(|values| values.len() == 2) - .ok_or_else(|| format!("{what} {text:?} is not Vector2i(x, y)"))?; - Ok([values[0], values[1]]) -} - -fn saved_int(value: &Value, what: &str) -> Result { - value.as_i64().ok_or_else(|| format!("{what} must be an integer")) -} - -impl GeneratedTrack { - /// A track the game saved: the JSON inside a gzip `.track` file. Tiles, - /// the start tile and its connection are all a scene needs. The path must - /// close; it is checked here, as `curve_controls` assumes it. - pub(crate) fn from_saved(saved: &Value, name: &str) -> Result { - let entries = saved["Tiles"] - .as_object() - .ok_or("the file has no Tiles; it is not a saved track")?; - if entries.is_empty() { - return Err("the track has no tiles".into()); - } - let mut tiles = Vec::with_capacity(entries.len()); - for (key, tile) in entries { - let position = saved_coords(&Value::String(key.clone()), "tile position")?; - let id = &tile["Block"]["TileSetId"]; - let atlas = saved_int(&id["AtlasId"], "AtlasId")?; - let alternative = saved_int(&id["AlternativeId"], "AlternativeId")?; - let block = blocks() - .iter() - .position(|b| b.atlas as i64 == atlas && b.alternative as i64 == alternative) - .ok_or_else(|| { - format!("tile {key} uses an unknown block (atlas {atlas}, alternative {alternative})") - })?; - tiles.push(GeneratedTile { position, block }); - } - let start = saved_coords(&saved["StartTileCoords"], "StartTileCoords")?; - let c = &saved["StartTileConnection"]; - let side = saved_int(&c["Side"], "StartTileConnection.Side")?; - let index = saved_int(&c["Index"], "StartTileConnection.Index")?; - let kind = saved_int(&c["Type"], "StartTileConnection.Type")?; - if !(0..4).contains(&side) || !(0..3).contains(&index) || kind < 0 { - return Err("the start connection is out of range".into()); - } - let connection = Connection { - side: side as usize, - index: index as i32, - kind: kind as usize, - }; - // Walk the path as curve_controls does. - let (mut position, mut incoming) = (start, connection); - for _ in 0..tiles.len() { - let tile = tiles - .iter() - .find(|t| t.position == position) - .ok_or_else(|| format!("the path leaves the track at tile ({}, {})", position[0], position[1]))?; - let connections = &blocks()[tile.block].connections; - if !connections.contains(&incoming) { - return Err(format!("the path is broken at tile ({}, {})", position[0], position[1])); - } - let outgoing = connections - .iter() - .find(|&&c| c != incoming) - .ok_or_else(|| format!("tile ({}, {}) has no exit", position[0], position[1]))?; - position = add(position, NEIGHBORS[outgoing.side]); - incoming = Connection { - side: outgoing.side ^ 1, - ..*outgoing - }; - if position == start { - return Ok(GeneratedTrack { - name: name.to_owned(), - config: RandomTrackConfig { - length: 0, - allow_double: true, - start: None, - start_direction: None, - surfaces: None, - distribution: 0, - }, - start, - connection, - tiles, - }); - } - } - Err("the track's path does not return to its start".into()) - } -} - -/// Moves a scene's TileMap to `position`, as the game places it. Explicit -/// shape origins move with it. -pub fn place_tile_map(scene: &mut Value, position: [i32; 2]) { - let track = &mut scene["track"]; - let previous = crate::track::world::vector(&track["tile_map_position"]); - track["tile_map_position"] = json!(position); - if let Some(shapes) = track["physics_shapes"].as_array_mut() { - for shape in shapes { - if let Some(origin) = shape.get("origin").map(crate::track::world::vector) { - shape["origin"] = json!([ - origin.x - previous.x + position[0] as f64, - origin.y - previous.y + position[1] as f64 - ]); - } - } - } -} - -/// A saved game track as a training scene for independent cars, using the -/// vehicle and physics of `template`. -pub fn saved_track_scene(saved: &Value, name: &str, template: &Value, math: MathProfile) -> Result { - let mut scene = GeneratedTrack::from_saved(saved, name)?.to_scene(template, math); - place_tile_map(&mut scene, GAME_TILE_MAP_POSITION); - scene["track"]["native_broadphase"] = Value::Bool(false); - Ok(scene) -} - -#[cfg(test)] -mod saved_tests { - use super::*; - - /// A generated track in the game's saved `.track` layout. - fn saved(track: &GeneratedTrack) -> Value { - let tiles: serde_json::Map = track - .tiles - .iter() - .map(|t| { - let key = format!("Vector2i({}, {})", t.position[0], t.position[1]); - let b = &blocks()[t.block]; - ( - key.clone(), - json!({"Block":{"TileSetId":{"AtlasId":b.atlas,"AlternativeId":b.alternative}},"Coords":key}), - ) - }) - .collect(); - json!({"Tiles":tiles,"StartTileCoords":format!("Vector2i({}, {})", track.start[0], track.start[1]), - "StartTileConnection":{"Side":track.connection.side,"Index":track.connection.index,"Type":track.connection.kind}}) - } - - /// Prints a generated track in the saved layout: the web app's test - /// fixture (`cargo test --lib print_saved_fixture -- --ignored --nocapture`). - #[test] - #[ignore] - fn print_saved_fixture() { - let mut config = RandomTrackConfig { - length: 12, - allow_double: false, - start: None, - start_direction: None, - surfaces: Some(vec![0, 1]), - distribution: 1, - }; - let track = generate(&mut config, &mut [7, 3, 5, 11]).unwrap(); - println!("{}", saved(&track)); - } - - #[test] - fn saved_tracks_round_trip_and_reject_open_paths() { - let mut config = RandomTrackConfig { - length: 16, - allow_double: true, - start: None, - start_direction: None, - surfaces: Some(vec![0, 1]), - distribution: 1, - }; - let track = generate(&mut config, &mut [1, 2, 3, 4]).unwrap(); - let file = saved(&track); - let parsed = GeneratedTrack::from_saved(&file, "test").unwrap(); - assert_eq!( - (parsed.start, parsed.connection, &parsed.tiles), - (track.start, track.connection, &track.tiles) - ); - let scene = saved_track_scene(&file, "test", &json!({"vehicle":{},"physics":{}}), MathProfile::Proton).unwrap(); - assert_eq!(scene["track"]["tile_map_position"], json!(GAME_TILE_MAP_POSITION)); - assert_eq!(scene["track"]["native_broadphase"], json!(false)); - let shape = &scene["track"]["physics_shapes"][0]; - let tile = shape["tile"].as_array().unwrap(); - assert_eq!( - shape["origin"], - json!([ - tile[0].as_f64().unwrap() * 768.0 + 387.0, - tile[1].as_f64().unwrap() * 768.0 + 384.0 - ]) - ); - - let mut open = file.clone(); - let removed = format!( - "Vector2i({}, {})", - track.tiles[3].position[0], track.tiles[3].position[1] - ); - open["Tiles"].as_object_mut().unwrap().remove(&removed); - assert!(GeneratedTrack::from_saved(&open, "open") - .unwrap_err() - .contains("leaves the track")); - let mut unknown = file.clone(); - unknown["Tiles"][&removed]["Block"]["TileSetId"]["AtlasId"] = json!(999); - assert!(GeneratedTrack::from_saved(&unknown, "unknown") - .unwrap_err() - .contains("unknown block")); - assert!(GeneratedTrack::from_saved(&json!({"Id": 1}), "other").is_err()); - } -} diff --git a/github-simulator/source/src/track/segment_grid.rs b/github-simulator/source/src/track/segment_grid.rs deleted file mode 100644 index 1978a799cd2bee207f6ab743cf6b8193933998d2..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/track/segment_grid.rs +++ /dev/null @@ -1,176 +0,0 @@ -//! Exact pruning for the game's linear nearest-segment scans. -//! -//! Godot's `Curve2D.get_closest_offset` and the track's path projection test -//! every baked segment. `SegmentGrid::candidates` returns, in ascending index -//! order, a superset of the segments whose float32 squared distance can be at -//! most the nearest one's. Callers rerun their original loop body on that -//! subset, so arithmetic, strict-less tie-breaking and NaN handling are -//! unchanged: every excluded segment is provably farther than an evaluated one. -use crate::math::godot_math::F2; -use std::cell::RefCell; - -const CELL: f64 = 64.0; -const MAX_CELLS: usize = 1 << 22; - -#[derive(Clone, Debug, Default)] -pub(crate) struct SegmentGrid { - lo: (f64, f64), - cell: f64, - width: usize, - height: usize, - /// CSR offsets into `items`, one row per cell. - start: Vec, - items: Vec, - boxes: Vec<[f64; 4]>, - /// Absolute slack for float32 rounding of projected points. - slack: f64, -} - -thread_local! { - static SCRATCH: RefCell> = const { RefCell::new(Vec::new()) }; -} - -impl SegmentGrid { - /// Segment `i` joins `points[i]` and `points[i + 1]`. `None` if any point - /// is not finite; callers then keep the linear scan. - pub(crate) fn new(points: &[F2]) -> Option { - if points.len() < 2 || points.iter().any(|p| !p.x.is_finite() || !p.y.is_finite()) { - return None; - } - let boxes: Vec<[f64; 4]> = points - .windows(2) - .map(|p| { - let (a, b) = ((p[0].x as f64, p[0].y as f64), (p[1].x as f64, p[1].y as f64)); - [a.0.min(b.0), a.1.min(b.1), a.0.max(b.0), a.1.max(b.1)] - }) - .collect(); - let lo = boxes - .iter() - .fold((f64::INFINITY, f64::INFINITY), |l, b| (l.0.min(b[0]), l.1.min(b[1]))); - let hi = boxes.iter().fold((f64::NEG_INFINITY, f64::NEG_INFINITY), |h, b| { - (h.0.max(b[2]), h.1.max(b[3])) - }); - let magnitude = [lo.0, lo.1, hi.0, hi.1].iter().fold(0.0f64, |m, v| m.max(v.abs())); - let mut cell = CELL; - let dims = |cell: f64| (((hi.0 - lo.0) / cell) as usize + 1, ((hi.1 - lo.1) / cell) as usize + 1); - while dims(cell).0 * dims(cell).1 > MAX_CELLS { - cell *= 2.0; - } - let (width, height) = dims(cell); - let span = |b: &[f64; 4]| { - let c = |v: f64, l: f64, n: usize| (((v - l) / cell) as usize).min(n - 1); - ( - c(b[0], lo.0, width), - c(b[1], lo.1, height), - c(b[2], lo.0, width), - c(b[3], lo.1, height), - ) - }; - let mut counts = vec![0u32; width * height + 1]; - for b in &boxes { - let (x0, y0, x1, y1) = span(b); - for y in y0..=y1 { - for x in x0..=x1 { - counts[y * width + x + 1] += 1; - } - } - } - for i in 1..counts.len() { - counts[i] += counts[i - 1]; - } - let mut fill = counts.clone(); - let mut items = vec![0u32; *counts.last().unwrap() as usize]; - for (i, b) in boxes.iter().enumerate() { - let (x0, y0, x1, y1) = span(b); - for y in y0..=y1 { - for x in x0..=x1 { - let slot = &mut fill[y * width + x]; - items[*slot as usize] = i as u32; - *slot += 1; - } - } - } - Some(SegmentGrid { - lo, - cell, - width, - height, - start: counts, - items, - boxes, - slack: 1.0 + magnitude * 1e-4, - }) - } - - fn cell_items(&self, x: usize, y: usize) -> &[u32] { - let c = y * self.width + x; - &self.items[self.start[c] as usize..self.start[c + 1] as usize] - } - - /// Calls `visit` with the ascending candidate indices. `distance(i)` must be - /// the caller's exact squared float32 distance to segment `i`. Returns - /// `false` without visiting when no finite bound exists (use the full scan). - pub(crate) fn scan(&self, query: F2, mut distance: impl FnMut(usize) -> f32, mut visit: impl FnMut(usize)) -> bool { - let (qx, qy) = (query.x as f64, query.y as f64); - if !qx.is_finite() || !qy.is_finite() { - return false; - } - let clamp = |v: f64, l: f64, n: usize| (((v - l) / self.cell).max(0.0) as usize).min(n - 1); - let (cx, cy) = (clamp(qx, self.lo.0, self.width), clamp(qy, self.lo.1, self.height)); - // Any evaluated finite distance bounds the nearest one from above. - let mut bound = f32::INFINITY; - let rings = self.width.max(self.height); - for r in 0..=rings { - let (x0, x1) = (cx.saturating_sub(r), (cx + r).min(self.width - 1)); - let (y0, y1) = (cy.saturating_sub(r), (cy + r).min(self.height - 1)); - for y in y0..=y1 { - for x in x0..=x1 { - if x.abs_diff(cx) != r && y.abs_diff(cy) != r { - continue; - } - for &i in self.cell_items(x, y) { - let d = distance(i as usize); - if d < bound { - bound = d; - } - } - } - } - if bound.is_finite() { - break; - } - } - if !bound.is_finite() { - return false; - } - let radius = (bound as f64).sqrt() * (1.0 + 1e-4) + self.slack; - let (x0, x1) = ( - clamp(qx - radius, self.lo.0, self.width), - clamp(qx + radius, self.lo.0, self.width), - ); - let (y0, y1) = ( - clamp(qy - radius, self.lo.1, self.height), - clamp(qy + radius, self.lo.1, self.height), - ); - SCRATCH.with(|scratch| { - let mut candidates = scratch.borrow_mut(); - candidates.clear(); - for y in y0..=y1 { - for x in x0..=x1 { - for &i in self.cell_items(x, y) { - let b = &self.boxes[i as usize]; - let dx = (b[0] - qx).max(qx - b[2]).max(0.0); - let dy = (b[1] - qy).max(qy - b[3]).max(0.0); - if dx * dx + dy * dy <= radius * radius { - candidates.push(i); - } - } - } - } - candidates.sort_unstable(); - candidates.dedup(); - candidates.iter().for_each(|&i| visit(i as usize)); - }); - true - } -} diff --git a/github-simulator/source/src/track/training_tracks.rs b/github-simulator/source/src/track/training_tracks.rs deleted file mode 100644 index 6af6270688bbfb25c641202a61af8cc0320edf92..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/track/training_tracks.rs +++ /dev/null @@ -1,436 +0,0 @@ -//! CPU generation and bounded prefetch of tracks for independent training cars. -use crate::math::profile::MathProfile; -#[cfg(not(target_arch = "wasm32"))] -use crate::math::vec2::V2; -use crate::track::random_track::{self, GeneratedTrack, RandomTrackConfig, TrackGenerator}; -#[cfg(not(target_arch = "wasm32"))] -use crate::track::world::World; -use crate::training::game_random::GameRandom; -use serde::{Deserialize, Serialize}; -use serde_json::Value; -#[cfg(not(target_arch = "wasm32"))] -use std::sync::{mpsc, Arc}; -#[cfg(not(target_arch = "wasm32"))] -use std::thread::{self, JoinHandle}; - -/// A fixed setting or uniformly sampled choices. -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(untagged)] -pub(crate) enum TrackChoice { - Fixed(T), - Choices(Vec), -} - -impl TrackChoice { - fn values(&self) -> &[T] { - match self { - Self::Fixed(value) => std::slice::from_ref(value), - Self::Choices(values) => values, - } - } - - fn sample(&self, rng: &mut GameRandom) -> T { - let values = self.values(); - values[rng.randrange(values.len())].clone() - } -} - -/// Tile count. Closed paths on the game's cardinal grid require even lengths. -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(untagged)] -pub(crate) enum TrackLength { - Fixed(i32), - Range { min: i32, max: i32 }, -} - -/// Fixed surface sets, explicit alternative sets, or a pool with sampled count. -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(untagged)] -pub(crate) enum TrackSurfaces { - Fixed(Vec), - Choices(Vec>), - Random { - pool: Vec, - count: TrackChoice, - }, -} - -impl TrackSurfaces { - fn sample(&self, rng: &mut GameRandom) -> Vec { - match self { - Self::Fixed(surfaces) => surfaces.clone(), - Self::Choices(sets) => sets[rng.randrange(sets.len())].clone(), - Self::Random { pool, count } => { - let count = count.sample(rng); - let mut surfaces = pool.clone(); - for i in (1..surfaces.len()).rev() { - surfaces.swap(i, rng.randrange(i + 1)); - } - surfaces.truncate(count); - surfaces - } - } - } - - fn validate(&self) -> Result<(), String> { - let valid_set = - |set: &[usize]| set.iter().all(|&s| s <= 2) && (0..set.len()).all(|i| !set[..i].contains(&set[i])); - match self { - Self::Fixed(set) if valid_set(set) => Ok(()), - Self::Choices(sets) if !sets.is_empty() && sets.iter().all(|s| valid_set(s)) => Ok(()), - Self::Random { pool, count } if !pool.is_empty() && valid_set(pool) - && !count.values().is_empty() && count.values().iter().all(|&n| n > 0 && n <= pool.len()) => Ok(()), - _ => Err("surfaces must use distinct IDs 0 (asphalt), 1 (dirt), 2 (ice); random counts must be in 1..=pool size and choices must not be empty".into()), - } - } -} - -impl TrackLength { - fn bounds(&self) -> (i32, i32) { - match *self { - Self::Fixed(length) => (length, length), - Self::Range { min, max } => (min, max), - } - } -} - -/// Training variants of the game's TrackFactory settings. Surface IDs are -/// asphalt = 0, dirt = 1, ice = 2; distribution is random = 0, sections = 1. -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(default, deny_unknown_fields)] -pub struct RandomTrainingTrackSettings { - pub(crate) length: TrackLength, - pub(crate) allow_double: TrackChoice, - pub(crate) surfaces: TrackSurfaces, - pub(crate) distribution: TrackChoice, - pub(crate) start: Option<[i32; 2]>, -} - -impl Default for RandomTrainingTrackSettings { - fn default() -> Self { - Self { - length: TrackLength::Range { min: 12, max: 40 }, - allow_double: TrackChoice::Fixed(true), - surfaces: TrackSurfaces::Random { - pool: vec![0, 1, 2], - count: TrackChoice::Choices(vec![1, 2, 3]), - }, - distribution: TrackChoice::Fixed(1), - start: None, - } - } -} - -impl RandomTrainingTrackSettings { - pub fn validate(&self) -> Result<(), String> { - let (min, max) = self.length.bounds(); - let [x, y, width, height] = random_track::bounds(); - if min < 4 || max > width * height || min > max || min + min % 2 > max { - return Err(format!( - "length must contain an even tile count in 4..={} with min <= max", - width * height - )); - } - if self.allow_double.values().is_empty() || self.distribution.values().is_empty() { - return Err("random track choices must not be empty".into()); - } - self.surfaces.validate()?; - if self.distribution.values().iter().any(|&d| d > 1) { - return Err("distribution must be 0 (random) or 1 (sections)".into()); - } - if self - .start - .is_some_and(|p| p[0] < x || p[0] >= x + width || p[1] < y || p[1] >= y + height) - { - return Err("start must be inside the game's track grid".into()); - } - Ok(()) - } - - fn sample(&self, rng: &mut GameRandom) -> RandomTrackConfig { - let (min, max) = self.length.bounds(); - let first = min + min % 2; - let length = first + 2 * rng.randrange(((max - first) / 2 + 1) as usize) as i32; - RandomTrackConfig { - length, - allow_double: self.allow_double.sample(rng), - start: self.start, - start_direction: None, - surfaces: Some(self.surfaces.sample(rng)), - distribution: self.distribution.sample(rng), - } - } -} - -#[cfg(not(target_arch = "wasm32"))] -pub struct PreparedTrainingTrack { - pub generation: u64, - pub track_index: usize, - pub track: GeneratedTrack, - pub world: Arc, - pub position: V2, - pub rotation: f64, - pub gpu_world: Option, -} - -// A generation owns its RNG state. Prefetch depth and process restarts cannot -// change track selection or consume the population's reproduction RNG. -fn track_state(seed: i64, generation: u64) -> [u64; 4] { - let mut state = (seed as u64) ^ generation.wrapping_mul(0xd1342543de82ef95); - std::array::from_fn(|_| { - state = state.wrapping_add(0x9e3779b97f4a7c15); - let mut z = state; - z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9); - z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb); - z ^ (z >> 31) - }) -} - -/// The track and independent-car scene for `generation` of a run with `seed`. -/// Validate `settings` first. -#[cfg(all(target_arch = "wasm32", feature = "wasm"))] -pub(crate) fn training_scene( - template: &Value, - settings: &RandomTrainingTrackSettings, - generator: Option<&TrackGenerator>, - seed: i64, - generation: u64, - math: MathProfile, -) -> Result<(GeneratedTrack, Value), String> { - training_scene_at(template, settings, generator, seed, generation, 0, math) -} - -/// Track zero preserves the historical seed stream; other slots have independent streams. -pub fn training_scene_at( - template: &Value, - settings: &RandomTrainingTrackSettings, - generator: Option<&TrackGenerator>, - seed: i64, - generation: u64, - track_index: usize, - math: MathProfile, -) -> Result<(GeneratedTrack, Value), String> { - let slot_seed = if track_index == 0 { - seed - } else { - ((seed as u64) ^ (track_index as u64).wrapping_mul(0xa0761d6478bd642f)) as i64 - }; - let mut rng = GameRandom::new(track_state(slot_seed, generation), 0); - // The game may shorten failed paths. Retry rather than violate the user's - // minimum; keep both configuration sampling and generation bounded. - for _ in 0..16 { - let mut config = settings.sample(&mut rng); - let mut state = rng.decision_state; - let result = match generator { - Some(generator) => generator.generate(&mut config, &mut state), - None => random_track::generate(&mut config, &mut state), - }; - rng.decision_state = state; - if let Ok(track) = result { - if track.config.length < settings.length.bounds().0 { - continue; - } - let mut scene = track.to_scene(template, math); - // CLI training uses independent cars on both backends. Native shared - // TileMap redraw replay remains available through TrainingRunner. - scene["track"]["native_broadphase"] = Value::Bool(false); - return Ok((track, scene)); - } - } - Err(format!( - "could not generate track for generation {generation} within the requested settings after 16 attempts" - )) -} - -#[cfg(not(target_arch = "wasm32"))] -fn prepare( - template: &Value, - settings: &RandomTrainingTrackSettings, - generator: Option<&TrackGenerator>, - seed: i64, - generation: u64, - track_index: usize, - math: MathProfile, -) -> Result { - let (track, scene) = training_scene_at(template, settings, generator, seed, generation, track_index, math)?; - let position = crate::track::world::vector(&scene["reset_position"]); - let rotation = scene["reset_rotation"].as_f64().unwrap(); - Ok(PreparedTrainingTrack { - generation, - track_index, - track, - world: Arc::new(World::from_scene_with(&scene, math)), - position, - rotation, - gpu_world: None, - }) -} - -#[cfg(not(target_arch = "wasm32"))] -/// A dedicated CPU producer prepares geometry and spatial queries while the -/// simulator consumes tracks. The queue is bounded and never reuses a track -/// when empty: the consumer waits for the next generation or receives an error. -pub struct TrainingTrackBuffer { - receiver: Option>>, - worker: Option>, -} - -#[cfg(not(target_arch = "wasm32"))] -impl TrainingTrackBuffer { - pub fn new( - template: Value, - settings: RandomTrainingTrackSettings, - seed: i64, - first_generation: u64, - capacity: usize, - prepare_gpu: bool, - math: MathProfile, - ) -> Result { - Self::new_batched( - template, - settings, - seed, - first_generation, - capacity, - prepare_gpu, - 1, - math, - ) - } - - #[allow(clippy::too_many_arguments)] - pub fn new_batched( - template: Value, - settings: RandomTrainingTrackSettings, - seed: i64, - first_generation: u64, - capacity: usize, - prepare_gpu: bool, - tracks_per_generation: usize, - math: MathProfile, - ) -> Result { - settings.validate()?; - if capacity == 0 { - return Err("track buffer size must be positive".into()); - } - if tracks_per_generation == 0 { - return Err("tracks per generation must be positive".into()); - } - let hash_seed = template["runtime"]["hashcode_seed"] - .as_u64() - .map(u32::try_from) - .transpose() - .map_err(|_| "HashCode seed exceeds u32")?; - let (sender, receiver) = mpsc::sync_channel(capacity); - let worker = thread::Builder::new() - .name("training-tracks".into()) - .spawn(move || { - let generator = hash_seed.map(TrackGenerator::new); - for generation in first_generation..u64::MAX { - for track_index in 0..tracks_per_generation { - let result = prepare( - &template, - &settings, - generator.as_ref(), - seed, - generation, - track_index, - math, - ) - .and_then(|mut track| { - if prepare_gpu { - track.gpu_world = Some(crate::gpu::hip::PreparedGpuWorld::new(&track.world)?); - } - Ok(track) - }); - let failed = result.is_err(); - if sender.send(result).is_err() || failed { - return; - } - } - } - }) - .map_err(|e| format!("could not start CPU track producer: {e}"))?; - Ok(Self { - receiver: Some(receiver), - worker: Some(worker), - }) - } - - pub fn next_track(&self) -> Result { - self.receiver - .as_ref() - .unwrap() - .recv() - .map_err(|_| "CPU track producer stopped".to_owned())? - } -} - -#[cfg(not(target_arch = "wasm32"))] -impl Drop for TrainingTrackBuffer { - fn drop(&mut self) { - // Disconnect first to release a producer blocked on a full queue. - self.receiver.take(); - if let Some(worker) = self.worker.take() { - let _ = worker.join(); - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::collections::HashSet; - - #[test] - fn default_sampling_covers_surface_counts_subsets_and_even_lengths() { - let settings = RandomTrainingTrackSettings::default(); - settings.validate().unwrap(); - let mut rng = GameRandom::new(track_state(7, 0), 0); - let mut seen = HashSet::new(); - let mut counts = [0; 3]; - for _ in 0..4096 { - let config = settings.sample(&mut rng); - assert!((12..=40).contains(&config.length) && config.length % 2 == 0); - let mut surfaces = config.surfaces.unwrap(); - counts[surfaces.len() - 1] += 1; - surfaces.sort(); - seen.insert(surfaces); - } - assert_eq!(seen.len(), 7, "all nonempty subsets should occur"); - assert!( - counts.iter().all(|&count| (1100..1600).contains(&count)), - "sample counts uniformly, rather than subsets: {counts:?}" - ); - } - - #[test] - fn settings_reject_invalid_lengths_choices_and_surface_counts() { - for value in [ - serde_json::json!({"length": 5}), - serde_json::json!({"length": {"min": 8, "max": 4}}), - serde_json::json!({"length": {"min": 3, "max": 6}}), - serde_json::json!({"length": 1000000}), - serde_json::json!({"allow_double": []}), - serde_json::json!({"distribution": [2]}), - serde_json::json!({"surfaces": [3]}), - serde_json::json!({"surfaces": [0, 0]}), - serde_json::json!({"surfaces": {"pool": [0, 1], "count": [1, 3]}}), - serde_json::json!({"surfaces": {"pool": [0, 1], "count": []}}), - serde_json::json!({"start": [-1000, 0]}), - ] { - let settings: RandomTrainingTrackSettings = serde_json::from_value(value.clone()).unwrap(); - assert!(settings.validate().is_err(), "accepted {value}"); - } - let settings: RandomTrainingTrackSettings = serde_json::from_value(serde_json::json!({ - "length": {"min": 5, "max": 9}, "surfaces": [[0], [1, 2]], "allow_double": [false, true], "distribution": [0, 1] - })).unwrap(); - settings.validate().unwrap(); - let mut rng = GameRandom::new(track_state(9, 0), 0); - for _ in 0..100 { - let config = settings.sample(&mut rng); - assert!([6, 8].contains(&config.length)); - assert!([vec![0], vec![1, 2]].contains(&config.surfaces.unwrap())); - } - } -} diff --git a/github-simulator/source/src/track/world.rs b/github-simulator/source/src/track/world.rs deleted file mode 100644 index 36e6e8a0bc15d3e59cacfd0406a30cc173dfe820..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/track/world.rs +++ /dev/null @@ -1,1168 +0,0 @@ -//! Scene loading and static track queries. -//! -//! A `World` is built once per map and shared by every simulated car. All -//! spatial acceleration structures are conservative: a grid only narrows the -//! candidate set, and the final choice is made with the same arithmetic and -//! tie-breaking as the Python linear scans. Whenever a grid cannot prove its -//! answer, the query falls back to the exact linear scan. - -use crate::math::godot_math::{ray_intersection, F2}; -use crate::math::profile::MathProfile; -use crate::math::pymath::{clamp, f32r, floor_i64}; -use crate::math::vec2::{closest_point, V2}; -use rayon::prelude::*; -use serde_json::Value; -use std::collections::HashMap; -use std::hash::{BuildHasherDefault, Hasher}; - -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct Surface { - pub(crate) grip: f64, - pub(crate) power: f64, - pub(crate) steering: f64, -} - -pub(crate) const ASPHALT: Surface = Surface { - grip: 1.0, - power: 1.0, - steering: 1.0, -}; - -/// FNV-1a for the few short, trusted surface names looked up for every wheel -/// on every tick; SipHash dominated that lookup. -#[derive(Clone, Copy, Debug)] -pub(crate) struct NameHasher(u64); - -impl Default for NameHasher { - fn default() -> Self { - NameHasher(0xcbf2_9ce4_8422_2325) - } -} - -impl Hasher for NameHasher { - fn finish(&self) -> u64 { - self.0 - } - fn write(&mut self, bytes: &[u8]) { - for &byte in bytes { - self.0 = (self.0 ^ byte as u64).wrapping_mul(0x0000_0100_0000_01b3); - } - } -} - -pub(crate) type SurfaceMap = HashMap>; - -#[derive(Clone, Debug)] -pub(crate) struct WheelConfig { - pub(crate) position: V2, - pub(crate) steering: bool, - pub(crate) max_angle_deg: f64, - pub(crate) power: f64, - pub(crate) brake_power: f64, - pub(crate) handbrake_power: f64, -} - -#[derive(Clone, Debug)] -pub struct VehicleConfig { - pub(crate) wheels: Vec, - pub(crate) mass: f64, - pub(crate) inertia: f64, - pub(crate) half_width: f64, - pub(crate) half_length: f64, - pub(crate) shape_size: V2, - pub(crate) shape_basis: (F2, F2), - pub(crate) shape_position: V2, - pub(crate) center_of_mass: V2, - pub(crate) friction: f64, - pub(crate) solver_iterations: usize, - pub(crate) max_contacts_reported: usize, - pub(crate) contact_bias: f64, - pub(crate) allowed_penetration: f64, - pub(crate) contact_recycle_radius: f64, - pub(crate) contact_max_separation: f64, - pub(crate) grip: f64, - pub(crate) steering_speed: f64, - pub(crate) center_steering: bool, - pub(crate) air_resistance: f64, - pub(crate) max_velocity: f64, - pub(crate) custom_integrator: bool, - pub(crate) gravity: V2, - pub(crate) linear_damp: f64, - pub(crate) angular_damp: f64, - pub(crate) bounce: f64, - pub(crate) surfaces: SurfaceMap, -} - -impl VehicleConfig { - /// `config.surfaces.get(name, ASPHALT)`. - pub(crate) fn surface(&self, name: &str) -> Surface { - self.surfaces.get(name).copied().unwrap_or(ASPHALT) - } -} - -/// A static convex tile collider with everything that does not depend on the car. -#[derive(Clone, Debug)] -pub struct PhysicsShape { - pub(crate) tile: Option<(i64, i64)>, - pub(crate) friction: f64, - pub(crate) bounce: f64, - /// Explicit pair ordering for a scene with known BVH allocation order. - /// Legacy exports omit this hidden native state; retain car-first behavior. - pub(crate) wall_first: bool, - pub(crate) points: Vec, - /// `_f32(origin)`. - pub(crate) origin_f32: V2, - /// `_f32(p - origin_f32)` for each point. - pub(crate) local: Vec, - /// `_normalize_f32((edge.y, -edge.x))` of each local edge. - pub(crate) local_normals: Vec, - pub(crate) min: V2, - pub(crate) max: V2, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct Wall { - pub start: V2, - pub end: V2, -} - -/// Uniform grid over which a list of item indices is stored per cell. -#[derive(Clone, Debug)] -struct CellLists { - origin: V2, - cell: f64, - nx: usize, - ny: usize, - starts: Vec, - items: Vec, -} - -impl CellLists { - fn from_lists(origin: V2, cell: f64, nx: usize, ny: usize, lists: Vec>) -> CellLists { - let mut starts = Vec::with_capacity(lists.len() + 1); - let mut items = Vec::new(); - starts.push(0); - for list in lists { - items.extend_from_slice(&list); - starts.push(items.len() as u32); - } - CellLists { - origin, - cell, - nx, - ny, - starts, - items, - } - } - - #[inline(always)] - fn cell_index(&self, point: V2) -> Option<(usize, usize)> { - let fx = ((point.x - self.origin.x) / self.cell).floor(); - let fy = ((point.y - self.origin.y) / self.cell).floor(); - if fx >= 0.0 && fy >= 0.0 && fx < self.nx as f64 && fy < self.ny as f64 { - Some((fx as usize, fy as usize)) - } else { - None - } - } - - #[inline(always)] - fn list(&self, ix: usize, iy: usize) -> &[u32] { - let cell = iy * self.nx + ix; - &self.items[self.starts[cell] as usize..self.starts[cell + 1] as usize] - } - - /// Cell range covering an axis-aligned box, or `None` if it leaves the grid. - #[inline(always)] - fn range(&self, min: V2, max: V2) -> Option<(usize, usize, usize, usize)> { - let x0 = ((min.x - self.origin.x) / self.cell).floor(); - let y0 = ((min.y - self.origin.y) / self.cell).floor(); - let x1 = ((max.x - self.origin.x) / self.cell).floor(); - let y1 = ((max.y - self.origin.y) / self.cell).floor(); - if x0 < 0.0 || y0 < 0.0 || x1 >= self.nx as f64 || y1 >= self.ny as f64 { - return None; - } - Some((x0 as usize, y0 as usize, x1 as usize, y1 as usize)) - } -} - -/// Candidate lists for nearest-segment queries. -/// -/// For cell `C`, `bound = min_s max_{p in C} dist(p, s)` (attained at a -/// corner because distance to a segment is convex). The list holds every -/// segment whose distance to `C` is at most `bound` plus a tolerance, in index -/// order. A best candidate within `bound` is therefore the global minimum, and -/// segments outside the list are strictly farther. -#[derive(Clone, Debug)] -struct NearestGrid { - lists: CellLists, - bound_sq: Vec, -} - -fn point_segment_distance(p: V2, a: V2, b: V2) -> f64 { - let d = b - a; - let len_sq = d.x * d.x + d.y * d.y; - let t = if len_sq > 0.0 { - (((p.x - a.x) * d.x + (p.y - a.y) * d.y) / len_sq).clamp(0.0, 1.0) - } else { - 0.0 - }; - let (qx, qy) = (a.x + d.x * t - p.x, a.y + d.y * t - p.y); - (qx * qx + qy * qy).sqrt() -} - -fn point_box_distance(p: V2, min: V2, max: V2) -> f64 { - let dx = (min.x - p.x).max(0.0).max(p.x - max.x); - let dy = (min.y - p.y).max(0.0).max(p.y - max.y); - (dx * dx + dy * dy).sqrt() -} - -/// Liang–Barsky: does the segment touch the closed box? -fn segment_touches_box(a: V2, b: V2, min: V2, max: V2) -> bool { - let d = b - a; - let (mut t0, mut t1) = (0.0f64, 1.0f64); - for (p, q) in [ - (-d.x, a.x - min.x), - (d.x, max.x - a.x), - (-d.y, a.y - min.y), - (d.y, max.y - a.y), - ] { - if p == 0.0 { - if q < 0.0 { - return false; - } - } else { - let r = q / p; - if p < 0.0 { - t0 = t0.max(r); - } else { - t1 = t1.min(r); - } - if t0 > t1 { - return false; - } - } - } - true -} - -fn box_segment_distance(min: V2, max: V2, a: V2, b: V2) -> f64 { - if segment_touches_box(a, b, min, max) { - return 0.0; - } - let corners = [min, V2::new(max.x, min.y), max, V2::new(min.x, max.y)]; - let mut best = point_box_distance(a, min, max).min(point_box_distance(b, min, max)); - for c in corners { - best = best.min(point_segment_distance(c, a, b)); - } - best -} - -impl NearestGrid { - fn build(segments: &[(V2, V2)], min: V2, max: V2, cell: f64) -> NearestGrid { - let nx = (((max.x - min.x) / cell).ceil() as usize).max(1); - let ny = (((max.y - min.y) / cell).ceil() as usize).max(1); - let half_diag = cell * std::f64::consts::SQRT_2 * 0.5; - let cells: Vec<(Vec, f64)> = (0..nx * ny) - .into_par_iter() - .map(|index| { - let (ix, iy) = (index % nx, index / nx); - let lo = V2::new(min.x + ix as f64 * cell, min.y + iy as f64 * cell); - let hi = V2::new(lo.x + cell, lo.y + cell); - let center = V2::new(lo.x + cell * 0.5, lo.y + cell * 0.5); - let center_dist: Vec = segments - .iter() - .map(|&(a, b)| point_segment_distance(center, a, b)) - .collect(); - let nearest_center = center_dist.iter().copied().fold(f64::INFINITY, f64::min); - // max over corners >= distance from the center, so only these can set the bound. - let corners = [lo, V2::new(hi.x, lo.y), hi, V2::new(lo.x, hi.y)]; - let mut bound = f64::INFINITY; - for (i, &(a, b)) in segments.iter().enumerate() { - if center_dist[i] <= nearest_center + half_diag + 1e-9 { - let far = corners - .iter() - .map(|&c| point_segment_distance(c, a, b)) - .fold(0.0, f64::max); - bound = bound.min(far); - } - } - let tolerance = 1e-6 * (1.0 + bound) + 1e-3; - let mut list = Vec::new(); - for (i, &(a, b)) in segments.iter().enumerate() { - // box distance >= center distance - half diagonal. - if center_dist[i] - half_diag <= bound + tolerance - && box_segment_distance(lo, hi, a, b) <= bound + tolerance - { - list.push(i as u32); - } - } - (list, bound * bound) - }) - .collect(); - let (lists, bound_sq): (Vec>, Vec) = cells.into_iter().unzip(); - NearestGrid { - lists: CellLists::from_lists(min, cell, nx, ny, lists), - bound_sq, - } - } - - #[inline(always)] - fn query(&self, point: V2) -> Option<(&[u32], f64)> { - let (ix, iy) = self.lists.cell_index(point)?; - Some((self.lists.list(ix, iy), self.bound_sq[iy * self.lists.nx + ix])) - } -} - -/// Items registered in every cell that their (padded) bounding box touches. -#[derive(Clone, Debug)] -struct BoxGrid { - lists: CellLists, -} - -impl BoxGrid { - fn build(boxes: &[(V2, V2)], min: V2, max: V2, cell: f64, pad: f64) -> BoxGrid { - let nx = (((max.x - min.x) / cell).ceil() as usize).max(1); - let ny = (((max.y - min.y) / cell).ceil() as usize).max(1); - let mut lists = vec![Vec::new(); nx * ny]; - for (i, &(lo, hi)) in boxes.iter().enumerate() { - let x0 = (((lo.x - pad - min.x) / cell).floor().max(0.0) as usize).min(nx - 1); - let y0 = (((lo.y - pad - min.y) / cell).floor().max(0.0) as usize).min(ny - 1); - let x1 = (((hi.x + pad - min.x) / cell).floor().max(0.0) as usize).min(nx - 1); - let y1 = (((hi.y + pad - min.y) / cell).floor().max(0.0) as usize).min(ny - 1); - for iy in y0..=y1 { - for ix in x0..=x1 { - lists[iy * nx + ix].push(i as u32); - } - } - } - BoxGrid { - lists: CellLists::from_lists(min, cell, nx, ny, lists), - } - } -} - -/// Dense lookup keyed by `(floor(x / 768), floor(y / 768))`. -#[derive(Clone, Debug)] -struct TileTable { - x0: i64, - y0: i64, - nx: i64, - ny: i64, - cells: Vec>, -} - -impl TileTable { - fn new(entries: &[((i64, i64), T)]) -> TileTable { - if entries.is_empty() { - return TileTable { - x0: 0, - y0: 0, - nx: 0, - ny: 0, - cells: Vec::new(), - }; - } - let x0 = entries.iter().map(|e| e.0 .0).min().unwrap(); - let y0 = entries.iter().map(|e| e.0 .1).min().unwrap(); - let nx = entries.iter().map(|e| e.0 .0).max().unwrap() - x0 + 1; - let ny = entries.iter().map(|e| e.0 .1).max().unwrap() - y0 + 1; - let mut cells = vec![None; (nx * ny) as usize]; - for ((x, y), value) in entries { - // Python dict construction: later tiles with the same key win. - cells[((y - y0) * nx + (x - x0)) as usize] = Some(value.clone()); - } - TileTable { x0, y0, nx, ny, cells } - } - - #[inline(always)] - fn get(&self, point: V2) -> Option<&T> { - let x = floor_i64(point.x / 768.0) - self.x0; - let y = floor_i64(point.y / 768.0) - self.y0; - if x < 0 || y < 0 || x >= self.nx || y >= self.ny { - return None; - } - self.cells[(y * self.nx + x) as usize].as_ref() - } -} - -pub struct Track { - /// Fresh native tree with an explicit shape construction order. - /// Omitted by legacy geometry exports, which lack native construction state. - pub native_broadphase: bool, - pub(crate) native_body_order_reverse: bool, - pub(crate) native_tilemap: bool, - pub(crate) native_cell_order: Vec<(i64, i64)>, - pub(crate) native_tile_order: Vec<(i64, i64)>, - pub bsp: Option>, - /// `bsp` arranged for raycasts. - bsp_rays: Option, - // Legacy exports omit whether an empty raycaster was constructed. - raycaster_present: Option, - pub walls: Vec, - pub shapes: Vec, - pub path: Vec, - pub(crate) path_forward: Vec, - pub curve: Option, - pub path_offsets: Vec, - path_grid: Option, - path_segments: crate::track::path_segments::PathSegments, - /// `atan2(a.cross(b), a.dot(b))` between consecutive exported tangents. - forward_angles: Vec, - surfaces: TileTable, - surface_names: TileTable, - default_surface: Surface, - connections: TileTable>, - wall_grid: Option, - ray_grid: Option, - shape_grid: Option, -} - -/// Everything a simulation needs that is shared and immutable. -pub struct World { - pub vehicle: VehicleConfig, - pub track: Track, - /// The C runtime math of the game being reproduced. - pub math: MathProfile, -} - -pub const PATH_WINDOW: f64 = 450.0; -const RAY_CELL: f64 = 64.0; -const RAY_PAD: f64 = 1e-3; - -#[derive(Clone, Copy, Debug)] -pub struct PathHit { - pub offset: f64, - pub near: V2, - pub tangent: V2, -} - -impl Track { - pub fn path_length(&self) -> f64 { - *self.path_offsets.last().unwrap_or(&0.0) - } - - /// The raycast arrangement of `bsp`, if any (used by the GPU export). - pub(crate) fn ray_tree(&self) -> Option<&crate::track::bsp::RayTree> { - self.bsp_rays.as_ref() - } - - /// Surface names and path-sensor connections over the union of both tile - /// tables: `(x0, y0, nx, ny, cells)` with cells row-major, each queried - /// with `get` at the tile centre. - #[allow(clippy::type_complexity)] - pub(crate) fn gpu_tiles(&self) -> (i64, i64, i64, i64, Vec<(Option, Option<(V2, V2)>)>) { - let tables = [ - ( - self.surface_names.x0, - self.surface_names.y0, - self.surface_names.nx, - self.surface_names.ny, - ), - ( - self.connections.x0, - self.connections.y0, - self.connections.nx, - self.connections.ny, - ), - ]; - let used: Vec<_> = tables.iter().filter(|t| t.2 > 0 && t.3 > 0).collect(); - if used.is_empty() { - return (0, 0, 0, 0, Vec::new()); - } - let x0 = used.iter().map(|t| t.0).min().unwrap(); - let y0 = used.iter().map(|t| t.1).min().unwrap(); - let x1 = used.iter().map(|t| t.0 + t.2).max().unwrap(); - let y1 = used.iter().map(|t| t.1 + t.3).max().unwrap(); - let mut cells = Vec::with_capacity(((x1 - x0) * (y1 - y0)) as usize); - for y in y0..y1 { - for x in x0..x1 { - let centre = V2::new((x as f64 + 0.5) * 768.0, (y as f64 + 0.5) * 768.0); - cells.push(( - self.surface_names.get(centre).cloned(), - self.connections.get(centre).and_then(|c| *c), - )); - } - } - (x0, y0, x1 - x0, y1 - y0, cells) - } - - /// Baked path segment rows `[x, y, dx, dy, len2]` (GPU export). - pub(crate) fn gpu_path_rows(&self) -> Vec<[f32; 5]> { - self.path_segments.rows() - } - - /// Whether the scene constructed a raycaster (`None` for legacy exports). - #[cfg(not(target_arch = "wasm32"))] - pub(crate) fn raycaster_present(&self) -> Option { - self.raycaster_present - } - - /// `config.surfaces.get(track.surface_at(point), ASPHALT)`. - #[inline(always)] - pub(crate) fn surface(&self, point: V2) -> Surface { - self.surfaces.get(point).copied().unwrap_or(self.default_surface) - } - - /// Vehicle surface properties are read live; only terrain identity belongs to the track. - pub(crate) fn vehicle_surface(&self, point: V2, vehicle: &VehicleConfig) -> Surface { - vehicle.surface(self.surface_names.get(point).map_or("asphalt", String::as_str)) - } - - /// Original TrackManager float projection and wrapped segment-index window. - pub fn closest_path(&self, point: V2, previous: Option) -> PathHit { - use crate::math::godot_math::F2; - assert!(self.path.len() >= 2, "track path needs at least two baked points"); - let count = self.path.len() - 1; - let length = self.path_length() as f32; - let posmod = |x: f32| { - let r = x % length; - if r < 0.0 { - r + length - } else { - r - } - }; - let index = |offset: f32| { - self.path_offsets - .binary_search_by(|v| v.partial_cmp(&(offset as f64)).unwrap()) - .unwrap_or_else(|i| i.saturating_sub(1)) as isize - }; - let (mut from, mut to) = (0isize, count as isize); - if let Some(previous) = previous.filter(|_| length > 2.0 * PATH_WINDOW as f32) { - let previous = previous as f32; - let center = index(posmod(previous)); - from = index(posmod(previous - PATH_WINDOW as f32)); - to = index(posmod(previous + PATH_WINDOW as f32)) + 1; - if from > center { - from -= count as isize; - } - if to < center { - to += count as isize; - } - } - let point = F2::from(point); - let segments = &self.path_segments; - let mut distance = f32::MAX; - let mut nearest = None; - // A full ascending scan can visit only the exact candidate subset. - let full = from == 0 && to == count as isize; - let visit = |i: usize| { - let d2 = segments.project(point, i).d2; - if d2 < distance { - distance = d2; - nearest = Some(i); - } - }; - if !(full - && self - .path_grid - .as_ref() - .is_some_and(|grid| grid.scan(point, |i| segments.project(point, i).d2, visit))) - { - // The wrapped window, as runs of ascending segment indices. - let mut i = from; - while i < to { - let start = i.rem_euclid(count as isize) as usize; - let run = ((to - i) as usize).min(count - start); - segments.scan(point, start..start + run, &mut distance, &mut nearest); - i += run as isize; - } - } - let Some(i) = nearest else { - return PathHit { - offset: 0.0, - near: V2::ZERO, - tangent: V2::ZERO, - }; - }; - let hit = segments.project(point, i); - let begin = self.path_offsets[i] as f32; - let end = self.path_offsets[i + 1] as f32; - PathHit { - offset: if length > 0.0 { - posmod(begin + (end - begin) * hit.fraction) as f64 - } else { - 0.0 - }, - near: hit.near.into(), - tangent: hit.delta.normalized().into(), - } - } - - /// `Track.closest_wall(point)`. - pub fn closest_wall(&self, point: V2) -> Option { - if self.raycaster_present == Some(false) { - return None; - } - if let Some(bsp) = &self.bsp { - return Some(bsp.closest(point)); - } - if self.raycaster_present == Some(true) { - return Some(V2::ZERO); - } - let candidate = |index: usize| { - let wall = &self.walls[index]; - let p = closest_point(point, wall.start, wall.end); - ((p - point).dot(p - point), p) - }; - if let Some((list, bound_sq)) = self.wall_grid.as_ref().and_then(|g| g.query(point)) { - let mut best: Option<(f64, V2)> = None; - for &index in list { - let c = candidate(index as usize); - if best.is_none_or(|b| c.0 < b.0) { - best = Some(c); - } - } - if let Some((d2, p)) = best { - if d2 <= bound_sq { - return Some(p); - } - } - } - let mut best: Option<(f64, V2)> = None; - for index in 0..self.walls.len() { - let c = candidate(index); - if best.is_none_or(|b| c.0 < b.0) { - best = Some(c); - } - } - best.map(|b| b.1) - } - - fn raycast_linear(&self, start: V2, end: V2) -> Option { - let mut best: Option<(f64, V2)> = None; - for wall in &self.walls { - if let Some(hit) = ray_intersection(start, end, wall.start, wall.end) { - let delta = F2::from(hit) - F2::from(start); - let d2 = delta.dot(delta) as f64; - if best.is_none_or(|b| d2 < b.0) { - best = Some((d2, hit)); - } - } - } - best.map(|b| b.1) - } - - /// `Track.raycast(start, end)`: nearest wall crossing, first wall on ties. - pub fn raycast(&self, start: V2, end: V2, stamps: &mut RayStamps) -> Option { - if self.raycaster_present == Some(false) { - return None; - } - if let Some(tree) = &self.bsp_rays { - return tree.raycast(start, end); - } - let Some(grid) = self.ray_grid.as_ref() else { - return self.raycast_linear(start, end); - }; - let lo = V2::new(start.x.min(end.x) - RAY_PAD, start.y.min(end.y) - RAY_PAD); - let hi = V2::new(start.x.max(end.x) + RAY_PAD, start.y.max(end.y) + RAY_PAD); - if grid.lists.range(lo, hi).is_none() { - return self.raycast_linear(start, end); - } - stamps.begin(self.walls.len()); - let ray = end - start; - let length = (ray.x * ray.x + ray.y * ray.y).sqrt(); - let chunks = ((length / (RAY_CELL * 0.5)).ceil() as usize).max(1); - // (d2, wall index, hit) - let mut best: Option<(f64, usize, V2)> = None; - for chunk in 0..chunks { - let t0 = chunk as f64 / chunks as f64; - let t1 = (chunk + 1) as f64 / chunks as f64; - let a = V2::new(start.x + ray.x * t0, start.y + ray.y * t0); - let b = V2::new(start.x + ray.x * t1, start.y + ray.y * t1); - let clo = V2::new(a.x.min(b.x) - RAY_PAD, a.y.min(b.y) - RAY_PAD); - let chi = V2::new(a.x.max(b.x) + RAY_PAD, a.y.max(b.y) + RAY_PAD); - let (x0, y0, x1, y1) = grid.lists.range(clo, chi).expect("chunk inside ray box"); - for iy in y0..=y1 { - for ix in x0..=x1 { - for &index in grid.lists.list(ix, iy) { - let index = index as usize; - if !stamps.mark(index) { - continue; - } - let wall = &self.walls[index]; - if let Some(hit) = ray_intersection(start, end, wall.start, wall.end) { - let delta = F2::from(hit) - F2::from(start); - let d2 = delta.dot(delta) as f64; - if best.is_none_or(|b| d2 < b.0 || (d2 == b.0 && index < b.1)) { - best = Some((d2, index, hit)); - } - } - } - } - } - if let Some((d2, _, _)) = best { - let reach = t1 * length; - if chunk + 1 < chunks && d2 < (reach - RAY_PAD).max(0.0).powi(2) { - break; - } - } - } - best.map(|b| b.2) - } - - /// `Track.path_sensor_point(point)`. - pub(crate) fn path_sensor_point(&self, point: V2) -> V2 { - match self.connections.get(point) { - Some(Some((first, last))) => { - let p = crate::math::godot_math::F2::from(point); - let a = crate::math::godot_math::F2::from(*first); - let v = crate::math::godot_math::F2::from(*last) - a; - let n = v.dot(v); - let closest = if n == 0.0 { - a - } else { - a + v * ((p.dot(v) - a.dot(v)).clamp(0.0, n) / n) - }; - (closest + (p - closest) * 0.5).into() - } - _ => point, - } - } - - /// Linear baked-path sample, used by AgentStats for center distance at - /// the same continuity-constrained offset that was selected for score. - pub fn path_position(&self, offset: f64) -> V2 { - if let Some(curve) = &self.curve { - return curve.position(offset as f32).into(); - } - if self.path.len() < 2 { - return self.path.first().copied().unwrap_or(V2::ZERO); - } - let offset = clamp(offset, 0.0, self.path_length()); - let index = (self.path.len() - 2).min(self.path_offsets.partition_point(|&x| x <= offset).saturating_sub(1)); - let (start, end) = (self.path_offsets[index], self.path_offsets[index + 1]); - let fraction = if end > start { - (offset - start) / (end - start) - } else { - 0.0 - }; - self.path[index] + (self.path[index + 1] - self.path[index]) * fraction - } - - /// `Track.path_direction(offset)`. - pub(crate) fn path_direction(&self, offset: f64) -> V2 { - let length = self.path_length(); - if length <= 0.0 { - return V2::ZERO; - } - let offset = clamp(offset, 0.0, length); - let index = (self.path.len() - 2).min(self.path_offsets.partition_point(|&x| x <= offset) - 1); - let (start, end) = (self.path_offsets[index], self.path_offsets[index + 1]); - let fraction = if end > start { - (offset - start) / (end - start) - } else { - 0.5 - }; - if !self.path_forward.is_empty() { - self.path_forward[index] - .rotated(self.forward_angles[index] * fraction) - .normalized() - } else { - (self.path[index + 1] - self.path[index]).normalized() - } - } - - /// Physics shapes whose bounding box may overlap the given box, sorted by index. - pub(crate) fn shape_candidates(&self, lo: V2, hi: V2, out: &mut Vec) { - out.clear(); - match self - .shape_grid - .as_ref() - .and_then(|g| g.lists.range(lo, hi).map(|r| (g, r))) - { - Some((grid, (x0, y0, x1, y1))) => { - for iy in y0..=y1 { - for ix in x0..=x1 { - out.extend_from_slice(grid.lists.list(ix, iy)); - } - } - out.sort_unstable(); - out.dedup(); - } - None => out.extend(0..self.shapes.len() as u32), - } - } -} - -/// Per-thread scratch marks for deduplicating walls during one raycast. -#[derive(Default, Clone)] -pub struct RayStamps { - marks: Vec, - generation: u32, -} - -impl RayStamps { - fn begin(&mut self, count: usize) { - if self.marks.len() < count { - self.marks.resize(count, 0); - } - self.generation = self.generation.wrapping_add(1); - if self.generation == 0 { - self.marks.iter_mut().for_each(|m| *m = 0); - self.generation = 1; - } - } - - /// Returns true the first time an index is seen in this raycast. - #[inline(always)] - fn mark(&mut self, index: usize) -> bool { - if self.marks[index] == self.generation { - false - } else { - self.marks[index] = self.generation; - true - } - } -} - -// --------------------------------------------------------------------------- -// Scene JSON -// --------------------------------------------------------------------------- - -fn num(value: &Value) -> f64 { - value - .as_f64() - .unwrap_or_else(|| panic!("expected a number, got {value}")) -} - -pub fn vector(value: &Value) -> V2 { - V2::new(num(&value[0]), num(&value[1])) -} - -/// Python truthiness fallback: `value or default`. -fn num_or(value: Option<&Value>, default: f64) -> f64 { - match value.and_then(Value::as_f64) { - Some(v) => v, - _ => default, - } -} - -fn get_or(object: &Value, key: &str, default: f64) -> f64 { - object.get(key).map(num).unwrap_or(default) -} - -pub fn load_vehicle(scene: &Value) -> VehicleConfig { - let car = &scene["vehicle"]; - let size = vector(&car["shape_size"]); - let angle = num(&car["shape_rotation"]); - // An exported matrix preserves managed/native constructor rounding and scale. - let shape_basis = car - .get("shape_basis") - .map(|b| (F2::from(vector(&b[0])), F2::from(vector(&b[1])))) - .unwrap_or_else(|| { - let (s, c) = crate::math::native_math::engine_sin_cos(angle as f32); - (F2 { x: c, y: s }, F2 { x: -s, y: c }) - }); - let physics = scene.get("physics").cloned().unwrap_or(Value::Null); - let physics_get = |key: &str, default: f64| physics.get(key).map(num).unwrap_or(default); - let surfaces = car["surfaces"] - .as_object() - .expect("vehicle surfaces") - .iter() - .map(|(name, values)| { - ( - name.clone(), - Surface { - grip: get_or(values, "grip", 1.0), - power: get_or(values, "power", 1.0), - steering: get_or(values, "steering", 1.0), - }, - ) - }) - .collect(); - let config = VehicleConfig { - wheels: car["wheels"] - .as_array() - .expect("wheels") - .iter() - .map(|w| WheelConfig { - position: vector(&w["position"]), - steering: w["steering"].as_bool().expect("wheel steering flag"), - max_angle_deg: num(&w["max_angle_deg"]), - power: num(&w["power"]), - brake_power: num(&w["brake_power"]), - handbrake_power: num(&w["handbrake_power"]), - }) - .collect(), - mass: num(&car["mass"]), - inertia: num(&car["inertia_server"]), - half_width: (angle.cos().abs() * size.x + angle.sin().abs() * size.y) / 2.0, - half_length: (angle.sin().abs() * size.x + angle.cos().abs() * size.y) / 2.0, - shape_size: size, - shape_basis, - shape_position: car.get("shape_position").map(vector).unwrap_or(V2::ZERO), - center_of_mass: car - .get("center_of_mass") - .or_else(|| car.get("shape_position")) - .map(vector) - .unwrap_or(V2::ZERO), - friction: num_or(car.get("friction"), 1.0), - solver_iterations: physics_get("solver_iterations", 16.0) as usize, - max_contacts_reported: num_or(car.get("max_contacts_reported"), 8.0) as usize, - contact_bias: physics_get("contact_default_bias", 0.8), - contact_recycle_radius: physics_get("contact_recycle_radius", 1.0), - contact_max_separation: physics_get("contact_max_separation", 1.5), - allowed_penetration: physics_get("contact_max_allowed_penetration", 0.3), - grip: num(&car["grip"]), - steering_speed: num(&car["steering_speed"]), - center_steering: car["center_steering"].as_bool().expect("center_steering"), - air_resistance: num(&car["air_resistance"]), - max_velocity: num(&car["max_velocity"]), - custom_integrator: car["custom_integrator"].as_bool().unwrap_or(false), - gravity: (F2::from(physics.get("gravity").map(vector).unwrap_or(V2::ZERO)) - * num_or(car.get("gravity_scale"), 1.0) as f32) - .into(), - linear_damp: num(&car["linear_damp"]), - angular_damp: num(&car["angular_damp"]), - bounce: num_or(car.get("bounce"), 0.0), - surfaces, - }; - assert!( - config.mass > 0.0 && config.inertia > 0.0 && config.half_width > 0.0 && config.half_length > 0.0, - "mass, inertia and car half extents must be positive" - ); - assert!(!config.wheels.is_empty(), "at least one wheel is required"); - config -} - -fn normalize_f32(v: V2) -> V2 { - let dot = f32r(f32r(v.x * v.x) + f32r(v.y * v.y)); - let length = f32r(dot.sqrt()); - if length != 0.0 { - V2::new(f32r(v.x / length), f32r(v.y / length)) - } else { - V2::ZERO - } -} - -impl PhysicsShape { - fn new(points: Vec, origin: V2, native_local: Option>) -> PhysicsShape { - let origin_f32 = V2::new(f32r(origin.x), f32r(origin.y)); - // Native exports preserve polygon support order and local float bits. - // Reconstructing them from translated world coordinates can lose both. - let local: Vec = native_local.unwrap_or_else(|| { - points - .iter() - .map(|p| V2::new(f32r(p.x - origin_f32.x), f32r(p.y - origin_f32.y))) - .collect() - }); - let local_normals = (0..local.len()) - .map(|i| { - let (point, next) = (local[i], local[(i + 1) % local.len()]); - let edge = V2::new(f32r(next.x - point.x), f32r(next.y - point.y)); - normalize_f32(V2::new(edge.y, -edge.x)) - }) - .collect(); - let min = V2::new( - points.iter().map(|p| p.x).fold(f64::INFINITY, f64::min), - points.iter().map(|p| p.y).fold(f64::INFINITY, f64::min), - ); - let max = V2::new( - points.iter().map(|p| p.x).fold(f64::NEG_INFINITY, f64::max), - points.iter().map(|p| p.y).fold(f64::NEG_INFINITY, f64::max), - ); - PhysicsShape { - points, - origin_f32, - local, - local_normals, - min, - max, - wall_first: false, - friction: 1.0, - bounce: 0.0, - tile: None, - } - } -} - -impl World { - /// `from_scene_with` under Proton, where the game traces were recorded. - pub fn from_scene(scene: &Value) -> World { - World::from_scene_with(scene, MathProfile::Proton) - } - - /// `compare_game_trace.load_scene(scene)` plus the static acceleration grids. - pub fn from_scene_with(scene: &Value, math: MathProfile) -> World { - let vehicle = load_vehicle(scene); - let track = &scene["track"]; - let empty = Vec::new(); - let array = |key: &str| track.get(key).and_then(Value::as_array).unwrap_or(&empty); - let tile_origin = track.get("tile_map_position").map(vector).unwrap_or(V2::ZERO); - let tile_size = track.get("tile_size").map(vector).unwrap_or(V2::new(768.0, 768.0)); - - let walls: Vec = array("walls") - .iter() - .map(|w| Wall { - start: vector(&w[0]), - end: vector(&w[1]), - }) - .collect(); - let shapes: Vec = array("physics_shapes") - .iter() - .map(|shape| { - let tile = shape.get("tile").map(vector).unwrap_or(V2::ZERO); - let origin = V2::new( - (tile.x + 0.5) * tile_size.x + tile_origin.x, - (tile.y + 0.5) * tile_size.y + tile_origin.y, - ); - let origin = shape.get("origin").map(vector).unwrap_or(origin); - let points = shape["points"] - .as_array() - .expect("shape points") - .iter() - .map(|p| vector(p) + tile_origin) - .collect(); - let local = shape["local_points"].as_array().map(|points| { - points - .iter() - .map(|v| crate::track::curve::json_vector(v).into()) - .collect() - }); - let mut collider = PhysicsShape::new(points, origin, local); - collider.tile = shape.get("tile").map(|t| (num(&t[0]) as i64, num(&t[1]) as i64)); - collider.wall_first = shape.get("wall_first").and_then(Value::as_bool).unwrap_or(false); - collider.friction = num_or(shape.get("friction"), num_or(track.get("wall_friction"), 1.0)); - collider.bounce = num_or(shape.get("bounce"), num_or(track.get("wall_bounce"), 0.0)); - collider - }) - .collect(); - let path: Vec = array("path") - .iter() - .map(|v| crate::math::godot_math::F2::from(vector(v)).into()) - .collect(); - let path_forward: Vec = array("path_forward").iter().map(vector).collect(); - assert!( - path_forward.is_empty() || path_forward.len() == path.len(), - "path_forward and path must have the same number of points" - ); - - let tiles = array("tiles"); - let key = |tile: &Value| (num(&tile["coords"][0]) as i64, num(&tile["coords"][1]) as i64); - let surface_entries: Vec<((i64, i64), Surface)> = tiles - .iter() - .map(|tile| { - ( - key(tile), - vehicle.surface(tile["surface"].as_str().expect("tile surface")), - ) - }) - .collect(); - let connection_entries: Vec<_> = tiles - .iter() - .filter_map(|tile| { - let points = tile.get("connections")?.as_array()?; - let pair = if points.is_empty() { - None - } else { - Some((vector(&points[0]), vector(points.last().unwrap()))) - }; - Some((key(tile), pair)) - }) - .collect(); - - let mut path_offsets = vec![0.0]; - for pair in path.windows(2) { - path_offsets.push( - (*path_offsets.last().unwrap() as f32 - + (crate::math::godot_math::F2::from(pair[1]) - crate::math::godot_math::F2::from(pair[0])) - .length()) as f64, - ); - } - let forward_angles = path_forward - .windows(2) - .map(|p| p[0].cross(p[1]).atan2(p[0].dot(p[1]))) - .collect(); - - // Grid extents: everything static on the map plus a margin for cars - // that leave the walls. Queries outside use the linear scans. - let mut lo = V2::new(f64::INFINITY, f64::INFINITY); - let mut hi = V2::new(f64::NEG_INFINITY, f64::NEG_INFINITY); - let mut include = |p: V2| { - lo = V2::new(lo.x.min(p.x), lo.y.min(p.y)); - hi = V2::new(hi.x.max(p.x), hi.y.max(p.y)); - }; - path.iter().for_each(|&p| include(p)); - walls.iter().for_each(|w| { - include(w.start); - include(w.end) - }); - shapes.iter().for_each(|s| s.points.iter().for_each(|&p| include(p))); - let margin = 768.0; - let (lo, hi) = ( - V2::new(lo.x - margin, lo.y - margin), - V2::new(hi.x + margin, hi.y + margin), - ); - let finite = lo.x.is_finite() && hi.x.is_finite(); - - let bsp = crate::track::bsp::Node::from_polygons(array("polygons")); - let raycaster_present = track["raycaster_present"].as_bool(); - let wall_segments: Vec<(V2, V2)> = walls.iter().map(|w| (w.start, w.end)).collect(); - // closest_wall only uses this grid for legacy scenes without a BSP - // or an explicit raycaster. Building it for large imported tracks - // otherwise scans every wall for every grid cell without using it. - let wall_grid = (finite && !walls.is_empty() && bsp.is_none() && raycaster_present.is_none()) - .then(|| NearestGrid::build(&wall_segments, lo, hi, 32.0)); - let wall_boxes: Vec<(V2, V2)> = walls - .iter() - .map(|w| { - ( - V2::new(w.start.x.min(w.end.x), w.start.y.min(w.end.y)), - V2::new(w.start.x.max(w.end.x), w.start.y.max(w.end.y)), - ) - }) - .collect(); - let ray_grid = - (finite && !walls.is_empty()).then(|| BoxGrid::build(&wall_boxes, lo, hi, RAY_CELL, RAY_PAD * 2.0)); - let shape_boxes: Vec<(V2, V2)> = shapes.iter().map(|s| (s.min, s.max)).collect(); - let shape_grid = (finite && !shapes.is_empty()).then(|| BoxGrid::build(&shape_boxes, lo, hi, 128.0, 1.0)); - - let path_segments = crate::track::path_segments::PathSegments::new(&path); - let path_grid = - crate::track::segment_grid::SegmentGrid::new(&path.iter().map(|&p| F2::from(p)).collect::>()); - let default_surface = vehicle.surface("asphalt"); - World { - track: Track { - native_broadphase: track.get("native_broadphase").and_then(Value::as_bool).unwrap_or(false), - native_body_order_reverse: track.get("native_body_order").and_then(Value::as_str) == Some("reverse"), - native_tilemap: track.get("native_creation").and_then(Value::as_str) == Some("tilemap"), - native_tile_order: tiles.iter().map(key).collect(), - native_cell_order: track["native_cell_order"] - .as_array() - .map(|cells| cells.iter().map(|t| (num(&t[0]) as i64, num(&t[1]) as i64)).collect()) - .unwrap_or_else(|| tiles.iter().map(key).collect()), - bsp_rays: bsp.as_deref().map(crate::track::bsp::RayTree::new), - bsp, - raycaster_present, - walls, - shapes, - path, - path_forward, - curve: track - .get("curve") - .map(|c| crate::track::curve::Curve::from_json(c, math)), - path_grid, - path_segments, - path_offsets, - forward_angles, - surfaces: TileTable::new(&surface_entries), - surface_names: TileTable::new( - &tiles - .iter() - .map(|tile| (key(tile), tile["surface"].as_str().unwrap().to_owned())) - .collect::>(), - ), - default_surface, - connections: TileTable::new(&connection_entries), - wall_grid, - ray_grid, - shape_grid, - }, - vehicle, - math, - } - } -} diff --git a/github-simulator/source/src/training/agent.rs b/github-simulator/source/src/training/agent.rs deleted file mode 100644 index 6201117217bc77b5901d7540d9232552ed065ebf..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/training/agent.rs +++ /dev/null @@ -1,59 +0,0 @@ -//! Training agents own their controls, statistics, and inference scratch. - -use super::TrainingStats; -use crate::nn::network::{ForwardScratch, Network}; -use crate::physics::car::{Car, Controls, SensorScratch}; -use crate::track::world::World; -use crate::training::evolution::AgentResult; - -/// Per-agent scratch buffers, kept with the agent so threads never share them. -#[derive(Default, Clone)] -pub(super) struct AgentScratch { - pub(super) sensors: SensorScratch, - pub(super) inputs: Vec, - pub(super) forward: ForwardScratch, -} - -#[derive(Clone)] -pub struct TrainingAgent { - pub network: Network, - pub car: Car, - pub stats: TrainingStats, - pub controls: Controls, - pub pending_contact: bool, - /// Tick at which the car became inactive in the current window. - pub deactivated_at: Option, - pub(super) scratch: AgentScratch, -} - -impl TrainingAgent { - /// `Schedule::update_stats`: statistics on ticks where - /// `tick % 6 == stats_phase`, then the idle elimination. - pub fn update_stats(&mut self, world: &World, tick: u64, stats_phase: u64, eliminate_when_idle: bool) { - let was_active = self.car.active; - if tick % 6 == stats_phase { - let first_stats_tick = if stats_phase == 0 { 6 } else { stats_phase }; - self.stats.update( - world, - &self.car, - &self.controls, - tick - first_stats_tick, - self.pending_contact, - ); - self.pending_contact = false; - if self.car.active && eliminate_when_idle && self.stats.idle_ticks > 80 { - self.car.deactivate(); - } - } - if was_active && !self.car.active { - self.deactivated_at = Some(tick); - } - } - pub fn result(&self) -> AgentResult<'_> { - AgentResult { - network: &self.network, - metrics: self.stats.metrics(), - update_count: self.stats.update_count, - } - } -} diff --git a/github-simulator/source/src/training/ars.rs b/github-simulator/source/src/training/ars.rs deleted file mode 100644 index d51a3ca751ee11520636bede468bff13f8684cfe..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/training/ars.rs +++ /dev/null @@ -1,369 +0,0 @@ -//! Augmented Random Search (Mania, Guy and Recht 2018, V2-t) with an elite pool. -//! -//! The search keeps one point `theta` in parameter space. Each population is -//! -//! ```text -//! [elite 0 .. elite E-1] [theta] [theta + nu*d0, theta - nu*d0] [theta + nu*d1, ...] ([theta]) -//! ``` -//! -//! The elites are the best distinct cars so far, run again unchanged; each -//! `d` is a standard normal direction and the trailing `theta` appears when -//! the population size leaves one slot over. After the generation runs, the -//! scores (`reward_values`, so rank based) steer one step: the `top_frac` best -//! pairs by `max(r+, r-)` move `theta` by -//! `alpha / (b * nu) * sum (r+ - r-) * d`. If those scores are all alike the -//! step is skipped. -//! -//! Scores are ranks within one generation, so they cannot be compared across -//! generations. The elite pool needs no such comparison: its members are in -//! every population, and the next pool is the best distinct cars of that -//! population, old elites and probes alike. - -use super::optimizer::{Optimizer, Produced, Record}; -use super::TrainingRandom; -use crate::math::profile::MathProfile; -use crate::nn::network::{parameter_count, Network}; -use crate::training::evolution::{reward_values, AgentResult, ArsSettings, EvolutionSettings, Generation}; -use rayon::prelude::*; - -/// Rejects settings that `Ars` cannot run with. -pub(crate) fn validate(s: &ArsSettings, population: usize) -> Result<(), String> { - let positive = |x: f64| x.is_finite() && x > 0.0; - if !positive(s.nu) || !positive(s.alpha) { - return Err("ars nu and alpha must be positive".into()); - } - if !(s.top_frac > 0.0 && s.top_frac <= 1.0) { - return Err("ars top_frac must be in (0, 1]".into()); - } - if !s.max_weight.is_finite() || s.max_weight < 0.0 { - return Err("ars max_weight must be 0 (off) or positive".into()); - } - if population < s.elite_count.saturating_add(3) { - return Err(format!( - "ars needs population >= elite_count + 3 (the elites, the search point and one pair), got {population} with {} elites", - s.elite_count - )); - } - Ok(()) -} - -/// The settings that shape a population, as checkpoints store them. -#[derive(Clone, Debug, PartialEq)] -pub(crate) struct Sampling { - pub(crate) population: usize, - pub(crate) elite_count: usize, - pub(crate) nu: f64, - pub(crate) max_weight: f64, -} - -impl Sampling { - fn of(s: &EvolutionSettings) -> Sampling { - Sampling { - population: s.population, - elite_count: s.ars.elite_count, - nu: s.ars.nu, - max_weight: s.ars.max_weight, - } - } -} - -#[derive(Clone, Debug, PartialEq)] -pub(crate) struct Elite { - pub(crate) params: Vec, - /// The reward it had in the generation that selected it. - pub(crate) score: f64, -} - -/// Where each kind of car sits in the population `Ars` produced last. -#[derive(Clone, Copy, Debug, PartialEq)] -struct Layout { - elites: usize, - pairs: usize, - size: usize, -} - -#[derive(Clone, Default)] -pub(crate) struct Ars { - shape: Vec, - /// The search point; empty until the first population is made. - theta: Vec, - /// Best first. - pool: Vec, - layout: Option, - /// The noise scale the last population was sampled with; `learn` divides - /// by it, whatever the settings say by then. - nu: f64, - /// The pairs' directions of the last population, one row of `theta.len()` each. - deltas: Vec, -} - -/// An `Ars` before it sampled a population: sampling again from `rng` repeats it. -#[derive(Clone)] -pub(crate) struct ArsRecord { - pub(crate) shape: Vec, - pub(crate) theta: Vec, - pub(crate) pool: Vec, - pub(crate) sampling: Sampling, - /// The generator before the directions were drawn. - pub(crate) rng: TrainingRandom, - /// The math the game's streams draw the directions with. - pub(crate) math: MathProfile, -} - -impl ArsRecord { - /// Whether `rebuild` can sample this record; a checkpoint may be corrupt. - pub(crate) fn validate(&self) -> Result<(), String> { - if self.shape.len() < 2 || self.shape.contains(&0) { - return Err("the checkpoint has an invalid network shape".into()); - } - let size = self.shape.windows(2).try_fold(0usize, |sum, w| { - w[0].checked_add(1)?.checked_mul(w[1])?.checked_add(sum) - }); - if Some(self.theta.len()) != size || self.pool.iter().any(|e| Some(e.params.len()) != size) { - return Err("the checkpoint has the wrong number of parameters".into()); - } - if self.theta.iter().any(|p| !p.is_finite()) - || self - .pool - .iter() - .any(|e| !e.score.is_finite() || e.params.iter().any(|p| !p.is_finite())) - { - return Err("non-finite checkpoint parameter or elite score".into()); - } - let s = &self.sampling; - let positive = |x: f64| x.is_finite() && x > 0.0; - if !positive(s.nu) || !s.max_weight.is_finite() || s.max_weight < 0.0 { - return Err("the checkpoint's ars noise or weight limit is out of range".into()); - } - if s.population < self.pool.len().min(s.elite_count).saturating_add(3) { - return Err("the checkpoint's population is too small for its elites".into()); - } - Ok(()) - } - - /// The sampled population, the generator after it, and the search state. - /// Call `validate` first. - pub(crate) fn rebuild(&self) -> (Vec, TrainingRandom, Ars) { - let mut ars = Ars { - shape: self.shape.clone(), - theta: self.theta.clone(), - pool: self.pool.clone(), - ..Ars::default() - }; - let mut rng = self.rng.clone(); - let networks = ars.sample(&self.sampling, &mut rng, self.math); - (networks, rng, ars) - } -} - -impl Ars { - /// Draws the directions and builds the population around `theta`. - fn sample(&mut self, sampling: &Sampling, rng: &mut TrainingRandom, math: MathProfile) -> Vec { - let n = self.theta.len(); - let elites = sampling.elite_count.min(self.pool.len()); - assert!( - sampling.population >= elites + 3, - "ars needs population >= elite_count + 3" - ); - let pairs = (sampling.population - elites - 1) / 2; - if sampling.max_weight > 0.0 { - let limit = sampling.max_weight; - let clamp_all = |v: &mut Vec| v.iter_mut().for_each(|x| *x = x.clamp(-limit, limit)); - clamp_all(&mut self.theta); - self.pool.iter_mut().for_each(|e| clamp_all(&mut e.params)); - } - self.nu = sampling.nu; - rng.standard_normals_into(pairs * n, &mut self.deltas, math); - let (theta, deltas, nu, limit) = (&self.theta, &self.deltas, sampling.nu, sampling.max_weight); - let clamp = move |v: f64| if limit > 0.0 { v.clamp(-limit, limit) } else { v }; - let probes: Vec<[Vec; 2]> = (0..pairs) - .into_par_iter() - .map(|i| { - let d = &deltas[i * n..(i + 1) * n]; - let side = - |sign: f64| -> Vec { theta.iter().zip(d).map(|(&t, &z)| clamp(t + sign * nu * z)).collect() }; - [side(1.0), side(-1.0)] - }) - .collect(); - let mut cars: Vec> = Vec::with_capacity(sampling.population); - cars.extend(self.pool[..elites].iter().map(|e| e.params.clone())); - cars.push(theta.clone()); - cars.extend(probes.into_iter().flatten()); - while cars.len() < sampling.population { - cars.push(theta.clone()); - } - self.layout = Some(Layout { - elites, - pairs, - size: sampling.population, - }); - let shape = &self.shape; - cars.into_par_iter().map(|p| Network::from_vector(shape, p)).collect() - } - - /// Whether `agents` are the population of the last `sample`. - fn matches(&self, agents: &[AgentResult]) -> bool { - let (Some(layout), Some(first)) = (self.layout, agents.first()) else { - return false; - }; - layout.size == agents.len() - && first.network.shape == self.shape - && self.theta.len() == first.network.params.len() - && self.deltas.len() == layout.pairs * self.theta.len() - } - - /// The new elite pool and the step of `theta`, from the scored population. - fn learn(&mut self, agents: &[AgentResult], scores: &[f64], settings: &EvolutionSettings) { - let Layout { elites, pairs, .. } = self.layout.expect("learn follows sample"); - let ars = &settings.ars; - - let mut order: Vec = (0..agents.len()).collect(); - order.sort_by(|&a, &b| scores[b].partial_cmp(&scores[a]).unwrap_or(std::cmp::Ordering::Equal)); - let mut pool: Vec = Vec::with_capacity(ars.elite_count); - for &i in &order { - if pool.len() >= ars.elite_count { - break; - } - let params = &agents[i].network.params; - if !pool.iter().any(|e| e.params == *params) { - pool.push(Elite { - params: params.clone(), - score: scores[i], - }); - } - } - self.pool = pool; - - // Probe i is at `elites + 1 + 2i` (plus side) and the next slot (minus side). - let mut ranked: Vec<(usize, f64, f64)> = (0..pairs) - .map(|i| (i, scores[elites + 1 + 2 * i], scores[elites + 2 + 2 * i])) - .collect(); - ranked.sort_by(|a, b| { - let (ka, kb) = (a.1.max(a.2), b.1.max(b.2)); - kb.partial_cmp(&ka).unwrap_or(std::cmp::Ordering::Equal) - }); - let used = ((ars.top_frac * pairs as f64).ceil() as usize).clamp(1, pairs); - ranked.truncate(used); - - let count = (2 * used) as f64; - let mean = ranked.iter().map(|r| r.1 + r.2).sum::() / count; - let variance = ranked - .iter() - .map(|r| (r.1 - mean).powi(2) + (r.2 - mean).powi(2)) - .sum::() - / count; - if variance.sqrt() < 1e-6 { - return; - } - let scale = ars.alpha / (used as f64 * self.nu); - let n = self.theta.len(); - for (i, plus, minus) in ranked { - let weight = scale * (plus - minus); - for (t, &z) in self.theta.iter_mut().zip(&self.deltas[i * n..(i + 1) * n]) { - *t += weight * z; - } - } - if ars.max_weight > 0.0 { - let limit = ars.max_weight; - self.theta.iter_mut().for_each(|t| *t = t.clamp(-limit, limit)); - } - } - - fn produce( - &mut self, - scores: Vec, - settings: &EvolutionSettings, - rng: &mut TrainingRandom, - math: MathProfile, - trace: bool, - ) -> Produced { - let sampling = Sampling::of(settings); - let before = trace.then(|| ArsRecord { - shape: self.shape.clone(), - theta: self.theta.clone(), - pool: self.pool.clone(), - sampling: sampling.clone(), - rng: rng.clone(), - math, - }); - let networks = self.sample(&sampling, rng, math); - Produced { - generation: Generation { - networks, - preserved_count: self.layout.map_or(0, |l| l.elites + 1), - rewards: scores, - }, - record: before.map(Record::Ars), - } - } -} - -impl Optimizer for Ars { - fn name(&self) -> &'static str { - "ars" - } - - fn boxed_clone(&self) -> Box { - Box::new(self.clone()) - } - - fn invalidate(&mut self) { - // `matches` fails from here on, so the next turnover searches on from the best car. - self.layout = None; - self.deltas.clear(); - } - - fn start( - &mut self, - seed: &Network, - settings: &EvolutionSettings, - rng: &mut TrainingRandom, - math: MathProfile, - trace: bool, - ) -> Produced { - let seed_result = [AgentResult { - network: seed, - metrics: [None; 14], - update_count: 0, - }]; - let scores = reward_values(&seed_result, &settings.rewards); - *self = Ars { - shape: seed.shape.clone(), - theta: seed.params.clone(), - ..Ars::default() - }; - debug_assert_eq!(self.theta.len(), parameter_count(&self.shape)); - self.produce(scores, settings, rng, math, trace) - } - - fn next( - &mut self, - agents: &[AgentResult], - scores: Option>, - settings: &EvolutionSettings, - rng: &mut TrainingRandom, - _scratch: &mut Vec, - math: MathProfile, - trace: bool, - ) -> Produced { - let scores = scores.unwrap_or_else(|| reward_values(agents, &settings.rewards)); - if self.matches(agents) { - self.learn(agents, &scores, settings); - } else { - // A new run, a restored population or a changed population size: - // the directions no longer belong to these cars. Search on from - // the best one instead. - let best = (1..scores.len()).fold(0, |best, i| if scores[i] > scores[best] { i } else { best }); - let network = agents[best].network; - *self = Ars { - shape: network.shape.clone(), - theta: network.params.clone(), - ..Ars::default() - }; - } - self.produce(scores, settings, rng, math, trace) - } -} - -#[cfg(test)] -mod tests; diff --git a/github-simulator/source/src/training/ars/tests.rs b/github-simulator/source/src/training/ars/tests.rs deleted file mode 100644 index 9cfc69ad4cebd781db85a1762b11767bb5369f8c..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/training/ars/tests.rs +++ /dev/null @@ -1,367 +0,0 @@ -use super::*; -use crate::training::evolution::Metrics; -use crate::training::game_random::GameRandom; -use crate::training::pyrandom::PyRandom; - -fn settings(population: usize, elite_count: usize) -> EvolutionSettings { - EvolutionSettings { - algorithm: "ars".into(), - population, - ars: ArsSettings { - nu: 0.1, - alpha: 0.05, - elite_count, - ..ArsSettings::default() - }, - ..EvolutionSettings::default() - } -} - -const SHAPE: [usize; 3] = [3, 4, 2]; -const MATH: MathProfile = MathProfile::Proton; - -fn seed() -> Network { - Network::xavier(&SHAPE, &mut PyRandom::new(5)) -} - -/// Cars whose `total_score` is `score(params)`. -fn agents<'a>(networks: &'a [Network], score: &dyn Fn(&[f64]) -> f64) -> Vec> { - networks - .iter() - .map(|network| { - let mut metrics: Metrics = [None; 14]; - metrics[0] = Some(score(&network.params)); - AgentResult { - network, - metrics, - update_count: 10, - } - }) - .collect() -} - -fn bits(networks: &[Network]) -> Vec> { - networks - .iter() - .map(|n| n.params.iter().map(|p| p.to_bits()).collect()) - .collect() -} - -fn distance(a: &[f64], b: &[f64]) -> f64 { - a.iter().zip(b).map(|(x, y)| (x - y) * (x - y)).sum::().sqrt() -} - -fn step( - ars: &mut Ars, - cars: &[Network], - s: &EvolutionSettings, - rng: &mut TrainingRandom, - score: &dyn Fn(&[f64]) -> f64, -) -> Produced { - let results = agents(cars, score); - ars.next(&results, None, s, rng, &mut Vec::new(), MATH, true) -} - -fn into_record(produced: Produced) -> (Vec, ArsRecord) { - let Some(Record::Ars(record)) = produced.record else { - panic!("no ars record") - }; - (produced.generation.networks, record) -} - -#[test] -fn the_first_population_is_the_seed_and_antithetic_probes() { - let (s, seed) = (settings(10, 2), seed()); - let mut rng = TrainingRandom::from(PyRandom::new(1)); - let mut ars = Ars::default(); - let generation = ars.start(&seed, &s, &mut rng, MATH, false).generation; - let cars = &generation.networks; - assert_eq!(cars.len(), 10); - assert_eq!(generation.preserved_count, 1, "no elites yet, only the search point"); - assert_eq!(cars[0].params, seed.params); - // 10 cars: the point, 4 pairs and one spare copy of the point. - assert_eq!(cars[9].params, seed.params); - for pair in cars[1..9].chunks(2) { - for ((p, m), t) in pair[0].params.iter().zip(&pair[1].params).zip(&seed.params) { - assert!(((p + m) / 2.0 - t).abs() < 1e-12, "probes straddle the point"); - } - assert_ne!(pair[0].params, pair[1].params); - } -} - -#[test] -fn climbs_a_quadratic() { - let target: Vec = (0..parameter_count(&SHAPE)) - .map(|i| ((i * 7 % 11) as f64 - 5.0) * 0.2) - .collect(); - let score = |p: &[f64]| -distance(p, &target); - let s = settings(40, 3); - let mut rng = TrainingRandom::from(PyRandom::new(2)); - let mut ars = Ars::default(); - let seed = seed(); - let start_distance = distance(&seed.params, &target); - let mut cars = ars.start(&seed, &s, &mut rng, MATH, false).generation.networks; - for _ in 0..150 { - let results = agents(&cars, &score); - cars = ars - .next(&results, None, &s, &mut rng, &mut Vec::new(), MATH, false) - .generation - .networks; - } - let end_distance = distance(&ars.theta, &target); - assert!( - end_distance < start_distance / 3.0, - "ars moved from {start_distance} to {end_distance}" - ); -} - -#[test] -fn the_pool_holds_the_best_distinct_cars_and_leads_the_next_generation() { - let s = settings(12, 3); - let mut rng = TrainingRandom::from(PyRandom::new(3)); - let mut ars = Ars::default(); - let first = ars.start(&seed(), &s, &mut rng, MATH, false).generation.networks; - let score = |p: &[f64]| p.iter().sum::(); - // The point is in the population twice (slot 0 and the spare); the pool keeps it once. - let mut order: Vec = (0..first.len()).collect(); - order.sort_by(|&a, &b| score(&first[b].params).partial_cmp(&score(&first[a].params)).unwrap()); - let mut best: Vec = Vec::new(); - for &i in &order { - if best.len() < 3 && !best.iter().any(|n| n.params == first[i].params) { - best.push(first[i].clone()); - } - } - let results = agents(&first, &score); - let next = ars - .next(&results, None, &s, &mut rng, &mut Vec::new(), MATH, false) - .generation; - assert_eq!(ars.pool.len(), 3); - assert_eq!( - bits(&next.networks[..3]), - bits(&best), - "the elites come first, best first" - ); - assert_eq!(next.preserved_count, 4); - assert_eq!(next.networks[3].params, ars.theta, "then the moved search point"); - assert_eq!(next.networks.len(), 12); -} - -#[test] -fn identical_scores_leave_the_search_point_alone() { - let s = settings(10, 1); - let mut rng = TrainingRandom::from(PyRandom::new(4)); - let mut ars = Ars::default(); - let seed = seed(); - let cars = ars.start(&seed, &s, &mut rng, MATH, false).generation.networks; - let results = agents(&cars, &|_| 7.0); - ars.next(&results, None, &s, &mut rng, &mut Vec::new(), MATH, false); - assert_eq!(ars.theta, seed.params); -} - -#[test] -fn a_foreign_population_restarts_from_its_best_car() { - let s = settings(10, 1); - let mut rng = TrainingRandom::from(PyRandom::new(6)); - let mut ars = Ars::default(); - ars.start(&seed(), &s, &mut rng, MATH, false); - let others: Vec = (0..6) - .map(|i| Network::xavier(&SHAPE, &mut PyRandom::new(100 + i))) - .collect(); - let results = agents(&others, &|p| p[0]); - let top = (0..6) - .max_by(|&a, &b| others[a].params[0].partial_cmp(&others[b].params[0]).unwrap()) - .unwrap(); - let next = ars - .next(&results, None, &s, &mut rng, &mut Vec::new(), MATH, false) - .generation; - assert_eq!(ars.theta, others[top].params); - assert_eq!(next.networks.len(), 10); -} - -/// A record rebuilds its population and generator, and the rebuilt search -/// state goes on exactly as the original does. -#[test] -fn records_rebuild_the_population_and_continue_identically() { - let s = settings(14, 2); - let mut rng = TrainingRandom::from(PyRandom::new(8)); - let mut ars = Ars::default(); - let started = ars.start(&seed(), &s, &mut rng, MATH, true); - let (mut cars, first) = into_record(started); - let (again, after, _) = first.rebuild(); - assert_eq!(bits(&again), bits(&cars)); - assert_eq!(after.to_json(), rng.to_json()); - - let score = |p: &[f64]| p.iter().map(|x| x.sin()).sum::(); - for _ in 0..4 { - let (next, record) = into_record(step(&mut ars, &cars, &s, &mut rng, &score)); - record.validate().unwrap(); - let (rebuilt, rebuilt_rng, mut restored) = record.rebuild(); - assert_eq!(bits(&rebuilt), bits(&next)); - assert_eq!(rebuilt_rng.to_json(), rng.to_json()); - // Both continue from the same cars and generator. - let (mut a_rng, mut b_rng) = (rng.clone(), rebuilt_rng); - let a = step(&mut ars, &next, &s, &mut a_rng, &score); - let b = step(&mut restored, &next, &s, &mut b_rng, &score); - assert_eq!(bits(&a.generation.networks), bits(&b.generation.networks)); - assert_eq!(a_rng.to_json(), b_rng.to_json()); - // Take `ars` back to before that trial step. - let (_, _, state) = record.rebuild(); - ars = state; - cars = next; - } -} - -#[test] -fn game_streams_drive_it_too() { - let s = settings(10, 1); - let mut rng = TrainingRandom::from(GameRandom::new([1, 2, 3, 4], 5)); - let mut ars = Ars::default(); - let cars = ars.start(&seed(), &s, &mut rng, MATH, false).generation.networks; - let results = agents(&cars, &|p| p[1]); - let next = ars - .next(&results, None, &s, &mut rng, &mut Vec::new(), MATH, false) - .generation; - assert_eq!(next.networks.len(), 10); - assert_ne!(next.networks[1].params, cars[1].params); -} - -#[test] -fn weights_stay_inside_the_limit() { - let mut s = settings(10, 1); - s.ars.max_weight = 0.3; - let mut rng = TrainingRandom::from(PyRandom::new(9)); - let mut ars = Ars::default(); - let seed = Network::from_vector(&SHAPE, vec![0.29; parameter_count(&SHAPE)]); - let cars = ars.start(&seed, &s, &mut rng, MATH, false).generation.networks; - assert!(cars.iter().flat_map(|n| &n.params).all(|p| p.abs() <= 0.3)); -} - -#[test] -fn settings_are_checked() { - let ok = ArsSettings::default(); - validate(&ok, 6).unwrap(); - assert!(validate(&ok, 5).unwrap_err().contains("elite_count + 3")); - for bad in [ - ArsSettings { nu: 0.0, ..ok.clone() }, - ArsSettings { - alpha: f64::NAN, - ..ok.clone() - }, - ArsSettings { - top_frac: 0.0, - ..ok.clone() - }, - ArsSettings { - top_frac: 1.5, - ..ok.clone() - }, - ArsSettings { - max_weight: -1.0, - ..ok.clone() - }, - ] { - assert!(validate(&bad, 50).is_err(), "{bad:?}"); - } -} - -#[test] -fn corrupt_records_are_rejected() { - let s = settings(8, 1); - let mut rng = TrainingRandom::from(PyRandom::new(10)); - let mut ars = Ars::default(); - let (_, good) = into_record(ars.start(&seed(), &s, &mut rng, MATH, true)); - good.validate().unwrap(); - let edits: [&dyn Fn(&mut ArsRecord); 4] = [ - &|r| { - r.theta.pop(); - }, - &|r| r.sampling.nu = 0.0, - &|r| r.sampling.population = 2, - &|r| r.shape = vec![3], - ]; - for edit in edits { - let mut broken = good.clone(); - edit(&mut broken); - assert!(broken.validate().is_err()); - } -} - -/// Scores that rank the first pair's plus probe best and the rest alike. -fn fixed_scores(len: usize) -> Vec { - let mut scores = vec![0.0; len]; - scores[1] = 10.0; - scores -} - -fn next_with(ars: &mut Ars, cars: &[Network], s: &EvolutionSettings, seed: i64) -> Vec { - let results = agents(cars, &|_| 0.0); - let mut rng = TrainingRandom::from(PyRandom::new(seed)); - ars.next( - &results, - Some(fixed_scores(cars.len())), - s, - &mut rng, - &mut Vec::new(), - MATH, - false, - ) - .generation - .networks -} - -#[test] -fn the_step_divides_by_the_noise_the_population_was_sampled_with() { - let run = |later_nu: f64| { - let s = settings(10, 0); - let (mut ars, mut rng) = (Ars::default(), TrainingRandom::from(PyRandom::new(1))); - let cars = ars.start(&seed(), &s, &mut rng, MATH, false).generation.networks; - let mut changed = s.clone(); - changed.ars.nu = later_nu; - next_with(&mut ars, &cars, &changed, 2); - ars.theta - }; - // The step happens before the new population is sampled, whatever nu says by then. - assert_eq!(run(0.1), run(0.001)); -} - -#[test] -fn a_rebuilt_search_keeps_the_noise_it_sampled_with() { - let s = settings(10, 0); - let mut rng = TrainingRandom::from(PyRandom::new(1)); - let (networks, record) = into_record(Ars::default().start(&seed(), &s, &mut rng, MATH, true)); - let mut other = s.clone(); - other.ars.nu = 0.001; - let (_, _, mut a) = record.rebuild(); - let (_, _, mut b) = record.rebuild(); - next_with(&mut a, &networks, &s, 3); - next_with(&mut b, &networks, &other, 3); - assert_eq!(a.theta, b.theta); -} - -#[test] -fn max_weight_bounds_the_search_point_and_elites_even_without_a_step() { - let mut s = settings(10, 2); - s.ars.max_weight = 0.01; - let (mut ars, mut rng) = (Ars::default(), TrainingRandom::from(PyRandom::new(1))); - let within = |cars: &[Network]| cars.iter().all(|n| n.params.iter().all(|p| p.abs() <= 0.01)); - let first = ars.start(&seed(), &s, &mut rng, MATH, false).generation.networks; - assert!(within(&first), "the first population obeys the limit"); - // Tied scores skip the step; the elites and the point still obey the limit. - let second = next_with(&mut ars, &first, &s, 2); - assert!(within(&second)); - // A tighter limit later applies to the pool as well. - s.ars.max_weight = 0.005; - let third = next_with(&mut ars, &second, &s, 3); - assert!(third.iter().all(|n| n.params.iter().all(|p| p.abs() <= 0.005))); -} - -#[test] -fn an_edited_probe_makes_the_search_start_from_the_best_car() { - let s = settings(10, 0); - let (mut ars, mut rng) = (Ars::default(), TrainingRandom::from(PyRandom::new(1))); - let mut cars = ars.start(&seed(), &s, &mut rng, MATH, false).generation.networks; - cars[3].params[0] += 1.0; - ars.invalidate(); - next_with(&mut ars, &cars, &s, 2); - assert_eq!(ars.theta, cars[1].params, "the leader of the fixed scores"); -} diff --git a/github-simulator/source/src/training/batch_evaluation.rs b/github-simulator/source/src/training/batch_evaluation.rs deleted file mode 100644 index 961e86d220132002b93838b83ebdf688cefe8656..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/training/batch_evaluation.rs +++ /dev/null @@ -1,190 +0,0 @@ -//! Equal-weight evaluation of one ordered population on several tracks. -use crate::training::evolution::{reward_values, AgentResult, RewardSpec}; - -pub struct BatchEvaluation { - tracks: usize, - rewards: Vec, - totals: Vec<[f64; 14]>, - updates: Vec, - lap_counts: Vec, - lap_sums: Vec, -} - -pub struct BatchSummary { - pub fitness: Vec, - pub mean_scores: Vec, - pub metric_totals: Vec<[f64; 14]>, - pub update_counts: Vec, - pub lap_counts: Vec, - pub lapped_all_tracks: usize, - pub best_mean_lap_s: Option, -} - -impl BatchEvaluation { - pub fn new(population: usize) -> Self { - assert!(population > 0); - Self { - tracks: 0, - rewards: vec![0.0; population], - totals: vec![[0.0; 14]; population], - updates: vec![0; population], - lap_counts: vec![0; population], - lap_sums: vec![0.0; population], - } - } - - pub fn record(&mut self, results: &[AgentResult], specs: &[RewardSpec]) { - assert_eq!(results.len(), self.rewards.len()); - let rewards = reward_values(results, specs); - for (i, result) in results.iter().enumerate() { - self.rewards[i] += rewards[i]; - self.updates[i] += result.update_count; - for (metric, value) in result.metrics.iter().enumerate() { - if metric == 10 { - self.totals[i][metric] = value.unwrap_or(0.0); - } else if metric != 12 && metric != 13 { - self.totals[i][metric] += value.unwrap_or(0.0); - } - } - if let Some(performance) = result.metrics[12].filter(|v| *v > 0.0) { - self.lap_counts[i] += 1; - self.lap_sums[i] += 1.0 / performance; - } - } - self.tracks += 1; - } - - pub fn finish(self) -> BatchSummary { - assert!(self.tracks > 0); - let count = self.tracks as f64; - let complete_laps: Vec = self - .lap_counts - .iter() - .zip(&self.lap_sums) - .filter(|(laps, _)| **laps == self.tracks) - .map(|(_, sum)| sum / count) - .collect(); - let best_mean_lap_s = complete_laps.into_iter().reduce(f64::min); - let lapped_all_tracks = self.lap_counts.iter().filter(|&&laps| laps == self.tracks).count(); - let mut totals = self.totals; - for (i, row) in totals.iter_mut().enumerate() { - // Non-additive metrics retain their definitions over the full evaluation. - row[12] = if self.lap_counts[i] == self.tracks { - count / self.lap_sums[i] - } else { - 0.0 - }; - row[13] = if row[3] != 0.0 { - row[0] / (row[3] * (5.0 / 384.0)) - } else { - 0.0 - }; - } - BatchSummary { - fitness: self.rewards.into_iter().map(|value| value / count).collect(), - mean_scores: totals.iter().map(|row| row[0] / count).collect(), - metric_totals: totals, - update_counts: self.updates, - lap_counts: self.lap_counts, - lapped_all_tracks, - best_mean_lap_s, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::nn::network::Network; - - #[test] - fn consistency_beats_a_specialist_and_missing_laps_are_not_zero_times() { - let network = Network::from_vector(&[1, 1], vec![0.0; 2]); - let specs = vec![RewardSpec { - metric: "total_score".into(), - weight: 100, - kind: "default".into(), - }]; - let mut batch = BatchEvaluation::new(3); - for scores in [[1000.0, 20.0, 0.0], [0.0, 20.0, 30.0], [0.0, 20.0, 30.0]] { - let results: Vec<_> = scores - .into_iter() - .enumerate() - .map(|(i, score)| { - let mut metrics = [Some(0.0); 14]; - metrics[0] = Some(score); - metrics[10] = Some(7.0); - metrics[12] = if i == 0 { None } else { Some(0.1) }; - AgentResult { - network: &network, - metrics, - update_count: 10, - } - }) - .collect(); - batch.record(&results, &specs); - } - let summary = batch.finish(); - assert_eq!(summary.fitness, vec![1.0 / 3.0, 0.5, 2.0 / 3.0]); - assert!(summary.fitness[1] > summary.fitness[0]); - assert_eq!(summary.lapped_all_tracks, 2); - assert_eq!(summary.best_mean_lap_s, Some(10.0)); - assert_eq!(summary.update_counts, vec![30; 3]); - assert_eq!(summary.metric_totals[0][10], 7.0); - assert_eq!(summary.lap_counts[0], 0); - } - - #[test] - fn mixed_signed_and_average_rewards_are_combined_after_each_track_is_ranked() { - let network = Network::from_vector(&[1, 1], vec![0.0; 2]); - let specs = vec![ - RewardSpec { - metric: "total_score".into(), - weight: 60, - kind: "average".into(), - }, - RewardSpec { - metric: "collision_count".into(), - weight: -20, - kind: "default".into(), - }, - RewardSpec { - metric: "best_lap_performance".into(), - weight: 20, - kind: "default".into(), - }, - ]; - let mut batch = BatchEvaluation::new(3); - let mut expected = vec![0.0; 3]; - for track in 0..2 { - let results: Vec<_> = (0..3) - .map(|i| { - let mut metrics = [Some(0.0); 14]; - metrics[0] = Some([[90.0, 60.0, 30.0], [10.0, 20.0, 30.0]][track][i]); - metrics[11] = Some((i * 10) as f64); - metrics[12] = if i == track { - None - } else { - Some(1.0 / (10.0 + i as f64)) - }; - AgentResult { - network: &network, - metrics, - update_count: [100, 10, 20][i], - } - }) - .collect(); - for (sum, reward) in expected.iter_mut().zip(reward_values(&results, &specs)) { - *sum += reward / 2.0; - } - batch.record(&results, &specs); - } - let summary = batch.finish(); - assert_eq!(summary.fitness, expected); - assert_eq!(summary.mean_scores, vec![50.0, 40.0, 30.0]); - assert_eq!(summary.lap_counts, vec![1, 1, 2]); - // Two different fast drivers each miss a track; only the third is eligible. - assert_eq!(summary.lapped_all_tracks, 1); - assert_eq!(summary.best_mean_lap_s, Some(12.0)); - } -} diff --git a/github-simulator/source/src/training/evaluation.rs b/github-simulator/source/src/training/evaluation.rs deleted file mode 100644 index d72d8033a146da399d5baad28fa6d926bbe89cb4..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/training/evaluation.rs +++ /dev/null @@ -1,467 +0,0 @@ -//! CPU evaluation of one frozen model. Each track gets a new runner and car. -use crate::{ - math::profile::MathProfile, - nn::network::Network, - track::world::{vector, World}, - training::evolution::EvolutionSettings, - training::pyrandom::PyRandom, - training::{sensor_type, vision_angle, SensorLayout, TrainingRunner}, -}; -use serde_json::{json, Value}; -use sha2::{Digest, Sha256}; -use std::{ - fs, - path::{Path, PathBuf}, - sync::Arc, - time::Instant, -}; - -pub fn sha256(bytes: &[u8]) -> String { - format!("{:x}", Sha256::digest(bytes)) -} -fn load(path: &Path) -> Result<(Vec, Value), String> { - let bytes = fs::read(path).map_err(|e| format!("{}: {e}", path.display()))?; - let data = serde_json::from_slice(&bytes).map_err(|e| format!("{}: {e}", path.display()))?; - Ok((bytes, data)) -} -fn strings(value: &Value, label: &str) -> Result, String> { - value - .as_array() - .ok_or_else(|| format!("missing {label}"))? - .iter() - .map(|v| v.as_str().map(str::to_owned).ok_or_else(|| format!("invalid {label}"))) - .collect() -} -fn suite_file(root: &Path, row: &Value, key: &str) -> Result { - let relative = row[key].as_str().ok_or_else(|| format!("missing {key}"))?; - let path = PathBuf::from(relative); - if path.is_absolute() || path.components().any(|p| !matches!(p, std::path::Component::Normal(_))) { - return Err(format!("{key} must be relative to the suite directory")); - } - let (bytes, data) = load(&root.join(path))?; - if row[format!("{key}_sha256")].as_str() != Some(sha256(&bytes).as_str()) { - return Err(format!("{key} hash mismatch")); - } - Ok(data) -} - -fn finite_model_numbers(value: &Value) -> bool { - match value { - Value::Number(_) => value.as_f64().is_some_and(|x| x.is_finite() && (x as f32).is_finite()), - Value::Array(values) => values.iter().all(finite_model_numbers), - Value::Object(values) => values.values().all(finite_model_numbers), - _ => true, - } -} - -fn valid_vector(value: &Value) -> bool { - value.as_array().is_some_and(|v| { - v.len() == 2 - && v.iter() - .all(|x| x.as_f64().is_some_and(|x| x.is_finite() && x.abs() < 100_000.0)) - }) -} -fn validate_scene(scene: &Value) -> Result<(), String> { - if !finite_model_numbers(scene) { - return Err("scene parameters must be finite".into()); - } - let vehicle = &scene["vehicle"]; - if !vehicle.is_object() || !scene["track"].is_object() { - return Err("scene needs vehicle and track objects".into()); - } - for key in [ - "mass", - "inertia_server", - "shape_rotation", - "grip", - "steering_speed", - "air_resistance", - "max_velocity", - "linear_damp", - "angular_damp", - ] { - let number = vehicle[key] - .as_f64() - .filter(|x| x.is_finite()) - .ok_or_else(|| format!("invalid vehicle {key}"))?; - if ["mass", "inertia_server"].contains(&key) && number <= 0.0 { - return Err(format!("vehicle {key} must be positive")); - } - } - if !valid_vector(&vehicle["shape_size"]) - || vehicle["shape_size"] - .as_array() - .unwrap() - .iter() - .any(|v| v.as_f64().unwrap() <= 0.0) - || !vehicle["center_steering"].is_boolean() - { - return Err("invalid vehicle shape or steering".into()); - } - for key in ["shape_position", "center_of_mass"] { - if vehicle.get(key).is_some_and(|v| !valid_vector(v)) { - return Err(format!("invalid vehicle {key}")); - } - } - if let Some(basis) = vehicle.get("shape_basis") { - if !basis - .as_array() - .is_some_and(|v| v.len() == 2 && v.iter().all(valid_vector)) - { - return Err("invalid vehicle basis".into()); - } - } - let surfaces = vehicle["surfaces"].as_object().ok_or("missing vehicle surfaces")?; - for surface in surfaces.values() { - if !surface.is_object() { - return Err("invalid vehicle surface".into()); - } - for key in ["grip", "power", "steering"] { - if surface - .get(key) - .is_some_and(|v| !v.as_f64().is_some_and(f64::is_finite)) - { - return Err("invalid surface parameter".into()); - } - } - } - let wheels = vehicle["wheels"] - .as_array() - .filter(|v| !v.is_empty()) - .ok_or("missing vehicle wheels")?; - for wheel in wheels { - if !valid_vector(&wheel["position"]) || !wheel["steering"].is_boolean() { - return Err("invalid wheel pose".into()); - } - for key in ["max_angle_deg", "power", "brake_power", "handbrake_power"] { - if !wheel[key].as_f64().is_some_and(f64::is_finite) { - return Err(format!("invalid wheel {key}")); - } - } - } - let track = &scene["track"]; - let path = track["path"] - .as_array() - .filter(|v| v.len() >= 2) - .ok_or("missing lap path")?; - if !path.iter().all(valid_vector) { - return Err("invalid lap path".into()); - } - if let Some(forward) = track.get("path_forward") { - if !forward - .as_array() - .is_some_and(|v| (v.is_empty() || v.len() == path.len()) && v.iter().all(valid_vector)) - { - return Err("path forward dimensions differ".into()); - } - } - for key in ["tile_map_position", "tile_size"] { - if track.get(key).is_some_and(|v| !valid_vector(v)) { - return Err(format!("invalid track {key}")); - } - } - for key in ["polygons", "physics_shapes"] { - if let Some(shapes) = track.get(key) { - let shapes = shapes.as_array().ok_or("invalid track shapes")?; - for shape in shapes { - if !shape["points"] - .as_array() - .is_some_and(|v| v.len() >= 3 && v.iter().all(valid_vector)) - { - return Err("invalid polygon points".into()); - } - for key in ["origin", "tile"] { - if shape.get(key).is_some_and(|v| !valid_vector(v)) { - return Err("invalid shape origin".into()); - } - } - if let Some(local) = shape.get("local_points") { - if !local.as_array().is_some_and(|v| { - v.len() == shape["points"].as_array().unwrap().len() - && v.iter().all(|p| { - valid_vector(p) - || (p.is_object() - && p["x"].as_f64().is_some_and(f64::is_finite) - && p["y"].as_f64().is_some_and(f64::is_finite)) - }) - }) { - return Err("invalid local polygon".into()); - } - } - } - } - } - if let Some(walls) = track.get("walls") { - if !walls.as_array().is_some_and(|v| { - v.iter() - .all(|p| p.as_array().is_some_and(|p| p.len() == 2 && p.iter().all(valid_vector))) - }) { - return Err("invalid track walls".into()); - } - } - if let Some(tiles) = track.get("tiles") { - let tiles = tiles.as_array().ok_or("invalid track tiles")?; - for tile in tiles { - if !valid_vector(&tile["coords"]) || !tile["surface"].as_str().is_some_and(|n| surfaces.contains_key(n)) { - return Err("invalid track tile".into()); - } - if tile - .get("connections") - .is_some_and(|v| !v.as_array().is_some_and(|v| v.iter().all(valid_vector))) - { - return Err("invalid tile connections".into()); - } - } - } - if track - .get("native_cell_order") - .is_some_and(|v| !v.as_array().is_some_and(|v| v.iter().all(valid_vector))) - { - return Err("invalid native cell order".into()); - } - if let Some(curve) = track.get("curve") { - if !curve["bake_interval"] - .as_f64() - .is_some_and(|x| x.is_finite() && x > 0.0) - || !curve["points"].as_array().is_some_and(|v| { - v.len() >= 2 - && v.iter().all(|p| { - ["position", "incoming", "outgoing"].iter().all(|key| { - valid_vector(&p[key]) - || (p[key].is_object() - && p[key]["x"].as_f64().is_some_and(f64::is_finite) - && p[key]["y"].as_f64().is_some_and(f64::is_finite)) - }) - }) - }) - { - return Err("invalid track curve".into()); - } - } - Ok(()) -} - -/// `math` is the profile asked for on the command line; a suite may name one -/// in `options.math_profile`. Without either, this machine's math is used. -pub fn evaluate( - network_path: &Path, - model_path: &Path, - suite_path: &Path, - report_path: &Path, - math: Option, -) -> Result { - let started = Instant::now(); - let (candidate_bytes, candidate) = load(network_path)?; - let (model_bytes, model) = load(model_path)?; - if !finite_model_numbers(&model) { - return Err("sensor model parameters must be finite".into()); - } - let (suite_bytes, suite) = load(suite_path)?; - if !model["vehicle_type"] - .as_str() - .is_some_and(|v| ["rally", "formula", "truck", "snowmobile"].contains(&v.to_lowercase().as_str())) - { - return Err("invalid model vehicle".into()); - } - if suite["version"] != 1 { - return Err("unsupported suite version".into()); - } - let shape = candidate["shape"].as_array().ok_or("missing shape")?; - if !(2..=10).contains(&shape.len()) || shape.iter().any(|n| n.as_u64().is_none_or(|n| n == 0 || n > 1024)) { - return Err("invalid network dimensions".into()); - } - let network = Network::try_from_game_export(&candidate)?; - if network.params.iter().any(|x| !x.is_finite()) { - return Err("parameters must be finite".into()); - } - let inputs = strings(&candidate["inputs"], "ordered inputs")?; - let outputs = strings(&candidate["outputs"], "ordered outputs")?; - if inputs.len() != network.shape[0] || outputs.len() != *network.shape.last().unwrap() { - return Err("network and ordered I/O dimensions differ".into()); - } - if inputs - .iter() - .any(|n| vision_angle(n).is_none() && sensor_type(n).is_none()) - { - return Err("unknown sensor name".into()); - } - for name in &inputs { - if let Some(angle) = vision_angle(name) { - if !angle.is_finite() || angle.fract() != 0.0 || angle.abs() > 180.0 { - return Err("invalid ray angle".into()); - } - } - } - let controls: Vec<_> = outputs.iter().map(|n| n.to_lowercase()).collect(); - if controls != strings(&model["outputs"], "model outputs")? - || controls - .iter() - .any(|n| !["steering", "acceleration", "brake", "handbrake", "boost"].contains(&n.as_str())) - { - return Err("ordered model outputs differ from candidate".into()); - } - if let Some(exact) = model.get("sensor_layout") { - if SensorLayout::from_ordered(exact)?.names != inputs { - return Err("ordered sensor names differ".into()); - } - } else { - let sensors: Vec<_> = inputs - .iter() - .filter_map(|n| sensor_type(n)) - .map(str::to_owned) - .collect(); - // Models use snake_case sensor names; aliases describe the same ordered inputs. - let model_sensors = strings(&model["sensors"], "model sensors")?; - let normalized: Vec<_> = model_sensors - .iter() - .map(|n| { - sensor_type(n) - .unwrap_or_else(|| match n.as_str() { - "acceleration_front" => "accelerationFront", - "acceleration_side" => "accelerationSide", - "distance_from_wall" => "distanceFromWall", - "angular_velocity" => "angularVelocity", - "boost_capacity" => "boostCapacity", - "correct_direction" => "correctDirection", - "velocity_front" => "velocityFront", - "velocity_side" => "velocitySide", - "track_curvature" => "trackCurvature", - "wheel_angle" => "wheelAngle", - _ => n, - }) - .to_owned() - }) - .collect(); - if sensors != normalized { - return Err("ordered model sensors differ from candidate".into()); - } - let rays = model["vision"].as_array().ok_or("missing model vision")?; - if !rays.is_empty() - && rays.iter().map(|r| r["angle"].as_f64()).collect::>() - != inputs - .iter() - .filter_map(|n| vision_angle(n)) - .map(Some) - .collect::>() - { - return Err("ordered model rays differ from candidate".into()); - } - for ray in rays { - if !ray["angle"].as_f64().is_some_and(|a| a.is_finite() && a.abs() <= 180.0) - || !ray["length"].as_f64().is_some_and(|l| l.is_finite() && l > 0.0) - { - return Err("invalid model vision".into()); - } - } - } - let options = &suite["options"]; - let batch_count = options["batch_count"] - .as_u64() - .filter(|n| (1..=8).contains(n)) - .ok_or("invalid inference batch count")? as usize; - if options["backend"] != "cpu" { - return Err("suite backend must be cpu".into()); - } - let wall = options["eliminate_on_wall"] - .as_bool() - .ok_or("missing wall elimination rule")?; - let idle = options["idle_eliminate"] - .as_bool() - .ok_or("missing idle elimination rule")?; - let suite_math = match options.get("math_profile") { - None => None, - Some(name) => Some( - name.as_str() - .ok_or("invalid suite math profile")? - .parse::()?, - ), - }; - let math = match (math, suite_math) { - (Some(a), Some(b)) if a != b => return Err(format!("the suite uses math profile {b}, not {a}")), - (a, b) => a.or(b).unwrap_or_else(MathProfile::detect), - }; - let tracks = suite["tracks"] - .as_array() - .filter(|v| !v.is_empty()) - .ok_or("suite needs tracks")?; - let root = suite_path.parent().unwrap_or(Path::new(".")); - // Verify all files before simulation and avoid a partial successful report. - let mut prepared = Vec::new(); - let mut ids = std::collections::HashSet::new(); - for row in tracks { - let id = row["id"].as_str().filter(|v| !v.is_empty()).ok_or("missing track ID")?; - if !ids.insert(id) { - return Err("duplicate suite track ID".into()); - } - let scene = suite_file(root, row, "scene")?; - let spawn = suite_file(root, row, "spawn")?; - validate_scene(&scene)?; - if scene["vehicle"]["type"].as_str().map(str::to_lowercase) - != model["vehicle_type"].as_str().map(str::to_lowercase) - || model["vehicle_type"].as_str().map(str::to_lowercase) != suite["vehicle"].as_str().map(str::to_lowercase) - { - return Err("suite/model vehicle mismatch".into()); - } - let frame = spawn["frames"] - .as_array() - .and_then(|v| v.first()) - .ok_or("missing spawn frame")?; - if !frame["rotation"].as_f64().is_some_and(f64::is_finite) - || !frame["position"] - .as_array() - .is_some_and(|v| v.len() == 2 && v.iter().all(|x| x.as_f64().is_some_and(f64::is_finite))) - { - return Err("invalid spawn pose".into()); - } - let ticks = row["ticks"] - .as_u64() - .filter(|n| (1..=36000).contains(n)) - .ok_or("invalid track tick cap")?; - prepared.push((row, scene, frame.clone(), ticks)); - } - let mut results = Vec::new(); - for (row, scene, spawn, ticks) in prepared { - let track_started = Instant::now(); - // Malformed scene exports report an error instead of aborting the CLI. - let world = - std::panic::catch_unwind(|| World::from_scene_with(&scene, math)).map_err(|_| "invalid suite scene")?; - let one_lap_score = world.track.path_length() * (5.0 / 384.0); - if !one_lap_score.is_finite() || one_lap_score <= 0.0 { - return Err("track has no lap path".into()); - } - let layout = SensorLayout::from_exports(&candidate, &model, math); - let settings = EvolutionSettings { - population: 1, - ..Default::default() - }; - let mut runner = TrainingRunner::new( - Arc::new(world), - vector(&spawn["position"]), - spawn["rotation"].as_f64().unwrap(), - layout, - &outputs, - settings, - PyRandom::new(0), - batch_count, - 0, - wall, - idle, - ); - runner.resume(std::slice::from_ref(&network), 0); - runner.advance_generation(ticks); - let stats = &runner.agents[0].stats; - results.push(json!({"id": row["id"], "score": stats.total_score, "one_lap_score": one_lap_score, - "normalized_progress": stats.total_score / one_lap_score, - "lap_complete": stats.best_lap_time.is_some_and(|t| t > 0.0), - "best_lap_s": stats.best_lap_time.filter(|&t| t > 0.0), "collisions": stats.collision_count, - "simulated_ticks": runner.tick, "tick_cap": ticks, "elapsed_seconds": track_started.elapsed().as_secs_f64()})); - } - let exe = std::env::current_exe().map_err(|e| e.to_string())?; - let report = json!({"version": 1, "model_sha256": sha256(&model_bytes), "candidate_sha256": sha256(&candidate_bytes), "suite_sha256": sha256(&suite_bytes), - "simulator_sha256": sha256(&fs::read(exe).map_err(|e| e.to_string())?), "options": options, "math_profile": math, - "tracks": results, "elapsed_seconds": started.elapsed().as_secs_f64()}); - let temp = report_path.with_extension(format!("json.{}.tmp", std::process::id())); - fs::write(&temp, serde_json::to_vec_pretty(&report).unwrap()).map_err(|e| e.to_string())?; - fs::rename(&temp, report_path).map_err(|e| e.to_string())?; - Ok(report) -} diff --git a/github-simulator/source/src/training/evolution.rs b/github-simulator/source/src/training/evolution.rs deleted file mode 100644 index 6a4c0a6a9cf481feae4695a0e7b80c7b784b9701..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/training/evolution.rs +++ /dev/null @@ -1,1182 +0,0 @@ -//! Rewards, selection, crossover, and -//! mutation with CPython-compatible random draws. - -use crate::math::profile::MathProfile; -use crate::nn::network::Network; -use crate::training::pyrandom::PyRandom; -use rayon::prelude::*; -use serde_json::{json, Value}; - -pub(crate) trait DecisionSource { - fn random(&mut self) -> f64; - fn randrange(&mut self, bound: usize) -> usize; - fn game_arithmetic(&self) -> bool { - false - } -} -impl DecisionSource for PyRandom { - fn random(&mut self) -> f64 { - PyRandom::random(self) - } - fn randrange(&mut self, bound: usize) -> usize { - PyRandom::randrange(self, bound) - } -} -impl DecisionSource for crate::training::game_random::GameRandom { - fn random(&mut self) -> f64 { - crate::training::game_random::GameRandom::random(self) - } - fn randrange(&mut self, bound: usize) -> usize { - crate::training::game_random::GameRandom::randrange(self, bound) - } - fn game_arithmetic(&self) -> bool { - true - } -} - -pub const METRIC_NAMES: [&str; 14] = [ - "total_score", - "total_speed", - "total_abs_steering", - "total_actual_distance", - "total_drifting_amount", - "total_momentum_change_frequency", - "total_throttle_change_frequency", - "total_braking_frequency", - "total_distance_from_wall", - "total_distance_from_center", - "network_novelty", - "collision_count", - "best_lap_performance", - "total_racing_line_efficiency", -]; - -pub(crate) type Metrics = [Option; 14]; - -const AVERAGE_METRICS: [&str; 10] = [ - "total_score", - "total_speed", - "total_abs_steering", - "total_drifting_amount", - "total_actual_distance", - "total_distance_from_wall", - "total_distance_from_center", - "total_momentum_change_frequency", - "total_throttle_change_frequency", - "total_braking_frequency", -]; -const AVERAGE_ONLY_METRICS: [&str; 6] = [ - "total_abs_steering", - "total_distance_from_wall", - "total_distance_from_center", - "total_momentum_change_frequency", - "total_throttle_change_frequency", - "total_braking_frequency", -]; - -pub(crate) fn metric_index(name: &str) -> Option { - METRIC_NAMES.iter().position(|&m| m == name) -} - -#[derive(Clone, Debug, PartialEq, serde::Deserialize)] -#[serde(deny_unknown_fields)] -pub struct RewardSpec { - pub metric: String, - pub weight: i64, - #[serde(rename = "type")] - pub kind: String, -} - -#[derive(Clone, Copy)] -pub struct AgentResult<'a> { - pub network: &'a Network, - pub metrics: Metrics, - pub update_count: u64, -} - -impl AgentResult<'_> { - pub(crate) fn value(&self, spec: &RewardSpec) -> Option { - let value = self.metrics[metric_index(&spec.metric)?]?; - let metric = spec.metric.as_str(); - if (spec.kind == "average" || AVERAGE_ONLY_METRICS.contains(&metric)) && AVERAGE_METRICS.contains(&metric) { - return Some(if self.update_count != 0 { - value / (0.1 * self.update_count as f64) - } else { - 0.0 - }); - } - Some(value) - } -} - -/// Parameters of the `"ars"` algorithm (see `training::ars`). -#[derive(Clone, Debug, PartialEq, serde::Deserialize)] -#[serde(default, deny_unknown_fields)] -pub struct ArsSettings { - /// Standard deviation of the probe noise: probes are `theta +- nu * delta`. - pub nu: f64, - /// Step size of the search point. - pub alpha: f64, - /// Fraction of the antithetic pairs, best first, that steer the step. - pub top_frac: f64, - /// Best distinct cars carried over verbatim and re-evaluated each generation. - pub elite_count: usize, - /// Parameters are clamped to `[-max_weight, max_weight]`; 0 turns it off. - pub max_weight: f64, -} - -impl Default for ArsSettings { - fn default() -> Self { - ArsSettings { - nu: 0.05, - alpha: 0.05, - top_frac: 1.0, - elite_count: 3, - max_weight: 0.0, - } - } -} - -#[derive(Clone, Debug, serde::Deserialize)] -#[serde(default, deny_unknown_fields)] -pub struct EvolutionSettings { - /// `"ga"` (selection, crossover and mutation) or `"ars"`. - pub algorithm: String, - pub ars: ArsSettings, - pub population: usize, - pub selection_algorithm: String, - pub selection_size: usize, - pub crossover: String, - pub mutation_rate: f64, - pub adaptive_mutation: bool, - pub weight_decay: f64, - pub preserve_parents: String, - pub preserve_parents_size: usize, - pub rewards: Vec, -} - -impl Default for EvolutionSettings { - fn default() -> Self { - EvolutionSettings { - algorithm: "ga".into(), - ars: ArsSettings::default(), - population: 300, - selection_algorithm: "best".into(), - selection_size: 3, - crossover: "none".into(), - mutation_rate: 0.20000000298023224, - adaptive_mutation: false, - weight_decay: 0.0005000000237487257, - preserve_parents: "on_selection_size".into(), - preserve_parents_size: 3, - rewards: vec![RewardSpec { - metric: "total_score".into(), - weight: 100, - kind: "default".into(), - }], - } - } -} - -impl EvolutionSettings { - /// `EvolutionSettings.from_mcp`. - /// Panics on malformed settings; preserves trusted CLI/export coercions. - pub fn from_mcp(data: &Value) -> EvolutionSettings { - Self::try_from_mcp(data).unwrap_or_else(|e| panic!("{e}")) - } - - /// `from_mcp`, reporting malformed settings instead of panicking. - pub fn try_from_mcp(data: &Value) -> Result { - let data = data.get("settings").unwrap_or(data); - let mut s = EvolutionSettings::default(); - let bad = |key: &str| format!("invalid evolution settings: bad {key}"); - let int = |key: &str| -> Result, String> { - data.get(key) - .map(|v| v.as_f64().map(|x| x as usize).ok_or_else(|| bad(key))) - .transpose() - }; - let text = |key: &str| -> Result, String> { - data.get(key) - .map(|v| v.as_str().map(String::from).ok_or_else(|| bad(key))) - .transpose() - }; - if let Some(v) = text("algorithm")? { - s.algorithm = v; - } - if let Some(v) = data.get("ars") { - s.ars = serde_json::from_value(v.clone()).map_err(|e| format!("invalid evolution settings: ars: {e}"))?; - } - if let Some(v) = int("population")? { - s.population = v; - } - if let Some(v) = text("selection_algorithm")? { - s.selection_algorithm = v; - } - if let Some(v) = int("selection_size")? { - s.selection_size = v; - } - if let Some(v) = text("crossover")? { - s.crossover = v; - } - if let Some(v) = data.get("mutation_rate") { - s.mutation_rate = v.as_f64().ok_or_else(|| bad("mutation_rate"))?; - } - if let Some(v) = data.get("adaptive_mutation") { - s.adaptive_mutation = v.as_bool().ok_or_else(|| bad("adaptive_mutation"))?; - } - if let Some(v) = data.get("weight_decay") { - s.weight_decay = v.as_f64().ok_or_else(|| bad("weight_decay"))?; - } - if let Some(v) = text("preserve_parents")? { - s.preserve_parents = v; - } - if let Some(v) = int("preserve_parents_size")? { - s.preserve_parents_size = v; - } - if let Some(rewards) = data.get("rewards") { - s.rewards = rewards - .as_array() - .ok_or_else(|| bad("rewards"))? - .iter() - .map(|item| { - Ok(RewardSpec { - metric: item["metric"].as_str().ok_or_else(|| bad("reward metric"))?.into(), - weight: item["weight"].as_i64().ok_or_else(|| bad("reward weight"))?, - kind: item.get("type").and_then(Value::as_str).unwrap_or("default").into(), - }) - }) - .collect::>()?; - } - Ok(s) - } - - /// Validate creation settings after applying the embedding host's population override. - pub(crate) fn try_from_mcp_with_population( - data: &Value, - population: Option, - ) -> Result { - let fields = data.get("settings").unwrap_or(data); - for key in ["population", "selection_size", "preserve_parents_size"] { - if let Some(value) = fields.get(key) { - value - .as_f64() - .filter(|n| n.is_finite() && *n >= 0.0 && n.fract() == 0.0 && *n < usize::MAX as f64) - .ok_or_else(|| format!("invalid evolution settings: bad {key}"))?; - } - } - let mut settings = Self::try_from_mcp(data)?; - if let Some(population) = population { - settings.population = population; - } - settings.validate()?; - Ok(settings) - } - - pub(crate) fn validate(&self) -> Result<(), String> { - self.validate_algorithm()?; - if self.population == 0 || self.selection_size == 0 { - return Err("population and selection_size must be positive".into()); - } - if !["best", "tournament", "roulette"].contains(&self.selection_algorithm.as_str()) - || !["none", "single_point", "uniform"].contains(&self.crossover.as_str()) - || !["off", "on_selection_size", "on_custom"].contains(&self.preserve_parents.as_str()) - { - return Err("unknown selection, crossover or preservation mode".into()); - } - if !self.mutation_rate.is_finite() - || !(0.0..=10.0).contains(&self.mutation_rate) - || !self.weight_decay.is_finite() - || !(0.0..=1.0).contains(&self.weight_decay) - { - return Err("mutation_rate must be in 0..10 and weight_decay in 0..1".into()); - } - if self.rewards.is_empty() - || self.rewards.iter().any(|r| { - crate::training::evolution::metric_index(&r.metric).is_none() - || !["default", "average"].contains(&r.kind.as_str()) - }) - { - return Err("invalid reward metric or type".into()); - } - self.rewards.iter().try_fold(0i64, |total, r| { - r.weight - .checked_abs() - .and_then(|w| total.checked_add(w)) - .ok_or("reward weights overflow") - })?; - Ok(()) - } - - /// `dataclasses.asdict(settings)`. - pub fn to_json(&self) -> Value { - let mut value = self.base_json(); - if self.algorithm != "ga" { - let ars = &self.ars; - value["algorithm"] = json!(self.algorithm); - value["ars"] = json!({ - "nu": ars.nu, - "alpha": ars.alpha, - "top_frac": ars.top_frac, - "elite_count": ars.elite_count, - "max_weight": ars.max_weight, - }); - } - value - } - - /// Checks the fields of the chosen algorithm that the algorithms cannot - /// repair themselves. Anything is accepted for the GA, as before. - pub fn validate_algorithm(&self) -> Result<(), String> { - match self.algorithm.as_str() { - "ga" => Ok(()), - "ars" => crate::training::ars::validate(&self.ars, self.population), - other => Err(format!("unknown algorithm {other:?} (ga or ars)")), - } - } - - fn base_json(&self) -> Value { - json!({ - "population": self.population, - "selection_algorithm": self.selection_algorithm, - "selection_size": self.selection_size, - "crossover": self.crossover, - "mutation_rate": self.mutation_rate, - "adaptive_mutation": self.adaptive_mutation, - "weight_decay": self.weight_decay, - "preserve_parents": self.preserve_parents, - "preserve_parents_size": self.preserve_parents_size, - "rewards": self.rewards.iter().map(|r| json!({"metric": r.metric, "weight": r.weight, "type": r.kind})).collect::>(), - }) - } -} - -pub struct Generation { - pub networks: Vec, - pub preserved_count: usize, - pub rewards: Vec, -} - -fn rank_normalize(values: &[Option]) -> Vec { - let mut distinct: Vec = values.iter().flatten().copied().collect(); - distinct.sort_by(|a, b| a.partial_cmp(b).expect("reward values must not be NaN")); - distinct.dedup_by(|a, b| a == b); - if distinct.is_empty() { - return vec![0.0; values.len()]; - } - if distinct.len() == 1 { - return values.iter().map(|v| if v.is_some() { 0.5 } else { 0.0 }).collect(); - } - let top = (distinct.len() - 1) as f64; - values - .iter() - .map(|v| match v { - Some(v) => { - let i = distinct - .binary_search_by(|d| d.partial_cmp(v).unwrap()) - .expect("value in distinct set"); - i as f64 / top - } - None => 0.0, - }) - .collect() -} - -/// Relative reward used for selection, with signed weights and rank ties. -pub fn reward_values(agents: &[AgentResult], specs: &[RewardSpec]) -> Vec { - let mut result = vec![0.0; agents.len()]; - let total_weight: i64 = specs.iter().map(|s| s.weight.abs()).sum(); - if total_weight == 0 { - return result; - } - for spec in specs { - let values: Vec> = agents.iter().map(|a| a.value(spec)).collect(); - let ranks = rank_normalize(&values); - let ratio = spec.weight as f64 / total_weight as f64; - for (index, rank) in ranks.into_iter().enumerate() { - result[index] += rank * ratio; - } - } - result -} - -/// The adaptive mutation scale; its log is the C runtime's (.NET Math.Log). -pub fn mutation_factor(network: &Network, algorithm: &str, math: MathProfile) -> f64 { - let total = network.params.len() as f64; - let layers = network.shape.len() as f64; - let log = math.log(total + 1.0); - let factor = match algorithm { - "xavier" => 0.516777 / (0.262982 + 0.050982 * log - 0.000139 * layers - 0.000278 * layers * layers), - "gaussian" => 1.646116 / (0.088941 + 0.350773 * log - 0.03423 * layers + 0.001063 * layers * layers), - "uniform" => 0.272879 / (-0.074226 + 0.07021 * log - 0.000777 * layers - 0.000321 * layers * layers), - _ => return 1.0, - }; - if factor > 0.0 { - let low = if factor > 0.1 { factor } else { 0.1 }; - if low < 10.0 { - low - } else { - 10.0 - } - } else { - 1.0 - } -} - -#[cfg(test)] -pub(crate) fn mutate_xavier(network: &Network, rate: f64, rng: &mut PyRandom, normalize: bool) -> Network { - if rate <= 0.0 { - return network.clone(); - } - let normals = rng.standard_normals(network.params.len()); - mutate_xavier_with(network.clone(), rate, normalize, &normals) -} - -/// `mutate_xavier` with its `gauss` draws supplied as standard normals `z` -/// (`gauss(0, sigma) == 0.0 + z * sigma`), in parameter order. -// Index order and parameter updates mirror the reference reproduction loop. -#[allow(clippy::needless_range_loop)] -fn mutate_xavier_with(mut network: Network, rate: f64, normalize: bool, normals: &[f64]) -> Network { - let scale = rate - * if normalize { - mutation_factor(&network, "xavier", MathProfile::Proton) - } else { - 1.0 - }; - let mut position = 0; - let shape = network.shape.clone(); - for w in shape.windows(2) { - let (inputs, outputs) = (w[0], w[1]); - let deviation = (2.0 / (inputs + outputs) as f64).sqrt() * scale; - for index in position..position + inputs * outputs { - network.params[index] += 0.0 + normals[index] * deviation; - } - position += inputs * outputs; - let bias_deviation = 0.1 * scale; - for index in position..position + outputs { - network.params[index] += 0.0 + normals[index] * bias_deviation; - } - position += outputs; - } - network -} - -/// `mutate_xavier_with` followed by `weight_decay` for a child that is a -/// plain copy of `source` (crossover "none"): the copy, the mutation and the -/// decay are one pass writing a fresh vector, instead of a clone and two -/// read-modify-write passes. Each parameter sees the same operations in the -/// same order: `p + (0.0 + z * deviation)`, then `* (1.0 - decay)` if decay -/// is positive. -fn mutated_copy(source: &Network, rate: f64, normalize: bool, normals: &[f64], decay: f64) -> Network { - let scale = rate - * if normalize { - mutation_factor(source, "xavier", MathProfile::Proton) - } else { - 1.0 - }; - let keep = 1.0 - decay; - let finish = move |v: f64| if decay > 0.0 { v * keep } else { v }; - let mut params = Vec::with_capacity(source.params.len()); - let mut position = 0; - for w in source.shape.windows(2) { - let (inputs, outputs) = (w[0], w[1]); - let deviation = (2.0 / (inputs + outputs) as f64).sqrt() * scale; - let weights = position..position + inputs * outputs; - params.extend( - source.params[weights.clone()] - .iter() - .zip(&normals[weights]) - .map(|(&p, &z)| finish(p + (0.0 + z * deviation))), - ); - position += inputs * outputs; - let bias_deviation = 0.1 * scale; - let biases = position..position + outputs; - params.extend( - source.params[biases.clone()] - .iter() - .zip(&normals[biases]) - .map(|(&p, &z)| finish(p + (0.0 + z * bias_deviation))), - ); - position += outputs; - } - Network { - shape: source.shape.clone(), - params, - } -} - -pub(crate) fn weight_decay(mut network: Network, rate: f64) -> Network { - if rate > 0.0 { - let keep = 1.0 - rate; - network.params.iter_mut().for_each(|v| *v *= keep); - } - network -} - -/// Indices sorted by descending score; Python's reverse sort keeps ties in order. -fn ranked(scores: &[f64]) -> Vec { - let mut indices: Vec = (0..scores.len()).collect(); - indices.sort_by(|&a, &b| scores[b].partial_cmp(&scores[a]).unwrap_or(std::cmp::Ordering::Equal)); - indices -} - -fn select(scores: &[f64], settings: &EvolutionSettings, rng: &mut impl DecisionSource) -> Vec { - let count = settings.selection_size; - let indices: Vec = match settings.selection_algorithm.as_str() { - "best" => ranked(scores).into_iter().take(count).collect(), - "tournament" => (0..count) - .map(|_| { - let mut best = rng.randrange(scores.len()); - for _ in 1..5 { - let i = rng.randrange(scores.len()); - if scores[i] > scores[best] { - best = i; - } - } - best - }) - .collect(), - "roulette" => { - let low = scores - .iter() - .copied() - .fold(f64::INFINITY, |a, b| if b < a { b } else { a }); - let high = scores - .iter() - .copied() - .fold(f64::NEG_INFINITY, |a, b| if b > a { b } else { a }); - let equal = if rng.game_arithmetic() { - let (a, b) = (low as f32, high as f32); - a == b || (a - b).abs() < (1e-6f32 * a.abs()).max(1e-6) - } else { - high == low - }; - let weights: Vec = scores - .iter() - .map(|&s| if !equal { (s - low) / (high - low) } else { 1.0 }) - .collect(); - let total = if rng.game_arithmetic() { - weights.iter().fold(0.0, |a, b| a + b) - } else { - crate::math::pymath::py_sum(weights.iter().copied()) - }; - (0..count) - .map(|_| { - let draw = rng.random(); - let mut cumulative = 0.0; - for (index, &weight) in weights.iter().enumerate() { - cumulative += weight / total; - if draw <= cumulative { - return index; - } - } - scores.len() - 1 - }) - .collect() - } - other => panic!("unknown selection algorithm: {other}"), - }; - indices -} - -pub(crate) const CROSSOVERS: [&str; 3] = ["none", "single_point", "uniform"]; - -/// The cars a reproduction breeds from, by index into the scored -/// population: `selected` in draw order (repeats kept), `preserved` best -/// first, and `best`, the highest-scoring car. -#[derive(Clone, Debug, PartialEq)] -pub(crate) struct Choice { - pub(crate) selected: Vec, - pub(crate) preserved: Vec, - pub(crate) best: usize, -} - -fn preserve_size(settings: &EvolutionSettings) -> usize { - match settings.preserve_parents.as_str() { - "off" => 0, - "on_selection_size" => settings.selection_size, - "on_custom" => settings.preserve_parents_size, - other => panic!("unknown parent preservation mode: {other}"), - } -} - -/// Selection and parent preservation: the first half of `reproduce`, and its -/// only random draws before crossover. `scores` must not be empty. -pub(crate) fn choose(scores: &[f64], settings: &EvolutionSettings, rng: &mut impl DecisionSource) -> Choice { - let selected = select(scores, settings, rng); - let ranked = ranked(scores); - let preserved = ranked - .iter() - .copied() - .take(preserve_size(settings).min(settings.population)) - .collect(); - Choice { - selected, - preserved, - best: ranked[0], - } -} - -/// The settings `breed` and `breed_game` read, as checkpoints store them. -#[derive(Clone, Debug, PartialEq)] -pub(crate) struct Breeding { - pub(crate) population: usize, - pub(crate) crossover: String, - pub(crate) mutation_rate: f64, - pub(crate) adaptive_mutation: bool, - pub(crate) weight_decay: f64, -} - -impl Breeding { - pub(crate) fn of(s: &EvolutionSettings) -> Breeding { - Breeding { - population: s.population, - crossover: s.crossover.clone(), - mutation_rate: s.mutation_rate, - adaptive_mutation: s.adaptive_mutation, - weight_decay: s.weight_decay, - } - } - - /// Settings that breed like the ones `of` read; selection fields default. - pub(crate) fn settings(&self) -> EvolutionSettings { - EvolutionSettings { - population: self.population, - crossover: self.crossover.clone(), - mutation_rate: self.mutation_rate, - adaptive_mutation: self.adaptive_mutation, - weight_decay: self.weight_decay, - ..Default::default() - } - } -} - -fn cross(parents: &[&Network], count: usize, algorithm: &str, rng: &mut impl DecisionSource) -> Vec { - if count == 0 { - return Vec::new(); - } - assert!(!parents.is_empty(), "selection produced no parents"); - if algorithm == "none" { - // No random decisions are needed; each child's copy is independent. - // Indexed collection retains the parent order of the serial path. - return (0..count) - .into_par_iter() - .map(|i| parents[i % parents.len()].clone()) - .collect(); - } - (0..count) - .map(|_| { - let first = parents[rng.randrange(parents.len())]; - let second = parents[rng.randrange(parents.len())]; - let (a, b) = (&first.params, &second.params); - let values = match algorithm { - "single_point" => { - let cut = rng.randrange(a.len()); - a[..cut].iter().chain(&b[cut..]).copied().collect() - } - "uniform" => a - .iter() - .zip(b) - .map(|(&x, &y)| if rng.random() < 0.5 { x } else { y }) - .collect(), - other => panic!("unknown crossover algorithm: {other}"), - }; - Network::from_vector(&first.shape, values) - }) - .collect() -} - -/// `reproduce(agents, settings, rng)` without user-controlled agents. -pub fn reproduce(agents: &[AgentResult], settings: &EvolutionSettings, rng: &mut PyRandom) -> Generation { - reproduce_with_scratch(agents, settings, rng, &mut Vec::new()) -} - -pub(crate) fn reproduce_with_scratch( - agents: &[AgentResult], - settings: &EvolutionSettings, - rng: &mut PyRandom, - normals: &mut Vec, -) -> Generation { - reproduce_scored_with_scratch(agents, reward_values(agents, &settings.rewards), settings, rng, normals) -} - -pub(crate) fn reproduce_scored_with_scratch( - agents: &[AgentResult], - scores: Vec, - settings: &EvolutionSettings, - rng: &mut PyRandom, - normals: &mut Vec, -) -> Generation { - validate_scores(agents, &scores); - let mut profile = crate::training::training_profile::Profile::new("reproduce_python"); - if agents.is_empty() { - return Generation { - networks: Vec::new(), - preserved_count: 0, - rewards: Vec::new(), - }; - } - profile.mark("rewards"); - let choice = choose(&scores, settings, rng); - let selected: Vec<&Network> = choice.selected.iter().map(|&i| agents[i].network).collect(); - let preserved: Vec = choice.preserved.iter().map(|&i| agents[i].network.clone()).collect(); - profile.mark("selection"); - let preserved_count = preserved.len(); - let networks = breed(&selected, preserved, settings, rng, normals, &mut profile); - Generation { - networks, - preserved_count, - rewards: scores, - } -} - -/// Crossover, mutation and weight decay: the second half of `reproduce`. -/// Returns `preserved` unchanged, then `settings.population` minus them -/// children of `selected`. Every draw comes from `rng`, so the same parents, -/// settings and generator state give the same networks. -pub(crate) fn breed( - selected: &[&Network], - preserved: Vec, - settings: &EvolutionSettings, - rng: &mut PyRandom, - normals: &mut Vec, - profile: &mut crate::training::training_profile::Profile, -) -> Vec { - let count = settings.population.saturating_sub(preserved.len()); - // Crossover "none" copies parents in order without random decisions; that - // copy is folded into the mutation pass below instead of materialized. - // Other algorithms build the children here, consuming the stream as before. - let copies = settings.crossover == "none"; - if copies && count > 0 { - assert!(!selected.is_empty(), "selection produced no parents"); - } - let crossed: Vec = if copies { - Vec::new() - } else { - cross(selected, count, &settings.crossover, rng) - }; - let source = |i: usize| -> &Network { - if copies { - selected[i % selected.len()] - } else { - &crossed[i] - } - }; - profile.mark("crossover"); - let mut networks = preserved; - // Children draw their mutation noise one after another from the shared - // stream; draw it all in order, then mutate the children in parallel. - let mut draws = 0; - let offsets: Vec = (0..count) - .map(|i| { - let start = draws; - if settings.mutation_rate > 0.0 { - draws += source(i).params.len(); - } - start - }) - .collect(); - rng.standard_normals_into(draws, normals); - profile.mark("normal_draws"); - let mutate = |child: Network, start: usize| { - let child = if settings.mutation_rate > 0.0 { - let normals = &normals[start..start + child.params.len()]; - mutate_xavier_with(child, settings.mutation_rate, settings.adaptive_mutation, normals) - } else { - child - }; - weight_decay(child, settings.weight_decay) - }; - let mutated: Vec = if copies { - (0..count) - .into_par_iter() - .zip(offsets) - .map(|(i, start)| { - let parent = source(i); - if settings.mutation_rate > 0.0 { - let normals = &normals[start..start + parent.params.len()]; - mutated_copy( - parent, - settings.mutation_rate, - settings.adaptive_mutation, - normals, - settings.weight_decay, - ) - } else { - weight_decay(parent.clone(), settings.weight_decay) - } - }) - .collect() - } else { - crossed - .into_par_iter() - .zip(offsets) - .map(|(child, start)| mutate(child, start)) - .collect() - }; - networks.extend(mutated); - profile.mark("mutation"); - networks -} - -/// Reproduction using the original game's independent decision/normal streams. -pub fn reproduce_game( - agents: &[AgentResult], - settings: &EvolutionSettings, - rng: &mut crate::training::game_random::GameRandom, - math: MathProfile, -) -> Generation { - reproduce_game_scored(agents, reward_values(agents, &settings.rewards), settings, rng, math) -} - -pub(crate) fn validate_scores(agents: &[AgentResult], scores: &[f64]) { - assert_eq!(agents.len(), scores.len(), "fitness must match the ordered population"); - assert!(scores.iter().all(|score| score.is_finite()), "fitness must be finite"); -} - -pub(crate) fn reproduce_game_scored( - agents: &[AgentResult], - scores: Vec, - settings: &EvolutionSettings, - rng: &mut crate::training::game_random::GameRandom, - math: MathProfile, -) -> Generation { - validate_scores(agents, &scores); - let mut profile = crate::training::training_profile::Profile::new("reproduce_game"); - if agents.is_empty() { - return Generation { - networks: Vec::new(), - preserved_count: 0, - rewards: Vec::new(), - }; - } - profile.mark("rewards"); - let choice = choose(&scores, settings, rng); - let selected: Vec<&Network> = choice.selected.iter().map(|&i| agents[i].network).collect(); - let preserved: Vec = choice.preserved.iter().map(|&i| agents[i].network.clone()).collect(); - let preserved_count = preserved.len(); - profile.mark("selection"); - let networks = breed_game(&selected, preserved, settings, rng, math, &mut profile); - Generation { - networks, - preserved_count, - rewards: scores, - } -} - -/// `breed` with the original game's streams. -pub(crate) fn breed_game( - selected: &[&Network], - mut networks: Vec, - settings: &EvolutionSettings, - rng: &mut crate::training::game_random::GameRandom, - math: MathProfile, - profile: &mut crate::training::training_profile::Profile, -) -> Vec { - let children = cross( - selected, - settings.population.saturating_sub(networks.len()), - &settings.crossover, - rng, - ); - profile.mark("crossover"); - for child in children { - let rate = settings.mutation_rate - * if settings.adaptive_mutation { - mutation_factor(&child, "xavier", math) - } else { - 1.0 - }; - let mut child = child.mutate_xavier_game(rate, rng, math); - if settings.weight_decay == 1.0 { - child.params.fill(0.0); - } else { - child = weight_decay(child, settings.weight_decay); - } - networks.push(child); - } - profile.mark("mutation"); - networks -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn trusted_mcp_settings_keep_cli_coercions_and_population_override() { - let data = json!({"settings":{ - "population":0,"selection_size":2.75,"preserve_parents_size":1.5,"rewards":[] - }}); - let mut settings = EvolutionSettings::from_mcp(&data); - assert_eq!(settings.population, 0); - assert_eq!(settings.selection_size, 2); - assert_eq!(settings.preserve_parents_size, 1); - settings.population = 4; // The CLI applies --population after parsing. - settings.validate_algorithm().unwrap(); - let network = Network::xavier(&[2, 2], &mut PyRandom::new(7)); - let agents = [ - AgentResult { - network: &network, - metrics: [Some(1.0); 14], - update_count: 1, - }, - AgentResult { - network: &network, - metrics: [Some(2.0); 14], - update_count: 1, - }, - ]; - let generation = reproduce(&agents, &settings, &mut PyRandom::new(8)); - assert_eq!(generation.networks.len(), 4); - assert_eq!(generation.rewards, vec![0.0, 0.0]); - - let parsed = EvolutionSettings::try_from_mcp(&json!({ - "rewards":[{"metric":"total_score","weight":1,"type":"legacy-kind"}] - })) - .unwrap(); - assert_eq!(parsed.rewards[0].kind, "legacy-kind"); - assert_eq!( - reward_values(&agents, &parsed.rewards), - reward_values(&agents, &EvolutionSettings::default().rewards) - ); - } - - #[test] - fn session_mcp_settings_validate_after_overrides_without_cli_coercions() { - assert_eq!( - EvolutionSettings::try_from_mcp_with_population(&json!({"population":0}), Some(4)) - .unwrap() - .population, - 4 - ); - for data in [ - json!({"population":0}), - json!({"rewards":[]}), - json!({"selection_size":2.75}), - json!({"preserve_parents_size":1.5}), - json!({"rewards":[{"metric":"total_score","weight":1,"type":"legacy-kind"}]}), - ] { - assert!( - EvolutionSettings::try_from_mcp_with_population(&data, None).is_err(), - "{data}" - ); - } - } - - /// Sequential reference: clone every child, then mutate and decay it in place. - fn reproduce_reference(agents: &[AgentResult], settings: &EvolutionSettings, rng: &mut PyRandom) -> Generation { - let scores = reward_values(agents, &settings.rewards); - let selected: Vec<&Network> = select(&scores, settings, rng) - .into_iter() - .map(|i| agents[i].network) - .collect(); - let size = preserve_size(settings); - let preserved: Vec = ranked(&scores) - .into_iter() - .take(size.min(settings.population)) - .map(|i| agents[i].network.clone()) - .collect(); - let children = cross( - &selected, - settings.population.saturating_sub(preserved.len()), - &settings.crossover, - rng, - ); - let preserved_count = preserved.len(); - let mut networks = preserved; - for child in children { - let child = if settings.mutation_rate > 0.0 { - mutate_xavier(&child, settings.mutation_rate, rng, settings.adaptive_mutation) - } else { - child - }; - networks.push(weight_decay(child, settings.weight_decay)); - } - Generation { - networks, - preserved_count, - rewards: scores, - } - } - - #[test] - fn fused_copy_mutation_matches_sequential_reproduction() { - let shape = [6, 5, 4]; - let mut seed = PyRandom::new(3); - let networks: Vec = (0..37).map(|_| Network::xavier(&shape, &mut seed)).collect(); - let agents: Vec = networks - .iter() - .enumerate() - .map(|(i, network)| { - let mut metrics: Metrics = [None; 14]; - metrics[0] = Some(((i * 7919) % 23) as f64 * 0.5); - AgentResult { - network, - metrics, - update_count: 10 + i as u64, - } - }) - .collect(); - let mut variants = Vec::new(); - for crossover in ["none", "single_point", "uniform"] { - for (mutation_rate, adaptive, weight_decay) in [ - (0.0, false, 0.0), - (0.3, false, 0.0), - (0.3, true, 0.0005000000237487257), - (1e-9, true, 0.0), - ] { - for (selection_algorithm, preserve_parents, population) in [ - ("best", "on_selection_size", 40), - ("tournament", "on_custom", 33), - ("roulette", "off", 5), - ] { - variants.push(EvolutionSettings { - population, - selection_algorithm: selection_algorithm.into(), - selection_size: 3, - crossover: crossover.into(), - mutation_rate, - adaptive_mutation: adaptive, - weight_decay, - preserve_parents: preserve_parents.into(), - preserve_parents_size: 2, - ..Default::default() - }); - } - } - } - let mut scratch = vec![f64::NAN; 5]; - for (v, settings) in variants.iter().enumerate() { - let (mut a, mut b) = (PyRandom::new(11 + v as i64), PyRandom::new(11 + v as i64)); - a.gauss(1.0); // a cached normal must survive both paths identically - b.gauss(1.0); - let expected = reproduce_reference(&agents, settings, &mut a); - let actual = reproduce_with_scratch(&agents, settings, &mut b, &mut scratch); - assert_eq!(actual.preserved_count, expected.preserved_count, "variant {v}"); - assert_eq!(actual.rewards, expected.rewards, "variant {v}"); - assert_eq!(actual.networks.len(), expected.networks.len(), "variant {v}"); - for (i, (x, y)) in actual.networks.iter().zip(&expected.networks).enumerate() { - assert_eq!(x.shape, y.shape); - let (xb, yb): (Vec, Vec) = ( - x.params.iter().map(|p| p.to_bits()).collect(), - y.params.iter().map(|p| p.to_bits()).collect(), - ); - assert_eq!(xb, yb, "variant {v} network {i}"); - } - assert_eq!(a.to_json(), b.to_json(), "variant {v}: generator state"); - assert_eq!(a.gauss(1.0).to_bits(), b.gauss(1.0).to_bits(), "variant {v}: next draw"); - } - } - - fn scored_agents(networks: &[Network]) -> Vec> { - networks - .iter() - .enumerate() - .map(|(i, network)| { - let mut metrics: Metrics = [None; 14]; - metrics[0] = Some(((i * 7919) % 23) as f64 * 0.5); - AgentResult { - network, - metrics, - update_count: 10 + i as u64, - } - }) - .collect() - } - - fn breeding_variants() -> Vec { - let mut variants = Vec::new(); - for crossover in CROSSOVERS { - for (mutation_rate, adaptive, weight_decay) in - [(0.0, false, 0.0), (0.3, true, 0.0005000000237487257), (1e-9, true, 1.0)] - { - for (selection_algorithm, preserve_parents, population) in [ - ("best", "on_selection_size", 40), - ("tournament", "on_custom", 33), - ("roulette", "off", 5), - ] { - variants.push(EvolutionSettings { - population, - selection_algorithm: selection_algorithm.into(), - selection_size: 3, - crossover: crossover.into(), - mutation_rate, - adaptive_mutation: adaptive, - weight_decay, - preserve_parents: preserve_parents.into(), - preserve_parents_size: 2, - ..Default::default() - }); - } - } - } - variants - } - - fn bits(networks: &[Network]) -> Vec> { - networks - .iter() - .map(|n| n.params.iter().map(|p| p.to_bits()).collect()) - .collect() - } - - /// Breeding from copies of the chosen parents, with the generator state - /// saved after selection and only the settings `Breeding` keeps, repeats - /// `reproduce` exactly. - #[test] - fn breeding_replays_from_the_chosen_parents_and_generator_state() { - let mut seed = PyRandom::new(3); - let networks: Vec = (0..37).map(|_| Network::xavier(&[6, 5, 4], &mut seed)).collect(); - let agents = scored_agents(&networks); - for (v, settings) in breeding_variants().iter().enumerate() { - let scores = reward_values(&agents, &settings.rewards); - let mut expected_rng = PyRandom::new(11 + v as i64); - expected_rng.gauss(1.0); - let mut chooser = expected_rng.clone(); - let expected = reproduce(&agents, settings, &mut expected_rng); - let choice = choose(&scores, settings, &mut chooser); - assert_eq!( - choice.preserved.first().copied().unwrap_or(choice.best), - choice.best, - "variant {v}" - ); - let selected: Vec = choice.selected.iter().map(|&i| agents[i].network.clone()).collect(); - let selected: Vec<&Network> = selected.iter().collect(); - let preserved = choice.preserved.iter().map(|&i| agents[i].network.clone()).collect(); - let mut replay = chooser.clone(); - let mut profile = crate::training::training_profile::Profile::new("test"); - let actual = breed( - &selected, - preserved, - &Breeding::of(settings).settings(), - &mut replay, - &mut Vec::new(), - &mut profile, - ); - assert_eq!(bits(&actual), bits(&expected.networks), "variant {v}"); - assert_eq!(replay.to_json(), expected_rng.to_json(), "variant {v}: generator state"); - - let mut game = crate::training::game_random::GameRandom::new([1, 2, 3, 4 + v as u64], 7 + v as i32); - let mut game_chooser = game.clone(); - let math = MathProfile::ALL[v % 3]; - let expected = reproduce_game(&agents, settings, &mut game, math); - let choice = choose(&scores, settings, &mut game_chooser); - let selected: Vec<&Network> = choice.selected.iter().map(|&i| agents[i].network).collect(); - let preserved = choice.preserved.iter().map(|&i| agents[i].network.clone()).collect(); - let actual = breed_game( - &selected, - preserved, - &Breeding::of(settings).settings(), - &mut game_chooser, - math, - &mut profile, - ); - assert_eq!(bits(&actual), bits(&expected.networks), "game variant {v}"); - assert_eq!( - game_chooser.to_json(), - game.to_json(), - "game variant {v}: generator state" - ); - } - } -} diff --git a/github-simulator/source/src/training/game_random.rs b/github-simulator/source/src/training/game_random.rs deleted file mode 100644 index 4f0f55cbf7acbcbb35613197103f95934db3b3f3..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/training/game_random.rs +++ /dev/null @@ -1,202 +0,0 @@ -//! The two independent random streams used by the shipped .NET 8 game. -//! Exact replay requires both states: the game seeds them independently. -use crate::math::profile::MathProfile; -use serde_json::{json, Value}; - -#[derive(Clone, Debug)] -pub struct GameRandom { - pub(crate) decision_state: [u64; 4], - seed_array: [i32; 56], - inext: usize, - inextp: usize, -} -impl GameRandom { - pub fn new(decision_state: [u64; 4], seed: i32) -> Self { - assert!(decision_state.iter().any(|&x| x != 0)); - let mut seed_array = [0; 56]; - let subtraction = if seed == i32::MIN { i32::MAX } else { seed.abs() }; - let mut mj = 161803398 - subtraction; - seed_array[55] = mj; - let mut mk = 1; - for i in 1..55 { - let ii = 21 * i % 55; - seed_array[ii] = mk; - mk = mj.wrapping_sub(mk); - if mk < 0 { - mk += i32::MAX; - } - mj = seed_array[ii]; - } - for _ in 0..4 { - for i in 1..56 { - seed_array[i] = seed_array[i].wrapping_sub(seed_array[1 + (i + 30) % 55]); - if seed_array[i] < 0 { - seed_array[i] += i32::MAX; - } - } - } - Self { - decision_state, - seed_array, - inext: 0, - inextp: 21, - } - } - fn next_u64(&mut self) -> u64 { - let s = &mut self.decision_state; - let result = s[1].wrapping_mul(5).rotate_left(7).wrapping_mul(9); - let t = s[1] << 17; - s[2] ^= s[0]; - s[3] ^= s[1]; - s[1] ^= s[2]; - s[0] ^= s[3]; - s[2] ^= t; - s[3] = s[3].rotate_left(45); - result - } - pub fn random(&mut self) -> f64 { - (self.next_u64() >> 11) as f64 * 1.1102230246251565e-16 - } - pub fn randrange(&mut self, bound: usize) -> usize { - assert!(bound <= i32::MAX as usize); - let bound = bound as u32; - let mut product = bound as u64 * (self.next_u64() >> 32); - if (product as u32) < bound { - let threshold = bound.wrapping_neg() % bound; - while (product as u32) < threshold { - product = bound as u64 * (self.next_u64() >> 32); - } - } - (product >> 32) as usize - } - pub(crate) fn normal_uniform(&mut self) -> f64 { - self.inext += 1; - if self.inext >= 56 { - self.inext = 1; - } - self.inextp += 1; - if self.inextp >= 56 { - self.inextp = 1; - } - let mut value = self.seed_array[self.inext] - self.seed_array[self.inextp]; - if value == i32::MAX { - value -= 1; - } - if value < 0 { - value += i32::MAX; - } - self.seed_array[self.inext] = value; - value as f64 * (1.0 / i32::MAX as f64) - } - /// .NET's normal draws, whose Math.Log is the C runtime's `math.log`. - pub fn normal_array(&mut self, count: usize, sigma: f64, math: MathProfile) -> Vec { - if count == 0 { - return Vec::new(); - } - let mut prefetched = ((count * 4) as f64 * (1.0 / std::f64::consts::PI)).ceil() as usize; - prefetched += prefetched % 2; - let uniforms: Vec<_> = (0..prefetched).map(|_| self.normal_uniform()).collect(); - let mut result = Vec::with_capacity(count); - let mut index = 0; - while result.len() < count { - let (a, b) = if index < prefetched { - let pair = (uniforms[index], uniforms[index + 1]); - index += 2; - pair - } else { - (self.normal_uniform(), self.normal_uniform()) - }; - let u = 2.0 * a - 1.0; - let v = 2.0 * b - 1.0; - let s = u * u + v * v; - if s >= 1.0 || s == 0.0 { - continue; - } - let factor = ((-2.0 * math.log(s)) / s).sqrt(); - result.push(0.0 + sigma * (u * factor)); - if result.len() < count { - result.push(0.0 + sigma * (v * factor)); - } - } - result - } - pub fn normal_state(&self) -> Value { - json!({"algorithm":"dotnet_compat","seed_array":self.seed_array.as_slice(),"inext":self.inext,"inextp":self.inextp}) - } - pub fn decision_json(&self) -> Value { - json!({"algorithm":"xoshiro256starstar","state":self.decision_state}) - } - pub fn to_json(&self) -> Value { - json!({"backend":"game","decisions":self.decision_json(),"normals":self.normal_state()}) - } - pub fn from_json(v: &Value) -> Self { - Self::try_from_json(v).expect("valid game random state") - } - pub fn try_from_json(v: &Value) -> Result { - let mut s = Self::new([1, 2, 3, 4], 0); - let decisions = v["decisions"]["state"] - .as_array() - .filter(|a| a.len() == 4) - .ok_or("invalid decision state length")?; - for (i, x) in decisions.iter().enumerate() { - s.decision_state[i] = x.as_u64().ok_or("invalid decision state word")?; - } - if s.decision_state.iter().all(|n| *n == 0) { - return Err("decision state must not be all zero".into()); - } - let normals = v["normals"]["seed_array"] - .as_array() - .filter(|a| a.len() == 56) - .ok_or("invalid normal state length")?; - for (i, x) in normals.iter().enumerate() { - s.seed_array[i] = x - .as_i64() - .filter(|n| *n >= 0 && *n < i32::MAX as i64) - .ok_or("invalid normal state word")? as i32; - } - if s.seed_array[1..].iter().all(|n| *n == 0) { - return Err("normal state must not be all zero".into()); - } - s.inext = v["normals"]["inext"] - .as_u64() - .filter(|n| *n < 56) - .ok_or("invalid normal index")? as usize; - s.inextp = v["normals"]["inextp"] - .as_u64() - .filter(|n| *n < 56) - .ok_or("invalid normal index")? as usize; - if (s.inextp + 55 - s.inext) % 55 != 21 { - return Err("invalid normal index separation".into()); - } - Ok(s) - } -} - -#[cfg(test)] -mod input_tests { - use super::*; - #[test] - fn validated_game_states_round_trip_and_reject_bad_indices() { - let mut source = GameRandom::new([1, 2, 3, 4], 19); - for _ in 0..70 { - source.normal_uniform(); - } - let valid = source.to_json(); - let restored = GameRandom::try_from_json(&valid).unwrap(); - assert_eq!(restored.to_json(), valid); - for field in ["inext", "inextp"] { - let mut bad = valid.clone(); - bad["normals"][field] = serde_json::json!(56); - assert!(GameRandom::try_from_json(&bad).is_err()); - } - let mut bad = valid.clone(); - bad["decisions"]["state"] = serde_json::json!([0, 0, 0, 0]); - assert!(GameRandom::try_from_json(&bad).is_err()); - bad = valid.clone(); - bad["normals"]["inextp"] = bad["normals"]["inext"].clone(); - assert!(GameRandom::try_from_json(&bad).is_err()); - bad = valid; - bad["normals"]["seed_array"][1] = serde_json::json!(-1); - assert!(GameRandom::try_from_json(&bad).is_err()); - } -} diff --git a/github-simulator/source/src/training/lineage.rs b/github-simulator/source/src/training/lineage.rs deleted file mode 100644 index 279f3323301e9ea003d754fb8cfbc37209af97f8..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/training/lineage.rs +++ /dev/null @@ -1,239 +0,0 @@ -//! Checkpoint RNG streams and reproducible parent lineage. - -use crate::math::profile::MathProfile; -use crate::nn::network::Network; -use crate::training::evolution::{reproduce, AgentResult, Breeding, Choice, EvolutionSettings, Generation, CROSSOVERS}; -use crate::training::pyrandom::PyRandom; -use serde_json::Value; - -/// Untagged checkpoints retain the historical Python backend. Game replay -/// checkpoints carry both independent .NET streams under an explicit tag. -#[derive(Clone)] -// Keep the public variants and their RNG state inline; boxing changes the API and adds an allocation. -#[allow(clippy::large_enum_variant)] -pub enum TrainingRandom { - Python(PyRandom), - Game(crate::training::game_random::GameRandom), -} -impl From for TrainingRandom { - fn from(r: PyRandom) -> Self { - Self::Python(r) - } -} -impl From for TrainingRandom { - fn from(r: crate::training::game_random::GameRandom) -> Self { - Self::Game(r) - } -} -impl TrainingRandom { - pub fn to_json(&self) -> Value { - match self { - Self::Python(r) => r.to_json(), - Self::Game(r) => r.to_json(), - } - } - pub fn from_json(v: &Value) -> Self { - Self::try_from_json(v).expect("valid training random state") - } - pub fn try_from_json(v: &Value) -> Result { - if v["backend"].as_str() == Some("game") { - Ok(Self::Game(crate::training::game_random::GameRandom::try_from_json(v)?)) - } else { - Ok(Self::Python(PyRandom::try_from_json(v)?)) - } - } - /// The game's streams use `math` (.NET Math.Log); the Python backend - /// reproduces the historical trainer, which ran on musl's. - pub fn reproduce(&mut self, agents: &[AgentResult], settings: &EvolutionSettings, math: MathProfile) -> Generation { - match self { - Self::Python(r) => reproduce(agents, settings, r), - Self::Game(r) => crate::training::evolution::reproduce_game(agents, settings, r, math), - } - } - pub(super) fn reproduce_scored( - &mut self, - agents: &[AgentResult], - scores: Vec, - settings: &EvolutionSettings, - scratch: &mut Vec, - math: MathProfile, - ) -> Generation { - match self { - Self::Python(r) => { - crate::training::evolution::reproduce_scored_with_scratch(agents, scores, settings, r, scratch) - } - Self::Game(r) => crate::training::evolution::reproduce_game_scored(agents, scores, settings, r, math), - } - } - /// The next `count` standard normal variates, in `out`. - pub(crate) fn standard_normals_into(&mut self, count: usize, out: &mut Vec, math: MathProfile) { - match self { - Self::Python(r) => r.standard_normals_into(count, out), - Self::Game(r) => *out = r.normal_array(count, 1.0, math), - } - } - pub fn xavier(&mut self, shape: &[usize], math: MathProfile) -> Network { - match self { - Self::Python(r) => Network::xavier(shape, r), - Self::Game(r) => Network::xavier_game(shape, r, math), - } - } - pub(crate) fn choose(&mut self, scores: &[f64], settings: &EvolutionSettings) -> Choice { - match self { - Self::Python(r) => crate::training::evolution::choose(scores, settings, r), - Self::Game(r) => crate::training::evolution::choose(scores, settings, r), - } - } - pub(crate) fn breed( - &mut self, - selected: &[&Network], - preserved: Vec, - settings: &EvolutionSettings, - math: MathProfile, - profile: &mut crate::training::training_profile::Profile, - ) -> Vec { - match self { - Self::Python(r) => { - crate::training::evolution::breed(selected, preserved, settings, r, &mut Vec::new(), profile) - } - Self::Game(r) => crate::training::evolution::breed_game(selected, preserved, settings, r, math, profile), - } - } -} - -/// One reproduction as checkpoints store it: the distinct parents, which of -/// them were selected and preserved, the breeding settings, and the -/// generator state after selection. `rebuild` repeats the breeding. -#[derive(Clone)] -pub(crate) struct Lineage { - /// Distinct parents in first use: the best car of the scored population - /// first, then preserved cars, then selected cars. - pub(crate) parents: Vec, - /// Indices into `parents`, in draw order; repeats are kept. - pub(crate) selected: Vec, - /// Indices into `parents`, best first. - pub(crate) preserved: Vec, - pub(crate) breeding: Breeding, - /// The generator state after selection, before crossover. - pub(crate) rng: TrainingRandom, - /// The math the game's streams draw with. - pub(crate) math: MathProfile, -} - -impl Lineage { - fn new( - agents: &[AgentResult], - choice: &Choice, - breeding: Breeding, - rng: TrainingRandom, - math: MathProfile, - ) -> Lineage { - let mut slots = std::collections::HashMap::new(); - let mut parents = Vec::new(); - let mut slot = |car: usize| -> u32 { - *slots.entry(car).or_insert_with(|| { - parents.push(agents[car].network.clone()); - (parents.len() - 1) as u32 - }) - }; - slot(choice.best); - let preserved = choice.preserved.iter().map(|&car| slot(car)).collect(); - let selected = choice.selected.iter().map(|&car| slot(car)).collect(); - Lineage { - parents, - selected, - preserved, - breeding, - rng, - math, - } - } - - /// Whether `rebuild` can breed this lineage; a checkpoint may be corrupt. - pub(crate) fn validate(&self) -> Result<(), String> { - let b = &self.breeding; - let Some(first) = self.parents.first() else { - return Err("the checkpoint has no networks".into()); - }; - if self.parents.iter().any(|p| p.shape != first.shape) { - return Err("the checkpoint's parents differ in shape".into()); - } - if first.shape.len() < 2 || first.shape.contains(&0) { - return Err("the checkpoint's parents have an invalid shape".into()); - } - // Checked, since a shape from a checkpoint may be huge (usize is 32 bits on wasm). - let expected = first.shape.windows(2).try_fold(0usize, |sum, w| { - w[0].checked_add(1)?.checked_mul(w[1])?.checked_add(sum) - }); - if self.parents.iter().any(|p| Some(p.params.len()) != expected) { - return Err("the checkpoint's parents have the wrong number of parameters".into()); - } - if b.population == 0 { - return Err("the checkpoint has no cars".into()); - } - if self - .selected - .iter() - .chain(&self.preserved) - .any(|&i| i as usize >= self.parents.len()) - { - return Err("a checkpoint parent index is out of range".into()); - } - if self.preserved.len() > b.population { - return Err("the checkpoint keeps more parents than it has cars".into()); - } - if self.selected.is_empty() && self.preserved.len() < b.population { - return Err("the checkpoint selects no parents".into()); - } - if !CROSSOVERS.contains(&b.crossover.as_str()) { - return Err("the checkpoint has an unknown crossover".into()); - } - if !(0.0..=10.0).contains(&b.mutation_rate) || !(0.0..=1.0).contains(&b.weight_decay) { - return Err("the checkpoint's mutation rate or weight decay is out of range".into()); - } - Ok(()) - } - - /// The bred population and the generator state after it. Call `validate` first. - pub(crate) fn rebuild(&self) -> (Vec, TrainingRandom) { - let mut rng = self.rng.clone(); - let selected: Vec<&Network> = self.selected.iter().map(|&i| &self.parents[i as usize]).collect(); - let preserved = self - .preserved - .iter() - .map(|&i| self.parents[i as usize].clone()) - .collect(); - let mut profile = crate::training::training_profile::Profile::new("rebuild"); - let networks = rng.breed(&selected, preserved, &self.breeding.settings(), self.math, &mut profile); - (networks, rng) - } -} - -/// `TrainingRandom::reproduce_scored`, also returning the reproduction as a -/// `Lineage`. `agents` must not be empty. -pub(super) fn reproduce_traced( - rng: &mut TrainingRandom, - agents: &[AgentResult], - scores: Vec, - settings: &EvolutionSettings, - math: MathProfile, -) -> (Generation, Lineage) { - crate::training::evolution::validate_scores(agents, &scores); - assert!(!agents.is_empty(), "a training generation needs at least one network"); - let mut profile = crate::training::training_profile::Profile::new("reproduce_traced"); - let choice = rng.choose(&scores, settings); - let lineage = Lineage::new(agents, &choice, Breeding::of(settings), rng.clone(), math); - let selected: Vec<&Network> = choice.selected.iter().map(|&i| agents[i].network).collect(); - let preserved: Vec = choice.preserved.iter().map(|&i| agents[i].network.clone()).collect(); - let preserved_count = preserved.len(); - profile.mark("selection"); - let networks = rng.breed(&selected, preserved, settings, math, &mut profile); - ( - Generation { - networks, - preserved_count, - rewards: scores, - }, - lineage, - ) -} diff --git a/github-simulator/source/src/training/mod.rs b/github-simulator/source/src/training/mod.rs deleted file mode 100644 index 336727704c2ac7b56c7f6e8a3cd102d429d68f03..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/training/mod.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! Game training scheduler, statistics, and population lifecycle. -//! -//! Independent mode runs each car through a window, then catches inactive cars -//! up to the final tick. Native shared broadphase scenes use a tick-major loop -//! to preserve contact ordering. Deactivated cars retain passive physics, while -//! their driving metrics stop updating. - -pub mod batch_evaluation; -#[cfg(not(target_arch = "wasm32"))] -pub mod evaluation; -pub mod evolution; -pub mod game_random; -pub mod pyrandom; -pub mod session; -pub(crate) mod training_profile; - -mod agent; -mod ars; -mod lineage; -mod optimizer; -mod runner; -mod sensors; -mod stats; - -pub use agent::TrainingAgent; -pub(crate) use lineage::Lineage; -pub use lineage::TrainingRandom; -#[cfg(any(feature = "server", all(target_arch = "wasm32", feature = "wasm")))] -pub(crate) use runner::CAR_STATE_FIELDS; -pub(crate) use runner::CAR_STATE_STRIDE; -pub use runner::{export_state_into, Mode, TrainingRunner}; -#[cfg(not(target_arch = "wasm32"))] -pub(crate) use sensors::{sensor_type, vision_angle}; -pub use sensors::{vision_length, SensorLayout}; -pub use stats::{ScoreTracker, TrainingStats}; diff --git a/github-simulator/source/src/training/optimizer.rs b/github-simulator/source/src/training/optimizer.rs deleted file mode 100644 index cc8fa5379b8d8935544cc47d8b4c0709ae1f8ab2..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/training/optimizer.rs +++ /dev/null @@ -1,160 +0,0 @@ -//! Population optimizers: how one generation of cars becomes the next. -//! -//! The runner evaluates a whole population, scores it, and hands both to an -//! `Optimizer`, which returns the networks of the next population. The GA -//! (`Ga`) breeds from the scored cars alone. Others, such as `Ars`, keep a -//! search state of their own between generations and use the cars only for -//! their scores. - -use super::ars::{Ars, ArsRecord}; -use super::lineage::reproduce_traced; -use super::{Lineage, TrainingRandom}; -use crate::math::profile::MathProfile; -use crate::nn::network::Network; -use crate::training::evolution::{reward_values, AgentResult, EvolutionSettings, Generation}; - -/// What a checkpoint needs to rebuild the population an optimizer produced. -#[derive(Clone)] -// Records are moved once per generation; boxing the larger variant buys nothing. -#[allow(clippy::large_enum_variant)] -pub(crate) enum Record { - /// The parents the GA bred from. - Lineage(Lineage), - /// The search state ARS sampled from. - Ars(ArsRecord), -} - -impl Record { - #[cfg(test)] - pub(crate) fn into_lineage(self) -> Option { - match self { - Record::Lineage(lineage) => Some(lineage), - Record::Ars(_) => None, - } - } -} - -/// A new population, with the record of how it was made when asked for. -pub(crate) struct Produced { - pub(crate) generation: Generation, - pub(crate) record: Option, -} - -pub(crate) trait Optimizer: Send + Sync { - /// The `settings.algorithm` this optimizer implements. - fn name(&self) -> &'static str; - - /// The first population, built from `seed`. Forgets any earlier state. - /// `math` is the session's profile, which the game's random streams draw with. - fn start( - &mut self, - seed: &Network, - settings: &EvolutionSettings, - rng: &mut TrainingRandom, - math: MathProfile, - trace: bool, - ) -> Produced; - - /// The population after `agents`, the cars of the finished generation in - /// population order. `scores` are their rewards (`reward_values` when - /// absent). `scratch` is reusable storage for random draws. With `trace`, - /// the result carries the record a checkpoint rebuilds it from. - #[allow(clippy::too_many_arguments)] // the call mirrors `start`, plus the cars and scratch - fn next( - &mut self, - agents: &[AgentResult], - scores: Option>, - settings: &EvolutionSettings, - rng: &mut TrainingRandom, - scratch: &mut Vec, - math: MathProfile, - trace: bool, - ) -> Produced; - - /// A copy of the search state, to put back when the population `next` - /// produced cannot be installed. - fn boxed_clone(&self) -> Box; - - /// The installed cars were edited from outside: forget whatever ties the - /// optimizer's state to the population it produced. - #[allow(dead_code)] // only the test and wasm `setNetworkJson` edit installed cars - fn invalidate(&mut self) {} -} - -/// The optimizer for `algorithm`. Unknown names panic, as the other settings do. -pub(crate) fn create(algorithm: &str) -> Box { - match algorithm { - "ga" => Box::new(Ga), - "ars" => Box::::default(), - other => panic!("unknown algorithm: {other}"), - } -} - -/// Selection, crossover and mutation (`training::evolution`). -pub(crate) struct Ga; - -impl Ga { - fn produce( - agents: &[AgentResult], - scores: Vec, - settings: &EvolutionSettings, - rng: &mut TrainingRandom, - scratch: &mut Vec, - math: MathProfile, - trace: bool, - ) -> Produced { - if trace { - let (generation, lineage) = reproduce_traced(rng, agents, scores, settings, math); - Produced { - generation, - record: Some(Record::Lineage(lineage)), - } - } else { - Produced { - generation: rng.reproduce_scored(agents, scores, settings, scratch, math), - record: None, - } - } - } -} - -impl Optimizer for Ga { - fn name(&self) -> &'static str { - "ga" - } - - fn boxed_clone(&self) -> Box { - Box::new(Ga) - } - - fn start( - &mut self, - seed: &Network, - settings: &EvolutionSettings, - rng: &mut TrainingRandom, - math: MathProfile, - trace: bool, - ) -> Produced { - let seed_result = [AgentResult { - network: seed, - metrics: [None; 14], - update_count: 0, - }]; - let scores = reward_values(&seed_result, &settings.rewards); - Self::produce(&seed_result, scores, settings, rng, &mut Vec::new(), math, trace) - } - - fn next( - &mut self, - agents: &[AgentResult], - scores: Option>, - settings: &EvolutionSettings, - rng: &mut TrainingRandom, - scratch: &mut Vec, - math: MathProfile, - trace: bool, - ) -> Produced { - let scores = scores.unwrap_or_else(|| reward_values(agents, &settings.rewards)); - Self::produce(agents, scores, settings, rng, scratch, math, trace) - } -} diff --git a/github-simulator/source/src/training/pyrandom.rs b/github-simulator/source/src/training/pyrandom.rs deleted file mode 100644 index 967f041ef93bf7ddf23e57c54ea020c26b0cac0e..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/training/pyrandom.rs +++ /dev/null @@ -1,390 +0,0 @@ -//! CPython's `random.Random` (MT19937) for the calls the trainer makes, so a -//! seeded Rust run reproduces `python -m driving_sim.train` exactly. - -const N: usize = 624; -const M: usize = 397; - -#[derive(Clone)] -pub struct PyRandom { - state: [u32; N], - index: usize, - gauss_next: Option, -} - -impl PyRandom { - /// `random.Random(seed)` for an integer seed. - pub fn new(seed: i64) -> PyRandom { - let mut n = seed.unsigned_abs(); - let mut key = Vec::new(); - while n != 0 { - key.push(n as u32); - n >>= 32; - } - if key.is_empty() { - key.push(0); - } - let mut rng = PyRandom { - state: [0; N], - index: N, - gauss_next: None, - }; - rng.init_by_array(&key); - rng - } - - /// Complete generator state for checkpoints (`gauss_next` as raw bits). - pub(crate) fn to_json(&self) -> serde_json::Value { - serde_json::json!({ - "state": self.state.to_vec(), - "index": self.index, - "gauss_next_bits": self.gauss_next.map(f64::to_bits), - }) - } - - #[cfg(test)] - pub(crate) fn from_json(data: &serde_json::Value) -> PyRandom { - Self::try_from_json(data).expect("valid random state") - } - - pub(crate) fn try_from_json(data: &serde_json::Value) -> Result { - let words = data["state"] - .as_array() - .filter(|w| w.len() == N) - .ok_or("invalid rng state length")?; - let words: Vec = words - .iter() - .map(|v| { - v.as_u64() - .and_then(|n| u32::try_from(n).ok()) - .ok_or("invalid rng state word") - }) - .collect::>()?; - let index = data["index"] - .as_u64() - .filter(|n| *n <= N as u64) - .ok_or("invalid rng index")? as usize; - if words.iter().all(|w| *w == 0) { - return Err("rng state must not be all zero".into()); - } - let gauss_next = match data.get("gauss_next_bits") { - None | Some(serde_json::Value::Null) => None, - Some(v) => Some(f64::from_bits(v.as_u64().ok_or("invalid rng gaussian bits")?)), - }; - if gauss_next.is_some_and(|n| !n.is_finite()) { - return Err("invalid rng gaussian value".into()); - } - Ok(PyRandom { - state: words.try_into().map_err(|_| "invalid rng state")?, - index, - gauss_next, - }) - } - - fn init_genrand(&mut self, seed: u32) { - self.state[0] = seed; - for i in 1..N { - let previous = self.state[i - 1]; - self.state[i] = 1812433253u32 - .wrapping_mul(previous ^ (previous >> 30)) - .wrapping_add(i as u32); - } - self.index = N; - } - - fn init_by_array(&mut self, key: &[u32]) { - self.init_genrand(19650218); - let (mut i, mut j) = (1usize, 0usize); - for _ in 0..N.max(key.len()) { - let previous = self.state[i - 1]; - self.state[i] = (self.state[i] ^ (previous ^ (previous >> 30)).wrapping_mul(1664525)) - .wrapping_add(key[j]) - .wrapping_add(j as u32); - i += 1; - j += 1; - if i >= N { - self.state[0] = self.state[N - 1]; - i = 1; - } - if j >= key.len() { - j = 0; - } - } - for _ in 0..N - 1 { - let previous = self.state[i - 1]; - self.state[i] = - (self.state[i] ^ (previous ^ (previous >> 30)).wrapping_mul(1566083941)).wrapping_sub(i as u32); - i += 1; - if i >= N { - self.state[0] = self.state[N - 1]; - i = 1; - } - } - self.state[0] = 0x8000_0000; - } - - fn twist(&mut self) { - let s = &mut self.state; - for k in 0..N - M { - let y = (s[k] & 0x8000_0000) | (s[k + 1] & 0x7fff_ffff); - s[k] = s[k + M] ^ (y >> 1) ^ (0u32.wrapping_sub(y & 1) & 0x9908_b0df); - } - for k in N - M..N - 1 { - let y = (s[k] & 0x8000_0000) | (s[k + 1] & 0x7fff_ffff); - s[k] = s[k + M - N] ^ (y >> 1) ^ (0u32.wrapping_sub(y & 1) & 0x9908_b0df); - } - let y = (s[N - 1] & 0x8000_0000) | (s[0] & 0x7fff_ffff); - s[N - 1] = s[M - 1] ^ (y >> 1) ^ (0u32.wrapping_sub(y & 1) & 0x9908_b0df); - self.index = 0; - } - - #[inline] - fn temper(mut y: u32) -> u32 { - y ^= y >> 11; - y ^= (y << 7) & 0x9d2c_5680; - y ^= (y << 15) & 0xefc6_0000; - y ^ (y >> 18) - } - - pub(crate) fn next_u32(&mut self) -> u32 { - if self.index >= N { - self.twist(); - } - let y = self.state[self.index]; - self.index += 1; - Self::temper(y) - } - - /// `random.random()`. - pub(crate) fn random(&mut self) -> f64 { - let a = self.next_u32() >> 5; - let b = self.next_u32() >> 6; - (a as f64 * 67108864.0 + b as f64) * (1.0 / 9007199254740992.0) - } - - /// Draw in MT state-sized blocks so tempering and conversion can be - /// vectorized. Twist boundaries and the final generator state are exactly - /// those of sequential `random()` calls, including odd starting indices. - fn fill_uniforms(&mut self, mut out: &mut [f64]) { - while !out.is_empty() { - if self.index == N { - self.twist(); - } - if self.index == N - 1 { - out[0] = self.random(); - out = &mut out[1..]; - continue; - } - let count = out.len().min((N - self.index) / 2); - let words = &self.state[self.index..self.index + count * 2]; - for (value, pair) in out[..count].iter_mut().zip(words.chunks_exact(2)) { - let a = Self::temper(pair[0]) >> 5; - let b = Self::temper(pair[1]) >> 6; - *value = (a as f64 * 67108864.0 + b as f64) * (1.0 / 9007199254740992.0); - } - self.index += count * 2; - out = &mut out[count..]; - } - } - - /// `random.gauss(0, sigma)`, including the cached second variate. - pub(crate) fn gauss(&mut self, sigma: f64) -> f64 { - let z = match self.gauss_next.take() { - Some(z) => z, - None => { - let x2pi = self.random() * std::f64::consts::TAU; - let g2rad = (-2.0 * (1.0 - self.random()).ln()).sqrt(); - self.gauss_next = Some(x2pi.sin() * g2rad); - x2pi.cos() * g2rad - } - }; - 0.0 + z * sigma - } - - /// The next `count` standard normal variates of `gauss`, identical to - /// `count` sequential `gauss(1)` calls (the result is `z`, not `0.0 + z`). - /// Uniform draws stay sequential; the transcendental math runs in parallel. - #[cfg(test)] - pub(crate) fn standard_normals(&mut self, count: usize) -> Vec { - let mut out = Vec::new(); - self.standard_normals_into(count, &mut out); - out - } - - /// Reuse storage without clearing the elements that will be overwritten. - /// - /// The generator is sequential, but the Box-Muller transform of each - /// filled block runs on the thread pool while the next block is drawn, so - /// the transcendental work overlaps the draws instead of following them. - /// Every element sees the same operations as before: `fill_uniforms` on - /// consecutive blocks equals one call over the whole slice, and each pair - /// is transformed on its own. - pub(crate) fn standard_normals_into(&mut self, count: usize, out: &mut Vec) { - let mut profile = crate::training::training_profile::Profile::new("normal_draws"); - let cached = if count > 0 { self.gauss_next.take() } else { None }; - // Store each pair's uniforms in place, then transform the pairs in parallel. - let offset = usize::from(cached.is_some()); - let pairs = (count - offset).div_ceil(2); - out.resize(offset + pairs * 2, 0.0); - if let Some(z) = cached { - out[0] = z; - } - const BLOCK: usize = 1 << 16; // doubles per block (even), about a millisecond of transform - let body = &mut out[offset..]; - rayon::scope(|scope| { - let mut rest = body; - while !rest.is_empty() { - let n = rest.len().min(BLOCK); - let (block, tail) = std::mem::take(&mut rest).split_at_mut(n); - self.fill_uniforms(block); - scope.spawn(move |_| Self::transform_pairs(block)); - rest = tail; - } - }); - if out.len() > count { - self.gauss_next = out.pop(); - } - profile.mark("draws_and_transform"); - } - - /// Box-Muller on stored uniform pairs, in place (`random.gauss`). - fn transform_pairs(block: &mut [f64]) { - for pair in block.chunks_exact_mut(2) { - let x2pi = pair[0] * std::f64::consts::TAU; - let g2rad = (-2.0 * (1.0 - pair[1]).ln()).sqrt(); - pair[0] = x2pi.cos() * g2rad; - pair[1] = x2pi.sin() * g2rad; - } - } - - /// `random.getrandbits(k)` for `1 <= k <= 32`. - fn getrandbits(&mut self, k: u32) -> u32 { - self.next_u32() >> (32 - k) - } - - /// `random.randrange(n)` for `1 <= n < 2**32`. - pub(crate) fn randrange(&mut self, n: usize) -> usize { - assert!(n >= 1 && n <= u32::MAX as usize, "randrange bound out of range"); - let k = usize::BITS - n.leading_zeros(); - loop { - let r = self.getrandbits(k) as usize; - if r < n { - return r; - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn matches_cpython_seed_7() { - // python3 -c "import random; r=random.Random(7); print(r.random(), r.random())" - let mut rng = PyRandom::new(7); - assert_eq!(rng.random(), 0.32383276483316237); - assert_eq!(rng.random(), 0.15084917392450192); - } - - #[test] - fn gauss_randrange_and_seeds_match_cpython() { - // r = random.Random(7); r.random(); r.random(); r.gauss(0, 1); r.gauss(0, 1); - // r.randrange(300); r.randrange(5); r.gauss(0, .3) - let mut rng = PyRandom::new(7); - rng.random(); - rng.random(); - assert_eq!(rng.gauss(1.0), -0.2260961647831047); - assert_eq!(rng.gauss(1.0), -0.3150684223311854); - assert_eq!(rng.randrange(300), 274); - assert_eq!(rng.randrange(5), 0); - assert_eq!(rng.gauss(0.3), -0.06891764470260762); - // Negative seeds use their absolute value; large seeds use several key words. - assert_eq!(PyRandom::new(-3).random(), 0.23796462709189137); - assert_eq!(PyRandom::new((1 << 40) + 5).random(), 0.5043802970418443); - } - - #[test] - fn json_state_round_trips() { - let mut a = PyRandom::new(5); - a.gauss(1.0); - let mut b = PyRandom::from_json(&a.to_json()); - for _ in 0..1000 { - assert_eq!(a.gauss(1.0).to_bits(), b.gauss(1.0).to_bits()); - } - } - - #[test] - fn batched_normals_match_sequential_gauss() { - for (skip, count) in [(0, 0), (0, 1), (1, 7), (3, 10000), (2, 10001)] { - let (mut a, mut b) = (PyRandom::new(11), PyRandom::new(11)); - for _ in 0..skip { - assert_eq!(a.gauss(1.0), b.gauss(1.0)); - } - let batch = a.standard_normals(count); - for z in batch { - assert_eq!((0.0 + z).to_bits(), b.gauss(1.0).to_bits()); - } - assert_eq!(a.gauss(1.0).to_bits(), b.gauss(1.0).to_bits()); - assert_eq!(a.random().to_bits(), b.random().to_bits()); - } - } - - #[test] - fn bulk_uniforms_preserve_every_twist_boundary_and_checkpoint() { - for seed in [0, 11, -3, (1 << 40) + 5] { - for index in 0..=N { - let mut expected = PyRandom::new(seed); - expected.gauss(1.0); // Keep a cached normal across uniform draws. - for _ in 0..index { - expected.next_u32(); - } - let mut actual = expected.clone(); - for count in [0, 1, 2, 311, 312, 313, 625] { - let mut values = vec![0.0; count]; - actual.fill_uniforms(&mut values); - for value in values { - assert_eq!(value.to_bits(), expected.random().to_bits()); - } - assert_eq!(actual.to_json(), expected.to_json()); - } - } - } - } - - #[test] - fn reused_normal_storage_preserves_cached_values_and_rng_state() { - let mut expected = PyRandom::new(123); - let mut actual = expected.clone(); - let mut storage = vec![f64::NAN; 20000]; - // Counts around the pipelining block size (65536 doubles) and beyond several blocks. - for count in [ - 0, 1, 0, 7, 623, 624, 625, 10001, 2, 0, 3, 65535, 65536, 65537, 131073, 300001, - ] { - actual.standard_normals_into(count, &mut storage); - assert_eq!(storage.len(), count); - for &value in &storage { - assert_eq!((0.0 + value).to_bits(), expected.gauss(1.0).to_bits()); - } - assert_eq!(actual.to_json(), expected.to_json()); - assert_eq!(actual.randrange(57), expected.randrange(57)); - } - } -} - -#[cfg(test)] -mod input_tests { - use super::*; - #[test] - fn malformed_words_indices_and_gaussian_bits_are_rejected() { - let valid = PyRandom::new(7).to_json(); - let mut bad = valid.clone(); - bad["state"][0] = serde_json::json!(u64::MAX); - assert!(PyRandom::try_from_json(&bad).is_err()); - bad = valid.clone(); - bad["index"] = serde_json::json!(625); - assert!(PyRandom::try_from_json(&bad).is_err()); - bad = valid; - bad["gauss_next_bits"] = serde_json::json!(f64::NAN.to_bits()); - assert!(PyRandom::try_from_json(&bad).is_err()); - } -} diff --git a/github-simulator/source/src/training/runner.rs b/github-simulator/source/src/training/runner.rs deleted file mode 100644 index 3ae4534fd319107ff19e7378252031b0c28d73e2..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/training/runner.rs +++ /dev/null @@ -1,561 +0,0 @@ -//! Population installation, vehicle reuse, and generation turnover. - -use super::agent::AgentScratch; -use super::optimizer::{self, Optimizer, Produced, Record}; -use super::{SensorLayout, TrainingAgent, TrainingRandom, TrainingStats}; -use crate::math::vec2::V2; -use crate::nn::network::Network; -use crate::physics::car::{Car, Controls, SensorScratch, DT}; -use crate::track::world::World; -use crate::training::evolution::{AgentResult, EvolutionSettings, Generation}; -use rayon::prelude::*; -use serde_json::Value; -use std::sync::Arc; - -mod gpu; -mod rays; -mod schedule; - -pub use gpu::export_state_into; - -/// Which output slot of `Controls` each network output feeds. -#[derive(Clone, Copy, Debug)] -enum ControlSlot { - Acceleration, - Steering, - Brake, - Handbrake, - Boost, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum Mode { - /// Each car runs the whole window on its own (fast path). - Independent, - /// All cars advance one tick at a time, as `TrainingRunner.step`. - Lockstep, -} - -/// A window advanced tick by tick with the ray sensors served by an external -/// raycaster (the WebGPU raycaster of the WebAssembly library). After -/// `TrainingRunner::begin_ray_window`, each tick is `prepare_ray_tick`, -/// `ray_queries`, and `finish_ray_tick` with the hits, until `prepare_ray_tick` -/// returns false; `end_ray_window` then commits the window. With hits equal -/// to the track's raycasts, the agents equal those of `advance`. -#[cfg(any(test, all(target_arch = "wasm32", feature = "wasm")))] -pub(crate) struct RayWindow { - start_tick: u64, - start_batch: usize, - ticks: u64, - stop_when_inactive: bool, - time_limit: Option, - executed: u64, - finished: bool, - transition_without_drive: bool, - batch_index: usize, - /// The agents whose controls update in the prepared tick. - pub(crate) inferring: Vec, -} - -#[cfg(any(test, all(target_arch = "wasm32", feature = "wasm")))] -impl RayWindow { - /// The tick the prepared step drives (`TrainingRunner::tick` plus the - /// executed count). - pub(crate) fn tick(&self) -> u64 { - self.start_tick + self.executed - } -} - -/// Values per car in `TrainingRunner::car_states`, in `CAR_STATE_FIELDS` order. -pub(crate) const CAR_STATE_STRIDE: usize = 14; -/// `car_states` layout: position, rotation, velocity, angular velocity, -/// active flag, distance score, lap count, best lap time (-1 without a lap), -/// wall contacts, boost energy, the steering wheel angle in degrees, and the -/// statistics update count. -#[cfg(any(feature = "server", all(target_arch = "wasm32", feature = "wasm")))] -pub(crate) const CAR_STATE_FIELDS: [&str; CAR_STATE_STRIDE] = [ - "x", - "y", - "rotation", - "velocity_x", - "velocity_y", - "angular_velocity", - "active", - "score", - "lap_count", - "best_lap_time", - "collision_count", - "boost", - "wheel_angle", - "update_count", -]; - -pub struct TrainingRunner { - pub world: Arc, - pub position: V2, - pub rotation: f64, - pub layout: SensorLayout, - outputs: Vec, - pub settings: EvolutionSettings, - pub rng: TrainingRandom, - /// Builds each new population; replaced when `settings.algorithm` changes. - pub(crate) optimizer: Box, - pub batch_count: usize, - pub stats_phase: u64, - pub eliminate_on_wall: bool, - pub eliminate_when_idle: bool, - pub agents: Vec, - pub generation: u64, - pub tick: u64, - pub batch_index: usize, - pub mode: Mode, - user_paused: bool, - physics: Option, -} - -impl TrainingRunner { - // Preserve the public constructor's independent replay settings. - #[allow(clippy::too_many_arguments)] - pub fn new( - world: Arc, - position: V2, - rotation: f64, - layout: SensorLayout, - output_names: &[String], - settings: EvolutionSettings, - rng: impl Into, - batch_count: usize, - stats_phase: u64, - eliminate_on_wall: bool, - eliminate_when_idle: bool, - ) -> TrainingRunner { - assert!(batch_count > 0, "batch_count must be positive"); - // Controls(**dict(zip(names, outputs))): later duplicates win, which - // sequential assignment reproduces. - let outputs = output_names - .iter() - .map(|name| match name.to_lowercase().as_str() { - "acceleration" => ControlSlot::Acceleration, - "steering" => ControlSlot::Steering, - "brake" => ControlSlot::Brake, - "handbrake" => ControlSlot::Handbrake, - "boost" => ControlSlot::Boost, - other => panic!("unknown control output: {other}"), - }) - .collect(); - TrainingRunner { - world, - position, - rotation, - layout, - outputs, - optimizer: optimizer::create(&settings.algorithm), - settings, - rng: rng.into(), - batch_count, - stats_phase: stats_phase % 6, - eliminate_on_wall, - eliminate_when_idle, - agents: Vec::new(), - generation: 0, - tick: 0, - batch_index: 0, - mode: Mode::Independent, - user_paused: false, - physics: None, - } - } - - /// GeneralConstants divides two integers before RoundToInt/Clamp. - pub fn batches_per_tick(&self) -> usize { - (8 / self.batch_count).clamp(1, 8) - } - - /// Replaces the optimizer when `settings.algorithm` no longer names it. - /// A fresh optimizer starts from the cars it is next given. - fn sync_optimizer(&mut self) { - if self.optimizer.name() != self.settings.algorithm { - self.optimizer = optimizer::create(&self.settings.algorithm); - } - } - - /// Forgets the optimizer's state, which belonged to cars no longer installed. - pub(crate) fn reset_optimizer(&mut self) { - self.optimizer = optimizer::create(&self.settings.algorithm); - } - - fn start_produced(&mut self, seed: &Network, trace: bool) -> Produced { - self.sync_optimizer(); - self.optimizer - .start(seed, &self.settings, &mut self.rng, self.world.math, trace) - } - - /// The population after the current agents, by the configured algorithm. - fn turnover(&mut self, scores: Option>, scratch: &mut Vec, trace: bool) -> Produced { - self.sync_optimizer(); - let results: Vec = self.agents.iter().map(TrainingAgent::result).collect(); - self.optimizer.next( - &results, - scores, - &self.settings, - &mut self.rng, - scratch, - self.world.math, - trace, - ) - } - - pub fn start(&mut self, seed: &Network) -> Generation { - let initial = self.start_produced(seed, false).generation; - self.install(&initial.networks, false); - initial - } - - /// `start`, returning the record a checkpoint rebuilds the first population from. - pub(crate) fn start_traced(&mut self, seed: &Network) -> Record { - let Produced { generation, record } = self.start_produced(seed, true); - self.install_with_novelty(generation.networks, false, None); - record.expect("a traced start returns its record") - } - - /// `resume` with networks the new agents take over without a copy. - pub(crate) fn resume_owned(&mut self, networks: Vec, generation: u64) { - self.reset_optimizer(); - self.install_with_novelty(networks, generation > 0, None); - self.generation = generation; - } - - /// `next_generation`, installing the offspring without a copy and - /// returning the record a checkpoint rebuilds them from. - pub(crate) fn next_generation_traced(&mut self) -> (Turnover, Record) { - let Produced { - generation: - Generation { - networks, - preserved_count, - rewards, - }, - record, - } = self.turnover(None, &mut Vec::new(), true); - self.install_with_novelty(networks, true, None); - self.stats_phase = 0; - self.generation += 1; - ( - Turnover { - preserved_count, - rewards, - }, - record.expect("a traced turnover returns its record"), - ) - } - - /// Reproduce the initial population, generate its track, then install cars. - /// Supply a fresh, independent track RNG state for each game generation. - pub fn start_random_track( - &mut self, - seed: &Network, - template: &Value, - config: &mut crate::track::random_track::RandomTrackConfig, - track_state: &mut [u64; 4], - ) -> Result<(Generation, crate::track::random_track::GeneratedTrack), &'static str> { - let initial = self.start_produced(seed, false).generation; - let track = self.generate_track(template, config, track_state)?; - self.install(&initial.networks, false); - Ok((initial, track)) - } - - /// Restore the state `next_generation` left behind: `networks` installed on - /// reused vehicles as generation `generation` (restore `rng` separately). - pub fn resume(&mut self, networks: &[Network], generation: u64) { - self.reset_optimizer(); - self.install(networks, generation > 0); - self.generation = generation; - } - - fn install(&mut self, networks: &[Network], reused: bool) { - self.install_with_novelty(networks.par_iter().cloned().collect(), reused, None); - } - - /// Installs `networks`, which the new agents take over without copying. - fn install_with_novelty(&mut self, networks: Vec, reused: bool, novelty: Option<&[f64]>) { - let mut profile = crate::training::training_profile::Profile::new("install"); - assert!(!networks.is_empty(), "a training generation needs at least one network"); - if let Some(values) = novelty { - assert_eq!(values.len(), networks.len()); - } - let count = networks.len() as f64; - let size = networks[0].params.len(); - let mean: Vec = if novelty.is_none() { - (0..size) - .into_par_iter() - .map(|i| networks.iter().map(|n| n.params[i]).fold(0.0, |a, b| a + b) / count) - .collect() - } else { - Vec::new() - }; - profile.mark("mean"); - let world = &*self.world; - let math = world.math; - let (position, rotation) = (self.position, self.rotation); - let retained = if reused { - self.agents.len().min(networks.len()) - } else { - 0 - }; - // VehicleManager keeps a FIFO of physical vehicles, independently of - // network selection. Native contact and broad-phase caches survive. - let mut old = std::mem::take(&mut self.agents).into_iter(); - let cars: Vec = networks - .iter() - .map(|_| { - if reused { - old.next() - .map(|agent| agent.car) - .unwrap_or_else(|| Car::new(world, position, rotation)) - } else { - Car::new(world, position, rotation) - } - }) - .collect(); - profile.mark("vehicles"); - self.agents = cars - .into_par_iter() - .zip(networks.into_par_iter()) - .enumerate() - .map(|(i, (mut car, network))| { - // VehicleManager resets both new and reused instances. - car.queue_reset(position, rotation); - let novelty = novelty.map_or_else( - || { - network - .params - .iter() - .zip(&mean) - .map(|(&a, &b)| math.pow(a - b, 2.0)) - .fold(0.0, |a, b| a + b) - .sqrt() - }, - |values| values[i], - ); - TrainingAgent { - network, - car, - stats: TrainingStats::new(novelty), - controls: Controls::default(), - pending_contact: false, - deactivated_at: None, - scratch: AgentScratch::default(), - } - }) - .collect(); - profile.mark("agents_and_novelty"); - if world.track.native_broadphase && self.physics.is_none() { - self.physics = Some(crate::physics::simulation::SharedPhysics::new( - world, - &mut self.agents.iter_mut().map(|a| &mut a.car).collect::>(), - )); - } else if let Some(physics) = self.physics.as_mut() { - physics.resize( - world, - &mut self.agents.iter_mut().map(|a| &mut a.car).collect::>(), - retained, - ); - } - self.settle_reset(); - profile.mark("passive_reset"); - } - - /// Reset an evaluation without replacing networks or advancing evolution. - /// Random training uses independent cars; native shared redraw replay is separate. - pub fn reset_evaluation(&mut self) { - assert!( - !self.world.track.native_broadphase, - "track batches require independent cars" - ); - let (position, rotation) = (self.position, self.rotation); - self.agents.par_iter_mut().for_each(|agent| { - let novelty = agent.stats.network_novelty; - agent.car.queue_reset(position, rotation); - agent.stats = TrainingStats::new(novelty); - agent.controls = Controls::default(); - agent.pending_contact = false; - agent.deactivated_at = None; - agent.scratch.sensors = SensorScratch::default(); - }); - self.stats_phase = 0; - self.settle_reset(); - } - - fn settle_reset(&mut self) { - let world = &*self.world; - Self::advance_physics( - world, - &mut self.agents, - &mut self.physics, - self.eliminate_on_wall, - false, - 0, - ); - // Reset above represents the first _IntegrateForces callback applying - // the pending spawn request. StartNextGeneration runs on the sixth - // eligible stable callback, before that callback's drive update. - let mut stable_callbacks = 0; - loop { - if self.agents.iter().all(|a| { - crate::math::godot_math::F2::from(a.car.velocity).length() < 0.001 - && (a.car.angular_velocity as f32) < 0.001 - }) { - stable_callbacks += 1; - } - if stable_callbacks == 6 { - break; - } - Self::advance_physics( - world, - &mut self.agents, - &mut self.physics, - self.eliminate_on_wall, - false, - 0, - ); - } - self.tick = 0; - self.batch_index = 0; - } - - fn advance_physics( - world: &World, - agents: &mut [TrainingAgent], - physics: &mut Option, - eliminate: bool, - drive: bool, - tick: u64, - ) { - if let Some(physics) = physics { - let controls: Vec<_> = agents.iter().map(|a| a.controls).collect(); - let was_active: Vec<_> = agents.iter().map(|a| a.car.active).collect(); - let contacts = physics.advance( - world, - &mut agents.iter_mut().map(|a| &mut a.car).collect::>(), - &controls, - eliminate, - drive, - ); - for ((agent, contact), was_active) in agents.iter_mut().zip(contacts).zip(was_active) { - agent.pending_contact |= contact; - if was_active && !agent.car.active { - agent.deactivated_at = Some(tick); - } - } - } else { - agents.par_iter_mut().for_each(|a| { - let was_active = a.car.active; - a.pending_contact |= if drive { - a.car.step(world, &a.controls, DT, eliminate) - } else { - a.car.passive_step(world, DT, eliminate) - }; - if was_active && !a.car.active { - a.deactivated_at = Some(tick); - } - }); - } - } - - pub fn results(&self) -> Vec> { - self.agents.iter().map(TrainingAgent::result).collect() - } - - /// The original reproduction -> random track -> TileMap redraw -> car reset order. - /// On success, `config` becomes the returned track's configuration, including - /// a pinned start and any fallback shortening, for the following generation. - pub fn next_generation_random_track( - &mut self, - template: &Value, - config: &mut crate::track::random_track::RandomTrackConfig, - track_state: &mut [u64; 4], - ) -> Result<(Generation, crate::track::random_track::GeneratedTrack), &'static str> { - // A failed track leaves the old population installed, so the optimizer - // must go on from the state that produced it. The RNG stays advanced. - let saved = self.optimizer.boxed_clone(); - let generation = self.reproduce_next(); - let track = match self.generate_track(template, config, track_state) { - Ok(track) => track, - Err(e) => { - self.optimizer = saved; - return Err(e); - } - }; - self.install_next(&generation); - Ok((generation, track)) - } - - fn generate_track( - &mut self, - template: &Value, - config: &mut crate::track::random_track::RandomTrackConfig, - track_state: &mut [u64; 4], - ) -> Result { - let track = if let Some(seed) = template["runtime"]["hashcode_seed"].as_u64() { - let seed = u32::try_from(seed).map_err(|_| "HashCode seed exceeds u32")?; - crate::track::random_track::TrackGenerator::new(seed).generate(config, track_state)? - } else { - crate::track::random_track::generate(config, track_state)? - }; - let math = self.world.math; - let scene = track.to_scene(template, math); - let position = crate::track::curve::json_vector(&scene["reset_position"]).into(); - let rotation = scene["reset_rotation"].as_f64().unwrap(); - self.replace_track(Arc::new(World::from_scene_with(&scene, math)), position, rotation); - *config = track.config.clone(); - Ok(track) - } - - /// Select a prepared track before starting or installing a generation. - pub fn replace_track(&mut self, world: Arc, position: V2, rotation: f64) { - if let Some(physics) = self.physics.as_mut() { - physics.queue_tilemap_redraw(self.world.clone()); - } - if self.physics.is_none() { - for agent in &mut self.agents { - agent.car.clear_track_contacts(); - } - } - self.world = world; - self.position = position; - self.rotation = rotation; - } - - fn reproduce_next(&mut self) -> Generation { - self.turnover(None, &mut Vec::new(), false).generation - } - - fn install_next(&mut self, generation: &Generation) { - self.install(&generation.networks, true); - self.stats_phase = 0; - self.generation += 1; - } - - /// Reproduce and reset cars on the current track. - pub fn next_generation(&mut self) -> Generation { - let generation = self.reproduce_next(); - self.install_next(&generation); - generation - } - - pub fn next_generation_with_fitness(&mut self, scores: Vec) -> Generation { - let generation = self.turnover(Some(scores), &mut Vec::new(), false).generation; - self.install_next(&generation); - generation - } -} - -/// The selection summary of a GPU turnover (`Generation` without the -/// offspring, which the installed agents own). -pub struct Turnover { - pub preserved_count: usize, - pub rewards: Vec, -} - -#[cfg(test)] -mod tests; diff --git a/github-simulator/source/src/training/runner/gpu.rs b/github-simulator/source/src/training/runner/gpu.rs deleted file mode 100644 index 6edb9b86c286d6be0ac3149940a61f1afa55f776..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/training/runner/gpu.rs +++ /dev/null @@ -1,332 +0,0 @@ -//! GPU state transfers, driving windows, and population turnover. - -use super::TrainingRunner; -#[cfg(not(target_arch = "wasm32"))] -use super::Turnover; -#[cfg(not(target_arch = "wasm32"))] -use crate::physics::car::Sensor; -#[cfg(not(target_arch = "wasm32"))] -use crate::training::evolution::Generation; -use crate::training::TrainingAgent; -use rayon::prelude::*; - -/// Fills `cars` and `out` with the GPU state of `agents` (`Car::gpu_export` -/// and `agent_export`) in place, resizing them to the population. Retaining -/// the vectors between windows keeps their pages mapped, and the indexed -/// parallel fill avoids the linked-list concatenation of a `Result` collect. -pub fn export_state_into( - agents: &[TrainingAgent], - vehicle: &crate::track::world::VehicleConfig, - surfaces: &crate::gpu::simulation::SurfaceTable, - cars: &mut Vec, - out: &mut Vec, -) -> Result<(), String> { - cars.resize(agents.len(), crate::gpu::simulation::GpuCar::zeroed()); - out.resize(agents.len(), crate::gpu::simulation::GpuAgent::default()); - agents - .par_iter() - .zip(cars.par_iter_mut()) - .zip(out.par_iter_mut()) - .try_for_each(|((a, c), g)| { - *c = a.car.gpu_export(vehicle, surfaces)?; - *g = crate::gpu::simulation::agent_export(a)?; - Ok::<(), String>(()) - }) -} - -impl TrainingRunner { - /// Every car's and agent's GPU state as words, for exact comparisons of - /// the CPU and GPU simulators. - pub(crate) fn canonical_state(&self, surfaces: &crate::gpu::simulation::SurfaceTable) -> Result, String> { - let mut cars = Vec::new(); - let mut agents = Vec::new(); - export_state_into(&self.agents, &self.world.vehicle, surfaces, &mut cars, &mut agents)?; - let mut out = crate::gpu::simulation::words(&cars); - out.extend(crate::gpu::simulation::words(&agents)); - Ok(out) - } - - /// A GPU simulator for this runner's world, vehicle and sensors, with - /// room for `capacity` agents. - #[cfg(not(target_arch = "wasm32"))] - pub fn gpu_sim<'a>( - &self, - world: &crate::gpu::hip::GpuWorld<'a>, - capacity: usize, - ) -> Result, String> { - let vehicle = crate::gpu::simulation::vehicle_desc(&self.world.vehicle)?; - // The GPU path sensors sample the Curve2D only; without track.curve the - // CPU falls back to the baked path, which the GPU does not implement. - let path_sensor = self - .layout - .sensors - .iter() - .any(|s| matches!(s, Sensor::CorrectDirection | Sensor::TrackCurvature { .. })); - if path_sensor && self.world.track.curve.is_none() && self.world.track.path.len() >= 2 { - return Err( - "the scene has no track.curve (Curve2D control points), which the correct_direction and \ - track_curvature sensors need on the GPU; add the curve to the scene or train without --gpu" - .into(), - ); - } - if self - .layout - .sensors - .iter() - .any(|sensor| matches!(sensor, Sensor::Raycast { length, .. } if *length <= 0.0 || !length.is_finite())) - { - return Err("the HIP simulator requires positive finite ray lengths".into()); - } - let sensors: Vec<_> = self - .layout - .sensors - .iter() - .map(crate::gpu::simulation::sensor_desc) - .collect(); - crate::gpu::simulation::GpuSim::try_new(world, &vehicle, &sensors, capacity) - } - - /// `advance_generation` on the GPU. - #[cfg(not(target_arch = "wasm32"))] - pub fn advance_generation_gpu( - &mut self, - sim: &mut crate::gpu::simulation::GpuSim, - world: &crate::gpu::hip::GpuWorld, - time_limit_ticks: u64, - ) -> Result { - let (ticks, time_limit) = self.gpu_generation_window(time_limit_ticks)?; - self.advance_window_gpu(sim, world, ticks, true, Some(time_limit)) - } - - /// The window length and time limit (seconds) of `advance_generation_gpu`. - #[cfg(not(target_arch = "wasm32"))] - pub(crate) fn gpu_generation_window(&self, time_limit_ticks: u64) -> Result<(u64, f64), String> { - if self.stats_phase != 0 { - return Err("GPU advance_generation needs statistics phase 0".into()); - } - let bound = (time_limit_ticks / 6).saturating_add(2).saturating_mul(6); - Ok((bound.saturating_sub(self.tick), time_limit_ticks as f64 / 60.0)) - } - - /// `advance_window` on the GPU: agents and cars are uploaded, advanced in - /// the tick-major order and read back. Networks are uploaded when - /// `sim.network_tag` differs from the generation. - #[cfg(not(target_arch = "wasm32"))] - pub fn advance_window_gpu( - &mut self, - sim: &mut crate::gpu::simulation::GpuSim, - world: &crate::gpu::hip::GpuWorld, - ticks: u64, - stop_when_inactive: bool, - time_limit: Option, - ) -> Result { - if ticks == 0 { - return Ok(0); - } - self.check_gpu_window()?; - self.upload_state_gpu(sim, world)?; - let executed = self.window_gpu(sim, ticks, stop_when_inactive, time_limit)?; - self.download_state_gpu(sim, world)?; - Ok(executed) - } - - /// Why the GPU cannot run this runner's windows, if it cannot. - #[cfg(not(target_arch = "wasm32"))] - pub(crate) fn check_gpu_window(&self) -> Result<(), String> { - if self.user_paused || self.physics.is_some() { - return Err("the GPU runner does not model paused or native-broadphase windows".into()); - } - if self.agents.is_empty() { - return Err("no agents".into()); - } - Ok(()) - } - - /// Uploads every car and agent, after clearing `deactivated_at` as a - /// window does (the device clears its copy when a window starts). - #[cfg(not(target_arch = "wasm32"))] - pub(crate) fn upload_state_gpu( - &mut self, - sim: &mut crate::gpu::simulation::GpuSim, - world: &crate::gpu::hip::GpuWorld, - ) -> Result<(), String> { - let mut profile = crate::training::training_profile::Profile::new("gpu_upload"); - for agent in &mut self.agents { - agent.deactivated_at = None; - } - // Retained staging buffers: filling them in place avoids allocating, - // page-faulting and concatenating population-sized vectors per window. - let (mut cars, mut agents) = sim.take_state_buffers(); - let exported = export_state_into( - &self.agents, - &self.world.vehicle, - &world.arrays.surfaces, - &mut cars, - &mut agents, - ); - profile.mark("export_state"); - let uploaded = exported.and_then(|()| sim.try_upload(&cars, Some(&agents))); - profile.mark("upload_state"); - sim.return_state_buffers(cars, agents); - uploaded - } - - /// A window over the uploaded cars and agents, which stay on the device: - /// only `tick` and the batch index advance here. Networks are uploaded - /// when `sim.network_tag` differs from the generation. - #[cfg(not(target_arch = "wasm32"))] - pub(crate) fn window_gpu( - &mut self, - sim: &mut crate::gpu::simulation::GpuSim, - ticks: u64, - stop_when_inactive: bool, - time_limit: Option, - ) -> Result { - use crate::gpu::simulation::*; - let mut profile = crate::training::training_profile::Profile::new("gpu_window"); - if ticks == 0 { - return Ok(0); - } - self.check_gpu_window()?; - if sim.cars != self.agents.len() { - return Err("the HIP simulator holds a different population".into()); - } - if sim.network_tag != Some(self.generation) || sim.network_count() != self.agents.len() { - let src = std::array::from_fn(|c| { - self.outputs - .iter() - .rposition(|&slot| slot as usize == c) - .map_or(-1, |j| j as i32) - }); - // Agents own the networks; the GPU uploader reuses its staging - // allocation while copying them without cloning parameter vectors. - sim.upload_agent_networks(&self.agents, src)?; - profile.mark("networks"); - sim.network_tag = Some(self.generation); - } - let args = WindowArgs { - start_tick: self.tick, - ticks, - start_batch: self.batch_index as u32, - batches_per_tick: self.batches_per_tick() as u32, - stats_phase: self.stats_phase as u32, - stop_when_inactive: stop_when_inactive as u32, - eliminate_on_wall: self.eliminate_on_wall as u32, - eliminate_when_idle: self.eliminate_when_idle as u32, - has_time_limit: time_limit.is_some() as u32, - pad: 0, - time_limit: time_limit.unwrap_or(0.0), - }; - let (executed, transition_without_drive) = sim.try_window(&args)?; - profile.mark("window"); - let bpt = self.batches_per_tick(); - self.tick += executed; - self.batch_index = (self.batch_index + ((executed as usize - transition_without_drive as usize) % 8) * bpt) % 8; - Ok(executed) - } - - /// Reads every uploaded car and agent back into the runner. - #[cfg(not(target_arch = "wasm32"))] - pub(crate) fn download_state_gpu( - &mut self, - sim: &mut crate::gpu::simulation::GpuSim, - world: &crate::gpu::hip::GpuWorld, - ) -> Result<(), String> { - let mut profile = crate::training::training_profile::Profile::new("gpu_download"); - if sim.cars != self.agents.len() { - return Err("the HIP simulator holds a different population".into()); - } - // Read back into the retained allocations. - let (mut cars, mut agents) = sim.take_state_buffers(); - let downloaded = sim.try_download(Some(&mut cars), Some(&mut agents)); - profile.mark("download_state"); - let imported = downloaded.and_then(|()| self.import_state(&cars, &agents, &world.arrays.surfaces)); - profile.mark("import_state"); - sim.return_state_buffers(cars, agents); - imported - } - - /// The rest of `gpu_import` for the whole population: `cars` and `agents` - /// hold what `advance_window_gpu` read back. - pub(crate) fn import_state( - &mut self, - cars: &[crate::gpu::simulation::GpuCar], - agents: &[crate::gpu::simulation::GpuAgent], - surfaces: &crate::gpu::simulation::SurfaceTable, - ) -> Result<(), String> { - self.agents - .par_iter_mut() - .zip(cars) - .zip(agents) - .try_for_each(|((a, c), g)| { - a.car.gpu_import(c, surfaces)?; - crate::gpu::simulation::agent_import(g, a); - Ok::<(), String>(()) - }) - } - - /// Reproduce with the same RNG stream, then compute population novelty on - /// the GPU. Uploaded offspring stay in place for the next driving window, - /// and the new agents take over the offspring networks without a copy - /// (read them from `agents`; only the selection summary is returned). - #[cfg(not(target_arch = "wasm32"))] - pub fn next_generation_gpu(&mut self, sim: &mut crate::gpu::simulation::GpuSim) -> Result { - self.next_generation_gpu_scored(sim, None) - } - - #[cfg(not(target_arch = "wasm32"))] - pub fn next_generation_gpu_with_fitness( - &mut self, - sim: &mut crate::gpu::simulation::GpuSim, - scores: Vec, - ) -> Result { - self.next_generation_gpu_scored(sim, Some(scores)) - } - - #[cfg(not(target_arch = "wasm32"))] - fn next_generation_gpu_scored( - &mut self, - sim: &mut crate::gpu::simulation::GpuSim, - scores: Option>, - ) -> Result { - let mut profile = crate::training::training_profile::Profile::new("gpu_turnover"); - // The preceding upload is complete, so its host buffer can hold noise - // until reproduction finishes. Refilling it then uploads the offspring. - let mut scratch = sim.take_parameter_buffer(); - // Until the offspring are installed, the optimizer must still describe the old cars. - let saved = self.optimizer.boxed_clone(); - let Generation { - networks, - preserved_count, - rewards, - } = self.turnover(scores, &mut scratch, false).generation; - sim.return_parameter_buffer(scratch); - profile.mark("reproduce"); - let src = std::array::from_fn(|c| { - self.outputs - .iter() - .rposition(|&slot| slot as usize == c) - .map_or(-1, |j| j as i32) - }); - let novelty = match sim.upload_networks(&networks, src).and_then(|()| { - profile.mark("networks"); - sim.try_novelty() - }) { - Ok(novelty) => novelty, - Err(e) => { - self.optimizer = saved; - return Err(e); - } - }; - profile.mark("novelty"); - self.install_with_novelty(networks, true, Some(&novelty)); - self.stats_phase = 0; - self.generation += 1; - sim.network_tag = Some(self.generation); - profile.mark("install"); - Ok(Turnover { - preserved_count, - rewards, - }) - } -} diff --git a/github-simulator/source/src/training/runner/rays.rs b/github-simulator/source/src/training/runner/rays.rs deleted file mode 100644 index 3626d70cf2c2b6f2c495e2944cfe47e96b0d51cb..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/training/runner/rays.rs +++ /dev/null @@ -1,176 +0,0 @@ -//! Tick-by-tick windows with externally supplied ray hits. - -#[cfg(any(test, all(target_arch = "wasm32", feature = "wasm")))] -use super::RayWindow; -use super::TrainingRunner; -#[cfg(any(test, all(target_arch = "wasm32", feature = "wasm")))] -use crate::physics::car::Sensor; -#[cfg(any(test, all(target_arch = "wasm32", feature = "wasm")))] -use rayon::prelude::*; - -impl TrainingRunner { - /// Starts a `RayWindow` of up to `ticks` ticks (`advance_window` with an - /// external raycaster). Paused runners, native shared broadphase scenes, - /// and ray sensors without a BSP ray tree are not supported. - #[cfg(any(test, all(target_arch = "wasm32", feature = "wasm")))] - pub(crate) fn begin_ray_window( - &mut self, - ticks: u64, - stop_when_inactive: bool, - time_limit: Option, - ) -> Result { - if self.user_paused || self.physics.is_some() { - return Err("external raycasting does not model paused or native-broadphase windows".into()); - } - if self.layout.ray_count() > 0 && self.world.track.ray_tree().is_none() { - return Err("the scene has no BSP raycaster; the ray sensors need its RayTree".into()); - } - for agent in &mut self.agents { - agent.deactivated_at = None; - } - Ok(RayWindow { - start_tick: self.tick, - start_batch: self.batch_index, - ticks, - stop_when_inactive, - time_limit, - executed: 0, - finished: false, - transition_without_drive: false, - batch_index: self.batch_index, - inferring: Vec::new(), - }) - } - - /// Runs the statistics of the next tick and selects the agents that infer - /// in it. Returns false once the window is complete or stops before - /// driving (every car inactive, or the time limit reached). - #[cfg(any(test, all(target_arch = "wasm32", feature = "wasm")))] - pub(crate) fn prepare_ray_tick(&mut self, w: &mut RayWindow) -> bool { - if w.finished || w.executed >= w.ticks { - w.finished = true; - return false; - } - w.executed += 1; - let tick = w.tick(); - w.batch_index = (w.start_batch + ((w.executed - 1) as usize % 8) * self.batches_per_tick()) % 8; - let mut agents = std::mem::take(&mut self.agents); - let stop = { - let schedule = self.schedule_for(agents.len()); - agents - .par_iter_mut() - .for_each(|agent| schedule.update_stats(agent, tick)); - let stop = tick % 6 == self.stats_phase && { - let first = if self.stats_phase == 0 { 6 } else { self.stats_phase }; - let elapsed = ((tick - first) / 6) as f64 * 0.1; - (w.stop_when_inactive && agents.iter().all(|a| !a.car.active)) - || w.time_limit.is_some_and(|limit| elapsed >= limit) - }; - if !stop { - w.inferring.clear(); - w.inferring.extend( - agents - .iter() - .enumerate() - .filter(|(i, a)| schedule.infers(*i, w.batch_index) && a.car.active) - .map(|(i, _)| i), - ); - } - stop - }; - self.agents = agents; - if stop { - w.transition_without_drive = true; - w.finished = true; - } - !stop - } - - /// The ray sensor segments of the inferring agents, `[start.x, start.y, - /// end.x, end.y]` in float32 as `RayTree::raycast` takes them: for each - /// agent of `w.inferring`, its `Sensor::Raycast` entries in layout order. - #[cfg(any(test, all(target_arch = "wasm32", feature = "wasm")))] - pub(crate) fn ray_queries(&self, w: &RayWindow, out: &mut Vec<[f32; 4]>) { - use crate::math::godot_math::F2; - out.clear(); - for &i in &w.inferring { - let car = &self.agents[i].car; - let start = F2::from(car.position); - for sensor in &self.layout.sensors { - if let Sensor::Raycast { degrees, length } = *sensor { - let end = F2::from(car.ray_end(degrees, length).0); - out.push([start.x, start.y, end.x, end.y]); - } - } - } - } - - /// Sets the inferring agents' controls from `hits` (one per query of - /// `ray_queries`, `[hit.x, hit.y, hit != 0, _]`), then drives every car. - #[cfg(any(test, all(target_arch = "wasm32", feature = "wasm")))] - pub(crate) fn finish_ray_tick(&mut self, w: &RayWindow, hits: &[[f32; 4]]) -> Result<(), String> { - let rays = self.layout.ray_count(); - if hits.len() != w.inferring.len() * rays { - return Err(format!( - "expected {} ray hits, got {}", - w.inferring.len() * rays, - hits.len() - )); - } - let tick = w.tick(); - let mut agents = std::mem::take(&mut self.agents); - { - let schedule = self.schedule_for(agents.len()); - for (&i, hits) in w.inferring.iter().zip(hits.chunks(rays.max(1))) { - let agent = &mut agents[i]; - let scratch = &mut agent.scratch; - schedule.layout.read_into_with_hits( - schedule.world, - &agent.car, - &mut scratch.sensors, - &mut scratch.inputs, - hits, - ); - schedule.set_controls(agent); - } - agents.par_iter_mut().for_each(|agent| schedule.step_agent(agent, tick)); - } - self.agents = agents; - Ok(()) - } - - /// Commits the window: `tick` and the inference cursor advance as after - /// `advance`. Returns the executed ticks. - #[cfg(any(test, all(target_arch = "wasm32", feature = "wasm")))] - pub(crate) fn end_ray_window(&mut self, w: RayWindow) -> u64 { - let bpt = self.batches_per_tick(); - self.tick = w.start_tick + w.executed; - self.batch_index = - (w.start_batch + ((w.executed as usize - w.transition_without_drive as usize) % 8) * bpt) % 8; - w.executed - } - - /// `CAR_STATE_STRIDE` values per agent (`CAR_STATE_FIELDS`), appended to `out`. - pub(crate) fn car_states(&self, out: &mut Vec) { - let steering = self.world.vehicle.wheels.iter().position(|w| w.steering); - for a in &self.agents { - let c = &a.car; - out.extend_from_slice(&[ - c.position.x, - c.position.y, - c.rotation, - c.velocity.x, - c.velocity.y, - c.angular_velocity, - c.active as u8 as f64, - a.stats.total_score, - a.stats.score.lap_count as f64, - a.stats.best_lap_time.unwrap_or(-1.0), - c.collision_count as f64, - c.boost_energy, - steering.map_or(0.0, |i| c.wheels[i].angle_deg), - a.stats.update_count as f64, - ]); - } - } -} diff --git a/github-simulator/source/src/training/runner/schedule.rs b/github-simulator/source/src/training/runner/schedule.rs deleted file mode 100644 index 51de96d9921d42629f6009e8e9cf7c2579363f12..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/training/runner/schedule.rs +++ /dev/null @@ -1,281 +0,0 @@ -//! Preserve car-major independent windows and tick-major shared physics windows. - -use super::{ControlSlot, Mode, TrainingRunner}; -use crate::physics::car::{Controls, DT}; -use crate::track::world::World; -use crate::training::{SensorLayout, TrainingAgent}; -use rayon::prelude::*; - -/// Immutable per-window schedule shared by all agents. -pub(super) struct Schedule<'a> { - pub(super) world: &'a World, - pub(super) layout: &'a SensorLayout, - outputs: &'a [ControlSlot], - stats_phase: u64, - batch_size: usize, - batches_per_tick: usize, - eliminate_on_wall: bool, - eliminate_when_idle: bool, -} - -impl Schedule<'_> { - pub(super) fn update_stats(&self, agent: &mut TrainingAgent, tick: u64) { - agent.update_stats(self.world, tick, self.stats_phase, self.eliminate_when_idle); - } - pub(super) fn drive_agent(&self, index: usize, agent: &mut TrainingAgent, tick: u64, batch_index: usize) { - self.update_controls(index, agent, batch_index); - self.step_agent(agent, tick); - } - /// The physics step of `drive_agent`, with the agent's current controls. - pub(super) fn step_agent(&self, agent: &mut TrainingAgent, tick: u64) { - let was_active = agent.car.active; - let contact = agent.car.step(self.world, &agent.controls, DT, self.eliminate_on_wall); - agent.pending_contact |= contact; - if was_active && !agent.car.active { - agent.deactivated_at = Some(tick); - } - } - /// Whether agent `index` belongs to an inference batch due at `batch_index`. - pub(super) fn infers(&self, index: usize, batch_index: usize) -> bool { - let batch = index / self.batch_size; - (batch + 8 - batch_index) % 8 < self.batches_per_tick - } - pub(super) fn update_controls(&self, index: usize, agent: &mut TrainingAgent, batch_index: usize) { - if self.infers(index, batch_index) && agent.car.active { - let scratch = &mut agent.scratch; - self.layout - .read_into(self.world, &agent.car, &mut scratch.sensors, &mut scratch.inputs); - self.set_controls(agent); - } - } - /// The network forward of the read inputs into the agent's controls. - pub(super) fn set_controls(&self, agent: &mut TrainingAgent) { - let scratch = &mut agent.scratch; - let outputs = agent - .network - .forward_into(&scratch.inputs, &mut scratch.forward, self.world.math); - let mut controls = Controls::default(); - for (slot, &value) in self.outputs.iter().zip(outputs) { - match slot { - ControlSlot::Acceleration => controls.acceleration = value, - ControlSlot::Steering => controls.steering = value, - ControlSlot::Brake => controls.brake = value, - ControlSlot::Handbrake => controls.handbrake = value, - ControlSlot::Boost => controls.boost = value, - } - } - agent.controls = controls; - } -} - -impl TrainingRunner { - pub(super) fn schedule(&self) -> Schedule<'_> { - Schedule { - world: &self.world, - layout: &self.layout, - outputs: &self.outputs, - stats_phase: self.stats_phase, - batch_size: self.agents.len().div_ceil(8), - batches_per_tick: self.batches_per_tick(), - eliminate_on_wall: self.eliminate_on_wall, - eliminate_when_idle: self.eliminate_when_idle, - } - } - - /// GameManager.OnPause during an installed running generation. Freeze - /// requests are deferred to native callbacks; the scheduler is not reset. - pub fn set_paused(&mut self, paused: bool) { - self.user_paused = paused; - for agent in &mut self.agents { - agent.car.set_frozen(paused); - } - } - pub fn is_paused(&self) -> bool { - self.user_paused - } - - /// `TrainingRunner.step`: advance all cars one physics tick. - pub fn step(&mut self) { - self.advance(1, false); - } - - /// Advance up to `ticks` ticks. With `stop_when_inactive`, stop after the - /// statistics callback at which every car is inactive. Returns the - /// number of native frames advanced. Paused frames preserve `tick` and - /// the inference/statistics cursors while native physics continues. - pub fn advance(&mut self, ticks: u64, stop_when_inactive: bool) -> u64 { - self.advance_window(ticks, stop_when_inactive, None) - } - - /// TrainGameManager checks elapsed time after stats and before driving. - /// `time_limit_ticks` specifies the configured time limit in 1/60 seconds. - pub fn advance_generation(&mut self, time_limit_ticks: u64) -> u64 { - let (ticks, time_limit) = self.generation_window(time_limit_ticks); - self.advance_window(ticks, true, Some(time_limit)) - } - - /// The remaining window of a generation with `time_limit_ticks`: the tick - /// bound and the time limit in seconds that `advance_generation` applies. - pub(crate) fn generation_window(&self, time_limit_ticks: u64) -> (u64, f64) { - assert_eq!( - self.stats_phase, 0, - "fresh game generations use reset statistics counters" - ); - let bound = (time_limit_ticks / 6 + 2) * 6; - (bound.saturating_sub(self.tick), time_limit_ticks as f64 / 60.0) - } - - pub(super) fn advance_window(&mut self, ticks: u64, stop_when_inactive: bool, time_limit: Option) -> u64 { - if ticks == 0 { - return 0; - } - if self.user_paused { - for _ in 0..ticks { - Self::advance_physics( - &self.world, - &mut self.agents, - &mut self.physics, - self.eliminate_on_wall, - false, - self.tick, - ); - } - return ticks; - } - let start_tick = self.tick; - let start_batch = self.batch_index; - let bpt = self.batches_per_tick(); - let mut agents = std::mem::take(&mut self.agents); - let mut physics = self.physics.take(); - for agent in &mut agents { - agent.deactivated_at = None; - } - let batch_at = |k: u64| (start_batch + ((k - 1) as usize % 8) * bpt) % 8; - let executed; - let mut transition_without_drive = false; - { - let schedule = self.schedule_for(agents.len()); - match self.mode { - Mode::Independent if physics.is_none() && !agents.is_empty() => { - // Car-major: each car runs the whole window alone, which keeps its - // network and state in cache and avoids per-tick barriers. A car stops - // before driving at the first statistics tick where the time - // limit is reached or, with `stop_when_inactive`, it is inactive. - // Cars never reactivate within a window, so the tick-major loop - // below would stop at the latest of these ticks, if every car - // stopped; cars that stopped earlier then catch up to it. - let stats_phase = self.stats_phase; - let limit_reached = |k: u64| { - let first = if stats_phase == 0 { 6 } else { stats_phase }; - time_limit.is_some_and(|limit| ((start_tick + k - first) / 6) as f64 * 0.1 >= limit) - }; - // Runs ticks `from..=to`; tick `from`'s statistics already ran - // when `resume` is set. Returns the tick at which `stop` held. - let run = |index: usize, - agent: &mut TrainingAgent, - from: u64, - to: u64, - resume: bool, - stop: &dyn Fn(u64, &TrainingAgent) -> bool| { - for k in from..=to { - let tick = start_tick + k; - if !(resume && k == from) { - schedule.update_stats(agent, tick); - if tick % 6 == stats_phase && stop(k, agent) { - return Some(k); - } - } - schedule.drive_agent(index, agent, tick, batch_at(k)); - } - None - }; - let stops: Vec> = agents - .par_iter_mut() - .with_max_len(1) - .enumerate() - .map(|(index, agent)| { - run(index, agent, 1, ticks, false, &|k, agent| { - limit_reached(k) || (stop_when_inactive && !agent.car.active) - }) - }) - .collect(); - let end = stops.iter().try_fold(0, |end, stop| stop.map(|k| end.max(k))); - agents - .par_iter_mut() - .with_max_len(1) - .enumerate() - .zip(&stops) - .for_each(|((index, agent), stop)| { - let Some(stopped) = *stop else { return }; - match end { - Some(end) if stopped < end => { - run(index, agent, stopped, end, true, &|k, agent| { - assert!(!agent.car.active, "a car reactivated within a window"); - k == end - }); - } - Some(_) => {} - None => { - run(index, agent, stopped, ticks, true, &|_, _| false); - } - } - }); - transition_without_drive = end.is_some(); - executed = end.unwrap_or(ticks); - } - _ => { - let mut k = 0; - while k < ticks { - k += 1; - let tick = start_tick + k; - let batch_index = batch_at(k); - agents - .par_iter_mut() - .for_each(|agent| schedule.update_stats(agent, tick)); - if tick % 6 == self.stats_phase { - let first = if self.stats_phase == 0 { 6 } else { self.stats_phase }; - let elapsed = ((tick - first) / 6) as f64 * 0.1; - if (stop_when_inactive && agents.iter().all(|a| !a.car.active)) - || time_limit.is_some_and(|limit| elapsed >= limit) - { - transition_without_drive = true; - break; - } - } - if physics.is_some() { - agents - .par_iter_mut() - .enumerate() - .for_each(|(index, agent)| schedule.update_controls(index, agent, batch_index)); - Self::advance_physics( - schedule.world, - &mut agents, - &mut physics, - self.eliminate_on_wall, - true, - tick, - ); - } else { - agents - .par_iter_mut() - .enumerate() - .for_each(|(index, agent)| schedule.drive_agent(index, agent, tick, batch_index)); - } - } - executed = k; - } - } - } - self.agents = agents; - self.physics = physics; - self.tick = start_tick + executed; - self.batch_index = (start_batch + ((executed as usize - transition_without_drive as usize) % 8) * bpt) % 8; - executed - } - - pub(super) fn schedule_for(&self, count: usize) -> Schedule<'_> { - let mut schedule = self.schedule(); - schedule.batch_size = count.div_ceil(8); - schedule - } -} diff --git a/github-simulator/source/src/training/runner/tests.rs b/github-simulator/source/src/training/runner/tests.rs deleted file mode 100644 index ee1850e51928b42d581d1f623fa5d85de0e6e6c2..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/training/runner/tests.rs +++ /dev/null @@ -1,384 +0,0 @@ -use super::*; -use crate::training::pyrandom::PyRandom; -use crate::training::Lineage; - -fn load(path: &str) -> Value { - serde_json::from_str(&std::fs::read_to_string(path).unwrap()).unwrap() -} - -/// A fixed generated track with the Formula vehicle. -fn generated_runner(population: usize) -> TrainingRunner { - let root = env!("CARGO_MANIFEST_DIR"); - let template_scene = load(&format!("{root}/assets/scenes/formula_template.json")); - let track_settings = serde_json::from_value(load(&format!("{root}/assets/random_track_settings.json"))).unwrap(); - let scene = crate::track::training_tracks::training_scene_at( - &template_scene, - &track_settings, - None, - 1729, - 0, - 0, - crate::math::profile::MathProfile::Proton, - ) - .unwrap() - .1; - let template = load(&format!("{root}/assets/networks/formula.json")); - let model = load(&format!("{root}/assets/models/formula.json")); - let spawn = serde_json::json!({"position":scene["reset_position"],"rotation":scene["reset_rotation"]}); - let outputs: Vec = template["outputs"] - .as_array() - .unwrap() - .iter() - .map(|v| v.as_str().unwrap().to_owned()) - .collect(); - let settings = EvolutionSettings { - population, - selection_size: 3, - preserve_parents_size: 2, - mutation_rate: 0.3, - weight_decay: 0.0, - ..Default::default() - }; - TrainingRunner::new( - Arc::new(World::from_scene(&scene)), - crate::track::world::vector(&spawn["position"]), - spawn["rotation"].as_f64().unwrap(), - SensorLayout::from_exports(&template, &model, crate::math::profile::MathProfile::Proton), - &outputs, - settings, - PyRandom::new(5), - 2, - 0, - true, - true, - ) -} - -fn param_bits(networks: impl IntoIterator>) -> Vec> { - networks - .into_iter() - .map(|n| n.borrow().params.iter().map(|p| p.to_bits()).collect()) - .collect() -} - -/// Traced turnover installs what untraced turnover does, and its lineage -/// rebuilds those networks and the generator state from the parents alone. -#[test] -fn traced_turnover_matches_and_its_lineage_rebuilds_it() { - let seed = Network::xavier(&[20, 8, 5], &mut PyRandom::new(9)); - for settings in [ - EvolutionSettings { - population: 10, - selection_size: 3, - preserve_parents_size: 2, - mutation_rate: 0.3, - weight_decay: 0.0, - ..Default::default() - }, - EvolutionSettings { - population: 7, - selection_algorithm: "tournament".into(), - selection_size: 4, - crossover: "uniform".into(), - mutation_rate: 0.2, - adaptive_mutation: true, - preserve_parents: "off".into(), - ..Default::default() - }, - ] { - let (mut a, mut b) = (generated_runner(10), generated_runner(10)); - a.settings = settings.clone(); - b.settings = settings.clone(); - a.start(&seed); - let first = b.start_traced(&seed).into_lineage().unwrap(); - assert_eq!( - first.parents.len(), - 1, - "the first generation is bred from the seed alone" - ); - let (rebuilt, rng) = first.rebuild(); - assert_eq!(param_bits(&rebuilt), param_bits(b.agents.iter().map(|x| &x.network))); - assert_eq!(rng.to_json(), b.rng.to_json()); - for _ in 0..2 { - a.advance(90, false); - b.advance(90, false); - let expected = a.next_generation(); - let (turnover, lineage) = b.next_generation_traced(); - let lineage = lineage.into_lineage().unwrap(); - assert_eq!( - (turnover.preserved_count, turnover.rewards.clone()), - (expected.preserved_count, expected.rewards.clone()) - ); - assert_eq!( - param_bits(b.agents.iter().map(|x| &x.network)), - param_bits(&expected.networks) - ); - assert_eq!(b.rng.to_json(), a.rng.to_json()); - assert_eq!((b.generation, b.stats_phase), (a.generation, a.stats_phase)); - lineage.validate().unwrap(); - assert!(lineage.parents.len() <= 1 + settings.selection_size + 2); - // The best parent leads; with parents kept it is also the first car. - if expected.preserved_count > 0 { - assert_eq!(param_bits([&lineage.parents[0]]), param_bits([&expected.networks[0]])); - } - let (rebuilt, rng) = lineage.rebuild(); - assert_eq!(param_bits(&rebuilt), param_bits(&expected.networks)); - assert_eq!(rng.to_json(), a.rng.to_json()); - } - } -} - -/// Without preservation nothing but the lineage's first slot holds the best -/// car, and tournament draws need not pick it first. -#[test] -fn the_best_car_leads_the_parents_without_preservation() { - let seed = Network::xavier(&[20, 8, 5], &mut PyRandom::new(9)); - let mut runner = generated_runner(10); - runner.settings = EvolutionSettings { - population: 10, - selection_algorithm: "tournament".into(), - selection_size: 3, - mutation_rate: 0.3, - preserve_parents: "off".into(), - ..Default::default() - }; - runner.start(&seed); - let mut first_draw_differed = false; - for _ in 0..6 { - runner.advance(90, false); - let before: Vec = runner.agents.iter().map(|x| x.network.clone()).collect(); - let (turnover, lineage) = runner.next_generation_traced(); - let lineage = lineage.into_lineage().unwrap(); - assert_eq!(turnover.preserved_count, 0); - assert!(lineage.preserved.is_empty()); - // The first car with the highest reward, as `ranked` keeps ties in order. - let best = (0..turnover.rewards.len()).fold(0, |best, i| { - if turnover.rewards[i] > turnover.rewards[best] { - i - } else { - best - } - }); - assert_eq!(param_bits([&lineage.parents[0]]), param_bits([&before[best]])); - let first_selected = &lineage.parents[lineage.selected[0] as usize]; - first_draw_differed |= param_bits([first_selected]) != param_bits([&before[best]]); - } - assert!( - first_draw_differed, - "the test must tell the best car from the first selected parent" - ); -} - -#[test] -fn lineage_validate_rejects_malformed_networks() { - let seed = Network::xavier(&[20, 8, 5], &mut PyRandom::new(9)); - let mut runner = generated_runner(10); - let lineage = runner.start_traced(&seed).into_lineage().unwrap(); - lineage.validate().unwrap(); - let invalid = |edit: &dyn Fn(&mut Lineage)| { - let mut broken = lineage.clone(); - edit(&mut broken); - assert!(broken.validate().is_err()); - }; - invalid(&|l| { - l.parents[0].params.pop(); - }); - invalid(&|l| l.parents[0].params.push(0.0)); - invalid(&|l| { - l.parents[0].shape = vec![20]; - }); - invalid(&|l| { - l.parents[0].shape = Vec::new(); - }); - invalid(&|l| { - l.parents[0].shape = vec![20, 0, 5]; - }); - invalid(&|l| { - l.parents[0].shape = vec![usize::MAX, usize::MAX]; - }); - invalid(&|l| { - l.parents[0].shape = vec![4, 4]; - l.parents[0].params = vec![0.0; 3]; - }); -} - -fn agent_state(a: &TrainingAgent) -> String { - let c = &a.car; - format!( - "{:?} {:?} {:?} {:?} {} {} {} {:?} {:?} {:?} {:?} {:?}", - c.position, - c.velocity, - c.acceleration, - c.body_basis, - c.active, - c.tick, - c.collision_count, - c.wheels, - a.controls, - a.pending_contact, - a.deactivated_at, - a.stats - ) -} - -/// Installing owned networks must leave the same agents as the cloning -/// path, including reused vehicles, statistics and network ownership. -#[test] -fn owned_install_matches_cloning_install() { - let mut a = generated_runner(24); - let mut b = generated_runner(24); - let seed = Network::xavier(&[20, 16, 5], &mut PyRandom::new(9)); - a.start(&seed); - b.start(&seed); - a.advance(90, false); - b.advance(90, false); - let results: Vec = a.agents.iter().map(TrainingAgent::result).collect(); - let generation = a - .rng - .clone() - .reproduce(&results, &a.settings, crate::math::profile::MathProfile::Proton); - a.install(&generation.networks, true); - b.install_with_novelty(generation.networks.clone(), true, None); - assert_eq!(a.agents.len(), b.agents.len()); - for (x, y) in a.agents.iter().zip(&b.agents) { - assert_eq!(x.network, y.network); - assert_eq!(agent_state(x), agent_state(y)); - } - // With supplied novelty values the same agents result, novelty aside. - let novelty: Vec = (0..generation.networks.len()).map(|i| i as f64 * 0.25).collect(); - let mut c = generated_runner(24); - c.start(&seed); - c.advance(90, false); - c.install_with_novelty(generation.networks.clone(), true, Some(&novelty)); - for (i, (x, y)) in a.agents.iter().zip(&c.agents).enumerate() { - assert_eq!(x.network, y.network); - assert_eq!(y.stats.network_novelty, novelty[i]); - let mut stats = y.stats.clone(); - stats.network_novelty = x.stats.network_novelty; - assert_eq!(format!("{:?}", x.stats), format!("{stats:?}")); - } -} - -/// Drives `runner` through a ray window whose hits come from the CPU ray tree. -fn advance_with_cpu_ray_window( - runner: &mut TrainingRunner, - ticks: u64, - stop_when_inactive: bool, - time_limit: Option, -) -> u64 { - use crate::math::godot_math::F2; - let world = runner.world.clone(); - let tree = world.track.ray_tree().expect("the fixture has a BSP ray tree"); - let mut window = runner.begin_ray_window(ticks, stop_when_inactive, time_limit).unwrap(); - let mut queries = Vec::new(); - let mut hits = Vec::new(); - while runner.prepare_ray_tick(&mut window) { - runner.ray_queries(&window, &mut queries); - assert_eq!(queries.len(), window.inferring.len() * runner.layout.ray_count()); - hits.clear(); - hits.extend(queries.iter().map(|q| { - match tree.raycast(V2::new(q[0] as f64, q[1] as f64), V2::new(q[2] as f64, q[3] as f64)) { - Some(hit) => { - let h = F2::from(hit); - [h.x, h.y, 1.0, 0.0] - } - None => [0.0; 4], - } - })); - runner.finish_ray_tick(&window, &hits).unwrap(); - } - runner.end_ray_window(window) -} - -fn assert_same_agents(a: &TrainingRunner, b: &TrainingRunner) { - assert_eq!( - (a.tick, a.batch_index, a.agents.len()), - (b.tick, b.batch_index, b.agents.len()) - ); - for (x, y) in a.agents.iter().zip(&b.agents) { - assert_eq!(agent_state(x), agent_state(y)); - } - let (mut sa, mut sb) = (Vec::new(), Vec::new()); - a.car_states(&mut sa); - b.car_states(&mut sb); - assert_eq!(sa.len(), a.agents.len() * CAR_STATE_STRIDE); - assert!(sa.iter().zip(&sb).all(|(p, q)| p.to_bits() == q.to_bits())); -} - -/// The externally served ray window reproduces `advance` in both modes, -/// including partial windows, elimination stops and the time limit. -#[test] -fn ray_window_matches_advance() { - let seed = Network::xavier(&[20, 16, 5], &mut PyRandom::new(9)); - for mode in [Mode::Independent, Mode::Lockstep] { - let mut a = generated_runner(24); - a.mode = mode; - a.start(&seed); - let mut b = generated_runner(24); - b.start(&seed); - assert_eq!( - a.advance(75, false), - advance_with_cpu_ray_window(&mut b, 75, false, None) - ); - assert_same_agents(&a, &b); - assert_eq!(a.advance(0, false), advance_with_cpu_ray_window(&mut b, 0, false, None)); - assert_same_agents(&a, &b); - // Two windows of a short generation: the time limit stops both before driving. - let (ticks, limit) = a.generation_window(600); - assert_eq!( - a.advance_window(ticks, true, Some(limit)), - advance_with_cpu_ray_window(&mut b, ticks, true, Some(limit)) - ); - assert_same_agents(&a, &b); - // Stops at the limit, or earlier once every car is eliminated. - assert!( - a.tick <= ticks + 75 && (a.tick > 500 || a.agents.iter().all(|x| !x.car.active)), - "tick {}", - a.tick - ); - a.next_generation(); - b.next_generation(); - assert_same_agents(&a, &b); - assert_eq!( - a.advance_generation(180), - advance_with_cpu_ray_window(&mut b, 200, true, Some(3.0)) - ); - assert_same_agents(&a, &b); - } - // Every car eliminated on walls or idle: the inactive stop ends the window. - let mut a = generated_runner(8); - a.start(&seed); - let mut b = generated_runner(8); - b.start(&seed); - assert_eq!( - a.advance(5400, true), - advance_with_cpu_ray_window(&mut b, 5400, true, None) - ); - assert!( - a.agents.iter().all(|x| !x.car.active) && a.tick < 5400, - "tick {}", - a.tick - ); - assert_same_agents(&a, &b); -} - -/// The retained export buffers hold exactly what per-agent exports produce. -#[cfg(not(target_arch = "wasm32"))] -#[test] -fn export_state_into_matches_per_agent_export() { - let mut runner = generated_runner(24); - runner.start(&Network::xavier(&[20, 16, 5], &mut PyRandom::new(9))); - runner.advance(150, false); - let surfaces = crate::gpu::simulation::SurfaceTable::new(&runner.world.vehicle); - let (mut cars, mut agents) = (vec![crate::gpu::simulation::GpuCar::zeroed(); 3], Vec::new()); - export_state_into(&runner.agents, &runner.world.vehicle, &surfaces, &mut cars, &mut agents).unwrap(); - assert_eq!((cars.len(), agents.len()), (24, 24)); - for (i, a) in runner.agents.iter().enumerate() { - let car = a.car.gpu_export(&runner.world.vehicle, &surfaces).unwrap(); - assert_eq!(format!("{:?}", cars[i]), format!("{car:?}")); - assert_eq!( - format!("{:?}", agents[i]), - format!("{:?}", crate::gpu::simulation::agent_export(a).unwrap()) - ); - } -} diff --git a/github-simulator/source/src/training/sensors.rs b/github-simulator/source/src/training/sensors.rs deleted file mode 100644 index 58e073cd1e6f59e285ade7c80a4a2c8656996971..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/training/sensors.rs +++ /dev/null @@ -1,311 +0,0 @@ -//! Ordered game sensor descriptors and input layout. - -use crate::math::profile::MathProfile; -use crate::math::vec2::V2; -use crate::physics::car::{Car, Sensor, SensorScratch}; -use crate::track::world::World; -use serde_json::Value; - -/// Map compact model input names to game sensor types. -pub(crate) fn sensor_type(name: &str) -> Option<&'static str> { - Some(match name { - "AccF" => "accelerationFront", - "AccS" => "accelerationSide", - "Wall" => "distanceFromWall", - "AngVel" => "angularVelocity", - "Boost" => "boostCapacity", - "Dir" => "correctDirection", - "Grip" => "grip", - "VelF" => "velocityFront", - "VelS" => "velocitySide", - "Curve" => "trackCurvature", - "Speed" | "VelAbs" => "speed", - "Wheel" => "wheelAngle", - _ => return None, - }) -} - -/// Parse `"↑ -35°"` into its angle. -pub(crate) fn vision_angle(name: &str) -> Option { - name.strip_prefix("↑ ")?.strip_suffix('°')?.trim().parse().ok() -} - -/// `SensorsEditor.CalculateSensorLength`: the editor gives every vision ray -/// `200 + 600 * |cos(degrees)|` pixels, in float32 with the game's UCRT cosf. -pub fn vision_length(degrees: f32, math: MathProfile) -> f32 { - 200.0 + 600.0 * math.cos(degrees * (std::f32::consts::PI / 180.0)).abs() -} - -#[derive(Clone, Debug)] -pub struct SensorLayout { - pub names: Vec, - pub sensors: Vec, -} - -impl SensorLayout { - /// Exact ordered descriptors captured from instantiated game sensors. - /// Names may repeat; each descriptor retains its own float parameters. - pub fn from_ordered(value: &Value) -> Result { - let rows = value["sensors"].as_array().ok_or("missing ordered sensors")?; - let names: Vec = value["names"] - .as_array() - .ok_or("missing ordered names")? - .iter() - .map(|v| { - v.as_str() - .map(str::to_owned) - .ok_or_else(|| "invalid sensor name".to_owned()) - }) - .collect::>()?; - if names.len() != rows.len() { - return Err("sensor/name count differs".into()); - } - let mut sensors = Vec::with_capacity(rows.len()); - for row in rows { - let number = |key: &str| { - row[key] - .as_f64() - .filter(|x| (*x as f32).is_finite()) - .map(|x| x as f32 as f64) - .ok_or_else(|| format!("missing sensor parameter {key}")) - }; - let positive_number = |key: &str| { - let value = number(key)?; - if value <= 0.0 { - return Err(format!( - "sensor parameter {key} must be positive after float32 conversion" - )); - } - Ok(value) - }; - let kind = row["$type"].as_str().ok_or("missing sensor type")?; - sensors.push(match kind { - "raycast" => Sensor::Raycast { - degrees: number("Degrees")?, - length: number("Length")?, - }, - "accelerationFront" => Sensor::AccelerationFront { - max_acceleration: positive_number("MaxAcceleration")?, - }, - "accelerationSide" => Sensor::AccelerationSide { - max_acceleration: positive_number("MaxAcceleration")?, - }, - "distanceFromWall" => Sensor::DistanceFromWall { - max_distance: positive_number("MaxDistance")?, - }, - "trackCurvature" => { - let min = number("MinLookaheadDistance")?.max(0.0); - Sensor::TrackCurvature { - min_lookahead: min, - max_lookahead: number("MaxLookaheadDistance")?.max(min), - } - } - "grip" => { - let offset = &row["PositionOffset"]; - let (x, y) = if offset.is_array() { - (&offset[0], &offset[1]) - } else { - (&offset["X"], &offset["Y"]) - }; - let component = |v: &Value| { - v.as_f64() - .filter(|x| (*x as f32).is_finite()) - .map(|x| x as f32 as f64) - .ok_or("missing grip offset component") - }; - Sensor::Grip { - offset: V2::new(component(x)?, component(y)?), - } - } - "speed" | "velocityFront" | "velocitySide" | "angularVelocity" | "wheelAngle" | "boostCapacity" - | "correctDirection" => Sensor::by_name(kind), - _ => return Err(format!("unknown sensor type {kind}")), - }); - } - Ok(SensorLayout { names, sensors }) - } - - /// `SensorLayout.from_exports(live_network, model)`. Vision rays the model - /// does not list get the editor's length under `math`. - pub fn from_exports(live_network: &Value, model: &Value, math: MathProfile) -> SensorLayout { - Self::try_from_exports(live_network, model, math).expect("valid sensor exports") - } - - pub fn try_from_exports(live_network: &Value, model: &Value, math: MathProfile) -> Result { - if let Some(exact) = model.get("sensor_layout") { - let layout = Self::from_ordered(exact)?; - let inputs: Vec<_> = live_network["inputs"] - .as_array() - .ok_or("missing network inputs")? - .iter() - .map(|v| v.as_str().ok_or("invalid input name")) - .collect::>()?; - if !layout.names.iter().map(String::as_str).eq(inputs) { - return Err("ordered sensor inputs differ from network".into()); - } - layout.validate_rays()?; - return Ok(layout); - } - let vision: Vec<(f64, f64)> = model["vision"] - .as_array() - .ok_or("missing model vision")? - .iter() - .map(|item| { - Ok(( - item["angle"] - .as_f64() - .filter(|n| (*n as f32).is_finite()) - .ok_or("invalid vision angle")?, - item["length"] - .as_f64() - .filter(|n| (*n as f32).is_finite()) - .ok_or("invalid vision length")?, - )) - }) - .collect::>()?; - let names: Vec = live_network["inputs"] - .as_array() - .ok_or("missing network inputs")? - .iter() - .map(|v| v.as_str().map(str::to_owned).ok_or("invalid input name")) - .collect::>()?; - let sensors = names - .iter() - .map(|name| match vision_angle(name) { - Some(angle) => { - if !(angle as f32).is_finite() { - return Err("invalid vision angle".into()); - } - // Python dict: the last vision entry with this angle wins. - // Angles the model does not list get the editor's length. - let length = vision - .iter() - .rev() - .find(|(a, _)| *a == angle) - .map_or_else(|| vision_length(angle as f32, math) as f64, |&(_, length)| length); - Ok(Sensor::Raycast { degrees: angle, length }) - } - None => sensor_type(name) - .map(Sensor::by_name) - .ok_or_else(|| format!("unknown sensor: {name}")), - }) - .collect::>()?; - let layout = SensorLayout { names, sensors }; - layout.validate_rays()?; - Ok(layout) - } - - fn validate_rays(&self) -> Result<(), String> { - for sensor in &self.sensors { - if let Sensor::Raycast { degrees, length } = sensor { - if !(*degrees as f32).is_finite() || !(*length as f32).is_finite() || (*length as f32) <= 0.0 { - return Err("ray angle must be finite and length positive".into()); - } - } - } - Ok(()) - } - - pub fn read_into(&self, world: &World, car: &Car, scratch: &mut SensorScratch, out: &mut Vec) { - scratch.invalidate(); - out.clear(); - out.extend(self.sensors.iter().map(|&sensor| car.sensor(world, sensor, scratch))); - } - - /// The number of `Sensor::Raycast` entries. - #[cfg(any(test, all(target_arch = "wasm32", feature = "wasm")))] - pub(crate) fn ray_count(&self) -> usize { - self.sensors - .iter() - .filter(|s| matches!(s, Sensor::Raycast { .. })) - .count() - } - - /// `read_into` with the ray sensors' track raycasts supplied by an external - /// raycaster: one `[hit.x, hit.y, hit != 0, _]` per `Sensor::Raycast`, in - /// layout order, for the ray `TrainingRunner::ray_queries` produced. - #[cfg(any(test, all(target_arch = "wasm32", feature = "wasm")))] - pub(crate) fn read_into_with_hits( - &self, - world: &World, - car: &Car, - scratch: &mut SensorScratch, - out: &mut Vec, - hits: &[[f32; 4]], - ) { - scratch.invalidate(); - out.clear(); - let mut hits = hits.iter(); - out.extend(self.sensors.iter().map(|&sensor| match sensor { - Sensor::Raycast { degrees, length } => { - let (end, length) = car.ray_end(degrees, length); - let h = hits.next().expect("one hit per ray sensor"); - let hit = (h[2] != 0.0).then(|| V2::from(crate::math::godot_math::F2 { x: h[0], y: h[1] })); - Car::ray_value(car.position, end, length, hit) - } - other => car.sensor(world, other, scratch), - })); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use serde_json::json; - - const NORMALIZED_SENSORS: [(&str, &str); 3] = [ - ("accelerationFront", "MaxAcceleration"), - ("accelerationSide", "MaxAcceleration"), - ("distanceFromWall", "MaxDistance"), - ]; - - fn ordered(kind: &str, parameter: &str, value: Value) -> Value { - let mut sensor = json!({"$type": kind}); - sensor[parameter] = value; - json!({"names": ["input"], "sensors": [sensor]}) - } - - #[test] - fn normalization_maxima_reject_zero_negative_and_float32_underflow() { - let network = json!({"inputs": ["input"]}); - for (kind, parameter) in NORMALIZED_SENSORS { - for value in [ - json!(0.0), - json!(-0.0), - json!(-1.0), - json!(1e-50), - json!(1e39), - Value::Null, - ] { - let layout = ordered(kind, parameter, value.clone()); - let error = SensorLayout::from_ordered(&layout).unwrap_err(); - assert!(error.contains(parameter), "{kind} {value}: {error}"); - let error = - SensorLayout::try_from_exports(&network, &json!({"sensor_layout": layout}), MathProfile::Proton) - .unwrap_err(); - assert!(error.contains(parameter), "{kind} {value}: {error}"); - } - } - } - - #[test] - fn positive_normalization_maxima_keep_float32_rounding_and_trusted_exports() { - let network = json!({"inputs": ["input"]}); - for (kind, parameter) in NORMALIZED_SENSORS { - for value in [f32::from_bits(1) as f64, 0.1, f32::MAX as f64] { - let ordered = ordered(kind, parameter, json!(value)); - let layout = - SensorLayout::from_exports(&network, &json!({"sensor_layout": ordered}), MathProfile::Proton); - let maximum = match layout.sensors[0] { - Sensor::AccelerationFront { max_acceleration } | Sensor::AccelerationSide { max_acceleration } => { - max_acceleration - } - Sensor::DistanceFromWall { max_distance } => max_distance, - other => panic!("unexpected sensor {other:?}"), - }; - assert_eq!(maximum.to_bits(), (value as f32 as f64).to_bits(), "{kind}: {value}"); - assert!(maximum > 0.0); - } - } - } -} diff --git a/github-simulator/source/src/training/session.rs b/github-simulator/source/src/training/session.rs deleted file mode 100644 index a65f1183ca1d217951d45a794501b060e14c3422..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/training/session.rs +++ /dev/null @@ -1,2219 +0,0 @@ -//! A simulation session for embedding hosts: the WebAssembly library -//! (src/wasm) and examples/wasm_reference.rs share it, so a host runs the -//! same code as the native reference. It wraps `TrainingRunner` with JSON -//! options, seeding, flat state export and generation-boundary checkpoints. - -use crate::math::profile::MathProfile; -use crate::math::vec2::V2; -use crate::nn::network::{parameter_count, Network}; -use crate::track::world::World; -use crate::training::ars::{ArsRecord, Elite, Sampling}; -#[cfg(all(target_arch = "wasm32", feature = "wasm"))] -use crate::training::evolution::METRIC_NAMES; -use crate::training::evolution::{Breeding, EvolutionSettings, CROSSOVERS}; -use crate::training::optimizer::Record; -use crate::training::pyrandom::PyRandom; -use crate::training::{Lineage, Mode, SensorLayout, TrainingAgent, TrainingRandom, TrainingRunner, CAR_STATE_STRIDE}; -#[cfg(any(test, all(target_arch = "wasm32", feature = "wasm")))] -use serde_json::json; -use serde_json::Value; -use std::sync::Arc; - -#[cfg(not(target_arch = "wasm32"))] -mod hip; -mod input; -#[cfg(not(target_arch = "wasm32"))] -pub use hip::device as hip_device; - -/// The spawn pose of every car. -#[derive(Clone, Copy, Debug, PartialEq, serde::Deserialize, serde::Serialize)] -pub struct Spawn { - pub position: [f64; 2], - pub rotation: f64, -} - -/// Where a session's driving windows run. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "lowercase")] -pub enum Backend { - /// The CPU runner (rayon). - #[default] - Cpu, - /// The native HIP simulator (libaltd_gpu.so); reproduction stays on the CPU. - Hip, -} - -/// Options of `Session::new`, as the JSON object hosts pass (camelCase keys, -/// every field optional). -#[derive(Clone, Debug, serde::Deserialize)] -#[serde(rename_all = "camelCase", default, deny_unknown_fields)] -pub struct SessionOptions { - /// Cars per generation; overrides `settings.population`. Default 32. - pub(crate) population: Option, - /// Seed of the Python-compatible training RNG (selection, mutation, Xavier networks). - pub(crate) seed: i64, - /// Inference batches per tick divisor (`--batch-count`). - pub(crate) batch_count: usize, - pub(crate) stats_phase: u64, - pub(crate) eliminate_on_wall: bool, - pub(crate) eliminate_when_idle: bool, - /// Spawn pose; without it the scene's `reset_position`/`reset_rotation` are used. - pub spawn: Option, - /// `"independent"` or `"lockstep"` (`--mode`). - pub mode: String, - /// Evolution settings as `EvolutionSettings::from_mcp` reads them. - pub(crate) settings: Option, - /// WebGPU only: every n-th ray query is also cast on the CPU and compared - /// (0 disables the check). - pub(crate) gpu_verify_every: u32, - /// `"cpu"` or `"hip"`. A native session falls back to the CPU when HIP - /// cannot run it (see `Session::backend_note`). - pub backend: Backend, - /// The C runtime math to reproduce (`"proton"`, `"win10-fma3"`, - /// `"win11-fma3"`); without it, the math of this machine - /// (`MathProfile::detect`). - pub math_profile: Option, -} - -impl Default for SessionOptions { - fn default() -> Self { - SessionOptions { - population: None, - seed: 0, - batch_count: 1, - stats_phase: 0, - eliminate_on_wall: false, - eliminate_when_idle: false, - spawn: None, - mode: "independent".into(), - settings: None, - gpu_verify_every: 0, - backend: Backend::Cpu, - math_profile: None, - } - } -} - -impl SessionOptions { - pub fn from_json(text: &str) -> Result { - serde_json::from_str(text).map_err(|e| format!("invalid session options: {e}")) - } -} - -pub struct Session { - pub runner: TrainingRunner, - #[cfg(all(target_arch = "wasm32", feature = "wasm"))] - pub(crate) options: SessionOptions, - #[cfg(any(test, feature = "server", all(target_arch = "wasm32", feature = "wasm")))] - network: Value, - /// Vehicle export retained to reject changes during track replacement. - #[cfg(any(test, feature = "server", all(target_arch = "wasm32", feature = "wasm")))] - vehicle: Value, - output_names: Vec, - boundary: Option, - #[cfg(not(target_arch = "wasm32"))] - hip: Option, - /// Why the session runs on another backend than its options asked for. - backend_note: Option, -} - -/// The generation that just began, as a checkpoint saves it. Kept apart from -/// the running cars so saving never touches them. -#[derive(Clone)] -struct Boundary { - generation: u64, - tick: u64, - saved: Saved, -} - -#[derive(Clone)] -enum Saved { - /// After a full restore (format 1 or JSON): every car's parameters and - /// the generator after them. Saved as format 1. - Population { - shape: Vec, - rng: TrainingRandom, - params: Vec, - }, - /// After a start or reproduction: what bred the cars. Saved as format 2. - Lineage(Lineage), - /// After an ARS start or reproduction: the search state that sampled the cars. Saved as format 3. - Ars(ArsRecord), -} - -/// Per-generation statistics hosts read instead of the full car states. -#[derive(Clone, Debug, PartialEq, serde::Serialize)] -#[serde(rename_all = "camelCase")] -pub struct GenerationSummary { - /// The first car with the highest distance score. - pub(crate) best_index: usize, - pub(crate) best_score: f64, - /// Scores added in car order, starting at +0, then divided by population. - #[serde(skip_serializing_if = "Option::is_none")] - pub(crate) average_score: Option, - /// JavaScript `Math.min` semantics, including NaN and negative zero. - #[serde(skip_serializing_if = "Option::is_none")] - pub(crate) worst_score: Option, - /// Cars with at least one completed lap. - pub(crate) lapped: usize, - pub(crate) active: usize, - /// The best lap of the generation: `(car, seconds)`. - pub(crate) lap_index: Option, - pub(crate) lap_time: Option, -} - -/// `checkpoint_bytes` magic: format 1, every car's networks. -const CHECKPOINT_MAGIC: &[u8; 8] = b"ALTDCKP1"; -/// Format 2: the parents a generation was bred from (`Lineage`). -const PARENTS_MAGIC: &[u8; 8] = b"ALTDCKP2"; -/// Format 3: the ARS search state a generation was sampled from (`ArsRecord`). -const ARS_MAGIC: &[u8; 8] = b"ALTDCKP3"; - -fn strings(value: &Value, what: &str) -> Result, String> { - value - .as_array() - .ok_or_else(|| format!("missing {what}"))? - .iter() - .map(|v| { - v.as_str() - .map(str::to_owned) - .ok_or_else(|| format!("{what} must be strings")) - }) - .collect() -} - -impl Session { - /// `scene`, `network` (a game network export with `inputs` and `outputs`, - /// optionally `weights`/`biases`) and `model` (sensor parameters) as the - /// CLI loads them. - pub fn new(scene: &Value, network: &Value, model: &Value, options: SessionOptions) -> Result { - input::scene(scene)?; - let spawn = match options.spawn { - Some(s) => s, - None => match ( - scene.get("reset_position"), - scene.get("reset_rotation").and_then(Value::as_f64), - ) { - (Some(p), Some(rotation)) => { - let p = crate::track::world::vector(p); - Spawn { - position: [p.x, p.y], - rotation, - } - } - _ => return Err("options.spawn is required: the scene has no reset_position".into()), - }, - }; - let mode = match options.mode.as_str() { - "independent" => Mode::Independent, - "lockstep" => Mode::Lockstep, - other => return Err(format!("unknown mode {other:?} (independent or lockstep)")), - }; - if options.batch_count == 0 { - return Err("batchCount must be positive".into()); - } - #[cfg(target_arch = "wasm32")] - if options.backend == Backend::Hip { - return Err("the HIP backend is only available in the native simulator".into()); - } - let output_names = strings(&network["outputs"], "network outputs")?; - for name in &output_names { - if !["acceleration", "steering", "brake", "handbrake", "boost"].contains(&name.to_lowercase().as_str()) { - return Err(format!("unknown control output: {name}")); - } - } - if !spawn.position.iter().all(|n| n.is_finite() && (*n as f32).is_finite()) - || !spawn.rotation.is_finite() - || !(spawn.rotation as f32).is_finite() - { - return Err("spawn pose must be finite after float32 conversion".into()); - } - strings(&network["inputs"], "network inputs")?; - let mut settings = options.settings.as_ref().map_or_else( - || Ok(EvolutionSettings::default()), - |data| EvolutionSettings::try_from_mcp_with_population(data, options.population), - )?; - if let Some(population) = options.population { - settings.population = population; - } - settings.validate()?; - let math = options.math_profile.unwrap_or_else(MathProfile::detect); - let world = Arc::new(World::from_scene_with(scene, math)); - let layout = SensorLayout::try_from_exports(network, model, math)?; - if layout - .sensors - .iter() - .any(|s| matches!(s, crate::physics::car::Sensor::WheelAngle)) - && !scene["vehicle"]["wheels"] - .as_array() - .unwrap() - .iter() - .any(|w| w["steering"].as_bool() == Some(true)) - { - return Err("wheel angle sensor requires a steering wheel".into()); - } - let mut runner = TrainingRunner::new( - world, - V2::new(spawn.position[0], spawn.position[1]), - spawn.rotation, - layout, - &output_names, - settings, - PyRandom::new(options.seed), - options.batch_count, - options.stats_phase, - options.eliminate_on_wall, - options.eliminate_when_idle, - ); - runner.mode = mode; - #[cfg(not(target_arch = "wasm32"))] - let (hip, backend_note) = match options.backend { - Backend::Cpu => (None, None), - Backend::Hip => match hip::HipState::new(&runner) { - Ok(state) => (Some(state), None), - Err(reason) => (None, Some(reason)), - }, - }; - #[cfg(target_arch = "wasm32")] - let backend_note = None; - Ok(Session { - runner, - #[cfg(all(target_arch = "wasm32", feature = "wasm"))] - options, - #[cfg(any(test, feature = "server", all(target_arch = "wasm32", feature = "wasm")))] - network: network.clone(), - #[cfg(any(test, feature = "server", all(target_arch = "wasm32", feature = "wasm")))] - vehicle: scene["vehicle"].clone(), - output_names, - boundary: None, - #[cfg(not(target_arch = "wasm32"))] - hip, - backend_note, - }) - } - - /// The network export's `shape` (or `summary.shape`), if any. - #[cfg(any(test, feature = "server", all(target_arch = "wasm32", feature = "wasm")))] - pub(crate) fn template_shape(&self) -> Option> { - let summary = self.network.get("summary").unwrap_or(&self.network); - summary - .get("shape")? - .as_array()? - .iter() - .map(|v| v.as_u64().map(|n| n as usize)) - .collect() - } - - /// Installs the first generation: mutations of the export's weights when - /// it has any, else of a Xavier network of its shape. - #[cfg(any(test, feature = "server", all(target_arch = "wasm32", feature = "wasm")))] - pub fn start(&mut self) -> Result<(), String> { - if self.network.get("weights").is_some() { - let shape = self.template_shape().ok_or("missing or invalid network shape")?; - self.check_shape(&shape)?; - let seed = Network::try_from_game_export(&self.network)?; - let record = self.runner_mut()?.start_traced(&seed); - self.record(record); - self.networks_changed(); - Ok(()) - } else { - let shape = self - .template_shape() - .ok_or("the network export has neither weights nor a shape; use start_with_shape")?; - self.start_with_shape(&shape) - } - } - - /// Installs the first generation from a Xavier network of `shape`, drawn - /// from the session RNG as `train-scratch` does. - pub fn start_with_shape(&mut self, shape: &[usize]) -> Result<(), String> { - self.check_shape(shape)?; - let runner = self.runner_mut()?; - let seed = runner.rng.xavier(shape, runner.world.math); - let record = runner.start_traced(&seed); - self.record(record); - self.networks_changed(); - Ok(()) - } - - fn check_shape(&self, shape: &[usize]) -> Result<(), String> { - let inputs = self.runner.layout.sensors.len(); - if shape.len() < 2 || shape.contains(&0) { - return Err(format!("invalid network shape {shape:?}")); - } - if checked_parameter_count(shape) - .and_then(|count| count.checked_mul(std::mem::size_of::())) - .is_none_or(|bytes| bytes > isize::MAX as usize) - { - return Err("the network shape exceeds the parameter buffer size".into()); - } - if shape[0] != inputs { - return Err(format!( - "the network has {} inputs but the sensor layout has {inputs}", - shape[0] - )); - } - if *shape.last().unwrap() < self.output_names.len() { - return Err(format!( - "the network has {} outputs but the export names {}", - shape.last().unwrap(), - self.output_names.len() - )); - } - Ok(()) - } - - /// The C runtime math the session reproduces. - pub fn math_profile(&self) -> MathProfile { - self.runner.world.math - } - - pub(crate) fn started(&self) -> bool { - !self.runner.agents.is_empty() - } - - fn require_started(&self) -> Result<(), String> { - if self.started() { - Ok(()) - } else { - Err("the session has not started".into()) - } - } - - pub(crate) fn agent(&self, index: usize) -> Result<&TrainingAgent, String> { - self.runner - .agents - .get(index) - .ok_or_else(|| format!("car {index} is outside the population of {}", self.runner.agents.len())) - } - - /// The backend that runs the driving windows. - pub fn backend(&self) -> Backend { - #[cfg(not(target_arch = "wasm32"))] - if self.hip.is_some() { - return Backend::Hip; - } - Backend::Cpu - } - - /// Why the session runs on the CPU although its options asked for HIP. - pub fn backend_note(&self) -> Option<&str> { - self.backend_note.as_deref() - } - - /// The networks changed without a new generation number, so a GPU - /// simulator must upload them again. - fn networks_changed(&mut self) { - #[cfg(not(target_arch = "wasm32"))] - if let Some(hip) = &mut self.hip { - hip.invalidate_networks(); - } - } - - /// The HIP simulator, after its startup comparison with the CPU; `None` - /// on the CPU backend. A failed comparison moves the session to the CPU. - #[cfg(not(target_arch = "wasm32"))] - fn hip(&mut self) -> Result, String> { - let Some(state) = self.hip.as_mut() else { - return Ok(None); - }; - if state.needs_verify() { - // The CPU continues from the runner if the comparison fails. - state.sync(&mut self.runner)?; - if let Err(reason) = state.verify(&mut self.runner) { - self.hip = None; - self.backend_note = Some(reason); - return Ok(None); - } - } - Ok(self.hip.as_mut()) - } - - /// Reads the cars and agents back from the HIP simulator if a window left - /// them only there. On HIP, `advance` and `advance_generation` do not - /// update the runner's cars and agents (only `tick` and the batch - /// index); call this before reading them, directly or with `car_states`, - /// `metrics`, `sensors`, `controls`, `best_lap` or `generation_summary`. - pub fn sync(&mut self) -> Result<(), String> { - #[cfg(not(target_arch = "wasm32"))] - if let Some(hip) = &mut self.hip { - hip.sync(&mut self.runner)?; - } - Ok(()) - } - - /// The runner, current and about to change its cars or agents. - fn runner_mut(&mut self) -> Result<&mut TrainingRunner, String> { - self.sync()?; - #[cfg(not(target_arch = "wasm32"))] - if let Some(hip) = &mut self.hip { - hip.host_changed(); - } - Ok(&mut self.runner) - } - - /// Panics if the runner's cars and agents are older than the HIP - /// simulator's (a missing `sync`). - fn assert_current(&self) { - #[cfg(not(target_arch = "wasm32"))] - assert!( - !self.hip.as_ref().is_some_and(hip::HipState::stale), - "the HIP simulator holds newer cars and agents; call Session::sync first" - ); - } - - pub fn advance(&mut self, ticks: u64, stop_when_inactive: bool) -> Result { - self.require_started()?; - #[cfg(not(target_arch = "wasm32"))] - if self.hip()?.is_some() { - let hip = self.hip.as_mut().unwrap(); - return hip.advance(&mut self.runner, ticks, stop_when_inactive); - } - Ok(self.runner.advance(ticks, stop_when_inactive)) - } - - pub fn advance_generation(&mut self, time_limit_ticks: u64) -> Result { - self.require_started()?; - if self.runner.stats_phase != 0 { - return Err("advance_generation needs statistics phase 0".into()); - } - #[cfg(not(target_arch = "wasm32"))] - if self.hip()?.is_some() { - let hip = self.hip.as_mut().unwrap(); - return hip.advance_generation(&mut self.runner, time_limit_ticks); - } - Ok(self.runner.advance_generation(time_limit_ticks)) - } - - /// Reproduces and installs the next generation; returns the preserved - /// parent count and every car's reward. - pub fn next_generation(&mut self) -> Result<(usize, Vec), String> { - self.require_started()?; - let (turnover, record) = self.runner_mut()?.next_generation_traced(); - self.record(record); - Ok((turnover.preserved_count, turnover.rewards)) - } - - /// Change the settings used by the next reproduction without resetting - /// cars, generation statistics or the training RNG. Population changes - /// take effect when `next_generation` installs the new population. - #[cfg(any(test, feature = "server", all(target_arch = "wasm32", feature = "wasm")))] - pub fn set_evolution_settings(&mut self, text: &str) -> Result<(), String> { - let settings: EvolutionSettings = - serde_json::from_str(text).map_err(|e| format!("invalid evolution settings: {e}"))?; - settings.validate()?; - self.runner.settings = settings; - Ok(()) - } - - /// `CAR_STATE_STRIDE` values per car (`training::CAR_STATE_FIELDS`). - pub fn car_states(&self) -> Vec { - self.assert_current(); - let mut out = Vec::with_capacity(self.runner.agents.len() * CAR_STATE_STRIDE); - self.runner.car_states(&mut out); - out - } - - /// The training metrics of car `index` in `METRIC_NAMES` order (NaN when unset). - pub fn metrics(&self, index: usize) -> Result, String> { - self.assert_current(); - Ok(self - .agent(index)? - .stats - .metrics() - .iter() - .map(|m| m.unwrap_or(f64::NAN)) - .collect()) - } - - #[cfg(all(target_arch = "wasm32", feature = "wasm"))] - pub(crate) fn metric_names() -> Vec { - METRIC_NAMES.iter().map(|&s| s.to_owned()).collect() - } - - /// The sensor inputs car `index` would read now. - pub fn sensors(&self, index: usize) -> Result, String> { - self.assert_current(); - let agent = self.agent(index)?; - let mut scratch = crate::physics::car::SensorScratch::default(); - let mut out = Vec::new(); - self.runner - .layout - .read_into(&self.runner.world, &agent.car, &mut scratch, &mut out); - Ok(out) - } - - #[cfg(all(target_arch = "wasm32", feature = "wasm"))] - pub(crate) fn sensor_names(&self) -> &[String] { - &self.runner.layout.names - } - - #[cfg(all(target_arch = "wasm32", feature = "wasm"))] - pub(crate) fn output_names(&self) -> &[String] { - &self.output_names - } - - /// `[acceleration, steering, brake, handbrake, boost]` of car `index`. - pub fn controls(&self, index: usize) -> Result<[f64; 5], String> { - self.assert_current(); - let c = self.agent(index)?.controls; - Ok([c.acceleration, c.steering, c.brake, c.handbrake, c.boost]) - } - - /// The network of car `index` as `{"shape", "weights", "biases"}`. - #[cfg(any(test, feature = "server", all(target_arch = "wasm32", feature = "wasm")))] - pub(crate) fn network_json(&self, index: usize) -> Result { - Ok(self.agent(index)?.network.to_json().to_string()) - } - - /// Replaces the network of car `index` with a game export of the same shape. - #[cfg(any(test, all(target_arch = "wasm32", feature = "wasm")))] - pub(crate) fn set_network_json(&mut self, index: usize, text: &str) -> Result<(), String> { - let value: Value = serde_json::from_str(text).map_err(|e| e.to_string())?; - let network = Network::try_from_game_export(&value)?; - let agent = self - .runner - .agents - .get_mut(index) - .ok_or_else(|| format!("car {index} is outside the population"))?; - if network.shape != agent.network.shape { - return Err(format!( - "shape {:?} differs from the population's {:?}", - network.shape, agent.network.shape - )); - } - agent.network = network; - // The optimizer's directions describe the population it produced, not this car. - self.runner.optimizer.invalidate(); - self.networks_changed(); - Ok(()) - } - - /// The best lap of the current generation: `(car, seconds)`. - pub(crate) fn best_lap(&self) -> Option<(usize, f64)> { - self.assert_current(); - self.runner - .agents - .iter() - .enumerate() - .filter_map(|(i, a)| a.stats.best_lap_time.map(|t| (i, t))) - .min_by(|a, b| a.1.total_cmp(&b.1).then(a.0.cmp(&b.0))) - } - - /// A generation-boundary checkpoint: generation, RNG state and networks, - /// restorable with `restore_checkpoint` on a session with the same - /// options and files. - #[cfg(any(test, all(target_arch = "wasm32", feature = "wasm")))] - pub(crate) fn checkpoint(&self) -> Result { - self.require_started()?; - let shape = &self.runner.agents[0].network.shape; - Ok(json!({ - "generation": self.runner.generation, - "tick": self.runner.tick, - "shape": shape, - "rng": rng_json(&self.runner.rng, self.math_profile()), - "networks": self.runner.agents.iter().map(|a| Value::from(a.network.params.clone())).collect::>(), - })) - } - - /// Installs the checkpoint's networks on reset cars as its generation - /// (`TrainingRunner::resume`) and restores its RNG. - #[cfg(any(test, feature = "server", all(target_arch = "wasm32", feature = "wasm")))] - pub(crate) fn restore_checkpoint(&mut self, value: &Value) -> Result<(), String> { - let shape: Vec = value["shape"] - .as_array() - .ok_or("missing shape")? - .iter() - .map(|v| v.as_u64().map(|n| n as usize).ok_or("invalid shape")) - .collect::>()?; - let generation = value["generation"].as_u64().ok_or("missing generation")?; - self.check_shape(&shape)?; - let size = crate::nn::network::parameter_count(&shape); - let mut params = Vec::new(); - for (i, row) in value["networks"] - .as_array() - .ok_or("missing networks")? - .iter() - .enumerate() - { - let row = row.as_array().ok_or("invalid network")?; - if row.len() != size { - return Err(format!( - "network {i} has {} parameters, shape {shape:?} needs {size}", - row.len() - )); - } - for v in row { - params.push(v.as_f64().ok_or("invalid parameter")?); - } - } - self.check_math(rng_math(&value["rng"])?)?; - let rng = if value["rng"].is_null() { - None - } else { - Some(TrainingRandom::try_from_json(&value["rng"])?) - }; - self.restore(&shape, generation, params, rng) - } - - fn restore( - &mut self, - shape: &[usize], - generation: u64, - params: Vec, - rng: Option, - ) -> Result<(), String> { - self.check_shape(shape)?; - let size = crate::nn::network::parameter_count(shape); - if params.is_empty() { - return Err("the checkpoint has no networks".into()); - } - if !params.len().is_multiple_of(size) { - return Err(format!( - "the checkpoint has {} parameters, not a multiple of the {size} of shape {shape:?}", - params.len() - )); - } - let networks: Vec = params - .chunks(size) - .map(|p| Network::from_vector(shape, p.to_vec())) - .collect(); - let runner = self.runner_mut()?; - if let Some(rng) = rng { - runner.rng = rng; - } - runner.resume(&networks, generation); - self.snapshot_population(); - self.networks_changed(); - Ok(()) - } - - /// Records the generation that just began, with every car's parameters, - /// as the checkpoint boundary (after a full restore). - fn snapshot_population(&mut self) { - let agents = &self.runner.agents; - let Some(first) = agents.first() else { return }; - let mut params = Vec::with_capacity(agents.len() * first.network.params.len()); - for a in agents { - params.extend_from_slice(&a.network.params); - } - let saved = Saved::Population { - shape: first.network.shape.clone(), - rng: self.runner.rng.clone(), - params, - }; - self.boundary = Some(Boundary { - generation: self.runner.generation, - tick: self.runner.tick, - saved, - }); - } - - /// Records the reproduction that bred the generation that just began. - fn record(&mut self, record: Record) { - self.boundary = Some(Boundary { - generation: self.runner.generation, - tick: self.runner.tick, - saved: match record { - Record::Lineage(lineage) => Saved::Lineage(lineage), - Record::Ars(ars) => Saved::Ars(ars), - }, - }); - } - - fn restore_ars(&mut self, generation: u64, record: ArsRecord) -> Result<(), String> { - record.validate()?; - self.check_math(record.math)?; - self.check_shape(&record.shape)?; - let (networks, rng, ars) = record.rebuild(); - let runner = self.runner_mut()?; - runner.rng = rng; - runner.resume_owned(networks, generation); - runner.optimizer = Box::new(ars); - self.record(Record::Ars(record)); - self.networks_changed(); - Ok(()) - } - - /// Refuses a checkpoint trained under other math: its cars would drive - /// and breed differently here. - fn check_math(&self, math: MathProfile) -> Result<(), String> { - if math == self.math_profile() { - Ok(()) - } else { - Err(format!( - "the checkpoint uses math profile {math}, this session {}", - self.math_profile() - )) - } - } - - fn restore_lineage(&mut self, generation: u64, lineage: Lineage) -> Result<(), String> { - lineage.validate()?; - self.check_math(lineage.math)?; - self.check_shape(&lineage.parents[0].shape)?; - let (networks, rng) = lineage.rebuild(); - let runner = self.runner_mut()?; - runner.rng = rng; - runner.resume_owned(networks, generation); - self.record(Record::Lineage(lineage)); - self.networks_changed(); - Ok(()) - } - - /// The generation boundary `checkpoint_bytes` saves: `(generation, tick)`. - #[cfg(any(test, feature = "server", all(target_arch = "wasm32", feature = "wasm")))] - pub(crate) fn boundary(&self) -> Option<(u64, u64)> { - self.boundary.as_ref().map(|b| (b.generation, b.tick)) - } - - /// The boundary of the current generation in binary: format 3 (see - /// `ars_bytes`) after an ARS start or reproduction, format 2 (see - /// `parent_bytes`) after a GA start or reproduction, format 1 (see - /// `population_bytes`) after restoring a full checkpoint. - pub fn checkpoint_bytes(&self) -> Result, String> { - let b = self.boundary.as_ref().ok_or("the session has not started")?; - match &b.saved { - Saved::Population { shape, rng, params } => { - population_bytes(b.generation, b.tick, shape, rng, self.math_profile(), params) - } - Saved::Lineage(lineage) => parent_bytes(b.generation, b.tick, lineage), - Saved::Ars(record) => ars_bytes(b.generation, b.tick, record), - } - } - - /// Restores a `checkpoint_bytes` checkpoint of any format. - pub fn restore_checkpoint_bytes(&mut self, bytes: &[u8]) -> Result<(), String> { - match bytes.get(..8) { - Some(m) if m == CHECKPOINT_MAGIC => { - let (shape, generation, params, rng) = read_population(bytes)?; - self.check_math(rng.as_ref().map_or(MathProfile::Proton, |(_, math)| *math))?; - self.restore(&shape, generation, params, rng.map(|(rng, _)| rng)) - } - Some(m) if m == PARENTS_MAGIC => { - let (generation, lineage) = read_parents(bytes)?; - self.restore_lineage(generation, lineage) - } - Some(m) if m == ARS_MAGIC => { - let (generation, record) = read_ars(bytes)?; - self.restore_ars(generation, record) - } - _ => Err("invalid binary checkpoint".into()), - } - } - - /// Statistics of the current generation (see `GenerationSummary`). - pub fn generation_summary(&self) -> GenerationSummary { - self.assert_current(); - let agents = &self.runner.agents; - let mut best = 0; - let mut total_score = 0.0; - let mut worst_score = f64::INFINITY; - for (i, a) in agents.iter().enumerate() { - let score = a.stats.total_score; - total_score += score; - if score.is_nan() || score < worst_score || (score == 0.0 && worst_score == 0.0 && score.is_sign_negative()) - { - worst_score = score; - } - if score > agents[best].stats.total_score { - best = i; - } - } - let lap = self.best_lap(); - GenerationSummary { - best_index: best, - best_score: agents.get(best).map_or(f64::NAN, |a| a.stats.total_score), - average_score: (!agents.is_empty()).then(|| total_score / agents.len() as f64), - worst_score: (!agents.is_empty()).then_some(worst_score), - lapped: agents.iter().filter(|a| a.stats.score.lap_count >= 1).count(), - active: self.active_count(), - lap_index: lap.map(|l| l.0), - lap_time: lap.map(|l| l.1), - } - } - - /// Cars that still drive (counted on the device while the runner's cars - /// are stale). - pub(crate) fn active_count(&self) -> usize { - #[cfg(not(target_arch = "wasm32"))] - if let Some(active) = self.hip.as_ref().and_then(hip::HipState::active_count) { - return active; - } - self.runner.agents.iter().filter(|a| a.car.active).count() - } - - /// Wall segments `[x0, y0, x1, y1]...` for drawing. - #[cfg(any(test, all(target_arch = "wasm32", feature = "wasm")))] - pub(crate) fn track_walls(&self) -> Vec { - self.runner - .world - .track - .walls - .iter() - .flat_map(|w| [w.start.x as f32, w.start.y as f32, w.end.x as f32, w.end.y as f32]) - .collect() - } - - /// The baked centre path `[x, y]...`. - #[cfg(any(test, all(target_arch = "wasm32", feature = "wasm")))] - pub(crate) fn track_path(&self) -> Vec { - self.runner.world.track.path.iter().flat_map(|p| [p.x, p.y]).collect() - } - - /// `[min_x, min_y, max_x, max_y]` over the walls, or the path without walls. - #[cfg(all(target_arch = "wasm32", feature = "wasm"))] - pub(crate) fn track_bounds(&self) -> [f64; 4] { - let track = &self.runner.world.track; - let points: Vec = if track.walls.is_empty() { - track.path.clone() - } else { - track.walls.iter().flat_map(|w| [w.start, w.end]).collect() - }; - points.iter().fold( - [f64::INFINITY, f64::INFINITY, f64::NEG_INFINITY, f64::NEG_INFINITY], - |b, p| [b[0].min(p.x), b[1].min(p.y), b[2].max(p.x), b[3].max(p.y)], - ) - } - - #[cfg(any(test, all(target_arch = "wasm32", feature = "wasm")))] - pub(crate) fn spawn(&self) -> Spawn { - Spawn { - position: [self.runner.position.x, self.runner.position.y], - rotation: self.runner.rotation, - } - } - - /// Selects the track the next installed generation drives, spawning at - /// the scene's reset pose. Call it at a generation boundary, before - /// `next_generation`; the vehicle must not change. - #[cfg(any(test, feature = "server", all(target_arch = "wasm32", feature = "wasm")))] - pub fn replace_track(&mut self, scene: &Value) -> Result<(), String> { - input::scene(scene)?; - if scene["vehicle"] != self.vehicle { - return Err("a replacement track must keep the vehicle".into()); - } - let position = scene - .get("reset_position") - .map(crate::track::world::vector) - .ok_or("the scene has no reset_position")?; - let rotation = scene - .get("reset_rotation") - .and_then(Value::as_f64) - .ok_or("the scene has no reset_rotation")?; - let world = Arc::new(World::from_scene_with(scene, self.math_profile())); - if world.track.native_broadphase != self.runner.world.track.native_broadphase { - return Err("a replacement track must keep the broadphase mode".into()); - } - self.runner_mut()? - .replace_track(world, V2::new(position.x, position.y), rotation); - #[cfg(not(target_arch = "wasm32"))] - if let Some(hip) = &mut self.hip { - if let Err(reason) = hip.replace_world(&self.runner) { - self.hip = None; - self.backend_note = Some(reason); - } - } - Ok(()) - } -} - -/// A checkpoint's RNG JSON: the generator state and the math profile it -/// draws with, which checkpoints of every format carry this way. Proton -/// checkpoints leave the profile out and keep their earlier bytes. -fn rng_json(rng: &TrainingRandom, math: MathProfile) -> Value { - let mut value = rng.to_json(); - if math != MathProfile::Proton { - value["mathProfile"] = serde_json::json!(math); - } - value -} - -/// The math profile of `rng_json`. Checkpoints from before profiles were Proton's. -fn rng_math(rng: &Value) -> Result { - match rng.get("mathProfile") { - None => Ok(MathProfile::Proton), - Some(name) => name - .as_str() - .ok_or_else(|| "invalid checkpoint math profile".to_string())? - .parse(), - } -} - -/// `parameter_count` for a shape from untrusted bytes: `None` on overflow -/// (usize is 32 bits on wasm). -fn checked_parameter_count(shape: &[usize]) -> Option { - shape.windows(2).try_fold(0usize, |sum, w| { - w[0].checked_add(1)?.checked_mul(w[1])?.checked_add(sum) - }) -} - -/// Format 1: `ALTDCKP1`, then little-endian u32 generation, tick, population, -/// layer count and RNG JSON length, the u32 shape, the RNG JSON, zero -/// padding to a multiple of 8 bytes and every network's f64 parameters. -fn population_bytes( - generation: u64, - tick: u64, - shape: &[usize], - rng: &TrainingRandom, - math: MathProfile, - params: &[f64], -) -> Result, String> { - let rng = rng_json(rng, math).to_string(); - let size = parameter_count(shape); - let header = [ - generation, - tick, - (params.len() / size) as u64, - shape.len() as u64, - rng.len() as u64, - ]; - let mut out = Vec::with_capacity(64 + rng.len() + params.len() * 8); - out.extend_from_slice(CHECKPOINT_MAGIC); - for n in header.iter().copied().chain(shape.iter().map(|&n| n as u64)) { - out.extend_from_slice( - &u32::try_from(n) - .map_err(|_| "checkpoint field exceeds u32")? - .to_le_bytes(), - ); - } - out.extend_from_slice(rng.as_bytes()); - out.resize(out.len().next_multiple_of(8), 0); - for p in params { - out.extend_from_slice(&p.to_le_bytes()); - } - Ok(out) -} - -type PopulationCheckpoint = (Vec, u64, Vec, Option<(TrainingRandom, MathProfile)>); - -/// Reads format 1: `(shape, generation, params, rng and math profile)`. -fn read_population(bytes: &[u8]) -> Result { - let invalid = || "invalid binary checkpoint".to_string(); - if bytes.len() < 28 || &bytes[..8] != CHECKPOINT_MAGIC { - return Err(invalid()); - } - let word = |i: usize| -> Result { - let at = i.checked_mul(4).and_then(|n| n.checked_add(8)).ok_or_else(invalid)?; - bytes - .get(at..at.checked_add(4).ok_or_else(invalid)?) - .map(|w| u32::from_le_bytes(w.try_into().unwrap()) as usize) - .ok_or_else(invalid) - }; - let (generation, population, layers, rng_len) = (word(0)? as u64, word(2)?, word(3)?, word(4)?); - let shape: Vec = (0..layers).map(|i| word(5 + i)).collect::>()?; - let rng_at = layers - .checked_add(5) - .and_then(|n| n.checked_mul(4)) - .and_then(|n| n.checked_add(8)) - .ok_or_else(invalid)?; - let rng_end = rng_at.checked_add(rng_len).ok_or_else(invalid)?; - let rng_text = bytes.get(rng_at..rng_end).ok_or_else(invalid)?; - let rng: Value = serde_json::from_slice(rng_text).map_err(|e| format!("invalid checkpoint RNG: {e}"))?; - let params_at = rng_end.next_multiple_of(8); - let size = if shape.len() >= 2 { - checked_parameter_count(&shape).ok_or_else(invalid)? - } else { - 0 - }; - let data = bytes.get(params_at..).ok_or_else(invalid)?; - if data - .chunks_exact(8) - .any(|p| !f64::from_le_bytes(p.try_into().unwrap()).is_finite()) - { - return Err("non-finite checkpoint parameter".into()); - } - let expected = population.checked_mul(size).and_then(|n| n.checked_mul(8)); - if expected != Some(data.len()) { - return Err(format!( - "the checkpoint holds {} bytes of parameters, {population} networks of shape {shape:?} need {}", - data.len(), - expected.map_or("more".to_string(), |n| n.to_string()) - )); - } - let params = data - .chunks_exact(8) - .map(|p| f64::from_le_bytes(p.try_into().unwrap())) - .collect(); - let rng = if rng.is_null() { - None - } else { - Some((TrainingRandom::try_from_json(&rng)?, rng_math(&rng)?)) - }; - Ok((shape, generation, params, rng)) -} - -/// Format 2, `ALTDCKP2`, little-endian: u32 words generation, tick, -/// population, layer count L, RNG JSON length R, parent count P, selected -/// count S, preserved count K, crossover (index in `CROSSOVERS`) and flags -/// (bit 0: adaptive mutation); the u32 shape, selected and preserved parent -/// indices; the RNG JSON; zero padding to a multiple of 8 bytes; f64 -/// mutation rate and weight decay; then the P parents' f64 parameters. -/// Words 0 to 4 sit where format 1 has them. -fn parent_bytes(generation: u64, tick: u64, l: &Lineage) -> Result, String> { - let rng = rng_json(&l.rng, l.math).to_string(); - let shape = &l.parents[0].shape; - let b = &l.breeding; - let crossover = CROSSOVERS - .iter() - .position(|&c| c == b.crossover) - .ok_or("unknown crossover")? as u64; - let header = [ - generation, - tick, - b.population as u64, - shape.len() as u64, - rng.len() as u64, - l.parents.len() as u64, - l.selected.len() as u64, - l.preserved.len() as u64, - crossover, - b.adaptive_mutation as u64, - ]; - let size = parameter_count(shape); - let mut out = Vec::with_capacity( - 64 + 4 * (shape.len() + l.selected.len() + l.preserved.len()) + rng.len() + 16 + l.parents.len() * size * 8, - ); - out.extend_from_slice(PARENTS_MAGIC); - let words = header - .into_iter() - .chain(shape.iter().map(|&n| n as u64)) - .chain(l.selected.iter().chain(&l.preserved).map(|&i| i as u64)); - for n in words { - out.extend_from_slice( - &u32::try_from(n) - .map_err(|_| "checkpoint field exceeds u32")? - .to_le_bytes(), - ); - } - out.extend_from_slice(rng.as_bytes()); - out.resize(out.len().next_multiple_of(8), 0); - out.extend_from_slice(&b.mutation_rate.to_le_bytes()); - out.extend_from_slice(&b.weight_decay.to_le_bytes()); - for p in l.parents.iter().flat_map(|n| &n.params) { - out.extend_from_slice(&p.to_le_bytes()); - } - Ok(out) -} - -/// Reads format 2 (see `parent_bytes`) and validates it: `(generation, lineage)`. -fn read_parents(bytes: &[u8]) -> Result<(u64, Lineage), String> { - let invalid = || "invalid binary checkpoint".to_string(); - let word = |i: usize| -> Result { - let at = i.checked_mul(4).and_then(|n| n.checked_add(8)).ok_or_else(invalid)?; - bytes - .get(at..at.checked_add(4).ok_or_else(invalid)?) - .map(|w| u32::from_le_bytes(w.try_into().unwrap()) as usize) - .ok_or_else(invalid) - }; - let (generation, population, layers, rng_len) = (word(0)? as u64, word(2)?, word(3)?, word(4)?); - let (parents, selected, preserved, crossover, flags) = (word(5)?, word(6)?, word(7)?, word(8)?, word(9)?); - let first_index = 10usize.checked_add(layers).ok_or_else(invalid)?; - let rng_at = first_index - .checked_add(selected) - .and_then(|n| n.checked_add(preserved)) - .and_then(|n| n.checked_mul(4)) - .and_then(|n| n.checked_add(8)) - .ok_or_else(invalid)?; - // Bound every count by the bytes present before allocating for it. - let rng_end = rng_at - .checked_add(rng_len) - .filter(|&end| end <= bytes.len()) - .ok_or_else(invalid)?; - let shape: Vec = (0..layers).map(|i| word(10 + i)).collect::>()?; - if shape.len() < 2 || shape.contains(&0) { - return Err(invalid()); - } - let index = |i: usize| word(i).map(|n| n as u32); - let selected_ix = (0..selected) - .map(|i| index(first_index + i)) - .collect::, _>>()?; - let preserved_ix = (0..preserved) - .map(|i| index(first_index + selected + i)) - .collect::, _>>()?; - let rng: Value = - serde_json::from_slice(&bytes[rng_at..rng_end]).map_err(|e| format!("invalid checkpoint RNG: {e}"))?; - if rng.is_null() { - return Err("the checkpoint has no random state".into()); - } - let rates_at = rng_end.next_multiple_of(8); - let float = |at: usize| { - bytes - .get(at..at.checked_add(8).ok_or_else(invalid)?) - .map(|b| f64::from_le_bytes(b.try_into().unwrap())) - .ok_or_else(invalid) - }; - let (mutation_rate, weight_decay) = (float(rates_at)?, float(rates_at + 8)?); - let size = checked_parameter_count(&shape).ok_or_else(invalid)?; - let stride = size.checked_mul(8).ok_or_else(invalid)?; - let data = bytes.get(rates_at + 16..).ok_or_else(invalid)?; - if data - .chunks_exact(8) - .any(|p| !f64::from_le_bytes(p.try_into().unwrap()).is_finite()) - { - return Err("non-finite checkpoint parameter".into()); - } - if parents.checked_mul(stride) != Some(data.len()) { - return Err(format!( - "the checkpoint holds {} bytes of parameters, {parents} networks of shape {shape:?} need more or fewer", - data.len() - )); - } - let crossover = CROSSOVERS - .get(crossover) - .ok_or("the checkpoint has an unknown crossover")?; - if flags > 1 { - return Err(invalid()); - } - let networks = data - .chunks_exact(stride) - .map(|chunk| { - Network::from_vector( - &shape, - chunk - .chunks_exact(8) - .map(|p| f64::from_le_bytes(p.try_into().unwrap())) - .collect(), - ) - }) - .collect(); - let lineage = Lineage { - parents: networks, - selected: selected_ix, - preserved: preserved_ix, - breeding: Breeding { - population, - crossover: (*crossover).into(), - mutation_rate, - adaptive_mutation: flags == 1, - weight_decay, - }, - rng: TrainingRandom::try_from_json(&rng)?, - math: rng_math(&rng)?, - }; - lineage.validate()?; - Ok((generation, lineage)) -} - -/// Format 3, `ALTDCKP3`, little-endian: u32 words generation, tick, -/// population, layer count L, RNG JSON length R, elite pool size E and elite -/// count K; the u32 shape; the RNG JSON; zero padding to a multiple of 8 -/// bytes; f64 nu and max weight; the f64 parameters of the search point; then -/// each of the E pool members' f64 parameters followed by its f64 score. -/// Words 0 to 4 sit where formats 1 and 2 have them. -fn ars_bytes(generation: u64, tick: u64, r: &ArsRecord) -> Result, String> { - let rng = rng_json(&r.rng, r.math).to_string(); - let s = &r.sampling; - let header = [ - generation, - tick, - s.population as u64, - r.shape.len() as u64, - rng.len() as u64, - r.pool.len() as u64, - s.elite_count as u64, - ]; - let size = r.theta.len(); - let mut out = Vec::with_capacity( - 8 + 4 * (header.len() + r.shape.len()) + rng.len() + 24 + (r.pool.len() + 1) * (size + 1) * 8, - ); - out.extend_from_slice(ARS_MAGIC); - for n in header.into_iter().chain(r.shape.iter().map(|&n| n as u64)) { - out.extend_from_slice( - &u32::try_from(n) - .map_err(|_| "checkpoint field exceeds u32")? - .to_le_bytes(), - ); - } - out.extend_from_slice(rng.as_bytes()); - out.resize(out.len().next_multiple_of(8), 0); - out.extend_from_slice(&s.nu.to_le_bytes()); - out.extend_from_slice(&s.max_weight.to_le_bytes()); - for p in &r.theta { - out.extend_from_slice(&p.to_le_bytes()); - } - for e in &r.pool { - for p in &e.params { - out.extend_from_slice(&p.to_le_bytes()); - } - out.extend_from_slice(&e.score.to_le_bytes()); - } - Ok(out) -} - -/// Reads format 3 (see `ars_bytes`) and validates it: `(generation, record)`. -fn read_ars(bytes: &[u8]) -> Result<(u64, ArsRecord), String> { - let invalid = || "invalid binary checkpoint".to_string(); - let word = |i: usize| -> Result { - let at = i.checked_mul(4).and_then(|n| n.checked_add(8)).ok_or_else(invalid)?; - bytes - .get(at..at.checked_add(4).ok_or_else(invalid)?) - .map(|w| u32::from_le_bytes(w.try_into().unwrap()) as usize) - .ok_or_else(invalid) - }; - let (generation, population, layers, rng_len) = (word(0)? as u64, word(2)?, word(3)?, word(4)?); - let (pool_len, elite_count) = (word(5)?, word(6)?); - let rng_at = 7usize - .checked_add(layers) - .and_then(|n| n.checked_mul(4)) - .and_then(|n| n.checked_add(8)) - .ok_or_else(invalid)?; - // Bound every count by the bytes present before allocating for it. - let rng_end = rng_at - .checked_add(rng_len) - .filter(|&end| end <= bytes.len()) - .ok_or_else(invalid)?; - let shape: Vec = (0..layers).map(|i| word(7 + i)).collect::>()?; - if shape.len() < 2 || shape.contains(&0) { - return Err(invalid()); - } - let rng: Value = - serde_json::from_slice(&bytes[rng_at..rng_end]).map_err(|e| format!("invalid checkpoint RNG: {e}"))?; - if rng.is_null() { - return Err("the checkpoint has no random state".into()); - } - let floats_at = rng_end.next_multiple_of(8); - let size = checked_parameter_count(&shape).ok_or_else(invalid)?; - let data = bytes.get(floats_at..).ok_or_else(invalid)?; - // nu and max weight, the search point, then the pool members and their scores. - let expected = pool_len - .checked_mul(size.checked_add(1).ok_or_else(invalid)?) - .and_then(|n| n.checked_add(size)) - .and_then(|n| n.checked_add(2)) - .and_then(|n| n.checked_mul(8)); - if expected != Some(data.len()) { - return Err(format!( - "the checkpoint holds {} bytes of search state, a shape of {shape:?} with {pool_len} elites needs a different amount", - data.len() - )); - } - let mut floats = data.chunks_exact(8).map(|p| f64::from_le_bytes(p.try_into().unwrap())); - let mut take = |count: usize| -> Vec { floats.by_ref().take(count).collect() }; - let (nu, max_weight) = (take(1)[0], take(1)[0]); - let theta = take(size); - let pool = (0..pool_len) - .map(|_| Elite { - params: take(size), - score: take(1)[0], - }) - .collect(); - let record = ArsRecord { - shape, - theta, - pool, - sampling: Sampling { - population, - elite_count, - nu, - max_weight, - }, - rng: TrainingRandom::try_from_json(&rng)?, - math: rng_math(&rng)?, - }; - record.validate()?; - Ok((generation, record)) -} - -#[cfg(test)] -mod tests { - use super::*; - - fn load(path: &str) -> Value { - let root = env!("CARGO_MANIFEST_DIR"); - serde_json::from_str(&std::fs::read_to_string(format!("{root}/{path}")).unwrap()).unwrap() - } - - fn generated_scene() -> Value { - let settings = serde_json::from_value(load("assets/random_track_settings.json")).unwrap(); - crate::track::training_tracks::training_scene_at( - &load("assets/scenes/formula_template.json"), - &settings, - None, - 1729, - 0, - 0, - crate::math::profile::MathProfile::Proton, - ) - .unwrap() - .1 - } - - fn generated_session(options: &str) -> Session { - Session::new( - &generated_scene(), - &load("assets/networks/formula.json"), - &load("assets/models/formula.json"), - SessionOptions::from_json(options).unwrap(), - ) - .unwrap() - } - - #[test] - fn options_parse_with_defaults_and_reject_unknown_keys() { - let o = - SessionOptions::from_json(r#"{"population": 8, "seed": 3, "eliminateOnWall": true, "mode": "lockstep"}"#) - .unwrap(); - assert_eq!( - ( - o.population, - o.seed, - o.batch_count, - o.eliminate_on_wall, - o.eliminate_when_idle, - o.mode.as_str() - ), - (Some(8), 3, 1, true, false, "lockstep") - ); - assert!(SessionOptions::from_json(r#"{"populaton": 8}"#).is_err()); - assert!(SessionOptions::from_json("{}").unwrap().population.is_none()); - } - - #[test] - fn oversized_shapes_fail_before_allocating_parameters() { - let mut session = generated_session(r#"{"population":2}"#); - assert!(session.start_with_shape(&[20, usize::MAX, 5]).is_err()); - assert!(!session.started()); - session.start_with_shape(&[20, 8, 5]).unwrap(); - assert!(session.started()); - } - - #[test] - fn evolution_updates_preserve_statistics_and_rng_until_turnover() { - let mut s = generated_session(r#"{"population":6,"seed":5,"eliminateOnWall":true}"#); - s.start_with_shape(&[20, 8, 5]).unwrap(); - s.advance_generation(120).unwrap(); - let states = s.car_states(); - let rng = s.runner.rng.to_json(); - let generation = s.runner.generation; - s.set_evolution_settings(r#"{"population":9,"mutation_rate":0.1,"weight_decay":0}"#) - .unwrap(); - assert_eq!(s.car_states(), states); - assert_eq!(s.runner.rng.to_json(), rng); - assert_eq!(s.runner.generation, generation); - assert!(s.set_evolution_settings(r#"{"mutation_rate":-1}"#).is_err()); - assert!(s - .set_evolution_settings(r#"{"selection_algorithm":"unknown"}"#) - .is_err()); - assert!(s - .set_evolution_settings(r#"{"rewards":[{"metric":"unknown","weight":100,"type":"default"}]}"#) - .is_err()); - assert!(s.set_evolution_settings(r#"{"population":0}"#).is_err()); - assert!(s.set_evolution_settings(r#"{"mutaton_rate":0.1}"#).is_err()); - assert_eq!(s.runner.settings.mutation_rate, 0.1); - let (_, rewards) = s.next_generation().unwrap(); - assert_eq!(rewards.len(), 6); - assert_eq!(s.runner.agents.len(), 9); - assert_eq!(s.runner.generation, generation + 1); - } - - #[test] - fn session_runs_generations_and_checkpoints_round_trip() { - let mut s = generated_session( - r#"{"population": 6, "seed": 5, "batchCount": 2, "eliminateOnWall": true, "eliminateWhenIdle": true, - "settings": {"selection_size": 3, "preserve_parents_size": 2, "mutation_rate": 0.3, "weight_decay": 0.0}}"#, - ); - assert!(s.advance(1, false).is_err()); - assert!(s.start_with_shape(&[19, 5]).is_err(), "input count mismatch"); - s.start_with_shape(&[20, 8, 5]).unwrap(); - assert_eq!(s.car_states().len(), 6 * CAR_STATE_STRIDE); - assert_eq!(s.sensors(0).unwrap().len(), 20); - assert_eq!(s.metrics(0).unwrap().len(), 14); - assert_eq!(s.advance(120, false).unwrap(), 120); - let (preserved, rewards) = s.next_generation().unwrap(); - // `on_selection_size` preserves the three selected parents. - assert_eq!((preserved, rewards.len()), (3, 6)); - let checkpoint = s.checkpoint().unwrap(); - s.advance_generation(300).unwrap(); - let states = s.car_states(); - assert_eq!(s.track_walls().len() % 4, 0); - assert!(!s.track_path().is_empty()); - // The same generation replays from the checkpoint in a fresh session. - let mut t = generated_session( - r#"{"population": 6, "seed": 5, "batchCount": 2, "eliminateOnWall": true, "eliminateWhenIdle": true, - "settings": {"selection_size": 3, "preserve_parents_size": 2, "mutation_rate": 0.3, "weight_decay": 0.0}}"#, - ); - t.restore_checkpoint(&checkpoint).unwrap(); - assert_eq!(t.runner.generation, 1); - t.advance_generation(300).unwrap(); - assert!(states - .iter() - .zip(&t.car_states()) - .all(|(a, b)| a.to_bits() == b.to_bits())); - assert_eq!(s.network_json(0).unwrap(), t.network_json(0).unwrap()); - s.set_network_json(1, &t.network_json(0).unwrap()).unwrap(); - assert_eq!(s.network_json(1).unwrap(), t.network_json(0).unwrap()); - // A malformed export (one weight row) and a well-formed one of another shape are both rejected. - assert!(s - .set_network_json( - 1, - r#"{"shape":[20,5],"weights":[[[0.0,0,0,0,0]]],"biases":[[0,0,0,0,0]]}"# - ) - .is_err()); - assert!(s - .set_network_json( - 1, - &Network::xavier(&[20, 5], &mut PyRandom::new(1)).to_json().to_string() - ) - .is_err()); - assert_eq!(s.network_json(1).unwrap(), t.network_json(0).unwrap()); - } - - #[test] - fn generation_summary_score_aggregates_follow_exported_scores_through_turnover() { - let mut session = generated_session(r#"{"population":6,"seed":5,"eliminateOnWall":true}"#); - session.start_with_shape(&[20, 8, 5]).unwrap(); - for generation in 0..2 { - session.advance_generation(240).unwrap(); - assert_eq!(session.runner.generation, generation); - let states = session.car_states(); - let scores: Vec = states.chunks_exact(CAR_STATE_STRIDE).map(|car| car[7]).collect(); - // The worker adds exported scores in car order, starting at +0. - let average = scores.iter().fold(0.0, |sum, score| sum + score) / scores.len() as f64; - let worst = scores.iter().copied().min_by(f64::total_cmp).unwrap(); - let summary = serde_json::to_value(session.generation_summary()).unwrap(); - assert_eq!( - summary["averageScore"] - .as_f64() - .expect("summary averageScore") - .to_bits(), - average.to_bits(), - "generation {generation} average" - ); - assert_eq!( - summary["worstScore"].as_f64().expect("summary worstScore").to_bits(), - worst.to_bits(), - "generation {generation} worst" - ); - session.next_generation().unwrap(); - let summary = serde_json::to_value(session.generation_summary()).unwrap(); - assert_eq!(summary["averageScore"].as_f64().unwrap().to_bits(), 0.0f64.to_bits()); - assert_eq!(summary["worstScore"].as_f64().unwrap().to_bits(), 0.0f64.to_bits()); - } - } - - #[test] - fn generation_summary_score_aggregates_preserve_javascript_rounding_and_minimum() { - let mut session = generated_session(r#"{"population":3,"seed":5}"#); - let summary = serde_json::to_value(session.generation_summary()).unwrap(); - assert!(summary.get("averageScore").is_none()); - assert!(summary.get("worstScore").is_none()); - session.start_with_shape(&[20, 8, 5]).unwrap(); - for (scores, average, worst) in [ - ([1e16, 1.0, -1e16], 0.0, -1e16), - ([0.0, -0.0, 0.0], 0.0, -0.0), - ([-0.0, 0.0, 0.0], 0.0, -0.0), - ([-0.0, -0.0, -0.0], 0.0, -0.0), - ([f64::MAX, f64::MAX, 0.0], f64::INFINITY, 0.0), - ([f64::INFINITY, 1.0, 2.0], f64::INFINITY, 1.0), - ([f64::NEG_INFINITY, 1.0, 2.0], f64::NEG_INFINITY, f64::NEG_INFINITY), - ([f64::INFINITY, f64::NEG_INFINITY, 0.0], f64::NAN, f64::NEG_INFINITY), - ([f64::NAN, 1.0, -2.0], f64::NAN, f64::NAN), - ([1.0, f64::NAN, -2.0], f64::NAN, f64::NAN), - ([1.0, -2.0, f64::NAN], f64::NAN, f64::NAN), - ] { - for (agent, score) in session.runner.agents.iter_mut().zip(scores) { - agent.stats.total_score = score; - } - let summary = session.generation_summary(); - let wire = serde_json::to_value(&summary).unwrap(); - for (key, actual, expected) in [ - ("averageScore", summary.average_score.unwrap(), average), - ("worstScore", summary.worst_score.unwrap(), worst), - ] { - if expected.is_nan() { - assert!(actual.is_nan(), "{key} for {scores:?}"); - } else { - assert_eq!(actual.to_bits(), expected.to_bits(), "{key} for {scores:?}"); - } - if expected.is_finite() { - assert_eq!(wire[key].as_f64().unwrap().to_bits(), expected.to_bits()); - } else { - assert!( - wire.get(key).unwrap().is_null(), - "nonfinite {key} is unavailable on the wire" - ); - } - } - } - } - - #[test] - fn binary_checkpoints_save_the_generation_boundary() { - let options = r#"{"population": 6, "seed": 5, "eliminateOnWall": true, "eliminateWhenIdle": true, - "settings": {"selection_size": 3, "mutation_rate": 0.3, "weight_decay": 0.0}}"#; - let mut s = generated_session(options); - assert!(s.checkpoint_bytes().is_err()); - s.start_with_shape(&[20, 8, 5]).unwrap(); - s.advance_generation(240).unwrap(); - let summary = s.generation_summary(); - let states = s.car_states(); - let best = (0..6).fold(0, |b, i| { - if states[i * CAR_STATE_STRIDE + 7] > states[b * CAR_STATE_STRIDE + 7] { - i - } else { - b - } - }); - assert_eq!( - (summary.best_index, summary.best_score), - (best, states[best * CAR_STATE_STRIDE + 7]) - ); - assert_eq!( - summary.active, - (0..6).filter(|i| states[i * CAR_STATE_STRIDE + 6] == 1.0).count() - ); - assert_eq!(summary.active, s.active_count()); - s.next_generation().unwrap(); - // The boundary is the checkpoint of the generation that just began, - // however far that generation has since advanced. - let json = s.checkpoint().unwrap(); - s.advance(120, false).unwrap(); - let bytes = s.checkpoint_bytes().unwrap(); - assert_eq!(&bytes[..8], b"ALTDCKP2"); - assert_eq!(s.boundary(), Some((1, 0))); - let mut t = generated_session(options); - t.restore_checkpoint_bytes(&bytes).unwrap(); - assert_eq!(t.checkpoint().unwrap(), json); - assert_eq!(t.checkpoint_bytes().unwrap(), bytes); - let mut u = generated_session(options); - u.restore_checkpoint(&json).unwrap(); - let full = u.checkpoint_bytes().unwrap(); - assert_eq!(&full[..8], b"ALTDCKP1"); - let mut w = generated_session(options); - w.restore_checkpoint_bytes(&full).unwrap(); - assert_eq!(w.checkpoint().unwrap(), json); - assert_eq!(w.checkpoint_bytes().unwrap(), full); - assert!(t.restore_checkpoint_bytes(&bytes[..bytes.len() - 8]).is_err()); - assert!(t.restore_checkpoint_bytes(b"ALTDCKP0").is_err()); - } - - fn state_bits(s: &mut Session) -> Vec { - s.sync().unwrap(); - s.car_states().iter().map(|v| v.to_bits()).collect() - } - - const PARENT_OPTIONS: [&str; 3] = [ - r#"{"population": 12, "seed": 5, "eliminateOnWall": true, "settings": {"selection_algorithm": "tournament", "selection_size": 4, "crossover": "uniform", "mutation_rate": 0.3, "adaptive_mutation": true, "weight_decay": 0.001, "preserve_parents": "on_custom", "preserve_parents_size": 2}}"#, - r#"{"population": 12, "seed": 6, "eliminateOnWall": true, "settings": {"selection_algorithm": "roulette", "selection_size": 3, "crossover": "single_point", "mutation_rate": 0.2, "preserve_parents": "off"}}"#, - r#"{"population": 12, "seed": 7, "eliminateOnWall": true, "settings": {"selection_size": 3, "mutation_rate": 0.2}}"#, - ]; - - /// A parent checkpoint holds the parents, not the generation, yet it - /// restores every network, the generator and later generations exactly, - /// even after the session's settings have moved on. - #[test] - fn parent_checkpoints_rebuild_the_generation_exactly() { - for options in PARENT_OPTIONS { - let mut s = generated_session(options); - s.start_with_shape(&[20, 8, 5]).unwrap(); - let first = s.checkpoint_bytes().unwrap(); - assert_eq!(&first[..8], b"ALTDCKP2"); - let mut t = generated_session(options); - t.restore_checkpoint_bytes(&first).unwrap(); - assert_eq!(t.checkpoint().unwrap(), s.checkpoint().unwrap(), "generation 0"); - assert_eq!(t.checkpoint_bytes().unwrap(), first); - - s.advance_generation(120).unwrap(); - s.next_generation().unwrap(); - s.advance_generation(120).unwrap(); - // A schedule or stage change: the next reproduction breeds 9 cars - // at another rate, which a session built with `options` lacks. - s.set_evolution_settings(r#"{"population": 9, "selection_algorithm": "tournament", "selection_size": 2, "crossover": "uniform", "mutation_rate": 0.05, "weight_decay": 0.01, "preserve_parents": "on_custom", "preserve_parents_size": 1}"#).unwrap(); - s.next_generation().unwrap(); - let bytes = s.checkpoint_bytes().unwrap(); - let json = s.checkpoint().unwrap(); - let mut u = generated_session(options); - u.restore_checkpoint_bytes(&bytes).unwrap(); - assert_eq!(u.runner.agents.len(), 9); - assert_eq!(u.runner.generation, 2); - assert_eq!(u.checkpoint().unwrap(), json, "every network and the generator"); - assert_eq!(u.checkpoint_bytes().unwrap(), bytes); - u.set_evolution_settings(r#"{"population": 9, "selection_algorithm": "tournament", "selection_size": 2, "crossover": "uniform", "mutation_rate": 0.05, "weight_decay": 0.01, "preserve_parents": "on_custom", "preserve_parents_size": 1}"#).unwrap(); - for _ in 0..2 { - s.advance_generation(120).unwrap(); - u.advance_generation(120).unwrap(); - assert_eq!(state_bits(&mut u), state_bits(&mut s)); - assert_eq!(u.next_generation().unwrap(), s.next_generation().unwrap()); - assert_eq!(u.checkpoint_bytes().unwrap(), s.checkpoint_bytes().unwrap()); - } - } - } - - const ARS_OPTIONS: &str = - r#"{"population": 16, "seed": 4, "settings": {"algorithm": "ars", "ars": {"elite_count": 2}}}"#; - - /// An ARS checkpoint restores every network, the generator and the search - /// state: both sessions then produce the same later generations. - #[test] - fn ars_checkpoints_rebuild_the_generation_exactly() { - let mut s = generated_session(ARS_OPTIONS); - s.start_with_shape(&[20, 8, 5]).unwrap(); - let first = s.checkpoint_bytes().unwrap(); - assert_eq!(&first[..8], b"ALTDCKP3"); - let mut t = generated_session(ARS_OPTIONS); - t.restore_checkpoint_bytes(&first).unwrap(); - assert_eq!(t.checkpoint().unwrap(), s.checkpoint().unwrap(), "generation 0"); - assert_eq!(t.checkpoint_bytes().unwrap(), first); - - for _ in 0..3 { - s.advance_generation(120).unwrap(); - let (preserved, rewards) = s.next_generation().unwrap(); - assert_eq!(rewards.len(), 16); - assert!(preserved <= 3); - } - let bytes = s.checkpoint_bytes().unwrap(); - assert_eq!(&bytes[..8], b"ALTDCKP3"); - let json = s.checkpoint().unwrap(); - let mut u = generated_session(ARS_OPTIONS); - u.restore_checkpoint_bytes(&bytes).unwrap(); - assert_eq!(u.runner.generation, 3); - assert_eq!(u.checkpoint().unwrap(), json, "every network and the generator"); - assert_eq!(u.checkpoint_bytes().unwrap(), bytes); - for _ in 0..2 { - s.advance_generation(120).unwrap(); - u.advance_generation(120).unwrap(); - assert_eq!(state_bits(&mut u), state_bits(&mut s)); - assert_eq!(u.next_generation().unwrap(), s.next_generation().unwrap()); - assert_eq!(u.checkpoint_bytes().unwrap(), s.checkpoint_bytes().unwrap()); - } - } - - #[test] - fn malformed_ars_checkpoints_are_rejected() { - let mut s = generated_session(ARS_OPTIONS); - s.start_with_shape(&[20, 8, 5]).unwrap(); - let good = s.checkpoint_bytes().unwrap(); - let mut t = generated_session(ARS_OPTIONS); - for len in [0, 8, 20, 40, good.len() / 2, good.len() - 1] { - assert!(t.restore_checkpoint_bytes(&good[..len]).is_err(), "{len} bytes"); - } - let mut extended = good.clone(); - extended.extend_from_slice(&[0; 8]); - assert!(t.restore_checkpoint_bytes(&extended).is_err()); - // A population too small for the elites it names (word 6 is the elite count). - let mut small = good.clone(); - small[8 + 2 * 4..8 + 3 * 4].copy_from_slice(&1u32.to_le_bytes()); - assert!(t.restore_checkpoint_bytes(&small).is_err()); - t.restore_checkpoint_bytes(&good).unwrap(); - } - - /// The algorithm can change between generations; a new one continues from - /// the cars it is given. - #[test] - fn the_algorithm_can_change_between_generations() { - let mut s = generated_session(PARENT_OPTIONS[0]); - s.start_with_shape(&[20, 8, 5]).unwrap(); - s.advance_generation(60).unwrap(); - s.next_generation().unwrap(); - assert_eq!(&s.checkpoint_bytes().unwrap()[..8], b"ALTDCKP2"); - s.advance_generation(60).unwrap(); - s.set_evolution_settings(r#"{"algorithm": "ars", "population": 10, "ars": {"elite_count": 1}}"#) - .unwrap(); - s.next_generation().unwrap(); - assert_eq!(s.runner.agents.len(), 10); - assert_eq!(&s.checkpoint_bytes().unwrap()[..8], b"ALTDCKP3"); - s.advance_generation(60).unwrap(); - s.next_generation().unwrap(); - s.set_evolution_settings(r#"{"algorithm": "ga", "population": 7}"#) - .unwrap(); - s.advance_generation(60).unwrap(); - s.next_generation().unwrap(); - assert_eq!(s.runner.agents.len(), 7); - assert_eq!(&s.checkpoint_bytes().unwrap()[..8], b"ALTDCKP2"); - } - - /// Restoring a full checkpoint drops the search state: ARS continues from - /// the best restored car. - #[test] - fn ars_continues_after_a_full_restore() { - let mut s = generated_session(ARS_OPTIONS); - s.start_with_shape(&[20, 8, 5]).unwrap(); - s.advance_generation(60).unwrap(); - let full = s.checkpoint().unwrap(); - let mut t = generated_session(ARS_OPTIONS); - t.restore_checkpoint(&full).unwrap(); - t.advance_generation(60).unwrap(); - t.next_generation().unwrap(); - assert_eq!(t.runner.agents.len(), 16); - } - - #[test] - fn ars_settings_are_validated() { - let bad = |options: &str| { - Session::new( - &generated_scene(), - &load("assets/networks/formula.json"), - &load("assets/models/formula.json"), - SessionOptions::from_json(options).unwrap(), - ) - .err() - }; - assert!(bad(r#"{"population": 4, "settings": {"algorithm": "ars"}}"#) - .unwrap() - .contains("elite_count + 3")); - assert!(bad(r#"{"population": 16, "settings": {"algorithm": "cma"}}"#) - .unwrap() - .contains("unknown algorithm")); - assert!(bad(r#"{"population": 16, "settings": {"algorithm": "ars", "ars": {"nu": 0}}}"#).is_some()); - for malformed in [ - r#"{"algorithm": "ars", "ars": {"bogus": 1}}"#, - r#"{"algorithm": "ars", "ars": {"nu": "high"}}"#, - r#"{"algorithm": 3}"#, - r#"{"rewards": [{"metric": 1, "weight": 100}]}"#, - ] { - let options = format!(r#"{{"population": 16, "settings": {malformed}}}"#); - assert!(bad(&options).is_some(), "{malformed}"); - } - let mut s = generated_session(ARS_OPTIONS); - assert!(s - .set_evolution_settings(r#"{"algorithm": "ars", "population": 3}"#) - .is_err()); - assert!(s - .set_evolution_settings(r#"{"algorithm": "ars", "population": 20, "ars": {"bogus": 1}}"#) - .is_err()); - s.set_evolution_settings(r#"{"algorithm": "ars", "population": 20}"#) - .unwrap(); - } - - /// The checkpoint grows with the parents, not the cars. - #[test] - fn parent_checkpoints_scale_with_the_selection() { - let mut s = generated_session( - r#"{"population": 60, "seed": 5, "settings": {"selection_size": 3, "mutation_rate": 0.2}}"#, - ); - s.start_with_shape(&[20, 8, 5]).unwrap(); - s.advance_generation(60).unwrap(); - s.next_generation().unwrap(); - let bytes = s.checkpoint_bytes().unwrap(); - let word = |i: usize| u32::from_le_bytes(bytes[8 + i * 4..12 + i * 4].try_into().unwrap()) as usize; - let params = crate::nn::network::parameter_count(&[20, 8, 5]); - assert_eq!((word(2), word(5)), (60, 3), "60 cars bred from the 3 best, preserved"); - // The generator's JSON (about 7 KB) is the fixed part, so compare with - // a quarter of the 60 cars' parameters rather than a few networks. - assert!(bytes.len() < 60 * params * 8 / 4, "{} bytes", bytes.len()); - assert!(bytes.len() >= 3 * params * 8, "the 3 parents are all there"); - } - - /// Format 1 checkpoints of earlier versions still restore. - #[test] - fn full_checkpoints_still_restore() { - let options = PARENT_OPTIONS[0]; - let mut s = generated_session(options); - s.start_with_shape(&[20, 8, 5]).unwrap(); - s.advance_generation(120).unwrap(); - s.next_generation().unwrap(); - let mut v = generated_session(options); - v.restore_checkpoint(&s.checkpoint().unwrap()).unwrap(); - let full = v.checkpoint_bytes().unwrap(); - assert_eq!(&full[..8], b"ALTDCKP1"); - let mut w = generated_session(options); - w.restore_checkpoint_bytes(&full).unwrap(); - assert_eq!(w.checkpoint().unwrap(), s.checkpoint().unwrap()); - w.advance_generation(120).unwrap(); - s.advance_generation(120).unwrap(); - w.next_generation().unwrap(); - s.next_generation().unwrap(); - assert_eq!( - w.checkpoint_bytes().unwrap(), - s.checkpoint_bytes().unwrap(), - "the next save is format 2" - ); - } - - /// Both RNG backends replay every profile through JSON, population and - /// parent checkpoints, and a mismatched restore leaves the session alone. - #[test] - fn checkpoints_preserve_profiles_and_reject_mismatches() { - for math in MathProfile::ALL { - for game_rng in [false, true] { - let options = serde_json::json!({ - "population": 6, "seed": 5, "mathProfile": math, - "settings": { - "selection_size": 3, "mutation_rate": 0.3, - "adaptive_mutation": true, "weight_decay": 0.001, - }, - }) - .to_string(); - let mut source = generated_session(&options); - if game_rng { - source.runner.rng = crate::training::game_random::GameRandom::new([1, 2, 3, 4], 12345).into(); - } - source.start_with_shape(&[20, 8, 5]).unwrap(); - source.advance_generation(90).unwrap(); - source.next_generation().unwrap(); - let json = source.checkpoint().unwrap(); - assert_eq!(rng_math(&json["rng"]).unwrap(), math); - let parents = source.checkpoint_bytes().unwrap(); - let mut from_json = generated_session(&options); - from_json.restore_checkpoint(&json).unwrap(); - let population = from_json.checkpoint_bytes().unwrap(); - assert_eq!(&parents[..8], PARENTS_MAGIC); - assert_eq!(&population[..8], CHECKPOINT_MAGIC); - let mut from_population = generated_session(&options); - from_population.restore_checkpoint_bytes(&population).unwrap(); - let mut from_parents = generated_session(&options); - from_parents.restore_checkpoint_bytes(&parents).unwrap(); - assert_eq!(from_population.checkpoint_bytes().unwrap(), population); - assert_eq!(from_parents.checkpoint_bytes().unwrap(), parents); - for session in [&from_json, &from_population, &from_parents] { - assert_eq!(session.math_profile(), math); - assert_eq!(session.checkpoint().unwrap(), json); - } - for other in MathProfile::ALL.into_iter().filter(|&p| p != math) { - let mut wrong_options: Value = serde_json::from_str(&options).unwrap(); - wrong_options["mathProfile"] = serde_json::json!(other); - let mut wrong = generated_session(&wrong_options.to_string()); - wrong.start_with_shape(&[20, 8, 5]).unwrap(); - let before = wrong.checkpoint_bytes().unwrap(); - let before_states = state_bits(&mut wrong); - for bytes in [&parents, &population] { - assert!(wrong - .restore_checkpoint_bytes(bytes) - .unwrap_err() - .contains("math profile")); - } - assert!(wrong.restore_checkpoint(&json).unwrap_err().contains("math profile")); - assert_eq!(wrong.checkpoint_bytes().unwrap(), before); - assert_eq!(state_bits(&mut wrong), before_states); - } - for _ in 0..2 { - source.advance_generation(90).unwrap(); - let states = state_bits(&mut source); - source.next_generation().unwrap(); - let checkpoint = source.checkpoint_bytes().unwrap(); - for session in [&mut from_json, &mut from_population, &mut from_parents] { - session.advance_generation(90).unwrap(); - assert_eq!(state_bits(session), states, "{math}, game RNG {game_rng}"); - session.next_generation().unwrap(); - assert_eq!(session.checkpoint_bytes().unwrap(), checkpoint); - } - } - } - } - } - - /// Before profiles, a checkpoint could omit its RNG. That still means - /// Proton math, whether it is saved as JSON or in the population format. - #[test] - fn legacy_checkpoints_without_rng_keep_proton_math() { - let options = r#"{"population": 2, "mathProfile": "proton"}"#; - let mut source = generated_session(options); - source.start_with_shape(&[20, 8, 5]).unwrap(); - let mut json = source.checkpoint().unwrap(); - let mut full = generated_session(options); - full.restore_checkpoint(&json).unwrap(); - let bytes = full.checkpoint_bytes().unwrap(); - let rng_at = 28 + 4 * json["shape"].as_array().unwrap().len(); - let rng_len = u32::from_le_bytes(bytes[24..28].try_into().unwrap()) as usize; - let data_at = (rng_at + rng_len).next_multiple_of(8); - let mut legacy = bytes[..rng_at].to_vec(); - legacy[24..28].copy_from_slice(&4u32.to_le_bytes()); - legacy.extend_from_slice(b"null"); - legacy.resize(legacy.len().next_multiple_of(8), 0); - legacy.extend_from_slice(&bytes[data_at..]); - json["rng"] = Value::Null; - for math in MathProfile::ALL { - let mut target = generated_session(&serde_json::json!({"mathProfile": math}).to_string()); - let json_result = target.restore_checkpoint(&json); - let bytes_result = target.restore_checkpoint_bytes(&legacy); - if math == MathProfile::Proton { - json_result.unwrap(); - bytes_result.unwrap(); - assert_eq!(target.runner.agents.len(), 2); - } else { - assert!(json_result.unwrap_err().contains("math profile")); - assert!(bytes_result.unwrap_err().contains("math profile")); - assert!(!target.started()); - } - } - } - - /// Corrupt parent checkpoints are errors, never panics (a panic traps WASM). - #[test] - fn malformed_parent_checkpoints_are_rejected() { - let options = PARENT_OPTIONS[0]; - let mut s = generated_session(options); - s.start_with_shape(&[20, 8, 5]).unwrap(); - s.advance_generation(60).unwrap(); - s.next_generation().unwrap(); - let bytes = s.checkpoint_bytes().unwrap(); - let word = |b: &[u8], i: usize| u32::from_le_bytes(b[8 + i * 4..12 + i * 4].try_into().unwrap()); - let set = |i: usize, value: u32| { - let mut b = bytes.clone(); - b[8 + i * 4..12 + i * 4].copy_from_slice(&value.to_le_bytes()); - b - }; - let (layers, parents, selected, preserved) = ( - word(&bytes, 3) as usize, - word(&bytes, 5), - word(&bytes, 6), - word(&bytes, 7), - ); - let first_index = 10 + layers; - let rates_at = { - let rng_at = 8 + (first_index + (selected + preserved) as usize) * 4; - (rng_at + word(&bytes, 4) as usize).next_multiple_of(8) - }; - let mut nan_rate = bytes.clone(); - nan_rate[rates_at..rates_at + 8].copy_from_slice(&f64::NAN.to_le_bytes()); - let mut no_selection = set(6, 0); - // Drop the selected indices so the rest of the layout stays valid. - no_selection.drain(8 + first_index * 4..8 + (first_index + selected as usize) * 4); - let cases: Vec<(&str, Vec)> = vec![ - ("truncated", bytes[..bytes.len() - 8].to_vec()), - ("header only", bytes[..40].to_vec()), - ("index past the parents", set(first_index, parents)), - ("huge selection", set(6, u32::MAX)), - ("huge parent count", set(5, u32::MAX)), - ("no cars", set(2, 0)), - ("more kept than cars", set(2, preserved - 1)), - ("unknown crossover", set(8, 3)), - ("unknown flags", set(9, 2)), - ("NaN mutation rate", nan_rate), - ("selects nothing", no_selection), - ("one-layer shape", set(3, 1)), - ]; - for (what, case) in cases { - let mut t = generated_session(options); - assert!( - std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| t - .restore_checkpoint_bytes(&case) - .is_err())) - .unwrap_or(false), - "{what}" - ); - } - } - - /// HIP tests share the process's one device claim, so they run one at a time. - #[cfg(not(target_arch = "wasm32"))] - static HIP_TESTS: std::sync::Mutex<()> = std::sync::Mutex::new(()); - - /// The HIP test lock, or `None` (the test is skipped) without a device. - #[cfg(not(target_arch = "wasm32"))] - fn hip_or_skip() -> Option> { - let guard = HIP_TESTS.lock().unwrap_or_else(|e| e.into_inner()); - match hip_device() { - Ok(_) => Some(guard), - Err(e) => { - eprintln!("skipping the HIP test: {e}"); - None - } - } - } - - #[cfg(not(target_arch = "wasm32"))] - fn with_backend(options: &str, backend: &str) -> String { - let mut value: Value = serde_json::from_str(options).unwrap(); - value["backend"] = Value::from(backend); - value.to_string() - } - - #[test] - fn backend_option_parses_and_defaults_to_cpu() { - assert_eq!(SessionOptions::from_json("{}").unwrap().backend, Backend::Cpu); - assert_eq!( - SessionOptions::from_json(r#"{"backend":"hip"}"#).unwrap().backend, - Backend::Hip - ); - assert!(SessionOptions::from_json(r#"{"backend":"webgpu"}"#).is_err()); - let s = generated_session(r#"{"population": 2}"#); - assert_eq!((s.backend(), s.backend_note()), (Backend::Cpu, None)); - } - - /// A HIP session drives exactly like a CPU session through windows, - /// whole generations and turnovers, and their checkpoints are - /// interchangeable. - #[cfg(not(target_arch = "wasm32"))] - #[test] - fn hip_sessions_match_cpu_sessions() { - let Some(_hip) = hip_or_skip() else { return }; - let options = PARENT_OPTIONS[0]; - let mut cpu = generated_session(options); - let mut gpu = generated_session(&with_backend(options, "hip")); - assert_eq!((gpu.backend(), gpu.backend_note()), (Backend::Hip, None)); - cpu.start_with_shape(&[20, 8, 5]).unwrap(); - gpu.start_with_shape(&[20, 8, 5]).unwrap(); - for (ticks, stop) in [(1, false), (37, false), (90, true)] { - assert_eq!(cpu.advance(ticks, stop).unwrap(), gpu.advance(ticks, stop).unwrap()); - assert_eq!(state_bits(&mut gpu), state_bits(&mut cpu), "window of {ticks}"); - } - // Consecutive windows keep the cars on the device, which counts the - // active ones; older libraries without the count read every window back. - let resident = hip_device() - .unwrap() - .0 - .try_symbol::("altd_gpu_sim_active_count") - .is_some(); - for ticks in [6, 1, 47, 120] { - assert_eq!(cpu.advance(ticks, true).unwrap(), gpu.advance(ticks, true).unwrap()); - assert_eq!(gpu.hip.as_ref().unwrap().stale(), resident); - assert_eq!( - (gpu.runner.tick, gpu.runner.batch_index), - (cpu.runner.tick, cpu.runner.batch_index) - ); - assert_eq!(gpu.active_count(), cpu.active_count(), "window of {ticks}"); - } - if resident { - assert!(std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| gpu.car_states())).is_err()); - } - assert_eq!(state_bits(&mut gpu), state_bits(&mut cpu), "consecutive windows"); - assert!(!gpu.hip.as_ref().unwrap().stale()); - assert_eq!(gpu.backend(), Backend::Hip, "{:?}", gpu.backend_note()); - assert_eq!( - cpu.advance_generation(600).unwrap(), - gpu.advance_generation(600).unwrap() - ); - assert_eq!(state_bits(&mut gpu), state_bits(&mut cpu)); - for generation in 1..=2 { - assert_eq!(gpu.next_generation().unwrap(), cpu.next_generation().unwrap()); - assert_eq!(gpu.checkpoint_bytes().unwrap(), cpu.checkpoint_bytes().unwrap()); - assert_eq!( - cpu.advance_generation(600).unwrap(), - gpu.advance_generation(600).unwrap() - ); - assert_eq!(gpu.active_count(), cpu.active_count()); - assert_eq!(state_bits(&mut gpu), state_bits(&mut cpu), "generation {generation}"); - assert_eq!(gpu.generation_summary(), cpu.generation_summary()); - } - // Checkpoints move between the backends. - let saved = gpu.checkpoint_bytes().unwrap(); - drop(gpu); - let mut gpu = generated_session(&with_backend(options, "hip")); - let mut from_gpu = generated_session(options); - gpu.restore_checkpoint_bytes(&cpu.checkpoint_bytes().unwrap()).unwrap(); - from_gpu.restore_checkpoint_bytes(&saved).unwrap(); - for s in [&mut gpu, &mut from_gpu] { - assert_eq!(s.advance_generation(600).unwrap(), cpu.runner.tick); - assert_eq!(state_bits(s), state_bits(&mut cpu)); - } - assert_eq!(gpu.backend(), Backend::Hip, "{:?}", gpu.backend_note()); - // A stage change to another generated track. - let settings = serde_json::from_value(load("assets/random_track_settings.json")).unwrap(); - let (_, next) = crate::track::training_tracks::training_scene_at( - &load("assets/scenes/formula_template.json"), - &settings, - None, - 4242, - 0, - 0, - crate::math::profile::MathProfile::Proton, - ) - .unwrap(); - for s in [&mut cpu, &mut gpu] { - s.replace_track(&next).unwrap(); - s.next_generation().unwrap(); - s.advance_generation(600).unwrap(); - } - assert_eq!(state_bits(&mut gpu), state_bits(&mut cpu), "the replaced track"); - assert_eq!(gpu.backend(), Backend::Hip, "{:?}", gpu.backend_note()); - - // An already verified session can restart with a different network - // shape and restore an earlier generation without stale GPU networks. - for s in [&mut cpu, &mut gpu] { - s.start_with_shape(&[20, 16, 5]).unwrap(); - } - assert!(gpu.hip.as_ref().unwrap().needs_verify()); - for s in [&mut cpu, &mut gpu] { - s.advance(30, false).unwrap(); - } - assert_eq!(state_bits(&mut gpu), state_bits(&mut cpu), "the restarted session"); - assert!(!gpu.hip.as_ref().unwrap().needs_verify()); - let checkpoint = cpu.checkpoint_bytes().unwrap(); - for s in [&mut cpu, &mut gpu] { - s.restore_checkpoint_bytes(&checkpoint).unwrap(); - } - assert!(gpu.hip.as_ref().unwrap().needs_verify()); - for s in [&mut cpu, &mut gpu] { - s.advance(30, false).unwrap(); - s.set_evolution_settings(r#"{"population":24,"selection_size":3}"#) - .unwrap(); - s.next_generation().unwrap(); - s.advance_generation(120).unwrap(); - } - assert_eq!(state_bits(&mut gpu), state_bits(&mut cpu), "the larger population"); - assert_eq!(gpu.checkpoint_bytes().unwrap(), cpu.checkpoint_bytes().unwrap()); - assert_eq!(gpu.backend(), Backend::Hip, "{:?}", gpu.backend_note()); - } - - /// Scenes HIP cannot run, and a second concurrent HIP session, fall back - /// to the CPU with a reason; the device is free again once a HIP session drops. - #[cfg(not(target_arch = "wasm32"))] - #[test] - fn hip_falls_back_to_the_cpu_with_a_reason() { - let Some(_hip) = hip_or_skip() else { return }; - let options = with_backend(r#"{"population": 4, "seed": 3}"#, "hip"); - let session = |scene: &Value| { - Session::new( - scene, - &load("assets/networks/formula.json"), - &load("assets/models/formula.json"), - SessionOptions::from_json(&options).unwrap(), - ) - .unwrap() - }; - let mut broadphase = generated_scene(); - broadphase["track"]["native_broadphase"] = Value::Bool(true); - let mut no_curve = generated_scene(); - no_curve["track"].as_object_mut().unwrap().remove("curve"); - for (scene, reason) in [(&broadphase, "native broadphase"), (&no_curve, "track.curve")] { - let s = session(scene); - assert_eq!(s.backend(), Backend::Cpu); - assert!(s.backend_note().unwrap().contains(reason), "{:?}", s.backend_note()); - } - let mut first = session(&generated_scene()); - assert_eq!(first.backend(), Backend::Hip); - let mut second = session(&generated_scene()); - assert_eq!( - (second.backend(), second.backend_note()), - (Backend::Cpu, Some("another session is using the GPU")) - ); - // The fallback still trains. - first.start_with_shape(&[20, 8, 5]).unwrap(); - second.start_with_shape(&[20, 8, 5]).unwrap(); - first.advance(30, false).unwrap(); - second.advance(30, false).unwrap(); - assert_eq!(state_bits(&mut first), state_bits(&mut second)); - drop(first); - assert_eq!(session(&generated_scene()).backend(), Backend::Hip); - - // The browser allows hidden layers wider than HIP's 16 lanes. - // Verification must preserve the initial state and fall back cleanly. - let mut wide = session(&generated_scene()); - let mut cpu = generated_session(r#"{"population":4,"seed":3}"#); - for s in [&mut wide, &mut cpu] { - s.start_with_shape(&[20, 32, 5]).unwrap(); - s.advance(30, false).unwrap(); - } - assert_eq!(wide.backend(), Backend::Cpu); - assert!(wide.backend_note().unwrap().contains("16"), "{:?}", wide.backend_note()); - assert_eq!(state_bits(&mut wide), state_bits(&mut cpu)); - } - - #[test] - fn export_weights_seed_the_first_generation() { - let scene = generated_scene(); - let spawn = serde_json::json!({"position":scene["reset_position"],"rotation":scene["reset_rotation"]}); - let options = SessionOptions { - population: Some(4), - spawn: Some(Spawn { - position: [ - spawn["position"][0].as_f64().unwrap(), - spawn["position"][1].as_f64().unwrap(), - ], - rotation: spawn["rotation"].as_f64().unwrap(), - }), - ..Default::default() - }; - let network = Network::xavier(&[20, 7, 5], &mut PyRandom::new(1)); - let mut export = network.to_json(); - for (key, value) in load("assets/networks/formula.json").as_object().unwrap() { - export[key] = value.clone(); - } - let mut s = Session::new(&scene, &export, &load("assets/models/formula.json"), options).unwrap(); - assert_eq!(s.template_shape(), Some(vec![20, 7, 5])); - s.start().unwrap(); - assert_eq!(s.runner.agents[0].network.shape, vec![20, 7, 5]); - assert_eq!(s.spawn().rotation, spawn["rotation"].as_f64().unwrap()); - } -} diff --git a/github-simulator/source/src/training/session/hip.rs b/github-simulator/source/src/training/session/hip.rs deleted file mode 100644 index faef1f753656915e6acee3523ebaf4317e9596db..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/training/session/hip.rs +++ /dev/null @@ -1,249 +0,0 @@ -//! The HIP backend of `Session`: driving windows run on libaltd_gpu.so while -//! reproduction, checkpoints and every reader stay on the CPU runner. The -//! cars and agents stay on the device between windows; `HipState::sync` -//! reads them back when the runner's copy is needed. - -use crate::gpu::hip::{Gpu, GpuWorld, PreparedGpuWorld}; -use crate::gpu::simulation::GpuSim; -use crate::track::world::World; -use crate::training::TrainingRunner; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::OnceLock; - -/// Ticks of the startup comparison with the CPU simulator. -const VERIFY_TICKS: u64 = 12; - -/// The process's HIP library and device name, opened on first use and never -/// closed, so sessions can own `'static` device handles. -static GPU: OnceLock> = OnceLock::new(); - -/// Set while a session holds the device: one HIP session per process. -static IN_USE: AtomicBool = AtomicBool::new(false); - -/// The HIP device and its name, or why it cannot be used. -pub fn device() -> Result<(&'static Gpu, &'static str), String> { - let opened = GPU.get_or_init(|| { - let gpu = Gpu::open(None)?; - crate::gpu::simulation::layout_matches(&gpu)?; - let name = gpu.device_name()?; - gpu.probe_kernels()?; - Ok((gpu, name)) - }); - match opened { - Ok((gpu, name)) => Ok((gpu, name.as_str())), - Err(e) => Err(e.clone()), - } -} - -/// The device claim of one session, released when the session drops. -struct Claim; - -impl Claim { - fn take() -> Option { - IN_USE - .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire) - .ok() - .map(|_| Claim) - } -} - -impl Drop for Claim { - fn drop(&mut self) { - IN_USE.store(false, Ordering::Release); - } -} - -/// Which copies of the population's cars and agents are current. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -enum Residency { - /// Only the runner's: the next window uploads them. - Host, - /// The runner's and the device's are the same. - Both, - /// Only the device's: a window ran since the last `sync`. - Device, -} - -pub(super) struct HipState { - // Declared before `world`, so it drops first: it refers to the world. - sim: GpuSim<'static>, - world: GpuWorld<'static>, - capacity: usize, - verified: bool, - residency: Residency, - /// The device's active car count while `residency` is `Device`. - active: usize, - _claim: Claim, -} - -fn upload_world(gpu: &'static Gpu, world: &World) -> Result, String> { - if world.track.shapes.is_empty() || world.track.path.len() < 2 { - return Err("the HIP simulator needs physics shapes and a path with at least two points".into()); - } - GpuWorld::try_from_prepared(gpu, PreparedGpuWorld::new(world)?) -} - -impl HipState { - /// A simulator for `runner`'s world, or why the runner needs the CPU. - pub(super) fn new(runner: &TrainingRunner) -> Result { - let (gpu, _) = device()?; - if runner.world.track.native_broadphase { - return Err("the HIP simulator does not model the native broadphase".into()); - } - let claim = Claim::take().ok_or("another session is using the GPU")?; - let world = upload_world(gpu, &runner.world)?; - let capacity = runner.settings.population.max(1); - let sim = runner.gpu_sim(&world, capacity)?; - Ok(HipState { - sim, - world, - capacity, - verified: false, - residency: Residency::Host, - active: 0, - _claim: claim, - }) - } - - /// Uploads the networks again before the next window (after a start, - /// restore or network change that kept the generation number). - pub(super) fn invalidate_networks(&mut self) { - self.sim.network_tag = None; - self.verified = false; - } - - /// Switches to the runner's new world and checks it again before the - /// next window. - #[cfg(any(test, feature = "server"))] - pub(super) fn replace_world(&mut self, runner: &TrainingRunner) -> Result<(), String> { - let world = upload_world(self.world.gpu(), &runner.world)?; - self.sim.try_set_world(&world)?; - // The simulator uses the new world now; the old one can go. - self.world = world; - self.verified = false; - Ok(()) - } - - /// Rebuilds the simulator when the population outgrew it. - fn fit(&mut self, runner: &mut TrainingRunner) -> Result<(), String> { - let population = runner.agents.len(); - if population > self.capacity { - self.sync(runner)?; - self.sim = runner.gpu_sim(&self.world, population)?; - self.capacity = population; - self.residency = Residency::Host; - } - Ok(()) - } - - /// Whether the runner's cars and agents are older than the device's. - pub(super) fn stale(&self) -> bool { - self.residency == Residency::Device - } - - /// The active car count, while the runner's cars are stale. - pub(super) fn active_count(&self) -> Option { - self.stale().then_some(self.active) - } - - /// Reads the device's cars and agents back into the runner if they are - /// newer. - pub(super) fn sync(&mut self, runner: &mut TrainingRunner) -> Result<(), String> { - if self.residency == Residency::Device { - runner.download_state_gpu(&mut self.sim, &self.world)?; - self.residency = Residency::Both; - } - Ok(()) - } - - /// The runner's cars or agents are about to change (after a `sync`): - /// the next window uploads them. - pub(super) fn host_changed(&mut self) { - debug_assert!(!self.stale()); - self.residency = Residency::Host; - } - - /// Whether the startup comparison still has to run. - pub(super) fn needs_verify(&self) -> bool { - !self.verified - } - - /// Runs `VERIFY_TICKS` on the CPU and on HIP from the same state and - /// compares every car and agent exactly; the runner ends as it started. - pub(super) fn verify(&mut self, runner: &mut TrainingRunner) -> Result<(), String> { - self.fit(runner)?; - self.sync(runner)?; - // The device ends with the comparison's state, the runner as it started. - self.residency = Residency::Host; - let surfaces = &self.world.arrays.surfaces; - let (backup, tick, batch) = (runner.agents.clone(), runner.tick, runner.batch_index); - runner.advance(VERIFY_TICKS, false); - let expected = runner.canonical_state(surfaces); - runner.agents = backup.clone(); - runner.tick = tick; - runner.batch_index = batch; - self.sim.network_tag = None; - let actual = expected.and_then(|expected| { - runner.advance_window_gpu(&mut self.sim, &self.world, VERIFY_TICKS, false, None)?; - Ok((expected, runner.canonical_state(surfaces)?)) - }); - runner.agents = backup; - runner.tick = tick; - runner.batch_index = batch; - let (expected, actual) = actual?; - if expected != actual { - return Err("HIP verification differs from the CPU simulator".into()); - } - self.verified = true; - Ok(()) - } - - pub(super) fn advance(&mut self, runner: &mut TrainingRunner, ticks: u64, stop: bool) -> Result { - self.window(runner, ticks, stop, None) - } - - pub(super) fn advance_generation(&mut self, runner: &mut TrainingRunner, limit: u64) -> Result { - let (ticks, time_limit) = runner.gpu_generation_window(limit)?; - self.window(runner, ticks, true, Some(time_limit)) - } - - /// `advance_window_gpu` without the readback: the cars and agents stay on - /// the device, and only their active count is read. - fn window( - &mut self, - runner: &mut TrainingRunner, - ticks: u64, - stop: bool, - time_limit: Option, - ) -> Result { - self.fit(runner)?; - if ticks == 0 { - return Ok(0); - } - runner.check_gpu_window()?; - if self.residency == Residency::Host { - runner.upload_state_gpu(&mut self.sim, &self.world)?; - self.residency = Residency::Both; - } - let before = self.residency; - match runner.window_gpu(&mut self.sim, ticks, stop, time_limit) { - Ok(executed) => { - self.residency = Residency::Device; - match self.sim.try_active_count()? { - Some(active) => self.active = active, - // An older library: read everything back, as before. - None => self.sync(runner)?, - } - Ok(executed) - } - Err(e) => { - // The device state may be partly advanced; the runner's is - // still whole if it was current. - if before == Residency::Both { - self.residency = Residency::Host; - } - Err(e) - } - } - } -} diff --git a/github-simulator/source/src/training/session/input.rs b/github-simulator/source/src/training/session/input.rs deleted file mode 100644 index 7293c62555e70a0e9dd2d34d6b9f655915351bf5..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/training/session/input.rs +++ /dev/null @@ -1,257 +0,0 @@ -//! Validate embedding-host scene JSON before legacy, trusted-data constructors. -use crate::math::vec2::V2; -use crate::track::world::Surface; -use serde_json::Value; - -fn number(v: &Value, label: &str) -> Result { - v.as_f64() - .filter(|n| n.is_finite() && (*n as f32).is_finite()) - .ok_or_else(|| format!("invalid number: {label}")) -} -fn vector(v: &Value, label: &str) -> Result<(), String> { - let pair = v - .as_array() - .filter(|a| a.len() == 2) - .ok_or_else(|| format!("invalid vector: {label}"))?; - for n in pair { - number(n, label)?; - } - Ok(()) -} -fn curve_vector(v: &Value, label: &str) -> Result<(), String> { - if v.is_array() { - vector(v, label) - } else { - number(&v["x"], label)?; - number(&v["y"], label)?; - Ok(()) - } -} -fn optional_number(v: &Value, key: &str) -> Result<(), String> { - if let Some(n) = v.get(key) { - number(n, key)?; - } - Ok(()) -} -fn optional_vector(v: &Value, key: &str) -> Result<(), String> { - if let Some(p) = v.get(key) { - vector(p, key)?; - } - Ok(()) -} -fn array<'a>(v: &'a Value, key: &str) -> Result<&'a [Value], String> { - match v.get(key) { - None => Ok(&[]), - Some(a) => a - .as_array() - .map(Vec::as_slice) - .ok_or_else(|| format!("invalid array: {key}")), - } -} - -fn cell_coords(v: &Value, label: &str) -> Result<[i64; 2], String> { - vector(v, label)?; - let read = |v: &Value| { - let n = number(v, label)?; - // Match the constructor's truncation without allowing saturating casts. - if n <= i64::MIN as f64 || n >= i64::MAX as f64 { - return Err(format!("{label} exceeds signed integer range")); - } - Ok(n as i64) - }; - Ok([read(&v[0])?, read(&v[1])?]) -} - -fn tile_capacity(lo: [i64; 2], hi: [i64; 2]) -> Result<(), String> { - let span = |i: usize| hi[i].checked_sub(lo[i]).and_then(|n| n.checked_add(1)); - let cells = span(0) - .and_then(|nx| span(1).and_then(|ny| nx.checked_mul(ny))) - .and_then(|n| usize::try_from(n).ok()) - .ok_or("tile table dimensions overflow")?; - // Each actual dense table must fit Vec's signed byte-offset capacity. - // This checks numerical allocation capacity, not a work quota. - if [ - std::alloc::Layout::array::>(cells), - std::alloc::Layout::array::>(cells), - std::alloc::Layout::array::>>(cells), - ] - .iter() - .any(Result::is_err) - { - return Err("tile table exceeds allocation capacity".into()); - } - Ok(()) -} - -pub(super) fn scene(v: &Value) -> Result<(), String> { - let car = v.get("vehicle").filter(|c| c.is_object()).ok_or("missing vehicle")?; - let track = v.get("track").filter(|c| c.is_object()).ok_or("missing track")?; - vector(&car["shape_size"], "shape_size")?; - if car["shape_size"] - .as_array() - .unwrap() - .iter() - .any(|x| x.as_f64().unwrap() <= 0.0) - { - return Err("shape_size must be positive".into()); - } - for key in [ - "shape_rotation", - "mass", - "inertia_server", - "grip", - "steering_speed", - "air_resistance", - "max_velocity", - "linear_damp", - "angular_damp", - ] { - number(&car[key], key)?; - } - if number(&car["mass"], "mass")? <= 0.0 || number(&car["inertia_server"], "inertia_server")? <= 0.0 { - return Err("mass and inertia must be positive".into()); - } - if car["center_steering"].as_bool().is_none() { - return Err("invalid center_steering".into()); - } - for key in ["shape_position", "center_of_mass"] { - optional_vector(car, key)?; - } - if let Some(basis) = car.get("shape_basis") { - let rows = basis.as_array().filter(|a| a.len() == 2).ok_or("invalid shape_basis")?; - for row in rows { - vector(row, "shape_basis")?; - } - } - let wheels = car["wheels"] - .as_array() - .filter(|a| !a.is_empty()) - .ok_or("missing wheels")?; - for wheel in wheels { - vector(&wheel["position"], "wheel position")?; - if wheel["steering"].as_bool().is_none() { - return Err("invalid wheel steering".into()); - } - for key in ["max_angle_deg", "power", "brake_power", "handbrake_power"] { - number(&wheel[key], key)?; - } - } - let surfaces = car["surfaces"].as_object().ok_or("missing vehicle surfaces")?; - for surface in surfaces.values() { - for key in ["grip", "power", "steering"] { - optional_number(surface, key)?; - } - } - // These fields have numeric/default semantics in the trusted constructor. - for key in ["friction", "max_contacts_reported", "gravity_scale", "bounce"] { - if let Some(n) = car.get(key).filter(|n| !n.is_null()) { - number(n, key)?; - } - } - if let Some(physics) = v.get("physics") { - for key in [ - "solver_iterations", - "contact_default_bias", - "contact_recycle_radius", - "contact_max_separation", - "contact_max_allowed_penetration", - ] { - optional_number(physics, key)?; - } - optional_vector(physics, "gravity")?; - } - optional_vector(v, "reset_position")?; - optional_number(v, "reset_rotation")?; - for key in ["tile_map_position", "tile_size"] { - optional_vector(track, key)?; - } - if let Some(size) = track.get("tile_size") { - if size.as_array().unwrap().iter().any(|x| x.as_f64().unwrap() <= 0.0) { - return Err("tile_size must be positive".into()); - } - } - for wall in array(track, "walls")? { - let points = wall.as_array().filter(|a| a.len() == 2).ok_or("invalid wall")?; - for point in points { - vector(point, "wall")?; - } - } - for shape in array(track, "physics_shapes")? { - optional_vector(shape, "origin")?; - if let Some(tile) = shape.get("tile") { - cell_coords(tile, "shape tile")?; - } - let points = shape["points"] - .as_array() - .filter(|a| a.len() >= 3) - .ok_or("invalid shape points")?; - for p in points { - vector(p, "shape points")?; - } - if let Some(local) = shape.get("local_points") { - let rows = local - .as_array() - .filter(|a| a.len() == points.len()) - .ok_or("invalid local_points")?; - for p in rows { - curve_vector(p, "local_points")?; - } - } - } - for polygon in array(track, "polygons")? { - let points = polygon["points"].as_array().ok_or("missing polygon points")?; - for p in points { - vector(p, "polygon point")?; - } - } - for key in ["path", "path_forward"] { - for p in array(track, key)? { - vector(p, key)?; - } - } - for cell in array(track, "native_cell_order")? { - cell_coords(cell, "native_cell_order")?; - } - if array(track, "path")?.len() < 2 { - return Err("track path needs at least two points".into()); - } - let forward = array(track, "path_forward")?; - if !forward.is_empty() && forward.len() != array(track, "path")?.len() { - return Err("path_forward and path lengths differ".into()); - } - let tiles = array(track, "tiles")?; - let mut lo = [i64::MAX; 2]; - let mut hi = [i64::MIN; 2]; - for tile in tiles { - let coords = cell_coords(&tile["coords"], "tile coords")?; - for i in 0..2 { - lo[i] = lo[i].min(coords[i]); - hi[i] = hi[i].max(coords[i]); - } - if tile["surface"].as_str().is_none() { - return Err("invalid tile surface".into()); - } - for p in array(tile, "connections")? { - vector(p, "tile connection")?; - } - } - if !tiles.is_empty() { - tile_capacity(lo, hi)?; - } - if let Some(curve) = track.get("curve") { - let interval = number(&curve["bake_interval"], "bake_interval")?; - if interval <= 0.0 || !(interval as f32).is_finite() { - return Err("invalid bake_interval".into()); - } - let controls = curve["points"] - .as_array() - .filter(|a| a.len() >= 2) - .ok_or("curve needs at least two control points")?; - for control in controls { - for key in ["position", "incoming", "outgoing"] { - curve_vector(&control[key], key)?; - } - } - } - Ok(()) -} diff --git a/github-simulator/source/src/training/stats.rs b/github-simulator/source/src/training/stats.rs deleted file mode 100644 index f32132a2ca4b80a3b628f7567d4c0b51aaca0ec3..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/training/stats.rs +++ /dev/null @@ -1,237 +0,0 @@ -//! Signed path scores, lap timing, and accumulated driving metrics. - -use crate::math::pymath::{clamp, py_min}; -use crate::math::vec2::V2; -use crate::physics::car::{Car, Controls}; -use crate::track::world::{Track, World}; -use crate::training::evolution::Metrics; -use std::collections::VecDeque; - -/// `ScoreTracker`: signed path score and forward-lap crossing on baked points. -#[derive(Clone, Debug, Default)] -pub struct ScoreTracker { - pub previous_offset: Option, - pub total_score: f64, - pub lap_count: u64, - pub went_backwards: bool, - /// Fraction of the last stats interval at which a forward lap completed. - pub lap_crossing_fraction: Option, -} - -impl ScoreTracker { - pub fn update(&mut self, track: &Track, position: V2) { - self.lap_crossing_fraction = None; - let length = track.path_length(); - if length <= 0.0 { - return; - } - let Some(previous) = self.previous_offset else { - let offset = track.closest_path(position, None).offset; - self.total_score += (if offset > length / 2.0 { offset - length } else { offset }) * (5.0 / 384.0); - self.went_backwards = offset > length / 2.0; - self.previous_offset = Some(offset); - return; - }; - let offset = track.closest_path(position, Some(previous)).offset; - let mut change = offset - previous; - if change > length / 2.0 { - change -= length; - } else if change < -length / 2.0 { - change += length; - } - self.total_score += change * (5.0 / 384.0); - let crossing = previous + change; - if crossing < 0.0 { - self.went_backwards = true; - } else if crossing >= length { - if !self.went_backwards { - self.lap_count += 1; - self.lap_crossing_fraction = Some((length - previous) / (length - previous + offset)); - } - self.went_backwards = false; - } - self.previous_offset = Some(offset); - } -} - -#[derive(Clone, Debug)] -pub struct TrainingStats { - pub score: ScoreTracker, - pub network_novelty: f64, - pub update_count: u64, - pub collision_count: u64, - pub total_score: f64, - pub total_speed: f64, - pub total_abs_steering: f64, - pub total_actual_distance: f64, - pub total_drifting_amount: f64, - pub total_momentum_change_frequency: f64, - pub total_throttle_change_frequency: f64, - pub total_braking_frequency: f64, - pub total_distance_from_wall: f64, - pub total_distance_from_center: f64, - pub best_lap_time: Option, - pub idle_ticks: u64, - pub previous_speed: f64, - pub previous_throttle: f64, - pub previous_score: f64, - pub previous_lap_count: u64, - pub lap_start_tick: u64, - pub drift_ticks: u64, - pub current_slip_angle_degrees: f64, - pub continuous_drift_time: f64, - pub max_continuous_drift_time: f64, - pub all_laps_time_sum: Option, - pub recent_score_diffs: VecDeque, -} - -impl TrainingStats { - pub fn new(network_novelty: f64) -> TrainingStats { - TrainingStats { - score: ScoreTracker::default(), - network_novelty, - update_count: 0, - collision_count: 0, - total_score: 0.0, - total_speed: 0.0, - total_abs_steering: 0.0, - total_actual_distance: 0.0, - total_drifting_amount: 0.0, - total_momentum_change_frequency: 0.0, - total_throttle_change_frequency: 0.0, - total_braking_frequency: 0.0, - total_distance_from_wall: 0.0, - total_distance_from_center: 0.0, - best_lap_time: None, - idle_ticks: 0, - previous_speed: 0.0, - previous_throttle: 0.0, - previous_score: 0.0, - previous_lap_count: 0, - lap_start_tick: 0, - drift_ticks: 0, - current_slip_angle_degrees: 0.0, - continuous_drift_time: 0.0, - max_continuous_drift_time: 0.0, - all_laps_time_sum: None, - recent_score_diffs: VecDeque::with_capacity(10), - } - } - - pub fn update(&mut self, world: &World, car: &Car, controls: &Controls, tick: u64, pending_contact: bool) { - let track = &world.track; - if pending_contact { - self.collision_count += 1; - } - if !car.active { - return; - } - self.update_count += 1; - let speed = crate::math::godot_math::F2::from(car.velocity).length() as f64; - let initializing_score = self.score.previous_offset.is_none(); - self.score.update(track, car.position); - let score = self.score.total_score; - // AgentStats.GetClosestOffset seeds TotalScore before UpdateScore - // takes its movement-difference baseline. - let diff = if initializing_score { - 0.0 - } else { - score - self.previous_score - }; - if self.recent_score_diffs.len() == 10 { - self.recent_score_diffs.pop_front(); - } - self.recent_score_diffs.push_back(diff); - let mean = - self.recent_score_diffs.iter().copied().fold(0.0, |a, b| a + b) / self.recent_score_diffs.len() as f64; - self.idle_ticks = if mean < 0.1 { self.idle_ticks + 1 } else { 0 }; - self.total_score = score; - self.previous_score = score; - self.total_speed += speed; - self.total_abs_steering += clamp(controls.steering, -1.0, 1.0).abs(); - self.total_actual_distance += speed * 0.1; - self.total_momentum_change_frequency += (speed - self.previous_speed).abs(); - self.total_throttle_change_frequency += - (clamp(controls.acceleration, -1.0, 1.0) - self.previous_throttle).abs(); - if controls.brake > 0.0001 || controls.handbrake > 0.0001 { - self.total_braking_frequency += 0.1; - } - self.previous_speed = speed; - self.previous_throttle = clamp(controls.acceleration, -1.0, 1.0); - if let Some(wall) = track.closest_wall(car.position) { - self.total_distance_from_wall += (crate::math::godot_math::F2::from(wall) - - crate::math::godot_math::F2::from(car.position)) - .length() as f64; - } - if track.path.len() >= 2 { - let center = track.path_position(self.score.previous_offset.unwrap_or(0.0)); - self.total_distance_from_center += (crate::math::godot_math::F2::from(center) - - crate::math::godot_math::F2::from(car.position)) - .length() as f64; - } - let right = car.body_basis.0.normalized(); - let velocity = crate::math::godot_math::F2::from(car.velocity); - let front = velocity.dot(right.rotated(-std::f32::consts::FRAC_PI_2)); - let side = velocity.dot(right); - self.current_slip_angle_degrees = (car.math.atan2(side, front) * 57.29578f32) as f64; - let slip_degrees = self.current_slip_angle_degrees.abs(); - self.drift_ticks = if speed > 50.0 && (15.0..=60.0).contains(&slip_degrees) { - self.drift_ticks + 1 - } else { - 0 - }; - if self.drift_ticks >= 3 { - self.total_drifting_amount += 0.1; - self.continuous_drift_time += 0.1; - self.max_continuous_drift_time = self.max_continuous_drift_time.max(self.continuous_drift_time); - } - if self.drift_ticks == 0 { - self.continuous_drift_time = 0.0; - } - if self.score.lap_count > self.previous_lap_count { - // EvolutionStatsLapsObserver rounds to milliseconds, with .NET's - // default midpoint-to-even rule. It stores the sample time as the - // next lap start, not the interpolated crossing time. - let fraction = self.score.lap_crossing_fraction.unwrap_or(1.0).clamp(0.0, 1.0); - let elapsed = (tick / 6) as f64 * 0.1 - (self.lap_start_tick / 6) as f64 * 0.1; - let lap_time = ((elapsed - 0.1 + fraction * 0.1) * 1000.0).round_ties_even() / 1000.0; - let current = match self.best_lap_time { - Some(best) => best, - _ => lap_time, - }; - self.best_lap_time = Some(py_min(current, lap_time)); - self.all_laps_time_sum = Some(self.all_laps_time_sum.unwrap_or(0.0) + lap_time); - self.lap_start_tick = tick; - self.previous_lap_count = self.score.lap_count; - } - } - - /// `TrainingStats.metrics()` in `METRIC_NAMES` order. - pub fn metrics(&self) -> Metrics { - let best_lap_performance = match self.best_lap_time { - Some(t) if t != 0.0 => Some(1.0 / t), - _ => None, - }; - let efficiency = if self.total_actual_distance != 0.0 { - self.total_score / (self.total_actual_distance * (5.0 / 384.0)) - } else { - 0.0 - }; - [ - Some(self.total_score), - Some(self.total_speed), - Some(self.total_abs_steering), - Some(self.total_actual_distance), - Some(self.total_drifting_amount), - Some(self.total_momentum_change_frequency), - Some(self.total_throttle_change_frequency), - Some(self.total_braking_frequency), - Some(self.total_distance_from_wall), - Some(self.total_distance_from_center), - Some(self.network_novelty), - Some(self.collision_count as f64), - best_lap_performance, - Some(efficiency), - ] - } -} diff --git a/github-simulator/source/src/training/training_profile.rs b/github-simulator/source/src/training/training_profile.rs deleted file mode 100644 index 0163999e9272eacfc0c9fede4438927790bd883d..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/training/training_profile.rs +++ /dev/null @@ -1,44 +0,0 @@ -//! Optional timings for population turnover and GPU transfers. -use std::sync::OnceLock; -use std::time::Instant; - -pub(crate) struct Profile { - name: &'static str, - previous: Option, - phases: Vec<(&'static str, f64)>, -} - -impl Profile { - pub(crate) fn new(name: &'static str) -> Self { - static ENABLED: OnceLock = OnceLock::new(); - // `Instant::now` is unavailable on wasm32-unknown-unknown; the - // environment variable cannot be set there either. - let enabled = *ENABLED.get_or_init(|| { - !cfg!(target_arch = "wasm32") && std::env::var("ALTD_TRAIN_PROFILE").is_ok_and(|v| v == "1") - }); - Self { - name, - previous: enabled.then(Instant::now), - phases: Vec::new(), - } - } - - pub(crate) fn mark(&mut self, name: &'static str) { - if let Some(previous) = &mut self.previous { - let now = Instant::now(); - self.phases.push((name, now.duration_since(*previous).as_secs_f64())); - *previous = now; - } - } -} - -impl Drop for Profile { - fn drop(&mut self) { - if self.previous.is_some() { - eprintln!( - "altd_train_profile {}", - serde_json::json!({"stage": self.name, "seconds": self.phases}) - ); - } - } -} diff --git a/github-simulator/source/src/wasm/float.wgsl b/github-simulator/source/src/wasm/float.wgsl deleted file mode 100644 index 7486a7825e26e10bd091cb2dd80f9f5c0fbee774..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/wasm/float.wgsl +++ /dev/null @@ -1,143 +0,0 @@ -// params.pad is uploaded as zero. Passing every operation through an integer -// XOR with this runtime value prevents contraction across rounding boundaries. -fn fp_round(value: f32) -> f32 { - return bitcast(bitcast(value) ^ params.pad); -} -fn fp_add(a: f32, b: f32) -> f32 { return fp_round(a + b); } -fn fp_sub(a: f32, b: f32) -> f32 { return fp_round(a - b); } -fn fp_mul(a: f32, b: f32) -> f32 { return fp_round(a * b); } -// Sign and selection operations preserve subnormal and signed-zero bits. -// Hardware float operations may flush subnormals or identify +0 and -0. -fn fp_abs(a: f32) -> f32 { return bitcast(bitcast(a) & 0x7fffffffu); } -fn fp_neg(a: f32) -> f32 { return bitcast(bitcast(a) ^ 0x80000000u); } -fn fp_select(a: f32, b: f32, condition: bool) -> f32 { - return bitcast(select(bitcast(a), bitcast(b), condition)); -} -// WGSL division permits 2.5 ULP error. Divide binary32 significands exactly -// with integers instead, then round once to nearest, ties to even. Normal -// operands whose quotient is normal take this branch-free path; zeros, -// infinities, NaNs, subnormals and quotients near the exponent limits take -// fp_div_general. The exponent field is added to the rounded 24-bit quotient, -// so a quotient that rounds up to 2^24 carries into the exponent. -fn fp_div(a: f32, b: f32) -> f32 { - let ab = bitcast(a); - let bb = bitcast(b); - let ae = (ab >> 23u) & 0xffu; - let be = (bb >> 23u) & 0xffu; - let biased = i32(ae) - i32(be) + 127; - if (ae == 0u || be == 0u || ae == 0xffu || be == 0xffu || biased < 2 || biased > 254) { - return fp_div_general(a, b); - } - var numerator = (ab & 0x7fffffu) | 0x800000u; - let divisor = (bb & 0x7fffffu) | 0x800000u; - let shift = numerator < divisor; - numerator = select(numerator, numerator << 1u, shift); - let exponent = biased - select(0, 1, shift); - // As in fp_div_general: a hardware estimate corrected by two steps each way. - var quotient = u32(f32(numerator) / f32(divisor) * 8388608.0); - let off = fp_sub(fp_mul(f32(quotient), f32(divisor)), f32(numerator) * 8388608.0); - var estimate = i32((numerator << 23u) - quotient * divisor); - for (var i = 0u; i < 2u; i = i + 1u) { - let low = estimate < 0; - quotient = select(quotient, quotient - 1u, low); - estimate = select(estimate, estimate + i32(divisor), low); - } - for (var i = 0u; i < 2u; i = i + 1u) { - let high = estimate >= i32(divisor); - quotient = select(quotient, quotient + 1u, high); - estimate = select(estimate, estimate - i32(divisor), high); - } - if (!(abs(off) < 1073741824.0) || estimate < 0 || u32(estimate) >= divisor) { return fp_div_general(a, b); } - let twice_remainder = u32(estimate) << 1u; - quotient = quotient + select(0u, 1u, twice_remainder > divisor || (twice_remainder == divisor && (quotient & 1u) != 0u)); - return bitcast(((ab ^ bb) & 0x80000000u) | ((u32(exponent - 1) << 23u) + quotient)); -} -fn fp_div_general(a: f32, b: f32) -> f32 { - let ab = bitcast(a); - let bb = bitcast(b); - let sign = (ab ^ bb) & 0x80000000u; - let am = ab & 0x7fffffffu; - let bm = bb & 0x7fffffffu; - let infinity = 0x7f800000u; - if (am > infinity || bm > infinity || (am == infinity && bm == infinity) || (am == 0u && bm == 0u)) { - return bitcast(0x7fc00000u ^ params.pad); - } - if (am == infinity || bm == 0u) { return bitcast(sign | infinity); } - if (am == 0u || bm == infinity) { return bitcast(sign); } - - var ae = i32(am >> 23u); - var be = i32(bm >> 23u); - var numerator = am & 0x7fffffu; - var divisor = bm & 0x7fffffu; - if (ae == 0) { - let shift = countLeadingZeros(numerator) - 8u; - numerator = numerator << shift; - ae = 1 - i32(shift); - } else { numerator = numerator | 0x800000u; } - if (be == 0) { - let shift = countLeadingZeros(divisor) - 8u; - divisor = divisor << shift; - be = 1 - i32(shift); - } else { divisor = divisor | 0x800000u; } - - var exponent = ae - be + 127; - if (numerator < divisor) { - numerator = numerator << 1u; - exponent = exponent - 1; - } - if (exponent > 254) { return bitcast(sign | infinity); } - if (exponent < -23) { return bitcast(sign); } - // quotient = floor(numerator * 2^23 / divisor), a 24-bit integer, and the - // remainder, from the hardware quotient corrected by up to two steps each - // way without branching. The remainder is computed modulo 2^32, exact while - // the true one fits an i32. The correctly rounded product proves that: it - // is within 2^26 of the true remainder. A device whose division is further - // off than the steps reach takes the bit loop. - var quotient = u32(f32(numerator) / f32(divisor) * 8388608.0); - let off = fp_sub(fp_mul(f32(quotient), f32(divisor)), f32(numerator) * 8388608.0); - var estimate = i32((numerator << 23u) - quotient * divisor); - for (var i = 0u; i < 2u; i = i + 1u) { - let low = estimate < 0; - quotient = select(quotient, quotient - 1u, low); - estimate = select(estimate, estimate + i32(divisor), low); - } - for (var i = 0u; i < 2u; i = i + 1u) { - let high = estimate >= i32(divisor); - quotient = select(quotient, quotient + 1u, high); - estimate = select(estimate, estimate - i32(divisor), high); - } - var remainder = u32(estimate); - if (!(abs(off) < 1073741824.0) || estimate < 0 || remainder >= divisor) { - remainder = numerator - divisor; - quotient = 1u; - for (var i = 0u; i < 23u; i = i + 1u) { - remainder = remainder << 1u; - quotient = quotient << 1u; - if (remainder >= divisor) { - remainder = remainder - divisor; - quotient = quotient | 1u; - } - } - } - if (exponent <= 0) { - // Shift the unrounded quotient into the subnormal range. The exact - // remainder contributes a sticky bit, avoiding double rounding. - let shift = u32(1 - exponent); - let halfway = 1u << (shift - 1u); - let discarded = quotient & ((1u << shift) - 1u); - quotient = quotient >> shift; - if (discarded > halfway || (discarded == halfway && (remainder != 0u || (quotient & 1u) != 0u))) { - quotient = quotient + 1u; - } - return bitcast(sign | quotient); - } - let twice_remainder = remainder << 1u; - if (twice_remainder > divisor || (twice_remainder == divisor && (quotient & 1u) != 0u)) { - quotient = quotient + 1u; - if (quotient == 0x1000000u) { - quotient = quotient >> 1u; - exponent = exponent + 1; - } - } - return bitcast(sign | (u32(exponent) << 23u) | (quotient & 0x7fffffu)); -} diff --git a/github-simulator/source/src/wasm/gpu.rs b/github-simulator/source/src/wasm/gpu.rs deleted file mode 100644 index 333b4c6595c36eb7cc74efd88d9356f5e73efd53..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/wasm/gpu.rs +++ /dev/null @@ -1,570 +0,0 @@ -//! The WebGPU raycaster: `rays.wgsl` runs the scene's BSP `RayTree` queries -//! (raycasts and closest wall points) in a compute shader, serving the ray -//! sensors of `TrainingRunner`'s ray window and batch queries for JavaScript. -//! -//! `float.wgsl` preserves the CPU's rounding boundaries and divides binary32 -//! significands with integers, avoiding WGSL fusion and approximate division. -//! `verify` compares a device with the CPU on random queries, and -//! `gpuVerifyEvery` samples the sensor rays of every window. These checks -//! diagnose hardware differences without replacing the GPU's results. - -use super::{Shared, Simulation}; -use crate::math::godot_math::F2; -use crate::math::vec2::V2; -use crate::track::bsp::RayTree; -use js_sys::{ArrayBuffer, Float32Array, Promise}; -use std::cell::{Cell, RefCell}; -use std::rc::Rc; -use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; -use wasm_bindgen_futures::{future_to_promise, JsFuture}; -use web_sys::{ - gpu_buffer_usage as usage, gpu_map_mode, GpuAdapter, GpuBindGroup, GpuBindGroupDescriptor, GpuBindGroupEntry, - GpuBuffer, GpuBufferBinding, GpuBufferDescriptor, GpuCompilationInfo, GpuCompilationMessage, GpuComputePipeline, - GpuComputePipelineDescriptor, GpuDevice, GpuPipelineLayout, GpuProgrammableStage, GpuQueue, GpuShaderModule, - GpuShaderModuleDescriptor, -}; - -/// The BSP arrays of `RayTree::gpu_arrays`. -pub type TreeArrays = (Vec<([u32; 4], [f32; 4], [f32; 4])>, Vec<[f32; 4]>, f32, usize); - -const FLOAT_SHADER: &str = include_str!("float.wgsl"); -const RAY_SHADER: &str = include_str!("rays.wgsl"); -/// `MAX_DEPTH` of rays.wgsl: the deepest BSP the shader's stack holds. -pub const MAX_DEPTH: usize = 48; -const WORKGROUP: u32 = 64; -/// `params.op` of rays.wgsl. -pub const OP_RAYCAST: u32 = 0; -pub const OP_CLOSEST_WALL: u32 = 1; - -fn error(message: impl std::fmt::Display) -> JsValue { - js_sys::Error::new(&message.to_string()).into() -} - -fn describe(e: JsValue) -> String { - e.dyn_ref::() - .map_or_else(|| format!("{e:?}"), |e| String::from(e.message())) -} - -/// The bytes of plain arrays of 32-bit words; WebAssembly is little-endian, as GPU buffers are. -fn bytes(rows: &[T]) -> &[u8] { - // SAFETY: T is `[u32; 12]` or `[f32; 4]`, without padding; the slice - // covers exactly the rows' memory for reading. - unsafe { std::slice::from_raw_parts(rows.as_ptr().cast::(), std::mem::size_of_val(rows)) } -} - -/// The CPU raycast in the shader's result layout. -pub(crate) fn cpu_raycast(tree: &RayTree, q: &[f32; 4]) -> [f32; 4] { - match tree.raycast(V2::new(q[0] as f64, q[1] as f64), V2::new(q[2] as f64, q[3] as f64)) { - Some(hit) => { - let h = F2::from(hit); - [h.x, h.y, 1.0, 0.0] - } - None => [0.0; 4], - } -} - -/// Whether two results denote the same hit (bit for bit) or the same miss. -pub(crate) fn same_result(a: &[f32; 4], b: &[f32; 4]) -> bool { - a[2] == b[2] && (a[2] == 0.0 || (a[0].to_bits() == b[0].to_bits() && a[1].to_bits() == b[1].to_bits())) -} - -struct Batch { - capacity: u32, - queries: GpuBuffer, - results: GpuBuffer, - readback: GpuBuffer, - bind_group: GpuBindGroup, -} - -/// The device resources of one scene's ray tree. -pub struct Raycaster { - device: GpuDevice, - queue: GpuQueue, - pipeline: GpuComputePipeline, - params: GpuBuffer, - nodes: GpuBuffer, - walls: GpuBuffer, - batch: RefCell>, - in_flight: Cell, - pub node_count: u32, - pub wall_count: u32, - pub depth: u32, - magnitude: f32, -} - -/// The shader compiler's messages, one per line. -async fn compilation_messages(module: &GpuShaderModule) -> String { - let Ok(info) = JsFuture::from(module.get_compilation_info()).await else { - return String::new(); - }; - let info: GpuCompilationInfo = info.unchecked_into(); - info.messages() - .iter() - .map(|m| { - let m: GpuCompilationMessage = JsValue::from(m).unchecked_into(); - format!("\n rays.wgsl:{}:{}: {}", m.line_num(), m.line_pos(), m.message()) - }) - .collect() -} - -impl Raycaster { - /// Compiles the shader and uploads the tree of `arrays` (`RayTree::gpu_arrays`). - pub async fn create(device: GpuDevice, arrays: TreeArrays) -> Result { - let (nodes, walls, magnitude, depth) = arrays; - if depth > MAX_DEPTH { - return Err(error(format!( - "the BSP depth {depth} exceeds the shader's stack of {MAX_DEPTH}" - ))); - } - let rows: Vec<[u32; 12]> = nodes - .iter() - .map(|(l, o, s)| { - [ - l[0], - l[1], - l[2], - l[3], - o[0].to_bits(), - o[1].to_bits(), - o[2].to_bits(), - o[3].to_bits(), - s[0].to_bits(), - s[1].to_bits(), - s[2].to_bits(), - s[3].to_bits(), - ] - }) - .collect(); - let queue = device.queue(); - let shader = format!("{FLOAT_SHADER}\n{RAY_SHADER}"); - let module = device.create_shader_module(&GpuShaderModuleDescriptor::new(&shader)); - let stage = GpuProgrammableStage::new(&module); - stage.set_entry_point("main"); - // `layout: "auto"`: the string stands in for a pipeline layout object. - let auto: GpuPipelineLayout = JsValue::from_str("auto").unchecked_into(); - let pipeline = match JsFuture::from( - device.create_compute_pipeline_async(&GpuComputePipelineDescriptor::new(&auto, &stage)), - ) - .await - { - Ok(pipeline) => pipeline.unchecked_into::(), - Err(e) => { - return Err(error(format!( - "the ray shader did not compile: {}{}", - describe(e), - compilation_messages(&module).await - ))) - } - }; - let params = Self::create_buffer(&device, 32, usage::UNIFORM | usage::COPY_DST)?; - let wall_count = walls.len() as u32; - let nodes = Self::upload(&device, &queue, bytes(&rows))?; - let walls = Self::upload(&device, &queue, bytes(&walls))?; - Ok(Raycaster { - device, - queue, - pipeline, - params, - nodes, - walls, - batch: RefCell::new(None), - in_flight: Cell::new(false), - node_count: rows.len() as u32, - wall_count, - depth: depth as u32, - magnitude, - }) - } - - fn create_buffer(device: &GpuDevice, size: u32, usage: u32) -> Result { - device.create_buffer(&GpuBufferDescriptor::new(size.max(16).next_multiple_of(16), usage)) - } - - fn upload(device: &GpuDevice, queue: &GpuQueue, data: &[u8]) -> Result { - let buffer = Self::create_buffer(device, data.len() as u32, usage::STORAGE | usage::COPY_DST)?; - if !data.is_empty() { - queue.write_buffer_with_u32_and_u8_slice(&buffer, 0, data)?; - } - Ok(buffer) - } - - /// Query buffers for at least `count` queries, grown in powers of two. - fn ensure_batch(&self, count: u32) -> Result<(), JsValue> { - if self.batch.borrow().as_ref().is_some_and(|b| b.capacity >= count) { - return Ok(()); - } - let capacity = count.next_power_of_two().max(1024); - let size = capacity * 16; - let queries = Self::create_buffer(&self.device, size, usage::STORAGE | usage::COPY_DST)?; - let results = Self::create_buffer(&self.device, size, usage::STORAGE | usage::COPY_SRC)?; - let readback = Self::create_buffer(&self.device, size, usage::MAP_READ | usage::COPY_DST)?; - let entries: Vec = [&self.params, &self.nodes, &self.walls, &queries, &results] - .iter() - .enumerate() - .map(|(i, buffer)| GpuBindGroupEntry::new_with_gpu_buffer_binding(i as u32, &GpuBufferBinding::new(buffer))) - .collect(); - let bind_group = self.device.create_bind_group(&GpuBindGroupDescriptor::new( - &entries, - &self.pipeline.get_bind_group_layout(0), - )); - *self.batch.borrow_mut() = Some(Batch { - capacity, - queries, - results, - readback, - bind_group, - }); - Ok(()) - } - - /// Runs `op` over `input` (`[f32; 4]` per query; see rays.wgsl) and reads - /// the results back. Queries run one at a time per raycaster. - pub async fn query(&self, op: u32, input: &[[f32; 4]]) -> Result, JsValue> { - let count = input.len() as u32; - if count == 0 { - return Ok(Vec::new()); - } - if self.in_flight.replace(true) { - return Err(error("a query is in flight; await it first")); - } - let result = self.run(op, input, count).await; - self.in_flight.set(false); - result - } - - async fn run(&self, op: u32, input: &[[f32; 4]], count: u32) -> Result, JsValue> { - self.ensure_batch(count)?; - let size = count * 16; - let readback = { - let batch = self.batch.borrow(); - let batch = batch.as_ref().expect("batch buffers"); - let params = [self.node_count, count, op, 0, self.magnitude.to_bits(), 0, 0, 0]; - self.queue - .write_buffer_with_u32_and_u8_slice(&self.params, 0, bytes(¶ms))?; - self.queue - .write_buffer_with_u32_and_u8_slice(&batch.queries, 0, bytes(input))?; - let encoder = self.device.create_command_encoder(); - let pass = encoder.begin_compute_pass(); - pass.set_pipeline(&self.pipeline); - pass.set_bind_group(0, Some(&batch.bind_group)); - pass.dispatch_workgroups(count.div_ceil(WORKGROUP)); - pass.end(); - encoder.copy_buffer_to_buffer_with_u32_and_u32_and_u32(&batch.results, 0, &batch.readback, 0, size)?; - self.queue.submit(&[encoder.finish()]); - batch.readback.clone() - }; - JsFuture::from(readback.map_async_with_u32_and_u32(gpu_map_mode::READ, 0, size)).await?; - let range: ArrayBuffer = readback.get_mapped_range_with_u32_and_u32(0, size)?; - let floats = Float32Array::new(&range).to_vec(); - readback.unmap(); - Ok(floats.chunks_exact(4).map(|c| [c[0], c[1], c[2], c[3]]).collect()) - } -} - -/// `navigator.gpu` of the window or worker, if WebGPU is available. -fn navigator_gpu() -> Result { - let navigator: JsValue = match web_sys::window() { - Some(window) => window.navigator().into(), - None => js_sys::global() - .dyn_into::() - .map_err(|_| error("no window or worker scope"))? - .navigator() - .into(), - }; - let gpu = js_sys::Reflect::get(&navigator, &"gpu".into())?; - if gpu.is_undefined() || gpu.is_null() { - return Err(error("WebGPU is not available in this browser")); - } - Ok(gpu.unchecked_into()) -} - -/// Requests a WebGPU adapter and device: a promise of a `GPUDevice`. -#[wasm_bindgen(js_name = requestGpuDevice)] -pub fn request_gpu_device(require_hardware: Option) -> Promise { - future_to_promise(async move { - let gpu = navigator_gpu()?; - let adapter = JsFuture::from(gpu.request_adapter()).await?; - if adapter.is_null() || adapter.is_undefined() { - return Err(error("WebGPU: no adapter")); - } - let adapter: GpuAdapter = adapter.unchecked_into(); - if require_hardware.unwrap_or(false) { - let info = js_sys::Reflect::get(adapter.as_ref(), &JsValue::from_str("info"))?; - let fallback = js_sys::Reflect::get(&info, &JsValue::from_str("isFallbackAdapter"))? - .as_bool() - .unwrap_or(false); - if fallback { - return Err(error( - "WebGPU training needs a hardware adapter; the software adapter runs on the CPU", - )); - } - } - JsFuture::from(adapter.request_device()).await.map(JsValue::from) - }) -} - -/// A scene's BSP walls on a WebGPU device. -#[wasm_bindgen] -pub struct GpuRaycaster { - inner: Rc, -} - -fn flatten(results: &[[f32; 4]]) -> JsValue { - let flat: Vec = results.iter().flatten().copied().collect(); - Float32Array::from(&flat[..]).into() -} - -fn queries(values: &[f32]) -> Result, JsValue> { - if !values.len().is_multiple_of(4) { - return Err(error("queries take four values each")); - } - Ok(values.chunks_exact(4).map(|c| [c[0], c[1], c[2], c[3]]).collect()) -} - -#[wasm_bindgen] -impl GpuRaycaster { - /// Compiles the shader and uploads the simulation's track to `device` - /// (from `requestGpuDevice()` or the host's own `GPUDevice`); resolves to - /// the raycaster, or rejects with the shader diagnostics. - pub fn create(device: GpuDevice, simulation: &Simulation) -> Promise { - let arrays = { - let session = simulation.shared.session.borrow(); - session - .runner - .world - .track - .ray_tree() - .map(RayTree::gpu_arrays) - .ok_or_else(|| error("the scene has no BSP raycaster")) - }; - future_to_promise(async move { - let raycaster = Raycaster::create(device, arrays?).await?; - Ok(GpuRaycaster { - inner: Rc::new(raycaster), - } - .into()) - }) - } - - pub(crate) fn inner(&self) -> Rc { - self.inner.clone() - } - - #[wasm_bindgen(getter, js_name = nodeCount)] - pub fn node_count(&self) -> u32 { - self.inner.node_count - } - - #[wasm_bindgen(getter, js_name = wallCount)] - pub fn wall_count(&self) -> u32 { - self.inner.wall_count - } - - #[wasm_bindgen(getter)] - pub fn depth(&self) -> u32 { - self.inner.depth - } - - /// Casts rays `[startX, startY, endX, endY]...`; resolves to a - /// Float32Array of `[hitX, hitY, hit ? 1 : 0, 0]` per ray. - pub fn raycast(&self, rays: &[f32]) -> Promise { - let inner = self.inner.clone(); - let rays = queries(rays); - future_to_promise(async move { Ok(flatten(&inner.query(OP_RAYCAST, &rays?).await?)) }) - } - - /// The closest wall point of `[x, y, 0, 0]...`; resolves to - /// `[x, y, 1, 0]` per point. - #[wasm_bindgen(js_name = closestWall)] - pub fn closest_wall(&self, points: &[f32]) -> Promise { - let inner = self.inner.clone(); - let points = queries(points); - future_to_promise(async move { Ok(flatten(&inner.query(OP_CLOSEST_WALL, &points?).await?)) }) - } - - /// Compares the device with the CPU on `rays` random rays and `points` - /// random points over the track's bounds: resolves to - /// `{rays, rayMismatches, points, pointMismatches}`. - pub fn verify(&self, simulation: &Simulation, rays: u32, points: u32, seed: u32) -> Promise { - let inner = self.inner.clone(); - let shared = simulation.shared.clone(); - future_to_promise(async move { - let (ray_queries, point_queries, expected_rays, expected_points) = { - let session = shared.session.borrow(); - let track = &session.runner.world.track; - let tree = track.ray_tree().ok_or_else(|| error("no BSP raycaster"))?; - let bounds = session.track_bounds(); - let mut rng = Xorshift(seed.max(1)); - let ray_queries: Vec<[f32; 4]> = (0..rays) - .map(|_| { - let start = rng.point(&bounds); - let angle = rng.unit() * std::f64::consts::TAU; - let length = 50.0 + rng.unit() * 750.0; - let end = V2::new(start.x + angle.cos() * length, start.y + angle.sin() * length); - let (s, e) = (F2::from(start), F2::from(end)); - [s.x, s.y, e.x, e.y] - }) - .collect(); - let point_queries: Vec<[f32; 4]> = (0..points) - .map(|_| { - let p = F2::from(rng.point(&bounds)); - [p.x, p.y, 0.0, 0.0] - }) - .collect(); - let expected_rays: Vec<[f32; 4]> = ray_queries.iter().map(|q| cpu_raycast(tree, q)).collect(); - let expected_points: Vec<[f32; 4]> = point_queries - .iter() - .map(|q| match track.closest_wall(V2::new(q[0] as f64, q[1] as f64)) { - Some(p) => { - let p = F2::from(p); - [p.x, p.y, 1.0, 0.0] - } - None => [0.0; 4], - }) - .collect(); - (ray_queries, point_queries, expected_rays, expected_points) - }; - let got_rays = inner.query(OP_RAYCAST, &ray_queries).await?; - let got_points = inner.query(OP_CLOSEST_WALL, &point_queries).await?; - let ray_mismatches = got_rays - .iter() - .zip(&expected_rays) - .filter(|(a, b)| !same_result(a, b)) - .count(); - let point_mismatches = got_points - .iter() - .zip(&expected_points) - .filter(|(a, b)| !same_result(a, b)) - .count(); - let first_mismatch = |queries: &[[f32; 4]], got: &[[f32; 4]], expected: &[[f32; 4]]| { - got.iter().zip(expected).position(|(a, b)| !same_result(a, b)).map(|i| { - serde_json::json!({ - "index": i, "query": queries[i], "got": got[i], "expected": expected[i], - "gotBits": got[i].map(f32::to_bits), "expectedBits": expected[i].map(f32::to_bits), - }) - }) - }; - let report = serde_json::json!({ - "rays": rays, "rayMismatches": ray_mismatches, "points": points, "pointMismatches": point_mismatches, - "firstRayMismatch": first_mismatch(&ray_queries, &got_rays, &expected_rays), - "firstPointMismatch": first_mismatch(&point_queries, &got_points, &expected_points), - }); - js_sys::JSON::parse(&report.to_string()) - }) - } -} - -/// A small deterministic generator for `verify`. -struct Xorshift(u32); - -impl Xorshift { - fn next(&mut self) -> u32 { - let mut x = self.0; - x ^= x << 13; - x ^= x >> 17; - x ^= x << 5; - self.0 = x; - x - } - fn unit(&mut self) -> f64 { - self.next() as f64 / 4294967296.0 - } - fn point(&mut self, bounds: &[f64; 4]) -> V2 { - let pad = 64.0; - V2::new( - bounds[0] - pad + self.unit() * (bounds[2] - bounds[0] + 2.0 * pad), - bounds[1] - pad + self.unit() * (bounds[3] - bounds[1] + 2.0 * pad), - ) - } -} - -/// `TrainingRunner`'s ray window with the ray sensors served by `raycaster`: -/// per tick, the statistics and the queries run on the CPU, the rays on the -/// GPU, then inference and the physics step on the CPU. With -/// `time_limit_ticks`, the window is the rest of the generation. -pub(crate) fn advance_window( - shared: Rc, - raycaster: Rc, - ticks: u64, - stop_when_inactive: bool, - time_limit_ticks: Option, -) -> Promise { - future_to_promise(async move { - if shared.busy.replace(true) { - return Err(error("a WebGPU window is already in flight")); - } - let result = run_window(&shared, &raycaster, ticks, stop_when_inactive, time_limit_ticks).await; - shared.busy.set(false); - result.map(|executed| JsValue::from(executed as u32)) - }) -} - -async fn run_window( - shared: &Shared, - raycaster: &Raycaster, - ticks: u64, - stop_when_inactive: bool, - time_limit_ticks: Option, -) -> Result { - shared.revision.set(shared.revision.get().wrapping_add(1)); - let (mut window, verify_every) = { - let mut session = shared.session.borrow_mut(); - if !session.started() { - return Err(error("the session has not started")); - } - let (ticks, time_limit) = match time_limit_ticks { - Some(limit) => { - if session.runner.stats_phase != 0 { - return Err(error("advanceGeneration needs statistics phase 0")); - } - let (ticks, seconds) = session.runner.generation_window(limit); - (ticks, Some(seconds)) - } - None => (ticks, None), - }; - let window = session - .runner - .begin_ray_window(ticks, stop_when_inactive, time_limit) - .map_err(error)?; - (window, session.options.gpu_verify_every as usize) - }; - let mut queries = Vec::new(); - loop { - { - let mut session = shared.session.borrow_mut(); - if !session.runner.prepare_ray_tick(&mut window) { - break; - } - session.runner.ray_queries(&window, &mut queries); - } - let hits = raycaster.query(OP_RAYCAST, &queries).await?; - if verify_every > 0 { - let session = shared.session.borrow(); - let tree = session - .runner - .world - .track - .ray_tree() - .ok_or_else(|| error("no BSP raycaster"))?; - let (mut checked, mut mismatches) = (0, 0); - for (query, hit) in queries.iter().zip(&hits).step_by(verify_every) { - checked += 1; - if !same_result(&cpu_raycast(tree, query), hit) { - mismatches += 1; - } - } - shared.gpu_rays_checked.set(shared.gpu_rays_checked.get() + checked); - shared - .gpu_ray_mismatches - .set(shared.gpu_ray_mismatches.get() + mismatches); - } - shared - .session - .borrow_mut() - .runner - .finish_ray_tick(&window, &hits) - .map_err(error)?; - } - Ok(shared.session.borrow_mut().runner.end_ray_window(window)) -} diff --git a/github-simulator/source/src/wasm/math_profile.rs b/github-simulator/source/src/wasm/math_profile.rs deleted file mode 100644 index e8ea4af56ae09e0c08b327a0de485f4fbb168d25..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/wasm/math_profile.rs +++ /dev/null @@ -1,115 +0,0 @@ -//! The math profile of the browser's machine (crate::math::profile). -//! -//! The Windows build is a high-entropy User-Agent Client Hint, which only an -//! asynchronous call returns. Module initialization starts one cached query; -//! the web package awaits it before its asynchronous initializer resolves. -//! Synchronous hosts guess from the platform until the query has finished. - -use crate::math::profile::MathProfile; -use js_sys::{Array, Function, Promise, Reflect}; -use std::cell::RefCell; -use std::sync::atomic::{AtomicU32, Ordering}; -use wasm_bindgen::prelude::*; -use wasm_bindgen_futures::{future_to_promise, JsFuture}; - -/// What `detectMathProfile` found: 0 until it finished, else the profile's -/// index plus one. -static DETECTED: AtomicU32 = AtomicU32::new(0); - -thread_local! { - /// The promise is local to a JavaScript realm; the result above is shared - /// with Rayon workers in a threaded package. - static DETECTION: RefCell> = const { RefCell::new(None) }; -} - -fn get(object: &JsValue, key: &str) -> Option { - Reflect::get(object, &key.into()) - .ok() - .filter(|v| !v.is_undefined() && !v.is_null()) -} - -/// `navigator.userAgentData`, present in Chromium browsers and their workers. -fn user_agent_data() -> Option { - get(&get(&js_sys::global(), "navigator")?, "userAgentData") -} - -fn on_windows() -> bool { - if let Some(platform) = user_agent_data().and_then(|data| get(&data, "platform")?.as_string()) { - return platform == "Windows"; - } - let navigator = get(&js_sys::global(), "navigator"); - navigator - .and_then(|n| get(&n, "userAgent")?.as_string()) - .is_some_and(|agent| agent.contains("Windows")) -} - -/// The profile of a Windows `platformVersion` hint, whose major component is -/// the Windows.Foundation.UniversalApiContract version: 19 arrived with -/// Windows 11 24H2 (build 26100), 23H2 still reports 15. -fn for_platform_version(version: &str) -> Option { - let major: u32 = version.split('.').next()?.parse().ok()?; - Some(if major >= 19 { - MathProfile::Win11Fma3 - } else { - MathProfile::Win10Fma3 - }) -} - -/// The profile of sessions whose options name none. Windows without a known -/// version is taken for a current Windows 11. -pub(crate) fn browser_math_profile() -> MathProfile { - match DETECTED.load(Ordering::Relaxed) { - 0 if on_windows() => MathProfile::Win11Fma3, - 0 => MathProfile::Proton, - n => MathProfile::ALL[n as usize - 1], - } -} - -async fn platform_version() -> Option { - let data = user_agent_data()?; - let method: Function = get(&data, "getHighEntropyValues")?.dyn_into().ok()?; - let hints = Array::of1(&"platformVersion".into()); - let promise: Promise = method.call1(&data, &hints).ok()?.dyn_into().ok()?; - get(&JsFuture::from(promise).await.ok()?, "platformVersion")?.as_string() -} - -/// Detects the math profile of this machine, which later sessions without a -/// `mathProfile` option use, and resolves to its name. Initialization and -/// later calls share one promise. Browsers without User-Agent Client Hints -/// (Firefox, Safari) cannot tell Windows 10 from 11. -#[wasm_bindgen(js_name = detectMathProfile)] -pub fn detect_math_profile() -> Promise { - DETECTION.with(|cached| { - cached - .borrow_mut() - .get_or_insert_with(|| { - future_to_promise(async { - let profile = match DETECTED.load(Ordering::Relaxed) { - 0 if on_windows() => match platform_version().await { - Some(version) => for_platform_version(&version).unwrap_or(MathProfile::Win11Fma3), - None => MathProfile::Win11Fma3, - }, - 0 => MathProfile::Proton, - n => MathProfile::ALL[n as usize - 1], - }; - DETECTED.store(profile.index() + 1, Ordering::Relaxed); - Ok(profile.name().into()) - }) - }) - .clone() - }) -} - -/// The names of every math profile. -#[wasm_bindgen(js_name = mathProfiles)] -pub fn math_profiles() -> Vec { - MathProfile::ALL.iter().map(|p| p.name().to_owned()).collect() -} - -/// A `mathProfile` argument: a profile name, or undefined for the default. -pub(crate) fn math_profile_arg(name: Option) -> Result { - match name { - Some(name) => name.parse().map_err(|e: String| JsError::new(&e)), - None => Ok(browser_math_profile()), - } -} diff --git a/github-simulator/source/src/wasm/mod.rs b/github-simulator/source/src/wasm/mod.rs deleted file mode 100644 index d0798fd0d6423dc0fc6e75e7a7d3d7fe8fdffbb8..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/wasm/mod.rs +++ /dev/null @@ -1,550 +0,0 @@ -//! The WebAssembly library: wasm-bindgen exports of `session::Session` for -//! JavaScript, and the WebGPU raycaster in `gpu`. Build with -//! `wasm/build.sh` (see wasm/README.md). -//! -//! Every method borrows the session for its own duration only; a WebGPU -//! window in flight (`advanceWithGpuRays`) rejects further mutations until -//! its promise settles. Invalid scene or model files abort the module -//! through the library's assertions, which the console panic hook reports; -//! options, network exports, indices and shapes are validated and reported -//! as errors. - -use crate::training::session::{Session, SessionOptions}; -use crate::training::{CAR_STATE_FIELDS, CAR_STATE_STRIDE}; -use js_sys::{Float32Array, Float64Array, Promise}; -use std::cell::{Cell, RefCell, RefMut}; -use std::rc::Rc; -use wasm_bindgen::prelude::*; - -pub mod gpu; -pub mod math_profile; -pub mod sim; - -#[cfg(feature = "wasm-threads")] -pub use wasm_bindgen_rayon::init_thread_pool; - -/// Query only after initThreadPool has completed in a threaded package. -#[wasm_bindgen(js_name = cpuThreadCount)] -pub fn cpu_thread_count() -> usize { - #[cfg(feature = "wasm-threads")] - { - rayon::current_num_threads() - } - #[cfg(not(feature = "wasm-threads"))] - { - 1 - } -} - -/// Integration tests use a barrier to prove work executes on every pool worker. -#[cfg(feature = "wasm-thread-test")] -#[wasm_bindgen(js_name = testThreadIndices)] -pub fn test_thread_indices() -> Vec { - #[cfg(feature = "wasm-threads")] - { - use std::sync::{Barrier, Mutex}; - let count = rayon::current_num_threads(); - let barrier = Barrier::new(count); - let indices = Mutex::new(Vec::with_capacity(count)); - rayon::scope(|scope| { - for _ in 0..count { - let barrier = &barrier; - let indices = &indices; - scope.spawn(move |_| { - indices - .lock() - .unwrap() - .push(rayon::current_thread_index().unwrap() as u32); - barrier.wait(); - }); - } - }); - let mut indices = indices.into_inner().unwrap(); - indices.sort_unstable(); - indices - } - #[cfg(not(feature = "wasm-threads"))] - { - vec![0] - } -} - -#[wasm_bindgen(start)] -pub fn init() { - console_error_panic_hook::set_once(); - // initSync and non-web targets cannot wait for the Windows version hint; - // start it automatically. The web package's async initializer awaits the - // same cached promise before users can construct their first session. - let _ = math_profile::detect_math_profile(); -} - -/// The crate version. -#[wasm_bindgen] -pub fn version() -> String { - env!("CARGO_PKG_VERSION").to_owned() -} - -fn js_error(message: impl std::fmt::Display) -> JsError { - JsError::new(&message.to_string()) -} - -fn parse(text: &str, what: &str) -> Result { - serde_json::from_str(text).map_err(|e| js_error(format!("{what}: {e}"))) -} - -fn vehicle_template(text: &str) -> Result { - let template = parse(text, "vehicle template")?; - if !template["vehicle"].is_object() || !template["physics"].is_object() { - return Err(js_error("the vehicle template needs vehicle and physics sections")); - } - Ok(template) -} - -/// Converts a track the game saved (the JSON inside a `.track` file) into a -/// training scene for independent cars, with the vehicle and physics of -/// `templateJson`. Malformed or open tracks are reported as errors. The -/// optional `mathProfile` names the math the scene is built with (the -/// session's, which `detectMathProfile` defaults). -#[wasm_bindgen(js_name = trackScene)] -pub fn track_scene( - track_json: &str, - name: &str, - template_json: &str, - math_profile: Option, -) -> Result { - let math = math_profile::math_profile_arg(math_profile)?; - let saved = parse(track_json, "track file")?; - let template = vehicle_template(template_json)?; - let scene = crate::track::random_track::saved_track_scene(&saved, name, &template, math).map_err(js_error)?; - Ok(scene.to_string()) -} - -/// The random training track of `generation` (0 for the first) in a run with -/// `seed`: `settingsJson` holds `RandomTrainingTrackSettings`. The scene uses -/// the game's TileMap position and independent cars, and the optional -/// `mathProfile` as `trackScene` does. -#[wasm_bindgen(js_name = randomTrackScene)] -pub fn random_track_scene( - template_json: &str, - settings_json: &str, - seed: f64, - generation: u32, - math_profile: Option, -) -> Result { - let math = math_profile::math_profile_arg(math_profile)?; - use crate::track::training_tracks::{training_scene, RandomTrainingTrackSettings}; - let template = vehicle_template(template_json)?; - let settings: RandomTrainingTrackSettings = - serde_json::from_str(settings_json).map_err(|e| js_error(format!("random track settings: {e}")))?; - settings.validate().map_err(js_error)?; - if seed.fract() != 0.0 || seed.abs() > 9_007_199_254_740_991.0 { - return Err(js_error("the track seed must be a safe integer")); - } - let generator = match template["runtime"]["hashcode_seed"].as_u64() { - Some(hash) => Some(crate::track::random_track::TrackGenerator::new( - u32::try_from(hash).map_err(|_| js_error("HashCode seed exceeds u32"))?, - )), - None => None, - }; - let (_, mut scene) = training_scene( - &template, - &settings, - generator.as_ref(), - seed as i64, - generation as u64, - math, - ) - .map_err(js_error)?; - crate::track::random_track::place_tile_map(&mut scene, crate::track::random_track::GAME_TILE_MAP_POSITION); - Ok(scene.to_string()) -} - -fn to_js(value: &serde_json::Value) -> Result { - js_sys::JSON::parse(&value.to_string()).map_err(|e| js_error(format!("{e:?}"))) -} - -/// State shared between a `Simulation` handle and its in-flight WebGPU windows. -pub(crate) struct Shared { - pub session: RefCell, - /// A WebGPU window is between its promise's creation and settlement. - pub busy: Cell, - /// Changes made through the CPU session invalidate resident GPU state. - pub revision: Cell, - pub gpu_rays_checked: Cell, - pub gpu_ray_mismatches: Cell, -} - -/// A population of cars on one track: the `TrainingRunner` of the native -/// trainer, driven from JavaScript. -#[wasm_bindgen] -pub struct Simulation { - pub(crate) shared: Rc, -} - -#[wasm_bindgen] -impl Simulation { - /// Changes the next reproduction's settings, retaining the population's - /// current statistics and RNG. See `Session::set_evolution_settings`. - #[wasm_bindgen(js_name = setEvolutionSettings)] - pub fn set_evolution_settings(&self, json: &str) -> Result<(), JsError> { - self.session_mut()?.set_evolution_settings(json).map_err(js_error) - } - - /// `sceneJson`, `networkJson` (inputs, outputs, optional weights) and - /// `modelJson` are the files the CLI takes; `options` is an object or - /// JSON string of `SessionOptions` (see wasm/README.md). - #[wasm_bindgen(constructor)] - pub fn new( - scene_json: &str, - network_json: &str, - model_json: &str, - options: JsValue, - ) -> Result { - let options = if options.is_undefined() || options.is_null() { - SessionOptions::default() - } else if let Some(text) = options.as_string() { - SessionOptions::from_json(&text).map_err(js_error)? - } else { - let text = js_sys::JSON::stringify(&options).map_err(|e| js_error(format!("options: {e:?}")))?; - SessionOptions::from_json(&String::from(text)).map_err(js_error)? - }; - let session = Session::new( - &parse(scene_json, "scene")?, - &parse(network_json, "network")?, - &parse(model_json, "model")?, - options, - ) - .map_err(js_error)?; - Ok(Simulation { - shared: Rc::new(Shared { - session: RefCell::new(session), - busy: Cell::new(false), - revision: Cell::new(0), - gpu_rays_checked: Cell::new(0), - gpu_ray_mismatches: Cell::new(0), - }), - }) - } - - fn session(&self) -> std::cell::Ref<'_, Session> { - self.shared.session.borrow() - } - - fn session_mut(&self) -> Result, JsError> { - if self.shared.busy.get() { - return Err(js_error("a WebGPU window is in flight; await its promise first")); - } - self.shared.revision.set(self.shared.revision.get().wrapping_add(1)); - Ok(self.shared.session.borrow_mut()) - } - - /// Selects the track the next generation drives: call it after the last - /// window of a generation and before `nextGeneration`. The scene must use - /// the same vehicle. - #[wasm_bindgen(js_name = replaceTrack)] - pub fn replace_track(&self, scene_json: &str) -> Result<(), JsError> { - let scene = parse(scene_json, "scene")?; - self.session_mut()?.replace_track(&scene).map_err(js_error) - } - - /// Installs the first generation from the network export's weights, or - /// from a Xavier network of its shape. - pub fn start(&self) -> Result<(), JsError> { - self.session_mut()?.start().map_err(js_error) - } - - /// Installs the first generation from a Xavier network of `shape` - /// (inputs first), drawn from the session's seeded RNG. - #[wasm_bindgen(js_name = startWithShape)] - pub fn start_with_shape(&self, shape: &[u32]) -> Result<(), JsError> { - let shape: Vec = shape.iter().map(|&n| n as usize).collect(); - self.session_mut()?.start_with_shape(&shape).map_err(js_error) - } - - /// Advances up to `ticks` physics ticks (60 per second); with - /// `stopWhenInactive` the window ends once every car is inactive. - /// Returns the executed ticks. - pub fn advance(&self, ticks: u32, stop_when_inactive: bool) -> Result { - self.session_mut()? - .advance(ticks as u64, stop_when_inactive) - .map(|n| n as u32) - .map_err(js_error) - } - - /// Runs the rest of the generation under a time limit in ticks, as the - /// game does. Returns the executed ticks. - #[wasm_bindgen(js_name = advanceGeneration)] - pub fn advance_generation(&self, time_limit_ticks: u32) -> Result { - self.session_mut()? - .advance_generation(time_limit_ticks as u64) - .map(|n| n as u32) - .map_err(js_error) - } - - /// Reproduces and installs the next generation: - /// `{preservedCount, rewards: Float64Array}`. - #[wasm_bindgen(js_name = nextGeneration)] - pub fn next_generation(&self) -> Result { - let (preserved_count, rewards) = self.session_mut()?.next_generation().map_err(js_error)?; - let result = js_sys::Object::new(); - js_sys::Reflect::set( - &result, - &"preservedCount".into(), - &JsValue::from(preserved_count as u32), - ) - .map_err(|e| js_error(format!("{e:?}")))?; - js_sys::Reflect::set(&result, &"rewards".into(), &Float64Array::from(&rewards[..])) - .map_err(|e| js_error(format!("{e:?}")))?; - Ok(result.into()) - } - - /// `advance` with the ray sensors cast on the GPU (tick-major). Resolves - /// to the executed ticks. See `GpuRaycaster`. - #[wasm_bindgen(js_name = advanceWithGpuRays)] - pub fn advance_with_gpu_rays( - &self, - raycaster: &gpu::GpuRaycaster, - ticks: u32, - stop_when_inactive: bool, - ) -> Promise { - gpu::advance_window( - self.shared.clone(), - raycaster.inner(), - ticks as u64, - stop_when_inactive, - None, - ) - } - - /// `advanceGeneration` with the ray sensors cast on the GPU. - #[wasm_bindgen(js_name = advanceGenerationWithGpuRays)] - pub fn advance_generation_with_gpu_rays(&self, raycaster: &gpu::GpuRaycaster, time_limit_ticks: u32) -> Promise { - gpu::advance_window( - self.shared.clone(), - raycaster.inner(), - 0, - true, - Some(time_limit_ticks as u64), - ) - } - - /// Ray queries that were also cast on the CPU (`gpuVerifyEvery`). - #[wasm_bindgen(js_name = gpuRaysChecked)] - pub fn gpu_rays_checked(&self) -> f64 { - self.shared.gpu_rays_checked.get() as f64 - } - - /// Checked ray queries whose GPU result differed from the CPU raycast. - #[wasm_bindgen(js_name = gpuRayMismatches)] - pub fn gpu_ray_mismatches(&self) -> f64 { - self.shared.gpu_ray_mismatches.get() as f64 - } - - #[wasm_bindgen(getter)] - pub fn started(&self) -> bool { - self.session().started() - } - - #[wasm_bindgen(getter)] - pub fn generation(&self) -> u32 { - self.session().runner.generation as u32 - } - - #[wasm_bindgen(getter)] - pub fn tick(&self) -> u32 { - self.session().runner.tick as u32 - } - - #[wasm_bindgen(getter)] - pub fn population(&self) -> u32 { - self.session().runner.agents.len() as u32 - } - - /// Whether a WebGPU window is in flight. - #[wasm_bindgen(getter)] - pub fn busy(&self) -> bool { - self.shared.busy.get() - } - - /// The number of ray sensors per car. - #[wasm_bindgen(js_name = rayCount)] - pub fn ray_count(&self) -> u32 { - self.session().runner.layout.ray_count() as u32 - } - - /// `carStateStride()` values per car, `carStateFields()` order. - #[wasm_bindgen(js_name = carStates)] - pub fn car_states(&self) -> Float64Array { - Float64Array::from(&self.session().car_states()[..]) - } - - #[wasm_bindgen(js_name = carStateStride)] - pub fn car_state_stride() -> u32 { - CAR_STATE_STRIDE as u32 - } - - #[wasm_bindgen(js_name = carStateFields)] - pub fn car_state_fields() -> Vec { - CAR_STATE_FIELDS.iter().map(|&s| s.to_owned()).collect() - } - - /// The training metrics of a car (`metricNames()` order; NaN when unset). - pub fn metrics(&self, index: u32) -> Result { - self.session() - .metrics(index as usize) - .map(|m| Float64Array::from(&m[..])) - .map_err(js_error) - } - - #[wasm_bindgen(js_name = metricNames)] - pub fn metric_names() -> Vec { - Session::metric_names() - } - - /// The sensor inputs a car would read now (`sensorNames()` order). - pub fn sensors(&self, index: u32) -> Result { - self.session() - .sensors(index as usize) - .map(|s| Float64Array::from(&s[..])) - .map_err(js_error) - } - - #[wasm_bindgen(js_name = sensorNames)] - pub fn sensor_names(&self) -> Vec { - self.session().sensor_names().to_vec() - } - - /// `[acceleration, steering, brake, handbrake, boost]` of a car. - pub fn controls(&self, index: u32) -> Result { - self.session() - .controls(index as usize) - .map(|c| Float64Array::from(&c[..])) - .map_err(js_error) - } - - #[wasm_bindgen(js_name = outputNames)] - pub fn output_names(&self) -> Vec { - self.session().output_names().to_vec() - } - - /// A car's network as `{"shape", "weights", "biases"}` JSON. - #[wasm_bindgen(js_name = networkJson)] - pub fn network_json(&self, index: u32) -> Result { - self.session().network_json(index as usize).map_err(js_error) - } - - /// Replaces a car's network with an export of the same shape. - #[wasm_bindgen(js_name = setNetworkJson)] - pub fn set_network_json(&self, index: u32, json: &str) -> Result<(), JsError> { - self.session_mut()? - .set_network_json(index as usize, json) - .map_err(js_error) - } - - /// The best lap of the generation as `{index, time}`, or null. - #[wasm_bindgen(js_name = bestLap)] - pub fn best_lap(&self) -> Result { - match self.session().best_lap() { - Some((index, time)) => to_js(&serde_json::json!({"index": index, "time": time})), - None => Ok(JsValue::NULL), - } - } - - /// Statistics of the generation so far: `{bestIndex, bestScore, averageScore, - /// worstScore, lapped, active, lapIndex, lapTime}` (lap fields null without a lap). - #[wasm_bindgen(js_name = generationSummary)] - pub fn generation_summary(&self) -> Result { - let s = self.session().generation_summary(); - let result = js_sys::Object::new(); - let set = |key: &str, value: JsValue| { - js_sys::Reflect::set(&result, &key.into(), &value) - .map(|_| ()) - .map_err(|e| js_error(format!("{e:?}"))) - }; - let number = |v: Option| v.map_or(JsValue::NULL, JsValue::from); - set("bestIndex", JsValue::from(s.best_index as u32))?; - set("bestScore", JsValue::from(s.best_score))?; - set("averageScore", number(s.average_score))?; - set("worstScore", number(s.worst_score))?; - set("lapped", JsValue::from(s.lapped as u32))?; - set("active", JsValue::from(s.active as u32))?; - set("lapIndex", number(s.lap_index.map(|i| i as f64)))?; - set("lapTime", number(s.lap_time))?; - Ok(result.into()) - } - - /// Cars that still drive. - #[wasm_bindgen(getter, js_name = activeCount)] - pub fn active_count(&self) -> u32 { - self.session().active_count() as u32 - } - - /// The checkpoint of the current generation's start: the parents it was - /// bred from and the generator state (`Session::checkpoint_bytes`, - /// format 2), or after restoring a full checkpoint every network - /// (format 1). The session keeps that boundary, so hosts request the - /// bytes only when they save. - #[wasm_bindgen(js_name = checkpointBytes)] - pub fn checkpoint_bytes(&self) -> Result, JsError> { - self.session().checkpoint_bytes().map_err(js_error) - } - - /// Restores a `checkpointBytes` checkpoint of either format, breeding a - /// format 2 generation again. - #[wasm_bindgen(js_name = restoreCheckpointBytes)] - pub fn restore_checkpoint_bytes(&self, bytes: &[u8]) -> Result<(), JsError> { - self.session_mut()?.restore_checkpoint_bytes(bytes).map_err(js_error) - } - - /// The generation `checkpointBytes` saves. - #[wasm_bindgen(getter, js_name = checkpointGeneration)] - pub fn checkpoint_generation(&self) -> Option { - self.session().boundary().map(|(g, _)| g as u32) - } - - /// A checkpoint of the current state (generation, RNG, networks) as JSON. - /// Call it at a generation boundary; `checkpointBytes` is the compact form. - #[wasm_bindgen(js_name = checkpointJson)] - pub fn checkpoint_json(&self) -> Result { - self.session().checkpoint().map(|v| v.to_string()).map_err(js_error) - } - - /// Installs a checkpoint's networks as its generation and restores its RNG. - #[wasm_bindgen(js_name = restoreCheckpointJson)] - pub fn restore_checkpoint_json(&self, json: &str) -> Result<(), JsError> { - self.session_mut()? - .restore_checkpoint(&parse(json, "checkpoint")?) - .map_err(js_error) - } - - /// GameManager.OnPause: frozen cars keep their pose. - #[wasm_bindgen(js_name = setPaused)] - pub fn set_paused(&self, paused: bool) -> Result<(), JsError> { - self.session_mut()?.runner.set_paused(paused); - Ok(()) - } - - /// Wall segments `[x0, y0, x1, y1]...`. - #[wasm_bindgen(js_name = trackWalls)] - pub fn track_walls(&self) -> Float32Array { - Float32Array::from(&self.session().track_walls()[..]) - } - - /// The baked centre path `[x, y]...`. - #[wasm_bindgen(js_name = trackPath)] - pub fn track_path(&self) -> Float64Array { - Float64Array::from(&self.session().track_path()[..]) - } - - /// `[minX, minY, maxX, maxY]` of the walls. - #[wasm_bindgen(js_name = trackBounds)] - pub fn track_bounds(&self) -> Float64Array { - Float64Array::from(&self.session().track_bounds()[..]) - } - - /// `[x, y, rotation]` of the spawn pose. - pub fn spawn(&self) -> Float64Array { - let s = self.session().spawn(); - Float64Array::from(&[s.position[0], s.position[1], s.rotation][..]) - } -} diff --git a/github-simulator/source/src/wasm/rays.wgsl b/github-simulator/source/src/wasm/rays.wgsl deleted file mode 100644 index 0d8b532b9a9e143da1c9e885e7a663770ee071bc..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/wasm/rays.wgsl +++ /dev/null @@ -1,182 +0,0 @@ -// BSP wall queries of src/track/bsp.rs for WebGPU, matching gpu/sim/rays.h with -// the same descent and pop order, so a query visits the walls the CPU visits -// and keeps the first of equal hits. The arithmetic is written operation by -// operation as the CPU evaluates it. float.wgsl supplies rounding boundaries -// and integer binary32 division. The host prepends those helpers to this module. - -struct RayNode { - // first wall, one past the last wall, front child, back child (NONE = 0xffffffff) - links: vec4, - // (cx, cy, hx, hy): the node's own walls, and its whole subtree - own: vec4, - subtree: vec4, -} - -struct Params { - node_count: u32, - count: u32, - // 0: raycast (start, end) -> (hit, hit != 0, 0); 1: closest wall (point) -> (point, 1, 0) - op: u32, - pad: u32, - magnitude: f32, - pad1: f32, - pad2: f32, - pad3: f32, -} - -@group(0) @binding(0) var params: Params; -@group(0) @binding(1) var nodes: array; -// (start.x, start.y, delta.x, delta.y): each node's split, then its segments -@group(0) @binding(2) var walls: array>; -@group(0) @binding(3) var queries: array>; -@group(0) @binding(4) var results: array>; - -const NONE: u32 = 0xffffffffu; -const MAX_DEPTH: u32 = 48u; -const F32_MAX: f32 = 3.4028234663852886e38; - -// `Bounds::beside`: the box lies beyond `margin` on one side of the ray's line. -fn beside(bounds: vec4, start: vec2, b: vec2, margin: f32) -> bool { - let c = fp_sub(fp_mul(b.x, fp_sub(bounds.y, start.y)), fp_mul(b.y, fp_sub(bounds.x, start.x))); - return abs(c) > fp_add(fp_mul(abs(b.x), fp_add(bounds.w, margin)), fp_mul(abs(b.y), fp_add(bounds.z, margin))); -} - -// `classify`: the side of p relative to the wall (origin, direction). -fn classify(wall: vec4, p: vec2) -> i32 { - let c = fp_sub(fp_mul(wall.z, fp_sub(p.y, wall.y)), fp_mul(wall.w, fp_sub(p.x, wall.x))); - if (c > 0.0001) { return 1; } - if (c < -0.0001) { return -1; } - return 0; -} - -fn front_first(split: vec4, start: vec2, end: vec2) -> bool { - let a = classify(split, start); - return a == 1 || (a == 0 && classify(split, end) == 1); -} - -// `RayTree::raycast`: the near subtree, then the node's own walls, then the far -// subtree when the ray crosses the split. Tiny rays never skip a box -// (the CPU uses an infinite margin). -fn raycast(start: vec2, end: vec2) -> vec4 { - let b = vec2(fp_sub(end.x, start.x), fp_sub(end.y, start.y)); - let scale = fp_add(fp_add(params.magnitude, abs(start.x)), abs(start.y)); - let skip = fp_add(abs(b.x), abs(b.y)) > 1e-10; - let margin = fp_add(1e-3, fp_mul(2e-5, scale)); - var stack: array; - var depth: u32 = 0u; - var current: u32 = select(NONE, 0u, params.node_count != 0u); - var hit = vec2(0.0, 0.0); - var pops: u32 = 0u; - loop { - loop { - if (current == NONE || depth >= MAX_DEPTH) { break; } - let node = nodes[current]; - if (skip && beside(node.subtree, start, b, margin)) { break; } - let split = walls[node.links.x]; - let a = classify(split, start); - let e = classify(split, end); - let near_front = a == 1 || (a == 0 && e == 1); - // Low bit: the ray crosses the split, so the far side follows. - stack[depth] = (current << 1u) | select(0u, 1u, a != e); - depth = depth + 1u; - current = select(node.links.w, node.links.z, near_front); - } - if (depth == 0u || pops > params.node_count) { break; } - pops = pops + 1u; - depth = depth - 1u; - let entry = stack[depth]; - let node = nodes[entry >> 1u]; - if (!(skip && beside(node.own, start, b, margin))) { - var best_d2 = F32_MAX; - var found = false; - for (var i = node.links.x; i < node.links.y; i = i + 1u) { - let wall = walls[i]; - // `godot_math::ray_wall_hit` - let denominator = fp_sub(fp_mul(wall.z, b.y), fp_mul(b.x, wall.w)); - let dx = fp_sub(wall.x, start.x); - let dy = fp_sub(wall.y, start.y); - let ray_fraction = fp_div(fp_add(fp_mul(fp_sub(0.0, wall.w), dx), fp_mul(wall.z, dy)), denominator); - let wall_fraction = fp_div(fp_sub(fp_mul(b.x, dy), fp_mul(b.y, dx)), denominator); - let p = vec2(fp_add(wall.x, fp_mul(wall.z, wall_fraction)), fp_add(wall.y, fp_mul(wall.w, wall_fraction))); - let ok = !(abs(denominator) < 1e-6) && ray_fraction >= 0.0 && ray_fraction <= 1.0 - && wall_fraction >= 0.0 && wall_fraction <= 1.0; - let ddx = fp_sub(p.x, start.x); - let ddy = fp_sub(p.y, start.y); - let d2 = fp_add(fp_mul(ddx, ddx), fp_mul(ddy, ddy)); - let better = ok && d2 < best_d2; - best_d2 = select(best_d2, d2, better); - hit = select(hit, p, better); - found = found || better; - } - if (found) { return vec4(hit.x, hit.y, 1.0, 0.0); } - } - let split = walls[node.links.x]; - let far = select(node.links.z, node.links.w, front_first(split, start, end)); - current = select(NONE, far, (entry & 1u) == 1u); - } - // Keep the terminal return outside the loop for Naga's control-flow validation. - return vec4(0.0, 0.0, 0.0, 0.0); -} - -// `Node::closest`: own walls, then the near subtree, then the far subtree -// when the split line is closer than the best wall so far. -fn closest_wall(p: vec2) -> vec4 { - var best_d2 = F32_MAX; - var found = false; - var closest = vec2(0.0, 0.0); - var stack: array; - var depth: u32 = 0u; - var current: u32 = select(NONE, 0u, params.node_count != 0u); - var pops: u32 = 0u; - loop { - loop { - if (current == NONE || depth >= MAX_DEPTH) { break; } - let node = nodes[current]; - for (var i = node.links.x; i < node.links.y; i = i + 1u) { - let wall = walls[i]; - let len2 = fp_add(fp_mul(wall.z, wall.z), fp_mul(wall.w, wall.w)); - var t = fp_sub(fp_add(fp_mul(p.x, wall.z), fp_mul(p.y, wall.w)), fp_add(fp_mul(wall.x, wall.z), fp_mul(wall.y, wall.w))); - t = select(t, 0.0, t < 0.0); - t = select(t, len2, t > len2); - t = fp_div(t, len2); - var candidate = vec2(fp_add(wall.x, fp_mul(wall.z, t)), fp_add(wall.y, fp_mul(wall.w, t))); - candidate = select(candidate, vec2(wall.x, wall.y), len2 == 0.0); - let dx = fp_sub(p.x, candidate.x); - let dy = fp_sub(p.y, candidate.y); - let d2 = fp_add(fp_mul(dx, dx), fp_mul(dy, dy)); - let better = !found || d2 < best_d2; - best_d2 = select(best_d2, d2, better); - closest = select(closest, candidate, better); - found = true; - } - stack[depth] = current; - depth = depth + 1u; - current = select(node.links.w, node.links.z, classify(walls[node.links.x], p) >= 0); - } - if (depth == 0u || pops > params.node_count) { break; } - pops = pops + 1u; - depth = depth - 1u; - let node = nodes[stack[depth]]; - let split = walls[node.links.x]; - let t = fp_div(fp_add(fp_mul(fp_sub(p.x, split.x), split.z), fp_mul(fp_sub(p.y, split.y), split.w)), - fp_add(fp_mul(split.z, split.z), fp_mul(split.w, split.w))); - let dx = fp_sub(p.x, fp_add(split.x, fp_mul(split.z, t))); - let dy = fp_sub(p.y, fp_add(split.y, fp_mul(split.w, t))); - let far_side = fp_add(fp_mul(dx, dx), fp_mul(dy, dy)) < best_d2; - let far = select(node.links.z, node.links.w, classify(split, p) >= 0); - current = select(NONE, far, far_side); - } - return vec4(closest.x, closest.y, 1.0, 0.0); -} - -@compute @workgroup_size(64) -fn main(@builtin(global_invocation_id) id: vec3) { - let i = id.x; - if (i >= params.count) { return; } - let q = queries[i]; - if (params.op == 0u) { - results[i] = raycast(q.xy, q.zw); - } else { - results[i] = closest_wall(q.xy); - } -} diff --git a/github-simulator/source/src/wasm/sim/f64.wgsl b/github-simulator/source/src/wasm/sim/f64.wgsl deleted file mode 100644 index be65a6f8c3dc85936a17bb0251ccf10d0a962922..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/wasm/sim/f64.wgsl +++ /dev/null @@ -1,683 +0,0 @@ -// IEEE 754 binary64 in integer arithmetic, correctly rounded to nearest, -// ties to even: WebGPU has no f64. `F64` holds the bits (x: low word, y: high -// word). Addition, subtraction and multiplication give the results of Berkeley -// SoftFloat 3e (addMagsF64, subMagsF64, f64_mul, roundPackToF64), but select -// between the cases instead of branching, since one thread runs per car and -// lanes that disagree run both sides; wasm/test/f64-reference.wgsl keeps the -// SoftFloat structure that wasm/test/exact.js compares them with. Division -// divides the significands bit by bit with a sticky remainder. NaNs propagate -// as on x86: the first NaN operand, quieted; invalid operations give the x86 -// default NaN. Subnormals are exact. -// -// Double-float ("f64 polyfill") libraries keep a value as an unevaluated sum -// of two f32 and are not correctly rounded, so they cannot reproduce the CPU -// bit for bit; they also lose the binary64 exponent range. - -alias F64 = vec2; -alias U64 = vec2; - -const F64_SIGN: u32 = 0x80000000u; -const F64_ZERO: F64 = F64(0u, 0u); -const F64_ONE: F64 = F64(0u, 0x3ff00000u); -const F64_DEFAULT_NAN: F64 = F64(0u, 0xfff80000u); - -// ---- 64-bit integers --------------------------------------------------- - -fn u64_add(a: U64, b: U64) -> U64 { - let lo = a.x + b.x; - return U64(lo, a.y + b.y + select(0u, 1u, lo < a.x)); -} -fn u64_sub(a: U64, b: U64) -> U64 { - return U64(a.x - b.x, a.y - b.y - select(0u, 1u, a.x < b.x)); -} -fn u64_lt(a: U64, b: U64) -> bool { return a.y < b.y || (a.y == b.y && a.x < b.x); } -fn u64_le(a: U64, b: U64) -> bool { return a.y < b.y || (a.y == b.y && a.x <= b.x); } -fn u64_eq(a: U64, b: U64) -> bool { return a.x == b.x && a.y == b.y; } -fn u64_nonzero(a: U64) -> bool { return (a.x | a.y) != 0u; } -// Shifts by counts that may differ between lanes select rather than branch, so -// a wave never runs several cases in turn. `(v << 1u) << (31u - s)` is -// v << (32 - s) for s in 1..31 and 0 for s = 0. -fn u64_shl(a: U64, n: u32) -> U64 { - let s = n & 31u; - let lo = a.x << s; - let hi = (a.y << s) | ((a.x >> 1u) >> (31u - s)); - return select(select(U64(lo, hi), U64(0u, lo), n >= 32u), U64(0u, 0u), n >= 64u); -} -fn u64_shr(a: U64, n: u32) -> U64 { - let s = n & 31u; - let lo = (a.x >> s) | ((a.y << 1u) << (31u - s)); - let hi = a.y >> s; - return select(select(U64(lo, hi), U64(hi, 0u), n >= 32u), U64(0u, 0u), n >= 64u); -} -// SoftFloat shiftRightJam64: any bit shifted out sets bit 0. -fn u64_shr_jam(a: U64, n: u32) -> U64 { - let z = u64_shr(a, n); - let lost = select(!u64_eq(u64_shl(z, n), a), u64_nonzero(a), n >= 64u); - return U64(z.x | select(0u, 1u, lost), z.y); -} -// countLeadingZeros from the binary32 exponent of x (or of x >> 8 from 2^24), -// converted exactly: Tint lowers the builtin to a ~20 instruction dependent -// polyfill, this takes a handful. Zero gives 158, clamped to 32. -fn clz32(x: u32) -> u32 { - let big = x >= 0x1000000u; - let v = select(x, x >> 8u, big); - return min(select(158u, 150u, big) - (bitcast(f32(v)) >> 23u), 32u); -} -fn u64_clz(a: U64) -> u32 { - let high = a.y != 0u; - return clz32(select(a.x, a.y, high)) + select(32u, 0u, high); -} -fn u64_from_u32(v: u32) -> U64 { return U64(v, 0u); } -fn u64_inc(a: U64) -> U64 { return u64_add(a, U64(1u, 0u)); } -// 32 x 32 -> 64 from 16-bit halves. -fn mul32(a: u32, b: u32) -> U64 { - let a0 = a & 0xffffu; - let a1 = a >> 16u; - let b0 = b & 0xffffu; - let b1 = b >> 16u; - let p00 = a0 * b0; - let p01 = a0 * b1; - let p10 = a1 * b0; - let p11 = a1 * b1; - let mid = p01 + p10; - let mid_carry = select(0u, 0x10000u, mid < p01); - let lo = p00 + (mid << 16u); - return U64(lo, p11 + (mid >> 16u) + mid_carry + select(0u, 1u, lo < p00)); -} -// The low 64 bits of a product. -fn u64_mul(a: U64, b: U64) -> U64 { - let p = mul32(a.x, b.x); - return U64(p.x, p.y + a.x * b.y + a.y * b.x); -} -// The 128-bit product as (w0, w1, w2, w3), least significant first. -fn u64_mul_wide(a: U64, b: U64) -> vec4 { - let p00 = mul32(a.x, b.x); - let p01 = mul32(a.x, b.y); - let p10 = mul32(a.y, b.x); - let p11 = mul32(a.y, b.y); - let mid = u64_add(p01, p10); - let mid_carry = select(0u, 1u, u64_lt(mid, p01)); - let w1 = p00.y + mid.x; - let carry = select(0u, 1u, w1 < p00.y); - let hi = u64_add(u64_add(p11, U64(mid.y, mid_carry)), U64(carry, 0u)); - return vec4(p00.x, w1, hi.x, hi.y); -} -// Unsigned quotient and remainder by a divisor below 2^16. -fn u64_div_small(a: U64, d: u32) -> U64 { - let h1 = (a.y >> 16u); - let q1 = h1 / d; - let r1 = h1 % d; - let h2 = (r1 << 16u) | (a.y & 0xffffu); - let q2 = h2 / d; - let r2 = h2 % d; - let h3 = (r2 << 16u) | (a.x >> 16u); - let q3 = h3 / d; - let r3 = h3 % d; - let h4 = (r3 << 16u) | (a.x & 0xffffu); - let q4 = h4 / d; - return U64((q3 << 16u) | q4, (q1 << 16u) | q2); -} - -// ---- classification ---------------------------------------------------- - -fn f64_exp(a: F64) -> i32 { return i32((a.y >> 20u) & 0x7ffu); } -fn f64_frac(a: F64) -> U64 { return U64(a.x, a.y & 0xfffffu); } -fn f64_signbit(a: F64) -> bool { return (a.y & F64_SIGN) != 0u; } -fn f64_isnan(a: F64) -> bool { - let h = a.y & 0x7fffffffu; - return h > 0x7ff00000u || (h == 0x7ff00000u && a.x != 0u); -} -fn f64_isinf(a: F64) -> bool { return (a.y & 0x7fffffffu) == 0x7ff00000u && a.x == 0u; } -fn f64_isfinite(a: F64) -> bool { return (a.y & 0x7ff00000u) != 0x7ff00000u; } -fn f64_neg(a: F64) -> F64 { return F64(a.x, a.y ^ F64_SIGN); } -fn f64_abs(a: F64) -> F64 { return F64(a.x, a.y & 0x7fffffffu); } -fn f64_inf(sign: bool) -> F64 { return F64(0u, select(0u, F64_SIGN, sign) | 0x7ff00000u); } -fn f64_signed_zero(sign: bool) -> F64 { return F64(0u, select(0u, F64_SIGN, sign)); } -fn f64_quiet(a: F64) -> F64 { return F64(a.x, a.y | 0x80000u); } -fn f64_propagate_nan(a: F64, b: F64) -> F64 { return f64_quiet(select(b, a, f64_isnan(a))); } -// Copies the sign of `s` onto `a`. -fn f64_copysign(a: F64, s: F64) -> F64 { return F64(a.x, (a.y & 0x7fffffffu) | (s.y & F64_SIGN)); } - -// ---- rounding and packing (SoftFloat conventions) ----------------------- -// `exp` is the biased exponent minus one; `sig` has its leading bit at 62. - -fn f64_pack(sign: bool, exp: i32, sig: U64) -> F64 { - return u64_add(U64(0u, select(0u, F64_SIGN, sign) | (u32(exp) << 20u)), sig); -} -fn f64_round_pack(sign: bool, exp_in: i32, sig_in: U64) -> F64 { - var exp = exp_in; - var sig = sig_in; - var round_bits = sig.x & 0x3ffu; - if (exp < 0 || exp >= 0x7fd) { - if (exp < 0) { - sig = u64_shr_jam(sig, u32(-exp)); - exp = 0; - round_bits = sig.x & 0x3ffu; - } else if (exp > 0x7fd || !u64_lt(u64_add(sig, U64(0x200u, 0u)), U64(0u, 0x80000000u))) { - return f64_inf(sign); - } - } - sig = u64_shr(u64_add(sig, U64(0x200u, 0u)), 10u); - if (round_bits == 0x200u) { sig.x = sig.x & 0xfffffffeu; } - if (!u64_nonzero(sig)) { exp = 0; } - return f64_pack(sign, exp, sig); -} -fn f64_norm_round_pack(sign: bool, exp: i32, sig: U64) -> F64 { - let shift = i32(u64_clz(sig)) - 1; - let e = exp - shift; - if (shift >= 10 && e >= 0 && e < 0x7fd) { - return f64_pack(sign, select(0, e, u64_nonzero(sig)), u64_shl(sig, u32(shift - 10))); - } - return f64_round_pack(sign, e, u64_shl(sig, u32(shift))); -} -// Normalizes a nonzero subnormal fraction: (exponent, significand with bit 52). -fn f64_norm_subnormal(frac: U64) -> vec3 { - let shift = i32(u64_clz(frac)) - 11; - let sig = u64_shl(frac, u32(shift)); - return vec3(u32(1 - shift), sig.x, sig.y); -} - -// ---- arithmetic ---------------------------------------------------------- - -// a + b with b's sign flipped by `flip` (F64_SIGN for subtraction). Lanes of -// a wave see different signs, exponent gaps and cancellation, and branching -// on those ran every case in turn (three times the uniform cost), so every -// finite case takes one path that selects instead. The larger magnitude `x` -// and the smaller `y` get their significands at bit 61 (subnormals have -// exponent 1 and no hidden bit); y is shifted right by the exponent gap with -// a sticky bit. The sum or difference is exact above its sticky bit, which -// lies at least 7 bits below the rounding position after normalizing -// (cancellation beyond one bit only happens when the gap is at most 1 and -// nothing was shifted out), so rounding it once is correctly rounded. The -// result cannot underflow inexactly; overflow gives infinity. -fn f64_add_signed(a: F64, b: F64, flip: u32) -> F64 { - let bs = F64(b.x, b.y ^ flip); - let swap = u64_lt(f64_abs(a), f64_abs(bs)); - let x = select(a, bs, swap); - let y = select(bs, a, swap); - let subtract = ((a.y ^ bs.y) & F64_SIGN) != 0u; - let ex_raw = f64_exp(x); - let ey_raw = f64_exp(y); - if (ex_raw == 0x7ff) { - // NaN operands propagate (b unflipped), inf - inf is invalid, - // otherwise the infinite operand with its effective sign. - let inf = select(x, F64_DEFAULT_NAN, subtract && ey_raw == 0x7ff); - return select(inf, f64_propagate_nan(a, b), f64_isnan(a) || f64_isnan(b)); - } - let ex = max(ex_raw, 1); - let mx = U64(x.x, (x.y & 0xfffffu) | select(0x100000u, 0u, ex_raw == 0)); - let my = U64(y.x, (y.y & 0xfffffu) | select(0x100000u, 0u, ey_raw == 0)); - let sx = U64(mx.x << 9u, (mx.y << 9u) | (mx.x >> 23u)); - let sy = u64_shr_jam(U64(my.x << 9u, (my.y << 9u) | (my.x >> 23u)), u32(ex - max(ey_raw, 1))); - let z = select(u64_add(sx, sy), u64_sub(sx, sy), subtract); - // Leading bit to 62, as far as the exponent allows (subnormal results), - // then round to nearest even at bit 10. - let shift = min(u64_clz(z) - 1u, u32(ex)); - let n = u64_shl(z, shift); - let rounded = u64_add(n, U64(0x200u, 0u)); - let sig = U64(((rounded.x >> 10u) | (rounded.y << 22u)) & select(0xffffffffu, 0xfffffffeu, (n.x & 0x3ffu) == 0x200u), rounded.y >> 10u); - // An exact zero is +0 for a difference and keeps the operands' sign for - // a sum; the leading bit of `sig` carries into the exponent field. - let nonzero = u64_nonzero(z); - let sign = (x.y & F64_SIGN) != 0u && (nonzero || !subtract); - let exp = select(0, ex - i32(shift), nonzero); - let finite = u64_add(U64(0u, select(0u, F64_SIGN, sign) | (u32(exp) << 20u)), sig); - return select(finite, f64_inf(sign), exp + i32(sig.y >> 20u) >= 0x7ff); -} -fn f64_add(a: F64, b: F64) -> F64 { return f64_add_signed(a, b, 0u); } -fn f64_sub(a: F64, b: F64) -> F64 { return f64_add_signed(a, b, F64_SIGN); } -// Normal operands whose product is normal before rounding and cannot round -// to infinity take a straight path; zeros, subnormals, infinities, NaNs and -// results near the ends of the range branch to SoftFloat's general code, -// which lanes rarely need. -fn f64_mul(a: F64, b: F64) -> F64 { - let ea = f64_exp(a); - let eb = f64_exp(b); - let ez = ea + eb - 0x3ff; - if (ea == 0 || eb == 0 || ea == 0x7ff || eb == 0x7ff || ez < 1 || ez > 0x7fc) { return f64_mul_general(a, b); } - let sa = U64(a.x << 10u, (((a.y & 0xfffffu) | 0x100000u) << 10u) | (a.x >> 22u)); - let sb = U64(b.x << 11u, (((b.y & 0xfffffu) | 0x100000u) << 11u) | (b.x >> 21u)); - let p = u64_mul_wide(sa, sb); - // The product's leading bit is at 125 or 126; keep the top 64 bits with - // a sticky bit and move the leading bit to 62. - let low = p.w < 0x40000000u; - let top = U64(p.z | select(0u, 1u, (p.x | p.y) != 0u), p.w); - let n = select(top, U64(top.x << 1u, (top.y << 1u) | (top.x >> 31u)), low); - let rounded = u64_add(n, U64(0x200u, 0u)); - let sig = U64(((rounded.x >> 10u) | (rounded.y << 22u)) & select(0xffffffffu, 0xfffffffeu, (n.x & 0x3ffu) == 0x200u), rounded.y >> 10u); - let exp = u32(ez - select(0, 1, low)); - return u64_add(U64(0u, ((a.y ^ b.y) & F64_SIGN) | (exp << 20u)), sig); -} -fn f64_mul_general(a: F64, b: F64) -> F64 { - let sign = ((a.y ^ b.y) & F64_SIGN) != 0u; - var ea = f64_exp(a); - var eb = f64_exp(b); - var sa = f64_frac(a); - var sb = f64_frac(b); - if (ea == 0x7ff) { - if (u64_nonzero(sa) || (eb == 0x7ff && u64_nonzero(sb))) { return f64_propagate_nan(a, b); } - if (eb == 0 && !u64_nonzero(sb)) { return F64_DEFAULT_NAN; } - return f64_inf(sign); - } - if (eb == 0x7ff) { - if (u64_nonzero(sb)) { return f64_propagate_nan(a, b); } - if (ea == 0 && !u64_nonzero(sa)) { return F64_DEFAULT_NAN; } - return f64_inf(sign); - } - if (ea == 0) { - if (!u64_nonzero(sa)) { return f64_signed_zero(sign); } - let n = f64_norm_subnormal(sa); - ea = i32(n.x); - sa = n.yz; - } - if (eb == 0) { - if (!u64_nonzero(sb)) { return f64_signed_zero(sign); } - let n = f64_norm_subnormal(sb); - eb = i32(n.x); - sb = n.yz; - } - var ez = ea + eb - 0x3ff; - sa = u64_shl(U64(sa.x, sa.y | 0x100000u), 10u); - sb = u64_shl(U64(sb.x, sb.y | 0x100000u), 11u); - let p = u64_mul_wide(sa, sb); - var sz = U64(p.z | select(0u, 1u, (p.x | p.y) != 0u), p.w); - if (u64_lt(sz, U64(0u, 0x40000000u))) { - ez = ez - 1; - sz = u64_shl(sz, 1u); - } - return f64_round_pack(sign, ez, sz); -} -// One digit of f64_div: floor(r * 2^18 / sb) for r < sb < 2^53, and the new -// remainder, as (digit, remainder low, remainder high, 1), or w = 0 if the f32 -// estimate (inv = 2^18 / sb) was further off than two steps either way. The -// remainder is computed modulo 2^64, and exactly when its top word beyond 64 -// bits matches its sign. -fn f64_div_digit(r: U64, sb: U64, inv: f32) -> vec4 { - var digit = min(u32((f32(r.y) * 4294967296.0 + f32(r.x)) * inv), 0x7ffffu); - let low = mul32(digit, sb.x); - let high = mul32(digit, sb.y); - let mid = low.y + high.x; - let top = high.y + select(0u, 1u, mid < low.y); - let shifted = u64_shl(r, 18u); - let product = U64(low.x, mid); - var remainder = u64_sub(shifted, product); - let remainder_top = (r.y >> 14u) - top - select(0u, 1u, u64_lt(shifted, product)); - var fits = remainder_top == select(0u, 0xffffffffu, i64_is_negative(remainder)); - for (var i = 0u; i < 2u; i = i + 1u) { - let low_digit = i64_is_negative(remainder); - digit = select(digit, digit - 1u, low_digit); - remainder = select(remainder, u64_add(remainder, sb), low_digit); - } - for (var i = 0u; i < 2u; i = i + 1u) { - let high_digit = !i64_is_negative(remainder) && !u64_lt(remainder, sb); - digit = select(digit, digit + 1u, high_digit); - remainder = select(remainder, u64_sub(remainder, sb), high_digit); - } - fits = fits && !i64_is_negative(remainder) && u64_lt(remainder, sb); - return vec4(digit, remainder.x, remainder.y, select(0u, 1u, fits)); -} -fn f64_div(a: F64, b: F64) -> F64 { - let sign = ((a.y ^ b.y) & F64_SIGN) != 0u; - var ea = f64_exp(a); - var eb = f64_exp(b); - var sa = f64_frac(a); - var sb = f64_frac(b); - if (ea == 0x7ff) { - if (u64_nonzero(sa)) { return f64_propagate_nan(a, b); } - if (eb == 0x7ff) { - if (u64_nonzero(sb)) { return f64_propagate_nan(a, b); } - return F64_DEFAULT_NAN; - } - return f64_inf(sign); - } - if (eb == 0x7ff) { - if (u64_nonzero(sb)) { return f64_propagate_nan(a, b); } - return f64_signed_zero(sign); - } - if (eb == 0) { - if (!u64_nonzero(sb)) { - if (ea == 0 && !u64_nonzero(sa)) { return F64_DEFAULT_NAN; } - return f64_inf(sign); - } - let n = f64_norm_subnormal(sb); - eb = i32(n.x); - sb = n.yz; - } - if (ea == 0) { - if (!u64_nonzero(sa)) { return f64_signed_zero(sign); } - let n = f64_norm_subnormal(sa); - ea = i32(n.x); - sa = n.yz; - } - var ez = ea - eb + 0x3fe; - sa = U64(sa.x, sa.y | 0x100000u); - sb = U64(sb.x, sb.y | 0x100000u); - if (u64_lt(sa, sb)) { - ez = ez - 1; - sa = u64_add(sa, sa); - } - // sa / sb lies in [1, 2): 55 quotient bits, then the remainder as sticky. - // The leading bit is 1; three 18-bit digits of long division follow, each - // estimated from an f32 reciprocal and corrected with its exact remainder. - let inv = 262144.0 / (f32(sb.y) * 4294967296.0 + f32(sb.x)); - var r = u64_sub(sa, sb); - var q = U64(1u, 0u); - var exact = true; - for (var i = 0u; i < 3u; i = i + 1u) { - let digit = f64_div_digit(r, sb, inv); - q = U64((q.x << 18u) | digit.x, (q.y << 18u) | (q.x >> 14u)); - r = digit.yz; - exact = exact && digit.w != 0u; - } - // A device whose f32 division is further off than the correction steps - // reach divides bit by bit. - if (!exact) { - r = sa; - q = U64(0u, 0u); - for (var i = 0u; i < 55u; i = i + 1u) { - q = u64_add(q, q); - if (!u64_lt(r, sb)) { - r = u64_sub(r, sb); - q.x = q.x | 1u; - } - r = u64_add(r, r); - } - } - let sz = u64_shl(q, 8u); - return f64_round_pack(sign, ez, U64(sz.x | select(0u, 1u, u64_nonzero(r)), sz.y)); -} - -// ---- fused multiply-add ---------------------------------------------------- - -struct U128 { - lo: U64, - hi: U64, -} - -fn u128_add(a: U128, b: U128) -> U128 { - let lo = u64_add(a.lo, b.lo); - return U128(lo, u64_add(u64_add(a.hi, b.hi), U64(select(0u, 1u, u64_lt(lo, a.lo)), 0u))); -} -fn u128_sub(a: U128, b: U128) -> U128 { - return U128(u64_sub(a.lo, b.lo), u64_sub(u64_sub(a.hi, b.hi), U64(select(0u, 1u, u64_lt(a.lo, b.lo)), 0u))); -} -fn u128_lt(a: U128, b: U128) -> bool { return u64_lt(a.hi, b.hi) || (u64_eq(a.hi, b.hi) && u64_lt(a.lo, b.lo)); } -fn u128_clz(a: U128) -> u32 { return select(u64_clz(a.hi), 64u + u64_clz(a.lo), !u64_nonzero(a.hi)); } -// Counts that wrap below zero are u64 shifts of 64 or more, which give zero. -fn u128_shl(a: U128, n: u32) -> U128 { - return U128(u64_shl(a.lo, n), u64_shl(a.hi, n) | u64_shr(a.lo, 64u - n) | u64_shl(a.lo, n - 64u)); -} -fn u128_shr_jam(a: U128, n: u32) -> U128 { - let z = U128(u64_shr(a.lo, n) | u64_shl(a.hi, 64u - n) | u64_shr(a.hi, n - 64u), u64_shr(a.hi, n)); - let back = u128_shl(z, n); - let lost = !u64_eq(back.lo, a.lo) || !u64_eq(back.hi, a.hi); - return U128(U64(z.lo.x | select(0u, 1u, lost), z.lo.y), z.hi); -} -// a * b + c, rounded once (C fma, SoftFloat's f64_mulAdd). NaN operands -// propagate in the order a, b, c; an infinite product with a zero factor, or -// one added to the opposite infinity, gives the default NaN. The callers are -// the Windows math kernels, which branch on uniform data far more than here. -fn f64_fma(a: F64, b: F64, c: F64) -> F64 { - let sign_p = ((a.y ^ b.y) & F64_SIGN) != 0u; - let sign_c = f64_signbit(c); - var ea = f64_exp(a); - var eb = f64_exp(b); - var ec = f64_exp(c); - if (ea == 0x7ff || eb == 0x7ff || ec == 0x7ff) { - if (f64_isnan(a) || f64_isnan(b) || f64_isnan(c)) { - return f64_quiet(select(select(c, b, f64_isnan(b)), a, f64_isnan(a))); - } - if (ea == 0x7ff || eb == 0x7ff) { - if (f64_is_zero(a) || f64_is_zero(b) || (ec == 0x7ff && sign_c != sign_p)) { return F64_DEFAULT_NAN; } - return f64_inf(sign_p); - } - return c; - } - // An exact zero product adds as a signed zero; a zero c leaves the - // product, rounded once. - if (f64_is_zero(a) || f64_is_zero(b)) { return f64_add(f64_signed_zero(sign_p), c); } - if (f64_is_zero(c)) { return f64_mul(a, b); } - var ma = f64_frac(a); - var mb = f64_frac(b); - var mc = f64_frac(c); - if (ea == 0) { - let n = f64_norm_subnormal(ma); - ea = i32(n.x); - ma = n.yz; - } - if (eb == 0) { - let n = f64_norm_subnormal(mb); - eb = i32(n.x); - mb = n.yz; - } - if (ec == 0) { - let n = f64_norm_subnormal(mc); - ec = i32(n.x); - mc = n.yz; - } - // Both terms as 128-bit significands with the leading bit at 125, worth - // sig * 2^(e - 1148): the product exactly, c shifted up by 73. - let p = u64_mul_wide(u64_shl(U64(ma.x, ma.y | 0x100000u), 10u), u64_shl(U64(mb.x, mb.y | 0x100000u), 10u)); - var wp = U128(p.xy, p.zw); - var e = ea + eb - 1022; - if (wp.hi.y < 0x20000000u) { - wp = u128_shl(wp, 1u); - e = e - 1; - } - var wc = U128(U64(0u, 0u), u64_shl(U64(mc.x, mc.y | 0x100000u), 9u)); - // Align the smaller exponent with a sticky bit. Gaps of at most one shift - // out only zeros (both terms end in zero bits), so a difference with - // massive cancellation is exact; beyond that the sticky bit lies far below - // the rounding position. - let d = e - ec; - if (d >= 0) { - wc = u128_shr_jam(wc, u32(d)); - } else { - wp = u128_shr_jam(wp, u32(-d)); - e = ec; - } - var w: U128; - var sign = sign_p; - if (sign_p == sign_c) { - w = u128_add(wp, wc); - } else if (u128_lt(wp, wc)) { - w = u128_sub(wc, wp); - sign = sign_c; - } else { - w = u128_sub(wp, wc); - } - if (!u64_nonzero(w.lo) && !u64_nonzero(w.hi)) { return F64_ZERO; } - // Leading bit to 126: the top word then holds it at 62 for rounding. - let shift = u128_clz(w) - 1u; - w = u128_shl(w, shift); - e = e - i32(shift); - return f64_round_pack(sign, e, U64(w.hi.x | select(0u, 1u, u64_nonzero(w.lo)), w.hi.y)); -} - -// ---- comparisons (false for NaN operands) --------------------------------- - -fn f64_both_zero(a: F64, b: F64) -> bool { return ((a.x | b.x) | ((a.y | b.y) & 0x7fffffffu)) == 0u; } -fn f64_lt(a: F64, b: F64) -> bool { - if (f64_isnan(a) || f64_isnan(b)) { return false; } - let sa = f64_signbit(a); - if (sa != f64_signbit(b)) { return sa && !f64_both_zero(a, b); } - return !u64_eq(a, b) && (sa != u64_lt(a, b)); -} -fn f64_le(a: F64, b: F64) -> bool { - if (f64_isnan(a) || f64_isnan(b)) { return false; } - let sa = f64_signbit(a); - if (sa != f64_signbit(b)) { return sa || f64_both_zero(a, b); } - return u64_eq(a, b) || (sa != u64_lt(a, b)); -} -fn f64_gt(a: F64, b: F64) -> bool { return f64_lt(b, a); } -fn f64_ge(a: F64, b: F64) -> bool { return f64_le(b, a); } -fn f64_eq(a: F64, b: F64) -> bool { - if (f64_isnan(a) || f64_isnan(b)) { return false; } - return u64_eq(a, b) || f64_both_zero(a, b); -} -fn f64_is_zero(a: F64) -> bool { return (a.x | (a.y & 0x7fffffffu)) == 0u; } - -// ---- conversions --------------------------------------------------------- - -fn f64_from_f32(f: f32) -> F64 { - let u = bitcast(f); - let sign = u & 0x80000000u; - let e = (u >> 23u) & 0xffu; - var frac = u & 0x7fffffu; - if (e == 0xffu) { - if (frac != 0u) { return F64(frac << 29u, sign | 0x7ff80000u | (frac >> 3u)); } - return F64(0u, sign | 0x7ff00000u); - } - if (e == 0u) { - if (frac == 0u) { return F64(0u, sign); } - let shift = clz32(frac) - 8u; - frac = frac << shift; - // The significand's leading bit adds one to the exponent field. - return F64(frac << 29u, (sign | ((0x380u - shift) << 20u)) + (frac >> 3u)); - } - return F64(frac << 29u, sign | ((e + 0x380u) << 20u) | (frac >> 3u)); -} -// SoftFloat roundPackToF32: `sig` has its leading bit at 30. -fn f32_round_pack(sign: u32, exp_in: i32, sig_in: u32) -> f32 { - var exp = exp_in; - var sig = sig_in; - var round_bits = sig & 0x7fu; - if (exp < 0 || exp >= 0xfd) { - if (exp < 0) { - let d = u32(-exp); - if (d < 31u) { - sig = (sig >> d) | select(0u, 1u, (sig << (32u - d)) != 0u); - } else { - sig = select(0u, 1u, sig != 0u); - } - exp = 0; - round_bits = sig & 0x7fu; - } else if (exp > 0xfd || sig + 0x40u >= 0x80000000u) { - return bitcast(sign | 0x7f800000u); - } - } - sig = (sig + 0x40u) >> 7u; - if (round_bits == 0x40u) { sig = sig & 0xfffffffeu; } - if (sig == 0u) { exp = 0; } - return bitcast(sign + (u32(exp) << 23u) + sig); -} -fn f64_to_f32(a: F64) -> f32 { - let sign = a.y & F64_SIGN; - let e = f64_exp(a); - let fh = a.y & 0xfffffu; - let fl = a.x; - if (e == 0x7ff) { - if ((fh | fl) != 0u) { return bitcast(sign | 0x7fc00000u | (fh << 3u) | (fl >> 29u)); } - return bitcast(sign | 0x7f800000u); - } - let frac = (fh << 10u) | (fl >> 22u) | select(0u, 1u, (fl & 0x3fffffu) != 0u); - if ((u32(e) | frac) == 0u) { return bitcast(sign); } - return f32_round_pack(sign, e - 0x381, frac | 0x40000000u); -} -fn f64_from_u32(v: u32) -> F64 { - if (v == 0u) { return F64_ZERO; } - let shift = clz32(v) + 21u; - return f64_pack(false, 0x432 - i32(shift), u64_shl(U64(v, 0u), shift)); -} -fn f64_from_i32(v: i32) -> F64 { - let negative = v < 0; - let r = f64_from_u32(select(u32(v), 0u - u32(v), negative)); - return F64(r.x, r.y | select(0u, F64_SIGN, negative)); -} -fn f64_from_u64(a: U64) -> F64 { - if (!u64_nonzero(a)) { return F64_ZERO; } - if ((a.y & 0x80000000u) != 0u) { return f64_round_pack(false, 0x43d, u64_shr_jam(a, 1u)); } - return f64_norm_round_pack(false, 0x43c, a); -} -// Two's complement 64-bit integers. -fn i64_neg(a: U64) -> U64 { return u64_sub(U64(0u, 0u), a); } -fn i64_is_negative(a: U64) -> bool { return (a.y & 0x80000000u) != 0u; } -fn i64_lt(a: U64, b: U64) -> bool { - return select(u64_lt(a, b), i64_is_negative(a), i64_is_negative(a) != i64_is_negative(b)); -} -fn i64_from_i32(v: i32) -> U64 { return U64(u32(v), select(0u, 0xffffffffu, v < 0)); } -fn f64_from_i64(a: U64) -> F64 { - let negative = i64_is_negative(a); - let m = select(a, i64_neg(a), negative); - if (!u64_nonzero(m)) { return F64_ZERO; } - if ((m.y & 0x80000000u) != 0u) { return f64_round_pack(negative, 0x43d, u64_shr_jam(m, 1u)); } - return f64_norm_round_pack(negative, 0x43c, m); -} -// Rust `as i64`: truncation, saturating, NaN to 0. -fn f64_to_i64(a: F64) -> U64 { - if (f64_isnan(a)) { return U64(0u, 0u); } - let e = f64_exp(a); - let negative = f64_signbit(a); - if (e < 0x3ff) { return U64(0u, 0u); } - if (e >= 0x43e) { return select(U64(0xffffffffu, 0x7fffffffu), U64(0u, 0x80000000u), negative); } - let sig = U64(a.x, (a.y & 0xfffffu) | 0x100000u); - var m: U64; - if (e >= 0x433) { m = u64_shl(sig, u32(e - 0x433)); } else { m = u64_shr(sig, u32(0x433 - e)); } - return select(m, i64_neg(m), negative); -} -// Rust `as i32`: truncation, saturating, NaN to 0. -fn f64_to_i32(a: F64) -> i32 { - if (f64_isnan(a)) { return 0; } - let e = f64_exp(a); - let negative = f64_signbit(a); - if (e < 0x3ff) { return 0; } - if (e >= 0x41e) { return select(2147483647, -2147483647 - 1, negative); } - let m = u64_shr(U64(a.x, (a.y & 0xfffffu) | 0x100000u), u32(0x433 - e)).x; - return select(i32(m), -i32(m), negative); -} -// Rust `as u64` of a nonnegative double below 2^64 (truncation). -fn f64_to_u64(a: F64) -> U64 { - let e = f64_exp(a); - if (f64_signbit(a) || e < 0x3ff) { return U64(0u, 0u); } - let sig = U64(a.x, (a.y & 0xfffffu) | 0x100000u); - if (e >= 0x433) { return u64_shl(sig, u32(e - 0x433)); } - return u64_shr(sig, u32(0x433 - e)); -} - -// ---- rounding to integers (SoftFloat f64_roundToInt) ------------------- - -fn f64_round_int(a: F64, ties_away: bool) -> F64 { - let e = f64_exp(a); - if (e <= 0x3fe) { - if (f64_is_zero(a)) { return a; } - var z = F64(0u, a.y & F64_SIGN); - let half_or_more = e == 0x3fe; - if (half_or_more && (ties_away || u64_nonzero(f64_frac(a)))) { z.y = z.y | 0x3ff00000u; } - return z; - } - if (e >= 0x433) { - if (e == 0x7ff && u64_nonzero(f64_frac(a))) { return f64_quiet(a); } - return a; - } - let last = u64_shl(U64(1u, 0u), u32(0x433 - e)); - let round_mask = u64_sub(last, U64(1u, 0u)); - var z = u64_add(a, u64_shr(last, 1u)); - if (!ties_away && !u64_nonzero(U64(z.x & round_mask.x, z.y & round_mask.y))) { - z = U64(z.x & ~last.x, z.y & ~last.y); - } - return U64(z.x & ~round_mask.x, z.y & ~round_mask.y); -} -// rint: nearest, ties to even. -fn f64_rint(a: F64) -> F64 { return f64_round_int(a, false); } -// round: nearest, ties away from zero. -fn f64_round(a: F64) -> F64 { return f64_round_int(a, true); } -fn f64_trunc(a: F64) -> F64 { - let e = f64_exp(a); - if (e < 0x3ff) { return F64(0u, a.y & F64_SIGN); } - if (e >= 0x433) { - if (e == 0x7ff && u64_nonzero(f64_frac(a))) { return f64_quiet(a); } - return a; - } - let mask = u64_sub(u64_shl(U64(1u, 0u), u32(0x433 - e)), U64(1u, 0u)); - return U64(a.x & ~mask.x, a.y & ~mask.y); -} -fn f64_floor(a: F64) -> F64 { - let t = f64_trunc(a); - if (f64_signbit(a) && !f64_eq(t, a) && !f64_isnan(a)) { return f64_sub(t, F64_ONE); } - return t; -} -fn f64_ceil(a: F64) -> F64 { - let t = f64_trunc(a); - if (!f64_signbit(a) && !f64_eq(t, a) && !f64_isnan(a)) { return f64_add(t, F64_ONE); } - return t; -} diff --git a/github-simulator/source/src/wasm/sim/generated.wgsl b/github-simulator/source/src/wasm/sim/generated.wgsl deleted file mode 100644 index d9cd04e4e5bbd711437c198ddd7422098b6360e8..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/wasm/sim/generated.wgsl +++ /dev/null @@ -1,4558 +0,0 @@ -// Generated from gpu/sim by wasm/gen-sim.py. Do not edit this file. - -struct Contact { - normal: vec2, - point: vec2, - wall_point: vec2, - local_point: vec2, - wall_local_point: vec2, - depth: f32, - shape: u32, - flags: u32, - normal_mass: f32, - tangent_mass: f32, - bias: f32, - bounce: f32, - friction: f32, - acc_normal: f32, - acc_tangent: f32, - acc_bias: f32, - acc_bias_center: f32, -} - -struct Car { - position: vec2, - velocity: vec2, - acceleration: vec2, - reset_acceleration: vec2, - basis_x: vec2, - basis_y: vec2, - rotation: f32, - transform_angle: f32, - angular_velocity: f32, - boost_energy: f32, - flags: u32, - collision_count: u32, - tick: U64, - wheel_angle: array, - wheel_previous: array, 4>, - wheel_surface: array, - wheel_has_previous: u32, - pair_count: u32, - contact_count: u32, - error: u32, - shape_position: vec2, - shape_size: vec2, - leaf_min: vec2, - leaf_max: vec2, - pairs: array, - axes: array, 16>, - contacts: array, -} - -struct Agent { - controls: array, - previous_offset: F64, - tracker_score: F64, - lap_crossing_fraction: F64, - lap_count: U64, - network_novelty: F64, - update_count: U64, - collision_count: U64, - total_score: F64, - total_speed: F64, - total_abs_steering: F64, - total_actual_distance: F64, - total_drifting_amount: F64, - total_momentum_change_frequency: F64, - total_throttle_change_frequency: F64, - total_braking_frequency: F64, - total_distance_from_wall: F64, - total_distance_from_center: F64, - best_lap_time: F64, - idle_ticks: U64, - previous_speed: F64, - previous_throttle: F64, - previous_score: F64, - previous_lap_count: U64, - lap_start_tick: U64, - drift_ticks: U64, - current_slip_angle_degrees: F64, - continuous_drift_time: F64, - max_continuous_drift_time: F64, - all_laps_time_sum: F64, - recent: array, - recent_start: u32, - recent_len: u32, - deactivated_at: U64, - flags: u32, - pad: u32, -} - -struct WheelDesc { - position: vec2, - steering: u32, - handbrake_off: u32, - power: f32, - brake_power: f32, - handbrake_power: f32, - brake_max: f32, - max_angle_deg: F64, -} - -struct VehicleDesc { - wheels: array, - wheel_count: u32, - custom_integrator: u32, - center_steering: u32, - solver_iterations: u32, - max_contacts_reported: u32, - pad0: u32, - steering_speed: F64, - max_velocity: F64, - dt: F64, - contact_max_separation: F64, - grip: f32, - air_resistance: f32, - mass: f32, - inertia: f32, - linear_damp: f32, - angular_damp: f32, - dt_f: f32, - max_velocity_f: f32, - gravity: vec2, - shape_basis_x: vec2, - shape_basis_y: vec2, - shape_size: vec2, - shape_half: vec2, - shape_position: vec2, - center_of_mass: vec2, - friction: f32, - bounce: f32, - contact_bias: f32, - allowed_penetration: f32, - recycle_radius: f32, - max_separation_f: f32, - pad1: f32, - pad2: f32, -} - -struct SensorDesc { - kind: u32, - a: f32, - b: f32, - offset: vec2, - pad: u32, -} - -struct RayNode { - first: u32, - last: u32, - front: u32, - back: u32, - own: vec4, - subtree: vec4, -} - -struct SurfaceDev { - grip: f32, - power: f32, - steering: f32, - pad: f32, -} - -struct TileCell { - surface: u32, - connected: u32, - first: vec2, - last: vec2, -} - -struct PathSeg { - x: f32, - y: f32, - dx: f32, - dy: f32, - len2: f32, - pad0: f32, - pad1: f32, - pad2: f32, -} - -struct ShapeDev { - origin: vec2, - friction: f32, - bounce: f32, - min_x: F64, - min_y: F64, - max_x: F64, - max_y: F64, - first: u32, - count: u32, - wall_first: u32, - pad: u32, -} - -struct NearGrid { - x0: F64, - y0: F64, - cell: F64, - nx: u32, - ny: u32, - start: u32, - items: u32, -} - -struct World { - ray_node_count: u32, - ray_wall_count: u32, - ray_depth: u32, - ray_magnitude: f32, - ray_nodes: u32, - ray_walls: u32, - surface_count: u32, - default_surface: u32, - surfaces: u32, - tile_x0: i32, - tile_y0: i32, - tile_nx: i32, - tile_ny: i32, - tiles: u32, - path_points: u32, - math_profile: u32, - path_segments: u32, - path_offsets: u32, - path_grid: NearGrid, - curve_points: u32, - curve_length: f32, - curve_position: u32, - curve_offsets: u32, - curve_forwards: u32, - curve_grid: NearGrid, - shape_count: u32, - shape_point_count: u32, - shapes: u32, - shape_local: u32, - shape_normals: u32, - shape_grid: NearGrid, - shape_margin: F64, - path_xy: u32, -} - -struct PathHit { - offset: F64, - near: vec2, - tangent: vec2, -} - -struct Sensors { - count: u32, - needs_path: u32, - s: array, -} - -struct ReadMask { - batch_size: u32, - batch_index: u32, - batches_per_tick: u32, - require_active: u32, -} - -struct Controls { - acceleration: F64, - steering: F64, - brake: F64, - handbrake: F64, - boost: F64, -} - -struct Support { - p: array, 2>, - n: u32, -} - -struct CarFrame { - bx: vec2, - by: vec2, - basis_x: vec2, - basis_y: vec2, - local: array, 4>, - axis_x: vec2, - axis_y: vec2, -} - -struct PairFrame { - origin: vec2, - car_offset: vec2, - wall_offset: vec2, - car: array, 4>, - wall_first: bool, -} - -struct SolveResult { - velocity: vec2, - bias_velocity: vec2, - angular: f32, - bias_angular: f32, - collided: bool, -} - -struct StepCarry { - old_velocity: vec2, - velocity: vec2, - previous_velocity: vec2, - angular: f32, - previous_angular: f32, - was_active: u32, - base: u32, -} - -var sim_world: World; - -var sim_vehicle: VehicleDesc; - -var sim_car: Car; - -var sim_agent: Agent; - -const MAX_WHEELS: i32 = 4i; - -const MAX_PAIRS: i32 = 16i; - -const MAX_CONTACTS: i32 = 32i; - -const MAX_SHAPE_POINTS: i32 = 16i; - -const MAX_SENSORS: i32 = 32i; - -const MAX_OUTPUTS: i32 = 8i; - -const MAX_LAYERS: i32 = 12i; - -const MAX_WIDTH: i32 = 32i; - -const RECENT: i32 = 10i; - -const ERR_PAIRS: u32 = 8u; - -const ERR_CONTACTS: u32 = 16u; - -const ERR_PATH_NAN: u32 = 32u; - -const CONTACT_WALL_FIRST: u32 = 1u; - -const CONTACT_USED: u32 = 2u; - -const CAR_ACTIVE: u32 = 1u; - -const CAR_RESET_RIGHT: u32 = 2u; - -const CAR_PENDING_VELOCITY: u32 = 4u; - -const CAR_HAS_SHAPE: u32 = 8u; - -const CAR_HAS_LEAF: u32 = 16u; - -const CAR_PAIR_CHECK: u32 = 32u; - -const AGENT_HAS_PREVIOUS_OFFSET: u32 = 1u; - -const AGENT_WENT_BACKWARDS: u32 = 2u; - -const AGENT_HAS_LAP_CROSSING: u32 = 4u; - -const AGENT_PENDING_CONTACT: u32 = 8u; - -const AGENT_HAS_BEST_LAP: u32 = 16u; - -const AGENT_HAS_ALL_LAPS: u32 = 32u; - -const AGENT_HAS_DEACTIVATED: u32 = 64u; - -const S_RAYCAST: u32 = 0u; - -const S_DISTANCE_FROM_WALL: u32 = 1u; - -const S_SPEED: u32 = 2u; - -const S_VELOCITY_FRONT: u32 = 3u; - -const S_VELOCITY_SIDE: u32 = 4u; - -const S_ACCELERATION_FRONT: u32 = 5u; - -const S_ACCELERATION_SIDE: u32 = 6u; - -const S_ANGULAR_VELOCITY: u32 = 7u; - -const S_WHEEL_ANGLE: u32 = 8u; - -const S_BOOST_CAPACITY: u32 = 9u; - -const S_GRIP: u32 = 10u; - -const S_CORRECT_DIRECTION: u32 = 11u; - -const S_TRACK_CURVATURE: u32 = 12u; - -const NONE: u32 = 4294967295u; - -const MAX_RAY_DEPTH: i32 = 48i; - -const ENGINE_EXCEPTION_COUNT: i32 = 0i; - -const ENGINE_EXCEPTION_STEP: i32 = 0i; - -const ERR_MANAGED_LARGE: u32 = 1u; - -const ERR_NATIVE_LARGE: u32 = 2u; - -const ERR_ENGINE_DOMAIN: u32 = 4u; - -const MATH_PROTON: u32 = 0u; - -const MATH_WIN10_FMA3: u32 = 1u; - -const MATH_WIN11_FMA3: u32 = 2u; - -const INV_LN2_N: F64 = U64(0x652b82feu, 0x40671547u); - -const NEG_LN2_HI_N: F64 = U64(0xfefa0000u, 0xbf762e42u); - -const NEG_LN2_LO_N: F64 = U64(0xbc9e3b3au, 0xbd0cf79au); - -const EXP_POLY: array = array(U64(0xfffffdbdu, 0x3fdfffffu), U64(0x5555543cu, 0x3fc55555u), U64(0xcf172b91u, 0x3fa55555u), U64(0x67a4d017u, 0x3f811111u)); - -const EXP_TAB: array = array(U64(0x00000000u, 0x00000000u), U64(0x00000000u, 0x3ff00000u), U64(0x1a88bf6eu, 0x3c9b3b4fu), U64(0xa9fb3335u, 0x3feff63du), U64(0x9cd8dc5du, 0xbc716013u), U64(0x3e778061u, 0x3fefec9au), U64(0x108766d1u, 0xbc905e7au), U64(0xe86e7f85u, 0x3fefe315u), U64(0x3567f613u, 0x3c8cd252u), U64(0xd3158574u, 0x3fefd9b0u), U64(0x23f98efau, 0xbc8bce80u), U64(0x29ddf6deu, 0x3fefd06bu), U64(0x61e6c861u, 0x3c60f74eu), U64(0x18759bc8u, 0x3fefc745u), U64(0x5b33d399u, 0x3c90a3e4u), U64(0xcac6f383u, 0x3fefbe3eu), U64(0x5d837b6du, 0x3c979aa6u), U64(0x6cf9890fu, 0x3fefb558u), U64(0x92fdeffcu, 0x3c8eb51au), U64(0x2b7247f7u, 0x3fefac92u), U64(0x702f9cd1u, 0x3c3ebe3du), U64(0x32d3d1a2u, 0x3fefa3ecu), U64(0x89906e0bu, 0xbc6a0334u), U64(0xaffed31bu, 0x3fef9b66u), U64(0x2a2fbd0eu, 0xbc955652u), U64(0xd0125b51u, 0x3fef9301u), U64(0x8c4eea55u, 0xbc5080efu), U64(0xc06c31ccu, 0x3fef8abdu), U64(0xb9d5f416u, 0xbc91c923u), U64(0xaea92de0u, 0x3fef829au), U64(0xe95c55afu, 0x3c80d3e3u), U64(0xc8a58e51u, 0x3fef7a98u), U64(0xeaa59348u, 0xbc801b15u), U64(0x3c7d517bu, 0x3fef72b8u), U64(0x55de323du, 0xbc8f1ff0u), U64(0x388c8deau, 0x3fef6af9u), U64(0x3f1353bfu, 0x3c8b898cu), U64(0xeb6fcb75u, 0x3fef635bu), U64(0x7611eb26u, 0xbc96d99cu), U64(0x84045cd4u, 0x3fef5be0u), U64(0x3e3a2f60u, 0x3c9aecf7u), U64(0x3168b9aau, 0x3fef5487u), U64(0xcb86389du, 0xbc8fe782u), U64(0x22fcd91du, 0x3fef4d50u), U64(0x44a6c38du, 0x3c8a6f41u), U64(0x88628cd6u, 0x3fef463bu), U64(0xb0e4047du, 0x3c807a05u), U64(0x917ddc96u, 0x3fef3f49u), U64(0xe3a8a894u, 0x3c968efdu), U64(0x6e756238u, 0x3fef387au), U64(0xf274487du, 0x3c875e18u), U64(0x4fb2a63fu, 0x3fef31ceu), U64(0x981fe7f2u, 0x3c80472bu), U64(0x65e27cddu, 0x3fef2b45u), U64(0x3f71085eu, 0xbc96b87bu), U64(0xe1f56381u, 0x3fef24dfu), U64(0x6d09ab31u, 0x3c82f7e1u), U64(0xf51fdee1u, 0x3fef1e9du), U64(0x1a6fbffau, 0xbc3d219bu), U64(0xd0dad990u, 0x3fef187fu), U64(0x720c0ab4u, 0x3c8b3782u), U64(0xa6e4030bu, 0x3fef1285u), U64(0x89cecb8fu, 0x3c6e1492u), U64(0xa93e2f56u, 0x3fef0cafu), U64(0x4db0abb6u, 0x3c834d75u), U64(0x0a31b715u, 0x3fef06feu), U64(0xe2ac744cu, 0x3c864201u), U64(0xfc4cd831u, 0x3fef0170u), U64(0x5dd3f84au, 0x3c8fdd39u), U64(0xb26416ffu, 0x3feefc08u), U64(0x3b8e5b04u, 0xbc86a380u), U64(0x5f929ff1u, 0x3feef6c5u), U64(0xcc4b5068u, 0xbc924aedu), U64(0x373aa9cbu, 0x3feef1a7u), U64(0x1b512d8eu, 0xbc9907f8u), U64(0x6d05d866u, 0x3feeecaeu), U64(0x3e9436d2u, 0xbc71d1e8u), U64(0x34e59ff7u, 0x3feee7dbu), U64(0xb3ce1b15u, 0xbc991919u), U64(0xc313a8e5u, 0x3feee32du), U64(0xa72a4c6du, 0x3c859f48u), U64(0x4c123422u, 0x3feedea6u), U64(0x7a28698au, 0xbc931260u), U64(0x04ac801cu, 0x3feeda45u), U64(0x4817895bu, 0xbc58a78fu), U64(0x21f72e2au, 0x3feed60au), U64(0x67499a1bu, 0xbc7c2c9bu), U64(0xd950a897u, 0x3feed1f5u), U64(0x60c2ac11u, 0x3c4363edu), U64(0x6061892du, 0x3feece08u), U64(0x3b0664efu, 0x3c966609u), U64(0xed1d0057u, 0x3feeca41u), U64(0xdaa10379u, 0x3c6ecce1u), U64(0xb5c13cd0u, 0x3feec6a2u), U64(0x3f0f1230u, 0x3c93ff8eu), U64(0xf0d7d3deu, 0x3feec32au), U64(0xbb7aafb0u, 0x3c7690ceu), U64(0xd5362a27u, 0x3feebfdau), U64(0xeb54e077u, 0x3c931dbdu), U64(0x99fddd0du, 0x3feebcb2u), U64(0x0071a38eu, 0xbc8f9434u), U64(0x769d2ca7u, 0x3feeb9b2u), U64(0xdc93a349u, 0xbc87deccu), U64(0xa2cf6642u, 0x3feeb6dau), U64(0xbd0f385fu, 0xbc78dec6u), U64(0x569d4f82u, 0x3feeb42bu), U64(0xec7b5cf6u, 0xbc861246u), U64(0xca5d920fu, 0x3feeb1a4u), U64(0x18fdd78eu, 0x3c933505u), U64(0x36b527dau, 0x3feeaf47u), U64(0x2f8a9b05u, 0x3c7b98b7u), U64(0xd497c7fdu, 0x3feead12u), U64(0xe21c5409u, 0x3c9063e1u), U64(0xdd485429u, 0x3feeab07u), U64(0x5019c6eau, 0x3c34c785u), U64(0x8a5946b7u, 0x3feea926u), U64(0x2b64c035u, 0x3c9432e6u), U64(0x15ad2148u, 0x3feea76fu), U64(0x6199769fu, 0xbc8ce44au), U64(0xb976dc09u, 0x3feea5e1u), U64(0x3bef4da8u, 0xbc8c33c5u), U64(0xb03a5585u, 0x3feea47eu), U64(0x892be9aeu, 0xbc845378u), U64(0x34ccc320u, 0x3feea346u), U64(0x78565858u, 0xbc93ceddu), U64(0x82552225u, 0x3feea238u), U64(0x807e1964u, 0x3c5710aau), U64(0xd44ca973u, 0x3feea155u), U64(0xbf5e2228u, 0xbc93b3efu), U64(0x667f3bcdu, 0x3feea09eu), U64(0x8734b982u, 0xbc6a12adu), U64(0x750bdabfu, 0x3feea012u), U64(0xb86da9eeu, 0xbc6367efu), U64(0x3c651a2fu, 0x3fee9fb2u), U64(0x54e08851u, 0xbc80dc3du), U64(0xf9519484u, 0x3fee9f7du), U64(0x7e5a3ecfu, 0xbc781f64u), U64(0xe8ec5f74u, 0x3fee9f75u), U64(0xc08b7db0u, 0xbc86ee4au), U64(0x48a58174u, 0x3fee9f9au), U64(0x1e55e68au, 0xbc861932u), U64(0x564267c9u, 0x3fee9febu), U64(0x5e09d4d3u, 0x3c909ccbu), U64(0x4fde5d3fu, 0x3feea069u), U64(0xb94da51du, 0xbc7b32dcu), U64(0x73eb0187u, 0x3feea114u), U64(0x5467c06bu, 0x3c94ecfdu), U64(0x0130c132u, 0x3feea1edu), U64(0xabd66c55u, 0x3c65ebe1u), U64(0x36cf4e62u, 0x3feea2f3u), U64(0x2fb3cf42u, 0xbc88a1c5u), U64(0x543e1a12u, 0x3feea427u), U64(0xf13b3734u, 0xbc9369b6u), U64(0x994cce13u, 0x3feea589u), U64(0x3a19ff1eu, 0xbc805e84u), U64(0x4623c7adu, 0x3feea71au), U64(0xd872576eu, 0xbc94d450u), U64(0x9b4492edu, 0x3feea8d9u), U64(0x5b0e8a00u, 0x3c90ad67u), U64(0xd98a6699u, 0x3feeaac7u), U64(0xc1f0eab4u, 0x3c8db72fu), U64(0x422aa0dbu, 0x3feeace5u), U64(0x9cc5e7ffu, 0xbc65b660u), U64(0x16b5448cu, 0x3feeaf32u), U64(0x59f35f44u, 0x3c7bf683u), U64(0x99157736u, 0x3feeb1aeu), U64(0xa71e3d83u, 0xbc93091fu), U64(0x0b91ffc6u, 0x3feeb45bu), U64(0x8b6c1e29u, 0xbc5da9b8u), U64(0xb0cdc5e5u, 0x3feeb737u), U64(0x7c90b959u, 0xbc6c23f9u), U64(0xcbc8520fu, 0x3feeba44u), U64(0x22f4f9aau, 0xbc924343u), U64(0x9fde4e50u, 0x3feebd82u), U64(0xd7668e4bu, 0xbc85ca6cu), U64(0x70ca07bau, 0x3feec0f1u), U64(0x2b91ce27u, 0x3c71affcu), U64(0x82a3f090u, 0x3feec491u), U64(0xe10a73bbu, 0x3c6dd235u), U64(0x19e32323u, 0x3feec863u), U64(0x22622263u, 0xbc87c504u), U64(0x7b5de565u, 0x3feecc66u), U64(0xe3e231d5u, 0x3c8b1c86u), U64(0xec4a2d33u, 0x3feed09bu), U64(0xd3bcbb15u, 0xbc91bbd1u), U64(0xb23e255du, 0x3feed503u), U64(0x9cee31d2u, 0x3c90cc31u), U64(0x1330b358u, 0x3feed99eu), U64(0x6e735ab3u, 0x3c846984u), U64(0x5579fdbfu, 0x3feede6bu), U64(0x978e9db4u, 0xbc82dfcdu), U64(0xbfd3f37au, 0x3feee36bu), U64(0x92cb3387u, 0x3c8c1a77u), U64(0x995ad3adu, 0x3feee89fu), U64(0x4ad1d9fau, 0xbc907b8fu), U64(0x298db666u, 0x3feeee07u), U64(0x56dcaebau, 0xbc55c3d9u), U64(0xb84f15fbu, 0x3feef3a2u), U64(0x3da6f640u, 0xbc90a40eu), U64(0x8de5593au, 0x3feef972u), U64(0x38ad9334u, 0xbc68d6f4u), U64(0xf2fb5e47u, 0x3feeff76u), U64(0x6b588a35u, 0xbc91eee2u), U64(0x30a1064au, 0x3fef05b0u), U64(0x0a5fddcdu, 0x3c74ffd7u), U64(0x904bc1d2u, 0x3fef0c1eu), U64(0xfa9298acu, 0xbc91bdfbu), U64(0x5bd71e09u, 0x3fef12c2u), U64(0x30af0cb3u, 0x3c736eaeu), U64(0xdd85529cu, 0x3fef199bu), U64(0x5c9ffd94u, 0x3c8ee332u), U64(0x5fffd07au, 0x3fef20abu), U64(0xd10959acu, 0x3c84e08fu), U64(0x2e57d14bu, 0x3fef27f1u), U64(0x384e1a67u, 0x3c63cdafu), U64(0x9406e7b5u, 0x3fef2f6du), U64(0x6c921968u, 0x3c676b2cu), U64(0xdcef9069u, 0x3fef3720u), U64(0x83ccb5d2u, 0xbc808a18u), U64(0x555dc3fau, 0x3fef3f0bu), U64(0x3ffffa6fu, 0xbc8fad5du), U64(0x4a07897cu, 0x3fef472du), U64(0x3875a949u, 0xbc900daeu), U64(0x080d89f2u, 0x3fef4f87u), U64(0xa63d07a7u, 0x3c74a385u), U64(0xdcfba487u, 0x3fef5818u), U64(0x2040220fu, 0xbc82919eu), U64(0x16c98398u, 0x3fef60e3u), U64(0xd5c192acu, 0x3c8e5a50u), U64(0x03db3285u, 0x3fef69e6u), U64(0xac016b4bu, 0x3c843a59u), U64(0xf301b460u, 0x3fef7321u), U64(0x07b43e1fu, 0xbc82d521u), U64(0x337b9b5fu, 0x3fef7c97u), U64(0x3b470dc9u, 0xbc892ab9u), U64(0x14f5a129u, 0x3fef8646u), U64(0x603a88d3u, 0x3c74b604u), U64(0xe78b3ff6u, 0x3fef902eu), U64(0x519d7271u, 0x3c83c5ecu), U64(0xfbc74c83u, 0x3fef9a51u), U64(0x8fd391f0u, 0xbc8ff712u), U64(0xa2a490dau, 0x3fefa4afu), U64(0xe223747du, 0xbc8dae98u), U64(0x2d8e67f1u, 0x3fefaf48u), U64(0x41aa2008u, 0x3c8ec3bcu), U64(0xee615a27u, 0x3fefba1bu), U64(0xc3a9eb32u, 0x3c842b94u), U64(0x376bba97u, 0x3fefc52bu), U64(0x31d185eeu, 0x3c8a64a9u), U64(0x5b6e4540u, 0x3fefd076u), U64(0xe43be3edu, 0xbc8e37bau), U64(0xad9cbe14u, 0x3fefdbfdu), U64(0x4d91cd9du, 0x3c77893bu), U64(0x819e90d8u, 0x3fefe7c1u), U64(0x4160cc89u, 0x3c5305c1u), U64(0x2b8f71f1u, 0x3feff3c2u)); - -const LN2_HI: F64 = U64(0xfefa3800u, 0x3fe62e42u); - -const LN2_LO: F64 = U64(0x93c76730u, 0x3d2ef357u); - -const POW_POLY: array = array(U64(0x00000000u, 0xbfe00000u), U64(0x55555560u, 0xbfe55555u), U64(0x00000006u, 0x3fe00000u), U64(0x9959554eu, 0x3fe99999u), U64(0x5529a47au, 0xbfe55555u), U64(0x9b4845e9u, 0xbff2495bu), U64(0x8b263fc3u, 0x3ff0002bu)); - -const POW_TAB: array, 128> = array, 128>(array(U64(0x00000000u, 0x3ff6a000u), U64(0xf2b9c800u, 0xbfd62c82u), U64(0x28375680u, 0x3cfab424u)), array(U64(0x00000000u, 0x3ff68000u), U64(0xbf580800u, 0xbfd5d1bdu), U64(0xd8e0f720u, 0xbd1ca508u)), array(U64(0x00000000u, 0x3ff66000u), U64(0x17455800u, 0xbfd57677u), U64(0xd5b6506du, 0xbd2362a4u)), array(U64(0x00000000u, 0x3ff64000u), U64(0x872df800u, 0xbfd51aadu), U64(0x9eb067d5u, 0xbce684e4u)), array(U64(0x00000000u, 0x3ff62000u), U64(0x95777800u, 0xbfd4be5fu), U64(0x93293ee0u, 0xbd041b69u)), array(U64(0x00000000u, 0x3ff60000u), U64(0xc21c6000u, 0xbfd4618bu), U64(0x484c84ccu, 0x3d13d82fu)), array(U64(0x00000000u, 0x3ff5e000u), U64(0x8686a800u, 0xbfd40430u), U64(0xed820b3au, 0x3cdc42f3u)), array(U64(0x00000000u, 0x3ff5c000u), U64(0x55694800u, 0xbfd3a64cu), U64(0x86519460u, 0x3d20b1c6u)), array(U64(0x00000000u, 0x3ff5a000u), U64(0x9a988000u, 0xbfd347ddu), U64(0xd4c58092u, 0x3d25594du)), array(U64(0x00000000u, 0x3ff58000u), U64(0xbae12000u, 0xbfd2e8e2u), U64(0x99b72bd8u, 0x3d267b1eu)), array(U64(0x00000000u, 0x3ff56000u), U64(0x13de8800u, 0xbfd2895au), U64(0xb6cfb03fu, 0x3d15ca14u)), array(U64(0x00000000u, 0x3ff56000u), U64(0x13de8800u, 0xbfd2895au), U64(0xb6cfb03fu, 0x3d15ca14u)), array(U64(0x00000000u, 0x3ff54000u), U64(0xfbcf7800u, 0xbfd22941u), U64(0x2853da76u, 0xbd165a24u)), array(U64(0x00000000u, 0x3ff52000u), U64(0xc1699800u, 0xbfd1c898u), U64(0x68e75404u, 0xbd1fafbcu)), array(U64(0x00000000u, 0x3ff50000u), U64(0xababa800u, 0xbfd1675cu), U64(0x3382a8f0u, 0x3d1f1fc6u)), array(U64(0x00000000u, 0x3ff4e000u), U64(0xf9ae4800u, 0xbfd1058bu), U64(0xfd055a66u, 0xbd26a8c4u)), array(U64(0x00000000u, 0x3ff4c000u), U64(0xe2739000u, 0xbfd0a324u), U64(0x7ef4030eu, 0xbd0c6beeu)), array(U64(0x00000000u, 0x3ff4a000u), U64(0x94b4d000u, 0xbfd04025u), U64(0x9ef42d7fu, 0xbcf036b8u)), array(U64(0x00000000u, 0x3ff4a000u), U64(0x94b4d000u, 0xbfd04025u), U64(0x9ef42d7fu, 0xbcf036b8u)), array(U64(0x00000000u, 0x3ff48000u), U64(0x6d5e4000u, 0xbfcfb918u), U64(0xab993c87u, 0x3d0d572au)), array(U64(0x00000000u, 0x3ff46000u), U64(0xcbdc6000u, 0xbfcef0adu), U64(0x9c86af24u, 0x3d2b26b7u)), array(U64(0x00000000u, 0x3ff44000u), U64(0x6e2af000u, 0xbfce2707u), U64(0x543fff10u, 0xbd172f4fu)), array(U64(0x00000000u, 0x3ff42000u), U64(0x6b4fc000u, 0xbfcd5c21u), U64(0xbbca681bu, 0x3d21ba91u)), array(U64(0x00000000u, 0x3ff40000u), U64(0xc79aa000u, 0xbfcc8ff7u), U64(0x689f8434u, 0x3d27794fu)), array(U64(0x00000000u, 0x3ff40000u), U64(0xc79aa000u, 0xbfcc8ff7u), U64(0x689f8434u, 0x3d27794fu)), array(U64(0x00000000u, 0x3ff3e000u), U64(0x742d9000u, 0xbfcbc286u), U64(0x318bb78fu, 0x3d194eb0u)), array(U64(0x00000000u, 0x3ff3c000u), U64(0x4e80c000u, 0xbfcaf3c9u), U64(0x3fcd9066u, 0x3cba4e63u)), array(U64(0x00000000u, 0x3ff3a000u), U64(0x1fe2b000u, 0xbfca23bcu), U64(0xdc46c1eau, 0xbd258c64u)), array(U64(0x00000000u, 0x3ff3a000u), U64(0x1fe2b000u, 0xbfca23bcu), U64(0xdc46c1eau, 0xbd258c64u)), array(U64(0x00000000u, 0x3ff38000u), U64(0x9cf45000u, 0xbfc9525au), U64(0x04c1d4e3u, 0xbd2ad1d9u)), array(U64(0x00000000u, 0x3ff36000u), U64(0x6520d000u, 0xbfc87fa0u), U64(0x7fdbfa09u, 0x3d2bbdbfu)), array(U64(0x00000000u, 0x3ff34000u), U64(0x0210e000u, 0xbfc7ab89u), U64(0x72534a58u, 0x3d2bdb90u)), array(U64(0x00000000u, 0x3ff34000u), U64(0x0210e000u, 0xbfc7ab89u), U64(0x72534a58u, 0x3d2bdb90u)), array(U64(0x00000000u, 0x3ff32000u), U64(0xe719d000u, 0xbfc6d60fu), U64(0xa3b2e266u, 0xbd10e46au)), array(U64(0x00000000u, 0x3ff30000u), U64(0x70a79000u, 0xbfc5ff30u), U64(0x9f105039u, 0xbd1e9e43u)), array(U64(0x00000000u, 0x3ff30000u), U64(0x70a79000u, 0xbfc5ff30u), U64(0x9f105039u, 0xbd1e9e43u)), array(U64(0x00000000u, 0x3ff2e000u), U64(0xe3a1b000u, 0xbfc526e5u), U64(0x90075b8fu, 0xbd20de8bu)), array(U64(0x00000000u, 0x3ff2c000u), U64(0x6ccb8000u, 0xbfc44d2bu), U64(0x6135783cu, 0x3d170cc1u)), array(U64(0x00000000u, 0x3ff2c000u), U64(0x6ccb8000u, 0xbfc44d2bu), U64(0x6135783cu, 0x3d170cc1u)), array(U64(0x00000000u, 0x3ff2a000u), U64(0x201e9000u, 0xbfc371fcu), U64(0x4d27543au, 0x3cf17886u)), array(U64(0x00000000u, 0x3ff28000u), U64(0xf81ff000u, 0xbfc29552u), U64(0x1771c408u, 0xbd248d30u)), array(U64(0x00000000u, 0x3ff26000u), U64(0xd52f6000u, 0xbfc1b72au), U64(0x1811a396u, 0xbd2e80a4u)), array(U64(0x00000000u, 0x3ff26000u), U64(0xd52f6000u, 0xbfc1b72au), U64(0x1811a396u, 0xbd2e80a4u)), array(U64(0x00000000u, 0x3ff24000u), U64(0x7cd09000u, 0xbfc0d77eu), U64(0x88e85bf4u, 0x3d0a6996u)), array(U64(0x00000000u, 0x3ff24000u), U64(0x7cd09000u, 0xbfc0d77eu), U64(0x88e85bf4u, 0x3d0a6996u)), array(U64(0x00000000u, 0x3ff22000u), U64(0x31dbe000u, 0xbfbfec91u), U64(0x5ca333f2u, 0xbd257554u)), array(U64(0x00000000u, 0x3ff20000u), U64(0x6e2b0000u, 0xbfbe2707u), U64(0x2af0003cu, 0x3d2a342cu)), array(U64(0x00000000u, 0x3ff20000u), U64(0x6e2b0000u, 0xbfbe2707u), U64(0x2af0003cu, 0x3d2a342cu)), array(U64(0x00000000u, 0x3ff1e000u), U64(0x8f5bc000u, 0xbfbc5e54u), U64(0x585fbe06u, 0xbd1d0c57u)), array(U64(0x00000000u, 0x3ff1c000u), U64(0x3a4ae000u, 0xbfba926du), U64(0xe85baac8u, 0x3d253935u)), array(U64(0x00000000u, 0x3ff1c000u), U64(0x3a4ae000u, 0xbfba926du), U64(0xe85baac8u, 0x3d253935u)), array(U64(0x00000000u, 0x3ff1a000u), U64(0xd631a000u, 0xbfb8c345u), U64(0x4d2f5668u, 0x3d137c29u)), array(U64(0x00000000u, 0x3ff1a000u), U64(0xd631a000u, 0xbfb8c345u), U64(0x4d2f5668u, 0x3d137c29u)), array(U64(0x00000000u, 0x3ff18000u), U64(0x8ae56000u, 0xbfb6f0d2u), U64(0xc93373dau, 0xbd269737u)), array(U64(0x00000000u, 0x3ff16000u), U64(0x3f062000u, 0xbfb51b07u), U64(0x61c65e57u, 0x3d1f025bu)), array(U64(0x00000000u, 0x3ff16000u), U64(0x3f062000u, 0xbfb51b07u), U64(0x61c65e57u, 0x3d1f025bu)), array(U64(0x00000000u, 0x3ff14000u), U64(0x961be000u, 0xbfb341d7u), U64(0xccf913dfu, 0x3d2c5edau)), array(U64(0x00000000u, 0x3ff14000u), U64(0x961be000u, 0xbfb341d7u), U64(0xccf913dfu, 0x3d2c5edau)), array(U64(0x00000000u, 0x3ff12000u), U64(0xeea38000u, 0xbfb16536u), U64(0x768fa309u, 0x3d147c5eu)), array(U64(0x00000000u, 0x3ff10000u), U64(0xc0118000u, 0xbfaf0a30u), U64(0x83368e91u, 0x3d2d599eu)), array(U64(0x00000000u, 0x3ff10000u), U64(0xc0118000u, 0xbfaf0a30u), U64(0x83368e91u, 0x3d2d599eu)), array(U64(0x00000000u, 0x3ff0e000u), U64(0x71198000u, 0xbfab42ddu), U64(0xe5d6704cu, 0x3d1c827au)), array(U64(0x00000000u, 0x3ff0e000u), U64(0x71198000u, 0xbfab42ddu), U64(0xe5d6704cu, 0x3d1c827au)), array(U64(0x00000000u, 0x3ff0c000u), U64(0xf632c000u, 0xbfa77458u), U64(0x34f2a1eeu, 0xbd2cfc46u)), array(U64(0x00000000u, 0x3ff0c000u), U64(0xf632c000u, 0xbfa77458u), U64(0x34f2a1eeu, 0xbd2cfc46u)), array(U64(0x00000000u, 0x3ff0a000u), U64(0xb9fec000u, 0xbfa39e87u), U64(0xf526feaau, 0x3cf502b7u)), array(U64(0x00000000u, 0x3ff0a000u), U64(0xb9fec000u, 0xbfa39e87u), U64(0xf526feaau, 0x3cf502b7u)), array(U64(0x00000000u, 0x3ff08000u), U64(0x0e780000u, 0xbf9f829bu), U64(0x7c7e09e4u, 0xbd298026u)), array(U64(0x00000000u, 0x3ff08000u), U64(0x0e780000u, 0xbf9f829bu), U64(0x7c7e09e4u, 0xbd298026u)), array(U64(0x00000000u, 0x3ff06000u), U64(0x07d58000u, 0xbf97b91bu), U64(0x93faa639u, 0xbd288d54u)), array(U64(0x00000000u, 0x3ff04000u), U64(0xb0fc0000u, 0xbf8fc0a8u), U64(0xf6d3a69cu, 0xbcdf1e7cu)), array(U64(0x00000000u, 0x3ff04000u), U64(0xb0fc0000u, 0xbf8fc0a8u), U64(0xf6d3a69cu, 0xbcdf1e7cu)), array(U64(0x00000000u, 0x3ff02000u), U64(0x6b100000u, 0xbf7fe02au), U64(0x0dda40e4u, 0xbd19e23fu)), array(U64(0x00000000u, 0x3ff02000u), U64(0x6b100000u, 0xbf7fe02au), U64(0x0dda40e4u, 0xbd19e23fu)), array(U64(0x00000000u, 0x3ff00000u), U64(0x00000000u, 0x00000000u), U64(0x00000000u, 0x00000000u)), array(U64(0x00000000u, 0x3ff00000u), U64(0x00000000u, 0x00000000u), U64(0x00000000u, 0x00000000u)), array(U64(0x00000000u, 0x3fefc000u), U64(0x75890000u, 0x3f801015u), U64(0x999d2be8u, 0xbd10c76bu)), array(U64(0x00000000u, 0x3fef8000u), U64(0x58938000u, 0x3f902056u), U64(0x06e2f7d2u, 0xbd23dc5bu)), array(U64(0x00000000u, 0x3fef4000u), U64(0x28c90000u, 0x3f984925u), U64(0x325a0c34u, 0xbd2aa0bau)), array(U64(0x00000000u, 0x3fef0000u), U64(0x89e74000u, 0x3fa0415du), U64(0x5cf1d753u, 0x3d0111c0u)), array(U64(0x00000000u, 0x3feec000u), U64(0xd42e0000u, 0x3fa466aeu), U64(0x75bdfd28u, 0xbd2c1673u)), array(U64(0x00000000u, 0x3fee8000u), U64(0x149fc000u, 0x3fa894aau), U64(0xd05a267du, 0xbd197995u)), array(U64(0x00000000u, 0x3fee4000u), U64(0xcdddc000u, 0x3faccb73u), U64(0x47d82807u, 0xbd1a68f2u)), array(U64(0x00000000u, 0x3fee2000u), U64(0xc006c000u, 0x3faeea31u), U64(0x4fc93b7bu, 0xbd0e113eu)), array(U64(0x00000000u, 0x3fede000u), U64(0xd1466000u, 0x3fb1973bu), U64(0x560d9e9bu, 0xbd25325du)), array(U64(0x00000000u, 0x3feda000u), U64(0xa7d1e000u, 0x3fb3bdf5u), U64(0xa5db4ed7u, 0x3d2cc85eu)), array(U64(0x00000000u, 0x3fed6000u), U64(0x4d97a000u, 0x3fb5e95au), U64(0x3c5d1d1eu, 0xbd2c6906u)), array(U64(0x00000000u, 0x3fed4000u), U64(0x0aeac000u, 0x3fb700d3u), U64(0xa99ded32u, 0x3cec1e8du)), array(U64(0x00000000u, 0x3fed0000u), U64(0x5d594000u, 0x3fb9335eu), U64(0x3abd47dau, 0x3d23115cu)), array(U64(0x00000000u, 0x3fecc000u), U64(0x8dad6000u, 0x3fbb6ac8u), U64(0x2bf768e5u, 0xbd139080u)), array(U64(0x00000000u, 0x3feca000u), U64(0x01bc4000u, 0x3fbc8858u), U64(0xc65aacd3u, 0x3d2646d1u)), array(U64(0x00000000u, 0x3fec6000u), U64(0x830a2000u, 0x3fbec739u), U64(0xafe645e0u, 0xbd2dc068u)), array(U64(0x00000000u, 0x3fec4000u), U64(0x39dbe000u, 0x3fbfe891u), U64(0x4fa10afdu, 0xbd2534d6u)), array(U64(0x00000000u, 0x3fec0000u), U64(0x8227e000u, 0x3fc1178eu), U64(0xce2d07f2u, 0x3d21ef78u)), array(U64(0x00000000u, 0x3febe000u), U64(0x7e23f000u, 0x3fc1aa2bu), U64(0x44389934u, 0x3d2ca78eu)), array(U64(0x00000000u, 0x3feba000u), U64(0x0c868000u, 0x3fc2d161u), U64(0xcb81b4a1u, 0x3d039d6cu)), array(U64(0x00000000u, 0x3feb8000u), U64(0xb0159000u, 0x3fc365fcu), U64(0x234b7289u, 0x3cc62fa8u)), array(U64(0x00000000u, 0x3feb4000u), U64(0x8333b000u, 0x3fc4913du), U64(0x54fdb678u, 0x3d258379u)), array(U64(0x00000000u, 0x3feb2000u), U64(0xe4a1b000u, 0x3fc527e5u), U64(0xe5697dc7u, 0x3d2633e8u)), array(U64(0x00000000u, 0x3feae000u), U64(0xbe8c1000u, 0x3fc6574eu), U64(0x2c3c2e78u, 0x3d19cf8bu)), array(U64(0x00000000u, 0x3feac000u), U64(0x8b757000u, 0x3fc6f012u), U64(0xe59c21e1u, 0xbd25118du)), array(U64(0x00000000u, 0x3feaa000u), U64(0x85445000u, 0x3fc7898du), U64(0x70914305u, 0xbd1c6610u)), array(U64(0x00000000u, 0x3fea6000u), U64(0xeb390000u, 0x3fc8beafu), U64(0xaae92cd1u, 0xbd073d54u)), array(U64(0x00000000u, 0x3fea4000u), U64(0xdcf70000u, 0x3fc95a5au), U64(0x58a0ff6fu, 0x3d07f228u)), array(U64(0x00000000u, 0x3fea0000u), U64(0x3c8ae000u, 0x3fca93edu), U64(0x50562169u, 0xbd287243u)), array(U64(0x00000000u, 0x3fe9e000u), U64(0x575bd000u, 0x3fcb31d8u), U64(0x4eace1aau, 0xbd0c358du)), array(U64(0x00000000u, 0x3fe9c000u), U64(0x383be000u, 0x3fcbd087u), U64(0x595412b6u, 0xbd2d4bc4u)), array(U64(0x00000000u, 0x3fe9a000u), U64(0xc6f01000u, 0x3fcc6ffbu), U64(0xc5962bd2u, 0xbcf1ec72u)), array(U64(0x00000000u, 0x3fe96000u), U64(0xb0d49000u, 0x3fcdb13du), U64(0xf715b035u, 0xbd2aff2au)), array(U64(0x00000000u, 0x3fe94000u), U64(0xffe71000u, 0x3fce530eu), U64(0x6041f430u, 0x3cc21227u)), array(U64(0x00000000u, 0x3fe92000u), U64(0xe4dd0000u, 0x3fcef5adu), U64(0x65bb8e11u, 0xbcca2115u)), array(U64(0x00000000u, 0x3fe90000u), U64(0x6cb3b000u, 0x3fcf991cu), U64(0xca0cdf30u, 0x3d1bcbecu)), array(U64(0x00000000u, 0x3fe8c000u), U64(0x604d5800u, 0x3fd07138u), U64(0x16ed4e91u, 0x3cf89cdbu)), array(U64(0x00000000u, 0x3fe8a000u), U64(0x67616000u, 0x3fd0c42du), U64(0x163ceae9u, 0x3d27188bu)), array(U64(0x00000000u, 0x3fe88000u), U64(0x8227e800u, 0x3fd1178eu), U64(0x63a5f01cu, 0xbd2c210eu)), array(U64(0x00000000u, 0x3fe86000u), U64(0xcbacf800u, 0x3fd16b5cu), U64(0xf7a51681u, 0x3d2b9acdu)), array(U64(0x00000000u, 0x3fe84000u), U64(0x635a6800u, 0x3fd1bf99u), U64(0x5147bdb7u, 0x3d2ca6edu)), array(U64(0x00000000u, 0x3fe82000u), U64(0x6d0eb800u, 0x3fd21445u), U64(0xba46baeau, 0x3d0a87deu)), array(U64(0x00000000u, 0x3fe7e000u), U64(0x7cdc9000u, 0x3fd2bef0u), U64(0x4a5004f4u, 0x3d2a9cfau)), array(U64(0x00000000u, 0x3fe7c000u), U64(0xe1d36000u, 0x3fd314f1u), U64(0xd3213cb8u, 0xbd28e27au)), array(U64(0x00000000u, 0x3fe7a000u), U64(0x76be1000u, 0x3fd36b67u), U64(0xb0f177c8u, 0x3d116ecdu)), array(U64(0x00000000u, 0x3fe78000u), U64(0x77333000u, 0x3fd3c252u), U64(0xb606bd5cu, 0x3d183b54u)), array(U64(0x00000000u, 0x3fe76000u), U64(0x23d5e800u, 0x3fd419b4u), U64(0xec90e09du, 0x3d08e436u)), array(U64(0x00000000u, 0x3fe74000u), U64(0xc271c800u, 0x3fd4718du), U64(0x0967d675u, 0xbd2f27ceu)), array(U64(0x00000000u, 0x3fe72000u), U64(0x9e173000u, 0x3fd4c9e0u), U64(0x1b0ad8a4u, 0xbd2e2089u)), array(U64(0x00000000u, 0x3fe70000u), U64(0x0738a000u, 0x3fd522aeu), U64(0x8164c759u, 0x3d2ebe70u)), array(U64(0x00000000u, 0x3fe6e000u), U64(0x53c8d000u, 0x3fd57bf7u), U64(0xee5d40efu, 0x3d1fadedu)), array(U64(0x00000000u, 0x3fe6c000u), U64(0xdf596000u, 0x3fd5d5bdu), U64(0x08a465dcu, 0xbd0a0b2au))); - -const LOG_POLY: array = array(U64(0x00000001u, 0xbfe00000u), U64(0x5551305bu, 0x3fd55555u), U64(0xffeb4590u, 0xbfcfffffu), U64(0x24f10111u, 0x3fc999b3u), U64(0xe506c89fu, 0xbfc55575u)); - -const LOG_NEAR_POLY: array = array(U64(0x00000000u, 0xbfe00000u), U64(0x55555577u, 0x3fd55555u), U64(0xfffffdcbu, 0xbfcfffffu), U64(0x9995dd0cu, 0x3fc99999u), U64(0x556745a7u, 0xbfc55555u), U64(0xa344de30u, 0x3fc24924u), U64(0xa4423d65u, 0xbfbfffffu), U64(0x282ad6cau, 0x3fbc7184u), U64(0x43b068ffu, 0xbfb999ebu), U64(0xf7afd085u, 0x3fb78182u), U64(0x75d145cdu, 0xbfb55213u)); - -const LOG_TAB: array, 128> = array, 128>(array(U64(0xc3e0de9fu, 0x3ff734f0u), U64(0x79e69000u, 0xbfd7cc7fu)), array(U64(0x6a2ce91fu, 0x3ff71378u), U64(0xc20d0000u, 0xbfd76feeu)), array(U64(0x08fab5a0u, 0x3ff6f260u), U64(0x1351e000u, 0xbfd713e3u)), array(U64(0x1f138c7du, 0x3ff6d1a6u), U64(0x38287800u, 0xbfd6b85bu)), array(U64(0x0bc5b4d1u, 0x3ff6b149u), U64(0x90807800u, 0xbfd65d55u)), array(U64(0x332f0cbau, 0x3ff69147u), U64(0x76180000u, 0xbfd602d0u)), array(U64(0x18224223u, 0x3ff6719fu), U64(0x86909000u, 0xbfd5a8cau)), array(U64(0x99a51ed9u, 0x3ff6524fu), U64(0x56035000u, 0xbfd54f43u)), array(U64(0xaa8f24c4u, 0x3ff63356u), U64(0xc36b4000u, 0xbfd4f637u)), array(U64(0x6b9ddc14u, 0x3ff614b3u), U64(0xfda85000u, 0xbfd49da7u)), array(U64(0x52c65c4cu, 0x3ff5f664u), U64(0x3989a800u, 0xbfd44592u)), array(U64(0xb5912c4fu, 0x3ff5d867u), U64(0x39b0b800u, 0xbfd3edf4u)), array(U64(0xcb5b90deu, 0x3ff5babcu), U64(0x448f7000u, 0xbfd396ceu)), array(U64(0xf2d91a78u, 0x3ff59d61u), U64(0x17bda000u, 0xbfd3401eu)), array(U64(0x12465687u, 0x3ff58056u), U64(0xef468000u, 0xbfd2e9e2u)), array(U64(0xcee76bd3u, 0x3ff56397u), U64(0x3830e000u, 0xbfd2941bu)), array(U64(0xe2a77f93u, 0x3ff54725u), U64(0x8cda8800u, 0xbfd23ec5u)), array(U64(0x42064583u, 0x3ff52affu), U64(0x29279000u, 0xbfd1e9e1u)), array(U64(0xdbb2bddfu, 0x3ff50f22u), U64(0x2b48f800u, 0xbfd1956du)), array(U64(0x4734ded7u, 0x3ff4f38fu), U64(0x9ab9f800u, 0xbfd14167u)), array(U64(0xcfde2840u, 0x3ff4d843u), U64(0x94ef9800u, 0xbfd0edd0u)), array(U64(0xc078a3c8u, 0x3ff4bd3eu), U64(0x18db1000u, 0xbfd09aa5u)), array(U64(0xc3e0258au, 0x3ff4a27fu), U64(0x5263b800u, 0xbfd047e6u)), array(U64(0x24d48434u, 0x3ff48805u), U64(0x4586f000u, 0xbfcfeb22u)), array(U64(0x1b192d0bu, 0x3ff46dceu), U64(0x7517b000u, 0xbfcf474au)), array(U64(0xd3391854u, 0x3ff453d9u), U64(0x3d103000u, 0xbfcea444u)), array(U64(0x44b4845au, 0x3ff43a27u), U64(0x44e9b000u, 0xbfce020du)), array(U64(0x4115f8fbu, 0x3ff420b5u), U64(0x2977f000u, 0xbfcd60a2u)), array(U64(0xda3ef4b1u, 0x3ff40782u), U64(0x04959000u, 0xbfccc001u)), array(U64(0x5d57fe8fu, 0x3ff3ee8fu), U64(0x56891000u, 0xbfcc2029u)), array(U64(0xa00b4ce9u, 0x3ff3d5d9u), U64(0x8d811000u, 0xbfcb8117u)), array(U64(0xc010c12bu, 0x3ff3bd60u), U64(0xccd3d000u, 0xbfcae2c9u)), array(U64(0x2b75dab8u, 0x3ff3a524u), U64(0x2e129000u, 0xbfca4540u)), array(U64(0xcd9fd002u, 0x3ff38d22u), U64(0x681df000u, 0xbfc9a877u)), array(U64(0xc5847a1cu, 0x3ff3755bu), U64(0x69483000u, 0xbfc90c6du)), array(U64(0x49ad36e2u, 0x3ff35dceu), U64(0xa645c000u, 0xbfc87120u)), array(U64(0x984dd440u, 0x3ff34679u), U64(0xb4143000u, 0xbfc7d68fu)), array(U64(0xceffcb24u, 0x3ff32f5cu), U64(0x3c627000u, 0xbfc73cb8u)), array(U64(0x75a10d49u, 0x3ff31877u), U64(0x9b376000u, 0xbfc6a39au)), array(U64(0x373e3990u, 0x3ff301c8u), U64(0x54b7a000u, 0xbfc60b31u)), array(U64(0xbb95f841u, 0x3ff2eb4eu), U64(0x76243000u, 0xbfc5737du)), array(U64(0x0219a9d1u, 0x3ff2d50au), U64(0x8fc23000u, 0xbfc4dc7bu)), array(U64(0xa8b7fd2au, 0x3ff2bef9u), U64(0x51d20000u, 0xbfc4462cu)), array(U64(0x7a0c1babu, 0x3ff2a91cu), U64(0xbc830000u, 0xbfc3b08au)), array(U64(0x6014b530u, 0x3ff29372u), U64(0x6b490000u, 0xbfc31b99u)), array(U64(0x5757a1f5u, 0x3ff27dfau), U64(0x90a44000u, 0xbfc28754u)), array(U64(0x9b1d3bbfu, 0x3ff268b3u), U64(0xf879a000u, 0xbfc1f3b9u)), array(U64(0x838ff5bdu, 0x3ff2539du), U64(0x252ca000u, 0xbfc160c8u)), array(U64(0xaac9083bu, 0x3ff23eb7u), U64(0x57f72000u, 0xbfc0ce7fu)), array(U64(0x2ba940b6u, 0x3ff22a01u), U64(0x49fea000u, 0xbfc03cdcu)), array(U64(0x96cc4132u, 0x3ff21579u), U64(0xbc4b8000u, 0xbfbf57bdu)), array(U64(0x1dd2fc9bu, 0x3ff20120u), U64(0x96404000u, 0xbfbe3708u)), array(U64(0x494d480bu, 0x3ff1ecf4u), U64(0x3ef94000u, 0xbfbd1798u)), array(U64(0x528f6569u, 0x3ff1d8f5u), U64(0x4ed8a000u, 0xbfbbf967u)), array(U64(0x11577e7cu, 0x3ff1c523u), U64(0x202f6000u, 0xbfbadc79u)), array(U64(0x74cb26e9u, 0x3ff1b17cu), U64(0xe7288000u, 0xbfb9c0c3u)), array(U64(0x0c2c1ab6u, 0x3ff19e01u), U64(0xb372c000u, 0xbfb8a646u)), array(U64(0x7bb670bdu, 0x3ff18ab0u), U64(0xb3ac0000u, 0xbfb78d01u)), array(U64(0x25efbcb6u, 0x3ff1778au), U64(0x45380000u, 0xbfb674f1u)), array(U64(0x354c31dau, 0x3ff1648du), U64(0x6d878000u, 0xbfb55e0eu)), array(U64(0x90275fddu, 0x3ff151b9u), U64(0xdea1e000u, 0xbfb4485cu)), array(U64(0xa432d24cu, 0x3ff13f0eu), U64(0x4d6aa000u, 0xbfb333d9u)), array(U64(0x7210f9dau, 0x3ff12c8bu), U64(0xf8c56000u, 0xbfb22079u)), array(U64(0x28ecb531u, 0x3ff11a30u), U64(0x98622000u, 0xbfb10e46u)), array(U64(0xda8434afu, 0x3ff107fbu), U64(0x6ad20000u, 0xbfaffa6cu)), array(U64(0x0f4e6bb3u, 0x3ff0f5eeu), U64(0x4a774000u, 0xbfadda8du)), array(U64(0x5d2a9fceu, 0x3ff0e406u), U64(0xe4850000u, 0xbfabbcecu)), array(U64(0x632ca521u, 0x3ff0d244u), U64(0x4012c000u, 0xbfa9a189u)), array(U64(0x7ce2981au, 0x3ff0c0a7u), U64(0x3302c000u, 0xbfa78858u)), array(U64(0x83c636d1u, 0x3ff0af2fu), U64(0x67d68000u, 0xbfa5715eu)), array(U64(0x98a01339u, 0x3ff09ddbu), U64(0x49658000u, 0xbfa35c8au)), array(U64(0xaf52e7dfu, 0x3ff08cabu), U64(0x64154000u, 0xbfa149e3u)), array(U64(0x2f4e28fbu, 0x3ff07b9fu), U64(0x82eb8000u, 0xbf9e72c0u)), array(U64(0x8c358f19u, 0x3ff06ab5u), U64(0x52528000u, 0xbf9a55f1u)), array(U64(0xa5ecf92cu, 0x3ff059eeu), U64(0xcf818000u, 0xbf963d62u)), array(U64(0xcdd12c90u, 0x3ff04949u), U64(0x8caa0000u, 0xbf9228fbu)), array(U64(0xc6f0ada9u, 0x3ff038c6u), U64(0x20f90000u, 0xbf8c317bu)), array(U64(0x137932a9u, 0x3ff02865u), U64(0x5daa0000u, 0xbf841935u)), array(U64(0x27ea7348u, 0x3ff01824u), U64(0xc2ec0000u, 0xbf781203u)), array(U64(0x0614b195u, 0x3ff00804u), U64(0x79240000u, 0xbf600409u)), array(U64(0xf726fa1au, 0x3fefe01fu), U64(0x84900000u, 0x3f6feff3u)), array(U64(0xc261ea74u, 0x3fefa11cu), U64(0x353d0000u, 0x3f87dc41u)), array(U64(0xb081992eu, 0x3fef6310u), U64(0xc4c28000u, 0x3f93cea3u)), array(U64(0x3ceeadcdu, 0x3fef25f6u), U64(0x14890000u, 0x3f9b9fc1u)), array(U64(0x039113e7u, 0x3feee9c8u), U64(0xce110000u, 0x3fa1b0d8u)), array(U64(0x78cbb1abu, 0x3feeae80u), U64(0xd001c000u, 0x3fa58a5bu)), array(U64(0xa29d0c9bu, 0x3fee741au), U64(0x40d88000u, 0x3fa95c83u)), array(U64(0x830a99b5u, 0x3fee3a91u), U64(0xef578000u, 0x3fad276au)), array(U64(0x09609a56u, 0x3fee01e0u), U64(0xe598c000u, 0x3fb07598u)), array(U64(0xe577bb98u, 0x3fedca01u), U64(0xe30d2000u, 0x3fb253f5u)), array(U64(0x0b7c9103u, 0x3fed92f2u), U64(0x8b380000u, 0x3fb42eddu)), array(U64(0x66fb5cceu, 0x3fed5cacu), U64(0x8757c000u, 0x3fb60659u)), array(U64(0xaa5ede9du, 0x3fed272cu), U64(0x356a0000u, 0x3fb7da76u)), array(U64(0x3e6b2ccdu, 0x3fecf26eu), U64(0x4e1c6000u, 0x3fb9ab43u)), array(U64(0xa2a77902u, 0x3fecbe6du), U64(0xbb0d6000u, 0x3fbb78c7u)), array(U64(0x6d37086du, 0x3fec8b26u), U64(0x32e72000u, 0x3fbd4313u)), array(U64(0xbd5d5804u, 0x3fec5894u), U64(0x71de6000u, 0x3fbf0a31u)), array(U64(0x33bb9f8cu, 0x3fec26b5u), U64(0x2b914000u, 0x3fc06715u)), array(U64(0xeeece73fu, 0x3febf583u), U64(0x8292b000u, 0x3fc14785u)), array(U64(0x75db96c1u, 0x3febc4fdu), U64(0xcdca3000u, 0x3fc2266eu)), array(U64(0x0c864a28u, 0x3feb951eu), U64(0xa6c55000u, 0x3fc303d7u)), array(U64(0xc5ef3e2cu, 0x3feb65e2u), U64(0x3c331000u, 0x3fc3dfc3u)), array(U64(0x67c9888bu, 0x3feb3748u), U64(0x6b7a8000u, 0x3fc4ba36u)), array(U64(0x211d304au, 0x3feb094bu), U64(0x28d1f000u, 0x3fc59339u)), array(U64(0x85f2ef7eu, 0x3feadbe8u), U64(0x2418f000u, 0x3fc66acdu)), array(U64(0x31603da2u, 0x3feaaf1du), U64(0xec669000u, 0x3fc740f8u)), array(U64(0x3fd358a7u, 0x3fea82e6u), U64(0xf51af000u, 0x3fc815c0u)), array(U64(0xef09738bu, 0x3fea5740u), U64(0x54f68000u, 0x3fc8e929u)), array(U64(0x90ab4b27u, 0x3fea2c2au), U64(0x02f84000u, 0x3fc9bb36u)), array(U64(0x1393f2d1u, 0x3fea01a0u), U64(0x1c2c0000u, 0x3fca8bedu)), array(U64(0x24db3c1bu, 0x3fe9d79fu), U64(0x5c01d000u, 0x3fcb5b51u)), array(U64(0x05c7b190u, 0x3fe9ae25u), U64(0xccbcc000u, 0x3fcc2967u)), array(U64(0xf297ce2fu, 0x3fe9852eu), U64(0xd5486000u, 0x3fccf635u)), array(U64(0xeea44b75u, 0x3fe95cbau), U64(0x3446c000u, 0x3fcdc1bdu)), array(U64(0x9de74838u, 0x3fe934c6u), U64(0xb8cfe000u, 0x3fce8c01u)), array(U64(0x2f6752e6u, 0x3fe90d4fu), U64(0xc0179000u, 0x3fcf5509u)), array(U64(0x8effd79du, 0x3fe8e652u), U64(0x121fb800u, 0x3fd00e6cu)), array(U64(0x9fcc007cu, 0x3fe8bfceu), U64(0x0e93d000u, 0x3fd071b8u)), array(U64(0xdabec30eu, 0x3fe899c0u), U64(0x9e867000u, 0x3fd0d46bu)), array(U64(0xaa2317fbu, 0x3fe87427u), U64(0x334bd000u, 0x3fd13687u)), array(U64(0xacb39a08u, 0x3fe84f00u), U64(0x67234800u, 0x3fd1980du)), array(U64(0xe8653e55u, 0x3fe82a49u), U64(0xe0cc8000u, 0x3fd1f8ffu)), array(U64(0x95f40260u, 0x3fe80601u), U64(0xd7636800u, 0x3fd2595fu)), array(U64(0x63e0a329u, 0x3fe7e225u), U64(0x0914a800u, 0x3fd2b930u)), array(U64(0x77dcb5adu, 0x3fe7beb3u), U64(0x10436000u, 0x3fd31872u)), array(U64(0x679725c2u, 0x3fe79baau), U64(0x6dec1800u, 0x3fd37726u)), array(U64(0xf2170657u, 0x3fe77907u), U64(0xfbaf3000u, 0x3fd3d54fu)), array(U64(0xdbd6130cu, 0x3fe756cau), U64(0xe32fe000u, 0x3fd432eeu))); - -const LOG_TAB2: array, 128> = array, 128>(array(U64(0x014fb66bu, 0x3fe61000u), U64(0x91425b3cu, 0x3c7e026cu)), array(U64(0x034db495u, 0x3fe63000u), U64(0x48005d41u, 0x3c8dbfeau)), array(U64(0x0d94d478u, 0x3fe65000u), U64(0x86d6a5b7u, 0x3c8e7fa7u)), array(U64(0x074e6fadu, 0x3fe67000u), U64(0x6b54254cu, 0x3c61fceau)), array(U64(0xfedf0faeu, 0x3fe68fffu), U64(0x4c590efdu, 0xbc7c7e27u)), array(U64(0x0763c5bcu, 0x3fe6b000u), U64(0x48dcda01u, 0xbc8ac168u)), array(U64(0x1e5cc1f6u, 0x3fe6d000u), U64(0x9d499311u, 0x3c833f1cu)), array(U64(0xeb05f63eu, 0x3fe6efffu), U64(0x1ae22d53u, 0xbc7e8004u)), array(U64(0x0e869780u, 0x3fe71000u), U64(0x71097952u, 0x3c7bff66u)), array(U64(0xfc67e912u, 0x3fe72fffu), U64(0x26bd8724u, 0x3c8c00e2u)), array(U64(0xdf81116au, 0x3fe74fffu), U64(0x6ef101d2u, 0xbc6e0291u)), array(U64(0x0f679c90u, 0x3fe77000u), U64(0xcd549c74u, 0xbc67fc71u)), array(U64(0xfa7ec835u, 0x3fe78fffu), U64(0x9ef50483u, 0x3c81bec1u)), array(U64(0xfe20c2e6u, 0x3fe7afffu), U64(0x29cc6465u, 0xbc707e17u)), array(U64(0xed3fc900u, 0x3fe7cfffu), U64(0x087b8b1cu, 0xbc808072u)), array(U64(0xe9261a76u, 0x3fe7efffu), U64(0x6d9df9aeu, 0x3c8dc028u)), array(U64(0x049ca3e8u, 0x3fe81000u), U64(0x51e54c33u, 0x3c897fd2u)), array(U64(0x17932c8fu, 0x3fe83000u), U64(0xb630f381u, 0xbc8afee9u)), array(U64(0x0633739cu, 0x3fe85000u), U64(0x6b6535bcu, 0x3c89bfbfu)), array(U64(0x204289c6u, 0x3fe87000u), U64(0xf3117b75u, 0xbc8bbf65u)), array(U64(0xebf57904u, 0x3fe88fffu), U64(0xa23dcb57u, 0xbc89006eu)), array(U64(0x22bc04dfu, 0x3fe8b000u), U64(0x38e04b0au, 0xbc7d00dfu)), array(U64(0xe50c1b8au, 0x3fe8cfffu), U64(0x46ff9f05u, 0xbc880071u)), array(U64(0xfc918e43u, 0x3fe8efffu), U64(0xd07a7038u, 0x3c83817bu)), array(U64(0x1efa5fc7u, 0x3fe91000u), U64(0x76dfb403u, 0x3c893e91u)), array(U64(0x13467bb9u, 0x3fe93000u), U64(0x4b980276u, 0x3c7f804eu)), array(U64(0xe6ee076fu, 0x3fe94fffu), U64(0xd9ff622eu, 0xbc8f7ef0u)), array(U64(0xde3c12d1u, 0x3fe96fffu), U64(0x962638bau, 0xbc7082aau)), array(U64(0xf4458a0du, 0x3fe98fffu), U64(0x9164a8efu, 0xbc87801bu)), array(U64(0xdd982e3eu, 0x3fe9afffu), U64(0x8a5a9337u, 0xbc8740e0u)), array(U64(0xed49fb66u, 0x3fe9cfffu), U64(0xc19be000u, 0x3c3fce08u)), array(U64(0x20f19c51u, 0x3fe9f000u), U64(0x27885b0au, 0xbc8a3faau)), array(U64(0x1145b006u, 0x3fea1000u), U64(0x9958da56u, 0x3c74ff48u)), array(U64(0x07bbf6fau, 0x3fea3000u), U64(0x8a2b6d18u, 0x3c8cbeabu)), array(U64(0x10971d79u, 0x3fea5000u), U64(0xdd787930u, 0x3c88fecau)), array(U64(0x1df52e48u, 0x3fea7000u), U64(0x3dd8abdbu, 0xbc8f4176u)), array(U64(0x1c593352u, 0x3fea9000u), U64(0x84c27612u, 0xbc8ebf02u)), array(U64(0x2a4f3e4bu, 0x3feab000u), U64(0x3cff3f5fu, 0xbc69fd04u)), array(U64(0xd7ae1ed1u, 0x3feacfffu), U64(0x129070b4u, 0xbc823ee7u)), array(U64(0xee510478u, 0x3feaefffu), U64(0xe00edea3u, 0x3c6a063eu)), array(U64(0xdb650d5bu, 0x3feb0fffu), U64(0x381f0ab9u, 0x3c5a06c8u)), array(U64(0xfeaaca57u, 0x3feb2fffu), U64(0x74233c1du, 0xbc79011eu)), array(U64(0xd995badcu, 0x3feb4fffu), U64(0x68862a9fu, 0xbc79ff10u)), array(U64(0x249e659cu, 0x3feb7000u), U64(0xd0864f3eu, 0x3c8aff45u)), array(U64(0xf9871640u, 0x3feb8fffu), U64(0x96c2c3f9u, 0x3c7cfe77u)), array(U64(0xd204cb4fu, 0x3febafffu), U64(0xeef22bc4u, 0xbc63ff27u)), array(U64(0xd2415c45u, 0x3febcfffu), U64(0xee3bea21u, 0xbc6cffb7u)), array(U64(0xf86309dfu, 0x3febefffu), U64(0x972e0b5cu, 0xbc814103u)), array(U64(0xe1b57653u, 0x3fec0fffu), U64(0x94b76a19u, 0x3c8bc164u)), array(U64(0xf1fa57e3u, 0x3fec2fffu), U64(0x8d30c6edu, 0xbc64feefu)), array(U64(0xdcbfe424u, 0x3fec4fffu), U64(0xbcec4775u, 0xbc843f68u)), array(U64(0xed54b9f7u, 0x3fec6fffu), U64(0xf053e0ecu, 0x3c847ea3u)), array(U64(0xeb998fd5u, 0x3fec8fffu), U64(0x8df992f1u, 0x3c738306u)), array(U64(0x2125219au, 0x3fecb000u), U64(0x64180e04u, 0xbc68fd8eu)), array(U64(0xdd94469cu, 0x3feccfffu), U64(0x1cc7ea72u, 0x3c8e7ebeu)), array(U64(0xeafdc476u, 0x3fecefffu), U64(0xad9f88feu, 0x3c8ebe39u)), array(U64(0x169af82bu, 0x3fed1000u), U64(0xa8b95a71u, 0x3c757d91u)), array(U64(0x0d0ff71du, 0x3fed3000u), U64(0x6970c7dau, 0x3c89c190u)), array(U64(0xea790fc4u, 0x3fed4fffu), U64(0xc558fe0cu, 0xbc580e37u)), array(U64(0x2edc87e5u, 0x3fed7000u), U64(0x4dc10f44u, 0xbc7f80d6u)), array(U64(0x21dc82aau, 0x3fed9000u), U64(0x94fd5c5cu, 0xbc747c8fu)), array(U64(0xd86b0283u, 0x3fedafffu), U64(0xc521617eu, 0x3c8c7f1du)), array(U64(0x296c4739u, 0x3fedd000u), U64(0xb2ffb153u, 0x3c88019eu)), array(U64(0xe54490f5u, 0x3fedefffu), U64(0xc652cc89u, 0x3c6e00d2u)), array(U64(0xcdabf694u, 0x3fee0fffu), U64(0x202d69d2u, 0xbc7f8340u)), array(U64(0xdb52c8ddu, 0x3fee2fffu), U64(0xca1b0864u, 0x3c7b00c1u)), array(U64(0xf24216efu, 0x3fee4fffu), U64(0xb094ab51u, 0x3c72ffa8u)), array(U64(0xe88a5e11u, 0x3fee6fffu), U64(0xb1efbe59u, 0xbc57f673u)), array(U64(0x119eff0du, 0x3fee9000u), U64(0x5e0bc801u, 0xbc84808du)), array(U64(0xdfa51744u, 0x3feeafffu), U64(0xd54320b5u, 0x3c780006u)), array(U64(0x1a127fa1u, 0x3feed000u), U64(0x60565c92u, 0xbc5002f8u)), array(U64(0x07babcc4u, 0x3feef000u), U64(0x5d35e611u, 0xbc854044u)), array(U64(0xf57a8d02u, 0x3fef0fffu), U64(0x39ef9105u, 0xbc4ffb31u)), array(U64(0x1ee58ac7u, 0x3fef3000u), U64(0xf2731155u, 0x3c8a81acu)), array(U64(0xf5823494u, 0x3fef4fffu), U64(0xd4d7c743u, 0x3c8a3f41u)), array(U64(0xfca94c6bu, 0x3fef6fffu), U64(0x1c987875u, 0xbc6202f4u)), array(U64(0xe1f9c441u, 0x3fef8fffu), U64(0xf477e74bu, 0x3c777dd1u)), array(U64(0xd2e0e37eu, 0x3fefafffu), U64(0x9a7ca331u, 0xbc6f0119u)), array(U64(0x1c77e49eu, 0x3fefd000u), U64(0x4bceacb1u, 0x3c7181eeu)), array(U64(0xf7e0c331u, 0x3fefefffu), U64(0x0170875au, 0xbc6e0537u)), array(U64(0xf465606eu, 0x3ff00fffu), U64(0x491c0adau, 0xbc8a7eadu)), array(U64(0xf3867a58u, 0x3ff02fffu), U64(0xc3fcb2e0u, 0xbc977f69u)), array(U64(0xfdfc0d17u, 0x3ff04fffu), U64(0x34cb945bu, 0x3c97bffeu)), array(U64(0x03cd4d82u, 0x3ff07000u), U64(0xc0e456cbu, 0x3c820083u)), array(U64(0xf9f2cbe8u, 0x3ff08fffu), U64(0xbe37751au, 0xbc6dffdfu)), array(U64(0x010cda65u, 0x3ff0b000u), U64(0xaee626ebu, 0xbc913f7fu)), array(U64(0x01a4d338u, 0x3ff0d000u), U64(0x79489ff7u, 0x3c807dfau)), array(U64(0xfadafdfdu, 0x3ff0efffu), U64(0x70d66bc0u, 0xbc770405u)), array(U64(0x0bbafd96u, 0x3ff11000u), U64(0x846d0b62u, 0x3c8e80d4u)), array(U64(0xfae5f45du, 0x3ff12fffu), U64(0x64fd36efu, 0x3c9dbffau)), array(U64(0x0dd59ad9u, 0x3ff15000u), U64(0x701250aeu, 0x3c9a0077u)), array(U64(0x0f21559au, 0x3ff17000u), U64(0xe2e3deeeu, 0x3c8dfdf9u)), array(U64(0xfc275426u, 0x3ff18fffu), U64(0xdc3b7273u, 0x3c910030u)), array(U64(0x123d3c59u, 0x3ff1b000u), U64(0x80030188u, 0x3c997f79u)), array(U64(0xf8299eb7u, 0x3ff1cfffu), U64(0xab9f8c67u, 0xbc65f932u)), array(U64(0xf48ad400u, 0x3ff1efffu), U64(0x9da75bebu, 0x3c937fbfu)), array(U64(0x0c8b86a4u, 0x3ff21000u), U64(0x91fd5b22u, 0x3c9f806bu)), array(U64(0x03854303u, 0x3ff23000u), U64(0xeb9fbf33u, 0x3c93ffc2u)), array(U64(0xffbcf684u, 0x3ff24fffu), U64(0x7e2e2e72u, 0x3c7601e7u)), array(U64(0xf52921d9u, 0x3ff26fffu), U64(0x767f0c61u, 0x3c7ffcbbu)), array(U64(0x14933a3cu, 0x3ff29000u), U64(0x3c02412bu, 0xbc7202cau)), array(U64(0x14556313u, 0x3ff2b000u), U64(0x33f21f02u, 0xbc928082u)), array(U64(0xebfe523bu, 0x3ff2cfffu), U64(0x384fdcf2u, 0xbc88ff7eu)), array(U64(0x0bb8ad96u, 0x3ff2f000u), U64(0x503041c5u, 0xbc85ff51u)), array(U64(0xfb7ae2afu, 0x3ff30fffu), U64(0x885e289du, 0xbc810071u)), array(U64(0xfeac5f7fu, 0x3ff32fffu), U64(0x3fb7b715u, 0xbc91ff5du)), array(U64(0x0ca66756u, 0x3ff35000u), U64(0x228b82bdu, 0x3c957f82u)), array(U64(0x11fbf721u, 0x3ff37000u), U64(0xc40dd5ccu, 0x3c8000bau)), array(U64(0xf9592fb9u, 0x3ff38fffu), U64(0x2db2a751u, 0xbc943f9du)), array(U64(0x04ddd242u, 0x3ff3b000u), U64(0x707638e1u, 0x3c857f6bu)), array(U64(0xf5b2c957u, 0x3ff3cfffu), U64(0x10bf1231u, 0x3c7a023au)), array(U64(0xeab0b418u, 0x3ff3efffu), U64(0x66b152b0u, 0x3c987f6du)), array(U64(0x1532aff4u, 0x3ff41000u), U64(0x5f198524u, 0x3c67f837u)), array(U64(0x17478b29u, 0x3ff43000u), U64(0x72dc5143u, 0x3c8301e6u)), array(U64(0xe795b463u, 0x3ff44fffu), U64(0xb8b2895au, 0x3c89ff69u)), array(U64(0xe80475e0u, 0x3ff46fffu), U64(0x9bc2f254u, 0xbc95c0b1u)), array(U64(0xef6fc1e7u, 0x3ff48fffu), U64(0xf23a2a72u, 0x3c9b4009u)), array(U64(0xe5bea704u, 0x3ff4afffu), U64(0xbf0d7d45u, 0xbc94ffb7u)), array(U64(0x171027deu, 0x3ff4d000u), U64(0x71dc6a3du, 0xbc99c064u)), array(U64(0x0ff03ee2u, 0x3ff4f000u), U64(0x0b85531cu, 0x3c977f89u)), array(U64(0x12dc4bd1u, 0x3ff51000u), U64(0x7166a436u, 0x3c600465u)), array(U64(0x1605277au, 0x3ff53000u), U64(0xce233209u, 0xbc96bfceu)), array(U64(0xecdb704cu, 0x3ff54fffu), U64(0x0505a1d7u, 0xbc890272u)), array(U64(0xef5f54a9u, 0x3ff56fffu), U64(0x0ec96412u, 0x3c9bbfe6u)), array(U64(0x17e61012u, 0x3ff59000u), U64(0x81afef90u, 0x3c887ec5u)), array(U64(0x03c93e92u, 0x3ff5b000u), U64(0x0abf0cc0u, 0xbc9f4108u)), array(U64(0x1d4919bcu, 0x3ff5d000u), U64(0xfb254729u, 0xbc98812au)), array(U64(0xe7b87a89u, 0x3ff5efffu), U64(0x80ed6904u, 0xbc947eb7u))); - -const ENGINE_EXCEPTIONS: array, 1> = array, 1>(array(u32(0i), u32(0i), u32(0i))); - -const ENGINE_DOMAIN: f32 = 16.0f; - -const UCRT_ATAN2F_ATAN256: array = array(U64(0xb72cfde9u, 0x3faff55bu), U64(0xa71d52a6u, 0x3fb0f99eu), U64(0xbf082d58u, 0x3fb1f86du), U64(0x318a4a9au, 0x3fb2f719u), U64(0x0e7c559du, 0x3fb3f59fu), U64(0x677292fbu, 0x3fb4f3fdu), U64(0x4fd2d7b2u, 0x3fb5f232u), U64(0xdcea4b0cu, 0x3fb6f03bu), U64(0x2602f10eu, 0x3fb7ee18u), U64(0x4478fb28u, 0x3fb8ebc5u), U64(0x53cfdcf1u, 0x3fb9e941u), U64(0x71c722b8u, 0x3fbae68au), U64(0xbe6f07c3u, 0x3fbbe39eu), U64(0x5c3cca32u, 0x3fbce07cu), U64(0x701eba6eu, 0x3fbddd21u), U64(0x2190043au, 0x3fbed98cu), U64(0x9aac2f6du, 0x3fbfd5bau), U64(0x84212b3du, 0x3fc068d5u), U64(0xccf40881u, 0x3fc0e6adu), U64(0x41060850u, 0x3fc16465u), U64(0xfb043726u, 0x3fc1e1fau), U64(0x171a535cu, 0x3fc25f6eu), U64(0xb2fba1ffu, 0x3fc2dcbdu), U64(0xedeb99a3u, 0x3fc359e8u), U64(0xe8c6626cu, 0x3fc3d6eeu), U64(0xc6092a9eu, 0x3fc453ceu), U64(0xa9da4f17u, 0x3fc4d087u), U64(0xba11570au, 0x3fc54d18u), U64(0x1e3ec269u, 0x3fc5c981u), U64(0xffb3aa73u, 0x3fc645bfu), U64(0x898933d8u, 0x3fc6c1d4u), U64(0xe8a7d201u, 0x3fc73dbdu), U64(0x4bce5b02u, 0x3fc7b97bu), U64(0xe398ebc7u, 0x3fc8350bu), U64(0xe2879c28u, 0x3fc8b06eu), U64(0x7d050271u, 0x3fc92ba3u), U64(0xe96c8626u, 0x3fc9a6a8u), U64(0x601081a5u, 0x3fca217eu), U64(0x1b403279u, 0x3fca9c23u), U64(0x574d780bu, 0x3fcb1696u), U64(0x529260a2u, 0x3fcb90d7u), U64(0x4d768466u, 0x3fcc0ae5u), U64(0x8a742e6du, 0x3fcc84bfu), U64(0x4e1d5395u, 0x3fccfe65u), U64(0xdf205736u, 0x3fcd77d5u), U64(0x864c9d9du, 0x3fcdf110u), U64(0x8e96ec4du, 0x3fce6a14u), U64(0x451d980cu, 0x3fcee2e1u), U64(0xf92c80ddu, 0x3fcf5b75u), U64(0xfc40dbe4u, 0x3fcfd3d1u), U64(0x510665b5u, 0x3fd025fau), U64(0xa03d6290u, 0x3fd061eeu), U64(0x97d86362u, 0x3fd09dc5u), U64(0xe509acb3u, 0x3fd0d97eu), U64(0x362431c9u, 0x3fd1151au), U64(0x3a9ce546u, 0x3fd15097u), U64(0xa30bf178u, 0x3fd18bf5u), U64(0x212dd883u, 0x3fd1c735u), U64(0x67e47c95u, 0x3fd20255u), U64(0x2b381041u, 0x3fd23d56u), U64(0x2057ef45u, 0x3fd27837u), U64(0xfd9b5fe2u, 0x3fd2b2f7u), U64(0x7a823cfeu, 0x3fd2ed98u), U64(0x4fb58951u, 0x3fd32818u), U64(0x3707ebcbu, 0x3fd36277u), U64(0xeb76157bu, 0x3fd39cb4u), U64(0x29271134u, 0x3fd3d6d1u), U64(0xad6c7d32u, 0x3fd410cbu), U64(0x36c2af09u, 0x3fd44aa4u), U64(0x84d0c21bu, 0x3fd4845au), U64(0x586890e6u, 0x3fd4bdeeu), U64(0x73869978u, 0x3fd4f75fu), U64(0x9951cd49u, 0x3fd530adu), U64(0x8e1b4cd7u, 0x3fd569d8u), U64(0x175e0f4eu, 0x3fd5a2e0u), U64(0xfbbe768du, 0x3fd5dbc3u), U64(0x0309cfe1u, 0x3fd61484u), U64(0xf635c1c5u, 0x3fd64d1fu), U64(0x9f5fa6fdu, 0x3fd68597u), U64(0xc9cbd76cu, 0x3fd6bdeau), U64(0x41e4def0u, 0x3fd6f619u), U64(0xd53aa2a9u, 0x3fd72e22u), U64(0x52817501u, 0x3fd76607u), U64(0x899118d1u, 0x3fd79dc6u), U64(0x4b63b3f7u, 0x3fd7d560u), U64(0x6a14b1d0u, 0x3fd80cd4u), U64(0xb8df95d7u, 0x3fd84422u), U64(0x0c1ebedbu, 0x3fd87b4bu), U64(0x394a1b25u, 0x3fd8b24du), U64(0x16f5cde8u, 0x3fd8e929u), U64(0x7cd0c662u, 0x3fd91fdeu), U64(0x43a34907u, 0x3fd9566du), U64(0x454d6b18u, 0x3fd98cd5u), U64(0x5cc58107u, 0x3fd9c316u), U64(0x66168001u, 0x3fd9f930u), U64(0x3e5e530bu, 0x3fda2f23u), U64(0xc3cc23fcu, 0x3fda64eeu), U64(0xd59e98cfu, 0x3fda9a92u), U64(0x5422058bu, 0x3fdad00fu), U64(0x20ae9343u, 0x3fdb0564u), U64(0x1da65c6cu, 0x3fdb3a91u), U64(0x2e737efbu, 0x3fdb6f96u), U64(0x378624a5u, 0x3fdba473u), U64(0x1e528191u, 0x3fdbd928u), U64(0xc94ec9efu, 0x3fdc0db4u), U64(0x1ff11eb6u, 0x3fdc4219u), U64(0x0aad71f8u, 0x3fdc7655u), U64(0x72f3631bu, 0x3fdcaa68u), U64(0x432c1350u, 0x3fdcde53u), U64(0x66b7f2adu, 0x3fdd1215u), U64(0xc9ec862bu, 0x3fdd45aeu), U64(0x5a1226f4u, 0x3fdd791fu), U64(0x0561bb4fu, 0x3fddac67u), U64(0xbb026974u, 0x3fdddf85u), U64(0x6b0744afu, 0x3fde127bu), U64(0x066cf51au, 0x3fde4548u), U64(0x7f175a34u, 0x3fde77ebu), U64(0xc7cf28c4u, 0x3fdeaa65u), U64(0xd43f8434u, 0x3fdedcb6u), U64(0x98f393cfu, 0x3fdf0edeu), U64(0x0b541417u, 0x3fdf40ddu), U64(0x21a4e495u, 0x3fdf72b2u), U64(0xd3029258u, 0x3fdfa45du), U64(0x175fdf83u, 0x3fdfd5e0u), U64(0x73c1a40bu, 0x3fe0039cu), U64(0x1e82422du, 0x3fe01c34u), U64(0x09250488u, 0x3fe034b7u), U64(0x314342e5u, 0x3fe04d25u), U64(0x94db30cfu, 0x3fe0657eu), U64(0x324e9b38u, 0x3fe07dc3u), U64(0x0861a58fu, 0x3fe095f3u), U64(0x1639866cu, 0x3fe0ae0eu), U64(0x5b5b43dau, 0x3fe0c614u), U64(0xd7aa6f7cu, 0x3fe0de05u), U64(0x8b67e295u, 0x3fe0f5e2u), U64(0x77307a0du, 0x3fe10daau), U64(0x9bfbd2a8u, 0x3fe1255du), U64(0xfb1b056eu, 0x3fe13cfbu), U64(0x96376469u, 0x3fe15485u), U64(0x6f5137e1u, 0x3fe16bfau), U64(0x88be7c13u, 0x3fe1835au), U64(0xe5299f99u, 0x3fe19aa5u), U64(0x87904284u, 0x3fe1b1dcu), U64(0x7341f64fu, 0x3fe1c8feu), U64(0xabdefeb3u, 0x3fe1e00bu), U64(0x3557138au, 0x3fe1f704u), U64(0x13e823b1u, 0x3fe20de8u), U64(0x4c1d192au, 0x3fe224b7u), U64(0xe2cc9e6au, 0x3fe23b71u), U64(0xdd17e501u, 0x3fe25217u), U64(0x40696da6u, 0x3fe268a9u), U64(0x1273d1b3u, 0x3fe27f26u), U64(0x59308e30u, 0x3fe2958eu), U64(0x1aded073u, 0x3fe2abe2u), U64(0x5e024465u, 0x3fe2c221u), U64(0x2961e48bu, 0x3fe2d84cu), U64(0x8406cbcau, 0x3fe2ee62u), U64(0x753b090au, 0x3fe30464u), U64(0x048874beu, 0x3fe31a52u), U64(0x39b78856u, 0x3fe3302bu), U64(0x1cce37bbu, 0x3fe345f0u), U64(0xb60eccceu, 0x3fe35ba0u), U64(0x0df6c503u, 0x3fe3713du), U64(0x2d3db11eu, 0x3fe386c5u), U64(0x1cd41719u, 0x3fe39c39u), U64(0xe5e2564au, 0x3fe3b198u), U64(0x91c78dc4u, 0x3fe3c6e4u), U64(0x2a188504u, 0x3fe3dc1cu), U64(0xb89e96f4u, 0x3fe3f13fu), U64(0x47569f48u, 0x3fe4064fu), U64(0xe06fea41u, 0x3fe41b4au), U64(0x8e4b26d5u, 0x3fe43032u), U64(0x5b795b55u, 0x3fe44506u), U64(0x52badc7fu, 0x3fe459c6u), U64(0x7efe4715u, 0x3fe46e72u), U64(0xeb5f7bfdu, 0x3fe4830au), U64(0xa3269ee1u, 0x3fe4978fu), U64(0xb1c71762u, 0x3fe4ac00u), U64(0x22de94e4u, 0x3fe4c05eu), U64(0x023414e8u, 0x3fe4d4a8u), U64(0x5bb6ec04u, 0x3fe4e8deu), U64(0x3b7dd17eu, 0x3fe4fd01u), U64(0xadc5ed81u, 0x3fe51110u), U64(0xbef1e9fau, 0x3fe5250cu), U64(0x7b89061eu, 0x3fe538f5u), U64(0xf0362c8fu, 0x3fe54ccau), U64(0x29c70c34u, 0x3fe5608du), U64(0x352b33b9u, 0x3fe5743cu), U64(0x1f732fbau, 0x3fe587d8u), U64(0xf5cfab9du, 0x3fe59b60u), U64(0xc5909517u, 0x3fe5aed6u), U64(0x9c244260u, 0x3fe5c239u), U64(0x87169b18u, 0x3fe5d589u), U64(0x941043cfu, 0x3fe5e8c6u), U64(0xd0d5cc49u, 0x3fe5fbf0u), U64(0x4b46e05eu, 0x3fe60f08u), U64(0x115d7b8du, 0x3fe6220du), U64(0x312d1f3bu, 0x3fe634ffu), U64(0xb8e20b8fu, 0x3fe647deu), U64(0xb6c07b02u, 0x3fe65aabu), U64(0x3923e086u, 0x3fe66d66u), U64(0x4e7e2857u, 0x3fe6800eu), U64(0x0556fb6au, 0x3fe692a4u), U64(0x6c4b0575u, 0x3fe6a527u), U64(0x920b3d98u, 0x3fe6b798u), U64(0x855c3198u, 0x3fe6c9f7u), U64(0x551553aeu, 0x3fe6dc44u), U64(0x10204aefu, 0x3fe6ee7fu), U64(0xc5784633u, 0x3fe700a7u), U64(0x84295198u, 0x3fe712beu), U64(0x5b4fae7bu, 0x3fe724c3u), U64(0x5a172dffu, 0x3fe736b6u), U64(0x8fba8e0fu, 0x3fe74897u), U64(0x0b82d8d8u, 0x3fe75a67u), U64(0xdcc6c6c0u, 0x3fe76c24u), U64(0x12ea22c7u, 0x3fe77dd1u), U64(0xbd5d315eu, 0x3fe78f6bu), U64(0xeb9c19a2u, 0x3fe7a0f4u), U64(0xad2e50fdu, 0x3fe7b26cu), U64(0x11a6092bu, 0x3fe7c3d3u), U64(0x289fa093u, 0x3fe7d528u), U64(0x01c114fdu, 0x3fe7e66cu), U64(0xacb97898u, 0x3fe7f79eu), U64(0x3940694au, 0x3fe808c0u), U64(0xb7158a4cu, 0x3fe819d0u), U64(0x36000005u, 0x3fe82ad0u), U64(0xc5cdee22u, 0x3fe83bbeu), U64(0x7653f7eau, 0x3fe84c9cu), U64(0x576cc2c5u, 0x3fe85d69u), U64(0x78f87ae5u, 0x3fe86e25u), U64(0xeadc5a2au, 0x3fe87ed0u), U64(0xbd023118u, 0x3fe88f6bu), U64(0xff57f1f7u, 0x3fe89ff5u), U64(0xc1cf3dfeu, 0x3fe8b06fu), U64(0x145cf49du, 0x3fe8c0d9u), U64(0x06f8c4cau, 0x3fe8d132u), U64(0xa99cc05du, 0x3fe8e17au), U64(0x0c44f167u, 0x3fe8f1b3u), U64(0x3eeef187u, 0x3fe901dbu), U64(0x5199833bu, 0x3fe911f3u), U64(0x54442d18u, 0x3fe921fbu)); - -const UCRT_TANH_HI: array = array(U64(0x00000000u, 0x3ff00000u), U64(0xd0000000u, 0x3ff059b0u), U64(0x60000000u, 0x3ff0b558u), U64(0xd0000000u, 0x3ff11301u), U64(0x30000000u, 0x3ff172b8u), U64(0x30000000u, 0x3ff1d487u), U64(0x60000000u, 0x3ff2387au), U64(0xf0000000u, 0x3ff29e9du), U64(0x00000000u, 0x3ff306feu), U64(0x30000000u, 0x3ff371a7u), U64(0x40000000u, 0x3ff3dea6u), U64(0x60000000u, 0x3ff44e08u), U64(0xd0000000u, 0x3ff4bfdau), U64(0x50000000u, 0x3ff5342bu), U64(0xd0000000u, 0x3ff5ab07u), U64(0xb0000000u, 0x3ff6247eu), U64(0x60000000u, 0x3ff6a09eu), U64(0xe0000000u, 0x3ff71f75u), U64(0x70000000u, 0x3ff7a114u), U64(0x90000000u, 0x3ff82589u), U64(0x40000000u, 0x3ff8ace5u), U64(0xb0000000u, 0x3ff93737u), U64(0x80000000u, 0x3ff9c491u), U64(0xb0000000u, 0x3ffa5503u), U64(0x90000000u, 0x3ffae89fu), U64(0xf0000000u, 0x3ffb7f76u), U64(0xd0000000u, 0x3ffc199bu), U64(0xd0000000u, 0x3ffcb720u), U64(0xd0000000u, 0x3ffd5818u), U64(0x30000000u, 0x3ffdfc97u), U64(0xa0000000u, 0x3ffea4afu), U64(0x50000000u, 0x3fff5076u)); - -const UCRT_TANH_LO: array = array(U64(0x00000000u, 0x00000000u), U64(0xa1d73e2au, 0x3e48ac2bu), U64(0x1ec53172u, 0x3e69f312u), U64(0xa4ebbf1bu, 0x3df25b50u), U64(0xf5b9bef9u, 0x3e68faa2u), U64(0xa7805b80u, 0x3e368b9au), U64(0x70cd83f6u, 0x3e6ceac4u), U64(0x84b09745u, 0x3e547f7bu), U64(0x2a5bd1abu, 0x3e64636eu), U64(0x2a9c5154u, 0x3e5ceaa7u), U64(0x446b6824u, 0x3e682468u), U64(0x40c4dbd0u, 0x3e18624bu), U64(0x9c750e5eu, 0x3e54d8a8u), U64(0x077c2a0fu, 0x3e5a753eu), U64(0x52b19260u, 0x3e6a90a8u), U64(0x58f87d03u, 0x3e0d2ac2u), U64(0x32422cbfu, 0x3e59fcefu), U64(0xe7ba46e2u, 0x3e61d8beu), U64(0x36bea881u, 0x3e4f580cu), U64(0x25159f11u, 0x3e62999cu), U64(0xdadd3e2au, 0x3e415506u), U64(0x9e8a0388u, 0x3e29b8bcu), U64(0x80e3e236u, 0x3e451f84u), U64(0xe45a1224u, 0x3e41f12au), U64(0x5abd0e6au, 0x3e62b5a7u), U64(0x37553d84u, 0x3e47daf2u), U64(0x38444196u, 0x3e6b0aa5u), U64(0xd22a0798u, 0x3e69df20u), U64(0x0e4bb40bu, 0x3e69f749u), U64(0xf5cb4656u, 0x3e4bdcdau), U64(0xcc2c7b9du, 0x3e452486u), U64(0x80ce9f09u, 0x3e66dc8au)); - -const UCRT_EXP_MUL: array = array(U64(0x00000000u, 0x3ff00000u), U64(0x3e778061u, 0x3ff02c9au), U64(0xd3158574u, 0x3ff059b0u), U64(0x18759bc8u, 0x3ff08745u), U64(0x6cf9890fu, 0x3ff0b558u), U64(0x32d3d1a2u, 0x3ff0e3ecu), U64(0xd0125b51u, 0x3ff11301u), U64(0xaea92de0u, 0x3ff1429au), U64(0x3c7d517bu, 0x3ff172b8u), U64(0xeb6fcb75u, 0x3ff1a35bu), U64(0x3168b9aau, 0x3ff1d487u), U64(0x88628cd6u, 0x3ff2063bu), U64(0x6e756238u, 0x3ff2387au), U64(0x65e27cddu, 0x3ff26b45u), U64(0xf51fdee1u, 0x3ff29e9du), U64(0xa6e4030bu, 0x3ff2d285u), U64(0x0a31b715u, 0x3ff306feu), U64(0xb26416ffu, 0x3ff33c08u), U64(0x373aa9cbu, 0x3ff371a7u), U64(0x34e59ff7u, 0x3ff3a7dbu), U64(0x4c123422u, 0x3ff3dea6u), U64(0x21f72e2au, 0x3ff4160au), U64(0x6061892du, 0x3ff44e08u), U64(0xb5c13cd0u, 0x3ff486a2u), U64(0xd5362a27u, 0x3ff4bfdau), U64(0x769d2ca7u, 0x3ff4f9b2u), U64(0x569d4f82u, 0x3ff5342bu), U64(0x36b527dau, 0x3ff56f47u), U64(0xdd485429u, 0x3ff5ab07u), U64(0x15ad2148u, 0x3ff5e76fu), U64(0xb03a5585u, 0x3ff6247eu), U64(0x82552225u, 0x3ff66238u), U64(0x667f3bcdu, 0x3ff6a09eu), U64(0x3c651a2fu, 0x3ff6dfb2u), U64(0xe8ec5f74u, 0x3ff71f75u), U64(0x564267c9u, 0x3ff75febu), U64(0x73eb0187u, 0x3ff7a114u), U64(0x36cf4e62u, 0x3ff7e2f3u), U64(0x994cce13u, 0x3ff82589u), U64(0x9b4492edu, 0x3ff868d9u), U64(0x422aa0dbu, 0x3ff8ace5u), U64(0x99157736u, 0x3ff8f1aeu), U64(0xb0cdc5e5u, 0x3ff93737u), U64(0x9fde4e50u, 0x3ff97d82u), U64(0x82a3f090u, 0x3ff9c491u), U64(0x7b5de565u, 0x3ffa0c66u), U64(0xb23e255du, 0x3ffa5503u), U64(0x5579fdbfu, 0x3ffa9e6bu), U64(0x995ad3adu, 0x3ffae89fu), U64(0xb84f15fbu, 0x3ffb33a2u), U64(0xf2fb5e47u, 0x3ffb7f76u), U64(0x904bc1d2u, 0x3ffbcc1eu), U64(0xdd85529cu, 0x3ffc199bu), U64(0x2e57d14bu, 0x3ffc67f1u), U64(0xdcef9069u, 0x3ffcb720u), U64(0x4a07897cu, 0x3ffd072du), U64(0xdcfba487u, 0x3ffd5818u), U64(0x03db3285u, 0x3ffda9e6u), U64(0x337b9b5fu, 0x3ffdfc97u), U64(0xe78b3ff6u, 0x3ffe502eu), U64(0xa2a490dau, 0x3ffea4afu), U64(0xee615a27u, 0x3ffefa1bu), U64(0x5b6e4540u, 0x3fff5076u), U64(0x819e90d8u, 0x3fffa7c1u)); - -const UCRT_EXP_LO: array = array(U64(0x00000000u, 0x00000000u), U64(0xc1dcdef9u, 0x3e6cef00u), U64(0xa1d73e2au, 0x3e48ac2bu), U64(0x901186beu, 0x3e60eb37u), U64(0x1ec53172u, 0x3e69f312u), U64(0x10103a17u, 0x3e469e8du), U64(0xa4ebbf1au, 0x3df25b50u), U64(0xbf668203u, 0x3e6d525bu), U64(0xf5b9bef9u, 0x3e68faa2u), U64(0xea796d31u, 0x3e66df96u), U64(0xa7805b80u, 0x3e368b9au), U64(0xac771dd6u, 0x3e60c519u), U64(0x70cd83f5u, 0x3e6ceac4u), U64(0x7495e99cu, 0x3e5789f3u), U64(0x84b09745u, 0x3e547f7bu), U64(0x2d002475u, 0x3e5b900cu), U64(0x2a5bd1abu, 0x3e64636eu), U64(0xfa64e430u, 0x3e4320b7u), U64(0x2a9c5154u, 0x3e5ceaa7u), U64(0xdba86f24u, 0x3e53967fu), U64(0x446b6824u, 0x3e682468u), U64(0x9f84325bu, 0x3e3f72e2u), U64(0x40c4dbd0u, 0x3e18624bu), U64(0x404f068eu, 0x3e5704f3u), U64(0x9c750e5eu, 0x3e54d8a8u), U64(0x9ab4cf62u, 0x3e5a74b2u), U64(0x077c2a0fu, 0x3e5a753eu), U64(0x699bb2c0u, 0x3e5ad49fu), U64(0x52b19260u, 0x3e6a90a8u), U64(0x21ba6f93u, 0x3e56b485u), U64(0x58f87d03u, 0x3e0d2ac2u), U64(0x24893ecfu, 0x3e42a911u), U64(0x32422cbeu, 0x3e59fcefu), U64(0x5de441c5u, 0x3e68ca34u), U64(0xe7ba46e1u, 0x3e61d8beu), U64(0x22fdba6au, 0x3e59099fu), U64(0x36bea881u, 0x3e4f580cu), U64(0x8841740au, 0x3e5b3d39u), U64(0x25159f11u, 0x3e62999cu), U64(0xd901c83bu, 0x3e668925u), U64(0xdadd3e2au, 0x3e415506u), U64(0x6c57304eu, 0x3e622aeeu), U64(0x9e8a0387u, 0x3e29b8bcu), U64(0x9f173d24u, 0x3e6fbc9cu), U64(0x80e3e235u, 0x3e451f84u), U64(0xc96535b5u, 0x3e66bbcau), U64(0xe45a1224u, 0x3e41f12au), U64(0xfd0fac90u, 0x3e55e7f6u), U64(0x5abd0e69u, 0x3e62b5a7u), U64(0xf5ed7fa1u, 0x3e609e2bu), U64(0x37553d84u, 0x3e47daf2u), U64(0x891ee83du, 0x3e12f074u), U64(0x38444196u, 0x3e6b0aa5u), U64(0x9694426fu, 0x3e6cafa2u), U64(0xd22a0797u, 0x3e69df20u), U64(0xf71a1e45u, 0x3e640f12u), U64(0x0e4bb40bu, 0x3e69f749u), U64(0x2b84600du, 0x3e4ed994u), U64(0xf5cb4656u, 0x3e4bdcdau), U64(0xd89cf44cu, 0x3e5e2cffu), U64(0xcc2c7b9du, 0x3e452486u), U64(0x4eee3fa4u, 0x3e6cc2b4u), U64(0x80ce9f09u, 0x3e66dc8au), U64(0x82e90a7eu, 0x3e39e90du)); - -const UCRT_EXP_HI: array = array(U64(0x00000000u, 0x3ff00000u), U64(0x30000000u, 0x3ff02c9au), U64(0xd0000000u, 0x3ff059b0u), U64(0x10000000u, 0x3ff08745u), U64(0x60000000u, 0x3ff0b558u), U64(0x30000000u, 0x3ff0e3ecu), U64(0xd0000000u, 0x3ff11301u), U64(0xa0000000u, 0x3ff1429au), U64(0x30000000u, 0x3ff172b8u), U64(0xe0000000u, 0x3ff1a35bu), U64(0x30000000u, 0x3ff1d487u), U64(0x80000000u, 0x3ff2063bu), U64(0x60000000u, 0x3ff2387au), U64(0x60000000u, 0x3ff26b45u), U64(0xf0000000u, 0x3ff29e9du), U64(0xa0000000u, 0x3ff2d285u), U64(0x00000000u, 0x3ff306feu), U64(0xb0000000u, 0x3ff33c08u), U64(0x30000000u, 0x3ff371a7u), U64(0x30000000u, 0x3ff3a7dbu), U64(0x40000000u, 0x3ff3dea6u), U64(0x20000000u, 0x3ff4160au), U64(0x60000000u, 0x3ff44e08u), U64(0xb0000000u, 0x3ff486a2u), U64(0xd0000000u, 0x3ff4bfdau), U64(0x70000000u, 0x3ff4f9b2u), U64(0x50000000u, 0x3ff5342bu), U64(0x30000000u, 0x3ff56f47u), U64(0xd0000000u, 0x3ff5ab07u), U64(0x10000000u, 0x3ff5e76fu), U64(0xb0000000u, 0x3ff6247eu), U64(0x80000000u, 0x3ff66238u), U64(0x60000000u, 0x3ff6a09eu), U64(0x30000000u, 0x3ff6dfb2u), U64(0xe0000000u, 0x3ff71f75u), U64(0x50000000u, 0x3ff75febu), U64(0x70000000u, 0x3ff7a114u), U64(0x30000000u, 0x3ff7e2f3u), U64(0x90000000u, 0x3ff82589u), U64(0x90000000u, 0x3ff868d9u), U64(0x40000000u, 0x3ff8ace5u), U64(0x90000000u, 0x3ff8f1aeu), U64(0xb0000000u, 0x3ff93737u), U64(0x90000000u, 0x3ff97d82u), U64(0x80000000u, 0x3ff9c491u), U64(0x70000000u, 0x3ffa0c66u), U64(0xb0000000u, 0x3ffa5503u), U64(0x50000000u, 0x3ffa9e6bu), U64(0x90000000u, 0x3ffae89fu), U64(0xb0000000u, 0x3ffb33a2u), U64(0xf0000000u, 0x3ffb7f76u), U64(0x90000000u, 0x3ffbcc1eu), U64(0xd0000000u, 0x3ffc199bu), U64(0x20000000u, 0x3ffc67f1u), U64(0xd0000000u, 0x3ffcb720u), U64(0x40000000u, 0x3ffd072du), U64(0xd0000000u, 0x3ffd5818u), U64(0x00000000u, 0x3ffda9e6u), U64(0x30000000u, 0x3ffdfc97u), U64(0xe0000000u, 0x3ffe502eu), U64(0xa0000000u, 0x3ffea4afu), U64(0xe0000000u, 0x3ffefa1bu), U64(0x50000000u, 0x3fff5076u), U64(0x80000000u, 0x3fffa7c1u)); - -const UCRT_POW_T1: array = array(U64(0x00000000u, 0x40000000u), U64(0x00000000u, 0x3fffe000u), U64(0x00000000u, 0x3fffc000u), U64(0x00000000u, 0x3fffa000u), U64(0x00000000u, 0x3fff8000u), U64(0x00000000u, 0x3fff6000u), U64(0x00000000u, 0x3fff4000u), U64(0x00000000u, 0x3fff2000u), U64(0x00000000u, 0x3fff0000u), U64(0x00000000u, 0x3ffee000u), U64(0x00000000u, 0x3ffec000u), U64(0x00000000u, 0x3ffea000u), U64(0x00000000u, 0x3ffe9000u), U64(0x00000000u, 0x3ffe7000u), U64(0x00000000u, 0x3ffe5000u), U64(0x00000000u, 0x3ffe3000u), U64(0x00000000u, 0x3ffe1000u), U64(0x00000000u, 0x3ffe0000u), U64(0x00000000u, 0x3ffde000u), U64(0x00000000u, 0x3ffdc000u), U64(0x00000000u, 0x3ffda000u), U64(0x00000000u, 0x3ffd9000u), U64(0x00000000u, 0x3ffd7000u), U64(0x00000000u, 0x3ffd5000u), U64(0x00000000u, 0x3ffd4000u), U64(0x00000000u, 0x3ffd2000u), U64(0x00000000u, 0x3ffd0000u), U64(0x00000000u, 0x3ffcf000u), U64(0x00000000u, 0x3ffcd000u), U64(0x00000000u, 0x3ffcb000u), U64(0x00000000u, 0x3ffca000u), U64(0x00000000u, 0x3ffc8000u), U64(0x00000000u, 0x3ffc7000u), U64(0x00000000u, 0x3ffc5000u), U64(0x00000000u, 0x3ffc3000u), U64(0x00000000u, 0x3ffc2000u), U64(0x00000000u, 0x3ffc0000u), U64(0x00000000u, 0x3ffbf000u), U64(0x00000000u, 0x3ffbd000u), U64(0x00000000u, 0x3ffbc000u), U64(0x00000000u, 0x3ffba000u), U64(0x00000000u, 0x3ffb9000u), U64(0x00000000u, 0x3ffb7000u), U64(0x00000000u, 0x3ffb6000u), U64(0x00000000u, 0x3ffb4000u), U64(0x00000000u, 0x3ffb3000u), U64(0x00000000u, 0x3ffb2000u), U64(0x00000000u, 0x3ffb0000u), U64(0x00000000u, 0x3ffaf000u), U64(0x00000000u, 0x3ffad000u), U64(0x00000000u, 0x3ffac000u), U64(0x00000000u, 0x3ffaa000u), U64(0x00000000u, 0x3ffa9000u), U64(0x00000000u, 0x3ffa8000u), U64(0x00000000u, 0x3ffa6000u), U64(0x00000000u, 0x3ffa5000u), U64(0x00000000u, 0x3ffa4000u), U64(0x00000000u, 0x3ffa2000u), U64(0x00000000u, 0x3ffa1000u), U64(0x00000000u, 0x3ffa0000u), U64(0x00000000u, 0x3ff9e000u), U64(0x00000000u, 0x3ff9d000u), U64(0x00000000u, 0x3ff9c000u), U64(0x00000000u, 0x3ff9a000u), U64(0x00000000u, 0x3ff99000u), U64(0x00000000u, 0x3ff98000u), U64(0x00000000u, 0x3ff97000u), U64(0x00000000u, 0x3ff95000u), U64(0x00000000u, 0x3ff94000u), U64(0x00000000u, 0x3ff93000u), U64(0x00000000u, 0x3ff92000u), U64(0x00000000u, 0x3ff90000u), U64(0x00000000u, 0x3ff8f000u), U64(0x00000000u, 0x3ff8e000u), U64(0x00000000u, 0x3ff8d000u), U64(0x00000000u, 0x3ff8b000u), U64(0x00000000u, 0x3ff8a000u), U64(0x00000000u, 0x3ff89000u), U64(0x00000000u, 0x3ff88000u), U64(0x00000000u, 0x3ff87000u), U64(0x00000000u, 0x3ff86000u), U64(0x00000000u, 0x3ff84000u), U64(0x00000000u, 0x3ff83000u), U64(0x00000000u, 0x3ff82000u), U64(0x00000000u, 0x3ff81000u), U64(0x00000000u, 0x3ff80000u), U64(0x00000000u, 0x3ff7f000u), U64(0x00000000u, 0x3ff7e000u), U64(0x00000000u, 0x3ff7d000u), U64(0x00000000u, 0x3ff7b000u), U64(0x00000000u, 0x3ff7a000u), U64(0x00000000u, 0x3ff79000u), U64(0x00000000u, 0x3ff78000u), U64(0x00000000u, 0x3ff77000u), U64(0x00000000u, 0x3ff76000u), U64(0x00000000u, 0x3ff75000u), U64(0x00000000u, 0x3ff74000u), U64(0x00000000u, 0x3ff73000u), U64(0x00000000u, 0x3ff72000u), U64(0x00000000u, 0x3ff71000u), U64(0x00000000u, 0x3ff70000u), U64(0x00000000u, 0x3ff6f000u), U64(0x00000000u, 0x3ff6e000u), U64(0x00000000u, 0x3ff6d000u), U64(0x00000000u, 0x3ff6c000u), U64(0x00000000u, 0x3ff6b000u), U64(0x00000000u, 0x3ff6a000u), U64(0x00000000u, 0x3ff69000u), U64(0x00000000u, 0x3ff68000u), U64(0x00000000u, 0x3ff67000u), U64(0x00000000u, 0x3ff66000u), U64(0x00000000u, 0x3ff65000u), U64(0x00000000u, 0x3ff64000u), U64(0x00000000u, 0x3ff63000u), U64(0x00000000u, 0x3ff62000u), U64(0x00000000u, 0x3ff61000u), U64(0x00000000u, 0x3ff60000u), U64(0x00000000u, 0x3ff5f000u), U64(0x00000000u, 0x3ff5e000u), U64(0x00000000u, 0x3ff5d000u), U64(0x00000000u, 0x3ff5c000u), U64(0x00000000u, 0x3ff5b000u), U64(0x00000000u, 0x3ff5a000u), U64(0x00000000u, 0x3ff59000u), U64(0x00000000u, 0x3ff58000u), U64(0x00000000u, 0x3ff58000u), U64(0x00000000u, 0x3ff57000u), U64(0x00000000u, 0x3ff56000u), U64(0x00000000u, 0x3ff55000u), U64(0x00000000u, 0x3ff54000u), U64(0x00000000u, 0x3ff53000u), U64(0x00000000u, 0x3ff52000u), U64(0x00000000u, 0x3ff51000u), U64(0x00000000u, 0x3ff50000u), U64(0x00000000u, 0x3ff50000u), U64(0x00000000u, 0x3ff4f000u), U64(0x00000000u, 0x3ff4e000u), U64(0x00000000u, 0x3ff4d000u), U64(0x00000000u, 0x3ff4c000u), U64(0x00000000u, 0x3ff4b000u), U64(0x00000000u, 0x3ff4a000u), U64(0x00000000u, 0x3ff4a000u), U64(0x00000000u, 0x3ff49000u), U64(0x00000000u, 0x3ff48000u), U64(0x00000000u, 0x3ff47000u), U64(0x00000000u, 0x3ff46000u), U64(0x00000000u, 0x3ff46000u), U64(0x00000000u, 0x3ff45000u), U64(0x00000000u, 0x3ff44000u), U64(0x00000000u, 0x3ff43000u), U64(0x00000000u, 0x3ff42000u), U64(0x00000000u, 0x3ff42000u), U64(0x00000000u, 0x3ff41000u), U64(0x00000000u, 0x3ff40000u), U64(0x00000000u, 0x3ff3f000u), U64(0x00000000u, 0x3ff3e000u), U64(0x00000000u, 0x3ff3e000u), U64(0x00000000u, 0x3ff3d000u), U64(0x00000000u, 0x3ff3c000u), U64(0x00000000u, 0x3ff3b000u), U64(0x00000000u, 0x3ff3b000u), U64(0x00000000u, 0x3ff3a000u), U64(0x00000000u, 0x3ff39000u), U64(0x00000000u, 0x3ff38000u), U64(0x00000000u, 0x3ff38000u), U64(0x00000000u, 0x3ff37000u), U64(0x00000000u, 0x3ff36000u), U64(0x00000000u, 0x3ff35000u), U64(0x00000000u, 0x3ff35000u), U64(0x00000000u, 0x3ff34000u), U64(0x00000000u, 0x3ff33000u), U64(0x00000000u, 0x3ff32000u), U64(0x00000000u, 0x3ff32000u), U64(0x00000000u, 0x3ff31000u), U64(0x00000000u, 0x3ff30000u), U64(0x00000000u, 0x3ff30000u), U64(0x00000000u, 0x3ff2f000u), U64(0x00000000u, 0x3ff2e000u), U64(0x00000000u, 0x3ff2e000u), U64(0x00000000u, 0x3ff2d000u), U64(0x00000000u, 0x3ff2c000u), U64(0x00000000u, 0x3ff2b000u), U64(0x00000000u, 0x3ff2b000u), U64(0x00000000u, 0x3ff2a000u), U64(0x00000000u, 0x3ff29000u), U64(0x00000000u, 0x3ff29000u), U64(0x00000000u, 0x3ff28000u), U64(0x00000000u, 0x3ff27000u), U64(0x00000000u, 0x3ff27000u), U64(0x00000000u, 0x3ff26000u), U64(0x00000000u, 0x3ff25000u), U64(0x00000000u, 0x3ff25000u), U64(0x00000000u, 0x3ff24000u), U64(0x00000000u, 0x3ff23000u), U64(0x00000000u, 0x3ff23000u), U64(0x00000000u, 0x3ff22000u), U64(0x00000000u, 0x3ff21000u), U64(0x00000000u, 0x3ff21000u), U64(0x00000000u, 0x3ff20000u), U64(0x00000000u, 0x3ff20000u), U64(0x00000000u, 0x3ff1f000u), U64(0x00000000u, 0x3ff1e000u), U64(0x00000000u, 0x3ff1e000u), U64(0x00000000u, 0x3ff1d000u), U64(0x00000000u, 0x3ff1c000u), U64(0x00000000u, 0x3ff1c000u), U64(0x00000000u, 0x3ff1b000u), U64(0x00000000u, 0x3ff1b000u), U64(0x00000000u, 0x3ff1a000u), U64(0x00000000u, 0x3ff19000u), U64(0x00000000u, 0x3ff19000u), U64(0x00000000u, 0x3ff18000u), U64(0x00000000u, 0x3ff18000u), U64(0x00000000u, 0x3ff17000u), U64(0x00000000u, 0x3ff16000u), U64(0x00000000u, 0x3ff16000u), U64(0x00000000u, 0x3ff15000u), U64(0x00000000u, 0x3ff15000u), U64(0x00000000u, 0x3ff14000u), U64(0x00000000u, 0x3ff13000u), U64(0x00000000u, 0x3ff13000u), U64(0x00000000u, 0x3ff12000u), U64(0x00000000u, 0x3ff12000u), U64(0x00000000u, 0x3ff11000u), U64(0x00000000u, 0x3ff11000u), U64(0x00000000u, 0x3ff10000u), U64(0x00000000u, 0x3ff0f000u), U64(0x00000000u, 0x3ff0f000u), U64(0x00000000u, 0x3ff0e000u), U64(0x00000000u, 0x3ff0e000u), U64(0x00000000u, 0x3ff0d000u), U64(0x00000000u, 0x3ff0d000u), U64(0x00000000u, 0x3ff0c000u), U64(0x00000000u, 0x3ff0c000u), U64(0x00000000u, 0x3ff0b000u), U64(0x00000000u, 0x3ff0a000u), U64(0x00000000u, 0x3ff0a000u), U64(0x00000000u, 0x3ff09000u), U64(0x00000000u, 0x3ff09000u), U64(0x00000000u, 0x3ff08000u), U64(0x00000000u, 0x3ff08000u), U64(0x00000000u, 0x3ff07000u), U64(0x00000000u, 0x3ff07000u), U64(0x00000000u, 0x3ff06000u), U64(0x00000000u, 0x3ff06000u), U64(0x00000000u, 0x3ff05000u), U64(0x00000000u, 0x3ff05000u), U64(0x00000000u, 0x3ff04000u), U64(0x00000000u, 0x3ff04000u), U64(0x00000000u, 0x3ff03000u), U64(0x00000000u, 0x3ff03000u), U64(0x00000000u, 0x3ff02000u), U64(0x00000000u, 0x3ff02000u), U64(0x00000000u, 0x3ff01000u), U64(0x00000000u, 0x3ff01000u), U64(0x00000000u, 0x3ff00000u), U64(0x00000000u, 0x3ff00000u)); - -const UCRT_POW_T2: array = array(U64(0x00000000u, 0x00000000u), U64(0xe01fe020u, 0x3effe01fu), U64(0x01fc07f0u, 0x3f1fc07fu), U64(0x1fa11caau, 0x3f31caa0u), U64(0x1f81f820u, 0x3f3f81f8u), U64(0x06ddaba6u, 0x3f488565u), U64(0x2909c560u, 0x3f519679u), U64(0x8c2ad433u, 0x3f57d910u), U64(0xf07c1f08u, 0x3f5f07c1u), U64(0x8b1c03ddu, 0x3f638ff0u), U64(0x03d980f6u, 0x3f680f66u), U64(0x7403d5d0u, 0x3f6d00f5u), U64(0x0b7672a0u, 0x3f331abfu), U64(0x5d43919bu, 0x3f506a96u), U64(0x0795ceb2u, 0x3f5ceb24u), U64(0xb834e67fu, 0x3f6522f3u), U64(0x3c3c3c3cu, 0x3f6c3c3cu), U64(0x1e01e01eu, 0x3f3e01e0u), U64(0xe21a291cu, 0x3f575b8fu), U64(0x403b9404u, 0x3f6403b9u), U64(0x7303b5ccu, 0x3f6cc0edu), U64(0xf3fc4da2u, 0x3f479118u), U64(0xe0b0ce46u, 0x3f5ed952u), U64(0xeae56404u, 0x3f695900u), U64(0x1d41d41du, 0x3f3d41d4u), U64(0xf16c69aeu, 0x3f5cb28fu), U64(0xdd80e866u, 0x3f696b1eu), U64(0x25fe30d9u, 0x3f4372e2u), U64(0x073615a2u, 0x3f60ad12u), U64(0x0397cdb3u, 0x3f6cdb2cu), U64(0x7b864407u, 0x3f52cc15u), U64(0xf7148404u, 0x3f664cb5u), U64(0x1c71c71cu, 0x3f3c71c7u), U64(0x1a930b84u, 0x3f6129a2u), U64(0x87f1e038u, 0x3f6f1e03u), U64(0xba80709bu, 0x3f5ad4e4u), U64(0x0381c0e0u, 0x3f6c0e07u), U64(0x1a362bb0u, 0x3f560fbau), U64(0x280dee96u, 0x3f6a5713u), U64(0x20f9ece9u, 0x3f53f596u), U64(0x83759f23u, 0x3f69f229u), U64(0x63fc8d5cu, 0x3f5478acu), U64(0xb4671656u, 0x3f6ad87bu), U64(0xfbb8148cu, 0x3f578b8eu), U64(0xd0369d03u, 0x3f6d0369u), U64(0x601b3748u, 0x3f5d212bu), U64(0x406c80d9u, 0x3f0b2036u), U64(0xb24547d1u, 0x3f629663u), U64(0x0d79435eu, 0x3f4435e5u), U64(0x2920bc03u, 0x3f67d0ffu), U64(0x15c06b16u, 0x3f55c06bu), U64(0x0fd7f954u, 0x3f6e3a5fu), U64(0xd4c77b03u, 0x3f61dec0u), U64(0x870ac52eu, 0x3f473289u), U64(0xa034da03u, 0x3f6a034du), U64(0xa2292856u, 0x3f5d041du), U64(0x1a41a41au, 0x3f3a41a4u), U64(0x8a39409du, 0x3f68550fu), U64(0xe92c0686u, 0x3f5b4fe5u), U64(0x1a01a01au, 0x3f3a01a0u), U64(0x2067b23au, 0x3f691d2au), U64(0xada0b4e5u, 0x3f5e7c5du), U64(0x25080ce1u, 0x3f468a77u), U64(0xaa21b490u, 0x3f6c49d4u), U64(0x33333333u, 0x3f633333u), U64(0x3b03fccfu, 0x3f54bc36u), U64(0x970e4f81u, 0x3f2c9f01u), U64(0xc6ef5b25u, 0x3f697617u), U64(0xadd3c0cau, 0x3f6161f9u), U64(0x6cb39806u, 0x3f5319feu), U64(0x1c451ab3u, 0x3f2f693au), U64(0x0321a9e2u, 0x3f6a9e24u), U64(0x3831f383u, 0x3f63831fu), U64(0xc4dcfc1cu, 0x3f5949ebu), U64(0x80c6980cu, 0x3f480c69u), U64(0xc5fe7403u, 0x3f6f9d00u), U64(0xd7e75347u, 0x3f69721eu), U64(0x0313381fu, 0x3f6381ecu), U64(0xaec12653u, 0x3f5b97c2u), U64(0x024ae3bau, 0x3f509ef3u), U64(0x18618618u, 0x3f386186u), U64(0xf00c2780u, 0x3f6e0184u), U64(0x657dba52u, 0x3f692ef5u), U64(0x05494030u, 0x3f649403u), U64(0x30303030u, 0x3f603030u), U64(0x80601806u, 0x3f580601u), U64(0x05fd017fu, 0x3f5017f4u), U64(0xd278e8ddu, 0x3f412a8au), U64(0x417d05f4u, 0x3f17d05fu), U64(0x5c02f7d6u, 0x3f6d6724u), U64(0xc1d986a9u, 0x3f6a4411u), U64(0x6c7316dfu, 0x3f6754d7u), U64(0xf149902fu, 0x3f649902u), U64(0x358c1a68u, 0x3f621023u), U64(0xd2a6c406u, 0x3f5f7390u), U64(0x05d5b2b1u, 0x3f5b2b08u), U64(0x745d1746u, 0x3f5745d1u), U64(0x07fa32c4u, 0x3f53c315u), U64(0x1b7af017u, 0x3f50a1fdu), U64(0xe3e0453au, 0x3f4bc36cu), U64(0x5c0b8170u, 0x3f4702e0u), U64(0x9300b793u, 0x3f4300b7u), U64(0x337c6cb1u, 0x3f3f76b4u), U64(0x1c860fb0u, 0x3f3a6268u), U64(0x16c16c17u, 0x3f36c16cu), U64(0x31a3cfc7u, 0x3f3490aau), U64(0x3729043eu, 0x3f33cd15u), U64(0x8d0bfd2eu, 0x3f3473a8u), U64(0x16816817u, 0x3f368168u), U64(0x16719f36u, 0x3f39f360u), U64(0x122f9016u, 0x3f3ec6a5u), U64(0xda5519cfu, 0x3f427c29u), U64(0x590b2164u, 0x3f4642c8u), U64(0x5606f00bu, 0x3f4ab5c4u), U64(0x0b11fd3cu, 0x3f4fd3b8u), U64(0xc6ba4eaau, 0x3f52cda0u), U64(0x60581606u, 0x3f560581u), U64(0xa4b7ef87u, 0x3f5990d0u), U64(0x40579d6fu, 0x3f5d6ee3u), U64(0xd9c54a69u, 0x3f60cf87u), U64(0x2620ae4cu, 0x3f631057u), U64(0x8ff522a2u, 0x3f65798cu), U64(0x02b580adu, 0x3f680ad6u), U64(0x4799546fu, 0x3f6ac3e2u), U64(0x02b1da46u, 0x3f6da461u), U64(0x01580560u, 0x3f158056u), U64(0x06b39a23u, 0x3f3ed3c5u), U64(0xe2970f60u, 0x3f4cbdd3u), U64(0x55555555u, 0x3f555555u), U64(0xee0bf805u, 0x3f5c979au), U64(0xe81fd58eu, 0x3f621291u), U64(0x500a9580u, 0x3f65feadu), U64(0xc5f02a3au, 0x3f6a0fd5u), U64(0x23898adcu, 0x3f6e45c2u), U64(0x15015015u, 0x3f350150u), U64(0xea64d422u, 0x3f4c7b16u), U64(0xbc14e5e1u, 0x3f57829cu), U64(0xb8589720u, 0x3f60877du), U64(0x4b5edceau, 0x3f65710eu), U64(0x4d1fc1c8u, 0x3f6a7dbbu), U64(0xa57eb503u, 0x3f6fad40u), U64(0xb00a5140u, 0x3f43fd6bu), U64(0xcb419ba9u, 0x3f54e78eu), U64(0x029100a4u, 0x3f600a44u), U64(0x5c28f5c3u, 0x3f65c28fu), U64(0xb2c0cc4au, 0x3f6b9c68u), U64(0xb9f34381u, 0x3f2978feu), U64(0x3bb6500au, 0x3f4ecf16u), U64(0x8b67ebb9u, 0x3f5be195u), U64(0x57dc9a3bu, 0x3f644e61u), U64(0xaa3f0ddfu, 0x3f6acc4bu), U64(0xcb2a247bu, 0x3f26a4cbu), U64(0x50505050u, 0x3f505050u), U64(0x39959819u, 0x3f5e0b44u), U64(0x6027f602u, 0x3f66027fu), U64(0x4b5e0db4u, 0x3f6d1e85u), U64(0x254813e2u, 0x3f4165e7u), U64(0xa9d716efu, 0x3f576646u), U64(0xf757ce88u, 0x3f632b48u), U64(0x4652a906u, 0x3f6ac1b2u), U64(0x13b13b14u, 0x3f33b13bu), U64(0xeb208984u, 0x3f5490e1u), U64(0x30fec66eu, 0x3f623858u), U64(0xcc111b7eu, 0x3f6a45a6u), U64(0x13813814u, 0x3f338138u), U64(0x2517b708u, 0x3f556f47u), U64(0xbc0e8f2au, 0x3f631be7u), U64(0x3e55f044u, 0x3f6b9cbfu), U64(0x5bc609a9u, 0x3f40e7d9u), U64(0x804d19e7u, 0x3f59e6b3u), U64(0xaf7963c2u, 0x3f65c8b6u), U64(0xd43bf402u, 0x3f6eb9dau), U64(0x5885fb37u, 0x3f4f1a51u), U64(0xd3d76c02u, 0x3f60eeb1u), U64(0x61a32026u, 0x3f6a3202u), U64(0x40260390u, 0x3f3c82acu), U64(0x84bda12fu, 0x3f5a12f6u), U64(0xfda2962cu, 0x3f669d43u), U64(0xc04b8097u, 0x3f02e025u), U64(0xb542804bu, 0x3f542804u), U64(0x02593f6au, 0x3f63f69bu), U64(0xb46e21fau, 0x3f6df31cu), U64(0x04ad012bu, 0x3f5012b4u), U64(0xe7820a7fu, 0x3f623925u), U64(0xc8253c82u, 0x3f6c8253u), U64(0xc02526e5u, 0x3f4b92ddu), U64(0x11602511u, 0x3f616025u), U64(0x439c9adfu, 0x3f6bf471u), U64(0x0939a85cu, 0x3f4a85c4u), U64(0xac024d16u, 0x3f6166f9u), U64(0x0125e227u, 0x3f6c44e1u), U64(0x8bbd90e5u, 0x3f4cebf4u), U64(0x92492492u, 0x3f624924u), U64(0x2ec0b673u, 0x3f6d6f2eu), U64(0x6af37c05u, 0x3f5159e2u), U64(0x40245402u, 0x3f640245u), U64(0x43f6f024u, 0x3f6f6f02u), U64(0x21579805u, 0x3f55e601u), U64(0xcf81b10fu, 0x3f668e18u), U64(0x12012012u, 0x3f320120u), U64(0x047dc11fu, 0x3f5c11f7u), U64(0xff70985eu, 0x3f69e878u), U64(0xfdc3a219u, 0x3f4779d9u), U64(0x5c957907u, 0x3f61eaceu), U64(0x450239e1u, 0x3f6e0d5bu), U64(0x73816367u, 0x3f548bf0u), U64(0x8dda5202u, 0x3f669480u), U64(0x2bae2b21u, 0x3f37c67fu), U64(0x69ee5847u, 0x3f5ee584u), U64(0xc0233c02u, 0x3f6c0233u), U64(0x328a7012u, 0x3f514e02u), U64(0x2057b573u, 0x3f656107u), U64(0x11811812u, 0x3f318118u), U64(0x6f5a1060u, 0x3f5e2864u), U64(0x84e6f1d7u, 0x3f6c0d12u), U64(0xf0c80459u, 0x3f523543u), U64(0xea4e1a09u, 0x3f663cbeu), U64(0xdd5c8cb8u, 0x3f3b9a3fu), U64(0x159a76d2u, 0x3f60be1cu), U64(0x688e4838u, 0x3f6e1d1au), U64(0xd72044d7u, 0x3f572044u), U64(0xdb81577bu, 0x3f691713u), U64(0xe9819b50u, 0x3f4ac73au), U64(0x4e904cf6u, 0x3f646033u), U64(0x11111111u, 0x3f311111u), U64(0x0441fef0u, 0x3f5feef8u), U64(0xfde021feu, 0x3f6de021u), U64(0xcc9686a0u, 0x3f57b7eau), U64(0xcd391fbcu, 0x3f69ead7u), U64(0x09804390u, 0x3f501956u), U64(0x1e8d2b32u, 0x3f664151u), U64(0xacf1ce96u, 0x3f4222b1u), U64(0x79b47582u, 0x3f62e29fu), U64(0x682e11cdu, 0x3f24f0d1u), U64(0x96771e4du, 0x3f5f9bb0u), U64(0x5dd96ae2u, 0x3f6e5ee4u), U64(0xa0429a04u, 0x3f5a0429u), U64(0x5f06c021u, 0x3f6bb74du), U64(0x04254fceu, 0x3f54fce4u), U64(0xeacbc402u, 0x3f695766u), U64(0x08421084u, 0x3f508421u), U64(0x71d5c338u, 0x3f673e53u), U64(0x3fbe3368u, 0x3f493052u), U64(0xf225f6c4u, 0x3f656b38u), U64(0x8d4fdf3bu, 0x3f426e97u), U64(0xe4eb0cc6u, 0x3f63dd40u), U64(0x73404146u, 0x3f397f7du), U64(0x2cc98af1u, 0x3f629398u), U64(0x10410410u, 0x3f304104u), U64(0x048ff7e4u, 0x3f618d6fu), U64(0xebc349deu, 0x3f2236a3u), U64(0xee53d18cu, 0x3f60c9f8u), U64(0x81020408u, 0x3f102040u), U64(0xa2f46ea6u, 0x3f60486cu), U64(0x10101010u, 0x3ef01010u), U64(0x02010080u, 0x3f600804u), U64(0x00000000u, 0x00000000u)); - -const UCRT_POW_T3: array = array(U64(0x00000000u, 0x00000000u), U64(0xa0000000u, 0x3f6ff00au), U64(0x60000000u, 0x3f7fe02au), U64(0x50000000u, 0x3f87dc47u), U64(0xb0000000u, 0x3f8fc0a8u), U64(0x40000000u, 0x3f93cea4u), U64(0x00000000u, 0x3f97b91bu), U64(0x20000000u, 0x3f9b9fc0u), U64(0x00000000u, 0x3f9f829bu), U64(0x80000000u, 0x3fa1b0d9u), U64(0xb0000000u, 0x3fa39e87u), U64(0xa0000000u, 0x3fa58a5bu), U64(0xf0000000u, 0x3fa77458u), U64(0x00000000u, 0x3fa95c83u), U64(0x70000000u, 0x3fab42ddu), U64(0x80000000u, 0x3fad276bu), U64(0xc0000000u, 0x3faf0a30u), U64(0x30000000u, 0x3fb07598u), U64(0xe0000000u, 0x3fb16536u), U64(0x20000000u, 0x3fb253f6u), U64(0x90000000u, 0x3fb341d7u), U64(0xb0000000u, 0x3fb42edcu), U64(0x30000000u, 0x3fb51b07u), U64(0xa0000000u, 0x3fb60658u), U64(0x80000000u, 0x3fb6f0d2u), U64(0x60000000u, 0x3fb7da76u), U64(0xd0000000u, 0x3fb8c345u), U64(0x40000000u, 0x3fb9ab42u), U64(0x30000000u, 0x3fba926du), U64(0x20000000u, 0x3fbb78c8u), U64(0x80000000u, 0x3fbc5e54u), U64(0xd0000000u, 0x3fbd4313u), U64(0x60000000u, 0x3fbe2707u), U64(0xc0000000u, 0x3fbf0a30u), U64(0x30000000u, 0x3fbfec91u), U64(0x10000000u, 0x3fc06715u), U64(0x70000000u, 0x3fc0d77eu), U64(0x80000000u, 0x3fc14785u), U64(0xd0000000u, 0x3fc1b72au), U64(0x10000000u, 0x3fc2266fu), U64(0xf0000000u, 0x3fc29552u), U64(0x10000000u, 0x3fc303d7u), U64(0x20000000u, 0x3fc371fcu), U64(0xb0000000u, 0x3fc3dfc2u), U64(0x60000000u, 0x3fc44d2bu), U64(0xf0000000u, 0x3fc4ba36u), U64(0xe0000000u, 0x3fc526e5u), U64(0xd0000000u, 0x3fc59338u), U64(0x70000000u, 0x3fc5ff30u), U64(0x40000000u, 0x3fc66acdu), U64(0xe0000000u, 0x3fc6d60fu), U64(0xf0000000u, 0x3fc740f8u), U64(0x00000000u, 0x3fc7ab89u), U64(0xa0000000u, 0x3fc815c0u), U64(0x60000000u, 0x3fc87fa0u), U64(0xd0000000u, 0x3fc8e928u), U64(0x90000000u, 0x3fc9525au), U64(0x20000000u, 0x3fc9bb36u), U64(0x10000000u, 0x3fca23bcu), U64(0xf0000000u, 0x3fca8becu), U64(0x40000000u, 0x3fcaf3c9u), U64(0x90000000u, 0x3fcb5b51u), U64(0x70000000u, 0x3fcbc286u), U64(0x50000000u, 0x3fcc2968u), U64(0xc0000000u, 0x3fcc8ff7u), U64(0x40000000u, 0x3fccf635u), U64(0x60000000u, 0x3fcd5c21u), U64(0xa0000000u, 0x3fcdc1bcu), U64(0x60000000u, 0x3fce2707u), U64(0x50000000u, 0x3fce8c02u), U64(0xc0000000u, 0x3fcef0adu), U64(0x50000000u, 0x3fcf550au), U64(0x60000000u, 0x3fcfb918u), U64(0x40000000u, 0x3fd00e6cu), U64(0x90000000u, 0x3fd04025u), U64(0x50000000u, 0x3fd071b8u), U64(0xe0000000u, 0x3fd0a324u), U64(0x50000000u, 0x3fd0d46bu), U64(0xf0000000u, 0x3fd1058bu), U64(0x00000000u, 0x3fd13687u), U64(0xa0000000u, 0x3fd1675cu), U64(0x20000000u, 0x3fd1980du), U64(0xc0000000u, 0x3fd1c898u), U64(0x90000000u, 0x3fd1f8ffu), U64(0xf0000000u, 0x3fd22941u), U64(0x10000000u, 0x3fd25960u), U64(0x10000000u, 0x3fd2895au), U64(0x30000000u, 0x3fd2b930u), U64(0xb0000000u, 0x3fd2e8e2u), U64(0xc0000000u, 0x3fd31871u), U64(0x90000000u, 0x3fd347ddu), U64(0x60000000u, 0x3fd37726u), U64(0x50000000u, 0x3fd3a64cu), U64(0xa0000000u, 0x3fd3d54fu), U64(0x80000000u, 0x3fd40430u), U64(0x20000000u, 0x3fd432efu), U64(0xc0000000u, 0x3fd4618bu), U64(0x80000000u, 0x3fd49006u), U64(0x90000000u, 0x3fd4be5fu), U64(0x30000000u, 0x3fd4ec97u), U64(0x80000000u, 0x3fd51aadu), U64(0xc0000000u, 0x3fd548a2u), U64(0x10000000u, 0x3fd57677u), U64(0xb0000000u, 0x3fd5a42au), U64(0xb0000000u, 0x3fd5d1bdu), U64(0x70000000u, 0x3fd5ff30u), U64(0xf0000000u, 0x3fd62c82u), U64(0x70000000u, 0x3fd659b5u), U64(0x10000000u, 0x3fd686c8u), U64(0x20000000u, 0x3fd6b3bbu), U64(0xa0000000u, 0x3fd6e08eu), U64(0xe0000000u, 0x3fd70d42u), U64(0xf0000000u, 0x3fd739d7u), U64(0x10000000u, 0x3fd7664eu), U64(0x50000000u, 0x3fd792a5u), U64(0x00000000u, 0x3fd7bedeu), U64(0x30000000u, 0x3fd7eaf8u), U64(0x10000000u, 0x3fd816f4u), U64(0xd0000000u, 0x3fd842d1u), U64(0x90000000u, 0x3fd86e91u), U64(0x80000000u, 0x3fd89a33u), U64(0xc0000000u, 0x3fd8c5b7u), U64(0x80000000u, 0x3fd8f11eu), U64(0xe0000000u, 0x3fd91c67u), U64(0x10000000u, 0x3fd94794u), U64(0x40000000u, 0x3fd972a3u), U64(0x80000000u, 0x3fd99d95u), U64(0x00000000u, 0x3fd9c86bu), U64(0xe0000000u, 0x3fd9f323u), U64(0x60000000u, 0x3fda1dc0u), U64(0x90000000u, 0x3fda4840u), U64(0x90000000u, 0x3fda72a4u), U64(0x90000000u, 0x3fda9cecu), U64(0xc0000000u, 0x3fdac718u), U64(0x30000000u, 0x3fdaf129u), U64(0x00000000u, 0x3fdb1b1eu), U64(0x70000000u, 0x3fdb44f7u), U64(0x90000000u, 0x3fdb6eb5u), U64(0x90000000u, 0x3fdb9858u), U64(0x80000000u, 0x3fdbc1e0u), U64(0x90000000u, 0x3fdbeb4du), U64(0xf0000000u, 0x3fdc149fu), U64(0xa0000000u, 0x3fdc3dd7u), U64(0xe0000000u, 0x3fdc66f4u), U64(0xc0000000u, 0x3fdc8ff7u), U64(0x70000000u, 0x3fdcb8e0u), U64(0x00000000u, 0x3fdce1afu), U64(0xa0000000u, 0x3fdd0a63u), U64(0x70000000u, 0x3fdd32feu), U64(0x90000000u, 0x3fdd5b7fu), U64(0x20000000u, 0x3fdd83e7u), U64(0x30000000u, 0x3fddac35u), U64(0x00000000u, 0x3fddd46au), U64(0x90000000u, 0x3fddfc85u), U64(0x10000000u, 0x3fde2488u), U64(0xa0000000u, 0x3fde4c71u), U64(0x60000000u, 0x3fde7442u), U64(0x60000000u, 0x3fde9bfau), U64(0xd0000000u, 0x3fdec399u), U64(0xc0000000u, 0x3fdeeb20u), U64(0x50000000u, 0x3fdf128fu), U64(0xb0000000u, 0x3fdf39e5u), U64(0xf0000000u, 0x3fdf6123u), U64(0x30000000u, 0x3fdf884au), U64(0x80000000u, 0x3fdfaf58u), U64(0x20000000u, 0x3fdfd64fu), U64(0x00000000u, 0x3fdffd2eu), U64(0xb0000000u, 0x3fe011fau), U64(0xa0000000u, 0x3fe02552u), U64(0xe0000000u, 0x3fe0389eu), U64(0x90000000u, 0x3fe04bdfu), U64(0xb0000000u, 0x3fe05f14u), U64(0x50000000u, 0x3fe0723eu), U64(0x80000000u, 0x3fe0855cu), U64(0x40000000u, 0x3fe0986fu), U64(0xb0000000u, 0x3fe0ab76u), U64(0xe0000000u, 0x3fe0be72u), U64(0xc0000000u, 0x3fe0d163u), U64(0x80000000u, 0x3fe0e449u), U64(0x10000000u, 0x3fe0f724u), U64(0x90000000u, 0x3fe109f3u), U64(0x10000000u, 0x3fe11cb8u), U64(0x90000000u, 0x3fe12f71u), U64(0x20000000u, 0x3fe14220u), U64(0xd0000000u, 0x3fe154c3u), U64(0xa0000000u, 0x3fe1675cu), U64(0xb0000000u, 0x3fe179eau), U64(0x00000000u, 0x3fe18c6eu), U64(0xb0000000u, 0x3fe19ee6u), U64(0xb0000000u, 0x3fe1b154u), U64(0x10000000u, 0x3fe1c3b8u), U64(0xf0000000u, 0x3fe1d610u), U64(0x50000000u, 0x3fe1e85fu), U64(0x40000000u, 0x3fe1faa3u), U64(0xd0000000u, 0x3fe20cdcu), U64(0xf0000000u, 0x3fe21f0bu), U64(0xd0000000u, 0x3fe23130u), U64(0x60000000u, 0x3fe2434bu), U64(0xc0000000u, 0x3fe2555bu), U64(0x00000000u, 0x3fe26762u), U64(0x10000000u, 0x3fe2795eu), U64(0x00000000u, 0x3fe28b50u), U64(0xf0000000u, 0x3fe29d37u), U64(0xf0000000u, 0x3fe2af15u), U64(0xe0000000u, 0x3fe2c0e9u), U64(0x00000000u, 0x3fe2d2b4u), U64(0x30000000u, 0x3fe2e474u), U64(0x90000000u, 0x3fe2f62au), U64(0x30000000u, 0x3fe307d7u), U64(0x00000000u, 0x3fe3197au), U64(0x30000000u, 0x3fe32b13u), U64(0xb0000000u, 0x3fe33ca2u), U64(0x90000000u, 0x3fe34e28u), U64(0xe0000000u, 0x3fe35fa4u), U64(0xb0000000u, 0x3fe37117u), U64(0xf0000000u, 0x3fe38280u), U64(0xd0000000u, 0x3fe393e0u), U64(0x30000000u, 0x3fe3a537u), U64(0x40000000u, 0x3fe3b684u), U64(0xf0000000u, 0x3fe3c7c7u), U64(0x60000000u, 0x3fe3d902u), U64(0x90000000u, 0x3fe3ea33u), U64(0x80000000u, 0x3fe3fb5bu), U64(0x40000000u, 0x3fe40c7au), U64(0xe0000000u, 0x3fe41d8fu), U64(0x60000000u, 0x3fe42e9cu), U64(0xe0000000u, 0x3fe43f9fu), U64(0x50000000u, 0x3fe4509au), U64(0xc0000000u, 0x3fe4618bu), U64(0x30000000u, 0x3fe47274u), U64(0xd0000000u, 0x3fe48353u), U64(0x80000000u, 0x3fe4942au), U64(0x50000000u, 0x3fe4a4f8u), U64(0x60000000u, 0x3fe4b5bdu), U64(0xa0000000u, 0x3fe4c679u), U64(0x30000000u, 0x3fe4d72du), U64(0x10000000u, 0x3fe4e7d8u), U64(0x30000000u, 0x3fe4f87au), U64(0xc0000000u, 0x3fe50913u), U64(0xc0000000u, 0x3fe519a4u), U64(0x20000000u, 0x3fe52a2du), U64(0x00000000u, 0x3fe53aadu), U64(0x60000000u, 0x3fe54b24u), U64(0x50000000u, 0x3fe55b93u), U64(0xd0000000u, 0x3fe56bf9u), U64(0xf0000000u, 0x3fe57c57u), U64(0xb0000000u, 0x3fe58cadu), U64(0x20000000u, 0x3fe59cfbu), U64(0x40000000u, 0x3fe5ad40u), U64(0x30000000u, 0x3fe5bd7du), U64(0xd0000000u, 0x3fe5cdb1u), U64(0x50000000u, 0x3fe5dddeu), U64(0xa0000000u, 0x3fe5ee02u), U64(0xd0000000u, 0x3fe5fe1eu), U64(0xf0000000u, 0x3fe60e32u), U64(0xf0000000u, 0x3fe61e3eu), U64(0xe0000000u, 0x3fe62e42u)); - -const UCRT_POW_T4: array = array(U64(0x00000000u, 0x00000000u), U64(0x0250435au, 0x3db5885eu), U64(0x11f86ed2u, 0x3de620cfu), U64(0xedba4a25u, 0x3dff0214u), U64(0x79f3db4eu, 0x3dbf807cu), U64(0xa779a52bu, 0x3dea352bu), U64(0x6aa49fd5u, 0x3dff56c4u), U64(0x5fef5196u, 0x3dfebe46u), U64(0x0099f1f8u, 0x3e0cf066u), U64(0xff85945du, 0x3e1247b2u), U64(0xbf5202b6u, 0x3e13fd7au), U64(0xa918d51eu, 0x3e1f91c9u), U64(0xf118d3cau, 0x3e08cb73u), U64(0xd6fad074u, 0x3e1d91c7u), U64(0xec28d14cu, 0x3de1971bu), U64(0xa423c78au, 0x3e15b616u), U64(0x617cc971u, 0x3da162a6u), U64(0xc4c06d29u, 0x3e166391u), U64(0xc1d0c4b8u, 0x3e2d46f5u), U64(0x2df1f6d3u, 0x3e2e1428u), U64(0x424a660du, 0x3e186f47u), U64(0xe077753eu, 0x3e2d4c8du), U64(0x7ed24f1cu, 0x3e2e0c30u), U64(0x8763bdd3u, 0x3e226ea1u), U64(0x9737c933u, 0x3e25cad6u), U64(0x99088901u, 0x3e2af625u), U64(0x83d6b2d0u, 0x3e18c66cu), U64(0xb36fb30fu, 0x3e1880ceu), U64(0xc6ca17a4u, 0x3e2495aau), U64(0x4210878cu, 0x3e2761dbu), U64(0x862bac2fu, 0x3e2eb78eu), U64(0x75790dd9u, 0x3e19b2cdu), U64(0xcbd3d50fu, 0x3e2c55e5u), U64(0x617cc971u, 0x3db162a6u), U64(0xaaa2e519u, 0x3dfdbeabu), U64(0x7150c647u, 0x3e1652cbu), U64(0xb2cd2ee2u, 0x3e39a11cu), U64(0xb1a28813u, 0x3e219d0au), U64(0x80a41811u, 0x3e24bd9eu), U64(0x96faa3dfu, 0x3e3214b5u), U64(0x46980bb8u, 0x3e303feau), U64(0xa5fd28c7u, 0x3e31c8ffu), U64(0x3bcd96c5u, 0x3dce8f74u), U64(0x395315c6u, 0x3dfd98c5u), U64(0x3ccfa7b2u, 0x3e3996fau), U64(0x2ad13037u, 0x3e1cd2afu), U64(0xbd17200eu, 0x3e1d0da1u), U64(0x0ba68b75u, 0x3e333041u), U64(0x790e7c41u, 0x3df4f27au), U64(0x86f6ff1bu, 0x3e13956au), U64(0x723551d9u, 0x3e2c6748u), U64(0x9326cdfcu, 0x3e2500deu), U64(0x48df1b59u, 0x3e1086c8u), U64(0xad6836ffu, 0x3e04357eu), U64(0x42408024u, 0x3e248324u), U64(0x8154b13du, 0x3e3d10dau), U64(0x68ec8260u, 0x3e39e8adu), U64(0x06abaf18u, 0x3e3cfbf7u), U64(0xc6326e23u, 0x3e3fc56au), U64(0x3185cf21u, 0x3e39105eu), U64(0xe5b19cc0u, 0x3e3d017fu), U64(0x48dd13feu, 0x3e3d1f6bu), U64(0x58a7e73au, 0x3e20b633u), U64(0x028c211cu, 0x3e263063u), U64(0x86b09760u, 0x3e2e6a68u), U64(0xb891cd03u, 0x3e3c138bu), U64(0x22b7221au, 0x3e369f77u), U64(0xac1a628cu, 0x3df57d8fu), U64(0xcbd3d50fu, 0x3e3c55e5u), U64(0xff48fe2eu, 0x3e1552d2u), U64(0x6ca431bcu, 0x3e37b8b2u), U64(0xdc1c5f6du, 0x3e292decu), U64(0x551aaa8cu, 0x3e3abc7cu), U64(0x731a354bu, 0x3e36b540u), U64(0x036b89efu, 0x3e32d341u), U64(0x1a3a2e0fu, 0x3e4f9ab2u), U64(0x1afb9fbdu, 0x3e239c87u), U64(0x2c81f640u, 0x3e3e6addu), U64(0xaa313f41u, 0x3e435c95u), U64(0x82f6cc53u, 0x3e249d45u), U64(0x1c07398fu, 0x3e47574cu), U64(0xdece9e8du, 0x3e4ba846u), U64(0xafbc68e7u, 0x3e16999fu), U64(0xe51b0103u, 0x3e4c9145u), U64(0xcb44850au, 0x3e479ef2u), U64(0x3de11275u, 0x3e0beec7u), U64(0x1af5a498u, 0x3e2ef435u), U64(0x493b4a50u, 0x3e45713au), U64(0x61385992u, 0x3e45c23au), U64(0x09f57299u, 0x3e42a883u), U64(0xa9ac8aceu, 0x3e4530fau), U64(0xd792a758u, 0x3e25fec2u), U64(0xa71cbcd7u, 0x3e35a517u), U64(0x3e1cd9a3u, 0x3e3707dcu), U64(0x8ef43049u, 0x3e3a1a9fu), U64(0x276b3674u, 0x3e4409d0u), U64(0x13e85bd9u, 0x3e20e2f6u), U64(0x33001e5fu, 0x3df00274u), U64(0x836d3265u, 0x3e35dde2u), U64(0x4d7aaf04u, 0x3e230013u), U64(0xb42724f5u, 0x3e3cb7e0u), U64(0x167e6308u, 0x3e2d6e93u), U64(0xb1526adbu, 0x3e3d1569u), U64(0x338a1a41u, 0x3e0e99fcu), U64(0x94a11b1cu, 0x3e4eb013u), U64(0x790e7c41u, 0x3e04f27au), U64(0xa97b7af9u, 0x3e25ce3cu), U64(0x940ed857u, 0x3e281f0fu), U64(0x5d88857cu, 0x3e4d3629u), U64(0xec4af526u, 0x3e21aca1u), U64(0xc7182726u, 0x3e445743u), U64(0xaead337eu, 0x3e23c491u), U64(0x1a738931u, 0x3e3aef40u), U64(0x76092a29u, 0x3e21cedeu), U64(0x44f82bb4u, 0x3e4fba8fu), U64(0x7f3c3e1au, 0x3e446f5fu), U64(0x86c9674bu, 0x3e47055fu), U64(0x2b6b6e1au, 0x3e4b41a9u), U64(0x2e927628u, 0x3e443d16u), U64(0x4013f9b1u, 0x3e446617u), U64(0x6ad69c62u, 0x3e3b0509u), U64(0x150faa58u, 0x3e40b169u), U64(0x1df85da7u, 0x3e3cd98bu), U64(0x7b0f8fa8u, 0x3e468b50u), U64(0xf57499bau, 0x3e48422du), U64(0x86970274u, 0x3e113515u), U64(0xacba92eeu, 0x3e117e08u), U64(0x14dd0229u, 0x3e26e043u), U64(0x97e56d1au, 0x3e497f30u), U64(0x55901286u, 0x3e3356e6u), U64(0x457f94d6u, 0x3e0cb761u), U64(0xa85a9dacu, 0x3e39af67u), U64(0x931a909fu, 0x3e453410u), U64(0x206058f5u, 0x3e22c587u), U64(0x58899c22u, 0x3e223bc3u), U64(0xb6d223cbu, 0x3e4d7bf8u), U64(0xc5197ddbu, 0x3e47991eu), U64(0xbb3a9219u, 0x3e4a79e6u), U64(0xed663ec5u, 0x3e3a4c43u), U64(0x1484f438u, 0x3e461b5au), U64(0xf7ef0c3au, 0x3e4b4e36u), U64(0x6acd0d1bu, 0x3e115f02u), U64(0x35cecf05u, 0x3e3f36b5u), U64(0xbf3eb5c6u, 0x3e2ffb7fu), U64(0x86b09760u, 0x3e3e6a68u), U64(0x27f5bbc3u, 0x3e3135ebu), U64(0xd6f6fa57u, 0x3e470be7u), U64(0xc84ab338u, 0x3e4ce43cu), U64(0xaac3bd91u, 0x3e4c01d7u), U64(0x07961060u, 0x3e45c58du), U64(0xf941456eu, 0x3e3628bcu), U64(0xa8461cd2u, 0x3e4c58b2u), U64(0x82fb989au, 0x3e330712u), U64(0x6a80f09cu, 0x3e420dabu), U64(0x4c397b1eu, 0x3e44f8d8u), U64(0x08599e48u, 0x3e40d0eeu), U64(0x7e37da36u, 0x3e1d6878u), U64(0xd591bafcu, 0x3e366187u), U64(0x00bae772u, 0x3e223466u), U64(0xd0d61b8eu, 0x3e390377u), U64(0xd966b907u, 0x3e4f5e0du), U64(0xb79a00e2u, 0x3e49023cu), U64(0x58c28ad8u, 0x3e44e051u), U64(0x08b18ae4u, 0x3e3bfa7bu), U64(0x3db35f67u, 0x3e4ef1e6u), U64(0x39493d4fu, 0x3e0ec2aeu), U64(0x30ab2fa0u, 0x3e40afe9u), U64(0xa1810dd4u, 0x3e225ff8u), U64(0xfb1a71a5u, 0x3e469743u), U64(0x76785571u, 0x3e5f9cc6u), U64(0xa4cbf982u, 0x3e5b524du), U64(0x381535b8u, 0x3e5a4c8bu), U64(0x809caf2cu, 0x3e5839beu), U64(0x1cb82c13u, 0x3e50968au), U64(0x41723fb5u, 0x3e5eae6au), U64(0xa380a4dbu, 0x3e5d9c29u), U64(0x0ada625eu, 0x3e4094aau), U64(0x6fc108cau, 0x3e5973adu), U64(0x2fdbab97u, 0x3e474732u), U64(0xfa9d4221u, 0x3e593692u), U64(0x2dfbc7d9u, 0x3e5c5a99u), U64(0xe102387au, 0x3e4e1f33u), U64(0xf14c048cu, 0x3e464fbeu), U64(0x13ca5e3bu, 0x3e4490f5u), U64(0x4d4c799du, 0x3e37a6afu), U64(0x1c07398fu, 0x3e57574cu), U64(0x417f8c1cu, 0x3e57b133u), U64(0x0c176514u, 0x3e5feb9eu), U64(0xbb3172f7u, 0x3e419f25u), U64(0x7bbfb852u, 0x3e45f68au), U64(0x497929f1u, 0x3e5ee278u), U64(0x06109d58u, 0x3e5ccee0u), U64(0xa07bd8b3u, 0x3e5ce081u), U64(0x981817b8u, 0x3e570e12u), U64(0xd93503d0u, 0x3e292ab6u), U64(0xd7c3b61eu, 0x3e58cb7du), U64(0x0a0b78dau, 0x3e4efafdu), U64(0x67c4288eu, 0x3e5e9072u), U64(0x96780875u, 0x3e5d31efu), U64(0xfcd2ad50u, 0x3e23430du), U64(0xd75bc1f9u, 0x3e344d88u), U64(0x055e04fcu, 0x3e5bec0fu), U64(0x1590b9adu, 0x3e5d8561u), U64(0x8e583229u, 0x3df32056u), U64(0x1772f538u, 0x3e5a891du), U64(0xdabba74du, 0x3e22edc9u), U64(0xa1015086u, 0x3e4b9009u), U64(0x8c5b1a19u, 0x3e52a12au), U64(0xf0fdac85u, 0x3e3a7885u), U64(0xd43ac691u, 0x3e5f4ffcu), U64(0xe2640aadu, 0x3e52243au), U64(0x299035d3u, 0x3e546513u), U64(0xa62dd725u, 0x3e5b39c3u), U64(0x40049f51u, 0x3e5ba6ddu), U64(0xd7177409u, 0x3e451d1eu), U64(0xfd7f5216u, 0x3e5cb0f2u), U64(0xcd4e2213u, 0x3e3ab150u), U64(0xf3193844u, 0x3e5cfd7bu), U64(0x455f1dbdu, 0x3e53fff8u), U64(0x0b905fc9u, 0x3e5fee64u), U64(0xf548084cu, 0x3e54e2adu), U64(0xdc1ecdd2u, 0x3e3b597au), U64(0x096d3a75u, 0x3e4345bdu), U64(0xd2453c8bu, 0x3e5101b9u), U64(0x5cc8c979u, 0x3e508ce5u), U64(0x7e595f71u, 0x3e5bbf01u), U64(0x3bd393dcu, 0x3e37ce73u), U64(0xa503f8a1u, 0x3e233bb0u), U64(0x13e85bd9u, 0x3e30e2f6u), U64(0x5a635b3cu, 0x3e5e6755u), U64(0xf73d5e8bu, 0x3e2ea88du), U64(0x3bda18a8u, 0x3e3d17e0u), U64(0x76044f7eu, 0x3e5b607du), U64(0xe71bc2fcu, 0x3e52adc4u), U64(0x7362d1d9u, 0x3e5f99dcu), U64(0x008e6a6au, 0x3e5473fau), U64(0x09cb0985u, 0x3e2b75bbu), U64(0xd10b9abau, 0x3e5ea04du), U64(0xd6979674u, 0x3e5802d0u), U64(0xccd99094u, 0x3e174688u), U64(0xabb9df22u, 0x3e496f16u), U64(0xf2aa374fu, 0x3e46e66du), U64(0x5ea4550au, 0x3e4e6652u), U64(0x34f20cbdu, 0x3e42d02fu), U64(0x65047188u, 0x3e46cfceu), U64(0x842d58b8u, 0x3e39b78cu), U64(0x24c24bc9u, 0x3e4735e6u), U64(0xf7dd1adfu, 0x3e47eba1u), U64(0x59f65355u, 0x3e586b3eu), U64(0x637f1b4du, 0x3e1ce38eu), U64(0xc919edc7u, 0x3e58d82eu), U64(0x8ddcfa37u, 0x3e4c5264u), U64(0xeae1ac12u, 0x3e52482cu), U64(0x311aba4fu, 0x3e55a312u), U64(0x6329f225u, 0x3e411e23u), U64(0xcd2f246cu, 0x3e5b48c8u), U64(0xef35793cu, 0x3e6efa39u)); - -const UCRT_POW11_INVC: array = array(U64(0x00000000u, 0x3ff6a000u), U64(0x00000000u, 0x3ff68000u), U64(0x00000000u, 0x3ff66000u), U64(0x00000000u, 0x3ff64000u), U64(0x00000000u, 0x3ff62000u), U64(0x00000000u, 0x3ff60000u), U64(0x00000000u, 0x3ff5e000u), U64(0x00000000u, 0x3ff5c000u), U64(0x00000000u, 0x3ff5a000u), U64(0x00000000u, 0x3ff58000u), U64(0x00000000u, 0x3ff56000u), U64(0x00000000u, 0x3ff56000u), U64(0x00000000u, 0x3ff54000u), U64(0x00000000u, 0x3ff52000u), U64(0x00000000u, 0x3ff50000u), U64(0x00000000u, 0x3ff4e000u), U64(0x00000000u, 0x3ff4c000u), U64(0x00000000u, 0x3ff4a000u), U64(0x00000000u, 0x3ff4a000u), U64(0x00000000u, 0x3ff48000u), U64(0x00000000u, 0x3ff46000u), U64(0x00000000u, 0x3ff44000u), U64(0x00000000u, 0x3ff42000u), U64(0x00000000u, 0x3ff40000u), U64(0x00000000u, 0x3ff40000u), U64(0x00000000u, 0x3ff3e000u), U64(0x00000000u, 0x3ff3c000u), U64(0x00000000u, 0x3ff3a000u), U64(0x00000000u, 0x3ff3a000u), U64(0x00000000u, 0x3ff38000u), U64(0x00000000u, 0x3ff36000u), U64(0x00000000u, 0x3ff34000u), U64(0x00000000u, 0x3ff34000u), U64(0x00000000u, 0x3ff32000u), U64(0x00000000u, 0x3ff30000u), U64(0x00000000u, 0x3ff30000u), U64(0x00000000u, 0x3ff2e000u), U64(0x00000000u, 0x3ff2c000u), U64(0x00000000u, 0x3ff2c000u), U64(0x00000000u, 0x3ff2a000u), U64(0x00000000u, 0x3ff28000u), U64(0x00000000u, 0x3ff26000u), U64(0x00000000u, 0x3ff26000u), U64(0x00000000u, 0x3ff24000u), U64(0x00000000u, 0x3ff24000u), U64(0x00000000u, 0x3ff22000u), U64(0x00000000u, 0x3ff20000u), U64(0x00000000u, 0x3ff20000u), U64(0x00000000u, 0x3ff1e000u), U64(0x00000000u, 0x3ff1c000u), U64(0x00000000u, 0x3ff1c000u), U64(0x00000000u, 0x3ff1a000u), U64(0x00000000u, 0x3ff1a000u), U64(0x00000000u, 0x3ff18000u), U64(0x00000000u, 0x3ff16000u), U64(0x00000000u, 0x3ff16000u), U64(0x00000000u, 0x3ff14000u), U64(0x00000000u, 0x3ff14000u), U64(0x00000000u, 0x3ff12000u), U64(0x00000000u, 0x3ff10000u), U64(0x00000000u, 0x3ff10000u), U64(0x00000000u, 0x3ff0e000u), U64(0x00000000u, 0x3ff0e000u), U64(0x00000000u, 0x3ff0c000u), U64(0x00000000u, 0x3ff0c000u), U64(0x00000000u, 0x3ff0a000u), U64(0x00000000u, 0x3ff0a000u), U64(0x00000000u, 0x3ff08000u), U64(0x00000000u, 0x3ff08000u), U64(0x00000000u, 0x3ff06000u), U64(0x00000000u, 0x3ff04000u), U64(0x00000000u, 0x3ff04000u), U64(0x00000000u, 0x3ff02000u), U64(0x00000000u, 0x3ff02000u), U64(0x00000000u, 0x3ff00000u), U64(0x00000000u, 0x3ff00000u), U64(0x00000000u, 0x3fefc000u), U64(0x00000000u, 0x3fef8000u), U64(0x00000000u, 0x3fef4000u), U64(0x00000000u, 0x3fef0000u), U64(0x00000000u, 0x3feec000u), U64(0x00000000u, 0x3fee8000u), U64(0x00000000u, 0x3fee4000u), U64(0x00000000u, 0x3fee2000u), U64(0x00000000u, 0x3fede000u), U64(0x00000000u, 0x3feda000u), U64(0x00000000u, 0x3fed6000u), U64(0x00000000u, 0x3fed4000u), U64(0x00000000u, 0x3fed0000u), U64(0x00000000u, 0x3fecc000u), U64(0x00000000u, 0x3feca000u), U64(0x00000000u, 0x3fec6000u), U64(0x00000000u, 0x3fec4000u), U64(0x00000000u, 0x3fec0000u), U64(0x00000000u, 0x3febe000u), U64(0x00000000u, 0x3feba000u), U64(0x00000000u, 0x3feb8000u), U64(0x00000000u, 0x3feb4000u), U64(0x00000000u, 0x3feb2000u), U64(0x00000000u, 0x3feae000u), U64(0x00000000u, 0x3feac000u), U64(0x00000000u, 0x3feaa000u), U64(0x00000000u, 0x3fea6000u), U64(0x00000000u, 0x3fea4000u), U64(0x00000000u, 0x3fea0000u), U64(0x00000000u, 0x3fe9e000u), U64(0x00000000u, 0x3fe9c000u), U64(0x00000000u, 0x3fe9a000u), U64(0x00000000u, 0x3fe96000u), U64(0x00000000u, 0x3fe94000u), U64(0x00000000u, 0x3fe92000u), U64(0x00000000u, 0x3fe90000u), U64(0x00000000u, 0x3fe8c000u), U64(0x00000000u, 0x3fe8a000u), U64(0x00000000u, 0x3fe88000u), U64(0x00000000u, 0x3fe86000u), U64(0x00000000u, 0x3fe84000u), U64(0x00000000u, 0x3fe82000u), U64(0x00000000u, 0x3fe7e000u), U64(0x00000000u, 0x3fe7c000u), U64(0x00000000u, 0x3fe7a000u), U64(0x00000000u, 0x3fe78000u), U64(0x00000000u, 0x3fe76000u), U64(0x00000000u, 0x3fe74000u), U64(0x00000000u, 0x3fe72000u), U64(0x00000000u, 0x3fe70000u), U64(0x00000000u, 0x3fe6e000u), U64(0x00000000u, 0x3fe6c000u)); - -const UCRT_POW11_LOGC: array = array(U64(0xf2b9c800u, 0xbfd62c82u), U64(0xbf580800u, 0xbfd5d1bdu), U64(0x17455800u, 0xbfd57677u), U64(0x872df800u, 0xbfd51aadu), U64(0x95777800u, 0xbfd4be5fu), U64(0xc21c6000u, 0xbfd4618bu), U64(0x8686a800u, 0xbfd40430u), U64(0x55694800u, 0xbfd3a64cu), U64(0x9a988000u, 0xbfd347ddu), U64(0xbae12000u, 0xbfd2e8e2u), U64(0x13de8800u, 0xbfd2895au), U64(0x13de8800u, 0xbfd2895au), U64(0xfbcf7800u, 0xbfd22941u), U64(0xc1699800u, 0xbfd1c898u), U64(0xababa800u, 0xbfd1675cu), U64(0xf9ae4800u, 0xbfd1058bu), U64(0xe2739000u, 0xbfd0a324u), U64(0x94b4d000u, 0xbfd04025u), U64(0x94b4d000u, 0xbfd04025u), U64(0x6d5e4000u, 0xbfcfb918u), U64(0xcbdc6000u, 0xbfcef0adu), U64(0x6e2af000u, 0xbfce2707u), U64(0x6b4fc000u, 0xbfcd5c21u), U64(0xc79aa000u, 0xbfcc8ff7u), U64(0xc79aa000u, 0xbfcc8ff7u), U64(0x742d9000u, 0xbfcbc286u), U64(0x4e80c000u, 0xbfcaf3c9u), U64(0x1fe2b000u, 0xbfca23bcu), U64(0x1fe2b000u, 0xbfca23bcu), U64(0x9cf45000u, 0xbfc9525au), U64(0x6520d000u, 0xbfc87fa0u), U64(0x0210e000u, 0xbfc7ab89u), U64(0x0210e000u, 0xbfc7ab89u), U64(0xe719d000u, 0xbfc6d60fu), U64(0x70a79000u, 0xbfc5ff30u), U64(0x70a79000u, 0xbfc5ff30u), U64(0xe3a1b000u, 0xbfc526e5u), U64(0x6ccb8000u, 0xbfc44d2bu), U64(0x6ccb8000u, 0xbfc44d2bu), U64(0x201e9000u, 0xbfc371fcu), U64(0xf81ff000u, 0xbfc29552u), U64(0xd52f6000u, 0xbfc1b72au), U64(0xd52f6000u, 0xbfc1b72au), U64(0x7cd09000u, 0xbfc0d77eu), U64(0x7cd09000u, 0xbfc0d77eu), U64(0x31dbe000u, 0xbfbfec91u), U64(0x6e2b0000u, 0xbfbe2707u), U64(0x6e2b0000u, 0xbfbe2707u), U64(0x8f5bc000u, 0xbfbc5e54u), U64(0x3a4ae000u, 0xbfba926du), U64(0x3a4ae000u, 0xbfba926du), U64(0xd631a000u, 0xbfb8c345u), U64(0xd631a000u, 0xbfb8c345u), U64(0x8ae56000u, 0xbfb6f0d2u), U64(0x3f062000u, 0xbfb51b07u), U64(0x3f062000u, 0xbfb51b07u), U64(0x961be000u, 0xbfb341d7u), U64(0x961be000u, 0xbfb341d7u), U64(0xeea38000u, 0xbfb16536u), U64(0xc0118000u, 0xbfaf0a30u), U64(0xc0118000u, 0xbfaf0a30u), U64(0x71198000u, 0xbfab42ddu), U64(0x71198000u, 0xbfab42ddu), U64(0xf632c000u, 0xbfa77458u), U64(0xf632c000u, 0xbfa77458u), U64(0xb9fec000u, 0xbfa39e87u), U64(0xb9fec000u, 0xbfa39e87u), U64(0x0e780000u, 0xbf9f829bu), U64(0x0e780000u, 0xbf9f829bu), U64(0x07d58000u, 0xbf97b91bu), U64(0xb0fc0000u, 0xbf8fc0a8u), U64(0xb0fc0000u, 0xbf8fc0a8u), U64(0x6b100000u, 0xbf7fe02au), U64(0x6b100000u, 0xbf7fe02au), U64(0x00000000u, 0x00000000u), U64(0x00000000u, 0x00000000u), U64(0x75890000u, 0x3f801015u), U64(0x58938000u, 0x3f902056u), U64(0x28c90000u, 0x3f984925u), U64(0x89e74000u, 0x3fa0415du), U64(0xd42e0000u, 0x3fa466aeu), U64(0x149fc000u, 0x3fa894aau), U64(0xcdddc000u, 0x3faccb73u), U64(0xc006c000u, 0x3faeea31u), U64(0xd1466000u, 0x3fb1973bu), U64(0xa7d1e000u, 0x3fb3bdf5u), U64(0x4d97a000u, 0x3fb5e95au), U64(0x0aeac000u, 0x3fb700d3u), U64(0x5d594000u, 0x3fb9335eu), U64(0x8dad6000u, 0x3fbb6ac8u), U64(0x01bc4000u, 0x3fbc8858u), U64(0x830a2000u, 0x3fbec739u), U64(0x39dbe000u, 0x3fbfe891u), U64(0x8227e000u, 0x3fc1178eu), U64(0x7e23f000u, 0x3fc1aa2bu), U64(0x0c868000u, 0x3fc2d161u), U64(0xb0159000u, 0x3fc365fcu), U64(0x8333b000u, 0x3fc4913du), U64(0xe4a1b000u, 0x3fc527e5u), U64(0xbe8c1000u, 0x3fc6574eu), U64(0x8b757000u, 0x3fc6f012u), U64(0x85445000u, 0x3fc7898du), U64(0xeb390000u, 0x3fc8beafu), U64(0xdcf70000u, 0x3fc95a5au), U64(0x3c8ae000u, 0x3fca93edu), U64(0x575bd000u, 0x3fcb31d8u), U64(0x383be000u, 0x3fcbd087u), U64(0xc6f01000u, 0x3fcc6ffbu), U64(0xb0d49000u, 0x3fcdb13du), U64(0xffe71000u, 0x3fce530eu), U64(0xe4dd0000u, 0x3fcef5adu), U64(0x6cb3b000u, 0x3fcf991cu), U64(0x604d5800u, 0x3fd07138u), U64(0x67616000u, 0x3fd0c42du), U64(0x8227e800u, 0x3fd1178eu), U64(0xcbacf800u, 0x3fd16b5cu), U64(0x635a6800u, 0x3fd1bf99u), U64(0x6d0eb800u, 0x3fd21445u), U64(0x7cdc9000u, 0x3fd2bef0u), U64(0xe1d36000u, 0x3fd314f1u), U64(0x76be1000u, 0x3fd36b67u), U64(0x77333000u, 0x3fd3c252u), U64(0x23d5e800u, 0x3fd419b4u), U64(0xc271c800u, 0x3fd4718du), U64(0x9e173000u, 0x3fd4c9e0u), U64(0x0738a000u, 0x3fd522aeu), U64(0x53c8d000u, 0x3fd57bf7u), U64(0xdf596000u, 0x3fd5d5bdu)); - -const UCRT_POW11_LOGCTAIL: array = array(U64(0x28375680u, 0x3cfab424u), U64(0xd8e0f720u, 0xbd1ca508u), U64(0xd5b6506du, 0xbd2362a4u), U64(0x9eb067d5u, 0xbce684e4u), U64(0x93293ee0u, 0xbd041b69u), U64(0x484c84ccu, 0x3d13d82fu), U64(0xed820b3au, 0x3cdc42f3u), U64(0x86519460u, 0x3d20b1c6u), U64(0xd4c58092u, 0x3d25594du), U64(0x99b72bd8u, 0x3d267b1eu), U64(0xb6cfb03fu, 0x3d15ca14u), U64(0xb6cfb03fu, 0x3d15ca14u), U64(0x2853da76u, 0xbd165a24u), U64(0x68e75404u, 0xbd1fafbcu), U64(0x3382a8f0u, 0x3d1f1fc6u), U64(0xfd055a66u, 0xbd26a8c4u), U64(0x7ef4030eu, 0xbd0c6beeu), U64(0x9ef42d7fu, 0xbcf036b8u), U64(0x9ef42d7fu, 0xbcf036b8u), U64(0xab993c87u, 0x3d0d572au), U64(0x9c86af24u, 0x3d2b26b7u), U64(0x543fff10u, 0xbd172f4fu), U64(0xbbca681bu, 0x3d21ba91u), U64(0x689f8434u, 0x3d27794fu), U64(0x689f8434u, 0x3d27794fu), U64(0x318bb78fu, 0x3d194eb0u), U64(0x3fcd9066u, 0x3cba4e63u), U64(0xdc46c1eau, 0xbd258c64u), U64(0xdc46c1eau, 0xbd258c64u), U64(0x04c1d4e3u, 0xbd2ad1d9u), U64(0x7fdbfa09u, 0x3d2bbdbfu), U64(0x72534a58u, 0x3d2bdb90u), U64(0x72534a58u, 0x3d2bdb90u), U64(0xa3b2e266u, 0xbd10e46au), U64(0x9f105039u, 0xbd1e9e43u), U64(0x9f105039u, 0xbd1e9e43u), U64(0x90075b8fu, 0xbd20de8bu), U64(0x6135783cu, 0x3d170cc1u), U64(0x6135783cu, 0x3d170cc1u), U64(0x4d27543au, 0x3cf17886u), U64(0x1771c408u, 0xbd248d30u), U64(0x1811a396u, 0xbd2e80a4u), U64(0x1811a396u, 0xbd2e80a4u), U64(0x88e85bf4u, 0x3d0a6996u), U64(0x88e85bf4u, 0x3d0a6996u), U64(0x5ca333f2u, 0xbd257554u), U64(0x2af0003cu, 0x3d2a342cu), U64(0x2af0003cu, 0x3d2a342cu), U64(0x585fbe06u, 0xbd1d0c57u), U64(0xe85baac8u, 0x3d253935u), U64(0xe85baac8u, 0x3d253935u), U64(0x4d2f5668u, 0x3d137c29u), U64(0x4d2f5668u, 0x3d137c29u), U64(0xc93373dau, 0xbd269737u), U64(0x61c65e57u, 0x3d1f025bu), U64(0x61c65e57u, 0x3d1f025bu), U64(0xccf913dfu, 0x3d2c5edau), U64(0xccf913dfu, 0x3d2c5edau), U64(0x768fa309u, 0x3d147c5eu), U64(0x83368e91u, 0x3d2d599eu), U64(0x83368e91u, 0x3d2d599eu), U64(0xe5d6704cu, 0x3d1c827au), U64(0xe5d6704cu, 0x3d1c827au), U64(0x34f2a1eeu, 0xbd2cfc46u), U64(0x34f2a1eeu, 0xbd2cfc46u), U64(0xf526feaau, 0x3cf502b7u), U64(0xf526feaau, 0x3cf502b7u), U64(0x7c7e09e4u, 0xbd298026u), U64(0x7c7e09e4u, 0xbd298026u), U64(0x93faa639u, 0xbd288d54u), U64(0xf6d3a69cu, 0xbcdf1e7cu), U64(0xf6d3a69cu, 0xbcdf1e7cu), U64(0x0dda40e4u, 0xbd19e23fu), U64(0x0dda40e4u, 0xbd19e23fu), U64(0x00000000u, 0x00000000u), U64(0x00000000u, 0x00000000u), U64(0x999d2be8u, 0xbd10c76bu), U64(0x06e2f7d2u, 0xbd23dc5bu), U64(0x325a0c34u, 0xbd2aa0bau), U64(0x5cf1d753u, 0x3d0111c0u), U64(0x75bdfd28u, 0xbd2c1673u), U64(0xd05a267du, 0xbd197995u), U64(0x47d82807u, 0xbd1a68f2u), U64(0x4fc93b7bu, 0xbd0e113eu), U64(0x560d9e9bu, 0xbd25325du), U64(0xa5db4ed7u, 0x3d2cc85eu), U64(0x3c5d1d1eu, 0xbd2c6906u), U64(0xa99ded32u, 0x3cec1e8du), U64(0x3abd47dau, 0x3d23115cu), U64(0x2bf768e5u, 0xbd139080u), U64(0xc65aacd3u, 0x3d2646d1u), U64(0xafe645e0u, 0xbd2dc068u), U64(0x4fa10afdu, 0xbd2534d6u), U64(0xce2d07f2u, 0x3d21ef78u), U64(0x44389934u, 0x3d2ca78eu), U64(0xcb81b4a1u, 0x3d039d6cu), U64(0x234b7289u, 0x3cc62fa8u), U64(0x54fdb678u, 0x3d258379u), U64(0xe5697dc7u, 0x3d2633e8u), U64(0x2c3c2e78u, 0x3d19cf8bu), U64(0xe59c21e1u, 0xbd25118du), U64(0x70914305u, 0xbd1c6610u), U64(0xaae92cd1u, 0xbd073d54u), U64(0x58a0ff6fu, 0x3d07f228u), U64(0x50562169u, 0xbd287243u), U64(0x4eace1aau, 0xbd0c358du), U64(0x595412b6u, 0xbd2d4bc4u), U64(0xc5962bd2u, 0xbcf1ec72u), U64(0xf715b035u, 0xbd2aff2au), U64(0x6041f430u, 0x3cc21227u), U64(0x65bb8e11u, 0xbcca2115u), U64(0xca0cdf30u, 0x3d1bcbecu), U64(0x16ed4e91u, 0x3cf89cdbu), U64(0x163ceae9u, 0x3d27188bu), U64(0x63a5f01cu, 0xbd2c210eu), U64(0xf7a51681u, 0x3d2b9acdu), U64(0x5147bdb7u, 0x3d2ca6edu), U64(0xba46baeau, 0x3d0a87deu), U64(0x4a5004f4u, 0x3d2a9cfau), U64(0xd3213cb8u, 0xbd28e27au), U64(0xb0f177c8u, 0x3d116ecdu), U64(0xb606bd5cu, 0x3d183b54u), U64(0xec90e09du, 0x3d08e436u), U64(0x0967d675u, 0xbd2f27ceu), U64(0x1b0ad8a4u, 0xbd2e2089u), U64(0x8164c759u, 0x3d2ebe70u), U64(0xee5d40efu, 0x3d1fadedu), U64(0x08a465dcu, 0xbd0a0b2au)); - -const UCRT_POW11_EXP: array = array(U64(0x00000000u, 0x00000000u), U64(0x00000000u, 0x3ff00000u), U64(0xc61851acu, 0xbc84e82fu), U64(0xfa5abcbfu, 0x3feffb1au), U64(0x1a88bf6eu, 0x3c9b3b4fu), U64(0xa9fb3335u, 0x3feff63du), U64(0xd8521d32u, 0xbc82985du), U64(0x143b0281u, 0x3feff168u), U64(0x9cd8dc5du, 0xbc716013u), U64(0x3e778061u, 0x3fefec9au), U64(0x7061bfbdu, 0x3c651e61u), U64(0x2e11bbccu, 0x3fefe7d4u), U64(0x108766d1u, 0xbc905e7au), U64(0xe86e7f85u, 0x3fefe315u), U64(0x437fa426u, 0x3c845fadu), U64(0x72f654b1u, 0x3fefde5fu), U64(0x3567f613u, 0x3c8cd252u), U64(0xd3158574u, 0x3fefd9b0u), U64(0x642b232fu, 0xbc954529u), U64(0x0e3c1f89u, 0x3fefd50au), U64(0x23f98efau, 0xbc8bce80u), U64(0x29ddf6deu, 0x3fefd06bu), U64(0x8ef5c32eu, 0x3c829370u), U64(0x2b72a836u, 0x3fefcbd4u), U64(0x61e6c861u, 0x3c60f74eu), U64(0x18759bc8u, 0x3fefc745u), U64(0x0905b2a4u, 0xbc95b928u), U64(0xf66607e0u, 0x3fefc2bdu), U64(0x5b33d399u, 0x3c90a3e4u), U64(0xcac6f383u, 0x3fefbe3eu), U64(0x32c4b7e7u, 0x3c84f31fu), U64(0x9b1f3919u, 0x3fefb9c7u), U64(0x5d837b6du, 0x3c979aa6u), U64(0x6cf9890fu, 0x3fefb558u), U64(0x30d06420u, 0x3c9407fbu), U64(0x45e46c85u, 0x3fefb0f1u), U64(0x92fdeffcu, 0x3c8eb51au), U64(0x2b7247f7u, 0x3fefac92u), U64(0xb3b9911bu, 0xbc9a5d04u), U64(0x23395decu, 0x3fefa83bu), U64(0x702f9cd1u, 0x3c3ebe3du), U64(0x32d3d1a2u, 0x3fefa3ecu), U64(0xf0739546u, 0xbc937a01u), U64(0x5fdfa9c5u, 0x3fef9fa5u), U64(0x89906e0bu, 0xbc6a0334u), U64(0xaffed31bu, 0x3fef9b66u), U64(0xb04ef0a5u, 0x3c8b8268u), U64(0x28d7233eu, 0x3fef9730u), U64(0x2a2fbd0eu, 0xbc955652u), U64(0xd0125b51u, 0x3fef9301u), U64(0x44a2ebccu, 0xbc9ac46eu), U64(0xab5e2ab6u, 0x3fef8edbu), U64(0x8c4eea55u, 0xbc5080efu), U64(0xc06c31ccu, 0x3fef8abdu), U64(0x90c9f860u, 0xbc65704eu), U64(0x14f204abu, 0x3fef86a8u), U64(0xb9d5f416u, 0xbc91c923u), U64(0xaea92de0u, 0x3fef829au), U64(0x57e46280u, 0xbc897ceau), U64(0x934f312eu, 0x3fef7e95u), U64(0xe95c55afu, 0x3c80d3e3u), U64(0xc8a58e51u, 0x3fef7a98u), U64(0x29e2b9d2u, 0x3c56f014u), U64(0x5471c3c2u, 0x3fef76a4u), U64(0xeaa59348u, 0xbc801b15u), U64(0x3c7d517bu, 0x3fef72b8u), U64(0x2459034bu, 0x3c6e653bu), U64(0x8695bbc0u, 0x3fef6ed4u), U64(0x55de323du, 0xbc8f1ff0u), U64(0x388c8deau, 0x3fef6af9u), U64(0xa345b7dcu, 0x3c92cc7eu), U64(0x58375d2fu, 0x3fef6726u), U64(0x3f1353bfu, 0x3c8b898cu), U64(0xeb6fcb75u, 0x3fef635bu), U64(0x2876ea9eu, 0x3c957bfbu), U64(0xf8138a1cu, 0x3fef5f99u), U64(0x7611eb26u, 0xbc96d99cu), U64(0x84045cd4u, 0x3fef5be0u), U64(0x73af2155u, 0x3c8cdc18u), U64(0x95281c6bu, 0x3fef582fu), U64(0x3e3a2f60u, 0x3c9aecf7u), U64(0x3168b9aau, 0x3fef5487u), U64(0x4653a131u, 0xbc949368u), U64(0x5eb44027u, 0x3fef50e7u), U64(0xcb86389du, 0xbc8fe782u), U64(0x22fcd91du, 0x3fef4d50u), U64(0x9077520au, 0xbc98e289u), U64(0x8438ce4du, 0x3fef49c1u), U64(0x44a6c38du, 0x3c8a6f41u), U64(0x88628cd6u, 0x3fef463bu), U64(0xd4f59273u, 0x3c9120fcu), U64(0x3578a819u, 0x3fef42beu), U64(0xb0e4047du, 0x3c807a05u), U64(0x917ddc96u, 0x3fef3f49u), U64(0xc188c9b8u, 0x3c89b788u), U64(0xa27912d1u, 0x3fef3bddu), U64(0xe3a8a894u, 0x3c968efdu), U64(0x6e756238u, 0x3fef387au), U64(0xca90ef84u, 0x3c877afbu), U64(0xfb82140au, 0x3fef351fu), U64(0xf274487du, 0x3c875e18u), U64(0x4fb2a63fu, 0x3fef31ceu), U64(0x082876eeu, 0x3c91512fu), U64(0x711ece75u, 0x3fef2e85u), U64(0x981fe7f2u, 0x3c80472bu), U64(0x65e27cddu, 0x3fef2b45u), U64(0xc7d75ec6u, 0x3c9a02f0u), U64(0x341ddf29u, 0x3fef280eu), U64(0x3f71085eu, 0xbc96b87bu), U64(0xe1f56381u, 0x3fef24dfu), U64(0xe78260bfu, 0xbc803297u), U64(0x7591bb70u, 0x3fef21bau), U64(0x6d09ab31u, 0x3c82f7e1u), U64(0xf51fdee1u, 0x3fef1e9du), U64(0x5ccd9fbfu, 0xbc95b77eu), U64(0x66d10f13u, 0x3fef1b8au), U64(0x1a6fbffau, 0xbc3d219bu), U64(0xd0dad990u, 0x3fef187fu), U64(0x40b4251eu, 0xbc91e75cu), U64(0x39771b2fu, 0x3fef157eu), U64(0x720c0ab4u, 0x3c8b3782u), U64(0xa6e4030bu, 0x3fef1285u), U64(0xf1f7785au, 0x3c98a911u), U64(0x1f641589u, 0x3fef0f96u), U64(0x89cecb8fu, 0x3c6e1492u), U64(0xa93e2f56u, 0x3fef0cafu), U64(0x98db7dbbu, 0xbc61e7c9u), U64(0x4abd886bu, 0x3fef09d2u), U64(0x4db0abb6u, 0x3c834d75u), U64(0x0a31b715u, 0x3fef06feu), U64(0x11faadf4u, 0x3c85425cu), U64(0xedeeb2fdu, 0x3fef0432u), U64(0xe2ac744cu, 0x3c864201u), U64(0xfc4cd831u, 0x3fef0170u), U64(0xa03e2847u, 0xbc979517u), U64(0x3ba8ea32u, 0x3feefeb8u), U64(0x5dd3f84au, 0x3c8fdd39u), U64(0xb26416ffu, 0x3feefc08u), U64(0x46da4beeu, 0xbc800e2au), U64(0x66e3fa2du, 0x3feef962u), U64(0x3b8e5b04u, 0xbc86a380u), U64(0x5f929ff1u, 0x3feef6c5u), U64(0x03972b34u, 0xbc874308u), U64(0xa2de883bu, 0x3feef431u), U64(0xcc4b5068u, 0xbc924aedu), U64(0x373aa9cbu, 0x3feef1a7u), U64(0x0ae02d94u, 0xbc954de3u), U64(0x231e754au, 0x3feeef26u), U64(0x1b512d8eu, 0xbc9907f8u), U64(0x6d05d866u, 0x3feeecaeu), U64(0x7e1c03ecu, 0xbc94f248u), U64(0x1b7140efu, 0x3feeea40u), U64(0x3e9436d2u, 0xbc71d1e8u), U64(0x34e59ff7u, 0x3feee7dbu), U64(0x32fcb2f4u, 0x3c914a54u), U64(0xbfec6cf4u, 0x3feee57fu), U64(0xb3ce1b15u, 0xbc991919u), U64(0xc313a8e5u, 0x3feee32du), U64(0xa5562a2fu, 0x3c79c3bbu), U64(0x44ede173u, 0x3feee0e5u), U64(0xa72a4c6du, 0x3c859f48u), U64(0x4c123422u, 0x3feedea6u), U64(0x12e21658u, 0xbc85a716u), U64(0xdf1c5175u, 0x3feedc70u), U64(0x7a28698au, 0xbc931260u), U64(0x04ac801cu, 0x3feeda45u), U64(0x6f1d24d6u, 0x3c86421fu), U64(0xc367a024u, 0x3feed822u), U64(0x4817895bu, 0xbc58a78fu), U64(0x21f72e2au, 0x3feed60au), U64(0x815fce65u, 0xbc9348a6u), U64(0x2709468au, 0x3feed3fbu), U64(0x67499a1bu, 0xbc7c2c9bu), U64(0xd950a897u, 0x3feed1f5u), U64(0x984d9871u, 0x3c835c43u), U64(0x3f84b9d4u, 0x3feecffau), U64(0x60c2ac11u, 0x3c4363edu), U64(0x6061892du, 0x3feece08u), U64(0x8d9473a0u, 0xbc632afcu), U64(0x42a7d232u, 0x3feecc20u), U64(0x3b0664efu, 0x3c966609u), U64(0xed1d0057u, 0x3feeca41u), U64(0x44de020eu, 0xbc95fc5eu), U64(0x668b3237u, 0x3feec86du), U64(0xdaa10379u, 0x3c6ecce1u), U64(0xb5c13cd0u, 0x3feec6a2u), U64(0x8327c42fu, 0xbc7ea014u), U64(0xe192aed2u, 0x3feec4e1u), U64(0x3f0f1230u, 0x3c93ff8eu), U64(0xf0d7d3deu, 0x3feec32au), U64(0xd1a88022u, 0xbc7a843au), U64(0xea6db7d7u, 0x3feec17du), U64(0xbb7aafb0u, 0x3c7690ceu), U64(0xd5362a27u, 0x3feebfdau), U64(0xbf144e63u, 0x3c892ca3u), U64(0xb817c114u, 0x3feebe41u), U64(0xeb54e077u, 0x3c931dbdu), U64(0x99fddd0du, 0x3feebcb2u), U64(0xb04aa8b0u, 0xbc902c99u), U64(0x81d8abffu, 0x3feebb2du), U64(0x0071a38eu, 0xbc8f9434u), U64(0x769d2ca7u, 0x3feeb9b2u), U64(0x67e67118u, 0x3c73e34fu), U64(0x7f4531eeu, 0x3feeb841u), U64(0xdc93a349u, 0xbc87deccu), U64(0xa2cf6642u, 0x3feeb6dau), U64(0x197ba0f0u, 0xbc75a3b1u), U64(0xe83f4eefu, 0x3feeb57du), U64(0xbd0f385fu, 0xbc78dec6u), U64(0x569d4f82u, 0x3feeb42bu), U64(0x88075068u, 0x3c81bd28u), U64(0xf4f6ad27u, 0x3feeb2e2u), U64(0xec7b5cf6u, 0xbc861246u), U64(0xca5d920fu, 0x3feeb1a4u), U64(0xae89ef8fu, 0xbc896be8u), U64(0xdde910d2u, 0x3feeb070u), U64(0x18fdd78eu, 0x3c933505u), U64(0x36b527dau, 0x3feeaf47u), U64(0x90348602u, 0xbc88e6acu), U64(0xdbe2c4cfu, 0x3feeae27u), U64(0x2f8a9b05u, 0x3c7b98b7u), U64(0xd497c7fdu, 0x3feead12u), U64(0x1365c3acu, 0xbc91af7fu), U64(0x27ff07ccu, 0x3feeac08u), U64(0xe21c5409u, 0x3c9063e1u), U64(0xdd485429u, 0x3feeab07u), U64(0x40d1898au, 0xbc943a35u), U64(0xfba87a03u, 0x3feeaa11u), U64(0x5019c6eau, 0x3c34c785u), U64(0x8a5946b7u, 0x3feea926u), U64(0xddaa8090u, 0xbc951f58u), U64(0x90998b93u, 0x3feea845u), U64(0x2b64c035u, 0x3c9432e6u), U64(0x15ad2148u, 0x3feea76fu), U64(0x8e50a17cu, 0xbc82e164u), U64(0x20dceb71u, 0x3feea6a3u), U64(0x6199769fu, 0xbc8ce44au), U64(0xb976dc09u, 0x3feea5e1u), U64(0xda98a575u, 0x3c95f30eu), U64(0xe6cdf6f4u, 0x3feea52au), U64(0x3bef4da8u, 0xbc8c33c5u), U64(0xb03a5585u, 0x3feea47eu), U64(0xa8a72159u, 0x3c917ecdu), U64(0x1d1929fdu, 0x3feea3ddu), U64(0x892be9aeu, 0xbc845378u), U64(0x34ccc320u, 0x3feea346u), U64(0xcee1ae6eu, 0xbc9345f3u), U64(0xfebc8fb7u, 0x3feea2b9u), U64(0x78565858u, 0xbc93ceddu), U64(0x82552225u, 0x3feea238u), U64(0xdf910406u, 0xbc85c33fu), U64(0xc70833f6u, 0x3feea1c1u), U64(0x807e1964u, 0x3c5710aau), U64(0xd44ca973u, 0x3feea155u), U64(0xb5789604u, 0x3c81079au), U64(0xb19e9538u, 0x3feea0f4u), U64(0xbf5e2228u, 0xbc93b3efu), U64(0x667f3bcdu, 0x3feea09eu), U64(0x61cd7778u, 0x3c727df1u), U64(0xfa75173eu, 0x3feea052u), U64(0x8734b982u, 0xbc6a12adu), U64(0x750bdabfu, 0x3feea012u), U64(0x4a05b767u, 0x3c93f992u), U64(0xddd47645u, 0x3fee9fdcu), U64(0xb86da9eeu, 0xbc6367efu), U64(0x3c651a2fu, 0x3fee9fb2u), U64(0x39a8b5efu, 0xbc875579u), U64(0x98593ae5u, 0x3fee9f92u), U64(0x54e08851u, 0xbc80dc3du), U64(0xf9519484u, 0x3fee9f7du), U64(0x56fa9d1au, 0x3c51ed2fu), U64(0x66f42e87u, 0x3fee9f74u), U64(0x7e5a3ecfu, 0xbc781f64u), U64(0xe8ec5f74u, 0x3fee9f75u), U64(0x9006c909u, 0xbc88e67au), U64(0x86ead08au, 0x3fee9f82u), U64(0xc08b7db0u, 0xbc86ee4au), U64(0x48a58174u, 0x3fee9f9au), U64(0x66977ac8u, 0x3c865975u), U64(0x35d7cbfdu, 0x3fee9fbdu), U64(0x1e55e68au, 0xbc861932u), U64(0x564267c9u, 0x3fee9febu), U64(0x028a5c3au, 0x3c92c0b7u), U64(0xb1ab6e09u, 0x3feea024u), U64(0x5e09d4d3u, 0x3c909ccbu), U64(0x4fde5d3fu, 0x3feea069u), U64(0xf49cc78cu, 0x3c8a30fau), U64(0x38ac1cf6u, 0x3feea0b9u), U64(0xb94da51du, 0xbc7b32dcu), U64(0x73eb0187u, 0x3feea114u), U64(0x519d7b5bu, 0xbc92dad3u), U64(0x0976cfdbu, 0x3feea17bu), U64(0x5467c06bu, 0x3c94ecfdu), U64(0x0130c132u, 0x3feea1edu), U64(0x10fd15c2u, 0x3c87d514u), U64(0x62ff86f0u, 0x3feea26au), U64(0xabd66c55u, 0x3c65ebe1u), U64(0x36cf4e62u, 0x3feea2f3u), U64(0x29969871u, 0xbc760a36u), U64(0x8491c491u, 0x3feea387u), U64(0x2fb3cf42u, 0xbc88a1c5u), U64(0x543e1a12u, 0x3feea427u), U64(0xe3fdef5du, 0x3c8b18c6u), U64(0xadd106d9u, 0x3feea4d2u), U64(0xf13b3734u, 0xbc9369b6u), U64(0x994cce13u, 0x3feea589u), U64(0xdcb1390au, 0x3c90ec1du), U64(0x1eb941f7u, 0x3feea64cu), U64(0x3a19ff1eu, 0xbc805e84u), U64(0x4623c7adu, 0x3feea71au), U64(0x4f3afa1eu, 0xbc522ceau), U64(0x179f5b21u, 0x3feea7f4u), U64(0xd872576eu, 0xbc94d450u), U64(0x9b4492edu, 0x3feea8d9u), U64(0x9b958471u, 0x3c7c8854u), U64(0xd931a436u, 0x3feea9cau), U64(0x5b0e8a00u, 0x3c90ad67u), U64(0xd98a6699u, 0x3feeaac7u), U64(0x962f7877u, 0x3c931143u), U64(0xa478580fu, 0x3feeabd0u), U64(0xc1f0eab4u, 0x3c8db72fu), U64(0x422aa0dbu, 0x3feeace5u), U64(0x6f112479u, 0x3c93e9e9u), U64(0xbad61778u, 0x3feeae05u), U64(0x9cc5e7ffu, 0xbc65b660u), U64(0x16b5448cu, 0x3feeaf32u), U64(0xa4a38df0u, 0xbc8dac42u), U64(0x5e0866d9u, 0x3feeb06au), U64(0x59f35f44u, 0x3c7bf683u), U64(0x99157736u, 0x3feeb1aeu), U64(0x98b1ed84u, 0x3c8b99ddu), U64(0xd0282c8au, 0x3feeb2feu), U64(0xa71e3d83u, 0xbc93091fu), U64(0x0b91ffc6u, 0x3feeb45bu), U64(0x50cbb750u, 0xbc7885adu), U64(0x53aa2fe2u, 0x3feeb5c3u), U64(0x8b6c1e29u, 0xbc5da9b8u), U64(0xb0cdc5e5u, 0x3feeb737u), U64(0x5f3e0301u, 0xbc82d5e8u), U64(0x2b5f98e5u, 0x3feeb8b8u), U64(0x7c90b959u, 0xbc6c23f9u), U64(0xcbc8520fu, 0x3feeba44u), U64(0x28996971u, 0xbc516694u), U64(0x9a7670b3u, 0x3feebbddu), U64(0x22f4f9aau, 0xbc924343u), U64(0x9fde4e50u, 0x3feebd82u), U64(0xc1c4c014u, 0x3c71f2b2u), U64(0xe47a22a2u, 0x3feebf33u), U64(0xd7668e4bu, 0xbc85ca6cu), U64(0x70ca07bau, 0x3feec0f1u), U64(0x04f166b6u, 0xbc9294f3u), U64(0x4d53fe0du, 0x3feec2bbu), U64(0x2b91ce27u, 0x3c71affcu), U64(0x82a3f090u, 0x3feec491u), U64(0x414c07d3u, 0xbc8a1e58u), U64(0x194bb8d5u, 0x3feec674u), U64(0xe10a73bbu, 0x3c6dd235u), U64(0x19e32323u, 0x3feec863u), U64(0x58a20091u, 0xbc79740bu), U64(0x8d07f29eu, 0x3feeca5eu), U64(0x22622263u, 0xbc87c504u), U64(0x7b5de565u, 0x3feecc66u), U64(0x0a2b96c2u, 0x3c916583u), U64(0xed8eb8bbu, 0x3feece7au), U64(0xe3e231d5u, 0x3c8b1c86u), U64(0xec4a2d33u, 0x3feed09bu), U64(0xbe27874bu, 0xbc903d5cu), U64(0x80460ad8u, 0x3feed2c9u), U64(0xd3bcbb15u, 0xbc91bbd1u), U64(0xb23e255du, 0x3feed503u), U64(0x8980fce0u, 0x3c598617u), U64(0x8af46052u, 0x3feed74au), U64(0x9cee31d2u, 0x3c90cc31u), U64(0x1330b358u, 0x3feed99eu), U64(0x75b1f2a5u, 0xbc894729u), U64(0x53c12e59u, 0x3feedbfeu), U64(0x6e735ab3u, 0x3c846984u), U64(0x5579fdbfu, 0x3feede6bu), U64(0xa34b7e7fu, 0x3c7d8157u), U64(0x21356ebau, 0x3feee0e5u), U64(0x978e9db4u, 0xbc82dfcdu), U64(0xbfd3f37au, 0x3feee36bu), U64(0x231ebb7du, 0x3c8c8a4eu), U64(0x3a3c2774u, 0x3feee5ffu), U64(0x92cb3387u, 0x3c8c1a77u), U64(0x995ad3adu, 0x3feee89fu), U64(0x11a142e5u, 0xbc888c8du), U64(0xe622f2ffu, 0x3feeeb4cu), U64(0x4ad1d9fau, 0xbc907b8fu), U64(0x298db666u, 0x3feeee07u), U64(0xa41433c7u, 0x3c889c2eu), U64(0x6c9a8952u, 0x3feef0ceu), U64(0x56dcaebau, 0xbc55c3d9u), U64(0xb84f15fbu, 0x3feef3a2u), U64(0xdac8ff80u, 0xbc7274aeu), U64(0x15b749b1u, 0x3feef684u), U64(0x3da6f640u, 0xbc90a40eu), U64(0x8de5593au, 0x3feef972u), U64(0xce76df06u, 0x3c85c620u), U64(0x29f1c52au, 0x3feefc6eu), U64(0x38ad9334u, 0xbc68d6f4u), U64(0xf2fb5e47u, 0x3feeff76u), U64(0xe1b51e41u, 0xbc8fda52u), U64(0xf22749e4u, 0x3fef028cu), U64(0x6b588a35u, 0xbc91eee2u), U64(0x30a1064au, 0x3fef05b0u), U64(0x7b3e2cd8u, 0xbc32141au), U64(0xb79a6f1fu, 0x3fef08e0u), U64(0x0a5fddcdu, 0x3c74ffd7u), U64(0x904bc1d2u, 0x3fef0c1eu), U64(0x507554e5u, 0xbc302899u), U64(0xc3f3a207u, 0x3fef0f69u), U64(0xfa9298acu, 0xbc91bdfbu), U64(0x5bd71e09u, 0x3fef12c2u), U64(0xd4c0010cu, 0xbc80dda2u), U64(0x6141b33du, 0x3fef1628u), U64(0x30af0cb3u, 0x3c736eaeu), U64(0xdd85529cu, 0x3fef199bu), U64(0xaadf8d68u, 0xbc8a007du), U64(0xd9fa652cu, 0x3fef1d1cu), U64(0x5c9ffd94u, 0x3c8ee332u), U64(0x5fffd07au, 0x3fef20abu), U64(0x391181d3u, 0x3c836909u), U64(0x78fafb22u, 0x3fef2447u), U64(0xd10959acu, 0x3c84e08fu), U64(0x2e57d14bu, 0x3fef27f1u), U64(0xdbdf9547u, 0xbc811cd7u), U64(0x8988c933u, 0x3fef2ba8u), U64(0x384e1a67u, 0x3c63cdafu), U64(0x9406e7b5u, 0x3fef2f6du), U64(0x7bef6621u, 0xbc7ac28bu), U64(0x5751c4dbu, 0x3fef3340u), U64(0x6c921968u, 0x3c676b2cu), U64(0xdcef9069u, 0x3fef3720u), U64(0x7207b9e1u, 0xbc703058u), U64(0x2e6d1675u, 0x3fef3b0fu), U64(0x83ccb5d2u, 0xbc808a18u), U64(0x555dc3fau, 0x3fef3f0bu), U64(0x592af7fcu, 0xbc8cc734u), U64(0x5b5bab74u, 0x3fef4315u), U64(0x3ffffa6fu, 0xbc8fad5du), U64(0x4a07897cu, 0x3fef472du), U64(0x44f587e8u, 0x3c87752au), U64(0x2b08c968u, 0x3fef4b53u), U64(0x3875a949u, 0xbc900daeu), U64(0x080d89f2u, 0x3fef4f87u), U64(0xefeef52eu, 0x3c85b66fu), U64(0xeacaa1d6u, 0x3fef53c8u), U64(0xa63d07a7u, 0x3c74a385u), U64(0xdcfba487u, 0x3fef5818u), U64(0xd908a96eu, 0x3c5159d9u), U64(0xe862e6d3u, 0x3fef5c76u), U64(0x2040220fu, 0xbc82919eu), U64(0x16c98398u, 0x3fef60e3u), U64(0x16117a68u, 0x3c8c254du), U64(0x71ff6075u, 0x3fef655du), U64(0xd5c192acu, 0x3c8e5a50u), U64(0x03db3285u, 0x3fef69e6u), U64(0x9fbd0e03u, 0xbc8d8c32u), U64(0xd63a8315u, 0x3fef6e7cu), U64(0xac016b4bu, 0x3c843a59u), U64(0xf301b460u, 0x3fef7321u), U64(0xfbd5f2a6u, 0xbc8ea6e6u), U64(0x641c0658u, 0x3fef77d5u), U64(0x07b43e1fu, 0xbc82d521u), U64(0x337b9b5fu, 0x3fef7c97u), U64(0xeab2cbb4u, 0xbc63e8e3u), U64(0x6b197d17u, 0x3fef8167u), U64(0x3b470dc9u, 0xbc892ab9u), U64(0x14f5a129u, 0x3fef8646u), U64(0xcd0d2cd9u, 0xbc8b7966u), U64(0x3b16ee12u, 0x3fef8b33u), U64(0x603a88d3u, 0x3c74b604u), U64(0xe78b3ff6u, 0x3fef902eu), U64(0x4c2ff1cfu, 0xbc776caau), U64(0x24676d76u, 0x3fef9539u), U64(0x519d7271u, 0x3c83c5ecu), U64(0xfbc74c83u, 0x3fef9a51u), U64(0x525d9940u, 0xbc81d5fcu), U64(0x77cdb740u, 0x3fef9f79u), U64(0x8fd391f0u, 0xbc8ff712u), U64(0xa2a490dau, 0x3fefa4afu), U64(0xaaea3d21u, 0x3c855cd8u), U64(0x867cca6eu, 0x3fefa9f4u), U64(0xe223747du, 0xbc8dae98u), U64(0x2d8e67f1u, 0x3fefaf48u), U64(0x7c2bed4au, 0x3c826994u), U64(0xa2188510u, 0x3fefb4aau), U64(0x41aa2008u, 0x3c8ec3bcu), U64(0xee615a27u, 0x3fefba1bu), U64(0x7e9afe9eu, 0xbc83b613u), U64(0x1cb6412au, 0x3fefbf9cu), U64(0xc3a9eb32u, 0x3c842b94u), U64(0x376bba97u, 0x3fefc52bu), U64(0x878ba7c7u, 0xbc69fa74u), U64(0x48dd7274u, 0x3fefcac9u), U64(0x31d185eeu, 0x3c8a64a9u), U64(0x5b6e4540u, 0x3fefd076u), U64(0x75ee0efeu, 0x3c901f3au), U64(0x798844f8u, 0x3fefd632u), U64(0xe43be3edu, 0xbc8e37bau), U64(0xad9cbe14u, 0x3fefdbfdu), U64(0xe6ed84fau, 0xbc516a9cu), U64(0x02243c89u, 0x3fefe1d8u), U64(0x4d91cd9du, 0x3c77893bu), U64(0x819e90d8u, 0x3fefe7c1u), U64(0xb2effc76u, 0xbc699c7du), U64(0x3692d514u, 0x3fefedbau), U64(0x4160cc89u, 0x3c5305c1u), U64(0x2b8f71f1u, 0x3feff3c2u), U64(0x677f9840u, 0x3c64b458u), U64(0x6b2a23d9u, 0x3feff9d9u)); - -const PI_F: f32 = 3.1415927410125732f; - -fn load_World(o: u32) -> World { return World(world_data[(o + 0u)], world_data[(o + 1u)], world_data[(o + 2u)], bitcast(world_data[(o + 3u)]), world_data[(o + 4u)], world_data[(o + 5u)], world_data[(o + 6u)], world_data[(o + 7u)], world_data[(o + 8u)], i32(world_data[(o + 9u)]), i32(world_data[(o + 10u)]), i32(world_data[(o + 11u)]), i32(world_data[(o + 12u)]), world_data[(o + 13u)], world_data[(o + 14u)], world_data[(o + 15u)], world_data[(o + 16u)], world_data[(o + 17u)], load_NearGrid((o + 18u)), world_data[(o + 28u)], bitcast(world_data[(o + 29u)]), world_data[(o + 30u)], world_data[(o + 31u)], world_data[(o + 32u)], load_NearGrid((o + 34u)), world_data[(o + 44u)], world_data[(o + 45u)], world_data[(o + 46u)], world_data[(o + 47u)], world_data[(o + 48u)], load_NearGrid((o + 50u)), U64(world_data[(o + 60u)], world_data[(o + 60u) + 1u]), world_data[(o + 62u)]); } - -fn load_NearGrid(o: u32) -> NearGrid { return NearGrid(U64(world_data[(o + 0u)], world_data[(o + 0u) + 1u]), U64(world_data[(o + 2u)], world_data[(o + 2u) + 1u]), U64(world_data[(o + 4u)], world_data[(o + 4u) + 1u]), world_data[(o + 6u)], world_data[(o + 7u)], world_data[(o + 8u)], world_data[(o + 9u)]); } - -fn load_VehicleDesc(o: u32) -> VehicleDesc { return VehicleDesc(array(load_WheelDesc(((o + 0u) + 0u)), load_WheelDesc(((o + 0u) + 10u)), load_WheelDesc(((o + 0u) + 20u)), load_WheelDesc(((o + 0u) + 30u))), world_data[(o + 40u)], world_data[(o + 41u)], world_data[(o + 42u)], world_data[(o + 43u)], world_data[(o + 44u)], world_data[(o + 45u)], U64(world_data[(o + 46u)], world_data[(o + 46u) + 1u]), U64(world_data[(o + 48u)], world_data[(o + 48u) + 1u]), U64(world_data[(o + 50u)], world_data[(o + 50u) + 1u]), U64(world_data[(o + 52u)], world_data[(o + 52u) + 1u]), bitcast(world_data[(o + 54u)]), bitcast(world_data[(o + 55u)]), bitcast(world_data[(o + 56u)]), bitcast(world_data[(o + 57u)]), bitcast(world_data[(o + 58u)]), bitcast(world_data[(o + 59u)]), bitcast(world_data[(o + 60u)]), bitcast(world_data[(o + 61u)]), vec2(bitcast(world_data[(o + 62u) + 0u]), bitcast(world_data[(o + 62u) + 1u])), vec2(bitcast(world_data[(o + 64u) + 0u]), bitcast(world_data[(o + 64u) + 1u])), vec2(bitcast(world_data[(o + 66u) + 0u]), bitcast(world_data[(o + 66u) + 1u])), vec2(bitcast(world_data[(o + 68u) + 0u]), bitcast(world_data[(o + 68u) + 1u])), vec2(bitcast(world_data[(o + 70u) + 0u]), bitcast(world_data[(o + 70u) + 1u])), vec2(bitcast(world_data[(o + 72u) + 0u]), bitcast(world_data[(o + 72u) + 1u])), vec2(bitcast(world_data[(o + 74u) + 0u]), bitcast(world_data[(o + 74u) + 1u])), bitcast(world_data[(o + 76u)]), bitcast(world_data[(o + 77u)]), bitcast(world_data[(o + 78u)]), bitcast(world_data[(o + 79u)]), bitcast(world_data[(o + 80u)]), bitcast(world_data[(o + 81u)]), bitcast(world_data[(o + 82u)]), bitcast(world_data[(o + 83u)])); } - -fn load_WheelDesc(o: u32) -> WheelDesc { return WheelDesc(vec2(bitcast(world_data[(o + 0u) + 0u]), bitcast(world_data[(o + 0u) + 1u])), world_data[(o + 2u)], world_data[(o + 3u)], bitcast(world_data[(o + 4u)]), bitcast(world_data[(o + 5u)]), bitcast(world_data[(o + 6u)]), bitcast(world_data[(o + 7u)]), U64(world_data[(o + 8u)], world_data[(o + 8u) + 1u])); } - -fn load_RayNode(o: u32) -> RayNode { return RayNode(world_data[(o + 0u)], world_data[(o + 1u)], world_data[(o + 2u)], world_data[(o + 3u)], vec4(bitcast(world_data[(o + 4u) + 0u]), bitcast(world_data[(o + 4u) + 1u]), bitcast(world_data[(o + 4u) + 2u]), bitcast(world_data[(o + 4u) + 3u])), vec4(bitcast(world_data[(o + 8u) + 0u]), bitcast(world_data[(o + 8u) + 1u]), bitcast(world_data[(o + 8u) + 2u]), bitcast(world_data[(o + 8u) + 3u]))); } - -fn load_TileCell(o: u32) -> TileCell { return TileCell(world_data[(o + 0u)], world_data[(o + 1u)], vec2(bitcast(world_data[(o + 2u) + 0u]), bitcast(world_data[(o + 2u) + 1u])), vec2(bitcast(world_data[(o + 4u) + 0u]), bitcast(world_data[(o + 4u) + 1u]))); } - -fn load_PathSeg(o: u32) -> PathSeg { return PathSeg(bitcast(world_data[(o + 0u)]), bitcast(world_data[(o + 1u)]), bitcast(world_data[(o + 2u)]), bitcast(world_data[(o + 3u)]), bitcast(world_data[(o + 4u)]), bitcast(world_data[(o + 5u)]), bitcast(world_data[(o + 6u)]), bitcast(world_data[(o + 7u)])); } - -fn load_SurfaceDev(o: u32) -> SurfaceDev { return SurfaceDev(bitcast(world_data[(o + 0u)]), bitcast(world_data[(o + 1u)]), bitcast(world_data[(o + 2u)]), bitcast(world_data[(o + 3u)])); } - -fn load_ShapeDev(o: u32) -> ShapeDev { return ShapeDev(vec2(bitcast(world_data[(o + 0u) + 0u]), bitcast(world_data[(o + 0u) + 1u])), bitcast(world_data[(o + 2u)]), bitcast(world_data[(o + 3u)]), U64(world_data[(o + 4u)], world_data[(o + 4u) + 1u]), U64(world_data[(o + 6u)], world_data[(o + 6u) + 1u]), U64(world_data[(o + 8u)], world_data[(o + 8u) + 1u]), U64(world_data[(o + 10u)], world_data[(o + 10u) + 1u]), world_data[(o + 12u)], world_data[(o + 13u)], world_data[(o + 14u)], world_data[(o + 15u)]); } - -fn load_SensorDesc(o: u32) -> SensorDesc { return SensorDesc(world_data[(o + 0u)], bitcast(world_data[(o + 1u)]), bitcast(world_data[(o + 2u)]), vec2(bitcast(world_data[(o + 3u) + 0u]), bitcast(world_data[(o + 3u) + 1u])), world_data[(o + 5u)]); } - -fn load_Car(o: u32) { -sim_car.position = vec2(bitcast(cars[(o+0u) + 0u]), bitcast(cars[(o+0u) + 1u])); -sim_car.velocity = vec2(bitcast(cars[(o+2u) + 0u]), bitcast(cars[(o+2u) + 1u])); -sim_car.acceleration = vec2(bitcast(cars[(o+4u) + 0u]), bitcast(cars[(o+4u) + 1u])); -sim_car.reset_acceleration = vec2(bitcast(cars[(o+6u) + 0u]), bitcast(cars[(o+6u) + 1u])); -sim_car.basis_x = vec2(bitcast(cars[(o+8u) + 0u]), bitcast(cars[(o+8u) + 1u])); -sim_car.basis_y = vec2(bitcast(cars[(o+10u) + 0u]), bitcast(cars[(o+10u) + 1u])); -sim_car.rotation = bitcast(cars[(o+12u)]); -sim_car.transform_angle = bitcast(cars[(o+13u)]); -sim_car.angular_velocity = bitcast(cars[(o+14u)]); -sim_car.boost_energy = bitcast(cars[(o+15u)]); -sim_car.flags = cars[(o+16u)]; -sim_car.collision_count = cars[(o+17u)]; -sim_car.tick = U64(cars[(o+18u)], cars[(o+18u) + 1u]); -for (var copy_1=0u; copy_1<4u+params.pad; copy_1=copy_1+1u) { -sim_car.wheel_angle[copy_1] = bitcast(cars[((o+20u)+copy_1*1u)]); -} -for (var copy_2=0u; copy_2<4u+params.pad; copy_2=copy_2+1u) { -sim_car.wheel_previous[copy_2] = vec2(bitcast(cars[((o+24u)+copy_2*2u) + 0u]), bitcast(cars[((o+24u)+copy_2*2u) + 1u])); -} -for (var copy_3=0u; copy_3<4u+params.pad; copy_3=copy_3+1u) { -sim_car.wheel_surface[copy_3] = cars[((o+32u)+copy_3*1u)]; -} -sim_car.wheel_has_previous = cars[(o+36u)]; -sim_car.pair_count = cars[(o+37u)]; -sim_car.contact_count = cars[(o+38u)]; -sim_car.error = cars[(o+39u)]; -sim_car.shape_position = vec2(bitcast(cars[(o+40u) + 0u]), bitcast(cars[(o+40u) + 1u])); -sim_car.shape_size = vec2(bitcast(cars[(o+42u) + 0u]), bitcast(cars[(o+42u) + 1u])); -sim_car.leaf_min = vec2(bitcast(cars[(o+44u) + 0u]), bitcast(cars[(o+44u) + 1u])); -sim_car.leaf_max = vec2(bitcast(cars[(o+46u) + 0u]), bitcast(cars[(o+46u) + 1u])); -for (var copy_4=0u; copy_4<16u+params.pad; copy_4=copy_4+1u) { -sim_car.pairs[copy_4] = cars[((o+48u)+copy_4*1u)]; -} -for (var copy_5=0u; copy_5<16u+params.pad; copy_5=copy_5+1u) { -sim_car.axes[copy_5] = vec2(bitcast(cars[((o+64u)+copy_5*2u) + 0u]), bitcast(cars[((o+64u)+copy_5*2u) + 1u])); -} -for (var copy_6=0u; copy_6<32u+params.pad; copy_6=copy_6+1u) { -sim_car.contacts[copy_6].normal = vec2(bitcast(cars[(((o+96u)+copy_6*22u)+0u) + 0u]), bitcast(cars[(((o+96u)+copy_6*22u)+0u) + 1u])); -sim_car.contacts[copy_6].point = vec2(bitcast(cars[(((o+96u)+copy_6*22u)+2u) + 0u]), bitcast(cars[(((o+96u)+copy_6*22u)+2u) + 1u])); -sim_car.contacts[copy_6].wall_point = vec2(bitcast(cars[(((o+96u)+copy_6*22u)+4u) + 0u]), bitcast(cars[(((o+96u)+copy_6*22u)+4u) + 1u])); -sim_car.contacts[copy_6].local_point = vec2(bitcast(cars[(((o+96u)+copy_6*22u)+6u) + 0u]), bitcast(cars[(((o+96u)+copy_6*22u)+6u) + 1u])); -sim_car.contacts[copy_6].wall_local_point = vec2(bitcast(cars[(((o+96u)+copy_6*22u)+8u) + 0u]), bitcast(cars[(((o+96u)+copy_6*22u)+8u) + 1u])); -sim_car.contacts[copy_6].depth = bitcast(cars[(((o+96u)+copy_6*22u)+10u)]); -sim_car.contacts[copy_6].shape = cars[(((o+96u)+copy_6*22u)+11u)]; -sim_car.contacts[copy_6].flags = cars[(((o+96u)+copy_6*22u)+12u)]; -sim_car.contacts[copy_6].normal_mass = bitcast(cars[(((o+96u)+copy_6*22u)+13u)]); -sim_car.contacts[copy_6].tangent_mass = bitcast(cars[(((o+96u)+copy_6*22u)+14u)]); -sim_car.contacts[copy_6].bias = bitcast(cars[(((o+96u)+copy_6*22u)+15u)]); -sim_car.contacts[copy_6].bounce = bitcast(cars[(((o+96u)+copy_6*22u)+16u)]); -sim_car.contacts[copy_6].friction = bitcast(cars[(((o+96u)+copy_6*22u)+17u)]); -sim_car.contacts[copy_6].acc_normal = bitcast(cars[(((o+96u)+copy_6*22u)+18u)]); -sim_car.contacts[copy_6].acc_tangent = bitcast(cars[(((o+96u)+copy_6*22u)+19u)]); -sim_car.contacts[copy_6].acc_bias = bitcast(cars[(((o+96u)+copy_6*22u)+20u)]); -sim_car.contacts[copy_6].acc_bias_center = bitcast(cars[(((o+96u)+copy_6*22u)+21u)]); -} -} - -fn store_Car(o: u32) { -cars[(o+0u) + 0u] = bitcast(sim_car.position.x); -cars[(o+0u) + 1u] = bitcast(sim_car.position.y); -cars[(o+2u) + 0u] = bitcast(sim_car.velocity.x); -cars[(o+2u) + 1u] = bitcast(sim_car.velocity.y); -cars[(o+4u) + 0u] = bitcast(sim_car.acceleration.x); -cars[(o+4u) + 1u] = bitcast(sim_car.acceleration.y); -cars[(o+6u) + 0u] = bitcast(sim_car.reset_acceleration.x); -cars[(o+6u) + 1u] = bitcast(sim_car.reset_acceleration.y); -cars[(o+8u) + 0u] = bitcast(sim_car.basis_x.x); -cars[(o+8u) + 1u] = bitcast(sim_car.basis_x.y); -cars[(o+10u) + 0u] = bitcast(sim_car.basis_y.x); -cars[(o+10u) + 1u] = bitcast(sim_car.basis_y.y); -cars[(o+12u)] = bitcast(sim_car.rotation); -cars[(o+13u)] = bitcast(sim_car.transform_angle); -cars[(o+14u)] = bitcast(sim_car.angular_velocity); -cars[(o+15u)] = bitcast(sim_car.boost_energy); -cars[(o+16u)] = u32(sim_car.flags); -cars[(o+17u)] = u32(sim_car.collision_count); -cars[(o+18u)] = sim_car.tick.x; cars[(o+18u) + 1u] = sim_car.tick.y; -for (var copy_7=0u; copy_7<4u+params.pad; copy_7=copy_7+1u) { -cars[((o+20u)+copy_7*1u)] = bitcast(sim_car.wheel_angle[copy_7]); -} -for (var copy_8=0u; copy_8<4u+params.pad; copy_8=copy_8+1u) { -cars[((o+24u)+copy_8*2u) + 0u] = bitcast(sim_car.wheel_previous[copy_8].x); -cars[((o+24u)+copy_8*2u) + 1u] = bitcast(sim_car.wheel_previous[copy_8].y); -} -for (var copy_9=0u; copy_9<4u+params.pad; copy_9=copy_9+1u) { -cars[((o+32u)+copy_9*1u)] = u32(sim_car.wheel_surface[copy_9]); -} -cars[(o+36u)] = u32(sim_car.wheel_has_previous); -cars[(o+37u)] = u32(sim_car.pair_count); -cars[(o+38u)] = u32(sim_car.contact_count); -cars[(o+39u)] = u32(sim_car.error); -cars[(o+40u) + 0u] = bitcast(sim_car.shape_position.x); -cars[(o+40u) + 1u] = bitcast(sim_car.shape_position.y); -cars[(o+42u) + 0u] = bitcast(sim_car.shape_size.x); -cars[(o+42u) + 1u] = bitcast(sim_car.shape_size.y); -cars[(o+44u) + 0u] = bitcast(sim_car.leaf_min.x); -cars[(o+44u) + 1u] = bitcast(sim_car.leaf_min.y); -cars[(o+46u) + 0u] = bitcast(sim_car.leaf_max.x); -cars[(o+46u) + 1u] = bitcast(sim_car.leaf_max.y); -for (var copy_10=0u; copy_10<16u+params.pad; copy_10=copy_10+1u) { -cars[((o+48u)+copy_10*1u)] = u32(sim_car.pairs[copy_10]); -} -for (var copy_11=0u; copy_11<16u+params.pad; copy_11=copy_11+1u) { -cars[((o+64u)+copy_11*2u) + 0u] = bitcast(sim_car.axes[copy_11].x); -cars[((o+64u)+copy_11*2u) + 1u] = bitcast(sim_car.axes[copy_11].y); -} -for (var copy_12=0u; copy_12<32u+params.pad; copy_12=copy_12+1u) { -cars[(((o+96u)+copy_12*22u)+0u) + 0u] = bitcast(sim_car.contacts[copy_12].normal.x); -cars[(((o+96u)+copy_12*22u)+0u) + 1u] = bitcast(sim_car.contacts[copy_12].normal.y); -cars[(((o+96u)+copy_12*22u)+2u) + 0u] = bitcast(sim_car.contacts[copy_12].point.x); -cars[(((o+96u)+copy_12*22u)+2u) + 1u] = bitcast(sim_car.contacts[copy_12].point.y); -cars[(((o+96u)+copy_12*22u)+4u) + 0u] = bitcast(sim_car.contacts[copy_12].wall_point.x); -cars[(((o+96u)+copy_12*22u)+4u) + 1u] = bitcast(sim_car.contacts[copy_12].wall_point.y); -cars[(((o+96u)+copy_12*22u)+6u) + 0u] = bitcast(sim_car.contacts[copy_12].local_point.x); -cars[(((o+96u)+copy_12*22u)+6u) + 1u] = bitcast(sim_car.contacts[copy_12].local_point.y); -cars[(((o+96u)+copy_12*22u)+8u) + 0u] = bitcast(sim_car.contacts[copy_12].wall_local_point.x); -cars[(((o+96u)+copy_12*22u)+8u) + 1u] = bitcast(sim_car.contacts[copy_12].wall_local_point.y); -cars[(((o+96u)+copy_12*22u)+10u)] = bitcast(sim_car.contacts[copy_12].depth); -cars[(((o+96u)+copy_12*22u)+11u)] = u32(sim_car.contacts[copy_12].shape); -cars[(((o+96u)+copy_12*22u)+12u)] = u32(sim_car.contacts[copy_12].flags); -cars[(((o+96u)+copy_12*22u)+13u)] = bitcast(sim_car.contacts[copy_12].normal_mass); -cars[(((o+96u)+copy_12*22u)+14u)] = bitcast(sim_car.contacts[copy_12].tangent_mass); -cars[(((o+96u)+copy_12*22u)+15u)] = bitcast(sim_car.contacts[copy_12].bias); -cars[(((o+96u)+copy_12*22u)+16u)] = bitcast(sim_car.contacts[copy_12].bounce); -cars[(((o+96u)+copy_12*22u)+17u)] = bitcast(sim_car.contacts[copy_12].friction); -cars[(((o+96u)+copy_12*22u)+18u)] = bitcast(sim_car.contacts[copy_12].acc_normal); -cars[(((o+96u)+copy_12*22u)+19u)] = bitcast(sim_car.contacts[copy_12].acc_tangent); -cars[(((o+96u)+copy_12*22u)+20u)] = bitcast(sim_car.contacts[copy_12].acc_bias); -cars[(((o+96u)+copy_12*22u)+21u)] = bitcast(sim_car.contacts[copy_12].acc_bias_center); -} -} - -fn load_Agent(o: u32) { -for (var copy_13=0u; copy_13<5u+params.pad; copy_13=copy_13+1u) { -sim_agent.controls[copy_13] = U64(agents[((o+0u)+copy_13*2u)], agents[((o+0u)+copy_13*2u) + 1u]); -} -sim_agent.previous_offset = U64(agents[(o+10u)], agents[(o+10u) + 1u]); -sim_agent.tracker_score = U64(agents[(o+12u)], agents[(o+12u) + 1u]); -sim_agent.lap_crossing_fraction = U64(agents[(o+14u)], agents[(o+14u) + 1u]); -sim_agent.lap_count = U64(agents[(o+16u)], agents[(o+16u) + 1u]); -sim_agent.network_novelty = U64(agents[(o+18u)], agents[(o+18u) + 1u]); -sim_agent.update_count = U64(agents[(o+20u)], agents[(o+20u) + 1u]); -sim_agent.collision_count = U64(agents[(o+22u)], agents[(o+22u) + 1u]); -sim_agent.total_score = U64(agents[(o+24u)], agents[(o+24u) + 1u]); -sim_agent.total_speed = U64(agents[(o+26u)], agents[(o+26u) + 1u]); -sim_agent.total_abs_steering = U64(agents[(o+28u)], agents[(o+28u) + 1u]); -sim_agent.total_actual_distance = U64(agents[(o+30u)], agents[(o+30u) + 1u]); -sim_agent.total_drifting_amount = U64(agents[(o+32u)], agents[(o+32u) + 1u]); -sim_agent.total_momentum_change_frequency = U64(agents[(o+34u)], agents[(o+34u) + 1u]); -sim_agent.total_throttle_change_frequency = U64(agents[(o+36u)], agents[(o+36u) + 1u]); -sim_agent.total_braking_frequency = U64(agents[(o+38u)], agents[(o+38u) + 1u]); -sim_agent.total_distance_from_wall = U64(agents[(o+40u)], agents[(o+40u) + 1u]); -sim_agent.total_distance_from_center = U64(agents[(o+42u)], agents[(o+42u) + 1u]); -sim_agent.best_lap_time = U64(agents[(o+44u)], agents[(o+44u) + 1u]); -sim_agent.idle_ticks = U64(agents[(o+46u)], agents[(o+46u) + 1u]); -sim_agent.previous_speed = U64(agents[(o+48u)], agents[(o+48u) + 1u]); -sim_agent.previous_throttle = U64(agents[(o+50u)], agents[(o+50u) + 1u]); -sim_agent.previous_score = U64(agents[(o+52u)], agents[(o+52u) + 1u]); -sim_agent.previous_lap_count = U64(agents[(o+54u)], agents[(o+54u) + 1u]); -sim_agent.lap_start_tick = U64(agents[(o+56u)], agents[(o+56u) + 1u]); -sim_agent.drift_ticks = U64(agents[(o+58u)], agents[(o+58u) + 1u]); -sim_agent.current_slip_angle_degrees = U64(agents[(o+60u)], agents[(o+60u) + 1u]); -sim_agent.continuous_drift_time = U64(agents[(o+62u)], agents[(o+62u) + 1u]); -sim_agent.max_continuous_drift_time = U64(agents[(o+64u)], agents[(o+64u) + 1u]); -sim_agent.all_laps_time_sum = U64(agents[(o+66u)], agents[(o+66u) + 1u]); -for (var copy_14=0u; copy_14<10u+params.pad; copy_14=copy_14+1u) { -sim_agent.recent[copy_14] = U64(agents[((o+68u)+copy_14*2u)], agents[((o+68u)+copy_14*2u) + 1u]); -} -sim_agent.recent_start = agents[(o+88u)]; -sim_agent.recent_len = agents[(o+89u)]; -sim_agent.deactivated_at = U64(agents[(o+90u)], agents[(o+90u) + 1u]); -sim_agent.flags = agents[(o+92u)]; -sim_agent.pad = agents[(o+93u)]; -} - -fn store_Agent(o: u32) { -for (var copy_15=0u; copy_15<5u+params.pad; copy_15=copy_15+1u) { -agents[((o+0u)+copy_15*2u)] = sim_agent.controls[copy_15].x; agents[((o+0u)+copy_15*2u) + 1u] = sim_agent.controls[copy_15].y; -} -agents[(o+10u)] = sim_agent.previous_offset.x; agents[(o+10u) + 1u] = sim_agent.previous_offset.y; -agents[(o+12u)] = sim_agent.tracker_score.x; agents[(o+12u) + 1u] = sim_agent.tracker_score.y; -agents[(o+14u)] = sim_agent.lap_crossing_fraction.x; agents[(o+14u) + 1u] = sim_agent.lap_crossing_fraction.y; -agents[(o+16u)] = sim_agent.lap_count.x; agents[(o+16u) + 1u] = sim_agent.lap_count.y; -agents[(o+18u)] = sim_agent.network_novelty.x; agents[(o+18u) + 1u] = sim_agent.network_novelty.y; -agents[(o+20u)] = sim_agent.update_count.x; agents[(o+20u) + 1u] = sim_agent.update_count.y; -agents[(o+22u)] = sim_agent.collision_count.x; agents[(o+22u) + 1u] = sim_agent.collision_count.y; -agents[(o+24u)] = sim_agent.total_score.x; agents[(o+24u) + 1u] = sim_agent.total_score.y; -agents[(o+26u)] = sim_agent.total_speed.x; agents[(o+26u) + 1u] = sim_agent.total_speed.y; -agents[(o+28u)] = sim_agent.total_abs_steering.x; agents[(o+28u) + 1u] = sim_agent.total_abs_steering.y; -agents[(o+30u)] = sim_agent.total_actual_distance.x; agents[(o+30u) + 1u] = sim_agent.total_actual_distance.y; -agents[(o+32u)] = sim_agent.total_drifting_amount.x; agents[(o+32u) + 1u] = sim_agent.total_drifting_amount.y; -agents[(o+34u)] = sim_agent.total_momentum_change_frequency.x; agents[(o+34u) + 1u] = sim_agent.total_momentum_change_frequency.y; -agents[(o+36u)] = sim_agent.total_throttle_change_frequency.x; agents[(o+36u) + 1u] = sim_agent.total_throttle_change_frequency.y; -agents[(o+38u)] = sim_agent.total_braking_frequency.x; agents[(o+38u) + 1u] = sim_agent.total_braking_frequency.y; -agents[(o+40u)] = sim_agent.total_distance_from_wall.x; agents[(o+40u) + 1u] = sim_agent.total_distance_from_wall.y; -agents[(o+42u)] = sim_agent.total_distance_from_center.x; agents[(o+42u) + 1u] = sim_agent.total_distance_from_center.y; -agents[(o+44u)] = sim_agent.best_lap_time.x; agents[(o+44u) + 1u] = sim_agent.best_lap_time.y; -agents[(o+46u)] = sim_agent.idle_ticks.x; agents[(o+46u) + 1u] = sim_agent.idle_ticks.y; -agents[(o+48u)] = sim_agent.previous_speed.x; agents[(o+48u) + 1u] = sim_agent.previous_speed.y; -agents[(o+50u)] = sim_agent.previous_throttle.x; agents[(o+50u) + 1u] = sim_agent.previous_throttle.y; -agents[(o+52u)] = sim_agent.previous_score.x; agents[(o+52u) + 1u] = sim_agent.previous_score.y; -agents[(o+54u)] = sim_agent.previous_lap_count.x; agents[(o+54u) + 1u] = sim_agent.previous_lap_count.y; -agents[(o+56u)] = sim_agent.lap_start_tick.x; agents[(o+56u) + 1u] = sim_agent.lap_start_tick.y; -agents[(o+58u)] = sim_agent.drift_ticks.x; agents[(o+58u) + 1u] = sim_agent.drift_ticks.y; -agents[(o+60u)] = sim_agent.current_slip_angle_degrees.x; agents[(o+60u) + 1u] = sim_agent.current_slip_angle_degrees.y; -agents[(o+62u)] = sim_agent.continuous_drift_time.x; agents[(o+62u) + 1u] = sim_agent.continuous_drift_time.y; -agents[(o+64u)] = sim_agent.max_continuous_drift_time.x; agents[(o+64u) + 1u] = sim_agent.max_continuous_drift_time.y; -agents[(o+66u)] = sim_agent.all_laps_time_sum.x; agents[(o+66u) + 1u] = sim_agent.all_laps_time_sum.y; -for (var copy_16=0u; copy_16<10u+params.pad; copy_16=copy_16+1u) { -agents[((o+68u)+copy_16*2u)] = sim_agent.recent[copy_16].x; agents[((o+68u)+copy_16*2u) + 1u] = sim_agent.recent[copy_16].y; -} -agents[(o+88u)] = u32(sim_agent.recent_start); -agents[(o+89u)] = u32(sim_agent.recent_len); -agents[(o+90u)] = sim_agent.deactivated_at.x; agents[(o+90u) + 1u] = sim_agent.deactivated_at.y; -agents[(o+92u)] = u32(sim_agent.flags); -agents[(o+93u)] = u32(sim_agent.pad); -} - -fn load_sensor_Car(o: u32) { -sim_car.position = vec2(bitcast(cars[(o+0u) + 0u]), bitcast(cars[(o+0u) + 1u])); -sim_car.velocity = vec2(bitcast(cars[(o+2u) + 0u]), bitcast(cars[(o+2u) + 1u])); -sim_car.acceleration = vec2(bitcast(cars[(o+4u) + 0u]), bitcast(cars[(o+4u) + 1u])); -sim_car.reset_acceleration = vec2(bitcast(cars[(o+6u) + 0u]), bitcast(cars[(o+6u) + 1u])); -sim_car.basis_x = vec2(bitcast(cars[(o+8u) + 0u]), bitcast(cars[(o+8u) + 1u])); -sim_car.basis_y = vec2(bitcast(cars[(o+10u) + 0u]), bitcast(cars[(o+10u) + 1u])); -sim_car.rotation = bitcast(cars[(o+12u)]); -sim_car.transform_angle = bitcast(cars[(o+13u)]); -sim_car.angular_velocity = bitcast(cars[(o+14u)]); -sim_car.boost_energy = bitcast(cars[(o+15u)]); -sim_car.flags = cars[(o+16u)]; -sim_car.collision_count = cars[(o+17u)]; -sim_car.tick = U64(cars[(o+18u)], cars[(o+18u) + 1u]); -for (var copy_17=0u; copy_17<4u+params.pad; copy_17=copy_17+1u) { -sim_car.wheel_angle[copy_17] = bitcast(cars[((o+20u)+copy_17*1u)]); -} -sim_car.error = cars[(o+39u)]; -} - -fn load_StepCarry(o: u32) -> StepCarry { return StepCarry(vec2(bitcast(results[(o + 0u) + 0u]), bitcast(results[(o + 0u) + 1u])), vec2(bitcast(results[(o + 2u) + 0u]), bitcast(results[(o + 2u) + 1u])), vec2(bitcast(results[(o + 4u) + 0u]), bitcast(results[(o + 4u) + 1u])), bitcast(results[(o + 6u)]), bitcast(results[(o + 7u)]), results[(o + 8u)], results[(o + 9u)]); } - -fn store_StepCarry(o: u32, v: StepCarry) { -results[(o + 0u) + 0u] = bitcast(v.old_velocity.x); -results[(o + 0u) + 1u] = bitcast(v.old_velocity.y); -results[(o + 2u) + 0u] = bitcast(v.velocity.x); -results[(o + 2u) + 1u] = bitcast(v.velocity.y); -results[(o + 4u) + 0u] = bitcast(v.previous_velocity.x); -results[(o + 4u) + 1u] = bitcast(v.previous_velocity.y); -results[(o + 6u)] = bitcast(v.angular); -results[(o + 7u)] = bitcast(v.previous_angular); -results[(o + 8u)] = u32(v.was_active); -results[(o + 9u)] = u32(v.base); -} - -fn sin_kernel(x_arg: F64) -> f32 { -var x = x_arg; - -var z: F64 = f64_mul(x, x); -var s1: F64 = f64_neg(F64(0x55555555u, 0x3fc55555u)); -if ((f64_gt(x, f64_neg(F64(0x40000000u, 0x3f8002aau))) && f64_lt(x, F64(0x40000000u, 0x3f8002aau)))) { -return f64_to_f32(f64_mul(x, f64_add(F64(0x00000000u, 0x3ff00000u), f64_mul(s1, z)))); -} -var w: F64 = f64_mul(z, z); -var r: F64 = f64_add(f64_neg(F64(0x1a01a01au, 0x3f2a01a0u)), f64_mul(z, F64(0xa556c734u, 0x3ec71de3u))); -var s: F64 = f64_mul(z, x); -return f64_to_f32(f64_add(f64_add(x, f64_mul(s, f64_add(s1, f64_mul(z, F64(0x11111111u, 0x3f811111u))))), f64_mul(f64_mul(s, w), r))); - -} - -fn cos_kernel(x_arg: F64) -> f32 { -var x = x_arg; - -var z: F64 = f64_mul(x, x); -if ((f64_gt(x, f64_neg(F64(0x00000000u, 0x3f800200u))) && f64_lt(x, F64(0x00000000u, 0x3f800200u)))) { -return f64_to_f32(f64_sub(F64(0x00000000u, 0x3ff00000u), f64_mul(F64(0x00000000u, 0x3fe00000u), z))); -} -return f64_to_f32(f64_add(F64(0x00000000u, 0x3ff00000u), f64_mul(z, f64_add(f64_neg(F64(0x00000000u, 0x3fe00000u)), f64_mul(z, f64_add(F64(0x55555555u, 0x3fa55555u), f64_mul(z, f64_add(f64_neg(F64(0x16c16c17u, 0x3f56c16cu)), f64_mul(z, f64_add(F64(0x1a01a01au, 0x3efa01a0u), f64_mul(z, f64_neg(F64(0xb7789f5cu, 0x3e927e4fu))))))))))))); - -} - -fn reduce_medium(x_arg: f32, y: ptr, err: ptr) -> i32 { -var x = x_arg; - -if (((bitcast(x) & 0x7fffffffu) >= 0x4dc90fdbu)) { -(*err) = ((*err) | ERR_NATIVE_LARGE); -} -var toint: F64 = f64_div(F64(0x00000000u, 0x3ff80000u), F64(0x00000000u, 0x3cb00000u)); -var n: F64 = f64_sub(f64_add(f64_mul(f64_from_f32(x), F64(0x6dc9c883u, 0x3fe45f30u)), toint), toint); -var xd: F64 = f64_from_f32(x); -(*y) = f64_sub(f64_sub(xd, f64_mul(n, F64(0x50000000u, 0x3ff921fbu))), f64_mul(n, F64(0x611a6263u, 0x3e5110b4u))); -var quarter: F64 = f64_from_f32(bitcast(0x3f490fdbu)); -if (f64_lt((*y), f64_neg(quarter))) { -n = f64_sub(n, F64(0x00000000u, 0x3ff00000u)); -(*y) = f64_sub(f64_sub(xd, f64_mul(n, F64(0x50000000u, 0x3ff921fbu))), f64_mul(n, F64(0x611a6263u, 0x3e5110b4u))); -} - else { -if (f64_gt((*y), quarter)) { -n = f64_add(n, F64(0x00000000u, 0x3ff00000u)); -(*y) = f64_sub(f64_sub(xd, f64_mul(n, F64(0x50000000u, 0x3ff921fbu))), f64_mul(n, F64(0x611a6263u, 0x3e5110b4u))); -} - -} -return f64_to_i32(n); - -} - -fn native_sin(x_arg: f32, err: ptr) -> f32 { -var x = x_arg; - -var bits: u32 = (bitcast(x) & 0x7fffffffu); -var negative: bool = fp_signbit(x); -var a: F64 = f64_from_f32(x); -var p: F64 = F64(0x54442d18u, 0x3ff921fbu); -if ((bits <= 0x3f490fdau)) { -return fp_select(sin_kernel(a), x, (bits < 0x39800000u)); -} -if ((bits <= 0x407b53d1u)) { -if ((bits <= 0x4016cbe3u)) { -return fp_select(cos_kernel(f64_sub(a, p)), fp_neg(cos_kernel(f64_add(a, p))), negative); -} -return sin_kernel(select(f64_neg(f64_sub(a, f64_mul(F64(0x00000000u, 0x40000000u), p))), f64_neg(f64_add(a, f64_mul(F64(0x00000000u, 0x40000000u), p))), negative)); -} - -if ((bits <= 0x40e231d5u)) { -if ((bits <= 0x40afeddfu)) { -return fp_select(fp_neg(cos_kernel(f64_sub(a, f64_mul(F64(0x00000000u, 0x40080000u), p)))), cos_kernel(f64_add(a, f64_mul(F64(0x00000000u, 0x40080000u), p))), negative); -} -return sin_kernel(select(f64_sub(a, f64_mul(F64(0x00000000u, 0x40100000u), p)), f64_add(a, f64_mul(F64(0x00000000u, 0x40100000u), p)), negative)); -} - -if (!(fp_isfinite(x))) { -return fp_invalid(x); -} -var y: F64; -var n: i32 = reduce_medium(x, &(y), err); -switch ((n & 3i)) { -case 0i: { -return sin_kernel(y); -} -case 1i: { -return cos_kernel(y); -} -case 2i: { -return sin_kernel(f64_neg(y)); -} -default: { -return fp_neg(cos_kernel(y)); -} -} - - -} - -fn native_cos(x_arg: f32, err: ptr) -> f32 { -var x = x_arg; - -var bits: u32 = (bitcast(x) & 0x7fffffffu); -var negative: bool = fp_signbit(x); -var a: F64 = f64_from_f32(x); -var p: F64 = F64(0x54442d18u, 0x3ff921fbu); -if ((bits <= 0x3f490fdau)) { -return fp_select(cos_kernel(a), bitcast(0x3f800000u), (bits < 0x39800000u)); -} -if ((bits <= 0x407b53d1u)) { -if ((bits > 0x4016cbe3u)) { -return fp_neg(cos_kernel(select(f64_sub(a, f64_mul(F64(0x00000000u, 0x40000000u), p)), f64_add(a, f64_mul(F64(0x00000000u, 0x40000000u), p)), negative))); -} -return sin_kernel(select(f64_sub(p, a), f64_add(a, p), negative)); -} - -if ((bits <= 0x40e231d5u)) { -if ((bits > 0x40afeddfu)) { -return cos_kernel(select(f64_sub(a, f64_mul(F64(0x00000000u, 0x40100000u), p)), f64_add(a, f64_mul(F64(0x00000000u, 0x40100000u), p)), negative)); -} -return sin_kernel(select(f64_sub(a, f64_mul(F64(0x00000000u, 0x40080000u), p)), f64_sub(f64_neg(a), f64_mul(F64(0x00000000u, 0x40080000u), p)), negative)); -} - -if (!(fp_isfinite(x))) { -return fp_invalid(x); -} -var y: F64; -var n: i32 = reduce_medium(x, &(y), err); -switch ((n & 3i)) { -case 0i: { -return cos_kernel(y); -} -case 1i: { -return sin_kernel(f64_neg(y)); -} -case 2i: { -return fp_neg(cos_kernel(y)); -} -default: { -return sin_kernel(y); -} -} - - -} - -fn native_atan(x_arg: f32) -> f32 { -var x = x_arg; - -var negative: bool = fp_signbit(x); -var bits: u32 = (bitcast(x) & 0x7fffffffu); -var high: array = array(bitcast(0x3eed6338u ^ params.pad), bitcast(0x3f490fdau ^ params.pad), bitcast(0x3f7b985eu ^ params.pad), bitcast(0x3fc90fdau ^ params.pad)); -var low: array = array(bitcast(0x31ac3769u ^ params.pad), bitcast(0x33222168u ^ params.pad), bitcast(0x33140fb4u ^ params.pad), bitcast(0x33a22168u ^ params.pad)); -if ((bits >= 0x4c800000u)) { -return fp_select(fp_copysign(high[u32(3i)], x), x, fp_isnan(x)); -} -var id: i32; -if ((bits < 0x3ee00000u)) { -if ((bits < 0x39800000u)) { -return x; -} -id = -(1i); -} - else { -x = fp_abs(x); -if ((bits < 0x3f980000u)) { -if ((bits < 0x3f300000u)) { -id = 0i; -x = fp_div(fp_sub(fp_mul(bitcast(0x40000000u), x), bitcast(0x3f800000u)), fp_add(bitcast(0x40000000u), x)); -} - else { -id = 1i; -x = fp_div(fp_sub(x, bitcast(0x3f800000u)), fp_add(x, bitcast(0x3f800000u))); -} - -} - else { -if ((bits < 0x401c0000u)) { -id = 2i; -x = fp_div(fp_sub(x, bitcast(0x3fc00000u)), fp_add(bitcast(0x3f800000u), fp_mul(bitcast(0x3fc00000u), x))); -} - else { -id = 3i; -x = fp_div(fp_neg(bitcast(0x3f800000u)), x); -} - -} -} - -var z: f32 = fp_mul(x, x); -var w: f32 = fp_mul(z, z); -var s1: f32 = fp_mul(z, fp_add(bitcast(0x3eaaaaa9u), fp_mul(w, fp_add(bitcast(0x3e11f50du), fp_mul(w, bitcast(0x3d7cac25u)))))); -var s2: f32 = fp_mul(w, fp_add(fp_neg(bitcast(0x3e4cca98u)), fp_mul(w, fp_neg(bitcast(0x3dda1247u))))); -if ((id < 0i)) { -return fp_sub(x, fp_mul(x, fp_add(s1, s2))); -} -var r: f32 = fp_sub(high[u32(id)], fp_sub(fp_sub(fp_mul(x, fp_add(s1, s2)), low[u32(id)]), x)); -return fp_select(r, fp_neg(r), negative); - -} - -fn native_atan2(y_arg: f32, x_arg: f32) -> f32 { -var y = y_arg; -var x = x_arg; - -var pi: f32 = bitcast(0x40490fdbu); -var pi_lo: f32 = bitcast(0xb3bbbd2eu ^ params.pad); -if ((fp_isnan(x) || fp_isnan(y))) { -return fp_nan_operand(y, x); -} -var ix: u32 = bitcast(x); -var iy: u32 = bitcast(y); -if ((ix == 0x3f800000u)) { -return native_atan(y); -} -var m: u32 = (((iy >> u32(31i)) & 1u) | ((ix >> u32(30i)) & 2u)); -ix = (ix & 0x7fffffffu); -iy = (iy & 0x7fffffffu); -if ((iy == u32(0i))) { -return fp_select(fp_select(fp_neg(pi), pi, (m == u32(2i))), y, (m <= u32(1i))); -} -if ((ix == u32(0i))) { -return fp_copysign(fp_div(pi, bitcast(0x40000000u)), y); -} -if ((ix == 0x7f800000u)) { -if ((iy == 0x7f800000u)) { -var v: array = array(fp_div(pi, bitcast(0x40800000u)), fp_div(fp_neg(pi), bitcast(0x40800000u)), fp_div(fp_mul(bitcast(0x40400000u), pi), bitcast(0x40800000u)), fp_div(fp_mul(fp_neg(bitcast(0x40400000u)), pi), bitcast(0x40800000u))); -return v[m]; -} - -var v: array = array(bitcast(0x00000000u), fp_neg(bitcast(0x00000000u)), pi, fp_neg(pi)); -return v[m]; -} - -if ((((ix + (26u << u32(23i))) < iy) || (iy == 0x7f800000u))) { -return fp_copysign(fp_div(pi, bitcast(0x40000000u)), y); -} -var z: f32 = fp_select(native_atan(fp_abs(fp_div(y, x))), bitcast(0x00000000u), (((m & u32(2i)) != u32(0)) && ((iy + (26u << u32(23i))) < ix))); -switch (m) { -case u32(0i): { -return z; -} -case u32(1i): { -return fp_neg(z); -} -case u32(2i): { -return fp_sub(pi, fp_sub(z, pi_lo)); -} -default: { -return fp_sub(fp_sub(z, pi_lo), pi); -} -} - - -} - -fn packed_sine(value_arg: f32, err: ptr) -> f32 { -var value = value_arg; - -var a: f32 = fp_abs(value); -if (!((a <= bitcast(0x461c4000u)))) { -(*err) = ((*err) | ERR_MANAGED_LARGE); -} -var rounded: f32 = fp_add(fp_mul(a, bitcast(0x3ea2f983u ^ params.pad)), bitcast(0x4b400000u ^ params.pad)); -var n: f32 = fp_sub(rounded, bitcast(0x4b400000u ^ params.pad)); -var r: f32 = fp_sub(a, fp_mul(n, bitcast(0x40490000u ^ params.pad))); -r = fp_sub(r, fp_mul(n, bitcast(0x3a7da000u ^ params.pad))); -r = fp_sub(r, fp_mul(n, bitcast(0x34222000u ^ params.pad))); -r = fp_sub(r, fp_mul(n, bitcast(0x2cb4611au ^ params.pad))); -var square: f32 = fp_mul(r, r); -r = bitcast((bitcast(r) ^ (bitcast(rounded) << u32(31i))) ^ params.pad); -var p: f32 = fp_mul(bitcast(0x362edef8u ^ params.pad), square); -p = fp_add(p, bitcast(0xb94fb7ffu ^ params.pad)); -p = fp_mul(p, square); -p = fp_add(p, bitcast(0x3c088766u ^ params.pad)); -p = fp_mul(p, square); -p = fp_add(p, bitcast(0xbe2aaaa6u ^ params.pad)); -var result: f32 = fp_add(r, fp_mul(fp_mul(square, p), r)); -return bitcast((bitcast(result) ^ (bitcast(value) & 0x80000000u)) ^ params.pad); - -} - -fn managed_sin_cos(angle_arg: f32, s: ptr, c: ptr, err: ptr) { -var angle = angle_arg; - -if (fp_isnan(angle)) { -(*s) = angle; -(*c) = bitcast(((bitcast(angle) & 0x7fffffffu) | 0x00400000u) ^ params.pad); -return; -} - -var a: f32 = fp_abs(angle); -if (!((a >= bitcast(0x39000000u ^ params.pad)))) { -(*s) = angle; -(*c) = fp_sub(bitcast(0x3f800000u), fp_mul(fp_mul(a, a), bitcast(0x3f000000u))); -return; -} - -(*s) = packed_sine(angle, err); -(*c) = packed_sine(fp_add(a, bitcast(0x3fc90fdbu)), err); - -} - -fn engine_sin_cos_raw(xf_arg: f32, s: ptr, c: ptr) { -var xf = xf_arg; - -var x: F64 = f64_from_f32(xf); -var n: F64 = f64_rint(f64_mul(x, F64(0x6dc9c883u, 0x3fe45f30u))); -var y: F64 = f64_sub(f64_sub(x, f64_mul(n, F64(0x54400000u, 0x3ff921fbu))), f64_mul(n, F64(0x1a626331u, 0x3dd0b461u))); -y = select(y, x, f64_eq(n, F64(0x00000000u, 0x00000000u))); -var z: F64 = f64_mul(y, y); -var w: F64 = f64_mul(z, z); -var r: F64 = f64_add(f64_add(F64(0x1110f8a6u, 0x3f811111u), f64_mul(z, f64_add(f64_neg(F64(0x19c161d5u, 0x3f2a01a0u)), f64_mul(z, F64(0x57b1fe7du, 0x3ec71de3u))))), f64_mul(f64_mul(z, w), f64_add(f64_neg(F64(0x8a2b9cebu, 0x3e5ae5e6u)), f64_mul(z, F64(0x5acfd57cu, 0x3de5d93au))))); -var sy: F64 = select(f64_add(y, f64_mul(f64_mul(z, y), f64_add(f64_neg(F64(0x55555549u, 0x3fc55555u)), f64_mul(z, r)))), y, f64_lt(f64_abs(y), F64(0x00000000u, 0x3e400000u))); -var rc: F64 = f64_add(f64_mul(z, f64_add(F64(0x5555554cu, 0x3fa55555u), f64_mul(z, f64_add(f64_neg(F64(0x16c15177u, 0x3f56c16cu)), f64_mul(z, F64(0x19cb1590u, 0x3efa01a0u)))))), f64_mul(f64_mul(w, w), f64_add(f64_neg(F64(0x809c52adu, 0x3e927e4fu)), f64_mul(z, f64_add(F64(0xbdb4b1c4u, 0x3e21ee9eu), f64_mul(z, f64_neg(F64(0xbe8838d4u, 0x3da8fae9u)))))))); -var hz: F64 = f64_mul(F64(0x00000000u, 0x3fe00000u), z); -var one_minus: F64 = f64_sub(F64(0x00000000u, 0x3ff00000u), hz); -var cy: F64 = f64_add(one_minus, f64_add(f64_sub(f64_sub(F64(0x00000000u, 0x3ff00000u), one_minus), hz), f64_mul(z, rc))); -var q: i32 = (f64_to_i32(n) & 3i); -var sv: F64 = select(sy, cy, ((q & 1i) != i32(0))); -var cv: F64 = select(cy, sy, ((q & 1i) != i32(0))); -sv = select(sv, f64_neg(sv), ((q & 2i) != i32(0))); -cv = select(cv, f64_neg(cv), (((q + 1i) & 2i) != i32(0))); -(*s) = f64_to_f32(sv); -(*c) = f64_to_f32(cv); - -} - -fn engine_sin_cos(x_arg: f32, s: ptr, c: ptr, err: ptr) { -var x = x_arg; - -if (!((fp_abs(x) <= ENGINE_DOMAIN))) { -(*err) = ((*err) | ERR_ENGINE_DOMAIN); -} -engine_sin_cos_raw(x, s, c); -if ((ENGINE_EXCEPTION_COUNT == 0i)) { -return; -} -var key: u32 = bitcast(x); -var lo: i32 = 0i; -{ -var step: i32 = ENGINE_EXCEPTION_STEP; -loop { - -if (!((step > 0i))) { break; } -{ -var probe: i32 = (lo + step); -lo = select(lo, probe, ((probe < ENGINE_EXCEPTION_COUNT) && (ENGINE_EXCEPTIONS[u32(probe)][u32(0i)] <= key))); -} - -continuing { -step = (step >> u32(1i)); -} -} -} -var hit: bool = (ENGINE_EXCEPTIONS[u32(lo)][u32(0i)] == key); -(*s) = fp_select((*s), bitcast(ENGINE_EXCEPTIONS[u32(lo)][u32(1i)] ^ params.pad), hit); -(*c) = fp_select((*c), bitcast(ENGINE_EXCEPTIONS[u32(lo)][u32(2i)] ^ params.pad), hit); - -} - -fn exp_special(tmp_arg: F64, bits_arg: U64, k_arg: U64) -> F64 { -var tmp = tmp_arg; -var bits = bits_arg; -var k = k_arg; - -if (u64_eq((k & U64(0x80000000u, 0x00000000u)), i64_from_i32(0i))) { -bits = u64_sub(bits, u64_shl(U64(0x000003f1u, 0x00000000u), u32(52i))); -var scale: F64 = bits; -return f64_mul(u64_shl(U64(0x000007f0u, 0x00000000u), u32(52i)), f64_add(scale, f64_mul(scale, tmp))); -} - -bits = u64_add(bits, u64_shl(U64(0x000003feu, 0x00000000u), u32(52i))); -var scale: F64 = bits; -var y: F64 = f64_add(scale, f64_mul(scale, tmp)); -if (f64_lt(f64_abs(y), F64(0x00000000u, 0x3ff00000u))) { -var one: F64 = select(F64(0x00000000u, 0x3ff00000u), f64_neg(F64(0x00000000u, 0x3ff00000u)), f64_lt(y, F64(0x00000000u, 0x00000000u))); -var lo: F64 = f64_add(f64_sub(scale, y), f64_mul(scale, tmp)); -var hi: F64 = f64_add(one, y); -lo = f64_add(f64_add(f64_sub(one, hi), y), lo); -y = f64_sub(f64_add(hi, lo), one); -if (f64_eq(y, F64(0x00000000u, 0x00000000u))) { -y = (bits & u64_shl(U64(0x00000001u, 0x00000000u), u32(63i))); -} -} - -return f64_mul(F64(0x00000000u, 0x00100000u), y); - -} - -fn exp_inner(x_arg: F64, xtail_arg: F64, negative_arg: bool) -> F64 { -var x = x_arg; -var xtail = xtail_arg; -var negative = negative_arg; - -var exponent: U64 = (u64_shr(x, u32(52i)) & i64_from_i32(0x7ffi)); -if (u64_lt(exponent, i64_from_i32(969i))) { -return select(f64_add(F64(0x00000000u, 0x3ff00000u), x), f64_neg(f64_add(F64(0x00000000u, 0x3ff00000u), x)), negative); -} -if (u64_ge(exponent, i64_from_i32(1033i))) { -var y: F64 = select(F64(0u, 0x7ff00000u), F64(0x00000000u, 0x00000000u), f64_signbit(x)); -return select(y, f64_neg(y), negative); -} - -var kd: F64 = f64_round(f64_mul(INV_LN2_N, x)); -var ki: U64 = f64_to_i64(kd); -var r: F64 = f64_add(f64_add(f64_add(x, f64_mul(kd, NEG_LN2_HI_N)), f64_mul(kd, NEG_LN2_LO_N)), xtail); -var idx: i32 = (2i * i32(u64_mod_small(ki, u32(128i)).x)); -var bias: U64 = select(i64_from_i32(0i), u64_shl(U64(0x00000800u, 0x00000000u), u32(7i)), negative); -var top: U64 = u64_shl(u64_add(ki, bias), u32(45i)); -var tail: F64 = EXP_TAB[u32(idx)]; -var bits: U64 = u64_add(EXP_TAB[u32((idx + 1i))], top); -var r2: F64 = f64_mul(r, r); -var tmp: F64 = f64_add(f64_add(f64_add(tail, r), f64_mul(r2, f64_add(EXP_POLY[u32(0i)], f64_mul(r, EXP_POLY[u32(1i)])))), f64_mul(f64_mul(r2, r2), f64_add(EXP_POLY[u32(2i)], f64_mul(r, EXP_POLY[u32(3i)])))); -if (u64_ge(exponent, i64_from_i32(1032i))) { -return exp_special(tmp, bits, ki); -} -var scale: F64 = bits; -return f64_add(scale, f64_mul(scale, tmp)); - -} - -fn dexp(x_arg: F64) -> F64 { -var x = x_arg; - -if (f64_isnan(x)) { -return x; -} -return exp_inner(x, F64(0x00000000u, 0x00000000u), (0i != i32(0))); - -} - -fn pow_log(ix_arg: U64, out_hi: ptr, out_lo: ptr) { -var ix = ix_arg; - -var tmp: U64 = u64_sub(ix, U64(0x00000000u, 0x3fe69555u)); -var i: i32 = i32(u64_mod_small(u64_shr(tmp, u32(45i)), u32(128i)).x); -var kd: F64 = f64_from_i64(i64_shr(tmp, u32(52i))); -var iz: U64 = u64_sub(ix, (tmp & u64_shl(U64(0x00000fffu, 0x00000000u), u32(52i)))); -var z: F64 = iz; -var invc: F64 = POW_TAB[u32(i)][u32(0i)]; -var logc: F64 = POW_TAB[u32(i)][u32(1i)]; -var logctail: F64 = POW_TAB[u32(i)][u32(2i)]; -var zhi: F64 = (u64_add(iz, u64_shl(U64(0x00000001u, 0x00000000u), u32(31i))) & u64_shl(~(U64(0x00000000u, 0x00000000u)), u32(32i))); -var zlo: F64 = f64_sub(z, zhi); -var rhi: F64 = f64_sub(f64_mul(zhi, invc), F64(0x00000000u, 0x3ff00000u)); -var rlo: F64 = f64_mul(zlo, invc); -var r: F64 = f64_add(rhi, rlo); -var t1: F64 = f64_add(f64_mul(kd, LN2_HI), logc); -var t2: F64 = f64_add(t1, r); -var lo1: F64 = f64_add(f64_mul(kd, LN2_LO), logctail); -var lo2: F64 = f64_add(f64_sub(t1, t2), r); - -var ar: F64 = f64_mul(POW_POLY[0u + u32(0i)], r); -var ar2: F64 = f64_mul(r, ar); -var ar3: F64 = f64_mul(r, ar2); -var arhi: F64 = f64_mul(POW_POLY[0u + u32(0i)], rhi); -var arhi2: F64 = f64_mul(rhi, arhi); -var hi: F64 = f64_add(t2, arhi2); -var lo3: F64 = f64_mul(rlo, f64_add(ar, arhi)); -var lo4: F64 = f64_add(f64_sub(t2, hi), arhi2); -var p: F64 = f64_mul(ar3, f64_add(f64_add(POW_POLY[0u + u32(1i)], f64_mul(r, POW_POLY[0u + u32(2i)])), f64_mul(ar2, f64_add(f64_add(POW_POLY[0u + u32(3i)], f64_mul(r, POW_POLY[0u + u32(4i)])), f64_mul(ar2, f64_add(POW_POLY[0u + u32(5i)], f64_mul(r, POW_POLY[0u + u32(6i)]))))))); -var lo: F64 = f64_add(f64_add(f64_add(f64_add(lo1, lo2), lo3), lo4), p); -var y: F64 = f64_add(hi, lo); -(*out_hi) = y; -(*out_lo) = f64_add(f64_sub(hi, y), lo); - -} - -fn integer_kind(bits_arg: U64) -> u32 { -var bits = bits_arg; - -var e: U64 = (u64_shr(bits, u32(52i)) & i64_from_i32(0x7ffi)); -if (u64_lt(e, i64_from_i32(1023i))) { -return u32(0i); -} -if (u64_gt(e, i64_from_i32(1075i))) { -return u32(2i); -} -var bit: U64 = u64_shl(U64(0x00000001u, 0x00000000u), u32(u64_sub(i64_from_i32(1075i), e).x)); -return u32(select(select(2i, 1i, u64_nonzero((bits & bit))), 0i, u64_nonzero((bits & u64_sub(bit, i64_from_i32(1i)))))); - -} - -fn dpow(x_arg: F64, y_arg: F64) -> F64 { -var x = x_arg; -var y = y_arg; - -if (u64_eq(x, F64(0x00000000u, 0x3ff00000u))) { -return F64(0x00000000u, 0x3ff00000u); -} -var SIGN: U64 = u64_shl(U64(0x00000001u, 0x00000000u), u32(63i)); -var ix: U64 = x; -var iy: U64 = y; -var topx: u32 = u32(u64_shr(ix, u32(52i)).x); -var topy: u32 = u32(u64_shr(iy, u32(52i)).x); -var negative: bool = (0i != i32(0)); -if ((((topx - 1u) >= 0x7feu) || (((topy & 0x7ffu) - 0x3beu) >= 0x80u))) { -if ((f64_eq(y, F64(0x00000000u, 0x00000000u)) || !(f64_isfinite(y)))) { -if (f64_eq(y, F64(0x00000000u, 0x00000000u))) { -return F64(0x00000000u, 0x3ff00000u); -} -if (f64_eq(x, F64(0x00000000u, 0x3ff00000u))) { -return F64(0x00000000u, 0x3ff00000u); -} -if ((f64_isnan(x) || f64_isnan(y))) { -return f64_nan_operand(x, y); -} -if (f64_eq(f64_abs(x), F64(0x00000000u, 0x3ff00000u))) { -return F64(0x00000000u, 0x3ff00000u); -} -if ((f64_lt(f64_abs(x), F64(0x00000000u, 0x3ff00000u)) == !(f64_signbit(y)))) { -return F64(0x00000000u, 0x00000000u); -} -return f64_mul(y, y); -} - -if ((f64_eq(x, F64(0x00000000u, 0x00000000u)) || !(f64_isfinite(x)))) { -var x2: F64 = select(f64_mul(x, x), f64_nan_operand(x, x), f64_isnan(x)); -if ((f64_signbit(x) && (integer_kind(iy) == u32(1i)))) { -x2 = (x2 ^ u64_shl(U64(0x00000001u, 0x00000000u), u32(63i))); -} -return select(x2, f64_div(F64(0x00000000u, 0x3ff00000u), x2), (f64_signbit(y) && !(f64_isnan(x2)))); -} - -if (f64_signbit(x)) { -var kind: u32 = integer_kind(iy); -if ((kind == u32(0i))) { -return U64(0x00000000u, 0xfff80000u); -} -negative = (kind == u32(1i)); -ix = (ix & ~(SIGN)); -topx = (topx & u32(0x7ffi)); -} - -if ((((topy & 0x7ffu) - 0x3beu) >= 0x80u)) { -if (u64_eq(ix, F64(0x00000000u, 0x3ff00000u))) { -return F64(0x00000000u, 0x3ff00000u); -} -if (((topy & 0x7ffu) < 0x3beu)) { -return select(f64_sub(F64(0x00000000u, 0x3ff00000u), y), f64_add(F64(0x00000000u, 0x3ff00000u), y), u64_gt(ix, F64(0x00000000u, 0x3ff00000u))); -} -return select(F64(0x00000000u, 0x00000000u), F64(0u, 0x7ff00000u), (u64_gt(ix, F64(0x00000000u, 0x3ff00000u)) == (topy < 0x800u))); -} - -if ((topx == u32(0i))) { -ix = u64_sub((f64_mul(x, F64(0x00000000u, 0x43300000u)) & ~(SIGN)), u64_shl(U64(0x00000034u, 0x00000000u), u32(52i))); -} -} - -var hi: F64; -var lo: F64; -pow_log(ix, &(hi), &(lo)); -var yhi: F64 = (iy & u64_shl(~(U64(0x00000000u, 0x00000000u)), u32(27i))); -var ylo: F64 = f64_sub(y, yhi); -var lhi: F64 = (hi & u64_shl(~(U64(0x00000000u, 0x00000000u)), u32(27i))); -var llo: F64 = f64_add(f64_sub(hi, lhi), lo); -var ehi: F64 = f64_mul(yhi, lhi); -var elo: F64 = f64_add(f64_mul(ylo, lhi), f64_mul(y, llo)); -return exp_inner(ehi, elo, negative); - -} - -fn game_expm1(x_arg: F64) -> F64 { -var x = x_arg; - -var high: u32 = (u32(u64_shr(x, u32(32i)).x) & 0x7fffffffu); -var negative: bool = f64_signbit(x); -if ((high >= 0x4043687au)) { -if (f64_isnan(x)) { -return x; -} -if (f64_isinf(x)) { -return select(x, f64_neg(F64(0x00000000u, 0x3ff00000u)), negative); -} -if (negative) { -return f64_neg(F64(0x00000000u, 0x3ff00000u)); -} -if (f64_gt(x, F64(0xfefa39efu, 0x40862e42u))) { -return F64(0u, 0x7ff00000u); -} -} - -var k: i32; -var c: F64; -if ((high > 0x3fd62e42u)) { -var hi: F64; -var lo: F64; -if ((high < 0x3ff0a2b2u)) { -if (!(negative)) { -hi = f64_sub(x, F64(0xfee00000u, 0x3fe62e42u)); -lo = F64(0x35793c76u, 0x3dea39efu); -k = 1i; -} - else { -hi = f64_add(x, F64(0xfee00000u, 0x3fe62e42u)); -lo = f64_neg(F64(0x35793c76u, 0x3dea39efu)); -k = -(1i); -} - -} - else { -k = f64_to_i32(f64_add(f64_mul(F64(0x652b82feu, 0x3ff71547u), x), select(F64(0x00000000u, 0x3fe00000u), f64_neg(F64(0x00000000u, 0x3fe00000u)), negative))); -hi = f64_sub(x, f64_mul(f64_from_i32(k), F64(0xfee00000u, 0x3fe62e42u))); -lo = f64_mul(f64_from_i32(k), F64(0x35793c76u, 0x3dea39efu)); -} - -x = f64_sub(hi, lo); -c = f64_sub(f64_sub(hi, x), lo); -} - else { -if ((high < 0x3c900000u)) { -return x; -} - else { -k = 0i; -c = F64(0x00000000u, 0x00000000u); -} - -} -var hfx: F64 = f64_mul(F64(0x00000000u, 0x3fe00000u), x); -var hxs: F64 = f64_mul(x, hfx); -var r1: F64 = f64_add(F64(0x00000000u, 0x3ff00000u), f64_mul(hxs, f64_add(f64_neg(F64(0x111110f4u, 0x3fa11111u)), f64_mul(hxs, f64_add(F64(0x19fe5585u, 0x3f5a01a0u), f64_mul(hxs, f64_add(f64_neg(F64(0x9eaadbb7u, 0x3f14ce19u)), f64_mul(hxs, f64_add(F64(0x86e65239u, 0x3ed0cfcau), f64_mul(hxs, f64_neg(F64(0x6e09c32du, 0x3e8afdb7u)))))))))))); -var t: F64 = f64_sub(F64(0x00000000u, 0x40080000u), f64_mul(r1, hfx)); -var e: F64 = f64_mul(hxs, f64_div(f64_sub(r1, t), f64_sub(F64(0x00000000u, 0x40180000u), f64_mul(x, t)))); -if ((k == 0i)) { -return f64_sub(x, f64_sub(f64_mul(x, e), hxs)); -} -e = f64_sub(f64_mul(x, f64_sub(e, c)), c); -e = f64_sub(e, hxs); -if ((k == -(1i))) { -return f64_sub(f64_mul(F64(0x00000000u, 0x3fe00000u), f64_sub(x, e)), F64(0x00000000u, 0x3fe00000u)); -} -if ((k == 1i)) { -return select(f64_add(F64(0x00000000u, 0x3ff00000u), f64_mul(F64(0x00000000u, 0x40000000u), f64_sub(x, e))), f64_mul(f64_neg(F64(0x00000000u, 0x40000000u)), f64_sub(e, f64_add(x, F64(0x00000000u, 0x3fe00000u)))), f64_lt(x, f64_neg(F64(0x00000000u, 0x3fd00000u)))); -} -var twopk: F64 = u64_shl(i64_from_i32((0x3ffi + k)), u32(52i)); -if (((k < 0i) || (k > 56i))) { -var y: F64 = f64_add(f64_sub(x, e), F64(0x00000000u, 0x3ff00000u)); -return f64_sub(select(f64_mul(y, twopk), f64_mul(f64_mul(y, F64(0x00000000u, 0x40000000u)), U64(0x00000000u, 0x7fe00000u)), (k == 1024i)), F64(0x00000000u, 0x3ff00000u)); -} - -var inverse: F64 = u64_shl(i64_from_i32((0x3ffi - k)), u32(52i)); -return select(f64_mul(f64_add(f64_sub(x, f64_add(e, inverse)), F64(0x00000000u, 0x3ff00000u)), twopk), f64_mul(f64_add(f64_sub(x, e), f64_sub(F64(0x00000000u, 0x3ff00000u), inverse)), twopk), (k < 20i)); - -} - -fn game_tanh_branchy(value_arg: F64) -> F64 { -var value = value_arg; - -var x: F64 = f64_abs(value); -var high: u32 = u32(u64_shr(x, u32(32i)).x); -var t: F64; -if ((high > 0x3fe193eau)) { -if ((high > 0x40340000u)) { -t = f64_sub(F64(0x00000000u, 0x3ff00000u), f64_div(F64(0x00000000u, 0x00000000u), x)); -} else { -var u: F64 = game_expm1(f64_mul(F64(0x00000000u, 0x40000000u), x)); -t = f64_sub(F64(0x00000000u, 0x3ff00000u), f64_div(F64(0x00000000u, 0x40000000u), f64_add(u, F64(0x00000000u, 0x40000000u)))); -} - -} - else { -if ((high > 0x3fd058aeu)) { -var u: F64 = game_expm1(f64_mul(F64(0x00000000u, 0x40000000u), x)); -t = f64_div(u, f64_add(u, F64(0x00000000u, 0x40000000u))); -} - else { -if ((high >= 0x00100000u)) { -var u: F64 = game_expm1(f64_mul(f64_neg(F64(0x00000000u, 0x40000000u)), x)); -t = f64_div(f64_neg(u), f64_add(u, F64(0x00000000u, 0x40000000u))); -} - else { -t = x; -} -} -} -return select(t, f64_neg(t), f64_signbit(value)); - -} - -fn pow2_of(k_arg: i32) -> F64 { -var k = k_arg; - -return u64_shl(i64_from_i32((k + 0x3ffi)), u32(52i)); - -} - -fn expm1_lane(x_arg: F64) -> F64 { -var x = x_arg; - -var h: u32 = (u32(u64_shr(x, u32(32i)).x) & 0x7fffffffu); -var negative: bool = f64_signbit(x); -var reduce: bool = (h > 0x3fd62e42u); -var tiny: bool = (h < 0x3c900000u); -var near: bool = (h < 0x3ff0a2b2u); -var rounded: F64 = f64_add(f64_mul(F64(0x652b82feu, 0x3ff71547u), x), select(F64(0x00000000u, 0x3fe00000u), f64_neg(F64(0x00000000u, 0x3fe00000u)), negative)); -var ki: i32 = select(0i, select(f64_to_i32(rounded), select(1i, -(1i), negative), near), reduce); -var k: F64 = f64_from_i32(ki); -var hi: F64 = f64_sub(x, f64_mul(k, F64(0xfee00000u, 0x3fe62e42u))); -var lo: F64 = f64_mul(k, F64(0x35793c76u, 0x3dea39efu)); -var xr: F64 = select(x, f64_sub(hi, lo), reduce); -var c: F64 = select(F64(0x00000000u, 0x00000000u), f64_sub(f64_sub(hi, xr), lo), reduce); -var hfx: F64 = f64_mul(F64(0x00000000u, 0x3fe00000u), xr); -var hxs: F64 = f64_mul(xr, hfx); -var p: F64 = f64_mul(hxs, f64_neg(F64(0x6e09c32du, 0x3e8afdb7u))); -p = f64_mul(hxs, f64_add(F64(0x86e65239u, 0x3ed0cfcau), p)); -p = f64_mul(hxs, f64_add(f64_neg(F64(0x9eaadbb7u, 0x3f14ce19u)), p)); -p = f64_mul(hxs, f64_add(F64(0x19fe5585u, 0x3f5a01a0u), p)); -p = f64_mul(hxs, f64_add(f64_neg(F64(0x111110f4u, 0x3fa11111u)), p)); -var r1: F64 = f64_add(F64(0x00000000u, 0x3ff00000u), p); -var t: F64 = f64_sub(F64(0x00000000u, 0x40080000u), f64_mul(r1, hfx)); -var e: F64 = f64_mul(hxs, f64_div(f64_sub(r1, t), f64_sub(F64(0x00000000u, 0x40180000u), f64_mul(xr, t)))); -var k0: F64 = f64_sub(xr, f64_sub(f64_mul(xr, e), hxs)); -e = f64_sub(f64_sub(f64_mul(xr, f64_sub(e, c)), c), hxs); -var xme: F64 = f64_sub(xr, e); -var km1: F64 = f64_sub(f64_mul(F64(0x00000000u, 0x3fe00000u), xme), F64(0x00000000u, 0x3fe00000u)); -var k1: F64 = select(f64_add(F64(0x00000000u, 0x3ff00000u), f64_mul(F64(0x00000000u, 0x40000000u), xme)), f64_mul(f64_neg(F64(0x00000000u, 0x40000000u)), f64_sub(e, f64_add(xr, F64(0x00000000u, 0x3fe00000u)))), f64_lt(xr, f64_neg(F64(0x00000000u, 0x3fd00000u)))); -var twopk: F64 = pow2_of(ki); -var inverse: F64 = pow2_of(-(ki)); -var far: bool = ((ki < 0i) || (ki > 56i)); -var far_value: F64 = f64_sub(f64_mul(f64_add(xme, F64(0x00000000u, 0x3ff00000u)), twopk), F64(0x00000000u, 0x3ff00000u)); -var below20: F64 = f64_mul(f64_add(xme, f64_sub(F64(0x00000000u, 0x3ff00000u), inverse)), twopk); -var from20: F64 = f64_mul(f64_add(f64_sub(xr, f64_add(e, inverse)), F64(0x00000000u, 0x3ff00000u)), twopk); -var general: F64 = select(select(from20, below20, (ki < 20i)), far_value, far); -var result: F64 = select(select(select(general, k1, (ki == 1i)), km1, (ki == -(1i))), k0, (ki == 0i)); -return select(result, x, tiny); - -} - -fn game_tanh(value_arg: F64) -> F64 { -var value = value_arg; - -var x: F64 = f64_abs(value); -var h: u32 = u32(u64_shr(x, u32(32i)).x); -if ((h > 0x40340000u)) { -var t: F64 = f64_sub(F64(0x00000000u, 0x3ff00000u), f64_div(F64(0x00000000u, 0x00000000u), x)); -return select(t, f64_neg(t), f64_signbit(value)); -} - -var big: bool = (h > 0x3fe193eau); -var mid: bool = (h > 0x3fd058aeu); -var normal: bool = (h > 0x000fffffu); -var t: F64 = expm1_lane(select(f64_mul(f64_neg(F64(0x00000000u, 0x40000000u)), x), f64_mul(F64(0x00000000u, 0x40000000u), x), mid)); -var d: F64 = f64_add(t, F64(0x00000000u, 0x40000000u)); -var q: F64 = f64_div(select(t, F64(0x00000000u, 0x40000000u), big), d); -var result: F64 = select(select(select(x, f64_neg(q), normal), q, mid), f64_sub(F64(0x00000000u, 0x3ff00000u), q), big); -return select(result, f64_neg(result), f64_signbit(value)); - -} - -fn win_isnan(x_arg: F64) -> bool { -var x = x_arg; - -return u64_gt((x & U64(0xffffffffu, 0x7fffffffu)), U64(0x00000000u, 0x7ff00000u)); - -} - -fn win_quiet(x_arg: F64) -> F64 { -var x = x_arg; - -return (x | U64(0x00000000u, 0x00080000u)); - -} - -fn win_nan_operand(a_arg: F64, b_arg: F64) -> F64 { -var a = a_arg; -var b = b_arg; - -return select(win_quiet(b), win_quiet(a), win_isnan(a)); - -} - -fn win_atan2f(yf_arg: f32, xf_arg: f32) -> f32 { -var yf = yf_arg; -var xf = xf_arg; - -if (((bitcast(xf) & 0x7fffffffu) > 0x7f800000u)) { -return bitcast((bitcast(xf) | 0x00400000u) ^ params.pad); -} -if (((bitcast(yf) & 0x7fffffffu) > 0x7f800000u)) { -return bitcast((bitcast(yf) | 0x00400000u) ^ params.pad); -} -var x: F64 = f64_from_f32(xf); -var y: F64 = f64_from_f32(yf); -var xb: U64 = x; -var yb: U64 = y; -var de: i32 = (i32((u64_shr(yb, u32(52i)) & U64(0x000007ffu, 0x00000000u)).x) - i32((u64_shr(xb, u32(52i)) & U64(0x000007ffu, 0x00000000u)).x)); -var ax: U64 = (xb & U64(0xffffffffu, 0x7fffffffu)); -var ay: U64 = (yb & U64(0xffffffffu, 0x7fffffffu)); -var xneg: bool = u64_ne(u64_shr(xb, u32(63i)), U64(0x00000000u, 0x00000000u)); -var yneg: bool = u64_ne(u64_shr(yb, u32(63i)), U64(0x00000000u, 0x00000000u)); -var pi: f32 = bitcast(0x40490fdbu ^ params.pad); -var half_pi: f32 = bitcast(0x3fc90fdbu ^ params.pad); -if (u64_eq(ay, U64(0x00000000u, 0x00000000u))) { -if (xneg) { -return fp_select(pi, fp_neg(pi), yneg); -} -return yf; -} - -if ((u64_eq(ax, U64(0x00000000u, 0x00000000u)) && yneg)) { -return fp_neg(half_pi); -} -if ((de > 26i)) { -return fp_select(half_pi, fp_neg(half_pi), yneg); -} -if (((de < -(13i)) && !(xneg))) { -if ((de < -(150i))) { -return fp_select(bitcast(0u ^ params.pad), bitcast(0x80000000u ^ params.pad), yneg); -} -return f64_to_f32(f64_div(y, x)); -} - -if (((de < -(26i)) && xneg)) { -return fp_select(pi, fp_neg(pi), yneg); -} -if ((u64_eq(ay, U64(0x00000000u, 0x7ff00000u)) && u64_eq(ax, U64(0x00000000u, 0x7ff00000u)))) { -if (xneg) { -return fp_select(bitcast(0x4016cbe4u ^ params.pad), bitcast(0xc016cbe4u ^ params.pad), yneg); -} -return fp_select(bitcast(0x3f490fdbu ^ params.pad), bitcast(0xbf490fdbu ^ params.pad), yneg); -} - -var sx: F64 = select(x, f64_neg(x), xneg); -var sy: F64 = select(y, f64_neg(y), yneg); -var swap: bool = f64_gt(sy, sx); -var num: F64 = select(sy, sx, swap); -var den: F64 = select(sx, sy, swap); -var t: F64 = f64_div(num, den); -var r: F64; -if (f64_gt(t, F64(0x00000000u, 0x3fb00000u))) { -var n: i32 = f64_to_i32(f64_add(f64_mul(t, F64(0x00000000u, 0x40700000u)), F64(0x00000000u, 0x3fe00000u))); -var nd: F64 = f64_from_i32(n); -var u: F64 = f64_div(f64_sub(f64_mul(num, F64(0x00000000u, 0x40700000u)), f64_mul(nd, den)), f64_add(f64_mul(nd, num), f64_mul(den, F64(0x00000000u, 0x40700000u)))); -var hi: F64 = f64_add(u, UCRT_ATAN2F_ATAN256[u32((n - 16i))]); -var u3: F64 = f64_mul(u, u); -u3 = f64_mul(u3, u); -u3 = f64_mul(u3, F64(0x55550877u, 0x3fd55555u)); -r = f64_sub(hi, u3); -} - else { -if (f64_lt(t, F64(0xeb1c432du, 0x3f1a36e2u))) { -r = t; -} - else { -var t2: F64 = f64_mul(t, t); -var p: F64 = f64_sub(F64(0x999643a3u, 0x3fc99999u), f64_mul(t2, F64(0x82bd6be1u, 0x3fc24924u))); -p = f64_mul(p, t2); -var t3: F64 = f64_mul(t2, t); -var q: F64 = f64_sub(F64(0x55555538u, 0x3fd55555u), p); -q = f64_mul(q, t3); -r = f64_sub(t, q); -} - -} -if (swap) { -r = f64_sub(F64(0x54442d18u, 0x3ff921fbu), r); -} -if (xneg) { -r = f64_sub(F64(0x54442d18u, 0x400921fbu), r); -} -if (yneg) { -r = f64_neg(r); -} -return f64_to_f32(r); - -} - -fn win_exp_finite(x_arg: F64) -> F64 { -var x = x_arg; - -if (u64_le((x & U64(0xffffffffu, 0x7fffffffu)), U64(0x00000000u, 0x3e500000u))) { -return f64_add(x, F64(0x00000000u, 0x3ff00000u)); -} -var n: i32 = f64_to_i32(f64_mul(x, F64(0x652b82feu, 0x40571547u))); -var nd: F64 = f64_from_i32(n); -var r: F64 = f64_add(f64_mul(nd, f64_neg(F64(0xbc9e3b39u, 0x3d1cf79au))), f64_fma(nd, f64_neg(F64(0xfefa0000u, 0x3f862e42u)), x)); -var j: i32 = (n & 63i); -var k: i32 = (n >> u32(6i)); -var p: F64 = f64_fma(F64(0x16c16c17u, 0x3f56c16cu), r, F64(0x11111111u, 0x3f811111u)); -p = f64_fma(p, r, F64(0x55555555u, 0x3fa55555u)); -p = f64_fma(p, r, F64(0x55555555u, 0x3fc55555u)); -p = f64_fma(p, r, F64(0x00000000u, 0x3fe00000u)); -var e: F64 = f64_fma(f64_mul(r, r), p, r); -var t: F64 = f64_add(f64_mul(e, UCRT_EXP_MUL[u32(j)]), UCRT_EXP_LO[u32(j)]); -var res: F64 = f64_add(t, UCRT_EXP_HI[u32(j)]); -if (((k > -(1022i)) || ((k == -(1022i)) && f64_ge(res, F64(0x00000000u, 0x3ff00000u))))) { -return u64_add(res, u64_shl(i64_from_i32(k), u32(52i))); -} -return f64_mul(res, u64_shl(U64(0x00000001u, 0x00000000u), u32(((k + 1074i) & 63i)))); - -} - -fn win10_exp(x_arg: F64) -> F64 { -var x = x_arg; - -var xb: U64 = x; -if (u64_ge((xb & U64(0xffffffffu, 0x7fffffffu)), U64(0x00000000u, 0x7ff00000u))) { -if (u64_eq(xb, U64(0x00000000u, 0x7ff00000u))) { -return x; -} -if (u64_eq(xb, U64(0x00000000u, 0xfff00000u))) { -return F64(0x00000000u, 0x00000000u); -} -return win_quiet(x); -} - -if (f64_gt(x, F64(0xfefa39efu, 0x40862e42u))) { -return U64(0x00000000u, 0x7ff00000u); -} -if (f64_lt(x, f64_neg(F64(0xdfefd9d0u, 0x40874046u)))) { -return F64(0x00000000u, 0x00000000u); -} -return win_exp_finite(x); - -} - -fn win11_exp(x_arg: F64) -> F64 { -var x = x_arg; - -var xb: U64 = x; -if (u64_ge((xb & U64(0xffffffffu, 0x7fffffffu)), U64(0x00000000u, 0x7ff00000u))) { -if (u64_eq(xb, U64(0x00000000u, 0x7ff00000u))) { -return x; -} -if (u64_eq(xb, U64(0x00000000u, 0xfff00000u))) { -return F64(0x00000000u, 0x00000000u); -} -return win_quiet(x); -} - -if (f64_gt(x, F64(0xfefa39efu, 0x40862e42u))) { -return U64(0x00000000u, 0x7ff00000u); -} -if (f64_lt(x, f64_neg(F64(0xdfefd9d0u, 0x40874046u)))) { -return select(F64(0x00000000u, 0x00000000u), U64(0x00000001u, 0x00000000u), f64_ge(x, f64_neg(F64(0xd52d3051u, 0x40874910u)))); -} -return win_exp_finite(x); - -} - -fn win_tanh_k(i_arg: i32, up_arg: bool) -> F64 { -var i = i_arg; -var up = up_arg; - -var v: F64 = F64(0x00000000u, 0x00000000u); -if ((i == 0i)) { -v = select(f64_neg(F64(0xfaf479c9u, 0x3f2a387bu)), f64_neg(F64(0xc69835ddu, 0x3f25b483u)), up); -} -if ((i == 1i)) { -v = select(F64(0x0d09af81u, 0x3e4e82d1u), F64(0x847e6cecu, 0x3e48cc5au), up); -} -if ((i == 2i)) { -v = select(F64(0xcecad284u, 0x3f2b68b3u), F64(0xf334e750u, 0x3f26af77u), up); -} -if ((i == 3i)) { -v = select(F64(0xcc7a88a9u, 0x3f94a3d4u), F64(0xbaec487fu, 0x3f912335u), up); -} -if ((i == 4i)) { -v = select(F64(0xb90302aeu, 0x3f920629u), F64(0x6e212118u, 0x3f8defadu), up); -} -if ((i == 5i)) { -v = select(F64(0x1c4ab94cu, 0x3fd86cd0u), F64(0x59bcc5f6u, 0x3fd44d14u), up); -} -if ((i == 6i)) { -v = select(F64(0xe8007758u, 0x3fd189b6u), F64(0x7c5ae288u, 0x3fcd2859u), up); -} -if ((i == 7i)) { -v = select(F64(0x5c00b304u, 0x3fea4e92u), F64(0x1d442afdu, 0x3fe5de43u), up); -} -return v; - -} - -fn win_tanh_scale1(k_arg: i32) -> F64 { -var k = k_arg; - -return u64_shl(i64_from_i32((((k + select(0i, 1i, (k < 0i))) >> u32(1i)) + 1023i)), u32(52i)); - -} - -fn win_tanh_scale2(k_arg: i32) -> F64 { -var k = k_arg; - -return u64_shl(i64_from_i32(((k - ((k + select(0i, 1i, (k < 0i))) >> u32(1i))) + 1023i)), u32(52i)); - -} - -fn win10_tanh(x_arg: F64) -> F64 { -var x = x_arg; - -var xb: U64 = x; -var ab: U64 = (xb & U64(0xffffffffu, 0x7fffffffu)); -if (u64_lt(ab, U64(0x00000000u, 0x3e300000u))) { -return x; -} -if (u64_gt(ab, U64(0x00000000u, 0x7ff00000u))) { -return win_quiet(x); -} -var neg: bool = u64_ne(ab, xb); -var a: F64 = ab; -if (f64_gt(a, F64(0x00000000u, 0x40340000u))) { -return select(F64(0x00000000u, 0x3ff00000u), f64_neg(F64(0x00000000u, 0x3ff00000u)), neg); -} -var res: F64; -if (f64_gt(a, F64(0x00000000u, 0x3ff00000u))) { -var a2: F64 = f64_add(a, a); -var n: i32 = f64_to_i32(f64_add(f64_mul(a2, F64(0x652b82feu, 0x40471547u)), F64(0x00000000u, 0x3fe00000u))); -var j: i32 = (n & 31i); -var nd: F64 = f64_from_i32(n); -var hi: F64 = UCRT_TANH_HI[u32(j)]; -var lo: F64 = UCRT_TANH_LO[u32(j)]; -var k: i32 = ((n - j) >> u32(5i)); -var r6: F64 = f64_sub(a2, f64_mul(nd, F64(0xfe000000u, 0x3f962e42u))); -var r1: F64 = f64_mul(f64_from_i32(-(n)), F64(0xe6af278eu, 0x3dcf473du)); -var r: F64 = f64_add(r1, r6); -var p: F64 = f64_add(f64_mul(r, F64(0x8d739765u, 0x3f56c172u)), F64(0xb7aa905eu, 0x3f811115u)); -p = f64_add(f64_mul(p, r), F64(0x55545d4eu, 0x3fa55555u)); -p = f64_add(f64_mul(p, r), F64(0x55548f7cu, 0x3fc55555u)); -p = f64_mul(p, r); -p = f64_add(p, F64(0x00000000u, 0x3fe00000u)); -p = f64_mul(p, f64_mul(r, r)); -p = f64_add(p, r1); -p = f64_add(p, r6); -p = f64_mul(p, f64_add(lo, hi)); -p = f64_add(p, lo); -var t: F64 = f64_mul(f64_add(hi, p), win_tanh_scale1(k)); -t = f64_mul(t, win_tanh_scale2(k)); -t = f64_add(t, F64(0x00000000u, 0x3ff00000u)); -res = f64_sub(F64(0x00000000u, 0x3ff00000u), f64_div(F64(0x00000000u, 0x40000000u), t)); -} - else { -var up: bool = f64_ge(a, F64(0xcccccccdu, 0x3fecccccu)); -var x2: F64 = f64_mul(a, a); -var x3: F64 = f64_mul(x2, a); -var p: F64 = f64_sub(win_tanh_k(0i, up), f64_mul(x2, win_tanh_k(1i, up))); -p = f64_mul(p, x2); -p = f64_sub(p, win_tanh_k(4i, up)); -p = f64_mul(p, x2); -p = f64_sub(p, win_tanh_k(6i, up)); -p = f64_mul(p, x3); -var q: F64 = f64_add(f64_mul(x2, win_tanh_k(2i, up)), win_tanh_k(3i, up)); -q = f64_mul(q, x2); -q = f64_add(q, win_tanh_k(5i, up)); -q = f64_mul(q, x2); -q = f64_add(q, win_tanh_k(7i, up)); -res = f64_add(f64_div(p, q), a); -} - -return select(res, f64_neg(res), neg); - -} - -fn win11_tanh(x_arg: F64) -> F64 { -var x = x_arg; - -var xb: U64 = x; -var ab: U64 = (xb & U64(0xffffffffu, 0x7fffffffu)); -if (u64_lt(ab, U64(0x00000000u, 0x3e300000u))) { -return x; -} -if (u64_gt(ab, U64(0x00000000u, 0x7ff00000u))) { -return win_quiet(x); -} -var neg: bool = u64_ne(ab, xb); -var a: F64 = ab; -if (f64_gt(a, F64(0x00000000u, 0x40340000u))) { -return select(F64(0x00000000u, 0x3ff00000u), f64_neg(F64(0x00000000u, 0x3ff00000u)), neg); -} -var res: F64; -if (f64_gt(a, F64(0x00000000u, 0x3ff00000u))) { -var a2: F64 = f64_add(a, a); -var n: i32 = f64_to_i32(f64_add(f64_mul(a2, F64(0x652b82feu, 0x40471547u)), F64(0x00000000u, 0x3fe00000u))); -var j: i32 = (n & 31i); -var nd: F64 = f64_from_i32(n); -var hi: F64 = UCRT_TANH_HI[u32(j)]; -var lo: F64 = UCRT_TANH_LO[u32(j)]; -var k: i32 = ((n - j) >> u32(5i)); -var r6: F64 = f64_fma(f64_neg(nd), F64(0xfe000000u, 0x3f962e42u), a2); -var r1: F64 = f64_mul(f64_from_i32(-(n)), F64(0xe6af278eu, 0x3dcf473du)); -var r: F64 = f64_add(r1, r6); -var p: F64 = f64_fma(F64(0x8d739765u, 0x3f56c172u), r, F64(0xb7aa905eu, 0x3f811115u)); -p = f64_fma(p, r, F64(0x55545d4eu, 0x3fa55555u)); -p = f64_fma(p, r, F64(0x55548f7cu, 0x3fc55555u)); -p = f64_fma(p, r, F64(0x00000000u, 0x3fe00000u)); -var m: F64 = f64_add(f64_fma(f64_mul(r, r), p, r1), r6); -m = f64_fma(f64_add(hi, lo), m, lo); -var t: F64 = f64_mul(f64_add(hi, m), win_tanh_scale1(k)); -t = f64_fma(t, win_tanh_scale2(k), F64(0x00000000u, 0x3ff00000u)); -res = f64_sub(F64(0x00000000u, 0x3ff00000u), f64_div(F64(0x00000000u, 0x40000000u), t)); -} - else { -var up: bool = f64_ge(a, F64(0xcccccccdu, 0x3fecccccu)); -var x2: F64 = f64_mul(a, a); -var x3: F64 = f64_mul(x2, a); -var p: F64 = f64_fma(f64_neg(win_tanh_k(1i, up)), x2, win_tanh_k(0i, up)); -p = f64_fma(p, x2, f64_neg(win_tanh_k(4i, up))); -p = f64_fma(p, x2, f64_neg(win_tanh_k(6i, up))); -p = f64_mul(p, x3); -var q: F64 = f64_fma(win_tanh_k(2i, up), x2, win_tanh_k(3i, up)); -q = f64_fma(q, x2, win_tanh_k(5i, up)); -q = f64_fma(q, x2, win_tanh_k(7i, up)); -res = f64_add(f64_div(p, q), a); -} - -return select(res, f64_neg(res), neg); - -} - -fn win_integer_kind(yb_arg: U64) -> i32 { -var yb = yb_arg; - -var e: U64 = (u64_shr(yb, u32(52i)) & U64(0x000007ffu, 0x00000000u)); -if (u64_lt(e, U64(0x000003ffu, 0x00000000u))) { -return 0i; -} -if (u64_gt(e, U64(0x00000433u, 0x00000000u))) { -return 2i; -} -var bit: U64 = u64_shl(U64(0x00000001u, 0x00000000u), u32(u64_sub(U64(0x00000433u, 0x00000000u), e).x)); -if (u64_ne((yb & u64_sub(bit, U64(0x00000001u, 0x00000000u))), U64(0x00000000u, 0x00000000u))) { -return 0i; -} -return select(2i, 1i, u64_ne((yb & bit), U64(0x00000000u, 0x00000000u))); - -} - -fn win10_pow_core(x_arg: F64, y_arg: F64) -> F64 { -var x = x_arg; -var y = y_arg; - -var xb: U64 = x; -var yb: U64 = y; -if (u64_eq(yb, U64(0x00000000u, 0x3ff00000u))) { -return x; -} -if (u64_eq(xb, U64(0x00000000u, 0x3ff00000u))) { -return F64(0x00000000u, 0x3ff00000u); -} -var mb: U64 = xb; -var ex: i32 = (i32(u64_shr(xb, u32(52i)).x) - 1023i); -var mant: U64 = (xb & U64(0xffffffffu, 0x000fffffu)); -if ((ex == -(1023i))) { -mb = f64_sub((mant | U64(0x00000000u, 0x3ff00000u)), F64(0x00000000u, 0x3ff00000u)); -mant = (mb & U64(0xffffffffu, 0x000fffffu)); -ex = (i32(u64_shr(mb, u32(52i)).x) - 2045i); -} - -var e: F64 = f64_from_i32(ex); -var f1: U64 = u64_add((mb & U64(0x00000000u, 0x000ff000u)), u64_shl((mb & U64(0x00000000u, 0x00000800u)), u32(1i))); -var j: i32 = i32(u64_shr(f1, u32(44i)).x); -var d: F64 = f64_sub((f1 | U64(0x00000000u, 0x3fe00000u)), (mant | U64(0x00000000u, 0x3fe00000u))); -var hiv: F64 = f64_mul(d, UCRT_POW_T1[u32(j)]); -var lov: F64 = f64_mul(d, UCRT_POW_T2[u32(j)]); -var z: F64 = f64_add(hiv, lov); -var err: F64 = f64_sub(hiv, z); -var zz: F64 = f64_mul(z, z); -err = f64_add(err, lov); -var p: F64 = f64_fma(F64(0x92492494u, 0x3fc24924u), z, F64(0x55555555u, 0x3fc55555u)); -p = f64_fma(p, z, F64(0x9999999au, 0x3fc99999u)); -p = f64_fma(p, z, F64(0x00000000u, 0x3fd00000u)); -p = f64_fma(p, z, F64(0x55555555u, 0x3fd55555u)); -p = f64_fma(p, z, F64(0x00000000u, 0x3fe00000u)); -p = f64_fma(p, zz, err); -var s5: F64 = f64_fma(e, F64(0xef35793cu, 0x3e6efa39u), f64_neg(p)); -var s3: F64 = f64_add(s5, UCRT_POW_T4[u32(j)]); -var s1: F64 = s3; -s3 = f64_sub(s3, z); -var lg: F64 = f64_fma(e, F64(0xe0000000u, 0x3fe62e42u), UCRT_POW_T3[u32(j)]); -var s7: F64 = lg; -lg = f64_add(lg, s3); -var lg_hi: F64 = (lg & U64(0xf8000000u, 0xffffffffu)); -var zs: F64 = f64_add(z, s3); -s7 = f64_sub(s7, lg); -s1 = f64_sub(s1, zs); -s7 = f64_add(s7, s3); -s7 = f64_add(s7, s1); -s7 = f64_add(s7, f64_sub(lg, lg_hi)); -var y_hi: F64 = (yb & U64(0xf8000000u, 0xffffffffu)); -var y_lo: F64 = f64_sub(y, y_hi); -var a3: F64 = f64_mul(y_lo, s7); -a3 = f64_add(a3, f64_mul(y_lo, lg_hi)); -a3 = f64_add(a3, f64_mul(s7, y_hi)); -var a6: F64 = f64_mul(lg_hi, y_hi); -var prod_hi: F64 = f64_add(a6, a3); -var prod_lo: F64 = f64_sub(a6, prod_hi); -prod_lo = f64_add(prod_lo, a3); -var w: F64 = f64_mul(prod_hi, F64(0x652b82feu, 0x40571547u)); -if (f64_gt(w, F64(0x00000000u, 0x40f00000u))) { -return U64(0x00000000u, 0x7ff00000u); -} -if (f64_lt(w, f64_neg(F64(0x00000000u, 0x40f0cc00u)))) { -return F64(0x00000000u, 0x00000000u); -} -var n: i32 = f64_to_i32(f64_sub(f64_add(w, F64(0x00000000u, 0x43380000u)), F64(0x00000000u, 0x43380000u))); -var nd: F64 = f64_from_i32(n); -var r0: F64 = f64_fma(f64_neg(nd), F64(0xf0000000u, 0x3f862e42u), prod_hi); -var jj: i32 = (n & 63i); -var k: i32 = ((n - jj) >> u32(6i)); -var r: F64 = f64_add(r0, f64_mul(nd, f64_neg(F64(0xde6af278u, 0x3dfdf473u)))); -r = f64_add(r, prod_lo); -var q: F64 = f64_fma(F64(0x16c16c17u, 0x3f56c16cu), r, F64(0x11111111u, 0x3f811111u)); -q = f64_fma(q, r, F64(0x55555555u, 0x3fa55555u)); -q = f64_fma(q, r, F64(0x55555555u, 0x3fc55555u)); -q = f64_fma(q, r, F64(0x00000000u, 0x3fe00000u)); -q = f64_fma(q, r, F64(0x00000000u, 0x3ff00000u)); -q = f64_mul(q, r); -var lo: F64 = f64_mul(q, UCRT_EXP_LO[u32(jj)]); -var u: F64 = f64_mul(q, UCRT_EXP_HI[u32(jj)]); -lo = f64_add(lo, UCRT_EXP_LO[u32(jj)]); -u = f64_add(u, lo); -u = f64_add(u, UCRT_EXP_HI[u32(jj)]); -var scale: U64 = u64_shl(i64_from_i32((k + 1023i)), u32(52i)); -if ((k <= -(1022i))) { -if (((k == -(1022i)) && f64_ge(u, F64(0x00000000u, 0x3ff00000u)))) { -return f64_mul(u, scale); -} -if (i64_gt(prod_hi, U64(0xdfefd9d0u, 0xc0874046u))) { -return U64(0x00000001u, 0x00000000u); -} -var sh: i32 = select(0i, (k + 1074i), ((k + 1074i) > 0i)); -return f64_mul(u, u64_shl(U64(0x00000001u, 0x00000000u), u32((sh & 63i)))); -} - -if (((k + 1023i) == 2047i)) { -if (f64_ge(u, F64(0x00000000u, 0x3ff00000u))) { -return U64(0x00000000u, 0x7ff00000u); -} -return (u | U64(0x00000000u, 0x7fe00000u)); -} - -return f64_mul(u, scale); - -} - -fn win10_pow(x_arg: F64, y_arg: F64) -> F64 { -var x = x_arg; -var y = y_arg; - -var xb: U64 = x; -var yb: U64 = y; -var xnan: bool = win_isnan(x); -var ynan: bool = win_isnan(y); -var qnan: F64 = select(win_quiet(y), win_quiet(x), (xnan && ((!(ynan) || u64_eq(u64_shr(xb, u32(63i)), U64(0x00000000u, 0x00000000u))) || u64_ne(u64_shr(yb, u32(63i)), U64(0x00000000u, 0x00000000u))))); -if (((xnan && u64_eq((xb & U64(0x00000000u, 0x00080000u)), U64(0x00000000u, 0x00000000u))) || (ynan && u64_eq((yb & U64(0x00000000u, 0x00080000u)), U64(0x00000000u, 0x00000000u))))) { -return qnan; -} -if ((u64_eq((yb & U64(0xffffffffu, 0x7fffffffu)), U64(0x00000000u, 0x00000000u)) || u64_eq(xb, U64(0x00000000u, 0x3ff00000u)))) { -return F64(0x00000000u, 0x3ff00000u); -} -if ((xnan || ynan)) { -return qnan; -} -var xneg: bool = u64_ne(u64_shr(xb, u32(63i)), U64(0x00000000u, 0x00000000u)); -var axb: U64 = (xb & U64(0xffffffffu, 0x7fffffffu)); -var yinf: bool = u64_eq((yb & U64(0xffffffffu, 0x7fffffffu)), U64(0x00000000u, 0x7ff00000u)); -var kind: i32 = win_integer_kind(yb); -if ((u64_eq(axb, U64(0x00000000u, 0x3ff00000u)) && (yinf || (kind == 2i)))) { -return F64(0x00000000u, 0x3ff00000u); -} -var ypos: bool = u64_eq(u64_shr(yb, u32(63i)), U64(0x00000000u, 0x00000000u)); -if (yinf) { -return select(U64(0x00000000u, 0x7ff00000u), F64(0x00000000u, 0x00000000u), (u64_lt(axb, U64(0x00000000u, 0x3ff00000u)) == ypos)); -} -var sign: U64 = select(U64(0x00000000u, 0x00000000u), U64(0x00000000u, 0x80000000u), (xneg && (kind == 1i))); -if (u64_eq(axb, U64(0x00000000u, 0x00000000u))) { -return (select(U64(0x00000000u, 0x7ff00000u), U64(0x00000000u, 0x00000000u), ypos) | sign); -} -if (u64_eq(axb, U64(0x00000000u, 0x7ff00000u))) { -return (select(U64(0x00000000u, 0x00000000u), U64(0x00000000u, 0x7ff00000u), ypos) | sign); -} -if ((xneg && (kind == 0i))) { -return U64(0x00000000u, 0xfff80000u); -} -var ye: U64 = (yb & U64(0x00000000u, 0x7ff00000u)); -if (u64_gt(ye, U64(0x00000000u, 0x43e00000u))) { -return select(U64(0x00000000u, 0x7ff00000u), F64(0x00000000u, 0x00000000u), (u64_lt(axb, U64(0x00000000u, 0x3ff00000u)) == ypos)); -} -if (u64_lt(ye, U64(0x00000000u, 0x3c000000u))) { -return (f64_add(y, F64(0x00000000u, 0x3ff00000u)) | sign); -} -return (win10_pow_core(axb, y) | sign); - -} - -fn win11_pow(x_arg: F64, y_arg: F64) -> F64 { -var x = x_arg; -var y = y_arg; - -var ix: U64 = x; -var iy: U64 = y; -var topx: u32 = u32(u64_shr(ix, u32(52i)).x); -var topy: u32 = u32(u64_shr(iy, u32(52i)).x); -var sign_bias: U64 = U64(0x00000000u, 0x00000000u); -if ((((topx - 1u) >= 0x7feu) || (((topy & 0x7ffu) - 0x3beu) >= 0x80u))) { -if (u64_ge(u64_sub(u64_mul_low(iy, U64(0x00000002u, 0x00000000u)), U64(0x00000001u, 0x00000000u)), U64(0xffffffffu, 0xffdfffffu))) { -if (u64_eq(u64_mul_low(iy, U64(0x00000002u, 0x00000000u)), U64(0x00000000u, 0x00000000u))) { -return select(F64(0x00000000u, 0x3ff00000u), win_nan_operand(x, y), u64_gt(u64_mul_low((ix ^ U64(0x00000000u, 0x00080000u)), U64(0x00000002u, 0x00000000u)), U64(0x00000000u, 0xfff00000u))); -} -if (u64_eq(ix, U64(0x00000000u, 0x3ff00000u))) { -return select(F64(0x00000000u, 0x3ff00000u), win_nan_operand(x, y), u64_gt(u64_mul_low((iy ^ U64(0x00000000u, 0x00080000u)), U64(0x00000002u, 0x00000000u)), U64(0x00000000u, 0xfff00000u))); -} -if ((u64_gt(u64_mul_low(ix, U64(0x00000002u, 0x00000000u)), U64(0x00000000u, 0xffe00000u)) || u64_gt(u64_mul_low(iy, U64(0x00000002u, 0x00000000u)), U64(0x00000000u, 0xffe00000u)))) { -return win_nan_operand(x, y); -} -if (u64_eq(u64_mul_low(ix, U64(0x00000002u, 0x00000000u)), U64(0x00000000u, 0x7fe00000u))) { -return F64(0x00000000u, 0x3ff00000u); -} -if ((u64_lt(u64_mul_low(ix, U64(0x00000002u, 0x00000000u)), U64(0x00000000u, 0x7fe00000u)) == u64_eq(u64_shr(iy, u32(63i)), U64(0x00000000u, 0x00000000u)))) { -return F64(0x00000000u, 0x00000000u); -} -return f64_mul(y, y); -} - -if (u64_ge(u64_sub(u64_mul_low(ix, U64(0x00000002u, 0x00000000u)), U64(0x00000001u, 0x00000000u)), U64(0xffffffffu, 0xffdfffffu))) { -var x2: U64 = select(f64_mul(x, x), win_quiet(x), win_isnan(x)); -var neg: U64 = U64(0x00000000u, 0x00000000u); -if ((u64_ne(u64_shr(ix, u32(63i)), U64(0x00000000u, 0x00000000u)) && (win_integer_kind(iy) == 1i))) { -neg = U64(0x00000000u, 0x80000000u); -} -x2 = (x2 ^ neg); -if ((u64_eq(u64_shr(iy, u32(63i)), U64(0x00000000u, 0x00000000u)) || win_isnan(x))) { -return x2; -} -if (u64_eq(u64_mul_low(ix, U64(0x00000002u, 0x00000000u)), U64(0x00000000u, 0x00000000u))) { -return (U64(0x00000000u, 0x7ff00000u) | neg); -} -return f64_div(F64(0x00000000u, 0x3ff00000u), x2); -} - -if (u64_ne(u64_shr(ix, u32(63i)), U64(0x00000000u, 0x00000000u))) { -var kind: i32 = win_integer_kind(iy); -if ((kind == 0i)) { -return U64(0x00000000u, 0xfff80000u); -} -if ((kind == 1i)) { -sign_bias = U64(0x00080000u, 0x00000000u); -} -ix = (ix & U64(0xffffffffu, 0x7fffffffu)); -topx = (topx & 0x7ffu); -} - -if ((((topy & 0x7ffu) - 0x3beu) >= 0x80u)) { -if (u64_eq(ix, U64(0x00000000u, 0x3ff00000u))) { -return F64(0x00000000u, 0x3ff00000u); -} -if (((topy & 0x7ffu) < 0x3beu)) { -return select(f64_sub(F64(0x00000000u, 0x3ff00000u), y), f64_add(y, F64(0x00000000u, 0x3ff00000u)), u64_gt(ix, U64(0x00000000u, 0x3ff00000u))); -} -return select(F64(0x00000000u, 0x00000000u), U64(0x00000000u, 0x7ff00000u), (u64_gt(ix, U64(0x00000000u, 0x3ff00000u)) == (topy < 0x800u))); -} - -if ((topx == 0u)) { -ix = u64_sub((f64_mul(x, F64(0x00000000u, 0x43300000u)) & U64(0xffffffffu, 0x7fffffffu)), U64(0x00000000u, 0x03400000u)); -} -} - -if (u64_eq(iy, U64(0x00000000u, 0x3ff00000u))) { -return x; -} -if (u64_eq(ix, U64(0x00000000u, 0x3ff00000u))) { -return select(F64(0x00000000u, 0x3ff00000u), f64_neg(F64(0x00000000u, 0x3ff00000u)), u64_ne(sign_bias, U64(0x00000000u, 0x00000000u))); -} -var tmp: U64 = u64_sub(ix, U64(0x00000000u, 0x3fe69555u)); -var i: i32 = i32((u64_shr(tmp, u32(45i)) & U64(0x0000007fu, 0x00000000u)).x); -var kd: F64 = f64_from_i32(i32(i64_shr(tmp, u32(52i)).x)); -var z: F64 = u64_sub(ix, (tmp & U64(0x00000000u, 0xfff00000u))); -var r: F64 = f64_fma(UCRT_POW11_INVC[u32(i)], z, f64_neg(F64(0x00000000u, 0x3ff00000u))); -var t1: F64 = f64_add(f64_mul(kd, F64(0xfefa3800u, 0x3fe62e42u)), UCRT_POW11_LOGC[u32(i)]); -var ar: F64 = f64_mul(r, f64_neg(F64(0x00000000u, 0x3fe00000u))); -var ar2: F64 = f64_mul(ar, r); -var lo3: F64 = f64_fma(ar, r, f64_neg(ar2)); -var lo1: F64 = f64_add(f64_mul(kd, F64(0x93c76730u, 0x3d2ef357u)), UCRT_POW11_LOGCTAIL[u32(i)]); -var t2: F64 = f64_add(t1, r); -var lo2: F64 = f64_add(f64_sub(t1, t2), r); -var lo: F64 = f64_add(lo3, f64_add(lo1, lo2)); -var hi: F64 = f64_add(ar2, t2); -var lo4: F64 = f64_add(f64_sub(t2, hi), ar2); -lo = f64_add(lo, lo4); -var p: F64 = f64_add(f64_sub(F64(0x9959554eu, 0x3fe99999u), f64_mul(r, F64(0x5529a47au, 0x3fe55555u))), f64_mul(f64_sub(f64_mul(r, F64(0x8b263fc3u, 0x3ff0002bu)), F64(0x9b4845e9u, 0x3ff2495bu)), ar2)); -p = f64_add(f64_mul(p, ar2), f64_sub(f64_mul(r, F64(0x00000006u, 0x3fe00000u)), F64(0x55555560u, 0x3fe55555u))); -p = f64_mul(p, f64_mul(ar2, r)); -lo = f64_add(lo, p); -var lhi: F64 = f64_add(lo, hi); -var ltail: F64 = f64_add(f64_sub(hi, lhi), lo); -var ehi: F64 = f64_mul(lhi, y); -var elo: F64 = f64_add(f64_fma(y, lhi, f64_neg(ehi)), f64_mul(ltail, y)); -var abstop: u32 = (u32(u64_shr(ehi, u32(52i)).x) & 0x7ffu); -if (((abstop - 0x3c9u) >= 0x3fu)) { -if (((abstop - 0x3c9u) >= 0x80000000u)) { -var one: F64 = f64_add(ehi, F64(0x00000000u, 0x3ff00000u)); -return select(one, f64_neg(one), u64_ne(sign_bias, U64(0x00000000u, 0x00000000u))); -} - -if ((abstop >= 0x409u)) { -var s: U64 = select(U64(0x00000000u, 0x00000000u), U64(0x00000000u, 0x80000000u), u64_ne(sign_bias, U64(0x00000000u, 0x00000000u))); -return (select(U64(0x00000000u, 0x7ff00000u), U64(0x00000000u, 0x00000000u), u64_ne(u64_shr(ehi, u32(63i)), U64(0x00000000u, 0x00000000u))) | s); -} - -abstop = 0u; -} - -var kd2: F64 = f64_add(f64_mul(ehi, F64(0x652b82feu, 0x40771547u)), F64(0x00008000u, 0x42380000u)); -var ki: U64 = u64_shr(kd2, u32(16i)); -var kn: F64 = f64_from_i32(i32(u32(ki.x))); -var er: F64 = f64_add(f64_add(f64_sub(ehi, f64_mul(kn, F64(0xfefc0000u, 0x3f662e42u))), f64_mul(kn, F64(0xa86c3899u, 0x3d2c610cu))), elo); -var idx: i32 = (2i * i32((ki & U64(0x000000ffu, 0x00000000u)).x)); -var sbits: U64 = u64_add(UCRT_POW11_EXP[u32((idx + 1i))], u64_shl(u64_add(ki, sign_bias), u32(44i))); -var er2: F64 = f64_mul(er, er); -var e: F64 = f64_add(f64_mul(er2, f64_add(f64_mul(er, F64(0x5555543cu, 0x3fc55555u)), F64(0xfffffdbdu, 0x3fdfffffu))), f64_add(er, UCRT_POW11_EXP[u32(idx)])); -e = f64_add(e, f64_mul(f64_mul(er2, er2), f64_add(f64_mul(er, F64(0x67a4b553u, 0x3f811111u)), F64(0xcf16e1edu, 0x3fa55555u)))); -if ((abstop != 0u)) { -return f64_add(f64_mul(e, sbits), sbits); -} -if (u64_eq((ki & U64(0x80000000u, 0x00000000u)), U64(0x00000000u, 0x00000000u))) { -var scale: F64 = u64_sub(sbits, U64(0x00000000u, 0x3f100000u)); -return f64_mul(f64_add(f64_mul(e, scale), scale), F64(0x00000000u, 0x7f000000u)); -} - -var sb: U64 = u64_add(sbits, U64(0x00000000u, 0x3fe00000u)); -var scale: F64 = sb; -var se: F64 = f64_mul(scale, e); -var v: F64 = f64_add(scale, se); -if (u64_lt((v & U64(0xffffffffu, 0x7fffffffu)), U64(0x00000000u, 0x3ff00000u))) { -var one: F64 = select(F64(0x00000000u, 0x3ff00000u), f64_neg(F64(0x00000000u, 0x3ff00000u)), f64_lt(v, F64(0x00000000u, 0x00000000u))); -var h: F64 = f64_add(one, v); -var l: F64 = f64_add(f64_add(f64_sub(scale, v), se), f64_add(f64_sub(one, h), v)); -v = f64_sub(f64_add(l, h), one); -if (f64_eq(v, F64(0x00000000u, 0x00000000u))) { -v = (sb & U64(0x00000000u, 0x80000000u)); -} -} - -return f64_mul(v, F64(0x00000000u, 0x00100000u)); - -} - -fn profile_atan2(math_arg: u32, y_arg: f32, x_arg: f32) -> f32 { -var math = math_arg; -var y = y_arg; -var x = x_arg; - -if ((math == MATH_PROTON)) { -return native_atan2(y, x); -} -return win_atan2f(y, x); - -} - -fn profile_exp(math_arg: u32, x_arg: F64) -> F64 { -var math = math_arg; -var x = x_arg; - -if ((math == MATH_PROTON)) { -return dexp(x); -} -if ((math == MATH_WIN10_FMA3)) { -return win10_exp(x); -} -return win11_exp(x); - -} - -fn profile_pow(math_arg: u32, x_arg: F64, y_arg: F64) -> F64 { -var math = math_arg; -var x = x_arg; -var y = y_arg; - -if ((math == MATH_PROTON)) { -return dpow(x, y); -} -if ((math == MATH_WIN10_FMA3)) { -return win10_pow(x, y); -} -return win11_pow(x, y); - -} - -fn profile_tanh(math_arg: u32, x_arg: F64) -> F64 { -var math = math_arg; -var x = x_arg; - -if ((math == MATH_PROTON)) { -return game_tanh(x); -} -if ((math == MATH_WIN10_FMA3)) { -return win10_tanh(x); -} -return win11_tanh(x); - -} - -fn v2(x_arg: f32, y_arg: f32) -> vec2 { -var x = x_arg; -var y = y_arg; - -return vec2(x, y); - -} - -fn dot(a_arg: vec2, b_arg: vec2) -> f32 { -var a = a_arg; -var b = b_arg; - -return fp_add(fp_mul(a.x, b.x), fp_mul(a.y, b.y)); - -} - -fn cross(a_arg: vec2, b_arg: vec2) -> f32 { -var a = a_arg; -var b = b_arg; - -return fp_sub(fp_mul(a.x, b.y), fp_mul(a.y, b.x)); - -} - -fn length(a_arg: vec2) -> f32 { -var a = a_arg; - -return fp_sqrt(dot(a, a)); - -} - -fn same_bits(a_arg: vec2, b_arg: vec2) -> bool { -var a = a_arg; -var b = b_arg; - -return ((bitcast(a.x) == bitcast(b.x)) && (bitcast(a.y) == bitcast(b.y))); - -} - -fn normalized(a_arg: vec2) -> vec2 { -var a = a_arg; - -var n: f32 = length(a); -return select(vec2(fp_div(a.x, n), fp_div(a.y, n)), vec2(bitcast(0x00000000u), bitcast(0x00000000u)), (n == bitcast(0x00000000u))); - -} - -fn limit(a_arg: vec2, max_arg: f32) -> vec2 { -var a = a_arg; -var max = max_arg; - -var n: f32 = length(a); -return select(a, v2_mul(vec2(fp_div(a.x, n), fp_div(a.y, n)), vec2(max)), ((n > max) && (n > bitcast(0x00000000u)))); - -} - -fn rotated(a_arg: vec2, angle_arg: f32, err: ptr) -> vec2 { -var a = a_arg; -var angle = angle_arg; - -var s: f32; -var c: f32; -managed_sin_cos(angle, &(s), &(c), err); -return vec2(fp_sub(fp_mul(a.x, c), fp_mul(a.y, s)), fp_add(fp_mul(a.x, s), fp_mul(a.y, c))); - -} - -fn rmax(a_arg: f32, b_arg: f32) -> f32 { -var a = a_arg; -var b = b_arg; - -return fp_select(fp_select(a, b, (b > a)), b, fp_isnan(a)); - -} - -fn rmin(a_arg: f32, b_arg: f32) -> f32 { -var a = a_arg; -var b = b_arg; - -return fp_select(fp_select(a, b, (b < a)), b, fp_isnan(a)); - -} - -fn rclamp(x_arg: f32, lo_arg: f32, hi_arg: f32) -> f32 { -var x = x_arg; -var lo = lo_arg; -var hi = hi_arg; - -return fp_select(fp_select(x, hi, (x > hi)), lo, (x < lo)); - -} - -fn py_max(a_arg: F64, b_arg: F64) -> F64 { -var a = a_arg; -var b = b_arg; - -return select(a, b, f64_gt(b, a)); - -} - -fn py_min(a_arg: F64, b_arg: F64) -> F64 { -var a = a_arg; -var b = b_arg; - -return select(a, b, f64_lt(b, a)); - -} - -fn py_clamp(v_arg: F64, lo_arg: F64, hi_arg: F64) -> F64 { -var v = v_arg; -var lo = lo_arg; -var hi = hi_arg; - -return py_min(hi, py_max(lo, v)); - -} - -fn transform_point(position_arg: vec2, x_arg: vec2, y_arg: vec2, p_arg: vec2) -> vec2 { -var position = position_arg; -var x = x_arg; -var y = y_arg; -var p = p_arg; - -return v2_add(v2_add(v2_mul(x, vec2(p.x)), v2_mul(y, vec2(p.y))), position); - -} - -fn signed_sensor(value_arg: f32, maximum_arg: f32) -> F64 { -var value = value_arg; -var maximum = maximum_arg; - -var s_from: f32 = fp_sub(bitcast(0x00000000u), maximum); -var weight: f32 = fp_div(fp_sub(value, s_from), fp_sub(maximum, s_from)); -return f64_from_f32(rclamp(fp_add(fp_neg(bitcast(0x3f800000u)), fp_mul(bitcast(0x40000000u), weight)), fp_neg(bitcast(0x3f800000u)), bitcast(0x3f800000u))); - -} - -fn tile_at(p_arg: vec2) -> i32 { -var p = p_arg; - -var x: i32 = (f64_to_i32(f64_floor(f64_div(f64_from_f32(p.x), F64(0x00000000u, 0x40880000u)))) - sim_world.tile_x0); -var y: i32 = (f64_to_i32(f64_floor(f64_div(f64_from_f32(p.y), F64(0x00000000u, 0x40880000u)))) - sim_world.tile_y0); -if (((((x < 0i) || (y < 0i)) || (x >= sim_world.tile_nx)) || (y >= sim_world.tile_ny))) { -return -(1i); -} -return ((y * sim_world.tile_nx) + x); - -} - -fn vehicle_surface(p_arg: vec2) -> u32 { -var p = p_arg; - -var t: i32 = tile_at(p); -return select(sim_world.default_surface, load_TileCell(sim_world.tiles + (0u + u32(t)) * 6u).surface, (t >= 0i)); - -} - -fn path_sensor_point(p_arg: vec2) -> vec2 { -var p = p_arg; - -var t: i32 = tile_at(p); -if (((t < 0i) || !((load_TileCell(sim_world.tiles + (0u + u32(t)) * 6u).connected != u32(0))))) { -return p; -} -var a: vec2 = load_TileCell(sim_world.tiles + (0u + u32(t)) * 6u).first; -var v: vec2 = v2_sub(load_TileCell(sim_world.tiles + (0u + u32(t)) * 6u).last, a); -var n: f32 = dot(v, v); -var closest: vec2 = select(v2_add(a, v2_mul(v, vec2(fp_div(rclamp(fp_sub(dot(p, v), dot(a, v)), bitcast(0x00000000u), n), n)))), a, (n == bitcast(0x00000000u))); -return v2_add(closest, v2_mul(v2_sub(p, closest), vec2(bitcast(0x3f000000u)))); - -} - -fn grid_cell(g_arg: NearGrid, q_arg: vec2) -> u32 { -var g = g_arg; -var q = q_arg; - -var fx: F64 = f64_floor(f64_div(f64_sub(f64_from_f32(q.x), g.x0), g.cell)); -var fy: F64 = f64_floor(f64_div(f64_sub(f64_from_f32(q.y), g.y0), g.cell)); -if (!((((f64_ge(fx, F64(0x00000000u, 0x00000000u)) && f64_ge(fy, F64(0x00000000u, 0x00000000u))) && f64_lt(fx, f64_from_u32(g.nx))) && f64_lt(fy, f64_from_u32(g.ny))))) { -return NONE; -} -return ((u32(f64_to_i64(fy).x) * g.nx) + u32(f64_to_i64(fx).x)); - -} - -fn curve_interval(offset_arg: f32, t: ptr, err: ptr) -> u32 { -var offset = offset_arg; - -if (fp_isnan(offset)) { -(*err) = ((*err) | ERR_PATH_NAN); -} -offset = rclamp(offset, bitcast(0x00000000u), sim_world.curve_length); -var start: u32 = u32(0i); -var end: u32 = sim_world.curve_points; -var index: u32 = ((start + end) / u32(2i)); -loop { - -if (!((start < index))) { break; } -{ -if ((offset <= bitcast(world_data[sim_world.curve_offsets + (0u + index) * 1u]))) { -end = index; -} else { -start = index; -} -index = ((start + end) / u32(2i)); -} - -} -index = min(index, (sim_world.curve_points - u32(2i))); -var span: f32 = fp_sub(bitcast(world_data[sim_world.curve_offsets + (0u + (index + u32(1i))) * 1u]), bitcast(world_data[sim_world.curve_offsets + (0u + index) * 1u])); -(*t) = fp_select(fp_div(fp_sub(offset, bitcast(world_data[sim_world.curve_offsets + (0u + index) * 1u])), span), bitcast(0x3f000000u), (span < bitcast(0x34000000u))); -return index; - -} - -fn curve_direction(offset_arg: f32, err: ptr) -> vec2 { -var offset = offset_arg; - -var t: f32; -var i: u32 = curve_interval(offset, &(t), err); -var a: vec2 = vec2(bitcast(world_data[sim_world.curve_forwards + (0u + i) * 2u + 0u]), bitcast(world_data[sim_world.curve_forwards + (0u + i) * 2u + 1u])); -var b: vec2 = vec2(bitcast(world_data[sim_world.curve_forwards + (0u + (i + u32(1i))) * 2u + 0u]), bitcast(world_data[sim_world.curve_forwards + (0u + (i + u32(1i))) * 2u + 1u])); -var a2: f32 = dot(a, a); -var b2: f32 = dot(b, b); -if (((a2 == bitcast(0x00000000u)) || (b2 == bitcast(0x00000000u)))) { -return normalized(v2_add(a, v2_mul(v2_sub(b, a), vec2(t)))); -} -var len: f32 = fp_sqrt(a2); -var result_length: f32 = fp_add(len, fp_mul(fp_sub(fp_sqrt(b2), len), t)); -var angle: f32 = fp_mul(profile_atan2(sim_world.math_profile, cross(a, b), dot(a, b)), t); -var s: f32; -var c: f32; -engine_sin_cos(angle, &(s), &(c), err); -return normalized(v2_mul(vec2(fp_sub(fp_mul(a.x, c), fp_mul(a.y, s)), fp_add(fp_mul(a.x, s), fp_mul(a.y, c))), vec2(fp_div(result_length, len)))); - -} - -fn curve_position(offset_arg: f32, err: ptr) -> vec2 { -var offset = offset_arg; - -var t: f32; -var i: u32 = curve_interval(offset, &(t), err); -var a: vec2 = vec2(bitcast(world_data[sim_world.curve_position + (0u + i) * 2u + 0u]), bitcast(world_data[sim_world.curve_position + (0u + i) * 2u + 1u])); -return v2_add(a, v2_mul(v2_sub(vec2(bitcast(world_data[sim_world.curve_position + (0u + (i + u32(1i))) * 2u + 0u]), bitcast(world_data[sim_world.curve_position + (0u + (i + u32(1i))) * 2u + 1u])), a), vec2(t))); - -} - -fn curve_project(q_arg: vec2, i_arg: u32, d2: ptr) -> f32 { -var q = q_arg; -var i = i_arg; - -var span: f32 = fp_sub(bitcast(world_data[sim_world.curve_offsets + (0u + (i + u32(1i))) * 1u]), bitcast(world_data[sim_world.curve_offsets + (0u + i) * 1u])); -var origin: vec2 = vec2(bitcast(world_data[sim_world.curve_position + (0u + i) * 2u + 0u]), bitcast(world_data[sim_world.curve_position + (0u + i) * 2u + 1u])); -var e: vec2 = v2_sub(vec2(bitcast(world_data[sim_world.curve_position + (0u + (i + u32(1i))) * 2u + 0u]), bitcast(world_data[sim_world.curve_position + (0u + (i + u32(1i))) * 2u + 1u])), origin); -var direction: vec2 = vec2(fp_div(e.x, span), fp_div(e.y, span)); -var d: f32 = rclamp(dot(v2_sub(q, origin), direction), bitcast(0x00000000u), span); -var delta: vec2 = v2_sub(v2_add(origin, v2_mul(direction, vec2(d))), q); -(*d2) = dot(delta, delta); -return fp_add(bitcast(world_data[sim_world.curve_offsets + (0u + i) * 1u]), d); - -} - -fn curve_closest_offset(q_arg: vec2) -> f32 { -var q = q_arg; - -var nearest: f32 = bitcast(0x00000000u); -var distance: f32 = fp_neg(bitcast(0x3f800000u)); -var count: u32 = (sim_world.curve_points - u32(1i)); -if ((sim_world.curve_points < u32(2i))) { -return nearest; -} -{ -var lambda_visit_i: u32 = u32(0i); -loop { -var d2: f32; -var offset: f32 = curve_project(q, lambda_visit_i, &(d2)); -if (((distance < bitcast(0x00000000u)) || (d2 < distance))) { -nearest = offset; -distance = d2; -} - - -break; -} -} - - -var cell: u32 = grid_cell(sim_world.curve_grid, q); -if ((cell != NONE)) { -{ -var k: u32 = world_data[sim_world.curve_grid.start + (0u + cell) * 1u]; -var e: u32 = world_data[sim_world.curve_grid.start + (0u + (cell + u32(1i))) * 1u]; -loop { - -if (!((k < e))) { break; } -{ -var i: u32 = world_data[sim_world.curve_grid.items + (0u + k) * 1u]; -if ((i > u32(0i))) { -var lambda_visit_i: u32 = i; -loop { -var d2: f32; -var offset: f32 = curve_project(q, lambda_visit_i, &(d2)); -if (((distance < bitcast(0x00000000u)) || (d2 < distance))) { -nearest = offset; -distance = d2; -} - - -break; -} -} - - -} - -continuing { -k = (k + 1u); -} -} -} -} - else { -{ -var i: u32 = u32(1i); -loop { - -if (!((i < count))) { break; } -{ -var lambda_visit_i: u32 = i; -loop { -var d2: f32; -var offset: f32 = curve_project(q, lambda_visit_i, &(d2)); -if (((distance < bitcast(0x00000000u)) || (d2 < distance))) { -nearest = offset; -distance = d2; -} - - -break; -} -} - -continuing { -i = (i + 1u); -} -} -} - -} - -return nearest; - -} - -fn path_project(s_arg: PathSeg, p_arg: vec2, fraction: ptr, near: ptr>) -> f32 { -var s = s_arg; -var p = p_arg; - -var start: vec2 = vec2(s.x, s.y); -var delta: vec2 = vec2(s.dx, s.dy); -(*fraction) = fp_select(bitcast(0x00000000u), rclamp(fp_div(dot(v2_sub(p, start), delta), s.len2), bitcast(0x00000000u), bitcast(0x3f800000u)), (s.len2 > bitcast(0x00000000u))); -(*near) = v2_add(start, v2_mul(delta, vec2((*fraction)))); -var diff: vec2 = v2_sub(p, (*near)); -return dot(diff, diff); - -} - -fn path_posmod(x_arg: f32, length_arg: f32) -> f32 { -var x = x_arg; -var length = length_arg; - -var r: f32 = fp_mod(x, length); -return fp_select(r, fp_add(r, length), (r < bitcast(0x00000000u))); - -} - -fn path_index(offset_arg: f32, err: ptr) -> i32 { -var offset = offset_arg; - -var v: F64 = f64_from_f32(offset); -if (f64_isnan(v)) { -(*err) = ((*err) | ERR_PATH_NAN); -return 0i; -} - -var lo: u32 = u32(0i); -var hi: u32 = sim_world.path_points; -loop { - -if (!((lo < hi))) { break; } -{ -var mid: u32 = ((lo + hi) / u32(2i)); -if (f64_lt(U64(world_data[sim_world.path_offsets + (0u + mid) * 2u], world_data[sim_world.path_offsets + (0u + mid) * 2u + 1u]), v)) { -lo = (mid + u32(1i)); -} else { -hi = mid; -} -} - -} -if (((lo < sim_world.path_points) && f64_eq(U64(world_data[sim_world.path_offsets + (0u + lo) * 2u], world_data[sim_world.path_offsets + (0u + lo) * 2u + 1u]), v))) { -return i32(lo); -} -return i32(select(u32(0i), (lo - u32(1i)), (lo > u32(0i)))); - -} - -fn closest_path(point_arg: vec2, has_previous_arg: bool, previous_arg: F64, err: ptr) -> PathHit { -var point = point_arg; -var has_previous = has_previous_arg; -var previous = previous_arg; - -var count: i32 = i32((sim_world.path_points - u32(1i))); -var length: f32 = f64_to_f32(U64(world_data[sim_world.path_offsets + (0u + u32(count)) * 2u], world_data[sim_world.path_offsets + (0u + u32(count)) * 2u + 1u])); -var s_from: i32 = 0i; -var to: i32 = count; -if ((has_previous && (length > bitcast(0x44610000u)))) { -var prev: f32 = f64_to_f32(previous); -var center: i32 = path_index(path_posmod(prev, length), err); -s_from = path_index(path_posmod(fp_sub(prev, bitcast(0x43e10000u)), length), err); -to = (path_index(path_posmod(fp_add(prev, bitcast(0x43e10000u)), length), err) + 1i); -if ((s_from > center)) { -s_from = (s_from - count); -} -if ((to < center)) { -to = (to + count); -} -} - -var distance: f32 = bitcast(0x7f7fffffu); -var nearest: i32 = -(1i); -var cell: u32 = select(NONE, grid_cell(sim_world.path_grid, point), ((s_from == 0i) && (to == count))); -if ((cell != NONE)) { -{ -var k: u32 = world_data[sim_world.path_grid.start + (0u + cell) * 1u]; -var e: u32 = world_data[sim_world.path_grid.start + (0u + (cell + u32(1i))) * 1u]; -loop { - -if (!((k < e))) { break; } -{ -var lambda_visit_i: i32 = i32(world_data[sim_world.path_grid.items + (0u + k) * 1u]); -loop { -var fraction: f32; -var near: vec2; -var d2: f32 = path_project(load_PathSeg(sim_world.path_segments + (0u + u32(lambda_visit_i)) * 8u), point, &(fraction), &(near)); -if ((d2 < distance)) { -distance = d2; -nearest = lambda_visit_i; -} - - -break; -} -} - -continuing { -k = (k + 1u); -} -} -} - -} - else { -{ -var i: i32 = s_from; -loop { - -if (!((i < to))) { break; } -{ -var lambda_visit_i: i32 = (((i % count) + count) % count); -loop { -var fraction: f32; -var near: vec2; -var d2: f32 = path_project(load_PathSeg(sim_world.path_segments + (0u + u32(lambda_visit_i)) * 8u), point, &(fraction), &(near)); -if ((d2 < distance)) { -distance = d2; -nearest = lambda_visit_i; -} - - -break; -} -} - -continuing { -i = (i + 1i); -} -} -} - -} - -if ((nearest < 0i)) { -return PathHit(F64(0x00000000u, 0x00000000u), vec2(bitcast(0x00000000u), bitcast(0x00000000u)), vec2(bitcast(0x00000000u), bitcast(0x00000000u))); -} -var s: PathSeg = load_PathSeg(sim_world.path_segments + (0u + u32(nearest)) * 8u); -var fraction: f32; -var near: vec2; -path_project(s, point, &(fraction), &(near)); -var begin: f32 = f64_to_f32(U64(world_data[sim_world.path_offsets + (0u + u32(nearest)) * 2u], world_data[sim_world.path_offsets + (0u + u32(nearest)) * 2u + 1u])); -var end: f32 = f64_to_f32(U64(world_data[sim_world.path_offsets + (0u + u32((nearest + 1i))) * 2u], world_data[sim_world.path_offsets + (0u + u32((nearest + 1i))) * 2u + 1u])); -var offset: F64 = select(F64(0x00000000u, 0x00000000u), f64_from_f32(path_posmod(fp_add(begin, fp_mul(fp_sub(end, begin), fraction)), length)), (length > bitcast(0x00000000u))); -return PathHit(offset, near, normalized(vec2(s.dx, s.dy))); - -} - -fn beside(box_arg: vec4, start_arg: vec2, b_arg: vec2, margin_arg: f32) -> bool { -var box = box_arg; -var start = start_arg; -var b = b_arg; -var margin = margin_arg; - -var cross: f32 = fp_sub(fp_mul(b.x, fp_sub(box.y, start.y)), fp_mul(b.y, fp_sub(box.x, start.x))); -return (fp_abs(cross) > fp_add(fp_mul(fp_abs(b.x), fp_add(box.w, margin)), fp_mul(fp_abs(b.y), fp_add(box.z, margin)))); - -} - -fn classify(wall_arg: vec4, p_arg: vec2) -> i32 { -var wall = wall_arg; -var p = p_arg; - -var cross: f32 = fp_sub(fp_mul(wall.z, fp_sub(p.y, wall.y)), fp_mul(wall.w, fp_sub(p.x, wall.x))); -return select(select(0i, -(1i), (cross < fp_neg(bitcast(0x38d1b717u)))), 1i, (cross > bitcast(0x38d1b717u))); - -} - -fn ray_wall_hit(start_arg: vec2, b_arg: vec2, wall_arg: vec4, hit: ptr>) -> bool { -var start = start_arg; -var b = b_arg; -var wall = wall_arg; - -var denominator: f32 = fp_sub(fp_mul(wall.z, b.y), fp_mul(b.x, wall.w)); -var dx: f32 = fp_sub(wall.x, start.x); -var dy: f32 = fp_sub(wall.y, start.y); -var ray_fraction: f32 = fp_div(fp_add(fp_mul(fp_sub(bitcast(0x00000000u), wall.w), dx), fp_mul(wall.z, dy)), denominator); -var wall_fraction: f32 = fp_div(fp_sub(fp_mul(b.x, dy), fp_mul(b.y, dx)), denominator); -(*hit) = vec2(fp_add(wall.x, fp_mul(wall.z, wall_fraction)), fp_add(wall.y, fp_mul(wall.w, wall_fraction))); -return ((((!((fp_abs(denominator) < bitcast(0x358637bdu))) && (ray_fraction >= bitcast(0x00000000u))) && (ray_fraction <= bitcast(0x3f800000u))) && (wall_fraction >= bitcast(0x00000000u))) && (wall_fraction <= bitcast(0x3f800000u))); - -} - -fn raycast(start_arg: vec2, end_arg: vec2, hit: ptr>) -> bool { -var start = start_arg; -var end = end_arg; - -var b: vec2 = vec2(fp_sub(end.x, start.x), fp_sub(end.y, start.y)); -var scale: f32 = fp_add(fp_add(sim_world.ray_magnitude, fp_abs(start.x)), fp_abs(start.y)); -var margin: f32 = f64_to_f32(select(F64(0u, 0x7ff00000u), f64_from_f32(fp_add(bitcast(0x3a83126fu), fp_mul(bitcast(0x37a7c5acu), scale))), (fp_add(fp_abs(b.x), fp_abs(b.y)) > bitcast(0x2edbe6ffu)))); -var stack: array; -var depth: i32 = 0i; -var current: u32 = select(NONE, u32(0i), (sim_world.ray_node_count != u32(0))); -{ - -loop { - -if (!(true)) { break; } -{ -loop { - -if (!((current != NONE))) { break; } -{ -var node: RayNode = load_RayNode(sim_world.ray_nodes + (0u + current) * 12u); -if (beside(node.subtree, start, b, margin)) { -break; -} -var s_partition: vec4 = vec4(bitcast(world_data[sim_world.ray_walls + (0u + node.first) * 4u + 0u]), bitcast(world_data[sim_world.ray_walls + (0u + node.first) * 4u + 1u]), bitcast(world_data[sim_world.ray_walls + (0u + node.first) * 4u + 2u]), bitcast(world_data[sim_world.ray_walls + (0u + node.first) * 4u + 3u])); -var a: i32 = classify(s_partition, start); -var e: i32 = classify(s_partition, end); -var front_first: bool = ((a == 1i) || ((a == 0i) && (e == 1i))); -let post_1 = depth; -depth = (depth + 1i); -stack[u32(post_1)] = ((current << u32(1i)) | u32((a != e))); -current = select(node.back, node.front, front_first); -} - -} -if ((depth == 0i)) { -return (0i != i32(0)); -} -let post_2 = depth; -depth = (depth - 1i); -var entry: u32 = stack[u32(depth)]; -var node: RayNode = load_RayNode(sim_world.ray_nodes + (0u + (entry >> u32(1i))) * 12u); -if (!(beside(node.own, start, b, margin))) { -var distance: f32 = bitcast(0x7f7fffffu); -var found: bool = (0i != i32(0)); -{ -var i: u32 = node.first; -loop { - -if (!((i < node.last))) { break; } -{ -var p: vec2; -var ok: bool = ray_wall_hit(start, b, vec4(bitcast(world_data[sim_world.ray_walls + (0u + i) * 4u + 0u]), bitcast(world_data[sim_world.ray_walls + (0u + i) * 4u + 1u]), bitcast(world_data[sim_world.ray_walls + (0u + i) * 4u + 2u]), bitcast(world_data[sim_world.ray_walls + (0u + i) * 4u + 3u])), &(p)); -var dx: f32 = fp_sub(p.x, start.x); -var dy: f32 = fp_sub(p.y, start.y); -var d2: f32 = fp_add(fp_mul(dx, dx), fp_mul(dy, dy)); -var better: bool = (ok && (d2 < distance)); -distance = fp_select(distance, d2, better); -(*hit) = select((*hit), p, better); -found = (found | better); -} - -continuing { -i = (i + 1u); -} -} -} -if (found) { -return (1i != i32(0)); -} -} - -var s_partition: vec4 = vec4(bitcast(world_data[sim_world.ray_walls + (0u + node.first) * 4u + 0u]), bitcast(world_data[sim_world.ray_walls + (0u + node.first) * 4u + 1u]), bitcast(world_data[sim_world.ray_walls + (0u + node.first) * 4u + 2u]), bitcast(world_data[sim_world.ray_walls + (0u + node.first) * 4u + 3u])); -var front_first: bool = ((classify(s_partition, start) == 1i) || ((classify(s_partition, start) == 0i) && (classify(s_partition, end) == 1i))); -current = select(NONE, select(node.front, node.back, front_first), ((entry & u32(1i)) != u32(0))); -} - -continuing { - -} -} -} - -return bool(); -} - -fn closest_wall_point(p_arg: vec2) -> vec2 { -var p = p_arg; - -var distance: f32 = f64_to_f32(F64(0u, 0x7ff00000u)); -var closest: vec2 = vec2(bitcast(0x00000000u), bitcast(0x00000000u)); -var stack: array; -var depth: i32 = 0i; -var current: u32 = select(NONE, u32(0i), (sim_world.ray_node_count != u32(0))); -{ - -loop { - -if (!(true)) { break; } -{ -loop { - -if (!((current != NONE))) { break; } -{ -var node: RayNode = load_RayNode(sim_world.ray_nodes + (0u + current) * 12u); -{ -var i: u32 = node.first; -loop { - -if (!((i < node.last))) { break; } -{ -var wall: vec4 = vec4(bitcast(world_data[sim_world.ray_walls + (0u + i) * 4u + 0u]), bitcast(world_data[sim_world.ray_walls + (0u + i) * 4u + 1u]), bitcast(world_data[sim_world.ray_walls + (0u + i) * 4u + 2u]), bitcast(world_data[sim_world.ray_walls + (0u + i) * 4u + 3u])); -var len2: f32 = fp_add(fp_mul(wall.z, wall.z), fp_mul(wall.w, wall.w)); -var t: f32 = fp_sub(fp_add(fp_mul(p.x, wall.z), fp_mul(p.y, wall.w)), fp_add(fp_mul(wall.x, wall.z), fp_mul(wall.y, wall.w))); -t = fp_select(fp_select(t, len2, (t > len2)), bitcast(0x00000000u), (t < bitcast(0x00000000u))); -t = fp_div(t, len2); -var candidate: vec2 = select(vec2(fp_add(wall.x, fp_mul(wall.z, t)), fp_add(wall.y, fp_mul(wall.w, t))), vec2(wall.x, wall.y), (len2 == bitcast(0x00000000u))); -var dx: f32 = fp_sub(p.x, candidate.x); -var dy: f32 = fp_sub(p.y, candidate.y); -var d2: f32 = fp_add(fp_mul(dx, dx), fp_mul(dy, dy)); -var better: bool = (d2 < distance); -distance = fp_select(distance, d2, better); -closest = select(closest, candidate, better); -} - -continuing { -i = (i + 1u); -} -} -} -let post_3 = depth; -depth = (depth + 1i); -stack[u32(post_3)] = current; -current = select(node.back, node.front, (classify(vec4(bitcast(world_data[sim_world.ray_walls + (0u + node.first) * 4u + 0u]), bitcast(world_data[sim_world.ray_walls + (0u + node.first) * 4u + 1u]), bitcast(world_data[sim_world.ray_walls + (0u + node.first) * 4u + 2u]), bitcast(world_data[sim_world.ray_walls + (0u + node.first) * 4u + 3u])), p) >= 0i)); -} - -} -if ((depth == 0i)) { -return closest; -} -let post_4 = depth; -depth = (depth - 1i); -var node: RayNode = load_RayNode(sim_world.ray_nodes + (0u + stack[u32(depth)]) * 12u); -var s_partition: vec4 = vec4(bitcast(world_data[sim_world.ray_walls + (0u + node.first) * 4u + 0u]), bitcast(world_data[sim_world.ray_walls + (0u + node.first) * 4u + 1u]), bitcast(world_data[sim_world.ray_walls + (0u + node.first) * 4u + 2u]), bitcast(world_data[sim_world.ray_walls + (0u + node.first) * 4u + 3u])); -var t: f32 = fp_div(fp_add(fp_mul(fp_sub(p.x, s_partition.x), s_partition.z), fp_mul(fp_sub(p.y, s_partition.y), s_partition.w)), fp_add(fp_mul(s_partition.z, s_partition.z), fp_mul(s_partition.w, s_partition.w))); -var dx: f32 = fp_sub(p.x, fp_add(s_partition.x, fp_mul(s_partition.z, t))); -var dy: f32 = fp_sub(p.y, fp_add(s_partition.y, fp_mul(s_partition.w, t))); -var far: bool = (fp_add(fp_mul(dx, dx), fp_mul(dy, dy)) < distance); -current = select(NONE, select(node.front, node.back, (classify(s_partition, p) >= 0i)), far); -} - -continuing { - -} -} -} - -return vec2(); -} - -fn reads(m_arg: ReadMask, index_arg: u32) -> bool { -var m = m_arg; -var index = index_arg; - -var batch: u32 = (index / m.batch_size); -return (((((batch + u32(8i)) - m.batch_index) % u32(8i)) < m.batches_per_tick) && (!((m.require_active != u32(0))) || ((sim_car.flags & CAR_ACTIVE) != u32(0)))); - -} - -fn actual_velocity() -> vec2 { - - -return select(sim_car.velocity, vec2(bitcast(0x00000000u), bitcast(0x00000000u)), ((sim_car.flags & CAR_PENDING_VELOCITY) != u32(0))); - -} - -fn actual_angular() -> f32 { - - -return fp_select(sim_car.angular_velocity, bitcast(0x00000000u), ((sim_car.flags & CAR_PENDING_VELOCITY) != u32(0))); - -} - -fn actual_acceleration() -> vec2 { - - -return select(sim_car.acceleration, sim_car.reset_acceleration, ((sim_car.flags & CAR_PENDING_VELOCITY) != u32(0))); - -} - -fn cached_right() -> vec2 { - - -return select(normalized(sim_car.basis_x), vec2(bitcast(0x00000000u), bitcast(0x00000000u)), ((sim_car.flags & CAR_RESET_RIGHT) != u32(0))); - -} - -fn cached_front() -> vec2 { - - -var r: vec2 = cached_right(); -var s: f32 = bitcast(0xbf7fffffu ^ params.pad); -var co: f32 = bitcast(0xb3bbbd2eu ^ params.pad); -return vec2(fp_sub(fp_mul(r.x, co), fp_mul(r.y, s)), fp_add(fp_mul(r.x, s), fp_mul(r.y, co))); - -} - -fn sensor_path_offset() -> f32 { - - -return curve_closest_offset(path_sensor_point(sim_car.position)); - -} - -fn sensor_value(s_arg: SensorDesc, here_arg: f32, err: ptr) -> F64 { -var s = s_arg; -var here = here_arg; - -switch (s.kind) { -case S_RAYCAST: { -{ -var local: vec2 = s.offset; -var end: vec2 = transform_point(sim_car.position, sim_car.basis_x, sim_car.basis_y, local); -var hit: vec2; -if (!(raycast(vec2(sim_car.position.x, sim_car.position.y), vec2(end.x, end.y), &(hit)))) { -return F64(0x00000000u, 0x00000000u); -} -if (((hit.x == end.x) && (hit.y == end.y))) { -return F64(0x00000000u, 0x00000000u); -} -return f64_from_f32(fp_sub(bitcast(0x3f800000u), fp_div(length(v2_sub(vec2(hit.x, hit.y), sim_car.position)), s.b))); -} - -} -case S_DISTANCE_FROM_WALL: { -{ -var wall: vec2 = closest_wall_point(vec2(sim_car.position.x, sim_car.position.y)); -return f64_from_f32(rclamp(fp_div(length(v2_sub(sim_car.position, vec2(wall.x, wall.y))), s.a), bitcast(0x00000000u), bitcast(0x3f800000u))); -} - -} -case S_SPEED: { -return f64_from_f32(rclamp(fp_div(length(sim_car.velocity), sim_vehicle.max_velocity_f), bitcast(0x00000000u), bitcast(0x3f800000u))); -} -case S_VELOCITY_FRONT: { -return signed_sensor(dot(actual_velocity(), cached_front()), sim_vehicle.max_velocity_f); -} -case S_VELOCITY_SIDE: { -return signed_sensor(dot(actual_velocity(), cached_right()), fp_div(sim_vehicle.max_velocity_f, bitcast(0x40000000u))); -} -case S_ACCELERATION_FRONT: { -return signed_sensor(dot(actual_acceleration(), cached_front()), s.a); -} -case S_ACCELERATION_SIDE: { -return signed_sensor(dot(actual_acceleration(), cached_right()), s.a); -} -case S_ANGULAR_VELOCITY: { -return signed_sensor(actual_angular(), bitcast(0x40000000u)); -} -case S_WHEEL_ANGLE: { -{ -var i: u32 = u32(0i); -loop { - -if (!((((i + u32(1i)) < sim_vehicle.wheel_count) && !((sim_vehicle.wheels[i].steering != u32(0)))))) { break; } -i = (i + 1u); -} -return signed_sensor(sim_car.wheel_angle[i], bitcast(0x42340000u)); -} - -} -case S_BOOST_CAPACITY: { -return py_clamp(f64_from_f32(sim_car.boost_energy), F64(0x00000000u, 0x00000000u), F64(0x00000000u, 0x3ff00000u)); -} -case S_GRIP: { -{ -var p: vec2 = v2_add(sim_car.position, rotated(s.offset, sim_car.rotation, err)); -return f64_from_f32(load_SurfaceDev(sim_world.surfaces + (0u + vehicle_surface(p)) * 4u).grip); -} - -} -case S_CORRECT_DIRECTION: { -{ -if ((sim_world.path_points < u32(2i))) { -return F64(0x00000000u, 0x00000000u); -} -var tangent: vec2 = normalized(curve_direction(here, err)); -var forward: vec2 = normalized(cached_front()); -return f64_from_f32(rclamp(dot(forward, tangent), fp_neg(bitcast(0x3f800000u)), bitcast(0x3f800000u))); -} - -} -case S_TRACK_CURVATURE: { -{ -if ((sim_world.path_points < u32(2i))) { -return F64(0x00000000u, 0x00000000u); -} -var tangent: vec2 = normalized(curve_direction(here, err)); -if ((sim_world.curve_length <= bitcast(0x00000000u))) { -return F64(0x00000000u, 0x00000000u); -} -var speed: f32 = rclamp(fp_div(length(actual_velocity()), sim_vehicle.max_velocity_f), bitcast(0x00000000u), bitcast(0x3f800000u)); -var lo: f32 = rmax(s.a, bitcast(0x00000000u)); -var hi: f32 = rmax(s.b, lo); -var offset: f32 = fp_mod(fp_add(here, fp_add(lo, fp_mul(fp_sub(hi, lo), speed))), sim_world.curve_length); -var ahead: vec2 = normalized(curve_direction(offset, err)); -return f64_from_f32(rclamp(fp_div(fp_abs(profile_atan2(sim_world.math_profile, cross(tangent, ahead), dot(tangent, ahead))), PI_F), bitcast(0x00000000u), bitcast(0x3f800000u))); -} - -} -default: {} -} -return F64(0x00000000u, 0x00000000u); - -} - -fn normalized_controls(c_arg: array) -> Controls { -var c = c_arg; - -return Controls(py_clamp(c[u32(0i)], f64_neg(F64(0x00000000u, 0x3ff00000u)), F64(0x00000000u, 0x3ff00000u)), py_clamp(c[u32(1i)], f64_neg(F64(0x00000000u, 0x3ff00000u)), F64(0x00000000u, 0x3ff00000u)), py_clamp(c[u32(2i)], f64_neg(F64(0x00000000u, 0x3ff00000u)), F64(0x00000000u, 0x3ff00000u)), py_clamp(c[u32(3i)], F64(0x00000000u, 0x00000000u), F64(0x00000000u, 0x3ff00000u)), py_clamp(c[u32(4i)], F64(0x00000000u, 0x00000000u), F64(0x00000000u, 0x3ff00000u))); - -} - -fn is_zero(a_arg: vec2) -> bool { -var a = a_arg; - -return ((a.x == bitcast(0x00000000u)) && (a.y == bitcast(0x00000000u))); - -} - -fn godot_ease(math_arg: u32, value_arg: F64, curve_arg: F64) -> F64 { -var math = math_arg; -var value = value_arg; -var curve = curve_arg; - -return f64_sub(F64(0x00000000u, 0x3ff00000u), profile_pow(math, f64_sub(F64(0x00000000u, 0x3ff00000u), py_clamp(value, F64(0x00000000u, 0x00000000u), F64(0x00000000u, 0x3ff00000u))), f64_div(F64(0x00000000u, 0x3ff00000u), curve))); - -} - -fn combine(brake_arg: vec2, drive_arg: vec2, brake_max_arg: f32, drive_max_arg: f32) -> vec2 { -var brake = brake_arg; -var drive = drive_arg; -var brake_max = brake_max_arg; -var drive_max = drive_max_arg; - -var n: f32 = length(brake); -if (((n < bitcast(0x38d1b717u)) || (brake_max <= bitcast(0x00000000u)))) { -return v2_add(brake, drive); -} -var allowed: f32 = fp_add(n, fp_mul(drive_max, fp_sub(bitcast(0x3f800000u), rmin(fp_div(n, brake_max), bitcast(0x3f800000u))))); -var result: vec2 = v2_add(brake, drive); -var axis: vec2 = normalized(brake); -var projection: f32 = dot(result, axis); -return select(result, v2_sub(result, v2_mul(axis, vec2(fp_sub(projection, allowed)))), (projection > allowed)); - -} - -fn set_transform_angle(math_arg: u32, angle_arg: f32, err: ptr) { -var math = math_arg; -var angle = angle_arg; - -var s: f32; -var c: f32; -engine_sin_cos(angle, &(s), &(c), err); -sim_car.transform_angle = angle; -sim_car.basis_x = vec2(c, s); -sim_car.basis_y = vec2(fp_neg(s), c); -sim_car.rotation = profile_atan2(math, s, c); - -} - -fn update_shape() { - - -var bx: vec2 = sim_car.basis_x; -var by: vec2 = sim_car.basis_y; -var x: vec2 = v2_add(v2_mul(bx, vec2(sim_vehicle.shape_basis_x.x)), v2_mul(by, vec2(sim_vehicle.shape_basis_x.y))); -var y: vec2 = v2_add(v2_mul(bx, vec2(sim_vehicle.shape_basis_y.x)), v2_mul(by, vec2(sim_vehicle.shape_basis_y.y))); -var size: vec2 = sim_vehicle.shape_size; -var shape_origin: vec2 = v2_add(v2_add(v2_mul(bx, vec2(sim_vehicle.shape_position.x)), v2_mul(by, vec2(sim_vehicle.shape_position.y))), sim_car.position); -var pos: vec2 = v2_add(v2_add(v2_mul(x, vec2(fp_mul(fp_neg(size.x), bitcast(0x3f000000u)))), v2_mul(y, vec2(fp_mul(fp_neg(size.y), bitcast(0x3f000000u))))), shape_origin); -var dx: vec2 = v2_mul(x, vec2(size.x)); -var dy: vec2 = v2_mul(y, vec2(size.y)); -var rp: vec2 = pos; -var rs: vec2 = vec2(bitcast(0x00000000u), bitcast(0x00000000u)); -{ -var lambda_expand_rect_p: vec2 = v2_add(pos, dx); -loop { -var e: vec2 = v2_add(rp, rs); -var b: vec2 = vec2(rmin(rp.x, lambda_expand_rect_p.x), rmin(rp.y, lambda_expand_rect_p.y)); -var f: vec2 = vec2(rmax(e.x, lambda_expand_rect_p.x), rmax(e.y, lambda_expand_rect_p.y)); -rp = b; -rs = v2_sub(f, b); - -break; -} -} - - -{ -var lambda_expand_rect_p: vec2 = v2_add(pos, dy); -loop { -var e: vec2 = v2_add(rp, rs); -var b: vec2 = vec2(rmin(rp.x, lambda_expand_rect_p.x), rmin(rp.y, lambda_expand_rect_p.y)); -var f: vec2 = vec2(rmax(e.x, lambda_expand_rect_p.x), rmax(e.y, lambda_expand_rect_p.y)); -rp = b; -rs = v2_sub(f, b); - -break; -} -} - - -{ -var lambda_expand_rect_p: vec2 = v2_add(v2_add(pos, dx), dy); -loop { -var e: vec2 = v2_add(rp, rs); -var b: vec2 = vec2(rmin(rp.x, lambda_expand_rect_p.x), rmin(rp.y, lambda_expand_rect_p.y)); -var f: vec2 = vec2(rmax(e.x, lambda_expand_rect_p.x), rmax(e.y, lambda_expand_rect_p.y)); -rp = b; -rs = v2_sub(f, b); - -break; -} -} - - -var margin: f32 = fp_select(bitcast(0x00000000u), f64_to_f32(f64_mul(f64_mul(f64_from_f32(fp_add(sim_car.shape_size.x, sim_car.shape_size.y)), F64(0x00000000u, 0x3fe00000u)), F64(0x9999999au, 0x3fa99999u))), ((sim_car.flags & CAR_HAS_SHAPE) != u32(0))); -rp = v2_sub(rp, vec2(margin, margin)); -rs = v2_add(rs, vec2(fp_mul(margin, bitcast(0x40000000u)), fp_mul(margin, bitcast(0x40000000u)))); -sim_car.shape_position = rp; -sim_car.shape_size = rs; -sim_car.flags = (sim_car.flags | CAR_HAS_SHAPE); -var raw_min: vec2 = rp; -var raw_max: vec2 = v2_add(rp, rs); -if (!(((sim_car.flags & CAR_HAS_LEAF) != u32(0)))) { -sim_car.leaf_min = raw_min; -sim_car.leaf_max = raw_max; -sim_car.flags = (sim_car.flags | (CAR_HAS_LEAF | CAR_PAIR_CHECK)); -return; -} - -var threshold: f32 = f64_to_f32(f64_mul(f64_mul(f64_mul(f64_from_f32(bitcast(0x3dcccccdu)), F64(0x00000000u, 0x40000000u)), F64(0x00000000u, 0x40000000u)), f64_from_f32(bitcast(0x3f8ccccdu)))); -var density: f32 = f64_to_f32(f64_mul(f64_from_u32(sim_car.pair_count), f64_div(F64(0x00000000u, 0x3ff00000u), F64(0x00000000u, 0x40220000u)))); -var grow: f32 = fp_mul(bitcast(0x3dcccccdu), fp_sub(bitcast(0x3f800000u), rmin(density, bitcast(0x3f800000u)))); -var old_size: vec2 = v2_sub(sim_car.leaf_max, sim_car.leaf_min); -var rt_max: vec2 = v2_add(sim_car.leaf_min, old_size); -var encloses: bool = ((((raw_min.x >= sim_car.leaf_min.x) && (raw_min.y >= sim_car.leaf_min.y)) && (raw_max.x <= rt_max.x)) && (raw_max.y <= rt_max.y)); -if ((encloses && (fp_sub(fp_add(old_size.x, old_size.y), fp_add(rs.x, rs.y)) < threshold))) { -return; -} -sim_car.leaf_min = v2_sub(raw_min, vec2(grow, grow)); -sim_car.leaf_max = v2_add(raw_max, vec2(grow, grow)); -sim_car.flags = (sim_car.flags | CAR_PAIR_CHECK); - -} - -fn project_on_line(point_arg: vec2, e0_arg: vec2, e1_arg: vec2) -> vec2 { -var point = point_arg; -var e0 = e0_arg; -var e1 = e1_arg; - -var direction: vec2 = v2_sub(e1, e0); -var offset: vec2 = v2_sub(point, e0); -var ls: f32 = dot(direction, direction); -if ((ls < bitcast(0x1e3ce508u))) { -return e0; -} -var fraction: f32 = fp_div(dot(direction, offset), ls); -return v2_add(e0, v2_mul(direction, vec2(fraction))); - -} - -fn rectangle_supports(car_arg: array, 4>, direction_arg: vec2, bx_arg: vec2, by_arg: vec2) -> Support { -var car = car_arg; -var direction = direction_arg; -var bx = bx_arg; -var by = by_arg; - -var local: vec2 = normalized(vec2(dot(bx, direction), dot(by, direction))); -if (f64_gt(f64_from_f32(fp_abs(local.x)), F64(0x0e94ee39u, 0x3fefffd6u))) { -var choice_5: Support; if ((local.x > bitcast(0x00000000u))) { choice_5 = Support(array, 2>(car[u32(2i)], car[u32(1i)]), u32(2i)); } else { choice_5 = Support(array, 2>(car[u32(3i)], car[u32(0i)]), u32(2i)); } -return choice_5; -} -if (f64_gt(f64_from_f32(fp_abs(local.y)), F64(0x0e94ee39u, 0x3fefffd6u))) { -var choice_6: Support; if ((local.y > bitcast(0x00000000u))) { choice_6 = Support(array, 2>(car[u32(2i)], car[u32(3i)]), u32(2i)); } else { choice_6 = Support(array, 2>(car[u32(1i)], car[u32(0i)]), u32(2i)); } -return choice_6; -} -if ((local.x < bitcast(0x00000000u))) { -return Support(array, 2>(select(car[u32(3i)], car[u32(0i)], (local.y < bitcast(0x00000000u))), vec2(f32(0i), f32(0i))), u32(1i)); -} -return Support(array, 2>(select(car[u32(2i)], car[u32(1i)], (local.y < bitcast(0x00000000u))), vec2(f32(0i), f32(0i))), u32(1i)); - -} - -fn polygon_supports(shape_arg: ShapeDev, wall_offset_arg: vec2, direction_arg: vec2) -> Support { -var shape = shape_arg; -var wall_offset = wall_offset_arg; -var direction = direction_arg; - -direction = normalized(direction); - - -var maximum: f32 = f64_to_f32(f64_neg(F64(0u, 0x7ff00000u))); -var best: u32 = u32(0i); -var edge: u32 = NONE; -var n: u32 = shape.count; -{ -var i: u32 = u32(0i); -loop { - -if (!((i < u32(MAX_SHAPE_POINTS)))) { break; } -{ -if ((i >= n)) { -break; -} -var projection: f32 = dot(vec2(bitcast(world_data[sim_world.shape_local + ((0u + shape.first) + i) * 2u + 0u]), bitcast(world_data[sim_world.shape_local + ((0u + shape.first) + i) * 2u + 1u])), direction); -var higher: bool = (projection > maximum); -maximum = fp_select(maximum, projection, higher); -best = select(best, i, higher); -edge = select(edge, i, ((edge == NONE) && f64_gt(f64_from_f32(dot(vec2(bitcast(world_data[sim_world.shape_normals + ((0u + shape.first) + i) * 2u + 0u]), bitcast(world_data[sim_world.shape_normals + ((0u + shape.first) + i) * 2u + 1u])), direction)), F64(0x0e94ee39u, 0x3fefffd6u)))); -} - -continuing { -i = (i + 1u); -} -} -} -if ((edge != NONE)) { -return Support(array, 2>(v2_add(vec2(bitcast(world_data[sim_world.shape_local + ((0u + shape.first) + edge) * 2u + 0u]), bitcast(world_data[sim_world.shape_local + ((0u + shape.first) + edge) * 2u + 1u])), wall_offset), v2_add(vec2(bitcast(world_data[sim_world.shape_local + ((0u + shape.first) + select((edge + u32(1i)), u32(0i), ((edge + u32(1i)) == n))) * 2u + 0u]), bitcast(world_data[sim_world.shape_local + ((0u + shape.first) + select((edge + u32(1i)), u32(0i), ((edge + u32(1i)) == n))) * 2u + 1u])), wall_offset)), u32(2i)); -} -return Support(array, 2>(v2_add(vec2(bitcast(world_data[sim_world.shape_local + ((0u + shape.first) + best) * 2u + 0u]), bitcast(world_data[sim_world.shape_local + ((0u + shape.first) + best) * 2u + 1u])), wall_offset), vec2(f32(0i), f32(0i))), u32(1i)); - -} - -fn contact_pairs(a_arg: Support, b_arg: Support, push_arg: vec2, out: ptr, 2>, 2>>) -> u32 { -var a = a_arg; -var b = b_arg; -var push = push_arg; - -if (((a.n == u32(1i)) && (b.n == u32(1i)))) { -(*out)[u32(0i)][u32(0i)] = a.p[u32(0i)]; -(*out)[u32(0i)][u32(1i)] = b.p[u32(0i)]; -return u32(1i); -} - -if ((a.n == u32(1i))) { -(*out)[u32(0i)][u32(0i)] = a.p[u32(0i)]; -(*out)[u32(0i)][u32(1i)] = project_on_line(a.p[u32(0i)], b.p[u32(0i)], b.p[u32(1i)]); -return u32(1i); -} - -if ((b.n == u32(1i))) { -(*out)[u32(0i)][u32(0i)] = project_on_line(b.p[u32(0i)], a.p[u32(0i)], a.p[u32(1i)]); -(*out)[u32(0i)][u32(1i)] = b.p[u32(0i)]; -return u32(1i); -} - -var tangent: vec2 = vec2(push.y, fp_neg(push.x)); -var distance_a: f32 = dot(push, a.p[u32(0i)]); -var distance_b: f32 = dot(push, b.p[u32(0i)]); -var key: array = array(dot(a.p[u32(0i)], tangent), dot(a.p[u32(1i)], tangent), dot(b.p[u32(0i)], tangent), dot(b.p[u32(1i)], tangent)); -var order: array = array(u32(0i), u32(1i), u32(2i), u32(3i)); -{ -var i: i32 = 1i; -loop { - -if (!((i < 4i))) { break; } -{ -var j: i32 = i; -loop { - -if (!(((j > 0i) && (key[order[u32(j)]] < key[order[u32((j - 1i))]])))) { break; } -{ -var t: u32 = order[u32(j)]; -order[u32(j)] = order[u32((j - 1i))]; -order[u32((j - 1i))] = t; -} - -continuing { -j = (j - 1i); -} -} -} -continuing { -i = (i + 1i); -} -} -} -var count: u32 = u32(0i); -{ -var k: i32 = 1i; -loop { - -if (!((k < 3i))) { break; } -{ -var o: u32 = order[u32(k)]; -var from_car: bool = (o < u32(2i)); -var point: vec2 = select(b.p[(o - u32(2i))], a.p[o], from_car); -var distance: f32 = fp_sub(dot(push, point), fp_select(distance_a, distance_b, from_car)); -var other: vec2 = v2_sub(point, v2_mul(push, vec2(distance))); -var p0: vec2 = select(other, point, from_car); -var p1: vec2 = select(point, other, from_car); -if (f64_le(f64_from_f32(dot(push, p0)), f64_sub(f64_from_f32(dot(push, p1)), F64(0x88e368f1u, 0x3ee4f8b5u)))) { -(*out)[count][u32(0i)] = p0; -(*out)[count][u32(1i)] = p1; -count = (count + 1u); -} - -} - -continuing { -k = (k + 1i); -} -} -} -return count; - -} - -fn shape_overlaps(s_arg: ShapeDev, left_arg: F64, right_arg: F64, top_arg: F64, bottom_arg: F64) -> bool { -var s = s_arg; -var left = left_arg; -var right = right_arg; -var top = top_arg; -var bottom = bottom_arg; - -return !((((f64_lt(s.max_x, left) || f64_gt(s.min_x, right)) || f64_lt(s.max_y, top)) || f64_gt(s.min_y, bottom))); - -} - -fn update_pairs(left_arg: F64, right_arg: F64, top_arg: F64, bottom_arg: F64) { -var left = left_arg; -var right = right_arg; -var top = top_arg; -var bottom = bottom_arg; - -var kept: u32 = u32(0i); -{ -var i: u32 = u32(0i); -loop { - -if (!((i < sim_car.pair_count))) { break; } -{ -var index: u32 = sim_car.pairs[i]; -if (shape_overlaps(load_ShapeDev(sim_world.shapes + (0u + index) * 16u), left, right, top, bottom)) { -sim_car.pairs[kept] = index; -sim_car.axes[kept] = sim_car.axes[i]; -kept = (kept + 1u); -} - -} - -continuing { -i = (i + 1u); -} -} -} -var retained: u32 = kept; -var g: NearGrid = sim_world.shape_grid; -var fx: F64 = f64_floor(f64_div(f64_sub(left, g.x0), g.cell)); -var fy: F64 = f64_floor(f64_div(f64_sub(top, g.y0), g.cell)); -var inside: bool = (((((f64_ge(fx, F64(0x00000000u, 0x00000000u)) && f64_ge(fy, F64(0x00000000u, 0x00000000u))) && f64_lt(fx, f64_from_u32(g.nx))) && f64_lt(fy, f64_from_u32(g.ny))) && f64_lt(right, f64_add(f64_add(g.x0, f64_mul(f64_add(fx, F64(0x00000000u, 0x3ff00000u)), g.cell)), sim_world.shape_margin))) && f64_lt(bottom, f64_add(f64_add(g.y0, f64_mul(f64_add(fy, F64(0x00000000u, 0x3ff00000u)), g.cell)), sim_world.shape_margin))); -if (inside) { -var cell: u32 = ((u32(f64_to_i64(fy).x) * g.nx) + u32(f64_to_i64(fx).x)); -{ -var k: u32 = world_data[g.start + (0u + cell) * 1u]; -var e: u32 = world_data[g.start + (0u + (cell + u32(1i))) * 1u]; -loop { - -if (!((k < e))) { break; } -{ -var lambda_visit_index: u32 = world_data[g.items + (0u + k) * 1u]; -loop { -if (!(shape_overlaps(load_ShapeDev(sim_world.shapes + (0u + lambda_visit_index) * 16u), left, right, top, bottom))) { -break; -} -var duplicate: bool = (0i != i32(0)); -{ -var j: u32 = u32(0i); -loop { - -if (!((j < retained))) { break; } -duplicate = (duplicate | (sim_car.pairs[j] == lambda_visit_index)); -continuing { -j = (j + 1u); -} -} -} -if (duplicate) { -break; -} -if ((kept == u32(MAX_PAIRS))) { -sim_car.error = (sim_car.error | ERR_PAIRS); -break; -} - -sim_car.pairs[kept] = lambda_visit_index; -sim_car.axes[kept] = vec2(bitcast(0x00000000u), bitcast(0x00000000u)); -kept = (kept + 1u); - -break; -} -} - -continuing { -k = (k + 1u); -} -} -} - -} - else { -{ -var i: u32 = u32(0i); -loop { - -if (!((i < sim_world.shape_count))) { break; } -{ -var lambda_visit_index: u32 = i; -loop { -if (!(shape_overlaps(load_ShapeDev(sim_world.shapes + (0u + lambda_visit_index) * 16u), left, right, top, bottom))) { -break; -} -var duplicate: bool = (0i != i32(0)); -{ -var j: u32 = u32(0i); -loop { - -if (!((j < retained))) { break; } -duplicate = (duplicate | (sim_car.pairs[j] == lambda_visit_index)); -continuing { -j = (j + 1u); -} -} -} -if (duplicate) { -break; -} -if ((kept == u32(MAX_PAIRS))) { -sim_car.error = (sim_car.error | ERR_PAIRS); -break; -} - -sim_car.pairs[kept] = lambda_visit_index; -sim_car.axes[kept] = vec2(bitcast(0x00000000u), bitcast(0x00000000u)); -kept = (kept + 1u); - -break; -} -} - -continuing { -i = (i + 1u); -} -} -} - -} - -sim_car.pair_count = kept; -sim_car.flags = (sim_car.flags & ~(CAR_PAIR_CHECK)); - -} - -fn car_frame() -> CarFrame { - - -var f: CarFrame; -f.bx = sim_car.basis_x; -f.by = sim_car.basis_y; -f.basis_x = v2_add(v2_mul(f.bx, vec2(sim_vehicle.shape_basis_x.x)), v2_mul(f.by, vec2(sim_vehicle.shape_basis_x.y))); -f.basis_y = v2_add(v2_mul(f.bx, vec2(sim_vehicle.shape_basis_y.x)), v2_mul(f.by, vec2(sim_vehicle.shape_basis_y.y))); -var sxs: array = array(fp_neg(bitcast(0x3f800000u)), bitcast(0x3f800000u), bitcast(0x3f800000u), fp_neg(bitcast(0x3f800000u))); -var sys: array = array(fp_neg(bitcast(0x3f800000u)), fp_neg(bitcast(0x3f800000u)), bitcast(0x3f800000u), bitcast(0x3f800000u)); -{ -var i: i32 = 0i; -loop { - -if (!((i < 4i))) { break; } -{ -var x: f32 = fp_mul(sxs[u32(i)], sim_vehicle.shape_half.x); -var y: f32 = fp_mul(sys[u32(i)], sim_vehicle.shape_half.y); -f.local[u32(i)] = vec2(fp_add(fp_mul(f.basis_x.x, x), fp_mul(f.basis_y.x, y)), fp_add(fp_mul(f.basis_x.y, x), fp_mul(f.basis_y.y, y))); -} - -continuing { -i = (i + 1i); -} -} -} -f.axis_x = normalized(f.basis_x); -f.axis_y = normalized(f.basis_y); -return f; - -} - -fn pair_frame(f_arg: CarFrame, shape_arg: ShapeDev) -> PairFrame { -var f = f_arg; -var shape = shape_arg; - -var p: PairFrame; -p.wall_first = (shape.wall_first != u32(0i)); -p.origin = select(sim_car.position, shape.origin, p.wall_first); -p.car_offset = v2_sub(sim_car.position, p.origin); -p.wall_offset = v2_sub(shape.origin, p.origin); -var shape_origin: vec2 = v2_add(v2_add(v2_mul(f.bx, vec2(sim_vehicle.shape_position.x)), v2_mul(f.by, vec2(sim_vehicle.shape_position.y))), p.car_offset); -{ -var i: i32 = 0i; -loop { - -if (!((i < 4i))) { break; } -p.car[u32(i)] = v2_add(f.local[u32(i)], shape_origin); -continuing { -i = (i + 1i); -} -} -} -return p; - -} - -fn sat_pair(ci_arg: u32) -> vec2 { -var ci = ci_arg; - -var f: CarFrame = car_frame(); -var shape: ShapeDev = load_ShapeDev(sim_world.shapes + (0u + sim_car.pairs[ci]) * 16u); -var p: PairFrame = pair_frame(f, shape); -var n: u32 = shape.count; - -var wall: array, 16>; -{ -var i: u32 = u32(0i); -loop { - -if (!((i < u32(MAX_SHAPE_POINTS)))) { break; } -wall[i] = select(vec2(bitcast(0x00000000u), bitcast(0x00000000u)), v2_add(vec2(bitcast(world_data[sim_world.shape_local + ((0u + shape.first) + i) * 2u + 0u]), bitcast(world_data[sim_world.shape_local + ((0u + shape.first) + i) * 2u + 1u])), p.wall_offset), (i < n)); -continuing { -i = (i + 1u); -} -} -} - -var cached: vec2 = sim_car.axes[ci]; -var axis_count: u32 = (u32(3i) + n); -var best_depth: f32 = bitcast(0x58635fa9u); -var push: vec2 = vec2(bitcast(0x00000000u), bitcast(0x00000000u)); -{ -var k: u32 = u32(select(0i, 1i, is_zero(cached))); -loop { - -if (!((k < axis_count))) { break; } -{ -var axis: vec2; -if ((k >= u32(3i))) { -var e: u32 = (k - u32(3i)); -var en: vec2 = normalized(v2_sub(v2_add(vec2(bitcast(world_data[sim_world.shape_local + ((0u + shape.first) + select((e + u32(1i)), u32(0i), ((e + u32(1i)) == n))) * 2u + 0u]), bitcast(world_data[sim_world.shape_local + ((0u + shape.first) + select((e + u32(1i)), u32(0i), ((e + u32(1i)) == n))) * 2u + 1u])), p.wall_offset), v2_add(vec2(bitcast(world_data[sim_world.shape_local + ((0u + shape.first) + e) * 2u + 0u]), bitcast(world_data[sim_world.shape_local + ((0u + shape.first) + e) * 2u + 1u])), p.wall_offset))); -axis = vec2(en.y, fp_neg(en.x)); -} - else { -axis = select(select(f.axis_y, f.axis_x, (k == u32(1i))), cached, (k == u32(0i))); -} - -if ((f64_lt(f64_from_f32(fp_abs(axis.x)), F64(0x88e368f1u, 0x3ee4f8b5u)) && f64_lt(f64_from_f32(fp_abs(axis.y)), F64(0x88e368f1u, 0x3ee4f8b5u)))) { -axis = vec2(bitcast(0x00000000u), bitcast(0x3f800000u)); -} -var min_a: f32 = f64_to_f32(F64(0u, 0x7ff00000u)); -var max_a: f32 = f64_to_f32(f64_neg(F64(0u, 0x7ff00000u))); -var min_b: f32 = f64_to_f32(F64(0u, 0x7ff00000u)); -var max_b: f32 = f64_to_f32(f64_neg(F64(0u, 0x7ff00000u))); -{ -var i: i32 = 0i; -loop { - -if (!((i < 4i))) { break; } -{ -var d: f32 = dot(p.car[u32(i)], axis); -min_a = fp_select(min_a, d, (d < min_a)); -max_a = fp_select(max_a, d, (d > max_a)); -} - -continuing { -i = (i + 1i); -} -} -} -{ -var i: u32 = u32(0i); -loop { - -if (!((i < u32(MAX_SHAPE_POINTS)))) { break; } -{ -if ((i >= n)) { -break; -} -var d: f32 = dot(wall[i], axis); -min_b = fp_select(min_b, d, (d < min_b)); -max_b = fp_select(max_b, d, (d > max_b)); -} - -continuing { -i = (i + 1u); -} -} -} -var width_a: f32 = fp_mul(fp_sub(max_a, min_a), bitcast(0x3f000000u)); -var center_a: f32 = fp_mul(fp_add(min_a, max_a), bitcast(0x3f000000u)); -var dmin: f32 = fp_sub(fp_sub(min_b, width_a), center_a); -var dmax: f32 = fp_sub(fp_add(max_b, width_a), center_a); -if (((dmin > bitcast(0x00000000u)) || (dmax < bitcast(0x00000000u)))) { -sim_car.axes[ci] = axis; -return vec2(bitcast(0x00000000u), bitcast(0x00000000u)); -} - -var use_max: bool = (dmax < fp_abs(dmin)); -var depth: f32 = fp_select(fp_abs(dmin), dmax, use_max); -if ((depth < best_depth)) { -best_depth = depth; -push = select(-(axis), axis, use_max); -} - -} - -continuing { -k = (k + 1u); -} -} -} -if (!(is_zero(push))) { -sim_car.axes[ci] = vec2(bitcast(0x00000000u), bitcast(0x00000000u)); -} -return push; - -} - -fn pair_contacts(f_arg: CarFrame, ci_arg: u32, push_arg: vec2) -> u32 { -var f = f_arg; -var ci = ci_arg; -var push = push_arg; - -var shape_index: u32 = sim_car.pairs[ci]; -var shape: ShapeDev = load_ShapeDev(sim_world.shapes + (0u + shape_index) * 16u); -var p: PairFrame = pair_frame(f, shape); -var support_car: Support = rectangle_supports(p.car, -(push), f.basis_x, f.basis_y); -var support_wall: Support = polygon_supports(shape, p.wall_offset, push); -var pairs: array, 2>, 2>; -var made: u32 = contact_pairs(support_car, support_wall, push, &(pairs)); -var added: u32 = u32(0i); -{ -var k: u32 = u32(0i); -loop { - -if (!((k < made))) { break; } -{ -var car_point: vec2 = pairs[k][u32(0i)]; -var wall_point: vec2 = pairs[k][u32(1i)]; -var delta: vec2 = select(v2_sub(car_point, wall_point), v2_sub(wall_point, car_point), p.wall_first); -var depth: f32 = fp_sqrt(dot(delta, delta)); -if (!((depth > bitcast(0x00000000u)))) { -continue; -} -var normal: vec2 = vec2(fp_div(delta.x, depth), fp_div(delta.y, depth)); -if (!(p.wall_first)) { -normal = -(normal); -} -var relative: vec2 = v2_sub(car_point, p.car_offset); -var local: vec2 = vec2(dot(f.bx, relative), dot(f.by, relative)); -var global_a: vec2 = v2_add(v2_add(v2_mul(f.bx, vec2(local.x)), v2_mul(f.by, vec2(local.y))), p.car_offset); -var wall_local: vec2 = v2_sub(wall_point, p.wall_offset); -var global_b: vec2 = v2_add(wall_local, p.wall_offset); -var c: Contact = Contact(); -c.normal = normal; -c.point = v2_add(global_a, p.origin); -c.wall_point = v2_add(global_b, p.origin); -c.local_point = local; -c.wall_local_point = wall_local; -c.depth = dot(v2_sub(global_b, global_a), normal); -c.shape = shape_index; -c.flags = (select(0u, CONTACT_WALL_FIRST, p.wall_first) | CONTACT_USED); -merge_contact(c); -added = (added + 1u); -} - -continuing { -k = (k + 1u); -} -} -} -return added; - -} - -fn refresh_contact(contact_index_arg: u32, position_arg: vec2, bx_arg: vec2, by_arg: vec2) -> vec2 { -var contact_index = contact_index_arg; -var position = position_arg; -var bx = bx_arg; -var by = by_arg; - - -var shape: ShapeDev = load_ShapeDev(sim_world.shapes + (0u + sim_car.contacts[contact_index].shape) * 16u); -var origin: vec2 = select(position, shape.origin, ((sim_car.contacts[contact_index].flags & CONTACT_WALL_FIRST) != u32(0))); -var car_offset: vec2 = v2_sub(position, origin); -var wall_offset: vec2 = v2_sub(shape.origin, origin); -var car: vec2 = v2_add(v2_add(v2_mul(bx, vec2(sim_car.contacts[contact_index].local_point.x)), v2_mul(by, vec2(sim_car.contacts[contact_index].local_point.y))), car_offset); -var wall: vec2 = v2_add(sim_car.contacts[contact_index].wall_local_point, wall_offset); -sim_car.contacts[contact_index].depth = dot(v2_sub(wall, car), sim_car.contacts[contact_index].normal); -sim_car.contacts[contact_index].point = v2_add(car, origin); -sim_car.contacts[contact_index].wall_point = v2_add(wall, origin); -return v2_sub(v2_sub(wall, v2_mul(sim_car.contacts[contact_index].normal, vec2(sim_car.contacts[contact_index].depth))), car); - -} - -fn remove_contact(index_arg: u32) { -var index = index_arg; - -{ -var j: u32 = index; -loop { - -if (!(((j + u32(1i)) < sim_car.contact_count))) { break; } -sim_car.contacts[j] = sim_car.contacts[(j + u32(1i))]; -continuing { -j = (j + 1u); -} -} -} -sim_car.contact_count = (sim_car.contact_count - 1u); - -} - -fn contact_arm(c_arg: Contact, position_arg: vec2, bx_arg: vec2, by_arg: vec2, center_arg: vec2) -> vec2 { -var c = c_arg; -var position = position_arg; -var bx = bx_arg; -var by = by_arg; -var center = center_arg; - -var local: vec2 = v2_add(v2_mul(bx, vec2(c.local_point.x)), v2_mul(by, vec2(c.local_point.y))); -var offset: vec2 = select(vec2(bitcast(0x00000000u), bitcast(0x00000000u)), v2_sub(position, load_ShapeDev(sim_world.shapes + (0u + c.shape) * 16u).origin), ((c.flags & CONTACT_WALL_FIRST) != u32(0))); -return v2_sub(v2_sub(v2_add(local, offset), center), offset); - -} - -fn prepare_contacts() { - - -var position: vec2 = sim_car.position; -var bx: vec2 = sim_car.basis_x; -var by: vec2 = sim_car.basis_y; -var max_sep2: f32 = fp_mul(sim_vehicle.max_separation_f, sim_vehicle.max_separation_f); -{ -var i: u32 = u32(0i); -loop { - -if (!((i < sim_car.contact_count))) { break; } -{ - -var separation: vec2 = refresh_contact(i, position, bx, by); -if (((!(((sim_car.contacts[i].flags & CONTACT_USED) != u32(0))) || f64_lt(f64_from_f32(sim_car.contacts[i].depth), f64_neg(sim_vehicle.contact_max_separation))) || (dot(separation, separation) > max_sep2))) { -var last: u32 = i; -{ -var j: u32 = sim_car.contact_count; -loop { -let post_7 = j; -j = (j - 1u); -if (!((post_7 > i))) { break; } -if ((sim_car.contacts[j].shape == sim_car.contacts[i].shape)) { -last = j; -break; -} - -continuing { - -} -} -} -sim_car.contacts[i] = sim_car.contacts[last]; -remove_contact(last); -} - else { -sim_car.contacts[i].flags = (sim_car.contacts[i].flags & ~(CONTACT_USED)); -i = (i + 1u); -} - -} - -continuing { - -} -} -} -if (!(((sim_car.flags & CAR_HAS_SHAPE) != u32(0)))) { -update_shape(); -} -var qmin: vec2 = sim_car.leaf_min; -var qmax: vec2 = v2_add(sim_car.leaf_min, v2_sub(sim_car.leaf_max, sim_car.leaf_min)); -if (((sim_car.flags & CAR_PAIR_CHECK) != u32(0))) { -update_pairs(f64_from_f32(qmin.x), f64_from_f32(qmax.x), f64_from_f32(qmin.y), f64_from_f32(qmax.y)); -} -var kept: u32 = u32(0i); -{ -var i: u32 = u32(0i); -loop { - -if (!((i < sim_car.contact_count))) { break; } -{ -var paired: bool = (0i != i32(0)); -{ -var j: u32 = u32(0i); -loop { - -if (!((j < sim_car.pair_count))) { break; } -paired = (paired | (sim_car.pairs[j] == sim_car.contacts[i].shape)); -continuing { -j = (j + 1u); -} -} -} -if (paired) { -let post_8 = kept; -kept = (kept + 1u); -sim_car.contacts[post_8] = sim_car.contacts[i]; -} -} - -continuing { -i = (i + 1u); -} -} -} -sim_car.contact_count = kept; - -} - -fn merge_contact(nw_arg: Contact) { -var nw = nw_arg; - -var radius2: f32 = fp_mul(sim_vehicle.recycle_radius, sim_vehicle.recycle_radius); -var found: i32 = -(1i); -var same: u32 = u32(0i); -{ -var i: u32 = u32(0i); -loop { - -if (!((i < sim_car.contact_count))) { break; } -{ -var old: Contact = sim_car.contacts[i]; -if ((old.shape != nw.shape)) { -continue; -} -same = (same + 1u); -var a: vec2 = v2_sub(old.local_point, nw.local_point); -var b: vec2 = v2_sub(old.wall_local_point, nw.wall_local_point); -if ((((found < 0i) && (dot(a, a) < radius2)) && (dot(b, b) < radius2))) { -found = i32(i); -} -} - -continuing { -i = (i + 1u); -} -} -} -if ((found >= 0i)) { - -var r: Contact = nw; -r.acc_normal = sim_car.contacts[u32(found)].acc_normal; -r.acc_tangent = sim_car.contacts[u32(found)].acc_tangent; -r.acc_bias = sim_car.contacts[u32(found)].acc_bias; -r.acc_bias_center = sim_car.contacts[u32(found)].acc_bias_center; -sim_car.contacts[u32(found)] = r; -} - else { -if ((same < u32(2i))) { -if ((sim_car.contact_count == u32(MAX_CONTACTS))) { -sim_car.error = (sim_car.error | ERR_CONTACTS); -} else { -let post_9 = sim_car.contact_count; -sim_car.contact_count = (sim_car.contact_count + 1u); -sim_car.contacts[post_9] = nw; -} -} - else { -var least: i32 = -(1i); -var depth: f32 = nw.depth; -{ -var i: u32 = u32(0i); -loop { - -if (!((i < sim_car.contact_count))) { break; } -if (((sim_car.contacts[i].shape == nw.shape) && (sim_car.contacts[i].depth < depth))) { -depth = sim_car.contacts[i].depth; -least = i32(i); -} - -continuing { -i = (i + 1u); -} -} -} -if ((least >= 0i)) { -sim_car.contacts[u32(least)] = nw; -} -} - -} - -} - -fn solve_contacts(made_contacts_arg: u32, velocity_arg: vec2, angular_arg: f32, initial_velocity_arg: vec2, initial_angular_arg: f32) -> SolveResult { -var made_contacts = made_contacts_arg; -var velocity = velocity_arg; -var angular = angular_arg; -var initial_velocity = initial_velocity_arg; -var initial_angular = initial_angular_arg; - -var position: vec2 = sim_car.position; -var bx: vec2 = sim_car.basis_x; -var by: vec2 = sim_car.basis_y; -var s_active: array; -var active_count: u32 = u32(0i); -{ -var p: u32 = u32(0i); -loop { - -if (!((p < sim_car.pair_count))) { break; } -{ -var shape: u32 = sim_car.pairs[p]; -var collided: bool = (0i != i32(0)); -{ -var q: u32 = u32(0i); -loop { - -if (!((q < sim_car.pair_count))) { break; } -collided = (collided | ((((made_contacts >> q) & u32(1i)) != u32(0)) && (sim_car.pairs[q] == shape))); -continuing { -q = (q + 1u); -} -} -} -if (!(collided)) { -continue; -} -{ -var i: u32 = u32(0i); -loop { - -if (!((i < sim_car.contact_count))) { break; } -if (((sim_car.contacts[i].shape == shape) && (sim_car.contacts[i].depth > bitcast(0x00000000u)))) { -let post_10 = active_count; -active_count = (active_count + 1u); -s_active[post_10] = i; -} -continuing { -i = (i + 1u); -} -} -} -} - -continuing { -p = (p + 1u); -} -} -} -if ((active_count == u32(0i))) { -return SolveResult(velocity, vec2(bitcast(0x00000000u), bitcast(0x00000000u)), angular, bitcast(0x00000000u), (0i != i32(0))); -} -var inv_mass: f32 = fp_div(bitcast(0x3f800000u), sim_vehicle.mass); -var inv_inertia: f32 = fp_div(bitcast(0x3f800000u), sim_vehicle.inertia); -var inv_dt: f32 = fp_div(bitcast(0x3f800000u), sim_vehicle.dt_f); -var vel: vec2 = velocity; -var wv: f32 = angular; -var center: vec2 = v2_add(v2_mul(bx, vec2(sim_vehicle.center_of_mass.x)), v2_mul(by, vec2(sim_vehicle.center_of_mass.y))); -{ -var k: u32 = u32(0i); -loop { - -if (!((k < active_count))) { break; } -{ - -var shape: ShapeDev = load_ShapeDev(sim_world.shapes + (0u + sim_car.contacts[s_active[k]].shape) * 16u); -var arm: vec2 = contact_arm(sim_car.contacts[s_active[k]], position, bx, by, center); -var n: vec2 = sim_car.contacts[s_active[k]].normal; -var t: vec2 = vec2(fp_neg(n.y), n.x); -var rn: f32 = dot(arm, n); -var rt: f32 = dot(arm, t); -sim_car.contacts[s_active[k]].normal_mass = fp_div(bitcast(0x3f800000u), fp_add(inv_mass, fp_mul(inv_inertia, fp_sub(dot(arm, arm), fp_mul(rn, rn))))); -sim_car.contacts[s_active[k]].tangent_mass = fp_div(bitcast(0x3f800000u), fp_add(inv_mass, fp_mul(inv_inertia, fp_sub(dot(arm, arm), fp_mul(rt, rt))))); -sim_car.contacts[s_active[k]].bias = fp_mul(fp_mul(fp_neg(sim_vehicle.contact_bias), inv_dt), rmin(fp_add(fp_neg(sim_car.contacts[s_active[k]].depth), sim_vehicle.allowed_penetration), bitcast(0x00000000u))); -var rotational: vec2 = vec2(fp_mul(fp_neg(arm.y), initial_angular), fp_mul(arm.x, initial_angular)); -sim_car.contacts[s_active[k]].bounce = rclamp(fp_add(sim_vehicle.bounce, shape.bounce), bitcast(0x00000000u), bitcast(0x3f800000u)); -if ((sim_car.contacts[s_active[k]].bounce != bitcast(0x00000000u))) { -sim_car.contacts[s_active[k]].bounce = fp_mul(sim_car.contacts[s_active[k]].bounce, dot(v2_add(initial_velocity, rotational), n)); -} -sim_car.contacts[s_active[k]].friction = fp_abs(rmin(sim_vehicle.friction, shape.friction)); -var impulse: vec2 = v2_add(v2_mul(n, vec2(sim_car.contacts[s_active[k]].acc_normal)), v2_mul(t, vec2(sim_car.contacts[s_active[k]].acc_tangent))); -vel = v2_add(vel, v2_mul(impulse, vec2(inv_mass))); -wv = fp_add(wv, fp_mul(cross(v2_sub(v2_add(arm, center), center), impulse), inv_inertia)); -} - -continuing { -k = (k + 1u); -} -} -} -var bv: vec2 = vec2(bitcast(0x00000000u), bitcast(0x00000000u)); -var bw: f32 = bitcast(0x00000000u); -var bias_limit: f32 = f64_to_f32(f64_div(F64(0x54442d18u, 0x3fd921fbu), f64_from_f32(sim_vehicle.dt_f))); -{ -var iteration: u32 = u32(0i); -loop { - -if (!((iteration < sim_vehicle.solver_iterations))) { break; } -{ -{ -var k: u32 = u32(0i); -loop { - -if (!((k < active_count))) { break; } -{ - -var arm: vec2 = contact_arm(sim_car.contacts[s_active[k]], position, bx, by, center); -var impulse_arm: vec2 = v2_sub(v2_add(arm, center), center); -var n: vec2 = sim_car.contacts[s_active[k]].normal; -var t: vec2 = vec2(fp_neg(n.y), n.x); -var rotational: vec2 = vec2(fp_neg(arm.y), arm.x); -var at_contact: vec2 = v2_add(vel, v2_mul(rotational, vec2(wv))); -var normal_speed: f32 = dot(at_contact, n); -var tangent_speed: f32 = dot(at_contact, t); -var biased: f32 = dot(v2_add(bv, v2_mul(rotational, vec2(bw))), n); -var bias_impulse: f32 = fp_mul(fp_sub(sim_car.contacts[s_active[k]].bias, biased), sim_car.contacts[s_active[k]].normal_mass); -var previous_bias: f32 = sim_car.contacts[s_active[k]].acc_bias; -sim_car.contacts[s_active[k]].acc_bias = rmax(fp_add(previous_bias, bias_impulse), bitcast(0x00000000u)); -var impulse: vec2 = v2_mul(n, vec2(fp_sub(sim_car.contacts[s_active[k]].acc_bias, previous_bias))); -bv = v2_add(bv, v2_mul(impulse, vec2(inv_mass))); -bw = rclamp(fp_add(bw, fp_mul(cross(impulse_arm, impulse), inv_inertia)), fp_neg(bias_limit), bias_limit); -biased = dot(v2_add(bv, v2_mul(rotational, vec2(bw))), n); -if ((fp_abs(fp_sub(sim_car.contacts[s_active[k]].bias, biased)) > bitcast(0x3a83126fu))) { -var center_impulse: f32 = fp_div(fp_sub(sim_car.contacts[s_active[k]].bias, biased), inv_mass); -var previous_center: f32 = sim_car.contacts[s_active[k]].acc_bias_center; -sim_car.contacts[s_active[k]].acc_bias_center = rmax(fp_add(previous_center, center_impulse), bitcast(0x00000000u)); -bv = v2_add(bv, v2_mul(v2_mul(n, vec2(fp_sub(sim_car.contacts[s_active[k]].acc_bias_center, previous_center))), vec2(inv_mass))); -} - -var normal_impulse: f32 = fp_mul(fp_neg(fp_add(normal_speed, sim_car.contacts[s_active[k]].bounce)), sim_car.contacts[s_active[k]].normal_mass); -var previous_normal: f32 = sim_car.contacts[s_active[k]].acc_normal; -sim_car.contacts[s_active[k]].acc_normal = rmax(fp_add(previous_normal, normal_impulse), bitcast(0x00000000u)); -var friction_limit: f32 = fp_mul(sim_car.contacts[s_active[k]].friction, sim_car.contacts[s_active[k]].acc_normal); -var tangent_impulse: f32 = fp_mul(fp_neg(tangent_speed), sim_car.contacts[s_active[k]].tangent_mass); -var previous_tangent: f32 = sim_car.contacts[s_active[k]].acc_tangent; -sim_car.contacts[s_active[k]].acc_tangent = rclamp(fp_add(previous_tangent, tangent_impulse), fp_neg(friction_limit), friction_limit); -impulse = v2_add(v2_mul(n, vec2(fp_sub(sim_car.contacts[s_active[k]].acc_normal, previous_normal))), v2_mul(t, vec2(fp_sub(sim_car.contacts[s_active[k]].acc_tangent, previous_tangent)))); -vel = v2_add(vel, v2_mul(impulse, vec2(inv_mass))); -wv = fp_add(wv, fp_mul(cross(impulse_arm, impulse), inv_inertia)); -} - -continuing { -k = (k + 1u); -} -} -} -} - -continuing { -iteration = (iteration + 1u); -} -} -} -return SolveResult(vel, bv, wv, bw, (sim_vehicle.max_contacts_reported > u32(0i))); - -} - -fn step_begin(raw_arg: array, drive_arg: bool) -> StepCarry { -var raw = raw_arg; -var drive = drive_arg; - -var err: u32 = u32(0i); -var was_active: bool = ((sim_car.flags & CAR_ACTIVE) != u32(0)); -var control: Controls = normalized_controls(raw); -var impulse: vec2 = vec2(bitcast(0x00000000u), bitcast(0x00000000u)); -var torque: f32 = bitcast(0x00000000u); -var old_velocity: vec2 = sim_car.velocity; -var driving: bool = (was_active && drive); -if (driving) { -var boost: f32 = f64_to_f32(control.boost); -var energy: f32 = sim_car.boost_energy; -var strength: f32 = bitcast(0x00000000u); -if (((boost > bitcast(0x00000000u)) && (energy > bitcast(0x00000000u)))) { -energy = rmax(fp_sub(energy, fp_mul(boost, bitcast(0x3c23d70au))), bitcast(0x00000000u)); -strength = fp_mul(boost, bitcast(0x3f000000u)); -} - else { -if ((boost <= bitcast(0x00000000u))) { -energy = rmin(fp_add(energy, bitcast(0x3b03126fu)), bitcast(0x3f800000u)); -} - -} -sim_car.boost_energy = energy; -var drive_amount: f32 = f64_to_f32(f64_mul(control.acceleration, select(F64(0x00000000u, 0x3ff00000u), f64_add(F64(0x00000000u, 0x3ff00000u), f64_from_f32(strength)), f64_gt(control.acceleration, F64(0x00000000u, 0x00000000u))))); -var steering_multiplier: F64 = f64_div(F64(0x00000000u, 0x3ff00000u), f64_add(f64_mul(F64(0xd2f1a9fcu, 0x3f60624du), f64_from_f32(length(sim_car.velocity))), F64(0x00000000u, 0x3ff00000u))); -var body_x: vec2 = sim_car.basis_x; -var body_y: vec2 = sim_car.basis_y; -var ease: array; -var eased: array = array((0i != i32(0)), (0i != i32(0))); -{ -var i: u32 = u32(0i); -loop { - -if (!((i < sim_vehicle.wheel_count))) { break; } -{ -var spec: WheelDesc = sim_vehicle.wheels[i]; -var position: vec2 = transform_point(sim_car.position, body_x, body_y, spec.position); -var offset: vec2 = v2_sub(position, sim_car.position); -var wheel_velocity: vec2 = v2_sub(position, select(position, sim_car.wheel_previous[i], (((sim_car.wheel_has_previous >> i) & u32(1i)) != u32(0)))); -sim_car.wheel_previous[i] = position; -sim_car.wheel_has_previous = (sim_car.wheel_has_previous | (1u << i)); -var angle: f32 = fp_mul(sim_car.wheel_angle[i], fp_div(PI_F, bitcast(0x43340000u))); -var s: f32; -var c: f32; -managed_sin_cos(angle, &(s), &(c), &(err)); -var forward: vec2 = -(normalized(v2_add(v2_mul(body_x, vec2(fp_neg(s))), v2_mul(body_y, vec2(c))))); -var right: vec2 = normalized(v2_add(v2_mul(body_x, vec2(c)), v2_mul(body_y, vec2(s)))); -var extra: f32 = fp_mul(angle, fp_sub(load_SurfaceDev(sim_world.surfaces + (0u + sim_car.wheel_surface[i]) * 4u).steering, bitcast(0x3f800000u))); -if ((extra != bitcast(0x00000000u))) { -forward = rotated(forward, extra, &(err)); -right = rotated(right, extra, &(err)); -} - -var surface_index: u32 = vehicle_surface(position); -var surface: SurfaceDev = load_SurfaceDev(sim_world.surfaces + (0u + surface_index) * 4u); -sim_car.wheel_surface[i] = surface_index; -var direction: vec2 = normalized(wheel_velocity); -var braking: vec2 = limit(v2_add(v2_mul(-(direction), vec2(fp_mul(f64_to_f32(control.handbrake), spec.handbrake_power))), v2_mul(-(direction), vec2(fp_mul(f64_to_f32(control.brake), spec.brake_power)))), spec.brake_max); -var drive_force: vec2 = v2_mul(forward, vec2(fp_mul(drive_amount, spec.power))); -var wheel_drive: vec2 = v2_mul(combine(braking, drive_force, spec.brake_max, spec.power), vec2(surface.power)); -impulse = v2_add(impulse, wheel_drive); -torque = fp_add(torque, cross(offset, wheel_drive)); -var lateral_speed: f32 = dot(wheel_velocity, right); -var off: u32 = u32(select(0i, 1i, (spec.handbrake_off != u32(0)))); -if (!(eased[off])) { -ease[off] = godot_ease(sim_world.math_profile, select(control.handbrake, F64(0x00000000u, 0x00000000u), (off != u32(0))), F64(0x33333333u, 0x3fd33333u)); -eased[off] = (1i != i32(0)); -} - -var lateral_grip: F64 = f64_add(F64(0xa0000000u, 0x3fc99999u), f64_div(f64_add(F64(0x9999999au, 0x3fe99999u), f64_mul(f64_sub(F64(0x9999999au, 0x3fb99999u), F64(0x9999999au, 0x3fe99999u)), ease[off])), f64_add(F64(0x00000000u, 0x3ff00000u), profile_exp(sim_world.math_profile, f64_mul(f64_sub(f64_from_f32(length(wheel_velocity)), F64(0x00000000u, 0x407c2000u)), F64(0xe0000000u, 0x3f80624du)))))); -var effective_grip: f32 = f64_to_f32(f64_mul(f64_from_f32(sim_vehicle.grip), lateral_grip)); -var lateral: vec2 = v2_mul(v2_mul(right, vec2(fp_mul(fp_neg(effective_grip), lateral_speed))), vec2(surface.grip)); -impulse = v2_add(impulse, lateral); -torque = fp_add(torque, cross(offset, lateral)); -if ((spec.steering != u32(0))) { -var current: F64 = f64_from_f32(sim_car.wheel_angle[i]); -var s_target: F64 = f64_mul(f64_mul(steering_multiplier, control.steering), spec.max_angle_deg); -if ((f64_eq(control.steering, F64(0x00000000u, 0x00000000u)) && !((sim_vehicle.center_steering != u32(0))))) { -s_target = current; -} -sim_car.wheel_angle[i] = f64_to_f32(f64_add(current, f64_mul(f64_sub(s_target, current), f64_mul(sim_vehicle.steering_speed, steering_multiplier)))); -} - -} - -continuing { -i = (i + 1u); -} -} -} -impulse = v2_add(impulse, v2_mul(v2_mul(old_velocity, vec2(bitcast(0x3ba3d70au))), vec2(fp_neg(sim_vehicle.air_resistance)))); -} - -var velocity: vec2 = v2_add(old_velocity, v2_mul(impulse, vec2(fp_div(bitcast(0x3f800000u), sim_vehicle.mass)))); -if ((driving && f64_gt(f64_from_f32(length(old_velocity)), sim_vehicle.max_velocity))) { -velocity = v2_mul(normalized(old_velocity), vec2(sim_vehicle.max_velocity_f)); -} -var previous_velocity: vec2 = velocity; -var previous_angular: f32 = fp_add(sim_car.angular_velocity, fp_mul(torque, fp_div(bitcast(0x3f800000u), sim_vehicle.inertia))); -var angular: f32 = previous_angular; -if (!((sim_vehicle.custom_integrator != u32(0)))) { -var zero: vec2 = vec2(bitcast(0x00000000u), bitcast(0x00000000u)); -velocity = v2_add(v2_mul(velocity, vec2(rmax(fp_sub(bitcast(0x3f800000u), fp_mul(sim_vehicle.linear_damp, sim_vehicle.dt_f)), bitcast(0x00000000u)))), v2_mul(v2_mul(v2_add(v2_add(v2_mul(sim_vehicle.gravity, vec2(sim_vehicle.mass)), zero), zero), vec2(fp_div(bitcast(0x3f800000u), sim_vehicle.mass))), vec2(sim_vehicle.dt_f))); -angular = fp_mul(previous_angular, rmax(fp_sub(bitcast(0x3f800000u), fp_mul(sim_vehicle.angular_damp, sim_vehicle.dt_f)), bitcast(0x00000000u))); -} - -sim_car.error = (sim_car.error | err); -prepare_contacts(); -return StepCarry(old_velocity, velocity, previous_velocity, angular, previous_angular, u32(was_active), u32(0i)); - -} - -fn step_end(k_arg: StepCarry, drive_arg: bool, eliminate_on_wall_arg: bool) -> bool { -var k = k_arg; -var drive = drive_arg; -var eliminate_on_wall = eliminate_on_wall_arg; - -var err: u32 = u32(0i); -var f: CarFrame; -var framed: bool = (0i != i32(0)); -var made_contacts: u32 = u32(0i); -{ -var ci: u32 = u32(0i); -loop { - -if (!((ci < sim_car.pair_count))) { break; } -{ -var push: vec2 = sat_pair(ci); -if (is_zero(push)) { -continue; -} -if (!(framed)) { -f = car_frame(); -framed = (1i != i32(0)); -} - -var made: u32 = pair_contacts(f, ci, push); -made_contacts = (made_contacts | select(0u, (1u << ci), (made != u32(0)))); -} - -continuing { -ci = (ci + 1u); -} -} -} -var r: SolveResult = solve_contacts(made_contacts, k.velocity, k.angular, k.previous_velocity, k.previous_angular); -sim_car.velocity = r.velocity; -sim_car.angular_velocity = r.angular; -sim_car.position = vec2(fp_add(sim_car.position.x, fp_mul(fp_add(r.velocity.x, r.bias_velocity.x), sim_vehicle.dt_f)), fp_add(sim_car.position.y, fp_mul(fp_add(r.velocity.y, r.bias_velocity.y), sim_vehicle.dt_f))); -var angle_delta: f32 = fp_mul(fp_add(r.angular, r.bias_angular), sim_vehicle.dt_f); -var center: vec2 = v2_add(v2_mul(sim_car.basis_x, vec2(sim_vehicle.center_of_mass.x)), v2_mul(sim_car.basis_y, vec2(sim_vehicle.center_of_mass.y))); -if (f64_gt(f64_from_f32(dot(center, center)), f64_mul(F64(0x88e368f1u, 0x3ee4f8b5u), F64(0x88e368f1u, 0x3ee4f8b5u)))) { -var s: f32; -var c: f32; -engine_sin_cos(angle_delta, &(s), &(c), &(err)); -var turned: vec2 = vec2(fp_sub(fp_mul(center.x, c), fp_mul(center.y, s)), fp_add(fp_mul(center.x, s), fp_mul(center.y, c))); -sim_car.position = v2_add(sim_car.position, v2_sub(center, turned)); -} - -set_transform_angle(sim_world.math_profile, fp_add(sim_car.rotation, angle_delta), &(err)); -sim_car.tick = u64_add(sim_car.tick, i64_from_i32(select(0i, 1i, drive))); -update_shape(); -var pending_velocity: bool = ((sim_car.flags & CAR_PENDING_VELOCITY) != u32(0)); -var report: bool = ((r.collided && (k.was_active != u32(0))) && !(pending_velocity)); -if (report) { -sim_car.collision_count = (sim_car.collision_count + 1u); -if (eliminate_on_wall) { -sim_car.flags = (sim_car.flags & ~(CAR_ACTIVE)); -sim_car.reset_acceleration = sim_car.acceleration; -pending_velocity = (1i != i32(0)); -} - -} - -if (pending_velocity) { -sim_car.velocity = vec2(bitcast(0x00000000u), bitcast(0x00000000u)); -sim_car.angular_velocity = bitcast(0x00000000u); -} - -sim_car.flags = (sim_car.flags & ~((CAR_PENDING_VELOCITY | CAR_RESET_RIGHT))); -sim_car.acceleration = v2_sub(sim_car.velocity, k.old_velocity); -sim_car.error = (sim_car.error | err); -return report; - -} - -fn car_step(raw_arg: array, drive_arg: bool, eliminate_on_wall_arg: bool) -> bool { -var raw = raw_arg; -var drive = drive_arg; -var eliminate_on_wall = eliminate_on_wall_arg; - -var k: StepCarry = step_begin(raw, drive); -return step_end(k, drive, eliminate_on_wall); - -} - -fn path_length() -> F64 { - - -return select(F64(0x00000000u, 0x00000000u), U64(world_data[sim_world.path_offsets + (0u + (sim_world.path_points - u32(1i))) * 2u], world_data[sim_world.path_offsets + (0u + (sim_world.path_points - u32(1i))) * 2u + 1u]), (sim_world.path_points != u32(0))); - -} - -fn path_position(offset_arg: F64, err: ptr) -> vec2 { -var offset = offset_arg; - -if ((sim_world.curve_points != u32(0))) { -return curve_position(f64_to_f32(offset), err); -} -offset = py_clamp(offset, F64(0x00000000u, 0x00000000u), path_length()); -var lo: u32 = u32(0i); -var hi: u32 = sim_world.path_points; -loop { - -if (!((lo < hi))) { break; } -{ -var mid: u32 = ((lo + hi) / u32(2i)); -if (f64_le(U64(world_data[sim_world.path_offsets + (0u + mid) * 2u], world_data[sim_world.path_offsets + (0u + mid) * 2u + 1u]), offset)) { -lo = (mid + u32(1i)); -} else { -hi = mid; -} -} - -} -var index: u32 = min((sim_world.path_points - u32(2i)), select(u32(0i), (lo - u32(1i)), (lo != u32(0)))); -var start: F64 = U64(world_data[sim_world.path_offsets + (0u + index) * 2u], world_data[sim_world.path_offsets + (0u + index) * 2u + 1u]); -var end: F64 = U64(world_data[sim_world.path_offsets + (0u + (index + u32(1i))) * 2u], world_data[sim_world.path_offsets + (0u + (index + u32(1i))) * 2u + 1u]); -var fraction: F64 = select(F64(0x00000000u, 0x00000000u), f64_div(f64_sub(offset, start), f64_sub(end, start)), f64_gt(end, start)); - -return vec2(f64_to_f32(f64_add(U64(world_data[sim_world.path_xy + ((0u + (u32(2i) * index)) + u32(0i)) * 2u], world_data[sim_world.path_xy + ((0u + (u32(2i) * index)) + u32(0i)) * 2u + 1u]), f64_mul(f64_sub(U64(world_data[sim_world.path_xy + ((0u + (u32(2i) * index)) + u32(2i)) * 2u], world_data[sim_world.path_xy + ((0u + (u32(2i) * index)) + u32(2i)) * 2u + 1u]), U64(world_data[sim_world.path_xy + ((0u + (u32(2i) * index)) + u32(0i)) * 2u], world_data[sim_world.path_xy + ((0u + (u32(2i) * index)) + u32(0i)) * 2u + 1u])), fraction))), f64_to_f32(f64_add(U64(world_data[sim_world.path_xy + ((0u + (u32(2i) * index)) + u32(1i)) * 2u], world_data[sim_world.path_xy + ((0u + (u32(2i) * index)) + u32(1i)) * 2u + 1u]), f64_mul(f64_sub(U64(world_data[sim_world.path_xy + ((0u + (u32(2i) * index)) + u32(3i)) * 2u], world_data[sim_world.path_xy + ((0u + (u32(2i) * index)) + u32(3i)) * 2u + 1u]), U64(world_data[sim_world.path_xy + ((0u + (u32(2i) * index)) + u32(1i)) * 2u], world_data[sim_world.path_xy + ((0u + (u32(2i) * index)) + u32(1i)) * 2u + 1u])), fraction)))); - -} - -fn score_update(position_arg: vec2, err: ptr) { -var position = position_arg; - -sim_agent.flags = (sim_agent.flags & ~(AGENT_HAS_LAP_CROSSING)); -sim_agent.lap_crossing_fraction = F64(0x00000000u, 0x00000000u); -var length: F64 = path_length(); -if (f64_le(length, F64(0x00000000u, 0x00000000u))) { -return; -} -if (!(((sim_agent.flags & AGENT_HAS_PREVIOUS_OFFSET) != u32(0)))) { -var offset: F64 = closest_path(position, (0i != i32(0)), F64(0x00000000u, 0x00000000u), err).offset; -sim_agent.tracker_score = f64_add(sim_agent.tracker_score, f64_mul(select(offset, f64_sub(offset, length), f64_gt(offset, f64_div(length, F64(0x00000000u, 0x40000000u)))), f64_div(F64(0x00000000u, 0x40140000u), F64(0x00000000u, 0x40780000u)))); -sim_agent.flags = select((sim_agent.flags & ~(AGENT_WENT_BACKWARDS)), (sim_agent.flags | AGENT_WENT_BACKWARDS), f64_gt(offset, f64_div(length, F64(0x00000000u, 0x40000000u)))); -sim_agent.previous_offset = offset; -sim_agent.flags = (sim_agent.flags | AGENT_HAS_PREVIOUS_OFFSET); -return; -} - -var previous: F64 = sim_agent.previous_offset; -var offset: F64 = closest_path(position, (1i != i32(0)), previous, err).offset; -var change: F64 = f64_sub(offset, previous); -if (f64_gt(change, f64_div(length, F64(0x00000000u, 0x40000000u)))) { -change = f64_sub(change, length); -} else { -if (f64_lt(change, f64_div(f64_neg(length), F64(0x00000000u, 0x40000000u)))) { -change = f64_add(change, length); -} -} -sim_agent.tracker_score = f64_add(sim_agent.tracker_score, f64_mul(change, f64_div(F64(0x00000000u, 0x40140000u), F64(0x00000000u, 0x40780000u)))); -var crossing: F64 = f64_add(previous, change); -if (f64_lt(crossing, F64(0x00000000u, 0x00000000u))) { -sim_agent.flags = (sim_agent.flags | AGENT_WENT_BACKWARDS); -} - else { -if (f64_ge(crossing, length)) { -if (!(((sim_agent.flags & AGENT_WENT_BACKWARDS) != u32(0)))) { -sim_agent.lap_count = u64_add(sim_agent.lap_count, U64(1u,0u)); -sim_agent.lap_crossing_fraction = f64_div(f64_sub(length, previous), f64_add(f64_sub(length, previous), offset)); -sim_agent.flags = (sim_agent.flags | AGENT_HAS_LAP_CROSSING); -} - -sim_agent.flags = (sim_agent.flags & ~(AGENT_WENT_BACKWARDS)); -} - -} -sim_agent.previous_offset = offset; - -} - -fn stats_update(tick_arg: U64, err: ptr) { -var tick = tick_arg; - -if (((sim_agent.flags & AGENT_PENDING_CONTACT) != u32(0))) { -sim_agent.collision_count = u64_add(sim_agent.collision_count, U64(1u,0u)); -} -if (!(((sim_car.flags & CAR_ACTIVE) != u32(0)))) { -return; -} -sim_agent.update_count = u64_add(sim_agent.update_count, U64(1u,0u)); -var speed: F64 = f64_from_f32(length(sim_car.velocity)); -var initializing: bool = !(((sim_agent.flags & AGENT_HAS_PREVIOUS_OFFSET) != u32(0))); -score_update(sim_car.position, err); -var score: F64 = sim_agent.tracker_score; -var diff: F64 = select(f64_sub(score, sim_agent.previous_score), F64(0x00000000u, 0x00000000u), initializing); -if ((sim_agent.recent_len == u32(RECENT))) { -sim_agent.recent[sim_agent.recent_start] = diff; -sim_agent.recent_start = ((sim_agent.recent_start + u32(1i)) % u32(RECENT)); -} - else { -sim_agent.recent[((sim_agent.recent_start + sim_agent.recent_len) % u32(RECENT))] = diff; -sim_agent.recent_len = (sim_agent.recent_len + 1u); -} - -var sum: F64 = F64(0x00000000u, 0x00000000u); -{ -var k: u32 = u32(0i); -loop { - -if (!((k < sim_agent.recent_len))) { break; } -sum = f64_add(sum, sim_agent.recent[((sim_agent.recent_start + k) % u32(RECENT))]); -continuing { -k = (k + 1u); -} -} -} -sim_agent.idle_ticks = select(i64_from_i32(0i), u64_add(sim_agent.idle_ticks, i64_from_i32(1i)), f64_lt(f64_div(sum, f64_from_u32(sim_agent.recent_len)), F64(0x9999999au, 0x3fb99999u))); -sim_agent.total_score = score; -sim_agent.previous_score = score; -sim_agent.total_speed = f64_add(sim_agent.total_speed, speed); -sim_agent.total_abs_steering = f64_add(sim_agent.total_abs_steering, f64_abs(py_clamp(sim_agent.controls[u32(1i)], f64_neg(F64(0x00000000u, 0x3ff00000u)), F64(0x00000000u, 0x3ff00000u)))); -sim_agent.total_actual_distance = f64_add(sim_agent.total_actual_distance, f64_mul(speed, F64(0x9999999au, 0x3fb99999u))); -sim_agent.total_momentum_change_frequency = f64_add(sim_agent.total_momentum_change_frequency, f64_abs(f64_sub(speed, sim_agent.previous_speed))); -var throttle: F64 = py_clamp(sim_agent.controls[u32(0i)], f64_neg(F64(0x00000000u, 0x3ff00000u)), F64(0x00000000u, 0x3ff00000u)); -sim_agent.total_throttle_change_frequency = f64_add(sim_agent.total_throttle_change_frequency, f64_abs(f64_sub(throttle, sim_agent.previous_throttle))); -if ((f64_gt(sim_agent.controls[u32(2i)], F64(0xeb1c432du, 0x3f1a36e2u)) || f64_gt(sim_agent.controls[u32(3i)], F64(0xeb1c432du, 0x3f1a36e2u)))) { -sim_agent.total_braking_frequency = f64_add(sim_agent.total_braking_frequency, F64(0x9999999au, 0x3fb99999u)); -} -sim_agent.previous_speed = speed; -sim_agent.previous_throttle = throttle; -var wall: vec2 = closest_wall_point(vec2(sim_car.position.x, sim_car.position.y)); -sim_agent.total_distance_from_wall = f64_add(sim_agent.total_distance_from_wall, f64_from_f32(length(v2_sub(vec2(wall.x, wall.y), sim_car.position)))); -if ((sim_world.path_points >= u32(2i))) { -var center: vec2 = path_position(select(F64(0x00000000u, 0x00000000u), sim_agent.previous_offset, ((sim_agent.flags & AGENT_HAS_PREVIOUS_OFFSET) != u32(0))), err); -sim_agent.total_distance_from_center = f64_add(sim_agent.total_distance_from_center, f64_from_f32(length(v2_sub(center, sim_car.position)))); -} - -var right: vec2 = normalized(sim_car.basis_x); -var front: f32 = dot(sim_car.velocity, rotated(right, fp_neg(bitcast(0x3fc90fdbu)), err)); -var side: f32 = dot(sim_car.velocity, right); -sim_agent.current_slip_angle_degrees = f64_from_f32(fp_mul(profile_atan2(sim_world.math_profile, side, front), bitcast(0x42652ee1u))); -var slip: F64 = f64_abs(sim_agent.current_slip_angle_degrees); -sim_agent.drift_ticks = select(i64_from_i32(0i), u64_add(sim_agent.drift_ticks, i64_from_i32(1i)), ((f64_gt(speed, F64(0x00000000u, 0x40490000u)) && f64_ge(slip, F64(0x00000000u, 0x402e0000u))) && f64_le(slip, F64(0x00000000u, 0x404e0000u)))); -if (u64_ge(sim_agent.drift_ticks, i64_from_i32(3i))) { -sim_agent.total_drifting_amount = f64_add(sim_agent.total_drifting_amount, F64(0x9999999au, 0x3fb99999u)); -sim_agent.continuous_drift_time = f64_add(sim_agent.continuous_drift_time, F64(0x9999999au, 0x3fb99999u)); -sim_agent.max_continuous_drift_time = f64_max(sim_agent.max_continuous_drift_time, sim_agent.continuous_drift_time); -} - -if (u64_eq(sim_agent.drift_ticks, i64_from_i32(0i))) { -sim_agent.continuous_drift_time = F64(0x00000000u, 0x00000000u); -} -if (u64_gt(sim_agent.lap_count, sim_agent.previous_lap_count)) { -var fraction: F64 = select(F64(0x00000000u, 0x3ff00000u), sim_agent.lap_crossing_fraction, ((sim_agent.flags & AGENT_HAS_LAP_CROSSING) != u32(0))); -fraction = select(select(fraction, F64(0x00000000u, 0x3ff00000u), f64_gt(fraction, F64(0x00000000u, 0x3ff00000u))), F64(0x00000000u, 0x00000000u), f64_lt(fraction, F64(0x00000000u, 0x00000000u))); -var elapsed: F64 = f64_sub(f64_mul(f64_from_u64(u64_div_small(tick, u32(6i))), F64(0x9999999au, 0x3fb99999u)), f64_mul(f64_from_u64(u64_div_small(sim_agent.lap_start_tick, u32(6i))), F64(0x9999999au, 0x3fb99999u))); -var lap_time: F64 = f64_div(f64_rint(f64_mul(f64_add(f64_sub(elapsed, F64(0x9999999au, 0x3fb99999u)), f64_mul(fraction, F64(0x9999999au, 0x3fb99999u))), F64(0x00000000u, 0x408f4000u))), F64(0x00000000u, 0x408f4000u)); -var current: F64 = select(lap_time, sim_agent.best_lap_time, ((sim_agent.flags & AGENT_HAS_BEST_LAP) != u32(0))); -sim_agent.best_lap_time = py_min(current, lap_time); -sim_agent.all_laps_time_sum = f64_add(select(F64(0x00000000u, 0x00000000u), sim_agent.all_laps_time_sum, ((sim_agent.flags & AGENT_HAS_ALL_LAPS) != u32(0))), lap_time); -sim_agent.flags = (sim_agent.flags | (AGENT_HAS_BEST_LAP | AGENT_HAS_ALL_LAPS)); -sim_agent.lap_start_tick = tick; -sim_agent.previous_lap_count = sim_agent.lap_count; -} - - -} - -fn car_deactivate() { - - -sim_car.flags = ((sim_car.flags & ~(CAR_ACTIVE)) | CAR_PENDING_VELOCITY); -sim_car.reset_acceleration = sim_car.acceleration; - -} - -fn update_stats(tick_arg: U64, stats_tick_arg: U64, eliminate_when_idle_arg: bool, err: ptr) { -var tick = tick_arg; -var stats_tick = stats_tick_arg; -var eliminate_when_idle = eliminate_when_idle_arg; - -var was_active: bool = ((sim_car.flags & CAR_ACTIVE) != u32(0)); -stats_update(stats_tick, err); -sim_agent.flags = (sim_agent.flags & ~(AGENT_PENDING_CONTACT)); -if (((((sim_car.flags & CAR_ACTIVE) != u32(0)) && eliminate_when_idle) && u64_gt(sim_agent.idle_ticks, i64_from_i32(80i)))) { -car_deactivate(); -} -if ((was_active && !(((sim_car.flags & CAR_ACTIVE) != u32(0))))) { -sim_agent.deactivated_at = tick; -sim_agent.flags = (sim_agent.flags | AGENT_HAS_DEACTIVATED); -} - - -} \ No newline at end of file diff --git a/github-simulator/source/src/wasm/sim/helpers.wgsl b/github-simulator/source/src/wasm/sim/helpers.wgsl deleted file mode 100644 index 5b97615c6c2514f617884be79d643abc8fba0ead..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/wasm/sim/helpers.wgsl +++ /dev/null @@ -1,102 +0,0 @@ -struct SimParams { - population: u32, ticks: u32, start_tick: u32, start_batch: u32, - batches_per_tick: u32, stats_phase: u32, stop_inactive: u32, eliminate_wall: u32, - eliminate_idle: u32, has_limit: u32, limit: F64, - world_offset: u32, vehicle_offset: u32, sensors_offset: u32, sensor_count: u32, - layers: u32, stride: u32, shape_offset: u32, control_offset: u32, - needs_path: u32, pad: u32, reserved_a: u32, reserved_b: u32, -} -@group(0) @binding(0) var cars: array; -@group(0) @binding(1) var agents: array; -@group(0) @binding(2) var world_data: array; -@group(0) @binding(3) var networks: array>; -@group(0) @binding(4) var results: array; -@group(0) @binding(5) var params: SimParams; -fn f64_ne(a: F64,b: F64) -> bool { return !f64_eq(a,b); } -fn f64_max(a: F64,b: F64) -> F64 { return select(a,b,f64_lt(a,b) || f64_isnan(a)); } -fn u64_ne(a: U64,b: U64) -> bool { return !u64_eq(a,b); } -fn u64_gt(a: U64,b: U64) -> bool { return u64_lt(b,a); } -fn u64_ge(a: U64,b: U64) -> bool { return u64_le(b,a); } -fn i64_gt(a: U64,b: U64) -> bool { return i64_lt(b,a); } -fn i64_le(a: U64,b: U64) -> bool { return !i64_lt(b,a); } -fn i64_ge(a: U64,b: U64) -> bool { return !i64_lt(a,b); } -fn i64_shr(a: U64,n: u32) -> U64 { - if (n == 0u) { return a; } - let sign = select(0u,0xffffffffu,i64_is_negative(a)); - if (n >= 32u) { return U64(u32(i32(a.y) >> (n-32u)),sign); } - return U64((a.x >> n) | (a.y << (32u-n)),u32(i32(a.y) >> n)); -} -fn u64_mul_low(a: U64,b: U64) -> U64 { return u64_mul(a,b); } -fn u64_mod_small(a: U64,b: u32) -> U64 { return u64_sub(a,u64_mul(u64_div_small(a,b),U64(b,0u))); } -fn fp_isnan(a: f32) -> bool { return (bitcast(a) & 0x7fffffffu) > 0x7f800000u; } -fn fp_isinf(a: f32) -> bool { return (bitcast(a) & 0x7fffffffu) == 0x7f800000u; } -fn fp_isfinite(a: f32) -> bool { return (bitcast(a) & 0x7f800000u) != 0x7f800000u; } -fn fp_signbit(a: f32) -> bool { return (bitcast(a) & 0x80000000u) != 0u; } -fn fp_copysign(a: f32,b: f32) -> f32 { return bitcast((bitcast(a) & 0x7fffffffu) | (bitcast(b) & 0x80000000u)); } -// x86 NaN propagation: the first NaN operand, quieted. altd_invalid is the -// default NaN 0xffc00000 for non-NaN operands. -fn fp_nan_operand(a: f32,b: f32) -> f32 { return bitcast(select(bitcast(b),bitcast(a),fp_isnan(a)) | 0x00400000u); } -fn fp_invalid(a: f32) -> f32 { return bitcast(select(0xffc00000u,bitcast(a) | 0x00400000u,fp_isnan(a))); } -fn f64_nan_operand(a: F64,b: F64) -> F64 { let r=select(b,a,f64_isnan(a)); return F64(r.x,r.y | 0x00080000u); } -fn f64_invalid(a: F64) -> F64 { return select(F64(0u,0xfff80000u),F64(a.x,a.y | 0x00080000u),f64_isnan(a)); } -fn fp_mod(a: f32,b: f32) -> f32 { - let ad=f64_from_f32(a); let bd=f64_from_f32(b); - return f64_to_f32(f64_sub(ad,f64_mul(f64_trunc(f64_div(ad,bd)),bd))); -} -// Integer square root of a normalized binary32 significand, rounded once. -// Positive normal inputs take this branch-free path; the rest, and devices -// whose estimate is off, fp_sqrt_general. The root stays below 2^24, and the -// exponent field is added to it with its leading bit. -fn fp_sqrt(a: f32) -> f32 { - let bits=bitcast(a); - if (bits-0x800000u >= 0x7f000000u) { return fp_sqrt_general(a); } - let exponent=i32(bits >> 23u)-127; - let radicand=u64_shl(U64((bits & 0x7fffffu) | 0x800000u,0u),23u+u32(exponent & 1)); - let wide=f32(radicand.y)*4294967296.0+f32(radicand.x); - var root=u32(sqrt(wide)); - let off=fp_sub(fp_mul(f32(root),f32(root)),wide); - var remainder=i32(radicand.x-root*root); - for (var i=0u; i<2u; i=i+1u) { let low=remainder<0; root=select(root,root-1u,low); remainder=select(remainder,remainder+i32(2u*root+1u),low); } - for (var i=0u; i<2u; i=i+1u) { let high=remainder>i32(2u*root); remainder=select(remainder,remainder-i32(2u*root+1u),high); root=select(root,root+1u,high); } - if (!(abs(off)<1073741824.0) || remainder<0 || u32(remainder)>2u*root) { return fp_sqrt_general(a); } - root=root+select(0u,1u,u32(remainder)>root); - return bitcast((u32((exponent >> 1u)+126) << 23u)+root); -} -fn fp_sqrt_general(a: f32) -> f32 { - let bits=bitcast(a); let mag=bits & 0x7fffffffu; - if (mag == 0u || bits == 0x7f800000u) { return a; } - if ((bits & 0x80000000u) != 0u || mag > 0x7f800000u) { return bitcast(0x7fc00000u ^ params.pad); } - var exponent=i32(mag >> 23u)-127; - var sig=(mag & 0x7fffffu) | 0x800000u; - if (mag < 0x800000u) { - let shift=clz32(mag)-8u; - sig=mag << shift; exponent=-126-i32(shift); - } - let radicand=u64_shl(U64(sig,0u),23u+u32(exponent & 1)); - // root=floor(sqrt(radicand)) from the hardware estimate, corrected as in - // fp_div: the remainder is exact modulo 2^32 while the correctly rounded - // square (exact radicand, 24 significant bits) bounds it within an i32, and - // a device that is further off than the steps reach takes the bit loop. - let wide=f32(radicand.y)*4294967296.0+f32(radicand.x); - var root=u32(sqrt(wide)); - let off=fp_sub(fp_mul(f32(root),f32(root)),wide); - var remainder=i32(radicand.x-root*root); - for (var i=0u; i<2u; i=i+1u) { let low=remainder<0; root=select(root,root-1u,low); remainder=select(remainder,remainder+i32(2u*root+1u),low); } - for (var i=0u; i<2u; i=i+1u) { let high=remainder>i32(2u*root); remainder=select(remainder,remainder-i32(2u*root+1u),high); root=select(root,root+1u,high); } - if (!(abs(off)<1073741824.0) || remainder<0 || u32(remainder)>2u*root) { - root=0u; - for (var bit=0x800000u; bit != 0u; bit=bit >> 1u) { - let probe=root | bit; - if (u64_le(mul32(probe,probe),radicand)) { root=probe; } - } - remainder=i32(u64_sub(radicand,mul32(root,root)).x); - } - if (u32(remainder)>root) { root=root+1u; } - var out_exp=(exponent >> 1u)+127; - if (root == 0x1000000u) { root=root >> 1u; out_exp=out_exp+1; } - return bitcast((u32(out_exp) << 23u) | (root & 0x7fffffu)); -} -fn v2_add(a: vec2,b: vec2) -> vec2 { return vec2(fp_add(a.x,b.x),fp_add(a.y,b.y)); } -fn v2_sub(a: vec2,b: vec2) -> vec2 { return vec2(fp_sub(a.x,b.x),fp_sub(a.y,b.y)); } -fn v2_mul(a: vec2,b: vec2) -> vec2 { return vec2(fp_mul(a.x,b.x),fp_mul(a.y,b.y)); } -fn v2_div(a: vec2,b: vec2) -> vec2 { return vec2(fp_div(a.x,b.x),fp_div(a.y,b.y)); } diff --git a/github-simulator/source/src/wasm/sim/mod.rs b/github-simulator/source/src/wasm/sim/mod.rs deleted file mode 100644 index 3bee102cdde053e67aa2a54f408b60c1b4a6ccb3..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/wasm/sim/mod.rs +++ /dev/null @@ -1,537 +0,0 @@ -//! Full WebGPU training windows. Evolution and checkpoint serialization stay -//! in Rust; sensing, inference, statistics and physics run entirely in WGSL. -use super::{js_error, Shared, Simulation}; -use crate::gpu::simulation::{self, words, GpuAgent, GpuCar, RayNode, TrackArrays}; -use js_sys::{Promise, Uint32Array}; -use std::{ - cell::{Cell, RefCell}, - rc::Rc, -}; -use wasm_bindgen::prelude::*; -use wasm_bindgen_futures::{future_to_promise, JsFuture}; -use web_sys::GpuDevice; -mod world; -struct BusyGuard(Rc); -impl Drop for BusyGuard { - fn drop(&mut self) { - self.0.busy.set(false); - } -} - -#[wasm_bindgen(module = "/src/wasm/sim/runtime.js")] -extern "C" { - #[wasm_bindgen(catch, js_name = createSimulator)] - async fn create_simulator( - device: &GpuDevice, - source: &str, - world: &Uint32Array, - base: &Uint32Array, - progress: Option, - ) -> Result; - #[wasm_bindgen(typescript_type = "object")] - type Backend; - #[wasm_bindgen(method)] - fn upload( - this: &Backend, - cars: &Uint32Array, - agents: &Uint32Array, - networks: &Uint32Array, - base: &Uint32Array, - ) -> Promise; - #[wasm_bindgen(method)] - fn advance(this: &Backend, window: &Uint32Array, reset: bool) -> Promise; - #[wasm_bindgen(method, catch, js_name = setWorld)] - fn set_world(this: &Backend, world: &Uint32Array) -> Result<(), JsValue>; - #[wasm_bindgen(method)] - fn destroy(this: &Backend); - #[wasm_bindgen(method, catch, js_name = setLoad)] - fn set_load(this: &Backend, load: f64) -> Result<(), JsValue>; -} -const SHADER: &str = concat!( - include_str!("f64.wgsl"), - "\n", - include_str!("helpers.wgsl"), - "\n", - include_str!("../float.wgsl"), - "\n", - include_str!("generated.wgsl"), - "\n", - include_str!("window.wgsl") -); -const MAX_WINDOW_TICKS: u32 = 120; - -// These mirrors contain only numbers and arrays of numbers. The explicit -// little-endian word buffers match the generated raw WGSL loaders. Numeric -// fields and initialized, explicit padding cover each copied type's layout. -const _: () = { - assert!(std::mem::size_of::() == 3200); - assert!(std::mem::size_of::() == 376); - assert!(std::mem::size_of::() == 336); - assert!(std::mem::size_of::() == 24); - assert!(std::mem::offset_of!(GpuCar, contacts) == 384); - assert!(std::mem::offset_of!(GpuAgent, flags) == 368); -}; -fn append(data: &mut Vec, values: &[T]) -> Result { - let offset = u32::try_from(data.len()).map_err(|_| "WebGPU world exceeds address range")?; - data.extend(words(values)); - Ok(offset) -} -unsafe fn append_ptr(data: &mut Vec, pointer: *const T, count: usize) -> Result { - if count == 0 { - return Ok(0); - } - // SAFETY: WorldDesc points into its owned TrackArrays for these counts. - append(data, unsafe { std::slice::from_raw_parts(pointer, count) }) -} - -struct Inner { - backend: Backend, - shared: Rc, - surfaces: RefCell, - /// The session track the world buffer encodes. - world: RefCell>, - base: RefCell>, - tag: Cell>, - population: Cell, - busy: Cell, - shape: Vec, -} -impl Drop for Inner { - fn drop(&mut self) { - self.backend.destroy(); - } -} - -#[wasm_bindgen] -pub struct GpuSimulation { - inner: Rc, -} -#[wasm_bindgen] -impl GpuSimulation { - #[wasm_bindgen(js_name = create)] - /// Compiles the simulator for `simulation` and uploads its state. - /// `progress`, if given, is called as `progress(done, total, kernel)` - /// before each of the compute pipelines compiles and once when all have. - pub async fn create( - device: GpuDevice, - simulation: &Simulation, - progress: Option, - ) -> Result { - if simulation.shared.busy.get() { - return Err(js_error("a GPU window is in flight")); - } - simulation.shared.busy.set(true); - let _guard = BusyGuard(simulation.shared.clone()); - let encoded = { - let session = simulation.shared.session.borrow(); - encode(&session).map_err(js_error)? - }; - let world = encoded.world; - let base = encoded.base; - let (surfaces, shape) = (encoded.surfaces, encoded.shape); - let track = simulation.shared.session.borrow().runner.world.clone(); - let backend = create_simulator( - &device, - &SHADER - .replace("i < 55u", "i < 55u + params.pad") - .replace("i < 23u", "i < 23u + params.pad"), - &Uint32Array::from(world.as_slice()), - &Uint32Array::from(base.as_slice()), - progress, - ) - .await - .map_err(|e| js_error(format!("WebGPU compilation: {e:?}")))? - .unchecked_into(); - let inner = Rc::new(Inner { - backend, - shared: simulation.shared.clone(), - surfaces: RefCell::new(surfaces), - world: RefCell::new(track), - base: RefCell::new(base), - tag: Cell::new(None), - population: Cell::new(0), - busy: Cell::new(false), - shape, - }); - inner.upload_if_needed().await.map_err(js_error)?; - Ok(Self { inner }) - } - /// Runs device-only ticks with one readback per bounded window. - pub fn advance(&self, ticks: u32, stop_when_inactive: bool) -> Promise { - advance_window(self.inner.clone(), ticks, stop_when_inactive, None) - } - #[wasm_bindgen(js_name = advanceGeneration)] - pub fn advance_generation(&self, time_limit_ticks: u32) -> Promise { - advance_window(self.inner.clone(), 0, true, Some(time_limit_ticks)) - } - /// Limits the share of wall time the simulator keeps the GPU busy, in - /// (0, 1]; the rest is left idle for the desktop and other applications. - #[wasm_bindgen(js_name = setLoad)] - pub fn set_load(&self, load: f64) -> Result<(), JsError> { - self.inner - .backend - .set_load(load) - .map_err(|e| js_error(format!("{e:?}"))) - } - /// Compares a short window against WASM and restores its initial state. - /// Checks remain inside Rust; the host receives only their count. - pub fn verify(&self, ticks: u32) -> Promise { - let inner = self.inner.clone(); - if ticks == 0 || ticks > 60 { - return Promise::reject(&js_error("verification needs 1 to 60 ticks").into()); - } - if inner.busy.get() || inner.shared.busy.get() { - return Promise::reject(&js_error("a GPU window is in flight").into()); - } - inner.busy.set(true); - inner.shared.busy.set(true); - future_to_promise(async move { - if let Err(e) = inner.sync_world() { - inner.busy.set(false); - inner.shared.busy.set(false); - return Err(js_error(e).into()); - } - let (backup, tick, batch, expected) = { - let mut s = inner.shared.session.borrow_mut(); - let r = &mut s.runner; - let backup = r.agents.clone(); - let tick = r.tick; - let batch = r.batch_index; - r.advance(ticks as u64, false); - let expected = r.canonical_state(&inner.surfaces.borrow()); - r.agents = backup.clone(); - r.tick = tick; - r.batch_index = batch; - (backup, tick, batch, expected) - }; - inner.tag.set(None); - let result = match expected { - Err(e) => Err(e), - Ok(expected) => match run_window(&inner, ticks, false, None).await { - Err(e) => Err(e), - Ok(_) => inner - .shared - .session - .borrow() - .runner - .canonical_state(&inner.surfaces.borrow()) - .and_then(|actual| { - if actual != expected { - Err("WebGPU simulation verification differs from WASM".into()) - } else { - Ok(expected.len() as u32) - } - }), - }, - }; - { - let mut s = inner.shared.session.borrow_mut(); - s.runner.agents = backup; - s.runner.tick = tick; - s.runner.batch_index = batch; - } - inner.tag.set(None); - inner.shared.revision.set(inner.shared.revision.get().wrapping_add(1)); - inner.busy.set(false); - inner.shared.busy.set(false); - result.map(JsValue::from).map_err(|e| js_error(e).into()) - }) - } -} -struct Encoded { - world: Vec, - base: Vec, - surfaces: simulation::SurfaceTable, - shape: Vec, -} -/// The world buffer (track, vehicle, sensors, network shape and controls) -/// and the uniform base describing its layout. -fn encode(session: &crate::training::session::Session) -> Result { - let runner = &session.runner; - - if runner.world.track.native_broadphase || runner.world.track.shapes.is_empty() { - return Err("WebGPU simulation needs independent cars and physics shapes".into()); - } - let arrays = TrackArrays::new(&runner.world)?; - let (nodes, walls, magnitude, depth) = runner - .world - .track - .ray_tree() - .ok_or("WebGPU simulation needs a BSP ray tree")? - .gpu_arrays(); - if depth > 48 { - return Err("BSP exceeds WebGPU ray stack depth".into()); - } - let rays = ( - nodes - .into_iter() - .map(|(links, own, subtree)| RayNode { links, own, subtree }) - .collect(), - walls, - magnitude, - depth, - ); - let mut world = world::encode_world(&arrays.desc(&rays))?; - let vehicle = simulation::vehicle_desc(&runner.world.vehicle)?; - let vehicle_offset = append(&mut world, &[vehicle])?; - let sensors: Vec<_> = runner - .layout - .sensors - .iter() - .map(|sensor| simulation::with_ray_offset(simulation::sensor_desc(sensor), runner.world.math)) - .collect(); - if sensors.len() > 64 { - return Err("WebGPU supports at most 64 sensor inputs".into()); - } - let sensors_offset = append(&mut world, &sensors)?; - let first = runner.agents.first().ok_or("the session has not started")?; - if first.network.shape.iter().any(|&n| n > 64) { - return Err("WebGPU supports layer widths up to 64".into()); - } - let shape: Vec = first.network.shape.iter().map(|&n| n as u32).collect(); - let shape_offset = append(&mut world, &shape)?; - let sources = simulation::control_sources(session.output_names()); - let control_offset = append(&mut world, &sources)?; - let mut base = vec![0u32; 24]; - base[12] = 0; - base[13] = vehicle_offset; - base[14] = sensors_offset; - base[15] = sensors.len() as u32; - base[16] = shape.len() as u32; - base[17] = first.network.params.len() as u32; - base[18] = shape_offset; - base[19] = control_offset; - base[20] = sensors.iter().any(|s| s.kind == 11 || s.kind == 12) as u32; - base[23] = MAX_WINDOW_TICKS; - if base[20] != 0 && runner.world.track.curve.is_none() && runner.world.track.path.len() >= 2 { - return Err("WebGPU path sensors need the scene's Curve2D control points".into()); - } - Ok(Encoded { - world, - base, - surfaces: arrays.surfaces, - shape: first.network.shape.clone(), - }) -} -impl Inner { - /// Re-encodes the world buffer after `Simulation.replaceTrack`. - fn sync_world(&self) -> Result<(), String> { - let session = self.shared.session.borrow(); - if std::sync::Arc::ptr_eq(&session.runner.world, &self.world.borrow()) { - return Ok(()); - } - let encoded = encode(&session)?; - if encoded.shape != self.shape { - return Err("network shape changed; recreate the GPU simulator".into()); - } - // The sensor layout, which specializes the kernels, belongs to the - // session and stays the same. - let mut base = self.base.borrow_mut(); - let population = base[0]; - *base = encoded.base; - base[0] = population; - self.backend - .set_world(&Uint32Array::from(encoded.world.as_slice())) - .map_err(|e| format!("GPU world: {e:?}"))?; - *self.surfaces.borrow_mut() = encoded.surfaces; - *self.world.borrow_mut() = session.runner.world.clone(); - // Car state refers to the surface table: upload it again. - self.tag.set(None); - Ok(()) - } - async fn upload_if_needed(&self) -> Result<(), String> { - self.sync_world()?; - let (upload, generation, population) = { - let session = self.shared.session.borrow(); - let r = &session.runner; - if self.tag.get() == Some((r.generation, self.shared.revision.get())) - && self.population.get() == r.agents.len() - { - return Ok(()); - } - let mut cars = Vec::new(); - let mut agents = Vec::new(); - crate::training::export_state_into( - &r.agents, - &r.world.vehicle, - &self.surfaces.borrow(), - &mut cars, - &mut agents, - )?; - if r.is_paused() { - return Err("WebGPU does not model frozen cars".into()); - } - let first = r.agents.first().ok_or("no agents")?; - if first.network.shape != self.shape { - return Err("network shape changed; recreate the GPU simulator".into()); - } - let mut params = Vec::new(); - for a in &r.agents { - if a.network.shape != first.network.shape { - return Err("population network shapes differ".into()); - } - params.extend_from_slice(&a.network.params); - } - let mut base = self.base.borrow_mut(); - base[0] = r.agents.len() as u32; - let generation = r.generation; - let population = r.agents.len(); - let upload = self.backend.upload( - &Uint32Array::from(words(&cars).as_slice()), - &Uint32Array::from(words(&agents).as_slice()), - &Uint32Array::from(words(¶ms).as_slice()), - &Uint32Array::from(base.as_slice()), - ); - (upload, generation, population) - }; - JsFuture::from(upload).await.map_err(|e| format!("GPU upload: {e:?}"))?; - self.tag.set(Some((generation, self.shared.revision.get()))); - self.population.set(population); - Ok(()) - } -} -fn advance_window(inner: Rc, ticks: u32, stop: bool, limit: Option) -> Promise { - if inner.busy.get() || inner.shared.busy.get() { - return Promise::reject(&js_error("a GPU window is in flight").into()); - } - inner.busy.set(true); - inner.shared.busy.set(true); - future_to_promise(async move { - let result = run_window(&inner, ticks, stop, limit).await; - if result.is_err() { - inner.tag.set(None); - } - inner.busy.set(false); - inner.shared.busy.set(false); - result.map(JsValue::from).map_err(|e| js_error(e).into()) - }) -} -async fn run_window(inner: &Inner, ticks: u32, stop: bool, limit: Option) -> Result { - let total = { - let s = inner.shared.session.borrow(); - let r = &s.runner; - if limit.is_some() && r.stats_phase != 0 { - return Err("generation needs statistics phase zero".into()); - } - let total = u32::try_from(limit.map_or(ticks as u64, |t| ((t as u64 / 6 + 2) * 6).saturating_sub(r.tick))) - .map_err(|_| "GPU generation window exceeds u32 range")?; - let tick = u32::try_from(r.tick).map_err(|_| "GPU tick exceeds u32 range")?; - tick.checked_add(total) - .filter(|&n| n < u32::MAX) - .ok_or("GPU tick overflow")?; - total - }; - if total == 0 { - return Ok(0); - } - inner.upload_if_needed().await?; - let mut executed = 0; - while executed < total { - // Long submissions can trip a browser's GPU watchdog. Commit bounded - // windows while keeping many device-only ticks between readbacks. - let count = (total - executed).min(MAX_WINDOW_TICKS); - let (ran, transition) = run_chunk(inner, count, stop, limit, executed == 0).await?; - executed += ran; - if transition { - break; - } - } - Ok(executed) -} -async fn run_chunk( - inner: &Inner, - ticks: u32, - stop: bool, - limit: Option, - reset: bool, -) -> Result<(u32, bool), String> { - let window = { - let s = inner.shared.session.borrow(); - let r = &s.runner; - if limit.is_some() && r.stats_phase != 0 { - return Err("generation needs statistics phase zero".into()); - } - let tick = u32::try_from(r.tick).map_err(|_| "GPU tick exceeds u32 range")?; - tick.checked_add(ticks) - .filter(|&n| n < u32::MAX) - .ok_or("GPU tick overflow")?; - let bits = (limit.unwrap_or(0) as f64 / 60.0).to_bits(); - vec![ - ticks, - tick, - r.batch_index as u32, - r.batches_per_tick() as u32, - r.stats_phase as u32, - stop as u32, - r.eliminate_on_wall as u32, - r.eliminate_when_idle as u32, - limit.is_some() as u32, - bits as u32, - (bits >> 32) as u32, - ] - }; - let value = JsFuture::from(inner.backend.advance(&Uint32Array::from(window.as_slice()), reset)) - .await - .map_err(|e| format!("WebGPU window: {e:?}"))?; - let data = Uint32Array::new(&value).to_vec(); - let n = inner.population.get(); - let car_words = std::mem::size_of::() / 4; - let agent_words = std::mem::size_of::() / 4; - if data.len() != n * (car_words + agent_words) + 4 { - return Err("GPU returned invalid state size".into()); - } - fn decode(data: &[u32], stride: usize) -> Vec { - data.chunks_exact(stride) - .map(|row| { - // SAFETY: the shader writes every numeric field of the POD mirrors. - // Read unaligned because u32 arrays do not promise f64 alignment. - unsafe { std::ptr::read_unaligned(row.as_ptr().cast::()) } - }) - .collect() - } - let cars = decode::(&data[..n * car_words], car_words); - let agents = decode::(&data[n * car_words..n * (car_words + agent_words)], agent_words); - let header = &data[n * (car_words + agent_words)..]; - let executed = header[0]; - let transition = header[1]; - if executed == 0 - || executed > window[0] - || transition > 1 - || (transition == 0 && executed != window[0]) - || (transition != 0 && (window[1] + executed) % 6 != window[4]) - { - return Err(format!( - "GPU returned stopping tick {executed}, transition {transition}, for {} ticks from {}", - window[0], window[1] - )); - } - // Check the whole result before mutating the CPU session, so fallback can - // resume from its last valid window if shader domain checks fail. - if let Some(c) = cars.iter().find(|c| c.error != 0) { - return Err(format!("GPU simulation error bits {:#x}", c.error)); - } - let mut session = inner.shared.session.borrow_mut(); - let r = &mut session.runner; - if cars - .iter() - .any(|c| c.pair_count as usize > simulation::MAX_PAIRS || c.contact_count as usize > simulation::MAX_CONTACTS) - || agents - .iter() - .any(|a| a.recent_len as usize > simulation::RECENT || a.recent_start as usize >= simulation::RECENT) - { - return Err("GPU returned invalid collision or statistics counts".into()); - } - if cars.iter().any(|c| { - c.wheel_surface[..r.world.vehicle.wheels.len()] - .iter() - .any(|&i| i as usize >= inner.surfaces.borrow().values.len()) - }) { - return Err("GPU returned an invalid wheel surface".into()); - } - r.import_state(&cars, &agents, &inner.surfaces.borrow())?; - r.tick += executed as u64; - r.batch_index = (r.batch_index + ((executed - transition) as usize % 8) * r.batches_per_tick()) % 8; - inner.shared.revision.set(inner.shared.revision.get().wrapping_add(1)); - inner.tag.set(Some((r.generation, inner.shared.revision.get()))); - Ok((executed, transition != 0)) -} diff --git a/github-simulator/source/src/wasm/sim/runtime.js b/github-simulator/source/src/wasm/sim/runtime.js deleted file mode 100644 index ad92b915c559d983340144373dd6e00459abdde9..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/wasm/sim/runtime.js +++ /dev/null @@ -1,511 +0,0 @@ -// WebGPU command and buffer glue used only by the WASM GpuSimulation handle. -// All numerical work is WGSL. Buffers persist across windows and networks are -// uploaded only when the WASM population changes at a generation boundary. - -// Share of wall time the simulator keeps the GPU fed (setLoad changes it), -// the target GPU time of one submission, the shortest idle pause, and the -// submissions kept queued so completion latency does not starve the GPU. -const DEFAULT_LOAD = 0.85; -const SUBMISSION_MS = 4; -const MIN_IDLE_MS = 4; -const QUEUED_SUBMISSIONS = 3; -// `progress(done, total, kernel)` reports each pipeline before it compiles, -// then once more when all have. -export async function createSimulator(device, source, world, base, progress) { - source = specializeMath(source, world[15]); - source = specializeSensors(source, world, base); - device.pushErrorScope("out-of-memory"); - device.pushErrorScope("validation"); - let simulator; - try { - const bindGroupLayout = device.createBindGroupLayout({ - entries: [ - { - binding: 0, - visibility: GPUShaderStage.COMPUTE, - buffer: { type: "storage" }, - }, - { - binding: 1, - visibility: GPUShaderStage.COMPUTE, - buffer: { type: "storage" }, - }, - { - binding: 2, - visibility: GPUShaderStage.COMPUTE, - buffer: { type: "read-only-storage" }, - }, - { - binding: 3, - visibility: GPUShaderStage.COMPUTE, - buffer: { type: "read-only-storage" }, - }, - { - binding: 4, - visibility: GPUShaderStage.COMPUTE, - buffer: { type: "storage" }, - }, - { - binding: 5, - visibility: GPUShaderStage.COMPUTE, - buffer: { type: "uniform", hasDynamicOffset: true }, - }, - ], - }); - const layout = device.createPipelineLayout({ - bindGroupLayouts: [bindGroupLayout], - }); - const pipelines = []; - const entryPoints = [ - "sensors_kernel", - "forward_kernel", - "stats_kernel", - "stop_kernel", - "step_kernel", - "drive_kernel", - "reset_kernel", - ]; - for (const entryPoint of entryPoints) { - progress?.(pipelines.length, entryPoints.length, entryPoint); - const module = device.createShaderModule({ - code: shaderForEntry(source, entryPoint), - }); - const info = await module.getCompilationInfo(); - const errors = info.messages.filter((m) => m.type === "error"); - if (errors.length) - throw new Error( - errors - .map((m) => `${m.lineNum}:${m.linePos}: ${m.message}`) - .join("\n"), - ); - pipelines.push( - await device.createComputePipelineAsync({ - layout, - compute: { module, entryPoint }, - }), - ); - } - progress?.(pipelines.length, entryPoints.length, null); - simulator = new Simulator(device, bindGroupLayout, pipelines, world, base); - } finally { - const validation = await device.popErrorScope(); - const allocation = await device.popErrorScope(); - if (validation || allocation) { - simulator?.destroy(); - throw new Error((validation || allocation).message); - } - } - return simulator; -} -class Simulator { - constructor(device, layout, pipelines, world, base) { - this.device = device; - this.layout = layout; - this.pipelines = pipelines; - this.base = base; - this.mathProfile = world[15]; - this.buffers = []; - this.world = this.buffer( - world.byteLength, - GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, - ); - device.queue.writeBuffer(this.world, 0, world); - this.uniform = this.buffer( - base[23] * 256, - GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, - ); - this.lost = null; - device.lost.then((info) => { - this.lost = new Error(`WebGPU device lost: ${info.message}`); - }); - this.load = DEFAULT_LOAD; - this.tickMs = 2; // GPU time per tick, refined after each window - } - // A replaced track: the next upload binds the new world buffer. - setWorld(world) { - if (this.busy) throw new Error("WebGPU window is in flight"); - if (world[15] !== this.mathProfile) - throw new Error("Math profile changed; recreate the GPU simulator"); - const old = this.world; - this.world = this.buffer( - world.byteLength, - GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, - ); - this.device.queue.writeBuffer(this.world, 0, world); - old.destroy(); - this.buffers.splice(this.buffers.indexOf(old), 1); - this.populationSizes = null; - } - setLoad(load) { - if (!(load > 0 && load <= 1)) - throw new Error("GPU load must be in (0, 1]"); - this.load = load; - } - async waitForGpu(completion) { - // Firefox can defer completion callbacks to its next maintenance poll. - // Empty submissions prompt polling without dispatching simulation work. - // Only pending waits need the timer; fast completions cancel it first. - const timer = setInterval(() => { - if (this.lost) { - clearInterval(timer); - return; - } - try { - this.device.queue.submit([]); - } catch { - // Retain the original operation: a pending map must settle before - // the window releases its busy flag or reuses the readback buffer. - clearInterval(timer); - } - }, MIN_IDLE_MS); - try { - return await completion; - } finally { - clearInterval(timer); - } - } - buffer(size, usage) { - if ( - size > this.device.limits.maxBufferSize || - (usage & GPUBufferUsage.STORAGE && - size > this.device.limits.maxStorageBufferBindingSize) - ) - throw new Error("Population exceeds WebGPU buffer limits"); - const b = this.device.createBuffer({ size: Math.max(4, size), usage }); - this.buffers.push(b); - return b; - } - async upload(cars, agents, networks, base) { - if (this.busy) throw new Error("WebGPU window is in flight"); - if (this.lost) throw this.lost; - this.device.pushErrorScope("out-of-memory"); - this.device.pushErrorScope("validation"); - try { - this.resultSize = 16; - const sizes = [ - cars.byteLength, - agents.byteLength, - networks.byteLength, - (4 + base[0] * 192) * 4, - cars.byteLength + agents.byteLength + this.resultSize, - ]; - if (!this.populationSizes?.every((size, i) => size === sizes[i])) { - // Reproduction can resize a population. Replace its allocations then. - for (const b of this.populationBuffers ?? []) { - b.destroy(); - const index = this.buffers.indexOf(b); - if (index >= 0) this.buffers.splice(index, 1); - } - this.populationBuffers = []; - this.populationSizes = null; - const first = this.buffers.length; - try { - const usage = - GPUBufferUsage.STORAGE | - GPUBufferUsage.COPY_DST | - GPUBufferUsage.COPY_SRC; - this.cars = this.buffer(cars.byteLength, usage); - this.agents = this.buffer(agents.byteLength, usage); - this.networks = this.buffer( - networks.byteLength, - GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, - ); - this.resultSize = 16; - // Per car: 128 words of sensor inputs or step carry, then 64 - // sensor error words. - this.results = this.buffer((4 + base[0] * 192) * 4, usage); - this.readback = this.buffer( - cars.byteLength + agents.byteLength + this.resultSize, - GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST, - ); - this.populationBuffers = this.buffers.slice(first); - this.bindGroup = this.device.createBindGroup({ - layout: this.layout, - entries: [ - this.cars, - this.agents, - this.world, - this.networks, - this.results, - this.uniform, - ].map((buffer, binding) => ({ - binding, - resource: { buffer, ...(binding === 5 ? { size: 96 } : {}) }, - })), - }); - this.populationSizes = sizes; - } finally { - this.populationBuffers = this.buffers.slice(first); - } - } - this.carSize = cars.byteLength; - this.agentSize = agents.byteLength; - this.base = base; - this.device.queue.writeBuffer(this.cars, 0, cars); - this.device.queue.writeBuffer(this.agents, 0, agents); - this.device.queue.writeBuffer(this.networks, 0, networks); - } finally { - const validation = await this.device.popErrorScope(); - const allocation = await this.device.popErrorScope(); - if (validation || allocation) { - this.populationSizes = null; - throw new Error((validation || allocation).message); - } - } - } - async advance(window, reset) { - if (this.busy) throw new Error("WebGPU window is in flight"); - if (this.lost) throw this.lost; - this.busy = true; - this.device.pushErrorScope("out-of-memory"); - this.device.pushErrorScope("validation"); - let value; - try { - const uniform = this.base.slice(); - uniform.set(window, 1); - if (uniform[1] > this.base[23]) - throw new Error("GPU window exceeds its submission limit"); - const uniforms = new Uint32Array(uniform[1] * 64); - for (let k = 1; k <= uniform[1]; k++) { - uniforms.set(uniform, (k - 1) * 64); - uniforms[(k - 1) * 64 + 22] = k; - } - this.device.queue.writeBuffer(this.uniform, 0, uniforms); - this.device.queue.writeBuffer( - this.results, - 0, - new Uint32Array([uniform[1], 0, 0, 0]), - ); - // Split the window into short submissions so a desktop compositor - // sharing the GPU queue waits a few milliseconds at most, and leave - // the GPU idle for (1 - load) of the time. - const ticks = uniform[1]; - const size = Math.max( - 1, - Math.min(ticks, Math.floor(SUBMISSION_MS / this.tickMs)), - ); - const started = performance.now(); - let idle = 0; - const queued = []; - for (let first = 1; first <= ticks; first += size) { - const last = Math.min(ticks, first + size - 1); - const encoder = this.device.createCommandEncoder(); - const dispatch = (pipeline, k, count) => { - const pass = encoder.beginComputePass(); - pass.setPipeline(this.pipelines[pipeline]); - pass.setBindGroup(0, this.bindGroup, [(k - 1) * 256]); - pass.dispatchWorkgroups(count); - pass.end(); - }; - if (reset && first === 1) - dispatch(6, 1, Math.ceil(uniform[0] / 32)); - for (let k = first; k <= last; k++) { - const groups = Math.ceil(uniform[0] / 32); - if ((uniform[2] + k) % 6 === uniform[5]) { - dispatch(2, k, groups); - dispatch(3, k, 1); - } - dispatch(0, k, groups * uniform[15]); - dispatch(1, k, uniform[0]); - dispatch(5, k, groups); - dispatch(4, k, groups); - } - if (last === ticks) { - encoder.copyBufferToBuffer( - this.cars, - 0, - this.readback, - 0, - this.carSize, - ); - encoder.copyBufferToBuffer( - this.agents, - 0, - this.readback, - this.carSize, - this.agentSize, - ); - encoder.copyBufferToBuffer( - this.results, - 0, - this.readback, - this.carSize + this.agentSize, - this.resultSize, - ); - } - this.device.queue.submit([encoder.finish()]); - if (last === ticks) break; - // Bound the queued submissions. - queued.push(this.device.queue.onSubmittedWorkDone()); - if (queued.length > QUEUED_SUBMISSIONS) - await this.waitForGpu(queued.shift()); - const owed = - ((performance.now() - started - idle) * (1 - this.load)) / - this.load - - idle; - if (owed >= MIN_IDLE_MS) { - await this.waitForGpu(queued.at(-1)); - queued.length = 0; - const t = performance.now(); - await new Promise((resolve) => setTimeout(resolve, owed)); - idle += performance.now() - t; - } - } - await this.waitForGpu(this.readback.mapAsync(GPUMapMode.READ)); - const busy = performance.now() - started - idle; - this.tickMs = - (this.tickMs + Math.min(50, Math.max(0.05, busy / ticks))) / 2; - try { - value = new Uint32Array(this.readback.getMappedRange().slice(0)); - } finally { - this.readback.unmap(); - } - } finally { - const validation = await this.device.popErrorScope(); - const allocation = await this.device.popErrorScope(); - this.busy = false; - if (validation || allocation) - throw new Error((validation || allocation).message); - } - return value; - } - destroy() { - for (const b of this.buffers) b.destroy(); - this.buffers = []; - } -} - -// Remove unreachable functions before handing each pipeline to a driver. -// Large soft-float modules otherwise consume several GB during compilation. -export function shaderForEntry(source, entry) { - const functions = new Map(); - const regex = /(@compute\s+@workgroup_size\([^)]*\)\s*)?\bfn\s+(\w+)\s*\(/g; - let match; - while ((match = regex.exec(source))) { - const start = match.index; - const body = source.indexOf("{", regex.lastIndex); - let end = body + 1, - depth = 1; - while (depth) { - if (source[end] === "{") depth++; - else if (source[end] === "}") depth--; - end++; - } - functions.set(match[2], { start, end, body: source.slice(body, end) }); - regex.lastIndex = end; - } - const needed = new Set([entry]); - const queue = [entry]; - while (queue.length) { - const name = queue.pop(); - const body = functions.get(name)?.body; - if (!body) throw new Error(`Missing shader function ${name}`); - for (const call of body.matchAll(/\b(\w+)\s*\(/g)) { - if (functions.has(call[1]) && !needed.has(call[1])) { - needed.add(call[1]); - queue.push(call[1]); - } - } - } - for (const [name, fn] of [...functions].reverse()) - if (!needed.has(name)) - source = source.slice(0, fn.start) + source.slice(fn.end); - // Tables for other profiles are large. Remove their declarations as well - // as their functions, retaining constants referenced by other constants. - const constants = [...source.matchAll(/\bconst\s+(\w+)\s*:[^;]*;/g)]; - let reachable = source.replace(/\bconst\s+(\w+)\s*:[^;]*;/g, ""); - const pending = new Set(constants); - let changed = true; - while (changed) { - changed = false; - for (const constant of pending) { - if (new RegExp(`\\b${constant[1]}\\b`).test(reachable)) { - reachable += constant[0]; - pending.delete(constant); - changed = true; - } - } - } - for (const constant of [...pending].reverse()) - source = - source.slice(0, constant.index) + - source.slice(constant.index + constant[0].length); - return source; -} -// All profiles ship in the WASM package. Compile just the chosen session's -// dispatch targets, so the driver need not inline three soft-float kernels -// at every call site. The shared generated kernel source stays intact. -export function specializeMath(source, profile) { - if (![0, 1, 2].includes(profile)) - throw new Error(`Unknown GPU math profile ${profile}`); - const targets = - profile === 0 - ? { atan2: "native_atan2", exp: "dexp", pow: "dpow", tanh: "game_tanh" } - : { - atan2: "win_atan2f", - exp: `win${profile === 1 ? 10 : 11}_exp`, - pow: `win${profile === 1 ? 10 : 11}_pow`, - tanh: `win${profile === 1 ? 10 : 11}_tanh`, - }; - for (const [operation, target] of Object.entries(targets)) { - const signature = new RegExp( - `\\bfn profile_${operation}\\([^)]*\\)\\s*->\\s*\\w+\\s*\\{`, - ); - const match = signature.exec(source); - if (!match) throw new Error(`Missing shader profile_${operation}`); - const body = match.index + match[0].length; - let end = body, - depth = 1; - while (depth && end < source.length) { - if (source[end] === "{") depth++; - else if (source[end] === "}") depth--; - end++; - } - if (depth) throw new Error(`Incomplete shader profile_${operation}`); - const args = - operation === "atan2" - ? "y_arg, x_arg" - : operation === "pow" - ? "x_arg, y_arg" - : "x_arg"; - source = source.slice(0, body) + ` return ${target}(${args}); }` + source.slice(end); - } - return source; -} -function specializeSensors(source, world, base) { - const names = [ - "S_RAYCAST", - "S_DISTANCE_FROM_WALL", - "S_SPEED", - "S_VELOCITY_FRONT", - "S_VELOCITY_SIDE", - "S_ACCELERATION_FRONT", - "S_ACCELERATION_SIDE", - "S_ANGULAR_VELOCITY", - "S_WHEEL_ANGLE", - "S_BOOST_CAPACITY", - "S_GRIP", - "S_CORRECT_DIRECTION", - "S_TRACK_CURVATURE", - ]; - const used = new Set(); - for (let i = 0; i < base[15]; i++) used.add(names[world[base[14] + i * 6]]); - const start = source.indexOf("fn sensor_value("), - end = source.indexOf("\nfn ", start + 1); - let sensor = source.slice(start, end); - const ranges = []; - for (const m of sensor.matchAll(/case (S_\w+): \{/g)) { - if (used.has(m[1])) continue; - let depth = 1, - end = m.index + m[0].length; - while (depth) { - if (sensor[end] === "{") depth++; - else if (sensor[end] === "}") depth--; - end++; - } - ranges.push([m.index, end]); - } - for (const [start, end] of ranges.reverse()) - sensor = sensor.slice(0, start) + sensor.slice(end); - return source.slice(0, start) + sensor + source.slice(end); -} diff --git a/github-simulator/source/src/wasm/sim/window.wgsl b/github-simulator/source/src/wasm/sim/window.wgsl deleted file mode 100644 index 762ff23d0a86e7d9e297a088c6ef74e472cfaa61..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/wasm/sim/window.wgsl +++ /dev/null @@ -1,135 +0,0 @@ -// Full GPU simulation, with separate kernels to keep shader compilation bounded. -// Each (car, sensor) pair senses in its own invocation, sensor-major in blocks -// of 32 cars so a workgroup evaluates one sensor kind without divergence. The -// value goes to the car's input slots and its domain-error bits to a -// per-sensor word after the input area, which the forward kernel folds into -// the car's error field. -fn sensor_errors(index: u32) -> u32 { return 4u+params.population*128u+index*64u; } -fn sense(index: u32, sensor: u32) { - var err = 0u; - var here = 0.0f; - let desc = load_SensorDesc(params.sensors_offset+sensor*6u); - if (desc.kind == S_CORRECT_DIRECTION || desc.kind == S_TRACK_CURVATURE) { here = sensor_path_offset(); } - let value = sensor_value(desc, here, &err); - let at=4u+index*128u+sensor*2u; - results[at]=value.x;results[at+1u]=value.y; - results[sensor_errors(index)+sensor]=err; -} -fn statistics(k: u32) { - var err=0u; - let first=select(params.stats_phase,6u,params.stats_phase==0u); - update_stats(U64(params.start_tick+k,0u),U64(params.start_tick+k-first,0u),params.eliminate_idle != 0u,&err); - sim_car.error=sim_car.error | err; -} -fn time_limit(k: u32) -> bool { - if (params.has_limit==0u) { return false; } - let first=select(params.stats_phase,6u,params.stats_phase==0u); - let updates=(params.start_tick+k-first)/6u; - return f64_ge(f64_mul(f64_from_u32(updates),F64(0x9999999au,0x3fb99999u)),params.limit); -} -fn load_car_state(index: u32) { - sim_world=load_World(params.world_offset); - sim_vehicle=load_VehicleDesc(params.vehicle_offset); - load_Car(index*800u); - load_Agent(index*94u); -} -fn save_car_state(index: u32) { - store_Car(index*800u); - store_Agent(index*94u); -} - -fn eligible(index:u32) -> bool { - let batch_size=(params.population+7u)/8u; - let batch=(params.start_batch+((params.reserved_a-1u)%8u)*params.batches_per_tick)%8u; - return (sim_car.flags & CAR_ACTIVE) != 0u && (index/batch_size+8u-batch)%8u) { - let stride=(params.population+31u)/32u*32u; - let i=id.x%stride;let s=id.x/stride; - if(i>=params.population || s>=params.sensor_count || results[1]!=0u){return;} - sim_world=load_World(params.world_offset); - sim_vehicle=load_VehicleDesc(params.vehicle_offset); - load_sensor_Car(i*800u); - if(eligible(i)){sense(i,s);} -} -var layer_input: array; -var sensor_error: atomic; -@compute @workgroup_size(64) -fn forward_kernel(@builtin(workgroup_id) group:vec3, @builtin(local_invocation_id) lane:vec3) { - let i=group.x;let j=lane.x; - var enabled=false; - if(j==0u){atomicStore(&sensor_error,0u);} - if(i=0){control=layer_input[u32(src)];} - agents[i*94u+j*2u]=control.x;agents[i*94u+j*2u+1u]=control.y; - } -} -@compute @workgroup_size(32) -fn stats_kernel(@builtin(global_invocation_id) id:vec3) { - let i=id.x;if(i>=params.population || results[1]!=0u){return;} - load_car_state(i);statistics(params.reserved_a);save_car_state(i); -} -@compute @workgroup_size(1) -fn stop_kernel() { - if(results[1]!=0u){return;} - var live=false; - for(var i=0u;i) { - let i=id.x;if(i>=params.population){return;} - agents[i*94u+92u]=agents[i*94u+92u] & ~AGENT_HAS_DEACTIVATED; - agents[i*94u+90u]=0u;agents[i*94u+91u]=0u; -} -@compute @workgroup_size(32) -fn drive_kernel(@builtin(global_invocation_id) id:vec3) { - let i=id.x;if(i>=params.population || results[1]!=0u){return;} - load_car_state(i); - let carry=step_begin(sim_agent.controls,true); - store_StepCarry(4u+i*128u,carry);store_Car(i*800u); -} -@compute @workgroup_size(32) -fn step_kernel(@builtin(global_invocation_id) id:vec3) { - let i=id.x;if(i>=params.population || results[1]!=0u){return;} - load_car_state(i); - let was_active=(sim_car.flags & CAR_ACTIVE)!=0u; - let contact=step_end(load_StepCarry(4u+i*128u),true,params.eliminate_wall!=0u); - if(contact){sim_agent.flags=sim_agent.flags | AGENT_PENDING_CONTACT;} - if(was_active && (sim_car.flags & CAR_ACTIVE)==0u){ - sim_agent.flags=sim_agent.flags | AGENT_HAS_DEACTIVATED; - sim_agent.deactivated_at=U64(params.start_tick+params.reserved_a,0u); - } - save_car_state(i); -} diff --git a/github-simulator/source/src/wasm/sim/world.rs b/github-simulator/source/src/wasm/sim/world.rs deleted file mode 100644 index c955f9e942d1eb336775b8416e87e406614cfab7..0000000000000000000000000000000000000000 --- a/github-simulator/source/src/wasm/sim/world.rs +++ /dev/null @@ -1,110 +0,0 @@ -// Generated by wasm/gen-sim.py; offsets match generated.wgsl raw loaders. -use super::append_ptr; -use crate::gpu::simulation::WorldDesc; -pub(super) fn encode_world(w: &WorldDesc) -> Result, String> { - let mut data = vec![0u32; 64]; - data[0] = w.ray_node_count; - data[1] = w.ray_wall_count; - data[2] = w.ray_depth; - data[3] = w.ray_magnitude.to_bits(); - data[4] = unsafe { append_ptr(&mut data, w.ray_nodes, w.ray_node_count as usize) }?; - data[5] = unsafe { append_ptr(&mut data, w.ray_walls, w.ray_wall_count as usize) }?; - data[6] = w.surface_count; - data[7] = w.default_surface; - data[8] = unsafe { append_ptr(&mut data, w.surfaces, w.surface_count as usize) }?; - data[9] = i32::try_from(w.tile_x0).map_err(|_| "tile bounds exceed WebGPU range")? as u32; - data[10] = i32::try_from(w.tile_y0).map_err(|_| "tile bounds exceed WebGPU range")? as u32; - data[11] = i32::try_from(w.tile_nx).map_err(|_| "tile bounds exceed WebGPU range")? as u32; - data[12] = i32::try_from(w.tile_ny).map_err(|_| "tile bounds exceed WebGPU range")? as u32; - data[13] = unsafe { - append_ptr( - &mut data, - w.tiles, - (w.tile_nx as usize) - .checked_mul(w.tile_ny as usize) - .ok_or("tile count overflow")?, - ) - }?; - data[14] = w.path_points; - data[15] = w.math_profile; - data[16] = unsafe { append_ptr(&mut data, w.path_segments, w.path_points.saturating_sub(1) as usize) }?; - data[17] = unsafe { append_ptr(&mut data, w.path_offsets, w.path_points as usize) }?; - let cells = (w.path_grid.nx as usize) - .checked_mul(w.path_grid.ny as usize) - .ok_or("grid count overflow")?; - let count_path_grid = if cells > 0 { - unsafe { *w.path_grid.start.add(cells) as usize } - } else { - 0 - }; - let bits = w.path_grid.x0.to_bits(); - data[18] = bits as u32; - data[19] = (bits >> 32) as u32; - let bits = w.path_grid.y0.to_bits(); - data[20] = bits as u32; - data[21] = (bits >> 32) as u32; - let bits = w.path_grid.cell.to_bits(); - data[22] = bits as u32; - data[23] = (bits >> 32) as u32; - data[24] = w.path_grid.nx; - data[25] = w.path_grid.ny; - data[26] = unsafe { append_ptr(&mut data, w.path_grid.start, if cells > 0 { cells + 1 } else { 0 }) }?; - data[27] = unsafe { append_ptr(&mut data, w.path_grid.items, count_path_grid) }?; - data[28] = w.curve_points; - data[29] = w.curve_length.to_bits(); - data[30] = unsafe { append_ptr(&mut data, w.curve_position, w.curve_points as usize) }?; - data[31] = unsafe { append_ptr(&mut data, w.curve_offsets, w.curve_points as usize) }?; - data[32] = unsafe { append_ptr(&mut data, w.curve_forwards, w.curve_points as usize) }?; - let cells = (w.curve_grid.nx as usize) - .checked_mul(w.curve_grid.ny as usize) - .ok_or("grid count overflow")?; - let count_curve_grid = if cells > 0 { - unsafe { *w.curve_grid.start.add(cells) as usize } - } else { - 0 - }; - let bits = w.curve_grid.x0.to_bits(); - data[34] = bits as u32; - data[35] = (bits >> 32) as u32; - let bits = w.curve_grid.y0.to_bits(); - data[36] = bits as u32; - data[37] = (bits >> 32) as u32; - let bits = w.curve_grid.cell.to_bits(); - data[38] = bits as u32; - data[39] = (bits >> 32) as u32; - data[40] = w.curve_grid.nx; - data[41] = w.curve_grid.ny; - data[42] = unsafe { append_ptr(&mut data, w.curve_grid.start, if cells > 0 { cells + 1 } else { 0 }) }?; - data[43] = unsafe { append_ptr(&mut data, w.curve_grid.items, count_curve_grid) }?; - data[44] = w.shape_count; - data[45] = w.shape_point_count; - data[46] = unsafe { append_ptr(&mut data, w.shapes, w.shape_count as usize) }?; - data[47] = unsafe { append_ptr(&mut data, w.shape_local, w.shape_point_count as usize) }?; - data[48] = unsafe { append_ptr(&mut data, w.shape_normals, w.shape_point_count as usize) }?; - let cells = (w.shape_grid.nx as usize) - .checked_mul(w.shape_grid.ny as usize) - .ok_or("grid count overflow")?; - let count_shape_grid = if cells > 0 { - unsafe { *w.shape_grid.start.add(cells) as usize } - } else { - 0 - }; - let bits = w.shape_grid.x0.to_bits(); - data[50] = bits as u32; - data[51] = (bits >> 32) as u32; - let bits = w.shape_grid.y0.to_bits(); - data[52] = bits as u32; - data[53] = (bits >> 32) as u32; - let bits = w.shape_grid.cell.to_bits(); - data[54] = bits as u32; - data[55] = (bits >> 32) as u32; - data[56] = w.shape_grid.nx; - data[57] = w.shape_grid.ny; - data[58] = unsafe { append_ptr(&mut data, w.shape_grid.start, if cells > 0 { cells + 1 } else { 0 }) }?; - data[59] = unsafe { append_ptr(&mut data, w.shape_grid.items, count_shape_grid) }?; - let bits = w.shape_margin.to_bits(); - data[60] = bits as u32; - data[61] = (bits >> 32) as u32; - data[62] = unsafe { append_ptr(&mut data, w.path_xy, w.path_points as usize * 2) }?; - Ok(data) -} diff --git a/github_adapter.py b/github_adapter.py deleted file mode 100644 index d14bce0a90119eb0cb3ef09a17b361b300a8db7e..0000000000000000000000000000000000000000 --- a/github_adapter.py +++ /dev/null @@ -1,71 +0,0 @@ -"""Local bridge to unchanged native simulator; recipes never leave loopback.""" -import json,re,subprocess,time -from backend_policy import backend_args -from pathlib import Path -ROOT=Path(__file__).parent;REPO=ROOT/'github-simulator/source';BIN=REPO/'target/release/altd-sim' -def vector(n): - v=[] - for w,b in zip(n['weights'],n['biases']):v.extend(z for row in w for z in row);v.extend(b) - return v - -def execute(job,save,export): - c=job['config'];d=ROOT/'runs'/job['id'];x=job['template'];n=x['NeuralNetwork'];names=[f'sensor-{i}' for i in range(n['Shape'][0])] - training=c['mode']=='train';scratch=training and c['mutation']>0 - global_args,gpu_args=backend_args(c,BIN,scratch) - network={'shape':n['Shape'],'weights':n['Weights'],'biases':n['Biases'],'inputs':names,'outputs':['Steering','Acceleration']} - model={'sensor_layout':{'names':names,'sensors':x['Sensors']}} - snake=lambda name:re.sub(r'(?HF sign-in return - -

Returning sign-in to your training window. If that window was closed, close this tab and sign in again.

- diff --git a/index.html b/index.html index c53ba5d3412fef5a02148ae4267917e8d0dd5e0c..388f83282ef5b032e2a4da60e40860f9a06b5556 100644 --- a/index.html +++ b/index.html @@ -1,27 +1,26 @@ AI Learns To Drive -

AI Learns To Drive

Simulator by Zorro909 · UI and HF implementation by TiGa (seeker-cyber-maker).

Upstream simulator · Implementation fork · Game by ActivationFunction on Steam

Runner interface migration candidate — configurable single-run training, browser-local recipes and bounded batches. Network inspection and editing run in your browser. Track rendering and star evaluation are not connected.

Open the Protected beta app · Share this app address with testers; the HF repository page remains restricted. CPU is anonymous. Optional HF sign-in identifies account GPU requests; otherwise the limited anonymous allowance may apply. No personal token entry is requested.

+:root{color-scheme:dark;font:16px system-ui;background:#0c1420;color:#e5edf5}body{max-width:1400px;margin:auto;padding:22px}h1{font-size:32px;margin:0}h2{font-size:21px;margin:0 0 14px}small,.hint{color:#a2b4c8;line-height:1.6}.grid{display:grid;grid-template-columns:minmax(360px,.9fr) minmax(0,1.25fr);gap:24px}.card{background:#152233;border:1px solid #2a3d52;border-radius:16px;padding:22px;margin-top:20px}label{display:block;margin:12px 0 5px}input,select,button{font:inherit;background:#0c1725;color:#e5edf5;border:1px solid #49627c;border-radius:7px;padding:9px}input[type=number],select{width:100%;box-sizing:border-box}input[type=file]{max-width:100%;box-sizing:border-box}button{cursor:pointer}button.primary{background:#62dfb1;color:#102522;font-weight:700}button:disabled{opacity:.4;cursor:default}.row{display:grid;grid-template-columns:1fr 1fr;gap:15px}.reward{display:grid;grid-template-columns:1.7fr 1fr 70px 30px;gap:5px;margin:8px 0}.reward input{width:70px;box-sizing:border-box}a{color:#7ce4c3}canvas{width:100%;background:#09111b;border-radius:10px}#status{white-space:pre-wrap;line-height:1.7}table{width:100%;text-align:left;border-collapse:collapse;font-size:14px}td,th{padding:9px;border-bottom:1px solid #2a3d52}.pill{display:inline-block;background:#284539;border-radius:20px;padding:5px 12px;color:#91efc6;margin:6px 0}#error{color:#ffb5a2;white-space:pre-wrap}.toolbar{display:flex;gap:12px;align-items:center;flex-wrap:wrap;padding-bottom:16px;border-bottom:1px solid #2a3d52;margin-bottom:15px}.run-card{position:sticky;top:20px}details{margin-top:18px;border-top:1px solid #2a3d52;padding-top:14px}summary{cursor:pointer;font-weight:600;color:#c6d9e9}details[open]>summary{margin-bottom:12px}[hidden]{display:none!important}#map_note{font-size:14px}#runs{max-height:380px;overflow:auto}.table-scroll{overflow:auto}@media(max-width:850px){.grid{grid-template-columns:1fr}#drive .grid>section:nth-child(2){order:-1}.run-card{position:static}body{padding:16px}} +.tabs{display:flex;flex-wrap:wrap;gap:10px;margin:20px 0}.tabs button[aria-selected=true]{background:#62dfb1;color:#102522}.library{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:20px}.library input[type=text]{width:100%;box-sizing:border-box}.queue-row{display:flex;gap:8px;margin:8px 0}.queue-row select{flex:1}.pane{min-width:0}.map{min-height:300px;background:#09111b;border-radius:12px;padding:10px}.map canvas{display:block;width:100%;height:auto;max-height:320px;object-fit:contain}.map-legend{display:flex;gap:18px;flex-wrap:wrap;margin:10px 0;color:#c6d9e9;font-size:14px}pre{white-space:pre-wrap;overflow-wrap:anywhere}input[type=text]{width:100%;box-sizing:border-box}#editor_parameters input{max-width:240px} .eyebrow{color:#62dfb1;font-size:12px;letter-spacing:.18em;font-weight:800}.intro{font-size:19px;color:#b9cddd;max-width:820px;margin:10px 0 0}header{padding:12px 0 4px}header h1{font-size:clamp(30px,4vw,45px);letter-spacing:-.03em;margin-top:7px}.about{max-width:850px;font-size:14px;margin-top:15px}.about p{line-height:1.65}.card{box-shadow:0 10px 35px #050a1280}.tabs{position:sticky;top:0;z-index:3;background:#0c1420eF;padding:12px 0;border-bottom:1px solid #2a3d52}.tabs button{border-radius:12px;padding:11px 15px}.map-status{margin:7px 0;color:#dbe9f1;font-size:14px}#run{background:#62dfb1;color:#102522;font-weight:800;width:100%;margin-top:12px;padding:13px}.privacy-note{font-size:13px;line-height:1.5;color:#a2b4c8}.keep-result{display:flex;gap:10px;align-items:flex-start;padding:12px;border:1px solid #3b566b;border-radius:10px;background:#102030;line-height:1.4}.keep-result input{margin:3px 0 0;flex:none} .terrain{display:inline-block;width:11px;height:11px;border-radius:3px;margin-right:4px;background:#607e96}.terrain.dirt{background:#b89561}.terrain.ice{background:#b9d9ed}.terrain.start{background:#66e5aa} +
AI LEARNS TO DRIVE · TRAINING LAB

Train, inspect, repeat.

Pick a track and network, run a bounded training session, and keep your recipes in this browser.

About this beta and its data handling

Simulator by Zorro909; interface and hosting by TiGa. Inspired by the game by ActivationFunction. Built-in tracks are reconstructed replicas. Star challenges are not evaluated here.

Uploaded files are used for the requested run. Results are held briefly in application memory and deleted after verified download or expiry. Browser recipes contain settings only. Read the data policy.

Simulation

Training uses the settings on the Training runs & presets tab. Formula networks, steering and acceleration; speed and raycast sensors. Supported networks: Formula, steering and acceleration, speed/raycast sensors, 2–4 layers with up to 16 nodes.

-

Checking available backends…

+

Checking available backends…

-

CPU needs no sign-in. Checking optional GPU account connection…

+

CPU needs no sign-in. Checking optional GPU account connection…

Account help & troubleshooting

CPU works without an account. HF sign-in identifies GPU requests; it does not guarantee available allowance. Sign in before choosing files: sign-in reloads this page and clears selected files and unsaved edits. Saved recipes remain.

Your account token stays in page memory. Refreshing or closing this page disconnects the app. Disconnecting here does not sign you out of the HF website or delete job data.

Checks which account the GPU submission service sees. Runs automatically before GPU training; it does not check your remaining allowance.

Loading built-in replica tracks…

Built-in replicas are reconstructed editor-format layouts, not editor-authored or official packaged files. Star conditions are not evaluated.

-

Choose an uploaded file or use a network from the browser editor.

-

If unchecked, results expire 30 minutes after processing finishes. Repeat downloads do not extend that deadline. Full data handling & deletion policy

-

Run & results

+

Choose an uploaded file or use a network from the browser editor.

Uploads are processed for this run. By default, the server copy is deleted after the browser verifies the download.

This is optional and resets after each run. All results expire after five minutes. Data policy

+

Selected track & terrain

Loading selected track…

Asphalt Dirt Ice Start

Run & results

The browser keeps the received result in memory until discarded, replaced by a new run, or this page is closed. It is not saved to browser storage.

READY — no run started


 

Ready. Inputs remain on this device until you run the test.

-
Effective training settings
Generation results
Star challenges — not evaluated

The current runner has star-challenge reporting. Its evaluator and required statistics have not been ported to HF; no star result is inferred from a completed test.

Selected track & terrain

+
Effective training settings
Generation results
Star challenges — not evaluated

The current runner has star-challenge reporting. Its evaluator and required statistics have not been ported to HF; no star result is inferred from a completed test.

- +