Compare commits

...
6 Commits
Author SHA1 Message Date
irrlichtandClaude Opus 5 1d14076468 Drop the chunk palette; chunks are plain u16 tile arrays
The 64-entry per-chunk palette was a wire-format artifact and capped a chunk
at 64 distinct tiles. A Chunk is now Box<[u16; 1024]> with flags derived from
the id on lookup — the same layout a binary on-disk format would use.

Map path becomes the first CLI argument; assets/map_large is a generated
256×256 stress map (loads in ~0.5 ms release). The snapshot test takes
SNAPSHOT_MAP.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 23:40:52 +02:00
irrlichtandClaude Opus 5 5a055c048d Switch to 16-bit truecolor framebuffer and PNG assets
pbio moves to git.bnd.wtf/irrlicht/pbio.git branch truecolor16: the
framebuffer is now RGB565 u16, no palette. Assets are loaded from PNG via
the png crate and quantized to RGB565 on load; transparency comes from the
alpha channel as a per-tile opacity mask, so any color (black included) can
be opaque. The TGA loader and the palette-order fragility are gone.

overworld.png is the unmodified Mini-Medieval Overworld sheet (same 24-column
layout as the old TGA, so map tile ids are unchanged); entities.png holds the
player sprite extracted from the old entities.tga.

Adds an ignored snapshot test that renders the first frame headlessly to a
PNG for eyeballing the renderer without a window.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 23:17:41 +02:00
irrlichtandClaude Opus 5 64a6e1d42d Rewrite notes for the single-player architecture
Drop protocol.md; rewrite the tick-loop note around the game-loop-driven sim
and the intent model; rewrite the roadmap (network items removed, sim/game
crate layout, save/load and NPC blocking added); fix crate paths in the
remaining notes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 23:00:19 +02:00
irrlichtandClaude Opus 5 f594f12f35 Embed the sim in the game binary; drop networking
The client becomes the game: it owns a Sim instance and advances it with a
fixed-timestep accumulator (24 Hz ticks, rendering at frame rate). Rendering,
pathfinding and collision read the World directly instead of a chunk cache.

Sim::pending (window slot maps, late rule, horizon) is replaced by one intent
per entity for the next movement window — set_action replaces, executing the
window consumes. The whole client prediction/reconciliation machinery
(tick estimation, RTT lead, retraction, replay) is gone with the latency it
was built for; click-to-move now syncs the route against the sim position
after every window.

