diff --git a/.github/workflows/auto.yaml b/.github/workflows/auto.yaml index e156e95..8ec323c 100644 --- a/.github/workflows/auto.yaml +++ b/.github/workflows/auto.yaml @@ -85,7 +85,7 @@ jobs: touch .env && rm .env echo " - MAIN_MODULE="code-challenges" + MAIN_MODULE="CodeChallenges" PYTHON_PATH="python${{ env.PYTHON_VERSION }}" ARCHITECTURE="${{ env.ARCHITECTURE }}" RUST_BACKTRACE=full diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 8e2b75a..6980fce 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -61,4 +61,4 @@ jobs: - name: Message shell: bash run: |- - echo "Not yet implemented" + echo "::warning::Not yet implemented" diff --git a/.github/workflows/manual.yaml b/.github/workflows/manual.yaml index b5ba444..c3f7675 100644 --- a/.github/workflows/manual.yaml +++ b/.github/workflows/manual.yaml @@ -83,7 +83,7 @@ jobs: touch .env && rm .env echo " - MAIN_MODULE="code-challenges" + MAIN_MODULE="CodeChallenges" PYTHON_PATH="python${{ env.PYTHON_VERSION }}" ARCHITECTURE="${{ env.ARCHITECTURE }}" RUST_BACKTRACE=full @@ -106,14 +106,15 @@ jobs: # only performed if qa passes and option set deploy: name: DEPLOY + needs: 'qa' + # job only runs if deploy-checkbox is selected + if: ${{ github.event.inputs.deploy == 'true' }} runs-on: ${{ github.event.inputs.docker-image }} environment: "${{ github.event.inputs.environment }}" env: {} steps: - - uses: actions/checkout@v4.2.2 - - name: Message shell: bash run: |- - echo "Not yet implemented" + echo "::warning::Not yet implemented" diff --git a/.gitignore b/.gitignore index 3706613..81d8062 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ /** !/.gitignore !/.gitattributes -!/.funcignore # ---------------------------------------------------------------- # ROOT @@ -11,6 +10,7 @@ !/LICENCE !/justfile !/Cargo.toml +!/rustfmt.toml !/pyproject.toml !/*.lock !/.github diff --git a/Cargo.lock b/Cargo.lock index 3c3e9f6..92af14d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aho-corasick" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5" +dependencies = [ + "memchr", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -67,18 +76,45 @@ version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +[[package]] +name = "autocfg" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" +dependencies = [ + "autocfg 1.4.0", +] + [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "bitflags" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + [[package]] name = "camino" version = "1.1.9" @@ -121,9 +157,9 @@ dependencies = [ [[package]] name = "cargo-zigbuild" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6e9e856390d5b0a859acaeda16528f8a61c964bdb894c3216c254908f1c2ea" +checksum = "a9584d77470f7ffea2fb67fbcc9e8dbe9fa79a80dafd579a83507c0a08d1f658" dependencies = [ "anyhow", "cargo-config2", @@ -135,9 +171,9 @@ dependencies = [ "fat-macho", "fs-err", "path-slash", - "rustc_version", + "rustc_version 0.4.1", "rustflags", - "semver", + "semver 1.0.26", "serde", "serde_json", "shlex", @@ -153,7 +189,7 @@ checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" dependencies = [ "camino", "cargo-platform", - "semver", + "semver 1.0.26", "serde", "serde_json", "thiserror 2.0.12", @@ -167,9 +203,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" +checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" dependencies = [ "clap_builder", "clap_derive", @@ -177,9 +213,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" +checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" dependencies = [ "anstream", "anstyle", @@ -207,15 +243,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] -name = "code_challenges" -version = "0.1.0" +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" dependencies = [ - "cargo-zigbuild", - "dedent", - "dict_derive", - "rstest", - "serde", - "serde_json", + "bitflags 1.3.2", ] [[package]] @@ -224,11 +257,24 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "console" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e09ced7ebbccb63b4c65413d821f2e00ce54c5ca4514ddc6b3c892fdbcbc69d" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "unicode-width 0.2.1", + "windows-sys 0.60.2", +] + [[package]] name = "crc" -version = "3.2.1" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" dependencies = [ "crc-catalog", ] @@ -261,6 +307,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "dirs" version = "5.0.1" @@ -288,12 +340,28 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "env_home" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" +[[package]] +name = "env_logger" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b" +dependencies = [ + "log 0.3.9", + "regex 0.2.11", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -310,6 +378,19 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "extprim" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b1a357c911c352439b460d7b375b5c85977b9db395b703dfee5a94dfb4d66a2" +dependencies = [ + "num-traits", + "rand 0.6.5", + "rustc_version 0.2.3", + "semver 0.9.0", + "serde", +] + [[package]] name = "fat-macho" version = "0.4.9" @@ -325,9 +406,15 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f89bda4c2a21204059a977ed3bfe746677dfd137b83c339e702b0ac91d482aa" dependencies = [ - "autocfg", + "autocfg 1.4.0", ] +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + [[package]] name = "futures-core" version = "0.3.31" @@ -371,6 +458,35 @@ dependencies = [ "slab", ] +[[package]] +name = "general" +version = "0.2.0" +dependencies = [ + "cargo-zigbuild", + "dedent", + "dict_derive", + "indicatif", + "indoc", + "itertools", + "ndarray", + "rand 0.9.2", + "rand_chacha 0.9.0", + "rstest", + "rustfmt", + "serde", + "serde_json", + "strip-ansi-escapes", +] + +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width 0.1.14", +] + [[package]] name = "getrandom" version = "0.2.16" @@ -379,7 +495,19 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.3+wasi-0.2.4", ] [[package]] @@ -394,7 +522,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daa0a64d21a7eb230583b4c5f4e23b7e4e57974f96620f42a7e75e08ae66d745" dependencies = [ - "log", + "log 0.4.27", "plain", "scroll", ] @@ -421,18 +549,72 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "indicatif" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a646d946d06bedbbc4cac4c218acf4bbf2d87757a784857025f4d447e4e1cd" +dependencies = [ + "console", + "portable-atomic", + "unicode-width 0.2.1", + "unit-prefix", + "web-time", +] + +[[package]] +name = "indoc" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" + [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "js-sys" +version = "0.3.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0b063578492ceec17683ef2f8c5e89121fbd0b172cbc280635ab7567db2738" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.172" @@ -445,7 +627,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags", + "bitflags 2.9.0", "libc", ] @@ -455,18 +637,79 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +[[package]] +name = "log" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" +dependencies = [ + "log 0.4.27", +] + [[package]] name = "log" version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "matrixmultiply" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" +dependencies = [ + "autocfg 1.4.0", + "rawpointer", +] + [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "ndarray" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "882ed72dce9365842bf196bdeedf5055305f11fc8c03dee7bb0194a6cad34841" +dependencies = [ + "matrixmultiply", + "num-complex", + "num-integer", + "num-traits", + "portable-atomic", + "portable-atomic-util", + "rawpointer", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg 1.4.0", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -503,6 +746,30 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "proc-macro-crate" version = "3.3.0" @@ -530,27 +797,196 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +dependencies = [ + "autocfg 0.1.8", + "libc", + "rand_chacha 0.1.1", + "rand_core 0.4.2", + "rand_hc", + "rand_isaac", + "rand_jitter", + "rand_os", + "rand_pcg", + "rand_xorshift", + "winapi 0.3.9", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +dependencies = [ + "autocfg 0.1.8", + "rand_core 0.3.1", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_jitter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" +dependencies = [ + "libc", + "rand_core 0.4.2", + "winapi 0.3.9", +] + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +dependencies = [ + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.4.2", + "rdrand", + "winapi 0.3.9", +] + +[[package]] +name = "rand_pcg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +dependencies = [ + "autocfg 0.1.8", + "rand_core 0.4.2", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "redox_users" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom", + "getrandom 0.2.16", "libredox", "thiserror 1.0.69", ] +[[package]] +name = "regex" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384" +dependencies = [ + "aho-corasick 0.6.10", + "memchr", + "regex-syntax 0.5.6", + "thread_local", + "utf8-ranges", +] + [[package]] name = "regex" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ - "aho-corasick", + "aho-corasick 1.1.3", "memchr", "regex-automata", - "regex-syntax", + "regex-syntax 0.8.5", ] [[package]] @@ -559,9 +995,18 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ - "aho-corasick", + "aho-corasick 1.1.3", "memchr", - "regex-syntax", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-syntax" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7" +dependencies = [ + "ucd-util", ] [[package]] @@ -578,41 +1023,49 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "rstest" -version = "0.25.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fc39292f8613e913f7df8fa892b8944ceb47c247b78e1b1ae2f09e019be789d" +checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49" dependencies = [ "futures-timer", "futures-util", "rstest_macros", - "rustc_version", ] [[package]] name = "rstest_macros" -version = "0.25.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f168d99749d307be9de54d23fd226628d99768225ef08f6ffb52e0182a27746" +checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0" dependencies = [ "cfg-if", "glob", "proc-macro-crate", "proc-macro2", "quote", - "regex", + "regex 1.11.1", "relative-path", - "rustc_version", + "rustc_version 0.4.1", "syn 2.0.101", "unicode-ident", ] +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + [[package]] name = "rustc_version" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver", + "semver 1.0.26", ] [[package]] @@ -621,13 +1074,38 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a39e0e9135d7a7208ee80aa4e3e4b88f0f5ad7be92153ed70686c38a03db2e63" +[[package]] +name = "rustfmt" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec940eed814db0fb7ab928c5f5025f97dc55d1c0e345e39dda2ce9f945557500" +dependencies = [ + "diff", + "env_logger", + "getopts", + "kernel32-sys", + "libc", + "log 0.3.9", + "regex 0.2.11", + "serde", + "serde_derive", + "serde_json", + "strings", + "syntex_errors", + "syntex_syntax", + "term", + "toml", + "unicode-segmentation", + "winapi 0.2.8", +] + [[package]] name = "rustix" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ - "bitflags", + "bitflags 2.9.0", "errno", "libc", "linux-raw-sys", @@ -660,6 +1138,15 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + [[package]] name = "semver" version = "1.0.26" @@ -669,6 +1156,12 @@ dependencies = [ "serde", ] +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "serde" version = "1.0.219" @@ -691,9 +1184,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" dependencies = [ "itoa", "memchr", @@ -722,7 +1215,25 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ - "autocfg", + "autocfg 1.4.0", +] + +[[package]] +name = "strings" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa481ee1bc42fc3df8195f91f7cb43cf8f2b71b48bac40bf5381cfaf7e481f3c" +dependencies = [ + "log 0.3.9", +] + +[[package]] +name = "strip-ansi-escapes" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a8f8038e7e7969abb3f1b7c2a811225e9296da208539e0f79c5251d6cac0025" +dependencies = [ + "vte", ] [[package]] @@ -753,12 +1264,63 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syntex_errors" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3133289179676c9f5c5b2845bf5a2e127769f4889fcbada43035ef6bd662605e" +dependencies = [ + "libc", + "serde", + "serde_derive", + "syntex_pos", + "term", + "unicode-xid", +] + +[[package]] +name = "syntex_pos" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ab669fa003d208c681f874bbc76d91cc3d32550d16b5d9d2087cf477316470" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "syntex_syntax" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03815b9f04d95828770d9c974aa39c6e1f6ef3114eb77a3ce09008a0d15dd142" +dependencies = [ + "bitflags 0.9.1", + "extprim", + "log 0.3.9", + "serde", + "serde_derive", + "serde_json", + "syntex_errors", + "syntex_pos", + "unicode-xid", +] + [[package]] name = "target-lexicon" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" +[[package]] +name = "term" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1" +dependencies = [ + "kernel32-sys", + "winapi 0.2.8", +] + [[package]] name = "terminal_size" version = "0.4.2" @@ -809,6 +1371,24 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "thread_local" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "toml" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" +dependencies = [ + "serde", +] + [[package]] name = "toml_datetime" version = "0.6.9" @@ -831,24 +1411,152 @@ dependencies = [ "winnow", ] +[[package]] +name = "ucd-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abd2fc5d32b590614af8b0a20d837f32eca055edd0bbead59a9cfe80858be003" + [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + +[[package]] +name = "unit-prefix" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "323402cff2dd658f39ca17c789b502021b3f18707c91cdf22e3838e1b4023817" + +[[package]] +name = "utf8-ranges" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcfc827f90e53a02eaef5e535ee14266c1d569214c6aa70133a624d8a3164ba" + [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "vte" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "231fdcd7ef3037e8330d8e17e61011a2c244126acc0a982f4040ac3f9f0bc077" +dependencies = [ + "memchr", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.14.3+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e14915cadd45b529bb8d1f343c4ed0ac1de926144b746e2710f9cd05df6603b" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28d1ba982ca7923fd01448d5c30c6864d0a14109560296a162f80f305fb93bb" +dependencies = [ + "bumpalo", + "log 0.4.27", + "proc-macro2", + "quote", + "syn 2.0.101", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c3d463ae3eff775b0c45df9da45d68837702ac35af998361e2c84e7c5ec1b0d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb4ce89b08211f923caf51d527662b75bdc9c9c7aab40f86dcb9fb85ac552aa" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f143854a3b13752c6950862c906306adb27c7e839f7414cec8fea35beab624c1" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "which" version = "7.0.3" @@ -861,6 +1569,46 @@ dependencies = [ "winsafe", ] +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + [[package]] name = "windows-sys" version = "0.48.0" @@ -879,6 +1627,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -903,13 +1660,30 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -922,6 +1696,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -934,6 +1714,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -946,12 +1732,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -964,6 +1762,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -976,6 +1780,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -988,6 +1798,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -1000,11 +1816,17 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] @@ -1014,3 +1836,29 @@ name = "winsafe" version = "0.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + +[[package]] +name = "wit-bindgen" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] diff --git a/Cargo.toml b/Cargo.toml index d042c43..85a5fcc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,11 @@ # cargo-features = ["profile-rustflags"] [package] -name = "code_challenges" -version = "0.1.0" +name = "general" +version = "0.2.0" edition = "2024" description = "Code for challenges from various platforms" +homepage = "github.com/raj-open/code-challenges" rust-version = "1.86" authors = [ "raj-open ", @@ -12,7 +13,7 @@ authors = [ # cf. https://doc.rust-lang.org/cargo/reference/manifest.html # [lib] -# name = "code_challenges" +# name = "general" # crate-type = ["lib"] [workspace] @@ -35,12 +36,17 @@ members = [ # ] debug = true strip = false +opt-level = 3 +lto = true # link-time optimisation +codegen-units = 1 [profile.dev] # rustflags = [ # "-A", "unused_imports", # "-A", "dead_code", # ] +opt-level = 0 +lto = false # link-time optimisation [profile.test] # rustflags = [ @@ -50,21 +56,32 @@ strip = false [dependencies] serde = {version = "^1.0.219", features = ["derive"]} -serde_json = {version = "^1.0.140"} +serde_json = {version = "^1.0.143"} dict_derive = {version = "^0.6.0" } dedent = {version="^0.1.1"} +itertools = {version = "^0.14.0"} +indoc = {version="^2.0.6"} +indicatif = {version = "^0.18.0"} +strip-ansi-escapes = {version="^0.2.1"} +rand = {version="^0.9.2"} +rand_chacha = {version = "^0.9.0"} +# numpy = {version = "^0.26.0"} +ndarray = {version = "^0.16.1"} [dev-dependencies] -cargo-zigbuild = {version = "^0.20.0"} -rstest = {version = "^0.25.0"} -# FIXME: currently fail to build on unix -# rustfmt = {version = "^0.10.0", features = []} +cargo-zigbuild = {version = "^0.20.1"} +rstest = {version = "^0.26.1"} +rustfmt = {version = "^0.10.0", features = []} # just = {version = "^1.40.0"} [[bin]] -name = "code-challenges" +name = "CodeChallenges" path = "src/main.rs" [[bin]] -name = "hackerrank_mathematics" +name = "HackerRankMathematics" path = "src/problems/hackerrank/mathematics/main.rs" + +[[bin]] +name = "GeniusSquare" +path = "src/bin/games/genius_square/main.rs" diff --git a/README.md b/README.md index 82cd8e1..ca3ea8a 100644 --- a/README.md +++ b/README.md @@ -83,3 +83,128 @@ just tests-unit The `tests` folder contains integration tests for rust code, and unit tests for python code. + +## Execution of Binaries ## + +This repository contains some binaries (see [Cargo.toml](./Cargo.toml)): + +- `CodeChallenges` +- `HackerRankMathematics` +- `GeniusSquare` + +To run a binary call + +```bash +# runs with optimisation (slower compile time) +just run-rust {NAME_OF_BINARY} [flags] +# runs without optimisation (faster compile time, slower run time) +just dev-rust {NAME_OF_BINARY} [flags] +``` + +### Genius Squares ### + +The binary `GeniusSquare` solves instances of +the _Smart Games_ puzzle [Genius Square](https://smarttoysandgames.co.uk/uk/genius-square). +Usage is as follows: + +```bash +# provides random instance of the puzzle and solves it: +just run-rust GeniusSquare +# ... with random seeding for repeatability: +just run-rust GeniusSquare {Seed} +# solves an instance of the game for a particular initialisation (roll of the dice): +just run-rust GeniusSquare {Dice1} {Dice2} ... {Dice7} +``` + +e.g. + +```bash +just run-rust GeniusSquare 1234 # with random seed +just run-rust GeniusSquare B1 C4 D6 F1 F2 F3 F5 # with given initialisation +``` + +The `run` command builds and runs the binary. +To perform this separately, use + +```bash +just build-compile GeniusSquare +``` + +which produces the binary in [target/release/GeniusSquare](target/release/GeniusSquare). +The standalone binary can be called as above: + +```bash +./target/release/GeniusSquare +# with random seed +./target/release/GeniusSquare {Seed} +./target/release/GeniusSquare 1234 +# with given initialisation +./target/release/GeniusSquare {Dice1} {Dice2} ... {Dice7} +./target/release/GeniusSquare B1 C4 D6 F1 F2 F3 F5 +``` + +#### Example #### + +Calling + +```bash +just run-rust GeniusSquare B1 C4 D6 F1 F2 F3 F5 +``` + +results in + +```bash +Roll: B1, C4, D6, F1, F2, F3, F5. + + +Problem: +╔═══╦═══╤═══╤═══╤═══╤═══╤═══╕ +║ ║ A │ B │ C │ D │ E │ F │ +╠═══╬═══╪═══╪═══╪═══╪═══╪═══╡ +║ 1 ║ │ ■ │ │ │ │ ■ │ +╠───╬───┼───┼───┼───┼───┼───┤ +║ 2 ║ │ │ │ │ │ ■ │ +╠───╬───┼───┼───┼───┼───┼───┤ +║ 3 ║ │ │ │ │ │ ■ │ +╠───╬───┼───┼───┼───┼───┼───┤ +║ 4 ║ │ │ ■ │ │ │ │ +╠───╬───┼───┼───┼───┼───┼───┤ +║ 5 ║ │ │ │ │ │ ■ │ +╠───╬───┼───┼───┼───┼───┼───┤ +║ 6 ║ │ │ │ ■ │ │ │ +╙───╨───┴───┴───┴───┴───┴───┘ + +Compute solution... + +...completed in 725.18ms + +Solution: +╔═══╦═══╤═══╤═══╤═══╤═══╤═══╕ +║ ║ A │ B │ C │ D │ E │ F │ +╠═══╬═══╪═══╪═══╪═══╪═══╪═══╡ +║ 1 ║ 1 │ ■ │ 2 │ 2 │ Z │ ■ │ +╠───╬───┼───┼───┼───┼───┼───┤ +║ 2 ║ L │ X │ X │ Z │ Z │ ■ │ +╠───╬───┼───┼───┼───┼───┼───┤ +║ 3 ║ L │ X │ X │ Z │ T │ ■ │ +╠───╬───┼───┼───┼───┼───┼───┤ +║ 4 ║ L │ L │ ■ │ T │ T │ T │ +╠───╬───┼───┼───┼───┼───┼───┤ +║ 5 ║ 4 │ 4 │ 4 │ 4 │ C │ ■ │ +╠───╬───┼───┼───┼───┼───┼───┤ +║ 6 ║ 3 │ 3 │ 3 │ ■ │ C │ C │ +╙───╨───┴───┴───┴───┴───┴───┘ +``` + +in the console. +The solver currently relies on a brute force tree-search algorithm, +and provides solutions at the `Wizard` level, +viz. no collisions occur and none of the pieces + +```text +1 2 3 CC + 2 3 C + 3 +``` + +are adjacent (in the sense of touching edges). diff --git a/dist/VERSION b/dist/VERSION index 77d6f4c..0ea3a94 100644 --- a/dist/VERSION +++ b/dist/VERSION @@ -1 +1 @@ -0.0.0 +0.2.0 diff --git a/justfile b/justfile index 777a925..3b6a9a7 100644 --- a/justfile +++ b/justfile @@ -117,7 +117,6 @@ build-requirements: @just build-requirements-dependencies build-requirements-basic: - @rustup default stable @cargo update --verbose @cargo install --locked --force cargo-zigbuild @# cargo install --locked --force rustfmt @@ -147,7 +146,7 @@ run-py module="main" *args="": run-rust module="${MAIN_MODULE}" *args="": @just build-compile "{{module}}" @# "./target/release/{{module}}" {{args}} - @cargo run --bin "{{module}}" + @cargo run --release --bin "{{module}}" {{args}} # -------------------------------- # TARGETS: development @@ -156,6 +155,11 @@ run-rust module="${MAIN_MODULE}" *args="": dev *args: @echo "Not yet implemented" +dev-rust module="${MAIN_MODULE}" *args="": + @just build-compile "{{module}}" + @# "./target/release/{{module}}" {{args}} + @cargo run --bin "{{module}}" {{args}} + # -------------------------------- # TARGETS: tests # -------------------------------- @@ -171,26 +175,26 @@ tests-logs log_path="logs": test-unit path *args: @cargo zigbuild --tests @echo "run unit tests in $( just _rust_path_to_test_module "{{path}}")" - @cargo test --lib "$( just _rust_path_to_test_module "{{path}}")" {{args}} + @cargo test --lib "$( just _rust_path_to_test_module "{{path}}")" {{args}} -- --nocapture @# echo "run unit tests in $( just _rust_path_to_module "{{path}}")" - @# cargo test --lib "$( just _rust_path_to_module "{{path}}")" {{args}} + @# cargo test --lib "$( just _rust_path_to_module "{{path}}")" {{args}} -- --nocapture test-unit-optimised path *args: @cargo zigbuild --tests --release @echo "run unit tests in $( just _rust_path_to_test_module "{{path}}")" - @cargo test --lib "$( just _rust_path_to_test_module "{{path}}")" {{args}} + @cargo test --lib "$( just _rust_path_to_test_module "{{path}}")" {{args}} -- --nocapture @# echo "run unit tests in $( just _rust_path_to_module "{{path}}")" - @# cargo test --lib "$( just _rust_path_to_module "{{path}}")" {{args}} + @# cargo test --lib "$( just _rust_path_to_module "{{path}}")" {{args}} -- --nocapture tests-unit *args: @just _reset-logs @cargo zigbuild --tests - @cargo test --lib {{args}} + @cargo test --lib {{args}} -- --nocapture tests-unit-optimised *args: @just _reset-logs @cargo zigbuild --tests --release - @cargo test --lib {{args}} + @cargo test --lib {{args}} -- --nocapture # -------------------------------- # TARGETS: prettify @@ -200,7 +204,8 @@ prettify: @cargo fmt --verbose prettify-dry: - @cargo fmt --verbose --check + @echo "Not yet implemented" + @# cargo fmt --verbose --check # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # TARGETS: clean @@ -215,6 +220,7 @@ clean-basic log_path="logs": @- just _clean-all-files "." ".DS_Store" 2> /dev/null @echo "All build artefacts will be force removed." @cargo clean + @just _clean-all-files "." "*.rs.bk" @- rm -rf ".venv" 2> /dev/null @- rm -rf "target" 2> /dev/null @@ -280,5 +286,5 @@ check-system: check-system-requirements: @just _check-tool "cargo" "cargo" - @# just _check-tool "cargo fmt" "cargo fmt" + @# just _check-tool "cargo fmt -- --force" "cargo fmt" @just _check-tool "cargo-zigbuild" "cargo-zigbuild" diff --git a/pyproject.toml b/pyproject.toml index 9a5fb39..184aee3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = 'code_challenges' -version = "0.1.0" +version = "0.2.0" description = 'Code for challenges from various platforms' authors = [ {name="raj-open", email="raj-open@users.noreply.github.com"}, diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..19061f4 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,25 @@ +reorder_imports = false +combine_control_expr = false +condense_wildcard_suffixes = false +disable_all_formatting = false +# use_small_heuristics = "Off" + +fn_args_layout = "Tall" +fn_single_line = false + +hard_tabs = false +newline_style = "Unix" +trailing_comma = "Vertical" + +format_strings = false +force_explicit_abi = true + +struct_lit_width = 40 +fn_call_width = 60 +max_width = 100 +comment_width = 200 +normalize_comments = false +doc_comment_width = 200 +normalize_doc_attributes = false +wrap_comments = false +wrap_doc_comments = false diff --git a/src/_core/errors.rs b/src/_core/errors.rs index 8449f35..d991c12 100644 --- a/src/_core/errors.rs +++ b/src/_core/errors.rs @@ -1,12 +1,14 @@ /// ---------------------------------------------------------------- /// IMPORTS /// ---------------------------------------------------------------- + use std::fmt::Debug; /// ---------------------------------------------------------------- /// METHODS /// ---------------------------------------------------------------- +/// Converts error-like entities to strings #[allow(unused)] pub fn err_to_string(err: E) -> String where diff --git a/src/_core/mod.rs b/src/_core/mod.rs index a385ff7..a715ece 100644 --- a/src/_core/mod.rs +++ b/src/_core/mod.rs @@ -1,2 +1,3 @@ pub mod errors; +pub mod rand; pub mod strings; diff --git a/src/_core/rand.rs b/src/_core/rand.rs new file mode 100644 index 0000000..5191f06 --- /dev/null +++ b/src/_core/rand.rs @@ -0,0 +1,31 @@ +/// ---------------------------------------------------------------- +/// IMPORTS +/// ---------------------------------------------------------------- + +use rand; +use rand::SeedableRng; +use rand_chacha::ChaCha8Rng; + +/// ---------------------------------------------------------------- +/// METHODS +/// ---------------------------------------------------------------- + +#[allow(unused)] +pub fn seed_rng(x: Option) -> ChaCha8Rng { + match x { + Some(seed_str) => { + // string -> bytes + let mut seed = [0u8; 32]; + let seed_bytes = seed_str.as_bytes(); + let len = seed_bytes.len().min(32); + seed[..len].copy_from_slice(&seed_bytes[..len]); + + // create RNG + let rng = ChaCha8Rng::from_seed(seed); + return rng; + }, + None => { + return ChaCha8Rng::from_os_rng(); + }, + } +} diff --git a/src/_core/strings.rs b/src/_core/strings.rs index afd776c..9673e89 100644 --- a/src/_core/strings.rs +++ b/src/_core/strings.rs @@ -2,7 +2,7 @@ /// IMPORTS /// ---------------------------------------------------------------- -// +use strip_ansi_escapes::strip; /// ---------------------------------------------------------------- /// METHODS @@ -11,3 +11,15 @@ pub fn greet(name: &str) { println!("Hello, {}!", name); } + +#[allow(unused)] +/// Strips potential ANSII characters +pub fn purify_string(text: &String) -> String { + String::from_utf8(strip(text)).unwrap_or(text.clone()) +} + +#[allow(unused)] +/// Computes length of purified string +pub fn purify_string_length(text: &String) -> usize { + purify_string(text).len() +} diff --git a/src/app/messages.rs b/src/app/messages.rs new file mode 100644 index 0000000..b6ea18c --- /dev/null +++ b/src/app/messages.rs @@ -0,0 +1,45 @@ +/// ---------------------------------------------------------------- +/// IMPORTS +/// ---------------------------------------------------------------- + +use std::env; + +use super::super::_core::strings::purify_string_length; + +/// ---------------------------------------------------------------- +/// METHODS +/// ---------------------------------------------------------------- + +#[allow(unused)] +pub fn welcome_screen() { + let exe = env::current_exe().unwrap(); + let app_name: &str = exe.file_name().unwrap().to_str().unwrap(); + const VERSION: &str = env!("CARGO_PKG_VERSION"); + const URL: &str = env!("CARGO_PKG_HOMEPAGE"); + let lines: Vec = vec![ + format!("{app_name} \x1b[92;1mv{VERSION}\x1b[0m"), + format!("{URL}"), + ]; + display_bordered_message(lines); +} + +/// ---------------------------------------------------------------- +/// AUXILIARY METHODS +/// ---------------------------------------------------------------- + +fn display_bordered_message(lines: Vec) { + // determine padding + let n = lines.iter().map(purify_string_length).max().unwrap_or(0); + let hspace = " ".repeat(n + 2); + let hbar = "\u{2500}".repeat(n + 2); + + println!("\u{250C}{hbar}\u{2510}"); + println!("\u{2502}{hspace}\u{2502}"); + let _: Vec<_> = lines.iter().map(|line| { + let k = purify_string_length(line); + let pad = " ".repeat(n - k); + println!("\u{2502} {line}{pad} \u{2502}"); + }).collect(); + println!("\u{2502}{hspace}\u{2502}"); + println!("\u{2514}{hbar}\u{2518}"); +} diff --git a/src/app/mod.rs b/src/app/mod.rs new file mode 100644 index 0000000..2de8453 --- /dev/null +++ b/src/app/mod.rs @@ -0,0 +1,3 @@ +/// Submodules for app-level methods + +pub mod messages; diff --git a/src/bin/games/genius_square/algorithms/mod.rs b/src/bin/games/genius_square/algorithms/mod.rs new file mode 100644 index 0000000..06fb1e9 --- /dev/null +++ b/src/bin/games/genius_square/algorithms/mod.rs @@ -0,0 +1,3 @@ +/// Algorithms used to solve a game state. + +pub mod solve; diff --git a/src/bin/games/genius_square/algorithms/solve.rs b/src/bin/games/genius_square/algorithms/solve.rs new file mode 100644 index 0000000..89e6eae --- /dev/null +++ b/src/bin/games/genius_square/algorithms/solve.rs @@ -0,0 +1,93 @@ +/// ---------------------------------------------------------------- +/// IMPORTS +/// ---------------------------------------------------------------- + +use indicatif::ProgressBar; +use indicatif::ProgressStyle; + +use crate::models::constants::enums::ENUM_PIECES; +use crate::models::constants::enums::EnumPiece; +use crate::models::pieces::models::Piece; +use crate::models::board::models::GameBoard; + +/// ---------------------------------------------------------------- +/// METHODS +/// ---------------------------------------------------------------- + +/// Recursively solves by check all possibilities +pub fn solve_brute_force( + board: &GameBoard, +) -> GameBoard { + let obst = board.get_block().to_owned(); + match recursion(board, &obst, None, None) { + Some(board_) => { + return board_; + }, + None => { + return board.to_owned(); + } + } +} + +/// ---------------------------------------------------------------- +/// AUXILIARY METHODS +/// ---------------------------------------------------------------- + +fn recursion( + board: &GameBoard, + obst: &Piece, + option_kinds: Option<&[EnumPiece]>, + option_pbar: Option<&ProgressBar>, +) -> Option { + let kinds = option_kinds.unwrap_or(ENUM_PIECES); + let n = kinds.len() as u64; + + let pbar0 = ProgressBar::new(n); + let pbar: &ProgressBar; + match option_pbar { + Some(pbar_) => { + pbar = &pbar_; + }, + None => { + pbar = &pbar0; + let style = ProgressStyle::with_template("{spinner:.white} [{elapsed_precise}] [{wide_bar:.white}] {pos}/{len} ({eta_precise})"); + pbar.set_style(style.unwrap()) + } + } + + if n == 0 { + // if nothing left to solve, then return pieces, provide everything is filled + if obst.get_coweight() == 0 { + pbar.finish_and_clear(); + println!("...completed in {:.2?}", pbar.elapsed()); + return Some(board.to_owned()); + } + } else { + // otherwise go through all permissible moves for next piece and then proceed recursively + let kind = &kinds[0].clone(); + let kinds = &kinds[1..]; + let piece0 = Piece::from_kind(kind, None); // initialised piece + for piece in board.get_configurations(&piece0, &obst) { + pbar.inc(1); + // update the obstacle + let obst_ = obst.clone() + piece.clone(); + + // update the solution + let mut board_ = board.clone(); + board_.add_piece(&kind.clone(), &piece); + + // compute remainder of solution recursively + match recursion(&mut board_, &obst_, Some(kinds), Some(&pbar)) { + Some(board_) => { + return Some(board_); + }, + None => { + let k = pbar.position(); + pbar.set_position((k - 1).max(0)); + }, + } + } + } + + return None; +} diff --git a/src/bin/games/genius_square/features/mod.rs b/src/bin/games/genius_square/features/mod.rs new file mode 100644 index 0000000..aee96f5 --- /dev/null +++ b/src/bin/games/genius_square/features/mod.rs @@ -0,0 +1,3 @@ +/// Highest logic of application. + +pub mod setup_game; diff --git a/src/bin/games/genius_square/features/setup_game.rs b/src/bin/games/genius_square/features/setup_game.rs new file mode 100644 index 0000000..d3d85f3 --- /dev/null +++ b/src/bin/games/genius_square/features/setup_game.rs @@ -0,0 +1,39 @@ +/// ---------------------------------------------------------------- +/// IMPORTS +/// ---------------------------------------------------------------- + +use rand_chacha::ChaCha8Rng; + +use crate::models::dice::methods::roll_dice; +use crate::models::dice::models::Die; +use crate::models::constants::enums::EnumPiece; +use crate::models::pieces::models::Piece; +use crate::models::board::models::GameBoard; +use crate::algorithms::solve::solve_brute_force; + +/// ---------------------------------------------------------------- +/// METHODS +/// ---------------------------------------------------------------- + +pub fn feature_setup_game( + rng: &mut ChaCha8Rng, + option_roll: Option>, +) { + // Roll the dice + let faces = option_roll.unwrap_or_else(|| roll_dice(rng)); + let dice: Vec = faces.iter() + .map(|face| Die::from_string(face)) + .collect(); + println!("\nRoll: {}.\n", faces.join(", ")); + + // Establish the problem + let coords = dice.iter().map(|die| die.to_coords()).collect(); + let block = Piece::from_coords(coords, Some(EnumPiece::Block)); + let mut board = GameBoard::new(&block); + println!("\nProblem:\n{}", board.pretty()); + + // Solve the problem + println!("\nCompute solution...\n"); + board = solve_brute_force(&board); + println!("\nSolution:\n{}\n", board.pretty()); +} diff --git a/src/bin/games/genius_square/lib.rs b/src/bin/games/genius_square/lib.rs new file mode 100644 index 0000000..8c3bf74 --- /dev/null +++ b/src/bin/games/genius_square/lib.rs @@ -0,0 +1,5 @@ +/// Modules available for Genius Square binary + +pub mod algorithms; +pub mod features; +pub mod models; diff --git a/src/bin/games/genius_square/main.rs b/src/bin/games/genius_square/main.rs new file mode 100644 index 0000000..760e790 --- /dev/null +++ b/src/bin/games/genius_square/main.rs @@ -0,0 +1,28 @@ +/// ---------------------------------------------------------------- +/// IMPORTS +/// ---------------------------------------------------------------- + +use std::env; + +use general::app::messages::welcome_screen; +use general::_core; + +mod algorithms; +mod models; +mod features; + +use models::constants::dice::NUM_DICE; +use features::setup_game::feature_setup_game; + +/// ---------------------------------------------------------------- +/// MAIN +/// ---------------------------------------------------------------- + +fn main() { + let args: Vec = env::args().skip(1).collect(); + let option_roll = if args.len() >= NUM_DICE { Some(args[0..NUM_DICE].to_vec()) } else { None }; + let option_seed = if args.len() >= 1 { Some(args[args.len() - 1].clone()) } else { None }; + let mut rng = _core::rand::seed_rng(option_seed); + welcome_screen(); + feature_setup_game(&mut rng, option_roll); +} diff --git a/src/bin/games/genius_square/mod.rs b/src/bin/games/genius_square/mod.rs new file mode 100644 index 0000000..d0c2985 --- /dev/null +++ b/src/bin/games/genius_square/mod.rs @@ -0,0 +1,5 @@ +/// Modules for code base Genius Square + +pub mod algorithms; +pub mod features; +pub mod models; diff --git a/src/bin/games/genius_square/models/arrays/mod.rs b/src/bin/games/genius_square/models/arrays/mod.rs new file mode 100644 index 0000000..93e2fc4 --- /dev/null +++ b/src/bin/games/genius_square/models/arrays/mod.rs @@ -0,0 +1,3 @@ +/// Models for handling arrays + +pub mod models; diff --git a/src/bin/games/genius_square/models/arrays/models.rs b/src/bin/games/genius_square/models/arrays/models.rs new file mode 100644 index 0000000..1208bec --- /dev/null +++ b/src/bin/games/genius_square/models/arrays/models.rs @@ -0,0 +1,298 @@ +/// ---------------------------------------------------------------- +/// IMPORTS +/// ---------------------------------------------------------------- + +use ndarray::Array2; +use ndarray::s as slice; +use std::fmt::Debug; +use std::fmt::Display; +use std::fmt::Formatter; +use std::fmt::Result; +use std::ops::Add; +use std::ops::Mul; +use itertools::iproduct; +use itertools::Itertools; + +/// ---------------------------------------------------------------- +/// STRUCTS +/// ---------------------------------------------------------------- + +#[derive(Clone, Debug)] +pub struct BinArray { + m: usize, + n: usize, + values: Array2, +} + +/// ---------------------------------------------------------------- +/// IMPLEMENTATIONS +/// ---------------------------------------------------------------- + +impl BinArray { + pub fn from_coords( + coords: Vec<(usize, usize)>, + m: usize, + n: usize, + ) -> Self { + let mut values = Array2::::zeros((m, n)); + for coord in coords { + values[[coord.0, coord.1]] = 1; + } + Self {m, n, values} + } + + /// Gets the list of co-ordinates of the entries which are non-zero + pub fn to_coords(&self) -> Vec<(usize, usize)> { + self.values + .indexed_iter() + .filter_map(|((i, j), &v)| if v == 0 { None } else { Some((i, j)) }) + .collect() + } + + /// Determines a single co-ordinate to be used as an anchor point. + /// If none exists, defaults to (0, 0). + pub fn get_anchor(&self) -> (usize, usize) { + self.values + .indexed_iter() + .find_or_first(|&(_, &v)| v != 0) + .map_or((0, 0), |((i, j), _)| (i, j)) + } + + pub fn get_weight(&self) -> isize { + self.values + .mapv(|x| if x == 0 {0} else {1}) + .sum() + } + + pub fn get_coweight(&self) -> isize { + self.transform_invert().get_weight() + } + + pub fn get_shape(&self) -> (usize, usize) { + (self.m, self.n) + } + + pub fn get_values(&self) -> Array2 { + self.values.clone() + } + + /// Shifts array as far as possible to top left + pub fn recentre(&self) -> Self { + // determine maximal h+v-shifts + let coords = self.to_coords(); + let i_min: usize = *coords.iter().map(|(i, _)| i).min().unwrap_or(&0); + let j_min: usize = *coords.iter().map(|(_, j)| j).min().unwrap_or(&0); + // shift coords + let result = self.transform_shift(-(i_min as isize), -(j_min as isize)); + return result; + } + + /// Flips 0s and 1s + pub fn transform_invert(&self) -> Self { + let m = self.m; + let n = self.n; + let values = self.values.mapv(|x| if x == 0 {1} else {0}); + return Self {m, n, values}; + } + + pub fn transform_shift( + &self, + di: isize, + dj: isize, + ) -> Self { + // create blank 3 x 3 meta block + let m = self.m; + let n = self.n; + let mut slate = Array2::::zeros((3*m, 3*n)); + + // slot in values in location shifted from the middle + let i0 = self.m as isize + di; + let i1 = (self.m as isize) + i0; + let j0 = self.n as isize + dj; + let j1 = (self.n as isize) + j0; + let mut view = slate.slice_mut(slice![i0..i1, j0..j1]); + view.assign(&self.values); + + // restrict to "middle" part + let i0 = self.m; + let i1 = self.m + i0; + let j0 = self.n; + let j1 = self.n + j0; + let values = slate.slice_mut(slice![i0..i1, j0..j1]).to_owned(); + let result = Self {m, n, values}; + return result; + } + + pub fn transform_hflip(&self, recentre: bool) -> Self{ + let m = self.m; + let n = self.n; + let values = self.values.slice(slice![.., ..;-1]).to_owned(); + let mut result = Self {m, n, values}; + if recentre { + result = result.recentre(); + } + return result; + } + + pub fn transform_vflip(&self, recentre: bool) -> Self { + let m = self.m; + let n = self.n; + let values = self.values.slice(slice![..;-1, ..]).to_owned(); + let mut result = Self {m, n, values}; + if recentre { + result = result.recentre(); + } + return result; + + } + + pub fn transform_transpose(&self, recentre: bool) -> Self { + let m = self.m; + let n = self.n; + let values = self.values.t().to_owned(); + let mut result = Self {m, n, values}; + if recentre { + result = result.recentre(); + } + return result; + } + + pub fn transform_rotate(&self, k: i8, recentre: bool) -> Self { + match k { + 1 => { + return self.transform_transpose(false).transform_vflip(recentre); + }, + -1 => { + return self.transform_vflip(false).transform_transpose(recentre); + }, + _ => { + return self.clone(); + } + } + } + + /// For collision comparison + pub fn transform_dither(&self) -> Self { + let (m, n) = self.get_shape(); + let coords = self.to_coords(); + let mut arr = BinArray::from_coords(coords, m + 2, n + 2); + arr = arr.transform_shift(1, 1); + let arr1 = arr.transform_shift(-1, 0); + let arr2 = arr.transform_shift(1, 0); + let arr3 = arr.transform_shift(0, -1); + let arr4 = arr.transform_shift(0, 1); + arr = arr + arr1 + arr2 + arr3 + arr4; + let values = arr.values + .slice(slice![1..-1, 1..-1]) + .to_owned(); + let result = Self {m, n, values}; + return result; + } + + /// Determines all possible configurations + /// of the same array subject to + /// + /// - rotations, + /// - v- and h-flips, + /// - v- and h-shifts + /// + /// provided the moves preserve the "weight" of the shadow in the array + /// and provided + /// + /// - no collisions occur with an optional obstacle. + pub fn get_configurations( + &self, + option_obst: Option<&BinArray>, + ) -> impl Iterator { + let (m, n) = self.get_shape(); + let obst = option_obst.map_or_else(|| BinArray::from_coords(vec![], m, n), |x| x.clone()); + let free = obst.transform_invert(); + let iterator = iproduct!( + [0, 1, -1], + [false, true], + [false, true], + ) + // iterate through all orientations + .map(|(rot, vflip, hflip)| { + // recover original + let mut arr = self.clone(); + if rot != 0 { + arr = arr.transform_rotate(rot, false); + } + if vflip { + arr = arr.transform_vflip(false); + } + if hflip { + arr = arr.transform_hflip(false); + } + // NOTE: No longer need this as anchor point will be shifted + // if hflip | vflip | (rot != 0) { + // arr = arr.recentre(); + // } + return arr; + }) + // by fixing an anchor point and viewing the non-occupied positions + // get all possible shifts of the array + .map(move |arr| { + // an anchor point of the piece + let (i0, j0) = arr.get_anchor(); + let i0 = i0 as isize; + let j0 = j0 as isize; + // all non-occupied points on gameboard + let shifts = free + .to_coords() + .iter() + .map(|&(i, j)| { + let di = (i as isize) - i0; + let dj = (j as isize) - j0; + let arr_ = arr.transform_shift(di, dj); + return arr_; + }) + .collect::>(); + return shifts; + }) + // since returned a vector of possibilities, need to flatten + .flatten() + // if geometric operations shift shape off the grid, skip + .filter(|arr| { + let wt = self.get_weight(); + return arr.get_weight() >= wt; + }) + // if geometric operations collide with obstacle, skip + .filter(move |arr| { + let collision = arr.to_owned() * obst.to_owned(); + let penalty = -collision.get_weight(); + return penalty >= 0; + }); + return iterator; + } +} + +impl Display for BinArray { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.values) + } +} + +impl Add for BinArray { + type Output = Self; + + fn add(self, other: Self) -> Self::Output { + let m = self.m; + let n = self.n; + let mut values = self.values.to_owned() + other.values.to_owned(); + values = values.mapv(|x| x.min(1)); + return Self {m, n, values}; + } +} + +impl Mul for BinArray { + type Output = Self; + + fn mul(self, other: Self) -> Self::Output { + let m = self.m; + let n = self.n; + let values = self.values.to_owned() * other.values.to_owned(); + return Self {m, n, values}; + } +} diff --git a/src/bin/games/genius_square/models/board/mod.rs b/src/bin/games/genius_square/models/board/mod.rs new file mode 100644 index 0000000..a5034f1 --- /dev/null +++ b/src/bin/games/genius_square/models/board/mod.rs @@ -0,0 +1,3 @@ +/// Models for handling game board + +pub mod models; diff --git a/src/bin/games/genius_square/models/board/models.rs b/src/bin/games/genius_square/models/board/models.rs new file mode 100644 index 0000000..c0cc9e1 --- /dev/null +++ b/src/bin/games/genius_square/models/board/models.rs @@ -0,0 +1,209 @@ +/// ---------------------------------------------------------------- +/// IMPORTS +/// ---------------------------------------------------------------- + +use ndarray::Array2; +use std::fmt::Debug; +use std::fmt::Display; +use std::fmt::Formatter; +use std::fmt::Result; +use std::collections::HashMap; + +use crate::models::constants::board::*; +use crate::models::constants::dice::*; +use crate::models::constants::enums::*; +use crate::models::pieces::models::*; + +/// ---------------------------------------------------------------- +/// STRUCTS +/// ---------------------------------------------------------------- + +#[derive(Clone, Debug)] +pub struct GameBoard { + block: Piece, + pieces: HashMap, +} + +/// ---------------------------------------------------------------- +/// IMPLEMENTATIONS +/// ---------------------------------------------------------------- + +impl GameBoard { + pub fn new(block: &Piece) -> Self { + let pieces: HashMap = HashMap::new(); + return Self {block: block.clone(), pieces} + } + + #[allow(unused)] + pub fn add_piece(&mut self, symb: &EnumPiece, piece: &Piece) { + self.pieces.insert(symb.clone(), piece.clone()); + } + + #[allow(unused)] + pub fn set_pieces(&mut self, pieces: &HashMap) { + self.pieces = pieces.clone(); + } + + pub fn get_block(&self) -> &Piece { + &self.block + } + + pub fn to_string(&self) -> String { + let field = self.to_array_of_strings(false); + let text = Self::array_to_string(&field); + return text; + } + + #[allow(unused)] + pub fn to_formatted(&self) -> String { + let field = self.to_array_of_strings(true); + let text = Self::array_to_string(&field); + return text; + } + + pub fn pretty(&self) -> String { + let _m = GRID_HEIGHT; + let n = GRID_WIDTH; + let field = self.to_array_of_strings(true); + + fn create_border( + lcorner1: &str, + fill1: &str, + lcorner2: &str, + fill2: &str, + mid2: &str, + rcorner: &str, + n: usize, + ) -> String { + let middle = format!("{fill2}{mid2}{fill2}{fill2}").repeat(n-1); + format!("{lcorner1}{fill1}{fill1}{fill1}{lcorner2}{fill2}{fill2}{middle}{fill2}{rcorner}") + } + + let top1 = create_border("\u{02554}", "\u{2550}", "\u{02566}", "\u{2550}", "\u{2564}", "\u{2555}", n); + let top2 = create_border("\u{02560}", "\u{2550}", "\u{0256C}", "\u{2550}", "\u{256A}", "\u{2561}", n); + let mid = create_border("\u{02560}", "\u{2500}", "\u{0256C}", "\u{2500}", "\u{253C}", "\u{2524}", n); + let bot = create_border("\u{02559}", "\u{2500}", "\u{02568}", "\u{2500}", "\u{02534}", "\u{02518}", n); + + let head = FACE1_FMT.join(" \u{2502} ").to_string(); + let head = format!("{top1}\n\u{02551} \u{02551} {head} \u{2502}\n{top2}"); + + let middle = field.rows() + .into_iter() + .enumerate() + .map(|(i, row)| { + let index = FACE2_FMT[i]; + let line = row.iter().map(|s| s.to_string()).collect::>().join(" \u{2502} "); + return format!("\u{02551} {index} \u{02551} {line} \u{2502}"); + }) + .collect::>() + .join(format!("\n{mid}\n").as_str()); + + let text = format!("{head}\n{middle}\n{bot}"); + + return text; + } + + fn to_array_of_strings(&self, formatted: bool) -> Array2 { + let m = GRID_HEIGHT; + let n = GRID_WIDTH; + let mut trace: Array2 = Array2::from_elem((m, n), " ".to_string()); + let piece = self.get_block(); + for (i, j) in piece.to_coords() { + let alpha = if formatted { piece.get_symb_fmt() } else { piece.get_symb() }; + trace[[i, j]] = alpha; + } + for (_, piece) in self.pieces.iter() { + for (i, j) in piece.to_coords() { + let alpha = if formatted { piece.get_symb_fmt() } else { piece.get_symb() }; + trace[[i, j]] = alpha; + } + } + return trace; + } + + fn array_to_string(field: &Array2) -> String { + let n = GRID_WIDTH; + let hbar = "\u{2500}".repeat(n + 2); + let top = format!("\u{250C}{hbar}\u{2510}"); + let bot = format!("\u{2514}{hbar}\u{2518}"); + let middle = field.rows() + .into_iter() + .map(|row| { + let line = row.iter().map(|s| s.as_str()).collect::(); + return format!("\u{2502} {line} \u{2502}"); + }) + .collect::>() + .join("\n"); + let text = format!("{top}\n{middle}\n{bot}"); + return text; + } + + /// Determines all possible configurations + /// of the same piece subject to + /// + /// - rotations, + /// - v- and h-flips, + /// - v- and h-shifts + /// + /// provided the moves preserve the "weight" of the shadow in the array + /// and provided + /// + /// - no collisions occur with already placed pieces (marked by `obst`) + /// - the piece is not adjacent to certain other pieces. + pub fn get_configurations( + &self, + piece: &Piece, + obst: &Piece, + ) -> impl Iterator { + let mut used: Vec = vec![]; + let it = piece + // convert to positions + .get_positions() + // get all possible orientations + shifts which do not collide with obstacle + .get_configurations(Some(obst.get_positions())) + // skip all moves which lead to forbidden adjacent pieces + .filter(|pos| { + // only need to check for collisions of pieces of a paritcular kind + let kind = piece.get_kind(); + if !(NON_ADJACENT.contains(&kind)) { + return true; + } + let pos_dither = pos.transform_dither(); + for (s, q) in self.pieces.iter() { + // only need to check for collisions of pieces of a paritcular kind + if !(NON_ADJACENT.contains(s)) { + continue; + } + if *s == kind { + continue; + } + + let collision = pos_dither.to_owned() * q.get_positions().to_owned(); + let penalty = -collision.get_weight(); + if penalty < 0 { + return false; + } + } + return true; + }) + // convert to piece + .map(|pos| { + let kind = piece.get_kind(); + Piece::from_kind(&kind, Some(pos)) + }) + // skip duplicates + .filter(move |p| { + let value = p.to_string(); + let dupl = used.contains(&value); + used.push(value); + return !dupl; + }); + return it; + } +} + +impl Display for GameBoard { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.to_string()) + } +} diff --git a/src/bin/games/genius_square/models/constants/board.rs b/src/bin/games/genius_square/models/constants/board.rs new file mode 100644 index 0000000..9fb056d --- /dev/null +++ b/src/bin/games/genius_square/models/constants/board.rs @@ -0,0 +1,12 @@ +/// ---------------------------------------------------------------- +/// IMPORTS +/// ---------------------------------------------------------------- + +// + +/// ---------------------------------------------------------------- +/// CONSTANTS +/// ---------------------------------------------------------------- + +pub const GRID_HEIGHT: usize = 6; +pub const GRID_WIDTH: usize = 6; diff --git a/src/bin/games/genius_square/models/constants/dice.rs b/src/bin/games/genius_square/models/constants/dice.rs new file mode 100644 index 0000000..e2c50a9 --- /dev/null +++ b/src/bin/games/genius_square/models/constants/dice.rs @@ -0,0 +1,57 @@ +/// ---------------------------------------------------------------- +/// IMPORTS +/// ---------------------------------------------------------------- + +// + +/// ---------------------------------------------------------------- +/// CONSTANTS +/// ---------------------------------------------------------------- + +pub const FACE1: &[&str] = &[ + "A", + "B", + "C", + "D", + "E", + "F", +]; + +pub const FACE2: &[&str] = &[ + "1", + "2", + "3", + "4", + "5", + "6", +]; + +pub const FACE1_FMT: &[&str] = &[ + "\x1b[91,1mA\x1b[0m", + "\x1b[91,1mB\x1b[0m", + "\x1b[91,1mC\x1b[0m", + "\x1b[91,1mD\x1b[0m", + "\x1b[91,1mE\x1b[0m", + "\x1b[91,1mF\x1b[0m", +]; + +pub const FACE2_FMT: &[&str] = &[ + "\x1b[91,1m1\x1b[0m", + "\x1b[91,1m2\x1b[0m", + "\x1b[91,1m3\x1b[0m", + "\x1b[91,1m4\x1b[0m", + "\x1b[91,1m5\x1b[0m", + "\x1b[91,1m6\x1b[0m", +]; + +pub const DICE: &[&[&str]] = &[ + &["A5","A5","F2","F2","E1","B6"], + &["A6","A6","A6","F1","F1","F1"], + &["D5","E4","E5","E6","F4","F5"], + &["A2","A3","B1","B2","B3","C2"], + &["A1","C1","D1","D2","E2","F3"], + &["B4","C3","C4","D3","D4","E3"], + &["A4","B5","C5","C6","D6","F6"], +]; + +pub const NUM_DICE: usize = DICE.len(); diff --git a/src/bin/games/genius_square/models/constants/enums.rs b/src/bin/games/genius_square/models/constants/enums.rs new file mode 100644 index 0000000..2f27ee6 --- /dev/null +++ b/src/bin/games/genius_square/models/constants/enums.rs @@ -0,0 +1,124 @@ +/// ---------------------------------------------------------------- +/// IMPORTS +/// ---------------------------------------------------------------- + +use std::fmt::Debug; +use std::fmt::Display; +use std::fmt::Formatter; +use std::fmt::Result; + +use crate::models::arrays::models::BinArray; +use super::board::*; +use super::pieces::*; + +/// ---------------------------------------------------------------- +/// STRUCTS AND CONSTANTS +/// ---------------------------------------------------------------- + +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub enum EnumPiece { + Blank, + Block, + Symb1, + Symb2, + Symb3, + Symb4, + C, + L, + T, + X, + Z, +} + +pub const ENUM_PIECES: &[EnumPiece] = &[ + EnumPiece::Symb1, + EnumPiece::Symb2, + EnumPiece::Symb3, + EnumPiece::Symb4, + EnumPiece::C, + EnumPiece::L, + EnumPiece::T, + EnumPiece::X, + EnumPiece::Z, +]; + +pub const NON_ADJACENT: &[EnumPiece] = &[ + EnumPiece::Symb1, + EnumPiece::Symb2, + EnumPiece::Symb3, + EnumPiece::C, +]; + +/// ---------------------------------------------------------------- +/// IMPLEMENTATIONS +/// ---------------------------------------------------------------- + +impl EnumPiece { + #[allow(unused)] + pub const fn as_str(&self) -> &'static str { + match self { + EnumPiece::Blank => " ", + EnumPiece::Block => SYMB_BLOCK, + EnumPiece::Symb1 => SYMB_PIECE_1, + EnumPiece::Symb2 => SYMB_PIECE_2, + EnumPiece::Symb3 => SYMB_PIECE_3, + EnumPiece::Symb4 => SYMB_PIECE_4, + EnumPiece::C => SYMB_PIECE_C, + EnumPiece::L => SYMB_PIECE_L, + EnumPiece::T => SYMB_PIECE_T, + EnumPiece::X => SYMB_PIECE_X, + EnumPiece::Z => SYMB_PIECE_Z, + } + } + + #[allow(unused)] + pub const fn to_formatted(&self) -> &'static str { + match self { + EnumPiece::Blank => " ", + EnumPiece::Block => SYMB_FMT_BLOCK, + EnumPiece::Symb1 => SYMB_FMT_PIECE_1, + EnumPiece::Symb2 => SYMB_FMT_PIECE_2, + EnumPiece::Symb3 => SYMB_FMT_PIECE_3, + EnumPiece::Symb4 => SYMB_FMT_PIECE_4, + EnumPiece::C => SYMB_FMT_PIECE_C, + EnumPiece::L => SYMB_FMT_PIECE_L, + EnumPiece::T => SYMB_FMT_PIECE_T, + EnumPiece::X => SYMB_FMT_PIECE_X, + EnumPiece::Z => SYMB_FMT_PIECE_Z, + } + } + + pub fn get_positions(&self) -> BinArray { + let raw = match self { + EnumPiece::Blank => " ", + EnumPiece::Block => BLOCK, + EnumPiece::Symb1 => PIECE_1, + EnumPiece::Symb2 => PIECE_2, + EnumPiece::Symb3 => PIECE_3, + EnumPiece::Symb4 => PIECE_4, + EnumPiece::C => PIECE_C, + EnumPiece::L => PIECE_L, + EnumPiece::T => PIECE_T, + EnumPiece::X => PIECE_X, + EnumPiece::Z => PIECE_Z, + }; + let mut coords: Vec<(usize, usize)> = vec![]; + for (i, line) in raw.lines().enumerate() { + for (j, a) in line.chars().enumerate() { + if a.to_string() == "+" { + coords.push((i, j)); + } + } + } + let m = GRID_HEIGHT; + let n = GRID_WIDTH; + let positions = BinArray::from_coords(coords, m, n); + return positions; + } +} + +impl Display for EnumPiece { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.to_formatted()) + } +} diff --git a/src/bin/games/genius_square/models/constants/mod.rs b/src/bin/games/genius_square/models/constants/mod.rs new file mode 100644 index 0000000..416606a --- /dev/null +++ b/src/bin/games/genius_square/models/constants/mod.rs @@ -0,0 +1,6 @@ +/// Models for handling constants + +pub mod board; +pub mod dice; +pub mod enums; +pub mod pieces; diff --git a/src/bin/games/genius_square/models/constants/pieces.rs b/src/bin/games/genius_square/models/constants/pieces.rs new file mode 100644 index 0000000..01b890f --- /dev/null +++ b/src/bin/games/genius_square/models/constants/pieces.rs @@ -0,0 +1,109 @@ +/// ---------------------------------------------------------------- +/// IMPORTS +/// ---------------------------------------------------------------- + +use indoc::indoc; + +/// ---------------------------------------------------------------- +/// CONSTANTS +/// ---------------------------------------------------------------- + +pub const SYMB_BLOCK: &str = "\u{25A0}"; +pub const SYMB_FMT_BLOCK: &str = "\u{25A0}"; +pub const BLOCK: &str = indoc! {" + +..... + ...... + ...... + ...... + ...... +"}; + +pub const SYMB_PIECE_1: &str = "1"; +pub const SYMB_FMT_PIECE_1: &str = "1"; +pub const PIECE_1: &str = indoc! {" + +..... + ...... + ...... + ...... + ...... +"}; + +pub const SYMB_PIECE_2: &str = "2"; +pub const SYMB_FMT_PIECE_2: &str = "2"; +pub const PIECE_2: &str = indoc! {" + ++.... + ...... + ...... + ...... + ...... +"}; + +pub const SYMB_PIECE_3: &str = "3"; +pub const SYMB_FMT_PIECE_3: &str = "3"; +pub const PIECE_3: &str = indoc! {" + +++... + ...... + ...... + ...... + ...... +"}; + +pub const SYMB_PIECE_4: &str = "4"; +pub const SYMB_FMT_PIECE_4: &str = "4"; +pub const PIECE_4: &str = indoc! {" + ++++.. + ...... + ...... + ...... + ...... +"}; + +pub const SYMB_PIECE_C: &str = "C"; +pub const SYMB_FMT_PIECE_C: &str = "C"; +pub const PIECE_C: &str = indoc! {" + ++.... + +..... + ...... + ...... + ...... +"}; + +pub const SYMB_PIECE_L: &str = "L"; +pub const SYMB_FMT_PIECE_L: &str = "L"; +pub const PIECE_L: &str = indoc! {" + ++.... + +..... + +..... + ...... + ...... +"}; + +pub const SYMB_PIECE_T: &str = "T"; +pub const SYMB_FMT_PIECE_T: &str = "T"; +pub const PIECE_T: &str = indoc! {" + +++... + .+.... + ...... + ...... + ...... +"}; + +pub const SYMB_PIECE_X: &str = "X"; +pub const SYMB_FMT_PIECE_X: &str = "X"; +pub const PIECE_X: &str = indoc! {" + ++.... + ++.... + ...... + ...... + ...... +"}; + +pub const SYMB_PIECE_Z: &str = "Z"; +pub const SYMB_FMT_PIECE_Z: &str = "Z"; +pub const PIECE_Z: &str = indoc! {" + .++... + ++.... + ...... + ...... + ...... +"}; diff --git a/src/bin/games/genius_square/models/dice/methods.rs b/src/bin/games/genius_square/models/dice/methods.rs new file mode 100644 index 0000000..d4839a1 --- /dev/null +++ b/src/bin/games/genius_square/models/dice/methods.rs @@ -0,0 +1,21 @@ +/// ---------------------------------------------------------------- +/// IMPORTS +/// ---------------------------------------------------------------- + +use rand_chacha::ChaCha8Rng; +use rand::prelude::IndexedRandom; + +use crate::models::constants::dice::*; + +/// ---------------------------------------------------------------- +/// METHODS +/// ---------------------------------------------------------------- + +pub fn roll_dice( + rng: &mut ChaCha8Rng, +) -> Vec { + DICE + .iter() + .map(|die| die.choose(rng).unwrap().to_string()) + .collect() +} diff --git a/src/bin/games/genius_square/models/dice/mod.rs b/src/bin/games/genius_square/models/dice/mod.rs new file mode 100644 index 0000000..d66666c --- /dev/null +++ b/src/bin/games/genius_square/models/dice/mod.rs @@ -0,0 +1,4 @@ +/// Models for handling dice + +pub mod methods; +pub mod models; diff --git a/src/bin/games/genius_square/models/dice/models.rs b/src/bin/games/genius_square/models/dice/models.rs new file mode 100644 index 0000000..650325f --- /dev/null +++ b/src/bin/games/genius_square/models/dice/models.rs @@ -0,0 +1,60 @@ +/// ---------------------------------------------------------------- +/// IMPORTS +/// ---------------------------------------------------------------- + +use std::fmt::Debug; +use std::fmt::Display; +use std::fmt::Formatter; +use std::fmt::Result; + +use crate::models::constants::dice::*; + +/// ---------------------------------------------------------------- +/// STRUCTS +/// ---------------------------------------------------------------- + +#[derive(Copy, Clone, Debug)] +pub struct Die { + i: usize, + j: usize, +} + +/// ---------------------------------------------------------------- +/// IMPLEMENTATIONS +/// ---------------------------------------------------------------- + +impl Die { + pub fn from_string(face: &String) -> Die { + let chars: Vec = face.chars().map(|c| c.to_string()).collect(); + let char1 = chars.get(0).unwrap(); + let char2 = chars.get(1).unwrap(); + let index1 = FACE1.iter().position(|x| x == char1).unwrap(); + let index2 = FACE2.iter().position(|x| x == char2).unwrap(); + return Die { + i: index2, + j: index1, + } + } + + pub fn to_string(&self) -> String { + let char1: String = FACE1[self.j].to_string(); + let char2: String = FACE2[self.i].to_string(); + return format!("{char1}{char2}"); + } + + #[allow(unused)] + pub fn from_coords(i: usize, j: usize) -> Die { + return Die {i, j} + } + + #[allow(unused)] + pub fn to_coords(&self) -> (usize, usize) { + return (self.i, self.j) + } +} + +impl Display for Die { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.to_string()) + } +} diff --git a/src/bin/games/genius_square/models/mod.rs b/src/bin/games/genius_square/models/mod.rs new file mode 100644 index 0000000..33a3833 --- /dev/null +++ b/src/bin/games/genius_square/models/mod.rs @@ -0,0 +1,7 @@ +/// Models used in game. + +pub mod arrays; +pub mod board; +pub mod constants; +pub mod dice; +pub mod pieces; diff --git a/src/bin/games/genius_square/models/pieces/methods.rs b/src/bin/games/genius_square/models/pieces/methods.rs new file mode 100644 index 0000000..9e382d4 --- /dev/null +++ b/src/bin/games/genius_square/models/pieces/methods.rs @@ -0,0 +1,14 @@ +/// ---------------------------------------------------------------- +/// IMPORTS +/// ---------------------------------------------------------------- + +// use super::constants; + +/// ---------------------------------------------------------------- +/// METHODS +/// ---------------------------------------------------------------- + +#[allow(unused)] +pub fn method() { + panic!("not implemented"); +} diff --git a/src/bin/games/genius_square/models/pieces/mod.rs b/src/bin/games/genius_square/models/pieces/mod.rs new file mode 100644 index 0000000..2ea0b25 --- /dev/null +++ b/src/bin/games/genius_square/models/pieces/mod.rs @@ -0,0 +1,4 @@ +/// Models for handling pieces + +pub mod methods; +pub mod models; diff --git a/src/bin/games/genius_square/models/pieces/models.rs b/src/bin/games/genius_square/models/pieces/models.rs new file mode 100644 index 0000000..a0deed6 --- /dev/null +++ b/src/bin/games/genius_square/models/pieces/models.rs @@ -0,0 +1,188 @@ +/// ---------------------------------------------------------------- +/// IMPORTS +/// ---------------------------------------------------------------- + +use std::fmt::Debug; +use std::fmt::Display; +use std::fmt::Formatter; +use std::fmt::Result; +use std::ops::Add; +use std::ops::Mul; + +use crate::models::arrays::models::BinArray; +use crate::models::constants::board::*; +use crate::models::constants::enums::*; + +/// ---------------------------------------------------------------- +/// STRUCTS +/// ---------------------------------------------------------------- + +#[derive(Clone, Debug)] +pub struct Piece { + kind: EnumPiece, + positions: BinArray, +} + +/// ---------------------------------------------------------------- +/// IMPLEMENTATIONS +/// ---------------------------------------------------------------- + +impl Piece { + pub fn from_kind(kind: &EnumPiece, positions: Option) -> Self { + let kind = kind.clone(); + let positions = positions.unwrap_or_else(|| kind.get_positions()); + Self {kind, positions} + } + + pub fn from_coords( + coords: Vec<(usize, usize)>, + option_kind: Option, + ) -> Self { + let m = GRID_HEIGHT; + let n = GRID_WIDTH; + let positions = BinArray::from_coords(coords, m, n); + let kind = option_kind.unwrap_or(EnumPiece::Blank); + Self {kind, positions} + } + + pub fn to_coords(&self) -> Vec<(usize, usize)> { + self.positions.to_coords() + } + + pub fn get_kind(&self) -> EnumPiece { + self.kind.clone() + } + + pub fn get_symb(&self) -> String { + self.kind.as_str().to_string() + } + + pub fn get_symb_fmt(&self) -> String { + self.kind.to_formatted().to_string() + } + + pub fn get_positions(&self) -> &BinArray { + &self.positions + } + + #[allow(unused)] + pub fn set_positions(&mut self, positions: &BinArray) { + self.positions = positions.clone(); + } + + #[allow(unused)] + pub fn get_weight(&self) -> isize { + self.positions.get_weight() + } + + #[allow(unused)] + pub fn get_coweight(&self) -> isize { + self.positions.get_coweight() + } + + pub fn to_string(&self) -> String { + let n = GRID_WIDTH; + let hbar = "\u{2500}".repeat(n + 2); + let top = format!("\u{250C}{hbar}\u{2510}"); + let bot = format!("\u{2514}{hbar}\u{2518}"); + let middle = self.positions.get_values().rows() + .into_iter() + .map(|row| { + let line = row + .iter() + .map(|&val| { + if val == 1 { + "+".to_string() + } else { + ".".to_string() + } + }) + .collect::>() + .join(""); + return format!("\u{2502} {line} \u{2502}"); + }) + .collect::>() + .join("\n"); + let text = format!("{top}\n{middle}\n{bot}"); + return text; + } + + #[allow(unused)] + pub fn transform_hflip(&self, recentre: bool) -> Self { + let kind = self.get_kind(); + let positions = self.positions.transform_hflip(recentre); + let result = Self{kind, positions}; + return result; + } + + #[allow(unused)] + pub fn transform_vflip(&self, recentre: bool) -> Self { + let kind = self.get_kind(); + let positions = self.positions.transform_vflip(recentre); + let result = Self {kind, positions}; + return result; + + } + + #[allow(unused)] + pub fn transform_transpose(&self, recentre: bool) -> Self { + let kind = self.get_kind(); + let positions = self.positions.transform_transpose(recentre); + let result = Self {kind, positions}; + return result; + } + + #[allow(unused)] + pub fn transform_rotate(&self, k: i8, recentre: bool) -> Self { + let kind = self.get_kind(); + let positions = self.positions.transform_rotate(k, recentre); + let result = Self {kind, positions}; + return result; + } + + #[allow(unused)] + pub fn transform_shift( + &self, + di: isize, + dj: isize, + ) -> Self { + let kind = self.get_kind(); + let positions = self.positions.transform_shift(di, dj); + let result = Self {kind, positions}; + return result; + } + + #[allow(unused)] + pub fn transform_dither(&self) -> Self { + let kind = self.get_kind(); + let positions = self.positions.transform_dither(); + let result = Self {kind, positions}; + return result; + } +} + +impl Display for Piece { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.to_string()) + } +} + +impl Add for Piece { + type Output = Self; + + fn add(self, other: Self) -> Self::Output { + let kind = self.get_kind(); + let positions = self.get_positions().to_owned() + other.get_positions().to_owned(); + return Self {kind, positions}; + } +} + +impl Mul for Piece { + type Output = Self; + + fn mul(self, other: Self) -> Self::Output { + let kind = self.get_kind(); + let positions = self.get_positions().to_owned() * other.get_positions().to_owned(); + return Self {kind, positions}; + } +} diff --git a/src/lib.rs b/src/lib.rs index 5f8dab2..46446e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ -/// ---------------------------------------------------------------- -/// IMPORTS -/// ---------------------------------------------------------------- +/// Modules available from General crate for other crates + +pub mod app; pub mod _core; pub mod models; pub mod problems; diff --git a/src/main.rs b/src/main.rs index 1f306d2..7510e1c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ /// ---------------------------------------------------------------- /// IMPORTS /// ---------------------------------------------------------------- + mod _core; /// ---------------------------------------------------------------- diff --git a/src/mod.rs b/src/mod.rs index 63c7f2b..108d231 100644 --- a/src/mod.rs +++ b/src/mod.rs @@ -1,2 +1,5 @@ +/// Modules available for General code base + +pub mod app; pub mod models; pub mod problems; diff --git a/src/models/tree/base.rs b/src/models/tree/base.rs index 15a199f..f5962b2 100644 --- a/src/models/tree/base.rs +++ b/src/models/tree/base.rs @@ -2,7 +2,7 @@ /// IMPORTS /// ---------------------------------------------------------------- -// +/// /// ---------------------------------------------------------------- /// TRAITS diff --git a/src/models/tree/model.rs b/src/models/tree/model.rs index ba0d397..b918e32 100644 --- a/src/models/tree/model.rs +++ b/src/models/tree/model.rs @@ -1,16 +1,17 @@ /// ---------------------------------------------------------------- /// IMPORTS /// ---------------------------------------------------------------- -use std::vec; +use std::vec; use super::base::GenericTree; -use super::base::GenericTreeLike; use super::base::GenericTreeOrRoot; +use super::base::GenericTreeLike; /// ---------------------------------------------------------------- /// BASIC IMPLEMENTATION FOR GenericTree /// ---------------------------------------------------------------- +/// Implementation of a tree #[allow(unused)] impl GenericTree where @@ -65,10 +66,7 @@ where fn add(&mut self, other: GenericTreeOrRoot) { match other { GenericTreeOrRoot::Root(u) => { - let t = GenericTree { - root: u, - children: vec![], - }; + let t = GenericTree { root: u, children: vec![] }; self.children.push(t); } GenericTreeOrRoot::Tree(t) => { diff --git a/src/models/tree/tests_model.rs b/src/models/tree/tests_model.rs index 59ca1b3..653efc8 100644 --- a/src/models/tree/tests_model.rs +++ b/src/models/tree/tests_model.rs @@ -53,8 +53,7 @@ mod tests { ├─── alice: 23 ╰─── bob: 24 "# - ) - .to_string(); + ).to_string(); assert_eq!(t.to_string(), expected); } @@ -78,10 +77,7 @@ mod tests { name: Some("bird".to_string()), value: Some(2), }; - let pet1b = DummyNode { - name: None, - value: Some(3), - }; + let pet1b = DummyNode { name: None, value: Some(3) }; child1.add(GenericTreeOrRoot::Root(pet1a)); child1.add(GenericTreeOrRoot::Root(pet1b)); let child2 = DummyNode { @@ -98,16 +94,16 @@ mod tests { │ ╰─── _: 3 ╰─── bob: 24 "# - ) - .to_string(); + ).to_string(); assert_eq!(t.to_string(), expected); } } -// ---------------------------------------------------------------- -// AUXILIARY -// ---------------------------------------------------------------- +/// ---------------------------------------------------------------- +/// AUXILIARY +/// ---------------------------------------------------------------- +/// A dummy node type for test purposes #[derive(Clone)] struct DummyNode { name: Option, @@ -117,13 +113,9 @@ struct DummyNode { impl ToString for DummyNode { fn to_string(&self) -> String { match (&self.name, &self.value) { - (Some(name), Some(value)) => { - format!("{}: {}", name, value) - } + (Some(name), Some(value)) => format!("{}: {}", name, value), (Some(name), None) => name.clone(), - (None, Some(value)) => { - format!("_: {}", value) - } + (None, Some(value)) => format!("_: {}", value), (None, None) => "-".to_string(), } } diff --git a/src/problems/hackerrank/mathematics/main.rs b/src/problems/hackerrank/mathematics/main.rs index e9898a2..c5196c2 100644 --- a/src/problems/hackerrank/mathematics/main.rs +++ b/src/problems/hackerrank/mathematics/main.rs @@ -2,8 +2,6 @@ /// IMPORTS /// ---------------------------------------------------------------- -// - /// ---------------------------------------------------------------- /// MAIN /// ---------------------------------------------------------------- diff --git a/src/problems/hackerrank/mathematics/mod.rs b/src/problems/hackerrank/mathematics/mod.rs index 277f45b..5f03c25 100644 --- a/src/problems/hackerrank/mathematics/mod.rs +++ b/src/problems/hackerrank/mathematics/mod.rs @@ -1,4 +1,5 @@ /// Contains solutions to challenges in the Mathematics /// section of Hackerrank. /// Source: . + pub mod scalar_products; diff --git a/src/problems/hackerrank/mathematics/scalar_products/README.md b/src/problems/hackerrank/mathematics/scalar_products/README.md new file mode 100644 index 0000000..15f83fa --- /dev/null +++ b/src/problems/hackerrank/mathematics/scalar_products/README.md @@ -0,0 +1,126 @@ +# Problem: Scalar Products # + +Source: . + +Credit: User [@zemen](https://www.hackerrank.com/profile/zemen). + +## Description ## + +Let $C, m \in \mathbb{N}$. +Define + +$$ + (a_{i})_{i \in \mathbb{N}_{0}} + \subseteq + \mathbb{Z} / m\mathbb{Z} +$$ + +via + +- $a_{0} = 0$ +- $a_{1} = C$ +- $a_{i+2} = a_{i+1} + a_{i}$ + +and + +$$ +v_{k} + = \begin{pmatrix} + a_{2k}\\ + a_{2k + 1} + \end{pmatrix} +$$ + +for $k \in \mathbb{N}_{0}$. + +Given inputs $(C,M,n)$ compute $|S|$, +where + +$$ + S = \{ + \langle v_{i},\: v_{j} \rangle + \mid + 1 \leq i, j \leq n, + i \mathbb{N}eq j + \}, +$$ + +whereby the scalar products are again +computed over the ring $\mathbb{Z}/m\mathbb{Z}$. + +### Requirements ### + +Inputs occur in `stdin` of the form + +```bash +{C} {m} {n} +``` + +the output consists of a single number $|S|$, +streamed to `stdout`. + +### Example ### + +Sample input + +```bash +4 5 3 +``` + +one computes + +```text +a[0] = 0 +a[1] = 4 +a[2] = 4 +a[3] = 8 = 3 +a[4] = 7 = 2 +a[5] = 5 = 0 +a[6] = 2 +a[7] = 2 +a[8] = 4 +a[9] = 6 = 1 +a[10] = 5 = 0 +... +``` + +and thus + +$$ + v_{0} = \begin{pmatrix}0\\ 4\end{pmatrix}, + \: + v_{1} = \begin{pmatrix}4\\ 3\end{pmatrix}, + \: + v_{2} = \begin{pmatrix}2\\ 0\end{pmatrix}, + \: + v_{3} = \begin{pmatrix}2\\ 2\end{pmatrix}, + \: + \cdots +$$ + +The scalar products are + +$$ + S = \{ + \langle v_{1},\: v_{2} \rangle, + \langle v_{1},\: v_{3} \rangle, + \langle v_{2},\: v_{3} \rangle + \} + = \{ + (4 \cdot 2 + 3\cdot 0), + (4 \cdot 2 + 3\cdot 2), + (2 \cdot 2 + 0\cdot 2) + \} + = \{ + 3, + 14 \% 5, + 4 + \} + = \{3, 4\}. +$$ + +Hence the correct output is + +```bash +2 +``` diff --git a/src/problems/hackerrank/mathematics/scalar_products/approach1.rs b/src/problems/hackerrank/mathematics/scalar_products/approach1.rs index 2ac7b02..47c4f8f 100644 --- a/src/problems/hackerrank/mathematics/scalar_products/approach1.rs +++ b/src/problems/hackerrank/mathematics/scalar_products/approach1.rs @@ -1,5 +1,3 @@ -use core::iter::IntoIterator; -use core::iter::Iterator; /// # First approach # /// /// This approach is not mathematically optimised, @@ -10,11 +8,15 @@ use core::iter::Iterator; /// ---------------------------------------------------------------- /// IMPORTS /// ---------------------------------------------------------------- + +use core::iter::IntoIterator; +use core::iter::Iterator; // use core::convert::TryFrom; use std::collections::HashSet; use std::fmt::Debug; use std::io; use std::io::BufRead; +use std::io::Stdin; use std::slice::Iter; use std::str::FromStr; @@ -22,9 +24,11 @@ use std::str::FromStr; /// MAIN /// ---------------------------------------------------------------- +/// entry point when used as a script #[allow(unused)] fn main() { - let line = read_input(); + let lines = read_input(&io::stdin()); + let line = lines.iter().nth(0).unwrap(); let args: Vec = line.split(" ").map(|x| x.to_string()).collect(); let mut args: Iter<'_, String> = args.iter(); @@ -39,24 +43,24 @@ fn main() { } pub fn run(c: i32, m: i32, n: usize) -> usize { - let vecs = SeqPair::new(m, 0, c) + let vecs = SeqPair::new(m as i64, 0, c as i64) .into_iter() .map(|s| (s.current, s.next)) .skip(2) .step_by(2) .take(n); - let mut values: HashSet = HashSet::new(); + let mut values: HashSet = HashSet::new(); for (k, u) in vecs.enumerate() { - let vecs2 = SeqPair::new(m, u.0, u.1) + let vecs2 = SeqPair::new(m as i64, u.0, u.1) .into_iter() .map(|s| (s.current, s.next)) .skip(2) .step_by(2) .take(n - (k + 1)); for v in vecs2 { - let ip = (u.0 * v.0 + u.1 * v.1).rem_euclid(m); + let ip = (u.0 * v.0 + u.1 * v.1).rem_euclid(m as i64); values.insert(ip); } } @@ -68,12 +72,11 @@ pub fn run(c: i32, m: i32, n: usize) -> usize { /// ---------------------------------------------------------------- /// SECONDARY /// ---------------------------------------------------------------- - #[derive(Clone, Debug)] struct SeqPair { - modulus: i32, - current: i32, - next: i32, + modulus: i64, + current: i64, + next: i64, } trait EntityIterable { @@ -90,12 +93,8 @@ where } impl SeqPair { - fn new(modulus: i32, x: i32, y: i32) -> Self { - Self { - modulus, - current: x, - next: y, - } + fn new(modulus: i64, x: i64, y: i64) -> Self { + Self { modulus, current: x, next: y } } } @@ -134,11 +133,8 @@ impl IntoIterator for SeqPair { type Item = SeqPair; type IntoIter = EntityIterator; - fn into_iter(self) -> EntityIterator { - EntityIterator { - index: 0, - entity: self.clone(), - } + fn into_iter(self) -> Self::IntoIter { + return EntityIterator { index: 0, entity: self.clone() }; } } @@ -146,12 +142,11 @@ impl IntoIterator for SeqPair { /// AUXILIARY /// ---------------------------------------------------------------- +/// Obtains input lines from stdin +/// as a vector of strings. #[allow(unused)] -fn read_input() -> String { - let stdin = io::stdin(); - let mut input = stdin.lock().lines(); - let line = input.next().unwrap().unwrap().trim().to_string(); - return line; +fn read_input(stream: &Stdin) -> Vec { + stream.lock().lines().filter_map(Result::ok).collect() } #[allow(unused)] diff --git a/src/problems/hackerrank/mathematics/scalar_products/approach2.rs b/src/problems/hackerrank/mathematics/scalar_products/approach2.rs index 4a2a947..776843e 100644 --- a/src/problems/hackerrank/mathematics/scalar_products/approach2.rs +++ b/src/problems/hackerrank/mathematics/scalar_products/approach2.rs @@ -38,23 +38,27 @@ /// for `k in [3:2n]` via /// products of powers of the form `G^(2^l)`, /// relying on the binary representation of each `k`. -/// We thereby only need to compute `floor(log2(2n)) = O(log(n))` powers, -/// and the products of such powers are themselves `O(log(n))` -/// and we do this for `O(n)` entries. +/// +/// By reorganising the multiplication tasks into a binary tree, +/// the total time complexity for computing powers is O(n). +/// (One can more precisely compute this as lying between n/2 and n) /// So in total, the time complexity is /// /// ``` -/// O(n log(n)^2). +/// O(n) + O(n) = O(n) /// ``` /// ---------------------------------------------------------------- /// IMPORTS /// ---------------------------------------------------------------- -use std::collections::HashSet; + use std::fmt::Debug; use std::fmt::Display; use std::io; +use std::io::Stdin; use std::io::BufRead; +use std::collections::HashMap; +use std::collections::HashSet; use std::ops::Add; use std::ops::Mul; use std::ops::Rem; @@ -65,15 +69,17 @@ use std::str::FromStr; /// MAIN /// ---------------------------------------------------------------- +/// entry point when used as a script #[allow(unused)] fn main() { - let line = read_input(); + let lines = read_input(&io::stdin()); + let line = lines.iter().nth(0).unwrap(); let args: Vec = line.split(" ").map(|x| x.to_string()).collect(); let mut args: Iter<'_, String> = args.iter(); - let c = parse::(args.next().unwrap()); - let m = parse::(args.next().unwrap()); + let c = parse::(args.next().unwrap()); + let m = parse::(args.next().unwrap()); let n = parse::(args.next().unwrap()); let num_unique = run(c, m, n); @@ -81,92 +87,201 @@ fn main() { println!("{:?}", num_unique); } -pub fn run(c: i32, m: i32, n: usize) -> usize { +pub fn run(c: i64, m: i64, n: usize) -> usize { // sequence generated by symmetric matrix - let zero: Modulo = Modulo::new(0, m as i64); - let one: Modulo = Modulo::new(1, m as i64); - let matrix_f: SymmMatrix2x2> = SymmMatrix2x2 { - a: zero, - b: one, - d: one, + let zero: Modulo = Modulo::new(0, m); + let one: Modulo = Modulo::new(1, m); + let v0 = Vector2([zero, Modulo::new(c, m)]); + let matrix_f = SymmMatrix2x2 { a: zero, b: one, d: one }; + let matrix_g = matrix_f.pow2(); + + // compute system[k] = (_, G^k * v0) + let system = DynamicalSystem { + evolution: matrix_g, + state: v0, }; - - /* ---- * - * The n-the entry of the sequence - * is given by - * ---- */ - - // e is the smalles integer with 2^e >= (2*n) - let e = (2.0 * (n as f32)).log2().ceil() as i64; - - // compute G[k] = G^(2^k): - let mut matrix_g = matrix_f.pow2(); - let mut matrices_g = vec![matrix_g.clone()]; - for _ in 1..e { - matrix_g = matrix_g.pow2(); - matrices_g.push(matrix_g.clone()); - } + let n_max = 2 * (n as i64); + let system_powers = compute_powers(&system, n_max); // compute all powers of G: - let v0 = Vector2([zero, Modulo::new(c as i64, m as i64)]); let mut values: HashSet = HashSet::new(); - for k in 3..(2 * n) { - // compute v := G^k * v_0 using divide and conquer - let k_base2 = format!("{:b}", k); - let places = k_base2 - .chars() - .rev() - .enumerate() - .filter(|(_, d)| *d == '1') - .map(|(k, _)| k); - let mut v = v0.clone(); - for j in places { - v = matrices_g.get(j).unwrap().mul_vector(&v); - } + for k in 3.. n_max { + // k = i + j + // compute v := G^k * v_0 + let system_pow_k = system_powers.get(&(k as i64)).unwrap(); + let v = system_pow_k.state; // now compute < G^i v_0, G^j v_0 > = < G^k v_0, v_0 > = < v, v_0 > let value = Vector2::inner_product(&v, &v0).value; values.insert(value); } - let num_unique = values.len(); - return num_unique; + return values.len(); +} + +/// ---------------------------------------------------------------- +/// ALGORITHMS +/// ---------------------------------------------------------------- + +/// computes x^(2^k) for 1 <= k <= n +pub fn power_of_2_powers(x: &T, n: i64) -> HashMap +where + T: Clone + Mul + BinaryPowers, +{ + let mut results: HashMap = HashMap::new(); + let mut x_pow_2_pow = x.clone(); + results.insert(0, x_pow_2_pow.clone()); + (1.. n).for_each(|k| { + x_pow_2_pow = x_pow_2_pow.pow2(); + results.insert(k as i64, x_pow_2_pow.clone()); + }); + return results; +} + +/// efficiently computes x^k for 0 <= k <= n +/// Uses binary tree structure to avoid redundant computations. +/// Time complexity O(n) +pub fn compute_powers(x: &T, n: i64) -> HashMap +where + T: Clone + Mul + BinaryPowers, +{ + let mut powers: HashMap = HashMap::new(); + powers.insert(0, x.pow0()); + + // in trivial case stop + if n <= 0 { + return powers; + } + + /* -------------------------------- * + * Find l with 2^(l-1) <= n < 2^l (i.e. n has l digits) + * NOTE: + * 2^(l-1) <= n < 2^l + * <==> l - 1 <= log2(n) < l + * <==> l - 1 = floor(log2(n)) + * <==> l = floor(log2(n)) + 1 + * -------------------------------- */ + let l = (n as f64).log2().floor() as i64 + 1; + + // compute x^(2^k) for k from 0 to l-1 + let x_pow_2_pow = power_of_2_powers(x, l); + + // now compute along levels of a binary tree + let powers = (0.. l).fold( + powers, + |mut prev, k| { + let num_leaves = prev.len() as i64; + let x_pow_2_pow_k = x_pow_2_pow.get(&k).unwrap().clone(); + for (&j, value) in prev.clone().iter() { + let value_ = x_pow_2_pow_k.clone() * value.clone(); + prev.insert(j + num_leaves, value_); + }; + return prev; + } + ); + + return powers; } /// ---------------------------------------------------------------- -/// SECONDARY +/// STRUCTURES /// ---------------------------------------------------------------- -#[derive(Clone, Copy, PartialEq, Eq)] -struct Modulo +/// Helper structure for modulo computations +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct Modulo where - T: Display + Clone + Copy + PartialEq + Eq, + T: Clone + PartialEq + Eq, { - value: T, - modulus: T, + pub value: T, + pub modulus: T, +} + +#[derive(Copy, Clone, Debug)] +pub struct Vector2(pub [T; 2]); + +#[derive(Copy, Clone, Debug)] +pub struct SymmMatrix2x2 { + pub a: T, + pub b: T, + pub d: T, +} + +/// A dynamical system encodes +/// evolution = A +/// state = rho +/// with the assumption that rho = A*rho_0 +/// for some initial state rho_0 +#[derive(Copy, Clone, Debug)] +pub struct DynamicalSystem { + pub evolution: SymmMatrix2x2, + pub state: Vector2, +} + +/// ---------------------------------------------------------------- +/// TRAITS +/// ---------------------------------------------------------------- + +pub trait NumberLike { + fn positive(&self) -> bool; +} + +pub trait BinaryPowers { + fn zerolike(&self) -> Self; + fn pow0(&self) -> Self; + fn pow2(&self) -> Self; } -#[derive(Clone, Debug)] -struct Vector2([T; 2]); +/// ---------------------------------------------------------------- +/// IMPLEMENTATIONS +/// ---------------------------------------------------------------- -#[derive(Clone, Debug)] -struct SymmMatrix2x2 { - a: T, - b: T, - d: T, +impl NumberLike for i64 { + fn positive(&self) -> bool { + *self > 0 + } +} + +impl BinaryPowers for i64 { + fn zerolike(&self) -> Self { + 0 + } + fn pow0(&self) -> Self { + 1 + } + fn pow2(&self) -> Self { + self * self + } } impl Modulo where - T: Display + Clone + Copy + PartialEq + Eq, + T: Display + Clone + PartialEq + Eq, { - fn new(value: T, modulus: T) -> Self { + pub fn new(value: T, modulus: T) -> Self { Self { value, modulus } } } +impl Modulo +where + T: Copy + Clone + PartialEq + Eq + NumberLike + Rem, +{ + // type Output = T; + // fn rem(self, rhs: Self) -> Self::Output { + // } + + pub fn remainder(mut self) -> Self { + let m = self.modulus; + if m.positive() { + self.value = self.value.rem(m) + } + return self; + } +} + impl Display for Modulo where - T: Display + Clone + Copy + PartialEq + Eq, + T: Display + Clone + PartialEq + Eq, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{} (mod {})", self.value, self.modulus) @@ -175,7 +290,7 @@ where impl Debug for Modulo where - T: Display + Clone + Copy + PartialEq + Eq, + T: Display + Clone + PartialEq + Eq, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self) @@ -185,11 +300,11 @@ where impl Add for Modulo where T: Display - + Clone + Copy + + Clone + PartialEq + Eq - + Add + + NumberLike + Add + Rem, { @@ -197,64 +312,60 @@ where fn add(self, other: Self) -> Self::Output { let modulus = self.modulus; - Self { - value: (self.value + other.value) % modulus, - modulus, - } + let value = self.value + other.value; + return Self::new(value, modulus).remainder(); } } impl Mul for Modulo where T: Display - + Clone + Copy + + Clone + PartialEq + Eq + + NumberLike + Mul - + Add + Rem, { type Output = Self; fn mul(self, other: Self) -> Self::Output { let modulus = self.modulus; - Self { - value: (self.value * other.value) % modulus, - modulus, - } + let value = self.value * other.value; + return Self::new(value, modulus).remainder(); } } -impl Add for SymmMatrix2x2 +impl BinaryPowers for Modulo where - T: Copy + Add, + T: Copy + + Clone + + PartialEq + + Eq + + NumberLike + + Mul + + Rem + + BinaryPowers, { - type Output = Self; + fn zerolike(&self) -> Self { + let value = self.value.zerolike(); + let modulus = self.modulus; + Self { value, modulus } + } - fn add(self, other: Self) -> Self::Output { - Self { - a: self.a + other.a, - b: self.b + other.b, - d: self.d + other.d, - } + fn pow0(&self) -> Self { + let value = self.value.pow0(); + let modulus = self.modulus; + Self { value, modulus } } -} -// impl Mul for SymmMatrix2x2 -// where -// T: Copy + Mul + Add, -// { -// type Output = Self; - -// fn mul(self, other: Self) -> Self::Output { -// Self { -// a: self.a * other.a + self.b * other.b, -// b: self.a * other.b + self.b * other.d, -// d: self.b * other.b + self.d * other.d, -// } -// } -// } + fn pow2(&self) -> Self { + let modulus = self.modulus; + let value = self.value.pow2().rem(modulus); + Self { value, modulus } + } +} impl Vector2 where @@ -267,7 +378,7 @@ where impl Vector2 where - T: Copy + Mul + Add, + T: Copy + Clone + Mul + Add, { fn inner_product(u: &Self, v: &Self) -> T { u.get(0) * v.get(0) + u.get(1) * v.get(1) @@ -278,19 +389,132 @@ impl SymmMatrix2x2 where T: Copy + Mul + Add, { - fn pow2(self) -> Self { + fn mul_vector(&self, u: &Vector2) -> Vector2 { + Vector2( + [ + (self.a * u.get(0) + self.b * u.get(1)), + (self.b * u.get(0) + self.d * u.get(1)), + ], + ) + } +} + +impl Add for SymmMatrix2x2 +where + T: Clone + Add, +{ + type Output = Self; + + fn add(self, other: Self) -> Self::Output { Self { - a: self.a * self.a + self.b * self.b, - b: (self.a + self.d) * self.b, - d: self.b * self.b + self.d * self.d, + a: self.a + other.a, + b: self.b + other.b, + d: self.d + other.d, } } +} - fn mul_vector(&self, u: &Vector2) -> Vector2 { - Vector2([ - (self.a * u.get(0) + self.b * u.get(1)), - (self.b * u.get(0) + self.d * u.get(1)), - ]) +impl Mul for SymmMatrix2x2 +where + T: Clone + Mul, +{ + type Output = Self; + + fn mul(self, other: Self) -> Self::Output { + Self { + a: self.a * other.a, + b: self.b * other.b, + d: self.d * other.d, + } + } +} + +impl BinaryPowers for SymmMatrix2x2 +where + T: Copy + Mul + Add + BinaryPowers, +{ + fn zerolike(&self) -> Self { + Self { + a: self.a.pow0(), + b: self.b.zerolike(), + d: self.d.pow0(), + } + } + + fn pow0(&self) -> Self { + Self { + a: self.a.pow0(), + b: self.b.zerolike(), + d: self.d.pow0(), + } + } + + fn pow2(&self) -> Self { + let aa = self.a.pow2(); + let bb = self.b.pow2(); + let dd = self.d.pow2(); + let a = aa + bb; + let b = (self.a + self.d) * self.b; + let d = bb + dd; + Self { a, b, d } + } +} + +impl Mul for DynamicalSystem +where + T: Copy + Add + Mul, +{ + type Output = Self; + + /// Assumes dynamical system 1 and 2 share the same initial state + /// i.e. D1 = (U1, rho1), D2 = (U2, rho2) + /// with rho1 = U1*rho0 and rho2 = U2*rho0 + /// where rho0 = a common initial state + /// The "product" is determined by + /// application of evolution 1 to state2, + /// i.e. + /// + /// - replace evolution by U1 + /// - replace initial state by rho2 + /// + /// i.e. + /// + /// D1*D2 := (U1, U1 * rho2) + /// = (U1, U1 * U2 * rho0) + fn mul(self, other: Self) -> Self::Output { + let evolution = self.evolution; + let state = self.evolution.mul_vector(&other.state); + return Self { evolution, state }; + } +} + +impl BinaryPowers for DynamicalSystem +where + T: Copy + + Clone + + Add + + Mul + + BinaryPowers, +{ + fn zerolike(&self) -> Self { + Self { + evolution: self.evolution.zerolike(), + state: self.state, + } + } + + fn pow0(&self) -> Self { + Self { + evolution: self.evolution.pow0(), + state: self.state, + } + } + + fn pow2(&self) -> Self { + let mut evolution = self.evolution; + let state = self.evolution.mul_vector(&self.state); + evolution = evolution.pow2(); + Self { evolution, state } } } @@ -298,12 +522,11 @@ where /// AUXILIARY /// ---------------------------------------------------------------- +/// Obtains input lines from stdin +/// as a vector of strings. #[allow(unused)] -fn read_input() -> String { - let stdin = io::stdin(); - let mut input = stdin.lock().lines(); - let line = input.next().unwrap().unwrap().trim().to_string(); - return line; +fn read_input(stream: &Stdin) -> Vec { + stream.lock().lines().filter_map(Result::ok).collect() } #[allow(unused)] diff --git a/src/problems/hackerrank/mathematics/scalar_products/mod.rs b/src/problems/hackerrank/mathematics/scalar_products/mod.rs index 4020928..e5293f5 100644 --- a/src/problems/hackerrank/mathematics/scalar_products/mod.rs +++ b/src/problems/hackerrank/mathematics/scalar_products/mod.rs @@ -1,5 +1,6 @@ /// Solutions to the Hackerrank Mathematics challenge: /// . + pub mod approach1; pub mod approach2; diff --git a/src/problems/hackerrank/mathematics/scalar_products/tests_approach1.rs b/src/problems/hackerrank/mathematics/scalar_products/tests_approach1.rs index 7a1fb0a..a42a044 100644 --- a/src/problems/hackerrank/mathematics/scalar_products/tests_approach1.rs +++ b/src/problems/hackerrank/mathematics/scalar_products/tests_approach1.rs @@ -1,12 +1,14 @@ /// ---------------------------------------------------------------- /// IMPORTS /// ---------------------------------------------------------------- -use crate::problems::hackerrank::mathematics::scalar_products::approach1 as approach; + +use super::approach1 as approach; /// ---------------------------------------------------------------- /// TESTS /// ---------------------------------------------------------------- +/// bundle of tests #[cfg(test)] mod tests { use super::*; @@ -22,4 +24,12 @@ mod tests { let result = approach::run(1, 100, 1000); assert_eq!(result, 50); } + + /// NOTE: This approach is inefficient and therefore skipped. + #[test] + #[ignore] + fn test_case_heavy_1() { + let result = approach::run(991, 11495481, 112259); + assert_eq!(result, 224515); + } } diff --git a/src/problems/hackerrank/mathematics/scalar_products/tests_approach2.rs b/src/problems/hackerrank/mathematics/scalar_products/tests_approach2.rs index 9494312..96e8373 100644 --- a/src/problems/hackerrank/mathematics/scalar_products/tests_approach2.rs +++ b/src/problems/hackerrank/mathematics/scalar_products/tests_approach2.rs @@ -1,12 +1,14 @@ /// ---------------------------------------------------------------- /// IMPORTS /// ---------------------------------------------------------------- + use super::approach2 as approach; /// ---------------------------------------------------------------- /// TESTS /// ---------------------------------------------------------------- +/// bundle of tests #[cfg(test)] mod tests { use super::*; diff --git a/src/problems/hackerrank/mod.rs b/src/problems/hackerrank/mod.rs index d859f52..986e5ab 100644 --- a/src/problems/hackerrank/mod.rs +++ b/src/problems/hackerrank/mod.rs @@ -1,3 +1,5 @@ +pub mod mathematics; /// Contains solutions to Hackerrank challenges. /// Source: . -pub mod mathematics; + +pub mod project_euler; diff --git a/src/problems/hackerrank/project_euler/mod.rs b/src/problems/hackerrank/project_euler/mod.rs new file mode 100644 index 0000000..f6972f6 --- /dev/null +++ b/src/problems/hackerrank/project_euler/mod.rs @@ -0,0 +1,5 @@ +/// Contains solutions to challenges in the Mathematics +/// section of Hackerrank. +/// Source: . + +pub mod problem10_prime_summation; diff --git a/src/problems/hackerrank/project_euler/problem10_prime_summation/approach1.rs b/src/problems/hackerrank/project_euler/problem10_prime_summation/approach1.rs new file mode 100644 index 0000000..9d18f0c --- /dev/null +++ b/src/problems/hackerrank/project_euler/problem10_prime_summation/approach1.rs @@ -0,0 +1,99 @@ +/// # First approach # +/// +/// Computes Primes using the Sieve of Eratosthenes. +/// Then efficiently computes the cumulutative sum of primes +/// below certain integers. + +/// ---------------------------------------------------------------- +/// IMPORTS +/// ---------------------------------------------------------------- + +use std::io; +use std::io::BufRead; +use std::clone::Clone; +use std::fmt::Debug; +use std::cmp::Ord; +use std::collections::HashMap; +use std::io::Stdin; +use std::str::FromStr; + +/// ---------------------------------------------------------------- +/// MAIN +/// ---------------------------------------------------------------- + +/// entry point when used as a script +#[allow(unused)] +fn main() { + let lines = read_input(&io::stdin()); + let t = lines.iter().nth(0).unwrap().parse::(); + let numbers: Vec = lines.iter().skip(1).map(|x| x.parse().unwrap()).collect(); + + let sums = run(&numbers); + + for n in numbers.iter() { + if let Some(s) = sums.get(n) { + println!("{:?}", s); + } + } +} + +pub fn run(numbers: &Vec) -> HashMap { + let n_max: i64 = numbers.iter().fold(0, |prev, &n| prev.max(n)); + let primes = get_primes(n_max as i64); + let sums = compute_aggregates(&numbers, &primes); + return sums; +} + +/// ---------------------------------------------------------------- +/// HELPER METHODS +/// ---------------------------------------------------------------- + +/// computes the list of primes up to a value +fn get_primes(n_max: i64) -> Vec { + let mut map = HashMap::::new(); + let mut result = Vec::::new(); + for p in 2..(n_max + 1) { + if map.get(&p) != Some(&false) { + result.push(p); + ((2 * p)..(n_max + 1)).step_by(p as usize).for_each(|k| { + map.entry(k).or_insert(false); + }); + } + } + return result; +} + +fn compute_aggregates(numbers: &Vec, primes: &Vec) -> HashMap { + let mut numbers_sorted = numbers.clone(); + numbers_sorted.sort(); + numbers_sorted.dedup(); + let mut values = primes.clone(); + let mut sums = HashMap::::new(); + let mut sum: i64 = 0; + for n in numbers_sorted { + sum += values.iter().filter(|&&p| (p <= n)).sum::(); + values = values.iter().filter(|&&p| (p > n)).cloned().collect(); + sums.insert(n, sum); + } + return sums; +} + +/// ---------------------------------------------------------------- +/// AUXILIARY +/// ---------------------------------------------------------------- + +/// Obtains input lines from stdin +/// as a vector of strings. +#[allow(unused)] +fn read_input(stream: &Stdin) -> Vec { + stream.lock().lines().filter_map(Result::ok).collect() +} + +#[allow(unused)] +fn parse(text: &String) -> T +where + T: FromStr, + ::Err: Debug, +{ + return text.parse::().unwrap(); +} diff --git a/src/problems/hackerrank/project_euler/problem10_prime_summation/mod.rs b/src/problems/hackerrank/project_euler/problem10_prime_summation/mod.rs new file mode 100644 index 0000000..d27bc37 --- /dev/null +++ b/src/problems/hackerrank/project_euler/problem10_prime_summation/mod.rs @@ -0,0 +1,6 @@ +/// Solutions to the Hackerrank Project Euler challenge: +/// . +pub mod approach1; + +#[cfg(test)] +pub mod tests_approach1; diff --git a/src/problems/hackerrank/project_euler/problem10_prime_summation/tests_approach1.rs b/src/problems/hackerrank/project_euler/problem10_prime_summation/tests_approach1.rs new file mode 100644 index 0000000..3f8c44d --- /dev/null +++ b/src/problems/hackerrank/project_euler/problem10_prime_summation/tests_approach1.rs @@ -0,0 +1,27 @@ +/// ---------------------------------------------------------------- +/// IMPORTS +/// ---------------------------------------------------------------- + +use std::collections::HashMap; + +use super::approach1 as approach; + +/// ---------------------------------------------------------------- +/// TESTS +/// ---------------------------------------------------------------- + +/// bundle of tests +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_case1() { + let args = vec![5, 10]; + let result = approach::run(&args); + let mut expected = HashMap::::new(); + expected.insert(5, 10); + expected.insert(10, 17); + assert_eq!(result, expected); + } +} diff --git a/templates/template.env b/templates/template.env index d3a3044..1616d08 100644 --- a/templates/template.env +++ b/templates/template.env @@ -2,7 +2,7 @@ # PROJECT SETTINGS # ---------------------------------------------------------------- -MAIN_MODULE="code_challenges" +MAIN_MODULE="CodeChallenges" # ---------------------------------------------------------------- # LOCAL SYSTEM SETTINGS diff --git a/tests/lib.rs b/tests/lib.rs index ea13f49..d65fd56 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,4 +1,4 @@ -extern crate code_challenges; +extern crate general; extern crate rstest; // pub mod path_to_testmodule;