Removed: server, netsim, shared (wire types), client/net.rs. client renamed
to game. Headless tests cover the intent model.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 22:56:30 +02:00
irrlichtandClaude Opus 5 303c587aee Extract headless sim crate from server
Move chunk, entity, world, map, sim and load_world into a new sim library
crate together with the game rules from shared (player_action, action_delta,
delta_action, step_allowed, tile_collidable, tick constants). shared keeps
only the wire types and re-exports the rules, so server and client compile
unchanged. First step toward a local single-player build.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 22:52:10 +02:00
irrlichtandClaude Opus 5 5610c2c343 Point pbio dependency at git.bnd.wtf
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 22:50:44 +02:00
40 changed files with 1336 additions and 2413 deletions
Generated
+94 -29
View File
@@ -18,6 +18,12 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]] [[package]]
name = "ahash" name = "ahash"
version = "0.8.12" version = "0.8.12"
@@ -228,15 +234,6 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "client"
version = "0.1.0"
dependencies = [
"bytemuck",
"pbio",
"shared",
]
[[package]] [[package]]
name = "codespan-reporting" name = "codespan-reporting"
version = "0.12.0" version = "0.12.0"
@@ -328,6 +325,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "crc32fast"
version = "1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01a7799fd6b852db0e61728dde9a204c423b44d689dbd432522543614b490e78"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.21" version = "0.8.21"
@@ -398,12 +404,32 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "fdeflate"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
dependencies = [
"simd-adler32",
]
[[package]] [[package]]
name = "find-msvc-tools" name = "find-msvc-tools"
version = "0.1.4" version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
[[package]]
name = "flate2"
version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb"
dependencies = [
"crc32fast",
"miniz_oxide 0.9.1",
"zlib-rs",
]
[[package]] [[package]]
name = "foldhash" name = "foldhash"
version = "0.1.5" version = "0.1.5"
@@ -443,6 +469,15 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
[[package]]
name = "game"
version = "0.1.0"
dependencies = [
"pbio",
"png",
"sim",
]
[[package]] [[package]]
name = "gethostname" name = "gethostname"
version = "1.1.0" version = "1.1.0"
@@ -798,6 +833,26 @@ dependencies = [
"paste", "paste",
] ]
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
"simd-adler32",
]
[[package]]
name = "miniz_oxide"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c"
dependencies = [
"adler2",
"simd-adler32",
]
[[package]] [[package]]
name = "naga" name = "naga"
version = "27.0.3" version = "27.0.3"
@@ -854,10 +909,6 @@ dependencies = [
"jni-sys 0.3.0", "jni-sys 0.3.0",
] ]
[[package]]
name = "netsim"
version = "0.1.0"
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.19" version = "0.2.19"
@@ -1168,7 +1219,7 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]] [[package]]
name = "pbio" name = "pbio"
version = "0.1.0" version = "0.1.0"
source = "git+https://codeberg.org/irrlicht/rust-pbio.git#2d814718200a5585216f90766693bca1582b3c0e" source = "git+https://git.bnd.wtf/irrlicht/pbio.git?branch=truecolor16#8c647d3700fe3196d0fd511e80bc3c60b0b8f1ae"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"pollster", "pollster",
@@ -1220,6 +1271,19 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
[[package]]
name = "png"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61"
dependencies = [
"bitflags 2.10.0",
"crc32fast",
"fdeflate",
"flate2",
"miniz_oxide 0.8.9",
]
[[package]] [[package]]
name = "polling" name = "polling"
version = "3.11.0" version = "3.11.0"
@@ -1471,27 +1535,22 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "server"
version = "0.1.0"
dependencies = [
"bytemuck",
"shared",
]
[[package]]
name = "shared"
version = "0.1.0"
dependencies = [
"bytemuck",
]
[[package]] [[package]]
name = "shlex" name = "shlex"
version = "1.3.0" version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "sim"
version = "0.1.0"
[[package]]
name = "simd-adler32"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
[[package]] [[package]]
name = "simd_cesu8" name = "simd_cesu8"
version = "1.1.1" version = "1.1.1"
@@ -2421,3 +2480,9 @@ dependencies = [
"quote", "quote",
"syn", "syn",
] ]
[[package]]
name = "zlib-rs"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b268e58e7c693d7c271f93ffc4ba3b380412554231c85bf61ca7af91042a4112"
+1 -1
View File
@@ -1,3 +1,3 @@
[workspace] [workspace]
members = ["shared", "client", "server", "netsim"] members = ["sim", "game"]
resolver = "2" resolver = "2"
+256
View File
@@ -0,0 +1,256 @@
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,25,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,25,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,25,146,146,25,146,25,25,75,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,146,146,146,146,146,25,25,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,25,25,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,25,25,75,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,146,146,146,146,146,25,25,74,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,25,146,146,146,146,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,75,74,25,74,75,25,25,25,146,25,25,146,146,146,146,146,146,25,146,146,146,25,146,25,74,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,25,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,74,74,74,74,75,75,25,25,75,75,74,146,25,146,146,146,146,146,146,146,146,146,25,25,74,75,74,75,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,146,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,146,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,25,25,25,146,146,146,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,75,75,75,75,75,25,25,74,74,74,75,74,146,146,146,146,146,146,146,146,146,146,146,74,75,74,75,74,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,25,25
25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,146,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,146,146,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,25,146,146,74,75,74,74,75,74,25,25,25,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,75,75,74,25,75,25,146,74,75,75,146,146,146,146,146,146,146,146,146,74,75,74,25,75,74,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,25,146
25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,25,146,25,25,25,25,25,25,146,146,25,146,25,25,25,25,25,25,25,75,75,75,146,146,146,146,75,75,75,75,75,74,75,74,75,75,146,146,146,146,25,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,25,74,75,74,25,74,25,25,75,75,74,75,75,75,75,74,146,146,146,146,146,146,146,146,146,75,75,75,74,25,25,75,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,146
25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,25,146,25,25,25,25,25,25,25,75,75,74,75,74,25,25,146,75,75,75,74,146,75,75,75,74,25,146,146,146,146,146,146,146,146,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,75,74,75,25,75,74,25,25,75,74,74,74,74,75,75,74,75,146,146,146,146,146,146,146,75,74,74,75,74,75,74,75,25,25,25,25,25,25,25,146,146,146,146,146,146,25,25,146,146,146,25,25,25,25,25,146,146,25,146,146,146,146,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,146,146,146,146,146,146,146,146,146,25
25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,146,146,25,146,25,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,75,74,75,75,25,25,25,74,75,75,74,74,74,75,74,146,146,25,146,146,146,146,146,25,25,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,25,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,74,75,75,75,74,75,75,25,25,74,74,25,74,74,25,75,75,74,146,25,146,146,146,146,146,75,74,74,74,74,74,75,74,25,25,25,25,25,25,25,146,25,146,25,146,146,146,146,146,146,146,25,25,25,25,25,146,146,25,146,146,146,25,146,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146
25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,146,146,146,146,25,25,146,25,146,75,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,146,74,25,25,25,25,74,75,25,25,74,74,74,146,146,146,25,146,146,146,25,146,25,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,74,74,74,74,75,74,74,74,74,75,75,25,25,25,25,74,74,75,25,146,146,146,146,146,146,146,75,74,74,75,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,25,146,146,146,25,25,75,74,74,75,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,146,146,146,25
25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,146,25,25,25,75,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,74,146,25,75,146,25,25,25,75,25,74,74,75,74,75,146,146,25,146,146,146,146,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,75,75,75,74,74,75,25,25,75,75,74,25,25,25,75,74,75,75,146,25,25,146,146,146,146,146,74,74,74,75,25,25,25,25,25,25,25,25,25,25,146,25,146,25,25,146,146,146,146,146,74,74,74,74,75,75,75,75,74,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,25,146,146,146,146,146,146,146,146,146,146
25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,25,25,25,75,74,74,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,75,74,25,75,146,146,146,75,75,25,74,75,75,74,75,146,146,146,146,25,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,25,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,74,74,74,75,74,75,25,25,25,25,25,75,74,74,25,74,75,146,146,146,146,146,146,146,74,75,75,74,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,25,146,146,74,75,75,75,74,74,75,75,75,75,74,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25
25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,25,74,75,74,74,74,25,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,74,75,75,74,74,74,74,75,25,25,74,146,146,75,74,146,146,146,25,146,146,146,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,74,75,74,74,74,75,75,75,25,25,25,25,75,75,25,25,74,25,146,146,146,146,146,146,146,75,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,146,25,25,146,75,74,75,75,75,25,146,146,146,146,146,146,25,25,25,25,25,25,25,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,146,25,146,146,146,146
25,25,25,25,25,25,25,25,25,25,146,146,146,74,74,75,75,146,146,25,74,74,74,75,74,75,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,74,74,74,146,74,74,74,75,75,146,74,75,25,146,75,74,146,146,146,146,25,146,146,146,146,146,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,74,74,74,74,75,75,74,25,25,25,25,75,75,74,74,74,75,25,25,25,25,25,25,25,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,146,75,75,75,74,74,75,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,146,25,146
25,25,25,25,25,25,25,25,25,25,25,146,146,74,74,74,74,146,146,146,146,74,74,74,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,74,146,146,146,75,75,74,74,146,74,75,25,25,75,74,75,146,25,146,146,25,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,25,146,146,25,146,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,25,74,75,75,25,25,25,25,25,25,25,75,75,75,74,25,75,25,25,25,25,25,25,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,74,74,74,74,74,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,25,146,146,146,146,146,146
25,25,25,25,25,25,25,25,25,25,75,74,75,75,75,74,74,74,146,146,146,75,25,25,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,75,146,146,146,146,74,75,75,146,74,75,74,74,75,75,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,146,146,25,146,146,146,25,146,146,25,25,25,25,25,25,146,25,146,146,25,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,25,25,25,25,25,25,25,25,25,25,25,25,75,75,25,25,25,25,74,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,25,25,25,75,74,25,146,75,74,75,74,74,75,75,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,146,146,146
25,25,25,25,25,25,25,25,25,25,25,74,74,146,146,146,74,75,74,74,74,75,146,25,74,74,25,25,25,25,25,25,25,25,25,75,75,75,75,74,25,25,25,25,25,146,74,74,146,146,146,25,75,74,25,146,25,25,75,75,75,75,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,146,146,25,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,75,74,75,74,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,74,74,74,74,75,74,75,75,146,146,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,25,25
25,25,25,25,25,25,25,25,25,25,25,25,75,146,25,146,75,74,75,75,75,74,75,25,75,74,25,25,25,25,25,25,25,25,74,75,74,75,75,75,25,25,25,25,25,146,75,146,146,146,25,25,25,146,146,146,25,25,74,74,74,74,74,25,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,146,25,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,74,75,75,75,75,74,75,74,146,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,25
25,25,25,25,25,25,25,25,25,25,25,146,74,74,146,146,146,74,74,74,75,75,74,25,75,75,25,25,25,25,25,25,75,74,74,74,74,74,74,74,25,25,74,25,75,75,75,25,146,146,146,25,146,146,146,146,25,25,74,75,74,74,74,74,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,25,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,75,75,146,74,74,75,75,75,74,25,25,75,74,74,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25
25,25,25,25,25,25,25,25,25,25,146,146,146,74,146,146,25,74,74,75,75,74,74,74,74,74,25,25,25,25,25,75,75,74,75,75,75,74,75,25,74,75,75,75,74,74,146,146,146,146,146,146,25,146,146,25,25,25,75,75,75,75,74,74,74,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,75,75,75,75,74,74,75,25,25,25,74,75,146,146,25,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,74,75,74,75,74,74,25,25,25,25,25,25,25,75,75,74,74,75,75,74,75,25,75,75,74,74,74,25,25,146,146,146,146,146,146,146,25,25,25,25,74,75,74,74,75,75,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,75,74,25,146,74,74,75,74,75,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,75,75,75,146,146,146,146,75,74,74,75,74,74,75,74,25,25,75,25,75,75,75,74,74,74,74,74,74,74,25,74,75,25,75,75,75,25,25,25,25,146,25,25,25,25,25,25,146,74,146,75,74,74,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,25,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,25,146,25,146,146,25,25,25,25,146,146,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,75,74,75,75,75,75,74,75,25,75,74,75,25,25,25,25,25,75,74,25,25,25,146,146,146,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,146,25,146,25,25,25,25,25,25,25,74,75,146,146,146,146,75,75,75,75,74,75,74,75,25,25,74,74,74,75,74,75,25,75,75,75,75,25,25,74,75,74,74,74,75,25,25,25,25,25,25,25,25,25,25,75,74,74,74,74,75,74,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,146,146,146,25,146,25,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,25,146,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,75,75,75,74,74,75,74,75,75,74,75,75,25,75,25,75,75,74,74,74,25,25,25,146,146,146,25,146,25,25,25,25,25,25,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
146,146,25,146,146,25,25,25,25,25,75,75,74,25,146,146,146,146,75,75,74,74,74,75,25,25,25,75,74,75,75,75,75,25,75,75,25,25,146,74,75,75,75,146,146,75,25,25,25,25,25,25,25,25,25,74,75,75,75,146,74,75,75,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,146,146,146,146,146,25,25,25,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,75,74,74,75,74,74,74,75,75,75,75,75,25,75,75,75,74,25,25,25,25,25,25,25,25,146,146,25,25,25,25,25,25,146,25,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
146,146,146,146,75,75,75,74,25,75,74,75,25,146,146,146,146,146,75,75,75,74,75,75,75,25,75,75,75,75,25,25,25,25,25,25,25,146,74,75,75,75,75,146,146,146,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,74,74,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,25,146,146,25,146,146,146,146,25,146,146,25,146,146,146,146,146,146,146,146,146,25,25,25,146,146,146,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,74,75,75,75,74,75,74,74,75,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,146,146,146,74,74,25,74,25,75,75,75,75,25,25,25,146,75,74,75,75,74,74,74,74,75,74,74,75,25,25,25,25,25,25,25,146,74,75,75,74,74,146,25,146,146,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,146,146,146,146,25,146,146,25,146,146,146,25,146,25,25,25,146,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,146,146,146,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,25,75,74,74,75,74,75,74,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
146,25,146,146,74,75,74,74,74,25,75,74,75,25,25,25,25,74,75,75,74,75,75,74,25,74,74,75,75,25,25,25,75,74,74,146,75,75,75,75,146,74,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,25,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,74,74,74,74,75,74,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
146,146,146,146,75,75,74,75,75,74,75,75,75,25,25,25,75,74,75,74,75,74,75,74,25,25,25,75,74,74,74,75,75,74,74,75,75,75,75,75,75,74,75,75,146,25,146,146,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,25,146,146,25,25,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,74,74,74,75,75,74,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,74,75,75,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,146,25,25,25,74,75,74,75,75,74,75,74,25,25,25,74,74,75,25,75,74,74,75,74,74,25,25,25,25,25,75,74,74,75,25,74,74,75,74,75,146,75,74,146,146,146,146,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,75,25,74,75,74,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,74,25,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,74,74,75,74,74,75,75,75,25,25,25,25,25,74,74,25,74,75,74,75,74,25,25,25,25,25,25,74,74,74,146,25,74,74,74,25,75,146,146,146,146,146,25,146,146,25,25,25,25,25,146,25,25,146,146,25,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,146,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,75,25,25,25,74,146,146,146,75,74,25,146,25,25,25,25,25,25,25,25,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,75,75,75,74,74,74,74,74,74,25,25,25,25,25,75,74,74,75,25,25,75,25,25,25,25,75,74,75,75,25,25,146,25,146,75,74,75,146,25,146,25,146,146,146,25,25,25,146,25,146,146,146,25,146,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,25,25,146,146,25,146,146,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,146,146,74,75,146,146,74,74,74,75,75,74,74,25,25,25,74,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,75,75,74,75,75,75,75,74,74,25,25,25,25,25,25,25,75,25,25,25,25,25,25,25,25,75,75,74,25,25,25,146,25,146,146,146,74,74,146,146,146,146,146,146,25,25,25,146,146,146,146,146,146,25,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,146,25,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,25,25,25,25,25,25,25,25,25,25,74,25,74,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,25,146,146,25,25,25,25,25,25,25,146,146,146,74,75,25,74,74,146,75,74,74,74,74,74,74,75,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,74,74,74,74,74,25,25,25,25,25,25,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,25,146,74,146,146,25,25,146,146,25,25,146,146,146,146,146,146,146,146,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,74,75,74,146,25,25,25,25,25,25,25,25,25,74,74,74,75,74,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,146,146,146,74,74,75,146,74,74,75,75,25,75,74,75,25,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,75,25,25,25,25,25,25,25,25,25,25,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,74,146,25,146,146,146,25,25,25,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,25,25,25,25,25,25,25,25,25,75,75,74,146,146,146,25,25,25,25,25,25,25,74,25,25,75,75,75,74,75,75,75,25,25,146,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,75,146,146,25,25,74,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,74,75,74,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,74,75,74,75,146,146,146,146,25,25,25,146,146,146,25,146,146,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,146,146,146,25,25,25,25,25,25,25,25,74,74,74,146,146,146,146,146,25,25,25,25,25,74,25,25,75,75,75,74,25,25,75,74,25,25,25,146,146,146,25,146,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,75,25,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,74,146,146,146,146,25,25,25,25,25,25,25,146,146,146,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,25,25,25,25,25,25,25,25,74,75,74,74,146,146,146,25,25,25,25,75,75,74,25,75,75,75,74,74,25,75,74,75,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,74,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,75,25,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,146,146,75,75,74,146,25,25,146,75,74,75,74,75,75,74,25,75,75,75,75,25,74,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,74,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,146,146,146,146,146,146,146,25,146,25,146,146,146,25,25,25,25,25,25,25,25,146,146,146,25,146,146,25,25,25,25,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,146,146,74,75,74,146,74,146,75,74,74,74,25,25,74,25,74,74,75,75,25,25,25,75,25,25,25,25,74,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,146,146,146,146,25,25,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,146,25,75,74,75,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,75,74,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,25,25,146,146,146,146,146,25,25,146,25,25,146,146,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,146,146,25,146,146,146,25,146,25,25,25,146,25,146,25,146,25,146,25,25,25,25,25,25,25,25,25,146,146,75,74,146,75,75,75,74,74,75,25,25,74,25,74,74,75,25,25,25,74,75,74,75,25,25,74,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,146,146,146,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,25,74,74,25,146,146,146,146,146,146,146,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25
25,25,74,74,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,146,146,25,25,146,146,146,146,25,146,146,146,25,146,25,146,146,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,25,146,25,25,146,146,25,146,25,25,25,25,25,146,146,25,25,146,146,75,74,75,74,74,74,74,75,74,74,25,25,25,74,25,25,74,25,25,25,25,25,75,75,74,75,75,75,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,25,25,146,25,25,25,25,25,25,146,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,75,75,75,146,74,146,146,146,25,146,146,146,25,25,25,146,146,146,146,146,146,25,146,146,146,146,146,146,146,25,25,25,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25
25,25,25,75,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,25,25,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,146,146,146,25,146,25,146,146,146,146,146,25,25,146,25,146,146,146,146,146,146,25,25,146,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,25,25,25,25,146,25,25,25,25,25,146,146,146,146,146,146,146,146,75,75,25,146,75,75,75,74,75,75,25,75,75,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,74,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,75,74,74,74,146,146,146,146,146,146,146,146,25,25,25,146,25,25,146,146,146,146,146,146,146,146,146,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25
25,25,25,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,75,25,75,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,146,146,25,25,25,146,25,25,25,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,25,146,146,146,74,74,146,146,74,75,75,75,75,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,25,146,25,146,146,146,146,25,146,146,25,25,25,25,25,25,25,74,74,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,74,74,75,74,75,146,146,146,25,146,146,25,25,25,146,146,146,25,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,75,74,75,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,146,25,146,25,25,25,25,25,25,25,146,146,146,146,25,146,25,25,25,25,25,25,25,25,146,146,146,25,25,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,25,25,25,146,146,25,25,75,146,146,25,25,25,74,75,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,74,25,75,74,25,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,74,75,74,75,25,25,25,25,25,25,146,25,25,25,25,25,25,25,74,74,74,74,74,74,74,74,74,146,25,146,25,146,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,146,25,146,146,146,146,146,25,146,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,74,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,25,25,146,146,146,146,146,146,146,146,146,25,146,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,25,75,74,75,25,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,75,74,75,75,74,25,25,25,25,146,146,146,146,25,146,25,25,25,25,25,74,75,75,74,75,74,75,75,75,74,146,146,25,146,146,25,146,146,146,146,25,25,146,146,146,146,146,146,146,146,146,146,25,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25
25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,74,75,146,25,146,25,146,146,25,25,25,146,25,25,25,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,25,25,25,25,146,146,146,146,146,146,146,146,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,74,25,25,25,74,25,25,146,146,146,25,146,146,146,74,75,25,25,25,25,75,75,74,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,25,25,25,75,74,75,74,75,75,75,74,74,146,146,146,146,146,25,25,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25
25,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,75,74,146,75,75,75,25,25,25,75,74,75,74,74,75,75,75,75,74,75,146,74,74,75,146,146,25,25,146,25,146,146,25,25,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,25,25,25,146,146,146,25,146,146,146,146,146,146,25,146,25,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,74,25,74,75,75,25,25,146,146,25,74,75,75,75,75,75,74,75,25,74,74,74,75,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,25,25,74,74,75,75,75,74,75,75,74,74,146,146,146,146,146,146,25,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25
25,25,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,75,75,74,74,74,74,146,25,25,74,74,74,75,75,74,74,74,74,74,75,74,75,74,75,146,146,25,146,146,146,146,146,146,25,25,146,25,25,146,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,25,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,25,25,25,146,25,146,146,146,146,146,146,25,146,146,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,75,146,75,74,75,75,25,146,146,74,75,75,74,75,75,75,74,74,74,74,75,74,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,74,25,74,74,75,74,75,75,75,75,146,25,146,146,146,146,146,25,146,25,25,146,146,146,146,146,146,146,146,146,25,146,146,146,74,74,146,146,75,25,146,25,25,25,25,25,25,25,25,25
25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,74,146,146,146,146,25,74,74,75,74,74,75,74,25,25,146,74,75,74,75,74,74,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,25,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,75,75,75,74,75,75,75,74,146,74,75,74,75,74,75,74,75,75,74,74,74,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,25,146,146,25,25,25,75,74,74,74,146,146,74,75,74,25,25,146,146,146,146,146,146,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,74,74,74,74,74,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,146,146,146,146,146,146,75,74,74,74,74,74,74,25,74,74,75,75,75,74,74,25,146,146,146,146,146,25,146,146,146,146,146,146,25,25,25,25,146,146,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,146,146,25,146,25,146,146,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,146,146,146,25,146,146,25,146,146,146,146,25,25,25,25,25,25,25,146,25,146,146,75,74,75,74,74,74,75,25,75,74,74,75,74,75,75,75,75,75,74,74,74,25,25,25,25,146,146,146,146,146,146,146,146,146,25,146,146,146,146,25,25,25,74,75,74,75,75,75,146,25,146,146,146,146,146,146,146,25,146,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,75,25,25,74,74,25,25,25,146,25,25,25,25,25,25,25
25,25,25,146,146,146,146,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,75,74,75,74,75,74,75,25,75,75,75,74,74,75,75,146,146,146,146,25,146,146,146,146,146,146,146,25,25,25,25,25,146,146,146,25,146,146,146,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,25,146,146,146,146,25,25,25,25,25,25,25,25,146,146,25,146,74,74,75,74,75,74,74,74,74,75,75,75,25,75,75,75,74,74,74,74,74,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,75,74,25,25,25,146,25,25,25,146,25,146,146,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,75,75,25,25,25,25,146,146,146,25,25,25,25,25,25,25
25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,25,146,146,75,74,74,75,74,25,25,25,74,75,74,74,146,74,146,146,146,146,146,25,146,25,146,146,146,146,146,25,25,25,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,146,146,146,146,25,146,146,25,25,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,146,146,25,25,25,25,25,25,25,25,146,25,146,75,146,146,75,74,75,75,74,75,74,74,75,25,74,75,75,75,74,75,146,146,146,146,25,25,146,146,146,146,146,146,146,25,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,146,146,146,146,75,74,146,146,146,146,146,75,74,75,25,25,25,25,25,146,146,25,25,25,25,25,25,25
25,25,25,25,146,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,146,75,74,74,74,74,74,75,25,25,25,74,25,25,146,25,146,146,146,146,25,25,25,25,25,146,146,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,25,25,25,25,25,146,25,146,146,25,146,146,146,146,146,146,146,146,146,25,146,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,146,75,146,146,75,146,74,75,74,75,146,74,75,75,74,74,74,74,74,75,74,74,74,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,146,146,75,74,25,146,146,146,25,75,75,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25
25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,74,74,25,74,75,74,75,74,25,146,75,25,25,25,25,25,146,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,25,146,146,25,25,146,146,146,146,146,146,146,146,146,146,25,146,146,146,25,25,146,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,146,146,74,75,75,74,74,74,146,25,146,146,75,74,75,74,74,25,75,74,74,75,74,75,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,74,75,75,74,25,25,74,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,25,146,146,75,74,75,75,75,75,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,75,75,74,74,75,74,146,25,146,146,146,74,74,75,74,25,25,25,74,74,74,75,74,74,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,75,75,74,74,74,74,25,25,25,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,25,146,146,146,25,74,74,74,146,74,146,25,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,146,146,146,25,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,74,75,74,146,146,146,74,75,74,74,74,75,25,25,146,75,75,74,75,74,75,75,146,146,146,146,146,146,146,146,146,146,146,25,25,146,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,74,74,74,74,74,74,74,25,25,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,25,25,146,25,25,25,25,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,25,146,146,146,146,25,25,146,146,146,146,146,146,146,25,25,25,25,146,146,25,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,146,146,146,146,146,25,75,74,75,75,74,25,25,25,25,146,75,146,74,75,75,74,146,146,146,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,75,75,75,74,75,74,25,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,146,146,146,146,146,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,25,146,146,146,25,25,146,146,146,146,146,146,146,146,146,25,25,146,146,146,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,74,74,74,74,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,146,146,25,146,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,25,74,75,75,75,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,25,25,25,25,25,146,146,25,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,25,146,25,25,146,146,146,146,25,146,146,146,25,25,146,146,146,146,146,146,146,146,25,25,25,146,146,146,146,146,146,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,74,75,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,75,75,74,74,74,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,74,74,75,74,74,74,74,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,146,146,146,146,146,25,25,25,25,25,146,25,146,25,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,146,146,25,146,146,25,146,25,146,25,25,25,146,75,74,74,75,25,25,25,25,25,25,25,25,146,146,146,146,146,74,74,74,25,75,25,25,25,25,25,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,146,25,25,146,146,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,75,75,74,75,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,146,146,25,25,75,75,75,75,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,75,75,146,75,25,25,25,25,25,25,25,75,25,25,146,146,25,74,75,75,25,74,74,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,25,25,146,146,146,25,25,146,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,75,74,75,75,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,25,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,74,74,74,75,25,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,25,146,25,74,25,25,74,74,74,74,74,74,75,74,75,74,75,74,74,75,74,75,75,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,25,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,74,74,74,146,25,25,25,25,25,146,25,25,146,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,146,25,146,146,25,25,25,25,25,74,146,25,146,25,146,146,146,146,146,146,146,146,146,146,25,146,146,75,75,75,25,25,25,25,74,74,75,74,75,75,74,74,25,75,75,75,74,74,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,146,25,146,25,146,146,25,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,25,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,74,74,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,75,25,75,25,25,25,25,74,74,74,74,74,75,74,74,74,75,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,146,146,146,146,25,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,25,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,74,74,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,74,75,25,25,75,25,25,75,75,74,74,75,75,25,25,25,25,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,146,25,146,146,25,25,25,25,146,146,146,25,25,25,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,25,146,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,146,75,75,75,146,146,146,146,25,146,146,146,146,146,146,146,25,146,74,75,74,75,74,25,25,25,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,25,25,25,25,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25
25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,74,25,74,25,25,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,25,146,146,146,146,146,25,146,146,146,146,146,146,146,146,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,146,75,74,74,146,146,146,146,146,146,146,146,146,146,146,146,146,146,74,75,75,75,74,25,25,25,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,25,146,146,25,146,146,146,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25
25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,25,25,74,75,25,74,75,74,75,25,74,74,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,25,146,146,146,146,25,25,146,25,146,25,146,146,146,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,146,146,146,75,75,75,146,146,146,146,25,146,146,146,146,146,146,146,25,25,25,25,75,75,75,74,75,74,74,74,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,25,146,25,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25
146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,74,74,75,75,74,75,25,25,75,75,75,75,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,25,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,146,146,146,146,146,75,75,74,146,146,146,146,146,25,146,146,146,146,25,146,146,146,146,146,146,25,75,75,75,75,25,25,25,25,25,25,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,146,146,25,25,146,146,146,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25
146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,75,74,75,74,75,25,25,25,74,75,74,74,75,75,25,25,75,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,25,146,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,146,146,74,75,75,25,146,146,146,146,146,25,146,146,146,25,146,146,25,146,146,146,146,75,75,74,74,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,25,25,146,146,146,146,146,146,146,146,25,25,146,146,146,25,25,25,25,25,25,25
146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,75,75,75,75,74,75,25,25,25,74,74,74,75,75,74,25,146,75,74,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,25,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,146,25,25,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,146,146,25,146,74,146,146,146,146,146,146,25,146,146,146,146,146,146,146,25,146,146,146,146,75,74,75,75,25,25,25,25,25,25,25,146,146,146,25,25,25,25,25,25,25,25,25,146,25,25,146,146,146,146,146,25,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,25,146,146,25,146,25,25,25,146,146,146,25,146,25,146,146,146,146,146,25,25,25,25,25,25
146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,74,75,75,74,74,25,25,25,25,25,75,74,74,75,74,75,75,74,75,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,25,146,146,146,25,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,146,25,25,146,25,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,146,146,146,74,146,146,146,146,25,25,146,146,146,146,146,146,146,146,25,146,146,146,146,74,74,75,75,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,146,146,146,146,25,146,146,25,25,25,146,146,146,25,146,25,146,146,146,146,146,25,25,25,25,25
146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,75,74,75,25,25,25,25,25,25,75,74,75,75,146,146,146,74,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,25,146,25,146,25,146,146,25,146,146,25,146,25,25,25,25,25,146,146,146,25,25,146,146,146,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,146,75,74,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,75,74,75,75,25,25,74,25,25,25,146,25,25,25,25,25,25,25,25,146,25,146,146,146,146,25,146,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,25,146,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25
25,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,25,74,75,75,25,25,25,25,25,25,74,75,74,74,75,74,74,74,25,146,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,74,75,74,74,75,75,25,146,146,146,146,146,25,146,146,146,146,146,146,146,146,74,75,75,146,146,146,74,75,146,25,146,146,146,25,25,25,25,25,146,146,25,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,146,146,25,25,146,146,146,146,146,146,146,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,25,74,75,75,25,25,25,25,25,74,74,74,146,75,25,75,75,75,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,146,146,25,146,146,25,25,25,25,25,146,146,146,146,25,146,146,25,146,146,146,146,146,146,25,146,25,25,25,25,25,25,75,74,75,74,74,74,74,74,74,74,75,75,74,75,146,146,146,146,146,146,146,146,146,146,25,25,146,74,146,25,146,146,146,74,74,75,146,146,146,146,25,25,25,25,146,146,146,146,25,146,146,146,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,25,25,25,146,146,146,146,25,25,25,146,146,146,146,25,146,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,74,75,74,75,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,25,25,25,25,25,25,146,25,146,25,25,146,146,146,146,146,146,25,146,146,146,146,25,25,25,25,146,25,25,25,25,25,25,25,146,146,146,146,146,25,25,146,146,25,146,146,25,146,146,146,25,25,25,25,25,25,25,74,25,25,25,25,75,74,75,74,74,74,75,75,74,25,25,146,146,146,146,146,146,146,146,25,25,146,146,146,25,146,146,146,75,75,75,146,146,146,146,146,25,25,25,146,146,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,25,146,25,25,25,25,146,146,146,146,146,146,25,146,146,146,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,75,75,74,75,75,74,74,75,25,25,25,25,25,25,25,146,25,146,146,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,146,25,146,146,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,25,25,146,146,146,146,146,146,25,146,25,25,146,25,146,75,75,25,25,146,25,146,74,75,75,75,74,75,74,146,146,146,25,146,146,146,146,25,146,25,146,146,146,146,146,146,146,74,75,75,75,146,146,25,146,25,25,25,25,146,146,25,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,146,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,25,25,25,25,146,146,146,146,146,146,146,25,146,146,146,25,25,25,25,25
25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,75,74,25,25,25,25,25,25,25,25,25,25,25,25,75,74,25,75,74,74,75,75,75,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,146,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,146,25,25,146,146,146,146,146,146,25,25,25,146,25,146,146,74,74,25,74,75,75,25,146,74,75,74,25,25,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,75,146,146,146,146,25,25,146,146,146,146,146,25,146,25,25,146,146,146,25,146,25,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,25,146,146,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,146,146,25,25,146,146,146,25,146,146,146,25,25,25,25,25
25,25,25,25,25,25,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,146,25,146,25,25,25,25,25,25,25,75,74,75,75,75,74,25,25,25,25,25,25,25,75,74,75,74,75,75,75,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,146,146,146,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,25,25,25,146,146,146,146,146,146,146,25,25,25,146,74,74,74,75,74,74,75,74,75,74,75,25,25,25,146,25,146,25,146,146,146,146,146,146,146,146,146,25,146,146,25,146,146,146,146,146,146,75,25,146,25,25,25,25,146,146,146,146,146,146,146,146,25,146,25,25,146,146,146,25,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25
25,25,25,25,25,146,146,146,146,25,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,74,75,25,25,74,75,75,75,25,25,74,74,74,74,75,75,75,74,74,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,75,74,74,25,75,75,25,75,75,75,25,25,25,25,25,25,146,146,146,25,146,146,146,146,25,146,146,25,146,146,146,25,146,146,146,146,146,75,74,75,74,75,75,146,146,146,146,146,146,146,146,146,146,25,146,25,146,146,146,146,146,146,25,146,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,25,146,25,25,25,25,25,25,25
25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,75,75,74,25,25,25,74,74,74,25,74,75,25,75,25,75,74,75,74,74,25,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,74,74,75,74,74,74,74,75,75,75,25,25,25,25,25,25,25,25,146,25,25,146,25,146,146,146,146,146,25,25,146,146,146,146,146,146,146,25,74,75,75,74,74,74,75,146,146,146,74,75,75,146,146,146,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,75,74,25,25,25,25,74,25,75,75,75,74,25,74,74,75,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,25,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,74,75,75,74,74,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,146,146,146,146,146,146,146,146,25,75,75,75,75,74,75,74,146,146,75,74,74,75,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,75,75,74,74,74,25,25,25,75,25,75,75,75,74,74,75,74,74,74,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,74,74,75,75,75,74,74,74,75,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,25,75,75,75,74,75,74,74,75,25,146,25,146,75,74,146,146,146,25,25,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,25,25,75,74,75,25,25,25,75,74,74,75,25,25,25,25,25,25,75,75,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,25,146,146,146,25,25,25,74,75,75,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,75,74,74,74,74,74,74,25,75,25,146,74,146,25,146,146,146,146,146,146,146,146,146,25,146,146,25,146,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,75,75,74,74,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,25,25,75,74,25,25,25,25,75,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,25,25,74,75,74,25,25,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,74,75,74,74,74,74,75,74,75,75,146,25,146,146,146,25,146,146,25,146,146,146,146,146,146,146,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,146,25,25,25,25,75,75,75,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,146,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,74,74,75,25,25,74,74,75,75,74,74,75,74,75,146,25,146,146,25,146,146,146,25,146,146,146,25,25,25,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,25,25,25,146,146,25,25,25,25,75,74,75,74,75,25,25,25,75,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,75,74,74,74,74,75,75,74,74,75,75,75,74,75,146,146,146,146,146,25,25,146,146,146,146,25,25,25,146,146,25,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,146,25,25,146,146,25,25,75,74,75,75,74,74,75,74,75,74,25,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,25,74,74,74,75,74,74,75,74,74,74,75,75,74,75,146,146,146,146,146,25,146,146,146,146,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,25,25,25,25,74,75,74,75,75,74,75,75,75,75,74,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,74,75,74,74,74,74,75,74,74,74,74,75,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,146,146,146,146,25,25,146,146,146,146,146,146,146,146,146,146,25,146,25,146,146,25,25,25,25,25,75,25,75,74,74,74,74,75,74,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,75,74,75,75,74,75,75,75,75,74,75,75,75,74,75,74,146,146,146,146,146,146,146,146,25,25,25,25,25,146,146,146,25,146,146,25,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,75,74,75,75,75,75,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,75,75,74,74,74,75,75,75,25,75,75,75,74,74,74,74,146,146,146,146,146,146,146,25,146,25,25,25,25,25,146,146,146,146,146,146,146,25,25,146,146,146,146,25,25,146,146,146,146,25,146,25,25,25,25,25,25,25,25,74,75,74,74,75,74,75,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25
146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,74,74,75,75,75,74,75,75,74,75,74,75,75,75,25,75,146,146,146,146,146,146,146,146,146,146,25,25,25,25,146,146,146,146,146,146,146,146,146,146,25,146,146,146,25,25,25,146,25,146,25,25,25,25,25,25,25,75,74,75,25,74,75,74,75,75,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,146,146,146,146,25,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,146,146,25,25,25,146,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,75,74,75,74,74,74,25,75,74,74,75,75,75,74,25,146,146,146,146,146,25,146,146,146,146,146,146,25,25,25,146,146,25,146,146,146,146,146,25,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,74,74,74,25,25,75,75,25,75,75,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25
146,146,146,146,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,25,146,146,146,146,146,25,25,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,25,74,146,146,75,74,75,75,74,75,74,75,75,146,146,146,25,146,146,25,25,25,146,146,146,25,25,25,25,25,146,146,25,146,146,146,146,25,25,25,146,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,75,25,25,25,25,25,25,25,75,75,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146
146,146,146,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,74,75,146,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,25,146,146,146,146,146,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,74,75,25,25,25,146,146,25,25,146,146,25,146,25,146,25,146,146,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146
146,146,25,146,146,146,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,75,75,75,75,74,25,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,146,146,25,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,25,25,25,25,25,25,25,146,146,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,146,146,146,146,146,146
146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,75,74,75,75,74,74,74,146,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,25,25,25,25,25,146,146,146,25,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,146,25,25,25,25,146,25,146,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,146,25,25,75,74,74,74,25,74,75,75,74,75,25,25,25,25,25,25,146,146,146,146,146,25,146,25,25,25,25,146,146,146,146,146
146,146,146,146,146,146,146,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,75,75,75,74,75,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,25,25,25,25,25,25,146,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,146,25,146,146,146,146,25,146,146,146,146,146,146,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,75,74,25,75,25,74,75,75,75,25,75,75,25,25,25,25,25,146,25,146,25,146,146,146,146,146,25,25,25,146,25,25,146,146
25,25,25,146,25,25,25,25,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,75,75,146,75,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,25,25,146,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,146,146,146,25,25,25,25,25,25,25,25,25,146,25,25,25,146,25,25,25,25,25,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,25,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,75,75,74,75,25,75,25,25,74,75,74,25,25,25,25,25,146,146,146,146,146,146,25,146,146,146,146,25,25,25,25,146,25,25
25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,75,74,74,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,146,25,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,146,146,25,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,75,75,74,75,146,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,74,74,74,74,75,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,146,25,25,146,146,146,25,146,146,146,146,146,146,146,25,146,146,146,25,25,146,146,25,25,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,25,146,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,25,146,75,74,146,146,146,146,25,25,25,25,25,25,25,146,25,25,25,146,146,25,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,146,146,25,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,74,75,146,146,25,25,25,74,74,75,75,74,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,25,146,146,25,25,146,146,146,146,25,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,146,146,25,25,146,146,146,146,146,25,25,146,25,146,146,25,146,146,146,146,146,25,25,146,146,25,146,146,25,25,146,146,25,146,146,146,146,25,146,25,25,25,25,25,25,25,25,146,25,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,75,146,25,146,146,146,25,25,25,146,25,146,146,25,146,25,146,146,146,25,146,146,25,146,146,146,25,146,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,74,75,75,74,74,75,75,74,74,75,75,74,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,146,25,146,146,146,146,146,146,146,146,25,146,146,25,146,146,146,146,146,25,25,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,146,75,146,146,146,146,146,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,75,75,74,74,75,74,74,74,74,75,74,75,74,74,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,25,74,74,75,75,75,146,146,146,146,25,146,146,25,75,74,75,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,25,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,25,146,146,146,146,146,146,146,25,25,146,146,146,146,25,146,146,146,25,25,146,146,146,25,146,146,146,25,146,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,74,146,146,25,146,25,25,25,146,146,25,146,146,146,146,146,146,25,146,146,146,146,25,25,146,25,146,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,25,75,74,74,75,74,75,75,75,74,75,74,75,75,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,75,75,74,74,74,75,74,146,146,146,25,146,146,25,74,75,74,146,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,75,146,146,146,146,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25
25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,74,74,25,146,25,146,146,74,75,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,25,75,75,25,146,75,74,146,75,25,146,146,146,25,74,74,74,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,146,146,25,146,25,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,146,146,146,25,146,25,146,25,146,25,25,25,25,25,25,146,146,146,146,146,146,25,146,146,146,146,146,146,146,25,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,25,25,146,25,25,146,146,146,146,146,146,146,146,146,146,25,25,146,146,146,25,25,25,25,25,25,25,25,25
25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,74,146,146,146,25,146,146,146,146,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,25,25,25,25,146,74,75,74,146,146,25,74,75,74,75,75,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,146,146,25,146,146,146,146,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25
25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,25,25,25,25,25,75,75,74,25,25,75,74,74,74,146,146,146,146,25,25,146,146,146,146,146,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,146,25,146,146,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,25,146,146,146,146,146,25,146,146,146,146,146,25,25,25,25,25,146,146,146,146,146,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,25,25,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,74,25,25,25,25,25,74,75,75,25,25,25,25,74,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,146,25,25,146,146,146,146,146,146,25,146,25,146,146,25,25,25,25,25,25,25,25,25,146,146,146,25,146,25,146,146,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,146,146,146,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,146,146,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,75,75,25,25,25,25,25,74,74,75,75,25,25,25,75,74,25,25,25,146,146,146,146,146,146,146,25,25,25,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,146,25,25,146,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,75,74,74,74,25,25,25,25,74,74,74,25,25,25,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,25,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25
25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,75,74,74,74,74,146,25,25,25,74,75,75,75,75,74,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,25,146,146,25,146,146,146,146,146,25,146,25,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25
25,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,146,75,74,75,146,25,146,25,25,74,75,74,75,75,74,75,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,25,146,146,146,146,25,25,146,25,146,25,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,25,146,146,25,25,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,146,146,146,146,146,146,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25
25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,25,25,75,74,75,75,74,146,146,25,25,25,25,75,75,75,25,74,74,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,146,146,25,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,25,146,25,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25
146,146,25,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,146,146,146,146,146,25,25,25,25,25,74,75,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25
25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,146,25,25,25,25,25,25,25,74,74,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25
25,146,146,25,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,146,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,75,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,146,146,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,25,146,25,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25
25,146,146,25,25,146,25,146,146,146,146,146,25,25,25,146,25,146,146,146,25,25,25,25,146,146,146,146,146,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,146,25,146,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,25,146,146,146,146,146,25,146,25,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,75,75,75,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
146,146,146,146,146,146,146,146,146,146,146,25,25,25,146,146,25,146,146,146,146,25,25,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,25,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,25,25,25,25,25,25,25,74,74,74,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,146,146,146,25,146,146,146,25,25,25,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,25,25,25,25,146,25,146,146,146,146,25,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,75,25,74,75,75,75,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
146,146,25,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,25,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,146,146,25,146,25,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,25,25,25,25,25,146,25,146,146,146,146,146,146,146,25,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,74,74,74,74,75,75,75,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
146,146,25,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,146,146,25,146,25,25,25,25,146,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,146,146,146,25,146,146,146,25,146,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,25,25,146,146,146,25,146,25,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,25,75,74,75,74,75,74,75,75,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
146,25,25,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,74,75,75,25,75,75,74,75,25,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,146,146,25,25,25,146,25,146,146,25,146,146,25,25,25,25,25,146,146,146,146,25,146,146,146,146,146,146,146,25,25,25,146,146,146,146,146,146,146,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,25,25,74,74,74,75,74,75,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,75,75,74,74,75,74,74,75,75,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,146,25,25,25,146,146,25,146,146,146,146,25,146,25,25,25,25,25,146,146,25,146,146,146,146,25,146,146,25,25,25,25,25,146,146,146,146,146,146,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,25,74,74,75,74,75,75,75,74,74,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,146,25,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,25,25,25,75,75,25,74,75,75,74,74,75,75,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,146,146,146,25,146,146,146,25,25,146,146,146,25,25,25,25,25,146,146,146,25,146,146,146,146,25,146,25,25,25,25,146,25,146,146,146,25,25,25,25,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,74,75,25,74,74,75,75,74,75,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,75,74,75,74,74,75,74,74,25,25,74,75,75,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,146,146,146,25,146,146,146,146,146,146,25,146,146,25,25,25,146,146,146,146,146,146,146,25,146,25,146,25,25,25,25,25,25,146,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,25,74,74,25,75,74,75,75,74,75,74,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,75,74,74,25,75,25,75,74,75,25,25,74,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,25,25,146,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,146,146,146,146,25,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,25,25,75,74,74,74,75,74,25,75,75,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
146,146,146,146,146,146,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,74,25,146,74,74,75,25,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,146,146,146,146,146,25,146,146,25,146,146,146,25,25,25,25,25,25,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,74,74,25,25,25,25,25,25,75,74,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,146,25,146,146,25,25,25,25,146,146,146,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,25,146,146,75,25,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,25,25,25,25,25,25,25,75,25,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,146,146,146,146,146,25,25,146,146,146,146,146,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,74,75,74,75,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,146,146,146,146,25,146,146,25,25,25,146,146,25,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,75,75,146,74,74,74,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,146,146,146,146,25,25,25,25,146,25,146,146,146,146,25,25,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,75,74,74,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,146,146,146,25,146,25,25,25,25,25,146,146,146,146,25,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,75,74,74,74,74,74,74,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,146,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,25,74,75,146,146,146,74,146,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,146,25,25,25,25,25,146,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,75,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,146,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,25,146,25,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,146,146,146,146,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,75,74,74,74,75,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,75,25,25,74,75,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,25,146,146,146,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,75,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,25,25,146,146,25,146,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,146,146,146,25,25,25,25,25,25,74,75,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,146,146,146,146,25,146,146,25,25,25,25,25,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,25,146,25,25,25,25,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,25,146,25,25,146,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,25,25,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,74,74,74,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,25,146,146,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,74,74,75,75,75,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,74,25,25,25,25,25,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,25,25,146,25,25,25,25,25,74,75,74,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,75,74,74,75,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,75,74,75,74,25,25,74,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,146,146,146,25,25,25,25,25,74,74,75,75,74,74,74,146,146,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,75,74,25,25,25,25,25,74,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,25,146,146,146,146,146,146,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,75,74,75,75,75,74,75,74,75,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,74,74,75,25,25,75,25,146,25,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,25,146,146,146,25,25,25,25,25,25,25,74,74,74,146,74,75,25,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,75,74,75,75,75,25,74,74,25,25,75,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,146,146,25,146,146,25,25,25,146,25,146,146,25,146,146,25,25,25,25,25,75,75,74,25,74,75,75,75,74,75,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,146,146,146,146,146,25,25,25,74,25,74,74,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,25,25,146,146,146,25,146,146,146,146,25,25,25,25,25,25,146,25,146,75,75,25,146,75,74,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,146,146,146,146,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,75,74,74,74,74,74,75,75,75,75,75,74,25,25,25,25,25,146,146,146,146,25,146,25,25,146,146,146,146,146,25,146,146,146,146,25,25,146,146,146,146,25,25,146,146,25,25,74,75,75,75,74,74,75,74,74,74,74,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,25,146,146,146,146,146,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,25,25,74,75,74,75,75,146,146,75,74,146,146,25,146,146,25,25,25,25,25,25,146,146,146,146
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,146,146,25,25,25,25,146,146,146,146,146,74,146,146,25,146,74,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,146,146,25,146,146,146,25,25,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,74,74,75,74,75,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,146,146,146,146,146,146,25,25,25,25,75,74,74,25,75,74,75,75,74,74,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,146,25,74,74,75,74,75,25,74,74,74,25,146,25,146,146,25,25,25,25,25,25,146,146,146,146
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,146,25,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,146,25,146,25,25,25,146,25,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,75,25,25,25,74,75,25,25,25,25,25,25,25,25,146,25,146,146,146,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,146,146,146,146,146,146,25,25,25,74,25,75,74,74,74,25,74,25,25,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,25,146,146,146,146,146,25,25,75,74,74,75,75,146,74,75,74,75,146,146,146,146,146,25,25,25,25,146,146,146,25,146
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,25,146,146,146,25,25,25,25,146,25,146,146,146,146,25,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,25,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,25,25,25,25,75,74,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,25,25,25,25,146,146,146,25,25,25,25,25,146,146,146,146,146,25,25,25,25,75,74,75,74,25,75,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,74,75,74,74,74,74,146,74,74,146,146,146,25,25,25,25,25,146,146,146,146,146
146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,25,25,25,25,146,146,146,146,146,146,146,25,146,146,146,146,25,146,146,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,146,25,25,25,146,25,146,146,146,25,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,25,25,25,25,146,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,75,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,74,75,146,74,146,146,75,74,146,146,146,146,25,25,25,25,25,25,146,146,146,146
146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,146,146,25,25,25,25,146,146,146,25,25,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,74,74,25,74,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,75,74,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,25,25,146,146,25,25,25,25,25,25,25,75,75,146,74,74,75,75,25,146,25,146,146,25,25,25,25,25,25,146,146,146,146
25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,146,25,146,146,146,146,25,25,25,25,25,25,25,146,25,146,146,146,146,25,146,25,146,25,146,146,25,25,25,146,146,146,146,146,25,146,146,146,146,146,146,25,146,146,146,146,146,146,25,25,146,146,25,146,146,146,146,25,146,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,75,75,75,75,74,75,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,74,74,74,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,25,25,25,25,25,25,25,25,25,75,25,74,74,74,74,75,75,146,25,146,146,25,25,25,25,25,25,25,25,25,25,146,25
146,25,146,146,25,25,25,146,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,146,146,25,146,25,25,25,25,146,146,146,25,25,25,146,146,25,25,25,25,146,146,25,146,146,25,25,146,146,146,146,146,146,146,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,74,25,74,74,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,74,74,74,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,25,25,25,146,146,146,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,74,74,75,75,74,75,74,74,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25
25,146,146,25,146,146,146,146,25,25,146,25,25,146,146,146,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,25,146,146,146,25,25,25,25,25,25,146,25,25,25,25,25,25,146,25,25,25,146,146,146,146,25,146,146,25,146,25,25,146,146,146,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,74,25,25,74,74,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,75,75,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,75,75,74,146,25,146,146,146,25,75,74,25,25,25,146,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,74,74,25,25,25,75,74,75,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
146,25,146,146,146,146,146,25,146,146,146,25,146,146,25,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,25,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,75,75,74,74,74,75,146,146,146,146,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,25,25,25,25,25,146,75,74,74,25,25,25,74,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
146,146,146,146,146,25,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,75,74,74,74,75,74,146,25,146,75,75,74,25,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,25,146,25,25,25,146,146,146,146,146,75,74,25,146,146,146,75,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,146,146,146,25,146,25,146,146,146,146,25,25,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,75,75,25,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,74,74,74,74,75,75,75,75,75,146,25,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,25,25,146,146,25,146,74,75,74,75,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
146,25,146,25,146,25,146,25,146,146,146,25,146,146,146,25,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,74,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,74,75,74,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,25,25,25,25,25,25,25,146,146,146,146,146,75,74,74,74,25,146,75,74,146,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,146,25,146,146,146,74,75,146,146,146,146,146,146,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,25,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,146,25,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,75,74,74,75,74,74,75,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,25,25,25,25,25,25,25,25,25,146,146,146,74,74,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,75,74,75,74,146,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,146,25,25,25
25,25,25,146,146,146,146,146,146,25,146,146,146,25,146,146,25,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,25,25,74,75,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,74,74,146,146,146,146,75,74,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,25,25,25,25,25,25,25,25,146,146,146,146,146,74,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,25,25,25,25,25,146,146,146,146,146,146,75,74,74,75,146,146,146,146,146,146,146,146,25,146,146,146,146,25,25,146,25,146,146,146,146,146
25,25,146,146,146,146,25,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,25,146,25,25,25,25,25,146,25,146,146,146,25,146,25,146,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,25,25,74,74,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,74,75,74,74,146,146,146,75,75,75,75,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,75,75,74,25,25,25,25,25,25,25,146,146,146,74,75,146,146,25,146,146,25,146,146,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,146,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,25,25,25,25,146,146,146,146,146,146,146,74,74,75,74,146,146,146,146,146,146,146,25,146,25,146,146,25,25,146,146,25,146,146,25,25
25,146,146,25,146,146,25,25,146,146,146,25,25,146,25,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,146,25,146,146,146,146,25,146,146,25,146,146,25,25,25,25,25,146,146,146,146,146,146,146,25,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,75,25,75,75,25,25,25,25,74,75,75,25,25,25,25,25,25,25,25,146,146,74,75,74,146,146,146,25,75,75,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,74,25,75,74,25,25,25,25,25,25,25,75,75,74,146,146,146,25,25,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,25,25,25,146,146,146,25,146,146,25,25,25,146,25,146,146,146,146,146,25,146,146,146,25,25,146,146,146,146,146,146,146,146,146,75,75,74,75,146,146,146,146,146,146,25,25,146,25,146,146,146,146,146,25,146,146,146,146
146,146,146,146,146,146,25,146,25,25,25,25,25,25,146,146,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,25,146,146,146,146,146,146,146,146,25,146,146,146,146,25,25,146,146,146,146,146,146,25,146,25,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,75,25,74,74,75,74,74,75,75,74,75,75,75,75,75,25,25,25,25,146,146,146,146,146,146,146,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,74,75,75,75,25,25,25,75,74,75,74,74,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,146,146,146,146,25,146,146,146,146,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,146,146,146,146,146,146,25,146,146,146,75,75,74,74,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146
146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,146,146,146,146,25,146,146,146,146,146,146,146,146,25,25,25,146,146,25,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,74,74,74,75,74,74,74,75,75,75,74,25,74,74,74,74,74,25,146,146,146,25,146,25,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,75,75,75,74,75,25,25,25,25,75,74,74,74,74,146,25,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,146,25,146,25,146,146,25,146,25,25,146,146,146,146,25,146,146,146,146,146,146,25,25,146,146,146,146,146,146,74,74,146,75,74,74,74,74,75,74,146,146,146,146,146,146,146,146,25,25,146,146,146,146,146,146,146
25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,146,146,146,146,25,146,25,146,146,146,146,25,25,25,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,75,75,75,74,75,75,75,75,75,74,74,74,75,74,75,74,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,74,146,146,74,75,75,25,25,25,75,75,74,74,74,25,25,25,146,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,146,146,25,25,146,146,146,146,25,146,25,146,146,25,146,146,146,146,146,146,146,146,25,25,146,146,146,25,146,74,75,74,74,74,74,74,74,25,146,146,146,146,146,146,146,25,146,146,146,25,146,146,25,146,146,146
146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,25,25,25,25,146,146,146,25,146,146,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,146,25,146,146,25,146,146,146,146,146,146,146,146,25,146,146,146,146,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,25,74,25,25,25,74,74,75,74,74,25,74,75,74,75,74,74,146,146,146,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,146,25,25,25,146,146,25,146,74,75,25,25,74,74,74,74,74,74,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,146,146,146,74,75,146,146,146,74,74,25,25,146,25,146,146,25,146,146,146,146,146,25,25,146,25,146,146,146,25
146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,75,74,74,25,25,25,146,25,146,146,146,146,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,25,25,146,146,25,146,146,146,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,74,74,75,25,25,74,75,75,74,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,146,146,146,146,146,25,25,25,74,75,25,75,75,25,25,25,75,75,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,146,146,146,25,146,146,146,146,146,25,146,25,146,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,146,146,146,146,146,75,75,74,74,75,146,146,25,25,146,146,146,146,146,25,146,25,146,146,146,146,146,25,146,146,146
146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,25,74,74,74,74,25,74,75,74,74,146,146,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,25,146,146,146,25,146,146,146,146,146,146,146,146,146,25,146,25,146,146,146,146,146,146,146,25,146,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,25,25,25,74,74,74,75,75,146,146,146,146,146,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,25,146,146,146,146,25,146,146,146,25,146,74,75,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,146,146,25,25,25,146,146,146,25,146,146,146,146,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,146,25,25,25,25,146,146,146,146,25,146,146,25,146
25,25,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,25,25,25,74,75,74,75,74,74,25,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,146,146,146,146,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,74,25,25,25,25,75,74,74,146,146,25,25,25,25,146,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,74,74,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,25,146,25,25,25,146,146,25,146,146,146,25,146,146,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,25,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146
25,25,25,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,25,74,25,25,25,25,74,74,75,74,74,74,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,146,146,146,25,146,146,25,146,146,146,146,146,146,146,25,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,75,25,25,25,146,146,146,146,146,25,25,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,75,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,25,146,146,146,146,146,146,146,25,25,146,146,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,146,146,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146
25,25,25,25,25,146,25,25,25,25,74,25,25,25,25,25,25,25,25,25,25,74,75,25,75,75,74,25,25,25,75,25,74,75,75,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,25,146,25,146,146,25,25,25,25,25,25,146,146,25,146,146,146,146,146,25,146,146,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,75,74,75,25,25,25,25,146,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,25,25,146,146,25,146,146,146,25,146,25,146,146,146,25,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,146,146,146,146,146,146,25,146,146,25,146,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,146,25,146,146,146,25,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,74,25,25,25,25,25,25,25,25,25,75,74,75,74,74,74,75,75,75,74,25,25,25,74,74,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,25,146,146,146,146,146,146,25,146,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,25,146,146,146,146,25,146,146,146,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,146,146,146,146,146,146,146,146,146,146,25,146,146,25,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,75,74,75,75,25,25,74,75,75,74,75,75,75,75,75,74,25,75,74,75,74,25,25,75,74,75,74,75,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,25,146,146,146,25,25,25,25,25,25,146,25,146,146,146,146,146,146,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,146,146,146,146,146,146,146,146,146,146,25,146,146,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,74,25,25,74,75,75,75,74,74,74,25,25,25,25,25,25,25,74,75,74,75,25,25,25,25,74,74,74,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,25,146,146,146,146,25,146,25,25,146,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,146,25,146,146,146,25,146,146,146,146,25,146,146,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,75,74,75,75,74,25,25,25,25,25,74,74,75,75,25,25,25,74,74,25,25,146,25,146,146,25,25,146,25,25,146,146,146,146,25,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,146,25,25,25,25,146,146,146,146,146,146,25,146,146,25,146,25,146,25,146,25,146,146,146,146,146,25,25,146,146,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,74,75,74,75,75,25,25,25,25,25,25,25,75,25,25,75,74,74,74,75,146,146,146,25,146,146,25,146,146,25,146,146,146,146,146,146,146,25,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,25,25,25,146,25,146,146,146,146,146,25,25,146,146,146,146,146,146,146,146,146,25,146,25,25,25,146,25,146,146,146,25,25,25,146,146,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,74,75,75,74,25,25,25,25,25,25,25,25,25,25,25,25,75,75,74,74,74,74,25,146,146,146,146,25,146,146,25,146,146,146,25,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,146,146,146,146,25,146,146,146,146,146,146,25,146,146,146,25,146,146,25,25,146,25,25,146,146,25,146,25,25,146,25,25,25,146,25,25,25,25,25,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,25,146,25
25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,25,75,74,25,25,146,146,25,146,146,146,146,146,146,146,146,25,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,25,25,25,25,146,146,146,146,25,146,146,25,146,25,146,25,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146
25,25,25,25,25,25,25,25,25,146,25,146,146,25,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,74,75,74,25,25,75,25,146,25,146,25,146,146,146,25,146,146,146,146,146,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,146,146,146,146,146,146,25,146,146,146,25,25,25,25,146,25,146,146,25,146,25,25,146,25,146,146,146,146,25,146,146,146,146,25,25,25,25,146,146,146,146,146,146,146,146,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146
25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,74,25,74,74,74,75,74,25,75,74,25,146,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,25,146,25,25,25,25,25,25,25,146,25,25,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,146,146,25,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,25,146,146
25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,75,75,75,74,75,74,75,74,75,74,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,25,75,75,146,146,146,146,146,146
25,25,25,25,25,25,25,25,146,146,146,25,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,74,74,75,75,74,74,74,75,146,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,146,74,146,74,74,146,146,146,25,146,25
25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,74,75,75,74,75,75,74,146,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,146,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,74,74,75,75,146,146,146,25,146,146
25,25,25,25,25,25,25,25,25,25,146,146,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,74,74,75,75,74,74,146,146,146,25,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,146,146,146,146,25,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,25,146,146,146,146,146,25,146,146,146,146,146,146,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,74,74,74,74,146,25,146,146,146,146
25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,74,75,74,75,74,74,146,74,146,146,146,75,74,25,25,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,146,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,74,75,75,75,74,146,146,146,146,146
25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,75,74,74,74,74,75,74,75,75,146,146,146,75,74,75,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,74,74,25,146,146,146,146,146
25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,146,25,75,74,74,75,74,74,75,74,75,75,75,25,146,146,146,74,75,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,146,75,146,146,74,146,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,74,74,75,74,74,146,74,75,74,75,75,146,74,25,146,146,74,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,74,25,74,75,75,75,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,25,146,74,75,74,146,75,74,74,146,146,75,74,74,74,146,25,75,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,25,146,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,75,75,75,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,25,146,146,75,74,75,74,74,74,74,74,25,146,75,75,75,74,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,146,146,146,146,25,25,25,146,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,74,74,74,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,25,146,146,146,146,25,75,74,75,75,75,75,146,146,146,74,74,75,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,146,74,146,146,146,146,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,25,146,146,75,75,75,74,75,74,74,75,74,74,75,75,146,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,25,146,146,25,25,25,25,25,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,75,74,74,146,74,146,146,146,146,25,25,146,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,75,75,74,75,74,75,75,75,75,75,146,25,75,25,25,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,74,74,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,75,74,75,75,75,74,146,146,146,146,146,146,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,146,74,75,146,75,75,75,25,146,146,75,25,146,25,25,25,25,74,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,74,74,75,75,75,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,74,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,74,74,74,75,75,74,146,146,25,25,25,25,146,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,75,25,75,75,75,74,146,146,146,146,25,25,25,25,74,75,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,74,75,75,74,75,75,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,75,25,25,25,146,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,25,146,146,25,25,25,25,25,25,146,25,146,74,74,74,74,146,146,146,146,146,146,146,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,75,74,74,74,75,74,74,25,25,74,75,75,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,146,146,25,25,25,146,25,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,75,75,74,74,75,74,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,25,25,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,146,146,146,75,74,74,74,146,146,146,146,146,146,146,146,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,146,74,146,25,25,74,25,74,74,25,74,25,25,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,25,25,146,25,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,75,74,74,75,74,74,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,25,146,25,146,146,146,146,146,146,25,25,25,146,146,146,75,74,75,146,146,146,146,75,146,74,74,74,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,146,75,74,25,25,74,74,75,75,25,74,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,146,25,146,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,75,75,74,75,75,75,74,74,75,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,146,146,75,74,74,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,25,146,25,146,146,146,146,25,146,25,25,25,25,146,146,25,146,75,74,146,146,146,146,75,75,74,146,74,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,25,74,25,25,75,25,75,75,75,74,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,25,146,146,146,146,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,74,74,74,75,74,75,75,75,75,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,74,75,74,74,75,74,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,146,146,146,146,146,146,74,74,75,146,74,75,74,75,75,74,75,25
25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,25,25,25,25,146,25,146,146,25,146,25,25,146,146,146,25,25,74,75,75,74,75,25,74,75,74,25,25,25,25,25,25,25,25,25,146,25,146,25,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,74,74,74,74,75,74,74,146,25,75,74,74,75,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,74,74,75,74,75,74,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,25,146,146,146,146,25,25,25,25,146,146,25,146,146,146,74,74,74,75,75,74,74,74,25,74,74
25,25,25,25,25,146,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,146,25,146,146,146,25,146,146,25,25,25,25,75,74,75,75,25,25,25,74,75,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,74,75,75,75,146,75,74,146,146,75,75,74,75,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,75,74,75,74,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,25,25,25,25,74,75,75,74,75,75,75,74,75,75,74,75,75,74,74,75,25,74,75
25,25,146,146,146,146,25,146,146,25,25,25,146,146,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,74,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,25,25,25,146,146,146,146,146,146,25,146,25,25,146,25,25,25,25,25,25,25,25,25,25,146,75,75,74,75,74,146,75,75,74,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,74,74,75,74,74,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,75,74,74,74,75,75,75,75,75,74,74,75,75,75,74,75,74,75,74
25,25,146,25,146,146,146,146,146,146,25,25,25,146,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,146,25,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,146,25,146,146,25,146,146,25,25,25,146,25,25,25,25,25,25,25,25,25,25,146,74,74,75,74,74,146,75,74,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,74,75,74,146,75,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,25,25,25,146,75,74,75,74,75,75,74,74,74,74,74,146,146,74,75,75,75,75,25
146,146,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,25,146,25,146,146,146,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,146,146,146,146,146,146,25,146,146,146,146,25,25,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,146,146,74,75,74,75,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,75,75,74,74,75,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,25,25,25,25,25,25,25,146,146,74,74,74,75,74,146,74,74,74,74,146,75,75,25,74,74,75,74
146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,25,146,146,146,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,146,146,25,146,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,146,25,146,74,74,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,75,74,74,25,146,146,25,146,146,146,146,25,146,25,25,146,146,25,25,25,25,25,146,146,25,25,146,146,146,25,25,25,25,146,146,146,25,146,25,25,25,75,75,75,74,75,75,75,74,75,75,75,75,75,74
25,146,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,146,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,75,74,74,146,146,146,25,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,75,25,25,74,75,74,146,146,146,146,146,146,146,146,146,25,146,25,146,146,146,146,25,25,25,25,146,146,25,146,25,146,146,146,146,25,25,25,25,146,146,25,25,25,25,146,74,75,75,75,74,146,146,74,25,74,75,74,75,75
25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,25,25,25,25,146,25,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,74,75,75,74,74,74,146,146,25,146,146,25,146,146,25,25,146,146,25,25,146,146,146,25,25,146,25,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,146,74,74,75,146,146,25,146,75,25,74,75,74,25,25
146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,146,25,25,25,146,146,25,146,146,146,146,25,25,25,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,75,74,75,74,75,74,146,146,25,146,146,146,146,25,25,25,25,146,146,146,146,146,146,146,25,146,146,25,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,75,74,75,25,146,146,146,74,74,25,74,74,74,25,25
146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,25,146,25,146,25,25,25,25,25,25,25,25,146,146,25,146,146,25,146,146,146,25,25,25,25,25,146,146,146,146,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,74,75,74,75,75,74,75,74,75,74,74,25,25,25,25,25,25,75,74,74,75,75,74,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,146,25,146,25,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,75,75,74,25,25,25,25,25,75,74,25,25,74,74,74,25
146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,25,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,75,75,146,75,74,75,75,74,75,74,75,74,25,25,74,74,74,25,75,75,75,74,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,74,75,25,25,25,25,74,75,25,25,25,25,25,25,25
146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,25,25,25,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,146,146,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,74,146,75,75,74,75,74,75,75,75,74,74,74,75,74,74,74,74,25,74,74,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,25,25,25,146,146,146,146,25,146,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,25,25,25,25,25,25,25
146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,25,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,146,25,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,74,75,75,74,74,74,74,75,74,75,75,74,25,25,25,25,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,146,25,146,25,146,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,25,25,25,25,25,25,25,25
25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,75,74,75,146,25,25,146,75,74,74,74,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,75,25,25,25,25,25,25,25,25
25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,146,146,146,25,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,74,75,75,25,25,25,25,25,25,25,25,25,25
25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,25,25,146,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,25,25,25,25,146,146,25,25,146,146,25,25,25,25,146,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,146,146,146,25,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,25,25,25,25,25,25,25,25,25,25
25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,25,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,25,146,146,25,146,25,146,146,146,146,25,25,25,146,146,25,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,146,25,146,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,25,25,25,25,25,25,25,25,25,25
25,146,146,146,25,146,146,146,146,146,146,146,146,146,146,25,146,146,146,25,25,25,146,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,25,146,25,146,146,146,146,146,146,146,146,146,25,146,25,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,74,25,25,25,25,25,146,25,25,25,25,25
146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,25,146,146,146,146,146,146,146,146,25,146,146,146,25,25,146,25,146,25,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,25,146,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,75,74,25,25,146,146,25,146,146,25,146,25,25
25,146,146,25,25,25,25,146,146,146,25,146,146,146,25,146,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,75,74,25,146,146,146,146,146,25,146,146,146,25
25,146,146,146,146,146,146,146,25,146,146,146,25,146,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,146,25,25,25,146,146,146,146,146,146,146,146,146,146,25,146,146,25,25,25,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,25,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,25,25
25,146,146,146,146,25,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,25,25,25,25,25,25,25,146,25,25,25,25,146,146,146,25,146,25,25,146,25,146,25,75,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,25
25,25,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,25,146,146,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,146,146,25,25,75,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,146,146,146,146,146,146
25,25,25,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,146,146,25,146,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,146,146,25,146,146,25,146,25,146,146,146,74,75,74,75,74,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,25
25,25,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,146,146,74,74,146,146,146,146,75,75,74,74,75,25,25,25,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25
25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,146,146,146,146,25,146,146,146,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,146,75,75,74,146,146,75,74,74,75,74,74,25,25,25,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,25,146,146,146,146,146,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,74,74,75,74,146,74,74,74,75,75,74,75,75,75,75,75,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,146,146,146,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,75,74,75,75,75,74,146,74,74,74,74,75,74,74,74,74,75,74,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,146,25,25,146,25,146
25,25,146,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,74,74,74,75,74,25,25,75,75,25,75,75,75,74,74,75,75,75,74,74,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,146,25,25,146,146,146,146
25,146,146,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,146,25,25,25,25,25,25,75,74,74,25,25,25,25,25,25,25,75,74,75,75,25,74,75,75,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,25,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,146,146,146,146,146,25,146,146
146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,25,146,25,146,146,25,25,25,25,74,25,25,25,25,25,25,25,25,25,25,75,25,74,75,75,74,74,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,146,25,25,146,25,146,146,25,146,25,146
146,146,146,25,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,25,25,25,25,146,25,25,25,25,25,146,146,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,75,25,74,74,25,75,75,74,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146
25,146,146,146,146,25,146,25,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,146,25,25,146,146,146,146,146,146,146,146,146,25,25,146,146,146,146,146,146,25,146,25,146,146,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,74,74,75,75,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146
146,146,25,25,146,146,25,146,25,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,146,146,146,25,25,25,146,146,146,146,146,146,146,25,25,146,146,146,25,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,74,75,74,75,74,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,146,146,146,146,146,25
146,146,146,146,25,146,146,25,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,146,25,25,146,146,146,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,74,75,74,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,25,146,146,146,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,25,25,146,146,146,146,146,146,146,146,146,146,146
25,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,146,25,146,146,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,75,75,25,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,25,146,146,146,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,25,25,146,146,146,146,146,146,146,146,146,146,146
146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,146,146,146,25,146,146,25,25,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,25,146,146,146,146,146,146,146,146,146,146,146,25,146,25,146,146,146,146,146
25,146,25,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,25,146,146,146,146,146,146,146,25,146,146,146,146,146,25,25,146,146,146,146,146,146
25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,25,146,146,25,146,25,146,146,25,146,146,146,146,146,146,25,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,25,146,146,25,25,25,25,25,25,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,146,25,146,146,146
25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,146,146,25,146,146,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,25,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,25,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,146,146
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,25,146,146,146,146,25,146,25,146,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,25,146,146,146,146,25,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,146,25,25,25,25,25,25,25,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,146
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,146,25,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,146,25,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,25,146,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,25,146,25,25,146,146,146,146,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,146,146,146,25,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,146,146,146,146,25,146,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,25,146,25,25,146,25,146,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,25,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,25,25,146,146,25,146,25,146,146,146,25,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,25,25,25,25,146,146,146,146,146,146,25,25,25,146,146,146,146,146,146,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,25,25,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,146,146,146,25,146,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,146,146,25,146,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,25,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,25,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,25,146,25,146,146,146,146,146,146,146,146,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,25,146,146,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,25,25,146,146,25,25,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,146,146,146,146,25,146,146,25,25,146,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,25,146,25,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,25,146,146,25,25,25,146,146,25,146,25,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,25,146,25,146,25,146,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,146,146,25,25,25,25,25,25,25,25,146,146,146,146,25,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,25,25,25,25,146,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,146,146,25,146,25,146,25,25,146,146,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,25,146,146,146,146,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,25,25,25,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,25,146,146,25,146,146,25,146,146,25,25,25,25,25,25,25,25,146,25,146,25,146,146,25,146,146,146,146,25,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,146,146,25,146,146,146,146,146,146,146,146,146,146,25,146,146,25,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,25,25,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,146,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,25,146,25,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,146,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,25,146,146,146,146,25,25,25,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,25,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,25,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,146,146,146,25,25,25,25,25,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,146,146,146,25,146,25,146,25,146,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25
Binary file not shown.

After

Width:  |  Height:  |  Size: 127 B

Binary file not shown.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.
-9
View File
@@ -1,9 +0,0 @@
[package]
name = "client"
version = "0.1.0"
edition = "2024"
[dependencies]
shared = { path = "../shared" }
pbio = { git = "https://codeberg.org/irrlicht/rust-pbio.git" }
bytemuck = { version = "1.24.0", features = ["derive"] }
-72
View File
@@ -1,72 +0,0 @@
use std::fs::File;
use std::io::Read;
pub struct Image {
pub width: u32,
pub height: u32,
pub pixels: Vec<u8>,
}
impl Image {
/// Load an 8-bit indexed TGA (type 1 or type 9) from disk.
pub fn from_tga(path: &str) -> Self {
let mut file = File::open(path).unwrap();
let mut data = Vec::new();
file.read_to_end(&mut data).unwrap();
let image_type = data[2];
let width = u16::from_le_bytes([data[12], data[13]]) as u32;
let height = u16::from_le_bytes([data[14], data[15]]) as u32;
let colormap_entries = u16::from_le_bytes([data[5], data[6]]) as usize;
let colormap_entry_bytes = data[7] as usize / 8;
let pixel_offset = 18 + colormap_entries * colormap_entry_bytes;
let pixel_count = (width * height) as usize;
let mut pixels = vec![0u8; pixel_count];
match image_type {
1 => pixels.copy_from_slice(&data[pixel_offset..pixel_offset + pixel_count]),
9 => decode_rle(&data[pixel_offset..], &mut pixels),
_ => panic!("unsupported TGA image type: {}", image_type),
}
Image { width, height, pixels }
}
/// Split this image into 8×8 tiles, row-major. Tile ID = flat index into the Vec.
pub fn to_tileset(&self) -> Vec<[u8; 64]> {
let w = self.width as usize;
let cols = w / 8;
let rows = self.height as usize / 8;
let mut tiles = Vec::with_capacity(rows * cols);
for row in 0..rows {
for col in 0..cols {
let mut tile = [0u8; 64];
for tr in 0..8 {
for tc in 0..8 {
tile[tr * 8 + tc] = self.pixels[w * (row * 8 + tr) + col * 8 + tc];
}
}
tiles.push(tile);
}
}
tiles
}
}
fn decode_rle(src: &[u8], dst: &mut [u8]) {
let mut si = 0;
let mut di = 0;
while di < dst.len() {
let header = src[si];
si += 1;
let count = (header & 0x7f) as usize + 1;
if header & 0x80 != 0 {
dst[di..di + count].fill(src[si]);
si += 1;
} else {
dst[di..di + count].copy_from_slice(&src[si..si + count]);
si += count;
}
di += count;
}
}
-560
View File
@@ -1,560 +0,0 @@
mod pathfind;
mod pixelhelper;
use std::collections::{HashMap, VecDeque};
use crate::assets::Image;
use crate::input::{GameAction, InputState};
use crate::net::{EntityInfo, NetClient, NetEvent};
use shared::{chunk_id, delta_action, player_action, step_allowed, tile_collidable, TICKS_PER_MOVE};
pub enum GameSignal {
Quit,
}
/// The server's movement cadence (24 Hz base tick, movement on every 4th tick
/// → 6 Hz ≈ 167 ms/tile). Entity interpolation lerps over this interval.
const MOVE_INTERVAL_MS: usize = 167;
/// Length of one server tick. Steps are not paced by a local timer but scheduled onto
/// the server's tick timeline, estimated from the last `StatePacket` plus elapsed time.
const TICK_MS: f32 = 1000.0 / shared::TICK_HZ as f32;
/// Length of one movement window.
const WINDOW_MS: f32 = TICK_MS * TICKS_PER_MOVE as f32;
/// Safety margin on top of the measured RTT when choosing the scheduling lead — covers
/// jitter and the server answering pings on its tick grid.
const LEAD_MARGIN_MS: f32 = 50.0;
/// RTT sampling interval; the measurement drives the scheduling lead.
const PING_INTERVAL_MS: usize = 1000;
/// Upper bound on queued-but-unconfirmed steps — purely a prediction bound: window
/// addressing already caps what the server will hold (windows are consecutive and only
/// `sched_lead` ahead), while confirmations lag a full RTT behind, so on a slow link
/// several correct steps are legitimately in flight at once. Steps whose window the
/// acknowledgment cursor has passed expire automatically, so the path can never go
/// permanently stale.
const PATH_MAX_LEN: usize = 8;
/// Palette index for the path indicator. Pure blue in RGB332 (r=0, g=0, b=3).
const PATH_COLOR: u8 = 0b000_000_11;
/// Dimmer blue for planned-but-not-yet-sent route tiles (r=0, g=0, b=2).
const ROUTE_COLOR: u8 = 0b000_000_10;
/// Viewport geometry: 30×30 tiles of 8 px, top-left of the framebuffer.
const VIEW_TILES: i32 = 30;
const TILE_PX: i32 = 8;
const VIEW_PX: i32 = VIEW_TILES * TILE_PX;
/// Camera pan speed. The player walks 8 px per 167 ms ≈ 48 px/s; the camera is a bit
/// faster, so it trails during movement and settles right after the player stops.
const CAM_SPEED: f32 = 64.0; // px/s
/// A position correction farther than this is a teleport — snap instead of panning.
const CAM_SNAP_PX: f32 = 96.0;
/// Render-side smoothing state for one entity: the previously confirmed tile and how
/// long ago the current one was confirmed. Positions are tiles; rendering lerps between
/// them in pixels over one movement interval.
struct EntityLerp {
prev: (i32, i32),
cur: (i32, i32),
t_ms: usize,
}
/// One sent-but-unacknowledged step: the movement window it is scheduled for and its
/// *delta* — deliberately not an absolute tile. The server executes deltas, so the
/// prediction is derived by replaying pending deltas on top of the last authoritative
/// position: a server surprise shifts the whole prediction instead of invalidating it.
/// The window is both the retraction address and the expiry key against the ack cursor.
struct PathStep {
window: u32,
delta: (i32, i32),
}
pub struct Game {
#[allow(dead_code)]
tileset: Vec<[u8; 64]>,
entity_tileset: Vec<[u8; 64]>,
net: NetClient,
player_entity_id: u32,
/// Authoritative position, as last confirmed by the server.
player_pos: (i32, i32),
/// Queued future steps: sent to the server but not yet confirmed. Shown as the
/// planned path; the head is consumed as the server confirms each move.
path: VecDeque<PathStep>,
/// Planned route from click-to-move: tiles not yet sent to the server. Fed into
/// `path` one step per movement window. Keyboard input cancels it.
route: VecDeque<(i32, i32)>,
/// The persistent movement goal. Outlives the route: any surprise (blocked step,
/// server divergence) triggers a replan toward it, and while the mouse is held it
/// tracks the tile under the cursor. Cleared on arrival, unreachability, keyboard
/// override, or a click on an unreachable tile.
goal: Option<(i32, i32)>,
/// Tick clock: the last tick announced by a `StatePacket` and how long ago it
/// arrived — together an estimate of the server's current tick.
server_tick: u32,
tick_age_ms: usize,
/// Smoothed round-trip time from the automatic pings; `0` until the first pong.
rtt_ms: f32,
ping_accum_ms: usize,
/// Highest movement window a step was scheduled for. At most one step is scheduled
/// per window; retraction lowers this so freed windows can be re-addressed.
last_sched_window: u32,
/// Viewport top-left in world pixels. Follows the player linearly instead of
/// snapping tile-to-tile; only rendering rounds it to whole pixels.
cam: (f32, f32),
entities: Vec<EntityInfo>,
/// Per-entity interpolation state, keyed by entity id. Purely cosmetic — all game
/// logic keeps using the authoritative tile positions.
lerp: HashMap<u32, EntityLerp>,
}
impl Game {
pub fn start(server_addr: std::net::SocketAddr) -> Self {
let image = Image::from_tga("assets/tilesets/overworld.tga");
let tileset = image.to_tileset();
let entity_tileset = Image::from_tga("assets/tilesets/entities.tga").to_tileset();
let player_pos = (16, 16);
Game {
tileset,
entity_tileset,
net: NetClient::new(server_addr),
player_entity_id: 0,
player_pos,
path: VecDeque::new(),
route: VecDeque::new(),
goal: None,
server_tick: 0,
tick_age_ms: 0,
rtt_ms: 0.0,
ping_accum_ms: PING_INTERVAL_MS, // first ping fires immediately
last_sched_window: 0,
cam: (
(player_pos.0 * TILE_PX - VIEW_PX / 2) as f32,
(player_pos.1 * TILE_PX - VIEW_PX / 2) as f32,
),
entities: Vec::new(),
lerp: HashMap::new(),
}
}
/// True if the world tile at `(wx, wy)` blocks movement. An unknown chunk (outside the
/// loaded world) counts as blocked, so we never predict into the void.
fn tile_blocked(&self, wx: i32, wy: i32) -> bool {
let cid = chunk_id(wx.div_euclid(32) as i16, wy.div_euclid(32) as i16);
match self.net.chunk_cache.get(&cid) {
Some(c) => {
let idx = wy.rem_euclid(32) as usize * 32 + wx.rem_euclid(32) as usize;
tile_collidable(c.tiles[idx])
}
None => true,
}
}
pub fn update(&mut self, render_frame: &mut [u8], dt: usize, input: &InputState)
-> Option<GameSignal>
{
self.tick_age_ms += dt;
// Periodic RTT sampling — the measurement drives how far ahead steps are
// scheduled (`sched_lead`), so it has to stay current on a changing link.
self.ping_accum_ms += dt;
if self.ping_accum_ms >= PING_INTERVAL_MS {
self.ping_accum_ms = 0;
self.net.send_ping();
}
if input.mouse_clicked() || input.mouse_held() {
self.handle_click(input.mouse_pos(), !input.mouse_clicked());
}
self.step_movement(input);
if input.button_pressed(GameAction::Confirm) {
self.net.send_ping();
println!("ping sent");
}
for event in self.net.poll() {
match event {
NetEvent::Pong { rtt_ms } => {
self.rtt_ms = if self.rtt_ms == 0.0 {
rtt_ms as f32
} else {
0.8 * self.rtt_ms + 0.2 * rtt_ms as f32
};
}
NetEvent::State { tick, player_entity_id } => {
println!("state tick={tick} player_entity_id={player_entity_id}");
self.player_entity_id = player_entity_id;
self.server_tick = tick;
self.tick_age_ms = 0;
}
NetEvent::Chunk { chunk_id } => println!("chunk cached id={chunk_id}"),
NetEvent::Entity { tick, entities } => {
self.player_pos = entities.iter()
.find(|e| e.id == self.player_entity_id)
.map(|e| (e.pos_x as i32, e.pos_y as i32))
.unwrap_or(self.player_pos);
self.reconcile_path(tick);
self.track_lerp(&entities);
self.entities = entities;
}
NetEvent::Disconnected => println!("disconnected from server"),
}
}
if input.button_pressed(GameAction::Cancel) {
println!("Goodbye!");
return Some(GameSignal::Quit);
}
self.step_camera(dt);
self.step_lerp(dt);
self.render_viewport(render_frame);
None
}
/// Replay one pending delta the way the server will: through the shared movement
/// rule. A delta the server is going to reject does not move the prediction either —
/// so the prediction can never sit inside a wall, however far position and pending
/// steps have diverged.
fn replay_step(&self, p: (i32, i32), delta: (i32, i32)) -> (i32, i32) {
let to = (p.0 + delta.0, p.1 + delta.1);
if step_allowed(p, to, |x, y| self.tile_blocked(x, y)) { to } else { p }
}
/// The position movement continues from: the last authoritative position with every
/// pending delta replayed on top. Never stored — always derived, so it follows the
/// server automatically when a step was rejected or executed elsewhere.
fn predicted_pos(&self) -> (i32, i32) {
self.path.iter()
.fold(self.player_pos, |p, s| self.replay_step(p, s.delta))
}
/// Estimated current server tick: the last announced tick advanced by local elapsed
/// time. Broadcast latency makes this lag slightly behind the real server clock —
/// scheduling one window ahead plus the server's lateness rule absorbs that.
fn est_tick(&self) -> f32 {
self.server_tick as f32 + self.tick_age_ms as f32 / TICK_MS
}
/// The movement window the estimated server time is currently in.
fn est_window(&self) -> u32 {
(self.est_tick() / TICKS_PER_MOVE as f32) as u32
}
/// How many windows ahead to schedule so actions arrive *before* their window. The
/// tick estimate lags by the downlink and the action spends the uplink in transit —
/// together one RTT — so the lead must cover the RTT plus a jitter margin. At least
/// one: never schedule into the current window.
fn sched_lead(&self) -> u32 {
(((self.rtt_ms + LEAD_MARGIN_MS) / WINDOW_MS).ceil() as u32).max(1)
}
/// The newest window whose scheduled step can no longer be retracted in time: a
/// cancellation sent now needs the same lead an action does.
fn retract_horizon(&self) -> u32 {
self.est_window() + self.sched_lead() - 1
}
/// Where the camera wants to be: the viewport centered on the player's tile.
fn cam_target(&self) -> (f32, f32) {
(
(self.player_pos.0 * TILE_PX - VIEW_PX / 2) as f32,
(self.player_pos.1 * TILE_PX - VIEW_PX / 2) as f32,
)
}
/// The camera rounded to the pixel grid — the actual top-left of the rendered view.
fn cam_px(&self) -> (i32, i32) {
(self.cam.0.round() as i32, self.cam.1.round() as i32)
}
/// Follow the player linearly at `CAM_SPEED`, axis by axis. Corrections beyond
/// `CAM_SNAP_PX` (teleports, respawns) snap outright instead of panning across.
fn step_camera(&mut self, dt: usize) {
let (tx, ty) = self.cam_target();
if (tx - self.cam.0).abs().max((ty - self.cam.1).abs()) > CAM_SNAP_PX {
self.cam = (tx, ty);
return;
}
let step = CAM_SPEED * dt as f32 / 1000.0;
let approach = |c: f32, t: f32| {
if (t - c).abs() <= step { t } else { c + step * (t - c).signum() }
};
self.cam = (approach(self.cam.0, tx), approach(self.cam.1, ty));
}
/// Click-to-move: translate a framebuffer click into a world tile and adopt it as
/// the movement goal. The route is only a client-side plan — executed as ordinary
/// tick-addressed actions in `step_movement`, so the server keeps full authority.
/// With `hold` (button held after the initial click) this runs every frame and keeps
/// steering toward the tile under the cursor, replanning only when that tile changes
/// — camera movement alone shifts it too, not just moving the mouse.
fn handle_click(&mut self, (mx, my): (i32, i32), hold: bool) {
if mx < 0 || my < 0 || mx >= VIEW_PX || my >= VIEW_PX {
return; // outside the world viewport
}
let (cx, cy) = self.cam_px();
let goal = ((cx + mx).div_euclid(TILE_PX), (cy + my).div_euclid(TILE_PX));
// While steering, an unchanged goal needs no replan — unless the route was voided
// (blocked step) before reaching it; an empty route with the goal still ahead of
// the prediction means exactly that, so plan again.
if hold && self.goal == Some(goal)
&& (!self.route.is_empty() || self.predicted_pos() == goal)
{
return;
}
// Plan from where the player will be once the retractable steps are withdrawn:
// the confirmed position plus the deltas that can no longer be cancelled in
// time — not the tip of a prediction we are about to cancel.
let horizon = self.retract_horizon();
let start = self.path.iter()
.take_while(|s| s.window <= horizon)
.fold(self.player_pos, |p, s| self.replay_step(p, s.delta));
match pathfind::find_path(start, goal, |x, y| self.tile_blocked(x, y)) {
Some(steps) => {
// The plan changed: retract every scheduled-but-unexecuted step so the
// old intent cannot keep playing out on the server.
self.retract_future_steps();
self.route = steps.into();
self.goal = Some(goal);
}
// A discrete click on an unreachable tile cancels the plan; while steering,
// sweeping the cursor across a blocked tile keeps the current plan alive.
None if !hold => {
self.route.clear();
self.goal = None;
}
None => {}
}
}
/// Movement scheduling on the server timeline. A held direction key (which cancels
/// any goal) or the next planned route tile is addressed to the *next* movement
/// window — at most one step per window, so the send rate follows the server's
/// cadence by construction instead of racing it with a local timer. Steps are
/// validated locally with the shared movement rule; a blocked route step triggers
/// a replan toward the goal.
fn step_movement(&mut self, input: &InputState) {
// Both axes combine, so two held keys walk diagonally (a king move).
let kx = input.button_held(GameAction::Right) as i32
- input.button_held(GameAction::Left) as i32;
let ky = input.button_held(GameAction::Down) as i32
- input.button_held(GameAction::Up) as i32;
let dir = (kx != 0 || ky != 0).then_some((kx, ky));
if dir.is_some() {
// Manual input overrides click-to-move entirely, goal included.
self.route.clear();
self.goal = None;
}
let target = self.est_window() + self.sched_lead();
let ready = target > self.last_sched_window && self.path.len() < PATH_MAX_LEN;
let from = self.predicted_pos();
// The next step: either from the held key, or the head of the planned route.
let step = match dir {
Some((dx, dy)) => Some((from.0 + dx, from.1 + dy)),
None => self.route.front().copied(),
};
if let Some((nx, ny)) = step
&& ready
{
let allowed = step_allowed(from, (nx, ny), |x, y| self.tile_blocked(x, y));
if allowed {
if dir.is_none() {
self.route.pop_front();
}
self.path.push_back(PathStep { window: target, delta: (nx - from.0, ny - from.1) });
self.net.send_action(
delta_action(nx - from.0, ny - from.1),
target * TICKS_PER_MOVE,
);
self.last_sched_window = target;
} else if dir.is_none() {
self.replan_route();
}
}
}
/// Retract queued steps the server has (very likely) not executed yet: every step
/// addressed past the current estimated window gets a NOOP sent to its slot
/// (replacement = cancellation) and leaves the prediction. Steps at or before the
/// current window are left to be confirmed — cancelling those would race their
/// execution. Freed windows become addressable again.
fn retract_future_steps(&mut self) {
let horizon = self.retract_horizon();
while let Some(step) = self.path.back() {
if step.window <= horizon {
break;
}
self.net.send_action(player_action::NOOP, step.window * TICKS_PER_MOVE);
self.path.pop_back();
}
self.last_sched_window = self.path.back().map_or(horizon, |s| s.window);
}
/// Replan the route from the current prediction toward the persistent goal — the
/// client-side reaction to any surprise (blocked step, server divergence). Gives
/// the goal up only when it is reached or has become unreachable.
fn replan_route(&mut self) {
self.route.clear();
let Some(goal) = self.goal else { return };
if self.predicted_pos() == goal {
self.goal = None;
return;
}
match pathfind::find_path(self.predicted_pos(), goal, |x, y| self.tile_blocked(x, y)) {
Some(steps) => self.route = steps.into(),
None => self.goal = None,
}
}
/// Fold a fresh authoritative entity list into the interpolation table: a changed
/// position starts a new lerp from the old one, a jump of more than one tile
/// (Chebyshev — a teleport) snaps, and vanished entities are dropped.
fn track_lerp(&mut self, entities: &[EntityInfo]) {
for e in entities {
let cur = (e.pos_x as i32, e.pos_y as i32);
self.lerp.entry(e.id)
.and_modify(|l| {
if l.cur != cur {
let far = (cur.0 - l.cur.0).abs().max((cur.1 - l.cur.1).abs()) > 1;
l.prev = if far { cur } else { l.cur };
l.cur = cur;
l.t_ms = 0;
}
})
.or_insert(EntityLerp { prev: cur, cur, t_ms: 0 });
}
self.lerp.retain(|id, _| entities.iter().any(|e| e.id == *id));
}
/// Advance all interpolation clocks; each lerp completes after one movement interval.
fn step_lerp(&mut self, dt: usize) {
for l in self.lerp.values_mut() {
l.t_ms = (l.t_ms + dt).min(MOVE_INTERVAL_MS);
}
}
/// An entity's render position in world pixels: between its previous and current
/// tile, proportional to the time since the current one was confirmed.
fn entity_px(&self, e: &EntityInfo) -> (i32, i32) {
let cur = (e.pos_x as i32 * TILE_PX, e.pos_y as i32 * TILE_PX);
match self.lerp.get(&e.id) {
Some(l) => {
let f = l.t_ms as f32 / MOVE_INTERVAL_MS as f32;
let mix = |a: i32, b: i32| a + ((b - a) as f32 * f).round() as i32;
(mix(l.prev.0 * TILE_PX, cur.0), mix(l.prev.1 * TILE_PX, cur.1))
}
None => cur,
}
}
/// Fold a fresh authoritative snapshot into the prediction. The snapshot's tick is
/// an acknowledgment cursor on the shared timeline: every movement window at or
/// before it has been consumed — executed, rejected or lost, it no longer matters
/// which — so those steps expire, and the remaining deltas replay on top of the
/// confirmed position (`predicted_pos`). A server surprise thus *shifts* the
/// prediction instead of invalidating it; if the shifted route no longer connects
/// or misses the goal, send-time validation triggers a replan toward the goal.
fn reconcile_path(&mut self, tick: u32) {
let ack_window = tick / TICKS_PER_MOVE;
while self.path.front().is_some_and(|s| s.window <= ack_window) {
self.path.pop_front();
}
if self.path.is_empty() && self.route.is_empty() && self.goal == Some(self.player_pos) {
self.goal = None; // arrived — a later server correction must not walk us back
}
}
fn render_viewport(&self, frame: &mut [u8]) {
const WALL: u8 = 0x00;
const W: usize = 320;
frame.fill(0);
let (cx, cy) = self.cam_px();
let tx0 = cx.div_euclid(TILE_PX);
let ty0 = cy.div_euclid(TILE_PX);
let sx = -cx.rem_euclid(TILE_PX);
let sy = -cy.rem_euclid(TILE_PX);
// One extra row and column: with a sub-tile camera offset the viewport spans
// partial tiles on both edges.
for vy in 0..=VIEW_TILES {
for vx in 0..=VIEW_TILES {
let wx = tx0 + vx;
let wy = ty0 + vy;
let cid = chunk_id(wx.div_euclid(32) as i16, wy.div_euclid(32) as i16);
let tile_idx = wy.rem_euclid(32) as usize * 32 + wx.rem_euclid(32) as usize;
let tile_id = self.net.chunk_cache.get(&cid)
.map(|c| c.tiles[tile_idx])
.unwrap_or(0);
let px = sx + vx * TILE_PX;
let py = sy + vy * TILE_PX;
if let Some(tile) = self.tileset.get(tile_id as usize) {
pixelhelper::blit_tile(frame, W, px, py, tile);
} else {
for dy in 0..8 {
for dx in 0..8 {
pixelhelper::set_pixel(frame, W, px + dx, py + dy, WALL);
}
}
}
}
}
// Path pass — in-flight steps bright, planned-route tiles dim; over the world,
// under entities.
// Path tiles are derived by replaying the pending deltas from the confirmed
// position — the same fold the prediction uses (a rejected step marks in place).
let mut acc = self.player_pos;
let marks = self.path.iter()
.map(|s| {
acc = self.replay_step(acc, s.delta);
(acc, PATH_COLOR)
})
.collect::<Vec<_>>().into_iter()
.chain(self.route.iter().map(|&p| (p, ROUTE_COLOR)));
for ((wx, wy), color) in marks {
let px = wx * TILE_PX - cx;
let py = wy * TILE_PX - cy;
if px <= -TILE_PX || px >= VIEW_PX || py <= -TILE_PX || py >= VIEW_PX { continue; }
for dy in 0..8 {
for dx in 0..8 {
pixelhelper::set_pixel(frame, W, px + dx, py + dy, color);
}
}
}
// Entity pass — interpolated between the last two confirmed positions.
for e in &self.entities {
let (ex, ey) = self.entity_px(e);
let px = ex - cx;
let py = ey - cy;
if px <= -TILE_PX || px >= VIEW_PX || py <= -TILE_PX || py >= VIEW_PX { continue; }
if let Some(tile) = self.entity_tileset.get(e.type_id as usize) {
pixelhelper::blit_tile(frame, W, px, py, tile);
}
}
// The blit primitives clip against the framebuffer, not the viewport, so partial
// tiles on the right edge bleed into the 240..320 strip. Clear it; a real
// clip-rect belongs to the UI pass (see roadmap).
for y in 0..240usize {
frame[y * W + VIEW_PX as usize..y * W + W].fill(0);
}
}
}
-211
View File
@@ -1,211 +0,0 @@
use std::collections::HashMap;
use std::mem::size_of;
use std::net::{SocketAddr, UdpSocket};
use std::time::{Duration, Instant};
use bytemuck::bytes_of;
use shared::{ActionPacket, ChunkEntry, ChunkPacket, EntityPacket,
Header, PingPacket, PongPacket, StatePacket,
MAGIC, VERSION, packet_type};
pub struct CachedChunk {
pub version: u16,
pub tiles: Box<[u16; 1024]>, // decoded global tile IDs
}
pub struct EntityInfo {
pub id: u32,
pub type_id: u16,
pub pos_x: i16,
pub pos_y: i16,
pub hp: u16,
pub hp_max: u16,
pub flags: u8,
}
pub enum NetEvent {
Pong { rtt_ms: u64 },
State { tick: u32, player_entity_id: u32 },
Chunk { chunk_id: u32 },
/// An authoritative entity snapshot taken at server tick `tick` — the tick doubles
/// as the acknowledgment cursor for tick-addressed actions: every movement window
/// at or before it has been consumed.
Entity { tick: u32, entities: Vec<EntityInfo> },
Disconnected,
}
const FNV_BASIS: u32 = 2_166_136_261;
const FNV_PRIME: u32 = 16_777_619;
pub struct NetClient {
socket: UdpSocket,
ping_sent_at: Option<Instant>,
last_action_sent: Instant,
last_state_received: Option<Instant>,
pub chunk_cache: HashMap<u32, CachedChunk>,
last_state_chunks: Option<[ChunkEntry; 9]>,
pending_entities: Vec<EntityInfo>,
last_entity_checksum: u32,
last_state_tick: u32,
entity_checksum_accum: u32,
}
impl NetClient {
pub fn new(server_addr: SocketAddr) -> Self {
let socket = UdpSocket::bind("0.0.0.0:0").expect("bind failed");
socket.connect(server_addr).expect("connect failed");
socket.set_nonblocking(true).expect("set_nonblocking failed");
Self {
socket,
ping_sent_at: None,
last_action_sent: Instant::now(),
last_state_received: None,
chunk_cache: HashMap::new(),
last_state_chunks: None,
pending_entities: Vec::new(),
last_entity_checksum: 0,
last_state_tick: 0,
entity_checksum_accum: FNV_BASIS,
}
}
/// Send an action addressed to a server tick (see `ActionPacket::target_tick`).
/// `target_tick == 0` is a pure keep-alive / cache-ack with no scheduling intent.
pub fn send_action(&mut self, player_action: u16, target_tick: u32) {
let packet = ActionPacket {
header: Header::new(packet_type::ACTION),
auth_token: 0,
target_tick,
cache: {
let mut cache = [ChunkEntry { chunk_id: 0, version: 0 }; 9];
if let Some(slots) = self.last_state_chunks {
for (i, slot) in slots.iter().enumerate() {
let cid = slot.chunk_id;
let ver = self.chunk_cache.get(&cid)
.map(|c| c.version).unwrap_or(0);
cache[i] = ChunkEntry { chunk_id: cid, version: ver };
}
}
cache
},
player_action,
};
self.socket.send(bytes_of(&packet)).ok();
self.last_action_sent = Instant::now();
}
pub fn send_ping(&mut self) {
let ping = PingPacket { header: Header::new(packet_type::PING), timestamp_ms: 0 };
self.ping_sent_at = Some(Instant::now());
self.socket.send(bytes_of(&ping)).ok();
}
pub fn poll(&mut self) -> Vec<NetEvent> {
const KEEPALIVE: Duration = Duration::from_secs(2);
const TIMEOUT: Duration = Duration::from_secs(10);
if self.last_action_sent.elapsed() >= KEEPALIVE {
self.send_action(shared::player_action::NOOP, 0);
}
let mut events = Vec::new();
let mut buf = [0u8; 1200];
loop {
match self.socket.recv(&mut buf) {
Ok(n) => {
if n < size_of::<Header>() { continue; }
let header: Header = *bytemuck::from_bytes(&buf[..size_of::<Header>()]);
if header.magic != MAGIC || header.version != VERSION { continue; }
match header.packet_type {
packet_type::PONG if n >= size_of::<PongPacket>() => {
if let Some(sent_at) = self.ping_sent_at.take() {
let rtt_ms = sent_at.elapsed().as_millis() as u64;
events.push(NetEvent::Pong { rtt_ms });
}
}
packet_type::STATE if n >= size_of::<StatePacket>() => {
let pkt: StatePacket = *bytemuck::from_bytes(&buf[..size_of::<StatePacket>()]);
self.last_state_received = Some(Instant::now());
self.last_state_chunks = Some(pkt.chunks);
self.last_entity_checksum = pkt.entity_checksum;
self.last_state_tick = pkt.tick;
self.pending_entities.clear();
self.entity_checksum_accum = FNV_BASIS;
events.push(NetEvent::State { tick: pkt.tick, player_entity_id: pkt.player_entity_id });
}
packet_type::ENTITY if n >= size_of::<EntityPacket>() => {
let pkt: EntityPacket = *bytemuck::from_bytes(&buf[..size_of::<EntityPacket>()]);
if pkt.tick != self.last_state_tick {
// stale datagram from a previous state cycle
} else {
for i in 0..pkt.entity_count as usize {
let e = pkt.entities[i];
for byte in bytemuck::bytes_of(&e) {
self.entity_checksum_accum ^= *byte as u32;
self.entity_checksum_accum =
self.entity_checksum_accum.wrapping_mul(FNV_PRIME);
}
self.pending_entities.push(EntityInfo {
id: e.id,
type_id: e.type_id,
pos_x: e.pos_x,
pos_y: e.pos_y,
hp: e.hp,
hp_max: e.hp_max,
flags: e.entity_flags,
});
}
if pkt.packet_flags & 1 == 0 {
// last datagram in sequence
if self.entity_checksum_accum != self.last_entity_checksum {
self.send_action(shared::player_action::NOOP, 0);
}
events.push(NetEvent::Entity {
tick: pkt.tick,
entities: std::mem::take(&mut self.pending_entities),
});
}
}
}
packet_type::CHUNK if n >= size_of::<ChunkPacket>() => {
let pkt: ChunkPacket = *bytemuck::from_bytes(&buf[..size_of::<ChunkPacket>()]);
let id = pkt.chunk.chunk_id;
let version = pkt.chunk.version;
let palette = pkt.palette;
let raw = pkt.tiles;
let mut tiles = Box::new([0u16; 1024]);
for i in 0..256usize {
let b0 = raw[i * 3];
let b1 = raw[i * 3 + 1];
let b2 = raw[i * 3 + 2];
let ia = (b0 & 0x3F) as usize;
let ib = ((b0 >> 6) | ((b1 & 0x0F) << 2)) as usize;
let ic = ((b1 >> 4) | ((b2 & 0x03) << 4)) as usize;
let id_ = (b2 >> 2) as usize;
tiles[i * 4] = palette[ia];
tiles[i * 4 + 1] = palette[ib];
tiles[i * 4 + 2] = palette[ic];
tiles[i * 4 + 3] = palette[id_];
}
self.chunk_cache.insert(id, CachedChunk { version, tiles });
events.push(NetEvent::Chunk { chunk_id: id });
}
_ => {}
}
}
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => break,
Err(_) => break,
}
}
if let Some(t) = self.last_state_received {
if t.elapsed() >= TIMEOUT {
self.last_state_received = None;
events.push(NetEvent::Disconnected);
}
}
events
}
}
+9
View File
@@ -0,0 +1,9 @@
[package]
name = "game"
version = "0.1.0"
edition = "2024"
[dependencies]
sim = { path = "../sim" }
pbio = { git = "https://git.bnd.wtf/irrlicht/pbio.git", branch = "truecolor16" }
png = "0.18.1"
+67
View File
@@ -0,0 +1,67 @@
use std::fs::File;
use std::io::BufReader;
/// A decoded image in the framebuffer's pixel format: RGB565 plus a 1-bit alpha
/// mask. Truecolor sources are quantized on load (8 bit → 5/6/5 by truncation), so an
/// asset can never be in the "wrong" palette — there is none.
pub struct Image {
pub width: u32,
pub height: u32,
pub pixels: Vec<u16>,
/// One flag per pixel: `true` = opaque. Alpha ≥ 128 counts as opaque.
pub opaque: Vec<bool>,
}
/// An 8×8 sprite: RGB565 pixels row-major plus an opacity bitmask (bit `y*8+x`).
#[derive(Clone, Copy)]
pub struct Tile {
pub px: [u16; 64],
pub mask: u64,
}
impl Image {
/// Load a PNG from disk. Any color type / bit depth the `png` crate can expand to
/// 8-bit RGBA is accepted.
pub fn from_png(path: &str) -> Self {
let file = File::open(path).unwrap_or_else(|e| panic!("failed to open {path}: {e}"));
let mut decoder = png::Decoder::new(BufReader::new(file));
decoder.set_transformations(png::Transformations::normalize_to_color8() | png::Transformations::ALPHA);
let mut reader = decoder.read_info().unwrap_or_else(|e| panic!("bad PNG {path}: {e}"));
let mut buf = vec![0u8; reader.output_buffer_size().expect("PNG too large")];
let info = reader.next_frame(&mut buf).unwrap_or_else(|e| panic!("bad PNG {path}: {e}"));
assert_eq!(info.color_type, png::ColorType::Rgba, "{path}: expected RGBA after expansion");
let count = (info.width * info.height) as usize;
let mut pixels = Vec::with_capacity(count);
let mut opaque = Vec::with_capacity(count);
for p in buf[..count * 4].chunks_exact(4) {
pixels.push(pbio::rgb565(p[0], p[1], p[2]));
opaque.push(p[3] >= 128);
}
Image { width: info.width, height: info.height, pixels, opaque }
}
/// Split this image into 8×8 tiles, row-major. Tile ID = flat index into the Vec.
pub fn to_tileset(&self) -> Vec<Tile> {
let w = self.width as usize;
let cols = w / 8;
let rows = self.height as usize / 8;
let mut tiles = Vec::with_capacity(rows * cols);
for row in 0..rows {
for col in 0..cols {
let mut tile = Tile { px: [0; 64], mask: 0 };
for tr in 0..8 {
for tc in 0..8 {
let i = w * (row * 8 + tr) + col * 8 + tc;
tile.px[tr * 8 + tc] = self.pixels[i];
if self.opaque[i] {
tile.mask |= 1 << (tr * 8 + tc);
}
}
}
tiles.push(tile);
}
}
tiles
}
}
+418
View File
@@ -0,0 +1,418 @@
mod pathfind;
mod pixelhelper;
use std::collections::{HashMap, VecDeque};
use crate::assets::{Image, Tile};
use crate::input::{GameAction, InputState};
use sim::entity::Entity;
use sim::map::TileMap;
use sim::{delta_action, load_world, step_allowed, Sim, TICK_HZ, TICKS_PER_MOVE};
pub enum GameSignal {
Quit,
}
/// Length of one base tick. The sim advances in whole ticks; rendering runs at frame
/// rate on top and accumulates frame time into ticks.
const TICK_MS: f32 = 1000.0 / TICK_HZ as f32;
/// The movement cadence (movement on every 4th tick → 6 Hz ≈ 167 ms/tile). Entity
/// interpolation lerps over this interval.
const MOVE_INTERVAL_MS: usize = (TICK_MS * TICKS_PER_MOVE as f32) as usize;
/// Longest frame the sim will catch up on in one go. Anything slower (debugger, window
/// drag) is dropped instead of spiralling into a burst of ticks.
const MAX_FRAME_MS: f32 = 250.0;
/// Color of the planned route: dim blue.
const ROUTE_COLOR: u16 = pbio::rgb565(0, 0, 170);
/// Fill for tiles outside the tileset / world.
const VOID_COLOR: u16 = pbio::rgb565(0, 0, 0);
/// Viewport geometry: 30×30 tiles of 8 px, top-left of the framebuffer.
const VIEW_TILES: i32 = 30;
const TILE_PX: i32 = 8;
const VIEW_PX: i32 = VIEW_TILES * TILE_PX;
/// Camera pan speed. The player walks 8 px per 167 ms ≈ 48 px/s; the camera is a bit
/// faster, so it trails during movement and settles right after the player stops.
const CAM_SPEED: f32 = 64.0; // px/s
/// A position correction farther than this is a teleport — snap instead of panning.
const CAM_SNAP_PX: f32 = 96.0;
/// Render-side smoothing state for one entity: the previous tile and how long ago the
/// current one was taken. Positions are tiles; rendering lerps between them in pixels
/// over one movement interval.
struct EntityLerp {
prev: (i32, i32),
cur: (i32, i32),
t_ms: usize,
}
pub struct Game {
tileset: Vec<Tile>,
entity_tileset: Vec<Tile>,
sim: Sim,
player_id: u32,
/// Frame time not yet consumed by whole ticks.
tick_accum_ms: f32,
/// Planned route from click-to-move: tiles still to visit, fed into the sim one
/// step per movement window. Keyboard input cancels it.
route: VecDeque<(i32, i32)>,
/// The persistent movement goal. Outlives the route: a blocked step triggers a
/// replan toward it, and while the mouse is held it tracks the tile under the
/// cursor. Cleared on arrival, unreachability, keyboard override, or a click on an
/// unreachable tile.
goal: Option<(i32, i32)>,
/// Viewport top-left in world pixels. Follows the player linearly instead of
/// snapping tile-to-tile; only rendering rounds it to whole pixels.
cam: (f32, f32),
/// Per-entity interpolation state, keyed by entity id. Purely cosmetic — all game
/// logic keeps using the sim's tile positions.
lerp: HashMap<u32, EntityLerp>,
}
impl Game {
pub fn start(map_path: &str) -> Self {
let tileset = Image::from_png("assets/tilesets/overworld.png").to_tileset();
let entity_tileset = Image::from_png("assets/tilesets/entities.png").to_tileset();
let t0 = std::time::Instant::now();
let map = TileMap::from_csv(map_path);
let world = load_world(&map);
println!("loaded {map_path}: {}×{} tiles in {:.1} ms",
map.width, map.height, t0.elapsed().as_secs_f64() * 1e3);
let mut sim = Sim::new(world);
let player_id = sim.world.spawn_entity(0, (0, 0), 100);
let mut game = Game {
tileset,
entity_tileset,
sim,
player_id,
tick_accum_ms: 0.0,
route: VecDeque::new(),
goal: None,
cam: (0.0, 0.0),
lerp: HashMap::new(),
};
game.cam = game.cam_target();
game.track_lerp();
game
}
fn player_pos(&self) -> (i32, i32) {
let p = self.sim.world.entities[&self.player_id].pos;
(p.0 as i32, p.1 as i32)
}
/// True if the world tile at `(wx, wy)` blocks movement. Outside the loaded world
/// counts as blocked — the same rule the sim applies.
fn tile_blocked(&self, wx: i32, wy: i32) -> bool {
self.sim.world.tile_flags(wx as i16, wy as i16).map_or(true, |f| f.collidable())
}
pub fn update(&mut self, render_frame: &mut [u16], dt: usize, input: &InputState)
-> Option<GameSignal>
{
if input.mouse_clicked() || input.mouse_held() {
self.handle_click(input.mouse_pos(), !input.mouse_clicked());
}
self.step_movement(input);
// Advance the world in whole ticks. After every movement window the route is
// synced against the new position and the next step is scheduled right away,
// so a slow frame that spans several windows still walks every one of them.
self.tick_accum_ms += (dt as f32).min(MAX_FRAME_MS);
while self.tick_accum_ms >= TICK_MS {
self.tick_accum_ms -= TICK_MS;
if self.sim.step() {
self.track_lerp();
self.sync_route();
self.step_movement(input);
}
}
if input.button_pressed(GameAction::Cancel) {
println!("Goodbye!");
return Some(GameSignal::Quit);
}
self.step_camera(dt);
self.step_lerp(dt);
self.render_viewport(render_frame);
None
}
/// Where the camera wants to be: the viewport centered on the player's tile.
fn cam_target(&self) -> (f32, f32) {
let (px, py) = self.player_pos();
(
(px * TILE_PX - VIEW_PX / 2) as f32,
(py * TILE_PX - VIEW_PX / 2) as f32,
)
}
/// The camera rounded to the pixel grid — the actual top-left of the rendered view.
fn cam_px(&self) -> (i32, i32) {
(self.cam.0.round() as i32, self.cam.1.round() as i32)
}
/// Follow the player linearly at `CAM_SPEED`, axis by axis. Corrections beyond
/// `CAM_SNAP_PX` (teleports, respawns) snap outright instead of panning across.
fn step_camera(&mut self, dt: usize) {
let (tx, ty) = self.cam_target();
if (tx - self.cam.0).abs().max((ty - self.cam.1).abs()) > CAM_SNAP_PX {
self.cam = (tx, ty);
return;
}
let step = CAM_SPEED * dt as f32 / 1000.0;
let approach = |c: f32, t: f32| {
if (t - c).abs() <= step { t } else { c + step * (t - c).signum() }
};
self.cam = (approach(self.cam.0, tx), approach(self.cam.1, ty));
}
/// Click-to-move: translate a framebuffer click into a world tile and adopt it as
/// the movement goal. With `hold` (button held after the initial click) this runs
/// every frame and keeps steering toward the tile under the cursor, replanning only
/// when that tile changes — camera movement alone shifts it too, not just moving
/// the mouse.
fn handle_click(&mut self, (mx, my): (i32, i32), hold: bool) {
if mx < 0 || my < 0 || mx >= VIEW_PX || my >= VIEW_PX {
return; // outside the world viewport
}
let (cx, cy) = self.cam_px();
let goal = ((cx + mx).div_euclid(TILE_PX), (cy + my).div_euclid(TILE_PX));
// While steering, an unchanged goal needs no replan — unless the route was voided
// (blocked step) before reaching it; an empty route with the goal still ahead
// means exactly that, so plan again.
if hold && self.goal == Some(goal)
&& (!self.route.is_empty() || self.player_pos() == goal)
{
return;
}
match pathfind::find_path(self.player_pos(), goal, |x, y| self.tile_blocked(x, y)) {
Some(steps) => {
self.route = steps.into();
self.goal = Some(goal);
}
// A discrete click on an unreachable tile cancels the plan; while steering,
// sweeping the cursor across a blocked tile keeps the current plan alive.
None if !hold => {
self.route.clear();
self.goal = None;
self.sim.clear_action(self.player_id);
}
None => {}
}
}
/// Schedule the player's next step: a held direction key (which cancels any goal)
/// or the head of the planned route becomes the intent for the next movement
/// window. The sim executes at most one intent per window, so holding a key walks
/// at the movement cadence by construction. Steps are validated with the shared
/// movement rule; a blocked route step triggers a replan toward the goal.
fn step_movement(&mut self, input: &InputState) {
// Both axes combine, so two held keys walk diagonally (a king move). A tap
// shorter than a frame shows up as pressed only.
let axis = |neg: GameAction, pos: GameAction| {
(input.button_held(pos) || input.button_pressed(pos)) as i32
- (input.button_held(neg) || input.button_pressed(neg)) as i32
};
let kx = axis(GameAction::Left, GameAction::Right);
let ky = axis(GameAction::Up, GameAction::Down);
let dir = (kx != 0 || ky != 0).then_some((kx, ky));
if dir.is_some() {
// Manual input overrides click-to-move entirely, goal included.
self.route.clear();
self.goal = None;
}
let from = self.player_pos();
let step = match dir {
Some((dx, dy)) => Some((from.0 + dx, from.1 + dy)),
None => self.route.front().copied(),
};
let Some((nx, ny)) = step else { return };
if step_allowed(from, (nx, ny), |x, y| self.tile_blocked(x, y)) {
self.sim.set_action(self.player_id, delta_action(nx - from.0, ny - from.1));
} else if dir.is_none() {
self.replan_route();
}
}
/// After a movement window: drop the route head once the player stands on it, and
/// release the goal on arrival.
fn sync_route(&mut self) {
let pos = self.player_pos();
if self.route.front() == Some(&pos) {
self.route.pop_front();
}
if self.route.is_empty() && self.goal == Some(pos) {
self.goal = None;
}
}
/// Replan the route from the current position toward the persistent goal — the
/// reaction to a blocked step. Gives the goal up only when it is reached or has
/// become unreachable.
fn replan_route(&mut self) {
self.route.clear();
let Some(goal) = self.goal else { return };
let pos = self.player_pos();
if pos == goal {
self.goal = None;
return;
}
match pathfind::find_path(pos, goal, |x, y| self.tile_blocked(x, y)) {
Some(steps) => self.route = steps.into(),
None => self.goal = None,
}
}
/// Fold the sim's entity positions into the interpolation table: a changed position
/// starts a new lerp from the old one, a jump of more than one tile (Chebyshev — a
/// teleport) snaps, and vanished entities are dropped.
fn track_lerp(&mut self) {
let entities = &self.sim.world.entities;
for e in entities.values() {
let cur = (e.pos.0 as i32, e.pos.1 as i32);
self.lerp.entry(e.id)
.and_modify(|l| {
if l.cur != cur {
let far = (cur.0 - l.cur.0).abs().max((cur.1 - l.cur.1).abs()) > 1;
l.prev = if far { cur } else { l.cur };
l.cur = cur;
l.t_ms = 0;
}
})
.or_insert(EntityLerp { prev: cur, cur, t_ms: 0 });
}
self.lerp.retain(|id, _| entities.contains_key(id));
}
/// Advance all interpolation clocks; each lerp completes after one movement interval.
fn step_lerp(&mut self, dt: usize) {
for l in self.lerp.values_mut() {
l.t_ms = (l.t_ms + dt).min(MOVE_INTERVAL_MS);
}
}
/// An entity's render position in world pixels: between its previous and current
/// tile, proportional to the time since the current one was taken.
fn entity_px(&self, e: &Entity) -> (i32, i32) {
let cur = (e.pos.0 as i32 * TILE_PX, e.pos.1 as i32 * TILE_PX);
match self.lerp.get(&e.id) {
Some(l) => {
let f = l.t_ms as f32 / MOVE_INTERVAL_MS as f32;
let mix = |a: i32, b: i32| a + ((b - a) as f32 * f).round() as i32;
(mix(l.prev.0 * TILE_PX, cur.0), mix(l.prev.1 * TILE_PX, cur.1))
}
None => cur,
}
}
fn render_viewport(&self, frame: &mut [u16]) {
const W: usize = 320;
frame.fill(0);
let (cx, cy) = self.cam_px();
let tx0 = cx.div_euclid(TILE_PX);
let ty0 = cy.div_euclid(TILE_PX);
let sx = -cx.rem_euclid(TILE_PX);
let sy = -cy.rem_euclid(TILE_PX);
// One extra row and column: with a sub-tile camera offset the viewport spans
// partial tiles on both edges.
for vy in 0..=VIEW_TILES {
for vx in 0..=VIEW_TILES {
let wx = tx0 + vx;
let wy = ty0 + vy;
let (ccx, ccy) = sim::world::tile_to_chunk(wx as i16, wy as i16);
let (lx, ly) = sim::world::tile_to_local(wx as i16, wy as i16);
let tile_id = self.sim.world.chunk(ccx, ccy)
.map(|c| c.get_tile(lx, ly))
.unwrap_or(0);
let px = sx + vx * TILE_PX;
let py = sy + vy * TILE_PX;
if let Some(tile) = self.tileset.get(tile_id as usize) {
pixelhelper::blit_tile(frame, W, px, py, tile);
} else {
for dy in 0..8 {
for dx in 0..8 {
pixelhelper::set_pixel(frame, W, px + dx, py + dy, VOID_COLOR);
}
}
}
}
}
// Route pass — over the world, under entities.
for &(wx, wy) in &self.route {
let px = wx * TILE_PX - cx;
let py = wy * TILE_PX - cy;
if px <= -TILE_PX || px >= VIEW_PX || py <= -TILE_PX || py >= VIEW_PX { continue; }
for dy in 0..8 {
for dx in 0..8 {
pixelhelper::set_pixel(frame, W, px + dx, py + dy, ROUTE_COLOR);
}
}
}
// Entity pass — interpolated between the last two tiles.
for e in self.sim.world.entities.values() {
let (ex, ey) = self.entity_px(e);
let px = ex - cx;
let py = ey - cy;
if px <= -TILE_PX || px >= VIEW_PX || py <= -TILE_PX || py >= VIEW_PX { continue; }
if let Some(tile) = self.entity_tileset.get(e.type_id as usize) {
pixelhelper::blit_tile(frame, W, px, py, tile);
}
}
// The blit primitives clip against the framebuffer, not the viewport, so partial
// tiles on the right edge bleed into the 240..320 strip. Clear it; a real
// clip-rect belongs to the UI pass (see roadmap).
for y in 0..240usize {
frame[y * W + VIEW_PX as usize..y * W + W].fill(0);
}
}
}
#[cfg(test)]
mod snapshot {
/// Renders the first frame headlessly to the PNG named by `$SNAPSHOT` — a way to
/// eyeball the renderer without a window. `$SNAPSHOT_MAP` picks the map:
/// `SNAPSHOT=/tmp/frame.png cargo test -p game snapshot -- --ignored`
#[test]
#[ignore]
fn dump_frame() {
std::env::set_current_dir("..").unwrap();
let map = std::env::var("SNAPSHOT_MAP").unwrap_or_else(|_| "assets/map_test".into());
let mut g = super::Game::start(&map);
let input = crate::input::InputState::new();
let mut frame = vec![0u16; 320 * 240];
g.update(&mut frame, 0, &input);
let out = std::env::var("SNAPSHOT").unwrap();
let mut rgb = Vec::with_capacity(320 * 240 * 3);
for p in &frame {
rgb.push(((p >> 11) as u8) << 3);
rgb.push((((p >> 5) & 63) as u8) << 2);
rgb.push(((p & 31) as u8) << 3);
}
let f = std::fs::File::create(out).unwrap();
let mut enc = png::Encoder::new(std::io::BufWriter::new(f), 320, 240);
enc.set_color(png::ColorType::Rgb);
enc.write_header().unwrap().write_image_data(&rgb).unwrap();
}
}
@@ -75,7 +75,7 @@ pub fn find_path(
(1, -1), (1, 1), (-1, 1), (-1, -1), (1, -1), (1, 1), (-1, 1), (-1, -1),
] { ] {
let next = (cur.0 + dx, cur.1 + dy); let next = (cur.0 + dx, cur.1 + dy);
if !shared::step_allowed(cur, next, &blocked) { if !sim::step_allowed(cur, next, &blocked) {
continue; continue;
} }
let ng = g + if dx != 0 && dy != 0 { DIAGONAL_COST } else { CARDINAL_COST }; let ng = g + if dx != 0 && dy != 0 { DIAGONAL_COST } else { CARDINAL_COST };
@@ -96,7 +96,7 @@ mod tests {
fn assert_valid(start: (i32, i32), path: &[(i32, i32)], blocked: impl Fn(i32, i32) -> bool) { fn assert_valid(start: (i32, i32), path: &[(i32, i32)], blocked: impl Fn(i32, i32) -> bool) {
let mut from = start; let mut from = start;
for &to in path { for &to in path {
assert!(shared::step_allowed(from, to, &blocked), "illegal step {from:?} → {to:?}"); assert!(sim::step_allowed(from, to, &blocked), "illegal step {from:?} → {to:?}");
from = to; from = to;
} }
} }
@@ -1,6 +1,7 @@
/// Write a single palette index. Out-of-bounds coordinates are silently ignored. use crate::assets::Tile;
#[allow(dead_code)]
pub fn set_pixel(dst: &mut [u8], dst_w: usize, x: i32, y: i32, color: u8) { /// Write a single RGB565 pixel. Out-of-bounds coordinates are silently ignored.
pub fn set_pixel(dst: &mut [u16], dst_w: usize, x: i32, y: i32, color: u16) {
let dst_h = (dst.len() / dst_w) as i32; let dst_h = (dst.len() / dst_w) as i32;
if x < 0 || x >= dst_w as i32 || y < 0 || y >= dst_h { if x < 0 || x >= dst_w as i32 || y < 0 || y >= dst_h {
return; return;
@@ -10,8 +11,8 @@ pub fn set_pixel(dst: &mut [u8], dst_w: usize, x: i32, y: i32, color: u8) {
/// Copy a rectangle out of `src` into a new Vec (for building sprites/glyphs). /// Copy a rectangle out of `src` into a new Vec (for building sprites/glyphs).
#[allow(dead_code)] #[allow(dead_code)]
pub fn extract(src: &[u8], src_w: usize, x: usize, y: usize, w: usize, h: usize) -> Vec<u8> { pub fn extract(src: &[u16], src_w: usize, x: usize, y: usize, w: usize, h: usize) -> Vec<u16> {
let mut out = vec![0u8; w * h]; let mut out = vec![0u16; w * h];
for row in 0..h { for row in 0..h {
let s = src_w * (y + row) + x; let s = src_w * (y + row) + x;
let d = w * row; let d = w * row;
@@ -21,9 +22,10 @@ pub fn extract(src: &[u8], src_w: usize, x: usize, y: usize, w: usize, h: usize)
} }
/// Blit `src` (width `src_w`, height derived from src.len()) into `dst` at (dx, dy). /// Blit `src` (width `src_w`, height derived from src.len()) into `dst` at (dx, dy).
/// Clips against all four edges of `dst`; the destination position may be negative. /// Opaque copy, no transparency. Clips against all four edges of `dst`; the
/// destination position may be negative.
#[allow(dead_code)] #[allow(dead_code)]
pub fn blit(dst: &mut [u8], dst_w: usize, dx: i32, dy: i32, src: &[u8], src_w: usize) { pub fn blit(dst: &mut [u16], dst_w: usize, dx: i32, dy: i32, src: &[u16], src_w: usize) {
let dst_w_i = dst_w as i32; let dst_w_i = dst_w as i32;
let dst_h = (dst.len() / dst_w) as i32; let dst_h = (dst.len() / dst_w) as i32;
let src_h = (src.len() / src_w) as i32; let src_h = (src.len() / src_w) as i32;
@@ -46,9 +48,9 @@ pub fn blit(dst: &mut [u8], dst_w: usize, dx: i32, dy: i32, src: &[u8], src_w: u
} }
} }
/// Blit an 8×8 tile with transparency: index 0 is skipped. /// Blit an 8×8 tile with transparency: pixels whose mask bit is clear are skipped.
/// Clips against all four edges of `dst`; the destination position may be negative. /// Clips against all four edges of `dst`; the destination position may be negative.
pub fn blit_tile(dst: &mut [u8], dst_w: usize, dx: i32, dy: i32, tile: &[u8; 64]) { pub fn blit_tile(dst: &mut [u16], dst_w: usize, dx: i32, dy: i32, tile: &Tile) {
let dst_w_i = dst_w as i32; let dst_w_i = dst_w as i32;
let dst_h = (dst.len() / dst_w) as i32; let dst_h = (dst.len() / dst_w) as i32;
@@ -62,9 +64,9 @@ pub fn blit_tile(dst: &mut [u8], dst_w: usize, dx: i32, dy: i32, tile: &[u8; 64]
if x < 0 || x >= dst_w_i { if x < 0 || x >= dst_w_i {
continue; continue;
} }
let px = tile[(row * 8 + col) as usize]; let i = (row * 8 + col) as usize;
if px != 0 { if tile.mask & (1 << i) != 0 {
dst[dst_w * y as usize + x as usize] = px; dst[dst_w * y as usize + x as usize] = tile.px[i];
} }
} }
} }
+4 -9
View File
@@ -4,15 +4,13 @@ use std::time::{Duration, Instant};
mod assets; mod assets;
mod game; mod game;
mod input; mod input;
mod net;
fn main() { fn main() {
let mut plat = Platform::new(PlatformConfig { let mut plat = Platform::new(PlatformConfig {
title: "Roguelike".into(), title: "Forgotten Caves".into(),
window_size: (800, 600), window_size: (800, 600),
framebuffer_size: (320, 240), framebuffer_size: (320, 240),
aspect_ratio: Some(4.0 / 3.0), aspect_ratio: Some(4.0 / 3.0),
palette: pbio::palette::RGB332,
vsync: true, vsync: true,
mouse_capture: false, mouse_capture: false,
mouse_visible: true, mouse_visible: true,
@@ -21,12 +19,9 @@ fn main() {
let input_map = input::InputMap::new(); let input_map = input::InputMap::new();
let mut input_state = input::InputState::new(); let mut input_state = input::InputState::new();
let mut last_update = Instant::now(); let mut last_update = Instant::now();
// Optional server address (e.g. netsim proxy): `client 127.0.0.1:7778`. // Optional map path: `game assets/map_large`.
let server_addr = std::env::args().nth(1) let map_path = std::env::args().nth(1).unwrap_or_else(|| "assets/map_test".into());
.unwrap_or_else(|| "127.0.0.1:7777".into()) let mut game = game::Game::start(&map_path);
.parse()
.expect("bad server address");
let mut game = game::Game::start(server_addr);
while !plat.should_close() { while !plat.should_close() {
plat.poll_events(Some(Duration::from_millis(20))); plat.poll_events(Some(Duration::from_millis(20)));
-170
View File
@@ -1,170 +0,0 @@
//! Bad-internet simulator: a UDP proxy between game client and server that adds
//! delay, jitter and packet loss, per direction. Dev tool only — the game itself
//! never knows it exists.
//!
//! Usage:
//! cargo run -p netsim -- [--listen 7778] [--upstream 127.0.0.1:7777]
//! [--delay MS] [--jitter MS] [--loss PERCENT]
//! [--up-delay MS] [--up-jitter MS] [--up-loss PERCENT]
//! [--down-delay MS] [--down-jitter MS] [--down-loss PERCENT]
//!
//! `--delay/--jitter/--loss` set both directions; the `--up-*` (client → server) and
//! `--down-*` (server → client) variants override one direction. Jitter is uniform in
//! ±MS around the delay; reordering emerges from jitter naturally, as on a real link.
//! Point the client at the listen port (e.g. `client 127.0.0.1:7778`).
use std::cmp::Reverse;
use std::collections::BinaryHeap;
use std::net::{SocketAddr, UdpSocket};
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
#[derive(Clone, Copy)]
struct LinkParams {
delay_ms: f32,
jitter_ms: f32,
loss_pct: f32,
}
/// Xorshift64* — plenty for impairment dice; avoids pulling in a rand dependency.
struct Rng(u64);
impl Rng {
fn new() -> Self {
let seed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos() as u64;
Rng(seed | 1)
}
fn next(&mut self) -> u64 {
let mut x = self.0;
x ^= x >> 12;
x ^= x << 25;
x ^= x >> 27;
self.0 = x;
x.wrapping_mul(0x2545_F491_4F6C_DD1D)
}
/// Uniform in [0, 1).
fn unit(&mut self) -> f32 {
(self.next() >> 40) as f32 / (1u64 << 24) as f32
}
}
impl LinkParams {
/// Roll the dice for one datagram: `None` = lost, `Some(d)` = deliver after `d`.
fn impair(&self, rng: &mut Rng) -> Option<Duration> {
if rng.unit() * 100.0 < self.loss_pct {
return None;
}
let jitter = (rng.unit() * 2.0 - 1.0) * self.jitter_ms;
Some(Duration::from_secs_f32((self.delay_ms + jitter).max(0.0) / 1000.0))
}
}
/// A datagram waiting for its delivery time. `Up` = client → server.
enum Dir {
Up,
Down,
}
struct Pending {
due: Instant,
dir: Dir,
data: Vec<u8>,
}
// BinaryHeap ordering: only `due` matters (earliest first via Reverse).
impl PartialEq for Pending {
fn eq(&self, other: &Self) -> bool { self.due == other.due }
}
impl Eq for Pending {}
impl PartialOrd for Pending {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { Some(self.cmp(other)) }
}
impl Ord for Pending {
fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.due.cmp(&other.due) }
}
fn parse_args() -> (u16, SocketAddr, LinkParams, LinkParams) {
let mut listen: u16 = 7778;
let mut upstream: SocketAddr = "127.0.0.1:7777".parse().unwrap();
let mut both = LinkParams { delay_ms: 0.0, jitter_ms: 0.0, loss_pct: 0.0 };
let mut up_over = [None::<f32>; 3]; // delay, jitter, loss
let mut down_over = [None::<f32>; 3];
let mut args = std::env::args().skip(1);
while let Some(flag) = args.next() {
let val = args.next().unwrap_or_else(|| panic!("missing value for {flag}"));
let num = || val.parse::<f32>().unwrap_or_else(|_| panic!("bad number for {flag}: {val}"));
match flag.as_str() {
"--listen" => listen = val.parse().expect("bad listen port"),
"--upstream" => upstream = val.parse().expect("bad upstream address"),
"--delay" => both.delay_ms = num(),
"--jitter" => both.jitter_ms = num(),
"--loss" => both.loss_pct = num(),
"--up-delay" => up_over[0] = Some(num()),
"--up-jitter" => up_over[1] = Some(num()),
"--up-loss" => up_over[2] = Some(num()),
"--down-delay" => down_over[0] = Some(num()),
"--down-jitter" => down_over[1] = Some(num()),
"--down-loss" => down_over[2] = Some(num()),
_ => panic!("unknown flag {flag}"),
}
}
let apply = |over: [Option<f32>; 3]| LinkParams {
delay_ms: over[0].unwrap_or(both.delay_ms),
jitter_ms: over[1].unwrap_or(both.jitter_ms),
loss_pct: over[2].unwrap_or(both.loss_pct),
};
(listen, upstream, apply(up_over), apply(down_over))
}
fn main() {
let (listen, upstream, up, down) = parse_args();
let client_sock = UdpSocket::bind(("127.0.0.1", listen)).expect("bind listen port");
let server_sock = UdpSocket::bind("127.0.0.1:0").expect("bind upstream socket");
server_sock.connect(upstream).expect("connect upstream");
client_sock.set_nonblocking(true).unwrap();
server_sock.set_nonblocking(true).unwrap();
println!("netsim: 127.0.0.1:{listen} ⇄ {upstream}");
println!(" up: {:.0} ms ±{:.0} ms, {:.1}% loss", up.delay_ms, up.jitter_ms, up.loss_pct);
println!(" down: {:.0} ms ±{:.0} ms, {:.1}% loss", down.delay_ms, down.jitter_ms, down.loss_pct);
let mut rng = Rng::new();
let mut queue: BinaryHeap<Reverse<Pending>> = BinaryHeap::new();
// The one game client, learned from its first datagram (re-learned on address change).
let mut client_addr: Option<SocketAddr> = None;
let mut buf = [0u8; 2048];
loop {
let now = Instant::now();
while let Ok((n, from)) = client_sock.recv_from(&mut buf) {
client_addr = Some(from);
if let Some(d) = up.impair(&mut rng) {
queue.push(Reverse(Pending { due: now + d, dir: Dir::Up, data: buf[..n].to_vec() }));
}
}
while let Ok(n) = server_sock.recv(&mut buf) {
if let Some(d) = down.impair(&mut rng) {
queue.push(Reverse(Pending { due: now + d, dir: Dir::Down, data: buf[..n].to_vec() }));
}
}
while queue.peek().is_some_and(|Reverse(p)| p.due <= now) {
let Reverse(p) = queue.pop().unwrap();
match p.dir {
Dir::Up => { server_sock.send(&p.data).ok(); }
Dir::Down => {
if let Some(addr) = client_addr {
client_sock.send_to(&p.data, addr).ok();
}
}
}
}
std::thread::sleep(Duration::from_millis(1));
}
}
+4 -4
View File
@@ -2,7 +2,7 @@
## Current state ## Current state
Input is fully decoupled from the windowing backend. `client/src/input.rs` owns all input Input is fully decoupled from the windowing backend. `game/src/input.rs` owns all input
types. `main.rs` translates `pbio::Event::Key` events into `GameAction` values each frame and types. `main.rs` translates `pbio::Event::Key` events into `GameAction` values each frame and
stores them in an `InputState`. `game::update()` receives a `&InputState` and queries it with stores them in an `InputState`. `game::update()` receives a `&InputState` and queries it with
three distinct semantics. (The platform/windowing layer lives in the external `pbio` crate; three distinct semantics. (The platform/windowing layer lives in the external `pbio` crate;
@@ -10,7 +10,7 @@ game code only ever sees `pbio::Key`, never a raw backend type.)
## Architecture ## Architecture
### `GameAction` — `client/src/input.rs` ### `GameAction` — `game/src/input.rs`
Logical actions the game cares about. Game code never sees backend key types. Logical actions the game cares about. Game code never sees backend key types.
@@ -18,7 +18,7 @@ Logical actions the game cares about. Game code never sees backend key types.
pub enum GameAction { Up, Down, Left, Right, Confirm, Cancel } pub enum GameAction { Up, Down, Left, Right, Confirm, Cancel }
``` ```
### `InputMap` — `client/src/input.rs` ### `InputMap` — `game/src/input.rs`
Translates a `pbio::Key` to an `Option<GameAction>`. All key bindings live here. Translates a `pbio::Key` to an `Option<GameAction>`. All key bindings live here.
@@ -31,7 +31,7 @@ Translates a `pbio::Key` to an `Option<GameAction>`. All key bindings live here.
| Enter | Confirm | | Enter | Confirm |
| Escape | Cancel | | Escape | Cancel |
### `InputState` — `client/src/input.rs` ### `InputState` — `game/src/input.rs`
Three internal buffers, populated from `pbio::Event::Key` events in `main.rs`: Three internal buffers, populated from `pbio::Event::Key` events in `main.rs`:
+19 -12
View File
@@ -2,15 +2,22 @@
## Current state ## Current state
TGA loading is implemented in `client/src/assets.rs`. `Image::from_tga(path)` reads the file, PNG loading is implemented in `game/src/assets.rs`. `Image::from_png(path)` decodes via the
parses the 18-byte header, skips the colormap block, and returns an `Image` — supporting `png` crate (any color type, expanded to 8-bit RGBA) and quantizes on load to the
type 1 (uncompressed) and type 9 (RLE) via the private `decode_rle()` function in the same framebuffer's RGB565 format with `pbio::rgb565`; alpha ≥ 128 counts as opaque. The result
module. `game.rs` uses it directly: `Image::from_tga("assets/tilesets/overworld.tga")`. is an `Image { width, height, pixels: Vec<u16>, opaque: Vec<bool> }`. Artists work in
truecolor; there is no palette an asset could be "in the wrong order" for. `game.rs` uses it
directly: `Image::from_png("assets/tilesets/overworld.png")`.
`Image` also provides `to_tileset() -> Vec<[u8; 64]>`, which splits the image into 8×8 `Image` also provides `to_tileset() -> Vec<Tile>`, which splits the image into 8×8 tiles
tiles row-major. Every tile is emitted; tile ID is the flat row-major index into the Vec, row-major. A `Tile` is 64 RGB565 pixels plus a `u64` opacity bitmask. Every tile is
so tiles are never skipped or reordered. (Transparency is a per-pixel concern handled at emitted; tile ID is the flat row-major index into the Vec, so tiles are never skipped or
blit time by `blit_tile`, not by the splitter.) reordered. `overworld.png` is the unmodified Mini-Medieval `Overworld.png` sheet (24
columns), so Tiled tile ids line up with the Vec index.
History: the first iteration used 8-bit indexed TGAs on a palette framebuffer (RGB332,
`pbio` master branch). The colormap-ordering fragility and the index-0-is-transparent
convention are gone with the switch to `pbio`'s `truecolor16` branch.
`AssetStore` (a named registry) is **not yet implemented**. Assets are currently loaded `AssetStore` (a named registry) is **not yet implemented**. Assets are currently loaded
inline in `Game::start()`. inline in `Game::start()`.
@@ -22,12 +29,12 @@ files directly at the call site.
## Design notes ## Design notes
- `Image` struct: `width: u32`, `height: u32`, `pixels: Vec<u8>` — done. - `Image` struct: `width: u32`, `height: u32`, `pixels: Vec<u16>`, `opaque: Vec<bool>` — done.
- A simple `AssetStore` could be a `HashMap<&'static str, Image>` loaded at startup. - A simple `AssetStore` could be a `HashMap<&'static str, Image>` loaded at startup.
- No streaming needed — the whole game is small enough to load everything upfront in `start()`. - No streaming needed — the whole game is small enough to load everything upfront in `start()`.
- Palette index 0 is transparent when blitting sprites. This convention is enforced by - Transparency is the tile's mask bit, checked by `blit_tile()` in `pixelhelper.rs`. Any
`blit_tile()` in `pixelhelper.rs`; the loader itself does not need to handle it. color — black included — can be an opaque pixel.
- Pixel assets are stored as flat `Vec<u8>` of palette indices, row-major, width × height bytes. - Pixel assets are stored as flat `Vec<u16>` of RGB565 values, row-major, width × height.
- Future: consider embedding assets with `include_bytes!` to produce a single binary. - Future: consider embedding assets with `include_bytes!` to produce a single binary.
## Open questions ## Open questions
+6 -7
View File
@@ -6,20 +6,19 @@ No text rendering exists yet. No font loading or glyph blitting code exists.
## Goal ## Goal
Draw ASCII text into the `u8` framebuffer using a bitmap font, respecting the palette-index Draw ASCII text into the RGB565 framebuffer using a bitmap font.
color model (one u8 per pixel, no RGBA).
## Design notes ## Design notes
- A bitmap font is the right fit: each glyph is a small tile of palette indices, blitted - A bitmap font is the right fit: each glyph is a small masked tile, blitted
directly into the framebuffer using the existing blit primitive in `pixelhelper.rs`. directly into the framebuffer using the existing blit primitive in `pixelhelper.rs`.
- Likely approach: a fixed 8×8 or similar glyph grid packed into a TGA or raw binary, - Likely approach: a fixed 8×8 or similar glyph grid packed into a PNG,
one glyph per ASCII codepoint starting at 0x20 (space). one glyph per ASCII codepoint starting at 0x20 (space).
- The font image itself should be indexed: foreground pixels carry a nonzero palette index - The font image only needs its alpha mask: glyph pixels are opaque, background transparent;
(caller supplies the color), background pixels are index 0 (transparent/skip). the caller supplies the color and `draw_text` paints the mask with it.
- API sketch: - API sketch:
```rust ```rust
fn draw_text(frame: &mut [u8], font: &Font, x: i32, y: i32, color: u8, text: &str); fn draw_text(frame: &mut [u16], font: &Font, x: i32, y: i32, color: u16, text: &str);
``` ```
- `Font` holds the glyph sheet as an `Image` plus glyph width/height. - `Font` holds the glyph sheet as an `Image` plus glyph width/height.
+43 -65
View File
@@ -1,18 +1,41 @@
# Hybrid Tick Loop # Hybrid Tick Loop
Single 24 Hz base tick; subsystems self-select frequency via stride scheduling. Replaces the old Single 24 Hz base tick; subsystems self-select frequency via stride scheduling. The sim is
3-tier (20/10/1 Hz buckets) description from roadmap item 06. headless (`sim` crate) and advanced tick by tick from the game loop.
---
## Driving the sim from the render loop
Rendering runs at frame rate; the sim runs in whole ticks. `Game::update` accumulates frame
time and calls `Sim::step()` once per elapsed `TICK_MS` (`game/src/game.rs`):
```rust
self.tick_accum_ms += (dt as f32).min(MAX_FRAME_MS);
while self.tick_accum_ms >= TICK_MS {
self.tick_accum_ms -= TICK_MS;
if self.sim.step() { // true when a movement window executed
self.track_lerp(); // entity positions may have changed
self.sync_route(); // click-to-move bookkeeping
self.step_movement(input);// schedule the next step right away
}
}
```
`MAX_FRAME_MS` (250 ms) caps how much a single slow frame catches up, so a debugger pause or
window drag drops time instead of spiralling into a burst of ticks. Rendering interpolates
entity positions between tiles over one movement interval (`EntityLerp`), so the 6 Hz
movement cadence looks smooth at any frame rate.
--- ---
## Stride scheduling ## Stride scheduling
All subsystems hang off one monotonic `tick: u32` counter. Each subsystem fires when its All subsystems hang off one monotonic `Sim::tick` counter. Each subsystem fires when its
stride condition is met. **Current implementation:** stride condition is met. **Current implementation:**
``` ```
tick % 1 == 0 → input processing, inbound recv (main.rs) tick % 1 == 0 → (game loop) input processing, intent scheduling
tick % 2 == send_phase → broadcast state to clients, phase-staggered (net.rs, 12 Hz/client)
tick % 4 == 0 → entity_tick() — movement / collision (sim.rs, 6 Hz) tick % 4 == 0 → entity_tick() — movement / collision (sim.rs, 6 Hz)
tick % 8 == 0 → (reserved) slower AI routines, pathfinding refresh tick % 8 == 0 → (reserved) slower AI routines, pathfinding refresh
tick % 192 == 0 → (reserved) world simulation (8-sec cycle: weather, daylight, respawns) tick % 192 == 0 → (reserved) world simulation (8-sec cycle: weather, daylight, respawns)
@@ -26,8 +49,21 @@ at `% 192` is exactly an 8-second cycle.
`entity_tick` = one tile of movement, so the tick rate *is* the movement rate. 6 Hz yields the `entity_tick` = one tile of movement, so the tick rate *is* the movement rate. 6 Hz yields the
target ~6 tiles/sec. Once `energy`/`speed` are reintroduced, `entity_tick` can move up to 12 Hz target ~6 tiles/sec. Once `energy`/`speed` are reintroduced, `entity_tick` can move up to 12 Hz
(`% 2`) and per-entity `speed` sets the effective movement rate instead — decoupling sim rate (`% 2`) and per-entity `speed` sets the effective movement rate instead — decoupling sim rate
from movement rate. The broadcast rate (12 Hz/client) is deliberately higher than the movement from movement rate.
rate: it keeps position latency low and adds redundancy against packet loss.
---
## Intents: one action per entity per movement window
Actors do not act directly on the world; they schedule an intent (`Sim::set_action`). The
sim keeps exactly one intent per entity, setting it again replaces it, and executing the
movement window consumes it. So an actor moves at most one tile per window no matter how
often it changes its mind in between, and a tap shorter than a window still registers.
The player's intent is (re)set every frame from held keys or the click-to-move route; NPCs
will use the same call from their `think()` (roadmap 08). Validation goes through
`sim::step_allowed` on both sides — the pathfinder plans with the exact function the sim
executes, so a planned route never contains a step the sim will reject.
--- ---
@@ -57,61 +93,3 @@ if entity.energy >= ACTION_COST {
Fast entities (`speed >= ACTION_COST`) act every tick. Slow entities act every N ticks Fast entities (`speed >= ACTION_COST`) act every tick. Slow entities act every N ticks
naturally without any scheduler involvement. Haste and slow effects become simple `speed` naturally without any scheduler involvement. Haste and slow effects become simple `speed`
modifiers — no special-case scheduling needed. modifiers — no special-case scheduling needed.
---
## Simulation rate vs network rate
Sim and net rates are independent. The current targets:
| Layer | Rate | Stride |
|-------|------|--------|
| Base loop | 24 Hz | every tick |
| Network broadcast (per client, phase-staggered) | 12 Hz | `tick % 2 == send_phase` |
| Entity tick (movement / collision) | 6 Hz | `tick % 4 == 0` |
Clients receive a fresh `EntityPacket` every ~83 ms. For tile-based movement at 5–6 tiles/sec
this is sufficient with comfortable headroom.
Rough bandwidth per client at 12 Hz:
- `StatePacket` 72 B × 12 = 864 B/s
- `EntityPacket` ~1200 B × 12 = 14 400 B/s
- Total: ~15 KB/s outbound per client
---
## Tick-offset broadcasting (phase staggering)
Rather than flushing all clients on the same tick, assign each client a `send_phase` at
connection time and send only when the client's phase matches:
```rust
// ClientState gains:
send_phase: u8, // assigned as entity_id % BROADCAST_STRIDE at creation
// broadcast() skips clients whose phase doesn't match current tick:
if tick % BROADCAST_STRIDE as u32 != cs.send_phase as u32 { continue; }
```
Benefits:
- Outbound NIC load is flat across ticks instead of spiking every 2nd tick
- Scales with player count without architectural changes
- Each client still receives state at the same effective rate
---
## Why higher tick rate doesn't help high-ping players
```
Perceived latency ≈ RTT + tick_processing_delay + state_interval
LTE @ 100ms RTT, 24Hz sim, 12Hz net:
worst case: 100 + 42 + 83 = 225ms ← RTT-dominated
Same at 64Hz sim, 20Hz net:
worst case: 100 + 15 + 50 = 165ms ← 60ms gain, negligible for roguelike
```
RTT is the dominant term. Higher tick rates yield diminishing returns and increase server
CPU load for minimal perceived benefit. 24 Hz is sufficient for deliberate tile-based input
and remains acceptable at LTE latencies (~100ms RTT) and remote locations (~200ms RTT).
+14 -10
View File
@@ -1,7 +1,7 @@
# Map Loader # Map Loader
Loads level data from a Tiled CSV export, replacing the old hardcoded `build_world` wall loop. Loads level data from a Tiled CSV export, replacing the old hardcoded `build_world` wall loop.
Implemented in `server/src/map.rs`; wired into the world in `server/src/main.rs::load_world`. Implemented in `sim/src/map.rs`; wired into the world in `sim/src/lib.rs::load_world`.
--- ---
@@ -37,8 +37,8 @@ therefore shows up in the CSV as a large *negative* decimal when read as `i32`.
The loader parses each field as `i64`, reinterprets the low 32 bits as `u32`, and strips the The loader parses each field as `i64`, reinterprets the low 32 bits as `u32`, and strips the
flip flags with `& 0x1FFF_FFFF`, leaving the bare tile id. **Flip orientation is discarded for flip flags with `& 0x1FFF_FFFF`, leaving the bare tile id. **Flip orientation is discarded for
now** — flipped tiles render unflipped. Real flipping would need a flip-aware `blit_tile` on now** — flipped tiles render unflipped. Real flipping would need a flip-aware `blit_tile` plus
the client plus the flags carried through the protocol; deferred to the camera/sprite pass (09). a flip flag next to the tile id in the chunk; deferred to the camera/sprite pass (09).
--- ---
@@ -46,19 +46,23 @@ the client plus the flags carried through the protocol; deferred to the camera/s
Tile ids are used directly: Tile ids are used directly:
- **Server** — `tile_flags(id)` (in `map.rs`) maps an id to gameplay `TileFlags` - **Sim** — `tile_flags(id)` (in `map.rs`) maps an id to gameplay `TileFlags`
(collidable / opaque). Hand-maintained vocabulary; currently everything is walkable floor. (collidable / opaque) via the `sim::tile_collidable` vocabulary. Hand-maintained; test values only.
- **Client** — the id indexes straight into `overworld.tga` (`game.rs`). The tileset's tile - **Renderer** — the id indexes straight into `overworld.png` (`game.rs`). The tileset's tile
order *is* the id space; keep the Tiled tileset and `overworld.tga` in lockstep. order *is* the id space; keep the Tiled tileset and `overworld.png` in lockstep.
No `firstgid` subtraction: the authored ids already line up with `overworld.tga`. No `firstgid` subtraction: the authored ids already line up with `overworld.png`.
--- ---
## World assembly ## World assembly
`load_world` slices the map into 32×32 chunks via `Chunk::generate`, which builds each chunk's `load_world` slices the map into 32×32 chunks via `Chunk::generate`. A chunk is a plain
6-bit palette automatically. The map's top-left tile sits at world (0, 0). `[u16; 1024]` of tile ids; flags are derived from the id on lookup (`map::tile_flags`). That
array is also the natural on-disk layout should chunks ever be streamed from disk (RLE over
the `u16`s would do) — the CSV stays the interchange format for external tooling. The map's
top-left tile sits at world (0, 0). Loading a 256×256 map takes well under a millisecond in
release; the map path is the game's first CLI argument (`game assets/map_large`).
**Edge border:** a 30×30 map only fills part of chunk (0,0) (which spans tiles 0–31). Tiles **Edge border:** a 30×30 map only fills part of chunk (0,0) (which spans tiles 0–31). Tiles
inside a loaded chunk but outside the authored map are emitted as an *invisible solid border* inside a loaded chunk but outside the authored map are emitted as an *invisible solid border*
-324
View File
@@ -1,324 +0,0 @@
# Protocol
Wire format for client-server communication.
## Transport
- UDP (`std::net::UdpSocket`, non-blocking)
- Hard limit: **1200 bytes per datagram** (safe internet MTU; avoids IP fragmentation)
- All integers: little-endian
- Server streams `StatePackets` continuously; client sends `ActionPackets` only when needed
---
## Header
Every datagram begins with a 6-byte universal header.
| Offset | Size | Type | Field | Notes |
|--------|------|------|---------------|-----------------------------------------|
| 0 | 2 | u16 | `magic` | Fixed: `0x524C` ("RL") — rejects noise |
| 2 | 1 | u8 | `version` | Protocol version — server rejects mismatch |
| 3 | 1 | u8 | `packet_type` | See packet type table below |
| 4 | 2 | u16 | `client_type` | 0 = official client; others registered |
`client_type` allows the server to distinguish official clients, registered bots, and
forks for analytics and access control, without affecting the protocol logic.
---
## Packet types
| Packet type | id | Flow | Size |
|---------------|----|------------------|------------------|
| State | 0 | server → client | 72 bytes |
| Action | 1 | client → server | min. 74 bytes |
| Chunk Data | 2 | server → client | max. 909 bytes |
| Entity | 3 | server → client | max. 1200 bytes |
| Entity Query | 4 | client → server | 18 bytes |
| Entity Detail | 5 | server → client | max. 1200 bytes |
| Ping | 6 | client → server | 14 bytes |
| Pong | 7 | server → client | 14 bytes |
---
### StatePacket — server → client
Sent by the server at 10 Hz regardless of client activity.
Currently carries the chunk manifest for the 3×3 neighbourhood around the player.
| Offset | Size | Type | Field | Notes |
|--------|------|------------|---------------------|-------------------------------------------------|
| 0 | 6 | Header | `header` | packet_type = 0 |
| 6 | 4 | u32 | `tick` | Server tick counter |
| 10 | 54 | ChunkEntry | `chunks[9]` | 9 chunk entries |
| 64 | 4 | u32 | `player_entity_id` | Global entity ID of the receiving client |
| 68 | 4 | u32 | `entity_checksum` | FNV-1a over all EntityPacket payloads this tick |
**Total: 72 bytes**
The `entity_checksum` lets the client detect a lost `EntityPacket` without a dedicated
ACK: if the checksum differs from the one computed over the last received entity update,
the client knows to retransmit an `ActionPacket` (`target_tick = 0`, no-op action) to
prompt the server to re-send the current entity state.
---
### ActionPacket — client → server
Sent by the client on player action or on a chunk cache miss.
| Offset | Size | Type | Field | Notes |
|--------|------|--------------|-----------------|--------------------------------|
| 0 | 6 | Header | `header` | packet_type = 1 |
| 6 | 8 | u64 | `auth_token` | Token of the current session |
| 14 | 4 | u32 | `target_tick` | Tick the action is scheduled for (see below); 0 = keep-alive/ack only |
| 18 | 54 | ChunkEntry | `cache[9]` | Versions client currently holds |
| 72 | 2 | PlayerAction | `player_action` | Derived from user input |
| 74 | ? | ActionData | `action_data` | Dependent on PlayerAction |
**Minimum: 74 bytes** (no ActionData)
**Tick-addressed scheduling.** Actions are scheduled onto the server's tick timeline
instead of being consumed in arrival order. `target_tick` selects the movement window
(`target_tick / TICKS_PER_MOVE`, rounded up) the action executes in:
- A second action addressed to the same window **replaces** the first — this is how the
client retracts (NOOP) or changes a scheduled step until its window executes, and how
retransmits dedupe for free.
- A **late** action (window already passed on arrival) moves to the next window, but
only if that slot is empty: late actions fill gaps, they never override newer intent.
- Only the next `ACTION_WINDOW_HORIZON` windows are addressable; anything beyond is
dropped. Combined with one-action-per-window execution this bounds server memory and
movement speed regardless of client behavior.
- `target_tick = 0` carries no scheduling intent (keep-alive / cache-ack packets).
**ChunkEntry (6 bytes)**
| Offset | Size | Type | Field |
|--------|------|------|------------|
| 0 | 4 | u32 | `chunk_id` |
| 4 | 2 | u16 | `version` |
`chunk_id` encodes the chunk grid position: `(x as u16) | ((y as u16) << 16)`,
where x and y are signed chunk coordinates (i16 each).
**PlayerAction (u16)**
| Value | Action |
|-------|---------|
| 0 | No-Op (cache update only) |
| 1 | North |
| 2 | East |
| 3 | South |
| 4 | West |
$TODO — additional actions (interact, etc.)
---
### ChunkPacket — server → client
Sent by the server for each chunk the client is missing or has stale.
One chunk per datagram.
The world is divided into **32×32 tile chunks**.
- At most **4 chunks (2×2)** are visible at once.
- The 3×3 neighbourhood (9 chunks) covers all prefetch needs.
- Each chunk carries a **local palette** of up to 64 tile types.
- Tiles are encoded as 6-bit palette indices (4 tiles per 3 bytes, no padding).
| Offset | Size | Type | Field | Notes |
|-------------------|----------|--------------|-------------|---------------------------------|
| 0 | 6 | Header | `header` | packet_type = 2 |
| 6 | 6 | ChunkEntry | `chunk` | ID and version of this chunk |
| 12 | 1 | u8 | `pal_count` | Number of palette entries (≤64) |
| 13 | max. 128 | u16[] | `palette` | Global tile IDs, pal_count × 2 |
| 13 + pal_count×2 | 768 | packed u6[] | `tiles` | 1024 tiles, 6-bit indices |
**Maximum: 6 + 6 + 1 + 128 + 768 = 909 bytes**
**Palette** — up to 64 entries, each a global tile ID (u16) mapping local 6-bit index → world tile type.
**Tiles** — 1024 tiles packed as 256 groups of 3 bytes (4 tiles × 6 bits = 24 bits per group).
---
### EntityPacket — server → client
Sent by the server each tick, immediately after the `StatePacket`.
Contains the bulk entity update for all entities within the visible **30×30 tile viewport**.
Positions are **absolute world tile coordinates**.
If the entity count exceeds what fits in one datagram, the server sends multiple
`EntityPacket`s on the same tick; `packet_flags` bit 0 signals that more follow.
| Offset | Size | Type | Field | Notes |
|--------|----------------|-------------|----------------|--------------------------------|
| 0 | 6 | Header | `header` | packet_type = 3 |
| 6 | 4 | u32 | `tick` | Matches the `StatePacket` tick |
| 10 | 1 | u8 | `entity_count` | Entities in this datagram |
| 11 | 1 | u8 | `packet_flags` | bit 0: more packets follow |
| 12 | entity_count×… | EntityEntry | `entities` | Variable-length entries |
**Maximum: 1200 bytes** — without metadata: ⌊(1200 − 12) / 18⌋ = **66 entities per datagram**.
---
#### EntityEntry
| Offset | Size | Type | Field | Notes |
|--------|------------|------|-----------------|-----------------------------------------|
| 0 | 4 | u32 | `id` | Server-assigned entity ID (persistent) |
| 4 | 2 | u16 | `type_id` | Entity type; 0 = player |
| 6 | 2 | i16 | `pos_x` | Absolute world tile X |
| 8 | 2 | i16 | `pos_y` | Absolute world tile Y |
| 10 | 2 | u16 | `hp` | Current HP |
| 12 | 2 | u16 | `hp_max` | Max HP |
| 14 | 2 | u16 | `elo` | Elo rating (acts as level) |
| 16 | 1 | u8 | `entity_flags` | See flag table below |
| 17 | 1 | u8 | `meta_len` | Byte length of the TLV metadata block |
| 18 | `meta_len` | u8[] | `meta` | TLV metadata (0 bytes if none) |
**Base size: 18 bytes** (plus `meta_len` bytes of metadata).
**entity_flags:**
| Bit | Meaning |
|-----|-------------------------------------------------------|
| 0 | `is_static` — does not move (chest, sign, item stack) |
| 1 | `is_hostile` |
| 2 | `is_interactable` — show interaction prompt |
| 3–7 | reserved |
---
#### Inline metadata — TLV format
Inline metadata is a sequence of TLV (type–length–value) fields appended directly after
the fixed `EntityEntry` fields. `meta_len = 0` means no metadata is present.
Each TLV field:
| Offset | Size | Type | Field |
|--------|------|------|---------|
| 0 | 1 | u8 | `tag` |
| 1 | 1 | u8 | `len` |
| 2 | len | u8[] | `value` |
**Defined tags:**
| Tag | Name | Value format | Notes |
|------|------------------|--------------|--------------------------------|
| 0x01 | `status_effects` | u16 bitmask | Active status effects |
| 0x02 | `weapon` | u16 type_id | Currently equipped weapon |
| 0x03 | `armor` | u16 type_id | Currently equipped armor |
Large metadata (chest inventory, sign text, dialogue trees) is not included inline.
Request it via `EntityQueryPacket` (type 4) on player interaction.
---
### EntityQueryPacket — client → server
Sent when the client needs detailed metadata for a specific entity (on interaction,
hover, or similar trigger). The server responds with an `EntityDetailPacket`.
| Offset | Size | Type | Field | Notes |
|--------|------|--------|--------------|------------------------------|
| 0 | 6 | Header | `header` | packet_type = 4 |
| 6 | 8 | u64 | `auth_token` | Token of the current session |
| 14 | 4 | u32 | `entity_id` | Entity to query |
**Total: 18 bytes**
---
### EntityDetailPacket — server → client
Full metadata response for a queried entity. Sent in reply to an `EntityQueryPacket`.
| Offset | Size | Type | Field | Notes |
|--------|------------|--------|--------------|------------------------------|
| 0 | 6 | Header | `header` | packet_type = 5 |
| 6 | 4 | u32 | `tick` | Server tick at time of query |
| 10 | 4 | u32 | `entity_id` | Entity being described |
| 14 | 2 | u16 | `meta_len` | Byte length of metadata |
| 16 | `meta_len` | u8[] | `meta` | Full TLV metadata block |
**Maximum: 1200 bytes** (up to 1184 bytes of metadata).
---
### PingPacket / PongPacket — round-trip measurement
Liveness and RTT probe. The client sends a `PingPacket`; the server echoes the payload back
unchanged as a `PongPacket`. Both share the same layout.
| Offset | Size | Type | Field | Notes |
|--------|------|--------|----------------|--------------------------------------|
| 0 | 6 | Header | `header` | packet_type = 6 (Ping) / 7 (Pong) |
| 6 | 8 | u64 | `timestamp_ms` | Echoed verbatim by the server |
**Total: 14 bytes**
The client currently measures RTT locally via the elapsed time since the ping was sent, so
`timestamp_ms` is sent as 0; the field is reserved for server-stamped timing if needed later.
---
## Protocol flow
```
── every tick (10 Hz) ────────────────────────────────────────────────────────
server → client StatePacket (tick N, chunk manifest, entity_checksum)
server → client EntityPacket (tick N, entities 0–73)
server → client EntityPacket (tick N, entities 74–N, more=0) ← if needed
↓ client verifies entity_checksum
↓ checksum mismatch → retransmit ActionPacket (no-op)
↓ chunk cache miss on chunk X →
client → server ActionPacket (auth_token, seq, cache state, player_action)
server → client ChunkPacket (chunk X, full data)
server → client ChunkPacket (chunk Y, full data) ← if multiple misses
── on player interaction ─────────────────────────────────────────────────────
client → server EntityQueryPacket (auth_token, entity_id)
server → client EntityDetailPacket (entity_id, full TLV metadata)
```
The server reads the `ActionPacket`'s cache list, computes the diff against current
chunk versions, and sends one `ChunkPacket` per missing or stale chunk.
---
## Reliability
UDP is unreliable. The protocol handles loss without a dedicated ACK mechanism:
**Lost ActionPacket** — the server never learns of the cache miss and keeps streaming
StatePackets. The client retransmits the ActionPacket after **200–300 ms** if no
ChunkPacket has arrived.
**Lost ChunkPacket** — the server has already processed the ActionPacket and will not
retransmit spontaneously. The client's timeout fires (200–300 ms), it resends the
ActionPacket with its updated cache state (listing only still-missing chunks), and
the server sends the missing chunks again.
**Duplicate ActionPackets** — the server handles these idempotently. The cache list is
self-describing state; the server reads it, diffs against current versions, and sends
whatever is still missing. No deduplication logic is required.
**Lost StatePacket** — the next tick delivers the same chunk manifest. No retry needed;
the client simply waits one tick (~100 ms).
**Lost EntityPacket** — detected via the `entity_checksum` in the next `StatePacket`.
The client retransmits a no-op `ActionPacket` (`target_tick = 0`, no-op action,
current cache state); the server treats this as a normal diff request and re-sends the
full entity update for the tick.
**Lost EntityQueryPacket / EntityDetailPacket** — the client retransmits the query
after a 200–300 ms timeout if no `EntityDetailPacket` has arrived.
+80 -154
View File
@@ -1,129 +1,66 @@
# Roadmap # Roadmap
Workspace layout: `shared/` (wire types), `server/` (authoritative sim), `client/` (render Local single-player roguelike with a continuously simulated, tick-based world.
terminal). The platform layer — window, GPU, input, RGB332 palette — lives in the external
`pbio` crate (git dependency), not in this repo. Workspace layout: `sim/` (headless world simulation — tiles, entities, movement rules,
tick loop; no platform dependencies, unit-testable) and `game/` (the executable — window,
rendering, input, pathfinding, camera; owns a `Sim` and drives it). The platform layer —
window, GPU, input, RGB565 framebuffer — lives in the external `pbio` crate (git dependency,
branch `truecolor16`),
not in this repo.
History: this fork started from a client/server multiplayer architecture (authoritative UDP
server, thin client with prediction). The scope was cut to single player in September 2026;
the sim kept its authoritative shape, everything network-related was removed. The `sim`
crate's `tick`/`set_action` interface is the seam a server could be wrapped around again.
--- ---
## Achieved milestones ## Achieved milestones
- [x] TGA loader: `Image`, `from_tga`, `to_tileset`, `decode_rle` in `client/src/assets.rs` - [x] PNG loader: `Image::from_png` (via the `png` crate, quantized to RGB565 on load, alpha
- [x] Pixel helpers: `set_pixel`, `extract`, `blit`, `blit_tile` in `client/src/game/pixelhelper.rs` mask), `to_tileset` → `Vec<Tile>` in `game/src/assets.rs`. Framebuffer is 16-bit
— signed `i32` offsets, clip against all four framebuffer edges, index 0 = transparent truecolor (`pbio` branch `truecolor16`)
- [x] Input system: `GameAction`, `InputMap`, `InputState` in `client/src/input.rs` (over `pbio::Key`) - [x] Pixel helpers: `set_pixel`, `extract`, `blit`, `blit_tile` in `game/src/game/pixelhelper.rs`
- [x] Protocol spec: `notes/protocol.md` — Header, State, Action, Chunk, Entity, EntityQuery, — signed `i32` offsets, clip against all four framebuffer edges, per-tile opacity mask
EntityDetail, Ping/Pong; reliability model - [x] Input system: `GameAction`, `InputMap`, `InputState` in `game/src/input.rs` (over `pbio::Key`)
- [x] Shared crate: packet structs as `bytemuck::Pod` with compile-time size assertions, - [x] World model: `World` of 32×32 `Chunk`s (plain `[u16; 1024]` tile ids, flags derived
`player_action` / `packet_type` constants, `chunk_id` / `chunk_coords` on lookup — the network-era 64-entry palette is gone), entities indexed per chunk,
- [x] Server binary: 24 Hz tick loop, 3×3-chunk test world (`build_world`), wall collision, `entities_in_viewport`. Map path is the first CLI argument; `assets/map_large` is a
per-client action queue, `ChunkPacket` dispatch on cache miss, stale-client eviction generated 256×256 stress map
- [x] Client net: `NetClient` in `client/src/net.rs`, non-blocking UDP recv, - [x] Map loader: Tiled-CSV parser in `sim/src/map.rs` (flip-flag masking, content-derived
`StatePacket` / `EntityPacket` / `ChunkPacket` / `PongPacket` dispatch, entity-checksum retransmit
- [x] End-to-end loop: player moves on server, position reflected in `EntityPacket`, rendered on client
- [x] Tile rendering: chunk-based world rendering from received `ChunkPacket` data (6-bit unpack)
- [x] Tick-based movement: movement resolved in `entity_tick`, currently every 4th tick (`sim.rs`)
- [x] Sprite-based entity rendering: entity `type_id` → tile in `entities.tga`, blitted with transparency
- [x] Phase-staggered broadcast: per-client `send_phase`, flat outbound load across ticks
- [x] Ping/Pong RTT measurement (`PingPacket` / `PongPacket`)
- [x] Connection timeout: client emits `Disconnected` after 10 s without a `StatePacket`;
server evicts clients unseen for 10 s
- [x] Map loader: Tiled-CSV parser in `server/src/map.rs` (flip-flag masking, content-derived
dimensions), `load_world` slices it into chunks, invisible solid border at the map rim. dimensions), `load_world` slices it into chunks, invisible solid border at the map rim.
Replaces the hardcoded `build_world`. See `notes/07-map-loader.md` See `notes/07-map-loader.md`
- [x] Client movement prediction: queued unconfirmed steps (`path` in `client/src/game.rs`), - [x] Tile rendering: chunk-based world rendering straight from `World` (`render_viewport`)
paced at the server's 6 Hz movement cadence, reconciled against `EntityPacket` positions, - [x] Sprite-based entity rendering: entity `type_id` → tile in `entities.png`, blitted with transparency
dropped after idle timeout. Shared collision vocabulary `shared::tile_collidable` - [x] Hybrid tick loop: 24 Hz base tick, movement in `entity_tick` every 4th tick (6 Hz);
(currently empty — every tile walkable) keeps client prediction and server sim in lockstep. the game loop drives the sim with a fixed-timestep accumulator. See `notes/06-hybrid-tick-loop.md`
- [x] Click-to-move: framebuffer click → world tile, A* over the chunk cache - [x] Intent model: one scheduled action per entity per movement window (`Sim::set_action`),
(`client/src/game/pathfind.rs`, unknown chunks count as blocked), route translated into replaced on re-set, consumed on execution. Headless tests in `sim/src/sim.rs`
cardinal actions one step per movement interval — the server only ever sees movement
actions and stays authoritative. Keyboard input cancels the route; each step is
re-validated at send time and a blocked step voids the route. In-flight steps render
bright blue, planned route dim blue. Click-and-hold steers continuously: while the
button is held the route keeps replanning toward the tile under the cursor (only
when that tile changes — cursor or camera movement), sweeping across blocked tiles
keeps the current route, and a voided route replans automatically while held.
- [x] 8-directional movement in chessboard geometry: world physics use the Chebyshev - [x] 8-directional movement in chessboard geometry: world physics use the Chebyshev
metric — diagonal and cardinal steps are the same distance, a "circle" is a square metric — diagonal and cardinal steps are the same distance, a "circle" is a square
of tiles, matching the square viewport. Four diagonal actions in of tiles, matching the square viewport. The single-step rule lives in
`shared::player_action`; the single-step rule lives in `shared::step_allowed` `sim::step_allowed` (king move onto a free tile, diagonals additionally need both
(king move onto a free tile, diagonals additionally need both orthogonal neighbors orthogonal neighbors free — no corner cutting) and is the one function used by the
free — no corner cutting) and is the one function used by the server sim, client sim and the A*. Two held keys walk diagonally.
send-time validation and the client A* (8-connected, Chebyshev heuristic). Two held - [x] Collision vocabulary seeded with test values (`sim::tile_collidable`): id 146
keys walk diagonally. (trees/rocks) and id 0 (empty / the invisible world border). A proper tile-data file
- [x] Collision vocabulary seeded with test values (`shared::tile_collidable`): id 146 format replaces this table later.
(trees/rocks) and id 0 — id 0 doubles as the server's invisible world border, which - [x] Click-to-move: framebuffer click → world tile, A* over the world
the client previously mispredicted as walkable. A proper tile-data file format (`game/src/game/pathfind.rs`, 8-connected, Chebyshev heuristic), route fed into the
replaces this table later. sim one step per movement window. Keyboard input cancels the route; each step is
- [x] Tick-addressed action scheduling (supersedes two interim designs — a sequence- re-validated at scheduling time and a blocked step replans toward the persistent
deduped FIFO queue and its flow control — that fixed a periodic walking hitch and goal. Click-and-hold steers continuously: while the button is held the route keeps
a path/route deadlock but kept two free-running clocks racing each other). Actions replanning toward the tile under the cursor (only when that tile changes — cursor
are now scheduled onto the server's tick timeline: `ActionPacket.target_tick` or camera movement), sweeping across blocked tiles keeps the current route.
(formerly `sequence`) selects the movement window, a second action to the same - [x] Smooth camera follow: the camera is a float pixel position (`cam` in `game.rs`)
window *replaces* the first (retraction via NOOP, rescheduling, retransmit dedup), that tracks the player linearly at ~64 px/s, snapping only to whole pixels at
late actions fill only an *empty* next window (gap-filling without overriding render time (and outright on teleport-sized corrections). The viewport renders
newer intent — needed over real internet links so actions don't die pointlessly), with sub-tile offsets (31×31 tile pass + right-strip clip).
and only `ACTION_WINDOW_HORIZON = 3` future windows are addressable. The sim keeps - [x] Entity interpolation: per-id lerp table (`EntityLerp` in `game.rs`) — previous/current
per-entity window slot maps and executes at most one action per window, so floods tile plus a clock, rendered as a pixel lerp over one movement interval. Jumps of
can neither grow memory nor speed anyone up. The client estimates the server tick more than one tile (Chebyshev) snap. Purely cosmetic; game logic keeps using the
from `StatePacket.tick` plus elapsed time and schedules each step into the next sim's tile positions.
window — one send per window by construction, no local send timer, no clock-rate
race. `target_tick = 0` marks keep-alive/cache-ack packets with no scheduling
intent. See `notes/protocol.md` (ActionPacket).
- [x] Client-side unexpected-state handling: the movement goal is persistent (outlives
the planned route) and every surprise reroutes toward it — a blocked route step
replans instead of voiding the plan, and a confirmed position off the predicted
path (lost/rejected/overridden step) retracts all still-scheduled steps (NOOP to
their windows), drops the stale prediction and replans from the confirmed tile.
Changing plans mid-run (new click/steer target) likewise retracts scheduled-but-
unexecuted steps, so old intent stops playing out on the server within a window.
The goal is released on arrival, unreachability, keyboard override, or a discrete
click on an unreachable tile. This is the "reactive replanning" item formerly
parked under Later — Robustness.
- [x] `netsim` — bad-internet simulator (workspace member, dev tool): a UDP proxy adding
delay, jitter and loss per direction (`--delay/--jitter/--loss`, `--up-*`/`--down-*`
overrides; reordering emerges from jitter). No root, game-traffic only, zero deps.
The client takes an optional server address argument to point at it:
`cargo run -p netsim -- --delay 80 --jitter 30 --loss 5` + `client 127.0.0.1:7778`.
- [x] Bad-link hardening (netsim immediately broke the naive scheduling — locks under
isolated loss 15% and isolated delay 120 ms):
(a) RTT-adaptive scheduling lead — the client pings automatically (1 Hz, smoothed),
and schedules `ceil((rtt + margin) / window)` windows ahead instead of always one:
under systematic latency "late" had been the *normal* case. The retraction horizon
moves out the same way (a cancellation needs the same lead an action does).
(b) Late-rule cleanup in the sim — a late movement action keeps its *order*, not
its time (first still-empty upcoming window), so bunched late arrivals no longer
collapse onto one slot and eat each other; late NOOPs are dropped outright (as
gap-fillers they used to block real steps: retract → late NOOPs poison upcoming
windows → replanned steps eaten → retract again — a lock loop).
(c) Client stall watchdog — steps in flight but nothing confirmed for 750 ms means
the prediction is dead no matter why (e.g. *all* in-flight actions lost: the server
never moves, so the moved-off-plan desync detection never fires, path stays full,
nothing is ever sent again): retract, drop, replan toward the goal.
`PATH_MAX_LEN` is back to 8 as a pure prediction bound — server safety now comes
from window addressing, and on a slow link several correct steps are legitimately
unconfirmed at once (confirmations lag a full RTT).
- [x] Prediction rebuilt as predict → ack → replay (replaces the per-problem patches
above with structural robustness; fixed multi-second replan storms at high ping).
The old model stored absolute predicted tiles and reconciled by tile matching, so
any surprise "invalidated" the whole prediction and recovery meant clear + replan
from the confirmed position — but steps inside the retraction horizon cannot be
cancelled and still execute ("zombies"), shifting the server off every fresh plan
and re-triggering recovery in a loop. Now: pending steps are `(window, delta)`
pairs, `EntityPacket.tick` is the acknowledgment cursor (every window ≤ tick/4 is
provably consumed — executed, rejected or lost, it no longer matters which), and
the predicted position is always *derived* by replaying pending deltas on top of
the confirmed position. A surprise shifts the prediction instead of killing it; a
route that no longer connects triggers one clean replan toward the persistent goal
via the existing send-time validation. Deleted outright: tile-matching reconcile,
the moved-off-plan desync heuristic, the stall watchdog, and the idle path drop —
acked windows expire pending steps automatically, so the path cannot go stale.
The replay applies each pending delta through the shared `step_allowed` rule —
exactly as the server will — so a delta the server is going to reject does not
move the prediction either, and the predicted position can never sit inside a
wall (previously a diverged prediction could, causing a brief walk-into-wall
lock until the acks caught up).
--- ---
@@ -131,13 +68,10 @@ terminal). The platform layer — window, GPU, input, RGB332 palette — lives i
### 06 — Hybrid tick loop *(partially done)* ### 06 — Hybrid tick loop *(partially done)*
Design of record: **`notes/06-hybrid-tick-loop.md`** (24 Hz base tick + stride scheduling). Design of record: **`notes/06-hybrid-tick-loop.md`**.
This supersedes the original 20/10/1 Hz three-tier sketch.
- [x] 24 Hz base loop (`server/src/main.rs`) - [x] 24 Hz base loop driven from the game loop
- [x] Phase-staggered broadcast at 12 Hz/client (`send_phase` in `server/src/net.rs`) - [x] Stride layout settled: `entity_tick` at `%4` (6 Hz, sets movement rate)
- [x] Stride layout settled: `entity_tick` at `%4` (6 Hz, sets movement rate), broadcast at
`%2` (12 Hz/client). The `06` note now documents this and why.
- [ ] Action-point model (`energy` / `speed` per entity, act when `energy >= ACTION_COST`). - [ ] Action-point model (`energy` / `speed` per entity, act when `energy >= ACTION_COST`).
Removed from `entity.rs` for now as premature — reintroduce when NPCs (08) actually need it. Removed from `entity.rs` for now as premature — reintroduce when NPCs (08) actually need it.
Then `entity_tick` can move to `%2` (12 Hz) and `speed` sets the effective movement rate. Then `entity_tick` can move to `%2` (12 Hz) and `speed` sets the effective movement rate.
@@ -148,66 +82,61 @@ This supersedes the original 20/10/1 Hz three-tier sketch.
Remaining follow-ups when the need is concrete: Remaining follow-ups when the need is concrete:
- Collision vocabulary: seeded with test values (0, 146) in `shared::tile_collidable`. - Collision vocabulary: seeded with test values (0, 146) in `sim::tile_collidable`.
Decide on a proper file format for tile data (collision, opacity, …) instead of a Decide on a proper file format for tile data (collision, opacity, …) instead of a
hardcoded match, then feed both server and client from it. hardcoded match.
- Tile flipping: orientation is discarded on load; revisit with the sprite pass (09). - Tile flipping: orientation is discarded on load; revisit with the sprite pass (09).
- Multiple / object layers (spawns, triggers) — not yet parsed. - Multiple / object layers (spawns, triggers) — not yet parsed. The player currently
spawns at (0, 0).
--- ---
### 08 — Basic NPC entity + AI budget ### 08 — Basic NPC entity + AI budget
One dumb wandering enemy. Validates the simulation architecture before complexity accumulates. One dumb wandering enemy. Validates the simulation architecture before complexity accumulates.
Depends on the action-point model from 06 being reintroduced. Depends on the action-point model from 06 being reintroduced. NPCs schedule intents through
the same `Sim::set_action` the player uses.
- Add `EntityKind::Npc` with a `think() -> u32` method returning budget cost - Add `EntityKind::Npc` with a `think() -> u32` method returning budget cost
- Per sim-tick: distribute `think_budget = BASE / (clients + 1)` across entities ordered by player proximity - Per sim-tick: distribute a fixed `think_budget` across entities ordered by player proximity
- Complex entities consume more budget; simple ones less. Loop breaks at zero — natural load shedding. - Complex entities consume more budget; simple ones less. Loop breaks at zero — natural load shedding.
- No framework. No trait objects yet. A match on `EntityKind` is fine. - No framework. No trait objects yet. A match on `EntityKind` is fine.
```rust ```rust
let mut budget: u32 = BASE_BUDGET / (client_count + 1).max(1); let mut budget: u32 = BASE_BUDGET;
for entity in entities_by_player_proximity() { for entity in entities_by_player_proximity() {
if budget == 0 { break; } if budget == 0 { break; }
budget = budget.saturating_sub(entity.think(&mut world)); budget = budget.saturating_sub(entity.think(&mut world));
} }
``` ```
- Entity-vs-entity blocking: `step_allowed` only checks tiles. Once NPCs exist, occupied
tiles need to block too — in the sim *and* in the pathfinder's `blocked` closure.
--- ---
### 09 — Client camera *(camera follow done, entity interpolation open)* ### 09 — Client camera *(done — see Achieved)*
Sprite rendering already landed (see Achieved). Camera status: Sprite rendering, camera follow and entity interpolation have all landed.
- [x] Smooth camera follow: the camera is a float pixel position (`cam` in `game.rs`)
that tracks the player linearly at ~64 px/s, snapping only to whole pixels at
render time (and outright on teleport-sized corrections). The viewport renders
with sub-tile offsets (31×31 tile pass + right-strip clip).
- [x] Entity interpolation: per-id lerp table (`EntityLerp` in `game.rs`) beside the
entity list — previous/current tile plus a clock, rendered as a pixel lerp over
one movement interval. Jumps of more than one tile (Chebyshev) snap. Purely
cosmetic; game logic keeps using authoritative tile positions.
--- ---
### Later — UI / HUD ### Later — UI / HUD
- Bitmap font renderer: 8×8 glyph sheet TGA, `draw_text(frame, font, x, y, color, text)` — see `notes/04` - Bitmap font renderer: 8×8 glyph sheet PNG, `draw_text(frame, font, x, y, color, text)` — see `notes/04`
- UI primitives in `client/src/ui.rs`: `fill_rect`, `draw_rect`, `draw_hbar` — see `notes/05` - UI primitives in `game/src/ui.rs`: `fill_rect`, `draw_rect`, `draw_hbar` — see `notes/05`
- HUD layout (320×240): HP bar + player name bottom 16px, message log right panel - HUD layout (320×240): HP bar + player name bottom 16px, message log right panel
- A real clip-rect on the blit primitives belongs here — once the viewport and HUD panels are - A real clip-rect on the blit primitives belongs here — once the viewport and HUD panels are
two distinct regions, the abstraction earns its keep. Not before. two distinct regions, the abstraction earns its keep. Not before.
--- ---
### Later — Robustness + auth ### Later — Distribution
- Auth token handshake: replace source-address identity (`auth_token` field exists but is unused) - Asset embedding: `include_bytes!` for single-binary distribution (assets are currently
- Per-address rate limiting for the action ingest (only meaningful once identity is real — read relative to the working directory)
address spoofing bypasses any limit before then) - Save / load: the world is a plain `Sim` value — serialize it. Roguelike convention is a
- Multi-datagram `EntityPacket`: server currently truncates at 66 entities (`net.rs` TODO) single save slot deleted on load
- Asset embedding: `include_bytes!` for single-binary distribution
--- ---
@@ -215,11 +144,8 @@ Sprite rendering already landed (see Achieved). Camera status:
Once the simulation architecture is stable and a real level exists: Once the simulation architecture is stable and a real level exists:
- Doors, interactive objects (server-authoritative state) - Doors, interactive objects
- Combat: melee range check, HP, death, respawn - Combat: melee range check, HP, death, permadeath
- Inventory: on-demand via `EntityQueryPacket` / `EntityDetailPacket` - Inventory
- Day/night and weather on the slow tick — affect visibility, spawns - Day/night and weather on the slow tick (`% 192`) — affect visibility, spawns
- Procedural dungeon generation (BSP or cellular automata feeding into the same tile format) - Procedural dungeon generation (BSP or cellular automata feeding into the same tile format)
---
</content>
-8
View File
@@ -1,8 +0,0 @@
[package]
name = "server"
version = "0.1.0"
edition = "2024"
[dependencies]
shared = { path = "../shared" }
bytemuck = "1.24.0"
-102
View File
@@ -1,102 +0,0 @@
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
pub struct TileFlags(pub u8);
impl TileFlags {
pub const COLLIDABLE: u8 = 1 << 0;
pub const OPAQUE: u8 = 1 << 1;
pub const LIQUID: u8 = 1 << 2;
pub fn collidable(self) -> bool { self.0 & Self::COLLIDABLE != 0 }
pub fn opaque(self) -> bool { self.0 & Self::OPAQUE != 0 }
pub fn liquid(self) -> bool { self.0 & Self::LIQUID != 0 }
}
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
pub struct TileDef { pub tile_id: u16, pub flags: TileFlags }
#[derive(Debug)]
pub struct PaletteFullError;
pub struct Chunk {
pub palette: [TileDef; 64],
pub pal_count: u8,
pub tiles: Box<[u8; 1024]>,
pub version: u16,
}
impl Chunk {
/// Runtime use: all tiles empty, pal_count=1, version=1.
/// Clients with version=0 will always request this chunk.
pub fn new() -> Self {
Self {
palette: [TileDef::default(); 64],
pal_count: 1,
tiles: Box::new([0u8; 1024]),
version: 1,
}
}
/// Find existing palette entry or insert a new one.
/// Index 0 is always the null sentinel; this short-circuits for default TileDef.
/// Returns (palette_index, was_new). Does NOT bump version.
fn find_or_insert_raw(&mut self, def: TileDef) -> Result<(u8, bool), PaletteFullError> {
if def == TileDef::default() {
return Ok((0, false));
}
for i in 1..self.pal_count as usize {
if self.palette[i] == def {
return Ok((i as u8, false));
}
}
if self.pal_count >= 64 {
return Err(PaletteFullError);
}
let idx = self.pal_count;
self.palette[idx as usize] = def;
self.pal_count += 1;
Ok((idx, true))
}
/// World gen path. Calls the closure for every tile coordinate.
/// version=0 so the first StatePacket causes all clients to request it.
pub fn generate(f: impl Fn(u8, u8) -> TileDef) -> Result<Self, PaletteFullError> {
let mut chunk = Self {
palette: [TileDef::default(); 64],
pal_count: 1,
tiles: Box::new([0u8; 1024]),
version: 0,
};
for ly in 0u8..32 {
for lx in 0u8..32 {
let def = f(lx, ly);
let (idx, _) = chunk.find_or_insert_raw(def)?;
chunk.tiles[ly as usize * 32 + lx as usize] = idx;
}
}
chunk.version = 1;
Ok(chunk)
}
/// Set a tile at local coords. Bumps version if tile index or palette changed.
pub fn set_tile(&mut self, lx: u8, ly: u8, def: TileDef) -> Result<(), PaletteFullError> {
let (idx, was_new) = self.find_or_insert_raw(def)?;
let slot = &mut self.tiles[ly as usize * 32 + lx as usize];
let changed = *slot != idx;
*slot = idx;
if changed || was_new {
self.version = self.version.wrapping_add(1);
if self.version == 0 {
self.version = 1;
}
}
Ok(())
}
pub fn get_tile(&self, lx: u8, ly: u8) -> TileDef {
self.palette[self.tiles[ly as usize * 32 + lx as usize] as usize]
}
pub fn tile_flags(&self, lx: u8, ly: u8) -> TileFlags {
self.get_tile(lx, ly).flags
}
}
-71
View File
@@ -1,71 +0,0 @@
mod chunk;
mod entity;
mod world;
mod sim;
mod net;
mod map;
use std::thread;
use std::time::{Duration, Instant};
use chunk::{Chunk, TileDef, TileFlags};
use world::World;
use sim::Sim;
use net::Net;
use map::TileMap;
const TICK_HZ: u32 = 24;
const TICK_DURATION: Duration = Duration::from_micros(1_000_000 / TICK_HZ as u64);
/// Build a world from a loaded tile map. The map's top-left tile sits at world (0, 0); the
/// map is sliced into 32×32 chunks. Tiles inside a loaded chunk but outside the authored map
/// become an invisible solid border (id 0, collidable), so the walkable world edge sits flush
/// with the visible map rim instead of with the larger chunk boundary.
fn load_world(map: &TileMap) -> World {
let mut w = World::new();
let max_cx = (map.width.saturating_sub(1) >> 5) as i16;
let max_cy = (map.height.saturating_sub(1) >> 5) as i16;
for cy in 0..=max_cy {
for cx in 0..=max_cx {
let chunk = Chunk::generate(|lx, ly| {
let (wx, wy) = world::local_to_tile(cx, cy, lx, ly);
if wx < 0 || wy < 0 || wx >= map.width as i16 || wy >= map.height as i16 {
return TileDef { tile_id: 0, flags: TileFlags(TileFlags::COLLIDABLE) };
}
let id = map.tile(wx, wy);
TileDef { tile_id: id, flags: map::tile_flags(id) }
}).expect("palette overflow during world gen");
w.set_chunk(cx, cy, chunk);
}
}
w
}
fn main() {
let map = TileMap::from_csv("assets/map_test");
println!("loaded map {}×{} ({} tiles)", map.width, map.height, map.tiles.len());
let world = load_world(&map);
let mut sim = Sim::new(world);
let mut net = Net::new("127.0.0.1:7777").expect("failed to bind UDP socket");
let mut next_tick = Instant::now() + TICK_DURATION;
let mut tick = 0u32;
loop {
net.drain_recv();
net.evict_stale();
for addr in net.drain_new_clients() {
let entity_id = sim.world.spawn_entity(0, (0, 0), 100);
net.add_client(addr, entity_id);
println!("client connected: {addr} → entity {entity_id}");
}
let actions = net.drain_actions();
sim.tick(tick, &actions);
net.broadcast(&sim.world, tick);
thread::sleep(next_tick.saturating_duration_since(Instant::now()));
next_tick += TICK_DURATION;
tick = tick.wrapping_add(1);
}
}
-256
View File
@@ -1,256 +0,0 @@
use std::collections::{HashMap, VecDeque};
use std::mem::size_of;
use std::net::{SocketAddr, UdpSocket};
use std::sync::mpsc::{self, Receiver, SyncSender};
use std::thread;
use std::time::Instant;
use bytemuck::bytes_of;
use shared::{
ActionPacket, ChunkEntry, ChunkPacket, EntityEntry, EntityPacket,
Header, PingPacket, PongPacket, StatePacket,
MAGIC, VERSION, packet_type,
};
use crate::world::World;
pub struct NetClient {
pub entity_id: u32,
pub last_seen: Instant,
pub send_phase: u8,
/// Tick-addressed actions received since the last drain, in arrival order —
/// `(target_tick, action)`. Window resolution, replacement and the lateness rule
/// all live in the sim; this is only the per-tick hand-off buffer, bounded so a
/// flood between drains cannot grow it.
pub pending_actions: VecDeque<(u32, u16)>,
pub last_cache: [ChunkEntry; 9],
}
/// Hand-off buffer bound. Drained every tick; an honest client sends at most one
/// action per movement window, so a handful is already generous.
const PENDING_ACTIONS_MAX: usize = 8;
pub struct Net {
clients: HashMap<SocketAddr, NetClient>,
new_addrs: Vec<SocketAddr>,
recv_rx: Receiver<(SocketAddr, Vec<u8>)>,
send_tx: SyncSender<(SocketAddr, Vec<u8>)>,
}
impl Net {
pub fn new(addr: &str) -> std::io::Result<Self> {
let socket = UdpSocket::bind(addr)?;
let (recv_tx, recv_rx) = mpsc::sync_channel::<(SocketAddr, Vec<u8>)>(256);
let (send_tx, send_rx) = mpsc::sync_channel::<(SocketAddr, Vec<u8>)>(256);
let recv_sock = socket.try_clone()?;
thread::spawn(move || {
let mut buf = [0u8; 1200];
loop {
match recv_sock.recv_from(&mut buf) {
Ok((n, addr)) => {
if recv_tx.send((addr, buf[..n].to_vec())).is_err() {
break;
}
}
Err(_) => break,
}
}
});
let send_sock = socket.try_clone()?;
thread::spawn(move || {
loop {
match send_rx.recv() {
Ok((addr, data)) => { let _ = send_sock.send_to(&data, addr); }
Err(_) => break,
}
}
});
// socket is dropped here; send/recv threads own their copies
drop(socket);
Ok(Net { clients: HashMap::new(), new_addrs: Vec::new(), recv_rx, send_tx })
}
pub fn add_client(&mut self, addr: SocketAddr, entity_id: u32) {
self.clients.insert(addr, NetClient {
entity_id,
last_seen: Instant::now(),
send_phase: (entity_id % 2) as u8,
pending_actions: VecDeque::new(),
last_cache: [ChunkEntry { chunk_id: 0, version: 0 }; 9],
});
}
pub fn drain_recv(&mut self) {
loop {
match self.recv_rx.try_recv() {
Ok((addr, data)) => self.handle_packet(addr, data),
Err(_) => break,
}
}
}
fn handle_packet(&mut self, addr: SocketAddr, data: Vec<u8>) {
if data.len() < size_of::<Header>() {
return;
}
let header: Header = bytemuck::pod_read_unaligned(&data[..size_of::<Header>()]);
if header.magic != MAGIC || header.version != VERSION {
return;
}
if header.packet_type == packet_type::ACTION && data.len() == size_of::<ActionPacket>() {
let pkt: ActionPacket = bytemuck::pod_read_unaligned(&data);
if let Some(client) = self.clients.get_mut(&addr) {
client.last_seen = Instant::now();
client.last_cache = pkt.cache;
// `target_tick == 0` marks pure keep-alive / cache-ack packets — they
// must never enter the schedule. Everything else (including NOOPs, which
// *retract* the addressed window) is handed to the sim in arrival order.
let target_tick = pkt.target_tick;
if target_tick != 0 && client.pending_actions.len() < PENDING_ACTIONS_MAX {
client.pending_actions.push_back((target_tick, pkt.player_action));
}
} else if !self.new_addrs.contains(&addr) {
self.new_addrs.push(addr);
}
} else if header.packet_type == packet_type::PING && data.len() == size_of::<PingPacket>() {
let pkt: PingPacket = bytemuck::pod_read_unaligned(&data);
let pong = PongPacket {
header: Header::new(packet_type::PONG),
timestamp_ms: pkt.timestamp_ms,
};
let _ = self.send_tx.send((addr, bytes_of(&pong).to_vec()));
}
}
pub fn drain_new_clients(&mut self) -> Vec<SocketAddr> {
std::mem::take(&mut self.new_addrs)
}
pub fn evict_stale(&mut self) {
self.clients.retain(|_, c| c.last_seen.elapsed().as_secs() < 10);
}
pub fn drain_actions(&mut self) -> Vec<(u32, u32, u16)> {
self.clients.values_mut()
.flat_map(|c| {
let entity_id = c.entity_id;
c.pending_actions.drain(..)
.map(move |(target_tick, action)| (entity_id, target_tick, action))
})
.collect()
}
pub fn broadcast(&self, world: &World, tick: u32) {
for (addr, client) in &self.clients {
if tick % 2 != client.send_phase as u32 { continue; }
// Step 1 — resolve player position
let Some(entity) = world.entities.get(&client.entity_id) else { continue };
let pos = entity.pos;
let (player_cx, player_cy) = crate::world::tile_to_chunk(pos.0, pos.1);
// Step 2 — build 9-slot chunk manifest
let mut chunks = [ChunkEntry { chunk_id: 0, version: 0 }; 9];
for dy in -1i16..=1 {
for dx in -1i16..=1 {
let slot = ((dy + 1) * 3 + (dx + 1)) as usize;
let cx = player_cx + dx;
let cy = player_cy + dy;
chunks[slot] = ChunkEntry {
chunk_id: shared::chunk_id(cx, cy),
version: world.chunk(cx, cy).map_or(0, |c| c.version),
};
}
}
// Step 3 — build EntityEntry list + checksum
let viewport_entities = world.entities_in_viewport(pos);
let mut entries: Vec<EntityEntry> = viewport_entities.iter().map(|e| EntityEntry {
id: e.id,
type_id: e.type_id,
pos_x: e.pos.0,
pos_y: e.pos.1,
hp: e.hp,
hp_max: e.hp_max,
elo: 0,
entity_flags: 0,
meta_len: 0,
}).collect();
if entries.len() > 66 {
entries.truncate(66); // TODO: multi-datagram
}
let checksum = fnv1a_entities(&entries);
// Step 4 — send StatePacket (72 bytes)
let state = StatePacket {
header: Header::new(packet_type::STATE),
tick,
chunks,
player_entity_id: client.entity_id,
entity_checksum: checksum,
};
let _ = self.send_tx.send((*addr, bytes_of(&state).to_vec()));
// Step 5 — send EntityPacket (1200 bytes)
let mut pkt = <EntityPacket as bytemuck::Zeroable>::zeroed();
pkt.header = Header::new(packet_type::ENTITY);
pkt.tick = tick;
pkt.entity_count = entries.len() as u8;
pkt.packet_flags = 0; // no more-flag; single datagram
for (i, e) in entries.iter().enumerate() { pkt.entities[i] = *e; }
let _ = self.send_tx.send((*addr, bytes_of(&pkt).to_vec()));
// Step 6 — send ChunkPackets for cache misses
for slot in 0..9 {
let current = chunks[slot];
let cached = client.last_cache[slot];
let miss = cached.chunk_id != current.chunk_id || cached.version != current.version;
if miss {
let (cx, cy) = shared::chunk_coords(current.chunk_id);
if let Some(chunk) = world.chunk(cx, cy) {
let mut cpkt = <ChunkPacket as bytemuck::Zeroable>::zeroed();
cpkt.header = Header::new(packet_type::CHUNK);
cpkt.chunk = current;
cpkt.pal_count = chunk.pal_count;
for i in 0..64 { cpkt.palette[i] = chunk.palette[i].tile_id; }
cpkt.tiles = pack_tiles(&chunk.tiles);
let _ = self.send_tx.send((*addr, bytes_of(&cpkt).to_vec()));
}
}
}
}
}
}
fn fnv1a_entities(entries: &[EntityEntry]) -> u32 {
const FNV_BASIS: u32 = 2_166_136_261;
const FNV_PRIME: u32 = 16_777_619;
let mut h = FNV_BASIS;
for e in entries {
for &byte in bytes_of(e) {
h ^= byte as u32;
h = h.wrapping_mul(FNV_PRIME);
}
}
h
}
fn pack_tiles(tiles: &[u8; 1024]) -> [u8; 768] {
let mut raw = [0u8; 768];
for i in 0..256 {
let ia = tiles[i*4] & 0x3F;
let ib = tiles[i*4+1] & 0x3F;
let ic = tiles[i*4+2] & 0x3F;
let id = tiles[i*4+3] & 0x3F;
raw[i*3] = ia | (ib << 6);
raw[i*3+1] = (ib >> 2) | (ic << 4);
raw[i*3+2] = (ic >> 4) | (id << 2);
}
raw
}
-85
View File
@@ -1,85 +0,0 @@
use std::collections::{BTreeMap, HashMap};
use shared::{ACTION_WINDOW_HORIZON, TICKS_PER_MOVE};
use crate::world::World;
pub struct Sim {
pub world: World,
/// Scheduled actions per entity, keyed by movement window (`tick / TICKS_PER_MOVE`).
/// The window slot is the whole ordering model: an action addressed to an occupied
/// window *replaces* it (retraction and rescheduling by the client, dedup of
/// retransmits), a late action only fills the next window if it is empty, and
/// anything past `ACTION_WINDOW_HORIZON` is dropped — so no client can grow server
/// memory or move faster than one action per window.
pending: HashMap<u32, BTreeMap<u32, u16>>,
}
impl Sim {
pub fn new(world: World) -> Self {
Self { world, pending: HashMap::new() }
}
pub fn tick(&mut self, tick: u32, actions: &[(u32, u32, u16)]) {
// The earliest window still addressable at this tick. On a movement tick that is
// the window executing *this call* — actions arriving the same tick still count.
let min_window = tick.div_ceil(TICKS_PER_MOVE);
for &(entity_id, target_tick, action) in actions {
let slots = self.pending.entry(entity_id).or_default();
let window = target_tick.div_ceil(TICKS_PER_MOVE);
if window < min_window {
// Late. A movement action keeps its *order* instead of its time: it
// fills the first still-empty upcoming window, so bunched late arrivals
// don't collapse onto one slot and eat each other. A late NOOP is
// dropped — once its window has passed, its cancellation intent is
// ambiguous, and as a gap-filler it would block real steps (worst case
// one stale step executes; the client's reconciliation handles that).
if shared::action_delta(action).is_some() {
let gap = (min_window..min_window + ACTION_WINDOW_HORIZON as u32)
.find(|w| !slots.contains_key(w));
if let Some(w) = gap {
slots.insert(w, action);
}
}
} else if window - min_window < ACTION_WINDOW_HORIZON as u32 {
slots.insert(window, action); // newest addressing wins
}
}
if tick.is_multiple_of(TICKS_PER_MOVE) {
self.entity_tick(tick / TICKS_PER_MOVE);
}
}
fn entity_tick(&mut self, window: u32) {
// Exactly one action per entity per movement window, then the window is gone.
let actions: Vec<(u32, u16)> = self.pending.iter_mut()
.filter_map(|(&id, slots)| {
let action = slots.remove(&window);
slots.retain(|&w, _| w > window); // drop anything the timeline passed by
action.map(|a| (id, a))
})
.collect();
self.pending.retain(|_, slots| !slots.is_empty());
for (entity_id, action) in actions {
let delta = match shared::action_delta(action) {
Some(d) => d,
None => continue, // NOOP or garbage
};
let pos = match self.world.entities.get(&entity_id) {
Some(e) => e.pos,
None => continue,
};
let from = (pos.0 as i32, pos.1 as i32);
let to = (from.0 + delta.0, from.1 + delta.1);
// `step_allowed` is the shared movement rule (chessboard geometry, no corner
// cutting) — the client predicts with the exact same function.
let allowed = shared::step_allowed(from, to, |x, y| {
self.world.tile_flags(x as i16, y as i16).map_or(true, |f| f.collidable())
});
if allowed {
self.world.move_entity(entity_id, (to.0 as i16, to.1 as i16));
}
}
}
}
-7
View File
@@ -1,7 +0,0 @@
[package]
name = "shared"
version = "0.1.0"
edition = "2024"
[dependencies]
bytemuck = { version = "1.24.0", features = ["derive", "min_const_generics"] }
-225
View File
@@ -1,225 +0,0 @@
use bytemuck::{Pod, Zeroable};
pub mod player_action {
pub const NOOP: u16 = 0;
pub const NORTH: u16 = 1;
pub const EAST: u16 = 2;
pub const SOUTH: u16 = 3;
pub const WEST: u16 = 4;
pub const NORTH_EAST: u16 = 5;
pub const SOUTH_EAST: u16 = 6;
pub const SOUTH_WEST: u16 = 7;
pub const NORTH_WEST: u16 = 8;
}
/// The movement delta of an action, or `None` for `NOOP` and unknown values.
pub fn action_delta(action: u16) -> Option<(i32, i32)> {
match action {
player_action::NORTH => Some((0, -1)),
player_action::EAST => Some((1, 0)),
player_action::SOUTH => Some((0, 1)),
player_action::WEST => Some((-1, 0)),
player_action::NORTH_EAST => Some((1, -1)),
player_action::SOUTH_EAST => Some((1, 1)),
player_action::SOUTH_WEST => Some((-1, 1)),
player_action::NORTH_WEST => Some((-1, -1)),
_ => None,
}
}
/// The action for a single-step delta. Inverse of [`action_delta`]; panics on anything
/// that is not a king move.
pub fn delta_action(dx: i32, dy: i32) -> u16 {
match (dx, dy) {
(0, -1) => player_action::NORTH,
(1, 0) => player_action::EAST,
(0, 1) => player_action::SOUTH,
(-1, 0) => player_action::WEST,
(1, -1) => player_action::NORTH_EAST,
(1, 1) => player_action::SOUTH_EAST,
(-1, 1) => player_action::SOUTH_WEST,
(-1, -1) => player_action::NORTH_WEST,
d => panic!("non-step delta {d:?}"),
}
}
/// The single-step movement rule, shared by the server sim, client prediction and the
/// client pathfinder so all three agree on the same physics.
///
/// World geometry is chessboard (Chebyshev): diagonal and cardinal steps are the same
/// distance, so a step is any king move onto a free tile. A diagonal step additionally
/// requires *both* orthogonal neighbor tiles to be free — no squeezing between two
/// diagonally touching blockers (corner cutting).
pub fn step_allowed(
from: (i32, i32),
to: (i32, i32),
blocked: impl Fn(i32, i32) -> bool,
) -> bool {
let (dx, dy) = (to.0 - from.0, to.1 - from.1);
if dx.abs() > 1 || dy.abs() > 1 || (dx == 0 && dy == 0) {
return false;
}
if blocked(to.0, to.1) {
return false;
}
dx == 0 || dy == 0 || (!blocked(from.0 + dx, from.1) && !blocked(from.0, from.1 + dy))
}
/// Server base tick rate. The shared timeline all action scheduling is expressed in.
pub const TICK_HZ: u32 = 24;
/// Movement resolves on every `TICKS_PER_MOVE`-th tick — one "movement window" per
/// `TICKS_PER_MOVE` ticks (6 Hz). Window `w` executes at tick `w * TICKS_PER_MOVE`.
pub const TICKS_PER_MOVE: u32 = 4;
/// How many *future* movement windows a client may address. Part of the protocol
/// contract: actions targeted beyond this horizon are dropped, and the client's
/// unconfirmed in-flight steps must stay within it.
pub const ACTION_WINDOW_HORIZON: usize = 3;
pub const MAGIC: u16 = 0x524C;
pub const VERSION: u8 = 1;
pub mod packet_type {
pub const STATE: u8 = 0;
pub const ACTION: u8 = 1;
pub const CHUNK: u8 = 2;
pub const ENTITY: u8 = 3;
pub const ENTITY_QUERY: u8 = 4;
pub const ENTITY_DETAIL: u8 = 5;
pub const PING: u8 = 6;
pub const PONG: u8 = 7;
}
/// Encodes signed chunk grid coordinates into the wire chunk_id format.
pub fn chunk_id(cx: i16, cy: i16) -> u32 {
(cx as u16 as u32) | ((cy as u16 as u32) << 16)
}
/// Decodes a wire chunk_id back to signed chunk grid coordinates.
pub fn chunk_coords(id: u32) -> (i16, i16) {
(id as u16 as i16, (id >> 16) as u16 as i16)
}
/// Shared movement vocabulary: whether a tile id blocks movement.
///
/// Single source of truth for both the server (collision in the sim) and the client
/// (movement prediction). Keep this in lockstep with the tileset in `overworld.tga`.
///
/// Test values for now — a proper tile-data file format replaces this table later.
pub fn tile_collidable(tile_id: u16) -> bool {
match tile_id {
0 => true, // empty / world border (the server pads chunks past the map rim with id 0)
146 => true, // trees / rocks
_ => false,
}
}
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C, packed)]
pub struct Header {
pub magic: u16,
pub version: u8,
pub packet_type: u8,
pub client_type: u16,
}
impl Header {
pub fn new(packet_type: u8) -> Self {
Self { magic: MAGIC, version: VERSION, packet_type, client_type: 0 }
}
}
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C, packed)]
pub struct ChunkEntry {
pub chunk_id: u32,
pub version: u16,
}
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C, packed)]
pub struct ActionPacket {
pub header: Header,
pub auth_token: u64,
/// The server tick this action is scheduled for: it executes in the movement window
/// covering that tick, or — if it arrives late — in the next window *if that slot is
/// still empty* (late actions fill gaps, they never override newer intent). A second
/// action addressed to the same window replaces the first, so a scheduled step can be
/// retracted (NOOP) or changed until its window executes. `0` means "no scheduling
/// intent": pure keep-alive / cache-ack packets that must never touch the queue.
pub target_tick: u32,
/// Slot index = (dy+1)*3 + (dx+1), dx/dy ∈ {-1, 0, 1}.
/// Slot 4 is always the player's current chunk.
pub cache: [ChunkEntry; 9],
pub player_action: u16,
}
const _: () = assert!(std::mem::size_of::<ChunkEntry>() == 6);
const _: () = assert!(std::mem::size_of::<ActionPacket>() == 74);
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C, packed)]
pub struct StatePacket {
pub header: Header, // 6 (offset 0)
pub tick: u32, // 4 (offset 6)
/// Slot index = (dy+1)*3 + (dx+1), dx/dy ∈ {-1, 0, 1}.
/// Slot 4 is always the player's current chunk.
pub chunks: [ChunkEntry; 9], // 54 (offset 10)
pub player_entity_id: u32, // 4 (offset 64)
pub entity_checksum: u32, // 4 (offset 68)
} // total: 72 bytes
const _: () = assert!(std::mem::size_of::<StatePacket>() == 72);
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C, packed)]
pub struct PingPacket {
pub header: Header,
pub timestamp_ms: u64,
}
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C, packed)]
pub struct PongPacket {
pub header: Header,
pub timestamp_ms: u64,
}
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C, packed)]
pub struct ChunkPacket {
pub header: Header, // 6 bytes
pub chunk: ChunkEntry, // 6 bytes
pub pal_count: u8, // 1 byte
pub palette: [u16; 64], // 128 bytes
pub tiles: [u8; 768], // 768 bytes — 1024 tiles, 6-bit packed
} // total: 909 bytes
const _: () = assert!(std::mem::size_of::<ChunkPacket>() == 909);
#[repr(C, packed)]
#[derive(Clone, Copy, Pod, Zeroable)]
pub struct EntityEntry {
pub id: u32,
pub type_id: u16,
pub pos_x: i16,
pub pos_y: i16,
pub hp: u16,
pub hp_max: u16,
pub elo: u16,
pub entity_flags: u8,
pub meta_len: u8,
}
const _: () = assert!(std::mem::size_of::<EntityEntry>() == 18);
#[repr(C, packed)]
#[derive(Clone, Copy, Pod, Zeroable)]
pub struct EntityPacket {
pub header: Header,
pub tick: u32,
pub entity_count: u8,
pub packet_flags: u8,
pub entities: [EntityEntry; 66],
}
const _: () = assert!(std::mem::size_of::<EntityPacket>() == 1200);
+1 -1
View File
@@ -1,5 +1,5 @@
[package] [package]
name = "netsim" name = "sim"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
+53
View File
@@ -0,0 +1,53 @@
/// Gameplay properties of a tile, derived from its id (see `map::tile_flags`).
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
pub struct TileFlags(pub u8);
impl TileFlags {
pub const COLLIDABLE: u8 = 1 << 0;
pub const OPAQUE: u8 = 1 << 1;
pub const LIQUID: u8 = 1 << 2;
pub fn collidable(self) -> bool { self.0 & Self::COLLIDABLE != 0 }
pub fn opaque(self) -> bool { self.0 & Self::OPAQUE != 0 }
pub fn liquid(self) -> bool { self.0 & Self::LIQUID != 0 }
}
pub const CHUNK_SIZE: usize = 32;
pub const CHUNK_TILES: usize = CHUNK_SIZE * CHUNK_SIZE;
/// A 32×32 block of tile ids, row-major. This is the whole chunk: flags are derived from
/// the id on lookup, so the in-memory layout is already the plain `u16` array a binary
/// on-disk format would store.
pub struct Chunk {
pub tiles: Box<[u16; CHUNK_TILES]>,
}
impl Chunk {
/// All tiles id 0 (empty / solid border).
pub fn new() -> Self {
Self { tiles: Box::new([0; CHUNK_TILES]) }
}
/// World gen path. Calls the closure for every local tile coordinate.
pub fn generate(f: impl Fn(u8, u8) -> u16) -> Self {
let mut chunk = Self::new();
for ly in 0..CHUNK_SIZE {
for lx in 0..CHUNK_SIZE {
chunk.tiles[ly * CHUNK_SIZE + lx] = f(lx as u8, ly as u8);
}
}
chunk
}
pub fn set_tile(&mut self, lx: u8, ly: u8, id: u16) {
self.tiles[ly as usize * CHUNK_SIZE + lx as usize] = id;
}
pub fn get_tile(&self, lx: u8, ly: u8) -> u16 {
self.tiles[ly as usize * CHUNK_SIZE + lx as usize]
}
pub fn tile_flags(&self, lx: u8, ly: u8) -> TileFlags {
crate::map::tile_flags(self.get_tile(lx, ly))
}
}
+118
View File
@@ -0,0 +1,118 @@
//! Headless world simulation: tiles, entities, movement rules and the tick loop.
//! No platform or rendering dependencies — the game binary drives `Sim::tick`.
pub mod chunk;
pub mod entity;
pub mod map;
pub mod world;
mod sim;
pub use sim::Sim;
use chunk::Chunk;
use map::TileMap;
use world::World;
pub mod player_action {
pub const NOOP: u16 = 0;
pub const NORTH: u16 = 1;
pub const EAST: u16 = 2;
pub const SOUTH: u16 = 3;
pub const WEST: u16 = 4;
pub const NORTH_EAST: u16 = 5;
pub const SOUTH_EAST: u16 = 6;
pub const SOUTH_WEST: u16 = 7;
pub const NORTH_WEST: u16 = 8;
}
/// The movement delta of an action, or `None` for `NOOP` and unknown values.
pub fn action_delta(action: u16) -> Option<(i32, i32)> {
match action {
player_action::NORTH => Some((0, -1)),
player_action::EAST => Some((1, 0)),
player_action::SOUTH => Some((0, 1)),
player_action::WEST => Some((-1, 0)),
player_action::NORTH_EAST => Some((1, -1)),
player_action::SOUTH_EAST => Some((1, 1)),
player_action::SOUTH_WEST => Some((-1, 1)),
player_action::NORTH_WEST => Some((-1, -1)),
_ => None,
}
}
/// The action for a single-step delta. Inverse of [`action_delta`]; panics on anything
/// that is not a king move.
pub fn delta_action(dx: i32, dy: i32) -> u16 {
match (dx, dy) {
(0, -1) => player_action::NORTH,
(1, 0) => player_action::EAST,
(0, 1) => player_action::SOUTH,
(-1, 0) => player_action::WEST,
(1, -1) => player_action::NORTH_EAST,
(1, 1) => player_action::SOUTH_EAST,
(-1, 1) => player_action::SOUTH_WEST,
(-1, -1) => player_action::NORTH_WEST,
d => panic!("non-step delta {d:?}"),
}
}
/// The single-step movement rule, shared by the sim and the pathfinder so both agree
/// on the same physics.
///
/// World geometry is chessboard (Chebyshev): diagonal and cardinal steps are the same
/// distance, so a step is any king move onto a free tile. A diagonal step additionally
/// requires *both* orthogonal neighbor tiles to be free — no squeezing between two
/// diagonally touching blockers (corner cutting).
pub fn step_allowed(
from: (i32, i32),
to: (i32, i32),
blocked: impl Fn(i32, i32) -> bool,
) -> bool {
let (dx, dy) = (to.0 - from.0, to.1 - from.1);
if dx.abs() > 1 || dy.abs() > 1 || (dx == 0 && dy == 0) {
return false;
}
if blocked(to.0, to.1) {
return false;
}
dx == 0 || dy == 0 || (!blocked(from.0 + dx, from.1) && !blocked(from.0, from.1 + dy))
}
/// Base tick rate. The timeline all action scheduling is expressed in.
pub const TICK_HZ: u32 = 24;
/// Movement resolves on every `TICKS_PER_MOVE`-th tick — one "movement window" per
/// `TICKS_PER_MOVE` ticks (6 Hz). Window `w` executes at tick `w * TICKS_PER_MOVE`.
pub const TICKS_PER_MOVE: u32 = 4;
/// Movement vocabulary: whether a tile id blocks movement. Keep this in lockstep with
/// the tileset in `overworld.tga`.
///
/// Test values for now — a proper tile-data file format replaces this table later.
pub fn tile_collidable(tile_id: u16) -> bool {
match tile_id {
0 => true, // empty / world border (chunks past the map rim are padded with id 0)
146 => true, // trees / rocks
_ => false,
}
}
/// Build a world from a loaded tile map. The map's top-left tile sits at world (0, 0); the
/// map is sliced into 32×32 chunks. Tiles inside a loaded chunk but outside the authored map
/// get id 0 (solid, see `tile_collidable`), so the walkable world edge sits flush with the
/// visible map rim instead of with the larger chunk boundary.
pub fn load_world(map: &TileMap) -> World {
let mut w = World::new();
let max_cx = (map.width.saturating_sub(1) >> 5) as i16;
let max_cy = (map.height.saturating_sub(1) >> 5) as i16;
for cy in 0..=max_cy {
for cx in 0..=max_cx {
let chunk = Chunk::generate(|lx, ly| {
let (wx, wy) = world::local_to_tile(cx, cy, lx, ly);
map.tile(wx, wy) // 0 outside the map
});
w.set_chunk(cx, cy, chunk);
}
}
w
}
+4 -4
View File
@@ -64,12 +64,12 @@ impl TileMap {
} }
} }
/// Gameplay flags for a global tile id. Collision comes from the shared movement /// Gameplay flags for a global tile id. Collision comes from the movement vocabulary
/// vocabulary (`shared::tile_collidable`) so the client can predict it identically; /// (`crate::tile_collidable`) so the pathfinder predicts it identically; other flags
/// other flags (opaque, …) stay server-side. Tile ids index into `overworld.tga`. /// (opaque, …) will join here. Tile ids index into `overworld.png`.
pub fn tile_flags(tile_id: u16) -> TileFlags { pub fn tile_flags(tile_id: u16) -> TileFlags {
let mut bits = 0u8; let mut bits = 0u8;
if shared::tile_collidable(tile_id) { if crate::tile_collidable(tile_id) {
bits |= TileFlags::COLLIDABLE; bits |= TileFlags::COLLIDABLE;
} }
TileFlags(bits) TileFlags(bits)
Executable
+126
View File
@@ -0,0 +1,126 @@
use std::collections::HashMap;
use crate::TICKS_PER_MOVE;
use crate::world::World;
pub struct Sim {
pub world: World,
/// Base tick counter (24 Hz). Movement resolves every `TICKS_PER_MOVE`-th tick.
pub tick: u32,
/// Each entity's intent for the next movement window. Setting it again replaces
/// it, executing the window consumes it — so an actor moves at most one tile per
/// window no matter how often it changes its mind in between.
intents: HashMap<u32, u16>,
}
impl Sim {
pub fn new(world: World) -> Self {
Self { world, tick: 0, intents: HashMap::new() }
}
/// Schedule `action` for `entity_id`'s next movement window. NOOP clears the intent.
pub fn set_action(&mut self, entity_id: u32, action: u16) {
if crate::action_delta(action).is_some() {
self.intents.insert(entity_id, action);
} else {
self.intents.remove(&entity_id);
}
}
pub fn clear_action(&mut self, entity_id: u32) {
self.intents.remove(&entity_id);
}
/// Advance the world by one base tick. Returns `true` when this tick executed a
/// movement window, i.e. entity positions may have changed.
pub fn step(&mut self) -> bool {
self.tick = self.tick.wrapping_add(1);
if self.tick.is_multiple_of(TICKS_PER_MOVE) {
self.entity_tick();
true
} else {
false
}
}
fn entity_tick(&mut self) {
for (entity_id, action) in self.intents.drain() {
let delta = match crate::action_delta(action) {
Some(d) => d,
None => continue,
};
let pos = match self.world.entities.get(&entity_id) {
Some(e) => e.pos,
None => continue,
};
let from = (pos.0 as i32, pos.1 as i32);
let to = (from.0 + delta.0, from.1 + delta.1);
// `step_allowed` is the shared movement rule (chessboard geometry, no corner
// cutting) — the pathfinder plans with the exact same function.
let allowed = crate::step_allowed(from, to, |x, y| {
self.world.tile_flags(x as i16, y as i16).map_or(true, |f| f.collidable())
});
if allowed {
self.world.move_entity(entity_id, (to.0 as i16, to.1 as i16));
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::chunk::Chunk;
use crate::player_action;
/// Open floor (id 1) with a single wall tile at (5, 4) — id 146 is collidable in
/// the tile vocabulary.
fn open_world() -> World {
let mut w = World::new();
w.set_chunk(0, 0, Chunk::generate(|lx, ly| if (lx, ly) == (5, 4) { 146 } else { 1 }));
w
}
fn step_window(sim: &mut Sim) {
for _ in 0..TICKS_PER_MOVE {
sim.step();
}
}
#[test]
fn one_step_per_window_and_replacement() {
let mut sim = Sim::new(open_world());
let id = sim.world.spawn_entity(0, (4, 4), 1);
sim.set_action(id, player_action::SOUTH);
sim.set_action(id, player_action::NORTH); // replaces
sim.step(); // no window yet
assert_eq!(sim.world.entities[&id].pos, (4, 4));
step_window(&mut sim);
assert_eq!(sim.world.entities[&id].pos, (4, 3));
step_window(&mut sim); // intent consumed — no second step
assert_eq!(sim.world.entities[&id].pos, (4, 3));
}
#[test]
fn blocked_and_out_of_world_steps_are_rejected() {
let mut sim = Sim::new(open_world());
let id = sim.world.spawn_entity(0, (4, 4), 1);
sim.set_action(id, player_action::EAST); // into the wall at (5, 4)
step_window(&mut sim);
assert_eq!(sim.world.entities[&id].pos, (4, 4));
let edge = sim.world.spawn_entity(0, (0, 0), 1);
sim.set_action(edge, player_action::WEST); // no chunk there
step_window(&mut sim);
assert_eq!(sim.world.entities[&edge].pos, (0, 0));
}
#[test]
fn noop_clears_intent() {
let mut sim = Sim::new(open_world());
let id = sim.world.spawn_entity(0, (4, 4), 1);
sim.set_action(id, player_action::SOUTH);
sim.set_action(id, player_action::NOOP);
step_window(&mut sim);
assert_eq!(sim.world.entities[&id].pos, (4, 4));
}
}
+3 -3
View File
@@ -1,5 +1,5 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::chunk::{Chunk, TileDef, TileFlags, PaletteFullError}; use crate::chunk::{Chunk, TileFlags};
use crate::entity::Entity; use crate::entity::Entity;
pub fn tile_to_chunk(tx: i16, ty: i16) -> (i16, i16) { pub fn tile_to_chunk(tx: i16, ty: i16) -> (i16, i16) {
@@ -45,11 +45,11 @@ impl World {
self.chunks.get(&(cx, cy)).map(|c| c.tile_flags(lx, ly)) self.chunks.get(&(cx, cy)).map(|c| c.tile_flags(lx, ly))
} }
pub fn set_tile(&mut self, wx: i16, wy: i16, def: TileDef) -> Result<(), PaletteFullError> { pub fn set_tile(&mut self, wx: i16, wy: i16, id: u16) {
let (cx, cy) = tile_to_chunk(wx, wy); let (cx, cy) = tile_to_chunk(wx, wy);
let (lx, ly) = tile_to_local(wx, wy); let (lx, ly) = tile_to_local(wx, wy);
let chunk = self.chunks.entry((cx, cy)).or_insert_with(Chunk::new); let chunk = self.chunks.entry((cx, cy)).or_insert_with(Chunk::new);
chunk.set_tile(lx, ly, def) chunk.set_tile(lx, ly, id);
} }
pub fn spawn_entity(&mut self, type_id: u16, pos: (i16, i16), hp: u16) -> u32 { pub fn spawn_entity(&mut self, type_id: u16, pos: (i16, i16), hp: u16) -> u32 {