diff --git a/Cargo.lock b/Cargo.lock index 7425678..4355f1f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,31 +2,43 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addchain" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2e69442aa5628ea6951fa33e24efe8313f4321a91bd729fc2f75bdfc858570" +dependencies = [ + "num-bigint 0.3.3", + "num-integer", + "num-traits", +] + [[package]] name = "aead" -version = "0.3.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ + "crypto-common", "generic-array", ] [[package]] name = "aes" -version = "0.6.0" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ - "aes-soft", - "aesni", + "cfg-if", "cipher", + "cpufeatures", ] [[package]] name = "aes-gcm" -version = "0.8.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ "aead", "aes", @@ -37,54 +49,316 @@ dependencies = [ ] [[package]] -name = "aes-soft" -version = "0.6.4" +name = "ahash" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ - "cipher", - "opaque-debug", + "cfg-if", + "once_cell", + "version_check", + "zerocopy", ] [[package]] -name = "aesni" -version = "0.10.0" +name = "aho-corasick" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ - "cipher", - "opaque-debug", + "memchr", ] [[package]] -name = "aho-corasick" -version = "0.7.18" +name = "anyhow" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8" + +[[package]] +name = "aptos-crypto" +version = "0.0.3" dependencies = [ - "memchr", + "aes-gcm", + "anyhow", + "aptos-crypto-derive", + "ark-bls12-381", + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-groth16", + "ark-serialize", + "ark-std", + "base64", + "bcs", + "bitvec", + "blake2", + "blake2-rfc", + "blst", + "bulletproofs", + "byteorder", + "bytes", + "criterion", + "curve25519-dalek", + "curve25519-dalek-ng", + "digest 0.9.0", + "ed25519-dalek", + "ff", + "hex", + "hkdf", + "libsecp256k1", + "merlin", + "more-asserts", + "neptune", + "num-bigint 0.3.3", + "num-integer", + "once_cell", + "openrpc-schema", + "p256", + "poseidon-ark", + "proptest", + "proptest-derive", + "rand 0.7.3", + "rand_core 0.5.1", + "ring", + "serde", + "serde-name", + "serde_bytes", + "serde_json", + "sha2 0.10.8", + "sha2 0.9.9", + "sha3", + "signature 2.2.0", + "static_assertions", + "thiserror", + "tiny-keccak", + "trybuild", + "typenum", + "x25519-dalek", ] [[package]] -name = "ansi_term" -version = "0.12.1" +name = "aptos-crypto-derive" +version = "0.0.3" +dependencies = [ + "anyhow", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-bls12-381" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" dependencies = [ - "winapi", + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", ] [[package]] -name = "anyhow" -version = "1.0.53" +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-crypto-primitives" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3a13b34da09176a8baba701233fdffbaa7c1b1192ce031a3da4e55ce1f1a56" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-relations", + "ark-serialize", + "ark-snark", + "ark-std", + "blake2", + "derivative", + "digest 0.10.7", + "rayon", + "sha2 0.10.8", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools", + "num-traits", + "rayon", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest 0.10.7", + "itertools", + "num-bigint 0.4.6", + "num-traits", + "paste", + "rayon", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint 0.4.6", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-groth16" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0" +checksum = "20ceafa83848c3e390f1cbf124bc3193b3e639b3f02009e0e290809a501b95fc" +dependencies = [ + "ark-crypto-primitives", + "ark-ec", + "ark-ff", + "ark-poly", + "ark-relations", + "ark-serialize", + "ark-std", + "rayon", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "rayon", +] + +[[package]] +name = "ark-relations" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00796b6efc05a3f48225e59cb6a2cda78881e7c390872d5786aaf112f31fb4f0" +dependencies = [ + "ark-ff", + "ark-std", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest 0.10.7", + "num-bigint 0.4.6", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-snark" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d3cc6833a335bb8a600241889ead68ee89a3cf8448081fb7694c0fe503da63" +dependencies = [ + "ark-ff", + "ark-relations", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", + "rayon", +] [[package]] name = "arrayref" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" + +[[package]] +name = "arrayvec" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" +dependencies = [ + "nodrop", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "atty" @@ -92,16 +366,16 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "base-x" @@ -110,43 +384,61 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" [[package]] -name = "bcs" -version = "0.1.3" +name = "base16ct" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "510fd83e3eaf7263b06182f3550b4c0af2af42cb36ab8024969ff5ea7fcb2833" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bcs" +version = "0.1.4" +source = "git+https://github.com/aptos-labs/bcs.git?rev=d31fab9d81748e2594be5cd5cdf845786a30562d#d31fab9d81748e2594be5cd5cdf845786a30562d" dependencies = [ "serde", "thiserror", ] [[package]] -name = "bindgen" -version = "0.59.2" +name = "bellpepper" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8" +checksum = "9ae286c2cb403324ab644c7cc68dceb25fe52ca9429908a726d7ed272c1edf7b" dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "clap", - "env_logger", - "lazy_static", - "lazycell", - "log", - "peeking_take_while", - "proc-macro2 1.0.36", - "quote 1.0.15", - "regex", - "rustc-hash", - "shlex", - "which", + "bellpepper-core", + "byteorder", + "ff", +] + +[[package]] +name = "bellpepper-core" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d8abb418570756396d722841b19edfec21d4e89e1cf8990610663040ecb1aea" +dependencies = [ + "blake2s_simd", + "byteorder", + "ff", + "serde", + "thiserror", ] [[package]] name = "bit-set" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ "bit-vec", ] @@ -163,11 +455,17 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + [[package]] name = "bitvec" -version = "0.19.6" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55f93d0ef3363c364d5976646a38f04cf67cfe1d4c8d160cdea02cab2c116b33" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ "funty", "radium", @@ -175,6 +473,47 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "blake2-rfc" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" +dependencies = [ + "arrayvec 0.4.12", + "constant_time_eq 0.1.5", +] + +[[package]] +name = "blake2b_simd" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" +dependencies = [ + "arrayref", + "arrayvec 0.7.6", + "constant_time_eq 0.3.1", +] + +[[package]] +name = "blake2s_simd" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94230421e395b9920d23df13ea5d77a20e1725331f90fbbf6df6040b33f756ae" +dependencies = [ + "arrayref", + "arrayvec 0.7.6", + "constant_time_eq 0.3.1", +] + [[package]] name = "block-buffer" version = "0.9.0" @@ -185,6 +524,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "block-padding" version = "0.2.1" @@ -192,57 +540,93 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" [[package]] -name = "bstr" -version = "0.2.17" +name = "blst" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +checksum = "4378725facc195f1a538864863f6de233b500a8862747e7f165078a419d5e874" dependencies = [ - "lazy_static", - "memchr", - "regex-automata", + "cc", + "glob", + "threadpool", + "zeroize", +] + +[[package]] +name = "blstrs" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a8a8ed6fefbeef4a8c7b460e4110e12c5e22a5b7cf32621aae6ad650c4dcf29" +dependencies = [ + "blst", + "byte-slice-cast", + "ff", + "group", + "pairing", + "rand_core 0.6.4", "serde", + "subtle", +] + +[[package]] +name = "bulletproofs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40e698f1df446cc6246afd823afbe2d121134d089c9102c1dd26d1264991ba32" +dependencies = [ + "byteorder", + "clear_on_drop", + "curve25519-dalek-ng", + "digest 0.9.0", + "merlin", + "rand 0.8.5", + "rand_core 0.6.4", + "serde", + "serde_derive", + "sha3", + "subtle-ng", + "thiserror", ] [[package]] name = "bumpalo" -version = "3.9.1" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] -name = "byteorder" -version = "1.4.3" +name = "byte-slice-cast" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] -name = "bytes" -version = "1.1.0" +name = "byteorder" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] -name = "cast" -version = "0.2.7" +name = "bytes" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" dependencies = [ - "rustc_version", + "serde", ] [[package]] -name = "cc" -version = "1.0.73" +name = "cast" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] -name = "cexpr" -version = "0.6.0" +name = "cc" +version = "1.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" dependencies = [ - "nom", + "shlex", ] [[package]] @@ -253,22 +637,12 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cipher" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" -dependencies = [ - "generic-array", -] - -[[package]] -name = "clang-sys" -version = "1.3.1" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cc00842eed744b858222c4c9faf7243aafc6d33f92f96935263ef4d8a41ce21" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "glob", - "libc", - "libloading", + "crypto-common", + "inout", ] [[package]] @@ -277,15 +651,38 @@ version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ - "ansi_term", - "atty", - "bitflags", - "strsim", + "bitflags 1.3.2", "textwrap", "unicode-width", - "vec_map", ] +[[package]] +name = "clear_on_drop" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38508a63f4979f0048febc9966fadbd48e5dab31fd0ec6a3f151bbf4a74f7423" +dependencies = [ + "cc", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + [[package]] name = "core2" version = "0.4.0" @@ -297,24 +694,18 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.1" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] -[[package]] -name = "cpuid-bool" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" - [[package]] name = "criterion" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10" +checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" dependencies = [ "atty", "cast", @@ -338,57 +729,38 @@ dependencies = [ [[package]] name = "criterion-plot" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57" +checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" dependencies = [ "cast", "itertools", ] -[[package]] -name = "crossbeam-channel" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - [[package]] name = "crossbeam-deque" -version = "0.8.1" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.7" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "cfg-if", "crossbeam-utils", - "lazy_static", - "memoffset", - "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.7" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6" -dependencies = [ - "cfg-if", - "lazy_static", -] +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crunchy" @@ -396,6 +768,39 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + [[package]] name = "crypto-mac" version = "0.10.1" @@ -408,60 +813,72 @@ dependencies = [ [[package]] name = "csv" -version = "1.1.6" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" +checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" dependencies = [ - "bstr", "csv-core", - "itoa 0.4.8", + "itoa", "ryu", "serde", ] [[package]] name = "csv-core" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" dependencies = [ "memchr", ] [[package]] name = "ctr" -version = "0.6.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ "cipher", ] [[package]] -name = "curve25519-dalek-fiat" -version = "0.1.0" +name = "curve25519-dalek" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44339b9ecede7f72a0d3b012bf9bb5a616dc8bfde23ce544e42da075c87198f0" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" dependencies = [ "byteorder", - "digest", - "fiat-crypto", - "rand_core", + "digest 0.9.0", + "rand_core 0.5.1", "subtle", "zeroize", ] +[[package]] +name = "curve25519-dalek-ng" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c359b7249347e46fb28804470d071c921156ad62b3eef5d34e2ba867533dec8" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.6.4", + "serde", + "subtle-ng", + "zeroize", +] + [[package]] name = "data-encoding" -version = "2.3.2" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "data-encoding-macro" -version = "0.1.12" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86927b7cd2fe88fa698b87404b287ab98d1a0063a34071d92e575b72d3029aca" +checksum = "f1559b6cba622276d6d63706db152618eeb15b89b3e4041446b05876e352e639" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -469,63 +886,34 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.10" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5bbed42daaa95e780b60a50546aa345b8413a1e46f9a40a12907d3598f038db" +checksum = "332d754c0af53bc87c108fed664d121ecf59207ec4196041f04d6ab9002ad33f" dependencies = [ "data-encoding", - "syn 1.0.86", + "syn 1.0.109", ] [[package]] -name = "diem-crypto" -version = "0.0.3" +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ - "aes-gcm", - "anyhow", - "bcs", - "bindgen", - "bitvec", - "byteorder", - "bytes", - "cc", - "criterion", - "curve25519-dalek-fiat", - "diem-crypto-derive", - "digest", - "ed25519-dalek-fiat", - "hex", - "hkdf", - "libc", - "mirai-annotations", - "once_cell", - "openrpc-schema", - "proptest", - "proptest-derive", - "rand", - "rand_core", - "ripemd160", - "serde", - "serde-name 0.1.2", - "serde_bytes", - "serde_json", - "sha2", - "sha3", - "static_assertions", - "thiserror", - "tiny-keccak", - "trybuild", - "x25519-dalek-fiat", + "const-oid", + "pem-rfc7468", + "zeroize", ] [[package]] -name = "diem-crypto-derive" -version = "0.0.3" +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "anyhow", - "proc-macro2 1.0.36", - "quote 1.0.15", - "syn 1.0.86", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -537,70 +925,139 @@ dependencies = [ "generic-array", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid", + "crypto-common", + "subtle", +] + [[package]] name = "dyn-clone" -version = "1.0.4" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + +[[package]] +name = "ecdsa" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2626afccd7561a06cf1367e2950c4718ea04565e20fb5029b6c7d8ad09abcf" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature 2.2.0", + "spki", +] [[package]] name = "ed25519" -version = "1.3.0" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74e1069e39f1454367eb2de793ed062fac4c35c2934b76a81d90dd9abcd28816" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ "serde", - "signature", + "signature 1.6.4", ] [[package]] -name = "ed25519-dalek-fiat" -version = "0.1.0" +name = "ed25519-dalek" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97c6ac152eba578c1c53d2cefe8ad02e239e3d6f971b0f1ef3cb54cd66037fa0" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ - "curve25519-dalek-fiat", + "curve25519-dalek", "ed25519", - "rand", + "rand 0.7.3", "serde", "serde_bytes", - "sha2", + "sha2 0.9.9", "zeroize", ] [[package]] name = "either" -version = "1.6.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] -name = "env_logger" -version = "0.9.0" +name = "elliptic-curve" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", ] [[package]] name = "fastrand" -version = "1.7.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ - "instant", + "bitvec", + "byteorder", + "ff_derive", + "rand_core 0.6.4", + "subtle", ] [[package]] -name = "fiat-crypto" -version = "0.1.11" +name = "ff_derive" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2176874104231d65f2dd4d0c2e027e78614170d356c8ccfa826aaef103c8fe89" +checksum = "e9f54704be45ed286151c5e11531316eaef5b8f5af7d597b806fdb8af108d84a" +dependencies = [ + "addchain", + "cfg-if", + "num-bigint 0.3.3", + "num-integer", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] [[package]] name = "fnv" @@ -610,46 +1067,57 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.0.1" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ - "matches", "percent-encoding", ] [[package]] name = "funty" -version = "1.1.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "generic-array" -version = "0.14.5" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", ] [[package]] name = "getrandom" -version = "0.2.4" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] name = "ghash" -version = "0.3.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" dependencies = [ "opaque-debug", "polyval", @@ -657,15 +1125,43 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.0" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "group" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand 0.8.5", + "rand_core 0.6.4", + "rand_xorshift", + "subtle", +] [[package]] name = "half" -version = "1.8.2" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" + +[[package]] +name = "hashbrown" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hermit-abi" @@ -676,11 +1172,20 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] [[package]] name = "hkdf" @@ -688,8 +1193,18 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f" dependencies = [ - "digest", - "hmac", + "digest 0.9.0", + "hmac 0.10.1", +] + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac 0.8.0", + "digest 0.9.0", ] [[package]] @@ -698,141 +1213,196 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" dependencies = [ - "crypto-mac", - "digest", + "crypto-mac 0.10.1", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", ] [[package]] -name = "humantime" -version = "2.1.0" +name = "hmac-drbg" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array", + "hmac 0.8.1", +] [[package]] name = "idna" -version = "0.2.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] [[package]] -name = "instant" -version = "0.1.12" +name = "indexmap" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ - "cfg-if", + "equivalent", + "hashbrown 0.14.5", ] [[package]] -name = "itertools" -version = "0.10.3" +name = "inout" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "either", + "generic-array", ] [[package]] -name = "itoa" -version = "0.4.8" +name = "itertools" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] [[package]] name = "itoa" -version = "1.0.1" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.56" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] [[package]] name = "keccak" -version = "0.1.0" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin 0.9.8", +] [[package]] -name = "lazycell" -version = "1.3.0" +name = "libc" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] -name = "libc" -version = "0.2.118" +name = "libm" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e509672465a0504304aa87f9f176f2b2b716ed8fb105ebe5c02dc6dce96a94" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] -name = "libloading" -version = "0.7.3" +name = "libsecp256k1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" dependencies = [ - "cfg-if", - "winapi", + "arrayref", + "base64", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.8.5", + "serde", + "sha2 0.9.9", + "typenum", ] [[package]] -name = "log" -version = "0.4.14" +name = "libsecp256k1-core" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" dependencies = [ - "cfg-if", + "crunchy", + "digest 0.9.0", + "subtle", ] [[package]] -name = "matches" -version = "0.1.9" +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] [[package]] -name = "memchr" -version = "2.4.1" +name = "linux-raw-sys" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] -name = "memoffset" -version = "0.6.5" +name = "log" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] -name = "minimal-lexical" -version = "0.2.1" +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "merlin" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.6.4", + "zeroize", +] [[package]] -name = "mirai-annotations" -version = "1.12.0" +name = "more-asserts" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" +checksum = "1fafa6961cabd9c63bcd77a45d7e3b7f3b552b70417831fb0f56db717e72407e" [[package]] name = "multiaddr" @@ -882,64 +1452,110 @@ checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" dependencies = [ "proc-macro-crate", "proc-macro-error", - "proc-macro2 1.0.36", - "quote 1.0.15", - "syn 1.0.86", + "proc-macro2", + "quote", + "syn 1.0.109", "synstructure", ] [[package]] -name = "nom" -version = "7.1.0" +name = "neptune" +version = "13.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" +checksum = "06626c9ac04c894e9a23d061ba1309f28506cdc5fe64156d28a15fb57fc8e438" dependencies = [ - "memchr", - "minimal-lexical", - "version_check", + "bellpepper", + "bellpepper-core", + "blake2s_simd", + "blstrs", + "byteorder", + "ff", + "generic-array", + "log", + "pasta_curves", + "serde", + "trait-set", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "num-bigint" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", + "rand 0.7.3", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "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.14" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] name = "num_cpus" -version = "1.13.1" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", ] [[package]] name = "once_cell" -version = "1.9.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" -version = "11.1.3" +version = "11.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openrpc-schema" version = "0.1.0" -source = "git+https://github.com/starcoinorg/openrpc-rs?rev=f8ab047e30927cdf2f605b61a219c975d6c4f666#f8ab047e30927cdf2f605b61a219c975d6c4f666" +source = "git+https://github.com/starcoinorg/openrpc-rs?rev=1f2f7d3495e3bd3ef3b6fcf7c4e0602cad090d5e#1f2f7d3495e3bd3ef3b6fcf7c4e0602cad090d5e" dependencies = [ "schemars", "serde", @@ -947,22 +1563,85 @@ dependencies = [ ] [[package]] -name = "peeking_take_while" -version = "0.1.2" +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.8", +] + +[[package]] +name = "pairing" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" +dependencies = [ + "group", +] + +[[package]] +name = "pasta_curves" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e57598f73cc7e1b2ac63c79c517b31a0877cd7c402cdcaa311b5208de7a095" +dependencies = [ + "blake2b_simd", + "ff", + "group", + "hex", + "lazy_static", + "rand 0.8.5", + "serde", + "static_assertions", + "subtle", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] [[package]] name = "percent-encoding" -version = "2.1.0" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pkcs8" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] [[package]] name = "plotters" -version = "0.3.1" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" dependencies = [ "num-traits", "plotters-backend", @@ -973,35 +1652,58 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.2" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" [[package]] name = "plotters-svg" -version = "0.3.1" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" dependencies = [ "plotters-backend", ] [[package]] name = "polyval" -version = "0.4.5" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ - "cpuid-bool", + "cfg-if", + "cpufeatures", "opaque-debug", "universal-hash", ] +[[package]] +name = "poseidon-ark" +version = "0.0.1" +source = "git+https://github.com/arnaucube/poseidon-ark.git?rev=6d2487aa1308d9d3860a2b724c485d73095c1c68#6d2487aa1308d9d3860a2b724c485d73095c1c68" +dependencies = [ + "ark-bn254", + "ark-ff", + "ark-std", +] + [[package]] name = "ppv-lite86" -version = "0.2.16" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "primeorder" +version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] [[package]] name = "proc-macro-crate" @@ -1010,7 +1712,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" dependencies = [ "thiserror", - "toml", + "toml 0.5.11", ] [[package]] @@ -1020,9 +1722,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.36", - "quote 1.0.15", - "syn 1.0.86", + "proc-macro2", + "quote", + "syn 1.0.109", "version_check", ] @@ -1032,58 +1734,49 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2", + "quote", "version_check", ] [[package]] name = "proc-macro2" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -dependencies = [ - "unicode-xid 0.1.0", -] - -[[package]] -name = "proc-macro2" -version = "1.0.36" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ - "unicode-xid 0.2.2", + "unicode-ident", ] [[package]] name = "proptest" -version = "1.0.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0d9cc07f18492d879586c92b485def06bc850da3118075cd45d50e9c95b0e5" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" dependencies = [ "bit-set", - "bitflags", - "byteorder", + "bit-vec", + "bitflags 2.6.0", "lazy_static", "num-traits", - "quick-error 2.0.1", - "rand", - "rand_chacha", + "rand 0.8.5", + "rand_chacha 0.3.1", "rand_xorshift", "regex-syntax", "rusty-fork", "tempfile", + "unarray", ] [[package]] name = "proptest-derive" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90b46295382dc76166cb7cf2bb4a97952464e4b7ed5a43e6cd34e1fec3349ddc" +checksum = "9cf16337405ca084e9c78985114633b6827711d22b9e6ef6c6c0d665eb3f0b6e" dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "syn 0.15.44", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -1092,46 +1785,53 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - [[package]] name = "quote" -version = "0.6.13" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ - "proc-macro2 0.4.30", + "proc-macro2", ] [[package]] -name = "quote" -version = "1.0.15" +name = "radium" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" -dependencies = [ - "proc-macro2 1.0.36", -] +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] -name = "radium" -version = "0.5.3" +name = "rand" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] [[package]] name = "rand" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", - "rand_hc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", ] [[package]] @@ -1141,25 +1841,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", ] [[package]] name = "rand_core" -version = "0.6.3" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", ] [[package]] name = "rand_hc" -version = "0.3.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ - "rand_core", + "rand_core 0.5.1", ] [[package]] @@ -1168,99 +1877,103 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ - "rand_core", + "rand_core 0.6.4", ] [[package]] name = "rayon" -version = "1.5.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ - "autocfg", - "crossbeam-deque", "either", "rayon-core", ] [[package]] name = "rayon-core" -version = "1.9.1" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "lazy_static", - "num_cpus", -] - -[[package]] -name = "redox_syscall" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" -dependencies = [ - "bitflags", ] [[package]] name = "regex" -version = "1.5.4" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", + "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.1.10" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "rfc6979" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ - "winapi", + "hmac 0.12.1", + "subtle", ] [[package]] -name = "ripemd160" -version = "0.9.1" +name = "ring" +version = "0.16.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" dependencies = [ - "block-buffer", - "digest", - "opaque-debug", + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted", + "web-sys", + "winapi", ] [[package]] -name = "rustc-hash" -version = "1.1.0" +name = "rustc_version" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] [[package]] -name = "rustc_version" -version = "0.4.0" +name = "rustix" +version = "0.38.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36" dependencies = [ - "semver", + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1270,16 +1983,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" dependencies = [ "fnv", - "quick-error 1.2.3", + "quick-error", "tempfile", "wait-timeout", ] [[package]] name = "ryu" -version = "1.0.9" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -1293,8 +2006,9 @@ dependencies = [ [[package]] name = "schemars" version = "0.8.8" -source = "git+https://github.com/starcoinorg/schemars?rev=9b3705780b8fe9c8676ff82919869ba7405b1062#9b3705780b8fe9c8676ff82919869ba7405b1062" +source = "git+https://github.com/starcoinorg/schemars?rev=a64c6ddf7ca4796e090208b1476de2e53772042f#a64c6ddf7ca4796e090208b1476de2e53772042f" dependencies = [ + "bytes", "dyn-clone", "multiaddr", "schemars_derive", @@ -1305,31 +2019,39 @@ dependencies = [ [[package]] name = "schemars_derive" version = "0.8.8" -source = "git+https://github.com/starcoinorg/schemars?rev=9b3705780b8fe9c8676ff82919869ba7405b1062#9b3705780b8fe9c8676ff82919869ba7405b1062" +source = "git+https://github.com/starcoinorg/schemars?rev=a64c6ddf7ca4796e090208b1476de2e53772042f#a64c6ddf7ca4796e090208b1476de2e53772042f" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2", + "quote", "serde_derive_internals", - "syn 1.0.86", + "syn 1.0.109", ] [[package]] -name = "scopeguard" -version = "1.1.0" +name = "sec1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] [[package]] name = "semver" -version = "1.0.5" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0486718e92ec9a68fbed73bb5ef687d71103b142595b406835649bebd33f72c7" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.136" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] @@ -1344,21 +2066,11 @@ dependencies = [ "thiserror", ] -[[package]] -name = "serde-name" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016d6b17527eac0f2b0227fa36b1547fa98e5f856eecc5b3703c5024db4d7277" -dependencies = [ - "serde", - "thiserror", -] - [[package]] name = "serde_bytes" -version = "0.11.5" +version = "0.11.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" dependencies = [ "serde", ] @@ -1375,13 +2087,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.136" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", - "syn 1.0.86", + "proc-macro2", + "quote", + "syn 2.0.77", ] [[package]] @@ -1390,73 +2102,127 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dbab34ca63057a1f15280bdf3c39f2b1eb1b54c17e98360e511637aef7418c6" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", - "syn 1.0.86", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] name = "serde_json" -version = "1.0.79" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ - "itoa 1.0.1", + "indexmap", + "itoa", + "memchr", "ryu", "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +dependencies = [ + "serde", +] + [[package]] name = "sha2" version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ - "block-buffer", + "block-buffer 0.9.0", "cfg-if", "cpufeatures", - "digest", + "digest 0.9.0", "opaque-debug", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + [[package]] name = "sha3" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" dependencies = [ - "block-buffer", - "digest", + "block-buffer 0.9.0", + "digest 0.9.0", "keccak", "opaque-debug", ] [[package]] name = "shlex" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signature" -version = "1.5.0" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f054c6c1a6e95179d6f23ed974060dcefb2d9388bb7256900badad682c499de4" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] [[package]] name = "starcoin-crypto" version = "1.10.0-rc.2" dependencies = [ "anyhow", + "aptos-crypto", + "aptos-crypto-derive", "bcs", - "diem-crypto", - "diem-crypto-derive", "hex", "once_cell", - "rand", - "rand_core", + "rand 0.7.3", + "rand_core 0.5.1", "serde", - "serde-name 0.2.0", + "serde-name", "serde_bytes", "starcoin-crypto-macro", ] @@ -1465,9 +2231,9 @@ dependencies = [ name = "starcoin-crypto-macro" version = "1.10.0-rc.2" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", - "syn 1.0.86", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -1476,38 +2242,38 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - [[package]] name = "subtle" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +[[package]] +name = "subtle-ng" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" + [[package]] name = "syn" -version = "0.15.44" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "unicode-xid 0.1.0", + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] name = "syn" -version = "1.0.86" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", - "unicode-xid 0.2.2", + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] @@ -1516,10 +2282,10 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", - "syn 1.0.86", - "unicode-xid 0.2.2", + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", ] [[package]] @@ -1530,23 +2296,22 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.3.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi", + "once_cell", + "rustix", + "windows-sys 0.59.0", ] [[package]] name = "termcolor" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] @@ -1562,22 +2327,31 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "threadpool" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", - "syn 1.0.86", + "num_cpus", ] [[package]] @@ -1601,120 +2375,205 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.5.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] [[package]] name = "toml" -version = "0.5.8" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ + "indexmap", "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "tracing-core", +] + +[[package]] +name = "trait-set" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b79e2e9c9ab44c6d7c20d5976961b47e8f49ac199154daa514b77cd1ab536625" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] name = "trybuild" -version = "1.0.56" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d60539445867cdd9680b2bfe2d0428f1814b7d5c9652f09d8d3eae9d19308db" +checksum = "207aa50d36c4be8d8c6ea829478be44a372c6a77669937bb39c698e52f1491e8" dependencies = [ "glob", - "once_cell", "serde", + "serde_derive", "serde_json", "termcolor", - "toml", + "toml 0.8.19", ] [[package]] name = "typenum" -version = "1.15.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-bidi" -version = "0.3.7" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.19" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "unicode-width" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" - -[[package]] -name = "unicode-xid" -version = "0.1.0" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unicode-xid" -version = "0.2.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" [[package]] name = "universal-hash" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ - "generic-array", + "crypto-common", "subtle", ] [[package]] name = "unsigned-varint" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6889a77d49f1f013504cec6bf97a2c730394adedaeb1deb5ea08949a50541105" + +[[package]] +name = "untrusted" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.2.2" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", - "matches", "percent-encoding", ] [[package]] -name = "vec_map" -version = "0.8.2" +name = "valuable" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wait-timeout" @@ -1727,96 +2586,91 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.3.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", - "winapi", "winapi-util", ] [[package]] name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.79" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.79" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", - "lazy_static", "log", - "proc-macro2 1.0.36", - "quote 1.0.15", - "syn 1.0.86", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.77", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.79" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ - "quote 1.0.15", + "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.79" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", - "syn 1.0.86", + "proc-macro2", + "quote", + "syn 2.0.77", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.79" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "web-sys" -version = "0.3.56" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" dependencies = [ "js-sys", "wasm-bindgen", ] -[[package]] -name = "which" -version = "4.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a5a7e487e921cf220206864a94a89b6c6905bfc19f1057fa26a4cb360e5c1d2" -dependencies = [ - "either", - "lazy_static", - "libc", -] - [[package]] name = "winapi" version = "0.3.9" @@ -1835,11 +2689,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "winapi", + "windows-sys 0.59.0", ] [[package]] @@ -1849,39 +2703,152 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "wyz" -version = "0.2.0" +name = "windows-sys" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] [[package]] -name = "x25519-dalek-fiat" -version = "0.1.0" +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[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_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f3519d56987103ef084eba6ddfc3be45b7eaee08f2d60bc8495cdca30362a32" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "x25519-dalek" +version = "1.2.0" +source = "git+https://github.com/aptos-labs/x25519-dalek?branch=zeroize_v1#762a9501668d213daa4a1864fa1f9db22716b661" dependencies = [ - "curve25519-dalek-fiat", - "rand_core", + "curve25519-dalek", + "rand_core 0.5.1", "zeroize", ] +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "zeroize" -version = "1.5.2" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c88870063c39ee00ec285a2f8d6a966e5b6fb2becc4e8dac77ed0d370ed6006" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.3.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81e8f13fef10b63c06356d65d416b070798ddabcadc10d3ece0c5be9b3c7eddb" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", - "syn 1.0.86", - "synstructure", + "proc-macro2", + "quote", + "syn 2.0.77", ] diff --git a/Cargo.toml b/Cargo.toml index f203a86..305197b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,82 @@ [workspace] +resolver = "2" members = [ - "crates/diem-crypto", + "crates/aptos-crypto", + "crates/aptos-crypto-derive", "crypto", + "crypto/crypto-macro", ] -default-members = [ - "crypto", -] +default-members = ["crypto"] + +[workspace.dependencies] +aes-gcm = "0.10.3" +anyhow = "1.0.71" +ark-bn254 = "0.4.0" +ark-bls12-381 = "0.4.0" +ark-ec = "0.4.0" +ark-ff = "0.4.0" +ark-groth16 = "0.4.0" +ark-serialize = "0.4.0" +ark-std = { version = "0.4.0", features = ["getrandom"] } +base64 = "0.13.0" +bcs = { git = "https://github.com/aptos-labs/bcs.git", rev = "d31fab9d81748e2594be5cd5cdf845786a30562d" } +blake2 = "0.10.4" +blst = "0.3.11" +blake2-rfc = "0.2.18" +bulletproofs = { version = "4.0.0" } +bytes = { version = "1.4.0", features = ["serde"] } +byteorder = "1.4.3" +bitvec = "1.0.1" +criterion = "0.3.5" +curve25519-dalek = "3" +curve25519-dalek-ng = "4" +digest = "0.9.0" +ed25519-dalek = { version = "1.0.1", features = ["std", "serde"] } +ff = { version = "0.13", features = ["derive"] } +hex = { version = "0.4.3", features = ["serde"] } +hkdf = "0.10.0" +libsecp256k1 = "0.7.0" +merlin = "3" +more-asserts = "0.3.0" +neptune = { version = "13.0.0", default_features = false } +num-bigint = { version = "0.3.2", features = ["rand"] } +num-integer = "0.1.42" +once_cell = "1.10.0" +openrpc-schema = { git = "https://github.com/starcoinorg/openrpc-rs", rev = "1f2f7d3495e3bd3ef3b6fcf7c4e0602cad090d5e" } +p256 = { version = "0.13.2" } +poseidon-ark = { git = "https://github.com/arnaucube/poseidon-ark.git", rev = "6d2487aa1308d9d3860a2b724c485d73095c1c68" } +proc-macro2 = "1.0.18" +proptest = "1.4.0" +proptest-derive = "0.4.0" +quote = "1.0.6" +rand = "0.7.3" +rand_core = "0.5.1" +ring = { version = "0.16.20", features = ["std"] } +serde = "1.0.193" +serde_json = { version = "1.0.81", features = [ + "preserve_order", + "arbitrary_precision", +] } # Note: arbitrary_precision is required to parse u256 in JSON +trybuild = "1.0.80" +serde-name = "0.1.1" +serde_bytes = "0.11.6" +sha2 = "0.9.3" +sha2_0_10_6 = { package = "sha2", version = "0.10.6" } +sha3 = "0.9.1" +signature = "2.1.0" +static_assertions = "1.1.0" +thiserror = "1.0.37" +tiny-keccak = { version = "2.0.2", features = ["sha3"] } +typenum = "1.17.0" +x25519-dalek = "1.2.0" +starcoin-crypto = { path = "crypto" } +starcoin-crypto-macro = { path = "crypto/crypto-macro" } +aptos-crypto = { path = "crates/aptos-crypto", features = ["fuzzing"] } +aptos-crypto-derive = { path = "crates/aptos-crypto-derive" } +syn = { version = "1.0.30", features = ["derive"] } + [profile.dev] panic = "unwind" @@ -16,4 +85,7 @@ panic = "unwind" debug = false panic = "unwind" debug-assertions = false -codegen-units = 1 \ No newline at end of file +codegen-units = 1 + +[patch.crates-io] +x25519-dalek = { git = "https://github.com/aptos-labs/x25519-dalek", branch = "zeroize_v1" } diff --git a/crates/aptos-crypto-derive/Cargo.toml b/crates/aptos-crypto-derive/Cargo.toml new file mode 100644 index 0000000..2cb97a0 --- /dev/null +++ b/crates/aptos-crypto-derive/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "aptos-crypto-derive" +description = "Custom derives for `aptos-crypto`" +version = "0.0.3" + +authors = ["Aptos Labs "] +edition = "2021" +homepage = "https://aptoslabs.com" +license = "Apache-2.0" +publish = false +repository = "https://github.com/aptos-labs/aptos-core" +rust-version = "1.75.0" + + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0.38" +quote = "1.0.18" +syn = { version = "1.0.92", features = ["derive", "extra-traits"] } + +[dev-dependencies] +anyhow = "1.0.71" diff --git a/crates/aptos-crypto-derive/src/hasher.rs b/crates/aptos-crypto-derive/src/hasher.rs new file mode 100644 index 0000000..21d5351 --- /dev/null +++ b/crates/aptos-crypto-derive/src/hasher.rs @@ -0,0 +1,21 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +/// Converts a camel-case string to snake-case +pub fn camel_to_snake(text: &str) -> String { + let mut out = String::with_capacity(text.len()); + let mut first = true; + text.chars().for_each(|c| { + if !first && c.is_uppercase() { + out.push('_'); + out.extend(c.to_lowercase()); + } else if first { + first = false; + out.extend(c.to_lowercase()); + } else { + out.push(c); + } + }); + out +} diff --git a/crates/aptos-crypto-derive/src/lib.rs b/crates/aptos-crypto-derive/src/lib.rs new file mode 100644 index 0000000..6b00f9c --- /dev/null +++ b/crates/aptos-crypto-derive/src/lib.rs @@ -0,0 +1,477 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#![forbid(unsafe_code)] + +//! # Derive macros for crypto operations +//! This crate contains four types of derive macros: +//! +//! - the `SilentDebug` and SilentDisplay macros are meant to be used on private key types, and +//! elide their input for confidentiality. +//! - the `Deref` macro helps derive the canonical instances on new types. +//! - the derive macros for `aptos_crypto::traits`, namely `ValidCryptoMaterial`, `PublicKey`, `PrivateKey`, +//! `VerifyingKey`, `SigningKey` and `Signature` are meant to be derived on simple unions of types +//! implementing these traits. +//! - the derive macro for `aptos_crypto::hash::CryptoHasher`, which defines +//! the domain-separation hasher structures described in `aptos_crypto::hash` +//! (look there for details). This derive macro has for sole difference that it +//! automatically picks a unique salt for you, using the Serde name. For a container `Foo`, +//! this is usually equivalent to: +//! ```ignore +//! define_hasher! { +//! ( +//! FooHasher, +//! FOO_HASHER, +//! b"Foo" +//! ) +//! } +//! ``` +//! +//! # Unions of Signing Traits, in detail +//! +//! Those types typically come into play when you need to accept several +//! alternatives at runtime for several signature and verification schemes +//! (ex: BLS or EdDSA, see below). In this case, it is possible to declare +//! a triplet of enum types that each describe a 'sum type' (coproduct) of these +//! alternatives. This happens to be a signing scheme itself (it has +//! canonical signature, signing & verifying key types, and verifies all +//! expected properties by trivial dispatch). +//! +//! The macros below let you define this type of union trivially under two conditions: +//! - that the variant tags for the enum have the same name, i.e. if the BLS variant for the +//! `SignatureUnion` is `SignatureUnion::BLS(BLS12381Signature)`, then the variant of the +//! `PublicKeyUnion` for BLS must also be `PublicKeyUnion::BLS`, +//! - that you specify the associated types `PrivateKeyType`, `SignatureType` and `PublicKeyType` +//! for each of the three unions. `PrivateKeyType` provides the value for the +//! `VerifyingKeyMaterial` and `PublicKeyMaterial` associated types, `PublicKeyType` provides the +//! valid for the `SigningKeyMaterial` and `PrivateKeyMaterial` associated types and +//! `SignatureType` provides the value for the `SignatureMaterial` associated type. +//! +//! ## Example +//! +//! ```ignore +//! # #[macro_use] extern crate crypto-derive; +//! use aptos_crypto::{ +//! hash::HashValue, +//! bls12381::{BLS12381PrivateKey, BLS12381PublicKey, BLS12381Signature}, +//! ed25519::{Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature}, +//! }; +//! use aptos_crypto_derive::{ +//! SilentDebug, PrivateKey, PublicKey, Signature, SigningKey, ValidCryptoMaterial, VerifyingKey, +//! }; +//! +//! /// Generic public key enum +//! #[derive( +//! Debug, Clone, PartialEq, Eq, Hash, ValidCryptoMaterial, PublicKey, VerifyingKey, +//! )] +//! #[PrivateKeyType = "GenericPrivateKey"] +//! #[SignatureType = "GenericSignature"] +//! pub enum GenericPublicKey { +//! /// Ed25519 public key +//! Ed(Ed25519PublicKey), +//! /// BLS12-381 public key +//! BLS(BLS12381PublicKey), +//! } +//! /// Generic private key enum +//! #[derive(SilentDebug, ValidCryptoMaterial, PrivateKey, SigningKey)] +//! #[PublicKeyType = "GenericPublicKey"] +//! #[SignatureType = "GenericSignature"] +//! pub enum GenericPrivateKey { +//! /// Ed25519 private key +//! Ed(Ed25519PrivateKey), +//! /// BLS12-381 private key +//! BLS(BLS12381PrivateKey), +//! } +//! /// Generic signature enum +//! #[allow(clippy::large_enum_variant)] +//! #[derive(Clone, Debug, PartialEq, Eq, Hash, Signature)] +//! #[PrivateKeyType = "GenericPrivateKey"] +//! #[PublicKeyType = "GenericPublicKey"] +//! pub enum GenericSignature { +//! /// Ed25519 signature +//! Ed(Ed25519Signature), +//! /// BLS12-381 signature +//! BLS(BLS12381Signature), +//! } +//! ``` + +#![forbid(unsafe_code)] + +extern crate proc_macro; + +mod hasher; +mod unions; + +use hasher::camel_to_snake; +use proc_macro::TokenStream; +use proc_macro2::Span; +use quote::quote; +use std::iter::FromIterator; +use syn::{parse_macro_input, parse_quote, Data, DeriveInput, Ident}; +use unions::*; + +#[proc_macro_derive(SilentDisplay)] +pub fn silent_display(source: TokenStream) -> TokenStream { + let ast: DeriveInput = syn::parse(source).expect("Incorrect macro input"); + let name = &ast.ident; + let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); + + let gen = quote! { + // In order to ensure that secrets are never leaked, Display is elided + impl #impl_generics ::std::fmt::Display for #name #ty_generics #where_clause { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "", stringify!(#name)) + } + } + }; + gen.into() +} + +#[proc_macro_derive(SilentDebug)] +pub fn silent_debug(source: TokenStream) -> TokenStream { + let ast: DeriveInput = syn::parse(source).expect("Incorrect macro input"); + let name = &ast.ident; + let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); + + let gen = quote! { + // In order to ensure that secrets are never leaked, Debug is elided + impl #impl_generics ::std::fmt::Debug for #name #ty_generics #where_clause { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "", stringify!(#name)) + } + } + }; + gen.into() +} + +#[proc_macro_attribute] +pub fn key_name(_attr: TokenStream, item: TokenStream) -> TokenStream { + item +} + +/// Deserialize from a human readable format where applicable +#[proc_macro_derive(DeserializeKey)] +pub fn deserialize_key(source: TokenStream) -> TokenStream { + let ast: DeriveInput = syn::parse(source).expect("Incorrect macro input"); + let name = &ast.ident; + let name_string = find_key_name(&ast, name.to_string()); + let gen = quote! { + impl<'de> ::serde::Deserialize<'de> for #name { + fn deserialize(deserializer: D) -> std::result::Result + where + D: ::serde::Deserializer<'de>, + { + if deserializer.is_human_readable() { + let encoded_key = ::deserialize(deserializer)?; + ValidCryptoMaterialStringExt::from_encoded_string(encoded_key.as_str()) + .map_err(::custom) + } else { + // In order to preserve the Serde data model and help analysis tools, + // make sure to wrap our value in a container with the same name + // as the original type. + #[derive(::serde::Deserialize, Debug)] + #[serde(rename = #name_string)] + struct Value<'a>(&'a [u8]); + + let value = Value::deserialize(deserializer)?; + #name::try_from(value.0).map_err(|s| { + ::custom(format!("{} with {}", s, #name_string)) + }) + } + } + } + }; + gen.into() +} + +/// Serialize into a human readable format where applicable +#[proc_macro_derive(SerializeKey)] +pub fn serialize_key(source: TokenStream) -> TokenStream { + let ast: DeriveInput = syn::parse(source).expect("Incorrect macro input"); + let name = &ast.ident; + let name_string = find_key_name(&ast, name.to_string()); + let gen = quote! { + impl ::serde::Serialize for #name { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: ::serde::Serializer, + { + if serializer.is_human_readable() { + self.to_encoded_string() + .map_err(::custom) + .and_then(|str| serializer.serialize_str(&str[..])) + } else { + // See comment in deserialize_key. + serializer.serialize_newtype_struct( + #name_string, + serde_bytes::Bytes::new(&ValidCryptoMaterial::to_bytes(self).as_slice()), + ) + } + } + } + }; + gen.into() +} + +fn find_key_name(ast: &DeriveInput, name: String) -> String { + for attr in ast.attrs.iter() { + let ident = attr.path.get_ident(); + let name = ident.map(|ident| ident.to_string()); + if name == Some("key_name".into()) { + let list = attr.parse_meta().unwrap(); + let meta = match list { + syn::Meta::List(meta) => meta, + _ => panic!("Expected a List"), + }; + let token = match meta.nested.first().expect("Missing value") { + syn::NestedMeta::Lit(syn::Lit::Str(token)) => token, + _ => panic!("Expected LitStr"), + }; + return token.token().to_string().trim_matches('\"').to_string(); + } + } + + name +} + +#[proc_macro_derive(Deref)] +pub fn derive_deref(input: TokenStream) -> TokenStream { + let item = syn::parse(input).expect("Incorrect macro input"); + let (field_ty, field_access) = parse_newtype_fields(&item); + + let name = &item.ident; + let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); + + quote!( + impl #impl_generics ::std::ops::Deref for #name #ty_generics + #where_clause + { + type Target = #field_ty; + + fn deref(&self) -> &Self::Target { + #field_access + } + } + ) + .into() +} + +#[proc_macro_derive(ValidCryptoMaterial)] +pub fn derive_enum_valid_crypto_material(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + + let name = &ast.ident; + match ast.data { + Data::Enum(ref variants) => impl_enum_valid_crypto_material(name, variants), + Data::Struct(_) | Data::Union(_) => { + panic!("#[derive(ValidCryptoMaterial)] is only defined for enums") + } + } +} + +#[proc_macro_derive(PublicKey, attributes(PrivateKeyType))] +pub fn derive_enum_publickey(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + + let name = &ast.ident; + let private_key_type = get_type_from_attrs(&ast.attrs, "PrivateKeyType").unwrap(); + match ast.data { + Data::Enum(ref variants) => impl_enum_publickey(name, private_key_type, variants), + Data::Struct(_) | Data::Union(_) => { + panic!("#[derive(PublicKey)] is only defined for enums") + } + } +} + +#[proc_macro_derive(PrivateKey, attributes(PublicKeyType))] +pub fn derive_enum_privatekey(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + + let name = &ast.ident; + let public_key_type = get_type_from_attrs(&ast.attrs, "PublicKeyType").unwrap(); + match ast.data { + Data::Enum(ref variants) => impl_enum_privatekey(name, public_key_type, variants), + Data::Struct(_) | Data::Union(_) => { + panic!("#[derive(PrivateKey)] is only defined for enums") + } + } +} + +#[proc_macro_derive(VerifyingKey, attributes(PrivateKeyType, SignatureType))] +pub fn derive_enum_verifyingkey(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + + let name = &ast.ident; + let private_key_type = get_type_from_attrs(&ast.attrs, "PrivateKeyType").unwrap(); + let signature_type = get_type_from_attrs(&ast.attrs, "SignatureType").unwrap(); + match ast.data { + Data::Enum(ref variants) => { + impl_enum_verifyingkey(name, private_key_type, signature_type, variants) + } + Data::Struct(_) | Data::Union(_) => { + panic!("#[derive(PrivateKey)] is only defined for enums") + } + } +} + +#[proc_macro_derive(SigningKey, attributes(PublicKeyType, SignatureType))] +pub fn derive_enum_signingkey(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + + let name = &ast.ident; + let public_key_type = get_type_from_attrs(&ast.attrs, "PublicKeyType").unwrap(); + let signature_type = get_type_from_attrs(&ast.attrs, "SignatureType").unwrap(); + match ast.data { + Data::Enum(ref variants) => { + impl_enum_signingkey(name, public_key_type, signature_type, variants) + } + Data::Struct(_) | Data::Union(_) => { + panic!("#[derive(PrivateKey)] is only defined for enums") + } + } +} + +#[proc_macro_derive(Signature, attributes(PublicKeyType, PrivateKeyType))] +pub fn derive_enum_signature(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + + let name = &ast.ident; + let public_key_type = get_type_from_attrs(&ast.attrs, "PublicKeyType").unwrap(); + let private_key_type = get_type_from_attrs(&ast.attrs, "PrivateKeyType").unwrap(); + match ast.data { + Data::Enum(ref variants) => { + impl_enum_signature(name, public_key_type, private_key_type, variants) + } + Data::Struct(_) | Data::Union(_) => { + panic!("#[derive(PrivateKey)] is only defined for enums") + } + } +} + +// There is a unit test for this logic in the crypto crate, at +// aptos_crypto::unit_tests::cryptohasher — you may have to modify it if you +// edit the below. +#[proc_macro_derive(CryptoHasher)] +pub fn hasher_dispatch(input: TokenStream) -> TokenStream { + let item = parse_macro_input!(input as DeriveInput); + let hasher_name = Ident::new( + &format!("{}Hasher", &item.ident.to_string()), + Span::call_site(), + ); + let snake_name = camel_to_snake(&item.ident.to_string()); + let static_seed_name = Ident::new( + &format!("{}_SEED", snake_name.to_uppercase()), + Span::call_site(), + ); + + let static_hasher_name = Ident::new( + &format!("{}_HASHER", snake_name.to_uppercase()), + Span::call_site(), + ); + let type_name = &item.ident; + let param = if item.generics.params.is_empty() { + quote!() + } else { + let args = proc_macro2::TokenStream::from_iter( + std::iter::repeat(quote!((),)).take(item.generics.params.len()), + ); + quote!(<#args>) + }; + + let out = quote!( + /// Cryptographic hasher for an BCS-serializable #item + #[derive(Clone)] + pub struct #hasher_name(aptos_crypto::hash::DefaultHasher); + + static #static_seed_name: aptos_crypto::_once_cell::sync::OnceCell<[u8; 32]> = aptos_crypto::_once_cell::sync::OnceCell::new(); + + impl #hasher_name { + fn new() -> Self { + let name = aptos_crypto::_serde_name::trace_name::<#type_name #param>() + .expect("The `CryptoHasher` macro only applies to structs and enums"); + #hasher_name( + aptos_crypto::hash::DefaultHasher::new(&name.as_bytes())) + } + } + + static #static_hasher_name: aptos_crypto::_once_cell::sync::Lazy<#hasher_name> = + aptos_crypto::_once_cell::sync::Lazy::new(|| #hasher_name::new()); + + + impl std::default::Default for #hasher_name + { + fn default() -> Self { + #static_hasher_name.clone() + } + } + + impl aptos_crypto::hash::CryptoHasher for #hasher_name { + fn seed() -> &'static [u8; 32] { + #static_seed_name.get_or_init(|| { + let name = aptos_crypto::_serde_name::trace_name::<#type_name #param>() + .expect("The `CryptoHasher` macro only applies to structs and enums.").as_bytes(); + aptos_crypto::hash::DefaultHasher::prefixed_hash(&name) + }) + } + + fn update(&mut self, bytes: &[u8]) { + self.0.update(bytes); + } + + fn finish(self) -> aptos_crypto::hash::HashValue { + self.0.finish() + } + } + + impl std::io::Write for #hasher_name { + fn write(&mut self, bytes: &[u8]) -> std::io::Result { + use aptos_crypto::hash::CryptoHasher; + + self.0.update(bytes); + Ok(bytes.len()) + } + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } + } + + ); + out.into() +} + +#[proc_macro_derive(BCSCryptoHash)] +pub fn bcs_crypto_hash_dispatch(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + let name = &ast.ident; + let hasher_name = Ident::new(&format!("{}Hasher", &name.to_string()), Span::call_site()); + let error_msg = syn::LitStr::new( + &format!("BCS serialization of {} should not fail", name), + Span::call_site(), + ); + let generics = add_trait_bounds(ast.generics); + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + let out = quote!( + impl #impl_generics aptos_crypto::hash::CryptoHash for #name #ty_generics #where_clause { + type Hasher = #hasher_name; + + fn hash(&self) -> aptos_crypto::hash::HashValue { + use aptos_crypto::hash::CryptoHasher; + + let mut state = Self::Hasher::default(); + bcs::serialize_into(&mut state, &self).expect(#error_msg); + state.finish() + } + } + ); + out.into() +} + +fn add_trait_bounds(mut generics: syn::Generics) -> syn::Generics { + for param in generics.params.iter_mut() { + if let syn::GenericParam::Type(type_param) = param { + type_param.bounds.push(parse_quote!(Serialize)); + } + } + generics +} diff --git a/crates/aptos-crypto-derive/src/unions.rs b/crates/aptos-crypto-derive/src/unions.rs new file mode 100644 index 0000000..9862a61 --- /dev/null +++ b/crates/aptos-crypto-derive/src/unions.rs @@ -0,0 +1,309 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use proc_macro::TokenStream; +use quote::quote; +use syn::{DataEnum, Ident}; + +pub fn parse_newtype_fields(item: &syn::DeriveInput) -> (syn::Type, proc_macro2::TokenStream) { + let fields = match item.data { + syn::Data::Struct(ref body) => body.fields.iter().collect::>(), + _ => vec![], + }; + + let field_ty = match fields.len() { + 1 => Some(fields[0].ty.clone()), + _ => None, + }; + let field_ty = field_ty.expect("#[derive(Deref)] can only be used on structs with one field."); + + let field_name = match fields[0].ident { + Some(ref ident) => quote!(#ident), + None => quote!(0), + }; + + match field_ty { + syn::Type::Reference(syn::TypeReference { elem, .. }) => (*elem, quote!(self.#field_name)), + x => (x, quote!(&self.#field_name)), + } +} + +pub fn impl_enum_tryfrom(name: &Ident, variants: &DataEnum) -> proc_macro2::TokenStream { + // the TryFrom dispatch + let mut try_iter = variants.variants.iter(); + let first_variant = try_iter + .next() + .expect("#[derive(ValidCryptoMaterial] requires a non-empty enum."); + let first_variant_ident = &first_variant.ident; + let first_variant_arg = &first_variant + .fields + .iter() + .next() + .expect("Unrecognized enum for key types") + .ty; + + let mut try_chain = quote! { + #first_variant_arg::try_from(bytes).and_then(|key| Ok(#name::#first_variant_ident(key))) + }; + for variant in try_iter { + let variant_ident = &variant.ident; + let variant_arg = &variant + .fields + .iter() + .next() + .expect("Unrecognized enum for key types") + .ty; + try_chain.extend(quote!{ + .or_else(|_err| #variant_arg::try_from(bytes).and_then(|key| Ok(#name::#variant_ident(key)))) + }) + } + + quote! { + impl core::convert::TryFrom<&[u8]> for #name { + type Error = aptos_crypto::CryptoMaterialError; + fn try_from(bytes: &[u8]) -> std::result::Result<#name, Self::Error> { + #try_chain + } + } + } +} + +fn match_enum_to_bytes(name: &Ident, variants: &DataEnum) -> proc_macro2::TokenStream { + // the ValidCryptoMaterial dispatch proper + let mut match_arms = quote! {}; + for variant in variants.variants.iter() { + let variant_ident = &variant.ident; + + match_arms.extend(quote! { + #name::#variant_ident(key) => key.to_bytes().to_vec(), + }); + } + match_arms +} + +pub fn impl_enum_valid_crypto_material(name: &Ident, variants: &DataEnum) -> TokenStream { + let mut try_from = impl_enum_tryfrom(name, variants); + + let to_bytes_arms = match_enum_to_bytes(name, variants); + + try_from.extend(quote! { + + impl aptos_crypto::ValidCryptoMaterial for #name { + fn to_bytes(&self) -> Vec { + match self { + #to_bytes_arms + } + } + } + }); + try_from.into() +} + +pub fn get_type_from_attrs(attrs: &[syn::Attribute], attr_name: &str) -> syn::Result { + attrs + .iter() + .find(|attr| attr.path.is_ident(attr_name)) + .map_or_else( + || { + Err(syn::Error::new( + proc_macro2::Span::call_site(), + format!("Could not find attribute {}", attr_name), + )) + }, + |attr| match attr.parse_meta()? { + syn::Meta::NameValue(meta) => { + if let syn::Lit::Str(lit) = &meta.lit { + Ok(lit.clone()) + } else { + Err(syn::Error::new_spanned( + meta, + &format!("Could not parse {} attribute", attr_name)[..], + )) + } + } + bad => Err(syn::Error::new_spanned( + bad, + &format!("Could not parse {} attribute", attr_name)[..], + )), + }, + ) +} + +pub fn impl_enum_publickey( + name: &Ident, + private_key_type: syn::LitStr, + variants: &DataEnum, +) -> TokenStream { + let pkt: syn::Type = private_key_type.parse().unwrap(); + let mut from_match_arms = quote! {}; + for variant in variants.variants.iter() { + let variant_ident = &variant.ident; + + from_match_arms.extend(quote! { + #pkt::#variant_ident(key) => #name::#variant_ident(key.into()), + }); + } + let mut res = quote! { + impl From<&#pkt> for #name { + fn from(public_key: &#pkt) -> Self { + match public_key { + #from_match_arms + } + } + } + }; + res.extend(quote! { + impl aptos_crypto::PublicKey for #name { + type PrivateKeyMaterial = #pkt; + } + }); + res.into() +} + +pub fn impl_enum_privatekey( + name: &Ident, + public_key_type: syn::LitStr, + _variants: &DataEnum, +) -> TokenStream { + let pkt: syn::Type = public_key_type.parse().unwrap(); + let res = quote! { + impl aptos_crypto::PrivateKey for #name { + type PublicKeyMaterial = #pkt; + } + }; + res.into() +} + +pub fn impl_enum_verifyingkey( + name: &Ident, + private_key_type: syn::LitStr, + signature_type: syn::LitStr, + _variants: &DataEnum, +) -> TokenStream { + let pkt: syn::Type = private_key_type.parse().unwrap(); + let st: syn::Type = signature_type.parse().unwrap(); + let res = quote! { + impl aptos_crypto::VerifyingKey for #name { + type SigningKeyMaterial = #pkt; + type SignatureMaterial = #st; + } + impl aptos_crypto::private::Sealed for #name {} + }; + res.into() +} + +pub fn impl_enum_signingkey( + name: &Ident, + public_key_type: syn::LitStr, + signature_type: syn::LitStr, + variants: &DataEnum, +) -> TokenStream { + let pkt: syn::Type = public_key_type.parse().unwrap(); + let st: syn::Type = signature_type.parse().unwrap(); + + let mut match_arms_arbitrary = quote! {}; + let mut match_struct_arms = quote! {}; + for variant in variants.variants.iter() { + let variant_ident = &variant.ident; + + match_struct_arms.extend(quote! { + #name::#variant_ident(key) => Self::SignatureMaterial::#variant_ident(key.sign(message)?), + }); + match_arms_arbitrary.extend(quote! { + #name::#variant_ident(key) => Self::SignatureMaterial::#variant_ident(key.sign_arbitrary_message(message)), + }); + } + let res = quote! { + impl aptos_crypto::SigningKey for #name { + type VerifyingKeyMaterial = #pkt; + type SignatureMaterial = #st; + + fn sign(&self, message: &T) -> Result { + Ok(match self { + #match_struct_arms + }) + } + + #[cfg(test)] + fn sign_arbitrary_message(&self, message: &[u8]) -> Self::SignatureMaterial { + match self { + #match_arms_arbitrary + } + } + } + impl aptos_crypto::private::Sealed for #name {} + }; + res.into() +} + +pub fn impl_enum_signature( + name: &Ident, + public_key_type: syn::LitStr, + private_key_type: syn::LitStr, + variants: &DataEnum, +) -> TokenStream { + let priv_kt: syn::Type = private_key_type.parse().unwrap(); + let pub_kt: syn::Type = public_key_type.parse().unwrap(); + let mut res = impl_enum_tryfrom(name, variants); + let to_bytes_arms = match_enum_to_bytes(name, variants); + + let mut match_arms = quote! {}; + for variant in variants.variants.iter() { + let variant_ident = &variant.ident; + + match_arms.extend(quote! { + (#name::#variant_ident(sig), #pub_kt::#variant_ident(pk)) => { + sig.verify_arbitrary_msg(message, pk) + } + }) + } + + let mut match_struct_arms = quote! {}; + for variant in variants.variants.iter() { + let variant_ident = &variant.ident; + + match_struct_arms.extend(quote! { + (#name::#variant_ident(sig), #pub_kt::#variant_ident(pk)) => { + sig.verify(message, pk) + } + }) + } + + res.extend(quote! { + + impl aptos_crypto::Signature for #name { + type VerifyingKeyMaterial = #pub_kt; + type SigningKeyMaterial = #priv_kt; + + fn verify(&self, message: &T, public_key: &Self::VerifyingKeyMaterial) -> std::result::Result<(), aptos_crypto::error::Error> { + match (self, public_key) { + #match_struct_arms + _ => aptos_crypto::error::bail!( + "provided the wrong alternative in {:?}!", + (self, public_key) + ), + } + } + + fn verify_arbitrary_msg(&self, message: &[u8], public_key: &Self::VerifyingKeyMaterial) -> std::result::Result<(), aptos_crypto::error::Error> { + match (self, public_key) { + #match_arms + _ => aptos_crypto::error::bail!( + "provided the wrong alternative in {:?}!", + (self, public_key) + ), + } + } + + fn to_bytes(&self) -> Vec { + match self { + #to_bytes_arms + } + } + } + + impl aptos_crypto::private::Sealed for #name {} + }); + res.into() +} diff --git a/crates/aptos-crypto/Cargo.toml b/crates/aptos-crypto/Cargo.toml new file mode 100644 index 0000000..174e791 --- /dev/null +++ b/crates/aptos-crypto/Cargo.toml @@ -0,0 +1,124 @@ +[package] +name = "aptos-crypto" +description = "Aptos crypto" +version = "0.0.3" + +authors = ["Aptos Labs "] +edition = "2021" +homepage = "https://aptoslabs.com" +license = "Apache-2.0" +publish = false +repository = "https://github.com/aptos-labs/aptos-core" +rust-version = "1.75.0" + +[dependencies] +aes-gcm = { workspace = true } +anyhow = { workspace = true } +aptos-crypto-derive = { workspace = true } +ark-bn254 = { workspace = true } +ark-ec = { workspace = true } +ark-ff = { workspace = true } +ark-groth16 = { workspace = true } +ark-std = { workspace = true } +base64 = { workspace = true } +bcs = { workspace = true } +blst = { workspace = true } +bulletproofs = { workspace = true } +bytes = { workspace = true } +curve25519-dalek = { workspace = true } +curve25519-dalek-ng = { workspace = true } +digest = { workspace = true } +ed25519-dalek = { workspace = true } +ff = { workspace = true } +hex = { workspace = true } +hkdf = { workspace = true } +libsecp256k1 = { workspace = true } +merlin = { workspace = true } +more-asserts = { workspace = true } +neptune = { workspace = true } +num-bigint = { workspace = true } +num-integer = { workspace = true } +once_cell = { workspace = true } +p256 = { workspace = true } +poseidon-ark = { workspace = true } +proptest = { workspace = true, optional = true } +proptest-derive = { workspace = true, optional = true } +rand = { workspace = true } +rand_core = { workspace = true } +ring = { workspace = true } +serde = { workspace = true } +serde-name = { workspace = true } +serde_bytes = { workspace = true } +sha2 = { workspace = true } +sha2_0_10_6 = { workspace = true } +sha3 = { workspace = true } +signature = { workspace = true } +static_assertions = { workspace = true } +thiserror = { workspace = true } +tiny-keccak = { workspace = true } +typenum = { workspace = true } +x25519-dalek = { workspace = true } +openrpc-schema = { workspace = true } + +[dev-dependencies] +ark-bls12-381 = { workspace = true } +ark-bn254 = { workspace = true } +ark-serialize = { workspace = true } +ark-std = { workspace = true } +bitvec = { workspace = true } +blake2 = { workspace = true } +blake2-rfc = { workspace = true } +byteorder = { workspace = true } +criterion = { workspace = true } +proptest = { workspace = true } +proptest-derive = { workspace = true } +serde_json = { workspace = true } +trybuild = { workspace = true } + + +[features] +default = [] +assert-private-keys-not-cloneable = [] +cloneable-private-keys = [] +fuzzing = ["proptest", "proptest-derive", "cloneable-private-keys"] +testing = [] + +[[bench]] +name = "ark_bls12_381" +harness = false + +[[bench]] +name = "ark_bn254" +harness = false + +[[bench]] +name = "bls12381" +harness = false + +[[bench]] +name = "bulletproofs" +harness = false + +[[bench]] +name = "ed25519" +harness = false + +[[bench]] +name = "hash" +harness = false + +[[bench]] +name = "noise" +harness = false + +[[bench]] +name = "ristretto255" +harness = false + +[[bench]] +name = "secp256k1" +harness = false + +[[bench]] +name = "poseidon_bn254" +harness = false diff --git a/crates/aptos-crypto/README.md b/crates/aptos-crypto/README.md new file mode 100644 index 0000000..57e5aa1 --- /dev/null +++ b/crates/aptos-crypto/README.md @@ -0,0 +1,56 @@ +--- +id: crypto +title: Crypto +custom_edit_url: https://github.com/aptos-labs/aptos-core/edit/main/crypto/crypto/README.md +--- + +The crypto component hosts all the implementations of cryptographic primitives we use in Aptos: hashing, signatures, multisignatures, aggregate signatures, and key derivation/generation. + +To enforce type-safety for signature schemes, we rely on traits from [`traits.rs`](src/traits.rs) and [`validatable.rs`](src/validatable.rs). + +## Overview + +Aptos makes use of several cryptographic algorithms: + +- **SHA-3** as the main hash function + + Standardized in [FIPS 202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) + + Based on the [tiny_keccak](https://docs.rs/tiny-keccak/) crate +- **HKDF: HMAC-based Extract-and-Expand Key Derivation Function** + + Standardized in [RFC 5869](https://tools.ietf.org/html/rfc5869) + + Used to generate keys from a salt (optional), seed, and application-info (optional) +- **Ed25519** signatures and (naive) multisignatures + + Based on the [ed25519-dalek](https://docs.rs/ed25519-dalek/) crate with additional security checks (e.g., for malleability) +- **Boneh-Shacham-Lynn (BLS) multisignatures and aggregate signatures** + + Based on the [blst](https://docs.rs/blst/) crate + + Implemented on top of Barreto-Lynn-Scott BLS12-381 elliptic curves +- The **[Noise Protocol Framework](http://www.noiseprotocol.org/)** + - Used to create authenticated and encrypted communications channels between validators +- **X25519** key exchange + + Based on the [x25519-dalek](https://docs.rs/x25519-dalek) crate + + Used in our implementation of the [Noise Protocol Framework](http://www.noiseprotocol.org/) + +## Traits for safer cryptography implementation + +Before implementing a cryptographic primitive, be sure to read [`traits.rs`](src/traits.rs) and [`validatable.rs`](src/validatable.rs) to understand how to comply with our API as well as **some** of the security concerns involved. + +## How is this module organized? +``` + crypto/src + ├── bls12-381/ # Boneh-Lynn-Shacham (BLS) signatures over (Barreto-Lynn-Scott) BLS12-381 curves + ├── unit_tests/ # Unit tests + ├── lib.rs + ├── ed25519/ # Ed25519 implementation of the signing/verification API in traits.rs + ├── hash.rs # Hash function (SHA-3) + ├── hkdf.rs # HKDF implementation + ├── multi_ed25519.rs # MultiEd25519 implementation of the signing/verification API in traits.rs + ├── noise.rs # Noise Protocol Framework implementation + ├── test_utils.rs + ├── traits.rs # Traits for safer implementations of signature schemes + ├── validatable.rs # Traits for deferring validation of group elements (e.g., public keys, signatures) + └── x25519.rs # X25519 implementation + +``` + +## Changelog + + - This crate historically had support for (a different) BLS12-381, [EC-VRF](https://tools.ietf.org/id/draft-goldbe-vrf-01.html#rfc.section.5), and [SLIP-0010](https://github.com/satoshilabs/slips/blob/master/slip-0010.md), though were removed due to lack of use. The last git revision before the removal is 00301524. diff --git a/crates/aptos-crypto/benches/ark_bls12_381.rs b/crates/aptos-crypto/benches/ark_bls12_381.rs new file mode 100644 index 0000000..77d1feb --- /dev/null +++ b/crates/aptos-crypto/benches/ark_bls12_381.rs @@ -0,0 +1,649 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +#[macro_use] +extern crate criterion; + +use crate::bench_utils::{ + bench_function_add, bench_function_clone, bench_function_deser_comp, + bench_function_deser_uncomp, bench_function_div, bench_function_double, bench_function_eq, + bench_function_from_u64, bench_function_inv, bench_function_mul, bench_function_neg, + bench_function_pow_u256, bench_function_serialize_uncomp, bench_function_square, + bench_function_sub, +}; +use aptos_crypto::test_utils::random_bytes; +use ark_bls12_381::{Fq12, Fr, G1Affine, G1Projective, G2Affine, G2Projective}; +use ark_ec::{ + hashing::HashToCurve, pairing::Pairing, short_weierstrass::Projective, AffineRepr, CurveGroup, + Group, +}; +use ark_ff::{One, UniformRand, Zero}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::test_rng; +use criterion::{BenchmarkId, Criterion}; +use rand::thread_rng; +use std::ops::{Add, Mul, Neg}; + +mod bench_utils; + +fn msm_all_bench_cases() -> Vec { + let series_until_65 = (1..65).step_by(2); + let series_until_129 = (64..129).step_by(4); + let series_until_257 = (129..257).step_by(8); + series_until_65 + .chain(series_until_129) + .chain(series_until_257) + .collect::>() +} + +macro_rules! rand { + ($typ:ty) => {{ + <$typ>::rand(&mut test_rng()) + }}; +} + +macro_rules! serialize { + ($obj:expr, $method:ident) => {{ + let mut buf = vec![]; + $obj.$method(&mut buf).unwrap(); + buf + }}; +} + +fn bench_group(c: &mut Criterion) { + let mut group = c.benchmark_group("ark_bls12_381"); + + group.bench_function("fr_add", bench_function_add::); + + group.bench_function("fr_deser", bench_function_deser_uncomp::); + + group.bench_function("fr_deser_invalid_4_bytes", move |b| { + b.iter_with_setup( + || vec![0xFF_u8; 4], + |buf| { + let _k = Fr::deserialize_uncompressed(buf.as_slice()); + }, + ) + }); + + group.bench_function("fr_deser_invalid_4000_bytes", move |b| { + b.iter_with_setup( + || vec![0xFF_u8; 4000], + |buf| { + let _k = Fr::deserialize_uncompressed(buf.as_slice()); + }, + ) + }); + + group.bench_function("fr_deser_invalid_4000000_bytes", move |b| { + b.iter_with_setup( + || vec![0xFF_u8; 4000000], + |buf| { + let _k = Fr::deserialize_uncompressed(buf.as_slice()); + }, + ) + }); + + group.bench_function("fr_div", bench_function_div::); + group.bench_function("fr_double", bench_function_double::); + group.bench_function("fr_eq", bench_function_eq::); + group.bench_function("fr_from_u64", bench_function_from_u64::); + group.bench_function("fr_inv", bench_function_inv::); + group.bench_function("fr_mul", bench_function_mul::); + group.bench_function("fr_neg", bench_function_neg::); + group.bench_function("fr_pow_u256", bench_function_pow_u256::); + group.bench_function("fr_serialize", bench_function_serialize_uncomp::); + group.bench_function("fr_square", bench_function_square::); + group.bench_function("fr_sub", bench_function_sub::); + + group.bench_function("fr_mul_self", move |b| { + b.iter_with_setup( + || rand!(Fr), + |k| { + let _k2 = k.mul(&k); + }, + ) + }); + + group.bench_function("fr_one", move |b| { + b.iter_with_setup( + || {}, + |_| { + let _k = Fr::one(); + }, + ) + }); + + group.bench_function("fr_zero", move |b| { + b.iter_with_setup( + || {}, + |_| { + let _k = Fr::zero(); + }, + ) + }); + group.bench_function("fq12_add", bench_function_add::); + group.bench_function("fq12_clone", bench_function_clone::); + group.bench_function("fq12_deser", bench_function_deser_uncomp::); + group.bench_function("fq12_div", bench_function_div::); + group.bench_function("fq12_double", bench_function_double::); + group.bench_function("fq12_eq", bench_function_eq::); + group.bench_function("fq12_from_u64", bench_function_from_u64::); + group.bench_function("fq12_inv", bench_function_inv::); + group.bench_function("fq12_mul", bench_function_mul::); + group.bench_function("fq12_neg", bench_function_neg::); + group.bench_function("fq12_pow_u256", bench_function_pow_u256::); + group.bench_function("fq12_serialize", bench_function_serialize_uncomp::); + group.bench_function("fq12_square", bench_function_square::); + group.bench_function("fq12_sub", bench_function_sub::); + + group.bench_function("fq12_add_self", move |b| { + b.iter_with_setup( + || rand!(Fq12), + |e| { + let _e_2 = e.add(&e); + }, + ) + }); + + group.bench_function("fq12_mul_self", move |b| { + b.iter_with_setup( + || rand!(Fq12), + |e| { + let _e_2 = e.mul(&e); + }, + ) + }); + + group.bench_function("fq12_one", move |b| { + b.iter(|| { + let _e = Fq12::one(); + }) + }); + + group.bench_function("fq12_zero", move |b| { + b.iter_with_setup( + || (), + |_| { + let _res = Fq12::zero(); + }, + ) + }); + + group.bench_function("g1_affine_add", bench_function_add::); + group.bench_function( + "g1_affine_deser_comp", + bench_function_deser_comp::, + ); + group.bench_function( + "g1_affine_deser_uncomp", + bench_function_deser_uncomp::, + ); + group.bench_function("g1_affine_eq", bench_function_eq::); + + group.bench_function("g1_affine_generator", move |b| { + b.iter(|| { + let _res = G1Affine::generator(); + }) + }); + + group.bench_function("g1_affine_infinity", move |b| { + b.iter(|| { + let _res = G1Affine::zero(); + }) + }); + + group.bench_function("g1_affine_scalar_mul_to_proj", move |b| { + b.iter_with_setup( + || (rand!(G1Affine), rand!(Fr)), + |(p, k)| { + let _res = p.mul(k); + }, + ) + }); + + group.bench_function("g1_affine_neg", move |b| { + b.iter_with_setup( + || rand!(G1Affine), + |p| { + let _res = p.neg(); + }, + ) + }); + + group.bench_function("g1_affine_serialize_comp", move |b| { + b.iter_with_setup( + || rand!(G1Affine), + |p_affine| { + let _buf = serialize!(p_affine, serialize_compressed); + }, + ) + }); + + group.bench_function("g1_affine_serialize_uncomp", move |b| { + b.iter_with_setup( + || rand!(G1Affine), + |p_affine| { + let _buf = serialize!(p_affine, serialize_uncompressed); + }, + ) + }); + + group.bench_function("g1_affine_to_proj", move |b| { + b.iter_with_setup( + || rand!(G1Affine), + |p_affine| { + let _res = G1Projective::from(p_affine); + }, + ) + }); + + group.bench_function("g1_proj_add", move |b| { + b.iter_with_setup( + || { + let p = rand!(G1Projective); + let q = rand!(G1Projective); + (p, q) + }, + |(p, q)| { + let _res = p + q; + }, + ) + }); + + group.bench_function("g1_proj_double", move |b| { + b.iter_with_setup( + || rand!(G1Projective), + |p| { + let _q = p.double(); + }, + ) + }); + + group.bench_function("g1_proj_eq", move |b| { + b.iter_with_setup( + || { + let p = rand!(G1Projective); + let q = p; + (p, q) + }, + |(p, q)| { + let _res = p == q; + }, + ) + }); + + group.bench_function("g1_proj_generator", move |b| { + b.iter(|| { + let _res = G1Projective::generator(); + }) + }); + + group.bench_function("g1_proj_infinity", move |b| { + b.iter(|| { + let _res = G1Projective::zero(); + }) + }); + + group.bench_function("g1_proj_neg", move |b| { + b.iter_with_setup( + || rand!(G1Projective), + |p| { + let _q = p.neg(); + }, + ) + }); + + group.bench_function("g1_proj_scalar_mul", move |b| { + b.iter_with_setup( + || { + let p = rand!(G1Projective); + let k = rand!(Fr); + (p, k) + }, + |(p, k)| { + let _q = p.mul(k); + }, + ) + }); + + group.bench_function("g1_proj_sub", move |b| { + b.iter_with_setup( + || { + let p = rand!(G1Projective); + let q = rand!(G1Projective); + (p, q) + }, + |(p, q)| { + let _r = p - q; + }, + ) + }); + + group.bench_function("g1_proj_to_affine", move |b| { + b.iter_with_setup( + || rand!(G1Projective), + |p_proj| { + let _ = p_proj.into_affine(); + }, + ) + }); + + group.bench_function("g2_affine_add", move |b| { + b.iter_with_setup( + || (rand!(G2Affine), rand!(G2Affine)), + |(p1, p2)| { + let _p3 = p1 + p2; + }, + ) + }); + + group.bench_function("g2_affine_deser_comp", move |b| { + b.iter_with_setup( + || { + let p = rand!(G2Affine); + serialize!(p, serialize_compressed) + }, + |buf| { + let _p = G2Affine::deserialize_compressed(buf.as_slice()); + }, + ) + }); + + group.bench_function("g2_affine_deser_uncomp", move |b| { + b.iter_with_setup( + || { + let p = rand!(G2Affine); + serialize!(p, serialize_uncompressed) + }, + |buf| { + let _p = G2Affine::deserialize_uncompressed(buf.as_slice()); + }, + ) + }); + + group.bench_function("g2_affine_eq", move |b| { + b.iter_with_setup( + || { + let p1 = rand!(G2Affine); + let p2 = p1; + (p1, p2) + }, + |(p1, p2)| { + let _res = p1 == Projective::from(p2); + }, + ) + }); + + group.bench_function("g2_affine_generator", move |b| { + b.iter(|| { + let _res = G2Affine::generator(); + }) + }); + + group.bench_function("g2_affine_infinity", move |b| { + b.iter(|| { + let _res = G2Affine::zero(); + }) + }); + + group.bench_function("g2_affine_scalar_mul_to_proj", move |b| { + b.iter_with_setup( + || (rand!(G2Affine), rand!(Fr)), + |(p, k)| { + let _res = p.mul(k); + }, + ) + }); + + group.bench_function("g2_affine_neg", move |b| { + b.iter_with_setup( + || rand!(G2Affine), + |p| { + let _res = p.neg(); + }, + ) + }); + + group.bench_function("g2_affine_serialize_comp", move |b| { + b.iter_with_setup( + || rand!(G2Affine), + |p_affine| { + let _buf = serialize!(p_affine, serialize_compressed); + }, + ) + }); + + group.bench_function("g2_affine_serialize_uncomp", move |b| { + b.iter_with_setup( + || rand!(G2Affine), + |p_affine| { + let _buf = serialize!(p_affine, serialize_uncompressed); + }, + ) + }); + + group.bench_function("g2_affine_to_proj", move |b| { + b.iter_with_setup( + || rand!(G2Affine), + |p_affine| { + let _res = G2Projective::from(p_affine); + }, + ) + }); + + group.bench_function("g2_proj_add", move |b| { + b.iter_with_setup( + || { + let p = rand!(G2Projective); + let q = rand!(G2Projective); + (p, q) + }, + |(p, q)| { + let _res = p + q; + }, + ) + }); + + group.bench_function("g2_proj_double", move |b| { + b.iter_with_setup( + || rand!(G2Projective), + |p| { + let _q = p.double(); + }, + ) + }); + + group.bench_function("g2_proj_eq", move |b| { + b.iter_with_setup( + || { + let p = rand!(G2Projective); + let q = p; + (p, q) + }, + |(p, q)| { + let _res = p == q; + }, + ) + }); + + group.bench_function("g2_proj_generator", move |b| { + b.iter(|| { + let _res = G2Projective::generator(); + }) + }); + + group.bench_function("g2_proj_infinity", move |b| { + b.iter(|| { + let _res = G2Projective::zero(); + }) + }); + + group.bench_function("g2_proj_neg", move |b| { + b.iter_with_setup( + || rand!(G2Projective), + |p| { + let _q = p.neg(); + }, + ) + }); + + group.bench_function("g2_proj_scalar_mul", move |b| { + b.iter_with_setup( + || { + let p = rand!(G2Projective); + let k = rand!(Fr); + (p, k) + }, + |(p, k)| { + let _q = p.mul(k); + }, + ) + }); + + group.bench_function("g2_proj_sub", move |b| { + b.iter_with_setup( + || { + let p = rand!(G2Projective); + let q = rand!(G2Projective); + (p, q) + }, + |(p, q)| { + let _r = p - q; + }, + ) + }); + + group.bench_function("g2_proj_to_affine", move |b| { + b.iter_with_setup( + || rand!(G2Projective), + |p_proj| { + let _ = p_proj.into_affine(); + }, + ) + }); + + group.bench_function("pairing", move |b| { + b.iter_with_setup( + || (rand!(G1Affine), rand!(G2Affine)), + |(g1e, g2e)| { + let _res = ark_bls12_381::Bls12_381::pairing(g1e, g2e).0; + }, + ) + }); + + let linear_regression_max_num_datapoints = 20; + + let pairing_product_max_num_pairs = 100; + for num_pairs in (0..pairing_product_max_num_pairs) + .step_by(pairing_product_max_num_pairs / linear_regression_max_num_datapoints) + { + group.bench_function(BenchmarkId::new("pairing_product", num_pairs), |b| { + b.iter_with_setup( + || { + let g1_elements = (0..num_pairs).map(|_i| rand!(G1Affine)).collect::>(); + let g2_elements = (0..num_pairs).map(|_i| rand!(G2Affine)).collect::>(); + (g1_elements, g2_elements) + }, + |(g1_elements, g2_elements)| { + let _product = + ark_bls12_381::Bls12_381::multi_pairing(g1_elements, g2_elements).0; + }, + ); + }); + } + + for num_entries in msm_all_bench_cases() { + group.bench_function(BenchmarkId::new("g1_affine_msm", num_entries), |b| { + b.iter_with_setup( + || { + let elements = (0..num_entries) + .map(|_i| rand!(G1Affine)) + .collect::>(); + let scalars = (0..num_entries).map(|_i| rand!(Fr)).collect::>(); + (elements, scalars) + }, + |(elements, scalars)| { + let _res: G1Projective = + ark_ec::VariableBaseMSM::msm(elements.as_slice(), scalars.as_slice()) + .unwrap(); + }, + ); + }); + } + + for num_entries in msm_all_bench_cases() { + group.bench_function(BenchmarkId::new("g2_affine_msm", num_entries), |b| { + b.iter_with_setup( + || { + let elements = (0..num_entries) + .map(|_i| rand!(G2Affine)) + .collect::>(); + let scalars = (0..num_entries).map(|_i| rand!(Fr)).collect::>(); + (elements, scalars) + }, + |(elements, scalars)| { + let _res: G2Projective = + ark_ec::VariableBaseMSM::msm(elements.as_slice(), scalars.as_slice()) + .unwrap(); + }, + ); + }); + } + + let hash_to_curve_max_msg_len = 1048576; + + for msg_len in (0..hash_to_curve_max_msg_len) + .step_by(hash_to_curve_max_msg_len / linear_regression_max_num_datapoints) + { + group.bench_function(BenchmarkId::new("hash_to_g1_proj", msg_len), |b| { + b.iter_with_setup( + || { + let dst = random_bytes(&mut thread_rng(), 255); + let msg = random_bytes(&mut thread_rng(), msg_len); + (dst, msg) + }, + |(dst, msg)| { + let mapper = ark_ec::hashing::map_to_curve_hasher::MapToCurveBasedHasher::< + Projective, + ark_ff::fields::field_hashers::DefaultFieldHasher, + ark_ec::hashing::curve_maps::wb::WBMap, + >::new(dst.as_slice()) + .unwrap(); + let _new_element = ::from(mapper.hash(msg.as_slice()).unwrap()); + }, + ); + }); + } + + for msg_len in (0..hash_to_curve_max_msg_len) + .step_by(hash_to_curve_max_msg_len / linear_regression_max_num_datapoints) + { + group.bench_function(BenchmarkId::new("hash_to_g2_proj", msg_len), |b| { + b.iter_with_setup( + || { + let dst = random_bytes(&mut thread_rng(), 255); + let msg = random_bytes(&mut thread_rng(), msg_len); + (dst, msg) + }, + |(dst, msg)| { + let mapper = ark_ec::hashing::map_to_curve_hasher::MapToCurveBasedHasher::< + Projective, + ark_ff::fields::field_hashers::DefaultFieldHasher, + ark_ec::hashing::curve_maps::wb::WBMap, + >::new(dst.as_slice()) + .unwrap(); + let _new_element = ::from(mapper.hash(msg.as_slice()).unwrap()); + }, + ); + }); + } + + group.finish(); +} + +criterion_group!( + name = ark_bls12_381_benches; + config = Criterion::default(); //.measurement_time(Duration::from_secs(100)); + targets = bench_group); +criterion_main!(ark_bls12_381_benches); diff --git a/crates/aptos-crypto/benches/ark_bn254.rs b/crates/aptos-crypto/benches/ark_bn254.rs new file mode 100644 index 0000000..ad6ce0d --- /dev/null +++ b/crates/aptos-crypto/benches/ark_bn254.rs @@ -0,0 +1,552 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +#[macro_use] +extern crate criterion; + +use crate::bench_utils::{ + bench_function_add, bench_function_clone, bench_function_deser_comp, + bench_function_deser_uncomp, bench_function_div, bench_function_double, bench_function_eq, + bench_function_from_u64, bench_function_inv, bench_function_mul, bench_function_neg, + bench_function_pow_u256, bench_function_serialize_uncomp, bench_function_square, + bench_function_sub, +}; +use ark_bn254::{Bn254, Fq, Fq12, Fr, G1Affine, G1Projective, G2Affine, G2Projective}; +use ark_ec::{pairing::Pairing, short_weierstrass::Projective, AffineRepr, CurveGroup, Group}; +use ark_ff::{UniformRand, Zero}; +use ark_groth16::Groth16; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::test_rng; +use criterion::{Bencher, BenchmarkId, Criterion}; +use std::ops::{Mul, Neg}; + +mod bench_utils; + +fn msm_all_bench_cases() -> Vec { + let series_until_65 = (1..65).step_by(2); + let series_until_129 = (64..129).step_by(4); + let series_until_257 = (129..257).step_by(8); + series_until_65 + .chain(series_until_129) + .chain(series_until_257) + .collect::>() +} + +macro_rules! rand { + ($typ:ty) => {{ + <$typ>::rand(&mut test_rng()) + }}; +} + +macro_rules! serialize { + ($obj:expr, $method:ident) => {{ + let mut buf = vec![]; + $obj.$method(&mut buf).unwrap(); + buf + }}; +} + +fn bench_group(c: &mut Criterion) { + let mut group = c.benchmark_group("ark_bn254"); + + group.bench_function("groth16/verify", bench_groth16_verify); + + group.bench_function("fr_add", bench_function_add::); + group.bench_function("fr_clone", bench_function_clone::); + group.bench_function("fr_deser", bench_function_deser_uncomp::); + group.bench_function("fr_div", bench_function_div::); + group.bench_function("fr_double", bench_function_double::); + group.bench_function("fr_eq", bench_function_eq::); + group.bench_function("fr_from_u64", bench_function_from_u64::); + group.bench_function("fr_inv", bench_function_inv::); + group.bench_function("fr_mul", bench_function_mul::); + group.bench_function("fr_neg", bench_function_neg::); + group.bench_function("fr_pow_u256", bench_function_pow_u256::); + group.bench_function("fr_serialize", bench_function_serialize_uncomp::); + group.bench_function("fr_square", bench_function_square::); + group.bench_function("fr_sub", bench_function_sub::); + + group.bench_function("fq_add", bench_function_add::); + group.bench_function("fq_clone", bench_function_clone::); + group.bench_function("fq_deser", bench_function_deser_uncomp::); + group.bench_function("fq_div", bench_function_div::); + group.bench_function("fq_double", bench_function_double::); + group.bench_function("fq_eq", bench_function_eq::); + group.bench_function("fq_from_u64", bench_function_from_u64::); + group.bench_function("fq_inv", bench_function_inv::); + group.bench_function("fq_mul", bench_function_mul::); + group.bench_function("fq_neg", bench_function_neg::); + group.bench_function("fq_pow_u256", bench_function_pow_u256::); + group.bench_function("fq_serialize", bench_function_serialize_uncomp::); + group.bench_function("fq_square", bench_function_square::); + group.bench_function("fq_sub", bench_function_sub::); + + group.bench_function("fq12_add", bench_function_add::); + group.bench_function("fq12_clone", bench_function_clone::); + group.bench_function("fq12_deser", bench_function_deser_uncomp::); + group.bench_function("fq12_div", bench_function_div::); + group.bench_function("fq12_double", bench_function_double::); + group.bench_function("fq12_eq", bench_function_eq::); + group.bench_function("fq12_from_u64", bench_function_from_u64::); + group.bench_function("fq12_inv", bench_function_inv::); + group.bench_function("fq12_mul", bench_function_mul::); + group.bench_function("fq12_neg", bench_function_neg::); + group.bench_function("fq12_pow_u256", bench_function_pow_u256::); + group.bench_function("fq12_serialize", bench_function_serialize_uncomp::); + group.bench_function("fq12_square", bench_function_square::); + group.bench_function("fq12_sub", bench_function_sub::); + + group.bench_function("g1_affine_add", bench_function_add::); + group.bench_function( + "g1_affine_deser_comp", + bench_function_deser_comp::, + ); + group.bench_function( + "g1_affine_deser_uncomp", + bench_function_deser_uncomp::, + ); + group.bench_function("g1_affine_eq", bench_function_eq::); + group.bench_function("g1_affine_generator", move |b| { + b.iter(|| { + let _res = G1Affine::generator(); + }) + }); + + group.bench_function("g1_affine_infinity", move |b| { + b.iter(|| { + let _res = G1Affine::zero(); + }) + }); + + group.bench_function("g1_affine_scalar_mul_to_proj", move |b| { + b.iter_with_setup( + || (rand!(G1Affine), rand!(Fr)), + |(p, k)| { + let _res = p.mul(k); + }, + ) + }); + + group.bench_function("g1_affine_neg", move |b| { + b.iter_with_setup( + || rand!(G1Affine), + |p| { + let _res = p.neg(); + }, + ) + }); + + group.bench_function("g1_affine_serialize_comp", move |b| { + b.iter_with_setup( + || rand!(G1Affine), + |p_affine| { + let _buf = serialize!(p_affine, serialize_compressed); + }, + ) + }); + + group.bench_function("g1_affine_serialize_uncomp", move |b| { + b.iter_with_setup( + || rand!(G1Affine), + |p_affine| { + let _buf = serialize!(p_affine, serialize_uncompressed); + }, + ) + }); + + group.bench_function("g1_affine_to_proj", move |b| { + b.iter_with_setup( + || rand!(G1Affine), + |p_affine| { + let _res = G1Projective::from(p_affine); + }, + ) + }); + + group.bench_function("g1_proj_add", move |b| { + b.iter_with_setup( + || { + let p = rand!(G1Projective); + let q = rand!(G1Projective); + (p, q) + }, + |(p, q)| { + let _res = p + q; + }, + ) + }); + + group.bench_function("g1_proj_double", move |b| { + b.iter_with_setup( + || rand!(G1Projective), + |p| { + let _q = p.double(); + }, + ) + }); + + group.bench_function("g1_proj_eq", move |b| { + b.iter_with_setup( + || { + let p = rand!(G1Projective); + let q = p; + (p, q) + }, + |(p, q)| { + let _res = p == q; + }, + ) + }); + + group.bench_function("g1_proj_generator", move |b| { + b.iter(|| { + let _res = G1Projective::generator(); + }) + }); + + group.bench_function("g1_proj_infinity", move |b| { + b.iter(|| { + let _res = G1Projective::zero(); + }) + }); + + group.bench_function("g1_proj_neg", move |b| { + b.iter_with_setup( + || rand!(G1Projective), + |p| { + let _q = p.neg(); + }, + ) + }); + + group.bench_function("g1_proj_scalar_mul", move |b| { + b.iter_with_setup( + || { + let p = rand!(G1Projective); + let k = rand!(Fr); + (p, k) + }, + |(p, k)| { + let _q = p.mul(k); + }, + ) + }); + + group.bench_function("g1_proj_sub", move |b| { + b.iter_with_setup( + || { + let p = rand!(G1Projective); + let q = rand!(G1Projective); + (p, q) + }, + |(p, q)| { + let _r = p - q; + }, + ) + }); + + group.bench_function("g1_proj_to_affine", move |b| { + b.iter_with_setup( + || rand!(G1Projective), + |p_proj| { + let _ = p_proj.into_affine(); + }, + ) + }); + + group.bench_function("g2_affine_add", move |b| { + b.iter_with_setup( + || (rand!(G2Affine), rand!(G2Affine)), + |(p1, p2)| { + let _p3 = p1 + p2; + }, + ) + }); + + group.bench_function("g2_affine_deser_comp", move |b| { + b.iter_with_setup( + || { + let p = rand!(G2Affine); + serialize!(p, serialize_compressed) + }, + |buf| { + let _p = G2Affine::deserialize_compressed(buf.as_slice()); + }, + ) + }); + + group.bench_function("g2_affine_deser_uncomp", move |b| { + b.iter_with_setup( + || { + let p = rand!(G2Affine); + serialize!(p, serialize_uncompressed) + }, + |buf| { + let _p = G2Affine::deserialize_uncompressed(buf.as_slice()); + }, + ) + }); + + group.bench_function("g2_affine_eq", move |b| { + b.iter_with_setup( + || { + let p1 = rand!(G2Affine); + let p2 = p1; + (p1, p2) + }, + |(p1, p2)| { + let _res = p1 == Projective::from(p2); + }, + ) + }); + + group.bench_function("g2_affine_generator", move |b| { + b.iter(|| { + let _res = G2Affine::generator(); + }) + }); + + group.bench_function("g2_affine_infinity", move |b| { + b.iter(|| { + let _res = G2Affine::zero(); + }) + }); + + group.bench_function("g2_affine_scalar_mul_to_proj", move |b| { + b.iter_with_setup( + || (rand!(G2Affine), rand!(Fr)), + |(p, k)| { + let _res = p.mul(k); + }, + ) + }); + + group.bench_function("g2_affine_neg", move |b| { + b.iter_with_setup( + || rand!(G2Affine), + |p| { + let _res = p.neg(); + }, + ) + }); + + group.bench_function("g2_affine_serialize_comp", move |b| { + b.iter_with_setup( + || rand!(G2Affine), + |p_affine| { + let _buf = serialize!(p_affine, serialize_compressed); + }, + ) + }); + + group.bench_function("g2_affine_serialize_uncomp", move |b| { + b.iter_with_setup( + || rand!(G2Affine), + |p_affine| { + let _buf = serialize!(p_affine, serialize_uncompressed); + }, + ) + }); + + group.bench_function("g2_affine_to_proj", move |b| { + b.iter_with_setup( + || rand!(G2Affine), + |p_affine| { + let _res = G2Projective::from(p_affine); + }, + ) + }); + + group.bench_function("g2_proj_add", move |b| { + b.iter_with_setup( + || { + let p = rand!(G2Projective); + let q = rand!(G2Projective); + (p, q) + }, + |(p, q)| { + let _res = p + q; + }, + ) + }); + + group.bench_function("g2_proj_double", move |b| { + b.iter_with_setup( + || rand!(G2Projective), + |p| { + let _q = p.double(); + }, + ) + }); + + group.bench_function("g2_proj_eq", move |b| { + b.iter_with_setup( + || { + let p = rand!(G2Projective); + let q = p; + (p, q) + }, + |(p, q)| { + let _res = p == q; + }, + ) + }); + + group.bench_function("g2_proj_generator", move |b| { + b.iter(|| { + let _res = G2Projective::generator(); + }) + }); + + group.bench_function("g2_proj_infinity", move |b| { + b.iter(|| { + let _res = G2Projective::zero(); + }) + }); + + group.bench_function("g2_proj_neg", move |b| { + b.iter_with_setup( + || rand!(G2Projective), + |p| { + let _q = p.neg(); + }, + ) + }); + + group.bench_function("g2_proj_scalar_mul", move |b| { + b.iter_with_setup( + || { + let p = rand!(G2Projective); + let k = rand!(Fr); + (p, k) + }, + |(p, k)| { + let _q = p.mul(k); + }, + ) + }); + + group.bench_function("g2_proj_sub", move |b| { + b.iter_with_setup( + || { + let p = rand!(G2Projective); + let q = rand!(G2Projective); + (p, q) + }, + |(p, q)| { + let _r = p - q; + }, + ) + }); + + group.bench_function("g2_proj_to_affine", move |b| { + b.iter_with_setup( + || rand!(G2Projective), + |p_proj| { + let _ = p_proj.into_affine(); + }, + ) + }); + + group.bench_function("pairing", move |b| { + b.iter_with_setup( + || (rand!(G1Affine), rand!(G2Affine)), + |(g1e, g2e)| { + let _res = ark_bn254::Bn254::pairing(g1e, g2e).0; + }, + ) + }); + + let linear_regression_max_num_datapoints = 20; + + let pairing_product_max_num_pairs = 100; + for num_pairs in (0..pairing_product_max_num_pairs) + .step_by(pairing_product_max_num_pairs / linear_regression_max_num_datapoints) + { + group.bench_function(BenchmarkId::new("pairing_product", num_pairs), |b| { + b.iter_with_setup( + || { + let g1_elements = (0..num_pairs).map(|_i| rand!(G1Affine)).collect::>(); + let g2_elements = (0..num_pairs).map(|_i| rand!(G2Affine)).collect::>(); + (g1_elements, g2_elements) + }, + |(g1_elements, g2_elements)| { + let _product = ark_bn254::Bn254::multi_pairing(g1_elements, g2_elements).0; + }, + ); + }); + } + + for num_entries in msm_all_bench_cases() { + group.bench_function(BenchmarkId::new("g1_affine_msm", num_entries), |b| { + b.iter_with_setup( + || { + let elements = (0..num_entries) + .map(|_i| rand!(G1Affine)) + .collect::>(); + let scalars = (0..num_entries).map(|_i| rand!(Fr)).collect::>(); + (elements, scalars) + }, + |(elements, scalars)| { + let _res: G1Projective = + ark_ec::VariableBaseMSM::msm(elements.as_slice(), scalars.as_slice()) + .unwrap(); + }, + ); + }); + } + + for num_entries in msm_all_bench_cases() { + group.bench_function(BenchmarkId::new("g2_affine_msm", num_entries), |b| { + b.iter_with_setup( + || { + let elements = (0..num_entries) + .map(|_i| rand!(G2Affine)) + .collect::>(); + let scalars = (0..num_entries).map(|_i| rand!(Fr)).collect::>(); + (elements, scalars) + }, + |(elements, scalars)| { + let _res: G2Projective = + ark_ec::VariableBaseMSM::msm(elements.as_slice(), scalars.as_slice()) + .unwrap(); + }, + ); + }); + } + + group.finish(); +} + +fn bench_groth16_verify(b: &mut Bencher) { + let pvk = ark_groth16::PreparedVerifyingKey { + vk: ark_groth16::VerifyingKey { + alpha_g1: rand!(G1Affine), + beta_g2: rand!(G2Affine), + gamma_g2: rand!(G2Affine), + delta_g2: rand!(G2Affine), + gamma_abc_g1: vec![rand!(G1Affine), rand!(G1Affine)], + }, + alpha_g1_beta_g2: rand!(ark_bn254::Fq12), + gamma_g2_neg_pc: rand!(G2Affine).into(), + delta_g2_neg_pc: rand!(G2Affine).into(), + }; + + b.iter_with_setup( + || ark_groth16::Proof { + a: rand!(G1Affine), + b: rand!(G2Affine), + c: rand!(G1Affine), + }, + |proof| { + let result = Groth16::::verify_proof(&pvk, &proof, &[rand!(Fr)]); + assert!(matches!(result, Ok(false))) + }, + ) +} + +criterion_group!( + name = ark_bn254_benches; + config = Criterion::default(); //.measurement_time(Duration::from_secs(100)); + targets = bench_group); +criterion_main!(ark_bn254_benches); diff --git a/crates/aptos-crypto/benches/bench_utils.rs b/crates/aptos-crypto/benches/bench_utils.rs new file mode 100644 index 0000000..81905ca --- /dev/null +++ b/crates/aptos-crypto/benches/bench_utils.rs @@ -0,0 +1,167 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use ark_ff::{BigInteger256, Field}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{test_rng, UniformRand}; +use criterion::Bencher; +use std::ops::{Add, Div, Mul, Neg, Sub}; + +fn rand() -> T { + T::rand(&mut test_rng()) +} + +pub fn bench_function_add(b: &mut Bencher) { + b.iter_with_setup( + || (rand::(), rand::()), + |(e_1, e_2)| { + let _e_3 = e_1 + e_2; + }, + ) +} + +pub fn bench_function_clone(b: &mut Bencher) { + b.iter_with_setup( + || rand::(), + |e| { + let _e_2 = e.clone(); + }, + ) +} + +pub fn bench_function_deser_comp( + b: &mut Bencher, +) { + b.iter_with_setup( + || { + let e = rand::(); + let mut buf = vec![]; + e.serialize_compressed(&mut buf).unwrap(); + buf + }, + |buf| { + let _e = T::deserialize_compressed(buf.as_slice()).unwrap(); + }, + ) +} + +pub fn bench_function_deser_uncomp( + b: &mut Bencher, +) { + b.iter_with_setup( + || { + let e = rand::(); + let mut buf = vec![]; + e.serialize_uncompressed(&mut buf).unwrap(); + buf + }, + |buf| { + let _e = T::deserialize_uncompressed(buf.as_slice()).unwrap(); + }, + ) +} + +pub fn bench_function_div(b: &mut Bencher) { + b.iter_with_setup( + || (rand::(), rand::()), + |(e, f)| { + let _g = e.div(f); + }, + ) +} + +pub fn bench_function_double(b: &mut Bencher) { + b.iter_with_setup( + || rand::(), + |e| { + let _e_2 = e.double(); + }, + ) +} + +pub fn bench_function_eq(b: &mut Bencher) { + b.iter_with_setup( + || { + let e_1 = rand::(); + let e_2 = e_1.clone(); + (e_1, e_2) + }, + |(e_1, e_2)| { + let _res = e_1 == e_2; + }, + ) +} + +pub fn bench_function_from_u64 + UniformRand>(b: &mut Bencher) { + b.iter_with_setup(rand::, |i| { + let _res = T::from(i); + }) +} + +pub fn bench_function_inv(b: &mut Bencher) { + b.iter_with_setup( + || rand::(), + |e| { + let _e_inv = e.inverse(); + }, + ) +} + +pub fn bench_function_mul(b: &mut Bencher) { + b.iter_with_setup( + || (rand::(), rand::()), + |(e_1, e_2)| { + let _e_3 = e_1 * e_2; + }, + ) +} + +pub fn bench_function_neg(b: &mut Bencher) { + b.iter_with_setup( + || rand::(), + |e| { + let _e_2 = e.neg(); + }, + ) +} + +pub fn bench_function_pow_u256(b: &mut Bencher) { + b.iter_with_setup( + || { + let base = rand::(); + let exp = rand::(); + (base, exp) + }, + |(base, exp)| { + let _res = base.pow(exp); + }, + ) +} + +pub fn bench_function_serialize_uncomp(b: &mut Bencher) { + b.iter_with_setup( + || rand::(), + |e| { + let mut buf = vec![]; + e.serialize_uncompressed(&mut buf).unwrap(); + }, + ) +} + +pub fn bench_function_square(b: &mut Bencher) { + b.iter_with_setup( + || rand::(), + |e| { + let _res = e.square(); + }, + ) +} + +pub fn bench_function_sub(b: &mut Bencher) { + b.iter_with_setup( + || (rand::(), rand::()), + |(e, f)| { + let _res = e - f; + }, + ) +} diff --git a/crates/aptos-crypto/benches/bls12381.rs b/crates/aptos-crypto/benches/bls12381.rs new file mode 100644 index 0000000..41b5397 --- /dev/null +++ b/crates/aptos-crypto/benches/bls12381.rs @@ -0,0 +1,422 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +#[macro_use] +extern crate criterion; + +use aptos_crypto::{ + bls12381, + bls12381::ProofOfPossession, + test_utils::{random_keypairs, random_subset, KeyPair}, + traits::{Signature, SigningKey, Uniform}, + PrivateKey, +}; +use aptos_crypto_derive::{BCSCryptoHash, CryptoHasher}; +use criterion::{ + measurement::Measurement, AxisScale, BatchSize, BenchmarkGroup, BenchmarkId, Criterion, + PlotConfiguration, Throughput, +}; +use rand::{distributions, rngs::ThreadRng, thread_rng, Rng}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, CryptoHasher, BCSCryptoHash, Serialize, Deserialize)] +struct TestAptosCrypto(String); + +fn random_message(rng: &mut ThreadRng) -> TestAptosCrypto { + TestAptosCrypto( + rng.sample_iter(&distributions::Alphanumeric) + .take(256) + .map(char::from) + .collect::(), + ) +} + +fn bench_group(c: &mut Criterion) { + let mut group = c.benchmark_group("bls12381"); + + let plot_config = PlotConfiguration::default().summary_scale(AxisScale::Logarithmic); + + group.sample_size(1000); + group.plot_config(plot_config); + + pk_deserialize(&mut group); + sig_deserialize(&mut group); + pk_subgroup_membership(&mut group); + sig_subgroup_membership(&mut group); + aggregate_one_sigshare(&mut group); + aggregate_one_pk(&mut group); + + pop_create(&mut group); + pop_create_with_pubkey(&mut group); + pop_verify(&mut group); + + sign(&mut group); + verify_signature_share(&mut group); + + let mut size = 128; + for _ in 1..=4 { + // Even single-threaded, this function has higher throughput that `aggregate_one_sigshare` + aggregate_sigshare(&mut group, size); + + // Even single-threaded, this function has higher throughput than `aggregate_one_pk`. Seems + // to be due to only making a single call to blst::PublicKey::from_aggregate (which calls a + // $pk_to_aff function) for the entire batch. + aggregate_pks(&mut group, size); + + verify_multisig(&mut group, size); + verify_aggsig(&mut group, size); + size *= 2; + } + + group.finish(); +} + +/// Benchmarks the time to deserialize a BLS12-381 point representing a PK in G1. (Does not test for +/// prime-order subgroup membership.) +fn pk_deserialize(g: &mut BenchmarkGroup) { + let mut rng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + + g.bench_function("pk_deserialize", move |b| { + b.iter_with_setup( + || { + bls12381::PrivateKey::generate(&mut rng) + .public_key() + .to_bytes() + }, + |pk_bytes| bls12381::PublicKey::try_from(&pk_bytes[..]), + ) + }); +} + +/// Benchmarks the time to aggregate a BLS PK in G1. (Does not test for prime-order subgroup +/// membership.) +fn aggregate_one_pk(g: &mut BenchmarkGroup) { + let mut rng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + + g.bench_function("aggregate_one_pk", move |b| { + b.iter_with_setup( + || { + ( + bls12381::PrivateKey::generate(&mut rng).public_key(), + bls12381::PrivateKey::generate(&mut rng).public_key(), + ) + }, + |(pk1, pk2)| { + bls12381::PublicKey::aggregate(vec![&pk1, &pk2]).unwrap(); + }, + ) + }); +} + +/// Benchmarks the time to deserialize a BLS12-381 point representing a signature in G2. (Does not test for +/// prime-order subgroup membership.) +fn sig_deserialize(g: &mut BenchmarkGroup) { + let mut rng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + + g.bench_function("sig_deserialize", move |b| { + b.iter_with_setup( + || { + let sk = bls12381::PrivateKey::generate(&mut rng); + sk.sign(&TestAptosCrypto("Hello Aptos!".to_owned())) + .unwrap() + .to_bytes() + }, + |sig_bytes| bls12381::Signature::try_from(&sig_bytes[..]), + ) + }); +} + +/// Benchmarks the time to aggregate a BLS signature in G2. (Does not test for prime-order subgroup +/// membership.) +fn aggregate_one_sigshare(g: &mut BenchmarkGroup) { + let mut rng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + + g.bench_function("aggregate_one_sigshare", move |b| { + b.iter_with_setup( + || { + ( + bls12381::PrivateKey::generate(&mut rng) + .sign(&TestAptosCrypto("Hello Aptos!".to_owned())) + .unwrap(), + bls12381::PrivateKey::generate(&mut rng) + .sign(&TestAptosCrypto("Hello Aptos!".to_owned())) + .unwrap(), + ) + }, + |(sig1, sig2)| { + bls12381::Signature::aggregate(vec![sig1, sig2]).unwrap(); + }, + ) + }); +} + +fn pk_subgroup_membership(g: &mut BenchmarkGroup) { + let mut rng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + g.bench_function("pk_prime_order_subgroup_check", move |b| { + b.iter_with_setup( + || { + let kp = KeyPair::::generate(&mut rng); + kp.public_key + }, + |pk| pk.subgroup_check(), + ) + }); +} + +fn sig_subgroup_membership(g: &mut BenchmarkGroup) { + let mut rng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + g.bench_function("sig_prime_order_subgroup_check", move |b| { + b.iter_with_setup( + || { + let kp = KeyPair::::generate(&mut rng); + + // Currently, there's no better way of sampling a group element here + kp.private_key + .sign(&TestAptosCrypto("Hello Aptos!".to_owned())) + .unwrap() + }, + |sig| sig.subgroup_check(), + ) + }); +} + +fn pop_create(g: &mut BenchmarkGroup) { + let mut rng = thread_rng(); + + let priv_key = bls12381::PrivateKey::generate(&mut rng); + + g.throughput(Throughput::Elements(1)); + g.bench_function("pop_create", move |b| { + b.iter(|| ProofOfPossession::create(&priv_key)) + }); +} + +fn pop_create_with_pubkey(g: &mut BenchmarkGroup) { + let mut rng = thread_rng(); + + let kp = KeyPair::::generate(&mut rng); + + g.throughput(Throughput::Elements(1)); + g.bench_function("pop_create_with_pubkey", move |b| { + b.iter(|| ProofOfPossession::create_with_pubkey(&kp.private_key, &kp.public_key)) + }); +} + +fn pop_verify(g: &mut BenchmarkGroup) { + let mut rng = thread_rng(); + + let kp = KeyPair::::generate(&mut rng); + let pop = ProofOfPossession::create_with_pubkey(&kp.private_key, &kp.public_key); + + g.throughput(Throughput::Elements(1)); + g.bench_function("pop_verify", move |b| { + b.iter(|| { + let result = pop.verify(&kp.public_key); + assert!(result.is_ok()); + }) + }); +} + +fn sign(g: &mut BenchmarkGroup) { + let mut rng = thread_rng(); + + let priv_key = bls12381::PrivateKey::generate(&mut rng); + let msg = random_message(&mut rng); + + g.throughput(Throughput::Elements(1)); + g.bench_function("sign", move |b| b.iter(|| priv_key.sign(&msg).unwrap())); +} + +fn verify_signature_share(g: &mut BenchmarkGroup) { + let mut rng = thread_rng(); + + let keypair = KeyPair::::generate(&mut rng); + let msg = random_message(&mut rng); + let sig = keypair.private_key.sign(&msg).unwrap(); + + g.throughput(Throughput::Elements(1)); + g.bench_function("verify_signature_share", move |b| { + b.iter(|| { + let result = sig.verify(&msg, &keypair.public_key); + assert!(result.is_ok()); + }) + }); +} + +fn aggregate_pks(g: &mut BenchmarkGroup, size: usize) { + let mut rng = thread_rng(); + + // pick a bunch of random keypairs + let key_pairs: Vec> = + random_keypairs(&mut rng, size); + + g.throughput(Throughput::Elements(size as u64)); + g.bench_with_input( + BenchmarkId::new("aggregate_pks", size), + &size, + |b, &_size| { + b.iter_batched( + || { + let mut pks = vec![]; + for kp in key_pairs.iter() { + pks.push(&kp.public_key); + } + + pks + }, + |pks| { + let result = bls12381::PublicKey::aggregate(pks); + assert!(result.is_ok()); + }, + BatchSize::SmallInput, + ); + }, + ); +} + +fn aggregate_sigshare(g: &mut BenchmarkGroup, size: usize) { + let mut rng = thread_rng(); + + // pick a bunch of random keypairs + let key_pairs: Vec> = + random_keypairs(&mut rng, size); + + g.throughput(Throughput::Elements(size as u64)); + g.bench_with_input( + BenchmarkId::new("aggregate_sigshare", size), + &size, + |b, &_size| { + // pick a random message to aggregate a multisignature on + let msg = random_message(&mut rng); + + b.iter_batched( + || { + // each signer computes a signature share on the random message + let mut sigshares = vec![]; + for kp in key_pairs.iter() { + sigshares.push(kp.private_key.sign(&msg).unwrap()); + } + sigshares + }, + |sigshares| { + let result = bls12381::Signature::aggregate(sigshares); + assert!(result.is_ok()); + }, + BatchSize::SmallInput, + ); + }, + ); +} + +/// Benchmarks the time to verify a multisignature from the perspective of a verifier who has the +/// public keys of `n` signers and receives a multisignature from `size` of them +fn verify_multisig(g: &mut BenchmarkGroup, size: usize) { + let mut rng = thread_rng(); + + // pick `n` random keypairs + let mut key_pairs = vec![]; + let n = size * 2; + + for _ in 0..n { + key_pairs.push(KeyPair::::generate(&mut rng)); + } + + g.throughput(Throughput::Elements(size as u64)); + g.bench_with_input( + BenchmarkId::new("verify_multisig", size), + &size, + |b, &size| { + // pick a random message to aggregate a multisignature on + let msg = random_message(&mut rng); + + b.iter_batched( + || { + // pick a random subset of signers + let subset = random_subset(&mut rng, n, size); + + // each of the selected signers computes a signature share on the random message + let mut sigshares = vec![]; + let mut pks = vec![]; + + for i in subset { + sigshares.push(key_pairs[i].private_key.sign(&msg).unwrap()); + pks.push(&key_pairs[i].public_key) + } + + let multisig = bls12381::Signature::aggregate(sigshares).unwrap(); + + (pks, multisig) + }, + |(pks, multisig)| { + let aggpk = bls12381::PublicKey::aggregate(pks).unwrap(); + + let result = multisig.verify(&msg, &aggpk); + + assert!(result.is_ok()); + }, + BatchSize::SmallInput, + ); + }, + ); +} + +/// Benchmarks the time to verify an aggregate signature from the perspective of a verifier who +/// receives an aggregate signature from `n` signers. +fn verify_aggsig(g: &mut BenchmarkGroup, n: usize) { + let mut rng = thread_rng(); + + // pick `n` random keypairs + let mut key_pairs = vec![]; + + for _ in 0..n { + key_pairs.push(KeyPair::::generate(&mut rng)); + } + + g.throughput(Throughput::Elements(n as u64)); + g.bench_with_input(BenchmarkId::new("verify_aggsig", n), &n, |b, &_n| { + b.iter_batched( + || { + // each of the signers computes a signature share on a random message + let mut sigshares = vec![]; + let mut pks = vec![]; + let mut msgs = vec![]; + + for kp in key_pairs.iter() { + msgs.push(random_message(&mut rng)); + sigshares.push(kp.private_key.sign(msgs.last().unwrap()).unwrap()); + pks.push(&kp.public_key) + } + + let aggsig = bls12381::Signature::aggregate(sigshares).unwrap(); + + (msgs, pks, aggsig) + }, + |(msgs, pks, aggsig)| { + let msgs_refs = msgs.iter().collect::>(); + + let result = aggsig.verify_aggregate(&msgs_refs, &pks); + + assert!(result.is_ok()); + }, + BatchSize::SmallInput, + ); + }); +} + +criterion_group!( + name = bls12381_benches; + config = Criterion::default(); //.measurement_time(Duration::from_secs(100)); + targets = bench_group); +criterion_main!(bls12381_benches); diff --git a/crates/aptos-crypto/benches/bulletproofs.rs b/crates/aptos-crypto/benches/bulletproofs.rs new file mode 100644 index 0000000..95877aa --- /dev/null +++ b/crates/aptos-crypto/benches/bulletproofs.rs @@ -0,0 +1,148 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +#[macro_use] +extern crate criterion; + +use bulletproofs::{BulletproofGens, PedersenGens, RangeProof}; +use criterion::{measurement::Measurement, BenchmarkGroup, BenchmarkId, Criterion, Throughput}; +use curve25519_dalek_ng::scalar::Scalar; +use merlin::Transcript; +use rand::{thread_rng, Rng}; + +fn get_values(num_bits: usize, batch_size: usize) -> (Vec, Vec) { + let mut rng = thread_rng(); + + let v = (0..batch_size) + .map(|_| rng.gen_range(0u64, (2u128.pow(num_bits as u32) - 1u128) as u64)) + .collect::>(); + + // Sigh, some RngCore incompatibilites I don't want to deal with right now. + let b = (0..batch_size) + .map(|_| Scalar::hash_from_bytes::(b"some random blinder")) + .collect::>(); + + (v, b) +} + +fn bench_group(c: &mut Criterion) { + let mut group = c.benchmark_group("bulletproofs"); + + for num_bits in [32, 64] { + range_proof_deserialize(&mut group, num_bits); + } + + for batch_size in [1, 2] { + for num_bits in [32, 64] { + range_prove(&mut group, num_bits, batch_size); + range_verify(&mut group, num_bits, batch_size); + } + } + + group.finish(); +} + +fn range_prove(g: &mut BenchmarkGroup, num_bits: usize, batch_size: usize) { + let pg = PedersenGens::default(); + let bg = BulletproofGens::new(num_bits, batch_size); + + g.throughput(Throughput::Elements(batch_size as u64)); + g.bench_function( + BenchmarkId::new( + "range_prove", + format!("batch={}/bits={}", batch_size, num_bits), + ), + move |b| { + b.iter_with_setup( + || get_values(num_bits, batch_size), + |(v, b)| { + let mut t_prv = Transcript::new(b"some DST"); + assert!(RangeProof::prove_multiple( + &bg, + &pg, + &mut t_prv, + v.as_slice(), + b.as_slice(), + num_bits + ) + .is_ok()); + }, + ) + }, + ); +} + +fn range_proof_deserialize(g: &mut BenchmarkGroup, num_bits: usize) { + let bp_gens = BulletproofGens::new(num_bits, 1); + let pc_gens = PedersenGens::default(); + + g.throughput(Throughput::Elements(1)); + g.bench_function( + BenchmarkId::new("range_proof_deserialize", num_bits), + move |b| { + b.iter_with_setup( + || { + let (v, b) = get_values(num_bits, 1); + + let mut t = merlin::Transcript::new(b"AptosBenchmark"); + + let proof = RangeProof::prove_multiple( + &bp_gens, + &pc_gens, + &mut t, + v.as_slice(), + b.as_slice(), + num_bits, + ) + .unwrap(); + proof.0.to_bytes() + }, + |proof_bytes| { + assert!(RangeProof::from_bytes(&proof_bytes[..]).is_ok()); + }, + ) + }, + ); +} + +fn range_verify(g: &mut BenchmarkGroup, num_bits: usize, batch_size: usize) { + let bp_gens = BulletproofGens::new(num_bits, batch_size); + let pc_gens = PedersenGens::default(); + + g.throughput(Throughput::Elements(batch_size as u64)); + g.bench_function( + BenchmarkId::new( + "range_verify", + format!("batch={}/bits={}", batch_size, num_bits), + ), + move |b| { + b.iter_with_setup( + || { + let (v, b) = get_values(num_bits, batch_size); + + let mut t = merlin::Transcript::new(b"AptosBenchmark"); + + RangeProof::prove_multiple( + &bp_gens, + &pc_gens, + &mut t, + v.as_slice(), + b.as_slice(), + num_bits, + ) + .unwrap() + }, + |(proof, comm)| { + let mut t = merlin::Transcript::new(b"AptosBenchmark"); + + assert!(proof + .verify_multiple(&bp_gens, &pc_gens, &mut t, &comm, num_bits) + .is_ok()); + }, + ) + }, + ); +} + +criterion_group!(bulletproofs_benches, bench_group); +criterion_main!(bulletproofs_benches); diff --git a/crates/aptos-crypto/benches/ed25519.rs b/crates/aptos-crypto/benches/ed25519.rs new file mode 100644 index 0000000..2ed8a01 --- /dev/null +++ b/crates/aptos-crypto/benches/ed25519.rs @@ -0,0 +1,134 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[macro_use] +extern crate criterion; + +use aptos_crypto::{ + ed25519::{Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature}, + traits::{Signature, SigningKey, Uniform}, + PrivateKey, +}; +use aptos_crypto_derive::{BCSCryptoHash, CryptoHasher}; +use criterion::{measurement::Measurement, BenchmarkGroup, Criterion, Throughput}; +use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, scalar::Scalar}; +use rand::{distributions, prelude::ThreadRng, thread_rng, Rng}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, CryptoHasher, BCSCryptoHash, Serialize, Deserialize)] +pub struct TestAptosCrypto(pub String); + +fn random_message(rng: &mut ThreadRng) -> TestAptosCrypto { + TestAptosCrypto( + rng.sample_iter(&distributions::Alphanumeric) + .take(256) + .map(char::from) + .collect::(), + ) +} + +fn benchmark_groups(c: &mut Criterion) { + let mut group = c.benchmark_group("ed25519"); + + group.sample_size(1000); + + sig_verify_struct(&mut group); + sig_verify_zero_bytes(&mut group); + pk_deserialize(&mut group); + sig_deserialize(&mut group); + small_subgroup_check(&mut group); + + group.finish(); +} + +fn sig_verify_struct(g: &mut BenchmarkGroup) { + let mut csprng: ThreadRng = thread_rng(); + + let priv_key = Ed25519PrivateKey::generate(&mut csprng); + let pub_key: Ed25519PublicKey = (&priv_key).into(); + + g.throughput(Throughput::Elements(1)); + g.bench_function("sig_verify_struct", move |b| { + b.iter_with_setup( + || { + let msg = random_message(&mut csprng); + let sig = priv_key.sign(&msg).unwrap(); + (sig, msg) + }, + |(sig, msg)| sig.verify(&msg, &pub_key), + ) + }); +} + +/// Benchmarks the time to verify a signature on an empty message. (Used for gas estimation.) +fn sig_verify_zero_bytes(g: &mut BenchmarkGroup) { + let mut csprng: ThreadRng = thread_rng(); + + let priv_key = Ed25519PrivateKey::generate(&mut csprng); + let pub_key: Ed25519PublicKey = (&priv_key).into(); + + g.throughput(Throughput::Elements(1)); + g.bench_function("sig_verify_zero_bytes", move |b| { + b.iter_with_setup( + || { + // Just want to ensure a different signature each time, so signing a random message each time + let msg = random_message(&mut csprng); + priv_key.sign(&msg).unwrap() + }, + |sig| sig.verify_arbitrary_msg(b"", &pub_key), + ) + }); +} + +/// Benchmarks the time to check if an EdwardsPoint is in a small subgroup. +fn small_subgroup_check(g: &mut BenchmarkGroup) { + let point = ED25519_BASEPOINT_POINT; + let mut csprng = thread_rng(); + + g.throughput(Throughput::Elements(1_u64)); + g.bench_function("small_subgroup_check", move |b| { + b.iter_with_setup( + || Scalar::random(&mut csprng) * point, + |h| h.is_small_order(), + ) + }); +} + +/// Benchmarks the time to deserialize an Ed25519 public key from a sequence of bytes. +fn pk_deserialize(g: &mut BenchmarkGroup) { + let mut csprng = thread_rng(); + + g.throughput(Throughput::Elements(1_u64)); + g.bench_function("pk_deserialize", move |b| { + b.iter_with_setup( + || { + Ed25519PrivateKey::generate(&mut csprng) + .public_key() + .to_bytes() + }, + |pk_bytes| Ed25519PublicKey::try_from(&pk_bytes[..]), + ) + }); +} + +/// Benchmarks the time to deserialize an Ed25519 signature from a sequence of bytes. +fn sig_deserialize(g: &mut BenchmarkGroup) { + let mut csprng = thread_rng(); + + g.throughput(Throughput::Elements(1_u64)); + g.bench_function("sig_deserialize", move |b| { + b.iter_with_setup( + || { + Ed25519PrivateKey::generate(&mut csprng) + .sign(&TestAptosCrypto("Hello Aptos!".to_string())) + .unwrap() + .to_bytes() + }, + |sig_bytes| Ed25519Signature::try_from(&sig_bytes[..]), + ) + }); +} + +criterion_group!(ed25519_benches, benchmark_groups); +criterion_main!(ed25519_benches); diff --git a/crates/aptos-crypto/benches/generate-bench-data.sh b/crates/aptos-crypto/benches/generate-bench-data.sh new file mode 100755 index 0000000..c38c083 --- /dev/null +++ b/crates/aptos-crypto/benches/generate-bench-data.sh @@ -0,0 +1,43 @@ +no_threads="--features=blst/no-threads" + +cargo bench $no_threads -- "hash" + +cargo bench $no_threads -- "bls12381/sig_deserialize" +cargo bench $no_threads -- "bls12381/pk_deserialize" +cargo bench $no_threads -- "bls12381/pk_prime_order_subgroup_check" +cargo bench $no_threads -- "bls12381/sig_prime_order_subgroup_check" +cargo bench $no_threads -- "bls12381/aggregate_pks/1024" +cargo bench $no_threads -- "bls12381/aggregate_sigshare/1024" +cargo bench $no_threads -- "bls12381/pop_verify" + +cargo bench -- "ed25519/pk_deserialize" +cargo bench -- "ed25519/small_subgroup_check" +cargo bench -- "ed25519/sig_deserialize" +cargo bench -- "ed25519/sig_verify_zero_bytes" + +cargo bench -- "secp256k1/ecdsa_recover" + +cargo bench -- "ristretto255/basepoint_mul" +cargo bench -- "ristretto255/basepoint_double_mul" +cargo bench -- "ristretto255/point_add" +#cargo bench -- "ristretto255/point_clone" +cargo bench -- "ristretto255/point_compress" +cargo bench -- "ristretto255/point_decompress" +cargo bench -- "ristretto255/point_equals" +cargo bench -- "ristretto255/point_from_64_uniform_bytes" +cargo bench -- "ristretto255/point_identity" +cargo bench -- "ristretto255/point_mul" +cargo bench -- "ristretto255/point_neg" +cargo bench -- "ristretto255/point_sub" +#cargo bench -- "ristretto255/point_parse_arg" +cargo bench -- "ristretto255/scalar_add" +cargo bench -- "ristretto255/scalar_reduced_from_32_bytes" +cargo bench -- "ristretto255/scalar_uniform_from_64_bytes" +cargo bench -- "ristretto255/scalar_from_u128" +cargo bench -- "ristretto255/scalar_from_u64" +cargo bench -- "ristretto255/scalar_invert" +cargo bench -- "ristretto255/scalar_is_canonical" +cargo bench -- "ristretto255/scalar_mul" +cargo bench -- "ristretto255/scalar_neg" +cargo bench -- "ristretto255/scalar_sub" +#cargo bench -- "ristretto255/scalar_parse_arg" \ No newline at end of file diff --git a/crates/aptos-crypto/benches/generate-gas-csv.sh b/crates/aptos-crypto/benches/generate-gas-csv.sh new file mode 100755 index 0000000..9535931 --- /dev/null +++ b/crates/aptos-crypto/benches/generate-gas-csv.sh @@ -0,0 +1,161 @@ +#!/bin/bash + +scriptdir=$(cd $(dirname $0); pwd -P) + +repo_root=$(readlink -f $scriptdir/../../../) + +#echo "Repo root: $repo_root" + +critdir=$repo_root/target/criterion/ + +if [ $# -ne 1 ]; then + echo "Usage: $0 " + echo + echo "A bunch of .csv files will be created in " + exit 1 +fi + +outdir=$1 +mkdir -p $outdir +echo "Output directory: $outdir" + +# +# Variable-size input operations +# (Currently, just hashing) +# + +[ ! -d $critdir/hash/ ] && { echo "No benchmark results for $hash_func. Be sure to run 'cargo bench -- hash' first..."; exit 1; } +[ ! -f $scriptdir/parse-hash-benches.sh ] && { echo "Expected a '$scriptdir/parse-hash-benches.sh' executable file."; exit 1; } + +out_hash=$outdir/hash + +echo "hash_function,input_size,time_in_nanosecs" >${out_hash}-sha2-256.csv +$scriptdir/parse-hash-benches.sh SHA2-256 ${out_hash}-sha2-256.csv +echo "hash_function,input_size,time_in_nanosecs" >${out_hash}-sha2-512.csv +$scriptdir/parse-hash-benches.sh SHA2-512 ${out_hash}-sha2-512.csv +echo "hash_function,input_size,time_in_nanosecs" >${out_hash}-sha3-256.csv +$scriptdir/parse-hash-benches.sh SHA3-256 ${out_hash}-sha3-256.csv +echo "hash_function,input_size,time_in_nanosecs" >${out_hash}-keccak-256.csv +$scriptdir/parse-hash-benches.sh Keccak-256 ${out_hash}-keccak-256.csv +echo "hash_function,input_size,time_in_nanosecs" >${out_hash}-bls12381_g1.csv +$scriptdir/parse-hash-benches.sh hash_to_bls12381_g1 ${out_hash}-bls12381_g1.csv +echo "hash_function,input_size,time_in_nanosecs" >${out_hash}-bls12381_g2.csv +$scriptdir/parse-hash-benches.sh hash_to_bls12381_g2 ${out_hash}-bls12381_g2.csv + +get_mean() { + file=$1 + + mean=`cat "$file" | jq .slope.point_estimate` + if [ "$mean" = "null" ]; then + #echo "No .slope.point_estimate, using .mean.point_estimate" 1>&2 + + mean=`cat "$file" | jq .mean.point_estimate` + fi + + echo $mean +} + +# +# Fixed-size input operations +# +operations=" +bls12381:pk_deserialize +bls12381:aggregate_pks/1024 +bls12381:pk_prime_order_subgroup_check +bls12381:sig_deserialize +bls12381:aggregate_sigshare/1024 +bls12381:sig_prime_order_subgroup_check +bls12381:NOOP.per_sig_verify +bls12381:pop_verify +bls12381:NOOP.per_pairing + +ed25519:pk_deserialize +ed25519:small_subgroup_check +ed25519:sig_deserialize +ed25519:sig_verify_zero_bytes + +secp256k1:ecdsa_recover + +ristretto255:basepoint_mul +ristretto255:basepoint_double_mul +ristretto255:point_add +ristretto255:NOOP.point_clone +ristretto255:point_compress +ristretto255:point_decompress +ristretto255:point_equals +ristretto255:point_from_64_uniform_bytes +ristretto255:point_identity +ristretto255:point_mul +ristretto255:point_neg +ristretto255:point_sub +ristretto255:NOOP.point_parse_arg +ristretto255:scalar_add +ristretto255:scalar_reduced_from_32_bytes +ristretto255:scalar_uniform_from_64_bytes +ristretto255:scalar_from_u128 +ristretto255:scalar_from_u64 +ristretto255:scalar_invert +ristretto255:scalar_is_canonical +ristretto255:scalar_mul +ristretto255:scalar_neg +ristretto255:scalar_sub +ristretto255:NOOP.scalar_parse_arg +" + +while read -r module_and_op; do + module=`echo "$module_and_op" | cut -f 1 -d':'` + op=`echo "$module_and_op" | cut -f 2 -d':'` + out_file=$outdir/${module}.csv + + + # We leave spaces sometimes for ease of reading + if [ "$op" = "" ]; then + #echo "Skipping empty line..." + continue + fi + + echo + + # We use no-ops in the CSV file to leave a gap in case we will add a field there later, so that line offsets of other + # fields don't change and mess up our spreadsheet calculation. + if echo "$op" | grep "NOOP\..*" >/dev/null; then + noop=`echo "$op" | cut -f 2 -d'.'` + echo "Skipping $module no-op for $noop..." + echo $module,$noop,NA >>$out_file + continue + fi + + echo "$module module, $op operation" + + # Add the CSV headers in on the first iteration + if [ ! -f $out_file ]; then + echo "module,operation_name,time_in_nanosecs" >$out_file + fi + + # Make sure benchmarks were run + [ ! -d "$critdir/$module/$op/" ] && { + echo "No benchmark results for '$module/$op'. Be sure to run 'cargo bench -- $module/$op' first..."; + echo "$op,NA" >>$out_file + continue; + } + + mean=`get_mean "$critdir/$module/$op/new/estimates.json"` + + # For some functions that take an input of size n, we pick a reasonably large batch size n and estimate their per-input + # cost by dividing the total time by n. + if echo "$op" | grep ".*/.*" >/dev/null; then + op_name=`echo "$op" | cut -f 1 -d'/'` + batch_size=`echo "$op" | cut -f 2 -d'/'` + + echo "Batched operation: $op_name ($batch_size)" + + mean=`echo $mean / 1024.0 | bc -l` + fi + + echo "$module,$op,$mean" >>$out_file +done <<< "$operations" + +echo +echo "Exited gracefully." +echo + diff --git a/crates/aptos-crypto/benches/hash.rs b/crates/aptos-crypto/benches/hash.rs new file mode 100644 index 0000000..e72ce1d --- /dev/null +++ b/crates/aptos-crypto/benches/hash.rs @@ -0,0 +1,257 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +#[macro_use] +extern crate criterion; + +use aptos_crypto::{bls12381::DST_BLS_SIG_IN_G2_WITH_POP, test_utils::random_bytes}; +use blake2::{ + digest::{Update, VariableOutput}, + Blake2bVar, +}; +use blake2_rfc::blake2b::Blake2b; +use criterion::{ + measurement::Measurement, AxisScale, BenchmarkGroup, BenchmarkId, Criterion, PlotConfiguration, + Throughput, +}; +use digest::Digest; +use rand::thread_rng; +use sha2::{Sha256, Sha512}; +use std::ptr::null; +use tiny_keccak::{Hasher as KeccakHasher, Keccak}; + +/// Runs all the benchmarks. +fn bench_group(c: &mut Criterion) { + let mut group = c.benchmark_group("hash"); + + let plot_config = PlotConfiguration::default().summary_scale(AxisScale::Logarithmic); + + group.sample_size(1000); + group.plot_config(plot_config); + + let mut sizes = vec![0, 1]; + + let mut size = *sizes.last().unwrap(); + for _ in 1..=10 { + size *= 2; + sizes.push(size); + } + + for n in sizes { + sha2_256(&mut group, n); + sha2_512(&mut group, n); + sha3_256(&mut group, n); + hash_to_g1(&mut group, n, DST_BLS_SIG_IN_G2_WITH_POP); + hash_to_g2(&mut group, n, DST_BLS_SIG_IN_G2_WITH_POP); + keccak256(&mut group, n); + blake2_blake2b_256(&mut group, n); + blake2_rfc_blake2b_256(&mut group, n); + } + + group.finish(); +} + +/// Benchmarks the time to hash an arbitrary message of size n bytes using SHA2-256 +fn sha2_256(g: &mut BenchmarkGroup, n: usize) { + let mut rng = thread_rng(); + + g.throughput(Throughput::Bytes(n as u64)); + + g.bench_function(BenchmarkId::new("SHA2-256", n), move |b| { + b.iter_with_setup( + || random_bytes(&mut rng, n), + |bytes| { + assert_eq!(bytes.len(), n); + + let mut hasher = Sha256::new(); + + hasher.update(bytes); + + let output = hasher.finalize(); + assert_eq!(output.as_slice().len(), 32); + }, + ) + }); +} + +/// Benchmarks the time to hash an arbitrary message of size n bytes using SHA2-512 +fn sha2_512(g: &mut BenchmarkGroup, n: usize) { + let mut rng = thread_rng(); + + g.throughput(Throughput::Bytes(n as u64)); + + g.bench_function(BenchmarkId::new("SHA2-512", n), move |b| { + b.iter_with_setup( + || random_bytes(&mut rng, n), + |bytes| { + assert_eq!(bytes.len(), n); + + let mut hasher = Sha512::new(); + + hasher.update(bytes); + + let output = hasher.finalize(); + assert_eq!(output.as_slice().len(), 64); + }, + ) + }); +} + +/// Benchmarks the time to hash an arbitrary message of size n bytes using SHA3-256 +fn sha3_256(g: &mut BenchmarkGroup, n: usize) { + let mut rng = thread_rng(); + + g.throughput(Throughput::Bytes(n as u64)); + + g.bench_function(BenchmarkId::new("SHA3-256", n), move |b| { + b.iter_with_setup( + || random_bytes(&mut rng, n), + |bytes| { + assert_eq!(bytes.len(), n); + + let mut hasher = sha3::Sha3_256::new(); + + hasher.update(bytes); + + let output = hasher.finalize(); + assert_eq!(output.as_slice().len(), 32); + }, + ) + }); +} + +/// Benchmarks the time to hash an arbitrary message of size n bytes using Keccak-256 +fn keccak256(g: &mut BenchmarkGroup, n: usize) { + let mut rng = thread_rng(); + + g.throughput(Throughput::Bytes(n as u64)); + + g.bench_function(BenchmarkId::new("Keccak-256", n), move |b| { + b.iter_with_setup( + || random_bytes(&mut rng, n), + |bytes| { + assert_eq!(bytes.len(), n); + + let mut hasher = Keccak::v256(); + hasher.update(&bytes); + + let mut output = [0u8; 32]; + hasher.finalize(&mut output); + + assert_eq!(output.as_slice().len(), 32); + }, + ) + }); +} + +/// Benchmarks the time to hash an arbitrary message of size n bytes into G_1 using the specified DST. +pub fn hash_to_g1(g: &mut BenchmarkGroup, n: usize, dst: &[u8]) { + let mut rng = thread_rng(); + + g.throughput(Throughput::Bytes(n as u64)); + + g.bench_function(BenchmarkId::new("hash_to_bls12381_g1", n), move |b| { + b.iter_with_setup( + || random_bytes(&mut rng, n), + |mut bytes| unsafe { + assert_eq!(bytes.len(), n); + let mut elem = blst::blst_p1::default(); + + let elem_ptr: *mut blst::blst_p1 = &mut elem; + let bytes_ptr: *mut u8 = bytes.as_mut_ptr(); + + blst::blst_hash_to_g1( + elem_ptr, + bytes_ptr, + bytes.len(), + dst.as_ptr(), + dst.len(), + null(), + 0, + ); + }, + ) + }); +} + +/// Benchmarks the time to hash an arbitrary message of size n bytes into G_2 using the specified DST. +pub fn hash_to_g2(g: &mut BenchmarkGroup, n: usize, dst: &[u8]) { + let mut rng = thread_rng(); + + g.throughput(Throughput::Bytes(n as u64)); + + g.bench_function(BenchmarkId::new("hash_to_bls12381_g2", n), move |b| { + b.iter_with_setup( + || random_bytes(&mut rng, n), + |mut bytes| unsafe { + assert_eq!(bytes.len(), n); + let mut elem = blst::blst_p2::default(); + + let elem_ptr: *mut blst::blst_p2 = &mut elem; + let bytes_ptr: *mut u8 = bytes.as_mut_ptr(); + + blst::blst_hash_to_g2( + elem_ptr, + bytes_ptr, + bytes.len(), + dst.as_ptr(), + dst.len(), + null(), + 0, + ); + }, + ) + }); +} + +fn blake2_blake2b_256(g: &mut BenchmarkGroup, n: usize) { + let mut rng = thread_rng(); + + g.throughput(Throughput::Bytes(n as u64)); + + g.bench_function(BenchmarkId::new("blake2b-256/crate-blake2", n), move |b| { + b.iter_with_setup( + || random_bytes(&mut rng, n), + |bytes| { + assert_eq!(bytes.len(), n); + + let mut hasher = Blake2bVar::new(32).unwrap(); + hasher.update(&bytes); + let mut output = vec![0u8; 32]; + hasher.finalize_variable(&mut output).unwrap(); + + assert_eq!(output.as_slice().len(), 32); + }, + ) + }); +} + +fn blake2_rfc_blake2b_256(g: &mut BenchmarkGroup, n: usize) { + let mut rng = thread_rng(); + + g.throughput(Throughput::Bytes(n as u64)); + + g.bench_function( + BenchmarkId::new("blake2b-256/crate-blake2-rfc", n), + move |b| { + b.iter_with_setup( + || random_bytes(&mut rng, n), + |bytes| { + assert_eq!(bytes.len(), n); + + // Using the state context. + let mut context = Blake2b::new(32); + context.update(&bytes); + let hash = context.finalize(); + assert_eq!(hash.as_bytes().len(), 32); + }, + ) + }, + ); +} + +criterion_group!( + name = hash_benches; + config = Criterion::default(); //.measurement_time(Duration::from_secs(100)); + targets = bench_group); +criterion_main!(hash_benches); diff --git a/crates/aptos-crypto/benches/noise.rs b/crates/aptos-crypto/benches/noise.rs new file mode 100644 index 0000000..e4f212a --- /dev/null +++ b/crates/aptos-crypto/benches/noise.rs @@ -0,0 +1,132 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//! Don't forget to run this benchmark with AES-NI enable. +//! You can do this by building with the following flags: +//! `RUSTFLAGS="-Ctarget-cpu=skylake -Ctarget-feature=+aes,+sse2,+sse4.1,+ssse3"`. +//! + +#[macro_use] +extern crate criterion; + +use aptos_crypto::{ + noise::{handshake_init_msg_len, handshake_resp_msg_len, NoiseConfig, AES_GCM_TAGLEN}, + test_utils::TEST_SEED, + x25519, Uniform as _, ValidCryptoMaterial as _, +}; +use criterion::{Criterion, Throughput}; +use rand::SeedableRng; +use std::convert::TryFrom as _; + +const MSG_SIZE: usize = 4096; + +fn benchmarks(c: &mut Criterion) { + // bench the handshake + let mut group = c.benchmark_group("noise-handshake"); + group.throughput(Throughput::Elements(1)); + group.bench_function("connect", |b| { + // setup keys first + let mut rng = ::rand::rngs::StdRng::from_seed(TEST_SEED); + let initiator_static = x25519::PrivateKey::generate(&mut rng); + let initiator_static = initiator_static.to_bytes(); + let responder_static = x25519::PrivateKey::generate(&mut rng); + let responder_public = responder_static.public_key(); + let responder_static = responder_static.to_bytes(); + + let mut first_message = [0u8; handshake_init_msg_len(0)]; + let mut second_message = [0u8; handshake_resp_msg_len(0)]; + + b.iter(|| { + let initiator_static = + x25519::PrivateKey::try_from(initiator_static.clone().as_slice()).unwrap(); + let responder_static = + x25519::PrivateKey::try_from(responder_static.clone().as_slice()).unwrap(); + + let initiator = NoiseConfig::new(initiator_static); + let responder = NoiseConfig::new(responder_static); + + let initiator_state = initiator + .initiate_connection( + &mut rng, + b"prologue", + responder_public, + None, + &mut first_message, + ) + .unwrap(); + + let _ = responder + .respond_to_client_and_finalize( + &mut rng, + b"prologue", + &first_message, + None, + &mut second_message, + ) + .unwrap(); + let _ = initiator + .finalize_connection(initiator_state, &second_message) + .unwrap(); + }) + }); + group.finish(); + + let mut transport_group = c.benchmark_group("noise-transport"); + transport_group.throughput(Throughput::Bytes(MSG_SIZE as u64 * 2)); + transport_group.bench_function("AES-GCM throughput", |b| { + let mut buffer_msg = [0u8; MSG_SIZE + AES_GCM_TAGLEN]; + + // setup keys first + let mut rng = ::rand::rngs::StdRng::from_seed(TEST_SEED); + let initiator_static = x25519::PrivateKey::generate(&mut rng); + let responder_static = x25519::PrivateKey::generate(&mut rng); + let responder_public = responder_static.public_key(); + + // handshake first + let initiator = NoiseConfig::new(initiator_static); + let responder = NoiseConfig::new(responder_static); + + let mut first_message = [0u8; handshake_init_msg_len(0)]; + let mut second_message = [0u8; handshake_resp_msg_len(0)]; + + let initiator_state = initiator + .initiate_connection( + &mut rng, + b"prologue", + responder_public, + None, + &mut first_message, + ) + .unwrap(); + let (_, mut responder_session) = responder + .respond_to_client_and_finalize( + &mut rng, + b"prologue", + &first_message, + None, + &mut second_message, + ) + .unwrap(); + let (_, mut initiator_session) = initiator + .finalize_connection(initiator_state, &second_message) + .unwrap(); + + // bench throughput post-handshake + b.iter(move || { + let auth_tag = initiator_session + .write_message_in_place(&mut buffer_msg[..MSG_SIZE]) + .expect("session should not be closed"); + + buffer_msg[MSG_SIZE..MSG_SIZE + AES_GCM_TAGLEN].copy_from_slice(&auth_tag); + + let _plaintext = responder_session + .read_message_in_place(&mut buffer_msg[..MSG_SIZE + AES_GCM_TAGLEN]) + .expect("session should not be closed"); + }) + }); + transport_group.finish(); +} + +criterion_group!(benches, benchmarks); +criterion_main!(benches); diff --git a/crates/aptos-crypto/benches/parse-bench.sh b/crates/aptos-crypto/benches/parse-bench.sh new file mode 100755 index 0000000..11743fb --- /dev/null +++ b/crates/aptos-crypto/benches/parse-bench.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +set -e + +scriptdir=$(cd $(dirname $0); pwd -P) + +repo_root=$(readlink -f $scriptdir/../../../) + +#echo "Repo root: $repo_root" + +if [ $# -ne 2 ]; then + echo "Usage: $0 " + echo + echo " can be" + echo " bls12381" + echo " hash" + exit 1 +fi + +group_name=$1; shift +bench_name=$1 + +critdir=$repo_root/target/criterion/$group_name/$bench_name + +mean=`cat $critdir/new/estimates.json | jq .slope.point_estimate` +if [ "$mean" = "null" ]; then + echo "No .slope.point_estimate, using .mean.point_estimate" 1>&2 + + mean=`cat $critdir/$hash_func/$n/new/estimates.json | jq .mean.point_estimate` +fi + +echo "$mean" diff --git a/crates/aptos-crypto/benches/parse-hash-benches.sh b/crates/aptos-crypto/benches/parse-hash-benches.sh new file mode 100755 index 0000000..0dfcb6c --- /dev/null +++ b/crates/aptos-crypto/benches/parse-hash-benches.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +set -e + +scriptdir=$(cd $(dirname $0); pwd -P) + +repo_root=$(readlink -f $scriptdir/../../../) + +#echo "Repo root: $repo_root" + +critdir=$repo_root/target/criterion/hash + +if [ $# -ne 2 ]; then + echo "Usage: $0 " + echo + echo " can be:" + echo + echo " SHA2-256" + echo " SHA2-512" + echo " SHA3-256" + echo " hash_to_bls12381_g1" + echo " hash_to_bls12381_g2" + echo " Keccak-256" + + exit 1 +fi + +hash_func=$1; shift +outfile=$1 + +if [ ! -f $critdir/$hash_func/0/new/estimates.json ]; then + echo "No benchmark results for $hash_func. Be sure to run 'cargo bench' first..." + exit 1 +fi + +ns="0 1 2 4 8 16 32 64 128 256 512 1024" # 2048 4096 8192 16384 32768 65536" + +for n in $ns; do + mean=`cat $critdir/$hash_func/$n/new/estimates.json | jq .slope.point_estimate` + if [ "$mean" = "null" ]; then + #echo "No .slope.point_estimate, using .mean.point_estimate" 1>&2 + + mean=`cat $critdir/$hash_func/$n/new/estimates.json | jq .mean.point_estimate` + fi + #echo "$n -> $mean" + echo "$hash_func,$n,$mean" >>$outfile +done diff --git a/crates/aptos-crypto/benches/poseidon_bn254.rs b/crates/aptos-crypto/benches/poseidon_bn254.rs new file mode 100644 index 0000000..0c4e88a --- /dev/null +++ b/crates/aptos-crypto/benches/poseidon_bn254.rs @@ -0,0 +1,83 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +#[macro_use] +extern crate criterion; + +use aptos_crypto::{poseidon_bn254, test_utils::random_bytes}; +use ark_ff::PrimeField; +use criterion::{measurement::Measurement, BenchmarkGroup, BenchmarkId, Criterion, Throughput}; +use poseidon_ark::Poseidon; +use rand::thread_rng; + +/// Runs all the benchmarks. +fn bench_group(c: &mut Criterion) { + let mut group = c.benchmark_group("zk"); + + //let plot_config = PlotConfiguration::default().summary_scale(AxisScale::Logarithmic); + //group.plot_config(plot_config); + group.sample_size(50); + + for n in 1..=16 { + poseidon_bn254_ark(&mut group, n); + poseidon_bn254_neptune(&mut group, n); + } + + group.finish(); +} + +fn poseidon_bn254_neptune(g: &mut BenchmarkGroup, n: usize) { + let mut rng = thread_rng(); + + g.throughput(Throughput::Elements(n as u64)); + + g.bench_function(BenchmarkId::new("poseidon-bn254-neptune", n), move |b| { + b.iter_with_setup( + || { + (0..n) + .map(|_| { + let bytes = random_bytes(&mut rng, n); + ark_bn254::Fr::from_le_bytes_mod_order(bytes.as_slice()) + }) + .collect::>() + }, + |scalars| { + assert_eq!(scalars.len(), n); + + poseidon_bn254::hash_scalars(scalars).unwrap() + }, + ) + }); +} + +fn poseidon_bn254_ark(g: &mut BenchmarkGroup, n: usize) { + let mut rng = thread_rng(); + + g.throughput(Throughput::Elements(n as u64)); + + let hash = Poseidon::new(); + + g.bench_function(BenchmarkId::new("poseidon-bn254-ark", n), move |b| { + b.iter_with_setup( + || { + (0..n) + .map(|_| { + let bytes = random_bytes(&mut rng, n); + ark_bn254::Fr::from_le_bytes_mod_order(bytes.as_slice()) + }) + .collect::>() + }, + |scalars| { + assert_eq!(scalars.len(), n); + + hash.hash(scalars).unwrap() + }, + ) + }); +} + +criterion_group!( + name = zk_benches; + config = Criterion::default(); //.measurement_time(Duration::from_secs(100)); + targets = bench_group); +criterion_main!(zk_benches); diff --git a/crates/aptos-crypto/benches/ristretto255.rs b/crates/aptos-crypto/benches/ristretto255.rs new file mode 100644 index 0000000..de209de --- /dev/null +++ b/crates/aptos-crypto/benches/ristretto255.rs @@ -0,0 +1,335 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +#[macro_use] +extern crate criterion; + +use aptos_crypto::test_utils::random_bytes; +use criterion::{measurement::Measurement, BenchmarkGroup, BenchmarkId, Criterion, Throughput}; +use curve25519_dalek::{ + constants::RISTRETTO_BASEPOINT_TABLE, + ristretto::{CompressedRistretto, RistrettoPoint}, + scalar::Scalar, + traits::{Identity, VartimeMultiscalarMul}, +}; +use rand::{distributions::Uniform, prelude::ThreadRng, thread_rng, Rng}; +use std::ops::{Add, Mul, Neg, Sub}; + +fn benchmark_groups(c: &mut Criterion) { + let mut group = c.benchmark_group("ristretto255"); + + group.sample_size(1000); + + point_mul(&mut group); + basepoint_mul(&mut group); + basepoint_double_mul(&mut group); + point_add(&mut group); + point_compress(&mut group); + point_decompress(&mut group); + point_equals(&mut group); + point_from_64_uniform_bytes(&mut group); + point_identity(&mut group); + point_neg(&mut group); + point_sub(&mut group); + + scalar_add(&mut group); + scalar_reduced_from_32_bytes(&mut group); + scalar_uniform_from_64_bytes(&mut group); + scalar_from_u128(&mut group); + scalar_from_u64(&mut group); + scalar_invert(&mut group); + scalar_is_canonical(&mut group); + scalar_mul(&mut group); + scalar_neg(&mut group); + scalar_sub(&mut group); + + //for n in 1..=128 { + //for n in [256, 512, 1024, 2048, 4096] { + for n in [2, 8192, 16384, 32768] { + multi_scalar_mul(&mut group, n); + } + + group.finish(); +} + +fn multi_scalar_mul(g: &mut BenchmarkGroup, n: usize) { + let mut rng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + g.bench_function(BenchmarkId::new("vartime_multiscalar_mul", n), move |b| { + b.iter_with_setup( + || { + let points = (0..n) + .map(|_| RistrettoPoint::random(&mut rng)) + .collect::>(); + let scalars = (0..n) + .map(|_| Scalar::random(&mut rng)) + .collect::>(); + + (points, scalars) + }, + |(points, scalars)| { + RistrettoPoint::vartime_multiscalar_mul( + scalars.iter(), + points.iter().collect::>(), + ) + }, + ) + }); +} + +/// Benchmarks the time for a single scalar multiplication on the Ristretto255 basepoint (with precomputation). +fn basepoint_mul(g: &mut BenchmarkGroup) { + let mut rng: ThreadRng = thread_rng(); + + let basepoint = RISTRETTO_BASEPOINT_TABLE; + + g.throughput(Throughput::Elements(1)); + g.bench_function("basepoint_mul", move |b| { + b.iter_with_setup(|| Scalar::random(&mut rng), |a| basepoint.mul(&a)) + }); +} + +/// Benchmarks the time for a double scalar multiplication where one of the bases is the Ristretto255 basepoint. +fn basepoint_double_mul(g: &mut BenchmarkGroup) { + let mut rng: ThreadRng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + g.bench_function("basepoint_double_mul", move |b| { + b.iter_with_setup( + || { + ( + RistrettoPoint::random(&mut rng), + Scalar::random(&mut rng), + Scalar::random(&mut rng), + ) + }, + |(a_point, a, b)| { + RistrettoPoint::vartime_double_scalar_mul_basepoint(&a, &a_point, &b); + }, + ) + }); +} + +fn point_add(g: &mut BenchmarkGroup) { + let mut rng: ThreadRng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + g.bench_function("point_add", move |b| { + b.iter_with_setup( + || { + ( + RistrettoPoint::random(&mut rng), + RistrettoPoint::random(&mut rng), + ) + }, + |(a_point, b_point)| a_point.add(&b_point), + ) + }); +} + +fn point_compress(g: &mut BenchmarkGroup) { + let mut rng: ThreadRng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + g.bench_function("point_compress", move |b| { + b.iter_with_setup( + || RistrettoPoint::random(&mut rng), + |point| point.compress(), + ) + }); +} + +fn point_decompress(g: &mut BenchmarkGroup) { + let mut rng: ThreadRng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + g.bench_function("point_decompress", move |b| { + b.iter_with_setup( + || RistrettoPoint::random(&mut rng).compress().to_bytes(), + |bytes| CompressedRistretto(bytes).decompress(), + ) + }); +} + +fn point_equals(g: &mut BenchmarkGroup) { + let mut rng: ThreadRng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + g.bench_function("point_equals", move |b| { + b.iter_with_setup( + || { + let a = RistrettoPoint::random(&mut rng); + (a, a) + }, + |(a_point, b_point)| a_point.eq(&b_point), + ) + }); +} + +fn point_from_64_uniform_bytes(g: &mut BenchmarkGroup) { + let mut rng: ThreadRng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + g.bench_function("point_from_64_uniform_bytes", move |b| { + b.iter_with_setup( + || <[u8; 64]>::try_from(random_bytes(&mut rng, 64)).unwrap(), + |bytes| RistrettoPoint::from_uniform_bytes(&bytes), + ) + }); +} + +fn point_identity(g: &mut BenchmarkGroup) { + g.throughput(Throughput::Elements(1)); + g.bench_function("point_identity", move |b| b.iter(RistrettoPoint::identity)); +} + +/// Benchmarks the time for a single scalar multiplication on a Ristretto255 point (without precomputation). +fn point_mul(g: &mut BenchmarkGroup) { + let mut rng: ThreadRng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + g.bench_function("point_mul", move |b| { + b.iter_with_setup( + || (RistrettoPoint::random(&mut rng), Scalar::random(&mut rng)), + |(g, a)| g.mul(&a), + ) + }); +} + +fn point_neg(g: &mut BenchmarkGroup) { + let mut rng: ThreadRng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + g.bench_function("point_neg", move |b| { + b.iter_with_setup(|| RistrettoPoint::random(&mut rng), |point| point.neg()) + }); +} + +fn point_sub(g: &mut BenchmarkGroup) { + let mut rng: ThreadRng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + g.bench_function("point_sub", move |b| { + b.iter_with_setup( + || { + ( + RistrettoPoint::random(&mut rng), + RistrettoPoint::random(&mut rng), + ) + }, + |(a_point, b_point)| a_point.sub(&b_point), + ) + }); +} + +fn scalar_add(g: &mut BenchmarkGroup) { + let mut rng: ThreadRng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + g.bench_function("scalar_add", move |b| { + b.iter_with_setup( + || (Scalar::random(&mut rng), Scalar::random(&mut rng)), + // NOTE: Specifically moving 'b' in, just like the native Rust function does in Move + |(a, b)| a.add(b), + ) + }); +} + +fn scalar_reduced_from_32_bytes(g: &mut BenchmarkGroup) { + let mut rng: ThreadRng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + g.bench_function("scalar_reduced_from_32_bytes", move |b| { + b.iter_with_setup( + || <[u8; 32]>::try_from(random_bytes(&mut rng, 32)).unwrap(), + Scalar::from_bytes_mod_order, + ) + }); +} + +fn scalar_uniform_from_64_bytes(g: &mut BenchmarkGroup) { + let mut rng: ThreadRng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + g.bench_function("scalar_uniform_from_64_bytes", move |b| { + b.iter_with_setup( + || <[u8; 64]>::try_from(random_bytes(&mut rng, 64)).unwrap(), + |bytes| Scalar::from_bytes_mod_order_wide(&bytes), + ) + }); +} + +fn scalar_from_u128(g: &mut BenchmarkGroup) { + let mut rng: ThreadRng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + g.bench_function("scalar_from_u128", move |b| { + b.iter_with_setup(|| rng.sample(Uniform::new(0u128, u128::MAX)), Scalar::from) + }); +} + +fn scalar_from_u64(g: &mut BenchmarkGroup) { + let mut rng: ThreadRng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + g.bench_function("scalar_from_u64", move |b| { + b.iter_with_setup(|| rng.sample(Uniform::new(0u64, u64::MAX)), Scalar::from) + }); +} + +fn scalar_invert(g: &mut BenchmarkGroup) { + let mut rng: ThreadRng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + g.bench_function("scalar_invert", move |b| { + b.iter_with_setup(|| Scalar::random(&mut rng), |a| a.invert()) + }); +} + +fn scalar_is_canonical(g: &mut BenchmarkGroup) { + let mut rng: ThreadRng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + g.bench_function("scalar_is_canonical", move |b| { + b.iter_with_setup(|| Scalar::random(&mut rng), |a| a.is_canonical()) + }); +} + +fn scalar_mul(g: &mut BenchmarkGroup) { + let mut rng: ThreadRng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + g.bench_function("scalar_mul", move |b| { + b.iter_with_setup( + || (Scalar::random(&mut rng), Scalar::random(&mut rng)), + // NOTE: Specifically moving 'b' in, just like the native Rust function does in Move + |(a, b)| a.mul(b), + ) + }); +} + +fn scalar_neg(g: &mut BenchmarkGroup) { + let mut rng: ThreadRng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + g.bench_function("scalar_neg", move |b| { + b.iter_with_setup(|| Scalar::random(&mut rng), |a| a.neg()) + }); +} + +fn scalar_sub(g: &mut BenchmarkGroup) { + let mut rng: ThreadRng = thread_rng(); + + g.throughput(Throughput::Elements(1)); + g.bench_function("scalar_sub", move |b| { + b.iter_with_setup( + || (Scalar::random(&mut rng), Scalar::random(&mut rng)), + // NOTE: Specifically moving 'b' in, just like the native Rust function does in Move + |(a, b)| a.sub(b), + ) + }); +} + +criterion_group!(ristretto255_benches, benchmark_groups); +criterion_main!(ristretto255_benches); diff --git a/crates/aptos-crypto/benches/secp256k1.rs b/crates/aptos-crypto/benches/secp256k1.rs new file mode 100644 index 0000000..a9efb5b --- /dev/null +++ b/crates/aptos-crypto/benches/secp256k1.rs @@ -0,0 +1,47 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +#[macro_use] +extern crate criterion; + +use aptos_crypto::test_utils::random_bytes; +use criterion::{measurement::Measurement, BenchmarkGroup, Criterion, Throughput}; +use rand::{prelude::ThreadRng, thread_rng}; + +fn benchmark_groups(c: &mut Criterion) { + let mut group = c.benchmark_group("secp256k1"); + + group.sample_size(1000); + + ecdsa_recover(&mut group); + + group.finish(); +} + +/// Benchmarks the time to verify a signature. (Used for gas estimation.) +fn ecdsa_recover(g: &mut BenchmarkGroup) { + let mut csprng: ThreadRng = thread_rng(); + + let sk_bytes = random_bytes(&mut csprng, 32); + let secret_key = libsecp256k1::SecretKey::parse_slice(&sk_bytes[..]).unwrap(); + let pub_key = libsecp256k1::PublicKey::from_secret_key(&secret_key); + + g.throughput(Throughput::Elements(1)); + g.bench_function("ecdsa_recover", move |b| { + b.iter_with_setup( + || { + let bytes = random_bytes(&mut csprng, 32); + let msg = libsecp256k1::Message::parse_slice(&bytes[..]).unwrap(); + let sig = libsecp256k1::sign(&msg, &secret_key); + (sig, msg) + }, + |((sig, recovery_id), msg)| { + let pk = libsecp256k1::recover(&msg, &sig, &recovery_id).unwrap(); + assert_eq!(pk.serialize(), pub_key.serialize()); + }, + ) + }); +} + +criterion_group!(secp256k1_benches, benchmark_groups); +criterion_main!(secp256k1_benches); diff --git a/crates/aptos-crypto/proptest-regressions/unit_tests/ed25519_test.txt b/crates/aptos-crypto/proptest-regressions/unit_tests/ed25519_test.txt new file mode 100644 index 0000000..03ff0fa --- /dev/null +++ b/crates/aptos-crypto/proptest-regressions/unit_tests/ed25519_test.txt @@ -0,0 +1,7 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc ec13da969415616146d63ef5719c1b85edeaf577302acaedf74a9d9a6a713947 # shrinks to keypair = 20c93f42f85ddd35b05921dd19579d8d8e6d7d83003a93f1b319c14ba831871284203ef96d6c355e386860f9fe4b0bff9df8254c5866025c594ce57674111597b693 diff --git a/crates/aptos-crypto/proptest-regressions/unit_tests/p256_test.txt b/crates/aptos-crypto/proptest-regressions/unit_tests/p256_test.txt new file mode 100644 index 0000000..b6550c3 --- /dev/null +++ b/crates/aptos-crypto/proptest-regressions/unit_tests/p256_test.txt @@ -0,0 +1,14 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc 539e60874bc331d8b4664a1cc6cc906579f5fb3547428adbc6cbeea2a28e31ea # shrinks to keypair = 2023e235b5da887e01fab303e0cfffc959952c02d787bb2dfca7163fd05e0205ca41046a6b82adba722f31d51689afba24566a94ec37fc32d0ff520a5e6287fe75cd8271ea1489a22ee93b8568526fa93589ece0301fb9312cde1ef2778feaeb5c9168 +cc 84b533dd1f5cd9430aa1d8428646fbb73e1ee314be8317faa448117c680c1606 # shrinks to keypair = 2071a0cbcf6abcc57ac335c7b93ba478ed6b3eec8fb2c03761ef5f872696b38d334104cd791851816b4d18c8da299442a924fd8a7b86a5093b026d0052820560ece21df8149f42fed9460a04aeedc55630f4fcc08744fee4eaf9a3b6025ffc2b5cd73a +cc 1f0606f82d939fa43037a1ae1e104a6af5fcb17495a60acb727bc4d434c7ebc5 # shrinks to message = TestAptosCrypto("𐛚"), keypair = 20fca559cbb362e02384a4b09da66e5704131fcae30f0d4c4f7280bd7b6dc0f1ae4104ffa0b95e042e600449c5512db882b8698038149b3774254b2f554a289a99fe913c4a16bde282e3c27303597a9f63f7a6741efba82535cc42f65e7377224e71a9 +cc aa831748c8a3c9291a059cf7f146c8953a6d40ca772d417bc048a56d113ef947 # shrinks to x = 2114806089028681516, keypair = 200c125a0ee13b47d0248b551ad5a60bfd282d7c3189f9c411bc403765cad0c9ef4104dd6ad49f1f4162d22aba9c60f06ed68318f6538247b7fdfa99f05bd91ebf98daad39422a5d438ede4f0425acf20066a7aa53a2f171e885cf5365047ec4dbd09b +cc 482c9720ccab5338952c69ef05921d0a515c4cc1b412940383104cb580160fcf # shrinks to message = TestAptosCrypto(""), keypairs = [208b5526f3f5713933435a7cf9845720731661a848a100e35c90e0b39bf2b5951841040fd99726d76fae26119a5944808f344c2a78827b191b4762a8da4c4af78a1e2c39dfbb0cd47daf67781fd4844b706ffafc661fc9754a626b5820db299b6d7bce, 20afea7aa686b48be0949edc4eeffdaaac21d809a6b4bc2b04ea3ef289baf2b0d441041640a5468342d07b90246e2e3dc7b0c42c040f72e43dfc23e56531e3605190e1744632e74189d926f753801ae7df2951f4980e1bb8991c2cc9bdde48a79bcb3c, 205362b2d8ee3e3a45610b6d6827be860cef542fa6cfd04c5a3179742acb44201e4104a7c5b9892202dedbbc0c62da87ddf87bc353f6d8fc504c32aaab0475906165864edc3172aae096255787c8cd771a7b0a9a50cf00ffe6983271cdd7f55e209283, 20dc03d77ece8f3c465eee9251debed8caacf085c1d9db798f180dbf3ad2cb466741045b10ee95d307da8855743c537f9220caa1b9c46fa38dd1cea04bb376e034e3a4a68d227dbd81529cb794cf518ab45e64bb03bf4ad17fba5b5895ad463f91432b, 20bf72e06870b080ecb993f582b890e1622e0d74e675f5e861b5fa4678f3d520624104d6524bfb7ef7d0537b8430a923e6ce0d0c601725b611a9c845a4144e522a45079da32127ad93903c2243219458f61d4faa84165e936e9a1e9e3eb197a5f8cbc3, 200117adf8da18d89d85920057dfe74b8fc984aae97f77b98f721473c3ebbcc63e4104d24e193cd8507b68295dd68df185c4ebac4b976f43dd9af8bd31bc2290211bd9ab455c8ba1d9d0760ff4d896e90399ff175bb731d355084e01901805caae397b, 204771b8a594584e6bc2d74fdd0889c50074efdcb9db77ec3868736cda0244ab984104683ec98cd47981a6c9fd4dff14d834aff1e6d670f0de4c622ba6d2a733f1970b296fac0d03ab9afc1e11d6667259199705897befd74b4307d32fe7615c234582, 201a01076284a879796d503e73e8865b0aea6cb3d41d3189b121ab84933a86166b41045b59bc55ab9f39404c59692b0f37bbf856c26959c1b9d3265ae2d585eb9aebae950f39d0ae1a0811cc8bc04782d53ee3cb12ab4511576b58a12a689a3780e303, 20829bcb79633ec7a761110499bb6f1b100880e489a27bb2e2e10a3771dbbc35294104cfa6f78c73a5026508819797d2e725c045eef7ee8217b95dbca078e46bc624024fb8287c5e23a8da0e1bc7c42d54323f69c1fd98e80310287f98642af2917dbe, 2088336ef311e8adc58a77bf41dbe24a1cd3a6a125302aa6f7ef4a489e12dc6622410450cf7c8ee6778b6e92ab0e520f1c4cb6ff7df86772e8c1c9f33066193caed9bc305bac4fa8e3b6c187975608d3ccdbaaa95dc1baa732e89e848e2b049dd3e6e3] +cc 19487dcfe0f0e04e72c2065fb69c14ebd14adb8ea6ff9d70e1985ea325538fa1 # shrinks to msg = [65, 247, 56, 10, 230, 149, 73, 158, 144, 90, 166, 195, 70, 177, 6, 252, 86, 41, 56, 120, 40, 240, 224, 40, 127, 4, 219, 180, 253, 43, 93, 10, 217, 145, 155, 191, 132, 87, 87, 14, 169, 89, 206, 34, 39, 231, 149, 131, 204, 89, 79, 195, 9, 42, 62, 54, 63, 153, 183, 218, 198, 40, 131, 184, 228, 171, 68, 78, 46, 247, 38, 61, 109, 181, 46, 231, 174, 225, 187, 185, 71, 11, 197, 236, 239], keypair = 208f6aa70033d75374c9a898a184676eea8aa68060bdd01b889f2e046ee96d8cbb4104fe66b01f689f42071baf915ae70e75f75349b78fa320abe6c8109a88f678f04de9f5570372371d88dba0d377b37f663f2338aa63f21ca865d7e9ba56d71e66c6 +cc c0d5a00a2bfb7c18d4188babea8347983ad0b3027e5e4fd3c512ca1bab1bf417 # shrinks to message = TestAptosCrypto("&\"��9&𑥕\u{1d1ac}�₡"), keypair = 20f2ec360701a1027c67f237a8ced9e21d6ae058096435ee0032d5433c9997fca1410406928fcae4da8fbc63dcea3d60f4951fda1a9e17bc59ffde55e0d65de32684d75aeed4177885916e48713179a656e50a5b107372b53d10d25c885bbac1c270be +cc 8b646e2a1ab153933cafb58c017e87f0151c4f7c8438ca74ff34b0b3192f33aa # shrinks to message = TestAptosCrypto("Iೲೀ𑤷ລ\\<\u{1ac1}𖮈¥𞺧?xÁ𛅕T.$s:ዃ/?Ὅ\\<*יּ𐭜𐠈"), keypair = 20e83972f72633e4acce41281cd87c99267966dd0bae421dde66f5e1e8ffdfc78d4104af06935d9a454ed97a0471a2338a2273619cb094774a696bc9faeabcbd067f46cdf3ea6fd7e31375a779c54053c3b111ed5ebd3ae719b3611141b5c97b8033d5 diff --git a/crates/aptos-crypto/proptest-regressions/validatable.txt b/crates/aptos-crypto/proptest-regressions/validatable.txt new file mode 100644 index 0000000..b63af1f --- /dev/null +++ b/crates/aptos-crypto/proptest-regressions/validatable.txt @@ -0,0 +1,7 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc 77446f528abcb9c465e6aca665bd6b12fb222cf626f318f797e8c1da36ab1001 # shrinks to keypair = 20019122bb4967c043bfc03ec8978ebd6d3256569c4449d3fecd19ab6309636ae0208cc1429ae6353e776a76aaa23c09f188e901d24521544df0beddfe4b0bf09684 diff --git a/crates/aptos-crypto/src/asymmetric_encryption/elgamal_curve25519_aes256_gcm.rs b/crates/aptos-crypto/src/asymmetric_encryption/elgamal_curve25519_aes256_gcm.rs new file mode 100644 index 0000000..6ccc498 --- /dev/null +++ b/crates/aptos-crypto/src/asymmetric_encryption/elgamal_curve25519_aes256_gcm.rs @@ -0,0 +1,168 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + asymmetric_encryption::AsymmetricEncryption, + elgamal, + elgamal::{curve25519::Curve25519, ElGamalFriendlyGroup}, +}; +//TODO: see if we can reuse crate `ring`. +use aes_gcm::{ + aead::{ + rand_core::{CryptoRng as AeadCryptoRng, RngCore as AeadRngCore}, + Aead, Nonce, + }, + AeadCore, Aes256Gcm, Key, KeyInit, +}; +use anyhow::{anyhow, ensure}; +use curve25519_dalek::{ + edwards::{CompressedEdwardsY, EdwardsPoint}, + scalar::Scalar, +}; +use rand_core::{CryptoRng, RngCore}; +use sha3::{Digest, Sha3_256}; + +/// An asymmetric encryption which: +/// - uses AES-256-GCM to encrypt the original variable-length input, where the symmetric key is freshly sampled; +/// - uses ElGamal over the group that supports ED25519 signatures to encrypt the symmetric key. +pub struct ElGamalCurve25519Aes256Gcm {} + +impl ElGamalCurve25519Aes256Gcm { + fn hash_group_element_to_aes_key(element: &CompressedEdwardsY) -> Vec { + let mut hasher = Sha3_256::new(); + hasher.update(b"DST__AES_KEY_DERIVATION"); + hasher.update(element.to_bytes()); + hasher.finalize().to_vec() + } +} + +const SCHEME_NAME: &str = "ElGamalCurve25519Aes256Gcm"; + +impl AsymmetricEncryption for ElGamalCurve25519Aes256Gcm { + type PrivateKey = Scalar; + type PublicKey = EdwardsPoint; + + fn scheme_name() -> String { + SCHEME_NAME.to_string() + } + + fn key_gen(rng: &mut R) -> (Scalar, EdwardsPoint) { + elgamal::key_gen::(rng) + } + + fn enc( + main_rng: &mut R1, + aead_rng: &mut R2, + pk: &EdwardsPoint, + msg: &[u8], + ) -> anyhow::Result> { + ensure!( + pk.is_torsion_free(), + "ElGamalCurve25519Aes256Gcm enc failed with non-prime-order PK" + ); + + let aes_key_g1 = Curve25519::rand_element(main_rng); + let (elgamal_ciphertext_0, elgamal_ciphertext_1) = + elgamal::encrypt::(main_rng, pk, &aes_key_g1); + let aes_key_bytes = Self::hash_group_element_to_aes_key(&aes_key_g1.compress()); + let key = Key::::from_slice(aes_key_bytes.as_slice()); + let cipher = Aes256Gcm::new(key); + let nonce = Aes256Gcm::generate_nonce(aead_rng); + let nonce_bytes = nonce.to_vec(); + ensure!( + 12 == nonce_bytes.len(), + "ElGamalCurve25519Aes256Gcm enc failed with unexpected nonce len" + ); + + let aes_ciphertext = cipher.encrypt(&nonce, msg.as_ref()).map_err(|e| { + anyhow!( + "ElGamalCurve25519Aes256Gcm enc failed with aes error: {}", + e + ) + })?; + + let elgamal_ciphertext_0_bytes = elgamal_ciphertext_0.compress().to_bytes().to_vec(); + let elgamal_ciphertext_1_bytes = elgamal_ciphertext_1.compress().to_bytes().to_vec(); + + let serialized = [ + elgamal_ciphertext_0_bytes, // 32 bytes + elgamal_ciphertext_1_bytes, // 32 bytes + nonce_bytes, // 12 bytes + aes_ciphertext, // variable length + ] + .concat(); + + Ok(serialized) + } + + fn dec(sk: &Scalar, ciphertext: &[u8]) -> anyhow::Result> { + ensure!( + ciphertext.len() >= 76, + "ElGamalCurve25519Aes256Gcm dec failed with invalid ciphertext length" + ); + let c0 = CompressedEdwardsY::from_slice(&ciphertext[0..32]) + .decompress() + .ok_or_else(|| { + anyhow!("ElGamalCurve25519Aes256Gcm dec failed with invalid c0 element") + })?; + + ensure!( + c0.is_torsion_free(), + "ElGamalCurve25519Aes256Gcm dec failed with non-prime-order c0" + ); + + let c1 = CompressedEdwardsY::from_slice(&ciphertext[32..64]) + .decompress() + .ok_or_else(|| { + anyhow!("ElGamalCurve25519Aes256Gcm dec failed with invalid c1 element") + })?; + + ensure!( + c1.is_torsion_free(), + "ElGamalCurve25519Aes256Gcm dec failed with non-prime-order c1" + ); + + let aes_key_element = elgamal::decrypt::(sk, &c0, &c1).compress(); + let aes_key_bytes = Self::hash_group_element_to_aes_key(&aes_key_element); + let key = Key::::from_slice(aes_key_bytes.as_slice()); + let cipher = Aes256Gcm::new(key); + let nonce = Nonce::::from_slice(&ciphertext[64..76]); + let plaintext = cipher.decrypt(nonce, &ciphertext[76..]).map_err(|e| { + anyhow!("ElGamalCurve25519Aes256Gcm dec failed with aes decryption error: {e}") + })?; + Ok(plaintext) + } +} + +#[cfg(test)] +mod tests { + use crate::asymmetric_encryption::{ + elgamal_curve25519_aes256_gcm::ElGamalCurve25519Aes256Gcm, AsymmetricEncryption, + }; + + #[test] + fn gen_enc_dec() { + let mut main_rng = rand_core::OsRng; + let mut aead_rng = aes_gcm::aead::OsRng; + let (sk, pk) = ElGamalCurve25519Aes256Gcm::key_gen(&mut main_rng); + let msg = b"hello world again and again and again and again and again and again and again" + .to_vec(); + let ciphertext = + ElGamalCurve25519Aes256Gcm::enc(&mut main_rng, &mut aead_rng, &pk, msg.as_slice()) + .unwrap(); + assert_eq!( + msg, + ElGamalCurve25519Aes256Gcm::dec(&sk, ciphertext.as_slice()).unwrap() + ); + + // Empty message should also work. + let msg = b"".to_vec(); + let ciphertext = + ElGamalCurve25519Aes256Gcm::enc(&mut main_rng, &mut aead_rng, &pk, msg.as_slice()) + .unwrap(); + assert_eq!( + msg, + ElGamalCurve25519Aes256Gcm::dec(&sk, ciphertext.as_slice()).unwrap() + ); + } +} diff --git a/crates/aptos-crypto/src/asymmetric_encryption/mod.rs b/crates/aptos-crypto/src/asymmetric_encryption/mod.rs new file mode 100644 index 0000000..ab33455 --- /dev/null +++ b/crates/aptos-crypto/src/asymmetric_encryption/mod.rs @@ -0,0 +1,39 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +//! This module provides asymmetric encryption traits and instances. + +use aes_gcm::aead::rand_core::{CryptoRng as AeadCryptoRng, RngCore as AeadRngCore}; +use rand_core::{CryptoRng, RngCore}; + +/// Implement this to define an asymmetric encryption scheme. +pub trait AsymmetricEncryption { + /// A.k.a the decrypt key. + type PrivateKey; + + /// A.k.a the encrypt key. + type PublicKey; + + /// The name of the scheme. + fn scheme_name() -> String; + + /// Generate a key pair. Return `(private_key, public_key)`. + fn key_gen(rng: &mut R) -> (Self::PrivateKey, Self::PublicKey); + + /// The encryption algorithm. + /// TODO: adjust the dependencies so they can share a RNG. + fn enc( + rng: &mut R1, + aead_rng: &mut R2, + pk: &Self::PublicKey, + msg: &[u8], + ) -> anyhow::Result>; + + /// The decryption algorithm. + fn dec(sk: &Self::PrivateKey, ciphertext: &[u8]) -> anyhow::Result>; +} + +/// An asymmetric encryption which: +/// - uses AES-256-GCM to encrypt the original variable-length input, where the symmetric key is freshly sampled; +/// - uses ElGamal over the group that supports ED25519 signatures to encrypt the symmetric key. +pub mod elgamal_curve25519_aes256_gcm; diff --git a/crates/aptos-crypto/src/bls12381/bls12381_keys.rs b/crates/aptos-crypto/src/bls12381/bls12381_keys.rs new file mode 100644 index 0000000..e6e95ed --- /dev/null +++ b/crates/aptos-crypto/src/bls12381/bls12381_keys.rs @@ -0,0 +1,293 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +//! This module provides APIs for private keys and public keys used in Boneh-Lynn-Shacham (BLS) +//! aggregate signatures (including individual signatures and multisignatures) implemented on top of +//! Barreto-Lynn-Scott BLS12-381 elliptic curves (). +//! +//! The `PublicKey` struct is used to represent both the public key of an individual signer +//! as well as the aggregate public key of several signers. Before passing this struct as an +//! argument, the caller should *always* verify its proof-of-possession (PoP) via +//! `ProofOfPossession::verify`. +//! +//! The `PublicKey::aggregate` API assumes the caller has already verified +//! proofs-of-possession for all the given public keys and therefore all public keys are valid, +//! prime-order subgroup elements. +//! +//! In general, with the exception of `ProofOfPossession::verify` no library function should +//! be given a public key as argument without first verifying that public key's PoP. Note that +//! for aggregate public keys obtained via `PublicKey::aggregate` there is no PoP to verify, but +//! the security assumption will be that all public keys given as input to this function have had +//! their PoPs verified. + +use crate::{ + bls12381, bls12381::DST_BLS_SIG_IN_G2_WITH_POP, hash::CryptoHash, signing_message, traits, + CryptoMaterialError, Genesis, Length, Uniform, ValidCryptoMaterial, + ValidCryptoMaterialStringExt, VerifyingKey, +}; +use anyhow::{anyhow, Result}; +use aptos_crypto_derive::{DeserializeKey, SerializeKey, SilentDebug, SilentDisplay}; +use serde::Serialize; +use std::{convert::TryFrom, fmt}; + +#[derive(Clone, Eq, SerializeKey, DeserializeKey)] +/// A BLS12381 public key +pub struct PublicKey { + pub(crate) pubkey: blst::min_pk::PublicKey, + // NOTE: In order to minimize the size of this struct, we do not keep the PoP here. + // One reason for this is these PKs are stored in the root of the Merkle accumulator. +} + +#[derive(SerializeKey, DeserializeKey, SilentDebug, SilentDisplay)] +/// A BLS12381 private key +pub struct PrivateKey { + pub(crate) privkey: blst::min_pk::SecretKey, +} + +////////////////////////////////////////////////////// +// Implementation of public-and-private key structs // +////////////////////////////////////////////////////// + +impl PublicKey { + /// The length of a serialized PublicKey struct. + // NOTE: We have to hardcode this here because there is no library-defined constant. + pub const LENGTH: usize = 48; + + /// Serialize a PublicKey. + pub fn to_bytes(&self) -> [u8; Self::LENGTH] { + self.pubkey.to_bytes() + } + + /// Subgroup-checks the public key (i.e., verifies the public key is an element of the prime-order + /// subgroup and it is not the identity element). + /// + /// WARNING: Subgroup-checking is done implicitly when verifying the proof-of-possession (PoP) for + /// this public key in `ProofOfPossession::verify`, so this function should not be called + /// separately for most use-cases. We leave it here just in case. + pub fn subgroup_check(&self) -> Result<()> { + self.pubkey.validate().map_err(|e| anyhow!("{:?}", e)) + } + + /// Aggregates the public keys of several signers into an aggregate public key, which can be later + /// used to verify a multisig aggregated from those signers. + /// + /// WARNING: This function assumes all public keys have had their proofs-of-possession verified + /// and have thus been group-checked. + pub fn aggregate(pubkeys: Vec<&Self>) -> Result { + let blst_pubkeys: Vec<_> = pubkeys.iter().map(|pk| &pk.pubkey).collect(); + + // CRYPTONOTE(Alin): We assume the PKs have had their PoPs verified and thus have also been subgroup-checked + let aggpk = blst::min_pk::AggregatePublicKey::aggregate(&blst_pubkeys[..], false) + .map_err(|e| anyhow!("{:?}", e))?; + + Ok(PublicKey { + pubkey: aggpk.to_public_key(), + }) + } +} + +impl PrivateKey { + /// The length of a serialized PrivateKey struct. + // NOTE: We have to hardcode this here because there is no library-defined constant + pub const LENGTH: usize = 32; + + /// Serialize a PrivateKey. + pub fn to_bytes(&self) -> [u8; Self::LENGTH] { + self.privkey.to_bytes() + } +} + +/////////////////////// +// PrivateKey Traits // +/////////////////////// + +impl traits::PrivateKey for PrivateKey { + type PublicKeyMaterial = PublicKey; +} + +impl traits::SigningKey for PrivateKey { + type SignatureMaterial = bls12381::Signature; + type VerifyingKeyMaterial = PublicKey; + + fn sign( + &self, + message: &T, + ) -> Result { + Ok(bls12381::Signature { + sig: self + .privkey + .sign(&signing_message(message)?, DST_BLS_SIG_IN_G2_WITH_POP, &[]), + }) + } + + #[cfg(any(test, feature = "fuzzing"))] + fn sign_arbitrary_message(&self, message: &[u8]) -> bls12381::Signature { + bls12381::Signature { + sig: self.privkey.sign(message, DST_BLS_SIG_IN_G2_WITH_POP, &[]), + } + } +} + +impl traits::ValidCryptoMaterial for PrivateKey { + fn to_bytes(&self) -> Vec { + self.to_bytes().to_vec() + } +} + +impl Length for PrivateKey { + fn length(&self) -> usize { + Self::LENGTH + } +} + +impl TryFrom<&[u8]> for PrivateKey { + type Error = CryptoMaterialError; + + /// Deserializes a PrivateKey from a sequence of bytes. + fn try_from(bytes: &[u8]) -> std::result::Result { + Ok(Self { + privkey: blst::min_pk::SecretKey::from_bytes(bytes) + .map_err(|_| CryptoMaterialError::DeserializationError)?, + }) + } +} + +impl Uniform for PrivateKey { + fn generate(rng: &mut R) -> Self + where + R: ::rand::RngCore + ::rand::CryptoRng, + { + // CRYPTONOTE(Alin): This "initial key material (IKM)" is the randomness used inside key_gen + // below to pseudo-randomly derive the secret key via an HKDF + // (see ) + let mut ikm = [0u8; 32]; + rng.fill_bytes(&mut ikm); + let privkey = + blst::min_pk::SecretKey::key_gen(&ikm, &[]).expect("ikm length should be higher"); + Self { privkey } + } +} + +impl Genesis for PrivateKey { + fn genesis() -> Self { + let mut buf = [0u8; Self::LENGTH]; + buf[Self::LENGTH - 1] = 1; + Self::try_from(buf.as_ref()).unwrap() + } +} + +#[cfg(feature = "assert-private-keys-not-cloneable")] +static_assertions::assert_not_impl_any!(PrivateKey: Clone); + +#[cfg(any(test, feature = "cloneable-private-keys"))] +impl Clone for PrivateKey { + fn clone(&self) -> Self { + let serialized: &[u8] = &(self.to_bytes()); + PrivateKey::try_from(serialized).unwrap() + } +} + +////////////////////// +// PublicKey Traits // +////////////////////// + +impl From<&PrivateKey> for PublicKey { + fn from(private_key: &PrivateKey) -> Self { + Self { + pubkey: private_key.privkey.sk_to_pk(), + } + } +} + +impl traits::PublicKey for PublicKey { + type PrivateKeyMaterial = PrivateKey; +} + +impl VerifyingKey for PublicKey { + type SignatureMaterial = bls12381::Signature; + type SigningKeyMaterial = PrivateKey; +} + +impl ValidCryptoMaterial for PublicKey { + fn to_bytes(&self) -> Vec { + self.to_bytes().to_vec() + } +} + +impl Length for PublicKey { + fn length(&self) -> usize { + Self::LENGTH + } +} + +impl TryFrom<&[u8]> for PublicKey { + type Error = CryptoMaterialError; + + /// Deserializes a PublicKey from a sequence of bytes. + /// + /// WARNING: Does NOT subgroup-check the public key! Instead, the caller is responsible for + /// verifying the public key's proof-of-possession (PoP) via `ProofOfPossession::verify`, + /// which implicitly subgroup-checks the public key. + /// + /// NOTE: This function will only check that the PK is a point on the curve: + /// - `blst::min_pk::PublicKey::from_bytes(bytes)` calls `blst::min_pk::PublicKey::deserialize(bytes)`, + /// which calls `$pk_deser` in , + /// which is mapped to `blst_p1_deserialize` in + /// - `blst_p1_deserialize` eventually calls `POINTonE1_Deserialize_BE`, which checks + /// the point is on the curve: + fn try_from(bytes: &[u8]) -> std::result::Result { + Ok(Self { + pubkey: blst::min_pk::PublicKey::from_bytes(bytes) + .map_err(|_| CryptoMaterialError::DeserializationError)?, + }) + } +} + +impl std::hash::Hash for PublicKey { + fn hash(&self, state: &mut H) { + let encoded_pubkey = self.to_bytes(); + state.write(&encoded_pubkey); + } +} + +// PartialEq trait implementation is required by the std::hash::Hash trait implementation above +impl PartialEq for PublicKey { + fn eq(&self, other: &Self) -> bool { + self.to_bytes()[..] == other.to_bytes()[..] + } +} + +impl fmt::Debug for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", hex::encode(self.to_bytes())) + } +} + +impl fmt::Display for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", hex::encode(self.to_bytes())) + } +} + +#[cfg(any(test, feature = "fuzzing"))] +use crate::test_utils::KeyPair; +#[cfg(any(test, feature = "fuzzing"))] +use proptest::prelude::*; + +/// Produces a uniformly random BLS keypair from a seed +#[cfg(any(test, feature = "fuzzing"))] +pub fn keypair_strategy() -> impl Strategy> { + crate::test_utils::uniform_keypair_strategy::() +} + +#[cfg(any(test, feature = "fuzzing"))] +impl proptest::arbitrary::Arbitrary for PublicKey { + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + crate::test_utils::uniform_keypair_strategy::() + .prop_map(|v| v.public_key) + .boxed() + } +} diff --git a/crates/aptos-crypto/src/bls12381/bls12381_pop.rs b/crates/aptos-crypto/src/bls12381/bls12381_pop.rs new file mode 100644 index 0000000..146984f --- /dev/null +++ b/crates/aptos-crypto/src/bls12381/bls12381_pop.rs @@ -0,0 +1,158 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +//! This module provides APIs for _proofs-of-possesion (PoPs)_ used to prevent _rogue-key attacks_, +//! both for multisignatures and aggregate signatures. +//! +//! Rogue-key attacks were first introduced by Micali, Ohta and Reyzin [^MOR01] and PoPs were first +//! introduced by Ristenpart and Yilek [^RY07]. +//! +//! [^MOR01]: Accountable-Subgroup Multisignatures: Extended Abstract; by Micali, Silvio and Ohta, Kazuo and Reyzin, Leonid; in Proceedings of the 8th ACM Conference on Computer and Communications Security; 2001; +//! [^RY07]: The Power of Proofs-of-Possession: Securing Multiparty Signatures against Rogue-Key Attacks; by Ristenpart, Thomas and Yilek, Scott; in Advances in Cryptology - EUROCRYPT 2007; 2007 + +use crate::{ + bls12381::bls12381_keys::{PrivateKey, PublicKey}, + CryptoMaterialError, Length, ValidCryptoMaterial, ValidCryptoMaterialStringExt, +}; +use anyhow::{anyhow, Result}; +use aptos_crypto_derive::{DeserializeKey, SerializeKey}; +use blst::BLST_ERROR; +use std::{convert::TryFrom, fmt}; + +/// Domain separation tag (DST) for hashing a public key before computing its proof-of-possesion (PoP), +/// which is also just a signature. +pub const DST_BLS_POP_IN_G2: &[u8] = b"BLS_POP_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"; + +#[derive(Clone, Eq, SerializeKey, DeserializeKey)] +/// A proof-of-possesion (PoP) of a BLS12381 private key. +/// This is just a BLS signature on the corresponding public key. +pub struct ProofOfPossession { + pub(crate) pop: blst::min_pk::Signature, +} + +impl ProofOfPossession { + /// The length of a serialized ProofOfPossession struct. + // NOTE: We have to hardcode this here because there is no library-defined constant + pub const LENGTH: usize = 96; + + /// Serialize a ProofOfPossession. + pub fn to_bytes(&self) -> [u8; Self::LENGTH] { + self.pop.to_bytes() + } + + /// Subgroup-check the PoP (i.e., verifies the PoP is a valid group element). + /// + /// WARNING: Subgroup-checking is done implicitly in `verify` below, so this function need not be called + /// separately for most use-cases, as it incurs a performance penalty. We leave it here just in case. + pub fn subgroup_check(&self) -> Result<()> { + self.pop.validate(true).map_err(|e| anyhow!("{:?}", e)) + } + + /// Verifies the proof-of-possesion (PoP) of the private key corresponding to the specified + /// BLS public key. Implicitly, subgroup checks the PoP and the specified public key, so + /// the caller is not responsible for doing it manually. + pub fn verify(&self, pk: &PublicKey) -> Result<()> { + // CRYPTONOTE(Alin): We call the signature verification function with pk_validate set to true + // since we do not necessarily trust the PK we deserialized over the network whose PoP we are + // verifying here. + let result = self.pop.verify( + true, + &pk.to_bytes(), + DST_BLS_POP_IN_G2, + &[], + &pk.pubkey, + true, + ); + if result == BLST_ERROR::BLST_SUCCESS { + Ok(()) + } else { + Err(anyhow!( + "Proof-of-possession (PoP) did NOT verify: {:?}", + result + )) + } + } + + /// Creates a proof-of-possesion (PoP) of the specified BLS private key. This function + /// inefficiently recomputes the public key from the private key. To avoid this, the caller can + /// use `create_with_pubkey` instead, which accepts the public key as a second input. + pub fn create(sk: &PrivateKey) -> ProofOfPossession { + // CRYPTONOTE(Alin): The standard does not detail how the PK should be serialized for hashing purposes; we just do the obvious. + let pk = PublicKey { + pubkey: sk.privkey.sk_to_pk(), + }; + + ProofOfPossession::create_with_pubkey(sk, &pk) + } + + /// Creates a proof-of-possesion (PoP) of the specified BLS private key. Takes the + /// corresponding public key as input, to avoid inefficiently recomputing it from the + /// private key. + /// + /// WARNING: Does not subgroup-check the PK, since this function will be typically called on + /// a freshly-generated key-pair or on a correctly-deserialized keypair. + pub fn create_with_pubkey(sk: &PrivateKey, pk: &PublicKey) -> ProofOfPossession { + // CRYPTONOTE(Alin): The standard does not detail how the PK should be serialized for hashing purposes; we just do the obvious. + let pk_bytes = pk.to_bytes(); + + // CRYPTONOTE(Alin): We hash with DST_BLS_POP_IN_G2 as per https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature#section-4.2.3 + ProofOfPossession { + pop: sk.privkey.sign(&pk_bytes, DST_BLS_POP_IN_G2, &[]), + } + } +} + +////////////////////////////// +// ProofOfPossession Traits // +////////////////////////////// + +impl ValidCryptoMaterial for ProofOfPossession { + fn to_bytes(&self) -> Vec { + self.to_bytes().to_vec() + } +} +impl Length for ProofOfPossession { + fn length(&self) -> usize { + Self::LENGTH + } +} + +impl PartialEq for ProofOfPossession { + fn eq(&self, other: &Self) -> bool { + self.pop.to_bytes() == other.to_bytes() + } +} + +impl TryFrom<&[u8]> for ProofOfPossession { + type Error = CryptoMaterialError; + + /// Deserializes a BLS PoP from a sequence of bytes. + /// + /// WARNING: Does NOT subgroup-check the PoP! This is done implicitly when verifying the PoP in + /// `ProofOfPossession::verify` + fn try_from(bytes: &[u8]) -> std::result::Result { + Ok(Self { + pop: blst::min_pk::Signature::from_bytes(bytes) + .map_err(|_| CryptoMaterialError::DeserializationError)?, + }) + } +} + +impl std::hash::Hash for ProofOfPossession { + fn hash(&self, state: &mut H) { + let encoded_signature = self.to_bytes(); + state.write(&encoded_signature); + } +} + +impl fmt::Debug for ProofOfPossession { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", hex::encode(self.to_bytes())) + } +} + +impl fmt::Display for ProofOfPossession { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", hex::encode(self.to_bytes())) + } +} diff --git a/crates/aptos-crypto/src/bls12381/bls12381_sigs.rs b/crates/aptos-crypto/src/bls12381/bls12381_sigs.rs new file mode 100644 index 0000000..8331042 --- /dev/null +++ b/crates/aptos-crypto/src/bls12381/bls12381_sigs.rs @@ -0,0 +1,223 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +//! This module provides APIs for aggregating and verifying Boneh-Lynn-Shacham (BLS) aggregate +//! signatures (including individual signatures and multisignatures), implemented on top of +//! Barreto-Lynn-Scott BLS12-381 elliptic curves (. +//! +//! The `Signature` struct is used to represent either a: +//! +//! 1. signature share from an individual signer +//! 2. multisignature on a single message from many signers +//! 3. aggregate signature on different messages from many signers +//! +//! The signature verification APIs in `Signature::verify`, `Signature::verify_arbitrary_msg`, +//! `Signature::verify_aggregate` and `Signature::verify_aggregate_arbitrary_msg` do NOT +//! assume the signature to be a valid group element and will implicitly "subgroup-check" it. This +//! makes the caller's job easier and, more importantly, makes the library safer to use. + +use crate::{ + bls12381::{ + bls12381_keys::{PrivateKey, PublicKey}, + DST_BLS_SIG_IN_G2_WITH_POP, + }, + hash::CryptoHash, + signing_message, traits, CryptoMaterialError, Length, ValidCryptoMaterial, + ValidCryptoMaterialStringExt, +}; +use anyhow::{anyhow, Result}; +use aptos_crypto_derive::{DeserializeKey, SerializeKey}; +use blst::BLST_ERROR; +use serde::Serialize; +use std::{convert::TryFrom, fmt}; + +#[derive(Clone, Eq, SerializeKey, DeserializeKey)] +/// Either (1) a BLS signature share from an individual signer, (2) a BLS multisignature or (3) a +/// BLS aggregate signature +pub struct Signature { + pub(crate) sig: blst::min_pk::Signature, +} + +//////////////////////////////////////// +// Implementation of Signature struct // +//////////////////////////////////////// + +impl Signature { + /// The length of a serialized Signature struct. + // NOTE: We have to hardcode this here because there is no library-defined constant + pub const LENGTH: usize = 96; + + /// Serialize a Signature. + pub fn to_bytes(&self) -> [u8; Self::LENGTH] { + self.sig.to_bytes() + } + + /// Subgroup-checks the signature (i.e., verifies the signature is a valid group element). + /// + /// WARNING: Subgroup-checking is done implicitly when verifying signatures via + /// `Signature::verify_arbitrary_msg`. Therefore, this function should not be called separately + /// for most use-cases. We leave it here just in case. + pub fn subgroup_check(&self) -> Result<()> { + self.sig.validate(true).map_err(|e| anyhow!("{:?}", e)) + } + + /// Optimistically-aggregate signatures shares into either (1) a multisignature or (2) an aggregate + /// signature. The individual signature shares could be adversarial. Nonetheless, for performance + /// reasons, we do not subgroup-check the signature shares here, since the verification of the + /// returned multi-or-aggregate signature includes such a subgroup check. As a result, adversarial + /// signature shares cannot lead to forgeries. + pub fn aggregate(sigs: Vec) -> Result { + let sigs: Vec<_> = sigs.iter().map(|s| &s.sig).collect(); + let agg_sig = blst::min_pk::AggregateSignature::aggregate(&sigs[..], false) + .map_err(|e| anyhow!("{:?}", e))?; + Ok(Signature { + sig: agg_sig.to_signature(), + }) + } + + /// Verifies an aggregate signature on the messages in `msgs` under the public keys in `pks`. + /// Specifically, verifies that each `msgs[i]` is signed under `pks[i]`. The messages in `msgs` + /// do *not* have to be all different, since we use proofs-of-possession (PoPs) to prevent rogue + /// key attacks. + /// + /// WARNING: This function assumes that the public keys have been subgroup-checked by the caller + /// implicitly when verifying their proof-of-possession (PoP) in `ProofOfPossession::verify`. + pub fn verify_aggregate_arbitrary_msg(&self, msgs: &[&[u8]], pks: &[&PublicKey]) -> Result<()> { + let pks = pks + .iter() + .map(|&pk| &pk.pubkey) + .collect::>(); + + let result = self + .sig + .aggregate_verify(true, msgs, DST_BLS_SIG_IN_G2_WITH_POP, &pks, false); + + if result == BLST_ERROR::BLST_SUCCESS { + Ok(()) + } else { + Err(anyhow!("{:?}", result)) + } + } + + /// Serializes the messages of type `T` to bytes and calls `Signature::verify_aggregate_arbitrary_msg`. + pub fn verify_aggregate( + &self, + msgs: &[&T], + pks: &[&PublicKey], + ) -> Result<()> { + let mut messages: Vec> = vec![]; + for message in msgs { + messages.push(signing_message(*message)?); + } + + let msgs_refs = messages + .iter() + .map(|m| m.as_slice()) + .collect::>(); + + self.verify_aggregate_arbitrary_msg(&msgs_refs, pks) + } + + /// Return a dummy signature for testing. + #[cfg(any(test, feature = "fuzzing"))] + pub fn dummy_signature() -> Self { + use crate::{Genesis, SigningKey}; + + let private_key = PrivateKey::genesis(); + + let msg = b"hello foo"; + private_key.sign_arbitrary_message(msg) + } +} + +/////////////////////////// +// SignatureShare Traits // +/////////////////////////// +impl traits::Signature for Signature { + type SigningKeyMaterial = PrivateKey; + type VerifyingKeyMaterial = PublicKey; + + /// Serializes the message of type `T` to bytes and calls `Signature::verify_arbitrary_msg`. + fn verify(&self, message: &T, public_key: &PublicKey) -> Result<()> { + self.verify_arbitrary_msg(&signing_message(message)?, public_key) + } + + /// Verifies a BLS signature share or multisignature. Does not assume the signature to be + /// subgroup-checked. (For verifying aggregate signatures on different messages, a different + /// `verify_aggregate_arbitray_msg` function can be used.) + /// + /// WARNING: This function does assume the public key has been subgroup-checked by the caller, + /// either (1) implicitly when verifying the public key's proof-of-possession (PoP) in + /// `ProofOfPossession::verify` or (2) via `Validatable::::validate()`. + fn verify_arbitrary_msg(&self, message: &[u8], public_key: &PublicKey) -> Result<()> { + let result = self.sig.verify( + true, + message, + DST_BLS_SIG_IN_G2_WITH_POP, + &[], + &public_key.pubkey, + false, + ); + if result == BLST_ERROR::BLST_SUCCESS { + Ok(()) + } else { + Err(anyhow!("{:?}", result)) + } + } + + fn to_bytes(&self) -> Vec { + self.to_bytes().to_vec() + } +} + +impl ValidCryptoMaterial for Signature { + fn to_bytes(&self) -> Vec { + self.to_bytes().to_vec() + } +} +impl Length for Signature { + fn length(&self) -> usize { + Self::LENGTH + } +} + +impl fmt::Debug for Signature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", hex::encode(self.to_bytes())) + } +} + +impl fmt::Display for Signature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", hex::encode(self.to_bytes())) + } +} + +impl TryFrom<&[u8]> for Signature { + type Error = CryptoMaterialError; + + /// Deserializes a Signature from a sequence of bytes. + /// + /// WARNING: Does NOT subgroup-check the signature! Instead, this will be done implicitly when + /// verifying the signature. + fn try_from(bytes: &[u8]) -> std::result::Result { + Ok(Self { + sig: blst::min_pk::Signature::from_bytes(bytes) + .map_err(|_| CryptoMaterialError::DeserializationError)?, + }) + } +} + +impl std::hash::Hash for Signature { + fn hash(&self, state: &mut H) { + let encoded_signature = self.to_bytes(); + state.write(&encoded_signature); + } +} + +// PartialEq trait implementation is required by the std::hash::Hash trait implementation above +impl PartialEq for Signature { + fn eq(&self, other: &Self) -> bool { + self.to_bytes()[..] == other.to_bytes()[..] + } +} diff --git a/crates/aptos-crypto/src/bls12381/bls12381_validatable.rs b/crates/aptos-crypto/src/bls12381/bls12381_validatable.rs new file mode 100644 index 0000000..7f444ff --- /dev/null +++ b/crates/aptos-crypto/src/bls12381/bls12381_validatable.rs @@ -0,0 +1,196 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +//! This module implements the Validate trait for BLS12-381 public keys, which enables library users +//! to make sure public keys used for verifying normal (non-aggregated) signatures lie in the prime-order +//! subgroup of the BLS12-381 group. +//! +//! NOTE: For public keys used to verify multisignatures, aggregate signatures and signature shares, +//! library users need NOT rely on this `Validatable` wrapper and should instead verify +//! the proof-of-possession (PoP) of a public key, which implicitly guarantees the PK lies in the +//! prime-order subgroup. (See `bls12381_pop.rs` and `mod.rs` for details.) + +use crate::{bls12381::PublicKey, validatable::Validate, CryptoMaterialError, ValidCryptoMaterial}; +use anyhow::{anyhow, Result}; +use serde::{Deserialize, Serialize}; +use std::{convert::TryFrom, hash::Hash}; + +/// An unvalidated `PublicKey` +#[derive(Debug, Clone, Eq)] +pub struct UnvalidatedPublicKey(pub(crate) [u8; PublicKey::LENGTH]); + +impl UnvalidatedPublicKey { + /// Return key as bytes + pub fn to_bytes(&self) -> [u8; PublicKey::LENGTH] { + self.0 + } +} + +impl TryFrom<&[u8]> for UnvalidatedPublicKey { + type Error = CryptoMaterialError; + + /// Deserializes an UnvalidatedPublicKey from a sequence of bytes. + /// + /// WARNING: Does NOT do any checks whatsoever on these bytes beyond checking the length. + /// The returned `UnvalidatedPublicKey` can only be used to create a `Validatable::` + /// via `Validatable::::from_unvalidated`. + fn try_from(bytes: &[u8]) -> std::result::Result { + if bytes.len() != PublicKey::LENGTH { + Err(CryptoMaterialError::DeserializationError) + } else { + Ok(Self(<[u8; PublicKey::LENGTH]>::try_from(bytes).unwrap())) + } + } +} + +impl ValidCryptoMaterial for UnvalidatedPublicKey { + fn to_bytes(&self) -> Vec { + self.0.to_vec() + } +} + +impl Serialize for UnvalidatedPublicKey { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + if serializer.is_human_readable() { + let encoded = ::hex::encode(self.0); + serializer.serialize_str(&format!("0x{}", encoded)) + } else { + // See comment in deserialize_key. + serializer + .serialize_newtype_struct("PublicKey", serde_bytes::Bytes::new(self.0.as_ref())) + } + } +} + +impl<'de> Deserialize<'de> for UnvalidatedPublicKey { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + use serde::de::Error; + + if deserializer.is_human_readable() { + let encoded_key = ::deserialize(deserializer)?; + let bytes_out = ::hex::decode(&encoded_key[2..]).map_err(D::Error::custom)?; + <[u8; PublicKey::LENGTH]>::try_from(bytes_out.as_ref()) + .map(UnvalidatedPublicKey) + .map_err(D::Error::custom) + } else { + // In order to preserve the Serde data model and help analysis tools, + // make sure to wrap our value in a container with the same name + // as the original type. + #[derive(Deserialize)] + #[serde(rename = "PublicKey")] + struct Value<'a>(&'a [u8]); + + let value = Value::deserialize(deserializer)?; + <[u8; PublicKey::LENGTH]>::try_from(value.0) + .map(UnvalidatedPublicKey) + .map_err(D::Error::custom) + } + } +} + +impl Hash for UnvalidatedPublicKey { + fn hash(&self, state: &mut H) { + state.write(&self.0) + } +} + +impl PartialEq for UnvalidatedPublicKey { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Validate for PublicKey { + type Unvalidated = UnvalidatedPublicKey; + + fn validate(unvalidated: &Self::Unvalidated) -> Result { + let pk = Self::try_from(unvalidated.0.as_ref())?; + + if pk.subgroup_check().is_err() { + return Err(anyhow!("{:?}", CryptoMaterialError::SmallSubgroupError)); + } + + Ok(pk) + } + + fn to_unvalidated(&self) -> Self::Unvalidated { + UnvalidatedPublicKey(self.to_bytes()) + } +} + +#[cfg(test)] +mod test { + use crate::{ + bls12381::{PrivateKey, PublicKey, UnvalidatedPublicKey}, + test_utils::uniform_keypair_strategy, + validatable::Validate, + }; + use proptest::{prop_assert_eq, proptest}; + use std::{ + collections::hash_map::DefaultHasher, + hash::{Hash, Hasher}, + }; + + proptest! { + #[test] + fn bls12381_validatable_pk( + keypair in uniform_keypair_strategy::() + ) { + let valid = keypair.public_key; + let unvalidated = valid.to_unvalidated(); + + prop_assert_eq!(&unvalidated, &UnvalidatedPublicKey(valid.to_bytes())); + prop_assert_eq!(&valid, &PublicKey::validate(&unvalidated).unwrap()); + + // Ensure Serialize and Deserialize are implemented the same + + // BCS - A non-human-readable format + { + let serialized_valid = bcs::to_bytes(&valid).unwrap(); + let serialized_unvalidated = bcs::to_bytes(&unvalidated).unwrap(); + prop_assert_eq!(&serialized_valid, &serialized_unvalidated); + + let deserialized_valid_from_unvalidated: PublicKey = bcs::from_bytes(&serialized_unvalidated).unwrap(); + let deserialized_unvalidated_from_valid: UnvalidatedPublicKey = bcs::from_bytes(&serialized_valid).unwrap(); + + prop_assert_eq!(&valid, &deserialized_valid_from_unvalidated); + prop_assert_eq!(&unvalidated, &deserialized_unvalidated_from_valid); + } + + // JSON A human-readable format + { + let serialized_valid = serde_json::to_string(&valid).unwrap(); + let serialized_unvalidated = serde_json::to_string(&unvalidated).unwrap(); + prop_assert_eq!(&serialized_valid, &serialized_unvalidated); + + let deserialized_valid_from_unvalidated: PublicKey = serde_json::from_str(&serialized_unvalidated).unwrap(); + let deserialized_unvalidated_from_valid: UnvalidatedPublicKey = serde_json::from_str(&serialized_valid).unwrap(); + + prop_assert_eq!(&valid, &deserialized_valid_from_unvalidated); + prop_assert_eq!(&unvalidated, &deserialized_unvalidated_from_valid); + } + + + // Ensure Hash is implemented the same + let valid_hash = { + let mut hasher = DefaultHasher::new(); + valid.hash(&mut hasher); + hasher.finish() + }; + + let unvalidated_hash = { + let mut hasher = DefaultHasher::new(); + unvalidated.hash(&mut hasher); + hasher.finish() + }; + + prop_assert_eq!(valid_hash, unvalidated_hash); + } + } +} diff --git a/crates/aptos-crypto/src/bls12381/mod.rs b/crates/aptos-crypto/src/bls12381/mod.rs new file mode 100644 index 0000000..4747165 --- /dev/null +++ b/crates/aptos-crypto/src/bls12381/mod.rs @@ -0,0 +1,430 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +//! This module provides APIs for Boneh-Lynn-Shacham (BLS) aggregate signatures, including +//! normal (non-aggregated) signatures and multisignatures, on top of Barreto-Lynn-Scott BLS12-381 +//! elliptic curves. This module wraps the [blst](https://github.com/supranational/blst) library. +//! +//! Our multisignature and aggregate signature implementations are described in [^BLS04], [^Bold03], +//! except we use the proof-of-possession (PoP) scheme from [^RY07] to prevent rogue-key attacks +//! [^MOR01] where malicious signers adversarially pick their public keys in order to forge a +//! multisignature or forge an aggregate signature. +//! +//! Our normal (non-aggregated) signatures implementation requires CAREFUL use by developers to +//! prevent small-subgroup attacks. Specifically, developers must always wrap `bls12381::PublicKey` +//! objects as `Validatable::` and access the public key via +//! `Validatable::::valid()`. We give an example below. +//! +//! We implement the `Minimal-pubkey-size` variant from the BLS IETF draft standard [^bls-ietf-draft], +//! which puts the signatures in the group $\mathbb{G}_2$ and the public keys in $\mathbb{G}_1$. The +//! reasoning behind this choice is to minimize public key size, since public keys are posted on the +//! blockchain. +//! +//! # Overview of normal Boneh-Lynn-Shacham (BLS) signatures +//! +//! In a _normal signature scheme_, we have a single _signer_ who generates its own key-pair: +//! a _private-key_ and a corresponding _public key_. The signer can produce a _signature_ on a +//! _message_ `m` using its private-key. Any _verifier_ who has the public key can check that +//! the signature on `m` was produced by the signer. +//! +//! # Overview of Boneh-Lynn-Shacham (BLS) multisignatures +//! +//! In a _multisignature scheme_, we have `n` signers. Each signer `i` has their own key-pair `(sk_i, pk_i)`. +//! Any subset of `k` signers can collaborate to produce a succinct _multisignature_ on the *same* +//! message `m`. +//! +//! Typically, the `k` signers first agree on the message `m` via some protocol (e.g., `m` is the +//! latest block header in a blockchain protocol). Then, each signer produces a _signature share_ `s_i` +//! on `m` using their own private key `sk_i`. After this, each signer `i` sends their signature +//! share `s_i` to an _aggregator_: a dedicated, untrusted party who is responsible for aggregating +//! the signature shares into the final multisignature. For example, one of the signers themselves +//! could be the aggregator. +//! +//! Lastly, the aggregator can proceed in two ways: +//! +//! 1. Pessimistically verify each signature share, discarding the invalid ones, and then aggregate +//! the final multisignature. +//! +//! 2. Optimistically aggregate all signature shares, but verify the final multisignature at the end +//! to ensure no bad signature shares were included. If the multisignature does not verify, +//! revert to the pessimistic mode (or consider other approaches [^LM07]). +//! +//! Either way, the end result (assuming some of the signature shares were valid) will be a valid +//! multisignature on `m` which can be verified against an _aggregate public key_ of the involved +//! signers. +//! +//! Specifically, any verifier who knows the public keys of the signers whose shares were aggregated +//! into the multisignature, can first compute an _aggregate public key_ as a function of these +//! public keys and then verify the multisignature under this aggregate public key. +//! +//! Extremely important for security is that the verifier first ensure these public keys came with +//! valid proofs-of-possession (PoPs). Otherwise, multisignatures can be forged via _rogue-key attacks_ +//! [^MOR01]. +//! +//! # Overview of Boneh-Lynn-Shacham (BLS) aggregate signatures +//! +//! In an _aggregate signature scheme_ any subset of `k` out of `n` signers can collaborate to produce +//! a succinct _aggregate signature_ over (potentially) different message. Specifically, such an +//! aggregate signature is a succinct representation of `k` normal signatures, where the `i`th signature +//! from the `i`th signer is on some message `m_i`. Importantly, `m_i` might differ from the other `k-1` messages +//! signed by the other signers. +//! +//! Note that an aggregate signature where all the signed messages `m_i` are the same is just a +//! multisignature. +//! +//! Just like in a multisignature scheme, in an aggregate signature scheme there is an _aggregator_ +//! who receives _signature shares_ `s_i` from each signer `i` on their *own* message `m_i` and +//! aggregates the valid signature shares into an aggregate signature. (In contrast, recall that, +//! in a multisignature scheme, every signer `i` signed the same message `m`.) +//! +//! Aggregation proceeds the same as in a multisignature scheme (see notes in previous section). +//! +//! # A note on subgroup checks +//! +//! This library was written so that users who know nothing about _small subgroup attacks_ [^LL97], [^BCM+15e] +//! need not worry about them, **as long as library users either**: +//! +//! 1. For normal (non-aggregated) signature verification, wrap `PublicKey` objects using +//! `Validatable` +//! +//! 2. For multisignature, aggregate signature and signature share verification, library users +//! always verify a public key's proof-of-possession (PoP)** before aggregating it with other PKs +//! and before verifying signature shares with it. +//! +//! Nonetheless, we still provide `subgroup_check` methods for the `PublicKey` and `Signature` structs, +//! in case manual verification of subgroup membership is ever needed. +//! +//! # A note on domain separation tags (DSTs) +//! +//! Internal to this wrapper's implementation (and to the underlying blst library) is the careful +//! use of domain separation tags (DSTs) as per the BLS IETF draft standard [^bls-ietf-draft]. +//! +//! Specifically, **when signing a message** `m`, instead of signing as `H(m)^sk`, where `sk` is the +//! secret key, the library actually signs as `H(sig_dst | m)^sk`, where `sig_dst` is a DST for +//! message signing. +//! +//! In contrast, **when computing a proof-of-possesion (PoP)**, instead of signing the public key as +//! `H(pk)^sk`, the library actually signs as `H(sig_pop | pk)^sk`, where `sig_pop` is a DST for +//! signatures used during PoP creation. +//! +//! This way, we can clearly separate the message spaces of these two use cases of the secret key `sk`. +//! +//! # How to use this module to create and verify normal (non-aggregated) signatures on a single message +//! +//! A typical use of the normal (non-aggregated) signature library would look as follows. +//! +//! For signers: +//! +//! ``` +//! use std::iter::zip; +//! use aptos_crypto::test_utils::{KeyPair, TestAptosCrypto}; +//! use aptos_crypto::{bls12381, Signature, SigningKey, Uniform}; +//! use aptos_crypto::bls12381::bls12381_keys::{PrivateKey, PublicKey}; +//! use aptos_crypto::bls12381::ProofOfPossession; +//! use aptos_crypto_derive::{CryptoHasher, BCSCryptoHash}; +//! use rand_core::OsRng; +//! use serde::{Serialize, Deserialize}; +//! +//! let mut rng = OsRng; +//! +//! // A signer locally generated their own BLS key-pair via: +//! let kp = KeyPair::::generate(&mut rng); +//! +//! // Any arbitrary struct can be signed as long as it is properly "derived" via: +//! // +//! // #[derive(CryptoHasher, BCSCryptoHash, Serialize, Deserialize)] +//! // struct Message(String); +//! // +//! // Here, we'll sign an existing testing struct from `crates/aptos-crypto/src/test_utils.rs`: +//! // +//! // #[derive(Debug, Serialize, Deserialize)] +//! // pub struct TestAptosCrypto(pub String); +//! +//! +//! // The signer computes a normal signature on a message. +//! let message = TestAptosCrypto("test".to_owned()); +//! let sig = kp.private_key.sign(&message).unwrap(); +//! ``` +//! +//! For verifiers: +//! +//! ``` +//! use std::convert::TryFrom; +//! use std::iter::zip; +//! use aptos_crypto::test_utils::{KeyPair, TestAptosCrypto}; +//! use aptos_crypto::{bls12381, Signature, SigningKey, Uniform}; +//! use aptos_crypto::bls12381::bls12381_keys::{PrivateKey, PublicKey}; +//! use aptos_crypto::bls12381::{ProofOfPossession, UnvalidatedPublicKey}; +//! use aptos_crypto_derive::{CryptoHasher, BCSCryptoHash}; +//! use rand_core::OsRng; +//! use serde::{Serialize, Deserialize}; +//! use aptos_crypto::validatable::Validatable; +//! +//! // NOTE: These were generated by running `cargo test -- doc_test --ignored --nocapture` in `crates/aptos-crypto/` +//! let sk_bytes = hex::decode("65e0c364e0cc27ae4e90cb28059677c36fd11b47cbbab48a9cca3c34e92eefbb").unwrap(); +//! let pk_bytes = hex::decode("99ad6adb0a8b9a8c44dbf643a3ad6d11ff1fe90138db857382a8fc202334e6f8842e04055a729e4a4ba5b08161e7abd6").unwrap(); +//! // signature on TestAptosCrypto("test".to_owned()) +//! let sig_bytes = hex::decode("b266e156091c1d621304861654bae748cb3534bef86eb6ca1d482148ba7b1e3530eca47790a98971f421fe2d55f9d9af047807b5698cf559441b81288a022812d58669fee2d30b4c7bd86706c6a2128fd5b0c44c4bc171ca6e9d4c89196cac85").unwrap(); +//! +//! // A verifier typically obtains the public key of the signer (somehow) and deserializes it +//! +//! /////////////////////////////////////////////////////////////////////////////////////////////// +//! // WARNING: Before relying on any public key to verify a signature, a verifier MUST first // +//! // validate it using the `Validatable::` wrapper as follows: // +//! /////////////////////////////////////////////////////////////////////////////////////////////// +//! +//! // First, construct an UnvalidatedPublicKey struct +//! let pk_unvalidated = UnvalidatedPublicKey::try_from(pk_bytes.as_slice()); +//! if pk_unvalidated.is_err() { +//! println!("ERROR: Could NOT deserialize unvalidated PK"); +//! return; +//! } +//! +//! // Second, construct a Validatable:: struct out of this UnvalidatedPublicKey struct +//! let pk_validatable = Validatable::::from_unvalidated(pk_unvalidated.unwrap()); +//! +//! // Third, call validate() on it to get a subgroup-checked PK back. +//! // +//! // IMPORTANT NOTE: The result of this validation will be cached in a OnceCell so subsequent calls +//! // to this function will return very fast. +//! // +//! let pk = pk_validatable.validate(); +//! +//! if pk.is_err() { +//! println!("ERROR: Public key was either accidentally-corrupted or maliciously-generated."); +//! println!("Specifically, the public key is NOT a prime-order point."); +//! println!("As a result, this public key CANNOT be relied upon to verify any signatures!"); +//! return; +//! } +//! +//! let pk = pk.unwrap(); +//! let message = TestAptosCrypto("test".to_owned()); +//! +//! // deserialize the signature on `message` and verify it +//! let sig = bls12381::Signature::try_from(sig_bytes.as_slice()); +//! if sig.is_err() { +//! println!("ERROR: Could NOT deserialize signature"); +//! return; +//! } +//! +//! // Any verifier who has the signer's public key can verify the `(message, sig)` pair as: +//! let sig = sig.unwrap(); +//! if sig.verify(&message, &pk).is_ok() { +//! println!("Signature verified successfully!"); +//! } else { +//! println!("Signature did NOT verify!"); +//! } +//! +//! // If the verification passed, then the verifier is certain that the signer signed `message` +//! ``` +//! +//! # How to use this module to aggregate and verify multisignatures +//! +//! A typical use of the multisignature library would look as follows: +//! +//! ``` +//! use std::iter::zip; +//! use aptos_crypto::test_utils::KeyPair; +//! use aptos_crypto::{bls12381, Signature, SigningKey, Uniform}; +//! use aptos_crypto::bls12381::bls12381_keys::{PrivateKey, PublicKey}; +//! use aptos_crypto::bls12381::ProofOfPossession; +//! use aptos_crypto_derive::{CryptoHasher, BCSCryptoHash}; +//! use rand_core::OsRng; +//! use serde::{Serialize, Deserialize}; +//! +//! // Each signer locally generates their own BLS key-pair with a proof-of-possesion (PoP). +//! // We simulate this here, by storing each signer's key-pair in a vector. +//! let mut rng = OsRng; +//! +//! let num_signers = 1000; +//! +//! let mut key_pairs = vec![]; +//! let mut pops = vec![]; +//! for _ in 0..num_signers { +//! let kp = KeyPair::::generate(&mut rng); +//! pops.push(ProofOfPossession::create_with_pubkey(&kp.private_key, &kp.public_key)); +//! // Alternatively, but slower, can choose not to provide the PK and have it computed inside +//! // pops.push(ProofOfPossession::create(&kp.private_key)); +//! key_pairs.push(kp); +//! } +//! +//! // Any arbitrary struct can be signed as long as it is properly "derived" +//! #[derive(CryptoHasher, BCSCryptoHash, Serialize, Deserialize)] +//! struct Message(String); +//! +//! // Each signer then computes a signature share on a message. Again, we simulate using a vector. +//! let mut sigshares = vec![]; +//! let message = Message("test".to_owned()); +//! for kp in key_pairs.iter() { +//! let sig = kp.private_key.sign(&message).unwrap(); +//! sigshares.push(sig); +//! } +//! +//! // Then, an aggregator receives some of these signature shares and will attempt to aggregate +//! // them in a multisig. This aggregator can proceed _optimistically_ as follows: +//! +//! // First, when the aggregator boots up, it must verify that each signer's public key has a valid +//! // proof-of-possession (PoP)! +//! +//! /////////////////////////////////////////////////////////////////////////////////////////////// +//! // WARNING: Before relying on a public key to verify a multisignature or a signature share // +//! // one must MUST first verify that public key's PoP // +//! // // +//! // The importance of this step cannot be overstated! // +//! // // +//! // Put differently, a public key with an unverified PoP cannot be used securely for any // +//! // signature verification. This is why the code below first verifies PoPs of all public keys // +//! // that are later used to verify the multisignature against. // +//! /////////////////////////////////////////////////////////////////////////////////////////////// +//! for i in 0..pops.len() { +//! assert!(pops[i].verify(&key_pairs[i].public_key).is_ok()); +//! } +//! +//! // Second, now that the aggregator trusts the set of public keys, it can safely aggregate +//! // signature shares _optimistically_ into a multisignature which hopefully verifies. In this +//! // example, we assume the aggregator receives a signature share from every third signer (for simplicity). +//! +//! // Here, we simulate the aggregator receiving some signature shares. +//! let mut sigshares_received = vec![]; +//! for sigshare in sigshares.into_iter().step_by(3) { +//! sigshares_received.push(sigshare); +//! } +//! +//! // Here, the aggregator aggregates the received signature shares into a multisignature. +//! let multisig = bls12381::Signature::aggregate(sigshares_received.clone()).unwrap(); +//! +//! // Third, the aggregator checks that the _optimistic_ aggregation from above succeeded by +//! // verifying the multisig. For this, the aggregator will need to know the public keys of the +//! // signers whose signature shares were aggregated, so that it can aggregate them. +//! let mut pubkeys_to_agg = vec![]; +//! for kp in key_pairs.iter().step_by(3) { +//! pubkeys_to_agg.push(&kp.public_key); +//! } +//! +//! let aggpk = PublicKey::aggregate(pubkeys_to_agg.clone()).unwrap(); +//! +//! // Lastly, the aggregator checks the aggregated multisig verifies successfully. +//! assert!(multisig.verify(&message, &aggpk).is_ok()); +//! +//! // If the multisig failed verification, the aggregator can individually verify each of the +//! // signature shares to identify which ones are invalid and exclude them. There are also optimized +//! // methods for identifying bad signature shares faster when their relative frequency is low [^LM07]. +//! // However, we will not implement these yet. +//! for (sigshare, pk) in zip(sigshares_received, pubkeys_to_agg) { +//! assert!(sigshare.verify(&message, &pk).is_ok()); +//! } +//! ``` +//! +//! # How to use this module to aggregate and verify aggregate signatures +//! +//! A typical use of the aggregate signature library would look as follows: +//! +//! ``` +//! use std::iter::zip; +//! use aptos_crypto::test_utils::KeyPair; +//! use aptos_crypto::{bls12381, Signature, SigningKey, Uniform}; +//! use aptos_crypto::bls12381::bls12381_keys::{PrivateKey, PublicKey}; +//! use aptos_crypto::bls12381::ProofOfPossession; +//! use aptos_crypto_derive::{CryptoHasher, BCSCryptoHash}; +//! use rand_core::OsRng; +//! use serde::{Serialize, Deserialize}; +//! +//! // Each signer locally generates their own BLS key-pair with a proof-of-possesion (PoP). +//! // We simulate this here, by storing each signer's key-pair in a vector. +//! let mut rng = OsRng; +//! +//! let num_signers = 1000; +//! +//! let mut key_pairs = vec![]; +//! let mut pops = vec![]; +//! for _ in 0..num_signers { +//! let kp = KeyPair::::generate(&mut rng); +//! pops.push(ProofOfPossession::create_with_pubkey(&kp.private_key, &kp.public_key)); +//! // Alternatively, but slower, can choose not to provide the PK and have it computed inside +//! // pops.push(ProofOfPossession::create(&kp.private_key)); +//! key_pairs.push(kp); +//! } +//! +//! // Any arbitrary struct can be signed as long as it is properly "derived" +//! #[derive(CryptoHasher, BCSCryptoHash, Serialize, Deserialize)] +//! struct Message(String, usize); +//! +//! // Each signer `i` then computes a signature share on its own message `m_i`, which might +//! // differ from other signer's message `m_j`. Again, we simulate this using a vector. +//! let mut sigshares = vec![]; +//! let mut messages = vec![]; +//! for i in 0..num_signers { +//! let message = Message("different message".to_owned(), i); +//! let sig = key_pairs[i].private_key.sign(&message).unwrap(); +//! +//! messages.push(message); +//! sigshares.push(sig); +//! } +//! +//! // Then, an aggregator receives some of these signature shares and will attempt to aggregate +//! // them in an aggregate signature. This aggregator can proceed _optimistically_ as follows: +//! +//! // First, when the aggregator boots up, it must verify that each signer's public key has a valid +//! // proof-of-possession (PoP)! +//! +//! /////////////////////////////////////////////////////////////////////////////////////////////// +//! // WARNING: Before relying on the public keys of the signers for verifying aggregate // +//! // signatures or signature shares, one MUST first verify *every* signer's PoP. // +//! // // +//! // The importance of this step cannot be overstated! // +//! // // +//! /////////////////////////////////////////////////////////////////////////////////////////////// +//! for i in 0..pops.len() { +//! assert!(pops[i].verify(&key_pairs[i].public_key).is_ok()); +//! } +//! +//! // Second, now that the aggregator trusts the set of public keys, it can safely aggregate +//! // signature shares _optimistically_ into an aggregate signature which hopefully verifies. In this +//! // example, we assume the aggregator receives a signature share from every signer (for simplicity). +//! +//! // Here, we simulate the aggregator receiving all signature shares. +//! let sigshares_received = sigshares; +//! +//! // Here, the aggregator aggregates the received signature shares into an aggregate signature. +//! let aggsig = bls12381::Signature::aggregate(sigshares_received.clone()).unwrap(); +//! +//! // Third, the aggregator checks that the _optimistic_ aggregation from above succeeded by +//! // verifying the aggregate signature. For this, the aggregator will need to know the public keys +//! // of the signers whose signature shares were aggregated. +//! let msgs_refs = messages.iter().map(|m| m).collect::>(); +//! let pks_refs = key_pairs.iter().map(|kp| &kp.public_key).collect::>(); +//! assert!(aggsig.verify_aggregate(&msgs_refs, &pks_refs).is_ok()); +//! +//! // If the aggregate signature failed verification, the aggregator can individually verify each +//! // of the signature shares to identify which ones are invalid and exclude them. There are also +//! // optimized methods for identifying bad signature shares faster when their relative frequency +//! // is low [^LM07]. However, we will not implement these yet. +//! for i in 0..num_signers { +//! let (msg, sigshare, pk) = (msgs_refs[i], &sigshares_received[i], pks_refs[i]); +//! assert!(sigshare.verify(msg, pk).is_ok()); +//! } +//! ``` +//! +//! References: +//! +//! [^bls-ietf-draft]: BLS Signatures; by D. Boneh, S. Gorbunov, R. Wahby, H. Wee, Z. Zhang; +//! [^Bold03]: Threshold Signatures, Multisignatures and Blind Signatures Based on the Gap-Diffie-Hellman-Group Signature Scheme; by Boldyreva, Alexandra; in PKC 2003; 2002 +//! [^BLS04]: Short Signatures from the Weil Pairing; by Boneh, Dan and Lynn, Ben and Shacham, Hovav; in Journal of Cryptology; 2004; +//! [^BCM+15e] Subgroup security in pairing-based cryptography; by Paulo S. L. M. Barreto and Craig Costello and Rafael Misoczki and Michael Naehrig and Geovandro C. C. F. Pereira and Gustavo Zanon; in Cryptology ePrint Archive, Paper 2015/247; 2015; +//! [^LL97] A key recovery attack on discrete log-based schemes using a prime order subgroup; by Lim, Chae Hoon and Lee, Pil Joong; in Advances in Cryptology --- CRYPTO '97; 1997 +//! [^LM07]: Finding Invalid Signatures in Pairing-Based Batches; by Law, Laurie and Matt, Brian J.; in Cryptography and Coding; 2007 +//! [^MOR01]: Accountable-Subgroup Multisignatures: Extended Abstract; by Micali, Silvio and Ohta, Kazuo and Reyzin, Leonid; in Proceedings of the 8th ACM Conference on Computer and Communications Security; 2001; +//! [^RY07]: The Power of Proofs-of-Possession: Securing Multiparty Signatures against Rogue-Key Attacks; by Ristenpart, Thomas and Yilek, Scott; in Advances in Cryptology - EUROCRYPT 2007; 2007 + +/// Domain separation tag (DST) for hashing a message before signing it. +pub const DST_BLS_SIG_IN_G2_WITH_POP: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"; + +pub mod bls12381_keys; +pub mod bls12381_pop; +pub mod bls12381_sigs; +pub mod bls12381_validatable; + +pub use bls12381_keys::{PrivateKey, PublicKey}; +pub use bls12381_pop::ProofOfPossession; +pub use bls12381_sigs::Signature; +pub use bls12381_validatable::UnvalidatedPublicKey; diff --git a/crates/aptos-crypto/src/bulletproofs/mod.rs b/crates/aptos-crypto/src/bulletproofs/mod.rs new file mode 100644 index 0000000..10cb578 --- /dev/null +++ b/crates/aptos-crypto/src/bulletproofs/mod.rs @@ -0,0 +1,8 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +//! For now, this module stores some constants related to our implementation of Bulletproofs as a +//! Move API. + +/// The maximum range we'll use Bulletproofs with is [0, 2^64). Might need to revisit this later. +pub const MAX_RANGE_BITS: usize = 64; diff --git a/crates/aptos-crypto/src/compat.rs b/crates/aptos-crypto/src/compat.rs new file mode 100644 index 0000000..38a26d8 --- /dev/null +++ b/crates/aptos-crypto/src/compat.rs @@ -0,0 +1,63 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//! Wrapper structs for types that need [RustCrypto](https://github.com/RustCrypto) +//! traits implemented. + +use digest::{ + consts::{U136, U32}, + generic_array::GenericArray, + BlockInput, Digest, FixedOutput, Reset, Update, +}; +use tiny_keccak::{Hasher, Sha3}; + +/// A wrapper for [`tiny_keccak::Sha3::v256`] that +/// implements RustCrypto [`digest`] traits [`BlockInput`], [`Update`], [`Reset`], +/// and [`FixedOutput`]. Consequently, this wrapper can be used in RustCrypto +/// APIs that require a hash function (usually something that impls [`Digest`]). +#[derive(Clone)] +pub struct Sha3_256(Sha3); + +// ensure that we impl all of the sub-traits required for the Digest trait alias +static_assertions::assert_impl_all!(Sha3_256: Digest); + +impl Default for Sha3_256 { + #[inline] + fn default() -> Self { + Self(Sha3::v256()) + } +} + +impl BlockInput for Sha3_256 { + type BlockSize = U136; +} + +impl Update for Sha3_256 { + #[inline] + fn update(&mut self, data: impl AsRef<[u8]>) { + self.0.update(data.as_ref()); + } +} + +impl Reset for Sha3_256 { + #[inline] + fn reset(&mut self) { + *self = Self::default(); + } +} + +impl FixedOutput for Sha3_256 { + type OutputSize = U32; + + #[inline] + fn finalize_into(self, out: &mut GenericArray) { + self.0.finalize(out.as_mut()); + } + + #[inline] + fn finalize_into_reset(&mut self, out: &mut GenericArray) { + self.clone().finalize_into(out); + Reset::reset(self) + } +} diff --git a/crates/aptos-crypto/src/ed25519/ed25519_keys.rs b/crates/aptos-crypto/src/ed25519/ed25519_keys.rs new file mode 100644 index 0000000..b1f578b --- /dev/null +++ b/crates/aptos-crypto/src/ed25519/ed25519_keys.rs @@ -0,0 +1,329 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +//! This file implements traits for Ed25519 private keys and public keys. + +#[cfg(any(test, feature = "fuzzing"))] +use crate::test_utils::{self, KeyPair}; +use crate::{ + ed25519::{Ed25519Signature, ED25519_PRIVATE_KEY_LENGTH, ED25519_PUBLIC_KEY_LENGTH}, + hash::CryptoHash, + traits::*, +}; +use aptos_crypto_derive::{DeserializeKey, SerializeKey, SilentDebug, SilentDisplay}; +use core::convert::TryFrom; +use curve25519_dalek::{edwards::CompressedEdwardsY, scalar::Scalar}; +use ed25519_dalek::ExpandedSecretKey; +#[cfg(any(test, feature = "fuzzing"))] +use proptest::prelude::*; +use serde::Serialize; +use std::fmt; + +/// An Ed25519 private key +#[derive(DeserializeKey, SerializeKey, SilentDebug, SilentDisplay)] +pub struct Ed25519PrivateKey(pub(crate) ed25519_dalek::SecretKey); + +#[cfg(feature = "assert-private-keys-not-cloneable")] +static_assertions::assert_not_impl_any!(Ed25519PrivateKey: Clone); + +#[cfg(any(test, feature = "cloneable-private-keys"))] +impl Clone for Ed25519PrivateKey { + fn clone(&self) -> Self { + let serialized: &[u8] = &(self.to_bytes()); + Ed25519PrivateKey::try_from(serialized).unwrap() + } +} + +/// An Ed25519 public key +#[derive(DeserializeKey, Clone, SerializeKey)] +pub struct Ed25519PublicKey(pub(crate) ed25519_dalek::PublicKey); + +impl Ed25519PrivateKey { + /// The length of the Ed25519PrivateKey + pub const LENGTH: usize = ED25519_PRIVATE_KEY_LENGTH; + + /// Serialize an Ed25519PrivateKey. + pub fn to_bytes(&self) -> [u8; ED25519_PRIVATE_KEY_LENGTH] { + self.0.to_bytes() + } + + /// Deserialize an Ed25519PrivateKey without any validation checks apart from expected key size. + fn from_bytes_unchecked( + bytes: &[u8], + ) -> std::result::Result { + match ed25519_dalek::SecretKey::from_bytes(bytes) { + Ok(dalek_secret_key) => Ok(Ed25519PrivateKey(dalek_secret_key)), + Err(_) => Err(CryptoMaterialError::DeserializationError), + } + } + + /// Private function aimed at minimizing code duplication between sign + /// methods of the SigningKey implementation. This should remain private. + fn sign_arbitrary_message(&self, message: &[u8]) -> Ed25519Signature { + let secret_key: &ed25519_dalek::SecretKey = &self.0; + let public_key: Ed25519PublicKey = self.into(); + let expanded_secret_key: ed25519_dalek::ExpandedSecretKey = + ed25519_dalek::ExpandedSecretKey::from(secret_key); + let sig = expanded_secret_key.sign(message.as_ref(), &public_key.0); + Ed25519Signature(sig) + } + + /// Derive the actual scalar represented by the secret key. + /// TODO: We are temporarily breaking the abstraction here and exposing the SK scalar. In the future, we should add traits for encryption inside aptos-crypto so that we can both sign and decrypt with an Ed25519PrivateKey. + pub fn derive_scalar(&self) -> Scalar { + let expanded_bytes = ExpandedSecretKey::from(&self.0).to_bytes(); + let bits = expanded_bytes[..32] + .try_into() + .expect("converting [u8; 64] to [u8; 32] should work"); + Scalar::from_bits(bits).reduce() + } +} + +impl Ed25519PublicKey { + /// The maximum size in bytes. + pub const LENGTH: usize = ED25519_PUBLIC_KEY_LENGTH; + + /// Serialize an Ed25519PublicKey. + pub fn to_bytes(&self) -> [u8; ED25519_PUBLIC_KEY_LENGTH] { + self.0.to_bytes() + } + + /// Deserialize an Ed25519PublicKey without any validation checks apart from expected key size + /// and valid curve point, although not necessarily in the prime-order subgroup. + /// + /// This function does NOT check the public key for membership in a small subgroup. + pub(crate) fn from_bytes_unchecked( + bytes: &[u8], + ) -> std::result::Result { + match ed25519_dalek::PublicKey::from_bytes(bytes) { + Ok(dalek_public_key) => Ok(Ed25519PublicKey(dalek_public_key)), + Err(_) => Err(CryptoMaterialError::DeserializationError), + } + } + + /// Deserialize an Ed25519PublicKey from its representation as an x25519 + /// public key, along with an indication of sign. This is meant to + /// compensate for the poor key storage capabilities of key management + /// solutions, and NOT to promote double usage of keys under several + /// schemes, which would lead to BAD vulnerabilities. + /// + /// This function does NOT check if the public key lies in a small subgroup. + /// + /// Arguments: + /// - `x25519_bytes`: bit representation of a public key in clamped + /// Montgomery form, a.k.a. the x25519 public key format. + /// - `negative`: whether to interpret the given point as a negative point, + /// as the Montgomery form erases the sign byte. By XEdDSA + /// convention, if you expect to ever convert this back to an + /// x25519 public key, you should pass `false` for this + /// argument. + #[cfg(test)] + pub(crate) fn from_x25519_public_bytes( + x25519_bytes: &[u8], + negative: bool, + ) -> Result { + if x25519_bytes.len() != 32 { + return Err(CryptoMaterialError::DeserializationError); + } + let key_bits = { + let mut bits = [0u8; 32]; + bits.copy_from_slice(x25519_bytes); + bits + }; + let mtg_point = curve25519_dalek::montgomery::MontgomeryPoint(key_bits); + let sign = u8::from(negative); + let ed_point = mtg_point + .to_edwards(sign) + .ok_or(CryptoMaterialError::DeserializationError)?; + Ed25519PublicKey::try_from(&ed_point.compress().as_bytes()[..]) + } + + /// Derive the actual curve point represented by the public key. + pub fn to_compressed_edwards_y(&self) -> CompressedEdwardsY { + let bytes = self.to_bytes(); + CompressedEdwardsY::from_slice(bytes.as_slice()) + } +} + +/////////////////////// +// PrivateKey Traits // +/////////////////////// + +impl PrivateKey for Ed25519PrivateKey { + type PublicKeyMaterial = Ed25519PublicKey; +} + +impl SigningKey for Ed25519PrivateKey { + type SignatureMaterial = Ed25519Signature; + type VerifyingKeyMaterial = Ed25519PublicKey; + + fn sign( + &self, + message: &T, + ) -> Result { + Ok(Ed25519PrivateKey::sign_arbitrary_message( + self, + signing_message(message)?.as_ref(), + )) + } + + #[cfg(any(test, feature = "fuzzing"))] + fn sign_arbitrary_message(&self, message: &[u8]) -> Ed25519Signature { + Ed25519PrivateKey::sign_arbitrary_message(self, message) + } +} + +impl Uniform for Ed25519PrivateKey { + fn generate(rng: &mut R) -> Self + where + R: ::rand::RngCore + ::rand::CryptoRng + ::rand_core::CryptoRng + ::rand_core::RngCore, + { + Ed25519PrivateKey(ed25519_dalek::SecretKey::generate(rng)) + } +} + +impl PartialEq for Ed25519PrivateKey { + fn eq(&self, other: &Self) -> bool { + self.to_bytes() == other.to_bytes() + } +} + +impl Eq for Ed25519PrivateKey {} + +// We could have a distinct kind of validation for the PrivateKey: e.g., checking the derived +// PublicKey is valid? +impl TryFrom<&[u8]> for Ed25519PrivateKey { + type Error = CryptoMaterialError; + + /// Deserialize an Ed25519PrivateKey. This method will check for private key validity: i.e., + /// correct key length. + fn try_from(bytes: &[u8]) -> std::result::Result { + // Note that the only requirement is that the size of the key is 32 bytes, something that + // is already checked during deserialization of ed25519_dalek::SecretKey + // + // Also, the underlying ed25519_dalek implementation ensures that the derived public key + // is safe and it will not lie in a small-order group, thus no extra check for PublicKey + // validation is required. + Ed25519PrivateKey::from_bytes_unchecked(bytes) + } +} + +impl Length for Ed25519PrivateKey { + fn length(&self) -> usize { + Self::LENGTH + } +} + +impl ValidCryptoMaterial for Ed25519PrivateKey { + fn to_bytes(&self) -> Vec { + self.to_bytes().to_vec() + } +} + +impl Genesis for Ed25519PrivateKey { + fn genesis() -> Self { + let mut buf = [0u8; ED25519_PRIVATE_KEY_LENGTH]; + buf[ED25519_PRIVATE_KEY_LENGTH - 1] = 1; + Self::try_from(buf.as_ref()).unwrap() + } +} + +////////////////////// +// PublicKey Traits // +////////////////////// + +// Implementing From<&PrivateKey<...>> allows to derive a public key in a more elegant fashion +impl From<&Ed25519PrivateKey> for Ed25519PublicKey { + fn from(private_key: &Ed25519PrivateKey) -> Self { + let secret: &ed25519_dalek::SecretKey = &private_key.0; + let public: ed25519_dalek::PublicKey = secret.into(); + Ed25519PublicKey(public) + } +} + +// We deduce PublicKey from this +impl PublicKey for Ed25519PublicKey { + type PrivateKeyMaterial = Ed25519PrivateKey; +} + +impl std::hash::Hash for Ed25519PublicKey { + fn hash(&self, state: &mut H) { + let encoded_pubkey = self.to_bytes(); + state.write(&encoded_pubkey); + } +} + +// Those are required by the implementation of hash above +impl PartialEq for Ed25519PublicKey { + fn eq(&self, other: &Ed25519PublicKey) -> bool { + self.to_bytes() == other.to_bytes() + } +} + +impl Eq for Ed25519PublicKey {} + +// We deduce VerifyingKey from pointing to the signature material +// we get the ability to do `pubkey.validate(msg, signature)` +impl VerifyingKey for Ed25519PublicKey { + type SignatureMaterial = Ed25519Signature; + type SigningKeyMaterial = Ed25519PrivateKey; +} + +impl fmt::Display for Ed25519PublicKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", hex::encode(self.0.as_bytes())) + } +} + +impl fmt::Debug for Ed25519PublicKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Ed25519PublicKey({})", self) + } +} + +impl TryFrom<&[u8]> for Ed25519PublicKey { + type Error = CryptoMaterialError; + + /// Deserialize an Ed25519PublicKey. This method will NOT check for key validity, which means + /// the returned public key could be in a small subgroup. Nonetheless, our signature + /// verification implicitly checks if the public key lies in a small subgroup, so canonical + /// uses of this library will not be susceptible to small subgroup attacks. + fn try_from(bytes: &[u8]) -> std::result::Result { + Ed25519PublicKey::from_bytes_unchecked(bytes) + } +} + +impl Length for Ed25519PublicKey { + fn length(&self) -> usize { + ED25519_PUBLIC_KEY_LENGTH + } +} + +impl ValidCryptoMaterial for Ed25519PublicKey { + fn to_bytes(&self) -> Vec { + self.0.to_bytes().to_vec() + } +} + +///////////// +// Fuzzing // +///////////// + +/// Produces a uniformly random Ed25519 keypair from a seed +#[cfg(any(test, feature = "fuzzing"))] +pub fn keypair_strategy() -> impl Strategy> { + test_utils::uniform_keypair_strategy::() +} + +/// Produces a uniformly random Ed25519 public key +#[cfg(any(test, feature = "fuzzing"))] +impl proptest::arbitrary::Arbitrary for Ed25519PublicKey { + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + crate::test_utils::uniform_keypair_strategy::() + .prop_map(|v| v.public_key) + .boxed() + } +} diff --git a/crates/aptos-crypto/src/ed25519/ed25519_sigs.rs b/crates/aptos-crypto/src/ed25519/ed25519_sigs.rs new file mode 100644 index 0000000..2204b82 --- /dev/null +++ b/crates/aptos-crypto/src/ed25519/ed25519_sigs.rs @@ -0,0 +1,188 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +//! This file implements traits for Ed25519 signatures. + +use crate::{ + ed25519::{Ed25519PrivateKey, Ed25519PublicKey, ED25519_SIGNATURE_LENGTH, L}, + hash::CryptoHash, + traits::*, +}; +use anyhow::{anyhow, Result}; +use aptos_crypto_derive::{DeserializeKey, SerializeKey}; +use core::convert::TryFrom; +use serde::Serialize; +use std::{cmp::Ordering, fmt}; + +/// An Ed25519 signature +#[derive(DeserializeKey, Clone, SerializeKey)] +pub struct Ed25519Signature(pub(crate) ed25519_dalek::Signature); + +impl Ed25519Signature { + /// The length of the Ed25519Signature + pub const LENGTH: usize = ED25519_SIGNATURE_LENGTH; + + /// Serialize an Ed25519Signature. + pub fn to_bytes(&self) -> [u8; ED25519_SIGNATURE_LENGTH] { + self.0.to_bytes() + } + + /// Deserialize an Ed25519Signature without any validation checks (malleability) + /// apart from expected signature size. + pub(crate) fn from_bytes_unchecked( + bytes: &[u8], + ) -> std::result::Result { + match ed25519_dalek::Signature::try_from(bytes) { + Ok(dalek_signature) => Ok(Ed25519Signature(dalek_signature)), + Err(_) => Err(CryptoMaterialError::DeserializationError), + } + } + + /// return an all-zero signature (for test only) + #[cfg(any(test, feature = "fuzzing"))] + pub fn dummy_signature() -> Self { + Self::from_bytes_unchecked(&[0u8; Self::LENGTH]).unwrap() + } + + /// Check for correct size and third-party based signature malleability issues. + /// This method is required to ensure that given a valid signature for some message under some + /// key, an attacker cannot produce another valid signature for the same message and key. + /// + /// According to [RFC8032](https://tools.ietf.org/html/rfc8032), signatures comprise elements + /// {R, S} and we should enforce that S is of canonical form (smaller than L, where L is the + /// order of edwards25519 curve group) to prevent signature malleability. Without this check, + /// one could add a multiple of L into S and still pass signature verification, resulting in + /// a distinct yet valid signature. + /// + /// This method does not check the R component of the signature, because R is hashed during + /// signing and verification to compute h = H(ENC(R) || ENC(A) || M), which means that a + /// third-party cannot modify R without being detected. + /// + /// Note: It's true that malicious signers can already produce varying signatures by + /// choosing a different nonce, so this method protects against malleability attacks performed + /// by a non-signer. + pub fn check_s_malleability(bytes: &[u8]) -> std::result::Result<(), CryptoMaterialError> { + if bytes.len() != ED25519_SIGNATURE_LENGTH { + return Err(CryptoMaterialError::WrongLengthError); + } + if !Ed25519Signature::check_s_lt_l(&bytes[32..]) { + return Err(CryptoMaterialError::CanonicalRepresentationError); + } + Ok(()) + } + + /// Check if S < L to capture invalid signatures. + fn check_s_lt_l(s: &[u8]) -> bool { + for i in (0..32).rev() { + match s[i].cmp(&L[i]) { + Ordering::Less => return true, + Ordering::Greater => return false, + _ => {} + } + } + // As this stage S == L which implies a non canonical S. + false + } +} + +////////////////////// +// Signature Traits // +////////////////////// + +impl Signature for Ed25519Signature { + type SigningKeyMaterial = Ed25519PrivateKey; + type VerifyingKeyMaterial = Ed25519PublicKey; + + /// Verifies that the provided signature is valid for the provided message, going beyond the + /// [RFC8032](https://tools.ietf.org/html/rfc8032) specification, checking both scalar + /// malleability and point malleability (see documentation [here](https://docs.rs/ed25519-dalek/latest/ed25519_dalek/struct.PublicKey.html#on-the-multiple-sources-of-malleability-in-ed25519-signatures)). + /// + /// This _strict_ verification performs steps 1,2 and 3 from Section 5.1.7 in RFC8032, and an + /// additional scalar malleability check (via [Ed25519Signature::check_s_malleability][Ed25519Signature::check_s_malleability]). + /// + /// This function will ensure both the signature and the `public_key` are not in a small subgroup. + fn verify( + &self, + message: &T, + public_key: &Ed25519PublicKey, + ) -> Result<()> { + Self::verify_arbitrary_msg(self, &signing_message(message)?, public_key) + } + + /// Checks that `self` is valid for an arbitrary &[u8] `message` using `public_key`. + /// Outside of this crate, this particular function should only be used for native signature + /// verification in Move. + /// + /// This function will check both the signature and `public_key` for small subgroup attacks. + fn verify_arbitrary_msg(&self, message: &[u8], public_key: &Ed25519PublicKey) -> Result<()> { + // NOTE: ed25519::PublicKey::verify_strict already checks that the s-component of the signature + // is not mauled, but does so via an optimistic path which fails into a slower path. By doing + // our own (much faster) checking here, we can ensure dalek's optimistic path always succeeds + // and the slow path is never triggered. + Ed25519Signature::check_s_malleability(&self.to_bytes())?; + + // NOTE: ed25519::PublicKey::verify_strict checks that the signature's R-component and + // the public key are *not* in a small subgroup. + public_key + .0 + .verify_strict(message, &self.0) + .map_err(|e| anyhow!("{}", e)) + .and(Ok(())) + } + + fn to_bytes(&self) -> Vec { + self.0.to_bytes().to_vec() + } +} + +impl Length for Ed25519Signature { + fn length(&self) -> usize { + ED25519_SIGNATURE_LENGTH + } +} + +impl ValidCryptoMaterial for Ed25519Signature { + fn to_bytes(&self) -> Vec { + self.to_bytes().to_vec() + } +} + +impl std::hash::Hash for Ed25519Signature { + fn hash(&self, state: &mut H) { + let encoded_signature = self.to_bytes(); + state.write(&encoded_signature); + } +} + +impl TryFrom<&[u8]> for Ed25519Signature { + type Error = CryptoMaterialError; + + fn try_from(bytes: &[u8]) -> std::result::Result { + // We leave this check here to detect mauled signatures earlier, since it does not hurt + // performance much. (This check is performed again in Ed25519Signature::verify_arbitrary_msg + // and in ed25519-dalek's verify_strict API.) + Ed25519Signature::check_s_malleability(bytes)?; + Ed25519Signature::from_bytes_unchecked(bytes) + } +} + +// Those are required by the implementation of hash above +impl PartialEq for Ed25519Signature { + fn eq(&self, other: &Ed25519Signature) -> bool { + self.to_bytes()[..] == other.to_bytes()[..] + } +} + +impl Eq for Ed25519Signature {} + +impl fmt::Display for Ed25519Signature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", hex::encode(&self.0.to_bytes()[..])) + } +} + +impl fmt::Debug for Ed25519Signature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Ed25519Signature({})", self) + } +} diff --git a/crates/aptos-crypto/src/ed25519/mod.rs b/crates/aptos-crypto/src/ed25519/mod.rs new file mode 100644 index 0000000..0be25c2 --- /dev/null +++ b/crates/aptos-crypto/src/ed25519/mod.rs @@ -0,0 +1,55 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +//! This module provides an API for the PureEdDSA signature scheme over the Ed25519 twisted +//! Edwards curve as defined in [RFC8032](https://tools.ietf.org/html/rfc8032). +//! +//! Signature verification also checks and rejects non-canonical signatures. +//! +//! # Examples +//! +//! ``` +//! use aptos_crypto_derive::{CryptoHasher, BCSCryptoHash}; +//! use aptos_crypto::{ +//! ed25519::*, +//! traits::{Signature, SigningKey, Uniform}, +//! test_utils::KeyPair +//! }; +//! use rand::{rngs::StdRng, SeedableRng}; +//! use rand_core::OsRng; +//! use serde::{Serialize, Deserialize}; +//! +//! #[derive(Serialize, Deserialize, CryptoHasher, BCSCryptoHash)] +//! pub struct TestCryptoDocTest(String); +//! let message = TestCryptoDocTest("Test message".to_string()); +//! +//! let mut rng = OsRng; +//! let kp = KeyPair::::generate(&mut rng); +//! +//! let signature = kp.private_key.sign(&message).unwrap(); +//! assert!(signature.verify(&message, &kp.public_key).is_ok()); +//! ``` + +/// The length of the Ed25519PrivateKey +pub const ED25519_PRIVATE_KEY_LENGTH: usize = ed25519_dalek::SECRET_KEY_LENGTH; +/// The length of the Ed25519PublicKey +pub const ED25519_PUBLIC_KEY_LENGTH: usize = ed25519_dalek::PUBLIC_KEY_LENGTH; +/// The length of the Ed25519Signature +pub const ED25519_SIGNATURE_LENGTH: usize = ed25519_dalek::SIGNATURE_LENGTH; + +/// The order of ed25519 as defined in [RFC8032](https://tools.ietf.org/html/rfc8032). +const L: [u8; 32] = [ + 0xED, 0xD3, 0xF5, 0x5C, 0x1A, 0x63, 0x12, 0x58, 0xD6, 0x9C, 0xF7, 0xA2, 0xDE, 0xF9, 0xDE, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, +]; + +pub mod ed25519_keys; +pub mod ed25519_sigs; + +#[cfg(any(test, feature = "fuzzing"))] +pub use ed25519_keys::keypair_strategy; +pub use ed25519_keys::{ + Ed25519PrivateKey, Ed25519PrivateKey as PrivateKey, Ed25519PublicKey, + Ed25519PublicKey as PublicKey, +}; +pub use ed25519_sigs::{Ed25519Signature, Ed25519Signature as Signature}; diff --git a/crates/aptos-crypto/src/elgamal/curve25519.rs b/crates/aptos-crypto/src/elgamal/curve25519.rs new file mode 100644 index 0000000..7ea864f --- /dev/null +++ b/crates/aptos-crypto/src/elgamal/curve25519.rs @@ -0,0 +1,44 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::elgamal::ElGamalFriendlyGroup; +use rand_core::{CryptoRng, RngCore}; +use std::ops::Mul; + +/// ElGamal encryption over Curve25519. +pub struct Curve25519 {} + +impl ElGamalFriendlyGroup for Curve25519 { + type Element = curve25519_dalek::edwards::EdwardsPoint; + type Scalar = curve25519_dalek::scalar::Scalar; + + fn rand_scalar(rng: &mut R) -> Self::Scalar { + Self::Scalar::random(rng) + } + + fn generator_mul(scalar: &Self::Scalar) -> Self::Element { + curve25519_dalek::constants::ED25519_BASEPOINT_TABLE.mul(scalar) + } + + fn add(a: &Self::Element, b: &Self::Element) -> Self::Element { + a + b + } + + fn sub(a: &Self::Element, b: &Self::Element) -> Self::Element { + a - b + } + + fn mul(a: &Self::Element, s: &Self::Scalar) -> Self::Element { + s * a + } +} + +#[cfg(test)] +mod tests { + use crate::elgamal::{curve25519::Curve25519, test_keygen_enc_dec}; + + #[test] + fn basic() { + test_keygen_enc_dec::() + } +} diff --git a/crates/aptos-crypto/src/elgamal/mod.rs b/crates/aptos-crypto/src/elgamal/mod.rs new file mode 100644 index 0000000..2ed2862 --- /dev/null +++ b/crates/aptos-crypto/src/elgamal/mod.rs @@ -0,0 +1,78 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +//! This module provides ElGamal generic constructions and concrete schemes. + +use rand_core::{CryptoRng, RngCore}; +use std::fmt::Debug; + +/// This trait captures the group operations needed to implement ElGamal. +pub trait ElGamalFriendlyGroup { + /// The scalar type. + type Scalar: Debug; + + /// The group element type. + type Element: Debug + Eq; + + /// Generate a random scalar. + fn rand_scalar(rng: &mut R) -> Self::Scalar; + + /// Compute `s*G` where `s` is a scalar and `G` is the group generator. + fn generator_mul(scalar: &Self::Scalar) -> Self::Element; + + /// Compute `A+B` where `A` and `B` are 2 group elements. + fn add(a: &Self::Element, b: &Self::Element) -> Self::Element; + + /// Compute `A-B` where `A` and `B` are 2 group elements. + fn sub(a: &Self::Element, b: &Self::Element) -> Self::Element; + + /// Compute `s*A` where `A` is a group element and `s` is a scalar. + fn mul(a: &Self::Element, s: &Self::Scalar) -> Self::Element; + + /// Generate a random group element. + fn rand_element(rng: &mut R) -> Self::Element { + Self::generator_mul(&Self::rand_scalar(rng)) + } +} + +/// ElGamal encryption scheme over Curve25519. +pub mod curve25519; + +/// Return a key pair `(private_key, public_key)` for El Gamal encryption over BLS12-381 G1. +pub fn key_gen( + rng: &mut R, +) -> (G::Scalar, G::Element) { + let sk = G::rand_scalar(rng); + let pk = G::generator_mul(&sk); + (sk, pk) +} + +/// ElGamal encryption. +pub fn encrypt( + rng: &mut R, + pk: &G::Element, + msg: &G::Element, +) -> (G::Element, G::Element) { + let r = G::rand_scalar(rng); + let c0 = G::generator_mul(&r); + let c1 = G::add(msg, &G::mul(pk, &r)); + (c0, c1) +} + +/// ElGamal decryption. +pub fn decrypt( + sk: &G::Scalar, + c0: &G::Element, + c1: &G::Element, +) -> G::Element { + G::sub(c1, &G::mul(c0, sk)) +} + +#[cfg(test)] +fn test_keygen_enc_dec() { + let mut rng = rand_core::OsRng; + let (sk, pk) = key_gen::(&mut rng); + let msg = G::rand_element(&mut rng); + let (c0, c1) = encrypt::(&mut rng, &pk, &msg); + assert_eq!(msg, decrypt::(&sk, &c0, &c1)); +} diff --git a/crates/aptos-crypto/src/encoding_type.rs b/crates/aptos-crypto/src/encoding_type.rs new file mode 100644 index 0000000..b60bcb5 --- /dev/null +++ b/crates/aptos-crypto/src/encoding_type.rs @@ -0,0 +1,127 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +//! This module provides utility for reading and writing crypto keys +//! in different formats used by the blockchain. + +use crate::{traits::ValidCryptoMaterialStringExt, ValidCryptoMaterial}; +use core::{ + fmt::{Display, Formatter}, + str::FromStr, +}; +use std::{fmt::Debug, path::Path}; +use thiserror::Error; + +/// Encoding error +#[derive(Debug, Error)] +pub enum EncodingError { + /// Error encoding or decoding BCS + #[error("Error (de)serializing '{0}': {1}")] + BCS(&'static str, bcs::Error), + /// Error for unable to parse + #[error("Unable to parse '{0}': error: {1}")] + UnableToParse(&'static str, String), + /// Error for unable to read a given file + #[error("Unable to read file '{0}', error: {1}")] + UnableToReadFile(String, String), + /// UTF8 error + #[error("Unexpected error: {0}")] + UTF8(String), +} + +impl std::convert::From for EncodingError { + fn from(e: std::string::FromUtf8Error) -> Self { + EncodingError::UTF8(e.to_string()) + } +} + +/// Types of encodings used by the blockchain +#[derive(Clone, Copy, Debug, Default)] +pub enum EncodingType { + /// Binary Canonical Serialization + BCS, + /// Hex encoded e.g. 0xABCDE12345 + #[default] + Hex, + /// Base 64 encoded + Base64, +} + +impl EncodingType { + /// Encodes `Key` into one of the `EncodingType`s + pub fn encode_key( + &self, + name: &'static str, + key: &Key, + ) -> Result, EncodingError> { + Ok(match self { + EncodingType::Hex => hex::encode_upper(key.to_bytes()).into_bytes(), + EncodingType::BCS => bcs::to_bytes(key).map_err(|err| EncodingError::BCS(name, err))?, + EncodingType::Base64 => base64::encode(key.to_bytes()).into_bytes(), + }) + } + + /// Loads a key from a file + pub fn load_key( + &self, + name: &'static str, + path: &Path, + ) -> Result { + self.decode_key(name, read_from_file(path)?) + } + + /// Decodes an encoded key given the known encoding + pub fn decode_key( + &self, + name: &'static str, + data: Vec, + ) -> Result { + match self { + EncodingType::BCS => { + bcs::from_bytes(&data).map_err(|err| EncodingError::BCS(name, err)) + } + EncodingType::Hex => { + let hex_string = String::from_utf8(data)?; + Key::from_encoded_string(hex_string.trim()) + .map_err(|err| EncodingError::UnableToParse(name, err.to_string())) + } + EncodingType::Base64 => { + let string = String::from_utf8(data)?; + let bytes = base64::decode(string.trim()) + .map_err(|err| EncodingError::UnableToParse(name, err.to_string()))?; + Key::try_from(bytes.as_slice()).map_err(|err| { + EncodingError::UnableToParse(name, format!("Failed to parse key {:?}", err)) + }) + } + } + } +} + +fn read_from_file(path: &Path) -> Result, EncodingError> { + std::fs::read(path) + .map_err(|e| EncodingError::UnableToReadFile(format!("{}", path.display()), e.to_string())) +} + +impl Display for EncodingType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let str = match self { + EncodingType::BCS => "bcs", + EncodingType::Hex => "hex", + EncodingType::Base64 => "base64", + }; + write!(f, "{}", str) + } +} + +impl FromStr for EncodingType { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "hex" => Ok(EncodingType::Hex), + "bcs" => Ok(EncodingType::BCS), + "base64" => Ok(EncodingType::Base64), + _ => Err("Invalid encoding type"), + } + } +} diff --git a/crates/aptos-crypto/src/error.rs b/crates/aptos-crypto/src/error.rs new file mode 100644 index 0000000..37ccf7c --- /dev/null +++ b/crates/aptos-crypto/src/error.rs @@ -0,0 +1,7 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//! Rexport the error types needed for the various crypto traits + +pub use anyhow::{bail, Error}; diff --git a/crates/aptos-crypto/src/hash.rs b/crates/aptos-crypto/src/hash.rs new file mode 100644 index 0000000..9c2687a --- /dev/null +++ b/crates/aptos-crypto/src/hash.rs @@ -0,0 +1,779 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//! This module defines traits and implementations of +//! [cryptographic hash functions](https://en.wikipedia.org/wiki/Cryptographic_hash_function) +//! +//! It is designed to help authors protect against two types of real world attacks: +//! +//! 1. **Semantic Ambiguity**: imagine that Alice has a private key and is using +//! two different applications, X and Y. X asks Alice to sign a message saying +//! "I am Alice". Alice accepts to sign this message in the context of X. However, +//! unbeknownst to Alice, in application Y, messages beginning with the letter "I" +//! represent transfers. " am " represents a transfer of 500 coins and "Alice" +//! can be interpreted as a destination address. When Alice signed the message she +//! needed to be aware of how other applications might interpret that message. +//! +//! 2. **Format Ambiguity**: imagine a program that hashes a pair of strings. +//! To hash the strings `a` and `b` it hashes `a + "||" + b`. The pair of +//! strings `a="foo||", b = "bar"` and `a="foo", b = "||bar"` result in the +//! same input to the hash function and therefore the same hash. This +//! creates a collision. +//! +//! Regarding (1), this library makes it easy for developers to create as +//! many new "hashable" Rust types as needed so that each Rust type hashed and signed +//! has a unique meaning, that is, unambiguously captures the intent of a signer. +//! +//! Regarding (2), this library provides the `CryptoHasher` abstraction to easily manage +//! cryptographic seeds for hashing. Hashing seeds aim to ensure that +//! the hashes of values of a given type `MyNewStruct` never collide with hashes of values +//! from another type. +//! +//! Finally, to prevent format ambiguity within a same type `MyNewStruct` and facilitate protocol +//! specifications, we use [Binary Canonical Serialization (BCS)](https://docs.rs/bcs/) +//! as the recommended solution to write Rust values into a hasher. +//! +//! # Quick Start +//! +//! To obtain a `hash()` method for any new type `MyNewStruct`, it is (strongly) recommended to +//! use the derive macros of `serde` and `aptos_crypto_derive` as follows: +//! ``` +//! use aptos_crypto::hash::CryptoHash; +//! use aptos_crypto_derive::{CryptoHasher, BCSCryptoHash}; +//! use serde::{Deserialize, Serialize}; +//! #[derive(Serialize, Deserialize, CryptoHasher, BCSCryptoHash)] +//! struct MyNewStruct { /*...*/ } +//! +//! let value = MyNewStruct { /*...*/ }; +//! value.hash(); +//! ``` +//! +//! Under the hood, this will generate a new implementation `MyNewStructHasher` for the trait +//! `CryptoHasher` and implement the trait `CryptoHash` for `MyNewStruct` using BCS. +//! +//! # Implementing New Hashers +//! +//! The trait `CryptoHasher` captures the notion of a pre-seeded hash function, aka a "hasher". +//! New implementations can be defined in two ways. +//! +//! ## Derive macro (recommended) +//! +//! For any new structure `MyNewStruct` that needs to be hashed, it is recommended to simply +//! use the derive macro [`CryptoHasher`](https://doc.rust-lang.org/reference/procedural-macros.html). +//! +//! ``` +//! use aptos_crypto_derive::CryptoHasher; +//! use serde::Deserialize; +//! #[derive(Deserialize, CryptoHasher)] +//! #[serde(rename = "OptionalCustomSerdeName")] +//! struct MyNewStruct { /*...*/ } +//! ``` +//! +//! The macro `CryptoHasher` will define a hasher automatically called `MyNewStructHasher`, and derive a salt +//! using the name of the type as seen by the Serde library. In the example above, this name +//! was changed using the Serde parameter `rename`: the salt will be based on the value `OptionalCustomSerdeName` +//! instead of the default name `MyNewStruct`. +//! +//! ## Customized hashers +//! +//! **IMPORTANT:** Do NOT use this for new code unless you know what you are doing. +//! +//! This library also provides a few customized hashers defined in the code as follows: +//! +//! ``` +//! # // To get around that there's no way to doc-test a non-exported macro: +//! # macro_rules! define_hasher { ($e:expr) => () } +//! define_hasher! { (MyNewDataHasher, MY_NEW_DATA_HASHER, MY_NEW_DATA_SEED, b"MyUniqueSaltString") } +//! ``` +//! +//! # Using a hasher directly +//! +//! **IMPORTANT:** Do NOT use this for new code unless you know what you are doing. +//! +//! ``` +//! use aptos_crypto::hash::{CryptoHasher, TestOnlyHasher}; +//! +//! let mut hasher = TestOnlyHasher::default(); +//! hasher.update("Test message".as_bytes()); +//! let hash_value = hasher.finish(); +//! ``` +#![allow(clippy::arithmetic_side_effects)] +use bytes::Bytes; +use hex::FromHex; +use more_asserts::debug_assert_lt; +use once_cell::sync::{Lazy, OnceCell}; +use openrpc_schema::schemars::gen::SchemaGenerator; +use openrpc_schema::schemars::schema::{InstanceType, Schema, SchemaObject}; +use openrpc_schema::schemars::JsonSchema; +#[cfg(any(test, feature = "fuzzing"))] +use proptest_derive::Arbitrary; +use rand::{rngs::OsRng, Rng}; +use serde::{de, ser}; +use std::{ + self, + convert::{AsRef, TryFrom}, + fmt, + str::FromStr, +}; +use tiny_keccak::{Hasher, Sha3}; + +/// A prefix used to begin the salt of every hashable structure. The salt +/// consists in this global prefix, concatenated with the specified +/// serialization name of the struct. +pub(crate) const HASH_PREFIX: &[u8] = b"STARCOIN::"; + +/// Output value of our hash function. Intentionally opaque for safety and modularity. +#[derive(Clone, Copy, Eq, Hash, PartialEq, PartialOrd, Ord)] +#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))] +pub struct HashValue { + hash: [u8; HashValue::LENGTH], +} + +impl HashValue { + /// The length of the hash in bytes. + pub const LENGTH: usize = 32; + /// The length of the hash in bits. + pub const LENGTH_IN_BITS: usize = Self::LENGTH * 8; + + /// Create a new [`HashValue`] from a byte array. + pub fn new(hash: [u8; HashValue::LENGTH]) -> Self { + HashValue { hash } + } + + /// Create from a slice (e.g. retrieved from storage). + pub fn from_slice>(bytes: T) -> Result { + <[u8; Self::LENGTH]>::try_from(bytes.as_ref()) + .map_err(|_| HashValueParseError) + .map(Self::new) + } + + /// Dumps into a vector. + pub fn to_vec(&self) -> Vec { + self.hash.to_vec() + } + + /// Creates a zero-initialized instance. + pub const fn zero() -> Self { + HashValue { + hash: [0; HashValue::LENGTH], + } + } + + /// Create a cryptographically random instance. + pub fn random() -> Self { + let mut rng = OsRng; + let hash: [u8; HashValue::LENGTH] = rng.gen(); + HashValue { hash } + } + + /// Creates a random instance with given rng. Useful in unit tests. + pub fn random_with_rng(rng: &mut R) -> Self { + let hash: [u8; HashValue::LENGTH] = rng.gen(); + HashValue { hash } + } + + /// Convenience function that computes a `HashValue` internally equal to + /// the sha3_256 of a byte buffer. It will handle hasher creation, data + /// feeding and finalization. + /// + /// Note this will not result in the `::hash()` for any + /// reasonable struct T, as this computes a sha3 without any ornaments. + pub fn sha3_256_of(buffer: &[u8]) -> Self { + let mut sha3 = Sha3::v256(); + sha3.update(buffer); + HashValue::from_keccak(sha3) + } + + #[cfg(test)] + pub fn from_iter_sha3<'a, I>(buffers: I) -> Self + where + I: IntoIterator, + { + let mut sha3 = Sha3::v256(); + for buffer in buffers { + sha3.update(buffer); + } + HashValue::from_keccak(sha3) + } + + fn as_ref_mut(&mut self) -> &mut [u8] { + &mut self.hash[..] + } + + fn from_keccak(state: Sha3) -> Self { + let mut hash = Self::zero(); + state.finalize(hash.as_ref_mut()); + hash + } + + /// Returns the `index`-th bit in the bytes. + pub fn bit(&self, index: usize) -> bool { + debug_assert!(index < Self::LENGTH_IN_BITS); // assumed precondition + let pos = index / 8; + let bit = 7 - index % 8; + (self.hash[pos] >> bit) & 1 != 0 + } + + /// Returns the `index`-th nibble in the bytes. + pub fn nibble(&self, index: usize) -> u8 { + debug_assert!(index < Self::LENGTH * 2); // assumed precondition + let pos = index / 2; + let shift = if index % 2 == 0 { 4 } else { 0 }; + (self.hash[pos] >> shift) & 0x0F + } + + /// Return the `index`-th byte in the bytes. + pub fn byte(&self, index: usize) -> u8 { + debug_assert!(index < Self::LENGTH); // assumed precondition + self.hash[index] + } + + /// Returns a `HashValueBitIterator` over all the bits that represent this `HashValue`. + pub fn iter_bits(&self) -> HashValueBitIterator<'_> { + HashValueBitIterator::new(self) + } + + /// Constructs a `HashValue` from an iterator of bits. + pub fn from_bit_iter( + iter: impl ExactSizeIterator, + ) -> Result { + if iter.len() != Self::LENGTH_IN_BITS { + return Err(HashValueParseError); + } + + let mut buf = [0; Self::LENGTH]; + for (i, bit) in iter.enumerate() { + if bit { + buf[i / 8] |= 1 << (7 - i % 8); + } + } + Ok(Self::new(buf)) + } + + /// Returns the length of common prefix of `self` and `other` in bits. + pub fn common_prefix_bits_len(&self, other: HashValue) -> usize { + self.iter_bits() + .zip(other.iter_bits()) + .take_while(|(x, y)| x == y) + .count() + } + + /// Full hex representation of a given hash value. + pub fn to_hex(&self) -> String { + format!("{:x}", self) + } + + /// Full hex representation of a given hash value with `0x` prefix. + pub fn to_hex_literal(&self) -> String { + format!("{:#x}", self) + } + + /// Parse a given hex string to a hash value. + pub fn from_hex>(hex: T) -> Result { + <[u8; Self::LENGTH]>::from_hex(hex) + .map_err(|_| HashValueParseError) + .map(Self::new) + } + + /// Create a hash value whose contents are just the given integer. Useful for + /// generating basic mock hash values. + /// + /// Ex: HashValue::from_u64(0x1234) => HashValue([0, .., 0, 0x12, 0x34]) + #[cfg(any(test, feature = "fuzzing"))] + pub fn from_u64(v: u64) -> Self { + let mut hash = [0u8; Self::LENGTH]; + let bytes = v.to_be_bytes(); + hash[Self::LENGTH - bytes.len()..].copy_from_slice(&bytes[..]); + Self::new(hash) + } + + #[inline(always)] + pub fn from_le_u64(arr: [u64; 4]) -> Self { + let mut ret = [0; Self::LENGTH]; + ret.chunks_exact_mut(8) + .zip(arr.iter()) + .for_each(|(bytes, word)| bytes.copy_from_slice(&word.to_le_bytes())); + Self::new(ret) + } + #[inline(always)] + pub fn from_u64_word(word: u64) -> Self { + Self::from_le_u64([0, 0, 0, word]) + } + + /// Parse a given hex string to a hash value + pub fn from_hex_literal(literal: &str) -> Result { + if literal.is_empty() { + return Err(HashValueParseError); + } + let literal = literal.strip_prefix("0x").unwrap_or_else(|| literal); + let hex_len = literal.len(); + // If the string is too short, pad it + if hex_len < Self::LENGTH * 2 { + let mut hex_str = String::with_capacity(Self::LENGTH * 2); + for _ in 0..Self::LENGTH * 2 - hex_len { + hex_str.push('0'); + } + hex_str.push_str(&literal); + Self::from_hex(hex_str) + } else { + Self::from_hex(&literal) + } + } +} + +impl ser::Serialize for HashValue { + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + if serializer.is_human_readable() { + serializer.serialize_str(&self.to_string()) + } else { + // In order to preserve the Serde data model and help analysis tools, + // make sure to wrap our value in a container with the same name + // as the original type. + serializer + .serialize_newtype_struct("HashValue", serde_bytes::Bytes::new(&self.hash[..])) + } + } +} + +impl<'de> de::Deserialize<'de> for HashValue { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + if deserializer.is_human_readable() { + let encoded_hash = ::deserialize(deserializer)?; + HashValue::from_str(encoded_hash.as_str()) + .map_err(::custom) + } else { + // See comment in serialize. + #[derive(::serde::Deserialize)] + #[serde(rename = "HashValue")] + struct Value<'a>(&'a [u8]); + + let value = Value::deserialize(deserializer)?; + Self::from_slice(value.0).map_err(::custom) + } + } +} + +impl Default for HashValue { + fn default() -> Self { + HashValue::zero() + } +} + +impl AsRef<[u8; HashValue::LENGTH]> for HashValue { + fn as_ref(&self) -> &[u8; HashValue::LENGTH] { + &self.hash + } +} + +impl std::ops::Deref for HashValue { + type Target = [u8; Self::LENGTH]; + + fn deref(&self) -> &Self::Target { + &self.hash + } +} + +impl std::ops::Index for HashValue { + type Output = u8; + + fn index(&self, s: usize) -> &u8 { + self.hash.index(s) + } +} + +impl fmt::Binary for HashValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for byte in &self.hash { + write!(f, "{:08b}", byte)?; + } + Ok(()) + } +} + +impl fmt::LowerHex for HashValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { + write!(f, "0x")?; + } + for byte in &self.hash { + write!(f, "{:02x}", byte)?; + } + Ok(()) + } +} + +impl fmt::Debug for HashValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "HashValue({:#x})", self) + } +} + +impl fmt::Display for HashValue { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:#x}", self) + } +} +impl From for Bytes { + fn from(value: HashValue) -> Bytes { + Bytes::copy_from_slice(value.hash.as_ref()) + } +} + +impl FromStr for HashValue { + type Err = HashValueParseError; + + fn from_str(s: &str) -> Result { + HashValue::from_hex_literal(s) + } +} + +/// Parse error when attempting to construct a HashValue +#[derive(Clone, Copy, Debug)] +pub struct HashValueParseError; + +impl fmt::Display for HashValueParseError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "unable to parse HashValue") + } +} + +impl std::error::Error for HashValueParseError {} + +/// An iterator over `HashValue` that generates one bit for each iteration. +pub struct HashValueBitIterator<'a> { + /// The reference to the bytes that represent the `HashValue`. + hash_bytes: &'a [u8], + pos: std::ops::Range, + // invariant hash_bytes.len() == HashValue::LENGTH; + // invariant pos.end == hash_bytes.len() * 8; +} + +impl<'a> HashValueBitIterator<'a> { + /// Constructs a new `HashValueBitIterator` using given `HashValue`. + fn new(hash_value: &'a HashValue) -> Self { + HashValueBitIterator { + hash_bytes: hash_value.as_ref(), + pos: (0..HashValue::LENGTH_IN_BITS), + } + } + + /// Returns the `index`-th bit in the bytes. + fn get_bit(&self, index: usize) -> bool { + debug_assert_eq!(self.hash_bytes.len(), HashValue::LENGTH); // invariant + debug_assert_lt!(index, HashValue::LENGTH_IN_BITS); // assumed precondition + let pos = index / 8; + let bit = 7 - index % 8; + (self.hash_bytes[pos] >> bit) & 1 != 0 + } +} + +impl<'a> std::iter::Iterator for HashValueBitIterator<'a> { + type Item = bool; + + fn next(&mut self) -> Option { + self.pos.next().map(|x| self.get_bit(x)) + } + + fn size_hint(&self) -> (usize, Option) { + self.pos.size_hint() + } +} + +impl<'a> std::iter::DoubleEndedIterator for HashValueBitIterator<'a> { + fn next_back(&mut self) -> Option { + self.pos.next_back().map(|x| self.get_bit(x)) + } +} + +impl<'a> std::iter::ExactSizeIterator for HashValueBitIterator<'a> {} + +/// A type that can be cryptographically hashed to produce a `HashValue`. +/// +/// In most cases, this trait should not be implemented manually but rather derived using +/// the macros `serde::Serialize`, `CryptoHasher`, and `BCSCryptoHash`. +pub trait CryptoHash { + /// The associated `Hasher` type which comes with a unique salt for this type. + type Hasher: CryptoHasher; + + /// Hashes the object and produces a `HashValue`. + fn hash(&self) -> HashValue; +} + +/// A trait for representing the state of a cryptographic hasher. +pub trait CryptoHasher: Default + std::io::Write { + /// the seed used to initialize hashing `Self` before the serialization bytes of the actual value + fn seed() -> &'static [u8; 32]; + + /// Write bytes into the hasher. + fn update(&mut self, bytes: &[u8]); + + /// Finish constructing the [`HashValue`]. + fn finish(self) -> HashValue; + + /// Convenience method to compute the hash of a complete byte slice. + fn hash_all(bytes: &[u8]) -> HashValue { + let mut hasher = Self::default(); + hasher.update(bytes); + hasher.finish() + } +} + +/// The default hasher underlying generated implementations of `CryptoHasher`. +#[doc(hidden)] +#[derive(Clone)] +pub struct DefaultHasher { + state: Sha3, +} + +impl DefaultHasher { + #[doc(hidden)] + /// This function does not return a HashValue in the sense of our usual + /// hashes, but a construction of initial bytes that are fed into any hash + /// provided we're passed a (bcs) serialization name as argument. + pub fn prefixed_hash(buffer: &[u8]) -> [u8; HashValue::LENGTH] { + // The salt is initial material we prefix to actual value bytes for + // domain separation. Its length is variable. + let salt: Vec = [HASH_PREFIX, buffer].concat(); + // The seed is a fixed-length hash of the salt, thereby preventing + // suffix attacks on the domain separation bytes. + HashValue::sha3_256_of(&salt[..]).hash + } + + #[doc(hidden)] + pub fn new(typename: &[u8]) -> Self { + let mut state = Sha3::v256(); + if !typename.is_empty() { + state.update(&Self::prefixed_hash(typename)); + } + DefaultHasher { state } + } + + #[doc(hidden)] + pub fn update(&mut self, bytes: &[u8]) { + self.state.update(bytes); + } + + #[doc(hidden)] + pub fn finish(self) -> HashValue { + let mut hasher = HashValue::default(); + self.state.finalize(hasher.as_ref_mut()); + hasher + } +} + +impl fmt::Debug for DefaultHasher { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "DefaultHasher: state = Sha3") + } +} + +macro_rules! define_hasher { + ( + $(#[$attr:meta])* + ($hasher_type: ident, $hasher_name: ident, $seed_name: ident, $salt: expr) + ) => { + + #[derive(Clone, Debug)] + $(#[$attr])* + pub struct $hasher_type(DefaultHasher); + + impl $hasher_type { + fn new() -> Self { + $hasher_type(DefaultHasher::new($salt)) + } + } + + static $hasher_name: Lazy<$hasher_type> = Lazy::new(|| { $hasher_type::new() }); + static $seed_name: OnceCell<[u8; 32]> = OnceCell::new(); + + impl Default for $hasher_type { + fn default() -> Self { + $hasher_name.clone() + } + } + + impl CryptoHasher for $hasher_type { + fn seed() -> &'static [u8;32] { + $seed_name.get_or_init(|| { + DefaultHasher::prefixed_hash($salt) + }) + } + + fn update(&mut self, bytes: &[u8]) { + self.0.update(bytes); + } + + fn finish(self) -> HashValue { + self.0.finish() + } + } + + impl std::io::Write for $hasher_type { + fn write(&mut self, bytes: &[u8]) -> std::io::Result { + self.0.update(bytes); + Ok(bytes.len()) + } + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } + } + }; +} + +define_hasher! { + /// The hasher used to compute the hash of an internal node in the transaction accumulator. + ( + TransactionAccumulatorHasher, + TRANSACTION_ACCUMULATOR_HASHER, + TRANSACTION_ACCUMULATOR_SEED, + b"TransactionAccumulator" + ) +} + +define_hasher! { + /// The hasher used to compute the hash of an internal node in the event accumulator. + ( + EventAccumulatorHasher, + EVENT_ACCUMULATOR_HASHER, + EVENT_ACCUMULATOR_SEED, + b"EventAccumulator" + ) +} + +define_hasher! { + /// The hasher used to compute the hash of an internal node in the Sparse Merkle Tree. + ( + SparseMerkleInternalHasher, + SPARSE_MERKLE_INTERNAL_HASHER, + SPARSE_MERKLE_INTERNAL_SEED, + b"SparseMerkleInternal" + ) +} + +define_hasher! { + /// The hasher used as a placeholder. + ( + DummyHasher, + DUMMY_HASHER, + DUMMY_SEED, + b"Dummy" + ) +} + +define_hasher! { + /// The hasher used only for testing. It doesn't have a salt. + (TestOnlyHasher, TEST_ONLY_HASHER, TEST_ONLY_SEED, b"") +} + +fn create_literal_hash(word: &str) -> HashValue { + let mut s = word.as_bytes().to_vec(); + assert!(s.len() <= HashValue::LENGTH); + s.resize(HashValue::LENGTH, 0); + HashValue::from_slice(&s).expect("Cannot fail") +} + +/// Placeholder hash of `Accumulator`. +pub static ACCUMULATOR_PLACEHOLDER_HASH: Lazy = + Lazy::new(|| create_literal_hash("ACCUMULATOR_PLACEHOLDER_HASH")); + +/// Placeholder hash of `SparseMerkleTree`. +pub static SPARSE_MERKLE_PLACEHOLDER_HASH: Lazy = + Lazy::new(|| create_literal_hash("SPARSE_MERKLE_PLACEHOLDER_HASH")); + +/// Block id reserved as the id of parent block of the genesis block. +pub static PRE_GENESIS_BLOCK_ID: Lazy = + Lazy::new(|| create_literal_hash("PRE_GENESIS_BLOCK_ID")); + +/// Genesis block id is used as a parent of the very first block executed by the executor. +pub static GENESIS_BLOCK_ID: Lazy = Lazy::new(|| { + // This maintains the invariant that block.id() == block.hash(), for + // the genesis block and allows us to (de/)serialize it consistently + HashValue::new([ + 0x5E, 0x10, 0xBA, 0xD4, 0x5B, 0x35, 0xED, 0x92, 0x9C, 0xD6, 0xD2, 0xC7, 0x09, 0x8B, 0x13, + 0x5D, 0x02, 0xDD, 0x25, 0x9A, 0xE8, 0x8A, 0x8D, 0x09, 0xF4, 0xEB, 0x5F, 0xBA, 0xE9, 0xA6, + 0xF6, 0xE4, + ]) +}); + +/// Provides a test_only_hash() method that can be used in tests on types that implement +/// `serde::Serialize`. +/// +/// # Example +/// ``` +/// use aptos_crypto::hash::TestOnlyHash; +/// +/// b"hello world".test_only_hash(); +/// ``` +pub trait TestOnlyHash { + /// Generates a hash used only for tests. + fn test_only_hash(&self) -> HashValue; +} + +impl TestOnlyHash for T { + fn test_only_hash(&self) -> HashValue { + let bytes = bcs::to_bytes(self).expect("serialize failed during hash."); + let mut hasher = TestOnlyHasher::default(); + hasher.update(&bytes); + hasher.finish() + } +} + +impl JsonSchema for HashValue { + fn schema_name() -> String { + "HashValue".to_owned() + } + + fn json_schema(_: &mut SchemaGenerator) -> Schema { + SchemaObject { + instance_type: Some(InstanceType::String.into()), + format: Some("HashValue".to_owned()), + ..Default::default() + } + .into() + } +} + +impl From for HashValue { + #[inline(always)] + fn from(word: u64) -> Self { + Self::from_u64_word(word) + } +} + +#[cfg(test)] +mod tests { + use crate::HashValue; + + #[test] + fn test_serialize() { + let hash = HashValue::random(); + + let json_value = serde_json::to_string(&hash).unwrap(); + println!("{}", json_value); + assert_eq!(json_value, format!("\"{}\"", hash.to_string())); + + let de_hash = serde_json::from_slice::(json_value.as_bytes()).unwrap(); + let de_hash2: HashValue = serde_json::from_str::(&json_value).unwrap(); + assert_eq!(hash, de_hash); + assert_eq!(hash, de_hash2); + } + + #[test] + fn test_serialize_and_deserialize() { + let hash = HashValue::zero(); + let bytes = bcs::to_bytes(&hash).unwrap(); + const BUF_SIZE: usize = 33; + let mut buf: [u8; BUF_SIZE] = [0; BUF_SIZE]; + buf[0] = HashValue::LENGTH as u8; + assert_eq!(bytes.as_slice(), buf); + let hash1 = bcs::from_bytes::(&bytes).unwrap(); + assert_eq!(hash1, hash); + } +} diff --git a/crates/aptos-crypto/src/hkdf.rs b/crates/aptos-crypto/src/hkdf.rs new file mode 100644 index 0000000..efda450 --- /dev/null +++ b/crates/aptos-crypto/src/hkdf.rs @@ -0,0 +1,199 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//! An implementation of HKDF, the HMAC-based Extract-and-Expand Key Derivation Function +//! based on [RFC 5869](https://tools.ietf.org/html/rfc5869). +//! +//! The key derivation function (KDF) is intended to support a wide range of applications and +//! requirements, and is conservative in its use of cryptographic hash functions. In particular, +//! this implementation is compatible with hash functions that output 256 bits or more, such as +//! SHA256, SHA3-256 and SHA512. +//! +//! HKDF follows the "extract-then-expand" paradigm, where the KDF logically consists of two +//! modules: the first stage takes the input keying material (the seed) and "extracts" from it a +//! fixed-length pseudorandom key, and then the second stage "expands" this key into several +//! additional pseudorandom keys (the output of the KDF). For convenience, a function that runs both +//! steps in a single call is provided. Note that along with an initial high-entropy seed, a user +//! can optionally provide salt and app-info byte-arrays for extra security guarantees and domain +//! separation. +//! +//! # Applications +//! +//! HKDF is intended for use in a wide variety of KDF applications (see [Key derivation function](https://en.wikipedia.org/wiki/Key_derivation_function)), including: +//! a) derivation of keys from an origin high-entropy master seed. This is the recommended approach +//! for generating keys, especially when a True Random Generator is not available. +//! b) derivation of session keys from a shared Diffie-Hellman value in a key-agreement protocol. +//! c) combining entropy from multiple sources of randomness, such as entropy collected +//! from system events, user's keystrokes, /dev/urandom etc. The combined seed can then be used to +//! generate cryptographic keys for account, network and transaction signing keys among the others. +//! d) hierarchical private key derivation, similarly to Bitcoin's BIP32 protocol for easier key +//! management. +//! e) hybrid key generation that combines a master seed with a PRNG output for extra security +//! guarantees against a master seed leak or low PRNG entropy. +//! +//! # Recommendations +//! +//! **Salt** +//! HKDF can operate with and without random 'salt'. The use of salt adds to the strength of HKDF, +//! ensuring independence between different uses of the hash function, supporting +//! "source-independent" extraction, and strengthening the HKDF use. The salt value should be a +//! random string of the same length as the hash output. A shorter or less random salt value can +//! still make a contribution to the security of the output key material. Salt values should be +//! independent of the input keying material. In particular, an application needs to make sure that +//! salt values are not chosen or manipulated by an attacker. +//! +//! *Application info* +//! Key expansion accepts an optional 'info' value to which the application assigns some meaning. +//! Its objective is to bind the derived key material to application- and context-specific +//! information. For example, 'info' may contain a protocol number, algorithm identifier, +//! child key number (similarly to [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)), etc. The only technical requirement for 'info' is that +//! it be independent of the seed. +//! +//! **Which function to use: extract, expand or both?** +//! Unless absolutely sure of what they are doing, applications should use both steps — if only for +//! the sake of compatibility with the general case. +//! +//! # Example +//! +//! Run HKDF extract-then-expand so as to return 64 bytes, using 'salt', 'seed' and 'info' as +//! inputs. +//! ``` +//! use aptos_crypto::hkdf::Hkdf; +//! use sha2::Sha256; +//! +//! // some bytes required for this example. +//! let raw_bytes = [2u8; 10]; +//! // define salt +//! let salt = Some(&raw_bytes[0..4]); +//! // define seed - in production this is recommended to be a 32 bytes or longer random seed. +//! let seed = [3u8; 32]; +//! // define application info +//! let info = Some(&raw_bytes[4..10]); +//! +//! // HKDF extract-then-expand 64-bytes output +//! let derived_bytes = Hkdf::::extract_then_expand(salt, &seed, info, 64); +//! assert_eq!(derived_bytes.unwrap().len(), 64) +//! ``` + +use digest::{ + generic_array::{self, ArrayLength}, + BlockInput, FixedOutput, Reset, Update, +}; +use generic_array::typenum::{IsGreaterOrEqual, True, U32}; +use std::marker::PhantomData; +use thiserror::Error; + +/// Hash function is not supported if its output is less than 32 bits. +type DMinimumSize = U32; + +/// Seed (ikm = initial key material) is not accepted if its size is less than 16 bytes. This is a +/// precautionary measure to prevent HKDF misuse. 128 bits is the minimum accepted seed entropy +/// length in the majority of today's applications to avoid brute forcing. +/// Note that for Ed25519 keys, random seeds of at least 32 bytes are recommended. +const MINIMUM_SEED_LENGTH: usize = 16; + +/// Structure representing the HKDF, capable of HKDF-Extract and HKDF-Expand operations, as defined +/// in RFC 5869. +#[derive(Clone, Debug)] +pub struct Hkdf +where + D: Update + BlockInput + FixedOutput + Reset + Default + Clone, + D::BlockSize: ArrayLength, + D::OutputSize: ArrayLength, + D::OutputSize: IsGreaterOrEqual, +{ + _marker: PhantomData, +} + +impl Hkdf +where + D: Update + BlockInput + FixedOutput + Reset + Default + Clone, + D::BlockSize: ArrayLength + Clone, + D::OutputSize: ArrayLength, + D::OutputSize: IsGreaterOrEqual, +{ + /// The RFC5869 HKDF-Extract operation. + pub fn extract(salt: Option<&[u8]>, ikm: &[u8]) -> Result, HkdfError> { + if ikm.len() < MINIMUM_SEED_LENGTH { + return Err(HkdfError::InvalidSeedLengthError); + } + Ok(Hkdf::::extract_no_ikm_check(salt, ikm)) + } + + fn extract_no_ikm_check(salt: Option<&[u8]>, ikm: &[u8]) -> Vec { + let (arr, _hkdf) = hkdf::Hkdf::::extract(salt, ikm); + arr.to_vec() + } + + /// The RFC5869 HKDF-Expand operation. + pub fn expand(prk: &[u8], info: Option<&[u8]>, length: usize) -> Result, HkdfError> { + // According to RFC5869, MAX_OUTPUT_LENGTH <= 255 * HashLen — which is + // checked below. + // We specifically exclude a zero size length as well. + if length == 0 { + return Err(HkdfError::InvalidOutputLengthError); + } + + let hkdf = + hkdf::Hkdf::::from_prk(prk).map_err(|_| HkdfError::WrongPseudorandomKeyError)?; + let mut okm = vec![0u8; length]; + hkdf.expand(info.unwrap_or(&[]), &mut okm) + // length > D::OutputSize::to_usize() * 255 + .map_err(|_| HkdfError::InvalidOutputLengthError)?; + Ok(okm) + } + + /// HKDF Extract then Expand operation as a single step. + pub fn extract_then_expand( + salt: Option<&[u8]>, + ikm: &[u8], + info: Option<&[u8]>, + length: usize, + ) -> Result, HkdfError> { + let prk = Hkdf::::extract(salt, ikm)?; + Hkdf::::expand(&prk, info, length) + } + + /// CAUTION: This is not recommended because it does not take an ikm (seed) as an input and + /// thus, it is not fully compliant with the HKDF RFC (which always expects a non-zero ikm). + /// Please use `extract_then_expand` instead, unless you know what you are doing. + /// + /// This api is currently required by the Noise protocol [HKDF specs](https://noiseprotocol.org/noise.html#hash-functions). + /// + /// HKDF Extract then Expand operation as a single step, but without an ikm input. + pub fn extract_then_expand_no_ikm( + salt: Option<&[u8]>, + info: Option<&[u8]>, + length: usize, + ) -> Result, HkdfError> { + let prk = Hkdf::::extract_no_ikm_check(salt, &[]); + Hkdf::::expand(&prk, info, length) + } +} + +/// An error type for HKDF key derivation issues. +/// +/// This enum reflects there are various causes of HKDF failures, including: +/// a) requested HKDF output size exceeds the maximum allowed or is zero. +/// b) hash functions outputting less than 32 bits are not supported (i.e., SHA1 is not supported). +/// c) small PRK value in HKDF-Expand according to RFC 5869. +/// d) any other underlying HMAC error. +#[derive(Clone, Debug, PartialEq, Eq, Error)] +pub enum HkdfError { + /// HKDF expand output exceeds the maximum allowed or is zero. + #[error("HKDF expand error - requested output size exceeds the maximum allowed or is zero")] + InvalidOutputLengthError, + /// PRK on HKDF-Expand should not be less than the underlying hash output bits. + #[error( + "HKDF expand error - the pseudorandom key input ('prk' in RFC 5869) \ + is less than the underlying hash output bits" + )] + WrongPseudorandomKeyError, + /// HMAC key related error; unlikely to happen because every key size is accepted in HMAC. + #[error("HMAC key error")] + MACKeyError, + /// HKDF extract input seed should not be less than the minimum accepted. + #[error("HKDF extract error - input seed is too small")] + InvalidSeedLengthError, +} diff --git a/crates/aptos-crypto/src/lib.rs b/crates/aptos-crypto/src/lib.rs new file mode 100644 index 0000000..2141f19 --- /dev/null +++ b/crates/aptos-crypto/src/lib.rs @@ -0,0 +1,38 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#![forbid(unsafe_code)] +#![deny(missing_docs)] + +//! A library supplying various cryptographic primitives +pub mod asymmetric_encryption; +pub mod bls12381; +pub mod bulletproofs; +pub mod compat; +pub mod ed25519; +pub mod elgamal; +pub mod encoding_type; +pub mod error; +pub mod hash; +pub mod hkdf; +pub mod multi_ed25519; +pub mod noise; +pub mod secp256k1_ecdsa; +pub mod secp256r1_ecdsa; +pub mod test_utils; +pub mod traits; +pub mod validatable; +pub mod x25519; + +pub mod poseidon_bn254; +#[cfg(test)] +mod unit_tests; + +pub use self::traits::*; +pub use hash::HashValue; +// Reexport once_cell and serde_name for use in CryptoHasher Derive implementation. +#[doc(hidden)] +pub use once_cell as _once_cell; +#[doc(hidden)] +pub use serde_name as _serde_name; diff --git a/crates/aptos-crypto/src/multi_ed25519.rs b/crates/aptos-crypto/src/multi_ed25519.rs new file mode 100644 index 0000000..2b28052 --- /dev/null +++ b/crates/aptos-crypto/src/multi_ed25519.rs @@ -0,0 +1,666 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//! This module provides an API for the accountable threshold multi-sig PureEdDSA signature scheme +//! over the ed25519 twisted Edwards curve as defined in [RFC8032](https://tools.ietf.org/html/rfc8032). +//! +//! Signature verification also checks and rejects non-canonical signatures. + +use crate::{ + ed25519::{ + Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature, ED25519_PRIVATE_KEY_LENGTH, + ED25519_PUBLIC_KEY_LENGTH, ED25519_SIGNATURE_LENGTH, + }, + hash::{CryptoHash, CryptoHasher}, + traits::*, +}; +use anyhow::{anyhow, Result}; +use aptos_crypto_derive::{DeserializeKey, SerializeKey, SilentDebug, SilentDisplay}; +use core::convert::TryFrom; +use rand::Rng; +use serde::Serialize; +use std::{convert::TryInto, fmt}; + +/// const for max number of ed25519 keys allowed for multi-ed25519 keys +pub const MAX_NUM_OF_KEYS: usize = 32; +/// Number of bytes used for the bitmap in a MultiEd25519 private key +pub const BITMAP_NUM_OF_BYTES: usize = 4; + +/// Vector of private keys in the multi-key Ed25519 structure along with the threshold. +#[derive(DeserializeKey, Eq, PartialEq, SilentDisplay, SilentDebug, SerializeKey)] +pub struct MultiEd25519PrivateKey { + private_keys: Vec, + threshold: u8, +} + +#[cfg(feature = "assert-private-keys-not-cloneable")] +static_assertions::assert_not_impl_any!(MultiEd25519PrivateKey: Clone); + +/// Vector of public keys in the multi-key Ed25519 structure along with the threshold. +#[derive(Clone, DeserializeKey, Eq, PartialEq, SerializeKey)] +pub struct MultiEd25519PublicKey { + public_keys: Vec, + threshold: u8, +} + +/// Vector of the multi-key signatures along with a 32bit [u8; 4] bitmap required to map signatures +/// with their corresponding public keys. +/// +/// Note that bits are read from left to right. For instance, in the following bitmap +/// [0b0001_0000, 0b0000_0000, 0b0000_0000, 0b0000_0001], the 3rd and 31st positions are set. +#[derive(Clone, DeserializeKey, Eq, PartialEq, SerializeKey)] +pub struct MultiEd25519Signature { + signatures: Vec, + bitmap: [u8; BITMAP_NUM_OF_BYTES], +} + +impl MultiEd25519PrivateKey { + /// Construct a new MultiEd25519PrivateKey. + pub fn new( + private_keys: Vec, + threshold: u8, + ) -> std::result::Result { + let num_of_private_keys = private_keys.len(); + if threshold == 0 || num_of_private_keys < threshold as usize { + Err(CryptoMaterialError::ValidationError) + } else if num_of_private_keys > MAX_NUM_OF_KEYS { + Err(CryptoMaterialError::WrongLengthError) + } else { + Ok(MultiEd25519PrivateKey { + private_keys, + threshold, + }) + } + } + + /// Serialize a MultiEd25519PrivateKey. + pub fn to_bytes(&self) -> Vec { + to_bytes(&self.private_keys, self.threshold) + } +} + +impl MultiEd25519PublicKey { + /// Construct a new MultiEd25519PublicKey. + /// --- Rules --- + /// a) threshold cannot be zero. + /// b) public_keys.len() should be equal to or larger than threshold. + /// c) support up to MAX_NUM_OF_KEYS public keys. + pub fn new( + public_keys: Vec, + threshold: u8, + ) -> std::result::Result { + let num_of_public_keys = public_keys.len(); + if threshold == 0 || num_of_public_keys < threshold as usize { + Err(CryptoMaterialError::ValidationError) + } else if num_of_public_keys > MAX_NUM_OF_KEYS { + Err(CryptoMaterialError::WrongLengthError) + } else { + Ok(MultiEd25519PublicKey { + public_keys, + threshold, + }) + } + } + + /// Getter public_keys + pub fn public_keys(&self) -> &Vec { + &self.public_keys + } + + /// Getter threshold + pub fn threshold(&self) -> &u8 { + &self.threshold + } + + /// Serialize a MultiEd25519PublicKey. + pub fn to_bytes(&self) -> Vec { + to_bytes(&self.public_keys, self.threshold) + } +} + +/////////////////////// +// PrivateKey Traits // +/////////////////////// + +/// Convenient method to create a MultiEd25519PrivateKey from a single Ed25519PrivateKey. +impl From<&Ed25519PrivateKey> for MultiEd25519PrivateKey { + fn from(ed_private_key: &Ed25519PrivateKey) -> Self { + MultiEd25519PrivateKey { + private_keys: vec![Ed25519PrivateKey::try_from(&ed_private_key.to_bytes()[..]).unwrap()], + threshold: 1u8, + } + } +} + +impl PrivateKey for MultiEd25519PrivateKey { + type PublicKeyMaterial = MultiEd25519PublicKey; +} + +impl SigningKey for MultiEd25519PrivateKey { + type SignatureMaterial = MultiEd25519Signature; + type VerifyingKeyMaterial = MultiEd25519PublicKey; + + /// Uses the first `threshold` private keys to create a MultiEd25519 signature on `message`. + /// (Used for testing only.) + fn sign( + &self, + message: &T, + ) -> Result { + let mut bitmap = [0u8; BITMAP_NUM_OF_BYTES]; + let mut signatures: Vec = vec![]; + + for (i, private_key) in self + .private_keys + .iter() + .take(self.threshold as usize) + .enumerate() + { + bitmap_set_bit(&mut bitmap, i); + signatures.push(private_key.sign(message)?); + } + + Ok(MultiEd25519Signature { signatures, bitmap }) + } + + #[cfg(any(test, feature = "fuzzing"))] + fn sign_arbitrary_message(&self, message: &[u8]) -> MultiEd25519Signature { + let mut signatures: Vec = Vec::with_capacity(self.threshold as usize); + let mut bitmap = [0u8; BITMAP_NUM_OF_BYTES]; + for (i, private_key) in self + .private_keys + .iter() + .take(self.threshold as usize) + .enumerate() + { + bitmap_set_bit(&mut bitmap, i); + signatures.push(private_key.sign_arbitrary_message(message)); + } + + MultiEd25519Signature { signatures, bitmap } + } +} + +// Generating a random K out-of N key for testing. +impl Uniform for MultiEd25519PrivateKey { + fn generate(rng: &mut R) -> Self + where + R: ::rand::RngCore + ::rand::CryptoRng, + { + let num_of_keys = rng.gen_range(1, MAX_NUM_OF_KEYS + 1); + let mut private_keys: Vec = Vec::with_capacity(num_of_keys); + for _ in 0..num_of_keys { + private_keys.push( + Ed25519PrivateKey::try_from( + &ed25519_dalek::SecretKey::generate(rng).to_bytes()[..], + ) + .unwrap(), + ); + } + let threshold = rng.gen_range(1, num_of_keys + 1) as u8; + MultiEd25519PrivateKey { + private_keys, + threshold, + } + } +} + +impl TryFrom<&[u8]> for MultiEd25519PrivateKey { + type Error = CryptoMaterialError; + + /// Deserialize an Ed25519PrivateKey. This method will also check for key and threshold validity. + fn try_from(bytes: &[u8]) -> std::result::Result { + if bytes.is_empty() { + return Err(CryptoMaterialError::WrongLengthError); + } + let (threshold, _) = check_and_get_threshold(bytes, ED25519_PRIVATE_KEY_LENGTH)?; + + let private_keys: Result, _> = bytes + .chunks_exact(ED25519_PRIVATE_KEY_LENGTH) + .map(Ed25519PrivateKey::try_from) + .collect(); + + private_keys.map(|private_keys| MultiEd25519PrivateKey { + private_keys, + threshold, + }) + } +} + +impl Length for MultiEd25519PrivateKey { + fn length(&self) -> usize { + self.private_keys.len() * ED25519_PRIVATE_KEY_LENGTH + 1 + } +} + +impl ValidCryptoMaterial for MultiEd25519PrivateKey { + fn to_bytes(&self) -> Vec { + self.to_bytes() + } +} + +impl Genesis for MultiEd25519PrivateKey { + fn genesis() -> Self { + let mut buf = [0u8; ED25519_PRIVATE_KEY_LENGTH]; + buf[ED25519_PRIVATE_KEY_LENGTH - 1] = 1u8; + MultiEd25519PrivateKey { + private_keys: vec![Ed25519PrivateKey::try_from(buf.as_ref()).unwrap()], + threshold: 1u8, + } + } +} + +////////////////////// +// PublicKey Traits // +////////////////////// + +/// Convenient method to create a MultiEd25519PublicKey from a single Ed25519PublicKey. +impl From for MultiEd25519PublicKey { + fn from(ed_public_key: Ed25519PublicKey) -> Self { + MultiEd25519PublicKey { + public_keys: vec![ed_public_key], + threshold: 1u8, + } + } +} + +/// Implementing From<&PrivateKey<...>> allows to derive a public key in a more elegant fashion. +impl From<&MultiEd25519PrivateKey> for MultiEd25519PublicKey { + fn from(private_key: &MultiEd25519PrivateKey) -> Self { + let public_keys = private_key + .private_keys + .iter() + .map(PrivateKey::public_key) + .collect(); + MultiEd25519PublicKey { + public_keys, + threshold: private_key.threshold, + } + } +} + +/// We deduce PublicKey from this. +impl PublicKey for MultiEd25519PublicKey { + type PrivateKeyMaterial = MultiEd25519PrivateKey; +} + +#[allow(clippy::derived_hash_with_manual_eq)] +impl std::hash::Hash for MultiEd25519PublicKey { + fn hash(&self, state: &mut H) { + let encoded_pubkey = self.to_bytes(); + state.write(&encoded_pubkey); + } +} + +impl TryFrom<&[u8]> for MultiEd25519PublicKey { + type Error = CryptoMaterialError; + + /// Deserialize a MultiEd25519PublicKey. This method will also check for threshold validity. + /// This method will NOT ensure keys are safe against small subgroup attacks, since our signature + /// verification API will automatically prevent it. + fn try_from(bytes: &[u8]) -> std::result::Result { + if bytes.is_empty() { + return Err(CryptoMaterialError::WrongLengthError); + } + let (threshold, _) = check_and_get_threshold(bytes, ED25519_PUBLIC_KEY_LENGTH)?; + let public_keys: Result, _> = bytes + .chunks_exact(ED25519_PUBLIC_KEY_LENGTH) + .map(Ed25519PublicKey::try_from) + .collect(); + public_keys.map(|public_keys| MultiEd25519PublicKey { + public_keys, + threshold, + }) + } +} + +/// We deduce VerifyingKey from pointing to the signature material +impl VerifyingKey for MultiEd25519PublicKey { + type SignatureMaterial = MultiEd25519Signature; + type SigningKeyMaterial = MultiEd25519PrivateKey; +} + +impl fmt::Display for MultiEd25519PublicKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", hex::encode(self.to_bytes())) + } +} + +impl fmt::Debug for MultiEd25519PublicKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "MultiEd25519PublicKey({})", self) + } +} + +impl Length for MultiEd25519PublicKey { + fn length(&self) -> usize { + self.public_keys.len() * ED25519_PUBLIC_KEY_LENGTH + 1 + } +} + +impl ValidCryptoMaterial for MultiEd25519PublicKey { + fn to_bytes(&self) -> Vec { + self.to_bytes() + } +} + +impl MultiEd25519Signature { + /// This method will also sort signatures based on index. + pub fn new( + signatures: Vec<(Ed25519Signature, u8)>, + ) -> std::result::Result { + let num_of_sigs = signatures.len(); + if num_of_sigs == 0 || num_of_sigs > MAX_NUM_OF_KEYS { + return Err(CryptoMaterialError::ValidationError); + } + + let mut sorted_signatures = signatures; + sorted_signatures.sort_by(|a, b| a.1.cmp(&b.1)); + + let mut bitmap = [0u8; BITMAP_NUM_OF_BYTES]; + + // Check if all indexes are unique and < MAX_NUM_OF_KEYS + let (sigs, indexes): (Vec<_>, Vec<_>) = sorted_signatures.into_iter().unzip(); + for i in indexes { + // If an index is out of range. + if i < MAX_NUM_OF_KEYS as u8 { + // if an index has been set already (thus, there is a duplicate). + if bitmap_get_bit(bitmap, i as usize) { + return Err(CryptoMaterialError::BitVecError( + "Duplicate signature index".to_string(), + )); + } else { + bitmap_set_bit(&mut bitmap, i as usize); + } + } else { + return Err(CryptoMaterialError::BitVecError( + "Signature index is out of range".to_string(), + )); + } + } + Ok(MultiEd25519Signature { + signatures: sigs, + bitmap, + }) + } + + /// Creates a new MultiEd25519signature by given signatures and bitmap. + pub fn new_with_signatures_and_bitmap( + signatures: Vec, + bitmap: [u8; BITMAP_NUM_OF_BYTES], + ) -> Self { + Self { signatures, bitmap } + } + + /// Getter signatures. + pub fn signatures(&self) -> &Vec { + &self.signatures + } + + /// Getter bitmap. + pub fn bitmap(&self) -> &[u8; BITMAP_NUM_OF_BYTES] { + &self.bitmap + } + + /// Serialize a MultiEd25519Signature in the form of sig0||sig1||..sigN||bitmap. + pub fn to_bytes(&self) -> Vec { + let mut bytes: Vec = self + .signatures + .iter() + .flat_map(|sig| sig.to_bytes().to_vec()) + .collect(); + bytes.extend(&self.bitmap[..]); + bytes + } +} + +////////////////////// +// Signature Traits // +////////////////////// + +impl TryFrom<&[u8]> for MultiEd25519Signature { + type Error = CryptoMaterialError; + + /// Deserialize a MultiEd25519Signature. This method will also check for malleable signatures + /// and bitmap validity. + fn try_from(bytes: &[u8]) -> std::result::Result { + let length = bytes.len(); + let bitmap_num_of_bytes = length % ED25519_SIGNATURE_LENGTH; + let num_of_sigs = length / ED25519_SIGNATURE_LENGTH; + + if num_of_sigs == 0 + || num_of_sigs > MAX_NUM_OF_KEYS + || bitmap_num_of_bytes != BITMAP_NUM_OF_BYTES + { + return Err(CryptoMaterialError::WrongLengthError); + } + + let bitmap = match bytes[length - BITMAP_NUM_OF_BYTES..].try_into() { + Ok(bitmap) => bitmap, + Err(_) => return Err(CryptoMaterialError::DeserializationError), + }; + if bitmap_count_ones(bitmap) != num_of_sigs as u32 { + return Err(CryptoMaterialError::DeserializationError); + } + + let signatures: Result, _> = bytes + .chunks_exact(ED25519_SIGNATURE_LENGTH) + .map(Ed25519Signature::try_from) + .collect(); + signatures.map(|signatures| MultiEd25519Signature { signatures, bitmap }) + } +} + +impl Length for MultiEd25519Signature { + fn length(&self) -> usize { + self.signatures.len() * ED25519_SIGNATURE_LENGTH + BITMAP_NUM_OF_BYTES + } +} + +#[allow(clippy::derived_hash_with_manual_eq)] +impl std::hash::Hash for MultiEd25519Signature { + fn hash(&self, state: &mut H) { + let encoded_signature = self.to_bytes(); + state.write(&encoded_signature); + } +} + +impl fmt::Display for MultiEd25519Signature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", hex::encode(&self.to_bytes()[..])) + } +} + +impl fmt::Debug for MultiEd25519Signature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "MultiEd25519Signature({})", self) + } +} + +impl ValidCryptoMaterial for MultiEd25519Signature { + fn to_bytes(&self) -> Vec { + self.to_bytes() + } +} + +impl Signature for MultiEd25519Signature { + type SigningKeyMaterial = MultiEd25519PrivateKey; + type VerifyingKeyMaterial = MultiEd25519PublicKey; + + fn verify( + &self, + message: &T, + public_key: &MultiEd25519PublicKey, + ) -> Result<()> { + // NOTE: Public keys need not be validated because we use ed25519_dalek's verify_strict, + // which checks for small order public keys. + let mut bytes = ::Hasher::seed().to_vec(); + bcs::serialize_into(&mut bytes, &message) + .map_err(|_| CryptoMaterialError::SerializationError)?; + Self::verify_arbitrary_msg(self, &bytes, public_key) + } + + /// Checks that `self` is valid for an arbitrary &[u8] `message` using `public_key`. + /// Outside of this crate, this particular function should only be used for native signature + /// verification in Move. + fn verify_arbitrary_msg( + &self, + message: &[u8], + public_key: &MultiEd25519PublicKey, + ) -> Result<()> { + // NOTE: Public keys need not be validated because we use ed25519_dalek's verify_strict, + // which checks for small order public keys. + match bitmap_last_set_bit(self.bitmap) { + Some(last_bit) if (last_bit as usize) < public_key.public_keys.len() => (), + _ => { + return Err(anyhow!( + "{}", + CryptoMaterialError::BitVecError("Signature index is out of range".to_string()) + )) + } + }; + if bitmap_count_ones(self.bitmap) < public_key.threshold as u32 { + return Err(anyhow!( + "{}", + CryptoMaterialError::BitVecError( + "Not enough signatures to meet the threshold".to_string() + ) + )); + } + let mut bitmap_index = 0; + // TODO: Eventually switch to deterministic batch verification + for sig in &self.signatures { + while !bitmap_get_bit(self.bitmap, bitmap_index) { + bitmap_index += 1; + } + sig.verify_arbitrary_msg(message, &public_key.public_keys[bitmap_index])?; + bitmap_index += 1; + } + Ok(()) + } + + fn to_bytes(&self) -> Vec { + self.to_bytes() + } +} + +impl From for MultiEd25519Signature { + fn from(ed_signature: Ed25519Signature) -> Self { + MultiEd25519Signature { + signatures: vec![ed_signature], + // "1000_0000 0000_0000 0000_0000 0000_0000" + bitmap: [0b1000_0000u8, 0u8, 0u8, 0u8], + } + } +} + +////////////////////// +// Helper functions // +////////////////////// + +/// Helper function used to convert a slice of MultiEd25519 keys to a sequence of bytes by +/// concatenating the serialized `keys` and the `threshold`. +fn to_bytes(keys: &[T], threshold: u8) -> Vec { + let mut bytes: Vec = keys + .iter() + .flat_map(ValidCryptoMaterial::to_bytes) + .collect(); + bytes.push(threshold); + bytes +} + +/// Helper method to get the threshold `t` and the # of sub PKs `n` from a serialized `t`-out-of-`n` MultiEd25519 key payload. +pub fn check_and_get_threshold( + bytes: &[u8], + key_size: usize, +) -> std::result::Result<(u8, u8), CryptoMaterialError> { + let payload_length = bytes.len(); + if bytes.is_empty() { + return Err(CryptoMaterialError::WrongLengthError); + } + let threshold_num_of_bytes = payload_length % key_size; + let num_of_keys = payload_length / key_size; + let threshold_byte = bytes[bytes.len() - 1]; + + if num_of_keys == 0 || num_of_keys > MAX_NUM_OF_KEYS || threshold_num_of_bytes != 1 { + Err(CryptoMaterialError::WrongLengthError) + } else if threshold_byte == 0 || threshold_byte > num_of_keys as u8 { + Err(CryptoMaterialError::ValidationError) + } else { + Ok((threshold_byte, num_of_keys as u8)) + } +} + +fn bitmap_set_bit(input: &mut [u8; BITMAP_NUM_OF_BYTES], index: usize) { + let bucket = index / 8; + // It's always invoked with index < 32, thus there is no need to check range. + let bucket_pos = index - (bucket * 8); + input[bucket] |= 128 >> bucket_pos as u8; +} + +// Helper method to get the input's bit at index. +fn bitmap_get_bit(input: [u8; BITMAP_NUM_OF_BYTES], index: usize) -> bool { + let bucket = index / 8; + // It's always invoked with index < 32, thus there is no need to check range. + let bucket_pos = index - (bucket * 8); + (input[bucket] & (128 >> bucket_pos as u8)) != 0 +} + +// Returns the number of set bits. +fn bitmap_count_ones(input: [u8; BITMAP_NUM_OF_BYTES]) -> u32 { + input.iter().map(|a| a.count_ones()).sum() +} + +// Find the last set bit. +fn bitmap_last_set_bit(input: [u8; BITMAP_NUM_OF_BYTES]) -> Option { + input + .iter() + .rev() + .enumerate() + .find(|(_, byte)| byte != &&0u8) + .map(|(i, byte)| (8 * (BITMAP_NUM_OF_BYTES - i) - byte.trailing_zeros() as usize - 1) as u8) +} + +#[test] +fn bitmap_tests() { + let mut bitmap = [0b0100_0000u8, 0b1111_1111u8, 0u8, 0b1000_0000u8]; + assert!(!bitmap_get_bit(bitmap, 0)); + assert!(bitmap_get_bit(bitmap, 1)); + for i in 8..16 { + assert!(bitmap_get_bit(bitmap, i)); + } + for i in 16..24 { + assert!(!bitmap_get_bit(bitmap, i)); + } + assert!(bitmap_get_bit(bitmap, 24)); + assert!(!bitmap_get_bit(bitmap, 31)); + assert_eq!(bitmap_last_set_bit(bitmap), Some(24)); + + bitmap_set_bit(&mut bitmap, 30); + assert!(bitmap_get_bit(bitmap, 30)); + assert_eq!(bitmap_last_set_bit(bitmap), Some(30)); +} + +#[test] +fn test_key_boundaries() { + let pr = Ed25519PrivateKey::generate_for_testing(); + let multi_pr = MultiEd25519PrivateKey::new(vec![pr], 1).unwrap(); + let pb = multi_pr.public_key(); + + let msg = &[]; + let sig = multi_pr.sign_arbitrary_message(msg); + + let pb_bytes = pb.to_bytes(); + let mut sig_bytes = sig.to_bytes(); + let sig_len = sig_bytes.len(); + assert_eq!(sig_len, 68); + assert_eq!(sig_bytes[sig_len - 4], 0b10000000); + assert_eq!(sig_bytes[sig_len - 3], 0); + assert_eq!(sig_bytes[sig_len - 2], 0); + assert_eq!(sig_bytes[sig_len - 1], 0); + sig_bytes[sig_len - 4] = 0b01000000; + + let bad_sig = MultiEd25519Signature::try_from(sig_bytes.as_slice()).unwrap(); + let pub_key = MultiEd25519PublicKey::try_from(pb_bytes.as_slice()).unwrap(); + bad_sig.verify_arbitrary_msg(msg, &pub_key).unwrap_err(); +} diff --git a/crates/aptos-crypto/src/noise.rs b/crates/aptos-crypto/src/noise.rs new file mode 100644 index 0000000..13a08e4 --- /dev/null +++ b/crates/aptos-crypto/src/noise.rs @@ -0,0 +1,715 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//! Noise is a [protocol framework](https://noiseprotocol.org/) which we use to +//! encrypt and authenticate communications between nodes of the network. +//! +//! This file implements a stripped-down version of Noise_IK_25519_AESGCM_SHA256. +//! This means that only the parts that we care about (the IK handshake) are implemented. +//! +//! Note that to benefit from hardware support for AES, you must build this crate with the following +//! flags: `RUSTFLAGS="-Ctarget-cpu=skylake -Ctarget-feature=+aes,+sse2,+sse4.1,+ssse3"`. +//! +//! Note that we cannot use the harder-to-shoot-yourself-in-the-foot types of the `ring` crate such +//! as OpeningKey/SealingKey and NonceSequence, since Noise's key derivation is non-trivial and +//! requires us to work with keys as byte arrays. As a result, we make use of `LessSafeKey`, +//! `UnboundKey` and `Nonce::assume_unique_for_key`. +//! +//! Usage example: +//! +//! ``` +//! use aptos_crypto::{noise, x25519, traits::*}; +//! use rand::prelude::*; +//! +//! # fn main() -> Result<(), aptos_crypto::noise::NoiseError> { +//! let mut rng = rand::thread_rng(); +//! let initiator_static = x25519::PrivateKey::generate(&mut rng); +//! let responder_static = x25519::PrivateKey::generate(&mut rng); +//! let responder_public = responder_static.public_key(); +//! +//! let initiator = noise::NoiseConfig::new(initiator_static); +//! let responder = noise::NoiseConfig::new(responder_static); +//! +//! let payload1 = b"the client can send an optional payload in the first message"; +//! let mut buffer = vec![0u8; noise::handshake_init_msg_len(payload1.len())]; +//! let initiator_state = initiator +//! .initiate_connection(&mut rng, b"prologue", responder_public, Some(payload1), &mut buffer)?; +//! +//! let payload2 = b"the server can send an optional payload as well as part of the handshake"; +//! let mut buffer2 = vec![0u8; noise::handshake_resp_msg_len(payload2.len())]; +//! let (received_payload, mut responder_session) = responder +//! .respond_to_client_and_finalize(&mut rng, b"prologue", &buffer, Some(payload2), &mut buffer2)?; +//! assert_eq!(received_payload.as_slice(), &payload1[..]); +//! +//! let (received_payload, mut initiator_session) = initiator +//! .finalize_connection(initiator_state, &buffer2)?; +//! assert_eq!(received_payload.as_slice(), &payload2[..]); +//! +//! let message_sent = b"hello world".to_vec(); +//! let mut buffer = message_sent.clone(); +//! let auth_tag = initiator_session +//! .write_message_in_place(&mut buffer)?; +//! buffer.extend_from_slice(&auth_tag); +//! +//! let received_message = responder_session +//! .read_message_in_place(&mut buffer)?; +//! +//! assert_eq!(received_message, message_sent.as_slice()); +//! +//! # Ok(()) +//! # } +//! ``` +//! +#![allow(clippy::arithmetic_side_effects)] + +use crate::{hash::HashValue, hkdf::Hkdf, traits::Uniform as _, x25519, ValidCryptoMaterial}; +use ring::aead::{self, Aad, LessSafeKey, UnboundKey}; +use sha2::Digest; +use std::{ + convert::{TryFrom as _, TryInto}, + io::{Cursor, Read as _, Write as _}, +}; +use thiserror::Error; + +// +// Useful constants +// ---------------- +// + +/// A noise message cannot be larger than 65535 bytes as per the specification. +pub const MAX_SIZE_NOISE_MSG: usize = 65535; + +/// The authentication tag length of AES-GCM. +pub const AES_GCM_TAGLEN: usize = 16; + +/// The only Noise handshake protocol that we implement in this file. +const PROTOCOL_NAME: &[u8] = b"Noise_IK_25519_AESGCM_SHA256\0\0\0\0"; + +/// The nonce size we use for AES-GCM. +const AES_NONCE_SIZE: usize = 12; + +/// A handy const fn to get the expanded size of a plaintext after encryption +pub const fn encrypted_len(plaintext_len: usize) -> usize { + plaintext_len + AES_GCM_TAGLEN +} + +/// A handy const fn to get the size of a plaintext from a ciphertext size +pub const fn decrypted_len(ciphertext_len: usize) -> usize { + ciphertext_len - AES_GCM_TAGLEN +} + +/// A handy const fn to get the size of the first handshake message +pub const fn handshake_init_msg_len(payload_len: usize) -> usize { + // e + let e_len = x25519::PUBLIC_KEY_SIZE; + // encrypted s + let enc_s_len = encrypted_len(x25519::PUBLIC_KEY_SIZE); + // encrypted payload + let enc_payload_len = encrypted_len(payload_len); + // + e_len + enc_s_len + enc_payload_len +} + +/// A handy const fn to get the size of the second handshake message +pub const fn handshake_resp_msg_len(payload_len: usize) -> usize { + // e + let e_len = x25519::PUBLIC_KEY_SIZE; + // encrypted payload + let enc_payload_len = encrypted_len(payload_len); + // + e_len + enc_payload_len +} + +/// Convenience method to wrap an `&[u8]` AES key into a `LessSafeKey` type of the `ring` crate +fn aes_key(key: &[u8]) -> LessSafeKey { + LessSafeKey::new( + UnboundKey::new(&aead::AES_256_GCM, key).expect("Unexpected AES256-GCM key length"), + ) +} + +/// This implementation relies on the fact that the hash function used has a 256-bit output +#[rustfmt::skip] +const _: [(); 32] = [(); HashValue::LENGTH]; + +// +// Errors +// ------ +// + +/// A NoiseError enum represents the different types of error that noise can return to users of the crate +#[derive(Debug, Error)] +pub enum NoiseError { + /// the received message is too short to contain the expected data + #[error("noise: the received message is too short to contain the expected data")] + MsgTooShort, + + /// HKDF has failed (in practice there is no reason for HKDF to fail) + #[error("noise: HKDF has failed")] + Hkdf, + + /// encryption has failed (in practice there is no reason for encryption to fail) + #[error("noise: encryption has failed")] + Encrypt, + + /// could not decrypt the received data (most likely the data was tampered with + #[error("noise: could not decrypt the received data")] + Decrypt, + + /// the public key received is of the wrong format + #[error("noise: the public key received is of the wrong format")] + WrongPublicKeyReceived, + + /// session was closed due to decrypt error + #[error("noise: session was closed due to decrypt error")] + SessionClosed, + + /// the payload that we are trying to send is too large + #[error("noise: the payload that we are trying to send is too large")] + PayloadTooLarge, + + /// the message we received is too large + #[error("noise: the message we received is too large")] + ReceivedMsgTooLarge, + + /// the response buffer passed as argument is too small + #[error("noise: the response buffer passed as argument is too small")] + ResponseBufferTooSmall, + + /// the nonce exceeds the maximum u64 value (in practice this should not happen) + #[error("noise: the nonce exceeds the maximum u64 value")] + NonceOverflow, +} + +// +// helpers +// ------- +// + +fn hash(data: &[u8]) -> Vec { + sha2::Sha256::digest(data).to_vec() +} + +fn hkdf(ck: &[u8], dh_output: Option<&[u8]>) -> Result<(Vec, Vec), NoiseError> { + let dh_output = dh_output.unwrap_or(&[]); + let hkdf_output = if dh_output.is_empty() { + Hkdf::::extract_then_expand_no_ikm(Some(ck), None, 64) + } else { + Hkdf::::extract_then_expand(Some(ck), dh_output, None, 64) + }; + + let hkdf_output = hkdf_output.map_err(|_| NoiseError::Hkdf)?; + let (k1, k2) = hkdf_output.split_at(32); + Ok((k1.to_vec(), k2.to_vec())) +} + +fn mix_hash(h: &mut Vec, data: &[u8]) { + h.extend_from_slice(data); + *h = hash(h); +} + +fn mix_key(ck: &mut Vec, dh_output: &[u8]) -> Result, NoiseError> { + let (new_ck, k) = hkdf(ck, Some(dh_output))?; + *ck = new_ck; + Ok(k) +} + +// +// Noise implementation +// -------------------- +// + +/// A key holder structure used for both initiators and responders. +#[derive(Debug)] +pub struct NoiseConfig { + private_key: x25519::PrivateKey, + public_key: x25519::PublicKey, +} + +/// Refer to the Noise protocol framework specification in order to understand these fields. +#[cfg_attr(test, derive(Clone))] +pub struct InitiatorHandshakeState { + /// rolling hash + h: Vec, + /// chaining key + ck: Vec, + /// ephemeral key + e: x25519::PrivateKey, + /// remote static key used + rs: x25519::PublicKey, +} + +/// Refer to the Noise protocol framework specification in order to understand these fields. +#[cfg_attr(test, derive(Clone))] +pub struct ResponderHandshakeState { + /// rolling hash + h: Vec, + /// chaining key + ck: Vec, + /// remote static key received + rs: x25519::PublicKey, + /// remote ephemeral key receiced + re: x25519::PublicKey, +} + +impl NoiseConfig { + /// A peer must create a NoiseConfig through this function before being able to connect with other peers. + pub fn new(private_key: x25519::PrivateKey) -> Self { + // we could take a public key as argument, and it would be faster, but this is cleaner + let public_key = private_key.public_key(); + Self { + private_key, + public_key, + } + } + + /// Handy getter to access the configuration's public key + pub fn public_key(&self) -> x25519::PublicKey { + self.public_key + } + + // + // Initiator + // --------- + + /// An initiator can use this function to initiate a handshake with a known responder. + pub fn initiate_connection( + &self, + rng: &mut (impl rand::RngCore + rand::CryptoRng), + prologue: &[u8], + remote_public: x25519::PublicKey, + payload: Option<&[u8]>, + response_buffer: &mut [u8], + ) -> Result { + // checks + let payload_len = payload.map(<[u8]>::len).unwrap_or(0); + let buffer_size_required = handshake_init_msg_len(payload_len); + if buffer_size_required > MAX_SIZE_NOISE_MSG { + return Err(NoiseError::PayloadTooLarge); + } + if response_buffer.len() < buffer_size_required { + return Err(NoiseError::ResponseBufferTooSmall); + } + // initialize + let mut h = PROTOCOL_NAME.to_vec(); + let mut ck = PROTOCOL_NAME.to_vec(); + let rs = remote_public; // for naming consistency with the specification + mix_hash(&mut h, prologue); + mix_hash(&mut h, rs.as_slice()); + + // -> e + let e = x25519::PrivateKey::generate(rng); + let e_pub = e.public_key(); + + mix_hash(&mut h, e_pub.as_slice()); + let mut response_buffer = Cursor::new(response_buffer); + response_buffer + .write(e_pub.as_slice()) + .map_err(|_| NoiseError::ResponseBufferTooSmall)?; + + // -> es + let dh_output = e.diffie_hellman(&rs); + let k = mix_key(&mut ck, &dh_output)?; + + // -> s + let aead = aes_key(&k[..]); + let mut in_out = self.public_key.to_bytes(); + let nonce = aead::Nonce::assume_unique_for_key([0u8; AES_NONCE_SIZE]); + + aead.seal_in_place_append_tag(nonce, Aad::from(&h), &mut in_out) + .map_err(|_| NoiseError::Encrypt)?; + + mix_hash(&mut h, &in_out[..]); + response_buffer + .write(&in_out[..]) + .map_err(|_| NoiseError::ResponseBufferTooSmall)?; + + // -> ss + let dh_output = self.private_key.diffie_hellman(&rs); + let k = mix_key(&mut ck, &dh_output)?; + + // -> payload + let aead = aes_key(&k[..]); + let mut in_out = payload.unwrap_or(&[]).to_vec(); + let nonce = aead::Nonce::assume_unique_for_key([0u8; AES_NONCE_SIZE]); + + aead.seal_in_place_append_tag(nonce, Aad::from(&h), &mut in_out) + .map_err(|_| NoiseError::Encrypt)?; + + mix_hash(&mut h, &in_out[..]); + + response_buffer + .write(&in_out[..]) + .map_err(|_| NoiseError::ResponseBufferTooSmall)?; + + // return + let handshake_state = InitiatorHandshakeState { h, ck, e, rs }; + Ok(handshake_state) + } + + /// A client can call this to finalize a connection, after receiving an answer from a server. + pub fn finalize_connection( + &self, + handshake_state: InitiatorHandshakeState, + received_message: &[u8], + ) -> Result<(Vec, NoiseSession), NoiseError> { + // checks + if received_message.len() > MAX_SIZE_NOISE_MSG { + return Err(NoiseError::ReceivedMsgTooLarge); + } + // retrieve handshake state + let InitiatorHandshakeState { + mut h, + mut ck, + e, + rs, + } = handshake_state; + + // <- e + let mut re = [0u8; x25519::PUBLIC_KEY_SIZE]; + let mut cursor = Cursor::new(received_message); + cursor + .read_exact(&mut re) + .map_err(|_| NoiseError::MsgTooShort)?; + mix_hash(&mut h, &re); + let re = x25519::PublicKey::from(re); + + // <- ee + let dh_output = e.diffie_hellman(&re); + mix_key(&mut ck, &dh_output)?; + + // <- se + let dh_output = self.private_key.diffie_hellman(&re); + let k = mix_key(&mut ck, &dh_output)?; + + // <- payload + let offset = cursor.position() as usize; + + let aead = aes_key(&k[..]); + let mut in_out = cursor.into_inner()[offset..].to_vec(); + let nonce = aead::Nonce::assume_unique_for_key([0u8; AES_NONCE_SIZE]); + let plaintext = aead + .open_in_place(nonce, Aad::from(&h), &mut in_out) + .map_err(|_| NoiseError::Decrypt)?; + + // split + let (k1, k2) = hkdf(&ck, None)?; + let session = NoiseSession::new(k1, k2, rs); + + // + Ok((plaintext.to_vec(), session)) + } + + // + // Responder + // --------- + // There are two ways to use this API: + // - either use `parse_client_init_message()` followed by `respond_to_client()` + // - or use the all-in-one `respond_to_client_and_finalize()` + // + // the reason for the first deconstructed API is that we might want to do + // some validation of the received initiator's public key which might + // + + /// A responder can accept a connection by first parsing an initiator message. + /// The function respond_to_client is usually called after this to respond to the initiator. + pub fn parse_client_init_message( + &self, + prologue: &[u8], + received_message: &[u8], + ) -> Result< + ( + x25519::PublicKey, // initiator's public key + ResponderHandshakeState, // state to be used in respond_to_client + Vec, // payload received + ), + NoiseError, + > { + // checks + if received_message.len() > MAX_SIZE_NOISE_MSG { + return Err(NoiseError::ReceivedMsgTooLarge); + } + // initialize + let mut h = PROTOCOL_NAME.to_vec(); + let mut ck = PROTOCOL_NAME.to_vec(); + mix_hash(&mut h, prologue); + mix_hash(&mut h, self.public_key.as_slice()); + + // buffer message received + let mut cursor = Cursor::new(received_message); + + // <- e + let mut re = [0u8; x25519::PUBLIC_KEY_SIZE]; + cursor + .read_exact(&mut re) + .map_err(|_| NoiseError::MsgTooShort)?; + mix_hash(&mut h, &re); + let re = x25519::PublicKey::from(re); + + // <- es + let dh_output = self.private_key.diffie_hellman(&re); + let k = mix_key(&mut ck, &dh_output)?; + + // <- s + let mut encrypted_remote_static = [0u8; x25519::PUBLIC_KEY_SIZE + AES_GCM_TAGLEN]; + cursor + .read_exact(&mut encrypted_remote_static) + .map_err(|_| NoiseError::MsgTooShort)?; + + let aead = aes_key(&k[..]); + let mut in_out = encrypted_remote_static.to_vec(); + let nonce = aead::Nonce::assume_unique_for_key([0u8; AES_NONCE_SIZE]); + let rs: &[u8] = aead + .open_in_place(nonce, Aad::from(&h), &mut in_out) + .map_err(|_| NoiseError::Decrypt)?; + + let rs = x25519::PublicKey::try_from(rs).map_err(|_| NoiseError::WrongPublicKeyReceived)?; + mix_hash(&mut h, &encrypted_remote_static); + + // <- ss + let dh_output = self.private_key.diffie_hellman(&rs); + let k = mix_key(&mut ck, &dh_output)?; + + // <- payload + let offset = cursor.position() as usize; + let received_encrypted_payload = &cursor.into_inner()[offset..]; + + let aead = aes_key(&k[..]); + let mut in_out = received_encrypted_payload.to_vec(); + let nonce = aead::Nonce::assume_unique_for_key([0u8; AES_NONCE_SIZE]); + let received_payload = aead + .open_in_place(nonce, Aad::from(&h), &mut in_out) + .map_err(|_| NoiseError::Decrypt)?; + mix_hash(&mut h, received_encrypted_payload); + + // return + let handshake_state = ResponderHandshakeState { h, ck, rs, re }; + Ok((rs, handshake_state, received_payload.to_vec())) + } + + /// A responder can respond to an initiator by calling this function with the state obtained, + /// after calling parse_client_init_message + pub fn respond_to_client( + &self, + rng: &mut (impl rand::RngCore + rand::CryptoRng), + handshake_state: ResponderHandshakeState, + payload: Option<&[u8]>, + response_buffer: &mut [u8], + ) -> Result { + // checks + let payload_len = payload.map(<[u8]>::len).unwrap_or(0); + let buffer_size_required = handshake_resp_msg_len(payload_len); + if buffer_size_required > MAX_SIZE_NOISE_MSG { + return Err(NoiseError::PayloadTooLarge); + } + if response_buffer.len() < buffer_size_required { + return Err(NoiseError::ResponseBufferTooSmall); + } + + // retrieve handshake state + let ResponderHandshakeState { + mut h, + mut ck, + rs, + re, + } = handshake_state; + + // -> e + let e = x25519::PrivateKey::generate(rng); + let e_pub = e.public_key(); + + mix_hash(&mut h, e_pub.as_slice()); + let mut response_buffer = Cursor::new(response_buffer); + response_buffer + .write(e_pub.as_slice()) + .map_err(|_| NoiseError::ResponseBufferTooSmall)?; + + // -> ee + let dh_output = e.diffie_hellman(&re); + mix_key(&mut ck, &dh_output)?; + + // -> se + let dh_output = e.diffie_hellman(&rs); + let k = mix_key(&mut ck, &dh_output)?; + + // -> payload + let aead = aes_key(&k[..]); + let mut in_out = payload.unwrap_or(&[]).to_vec(); + let nonce = aead::Nonce::assume_unique_for_key([0u8; AES_NONCE_SIZE]); + aead.seal_in_place_append_tag(nonce, Aad::from(&h), &mut in_out) + .map_err(|_| NoiseError::Encrypt)?; + + mix_hash(&mut h, &in_out[..]); + + response_buffer + .write(&in_out[..]) + .map_err(|_| NoiseError::ResponseBufferTooSmall)?; + + // split + let (k1, k2) = hkdf(&ck, None)?; + let session = NoiseSession::new(k2, k1, rs); + + // + Ok(session) + } + + /// This function is a one-call that replaces calling the two functions parse_client_init_message + /// and respond_to_client consecutively + pub fn respond_to_client_and_finalize( + &self, + rng: &mut (impl rand::RngCore + rand::CryptoRng), + prologue: &[u8], + received_message: &[u8], + payload: Option<&[u8]>, + response_buffer: &mut [u8], + ) -> Result< + ( + Vec, // the payload the initiator sent + NoiseSession, // The created session + ), + NoiseError, + > { + let (_, handshake_state, received_payload) = + self.parse_client_init_message(prologue, received_message)?; + let session = self.respond_to_client(rng, handshake_state, payload, response_buffer)?; + Ok((received_payload, session)) + } +} + +// +// Post-Handshake +// -------------- + +/// A NoiseSession is produced after a successful Noise handshake, and can be use to encrypt and decrypt messages to the other peer. +#[cfg_attr(test, derive(Clone))] +pub struct NoiseSession { + /// a session can be marked as invalid if it has seen a decryption failure + valid: bool, + /// the public key of the other peer + remote_public_key: x25519::PublicKey, + /// key used to encrypt messages to the other peer + write_key: Vec, + /// associated nonce (in practice the maximum u64 value cannot be reached) + write_nonce: u64, + /// key used to decrypt messages received from the other peer + read_key: Vec, + /// associated nonce (in practice the maximum u64 value cannot be reached) + read_nonce: u64, +} + +impl NoiseSession { + fn new(write_key: Vec, read_key: Vec, remote_public_key: x25519::PublicKey) -> Self { + Self { + valid: true, + remote_public_key, + write_key, + write_nonce: 0, + read_key, + read_nonce: 0, + } + } + + /// create a dummy session with 0 keys + #[cfg(any(test, feature = "fuzzing"))] + pub fn new_for_testing() -> Self { + Self::new( + vec![0u8; 32], + vec![0u8; 32], + [0u8; x25519::PUBLIC_KEY_SIZE].into(), + ) + } + + /// obtain remote static public key + pub fn get_remote_static(&self) -> x25519::PublicKey { + self.remote_public_key + } + + /// encrypts a message for the other peers (post-handshake) + /// the function encrypts in place, and returns the authentication tag as result + pub fn write_message_in_place(&mut self, message: &mut [u8]) -> Result, NoiseError> { + // checks + if !self.valid { + return Err(NoiseError::SessionClosed); + } + if message.len() > MAX_SIZE_NOISE_MSG - AES_GCM_TAGLEN { + return Err(NoiseError::PayloadTooLarge); + } + + // encrypt in place + let write_key = aes_key(&self.write_key[..]); + + let mut nonce = [0u8; 4].to_vec(); + nonce.extend_from_slice(&self.write_nonce.to_be_bytes()); + assert_eq!(nonce.len(), 12); + let nonce = aead::Nonce::assume_unique_for_key( + nonce.try_into().expect("Incorrect AES256-GCM nonce length"), + ); + + let authentication_tag = write_key + .seal_in_place_separate_tag(nonce, aead::Aad::empty(), message) + .map_err(|_| NoiseError::Encrypt)?; + + // increment nonce + self.write_nonce = self + .write_nonce + .checked_add(1) + .ok_or(NoiseError::NonceOverflow)?; + + // return a subslice without the authentication tag + Ok(authentication_tag.as_ref().into()) + } + + /// decrypts a message from the other peer (post-handshake) + /// the function decrypts in place, and returns a subslice without the auth tag + pub fn read_message_in_place<'a>( + &mut self, + message: &'a mut [u8], + ) -> Result<&'a [u8], NoiseError> { + // checks + if !self.valid { + return Err(NoiseError::SessionClosed); + } + if message.len() > MAX_SIZE_NOISE_MSG { + self.valid = false; + return Err(NoiseError::ReceivedMsgTooLarge); + } + if message.len() < AES_GCM_TAGLEN { + self.valid = false; + return Err(NoiseError::ResponseBufferTooSmall); + } + + // decrypt in place + let read_key = aes_key(&self.read_key[..]); + + let mut nonce = [0u8; 4].to_vec(); + nonce.extend_from_slice(&self.read_nonce.to_be_bytes()); + assert_eq!(nonce.len(), 12); + let nonce = aead::Nonce::assume_unique_for_key( + nonce.try_into().expect("Wrong AES256-GCM nonce length"), + ); + + read_key + .open_in_place(nonce, aead::Aad::empty(), message) + .map_err(|_| { + self.valid = false; + NoiseError::Decrypt + })?; + + let (buffer, _authentication_tag) = message.split_at_mut(message.len() - AES_GCM_TAGLEN); + + // increment nonce + self.read_nonce = self + .read_nonce + .checked_add(1) + .ok_or(NoiseError::NonceOverflow)?; + + // return a subslice of the buffer representing the decrypted plaintext + Ok(buffer) + } +} + +impl std::fmt::Debug for NoiseSession { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "NoiseSession[...]") + } +} diff --git a/crates/aptos-crypto/src/poseidon_bn254/alt_fr.rs b/crates/aptos-crypto/src/poseidon_bn254/alt_fr.rs new file mode 100644 index 0000000..2fb80a8 --- /dev/null +++ b/crates/aptos-crypto/src/poseidon_bn254/alt_fr.rs @@ -0,0 +1,36 @@ +// Copyright (c) Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use ark_ff::{BigInteger, PrimeField as ArkPrimeField}; +use ff::PrimeField; + +/// Unfortunately, `neptune` is not compatible with the arkworks ff traits. +#[derive(PrimeField)] +#[PrimeFieldModulus = "21888242871839275222246405745257275088548364400416034343698204186575808495617"] +#[PrimeFieldGenerator = "5"] +#[PrimeFieldReprEndianness = "little"] +pub struct AltFr([u64; 4]); + +impl From for AltFr { + fn from(fr: ark_bn254::Fr) -> Self { + AltFr::from_repr_vartime(AltFrRepr( + fr.into_bigint() + .to_bytes_le() + .try_into() + .expect("Expected ark_bn254::Fr to have 32 byte length"), + )) + .expect("The ark_bn254::Fr bytes were expected to be valid") + } +} + +impl From for ark_bn254::Fr { + fn from(fr: AltFr) -> Self { + ark_bn254::Fr::from_le_bytes_mod_order(fr.to_repr().as_ref()) + } +} + +impl From<&str> for AltFr { + fn from(hex: &str) -> Self { + AltFr::from_str_vartime(hex).unwrap() + } +} diff --git a/crates/aptos-crypto/src/poseidon_bn254/constants.rs b/crates/aptos-crypto/src/poseidon_bn254/constants.rs new file mode 100644 index 0000000..116599b --- /dev/null +++ b/crates/aptos-crypto/src/poseidon_bn254/constants.rs @@ -0,0 +1,14859 @@ +// Copyright (c) Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::poseidon_bn254::alt_fr::AltFr; +use neptune::{hash_type::HashType, poseidon::PoseidonConstants, Strength}; +use once_cell::sync::Lazy; +use typenum::{Unsigned, U1, U10, U11, U12, U13, U14, U15, U16, U2, U3, U4, U5, U6, U7, U8, U9}; + +const FULL_ROUNDS: usize = 8; +static PARTIAL_ROUNDS: Lazy> = Lazy::new(|| { + vec![ + 56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68, + ] +}); + +static BN254_CONSTANTS: Lazy<(Vec>, Vec>>)> = Lazy::new(constants); + +macro_rules! neptune_constants { + ($constants:expr, $matrices:expr, $ui:ty) => {{ + let w = <$ui>::to_usize(); + PoseidonConstants::new_from_parameters( + w + 1, + $matrices[w - 1].clone(), + $constants[w - 1].clone(), + FULL_ROUNDS, + PARTIAL_ROUNDS[w - 1], + HashType::::Sponge, + Strength::Standard, + ) + }}; +} + +pub(crate) static POSEIDON_1: Lazy> = + Lazy::new(|| neptune_constants!(BN254_CONSTANTS.0, BN254_CONSTANTS.1, U1)); +pub(crate) static POSEIDON_2: Lazy> = + Lazy::new(|| neptune_constants!(BN254_CONSTANTS.0, BN254_CONSTANTS.1, U2)); +pub(crate) static POSEIDON_3: Lazy> = + Lazy::new(|| neptune_constants!(BN254_CONSTANTS.0, BN254_CONSTANTS.1, U3)); +pub(crate) static POSEIDON_4: Lazy> = + Lazy::new(|| neptune_constants!(BN254_CONSTANTS.0, BN254_CONSTANTS.1, U4)); +pub(crate) static POSEIDON_5: Lazy> = + Lazy::new(|| neptune_constants!(BN254_CONSTANTS.0, BN254_CONSTANTS.1, U5)); +pub(crate) static POSEIDON_6: Lazy> = + Lazy::new(|| neptune_constants!(BN254_CONSTANTS.0, BN254_CONSTANTS.1, U6)); +pub(crate) static POSEIDON_7: Lazy> = + Lazy::new(|| neptune_constants!(BN254_CONSTANTS.0, BN254_CONSTANTS.1, U7)); +pub(crate) static POSEIDON_8: Lazy> = + Lazy::new(|| neptune_constants!(BN254_CONSTANTS.0, BN254_CONSTANTS.1, U8)); +pub(crate) static POSEIDON_9: Lazy> = + Lazy::new(|| neptune_constants!(BN254_CONSTANTS.0, BN254_CONSTANTS.1, U9)); +pub(crate) static POSEIDON_10: Lazy> = + Lazy::new(|| neptune_constants!(BN254_CONSTANTS.0, BN254_CONSTANTS.1, U10)); +pub(crate) static POSEIDON_11: Lazy> = + Lazy::new(|| neptune_constants!(BN254_CONSTANTS.0, BN254_CONSTANTS.1, U11)); +pub(crate) static POSEIDON_12: Lazy> = + Lazy::new(|| neptune_constants!(BN254_CONSTANTS.0, BN254_CONSTANTS.1, U12)); +pub(crate) static POSEIDON_13: Lazy> = + Lazy::new(|| neptune_constants!(BN254_CONSTANTS.0, BN254_CONSTANTS.1, U13)); +pub(crate) static POSEIDON_14: Lazy> = + Lazy::new(|| neptune_constants!(BN254_CONSTANTS.0, BN254_CONSTANTS.1, U14)); +pub(crate) static POSEIDON_15: Lazy> = + Lazy::new(|| neptune_constants!(BN254_CONSTANTS.0, BN254_CONSTANTS.1, U15)); +pub(crate) static POSEIDON_16: Lazy> = + Lazy::new(|| neptune_constants!(BN254_CONSTANTS.0, BN254_CONSTANTS.1, U16)); + +/// Returns BN254-specific Poseidon constants. +/// From https://github.com/arnaucube/poseidon-rs/blob/master/src/constants.rs. +pub fn constants() -> (Vec>, Vec>>) { + let c_str: Vec> = vec![ + vec![ + "4417881134626180770308697923359573201005643519861877412381846989312604493735".into(), + "5433650512959517612316327474713065966758808864213826738576266661723522780033".into(), + "13641176377184356099764086973022553863760045607496549923679278773208775739952".into(), + "17949713444224994136330421782109149544629237834775211751417461773584374506783".into(), + "13765628375339178273710281891027109699578766420463125835325926111705201856003".into(), + "19179513468172002314585757290678967643352171735526887944518845346318719730387".into(), + "5157412437176756884543472904098424903141745259452875378101256928559722612176".into(), + "535160875740282236955320458485730000677124519901643397458212725410971557409".into(), + "1050793453380762984940163090920066886770841063557081906093018330633089036729".into(), + "10665495010329663932664894101216428400933984666065399374198502106997623173873".into(), + "19965634623406616956648724894636666805991993496469370618546874926025059150737".into(), + "13007250030070838431593222885902415182312449212965120303174723305710127422213".into(), + "16877538715074991604507979123743768693428157847423939051086744213162455276374".into(), + "18211747749504876135588847560312685184956239426147543810126553367063157141465".into(), + "18151553319826126919739798892854572062191241985315767086020821632812331245635".into(), + "19957033149976712666746140949846950406660099037474791840946955175819555930825".into(), + "3469514863538261843186854830917934449567467100548474599735384052339577040841".into(), + "989698510043911779243192466312362856042600749099921773896924315611668507708".into(), + "12568377015646290945235387813564567111330046038050864455358059568128000172201".into(), + "20856104135605479600325529349246932565148587186338606236677138505306779314172".into(), + "8206918720503535523121349917159924938835810381723474192155637697065780938424".into(), + "1309058477013932989380617265069188723120054926187607548493110334522527703566".into(), + "14076116939332667074621703729512195584105250395163383769419390236426287710606".into(), + "10153498892749751942204288991871286290442690932856658983589258153608012428674".into(), + "18202499207234128286137597834010475797175973146805180988367589376893530181575".into(), + "12739388830157083522877690211447248168864006284243907142044329113461613743052".into(), + "15123358710467780770838026754240340042441262572309759635224051333176022613949".into(), + "19925004701844594370904593774447343836015483888496504201331110250494635362184".into(), + "10352416606816998476681131583320899030072315953910679608943150613208329645891".into(), + "10567371822366244361703342347428230537114808440249611395507235283708966113221".into(), + "5635498582763880627392290206431559361272660937399944184533035305989295959602".into(), + "11866432933224219174041051738704352719163271639958083608224676028593315904909".into(), + "5795020705294401441272215064554385591292330721703923167136157291459784140431".into(), + "9482202378699252817564375087302794636287866584767523335624368774856230692758".into(), + "4245237636894546151746468406560945873445548423466753843402086544922216329298".into(), + "12000500941313982757584712677991730019124834399479314697467598397927435905133".into(), + "7596790274058425558167520209857956363736666939016807569082239187494363541787".into(), + "2484867918246116343205467273440098378820186751202461278013576281097918148877".into(), + "18312645949449997391810445935615409295369169383463185688973803378104013950190".into(), + "15320686572748723004980855263301182130424010735782762814513954166519592552733".into(), + "12618438900597948888520621062416758747872180395546164387827245287017031303859".into(), + "17438141672027706116733201008397064011774368832458707512367404736905021019585".into(), + "6374197807230665998865688675365359100400438034755781666913068586172586548950".into(), + "2189398913433273865510950346186699930188746169476472274335177556702504595264".into(), + "6268495580028970231803791523870131137294646402347399003576649137450213034606".into(), + "17896250365994900261202920044129628104272791547990619503076839618914047059275".into(), + "13692156312448722528008862371944543449350293305158722920787736248435893008873".into(), + "15234446864368744483209945022439268713300180233589581910497691316744177619376".into(), + "1572426502623310766593681563281600503979671244997798691029595521622402217227".into(), + "80103447810215150918585162168214870083573048458555897999822831203653996617".into(), + "8228820324013669567851850635126713973797711779951230446503353812192849106342".into(), + "5375851433746509614045812476958526065449377558695752132494533666370449415873".into(), + "12115998939203497346386774317892338270561208357481805380546938146796257365018".into(), + "9764067909645821279940531410531154041386008396840887338272986634350423466622".into(), + "8538708244538850542384936174629541085495830544298260335345008245230827876882".into(), + "7140127896620013355910287215441004676619168261422440177712039790284719613114".into(), + "14297402962228458726038826185823085337698917275385741292940049024977027409762".into(), + "6667115556431351074165934212337261254608231545257434281887966406956835140819".into(), + "20226761165244293291042617464655196752671169026542832236139342122602741090001".into(), + "12038289506489256655759141386763477208196694421666339040483042079632134429119".into(), + "19027757334170818571203982241812412991528769934917288000224335655934473717551".into(), + "16272152964456553579565580463468069884359929612321610357528838696790370074720".into(), + "2500392889689246014710135696485946334448570271481948765283016105301740284071".into(), + "8595254970528530312401637448610398388203855633951264114100575485022581946023".into(), + "11635945688914011450976408058407206367914559009113158286982919675551688078198".into(), + "614739068603482619581328040478536306925147663946742687395148680260956671871".into(), + "18692271780377861570175282183255720350972693125537599213951106550953176268753".into(), + "4987059230784976306647166378298632695585915319042844495357753339378260807164".into(), + "21851403978498723616722415377430107676258664746210815234490134600998983955497".into(), + "9830635451186415300891533983087800047564037813328875992115573428596207326204".into(), + "4842706106434537116860242620706030229206345167233200482994958847436425185478".into(), + "6422235064906823218421386871122109085799298052314922856340127798647926126490".into(), + "4564364104986856861943331689105797031330091877115997069096365671501473357846".into(), + "1944043894089780613038197112872830569538541856657037469098448708685350671343".into(), + "21179865974855950600518216085229498748425990426231530451599322283119880194955".into(), + "14296697761894107574369608843560006996183955751502547883167824879840894933162".into(), + "12274619649702218570450581712439138337725246879938860735460378251639845671898".into(), + "16371396450276899401411886674029075408418848209575273031725505038938314070356".into(), + "3702561221750983937578095019779188631407216522704543451228773892695044653565".into(), + "19721616877735564664624984774636557499099875603996426215495516594530838681980".into(), + "6383350109027696789969911008057747025018308755462287526819231672217685282429".into(), + "20860583956177367265984596617324237471765572961978977333122281041544719622905".into(), + "5766390934595026947545001478457407504285452477687752470140790011329357286275".into(), + "4043175758319898049344746138515323336207420888499903387536875603879441092484".into(), + "15579382179133608217098622223834161692266188678101563820988612253342538956534".into(), + "1864640783252634743892105383926602930909039567065240010338908865509831749824".into(), + "15943719865023133586707144161652035291705809358178262514871056013754142625673".into(), + "2326415993032390211558498780803238091925402878871059708106213703504162832999".into(), + "19995326402773833553207196590622808505547443523750970375738981396588337910289".into(), + "5143583711361588952673350526320181330406047695593201009385718506918735286622".into(), + "15436006486881920976813738625999473183944244531070780793506388892313517319583".into(), + "16660446760173633166698660166238066533278664023818938868110282615200613695857".into(), + "4966065365695755376133119391352131079892396024584848298231004326013366253934".into(), + "20683781957411705574951987677641476019618457561419278856689645563561076926702".into(), + "17280836839165902792086432296371645107551519324565649849400948918605456875699".into(), + "17045635513701208892073056357048619435743564064921155892004135325530808465371".into(), + "17055032967194400710390142791334572297458033582458169295920670679093585707295".into(), + "15727174639569115300068198908071514334002742825679221638729902577962862163505".into(), + "1001755657610446661315902885492677747789366510875120894840818704741370398633".into(), + "18638547332826171619311285502376343504539399518545103511265465604926625041234".into(), + "6751954224763196429755298529194402870632445298969935050224267844020826420799".into(), + "3526747115904224771452549517614107688674036840088422555827581348280834879405".into(), + "15705897908180497062880001271426561999724005008972544196300715293701537574122".into(), + "574386695213920937259007343820417029802510752426579750428758189312416867750".into(), + "15973040855000600860816974646787367136127946402908768408978806375685439868553".into(), + "20934130413948796333037139460875996342810005558806621330680156931816867321122".into(), + "6918585327145564636398173845411579411526758237572034236476079610890705810764".into(), + "14158163500813182062258176233162498241310167509137716527054939926126453647182".into(), + "4164602626597695668474100217150111342272610479949122406544277384862187287433".into(), + "12146526846507496913615390662823936206892812880963914267275606265272996025304".into(), + "10153527926900017763244212043512822363696541810586522108597162891799345289938".into(), + "13564663485965299104296214940873270349072051793008946663855767889066202733588".into(), + "5612449256997576125867742696783020582952387615430650198777254717398552960096".into(), + "12151885480032032868507892738683067544172874895736290365318623681886999930120".into(), + "380452237704664384810613424095477896605414037288009963200982915188629772177".into(), + "9067557551252570188533509616805287919563636482030947363841198066124642069518".into(), + "21280306817619711661335268484199763923870315733198162896599997188206277056900".into(), + "5567165819557297006750252582140767993422097822227408837378089569369734876257".into(), + "10411936321072105429908396649383171465939606386380071222095155850987201580137".into(), + "21338390051413922944780864872652000187403217966653363270851298678606449622266".into(), + "12156296560457833712186127325312904760045212412680904475497938949653569234473".into(), + "4271647814574748734312113971565139132510281260328947438246615707172526380757".into(), + "9061738206062369647211128232833114177054715885442782773131292534862178874950".into(), + "10134551893627587797380445583959894183158393780166496661696555422178052339133".into(), + "8932270237664043612366044102088319242789325050842783721780970129656616386103".into(), + "3339412934966886386194449782756711637636784424032779155216609410591712750636".into(), + "9704903972004596791086522314847373103670545861209569267884026709445485704400".into(), + "17467570179597572575614276429760169990940929887711661192333523245667228809456".into(), + ], + vec![ + "6745197990210204598374042828761989596302876299545964402857411729872131034734".into(), + "426281677759936592021316809065178817848084678679510574715894138690250139748".into(), + "4014188762916583598888942667424965430287497824629657219807941460227372577781".into(), + "21328925083209914769191926116470334003273872494252651254811226518870906634704".into(), + "19525217621804205041825319248827370085205895195618474548469181956339322154226".into(), + "1402547928439424661186498190603111095981986484908825517071607587179649375482".into(), + "18320863691943690091503704046057443633081959680694199244583676572077409194605".into(), + "17709820605501892134371743295301255810542620360751268064484461849423726103416".into(), + "15970119011175710804034336110979394557344217932580634635707518729185096681010".into(), + "9818625905832534778628436765635714771300533913823445439412501514317783880744".into(), + "6235167673500273618358172865171408902079591030551453531218774338170981503478".into(), + "12575685815457815780909564540589853169226710664203625668068862277336357031324".into(), + "7381963244739421891665696965695211188125933529845348367882277882370864309593".into(), + "14214782117460029685087903971105962785460806586237411939435376993762368956406".into(), + "13382692957873425730537487257409819532582973556007555550953772737680185788165".into(), + "2203881792421502412097043743980777162333765109810562102330023625047867378813".into(), + "2916799379096386059941979057020673941967403377243798575982519638429287573544".into(), + "4341714036313630002881786446132415875360643644216758539961571543427269293497".into(), + "2340590164268886572738332390117165591168622939528604352383836760095320678310".into(), + "5222233506067684445011741833180208249846813936652202885155168684515636170204".into(), + "7963328565263035669460582454204125526132426321764384712313576357234706922961".into(), + "1394121618978136816716817287892553782094854454366447781505650417569234586889".into(), + "20251767894547536128245030306810919879363877532719496013176573522769484883301".into(), + "141695147295366035069589946372747683366709960920818122842195372849143476473".into(), + "15919677773886738212551540894030218900525794162097204800782557234189587084981".into(), + "2616624285043480955310772600732442182691089413248613225596630696960447611520".into(), + "4740655602437503003625476760295930165628853341577914460831224100471301981787".into(), + "19201590924623513311141753466125212569043677014481753075022686585593991810752".into(), + "12116486795864712158501385780203500958268173542001460756053597574143933465696".into(), + "8481222075475748672358154589993007112877289817336436741649507712124418867136".into(), + "5181207870440376967537721398591028675236553829547043817076573656878024336014".into(), + "1576305643467537308202593927724028147293702201461402534316403041563704263752".into(), + "2555752030748925341265856133642532487884589978209403118872788051695546807407".into(), + "18840924862590752659304250828416640310422888056457367520753407434927494649454".into(), + "14593453114436356872569019099482380600010961031449147888385564231161572479535".into(), + "20826991704411880672028799007667199259549645488279985687894219600551387252871".into(), + "9159011389589751902277217485643457078922343616356921337993871236707687166408".into(), + "5605846325255071220412087261490782205304876403716989785167758520729893194481".into(), + "1148784255964739709393622058074925404369763692117037208398835319441214134867".into(), + "20945896491956417459309978192328611958993484165135279604807006821513499894540".into(), + "229312996389666104692157009189660162223783309871515463857687414818018508814".into(), + "21184391300727296923488439338697060571987191396173649012875080956309403646776".into(), + "21853424399738097885762888601689700621597911601971608617330124755808946442758".into(), + "12776298811140222029408960445729157525018582422120161448937390282915768616621".into(), + "7556638921712565671493830639474905252516049452878366640087648712509680826732".into(), + "19042212131548710076857572964084011858520620377048961573689299061399932349935".into(), + "12871359356889933725034558434803294882039795794349132643274844130484166679697".into(), + "3313271555224009399457959221795880655466141771467177849716499564904543504032".into(), + "15080780006046305940429266707255063673138269243146576829483541808378091931472".into(), + "21300668809180077730195066774916591829321297484129506780637389508430384679582".into(), + "20480395468049323836126447690964858840772494303543046543729776750771407319822".into(), + "10034492246236387932307199011778078115444704411143703430822959320969550003883".into(), + "19584962776865783763416938001503258436032522042569001300175637333222729790225".into(), + "20155726818439649091211122042505326538030503429443841583127932647435472711802".into(), + "13313554736139368941495919643765094930693458639277286513236143495391474916777".into(), + "14606609055603079181113315307204024259649959674048912770003912154260692161833".into(), + "5563317320536360357019805881367133322562055054443943486481491020841431450882".into(), + "10535419877021741166931390532371024954143141727751832596925779759801808223060".into(), + "12025323200952647772051708095132262602424463606315130667435888188024371598063".into(), + "2906495834492762782415522961458044920178260121151056598901462871824771097354".into(), + "19131970618309428864375891649512521128588657129006772405220584460225143887876".into(), + "8896386073442729425831367074375892129571226824899294414632856215758860965449".into(), + "7748212315898910829925509969895667732958278025359537472413515465768989125274".into(), + "422974903473869924285294686399247660575841594104291551918957116218939002865".into(), + "6398251826151191010634405259351528880538837895394722626439957170031528482771".into(), + "18978082967849498068717608127246258727629855559346799025101476822814831852169".into(), + "19150742296744826773994641927898928595714611370355487304294875666791554590142".into(), + "12896891575271590393203506752066427004153880610948642373943666975402674068209".into(), + "9546270356416926575977159110423162512143435321217584886616658624852959369669".into(), + "2159256158967802519099187112783460402410585039950369442740637803310736339200".into(), + "8911064487437952102278704807713767893452045491852457406400757953039127292263".into(), + "745203718271072817124702263707270113474103371777640557877379939715613501668".into(), + "19313999467876585876087962875809436559985619524211587308123441305315685710594".into(), + "13254105126478921521101199309550428567648131468564858698707378705299481802310".into(), + "1842081783060652110083740461228060164332599013503094142244413855982571335453".into(), + "9630707582521938235113899367442877106957117302212260601089037887382200262598".into(), + "5066637850921463603001689152130702510691309665971848984551789224031532240292".into(), + "4222575506342961001052323857466868245596202202118237252286417317084494678062".into(), + "2919565560395273474653456663643621058897649501626354982855207508310069954086".into(), + "6828792324689892364977311977277548750189770865063718432946006481461319858171".into(), + "2245543836264212411244499299744964607957732316191654500700776604707526766099".into(), + "19602444885919216544870739287153239096493385668743835386720501338355679311704".into(), + "8239538512351936341605373169291864076963368674911219628966947078336484944367".into(), + "15053013456316196458870481299866861595818749671771356646798978105863499965417".into(), + "7173615418515925804810790963571435428017065786053377450925733428353831789901".into(), + "8239211677777829016346247446855147819062679124993100113886842075069166957042".into(), + "15330855478780269194281285878526984092296288422420009233557393252489043181621".into(), + "10014883178425964324400942419088813432808659204697623248101862794157084619079".into(), + "14014440630268834826103915635277409547403899966106389064645466381170788813506".into(), + "3580284508947993352601712737893796312152276667249521401778537893620670305946".into(), + "2559754020964039399020874042785294258009596917335212876725104742182177996988".into(), + "14898657953331064524657146359621913343900897440154577299309964768812788279359".into(), + "2094037260225570753385567402013028115218264157081728958845544426054943497065".into(), + "18051086536715129874440142649831636862614413764019212222493256578581754875930".into(), + "21680659279808524976004872421382255670910633119979692059689680820959727969489".into(), + "13950668739013333802529221454188102772764935019081479852094403697438884885176".into(), + "9703845704528288130475698300068368924202959408694460208903346143576482802458".into(), + "12064310080154762977097567536495874701200266107682637369509532768346427148165".into(), + "16970760937630487134309762150133050221647250855182482010338640862111040175223".into(), + "9790997389841527686594908620011261506072956332346095631818178387333642218087".into(), + "16314772317774781682315680698375079500119933343877658265473913556101283387175".into(), + "82044870826814863425230825851780076663078706675282523830353041968943811739".into(), + "21696416499108261787701615667919260888528264686979598953977501999747075085778".into(), + "327771579314982889069767086599893095509690747425186236545716715062234528958".into(), + "4606746338794869835346679399457321301521448510419912225455957310754258695442".into(), + "64499140292086295251085369317820027058256893294990556166497635237544139149".into(), + "10455028514626281809317431738697215395754892241565963900707779591201786416553".into(), + "10421411526406559029881814534127830959833724368842872558146891658647152404488".into(), + "18848084335930758908929996602136129516563864917028006334090900573158639401697".into(), + "13844582069112758573505569452838731733665881813247931940917033313637916625267".into(), + "13488838454403536473492810836925746129625931018303120152441617863324950564617".into(), + "15742141787658576773362201234656079648895020623294182888893044264221895077688".into(), + "6756884846734501741323584200608866954194124526254904154220230538416015199997".into(), + "7860026400080412708388991924996537435137213401947704476935669541906823414404".into(), + "7871040688194276447149361970364037034145427598711982334898258974993423182255".into(), + "20758972836260983284101736686981180669442461217558708348216227791678564394086".into(), + "21723241881201839361054939276225528403036494340235482225557493179929400043949".into(), + "19428469330241922173653014973246050805326196062205770999171646238586440011910".into(), + "7969200143746252148180468265998213908636952110398450526104077406933642389443".into(), + "10950417916542216146808986264475443189195561844878185034086477052349738113024".into(), + "18149233917533571579549129116652755182249709970669448788972210488823719849654".into(), + "3729796741814967444466779622727009306670204996071028061336690366291718751463".into(), + "5172504399789702452458550583224415301790558941194337190035441508103183388987".into(), + "6686473297578275808822003704722284278892335730899287687997898239052863590235".into(), + "19426913098142877404613120616123695099909113097119499573837343516470853338513".into(), + "5120337081764243150760446206763109494847464512045895114970710519826059751800".into(), + "5055737465570446530938379301905385631528718027725177854815404507095601126720".into(), + "14235578612970484492268974539959119923625505766550088220840324058885914976980".into(), + "653592517890187950103239281291172267359747551606210609563961204572842639923".into(), + "5507360526092411682502736946959369987101940689834541471605074817375175870579".into(), + "7864202866011437199771472205361912625244234597659755013419363091895334445453".into(), + "21294659996736305811805196472076519801392453844037698272479731199885739891648".into(), + "13767183507040326119772335839274719411331242166231012705169069242737428254651".into(), + "810181532076738148308457416289197585577119693706380535394811298325092337781".into(), + "14232321930654703053193240133923161848171310212544136614525040874814292190478".into(), + "16796904728299128263054838299534612533844352058851230375569421467352578781209".into(), + "16256310366973209550759123431979563367001604350120872788217761535379268327259".into(), + "19791658638819031543640174069980007021961272701723090073894685478509001321817".into(), + "7046232469803978873754056165670086532908888046886780200907660308846356865119".into(), + "16001732848952745747636754668380555263330934909183814105655567108556497219752".into(), + "9737276123084413897604802930591512772593843242069849260396983774140735981896".into(), + "11410895086919039954381533622971292904413121053792570364694836768885182251535".into(), + "19098362474249267294548762387533474746422711206129028436248281690105483603471".into(), + "11013788190750472643548844759298623898218957233582881400726340624764440203586".into(), + "2206958256327295151076063922661677909471794458896944583339625762978736821035".into(), + "7171889270225471948987523104033632910444398328090760036609063776968837717795".into(), + "2510237900514902891152324520472140114359583819338640775472608119384714834368".into(), + "8825275525296082671615660088137472022727508654813239986303576303490504107418".into(), + "1481125575303576470988538039195271612778457110700618040436600537924912146613".into(), + "16268684562967416784133317570130804847322980788316762518215429249893668424280".into(), + "4681491452239189664806745521067158092729838954919425311759965958272644506354".into(), + "3131438137839074317765338377823608627360421824842227925080193892542578675835".into(), + "7930402370812046914611776451748034256998580373012248216998696754202474945793".into(), + "8973151117361309058790078507956716669068786070949641445408234962176963060145".into(), + "10223139291409280771165469989652431067575076252562753663259473331031932716923".into(), + "2232089286698717316374057160056566551249777684520809735680538268209217819725".into(), + "16930089744400890347392540468934821520000065594669279286854302439710657571308".into(), + "21739597952486540111798430281275997558482064077591840966152905690279247146674".into(), + "7508315029150148468008716674010060103310093296969466203204862163743615534994".into(), + "11418894863682894988747041469969889669847284797234703818032750410328384432224".into(), + "10895338268862022698088163806301557188640023613155321294365781481663489837917".into(), + "18644184384117747990653304688839904082421784959872380449968500304556054962449".into(), + "7414443845282852488299349772251184564170443662081877445177167932875038836497".into(), + "5391299369598751507276083947272874512197023231529277107201098701900193273851".into(), + "10329906873896253554985208009869159014028187242848161393978194008068001342262".into(), + "4711719500416619550464783480084256452493890461073147512131129596065578741786".into(), + "11943219201565014805519989716407790139241726526989183705078747065985453201504".into(), + "4298705349772984837150885571712355513879480272326239023123910904259614053334".into(), + "9999044003322463509208400801275356671266978396985433172455084837770460579627".into(), + "4908416131442887573991189028182614782884545304889259793974797565686968097291".into(), + "11963412684806827200577486696316210731159599844307091475104710684559519773777".into(), + "20129916000261129180023520480843084814481184380399868943565043864970719708502".into(), + "12884788430473747619080473633364244616344003003135883061507342348586143092592".into(), + "20286808211545908191036106582330883564479538831989852602050135926112143921015".into(), + "16282045180030846845043407450751207026423331632332114205316676731302016331498".into(), + "4332932669439410887701725251009073017227450696965904037736403407953448682093".into(), + "11105712698773407689561953778861118250080830258196150686012791790342360778288".into(), + "21853934471586954540926699232107176721894655187276984175226220218852955976831".into(), + "9807888223112768841912392164376763820266226276821186661925633831143729724792".into(), + "13411808896854134882869416756427789378942943805153730705795307450368858622668".into(), + "17906847067500673080192335286161014930416613104209700445088168479205894040011".into(), + "14554387648466176616800733804942239711702169161888492380425023505790070369632".into(), + "4264116751358967409634966292436919795665643055548061693088119780787376143967".into(), + "2401104597023440271473786738539405349187326308074330930748109868990675625380".into(), + "12251645483867233248963286274239998200789646392205783056343767189806123148785".into(), + "15331181254680049984374210433775713530849624954688899814297733641575188164316".into(), + "13108834590369183125338853868477110922788848506677889928217413952560148766472".into(), + "6843160824078397950058285123048455551935389277899379615286104657075620692224".into(), + "10151103286206275742153883485231683504642432930275602063393479013696349676320".into(), + "7074320081443088514060123546121507442501369977071685257650287261047855962224".into(), + "11413928794424774638606755585641504971720734248726394295158115188173278890938".into(), + "7312756097842145322667451519888915975561412209738441762091369106604423801080".into(), + "7181677521425162567568557182629489303281861794357882492140051324529826589361".into(), + "15123155547166304758320442783720138372005699143801247333941013553002921430306".into(), + "13409242754315411433193860530743374419854094495153957441316635981078068351329".into(), + ], + vec![ + "11633431549750490989983886834189948010834808234699737327785600195936805266405".into(), + "17353750182810071758476407404624088842693631054828301270920107619055744005334".into(), + "11575173631114898451293296430061690731976535592475236587664058405912382527658".into(), + "9724643380371653925020965751082872123058642683375812487991079305063678725624".into(), + "20936725237749945635418633443468987188819556232926135747685274666391889856770".into(), + "6427758822462294912934022562310355233516927282963039741999349770315205779230".into(), + "16782979953202249973699352594809882974187694538612412531558950864304931387798".into(), + "8979171037234948998646722737761679613767384188475887657669871981433930833742".into(), + "5428827536651017352121626533783677797977876323745420084354839999137145767736".into(), + "507241738797493565802569310165979445570507129759637903167193063764556368390".into(), + "6711578168107599474498163409443059675558516582274824463959700553865920673097".into(), + "2197359304646916921018958991647650011119043556688567376178243393652789311643".into(), + "4634703622846121403803831560584049007806112989824652272428991253572845447400".into(), + "17008376818199175111793852447685303011746023680921106348278379453039148937791".into(), + "18430784755956196942937899353653692286521408688385681805132578732731487278753".into(), + "4573768376486344895797915946239137669624900197544620153250805961657870918727".into(), + "5624865188680173294191042415227598609140934495743721047183803859030618890703".into(), + "8228252753786907198149068514193371173033070694924002912950645971088002709521".into(), + "17586714789554691446538331362711502394998837215506284064347036653995353304693".into(), + "12985198716830497423350597750558817467658937953000235442251074063454897365701".into(), + "13480076116139680784838493959937969792577589073830107110893279354229821035984".into(), + "480609231761423388761863647137314056373740727639536352979673303078459561332".into(), + "19503345496799249258956440299354839375920540225688429628121751361906635419276".into(), + "16837818502122887883669221005435922946567532037624537243846974433811447595173".into(), + "5492108497278641078569490709794391352213168666744080628008171695469579703581".into(), + "11365311159988448419785032079155356000691294261495515880484003277443744617083".into(), + "13876891705632851072613751905778242936713392247975808888614530203269491723653".into(), + "10660388389107698747692475159023710744797290186015856503629656779989214850043".into(), + "18876318870401623474401728758498150977988613254023317877612912724282285739292".into(), + "15543349138237018307536452195922365893694804703361435879256942490123776892424".into(), + "2839988449157209999638903652853828318645773519300826410959678570041742458201".into(), + "7566039810305694135184226097163626060317478635973510706368412858136696413063".into(), + "6344830340705033582410486810600848473125256338903726340728639711688240744220".into(), + "12475357769019880256619207099578191648078162511547701737481203260317463892731".into(), + "13337401254840718303633782478677852514218549070508887338718446132574012311307".into(), + "21161869193849404954234950798647336336709035097706159414187214758702055364571".into(), + "20671052961616073313397254362345395594858011165315285344464242404604146448678".into(), + "2772189387845778213446441819361180378678387127454165972767013098872140927416".into(), + "3339032002224218054945450150550795352855387702520990006196627537441898997147".into(), + "14919705931281848425960108279746818433850049439186607267862213649460469542157".into(), + "17056699976793486403099510941807022658662936611123286147276760381688934087770".into(), + "16144580075268719403964467603213740327573316872987042261854346306108421013323".into(), + "15582343953927413680541644067712456296539774919658221087452235772880573393376".into(), + "17528510080741946423534916423363640132610906812668323263058626230135522155749".into(), + "3190600034239022251529646836642735752388641846393941612827022280601486805721".into(), + "8463814172152682468446984305780323150741498069701538916468821815030498611418".into(), + "16533435971270903741871235576178437313873873358463959658178441562520661055273".into(), + "11845696835505436397913764735273748291716405946246049903478361223369666046634".into(), + "18391057370973634202531308463652130631065370546571735004701144829951670507215".into(), + "262537877325812689820791215463881982531707709719292538608229687240243203710".into(), + "2187234489894387585309965540987639130975753519805550941279098789852422770021".into(), + "19189656350920455659006418422409390013967064310525314160026356916172976152967".into(), + "15839474183930359560478122372067744245080413846070743460407578046890458719219".into(), + "1805019124769763805045852541831585930225376844141668951787801647576910524592".into(), + "323592203814803486950280155834638828455175703393817797003361354810251742052".into(), + "9780393509796825017346015868945480913627956475147371732521398519483580624282".into(), + "14009429785059642386335012561867511048847749030947687313594053997432177705759".into(), + "13749550162460745037234826077137388777330401847577727796245150843898019635981".into(), + "19497187499283431845443758879472819384797584633472792651343926414232528405311".into(), + "3708428802547661961864524194762556064568867603968214870300574294082023305587".into(), + "1339414413482882567499652761996854155383863472782829777976929310155400981782".into(), + "6396261245879814100794661157306877072718690153118140891315137894471052482309".into(), + "2069661495404347929962833138824526893650803079024564477269192079629046031674".into(), + "15793521554502133342917616035884588152451122589545915605459159078589855944361".into(), + "17053424498357819626596285492499512504457128907932827007302385782133229252374".into(), + "13658536470391360399708067455536748955260723760813498481671323619545320978896".into(), + "21546095668130239633971575351786704948662094117932406102037724221634677838565".into(), + "21411726238386979516934941789127061362496195649331822900487557574597304399109".into(), + "1944776378988765673004063363506638781964264107780425928778257145151172817981".into(), + "15590719714223718537172639598316570285163081746016049278954513732528516468773".into(), + "1351266421179051765004709939353170430290500926943038391678843253157009556309".into(), + "6772476224477167317130064764757502335545080109882028900432703947986275397548".into(), + "10670120969725161535937685539136065944959698664551200616467222887025111751992".into(), + "4731853626374224678749618809759140702342195350742653173378450474772131006181".into(), + "14473527495914528513885847341981310373531349450901830749157165104135412062812".into(), + "16937191362061486658876740597821783333355021670608822932942683228741190786143".into(), + "5656559696428674390125424316117443507583679061659043998559560535270557939546".into(), + "8897648276515725841133578021896617755369443750194849587616503841335248902806".into(), + "14938684446722672719637788054570691068799510611164812175626676768545923371470".into(), + "15284149043690546115252102390417391226617211133644099356880071475803043461465".into(), + "2623479025068612775740107497276979457946709347831661908218182874823658838107".into(), + "6809791961761836061129379546794905411734858375517368211894790874813684813988".into(), + "2417620338751920563196799065781703780495622795713803712576790485412779971775".into(), + "4445143310792944321746901285176579692343442786777464604312772017806735512661".into(), + "1429019233589939118995503267516676481141938536269008901607126781291273208629".into(), + "19874283200702583165110559932895904979843482162236139561356679724680604144459".into(), + "13426632171723830006915194799390005513190035492503509233177687891041405113055".into(), + "10582332261829184460912611488470654685922576576939233092337240630493625631748".into(), + "21233753931561918964692715735079738969202507286592442257083521969358109931739".into(), + "15570526832729960536088203016939646235070527502823725736220985057263010426410".into(), + "9379993197409194016084018867205217180276068758980710078281820842068357746159".into(), + "20771047769547788232530761122022227554484215799917531852224053856574439035591".into(), + "20468066117407230615347036860121267564735050776924839007390915936603720868039".into(), + "5488458379783632930817704196671117722181776789793038046303454621235628350505".into(), + "1394272944960494549436156060041871735938329188644910029274839018389507786995".into(), + "5147716541319265558364686380685869814344975511061045836883803841066664401308".into(), + "14583556014436264794011679557180458872925270147116325433110111823036572987256".into(), + "11881598145635709076820802010238799308467020773223027240974808290357539410246".into(), + "1566675577370566803714158020143436746360531503329117352692311127363508063658".into(), + "212097210828847555076368799807292486212366234848453077606919035866276438405".into(), + "7447795983723838393344606913699113402588250391491430720006009618589586043349".into(), + "7626475329478847982857743246276194948757851985510858890691733676098590062312".into(), + "148936322117705719734052984176402258788283488576388928671173547788498414614".into(), + "15456385653678559339152734484033356164266089951521103188900320352052358038156".into(), + "18207029603568083031075933940507782729612798852390383193518574746240484434885".into(), + "2783356767974552799246444090988849933848968900471538294757665724820698962027".into(), + "2721136724873145834448711197875719736776242904173494370334510875996324906822".into(), + "2101139679159828164567502977338446902934095964116292264803779234163802308621".into(), + "8995221857405946029753863203034191016106353727035116779995228902499254557482".into(), + "502050382895618998241481591846956281507455925731652006822624065608151015665".into(), + "4998642074447347292230083981705092465562944918178587362047610976950173759150".into(), + "9349925422548495396957991080641322437286312278286826683803695584372829655908".into(), + "11780347248050333407713097022607360765169543706092266937432199545936788840710".into(), + "17875657248128792902343900636176628524337469245418171053476833541334867949063".into(), + "10366707960411170224546487410133378396211437543372531210718212258701730218585".into(), + "16918708725327525329474486073529093971911689155838787615544405646587858805834".into(), + "18845394288827839099791436411179859406694814287249240544635770075956540806104".into(), + "9838806160073701591447223014625214979004281138811495046618998465898136914308".into(), + "10285680425916086863571101560978592912547567902925573205991454216988033815759".into(), + "1292119286233210185026381033809498665433650491423040630240164455269575958565".into(), + "2665524343601461489082054230426835550060387413710679950970616347092017688857".into(), + "13502286133892103192305476866434484921895765252706158317341618311553476426306".into(), + "686854655578191041672292972738875170071982317195092845673566320025160026512".into(), + "9315942923163981372372434957632152754092082859001311184186702151150554806508".into(), + "17166793131238158480636170455452575971861309825745828685724097210995239015581".into(), + "4443784618760852757287735236046535266034706880634443644576653970979377878608".into(), + "21470445782021672615018345703580059646973568891521510437236903770708690160080".into(), + "6932852445473908850835611723958058203645654625170962537129706393570586565567".into(), + "17078326120157725640173982185667969009350208542843294226397809921509565607842".into(), + "19251873001736801921864956728611772738233338338726553113352118847732921831266".into(), + "13062907978694932362695258750558734366820802962383346229947907261606619788585".into(), + "16576609187793673559170206379939616900133457644695219057683704871664434872406".into(), + "17140499059660867342372156843620845644831519603574612796639429147195776838516".into(), + "16226688173010504218547945848523900236290532501559570164276462499487632388445".into(), + "2806068123803905806401128967330263340459046260107112845068533446899070326517".into(), + "17788735370835052317224182711467216134690146479710634688273650370951230404901".into(), + "9840665370904113434661468973557421114403401847108482949465899631150766783733".into(), + "17357287363046228581837055771327121704742940914150998420465281177406182088510".into(), + "8956082469997974864521346025916496675956939495318858500685756691488425559998".into(), + "10583741436561099911914917245130852199607666337956354910388730829023746895549".into(), + "15241902639811607164983030447109332729761435946009172128089506810551693978973".into(), + "10889882303914055687481932975789161945462141459528413507160087442461090813788".into(), + "19789561133254944544821898921133697408237804586549835559829396563401674817160".into(), + "20741336668287037026472434608739333171202674306575625457456116338034432647230".into(), + "17864073449995977742930566850933082711031717858550870842712972350665650521079".into(), + "6017691253505466300212182439349954426085752315661098358839308909771637792741".into(), + "5209125836207196173669497054522582922896061838702136844305036341250990710540".into(), + "8138726312837322624537330169363664364899441867118983214176695868443641051381".into(), + "15491983986041746833254372934846748393213690608865689646440909282144232382678".into(), + "5054332867608171303802774230688792431028169804536607979111644888500809938980".into(), + "15427030776591294577308915282298854681562344215287630895931797573417982096417".into(), + "21754057982677295571284116502193272661309010996970316384923307174180521790164".into(), + "16265286590463120486705206231835953324076688991892805307349612983237844034032".into(), + "17679791107777049796013011282788633179411040182820636236163074053597517790779".into(), + "4281652562868629887097957174897458165728741859103571825874408386197225591996".into(), + "9168010397863299719604788533602757515513214141450093775967322808686129400625".into(), + "17584182367226175071087689123358883902969885218985589531538416263709138156515".into(), + "15671512310414658663135385639435845966109237059155734764323312289873534719186".into(), + "10536294659491685326297777845632759824567028904726211134518740400643540109527".into(), + "13431319759608247201135260841651365578663315527795431484765940626659812285319".into(), + "9584697124715190200241839387725546204368618031045071660911490086723434692561".into(), + "5180327104839158483066851400960171505063442195966219343315555549982472660055".into(), + "18888217223053385111625483360538133292128748730565502371803782424772027937822".into(), + "19535732913737027522540340630296365525208404217634392013266346283017745945894".into(), + "8577759627886344995887423695190093296190181539234301534326157005220006624466".into(), + "16793670928407147476673650839110019799844249677846432113010280456483595763987".into(), + "13926032620965299897272071104154310460519723329016284975305942957859374938463".into(), + "4794697578055472890255676575927616606591024075768967985031137397587590174501".into(), + "3529566190782060578446859853852791941913086545101307988176595267965876143250".into(), + "3975008029239568933166738482470827494289192118694622729549964538823092192163".into(), + "17739094873244464728483944474780943281491793683051033330476367597242349886622".into(), + "7367136451127531266518046223598095299278392589059366687082785080179161005418".into(), + "11175297939460631138047404082172242706491354303440776362693987984031241399771".into(), + "21687543815463985355165197827968086406938428974327951792877419032069230058777".into(), + "21156136641989461785420005321350884477682466566148802533375726181416623358719".into(), + "17347558768803521970212188258074365309929638984714303299899732035040892048478".into(), + "16293716234695956076322008955071091921491953458541407305955104663269677475740".into(), + "4206144021605871396668976569508168522675546062304959729829228403361714668567".into(), + "19988050626299122864942213847548542155670073758974734015174045163059179151544".into(), + "747972634423324369570795147739377097591383105262743308036321386836856106229".into(), + "4612470951309047869982067912468200581649949743307592869671537990797895413707".into(), + "9630852913694079049153027193127278569487291430069466630362958024525616303220".into(), + "17941539917430916523930519432495442476511211427972760202450248798031711471474".into(), + "20332911350443969653703295317915788278109458962706923653715140186132935894113".into(), + "21764801803055897327474057344100833670291402543384934706514147201527191846513".into(), + "18792043166429470991157980448329308661526906138700725174612608941551872082876".into(), + "12308177224490762720061048892842527800271687977085172836705858261595655154325".into(), + "6234555076867437297776538521925679658360922070165740193866337972293380196151".into(), + "4651047048822067434403056477377459986292934655827821636179452835839127581305".into(), + "4762047093602693619418269784972874862577325737690375448572644958129932507374".into(), + "12373514879531674477721132062882065826558811149582829246378921774344318418269".into(), + "452512704634345955634014968317367844987135264395068376894497483188243356523".into(), + "21642936370936057063268550589361090955573362743817395689260298777690935495218".into(), + "16170209200627740434842090607802586195654207376087117044989637541681675086276".into(), + "11682826760471401430136435257946377996085824742031456481961511737883954750045".into(), + "20628055165039718158878805520495324869838279647796500565701893698896698211929".into(), + "16438375313036818694140277721632185529697783132872683043559674569424388375143".into(), + "4855690425141732729622202649174026736476144238882856677953515240716341676853".into(), + "11680269552161854836013784579325442981497075865007420427279871128110023581360".into(), + "7052688838948398479718163301866620773458411881591190572311273079833122884040".into(), + "10339199500986679207942447430230758709198802637648680544816596214595887890122".into(), + "16310974164366557619327768780809157500356605306298690718711623172209302167675".into(), + "4572051236178600578566286373491186377601851723137133424312445102215267283375".into(), + "20933392620931420860078756859763708025350478446661033451436796955762857910093".into(), + "10145870387395991071594748880090507240612313913083518483680901820696866812598".into(), + "11173854866888110108878560284050142518686158431744851782991510385755602063727".into(), + "3895357290105797542988795070918100785105415165483657264407967118738833241858".into(), + "16358886674154007883356717944805100413481233709808000948036974385803613296849".into(), + "10544067501284177518983466437755150442726536257903869254459488412549270232123".into(), + "10495171258604974589451578238018388630585794890815982293891430761424812600427".into(), + "13820724103604550843562070971473423552484851063169471886037640613650155173554".into(), + "2334954333435579600152488915208745055087482119087065911968347050969338669409".into(), + "15100284614446277058846085121308897497066957549089629374506920751044105723791".into(), + "8493821960754696376711287628276980042183127459347650448500304251148421115590".into(), + "18612435536889941393944858783110719304584209891406420832295898519317994950798".into(), + "362101794940079733974215941991047456600874474038781578925062694203564740952".into(), + "11020033081956343850903875701444955317664141075326494650405276926536449284939".into(), + "9396289482656518627529185765935649373549564165735162258912975312413185691167".into(), + "6879055176150676925438486069371149089824290576271090206945130252868108043422".into(), + "12466610601804566637227883322591924115458766539177061670432424956205788935144".into(), + "6570302110526154075173287644133038486970998888099669190857256824048085590052".into(), + "20997862990590350605775941983360263378441519274215787225587679916056749626824".into(), + "2642485040919927233352421501444361753154137311893617974318977215281720542724".into(), + "18832940311494549247524002614969382413324906834787422940144532352384742506504".into(), + "18751288968473015103659806087408412890105261892140397690496125593160830694164".into(), + "13938622158186434739533995447553824444480420613323252752005511269934155122652".into(), + "12878982657080117316101160964182202074759312554860119090514406868768962707099".into(), + "13757859113119127982418426758782225628393556023865807897214601826218702003247".into(), + "11817871682869491875135867072669251115204978941736982465520516648114811792373".into(), + "11336448548896065624515261709306933490181794458266726453198857687608284871020".into(), + "194970717714150352477887371297168267861902418496792228400198694925721020795".into(), + "4999282817977533227652305360183045040853565298259070645110453061034932285549".into(), + "17094174197873140035316532568922652294881600587639905417701074492648767414173".into(), + "8484251464872873032022789624790167173458682056313339863651348894878144808746".into(), + "10260366716129057466862964875306868898686918428814373470382979997177852668590".into(), + "549263552864476084904464374701167884060947403076520259964592729731619317724".into(), + "10052714818439832487575851829190658679562445501271745818931448693381812170889".into(), + "1735373362835209096342827192021124337509188507323448903608623506589963950966".into(), + "7998373949540733111485892137806629484517602009122941425332571732658301689428".into(), + "9035170288660659483243066011612158174896974797912618405030929911180945246244".into(), + "6458619567307414386633203375143968061892762498463026121155477954682976784731".into(), + "12314261817227551876673777186352972884847144237148169773300066404053441924532".into(), + "19869454329688183813243851218196625862680921049019496233616575272637276975230".into(), + "20326917073492686652690019138603910654692396590122884746951129061818467704300".into(), + "20403270805536666081472738304916561119325397964511536801752236086414818653063".into(), + "2865941730880218719188224311916978807415673142487507504983320505748719154068".into(), + "20614246027521726470902405957496110178017768563127335842405314212897493119848".into(), + "12060194341463088508348622863463208827312128863463014006529428845777217660299".into(), + "1128906798719793375274166820235650701301189774851381709919492584451845983197".into(), + "19670876372911656158743764425809421400123168087389888660308456184201759209723".into(), + "5647230694522866559497222129254930524469944430191328619422533907417776118543".into(), + "318629082509194371490189248876734616088516535434806492900653650176451776632".into(), + "13685970881538585172319228162662520285656571966985351768743970447782846353365".into(), + "8283840607829148567836919316142994745766280854211662326632930274668867638198".into(), + "8968895518159422029900464138741638511289476298837958524156654785428413265371".into(), + "10061801991000917366002570579819627134666386452411986168205986791283562415829".into(), + ], + vec![ + "6652655389322448471317061533546982911992554640679550674058582942754771150993".into(), + "2411464732857349694082092299330329691469354396507353145272547491824343787723".into(), + "21491443688002139478732659842894153142870918973450440713149176834049574486740".into(), + "20196926676989483530222124573030747187074792043523478381149800153065505592963".into(), + "12986278951352369831003505493892366673723882190521699331613883287145355738793".into(), + "21126146258242782643168619000295062005037298340836817770565977031890883232034".into(), + "15509665795506578582538177431401381655815033647735781734613703976071034655246".into(), + "6989769181472743404364681671283889685042701491627165526899522083327752110839".into(), + "7062179885254277466334896166987547257487047183881628199983668518000910197987".into(), + "13842521112365108087725039904948872289730786568469683976372377853164252494752".into(), + "3830559505943186272618534143266118508463381443414165428900505002474439179836".into(), + "17704863473432653834041116667846189591617394753001613253930974854399793083900".into(), + "875580502229441633079974792778818749112423694973231971690365132230865385439".into(), + "1971134273535892826573832061354985059300866001765691176219451252512658771248".into(), + "4865738840363990164915013008693722144676933915103280504727326977328013515878".into(), + "1148603338028060679975883868174895825055359423662532941509525326937127571764".into(), + "17506086433923270253695698017062834613463718526046463655503742220257039588796".into(), + "21580033018107258179208198773211859664893072138803756118939260252922297665067".into(), + "15411900706973212043830142913959920716501447427702082030760032355626616412240".into(), + "12219699506725448409610279620972339448030565224304464695714944121760832152291".into(), + "4525719544192047521328360848269156485222470829314314216955024799558286708479".into(), + "19667371373588322336224317159113441765198420040800065314868656839300028747331".into(), + "18916925604689704279265158984702141998345424765142129953154245912230835240445".into(), + "12789343981741773931665143789673052782408749041041266509485929045869073416222".into(), + "3094428508959717445577232225505810354980663487713729230015754183012845687401".into(), + "18544590634480965569098056786078005630500574069468005220462377474861119476492".into(), + "20990087440247450018723844204951613913840993427110495085701200965767234569705".into(), + "17552251989761134508416634118845221324472178264364440017634233349418103869223".into(), + "21000797802575507763447855752602183842956182733750968489641741136166640639409".into(), + "19292751508591545849778577901067988044973302547209758604667395356943370737868".into(), + "18314088316445539319869442180584299715533304874169767778761887632882728399870".into(), + "15003745150856597539000559910957155642193629735521291045949652201905498569732".into(), + "7839443900003691950104175747634267110464104444913379977500178134209666299140".into(), + "13568305490393393394812598233983935295266242465548739772708079888867621061127".into(), + "6453005227995051361096639028742707098785560656441339640433794156400437698140".into(), + "1420171596348195609536167209221442141824294918625468780931400849866478645240".into(), + "8347329128252205996443084339884155586061343024498283583400215109265013719709".into(), + "7893774494551056447960817286805128884970061671041428326788899872964096959040".into(), + "8970476243368194065341537088653900235777512204874037182428362347342487241690".into(), + "239049405935404678508864874854718951364753739466303321590415544572014148257".into(), + "15772878921699764223771017074289335629553777447709755479885293350677783703695".into(), + "5416082112919155131434995906647355834510201879607888732259087164602171650389".into(), + "4384524908062410354304345761652962203632712291085564157560146286207296352050".into(), + "4210984612917608245844011498198864216639269565627982123611519493203177283139".into(), + "18816442907032290878644773027005263628136050677095986565400687355912498966559".into(), + "21443510232279945782338486087712914668515437675585863788610958361560172084515".into(), + "3234314779308300525339049581669531363375743827111579883853941968586490182859".into(), + "11029499234949696730080035941750777601416171837281021031653841244636590396063".into(), + "11145210633226924132308292113124660576759662647204939721872338908644906571564".into(), + "4583160563963432761409369246361117506465307518522062239686649163525543782173".into(), + "9813992026757562966842771727657080117609486122615087352428596024939855084450".into(), + "10084171857039480706430282187972782725948479260179367780776125786119489581409".into(), + "3874212709197875589640151274548083098712939093643165182881681226579903752816".into(), + "21595542491397091124739711708612983479307589335640792812157875295064235960610".into(), + "2068530815441314105493629066002923150651375034543842424822712297257260726954".into(), + "2673459852071215292298131389250564595426361004231758522146794940265552265806".into(), + "8591046256746588406353455230465605224309754008961178558834659065898923355164".into(), + "1020055192431352394776887540248098706183934464205704158014904833376067287118".into(), + "11085709480582865378042656141271006552092494690130782253913953070642865919312".into(), + "5673844083530503489429922596812992664928167369104420134641855283771127716005".into(), + "10492199162275168254265892158402955076490959375050993042712629236807564461542".into(), + "2280843393156259739329331366624245275580688891778782679394848304764573859886".into(), + "6807797027131305026345508953353882265754363485246407959111359919046340709440".into(), + "12692191384043938397944633973317584101723715998700063415107128429315536223446".into(), + "19818676957110967644349139912613239435706480354664804036688552936554140369382".into(), + "18055602608192644695569077694296748842203151828348990995792087204755925787339".into(), + "20934555391215769430553078793246717148484784880715746179415906355043590089450".into(), + "11420705181439111353998210442417752592951340005396931802449360401461783159557".into(), + "19878854521263746227125001670931867821366047088989510542865511663910116386085".into(), + "8568201846715449867087132677683368912214864824182424933182820310911278496552".into(), + "19198701614488576617610339232794062430644024620523684127268879880793305460015".into(), + "15262122764244854433806270478871594904740306012582364033343126589996733802868".into(), + "6412758421155818207287638337822550233376667015263373809976157264137577776202".into(), + "17371585001641430978766734501830788427263945848682170096055857509304472649262".into(), + "20262970042379497707724791203314262108784948621691331141565359315001027736581".into(), + "3859750447119748295302212198327542106766447958113540005985799287718502362717".into(), + "1172269945800307665458943534144481495673510885455899148864236015097947176746".into(), + "8164247467959680477306326470118519335673181279975551434197731340070491876250".into(), + "4513977811114181395323888111232002391599397736872779927267726121435887238972".into(), + "1075250595927474080680862736233039825365918646878264905022213616210377518447".into(), + "18658420120424372681792175914064174056413842231969276203770574969914576681364".into(), + "17769673440848360838244654765103041739044212539359630263894092078288342647801".into(), + "4319086204044362848967484441065231939136453667264715596505827197873119273506".into(), + "11221173270629292820060668122527062274557317856738971635698169204652845111606".into(), + "8635411372759272135249379415383299350267629947167809163276219879514948820576".into(), + "926977621651476360285369760355547766944001783780761167546467658394097283069".into(), + "17702143780592866375901805387463459229828093905183622296234691441436877570082".into(), + "629612289140842594504574984021125242351317893847688437087866691775821981724".into(), + "19990548577495092294245865870717186004301934545721835081514347926537975465539".into(), + "7124830628609719908679298707909792306162298058570958688501370177898647946696".into(), + "14620227791860703231425817538142948793892390269806790476396226159679984968174".into(), + "18495581997440241868332244230687799183899751339442721677540757155760745277888".into(), + "16922065056093401385376103551657968760602009001905886435813054626317776258714".into(), + "9969610601962874779035054685661667941954971427956866645694064022029705170229".into(), + "15281641269114187762159685323068136816556739502211864119670902056596295644116".into(), + "12114994625438879103001132949163961965524612903017200394727056658298824651596".into(), + "4840986177718281128440833017205097196672382395936939379498412745183060615212".into(), + "12847307562796769659308999092658905656250954898192781948610713494470441775991".into(), + "20290096217351155282642224215178246911041509999959311313223857240001143893317".into(), + "16151664509646153154405691138084115125600386733136285504828908979176781265710".into(), + "13848845391482751436287906247470303487958950799995701248612703022979890932133".into(), + "6335716166231441585596963683321661194889815181545222079376536449814718259931".into(), + "1824302750039354704619545544386637317858342555634601563660279997221547953768".into(), + "11327469654081586239268713126961534952233559223228327222485848924908493444712".into(), + "10077703415170135154603829433031861799853903739210136452726077323833067256620".into(), + "16368073884579385814331927334821006319227867093692644942500207970751483237405".into(), + "10621580796499573269115131164341885791299038227955222944695715163010783205295".into(), + "2099241376651019397894434242565225315652133572870234550073686122343103853816".into(), + "17104632243449417396641550271977294699471083572885397875525767745512335891599".into(), + "1935453754847256492223646005402770357836971113012418013930273797463411526183".into(), + "7492761611332930896292052363224494314920390056637668407353957465667515477934".into(), + "16836705924460095689555600825174696605443212968244843485187771119291716736958".into(), + "16995495500678141665340056658079449793587669420913589967848082091551329904176".into(), + "16097379973857697753436437302681608056543122759719328497348770844548177814262".into(), + "17476569537128329379528694049566216604638194592812108658767104922628767500420".into(), + "17997217989870184804787026924935938133194070033518938653831611194683423549591".into(), + "17573343771046232580761295935281170028624495346579002725814597714902588657750".into(), + "2450087639204541254902859018960918562514681200270997307467560465282168310665".into(), + "17288084325555056222618040923753050382954155896826087372317882602328092535440".into(), + "21837047676579063581498107773514419735425738753079336764356909012851439336687".into(), + "370061273472837873736743292149368449614309676635341873070086681342317566380".into(), + "420725183996224279379885018872359102189091670793820517618337092091910692771".into(), + "4966571645678139143731798992823327185758562224229132271884647901363447388530".into(), + "5039558223429273757296118284876763395391635773837549121798873235133698166026".into(), + "14663152729953724779401067486012084029581847325524052152795817923033297673686".into(), + "7201040456590575809960214033959496417566605177095808543357813677845263237276".into(), + "16872945504528960415453618286121813996587432836152082188694652370255998768595".into(), + "4914824783780909279212078186433590922437371437384817332713271291839616026466".into(), + "17503018483514413315464207189113334433424965178631599286655188843769810245465".into(), + "4087750571011463387872022799241315348852213278729592692674275176152296405923".into(), + "4006961923780091252337105595934918049936238157468198971234322013673884171131".into(), + "4481908842184366902145805444001507554481032302978790080019710161108326487967".into(), + "13532316826436461968093937893872910736305115143550039673102602344678825540956".into(), + "11602986656925867325907196773754426955346837006705269228226729102186031417465".into(), + "15306992574062791537454541745213815567999895856471097922112648012979731636068".into(), + "4497571735611504561173050536899411999551839050319538712220770383407135602945".into(), + "2571242673174714867278075260451133687893879636121064640779554188161591611843".into(), + "7070272070524747733177730083966686149849667613589868731851816020060781720851".into(), + "1308310289745495626002351437755820460104812708071634598163946330870933261232".into(), + "9483468192990391193401121929514821570714432121414330663623018046165053411090".into(), + "7317568349845215930675847155716598288688799068821709820024570206796617676748".into(), + "1918505733423704616434273602054555051755671749253598966287072464475922854850".into(), + "15158168161084905689406532256983805923258003804476527617207287404280855731962".into(), + "6855540174355511438343304861678411868002455139032857270673849263857877330771".into(), + "5989863238360846166935911112885654223487221280254816980802479355446167746774".into(), + "20283337058688740322296928691341300752003492063748410749625272920572074851396".into(), + "18957132189629332408653055312790838576277703952267542471751593810468444454136".into(), + "15764518568966520670995753676429154315765754748131847346608706222194564055358".into(), + "7192524197002826721654253762628934164676539329903087107420445743247046038858".into(), + "142950766663597487919643890566358241353679421113406309294925836697585309311".into(), + "15012262168187689680572958978610204856600235635916074406168861726626292993057".into(), + "20795666834671497603181209610179324236645779324677512349797033323222380300794".into(), + "12650341271833683789775531792948185319868795529390391267833516836256688318306".into(), + "5597700232877580665749288204589530549415282468176625525368428476461504532052".into(), + "20949303924691159143653175365242293984396858344688574262804199947001630916385".into(), + "10746523145835332938672833282581864816136388045771578294905302886974358762209".into(), + "4998982766221590779170630035756820066555357949247521575936385387288356143784".into(), + "6936999580131731861735955554005106460473097800566952971315565150681540640020".into(), + "6670695360676548472482680016233507548657051302712214051977034166870814430578".into(), + "12210816592786563975173850937247594401582085430897698766795696447223454826466".into(), + "14933901149105284237676334791785996160108290333321693498322435129559137152007".into(), + "3848529433916624869590379003597911090976938589461403388133685310398004369431".into(), + "12778805225074604003024964969486878839359935515509480774809299341511161183802".into(), + "3288267180428684202786697419666969564766921974531343432588030535602163038467".into(), + "1272672432174256751826350693883913844502039730140570583479554071765667798207".into(), + "21130828804874452930669244946376257892693846272313548250936991077452679117587".into(), + "21254559353072473881932828401787134230282801383134765683324465204971002861493".into(), + "4116075860631781527931204624078712926526805345818156200756399332393348685924".into(), + "17435888597009729827411190999389277840088354756277916760187756022854497211746".into(), + "15837398163415665169712832984380121382150588321621493928953938599666110830812".into(), + "17988638446757562417082379159769772097890681265659458369075768452342579854303".into(), + "8144561030363576879343874888624208577604401139613622673042754207987577727758".into(), + "20020299925602421262203305284307419339160247406220693128040712457114283033661".into(), + "2945951415037890626891130390523013930737768652394758977777336357159436605764".into(), + "1505954324723537402640844232704189835623922400329086438898375859826553573763".into(), + "11851584491756305117491374581845512067704002072833714119284164514457248861803".into(), + "14471204965036278214508938537949717553799007630471016532866101610339050785912".into(), + "7163557293233604902868673807221391042191134560333950452577270522828534690707".into(), + "17291625782465108601367695465389799786592304061550212130987221355832952230827".into(), + "10240907112109243116543462081552827576656826251172050843989873656917271396422".into(), + "20702261919346727858635106264046787321170414155594199951578791234276181642650".into(), + "16678253307828004252292273162411388452019952018258857370242272543091326285541".into(), + "19810917631941180098047817620026253706643400683524412974923209268916769874447".into(), + "3357220165225360610202375608872621445880880830154732998557832689480921421791".into(), + "4392285438534542495332422274902727975330102148971785438164412161504066619105".into(), + "14642025133729666610167675086855441462580619607677226879159952689184960379911".into(), + "18142623439987890999821892559271093087005885278955082040377769578204898750505".into(), + "11769399023330099592616157336702104329646487200891911089287290893650532639221".into(), + "7261353756299584174448625214367175510387913706095214313669922259027644778060".into(), + "10406994568199070863112470594593301582798997458844791396920771226539013327304".into(), + "7475277967562870216712397220016587384793504784585573136176313471517144184018".into(), + "9598064630327104406929367986473441777975480987434868213697837347643980267620".into(), + "21137410002545951849752865514437404724653771608225272412595423069852350320648".into(), + "12345612867231779996383303763804719815752861524077922121654106906093103051400".into(), + "16461750199070055335468534730937701659470268635084522644824623393184528879703".into(), + "7829250842543018165409887731515254191943527926556191989558018633300783421935".into(), + "19801151644322693878208767560968285812646931156576102755771403150148125880648".into(), + "808770634664491371274943928223981161442027957963181999892266696287962813461".into(), + "2298122748772261447929855283951027113218922003687701626762072351622993276571".into(), + "17407798064458858450209051887305178872029674498718760624162479511390762310526".into(), + "18585562277464562541666582720366573863334618817908062612923861658144918595030".into(), + "733976598693219656339731904831283238690050114241501938501377743874139460889".into(), + "11316063986696838098122262534148335669847478050407756877728672233736962269417".into(), + "17614529714381496379478130066245111825610297227468263851608027100133421612826".into(), + "12110694197729365219340374599835523099651939156213930558791147158357810646901".into(), + "4337343008663255658976574468931581484970687989356019720784093082313510905405".into(), + "1379188959674402095268172673987199124815512095460112504778179157481327937561".into(), + "3116148242507754420428768481157196067508084836097458698846114802493377512591".into(), + "13306507137873332434793374848948087993544118494881134631519748904811343155566".into(), + "18496878480807017010077624766326681523549495609998881196570603040242554712562".into(), + "3940126764022508707486095199473913866137718790062498893812401335738707507732".into(), + "10030078765792498033316282784150304209584388923549357286679864120250994473810".into(), + "18519871685760382462428068450331593474924737719734568498029727699878543899254".into(), + "12599428893576891013523136950822667754415283296587096197120138265392279834128".into(), + "16038578953099895530943034305356008247313649524436132877362941968861459073483".into(), + "14319233878082524834510736727226054073026413911339853399113450188859080424272".into(), + "13710161613540579690732775978855380876556751245265568031703536595040993113748".into(), + "14958726446649273856607176275240008023824615720456760403465034344703779274727".into(), + "20935428111942360630758629263346308597806819928838924586682307174931367773605".into(), + "5826394436548487315966647466017047216786257295199620110266250301500717796281".into(), + "31401797997389676486806123612280306684597605608110075525648021056710776011".into(), + "10784171495708237485952707518956314344821522727746927291389338644844400581452".into(), + "11604345371765580191117799693565193618158448665352599382713281103552305960442".into(), + "1378145039624937931836538950217364481423707761527018494355648047365613434790".into(), + "10284294167221806561993937798090888689421933711157676807977401896199778472860".into(), + "8233695574758520342808807499924062869636681352769371531557726871630696672029".into(), + "6570581391072134029876349038190171593169496519436674767949949730275868319732".into(), + "4026501263908027819614805027945064360196399012004574117767831931274788631138".into(), + "21091098569404004244061462065218203986433580687172854429523306262593782053656".into(), + "20711772916118045406356429185975897495222240215931761100801599257137350834799".into(), + "3165519312799351250309462589160165591299333587158531489859211268084164422251".into(), + "16470663723473939739601217501478624726068461799539012562455639586886033078064".into(), + "15672299304945968727435591100602007503785845873606917887638890765525875123857".into(), + "21393538327627889838198844493522533627143658125568123117776524944297103649079".into(), + "7688819203734248199049004650451546300187194458173935784579101984183800649342".into(), + "6609663518412297884695057080546416278366560290439222127471462938252865438638".into(), + "3476303650597281786976907813110835564442121684386467570637538230409080744769".into(), + "20633582549754495054832414039299188930065286005370053173386561254823483851717".into(), + "18067076834611402459142612082327591538480657933568191619109271502102126814407".into(), + "157209609820117793892254328219308970217366919934739036156851508233236414461".into(), + "1848396116513925340973398423998379465460554039715233953825786874352442451413".into(), + "188642786730195655565401615804782553245486295156304142809552609651873793325".into(), + "540089254487190924787439362270708251103955915909358626209177199653451469720".into(), + "12796274768956950589847157187031845061404119522843128177103898080653493269942".into(), + "1785666356337148874573621868025910291826158842346617719666738769156993598966".into(), + "20649919247042517528354490854561347316237285929352042389729444382153378749538".into(), + "9568390566108569727471722677925269460696523515877621230569682954652430518787".into(), + "8590683334740232786825518158771304803451657249486419816607179533515442407283".into(), + "9321198393538172042803957409292145345834077448228642847843261373640165958582".into(), + "3651905214805616378360839954289447530035139753215923648216350128870943481828".into(), + "1324345422558073117779462079218851558068746895262914344818945294328678893083".into(), + "6666363895154434021620869731925915051086919707989020578203743660669796175288".into(), + "9850757893972463103359995012900314323213006625927501272997539940766979170137".into(), + "10214293226445704940138790188111862069675188797488928722469679760666574484266".into(), + "16862124085118494177559484642483513597285992646267864845521573612482278871023".into(), + "9172340118369291059693735314505606817316211450324955429310200429408035954801".into(), + "1968992755714619414656181112336357119271845800144345284299978250769356388249".into(), + "17192498940296212027365280042755701662136570107224000496521552617655679821443".into(), + "10063385968535643122430064779260670089120686456635080613693015398478175344193".into(), + "20101961459945738562625328882763768836449780661345042148985756598106706734632".into(), + "12704305975772252539534386080950631076046431529894091327218544197389260775334".into(), + "3008242816727585639441748210631464697850194693570485141354082562181236010097".into(), + "7797705698071555811456747812384107102104184812467361013142453143842134807658".into(), + "19323240331433203844038522035479659453946066968727795017745942269828428751105".into(), + "1698137797127320576751729191866734754105401103859852376273763815257758421427".into(), + "17656850887825900397821271738817912328294075224643535784810269137125067875996".into(), + "20755447986835730799031196367323817361150623932048563112034040627213597261325".into(), + "6221130271964372280138992636208062417325313096379273438539556580491430711297".into(), + "11042709376363248213366896208587241517252100440844476816212498352999929578287".into(), + "987361321094619571176752720390429919723900732295551211263814448408232028205".into(), + "15077982986114392945859048373768437818569856001604485167476360943078774679228".into(), + "6278894644165961404521866714059972066255652200107181684047812674333675794053".into(), + "2649747800006903047073625320829560088088800522557851927539477888486006072675".into(), + "2636278052351769676017824297717609512488651850924228608531372135635042762078".into(), + "816232991472315395984098922575496846552245086608787214581606973359616326446".into(), + "14372687274434205592004117128588852491871014819273428668840779210928924573820".into(), + "7351401720390274950322621121981079413650308506660552567079785209176949174210".into(), + "10275293929161727274572318228903710245677747557851999483919909420098936352013".into(), + "14869686444606195206734119702227763209172799407142930791211203702643805341518".into(), + "937617196362766626935279232045712623531859540210120280128165029613358941709".into(), + "21331527351771920568751070369057714014285398281585036009305608379072813379081".into(), + "4305436470381074948146072259605215282335211631970525440530773004228212378618".into(), + "5894273721571292784412707230481346442881109207745969297947253583203466014760".into(), + "6512250441044591603946512492071171861967500633638753443182294740883123881284".into(), + "20863871952569294813936866452848141274047362082838805921071316386912981651979".into(), + "18788566662709810970880679984141390717017951403407913908833463086244783373013".into(), + "7784927597396249543149135503684024377171301321636804832597181795981969626201".into(), + "13818519831569592521516488188127966399245767953522268350556654747680372036664".into(), + "10515208647860053151690062640705322684876580250632027862984821874343071549235".into(), + "797604926079325807488629085866693514275115789253871397971708541758696512985".into(), + "8741784289526985522570446847275649913333939699807282742190607491216732972386".into(), + "20966712704043418981047968701828936463778140093909973286855779694780086635828".into(), + "11359697297415630167449040380538108774924967116147664240213257348125754475868".into(), + "8070907838094569287067982462230761680706116783989613960066342967469297961118".into(), + "1868550288036217638713133945402464194193242298015503906068429633793800456561".into(), + "198709459347510170000840600179608479136663571567208109852828485236018304733".into(), + "1601154135701845545733926027872374554514541574822026314034696802419388627041".into(), + "4363994778006302991481199477873248350039564117453810275561422974475581105893".into(), + "773054378219982710451611471050404495804413666789496412742983455527754059148".into(), + "5209426340109575519362014651321132459061755868557415513439993327176584352934".into(), + "16124961412020675839394907565568143713078242978522632778625312854364651991011".into(), + "20812496670075231301471694692369245988519082317145989298573032859079075730004".into(), + "3312489967581906638742585802390894285073229440039144559060030129184388053832".into(), + "2967475373447822846542676378804990140732835322255774209561143670843223463335".into(), + "19744585401442299381952694102570931935735276268739851233412754166721728873141".into(), + "20026293345566344685499234599699178313754630774489046573312844763673073616936".into(), + "2611303659034102517884318354550433047021831422518437228002960700934925644951".into(), + "6230291832603218406134986471162106408091661326026848531605999413028246206577".into(), + "9126162046556730019959291776456914453189657463686708035601186672661595109020".into(), + "18827736146609035067773173111376739253733288103277133456626928961785293662143".into(), + "2328703958261360872869074208611873245571971231035163763965210852182760438390".into(), + "13796410059666172174899788866809560044715551934510722965495280798363043241416".into(), + "1593663256684781552813616365605526150610454082601584196604084376715746899324".into(), + "1565874145189898288764434737762721576951043839540107044892767693968417810945".into(), + "8709849304563896945461696717753976956465219721409993781555147204068634555572".into(), + "2994256803561260177499267243802460581941891553208150783951937342406846377191".into(), + "10452746656507347152042187616753027475507881362159944564077673851918869542550".into(), + "20130580998875572619695450234900655050996104101008767761546912649074040426200".into(), + "18926933358104691474037431437316089682088433006245222723356764715400831411716".into(), + "3783551594057498940671877156409957274854990650480535806320220142873170375307".into(), + "7919031943604095374667473717154511882451510130166237539514111182596247372692".into(), + "14518552587329209714850286012780632801030157943402419401997576700600952906519".into(), + "4770764028263701271241862755569969531641408032906982530346384375773459918490".into(), + "10866502826034731763529371496585294375373238783964914673031891984092997621879".into(), + "4234148117462322266937279401468367908013627589417699250592523530383852950379".into(), + "10747942066055887965185603234524367638106812660210378090215017248140719240336".into(), + "2587411532912868255102795810490361867789634574022411742057853375399270197531".into(), + "17350061113113681344498080520518808976916692173267298878258722510332360424059".into(), + "16490282364669098969805528215926442920328903121380947471680517193373377657129".into(), + "9274691782659584680377375192682066090127280485689527337429804211265749864190".into(), + "7630965482352419767782717986075793694403609453648729580916814032587325374653".into(), + "9483872310024003776681196467845329825094379763716541754956796450187787638623".into(), + "12182966986735661215639970080491757244218854808156498220088212871061979325833".into(), + "1853790963611367149183440339188924598268644281518961106776656221408171642714".into(), + "17425077915972423995335545370701802959607559878032910147159424242864219303096".into(), + "14571075346526399549826264845894977639678567831720652860528738036970272895919".into(), + "5627701855249158721927849603102149698163511782011562166637339712383551336091".into(), + "3620805686755372260289125555061886982808014642356719556961142525373021656729".into(), + "11556995641752009899073583627136467840237831247117281278719511600076965602980".into(), + "18960242154096055221658318882298412299294886669455506299567210308762501113202".into(), + ], + vec![ + "9174141306060971809979631725764298697615039980311809306145004207410652431953".into(), + "4847693924685156250211477469465516228032151306221739650606132660616428517315".into(), + "19669833054057639609249840291533340493211768292967819468538893000195036768991".into(), + "19800508893433268850924828171290876015556093796000695603651522426066333836892".into(), + "8244699449852279148780456022144420353408196866113049322676048275081354214716".into(), + "1563672068712965454176533719400672258364596155638916268717470967009721945171".into(), + "12723223712027468580318230235559705540011996847167975439677647504573149248849".into(), + "19944398841194165937952509356635863229327574447452745793253427406349161295763".into(), + "21218058308392585368594275702746106483411305671883946244077923955757637296177".into(), + "18442884961885927579732373746933397748806426938144021013884176466434407012116".into(), + "11138408360119814115926439449668526422561003790198269766757675305576549475808".into(), + "12724564576884231109847024566806896391934587839830522481308995309797961575379".into(), + "4897733190252075532660075013731462724561461746919488679609618967302541674417".into(), + "4797748331306263412471031924618974997396620231469532262170060449304337691527".into(), + "8626839560132907403537141283531395025838110825355541158539075100658769738351".into(), + "6096293906324574249636975851522292408228519044739444932687579741964974917617".into(), + "2351617695830568421216396081605990689071283678701192113347036659596049514149".into(), + "3045682390398203085155257535118136303069379656645406266260961816947178911890".into(), + "6935829264874515341379952008241845470659188886156484974987865751370715745075".into(), + "19847439266968955911971997829840067368072860877451092633069920565944933744280".into(), + "12795097343831149148337906863235678514689648096503928066579129201713661539889".into(), + "10424580232112390318877053133877999442988769389050776486274146627765228950235".into(), + "11651452649618223740363812212607761589812354035139843126315028745587570714609".into(), + "21307929358023177131550002602820591970791247513576735567457471459920519084552".into(), + "2579908580162153663820021562014873149811195641589016321720930006635393981680".into(), + "8198198178555784054784079137247244121807775986273563786249987394640289859893".into(), + "17176088986876377315956611075288620878117708836881362200541916957398026761276".into(), + "671389874397910339333118510595007038137908096657753354622355890021074216004".into(), + "19161949137729278558310070194809106779119877882343914445178348849980058405327".into(), + "10827554013954037091657804154642286174226562252063767377995268439458401752538".into(), + "11693672899474469123468133710607776304784343543318650064064636202512816205843".into(), + "7026547767612627656560992117440221331093280829523426249915938274837157551621".into(), + "14422968137896343032446633683271253661000603582016449215470992885331170459671".into(), + "7685352543184863430081115767111935982586458632527708735083385591291346555502".into(), + "14089009391529192464370954954330128327830078875414722902347666490457756695535".into(), + "8424161061743752192085022963953944100289245618074575727145394775891645849043".into(), + "9809236779073852557054640507912802523501426410996355424610807253990040160483".into(), + "14100245203768962710288059230665566265892855964739454261791429988929622355986".into(), + "7775683622333704945225255741567928967674629526812606133980425422182282014012".into(), + "8739247215686497264451630351996892836638898510934389758205488381695687859658".into(), + "9431876969679115468275053745264413939426444105271849398322497961102606290132".into(), + "257914055321743732506701382989022126153391940932933566664491918941925247878".into(), + "21801414068435960590201256257290267142214176965736081788536576642934903066059".into(), + "9465495933537134443327560834432669768951376466867005153580146079082722525723".into(), + "7862366214258716333873810314803222267215825847232397599183717032713290878315".into(), + "10701164906390193792620967030790214270231326273599373762943959252633779929633".into(), + "11951628827727068395937910010248864431667047516686609553745879936868276916066".into(), + "14268744039571470490378560085356767818183790841094115879980723591887874138419".into(), + "14468215915818797151199796266933432577607248341385185700017147731054148927023".into(), + "1523824033338639123415809477892820349580561577160869448927791050266158538520".into(), + "13559991428776910947424645696251487328999214391124402586267086012691140984198".into(), + "18151203063828433535061866995346135260543721730169485344610433976436663085882".into(), + "13436242600153492361692256644258899977135098134175123174795293078081801647137".into(), + "9384556671429507406657070680351030238568956203341356106463890924933167416522".into(), + "20321079285577981781556986944841048777999006905303986053275199507771332527205".into(), + "13510502130738135726695195328780836716597947131948116750163533622597187969844".into(), + "20903049289119144354363108865308751668897757360882852151457514926552553533040".into(), + "5611953645512225417723205546533389174830971368309601830751921473015551069534".into(), + "8816886019615642422040038431962872654062471314244185285424018745071289038220".into(), + "16751828354835345790163611999302863949792305206769993810746019449909446216365".into(), + "10421654749141018171116296259626916395875529220250947127973888230084671091757".into(), + "6065225315766552671037285757918350882361743810888619479819895087632281975681".into(), + "5737755346739850738724717271213687543479332312420206954339242459110768587128".into(), + "14770522272891919220644639305274656491731294860310497013287297810648680944682".into(), + "2777394791070450473479179489594969793054480209411136328689318984981401732197".into(), + "10039559932930709555975364107098145624058027439566384376771787183526929807647".into(), + "20757756003754261934858081777796652436155530474748550156383127600004580439167".into(), + "13253166894715452480712170898662712132411702335275401581167208877688374856806".into(), + "2037004052447343668129085129987646907388123739343356363273464870501805506884".into(), + "21829471491172175426560705585746893969222010633542962882847909490991398830669".into(), + "5130395545419191392223692116621486075405299333195732914002649716762739787586".into(), + "20333821730990393095934147177227294218344864602777744425090741435432040213391".into(), + "13629653802252084129446975515814037702423511189484562534040643669977716900228".into(), + "18489091892360842692678715136565494502607711254719045543684163289077857041829".into(), + "21380328601365035012832876315565064374684993115210423862017233170195286906080".into(), + "2280052193465635727584791148501382679094142036232980037838088033232747821762".into(), + "21415541711468815972744677841317235994302058341802530962394281077076174148777".into(), + "17146992672828650459975820445250769505470616910596779130798889014378635881076".into(), + "21676475584514120109058208398560066698690773910598518925936412952356431597439".into(), + "18337052978997482578725645166749278142628133291693686105612531426715865276143".into(), + "14864089429815580405957698645045711801464462794754089671996837547347950054532".into(), + "10834607317840698149140890207826430113987295440254355899459691878793978994131".into(), + "1157143498448645320415276909137008396665083714591338741616893578930275511205".into(), + "5027542104048754930085470328670427788489455916338375169351586496298129661248".into(), + "1922685817237874482932428650501872692326329693528175054457715565489676406535".into(), + "3071473720617798005831658342971536643616129392641449174655528578463370685788".into(), + "21091078808046042460442535848913779439792606439995062001271357804782672390627".into(), + "19773167374024045118471391738750949555178717045037157435777574972149053404157".into(), + "6418695831178793575992210834992785624340084513619644969535805236049937971859".into(), + "6317875495482489567338519005308431806047606843913867465201005132273298011425".into(), + "18001249545956637376455848019549801116909661454019565655561439372098476761813".into(), + "15530167556609139699164228289904946047951254183080358784988008899829027775935".into(), + "8702757129830652230304011519426558036441096750485189115358314568895250616455".into(), + "6369986882953061252605652398893489899416599935424066958291402945530517772170".into(), + "6842894437627604179732847187262933342846269043996061072487488027804029200046".into(), + "20951621154051947571647917571547811655800779287153833018533872651413529893817".into(), + "1219277535080749134805291725937516331501172121638812333911793209536894469364".into(), + "11704605822590166851511022757496386950530399074796545751042566537118336773236".into(), + "5983427701962592508775640503988144495847156070437130549832329402380170245893".into(), + "20169091361583397776908351163571343158517532527313940288212943504015977979442".into(), + "3347733015762117176159731683196584632702931062411889821726902331981723958255".into(), + "16217509027282489850987935065936382820558307489954122630844029918951230268972".into(), + "10781269196927764524006466217779648732772805761839205677745819812868343369087".into(), + "10568911823766972365218731330080733630028238366288098114239172953421915095075".into(), + "5568774544682750792074131352530555554984876659733959079036284517928264996437".into(), + "17854353469028651373397049175548228061144941710027186166132671198740388767529".into(), + "6573034112757039329551886086829829282007989555105157401271097204633906940776".into(), + "14069627287078359391137554212536883450595451640858724555679971658981340584258".into(), + "21119713641590541511025673864154852875977162278614553796484277752677323191505".into(), + "12802116677235410441672624559825044917295689876859311183079161588690810005363".into(), + "16037054471696658545113065872215787085337497333273419984439267709950724531124".into(), + "11698654309680908244303850432833183602706804558317993513795996394673734185716".into(), + "15147889780127043019188099948246961619198549928908180192590946633702778981583".into(), + "3657342516407201801006680507925024451922115018712017224805778401726428603983".into(), + "19776786467141868744713630352693556348834540992018636838044610844396164981103".into(), + "7980994848490005281733955776875257044050741738176865989521982608944874160873".into(), + "12415191330803073018395217955802011585094769098717180100014182475381600382452".into(), + "9300986814650530426668152137665814177758578011365736727321578452726378799933".into(), + "4412208980274764197258090802604347599791567698589180187154608728755887977460".into(), + "2582317668924231956058541757507620542434237159213236485179804217989764223164".into(), + "19860814395849792324574773787600734118308975251437485131415273418632757301303".into(), + "2765909129639570206766170018363951893338720647679193401532780051354569922989".into(), + "5402210382809272147099442645489124829067576777592680891367494969197685281513".into(), + "21011104174655621871977821285307554463403659856745964274018020456838460357574".into(), + "7018364707286303918877589672878574811337524823085078243421192184715151775983".into(), + "136380103284908296988715215087018020601815024625535396780012012453684253071".into(), + "15953315437474610448052466140270091879233956524793052736202793153707558909889".into(), + "5912305909658884889781037379491781973092020933879206417274479331390062715252".into(), + "21575635295587180789566592951559325743281772394055590203112195979769645712827".into(), + "1541325805478255472079288730846072146731241030100908414806224735345400173350".into(), + "17207219201921814683730773200330679841907450967511507012179337438654141678023".into(), + "18266907794578843029196926509122804272900478710738403531664855427655744759655".into(), + "1204224895193276222782842236712348692319665277014183965830735736728887994581".into(), + "4023246588034712778784328407820569751989619386134504404739514704773521558127".into(), + "9064437981037864995763386367268294611921404895425171966596873454090899491243".into(), + "18733802217274421976148972926716884457128521840010001893311936746027998476583".into(), + "684088380644531080099595788833220377905013807951051638705160997709156627273".into(), + "11994830816367980341637110785269531718699655485484715851375754143223090344544".into(), + "1831724566362300629700078416489434571462666430381219293205871349415506993475".into(), + "476710745682537342427691635955087951551678644045621275039835625280220347951".into(), + "3586272766499559446129476613035465343616602918105042144185864609818186807939".into(), + "21220348736799044560439132291243370111879983677197111626309132298278891334631".into(), + "13683795063599185801186093771702503913590598475095473714851383723199050309401".into(), + "16118007386401646906425171859166434660243697555307927508268622819509657450614".into(), + "20930641024767526790605168032291665313905337763598128831404465184891980632233".into(), + "8098646212401100552303711812039666794078834386731698810205195111722330322418".into(), + "11585783577173465460243373201831086724911159484415020913089605532852648999143".into(), + "6939053275662244505087635417541857793206828446247848992283188764105131966721".into(), + "12798043540382494855660472922674138947867597503468216532170157050160462426199".into(), + "20713389801600667412553956346192236970217099413304167366340548074880917096741".into(), + "8708207547232102069057776099666995672015399188924281674772351753887161579745".into(), + "16016293152251662056020528248861487281148011452459422778601663166015837379163".into(), + "14324897997637439510797191208789711173129460994362368408063402682894248793270".into(), + "5652996184880208428967511742390474289004021508049280419259474250332590598159".into(), + "9877106633097964013050071703002221796318046172981334418310092241450453368579".into(), + "5385816971548914185604875069230499528103133871233951354186676373318036241822".into(), + "8683091293306949708478955451280670950858818602696102489349595054818146782362".into(), + "16854975838650963077652189417311897888852709425835763860743171659164792100482".into(), + "2485160816649177905834265823672532710299580013309324666453183278408904845122".into(), + "13571692148185502188613896013359942531817915076247598483272449919094247957149".into(), + "11899399615412173136098732970606292047945698835588882297719609812145308198009".into(), + "16827672312681684936590464376780346837611857292837989006980972390576065571472".into(), + "15588237822592586948064701827497915157359094833395277985658706133691498343174".into(), + "18356642512438827417103800170157877145465512961188328254773957819312191285168".into(), + "21642368145757804795143182901389223409544979732781450480847315495418822041608".into(), + "13104082060493963869934085622104709047787444250961437496674916673804812287386".into(), + "1561532086277971111804773016487251313460788916643968126116038406859074212104".into(), + "2718320602791009266532615731130512762296058687816604986701989820504700684864".into(), + "6182683520717583142027400659687593712743548729948584058329789905227082638908".into(), + "5757242145794370726637363237313640925174531077560764545993554185332488520899".into(), + "13688467192244237790806289073845563960119021610896694359815485764764608925981".into(), + "12528461541936459922472167643986446262977222390263675720335825628163511159437".into(), + "4897268894447399415795897967133432014527122426051771866816059363418177665482".into(), + "764332419588242767884018802335623760055144509861323437945071732931233600264".into(), + "11755468878196093893190753985692714003062307843033761257593209352165323938879".into(), + "6006022813561851182403581780143813226749481175437001910923100661321563995672".into(), + "13901542382190510449243772206670622017835690746895066410475076631498053123535".into(), + "17648853891656481911225897080296737974064729032668806126284849597245044343224".into(), + "15106333841965710929952896897521673254279668876709612770907537801609875568099".into(), + "20899315415025260484895459315726322363345188136910564549344894025053466430346".into(), + "1409310408943258102775009950750654615881913956151269414096059752250092035807".into(), + "3899088673345731523976816322438172722785832982334214339521575164464706226294".into(), + "21406686765584824639201351330529610299177537976609066339927938099572420696135".into(), + "9121591670793901722224770893633585291275002987585289305307167711146944200595".into(), + "10711764678410479049841945177317023555168593838022414378232020467195337241279".into(), + "6599257303974597452501135281719536074294806740553273627128065549267140155175".into(), + "2142616913275380526921597026822750992917222975992774063376747381991404337593".into(), + "16361086527663411948363284957489078505159658832010445114438602510508720771278".into(), + "17122647864721668762640781848678028227021534122268561738445496382823789619088".into(), + "21708018685042482318786273055293241752114005312590172460099480713746031274624".into(), + "8303630654111760473056607545365338851734309857718959193970615705292826806179".into(), + "3658686547507488906491014260011151850549759409901579684176172268581462329020".into(), + "7720024124908065424512743488999250878143598904717873371853608249805302871508".into(), + "8805244918657836956533473437651380347005779399042661429698187314657501156241".into(), + "6303681354794120075893215838935586592706844702088252970663343726024171795351".into(), + "21512507181643408509426104627003618425209526633080701556628608990726677651135".into(), + "11835373417333287523801757951049679177935522717858158305516568595764125190183".into(), + "13059698839045014411602727811400239840163533672024084777768305507840091151855".into(), + "17635240655824524168378284083397931667938326555447077097306236826752492079430".into(), + "3374412791113107178205006579112630099131939030015047870738873452427211677886".into(), + "649711083340882271985565833699379436167716866997851102439037906608755280128".into(), + "20002805138014565226408902156524463368767807620908543995020210484077706418135".into(), + "11071355197960433041624284534649121637702414580710232237233568479006159191217".into(), + "1105441595020980635809093220782460032826849883993030969714432603468135735502".into(), + "9652765957610682812348919340146799318537766051849796416434577860126024594091".into(), + "19248299650856496267902926731608572596705132576830681367365128976226233392929".into(), + "15285802367070100569572399512275861017714681455564415244982064571963339715277".into(), + "19970416835730683993734843405673457882587154729456022607061085470691843864556".into(), + "1017865638757684714433500504002748241987153668285974836527484933462490771227".into(), + "17284848056169793253916338792235498052654877955690514601079806604278964099314".into(), + "11718277105372928962350331838305733149270432706448484259807630484543527733952".into(), + "6670793378364949883511003949124179112275066568088468958915163969545409700112".into(), + "17088789393958965094855662340742013087397643056458490270185660553870734946796".into(), + "1930788514812600942005320214284180860980345276633471423966020111188605196111".into(), + "8844343159753729614645407314580317697758296041737296276765583948670245312842".into(), + "16657939543606018325703787748629433167511611178952563626096990460124133990109".into(), + "15333343644239485619497914931918504163396626751908652058758135581206765801100".into(), + "16533875915742793452819179569144271760125646811168930162441077117553849625884".into(), + "19679534317472082858641184998487299940737032844519038845860980362664393659234".into(), + "16385719932525604857740698205965045007053424961009717093945644387917936681719".into(), + "14490521084213123170781774542655088188106794646066074998587858678154251198444".into(), + "6386781978322405984893078797365492485297499058328348606653460996474947075858".into(), + "17508047533433736707046937662428611868296556965172642086594091783148965906980".into(), + "14904597000414815084666285064575232635645852687797347860862157463159487771060".into(), + "14979972442969995336727018758631782107138089738395941038626891064816880204567".into(), + "5299243186271864957800928637599294208954109271450189950375274196644046222516".into(), + "16189884555052883188473617525411302750109401983487269295700675997730645714379".into(), + "1645560170870292006287241616671417605853047420339675073261660626733726665673".into(), + "17866745974872498136933906591373095763114066893081150553715211393380040095383".into(), + "5744849574386643500716045532645657520001448510343827372577217716983339773799".into(), + "14021966200238971589811034967347517039341058556783068950884921208853167419283".into(), + "1201178089866013320759085637098781870734315826415474628546655403142858044361".into(), + "5875644793836087035760988842421852197052681650818034527831700615895391179258".into(), + "10875065950479466897559006840696567433921014267247530366235539292597441428702".into(), + "2221662399199449388725697795500999209427453463134383582414172135385907744785".into(), + "9758513532658579204941116584445291102215928928145103503086996542188799521709".into(), + "20879593323317766577775570558015407573466986714590017262168011643343469361329".into(), + "17225846522404915080676699509636264825833159640824918876741681229188434930856".into(), + "15189442986691997434021855855358620506645387296294217783597931695143376252483".into(), + "15973617135551858849206811241799666696907820418171736027820254766840973764431".into(), + "11888113439449420418408437784450952639345990804839507528208325036625374967083".into(), + "12365920814385241227394825974928370916184942218042429533600397623369545597697".into(), + "11966175169612449906889690852332416255478894176917636726028104087408060623141".into(), + "11163554022908212145274813635928762748847331295589087669583554722521180712379".into(), + "15273476004030808005186443499782264987539818978741159793745891769358221570633".into(), + "2013969196885866182480519514425192091338553670034650196068995589691938248955".into(), + "5008975446746271526106846692137145404766553748264648461545948417006052208130".into(), + "3926749194225734582453671614337621250954608160208554883789519551411469033731".into(), + "1635544156808471185144068767649088695307748439189898784051754434524720057896".into(), + "17144944482517962143604430553750908864860079758005337246916094084534304051981".into(), + "13823503533305241872793740090687668844401004819859520464168798913603662683770".into(), + "16335911272023134851779534303717879370955813837529588982953758998930285394340".into(), + "14467284210444150699969889681308566002886261365990840091849371665183151060295".into(), + "10578205764525658336257882813734672799527733392763965031628376897794294290414".into(), + "18771425328697137255453620743509164311086906349726510394566012237817674245865".into(), + "21804626093983212038528370352039806004465345685985435415809095637323683466452".into(), + "12056805308954301132385034564357716323176447186932453788072119595595483786736".into(), + "14307195735327805282612857510308008767450554777122724855715789120735513378827".into(), + "6848201070063637295416045855906784325422580350462489495889308309540335269587".into(), + "631364713487758647973016689203003205602593076699875191323345338325349259049".into(), + "16214655556434201961140525501007839859074077768660052713461045928979956365067".into(), + "20940788212183642266181811368870506130164462254923655617893660245551698033523".into(), + "8257440848494309435270838240795567828478627302119374684511017376568090372435".into(), + "13701089242130867705897643891164147923878521147124165292045879194108024940909".into(), + "6895272953337895406509859406973110417619874994579965619097329249292199573333".into(), + "530437169778092455975584310016745919549274205817234464915791595041990209639".into(), + "9008612822403008353420189298381046023002474279157557733428254452507266389025".into(), + "14863423501786052071018008300345884780479084379412157784789951872243409629758".into(), + "20091026239041315645045502002997446404106877721183777765607724358538559881231".into(), + "11103877261161399045807234470901399725912406134008627937945079980590775715243".into(), + "21529163495181909351665093277427712610965764606448489357319207727176092439794".into(), + "19540446772694448035410067193880900774391072899517686330271100773183944540294".into(), + "17549510450820803306426739851959754252204444648959723652883552677325100583689".into(), + "12252518814610348662318155253547558779974557529822012236107550517806390105567".into(), + "8058115132085119666951861652409945532276905989404523986413207631657437321956".into(), + "15916100116790431839835734530362130437167135501074855072245598938219364570910".into(), + "14256533476494466694764843270015662315303617568641801280831873052211753536970".into(), + "17865471381417606502707639037418669122823481329049436020149405646709537112534".into(), + "14015711483636570179335132940981982618090553643653746531174110949872682031017".into(), + "6075776171664976866533080327142904134938121198707020111533599997509054627652".into(), + "6357981809351565370498807027309828058036389418343890944791766504532174516243".into(), + "15145296985037303761634018005118672316118004891352906450983918852209191841446".into(), + "2473672396516437070485250176897956191104549656554290725379242542480862701754".into(), + "11059085933391482002269653121188853142706883316754376424538662772943167665341".into(), + "14804069155713123448375113552227724310276294677318593116834685772120057819258".into(), + "10146378656966122923223443263705119557842694560695035707977826044606938090895".into(), + "21828309590915152213768434346306434851424116996828875020020066586363340244814".into(), + "15568879616082229996551157805731419126872501425454775741945679993142071548779".into(), + "17504079509060638501918729619244098692140123800571022969294759717277257664716".into(), + "2998311560047298465700351970612785742605093777116697796464434026101441410385".into(), + "20229972737818088327107446854254558628041027965197447598027135778783710740259".into(), + "14884874200763033520375899992902136897590350894844904733314191389520252900641".into(), + "9619409751736964504139815024141276029474791187139050183491749032619248817404".into(), + "11534029087676783672833531415041588991838838078174102967049055562568798961925".into(), + "17106297093375816944137015955705541133308466659538554159312635106186252148471".into(), + "21676736161168806529097919794022110433487869702564846859065695507460463414524".into(), + "12596447704589377083704857810305080195761099125652005594925931498073219198049".into(), + "310943124066162607352831846280730445558498286205117614171844835745706684432".into(), + "16013029710570597613246104892930389004941711962070683476555063566372534206859".into(), + "14282564976066063966062366540992448474634085812789771416509095817495183298269".into(), + "20757241092771652500911491636894210910134068426068355089789205706892703219255".into(), + "17084251309147907751212619949757520468224028014308500329099194408342072624132".into(), + "14680350698112448759886861002622963534698534998651150537754386791270019720748".into(), + "17739512731440543100681958009173086667000199263945053345384367808940651002571".into(), + "8967486063900234709994801661246451094429250620940593387993430620369318619734".into(), + "3906067814916986286272005884942051451306945488494283077675304366798199289520".into(), + "2517004675157816404807349457307096161030587393097616279110332574293494030636".into(), + "9995302877359286298434340810356550712107485295049220989690824504445305103587".into(), + "12849909876017357260683411536833847986127911582040960825577300322066595609115".into(), + "18074515800779889507358182860997188274134395074469953155084226981497567860114".into(), + "6692811728183968363967959295970424292426462800383828091752006855360167264617".into(), + "17859827663908740084792157440799065184931609649811664442236242315795442091367".into(), + "12243409340804252499520308602187370739653046835019551522661290645230850934962".into(), + "3009118420068966587115224335717185828292538080040896739662684632413054772046".into(), + "15856202298588272962175258696610233941787471472716811521132004805327415486141".into(), + "7549804594729480554341356998842376772514802673462970334329441043324983960866".into(), + "6390806437030742378988258255983502109201709511321162596105974797942236431761".into(), + "17370236522182003753669946647208335160124999930136364231371998757664000198520".into(), + "2261672244214630177095236704932243497157963117166120717011661647779055001646".into(), + "17325026196605130064689259977831126468940872193987407658419640959345091161632".into(), + "3631641025220845885502691330008982895233731506600778684638817282531001457735".into(), + "8656561399441987116927438675277763317789561532507396244334062468892541066084".into(), + "4069166732330197412844703565599514109399373916243310212229125901351402003915".into(), + "19808198732373520522982274785888742523226720967259539531129335924093928174880".into(), + "8555796834031869022510134190573521699378201702450788201649007358450530423866".into(), + "17759660636058865290579521740750449606781204755231964378855563896473545202303".into(), + "1335826395218609619260020055566056869243760115287254209950063597653055872566".into(), + "21596200365241795669701682696176077888309278223833581800772036945674858315765".into(), + "12619752319673193899296833725747186284394167228468888029626464753793997178599".into(), + "17420588547980145067421969830249755561311178399975476925894947008643385243007".into(), + "10337481272389772505654575850886249605422739785111225132545740838911222864209".into(), + "17928431631046752749930349099366498612885288622404560316665023363985966878427".into(), + "3075798659324203306711977985120251896073145961913793478792728028765206521425".into(), + "4639500613932181914847461422373341918892878975546430906324216810326467690534".into(), + "15396322795715441250300995201889120935591602515487993982711884319616897970533".into(), + "6391276937505284102735701938724106665734769352007891548547667448647832351929".into(), + "6811373320779057384916660178551330838095673247430496448933336925226142036083".into(), + "6590973140323934807800215988687710942074412987201753370126190631819398102173".into(), + "19364648614154949386936259588484266535262135334799266379433252509193375956715".into(), + "4702754284612371917466042550086249683933140314858807272591351280832918881874".into(), + "1081036249074169248236179367049085684430282426446509768147097371368406374049".into(), + "18548093223441988703029589168425055383154624592689171393242936199350770119589".into(), + "11098999608073377668352846814752381891400020647878345005629685447730764310163".into(), + "16001262992680194260590639872321865154716987495605624862471107193457192704714".into(), + "21696229443869118415905915570780926763029898831113534481730746953640692230062".into(), + "11716215712634983607563947056324900205144202447594949676250978337464771243867".into(), + "1778908113733035314726603632369389424542091991692308812147944884836647395775".into(), + "4019081204388123040098634987844274011285321286777408246805308194144238418480".into(), + "3473266952388383063447927231564219811787341139731701190625605897592140631276".into(), + "10457881304788072618845101933412333126160339089704353596608910674508961127232".into(), + "14926101732700077295531234099443522459232814784151318061435025890154852791802".into(), + "4036967072197259618286839959572768559469665646019907384624959071646231971399".into(), + "12776716624632228928613396031717959431597335742467953143594165782617234803915".into(), + "18894783424164609284436913400522166453255844750192864579927645453695213022195".into(), + "6303809107919167113924303987533838414137996606980561570652539716097058487126".into(), + "4729698693443803882717817492985796053343431875965792864932005291979914613160".into(), + "1645790034267553926884568714540144778649055395816210525904813567839945991808".into(), + "8138260225269705405100573121045873922755899939885385491610389913906979427176".into(), + "680936760009829486282006800072001712155424246576949107399338687767760991887".into(), + "17240357869291182045663678468827695873425113788704614245279840174870850373113".into(), + "19100963939745621863641468371111320143895293700517367016077996431570157414340".into(), + "16188989656090417148189510820963186890780289777598053654241741803194118100843".into(), + "18027402882394597868782011288920739982398714370069420860949975937357531046151".into(), + "17780529984916796963712255733293310230026423072958099290880849386941451922559".into(), + "20004531511171838591303710792081846238092292916166965045929062171308088520097".into(), + "13855731634251510230399834192704620793850325654395687428672253016405315169901".into(), + "16872938837392115669581040432902657478544143723662502779821325505282093696739".into(), + "2541555081244462826761076743762714962901590548271316707071685417008817634653".into(), + "5136424039269088350807839181761422963254683236279333039713142751702136147963".into(), + "19216238128964101420135465007632926445321991494181045543846024053552797518994".into(), + "18868537488540023742258053821537824724371813776839672880900985865823137839953".into(), + "18246710415801024039719497716350501105591286880983169809863166130543617917249".into(), + "20608694004331631709610739723463009412162748201282986294016482926528443868949".into(), + "11318113915971658853560322943565673154831611543653209084299774855226816037778".into(), + "16240989418312335385576389959938922684406585560688799437547298624184839261343".into(), + "16171299673760267132909753100946681733778389681324959987573199154235691694977".into(), + "8036823955656422391918380552495301547890420665617977624790236120392727764522".into(), + "20269862530534739231936251654244170650781428788816658397167110617927916774329".into(), + "2368678892744667199202318323282128737449992006513656480477288092472671147090".into(), + "4618078962163037429845764284139891171861860687111566735174912070413086829215".into(), + "12695350627501306162901105159009497730633599768443844225981772758225613194238".into(), + "16356283146491744069785034066388746989409816380917535719898337817088223419024".into(), + "6407893217596287850421377738867081146106659458551198123106454022096864887316".into(), + "18168868018352364136212098098453930600797374324006271488950341490483455519349".into(), + "18352629174410142476418438008157117497168118524562206830585500251463010761689".into(), + "4344169393287991961961456515301754172943022039566219343212376057129143739343".into(), + "19424839806870716108478074501405697296961947409763509419111261767390677718987".into(), + "5796037897847804302272999466834285170265203646465480652521088328457333766863".into(), + "17402105801450379889120987010453669096275392789725153915905747267778100864362".into(), + "15540989618743824352651126288511222263828123668208146479603617243655978402205".into(), + "945810410725426921570254447269595873973858272778720657523509910503434094174".into(), + "6962323734045776666289031609372270190654631739266635759799844631053633876675".into(), + "11382945272742312954364642163371436855283161775445664525053938433459897196647".into(), + "18940251871958826726849623572811640436342841713786099464305053400421580490631".into(), + "13969540696178305383564753026163726563325318478290740131984853424331762285147".into(), + "4841983966001277917879506889862519614692143906356361564304719688757862622407".into(), + "8939049562492171082419559182596894186639203815268680721033389307282239000385".into(), + "19265363396776097866041313346787101192508520582744521467413665478819721956884".into(), + "337106861429123598189388456471513480497137213511877011021531147545809512194".into(), + "251367482782327915297484770356856386307188967585026711663629212746150191478".into(), + "19506616511267234489421548744907283107923549136620297132842391511025844759064".into(), + "20633589633280372440758096707466273580151526293980868749421563697429194761212".into(), + "18833062060138888612708634036427140134887774731041742144004707524569102994071".into(), + "2927291160590267909596732410727396533948837350308818016906834558527125752899".into(), + "7095572562193114209617459307511041110255341231707924363346373597653253806883".into(), + "14274988113217913224290208839851596837329960221329537670822013510325939323091".into(), + "9965830780560026128320556230399915681196410289456547935188741323403719404039".into(), + "10333365845496980935202034863900757172839454015352626511769637076650624839070".into(), + ], + vec![ + "15193892625865514930501893609026366493846449603945567488151250645948827690215".into(), + "8655680243784803430516500496316192098841666200175185895457692057709359214457".into(), + "11710807066713707084726423334946631888369490193496350458331067367713412617049".into(), + "15442364818086019103203999366702499670382575019009657513015496640703659810202".into(), + "1358747428976145481402682338881091555771254635226375581638965497131373838774".into(), + "15658002471767984962034589730824699545808755102240624650914676102923421241582".into(), + "6420480504329990097173256112095253518339231893829818344055438052479612135029".into(), + "15457172495394305353698644252424643614748461590123908880271021612601244389162".into(), + "5745943350537490600340174787616110056830333091917248931684290284533019091654".into(), + "3877253492903478989342845512796806320713689655633086736499730391667425329322".into(), + "11257677301507982757739320943403112189613848490812422490591766717141506751601".into(), + "16906586852467953445509312290627525856126394969718997799028223470195783329296".into(), + "15263589725854108297280528692120758129000336125328939290924952731952242586386".into(), + "21735940039489460025710098364749096267519151075908323637361429746399161905338".into(), + "20023056608360522105358681147781839024069418874082333862551226466128829664291".into(), + "5677500725280079960679484373333947430817198394184436922575072427342643665917".into(), + "3080516739494460477657748111767941482024045797587058388950619118994388252853".into(), + "21486496065617100719537932626843898998311175055335457507845650282870586541596".into(), + "5371049178920102602305531530023787518286335086323221270202212974241707302466".into(), + "3074817222296007572297581554183445947239252698770067839721345984255386069425".into(), + "19180807038569629573914331337874446591506172622522351734982093457681161813141".into(), + "16937785199372956273358037645552299688842385008757508130180245705952406225194".into(), + "1688218397616770248184651775433764527272029131542529408516364801909017591719".into(), + "16315958669815317541884966612581197291281164499674338063931623110684590850347".into(), + "6218230753007070123505625054833158632732536069700963073464625252554943737669".into(), + "17774528060285257656595928889288330429565059134928074258373583886985960212139".into(), + "16197131592052727313460949906369199026477758140133103701908949020106767192893".into(), + "13418604038232148873269488320329340508522225417123160144993642839875173062296".into(), + "7265658443160253752317166706266927598319661172006072732797351716897681315157".into(), + "17200150079219747370109251547638276280610591698078334228421747259741754887".into(), + "8627121890622175767416692555014275717515106888840919734160364408960047296494".into(), + "14546964505431549758350267964924534495477687922558528647552728692912697049247".into(), + "17132720822762740343718421124251772119916072270451579802112353604446214831761".into(), + "234333065870376500756753915306346778417056884715946003873280290982247600083".into(), + "18375643491701271245209094287106352436174133929245169725584150600992143374298".into(), + "5158448692161567615645197008737390561357077078129599243188536485308363800282".into(), + "614161645152783610732075198073600394068518413590650990586931263981193439341".into(), + "12661793104597977909223565537293318966803153852970198322604479648383643541371".into(), + "13041905650419760925682179803296711066088286278603171065755078690359168540579".into(), + "15006023590144168506070897325649191051975999212058008674224953860265667513015".into(), + "4983349941266961584317889823965291023669365981564144622292227613558024302012".into(), + "482274340065333833495445682213681402212945945150526736364263233985449810602".into(), + "3966893131006556898236790392613869798057510088913626163333804949895810673044".into(), + "20923301526284527685000591080290190641416245135554916208054502046381491809443".into(), + "20838692384005825835959734210506718428443540957544929066941550833051093000166".into(), + "8282357714606447781782716442854085217089572080066047419459610560432999443766".into(), + "5410651444876169088887579490283094453001167796545260026969919887357676973543".into(), + "15276966646285075387317940436655285872037988805762800567413073418506412856419".into(), + "15066911464727337689573664613158712498015597773345106524271610486257089622849".into(), + "14583790985054968382519116885383608902981814292128186470697458065499359610203".into(), + "12059090796146479535492139954279038037217093044815277624197659219529427760034".into(), + "7273811886044732271171500579064359282424476926867187108258957006777685922641".into(), + "1463086899665237074608503061872751147444637332808872866814340325832200880984".into(), + "4403177494620214359779479537027014449448686844655371530169401219256448130398".into(), + "10860968418848589590932601250051274256181778387706764281989724391784015147562".into(), + "5268786978207139542368199165627108325282167169564314266747401266496556301775".into(), + "10683355823176907476704511935094343405052640940909677712096702771871787224727".into(), + "12998090263935761477316698114799901126086030852595294916463464609721875730852".into(), + "21401280461419124637791689956622923839426783908187419462727763377498739154778".into(), + "9827224472048063173905906705579289843819400982583185823840008976971109664519".into(), + "6215804144039763858354471461864183189301201862376216122255322421321775987311".into(), + "15461308489200344015891625455653488930440613755785081602434124530381300882814".into(), + "19336334695450889400681207491394600659946256404722006637851709906131899294790".into(), + "1712331165786355540802697725399423752392267480553199895882357858951999960061".into(), + "18153038525983970702748717571053178456148003321236490384959117581005013333018".into(), + "1080183517033034908031748897211289245459330899463186432840251241943892326023".into(), + "8948022108193679628295152361559653763100984324221629445749311939820327674857".into(), + "9553342289560502306921915013446606435600388298465288181461633559299564421155".into(), + "12714965617376828547637017050548818007690047452402682720666099310241001848988".into(), + "10945704657865102635748104464461970844653553427083981539165832149959193156197".into(), + "17511714411688352203059545713591160825310809755917403629838415797949261359373".into(), + "9253691969419856285051096287845246422848295397226841130282244592511676512433".into(), + "12218945350859454581754463621617733341764245716874083264842931063272433793037".into(), + "15268139709971695434346690496076067658968455677120655340969837725391575270485".into(), + "7948825129295102283421620705853168119104356217418364837218892682579042520651".into(), + "6887299291348589691868712194070626390224806410428583073294593431810559288717".into(), + "3610235157455454109573625364057240708256027358184031380521552355839155549623".into(), + "16532488069063334064099666525339953823111673083177894678898823509406678724969".into(), + "19317517725107761280217103201908049748015068578935276576200982249386084367574".into(), + "14980901224290526859762385599553818204548992110637275324411078408232697158492".into(), + "7741797285700915051013289492475875831764653137095445146268474269974647962596".into(), + "11964233864746181868467810392101989052496076326472717372132104394243614334823".into(), + "12746657111181947224582102380049766839578185276220682311596480990298620200286".into(), + "6408726946032901840418309506578019708113712492100046332894630652186614300568".into(), + "20959261828945984489015610988397031913577918654575078054490013338416801523934".into(), + "3173674599420546165852740604987014294355430358334465189504551707066179193914".into(), + "16110281513253204315524614633789708146700074483476149119440509845258215816735".into(), + "17135377580103690088853370572199271964414896742342749305424508776150797285064".into(), + "1405769920008485935711505753346340073052795087429311991287498566024570212365".into(), + "19088073362945853867763169651582894739272002359692597239222895238839593467749".into(), + "19897231284455588615416169252449008151349728648961637517447194842672488184146".into(), + "20476415629812014715153863754869742189693986277342067785614833846523246536739".into(), + "11074321446706734150375041020583051611133090415774365192315805856051215270782".into(), + "15231367549323128694183572409135806408519505225209496441892541205465727777072".into(), + "10515952069292929457050921929301902464262874744159361114100398880194109971971".into(), + "3216370118771824418364829250073852356774095079734089790620447714552849459645".into(), + "1940445924652458480775282556203659335417827058983719042726494187979000691704".into(), + "7899310668555694144370607061960060230071621529123669746309839400642332452086".into(), + "3125410912833939638823760577011271607678545358020637189655641109813198731542".into(), + "2980079409624774815878860133121670095839651294537928173829312563570356348730".into(), + "3766498515736372882285796238406751547889526137955288498682767455795237989580".into(), + "21751217522789414135074956130080241003845828660310903627224390345319859795839".into(), + "4947229586642010378772262640583556676497656670779800090478805824039760706318".into(), + "2168676839236948809859825591626629233985269801981092020040909992251312517552".into(), + "21172906642114648036685108008020762271569381607092920279879047961076646303327".into(), + "882675742500939602754673078407141697482716600335919344527751158504426951699".into(), + "20942968937722199705624825492102184647835614761458159157410261242387423597787".into(), + "21880640497503102067412608072166388563991106464538369680846671301780353850077".into(), + "17593472026567804917122179982860735087124786197105685847979050530954084564297".into(), + "4492875530722152383516030266828166766820778742874238188105265500984280376666".into(), + "6799763500412433367637987497601148507907071065930142757525839585946238894092".into(), + "7812331664758167657763399273963290017340604299019483750344476103319142702775".into(), + "2222332747647756867926707541092465789402467819000336747029352557749400316077".into(), + "20438798382149666667185974604464532451975024544676922060351031604444896151494".into(), + "16155157103796724378615022758633778903205872772589663310774455593497441785913".into(), + "20281325298063880945091623185126257485818350714264176365501683813650871716911".into(), + "4922178080989486450454493110764936742315495846015561426329316977670113220071".into(), + "19579063976700768282784922967523980346960151903154507737857728349662090787824".into(), + "2458828873355000645851832396764221987760639423132968569631493912353159373462".into(), + "21166618206785010755521994106737991950548963896649678270059527421944129497211".into(), + "9131643699583013708059191290958290089892787165715294157378879201986981390031".into(), + "1820371114511473946932363841206094088983972935646887524223011276305844153307".into(), + "7264184404232663540867032945940974372967974872966180860960243405462016972362".into(), + "11228656105550475045610757902396386402555430893045183008968975441800824215261".into(), + "7151503559113638565935009743218857812859208253653498318591469659718664783964".into(), + "16876040581364499037941813142092448836399042253618385783944016186340703846779".into(), + "10334125383426918152464737478646460879481305348617711177774418125714273980769".into(), + "18900559046103390399749767994653107625464807708680067464279674225251110804100".into(), + "18685667289312169245526749652972366835289568864080726348092618145885982989561".into(), + "19970582871354083670567197978171723431124602481748785146813441774826500485907".into(), + "15873472427137024971035326229485784626398898771525077832924901475242073457867".into(), + "9090803292122260583635467396769157643561973206888822931647063181944243467413".into(), + "10156295009710074552070572489422360071526675259143523597882131082376797944708".into(), + "18600630374968456966046654667577076758720435487386724419578803020365834014000".into(), + "21292291483064245088298314957584631356250347533568992016547598449487977536460".into(), + "2784266893057214755054197979675795184619614089277590464548240934105557638370".into(), + "21206743389683892419024645604723431382001453245850423743581664552645211926469".into(), + "7915761821775326316473924816837591351530533394717381318596295803119061411675".into(), + "21881095237485064870468603451853549262304643738646051878343976465227744077912".into(), + "2011784725603622472271597952122938645154942022107573948889667939904597454410".into(), + "21059869383015715705096974077910228193608826877524913363323189378554601804559".into(), + "13660545486380051482020817701263881806531607595506890631732662177505270213284".into(), + "10831091042775967380899180760062457635694790868286967266013231823406639854653".into(), + "149288128407476550494800886735600251983375852319258454101603889073198917321".into(), + "4032475033542195421623899365282946172767274020529645277615759958662043553317".into(), + "17860535012887415629230166789742533149365132198763199254812432302158542514395".into(), + "611194463774512114860065022851497908950074400927073001695280142990812150583".into(), + "5518364261187313845085346561539515049557757056751872639492957432879259341390".into(), + "783263978868449790737487156609432867806742277074765259237378374864740012575".into(), + "19059339826992310300213673274315612374137067865428300882729551175173242291657".into(), + "3179709304184015397125565132235783368222831063701934511986753856772139349894".into(), + "10954198701843076039176000728742415722273043852061382139560487789741501275316".into(), + "16411266672500930935370066093245284646483148609897099268661795671514664627451".into(), + "14614816948231085620934132277599546641612327229810158468490195811014141518325".into(), + "2458257206135880430320027516329707989817636936777744813891328347210486074414".into(), + "13549483340434455515002570470395006683062583844603627042649952800864870013910".into(), + "14465927800403373425828183741641078057513049263889255157342086762479739044711".into(), + "4039391352709218793104596256671892882216573882631238721514928981154171136548".into(), + "12750457082077152291009387792121930725761848879916565703854704756389714536037".into(), + "20703941646953337308096638741387402857948436803334980867971163138332859477843".into(), + "20148755487317949638981041809982361196106823990400472213765926589941031736503".into(), + "19035096428824471222963574043396024781574056587456391309795571372815435282399".into(), + "13597108420431213178364236660710194375344287228654817880431599113069659963625".into(), + "16737817219786305757887002253067607822378794077688837656791543060369162185533".into(), + "5164935079689729145670846016031605160169301936105766707946436049006171651941".into(), + "21653381930704765824477248798502813954284378782353810890869232482999795586793".into(), + "2062605478140760101860087118379474541965619844748678233207247884294051836812".into(), + "6841505950265078437298089354417829781031272459823272323626556598403583002674".into(), + "18723551101558427097952125661588457059960574026361073828482106612260297969553".into(), + "7898804490983679270754258611113569895515918945891808074921872907759024464249".into(), + "10882278698112390755842292529204069263813359338030917602809789513528936860051".into(), + "19447560013395173052961224723195565400117958329259001072560983848146677205053".into(), + "6251288025262210726686494480483550276704856797649458538460443509657307219922".into(), + "13176666617050786358406074057104742181338809005466316548399895981897535342946".into(), + "20703225796049910173111490454489910459787604528779911406172217267261190895618".into(), + "20336720518722954780604743873837334696992422089627753769439653667292899832714".into(), + "21420427865372074512365684526694872695798980614525900481233709853915806389425".into(), + "2498895690812694987926199054702295457557454143930759961192198950277119149872".into(), + "18753512301709603592612141197073246313430368834576850495154922324845448997662".into(), + "13229612292359498096055458608547157785066962647476451239567069089111704445000".into(), + "2690879919643532184588441383789963956137193400890598777054187145581183393168".into(), + "14142396602342548413722428497204107502988046500369932366351553161157672540408".into(), + "20448725195660080278132534867269279218381543910636641344871383714386318629041".into(), + "2559459540570011016181396098001618067535109329950570139376049832813577592045".into(), + "2209294835847631004298393339896770055851570184195462947318472391473531519454".into(), + "14610669112573509857774678749257346364319969641690596877040685661582231189775".into(), + "15281088465087253563674405311018738676067395725444151577815750152538449780965".into(), + "8600553033773805414817363397077178137667131851961144771667772828459236208319".into(), + "2748346039979601666392027583251905158817539034260921486084376270967628661657".into(), + "6854960712378511006304629447898292218014632388505703802374806527561178043857".into(), + "20207552563190343462280438839438087615024485494479390954719687107061991587248".into(), + "10281541252271366635718295778088948309847900730867531177275273130071062184625".into(), + "18855605847424121529776135453072696981767402526737712879984848146282568841809".into(), + "4160214035780913418097601322951078913381556877408879904436917334405689553255".into(), + "2122867135885631508183413043949777333811557914428796322029495785048111325437".into(), + "18793959580906171893053069386015945646795465354959679615181136313144978078417".into(), + "1043591673717355695648236328597936528752358227297053230241551190351813693314".into(), + "15686469257015275311444450012704351019335987785561570672026138336552980987277".into(), + "14048856209379833670666148034655599475317994357805584661156301746235313941815".into(), + "1011563953969880478397969933799483261900428580241502003261587014788238280391".into(), + "19240556623066672446907714818724971233422104071815927265423017590508305430997".into(), + "2121904286573815063480388650799381683473766736407678915747169455786741101182".into(), + "6724437969134367395210139771738563153857495313330774537559578422672993498270".into(), + "20206855573383441961836932177838081339503382415601366823182724056749038447809".into(), + "3659051978213562322887447057085386386485486575515693147713900345497451171308".into(), + "21246119528547168535908718411570119652856799993958321864163737649108920924448".into(), + "10446114322905404392321651684574668727564081327779662579984472408056125404335".into(), + "10052242287865403393859620372179811039720807230902452334457123873762222543944".into(), + "6373462744579965543231173757071025010089494620309953425653057223643612177083".into(), + "11716070974813426833631730493593924834405915845847679294742728105127112594434".into(), + "6451284530793440411577197006976867289209413848762574411101073727224316913966".into(), + "20143217291446069633369261481904349401356557325260758866598205109039367201468".into(), + "7741896897172494958877302103827661518814930985518070029789560123401964418102".into(), + "7414486245715284930410091802521351113719159777210731898112598211035848096490".into(), + "6480506916211642204624111742530825907262535747743645014149694168805302825019".into(), + "18349725066341807634895742572304899830893334427067633858521634672944685466440".into(), + "1838291082333887710851505844271184097051704051003105078056248035350245616867".into(), + "19201915197596065583046168024521824662441686729039260890206806469763190071269".into(), + "11253788423541320580105520117231178489492440242200599071301755928628199128159".into(), + "6048832714406694444296771635481934823208451249770515560893368035838759154821".into(), + "6398008918881249487422929614611145638894557821587972164243877575640548705346".into(), + "7013037564266297435879776776659289982125632651326438965546874242685502904730".into(), + "5942504790082366811245813670914617310604940200824079289270465669331434165301".into(), + "14344789199380317440464969138686896230070901882253997360605407637865754361287".into(), + "19920212380356573378521292048728904573841049083972983190424200459025557666792".into(), + "8983390577894750782268266038315113359711163721228398686939390484499979421166".into(), + "14953991148867572055684497824790735528852361750007063016470842397064705671772".into(), + "5592033578501586280289038012647352732276003389059749788953239057845882297561".into(), + "14076883072716069263619564306953450824526010844333044566762059693672378725675".into(), + "11108270411921226463443318601950168860230077781212396032908932369105145901793".into(), + "3681277588815101350213324449908372578846563884174807724121308021640034446476".into(), + "7194753190480156904207319938161903897566477363779122267985209483435838216959".into(), + "21241255448366937244332942306324590869759761073985963892514045368815880517382".into(), + "6203071960722514588958553813186803009742459823360660333787981951206442471249".into(), + "19041823565851118046937769551785013706136778514067168239416647071096062639366".into(), + "4928136619692555022185087228378238193895894009623071873887735418398682287593".into(), + "16266329364886004534411977872528706660422476743809029518681886596981922182359".into(), + "8814684891729998059175829142248330760704444206534875755023421115211106199303".into(), + "11072277000652722690981202459933101924925520292174200155471966778637063588914".into(), + "15889576313969861857250394875354819627977602318110620311480656842740292435237".into(), + "6934515229262494305594741689326968268143898236690173897991110238064230886755".into(), + "16212991575388366798683594066983659236103186124339324856776288894513503543244".into(), + "21100508914867482363389012032457112622475533432309937238082785660233880354422".into(), + "10381104469089401657446748653199843213201270332853172509558263968565255702795".into(), + "8849389605935865968361613766905708889092097013638425059146677490704442276611".into(), + "4826404934194100291623537890117339503344940312401101713754206109744511979962".into(), + "9981819567268652304810465083896863711149056310505889216307212434682251812603".into(), + "16218484218588441290424553684558267080330286201433140852298971691458926313766".into(), + "21317661296916247018967238829275056855142711494630067664736600708605437812892".into(), + "19523923008662567951910986132173659591346561824926093935331274289896011695634".into(), + "21439241836891927940168832009944210084078628922824257988298290967895179737163".into(), + "3818036890597976956138669961319975835941979944306305168232209375279960168960".into(), + "10212547715001519604442389033695156945619060410131175896383181616280631586732".into(), + "956283172524544133830416114111944076629240232397666924807554743752464221045".into(), + "8545109273807246425343308224167362024331960554428088718932211551700420545275".into(), + "5647769597708100114837534314408246331518385631750569421373379085922684908872".into(), + "21776221280695269311212391423788179027868152904973644113087833004348746215729".into(), + "15989020831232836203074762591626149244364214836699154611339161287030952623233".into(), + "9384665943619921791886218744024370375464874104981653298499433530463000935024".into(), + "15469006121097295841026542766455781293432005131673839148320165243166330403027".into(), + "16103671377537767724271717097892044266704736999841135349844319906338275108222".into(), + "842367229428650719054831004741080336526228967970570607897528985803108607790".into(), + "8752325400224955775788313769797750158375262384121380328719514077259567119347".into(), + "4803861091350023344885030428100876947830986453029412601567992550504530969575".into(), + "7917553047944370948250445233027936387189889293110390303835890604428798853681".into(), + "16378323148632546424902611135263436821435778030958161546757828745002247975096".into(), + "19873719885630097137106352132870659633926425645300622070145979694717581586592".into(), + "20324790419158243246762098227260178678767896786893299456278167341205663612964".into(), + "4358908354524026935988729716331497263147669784003421920394531784876541301801".into(), + "14403952632095852077754539203207047943619815438482171213105824864831554185165".into(), + "16410713482142323347391147127545553384558868490870150984280601225023662513809".into(), + "7304216341846662695189617252648753140769311862815448449926830269690397729157".into(), + "16792943782280077475956215580025612636120139194657275471595325031090407485768".into(), + "18494329391227402645175320826355306995912366111176422593669423022411884295357".into(), + "3277597348237827068690736756050060740435013727549848360800059544123155276133".into(), + "9396765756719511114743964794180256605700037182617127755220919249774110852382".into(), + "5637053961584389263881381098869862042993858662768294676971865632259649027245".into(), + "1752142832257643043564515360000718468888861086573246457619082905919623770956".into(), + "14504506574384680785750882507533398260948836347427103366421836731538357314790".into(), + "18947994518078004413210940685748534988014581551965984303066903086446389273117".into(), + "8931855168578615387850254663107425567403115805663142600825724478150698936342".into(), + "10982092525200624040399870568387498905840578524691489797530932831401946309626".into(), + "4738907023206802373255186532236849256768509848242049657234258536668430260775".into(), + "10888145285628319545262252531874405309329869513560101920454793431198094714989".into(), + "4767721624212785367044047554655794533816937807005608600525762243335180089923".into(), + "4054394679973840378112083329204220302222586590732553688297938891619998137578".into(), + "15390471663419625573793381445844013245022413344196724396864223784781333233143".into(), + "690498740448849288977645176879593806019080276382495160049117613302192708860".into(), + "3326968907274045758110436838010900592335267522219473049427145975873344598768".into(), + "19461545874830130561487975864151403334363998126023624462211037468138940028328".into(), + "2255249425919459031033123095731665691066980364231819200773725596456576056043".into(), + "17139538647342063569964264947811360956712827863014723985947727876623459280539".into(), + "262834317961189780923232082352297808796511874872711860311746704570027370416".into(), + "17784213646586812350819691264737755884800773322574478474130308351003659945289".into(), + "9206479615073686723914227166450906925650471865894639492301222855979337534393".into(), + "5955379232184076713510750681781395826148323482009739159408415185190732125682".into(), + "16345512244217240951729073298135981012471478596479891072149124888060645303490".into(), + "20053701095030547796310908765544502773063879272854547881438596069907281565287".into(), + "11519146559536679602608982593432194283609736022486509747046459824035493513614".into(), + "10868663839942247532249591973192159672852196011910414460124452013501564199585".into(), + "12668355291693420029179738224611760713369106517542315102687346083105601320689".into(), + "4091011252347209563858280520339886760216002486858313383741839652119084430270".into(), + "11416347683590132388448480763970462739172261435271326798646502987745949753371".into(), + "4462763980178675172541782335457125059884067698347130082276003539434128058577".into(), + "21728891122467658477520865529973242372850367356840114983386033432316519759391".into(), + "9556106604731806817435679463077765288658189491612307664294729425381901530224".into(), + "5086982973132652080709554654284904229374030594786774699435814748257879554118".into(), + "2278505454992311041650060186856758463754878439802195559533882189615578260695".into(), + "16123495070352975934848591912315341924608875638550779884194576881433498909405".into(), + "13177225503435100563531015597038445430211235761527278782674200718068329833622".into(), + "11626932451843299545922103072142674578946680165802341368625957942237790110177".into(), + "8872973246419344365802198448930136062421718851114220299577394844231810068090".into(), + "11920016786052130191738519934437207519332291620474831138559948859328822621221".into(), + "2773753221970604083383541092979093729869734021029185810064937974430862835870".into(), + "1194583082499114147792330367943150006952486615245506995832323057119894886077".into(), + "15293312601348482070373672684782686300692505365845870624263228679370968807837".into(), + "2292156760291800990693425534213440357167359161992251338587906324724034592198".into(), + "20920049766730284147153707151387304988393631464951398563908410768221002588086".into(), + "3587899345078220957148828249287269521408604837648269936718299413697642586126".into(), + "5857527906708110948691023855516662527925762284342493618496858248142623857037".into(), + "18312267494676788897591109008609888960798722042916784593521762607767538629817".into(), + "18354455618287562133438807735729369657256664914390381320892039403006410339493".into(), + "18594037435499535688023807489676900345345731643180370940972090155512943637000".into(), + "6361231157299815359812386352981667048590510979947935475914610076041390336883".into(), + "6503045850716008738909204934356093641022474278658078426701342798380459107813".into(), + "15826908470360778431798326530563200301151807861414464213699967513881040969457".into(), + "913167165738148713876672473302437265273760468892350716109373788573860454641".into(), + "5163418960719047707254162004625467116036830361107107814320243058319914687515".into(), + "1852750695670141634014249062360862036043602867770163972096325792863710036947".into(), + "16164029969996795952250343426848596535809001568622155377829217918121790073916".into(), + "42291476149937488089591434144089904529405222471677684973768504172369443350".into(), + "1329340386229357940610579826659090359930768580941108555938139535621252899508".into(), + "14087936453397725507000489457270864434699508074557952952329368237400407748133".into(), + "11454917885298514922755456675259734718428103879515668717779418480236210705323".into(), + "17749966508430836878443008025013283275306943216523661550528505419303121693213".into(), + "16617298839486771009961431205770630163409905047728421465641369616889696635464".into(), + "5622873871440608391107520706189063847917690892897751818294742462879871297589".into(), + "13537715561706278379083684257583804567523085149672090320983273122424669242274".into(), + "12609629910090871112615676094781247031353826207267723991911250780907380059468".into(), + "11881347692420971451998583525696964339513193164613288356598017302547676912004".into(), + "3620434358220496198439193226313617496907852030586214671337652678218740406153".into(), + "16586456872124455799862826347901525401871594428044067424833235946565396779382".into(), + "19602593015746956165116919928045364895525104709835703557292833702385934632182".into(), + "2465427491077301663150648330772125184470808854603184374760649420983178107738".into(), + "12521323976712195518272978277895155774288446093713549157148428964880747896725".into(), + "361951232333654306694462853852464888974834703718677826403016226307188397185".into(), + "20048343816024297162848487251896481827914904696805156112188099141327595641104".into(), + "997638030405613623344188782838773314122493364653596616029491564227193697621".into(), + "10932007654988104622042938184134556963651043067553327861790671211490960094259".into(), + "47171599193060570819891696279547021610376047998583333086685382152080932821".into(), + "14669115378939104862697280661831896914139331878760241858539421915983017116504".into(), + "17868874372855679948405169936193924176514630305572838555185339642210810710203".into(), + "10178296575837129106771098084407669500326673901243393867574658658064222502028".into(), + "11497182727976130924559852428316615034304736115488257034951588831868596612725".into(), + "18847036158089242140209840241495282890278502700082131513222116906134183113862".into(), + "15514518995390761662346743876733004358408187550386554449789531199638765348953".into(), + "11474102901522012346251529527050392650125347221410246734211005177721289856415".into(), + "6612195415835443084676700243243174090072629504450965229103970796390091290688".into(), + "11572474094368358234669561324969692616275099241307798860733942350364532366113".into(), + "3855324911963410548772360326122995145790506408472649961229511965629894550308".into(), + "8802640003128749594245736338745752744580147773009816234644244502373660889677".into(), + "15676839305513015047736600040932186843826469281853634239081282896349443894145".into(), + "11124722103091011602185413968164672678635980457394627450785290630813993266691".into(), + "15087674670944618980358596427703842917302233637812357643695687556421910213028".into(), + "457555060782651847600218200815104907046227486293278645126081160142069992497".into(), + "5340353060455057701755599760342180989590806327490432497082435572367648024359".into(), + "3289809733259936118731355294329652879189400852472418229718273887860572748363".into(), + "1821386174933044868215348232606758690922944887434531299978498726875279584854".into(), + "17399236630582894158137572250502674699298844870791766041927951699287421557453".into(), + "16772722824042046255416248879357647708113647471330900665176012648038469814744".into(), + "331374066696126093678097185404981758791664151917354547180452342655690460271".into(), + "5482079579065945934120471179616600325379965440378196448353560421120276746028".into(), + "11861638874356162254375133266687016527365630872709665703116365332534843803431".into(), + "19751278476934230895840638614095718373810690662562196455711240141902305648888".into(), + "21017623330912840225230534280017695045717261514215145256795880310933667407841".into(), + "9692530233397639077769939390011937602190121885296235066426091743618448584134".into(), + "7914031992737639503490179289412369887137436318696390718781298556229610513180".into(), + "5046304088054212585035723354298412694927209198400753780585596829596665931980".into(), + "12735457541003664856181534137486291132119134214862779086936585300598349629287".into(), + "8144204472889944485922664106370529127382213990656088602566223875490414163362".into(), + "5526161442679804982165840590640681348630369336752481706044759543203459722566".into(), + "4665464612431440885211271075488840033628676516298384234452346107374012633528".into(), + "8451965709652752887539585363308640999657377914501438391781526068371105983117".into(), + "18990458193856163728406448194111866469438835810342179114684453609893347662421".into(), + "14602960690767985987882800342208585041637986661619503513589079723840776294824".into(), + "294650277854196485752526848096008214721988745350555311479128101695333774927".into(), + "9930361494944692931597991649915857642608730961125454734483697613693272941776".into(), + "17972565769620820679641368732920396905240248490243886868922250461473059009007".into(), + "11842743032528966560856860268344505094861546674985872961254820091273444880060".into(), + "2260251491209762630871337015316066081541066308706934094017641769176593121838".into(), + "21336986809148977544823484666876006147697590184356254785752148187171367963063".into(), + "15637234083283356311249527335446193685599985235080555266374006156231977517227".into(), + "7637477891046186378249227336975234440873859617986704147458186423096226771577".into(), + "10435340982947407847927678888878882924793449778165415690957335683641419176012".into(), + "21071574044063633264442120715854514033847137356154103023224485568597330648075".into(), + "20085745552872944745120547909310789275453780111307008151203836541147270866122".into(), + "2369255222739182549768488367357061329939116877812397072967912842660453854658".into(), + "3320710154094663715463854219978294133429318041799642537800174050047893035878".into(), + "2437552820481788519744888712380245016748276158860265401041560980354471184914".into(), + "6687580113987208531705167517979176727449238324356562435678492283111952291541".into(), + "13835828959457330678345759960614663723017667326485961761361157914420441377430".into(), + "1823843951353887792473925888956554516299304358703549730900495356152013614424".into(), + "18229384804985230011714562427207966412342158903455811854157839446374012856695".into(), + "4983049472282717134994110428470567601005310848076496400503178535459679438524".into(), + "2047051967230753763135778305592853785901616983565528680886843131244871631064".into(), + "17059505494771925862841990046823342770591010831955480339095397897088168520686".into(), + "5845823714127413134610517798305104245114036685335948729450609519089263487144".into(), + "19810252752845594230307894817800427820113926573704856490871938876757561680148".into(), + "20741340243371419379519807725035036726040739024854919427690724405113594586449".into(), + "17305746835229988220561638584011917989169628535378748397361130724475478785704".into(), + "16273970657972145440112726408308019138099820274904080726219726815138597785735".into(), + "4927605725478881247988642936459897069651251926499343645614635597380235002430".into(), + "4076655226193629464789557616268492785057128805549395585385432329518368497686".into(), + "18134767316186963456589895259454813585756254459227058992203617493951135964914".into(), + "20798436806114056077588608064161229365173163847083955162560624566238528904361".into(), + "8811900287453512972593412116532745098600991077158875340182906101108258578231".into(), + "1611466530857794066271650650204918615746591649578992581483080164777650137733".into(), + "19520757346022691586967284723955378385034675472244175822936613026597514818901".into(), + "8258287931139503595713718829279050060190693609290797346704848518381891359704".into(), + "13807143439443425137076128013998009581746894329904809421858222329599144124143".into(), + "2034200548964915935625429760202284220693125881760822084201315022529206424506".into(), + "20594375914400911567795140472107624446159181622166676420027082349633992663301".into(), + "17773828019575037451999782968066986504577459910353828196403976545023426528432".into(), + "10645884969014005687699860915213473815514464399964009808411811895545112650817".into(), + "3135829883501342672772973577699379927756997243617424917654928164800203666496".into(), + "21807676600134151299257078976418813484444183016737321278512745883771478511369".into(), + "14168063038909284721702678019083222059818438340503980617872573468231611140141".into(), + "19022539506931505257153342575586362988716958060936788031721967221986624233067".into(), + "919797128086310623571009200546035983274688764270933413427846490906074137487".into(), + "10651353481391913627770814216074873532920753703051075188645774021198634943682".into(), + "21601553598752750925049978818528421110707879819831249175157596816870100048288".into(), + "9544964974935674319204796617933096476421551193682156030394816088243121582636".into(), + "17113833205578964054057051521784698139661258340576694677296240312431808476286".into(), + "9889647672195559279745677506312894570402108521106900082889976819798270827735".into(), + "16028191999932520938901585234936954312994452706490572504997534210876573833649".into(), + "19224701772787524647172128751148104366752057774529591812815327738829591289117".into(), + "8065294760892477625290114823800398061529770004833832691347498933238361039736".into(), + "8385011404987806129246014860479833290406969218526611328586242951296814426438".into(), + "17626526623257098006524211054563886193098683828265081734658432468695686509315".into(), + "9760584950604786147191288118087660976225563461953070125437519145090832114537".into(), + "3282956645059793949082172795607530130101621492305193365378997603911833418463".into(), + "3788543541342252822847978185963388795825378340921321139695221828685330606335".into(), + "5728277403393912877393143174229934529937061751983246730506397742038949251701".into(), + "20532577038632159357383817240596922896191478140446876998140515404169184846609".into(), + "6138500779693128517529525961343097735306947649093633133232282430353593175172".into(), + "16387038830089541476468870208162294639575042754761542956218362331966004300870".into(), + "10184264376398708852688445921404363179240954227345322711923845040842165453208".into(), + "12576299651793170522912156101640799825541149618303513174146382191633847258859".into(), + "1340015400080181141720946234858756484323564628916867888877667239334982793481".into(), + "733959369856163480135680991009606990817015555938726628110611986599242143578".into(), + "11467033813562140192244869512537566463715027496952375979909160849747976831918".into(), + "4619667645046391146577435774790188488541561222783010406420406869960248783331".into(), + "58552761198135931030902257754896948615688045302818928845814661296914920622".into(), + "1199849881730507352706524556330002080538296688430736582840314007371442152147".into(), + "7124502590511184113044595527748024819132713282667933641439666531514739645089".into(), + "8623660134669459112474551498616256867375253975034970808437732784494772311361".into(), + "12655669439191191182341423414424342421477486764113555800095493091893820045534".into(), + "18432703875775002490514477493898870315422995231506677048275960580528644904682".into(), + "15467220287938881354678249472400749704814316816035426814619089032223454845193".into(), + "2851120240492392321044027263769720216640877441121430445737594074121655318176".into(), + "20519914249934881206828098454303256358482675671718589102535780334267934987941".into(), + "17275124961392392047135728713829752470490098022504524438869454049765356211723".into(), + "3323710067527231515807603961736782048796606296990840839366613937968342331886".into(), + "4468708240622802562056471128793253296493002925988003094771284205007772045098".into(), + "9006494818135081033869830730030943407240565201693254355620348420258773924028".into(), + "2624130417875598753127999576825019766166727976335690685433712946223008520912".into(), + "164131399455376615654870570697119442360078693174350746600132391198500093412".into(), + "14931668887432843139264972187415200544679230597820424081936926034478502874299".into(), + "1638753880783574431267395352024193675000113296497173968722590753809640941864".into(), + "15505380865926802396097545843811910443367233632805651511272732002583232431557".into(), + "17973744614207669251901495093091561913998272050499760575282030108740677066624".into(), + "6137688223696761009295745609563284204827706564566466060484103844265403078408".into(), + "14774243062532823236792831566222119634320864630838624098798648826842418775856".into(), + "15864970393171078370207775103899428499600152663946379517190945807315353544891".into(), + "19010063123357565300336230971672519561204810737546730911549311353159512986740".into(), + "12607162829921425080830052984475623157169603642577010527391007035133383807243".into(), + "17803108634879437217723652777640120469990779759700458421844361066182881628345".into(), + "10065874953507223318296028499872542865030107611981933577973812883589535269142".into(), + "3276471432535144390388324850641020151392959100393035635141206272558418581928".into(), + "7532054601401798035926415744768772852833516520318445183340725930886329458991".into(), + "18893822928119227829016544343228228897166113682019317256005502643243867377334".into(), + "15940597493253236451533839310728876441657428995464658827726295547815292644378".into(), + "4268009387843764409267791203070919313017052533005657826253994943184768120896".into(), + "21611251949238422413354051947529388972078300717392131751061464498329326474580".into(), + "12516447001729804412674006874184731098280474050775388553768469608793631490618".into(), + "49838549447142926741568525697026885045023997277705726329780325103507790978".into(), + "19763902910323896567698991616245963026306943100978479625077573937114135803058".into(), + "12029297973430627253212633299020402005457460023136429653800185001711727387314".into(), + "17676997725594777991384952086633589048516371093397126876621255518370680168503".into(), + "10567543371894667303450346380722020266352683222046730266924342174164712049360".into(), + "14583364850544999818712646438016435003942847076919084667364987497592599663937".into(), + "17348091487238815837308569582101875357715798351834275089190053280855958465528".into(), + "8743083090296259283603789316855921930102444739264013461469099560398359267240".into(), + "15114064505647935792598848256320570567717917317803629185764147361301698519005".into(), + "18332675991829764561879941291908436508530604635608341316693114747813051532006".into(), + "1757567731797951053080580099911774643896363235228742197150882457231133285549".into(), + "6526388717947413328592956348507481629843816325885832861915399601868279124246".into(), + ], + vec![ + "8243355230504186170667337521705529968548180153769821936979698914169521362326".into(), + "21549235422807751640146583237936799392598740234259041629069949854834009192195".into(), + "15309683586299089746803554818142261058154570215179112411063662706557055610156".into(), + "12007539402495575255755232938576927941514879725482443887151392201585760698040".into(), + "18793669376013417649313139054009540629720623019893420956495818743913188610515".into(), + "6637074549079529416739232814950531409613090469922787253991308038219905474403".into(), + "3042007484821627445120830225760006405192082634864137749621636257026891883326".into(), + "5337388510268581167254715112479133594089770138749507073603490761032513368106".into(), + "12325446798142239188409242319577957593792614990556679862642230477712636037037".into(), + "676789245562467194073706116744095779362669155912771165373940448756070927910".into(), + "5854747984773506278911353281567883752585612596682487681686710970786834920041".into(), + "11245406467967785626327694659468342056789182160059009120973665143197638081760".into(), + "10395601815816075071544509552592627172226369015806880764151195346316980080894".into(), + "6756096862783612163697577917108261850810460757753491809406999449771712474223".into(), + "1708595072322964393019739105130946639405776432058599259998973103484499438306".into(), + "2817817145890818701877539103826217929456570347854153048034669346981432211659".into(), + "20337270972708498869284875601749656006552838338471813066271573323209168221011".into(), + "19192338172842323468707146045612196807750411464817516820711948717057036544820".into(), + "17223253657227310295312621282100531845543865578630870272599545474783775759681".into(), + "15004735209586276209064505708625280228119288986650187909395010184201059452346".into(), + "3875652974956649356154345677088455126258183810851242537013757276075769588050".into(), + "10514447960615206081458524578173743817818597124482828867666984705327684376752".into(), + "2087647010835075851760610474040959236825470174942075295716631067964093542910".into(), + "5927163251920754154392384551305623830535034440727310604898855074616515892551".into(), + "20585333621997037505291454298836355589763292536744926081563336065939121006537".into(), + "19320876518201905459682928158170419256739531666800973485138890064423348282196".into(), + "15942638804716709831210239594904570403189415026144938623559274984027906868220".into(), + "11197022744936474661934096628367688581641778841814728682794507017845346201383".into(), + "11034020922250561671038205476395109731446686553549026383358725302157324264144".into(), + "7574933006942933995255906769787776608010920618615581322603847524789684181970".into(), + "10061361506744906780155460423367413099657465765582917482575074226383566926764".into(), + "18611343221859570540963418999548488653944851224739716224660835306206658947980".into(), + "17094203924957299390365889251598099482992645049968199405515681968938743421467".into(), + "9407145832890449495071969940777105644547801064593141904558463573167881762713".into(), + "10921438560879150587765515492087524756046482460218342400194862909363870270743".into(), + "15101279960899220452674629307354995123411280418550386595937683027146194547144".into(), + "1872357133681596467751878560069114718371273548294363719900935160833598069645".into(), + "15505500304018853111989216259257978796595506623204851206292254759641600763191".into(), + "2079667978353221447444850850900204451820443725835104896018664141845782871343".into(), + "2852655320672908960411014862634757863509253400797831983637863741066632490909".into(), + "2702824031197306101989338159138451445088523866133498139857862801497066633794".into(), + "14553308731276493692643101846551382187575566516925133957384350697980935154102".into(), + "4314969815396483242407853639218064117498232660761075778657880116870422414637".into(), + "20236724297078811959918602376319440958076910292454596856154100774072250182183".into(), + "6360017115980704736383763605019264589498600998515606807745670287390050560160".into(), + "20856970531105411628054833058646203890148287930330473527735908484791842390307".into(), + "17691356258507144960616314395885779533907781694329041597441621553108536658757".into(), + "4464167934150673174817562382299722091160711333547138388803048452674668158635".into(), + "11538922347277268848344412167140306567742076984016453903533772667841006045703".into(), + "15558861252260038101730449864896864763293561339637017072015859069059083288561".into(), + "931980552683520059135814229579184511049009637966018180567726214946979768011".into(), + "12746506550979326220422215987591117730943427023997792332255149062957909690818".into(), + "16416138987000536018990311324687201169959549714116951891693452597169869821726".into(), + "7473835750915837381583185047008243788613524206396316652305987269933344653773".into(), + "21223994082372071324452834147900730753626104062167370333103771844983134656961".into(), + "11102363694946721470818933128034696027504133564649607436252022322296041603786".into(), + "2666835000155694643357391634256423691785613060199379949509682292216642706081".into(), + "16883033667413528795407641102416904598130659502290474063092941543309042023190".into(), + "13093053604456598783294628038129487761924241298889312497497820946915331319389".into(), + "7426349812936697309541457521193139970366533826612714195359894150484429907425".into(), + "5243217285990182677741567384304278362485372018078770234262925321063263504918".into(), + "21185490040917275396474067542756068684704036418473170810170344320388557093876".into(), + "16181135763579884029508432324330748636846464150219757303321560798898398598349".into(), + "18088358880437096005757355821526785623101357556483672471222924931365890201571".into(), + "20418860027198053484245336569800730261127301261293595190270103940460998981236".into(), + "2058948081811170389115771489993053947061173620273801887242248130631460165879".into(), + "6353796008567532863300373986154930294334380098977007704532496889557690195858".into(), + "15854609649070278722833415779491666201355987522519101725393408435189057056690".into(), + "1355942327518086746604287131396672941922424788908995789539897301592998007690".into(), + "10194046920666955610804398522181498854525794643476895032285888778350918459761".into(), + "18342608728256650520630397534564293474806178807929639999068140223470256007117".into(), + "16101948218093381908101491223075947943147313203969904451859930796280152622017".into(), + "9866645853452683082481412876547916795343134459981103407915522925093474319332".into(), + "9309485422719740772955698359258466728180120624442685713365406080485336040166".into(), + "5201701081505060757054562398073722930344229781365241858092054974705598137660".into(), + "5279555243870694216927790669819597822350327573071817682265773244733785382064".into(), + "10661662716572743893824841881707597899963881485303936548294117975770384660590".into(), + "4306964326426793675768869124893413588264762573088622132302954501394542576141".into(), + "19945975928045383298785833694292459276727208605892865429301546022994613804030".into(), + "5037834331249812829239656466783521330249138768989720606017856991559732121456".into(), + "20693877087308232030611148201802513236570270737947270986743265610517665094074".into(), + "17748932969923719316564673051784340920943155490113289807023660243301385585070".into(), + "16950307665556055391386715682532553772527550247031548278958142572490582126842".into(), + "15034211391483347494286112687349366897258989065045859280146461213731663274520".into(), + "3455096385235320554100221104677124747996171720170690637998043454239897385610".into(), + "11220329458242704347549150795173830262585759464331372299692251819012138352257".into(), + "8230076319752658879891285909687940775399748755759819661970430769188439691274".into(), + "4178690445391578185009939705412120505162313641744671740163024993195883735198".into(), + "18632680236376151061913536149173846032710756800956417249233907621575802688710".into(), + "14168747730472612819827430620596085566004981811676505988180237018638188025380".into(), + "16777617016129912124437138351698263064579177499617525409625791377061066895460".into(), + "403267570119386144603206457308168792379980670187570608148634410971295877610".into(), + "11045890302538505532103216886575539246473207034538532950483165910580782953337".into(), + "2632893274667647784827087132221744991131294771819888858265016332574437797556".into(), + "14022461303364013571172470728150898521630042996798160127819093871974124417229".into(), + "18349129573612583311962846403448135938849737390546876598640066736462315682295".into(), + "8009723611300112743690923532773238474616291315457276539919568488041436720507".into(), + "3287586297388209299132232426281031982329712892122181769502106059441842217623".into(), + "19893256464101780566218598404932657965361824655069879954668551189408491121155".into(), + "21779954643920608321663779655887581582907923850271820082121309309571440586162".into(), + "13938145028737822338330333388496944993576078307754676998341398757402576278690".into(), + "17280605833933949866452995551396279974325968699794264573823990818913515933775".into(), + "11562775307500290654949270847967546133812416593099094805234457839659652146289".into(), + "21556021192476590536800970202944195471695121915357500612310904064652863447972".into(), + "17407055226077297021071802288772735837293135175537846248261973015744713174949".into(), + "21295838064085671525042198277220548723525913660103018392096215316189390548013".into(), + "14589917958236435754986191512564058641868109230240077937707647376289105324812".into(), + "4538073055458854134606640263494592220617270326115451287834630189270577020111".into(), + "21247609438242282269742265796811514090579388884916478939008977411932487423659".into(), + "19263560475610984724826226948356735903574936974192558145730920786586162783055".into(), + "1898614508331499418660051276594019416852890004788354240344418815409520758722".into(), + "13346547977920686435662774643991891597826323722140876186086635239306340843003".into(), + "12144969177194297999321084025481801838621405926243412487948189180755523714531".into(), + "11624156909934489978766768065107924627236090741698411458481638802308500352917".into(), + "8674349037900011131899280296161700067911742760618648557038290076406601619864".into(), + "18627233188669469962636721109716646416813512041955577645627776298400086440228".into(), + "1153719160094308748956884656041023320488424966635003188538565876464091909764".into(), + "8000003066081501211900754070779689975656073731442793160620896624291841806771".into(), + "12069801117560082050163959286673266840809976769131514316118288648293224324822".into(), + "11694828863372498882861202648883355759680038037706633938668096525787115759720".into(), + "1181495201505177954430275085371953511604847831716865494220845031383860562941".into(), + "18321980275956746302814628602546438645691886543647725888694024551609678639266".into(), + "2785661975937033521551267460848061931764727388015171856456622007438303671899".into(), + "15557886094116287182932984983441793820379366058597052543066101158081817575352".into(), + "175179830261452669822497364983291141568331314582563701393865403724263011876".into(), + "10455128373814266139918350629083299308526836847946708764631040462916637941146".into(), + "12622681406523708498691044494295298210175441851465578469593208754136900020434".into(), + "9624138424345877000077746656879336097173254842107184716328214933320809030543".into(), + "11726383465426411877912203592949370178096897707629953853811352568008881233112".into(), + "17566146584557385507728086844334319515338136183689530813551207417981719751958".into(), + "18423839150858891406289385710861955437811779173242111498197433255650436048047".into(), + "17408376662161624435555256564084894291578222902661202310977717110546842356960".into(), + "20995943422377609225953642092578140203148330329113983394181012996247925741957".into(), + "10409490873284794620245703460832015892256721643100501421596423100640512505920".into(), + "15047062105747285153444463303020356100177963702386173227676803770571846532695".into(), + "4535940688608096040988822900684697329863791065464226849059470519882399535780".into(), + "18980357680792173392910397806033731294240363676914829395702138582894418363978".into(), + "16468042735091009392571235146440392007609078458297170996132218787642722263238".into(), + "1869769403621899262774247370472546961521039203681166934356431996537822108263".into(), + "6151829532330885020831674048300360431343535966534922988242884341920915237665".into(), + "14373964388615044752046531046884609884388869283450342961030080770253954449754".into(), + "21429869771065858399481388829822721985084474326196139156050788103070270663923".into(), + "11836916222341149344359827526882466618136359738495035945807998286429671739008".into(), + "4542193081188277792793758113018430324598765345700596639963408884670534634317".into(), + "17262340128494663310404052919129368521415818617921877469042393034218456907650".into(), + "11614110585474201606235056157412783071151951301104822431509283035322273244217".into(), + "17241248261774133453753660970137875514052923171943595080766050681996607133130".into(), + "2990875140768570679733810173464987023133165559726680992079139149034178002777".into(), + "10032389096385585741539206260012253444831624820404318451026478423856181568200".into(), + "8391217416130739565515338215591963109158836617019021044489286448654465296819".into(), + "8553700889274799411012667201578367398970695661169430162294018618925895640041".into(), + "13529692770771168133213371031275281478756443444824139121847596546264553079152".into(), + "14478949636372928879378459122088894160202116364833386541382488835123981766413".into(), + "18528743543311452855194545818079449921167163839226390851954136986727320245809".into(), + "11724222260540829258562889360923785293478512718704276634048783603461995522859".into(), + "2652532822068043785753514309321715043229885635900630208154874285707479247265".into(), + "16473666207635815797882774885364997250503755116232911726426811919269547851975".into(), + "12436631741803099512327160776479880302093882812091908650798222524569929954222".into(), + "13061081443094122428989571162147084312340276850316867585582410062467362267361".into(), + "20909566607465067204267258789556187669343825005173558971220332255443231196363".into(), + "14278016202378252898173761523743422243750790190417896338147106476354187349947".into(), + "7703701752136585609667768350038563449121231460368808945757767724712186009894".into(), + "1622258312841010773225479468430896972269503924285598181547410615000034107894".into(), + "4706114868510775588142857635375822293570353199661120256611528287780303504954".into(), + "12723022498690150801900112713057006417552064300221766812928489357200260312668".into(), + "7736508633931646965699972944684083339925061856252811104228904321699984469949".into(), + "195095354858363944780141950724441876473553677166595890451203685104276178612".into(), + "18877614091447727762374351623731936445361116363480970639310200637662433378180".into(), + "17239262588506530491210045452642505719938421789517734104955853192075731537629".into(), + "3391556611912995522919492308422471958888145521362922265487749943660431330300".into(), + "10164629656754294522862462407441648133619259920942013682702008716587122474446".into(), + "11939828733425435518898229234599966533928666730047925120030711579782543312731".into(), + "17335155958861138542643885799966192412363788951639890938680530110842555336617".into(), + "21068414996957890621467676209673805582866493104159841584377567318112060433438".into(), + "18041291613104743972430309067462668732698702146146761776321539150844598296986".into(), + "6149130772490689572076747194977244577047643214871016443290724757756394340290".into(), + "12105848363324940274456322072887282559016226587661485273111872063034847034485".into(), + "5683957548001811989600472365740829603387405501208071642225953069881259762607".into(), + "16529542077365261070047716411124689196456625611983373158922227651721798753876".into(), + "11961524596519782767188645738887896272947446382672325012202336646508449392990".into(), + "9785728068011868312995387469680578201705397880590293454099364001157116688561".into(), + "18127416268588083447440821307938591826251677223119815897950307944959875167560".into(), + "19296461637807972438220899702591874518336722552660488565818484435311224286288".into(), + "6801016831512114134395242293457679538495311188529990156831889204433183626116".into(), + "2964298470426582070507861407971247200639242211740381994158541687335361446525".into(), + "13485975887078791259342768620261671076376983307468484850600890777864999230190".into(), + "18842264035089067687391583729082424222425351385494040849910540441253540345719".into(), + "14703642210510851071131854548671393020078600676544458548174965732036621712435".into(), + "21220214849253889952179905879367949668848598115028365535238742829171770487419".into(), + "11808561815315084933226034934054773302447242219261466208644893422841430468026".into(), + "13540888692913543742580940929469376532537583430034252053023468103862294761259".into(), + "7244161097354558003276348625436123965060461415149286453943040900234287411785".into(), + "14838699086047571226987010390426316539929576717533827724866261274778253262656".into(), + "14556703155521968503536618488028548581329555701042498979115582733446728182407".into(), + "7681623302896593715513288894378158777679657507901023568046253058158573848701".into(), + "1088441387469941348668229287331864702951247349577784177659963097331109780661".into(), + "7314603916265509104428110912296267885635061026393352039011815022900719549691".into(), + "3986211915826218802854255636104488183733664187834078111248006041750140814882".into(), + "7773946401984571616670752866609685859292708427659817737120107917606152933392".into(), + "2842014599902358831415178364343115068084073955515903534808862171830738904933".into(), + "5310724334723991338015239276468023426385678184604207589409781216959654582406".into(), + "5255222348968955358505450804240823699077014235887887249383824524518164498567".into(), + "4683270496545943333741165516340250527555279356319043788098737100323469078711".into(), + "1419863943011284607504318632953959861647793372073243840131919334395882404459".into(), + "7983638904317557271319561780754076927110887040374328063199742162092282580125".into(), + "5569432847705373609838086039153225563020182698189928344759413994203981320990".into(), + "15459233133041758499623402905899885787129812358908703405750502906067055055230".into(), + "13557004098047782158753673078158469379829777184696159361573537670440394932233".into(), + "15455882302725774286899673141535924396516348007554186719344822187820635072053".into(), + "3420919058826876625284567898132572990967515410265578892047210512917031439632".into(), + "20100418454140979684745740106982178755085746706837715848777042819378494283102".into(), + "2569258507332519764813672456351707773863376375715947817185409500202699032309".into(), + "11051426796304102496144764766958179671506736496976882366028801902480842422589".into(), + "12740229748287653735988491742372785228070141556372656548689214318469788908817".into(), + "21628842595664718258888324339774974922449098458375293925060310284267692457557".into(), + "16339231976272978519029290439531768093693541721039081313180796119705575069472".into(), + "40124736742096746520902512885311967045111742860721554225254094895613700655".into(), + "17732965892472841235257958105891466451086090480423956940377743815006013439".into(), + "21822629194074446176794925064792912534191501981075390813302606875002422233533".into(), + "9308214945046921143097017249780654286051601646816113552080893008307002107495".into(), + "1407926751839535775233537792971129618756456590720440342541085713782189375466".into(), + "5640645423977029900985251540406734874840031539109774937559862819450972865688".into(), + "5033216407501194252797695593441325021622991729008118693554186469034086370061".into(), + "8067057037475400447259522316648004416684453970851364075976857314405950145375".into(), + "3763719773038467529952189678629891209905984306908045328296798459182240539135".into(), + "16939797418368521863388331657892541744299855742774206972703171911218723184714".into(), + "4830944198856568835319759101429165879092462296316662230100861015921313890231".into(), + "12704214658232136513943612645116991664417275945120192627735782298715562058820".into(), + "9273823420095008025667777982828688153052061387261780450903573585273931011552".into(), + "11055274871946976331353174512200687536982312509623944578515862663278819898965".into(), + "6608499500253253446996042326570359354182967780655057286059057541317584758989".into(), + "20888058022129906086941050692798413401844596394165346138911969309287247738108".into(), + "13297667979268130800823342819300433555314639138313483863899090834749801969571".into(), + "18968104066692458124571065270953767119743779337036553042450471941512165236867".into(), + "14932841303199490878640323744926137685749952622800747995690439854118498001885".into(), + "6250599214474930878673138968631643032807502364864165001640712550360147900771".into(), + "13872044280192246670253542029636668414586465840988190477111017540404431909403".into(), + "81456119668307937036914780206985985650137679027930766352442712034886058018".into(), + "8178364156193615628946078892680068624209694278864784660439209878556857933585".into(), + "20847565685305938921688196081711559611104247746032524045765048360946563554616".into(), + "14790603163347071870110696142274029411377352843070075577069234486581346354229".into(), + "18977464663780407707262531952390299277523056655145169930121579582916387871374".into(), + "16780630803676794749613238124686604459373604071531057035207376612438682381040".into(), + "20186476042367781999034353334494913683828163385175556939730585228743410724033".into(), + "6782638209588187356802454014110236225878206067794807253486060610876934918759".into(), + "8993456778572039939715813797180666624819850516232234360679317411311388323391".into(), + "19966302498904269727099815984264954717659138861990152509516897188319443441697".into(), + "20169703794592063233917650314404110898564218327366603108408586484609331826027".into(), + "5979829627203584558315118820578826847995466683728103070319484562170838879477".into(), + "8237679343008214539352062545936737645555361114339038346011678993504862443129".into(), + "12382432100828502258569798167004899872248210099869176340581848176730802349663".into(), + "1568185664985590267262857882936657784210740515169196983171026814738347336756".into(), + "21214766447038120613598232832812136678657988502205964335817205381807920739938".into(), + "7692941991237742474520327457310452870153482370889548010226143053981890424652".into(), + "13595129445265049664221406027681079958478209116108739005508499004805469917071".into(), + "19188096071580221579092496028987371780642557049389322053081699235155567772173".into(), + "17975673380464001374676034638564230054429981676012676440863525293845130019904".into(), + "20841685157342026757711329464299804445471940020955209397956987009823404283299".into(), + "7510778644672212989684926383821874729073504800968951172295535413714975603558".into(), + "5412964648109092367425127656145675316528154462488440576988541278054587052058".into(), + "6998001450950528857399821530729656471745472711969582871968416561472553420135".into(), + "10017795190513370580285083759517584035694996563220913850722002288744022757377".into(), + "12113185651597474067026664715619946415749981707739597619454641751791169267554".into(), + "20451540737363571466111039734160615184627155382583098695879349204357410296631".into(), + "729116950403569953818905038668361626861855541652418271170712441039707291924".into(), + "6874571610670154627346562968411422088198077609945741147515101915358108207688".into(), + "20307824547105117373454598908217917152093200208838326389260620574762152675045".into(), + "8758875530447210792904496135011086289851932865540018278850670496425499052683".into(), + "13224694410602002105805224454797207933944742532123981533211431845662395381395".into(), + "6621493224766717216701548708726891168784911176896760330321592836065310482866".into(), + "13937858022779991611039558948054774910543950212969141252259896915615778617893".into(), + "4917806030251482092362529677296731621677399228082641707762616055246746126061".into(), + "16304922224312728276104330461175394847795848175925462853738047204383447573035".into(), + "16678452722472429203861326329044632626530032631343862086351886162579978046420".into(), + "9974691111613144697061424119079539196535411918411684404824080439336446439564".into(), + "12391128852318795781829794456501239823062804741032268163807689059014957151322".into(), + "16376931186038869228971542812469753097050036606517944132293138523631153279825".into(), + "3057841358487505418761470758562979965285993261118087156094367416201750095404".into(), + "15045409518037090814105826994439679855639635253710791541219370329682069820225".into(), + "13442376736433669968016223589180307683361433436806777011753497283272674012644".into(), + "18917174176736242961299708438032963296686220808211170958894252981698475343631".into(), + "11380920704380401611525239094209208940853859054744619020167150893676619275400".into(), + "5399632748693319676480270098239871368958944610827825094400876104909425716392".into(), + "3072779406768337118240884091792704214322792415195488652476136252175179362880".into(), + "8351873470285292321562674159922105545256148886389216816367528787141186556758".into(), + "19039526722628732399365091326361517675801947890934047817293511021151913744591".into(), + "11316453563295765895775061205389385485172841919365628835333993250531664655988".into(), + "7850755275953939062184858524678116551304016605992491147837939252676680785208".into(), + "189663666172994057560830062107872734380479327839628938168402275701561917176".into(), + "8944554955574110171273295960753608410178793391130829960067372967633462961614".into(), + "7116498249918759493875054905542634690892118438594298685578805598675410965669".into(), + "2535963611074434631003149876163530430931993688129878286594756194015465278460".into(), + "18022460558081751594574692271414706303627866472796139479944146908393139741182".into(), + "15341193598946540230880135952221211503846552166425406354080863978843527894671".into(), + "2942431717153385426545606490874257811230086292797817271859433296359160259239".into(), + "3009774438756820489964746831334449123894740822794580986556997529296717581423".into(), + "9496138301121689616049759054935646143502980987880350156990306735995260671175".into(), + "4076156724842725224174300000468119057699244699381290980710548119313376968129".into(), + "20301500572584246879220468905731058339249778940966192891128325027181404226629".into(), + "12240449395531309263037726882974869058539543342019721791945417590157321444565".into(), + "2734576041547526732946886809654954568832411068107541730145912482251139322538".into(), + "1913611111144137178181099357504813610426696502807761974432419767623037547574".into(), + "8323981703091520786969788588517080546120036429535328021157459160571413370125".into(), + "17608089795804665912003122420873117027406690592641558991713120617999818930151".into(), + "17954961401611739290579723858653246962839079599354059880628870682426849304674".into(), + "7693642591048722104105715300765742636898670019493041402551952316778508785882".into(), + "10925165536949195683545612102300879902373347522535838874708839717193999335745".into(), + "16740598974035404805544189925980303793846400946043080633235004418045311113846".into(), + "3028458114292500648266975052798389647613432243149006395166123161184170940972".into(), + "2817600861932061603203157785548222970685465773360278995551965365313604217882".into(), + "2811366666795973435332404603090484498270752802044239619104866535127344245139".into(), + "6901007103297959557257110184636027233977945890205420866896244199105220459744".into(), + "6811040256124961160848956238308470640308462502755753004833080999365205628787".into(), + "846642049586630199735666112786431409696508103735494916428842550432654381594".into(), + "13061166881718302681365231291832588791959186056326831853549555763101859584396".into(), + "1581547457654855644173875819143310956457964952802128135344084991507959176621".into(), + "12591698412731075291488515328885878994038884715020576113812619060374399968487".into(), + "7129047166046749599109058206849766841261983329246180789653876287940952140294".into(), + "17780920041966559015242418384239510699940753783778307759603993814380170147815".into(), + "11411967002648206460094819913767451172535988461576286592244752756526683869398".into(), + "6535147980143805768211908880661065989475773196469834562468932004056012068981".into(), + "12872366293792794368642323198969017581196463071340612957009439105182673573396".into(), + "3845096876544992085668616039795853840768469571100517631039776002796484609549".into(), + "20386025860348257305841141103130861239832870083066852913792413739711579490278".into(), + "5663975388273723452136125938377376330824298621841190787892884430812699456136".into(), + "20880523335705106555101009571713688438858731841737802690910851430800496104934".into(), + "8664815262171336902475127109386834836220742848950659183106085559300961747316".into(), + "15212672296023611959246835252860546019670000046804751249547303425954183847429".into(), + "3786255974807528210793957400325837912933369979823637013145025357556219775102".into(), + "19646410587152058982763388053845872310164493339475512721275474101828150077273".into(), + "14407426259630290801648546162995549804322572985407158009259933675410180400077".into(), + "1275955073103101917295562169849127375209112030395179332033340866715396722452".into(), + "5487750760448101899937260261898752719887276580825994742322208269609306618405".into(), + "12414079753210256499611439235670285717945909010061941159696368398137523291140".into(), + "18058271753030912252347026705895506604519018890772902865355002646910918153759".into(), + "13935235821735626611156505080089322797654275868806802361406549798199236177528".into(), + "17110498079878546324718511787669387410942622969712445909354000807236690314957".into(), + "10687508266469903792000405420136150569946636272800228999781195239976105560612".into(), + "1277956894120355360649091990517188151791867400124079104247693321263057601099".into(), + "929982009519538400155920125117423265869657236620766216139182914925009802954".into(), + "16559970949358997473575123467518158994842000800881347427572300986319432656507".into(), + "863852544580033885106607226598354103099120172650200980695458006092725115354".into(), + "436810575313416269983882563851323926836428928449351162094565391723605483516".into(), + "6334913013691170767138698286357556285297887475783792365865857018173994149486".into(), + "17785859069146472999908840832788077051672090890508101583397157534162626183973".into(), + "427206014337914391283601765560115825767253196347193816620589108299037926541".into(), + "15115704735938262072587983952645382098893412471333885175144579020987265065203".into(), + "12017969315449748476118643575203596675122272214009056004034938899095907760206".into(), + "20642434407226804845623813766397536183962927868804716012482833199686414302852".into(), + "18982318327848493301474677819747807686491978396022748137991684529478469330097".into(), + "2306193794828709014215315860179466106408084703631347012188232489780230095671".into(), + "7060813397820173935956757571314686808083877731722252822508055423697679476893".into(), + "9925864312610988474999359617458205534034473691089101964213562993662824159034".into(), + "14036238569106986370932971272638702550236692459418895654245682921654874601312".into(), + "9509048813859143088347263336607686057099400727479311504780670742158653486206".into(), + "6842166521132564137619008158396211111980991013087076743268157882198576269675".into(), + "10217353423046013950417213172971567565900229914457220187215408404202554351836".into(), + "18220384419265532097596052952017594673237799959023133602933674050572298730193".into(), + "17866822945198657177461453619458294532377313634196332518543246556611008452933".into(), + "17694368679979949511817467967015330546905282492241200905890171992458134240678".into(), + "18971922685739566979638356009544944454629162680819328093994329160719843056737".into(), + "18684937612086669383439812199377945074448160740155966772829350355651237261795".into(), + "9235876281667970051504588287667786944160228843888838710239865727309603061015".into(), + "6187574163551283282357553100017400574873868151705871779659681332774938473442".into(), + "17196369096305464930639002419417036905613312721767481044644254878990952814786".into(), + "18296927216321111202881056198300973553112302777685079899199090840516364581791".into(), + "4983948188027170589078739023086929105628955321978589464920358286161528573448".into(), + "2276814237931645487686771259585160667452008745791625290365802841496721618760".into(), + "4138273157833414032755498052453436990872835066620446328921138739885868998379".into(), + "5835580830979414828575054128735121537583042482361311845838347096674448689116".into(), + "20992630219061340843601881100837482710979119542034786928296223633950908472388".into(), + "1118381353525339785976839119511758587763620520383755136959051018516094253090".into(), + "10337002023922138844951367775712178432524190386722995225923120494344904079950".into(), + "9765947418137225404722546740514250763898752374389411503005283184253024586058".into(), + "15411836962046751164622748177831913963909013265942110958658714173394711125370".into(), + "20722527012138131360820192152290968950993396481440050289358737370268218859591".into(), + "16585853587281811014582898583977502965045639444130273779047322749735299560207".into(), + "21436098743421172924014781240823435281025352300035264733201366114473419058727".into(), + "14178112462860881459540462916598447735177675761773338824394753907217898488960".into(), + "2590560710846804342662010467713568407285290476715663333366063002353018991264".into(), + "17949223181156469858379065899254284317305309247290121304422294912030586532673".into(), + "6940063127036366626640075420306454154706369567406835284901717013872681276911".into(), + "13212339415583029091219180722363760875223983190396769244985733901171214077679".into(), + "11143838426689049623360248250302972103117784521940658207527698432687552942591".into(), + "4994693363062895106345077091869420711664571716019971952890352464184561249569".into(), + "7785839099197795033948112451740381108555553042322704038905686323540025631473".into(), + "15291655295654923849266753004503491258117644584862711291502217292211074445996".into(), + "18223946690101945712849081159295298164630378278313069852577349403051751559726".into(), + "13247893325056509281811135293440873471348664328435966021736203439379360560346".into(), + "1838627965154116499570588511051176331708387980121591719463695143475045130831".into(), + "21746931323535899361372833028120884537569529325326959379977185108159655128847".into(), + "1569229799996373000993208676467175871896208509249271061977636872731081653113".into(), + "18668959729045139805375896352501526759923123936419773886979446262254907152787".into(), + "12698285530824454564359053510831159718450594302921296519937334733529589738160".into(), + "5743752602883180080321224936560739109224279187008023590149271256478879997507".into(), + "17615461436426765950762679333452659818080751337498512367037395397687644820677".into(), + "4379963027402443949761342437016192165148025657715626365315450970388283739261".into(), + "12622442863880120105122485141053297017921305018805552070109568547893924027508".into(), + "16493349884995741255319414030015325273883108492981717376626952633010860098410".into(), + "11501183900713163689133184470477728399861217340901493951105967658399341986313".into(), + "13184464903575565740074003127437693743650101614906307232173855163739473476900".into(), + "19056993236227362680720448341933549082689888775458266843506880469982452347227".into(), + "1180947252747369471066257076205537751320494098262241412291924855089764608729".into(), + "16229532924404554580195616835338949126663348103713418556119694233568376894947".into(), + "8604714607572995451336310555882946070542334844212691610961393592348706930493".into(), + "8362594100280133221998296898045505539071433915735634439526614339277300552370".into(), + "16399159148365956463951582514857891684943332179297226423628752792536028483990".into(), + "20791958918883897879651946680726738927333774947616022833294686415482396438838".into(), + "6976099533465307077876553477341301102578695004868981952387720840685240842560".into(), + "17588607896443047770053818219711270035985826074286753981361920802895326076124".into(), + "12865981806811655044812914486873432317316688987331760480657262748139002813688".into(), + "19080259696546964979932036247707282742365340353585423017939782931928015046575".into(), + "5475353703257038456872747308072401784844227202792527428899399083236860900298".into(), + "699444932025038530835460727165156424336147795146205258896894678525124927461".into(), + "15695622674480818777943366659102932349783785381339274197766151422625765388038".into(), + "7644428489984569999599080644830401450294253782967784792584750934960812468382".into(), + "2484044190398385977417569061356693291812041338880061938702052957819048506706".into(), + "8456986467797277421685766156179980502998860530369856189405630837033584471075".into(), + "5054041625001826317568038929780665383894838531896986763764007995985738029810".into(), + "5197336058480822437408118036219119090707158130910220019747427914262297331861".into(), + "8896147437242770809876821567936215621570430903276974181159659855796295866923".into(), + "20755757167342693300106178757642141909843395817794855978028122598254488316281".into(), + "12495257799325917448205113238508489684392516282807104246531380538192500498286".into(), + "17639970982424592615983334078785592256655637539816187733799215839326807071148".into(), + "8140016957188286078776165555436655378303814378750387793587919949009492167586".into(), + "17209468066776420206923060639618147772644663380208004030591040036263548572020".into(), + "2619409586309117922582791327977378099828554504012201484641253637770276078843".into(), + "11172679254412598275301264634812740710430873755458899712228629497147611473029".into(), + "16829502099778629987235691213955928527920624415791356237580609633148661633897".into(), + "592799060717298365629187138482067858694007427100574367745567028165989185342".into(), + "16864381084532235865281462338072964457337415344658720676113860956416999505572".into(), + "1015589663070446561434523645329239389344944669662180065723984179503017360337".into(), + "9982212112174542265411457778485410853904388759147308861218634697975431894510".into(), + "5412525702631618381358272227447367851318305617863423359948039591381065713581".into(), + "9852930575259000100332996271562617389630146990442517175422889296173516799181".into(), + "6036993105785310658467845672504384047591296265363803946714632979523201713762".into(), + "1821500632172143873156399122734194851200445368324858351038486833883177057468".into(), + "21556520116213603298246786137688925835788594639953568860110645708136881336676".into(), + "658318860971707056155247027603536846915894897192791739866840963356575472681".into(), + "602842622617647573132938965729563329852165494525296971607175031334298950242".into(), + "1151063223719891516862415316972915766442753873652837551132768558136109394634".into(), + "20030054542089253165409106868864476953251573918915762537158006593968012247497".into(), + "14455078111822464502989472874268580626098857184523941794725425258923962713053".into(), + "1699191450188970110166570608380346465689006650580298122024202987580198200132".into(), + "13971136504849280501801880342723497383580392506287195375689019810750613223527".into(), + "11259011415071078991947983706483998982146186263873384729739331890304233635860".into(), + "17741270384736018529047001790810396141344433078911295725171243367964019815741".into(), + "3617456068852846022110280599700245470402025130645759911795429861830057016581".into(), + "18773989857774369564707484486703863617112883499664601804221477949481034222590".into(), + "391101570414854801618801587626783162239406618115954162053108159404294160435".into(), + "3752824438659815340558915518196975380567589032517034180452547083690665271869".into(), + "13652227089592801810376789544861979384538590096633526007583054323554301421745".into(), + "5753030785259259818058977992956569985665739253964735992489420513570911607".into(), + "12794765444364718066463627091127875266371595037234762762560519184694440318642".into(), + "1844165267423966444579133456200541636533189889959706801468771335509321515822".into(), + "799352162562582415493264759184613437140226428304061991778193411771388762097".into(), + "15915114786946818157476898276501926276831197920612814619300062353559927906953".into(), + "13041871949144831370743756131359537126101784549008553888408794912277392285626".into(), + "1684702427149441531010110315726002248751792272226034774456204740385384491604".into(), + "10195318610969070608511028432066597876456281143783329459466964443360549551082".into(), + "13714193389971576085579160116206487363436474313560046541969781285568217247624".into(), + "12202470771012770210445954644081270058473831351768121852596394422757629850892".into(), + "7784616613742667796197638965440313242748565680231200921682296807888993222090".into(), + "18581613859576442652033888735999982405110741068271804741467526764394720805037".into(), + "14828223806255884089537896775456938290494683211666564494946175120085694803958".into(), + "6191868112332934762674478056112840408041237177775248347690069948259811627101".into(), + "6055199518589075551800066499277675747934144570099354689629636497613775458486".into(), + "20043219892592698889412649805669712950039510114250762278667968995416842502234".into(), + "10591576812697540586115991527347511638405122244793393962099090930538459086772".into(), + "8146910292072979142616688207315340017602882692938548874592904341871514175303".into(), + "15451576003386544225828312996072681331940167554848966592330715947662789205180".into(), + "21156998090948310800651324456525534600543417534335507361948830316109451323115".into(), + "21421497039083336739241851024868234958744697872115637345287618993148799764131".into(), + "8835309990713613011240324096693076755485475658999871502819747407829989219746".into(), + "13102158958973358955423565573049580406238531533936309830903999596178966162490".into(), + "19927703189662863743499379923522860979653455328626544661291243971618992342837".into(), + "18417771183154820005238210056528713167003520086953806649233005148247829186154".into(), + "13242250186667974182640987653516460478853973058739850129463954545512907574522".into(), + "10971901023853281329361069638276077765206234747340067637718378767976633645829".into(), + "20436550472837870181409690438226695091760115955076127106091878852797639823191".into(), + "683842651763399941903331243661454687566310039977770092715404267515366625429".into(), + "3304534668380354910105587611199035768704466410761708200478786163367382500984".into(), + "14327892159763789670354328059011011973128878640806462164819794130243254129821".into(), + "13712101990593648405837473744314130986494510088132644940425089514662460031793".into(), + "1270386163717136732049662990020454155453019401464056820650142849751291739739".into(), + "4559668312052315567004252521434018809625818725552950834596073025095274632653".into(), + "10289456013947128246221059115194021747046925564818529566042034047888244657473".into(), + "6981981682422059144716871555026845840161063380660424650450978975416029699739".into(), + "13275723002453843398308458799872954358948259042779675411059905047590837397361".into(), + "18372074965684100000331046096891533070433189717560527825752357282553296305210".into(), + "6007153627662867365254986874716350833679184737288669421698890656788831322929".into(), + "11557682792813633323168221751485510314542594132819842305598531070629168100143".into(), + "10536598621155464430657941977974614272794233321865085717974545329727298277125".into(), + "20566123440884795144385782557360498238445700080133152934423121801124172346047".into(), + "5484210585392274768700243869223282957415576141086566136019633416151230114084".into(), + "4675266041161206862174450141632759296562489084453522360678052892725376421684".into(), + "14506966485061491552710372008504993235111668026216492386033611735228479487468".into(), + "3682565950309631924420685101131217452257499881999322497664342243267291843503".into(), + "16753306733039910894513530708776251948831720207834805689601646616427039909037".into(), + "11892397629144764406188085785897237236955294380381710017192179450763501663923".into(), + "17027229171478232498721421673139332166581061755210509139252013418924500461243".into(), + "3560458480908782960366816146149753544371185355186140843210760460011482921556".into(), + "2523290942811919827064721825289040221770310594770466909167316010377190569820".into(), + "17586848354290518015476851435178627882600199642491204839902589087637701736514".into(), + "18771893348474501482962831973790983143756587183687952333177929270650139940171".into(), + "6788202157749582404834375771398928959748074435244246320016871403739257327326".into(), + "11025631863450004428764861086496374449453982180198151399523240056816657483248".into(), + "3256907622263919521402687344729539839835290137654795380148237049547054026004".into(), + "729757374802086603625382264910105909740146180896096383332210024077887641124".into(), + "19863253866253150070643618896444516678169346690564661550005769233120838139485".into(), + "12468569017378925985548033310919519222810416238732327538088208928920140959143".into(), + "712344748962578398623451251358410865586764243720605242158768608887082462846".into(), + "8546087066371010720013920767653366050032317738437010080974697619001241722483".into(), + "17144825509786899110344839698077839239721239583625175190269757913667929043953".into(), + "10651563297701188942358589203989937961905153035428112097802788565849122022100".into(), + "19602341346389413323180922571631527509531683866957468565049297030414658843948".into(), + "9238186664745057178430953403953596421917515090260446457039212350976296818523".into(), + "263640414028390180122517954487976369901122460517389747631764885875587715955".into(), + "2311641918305077640172935641310996393584851078677397516017312506521775283636".into(), + "12911852110192471656473443086611566556755106535388637084532737811151296554463".into(), + "10436700004928765835031725654432267178079115705246966695358470216435798181674".into(), + "12755555289896266917759922247555708737024386059041699214870911784508162783525".into(), + "17390583422165077903045260639521919716984664232208360646931078032292219709718".into(), + "7412526952366864882775200227476857681850213243362827192310877977391550357930".into(), + "5016060582872027330190350728607317487069057897723717249157495640519710863591".into(), + "70447200134990075406173842139872041532268968648265338736409860251327029352".into(), + "1545500244158153586647380894391367444874762740407966854865957002078767363820".into(), + "2082567114283705201161441383508830647153064041365131752708347264051557391980".into(), + "7773933577113494097575644205473257493685202208592412633139277067190461074505".into(), + "15907352821797623044340355088248954282080052141018731890243639338361458586983".into(), + "2453390435048874114321626738320866552399505338711520013030652128583351121221".into(), + "9182038581165182763924458518550360578443802241218652973210280653624820005202".into(), + "13176557622325900598244222336641110473108400343854387783748570353220729582767".into(), + "10599983241136666078578113335543683963633036808782400964809769571709020578918".into(), + "1430816790456574892099931300141571059151141389317227589818258647628212654923".into(), + "7207251746626434553568433426934231676780727971853793874008147862305418016123".into(), + "3847365229378532841231862621068765430417579646617713430532944299440264931969".into(), + "922422158589085666348657924088867593873646110588554410818179794404300446471".into(), + "4298485174770134050325487753075508760849575591910135387686931072102416450479".into(), + "9475141350581193757416877790061277619494551108434152557051757495614692231364".into(), + "7750163624390542388958191386016094472536166330496081849246099823270737686866".into(), + "14363173695671306304956071467171940429435853698217676411185837490356013810171".into(), + "3402134714494071567155197273072160417049647120230862441840621369782667867977".into(), + "11378968132153772980874973211734670604659991740586197794619174704886870525408".into(), + "2500862781199005154907185089778932765489906994365960644306361544820582839768".into(), + "21880931942133046355810983155922578513531850539420426025723154879488808270315".into(), + "17850206894189265929807971665186479441938275634968267590809377452033564010382".into(), + "18427883853363251276513100116480886898434829323430684895879968439179171503760".into(), + "18758795974827407022563870795763356401215175366078230621502388363785425038612".into(), + "15672649260544536516531393740985073476934112035694203841471047634286525005174".into(), + "14497479780124030172334631091033639981498927489925809517218125709975200816290".into(), + "11190855071574099336548308963044121660452976926988171712775481672446931541539".into(), + "8339442292395337481335048552147626044800877206694030770577319544121541364092".into(), + "2461178629683239975488518502624530284391365519847067341739449204945212652770".into(), + "3972313936510404965199308344697399140590038866586718833591813109326652018667".into(), + "3224811019580618549699828950033477378112059204060062023677479068506440937528".into(), + "18443657715765406615721041820828109800966587434816919981514222787674698772960".into(), + "666201271764511484388505793135876064418452477237751508215203932379618265382".into(), + "4434899717815685275523711262432486808621984251515429736982413712108987655422".into(), + "14584918585762085382434085071460369807803840154636220934254933165793423091295".into(), + "15646480282455307022430957975574008173154630787861430193406352480280577045711".into(), + "402840791633175231660910669665966910050981784044822648466848382615330599909".into(), + "15437492296189220094817534101128968523410729375545135146260659057729649968314".into(), + "13987760171743052442513877961667805977500573882586118554487715622045738218279".into(), + "12589095501858681021442730872878907609617459069328956803139727387371467358051".into(), + "17551064250089164193025672794811675406761638177060737129533175904585851772273".into(), + "13500706213131978087516005477128059726177752268287240395927379509000435850498".into(), + "7331629294073516250840302816971095420668983701195024195892939287001016568514".into(), + "12949377725980318589136021850295478499564248427839661600142796482665024587971".into(), + "3988955063770305621858590171391799353484164878730082586815877210936858093890".into(), + "20512156157023978986265779260320491356890557397261515752540394821171756173724".into(), + "11624190532749034673782735319581023504009231230729490439584417709012081446066".into(), + "12473562150323140802035699452896239306300376623759190078147999182702752528013".into(), + "21504777935543484323252258287484534200045631968996932563017737909760083499017".into(), + "16104745906544338230790783632377375683831341202924378150021598903321494336736".into(), + "8312554144734150053969625169851557776466370096299754626528722906617398229171".into(), + ], + vec![ + "14715728137766105031387583973733149375806784983272780095398485311648630967927".into(), + "12450793357728630597819493697261391961392738728208603858426218806728799382497".into(), + "4427733724068610336929510244982091587998132283636864368924406075658439074153".into(), + "17863554236640577761956319447874252524561947852685470820159498661269344021716".into(), + "10723868775598272126873918500257797117892409794706524915527428530195343520361".into(), + "8041366806917098496431513544630989490693774700064656765914266570204855843526".into(), + "13046986480231887538692223126751085950758763070227069247275787663666591811005".into(), + "20228999562936372999611354929112125019466353738760451044697249912024766542482".into(), + "14238976012080913074226552202264063302466135977295108038770514743089287570221".into(), + "19486717852389551661121716850619781027370627632295683938875312739716376501717".into(), + "15733057748709959668511822511174594221965585899587926036013893958610587491491".into(), + "12041333229715539748857491855115983195198694619439452683631630426350435252478".into(), + "1829888811413627407640409778757789140470123549237476514374669162490680512211".into(), + "10288898018349095056494632386514957183841700001184195479721999387950102580094".into(), + "7360553146019695788111059047354435502690072975650576744373916804385350955674".into(), + "17476063720528136669048514677420727796180556343667231122803521620226101935369".into(), + "18384724266969916899691009636435516722111206340289089258767862754828208946542".into(), + "11046121967047431151707881264774621308937270618998625466342467829704953599782".into(), + "20018232138773775379089542131722766973741687507582662224374276186775807685863".into(), + "7926534193496947015875888176706209291021745851605316909116853588598743879034".into(), + "8826996877877607049084007876351017199517432230182001641783930871320527792100".into(), + "11760708819943554023765145606995747732169597984739408998714117029765838566505".into(), + "19598000655770319703844060561747179253151181702222064644764822676806532882514".into(), + "15036675263180992517064890091049355832990063162957265821390555448206776251789".into(), + "1053420874580688637503969479036991299021138740018858993455108201424412879748".into(), + "3723543690610038931361367959096800720510056325209292666118208798533818425035".into(), + "4599370243050726453512484851927735252841106375733105184316191846221056036380".into(), + "18291400382386598447603657416871816375751118990979359745849342284893280004873".into(), + "300341627009231088404894405580745838091318300821994947846008201887884150151".into(), + "13332605655619720841053062902143052543375741442250678582318225211621890248982".into(), + "13197729598850829723360679245789196039442968018972826673455394330035263151299".into(), + "510788688496484172389408566109007465667555285205327059265048317979249570221".into(), + "1685584118031999835794907889275254096486823415278284757369286336252006457602".into(), + "15103945090904102223538479231258677032197950627619049222966748226967974852043".into(), + "6653802896618953033344296077900828173967467309849915708475948018848254380036".into(), + "9254803560511166426410537422101769642611302194250107918342410310963831784950".into(), + "17006557344160230194691541621666219420787918477303225545533644141096551358258".into(), + "773112329554511160545400721342977593377624843987783062638455005748446223137".into(), + "6671483881284330250685026918783029584764740571210869197688044338476895092050".into(), + "20812941492969561606721983530907505914064782270990490150214736286311482532652".into(), + "1156984923268097592347582093730300227184163551449762803735684309575717323017".into(), + "15303159756724065068145651405407765401796657934219121639364061501460295743948".into(), + "18999785075801878445291021498876384414176522501978873700451842582224940767334".into(), + "3782716983967799050957535371991538595453996691838733068933109780481907925378".into(), + "810443910646366078824923626573819081371243815242873044781414798707744583851".into(), + "3940687718063184864573934886068875138239553970085689518511531571139105765743".into(), + "1222092197964451545227395363538155091563596468425395922702697716100572937718".into(), + "11901775018663948557424314950737290815973735008800495766054692238446226616230".into(), + "21839369981774608005059280910009281502958794510307248992429390932011110951241".into(), + "819873152679629471918450179717035855395702808145570990556719950289951175212".into(), + "2918016794043041559376798791171848118057043459636680115122516324180788251680".into(), + "10788401265856066217998495397128704450484607734353922353470809976686155443188".into(), + "13599498756047543641157208425687419183141596017402196474108059160235795892976".into(), + "4993390793677030007023804867617329393931635615810976661139461248253851471412".into(), + "973050533401342110180605419751137563184725082821038770229241448201970125921".into(), + "14313276246574487682858906899808269544140218917497205965354285099641091349756".into(), + "18746777136177241043722556179260854313319807637092383577312657349740719965076".into(), + "14517023428366357570216698819722831600577825429761151189605029742824536459972".into(), + "20223198094330596704408798588338060788093323967112845691364940702136543962642".into(), + "2924401185705980722600796492514644487545258803954418619331883216838542308543".into(), + "485440919681570468713530641755278841324413691217763990572458853294843435089".into(), + "21560476826107225363638525612645382878298890750874072774141701406519608285783".into(), + "7856508582404120415593106596945280577031904101959961641860467517902309769386".into(), + "1505151890969527772884247006998953879441745452105187039442954300997320053301".into(), + "18861812597641777105968621029392243993700881183944538936666186678355756609806".into(), + "11964609307983840306843122014689504510236749206766494519381451521217569407396".into(), + "17764783391855759749651949748230026302359698415337858912932633638930034077791".into(), + "16562247632438820849068750036602367255890087581186727955070681252413797347277".into(), + "3341595358840888933968836940161983842834749603437573997372892853189756769506".into(), + "3198140245778498430686233550970322127895441994253754893043542706415030678798".into(), + "9829840339700031668849847901844029075426216057792062644639239580989060312114".into(), + "5999422607425238131817993672620301343082348300090537110946144186609066413585".into(), + "19901271533560906428202710740924807375620638454776660078183104891177283526156".into(), + "16697165654181109350158134734382046723004976300078845885330478879604895897280".into(), + "19171906568090360833249366643372143476587242793789646446664643684138123124668".into(), + "5557557332632668793539639636185643553639926364115539987556075445308999628265".into(), + "4797522865199880517123583692586561796505378758857130153602827907909887751116".into(), + "15409514194242892627651944305634286919424076146534027188938906487506413405089".into(), + "10407013998132974348561594118793213466618426284969698091916131778477581263008".into(), + "2534925381155806875978186916525958864791165037467997034976228683909613017312".into(), + "16140842893634434452708565053572928560639256480905937421023970743339301598617".into(), + "7517617592925372620130293329989654305076737363747701594349097857054039164182".into(), + "17572708764253481596340159581412737527195601517063980704204677005617144607526".into(), + "16697796470163537491131716229045730242536059781538196375577575057386248458494".into(), + "38275164685285960308550480834951641755153240877853193094138358285155638204".into(), + "19780228589871041196871406056718374983456578990309085234484187723923738516508".into(), + "4573417308961077301452769955811063226515352449986725327722241421281202736681".into(), + "4768055042642730073498433238804346134649067788593835428664493008393684000706".into(), + "17566912618951175959416490797476610679702184562687840273697859062459883449046".into(), + "11477598695424707935165112148975667441147635429812599883095916948275334113413".into(), + "3408907078049921938725945268376819484694115736385272440041090673225197146180".into(), + "2488590561390551829094067182419871806900177001183027832070626654223650976899".into(), + "12116557895894464059885135778994901345424716569754903115015740397131803733982".into(), + "15881232965640921626180413777392630630338847181632662075996983398726326426432".into(), + "20914323757596181391651855665547258251038466184617935369425714249299063760685".into(), + "4275923143992397246911855313401177253209967573031785993454148836244404305934".into(), + "13098973753894185378061607442839048669135765294488505596582737281481575045554".into(), + "7995472162206735324879506324600884378126850726543803581430135236761716527753".into(), + "3690915804478314734124615543749602171459078573370790663994412906012450478823".into(), + "1256453655839486811750227055618146120819862944082463957526146264573763714294".into(), + "4406492967670422538631080907830590263463047897583684262207883537903678091970".into(), + "13380843970691717863215678292643800288491103227905602355694129412234174194363".into(), + "19680159398793220289979983679401118779763854719759576408245027038965290325739".into(), + "8515713472495355510508289305321355004480161123461789103991491891201940557902".into(), + "18392703846804297332972535728243845000077361414687818948278976164182674947067".into(), + "19823604647876421559318429394175186838817554072847524297827763377975574273192".into(), + "17719715026846703054856559310322577442906188886145763860157972477138788247667".into(), + "8745282777320550983079435446349157218001552450433897097227622172209480270781".into(), + "3259368608255603766247016957318442624095407655100612967940789373312058996520".into(), + "3379679235619387594255002628664818227413294377266729211815713998759100259668".into(), + "10282673789366804521601844018863748004632586596870138135887183100195194767004".into(), + "8431227731426467642712572981755086675999345721043460063547234289139267810255".into(), + "14117058124827023634266519281629142766485227596060997608233088670325722698559".into(), + "17113232771025226173986361792697170950811880770802373827827162227101499645884".into(), + "9906220434844104062978204733717072107397540599291396561476275675218575564970".into(), + "711369587296778404961826907371863989722457674941832862265420496583620086218".into(), + "10995654568685707735109869974152491589223292425449581061000447170660561828729".into(), + "17197923097868441003908860864777521604587651639410061820516916970875615238246".into(), + "3121715947184842829391029463556305441693293825061846129844634146823663627601".into(), + "8817835750782344079827519863863370969960597321588294656839911940551490704717".into(), + "21074199894730915603594812797833479514843396752652846676596119472522115586998".into(), + "8903588044620722375103549330291845285230849782400990458525441823641905996819".into(), + "7157451412319473873395155428325762769952294079544485671397508107346256362850".into(), + "5366933733103001902997281886950280717532636892191522349820059149392915169558".into(), + "3729196254269053915687004590799382892429870424157270200083981101426772909827".into(), + "3918096703119862723362353838062260616080657756068272173354821697584630247209".into(), + "11073027330528765229119199873305594827907404967404841004751556462671634016839".into(), + "16424651511178205757967439516888026957937418127900739730326874335888617161971".into(), + "17036562818332519536292487256920458988625450115083747105277938048739292827058".into(), + "795554890382567685751618566957270321871701261784565632343709559354970377145".into(), + "633072079840093073847779349151531317793918731920375040247534587265858418734".into(), + "19421194221177975514787747427021411300539454454371387008642591623632727982196".into(), + "9954719107136377193496025917640974425520732567100168938432529522254697824571".into(), + "8674312532180246290069249621352567303340886011365637785384772665860996736758".into(), + "14809129550856657213168714888239735820810817787153747648450536960647330811703".into(), + "18479959092813678391370975524549834571584338614798320263799188362327888537937".into(), + "11754080849414921164216607793483937490683185256818320971638570891360029327056".into(), + "10287736699385961112844233987245832756528102056561178731804188514133469579013".into(), + "14370616700332892416887680617217669883953806003377620695037833373409292189021".into(), + "12131262377053219810698216976753909777223459611599034218924662817794274728701".into(), + "15129974113281645648506209149692470898425572316691306513209191313993708898437".into(), + "7871644959999350003348485402403894487663479920989578076708137744830000430296".into(), + "1576915733292398470896862707357585951921545131195468346129170132189223165938".into(), + "13316238922195025030929715018519212370128739646325014577776776032463179349855".into(), + "15160020868051885495078648274966503057453505806774983308629511566464684311627".into(), + "1692269682153339201433258246771340974628904846837119864247013056373782718416".into(), + "19628837155426033423644376042848583705054394443378101622337255362403724735047".into(), + "19222966046507618124793516210121558272031295169005274768240595331459420997142".into(), + "12990748614547458190976906297393525840623470679364771518133250166378979874463".into(), + "10124996030376091099517250678153357142212975502206884325977282211158514276950".into(), + "17630673366223237394418802287655202715156124721482801416980858260564381593966".into(), + "6743037447395702022066513290929048145404894812633440602191382691018136524423".into(), + "3910195434942407507599129230554588207801501224467133349280934483448828467487".into(), + "2025953242925331197360540874793022332074847486979998082380244277507702608951".into(), + "17290925253475198968609624243667228472127383792887388480830073536530705682760".into(), + "15557314422719360545874148111856256188428921052029295715627017447052250706766".into(), + "19758557148246918190283097589287660972538989627091387035573386136809005998935".into(), + "10859351185398338650386876904094285059182038967427299340069909694684844129362".into(), + "3496018793417449121342556434800740598384008787187762642325224753304909741349".into(), + "13695501250971489187692201493870442254612771332042272465953359508617675704938".into(), + "13572242195808512474816152630443442412961099907068902213470234329372028271256".into(), + "6257061132956659095252686302119011010885219692712894010340612889095488866530".into(), + "4330599809632843338876238530496396340118064854909940219910748808728579051913".into(), + "9157987606978264109338780586425009211347479724574125407732261019832259951031".into(), + "2328698634372378957406958821467382289342903425118775270878244960387352862845".into(), + "20636525922386221727012980541907198653039323429055563362662406273278160984146".into(), + "15847894355448175995216566821171916679432807087340467956339517156584053817157".into(), + "1942360378421747943668019094002571732886982847410366696537432314848905467679".into(), + "9512432294361739988724195228775769058251373607278744642461344881575127503031".into(), + "7373765909536890992660842391636719615263272667672747352621337161184389163446".into(), + "16805165862480928364732162070809175154629112007405963636466097184868514458659".into(), + "7667777941325858499291332847392489530780564386762784335358233711706517931292".into(), + "6446208647487337326336908745536052288215677968074882840304817109073334759485".into(), + "11285516171986135785540153632137541881991922296507010937224736080386568662797".into(), + "10115214387228124714106659470937696440920497755599449040012569123044717722706".into(), + "15485618097017003479590081826451772255273462073640651108645768569284210541135".into(), + "14933383877101576453093795963534828854771957327481830015228527838452944594646".into(), + "12699366929120600543724208703956381057734625711467645612998923493410472579972".into(), + "12636366946456086231704939526732303791619337704833963854669708252203542584210".into(), + "12149350767700952579168066320091211427411187251056390220529300991824437924228".into(), + "7521252564104984899409328139379375498829232271563704354107116269254046402507".into(), + "12033991121152464927378622393121300999333393690763174606686511857615848602007".into(), + "17232776948709347607296344257668859070263618035653710252910881198999758003380".into(), + "8692908682458431891302516268928916165669902656866484222966303081483718910104".into(), + "1253076047322637463481069610081050841277544153675308425513468857300598987482".into(), + "17753389824587331559955818909257943804816005297310986968447179587639048799696".into(), + "5220269242560242526244582743085713945173060875457087963936380952653150665967".into(), + "17126848126303954156127690428371193690154903947228604938919561454676410821149".into(), + "16844245036721981603144243350071451732279678956963696493069130132912694448751".into(), + "16797761350119564409426534689125994845767740388070744929816576998448097719798".into(), + "19353620610135120026060560134469588460709151673182029068633909633596535108020".into(), + "19135326024992044270104645311242450367403619348108625528873986701416220617679".into(), + "17665816362466043406415418194780245586053150534372814020191541209753248047067".into(), + "11399583108978058354832763133747562621839059603612742599115200702193127837394".into(), + "10094334549114303273265943473013412623520307578724043117639269488721170750917".into(), + "21601458494506173036246860827162868889968956934810679234022762622742359366252".into(), + "6386580477827919478878489737663301647954047211008970416851133263802072756591".into(), + "4792043837032853062947152822210390150724912812294333339974827814683543135564".into(), + "20876886123310865680023706563792643033695666593071136348323857270657128199374".into(), + "5931154799422838405687052216230902279350178420072288819326391251206607447359".into(), + "5239679324690579237822809044372316561806419523557737441242604861240795339076".into(), + "10385003741667422202343482240152986976068622687279646189490976516013598227432".into(), + "8464156248644168452015929033942509092145250244998026718035923409819766539834".into(), + "13177537753162628205208392995644675716264814191265988042404781479197639366733".into(), + "5919477377826036950488668794024141041792143979412430063956231337921980979482".into(), + "1351402666854456730370541080745509803482004768817122599092881844387000676155".into(), + "6818673776641149273361875347660949176445649468306471072411086367313332518455".into(), + "1366646945884507587781123424154966453464902291438811059924651777083838835678".into(), + "16219293249111347900064666257423013936256436002819357345030961998874555359000".into(), + "779230149490072246312543789505064727370429119089791148581854356816464370377".into(), + "18480337167389263493513952937037301086055810692872257722500635290543939189393".into(), + "1345414110418158215433956620396568245327910182467730711109133441878095212920".into(), + "12518315654451653143886317929532883727219058399486775127781649065277400104111".into(), + "19716171362713656659833259243590727588692449255201500490000859973307782246016".into(), + "1865072487559894165339723956247507020827160163812334855490266264867949416605".into(), + "8915174456326318257703177400411158958853446829269268103252573093652570933472".into(), + "20191934956657253997484040571514242713447218897800997897558899754776252309230".into(), + "3900170788760364547006546697350123842323924137566872497612605525517074710000".into(), + "2242244954905694264442292936230335662862827521454977184433268725352453968501".into(), + "17212753633823250440920113486091598217346743686574392123683302470302281044057".into(), + "11939276774333100126191320505078174289237596631307779156488772314461752488631".into(), + "361355126674011999247836373885105218009746852422112563922207274436194144681".into(), + "3861054771271956681986534133247127581996350841974597302976225613765246291116".into(), + "19968479093411941747037123171825881488638273087679549521610505739311299462846".into(), + "8537196135596544183619390135426012949552627827993128615534814021127294540392".into(), + "2438879838432432949185118142364194193697006515067980632650379470739663214843".into(), + "10769366200854175394348657213265947929465261545591304593688343101111720627317".into(), + "8455019976119342575889554308499186802278388693477937667704910645050957262689".into(), + "20644389417984700539779514908032253651696357386572813102276555909201716748299".into(), + "8820039786383750409041489202684137325382534899692778928304664068322226640076".into(), + "8636461459675525672530300171201543901107046823820677414340465229975162161919".into(), + "9061524648737340075438868917468774023866583922769991567001812766008277156749".into(), + "7602969742956570438827438826124187210014769304752116695796494779120606534919".into(), + "17880480383024583813657184645997268710007005482705400161841684734099773182094".into(), + "12468433127385453618607022105559942067759302463679348320088817783890080634670".into(), + "5227335513133160328788197758812517500875193491652227971114102085123079105787".into(), + "6151293357148965084809035339276030775032864902311425722089088413878852880603".into(), + "13699219811250783019541356007733829713463891996344484242492968708316395244276".into(), + "20523944015644472920486129305620987253227711059638489683670518491277805771642".into(), + "10421521516830672217871475174620176828341870738569247402138774913961149048583".into(), + "15243709334491280025949017219424981672670169674700467979049999809115231651422".into(), + "15516151337135073170256217447458198066207320794936363948307836943072374966170".into(), + "17337341094266438501679457986886656365327787301649468585664115813920643670255".into(), + "13262611487153423909813660830277859169133522588408913308784951544213550636850".into(), + "18531665394082016871726276363920851282983017715104457591860421181826617619235".into(), + "3700454591945927209171569025131477008196191968736477330379417168348613474972".into(), + "3604972001659087732761769946443190920343158947813896848729866695375607825911".into(), + "20952949990925307134028293094501736726689724950451065635729323134614933963162".into(), + "9405357171465854081502883779215538022417071330241830295392540662303830897477".into(), + "21638057691528924765719568024989208898293733581278465977164525893773900371884".into(), + "1423261214711655336057796638966786076518765517452404205191550645234914655224".into(), + "4051452662373209612509106830833400151748328181316060758960838588997502328136".into(), + "18894191275634392250799133342573131067016712303481664374003128715704286175519".into(), + "8319722910647187566775047002603641370685637216565762886509056643924765393708".into(), + "18376807271218398458453428415456722166053637869198381036620575958015471551748".into(), + "12035584964270041086110602893321059914382792217135345721427943800456312398294".into(), + "12648928151571890511419082198798501903838843998709266232987169892491925610349".into(), + "21412038262513052722667255278175073999553643537758589877888129674442282140610".into(), + "7706735190856341161262212613554225730619876208755452623628315796884166016734".into(), + "10999966015370832078836488333389544875338251739488999274500058322944383211399".into(), + "4088296406085952300442596245852961024918851819760395990644634222875937267642".into(), + "19399822412575078284884340953745677500886533272999950579143260384703504507006".into(), + "3008499431966541245607724530938385192395211534821775780577277325698653345072".into(), + "21447244586691806434401916456546893987941039399147865009673973728056412619884".into(), + "893624395222035047010673050230651164575948871010677581303166873938544655581".into(), + "21402344785412208717452894839332459679574051179708007417742748857146495441368".into(), + "9392712010553327328684355664342647815409597079361837524976044019430681532876".into(), + "11566000613582826375650817776243972243778859250974226949316472392849073658674".into(), + "12900046757905605731200852057204734685283283637014313056501123642345467590346".into(), + "2147232762440136333246788660102778148879449441151868600321283583777116020664".into(), + "16301766972982581403924204059742972933467455194833897714073756335881543890771".into(), + "9546560122931098895129690583175071306095759562194496054583390881525378967396".into(), + "3814097068175987733354103462855355721851435755267819873064912557751073632829".into(), + "10704509016547426355599213335456446765914211024738080860797634337598031536580".into(), + "11921271012710313311785310319425095342886561942032945429395596578758895308264".into(), + "21265249694322068914280109016742517903125526413969519857556032179013285196924".into(), + "7207578215754030787157150149235357460121567678249968060366462431427104673093".into(), + "20820013978092841458072065536574129286011620075823185493370309064760526240362".into(), + "16441600678335369077753559950421185577542163640313037056248177018465084864223".into(), + "297097313501884278852369638329400055327872945847645211148627847628970916078".into(), + "18298084629287541333205519012404334789930413367615524379442280529941257264699".into(), + "15206243674059814574375077493088319889784970587286591062649045683132661681752".into(), + "18726053049188513051286348977772545167577661574609708038977390139794201099882".into(), + "20262858185621074639529176348089123044694437795099449154711162805012934737131".into(), + "2249345697973053772423677422936999849381692933292653912080014325442939977122".into(), + "20814726663898441680439335735982981967722006066824203970896314191676769388296".into(), + "3816485989624386223507317175678560807682224519267326958526058565555245734714".into(), + "16741230612980371365533431648017361867585544111098407772560748428499802539906".into(), + "2436865301432265520692873922135716828388518032014231744012990863912440945389".into(), + "5265261577128499220460184630262997769060828863581478135168474766310582001180".into(), + "20550548783058990082416235781987882123241946829605049684648813233836863290502".into(), + "21523044301008793877416122201092687874337292497403523925455260117417170777735".into(), + "9283421400783174646451499708802113832695004549893166692004850391713463380536".into(), + "17813773547838391112844362681067751767404443478918792865885006908077545151618".into(), + "16486730475669947890512191574075897324037778751496940417084163322433837359720".into(), + "11367125189013824464048785896422572845103707778462525259651446893275289247873".into(), + "4759445724467851058773503846834304672223785226936531021666916376323562671488".into(), + "782273457631193956426744043048759353979593033245260492990657945904665284910".into(), + "13487130697992008212099652811750242205045881544509489831523448570173633517977".into(), + "15621563974535086891768796441515013364217522966350445838133979748032034816142".into(), + "19364835034502915244801518193980688426244659266819997726035650961451415757173".into(), + "21037385853462058267099182407141652124171361973889761119816789091401609511088".into(), + "20434791917020905003166852059282129255412677606775079570484129378535005615291".into(), + "4835039666519156760310260600042269943079463379265872618778854224413385690994".into(), + "17796521681519947552208651467058827825861565135255248123077469895978163706264".into(), + "2823350440792171019111081223801188552138104039380675927963458669980277420276".into(), + "16030935304664378631941573945857397096373696981104104381156313618686049806120".into(), + "17523561865544155408760007908067668065236326734119657233234283826019015377013".into(), + "3861341406966982603014220134107636493882146780655211775629734223927755221098".into(), + "1327887013530867777305056212037691710827939709365211251951525926327942169414".into(), + "16874372098146373517691588057974501095408377103185981262983559391956463291137".into(), + "1335930538845994150082853775454018356383085560294444442667355553131066129276".into(), + "16846954448852864630121063053695845658867759327963014776419090787323732938912".into(), + "1910615356880143423765930148112668984411979710628153215580997630269783916489".into(), + "8793723522335768214688108364110927144836722932802666660252079036893034856492".into(), + "3725321587522884864935206279104882080790553804758085564413847527197687551835".into(), + "17549397166194503933313005107479073474671951786436058351827338574279485542057".into(), + "6575272615526665941236934551769345604089554458721499014263130089965203838692".into(), + "19479945993771870488240738504390121923410154808673876321101554256856036124677".into(), + "15218540520084042504179141700157006972641510542203443030571191341196460163766".into(), + "11605382280428426652337162672330854829498688801746852913129963366330544359414".into(), + "19452583367341408020642116770501289011436457479987875413223766731278874726613".into(), + "2498463382382553480222037299113185800507848748313035345734629490930688205092".into(), + "1815123960727364421144419865126922339611466868807520419660969560789979822474".into(), + "20531692711768862540943545541715345229360673134388506876856593310216372259130".into(), + "21106443640856542784867046664180461359993554892163126756059125921876166419615".into(), + "8538925154199646282458477113696635826112766123791239931164489946578874271866".into(), + "6179996393486486548378164504724190431464526698002381214818146508779777698063".into(), + "1334556948430115939422649531996020210538905726908545666936164977436729124944".into(), + "14555087544451841622469763698691954343538388285983305607235034906273022598676".into(), + "3263678860186354326206053303615515256258748076250020171477442794745232038780".into(), + "1342606052959540554052550853649027290857482440100275878202185177537473434874".into(), + "19067318604617984900108104413860593038444834168491290140413988853573796446193".into(), + "11453576191720077983310542494091726783885546118293459348522522324645101050430".into(), + "3772400828106882724656632136643514300687950364203707059277582466654856015909".into(), + "19928616354232846804233301414766074864065580313304404532140360351457581578733".into(), + "17669618023197654971616078177762451816976570462585423216749814198562722234016".into(), + "20487504497482961764356160511764652912371612840137405927810776425577238052311".into(), + "15959943319286858239034503624455112049217253792773599324329593237810330429519".into(), + "18384331160163107383609864825156022277275076414745740108239579270660154123750".into(), + "11807744905122445070761653068499781933485269571078706728521902995972849333739".into(), + "21636069700028297640587439425598371999203459272489053044479958900301869951268".into(), + "5974406255004817187688462241155741022204236935194897255519053490391727654963".into(), + "18655439470676485950283686008645538637216956533059508817637925480405213882893".into(), + "15164692255429309369428108531856612257028649418370969640920631880841690009016".into(), + "12342219963417210875401056442100023070134657858086394031902694268469750570612".into(), + "481209231155250366998260270814874408671884781003382050138985430923825730090".into(), + "3242985953168013112117560001466320034030784952490866310190327264524235633420".into(), + "13671160391160864796369771052335315926068131063004086507703804642392143876725".into(), + "16716228406804746939632807079686149044089946710213611348848847599210659020138".into(), + "9496049727665863372935045496498617414460003517119878231671018103126084599100".into(), + "16483340875218689502751737973203780724082025375353804209734656041473116836207".into(), + "2627597076078148403546873341483726933849452415436198036537442451261384383723".into(), + "20527956374075302103516613197928664717455732919429461243667758971357150882342".into(), + "11711450220231538029408058975978592998998598526983681112180323327131923215776".into(), + "14877293714143600802178367397934915488570060506993092692625720179311507474506".into(), + "17326201000468992158693082078045140389930457394232528033746431682308160431934".into(), + "8241890704089720408679017565592201736334812957892898769189351788325500937732".into(), + "6134985085876540657808139826388808003135254271482158519839818774839726308917".into(), + "6944918715501093472287921248184355748547193680657762762284351108190443908482".into(), + "20293371855859360749476040038457808453751087076170457949707661658124460443795".into(), + "12686929429491234226470786986230897140429036877303905464553700071658994784104".into(), + "17469937611674874489854850805106365496296990924579100118175990663783068480118".into(), + "4389315288495042551686883151731749050970801790377604942482415778510472384968".into(), + "11356013296312574683565144017425132580728729177241949155779586695189495537084".into(), + "5103616537832821778796048073410908442363049367034544148603830689894368565040".into(), + "17797731362169406634431131949969435652804582561417001546024888062211188454886".into(), + "14413974530545126251158359344156378502844867672748912889426381728267720393327".into(), + "18860675036245741580291857551498220749884348391920381715922087052471051304459".into(), + "2078681010293955893545295223175290151677764183673754633340142745613957031877".into(), + "11594462210573371469687203943585180057860108341927961420756260896877407822187".into(), + "8232172476137304604696594035794651005660416081930158074561971898151387789159".into(), + "16234745736110953717672420346414210260779855851076189537371942811750295876135".into(), + "12403261277735118438898936378116787991453555210970659659639856670648844247938".into(), + "10260185954137740247486488192570496092684935183379388125044125653647328054023".into(), + "12655661577981598013787126068450556825218951206788052328715378240540030673155".into(), + "18875782029492829253540920061867800401544385695523240332551730645990253683286".into(), + "13000939909369679921538945109975441940863265779072482929455684540500587590629".into(), + "239651505606383903278277662841450805219997298453219985892834268956273681444".into(), + "14053674646208577108881262953518523519057705122297176784230960366018789686467".into(), + "3606574524342197944154321263420984044427893927972300192386619594198948706444".into(), + "4925738689374393290519002876270198297196104042467164940497567711764321354393".into(), + "9820857610236925174040210045575219513594477725958302510866127781620764675531".into(), + "8644935227560188528158307606853375529544842899940616765747319983176480635667".into(), + "12589563927120228887319930197852404057542625019034806374830349240796880735981".into(), + "13728987671030134173563628755348391107370774536000844606094840710456114349003".into(), + "15280672692530045491619672502933299001869276703035606138561063102232345967821".into(), + "21236672540209166733321925277807375026701626666734236841532747395149863205571".into(), + "18193368154219306112046312834283644566129199372283662927472078427038205531636".into(), + "17828956732555553542546753429670551891943977601119756829631880115504235233984".into(), + "16641047964358580103472953437535358748387376425127849904658691126285684204504".into(), + "7196281413799658043487145161620082973834461754768351228587249162400339111893".into(), + "21279455923934963235610861427104388147894350922169838127737714784897083581830".into(), + "10868227810739752166142906769497786680491652628709341836398414527811509748689".into(), + "2545479497580424357309396388184225593698470568625667945691755386799845345027".into(), + "18560104754451358950174079457178017278416450108044438296553162755384040068059".into(), + "11209544817144484509471895492404241079181269159060632258040504564376475442191".into(), + "14007605578670373547623429803718323316371456029307063658189484725071020560017".into(), + "19316201371814679831554697580647476192318282119512681720915001227483533198021".into(), + "16788142218280927569387096932066591137887806957079516944927766625343518189548".into(), + "961359518362994763330685811948798278197676602059504713988410706948791494727".into(), + "19776591693739287332042935252284088014720557305781829207369487992244783048185".into(), + "9480779019638564372864984254416095889603560407402750333423136372713778963272".into(), + "7812061847536565125280880398757948966749177710701972331770694629380983832516".into(), + "14806224217889264732099766866344263686300132511433376375954468192761174167878".into(), + "10982734897602724370866115596864634266746118759609469486863878972425453415519".into(), + "9054801238670111257982773992849940941038784597792282084645523468554872244495".into(), + "16788499373458165601983802204061832376825550128562541027433580619384299691535".into(), + "4361212778425224413929793165968418385407821814716394404713983701050982051159".into(), + "21198869506404830651226227162808186595284220877501140400488215541390720176503".into(), + "7255012904510681544072472510832565052731304049336267892176928038570971034121".into(), + "9737409770400739938717035426255379270654933363992002237053138761832402079248".into(), + "14206577906412186888550704503752653056320975796075254442765439825369882967977".into(), + "19036632138581200062386943078412086222459679497578993523004498970778925638274".into(), + "2855178582526872375806959544405581665248537620420194093904041355969926293337".into(), + "12896727255458884273207928529421874672712973447260798892551468479503233439215".into(), + "20930350939164528694912500193219456539952966506926646436560438515643683077210".into(), + "184093243282405111677536457857692693581379037444126410664343605529966199122".into(), + "15658149328429348710722591333703516363901544310832580304722884306208924451465".into(), + "17544235160628712643216064131303569753533519783718786133736357990785709619346".into(), + "9378984995834426590515136439048146470293781405649183047514776402081048834772".into(), + "15827462476470655610816981948418438654022314364182315935007413461648751735708".into(), + "13474113844360907776462232979612140726930720201237003164521648175005015977732".into(), + "1846676454601041085237775396212630553832771346942418764660365576890630152018".into(), + "8958790186410745003596973786908460746144469347369569174866696175944574520886".into(), + "16716100142556090678395507171596864615262575578180211444515549196841601774046".into(), + "17584363243087108058467208592097637069605249776196694465943790236027601639916".into(), + "15462568643993327150997687623907692370120490318886920754261967569094539968909".into(), + "11670427917584674115542198398366950879185738970881616803513412243898491416455".into(), + "5883010686944177614793479335292002976406988590121850032334552332298599405710".into(), + "20848023045403944451304856285219275218146149181988087184275301094312642906291".into(), + "20892609628755793476767683891284835591758207667306100001065280698890821585620".into(), + "11041559416099382923560246079300939393371149141074957197352566129686429429340".into(), + "17004024027027164912556351303862470964296900000646134239805113699616064012220".into(), + "110742314120280698533248152539115345099402903868297760208823130532853128340".into(), + "13611598917097489441998314826578736196564311189470688979687759717921520208428".into(), + "20362978391139708024092837231934567580385484740720090300868417284017430844864".into(), + "4130975720087443718484415210347908638971321493417335260526136858657572592254".into(), + "15799784358302997284875412214187555553319485274948108081666806701893845835839".into(), + "12410480753305882251320943831026503736012757975027018073585110506521877824193".into(), + "11835843853657957571888855948788121206617247107501669280697395787347649231752".into(), + "3326313455005237548503557557286834479752096887215379141590090769222516357133".into(), + "3193633369267878319453517203588676707547172638050950764150162277144428673066".into(), + "3543696055990388683071939150214505536733386566291338758519836333135488212473".into(), + "453840133795717001022433249997110059635014609516452256954528366651276289770".into(), + "10086004265216215714804100477403907145516617200748655771783383139854288214070".into(), + "18938459257787140207383332020952460039308194017940327258304986766920440675756".into(), + "18017538799787896442217663532610710859333377084532654794368604069493775630216".into(), + "5517691591172342790575564654696650661133600869824307632295945043592492062300".into(), + "5846204096126701465613249085053971321249645306247508562697696901334354225619".into(), + "3177064511134248081568628736306700282095095665917536853000298191943047784014".into(), + "7886005759395499452194553110700824805018792487440311729836576312028682853862".into(), + "19249432464407391173245558257296856631584193393398113008165174416171947900609".into(), + "16818455958785909569371690525990846776263170512884599090849081099178789681425".into(), + "16250344336602567919050898941410625842485562539342327155695417850618940905704".into(), + "6273998461375119044609362240019558608655450921258416376794979330773412610302".into(), + "15933077340738498731035173703791932079747269039222967104684412531145625747085".into(), + "17631878023023477567294765381542867314814954498487832435087010633074888584009".into(), + "3387656327342575368928488173891176548794878068816523542226413637288662472792".into(), + "15770343706243316227190526252701886989383556270818375222569120097305537622560".into(), + "21025947829537149117391184273139276031347299127217645728072786010534368285621".into(), + "11728430055160129100077268133090903533902452454196978455625432056779499908581".into(), + "2184576630760971645143677026393147474439766939689140114811262608230414186937".into(), + "20744811853491523948066896610767067484129121010717068573365370365324040781186".into(), + "5378129452609441814399329369785055593231824205814541852039878139773312247469".into(), + "18082900764136659604287793533371380099349929291808230688664846500365863263118".into(), + "10463958995559323021196963984934883570109613942564610388110191948063546468897".into(), + "244120224370345949702567256216804961153505781666838608095297311545160357032".into(), + "17924705581798291273661662368787600134425123985006190354093511903371507000154".into(), + "3107793385049037773698181795186417899797325916401357881664725445733609110598".into(), + "5665818573123185227274537904890713907625420710982346291959547939830358917272".into(), + "967322682615997637785254033877348832211978156650281338584051044602311410196".into(), + "19419941178285529854771216440310658103611219351729270204884834098822007849679".into(), + "6901963792883328370624032472781824547409040392368725235274158498520441238159".into(), + "13721659825627300509722716825333808233371435398666022190921612703736274379535".into(), + "2784281502858555298249063959836879135450746982163416748737579846439268828933".into(), + "9904373282060708277943634486822397019446454722637742217276784802015824898651".into(), + "5782567592658163731724098371574354386783075175203877502094122152538152467682".into(), + "10854330629450460532485325799036675355255970975925867222693267730198057197195".into(), + "7162558805520478103072398765799613453839879264508883857822705210986309908966".into(), + "14561060495007338369036260685346480181377385446422680685283066135483167829865".into(), + "11521954935420160563214644175207412771411940789064933791820101643809540481492".into(), + "3893071612329582305940837979511590531534863287842007408024123330272447072664".into(), + "19982770443796802008915975147614604175753586689418309845602797606117149147490".into(), + "19714753609495058998670661272525609201695470529132258598980221623379639411831".into(), + "10656632215192474178114431876399520721084839753473211054259843433641616176373".into(), + "15519943627473966175746342389219894179761085602008029155282295063466585111230".into(), + "429220418726674010600368106136723992478318707196454289985261340376476917460".into(), + "16943119555428737036287647863079565463224985076466268175824843518378134856246".into(), + "7079268853451648384434335899135383974808119657387366504271184409878695702895".into(), + "5787261347913259367727842908192773692002199385877294080619854106978539332397".into(), + "8254314874636465273639128395147895313719165057850599581478980264860146008069".into(), + "15417738281457065064716789110361253613929614783743035738325702945037527193953".into(), + "8995940809050737092434676062651493038351424361820394016896779859938155003450".into(), + "8930952966754141446126393622188683431566029237395186071059700311531927009283".into(), + "9012970415439810859538557593310902447051948348093454112737452817814629449500".into(), + "21700461010267441715993595978543322483687194036588160210184366057201658507847".into(), + "19191426116308521669196161733982754533604260068907220372422504926794231257150".into(), + "18022413735343984488479130392027693687461867574196874267731354592562070094392".into(), + "13853879871506882218224060020827336496729967255850404386800036291019021382781".into(), + "13303720125164503437055631247918150173085142868095887759030649510172293881844".into(), + "12463581809293287384469946044562671884924464520288697069370030386140109068261".into(), + "20468619377263375923071378952981485015200979956112400596511865225946853604157".into(), + "16682148710681177357125570715056314888342059670705617513402649433802720432267".into(), + "16299073895000203963165709887505572454180623116454760411179563591228007694413".into(), + "6439155427163506786329349605983728674821430800627321435200421453561910062302".into(), + "16531483734580605436075637034861280240342858648848575098901014901746112480232".into(), + "17413802217650584016261506268242623594956116228659732892682224912798301233645".into(), + "19833018739354446018077109493089909435818386368530968355647208939546565982905".into(), + "13005203599293796776324509750491064421128717423989464867065044987475986374420".into(), + "15433711189444672576513248931602290892518442446252602686878477157678233603772".into(), + "11272192842480959445178012145556234469776261923967845001064211055340129168135".into(), + "21349777755000957327199310930646977290027138137542241555905014230683052104267".into(), + "2414795183415356147955181901405712632718942970568205736628916600696077941534".into(), + "13910388410253717440990758214044472114511432613509643223811561885135488623236".into(), + "10073917454281511762447567386654530277776617831005093724557094001489771821135".into(), + "15674657915196276639699997458656008228696751013801231738985398708672037426000".into(), + "12030695425048598984176709301472822771003849589255577773183310838231109921591".into(), + "6658172369461756755506276881582345916252610724131747740625283609123100367529".into(), + "6460801016753822141904293563006139350014125998787400018150863192907944207957".into(), + "10798491465896968361800574703868612181389697312199241920447162078078725409638".into(), + "6331917501914253534943383807348566698937757752033630507696817298838693259937".into(), + "21521172968280414216108032807577565012642487518706778276505136864150789112592".into(), + "11443202152743097070847729825799673217706162711935940510632741405015900516668".into(), + "10360970774813507384412119692215277392320350056791930702078433469299837875151".into(), + "8111678922881662305935841208620197469657237670526301850210945861223648259810".into(), + "3828566775247110089904016755996284741548002327940628727687176763639903716661".into(), + "21019871488460899469684764817167629979753844957147537040703291790231271795829".into(), + "11744049805554498869931942573519884330545637954557542018916739662277241821806".into(), + "4521092770491436085084640166923844634777984445583984077999595768778116564222".into(), + "2428018726292924561718904390333390438951211767580762396913313600061529081905".into(), + "2672992591753804066533616673591169777906973091506536575810912266557203322920".into(), + "5631180351966611479340932319081124575466459942666630580683510336616679680271".into(), + "10149209329290376952496655294191511204529081153402908137750268385347783758010".into(), + "18292794133971639465196495021864699906132845458944945214425906730119328661326".into(), + "21442863185355178191454777233963814974940050392649316620141474331670970354424".into(), + "3768420898310640667772098495371174917665155708578905018940113026409140957987".into(), + "13677778555119984843885943251631654212176086447994430552012266440677394344669".into(), + "13884681165958999171515885225547717032289759601884108191367706162606597842698".into(), + "123196094575938824660055152882088188411485715788351262262924974166600702398".into(), + "1121836698372380581784934880625694675020871234049336489788624481922395781738".into(), + "20941331435492311592529607715649713508861806194386837398916323083940590908651".into(), + "2470912827043971002614412337239267059969980871643559631900987795139200233821".into(), + "10806505189594612637071931546921663393081238567888534876058498530874738324701".into(), + "667951375802630033661777802749339877422061577764798227349674331630120025667".into(), + "18416355600415187627018330134584431345513028652497077471935121971918269469363".into(), + "14167152054564590179475064444026440101215733530475912312508414765738108715862".into(), + "18633695428427030575173671831485026260967985663658201463236228419717189642766".into(), + "152822669216765741203342297512101138657182497046533047369566701489981099230".into(), + "13835701173750333056481994253160471551109858589047436642253159392878873667798".into(), + "3993942321148722649703549241999711668949060533276325947207349685002693878681".into(), + "15582244332423092177434976075689385819450099629893355758782548118218073388706".into(), + "15110236879710270343688993144525012407319759236015974251051640787524859884359".into(), + "5104405092803829419537383694663582438349376353030379488011426113631155364320".into(), + "11034886586481561934231698674217393887518948538322130743646058638919797229737".into(), + "21614370562083755709911993869347579638113152610927033622836963904672826178593".into(), + "11909716327216431973191112809713028257963610176155315584304717743448686635887".into(), + "9670047520194835060472941420215502268522351803257892125345072551055025494562".into(), + "8752044341583145728028411582583224350471084864272507077624316823400738066962".into(), + "20685513123216586620977713797881862528998788503897607377725195418550074311551".into(), + "20219162196364967181713755472576994456615542213293827108438968625041058321145".into(), + "18287830464300889532838439052863785386620820747210980263612361113628554829988".into(), + "10146051396529576924597355409059465520468869175466632446875430377637660889879".into(), + "13466459020798488583841582724067017412922317425102130151754649408559458307937".into(), + "14062280191830459071860023268317938748180670907089383563443465249500572357980".into(), + "18486553995294693573565546696966437493113894571993019524170031057367640632085".into(), + "11156566424349445901806390826392443373766529722049710427351550423908421767094".into(), + "209671637225069235519570008386635562520193585953162475265417907100134848923".into(), + "17226989944018790920809176115775819865824823495740082575382169759054625372382".into(), + "15644589951345053163188258692419292119540702867922222648564209455819510994564".into(), + "3689635641036835670663293726548900381724135109917216986885298700630212836435".into(), + "3367607896403464195671402279459329078003744183784952830994679539910724667259".into(), + "6227320552634621985217890398406127207902736210419315868051857823685244516725".into(), + "7357930890687295365886228617478473072206575811998185548162905341534675558305".into(), + "9337019296542497689612612043175604595811913796434346282222317112981594913389".into(), + "14658782859891978670907070276103444826326577838777644289370207112293812556778".into(), + "1700861002075407761970169168361393086239805454951858464329713573177596208454".into(), + "8422307882422345667268572118847227804767508317685246864132851358134342544918".into(), + "3824678171886439611637777800578730196591582015637069631407414390326082519384".into(), + "7520989644070067743500997565082513560943860081670904302057616063200273050286".into(), + "5278276919931895959830110725703210158384647399821914390314400092195592076331".into(), + "14590632939277529585876696200177152214896495867542780671631701634592299041714".into(), + "14365499645924743985349770983085181263329435144891175678390938245209017764418".into(), + "2519790270252875654107597063434691592006935573176284731324585122712988059511".into(), + "17688843544040778657269233842324532395371012201506418912518394656290716826075".into(), + "16584068781164994465207120381716024087231836173689783891650623302438290695506".into(), + "12224860044594664185598615945328866758529752520066027818906177267571423023661".into(), + "13664317767999211366109254182438581912610775541954425083255023643648887081779".into(), + "19324196860555787958873349597666822462940695051471419602454830948112942481945".into(), + "15338841226759355791277440652242849878000656382388414806186764010001628984934".into(), + "11076363155150973228897602285090741665942726007445165132980573631249449594126".into(), + "11228309866140794620879641097623963859536328868056691748463227126359575786386".into(), + "4762608512226640372168720665137259637840828925512114281702049841301872652787".into(), + "18282645934358125859102195916568492018711932725386725562892735740355836227532".into(), + "12803228415054755333149187333584509982900042807310255834005394843350472605458".into(), + "17675693156369747720817703064233611574822178844066411565804543111769294187197".into(), + "9900029048144575309490519431063332695303076438539483419053219772370202428926".into(), + "3684590949621971596368895784562632626464811455818343794800044114209066071601".into(), + "5443335602638685057982926800093482287199751584817191972983546508574786160090".into(), + "11352900694666160844325992247118358443639716695965864728670968730093466793722".into(), + "9836739435541786452166525951732520477055729763398281521212184905286650567233".into(), + "8222926590877635625730738050718327099397892409701316035188479123499338707893".into(), + "8154558268770648194631329585722892880905143452138234292827603893129808716905".into(), + "20661038342485310632612091028394348057035659683250957045340774030445861865592".into(), + "9136910062528018177460276667688174167129493547069053533874280111057356360561".into(), + "4362513385797089229061458501847196255783651860098500705320631416351847846956".into(), + "2061137061600029258110405980965338431925491466724330216028866028449889153371".into(), + "14607676885409772552908782897874144975643999944034675480739173900267789420534".into(), + "215346512487318428553079809620502708407272005519315271404209452927497999118".into(), + "18044026902282362371439577283764019415115969502361960218708274179281044595578".into(), + "9652478245641134951513165220881528043195466248948069255527062590256621034842".into(), + "20994154929281322813927859895894589885437941429166007529912073756113466975582".into(), + "20752721666010515144550782025078875036488075535083563976118804420187462745253".into(), + "20857028711523544595627940704882176284224509745902984714255291431664146535922".into(), + "9631521770540523913735742126933921923952197512938165111866628665235591582568".into(), + "18950423265182779471595998716023482060645307106263127634953888715515988505533".into(), + "1436791836740130330138273456892846001841969807914099860317370076565131805680".into(), + "18145299176463660895047063984288790313564980703886502044680749544519011424826".into(), + "7008134596456692891696131297028980612714475387065733972352529833092170154127".into(), + "18054087496593103261596842546955317831262607456582498514349407492750291465651".into(), + "2460661191051979147731673103829326449069370361298340160666765010767300969003".into(), + "1121019547339042268901204213478561141018690742635442229019134496736639790078".into(), + "13486140142607002128358893931572108539446504181590991898872881746144618091798".into(), + "14485083458755292442253176062192342099468601222388603924363708902524652589634".into(), + "17684636079328478898730536417772675839399177918554869673260926729643471105206".into(), + "12382939536995562937141167025903251534081453604974163882762565576243762872206".into(), + "5191757256912351314880102858899907666377813090645991709894707944196053941770".into(), + "18397247107649643640823283145149323187327745749077714626730537494597891967945".into(), + "21508632378351416585385353654317189405917247727406155133342616741543833680788".into(), + "19108354768686907995261340253443420621814860995097718380505789237761300853182".into(), + "3649609518051015699386442513879956346519312025847003339036530556474594795760".into(), + "11893851425092314587513815253407979901615516208632062595457152391110352908805".into(), + "13296593391067251947204447959241604616835056311051696511507435925462940176830".into(), + "18493557674615580922923001229788184231889430766683327472934879670006059540367".into(), + "7669746659590113244880799806073731587177781693253502772068846650012974230120".into(), + "19370654200032786851343971085637480775724705092664059950989935645178139099864".into(), + "1331955346226787928500793024038189892044219824334532771311923855914410290305".into(), + "14488880297827410405382492933041130286687512096290491259710680579157544248910".into(), + "6760882547908259908954677726421351194118695606292587659467769365205068189814".into(), + ], + vec![ + "6377232663526537440095439257883018477761342422116697881186123375221738885878".into(), + "851539971462439380385862352460596759101811723695394639617127852578681769809".into(), + "8777577262325190174206575699458733195047013200879424709893142671840513604890".into(), + "21694543997668766291509756109744969193435163886467863962355853609369758783238".into(), + "9577278996811393500051721677710083593799044422389686435650597107832854019185".into(), + "16323954252044716897246121150114593642230197187021287621193086593549237094775".into(), + "9789909425016820105251161906130605326280235056822272235912508431951118212950".into(), + "5766700650277227528545902607164112169119010038912902265869378685414299620760".into(), + "14342521005374081251816746055115831251291272287569749723238975882435091047876".into(), + "2566050045458470252423704003188705777658084864238473334290159653618543192811".into(), + "8762700051029310248153110133778709519032029454737126719215892745208105815416".into(), + "4708553466520767412303631379034292236924119642035476122997253385705160556618".into(), + "4755252554118675759917549980023743559070421272488077422007409392838436797712".into(), + "8781462767081720534606018702554359272062136386754094559457527802951016005606".into(), + "19167810216492792969016670752653089791475857662598893252819620255611011677188".into(), + "12379801295054424513880366937656969081677178004556540562031393564676230427743".into(), + "3873349522143254287251699452075145107916086554326675869006906246349942638560".into(), + "1683302064923931554193379270562867202085645938091131834974486624990867609624".into(), + "2777362700160137801933468204963311934247500177582714816722898763176642740860".into(), + "5330041075666088752029210636784758218847391095319460299231210692948196701638".into(), + "11849341704739004206642161112350419905150271791787570525216826204427280723792".into(), + "7477184099861050355308565098520563835117942875101546634259876195229073147282".into(), + "18811741129290507103385501216699521500514849038287903802256059864942452310117".into(), + "15644162092778325718506673614750051639809307056147336506838023349115605106787".into(), + "15072682042494620166496832302000289519302436589952610199010633012972669445593".into(), + "7385535266916101728534366006042662339391797772494836337087929961280561819695".into(), + "10606300178546340442574451452231017670874690381506662581848460294140286741651".into(), + "343808333592012682122858022517390819973432303579818622412786360520154826142".into(), + "6686378289544833739489172513893542192299224296746579469451376125664696638046".into(), + "9325668720082019512834072623751272154060473105966456255302021143714657867878".into(), + "13237356616132921941407245964289360960304194019926733744472216846697663447262".into(), + "1723892942664599421365079138681309575413323508685958773158319650163306910931".into(), + "6845174279248890961319599668687600484787455619619716546069389087383603254137".into(), + "14429592766972645051919899517480716657546426049902884218808698177731678278944".into(), + "2012555589304829161260427679955782678928810146332132910441113793264100264511".into(), + "15162287124358358727307007568219331690174000191414576263088727973180750593247".into(), + "6171544970310792508799412092397912280594923286679674322244394636145740843662".into(), + "11560360683323732335070294251287274796083850957500974817278726137032659811346".into(), + "7954890646285422519425515982220441977570181574595597355546782742910060927756".into(), + "2121066076676892095526555416241546752515994960009188371572036715916593676611".into(), + "1030002705665772802770205305890036009903459272665864721338890073927102958060".into(), + "16664112528630425414995233349042921759383114978671010908728891678245502701008".into(), + "16224205339340335222764551648549356562936176805367408466634736640263613613659".into(), + "5567916191875465998022755280584031341089937668974064792042640432034217833475".into(), + "10561503261915825576621563677167739482566911623771072553907339314680805249099".into(), + "1281495108038254322634503929806042733441491866895195579580212370919762081047".into(), + "9600700315845518751455006692480832601523246124684033595437676082879283709816".into(), + "15989333248905201890715282122260615227836016238448185882687783867814184655170".into(), + "3846245593630362971844915233982274952290718501967612724027782949411933001202".into(), + "9981027954269438386336412342904724691774209648042702865994578173145958992921".into(), + "8623595877941915162474742420309695649920307514068323484728910858137792561119".into(), + "21103940025922636831675399050467233863786411927021772979799068688191712316972".into(), + "18892924253208304354853962839524897599416779246859691035714354037906441368765".into(), + "17137414752196927825772499610314584261795745874954692214656847237011815603711".into(), + "20412422497099028107138997806006051244688526968840932637543351831550882135155".into(), + "11225636734520002481404086272590673372060731353304957503311626880321065568136".into(), + "18380442589598191047463232737740533198002604231823107797039491680652883496794".into(), + "1080201698768913889646664841066956319958767123758689784419321296338840961295".into(), + "14348402455238680465583355269916779409600823120873923092214453448424409970818".into(), + "3841435364722615893087024818655055436552226081083242159440517874888324292581".into(), + "3408210599862246992363134715624815235769905293647431849706383726509064300506".into(), + "12828232946525727915578787875290899244261094596690184893334123105536745936334".into(), + "5483797730688489537248191960281635992343565537360149542110268773175134131314".into(), + "15646042484365011867018844828962923289034117590475224947755290094723626891273".into(), + "2658047411395048849255440353544966399245817841159701280361972904541325691434".into(), + "19496407504291857422030801612379213952698163884670351003527359060477191854359".into(), + "11599969200544990318778456235768317543324325704256981991953010275435791017626".into(), + "12534635949431553834868179572769836881352677117158862189611147293522496413113".into(), + "14223314197724082301050736397492110631416043159307338723464864105007185825079".into(), + "19822547161504065277026677714514212915462043072809743766437313193660041742198".into(), + "18248624683501549165279508462273639851850239430868786828229911137041335077425".into(), + "1772507929668430466250295341031184282507314702999122972093244182511342701791".into(), + "12698826328883589821004991321815018437184890565199562478819948777685095680390".into(), + "2107256591274868946942358544310209350935597133418111664653447540003390113607".into(), + "8347096431391887603955816523197766644723983907921702200049607244690524226105".into(), + "7546736802459880596530318577784618006564482951653292089248497980343037655783".into(), + "7337317896163766810388205540011597034395961854295494001017429381228506327036".into(), + "7657535373588628884973408484470050620893383237179421367090832333743641042323".into(), + "13132621069544809006163499228792832380417930375502811639756010731409020775733".into(), + "3045981446877420174701593721028589257508837379178848429319604486793747007869".into(), + "1665034234802535695418712526119528879364535660712727125979361452433857586005".into(), + "7153904853002570654968228858836861252211159237410458977141045356668538557384".into(), + "20486065252261216388496191302294274939758504298167574880569796877079451248375".into(), + "21146476302842253436461025615017889905755773199293419435978649511293941220143".into(), + "4692883070549935264853696204165792104522817067387529940796053065681435988854".into(), + "6240088307004733902463222083449545201088681283438656231355545734118814247048".into(), + "11555561118019341206516697020813127391202363312629469259248586095720628837415".into(), + "19260547999655668000047734411254185932378393753746099027853756019009568507886".into(), + "20469506109273046972497148219051635976793704979896239651651205124084812945608".into(), + "1236647274759658638933992315999684238758461477931896092313814863963831171033".into(), + "11384423918232921171964979139440160725835135313593824548598134875347314405204".into(), + "19785250372370249720518667471906851686135385809175031332352733767471970846466".into(), + "4246521523867165828929729227115582186945308737715737226156399485201514735146".into(), + "9952732737001449699912255226665360960719170484486452179287528363081995818191".into(), + "16411145939754797754686024918808322973332629854064127851496226070432060579489".into(), + "18478056933955827759744830164752062474839918604932227276753757763884050277828".into(), + "13204687970556138498219183195522996570326298997850204255083662628089078309770".into(), + "6486083806326529246393301553077241033740361238170679962888274443184188794118".into(), + "9215573806816888307072467120643373006129084289252457249266574218300367297487".into(), + "5157456141970297671458245970390083650482632128904852982724214364911239574334".into(), + "17822680498490868828738779948851745357227318213932493146619109948725716270324".into(), + "8322423511882718045936027421959946860221136505721064786938368517081088404769".into(), + "1146280240837664421126981150154328736275224000612293306261498532925677882509".into(), + "20006445160687044351950305884447426432577260116801089758873885688911862838124".into(), + "13132217654381318972692935199671458140461723506405656953229384472487720023845".into(), + "2321904844688587860096390475332685957247396755436885306389445060312694195758".into(), + "17673723499361727802425795357032445257876321564734597671004472729727016538966".into(), + "17836648739374245973743495166940645001620159031723548669336786509810303589036".into(), + "19509523664323410269318198214695596019790796169778932865716910251136766472034".into(), + "21365014298519541792222476772118358897898683224332026502540401938408420183049".into(), + "2443777802329356458012563966932795162347891060116795715814546844741577072487".into(), + "6373148417441446230918754690291753760232604931431996749195267137425734054207".into(), + "17543938461501434657363693054851238526018672792888706636605942303973500302856".into(), + "13900881200135928840365427722717255359580153642574547892815287601924416317614".into(), + "9982616108044216660339683982954165936737826707471259937628917232293660834440".into(), + "16457765153339087464480638859689501343872608914611554385236118860039346779707".into(), + "6882633521418674793651640056518599843365736128725139938457347614662141371026".into(), + "6558481420440543921623853603383694405865402572023705828527015346924767906364".into(), + "21119564418700154632542186570611885700968350571284986971813890102274419338575".into(), + "15668498634043584871060292933787839904928585875204605204028656239629550300891".into(), + "17754567428791571673016885915321661773655444664247443414002133544771398853149".into(), + "11486839919314218506003227795241691164988634920977758623356018460082101365168".into(), + "7521215937712438604222096500164256001666624136838511497877267672752282058366".into(), + "1168385489601974578347279341199760237159478798101796567718644776242789923601".into(), + "13117296414138131801834212262010987517820472685866772554743932452738843734333".into(), + "7749628482107487230728683638475509704638633069294493147970362304145477016517".into(), + "17102526463093059579604328209955502975564943362848110499048688895825859834607".into(), + "11877937469390065191819717631885427975032604373385026940117533544572408798485".into(), + "9490483077873795676333795591814325768750891664453067983811838122306462917887".into(), + "10191097995163502256819397252907242166733175440759424521233105453843778654820".into(), + "20532048353899648065110204116821557712236052515292993984913500569982446829913".into(), + "17354150523998248091397848718695616500251280749145663998809707730330346369346".into(), + "7747355680464214426243190602078017576496231133574539162185621451748634683393".into(), + "8756715326391069596985357282435500586860011252775122994945356255643854963530".into(), + "21536474090524236379254986352553305027867958936847041677436373000730213533274".into(), + "19764807787330426181831011653714787282097837960248105858049804952757136862708".into(), + "16451394978386784206980716591328721244692005310628203853347165858510983157051".into(), + "9958807580185090358651106618892828843813431270139145526116189671892797920190".into(), + "8667474404638571999095228348352836564567923532278597424241711463350637692261".into(), + "9754527193113710990714009078343561220541479581342251031659489697296746296505".into(), + "11755501121260346797952875679164705763574741595098729931802001980529024314166".into(), + "17343653273660706017905395390969914833245644319150049521732399082825162090569".into(), + "1010378412861729818622385301577181571311206842734096023690850995284550560689".into(), + "12422787992288066268619146495902983268274452848893191113634050431879037454803".into(), + "13351916057777123695069150362950067630454763506856720439068955445038740053267".into(), + "3268740447474291563746626019604727880178668296496938516526099943483641022899".into(), + "17039539378002212101604857371251026489627400179253250833603358068705093844865".into(), + "12719626976782614661983190476189661930540289684710840404911817755168256363569".into(), + "7334691511591452788631693316255271478502517924558055271367172394856435073355".into(), + "4177321927122082121158728850724807513004613701936483594734414988911849675880".into(), + "8517156232219806038206488131493677748028421797072860831547349043281348142926".into(), + "5916342138159497146131772146268734765710150180676587683832045824388433290036".into(), + "21136252379072914855830890952695340864582847490462136128874077543348574696616".into(), + "10470343058787342159878702644341062172468027793693540114390435145428370707552".into(), + "9367903847960780027900264774906616911120367129803429048363499797310012009648".into(), + "9181708529218875829085211480957344367690955470310754169594385873272587183681".into(), + "5161879954208731149141751476094480416185338457043041781556700389106447006281".into(), + "14144603730790033561496908848503636331176898859925995171200576238014458649562".into(), + "17528475461722173509900495818763366567939364295306035018228011778228457876695".into(), + "449678858200791083139507971543902712490268199763356935472396275788444419520".into(), + "16677139862336739737616497869711537864422474730319606529800927093596292773684".into(), + "5307258894824770781811695261046705160386034275400321369052582330095447609528".into(), + "7066455454850758706236264136180958260707829859148436565416678574940588976717".into(), + "17464972536694182038180604993012612781624892485542021128713500864406129068272".into(), + "1827574381303563082711258077787177320452649985803311391711223171032580182910".into(), + "5005224603218153811845694200896301756909926774436577539307979565859865998867".into(), + "13761569869627153404623558442531816440237410187883129666707204029577726405280".into(), + "7377645231791556592153877817212695036000405157132509030366572797100109551371".into(), + "18929000938053222386693378771208400814166536156735504775432130183653620422676".into(), + "10277912490419902146704238375228669373848088391763413912903062245600018539005".into(), + "18100670309576234559559738539779745691447892297890679181953368845735547051936".into(), + "19792596568315968292503371918803163404916721074550555295039247624601141857094".into(), + "6568943725774928078767297883788056758405958920376813493652098209237338819058".into(), + "9708751028820311560873537735196353024741491886920686045780276405320332052014".into(), + "20999195425108372543557431885250084840784235258564621629580763574508120639473".into(), + "4668167020556593094762451685509419403613005848434427529155545560914783238805".into(), + "10260166712816802791730167674468655124354432849926388591536360669342400828562".into(), + "3588854010476278115364011192514859807683141010842317346600561233941024545452".into(), + "955642453625490778540666824328669289325333312941525596301926803494785606357".into(), + "4102026113333601512449185655242077481750021570821512149654744152647996823622".into(), + "12901729067332459436197297782018174449541114313233855185609986472102830633274".into(), + "1432400515841095916662233518576616625504866912337953671606919993429647264779".into(), + "13448330934580056368019676193029108114576729981976748604441994201646531786832".into(), + "18698774355061680075847219006041299002465669495077065798256778682757699200357".into(), + "6114255237222848342826972972054203376750041293553715842262723528673741797160".into(), + "8290432657858704891136963220676191057527195528510584103201077577863568506432".into(), + "10245893420315465808958329213978599152040050245022584266757834865645078424612".into(), + "16235075160725310956334026818354575166666144493784149178325740173109469893465".into(), + "6096223185593495310139379444667947750109489326578429517185410779366192202063".into(), + "15140535409353326038030605492985074291044727716595244779768492745470176024609".into(), + "2176086602170005476358821348469239586548222322021168089824748815230407069862".into(), + "13619789468668594404222482251836770591464929359372018436289664558758704681508".into(), + "8310543107961996371575168146304294641952910047046695374038288287522235989972".into(), + "7738370036488385965043396010845927300705713772735513648600973583706126470834".into(), + "4479339978160586717158719172802732929733916533373426058515465717943672025882".into(), + "4249199078635815430904933856748414549211196022798648243994671515262509861644".into(), + "4217767457132611540965149700331359170343442048612092095364557557503970133933".into(), + "13153296757017742961007840475261159306564053749538202546045661292791402275573".into(), + "4396888098805340064553349190742819413046668458070694926676742098287729413444".into(), + "16734434548572604008363129496559919254718993826492605430831689587940707338899".into(), + "9015659000250675923210953833943081286931414181352124970688626484896488861379".into(), + "7859006238840384066905305454236928888746240833462349287375399918976884871408".into(), + "20111156231978127386472936347996655414872475039138926155687856551161732442682".into(), + "20628144438246471187747981572742727430082255446467380395647482352559593647287".into(), + "10829450719086027299358038584474043478547531034689618353624096053194488185624".into(), + "3900905003848877440680433966861518022290921312029158328541666844523704712962".into(), + "17855611209216805679188603521771950395726550847102335142668673883933213178620".into(), + "3545647030011914165273791133303433140616659042668342263053968795372726840341".into(), + "12285059161807384662955183653994648298401051593713604819454219983692697182696".into(), + "11819552939527124997493513022814576182004246358800352547817016502521627790011".into(), + "14301577158059901977856927221571457807294693699285069296999743154546478489569".into(), + "3571634356329355229397931369891424491520267531585441552581855575412868351910".into(), + "4493956823795845864156868703591503758707793967931549819151350879135230170242".into(), + "18261935892851512416084887686097384445742684392402688030129684935728742717201".into(), + "11603603642132262206403092178979219208473125803069223229133184466185736048060".into(), + "4847487817017177565347080569283215504545281846426948697937793918200171528656".into(), + "4069745589764729706654816299792297539062872486670505242875943264008484198412".into(), + "21282151145529600768369623290936085172906963145870658008436208961308948924585".into(), + "10721916615176439690683002129869911178402752315827226965537395702918089626824".into(), + "20400924989628432852029073867249809947097995745931036434033577251949709693425".into(), + "13612038717302251316998414209460162307179960669236088330389280060785328588738".into(), + "2142054298626034610320009155682451576863946725173133307732467701538715335347".into(), + "6814074799679801916559787533428482395152765022569535278039545933747386331226".into(), + "3320993272550636151137746220977818986579019792097013138902071906802378678391".into(), + "14404588996507110096126959822135132305375204264088975725278990285893078946890".into(), + "19449156048766944910033639666691724350749810714682158108539166281157709899569".into(), + "21600390672948543610212878389553096169635598817477527886039588952230732642418".into(), + "16122909565998431497578901034409662715618749437754826295511086000491610510803".into(), + "3814026203802323919937341565517130280297397500968227915639071188757380515963".into(), + "6912908852134560099979027891279882003635111070588372993239339154823206466274".into(), + "8991012532130902495044589989450658026455069044478725949828656540931441650779".into(), + "19794616058753707346170576299297623557371037336156984230370345545981446397931".into(), + "2577593820399732466692625387687370505160505291664134162589397465829320209836".into(), + "10545990182245838392125531729060296377668723705525703355179325185018108067002".into(), + "12532592142366733026886391992589159605208721772700692225488484422366892623887".into(), + "19135911891605926936423877585461852787990719411437518367185457251216578059981".into(), + "11635603342092216271740512684448806260427922119693878652222869987036671146111".into(), + "7718247137511759231158297248913810065531288952022630354624599924037308251451".into(), + "15449533941190926955831618735652142785144234166497064450979389633622292725920".into(), + "14793399192194938994493676084408874396657844744757917843286252680102153699293".into(), + "9379880417179271734210305738187417887144762048262218697049423795232738616822".into(), + "10001585874846875226646763599153358317197291234293545431914341192928883246454".into(), + "17564611789675170872923370710570629576001514727841256489502945348076745512773".into(), + "13526676577413987112607573245391605865887231830025935262602066777025060147701".into(), + "18771091487566471187260929156402254761992313431761027006794035379840343952064".into(), + "17672790933843778353408361605666344788858296349839035375070469185645819063077".into(), + "10136925806345148466019786355963896194230642602748938687391144254701550469628".into(), + "11669469369568255529354542182318275079656673415035219767446071893709388727608".into(), + "8717156787967537877037123555054580463721012975068417092258849467938967272751".into(), + "3574689732222366081898222156809576015147776290993716837975298246375762980084".into(), + "2936447189567283726966987004410103389002644634186413346432130822474131530801".into(), + "1699723231413680239740710996339133622208402062115605498128909025253321290927".into(), + "20239438661176091033530372775196947702401783521122338633601531691101072063415".into(), + "509222199143055079823531599510182326541217708021228426946045537726376153595".into(), + "12460587031004227589188413497497959758507406039249371767737225675508588807598".into(), + "9172361948368872306701383997811949982264909388810393902913358291336142380374".into(), + "15024321518919789320143737927991052999071746110692384451602809887435324670247".into(), + "19363337726355099236128975299462078599854604247644073095764375371642393487744".into(), + "11352512845451687563998689687452223516295911399723160879302208800753615647616".into(), + "5033097489048897691788022265636063060230313291173145751178865731392231547832".into(), + "9342768693529219155995840295623046316860027403351256239528640640660995546250".into(), + "11343407235843410518451234635552443892628096773317032816539735746541252484029".into(), + "20271637634427257791277080766628733956630399511643807969496831759934416995624".into(), + "12979118904307784600641734806775453265865076574307149193300157552133646759000".into(), + "2609049561347471594361989318849223604030501563821990609941361801853208873325".into(), + "16638136645184843973996251461253142824084602885453534706330402604048300209367".into(), + "6290150467317840195062942193162131367777911299731759519747208884085640022080".into(), + "21846903793348064415550139579740558481601211918214432128739680250084492380404".into(), + "6881355315007836102200696266576401649721953812561017678365140505591991478449".into(), + "11991852144633415808902015898168146769921125504309617193757255165202163636329".into(), + "20579180498569037366675392921380645532641402855187025365767529341564826966764".into(), + "4127941604046459390852849136716344563624191244387948802112999500161867081345".into(), + "12726445769512078351769013120614118104254671239018619408743816227993876252991".into(), + "12822824504588887927083519548290538468815267612767490908011540889502830894241".into(), + "3525790082239104371118456157894419087904422095178764587616607951352330724979".into(), + "5534817540911273750657456222142677256882873311813581893871590176089715612985".into(), + "1615881228089658726040568147025008122728572958650432110106281742560315865517".into(), + "1471725164982594409495579793735446246197281099521356897919523009694047660159".into(), + "1375309198078109412495220212570536673190607625762682203229827372752214429058".into(), + "19114911117517497826908513598723039822664580418797141695087511229965144677908".into(), + "9628666313906709051161166309431160628627430386029173286325286404453712266410".into(), + "21852518218578549606473925058864730694915463701150591631085298334480610743316".into(), + "12775432117186202301959614842766511797651599815903402927721712100175714308106".into(), + "139892473068642488659633517109052420816080027074176062905422560867217142259".into(), + "8678567564479314009205848092936065091488089332028298130303782323700697895584".into(), + "11749646464324896227459490085151303579078783519261033148424203540751860385879".into(), + "20522934943803615109303532925965718163549240564060985032796290524829499285217".into(), + "20661244899066232726889114470941159662948096319289349895724712936883638757146".into(), + "1712076112157842791409364964341168524175271666408017461435914200921357859979".into(), + "5274198338007371549113715286886410970476178374473353058525495747031417868052".into(), + "6737897812641394021946938592351495323837784050553060267876717564065727066209".into(), + "7802413308864463219891658906834234180067201307743855789866460725804890591074".into(), + "10598878996622948168711729113266592565050867869138946404948068606726933771770".into(), + "8999501853368885436259381006393167420075229434053961224100936590306072807402".into(), + "10154159140828416502096070052350440839365634698281866510618130671547713671046".into(), + "10116420503162714112005525549243891887026278400783073704446798028762825422117".into(), + "5545266571933610687233921232979606259360579780771113897229483385986421780729".into(), + "21233107093610116862049125654360754386798111684938073821049243264439651570366".into(), + "5307392033140435516838295705521564813869712246929667484768640666687057034707".into(), + "21375317482759736213193607973501605926935171024163842355374965533706641104549".into(), + "2517892809920213533018018674089754443879541674948230773132283920676903837393".into(), + "14360345633113115894388501084706102426582517876835778852281477705852716869669".into(), + "17053269301717416242405268053150416723822210193991005718303172171427452536837".into(), + "9906602428995106334942925928993621430906117671319208771657883136890126991982".into(), + "10354603022009709342018106446249264303223237761462844795940043835225457441783".into(), + "13398916116699661698644750814188836272580610770712272177556442659081018605804".into(), + "3191149493139822128538617654520106298782669019011287540692938944153323592958".into(), + "4691984423256762483396977170887219469164634656601998705889064679728271695271".into(), + "17844101314007938193168524989091446035911779338131517299817551328197378135054".into(), + "635635940269936042377013194809642013073936660346940411744079076043939544740".into(), + "12630888356903717277892931534313436641457206712723665520324533786104375264085".into(), + "14140466574121870700874387789856251566070511990708575066948924661369491559256".into(), + "20370174501238434846223710470633284656345430614321812270231526837854520663574".into(), + "654339196866659831266130941576738975707930915283825590200406138066808189370".into(), + "5107706503321722709363385752813500904785775244074262870879244969961234309572".into(), + "12198789333458214522406820255828653820578540569170284513887146039452722146485".into(), + "16249136895399135618027043741098607972773831911496869243661084436243711109565".into(), + "11074204104909151859533339603597929732173550047640253831218390211736882449440".into(), + "17207343273400097590016935219508528858538698779767314862863101225959250875891".into(), + "21262587236682681589242692923329018584317630804742733118429592061667487058638".into(), + "2280753183511690306986430331340197673251265546818209861935234085004534230414".into(), + "10254003274920664842497725816382563578440196723429688051808776569200020879745".into(), + "7759959068226022198572347902743272598191759849179433231578496544131111538092".into(), + "14214316923200364820492127575076874888948881174491573576707716667988807435892".into(), + "9095544195644789922175839073929462149959586189940443682544660563790242551416".into(), + "12649782796197868227327830841571149621604197483943737926454728454326532192518".into(), + "16667983042241619901264223140344714132852698906756725392221839503187142280785".into(), + "18383173644675687530390651274503384113459403614974472825953064896216388971884".into(), + "14628141534803375030737634780152921077373561843078668533631869027351905379871".into(), + "4561456211241649459019745200365185624320798675863426759321227045069329801664".into(), + "12289778497980320566229781933786841623485008232338420865642173363101571189872".into(), + "11975067175623680843959076988032062157059134808170430074549557650028254491562".into(), + "13608090049838182253377471459358669610089588259298499702848843266957511619603".into(), + "18788677463812827554544269966162484344784391485047475243938219549074331613206".into(), + "4881667965195655156201239071358064948072146968570883219541497388280721871638".into(), + "16809375763554448903183355905513828131657823301396908506126469252462160193891".into(), + "11654980193951434743713680917141406309927211470940308818720103124789012440740".into(), + "5948986090649283108120495678646398398833638108013243775975352725637369743753".into(), + "13726437662355203524944601292802877111764435687388166013301616156301223567122".into(), + "17457684154769676997584569814978347304006793119360850469355899127743508915640".into(), + "12768218426713967613672155360932690915682228245638743313771591055379281067565".into(), + "18615157783057763780308448635283319685644271696762198401466573335150909781420".into(), + "17036103965935103170115214353570299052411478859579659720326549902160146217969".into(), + "7150547909167136034355368387927463942994048322819447869013206336674786486676".into(), + "2719491185384365067577935615529406253538979948934035375021293885648964670289".into(), + "855254098406697810192507782360318218871612459371212156821698546331701832542".into(), + "17895542168549626634871327801987932137768300191314912757298899767762396172426".into(), + "1291731410872228901975204398582554998148747251553901848273154130903421546804".into(), + "11405841699040163552814729751623598889450140001218648787184675782417123019196".into(), + "20354628821067577915648505449000078769730992376974982139779446148223962828730".into(), + "10728669080369994464716817080074001337835696213713101955141340296929303809038".into(), + "12274798389677182426524706446934567986140214102647161938159083665963012760572".into(), + "9739537323825422719175243872981907235223292084323462340852187679763167385510".into(), + "6755543715589769777862111458854566169790920787355600259792850662088606657716".into(), + "12126305553295538434422174339664597983843351746744739132557070380077267264590".into(), + "21398878068159838390422213444802849205194012142590812651530393856342428295355".into(), + "2774987220129009778448086748836270418387857350731020745311729965596748575420".into(), + "12913355749231079157637607439722332211156795881932799993926379679617800720875".into(), + "14002442102691266344434178456827064608589741346471232083579257521903268988567".into(), + "3222000382376546717389770889292702303294866053055569943089969635516575396692".into(), + "3351662579522271904655802780809448930926900026265684873057471441126697202347".into(), + "8791346592452796050710862947776730913908702488104309880248248201823569029744".into(), + "9103312554290751080966931020955359921020213554904162168782078094097630022273".into(), + "11400762474859869669799163954742952133400547669811496311736883651700759825519".into(), + "21705862854411927719091657128107442151318579935925212397760983799549487960024".into(), + "43293469326476059594440269130071583321324378720711508957751360314337673988".into(), + "15189563636069530033236842376689049504908622054060814965474910283072587672143".into(), + "8828581874201341688220445093730125081012615712886123840297097551587278948881".into(), + "4848334665131642773713411076824550175891079883511516252808850475348208758729".into(), + "12614662329834759430739626588497950629642362637723265950380896878486239042155".into(), + "14266355110863104530316810105072189601641436180167942829728212140901379263956".into(), + "17294822327168915058983104150822364346325840621309612804535067234675456850670".into(), + "455077676048323303580854786776522812365753723694705688462762996845811407009".into(), + "1883008531326217838820507370543781882290544271112198177704053499351851681273".into(), + "4529982458238957976485223202768823175919650810452913863593242825418350872543".into(), + "2955110035783314707155321084983433537364088072013879095266124577362993635626".into(), + "12967684942790110900749491528799008728014372120122130310632754743394775825471".into(), + "2505175694719163834796124853767853797404065196427012487113935936321224863546".into(), + "3664644243909527501109080877083112285282350623935581596506736195569817810680".into(), + "13149615309511991949247749473495035486060198385636687198656459619774102038819".into(), + "11675634552285986901460163850174089221624080337542386824203554725353862698973".into(), + "13792241875469760333807839408609766753472484847219134966123237707758891089388".into(), + "10227619387285566606296112062328719372414665296090743089679008143127205545123".into(), + "12205580044090621048077686897527020579927082420674484835775119696613313405371".into(), + "15208177648579495968812696435767989756568450150931633443711377393141060828911".into(), + "6203604398339912597796330237774861234098507297133105016255969208311447220376".into(), + "8763991852009928642943035844463232737815423121200913107779495834216057423172".into(), + "94997075732443240393502290740081268967228130793200255689370554790238240979".into(), + "5269706528648934838705302424114829386876854870088873864481541735257304941866".into(), + "16262872163060208420029150392453401749302720607531142669546071916932112668551".into(), + "8143677473154806611855981628530143157024134934286519271401846470427527147543".into(), + "11345763412284980808950171535310000445785575947246043879446971501610507015402".into(), + "11334003190684962190461352102674801284421167648666972792173808801060633658405".into(), + "16679481501466111466382494296272414977838924060754574747550155015005991797168".into(), + "8717276046214261706367755700328685033217888501922395351508483646143935055357".into(), + "7961818553714372394804939036958306706260485011393197807672306728909086465660".into(), + "11525513262393719593247460022637862437588876074380005892794471847411833011017".into(), + "606475753731577839300896422047168640216288859417610094202862133247546805011".into(), + "16216814510391154599441870283663624434960433363418315765904453229709017881340".into(), + "15952802892860522124812654660125132147349553268606042022775941410073735145502".into(), + "21186206422036180717988803903908106965745591204944021637150186904241796046981".into(), + "6944713991333056327587649834988524981544213700413795245346955488518554424877".into(), + "10185921515443194095530439914620341794217076478391514698779175769548747359464".into(), + "2855919726997577698604215361496800379220625688194012864111223639042676660976".into(), + "10433229026112136773123800168383892031427061365180473556460194903407247351339".into(), + "19427072191035469870347195569940358526740232693667547123354147821192789102650".into(), + "15644214414239800285411872105206002044880495869535621299007907341206661859610".into(), + "17127893436675976263942308327239908450163069123121279064123261885014993455970".into(), + "14398850115453023364458586202624524741971410970483408578015632196929131043640".into(), + "2352925542671141920909613532344506652984644250240272091365991738093178607788".into(), + "10172679680316429884824619312723045840067903978738893283037233244389430249509".into(), + "5772194565812391639885669321239603226420464592665765672957012371288977540856".into(), + "1472565977275775489403659554782408548523807638115405629626295474159727078930".into(), + "13025952383827165156872004962772766979272981810832459552230024272976389350529".into(), + "17564758020035152957739819749712385631015761969283204715352893305611222727560".into(), + "16405514590762165467156068648660873304776064039873164250553927264160414855875".into(), + "3690038285204558326066250230248548873947936562327070117434442040765096220855".into(), + "17531140782998595396669833094850381400841695523035934903128529012616296113949".into(), + "13168227885444297954910232183764567700890648537512256626187184942939569199780".into(), + "18839532764289221356950914594613087164434882227921649252297932780104554074257".into(), + "15690093187279544523035828189537219904387587938956853550130942856247262425989".into(), + "4880718242480815050016604653617276367751146942554754727539176934739417585000".into(), + "5767740184065294852679113060036925102298292735253600173647375101014669432601".into(), + "15274885987576180935369267696345371924274924872703997373047266442038483500100".into(), + "3761934589481780435501384301483991135823312027596840500386086136996196777919".into(), + "17076314579416028845718050745024649043011795764818118471181221546277962595385".into(), + "15977291426665432194805519962451891500931039499992781917651419845757747435870".into(), + "16614058019242709692964725060926415535596693985378737640811775269245183858531".into(), + "1462290731020299561638622134991867491216642860791478796650694102212515942203".into(), + "15285513431606451603488599544624561225569394909979737124945690710031449859251".into(), + "2108108609418057784389651014871434275919186190456064517027979476451544519949".into(), + "20785453923508177251995968212118965193411372702846000862603422090322205734041".into(), + "10673883530097564269607463521683039178512337022363698173454334902283390457863".into(), + "5454676180617583613153450533396644120109614173104204009158828769649771925347".into(), + "2308424025465899393970422846699273218513861797562089060060302466408498727592".into(), + "7521209677407171946300925747807233725946342430543358547121236461545495788103".into(), + "20573024021944297233730031260403312250074182051596062256738574515403295322803".into(), + "9021049816696497696749267473705619787233696328020950183323376943545740004437".into(), + "1865524799085825018345305851095200435959748376626055576269783687778950437080".into(), + "4246459257342056987998012962024687622955192847494871592608632692951161262129".into(), + "19427245695591302750338395658196139112041609989862338102918570832807123117503".into(), + "21526088318229932910068935959652342185575362039375447361171773752278498069664".into(), + "16191911454644420751852228901282854304136684923356871310294001321086062307024".into(), + "19158667047991453406718020435447139785846629820299132383267410641682384370576".into(), + "17700355947456307421977232728703295781949761612179494776757369350093380226828".into(), + "4397993077875324081650432554749300545148338322818494126385494799541152486594".into(), + "7333345984549245920976934899658584162392463672999208699449239323807521633803".into(), + "16152786000399924760932165522677937364378936868710087711558780050692283598645".into(), + "11094138063712503930108269043784479707229515809275605844595768164840752472377".into(), + "1142389644011176261530925136868504263430131536560167284105696375014595703389".into(), + "7947253178759456244011070491140902256859556283134055577001208685178146652802".into(), + "10134714213573683528928794197545260748403530346308318902760067810116839777206".into(), + "10883490621674448000789965625707254871804535943755669299023415555830424281655".into(), + "3066669045698349285650257964282463204141692000014900401186330770537030238642".into(), + "20081606580658665084706560391544166403289461596409550084066896490552960642479".into(), + "6155695406737593769799569079058291297835886186017961489006277167996716002247".into(), + "8515542419918302650566740947332213674125125387478669670818010011334680546574".into(), + "20110138544602147391149732696810223660265845787019234225223667638537351747051".into(), + "10742562600839811890186721654855708402836531506887842079602293928208078373859".into(), + "1525909842404376057305890458989201366856740974115647536551418424527131666169".into(), + "9234236770964972467234889051483942687447344616202849085905582739078803045160".into(), + "2606008597707158849245186414604531348516676367660138989733829835914698874012".into(), + "11796760152309688912186437926169763667717914107947289133346750725444306914631".into(), + "3851377590989618761627509080069107348568977801140383151662774695134986557597".into(), + "3154796336225549288800236024241376487736097402096695364582935878596928093781".into(), + "5510420220665221034838403251084456079592641926247054056939582862556776533398".into(), + "16879696361470925506825934570612427394067380616879319126544582610342257894977".into(), + "17284649816450637986813517327310560148892948427878914253815654865007623581799".into(), + "2833030338755501226260678429168722114253235114087325082893557705288230185302".into(), + "7251727070132476220556183457711849452579027647540936257172964240637390843337".into(), + "9575600794079237337218029036087316899348403790684983148717343996829930177678".into(), + "5979306342004680208572077566967420789575741134560343288662733429960668391430".into(), + "6933647198431187357226909706624729057428619440859131169201765838043283476077".into(), + "19168763196217781731628598897969332495895458624888700544934409934147951462602".into(), + "19829437720109810470479999873902618673477593710269745617035524777507262876935".into(), + "10801527657674630131387061424571233632050383122132208702068470808513632048030".into(), + "3324631869609728132956796851449123815871495758162118183753705724979320742705".into(), + "15660020223439851550062020388305149722004313049334322631321250103301499692604".into(), + "5967221174170233933880215190156392465136752773253659731223749128915793467895".into(), + "11552412840249358443126681313794466948899653896063684296070673264241626555196".into(), + "18888906666917921069717332466525186303442592625253328171173131193932302764004".into(), + "9002714106167484391528827546180572105064179104525774089102236612700713696717".into(), + "11048302793662652213631623254022096575352405761944396370134943300004028917865".into(), + "16407259962762436968310192538354523615684343650696199890141991953040162326636".into(), + "2164382359471710099213275616027528225843146809226250465653585796261822737346".into(), + "10534211142437421383941598776341383955315039281979396571221999789915575101599".into(), + "10666069653734320813631520107249402109728041265475244772290972049841823847727".into(), + "1065893272480854115563003062650162503958098706206084024581187831004515125777".into(), + "7996693710845330472615496754914137367924796497947601638820266498225398515015".into(), + "779460584637732468426955849206233546264346430125102128427414524469939157403".into(), + "18123487244613769854380966736011971128129435530397043581264858565849669710522".into(), + "16483639590865705054952634993109499660754781472434181022666579985294889582777".into(), + "15847717171578788545161470441065692492419364914649476971042627117577578420712".into(), + "20212509548766945890162316166677458350544092615956421310024681889193656903241".into(), + "11915838540527285091679839305488591026042934314354878896592571753630253407873".into(), + "1911170159637126384084881836969666801716554202345435461162520643252230842951".into(), + "2519681707616189243873692429706701363981726227724944939887394039132311807155".into(), + "10525548620009931418869190498282964825273460634626042574365806552478535675090".into(), + "1007287689974936217568163398662729361983046876939574893360458678319961439943".into(), + "8918601076290071318953673276836220960249911431394510238963515695083601259416".into(), + "1730552670090087588255812224149698933189694835321291448382024953001539943933".into(), + "2692212389857059051251821082045515561682372012597270034725442717100828072430".into(), + "5182752960118030069130081328243349382982053565156074602369672154634481788415".into(), + "4186223293158399083027342405464955373429347225209836842209182753945871255191".into(), + "20698226478040525601636369286446332773903307913544069889209490555501069209140".into(), + "7848961029873906562639009619165977234892385836769337804546327846501965814365".into(), + "13921935366749820642106107225856381492084238464963488385755265898574906919842".into(), + "16615913254044390064369275838835406299949293202833109553557777908673881552518".into(), + "17884323890135880903838623664692905800576770771455839982202688346472867240539".into(), + "7671240599961467753478651569648667266853727017714987691902517051845809028276".into(), + "20540170969725852651721054123169504183251616229771312133024479630289328600435".into(), + "10885911618876068300178014171685162848957603467763239389113505729594133953822".into(), + "1821956738503711086168534921235313501143676686594517809385967874500584768914".into(), + "13900019517803309945097308036151373598859036005433194049333364013307418921490".into(), + "5611521340578197244418404931199594069340846248086844299279812110836336410894".into(), + "3871676825880602831129014665958446397148564007925285835033897270539554583982".into(), + "8660883522091722908446182204212531338997991855689061026827047565345925404852".into(), + "21369377758054514487929500216341215576252815582773920801000959327560535036910".into(), + "2412441365534353741053138780929948381707801794421791967644299920632399699760".into(), + "3962365750665624098182798369136001622041855947680873409890054621414139629589".into(), + "15001230629104674518532645948322038846835064889071294141298776340346988584355".into(), + "14880674002301694876681052915629907300219131310128131182663986425102259721980".into(), + "1824158054562585980316309768551123828629842232225429989383412402628330419987".into(), + "2859240361399565691265380107352020662064288346296545444794991049234114013603".into(), + "5166447910779952920543762328108096228333459621541947567522578212254733957716".into(), + "1007490453560275100906582245944853446313521002614529198832736321283620962455".into(), + "12357031909288499920427512282658173574064133416008366625033457013843709684703".into(), + "4908601693798278768903637729333835430381717019653632816739154645536641464365".into(), + "3707521788266753982537350852704241176639571081938875735935799519005785641932".into(), + "843648473424733414090549470297108007717585086811320449394192229155836735429".into(), + "15745469357122732280411707850429686485179261034785562466267026969795211925342".into(), + "10805521614324416411410037641243182724170403739698507241460822321812492198235".into(), + "12065996971764501469604703952781437881524721789450462435182323617647626062291".into(), + "18470487586372818033539023969236693307855263683332928699228943202064060965843".into(), + "9678881733477238994400069917737562227773718359505757479334487914298656155782".into(), + "14188770590313091787681039051598250350716760439974403531322107017294575035366".into(), + "1689506610383677575502055715917181866056070036146379850885318902788859831848".into(), + "7506643906911078844726412866492372822643011138706243954480387034094577464841".into(), + "8878900588521598109866546176968107824348718386295453778616479348498221694856".into(), + "6870283245627392817090238076492510507322368840526195161203457308149059064709".into(), + "13770891113056770539767147807436112954402870897635701688432832537357791153049".into(), + "8780360192411651104527843471573635887257900701816701590232913021169255451999".into(), + "16171874311798197525909736376097249943669070404511356154621097034937309906406".into(), + "5735048971584791925371366175024708991668126697294479025115320761565490442773".into(), + "13229825087263355390945471875664433936256658682181329545626347347763439801377".into(), + "7075776235062840728475772028477434335481293337646194082436681309306082067951".into(), + "3621624535897102185304108550400513064448572525424419762329290603569136345699".into(), + "8909048763816678806291702062491546272181098732106818908949274412295033032278".into(), + "5679617488269393681126385387791795896201443046828191787132461584904242222704".into(), + "10105279386430545635207898937992374339634464824905552041741267814807859103503".into(), + "1875089455725008938828188584567435691926878726057441578850433616115151436992".into(), + "7477746898926143788312536189451854889707733370802231032871868774692083118085".into(), + "16386910598448767875426039948412876247096832715449382492730559004257126956048".into(), + "6033432876275593677037331179852069653085950234927263672572175689211500790655".into(), + "791047936106933006999775822964245334290371384592876255418502363688874468612".into(), + "3694863804142244539430013086697918876101028434198177049748051320963196192691".into(), + "13376682105243214738309576815193178135837123827716063891638620885853287922296".into(), + "13805679067029770984721773402288418271927678860935425166689344094492548637057".into(), + "17136756160106085935819446871311843510549583818138636089816199715569134828084".into(), + "8109923439508142364330425377154151984256104223553589804214354752109994937475".into(), + "11286249512402084538299269911568404171399278286810916548813396237061620605376".into(), + "2584359677745194465235893977254941998996461095474372764450843406506578038395".into(), + "416048929518775256195106483480946138344969950953407172345234478269849793039".into(), + "13382468529403508085121262401866099849753365548098731797553497976873204205709".into(), + "18439376867491626480684697800985907843592103465829293677253579863338320195923".into(), + "17849262151524731521717998935547978553691621192854239431428238124396600612632".into(), + "14944765584636478787586231666077419063063746476683640145827104348311750508175".into(), + "5770158920816863002535760768793204546036568907423666489027939717423021627834".into(), + "8964766646616906071571735802649575322546984330618572612076303377141821583837".into(), + "12741361066585979303854420163873489016934487106922559130204774618881492938249".into(), + "17155650977068989655844853330647332708031817408933941319367040661444377002549".into(), + "15853135222927269109853709968587618568449580274775781838704992776508260190624".into(), + "18643405831130205120436044797258461126994691116505008303058249039104682298143".into(), + "18381699905134802759607923737176411134148483921733890751508148644565049938661".into(), + "20771413818150322305462626795715653983446198751749952090107489229813229842550".into(), + "6565230743597462418756949526565014729968376635326046194605417268756666319845".into(), + "7773589221172310936780704579925742277511016906417542067089964629576305572209".into(), + "14322739515259154048217383149571029180760938501429981700631245079741667837826".into(), + "2211777926093208827662641428669122029599076421656786665814449320380720581777".into(), + "11959359909854207341226535525210590494408522984553921513181278512110987087969".into(), + "15631964544474412103208297614910610111786652867610037534463100143218625118186".into(), + "6469947677147265061709088012775208497425320831040679910475868641704170242154".into(), + "14273192286218513903657400373354426033843730885130815917604222619132646117025".into(), + "1642305946822726119533692746690241550779067996073465721872854402290979482744".into(), + "12434472654594642340204970901697561562492434882039006954954548364905225683590".into(), + "18258333676501263903599077497799178704676070138517903780843551776313851126197".into(), + "3154393734777301102615783657097778679162307608952237328943250741895135803910".into(), + "21442924273325571860480847088112128477994226721014369494483203254968684948447".into(), + "12633492209679682779165226815843032818326050497846354378961630197925565487104".into(), + "5676035885913185017175294353150288993325590895201150557890586619719458353554".into(), + "7971989338965840486933559372839713095797443968865735128939127037392334228821".into(), + "21734085994605079916025200907658739534755548178328895704833631044593482792710".into(), + "2240615781815075893018901266381250882106533308854256044823388761533658366337".into(), + "19418757086351270137870941178188715468785231202435740939860626745964359442005".into(), + "17658978545567285097910442499706845586224912649469751675828983048696346031860".into(), + "5266889563561183172939683250833485521316204021724495164611029326138684723583".into(), + "11128996685041402949333841527417312168812141699419947558499636572258705639448".into(), + "4876070090528783965375514438749842173086453529590096369168455769565070437109".into(), + "21181864929467364713132566956143162901336986129207766576689052262132856884478".into(), + "6923705508410326350945166370499325965219890886201185955752583121521122421736".into(), + "21369826512764035509040463750392604795868027299873193291279213169276615858803".into(), + "4704097774288345414783119239472343512761071550401447208590340029300816575296".into(), + "19335571041256357691347835994830129551703141581491004773597606130380555930148".into(), + "13594634273515260829141229000646270213542265375428591690952060475706589483636".into(), + "9015254066149037983117828022107604427546915195306105494160093361353622246415".into(), + "4212072016320343773172520900223699632828574585381041109390033407447488634385".into(), + "1102272748190005338904701274717935307889530536456982005034282115032583335111".into(), + "2736300324710728232038909644774832551915915663932530690557461126955645530086".into(), + "4835822801818378290852301048163831283974282641105089345434084063764482095069".into(), + "8984658187927452678459930452346646248663512083082894198317485712426204609811".into(), + "16633394668571656740012069555016467267297836103775110968049070854163114237249".into(), + "19001311544890254637467757524866604779430904938238046630395635640108752440416".into(), + "1519708912837962563938791348422981399413816048437970967955664087668434079252".into(), + "21331841844372791543710397499630585987940452814579176990050363535517182938392".into(), + "8907423080053260966677751803467685389883909962381980865910698188903614425511".into(), + "20461083176034684313938738010980183835826228331514122093950995340802162488751".into(), + "5841016603634386755712428070725428370463826644379529834480138063613116133165".into(), + "6169966163271811065481708286709332934104602048421028222790926506913444959185".into(), + "11123503881380576416779399092503011764979780788355198614717104650873109848686".into(), + "5910862578643213547409014086432565110080048906429291885019036113399453987536".into(), + "15326247930625028665035962093121096584742207235393215425729927190450227004180".into(), + "11869108106148533163877422279522558694860624980410176225979111384418155447592".into(), + "11642189959177792725300751489041811935498998846917246538427709581655066866054".into(), + "17383982459896791073091103411713034950019457760840905433100761135071197717377".into(), + "12914884881288305014413720371780941499071473169305332026567402160980899728234".into(), + "1208570539962036907751277903184876797538221568150857585830629441131315144451".into(), + "2323312703274674937703540014228631818729280611690694743543061204714949346767".into(), + "17526158529185414889134597474769916490357130956125485429641429836800727655323".into(), + "16037781653504828212828867735133494813590828457250361954137565662868257318996".into(), + "5407222038113928707955890035984954296613370389143420455278606330416003035740".into(), + "6601323218218927237946555844476405370566012808107313139506360075146521702852".into(), + "21534867504549849931394770956914633425223988570769925897030241609788158519056".into(), + "11391084204734238133980784274225569005339670690396227409155789097373857915450".into(), + "19888681657658973285687297761078939800531741094128408699392512189997402853670".into(), + "5332577406232753436405602237019361733171667669604896195130680826397119398314".into(), + "9059600173937645065621009092166260440988612068018047518892978276246308722168".into(), + "5301647598491778367767843695092267888185214608095464352349739315757388259345".into(), + "15071875841892141860455823800612444188659593796102096590428526661356131692597".into(), + "435778089283152858387915635050172265804266241934673566488879021420875384052".into(), + "7253178577349028822394834490127746885621571949621301778647260365262978601526".into(), + "1165301643600280009322317413038719889346104283514223548696059636052018656756".into(), + "19642265897347119192967952847445594557926149874981543320111550169192389374838".into(), + "18546070171142820328487316603855788669722411491547154393382552167546945234264".into(), + "7547722208697813989490512139624144757215998986985181672151192221427497361833".into(), + "21765466939205480830726051696775103538365853268148356693813986094780619132689".into(), + "18066916718526031105430041386116904278122614509807635157495047212689329422097".into(), + "5567247965934318360602471432895130358745239227464728512128895794990091560111".into(), + "5268155242325913085615961568740884974081158441192412337454790453319219641639".into(), + "15275184776514682919662155241139211655693258637032715569152004434925162363584".into(), + "2179932316974455074499483491372661376888522799119184614149917286749483028849".into(), + "4621405833740121725678441855520519041228014222881228746009997774516764304847".into(), + "18479339496682302897017710580780845640172065272400836860950352488372424120096".into(), + "18557033214894447033261016367420154444088029564584596463030003672186270737859".into(), + "17200647521217215683722635843299601233863161230648748208816378327209201596867".into(), + "2688200399163263049122461117289599907708945179806531635658550456745433215543".into(), + "17415923022624960613510570384321832458760537169605919976471719634183381965151".into(), + "8535568676654346647569376193583594529721160524940659464514765031328360321359".into(), + "2988350233499987577118772810582267364198360724494195915351933486792072227259".into(), + "6210620559937378902878629998722853418159056595764204463986805098114518009318".into(), + "18310997638100731876764014221687199913435941820864752235028122857262977059320".into(), + "13808863093029019329144013239638801163784179422135484562563156294580677444841".into(), + "12914098780625672320372746314998075565042263956380764233047047668841824625393".into(), + "3305953095296069725576430940963322680842934544212876613078125492458854044556".into(), + "17822002370183286453254419050088898676184956527990121400547957986510775899261".into(), + "14649614548507754711891632411080554775651572143141206447218484052001553765684".into(), + "8109105787614676314388565737575005920261787167569229710081822989465469152070".into(), + "10883787415840547710621559198296269932318487849486120162467244238276174398529".into(), + "19330421887539219495728995383446553835046303504116353064676615752269857951508".into(), + "10124601385542856860656799235536724683363368315551234368605365576161456958605".into(), + "12586681054057195636996904714881904902720157016325364273875514276164279959735".into(), + "3757165144866249284026269096089043031459271594379869902321741804365836799544".into(), + "462405354647627586345675602760959074379157140949064478801734872040710235207".into(), + "1920260561025181767428737283255569518557618670101985233779064285550376374001".into(), + "16427781514757709627041602025461703818581117100125480080881962557048804215419".into(), + "11285710669637766333985664845741699850953316015510407645702772350620723934913".into(), + "12651159368793078896654108663982020308442449053255003548814357252262150375808".into(), + "5416603170144910655254279621683623582654125103494176617805338836184837812634".into(), + "11735378404903665808621855768545179642393017572035737886573368447383433091210".into(), + "12201557541429330882554855185927797569372240536806738921957792139394552229540".into(), + "12315234857861194987612137097457466405047598720463755079786381945885951345726".into(), + ], + vec![ + "3312280834382673867321630616941760639861515464094877629805120494360011490649".into(), + "2977163727414618213643725802224710174200189681501907689708278449275625624600".into(), + "17077799405481633745546679084486353025600250694578521370656758513725453934742".into(), + "16210306379465933080277173890273457210762404430253284947889895341785601089390".into(), + "12306944281360832043572721821716739944034354411349456264300159795863030116042".into(), + "18510116198433364516004461541873904955505055486081639325191415467980353992665".into(), + "20171004681243290383397478639749648403640483231061520437292129889020927831789".into(), + "14216817867363924461443287436236130110420963767734530602158091056747850914504".into(), + "1744244489393376249430799522637230427855625055124494576876957852023791759325".into(), + "7129585843913921821110812028399979173266826966429993304814409727296136874103".into(), + "13988404968409685403326663187671698975733642730286219911002602918437679901860".into(), + "16920638563061193407835052036305459051417920006020130846290964059193276218943".into(), + "13427470979587753937642822251845898994765547507421045262408080469011197338416".into(), + "4951343674183369875263494313821909834781608226183187917185914650592996842297".into(), + "8735432128376864913532575297691608527208524295369583136876379560735670033436".into(), + "11506191123509320764309490571835590914265043835723578099434891464809664894670".into(), + "11860937155601787389575216601739962955686948385964703064981923542694893487713".into(), + "10828014814384016644406621862814561467981545748254521286078083584574739936124".into(), + "4776443388323724363690349417451836132533385226194261923508005690866227183177".into(), + "16639991477904781874568583121789695884424899367435699700192357030587346907362".into(), + "63646460855616590932366082663720069120195784636826940928051354727568777173".into(), + "13872046592464170980392921000435473279488582870563366687909512908597101541579".into(), + "2903974073963148433036990643522953333905364588702098342158750794553468542208".into(), + "13972003294927197976860685316247379324920372226280483226377669417062686947906".into(), + "13868504415208879955736997036981136359599594082926486546672963310629609974499".into(), + "8628454286960990606041697926325623221475249983044062173729208432417417750989".into(), + "11883151436851199698252725037318235080760967011947670552161788839027156740653".into(), + "12343286145222861673187914724623142745362406729785896500578210646320713733295".into(), + "13240444180513188371213070455300718988708567038226911680564203350475184135088".into(), + "17225520781620743741263824011599257737446498734177523099795883619924078366351".into(), + "19655364901125310778629982245392767297984820167114789563931170716695750865678".into(), + "10695212283696096281132213692300627346215324066996398339089365835910730561368".into(), + "17455827037964560948521638828961298975187156274841334841074216120311558804710".into(), + "4382628454630801450077593442523632674983545561122254229775876033334437860489".into(), + "2989679808908579687765205532943224072820070594304724633625888454150084631070".into(), + "11805110513334566809098991762079653200819786354513225301120697798603509678793".into(), + "11155601993809199490094826448585668454159517005968097566996288487138255635079".into(), + "20815808565222781796761554019974947099963702926722486604443826341123930622896".into(), + "13761986205307878615460321377264044874010177269812603883540272922422883198979".into(), + "4128370443753246025606114169149035926146394950057754901868026654335917771101".into(), + "9124459754124711043815747649012164723935008821275709951889811488049411892152".into(), + "9090302853448501945809598864330384875455576758214379286451409039855851539841".into(), + "2611158043123761410817152601927516372222739024190920897371539155273224585905".into(), + "7810155525349201315441227323507184944737106980192978514457375337581213081055".into(), + "15249792920950763850993517571920601821568311809174912509031437118050063777525".into(), + "9921562618684201533699293485620188871082795988970107962425978745591461148033".into(), + "5414565587238798987874533039507833069864773781235217517734130524969939419113".into(), + "11085088558016600803149927568829483866020077054827488081829062938808919799801".into(), + "14625818170426402577283649455001357862456467374537021301451880577607106726350".into(), + "11633423263664104506250995123678976351167359685182986641330218055115412525059".into(), + "850899676511673590463501492472742017261347013078251845952824541653369171366".into(), + "1151196396804070641219917031443763313740170846357000808196046482153806050391".into(), + "14335865562369989392722415692597018596062508514718062574213639729550753584518".into(), + "16165235837546690396792041022833296046736592940683337326949013823518030323769".into(), + "6408098377682813619850142098164780329355428625834955152400958576961120931380".into(), + "21423078154100258346688828020904418548554497199114189102725137286930868022538".into(), + "21410217312460027364452766052404349950133415202888862471159726884511205456308".into(), + "11096458349387432633781125315606380225216758142658673665437375204143306261827".into(), + "8325016245207932555950059013310928498109578689420634828690059159861280125058".into(), + "19120379171572846193451984132488836287003157955913585086569467602620121544962".into(), + "15548527549295346334479152412121638073047322389003226521019746206475690857238".into(), + "18510388922178653264949279980605433471616208226685385338922150015851847524587".into(), + "10348792093961922144290405029634579505420677504732581441616360506905620826544".into(), + "15620530873228503134208333316448182712901874887467615537478242394290577386453".into(), + "20675095206674416295505702734315255961699360732886837937979451051773488542601".into(), + "21623072135556656816074223163769126264910877148099315666172910436076676028248".into(), + "20081866552983231525843337497912436927329172443916196026679813450860013966767".into(), + "18951602909834406133815764463249536936478826139929746486734639774975812472830".into(), + "2378298801465585253495854322066504889300467395616155363977621341822205588163".into(), + "7488881447347091058309495011922138185162376888083687541108039236558708660027".into(), + "7225960736862847948475548065508931552528126330579521291190635548005260333390".into(), + "2970482373109443685421063487292183827150496377598629996362474558885993864176".into(), + "21734396161383902760672518999845141937065773419899625136190973118533490737305".into(), + "18986003927424880427453510819113519633513383085919786483726156674699889468820".into(), + "9438591792749742425198760386567115998731905494024392289196006582112950891516".into(), + "1973470346155075248881651300830631935042830364217214073906277058522882105581".into(), + "13791817954605171888781711015587425735543149752618332811612012047724803625120".into(), + "19907506629242812934309078271817109951335091294976452370249234742626462372763".into(), + "7913144809845970358468253448033359382532594356254540499933138806226450398795".into(), + "18786719915196826164016145948631708915544945593653634575689240810328854731069".into(), + "6216910690440344513687669938632060553287695043116654216600911463719413604341".into(), + "13963849909448408572632889978998938642084255012365277702934245702305832007005".into(), + "15044749345099947962217476120263824356898437745229321420257770262954985403569".into(), + "16364542436173489908162544844694842746117292595057249540926274681176755002520".into(), + "18582462045999492294572047486602601352613220856614634390669602502489215736186".into(), + "17106926147340558311732938581031578597532846523271406141897942990261560966411".into(), + "21322204968737434192679865858477095835509790782891920152044718985713652659654".into(), + "5180799590809942717590072710973007480225145652031514943521639316776384894144".into(), + "17327427769240537767056224186269899694170666500703858813203303301835294225360".into(), + "12795226231932513901983143961810913124288321956641191561823103283353203953207".into(), + "21372571405151778511626450096485639101933984498294796899325401511071517993005".into(), + "17332300051629640554924347563178488077648079908152291094969979591786473880576".into(), + "15196566643676483159109284476812079407469293035334983717971092729005139246767".into(), + "11231435533605861369104078150337976075098864909987541459896164149690464379912".into(), + "2542242881481757350297627187544723135156543853134475677976341602496676452300".into(), + "8414397814915895101029770303847330205837773505982623383796193602695811122514".into(), + "20884120591077768210550361293303217187539921930869577755703150175746557517590".into(), + "890887689926165781226591337399563217015154445631688026491595317246205069769".into(), + "7911456647213080390137926613780820211986390960677915664339039845274033704843".into(), + "8086506254215085366863905182926378819432294332631815038288539116592866850975".into(), + "17073560037383161517623747613125771654997798954263090273202401066518468858416".into(), + "7837662874931236958961077774488634261606966280628598881837875736120392183663".into(), + "1858593536177571875355498014960393860692560832490490994119420708212532861823".into(), + "2029815832061982114925482969267067531992443107339599745974909462412297854269".into(), + "9638056659007828434670080110322180928336402529095853481855440674860675897358".into(), + "12340090961747329883452841216385303705169146011720720105809463143491658552866".into(), + "8529510825845236935037301291679257193837435275552609577171369718448324660013".into(), + "4191225223853834143599110761318464286196261585898512902425773974473427757456".into(), + "21023319679135760820789157052868478773370737850886002789313940767424256194356".into(), + "2142526213326613906831098262446296658577569180284637200640763867788179991570".into(), + "4585042017455545746057351957720948202590802829528217263388726369695890670411".into(), + "10010708638531752335740573636867640657039858389864446260449096531856416238708".into(), + "12350783923218275606957028867683130968505845112714635024839732952215498877130".into(), + "11964414264447046767815837108295373861475122940092369903025175128013924386713".into(), + "18615506936496648840383399989920850970951406181285583088268789582149764054418".into(), + "14310480127014971068920522498055725465578544829224822580493909725079522528728".into(), + "19209887743481507043830894286806384055543596834772181434542567255388724841969".into(), + "9558553005299269735641084020216560749888705968385627813150807919487080187993".into(), + "16359179198438658598155755638987769554841536772865629517841526255130745409879".into(), + "13009386603860183254204895522650012288119732961876401784040013994677688723325".into(), + "16165167702867558446013999607792716532235725666199142971707303433269257270111".into(), + "9526255138490973975321568846030367381138407886665647949792126204681244064012".into(), + "11173959697998471600463637717719289778917744248391051771189332133876606070906".into(), + "8924806383303834750473706479936998674798746387089627706877519918521882863256".into(), + "12086306023907343271920056592137012844278234154616893254064962297944646888123".into(), + "20523426725749375175935122656912302902564898343946638208821839160006976692150".into(), + "5207650950010803883388912523741229153368067016192964642079270816449299041225".into(), + "1323145474328028634780912405048390126320417262412962953050466352509016682042".into(), + "15985641921260285694054233699922160344414776687043687582488491933807565444789".into(), + "13031771899737217701535545098380455304311851135903656399978477292561795000214".into(), + "12687226379083740035799525440904048416465917656850081330730343990236073218003".into(), + "5180214195408850700722613770944601169370584228844657891855699394648642429923".into(), + "1934847650429153525882808430984088614158092322287601778579498068360011630130".into(), + "13221691213669397834454903625729859574410480120034103769424898865047475910400".into(), + "7358428584159841472154153892839275459119229492180053472830555381908577651936".into(), + "10890590867941343184378544765218808644776443230231834463183835780142311216436".into(), + "18106794932751537043075991633023418950145862470753313409722049187095789146702".into(), + "20653121417027117499755994750040261936029675942959877982604976723194002882577".into(), + "14917693547715740204091617501230563717468258023142717832164274356453628117609".into(), + "12588115399854852983923905079011727575933343603816172388719899888494451203866".into(), + "8737187804839661132607601320524529444256555649101505512380882940221377341037".into(), + "20181178129036534248081631452735124104458169744675071995191727629040780050092".into(), + "11847774883596070919125373409969812806188814542338127843683622869859801757028".into(), + "11792352762436629909341171173296810156800556812208317414203786807634354583560".into(), + "5039046929001603921830951923781308597413960825857893817665425268295870771062".into(), + "12894400705986579725245788498699203221654445738734999525700614560206785491732".into(), + "791109735149055364851227398853119460170782218988367975658618461497391330160".into(), + "9962476109282060862882002156903460806939219308562214446822701651882476052460".into(), + "1691961422009775486402337895262889415967004356867228407841177856000156435254".into(), + "13137802539241815005932694907183711832661205367415214501122657136334369092290".into(), + "14887252261014844625921142022701177088174316290502741584519763408145964517832".into(), + "4710184813279024135486906523528903891606006797755655291767343324947631364837".into(), + "3801875732725114698561125696836343357073399124260852478317754268545097096329".into(), + "9198233237012512530002016044631468704269680036230256629514440524866699707396".into(), + "19696310211066978020836317292330659233588020682368119069383469865122133603780".into(), + "3893729283153055478198590025767324126269879907832064182575052081004216821718".into(), + "17970322286541481482483545514302125656124172449128411566036569217140609597331".into(), + "3173691464765770821621367532348960211199783691357143005417355252781889173280".into(), + "2243542900289123996173306626857758207765925487218044091235839910321809834188".into(), + "9813067765525696381929832954440764980538359181380263390331469145209686852182".into(), + "7510903347384066721813722580189668539045635640361656120910145998846374499699".into(), + "6798424040308056706713925899327404372610976624998869962003671422212921682045".into(), + "13151104198292309578579089832681402880200764912297978086539824832412224275732".into(), + "2808835590734075710953411057250272995919027932824027538775542577464926767888".into(), + "12588027297759258617444062604210692398005597744782878589643963326768133974361".into(), + "20852428689501418456174033597851660248293428726014401905473174242849532966301".into(), + "21296758342898944987847487225137782473520558597036376704831839057822676021941".into(), + "17749269130031982625604134600446353874123371420684509963863640790293640598822".into(), + "12696062143950532198094822890688877675740383270827145944853466311043725527586".into(), + "10234631072965977425954576972633890272283449053659669736631819751429358646105".into(), + "15090656934406651728161310654614148547687401766770336347796859883364028754099".into(), + "20297283938807526130287519089364625872430415084303524848853727877999978340129".into(), + "4196012712072569404315829685460688625941300450108222372646584487742105064258".into(), + "7048902004425912498834833883033670218379565315927717075817051690008258707008".into(), + "10997088676112427186865690409423506551772833502185768664867716439827194601092".into(), + "16660341545280246485425051709291678711850828822636537086457121822086705580853".into(), + "7507040282824500274626435275567162035280839409294222666356976034789594111255".into(), + "16582934771732736721752353176711248084060824111646755305570354937871610952840".into(), + "8438441158635690733311342509710555256926785449251473129940736660330253698675".into(), + "5997879363655967621652122271982901641685668225243330913415373444659935275798".into(), + "377781155818540738714095913828188470350179572448699653767572496979822660266".into(), + "21256042944489939677834126729197446597005830228623173527229465226360970832340".into(), + "13462424045682641929202812263623136219335301688716109209361496824976128063539".into(), + "16369985325316675741289392258637854017776166143141100299636544614904713590628".into(), + "19352389303901189710313048379405187204819083083443266317372096723889889111665".into(), + "10276552469336314142974101263431204498273961662600910736694134016860746072245".into(), + "18939965576088516958025629296747895354615121399053610843001474455787438484084".into(), + "16990956322664851054201977827659189601868639970054898553161570861097544812211".into(), + "9274107214677498542007885890977699632511964198928324688228114708372115109190".into(), + "19938102848869576488504727710754065647382136279175127752338128473551869244856".into(), + "10947279549049485525804912961584261533182913140524900494781425525696586998449".into(), + "17561385783620224695956276284426542193322408499898012489406312644517964425011".into(), + "18549237431132768472559432396178388563409301393950605704070488347475233640622".into(), + "4658944912168763919889884489643908669977608714692544551960581141528777169667".into(), + "14358732577825715965736448712278683090180078342840106686279626250756225140484".into(), + "12281258853616996819181958497578187729715864708853144319900642420503103456870".into(), + "4069586715276899433893814672543891090785738315323966746105563017207510306537".into(), + "15392338154382717986608388646339008932890583126043490476751054331156284554276".into(), + "4517279215584833397018875849825808766631742728356316383241841280072723947253".into(), + "11495036315995422771228762992950108862694073551665420165823433137577472792783".into(), + "3488400840286926839516544735442894876148193865245875295688572119503866691461".into(), + "4483680161275568275540790250446502401130629200290761781427443716038558729253".into(), + "3942379126490099533582685851970104926928015397351808560877929535138811915511".into(), + "11401623410099323944960285754389435394954200879740972352984594598244147798394".into(), + "1704354226179051409424294551695786920602664449209459493691051505451583669264".into(), + "6126483205301561395856828080510799275633402498512671992649940369928504550860".into(), + "13606061849345999397793916269931170782757704442972401347887622528829626440142".into(), + "15164492401059698496285802476110470458129619713288710918549993367398699891422".into(), + "3351459252264231792942138342057542378418534651418845818443384064630601195925".into(), + "2825813294993932054460723098369264495005871082754465730530978747926493034970".into(), + "10630213694333677464069113775948160876773011480314305970870772509710784295967".into(), + "17166444840808098079862920467594387590120730690478215313452389257289409232274".into(), + "15584725536763467888732670366582920601197464509911998898151204840990765872853".into(), + "3842723796922381697995350228115144219456021393259361082360164730224542750465".into(), + "4831891679443026612628331848829793784542170442278947325598774522135713804734".into(), + "7600866468317146506151393588648544489445105617254544624521183070936649742997".into(), + "10583687645778325936823793169279337717905917984785560251868239070559362307278".into(), + "6634607933824414124502549710741812867975263049249231353927669720791801705509".into(), + "21200609344869125199593456019818101515444150712862469998882355110348140005358".into(), + "14361714336789127058022203168794208348007919167174968797533361168719704534209".into(), + "1575806469795254200103212820564180016945577757735332906646132503588215912546".into(), + "10628306906313535336091114056786842183955113155572070406112143573768388478883".into(), + "3089301712473516586340147971264840334080312453733093398291612940108596635146".into(), + "8645795529068430110488497380845362622141451911891312096721911200350500642213".into(), + "16022924422796267113820566797422451700895182144910797483992348492987850640540".into(), + "21668559624091676943635408329170173151410363888407430983622558174006481336306".into(), + "2072754212505196807037151376956574080111414803663052483852517767215769333105".into(), + "344312312360359816789223515659978323664333965848417356296539814750513103748".into(), + "8220091259162471818305840126401210949950597324344081712931466340258084434570".into(), + "8081153019090920515448830088459971939645826679918559179389404338475619307845".into(), + "21449241241855558649730447089457892378091011965030006823417837265982774602267".into(), + "6067510299282418337933484892144351034696356612154920492037935939636907603847".into(), + "21026143172839641048848076059364862575379425612028621103404211601411048352556".into(), + "6070438567473697541030303531314185338530006003062193748841965554522662407077".into(), + "8255130291759907832990229603685682560848952190335394603030993899908706198932".into(), + "9546680581091867708464426246566048171085730057856647964775027268282058986619".into(), + "19031748019605060953238957886441808197649034817040423262874632275713470713752".into(), + "17096522586030137478189913833428299115382271406265963567569748424828047092166".into(), + "14391874539708946761001075594593462740437739522325069415219274013650732876769".into(), + "5044944855410335767979170189040984719402886766879289090117403978287180516155".into(), + "12039442478120524220891851909305716836138223152615593874775855237995395028479".into(), + "3523930243951081542915282337519662387190599423550808129910485998252637662407".into(), + "21645562630446380089971004272761942543158290220498842219097308729146973947053".into(), + "9635476273420949200636403482900193969961822409725076345852310152023632001652".into(), + "20108896868320980092863545615065555118659548096203643467891646750271137801454".into(), + "9185755445384903374596265352827852278482253313517817304360514098507824628136".into(), + "15729448650969486701474329849747687235995473855623732466626603841221282908291".into(), + "17997496197003273211403721001065741564292124821674411166555153239941325690779".into(), + "1543484160386036195130212436149054066156454121707864502325718576708010327712".into(), + "16363634132599571248968010449034783328746466576733841612125967259037812456762".into(), + "11430550344567145066773020651025090506980499195413761807852670685184554749682".into(), + "6562474642758509619875130343887830140546824045253242049540363976665708088792".into(), + "3689788469446976140319903688077885060368720863836266384022598571957269060684".into(), + "946161443553297471232017467760713330784613638346698353392670707850882897488".into(), + "17775152994890017803033120420023910950366134526347402563464114996856742054838".into(), + "18013595561755224808896799093133992096629438550898908281663802197342115179235".into(), + "19550347733494203322271272376490635125389454301454207200256111665786140278650".into(), + "12387591141491143807678990173849961323772837586376210707337202980616282598236".into(), + "15615445216195972598868146032329101929277001754848917412471279643674750521844".into(), + "11157377570334948360516367332475698893237579888172217677485190937247699033770".into(), + "7130958205578403403951515578418204249480908544151977781217247531796937245530".into(), + "13077463747714835214281685128657845926832593706922024570198457201391820941874".into(), + "20874866618752897980777992983172936086725928829995747401713185587049763764007".into(), + "9529341163966534350579350780474173060797076273832542721772111561732792505333".into(), + "11142469391388881243303156644691476683457853027534314004453288809998298235798".into(), + "15759641247899199119184149872340402267600476479959598970774176792522105838715".into(), + "4932052011278518183097054145820640615211087138945458940865038794988474891006".into(), + "10432003521315545222242540576886148188507417318972677351911763831484066879233".into(), + "17415623007807480250676335953220588929877876653555913668269587494499596716638".into(), + "6058502442301803313347169632367675883843035525640290470399543491756905404121".into(), + "4937981332834016148179351587580087161307555148093009345111656135565068163315".into(), + "4767276541447291442260238717093680167532207332378535289044385385412677713720".into(), + "3479278227276561004751092786681066281912177752486503430342235086646969267132".into(), + "7943278285140068849348170498689330975011840215116541312829451500334428180149".into(), + "17789865444817191695636689430256327163274376252861234888599341795716694863906".into(), + "11313788642745794682263060233193097297009786646629777858110154245289610960322".into(), + "15451318802981646436556355654707114069777551322432702430595933898879175343955".into(), + "7517270022604650172540378758621070031306040808100514057036893386092388842581".into(), + "10009597627594120061689516706238257978394705225993629762944338270984243419952".into(), + "16287228641302237661911798504390175297327206600486567815634358152959586818336".into(), + "16488249714776419229986970659608164306511103745532215897060984940903927940510".into(), + "13354728687549843942265910857129412843774195483459170542959107134210371522923".into(), + "5514821690255816781301952402733589181643174815400146100135235253605240813486".into(), + "5770103106430309708547950317514210165673407448643264387718101428086149468878".into(), + "2694023365380190286709306630457152471177911482921639231487592222438630988204".into(), + "2596360308486910596331175398746479488324950265788021808703834738076552537175".into(), + "9039714509260386661160667890165090643713802650277924766536703740888044388658".into(), + "9604908742305952316295642934650340892998619318487917447839433792965436657411".into(), + "10397877232904834686750242142278095498016920001087513253065011131758634311960".into(), + "9647356378465005298504489726754445992380752731297711041297431711301119823740".into(), + "2901124847048448064485279495316677205686659456282057720583010224075223188929".into(), + "19604711253175713682339631659824057587938105276478055043815802354580551412854".into(), + "13814073762260094871340458282144499467492241477826265605381954643255652812563".into(), + "19191277082146641271114112092268339387374938745710902056016346575725741574791".into(), + "4351438127825195342433511764688522108908810411430938576079426214421798123584".into(), + "13791103210113222833666019051739255444684344392875106989928046003279497788407".into(), + "21004705702783423413965111534445421935934565517891538415275359891592731977145".into(), + "2271618466337144497382665490697115072125990892802659816927722507215828359501".into(), + "17558902460062424775395394774553055038697408789830072471787389990226026812332".into(), + "19541727150539905151936633341362726755459275099586077305409909896489919664394".into(), + "13692791587272861333747931194991249996571271400946738920492512367046518055252".into(), + "16261859926552105019169611795692937520291616941485096436900016176047007140288".into(), + "13364750885171681175943985569408970262762845797040643237076315743322239541667".into(), + "13621972138767941419375174302334978784396954063748685093059444353200957635883".into(), + "7816366958996751828525267034982378444982423551198678280151852851213963203655".into(), + "5387588357364980194077356588147257085032575072648295273044945567343418708153".into(), + "5561769804258805723102666784326040807434856561620360208220095378441903055863".into(), + "2759015372384926293713393955452664141507032820282479368223024343744582995438".into(), + "13981134543106614704319578938205751472742054796552637642168049339888724312989".into(), + "7990257879146478669584929211133379459065259065980981194790047745364505663434".into(), + "11876127927930413842997415069288338406075999315865409152476435081888707100248".into(), + "16570551097531678234333612414250188586376313376162262758608423816369347322204".into(), + "2912142129158840181609698148893520848427743435752721748118484524307819669362".into(), + "21302711682094568025057587941584825132522581272692339045586256377620698610739".into(), + "12312191496853376716581371884132892074646637957572159612619070514615509157537".into(), + "6766172753918389200603997763713298721859536050788836585740520353915599111166".into(), + "11495484070470791342676704007304510997655718568304345126153023374072812971624".into(), + "20811664890504244597768955277493527673608205732118233091518542968150115526854".into(), + "16063703627762167667966504135478113457485558849729490090362731172389730931393".into(), + "14866560644094965611504947304610358591133796414154206860995591970456047599477".into(), + "14070826318654672995642970624813758954945583937085129314124463556918282721543".into(), + "2479865882631638665546581282869746245766113042059506929860680221143260292935".into(), + "17398990555602339276684995684280302899074008602689308101178965669933564447317".into(), + "608876042484390223586509032355843816374709085426455763238940304845396291392".into(), + "19093816648588316664468783915406437520968219066632631005915358745999525844761".into(), + "6752057404464894771427129594636749868828376343179986455821808045648379935245".into(), + "19888182199106435309043037013966294783272588579928340631894231065863897158437".into(), + "10082296017960550499269875102368612265371627577709879253045116718197037435449".into(), + "14616172738414901831631177911088055616005574740810051337096702495520738285374".into(), + "14075121030661423148825920501776601474873747801291899275268094618775991305078".into(), + "186569155903133115291205664388893859453551519692185893083420327670792162388".into(), + "21299949831814715974370318270705239305629929085336938013302553408219032200083".into(), + "10073139629403946009284887013173459030563893496973388848686052378045073830551".into(), + "1412198475257996126786641438141729921783447169582336707894995512036569315500".into(), + "7779964092821851708726534513875342218194584080198381624983100710844846404634".into(), + "948257338952437735532120031007610523249359469819114853137850805544224196883".into(), + "21258332661476723168534084283213459804574003007208501208541743948207937371185".into(), + "3571248067320885077052671052657329120832155265049255115581589211873377624342".into(), + "18767668432875868101830472546043294158876614861521938962321471373462978174897".into(), + "9300839292250613795491408219504842564421473000721822643979051375966405606193".into(), + "7064697496603343927134791624768930499810256237088252983584462738613126284623".into(), + "11853953981626198865163123682396255427116155876021473995289924391523536239496".into(), + "19771876990095842377875081223218001588478929046615599374496234715810020326293".into(), + "14316898881193279100266501073998404340884941240727243537292505454856307190130".into(), + "14519359582636080510571261582400983934670011637905562203617314435861115398211".into(), + "15526562931305845620715599917553561556373761549853248996847605985788604583130".into(), + "13078262487107679022841832022599167182597313495640194703513299650288211974881".into(), + "8245559318515118189428376002847388656528865763199194533482833563489439004385".into(), + "14003329258302578586391126522614048791257224051921678844212914592624869865893".into(), + "11725000422609501650997806599141176528384787601555518111375908215801780233121".into(), + "16350891150329350826705554113837971740496662498788954571303936439413078899269".into(), + "6411076187467760129645481080704474318810486795627076355267125729971512535475".into(), + "21018151777353913573383620107187764987866574825379316352340980146357643078382".into(), + "11358423056150266238825561031091840555431271536659827607676154506546242135862".into(), + "19065833030377987569441381994102534825367123386452612836837050555888073644388".into(), + "2562667974917478689245733861669928078902565222389031120733094553006681886960".into(), + "3471326938285931978123873023764646019622642761349433536231294291602304868485".into(), + "2563009390241284210001442885151389460822558817932742264596703331331069807625".into(), + "1840397005312580276560341795534676176103474806443827954038672349702326279641".into(), + "13608853165319143662474434916965520937512197511217746962619929048696566631710".into(), + "19629513285164329969551640449451527909298649847319347893901982383162686335860".into(), + "15394390010333989214892827255905093646577605209323130947826658910240751517983".into(), + "12111906652703403073758441298171362667165880553934054594322507856217299245492".into(), + "4297972575899320549167090598678621826992022863628750628394635867928789323371".into(), + "13452780308396674980210210234355016032880538147426066947029982271570560372078".into(), + "2831837563806484576977992168295535809035697292555489411927347217736327891352".into(), + "16435334356674630023459784576727565801947528558756855054741604645713144334059".into(), + "4127342592978010203327455190780821692230702238156658795505993209368069797198".into(), + "19169399182658468533946629204100516992531108319585640665183465573285725904462".into(), + "8598406387986750884545663645357594697293280337319305106433909682884489291980".into(), + "13371924317816745712309163118606793190008424593275425616115063429517298127531".into(), + "6966840677637301630181597160079353321163372268840965853806074036299182143344".into(), + "7360050884120437815429010939441335996325758450523147733433611465713273074289".into(), + "18472779235592140583651215638681270421642809888489121199352426980884551699243".into(), + "20418315222771285674269327640703205319629639584244730201946908647328012868945".into(), + "17026399731940643956418206353642733600139323525975091345342870617418039094945".into(), + "12615211028716399762155462346481728092959316603121559521948867446476882144221".into(), + "12396056279980752806946158389419170236239219009894918041607073720841998512650".into(), + "3020267183116758520311575446823726815087071183512473845404855371056317502914".into(), + "4656542918173262538192266136796850668036544742787442489947994092170369122585".into(), + "6651580153776746866797134628474708113133308603326248742448594405127096716435".into(), + "11937440848975251505322944134185172341897441221500443657141975704868428110206".into(), + "1361826966800566560222081321267933925488529241685130208478269338961124266689".into(), + "19943953771229014728170188452413560878330164558859793245021220185499985258316".into(), + "5000555778712004519940673290591853777960346285299451454158729230859752281548".into(), + "8421588432304272431559534311544652626307929129739056740355326524785204863675".into(), + "20295164315900629557422352618552527295246432971816033569783057482727719683327".into(), + "19373368537162638225474243050103347264636555313351270279300938524044041811781".into(), + "19301584842175411162376576546656332386749822306022512981007234449440059593206".into(), + "10803063363791701506227503212696578672701333662552932509544627248354076467851".into(), + "21190447586310863247879892773912245039992873717104751727825265558975676297878".into(), + "10001943442280168315414950100438358800275893831224620600570013850341287546634".into(), + "11419108205602584425734602189435177828356990665164795815514509894518490834107".into(), + "9176365528060199110731574560388345526295591893454204513780413140739307927521".into(), + "14645264015971411259428809172021065485340465742768211283864072707152051283922".into(), + "2059680394752128527783641175137415159902594391639901766513696812385555753758".into(), + "1362210878544210374207986445190412893419873862912439530157828224644264948744".into(), + "1767020623225215375042202191031932704135774649037849280381750341452687445659".into(), + "12814227243430147774278313161816717185894261558098935373927981841389635793984".into(), + "3650392274313221017884542662769899210301363377135174682150190146413571526895".into(), + "8605030053804448166307797789010404548793085578661466947278557642849458591843".into(), + "16243873345435203695859375103717652101719490281058055690129195139240095336348".into(), + "18253688126813406708458581768662237883226849901952565733241285746065046225854".into(), + "16894577548232574374120767026426945561995165201786189772245769116072691636300".into(), + "6423863987901820348928069732046971656713531289251833729857903917381295249467".into(), + "2313252375346549956530493743826026444705500798866360084159932659530274395286".into(), + "14351648867218654569586186362113600409735374191851776253843321710353903071560".into(), + "12748602702906129786642754668169435482057869301200357523545630557138115816573".into(), + "9709541611470508424169916733595066328177420935464065317839768365879702321494".into(), + "7178910033786777709713498702843323736165699178795466682345863412805925816311".into(), + "3353312211975166615897632007255179570864390972587901806985844706830647691440".into(), + "21333017166590824659869638126407263965780255207193356400088734092911593056348".into(), + "5028007188794597820037691911407426228966175740057746443994889279713514676932".into(), + "10712123321028508395224612143524241532236128389312765281020405990000255535908".into(), + "9585591632158092894254183832493550005011787447364800638915694118244958639233".into(), + "19409055735214428219964362626158681016506721676720000843605743187110212915626".into(), + "7013515054956821958187738653419570061640238138610196975216698509582854871893".into(), + "14195757172805460680435532998286722310304400118130745528870652671654335152142".into(), + "21642947172805342858920270288264451136332732954445458690700494722054302963125".into(), + "17447291201981220908729619112892222175870413627521563259423394750686485556049".into(), + "7142752135376494645636618098683429493620847661912582079125447091506892873792".into(), + "11965386305718013298615263282817897360134689279593443592649060011294217114060".into(), + "3269287439460269094755519346810063037578116295811194645623863516503159243267".into(), + "2159120529508493457616143785556394024190164584915596617279717229000244189542".into(), + "13216089736556667969033672749174760723927614320685905176389819791237404133418".into(), + "5739580235677403106179902507340026453834485061930671765794215270028117113169".into(), + "10146496637166180435280660177294964473638207568055892239396905092844948797979".into(), + "1559489237751880726567698923572583310388557924219345825351973445625480867548".into(), + "2596197625765120503438747432434144655985802369868414306286213301002200644404".into(), + "9161937832793127953923149604860857118908349157525975074572071369698144813595".into(), + "6530106539927673920262526515085998672101393587740796643755609207665748320714".into(), + "3454043461578374982297475090824066193432500488323777575207325753404726668622".into(), + "3344507163548616813143529013320782527939556912905450999614978312225536850079".into(), + "3495919320773160724078129285494188621289377270658655167666727615227933095154".into(), + "5516726577701478990891722326081439827260225708304904918582005618283443949920".into(), + "15205180713187999862939063125387810274995561110852513151225711366795547591281".into(), + "6648123555675422871529580981673252698816139021904779960285680145979314385726".into(), + "15841058754930028584592543963971426098015345537516384042315342065435733026794".into(), + "11980445534925903182700789375967305825089780148976633769394511063611931605378".into(), + "13752792603594569707463845404055123799321888887568435005345905056744143696677".into(), + "451907199992567080921227130990235823262116394775607208182891587168397627680".into(), + "18806959341268394516719451015233616843300011700855654586308376758853129828525".into(), + "11666933396513215238559883168577675591860013867821037377342211197150469047378".into(), + "18261858072527055096686249862512380033913481610225001662137639137871862029384".into(), + "21560391742011390943072232187892257373961479111877969400490351307926336615662".into(), + "21610883506787217615067001898587573081135488752397283684823910785658887961932".into(), + "8408073424459585535553155824732179764716727837637392334926567945546036849813".into(), + "15210371269884178529776581719481988665617111145372052101377092280056914506280".into(), + "12126920168856651470145408021353708994570254510986190646194434994722466503351".into(), + "5069682791790920726869832051575701989172948895827442056320603681340757243410".into(), + "5206562653380935478898854557078389821615981777078527007269200218469247241072".into(), + "20147037355698495391549064646435751868583938479186070809834102358533096530919".into(), + "21648765692141622487379563405180427559394486974406039602691179361899957186441".into(), + "20151743655500786952430656789085402151537047806266385436562459773879995501836".into(), + "5934394754413383977455838937623522376367295023125030069688154658727645145368".into(), + "12603931706566171371403975785994747990660901389421654410200684089833302667483".into(), + "10026374951811566765702661526223829303823304592138671853309110795200798195351".into(), + "20990348049520375634430049514368373247447730079841716683325070058824866344618".into(), + "21619384435032220705428130345438986008128830023896849505537357845578039663569".into(), + "13891656325513979012692764566925054630715484836057215275096343649598208636064".into(), + "10516755320080836193722788097986010349704472844888704193817055904209452357159".into(), + "1187924241706018031195408222997566778745910130631759501304388015015493122577".into(), + "18176236836230977144874543476758297865658817104610686283126581582188371550406".into(), + "12485836949850082215229286502965907740610097753180405472080386359153328488121".into(), + "11294821091195763819640094447903397628795163768975434879119673473115769322204".into(), + "15235618132532149904626305329048631193060570832794115447207190941624057752196".into(), + "5247748280389397626917162873894276536157877881047135043409628106456158304439".into(), + "12327729816009770868743571696284061855435639336028566901611215454206952326402".into(), + "16602949418050455338609614423670909880567257158111141482461606053345944875474".into(), + "19481131052077011331591495382924133179323957631986844309716638852480977994007".into(), + "14534922095860994138017014871440884145230839148117198707882202478464336849118".into(), + "14698138249781405938575978017673685687915842935570150501408655390412744761438".into(), + "2268825110634009232539217412695133214837154986621027045784924577498202527005".into(), + "48564023730318244345588908855562476416102457310638224830024196372769683274".into(), + "13911429573364091016107598756829373924944177606409768266066645212252032518222".into(), + "3072739629129037084628601278178937800555134277149245900071628775426363271424".into(), + "17673164704069533606602950374212967524179027652656167791832581708629862878595".into(), + "13337993423730131992645864661190106010697884214050744564788997133642079615746".into(), + "10864361443255322638825945640705455870794346349925711501403405779308411872877".into(), + "12776251935559898232336391171201768361485692108948381787636663233683642720570".into(), + "5216617488642405414264845781989153364217494006051772154230377224854279460364".into(), + "19003202417167005941636726827739223707886908723467287344441657245420838333458".into(), + "1782933238160323149082341139973263731861637960761110984576354403382733192508".into(), + "14049307335094461452125704957817909153045723261672970005965857346428556786126".into(), + "602292860913070228058919352868668861608870961459967730841598276439118039884".into(), + "4532222937868395031497077142687900610420285824272982687191592830322532698890".into(), + "2099811981776987988652353966067242893298763890123265819355994207644365313244".into(), + "14963517993601358850130080519783504904964131507655857293183966569857011289997".into(), + "5628630657751613134050345890283140633799644494629647136869171968642164797575".into(), + "20616991532748403949152312405329497795753361035307036075488592441729795933793".into(), + "9449735601866166994904286297362590639752895462487835902681383517319419222042".into(), + "4855933188947772127110139132590210785552455770275991391814399396690242166773".into(), + "5236021116842873402180213939626155398638345682608182461380158609761274518585".into(), + "8831771317829874397651604675303149337495578376535991692269465791612651585623".into(), + "3088758799070584868972772977513492687395394215140736055156925465547111629717".into(), + "9647320052871832007164877116869148540664111922421469306708391658695763892785".into(), + "13710265959446629407575266748628250489155448638018691055085275421674700880156".into(), + "3990937300186809757319839603902135620837406120640478356311388415953195593911".into(), + "14183797850975043036582133930342253303942122026304366900989797745492038794070".into(), + "7977072622674677196160507229659485240881624765782396719467815964585050639939".into(), + "19241094982310069445586766283605264689470697759972104264399773392459234417767".into(), + "1955047691838825101723033568109446280275825112435318858983042058841632309045".into(), + "19123726709557210987172343550395139275104840920797922301604764165695899093438".into(), + "11017985516143054945716095457545428692165844636350104202156363406141221710733".into(), + "6698050162913212144220380056054973138261583127187888344395668170533632399574".into(), + "9966258961247618428931643789269822918034016436345337589754558490493039380634".into(), + "5992044103018260979899747087179477222620402658511576502983751749529532417546".into(), + "4323136703775346257297028867845707816483717723305960372757928628051725499745".into(), + "21794175040927871006132858751099842549067139289302899933403157116500725993565".into(), + "4135205477235797120209733594064542822286242795709961420067213468324705376738".into(), + "10502424667236605030274589825431626273558506810276755098371543260305259735173".into(), + "14846816339594032738719600614652189538461600190221870378597670293625955865265".into(), + "2524366986896165752091070306635323844562001546653320749202855717200081979142".into(), + "20323646694453380576115350130108187509393437723558716335846490151009228206070".into(), + "17922328316124774235198837264790120266787900323072144966876142176581863552114".into(), + "1182636510606531787673615350114833983759075243161678674924493347851641505038".into(), + "3207389967170019411093119580069334588690357331504170475184259410760933425232".into(), + "8369192092815635351510306100580533943309224255100566350954698456188479225517".into(), + "12030967886392195699293618525716046167148513246572831396936379799444387335164".into(), + "14022141444250217995147289887822373716179013971005541169826207913322588624137".into(), + "1819830254074794257713310243249746692467827341614315920377358118637602832394".into(), + "5214222934428733796770137135908910242359102117443480657729178969918179851403".into(), + "18138416073511031005117669443972799853597050093891426716335475360173308913456".into(), + "15787040542336098200132680028140910620950068073851656692331927514281562129727".into(), + "821824702031897351041935750619058900741428025283533564678462466715419856156".into(), + "13945650681120508256113693182498673378848691411623235404408416652983710360306".into(), + "21565424898814963463657730321537398782420569248706699928677873449861325896156".into(), + "19609389297655053996872511820224772969554473578452634334180619061868015512289".into(), + "11052162512235765382251445485034453773059444701618444976454738201341222665559".into(), + "12166351899911550354968692629609980749815884172876848625284742219657445788950".into(), + "9357004866878440721090407569238169828150449366762652916034388742054494762892".into(), + "12114234396159313460221530030197986658691629189646106573755288837341527114909".into(), + "20697910091918282046230152102481351960195609070206375824763685775389954486953".into(), + "21217736707553075351032987119547432929010871168087324138739787787951139001106".into(), + "17381364776399568820088774691862728632279841729367582228657079823795538435769".into(), + "20943148878105082737615418415826125298002846495574584317442899738454169666093".into(), + "7269215523851335046672197634746347471983448536436098948086942864058221294848".into(), + "11453469784625194830295468663538063129244082440999065497133984430619118688913".into(), + "21018146386490226380790917971415394820859127890200962707883416995754459780694".into(), + "4499764602796942905287899120539899295087557389188509075023605344029409757976".into(), + "6253534478331781543005728896589051616547370996281199587944537185086057029207".into(), + "11552217017582571341399470106065323830802102428782340770897703340827468796773".into(), + "10819374663920577173719455195284609222419659549106826920462161534187158953031".into(), + "14349899656862395011953779288216924779057701185080247070042551534943737976358".into(), + "909510146269051589147289065017979551607491214019225341737807929286505047312".into(), + "7822991049400510485087251716608572124487352922137650148085367861004077510787".into(), + "725869830997965165130427627260429551001667760503255008495920045139979612999".into(), + "16876874328062330437058858063404447748425817762641981976045186363782457609729".into(), + "5386742002628492275210178331450945300388007254427357938003565527570148075266".into(), + "5628231453725040085705711150750978455046683883113398227942371706440013519778".into(), + "5207331605257641510707403983352805778295440159078505882865873548456617745902".into(), + "10535586157456772677635305329613970037048119551546559771279117980166707049697".into(), + "10634858349767776643334691454267563140934605084766184133586786266071708634005".into(), + "8194830686373761187394649827049013218313677678976649876752071409303459578949".into(), + "7100588172111463994475475315895964854310052675103208262359133193362837864219".into(), + "12698529806569743134165801249102690453926582725077752506169518961941228191732".into(), + "3192504222109013167359339531059877081526474219178099463731713708944911915094".into(), + "4863002263813052668246974534880517465192842993905851311847938713684622052826".into(), + "5692128778265216733946522388190452257048514412064102477216511313048856040820".into(), + "10126422206202050217110361146045359045967276838268265874082659894234609632351".into(), + "5816818864633980489627669987512782856651289984181002178349070605011354887917".into(), + "6571596820513333489228887403836877987952738840623146829273932098108053062770".into(), + "14089943097204317257370418561461590134190932157472606744328891293369209454647".into(), + "15650892875459028952140088123225234819706577582764200222337535746289113550733".into(), + "15234537030902822319330116055095343429285315370864285347784850359841976501754".into(), + "19442144493035982963093880999374393289974808397308087180018619507528159896278".into(), + "4787721462177588970261549242306006751465139812381940463247588627972131957846".into(), + "21188266388402646362923963971922131649215999518682418035026104849712460470697".into(), + "7670120618673949888866627327541465118295930474243507723982310418317698793909".into(), + "5278076033158881738611446681468941913572289692389442858337761263328869905538".into(), + "6812114686357362596670812436414685361641526159356770435320544521002690748983".into(), + "14512519858142345768369977582630083215640443350401036244408402584958380156234".into(), + "2883669043634370578290606585906686768752718215322315377961330865510599230166".into(), + "1390031372189262099215439174834826964629551818632345640955909690959488475368".into(), + "2621604580741170950714666893803390793334876512814103152393976192943909783864".into(), + "16238046050182542127054230631321316896861946156551854572617517416519877517341".into(), + "17244586605437271554745538098470023522850216211045683035769492251097903894816".into(), + "19367233841767536085266806989570452145418274769895229567119343509637411304432".into(), + "60922694691641059020255299196606231058588207683679919735176896931621476211".into(), + "11404572340596870708576661187798839793333697334049619025922813709736021654992".into(), + "19297195320049214749532857744175644917624562154332216539771469808458364580570".into(), + "20000477679980055675786972902810034261882771620364631737011618258163102706803".into(), + "5145215345848317594229128468995966108988375785838942899274867896011577909800".into(), + "12745405048448113216886186019434813760835051470566703617056298583393105029140".into(), + "6134671602520394562619244398452258969460576801019889394876295515660617122846".into(), + "4914589878992627637690178006894707570950584383330674027731006564533772285206".into(), + "12220903700447451099363813812957915640017509928028867600267100643383476677026".into(), + "11628588304761135026225316417404056298817057087404654319307918387873030085506".into(), + "17423007615206678675839176821651284038190814226425327926691523730687305701329".into(), + "8965704645750924279210398083172931641857031322145961884520939048845530423732".into(), + "20213900813945518409362660921232075042618186257655700796304283970465683248159".into(), + "14023473417795456589963878717505287517669917599805973068283274798230783465678".into(), + "9113725901633633321816296721315176702107873006937820201651085782607150391484".into(), + "548172112211359610960958872299753762974328909291797522208085351481742658142".into(), + "21706861375052844683595820457538688901531393763031119579322611763049792865067".into(), + "12400834297098033195346480176669546452877127463842848395269580039592533946307".into(), + "18693408652472113120418656602007998722418946699666834214243410738193362834090".into(), + "5734025542584829419095063290697837279993460564092839068131370421231150830964".into(), + "18924630076511631254945042871273344212804227110396594419618744375738473997458".into(), + "21814215076304113439159605823570645338718666334965210428557308969012775849083".into(), + "15874497347478704100595228569012157674761828758432798819588001847304550645894".into(), + "15630701992043410338260010633128892269216736379332650587736588580296378591400".into(), + "21182316539430719202319661920312710080481096926576230463259085025268931402724".into(), + "13427632056110349253603837202352903590918428276805663374267217475392463207302".into(), + "10911219508671048309975190269088900191221091803425594935965400900508004506426".into(), + "9158857576095512666804740759126852985161480036845995629848333137432364744220".into(), + "3061727900948996735547324485012991687515955796128872254459502313385493651603".into(), + "9484087600338383596528641950981362023967545451847364854943599173866192324557".into(), + "15244438304843092101886329998640757457334825678432810119480500702489267475870".into(), + "966872391220567196912397735096848312956568724961332613947113843660023937964".into(), + "11463695793311663093741054432425795718511021724948561649345772770204331282701".into(), + "4301779131461344863695073848029046722184482171568650483218315300688393400530".into(), + "503085225024315480816360605760267420357374543116157267432728664696709665901".into(), + "2152733644489285468694016160698899213530839938744988003049661235638107035210".into(), + "15311799268435674361916200869320882308334015315600749428161144697607223747926".into(), + "8821276262247279528293843357774532114304424863546634692022624148120506964899".into(), + "15012215707563410212327951685975753138452396938302623171526151974041926043490".into(), + "3832600555460338220245921307060619972944914111584149730708347861160918312276".into(), + "4945512356937128186743764272299580525076021077689098424357488427223926571902".into(), + "6352619611110096346414873193740535134097751304211531527845737802795592283759".into(), + "10487432688972604512516548109465855881572886261854521534610720416206960307854".into(), + "17622489873780167264774227253975567347204222125869234241462457566132215071928".into(), + "7633353550826098030742486107449393642153730338494606903118637850284315354119".into(), + "14024635247358847977026819464107185157647377185834445176147145227141238505750".into(), + "3839080266490755896783758766005919524636645447105234209420661161997449147283".into(), + "14295400414627547924873035695592309131547135626435254888679625991957098691157".into(), + "9699346383521400413122931609905328425995515996718354291677850659748262244552".into(), + "15892775179188924258683368801772704553840476123482448500492733919344870447160".into(), + "20199645131225911044918007618559374867899861460760047877106143132248914945273".into(), + "12001320852019564045161245401305456578767647373680507608908849898281736831048".into(), + "14109337051994798438391426281455499687696832970217465295841248395157195678143".into(), + "16327788711327335703187954019173408665382603544801329348791567767829649582022".into(), + "14165345295389244679058422302892574411018017024852263235622041585515310700440".into(), + "18098328140605761490641355018350075852862336947591383947449899194910504851997".into(), + "17531934292095415392849417096338882004139134123682574245576387314018394586354".into(), + "1733676683546726275638177172968959379163822515869952716090548401850898249951".into(), + "15390925956113897743988821133394062442365524479421281886945584164621467664839".into(), + "6308234777769318028945248133710542338224131559387923000379414195599812605031".into(), + "18195940437872982705964842501287028751711989848794835251153117845868361639502".into(), + "17490283477945958076039190253339223109810835246475119772790163703167194121322".into(), + "15408396829836728115954873718864748500411741345264521674331031030247235075214".into(), + "3551438511462216897614311415123740632223550882497302889988823516019801455881".into(), + "1211190935634033568807015118513121103447278874630092924424376539010769615279".into(), + "17406162668797550222384115208538224445727151774389004540678231208053715695274".into(), + "20479453675762626017106163313206256229631927324901802475043478933123265498282".into(), + "14782425647559744167182627327791886056751281195680302914811546501028210982186".into(), + "925131908338441634745517290354664216337826881101842522634123686010539186420".into(), + "8849707997196192490777875561563208206085973382242717199438345627124299133090".into(), + "2076454320990941741925816538401596397546951763404696804371822796867698785368".into(), + "17173134576726258494214130646402838956462459094351663199002055284799185636563".into(), + "8278507153110957550422039807446721030659313329793830361362606551912544734153".into(), + "21454855413436031867439010732150284197408206511012140387650556740240705522088".into(), + "8938141846612216610282603832016394896549074914747256157702516831379429923666".into(), + "3753004427655295864092888874901033504634515083469621309422669489429783662244".into(), + "2366435134561993720630980767268361780485124472309182242565985427615594260755".into(), + "16165357231356434631067514709216775568766632800191759830231349057343507534434".into(), + "19619001519241758891959372143573350428188652312535228885075978087065138604446".into(), + "18529730720102625006097708392875275467779439015225330756494090875760858662324".into(), + "5652897308997297007169294392190006281027830654965418415676327691132517009477".into(), + "13575260089385236342970650047136574844156206796954949409405285738862382638131".into(), + "13051488122183433321152129519814259420249179451391427892934958106760313757814".into(), + "16675947070785780493219656951197382898525312482169747446344506906972920892954".into(), + "7175175603774065686796811506353067100604576034944842446058484596637779839013".into(), + "1400986416499873501628131339252214540318552035055662222249409246587339999877".into(), + "3464466866466806017745751012761954006390229805750136348643137486103734148919".into(), + "18217858987033907489494644734206558680641423556261333390037659204834788684410".into(), + "8406086726746620686898224574302006639796660679494669881199476338693847188630".into(), + "17762341698784805474670945153199304225802481228485479435097247827908738620552".into(), + "6223712186181276291315191942450380783893133091457934083606193355728586611122".into(), + "4282900419919891825156385371067226030543529098387005106356966928605004273721".into(), + "21245665052422749020204481966374990873300815343268950998971123603215733315757".into(), + "6982432590923765268313576400153302843651774940912624901824133236148956994132".into(), + "14604328114465343527250129263480156133215257207190011581958944554537477242146".into(), + "4419171346687102376610201013900704798965178967312178557853149472731770706501".into(), + "13245918223750947891398096117311260221538002135539665270124077712098715822692".into(), + "3347693769680689230259648405537595614327886881079646330652620517715295020391".into(), + "11097962153704048182055796266608903112109780112107618586656031611021161690259".into(), + "103842991820173997936966388104541957651469231887516072360536421395334472158".into(), + "10458487052445928020397135451416595147106158179032040258831628955988702925482".into(), + "10900270612365580629478084761129851570116267768979394602611763172198923647648".into(), + "9903203205178879991225339006690459919299342990467545909511182892622541268992".into(), + "7206476963459461034052924246171589839665793743462578906114104789862742647145".into(), + "6264046538214382166095990603552597379083154170591970728068583459736905241469".into(), + "15208506074912456921421298757607137779904668310165212805848069781063650192568".into(), + "14399898497646210477881830976333295219807765057833256267205543494652425724063".into(), + "14270878504405431259159385066097540588788179903719271466546150744452837482681".into(), + "2299469207691207005105135549235035285287116575059039549352935321891742095084".into(), + "16821622844104951063765780536072686283845403348056960050391910562149383647957".into(), + "9956020421344380373447956054462292461416421439742573500231969663699680679093".into(), + "15930825774426452340437887971015387876460967360948156787859264923789690195070".into(), + "10985728214199775151782154608793108221074450547445302100877022826701613291080".into(), + "11760641126739413999815391346325702178866867588284525333479386263279622799779".into(), + "12964764722311909921385583909124655151898642200440943348322605636391779313939".into(), + "18379853927966213921019716164768637873544788239921704156188954474082485441441".into(), + "15296912762863693782000299085702814599512114536531293086824435743573000852798".into(), + "11055306663322542945964813101077231103838051414460193023274311763885249453027".into(), + "8057951225246999526336336507654165264870114340865557474750852826611325059180".into(), + "7217454204491887928591297314854022623729750971598154127245160014826014134117".into(), + "14565102336180706753392276751790538414003260023904084675564779334184014612773".into(), + "16932806574869520697162166869588187543526028859439455500295852309668070598166".into(), + "9179898117866495045233038402762881324885778157159220317172535561846474551689".into(), + "18779105085403868166483619694881906385652758088668461143173550878413116028260".into(), + "11214136111470281555815079365242861297786837712776840366750250043823307862209".into(), + "11442992999493383524983810131599863922453401327420664873617599429059754849857".into(), + "16271323289864628346209913310699661044288655850612550590359075955707090062050".into(), + "17108025350334840622180169650569770852833940341473970170517727066627050749958".into(), + "13146248878892216294361188166665572028549000139712229260161108594416711673870".into(), + "11427372820077962289622241022478467149286157602797953190515658164839928300751".into(), + "893014835995617411380095483743111031917691099253220683676712243294883268295".into(), + "8517095300100078150575343693630592663417209558229239005971749331683768522616".into(), + "17927627171733468726764817859266182414919024487868681391571481635385817509424".into(), + "2910874756516541331498135086483127887295864033618596807255973264881493597680".into(), + "10351245804237587891981133156579115865490473264567370459691707360959684413970".into(), + "12694211070090545997357986982035119982901819345677093614248046140600061957940".into(), + "20283022323110075954579979019632104756139185475301167929737027848763705068147".into(), + "15931844873010780159923673780071721836876296130477744289335217834580073984287".into(), + "21827223850924748993974869213271539530659373002293617263857321497283795014386".into(), + "2216130285102621170883691491664557599377015082391517585865064633069426967470".into(), + "11373405503815639839270612384753272139664561558477978654573651197371057815151".into(), + "12313875735580427829620793313088533328915412934622924938520740599744871213525".into(), + "15142465061035421304605695594042142745567585293979295525147576522422528415408".into(), + "19911123862342914329674984568751978176595422003509172434076759943963715219260".into(), + "14262659769499978824697304563726231560538953195673261355416096174585413608681".into(), + "14610683638141173050828300939153196590488444327501938863403192781649951814095".into(), + "6637311287977459850936997036417480664822288091481538821362996372726159029429".into(), + "15501657635921439788956110615190078759397404083654787694613599184616807587119".into(), + "12415917438013473637314666005336278137449129158908612828862230927356476129214".into(), + "3977073659694868689017260754925680384676839395821102222683036599314487912596".into(), + "18021973030966989893231530982789547568199608264409750239459349680822956679629".into(), + "4358661372125150502295103743733014495017362384424029473164385391234779773434".into(), + "12150574573348685676904261170022340625206210894206500162919624199154445311926".into(), + "19059564947466261856206182248882637129860510906425942015878496836349348075264".into(), + "457803388293300831659933030795457353247367081441494540658944608302089214573".into(), + "5212046878650303869536834084593989055518385481488493726863247051727387374999".into(), + "10866963984944634834508529257930003255650324224099527428753028080514409460102".into(), + "1567398629202166035061920213544077752133493577343656913517034032663760331227".into(), + "1641623291671542299150077964313362112100008772415510478337935841080756871031".into(), + "1814807183609065925468700221459835599229505862471630844515076416397596161921".into(), + "18676591284813153230980779135662585498854259214169962891045023401932640144792".into(), + "14582715392475730800118541203172619815507444658499922254753619352141615271717".into(), + "5971201190012257917828174333108220091030383280212145099110362036705465802488".into(), + "9745736062659480171553506036948675113562718355498342585124081392171087285152".into(), + "20654605762814031873434196876396785695406369410937473580199071780191334325949".into(), + "11844011087342569590330041590202444612516395732736950853259915342942395929104".into(), + "12960729918447154570349545809398065983315955190065107446283057843674060399978".into(), + "1912969365260456210218541172655492769437123937988143458973053151759348812779".into(), + "6751278463897225433246228986310134589363747158535699169746372748837067912354".into(), + "1405433090804402720301319240257714728362644500702704160642087476923303165683".into(), + "17334203531491537592342457272578981899701055200802551766264091441355104806736".into(), + "16575765689548766530893504204590870417122916172019674976779799591211403721688".into(), + "12220976854598379181285138460526335801169364473128100040253401652070229420610".into(), + "18565358629024373944198152078259356971563125515748886809957420832630624111647".into(), + "18999218340435052500780578040305741910732238048488832712380773170203478504280".into(), + "828469997232485965462944215365058139980461203417902215718115878220834198655".into(), + "2385864265255813442011864217087321326832147642514277043179825610035123479208".into(), + "11753087538734344167619786844012793078263845468062416000491296071906848786208".into(), + "15297341509659920002867070368227904487875907462849401568145592054120947559782".into(), + "17451904737564664855895845058410281315610351460549944647244100267528838462231".into(), + "16288687708586503845827496209229686543350756101251970070831852982828263987682".into(), + "17372190154611010904681208309570706747429785600046317069452832284803213135091".into(), + "15112572862198958048036722322696004521384539985617885597335257056722713402392".into(), + "3090030985871039557449539908126365491166718509536148045184016492804246563853".into(), + "7579986120189318071341140006570025802626137718385790787108832330451957404666".into(), + "4656625991418301006397045145531617124589866163929416672123076523538862286851".into(), + "19496020801558863926434816068743011465607626373023757648812447085667986307007".into(), + "13805329389975072362759970417944582121050551902725997016013251740633448473430".into(), + "18188229086122124131780255528737492392822604407289151260248713532509782736083".into(), + "14611835504502434276576475613524726992333885650957002636192032800434268523844".into(), + "10917400920268125157880112072188560060076995206390296416305594303287585051306".into(), + "13972231301490257961012583958703459454850999751676553092417436936263553366685".into(), + "18328503113772388769765260234570641415081417900145352728842854263561232438288".into(), + "237123037398988309289930744284813441581716633481073379308753775064025864085".into(), + "1703922543704124573472350868371820728886443372137290454699485327716109471338".into(), + "11303106151640297276547682977648977961130813769323129464984120148868796657869".into(), + "780835119818217499025356174332702814577120774118181250962295467182217505278".into(), + "7513776436281774240010548782707301011961852707217067098086617267751060890346".into(), + "7053386733865795782344919942070418964042982090173077686333893187474076408826".into(), + "15388377229685250353999590264475768848666849601695837177100149716633411294502".into(), + "18619784384287698574679414094727959444475916860934398666937358687019863556637".into(), + "6843956467116800550015577789758902600855297665349663446001269088726857226048".into(), + "15027464760260005079316932218316611292633334115528481474392667994765319146064".into(), + "3113497944921123135464097580210634621999169473508798646842600013742294203321".into(), + "13922875552185745353363519166943408041900501129716418917715862175817917028807".into(), + "6529143033058355347608548436453630783222625692384446386966623567146527415664".into(), + "10224300829854530831381318053279338710395220281107133842738514322747972858570".into(), + "20406735123273907201110501114056492687992916259913310564107896450511212255632".into(), + "14266011897697023567365526625467367950062937074006135166584691625548787309637".into(), + "6266823350995327247595338658995148275348018579417278062676982980758032485160".into(), + "13382224019010393958018966744541120649420499028243672845532376433487586111361".into(), + "17219221786488657036851893458562210759167416100910882352030211878162886142909".into(), + "20162856829897101788693886235042939518715128237037117615656868509223185121680".into(), + "19255871117980259796107225408238738190418971755264416661458453217281817348088".into(), + "14978990393744959794726413403754476561984809686673856442658550453303982912228".into(), + "19671827853594912540136789344716532059747421261482796306917878259275666282337".into(), + "10732390517629598846596313835253769945253387192277550975463036830136687386844".into(), + "17133415435299159639005309603217597288690767318016826234871049396559867937550".into(), + "4238294829801619171082019239832000810131476156600856298143082034448277968560".into(), + "7722327574874647121890494265704613777043492303801725732359520952354380593844".into(), + "12398521387704975769381094211621106917110829744392349946808293674617176531795".into(), + "11200776058775831613393435187103560072186926574904464246036376746754002391683".into(), + "1438209332219971409989076556134294085612745796338690516891328286502107803440".into(), + "2937570830929161760131625263024599999319751748792625900610926821498514203833".into(), + "11043761204766219739712866580984621876221567501998807492499956572251345283273".into(), + "17161703263657344245212174231645949667297741070344916641526821962829393621412".into(), + "18619070492541478298708071132730313137437250499943292747935022132254737182602".into(), + "8379242582671699656964937417808578910591927550726975853520669689659712748923".into(), + "4461581878275014470922963327129159901508275066328903674248853463321568123734".into(), + "8814779309876923204577653695802478749454337142645142889405691969158642344463".into(), + "5916090197404556150255058868159426544438846565532168821258584401868379000295".into(), + "13450130187167769463125976820336122102195298233114472585228206936434118870547".into(), + "16312137321831916960674847439149202523298938366384118998520447019250845846949".into(), + "3523905992883153108179926090860559860264215076129040324355490516029055984441".into(), + "9941544263434232818617581658227915119072214833223636007382930887032695504069".into(), + "5866421591020072450638507367259603444231034674337808358938713064987239652864".into(), + ], + vec![ + "9531912189466476916568861603725087174113765795321185751191491139118805372511".into(), + "19478790955972117697384547134063865214385101762783978876290106323878777065880".into(), + "1060742683967616999946346623778902980408439861665078741681079083728753884980".into(), + "903807389911706192945621790390189278582333785459643079682311503388875632444".into(), + "12234856961774107626670146718644057065701939000315796085771709903680607765980".into(), + "7144222788160875459149511026237781941826966456792731247241581603475033054157".into(), + "20698157631699630824067955298548696950174021158305550220448729049898476869972".into(), + "2568732479597962725718653653192533652726437462680484211279054254158600287844".into(), + "15284734447838568972934847154537240941736455475374750484902463133334986327455".into(), + "11481338136657097309136761414013103615752281785975103234128467186355727286020".into(), + "10612154017018914947135783711517517074090274235061056248497547614631916723906".into(), + "3055604116750189009829046286798613018321687824856056202070161812694453230232".into(), + "15724356578668121294244613323219019733691132614053416867337612541744349705307".into(), + "14233178571890186223833398248040498471414046807101676904418988635462870136058".into(), + "15569884202072601673299042953201725479510195453493088500579693825511613908294".into(), + "2457655896277475748047646151777860503559023325500398204274047419681119270433".into(), + "18721554772384051287322049295337473476448259922773695010270945658995924622719".into(), + "14475562052679553333706265906284533085428658760049287899372861273825967798347".into(), + "15603408794229592986464670990674550905587371918421438132217255356917740776753".into(), + "16291020844798157226549685169153735235751877205762229274467406522127105739380".into(), + "1562912009705461355815110217333069399929165391790667011738168557818438924927".into(), + "11269000847557922376283381139665491298931690005135341373215203639253515983197".into(), + "7952882227157644034022652849613475711023423321505769231128945987664804912273".into(), + "8477623463686474590677390307150152439632807913002306242882220286965657503044".into(), + "16240923770208341542260111895431885216031871921759466016566105290964433552216".into(), + "3342849074804996662671621919352322750206307938435597035721084610748972602222".into(), + "17931702006999673813463835283048793204134166638699010505988519059393386641671".into(), + "20775104403560355444897460398679593487716638645215251442678183285176599574637".into(), + "5119120619161234799144523865081220455479981813019860080498855561756227023701".into(), + "14966757356872002341966020507506536570537078023526508344654458912397759281515".into(), + "4186575643717625662604368224196455420957673000117335440321019421096129003102".into(), + "19732993039041335023904970539797704576579318087957672270477209415993860238993".into(), + "12070985110749915673363345667648496564808814323429491368289711883810537898097".into(), + "11426729904237322553170474047093755836047277723343283963043397762276736900525".into(), + "3316549680511845723553051012477874001156246623643069548980284069093192393275".into(), + "8181145696278572952623145148622555644838658979012817598939443116317742063663".into(), + "5285104126733708771192424010645473580676014333664755598112717745696037070786".into(), + "20879460004955312065246827250994614761073401947673232470833608851757692374701".into(), + "9225423353187148331993357472363957540141522302215231248106234750413173980751".into(), + "15882456781720382955442431000301149138213435084361689595107598612703885378636".into(), + "740238561845104412391944006282242126007894120431256540495139829806405656182".into(), + "180417058725083825889148913143008776006125349068680455724690397852076567353".into(), + "14174293519955274733372251014027762210778831326913295280840585355009025244339".into(), + "8589132560109336742397080501737400189892522466285798728529721670916644735009".into(), + "18608579889305084432265993644262889161778187018060113992221234915464772250895".into(), + "13255562856310211555141734542720764165551582487712004861593515585281326120780".into(), + "20040426996876166323258012360708147873405329072458300306708975663127708222443".into(), + "3623806905318973096995680510049954452579199046783492207512283522220396562822".into(), + "11410684734000900004860241940266501902598169149094443542781697771532926351566".into(), + "17135477854627682759515989387435395911766329083966273406049980901836411403416".into(), + "13593359321772915930716800157828568438953037754245269488538472861500074218774".into(), + "8851367853862849158416384345517193602505667487160151588020171989122435915705".into(), + "8486258745426030087193511835657366486234171438833758681014633470174920075557".into(), + "14747490337504821136965789527401826047053378817043597026988257648138435336926".into(), + "5328238162919446506214025777104049372124957345257645933410622558507658284713".into(), + "11965770224330607826076459132274399741504292999992921785782448737931531131492".into(), + "6397958737008655664539418210486834384969900796875514632281024739857683313517".into(), + "19298424785220686762403958616740155152857782164943661260316780711617884649980".into(), + "20019455348785333548837795973650078300677031008828877476192963238765813699604".into(), + "498876295068369039941891884133922605442597373275805987652958540603151502762".into(), + "15673948845182330978914854481229214769118100412263880346214990198207268219918".into(), + "15636974430517603355685549955558840639961287398389993551242671229593610041279".into(), + "21849352155479133596816471850644588093828045783688624472176874329078465894038".into(), + "3710541309080539224803365587698192640065043989988405183436118622332060826084".into(), + "7400908409303343897763329119747918294591391269311551693625176644493777241247".into(), + "19095563460120996369257805492605849292942048841569757166438202426234116219528".into(), + "7450358777752652255259382961368280387692895530851667830759412613948095056314".into(), + "12616362847029801456780520061399223912377922714639139395311897747307063246381".into(), + "2962396389753936883856793158008198181187288822144777145656778061306062270271".into(), + "20597083262311447290143345580862519411179427096464102505623468932397345199901".into(), + "5990063075931255958499395880323163728626495197217954905778613462408195572191".into(), + "9361904499252311797420064091418454593805808979929563324171298064587942042557".into(), + "15745983602860127054098117502599813732367772584805282887983680577406906842509".into(), + "12155753549127535852899642287036910835293646693799050941038502705904075221647".into(), + "17784550108352452858546269360461997898333463961938370347697582728197589684086".into(), + "2486952113321483072183664368099776164387734014933893158352223909214365526945".into(), + "4903545427310923282188595180893897246459463256398425200307382903876773643166".into(), + "4976787025628832138140129068285715692634473783721132777844066348049003658995".into(), + "17903848557396420363857191109854268785343114927997153696759251220232810232708".into(), + "4112316506554809604392743006504568448930584669405889138267627731770713169451".into(), + "12564921445775298869601661793895717261354052767541351424764087809438338341443".into(), + "21338339987232841174606235608205622863643553456415651377028015070388899605425".into(), + "15088854240518267391884187352051335059960134381458770666979402557846290860043".into(), + "6521785672326022155410896706891346109402758862672817874198014517122929534795".into(), + "17713017733654015756718215699238009705617793897969373435335184169505068045344".into(), + "17520588725173134454654213370481559878188684108641520017594192886986887410727".into(), + "20370353420481573040209617326772538556774101029132132235011046989968543481853".into(), + "1112630910445876674622022594317693604373148988518983600314476620278857862788".into(), + "13947879294001229379895373879944689637841444307211953543137372048528771620266".into(), + "19992733820130513366333925886960985070219788486605690639975080516132656879852".into(), + "20463686239191469605722985205294798392601180601616445763439546206772797296794".into(), + "2399836272490730222789561326076432853292090595513010294231690669763899804691".into(), + "1034212741378241815550350670737161423052400169194393567236687087583799668630".into(), + "11440929010875860174917763753574001672473682534215635955505970318252592490865".into(), + "14189960926817818424784917441574757301766760887770685069486059337906765736963".into(), + "12315331213328078044898516229848174792421271854142211159524426707506661472774".into(), + "19715153274097520886062594899069030152815615989925596868748132112713500919606".into(), + "5263645674775021082527379900656937946372398687742691252725061831191269814647".into(), + "17116922705336407879424247270343125398621621836566941362988393678055183546962".into(), + "12965075932786028962944831711285166109972351055088920502397645336995474504411".into(), + "12016029264981766758974787101200878559176627961323691741528038308088561486007".into(), + "5729800253105809140207889626731612970039128485797787837679696331851897001188".into(), + "12918441439057112476878371772096947366502166083699226943980929800631997660377".into(), + "12122801182972566919042281354391366131886072331873675435499031824985283406080".into(), + "2001381046477673840829875588313268487326797326656965151814712798463828403972".into(), + "19581992323872865003173357646589705276192927057599589666190396129558198469295".into(), + "12032241883699803399815479981073900218390137511663730251199663857029681112519".into(), + "4486673663248066106745570699596745555749344770988892121755846988206654958447".into(), + "5880818668316784480273321916242537222449799562687899946001667784663774949284".into(), + "2395463900919111658295342588362130947823185484448880299307422522723986076206".into(), + "12950983061674357110606111858229311283119838941890561558124046774295323335099".into(), + "2237414129351562764861995011211505967387529547007150899242139479636758409691".into(), + "11297958435815311160313316754342324739290776717531068730041530178142477489469".into(), + "5317255860197359110199894572100590405469568868980740298600361799734023336258".into(), + "20883201721979021051679862781044901365707874143019054645125857460321063446040".into(), + "8182241566037373547703812967620649098015366536333588850170211599244447070966".into(), + "9597640642388296782056528355964209027337895123387144965957122193392739912194".into(), + "17036534143228977344737945793420370811715806669058615712195620745133828881359".into(), + "11612810822316258618979762480131695561483235138506664012253801075650313633340".into(), + "8933421864748994361777741056369848147887887317449099739627452478628920292098".into(), + "14444070019150868677613599250973948199109012042816076287250439535772792130077".into(), + "1409252976182963241946410463334709182528602279352691338387472032099813179046".into(), + "11952347912832393217697966069578145988700552783923451999893230696985016550129".into(), + "20459418532142116378042115609733769114184932636063412849272885250416180990630".into(), + "3551971311295052916014184741437675944777358383038912487497306982998424802991".into(), + "17338671457268891559841041605089015291458737421392337141908035764681448452092".into(), + "20968343054904260510606682646995696017767749421435540168583945905747214877394".into(), + "21712394213185895207403184789091961285240129068046002376558625468598692072071".into(), + "7634967402785998572909457534910529497063727153788753124164657866643377741907".into(), + "19849927917300065970782875128524194424999119369431340457180683372405088384137".into(), + "15012913963479022629048645986836271605475934011232886489828149973693122241964".into(), + "9429717687014890707005420840742234597648724772846627377968206537995029096421".into(), + "2567188197501474210757971384017028034832891166328657542206945684532913152990".into(), + "8254698320328328672738727190211578815599233720695888584349629846354725718490".into(), + "17284254811803472776585305793072871837922263190673228934354908231725144505079".into(), + "14935255735939697495362584034949670161356891451287847368207930876840786524288".into(), + "15799240079228766996307573606262831147697506141321254927976890662889015144612".into(), + "6455095726770008351501849656650198371508317633852287113372325351871125339180".into(), + "13561632639572229898860364095655372109051739280251390860766414218384828962360".into(), + "3063643914261015068338656198636076696013594289440915753234546912875968584726".into(), + "523490668760084285231907250925228161954264885492417167368043075448446094968".into(), + "8963235882996992436242593243282162189452480942366129170093695041925502792493".into(), + "2822355405499444122504244694377602321152414880107322020961544617958820110728".into(), + "9900962978347352867015759419877923426715239847996616012083851486863818525240".into(), + "8479746689369630997477042503538860863372007378750903762662394891426272525808".into(), + "16389683851395581748247134197181679668454182692374139436248933773102047859911".into(), + "53288238128999697340898456182379718764153689572603829218558226366719788005".into(), + "19367926616347613140684682635393090785537600078242140764999539757098535716540".into(), + "15865351283225378120524944291419029041302905726785499676495076019805256191362".into(), + "3306541431760940351422576606963790342897050957331449111434563465335195596604".into(), + "15691440019263506851813897307599578949230726834044329611626721124859202292981".into(), + "5471927110529676053035425189646230106778686168719884591627642899285735629074".into(), + "21754232156244379849332999145646221923063494235010275675811923863906511018870".into(), + "12691978963824033486319665437354495198660382717956695927101804737187302726210".into(), + "18287930615191907073854709805749959615454583394829992950666668999967617218809".into(), + "19651897318700114806760486800306423305046969021116395207725042165875775288623".into(), + "14108212236437313184992367338426565912378398742941435667080068116020546770682".into(), + "16999325425454851471000400176480399381440753089661500361295692310681628123507".into(), + "3115621817064730307934070024882122808835435845590131246374645701268123516395".into(), + "7664237802103239138782764325077413451892718354904226049706189059570344004578".into(), + "10628974555344045044380058670185713528581140293158876652923633181919907860739".into(), + "14113596208322488293154755998142305972509624369829087071590744312673264784912".into(), + "17464956897498328434098241327703906248729109105264442832066471889452262140737".into(), + "3306236514619936703817193243747225028048646569484446722868278187142340885606".into(), + "1623642646505969781806818562148062924427827461950798461560800231691312453957".into(), + "20812904557023426950178015281581063697341146335603865156046444216576536158479".into(), + "19408105996475064309045111496637572365855316086113347445820645560307476671120".into(), + "19899769975875815048945884537401834080401837433178820364413791122141366658704".into(), + "19592039825831921790276944621603771771915464940035526138844575559711905257984".into(), + "16653297576149039984587104062729832787430849403617977005988111857683169262526".into(), + "9600749907793807254814822662348056400280037933879058284466549496283747987313".into(), + "4481371968429561669477332540784950497549507725682080986909471734800926537000".into(), + "901971752662861027246344512729868409066847439697888994727629776003122477075".into(), + "13205641411901359105414809635566186722302364256582520884685762468367549487609".into(), + "8771921370123544696277176259984315154171500395079224159374189372693912295384".into(), + "5174975161282723002634664972730414710732925114695376584668768207090019053678".into(), + "19270513804464040100949263044402755496385234769453916866026752765439740596759".into(), + "21159175882383323828715178134104379154154907971797847682741774730382256211237".into(), + "12713085776674794152643870369276859988930155976989577699924346924648017649124".into(), + "14416880591724909781230787197474161383703110620356522560207527702042764430665".into(), + "5782775977590089860582593877799736603230821040322957359619681360364621989990".into(), + "4476458740184410293724437897879594951652562625859170065641235900450054752275".into(), + "2721806229175990973363729919729809746838498228946916302499795336059265315654".into(), + "20919749045138523262313225773151110447007311142131682485436367417448435698000".into(), + "8313744079461516868330165973726769605125778482558912200259619501953066297351".into(), + "4735608165219550054732197605756732753518722979705458514715803339137012677807".into(), + "7197734920840596436781335789800800949073525267054200029424794448358957228282".into(), + "21023118349982419692339995216673708826739468972915634059252379895041124776477".into(), + "7361684304536881013610906796097454038829033466585052789468836114776912784525".into(), + "20982662530655024673207268539471241193218964077396502472563124088553292010609".into(), + "16223276590978113205386347640415975693035764887550533934078089596214147119142".into(), + "16855050273631391834996719887442993598537594692357829619807594900987141342279".into(), + "9447886870473301829371932239272857617552029212858469830955734178259213680272".into(), + "20832064076469714454949991237859093636177561963691091143340405814107144265638".into(), + "8639667923938888615556970427023258757087537766667818930469506422086574042603".into(), + "17264766416417856804513499340974616744303058001571461329697855072803549043449".into(), + "17850880215521300838724196369662075783108857625488733013926883881070746974079".into(), + "2555423581835096020752363625782761123603321345233444467023623837801470232396".into(), + "287393475373800420687070196983041262901299109116957212897422666775745916878".into(), + "8502409129845334137896426506380979458386453989629214624807418474240644490658".into(), + "17018903560361810748608552810594034468026623560117430817297647108682661433399".into(), + "3048293276213593502083356886493678223968770697826563060833194090969377703128".into(), + "21410637907835525984647506144589609474305790431496436733818309371828966617019".into(), + "11884766464429332615888730245830253775469158860120272524933094891366345545984".into(), + "1404751133279371734382153067811682787285007352946819870030827863597870240066".into(), + "18533103709771766357713924189614422821669511650512088621974348524596673777463".into(), + "2245746843374377731985282335022648129668387693745835052018700591124673151499".into(), + "7164798246992914028921284523335117888604816694373355756451086598952914018661".into(), + "6547792475751145534229851742995938089417776974069115284886265458921995253635".into(), + "20902177810420148842648372790754609922473216539157084210657172695295335305053".into(), + "6725829188763173291829388608607411327488468861041838394166490103857184986505".into(), + "14877246205681405878429147764831613204208078224683457409980647858475648710211".into(), + "12044111044800943869325640584272709159106488829628178962818078442934011738582".into(), + "19605231155471418918091518582371485038913937726379108169052428896528644150703".into(), + "7703294969707260451899023373169584023358521879520637237253564638222384534453".into(), + "8646812596577526500078198446696820195057640949335282980927180585777038852172".into(), + "21737243780210633072252501142816485256289884504027720271756348514717163502438".into(), + "11106629219138713431737722416937344915839184447234152498273695046605412717853".into(), + "10776231601191535915748483208366958548059216019309096513250460388757814780540".into(), + "9570793764533387448268417521329594330897759769553789809193103230402046525968".into(), + "17679865315281322154038759337512810633677645281028940369384987651076506608513".into(), + "20575490035801628850469464797342943234291317406364190353186174477643080398289".into(), + "1245923103633930975302215942611213577964303545113844948930979175833556792302".into(), + "14007490679559289327503467618330290757962057021886611213555145469946035182448".into(), + "11028796735236022300483437389742268989348582903251651368706557599156682851942".into(), + "1556450270937619861275756908441730263281625715497049030992434005539032147429".into(), + "7378353234583563315614681819214067843031828330274954124783678863055213281539".into(), + "14666996645204144598922205770204386241062541706976832276647424877691625110244".into(), + "753795180391574509648864319525512763100129705111118203505048417049290278879".into(), + "14153559413038217294172720643039696432149555359470735393924921410988488820985".into(), + "17643076982867120574893553230100217333463357095994549461637738074442388603622".into(), + "3578933943193517852178000450144796032210613197873582924120228282439809826887".into(), + "15520463167953252157432945917399723977264972626433304226673421190651502497625".into(), + "17211537329205623730168383274756825219539545439487707555783029872192419895198".into(), + "9802003881898384149235538364488830129822967333024012959168976309447054827302".into(), + "4277753965394311084543194762939932271526308682615262493526499362805330624922".into(), + "3936260403240718180440931086768355327655463497858616640513270218914599723610".into(), + "11517607066800361513856730397176372083814736323098377670788083637850655404741".into(), + "2243213365229554429675654538157029776597503775940697016289614299870958139625".into(), + "19764298184704281654348385808308820923079630109704889940202178107926508921613".into(), + "21440201729469627284915716136426033084639303793622913729895539439609852676483".into(), + "18865357344271429482320975819341476664123167289165510208121280487753271844163".into(), + "12062934463233700754743031150573591813637006411260363192982014497532786059086".into(), + "7253500424782653645261846666860267886758998073654253013071356444451212225059".into(), + "17688280285743970528696341296072681725398428119833052669003422836815517127146".into(), + "7296931094727393709963826609746626294291079390951795401509403882666626382710".into(), + "8944898010968990710934167772370816765379846965311172103743060979737069116954".into(), + "2895100563060833104606048044246656016901407622471819180004918143679777127583".into(), + "10049128690674011365994516711808882356477362224046294433720641467109283787161".into(), + "20809590643316933764584815864182144455784236186778450054494378700964393080470".into(), + "2552959198983852034601282535517138215015669324552132996051383405020865478519".into(), + "90024510729703363025920398892759893841503303159824575434815310687587906738".into(), + "2438402150656393945636277445769226062775867161339657212187540106747284678617".into(), + "10290839327200266675482043018636710429195359786802953032677212989307055380477".into(), + "13366445998820331718698457472184546771717827833722684584206646838628538349970".into(), + "12045715338147601791240214774093449384918104627310882799600088155093595426276".into(), + "5295711531017416568724842720044505396040371477829249515344865310124246197980".into(), + "16400063927954668864214504458520657934817841218321732614334739112437744352318".into(), + "21834268150215228276838182444784793459928322632598421948695291555558497507284".into(), + "4036405082017497222204927366812040809401891394189816634089482334837550533053".into(), + "16758010699927998006904853632308429832737059675503937996784758385473294734767".into(), + "6851323788614107612891077153396783509346340795017622764414273403342247098685".into(), + "21242220940510077461844092816930517552035812681947928514550083154475902790925".into(), + "13803558028343943472219115813916734264304316037691831164863336831915596589841".into(), + "3876897423511600793257560948470150518761848801469200306022138001232700430094".into(), + "7873917719931107857936523614722924550340227737594948157729078597073258431354".into(), + "17791290553613199208672721864188024787529313403544902623821727403694762316966".into(), + "4509080040196139117885863635009327583647452661266153933806446923314670630597".into(), + "7350149044068266301432651752105457672961955568647004218727518439545294630219".into(), + "11503072465150450162639608555774152895393875487845468619329760555955360089418".into(), + "20146546283112595992038998747954746814923105695946909075842241737548546646215".into(), + "3500661581148278300592438598016870333493992384875150415517341312342838567317".into(), + "16461061867640825231072403805782329353930546739796841505995525047519357319535".into(), + "17419473457085759867681212087981664406569947154579733189885809269345147937957".into(), + "10330084554893529954763451132860402555512473901655458870771994310647661645739".into(), + "10271306411842332407495000297515719691188342515212338700550984155237667774587".into(), + "6010614376730465540824294088181704678570427743830238021642540164120787031818".into(), + "12211956821531047081164047854393417207581329108889222238958146335322243725690".into(), + "18026227011560759880520787750218090767851915271628616216035782860818091527867".into(), + "18039053385352422125173521135868691523650276298214786020953324823686654441993".into(), + "21572961947836757087236508142423227244602094617521885308238188781724980380450".into(), + "6952631457061838308719934275464119179697248499343745875411129234786468691942".into(), + "19811900079804396120677471049757957457743518480096470411626681111649768726526".into(), + "163137711020707070877550733174177256426010367795106166965347507600009523229".into(), + "1531253433144642057738530256773902523524280331634644205392973718192895734956".into(), + "1179712150612295194734157884657760659826346075044485388437451245699680152105".into(), + "2729710616419138683641657553469892671426309689560939345577118202079250084236".into(), + "14266054344296739494440365684098525973737250786286154516241047965660978988270".into(), + "15026169712587706365294070206936700891511371850326743083547381843796350317368".into(), + "18744742519058537072956390943103111325221767611576688631532967525091526599212".into(), + "6993198712278030093898597400393962073007955430382142820465138278710889662382".into(), + "2553141820754951085470014997086944345325965756649326165555729099768987826967".into(), + "9720940197604032044323074196225989267295799246574437528002073812640512558241".into(), + "17856885570331180579712689563952926777618369125836010409080422245803122826927".into(), + "9316647413775248445962902207564207480723728241070369123275732644991331488677".into(), + "14494957198204561202111268006350266984666563257512595231100502951029167863544".into(), + "20990514914454458611724181917618445270429441499421925167963524958552424564602".into(), + "4471904645719453756418626304970674400441871174741396875599881395188613340474".into(), + "4684361390046158999250125407700867527620809473388545037966531069883415383449".into(), + "21702205249470184149930308057282607474924454457414198304211876480322358864836".into(), + "14125417641286741606705374243071146588481196226954437992372061774013416347499".into(), + "3703736300177825900679993762695155186404123107499914435631005694624510941992".into(), + "11732888492654216406505460448043873705030240266921135732988822696780340208946".into(), + "11933466978112969006229130396464073172307879223153980062784019174478752039844".into(), + "15171000950368540171664824924290660122824429581656415561069429955358449308299".into(), + "10369486846273450968605452628621668108353399309158716739554974304206007657467".into(), + "13426080534643441487992009643036732384216019130391445221701162507604477874092".into(), + "17202835976136422766224805235458239972845135749171745123647755691905320873240".into(), + "13003013510914746051603710434071109936447243726946691715828935535255320452661".into(), + "1672563195387555152025850519159013960714433928303467231569405674199001377183".into(), + "3249806644396047380745964934114987054912135505311534093405371566690536038255".into(), + "10852655605415322578561541069174913253244788503288539822168510897694861970183".into(), + "21180450898750475729531605846185254439124003420647553715507766256141444893981".into(), + "17598208134643261198923845998804266441503487273381944592003474913329048411161".into(), + "10560430398658881239820534975865220513135495522309722014743734708940471341227".into(), + "15940735766678288146218967299383564752403182243037693045705028442779781161429".into(), + "19840721391058424508364187473208857771270089687968326863727366457103504697646".into(), + "15270655938735284505864085399423312069359806468400434801172200215370037695961".into(), + "1774340315063362619348165557959238806046379939754082379283600857530102490105".into(), + "1931473872849034457489264575043647960104881629328524494861951197166741512954".into(), + "16037503795660448209256659529206619750469738788435653064537663524466496564778".into(), + "11157294725944164923451173319705000360949753554936504404517553331688337467555".into(), + "1860977382172821235372346764481514761526862185187436148114012977011560461667".into(), + "7554608809888667328824255380474433026692004240460849082770678269699854930661".into(), + "3812849161473445621072899414157938525926417912914872301607900000816805333995".into(), + "3248257538394266288019799770714987078883986769586407060306626631267726656239".into(), + "8987711810247040851774885826425578893413569065851274558714617106373602624631".into(), + "7002808931888012021033077336936962976185388694934605760542880449764655464580".into(), + "16260071391005121724128685673030785454758719454582904114834030329423907463299".into(), + "17301316944867831355470414388420250906247821380560618462329913428602672908525".into(), + "19139125176792761113369565771916762091089412261263410641074341972472699592037".into(), + "8186463001964086803315373078973607488567538876768905527322257145549601226661".into(), + "19715283945499391078117757449557157262014864431503022516493444981357377102136".into(), + "10201366877793384824708227219959809605313335011896439133665539069386849213406".into(), + "18657679796594390529926518390837962343392692362549805385222541270238612706361".into(), + "7486580570539115306934569904905950241452751461260355433571179343632374451808".into(), + "13210602081373832317780583229140785143149759155013988676929972636959898239126".into(), + "20547921916591862189543062064340935912295194861078724580978244369580188659785".into(), + "11184938862747662895633149920437248716375494917732149572183883382335006977362".into(), + "4564430184172465843641043220857544376847031874739611967386619487659896927977".into(), + "7141474497649513954000384074955704105249233034539438859110223132506810864380".into(), + "18152904455695511183625765039917137650094202440231199346119983777378283365068".into(), + "3538073603808733813419298816140509998806938194073503117538238879763400661830".into(), + "9176198085009858977059589400831647890975463959467315677036348254379008625292".into(), + "5358677810755468005042017406822742639656433122059352601335552810789858869197".into(), + "14917263528903508585499167331159661105708948785920472586609175307188322712065".into(), + "17459264445642522670023202416244479198596207282984954048459225794985333792860".into(), + "18314375996391380435570814127239902609672007999779514985166492867417826496725".into(), + "3831982114680501270333115239154594098561342624259756350309439921335535690506".into(), + "20912918104665637113589191778450022009791755974254243256571842103252066396138".into(), + "13641033990672188411010402475025484333293999311081344333460071084676167264579".into(), + "11812251882211406110219542299092601278938747815723382992634003397593468524422".into(), + "6831378526897417143208721431885442730179700977603214290972417120210221051272".into(), + "20566372962271447276247738296560940562676849411310335464134428317792972599703".into(), + "12645078398048087182293658191360551774451388858142913314124550234755654392800".into(), + "18848512616065065931619133098070120448249278799586409723288680340437325332421".into(), + "1336951976108001940844939305921535183133161033109848951359362159969984671656".into(), + "12850569062942139371781102794220985236887099863147625303610933201225464388254".into(), + "17095240610637692323679811976243261972135774635008876108449261858372552686880".into(), + "9827905280236638995189439521379169431346954052078412963417337148237294494508".into(), + "21756974284757687824674400821247092844224232050435881799264172836701664225928".into(), + "21071024335098050648915070341467331164448998348673899538194227711074199728981".into(), + "4575704980288845009798550678411618457862054601563658773482638217288811407473".into(), + "17637889149961283902802970141414602882266203898461797433796173000208254839889".into(), + "11333768795263093043465906246853631488076833683398258097056018834885708094795".into(), + "13237413177387831469342322098638791101575804381099372225484902457329675766563".into(), + "17644690723564191319649063794394358703071761592119981992193950976485565800738".into(), + "4591566130221102246468298018361020201365534301234509687763578673293960293459".into(), + "16880258892960147740975242226985692424894589139379261939284621634132328476631".into(), + "10753253761433799332298845732798867046726308621524332340930656512278172577964".into(), + "6294042736878992012448357827249736553756583459648169754872627240315677645950".into(), + "2668606416345354198548309314080005930475082556427535177631964192773305378348".into(), + "5244780336143933367304708103198860683485779489995095073384345019218674347056".into(), + "6113177497707123341972721698332981926287006839555002718473535918066824223034".into(), + "19824522867272770281143825792424101858356781501070734931688923418771980499872".into(), + "16889524920861245550560790459946848535789402393200784095555799980201051324212".into(), + "8007523897573727144887255293212203546407757295680344011669395834627039767919".into(), + "10043972607564040581522896355091368769007227979993058004755493078808025405133".into(), + "10815879623965899246220301442969866254505652264417667341661157537312485515831".into(), + "11534947289183790267549825879293816175921818072299730180286203718992863665552".into(), + "19282679238458671513808783870622522462539288436443040483594699323647255140729".into(), + "20993973658899710060621174143488118082242133512002997296378188507484929417581".into(), + "2207137584463635998515616548473102748156816296061395772266322132360447742245".into(), + "1906176303891379788266254493945992658935732580384991589403254257107793504984".into(), + "356009908103458769499047304270411564071705602811825215044685668712377389204".into(), + "21267156990661889558161221297711049481880243497825013463587260785886036981575".into(), + "9511026544995595131578865949819301816364113937202346971334793771967449171857".into(), + "8732150772674776446232653795368043280414095192666412295836279503085501609742".into(), + "10433912179719211724964521733928883750833304291050102060071171195374738464494".into(), + "5892753211405353458072760353770523366305643800457149832130812257775605711230".into(), + "6072173732058843459603530982418555383917328480859906190255818160437636798624".into(), + "12537411810282601586400313857613201485446415283134353086651901233218273826014".into(), + "6917382548294560607950289035591212885648538608975878502811670462230179895717".into(), + "17522466317865452308300593635805699602531255301383258028599652149794243037329".into(), + "13441087273853521452830121325700732493909228483089386934391461759499905593163".into(), + "16343244966838333682425984150791963916204788641990311583878776392583665902296".into(), + "5360331632046740140575498209972912123753748791093482084409464010367952817372".into(), + "17326261745640411514384830391871497358293487167841563962966663074154000109605".into(), + "11832463539058003103012764436368900007479300735896611859760466513560696030373".into(), + "9332530307651641794919337618798260519292245685937263028043423079258661105173".into(), + "18589656636963228136343540063542372097923688201290293330159927366465643313091".into(), + "19867733544550615468612533273987508641098463295160374186223389748717034468502".into(), + "21122779377039628476144200947919650885809091682970982371472379500808529828990".into(), + "3027016623356326893238890601376138710903222026620502687697483362378227239102".into(), + "18688538985571458464198537125324538836996766056814806096466271703920561848229".into(), + "7011851562043637216927989978271187524190844597096440024970832115577672150682".into(), + "6976276474543022394298383474157051715295086098335344328214084449502467982887".into(), + "17332130117530488369551999111592288939240424248347612731261799228734373086235".into(), + "11760005773828232229811117479200618747574322788890348748345009852749579147876".into(), + "2298592572354833593457878362078773063587664198044761229360308644323231458063".into(), + "17955258451898615415362730916130728340133607644716002859752428726933265234272".into(), + "1495160066997898368260698151648234013994510168440470178901776412293213577673".into(), + "9194083678217569822513593808499809262580238989709793956568701401108176409146".into(), + "7873147134182176342443626581580817861100691419143183095128784103924898338758".into(), + "16767569430250994260298195321407617265691776202802582359934917139510241475993".into(), + "21740623993440608033243076692676917908105712281956015230238773795741552902992".into(), + "13259811027098062609299383180292487555512077058811432839971137250443197786014".into(), + "9862179315713982904488948286370860706178740748908626205136774943873439543978".into(), + "5283798667794826693954690769969228006851703579605314207478989967594232871036".into(), + "4252584733067990475653166725237187089775889157323429734580856783776820381431".into(), + "12973205666610532230766585121029838349435114222222352682635156927179891214439".into(), + "287633827640277544463301235265949941438188349560881553375976425107945733447".into(), + "9088334958857736274159987824864641712847858830802240840003039491617952671684".into(), + "2339929702478917712535010770935571285116447513742167345028949204167372785538".into(), + "15676860121778371383024352289403909294835655990865877574625753945822204636877".into(), + "12764603061737711341313014678756180651482961849974746341425597804386430732092".into(), + "4838308895208597109198370123135942497394705098532245211834944708850927126449".into(), + "15227615340505775109084051882530634970803647261111629470904659446755110863564".into(), + "4983488923883229416538630982432842594678976476580011041728398828823305128910".into(), + "13108944499697608875788753758764556629418014383923261351989967790696566041318".into(), + "12840723598548611269818280989252209606886290717050913748780108061152527561849".into(), + "2952676596403552231673795576822772308483388566436384169804788313391241403511".into(), + "6122163114479319080164532464109940924328926458633830912986940513870140253539".into(), + "10991285734105320972904604247873526149047475605919579896179431815938092849653".into(), + "7998273930160782917435322581864589776803914924450929021174489205585585635343".into(), + "18738565009620163104846733922742158284630011729404799909319718271414375079555".into(), + "5267789578561581666385683546519898747246978052550117863765974145167441262779".into(), + "11531529707082144080873135819545917882620969294873081450175882450957416920799".into(), + "18780101368050367674291690806832757013309591034560713102040071432479406757422".into(), + "4294785451272921756656022299749163922793391211928749628139961864310109196353".into(), + "4709384663557124742915933403093691049837232917871836683405730215659929289723".into(), + "14881387167977166163300411047605462807380624840482159906092434632182882848470".into(), + "17882364021447094903093424534075826770336257800280425962604559742290433282224".into(), + "11702502532561044172145178834042891352292518800414731965233576503757342967409".into(), + "14059864978850405613608096166397211820819484772138676470000521070869797461865".into(), + "1780174207952381531321288553792322300359882548955331030522024911690744446923".into(), + "13028727766195703514931384985263026051594836628118028800883341509333550916528".into(), + "7588265429164678210439639249095081332309245305288210445601269417147124081569".into(), + "9642265589138009721420730439928969906364025440120910401773575492361904450076".into(), + "10203823545087224726196594521548654230876294164502500177215475003344697986058".into(), + "17120058099854629688232575694825768634386944759592366006492580522962871073389".into(), + "4222192359475448381394699451673800507222765289015074154697111370954996368735".into(), + "3993784121910007141124454385846036053674166308991223986620192089439929299772".into(), + "8723933019744566416337803833347252328328807961776170035099333968332078001000".into(), + "9712660217038548658606833881897330347907173853654574936296015219821374200265".into(), + "2760535358459121107870901826259907500576486637522442431581134088651955908758".into(), + "17117109873353435959712351313249763994000699552995926658366410602189225375298".into(), + "15834046942685590805387820842211523319635967093317938586307927436446892526328".into(), + "20540611330857822392080560199608407622162192943120548518785800532555099780387".into(), + "859364761671967022958373408533303411107861007743219314752373993491065278500".into(), + "2969887420026581030514962118165604753428498019600416703008438822193590952680".into(), + "18165207804423071202274226128984095583162198441473954125845000361590859071975".into(), + "1625906472120027348769657238152772240899619610263515830625917309020852034930".into(), + "20463932955653127762144495520150037479624855783264043490725916287067071124695".into(), + "4638144661708592747026214570394653618741348169520846615537958570570478926642".into(), + "14161574596481971299443338101939032775184122775644932228990090697003591542529".into(), + "8347184524978696917103428134030602422953969299144482352589164719698955992623".into(), + "15813323300222999983430155647025925955486314051966513470055962106024932062990".into(), + "15881789235120065904292616653574390818033995432226550640643889297228035707625".into(), + "7140692095240485283381028349925581289679980854833888018152159900503732423858".into(), + "19010917339674038277064587622527941116247281757743905062456743932291367865210".into(), + "14626089726879708665184866329935404127909714172968102824599946119716141024551".into(), + "7794590602415713436531570478653055404401179384350609462543106242181653823825".into(), + "8762400452539506415642761970628528841241237486958209742944443338295503154358".into(), + "8591771034219068368320608087757251879675617969654197927442982939795060041474".into(), + "16722135923767200283514351262528880285637400101562656889998995533577538049598".into(), + "89464124861540540773346410834052352014308242060847315373495379383140228595".into(), + "527363371365063907312062782674442937761539145521639367736772902232593392996".into(), + "2507841963726490485511335482108618130879932510673214677761709746737826243610".into(), + "11600559113928855816564236457966654301430060930571013803772049573600205529037".into(), + "4588632959705258135254026492226920833318427708514752831324568512946022675194".into(), + "10614112216523109461118034753484116653637318222297437542653638803709000476069".into(), + "9172525851971416839816439437260881435627775489982516326698553647792223169716".into(), + "5792656644651436247861358501481981837063028644438334753333821264192324104118".into(), + "12896881102412475846285309217744373816727588379892851016206737393174951411645".into(), + "11468716910877986153680082696909131615615571055661580004775449060276352989554".into(), + "16433370238306953119452573705529989067794419309147323079675422756276020408035".into(), + "1273883504219371718563834088540759123243117439006904940363157254976913033897".into(), + "12035946135822991823593467599431819071656367730183582372112230833767543617935".into(), + "1584468415480349659495095652095228645182004993793756033988320959253925346826".into(), + "21386287619427343417104905630296265178906342440158736945069239788114458023785".into(), + "8353771053561995945612416527531464945642582086497086814556341372197660908215".into(), + "21451387710023590786286131498774138351594763095518652036485484549943942198584".into(), + "4310607037909191923700937557843586755810945477870372794480216273434853743125".into(), + "14760194187040766562518106717503624683554510559335502353786749509033100485706".into(), + "1989722256243159180961728174407914946336713030784288455577084756567680416269".into(), + "6686508185527220254925691658356528598024140978521686081374828691583660936030".into(), + "10702736220547282218129581824426150350875608542279410979052446031034395032616".into(), + "21518832429688884430739158604898893220375510075322761868526168806092202700739".into(), + "18894734518217824473490018310535147336620281698236505678307171602214420149474".into(), + "13651080497325379255207286313267854150042736096310351308026009382931418587349".into(), + "13005423795701931110183481387065874846108794433756894029607220148510576563022".into(), + "1791508640954698127256430696076954377556671221508186631495000025000560314469".into(), + "17328036510202475587667192203250772802218778208191122529787186719212302087427".into(), + "10657963758634548274685968417226435837089760791121929723402922305566484150027".into(), + "1373906129582260565717689440357805911187492936689152530374097510483815842229".into(), + "9959127778329368795108284764279846598110110516049484774588901738715030294501".into(), + "7658768549089819420025961609972525260083323240153758777076357709438863924071".into(), + "8556349016311563259984530239150734383111823917689615136867121162886430935013".into(), + "5584784611907644257624218172047384219563403421449130896517357173082649535408".into(), + "12250428484720929053394094480089037225116943346244386549438429776642627577081".into(), + "5894587027805929865241808030013307622768224687737099108946198448930484926182".into(), + "4640000227377695114780725548825120725107463526354871525420448370293105443156".into(), + "14209260856334030476503172714481690115491571124367768551485878619193949022436".into(), + "12847259932859893012278918973069680996469229489435970735462165583338364177587".into(), + "14707765228045361126133465815432967736466274565508518646993164305646449611538".into(), + "4776938221827642494595066454007024055266353658953900973539209110395754289252".into(), + "17345634507638246813894504405651157443245008745673515073520154967327741083655".into(), + "14030342049076255140305545268617582878430218358427444316024303212302433090888".into(), + "3081608613732784391792246085705209806228642369232320349344206761814264266822".into(), + "6925544598968002128460287121334907968171640408686960604018975554503413545297".into(), + "6436833790683001261207201071187060041301666725391276179040664384301147754533".into(), + "15822971282077757697408895884235910303980603368062126288859779292088533186609".into(), + "11405645516905544384081084440311050207481018746793339445931815864373601814301".into(), + "8057851956466271394801907096296448157702512819801554261185443997708792484686".into(), + "13400701029177845728937105456192536667124279186738602373189177143094323643048".into(), + "11966218129646969536507242899486947493941404691279766045602150839476320695158".into(), + "15426703084090770508053125497176086377457196612492405232875849008237608103107".into(), + "6284964429736232174040445851307654679317453722810844133660225254133335862235".into(), + "13541475439682330895175095901646773164410091796597408681295206023344696136380".into(), + "746760486363018018017386520506969904420399625886684141218702464564311095236".into(), + "17524104887381990279039312517306518062132617046713819525042298049738730617133".into(), + "879502104476554315787832344079612181105511927448955207253764010183827781938".into(), + "3322243116889956217287455917347322211093649776443788544812848110324752380511".into(), + "20993304704177227988946248236539217504266305909163005794546179470620336655896".into(), + "15297086470600095823350435092749336605284701364464412627268330863173424318784".into(), + "7926831990131610326614433341787624222916431629609577848917665290693634271603".into(), + "11271157869866761687706729926953398656059815012440629723619426315368733378302".into(), + "18299023753364987381538199358983670915973737470621191699559419975990005285294".into(), + "1896523269277706381575521315908503178186583134996425770080392278759498041496".into(), + "12890346501512276297101448381578516979954988837373044432656264176509301317397".into(), + "50683999778726139626379750772467319774169558910797110829251191014869406584".into(), + "2535823474764747710030290051647541950275171691698903262059516064562837569538".into(), + "21244438525273542723000364561332886617309030482024380299212725538173747919525".into(), + "3566180332124074953578668480009215478053830658040437750684892555496819182968".into(), + "6934760873335280553519652185111971021084408206212600990256329557702647231767".into(), + "10716990663016621229729175818785901525044168020825517922453745625674725718755".into(), + "16032906457960033408967366361214250817346739568272864358714497113973437241307".into(), + "3843556041176221664914001975628278428195138883854078534543734996192939783081".into(), + "20159201956050700213417267403145919082229292739510798606395981692137742005549".into(), + "3946206250066677142194595031646387588831888396304269420593204783498093935566".into(), + "15041638172488186356886629449548027800133537204416329580391498341162500701165".into(), + "13278796648404195845011313500265526847654531008699937318834233691653233844557".into(), + "9401997027308433525669054020953469816624994577441846092561461184007344721828".into(), + "1983197599992175511555068756140439953159901969155075046465287261273690782516".into(), + "14404434816783701517710626513536590424905462755924351795276920360393132910619".into(), + "2265153992995968786485646516094566778663253373238301922539677847420824091645".into(), + "7566790081073938501935204486802755123563765476651717380300659543531475311797".into(), + "847895502208273199247564103189534214890423186204714047221696858073193176165".into(), + "11963011786171849258296957535441472428283852585041548938428326288746833247421".into(), + "17377294274187023009074658259347356248383537938503682836124988411002248431249".into(), + "1025753579901801011233785198665030697958075722777337374303401815648687864152".into(), + "19284019504590857558072823196945292661333776228606049953803221551974077538854".into(), + "314204118013777455817026750462293710777754680076389729833422020056750157783".into(), + "16480262479960754021966845575947830901794064422051876558808623512389290214607".into(), + "1118467174052506904085279606913979573425427098904935496893365844271017524128".into(), + "19632743231294128529717484081406317899334378150630214776880617163102648968680".into(), + "3911642624469919820429547144355740104724495930390962678347147942124333228884".into(), + "2066832981312040153658964981292230050110396574171753029116180129049784719869".into(), + "5311750082320962006044746418239249402583149931726835813032955695199748985964".into(), + "1668605805291951132468779303736499626803321838797652736089353191224211818512".into(), + "8117527083300107176240321998574781132203045054266787492339353711594335529709".into(), + "21295747743511951700871264160352432803897824342737595930859857743511472313518".into(), + "17388383598447322131131146498858701344049179581377651662282122447439867066027".into(), + "1285164961547283134680842635704675169007050470102043475645519044978941760131".into(), + "1875066966449664224570600546742926101175136253576702799078476168727336032193".into(), + "376316574400845651365366460471492295014146006765806307181242245707469972823".into(), + "17926797393698382320151787394969682377223530524132422415508830165925306730854".into(), + "12888413344274698384267384086384474622307363694087246755565655858542934044367".into(), + "2287154516202749380541196057272317720805230156010471100500251268302509398358".into(), + "12091269896021030725381711946583875149737687962883250402694805482865888072651".into(), + "2866048333839936509265230844807967492604533527765757660355779329791008604324".into(), + "11835071699154512268334373994146252361488263767766913426957050581680705629365".into(), + "9769574961167512396467538163460983111263497516872198932597594846643533678886".into(), + "6652717430998307400653769850757364796566010885765190753303419969578998970914".into(), + "11706514210298927940240806568375915039663039872151275540599047371226868127844".into(), + "14503173259289644850389467422758670421081762433800521230764199189580041755978".into(), + "14819563269715549434612532555018280611140077716865566327825448288943759458075".into(), + "10680481968851597237106707337987338182186447633463836689709242917734021556532".into(), + "8842568676211857695295830062491586931539601201165478151045194686251206282625".into(), + "6530738090493870852939715092085577161577833016969257865187421674974986108366".into(), + "9122348442271830670856465114634608231502719005088669868106730029655164877153".into(), + "8276880496499930549062062890222235677345093568048001523633692507246467514288".into(), + "9159103567267771525632277936018480854652194329966882107551346987942837974086".into(), + "16469319105211987147868896414457254521940280033150431057117354850784171354412".into(), + "10187807714948737954868581824072203418416253898435396377923197796615888421106".into(), + "12487902950231724077418777903531512931204649041424531773792238730349062186046".into(), + "7128847500406995361903929050204217572789701023482188856789540128660376992648".into(), + "5624482939513897521205456066520453513748417048856589163079298385117683428424".into(), + "16435310041815433976313715464222273283215148709695464992270777828213079073600".into(), + "6053110243546717824144224394047966869215089006720239359415188827006018953287".into(), + "12155283178966050547567835509058470201630806787546132608678364857190533906654".into(), + "4516299433052637321210501303761417706907638446979735686067715866937355560218".into(), + "12667848710309960989666753249292981950689410172688589690222261363826904222200".into(), + "20798966719534261926649504156624517814254561351583702314533431478871413942185".into(), + "17967549743799112209110259394081841952094330269294500558315036929658897218889".into(), + "17847714150935110939206476281446907230585782511584357788270839909827513843593".into(), + "3706525735092193828297046649401002937097518714707649753675691741717374375329".into(), + "7951431498272893618049118070591089643051946670270891223821638082032777246372".into(), + "18050610739452223409884731462410705437134574403721907246909018802102148878228".into(), + "7681233452521913704953218921671929208806506209990716853575264423980840711485".into(), + "13423126358538883907022095116894303361057277664724853411599423630968340874618".into(), + "16385186307753207860551232886321416660506143479955163989130433021552655688751".into(), + "19863338799254785908021006319182666048802363286601429063342266271260238637645".into(), + "18613864443384979831007688916611349052439959929170133474546815634162799183324".into(), + "16332951432721718166552572222119667287478083212745997662304261644978474988848".into(), + "15183520838042840868796366239500461619005160337097541115133209726620419190272".into(), + "21819632461581352219365791577295628387565956152415171439862191463144627141459".into(), + "14048533271709057229957508911818231901369646548751751133058494379648714713155".into(), + "5460165509823151384714546779805746551465260311624889230527298343216031522426".into(), + "7812320479950772901428223193019468171803323319794359571259026602925413892220".into(), + "6861047298949697363524436975701264276161947671796912946484193048676968364570".into(), + "15350000814702190667768044439440847568690389883802239839819456575900015807805".into(), + "13617689242559757166326885110516025083265117478902533337635065578646035372109".into(), + "7476843340092386505769477537028758806323421556263090744578424892783190404262".into(), + "14826737006932111183364733677544376863485041173382717893595766527131815613201".into(), + "20482222533304950354756700764919164516648372491559583778492129756055561547902".into(), + "20761958166613639518991371846609568602964716408676516216766187562459779452764".into(), + "383428479364004198529582849470440881390404766297544454256885030893768964772".into(), + "9096063532469724072461386558496798312147772678120608720218640484115240695555".into(), + "2933312980053872879818024429044461919397766967639471518371949595510364515880".into(), + "9299316327986512295931957414766538459714522358165090755549540858707887390215".into(), + "13488092390166418062347006407529577052576958127764741552594028975338262414671".into(), + "9829077383468426688376344724031102291718825262406301642537010429132676001524".into(), + "17306252922949843274941351200908184305715966839608017699204279919732909328171".into(), + "17087445274779483487397521683084452362124400111143021068068404568736590545834".into(), + "12957176730411938817539171430465916443660534484629425102659921020718336935547".into(), + "13051137677735706323786222675600013609665436923061132301167672971485926193799".into(), + "16896405620483797393236904159048703206424672638297382666760526590725313874095".into(), + "12982786219003483259026043754729673677479461289605569386777999422861331902406".into(), + "2025152781178874327274493576789397778291707054468278801142821085671743579422".into(), + "4335304794121497318666085856101737871981186359433585080678838732369068576376".into(), + "12639022425423716913754007933194900468141465623346480681305462334218095484056".into(), + "8623058240535559764777469232729047628411050024996927445391922152785990079007".into(), + "20506335897603554545414833361572976187621941529253485759521266317325651663673".into(), + "21253532477517846630911098846446871983813752932337294199438073014278982612265".into(), + "7690372912700906813961714562821752274841425123411432854046237798188463759714".into(), + "14774079281706150157728885377093724231164512116594840467791057304501073046227".into(), + "2057509476014278951878531863305791034496952436217061173216277825372715694262".into(), + "11541539364361827516114507593054831030438985642093836719960774810015976254752".into(), + "14751271529818502394785288765886130704541730104426205431367858892918636267768".into(), + "20653824984831671829709191706456216192511770845470322444515326861038843106056".into(), + "21794617070379980226628233360507465028625654800811478227502718633923944312665".into(), + "12337675699660313500286885345145964244870355316351529815248466118884457457726".into(), + "9527268832683847687152279087478212995292048665053951255936397859316414854388".into(), + "9676848840550815858798680133955006115514366150476209999729932493584446713554".into(), + "13718457801302749437654397628557435190188493872576865462727587887532950355038".into(), + "3867324761996955514268635801392015956546058763053808698121442586705196957126".into(), + "3945177530905850250597694151686052963152292050981415927393741981018273529029".into(), + "9473309264240097344589730948390594137445381539328377904021534848676986947654".into(), + "9996385364578703060792470002743943020203968343080025005448261317627854389642".into(), + "7671875350824730767796781844941080563527207835942241721783016221076158519589".into(), + "21342545963270813418079341151020475012992255268798567166496112378940512106806".into(), + "8273987785742417777035293384288140933989540189974521462819168552306151314447".into(), + "16162516432221515902182136237589464594279404001688534734264317844425059661177".into(), + "13403805360231306331901242144909690435939614369536490494604700225511328976179".into(), + "21640586272648741017978258285249302214133507217427529405059807093199643534960".into(), + "13103298902142682492657114828016226525668797790480257241873358498200705227711".into(), + "3251016042668618943397347852790038985788901898321438389540422820491143534512".into(), + "19418047092207786422594959190763286166909227118034848590779428639908706128141".into(), + "16018739283901993654877226905623763753431466873170287728134253742237701259452".into(), + "17444946939314191179654576411859454283526068144165928707555552999432295774923".into(), + "15128585121813842824984220733706899232093493608422847549831681212432769497258".into(), + "14256042951767483060181875165067417249801798477941578288282791927486641413650".into(), + "9120441102041865153472829977920901798404277796873323854759847010789551358565".into(), + "16824614118285494743762017938800453590083305489658290570438237922163546634147".into(), + "14066296536773839458459021526010711790088172526749593960873828093107983908643".into(), + "5017173601281838065126812757441844223954524936921765951283764327160567691674".into(), + "6254306109691187702602819001106221686689064495376117914248886700342291435542".into(), + "8093845266500847743481398069264511597137596242374064050252920004891985410254".into(), + "3578536483838573103029984288079029549393340013685219142070711310306738904812".into(), + "4046636520929111297180341957015914921052723918845140326762981717454102872515".into(), + "7446649718318072281388998859356555024817100796345520325890349758135889404482".into(), + "12366828085438268177090003236704251656102820345668679675414643578247762288715".into(), + "10235336658636336826692093860668421345161445948384101191460476320469211290719".into(), + "21557699385719674753050624572282725191612933706864216121110471118396873804204".into(), + "21676195031234643974566056999169564975565168161106308063972653296222128822558".into(), + "6363029841115916180506080615867336124807912331702908898766344258887335068638".into(), + "6363363442473598630315975560083028488315295047134858188348763022659100540644".into(), + "7124701104304155953181957655778532711129611550814127577450223681003954918096".into(), + "7205717562822389401795306034882449236003182581929449060008168502546885140420".into(), + "9228704321577313703028591075278114440418729713968708821922150367010520752433".into(), + "17614180549151511016628006398127365390131305608457060951973438257438000114647".into(), + "6418032200333926158639440620980173724897942384057810379437154014285875236793".into(), + "1563831431103246736895130173427149853201118983353993792302858411456255839836".into(), + "2319462743404521965297213055024756354349733992588917297296444679195296432311".into(), + "11927767606932195981021511932625975307755048477176557643420944859867328059951".into(), + "11544836785666122785129617591037772824479186137515525949187438698705551844116".into(), + "1594554937757396474339277849284988438779469013126014780880063013190163371374".into(), + "3090898925598297656723114627648156566177591433413115455417582215942083585019".into(), + "7446365483911748580722671015415226813450613606623816089630076567220802142731".into(), + "7277804562221458254100595374565579800145724793164291138831756170012315181874".into(), + "6742114513180059066582114839865181272182587124666919323239785847603969045455".into(), + "17639336822454434815461281360929380680585323524008927548062809039584978130493".into(), + "3577053914395256679157728351997326335158820293078866565158939674077090010647".into(), + "8416461669288393104752370629542582785060946946477447413022986748926853429341".into(), + "6094400991411029253990297530870616276561743402531435382960598226029406844554".into(), + "17249210115827920255165876773266699947215322191164909559927557290769226533383".into(), + "16910579881016588643870584852395328514859173042128957721312044020554328803878".into(), + "18548800605828674358041671058553674062230830447893195908258772241250469878618".into(), + "19911311709285615075760278233929935358167599770457743440215721586291252049005".into(), + "14163239340563250030474254441824385465013759375574504142110478434407678615583".into(), + "11636512441693145656185679602762396225011813780067642639202962721478584613773".into(), + "11627821975451263242823912745820712501746551642630006051017450144021728526893".into(), + "8744830043459757570443313311577350804214757169018319227421153827794333111861".into(), + "15502529014760684785001315229904504412899268461167235130234930235025949266727".into(), + "6349631474781311552829280885666605465500111601027794362415725063684145078154".into(), + "3750521632595755853919734934063554601358504982902984744970328457148877834110".into(), + "9151149071511054371049219579896119997746835784236581257692741291133486546793".into(), + "19272083712610290647942715418077844472245445971738771938657138000646231164194".into(), + "4825691749471936225587218745193692386291702673577201485716323799193545312215".into(), + "10104595857457718843210330892798628518507052980455210862700838136878095844947".into(), + "583353789674136864282067863249996585561603923757420881823548159264798915805".into(), + "9605601189839871334439279312434848410060547509366999421152290140420275791851".into(), + "9086947882810130237770476626970553738390827239675874433366363671998428155733".into(), + "939087130792625935525433768373221635016459152936104283115879791761930303865".into(), + "13281505022430256926767514868008519612680654933479762886734630854898956588159".into(), + "8791789091182070262897704714134448615861594471173394864103714557845783501440".into(), + "21362425094620759937169033441239641099917726266745789893288044988022041089124".into(), + "21264794594588373773536904588090499011630449220310853635095227724956212914745".into(), + "2201440000983888343103152416697093803247262395826184450099145084240341640621".into(), + "3661470436482399872770855708635287053510507688269721514323020960038319367739".into(), + "7839180633039164608733079650881737341586893235460147432870429877862925246133".into(), + "1364292691308802542355455613317294540368840799230835218243676354771769346816".into(), + "8772467089802087196594680904948784186594877728663145042664601336273257169460".into(), + "3528294282612889297228918585517392070857581849271784732463382671241778115171".into(), + "12283613789492092234060914364373118974353698674396863245366831728069561564466".into(), + "12820604492590938203463369747918352644874868843295488782427665387357902190399".into(), + "13019897240405241197543377542947534699610589548844172163916774096781016951470".into(), + "8978439745720039208900766235834036199032761881062736449080983584518864263026".into(), + "15222275515593239598230012498885809744566950744378381699594010392927659453864".into(), + "6158846143100676033784762660398310184837175038752344358512599894306514737286".into(), + "3419255917977463022603617504775890366430819682320515293245827217078337799273".into(), + "2797549320616484645720845216321636133654549511963540737715630452964584943931".into(), + "17033753202496389008484483700445909344644255293271694371709019653557282454651".into(), + "1896525136564670388001069429586407694373044886658459111231294687089652643180".into(), + "15410846104953506982390647924353051056630524372570695181992786623509940504512".into(), + "19568185152800187670153137856814273221696851955003399523563214963248234462015".into(), + "20371827999905064832491002071821973934784290275145413746617942892257828998608".into(), + "18823477734276348993184895526705908983521278925070561827518170646437136680627".into(), + "5656115226652970780235535706189807810471572029095698537591078819723656151288".into(), + "6421434687984511183440563950015895899135677057679953590513947822215985604920".into(), + "12040384754225464077155439050798210419160821091649797103441177013046869869449".into(), + "9772259825341200622705640733478002328903138705997041039987261797557503104066".into(), + "16498280031288829288227785307302736352936199535492502196962915104633784174443".into(), + "2797879572297199909334218711222177900854374704477138347081058961121300793309".into(), + "15644078107885262244699428484967291897698559437568086329631330880220396182101".into(), + "1929505350484341295824323699861178332578728141996333839839142429658318709684".into(), + "19829107623275996909068882503976730773632786622439634235897400991781002387336".into(), + "2349581104996259830058120951667460730076264470159293978617685139254844279099".into(), + "9982268711644224005105745225708318829304834159499232155579405472268549361372".into(), + "9283048909915207444384413611285315401509676836116090349272606584736947471535".into(), + "7228184586853275989616881380719765868918305938029647325846170897121335348738".into(), + "9356148801854782728189493667017091537583121935960987886599900693915567464857".into(), + "505609318575290577159607039016980344147219758564644283699477849237669899416".into(), + "14366218774993226370408163695258733389122646477999733966996434517776361579764".into(), + "2089359639878444205205504625611416181841134778729917955490118311877471215733".into(), + "698820948911402576486200349924367867353277614409335325450207885100392818369".into(), + "17294754169238631079500422194295778880256675645147530605338890302947026045725".into(), + "5743114444764763990137499217919738961538814043960695215081370978081114004215".into(), + "1532354676442440269809361487929394117850339080897231885482935144216545001511".into(), + "15429279362464933048379761813318395642886047028548664371898579661267949218379".into(), + "3996365200571187062769515782559269970838089444839642643284597375210723401466".into(), + "13855942579438265547807447657820171102227439902714241541267837936207338677600".into(), + "10017315263017624310653373895363026608166903346797759979504062819071927130663".into(), + "16907630432341625239068562338272519243551636457245225084107387534876995927261".into(), + "7499064872525146201613148795250282369576850162788310832273877734938523830020".into(), + "18958126705503391332983508022395142836904315587480202621370522508346095401093".into(), + "10592492611778631944134585106195825750734295091365114316628335289401976557048".into(), + "10233935978161235645487498670591630814869863266947595645533111837358567006403".into(), + "18003113120454128964459787917100973688362112412222897499908177070949705432002".into(), + "7039143063746813662581342159908917130120555794319120640254054115394750527325".into(), + "14642639620523941644124538953496946557379420056235185193901717503194398478844".into(), + "12510354962778923417991098874785395513418703200631409704677335762559702597729".into(), + "10937907264762102220987756825649855260366448774314976509103469790406549413612".into(), + "2856291402269285412222070625188683697884675330846580808282552440813379388545".into(), + "975737765008679829063246253813268073237541742979444038400325932865440884821".into(), + "10846579585900798888776933082578183338805087990458930175500622018769094956268".into(), + "4529602228982157635664617353676370544979061743349920696434925695574883870240".into(), + "3217171861525061655884750653752887679288235310048470832136609603512214131719".into(), + "8115945363448342140068934999604399115922267391140037880996539055087195710388".into(), + "17017061429975823256838374218603998280878588780557395344098357759667769523464".into(), + "17414822882733297737417491176979790219384604825369331340721407024843872424842".into(), + "21674124478965998545524545297025636759207453169144537816299040483190680515948".into(), + "17404327139726954519140759696696303110669221199013129437266401728134593481209".into(), + "17114525865368445983838697023323950751616885090116888636158517909348665004808".into(), + "13606067029568271979635008642466621607360271211611257253834134991337929444625".into(), + "12697011972510852002753203688496604009044404157472710768958968780456921052232".into(), + "17908262237776925120663573891641823998363270265606050465713306950786509533013".into(), + "21456469759562487171462776687926242591840822996019886989101706511495101340835".into(), + "266521013932216168881283771008004557613060760037591295825839278412991410595".into(), + "4426122336489578598096667658844019947877597098504167195068107491300030650784".into(), + "9386708946433085177945274757444606300245243689157314090008872819375610588441".into(), + "17571201661176038894845217669278055388543910674470132087585433795958011317108".into(), + "4455879691406517778738600662496288227132258098615032267607735047373372289231".into(), + "10411615872931007756022633910690379435607807745769276655537160609832149769392".into(), + "1369325193519585971093564667957731115918550810109208821335573114054160355015".into(), + "11701493479673417478812558973813785951698004134589804881992366063053267216767".into(), + "1604976076096057025943679983359889701146704427744088265030663068037609417365".into(), + "5807894190026658662778129602314598491833803649976558289016319277596337255402".into(), + "6604568830557716159440870492208510177395633789687151427836556682055398194130".into(), + "13221322798326908033948171538573182873220915914806870794442015701313412462981".into(), + "4276423994177015753526554880283981182675757973382385661312000468093685717116".into(), + "20731982721228405909675906960993136550102478961136398510514645316644217252635".into(), + "5084416110782735943752807643199425478022977025123417839387489101289418017943".into(), + "2080632875247898509655097068576655654491657659787279629928499240720334125890".into(), + ], + vec![ + "8798508051216852101945770298446195263426534955832706109793971883081428107277".into(), + "18986509933027080134736570007084643366993581436980149915398754460582000677690".into(), + "12888632176077676513526604615863975427335143923557716212154194444095746278696".into(), + "15218363940991075171925464923999816479227567707705088260324253815128900522726".into(), + "5123856245344699442960347063644568841985144075867098178729206123344695866632".into(), + "937497109650566529304426914695525196119182564119552807475810542139513078711".into(), + "824338985948318627675352935105995425917124279032809525354560876904860400681".into(), + "13942203133811236390194976449250445722833065335401645546668049734024419803027".into(), + "21193497547768556525501812324063541319364865851059424372272833186043647143451".into(), + "8250588221665279725951753271334754148372733354841465281007604695074534688729".into(), + "19810263445128123914159130235504848033923366950322026683325764129239721093372".into(), + "14320576994914109414332501339118128158228930675887122896979269439627163060693".into(), + "18889264041663920880871940494605753485014632588652805059026615325416030193719".into(), + "10698608730721598729809918763148392473410663742558667693804099019738511427166".into(), + "5471531298285601691838160103499608876767872950664004448938449413930813358696".into(), + "11457601248269622750970644898105156673192281665198367584690238105651871243849".into(), + "16992951642443963917535033612483493956841253647152989134657936764458589935104".into(), + "10681874111209657199770620844475073359855652721773538207614365548304774934508".into(), + "21875342192304323235337862160226992419479798345905340775869689347883470787957".into(), + "18783598503862573040343207522848147282351537545213737188311758281500392127423".into(), + "1221270316640374234025430293653174054626512367600614548099335037611485538579".into(), + "3803127350594514565431185919859613836470412459545124433755604579955078714169".into(), + "4692704173297526575662787527414813729052368393667564364621548376076502682045".into(), + "14877344233698668087821753402566363996983618742746012792982725192235615274582".into(), + "1054560579819817802724755404438987869385528807680314510667741074193700268397".into(), + "8052216208184446695555642950254618937388238814298100142455645263386380520432".into(), + "21528486310986712871728288619461268100617012373791428855787397091733571140849".into(), + "9607239927604256097021258716815750648977424271874860033430637795556811442682".into(), + "2196312812082021938532541315765264710074289763312450521728804241715253452502".into(), + "6605540160838083938636248853297421723407665107595785929587574408377531474397".into(), + "6525948153406525772960169117380395975817023461160134354401655738067058124631".into(), + "1003556227519998429556476568497227440137917634405973503866888411893650518666".into(), + "13677584968475474622689223355399150668790874783709262452797580070344541448836".into(), + "4272686670447940608576804106249107479440735969672981187306042096242258850880".into(), + "7723948150633857131095670967217469617626942590283045283879017721699420758313".into(), + "9563761958679910727099148022742805150896356950128100201760652748527446140573".into(), + "2850052690445041465388681891454371728435601558220304706587137488583335087257".into(), + "8526775100524058290507772716277462650712450825299014127801740914862834880076".into(), + "3867865860920444582361195032750323847757212456942796852256870663055781206979".into(), + "9531131437985240947539412881613144640386246585015365913532093261866651903759".into(), + "7876176264763636659141490167245054739857019306622806711043600468363729864093".into(), + "19195073274099628437757763039411307254419435216466391625117296911023053159082".into(), + "17201596318516267959242478674525331020962456681235714852429128354797936423512".into(), + "16429987001981270427843548313178399452962937303668954688438246688799503164984".into(), + "2808058398896238113910841463946229560728543614115874664163904810633100174930".into(), + "14219106904819518684382581834179422938681605045169559176622588393807041033595".into(), + "10530037691301443797595179975755234137332257307745168342967468344289118325757".into(), + "1780683171090951191330938815635375996828930714110381590102230999415067289139".into(), + "20631152509228411005601100836698224543066495368069041690921190101674287477942".into(), + "808499207021654020396580110067573161997188131641925849629551231827774305643".into(), + "16396331554632517617174441423319546296247483740125085437207362257874071846324".into(), + "2856734177282633594896606301173852733142847026457749184584827572766646232552".into(), + "10322006004350928347565262203595059371944943723449117022023179276501731876345".into(), + "2683049508597401695548071522846046195699870016352617057472448981851954812059".into(), + "16247545910658018105089170383534659574306111291405960552997108542031333797235".into(), + "18622054704352443244328095600847746501599243235049513145116519278941866455272".into(), + "8702183185743408572844482710294629749653378107594727106707156268893318308842".into(), + "14401120539669001367195063486710670290450638416936122540739824928865783084126".into(), + "1008932491940705147356558882115440120351684984366727805937095853000783157110".into(), + "3335415470820584035672268458205991064638866247863327497697585974180788121327".into(), + "675348271572506248948949205284720732296364868854507020989147277054109697307".into(), + "11425495584918585085965114263893597096866227224211705734884180990929134229634".into(), + "3139501819004172358132939651256683854267531704642601593201346365873114559764".into(), + "5783084359807455756426365693604321435940832494660634548089377362001475784943".into(), + "12095094864547001070709219304829411865589880746021553931719922191437710414601".into(), + "16318907049539057156624360879403805284297767691522365017367114160528018468654".into(), + "11395679267999229912686944592292906403252965050428182621076890662342583575672".into(), + "20304144520028193526915712162937805381528012861129116050582705669151951550558".into(), + "9782367134866186358061353221055410602027546275400836771195060684844149613708".into(), + "5028029607389470804476265609647401798747012470860555590083631854016382301797".into(), + "9007627713847325367341162188681367076476205669132463414398773746849014673910".into(), + "14471089911822534831111187815987523323244485794708545360218598163622346282781".into(), + "8019180100611273479652907195647951337613619920735853449223048779220663542855".into(), + "9047131412613791119405208650892730556330287685760678509615107996866748231916".into(), + "7335839265467743759482091707230237155828204454352010285192894893585057443442".into(), + "14787708665825803366954624175778735065630831061644057898061768895034788739773".into(), + "13527353131634051756987040962797036674382517083826397597671775590484363794989".into(), + "14976182354818370036095296869648510633049821903988384882639150664375161429327".into(), + "1560099560219605560187324067315095140041097722845226997637592687804174779694".into(), + "571671177230396783003670900588907847431348620829409362554250135027808522430".into(), + "3595401819266532411007886403698316446251107730632935527816690692561072394769".into(), + "5582619801223860759886119158956613012355932421337001876528290847176002785551".into(), + "12913958465964733555039885816821695149723050623118481194340346951472209298707".into(), + "10229385924925018504492473674441213223237935733123654864337438778499472034702".into(), + "21343876046925805512584981384457719270702482935633311830189677174062117124219".into(), + "6420204451041321645976980917151646516083600244123365393849746698818033299571".into(), + "19251614427061141498566572096097506725955838624383486128732046911906166384020".into(), + "3637855341628865891325364423447146770285076487178203925892266081818756628695".into(), + "6291095057555542758591776864498618016939082736178179913313186141723218066258".into(), + "15680673126007380386463552533962308522676179674235002556809204723523731273305".into(), + "14161910492383069106925371061927425194676913565753916851706224000847740857190".into(), + "8408124204639665996401588312942126836254203358943885876199057731364731921137".into(), + "1799578340300310412035304211591366593871976523584701800579066126447600019902".into(), + "3622312705549913151631696033404711371424834689912295490477655370864706883384".into(), + "13273792131373119117124844835198348538764494606049066494384531598904652565002".into(), + "14270260908863510838028225519201826231421616414346395482851862560529215543207".into(), + "1633715375341396582749269086363023314520014551113162582725277839347569228786".into(), + "3306641014838747329624593360740422499925312234577578401365800985267347686108".into(), + "14198045292969051397674853108255956449504264447127690313478218267366467132592".into(), + "13392638089393029927126006202520304645547980480174653070691229667854016527907".into(), + "3343885948493512216041748082192584170148231918609106413514016808164860398278".into(), + "3168199878854830552224332084447744849594328418779455928665560385861883941432".into(), + "4414580242526682670231711868212166617171432599055299074136917305689841663873".into(), + "14102753845056293060191802617344737652392043747319090694003879675100249630273".into(), + "5573924339361534820906966230551252047468623512376332682801232194633308271653".into(), + "21326161716444219551593862645873877150197335577674154159730974795774063715301".into(), + "19432958264517639868400987651848393946587739437911153002798497064389336859842".into(), + "11213890749343283806487445619338898013466071000086191642490563684623068539710".into(), + "9198922780305686612592947154769134303077110139729181291466776593119643796878".into(), + "21136183246673473125620223552691909911094171966517655547561646692807715461191".into(), + "18418245895226184084223713341727403858051315526021187444511237652982687814864".into(), + "10761950212602122725874752124635327020166823895511275020048089638533400314021".into(), + "16715137911831586771799460729625653907150059087921720918100590730689273071536".into(), + "17773351327415330719066508685011039352345666357995827941409702750841801825772".into(), + "20231347041576378765333233596636479502524461869780149044300194060543432934519".into(), + "18064563634590190884918686050639690971635922850408462075670660991277444012424".into(), + "19311806793959939929201104461555154688516767798907269687984909834335608414545".into(), + "18574604846458382436863152415585348247597412723601050266815439019897542533749".into(), + "6791419133329459189167506884324613003818238896866734783035998091675242101498".into(), + "16020026112470750192482231650248429937269643957555877460849721131766422051297".into(), + "20044512180053459577124670295358045421070313597307221323295226934357969810703".into(), + "5766192039785553259974801912149788259566859976619902528741727877731329189756".into(), + "13160650100556144411626475539530712035286115503483655434606927361569861225444".into(), + "9256036311849117781394603817644061414251138927392105895934210116699839233747".into(), + "16660304826343536533933296919943639532544710122612011090406376521894383168856".into(), + "5613106871918486982198177086366936520094704188389198685078718794779139126942".into(), + "13070598684216243718425316519583985756848433332087820965201044383296972346897".into(), + "20442537178749694119236982549472062379434941056035907413284316764767638577672".into(), + "7934380525369255820244941077363404413659578389361986061643146925957595190009".into(), + "5098727584136148023264970031064585917675686576590855834683443649082028079965".into(), + "20311250985440103383589841574737981968195820548330595923330987127317084128947".into(), + "3768224286214159313341476585351274256214081884717440852909749729584659454712".into(), + "9851045187544813632080431293737874643968516290829960553238754661188963821885".into(), + "19015242154372799267961042586541854827581444440949327264389997835941454171864".into(), + "6579883332160451787803375748174162900180016921849643588581107525271529605849".into(), + "10040045688615204420748720492172111994988850765670088109590569954563466338788".into(), + "16200830095429004437461800470410149972585288873073724430176128378316485348605".into(), + "9682955393234868764418991570718636989945366768432593131049879027562290804128".into(), + "12304247432606946020923001398973669839540045070461170795482951827192644001957".into(), + "18191542513141047176662091981681722742011120542435182371090664849144982674881".into(), + "18914155310347961884616925389217412880576183372588965807237661684331120845382".into(), + "13251661686687775186900604006618374427674252707179991219741046747323389941232".into(), + "10249194086135063113459170280632696947988284060709952681203426883826751049011".into(), + "18372233147890747575375422462203968154682646712463764047424428288714397527619".into(), + "4638790386282413677458315728120268440302827185471341027696814407017634332517".into(), + "16623961311962310335126734990913554770362581939583424145026358913546809927380".into(), + "17860493200690068167535851968852986582339867469465298666834410487621188113531".into(), + "4608305735853858774877121807982873005510358307902418229099062852681969399389".into(), + "14245817054597951763417586258404021745844024655622942045401677661648532977993".into(), + "3809843434538253198074703856325693344260532607506586917570357443895551048805".into(), + "17962207143381407459791220803798424686492359980872541932770670422523978330935".into(), + "328196272784453082510222579442282995329426913454628381427614217629272010096".into(), + "12374354866281475555079168690683958365929973901084398491477273797832999248789".into(), + "20853802547446175043916531515983184048009878116903616041233975407782898112824".into(), + "21496110961478435785328603969876614135441859248001892605282920516413194792470".into(), + "174344306580108774551764089189043165870298462272157912012996952342994211137".into(), + "20687536797191141044727224925607387673330130224876076220260820047611716395831".into(), + "13067331728561732140381851790951434625007152955086246816336518249186949048391".into(), + "3797862935019829095677036741096081889651388634128316641681020782687008662828".into(), + "19718547173056417343715166978970272483152981364164960220358919266690668222316".into(), + "4038867288048886379019053981446145278427073511478003014108759946240227146792".into(), + "18486919751583655613568544119169758176904901075587096135301780773087898879007".into(), + "9808503221335286135961820380426080374193561918865479543141400875074697503967".into(), + "11673653729232546119337900700609058880494248359777926756321171038104786463073".into(), + "2206576012107962093523647385331312771747616647783007209109500521090968991064".into(), + "9800772269959998996051474306860888030671933506518907174943285018516216530078".into(), + "704115937219213713489981656324169840669771632134921090864597729550810437103".into(), + "10450936455424589171986477572900171091143445830786569605988162504754224051029".into(), + "21102186642636060890935221995324710172830713161820423345683707273376266063905".into(), + "9163879187976750746407679763920281609583516506018439539723656773309855786163".into(), + "3472157821801145643950882966615731480318387993084591537527672102896556538908".into(), + "2411890225861629547000508300507629335095483162292985158748687911181232049540".into(), + "4985409929001444812993021607654361988376208034496482520498705351994820408485".into(), + "11553789937761176964063895958945223002657928340200203811287735666719305693830".into(), + "16847788791386791842258653027481339013349340413294800029138566625971691859716".into(), + "6760556724223230789549789607828387138865070021837886343220335025271850431529".into(), + "20596400847159894247975659733452116044170283916549483164302854789226390584120".into(), + "11672317719239435686809215238371249803784157167042393847476181444969645566102".into(), + "2540677976918908199177572390470632080272842549139729131369041579508211107531".into(), + "19442658692908544356400723297250992586408893464353224580638700020103731410947".into(), + "3126219492402213962620193562481318950501969435505738692018385930122987610651".into(), + "13815812651475874136168661258439176421344570120756240838952907916838907109528".into(), + "21443943680756083274641262528830913057368026549878765925197792130996882062057".into(), + "4605307168758134905354332635309004486671872776946937049460665298426311628483".into(), + "7482893834680785986692353891953868933021236845332400077427554963067657007377".into(), + "5511796194740668281350270549653399801893104530256753635073935873703864854017".into(), + "1743641818114051150153510266165400817966589635697948934570080374390326493276".into(), + "479348932723256576178997677888058436058269610751399398192779646687149113652".into(), + "2911895727864385123299618342188487304466499987531177084686132082340794352256".into(), + "20721945554748282229818069151690746976633856763939126384250406858494706697945".into(), + "12250582445106986808691543043877193096050336861542243284061992098674816799798".into(), + "1203513568724767846410309911297210537295690041996158208540636591896498697618".into(), + "6015583439542775167098811213988699848659623500530189193065070507681443523926".into(), + "13090526755326389175310761634465981673176179379933796187486632857997253599596".into(), + "5204691423542830100481358117753845481374238423707185894207602712512441617128".into(), + "13451357406880550276974168929055560555273701366887507611491312211347374906677".into(), + "10327523031922710495308957226255708193968398139079389167803483310323608563088".into(), + "20154372727310541088852354530646786495003018189623626400646862775892232254942".into(), + "14022574434398251358742711545518515409780772767696241502841372748111287365393".into(), + "5800935164580219886597276061890276866350374777469717717325353972942679889986".into(), + "4950159996548574497193804912590478284182513481419060390699450490223901282265".into(), + "16651189959199821925513089100199135327590269582473005780223712025001916349248".into(), + "21313574815160763657406207917526347065428582824748947465014119238740065134827".into(), + "16527537328830884258876707902602749401809299353104838028680907493128406403510".into(), + "15328203752181180061659863584748718494669846163715366299567183721088123843121".into(), + "12748724793783078481516963303490468110393403014501561235873124284244901317221".into(), + "18960625031156828010763136389916953653374660277691326636541170359289051172470".into(), + "3539001622988573864365704884620979405364663891195701665878843924015974631139".into(), + "19440492503123228668247641573804742990539424862427319298139890001019307139951".into(), + "20663734806319981969735713814960326046259899097186434280597673603867647826563".into(), + "5638550801924521462873676353658824547611149604620661155399127974706744652941".into(), + "1371302213304057480201653073728712066696195910046203705054614806040591336203".into(), + "14916746318031473743643312292935459967335724866678950897137588157969538011797".into(), + "21398978398848411648231431125400371920346497858046605440294787662089399525957".into(), + "17030308547789363149594169656019066171879481623126106541463663868095060483032".into(), + "2440921987343876776494963936103708492610319137255329185550543355589457647052".into(), + "16578274606037635679209783094809437790602872464993592428997022911117833097149".into(), + "584917957795662537423624049408930838570931949382380283787668885015699442922".into(), + "16787159358764578324620871006477408253726184555269443751955596568136636185295".into(), + "16078611438600534298861481344414944861682463507987217038328914463538066716267".into(), + "8692607842423869778858860521189757647588004328790444839011233818764128397786".into(), + "17461551965098847063794186994739508584146822190403534677972847044656722703432".into(), + "19452713388671907333636730760656181158378071792518077405629847148635099895953".into(), + "12121230247359921951208053896624893871654915559955364031951409301929413070034".into(), + "4704830575919764561517254527427722789804729564629801789022427396957683932024".into(), + "10670972751746137966064978863033768730585193577203300636726301560193269271720".into(), + "8049285218762936069773906017628569610476710004139648882735854791085139873871".into(), + "5327956364112241273910968805653529609896670219677653687154759819173833984314".into(), + "16209531049562245939203780811684066382629831507349840609244051677227948603148".into(), + "2041493598600414785071850826176044493717208172684229753087779307523403234108".into(), + "14983712813504746225033809615975227528832540720868897893292242774973221269350".into(), + "12085999002734552992271490964670175347983721486216238101789957598273374406404".into(), + "2010148192415347724117376912318888220341164166588569584654147279107677185092".into(), + "14204865408877079652583508111640133693635228399739312350971609944706493769468".into(), + "13330601146337713429471908542961289712020817519764634531830442337284933922809".into(), + "9363311761287060295640718461317428792455498134298183595944737677775311452049".into(), + "326899109513011756995862753231193478723114246336082392308214181273112126881".into(), + "21599711748797254989207108046977874436425859822146104148381410251216892766416".into(), + "4801349866375335173289265243677780019445351052028447809305464229839535867723".into(), + "14155733033291624452674783678844450106798515225633669031829757876654937709468".into(), + "6249525168453913775215814309255487077884826362528424439922890888603955052499".into(), + "14928369633463338040897134982481546746234350670771618196847518086862393490357".into(), + "9125617238450534926549876296505645845116172794938106232962454586773357787390".into(), + "17103886312678741706528259762965004480595318892735191450742885850881411200483".into(), + "9561133527164819548073730465187187729026229121272078865756229410363525806403".into(), + "15499339092812475393717971205752228601033972883632736148845868837711619810573".into(), + "4039978125105304479934956398755842410107811106124175538862554473225969256475".into(), + "2947887467809254352800319275622693352512539235808226814612313636217910816213".into(), + "10793550355337981917672616521499626144068251539193270238994640132315408061844".into(), + "15282024117559604646251556024222379207458577842775127240432856222129525583388".into(), + "4938487600404790095807201685235688647996095712925228362099985579653367819830".into(), + "1580271313232748704254579894100134003454608977989565849293374431242287188203".into(), + "17650488790850180925611249978112046323146336603695505846749648092705166179072".into(), + "6318477650409317961441518371381185077182540555690253948041195660104038462403".into(), + "18703841587534276582593528858657361438697750302064291203982535124189673569022".into(), + "6570410039872146139634976314211122538651252189045191357367613723647837080131".into(), + "4058984348953256905202616995215664252962770887032362374636631606975462596559".into(), + "18730155671005907941866324013560951043352315199408210078231145351355402093830".into(), + "9651860366009425828519962206873795166909640005109793700260660519169015873934".into(), + "5838396248515026677336009419348618624164637668061855829375740686597759055073".into(), + "15333954343347884216807246400300286008899152705372200767215516907035196412750".into(), + "1930765848863686695612705389177613542562429368163669004940380022708446841540".into(), + "10766617427881894379670242247746203663519351900247145805786260703840625869300".into(), + "5568751333625760749025074267385171935854368145626703295693077672484816860148".into(), + "1572988267140272358245681202814159120843671145631424724491020423275325461878".into(), + "19938390788588115516825752214441323822601991108943527563767657270216212171610".into(), + "7571912702211887911260059806577378242621772456912160075153794388926572849145".into(), + "6858979093205494695976875629144179789647553123886814202229119872013338208712".into(), + "18762692183810081055439753242004382629435200839335768813270546391733006575124".into(), + "15741259924712306779754554541398522471185852133913466875986860727321229948852".into(), + "2627808396704584386154002253183040546217470699174068260518444240974395718442".into(), + "13588659892026426409480708338634242111974891480475426278869635762077927444692".into(), + "19520457627758833811699878500944809253730386938893290068023237193482219846683".into(), + "8924178241223608091232956607418048641173985923478293687667819226283386788940".into(), + "5816806026738575389886097663948769844875803508574710698349454181988557080269".into(), + "12475579687915690911104520027833630083645170013159764349224529153441972577879".into(), + "21056333315327274333873056478119184274531121536034759580035646721078602460263".into(), + "21444457911856423029054975409869505918084415765420838103204937942386934191806".into(), + "9510785855264774762445687543927870120017463057252636764104600528992946593471".into(), + "20924833468200564542464376523632289762564405387275694375761127017107713239560".into(), + "10520571364539530696429244627292574117914912315372596265076914837029323332385".into(), + "15676633242665585964368692080238475248556973102970746891067091503318954352189".into(), + "7605218463440663468653884707661331637891252539306862971462289235362996643878".into(), + "8655416175027898012639173297596079731611447754405890380300192905812736625807".into(), + "6587158966606269464624627411770696301475420343587060761712532930272301692115".into(), + "17855802086555955087208003626983743721607218545075932831175321330674668519051".into(), + "19637270878895908891611772385398729322367589125936403809351999767233932966110".into(), + "13844120079506591315671522711954211929776840478156307979285069853392230439385".into(), + "3896487627666829488959793133071741228529962458688538373863209568407375861949".into(), + "8072922412250331052234441827200394762964253395032633720559799911487619573310".into(), + "3446650687448682504274462997312106041057536787701884723175991150707212663201".into(), + "6750980474265223706441237350209360446038939446132826129894534048737231433466".into(), + "7932667749655660682554204871450377554998925079055538597319130615505233358592".into(), + "4551937123821223485620220711817547511116227596903370212234130807711068597972".into(), + "8450248183513826743946014093205824327880033986070570398677443659142729011179".into(), + "20523087403588105156975104084680617581362696346833272310988747947340344564064".into(), + "8021873326844086700417206574915335220587232800373998588094532336448721297757".into(), + "21537680614486477925570839538957839593006098192503694567393749587557940631967".into(), + "16568393458463797316294547173419555682646841229029462217197639056287054602566".into(), + "16910235738263410812877183305130636228751042299094737782744154576712268208807".into(), + "5629150734321853465674510845327410940323442617216962413835214171701329559576".into(), + "12765131184498380847448550948920304867469527623961001350816744753387093810315".into(), + "9954645632746218155245325585480173993199044803581756137160936155060542951690".into(), + "4353999640943599221118281386228426498583991413938542575297776232303344359940".into(), + "4598711079993417664489400987736110506328223791894077517963275447468098340703".into(), + "17317745377506102303500052440899062192892945727209124468437255966617200532771".into(), + "12259326473392165107331141130100680225887121989921120777667514143912156322366".into(), + "11811238683014115897858309619999599808008425203222607881921631505596453270838".into(), + "16699081706307776306233318220897168744889069844294482997417168581249408879966".into(), + "2568026780472621467236485312283731374848886964214142251868040165224695316461".into(), + "14887991441574434999687627633546581501365901473545173785249018184685706196387".into(), + "3569299155477721143892535842178855295807355615071744617918410766174933059043".into(), + "5498879700681283951662042539400107058426136148099967700430316029080607629654".into(), + "11559660278391175834668360323261696294263059777402960440249595574565141656556".into(), + "2245933817469402341452987887837905070237745132811239554297372991710914298182".into(), + "16951354954717164970902473060335333600574402706601527686301478774700332367102".into(), + "21515931335312240603168050753263038826093559960430063520103206567658032925825".into(), + "1249411708644964091210440756030782120444589186855809089574669034358018078687".into(), + "5918980335509160299664261285796224662727132279493574691223062837411060350013".into(), + "17739377184579332650035245294019601185834503109963479023132769460626331642282".into(), + "18469744224495747473457470133545294362819142862108342977835066995529981406258".into(), + "5759336781416534390511113561800934668877971293662332789437681884454072500101".into(), + "21332416097583521698710421619829192185814871724795352151990455290564854827164".into(), + "14991738373834450996520664429440490206770501447625984036209559286781489862494".into(), + "2292383959319987392245961291693765909376521855421393945872334290076611679644".into(), + "20271880109317422113691100242542434637543023179088091376781918851828907851136".into(), + "16343203714510839250939280610780086672320399980775100100207359519193914733737".into(), + "19668001496569040779289443981105684833630116796777138243208746501411413314400".into(), + "14867270521039301446053304216390047301289545979306959364422905455339336587211".into(), + "9929001722154986846461251622116577110986506194975595510541659282347340459331".into(), + "2420696236635890839197290329114608036709859071844599545644710485458818798726".into(), + "7023164250260724184662962208964026963022579890290101367655364067452762770181".into(), + "20508512038053113727358039673385440054337319672353371627416928672143778575477".into(), + "824002554707189532054522614675201805472052085843409216867120989167259938133".into(), + "8081396930643908204119214488991799751072819763127464506132047127093449268743".into(), + "6146669896192112367311424975035875525640347544666981144388229137148737996133".into(), + "13917552221424458749711502489369248582964839439204214559672563587245770000377".into(), + "8550914481815010035465313609214308578203171728075688232687655725801893823141".into(), + "17861710896808118014581828397113186358973121215643160735097781787683890541230".into(), + "15595160929818874035046864313228963482705740989418337036962890306288699599497".into(), + "19810339910854592376336564454416808551332919752503671392907063850461349060479".into(), + "14234414635213293332305234819580453743707554476430770368421971402668862484872".into(), + "19388187253394163585379147543203559718098061800153070411989058669962446974654".into(), + "9996031022330018431773704931033232677865134703871368350999928908636869707277".into(), + "4533799599412452199761684722747577946145532223581271248962115254510905734009".into(), + "21793143524581931493773122340799146504825838409926910431529124937257399233338".into(), + "19692292712401787423885461139555179726227035221831860547833110141089023723875".into(), + "15361825934066824966062130821388513267446230286103312548685290096230989843672".into(), + "5736434899097417592114701714539404395501559559582922276628268708302331297718".into(), + "6034516841645751575815362526363553327142008775019507199148571280847313939846".into(), + "3103941851061425456578524836013350351780973108926423482048781831843642166611".into(), + "5899337062497322191697133509006899741055733585263214548357752956989422491317".into(), + "963781557317112122222004641921550021210279319264222273713987285679221730138".into(), + "12218612067667826360750451807845242059538814081271178077944945859470451709133".into(), + "19859087159769083790343803575983864397266266537354870798907672197319357964101".into(), + "19984410602577911276019354715470427122599274838994470055478829965861962241047".into(), + "1510088699001180548764185585999030426586790568209473093795384481408826277110".into(), + "6626530934788897049551186952232009297818099583639020036000874279734503731588".into(), + "11102385732315753875246033828526944990963999730005798544082045825035822583016".into(), + "6109966799003077547022556221879889867437672084424732190493586606121191686895".into(), + "11639465666095459063122039730377449150155515188121884615987948657973378713177".into(), + "5087119226269603720517452201720024842953062937909041658414079638720935374157".into(), + "14200979596305023031605438052846373683912540635435718213758112152603438068044".into(), + "10504614528473632996392119937661213820553616805799536317962705212982659337252".into(), + "16896136248461578519387122861681641687612480673542507251813953270071486348436".into(), + "19381803511891750827107737252264239897475475490344583112691529084617905175082".into(), + "17541407330179013144706644068775388402691308134663811736351559302382670708984".into(), + "15072973642203756087665519422382641458207399650896006837784917038035882610771".into(), + "2308847250281515017414251485377760361489324048295236605725148595795546323946".into(), + "12235376578863551481515043248404435233298457594750687771966106520323367004722".into(), + "234919702386651406701334351738513931283772159856053034152613436560637774413".into(), + "21637694258900941342752268794989465303386552780774074413553118735507317693719".into(), + "5017784917192017182377763628730148212610872485218540677293490321169201123948".into(), + "770314504054605850178900177028157853527192178178763005605945816624700149604".into(), + "15876814891128030074112970202145286137873841691080700504603122203177789175573".into(), + "21396093758862368540558980838454280107203592525118989209906020509719986520353".into(), + "12395505038782995420136281017345429633379095201006284425785781752730355509942".into(), + "4615582568807690433357570528319145785065741006112040062899881545600179737110".into(), + "8043772689074175830138340191606864001269151441770005593654367646112660385954".into(), + "5087060495541107066427394344208368671085864351635929517190622637786924485857".into(), + "16943670806889779731197043466545390303516518769717829999301840370920339407796".into(), + "13534112488771797914884729261168139500575711545907958816183168317604824618336".into(), + "16825197446319806977233945546803116869802796446301133858223973227155555550786".into(), + "415637162126135168159032709770391274308945193836031409779140830879628784155".into(), + "18387406758235678040900363524694503366739497074863830200933011303909731885356".into(), + "11179137042211084753827842203552443809484306307318317082624519139125401844623".into(), + "21558283039234113122805760097265638181763687038555764258857715920818106215523".into(), + "984886258318300688046776785673289560458722425831829175079944141959971496951".into(), + "15742677322520864401961943379313620949320295584851281667782848443614715444083".into(), + "4674404119733957791400958869998857022224647524150794463860851329191601384751".into(), + "8650727403827795422786989886519743572356271096745868930429292580249174286422".into(), + "13805664248894834838953946033892670782284990791974158822573815009987148935892".into(), + "16425836039758852245186956621689031634149287919733928973102442742483370034636".into(), + "12636984265967190523588079564146349074168180793368546716527682276529007569774".into(), + "6817261026014072560057398041777830163991050944713581458081292224492038152943".into(), + "3842591846766588833800550804121369117907947886204778461433916219752157723658".into(), + "37271592202861207450496704231110333036724082963271323982767766566562691532".into(), + "12329580473492550135339911865680237906360631219143722403158320079104416996484".into(), + "15059957190605121115530324002634619045808465757564252411076061916764353063726".into(), + "11049964099753534691591813062197785153610766318641253048434615460367998657091".into(), + "9547664187984679827610628603629867733891345475510185082362431432235944303887".into(), + "18137012162384412674879864487567137486356542869085283721914855801046173331814".into(), + "3685098687651674532509991642879131752314328195366675630043049707942891170584".into(), + "4350994188192128309646975341689829464702598370274816402210557114155641708487".into(), + "6257821888636845714306847817710237763041012260573089876542080703522920377274".into(), + "13253018959425522927774577647502632317700972755867567550168937147898302735178".into(), + "16128520328840705715994832968486859989808820923410546017533359121760378044520".into(), + "20170678152633918405966535017048153387865751946705242558406886703664743352398".into(), + "3126527212972403378701842254033672681067914451311377332339121677604578433846".into(), + "6867262232134903047018643171960682024396916574079628095990181177262447593546".into(), + "5158367668931323144529189475506903329651812153505452886267986731006143844385".into(), + "12754662355373887343223103245500264953264637968818954284520990187567577818296".into(), + "8184207275666143481387081482876989547621596168672026673494737134509515879012".into(), + "18182047594483210481213043235431614943690478827150601132558952379296218497290".into(), + "4111619186005075315994626077421177613466749589414703176717884282581714875444".into(), + "12594743600504910896656839099899310633582995354714016930508253088263104106919".into(), + "5906863249972407611468323572776947701049556100204019241509444413033282677489".into(), + "1980080210708070252092652836439362225685565242001269004929621280778029319218".into(), + "5317272650834519781155258400716302959016815255066834148961703371077671280690".into(), + "6755929511172704567967900822149236127789264783942356076752317952971695408598".into(), + "756653592358533438118443361825151465623048518178327516109550024774438883797".into(), + "13279387228067311574007940426672455425799739197632963751860641037826906383836".into(), + "3438061374000905227745499451129047661964429059615559246017344409320261233525".into(), + "7182178722730387940200728260680152279805701386434851756285719594136478079342".into(), + "3232238101879637288089008409532896629862152604458430213480076264229233742462".into(), + "18349073713456068979069801924492847615265826078935260207982474961526062084683".into(), + "14669461622305417414928378093274931180941168738663334898025953986981156308555".into(), + "21025953377099738424497295026970813014051167806216418684116056037559447904210".into(), + "5255626714680656571286256248936450375168592101301691959500945926924371301668".into(), + "17282802977887473307860913184523745285865757781571388182716814513918008763083".into(), + "7631811346302144382630250446520799917692672405169780636309223495193617534466".into(), + "20646156492067995141068212601070346054539051558428492870963989969028947901818".into(), + "11672162857509134152068238052735772663152403666766041363264045356497052787977".into(), + "11887096806532260110007600390059506485935624620297229185953255478078857108029".into(), + "21264877166054737412301473554803422700628127592263555002579170780541880711583".into(), + "17943121804479032447858025131006451946994591698842739992603829945304217132668".into(), + "21741868983463662771047103688885133076855201341036000119862339617136906339944".into(), + "1382607011220387511006322153920014038613273342046755883486188937618436917353".into(), + "15178143370568504520122235741397126257210602740650018201900729283546561696689".into(), + "19508797361068986472532319429763120919434246449052564653746851758275251767862".into(), + "15432472447501574952637182729640656917161000067956855273526749406835661136043".into(), + "3646314932117173966129769917469131878046956851972292537742853525040261895364".into(), + "9954239280150263233476434487622548184763162539776975088510280448547072413987".into(), + "2860206851873890237457963710655623849107446413141590367626845970849894602577".into(), + "7624884329556925103103971646513913063945080531848229875878001163429377892391".into(), + "13360943796731806773064110741074091445121635972973177327949717322801106153527".into(), + "11667836424651042501953164038576384835169183507720986280095771344954386965055".into(), + "11905856768069261907772799745883705412557691028383268959241049562161180720782".into(), + "12186322648958524087383458213507970038354441682650936942717890758775537576715".into(), + "6206377886698888214976033056004148608870783801657981445534961772175420895794".into(), + "9985786843211942401460306610504559890644549482837388550821870421722527522129".into(), + "7131871091468632512858311810874313071133138864883788963707243539301333044473".into(), + "6863753977467122978046292703800221264317404999280683794261458513920582107611".into(), + "21254007028585610870255249088819596192746568962023826736493046170403367390748".into(), + "19316740566828302369845437096994220205932229807972164794086423967490376053120".into(), + "20911390901671963410235703071566058542087894960862283015326592608742412435513".into(), + "10947283094344459246570251013404575174415295611564471102942451430110207789270".into(), + "6629514866202977686466275098623310374411361176607669574816969946802287210730".into(), + "4306637640746831336889157874035984930432037257905593217438853767758187627966".into(), + "17842812026941818985973284751293083233650807309923063938909281923223690155081".into(), + "20979787799837328650256681706033358558090809594843147876018416522952145532570".into(), + "1084395234163143750754914936597532036526030294466367212427999310133490427432".into(), + "16411000398229165199635990982959651490150157098065257264356189234661735382064".into(), + "19529434083761568271396183578711196610141016284281861529889071952129275189455".into(), + "16011589790708451889651677332030836112945970875991060135355201890489173794391".into(), + "18538965510307116327958097310749618403759736800399477577603041239573656036369".into(), + "575597941676167484590243546673541695594364334533047994047106268896055691965".into(), + "10154888807896331879146339491538875786991737788606023861857914666322797287434".into(), + "5751458975032524671409758081174846089867075271579936974413090425599016414105".into(), + "20537352507069134091732836326103935751238399876923560688100837864640768370820".into(), + "388393267636951339744357828936678047569934289925965154807382118853267759467".into(), + "7988929055663996471299857775851374803891818939607048669296465149104280329338".into(), + "15854279006908524023292733986043025770694447645646628114187722686173514956837".into(), + "4521251106518907970739816189092928174098228189505033700492425014567434676545".into(), + "8157530011801905607623371429913769514135441595931542848447134013030997901186".into(), + "17210220909122096186649472888037140871466614174061205406900504999283153448361".into(), + "8320732794514386532143557744115700882386234810376915665505736773284988176468".into(), + "18545875500168811775790253714678031231209184655369087385271022571392488988409".into(), + "1254781372250935292066700779746601577560528849791669968836092329802026798977".into(), + "15937651643466826506099692353730922248216886908526960483039790489122416783291".into(), + "19652922102341829730840072437940487297445121681514373922080785529489772301904".into(), + "10503757973376634172224417598657275807288376059467419568957007645015644121173".into(), + "21061128242218898797612547848348324784465891245956046422267179077655910293944".into(), + "10184102990558905165359578703213338644155257878926023646496853865036973561949".into(), + "8666994074867894594122739436332358585611317775525945925668413909058593367375".into(), + "6295777974744736354234904948599345763309384248069989076851179466032501025536".into(), + "20085880035576919555482594174603593248431976403050445801147514535176924689286".into(), + "13336325268431575611445366940808804882241012554243123490613520755178670444338".into(), + "2851570355537839451958385805591947257368741486463662868962800334058987150319".into(), + "15338540229139367100976188303076834527818508966890772601613851240301217961568".into(), + "10371461591548028790292642751110580057135152343433828303350260803216047518940".into(), + "4721084162843564631441880187545375946421625038671651491284453281351611614059".into(), + "3995968148066851603072141668595964652983129138292929197582737382184727007080".into(), + "7098475965261931921667649758515107427024701354618287208328785191069896296466".into(), + "4781012573672976409656547624587641549313089733225902702392506095387529662756".into(), + "20670752440000061451737406274881546918677944716345491651236710839944182745380".into(), + "12418063571138285842036790076814486174694060062447109012153384823149675212111".into(), + "7811816758498775303698763826175925041786332617584902402713486064657610670887".into(), + "12183449593956360154917192849776155473122126272381798975678273623416489913308".into(), + "16618893942934404572687811463808390124523121286530811532642072424179325845074".into(), + "19406638405969722482660214331214972298000686566522726077456272338753715959695".into(), + "10114896891602590148908922343663289384078633766105057337614271468528428689648".into(), + "12530180531394196901972330325311958484582015641092775027610123114239686805374".into(), + "11041683014697540482690315932778449428117122302594146671576370088141482310955".into(), + "1320027017053531867758788220662625751339386035447221928720928792758652779021".into(), + "13527787960362675982060947767740337401799422304150087254060335221227857899395".into(), + "13729174130954201937892395283664312766703232538508820386345658917013576288760".into(), + "17930242209772558948222336463641867165312462566025913611385142785331060524930".into(), + "271824668681816275326818116439977879809479607030794905583278884369947027319".into(), + "21830554400118502077887728598392792929421142621825963621109394022313090676569".into(), + "6549136256809735363529860847857612644334750418268339758975670928910634751983".into(), + "7685854228210998769567354902455601859064733599194469796921674884534065104568".into(), + "10199783250519025166562439623829544304874659364288345176426181874053651581987".into(), + "5943875072296398061174787119165901172801385563744445220331957391085927545285".into(), + "13636089974665970650510341064071559084615387545694695884611938887433039781695".into(), + "15691183714733597776429523227229000727289026713809002490626629956876436864306".into(), + "8559680887315082801378048339294171503207432568538176257628851229613536726966".into(), + "18821447832687547445675145256777518497433117070692725937727249438327468662992".into(), + "6902270693879104513901555672223877604634815519768564032457887408714154308149".into(), + "12608804561676097764962511762420917859780577008987930524021637882085147011561".into(), + "7174750477556160125005376833627217199860110410544187422988321767638233689286".into(), + "8781965602133662570225157578692327961234407570869748187793896100695809894931".into(), + "2495257954047729673944010345252038964921244093254482708244520480856454194795".into(), + "12653575930477027537985334989917636556832984698125835912664116078189272484883".into(), + "19470590483773048893847572401709923514063082100887826438398963400593333077944".into(), + "11678017778870321267772215799288011478356485408686518093768970488464471570815".into(), + "9301784115496434107640350077213193580405719087632828834385703690626291479546".into(), + "16099421215741601628246289398699744194733547679841291314995675269636375173366".into(), + "4867605962743281143953362108297759970476738545503236589613425112307423983682".into(), + "4859206625232481008184446590986037965722621081800881620559019195768715084241".into(), + "16443855645256733949926481287606183123363410685496841842812427755007713532892".into(), + "9353805991413463049534222956121131872491808501219007024034024595326412518624".into(), + "14757310122557726344690376086282772240920466622515316103858910465445116386412".into(), + "16114284659120061956470765697027372797086020387576070056735297826889023849274".into(), + "18127578981196629851526283391389653885301579188814495702848892722544020055239".into(), + "19887508599212070022669423370158309019634146887428772891372609912344825709907".into(), + "17077080577854056476313617926548386910187074435097123438936357029443634140037".into(), + "13226301328255541197539849574625779242079524439809722670248005223174137129917".into(), + "4960598616057280250789993806465640165902417481299684068727179165412382990160".into(), + "1556369617946500605910052041338863524761596681612901076288655800401759143728".into(), + "8895728155072279486251055328673751534060735340135019245331916382281698263288".into(), + "2289316141803713796617375788451106287296998047295030310234564594074457471782".into(), + "5743949466604344050156379807720583796608463915463812355649877415948657877907".into(), + "4559156913695113135073666675680811432586708455776982064075705161369076248306".into(), + "15885688417651643378493200422346595912547116850227590423936072108251702846077".into(), + "21139883658951849592430718453383511736537035608453172046329191674046781461328".into(), + "13189533415954149093943176775514655876533454488327933935436293535694805743518".into(), + "17844001788757965004299951292259913492254300676101367510089343929865197748634".into(), + "14359892033284780014175740986504899257072848573639227540808851637993574775227".into(), + "6889876198036781721490463051636227217181360455045088258864933221139868422058".into(), + "4553935378945711471651589030479778870996380701114996688086418055402491525866".into(), + "21810472237273957206786186837441232294905360772841197216403531161190353859134".into(), + "9053152935239668672905478856994179206897589996100634932367989982680043213185".into(), + "7903591118665286176079138425128951407213501136276774569998580497121699548452".into(), + "3172076359801796733031209099819054888498110723937314354878294115265190182330".into(), + "20407097360746745587552307442773353270033615342704398250066232913190814364080".into(), + "7972160291863108260254701271534596191348694866685225653124288422099219831356".into(), + "2512979638327596184144169725891221385374685508088897591186666227523755823449".into(), + "12288114406943148383409449156486826621388266177895423682705260002640229785204".into(), + "2564780208026103504658010005943517057775957652807715289383367743075323562762".into(), + "1819044074894043026843741029300477751989386054356309111356865205713103547767".into(), + "15212165170979376853314159201892286112393022743512259361106527770911103031833".into(), + "14546339564581115494538806968483958415473632518920533358183015375037245948160".into(), + "16595063968058690402196826920205083730698910363019110917303832709685137640608".into(), + "15141874678776355759677532828036793885858184118820760885539425739465598163481".into(), + "18904695587436094037528024957965408339655627593105121455062940021234808627517".into(), + "19691835186381553427610210595631576698057108693742599090126967830113381498354".into(), + "8924094782966746617987215970302687574282302360164926405994916025579749820763".into(), + "19703754039872652879247596753980834118500205932499483040527802680246732547089".into(), + "1222369520366776525212393255592594259431640497623436777492426726914294313696".into(), + "3172344047869282929514752164510333501174713324035401216632890990510873753170".into(), + "20343327187307675920101255252384731549311614062918830229591450503470004905005".into(), + "21822953127524319865522817898285572788301109254914611303940997016471607290917".into(), + "4119200650145180459943542547991468255860361677520024595215679941381226516262".into(), + "884147971363709313208000703986053560492302821976778288962675801741886201394".into(), + "14179192339259619376051698338290832703239802203346051877647263334899616811424".into(), + "6257894125748014033621520751032796530871364895580315426488126143829369988797".into(), + "17601756798422918594467038792255913560794153830062518652267418453154739852356".into(), + "2915639491551461190644833247357672612074490352906156209159755564104341441334".into(), + "3231102949049971084860869512445280179160209744399934744399854930563348405672".into(), + "13021414206859086129077179016730508855909534243359809624497206694614763740073".into(), + "19605385510680022189244399663099114503798708854480688481468264949015383978657".into(), + "17021346162472827600023529413773560471771827295900312703532075252918830714763".into(), + "797420759229039341104668808766016498233266139304135256309887013677831743527".into(), + "2664903618363391534009727112222679446988960947334573306133164042707563086517".into(), + "3385549618192271440433411525547058485954511711526716925876581944420956285378".into(), + "1363918352372003902477247159358100870270143261694504161661979865477181878601".into(), + "17725129391688775312434400553484246750421349715293800612810825705415551685950".into(), + "5565167518043825254697387383458520114240098359236753821784350172714709788630".into(), + "12992626143189731743865280272360549263209967124441767673953882659571766635538".into(), + "11778225061486552005541072536540594809710403539663018251279263069564781285228".into(), + "14855802626429861561613008610208237167206379805668816510881008293269978954622".into(), + "4625126357862179281586979391899300109486628847801168774082540966299465334987".into(), + "6276441096446918387723860737716070503136385106381280295004924708881687527278".into(), + "1795411699207968753627657631153296652726915050129212264930465577194539027180".into(), + "3713335398598470745490569816543380122933539387356971485299483223125101783622".into(), + "8168117481656071903083355331291793306622574934522412245021942135108094609781".into(), + "2572110912790333345146174163319527254651486901580412685997510630243717870169".into(), + "7025592394229454054105021012166569277499787182283497117950324822835039311263".into(), + "4277841334216388876550672363276475272389275617444687843049115977026804420380".into(), + "13307361877516070836117482544809649307840244674059174145753388549462990581425".into(), + "3541353704173386321602971434547681828071166901335602064587889348895191485268".into(), + "7930538155879758019336102161941128485659185437630747910453735785539584502699".into(), + "16450196550849346986145420593545138121140944184099182530791513972233505276989".into(), + "20317647049317277971067996601837330680914292615460278222929651175763284855258".into(), + "12350794170306113031197271161768360871903372891508108500893141893703359009096".into(), + "3619108225491731892480788907918892224372271737626523316019551923666189881560".into(), + "7777524276793695474644184635794903798228263960698425657257115554422141857963".into(), + "13186962355302544705310483262369096768297692231774754772442065918802639434148".into(), + "3194922763633786447595049582526939577788731446948460263029416154575517633713".into(), + "21764951733983707370163229447076318279945301269175491715317016754083795809766".into(), + "16974123712053830964644120202072593533882217585039161631414357628477067270637".into(), + "7898037946204257594215947941255236785530939710112474014338432350690918260891".into(), + "13624131700578417700359325726622999876995826287578399131977467236788609909029".into(), + "711696429159123395134741628515974061981015372797792603239801873522576934606".into(), + "4454694656802154477610657384658122890655367951456133773797632953453271645583".into(), + "11258512209244601689743240316900203988033840864814099786180897714348264963892".into(), + "17104789493255338246571119977680210285205025930099647162805883642403493155704".into(), + "2453935444829235217884190831569908545427042660161097975703593935054598308172".into(), + "14865321440470518031331143407868649683664573698762400527582797926467679386433".into(), + "2136979677653477082590579842429661891149048368379361009535231454875783700576".into(), + "4168503404348819959713764969732410335826536398112246387116198899646877476352".into(), + "14962916637001251079646555121263487095246900845890500033998976380566908488027".into(), + "17784806976263484298477702017969482877962979780736193065620735094916915015057".into(), + "5811232683701857296108715612406368550641241305997590112572736833619623657335".into(), + "1137391750177492513773516911880446733365510948924761786959001414998578780190".into(), + "10377533661461362184186134231677823404558478220398261395966741229154302153880".into(), + "18289640374707184240721457180013244890344288557498104400868875166742835064845".into(), + "9860550380532355283228261241815983394260110595913388936133793178666677861361".into(), + "4874241362571018447675014656968369345960496639223360836208429603461250621707".into(), + "16865415500549523957415217905648921092860879005547743663493860402980518775170".into(), + "18108918097362824701832555936428153547583210042204293837516231255476913995307".into(), + "5814882735302537469013996470647050035634690283351899637246825700230993102653".into(), + "5547108529079575186420571231950116618134926722630598104799502651040507258925".into(), + "15206849009490203249915056590647669409740170384956300636367240071539934234123".into(), + "1640644832819090575801198338202018591671247888384450305015978486631872758131".into(), + "7653538136953954504326423293731449069773903668694073045541918594328126765475".into(), + "15742666466329712178554069131786945235650635594806638864788722339397338233904".into(), + "11117991054142148551955400250530241420008368948211075786946321189997485480110".into(), + "17233487593470788370048183367208371204821743424889641470669677410605695176177".into(), + "1026146833779113517033720464553035587620506307190537032842523110750653300409".into(), + "10367733177218458884804949015952210383318098385156812709591442491050289560766".into(), + "19795952047370883108661392932328291359892331001341014799939338429402797554384".into(), + "280679929395977649114582242142346138466205172264485418126097892277002431011".into(), + "11242092785741474644852794535567939603118987148698147008638503870866024069513".into(), + "5042585848515676287159684547630780418802525904631814404423696800729724702441".into(), + "18076295265515176256373769387621576799326851284582393083567912446445089657195".into(), + "7379637951216509909219158222610656534687563904425133806818053082443647546922".into(), + "3558112975941340025714332950698945379582897448619066846171473836063116876320".into(), + "9573479360636272045463430387538379484130343682411115582933078720270296582700".into(), + "15319039730033276973173680817870390182683166144884901717164880903880948070156".into(), + "12614337451840522588925552004159363860236857822744920028925848657320885223697".into(), + "11264872143423037953370674414336573063122101340016831919048747444897009600345".into(), + "63551039484071672399844495086043200468124594386399597718753671627443264469".into(), + "16482682562437246084957175194240509231216838776265462897558195414149416246757".into(), + "13173166853778783824789219649923900522843999506901247221151336885046420684780".into(), + "1129677032059045391430942335190974518204360360119509444502890845544005628775".into(), + "20501934327919041700769005911882447290880962332107224642437572131655999551570".into(), + "8568499318929533224876840771639984749494220751421895884374508523238084517848".into(), + "21259500369117112858046793730986192593905655365067722159200063822227414197523".into(), + "18511737784245858621248028192284641888665848600116211193811323135518967443130".into(), + "18603594203971848110309624617834986072777858306337940642153570206399896731227".into(), + "10817810567854821052648176407774626914880746517118466269332776843576340211709".into(), + "11799868341887426863553231699803195039814149532556595741528976577115279703638".into(), + "3757945008672794110380903983621818494776340526769633536295923729698796873418".into(), + "21147309772231799704776665377393022398575359474581992532654566514687692622381".into(), + "10106161080097685505341186856462439849936096425798398803226308955853233801088".into(), + "14262263816750661321610690116851316864336319623922717953366014185440110799541".into(), + "15141933436196205178645622716666344645155868155385407285733185900575537844179".into(), + "2528094772114073897461542081006497820213677180486542945691028610950494870091".into(), + "5348148742466995347556247741747876445951253226718284728036059196493410775291".into(), + "9450763449774399387985728640759709076413273047954552646207186077905960887466".into(), + "4298191617478880606290362740003112985908172107645583722035275913358222519724".into(), + "17030410720819419348097350980058866817562528502379090451415524440163140111041".into(), + "20216724013881673832814480585345314476760112405406761404693215284600396014822".into(), + "3030458477787500523997196317647453871345579139634715247165773460115609582330".into(), + "4498462169050723804654682081938858063245557243640516301148855502903911071559".into(), + "21824371776439248570562107317467644300343374167209593758174011062336014366712".into(), + "203060629973198497909844961226448148781283822023116446785885526153889141133".into(), + "13138719392503582101372758211337939905779727766832919837180755391232674501632".into(), + "20854865022095468190800800251894006373048202378274232848293926367900444699099".into(), + "18588694216199791340977948229034824338778108765317256872571104367383806683819".into(), + "2107723674718153039822163382990954569558924075626979445723435572951885590282".into(), + "17271751087973139804430243764301653129405836972234862722312699991367221547648".into(), + "6601353498913454241063626951167220829716071681800808284181512008975791453478".into(), + "12008446685921630692928691914657961114819560167122558958661337441533504934249".into(), + "2546793791363939452366278657165405700949349144309147725354170056455546924821".into(), + "7509639228703028871971491982924543490428017835786144030628769546327315651842".into(), + "7823420836979374535855612407590511724416995784427817183396908887631615569374".into(), + "14440328435891567217327191606342183091806515070572157964854472183381142306857".into(), + "13108790694241576486381404587677845524627865147145072471473988738194261674445".into(), + "19983383938241398968439231728950534655853676209193309061102635732967764422018".into(), + "11284194381233971884183176274626185331731276627680969047259362251065411330490".into(), + "1897088360814614813239622717000987185543144416227464749796311263447835372290".into(), + "5857538086726776983838909617047765193294143035705320425608254315461849915283".into(), + "4535962484854275896801499258204616949002112470323390523101255172552516276853".into(), + "11469688959565470671946358524828946654780293319597759366277869041848383938506".into(), + "20012499472065515722935879215306365340460493343044867272369823908813586319215".into(), + "7237913775106599492643101364689422933623029044428357142837972864389129948204".into(), + "4449045505867908852965142075471111990333999619831208332535528566178225482875".into(), + "18979580728127588847832634342765381189070401746541099544183581604411319882119".into(), + "19208895793797113211677254952138771238229741398313608749033216609457932026240".into(), + "5693339887541566707818870620263679026120058867908674300592248504295030161683".into(), + "5766336473322649734565794453161873006745293312886382597938223884039108263098".into(), + "10170500372500319676592379791726967782477062446940064561124188934689788907641".into(), + "10715178415744481025577257316838846623444784908902638764026977772991007652177".into(), + "9619234464729169347469055561718306819745671419829947117176160764784203967441".into(), + "1725396226859654685724455004555068750501182150139117204170589152838896682582".into(), + "12812488682187532465982035305360477706619015575668642434536603051988822585780".into(), + "11634913038840687277143479351580381035995147077852960651195662158592298943782".into(), + "590921624537670966145684975359593307845422616174883518035926979611039297697".into(), + "6994357700776589034911446516222866144704328090940819982199063011389928815878".into(), + "3578735612848960352693685408184960756564585180098599373883079061082850989193".into(), + "21635405933646037549623279995086014983969914199701848464885184930399308788578".into(), + "12427020649229929152974948764411072254994310665316587507041988661972823471907".into(), + "5659276116329803541059701770846868827421187991242679567541795719989019960482".into(), + "14409569205409757172710837837486699395207656272569912916147975142886221411048".into(), + "20174498258690846561506592709929532887311794985051466436061305284714705004116".into(), + "16144749571419779339987554753326186342500618892721832987727063682036648217899".into(), + "5806868382318732909919594488299463205875390466419224655308941273678069453955".into(), + "10943981499006299439884803446608501609107021429051016441936415463311572940454".into(), + "1924938839701254827227113595273674646757106690922839787536830436278569286096".into(), + "13979276902289154914577571664895855335838520340557113744304695510562836235170".into(), + "6432081587087260245919599999151100148827877257649626745290270317469081484560".into(), + "5063153002404552050282559105432952932908381495673902142745313078991883601735".into(), + "10740439837861405332126185761433228439796897508030224612484767505999462329793".into(), + "12737167379130609027705740172196108812439282784347836729589408028420120550933".into(), + "9849845512381592257963507051167559787679646439786540183276617129759373853388".into(), + "16005888832870175950940829700448047001850581165998249926419521156267014986701".into(), + "14353647838770412975928759605442634240665383526900174470441397150591603642336".into(), + "9246400563934815769990941385821129793696094365581285133233487333862515377594".into(), + "15148965960125250145435338272790355289745702742252704183388935133186551786499".into(), + "16453015856957375440870560801879892220374643048047546023962049108638908959772".into(), + "19594591292469468407344408128396176069613863801953800169935974455228391704047".into(), + "4786494574332708919915792733222077450535137127349578261762663721149074161698".into(), + "11960730258074073655703739602687794653921577208052039888289111813676118677358".into(), + "9848013027466807250590439150605527564079804116846510943688041755058752584380".into(), + "12899034273953092771018315472337883592076632199623658827592500124277160375343".into(), + "7444799755957099892747756300128576168091670485463138298855357993349282405924".into(), + "12078602067619785815908569606330383217651816461769668980367782310218465279082".into(), + "13207091244585343533434026539671489949734093160191356902826819065281696899279".into(), + "15052325250252549646358568907141836154046676521102037138442805194847473405285".into(), + "2221581370746975227443137026410298234625504939466881398515488592571828181681".into(), + "20243663867674839549278772384366563405687275170339456055149452630358919741102".into(), + "9989690042997457961300135390803031791688603090074435089369924996763771804683".into(), + "2394175383139107454721858241476296570939299158552117381451586030928104650195".into(), + "14935488700038770608988697394320688627203362240444723637257364275046157727507".into(), + "12264662514829291661352518974189445073203160920355111833291657311513159477694".into(), + "3827639321249325764386581723986872585672536887010319153929792264348424272953".into(), + "13487908627465024716967075008462681337662334712641104635432980395748705243220".into(), + "2271300450003775887579228062020983806396606091928055215698274988642377017880".into(), + "9536464905154879502929074042077088011040150301676217212058440880221802373843".into(), + "11224187363394886568032176392622806663752975492652881881608898287407285044670".into(), + "16348011463839183311989740909923102224397330137023072134870626410011917574109".into(), + "658746784212845073728972419644775580738462236799285816112006815020578863053".into(), + "10913847351958014923157235635013414182776322060119409210360083568773647202922".into(), + "5174660898410477334415120015192903322111803813558785977142572971088703464181".into(), + "13435625677277740540867428279095038525759637068783477185816475988654675258528".into(), + "16042236961096516425577672868221946711334425239903980281947225696871186016946".into(), + "5966732498522491800452631630164751678119619125003570554878083612603498334850".into(), + "19244421524004466175382168721538493986709062021717633087544245374056859990844".into(), + "8953835349510092921386508744116096000868233729683707705475555286120350297772".into(), + "19400107730527891751069598685513048198266948717144299472180340676627124394816".into(), + "1788315965311520067508595535605409766732846405132796308898593684888230118195".into(), + "4155306741635339863500551353079678816173576578036039224848730508326156410064".into(), + "314333752297483020587573946457568012126281776266668278461087690408622762890".into(), + "5814056308990522321998868808930979798909264311720790843253044589964499450995".into(), + "8139572664280730177398240130505806903517588262678422575082094265546418213115".into(), + "5891945033023051894996849367995027817716659752927558196235762187145542665778".into(), + "15816119508706283096011330636590931848201515685282376441014894443298875354927".into(), + "19765585883129509218113831432898646175337020355613337867993568030797990428267".into(), + "7571142440759701899321724284276618755088451471808734385481644938761653631281".into(), + "21372154727662886563674173359368281518527052830463392775221052936073944196548".into(), + "7081252182490962865977081179238366944226264513969472284114001831030270956045".into(), + "5223967788855971504613125877205268444815856778995847146009869071113923642838".into(), + "7616031404430100022715089975212311635761004286094499406826744277117238450802".into(), + "2964922410519663416704982602737862878081135021433460716313695716253990013571".into(), + "21452022130413843576926752262576843465698254258090353961686699256674273446998".into(), + "5615506565720477707126907662373732333382367798591588442255590186949548315516".into(), + "13758529852543878424689721858680184284389257236387722027067085472062441696813".into(), + "12270911407050929770266474860904788952698196744336477194920983584641315683723".into(), + "2263765921105974400584982144019036516750696376273595921030243162454245412438".into(), + "3726743207790008961875115608784686494039367610419333613620227882409630379219".into(), + "12237143845448219572451550465877077742264329541226110387544727976984528388742".into(), + "2409964261853228751087973651622025781333933822306168735290950339250464395333".into(), + "10085433407143766997440158468408095154887290417940533218330090781308733416929".into(), + "21228510717137782368419571997756416328932321564657530623869061270948157027291".into(), + "12056471592273332515153939599576024781202771932636795778229482480420755693571".into(), + "18213366556788088673353762707270651486693693110301565439057413547158475404035".into(), + "16502444929714671396753943873426599559613041594477504659253173973027014569781".into(), + "15099656057998038622782214961934296168421711158711759920714280998215172598701".into(), + "20662687648270300297638887955756931826210498273982273406811895752232434295545".into(), + "11319823310823640550774639562916849455005127799420683680222546170719181512525".into(), + "343967214752520057864245186069476394037815047607811439097329864410341714737".into(), + "17503305564468603693734885462989691651193248463782636796950758256530838277067".into(), + "12741605036457271400687978857670758064474033476970216981099556950125969853613".into(), + "20739683061558313403494347679816765721522415874620086168947803143944678465016".into(), + "7946584883804172227025447539224245968128890222556746649628342784345139347190".into(), + "8360187202053456670989734732945943173170474770371603015342370151451116337951".into(), + "20059356712109588518115933733238896878411826141623495434229710888297572857371".into(), + "8908929255369499016159063161703140212756115979036668550222341666828086389886".into(), + "17260316215848220480068274186000229981635360232751196604952216075987952224195".into(), + "16051483806631795891066047812303438046492064609198644392699337542232076970683".into(), + "13616797129917979734082711158569544985901822934952385632710806021914842733604".into(), + "7121878885265063724099916056725320671644022545379534508159519935129387437912".into(), + "5095784041111849236518098036393508072376638346067943869740820917637881991288".into(), + "15064740101319510049989912367011238454044423307266600930402588789041306100213".into(), + "12407265211081220177754043474825255990635653794940031741483370307852381609406".into(), + "20223426484470923566833281385864037127075416246760538371032215131656571722149".into(), + "3668052915108225265616930005001596789890471435407321351170819088999432868806".into(), + "11366073886283256473381607209905269811061951337531274662130606966429881340996".into(), + "12582247379355857288799702955077607346019480970920005735329811307693949234935".into(), + "20186938619235157734865577521404397515633405422805778974853873925656656294650".into(), + "16122019959033821849412382360062366364664833933984337294691025029563985515365".into(), + "254648902211450148074737989547563835748091091484232032343713819397130138530".into(), + "12172833228273710344765296926585753189586602769025493553315361303084014018635".into(), + "19945409419975886341220734130071717017479702410716315294907303154809664173135".into(), + "20712004789058289094102947689666173023011192678139675809444116843756511650735".into(), + "21105077263104009643989508074340105423304108979860049556255519287288188396634".into(), + "3538342015671194745411221642090452794460666099501316339752121561502241774127".into(), + "16008085602703591565147288064897579335322875565262127586367715180903703479852".into(), + "21422197449727241817827580893411034314307602976542729347805156844487333432241".into(), + "18799547353759539084890273894754702756999055145524208124647385295339036997359".into(), + "6376647829380850860365386439681035038162788714588766405462355210239582784337".into(), + "15847703763893026451216792359491847622491103863033823028942752373064363683711".into(), + "21753518314824757010443592444509359716445873322887601064788806456493419038035".into(), + "8018616956493930393966420225569393035642655431019891986498475494315186954398".into(), + "11747316959744110931059298678334515105505609504722662598929370282308351440074".into(), + "17893257331206244838570586905501874457956496735362457086282197258342214145307".into(), + "15961328802051806619901962582575367866289226289028087772591680471434203677570".into(), + "3236169926687880505076444874746358996739587258202752103445199991661738621601".into(), + "187795190351727293099714304116924667408668969503996281039583636956966649638".into(), + "239392318624502435385014551926381415338676314268675338353695671324466753801".into(), + "18674319341781941734600473869568608233805776945980535084119278717005726916471".into(), + "2771345443147472606857365655976656180452504182686204076714773790025740827296".into(), + "19450913042313320002652706987083488807755986380163327474873550819278969177372".into(), + "17088122609555961904069874590098938791545854926952758736324719542173158272235".into(), + "4614324608780439388145100049836115973615823562529414178724078049067318464093".into(), + "480346578559945500918475024651677711916332481019644166207062710283884336501".into(), + "3976896258432286534710425248577923525874780501683135711905396303214796569813".into(), + "19747713021879142821191045416534012039092734284665942138998895512844748934520".into(), + "18114341948852128544591474427791645861453096798290403362024577582905309063343".into(), + "16389740922636415377645046847550552757683160597807953014971200028725848855677".into(), + "14347956677754622309732029695580580261895229653511993172444407254733241454730".into(), + "7001790022045736128714608604018732713668075078108360908557371520612055210303".into(), + "19272993806385013542500911534969336370461053778032527920746797814743961064937".into(), + "20666992739220376856615766377753030050985727426420972006419425641580077380709".into(), + "18907720165776367637548948232402401955322000052328805042740522159109944714138".into(), + "9414327783699575006459158047005978347088459439117502202158387157394567126641".into(), + "1092342932834424845294743018087259301897007735031966382725140810717192908054".into(), + "3953337326499755715834998572085254989073834902724154050737740470058759118498".into(), + "11743581671122680453195477167432558941219274023697487318378655108983631999472".into(), + "14292519320039108746758578387893135567048390166844266988895150640926606231575".into(), + "3463895766707893138928153619402823425980920627031760477180165727285868840115".into(), + "17329024693549080975670914737054013479227561926835421032215803061920086593805".into(), + "17069029917639740020306921281456148921331852897276386771126788265397483239836".into(), + "17757973687563632162101087414879272203562112761746273122681647748208587693311".into(), + "4657037101807059847480820087972588696422739806756490960821623277361176723008".into(), + "10031688077390763868641078069309072895683953752120759377432369407780311740458".into(), + "13372959422785941027234359900894463458148714364727687916236363488102034358213".into(), + "21074689968655856473305620806477766182958744184789493409758269023421704332636".into(), + "19863757339575916461434905195916835719746715041076078424686010055572742030617".into(), + "20973060471220514134365115793881078818914475917880454352814198678201263612788".into(), + "20786322802664930407040804724442246307149058272537433665345242730163006184858".into(), + "13503324543441706240017837835235888103455201530371938269103987765298019164241".into(), + "21516545371414182695629550949358554432241582789246093981854557932438611855051".into(), + "4189468076924692882857105215398697213714749255613940842113064700527656187139".into(), + "6356916718613787191898160559615069566270314091650855868585425220261360364619".into(), + "5420879043826588690498755027108627480358930425000012254678542105050717051191".into(), + "20559290756493460657385989162478253178529811977588548661414171937311774738356".into(), + "7472237964286313816282734103247675024752524054147218881654824853569171740182".into(), + "5587656354563280475935968622322500135733558246449238814064790207486002634312".into(), + "10477445403396666334274248654801801931117602962284736619479124362799824273546".into(), + "6964581565574090633894249931196779138063582556737680018364077690424381812379".into(), + "14818621060919997284977287223747030674994127751231587714382341838934955054844".into(), + "1561884120897562502024552266455149126373072574485245753929487036177604271779".into(), + "15924114719216774839217247278498830109576711552882853826759218581616445364187".into(), + "4632899269214938328686734061038562662617634605056083586217293196683107383226".into(), + "414275447940334199421265067114189311498845927325050140106659496782723706629".into(), + "5919841635132642392689439793932385370460582843320887853171556097049171723480".into(), + "18213825499960993231734509662843099396992046367870190109129172304011496568015".into(), + "19071949566664135927732333412944575018103341781608323568493744120181505633727".into(), + "8055738556659889630969832748361502052460816679489752687851823555653740856922".into(), + "18552532582629910542384644188750599556440125083583835015670173118025699825513".into(), + "2484657787081400262201491424080522833403834349414361745080712757081157295905".into(), + "16983418852456608800545535696543676988540233741050842886180695807863040864765".into(), + "4238493546779457743228884918170815223308340385056120983013226844661472349292".into(), + "3630132677030524902934720712211822367613329466140887879132884750998038269473".into(), + "21054939309368014542663397737839771438999660373085660928983975055849174262524".into(), + "7764728564558131555197022370274162477498111431299751237388301945333560774986".into(), + "3593305607441900907267751550709584072658973999555174092879673804077111387503".into(), + "932359123290353753331737736031582956463161520044366983625790109891741693526".into(), + "4731952008769040870131753442142911445224559732127188801247458162044777478911".into(), + "1306090977867005427161468095288389390649620350907397989639122170049060845154".into(), + "7759081335204598570892984361955322222987825766180300022358547686895179745422".into(), + "6447993445251075592811527795404536687284747599271434406060192746881971850885".into(), + "8266912047004162765439491557705745929139857757706428454484671294796825044391".into(), + "1314461175505921794571050454800604608649221187723852105188152934159567388084".into(), + "5074821468464653205102869421665358795805465509804176663850666382476895922210".into(), + "587081217806702661903500809062925367993834792468594461022841500167739579474".into(), + "5264606314717935678144297576598053656417231843504500779254542039497862460981".into(), + "1236480134248818593225404950925034920588295884010411301784531216110810413323".into(), + "13089489778236095768305861425107967360517148121020346060932975978601131790499".into(), + "17291786821946523371971061686693758035121148041300868933078085040228288669056".into(), + "18124176342257558638062889896829524322479803409217207498687844328613679520644".into(), + "9905839015283285144616603221521153361618378103985458187286841556656656429741".into(), + "11809535677454857598311615002421831170743476213662971184893106000202355262469".into(), + "4105258996829645758179809917480600473288889890938687299484408634005613929027".into(), + "7249994723657380623014328438319457303659016324967581867420756005957067258456".into(), + "8317023308476962783876206275099298475926201657427120954368652120829272222159".into(), + "2011843005377857984465704450777581623363347938947142463422784260490016214095".into(), + "6478487501062645568595261797056495837204909062128557523543545066559983302854".into(), + "11332891765024246297067848612322823673574497640669814572890785705324624377152".into(), + "11310396674827683007586339726668223579107272179969446205517750064367092175748".into(), + "11099104200525710724155767594487158698174345291601467682384866473391802992487".into(), + "9363361473795780293736671173763300702046469200956940247194666073284271734887".into(), + "15151887186592767507545683753579108698447339021705506097120199743211891524973".into(), + "19881265730819081878858384565138117903272592569639633426872725304622695029688".into(), + "3011117599368717267651471428189462533336275598139751114483084233022831296797".into(), + "17520576180525374756578417994075832591962992432186234756248866458711765589626".into(), + "6155276852486738773749976144225301808688416984253808800735523464178476071704".into(), + "8631372484444450448190767419844298467817852912545286035361602469665228507964".into(), + "12205507917633391792796810613135648610406161967483463808645931187075958213927".into(), + "18222453239521787308656394705559544583576244681888667325435264246411012699229".into(), + "9033412577366915161372037999993563962295812571996632039862860993305238914757".into(), + "19454897503553988644403623432258520403053811767663131188695520064462224816016".into(), + "19817199370883464197796219716426529872878349643708961034403389904486407134637".into(), + "21290720498988312226623568214384902017696056682729297309424737295532449054922".into(), + "5006872489582777310304924776769064242968147807589802114955067857036564413999".into(), + "16420397542367588392786684585727906891797023280517827697299749514608736021404".into(), + "1300345640934139074405647472243803688216465663235066735522675025627248393466".into(), + "15474333493788166084607018352361187159147026284793436327548632778313351493399".into(), + "5141280817333171025837036045163967613491344199994520722703718097693724060240".into(), + "1674845241197502896336080529694883486542569529162942594482168089081833468174".into(), + "20749587232360151357799983517636847191215950040853109011833662933435613635469".into(), + "21065338667581575565163875505871743203827212418043845343534420193848874866739".into(), + "7051564661861006096495144384640210161011647325580302475305699820188085915761".into(), + "1121065721208670659965039607998179107433568481023836429952089959263844401629".into(), + "2114003185799450303041425285829091463889299614202766352631616657722513595953".into(), + "3334382524507526203206024024777692800878664806770692329489928846721311223692".into(), + "5349561335050661854855918228516028812586933810356904077294751460675531266141".into(), + "5703041280392778021418588942391165300974701557007394123618062308100870196445".into(), + "13673663050707034371520553032876769213055293448602317144467901970892454938354".into(), + "5483873888673148302254721546416819014852587382013956934541598037319090821435".into(), + "19318734450985860065269203041456593881321456849029001758521815811241052123018".into(), + "12851330354839827372437628165464795441114428848058858241484775351172304561256".into(), + "10041542640844175943995712334828870579034783871076347036534323366252583734600".into(), + "3589445341963638025665117050103495399260947312205835568650192524332423143081".into(), + "6240962307810325966231999724200767949498419326824465195783823947519631917688".into(), + ], + vec![ + "21845584790817078371458083471368949437776490472877850604640045078191512294989".into(), + "19653529167194186573342031346879012675435131167180408423801998487681049609228".into(), + "2161640783454164110262374377277313793192503897274785966059544028153063342839".into(), + "3054385704838408049711788708109646820127990212588286684954516786776077717445".into(), + "16635301713639076283966918721405743045341739008997361549904617279107633739991".into(), + "16225725689449395070421553385264743934675104858051374243666267106604469686859".into(), + "13459300344588917210133884568970491767131781128923804903594902942358152425193".into(), + "18076808066595657160589765822638228194586496301618971676220813553508533309824".into(), + "15694780501519741286439086116053845024521602672078716577385749258673935540935".into(), + "16872540725652861460604107748085417998883467929955001131816309200004825208678".into(), + "18265717256656085201140362928095712147159090935193139243454994794524622317396".into(), + "19947609676398163035598483882491861002323351300468831661031717096486749401185".into(), + "7065348755377637300800426777670517297442798705186719592553630280930183590981".into(), + "5932961695686545421777788171001317503890223201383043783424823453778021729657".into(), + "2576876576710667081195577161905871971928185892200731156106299121736713352612".into(), + "12555802030075275558510564984903031995058560722557285963258561359487710166944".into(), + "6675427211912119210966904017515062933064977336124153512189140864737663166184".into(), + "12222599141329943348111473622063042423140762381594560767061296463113725630598".into(), + "10362975561623981137760844569177506781739504467939943515494131756563631401384".into(), + "12107531632347826132438108083855485290921771214734424618157420540758495466114".into(), + "3236413362865617508850894632024597913345632839215458268170009274560894755059".into(), + "13269640842689962370480760671727240164372687082286848502921603339956981268889".into(), + "8634391395282326489812410144806386699066840792624899435283359752045476380023".into(), + "14222253145252965302168372301361324351891663598874510374220732501857922860417".into(), + "8638897321585802770244834010345336579513350450350318876059595307827518698264".into(), + "3865974821285567306249300995778666703191077355251395536574998983854299535502".into(), + "7110975263857442529682465175399830853738223819847928136560110618272170902664".into(), + "20747849667914457283564964533975989346418433637631755372433299771504842519646".into(), + "8525337218586373689263383636693181730826089466845186214607593322372086164902".into(), + "15284698092379128147447314807096025894174546974949762301780223877793581148749".into(), + "7697267363768037767923915437621109821518467158489394234232803683407232938777".into(), + "18552464418403713636184903318257896949762751716606049583572368219578636617127".into(), + "8197992130246671682417102475937137101180310399693221498504232752936385287840".into(), + "6267215711691005252092821645736755217101102985763928035625443919819600833817".into(), + "17433952956528062441211440388577119958754746388457390005635829394680376947181".into(), + "4412646415700345908595327060988016573431087490581498127967913815901104779675".into(), + "15833191543444816612091389727640172975373200401937212078659643034429446372420".into(), + "2496738022051529758808536908421531340554055955151436808600608028881270179985".into(), + "18865821309368781007158439319264225124435320863391412688953283839208730853556".into(), + "17356853444859379852104523062654303386446700475095103588258687890090805160214".into(), + "13809314487709569674040696128771324530575306163394456992422515709961741509179".into(), + "16441057642509466688507318755613056920894191930223829210993196564779122458374".into(), + "13126203992288988176378067141418363395032818736472538500506274256104409609416".into(), + "13787967697762688988712839398243982920760030565931489906532854488857802399817".into(), + "19656786571615920765259702874045834039087950126970888730192726308970074475638".into(), + "9496155930249199891836650339371004045437843955429376625371983436016490755144".into(), + "6133650980214690439646563353939486183333374330790371938934927663849630407389".into(), + "9878750392407472855744714788091164958447986838940220983459727015586370325700".into(), + "17172636845539329981401043696407061375416986816086343858560846660524543673690".into(), + "18266068074968659293646594793202575012585251834459743250538629833034512472364".into(), + "88170009072279647351671114053985696295758726185186407584170034409102883821".into(), + "7161623553009701438320582200845261728475099875569530553253147102879319777702".into(), + "19053465421328592764143131116469165876378776722378567877265112443326967288533".into(), + "15787821054928042708896412640384007326676625326396861263135967977164294783653".into(), + "9615173323505112477217305395762776033870692647990635021315350642536385774258".into(), + "10748585844981412663149047472971001536312431184916168605042111548221680655573".into(), + "17193496554920138070905215774356694517155445370515924892481894322048517125340".into(), + "20981027561373479728773182418686452188471940655764396684028110979140369006827".into(), + "21262972686125563821794309741130556914165641373895499794098515843451753652777".into(), + "17107658081493676403630365275304097003600155220274082934263833389505162130032".into(), + "12511894931761241561787052333055670047472612011302194769826693935710455626001".into(), + "17806912269440585736864672280987816575618315620498245554921738316327046304192".into(), + "19545292022072695979212345737390709562681030627834519269858193519314537426533".into(), + "7543800958185166098093959246645025070235714806654195385000743554473922936618".into(), + "4625475082534149232161405731224600013881237737740742465433231001161153655617".into(), + "12763413547514960206069967971684559703789391875445961922711624474020658766608".into(), + "16881857439631059221437594346198587121133192812269921863116602807689099485795".into(), + "3231850141984038920375478271531925025564375094627983241106994584163478821676".into(), + "12025284448956248150398233143375971306100458475218939512492496426861431663478".into(), + "4525425011555084029555870990647344303529266577387030581756537915893006804174".into(), + "3795501990414375556011654275060518399449630146003978757203023617760680325709".into(), + "4509128642188781738213513757207125341975604363824265606323571895455672910764".into(), + "12203047758734592203615462366516348191118610129553686354997732038970187121007".into(), + "13638687018405787761639420673973367506339916347486774445588648879776248875396".into(), + "1480604348496719441038523415355764543336234934845679094037807499653490493865".into(), + "10692361717579001228187212442602435054487474561573499974105521910665132275515".into(), + "5007773285184019093586454921139763432640822738583548849737728483623941935472".into(), + "14779176361271780821074929551122175132746249520990325894754727749064980218318".into(), + "8810869743928256631039877155210471039332035226261305285283609913565228857800".into(), + "18154173501970140404154140565370264376529820426626464552253141823874364956900".into(), + "11656303509442573507998921894257944851336988233800378817636452696829680890350".into(), + "17658875741559155615021995850723408238216088494443352771306394733637591342379".into(), + "18902160612986281748775835015982844003483334222891067179969353562662106908838".into(), + "21595310045201809412620701470963174488544609749637202383626402540041734306075".into(), + "19479000691333638193560008307308022198954460138615126342769742031420251871629".into(), + "2972632723219534905306782557604159867920608086253824361540409985645334537267".into(), + "3847529854347283694984727631990010016411937091308711649053729266492176961919".into(), + "2403423343103741792664345548178046472942690163193710191494177026621927723840".into(), + "4715026270007766325068951713831542483223564562870257986931331836552507312912".into(), + "10050946066792704139911540686518561899625455529845988837175998569612056291645".into(), + "8462612443675205626907647847435181314299724037672159352834910195653278990554".into(), + "21536131862603746518459731430650379927614037990254564979711500405913555717860".into(), + "7888269140704212528047592848167567446827917489655456773861487833453406017069".into(), + "1170377169572693438349285053961454675716678619265656860173330864613871015536".into(), + "19621259602533287127007734174330694952187330430513568066156633937834027643682".into(), + "17165866032840109673438683878903959007172818091757219456373859826178202458170".into(), + "14933333727688891927411002006378277183547380244506194877231532901665762194033".into(), + "9343867619319423527260818064767777717418825720619434299636538864535443951928".into(), + "20583768764298760141290708372295636534035063275069820261065362331153347542719".into(), + "2577125135040205550039517386386446603255535410845714513971957864613256569227".into(), + "17000415305135911202592785659979381300200707282766529883690542305214226820231".into(), + "12511346220893686853445962138128902612419766727072608028194026193211924059518".into(), + "4149764657828794758957335209849218835702975200988138041342768571619211624011".into(), + "12689460200478357229673533402655824055278729189265502524126586766684281271735".into(), + "13400421547966647260602415862775725767930791927979420926402318903131836007467".into(), + "2276424802663141757392692926366037451925020546827329479674513290273559843227".into(), + "13207120510498253902527141512742067250437249796266587317279641215901655830896".into(), + "11305234940211349140158920206630802129159533495623653407204084399910372957174".into(), + "5497774303468738593952657049388763226915569613134333653497976925327612433620".into(), + "11780989050042171707300156320134237383624870460000896361633114513125178671303".into(), + "11197132624111031475729914540831613691695800675640925983729890209473799714664".into(), + "16140443794881450163624886684327741887016098106725788249967592676372285999349".into(), + "11051595327889397185166361039119529941213436445931880650633610124485963060123".into(), + "4836646605031570157308209197698105249544981108702254887271797808619270080413".into(), + "8209009069949005083422618473679185090070432230901987251869909206753231145136".into(), + "16474909562767173375575972470108350369290532718799369728226238753027802729614".into(), + "6086055204212098936385823679317209054285543683998660974885049732319143968166".into(), + "15845663692001601094665565765687773803894441337616861842355695214969875896066".into(), + "9098420601018427903226168857535773321796962623728010568581252061114961655465".into(), + "14398020486027915166775907825392652502550084557499300430668649177321914768934".into(), + "2529766650486869899750453189656713618464482405989854109458777379339220142554".into(), + "10340604539765718682878802989600110179317791699890691229388614321680034249914".into(), + "9878876288226000720310995235539201725902546228232202248134844052052896121876".into(), + "15598631334601739991411156353640870142866768421111507060679644747315412727160".into(), + "12476036027414150699365888926628033468852849122092016651897900659667567319806".into(), + "20803031063635652861008057675598133287087428947752503544588637173888166669892".into(), + "11219667600899420706469792543049361323110081262796605270588885844619718041942".into(), + "9239500483618882938511561535687911499489200513580468310964041871852656042420".into(), + "2295137860333409753273134561401486593645236761709753752653034489900637997862".into(), + "513840239484316074257070195372896343613221099656421936676995152626965728304".into(), + "1240423160571920455132480263739570027604064696559298170495781456216075158172".into(), + "1000335770452092111597579881696386094005166181014081250146819592415429840281".into(), + "17732477784640275643871498532120858657597895452427536145089468999578047484919".into(), + "17170981021801009209909418054936322243367033572422755593175460758882664580508".into(), + "10474002915893131225108722048498342055539813455822038310171605686332057287523".into(), + "9603348396520941909404735719689357385027896452682306755794683717883589365434".into(), + "13997990766251226080324180880591113480591220558468819674906322487254600879864".into(), + "19165322589470665586312376362321121865643126447966933590462565164041476345514".into(), + "15889877351963901365648141054999234691357421706029208812358207119735767519856".into(), + "8668047067682657740038874191537478915165931784511538119786089297082841482372".into(), + "17366185630903586492716760756481097015828466883331247688174482412238176387717".into(), + "21876355395221655909209577718688727663428003322801196411166438189091863032706".into(), + "8342495812890419318878301820827026193204690451839354912098079931173365213850".into(), + "15887225707174335160711915570149252929623026695369026542224074924735308390993".into(), + "6378295267974387238182583304654614405910401210077434367173120406371155400542".into(), + "8599288341856208016511446109829802753279910611437057243892303163654271528710".into(), + "14384569526991087242726852106274677884712683003324629101047489219988189168985".into(), + "12554858483255821944447983588851327925753928086067377749529759729841684476442".into(), + "1244202115364291386279329790002693224358694897412189477392486275482043163127".into(), + "12995340746860271768506969399635216407185990298275348495956441603189500195767".into(), + "183027008509741755078232756367286076025735908110463758323021771971495606238".into(), + "3573731114015522988150416514182944755210001235963017910935079689853918043877".into(), + "18230391763740278339288441222957881377176531304894832542129957275399074045651".into(), + "5937391935832132177003018978899438941930145516233683431615177125757010459652".into(), + "11430996404641585372503695535042106445550893936697962891622779166741655729488".into(), + "9708207568505790825432073778606679121879367766370067169784170835562004694648".into(), + "13398805456168802594406839007653458669253514721606724093311019328402863953525".into(), + "9509172607193998955864433140854270981598209332315830561489225794322679022459".into(), + "15502257451151630587420754221500874210427552449899109970701467342791400645333".into(), + "11123898636843146818131986172543146864729611142704598768652657993190010179131".into(), + "14756654073235850387277128827870550186598788932163678015197111219905127222330".into(), + "2775596743802877046549055877116659535653734622828894792515801349479916697007".into(), + "19393614735747455293764838818808226619563895886462373669050764399041257455003".into(), + "1249320178045456927170274664240781660345447736835400684416240818258732622074".into(), + "12796406917637014666646024992328239080230785967810042823382614274279812889313".into(), + "8731150020101451449969760761271583792743252060005019647239509560664328018351".into(), + "9933789898048906634037638211430912333233899173340437080629891912049447301727".into(), + "7466451925704968193666093494894304477671721132981190905063657961063858961172".into(), + "18149794038935586919422942907608353967008376245075544434132167909292547581560".into(), + "2645338086907790474420268058095037943265764190369965623902938950146047558570".into(), + "3805226260777608297922168382052882356808083423152211373837998107098065055539".into(), + "4989302370880302660583716899846990066057960576662320219898456979014043515025".into(), + "1070415851697739963236557454136444292508409566097266743684211383600964969141".into(), + "12944245506579810779814853939016758644439439209107032516878032396119045414701".into(), + "11769349237256342071683275569967616595926207710668472449278925821680308524365".into(), + "7515880964846644263997052091312985911975830135506678004303761824155773875060".into(), + "12464357909913670578721898201968110981078170975300784494985401212355213230335".into(), + "12531063240134560397069746994123636136169642575111678588073390651101761449521".into(), + "6147041690306761331612194490775813268707261569782473045839636103273693189609".into(), + "20058953556654404269207947758850631052629168082562724690664696238258819343333".into(), + "10671449076032051862717464872993019906670729457438083279869852502398951441572".into(), + "9263778898906682528709315203073828182663073896201474787696895934306219116039".into(), + "4280018790033654530253273135477384827165889895493311470077828964783579313988".into(), + "830995672310831856642518343627405480722960393571135174568745694822266602592".into(), + "12314576340884680561277987296413286687270775984122709242612931854296075085360".into(), + "17095784374018350334250759671947431499861842886424783004809423482855754115377".into(), + "1870276341686143971262732849057691846357227120913088159474346497974038463590".into(), + "13105414947588023527779026549450388391878972150198998416931943855651155797800".into(), + "18101033009026370452679604491367700242633788731529564744613454397646795133212".into(), + "19518878118392884244142849835491751668891129028130325012169949275103156284312".into(), + "10048097099868399020473369161831917580618232227587941412911792567651718843249".into(), + "13720988503976110065971074438615366180970893755318530444813194189426744566454".into(), + "10206920761042395073417518236713913313307032364045319534823111708069826397746".into(), + "18034670825630502903409981952109964720282628088881306395156166316305807778803".into(), + "17265902307488235822005808880948915593987424296023174321440226533612013329935".into(), + "9429365481099850064016032865663594803605999180077296169535937496741805848481".into(), + "13186685927810765291293578499657740344232117785406437983769041709812502541933".into(), + "8033401834359804746763731113146503141828920003868357417085510169143014612168".into(), + "14829873372988191912376824090708426813704721449621613864198115299064092181833".into(), + "18455257181054398358097306328798734574318231150508108527392947186444436439740".into(), + "8119199559569330746396633117116309013908934582593556748077373019932279318500".into(), + "1284108314616459045915004998457007728060914233392140668856369685125999999332".into(), + "21852625948025981910963631951406283302114607467722317806583142816175293668265".into(), + "14294796340840469579766310516350659401625921390409030932793069064155351769891".into(), + "1783113620816999591947201004404230259494066685648885624939940386561651976668".into(), + "20631226309571056417714477834545747594141696200863258945898766619654143617824".into(), + "6903938062054591003542771080232078653298889568458433305746325082721608484955".into(), + "8256881116864939888001716449324731552833717991541507741291129726788047573071".into(), + "13687405621147199886679388919812090637070670174751017085656416653029285459311".into(), + "3996906960213710686419471713152378445913261990655942335471489495797584984773".into(), + "18609485914058202995712970621084391939086418713703752883448366703199989106805".into(), + "4737790485362149715518348725962346663474900881645708435962411601476490671749".into(), + "2822552925802124863166593828171280409095683790486058849977885272116463701928".into(), + "10894109234909999803847798036461479359595641981247612273970502633226141682614".into(), + "7246294570777241116302579516225659482637719855779307083700855528941826872713".into(), + "9013198024264523556163293154624511496427558116561774530941327330691750043628".into(), + "15151960056770776873319338589377108700463507419234056013408936730718825997019".into(), + "13896307757522598026854039348754398062744662686124720657491498342048635150631".into(), + "936098917227958663269717535799277732372546413929626514061262842640907378836".into(), + "5762347306349503820330412402395950955095460299470544346332901816556543923381".into(), + "9011672463961728692091882997147124758454879881989902775354620290305441817434".into(), + "8298508790377315029478690089877957774356278876814712068800423484382883105746".into(), + "18441738918640842202469737660313781809854248733890462980130113721522272925935".into(), + "11674356714078179732189659380227960024612632400322071192164520398757529418670".into(), + "21741531861742185590228910897294372846694047325677774428196013372184305103642".into(), + "10726909710822683151420187237143914110905744739798587383554354069582246136039".into(), + "14605794533892313551961292244462849563801666394421863533658628423573613615452".into(), + "18252321089326369077368015604893296999452816437445326292408407276685442204248".into(), + "16489511737082127400599277690755867907347415222776413612685047279770378853014".into(), + "12335271216576477339942530570019671580774683415225712107993157387028894905764".into(), + "4626127015007086021549442631728304324061443778286284381465764558652325702889".into(), + "20204422163930758830873934092037602825061261159909688022442254278786267559979".into(), + "5908008139766198991903649413759936674892226062609391695798412118889649830013".into(), + "18949218162516421406624855541340518738013110700902674485216824630035967277648".into(), + "7857158354961323527996104954036841386964344142685428573605787682122921808752".into(), + "18862506387045373228977985979058776771998946696427211846227485792071994347569".into(), + "21130307920872379374988363214010414716464915978170248808030666261671221223543".into(), + "4313228149429751211745340097430041420420928800665815874510443032063502322102".into(), + "14718887479978402714517595897896626933964355973039392802299204272000024809808".into(), + "6557683321264859885031038170253446373377122179252187766556430383002360480784".into(), + "17122476831292693338874176268140351589680349774087615644242938775777697017452".into(), + "1742427015172593656128486448061422456944242303531295475988475807736737942042".into(), + "2027235762831193006217486738647428392424417413253628143597055373587736173882".into(), + "18820467606179165158872620834655197843967670286762404094202648563691406576741".into(), + "10125524296683464483537740904779830909043887085914486254166411012113004643542".into(), + "449309305485238804235855079592488366640797157061432847068620096879828248038".into(), + "18174298857888178621780055946769726501913338858930582293820343672195281946232".into(), + "6524685458133323533884653748284163312203275737781993034758692071803006590911".into(), + "5779446108310889204396522202292098396120321905868746440329327547028173740638".into(), + "12117601207346032264472777912492513317230650895345305236204051403325746076935".into(), + "17386891799362994472985052445984141822367941805998642899269245566076967495748".into(), + "9179366996073356039318771265789433069240332535719540885945532359919512410546".into(), + "17951430600208154118507690771734790328820535928178248368783575740656844500941".into(), + "14192271756367781523935569988212364655718978403224403387301537958961554173399".into(), + "8221174165933793163901377098818684602634389528051583218569555199418881843300".into(), + "619524678975534727395248661178123212036053322038080027161284774894885656968".into(), + "6811391538547034359716390364062047260775039349151599816482953672829058524341".into(), + "10339322550348888691122766885623110168053574406341544663827975995639398303045".into(), + "834547622517043358034390288436661677632262995429052976763390684889069201500".into(), + "4521430246419400790694482286290497928487157243703675820424059696776489816307".into(), + "2856429439451475378239016597030439586695051951861405539943990631405466607827".into(), + "4111906549409128541352401705521383327041723912730932911918947847640475304547".into(), + "21238392279368838153073998702772069142756942255081903718049634748881681359750".into(), + "8024413919212120225608654701439440497293912481641002166919294547045993866155".into(), + "19638917754035941400580825054915831416618623037514650566052024405861982044782".into(), + "6363298127640737755043533904928034878703559551572444680730376201407773738099".into(), + "5951378579926778764731263132308899303911912211097125509228135617058018449106".into(), + "4299191983632622048727922852687899517860127872286225781380066638217198421795".into(), + "13147530014153459600370443470203975351218206668086180214214011567772265521187".into(), + "8932462500427500118784677946351185252169486521981702322459979960456999825085".into(), + "12898236202423618599592389067167172325929052486002381527830813712396261626227".into(), + "11097514049007489440587570256998525432340163981998515135891661207289091581098".into(), + "16939217257331433664860194883658557949066598882598231827843396489027582239493".into(), + "13558314929316884214321653422030897165706985165664325794343497050086242614930".into(), + "19068785933927002693740188802713137005358154768592984493993019480794959714772".into(), + "16219047234078325455733500620647043268680743772219676241027699492382953851822".into(), + "11566125486619880337971321232314420701391596877779136632592874470668332390604".into(), + "20247803830618639726625840441081484205981495541890152476410337155981008644211".into(), + "10982561928418184138823352638412764796110898816926174525705905870653768875109".into(), + "13865455597687551061085338688013943905732507438297014744008229213490729171094".into(), + "4623686187146061526075222395870304599606839445854872221666055672567414423759".into(), + "1297323969860517810025547983969849541225259454470688019030850877270479215579".into(), + "14722215188887952104786572716523684038024407798912988920497316253897978502603".into(), + "6270780574886929570568937279666637132374218041131318249843841984676427785096".into(), + "19252799885324088942740034016032302070594496616562387546044018090542659494091".into(), + "18789886326443791197255838067795128702043473536108855579944353350374719120963".into(), + "4004428942869607083699086726311786949460607919907288923307322356602295441411".into(), + "2877506877897472797549134443444956093653579146599656174810841010891875571228".into(), + "17100820986561348145097462952844272816935030845535472396118435485954197036467".into(), + "1347748684215721199150368750223253727950390142032926135229664962610534785585".into(), + "4154005697105794859947043472401749393077233408100643568245878899626874345951".into(), + "20933456016305992700551258430315019036248529816811141758791937748005878705139".into(), + "13670472427221544431486503737474464655536253995920989690199387492221273597238".into(), + "4887586768965099882782231950154450807371608927535862225573402423957578253712".into(), + "20873520765554306935202238707923195971241799663989834594965009759362455236319".into(), + "835408880898587261190035308896108471998627156220026699783039947254106997118".into(), + "1168560772528590466033559747490971813513190425644816736321133758228053063362".into(), + "11336620716299236865855310989061343228509583886619209006261263862998075759368".into(), + "12980812954661261543443724949390081395650693161075132555819949729859873154552".into(), + "10084252406967656001546402499122907140953995723236718543311202294822930374906".into(), + "1644532730355076490850165587814950390528959259229216666472798276024619697665".into(), + "9132885938811460827145905853169583367514769405887568204387077749084430489477".into(), + "2725900975136309543580364211869546121021875237274249313229081305044972484552".into(), + "16649132526365476017376435851279563056385888284228253635122650167497125722498".into(), + "12731917506454877589272897239554773638029036473865906141458475592969608419975".into(), + "13594653727945200481903485136384279897657772670755085103508581365171774040719".into(), + "113624989360858461940510511528457240403880397732971520793552365714387680115".into(), + "7789436153009366426158912863312519977840172895511546817589361831513864987768".into(), + "16143950714560148477045714466178426422461079160482754992311521791041688103078".into(), + "1313437798365489562563299651704767522651128839986029835665434483203980839176".into(), + "11252213995691856633725287186457035931029344732702134439407400361778220541650".into(), + "17736157138183214282785525992210357080431726331346127883418866302441793685861".into(), + "7180555369115773227093283745784562042441101566557650242391581159039035173198".into(), + "12210449409908576791549312007483796989239766997007433768188337654528776461829".into(), + "21645736522624670721457131423612680680560891552619001261475935027688037968464".into(), + "5181231859976513481992166491356437447372643837921293039933901630631480168455".into(), + "1822647984377309820943781894578040991064004363022862403681677456868041924061".into(), + "18871375493748399832446064841374255973869421566131394074253151764055941485971".into(), + "17104418810613244184376069439022653413672092602368197196666088607272531673120".into(), + "21352793177578730013875622823471064860889253282644816974785905687738185801553".into(), + "9232438288408252188083609458343687653050330220889343945216702072572457878290".into(), + "10304159048240945355136802455474936548590756836618851032143486546904634881690".into(), + "20884395191144673183897559585241012685606665510456998263218110825947409467914".into(), + "2819516448715556874313717913942313640504691377816634987702668983716292161632".into(), + "7032608084905515886212514778954491987158299018684745883187663518324250619807".into(), + "7559036428305013706113865872782652760220550823937161831466421604635377603666".into(), + "6166475941379369323404906159647871547076193215494151224037609592313271326716".into(), + "16084803679651600781285997577643662084204923480676236051120275962952234677286".into(), + "6658815199061181195201720843092877884603511322340036305618980989430727251871".into(), + "11532335040368550811455379206918215244397070466594500713220916970398105300183".into(), + "223243445248411375033016341241378810903532891138952541245228108341484828711".into(), + "3833279990355432746436294779672706393587026552206068860301259597964955244359".into(), + "20021860409670140006280261770552932742716715786145482153861376747693162890815".into(), + "3960940475385499730462877864983553000107482077213680397288242993303568324972".into(), + "15345391775733994458108231909936015494312151716517317512597014646307002937890".into(), + "18277195779471824870812914706668217603589292191686932445872231693455894425796".into(), + "12683998657212817923655738438553698764065257310760896868569072930043411914592".into(), + "2381616850898069714423289625050293940364468416728632275660762167225224591308".into(), + "17069254001556064617313013226218540738464402615134272312196301018778835133248".into(), + "18389457738365086195717208767239120166429330517506018082718782715649157736034".into(), + "5051258247116083013584227503078370155132486498229823799712010421955209135014".into(), + "7997986980169935581373258514853533781209293632678320920663489466999553296266".into(), + "9084231611273069170288134842362168520424117219722297893241580415854843050029".into(), + "9612017102514059830687104062100896777017998502712927457146148255553769995330".into(), + "9213917869163068338076473790268314749980273391311658654819685979854193078147".into(), + "3968414534544698110694188177667553264094402034823447016934487565301236025995".into(), + "13588240166210590726886944410340082403890007139576685347416857458181479170063".into(), + "8425770643762906660570072207190993414304346471106950840911695090319487814275".into(), + "1652806391920335323990555627698612547647520442841733871570123598479115999627".into(), + "18130776043151518739068856550215769502964647168112043888178493846726550417660".into(), + "19135684176515473484108881204420762325009779099777199113513850601885029049659".into(), + "18523926306415003163998391622948236629256503699057039444767412461271077678701".into(), + "14918281748682691271882204320979112338671695483566718123583225923120856316295".into(), + "8780852767944315665594165432479024772720494001116664689088446074574987494813".into(), + "12668508386943347180515022360355291044387148272127255261058463414454846963242".into(), + "4925095646155669413280350202274427260281996843455607837164490899877054944846".into(), + "2595801235413574965831438899756848851934934001108314616806171013774086568322".into(), + "16016207465062828688275342866097975914741628027802243675066228993942777558633".into(), + "15024416797683381415563755573423733835281624847950439607885361353822729812986".into(), + "13470695612540254635863734898909669181168896043111281127113964919743317383261".into(), + "16896649112088097145823038767205812267673142507502911459577638240816340032813".into(), + "5645009976160313260833270091943535119457478455011072475922688828021639421410".into(), + "602871159134715892643983949134724748180211299390344137568222174867411670803".into(), + "16865933848430240952665445021880291168087114172384742393439371745277930120747".into(), + "10638616947682848797128584610899807071800790542581123346366834113610995491100".into(), + "15823119882352004895965266205059246906297215947121418230976223923682971872516".into(), + "6335662959372125109139397987508104273775067107064186843021683240893850916670".into(), + "9015302195224176847832179354591500534302983921972567063156768309852422127379".into(), + "2916208839246568972516202976435804092919863447720049244313355052730101892832".into(), + "12462097677706957512887764105458513125712391543206668564860335255817545411462".into(), + "12645432056886268747459912808478608940565989560801243474226148714998374680219".into(), + "3180920336675161838114354449885404230480750260525222632193238650778186030194".into(), + "2765038069658052652090069059291472962961753315554068900506936144102952443412".into(), + "5456153930594691794518876425254629839890061712982880399455001410106283524009".into(), + "475588857464770407073340860667255085025964024669275489020055454066130988320".into(), + "11604422213771358649337761367286746573670774367844043829963235653833716835011".into(), + "21672531595983328080827235954964353440581013778240609331813180834517858151965".into(), + "4158282767336475980124978163165153479336138216933325437750412278000398054990".into(), + "6032343652765967766200639763117076693275022773598878447445516751911914770612".into(), + "1408994971542022766817088410277637989937031937486427645416181643240579694602".into(), + "5675369397167644827707774427966283386689036453227097867563588663607826158481".into(), + "16553836408556819911384314376537283029998074714786222283151724244687848945589".into(), + "11305817545614916257748042736988000193121142053044277161651567885557438654074".into(), + "830935697282120891923075627410843569046784310505888414612692179620161520408".into(), + "7111327705324010951262975966073430392141084274376586856008212260205185475707".into(), + "6041014280799995292240499899142298082182090482839684923821947404991902712715".into(), + "6861532079489296739625909197765090537384733717891476126939216899495278356435".into(), + "19512767336722762932902171703888917915267449700217444230295060094846152793374".into(), + "11362087145491820831916999481856548088963000118526088967383804499026380829346".into(), + "5345785713105818128869449775136443413167434948951799284901802489769456439786".into(), + "17034353050546517551433657764343123751855407033689772581554238132654775360523".into(), + "20948587704598096549680941899688868509854524167514075706787714294359604474171".into(), + "4994911941995400553330254818898448719600721576253446166237206475949959408151".into(), + "9688152172628114951713346942216519356583583295750612146456768853572234580639".into(), + "1343745797764784294443121474436370243518251011725658365765145682550385976553".into(), + "14125032499992691719199426991191077310874247595141214711018898761632184485555".into(), + "7693434862282992683488101124262547552730705787109948156284399632991566403920".into(), + "358969526543359888045815665915036908949414956460937733680920722686744813444".into(), + "10388303602140838746049100800433273910870825475060431768126678575590491651119".into(), + "12003019672927505670536349874007900046436859031431459357691867861903891807068".into(), + "100475008966885612365204906854412018822648141035087921833385587863034960276".into(), + "4380976634270985228665957813264807511324931231158992383372104805973515660993".into(), + "6796890817241810905532067210297629305992402701618394901609217064231507595098".into(), + "7688772779168713577464553940881893522916379475003769246647902913600123216803".into(), + "3719641128095581959416223705616297841752773711170097329183166123897200928969".into(), + "5811339040244843745049016703193592081836868671814750017107506365287109556605".into(), + "10595470132919677811136446867941153163237789139416696690218064678877242827885".into(), + "13030722908918648575860840375066614839148197698661025042088986261543919080554".into(), + "14270313844057453712442736904892342369114041625795108985761645871570953543978".into(), + "20755955200977796735614094726104970512145122846293966132203844248531587786651".into(), + "9582579815411229200777590264696860160165980649555432144848137253789895405534".into(), + "4350662320089661964064338788392477540031840065706369837322029959874552780000".into(), + "11294656075067466938178599864610454390092206885966775647004601601555888026051".into(), + "18946972646213006149468901853070557711933198234858050136328420076856760929327".into(), + "6183788518442229735210652665497651321434896307527846329693517674734369795163".into(), + "4357012315121006561105620778754114894493832281520086713987988030701496525309".into(), + "4220425144490786998849301484441980495606132822919030749888073325247349169938".into(), + "14847262709636261703845309954203315918152776098063861666522433996841734492983".into(), + "2207604061881398535333278399637137320820424466179707349350037157354962490429".into(), + "10374520449793714488752224568044645973292278262497536584844249765683309087900".into(), + "10046752763001031505421631811000499551831682948464981948135908582555522139635".into(), + "10191830759680421095919984376117799343794470779618993576052513294297834150862".into(), + "15076222662591944075232357720469485509899269012588377631779056491168825321779".into(), + "10030346249770809877262359992756272056670319300684833869196768465826414079904".into(), + "4720507975531560789365265597577186025064384475474978173798769571327267599028".into(), + "17191621856092434134985786932980111138837305571579695662259528942970338620884".into(), + "20148399372131987633026319910847242205929076170415419364579318442517450329463".into(), + "18536741510173922435024204045457609239829288683624474544615917164289358647693".into(), + "13631750264342742049519923217689863850040711017202360015936348405918551837067".into(), + "15610854494548438731317902472235239790464071411103741562289565846725908998069".into(), + "18387668285066173974072893371855420071639764562065930624890442086525580978084".into(), + "14766673420520500288514312076124802867192062521660406237863986898959550205282".into(), + "3732185864446238105520935485353063605521423641525603230547743875428671598696".into(), + "6726861976047081116380559824755952195618930495454092484512720173680677582718".into(), + "14230102015194545129058653592673168907799074203765469344837164009883877641547".into(), + "923698051441061755360657704655014798009775419003226945193621553062258046271".into(), + "9689434574353223411824004165283477169808920117596652474458537007050145695959".into(), + "6653548670473374668472111797166160911158340899061222339537692495508721251614".into(), + "3795538039750942962828904073106200577850807348658267681985654384336620251059".into(), + "16992880538370345570685975897913090022139738512471371596260168908478239467806".into(), + "2201111019550353407862043052634082471477202486461462021878536956251970423986".into(), + "10288260758300652435946350416864664136108249309736639494191503136475372654927".into(), + "15709483617520776431567199768118570957603227217270596124343406544848127051229".into(), + "15072326085023560939831375504206736581668538451241556883193631319058350825383".into(), + "12561979699882391671048614617301093740097194439650447103774288376923816448076".into(), + "11578577676104716528266269909528660737784973170422409028333858070522660907599".into(), + "4774024940257712349371248845501840009544530180895035663519273410524858053238".into(), + "6492684254039247711120616647406430763803059236762636509335603649550553435616".into(), + "20771468823570726361366818215861608649059576818091620623157713556101748134372".into(), + "11770234809483645852438195876591109251904813195909801267796013610714277347278".into(), + "10322220982623441869990996687272667519839773221084466704263092761447901512117".into(), + "3873750796549479014998049182061589742243147534079044128452565125163635863792".into(), + "16822068107147748367260830510353037256008422122115738671496285709779217814945".into(), + "9894626898191884524351933214321095257519558205219922050393534754827614996074".into(), + "14601192892171927905999982540260020693712306290426245380657926166256138073598".into(), + "9507456803001734658737641235710642159379435011566715643921196640676158132603".into(), + "17204767985326745166438450937743889339302038750836189001173941240117259581627".into(), + "11493735704785585407432943012768068237407868899310349385901453142043105992145".into(), + "20264601365705944529765978188292560353562052194559042151183990689436087045415".into(), + "6396166643103201291451232462310280514789516631380296812005611744416636341945".into(), + "6058445133237719937803819124490281802427048168421128127070586670707895911082".into(), + "17832742609350217271111001369998668554917153176201832135065834598001176606174".into(), + "8301910681660121964370976371283430908357731729231997961142961954342095329645".into(), + "21200700180783276860226125147359033530111487784446376587831447494082008760372".into(), + "1364486141742658225769231963526281625713690163326935801558529383187840699159".into(), + "20160669267302086336804098028492246933121636960809421351182280337233197662342".into(), + "3635162620064198915033858334726014494627531534027138539712906873727339940152".into(), + "20185829971410353373403938384712968683319708682735072330111840928607967762630".into(), + "201523690073703194002962504757428126542763652704293724447838128821998746350".into(), + "9709386469732131146744455888595822333747765095732359649526137736669486544832".into(), + "6214797208590500165345122859363723824928963832472111818012074756679462353515".into(), + "9545179136572857559578932159310632305901168870225039498707457861463081350889".into(), + "7958214991370581593237224161079745627463630298171564909746677637838740804837".into(), + "10143975143960280812282386246733660505472918014880729933237691446362785576312".into(), + "19887571710409013601609612720342170573208042935750400481652798519320490462147".into(), + "4385901431531790517574018221866017216283585679251714390188554453215701245125".into(), + "1664624586830909279573419685895566313699996183243836877427547144869129575305".into(), + "15793134038802395204513915871355486670799044444942914383804421551073411566881".into(), + "20470704207635104884647349860077097804311221255557839038451274311052027499926".into(), + "6142526726838860239230170135812194898945683787933736938595250804972260353985".into(), + "13855192186192101570267811921410903075943095341965742420598451678343592386178".into(), + "21061758735696246985536203700046768078820850320825204373370639318620908053680".into(), + "5662817483820503797666808429029051672943117302178352551614788456216271483237".into(), + "10333519634297417737172450445440333857302089078097476871967805971949123871529".into(), + "8218569982501149814702324407474792474686368395700170296571212557818282461359".into(), + "20233400137285640555195780437569385053497789288081872387339896466294226099158".into(), + "10072372834150094335098617649200237176564672558260325152023334458902878530776".into(), + "9266800412877476308026297272156162694485705323044163059710847355278349659379".into(), + "5276337117659173276438760052296857124207008171563166875174912047683077401125".into(), + "2006436301001583347899849869571017812498189722949517889639315185888688453745".into(), + "21001242218121797019239763758294108685115914209711313091446808028690842184156".into(), + "15200074016374430224281445993551091543367930754541238817021042732526880610544".into(), + "19710826528876999823124699885521051879572010811534557471867248186884736123929".into(), + "21404362987802009741223312847298906544768900296657310719715972255986007016585".into(), + "7600530036229891939971232581797836974827849142680304648314541550980439417795".into(), + "21632454463616985503979878539576013805183337895553881618120668190150301665261".into(), + "6334758595723856085036854188165101506598081801208405745986881361560891199193".into(), + "8057222251861033664666134852961391247267479839150238989503146436301385103200".into(), + "3219721852432286392418844864881031568045047266727931843416507933961433025545".into(), + "15429997317679838542619650717630685653076454689042566593207301547187192181410".into(), + "329174031237742757022307048272693803097641581155976690924186132266202912753".into(), + "21732746139483704151357074738923965870601454140619898689390165872304758524581".into(), + "12807082000874685484816248695921308340943213652025179667821051958845707861874".into(), + "16064026146032729000070008033632737632876654216683451632905581465015365908257".into(), + "4212217153278443796283906098959531995688364954974810176542537190766858609818".into(), + "21143229289970553639173591837823586981543332391397328546878889800049585920625".into(), + "16608191574158019592113901126750079565651977188938492362618874182536574927398".into(), + "3624421885419003645925287958934125046517213705659854889615838255824651468218".into(), + "19316501828231774431815118527212477451661840620435061360743197812130426231605".into(), + "7662891999266634580053960517366506438335835205004858023405036760628949585740".into(), + "16083409929948531376998108923041427957606534473459279085237388567102877584234".into(), + "13084239861882397209044914415955882241492175271640738785828432132564815850793".into(), + "18210156525345343590423422394059177051429214768712959878778772626339164549874".into(), + "12315464041324428320313158873511378501430493994363943547122434404767709664853".into(), + "8954605304811330146055184939317681861245665333018873540562258152334326318605".into(), + "17293224339343743174562117843833611404589728024045173960633854068798646969854".into(), + "12077428955835955218060944701333382577662880272982277398053344529073555194830".into(), + "7126046228531607392073289758329125358329379287853920330519927955077178144953".into(), + "18588044234272060286987286081803360761513524944191766041826466047510554640918".into(), + "20890448514466943340126201065304171306189507157689256667534257974304306634091".into(), + "6801003349507087476371788107799511762780253146305342273491215439215969413940".into(), + "3780619510354109448305575837112860323918556596060836509041441555683227828678".into(), + "8767334354313558924867811651574408725653000725545478833889044301516199439630".into(), + "21066572358677074507410523123848261256731407535120503516033231013762098795269".into(), + "18362039042064137510549303809539961459419339855353944845069477034687076489851".into(), + "8913523920256755688178316396983509800293437764919338700779034854771483564854".into(), + "11723401432613346160205532396432709544892024270048469362382924649893252868073".into(), + "13215697753311268652932970590975269015978051881025604765698398517094385751294".into(), + "10096875802507022627888049762027565166660383010058121573194339315127497644823".into(), + "20585340774134656767573027722172296226739825364200221949739798500696627689159".into(), + "10445154974790139558211972190334915738383718443406895551366524953208899973439".into(), + "21673116720904254079937605482525822519756033862602805679958950491098633276310".into(), + "8625969092860470813380520419082444422545844964279894335272441710185243798934".into(), + "4025288875907887520103326512414690750293134922246866469493778161136380894049".into(), + "14323499257030290650454481578106573336673758956871892054979745929234879010775".into(), + "4477626471847050821212591107628903165841822822191648560679737237549532746098".into(), + "6035678895480961290473069135353807694173152744259585744720200702817852056059".into(), + "13087537929005319145430419950489154179166101298384003566794942619862071759110".into(), + "15034538492744821867836124336381708186593112293438604485661643830336837149153".into(), + "8805139564894882006885168301537536158944953788019925854273090624783308357258".into(), + "19958581387726270611337524069699729934757503443234146496516617463929755531372".into(), + "18594586877352581063978277344172832910008313781358399221576815775414535759101".into(), + "20126420557250610569978010728469817957691099256920991551072367148797364737827".into(), + "12608220343122929299383466116607513455913710378844501818574872277345002049133".into(), + "7472029840435729707246144983643315764441672074448478483499047549570431985698".into(), + "9145534859649655987690711448922451001707059796425112511085875363972462746054".into(), + "14833791973699407961392823324214593517418186311389016446279089810253909982772".into(), + "5890565022258491346280610728283677536409667465411457717498759331136098626193".into(), + "6036941096143250281821883413338948223454138251490770618188682101941802166158".into(), + "21282043678033183106352077009071810571452663281954166213888935238324321673748".into(), + "20333804372144772829649652869275200527604963503726633269029046116798625871599".into(), + "588131164750999180590569033490334261814498911261696634224331209454245047082".into(), + "6825251444721740074952317618082720927334521807162055931163012547596328197449".into(), + "917697824543986394290655655205290161739195186499427989467675782006019774097".into(), + "15041830455925245428849291972925354368991606611607025955853683450026323915411".into(), + "18474239974098280549955893651580150846097530768719853315252027744284466692200".into(), + "3611784158748153035104121136455977264222066374809674800505557964617331443457".into(), + "20604659450393945231663570065906647078596881678092078709858521111095167311757".into(), + "12029452301550033145724326108003255082656089254479215038003174967331098163936".into(), + "15356676148621236051968257733981998213914051180019702757440018429006515803587".into(), + "9423362269266211501814985604120446961293044110825100766698681717935266136618".into(), + "15899728330782424719479421593102373102438201721567285551304437858855478298992".into(), + "19106020154064303782722594063821106692950347246115176981502414167189733529368".into(), + "18908465093637028691441981127932774442154173676388632144231302290143899401470".into(), + "14007866203893293106212925571168282959988090650409909185874688109550330526342".into(), + "5342742216767912627176491444663860490435717482395442556665323768598286007506".into(), + "3597106827456013501397704575480537495681742041904755766922490829038770978602".into(), + "19345954862705630389044258042054882168728404897041130532396992253851595567599".into(), + "13651761202893618441816264801125692678934609890284167560926428043317180108703".into(), + "16448172308101926626190311626973854215995348130429072972101942893358814042334".into(), + "13856649049195496934480457183055513754814884703875171340785294070612325977764".into(), + "2593658379576055879381789234288074898757591156526450196693707683508079496462".into(), + "12697568363447920404231218962893478531573733618972364303216707079428387821529".into(), + "10578645587798138343532931014102095372709438567484299904261443385326645475306".into(), + "9492492460728662705158180596118275120458955222134204696298182307017420905508".into(), + "18064023256112871749609279367679203109309609541901431681842430343172099602354".into(), + "1989944422169611668560419735240024090899518972063746001231995750158992709746".into(), + "19903230879435744155012664241474498197586597172440005182506995303093565641777".into(), + "1728511243862152479724441453437511013527085523030423049007411340644871969509".into(), + "1750200860549110325737825369011951368084185262869489413254703181129282667828".into(), + "8859957468047988361338455823958226995655820770262073750796277641227321341998".into(), + "19127783638542055071733839851815229696558338746338070032734486927449049698521".into(), + "7078877955496438548183194474726026189811269821062629263278508622456589680639".into(), + "3091324818623570240229136118759236075549426086908228304466341498471627762399".into(), + "14195346783292343393569267444162421134560117165562018311694222203790784872224".into(), + "7366930408724527902650425878366319531313526362168392830194685421619273052822".into(), + "2217598702761121052299380093973147900304006951260118283287015270774119856107".into(), + "16027808241528674736324615318588531063352235936452195394658431080548186455840".into(), + "9737799828204615060867766665537725835381601825573025977613959705161843611093".into(), + "8064501393540602388259218046920616332801811052858376733153856073004571475196".into(), + "16842422805430821078995618361206787780503480464556310995070028960079907202166".into(), + "11112062472802799468234830097050518793604563281810316763737206541428998286081".into(), + "3996143098194824093843968965659132847407504849137530967999278126450907191255".into(), + "6307499143062236655628471047117923013144505466650278288900215043429516667827".into(), + "13925586587992763438435497293527949989397459231522640308463502932206077961500".into(), + "13087452945182453051277972085750711303320428980713041081211383304385877763691".into(), + "18093207370226721487749634577199906992299418255969574006908138736154443387826".into(), + "14272048411789712231464337625432600879032064592923947963806042930999866142049".into(), + "5112973508688401012277318169584325529641020499005098236869086381676918059371".into(), + "15127813438826152618698293752642344087924814601034550461410899877754994198299".into(), + "6969695649962440087033894050377257471647257275605050327543911409064993144574".into(), + "6755033946723836072389613036793975461823169166252828563832361834388266522342".into(), + "6630309846049050707531890910192358661458908514573775713066783149977348075866".into(), + "19770918646745678079472470109308561511682083548143260940369403377403283034557".into(), + "21433670531093667302664653834628387344084043216006808509497422477800438816334".into(), + "20400712430451351419962326556923150417233487338363092310732550512855900177990".into(), + "9914702749825526925254257687000920413318621895298404563035360292451257098124".into(), + "18640006733688503648022700222053193091432820833842731658823766601630552120045".into(), + "15001143407594668782401370718438100322710708950874705625981291998887962398836".into(), + "6239670443845313966789296359213657096108058977356600364820415786936148776478".into(), + "6433425223024186602967246363501689243341949458306873394092764835636608100956".into(), + "4675918067976678709774913862522295500806856576135412363070396552169178386308".into(), + "1601751749870408670839362316137379218159388455643443723425257645986433854434".into(), + "13458041123361531838745263080315252882353509187267791623642428828275334206638".into(), + "14471699013899259577738777309533960354293333303365529990782321200494073324178".into(), + "20280430398960027135010448240285550337237252858424335652593355698129656795752".into(), + "15301671314764260006262782923235826735016067425075232061024349010859210176138".into(), + "8527420415370243636986575303113397012724035052720519440118106033441076019119".into(), + "3475125085974928986675306749785419160190213748540322213576708845513558192234".into(), + "9006980194944928107206620529900294492273459999862240882500316250867368209534".into(), + "18341113199274904243447557429147408702772610985345439625451614852727142466679".into(), + "641303854841744162878167226844505402377828908784963225549380412498130669293".into(), + "12717068536550964634894133707393353175533641253913906785707227145457684209533".into(), + "4743152264042462778433711542894756531279965736412696362852105777475691147133".into(), + "12538011691198347241319721961633309700824171196141123297965193643240835576405".into(), + "20609816220088637782952524444771775704801169004700347625707989982325840582078".into(), + "5706720205331708334089980420369782946367580993809969730038974418250776704373".into(), + "13670952207260779576827977242585356546757059123105172869720468020067882252702".into(), + "14767430839071230390516499921530329576551075490453331584132978354378461913602".into(), + "13855121278897572089789093806573448194721330823581341784260637700156610112500".into(), + "9029464346382645337824607466955710618425900404147532427117006888589723750841".into(), + "1522014999882036268928376276435422913665212355063956230967744832498304066925".into(), + "8291362624704304963260873050727236420693946064959013721544766852407674345583".into(), + "13589063181500587026605394799486087482543530017803239994475549538732438667645".into(), + "13329949252386676079710645426263377053183751810973538969788279784055444085091".into(), + "12386680681590613307772862225908657100001403625719832791304530216719729923003".into(), + "7313059443635334631417408104276385788845981785748211548782869561833456731323".into(), + "20041885136971069417608157269779303040508951822068411540615824115716811017904".into(), + "6497376869042504752008054895287149835408180730990917088183191265348284326357".into(), + "11125724561218240201117500197186012921479012541837342663628667709300377769139".into(), + "19153359808996709649541642396803885814514927865816858283729145733607372609049".into(), + "20152572433538554395328147373104228881714506304708579989936661727209606561409".into(), + "20016439558639474267285355593250644884337484988031314831332037478976434317958".into(), + "7527238505813249238885718653381005755459117986453125478349742303052560976989".into(), + "18157976047185465548629298047288220777638494790360987385974875362640599836871".into(), + "12215475598068312355286440209098941716275384959997573662132780937926913300161".into(), + "19661498481048830903134453807971238540013636804700867840177613880955309016673".into(), + "16552339415724980921932277403793066165528259484794112971750640492538556458696".into(), + "18694143444126088750994041983752049518157493949000498621268553485531912882317".into(), + "3713967579039675465490867111467075244841382142654006118952820645091137343179".into(), + "18003791572957152311824638498878311682370695010264579557670846595111585019742".into(), + "12786965212577099174577804313633357821910340940882518052929431837084369088642".into(), + "19196303109560878004701565248872597636081713248916827960361832879728708100252".into(), + "13503920737086542355495290621258674491187655992864087131383678759555676293479".into(), + "11236070378728091455898747203990505205014449224981497050563648348504784872625".into(), + "9626632821060958086537183012642203130268418644318548991569123722985663518084".into(), + "13034637881184844942276438495251249937482750510182566475651230823087848537016".into(), + "1332579251268852603948763724532278782861309157968811258504214239247723497919".into(), + "4440487054089857105584789445239911365602192945489433191329897791642671641022".into(), + "6061652828396333618822938589797622370923339089332057787863230635686741988906".into(), + "20443625886285957158375952392918402164453450517610142032850159380049279019890".into(), + "4971727988681610479349722318571442785499756930724120997617079739694258077982".into(), + "18834174333023112536455562602096071615163100988222060601738058925300216483554".into(), + "1418358707619290124642186511810240322979679442148132663647788839527968980321".into(), + "9146563693745266063334532013679253366507504975079644933043489415067653903203".into(), + "10198203189192787713857436708411760529883983417357336104059303759507359385362".into(), + "13457105298809247477659188594503481428440666731107167805254859937557834208828".into(), + "17541364687047908905500996158296679129819772711555398152345796866338919060112".into(), + "20692063470613585148684100931411467587781915529223705941251811918651910411051".into(), + "255059205539662129885859108675295842232143503986252525395037544865412535350".into(), + "12717171726844166947240066436059787676718930218202178144871226850052045252628".into(), + "11844159733516000729567466032644347431569093736490287481873540624836993897027".into(), + "11002794037358979127166193862990494036532493412598623614028528989937231393875".into(), + "74143635505641635571137692594773070124419429312664778822553090280375104361".into(), + "7056329304173643667817989532223449288690091032171349984912618184745639839146".into(), + "18641921263165235968711057500989010664480104039807002554029677125998810936539".into(), + "19559632820551845243777081617723524038257569278710484296878486836843940297771".into(), + "20335178684657665532201643783226288715290471620876336715869115375508863124376".into(), + "1400810679146237783184309652518065868576549410755675655514372408922313217181".into(), + "4901107318137271229016627403293875571324832946427895930672961592355892796524".into(), + "5432919409806696890084817096600220639163358481346859637851564223110298016918".into(), + "7553857716186244958951458648858814979749280269320630962042771757992327513590".into(), + "10889701053782808013047326782283414392744894366555627960860238324337962809504".into(), + "10225055313049541990073049693496605844127702734102449597528239569584063842673".into(), + "3178201789995579607967020188792297279206559603024986232709441132816492873170".into(), + "6847656471793158091195364320441056206778429470543211654532315995128613054310".into(), + "12528583474708786099486823217911492959125739967840882229222854187982855300696".into(), + "18467266119817551899238434875437425808022051394845724810256998883759435014686".into(), + "17038264911128891077018547486438046001433165723713308233430847345626729200927".into(), + "17955218916742955831569749120296633004126823963586826720707041154002710061824".into(), + "7211241300282304354221867200910811631523632613327223837945134059614958044309".into(), + "3903314564827641199204912578257298318982395101598969445634590319788688787892".into(), + "5807166989004370646148664081075213577472290526426314941919217927313093625705".into(), + "7286215868775919967580841436085988310805057373157718787975640918960675793864".into(), + "1250176112708058341117534856693109443612022505613546634776485361294747168622".into(), + "16240306074762036443870399577875028338427695244117010393369806321281281115890".into(), + "15136410716022250493730454911117704953380153308032786787731835907574927894490".into(), + "12835091061815802274496704101037812141879397401714459083078081237987781157602".into(), + "9740091907879287748866588073726349058132835900960520906530105112062664428632".into(), + "15914692134543071914048151066306389736053927516189539093172532429297748396169".into(), + "981367411385768085119476192577825808403597700860420619441932576179905250934".into(), + "8146729957224132449602418119478334118295844780357577983658167000941734233326".into(), + "590349410629703152398401018217258970865749864711821880028353966569788201764".into(), + "1825395473256187220041087977011184605194426952605089376835233620262931194993".into(), + "7123515738169356759148725471278134181610254795201025187792320024480691382570".into(), + "17726785796913877143757425784904469412799726913909454284181133920507167220504".into(), + "11651205611193223879329620496932918885123654427491270580240367578782038527170".into(), + "7363492115511936228845261039013774575631247230570693094937015313770023603570".into(), + "12933273009260754826631125313953911983930122727552157672205237418903525642215".into(), + "4587914992471995247205971137457571771256946895956563575187244174461900917940".into(), + "10792746122808647351065474080470836119851772052088452562229191086431179404978".into(), + "20854485826587742461285159604615271614646978291370447131740817759213177014870".into(), + "7837325030066353684135714390699866228186086140255598480265046489057880448954".into(), + "6126605304840233971990826423459339578353618021565536707601222973624714560789".into(), + "9552503931384803960142614388346661043921110897228435044165354206374121160883".into(), + "21548322368276531554717543098918449676251611754505041151177909568762524391378".into(), + "18180071753020516412865055548290459200005267908156340472690024449103604081545".into(), + "19794881883035632088168728687250887611858393833912902865301935760311201163703".into(), + "12697056234954492550471667283339416001528754790033934829512770385731960713685".into(), + "18997188462081688420104936253788181208943789361069483606382840563484861418437".into(), + "13583525090048988441168973253797491422796978202781056749054729098883924623495".into(), + "19314654753949754551488116156913608959581776089474885903850748881648064831000".into(), + "15181859580303507964634708303997342165949140724201743603210372837522352052588".into(), + "1716462370658876925282526879979978257325560447381441363811430134855582914078".into(), + "8712634103130722740728459133380535370865755065924597276965974996780521350960".into(), + "9765286726648838325814219419835139491993130621932376855733750920135287306908".into(), + "19656947946994771960907781244127562602401277628142459052853670194718812445557".into(), + "19486837510931007596539998551154014974137134735704825357816939421451262185625".into(), + "21628438018144169767837932394788626973405404712219671990762156320628259145217".into(), + "3756950750264795710121609861402232041406397600142239597456522141772347776528".into(), + "17539873562638205978340064500452614593864489286304276949397837992418594034919".into(), + "31501567535731664761828271772320938243617735864979347171792246363428922702".into(), + "20352331732176385362929127748957145775981304217223745596870640808444167485233".into(), + "11226695452056490225554147366326688917799653401898260586559601210633144658136".into(), + "11279669284613566548129429232553360604072423151872075897074160621145515173607".into(), + "18292000567925132069216498991188575797602397842406377969706321176845302551280".into(), + "17394537715319668724138949078062966852069669620345676386188677668125466782838".into(), + "1758531732991926253426465239898570102024009352558916489602014827893071642025".into(), + "10665213752467842898072683993424618781459586563812993734884914172018996230127".into(), + "8324794334508227576619756931916312729743681044831168054976090310260143855664".into(), + "6775442988166121274859488523805155937302608291295720635827623408019663108145".into(), + "18295585939863028658565671525942083548149809167848494003118620027469243247255".into(), + "12098041580596728911973293152187391806099106269046962124832995425376587592750".into(), + "4179756701368801644640767039230959196272720402682759516084041748778250510583".into(), + "16793512481702225401381949294778305375341554886405623120613660547710034551886".into(), + "4048682207452656355407948755890074205160404299490847582459375283758637453365".into(), + "1909268909139397440485048370655601811806256012192941212480683118923705439669".into(), + "3957160465110711873678443805553768764193925226448354732024034759529771356073".into(), + "19248605214573594176390251265821595013690814611654024253657271349217548577373".into(), + "13868854211319335141393401333112190211632180475309484073606978199113030773403".into(), + "18603198177495186944736198329095228547417364118472010561530819889899131849965".into(), + "14961608796326843943228062129500114917120869339277918579779470648489101516583".into(), + "1868951368661473960034964936590962790214375788389753455318330338723533775732".into(), + "10905586386180037341368454391941851073795645267274288454955833628477189437397".into(), + "14954127679800529819463733123869358237687206843820983026443645382142721727317".into(), + "100097327222806530673180018653982439008319453432490738486899052539918122124".into(), + "12805546034879808469495394895280673159114129337420886322224562831326122327785".into(), + "14806980879080756440413940765209361196073245400503251970584575890653655885707".into(), + "11244087337738788153622356037614703414595488262581651455100475566242231927775".into(), + "18570222240753995928776550172488765352849447095675359684346868515783858763055".into(), + "8341186767195379582987673341587963265518616797937499770780867790468554256976".into(), + "16417432689254526830515766295056137071443518160358174458760640472715479776587".into(), + "16841713253514960386433216886097940412703740575768017997853214356595383383598".into(), + "14194887769957144867963380965988802472719946120213216012427494993012946567028".into(), + "11529543378722012166333054004374317420377342192547947476057235546295927556027".into(), + "6228288822288755015903485852969606389475962987032899088796179982251184963398".into(), + "7936701363023944576389877378554772776727492414245244625252565794409904100031".into(), + "15001498940712434912521276724772034272928752268882108797824422534795904619598".into(), + "8473716056033059546731002019964120133827043541589774095605930332153697155158".into(), + "13622250811616358253729853546023741257702106070523773564774458974873057917325".into(), + "365025468139420802765331768400644334625920629446545823780682161731304140845".into(), + "15188301566744221387195339021364877229016833910188161414607699674936046087814".into(), + "15485783106434451702559510093123052549288846753530302451778527951586674987922".into(), + "16738591797575541545215307991580378005049794467812864864257667302719418546088".into(), + "20925739023580534811148616300343740428924012857265956692833219002712597286911".into(), + "14889383841375932440118953385212403650477118619024418302909728838111026909639".into(), + "16907126208352189929083708812165010150781216847422378013750129578871272492354".into(), + "14806337329187045580963626107721698864963698880842972656340333617747451702084".into(), + "18421538430347165869364634373319132341572867349320823968976066555816366184444".into(), + "2434665826886017086044423893890240566257398976888986717506547657107156268130".into(), + "4107084864210868857776853415457685723648054900688044144581857773829680966286".into(), + "9975696437638488842544898875508262421180359453753428380941136617917797565651".into(), + "4271657746853939228219168189957401961257189278944783942462379137544482211389".into(), + "6197095881644784733476508213210037292053663537272178396466755799876037787454".into(), + "17213367299854554180584426411972899951945067477706586927352113350447914118348".into(), + "459213126097844409580498104618008133837044548373643874721876534610892189686".into(), + "11543212578280368772068695265607222577889093375221336525779600349460584250291".into(), + "6403298899310300874478654413460079479003477532791552465502800475489780616607".into(), + "9679879153779101961935393514698788441597495345714510688032433497970373876200".into(), + "13406313792037016685976311954559553063553089970578743589839464948184425020319".into(), + "18447061183141116334003130470774130153225311547501637266696790170481921881877".into(), + "8380490825353548292894683756629385493171585978110314915970595197861650821468".into(), + "17748778220767931278266669246252235981422317813726374848817938354712043425250".into(), + "6989443852838917282085440504075239616539844445576232297589557657622451430824".into(), + "7831657729066776489302369169030303662771324770284845700052063314128105065459".into(), + "3925294941359174220766529276974428294409483857727579665373968233976340302182".into(), + "11937917292063962829330707841648838486196775641195800632090436249316243167473".into(), + "5653419633240940595644007985875227652581749358539968324306529520430709397246".into(), + "4710671638294627998538116427765044043262338916893456876128991289569569324348".into(), + "71394869385825610742630406624465024289788832054180269745498390791493509963".into(), + "14738615985593994376179875742690333041302941042023229128451420345640432224466".into(), + "19352351801479363044593690690009745314348194116715421753626607893015433391861".into(), + "7828134540699329284695760683671979636145431779072416908240193138816504514982".into(), + "21767328407325385326066972839469738803639297445302836419167176746643393964398".into(), + "4363185783182535592354777041764733165968243966270266866378133278599205816107".into(), + "12861900092547198665663242905083918119082751367916515401033164113207531942792".into(), + "26199715195773805806030726371679979491296157888969354080702618784507347327".into(), + "11422842882875515086151723214834881230789142279246505109137919711737652087050".into(), + "19051762302341786952522177542948673906514656868374502102797859778503069987555".into(), + "15304419135175746829806105353267450572438302765436283477928256874309497427510".into(), + "15459263896362250252603436487314027681146892842462223021544635764438917687246".into(), + "8022722450368098005612162808615173120915189731064159685566153653273958491689".into(), + "9933514811147109956084664025371824141534792260321581906034445537159964992688".into(), + "13658822996647839872436185744737341749492205026854185105317636224609004876375".into(), + "14379624233209822191260559290771588358188218543089774948098229868966603288924".into(), + "14958992313261503520770373701642009455846990471215730792533739030356906680013".into(), + "3008264124109355458910031641850139041159853342729069847393839537381972950880".into(), + "19920038730043462715603813734444323019432755215636179923851277692610637123174".into(), + "17230906594400326225968287949357928337332875589057677202822764136401667097576".into(), + "3295851897757873055813703602696670024935942454512523388333117755329662151138".into(), + "18768678356443319065659286382395610390856779982946155969362775884999359596659".into(), + "12460586127165955570640740026112067337902936125125513451212701690418329829958".into(), + "11058259665604247832957658651506896706861484197660414347806317812760432481190".into(), + "4480738127714472007170345890289828985073762836671874831610164101698497828459".into(), + "20356672216023774813369271811683185179666462848912908217024611254147608738543".into(), + "14909200607508800723484308159953932832836734953149954532962622450689109093081".into(), + "9061041843468229886389838095513813426084716371809563990186852970500432288083".into(), + "13877210447577198749973239837374936528445414390167357430100753821987322195433".into(), + "2839635391670662085450211408709683689698694308308101424896263880864913513894".into(), + "446790459594546823193048947973948688374393610819268956164912891701746906791".into(), + "15513394297485200475424313247005293190116598898090270540649465364671255026149".into(), + "6430544300458423909335482367339522552006494240640365398487065900428380674587".into(), + "167620735842959596421416387102879695919480795342009064535989276983300514975".into(), + "7851629689216713462310956055612887013872895546158054432042649792702892211944".into(), + "6972603886636382162328373670318167574093559853635999968924151410796861460213".into(), + "19413944499150555955013296242054320685906330193520693124525832782341915610104".into(), + "12263527706889748735493918123122221596983902069097405802497958581677957096038".into(), + "12724987466750360537318099905062339029588038359925927031456282032606443592484".into(), + "9708897601683631082524518294366690107466919479336384437075715215088985624241".into(), + "5574496517333770677326807465934080003942242224011251056548046091202080747952".into(), + "4426233947499799157064891373345188337862104712594055992042177154581455374210".into(), + "3323616387049914826284182817146044206680302487334156840960948301224727078983".into(), + "13004254604656434940963009501076991894817629784556574456240824509876544606284".into(), + "7301157702835275823143922645275544869236677259284614641634323459896004519527".into(), + "18085948818621221598774302236513376768745135801526414290158313790799455691975".into(), + "15187850571325688566534378765892972581891242830408971491252049503437712864152".into(), + "1246313402425855943486273052355580345666640430821848725745627960892819218740".into(), + "8647394846748193532697036206738902082890058434940110730560542269988727795912".into(), + "10064655809228901737773717896104022245084200167358447082271876992404069509689".into(), + "11849936299337845059630533008021049942767799074160683892846518986534992566533".into(), + "17222503330227123082665055850287821041983218187459530901865330120583332963245".into(), + "13182290359799776528759669559625168410983932493170206108456280901111876076502".into(), + "16922308846046171347608544335458702314243928203269931669473214292617484140503".into(), + "7558805163644194610710740916145749972491144028571940176012011742150030513578".into(), + "19569775351485108268007779638227959360722563519500120339858146797114197471682".into(), + "11059423706111256170971735544260170713544645580527143674901672982061741530101".into(), + "14932750633257527497920398771292456029356930315768552640257901217840963910603".into(), + "3437896142924792262479946438539259423504563537021031548773393821349707014872".into(), + "14782805101152549038431586973278748014002447400847124060642259960939628565044".into(), + "19682001999640336579725059393603666122104885151774112167088565361216155864092".into(), + "4233558995266469322360257198354042357488781625226686110373130567271844168385".into(), + "4047511689246902987779521780711143086427724115291088073014684434358317932158".into(), + "13856430748589477860039479213633534029749590309123560510004500567115861827367".into(), + "5679747763871928801801426015291106236767446876847749477902392536633742028903".into(), + "2480123750122829773963967496725275864472046134363280592972001159541781284867".into(), + "3193376214066985989527250356837102042223952356102254301113166141443841024511".into(), + "8062756153672663466999843325461563174537500405375937887043564985230465866772".into(), + "20934273497132536703003386629581299436206618333301538059074736379428757199398".into(), + "3964142000339408965627671968349646710437601305543271274636785761452376263382".into(), + "17659047756049778260030257809618419752271933594271518827815853540916989127582".into(), + "3930343349615664221386794926953117177573248229077177174315534154624751347076".into(), + "2895341264817391965072938270159088942930942041694844772295944189135688863196".into(), + "20818043348799096204572140288297522915577162783223193328931006646915252532953".into(), + "2323581855503968054101786162741712103392782241966098143187644164748998373979".into(), + "21506950445835751487883729097676565482258136522526260378635240810473639421131".into(), + "15652663761688777314836638604018575636259220826467294056546736904699739444415".into(), + "7055942973426557227578535789272026034619140718369524980726263182702440802766".into(), + "4949575918974841751734514119081067078445927926837555007268000255590820633720".into(), + "1398399621038450203083960309132739969264364773648996881599178794373813964423".into(), + "15732506799197618296258053128654220838812930643001991164136863850944185860296".into(), + "7794835596508268056707656540537636605902268233816181487738953353424162592877".into(), + "19334780957038992630028385332334147629399375592719839985802296503565556738324".into(), + "21635034093671076768880953665214862813027411650977124864184274112296716574632".into(), + "10688534301651495143056852052149424865722511689194196231759927113549978532269".into(), + "976280778878149721615236526300424224199708100054560296745736876703945737879".into(), + "1661236337347680899528966953581439111854379099437324147973193626090712244045".into(), + "376684649999303144466214973033408609742031466036082108742020038605665915381".into(), + "4455377013377822379614998251281676782235647079572422444530523356359416320717".into(), + "19050227778954804843017265242388770579299436899390433258601701002663268793950".into(), + "20176738847900667540339144241305449384029623393968373234965844794399275414945".into(), + "6563336600400724498009526486444642892816244088883489205001024055715188696046".into(), + "1774742053125969896553337470644410955775448576794083561882186570750312466523".into(), + "4720344938456070076793371724100707050168913253766155392188169516296487837219".into(), + "12386484928350266729292976320659720907387619673269670145583838638028478942370".into(), + "16511156064543308562970376025636430728255148453048880933972883964686852788569".into(), + "20161060548546289109867983393573712303980348945862267966490524939511877278515".into(), + "15745678416495620522776570951840736496696200297848371457414809276602866061937".into(), + "4371269787269411959190919691547618186773560770535442840395153562222650962400".into(), + "9183286542027774648383851710226269089477011745654659545146528858483988667073".into(), + "6831030131135399650979313302695351819580359371445002328037898607795789751628".into(), + "13808316624446561665670975675845412570522644424965154154503596711464812129094".into(), + "16573850887526809906244420164574513771138034770054149931125695105829690192839".into(), + "6536774143914522904090235450413350153861560318210355846585220439397199352760".into(), + "7433401028165144185448409996814186253264882020047922839648723974949988749262".into(), + "12023679188187515805015064799648457297092902074185758947196154639017034370941".into(), + "11526239531876823273618354625960431640864995247037473959040869772406787371745".into(), + "9965606134640358953439667616539308986324141554480312185321082780157059913160".into(), + "3745993460581557715489390110730324720335274000085036229679909083358071037494".into(), + "9383224303353982312273937680493563948228572509816601479265308820357851069284".into(), + "1487827268845002359294235769079867448539095616058866371843794273898817464325".into(), + "12752425410737476015270360130920506733622111261208379938979967899453342006543".into(), + "8485480321269023262044616550897625372838041041299338618725089063696829309383".into(), + "12576415359894007789769349543504370901641305511107563487651205088993508989632".into(), + "21051289172896982928450490300579871201387152257916628019163266526462496896736".into(), + "15867382033370376377605746630898769390447074387330276410404020451043720395429".into(), + "19175636645506837972748760370147539503389959528500852183571848133179892449157".into(), + "18562815464677471696203191443459870050160752363485379734113024945295929018010".into(), + "10670914301723557472895334516073215554660627986142935452136687134270343499283".into(), + "15711527071984806338686904487656233279759984782956970430068615997429454331060".into(), + "8708497335606838864375996732302168404094513852034309829472484268061328157580".into(), + "4188819471869628756655847772489988754984005896205326092058226221066145721122".into(), + "6512127543122355053584585699891817324589817660150620823656474181624359941201".into(), + "485283184789889690494624068272585257719678055879291431306025716514618916201".into(), + "12469998319178098719636931437231251055837496888416524814745527432538031712785".into(), + "1296020106178851400649855820617531046666668928599018437234450345456859444266".into(), + "10011446468770184926016968173857732818096269728811257796162165899021103326404".into(), + "21669153150071610384945525175896288044525929994331131233225247540220566590451".into(), + "20224127309865911841696813826362911446819981503618978937382553360215894705096".into(), + "11995994988481623386912855165099904216709109375061333173068533008232786187218".into(), + "9410915142880940758336239494539529470909530692135279575732428340046078892946".into(), + "540343796196110456420764600417592095932817965626261174892651237721778500283".into(), + "13684306082150121243464309606855998092353075421168995445316517423697818489078".into(), + "20162825761581699251737493230139692067729837621407892263730870777826880180708".into(), + "5352686262532923758786328475456237662420216644156835779261605606151509572690".into(), + "14736042040902670309572407869335525984156459843045648740121852458089457172710".into(), + "15139196852186360494919007897486360521240977399849539996614669434222882428974".into(), + "15545525411887550591483485994597179301368938856348279539852576272926020931130".into(), + "11742635236612776978185392287408975566560283293946133146948657681925156152641".into(), + "15933911044011218543142885674144914931062698638612248403425430247045038884929".into(), + "11224528638969252327238392070525091272297579382723799722332333037066879094771".into(), + "10619901685741876140226157641208788563882472537632692937793306998778377039845".into(), + "735284898318336599638540292516453331642892791568249331436388578312138627149".into(), + "12167882615164420400306427908818494367178873976878804005748498778042330871045".into(), + "963476496798286039732969224933550470500612316061831761002103352026702296408".into(), + "10589214229258115348033093903975219788029199245856576715398082842145004049938".into(), + "21557152519616203688970824263287584036198859167308821872261472006356046725775".into(), + "573869759526051245236872936984426432239686428333700586079723439581218874054".into(), + "9487380087795664760223154980070289042545005448274508346993861027000032902237".into(), + "15336219129184315874932697305233742095099896250996970622669888209373427981768".into(), + "18404646237880969912286263998117686518695361809473583072241508209656556652987".into(), + "11799938531163685220849832019747758716514224719376128562353011229084757783628".into(), + "16344281677414470708413218213072185241641469290932113189992078158350700334823".into(), + "13411067297619308137174262243170594092291209705703155674514006442224279302094".into(), + "347096376483719141995221552155578565668868956459941108952060114777596679497".into(), + "21711034316629345478518202673372708169627878437940325626238942704003376069559".into(), + "18882050099612599842940192045274010067786778900079004241849918566254608017530".into(), + "5764257238170917062706360501552002326820365975520365982521494644422134554879".into(), + "15701229555594307124251301047127236083745198814231032790649389796695363681317".into(), + "19380342180431843316217793484208165905833096992510614135124089140124740832734".into(), + "5309407894979098314097225178362235237148427064335413702368413584568729847810".into(), + "19819307748431076945425543888401840319829139172119377623119035071741874422686".into(), + "20514761050842041997240240118048816505140382250318444479066954659053422537618".into(), + "1344402086478653057873064952452423683387555289301580402150577440089615404706".into(), + "16697255135945674012509919105064086681485448643989533271078867487484115301326".into(), + "4621832835239804428359762201398927678345509964776233571546153039277467113103".into(), + "3806403786653298341904285490244574263470519864399252904304315511448068846106".into(), + "19829858960657679259830225387994355131769725454664502533894857362311545270016".into(), + "14331370957097049770140547885384829422939088321033246508644491038102571773100".into(), + "17602213870111183187455849162801944225604239824059691632957781727843794239813".into(), + "13431951289066965496411295925849182623784324208488725900281691974104907065599".into(), + "16717461480909222952301202387848079129664321734166452456973177865963036285419".into(), + "5335304795187086185403562863303763030150108130150704291401586263249269328606".into(), + "12084224077193103457678419187386990486069302392148214780322095375978824542075".into(), + "16562074867094659170642419054272949287052074902139533680078784684856883275478".into(), + "5593185214695460463728593774938986800789091570195563391333689785627694694435".into(), + "20794081973744988874029976307272029218401016626938878812907621515041534590160".into(), + "15290613354786940528909115982493729676786892068577902693057827852601851859953".into(), + "12549551509495378488198674258192692896591847320322918116198744545244343099813".into(), + "16172760618549958023639071988927160267123900859890153325976796899698706870529".into(), + "21355970775695686114097146789571025790146162225961276367865670606869296144640".into(), + "6758388446832091660422761046124378827605916325205674642673860154638138778023".into(), + "14106404069959859167123546389809591376301394503370836987998148793984105405167".into(), + "9246221316655556670219438263600949328330249756179668376432941216174575243170".into(), + "17439153852461693097676073072037755599321185173017314957734642307684740219597".into(), + "8081777382362242695298005068719699951526876053236775057946263871745968444089".into(), + "5955062815471620144467538738332476050500085920379711183343040972831100802372".into(), + "7135549743533376618652729621156592228493633259664287822661943632128030927327".into(), + "6448789306286809743113676641229641365524354356867853498363388856986312333060".into(), + "3836133206442752775823759632522089911744536837043185745585363415112795166600".into(), + "8306967267195429229901513132397768469414281874455321140328179401973148164445".into(), + "14279081291461540920232629360665447603436685365844514491245275283801357455033".into(), + "18801535274021869273717064373621101183756544015062966551566128881569524508060".into(), + "1290309525505162730469471418279301124342110934722264762505649909318202025429".into(), + "3684482296351796270499952631629720483486993312250657634474107248508746062899".into(), + "13174517099719760880623841117042663715711430443825712743356469456554740491052".into(), + "12367323038661460523248093127080567074978912019587769246568174104058982359108".into(), + "5328098381011440170293483375144450438184897394389341853376147620924621832373".into(), + "12027556664769952184858265393953265399418219730267455116938121684665358512140".into(), + "5729732888348277625941886883620484887709258694699060380279441089816970092500".into(), + "4021085134780294168590418396176917282024838594185209785357282466818023080991".into(), + "17212964584402772629081731052343755101208077509065176631705574779383125014657".into(), + "20917432005010641713392920477403385909781374146458276502002720501978360592707".into(), + "15295563898035514774486916635776497660635050791955975586488426545424778559212".into(), + "1359821178683766445655942489230783986383452848368510940932965034076334706033".into(), + "8110406013405532283463947127567634123149268608919736912581466809002624991926".into(), + "827196241038734359771319428856617658913932049877272548875698342240161957065".into(), + "3791462512735380195191512407306530846222290938359064542531485381855595890578".into(), + "11406017533405938491480863229977030648871769070115386849200353035840595881771".into(), + "520670302347263967262738466276891414776779776742458165836379179429124641366".into(), + "2448606803226047006259184225386977543879941109635656602752172963011710994490".into(), + "7191227529515483786577983572625758056228282372415415309332838080598142611412".into(), + "3307446417204024568218448979093609311776086880103323372856728466529459088211".into(), + "3813554883592508538818724202670630447381185344079357801330890767369937578758".into(), + "5250709431012867357277423372041580897716232143432581463056922412233378167706".into(), + "1091692079685509315891403580607856616782786079784896146193867325719999481531".into(), + "4469622327160639333299363654867070539113198566013450831894562184457403911597".into(), + "17323310137022557438196735285237348172608407585356043526322906169573029382454".into(), + "16687301461250715912904355201855034952586622338354151885963080312175524252322".into(), + "4918535113766539148632870060160447192025270821762409737556656897638575667317".into(), + "11072593994523457387389747805751581908149754361901108358077132406445033545004".into(), + "3195233728052289389067022539306995714028010328554486938319749354377057516714".into(), + "20817686894891259203267112342163112292566908705837357461445233267117792429649".into(), + "17690940439879640319699865226478735545090209049084370958835445823603146902951".into(), + "21690628807014351230241926358993998569388931315165663025074689739408667261702".into(), + "20459556268654252373755265495950372631044421643418312123931432417755203158692".into(), + "2615012003869030558909061586486157199297754407476408867239954327608829725401".into(), + "10416424720480840919038397124331362407722217483977166150169359209212504823727".into(), + "21194909967788113390574471899389319930586662664636393856442773352403088570772".into(), + "21522774899908415003330605964114671001565200771087582504208866633533540017562".into(), + "5506611081865651016465481008237511873335242056816344474949867549242155650988".into(), + "9546865422597752417260521115459137295056544614907437375816616393702205361356".into(), + "21830596616035544007289279751950703469521180388685936491933709578982542912017".into(), + "749611610802485176586202226352686769865522163389912514564999424571463284657".into(), + "6755555316754154503813947597597343604907450943684878399336695170747270471709".into(), + "12170672505455181293754012516713072944675179516356563083524964881029055628483".into(), + "6707337549725648754250613641063009246814657413626673293152655855698987287596".into(), + "10796498904992920132771232449170737749742455370471689413932089087977711623836".into(), + "8377594495858973573128154823903039902710916666748285670520960751078778241055".into(), + "3984683583649079079845275538974168947709880082512288559917193166572776389589".into(), + "3342097912826432514400613460588054784328673911636756618761620185294261653370".into(), + "16454683529696451672086473280821594992928717172228276559555553609303793416523".into(), + "18807899744562842658522302889072060930222870278511178161869216361165074271071".into(), + "15733467702619867688911165419464811721151813607792971238595067966297846247623".into(), + "18142314765654743253383559257284245535624730148238939832089390410939642651454".into(), + "11195224772751371350570884976842181779831961285896963666553011681145523971069".into(), + "439244367736881667144534860370666344402105076566699968755250684892603338082".into(), + "10704735720989025726743300184412677969903347430627186939154207946469729017738".into(), + "18578876165452990292495920166540986834727225658030437088905164910953626259691".into(), + "8557967377050572410782106944750290348752651270453684627569664713996263383548".into(), + "8761045577121588487744533431678957572685193486431511842082771483510204390408".into(), + "21449005117895748002614414579158497856671036353929407470614229969798846654314".into(), + "8742763699897193075490808534988551410985191733407596422193467672281063172120".into(), + "21640560187468057824325948244870722744182743184907610616373323268039154063822".into(), + "20504827051323136179653341596769205909304116951166709098994304340550034816775".into(), + "16172525553351370894318080522373536864524825253487998830365706514360525527841".into(), + "3591685457927038924948528662301423116917211093679974227116322357454330018554".into(), + "6023337410706762262272160833625601604408129024083079173527188974815598816732".into(), + "18314718271070348604361238020618683829686900661152269809595508349472847942062".into(), + "7836153641990744469550400020392942083885339413553995305082889896550143678297".into(), + "17661458352757891434962464787581216134089171454741034624471153568868635718017".into(), + "4122036760441574620764812242429576792598439550172741100479310420694208068906".into(), + "20862235339890611718235387895311549206105487705288574136561783190763219344570".into(), + "2850551701806552645458448511649317843914427882049290827414738885733429831896".into(), + "11247005361958789340696615426808536553465247486357927152826112838641725799209".into(), + "595377141566787974548956333113514797724094819448399289547808025689225222483".into(), + "11650535249856393960142913145219442364845834591244773725262296147168383099202".into(), + "20317269079309969728497469029861008772266846936951222269809633626856816105008".into(), + "18373353028723033673212698815557995243796350917201491012576826029280066160567".into(), + "7414281571887634589036890397094381209738607746338326059409587963803891852201".into(), + "18038763738650749116346354096722212724532913209902202281075842225944189538168".into(), + "1770786615956839194004148114013826935077292967352199469327501774219607260953".into(), + "7488031997643483244135856650059587771856081877857609929648968340004445042854".into(), + "8828420155416920637244885570572746512651260806059857940333819295380848667606".into(), + "10713758486859145329962611447140189455100648474003892294431344231535672892839".into(), + "19425964738010714594065721309197326935753724219070408633462182732050226443166".into(), + "2074939531697630696938698501714595125993887544292881866150005180791766942998".into(), + "12047170609336587447894029543834769407220306562179617939497216596360229783300".into(), + "1264730939976742978058162525644425945405879597356760675420608373469615068174".into(), + "16770820387947475117163540876143499701759349964098540413422664664028819497801".into(), + "16280068293625117002805552395286584356701604835446863786447466051605766622630".into(), + "5829399622833188992426267462479081456743106260845929463922917463933701539334".into(), + "10637584899633814574790036401685386209425675304700886428980902763634911791837".into(), + "9291782595793252587833322197337655894886134207930378095811922971339860144588".into(), + "5951026341599354622946777675133435736866407426534732764305509667517377130324".into(), + "4403580174404926097288523091529640188805282235669930524399146786861723458014".into(), + "5003639966922553172252470768563093188889050306376650665702227782913343083764".into(), + "6558197410800499965347594156873334155413890035105551024236266846011615264982".into(), + "2475377503098365612622742300317283313185534384082772531168342518799898155594".into(), + "13480086440294256226712366993542304396509909765502683907895525126405850150623".into(), + "6847184584651376043889746562956674365958636593745423518578010394007380039425".into(), + "834503921046130599339994952233856873876410737494970024102211605603428829597".into(), + "3623466614124962325494185109684730979089103423795285554788413052641061197022".into(), + ], + vec![ + "9296474750911444465025945061626611450573261102544117494435956219223872045013".into(), + "5146180402642819236271333772846779841277266622562021651959982317806096554632".into(), + "20454733758478774733902661862471466785458678414673417084919753466149262477949".into(), + "16939720779646471039361700224425387363963401457766730187304254365590145685643".into(), + "16508379613071589632225608856524646606497744838305343752126125339777944056893".into(), + "20867355466989514329400908378893992018169546886162092188544040547127653338003".into(), + "9562028189723224670918372808954372258890790835333923497839294908971703629044".into(), + "9227509288281899178360094205033853851646604997619773877005168383222677033411".into(), + "8509781355418257783679010460137433931646066572707591432255985024195888527301".into(), + "16352397893201944000231474199631274335610857147594743875684031732171609392779".into(), + "7168331327842901194795012403324475179802342440545189729818235304752196473490".into(), + "6805437858781519809721294519096505646809998863249026857458359222740400480093".into(), + "23666102285914319661214067066621928648019777016465806627627356483470384578".into(), + "4937167589168346771422573562192746031838523475116047879790134471501476035621".into(), + "1346691375815699628539641638232212515770545677194558930872887124097351576509".into(), + "40983635009581820916284370715993074471281691288657861297464948812277259151".into(), + "9682480346612414028382424944197316598030638601235308149341398931369789543950".into(), + "20156146228109066705562831124708208402478981918458654302206305397911512087980".into(), + "20244794515708180670563002449620617650597057411850557162891558214158122324521".into(), + "5973431503164625202944413714022034366590666955289560873103517106326649624937".into(), + "19003031440781649161524015602474761488168160816940423515211864494962159501828".into(), + "20712605099727052887180337155696823289955789009114447705180663042338119444677".into(), + "15751263674652110912724722781530878809312788917232502379399572977178318108528".into(), + "9759440007076373606849230657528653877996372541705166313887529308971932175875".into(), + "15753864581145946158736383142711883662973744817981456049085179548487345401534".into(), + "18516736976984654236711834595195133682620758902795528251669085852778367276210".into(), + "12505762035583653640932505661921159087471940695093437071026827168265805916842".into(), + "17973475260212324625956540069233265735155253700466680406635575121618318752819".into(), + "16893238145591341977099515648275086133943062918579997543738633084157810726656".into(), + "9669071880586595648023284909288486812190820353718106940661900367937844527179".into(), + "9349088318894477564190440283477178116641834248883927901328258581942583077569".into(), + "12186865849936919100247208155624431297953689379595363409626510899600302936538".into(), + "14191429059221279746000430832297028082244506830783856999765158986408198377932".into(), + "180235525898780969912044806879591313131227292826139844018949863600387171682".into(), + "10889092107911649889040150092427122550710450427668385404292705974881857473203".into(), + "20693842198933957663269842304864226322478103663024796211607837700737760050747".into(), + "19741631810531886646761927581354810838182866370317964412790883870153217103250".into(), + "20514964832794580053019916730141871673725635044918093569735954402314586611172".into(), + "1563081830952767432323468775999289294801679977848773475307184358241727189864".into(), + "6541733180459084604825913391805383670644609126706479690307729028795470985900".into(), + "14338950551652503213249338133071776308134322059752479422120853035176120166434".into(), + "9445962403225070324948180958168278025259517118659455884585920056598191781467".into(), + "17673503748974627902616087621827013509215108587044719385816919777868083778907".into(), + "6529067669277048252246949093210874205656745103861744893697681349157159395382".into(), + "18670351688014831561846153455584274335550086431574922181801742319576494275746".into(), + "16087198812208315804697003049026004024678636407919712012630915190666161641979".into(), + "5409244980912243970485734262038475969739470891565229610070736907935180643717".into(), + "68998709875023468479905615300134369445692715997447916337587330860748485243".into(), + "20797106660136416987264043187693969873379408007710762405000375236313842719688".into(), + "5277839105793767851333321356647781091931078040812937205152067116052352239761".into(), + "19813316014421099365974197500618281012606383357022846408656988057396247542705".into(), + "14651854807736789020692970167659260203349703129364255901280500438993569147591".into(), + "1953950121645869390891017290629473806985703435547819703015178261443018199466".into(), + "16966986407661837074189113052135209905137771226341312735335483526986039302676".into(), + "12196911666087491240035929989808617508758866763772938948559650992149525851273".into(), + "6748262588722620715981149705241221174138004527637005241186672382866432164292".into(), + "12351853389756326378242661122644604232955500481743248231924728979085806709527".into(), + "2565243335980149108987604508873860765952308183381198225102084152967125103373".into(), + "20241307878322245036836862464964995816180104231642798486998859049422772672116".into(), + "6173912854438429518808824631217531662651777757438489861961683800252069642271".into(), + "20373949892834246498138105881327221987245360841655169052122063615887220321147".into(), + "13269719501466581754706990055797732063851220874637620573076979035861249715318".into(), + "1578162066274870756439515512829351279665654675138074205960935197210851459902".into(), + "15699466465478499203883101572116714566818501168342041974225130392829754730992".into(), + "11504917391580472762574218883359632564098492996052059988389474392975779673931".into(), + "2471534962918953214626354556158558932844702538697900698209059019370494276871".into(), + "4461270721380837522838335428169036184773145514356202591842435276161887233701".into(), + "17625308215993897091838888595388246859096597033404108848402595233466786865507".into(), + "18815388073919190191562579690732647812407368396854637581401819367329270645091".into(), + "18268550843300936597164339843045347137811481798420250960800057984676328312298".into(), + "7226998309855583456700105258532413039112576746166962016437941756495852856370".into(), + "8072980663193342911861442510877605529321700341477138509973424538392015970628".into(), + "15269265747158306563417251524571764365372430081923021726955952481038826679058".into(), + "19910037920320033458941731683391812599895690039363377502140416867160565960964".into(), + "15486045363368901308969563040163325835584354680260407824152890548047474173382".into(), + "16416166568440710996186902352749667794644450153896415669656858302452107263361".into(), + "16719876352169334284151333613847626557780385036803676145001883066946186048592".into(), + "21250242709366561537300491285945444276614074599553674495821079421382627519383".into(), + "18996863118748910427791883574992010505069161233259708625680518449974859990560".into(), + "17413984989888387572882804568722637770904232773299911698227583956354366272051".into(), + "21597529235679499224130811468867862981087206894945208547265972184954721846149".into(), + "17271950603399986802591192304636434671723720851969778588610595038101316019146".into(), + "6798909530322911571363098835602980477334813891267210720952461019098407212259".into(), + "15221754151466299785203667902143040752208351220550667002070154445706666401776".into(), + "9969565572595685753856723561942014758757583006296150732797865871416014191612".into(), + "213937728610177048236193694412457038661914578783162813372436128728852762482".into(), + "15615646173777239619093550417838782753019556951599702936217300723011405507797".into(), + "7718045694050546136660011535056217361530495049313202420611728185159583583910".into(), + "6681562236645975908164269312121030538373320810986198141132181047805166475529".into(), + "15406190007218505433124772738477886033924738791369244099805663445061334497251".into(), + "12748925302440648174656014197639220978675653093966691038521528280766659388490".into(), + "18792405480956246176786158899722364426273579869771820318487342148987615503627".into(), + "21034833205180901867479986245412384531166379419487221053266758595893395608147".into(), + "14993530305792055254503672111497655292586320227932876629369830030730566504041".into(), + "9717186869050569007497148725820161576480205291215534893842038294586234260310".into(), + "8846581556095542298416062056868302371695747263219967935160771313384250848220".into(), + "5719380810982615524673837631402222949910527678512468701059039624789965209167".into(), + "5973398204091658428913223841081738015584068771535532858878785900515770169284".into(), + "390665721029373984714826019392824960041229454845430836402699997231854884676".into(), + "16884099579377843869279635395084651641533249525144181522512029929860550566335".into(), + "16716976662979247989662403288430439465273241484103184167374388250697744962395".into(), + "1289338592939287230201589454150014946433268062090374613616251000166621302313".into(), + "9317927644121595578754625372882467923006787730797841509740916536436754202554".into(), + "9792346806272266839932159619375452292987210695578600955864989901266954094960".into(), + "9296083977157792888817756474494818556822681079104304334773148774182988756476".into(), + "10323608200914404656342379674401080164220099943588034731349775904877409752904".into(), + "5665882510779783554356611639475035143817214951779938939820232711292028607543".into(), + "312967658452369621468725894369965307895839161467077935727514890821777198625".into(), + "20664282841391311519597819314930060269576136022608505709992590417893434920589".into(), + "5323350315725158992622594066490821193659726275127554898433661812401256218478".into(), + "16039351301369040724720782620886565139379163954982512295794534252600896786349".into(), + "13820693026747418768344065399393852077420709727979822490883114289330249839237".into(), + "13797176144228246839971555725332102191019905588045129521304130064480669479604".into(), + "20861818286081952786814445160300163171950644955642439845329412070095760317081".into(), + "6607366884537402504576829976217056754321721309944806800456553075345742381854".into(), + "16676075047056109258708765855713030298752565264610707737757882684833916800885".into(), + "2596872657894796835537858907147863451585796495210087845405329734139833282637".into(), + "1129905770296794876904745792895109768657958745069004656310552795892948378584".into(), + "3524768344101236852745306230236516385903870540974561871381448404874187920054".into(), + "990706121628678949689364737717640674078492689689565457695471004362978678643".into(), + "12808278123975447545284898374625872643451056414444411676912801184092083815084".into(), + "6358161990036632946294642058872786712712883111056083194592676110308231876108".into(), + "2793873016535929696420652383221830923887713214117784093571041985641508474546".into(), + "17305928010614439499134847438771424617226147912488714899621343734884653872709".into(), + "16284502064199759145361743401051151176267685561605421586130298595070638086967".into(), + "16669378724978577155412758169912049188272082311653969294949527064226374258950".into(), + "11861408925173541719238679231659983919027526756130925399544116580983984008351".into(), + "20319862771317637561778652893808792643716731292949307597222212602977661607952".into(), + "9965217723779805596104842963458526859730058546126612755023280528059812781678".into(), + "2231229143040861060383699098560329742848948229177358362095200642518040448582".into(), + "13286704465412914480712873676020914803973411257385854321890647189069199504571".into(), + "10047635595882320884409798135378777636448641999630707194378323292740008960417".into(), + "17087336000400458291414525340830964856800142468906625956701637877370905283650".into(), + "13535594575282729223473509709844990145664370669908108705760692736046179136608".into(), + "18619202345418036145538258744454149503028380077256559127312056952610301046510".into(), + "17607804586369163727645967551963527279187781470886431384954240254070602283137".into(), + "2633042556450301220705608140363996319867584411611510879359782399294296770849".into(), + "21731768638998447189300033743436435314153248129574741734083353285598244080117".into(), + "17469131737454037935821691482173830563221091395109883834897114256467877636669".into(), + "7750519651616125729362402583651846123740462681349952291880562002377909054050".into(), + "9447081032000164239615272903502339392629921187510175841323135270498903409976".into(), + "21517411732531464267453023393592251500842566572268409532461770072252092958721".into(), + "7876501141846244962862794902521772277174190799788868978106856756141641727971".into(), + "112943796512817751182977688559740204117088697116147815915631198815244286561".into(), + "21045605911504121416853713285261007893735116973950420994950288232723845218749".into(), + "1753558144636632850096274509984476357404441186238175375676398948118672743832".into(), + "15932527070583999576056453444162361416070384829011926782656187054064561647022".into(), + "10670345925352776542283780467580832901477001176626927666485810080031890388634".into(), + "7579612604695836258451031149594628860115515363868399672164640908835805063913".into(), + "16678286807861813265032290142015890821080993558199809221760573633471452514219".into(), + "15414078726286200869397977002633905619706559151321201886825291326757189172060".into(), + "3948850363929702397926233515670425547474212215810847662989169477642088807390".into(), + "16676527723244281555583733536047809454744760551541688199219131455695588765207".into(), + "14087355065973222250918209290719451048172913300838805685888196087459492418503".into(), + "8104074033209440421654249234822181338065658024406724406758136674393332631833".into(), + "5187207728881418438225188748028865692950088102865770071392557429006545519499".into(), + "10194509841193628413711183989804813746356025348148221750035788743075560349210".into(), + "10011332996303549537072329954668024063758462809797094286213034989313201039795".into(), + "16531650268844669358279021502930777186663937554425443224519161588787088219270".into(), + "4198803426144178314296362723890875454161257236407666607766866723687553739691".into(), + "4796265297638646381399181426481493643799772847333437062194249373663993441511".into(), + "5547609802231005143723955844984958632609857658378828698100141356246667308579".into(), + "9238958429509576319892727665041354560043563806656332888234436819654598656915".into(), + "12517630133819614217261779870726755029108748195491643235440705119352058557308".into(), + "10368014234449326131235994433740216752312468149182650354075154587397704894585".into(), + "487465315883418182347663117280302929984519597673016955398438666336416216955".into(), + "11185034244837678424376295011043645561088905841730131726788873681031338174455".into(), + "7421225151165727634904994585688522807152162423870550118585658136113463984936".into(), + "364806019968570676099566454292016788351689833960119434834370974516536828706".into(), + "19587762041779243275917908822520467733740516572707471226884058208850887204874".into(), + "17699580368555821903526305272862177569486615328343491839828472034082952960813".into(), + "15739138573269259325517597876326746482937619260543240058947651429318524320399".into(), + "17749815771695585990262895269645295514834869871993754550237121995617786628911".into(), + "6001281843453332722738415535953496883454214144943230146026585272790933236244".into(), + "12226248842279453247400486197034488006724059955941344491855600110495696624034".into(), + "6471818050024319863197241387509891745688912297463370650154695351698691437030".into(), + "17643944916136214612858153024594861226464689685432825152621939791565281052364".into(), + "21110507073644152221308742247603861828087429176260377204494317652281698346306".into(), + "5767681379026755049166284789191192306599058359643996830685583640018326835809".into(), + "20268516002780303771030074402103352053711651542638490897887552979541377898692".into(), + "12422024375924323315232423143442023050941283872688585715847426230444960244284".into(), + "20694378016149655833849108581703458975143197158248183642690852771732423212333".into(), + "3589864715711417186674798636588069952164879749159939425060725590030374075726".into(), + "12536189236026623553502052145542282538682809966098904986394409008189339523570".into(), + "2807680329090948893979159370041090163078264208853025987363628558807228757758".into(), + "11134364423913486029994525311335222090667061028008118303028273833566959977738".into(), + "17354950677485256631166408496835881692979370134492552889236570818914175381226".into(), + "9148772489395197941519683034415731069796332771510468178434252005940900987586".into(), + "11051248410353114396778661421385387052304377861539041695630551535039137025728".into(), + "18136540266852326106685174083549032333515525540247442551401150755180487725743".into(), + "3985310545130175559232554478662499499843818415947765870117105273186277940709".into(), + "4596525943567680471287409788454878169502290977680316722474714256040545023213".into(), + "21372340401058762695712005390221995267277180470546612925471076506446695116009".into(), + "14075257312640062020412482491643121652025454294874360644194100265009640244905".into(), + "16944503245314408069496787236107780704688926045771652862339257733069860638621".into(), + "6654085263135548167634225627185378533095825854533316762555824971264586187651".into(), + "14273015308464032668640083373269579252065688901233915918278293118708191497972".into(), + "11764478817260526126925152191588101018315006712336605965222952598280968869260".into(), + "18155813367404719470487615677791656111973118264369674303621800593085173024524".into(), + "10850039649283761393985741586943649861203783935137056310000609328911733104269".into(), + "14974871370837446170027589325430324426484756076474120128444445200816418916077".into(), + "11027268992558661543645635853060250199670617750348905948352231500166030736735".into(), + "7737561462151958609897925089803246494900406501278116203687665387696545068888".into(), + "8536397585289585667363111427598616854125268561779225310999422087929379230845".into(), + "20520653588911738970276129298340919743898256960050723343166349159335019506767".into(), + "15573689478722985997422248150576705853901581704550704092699815959211941071233".into(), + "14024637413191434115623282477558112970843649259330326803453616105375507215588".into(), + "21353180477994165001466351039795610246389619571796579650797005446278678838787".into(), + "10956420585038523642301267761059211443146357574323727078003385100699325814188".into(), + "17634836995596798350536418601573649005517540866687768902898663209263471034753".into(), + "9073831830223507407321206741538426673038288500939999264368323867070102600399".into(), + "8100061233881358546546217363648928017848251671613674228091904606518338839516".into(), + "14292884686436880002999028765738013651173249817687065086024117836878136072418".into(), + "17843666610704256421074257091600372846189412242040711501888312601205476877610".into(), + "7418496999733433074316279404200997784331208365418356982886646484553701153757".into(), + "21292338796777812243292835631771215001169911232666037289555901315952974221584".into(), + "21287440813442297789932443224883752220885368198719782335487617798315717278443".into(), + "12316285862536720021079405901921917335504024672055930573734336005894401174699".into(), + "3187192338760815459509556016254324490807541427929108750144803145054500486833".into(), + "6482850190067117044880439000213252508905126459059393355671593783930481002065".into(), + "8071947666795842917503687890154152458243669781913953337362618950188906690910".into(), + "21450848111897538423936810899713904909897296879883982984541189476327391575319".into(), + "10587193221429670115264727936502940314613396351335733485096445152378181351510".into(), + "3385684482890050363671196710999686076700864071377274740326856556910758812375".into(), + "15982506074704654515559890904595916307715088473006256644287900474899011899187".into(), + "11051707205631537861946095917392475702553421314274237258592689190929368154151".into(), + "9251268179161501251470629870144065342881530535654006637177099334228872748754".into(), + "15384401424299601275458808814042231311961518546060835375991090955541715971658".into(), + "15147611899621647169760029554894128666862161642594655274320073446753024250644".into(), + "10615109639224170479859701634647953864795396211020891058926612120687182210257".into(), + "14404478161927699835476596901422616937403414811611272602635749875651680921447".into(), + "8066178271733489868262162784101440625233465103401108911752234818690246453789".into(), + "20248276648316419325977215090702991722051292305169422329958075780094418699373".into(), + "2029002457916171156303194520244672392499138634810303216935386602108547985314".into(), + "21075731183134253416827200425853801562619807215475840771703429923133977728069".into(), + "14328824218343127380036695796575765857116907766196272954596277912518546721093".into(), + "19160580257616407097132379087560824295215411994049928358528355359904099586504".into(), + "8132787339090119872084216324271073977022107784441826475259174173324397592845".into(), + "13163912407679060956610455137901723175820157380216990622789851897632818074059".into(), + "8592347632837509846508131492057017267103172209796052768156176559923311880083".into(), + "7893378189384253642782385691967744125589477790209699483381677762080196748261".into(), + "12191210041395050728454216483522186705806341251170673836903099334267999015601".into(), + "2618630263994051322846776438484808085311498796047898764787894432257296575984".into(), + "13304976328362334711292646669717074062694791647749224129724116074506061700957".into(), + "624484185190294110172011007497433523891084548746061325634035014633317325140".into(), + "15930533828823189720011923265223664488732840218774700941123034073028585645207".into(), + "8694885217655511794794497743599831016837108207363203834593510913033381737488".into(), + "17331704109224667609742217259848258607875799984699711250101367091564505664522".into(), + "1143531962761362035993655799963451435028684354850849659611727546724143302608".into(), + "5414384052493950126841753058585655621007207302558963578017856767058437816718".into(), + "8343436020075567035126766344602793013516161248129384290738181959425998466132".into(), + "4832334637017830367140676637457267590768883455902783371477346052893423021577".into(), + "15960489327340978673041589758216206637417346222123069896632550487782527402630".into(), + "4557633726568362237945959401789921266559833502098724828463753976542737136513".into(), + "21292745345591415194683498267204047392092318996401128975083535124377610645118".into(), + "9328286636803072400168705383927986130049573701928015781654594560657277940196".into(), + "4380226305886799411237881027947999265683595948968607979382183326525427173817".into(), + "9767464780410766464122903880954110687963200055038905098288212464855640106788".into(), + "2341077008076389666838372736815646466482189988625828995627083487505977087946".into(), + "20374148380136094638123722089889049817432074134077941767483383760377289749825".into(), + "15787533442296590591714142770598935669897792586470392297628568666971956576563".into(), + "7633568663576517885027652011215374332444473983000466204175445586239040766264".into(), + "9580455424171239687884724329420941872151195359863514187490663550878551019919".into(), + "19911295277809179559313541842789165346180998073715101124443882860854846201169".into(), + "4696263765841909122685466774945153879000521574484303419094957852664345027123".into(), + "6118063668634440692631158765198200090955204724365488531804099825202234531440".into(), + "19475295610121478762637598544231725416556110773059271107863119783250115177307".into(), + "20179584065699439977644558242108004135948300904465438207718184556293527131765".into(), + "2676625519221951651755843575492662910087680586300055636532952929295968711540".into(), + "13080027857034076796318609243033548218531614593023186061246772929628249264284".into(), + "14263363493033688982783626695159053510832721506041125050282934376406154340703".into(), + "2706925569052822828207630841519123914917406668054471879280093987418440726365".into(), + "4296262245978655924075197707705805754837595446469608588294115404804858164512".into(), + "18695068331082119185822574502658840301072706119995753204333012565779354028154".into(), + "3337114200744822151016415017664070914837554273478224716426789950128003958134".into(), + "6975962532267211837956893753783559781855104297873769130878094791004904433267".into(), + "12231027575978562434100945363599501995871801149646690231633189894762835916126".into(), + "3888650643207658707541674459822797638642380807202524073478543941467788883170".into(), + "7676102615198465271172138409531332193754467889637067928466752449336805364120".into(), + "9811623913757464238022803188199125269665959963552692281954397128646993550529".into(), + "156972936087215783948417984577273879197745728180593700417440323304914519034".into(), + "16588845615557743733989563376260327418375621586848888397570715079613783522187".into(), + "12011819284668339582414804793555969540908484357893010282713633444914261107919".into(), + "9658635959610321892309182692375199095125363721825304507540533137281497868500".into(), + "12870566947850007704602286722292111587887779596594529150836676091488336717955".into(), + "17017505534531720639069672825866463960388881234646040969078396172375695830933".into(), + "12828756987874340466657656310127299330830014385503837959876241551509225608029".into(), + "18359630725137047619220866091928132852289131876343072040435717392635728807670".into(), + "17863140285101027979634946030001422306163257009614315954049905801373677640874".into(), + "3360582341611046517265570944669154454984314353967466888538655993528259823694".into(), + "17502670755424560054495200659249586316206934247721147538443383457515937914644".into(), + "13927649797430421409818392324255621487749881718200898095569535249421816857631".into(), + "7898469048097586242538236499785851298058905177690518852139435937277393454608".into(), + "5393613856505501947177492775058221051864341377769606207857027894289988034452".into(), + "3985540196927201732264077382523049411262602519666474163140081018404802939018".into(), + "9933548682032172763206440650514545057762401680471287764841975167579530669418".into(), + "4792769546077532311825527426101752293398958175332337087633556291257147047450".into(), + "5156370681902535309417699137971512021677704826447998658592162481410036210325".into(), + "4763178997526214364264420973622397594953864683634906055109407846288865926145".into(), + "16580450694184328601228411647550657642835283837702883837525078295714435399937".into(), + "18999059441252116518985471589622428967202991051386298987587330050320210739909".into(), + "10924663926297817693040648165612355343542542868891379772060513577838006191064".into(), + "21154439824594463741340124378173590149781737214921140238108168355994559155084".into(), + "19866718036982650474956880892863081255204850747846301678102177541466640536845".into(), + "5726341152794890743559900317356587749078768834368131432915420083753465739652".into(), + "9721930072810562138700262351588760483759343649062887160202262013979665248772".into(), + "8692713109320385702126858931428174471319167530336355188864897071149715822466".into(), + "17412352102001079694372298819013833514973940827985696439109156982412245617083".into(), + "12672821032755011921233434696998986247906307111868736700673800862103166163534".into(), + "10172970004738576173270726006846527765711642326938611469845242801391711907951".into(), + "9780618262465502284058891490165578338456400832996467543839344602761711940182".into(), + "18851700024286130309776897830342991020297865786362943013731692845791157935866".into(), + "1702470413617318930175864989944938294024268866839160834076264568335720361305".into(), + "4126610465068493007303692923323412316073023198781919888394734000856090363586".into(), + "18923463588257096056764527998950010226471775437259486940005784970583303111186".into(), + "21839276475997615446044485384539051285181302015654907519320251876081314334359".into(), + "8307810470154672938299109640101353533274474986026012646848648110658880568963".into(), + "21681710504784331775159894287594944141623698915845714535863219742158877134446".into(), + "13673546849110664891321911520052734162092211680871085198884137808051757614727".into(), + "2216218889963359247084339208809391515747106874740680794343547186367655541588".into(), + "19115757323666287625233094721549891816100455942516447718127879700062782999180".into(), + "9695759088071708435257280035083149593819491025470776577904130348886983773393".into(), + "14990538824992915759805383740971251128446161760292769615173978580109946105509".into(), + "13500547160059033639084125020765777005399100253116035721012986936873938870871".into(), + "11690620517343570003741441631732985155983668292416671376092598551135607742688".into(), + "14092995167301878045390831717670181419253620142006537525508736966592448309458".into(), + "3134074644668983555014847848317126775181820544161601574897784886291988720093".into(), + "21356667096328346522478877399307107750543924879742810010071567516379232573327".into(), + "19328894389922014017090671733282649341431509867352021154566219841026028128702".into(), + "16777200051435195914695940482277275703598221309253917444691069340963118078548".into(), + "12283150914371601398948839801032083610952821665268602559004246563482653048467".into(), + "8031025780211541530328680242940535108474848337234033825435617391087509668168".into(), + "11393488534594129202264782038673712630583567081414606145740976603672179543089".into(), + "9489545274708587303435157546929129885054870357881034831999260754593859728111".into(), + "6440297901700932642712459656137166095980435930130325599953289987347675780242".into(), + "6894515864899267480052927589331916028154612433273964297048946097044117290367".into(), + "16589536620737203892631819952021004148548871602361830198268389856564419047101".into(), + "4583341011172551024562134025995013510576215116397573125685762514159294995879".into(), + "5241434939346739917434576806032039711895865193494390109560604956906855912755".into(), + "17826720085025179303670747265498956093868413968174402515352860124554701720680".into(), + "14064177224800231170886754123172947484113558685092392153296947056267698509591".into(), + "20746239761736290080585462212523994318855974409743295600262800987044075760592".into(), + "9275091732796562047971474774056076192970912855497078395575768670699769126364".into(), + "19100348980922843718890560618583044384034028895423544873772560921345167893474".into(), + "8069571373226602742410885815839523915870737256484068502291195820379222033494".into(), + "1467293981248465102656318727619114500592839521238995988554329217082570049573".into(), + "6381288201275521270245376318098534380164014423572839901345374016424121324717".into(), + "15155207472765568640645705040738739058676005958167290460723767903432317983809".into(), + "11827307205908787524136950608324322915050519697651400085361964512106546953617".into(), + "21374803923432823385981703420361272374739614104998515368780896432531799636069".into(), + "1620198401197709963530631727016287708030541032398661971728351531077981225770".into(), + "4800625217713746765755573372019630732124860961049092382553729057755156854314".into(), + "16885045113746325978276736698359862381890909478720549186885416774857519433844".into(), + "18940059701033384450370562570502818829119108154708321114250461189220553261490".into(), + "17028163572961360646345264113167539623561504425952365941615928454393252072957".into(), + "1611295667261368131094571297996490796385397691978293531693197200319130547745".into(), + "12046456112369511110295802643603369841325461255354393474919590204243253663937".into(), + "7754599975225661234551263660451457408477000193002987626530262268904609507135".into(), + "2313209556333942391083901663566216617901599085931440197682535151404828238681".into(), + "11201045085233852526555327068198185072847585214550933934267941732063511616826".into(), + "4467724481011544137869960218540384367398804813150942792543111067523429032279".into(), + "20757297420378962887523157926616026028995120570567878138398997747263606554197".into(), + "17461435854008784993561933203163122288937104371480871856334630516397129102735".into(), + "11233240529972088933066139644647717712375997842172826165553170491212908480221".into(), + "13986517480527430903609790838030615892444640383875866009325809535938190376249".into(), + "4301929714047907689515030408708811722641058401192285565408232097014641811166".into(), + "17696794946098216231804098416293622660221918490072683908415671549432039016947".into(), + "7439888160491058128389941744272690247182736763968209695016076283096704865863".into(), + "18423890552826544443227868172047086037747632248290044990903922658938527547680".into(), + "2138999949837278709489832079964510183830385901897653052233908704951853637094".into(), + "8615970277754580973564280361114435711269905378483503993334701750963152744511".into(), + "1245284434872653312507712347938909167594598148561528981150147522912318212857".into(), + "16245768201990863935411700653063134435976966358871420800168843470345424885412".into(), + "12879861318323580145511978223564804504159396722688932396568245995032194740594".into(), + "2552284675231253239834976615340003107373525651900622595931449498227491766869".into(), + "980488019139379194961142515231528163966929536082343598209628148732322981564".into(), + "7264750621517252424008399274736151499285393886075664598878039391991523050881".into(), + "2234522067987218377738399151771319350093309605278907663987720519875860528325".into(), + "3115352127411243786318531509959426596747177553864136971519102170874744602976".into(), + "3667866402502198123848250630765454744061857715778956702490214141676773902196".into(), + "3070805472938376665214561241692318374677535766486562510422267606125416243825".into(), + "7823349275852374866499481666710330223055954666864547234510846404777769619933".into(), + "15260540996982618345416395328474127890176746670866447824367770359510137832585".into(), + "16879450644311650077796590833984905697151358442225768059780947025899660370589".into(), + "15320151525992649849875202875042133283365355535963553893454329179458534031485".into(), + "14632268633016875179098944659717422356155019599115523094512078700042675723400".into(), + "2897293850461343844445387032809649396366859437152990471719742534237008502809".into(), + "19666002760225463497556206571085469154515092232825812732816997876615778032614".into(), + "20118956923652349866364311940594053676005949829436315819799225245413961818420".into(), + "11744985056755246878773405286931074634975698324846162530423833591918403445926".into(), + "2015290434884472026423323399096281629968890595973845384920383969402450506434".into(), + "18249544278720359760062826368546772272198884406461513288445757927068468076813".into(), + "10696154943059697598572810332978032400281533074175542497867727183214332201316".into(), + "3971341798054170372777083512695270701169903687966221904151853325481552612966".into(), + "9959663659512617071119055590004415036736845473755952682208877124936249722622".into(), + "2181868782404294694165345801562667214869325590275262047722759281472694906368".into(), + "16543662218198449396015118334228119894116585978443535797616874635898860943302".into(), + "56444853332991525451220607324577660725674378827134771417770990653696578708".into(), + "18053264525785010356112278932118420278769989354317119094784720416230380388033".into(), + "963511433856494437037779480692599956325036862396765985497581773445785537789".into(), + "1640548195682821030419870544261428767973895477020340173398587128599119014650".into(), + "15577232677357883687312806010171709394572550002708034453277797023283497421495".into(), + "11090954381098620504472251563901051846520004004959525348540953956152044818938".into(), + "17126684395012300883745178409829259734795520186865031009709416122834152378303".into(), + "8422844186088908124243365558762687987341862957152073183459572023886741121702".into(), + "14662184649744589649763530324217861015549859438835398957469773613387857845572".into(), + "21031377022606130696364565229626602575982595290181000993536506695259062820250".into(), + "12689389963301532909415546365757541009330035137990488344057816579888146178666".into(), + "5214588312023400585366048036841380932988375130807938893042700215898846591858".into(), + "15041893128824524382738883860992957588596383766546394405410998279035324015705".into(), + "782192460855406809992878758154419200401389375934686194113667662665836876265".into(), + "5661239444927590688224044135973292706791664398184824354673135332710484834774".into(), + "3817944105257087067488946967989598283409314186886285679195995872866829761906".into(), + "9462770371773357009169408492776891016388486549772557694482752113820123322232".into(), + "15769763308905916572724877727092407815390795595917044619305377061254245212113".into(), + "13771914451667791497863967930538877162560051760275863288476375824988661915333".into(), + "706561967078473376761233253052150494577539018705821668938338502999740910843".into(), + "12390235197745683286169097117092390888206044377209705969844343148646378449506".into(), + "11145596728577531179888406424537808841248238586984730649700720979694309693930".into(), + "15681641822349110677199695953283484509740812777386467296516920337242539284544".into(), + "17574569459993920657262688380744386356573422289886331853177778938058648912158".into(), + "5624326259006122381461763176192388804186780246412847832931645688163746289220".into(), + "16496277716358307095107833912170230220700599576045238114071305799889738129888".into(), + "13435082168481898917836725813624706153275101528359222531631402061384065427333".into(), + "14145359265331851785705850315135126451460028716020850520657056103361268407457".into(), + "7994845231841309191598369070742738315856499258678582728014902950258896103570".into(), + "15231733338108258465034981184038200743349384534550555337079556783807466295081".into(), + "7713361220902077511242971379188205561979775701167781811837114504665826686675".into(), + "11242747039712276301478013037107867308448907318991409117791290960665848463140".into(), + "578482125379746689637837359294794469152617864738083364508568444154322295070".into(), + "10411717957746838375775334848320973308009407649021455865417509560443692302903".into(), + "20227006425680272844667600313609218155469920109215388465037566152370252232679".into(), + "4086233333215814731082706125141659248745981704236593522409792368006446650781".into(), + "6808584534953180557531972017829075410964510413317063595166364533912498333198".into(), + "14933356623681974917513408843614143743808832277364712291262158931793965369957".into(), + "8361829936147528340154802497787058714748584629340813748839621641190595526650".into(), + "5538387899076347863195504194335672966840748823754653039217054113809390157835".into(), + "6615705984888160955628612086123920128735091346352058978084487503753552928718".into(), + "4247091362085081761865598677360777173802067597672781739780149117241511260340".into(), + "4974625254311074298909209418614844254599548567616598303359921500987075775722".into(), + "9907284404283923304330882823688663208972219438730081750092058981162203620514".into(), + "14238658152145851054995618234965786835935641311230999365757330392464542182773".into(), + "5602036166343678358319941150088594172266643715315669628685676756102877898852".into(), + "10315461382975117983285315435824656113503983463182219407091635205239799886780".into(), + "18907680493658247637744313142676664986314600274326418373879671192300682190898".into(), + "8553303723584937031207437681614026720261370015480962759264727133956998736136".into(), + "6891145490942460969785177369488530687011218911690606251233112277697487049464".into(), + "19170245246106596353570021916624717497160367167030242670159454715827568730304".into(), + "12963872577905157066479007532642021260793201956955966146918319119859824768103".into(), + "19825143672793576100800645640010743262329532693712002233459160547290155991184".into(), + "122363237387596939334930000068508772338726942066663977131612051418977350004".into(), + "14083010722716557573128541885186700095072789367734739290283132018427397490175".into(), + "3438988943822218606847460625826294857914025511956848084069535393649532189446".into(), + "10824463893276561180588773956898041497815337719354064658470380006885795713398".into(), + "16647020853733827256761129360155419271528077519934494581063879760642045999108".into(), + "14779736608400098952280783906738683105991809429340153405681057429405486938750".into(), + "13142746404873014842415191205878004285381311757484122253939053617110681448928".into(), + "1386333773150414920705543260472370744952980454893569383627231135209073588228".into(), + "9697679119976816079822213749536923969219789272727871054723095487806154942217".into(), + "3460404774244857416356250946884568630025797822298777944076755223444927083223".into(), + "1247968706400267099989617117009880060043946421405967563604685657862139452642".into(), + "4507768933023927635709805634829365165299253230159842263645571663212665854586".into(), + "3426290614024232855381062903753364259178390841306198547519682507770097215868".into(), + "14446229460062832266543880366376059431654246279317515826580297094759435628761".into(), + "8067953446832743759173288639514778564060677449303509306198517269476636669111".into(), + "21177512050925009490126830107933732327927737774182499659239090599165030659885".into(), + "17012508948587701524178526490941665696519922093511307343255635334215277408110".into(), + "13790566627669249053646494281027520203709518820977624221038904711079372230598".into(), + "16958540834863372886175563802155893167203339755555003665356919890498736433754".into(), + "13423323660143533006628485455163236538714121124914329148003611317540843114521".into(), + "18732130441148222818743122719599795831305992743627249485606912380686722650328".into(), + "21744582565522883116221789698750442373155722956313850154199652336110648496029".into(), + "7144354002506963954801968243991190427536676772300009377345054067229788469802".into(), + "546773555852952995236535448961524872583912665647152978722419200006698017228".into(), + "4115449488659159484679292884434524602598250771770554945188450323754895822199".into(), + "8595752591492411960378746550184866534422481915151040676780917721787338199480".into(), + "10165757572338172851680092102888140824090130405358311565741520039277283108819".into(), + "13881202481966783370628597357006999877307398877082297393247431527668438855111".into(), + "164079380527758195225075822377759030452558671627014461867366308987961032431".into(), + "16597854725561663920154725753244355279782548142174519652163898109297275062756".into(), + "18782804026635015341133877400860675283426234307512052057983648925359584161851".into(), + "11803457827110915157916889714293161979623605698803927824089144848270204757888".into(), + "3790889121366006080676961526219702764983373811240106724685417109395959845261".into(), + "547387322589169298813085629174571402766705427171260176930124272482922852550".into(), + "2250336107342267953278191463235028062291289397936525878162948482155007182474".into(), + "18878522248575655887331882357174227152771300904536167927922454904180276940063".into(), + "5479817494658339363150524278522534672218076031607309533533517453567105458144".into(), + "14471434214677769494999424343272645790653559928767779107202935041996962964627".into(), + "15118605967190623209837537416237887934162874725496448474965882020979641133025".into(), + "3039022034764509339398468046956573675942916248030956764556346100611315129267".into(), + "11040960503243486479242227243433964824049773842453715686240136222246114618507".into(), + "14522024554516641986547166200505804498300840654246791399797761270595590503741".into(), + "6835224295711983166071915743041027338226822865794125690776093851440377194137".into(), + "13366807550844486078755421216059218883003148232530961590657899207604510596943".into(), + "13252007130485429909186723432146549270562877209239528962258894291232641470721".into(), + "2492164358128252077612941728095518049849413853275094452844245158937134153493".into(), + "17908308295682961911088181370570531617402259473536245650574431533231050717409".into(), + "19598815799287079436414455405296166262841941910574843546435474727483962158191".into(), + "12296683988787359527811189035096536832943622321600079901820428531954001883453".into(), + "3034216468239708296411368036093482845790159680213461245275097466511477145032".into(), + "21365662921308444671672644376862924257055009191312934218262773003828812482536".into(), + "21643121605272677508602911313799693744649969093302740901187983246336863173757".into(), + "17106474623853535335864151625226941722837229436558906235895607080504648969673".into(), + "5170493063367583327862860118856811567748934103888623432970286709351028834594".into(), + "16157297782580756629356992582192341437261969693717401640006420404649789675283".into(), + "12726902817862860900369295542623896245980247290073629666117912397630392192361".into(), + "3081548921497703421223514856379500960238907095191066190890406228882786538855".into(), + "13383733165408518558908753036431726341720113406574723548828112868926423953299".into(), + "17114711170811861625673949085444809223764181246137324935055609379301828432643".into(), + "490084821539048617783395755779188347718107877537460289908857747116706971191".into(), + "4649874458515161549974867873095987666703831126237318082002364917533053056940".into(), + "18309757296672459101064597681764458207032523283974093139605613289699615669226".into(), + "7819183041486966141608141659813055168335432042159899601871834502038946555307".into(), + "21341796073041881721040729550176895226179046215340279859887156449076052673189".into(), + "8964280440056181910087237574225882257648057952084687707991537684955336080915".into(), + "17214415387533445163014526685896715822410886705201252936359885537015585673030".into(), + "1103535976918892886566275197010540828604931300309949743647411661976344710321".into(), + "18989472017473838846112035962614068658032619400049135145786394166114140582702".into(), + "3261305106935504628452223922938937365963307399148885220969836911471709180672".into(), + "8250352610228962368545929233808273614693022774196635878184474970985288333646".into(), + "21336821559645974200167373849850832540612240140661136750527391104897925746090".into(), + "4804442184264650022914507746660662354579855239722797010340948536245079163068".into(), + "2980351069317208287298989743520302917458165906618872700838358045014403829821".into(), + "20789178264137518066753395738491570400211130020259232167860189045958431732223".into(), + "7043197173168479199982037485481873468680373055205806152875339535752299138160".into(), + "8803416368756444354155108122527515347994446936702934474369040166131196644971".into(), + "2327814593413519022021694520242495352706688146726587158472403609475455845031".into(), + "21198136442842149822575509914524766455809483419897291889042050133491478146813".into(), + "5588816308782619790412052393435292678862962083038610001710149574381906419148".into(), + "18457707371647662280814248200286355752809748069618488571669726734664943098742".into(), + "1234389524319480772210519595012254096434562205385972935754568629194134383384".into(), + "8590188161561261795686801611365857510200794054668578839168287669047410512276".into(), + "14470989978325106127256991312163042779416463931400090968073878504946069484989".into(), + "2305522070968512786738458204005001050714630746016184992510479809638463479749".into(), + "7477355783744938536562522733347415235458953622739577251175967178186753572312".into(), + "567290291622042941074701939710650764088062581452752653943609540087956251460".into(), + "20440460641466928749670992266608077799893623138336747959135603955102479614007".into(), + "3054829023148086310176268593952124817160009906490401089445995945337990758835".into(), + "1115018683950178281685995685874075286310060333362422341896901389981346771770".into(), + "7809307874649635295815564328714298840313171873184814252455880392757245438416".into(), + "16435322760021026161995603943756578712245360106977256514516858926570158830261".into(), + "15538054347747268689904146479533221617920707373984233068744782064536586402847".into(), + "2145106610221843472170037420099674539983626911224263405264737836509031499186".into(), + "10469489855508967606640238771767546316388755621910047798817316247787439887327".into(), + "19269976274347330455450094587958438145203987980029244301223712026832424496030".into(), + "9442647357194564700611387347788116150552532746010343621293769302924097756012".into(), + "3958820742734683830021295040986048520026707752457985546687457778678735761219".into(), + "11348329600459394107897118896385152488024741286593365288318995787678872184581".into(), + "7994061748163511046803584801099079098965771882190345699910054751589174660915".into(), + "17839452077827445141859404251925903822746019560954563061323642101356666431414".into(), + "13232228834563301156710808377438125900543901439977087615805079133929315161755".into(), + "21820445178921591403441486524597609508081666751352389500190809234730066099269".into(), + "16335255581063466649418326314739202718270800342509998411094897137707475468574".into(), + "7815038267931398026175910302031438514210779836695664391140597883647619726660".into(), + "6108907775060665614604453427927916450888778767561273091208488690292223394553".into(), + "18731401306874205485033288380603219954710608893214981010860304692443381676445".into(), + "9207310842136026530277150017022352541117999932757777916196896007342718304978".into(), + "4275597059348294910195928059046438407683127030809544814087898068951365707578".into(), + "8551726143850685533914504884506052366269066663000473253249997846823470852089".into(), + "17722307737167433007678801800007589919417323324870850217087785924251047414732".into(), + "9659885913999670803985648240505723928144853579243576606650908903914665975845".into(), + "4127785260298795455679482228123141201810011802311941172182140342819621401490".into(), + "7995480315864311476694461046721076190059098585527103653430045699541205743090".into(), + "2710887853573837837653751249029686223333938865050270571041154550753948687821".into(), + "10929954836518904033304553904442567770321295457957205208591304372724905918438".into(), + "14001950425766109615209637768509276336684867788731325954527529665290504740308".into(), + "13093798171633818149701610236482430519636222108426384974269506295925166461929".into(), + "341112534319506600537441322030652722098845095671561271148940215817004993097".into(), + "6110906587902719841743444555887325466851146812889558891609843725163499179004".into(), + "4611067237252812652698946201052371234192768537459773069641551009583406296376".into(), + "8813661389433335471770742946393259278020908342551057181562786643750538648664".into(), + "4082028201221928661179738646263379819458232996637786410627338568216272942842".into(), + "4387150015663192481675842540873436578480016784260152864928968972841893485475".into(), + "5798957359608013690581519427700262504660783802132038228115867574470007647157".into(), + "19722104754281068600204006779633412991974614044671884281702255273995426048636".into(), + "13237763289640918787304198678609438981723281080085860502804175876747302592942".into(), + "10350397375768871942356366001436797697061286173475393119069375716517495164748".into(), + "9716754658986776864790426519095473979615270133917036668138857377100462313944".into(), + "12559455792963077442707649745319068600669837985026401838048238001273610719876".into(), + "15691474792343664499590886820783843178573436351948842211318848415073250926238".into(), + "2315152971409777702150694033027032394728963326020928261979764797562368859984".into(), + "10145932773746660365948684949555749309408495540108698054181994778319619682499".into(), + "21031755674184251231918386922772885560228556184297650283520145773416227622020".into(), + "9656774539408247026483766906298154647226870877463506520381115258572281833777".into(), + "3703366867036538119931629591865173960052419475390100010069369247148720449735".into(), + "1155849517605989105668895794996696646805146181003961935923384046079365333956".into(), + "9462523438649504157500459081869061592673258626587962773726459717365228540892".into(), + "14558942402093706312345858485376818615887511613771478694966109228129293547963".into(), + "2812048253376149161843484904554248272181353897695109556171864471465530141300".into(), + "14581264237688674105789045795632866727938915214983870169917226308316644895751".into(), + "11806517668379902968824386871470726201210807458248501270142903146174354419162".into(), + "4072157532402496076820064418241903939352389786658517709744366379045220383162".into(), + "21813972253527564887362886309776329116642260196272043100875592141144150622785".into(), + "10464853353887659405357329005361733216328104382848677769669050984745487501147".into(), + "14772015460668233451710325522019255336227111235340236488781640750731630746977".into(), + "21527357780259989210243319716428302865642384867695769975035672624351025254966".into(), + "10891794486798293092627071927690327494090905691269827842959180201330098409394".into(), + "13485855904336418644657500553650073084861569474830727752625752553358760580285".into(), + "11448242815253167183767068865945471000243774132210813929164325622036544013989".into(), + "19410155802742110833632454245227392555968304439001009443806761608484053045554".into(), + "12167659128474886745250741538637209499739823457162062821474270919941304684088".into(), + "16680628007927224771777485144832808896272748488695838488671868152342742322186".into(), + "21298757047923195531118553955836039383942072424271519371368119907422148509278".into(), + "11697718923718927022148851233178802389523673168156063462791132046990360699116".into(), + "14786267704709019791824267590492332247783919098705756794174245591027365669638".into(), + "4029431706248165284072638415134780930697313346853706588481402470852714029894".into(), + "6637339357364931518321345115064554926275905427738668939829923099637974821930".into(), + "11721089735875799137502005413616822380279134029969641544449026524015093316862".into(), + "11258510956066696357188198767462611223354755428568008829233602610737417570759".into(), + "17382216798452684990847332797566093767764854060615116426813871855967998298795".into(), + "4497813725838018324398390685637602656336499793658559967801659260213189483769".into(), + "9309905740467289615895980718346712749846725019185323382202916800724200146653".into(), + "4554312255934480823406477691403023661425250519363902231000470326223736059080".into(), + "15877126774677139396238817617140527789790929268045936819000883506734809330678".into(), + "102909811737053415440204213495606488273727867104784554516589930311835745189".into(), + "18412616692627375517300170963764173086100899052204830988805458281556342306704".into(), + "12497849254798953635808071778933029738229621492383212989583296055326390275927".into(), + "13190130371739872388747884175792586030658495147350847435106581404654633998344".into(), + "6569505978975360946878354767391913708682729333577071120385232557177773967150".into(), + "5486573030999616702165020120135398562758060949059349810688840632469582165448".into(), + "10402649247177704342444720212899468852439313211834653468379224661013914061054".into(), + "14870393688688048179476829039887867662050219694351765548412705799226912185902".into(), + "11112866336990864101289111553915677602536611363742637349719791431991672470238".into(), + "786378775702052024749700197105312354581130448431768994347811552323325137023".into(), + "12804314666740390639363966758718513558509859637020806917779321368800352250093".into(), + "16383777838797313001717025761299072170493145903921192845224827276088079299324".into(), + "18390795790658866542478563024059495583921996400061610405032233720404354578517".into(), + "19071180375782447479569450482330188708754447860024817674265020413221587566028".into(), + "7064459298524756483115092315588548572944660746058573142372140457074172044435".into(), + "1201661337668100759364810191918525473246105501963149603200893010746984240574".into(), + "14673821426225418930674597108599685172091690120026694257549676255194170791868".into(), + "8007695637892306475030605880993450528464588921978475145792244443873714795074".into(), + "11523933430814198704411556653252358930965476928606503642553530463689321317157".into(), + "4720685254208822524727003349181010163122046826828742211508555904133729568685".into(), + "2704641256375089357490975523544121554540568505171014265704664461404637160537".into(), + "17873735287572402838142305036318020439254672746974064930437541429239252592118".into(), + "15591377870113101853150486245422804312550411754131695747717894157544182932395".into(), + "18680866424621452161410252746147706712971801673312117262770403991597018010533".into(), + "351092121232997110902065296627489736041463573095030601146795248738327912247".into(), + "21410776897919290439576321311168524383878404907590335964810760611175870803022".into(), + "17236852113726644484641871544732171570702656662638441976539579805457648241354".into(), + "5141902767347064892821277441749408218256386923351945667768202586451461154007".into(), + "9061206488410388460545711706154817066096529615910172728152676288649487389209".into(), + "21501744386281091461333207234319069661329384520296829009605344252699881602491".into(), + "20523123786355828261901087089746676928050070859811577818342254696196216070331".into(), + "16340183952833650334331728750144828293293213803155672739442287164371494962264".into(), + "11997165266626958619141537683193751516111129514708339075256579474097934990898".into(), + "11727998419969202419125826890516046584560800158309390774599325003825426052070".into(), + "13931457202894182485995482691171342703522733597260457019434686547274942752168".into(), + "8889288417892697124945054364222718721845070552969370183001562339292498026982".into(), + "10607636906736209971474676586549274600168449611294688931553643349980364319662".into(), + "13373379279879419696628639312899105244563309886546920506727138704477895635504".into(), + "7516778707927565776230701116353902556324173125006482617300170811322359880287".into(), + "284142816263687923446544408203711951081978657997111022490562722442340431026".into(), + "1663629018772009800050577108421747444431944008366135531327269967377988698730".into(), + "15976560170935104892839671978479711290979780560451333565745657902528940986567".into(), + "2821894717706045749560970435026664943807315523436547166205412842765540126279".into(), + "16411046577927712325274395387891881155718376469810123282180259561336053360599".into(), + "14830500017171075678309056171812981524309710840633235720674153550980163992149".into(), + "18516862946535092410308127384088576733043708186179828736204273213102857123131".into(), + "2803122828384011132142130003641446021361083629354222582787564441368260233535".into(), + "8655048519095208179618171351501333519098495288987579151859877773818991560313".into(), + "17628029087309631764673769418575149062296127129914718449980855676793923700717".into(), + "12213182221565572417730092495989703548687597794044608925789870740387528958423".into(), + "1942254992896992114200957837548346988692238147738540593160350563013662461747".into(), + "18572481120445885888145197091838633506611989952111678544191205873056195137330".into(), + "18140911659901689098303870986888083953843757583911874535981587625576530534701".into(), + "12576912117641358754786901787127955567685279522839676706223096592944048067439".into(), + "9853587546273100154686512094544697531675085181100931274409412440600306885014".into(), + "11051022304629047704897656757884328402546597879242426643246383517726104682373".into(), + "21747178377575326127773834076417831630856635794535286757990250257220185876652".into(), + "14802894612148412188667842094308733856318572638297904757997514236587913861527".into(), + "20978447371728675031404263158195920974895558458780873769079890388209356430130".into(), + "1037699465155917084450585874080268028561047537346921243992399961945112783290".into(), + "4796870784617170576045350520159043022811420003073563826183620782718648038813".into(), + "9853767424749529094226213721882427217388134769321854800041359789143412725618".into(), + "16909978741728448404835572592895383783360498936047937910269991412415349672326".into(), + "1843438491269241815751499591631260690521017848626075859779071605413804154431".into(), + "14341379518602865584962107623594010671519157088756134035711400970326861508492".into(), + "11148607079771922951721552848779822101435560243853139254657574581631328105418".into(), + "7236118376476006500577604441648338380683405215283131664289729852364085763863".into(), + "9771089868728917573774866709686459919517030662395798431632764214674312680212".into(), + "9110797416046523350769719273137039416172041255824880845510758575776777500007".into(), + "11171998782921300624880315291878283252073139909993544537205045086249176031551".into(), + "6662646470172779612845827572259867913830316914285246080650610642251248472832".into(), + "7840073244312824502623215399534211251667887906747521505446844849673459134791".into(), + "15746172297311038934003440308705797271829102199583540526113736537482577060229".into(), + "6886137335014489748489631206590121074634704468637741127875111216297167474235".into(), + "420232306382434242751186173486850411467398307214919587427625546445400284482".into(), + "6101917029417345450869344197624899835171925047771043819872045680173320199082".into(), + "20902767593677591005062278004159629853556955206212143592781424915847484793984".into(), + "5806682782059166443158221425741828463380368530625835045262822570023351074794".into(), + "8937322067898730475133772449174500828509823386740428196387523130412301822446".into(), + "10698423376034838319080023911266285836831986186258448454885030311257173631094".into(), + "3804139705083664873950903601295520892698549383396840546300663481183626370753".into(), + "11666167980086510761582909817125709232688171970969608229444060852628732414641".into(), + "5195343484230074513500630640199601466733948553952258905990513870281939709899".into(), + "14624340714306078897305540751958284706578556105002276099158477226484579202503".into(), + "10083361130687068583126496521090418135813124339806220406205408201612392141304".into(), + "18641596433875997766126569678124500204213295416321224520910179376654463020142".into(), + "2202590858941069122312246576796422760143586439968023643572584733807707875591".into(), + "5606778593839139538330184356292709713321311150529135965207704575468867770974".into(), + "11958475164655758519149540613838246197985233378525766327961909142873639847913".into(), + "9229670488942629146704143920529444712626724835075235979345002293458105678408".into(), + "16754443145923358894066339463409997111280948017357151500387305969742481068301".into(), + "2409255870410101963458063337813762581436752424874011147061097403491760442109".into(), + "2214559337316973031881614971177447231501212859777889339656681156270831720504".into(), + "11421110362488121690592793129931919017457696830596130693048382951666066696299".into(), + "3820500999703948691966833683611482557851819710346178343790665741738753374219".into(), + "8756218158739871072278901807217627680176358846778830959237385752221505342071".into(), + "8032449730250468861487267430306584116120245106718506069817646852031479757007".into(), + "10243677923364935391432160448413375676421376200851541463326287529268288890040".into(), + "1128776597481264636069298358213640667665677523022374620075432951501260575292".into(), + "5644093261057489381910165706940112111858706948653266534656144218172488885132".into(), + "10992382177616530304259484530083164550434687278050566643026419033635199094809".into(), + "14561508845841116001383562169823893923516132174944052932403904968433057227748".into(), + "21863964650894297432572505174186286520067123468863642727441839125002713095277".into(), + "20755119936203820147240015392257516486599536773168698212709082762309828651450".into(), + "16959073226608628398387152299601201172109169744065562346871992262472923761717".into(), + "1422360831209405989940170583665509252269399522601915800432845527860741426172".into(), + "17389326678043835536716857792606285368419240597293609647618693076258681847452".into(), + "12197527885708217940863852882450902665816552534167940448166754267717839462635".into(), + "21611570070424770005490607477701523898795002037965850794497143400504853790221".into(), + "11892102040110397810197867975938039639353004723546075702397206087388005784781".into(), + "13516538003340091563836438322533954338394185445401173280530387106662595790317".into(), + "12048467357966566407092445451263285920234083860460644072449020638723740218202".into(), + "1735010081751908190348908571270511873519447755491670766716880879623393561967".into(), + "1975466263323070433866371621575002630840361531361829342799564009137157551290".into(), + "14332158763257273099367529977375112575029271371581919959093592722516585473205".into(), + "18308662877208906527685104306067008785578317983243580533749074382175572194649".into(), + "16147771244577038318738675557955640363954546932826136022515936117397152662906".into(), + "12310104474053287506280911462776789511537301762185944649254713583392295220302".into(), + "20727834941236374148190412755081722896905078541838912474132444343774737989102".into(), + "4772017368444066833412562265277526529104319024388995968178984274657250179860".into(), + "21049615822069603562632099674853063708639208121642147875791882100914627258965".into(), + "17646394174446098039184464463379581654623553955171175600685924254263103033801".into(), + "20405917523260600357692281918105892289710334792700216851356175227972968907413".into(), + "1288067623955526969302061242482178260544140652671589048188723078863398511616".into(), + "10835537730223027316050378066698794099751744792059501849218931290020119106372".into(), + "20990157274288749983727934109087848093794137769592917583326461140632177148526".into(), + "16941875865072358419938961334635058647868180482918222959682613586029647175353".into(), + "9843778843793690986119532637057920745140536160016368781021998854678847780413".into(), + "5125414792982533413337556654533863770640044560799521974799727390524971858377".into(), + "10864462495296760172304401051361158670010031482061392973919136670275578530070".into(), + "18638188379508145413237672170551928898223721010749214505292285199256581752296".into(), + "128198311352192300389214509562427846527025583978725552204209143056794270869".into(), + "8639617857907165397429617535993768106671328412853872156360019116822694247239".into(), + "4844530255382539675437452182943895677920405337347260379412255490567182702620".into(), + "1804675521808388615789572941859899805904819249465788091888167545049898256219".into(), + "13789062391664555041422570123657413036572356127853659660401072142070681096201".into(), + "2984776938673077398183780839702246034953572367406345432704517332443032892459".into(), + "2992847031807357662780885550148860874749067596604232667894089534007015158636".into(), + "8223481330716908812358677489080309112433589978061949842348101802357147464280".into(), + "15829423985005178435297810317464595131483457001231013788201382741845473393508".into(), + "7862158982782368203916129371016962734683904219117743973675651114828548094092".into(), + "19398173801438973209233005992391884365223581634877055780725266290648936529417".into(), + "19275863963890569296158744143112092383404704340976692571347641477470540348192".into(), + "21372102182460094800056358406250397703306525384763761377034855645794765423371".into(), + "9476173056510831081934271835896053957562178504127106649888600367232767217909".into(), + "8945631126280639679924915282277004291625109402912694487894772245410534789081".into(), + "7926253519938744933656970287399447078338275573508348380139787090085253922642".into(), + "16018716520675458445608001155996902645263605619338294478505090713237417816972".into(), + "14921856253793536148329746401088158240205455851010569917169181161371242017185".into(), + "2079875574141510421000322245042248069972426430967618609676296915817312672160".into(), + "15980599742048633141868186018745285129739756803812956666022183903444182047036".into(), + "12114893033109318668956482804227784987572764113979211644064098174620745412136".into(), + "2810098426330816525728669143855005497849959012191227510766066867001705905451".into(), + "9142041039259596869436365631928849454803241725030479248178020744616036193428".into(), + "16789255887716308252460957605463144609726855240455194269099382954389067176728".into(), + "17736208027338616948877595063010911429975090558603409155923157365536120955065".into(), + "21150410646727639080649942919157548722144769190804322567561458114897439673110".into(), + "8728923715723554965990413741235509264041126517176940864777942728557354521757".into(), + "18877975508697261368601618378333048287851497514619873601021983336084282573042".into(), + "5857216433172498779920090709564798696432088771454963449283671260189720263346".into(), + "3416739349758310085403544797458080484340996684307665591910516201053743952382".into(), + "10014661157797313244292934148350131996657869525790562708237180274791925878615".into(), + "5349492492153439871560702687440086188179846024931646961141799958661743380684".into(), + "8090802326500729500530853058367718173141097881651767619458686010109346379147".into(), + "3243876699925510348842975998823895571182658963945930929780026879935610333017".into(), + "2417624030466093278001094443901143647464035143208058634414228940495675339286".into(), + "14707208083442648370683763592077614356382972898797034092783713393639907619177".into(), + "17545631002841426592730923010822053419499562660641460363423698482744533869073".into(), + "4421380624161160840991891701912641613710893511150975452841445166075745805974".into(), + "1077414817249103950943875081715965937853608110732127257676522759590623460183".into(), + "3507723951294024188735232556852951953031271591466929388169161213367649583178".into(), + "20744267413157655820488615250021371239675627876287191610069053435816486296093".into(), + "10030806611757571510773710000844469375387967668887810533790132098599140714696".into(), + "4098608919187988291739821341638774871917086915024686655297087774191912756808".into(), + "13453755776490865531134267856063521315028666076529776333450382526888445047611".into(), + "11760020934758140446049369723964305600159936563536726075225660675360331528465".into(), + "6406037247037697473727755924858735296546535018055390677906442691851004797953".into(), + "11557358458236767995494355298696159281191676229524038284238319440638237580594".into(), + "6743825309135636487078170799646071187521226570701598888088674184356446519660".into(), + "1363740300261508449387087660891142217813794297057216573859174333622397381039".into(), + "17353086088993821287882776856791148032337098217763977250246938079060857079815".into(), + "6718938715458951426350417166499489427024335443629489544367851434423278337209".into(), + "18987489742261748676564017446964953975327326154943753293417429206546308211028".into(), + "15965659332352377592570514074534026676650570619443318860049459797446297666148".into(), + "20815651494112396184410166336624878644932077115689590904142342335838514032225".into(), + "13289193671002988855733226768851953818541013880219936597636372756289364866355".into(), + "1567979574094562858336626709833690822884595003609499722973068728155414218656".into(), + "1406894286237293229723971624858421702390230552459261102967091748375073005174".into(), + "127524353461789027170460839609791116742773867221485647945877046573898816494".into(), + "14964917196913210912898635537202273294898388149888129789323384554911752584731".into(), + "17573975090037837489354042024755208350546900760352759486497415097145199425437".into(), + "2810800768573675011848063715305835214048305201677436448513685242208560656239".into(), + "3182831596404920556846849906787256417421227529749874859977287400581732004348".into(), + "9293221288281284622603029762317683176118521399756060307438914930508616154161".into(), + "16673727443492180846335195490607282587311771952172084861120865668023189607968".into(), + "14861970206393540357193848383493197595010751639867769363872113104941368881368".into(), + "1947459730453714728047065931026988640356317164613412029204943819757651786269".into(), + "19307336986855330171250472129691969885469727663168817962206758878226019668925".into(), + "16272723820736869927531258455696258295545428585773733795358621236250129636598".into(), + "14067203273048816019998838866277520003916774532401938198162316693809632760951".into(), + "21551507147108044169777874975479534770240991315165807000112720115356653530622".into(), + "17717640130827927258645965421731152649890280820567693269781156985346197796607".into(), + "8341245955737785666220035421009659068228682392298228242003714027037157516189".into(), + "12339617302318828762816142250616108419519769797732290739230041883942137188888".into(), + "10123566056273217296763559440410478584111931737336670797432668142672222468278".into(), + "100330409846079023411270961296323123363585075447874304748480232510662674144".into(), + "1571586856190083814591643193451371280998279878174710717174319810741950319455".into(), + "17626078717574459956178559042223066518589738318146553446930321898524763562145".into(), + "12597474837573442277568310997313425097295312228965354157636545560783225415194".into(), + "9282629691296360927786998265863490172450342562246145699272852196309154676715".into(), + "11475383910279033220892799115018315595968658540404734297367799982184306742405".into(), + "21207578382911046461925166339581302823631975619749313031737447832273859578217".into(), + "9867737209512713742156966231095908977298144995685201344042106615506717185970".into(), + "5419250263664842635521293741086549956576183663718321466133029062700646217820".into(), + "10680301618473120824196964202055452643700003562180874183620820959381512387087".into(), + "11734118782551011491731695279474438578347930162244424307495336916011158044578".into(), + "15017496989515336959243556328206095618035482039354824183597442359483266217611".into(), + "6511621188986288868363472995938291798169517601112511039955221592826262879815".into(), + "1057116630043531259822644333647831844583791045276219751332477616072560117378".into(), + "19059685203310882657000346272623187407133972415131525465827340360192915270217".into(), + "658459778905143381055609790895794611242407110015004801647654573954369450993".into(), + "7603036257465538034144194526422841628761243007297872008772468055256213693455".into(), + "9990634061622229590998343828576831787715708492431159246049355339866257946620".into(), + "8811452869339089802509557934124104823648284438116748217266488878838078556311".into(), + "377338624779519383654026004940027827544698613282452894117899417005371283013".into(), + "20372432760811032237009833207612531537192479064270437032142982696970398716892".into(), + "15755624168218886509358985223476988622172061232118179707766784427029774962558".into(), + "813530157019364022188179508608879306640863329215682533796638454834580544088".into(), + "15366777101659035236493629475299055460750095122762878608882821037262385854171".into(), + "9412569363133467481819948072214644683803929543842857451635373713673797937906".into(), + "7663229432381596357590875498997196481050744300865119408024986926994876708428".into(), + "2802277205489381760400127515311144092806348374683690687945106801827615715524".into(), + "10398607437116928120233501860786387490329760704107357815670770315190035281298".into(), + "7713454417232694103114115606745926964297583014900008700978733395172058088330".into(), + "13765632281005573885638376819890884715413389187071206345092080625293363780493".into(), + "8481595129822383153686510359174155430289248263706955785322350737378030164791".into(), + "19662013748902527903033807785062252860528102610990787703983459073558867314734".into(), + "9308675917577065974819002737905225232234719968566022906777214056034139366532".into(), + "4460305046275724592205653945612957908431797179457871442468403547092050276501".into(), + "15300682822970503133550740797435695997154332692415846288727432330759835456273".into(), + "12237959719178772655779639362705631668446778143915144512652426042298606318280".into(), + "9074337685905972785113091085216241647906400276520026931456332814507320753649".into(), + "5631451392609103370493346239448329080866515797549576094657011047227839672541".into(), + "2121343968626452536232091590081231792044261748288461026059952209827486505534".into(), + "13428337287072393077290174792209446111049614088462784414150825648173933048712".into(), + "7637923455564736402006004688931010233604652911202725935144306827988683195798".into(), + "14145144407093909228652602933032447693975357132368479264731012546112843610158".into(), + "12996083206893173697199261663271430611160188106624571072102983887472126762111".into(), + "1731425456500536573020772566007405873501250425959888611809221924317821607399".into(), + "7235256019731839713165664289644294928953374624035695945735560280325779220974".into(), + "21068041929715170328271489788020516757451181357080748023363808822374772951544".into(), + "2613269578304019608168678260820703130619030694547890166073793773931717299431".into(), + "2895580764674798551230549071717806800560472156999393716124982187189744328131".into(), + "18080627279556321451131957341924578248984242962783250736655220087867816491510".into(), + "8541787229490752355605354050088489559660583814306921152210039891158051673167".into(), + "4773003619955976464416147076099271361363813479523212833233541104584120420092".into(), + "3902657294907605469046100206491238707374466860318674919042657295835038516976".into(), + "3278680356861257827773728996810819308957995332730291009303523122858553038927".into(), + "15799387696541061708257049935405397874115977721965461912710489016029411671536".into(), + "772127417921361022785917422978769290675101599783616547948775836973315286693".into(), + "21172675246800560416431972802840350117631380023487771635657330436402785697771".into(), + "18058055216124783386618232874069776997923635894599900180049394490069349321671".into(), + "6486774126469831729812548493701084245613732338682698890925717889323571075377".into(), + "15523424894706974280109344562353257665535312014690869100282355992032362528753".into(), + "1155165761749577539253357611425533199963683430668786603835581376097524220748".into(), + "12293270303409235416382105881403663503297523019464344830582632347870203818941".into(), + "3805029697086536368150132668072389131584837710357108954351676850002928826418".into(), + "10383737378445903520223858424352769490012472500810730319338019642336472424460".into(), + "19139905240360031959084644666653677709382597275601060683474709422388012451876".into(), + "20662040367334915413974593999171397527205617639639561319218396170856657836909".into(), + "20693045501044506792047932972050932132614012706885392044610670760209701329210".into(), + "7646436833143149025181986338139180317049527836176583246163126009131421429218".into(), + "18468267029773286403821867293367277456399346317458918979624725373649687804739".into(), + "3368837325920673645463134045719391515049969019513957645401010913338888270435".into(), + "11009184090818836540140614853735105475441340244881441725231380311734117124613".into(), + "16438249098211375676141540117467319174575385934122099955146653152842543524308".into(), + "1978426318655034633621003481179913733408584009919893346861360079137199026039".into(), + "10835870076129494328875329193612835430557328436367520958854318265497818180707".into(), + "9238585128656275231406470048000584932169168279280345637986485301554767537943".into(), + "9860408784513424998732752702463731273076404557148376009384338221275998841902".into(), + "6200534253629734756957567067039457925613934220422888818001227120423772581272".into(), + "17839095320044387599555680620747734440255270476109368494374200849742690805825".into(), + "15852210333828297391427169949742706715097259872699655846272917384634556986629".into(), + "13333830495117301018203298033441593118871576320662915411592512406905971557482".into(), + "20141868252044007123768212581882815770873248742867175012988635705853879924105".into(), + "19536412434762756561256835516560484981006774488160067264157868466806483409550".into(), + "13376474825075395326396381198361278687546895119831914117080307993251668968861".into(), + "5426793384935877497416931155195289044246814775306615779818726668069304816143".into(), + "7377826504693724659923090551901604172560455391003832805477022207542742841455".into(), + "15304023433512090811722731808458537014494291201252876199903945357536893662723".into(), + "21332460757344630529450409218029849205820804076582364819572498755186176235236".into(), + "10123770219692666599208924625777509883271312561577690589112346574716432563379".into(), + "19563297971101084260099820862783813929179580711197283960855071977068075400435".into(), + "4447207254069026895839688542596519844298864167018271339727580848327164733569".into(), + "10805247891957676953966507705079087855877568284903068495618773980630496028625".into(), + "15247455859749071900237435930523136345408272002648820316650110622790580875976".into(), + "1523513713815340967949299977561986680730112913862622151113511170812079722460".into(), + "2250092345142769832690429177076582432571233932766644284160212961505086786198".into(), + "5502205194489637815685308301009607192191278776537698439488907603617260091996".into(), + "12376110094727246736838305034851207905481280418572145688559541407100489476563".into(), + "10967543165217223191795950135818653456591208931427429972754586323384063916713".into(), + "3007127915385490247377746617881910295130312971235615226220093403044166435816".into(), + "15039810425747682043693978030807314591772830040893968588297240388685740832440".into(), + "21282434502237090849892927101680045504469095017581606642683944529102929897154".into(), + "17820554077495649709432871866725766383400924076526622095222498536082233385393".into(), + "3238921346058569633798159055732162989904755716515207404910193961449538091755".into(), + "6963065779232897395043653410311694992252845745677523471496668662716409139923".into(), + "12741150889158293058202855298380167280366511627706735086836055155215291856874".into(), + "8505231440570036863755136868680092759740858472921139738784962359970555463386".into(), + "16263867309272856871859017360735124501630350380225439505739588230939592341511".into(), + "11994652030543012675378700484156120788799454178056174639173766735729691200407".into(), + "7531001745062176388176369578077819605743320864253593427909699809569190244306".into(), + "11827863847264026226445842677102655786795007713756694062525124054010199531855".into(), + "16129429107591815909860445595042156146087129962390514833959473605760944146527".into(), + "7516787161495467824155321560594800913116855117833255279107222188682607365695".into(), + "10052162714097814491117037623656731155747350728656529965818461926126557689689".into(), + "15866126693591846474201863966897712396582450897559972801925967203045783070614".into(), + "1769345590280238074854062499047542732649300047644326060581629862863392048630".into(), + "6419881722518831944067972549598430355215508824227908646199374367237336336567".into(), + "8427762909619307952658968866443552584787988597438819510120993141703552873807".into(), + "7893311334904009120458996652611823451343891633360441850908461624396886564456".into(), + "19760139770663625588230394835227131962944058142185140281511805786245566094372".into(), + "3986819882418057034478817109485859706183862449050678809809350743539886112292".into(), + "19462740190201175689471975116541495793256811137828607475310915853573746549315".into(), + "4950984269438746319161544135494005780273536398471035822969257463380210776783".into(), + "5114126436371649572151778297196722433397981560175541938915777654399517384867".into(), + "6500502433233986724406965295441070598123907669991330544723786166137230830713".into(), + "16317287950659298477752416750967064455139776512193467637272228586615836234506".into(), + "14644311768591599882243015624322844079095287900058309581805861693953854143586".into(), + "5280400910054229241086158902425292691428494311972092134978890627089874392610".into(), + "15127218399002910266114392708998449241113801821878209170134753716497261629315".into(), + "2711401246560501813405727886361637939228317090729002412066290100904319360960".into(), + "1913027325164607464956755298437679573731669261937548387089448543475389709293".into(), + "19048913840687946111515885036078245399510860136071611921573959765837451269059".into(), + "21295287720552550910280526966903203926240851680963634134437049744624969307301".into(), + "3797109693086078196558789257647252246485873371501397666934942661912968057995".into(), + "17220092189283987679009565165189257352830222471797888201205665583060198133070".into(), + "15728859045813610297052814756440978974533041326711446421367132340721185641530".into(), + "7915246674314292718027357038683169057328569313936589479968179748005116348962".into(), + "3168535062320575750000050980968432832479836571078965478747377498891053785750".into(), + "13803986818738311305292789973880566948405947108749390476554600214529398575121".into(), + "2887021549045614886550633658248390500136948561362661868354602793719521054017".into(), + "9835777853770722919097370759265493857673816349027354889969484370501195735082".into(), + "9162920843634529872148328448437467777954760966007230180656824492469148201817".into(), + "1074954035830520777949184022573483523978030205622574039670230048399504082838".into(), + "14573949395382273725648387764764104587706358354270169802238874779976052312087".into(), + "392719645589691148855449884443988672799862378880367658864925364810308158541".into(), + "3685551641667332817505970777889874263257498648047179005679069858878239338060".into(), + "21636266924388678043670779518381645484139129597308274177295703055588816929542".into(), + "7321567536895702720774368439707249290671186165402244403568878177007380798611".into(), + "7172839196695686167303608986523055739039297465612557351529643226329584487820".into(), + "4229778574282653726990227569322645489471840372440828936529243607112035640074".into(), + "18782302953492985194605877986375464862209789031757664619583780308051670003641".into(), + "11974809482173143458078984920442199204778548823433238304966088658895546634996".into(), + "16243115558222486864600123441049281534776025041710213515085053285162975382052".into(), + "20774412501254836125508172987964523733402936236671082985700605339729723682856".into(), + "1483065919525318072073144044234229344901017930590205131905224416619628808251".into(), + "13859608645215082985012618178187462696738961393768893322969190913266642570277".into(), + "21405376459105682082984005097612235541120033931778677874212817450507078023807".into(), + "11689238963312530686999514700594656275688886785400717773550428094425955677451".into(), + "13770805346049970815658755939543097628223982878203541747699103196190562329538".into(), + "13464128750778834946158208517080192694313764854534278881787962107045396572111".into(), + "21407849640224904790247752730042846775377573219483807581870525715728151019912".into(), + "17742435015936027530371893290930447550842976399042348699434701387859868436168".into(), + "21607472280994630036571403820711131618277491312399185275464826427174795496015".into(), + "7688464458545967396781600592854525549649369419708847302997939388491317131965".into(), + "4387376978957625118504779199367262576455322502411234315349687321331690532595".into(), + "18931911591990162605728772083755015441650830148575725789586998382906047952930".into(), + "14640568984073443762298448827081280142493018130989596534711086636304928257387".into(), + "13306842042380077440486698326492089142513016713060879132515526658677539077869".into(), + "16831808713587057769987931635351931409992316524094756901264732606403438287007".into(), + "11322415449116068033081427912158622347193126836157199640557951056169543153666".into(), + "6851604032522439802523441319645962614365864202922586227207366934548805743971".into(), + "7865033453112245503939716349145002950003125074081779764874079104952011815860".into(), + "7859039670086457402270651853161690366745490673814388503748678622683637617076".into(), + "12915205729177842184089675224634811499690953326168778547490092950902372657871".into(), + "20937773388247481346344394224147944853257697507105144044681818248765914256516".into(), + "13287060897761790924722969858390853273555324655965292728828260759103061260482".into(), + "13306896346810583848301553492224587956204219709116827141735317644044595737290".into(), + "18219814232296473414944129594782177430971511097498319923644038792152844524438".into(), + "5428298390273304248312869576101694361963883992477060046151268450358678474516".into(), + "15201216935560530575994837331432805679558316705524948725894686934721622162910".into(), + "9185979018761703837993382762881735970994413889163208925197647077666234876915".into(), + "19181785293325954761013200262201782189994371972312628530578184844339518606203".into(), + "16637687071791710254394571978230957592057586799407792249481371375691006025241".into(), + "7540695380854477789623159801158526233798259867148336812702557598270412992232".into(), + "21033749924623312349444484016552328468392604073844480133610758629121621931608".into(), + "9900738449713019488539657454744856913719757496206137183076543026413680378482".into(), + "20521005306019974539700555774138941983710853964949502636832851583991666071090".into(), + "9148625417956423696904186924742393962087070575705980963607554918018064302410".into(), + "21162567363216498523188025354796572574708404245885103426215436531517848841283".into(), + "516078530341213545969683556809078278712251743628535410360059529914040404316".into(), + "16846240853341179628895683480146570801780199580812523539736983428470139888117".into(), + "3725063466997152760713869763783030250548722164672702170123853642540240150907".into(), + "15613553480222544404649584760559934580548305353382956847869893563076098726977".into(), + "20351109682633578989341194704546173168875553988784930480177729322649037322556".into(), + "11996883326331293630396983616262966081550223706386102455796773922602029237495".into(), + "7467763103586466278784854382537524995001493410600869109792768087664508057830".into(), + "2017508073008218056990628353468704346479637577205333604607959225275229259220".into(), + "1314798328573144515884513072855778523099366140621231925682355151505185465849".into(), + "8723657373526005784062072282873485835867170661363188128978957400998918352651".into(), + "17008566456391889354408264184115222893038736754092176958021304795161250142258".into(), + "1277201481182954066723110290486484624236238076832533331201564897838632505678".into(), + "2883326282329572573598657327001707359250291449257261775578634065330832194673".into(), + "4435817386812736743435792142021329255563452933485604716406076651024269234049".into(), + "12698961373404268899870565736817085990987024732021219796534551825706597317720".into(), + ], + vec![ + "8089493102530595468824649860529717181797071865148765611726566631095271469313".into(), + "14191702863884040950201894968554909828474263688190658890761244249722339089253".into(), + "7127251756910107506817428481560230656411897313679770455026102671367474097624".into(), + "4637045655841785226199626823170615348821917492997807409130873179711115857270".into(), + "20694397780982417522377524687614391225351058648522092200738043379276991211973".into(), + "7370528537006777008458800981771544164650999525791518443407240929647301337224".into(), + "8357863226135085648491488089966537213596680483394798064376015085804610201240".into(), + "20367087512494301090653054692863377975501664994817680661442168333644299005261".into(), + "1950307616347822794878104597377932622958317394806719432040871827549672571148".into(), + "19534568412595886801081532478416580190048212177563706092280027206347120417736".into(), + "3526428150493332211163778868665379218281231008068537115657136020226387337771".into(), + "8661888879209475483716403816709663500534119170711151152467249807886559994385".into(), + "2374871949454649266019269203973683966955509927334059254254180194157352943840".into(), + "2602346264611026079459352146265308073912201263350931622963181149514761751618".into(), + "16750875216633927741061710170647391823629779494283192027015930006045661350833".into(), + "17325348607596842041611786882495470980592459865615213806942169413086586278610".into(), + "19257833407854296241609506861086921463964253377452883026082737974007723285939".into(), + "16875536222414380047765946704936299627494679557426090013440678117262080229388".into(), + "2035577529925145134060996791483051399276402053645795728801937468290487364296".into(), + "14222296831200170749164428995066771764920247405996747821740895709606052517407".into(), + "19708208883712347371628256596476366883171584832295648266624355950215220127904".into(), + "9765600454835189412776212142789582666145342479104764891694616693066517922502".into(), + "13365055082376018935548592209736650793571432301565333849832704476237136996118".into(), + "12420692692663472732723794387493491467146971343430969855362995088112286983728".into(), + "17844493444787722109223680249951335211927705850925719012603471955916755628715".into(), + "11924944537382281343613401541176014853361783437608625866798343540969055542140".into(), + "11538333989403053525558050588509973706031711021450280471202165095037142068477".into(), + "18764881783775503232423409005005138632447539466481045749407270569758537393398".into(), + "883162740610443285913150132085694648635519837269866659825930623017971541006".into(), + "10171610390436513861069093903522467940290613868370765425375996220687957422386".into(), + "3782996878040749700177878799895260702330488343333700969514291046490113056911".into(), + "2239298968343190621001798397134314102896115970272480212453695048745962826317".into(), + "8391302051404015178833807081994898536482484463022532956140929200199667853077".into(), + "18096164030411129129794470313655227930799185448547884977984818798794318221345".into(), + "14613424916575212836238688482051323957035101724778773648452919665593660234946".into(), + "21828842930708391060823304507355770719075227415675853536112390374449711667078".into(), + "16155169369395425892836659730715232159396910561858841286007416017756724971516".into(), + "6878543234699575736262375462702866780772252779651204219697567141737826275015".into(), + "6515580357485419559928692633356133128461299871887195169242750902403019378404".into(), + "3124270150531482035695013416051086970123461406854560487654035672981879561715".into(), + "10574698281815682641771693421115734396172155905702803455639975437772362072107".into(), + "16383053320907906491543185419013182425229207444535577699313377451249772804551".into(), + "14128602818455692582904557734609240140547347183347545360942329621911511704432".into(), + "5010441108136365108046592406551529565385874487432431586303802326844966531017".into(), + "20105162467673383983751690623903949493335895059047083944279780871549558030082".into(), + "4243524359837792598965046978953117623831146922720880550516255054678204683627".into(), + "13992943040313469850370025986460260524555943628212884466234808367632688288666".into(), + "1356223459509978352432345061666791804860823828908318470584528377749655994659".into(), + "18723939192823222870283999271398340959482157197116460943968917507878394278385".into(), + "20636603031793247786862933945150382157149390912061952007323929980504212222032".into(), + "2128636310217902240014588202116301452804441854659460379597999718252532068242".into(), + "8892458127594737495267008183431736534386964959413452688074230438040504386190".into(), + "11063574691797903196312547816101464620325790434381460261930676459857164737407".into(), + "6204567085759054728130072959301910137692879386674785283047171198023995380593".into(), + "801138962072186678791979363551434811728526970692377261272697294596118718403".into(), + "12941641588837981062781447688458784558606932072335320068724403816548547438675".into(), + "1341967686547656677059175660053506857201298028878352128737987225407679621418".into(), + "11183207447266717668611438117401741351804062326702937903525908319309155043524".into(), + "18507917680426998547390822933408085624123062783706353845158832700169805897057".into(), + "17600956758590554476691669608345000941959084040124470813766683278150335958657".into(), + "483394656899715242498310209793599026850663551066988094136788591643990355221".into(), + "10090827432001200203094010206359607863114405903353400294360808239534902160627".into(), + "10039403020393871677252553778177538625214464462052976366251977746523421324197".into(), + "1924712848258707645798108847678093621275398467857399987976700805356929290674".into(), + "2167923809975069342404582706442301809782773089534518796665536897555762931998".into(), + "17151337466618795643803903749019481111634005237742283518609358703199930418386".into(), + "4693404918498880046514671012411195415185134287680728307646808116832211396470".into(), + "4839381186041991439202094011010050618021148651243083039175474529769659591768".into(), + "6368336559448535344096548979708211013055912139451892726345028976601375052582".into(), + "1969826462127986113899121152613324197819709993940588880171966228829834184618".into(), + "10949071784553227115382687140318153702912633728202525040498248461299029092720".into(), + "14575837988956266060370685891330921140979730144767405243152428616625585430364".into(), + "19025750374860322288379311751788635906611708349625974065695544669443137256255".into(), + "8764838805814072278932908632105809429351124473407135322177352737762640822858".into(), + "9508966145895699172941574119083482387827406291145211775810415653335356563307".into(), + "1499853062814132179278896768948580432185360181707926223121987796423000161587".into(), + "6748895423937754323870965728722563272366114856617067613970193226138372983547".into(), + "9814536604142267489967579335173323116472525708739068285104384748406479290252".into(), + "18898551978119284539473648820403307265793266184829523824179430658011490726286".into(), + "13332891294455702171134399009213907075208764727059236543259960129130320074982".into(), + "5806223588823614408610900761814632517803554600662169829453323047489266087405".into(), + "8600992771060584427141790133616651912076186875693672268259185811464115092188".into(), + "2345229032656719135417406942532115442859563039531904661440827899574284256939".into(), + "3389225209359511603097670682835778418062727662626674404140526378611375444627".into(), + "4001034682434006987902788179906586121095610380401857294704332109514659937637".into(), + "13067063794999809131589470979742146742674772176859338210659998590141023560073".into(), + "6021149641876662294720012642501582990883570287510418620500730995186581678990".into(), + "19744062936660555217694711756418639275110313106982456779211457821487189038259".into(), + "4280146833782122185864346406516267200729946534205141990397627632307276252746".into(), + "461380210352497258096433274264553487092816458993207056498455795007735775326".into(), + "9185904966475190600688426658264723956468287249470549941528026771035049026029".into(), + "14362213733702712122544208087232387435453213772974406963783416068708716342186".into(), + "3376133122839395926880247692021311631985267298172444834121553500975764354590".into(), + "15430636928872496244042298024957854104479533547499980110816885404855993587926".into(), + "10066678820787654667318707571747478964216410231002799394324814978870300697084".into(), + "13679726663800139966289976616317518761749708307387522908283736372782010246091".into(), + "14452498713182028708607687209189129972318024710956800636750706657647839477841".into(), + "4665977443169087186016514814881141858109411469723890078365751966690257156734".into(), + "20673947333628935106924449469424554002781432367358546714606202194020160454893".into(), + "8371937803667063739943392730412639954872585103736021824547232324141910768530".into(), + "4915642891224254203187535229956007243890165147322145197624420790022741880987".into(), + "17737894427474715513464524059598818432164911920893615068415915800842574129123".into(), + "21257751286440468433003251296883236318376753833102432675015194790356307947025".into(), + "11831044910484530710733100158936923549935832556815349526083442021617595246222".into(), + "6362553671302097483758288321671356788227750730995074141338726755028234269753".into(), + "14935971406087488699083625022278942628959200513244202282294495763807242526556".into(), + "15337281352851760799121170871115742187805714822038335053341937333897883894609".into(), + "11913020597445264316110942608419581486071629740611054234626537590474050938474".into(), + "1838104841451540707239841220222372684744879960533787766800248740162155353463".into(), + "12200586110950172266226854658183951607066336178441918240404611233042731023944".into(), + "19022187774400544497696484163376638844589760125070799280660843859892595613745".into(), + "20574219232807646025576258258887918895888511977194786423008507430088227970769".into(), + "16786939539299167614623107235117810634408742454195124075763115939760135086779".into(), + "17293110551721019748567259052064171353712124247596873005371071440209356535788".into(), + "3515336831584582569659665150343000056186787768627351931657274076732504326799".into(), + "281425181255898711371985357453012506419817930424882315501470435479210893648".into(), + "12703632225346671660598644961986122293501524448004046573092229475506828991982".into(), + "5589259102510829346047060861494427710966525350274659545392525201677772096380".into(), + "16136155264962057601674035851031539132207654054954939479993843931937787809008".into(), + "6577810342827559587628942717478127045802616172145914985109615391025970118111".into(), + "21163359977438641650165237544426961690819476598192610702649950529921750524674".into(), + "20111680006656824813833529237249851680178501017371275377838319189183351852836".into(), + "5575615948519348213741695691025005079345478514823636124237182535409145360452".into(), + "5523844323060926872300411712160500242292621601876678751811292002367512597282".into(), + "2341781247790586645587607723874752210647545627417738353111026163429313125329".into(), + "6287726121450538785847268284295299220404766172163996260292255850387839422364".into(), + "7510648235102286772333427364079209113816404370140168905255311267436378725194".into(), + "5010598474035699846534727472507206499428871255705467310762876793528248455251".into(), + "5700900579731011250748267725185425351062102227041063523439252105791254824347".into(), + "260864497479086430816275699586473497971665832148788274905912958670548982986".into(), + "789172560806161139006535038197594038879692565830598745604163919464033838458".into(), + "8100205286609553283468855217617464988341806863414698413875768149192804914582".into(), + "8832923720458983936387697888604823914542155000760959167380458346468815941677".into(), + "16408716740290439442219096309603613006830415688534254451854294128560591562449".into(), + "1113287770802944863484309232492725673552460311886837538428140710794437752025".into(), + "6321319205351235108330320300049882062942831936257890309503365991814791003082".into(), + "18560922321672258596191688202021423732168869299915307979899958586924038420673".into(), + "5189180998718572459539043290302679421565915950138990634526371489122263353689".into(), + "21059088895950142731136323564720383671175925416797251043903234558355128937274".into(), + "1758538453070760465213341124871446098960577889968251388551632606564361922354".into(), + "8880106936008308891697048583874212287093823544613405494775675377028797786924".into(), + "478113991762053058066048308336221551443374933771727184756201865704929054005".into(), + "5364027562586142278013801938849728210749918309705526248720112356632858758221".into(), + "16060418035757996187280816105829974246762721308664137780198420107462005497738".into(), + "5211176330971465153824216149376981285606706263050338982115099875409328929990".into(), + "10368007546243825628853187421535635929255384415227635572356066208437184727920".into(), + "11427684168459596442778160390157786957054817508127224050241732612892526145073".into(), + "18949700777601180659579150208396846544227381511915556752893455904600581402034".into(), + "7164876529024763696100437397763470424696261670104386888341279210050743231742".into(), + "11672893202716354350665939605724566655297144836947407230492353459173601370992".into(), + "3909710158111676258105703555994764863745260060728195069107924779522169147903".into(), + "11345007057187618276350650167405783488453459256756441881422153715183450077456".into(), + "16177820285119478894470005926103825065477859050875380004945625842748711696023".into(), + "14605787997931757094644238801452595674783310674883843033832086542610678460710".into(), + "8394271758834499906719701243007064560791869846306715272114566194508664069904".into(), + "21873434283102736245937985212570921154785039540401524103800440576512828034687".into(), + "5288693737755970825903341313388382553570352630888453317445067182115774973600".into(), + "21527468641133089700262079461319550104586512157305923587305104390806741900810".into(), + "20906707665998407830558869509347677772600060055286961495556134628147066213175".into(), + "12592377229521260472205372896527756013482774884841906928184642093380092134349".into(), + "12910817347876296654185371831758099341950752668436921762585644650086702363745".into(), + "19707023277054651202681442144426045379962145417802880101371794305270012080065".into(), + "5370501568233094600905283140114160955385907390235753857797923590879732764547".into(), + "4893525410028747500809250162311718717246192757157339358355279414251440821369".into(), + "16933412769230696712087772684638356779344465843185712014276708361284654443016".into(), + "14752252971762751211424871408209457845535321896357466404820315342528474475265".into(), + "4381439374316418237204510331759065345075613480172639747277311241150560517970".into(), + "18983302468109324889867853797358234303151484569217755559414917991855202421044".into(), + "1720853637877748574561923069656227402993841601703014320322493127808042278354".into(), + "18055674086054269831422192610284879350534759472987936156359168085840018213729".into(), + "10022985435599924530950214242280707656465340100733875531414649379326713485700".into(), + "19489289237398317489155396973411927208825921597467958674111652983018260376467".into(), + "2483969152386321683533021854216836603663579724438750369420802837936459253247".into(), + "7096495647820661985493920714559517131838710818830485847136203068929085317377".into(), + "11239734991193991562092142618442979132467959527040707676033760692044595660477".into(), + "13964689425465688308583323493745924709191744803740806305195114536185949582431".into(), + "16765583167493792488003846249322412563932361325466895848165782639635509252698".into(), + "9258699025821383775657130802030622351199780460158138938453769223645462201380".into(), + "7261743362549790776698831974802523106349906457536556119083744106556343806039".into(), + "12174595715224479314914773177369125156875875973781826642881907250708051936707".into(), + "5012109434360646519756574765661145101042498088812514145017131935992653466441".into(), + "4700224901573028234520633038757562359543812027452948633531739331172070339917".into(), + "5524276305760203869547781419982228286567487292869238897177777816144717638402".into(), + "17095081617164642751160867282346616888601375485714887290060713493809720024377".into(), + "13598351826191942256399407492736185075239065075291047608049023984461266141081".into(), + "17308136644455825036504551600136601372687794695239587014243640872267542984788".into(), + "21730115551932424945388178787861491256026727823117641122401508483473017057216".into(), + "5949879858200957394974414439063213931632579243992797429658708423363937192395".into(), + "12803563373135008849577150591968062878999687541475931973931310610530915756774".into(), + "6197313147432278847012935526443261694774265540021863115632496255704682355001".into(), + "7028974783929794816060380650452873559926928309476428464943277081506765913122".into(), + "4456589108827474318002287720505015907920793224049488679883557016295128273089".into(), + "6100631380538490803600079371988603006023173019183128962118450543769518343550".into(), + "20177888580254527519697427670797609876974132471313961205784103868094735572841".into(), + "12073238358529645427878918495905605836088922482621503239707831646933770345610".into(), + "21856100081180867115422179532992210187999850914786148325024530634866939637660".into(), + "12466984235357303385071417632152615871825808908890287534009467031177378240877".into(), + "21079839629488810269856967110699842674750899640941851601070908540874579869846".into(), + "17874776547285892135748821889881943162098691701279199858985484844123833691703".into(), + "1051310306073108765618056817409777616413695911463346454738615844650030082984".into(), + "13201928900083471438265807470373218252839081683324776881761206414378175945385".into(), + "10190119257010511211543154766347424644241552289850760573785222226686792626364".into(), + "4688333485355359912766058497543573340825697092661609637162867722200016624599".into(), + "17123765906835174464437643379916457993692257252235548439872663365295598699138".into(), + "17134395901615480964750415429455088485278477136528521479550104193427850309670".into(), + "17940167838269540610857156370913968688439335297994033024607831012271846654207".into(), + "17211561414938780959438190369003172382648707317417933395371238331823656001285".into(), + "16098871745352156970746489901084198750790375003919623885201193800062775011577".into(), + "8110023901773238127710250492301336333009382423651067999842110155158638763822".into(), + "2422126936887420595943345532312996428472865746519279257114791700143799243285".into(), + "10404075183720133448708607844137643895914849868988851944394714021532771347787".into(), + "15058460651933569411128043696753704901683095219148372416995175617698935925786".into(), + "12723555146577954839266714779606964953587444931552430067661560518513484316738".into(), + "5711657581065990142612711893585463369566607394626940266394184697079173482347".into(), + "536257458004844094090200378771107142207864145916201798362001449936167413480".into(), + "16904097338561724274235483260490381087656469057354151613747001642006805270380".into(), + "5885951988825802528717928331501311151617037953699202264796404860914064325522".into(), + "1638374036867203230684496314040773233229976276864977417441853704948079389600".into(), + "12231982678049991867660964493898657015614690001085229646831798098083877705620".into(), + "15427058040900308626278462505422407030810836290013997730209614367042825008816".into(), + "8287021582226423932765127558352821849475385690410389396789209560743363809623".into(), + "1082920048469231434960727235146664410555571531264546962637788898495946680112".into(), + "12010985569823806398477426440616395624594456642130882649075614403521532359981".into(), + "1296330560506042268524168200578272313263311415565810607634491211599523487141".into(), + "13952994457760775342452243315330031022436205430079469228726234965837656704462".into(), + "16985743548885988875181345488907967382109958231690477723834234297604193256890".into(), + "18108965571078287240054765173491615799731924163046353815744437352944165909003".into(), + "767530387704511846542067425779936904920481384673999726390798572580115466105".into(), + "13028307645272598127894992332463015335938472044187733546491605798735426870634".into(), + "7618140648066747223455845067208730203353985191563779819124185938438241329003".into(), + "9331096408013670718143367691921833484100954507332698681105640867440874379095".into(), + "18801244145798024592964955007270988394935375648640247134868164095028295260248".into(), + "3795459656522732094044639001176736675357207553263371458375866920248726997086".into(), + "837603424793724140470838171288512417218344963365692836088493443407197621528".into(), + "5831962429370013799978251611601242499647911352287066127245255213245836916243".into(), + "15608629818832866779793800587027419050315344573115273720682841552596629193016".into(), + "1032806196152635164955947370516112750756471391006183599859124038948506281039".into(), + "15696208732714216759620197747787990886188845785832889089576996959760026394301".into(), + "7234583861372635173862436546128104054595325252312930586077823639855585372879".into(), + "9351673222157837029929340587547626334173727344379333167413124993232252858910".into(), + "9944025695384518607461067522154717153892207999526158631473359210163311199905".into(), + "2388456762928315498284690717786606118379553818175612967295585468860359388241".into(), + "8762894023280585818322989976434413098781364467068028456427186735232945062041".into(), + "3982990592887978322236356963193527056120817698005386105106663667178275102339".into(), + "15305580827228104504176089928458181616132990558748682555478070517971443343847".into(), + "19329984412710376451608868772829323267202139442902035837598118892290482040500".into(), + "16858720166203416564086563387111926247316950607110418622245689767918477559853".into(), + "18241191751089003023961171787624834962373436019307905123520202159673396473314".into(), + "4235718345809909430818933555490446039661741163040234575769526351800272747420".into(), + "19336790216312697816897624746946849815032853517670504993859895524588720552146".into(), + "5833407345693372936993877824926303626341316298366978224289402960052763586750".into(), + "1246029211601050773151495439169819001283925818386531745942667315981536442422".into(), + "15220086581176169769190817430254624676596374261069714496878816359393753960898".into(), + "2365861900029834138601812494207710261363070950725532631535675557556177296586".into(), + "11098939996500166020441603457997996414027684936264124308565274744828953147961".into(), + "10188381420146390141161084654671015083379584925005132911919536024814384766817".into(), + "4842017639583023386245773507388835267437650777401251470903076835565728725044".into(), + "2473142702350888708640145449504461869356502285425231033191892981515210840933".into(), + "3420689338060147901034254227618353913575873772273264928595432321920193621195".into(), + "17283074258384994096612354354366039962078336488852681769982384814157770475001".into(), + "21006196209784588036771700052971738964442512933084691689593009338899209077510".into(), + "18493924441589584713280363204662484496255740376612784432044649586411869984552".into(), + "18050342602178255577111253065667811034294368946566845729063425104069824407598".into(), + "3678841436690736561960339537207788725774745841190361109044779271967419319179".into(), + "7229032436003943468306271354423246592397907094971085338195794532946516086746".into(), + "6074543407778673702938004547803408348112315194664133731027958879041743082105".into(), + "20066840392762178519205386382693042859978931394221116082945207576871299356172".into(), + "7392805501563984757154630589230967797392843859128971344647830779113836888313".into(), + "12821992933916071670969878929371658252743368422895791718047075564423909142719".into(), + "17668945380988406358182777882021562738916627352451654449781528301527115094220".into(), + "11046977583853814151360987998476416244370762366051844251247672138778958746421".into(), + "14002539550635338734484852844372585460419306414704948112807545053902011590061".into(), + "6606166606298514036368338857611676551502463523513184670555953533985389963443".into(), + "5446061818493011809543734117506203755719984305445968514525154959975352640311".into(), + "1111668700238284277732413088411620500087954609586256178652323895010927817926".into(), + "14149444834268176201304046807022441432518948094788616136522350328948362984806".into(), + "11145589672768136991149928307436511325152474561287303838823870178226307903252".into(), + "10735913608001838681702607049464645919999926391398426712273901727922068271016".into(), + "21604506935173200936527132172076877500255612688310066661071635553990139487164".into(), + "15839861321575777225084554553361389979179161218821903561853968278717519007256".into(), + "4119757287481158134674144154651639875410119871143992982693366998676854632059".into(), + "21215853876753845584435912754954004476252409742434691973232278809063267703513".into(), + "6474112081529376586123095858950370103982143188298415134572081362206457634840".into(), + "9652012486829416433331453480518336590399776148133427672040049807230079809918".into(), + "15197188648271522366095502604900923401004235531330211517976306351199244466155".into(), + "20653133196783868133525869637730316323485671920172550159454294692576016995406".into(), + "5372382735415117884334788700701478755716540017619541377211078296573383620529".into(), + "13207488856826369213621681118482759531513035177166234417284384049787214456366".into(), + "9796874023172008376751307392327706745295558991768659529793122654595100420492".into(), + "9923897870015737084413253850361127061692788482930375413255676958440886663490".into(), + "20345695890485642585927456012849850866425595188379347054737827588286474285546".into(), + "5970363931303429796131779726714445177344307738485323531135738898520018230730".into(), + "11099832493605970375935052305980301651220466371839450598182845139430995541342".into(), + "3984084259485258180085200952180050171170098877962423607492480207306084313759".into(), + "14795772399510400766677766636479617776597353783628606580152075755214440944984".into(), + "12411649494499018184602448007465543838785542392797557004957418343044512599298".into(), + "16626394877225947777126043865023393002546807148362691512796749460115071596438".into(), + "19056586788697785123807358806982924302021705802715675015657752728604200366151".into(), + "4434453689868848221439283056960830923558217744100875597646474729523600908085".into(), + "1558497076513757145941997689136111603376893591128645477322800957597462648163".into(), + "19855312204158524284458047353907697917092904349885231039917644721609392348549".into(), + "14296610840588714282305996857377052531476403374009704740496611471123591762688".into(), + "4255702919775232787836264045412364184259731261431115977947396605020618214051".into(), + "11933778709913818332041068411271656949772987367132405709674023277986614336789".into(), + "17039721891026771364303633162265090865698196106396237296534502784700270974342".into(), + "17677496806049413057414389850646660368467945835594987026827101037177591654389".into(), + "870853693457310346529819971157542197768087609061398183617507613118675541833".into(), + "18050709179661944189572532609164386192084249162773126990656248077533050260547".into(), + "12509892200475964347152748879404468874956023169504780638942472032501158691943".into(), + "21290469710465436947459252705329759527038443945558987181251205171276803887919".into(), + "11697948989002333965655987240828667368730889495575710652080571936823918605037".into(), + "8736925166569662118997397920658489847012314542439353474077461070832299486573".into(), + "15840706598368315931966990863663232282633707371020640698077565307266653295218".into(), + "2336556619780315190172318504866216965057876522664932909981631800248874766723".into(), + "3401386693708589750154291055903211427275255738949699968058464589080271508279".into(), + "2985234144487193053560662812519604867522985647272968908108472491424983944847".into(), + "4708870044417079288579972819522537183137753616055169595344370650267781985451".into(), + "14922125937386550210569417108464732470846637538443952204254988396764974305556".into(), + "21120247382250540544939703044577215089934389719849213503558017786742393072448".into(), + "2316928219829771701890891437885406935408196453098584469128414556890718887397".into(), + "6617231706636202699892427347447707304283374531129890683057423079735568483257".into(), + "8090371166463272188656489380541987815472988984318384965972491395697937697839".into(), + "15553944362864127106455863696894507934518908899701997904436263710171474812555".into(), + "13969363969212770297664533823398202808376213044430873505018872984372178723135".into(), + "12323663899898371993768411530695190245261583246438004058508426675150745422627".into(), + "16566875439359417854141203050455423662432117146587478868034846190942245403701".into(), + "2085197407488705803446596939115235993629808393250087488250514287409223581616".into(), + "3353163139852271259915937989527822937141797275129253304946488349312180510876".into(), + "12046433326685614994434242499247496702084910418498830651483913063626259071609".into(), + "14532850787151304296314608701306827699237853461438425922459530673831764439726".into(), + "1248026383149472348014634990743396120794718445584945878307644249641199424325".into(), + "8057577408830637105543973735712037717868264099646638037329061041095961932706".into(), + "483318248530006664922961695058460281946793595997210254862751288308569479187".into(), + "7597115662868588435202039449596982590007976221501668730777779068799655592123".into(), + "20576588045443131888269350588198092656950342341724106762422448997215666028156".into(), + "1519447772833782137513870714676314005111021965782273965574223992106447841589".into(), + "4564008647036378562957325007261730909909040333020122431007246472496053026801".into(), + "20058604285923748821870861850977236730661592466138938301165874489677433369725".into(), + "7444135345158592582936132362592519965410456021574373937334236087846829245911".into(), + "2619748000882337075816263795535027848943303678661676628903586805250445194179".into(), + "16416961968137667392446268131224700809614764673462182053193218154672694280838".into(), + "11133666903754924307114887742321725054257211678992195923482419349863465176820".into(), + "18185607668620497969429683073093259678523061297809671850245875139394070019601".into(), + "21100277417778348195596070047101971906596351310455360764416675735903158404363".into(), + "19242696527172349562445561603976204919773321047775511674132373901693664836452".into(), + "17641599298698108341739444819055281386405281781490793437098263180756526877861".into(), + "3867854105746657505538112984381791148258872175005817045777653312620913799165".into(), + "13673929044343556285939637616797217757029992003977067790150536754605693560092".into(), + "1413616068844936743999269099411394796998593026467324669628149625629672193659".into(), + "17182247321635044788965517464433108044764735758956097220834338897775232927292".into(), + "20369403412607032840251975012855741679474191692713337906759271231600952698270".into(), + "3368021137971787382116663997287877185273428758197831072626886100000321343009".into(), + "4058882993633327642712241699549337354407965426094339469485649412411970892252".into(), + "16745738472558294852806571713586420783108275141805815749526145532281107769806".into(), + "15581910313143599067008775890965264886765882549739040775187928885624726340064".into(), + "18274240790635707268508029122006654280996256674828118427269328392146795445159".into(), + "20233611060321484677112349983942832171860822463387331585688221516502467246411".into(), + "9578794697709092641086862842633192064390680557390699768090648655701242260988".into(), + "2852999879330450264744202807076238853213578558186322493583347517686662265691".into(), + "10741465223456896033565854764716115079295016404042872162238148987621341827594".into(), + "1287682441013790555075762279132093640356858755666376578858076731440259199594".into(), + "2754348824392215081276565702993043505186786457070961060283939332925679390910".into(), + "11228376276796390660691597656113154929593350154734497204665175991737933493193".into(), + "3311568804745845654494413785473574358224291288129470978720234105131364795989".into(), + "13909323234259677397453192122165186938471237491769110758142097116703508140857".into(), + "16483726454860443352311561107200491233365512138292033674515348800113826861405".into(), + "9812016698744294015002456058504358834959473085649393839832264042031387688048".into(), + "15177982625372891489410897122382147270421146795964397363826406495667593673282".into(), + "7519222254582187388553575935727848524972644886624855076224718541180666103816".into(), + "8301393036521557606902463799168007719863879180990998631543603658427343679902".into(), + "12294426767414989378173056202553334768408199886443392931845979330633289559578".into(), + "6739869506728400044391551988623920236526787529007157619233939477743084008138".into(), + "20069013437903411974438964103206724858007754851513756279456345172264534005155".into(), + "2018834934981362435424318060070780595968114685660751661802811774334240763265".into(), + "9403754976005906248280231838299490163038233707814936394634595857779528197369".into(), + "10417979303761863711608825801177810340012849177958033395021985945544086842565".into(), + "19604977682052360618680964990952175014659495782934855476845092758883928933035".into(), + "2688418347826037364876739676610309530415711729219616027453603183128414252617".into(), + "19429655781202385023906019671461539686080096105536288714975255299583443223945".into(), + "21850552082723421744247018249567438591533518178485575560955887428084379635362".into(), + "5617579950805240697000723938792084314286755653766678432489519653418680110671".into(), + "9555670299297105865403034351288164766308791892391888668326102655956067266166".into(), + "11495978801695516791606532228534536491719831441531647897999875880662918322774".into(), + "13446240540987488159401062110915298027617715146895881354765875946092155869706".into(), + "7830137135198409572038952404379496414181899227574329472045729197166487746395".into(), + "9556039398803760338966955211123892534270670688829455928022562515996727400498".into(), + "16536848921900120010270774925810022257466132951671306792158048247185847693295".into(), + "14984883995702583492812943326719347992462648669814085270913355351041523830317".into(), + "4449876409800178139287642958372279285293173800225271693328528758341654973266".into(), + "7254404965814435397385340781449208987509307430022649779312503908065673947649".into(), + "13482540945483896073831058930261724641277618328981766008722675404404347005009".into(), + "8516464109813705017846673381161730340458008814885800650922822516855928737104".into(), + "1123466447164703934328741246642448511199513706388932930170273400845584259512".into(), + "3549658554379334242628536166107543119171319090515117171924404150785175614651".into(), + "20050236656917680705173407104625075641049186986084540346810909246977321174260".into(), + "4328179367990709150244102496965604541058014472966567986200766436334838205729".into(), + "6523960484836646945629638483047198947455910731228954877131634689551005111678".into(), + "840118507410487418552201986292140077062095264830249777356756239948365951505".into(), + "9443485141542020716795178817254625558092596484849541439794346219916656780924".into(), + "17517352113160950398700094980635300524550125065948318365273967900505416951637".into(), + "3558475678500267728084169977682358432036940763329037251571517902892359350060".into(), + "21782716253294940222641143651977099452381765047328512527623130468565364299843".into(), + "5143600969269719315523014195013433247337519583796238248783742413790148311283".into(), + "6937766932051718809641611511685721559694403467477888704545484121202675152929".into(), + "10248511959409536383597760784458127936162061804351541810962726000050652155800".into(), + "15584432188633520588792022977848834701354218910295071102828776215462904575092".into(), + "16507813940325077551530806573175229924530686412372461197571308720671287449933".into(), + "11975780799589036547299759597975704141323969091612997515060812893050562154894".into(), + "2743143635303404960353780118079778350398309245035425997562746911685628540405".into(), + "7772317651868959663361913464992926253491072635393117289337616002993612374782".into(), + "2337519377771760840047685336751334655527732210909930380948789294151305059244".into(), + "3616132500009609097754462360087761508553832847286785396472146776175233605533".into(), + "16840763079152094997371697832566620838881567041180683145570810083272689914599".into(), + "7660270111053834825889064071065415593147131275236956990950685283090480450184".into(), + "20762611535627684584459315491847035257226740939101231132494468375819869523509".into(), + "11042134071344142565200223013849882651956544247603198913312251335786248573941".into(), + "1940414570916441943786503544322754456611678071701943345167834901237861815275".into(), + "6726537219661359488596406444614225094882184675245355532878420949418407567309".into(), + "13510096963519324808490741235975735288120771162631019837713476375721562862601".into(), + "8622373819062603720895891234592129397052930901106318032071934400492518897707".into(), + "17024729268332782126190150627072810510918979523772816301096433100167961982933".into(), + "5587788522776741050743071285424100117576582212221222382824387609679897172811".into(), + "12388474196602817840033287553175313493977358457560707192943603355479290048023".into(), + "17628897462697931613283808657115398230150168968257584770836553363771557372104".into(), + "14065018459890202233327759187460200256630019200670511082974394473420421694399".into(), + "6104554150616525884927725557698817344144889522156367737688769378902924558634".into(), + "10992664178398974993431947901729345793474931192615930318284939994040095999242".into(), + "10969415364246753478074755753965479915045305330246521543663858633748866143603".into(), + "1272956806332000540144317124032834499397723122931270496707464205344164793800".into(), + "20231387145101186513751955054188659753720260135170026275339142463242692015712".into(), + "13619345053559735757194629501334040876412094886252831860426351060385674007066".into(), + "15232357465863047438256672278087920688704670526832495687820763524016520668185".into(), + "398623536003273726220923501915643032345957170167431833298597360746684401150".into(), + "7028250324917735970960317543369047790628992348836498053408852879988541935724".into(), + "9906086796055827169135574765108677877733074061825927799032011921038506005373".into(), + "12788056119136131846722253277450922603309904756259034915496788214657808474919".into(), + "7651468821535297879836173382809170069017662802420683445433781610638854359553".into(), + "783979512383179545035255083828374733462990002107251717097983319441159249905".into(), + "8836457559777657917879541925866800908082149812458375523311279635635800064090".into(), + "20144830911959238621340309337187081519944136818938895654338696747143855879203".into(), + "6287181334311526693967500402732058645490470512478517679915786729496945390849".into(), + "6578356433253304325301622313818326592018737710185433748824597250242226401957".into(), + "7102838428799453179757741468552077315750505008228901893660362447987283707998".into(), + "497820320926977947430162841325159818293818758853362781494155076005984229339".into(), + "18160985608878840530944322708383120179552980243808985184923313895763861919637".into(), + "13151612592624659285402557051397602661028781656351284997191435582230535568502".into(), + "15780018535650817819214066689149640940245062275718550628416824107970066217991".into(), + "20500218068755784102392504532520873969817687889455201735073738515957629646567".into(), + "20888440956803193084788282136946318522770638138510716565517310266646640263708".into(), + "15971618258529607882347998799169510722902529521092131094419433339204085498306".into(), + "5293253007777884257810026986010586149314566166761521493811000336947093325852".into(), + "11581497334099661755434309930999056492294889088823870215073663090290932016658".into(), + "741799820113052677363038461398206547514107871854645629573511776865766479899".into(), + "10132388802463968927185535273034278687094388163001210029432141519239006715163".into(), + "9843127774781963908073388692741045845788815542021009420639678295559452765837".into(), + "3807159091255176851037359514650776155078897682364934585802230436085278410633".into(), + "6904224411392552889784909729922790171388456358295280077234569059475136886573".into(), + "13434461271014860123956911179522881298853645897065359788401689426904150525428".into(), + "12249944872255100022137726834287337969948262832177273992983186014370317983705".into(), + "1080721436007830510323005297307130991703868335217850548555329006531295547187".into(), + "20651452101643029862567543402727347872461377965899451538964773059299839803923".into(), + "8902465483627533643127758766065785114055357770410218037441501029224645377694".into(), + "17049203022284218301986830675304595858941437294636126244507847280421482250090".into(), + "20389632733862984292955678575857173748595770609738193230360968914580393387194".into(), + "2628553647311400613862969480533814764766703257834731382409245421165389763845".into(), + "7113709286888238485244587172405174762331565517345483851543315956528264191390".into(), + "21099219272139712501072896978936944264265953306877942445724732180674664269749".into(), + "6196322429352603190460567397253889100476004214002852031233332452859780069595".into(), + "8042719870001485176157425681827371545772290235839006373715151688719449512739".into(), + "9109639854367609940132679558323551490721386060080938849664245212200893608513".into(), + "20820097690315610819796383305043844702226083970185755192990081897048903995104".into(), + "12474580713368057036057537615251118300654026141156267907684197425413737128906".into(), + "4935701571378317615941939703137616736056472417588666676180454398865436202845".into(), + "11284674041151323919882446027842742890041719161934493530658741778909469887490".into(), + "10784274648620717180750449830334448050402020683452763192417352842268830091773".into(), + "7221763500960709043106304906102942899855402476011030187016839441411503790134".into(), + "21488573117797078819915291564603086854092528370673732237937703606783710747214".into(), + "10368071846917543663295415889847910771342838450725996382692227125006143332582".into(), + "7189971404170280285101021558651838178138415948558212834138150417852815291774".into(), + "3622212247952623996680563730554546964978269732772060499110124228651174919309".into(), + "4030261128316895271453652515658449745835613736325708089700420629607904984695".into(), + "17917906020983172293093657775725231153822157764639429745264576611859524349990".into(), + "7911943088433991265213669750138013059877768768674246542956611065207230165349".into(), + "1237568201834766592374215633649772138875843120827112365624718270590862573855".into(), + "3421335729151995408099494011827853754192179969298834320585486465552812589072".into(), + "3983646153441614758977318714985842223356917025653336289250912061318144657309".into(), + "11832501153279122995870584183546009144756312133876131903016034834496067810803".into(), + "9642188977879773189558548598547584337663289943484049782260608599281971392829".into(), + "17650631071515565052542834438104124084088724345481542312112093611903334892790".into(), + "11727835841766435406263157822186528758001431234638936084294421464770985535272".into(), + "4170985922866379778894675975982515174956777531610306941710536931798155932713".into(), + "1904373901352853963433078121273558128713374926256562753718137883724136086536".into(), + "20310106358538316988108567646970248789216107374650929380709349367764652307148".into(), + "10755444294914746017826019531774644079659674520334374055753829758342372728308".into(), + "21544150011748410796331015950886967191957797301800349750025078804307553228922".into(), + "4791178272915634809747385261951447492552726497734131770971381971399880792451".into(), + "21569911489317541997628510847711750299218484102264583013015022430206018097370".into(), + "469129421958624587885163292532024178451414238938436764179429972880411747423".into(), + "17178941568791193515971789991206572943108926053917999875254005411851993177716".into(), + "10061493654971891913017572469769124056153763544567322059270144080123216592759".into(), + "20809728888234709563099636671010468945654173909532666727272823672496239666493".into(), + "13977154471274089663104412732631175732414148541501676926172952880637640768097".into(), + "17159586220373273602393948660742324700378108883620196368910126875831365156676".into(), + "4073500643742253412297370658761396932893392648826435730598979045413705716932".into(), + "11493572320701213886088466735117685416116051720844612287863434228749232164029".into(), + "20892362924238047571943713393658663337479326222564740924788211586758905169271".into(), + "11301788649359616808987952401363629750830090004562424782094282760076674679757".into(), + "5346827191385082048034578496490044229132766705726383391388695220005635473547".into(), + "2201675309086116923924279562154152714268152257293936509978158351509848697027".into(), + "17917826577478806664624583743441880347892085028222113450954395295616044284297".into(), + "16998236939959525187887324821682683276592762127156629849525502305788028877451".into(), + "4214391906402965555572230230924694640705243512659675242003943183722900957472".into(), + "2247036556360539125313249919915213027481437766291950011195105972974928736865".into(), + "9520057428546863347057860284023378728609842187323632161028631359500291204227".into(), + "1828736612590741052604266070553266072363175828159047049110641125134408628099".into(), + "18248580838532636162005188449054546706775958322552749296747411333541139803294".into(), + "4834174634635006984667037697276171446677972706028785635301769650621697899268".into(), + "489734793278913821067993383657373029590536750171301785048262915525334613888".into(), + "20786356799510136039282916235428434827695977220581899018592633397684279594261".into(), + "13145806261364716646031477788186247592609087183869974953048604633861380872823".into(), + "3841420600164259772367211721293848988818647274121301867288590071853319905062".into(), + "6882597113948243507685423253840830929482565588666222909972451758645869849656".into(), + "18980943240416553858613600850285792319483905785062366023956546569017873987439".into(), + "29028675420319059649770139934414847448931804950673695214332234580922091102".into(), + "21775882073646833834016763184296217506960385126491507726769771477412306185790".into(), + "21300410381086030882514625793664995316371660662503398686828472860934288345599".into(), + "2472267536757789817746114566761390885333410401690592727164834695142144448445".into(), + "9723880451630142367622302246353057781908243406397604481250639516011253801606".into(), + "15313110769360181254868962440810453125247644871877064008660684649744823174300".into(), + "20564192458187565149788272882595180521577855436987831222746293278781628416968".into(), + "14590472970343049442795311527863765295112518097070620865372037933484660359574".into(), + "17062841617700893799013325463153088719125540677385032193714935878731444635251".into(), + "20200256413643364477818739581045874029732018697196252518589192345293202187361".into(), + "4016942840923529049014357120480338974430876995693135820048221911362323816443".into(), + "6537662900592412058939229458410964809681215707806173558553209350444735100542".into(), + "18297886978176179234700005359672839811634650525194200352748308964228483504767".into(), + "19662609243585750899960608765774063353658909677643282385185291970742157305960".into(), + "10221731387204953046960523786878755517637874808703460048675288088446619398011".into(), + "6359039963669314770815970957437041104980907769543152198211817790059980604175".into(), + "12215821248736109157702052060566873526833550744650237478543432061962195037956".into(), + "5935095671961849761918916493742868190941272209470285856382557274883130995501".into(), + "4892952774759542491697203073862387895082272084344610213076129801679488176656".into(), + "21229502683877666320987735203037721130704543292725579308736529207275811435612".into(), + "17138707315635877064706472595294259087755028865414511496916808945331553578413".into(), + "3064016632497858706665392042657089766401657752838410773325693629079632182414".into(), + "10697546089228176531454125219656376659283555269092429548580998097316823780605".into(), + "6383154332658396326745467904696030415502846256075951666613157084114882351628".into(), + "15399057406934719483674477103950525850935787593222687850952111073065008222381".into(), + "14222385230562109850723354194041721683341002968759594278414075630295191868997".into(), + "4196055868873306164684774878036056827685013979028843775890547079994909550345".into(), + "16495894724764636941708778916623546529990869478284583127943604032121307478877".into(), + "16662872698072302151631604897467020278898785941962087730337008570271651661242".into(), + "11474333637424192324648146148268767361869344542358721131204304533867910403346".into(), + "12663733223747936066515430496850730759882234287115648507227477484368789774494".into(), + "3278587449214209390499027872507714198695688159471276082957385919697290643338".into(), + "19852281910336140665278848076045642033038867843956224684297984081107396007913".into(), + "18119827693896204780441288678080897397841525584683046254519435612134656396725".into(), + "3004914531848687259880590983112983929408762744461212501319599913078181196369".into(), + "2744805266681634195002030539778050424843533661368265264531107280758966836574".into(), + "12003425394929552979405575446174684244823087440054609540505332749485291543559".into(), + "20899617298205776433505248799032591564097087832964850292073852661486959326400".into(), + "18977355049070793830198782732221581680757588074059967640900502934882545605494".into(), + "7774492678097446887356009465076901979542998304721485056654902284935411555748".into(), + "6257979871454846095389581530427518297635929206624687597975380788548536091169".into(), + "4200362970271656646831473686822497724241729322168501270846014047124987169639".into(), + "9626555465339955137152373316361342798468143010897667050837558740100066479490".into(), + "20599104767749131194316802432182162807569082153791773541255180345102031538229".into(), + "263120164166236772856966508954885356163375871249262527569221312470198863328".into(), + "14408635566922092915650756800314844499933046455992065014832556500390343919586".into(), + "5183079049039149593555827045385122348972389627856098826226304101776790536649".into(), + "9079663711634286747144802677527389482368428094888913737002695282424348199029".into(), + "1730279784246934965982679327290069428428752739692892522465325889599043152257".into(), + "19417355737429211343570897838176489391485942830255040228180844510907794134979".into(), + "4688867852231614618639054238240140493597889866886829462162366294959927124611".into(), + "9750344066229503722317800976404611879233170071789439067052923419204148303262".into(), + "3998708829439769920633774213209304480109842139515896658207686741134489049438".into(), + "21119879197425450902002580651023270363969638681856902851675391960298938692105".into(), + "21081985423839377197813891376664505429739140831959287968556239547809632016084".into(), + "2194830658993798006279037291585161216930179214066933009151541946587699367176".into(), + "10072059773309529038163761158232080490764157196414798621723969165341603556957".into(), + "12893922413945288356816723514764026149994443040839144958812668568890693761688".into(), + "6289043885924508250801248626630354568142964747989700154772682955247795752635".into(), + "7117847768519054206399668810694592559179837439189798231517777441723405148708".into(), + "12250493458894268361669347775761796626700321541112455611623342396141245275173".into(), + "7536889841233679602786965527644616779647633163370923759512908958092882343888".into(), + "13743804807205248364239225685151790362682679734025326476864976250180416573459".into(), + "15326756679387284645144696476114707028416084124671428061976908306742987298792".into(), + "14138908940825881278249529554167499601250081601885817719517135886992016259170".into(), + "18191604052705446018115707722428677617894390137039905732841218263472908863798".into(), + "5747208788515494994202809520653945260017053266507158973974399964661798663161".into(), + "16272405226087543462175411026937377018988783482980328154498443115982159189635".into(), + "14959941590764331840350995267307957753388831891977493365286637018137237442649".into(), + "17614662493206460042788564726025968337855721129414467376860164849857118120897".into(), + "20540842142176422103053529661013282478418611089363263097515168740633056607687".into(), + "3290501693077219671357645003459548779173001871326499393140572778475444534834".into(), + "6447714071113870576284977914086213514251182728059734289291667058468678194763".into(), + "10016099876948482641338204267737928588893338645093292975971465694121948536197".into(), + "14390773887169232315205787249570222449199544947506512428178148864937891297988".into(), + "14620241431433245619096116183667070956408821753296293274413828291589840475809".into(), + "8494548643236049061832844399476487754775580533076308093638832054930352314493".into(), + "13769276515160804060029880354652109198182914994347382081537158871751993099394".into(), + "12260040442111865637116150785319893997124694704607680451569915655443058209529".into(), + "18536763786390498498949547812325024712812297365266616445712766917775521176399".into(), + "17646030323097974380890038898728697538005884509434748880882937861292172824089".into(), + "20995211004588227271383296369921345208592928705280945008379701434887289620654".into(), + "14012493030585128283500948776342859322003214361666347021120271800114480256613".into(), + "13455652757462524273399933538052579123871086976307529744949349716824204294368".into(), + "13176577891048443118691856747592745460758840755040242121263455031909886205386".into(), + "10958007047406016650282891454669527247751542376915102729040783130554758505696".into(), + "2403556896710211170900277969533691258363807287618829314859189083914528732150".into(), + "21557822641920534343252006226462178185422073111762808253392360322675121740589".into(), + "21301363144372514699996437466711759383396133417588035206347144265914876911832".into(), + "21504297452761836117267872934293380498824936327520529664563583098264179167892".into(), + "19008511347839951812168011659083529710470044770506456216838511009924048532294".into(), + "20729557973386382250734811255901369475271445538588448251951105355722497057092".into(), + "1596029835560146984478838415844959546370291207350745477127036146832708172428".into(), + "18779043981021734310054761701946195503647990042042517453246401248591799960006".into(), + "18389072889502391007861938373344479996746585336278814994866091596798460590651".into(), + "11450018471721248431523978933304902015421867719856001119788032399031643640503".into(), + "4695792303828705800181239521907223047109864872167868532243675764473118182293".into(), + "7504424077032992561542388084424474665707766373549642837850671808747703476058".into(), + "16201871852003230376980125294909568327837373609180822015372468013683148626476".into(), + "9820880186139182810240320436271674897142093850866141441610372427566550592116".into(), + "4474392152763162487463052318741100507628836016813487468299920576199966718180".into(), + "6535313881166679884076402867701793095581869448631792643683900609540448349145".into(), + "10375499075088306448884735241746141379659872937880333028048697275256343984198".into(), + "3166175670215426596895103003450519990743126408554698027893417949729089445829".into(), + "11825805020940873618060190367149910908253947573120883350031415196553790678909".into(), + "15868728010683147011542066925511350470588043768845023267900379312401595633901".into(), + "439641324715765398013368707995400811722128832670521930633684822154134655293".into(), + "21389215620992100898484688007707226862245610434491939970737280409708735004882".into(), + "13567457078921654178980424734811793190031829390199528822285370655562819201102".into(), + "2491418221701715606532424215068729097730197896717397364135214058133617329024".into(), + "2700199371443700220147927924846092239465972046042951758397024540313204379477".into(), + "11101155530024798073181197117436499601132355735889219480985438157046671552075".into(), + "7696323106369972761130328211405739772776004396041557644397285018363028133006".into(), + "11874928928175576980576459276992229552831994515252269310033260164062026645644".into(), + "240537705962136501448280118114493602675578232672944677099273394877211057455".into(), + "2336277667844723086566433195865965121270861932965343039992167442067222380338".into(), + "7139477698789264606031986148297940201890262913499702250627665080791275501502".into(), + "5394022087396393768813205254315732819042275688988698297882925332496064046016".into(), + "19793961339963600744392705441271116638543292070344579052218623579091649097618".into(), + "15692138411890707768379631022903362931511371639119812810678572491600193150934".into(), + "17870790093336984580535630746458834581867280473989239593391879854453602020534".into(), + "8527221939827993709561548898140031570873635681631994323216682386420609693119".into(), + "20583146985132338324328431878826670751588212915037908674977314222878838415115".into(), + "18286401926708183610455794878977380145855167386311409319243563628128439897199".into(), + "14883280595953108786791995875596640384464375470758397510182264364463199426941".into(), + "15622372954112461900770387091989505548249197707826335911691325586886540655839".into(), + "15015231518950399896087228957162542423583966819051318376374846686122344072726".into(), + "4110275540712809298667287978052760900153083659165519222137609468305797552622".into(), + "12194886690835058962976784720918154631659193272765999855977381550575603733993".into(), + "15244557029932516875079143618219344257744782264536288508363832394149847400408".into(), + "10234512862511661102613686672198745648772485307900923622631623670310818907316".into(), + "11002446381083289255418443918954072583200871532274996806630154250111947858951".into(), + "8253167315431388215871891033564848144784018068378784924459677347009078928353".into(), + "12052362697319720881876615803891693831545089601220691511658506577522506606138".into(), + "4222271910532283822428121153000170791277438320812055898378713943538215316712".into(), + "5653799018158152396708984453701593562269544320974176828284977837083044089432".into(), + "7882074107401771620456811688079120160013843021919844939348749959599330360070".into(), + "16985024708206545567858733579435707605892308141313442111649869690900835499686".into(), + "15657638292217617224447239244820122003415140104485558954475025681733455080516".into(), + "5031435167133277726502623124488830505559857356049078624241295172338778604459".into(), + "18251569826624635398451261785711619709466995337593606694180128347754947431611".into(), + "13347210735949290753346060121453479592654490759925951960015474767685667320469".into(), + "15097256163521186466089214691748103981791931847659339134237546471578704117651".into(), + "18618539001972002803104792202843684224999903989910895364254642809781793542910".into(), + "3535158924054577294148391849345768098132565619653646126038220164469413178376".into(), + "5247111262643738028940705230194040190316748748116050300897903801701767549935".into(), + "21069968641157288335927634486731267978865294883466175079455493856177243522705".into(), + "15182016439888624540835624880056168611406902880254574867923247894452951108191".into(), + "2598270152918575231168351721309733272482825217008498067779287914396446525010".into(), + "5113774671804063464274403166444533180735260894306271253672198506848528131887".into(), + "10007988864825946871589264454040608387449058007032979960058775715911078006740".into(), + "6905160188004006948460048193173293195325586959399957154757620552653693466890".into(), + "11995759573081381716106152146833656576924747390008060686555489446745383610128".into(), + "3012863557306381401755965576258357584550751416148424411503270543269480674925".into(), + "2913988303571063477501511529375194010078206270482236339308347176586704669764".into(), + "5006524049092266462685266534385319119401827059140718482624293936686566174021".into(), + "17563183357860154367150870160675168713982368687948283812578969167511639823963".into(), + "17837755859790999437375156561325979429838685660428780384415700354505074279205".into(), + "16080522246080742261793784524730583623229678427635608200910206239897175583035".into(), + "2848708361765680705684254800195178864363439998055587286174501633177530616249".into(), + "1557519954300916467422364113319270366979873515036760226798787393508368098341".into(), + "11733665678441137100700293713589760125678167060800184611962879142675741739407".into(), + "16521409005131685666434785453319000098069672876632224692360259274386316622759".into(), + "18967967647390654785643258265157841586213408055603172849772769208320302959605".into(), + "17571504476351312669488808692966809815779784230239825235217863108338926113527".into(), + "1033277958139146991725614151292577955914352533347610456481561709576150765927".into(), + "14781931979382308373625511201561097203707487976646566051328122813686985362864".into(), + "4650875909473177450881505980066567106490273485086660264292847695230525365004".into(), + "2914409799137239617504079923731151032417003482030433682771204258557926592318".into(), + "1569377349856381614777306232175508373904011237210172681516586308749045834576".into(), + "6819560317780627274403497421141083285449699366697573487561555149873327611188".into(), + "16783037329427380826125478934695948949747971672927005004484739677562779828896".into(), + "7015004192682971326660995855922915203604605225985220083215349611396572003775".into(), + "4630816304408133216263172876471813378546980993666459834637527842126471866675".into(), + "1048974869579541183196305195192354335945589066090693848404581740062134729282".into(), + "18688715639285068183434963296396143793423259729912639999344372787920282768249".into(), + "10934347243740746938446533383348729597078488045109074394137337392516949956248".into(), + "6293725896350451546716316494602770048720703017891567394397586512386125658192".into(), + "9703640866977553131835948007053717693480506246962404280815291341798629276781".into(), + "7906707563931146558411066527524578790489630019846849753651156405406896838827".into(), + "9702629288736663637774512920196886201521548938988112829178357919548773380071".into(), + "11496918342981223624025423866080407628491859149911166113194854209867880853781".into(), + "5310797284993667039758118840812749372556974855379167283523727209282978379416".into(), + "4023078794142040206433592504682005395486039156676105856122688431286266944198".into(), + "16871217006715623479558921653467262427043486550696934636603562460293483965032".into(), + "11838323559167998464544738415089027103939009770214130070107400686110312526340".into(), + "4980088349530490327691411848559646523716982917225546360214229685978505879808".into(), + "9559603819596004088164413773616676054644268005451072511817132718244048934414".into(), + "16706530320004737946238925506482163802145108867695620161891008305110107710231".into(), + "17097818114876900788313405299390647115420321455669519337216918484593668351660".into(), + "5849414403812965413267782406018277549067364089813096482998057955032613458546".into(), + "15554882868930267918822925407791760045541474181510040631764755534058258253399".into(), + "21580870201101672999738147681605702976639626554191473007622680366607482413326".into(), + "6529597247071863795966038055163209598757897174702699285551712764342047434265".into(), + "1034354038040835970585668649926410980243448725331655567931872752141342573332".into(), + "6310767810288764607994771877877727315289627303796831154815613377794983946591".into(), + "996939452759173758835198197713244238928300388619776170462989538225909183250".into(), + "17830180799634516986244195887614846195714707335284590299432596686170229130983".into(), + "19874559606908304758854510017033500134646556009065042866233430268619254302205".into(), + "14009373839533951923879302153319163529100920268844053529575065501607218593395".into(), + "14946211767155341356149843492542554767905660369447051530372801216288515919491".into(), + "20201956319173972615406549564337979402376802300388863728690331165140287483581".into(), + "10535506466358617363674252301599248223977286129433846317382254624662176433824".into(), + "11029001794792043521930346330104687908171223556219538577009137251048931299365".into(), + "8200934006947443074514571335915268415570710146769685554653690866413931711817".into(), + "3756001813899409241012385101881052052933218317388650253688371184478299762463".into(), + "18636887900618256435910622299263466888026971253061227337296887637280237818897".into(), + "12062360063539460494176603336727957703003697981349882838776879968409682697224".into(), + "11001722900962847730169711618381123657713397243841075174948041047383605406084".into(), + "1100165499084337642558411348439001505815886538759724357694738775770474410180".into(), + "17434145857282894864497601363005649089714522250566258141982190265575138829458".into(), + "7528570688175530782083445771420581373161584322581902670613058236780353068011".into(), + "19971690728399713343613282829324748110469462189895719072018362834001408037396".into(), + "2720175852023799717892872520519566779572526301065275372226989199715421627391".into(), + "14943825869290446747104357486851686014418770156528443888330443464181536442315".into(), + "463946552619089332690398123288326517388219753658554785664250177816814153004".into(), + "17828919314250532241077886046465359044175524922026640483099339343132288207680".into(), + "17254663393643639301497528160788050107293708210389962463123967310041334977099".into(), + "10548323428945380231342461328951126333180031707800802905121494905097297919778".into(), + "15741600332199859123106892774234668638862408966232099592590755994226268019056".into(), + "1447099980864195663747093395834509033779875532198378629216846242387465017398".into(), + "5634271615965881595424665300014723762842669209816714518648909648355913896267".into(), + "8004046094319510531556517659927356739393971820240935140962728964910152787977".into(), + "20150098853172404856553856601505464455083744410516166414862502865711843962616".into(), + "6051577504932496971373107947431966202921763803799244564057047344716078611429".into(), + "11153752935200730615392163240542716924397740666674490478903681768299619893440".into(), + "574571428841330767683871500343913695389489991910772848795148223306401258112".into(), + "6644002840867033629815195671865575216742798076650013015371202918478434067981".into(), + "10643887442409207306340562760946079189746204276290341516975302787467861819914".into(), + "3310054989122793204488875713765468888781168322357480727671652094037000591815".into(), + "14260109332138908810733885305730437910719800845345795697882239548319688811219".into(), + "5210296637174697538908079595920299077484266890923778247313974722933789990659".into(), + "12099525392201549319291916254076192583959301537918452056524775934199172994864".into(), + "7553233508370030852973625898622143795815488816458055012305692281375143855601".into(), + "338064484385145371035100049761904487152067475286477053744625247222287881704".into(), + "21656711030424471443690125986754350631269722127039355499852011488517394872248".into(), + "21372195548620232488593781254533859082570347959319967220919951808088029020212".into(), + "6382011083971938503792804272844009960813722135917547320595901431805157825428".into(), + "10487620314920598491955634712479265679552728563160570833716083687459951803271".into(), + "12540875281457292616015016392478978686561298989814479837942710114246256185916".into(), + "18617705893970874028060182711496372099359212095272496239846702650061090540602".into(), + "18861927157583050522649066858336628481061253205783797036256319572504841854227".into(), + "697040343546965749510304464460790931385381610135802696124987191773265224046".into(), + "19957864832901242629222592391639674066674531543245225621817890196632976954948".into(), + "18958366438463449522535881612753516577920408884030961917206372848340856779454".into(), + "19038355323356709854907634385093529445778219247035572752290295596750121939785".into(), + "1288546653652611126768551729241141142059005744057588910355805731786584613343".into(), + "4046804685697550720337256987447438366138088525717758741922811410001913306096".into(), + "1660762585827970516291367008365082875857712512262252287267067642443918638514".into(), + "12490758580574114271593656499165833022285695011717771020275535753216014465968".into(), + "16141486882419673588223773309048616384177430148674560859754337286781225436609".into(), + "17480842754700647574286106752058337138867662945102069039800534716529718409113".into(), + "15604353321749597155917847886619012355001132908396771273202434684314342259150".into(), + "5617279871415371338336939332259796482400498338204339205308393954394249833452".into(), + "15174696383871305992552314836076160250704499338484600692522408887766438439385".into(), + "7427777880578061171924488290292477545896473049748950597878341286830693333925".into(), + "14092813309756880555589883560674882809640120093304366339387019683168400944554".into(), + "14371461943490927493590660203698012938705485668960202862586478499151043526062".into(), + "15747628341829994869775701388730275581025993393892718204096081193836534027616".into(), + "13069485962326240936812384345060376352182714448869348207061583108709995536141".into(), + "12897613411832214271030930629934621444235734267985429117525716946550585934838".into(), + "21009316561423875807053156778128985534726230082102006144117574738389765473006".into(), + "1961079851137768143186691785724923358396590015729408078834852546188718166268".into(), + "7449716401041984694438903080808992521397429308993548549788139074686598693961".into(), + "541840132011603695915673487977337384196812668637554519620724199330714724824".into(), + "8732237210312418481429872365007927003824155222103189850471564031658954501153".into(), + "6523310024353124781137808005011914742907908537623530782692226224442022388987".into(), + "21509127266455930277492767884139578463956043561699345583365079657386805307554".into(), + "13989299184155011575724091657019750720502004610297338129834280607581254559697".into(), + "13646882669283186383881254452527864262332184212165662411849024353857046450116".into(), + "14437939637607347068422046603975325432039018873549816106341419990853530813605".into(), + "12400256882351762977557053352624357859435580188305833994340851255855777961583".into(), + "5318260629482404358266028277859623051836786599425915900288734688050650647740".into(), + "1629695214770269012001751163254712973932233705878496747522731454404864982213".into(), + "13712071936286520679632088394106925703538964875601961825262229735589116706613".into(), + "16370398845138146853603171685418735096308178156853614168785529502102183447562".into(), + "531316798023999736377588166858362632977098508125317800095882267717108741597".into(), + "9318921203266238488822738977554047526587525733728386955697050423076330149397".into(), + "270377449844572577022377874031006263208440602734811115345017690504339807539".into(), + "9815014161182964374794581271513785279039355629218722105019385525592211310401".into(), + "9645578731676628215511306357368922184828636697072609821617709363109131724528".into(), + "8278125422456187544426337239756968442258418675393889722193805619331692372756".into(), + "3793686014229600852890515574697744650225321239380634655110095131274614241795".into(), + "11274706713173058310395599257524413726315092599580477329306442323879171602089".into(), + "15700516289342200878818538157175110895267852686641088808116754074662814875793".into(), + "14231470645515485158149500150154330504733538042275634884479799789708016096981".into(), + "17009943350256143028237187628790018788978713833977098031809639804676372324573".into(), + "11807744076512700514106955514318371416449053759940168158181473579522112561617".into(), + "8941191247819458429052997300931331828693768320666304381154122720332624423159".into(), + "20810756908118815132070882638097329621005628857845667418135716334205275433963".into(), + "17633865705062992379853990460318358612898966723353031319355975229233791359833".into(), + "20574714650596437874739546946725432737055645662464099228865520688481435554952".into(), + "20753133214966494228304506683244546691191781338293295716113306511488052350935".into(), + "20891917718961601440155839168193313007436009622881050810291426124657399756167".into(), + "15254490709565398763344025189458873521593894682543200304507470388535826802423".into(), + "1579686464441495454543935122179009252394402667945704420064352314351485765393".into(), + "2776164910982632895181744444931014666522951510059312025217047160140701867676".into(), + "18493452977704296940835401396385377366306134685072368153755232646737492155884".into(), + "7466441893052199319529201615576158956429673118460829755667264030485444838721".into(), + "4862779186474359727417788114204862127925883646628388801420770199523844825968".into(), + "4332442906725686065471004321063349183397729160637280571390764069298441846158".into(), + "6235595676847271156438843637266606946005787585155569320931268977334224113102".into(), + "3283545972614629083851761609721595874215964445590540647737064482671122002155".into(), + "20190238096841101806664255153033971438119127586536510897732203034569691981419".into(), + "16218082928311959370464862694396129351602418127064911331498297863184968070057".into(), + "18169106670538118432467974548733501686430842104311041161699612921427327878488".into(), + "13082519725061669934177494520541983774227971959053999627907293338903728791646".into(), + "17917738025521205167709067376626145668664963656058875474248539992479417821761".into(), + "10201169720940960483107286532267011054369536208475708600788396458879352247717".into(), + "10086018032281190838410153727159750412677882106214361975307419597102233105666".into(), + "12682273581371325166244148831560984283003922721179649899812794401686084266959".into(), + "21151848537444166847396763358911125604594195283585946282585966889014856464069".into(), + "5870563280212708258714113395803290954804582727013499575940699343839714979459".into(), + "15818574025355019546843656101475484151535156797083091329572001913076927497145".into(), + "9172129818981348435701876676880219576808443896350696165457788920197063328446".into(), + "10390962755465039363458687913467754632739357930439039623432597903165701965254".into(), + "6161501669511652251380864592245214064026880951608726788784852344316145896868".into(), + "5405539522259494292389572744962975583699928794990075330665720986459457361914".into(), + "15953141686281883540154911071743713625324924225902448030996737734044456286469".into(), + "21879157639189744211597108651045875700807085936267782759316146460177277605878".into(), + "11434924404918060113007413962226874803102772070441693086168193464929290844877".into(), + "20757082623598026102923542323531106955538082419998942072958829717667465557131".into(), + "8919981096169474486495376362947226335482024821650926316923260800584787410595".into(), + "4288509578096166644556335935101899777779885105511348520519380469688243082096".into(), + "16046529059566280251436022453196743987059059184968007751447655603059461800273".into(), + "11219038614950179129482943179862383597500934389713177845014944329948593838918".into(), + "10043039951240572103842000677370211126438633625308559058041812947576435228523".into(), + "13424594313385554045395046911081130962489696002268038176721598695954847052511".into(), + "1211793291463193275930858215285948372240170625699602215748915241438580144093".into(), + "13956919764638999174327326329058963982898576605238294264300483944344569472991".into(), + "21763763972390992315030985535287866258485311664824948828240050483482084352503".into(), + "10420983865433228040050251893025618324702726171912049836799135351770260802977".into(), + "18753701818722627673883564699398410722354636353042388382508975546895568464372".into(), + "3775364142286651074799331885877684616693199427012762917550076623916732710822".into(), + "8292671711306889259828642403052383798091670542487370026886155070540857817510".into(), + "12071789317828192902441324443833143078458416120447222512665882093850970086582".into(), + "18221914904286916930821501101975619932383182494940702022220624561466550429056".into(), + "1015894946220130503079847588005345557311861372219799708101258594371020452677".into(), + "7497079366974677538814211796849030622236102734688468791057012961185250977217".into(), + "10412524008150259624425778663311437889796006875864864836590446801100002463061".into(), + "242804608586097049060214639927231617495599500538071474816862835383660136678".into(), + "9327321863177171873474299501143270493456472132426279976071195256972553555122".into(), + "20841963613720468677785103552893139903309688713322442693081971171914472255790".into(), + "13630784939841089364059908940844732601072559390491244936534430865345773326547".into(), + "20060990491599613171081634276027504616735657335613608029945430961773413795782".into(), + "20369723769740268530808565104818084023349274297831884550854878783888535628782".into(), + "15076862507709493588855118027827375721947856558107489434980647124128748591027".into(), + "12996451838772518667606758612923003843581729895975567888341928977998771990237".into(), + "4052235171895745508958823720790274122684550078188920081778152850440806616718".into(), + "12335377703331511956642047444506386381049513479297082723690490432465187475758".into(), + "19412573446052112260556809146936739541890274154067075795508039506436684757726".into(), + "17815752693042999749244044082547565016456928313463444720511782669652296613554".into(), + "15784264337580227630693723391508440881780776717973842946134335715743491257089".into(), + "10667492170364941836778050228790234453197448653226319355770388144152747476935".into(), + "211997855288770996034164566467801948520054169907247985981041869092209280055".into(), + "4928982790134068590739975426823777812194464923471161503361037698280174981760".into(), + "18608672135933853582317718913701178979379432491853388463545185599228146691207".into(), + "2315480928424106999355320576364787114368100799067708928765166041722251581141".into(), + "7132821772386644248930179957111951717051009389997169728630437453984113307524".into(), + "11744909558588287567829975231377896922260739746243206347301263424478589809196".into(), + "21821616737585515642213483301817094657757210129023396850436714925413476278715".into(), + "11972412756334108055648790188313869962577071423570734765254546184728606861831".into(), + "9898102092275580917130558353359107455579856499298488646821411779664420246304".into(), + "9958879822047499292094876401302022972082946727826778952858218178060652972948".into(), + "19871205375909775929744751330720905649873502681808501037344623586575605895174".into(), + "16601345490108570384179778033128731955939874176242885190845545230306090545377".into(), + "3600818319871189164836691793538369796689767756596935154198009735998848369707".into(), + "20314694248383769870802453966233555256007670655653781360971318867730452026627".into(), + "13418958906290175260633447769067612135267907574578622587604011162488451092514".into(), + "14459952906281539694149485094281623760953141057796841403669604773900469687889".into(), + "19448408956408494949564099234438127422205398424291589853947309097810118078185".into(), + "7825443276535418239837092010081563810404777554695462770138005656816269166303".into(), + "8463248112790565810949249352339633764856217758597371912588298951088604363676".into(), + "9397106702637851943166369067733828452382029530464971824017078972309585633364".into(), + "15452595095505449307828854722016355425252678164966267614739918073395150429984".into(), + "10382687268437366227597120935808669117993505555024110738918156422833458968254".into(), + "8525129123003317464420034009755418250374332671627293582800681623438541074422".into(), + "2209915653341740331756848895690532824379055156318218590971248250386542567791".into(), + "15183382625497370680223757887016738067273663916736248647060220264288252640054".into(), + "5914882314939376011130904692287520473675802288813732481565058295269249787489".into(), + "10737923811176739642308957871008944847331638141618843900584295941869359201136".into(), + "1019349115878726003171044582547108836234898959299710235354611802499665438533".into(), + "17364570285151724843778637821645803441762402239038594475632207111794332738860".into(), + "1251558044831543718478805412133062543617572066062707552512150093969983011815".into(), + "7568361578986094203490921683991770751640726490619033852251843934473195496119".into(), + "15439738350303496845805351814211602783597897988159829928947802101119983398961".into(), + "20009975853963282344191402380547695759759731502702993682892413834957351648692".into(), + "2363424283223098643833999734510060612126929924822351279904900380971757668501".into(), + "17136028473467909987260660923882066072229277631411461261810169503965426571985".into(), + "21423452231832054119549543703585288464249234898851841557254723364015587266501".into(), + "6300803647873111234660196693510491620063330061194984556803532776163457642510".into(), + "2749674757601823156522337416483425720076662114029481936093706226193678544468".into(), + "16123152544885237760040277581510833657110306240025009956733742940484347179299".into(), + "1449335556475943566914615807167633374850907490355989852917815325720800540092".into(), + "18225835808858885369557291110939124591374272404427656191824524491194015802997".into(), + "10406157408365973197865488683230737937923502029339842530701446718978570586144".into(), + "17998633938868470446795829948946241208954439598754402047898587965138352946507".into(), + "17256506181869579387781206921456178119308959078547323103229058358574594315926".into(), + "13094455817799190250176997937777121524540082751332502303810836528817463694222".into(), + "773415265947842731444676861730775210907581945844672294668103420848288364110".into(), + "19038336321288882240456993528476377501756672900320404015643432684581055112943".into(), + "14760627809381104024315295117982862934682028410719221222838112174132134785643".into(), + "2299176916578536045959722453832883315456156391111537255429863337814706171473".into(), + "3669114713225196659381574151653298340387887549947282054635076450967251116153".into(), + "18251972251689708305163349578105700140002711019536040700377516042131395885101".into(), + "2208114007582253724814286240939815507122998650551224166265438176279104064289".into(), + "5311319465851004438404228350963430011209528099234009157573275300632566893387".into(), + "17704997085718018561575909495530243398580104458533121202802306468962737491818".into(), + "1933762715500501610210207583856164878867609341004969983518458273086102828809".into(), + "6386814998566871009542498039659511536698234159072885405476628256992102020495".into(), + "18831772844239784973934592955595859276748324649808851586824036127112826604451".into(), + "13462121128312374635146040270944207295588795209645130037644415451121036635451".into(), + "19731805313375129918425060059406339849106971447864245585064948352741000760923".into(), + "21643500371506858849424889544041572150599889621082729284012490052076449459481".into(), + "138548710091390954908010216339657754049773930079521223071410504190290814535".into(), + "6947102089761831011730597399319184690211329034898095828464106850135090631740".into(), + "12831053427863630744244436108113801672008361468343188269856032816742671083134".into(), + "4223577351003454708551493531516184634014386163092922955496011474432781253241".into(), + "2714897164693927923639587351413494956248140707486375397510165870974510685388".into(), + "20023038420823827383850598062203899779139064378489163672445486337197649825217".into(), + "1767883277856872395041944981599210930967554162796692711247113543638244036456".into(), + "11374943081906439317741616342767018365183355349301791743927619930324701508426".into(), + "11803403776542021481872407152864894420411711895295228328072919482156044458427".into(), + "10466014303238336135169608931533160584648879880459007777160264124745826740750".into(), + "20569672017414729176046551969057922112780177375685900568749563089152585958033".into(), + "17872595105302125548629012813042403567513885359499964690907253660570968601519".into(), + "10903006438854654900289559455490687959429486945748385817337220540948365309048".into(), + "18275515870643256842855966776500808668842291767076613550498342673701067299432".into(), + "630933085628668840611776843471079145620019967645658920379584401715066362709".into(), + "6825394902701793105668667323441283311921241346208501522910007054667207868452".into(), + "6858750193485140252798450603380083764362623733369268015553477107387074944232".into(), + "1478873465931112194691102753288035258894560238829532399713218315372158028033".into(), + "20647986868364738274961741160207964168903320676158610371768611791255451415798".into(), + "4340383424522509172928608655219336498337132315044301147894720301443278858359".into(), + "15694054630954602584443160345828698433451692126398102396637062194669728150097".into(), + "16587614091042345417694939817299154667821001841573634480871399767976428325370".into(), + "6780948432583055051074963887359666213579607675322530873343441117551279310337".into(), + "881888803549941181636901791599886290841557130000792317917044641982716656024".into(), + "8375540908772391314073560695793592104950470672337969483097880419747173630602".into(), + "10968989437172024632405943909737990264150382688586767944077705368389009715036".into(), + "18851349698000339475263334762001556036662188445499369323276947028195614775701".into(), + "17424154833179596191247217224032955577230090491464139783174467885567507995873".into(), + "3084264395291045886600299855227253813661906050535885634611163481700929635296".into(), + "9780424450121954867052166726264263906169948113675811543750657338499901472300".into(), + "14927658638057710246989480138441311677749528914441518171301964877558977872737".into(), + "9409069466927713011440733713350127030613235827582038407437204556786573082426".into(), + "21532551006723685558182869738272976023905972040234419867623013049768952103538".into(), + "5167833995989484123952079378988963389989994865716472465476503880641449880786".into(), + "14740174761652743774118447006447618386992142680472177663315668117221175944698".into(), + "10520805511348878943408706501985230943081177040800761729569633389778724896451".into(), + "8700454790689589285537042584692084777365997167238651880195034227220137266589".into(), + "2733945906594382609383183532639154621092632308934603150907320167864486314840".into(), + "10291836929398020145429078280830166142519086643388813863997726059451399134380".into(), + "1964652348248051415524467307507959105507630568171154193714989224874728277054".into(), + "2594310934406347332341406086415178782930670052092905736460388995932291385927".into(), + "14842121917468246033091527680370186171895171052114757236254597861059412933851".into(), + "10375528532253513340592396399825408524430850232835146379883683152448199357827".into(), + "12477762696438196028925893395417443678494787519078936487705085579706421333630".into(), + "18021944163681185812898833787149788656465569272261580921039580457574363675422".into(), + "14971348947199715425743490776577354350250241776207063239247262831298628671824".into(), + "18527079575564336560853661036281436413609153640671180843997315618816477020062".into(), + "10112969341623453459297605360048147871962444397321717541108011492952113746933".into(), + "9306811905619197449152929816385830205919355838468334319731910917748600490999".into(), + "3201856562784023690028197460260731000729676130502544364740684445045551547485".into(), + "17677019524999624366971265277439539545709248668323859450511125252478150097491".into(), + "13203760042423946863820551582066224159089369605713946028771025792976332967017".into(), + "8225744798383097681411492868795146058284602972120901801159373206820642535790".into(), + "1133207841388716114249120311196903646181388009397557241156806164775023041371".into(), + "15623890151225925383841140893652872349082273871391477651993101123938000801867".into(), + "21454879142557364834852736051259149378158105754952971451715419987896242909650".into(), + "8531552648559366596105198828222595210930697829968414405502802286874489507133".into(), + "9401758966490949657386555283103887925392012048184105735334446423864991206786".into(), + "14182985100305261645378993603110572106752819147111838091043200594637885017186".into(), + "3351125971378624693919014331249933776013465831551789721255791740304274394936".into(), + "19684429419661111328145464896944065690801463972795522382446603218587092452167".into(), + "10925858543362322055288327267322188904474484027644138316216578205314218206492".into(), + "638388255012974167128675944413974318871770367985490338348509002020449680093".into(), + "14959175299535994556786536841655834078592404933909341983568127492171858384391".into(), + "4565657688930940961208733539150773567906556683309024875952853834684480507269".into(), + "18262745015163208046754959923695809473174547423960055443621926543956602209348".into(), + "20270410959376379064306739018970053272238947330997122046760265674383899359657".into(), + "15290563044070434983378131412705775525253143318870138946525234035666015420725".into(), + "584590168302279667271049654248756511695470228213252786062709717390671267095".into(), + "4966088591187905712289854426727671159168968384675952400787560742388879645568".into(), + "3319669368740731092651343449167365623382886973437878280133897523197651190312".into(), + "9912174158284239213664392208740702249961859675813287499135373479592639861287".into(), + "8516379350240514281945243250529128948244709428452350892659587420308554264613".into(), + "2460558416671744640916958595519740745483455366236568201673238589517657340142".into(), + "15249503582713331075672114681569135202331428122201669911203912573537116149514".into(), + "5355701614078955989983290080356684040651137096346994307217952640790301753843".into(), + "7986859541259474766804707773691600248831464381736783455743764933501866864285".into(), + "20896597282789039779549640920767060676459830701472255011776479759676387046352".into(), + "21156365340242937197411344791352838273101399835862399143303393482855843470763".into(), + "8810320994466343375413166155559061722877903255339567630911647558329186883195".into(), + "13305939854322882862383989397499916187143744662574072474260034250574401506438".into(), + "14276510595076120373993498057833260625756300056428067994615344507361982835166".into(), + "20071507901284477038407301846126798169150113895071357904039436046848822333309".into(), + "8183811379732619835362931863732297053435925338898156873268534636771405892075".into(), + "13219046823251102005239988390708288792968718105445890139009709283489860275155".into(), + "9185773756453985715582986632685488321665392756676197265712130071752773976631".into(), + "20421943269702844355893550089604182682047061852707721138511917444304707579860".into(), + "12300280142885224975710767798713824153348765684179955650717019029210821361254".into(), + "12007970010282235734724332615090869243055192449773846575955902036212263482679".into(), + "6158352771477574748962005695592225162907260568470401385862937675095774244006".into(), + "9907046449530603675289866399335586386847026415848055552734816021036571412645".into(), + "20848733914096475328182569300389604774285326414256566622667108340302595094373".into(), + "8365137332145458646854804122757179721555979085847877907343540983873210953635".into(), + "5631007860876051233682685206319236038053360910127434722172168921859697775602".into(), + "7724822014604490732390628840430579796440157358556500058829335597037618014759".into(), + "369071834493409594945180455653375424679250473615867182702388739879310444614".into(), + "16769246200562822457100153851476834038704115916651426939703296994708244449575".into(), + "5761928395342052380070450997738075595651684639539927184355295101970822313162".into(), + "5566206280191314323446398313438814776510866532854163164867370641136219947308".into(), + "3342359366346342054674985507754083252076489241172458810297886110396010701777".into(), + "20961070283557581225918432539907379409525514793918155863543775859703676727621".into(), + "20556180232726542574747935614764094533414324377887922356987347326584809231233".into(), + "15941149549798383978046697670090370142285790712496734802869937995562571324293".into(), + "14600396724469636128403441949598983100001916970624980610808650856082650218424".into(), + "8853454033412621833577484167285435511890615294361962242058729914818351640066".into(), + "7796912448927288083851300561583091259511345752250157116939318925851741489961".into(), + "3163019852372632897541834965385695185434873283151315355155397996458747882742".into(), + "14952700133737822394693717249052150819987738575989807253015506109171868391432".into(), + "18525964798774229541041472797217632579102334608286281048443267822115517194616".into(), + "7102118124262431444884005767970969670970778150875473949502505015348122457394".into(), + "14204638357887780388176917062053732214797428898508168337150295424689096270757".into(), + "3277315675050465719399614805088484534242274431436504827247668714966713345731".into(), + "8238377613232984055051747513217839877952472190522754887201501162730400770486".into(), + "10224258402104106587652987351104010753816241645110952300303846844538654914570".into(), + "15487805061491340966964538995368847025698196465175525562567846982560655044359".into(), + "19219815971577427671261496058630992677146093954055100897228392314454716131964".into(), + "20451783587558695494081025729566916635107391693056303514267562508131052495026".into(), + "10604908883794901754591456082090038158328418399338332217230293938008589705164".into(), + "12629655676673767414687101606277783347408094199429831144029123923860076780652".into(), + "13709603387456116061265164612378749494975357559995793114409724336723134851988".into(), + "12280342248736515373215500158719410978133041790040105888782814256839967443115".into(), + "7016009627584978047738165231314996484413117067952510110423917440240605830479".into(), + "1524894728322552104763656617099911162075578893415210522528939966083567287381".into(), + "16110348090073067974052480411460325713852194746966461188536450049907379558864".into(), + "15849314408692105099989833438461121938666945258532089314493985018340786241149".into(), + "14526266680576776847324515988959661063035583100946967808594725417429124130409".into(), + "12662059588246809623132277946460950856148161126345911747740066547195150214304".into(), + "21784234245035153912497219470830935288918016717870099869682681847939205742526".into(), + "9781991278293660355311618579950123622358901576863101735826839255817708999571".into(), + "9734447579561113294373127302234593360683756540510409616709038385517649565283".into(), + "15783703381085552443769424496547949342637297192593569766965137630433735196499".into(), + "16818302710679242439066482280541233367708597536159643998239357880909455304".into(), + "17208557330797764065799659085570688998141169399652780006230425322841317471944".into(), + "68706297573456724111634550544331343558310761429949721000334906561427561646".into(), + "5166796490382346553866700788131214421083284131608596259217301814881739273429".into(), + "20336383765590527388425300334919391504612546166109544795528892908246473855987".into(), + "3743358254907302851720282727384172290999978135762491123193685721554867920482".into(), + "84015356866358900057683156333277329434974724445531256660202285630028495424".into(), + "13076432415761967873214874574212608989827734997835707946240987502102677974919".into(), + "674004304490160746369333425685153534516797008922146231179438460150958899661".into(), + "18008401489523347421324064960257465802035658427821867761642737239891301877084".into(), + "9230814159278735889507853746171683425354655931537527400639327580576056029011".into(), + "2338224170787780983513724541155899891770286010849997183556172649088034621522".into(), + "13774730216408957127941425099141817093498762972537953648152397183615811260761".into(), + "2008226192472961561861571910859568903805945585734651871281309234451941696550".into(), + "18191659524918356873059208424871952262892246019889299985115112751645849510652".into(), + "9977051090514243658919274826700877766433577306278237326432737577914469740819".into(), + "1382241145032463690710185532538145321402596788581319285825708537098818318411".into(), + "19630880072807438067933226715597469053645950344003618599545076855182567392314".into(), + "13804532717560570932337083609013921209558901545207945868049344755900793666387".into(), + "19508550180350246129831873366824988420065599516069494466305637280501827380920".into(), + "2848475116255698304240592397534148356674367429794174368229897070816851620326".into(), + "12588734179864636951823212209185103123565413699799377322920024420368718083095".into(), + "2232868200526350302935711180734463487873472319399673090914563494365441045552".into(), + "21377055866142483679502571618984666212079397034684178104676086894866487789275".into(), + "4635961124592421709613730717934803195170398911827834468836156717082833802089".into(), + "4795610144270291375198683497266358163517248476702360287932565598454128564576".into(), + "19878139464835077446055142241229890663517120197298970825225586650258704320634".into(), + "9323502139000335770056400844753650839571196769328639117682123746249546059680".into(), + "21112416038547244178847876677595343035216538411439215223028113590182490455841".into(), + "12697374728966756322005607409702182672422756890620656219302331428706184232671".into(), + "9995295835719608174333254715476035051015528223152237761898234348962139965339".into(), + "19838681448410021391386343877373049070883481927354420971134124213644615857262".into(), + "13348946731323604604321164467837298053616765723820806627202376009331748334141".into(), + "14459243825598700354634855807582241585214330632597159841271530816381999251613".into(), + "16398077966528830249699687699527475156225434078425955606570595942126461545256".into(), + "7643106356234289086355359290357087474077139338168525601611444369470624871398".into(), + "20959675452873599571224578970521717955557909237727928302612036851445583012450".into(), + "4540025033646420810215611052662284909609074316113304153198697292213556037365".into(), + "12027414908456226247222654846878068961735365399441671468269489072556154853636".into(), + "1619579475293093356383780863588610629947769633032102520272612883476001600909".into(), + "8181934427490631780501305508522146234029250148182651874753791841738732508561".into(), + "15274797459753339681175428319112732573480605162611546476487133996977708752899".into(), + "5881626303176748491435260832560365957494745248996859086310569735454431253109".into(), + "3420994635070209394832291125346590627475632172750870826118213841463585026009".into(), + "3253514739572615120245273590897551489662089732892213994698961442123844511104".into(), + "20203053841592538933587679570153207252397020205630948336602455897993666135475".into(), + "11921266577552501086897102705390311185747062744461377484820893183533643304437".into(), + "13241907169609567850145071795854997578682645559619300976538988140768348089882".into(), + "6151712987809690919701305472528460675037031217153195032569932714294427052374".into(), + "12205705969228027397734509241211850240154931572324813086389133244191540704608".into(), + "12945812038464520921512101569088203002779738300982254987224395885526111963420".into(), + "11566504805689497712142963920462066842262053081516919575350242192944407040992".into(), + "15723543550582708278977667347083710027075971342576647975064035406870923269719".into(), + "7860258127934035634020846943939055662221372673596335233171159964609391837625".into(), + "15528468237941422862373023290041270186925402725739650209357342065347651170444".into(), + "11352800656251355849609307793804420136337526597168059373534052608892527331301".into(), + "17897134085314321992998873273654112498897006369781950634488633467466113054350".into(), + "17437455229098036757167276638155356442563492876806914963325540308515770959304".into(), + "14997752382200324825750631040991638912810425809039371028733815341246302297900".into(), + "14919207184372260640968465889336548904504385718633186760383678178843526009885".into(), + "11844428225775333775773424762116332026943738109456526441415077427972318646084".into(), + "15239467296659614730514306837963031027890655569818408146133916080687077630265".into(), + "20117441496592956911595459290983302272515832294843762087476380541606326158806".into(), + "11211331365407064571488620323378276965399956667340240900293979889578003659573".into(), + "8791311092499361250396136430755948222248236194668427353488891370543849807047".into(), + "1774021261549926243219732938834617869058292002083739109596586460663663531688".into(), + "2787995130097988538722853640105055460288688373288517482894772682647739193296".into(), + "14036419256752421574134832204020173449399727322774622383219518631065684283606".into(), + "2503905040784637175494196915035325275253506707537902487189138102884611289404".into(), + "21664450508754049721193573452150382260579289185348178882539067755355712577906".into(), + "18957082022313129842427206117755534129197834418112547991189485282634468396440".into(), + "16781909482475992329419834248503013105141202009836651280677498803323770600224".into(), + "6643893904478082560922317503708920766922698054352006836803634579280954593309".into(), + "20411224098851507032152776776680744497103387047587118749494718067214956146818".into(), + "6932200046628136855984161612336282559188694975968887215970260451999517971798".into(), + "13683745075914427134220228573435856137115339570721809234203113630305711234299".into(), + "12049119081343437729871267418004147930387014906392381996118616615174663353079".into(), + "2441975952742754201500338273078694079713769380080349023008653075072257968553".into(), + "1011172201777323348203437837012280331103466286486119939185319004696667574496".into(), + "4513858889726009970880526008944305706495472698013415303299122950266699858614".into(), + "1829602352761774082971266699128478520319034089172917557099196106572588528973".into(), + "20888913280392789424820640494940209099778028785108373950708163556643786717499".into(), + "11546628016884748339883959858649314450404290142635813671623548341391668641333".into(), + "21503514976315590685255962605196280426006590386762670744592186136529638021924".into(), + ], + vec![ + "21579410516734741630578831791708254656585702717204712919233299001262271512412".into(), + "8554993601136913148229849281645942416873068991157116548355045570766869071269".into(), + "8349770263904395404819051886764727880530744217762197718931556224723090619132".into(), + "3123463970516625956994178947134086868722089624251980030957656091366977385793".into(), + "21442360932957798040744480141231788172382126494033577704060991460078536626315".into(), + "10231325350034913697901001930461380417506010080725776869094346614943052057882".into(), + "6920436402694617694727322082450000548200664649231576891284834027764418393590".into(), + "12792717999817516574604019538349201413861750406724026925198874802923611904714".into(), + "7319083527910098850218832163004092895955809799710817531274971443221833500573".into(), + "13757426179233640966146754686419290630140910517321420779897314617147307309749".into(), + "4049033549996591060740078431987567671358359797940903000648212935570542836589".into(), + "18201423118137949240970920992151778204900119273029679711616513196892916845798".into(), + "20625824460928171809204757749985517429359815439093046150315733891121610507133".into(), + "10457729085307334834523167401466014435492132985358294006123747181337070073721".into(), + "21561527744019186913993064335391813055903937050713577176254373319368609289121".into(), + "5599728995155490107164072595052340911357670532131511292391179640158683770855".into(), + "13966745298956307615009517188536529139238646569224392383446375189982202020807".into(), + "17756603569040095098346793596909383204174838953876788800894937537311312048006".into(), + "21742079076354402484587060728532755692106347543073105531119578054037775042874".into(), + "11100784872920528132266123983509067070706469425630493971770997902694662926998".into(), + "20400085312205960400585536190272432205634747302273829805331461533195763963464".into(), + "20028967251238446138082148432746545470729859763361092299497853989733022321309".into(), + "21646094126368547381762879012999402861347883442032865497835121981839683154574".into(), + "277256790316883617863153728392861425598900309956876809085316502674092638050".into(), + "829273940377701999291777589563653090200708284690056650568100074655963961702".into(), + "4606908934947031763433560217361121304957410936748694859993455652227072492205".into(), + "10769441872728289230396615620861141176949118733537017427393172917499470840245".into(), + "19521824504454300285368889620047541794275889938757845035419559810899465345698".into(), + "17161053048471962353174720811774420740284389196847515292313979813334039268748".into(), + "7908822737820790247231631548479205241063360318010733129560952138908448461427".into(), + "4877162162397125215823403409232508291458423909077159953565289381413423118030".into(), + "8487393998302601588798118543133789294087935184558260165377494640490662085979".into(), + "7454433584826937164880257351721993831542783218228578962943846432869272993591".into(), + "12600486335574416961082984651671003440366178113351945406282261259087640562075".into(), + "229943091042136639964977508364517877844589816262259724739584329059854831474".into(), + "5964363464498190105797451630207382654570897906930358361814931706994649645813".into(), + "15027885081212300130366181566116370954163923966760653842199145107431036749190".into(), + "6389712846176883524184535452348872799769012323597964483555439005016828865357".into(), + "13050625522428562689464418495099691361897297012535198348448952952830181214686".into(), + "1457960163867278804802442802649716001232992897386781587117754753421449788143".into(), + "19121642548533119996481133068671203033851078573250942970641264441950592334007".into(), + "3319626593342830359906793887689227542493167081286725783452961782138075389498".into(), + "10182025658554317340763807114890885589336807302908478511429803960136159439487".into(), + "5258867553475471512860996445670851629850555214065072419972647507253648925387".into(), + "17105844700483111456515253413030059462544811526461544189616915804056937372339".into(), + "15389507448590279891790879860335127747331525679865083633840630125275096453854".into(), + "8628144040598587326275852302297295030455205882673458629883629373821226515849".into(), + "7225764039772127797033872800338173049227188735693607855118081892986058306767".into(), + "20070937673840272071045130712690506696769042932336737261842298381743641619092".into(), + "12139783026483217581244209544149124607538399031092603229603855979370010147969".into(), + "17581810038009568123079980574064070648109195109589787948955203592875730952957".into(), + "12141791671600953962785570868053442402210784762014326945761482080946083167280".into(), + "10216251141439191088257104654134450392253712707713481968388586155680061818083".into(), + "1414175852848441331935246181908753253333655328715371554777242869802352097003".into(), + "12411223399258687363418284739063179467323133097416451119653175668156302546282".into(), + "14163252864986479721057694562184281568622251449154036885135516034438034547025".into(), + "18935280158362457804125825095786762216868621594409914695877661895590787449138".into(), + "19612073572528301850608997760721508284827614275438129248352825723957284031526".into(), + "19819295714156197944855748114967530774512172286810045467127912108030396642179".into(), + "11371593776080642722788656520803479745110058361122399788254568568846349693622".into(), + "4027805955664709942434150181718117928301673452282014923837288829996079949500".into(), + "12539691854417510068939338882045915380674719248923282579976372900935687263702".into(), + "21456335515466708235982252733061551106892811579323562996977622319059624115114".into(), + "1466175641997386496752167837552521008018514071345728218669064866303097231258".into(), + "16954396739281784813954958963214415095472216566673098897333193147120371509076".into(), + "12708223137926559496125521072416503266378368566414170219615449248989766379947".into(), + "8788739220646322755486256871812144068464402944468818647293944655221095435821".into(), + "13058732292597055849703973806172477675203122319912563406670103404654094386664".into(), + "8931344638882118593791237662384261193166536469680242356398517062367452395384".into(), + "15456845400516927354313637168726345061971892967841823745636300923188629474327".into(), + "5751588038559337581650296498368532807067353107651773867820816744681643949204".into(), + "11549544825319477431118343561134281237789591414423676704396089395115754641434".into(), + "19234147263577254818888926168924920479297919454657521855750553715796101778809".into(), + "3648349134208466654728357812767145066715472797730454946149007751312314206222".into(), + "7718151953117918461425809889893754434608769559584222279828239292761893621712".into(), + "8845522739821256867897474373924647700071798803600128774472020272335057310062".into(), + "12793577303328701474174653130332291657457728764837263636620978444987214166803".into(), + "12567791764609503071525053111715537148465248715927771041171097136254310005533".into(), + "14173284996087186652368776168561110401474338255050963923163796413857580470909".into(), + "18034666979500281081740131331708674377786999775618310647791265825609322054725".into(), + "21422354834639531449049641141105504766268284938752536446702823580190877745329".into(), + "10861911722118463296713372205424749768917665229584553370962914232866310912045".into(), + "1426840929949909140164228257293070123281693940796332643637029311310121856472".into(), + "14301944441994042783232016477141675248310618781100688243801831318561916576546".into(), + "8689261616262362847656173161257424730101884874532916838450695695508844076137".into(), + "15123977840288488307479771803223205244132730982232338102604391529168092315901".into(), + "14782587644869453236501780556963556761570896168324364501980524203741590116061".into(), + "20171126664277707857959263654502050384578410237255325322075457593732181023858".into(), + "5586442008782671473934242848395070351077466917106669778054075503048330770950".into(), + "14893034316669944289540729978541666240683450933307479859464390524607307041597".into(), + "16358386602267214062406516556279496593235072850353941542010428321612942609886".into(), + "18848866854232312978702044457572917667782740587353338084332267136131275700603".into(), + "1579215194993191651478809349088803658155969078580739214414910140585581589538".into(), + "17033458213089087701892498495271710586197475793707993846597834001983636294290".into(), + "12940326624292849673877504632305122955030021835426715254781235159065401203407".into(), + "2093340797218797584680567638361507396244460243439847174481303012347581894177".into(), + "5964973748129501579884254138099588668727348462189690734364404017042795728252".into(), + "1674681106235348685135834192630054282175690835155947917214719741317698144031".into(), + "6021317549494232079997036595203156655990507346855425821696978600367848015237".into(), + "6518804338080390019586997346732962982860290823548982950371646893604360711024".into(), + "15170463834922876947772409926040699970156014460508617331830728121385518919006".into(), + "15398930479669448663557196733417026149527004779216987588229439497346738958046".into(), + "2669700622596766237628533802450875322874330587389952384417956610466102910333".into(), + "3127548363874797616403801375102757494200866522631068411037184158214286131549".into(), + "6584403272373574724590091428656742867168271029343425772452216864199113551892".into(), + "18683280795877134163038651063011198948877602385157504892093537654399764426518".into(), + "12422086496748175124620724672115957665892586761203533990582978803368996339430".into(), + "9745099390463439278844126903162955736019504616033755299630228960871294951628".into(), + "6064302059807957253392579676216068692721552882853761726090911957467256977688".into(), + "860534097291826421956520903118828583111860517568849548148819591113129410233".into(), + "9809207437695386100460912579554305365027175459186390700555141956544839955242".into(), + "6576375143489291749779792018893403099848704832143183847385791291045583986902".into(), + "2551573667498115865454648920084921687986702958687913960418526064766186248697".into(), + "13043550024569409591105305093191112805611412364703194793685224224072149855745".into(), + "5369051621601119248797945023525768932797813569336410551989722329535217332717".into(), + "10399989003670197520503648853627144005300436598931266893609225004624861627954".into(), + "6159561484143246751423457452493034991227592994791307133044136210702400602726".into(), + "19651431183851896182934111830326153107040303776630454129626690653306388341484".into(), + "14970612719926241839940820046954288242553272322468930717244806728631485407526".into(), + "20461999502486452961875044483247881758853878278954851693532423676388213697528".into(), + "5016750536904085805050275769221233811927007383797241751325050175740220466319".into(), + "17316427284462136919522043989265881044949832745678035885743571937214912552561".into(), + "14932533665158850512241105212984927846164589888111067103835286341225240509742".into(), + "16012484446855626574765806641361955141820105388650596409595164514899481874274".into(), + "4863651915422513654068087402811690721104417928537042800794511645180712743925".into(), + "9478941069339421252769300213729433894403874553023597073962166402867140590783".into(), + "17529734771936454727002429801459948360484278991049231778922771896004721758963".into(), + "12672015814840095133854330679674924244657276110030612294537194913437310163995".into(), + "13442667219867515606432268873704321985951188504382080502019480975401891351960".into(), + "9346556839116407181813364316149756946394621057020562307256030525707411763792".into(), + "11720199480542613604905913885140886560194773593236018605042270421391171700142".into(), + "17713550818981273796962302212731756472046580671829192490772244177376146261137".into(), + "10658520565101402948486320613747540160084586467440232263820881394770094857487".into(), + "19120553688581692745126354026518291549778059267410591682456431863134002720631".into(), + "5837704130879353469974552270945063041776968090189866547252204737075618880582".into(), + "10952573317837731274507039100853076769322123807364252392559268333440123751056".into(), + "16175443191562457274813386127054957574917457114692631929042817125665307085782".into(), + "15651399869272720599280980856510555798668301135962902383119739133368631494409".into(), + "17982602271750585864051043003255537160144994845232276906099438195600610259340".into(), + "15564417296959768207318803300620712620729991326343744282367518107640962243181".into(), + "20488793123009381941807231432363887878967153580282107905075818200161452173728".into(), + "20845615892337349138315927113904389806784179140299978993512007141180651572609".into(), + "13259443846669565093311907999318849863250202720247601058242623542433998488480".into(), + "19583450200980335366984605451914375573108173291577305129314361755545713207859".into(), + "838397221216450052117952481963960818729311704107008458344946293696441980221".into(), + "5011508169974056046810100610889953041631883487165044189687841384459345302746".into(), + "11361142794005243853743618662384069287777450724025651283005645149554307240000".into(), + "633456298666951303063125949977139107258301407861096110769739192555903857431".into(), + "21529743295731761646584336858048296237991767869832124757149704140550994020630".into(), + "658601336565148638150196750528474813469384561163113116358457661687263150766".into(), + "7199686023207207605469992040453620774208316115852720699379826196410259893822".into(), + "6694724393708237460096397340665472114749949954099504252044742926933353832323".into(), + "16157341357004248687598290467887486980266976396840580002635620741183071441576".into(), + "18735931046113570691341052792512856145472697573166865032702507242384749856515".into(), + "6329726929169898271848965669873324332951781357803010027329026944351232247476".into(), + "7061209522680426874403579559245042585450674594552752972115778072823155787614".into(), + "8946282535333125111854282749852344921885511854306802144459174056051518348720".into(), + "3988279129283542026399878866455129212655792613180690875708855315104446444211".into(), + "19788373916759119069273555853960393438264058803290744373422102428664987314058".into(), + "14781179276955000841116554151929609467906998220188278865550170013658249984549".into(), + "14439714507608577761834821622197467839862763277392515730094487176490304118865".into(), + "7462417821368326183154895363497223167855553631230374112323789393782682375855".into(), + "14594890619141141626245598750050255728680683081531253962411791337998057959565".into(), + "10297822643679438597162031819677962851770740929826506853838015287832878785074".into(), + "4231562753232550403815225933022904310027433526532932589628179163940950572874".into(), + "10646529230382575523755302352793337142051175018996981972379640368359926883275".into(), + "12164083461947216214412634084378451605511487715584004827824972078192861740778".into(), + "14686738730377226817976749475359230017849316512373192440082122302915229733394".into(), + "1972065207953025646946682878392649299678417507238551179215236414492110157365".into(), + "2596220810659736571653162812588827790612138851645327014072494893329877375848".into(), + "19742890478753895876191378843357325113803569368242719397467948082842949716134".into(), + "3057722811279760312017893583084211485332195649925192440822130571411021625062".into(), + "5078380046721228959752000757775271056076152925648793679038634805597314364689".into(), + "13994065550182407605627049394529818937411332791332501915425485445970019749196".into(), + "8718903390300613451595895490522223543548941022555756021584328963874682051659".into(), + "2830037047734434537263368457077478915382396457133799070168011318577509852518".into(), + "6351328336589112831842317252922662854527642572646149728733839010100363641064".into(), + "2326811025141337415486606567159001979990362270551911801543813258783748664928".into(), + "20631048108966815289784074608382072458460940973738551134226168800345554951214".into(), + "14721204648069833595280990040775556291988674304257774357769088964732410863991".into(), + "4551815408348203166379129282299441425423743023821112768447969406023780214645".into(), + "16626589364834731158131207695359287961392723773324953999720551575045565560017".into(), + "2119119902895954746578148914775899257881351646176141505001582342610666449702".into(), + "21198001320003994532825962721847645202213745138854311312804244693213734456020".into(), + "16575653563112300802890760228610449333758690368857078853765813869752023855520".into(), + "19662199829424784148957376290310955285339135428848477356788149061587904074636".into(), + "14365866723679934269369638167309959664282416562097092385771370076826961535871".into(), + "10462405794880410718710911820471224752218613463413073336296570316774568383223".into(), + "2875852545895825315521599376020869827316570932962420930916494831426174036683".into(), + "9365014190378730240070787324401488407601702337948470069729769089736757502613".into(), + "10207664772554042762314615033378156185686480986853035052764356129216234339601".into(), + "14593204638464636358074700677706356615788934459664487787695216859702024204802".into(), + "16870253735699395936222032450057462746120274245691549241725232755997497777650".into(), + "10403141045354931831897350467824057442824486714796947030202528195269765938299".into(), + "13491630075210993306306680088269974619065901790274179689314972497025507972072".into(), + "11737690900303251277784941365088697342741256105580987740031964012547987099246".into(), + "6479411522140791199878732386964631711912096436445632282256505752750116503021".into(), + "1083921069605939352705123162314489784481429719496507658938926140311574639372".into(), + "17653617267480348306435879910355154811805166995916787026122631473598046826333".into(), + "14432164328022071373295386637326904766549408251892516429892690428586807444784".into(), + "7793671760657336901389781721891199068620856343884708391197481940646184551315".into(), + "1959765449995198342923438542063212054673245344496347360855620191194506656856".into(), + "13782621216902843666964879695291399503179812919290969771526164415604545126461".into(), + "19078359557987218636232226316587656499460623053969287998311225512418446587303".into(), + "15876205697805498189610174935234268016677320029636416200517433249256912037787".into(), + "7722805406045443730324325528663888753917114626390901464015754094863680930900".into(), + "19209717507699122245389693034814796437142853968464799877186934474171699195095".into(), + "6585127313235216502111419023607202115168882276771320178943546160044534358599".into(), + "20805733952846662054565520828206551321957145521190409450891077526078523041277".into(), + "1584895259816785676773529464055176663163421450657396866598813628883854756221".into(), + "20856003384184708896097189495372900159489733605505425206950257455550934589790".into(), + "14069406225378546242129093232844103602751521581800986668621079503726397000027".into(), + "1028001874294327945398022002342466649656159450229921471622913285892988310568".into(), + "17966371882429795190944428324003030336630819228004846253694564940042988400820".into(), + "12876427863944186509000750451848678588143362805691657085788824358176114653258".into(), + "8715273966427022806434959651283926754916348745411538421378504642158804932403".into(), + "4267431569502908019256597205760133122178817596876838981125845973819244098360".into(), + "19262568227942313166139131845806786518421402326465887345846202580592913547380".into(), + "4587852954486808043358798482712674466376898718708260090686361850306798447997".into(), + "14015025676058610927837367157870446606501041323636097276737547710444897937611".into(), + "1144137690247115846969300874102656726503285917182531654472086138598330367043".into(), + "9454537148137071892124156670991734830383436460928166061331273469365959775909".into(), + "13012777452486288707879402995033258068339583475193898659333632638157561913335".into(), + "14224623168753818289919819482009713832024545924040193705962378465158371078837".into(), + "9505762419233185123340587169990814384174703626434894051560218285882560747356".into(), + "7019256137023476309554512440166531052657689673592456244254319500937647800006".into(), + "17246363017424260106221670693985197925080671704985089438076796011281669775795".into(), + "14408593968797438981684807585445491387554770474935530894938751288467685293766".into(), + "11991202914737654500568163346559814604911312199759237827422720619371707311619".into(), + "17485055588733729741263685618351826350652951158142561188142663313955733134315".into(), + "12950471790443580303905847354051728755537901376411054363054627208333559704631".into(), + "1323445558625272814455691764419102369727943978699511295257501714917998936833".into(), + "8841255445674311239873770890653537908142822789956793926192649058263474173411".into(), + "9401395313777449751102417028930156506880556083443899378760756340424905478877".into(), + "21060472724780336112168263494706975132712741930000971588397970288258676527061".into(), + "3812019230904757099892361572360806117962259446525402823615305574055615634484".into(), + "3514498070156020200040973833201476402797883738073136493951314455793989266387".into(), + "7030071313560321306345374157122385026218445152391153750375781343870025689321".into(), + "5268144785716401601955218888231720448573921710587461252164696334890979118029".into(), + "8300685363844078100354914067414753644670881295812529449763437785519065857313".into(), + "18450162872081547013081002634376155990252513846297828484205311669445865332085".into(), + "13292716648615315298838871484647252965100904015865352978197979561906010036270".into(), + "21013686439245380148735850740480550290335132408628769676980938610161935118557".into(), + "3221231898146718165495762474085749772444251029483710808533124131909521295435".into(), + "21706362586702075336538820540433124172473413960581336734430968480142138077992".into(), + "13486895089928308553329688303040674812803987395822848864346610815697956322679".into(), + "17668589109420826004140047157134934003621500937594400640720194981334871115223".into(), + "15183620307048155399117286900834263744557758459406645835780045985078670266986".into(), + "3170561135789021599641212581208901692806457808161683895582450799937372092628".into(), + "6919102281737620426489877909256066802544737782816026767549277126151765906518".into(), + "4002850049662127756373199253004358198702226377235167238172929008366286605110".into(), + "1107642403321371666617924914652619792767807515753221818405051786669061003368".into(), + "11885648350895482809772026695774528865889321558031441531978106387676087338277".into(), + "15378937381250173939485112486205353769964903768096281219667852296031194688940".into(), + "16437477998115322080973717158007622249171727785394053923880768422172683727081".into(), + "7621121366936849931681051136134866470312478335111295204625916349299382392728".into(), + "21215912298727134138155623335764864420030241120133029649046764814328222738790".into(), + "3170821186412794476815678730429053912378444748965057631248759273529827834502".into(), + "16084650047636623354069916778426687798588694909600147172336375822549887134716".into(), + "5100048383300431022278915674482647701788618482574130930551328380700470664477".into(), + "6089313816847452477548960449234592795575689699536263376449482717087314881254".into(), + "3168424144644268762342999941888553529072271091445061821545187264931478820334".into(), + "10404448021076504176124328273829362151757942013323044839630548232003974036828".into(), + "4252171300134965718003785947840070216898476595813167252439064205515819918152".into(), + "6217524790069168495104329195931800727381161902791016429835385350530530741236".into(), + "10144395323727769803680924125110392290775229805005263966394467735634758369184".into(), + "16773588435110330580333921944382990185799235928388619755745278707444537536196".into(), + "7377711139925591251943689121143870901330717424145420584097007639933333168762".into(), + "21066973161927891686455166855433069549513160220938455527728499561969343776185".into(), + "19501795117214672544349409001236794757366887214419994956397195381058548371627".into(), + "2696597170314397939863800656320896858584168884088153464800095340021127302558".into(), + "21168940252375267860138608985225319663694276566348965377356649424639627399939".into(), + "8729578229953090469373121531954476034394661891580932672218135172268595941310".into(), + "2843049930012752804094477290180353526348132895187059237802643422297864799516".into(), + "11477013052507658297840246977078282722343354831202397750212752940967096033484".into(), + "15309765532985207981165438737385487871403890172460978621511228072457639715462".into(), + "5276795386880565031975868524294990841634011123906486763299115154863864095167".into(), + "16891192769456289619320007723900774808412675144364121907391915236424354884923".into(), + "16679134204463371830386366769161735700621394109825781706069839015605424451750".into(), + "3726779668847121591477372195002524410424328772388206362047548845558525545594".into(), + "309779746952337123192541883987405477861316061278735939472746602872146210577".into(), + "6116943109304762420893019486121829026477809860415070409858407396285217706031".into(), + "15461534026148460547793521829520247652146949380168641953893161519404620248399".into(), + "17490188391720816485403388897026469756907238275881766262188942134609165349946".into(), + "695497190921838164093269283587166536603898439348751059907333515445935240850".into(), + "5740644431998005711645731796487090897088650307567273144862464339327188211037".into(), + "2924287064221347495709747210800576855931727720487579410745268421394700507499".into(), + "14573731858717233227103947986267826915706941233771169473071405909223613015343".into(), + "7774646633424887132991406013236011457803238614262239840323638862678503960297".into(), + "17118778781828713462818509847054627300883191299007086864997977171169360843500".into(), + "1050282722198137586289457046966751947431030691327586256768002473254440863847".into(), + "13445581272162801515105119436838273760359267729139949483205892441735924907588".into(), + "18349340856764570254875357271690106769333653675997142444108297087440025433976".into(), + "4652091796588614730462648254434910913694389428280196479944835477366269369125".into(), + "131910217723821243570751713964244292677069024546597890404591990585681690631".into(), + "21456499410534986907068306761035738313492308692998775099405282472250364294940".into(), + "16716064024995806321979269360595144737028204641252901859053814928024784155019".into(), + "20095956414904309942694457361888203543242910622115746869354406017074786974736".into(), + "3757071258857910927294547413264306476593820051587638010209583502745249383710".into(), + "10923680137512329262337058371790257353876483628009769758330988714085083694256".into(), + "9956537089548622902535815248396999405593511084482117014020012399528161074693".into(), + "20271486778333058297667394153786073312398256884133810048721290976749349792007".into(), + "14016317906581503528126751971528140956503207448983448742275011042750601060387".into(), + "8746591469817011275278863986926266284079779997046001490419468813462270883629".into(), + "8984522535046539605932734670718817033319009310710809111062522425202730307709".into(), + "2799492130108020790397631531338556641981343238572990117018166640120082230253".into(), + "20327087348772938506861277218558621387616751140016505866350214286146319911488".into(), + "16319897245400426733698423830406514849234661290508433522979200712395774573821".into(), + "1519870284239742680329151132587900987246869312347047206488363104123425891937".into(), + "16461017950418215698742181372949724919904570515241953684108936425007551365381".into(), + "18543391801737989528567594217602323229839954429370908362237267818290566814608".into(), + "8685602143546136106524472764079001485875727469672211204721272795643296788175".into(), + "12069346074371335540240816613547917114412632205795651309433084773115960400274".into(), + "11988670992502988905175891565316960155103243235237609851490650832292602385997".into(), + "17403966780192169176887636449182981656483203656331951842717004987726351707328".into(), + "11324032593816374026994927363702518223149146507370165210922763265919399924610".into(), + "20234844998585751045105028410680389242420574978256032494303069254030260948453".into(), + "6845765195018211653702320958640753289098798259762906597713727891164248375213".into(), + "3889696987737574856084707743921432094490126858489993810406205011091688718490".into(), + "18124922133262643422852054628088483763756251512543531233603218549062255836441".into(), + "12631624196128994950514018157378748957581711189609992774676240830354853053921".into(), + "16425963978111788432975031188568839119447517807863396285900192209489855884936".into(), + "8953995026906183527591662774513923704717356851383827135468523305945599975444".into(), + "19118152579552440466145418131293777218789512388294771840750929877489494176003".into(), + "1815764556579131181044414049066036485535218062641438722387702702907845125991".into(), + "879360492005214940756250098489982523077085264774860932709313739626061471725".into(), + "6498879898701797309689806511256248775170834501358785166173840360873803298370".into(), + "6056271365753495672454589488039784798700896703812263056079587620516360143438".into(), + "4967400286944517048147291964513981345417368809842939145340090658371340301574".into(), + "4566978800722351857193060210384689061165216230883853755194411631523409885319".into(), + "1472717723310974984615849145251472221350601734805367368974791217038995119259".into(), + "13776027174095858271746500087697263062688345981919975914318610607049564847265".into(), + "8855093701782146194984545464090688949611436896016570549019701846352658653421".into(), + "18811023873792469394695147052306776708450724591075344787347859872893669250403".into(), + "7691358114878791244762289651247071530769998349760928484062499608910793916658".into(), + "7225113154421014531794800873967433806904006446284346811024995482075131277430".into(), + "4255179040498688460969978231175134372259939403539293652133247789357971307025".into(), + "20794639690572618510879417372961642037150870482839234645523330418091682849183".into(), + "8794944058569076073698664070073889624033307879863301731569984292406059326253".into(), + "2823363224083068189197562122730561866372340074232470613435703914053034396662".into(), + "2975786158778622849913792385919164957459418638496064741275725856836606581091".into(), + "16980083616151411398660130004732794369729853408844917920819260677159874549034".into(), + "10213122016673910048073131434217437693976054853300442236815045010440415530751".into(), + "15622844733073950747464963822187537454489639825890383970141531456136711221220".into(), + "18921846985235911245949818418983694297719932450371747402698247786766269226032".into(), + "3046395690298904837024144793490039860680756832120703445515317729068809524596".into(), + "2583060914190138727980083409436742233507882746342184052904895573386721270220".into(), + "15629635543342892581496645526353345602210475132913669274084203765913418607483".into(), + "12153931408209967834920681475366877980388294821029465748773772434628588757707".into(), + "7196162362609954988171353261930756212243825869655325258155368337285953898704".into(), + "10832344161051287447174100117756887153534049211780756324764320129746636797806".into(), + "6499757948467889740913713634114407800462786362067580649690672122741467699071".into(), + "7899568931631241171429470880810625833615306002278541085808972359443075433638".into(), + "12736525266255297750005081634236953960899087644399368181751506194282329326346".into(), + "3885354407428541945392073706505699854042913519198595640992547091062316682031".into(), + "3816703467689192492336832188741146914005957253921702912680936907951936401119".into(), + "20505128951308490293180094071215431973523299690572491020108425696662802268726".into(), + "12432320732362421179661978984313902638822551634634152631489842715192622778201".into(), + "5512525665270220248672390077644266875095263411830016769341860810913920051168".into(), + "10270457412178462574290846807202840698511884913948623978547229481110915603115".into(), + "17881266377985198177178319262577725460740518218103427901653715311091974197536".into(), + "8510066829287194091329244743814121446186389458433991751573612039633575413794".into(), + "9055280011331581456260576278219343217137015829507887846588161405525910242827".into(), + "8463744395607465452751391868824103994906876103054045629692996934328842962296".into(), + "13446532739903962714217041803396024505862416007402976354796893648172348836934".into(), + "20461627446006946445871657481203161690018232261049944659088120713658680145761".into(), + "13772437540389795965930455187657216773904696937414401755764626097076342530873".into(), + "1466290965505206997895149562488255231958570328271915401528301574657737237351".into(), + "458020438009477539046680120243558779131470015073376206673948106465783075724".into(), + "1421211327829956939723172049232055433330521137466148179271043779757165996383".into(), + "10974718981737058413203531348845889514701700275404265370241257111393906385203".into(), + "16573626183656890049533788707333278060585059246627869841186891657544526527154".into(), + "9919036431890005168544961022934926297421650894480789723772572215746478261369".into(), + "16576030844958446931425212176715038836356393446209694437953952981269181564296".into(), + "14994178560671337822305816896620048994236927709898591331458613992689693774863".into(), + "14008584724223915404998855561309674162219693188120257826434623735246475372519".into(), + "1069086166685137961564210887004146480746150199033946836704942515923278172866".into(), + "4441018674700315637078726026786902627166868710369033622977939038672598925463".into(), + "7640939046542807984747271754089880008702702616120100267707112408768750045699".into(), + "8165588904763498143729494321532511422369875008004817175656306909794360343847".into(), + "7474879615118088486232080639206966699381035482748779385536890548622822072536".into(), + "9155173050525839883630603125759778497949178563635870467694197797566249638786".into(), + "1792459779962272311746727109790955426562388793812453852666007819191763428793".into(), + "20263099395174426853367767733535578420938395109027150658334981283108594115829".into(), + "10303451485708370514931418998595531573075964003678715177508051557531947678705".into(), + "20321308667229656744129683025590573929765537488045735596831342276888215355579".into(), + "3624395650764060579161285692706624195562301326135878674105170566731046375182".into(), + "13334418042222364794805341409419804498243524763983861307681793920401725352392".into(), + "6303209034428307796195867505386756492498981454350686160404486917810670093803".into(), + "13336041503866400337979868539402126587964669853045701517757148479437143265311".into(), + "18767353303859794045974955054051240567712613481262860811042439117400294729243".into(), + "17648775923900705033106796825075257671428721760143056038927896765118752472038".into(), + "21205320334769131061393260976939399463360336322318482845795119858952799244444".into(), + "9155233455417262996613263565710157090618649015115334735214563627053911364986".into(), + "4147290460198895367036286052342502315935666580918696244195287512520682701639".into(), + "20798839743961894523110712994056132446749297094165852697406325453288254761759".into(), + "10967475255107274575063249329253351284200208982490877243231819333935797701403".into(), + "11107733429142106765947338633972307528508245023783319424287268815241476152303".into(), + "1684156923230048092780410733570635056170405885844154942542264956876312136441".into(), + "6785232819930341498287206635949744381666087373588334711760162570781902812411".into(), + "3481752771358613640419060374040695972756380958880755418404724590727014534662".into(), + "6255654521236333133437711371709951475374611142503566953029069344699939884443".into(), + "9785296768537417566104801168235697242332552358062255540417443962942437906303".into(), + "932526381863697246577606288678833611354323840639105626154693740791708621269".into(), + "9020085732775576234837702925603013416533345825301814972240093864457581450960".into(), + "9671990085159642158315772309293609967281764349933520964615615069622307368396".into(), + "19046031964146698646372035717767563096934497588033260517438574042056341692955".into(), + "4697021545834663993102099542459528774847962616007459562129439303924850728783".into(), + "834623291329335140949595387679815012162663877433757180177587164505466461752".into(), + "20037517567993747767719709123741990248999315495577556924410214834773675677458".into(), + "17745162106287481150568322325139792027305354960331855484108228857447789511536".into(), + "15954503136290327680020013900783441236098225469294487261574644367048211928766".into(), + "9447580075462399824000861225221012934395732074158377365412928734066137521046".into(), + "17638109886139425399034325504572336884601102137123870982594354109676619973628".into(), + "4210479489522190727435015049116453108500323090316045735249662201929219316543".into(), + "774908475070259367041407977077109033779248383557848908386935310773126844186".into(), + "18071521129238587956292632288468255723364769404447125651660575676226691230621".into(), + "13782927851189257932376007280798546946814788900735661108027866834341111483921".into(), + "9061904191092017185731693336948023564331336704118962360596629665269886564501".into(), + "4646677737877430404232975108246717822854320420060743321121290725024507501530".into(), + "248089340757097041959106969939801712104654938085655092524027449699183655736".into(), + "1925061776519306799931873233357921480445138537754738082308417409427350959191".into(), + "15090381222590604653751344079459745088601676165498775998808367167579158825796".into(), + "1507946310710275058017347456880204243614713444138569740896198937197257762391".into(), + "9047126832872244897061755443779135724439127934417426920025891305321906533199".into(), + "13210467659674398263347498463769321394392811495810195742972914861526145024497".into(), + "14930790949584540337435206846889685669928690545385782178172828133028677953780".into(), + "16709614617820458913659268544942181680363927832511699782451755998512279824691".into(), + "21741729565131743651162167923641181853909251404044798652854243082519505872026".into(), + "485748276371306614511019734420369335387299906747036897161996666725324275885".into(), + "113729363155019628451995418785166919308643202089193856958029953269497359518".into(), + "17315235749721747007692514702872348378105966945192310104719562066051041365785".into(), + "18164826565342651411070681557540176534582615273713551079309038721773735887418".into(), + "619154088023795178917549465782795919604596935887112076630902791174323663727".into(), + "6356343581175745844660675205642675571886993827862033572086056964311839166798".into(), + "9925861911435596968816749828945917008180837850750226489783169376408872855250".into(), + "20485137494818996958862809145264015810010636121731140334074571507470008401090".into(), + "19656735537075233905125360479201796779994483024563540752004528779684256455735".into(), + "15774243068028338132223253794279808721177740456197787086161475053638867039504".into(), + "10497591647442850110427658608196767909160517521573199077258279280018098065029".into(), + "210438711366620938094565498037172584426960275594120406278610962685482768128".into(), + "14574298645311491089278146576548733364636851707576602363349001846285614576119".into(), + "12543161131818467611229160618197160281328232851440846538778664249828945509460".into(), + "5869885827840429160723685065960013290766384774919021040094074809404023704797".into(), + "14800413279643792835835488664661954863340552091411990276270728475507101877386".into(), + "8706244411264045072904148746698442560958145712983473320099851020229801141593".into(), + "13670524729264788581176214636184652482954266319986131183163702474027069017797".into(), + "6632699635952855355682293206130135801472538218093800086693644923578553649842".into(), + "14292924690435752338909900819628889644248019746008463256978678218121438364347".into(), + "17727807116656957204562694366530472131199058877798797418410969267673712196106".into(), + "11223858699504492688371995874202967948461173433281557745471419331000190821973".into(), + "15274518373659950909975149452889634174127158243424169830798483206460674313743".into(), + "7047593141729299688704504157654237466438485917193869775780994636019451364555".into(), + "3378912259124002728608820025874776626345774864595321854977438303831475863909".into(), + "8837103908146248796259735965370584845236705198353666155001962958984824353698".into(), + "11869977356793268256679742068396296621649101901864556682314737356997442919617".into(), + "16469201342422734819465744813830056682872807194264521097865986969265941221776".into(), + "12990791567002738838300315843749937126679083554067128201599773654099601117791".into(), + "20519376488199491586810872596388233936561051835647732260479226405592285629272".into(), + "13549175857035221960740890286176570298280920124622104651989532035707272553373".into(), + "15234870385685844193215578943145814211167163612264546132211506983633757513134".into(), + "18704889004070339618907065844757112006583041625995006884582590095948666509530".into(), + "2234955996987396568560381823250866120960007674191266942270485492312904973151".into(), + "5564977611394684319243720881013051542355462582947649518330549023416464959605".into(), + "1112602741380454855327102347931753030221494100363029829630354546334828994329".into(), + "9128209482091812367673670394219617423875730198454831896130112252113491505252".into(), + "4376101007428143823621574836425643116618104664163736787280388345260726375856".into(), + "17331301745157506574267466700454501468193891305005371065208065202603272248798".into(), + "21846545094192300969525954249087973697025055955155275797969191218036594965229".into(), + "4498927631211901890366201669629694821005085404418517058539528045609417501903".into(), + "21785888954457119007377380145673667061898185873161711282742721281162112687992".into(), + "10412875728643419025694818649503070798945684772972009054835016566542500457165".into(), + "10759299704717838172704330861378107927462309935609553476471308013349659622009".into(), + "20590357061487044454315237834494568787816431390448334652405889873515373283815".into(), + "15673836157910318771949663697819073723405442238797105611030274826598578081102".into(), + "21475838557209838850221160141181337897147127057878049055974104365046170025610".into(), + "12207716311215040854533276425309298962214124105108870363296256769166507046674".into(), + "11559647355273809434894669847822031984160367996613651160672893927347403624957".into(), + "591243049503910187533030984285442752553334033673422383798814281778102029479".into(), + "16149820605774446835537612705920133098873928503868848274855310057025837143826".into(), + "17994206225397988293963936097869072606400130753097128779836685484696065715846".into(), + "14255641384045583862403050464134422000560975124837558223693835399889249787677".into(), + "17504545541397972120376845150557483982076571058727369435414053758625774050033".into(), + "20262964605263856854671661755299361371513888427580858149435552591882491905348".into(), + "12438123120680430355474353651690582027160630756728481836282192739044572633306".into(), + "10986893609190710977332579571229244255566870443973548516403950419251545530801".into(), + "16087745879258651861681909407049342984746351306967389910641236570806978994348".into(), + "3161560484131968841565856280480697486595651138902139808256180482675228360792".into(), + "13935262764632300233548760451251751405084774897832419843437366109926436448024".into(), + "3163708356211087269300587340868486145250960085863061247671736451529273060199".into(), + "2135858951319828747123196912541413689531634489616050528404576322173340406195".into(), + "19499372732572987294622585729244698705235805453794292187854255591946466724248".into(), + "17894089552733756400990786239277662075181623012822588150546768816557117698135".into(), + "10627476289794518149581909173158184680927686574280550541626159986648732476852".into(), + "5936760260909841348473330607703047527686233710292277373302498592584078811475".into(), + "1671632573007285452120449241726645612097503185776127730816609206067570503177".into(), + "3240964061357526102193296883771275932317464053465161060634703562592821382373".into(), + "1379991033533041123683674223861213511881534224092884787544607760074673933288".into(), + "16612309984678362724113323611405699676953754495433780645121123868554119586714".into(), + "8990362216544353251183644843626621823440172244041771554179888258848070990408".into(), + "7792126163077137553546721412284336099138469839853005420022151183726709890605".into(), + "3411642164274869168109174406711447272755835383047387248320765695217678712087".into(), + "20744964603175609148844959505361532818311810796853446841217756183414690636777".into(), + "15406348836110379416850862929702053477202461331674247559909297027963274533036".into(), + "6655070454278595774182716706613320667767771244472415371150701320921454970910".into(), + "5387596765852619562692588945067153848362301378074547750716364254552830548618".into(), + "11374645815567953400996409579523063654937913407852397185357281079289695498202".into(), + "4637259464430266322534397972465849540585061569384580106712860758129812888983".into(), + "11664424120893756026105365780051433965998344366144921839621845056675798876861".into(), + "12683173829762047157652990138425355617601406763777942452461639149511855161467".into(), + "13231456998217432692837253637279353598668867747754119281167569613817285339906".into(), + "18515103011277984777089818507163076049947207220694092563937098383351447664230".into(), + "12292599779221056615130747040145001893088895814628302035476053980079114062115".into(), + "6616224505832681391261988520162269850555430131210843369761179293711760268991".into(), + "9345928984249349577886491000914566117062182722884874880308419980252230631490".into(), + "18529045286534814640266328796625676801335117782021569247528965805503339014374".into(), + "9213994428894984896264727606602499546795637673535140817494171390234365346197".into(), + "16046191804660661030536723745809610250792224521907784347921533311133924729200".into(), + "20387305341543872348027149959300247251122586296441271309757521233850527145501".into(), + "20660888328716295053746457587105952359847177180169600462778173036912653930309".into(), + "20868619915262427287234582212448052350184310305114553346457821129256743588185".into(), + "17663781345821195819131504897468846255222386958594579879767055754372925042002".into(), + "18948627844717262137578097615546021495287533410031083974644721619152121684198".into(), + "13155646636245265066188794529431453696019006721019099462642586385520674193264".into(), + "11607002707400435612558884657345015071868104333677950253781252883531031962578".into(), + "7814494031495579790510272464332992505237344345380518199394208941034262720849".into(), + "1133679025909186037940532130396250706184367022550359228793807250960294591300".into(), + "17684405674061959945309711765066024811535672808158415749778270434181980067051".into(), + "12226477960064982944626820350420332900927073584585614331096659786290796181633".into(), + "19811925349932047710573270972607617621205677733103349721011497772488325290731".into(), + "8648978019377605720004678083600719374811142735403518791266825142192290428262".into(), + "13576053523641184415871793911230224707049224969614290468646557972152185575110".into(), + "19246597006093201923867388663361027928531131674594730473459152193253618076466".into(), + "658606426123772934076319360192555383963003130831902899767201848799898493860".into(), + "17290137813713243852250776166370982880777712118107986170022122607636402519519".into(), + "5468484217427333109722188824068218191654858622412608074953511216209386470685".into(), + "21574260030527757195923820887880669668169386788465310192452085714817280961577".into(), + "16977301410295947721817774534383483730974509825154556215531376468755351549427".into(), + "15733754299741726976786941703016488589821960074385924955118036049474131193882".into(), + "673051665852885808394122319132734442090810198084101653348055105938368910407".into(), + "13398071607416834058601862099131647257306816396251547336997555114713122083633".into(), + "12550653745423625869263455159672489463891823157792786197798074997479171696837".into(), + "9401077435832768325771683033269047231487705804464911158715103022642914069963".into(), + "12470382814922435426465904555965877134678525017061716145587676176477156930917".into(), + "4934832010550666003820836613445526232712748207490447572633148755378222021432".into(), + "10147431980198881931337046775655000300780675339813128987464503437796530727819".into(), + "568707585724749374908018432889236271799675619271430223486579915440515081761".into(), + "7940313483819289305875198195901580599194224791864182697672727755313391315497".into(), + "18743990138882019355059105644701125600589318429506940478381096888059645669064".into(), + "21390103921503672356366200239425679587351838964225860732507169466542370471389".into(), + "1162301008434626626179696713526875746106593765970497860274277780972785074172".into(), + "20866648005214775989252883040721823770262915606193206494879341544251201195074".into(), + "7345491533727875108206436501195735628498565230546589655556895625258093485858".into(), + "7625514906338569732343456767807169396379912415318224530910677456500209942160".into(), + "7862757427463847382674463970373593668082249748547169027835585420099101631793".into(), + "10678296207725321824311910857643347417220081751912275071080081222106552722672".into(), + "16952109495603736214033791235849594583684019147749856522239399814261672810874".into(), + "7275820797336428291178396248364001448190236523398157638561627689896470220284".into(), + "9969981466756203881447261853491208039919719371961809442805550510895150629471".into(), + "2820683912350770819480104529177445624170795283688064543451455124015297123761".into(), + "1768227783012842108298280205661549942041342510905211540896799241998425991810".into(), + "12106377471628405436369258554037168031059303858931832122655790690193581259368".into(), + "11133684149892568979034305171877692043755100938665306193308979862319584720202".into(), + "17034299967559262110637856726685484180103963204901371145115468175393665761738".into(), + "7846191388095544813700988786315012990125413238605101219369138067522789941990".into(), + "18501907765236851484806470528611625980973567313044760445349490286993727971054".into(), + "1249778578878632628279160684089277562912849600486367726058714364522238473984".into(), + "12800296338215947544269523539435530627816725976238738793426232070819346748361".into(), + "21158276531398412025848449677754681262986399643402199704435277598413275051402".into(), + "3445704611858969368326297112584817848554583085326694642313172161045347990986".into(), + "1096936495127696656709715702297708886712481387809916555366077657639639717937".into(), + "2595620917275871104056107465860277106075411236679259253450463497473275008081".into(), + "17133880510415980077665570077127522127719512648188691671645676431813033706096".into(), + "5074434632642876807560017812171878490042102579785821263047865225182212462287".into(), + "12326838696626858713891707574688276984801973627108664533453869827609207218879".into(), + "12304708188533179803341734069046800825101691162690698708386871234713091258421".into(), + "17039317314373515840113235553933676165005713953680758739139458602566292834442".into(), + "17138392904367957911746841143250516587142345504139102606501472531272348644184".into(), + "1667176916358419631869035157338470222219117704099667397276080580945958414759".into(), + "15905657308104601519308683078473840880633544254678278144766436492794698882065".into(), + "5818790161586099604498469370871887484543304527772047827828829241332373438952".into(), + "12442306790659206506330372960358262675060765197848150572966156688729178055833".into(), + "8830924073341698588202301965823165752475570241349344118304602842583809330430".into(), + "1875325218295968167735653385555759668710537053646457950513379995102733934773".into(), + "17881009017973237557824378556848686261064176014957336169110006388300690909574".into(), + "6685909350747636031504999384308339032316756393531361646826148497177153343308".into(), + "4784523754559868056092042886072700730690109555243230003862073871950864412709".into(), + "3579228774281988929604104014107948733265891035249209872698544379599035221295".into(), + "4279085918474431382642565261103862932684902464862097608738233691705350921017".into(), + "1137759862158982763491809087349360013612105180026300214422560224275715429456".into(), + "19720837769260646491270657488868445833170057293472427873975392955686128702328".into(), + "1718964391395646738355239526986888311027176887298320241700031309916500343965".into(), + "19881985451679395586972172156300827725375888727627109004411614649624414408826".into(), + "12172287315747539949563617756773178635584674160408496477097950831147264075213".into(), + "17818348202724817301996509561425729131915819248800408750587580377834731903089".into(), + "9526094675919376961704973755217702642807170426879590367299173409422751261551".into(), + "3828510101756167773639196204933352052436416699730184799283196091112205339599".into(), + "15234684891802383017182920097366274346299756278272648689423306851279308385365".into(), + "5259340948631483055118623587523845455777103002480150827336361752510452569472".into(), + "7522204711834833179288206454714805063533565847207420606996829544233502085239".into(), + "4461024416231372224773966846126492961855795819183621636555920813846035684924".into(), + "5024379057555899833767820321243484415757741022843259764504133153673503667684".into(), + "1944348388731771850836911827979470257687115271361319233348322098245989069259".into(), + "15065306781743278475238637320889039722240861069687500843669623160807616263519".into(), + "10185565710140214717926900268233420932083773824278146477403635012592608678058".into(), + "633282989982188437228855410820528038963373266773393154835968536206352769131".into(), + "15070609296388507673139787908428420573100959040320412658731328587817279331759".into(), + "21412207865606943642342520185052032190311633739645180935143927525277916223829".into(), + "7878150466166009547688919693656375135045159152200952621922018706527536944814".into(), + "21647238538593110963647188691796027858369673773306895927659346354059471769993".into(), + "499258196791436830394876102694379667868532135110557694409916330175138906869".into(), + "12884932140229984175244344672594504633237231438569204938753218905028898420879".into(), + "13897088195397438949124517043146998392030835253375096249186075371152838123957".into(), + "17292635307267344947001814307009165392877577631472640481991264040854674668918".into(), + "3402459730835246576414179745580422332982133349475000364518392993042878622965".into(), + "16196872248846772839551997397431726113069640679308998300903092440097004592538".into(), + "18016286200009868237596690430345509415593965676022902141045234608328285884800".into(), + "7857847074246536045428962801424396151797565515320554961976852417962849298458".into(), + "3893665144753462156875073379241733664547431241880239458489799597153819411492".into(), + "13248007323269384117634056450668957554780628822533632623855150478480951625390".into(), + "18276875079677833610517929467816562761552054119550676368999897123627796943528".into(), + "5189710327122023109841696471286837143455102090557692433286249568240177726287".into(), + "7945689534444199447942235389147404638251522098536584100372965245456120158597".into(), + "5423767002711680999240727030908088708424949959926318146878189397264151455693".into(), + "5995072902526586585223000514086044396450113354893476089124870631086600489121".into(), + "15275396095782853076485751600594247065910139735226081476243182046971110416839".into(), + "7337085477458019807945256166142271568824298087615645600806448794391563720590".into(), + "672727225441339286039441508031715798485173015033624765579287288711179551652".into(), + "18686118639158601820164163991883068337953517102808111440802394108699280438488".into(), + "14028695600256037576881239065837481650658243799767033132853744776667874365505".into(), + "58443191205451688703601063117164997697541381759347793811443271650169777861".into(), + "21065693836733911343227308560913091721048742432913504195640605496691788221566".into(), + "330731975523362608415899785414131718097488715216703281999950906065685546389".into(), + "16761217887229483694018038191674783405871691887163059178481478503168342732832".into(), + "3038029821773816605245101973977979223409779873144308602734440877027458504742".into(), + "15321291577034359343843366677170820527712726038561116015047816977743924220386".into(), + "13707411308735348097061010267850476217721836580681738128758120478182032058809".into(), + "17436870236156178819421588659602200365388268058796067042132167176029472760360".into(), + "6398050943215095842335686342334715469621392080870897191300696782676608765887".into(), + "3437851705781701539278760214561262405409371984542409183492392158766597659391".into(), + "7124479506211457523573057217353911508610466081033238660465711588737967142974".into(), + "17234798882124088040334413616145917195326697085511927239835381731514314303940".into(), + "7189678840883577982600011373449005624085144056595391210996212544957416984457".into(), + "11759897219791977703855743039753523539327151517291958119140418872939095386327".into(), + "20863399992661754827674386311991156323060622036945800022666816987897813665853".into(), + "19303856056826570957071409328135004293560711503205766767776395391371667771674".into(), + "20145537319102686898024411390314804436660254524742986363357188082440068405993".into(), + "10703774427752668974576334932100051927133058205544953271144849898179475571339".into(), + "18572949224433194996605971200517412011627412770605201537755920772491000856992".into(), + "17216568647787248412779004183002114283399247525457054305327730013550616639101".into(), + "1149230406252660804924057240581611320485721839789360487426995484240018883527".into(), + "16555038770640675528229594153638806495397639088923421322231578669556920716448".into(), + "4570242682115771574920314226681534421057130734296724983202350760375592356599".into(), + "17057311432878064305365984063315754288324488570316135529616408791432610322138".into(), + "10528094520692277398437810246572829940408932590689365643375030258637643836256".into(), + "18372709972473962897678516007961361205643396632377488479769034609756086158014".into(), + "21838679058944462510227588871439974934573578287277780576996692447397949481484".into(), + "1580935194501439869947304184720525369376830253681649968786933642354687903995".into(), + "20467445367185359735288490209436765649401864859115524728703996670394981839178".into(), + "1991219687938782033642326751499030593324853839359574632972633310438247709458".into(), + "1439755133435743273361732699887115117694558270034210455263872898360142260289".into(), + "16300147327505011303454000556020917254890545519131742846543658162285928042977".into(), + "20520490665047234670156871323008670381517271529350180242819417364083994994438".into(), + "6542862810250661443960454367170533117465189617181697143331232138215407738369".into(), + "1321343667823388542880641322895423041177577518845790583045290901719235409147".into(), + "2442599361719607622459103501177302579953568947914600055748918886479950617614".into(), + "5868547468478431065596263697373807107429411676393246849408309043501871198941".into(), + "12236312006031079186182743334640941452854376343312535021115608217162266564748".into(), + "10096437059927455034494932369876021874913717336999122002472353603141122745838".into(), + "16855287129299048285700210681474924546365514190517193678115189183373112094664".into(), + "11157210691115027557284750949314979990011151769293562411757900634452166569473".into(), + "12647512288244490046752876853167515938997386483355090398954955982145642073003".into(), + "14545106158760908532417185649648478400561191409850112377631214176889261943702".into(), + "13380884028176250201501329354617948687842379239413597035170382200236519084210".into(), + "1405533288610146305353614064658402594063683037599662723217460924638972391780".into(), + "12520645345847038927033831419634817247041895425958103228961545058464030447700".into(), + "5466316270148149873459535347452815877745317540692907701390798166221830387727".into(), + "5692512917835452114275964699861010134159019192212203758647174343214228494998".into(), + "20406430429993275527714460195190469680616300984772360022156779114506307005915".into(), + "2464398587529067351284637471629204475741470377175139272463319164375422871700".into(), + "14514168707742420576792107362849792246555009352726620152895701096554575857830".into(), + "13270866810108509432782712956020266539382253503195894711776480924657007904548".into(), + "9134846497886036320932877977667565061646780167819704730891157458029677347762".into(), + "6968315929580902986583144026090679082271496835598861747834541738159673277903".into(), + "7271494204378938515839411341192945686765403888626619615653078042877068436796".into(), + "18286507248612017881101512297381359645934672218434256889911439358758424747545".into(), + "11110097749119198784554144689049124335810208369471373426127558546444086832925".into(), + "8247301320031025187145317669710241636727060646792731418249820159438470005156".into(), + "14294937643948997192288252800043984341826315689719836039426003941357321500134".into(), + "7348709417616598268612977306891011618520420209855159927526151677200518293152".into(), + "8241665213979757536094384437357490949853796937135356315008833005380426051194".into(), + "14613486093064180194908604682667542845871833534699460114586605109597063839130".into(), + "9113009852475701814533040575255279924173382591876828814441237692300104301457".into(), + "13165038554179369948431676356672208670169443113631826535771459184335356285593".into(), + "16086269628133108266869931705626389777530735876369884384351431845165268738278".into(), + "6087260580031589238539099192443153008156470383537608551794229727048294996340".into(), + "20512862830064709642749702619534843169107182362836772285105007708471619038567".into(), + "19533181397104762456673826441378799870077181112746023293729567449839481548356".into(), + "14705683451830296925170674619865205995188883759493716060771015969186134607704".into(), + "12605792817445608495860292975716815181045267213200470818664710066347176280047".into(), + "3592996587655107072245474938139935892949344064921754274126873928977701118749".into(), + "15294232235999070733030126460937359944041996136808334336982336855120910512334".into(), + "1582309023481075306597300823293668340335902140969083485284345214470852671798".into(), + "1088465469116422019323930771299807477223547422484605269302564104323452973509".into(), + "8883047416031049757538663266985855517015256170002963846635310997205392683864".into(), + "3176854208559033403336658900147847127925792493324423188639056424099958393072".into(), + "13232382305984866276823393930594443402152224642505744090163285056748376747918".into(), + "2245408257366834949733190872330311995971080118654860159924241594642495281059".into(), + "2379830947280597194685159865267943843813010731722978284837340564719341924509".into(), + "17826177593330561305441136255554046830158210141415356927118963102466292013052".into(), + "8247294876117518524724460400399686295737179004092227538368916244669417581296".into(), + "9784484010004736296495309204028701732510703011300894445280176432324640320289".into(), + "5252332428538395620048044694806285484695976570037776918613895482295008833982".into(), + "14121980677685920870338206469007772221567007585360563336746921691628879549127".into(), + "6873280017888024918917070095341125311616790755139638467124027157089622101088".into(), + "11318363160576645628856748485098328456894971803038601852118504122294284627221".into(), + "4121152361595345093207401693369225144531013563993157593094962034269624651820".into(), + "20683333802753484511610127216996129924370396812165897253170891149253939360760".into(), + "18647670031404843711327708343249425853609227031502475411522973225042672187860".into(), + "2086657429367670063332013344180770222302417569271803215009583482264097874504".into(), + "19709435480590614483363007409253356879041585154641286906159427713982563792526".into(), + "4258866914660942354120395242129829740747833749509017072799985182732656616800".into(), + "9040566395371463976833369354842616182669127821598268274888838547664814843928".into(), + "4116084355554837400529216189356527577376881089426150781634827262283154963048".into(), + "21605169971906188704396331617487188526616554159276096155817564565239435978416".into(), + "17937211187232154582564924643486965895957558838414375726168318393094228123633".into(), + "1992558685702604567028071709121593075020313087150274357559040691997376317338".into(), + "3218330818271934662263059558699096796852863135305095433439395639068666897513".into(), + "9251019796910681440348368762961435892628247766190360978557679196269677702320".into(), + "15836693249656382763970061319175857247542072239256868703809948219144542014793".into(), + "15037961154776922819548802422798270596812938540093547487192133997609110330498".into(), + "2832985667948889103643595801116542064299718867280064733138075131374461684249".into(), + "17231308893316371049725716015653771498326104062757761825930828499226120649063".into(), + "18440767123713815329857061224743112764715289422724359826699690594664366648275".into(), + "19107308066370859685275861140608997220650596581264375407813390731366162034891".into(), + "5404929079073337935336499214267301362161341750373615311934457105389796446473".into(), + "20619481508117738469430272904911772560595466539395055218439600469328451867796".into(), + "13952701312362872281963137538185820474584748980975982879243876933687953690941".into(), + "249746714007340023576169446261897246896602417046451816160506088486861113993".into(), + "2031064645897563637919423800257706696891919492795925079196751344512608989440".into(), + "11565396752616415432819810596660051755078386394477458037668058318114060397110".into(), + "986400357247356567494096182209394879745469890962659581282966363140769490357".into(), + "4537710479751915891784323654735525989928281714716787626598475607721311119956".into(), + "6473679789570545843051971615499250002895246840329033388322918048865214899581".into(), + "9663160517547239942952324390296932496372339501369452594336052141919689254511".into(), + "5816463053578002459222339447164819661176549623835281810359748779077848682107".into(), + "15884891878740533639319804312855499125304552172182333174674400304788572220390".into(), + "3033403576169643300191876532878569681892028128211422158671974606431741064057".into(), + "10358010399523377550281291332476224381854765291561168350961812145251103430188".into(), + "5609119676745360689177377631509255444635743595725402489628064332010648311979".into(), + "9629834817212476647292649072966526100818069994746003213667948679343174132340".into(), + "9902671572518009781416980720459757101464549561371827438607346753254097417791".into(), + "7861900468190840021147507239605680877304969442537895993247780719118539599806".into(), + "17965713539282108900380748546130491121922627970953515439952089259237956451418".into(), + "11875127160859896409551848475414052588524539216162668112975117611722753563101".into(), + "12036060586969767703747823124372554748937989587973573397651022172958180951726".into(), + "9687479700009208904244134411901361698716242095445472067017865326177763818896".into(), + "9417881715546662771138833677126201059337478279387740105578558474324645551658".into(), + "1453393784429512178361095160478955627301424316545464923297558140633602643591".into(), + "3605661850144345838926820161654200172773665263094741288943401786929181563752".into(), + "9633343945573595088617177094944240138296472849064303543706399640795820973418".into(), + "12473139325003262775761026452954333162901791070446569863302424457084077761149".into(), + "12050383798910643577317544771125034193641466979797280393384224161250051391008".into(), + "10819280347517551331483926007074671789351833709996171469616831904311265033949".into(), + "13165812145639084020184122275986846677464323294523670942848342958951658208097".into(), + "4349080070868874275277456883424617452669491031060808106496345174056753179120".into(), + "10004465749978899491390807601888552616948704671794075232666191099858337042324".into(), + "5250553500845345497570122559573167112673420661316457421274200097450990672373".into(), + "12898588403041862218453963458103462644872308284833558427909835998108927951654".into(), + "18081519381738296094081786403175972446703155704846706982096034153576188388497".into(), + "17428269008435424731165207394539747495252057789950545989706433801022598432683".into(), + "21363561515458498091299721959548254375350819182606817501876503845082368630248".into(), + "7730068437198222255038306872157350083702057866362961468917603557490811496832".into(), + "804393402354592729694369925125379986785009558185994901175413585942989446396".into(), + "5734375191529745066389276413443500729878560514347145222515433157787509440061".into(), + "12298550311942812907291709877922367696701304981384797706564000787234950045527".into(), + "11599955784759432052811066625229516027776562566066732630526030964712154732575".into(), + "19052852164576098666742966317699449063762547007283635113650074658720294958279".into(), + "10025188726400272980943623464270412609902684461295237328964275116870569460545".into(), + "6716809200457040320003933672999684025455855713745782618771058247491604111902".into(), + "13265049252191301811995103374452481986951626617983097109447548540667223415855".into(), + "3243273032455921996687884516398249181181318053856384035604616213401999420878".into(), + "17596345835810824358133183122550505795231799288513594802190425260560699653345".into(), + "14128892069525759858946235743223382009747348980906404699362919849705747120829".into(), + "13560912558482649403704338887070267536467549681310240991684055262800855969142".into(), + "5146650134021051734120451478394931677954544403196703024645198760974236051136".into(), + "3656624380537215154312224820978463973302627823287220718508509926228536255064".into(), + "17192240779422792861455260231600955507004716950247796731699861536149977973585".into(), + "14505892053752720060006987522044578502315422621292564446333307406645448977523".into(), + "897766293354199284998764120885006378362273185992111179631631299334090746025".into(), + "9385522526111341509707107108914634969549440746118599228963287393277576591675".into(), + "16755551931355332730912571473383446100229118084434859591641172409884703288623".into(), + "7998487350537565460407312493288045720319128967533762889043801066609578748620".into(), + "7253071994474199371350196978282275858691584350754099653147319975858840509065".into(), + "1574915908528094850594155669399292110958198100218448518346743116412237429711".into(), + "4964033462714392747475438670479181630637512997723146160177292638427948047795".into(), + "448448106178274062085484969210407935775566171618153143848162702130236340948".into(), + "10632678501409460480908899293916037077432376165397750836428181066894132950422".into(), + "6560185425582299240893314836102079088239101938875583740502225690151903320396".into(), + "1579188680045650467520863974543764324416915154559115370865175124462148613819".into(), + "7512915739904886375944428372792517861202973027020854741625268080946313568103".into(), + "11437215569201917203849322845056384272839597879584906569042314064918170663914".into(), + "12242031850346916661228168797937142001106743366886274501414812437377823138597".into(), + "20754147834828558255955521879849243373047193258439455093561468286224927742847".into(), + "8071694345199003059727834592733443749021397716021823212294503054151589204390".into(), + "20008195907609188950845942022048531038227320303695709133926612651814670959266".into(), + "3407176450804539102225412244906714437508697739163709736953739213204054495282".into(), + "7164733851792769372626784323196646242013517015794517414070184073240948375536".into(), + "9905209321913745841790507726277960404981926942651589731149669818541827874214".into(), + "6005284264922151317117707687482989323446281090296110762814732182741632133904".into(), + "3429846891758468105169655789950721158959212737421209807144772069541192906919".into(), + "8594924739933231799186811828034272621585362372014000894229076451226180774305".into(), + "11871888849049325223331295756953912508046243621607006393548707001434995954238".into(), + "5685905463286653736673250214945816154927032180553041236977187605699044205816".into(), + "213743477607202689216486321834878291503247072102535716407202454507834205331".into(), + "12562677380228777117621213265298559705423375836884092254836487933903100167894".into(), + "8185089847800873546122723865396073717154949803733075710451409261160174437901".into(), + "6833793650798095878312843740119765700866687688998823144083128641706792979819".into(), + "4953313804596399497426456604424338320630850621290040554640752190475708172557".into(), + "2236942430605502498740725704270029328474562664606378240692747298661887827319".into(), + "1513449164658866312019182303051862147479160906256039744485216368678117608853".into(), + "20317977663255757433651259494490740600408545133225161089258307793180016743554".into(), + "2805473554735433677734689688066817081805362311169954998301659824970436339447".into(), + "15815182380045621180846286591593553907538664337286124104922652024183257765519".into(), + "8995040978147557956715534525198883385024370476405764975921649687446895470970".into(), + "8204191497925877512228406532110351579276808609895204619986274589454615200331".into(), + "9804350573365946552974871840565664742492437387015600083520724662514547605734".into(), + "2670072936416198613788578252423721270352613415834375050844481006480844299140".into(), + "18981033918047008332984286244333212532097768099085053122971946780791175897494".into(), + "12954085820551020895974547504930170442441222377530978192526624489157895091072".into(), + "9097838829535778653934125096834099047122481765699682650259018742771271485024".into(), + "7412500154264631610614836174268215158855286123479543445207793653417339063427".into(), + "4570719057023209933442917712353770885061956176422382707340131424665352082829".into(), + "3764270938841013974016907794451240962927342721628880682930414663597308799660".into(), + "3003259409352957401069823305356475477293574843963128589242837442372343327078".into(), + "4364045643562272249326662669199826714126432097046293502341074915354885207241".into(), + "7862497546139280137858115009924456872495405978857024048016201451567691552233".into(), + "2320345330709275050815413684718297588549014329479521210951823856644277128291".into(), + "3472052133118830373581093491758594658278444460214482406644025496494813391646".into(), + "18719780395522132269892764976785744172160001291971824246311191396996341879998".into(), + "2052034405003094013719516407189467871257070713148889132320853589752948812286".into(), + "12668293034393175159240347623878811740389002104040561234210863028172486003814".into(), + "3030226117016103704707119677479690730613220303512226016348578882444095217375".into(), + "10328007796868265401669271360208572151985515933147061282836598786351719442924".into(), + "2936234154384973501504058165728446155253408008794108135203900159696674018692".into(), + "488196422923618399001126311361138933209773385094782255292621218871598725659".into(), + "9986243106324062707533627601692123931503570667062976466271294044444369357821".into(), + "16946417777163299571223971859384083494444080413698624920773661721819621003999".into(), + "6290655646787769345150128237305014645896272983703662862175950979175390099996".into(), + "21576354856650767064171440939276202866931509433119578951322323205119147402280".into(), + "12396222895516500136715256668686614381644469516081823538030375243855459590807".into(), + "18323538829312240204982678233417444895412920429922269434090001873145951634449".into(), + "5312977696789603985280968610327711512128871567270265803635161870118441769141".into(), + "1256303934933114233929368123369287749699533819478400217199836088113619997588".into(), + "1766722289253438664291189751442574085464717822269757100835532731400395828058".into(), + "20358149792139162776765514570327167438522971057031059964363017830738523560815".into(), + "18792275959974477341072288096653531684536236050557537981692565367846354045353".into(), + "11262334644528432796511003492639130680887823443747110676752140224527884546026".into(), + "20501847923202184923710061886495102946632889060597711856226126913397710337656".into(), + "2407267120907499137598716355969720653158717479075795831021615986085480804669".into(), + "17307860167097742136268530831561503531461792239855909861554172148017416960805".into(), + "2162289869674344221546800054490037354766673860343307342595970088710829183154".into(), + "7028199420603561050998184473139307688291589649001033510888359073812068249126".into(), + "20994672525924208572413211399438489393060949253402639946739540459305617910138".into(), + "20173431861606460093483867873896693022082731481398426039971589468498814864556".into(), + "9008871249428555602906292660739315591043104408371562099899839775533895523948".into(), + "10700805877647067083283505504782153003756758200729717834117016178491024406477".into(), + "21112465741764258732843126921665151348166174697732269978267747373163853108430".into(), + "11434984188666323224207642407059759324684918143122828248101482207244887988138".into(), + "19774670032646206729857931401475084640123741797887602999266543966674579660422".into(), + "674655537444683637096570248236990032160746930820910298495859277351312061534".into(), + "12399952541366504233794506479162333424641228812125959815028858786807335765308".into(), + "10787188444435222346993386033929793480957373636649543232735691292731739491178".into(), + "8444484958726309128422673453859105535823575309761203789528942642797742183861".into(), + "6178443234860827296437957284319327879259579261423150811121652495400576061977".into(), + "9312686778448920745396192252021125483491892057300805307510293202738266404216".into(), + "21035346041382930907137468070148126300607151584874513632902588011146411628958".into(), + "21595380874048139145422335028184290720358814546434544789176692636485866078352".into(), + "18101046998013962303174610041325057852573934184601619434223015638490343790989".into(), + "19667939085949665955991312921627801957163412888505151951175612729243786613781".into(), + "6926779782333484660471017512018931263032231687078574887001518725808608895509".into(), + "21080823663408874605869582733040113428551737099016002655485931862299065393278".into(), + "3655825253274650356501864672127621389343134570773587805971056417535174536245".into(), + "3388541056162402337902270805874665052329588330241925030058121260891155159271".into(), + "21883983703980930327495083710303031860831358299523823198355219501906879066592".into(), + "6926045735792789706902681099796161456533517354066439495586980016226331896111".into(), + "4122959111197116982155867189740721062669675590322734927703841118527609239459".into(), + "13219980757163558837707442934888299444190968070207889541780648207183773341011".into(), + "13812897721847878024718349018307386755424010495939938584588468146497630813114".into(), + "12798580293550179822728288481477643428051047081525754174406779865733869649777".into(), + "19217125674525037886170888487423869066614323854468572412518413433041373346147".into(), + "375553361556668826677041847695421482571990138873344010326810282960129792132".into(), + "10834658340690112017927173678735250754641576226700757836412274044036729618775".into(), + "4000963705080416713341998395751411942820883040862140582570351568343523301185".into(), + "11715758996898666999645486245570559500678491132871745818362150491793495427655".into(), + "4263507095725155156517595569654198316584673531155392198157637482244808941446".into(), + "328008196666170836733724513867841384647787752809351062335576481522598738696".into(), + "16052279110256413457891846976681654137774297241350913171330588402386482933504".into(), + "8560435921497695081829647680343824474942212198090196568605135998993570233835".into(), + "18716362840678189592322462186141719951790452060116008532478803871048621361376".into(), + "6373296854260461347237798330630093524006414867838125038061282895929391886849".into(), + "18914705717475623119982002205679946947799155688028604423250694242332863378659".into(), + "3615848956195277523544974986464432491666056752600413462508649484050878259586".into(), + "14319383609652049721947038362087013158818175249064317958973877909037224815373".into(), + "16735322269478335929131611670089342135588205793924787907856943392431725411629".into(), + "20487989084414161462431324456040544956671737924589348832484117290264531005303".into(), + "991404779256099913569561460005735823176831315250109345694197087497778707447".into(), + "12403152875035099961283368388852584749460932263482397452647736730447907363221".into(), + "4086427505181709923565832636992346270489207870131792488930389136658523450334".into(), + "6143243951533181757415269381412864770996360748004903845141664179532399602512".into(), + "866706669878257575355607694505677029531447447184317717274975559227499614511".into(), + "5892914572161311963309388543417806910338156401784367910536431408323821746054".into(), + "17195629524346618476376551682847818577110341498528926796026110967915240955181".into(), + "11821298882555096497181913149211502245650586243756443688232286290083613236253".into(), + "9353931901221302781036617146700068445151424017933715326811471823048576997254".into(), + "287161824097962028466164990607592105232453331778514498980389789122436840050".into(), + "10649681231914156546306210447083574697745999931000675911245824305402757632471".into(), + "234428462053245029707379734508145490630528735544251100489484083890623632434".into(), + "18386986463595680658295944092336172488193344109864388929828691162200829447922".into(), + "12209205038092046905635371865560616384451716418834791040178049545643031921053".into(), + "14186020280018069417447809370772570171911004194015095921511996859327548729773".into(), + "18113938321789369780471778880758786581581161378582151974602552514969078582931".into(), + "4811662992940356526245596671631791612493389462608103550402208802794345444140".into(), + "20758900083194181903559645045132621401748979651195063688583538188953422299252".into(), + "9153554145073302417837255133650907646404668180089275208960151746312542076673".into(), + "9140083548410620433186361969381763393513996980985547052071730395844456832878".into(), + "1995008884754637182207447536267941537231422109677541855619755654197330974956".into(), + "20815985241080261061330547616924867409496454481048128927799047536950736307626".into(), + "3230373257763661238115338374579765087994728137593244037768237534083906441753".into(), + "15848607284344292541615961379571084541204910796797596203678379270219514113553".into(), + "10428395164296448020881569101995272889447042680020183589472054992027224827786".into(), + "13680122146242354024351667942887798315658431983426300974494489056276061013513".into(), + "4339724378247845795006138666633339576672603219988412146454211158778911139049".into(), + "16957453138566211779676251195537106997166365265767483860476580167187603818641".into(), + "6903442910536492004022094150099887848831560470459811862902118892324229883825".into(), + "13517533794136732881120013247194703467846939834067648417739110803619549581956".into(), + "8573259981507024845652388913624603863752296398651459450944134507080250987145".into(), + "7184725885552680253794089893559736740786331382981862187745268057810849566021".into(), + "7444911651670419666243138241908768267984541319117854062859415506699726860996".into(), + "11938251205745649282918409002562222384447298563524098642560439931062370036028".into(), + "18732676061737136378462265890132297759828778436681433094170064283098167411411".into(), + "8846532794008556689131513011423108042165045871690949838768507118557843169852".into(), + "18388218263536244281946085549244925864711296342518046885707040924859244937400".into(), + "17560203902531872086160045342305935339585392467343746359739902020514592872929".into(), + "13543101793457457855715055504451796018964110256417682942067062541530681224758".into(), + "12729201216656193290459172785293440324005383463664790973551946761644249872265".into(), + "20749632282565878390585307142178974866932241415051524225408429188255209021839".into(), + "2939350463247023543160206627078350892997171197177918468376868549940855641486".into(), + "479410521971742071578523736929989300479738063431591906751176641695551615616".into(), + "1215960229051771956876212387168875875104433482328478370485649365955599472636".into(), + "7395834689442865088318136274271999810879214824816761763663704696957588580019".into(), + "3437039086902274758103248708959011309309143141844585728091733160570479897488".into(), + "17467540498235313026126845934481902834360708027699640261573503748997917041831".into(), + "8422995023428881457749354920047230520807267797593122026229940569066728153312".into(), + "11738236614524316047928272298201726059224571806912522830504677268234776263625".into(), + "12990187369429149609670218646295887927962536468837638994349699374218402630706".into(), + "21623781561819578622543358003640431769369503776252057790027825149203611468091".into(), + "5669501143149367774701636329186878874504568114693026656335516415399035236206".into(), + "4575509333922357841125659935390034260822150782714631158836599383911958256934".into(), + "6033679984514005206127292322842556662338038222728990090780027736088500900555".into(), + "20960588060390074988596726655693601209728221976889320157850211914560680389602".into(), + "13413928900186225435324599444732846219399870995843202836070995363431243049194".into(), + "9234295907731818534748337816708817747432963728163044189490249482655118848141".into(), + "8555123146188721627588124037374319613226378704908045460846666824116325453744".into(), + "15273216586585654487893582395632825165015207795457148545076967758704213266502".into(), + "11125068324382272646583837096728304098536045737064996999867399544054690011336".into(), + "14959670405320615939216519873622952189488168539376564485090492806106125094875".into(), + "2609137341445825321095931245531863029306734693698914386115461720635228617677".into(), + "18022833118482437226280994646426462585862398739142688151319660825030833751162".into(), + "10525185277608968809171389414591123336341871721390818817629448578893073864965".into(), + "6744998464824276672348708743166802119889279164929133576466782672127996471038".into(), + "9344118411856392638882345685982595534877291739896163576786468663773481537247".into(), + "5834845743289993456669132331911158234826336724070643002101495844260807919180".into(), + "640787484559876348086629339096900566749702582335998287167888457763777550412".into(), + "3289778183958313592191132419001427890244410850048418852950969831934946260405".into(), + "21884067909485911557708530514125998779898644250629017666973972309955096019929".into(), + "12413233158576537989326032313779399359311922621054801366572548255508592334846".into(), + "19326971287642001797155516864971431422568657490079056599164481456075851978783".into(), + "9713216558784550226779073929454993288918740379425900055346553812422457613427".into(), + "18939709206067618242082635502394826733708666259540151764313557087718564592235".into(), + "16287201832008230557120811959387085081039691924582377369304872636500446900292".into(), + "5278016450516276499641394140854299047658079220708153291463991663971826612725".into(), + "8340962164398516487176161966485950102648589482540787401234013926762930809122".into(), + "2189360335587469104898894044890322510567148239646828290042687811234738731248".into(), + "3449789621053765306024986494635371148296184209757783340117745997173900013891".into(), + "9143283285825049929933624722851126967075894848265558258882809634241528781890".into(), + "1762589836333836268173539283261821177424666871790048223025892425442664744623".into(), + "14999716003380172870846520006929664713498492291151583767005426828389694408867".into(), + "6657396937759865997077915983426704746176142403322137472681086594905173286297".into(), + "15931240944401174032343453067874387364523175128749141502399457399279513999466".into(), + "1093656103569127166341414994548826968375416499363412936873802336598768367672".into(), + "15311996490740753142335607256171022029850671542830487572556180935394970969084".into(), + "2114569000427676164059607854200438792707180857430926937778356703166016436297".into(), + "20765151929972334364703309580474623940346949760797265180189525857343032900699".into(), + "15086899750477019230037863517768898777026828694611906648102458179274814888575".into(), + "11989598054307650119711820896035556261218721126425285775266572489599541225098".into(), + "9091334194223602201291135947382348087276953920280768131063598550602333641673".into(), + "662205156672119277592919023943339579805630811299947870362049797370818219037".into(), + "1644010308269285824046398201622466330980474102922491226041283891094107089659".into(), + "10802230809760799055453358968022894649048726749105153336410430294944584338077".into(), + "10002812375469387387638524822236342839480428168589080127597945114460807065506".into(), + "19156323136144190554785957482371465262949638224644638616894263767724257422262".into(), + "17733486484176040486420248003268701811107138651833773383126692059485133299226".into(), + "11591816707957137858747879818244290349664729458236317322129399152472444934827".into(), + "13438697403526660565724658068461816155647612966569784134628157461074851140504".into(), + "13266011411738324637226437761946084145971285033245245813436082422574964132769".into(), + "19496324867803620550902836425197207665281935127556215366411961309085032313784".into(), + "17828723725738030742535695874826508863700363541225603578112437608777873288872".into(), + "1354878734330830246945810749621035198088901780682128075198035721915088394677".into(), + "14191386869116296861827703975696048379271311137936832987484810982707013486938".into(), + "19738102036337682966068176606232821315534061860926048846327720022609950683611".into(), + "10125898950023999329674512710082674945903794489998271114320266993547905724563".into(), + "9735132529803303628278600229677357854336331158152745512812889827254017680646".into(), + "11236813353178034372164750145200156935906967316633703292491531523808184114591".into(), + "2838710343346183231321901296981007702601287106254090132583144723338172491341".into(), + "2260966150439461282635555159610786965904582998424984888756770014321125334804".into(), + "16205138689269860259863339330838066928731455712034665818609854024821608206800".into(), + "14474351689504859217283250911440109599364797953678136799363008066531050767071".into(), + "6800897037717406782446195419208951974623496109363697062923841346454038837657".into(), + "12439223794583479313810611548866683769924970108158641553689074442900057207206".into(), + "13368632268650760591616092601174213792332738038582508245973036690611922918115".into(), + "13045024155077800673372198196114071407850396125448182263497388710529843778012".into(), + "15909614634449771616261546064207953846549526793661176217246033322484959782377".into(), + "6286523689254946058242241932237494136885909209872547435875613686442878101066".into(), + "1349982286621373054175291479185807897612755726394394337821103602455947583575".into(), + "1790249021131563196559147247422502568900570496057134946620667686190353467913".into(), + "6402193114585552816271289059052503078472437339555969654050634927290142355144".into(), + "16657876151292774412640987426284773353540623435906797018927318161786869839630".into(), + "9323926757990898215781025566456682055829536111985821344881754566754550287464".into(), + "333557759600743416321063171376142987411973146341256173569676328325670137868".into(), + "2979415176549537735678518951412460315394554685744871595392830851604961695296".into(), + "11399925948142490745007741431204138659570087052831458877959853228053991616097".into(), + "17621136800256980786736718847997675507300992679800923101555538476888606832414".into(), + "19796186386112600298425768110232392434623500274218805673491160537841238382774".into(), + "6150633696896276954724869550196553826388538976047912638783098883173888551921".into(), + "8007744165470722314410652720217502653722691054745133622163922368886561184511".into(), + "9311054107030040402449841855464250035701299963653145080544526434268446648883".into(), + "17483374709267556993216232945834980676370211318754373298278341962960548363667".into(), + "10342541634151745634549333576310088998125069092773405335947539258498170319639".into(), + "2491113909032842407846736301373377587232014330531326042787834409966784029431".into(), + "2868273648562988738033854616949524236294200717479306399357880720258598168313".into(), + "10742331131465347969746591645056052543770202432577403033214021867405353469410".into(), + "11262784136416067912059431910504651513874071545823747299137490135956765416093".into(), + "1202953198131460912225596542947265149450470358249744433218140619832526467659".into(), + "1357188592392022798249752911997083133072840552839921198518049365021930318735".into(), + "6860322969159072475760401518666168198209741930191210869338776131213742985903".into(), + "17512830541972446751364043570597924188477065675882039340228337890464360413831".into(), + "11068769874220100931580712510878176008480346856568608049014134908792656846759".into(), + "174592415638141512586004221919684850728269107225502531102930877822615095893".into(), + "4529660791853304207880649392489892958123653811385478713812028357153062637877".into(), + "7261086657925858393339848092042570053113017792782297779091045392687309229931".into(), + "15947797860109427499686809495296483548232719158897790339724183452147879327839".into(), + "20792174987130011436800220305375988411204569143955960269071283382992617027310".into(), + "16004359657196683674112642475201997234099915262640166308760622340887283269558".into(), + "3052232100533123772344258509339894427157169209304015026294337193048877370052".into(), + "6932731371388662052904698619255543785138918159081404052613363170137706724722".into(), + "18786344120940971245053939784556033915582618306965577644860589607674568931598".into(), + "5543910602266336221969634219610367731229996706190750207556121552771043245699".into(), + "21500331075127237626915881392062922706776178350223005349901601754581763355631".into(), + "1647168321391235121087005294677186408057439085818209785557656918589059596681".into(), + "18530920602212288118571329897791681729275409540471839847360270409798512800049".into(), + "4854705606288041012191490612688235100018679131692705525031856778474580243436".into(), + "14030500314224264714821884320124695887543721243139555484376859209010937666224".into(), + "2929002685096227153223014766825090983318157894872369622190343561822695490151".into(), + "19571082362460435625048830465870659122288859631947561605202388130763965600724".into(), + "16262018111979731901324396497332329657354718141076944178560644525915310954979".into(), + "16511368307044025445048172352133100276074403358941934299584092446434550554652".into(), + "753252068911531248849891296487019536968643055711963719939888051839316478907".into(), + "4925743538740679971454033971243400511986874154138434310761553437631037517931".into(), + "6674573928014195188134308196945556256425786164998594495377003561132663149662".into(), + "12252972387729677079741673273643386252988421977551152663330085764982570989423".into(), + "8132942533382570630447992711549276375854208836400139615573300103975003948893".into(), + "1767955113430187982507552626731364143386403228830412651051530991601802225661".into(), + "15911785581735751807295094148896874484829666426702209837871379520505512480322".into(), + "8457495441274725464495320737396221038491121993793647195771335087939175598066".into(), + "4450592062983808718489321963811761580153883581981811699755824744572598554379".into(), + "17594374172823696288546149573800906143533062920680825959471396256808502015697".into(), + "5396987646216038506498234734744679865090292329649749592797349235509038812800".into(), + "21252439428941443609961710022512211297072223091751149596182317370399721579385".into(), + "18205060096578922655953660066798909616473486532390705801648322522122587090830".into(), + "18335844957174152986460129513698965339432209604680688295392195893700811299508".into(), + "2642745947776207344581721189041440984169021288344823913353017838424039626428".into(), + "12274412791221963394261975707650556785293993588861677826296117405653175236519".into(), + "9027620387743934195363518529726678172699541005293960006772332651314467807053".into(), + "447942484646789701542537528994314353531710489288574350580201677777950035282".into(), + "8057692910194936390243001022339132476522458573171096947792809940102911406995".into(), + "12466422220613642034159029765007116066052620609909566451840020754182208020143".into(), + "1171876794208841082635913903308771131102729703148972430947313152931064171577".into(), + "2996001803829960955342682545650059577915377668754838044698518610136674338212".into(), + "12306305078198172670943701974214620978597606399214937108559926306737496310941".into(), + "1710451046983264379967941386456520655992388623198157754427123889803480356000".into(), + "17294040628260706127805165314917539522165501484257724022309899345383198007240".into(), + "14850723644587717617416204055806811021029607124589054329504158261611106696194".into(), + "8626653016235921835569469801636971931942277275560421125462026339476626883966".into(), + "16134665167778000757091607171765139146858141948472867080489695707419285323252".into(), + "11129502792062576191937938132554397380912998297589916298721102071015359559556".into(), + "15130530042530114554524440015929780266039077867502338045121803478767735700077".into(), + "5922013411157909464044985892976718792018167710191222879749916950629215559119".into(), + "15641877241146443425426298971354708474362723075989298367558100488623078084763".into(), + "19807467678520679976886734556492012503982987205100734773481397135264223200444".into(), + "7230620319188569738319728589376800438996515892335609872222230868954870926930".into(), + "18168775389925542038768548563310374943094173375522704828602237046711660513509".into(), + "15879478673208122828337915995586151196586860000621630078538789187695874266640".into(), + "6026559623764003366786032824563693874782053604114144074018595121889075259493".into(), + "6581629708588609217727738860658907189729022916858514019350636298557029783381".into(), + "4392179832866307866014828901921327571555384668846640428849164693147430204259".into(), + "4039689077078382209094762982217352133778072152116592303105622250221872775182".into(), + "9439756845261909878224812875899975078023923372686432160229876632562036997401".into(), + "6300039613750170323909974701574854925454386098696929245502563775636617169329".into(), + "10245787402060152709811414110506095522941754087902005711632016024561712242938".into(), + "10770509446150307821311775909967816156602102352374955635502734154581919715219".into(), + "4445806424060983011786548514585153091691541530320683355086806781784085761841".into(), + "10891448460108394317032887254041302638257759566726100966329794645560947944086".into(), + "9055121436695773704587898653339600198373789357537888127317084878411452883579".into(), + "8149630563292186659644235715307853879418282762593116603286751824944805128957".into(), + "10164520096412584664920364217937374178249615925628067827666302990156368241543".into(), + "18787580258401627795053641123916277779446570183960070225650619813208411270515".into(), + "5840281460603743032409236132633000232507414411373674275076564056089047616513".into(), + "1156412247030909285380152844453068577291565904060426536020772600017567322066".into(), + "13125262001118293895463954353229315406286474760130294037415093324929424537303".into(), + "19341994820951858136925352932235917695982462478372266716377719180643995594202".into(), + "14230192493251599368583155353256194379698225636722091587422432258409725791125".into(), + "18604655597949661495485553681980778009433340166469812699886221824297364988905".into(), + "9484517765495232191823212744512718095318306206191891691619884709082351623103".into(), + "20661548256453489016691122417433244991048818711049327729621550642733467464794".into(), + "17946084952153038878596350204495193827996043096722462577062358769356218083033".into(), + "9008616567490400741620365097652579204686050477332773331876377443733844010527".into(), + "11222501299614818564801113661500553859475257082250633539503337648966645140931".into(), + "17547881514301195137575407746474668605213387222566272868597007270413523256918".into(), + "5795058045621081034219295632641782062332989904715400020698296776847639194943".into(), + "10503000549984486673912349445691696940798628902104169170719393589667267164117".into(), + "20627605141977035823281920467011832985049476688278934213570130229801586759490".into(), + "1617802179061215386952772461423280627948056362689193604176834379576303877113".into(), + "6791812933391289387114968397366223795809410677008110102514189342693442788856".into(), + "21469327667867855439208525645169470185070216384607993883382520539336879692857".into(), + "6209704596259199225388050030516880246912805490686902979517644524792187929012".into(), + "15048235302521180373501889249113906037936587642496276368753413449431797081949".into(), + "18778185090591194140890220889049934755279881763207537060835789721322305845157".into(), + "4164373610491096709440557803327678927854304626112734913682240155408254130319".into(), + "6352957909818668217728163741226734988658723962520906766539822539894434836600".into(), + "2081726858060934846688892186400729209835095311388667168398290314717411097500".into(), + "18997085104017244551079822479039381301947270057658366559839643454880390097993".into(), + "15381301300391534710642382148869618456973733474322247178000572705046719371489".into(), + "4198258606721523455517040976251319538290332464337310822593109666080121166366".into(), + "7557782631251063800473816565466881793101085796415715068491278248627266080710".into(), + "18931480224318877255548108668163955495398356426693160573742850933557366286581".into(), + "14006438920977933743471268674953904657230294434994593269165676508225740552815".into(), + "10552613214353127913757671512246056403297131067811718291694487016254806821431".into(), + "6942360360107439367031353171424554084301681329082198856946152988574077265518".into(), + "4821893567676204037011511732637248253220700045205121424698817536976139720741".into(), + "8570797921121743873772790445200803600261480452613730673514117912909339929304".into(), + "10210191620579364785116101817818502747961980686320008627354511305745123505207".into(), + "2219514545666614569412405614553938185242666345781569909039944438048257115280".into(), + "9472494948236350938478699115530847822498160599193967472596054792264811505177".into(), + "1966000492511706985369338139654568276174679894528461227404736596640963023833".into(), + "20046698515808110863133254224236843582890061845803004799852246848232152965906".into(), + "844879645041461183539563036099295737132271525297913466134251805988318847548".into(), + "4731835924119595590183070349774829521463227171971888362743688018910258502055".into(), + "6665591397442095781758927683212527568272080607916992672135848576259567872049".into(), + "12479020936095094085154050050726399793348028251321297846621658033596298393726".into(), + "13547094394131143768607425506026982562580191167096570425566382504500825930959".into(), + "14726791597032654225590630927699963688874228760066722543952181168729825328904".into(), + "16458751398904781219324882164741165137205147400663608606201428995139003250837".into(), + "1382702744652547575130131869708589984736039127219433507919721371542416805658".into(), + "14166947289061517490924648855242003709961143010494793848406604325534604905674".into(), + "17345393385322406098467895186375277399629828208438881931258894959842597584446".into(), + "12612093274589904544460621781003810044552285394822234432357810635329000095532".into(), + "2278721075910399058699498270814807833560549835812826924195171790394865401759".into(), + "1421362911034706266530649492401610048326109063529762443419685870164992649589".into(), + "20384150845787594899080953437004222732830782572958598551146508153682280572403".into(), + "3495056095843097816017033529053032886550694720911641681721955773137850234745".into(), + "14487246717687560703169821216097612442592434246076605506691782211475316706576".into(), + "13114661974362015466065924612631881453272278776220284208159815065535423486323".into(), + "9628837478322926908639029641093928246719582225562091179037197080152788423316".into(), + "16456984127855989467975376775709400558526389914848937751741445859284106536098".into(), + "19656266549615253685970702218272816322599871270060998775752454696892588799707".into(), + "16028536897482325114555541492282064621526855756620164892739323385311597784598".into(), + "11146275353887579825261271116625263398084632057838305651534583557054398181584".into(), + "1266798071261939757558459653520435752781168817699931045808536221140555755937".into(), + "11242956579746912399423902926753108012060763899073974272480062541403588053076".into(), + "14588608018127887593991920960165305144719061971763947578582110087376128309227".into(), + "5393054051309402705329828859286427425185515439646193479342950065083063200985".into(), + "18307950351265411595229078848795494559405008993794506472277342386767679169011".into(), + "743926999062879465491123202608691059211244131659206478586545917792531513741".into(), + "11450449580887323007201214040662031298427088361641716342102428346626191862079".into(), + "8918057399138776974775397421552812822591030597414174436378830421562569121660".into(), + "14007657148016840281780221954150751472316590653604019267269105557294795817454".into(), + "8897431240575139049076571283304115052162507033543918057893215164198174868677".into(), + "17409416377040437657574990618884772475255396933561856228823381810258648281132".into(), + "16403607054571283311114350132750182990388190855449188101005849970173367695642".into(), + "1508877263063909922036861471040526805004287450733006660015588614987220564178".into(), + "2405929790914208591765690668430163058227989428739435027301989746453297508008".into(), + "18415257845847977290620817418058909465150962144437291827522053905306094819516".into(), + "20783625745588167215135121578855692267545194811392233146323762172638833678083".into(), + "12583735518159929222168953087285648797218797099708802919538903595273472024870".into(), + "15030113532753402263141105846533650021837620483920708638977418255212037916690".into(), + "2044646201923266135898414814098969242685760821863709223961924291978394037140".into(), + "9562505824238493762747890608580676752653265912034931437600706699047478680911".into(), + "8576622443481230638247834729157921300954369512398151853927361194153863723577".into(), + "13502467573105057302451195240442043465889307076796874707692282952243564124313".into(), + "8659508751425384756294968991737225118186991099981336883558347987313410663071".into(), + "992836427155144083079734949008912409782777055146977880211513548288277680936".into(), + "14478698156814197173486553402886358323315401752422792440407576984439553283243".into(), + "20204662700070586254342470347097589341087597032672938910846995633724257876133".into(), + "1812172830570486768023198219726218583158255123460220955281515074543933097836".into(), + "17992769749806193849727573532001735714323064233429371707200828907106131204931".into(), + "11275304845104653478990825724663443324387792098049434796341439810037846570595".into(), + "11105199526118828533245892738176913532701964584183761946009726630431598631704".into(), + "11393903979182757061241357325556769026145343989318381225525091986823175945546".into(), + "14383047179623652129287498422471395756755287245693820194038638652931729043624".into(), + "20453515719997662355844823439261666280107449255435062001366805179715649416520".into(), + "16342434030539357589118587629035849207895930777526269265833103191510088418049".into(), + "18401618409669783606190734020469430253158741609682792710560527808266513994586".into(), + "16076963378432439787360187815002671229026877629938245603309362816936414414606".into(), + "11529521787481754741418171353792184686967806427253956770441456297160607170941".into(), + "12602344479327416239042579290059415968931395895265775848606068378636646131638".into(), + "7874996576706323538862472352643073872193797790899948188898578232811415974069".into(), + "4845431324532575010258701269749667428252187973009639700566740844013627970107".into(), + "14802018959157569593654049670448062053140035551074745737518084801052997376690".into(), + "1438261891736096301511736944301645044858355530036359764344568183983865122024".into(), + "9702483734495711444970258278431697259819553205295174290488141405714571120392".into(), + "745629299152271704331537263378566091569916279264951187804590327433939503876".into(), + "13647050656573274198980106604763314305257288237932552445752822394869548724758".into(), + "16745795700152546519998532820179128558740146524138261377398336069302091938316".into(), + "6511309643923082597828923519243281993300211181014429981003702021394673484309".into(), + "3158509665257424804238893020268668038794684527943417360064521274731483365497".into(), + "10106711021741632147292275661510721445094446856823798214773373487329113893162".into(), + "19539597394622940270506324036474158048415508212083560312665281444636039358507".into(), + "9075752841443178653052367723489194637305474480630925657941805297419704103107".into(), + "10984322959292625191378844945459317193701661510910417025411449026843907121641".into(), + "17927313671844804578171839390174936993766288170716470877087434657482712787254".into(), + "18872143162724264635018175304456023676931184143071101308504884978404622981510".into(), + "20515902079806118899516548385287144085761546545950360604606371922174253859996".into(), + "909520465300399063286844098900959690198495601569624327409635184475032912958".into(), + "20206446745684213293565932220432184473350758436589542174203682990821527627997".into(), + "18202239467972118142408259480727588505962487821013411792329301914239859726481".into(), + "7983919062477498484336722979137049758716865253938030483468589967847498836327".into(), + "8325614337212122905531318376855865321332189692321777599447159536409656516919".into(), + "8597163804726192574292212854626326634508890118939606311848139014738137868745".into(), + "18500944130179290196885189330659070775829081695220678669482033354888226989090".into(), + "7688562031300833340110739922365866810835026837265955101002957176624104705373".into(), + "19947182975808967149585827952170169153114833389807259043148589247054925861923".into(), + "21517453591126354047928769118216728006629501856431780724965733122370735013602".into(), + "19724728051616532722905708549906983762154956430000563385110162693578183573835".into(), + "12901041676897371025108379309395257314967524835524863357724200518477312324601".into(), + "3552976975075178791886580855175573474759766023206417049453393296909032090255".into(), + "19723727023156957738383690313848921568395238875690539785344406327140649751346".into(), + "8448482122526118108012095036728031952881057181233719818597059954973230302598".into(), + "19506723656402269183886569439436103718740054288352928129647596321989076586438".into(), + "14170198227468034198358292107569967739463050831964193487443803219358410981401".into(), + "2398884471809169728404698398912564280110318964918284340553363751754870608551".into(), + "20316276154960569208855163430299959763482642890911503054443013025286320052982".into(), + "16819346170274370820168489515108636849055213099894018965040750518138293949687".into(), + "14032485862625099598391073907784424901699264655891124043486362510876557553033".into(), + "3741512032866709198636578814340035900693212341083373891378285003365821212725".into(), + "12320998007117681710156579111620440763582440787871319835484669927670822914066".into(), + "18497785580008161471127317447391820533755559610828409772165480020067085485597".into(), + "4405750113669275276872999085838121306688869906871946649397890860602053182664".into(), + "9830919490764773329556696442544208406733862221424439787824742578837854955405".into(), + "11656214840454358128291973754562337687978444406755233034850997851692380129929".into(), + "16862314752433119612255838474361565358197526603416756657147704910780503202458".into(), + "13994730783691699927566696508271362101008053907735972055086535421903219406757".into(), + "1854398114607336170485701571991055833572643201188015258021784624866312103211".into(), + "20069390379729391520654648717585246938403361904664791681209786618785720069963".into(), + "19778444821535136400266636630030874919578160245360989612631440824622767847607".into(), + "21076683052368294199461719450518198858527944040936974409849054794891242446768".into(), + "20411961758852468040955165630870750226459296665790953169982230333559560296737".into(), + "7502887992944485334895259919103098713153732704425367381733235650810817022601".into(), + "8197671201190749689041918593081864258260291777801687985230863071067546597951".into(), + "570474345973383626887611250305231463005632817815912456645561438458026430574".into(), + "16187881277000098252637261715701961539204559080071010626416256497925074197451".into(), + "11845760454453819227975628605163841650771493308562299599588237256809780809041".into(), + "12496905116588812906791602937225630677262600876144492834414412499317258049345".into(), + "4202178453449927253051431595142836159883278283876420191896732301089503127871".into(), + "15280239602029688372093391224297422856227971384132785248484775299588777602819".into(), + "7675624637796659741242134119256859966414372807758392029058215748145801821134".into(), + "1558712828795805480069595730092542425736929845400776453853052297930907571419".into(), + "8635050664405255712941235442916789795311088451605341529181390886210645594786".into(), + "20012080883240845774134523050094772793782396855825781678689671532280266223297".into(), + "5332665745663855702669515489737590813722712486482340264533181716182162216492".into(), + "4485713577378561256572703202440374134284884194291407499970429041545518915674".into(), + "11594166683437400908552833183389222213106302657616725421515671119008232951402".into(), + "12334131761060442183744872783660449330900007808735685674075245978195977188916".into(), + "6469473140282499287719023824549928080797880736382216700350093546124693991876".into(), + "9636916035199580181413428196442500662713954920605937945590797926723030993514".into(), + "16259404193045476674338459391599179316200568243819582010383092764410171324132".into(), + "20780929014533211529148194875929589500835169316324843103846798105106103748435".into(), + "19665958036420604957261288306620614439114405875604211377014353932489316681936".into(), + "15722491897648578237100473323139351762887860215830601049261437931157637153597".into(), + "20474814639824406443111360000192533367769367587690127965461453022108167666929".into(), + "15306300298273142257702357120212730128497075786589008381550108606914393296015".into(), + "19116371381269652319147699604019975103087973589614811479290794650138683901396".into(), + ], + ]; + + let m_str: Vec>> = vec![ + vec![ + vec![ + "2910766817845651019878574839501801340070030115151021261302834310722729507541" + .into(), + "19727366863391167538122140361473584127147630672623100827934084310230022599144" + .into(), + ], + vec![ + "5776684794125549462448597414050232243778680302179439492664047328281728356345" + .into(), + "8348174920934122550483593999453880006756108121341067172388445916328941978568" + .into(), + ], + ], + vec![ + vec![ + "7511745149465107256748700652201246547602992235352608707588321460060273774987" + .into(), + "10370080108974718697676803824769673834027675643658433702224577712625900127200" + .into(), + "19705173408229649878903981084052839426532978878058043055305024233888854471533" + .into(), + ], + vec![ + "18732019378264290557468133440468564866454307626475683536618613112504878618481" + .into(), + "20870176810702568768751421378473869562658540583882454726129544628203806653987" + .into(), + "7266061498423634438633389053804536045105766754026813321943009179476902321146" + .into(), + ], + vec![ + "9131299761947733513298312097611845208338517739621853568979632113419485819303" + .into(), + "10595341252162738537912664445405114076324478519622938027420701542910180337937" + .into(), + "11597556804922396090267472882856054602429588299176362916247939723151043581408" + .into(), + ], + ], + vec![ + vec![ + "16023668707004248971294664614290028914393192768609916554276071736843535714477" + .into(), + "17849615858846139011678879517964683507928512741474025695659909954675835121177" + .into(), + "1013663139540921998616312712475594638459213772728467613870351821911056489570" + .into(), + "13211800058103802189838759488224684841774731021206389709687693993627918500545" + .into(), + ], + vec![ + "19204974983793400699898444372535256207646557857575315905278218870961389967884" + .into(), + "3722304780857845144568029505892077496425786544014166938942516810831732569870" + .into(), + "11920634922168932145084219049241528148129057802067880076377897257847125830511" + .into(), + "6085682566123812000257211683010755099394491689511511633947011263229442977967" + .into(), + ], + vec![ + "14672613178263529785795301930884172260797190868602674472542654261498546023746" + .into(), + "20850178060552184587113773087797340350525370429749200838012809627359404457643" + .into(), + "7082289538076771741936674361200789891432311337766695368327626572220036527624" + .into(), + "1787876543469562003404632310460227730887431311758627706450615128255538398187" + .into(), + ], + vec![ + "21407770160218607278833379114951608489910182969042472165261557405353704846967" + .into(), + "16058955581309173858487265533260133430557379878452348481750737813742488209262" + .into(), + "593311177550138061601452020934455734040559402531605836278498327468203888086" + .into(), + "341662423637860635938968460722645910313598807845686354625820505885069260074" + .into(), + ], + ], + vec![ + vec![ + "16789463359527776692258765063233607350971630674230623383979223533600140787105" + .into(), + "17179611066821656668705197789232102741366879862607190942874777813024566441829" + .into(), + "18653277315487164762584377009009109585010878033606596417396490909822722930739" + .into(), + "7373070639853668650581790286343199505413793790160702463077019294817051722180" + .into(), + "4823864393442908763804841692709014014130031798360007432734996408628916373879" + .into(), + ], + vec![ + "19196309854577132760746782449135315310664418272926255500908899397538686486585" + .into(), + "18123132816088485879885148351452823314623055244145916622592591084094232513914" + .into(), + "18436594886553181913092702411547018228276047601279727265790147051821171174455" + .into(), + "15167500404313194506503404655898040457721633218143681920692711693000769735187" + .into(), + "9437986152015460505719924283993842205604222075968464846270136901243896809793" + .into(), + ], + vec![ + "21445376105821232747280055223032050399373725161014449207033808524504027971613" + .into(), + "49684738714301073369749035791061182456037935161360748355432247732088942674".into(), + "9826409059947591908303145327284336313371973037536805760095514429930589897515" + .into(), + "8494798325496773219358794086647759478982958403252584257436898618394561204124" + .into(), + "21251937175072447337747316555423152807036003235223125066270735279039060889959" + .into(), + ], + vec![ + "5539100337780919206842837176908516952801756637410959104376645017856664270896" + .into(), + "6297628909516159190915174165284309160976659474973668336571577778869958189934" + .into(), + "12792263637464508665199868777503118105486490400267592501708855807938962470650" + .into(), + "17254685306085558791725544672172906900581495686070720065168939143671412445514" + .into(), + "3590396502942934679818900672232030233017710909687947858184099000783280809247" + .into(), + ], + vec![ + "19055249881366445073616526879263250763682650596233071589085239500077496415637" + .into(), + "7367697936402141224946246030743627391716576575953707640061577218995381577033" + .into(), + "1322791522030759131093883057746095061798181102708855007233180025036972924046" + .into(), + "20456741074925985565499300081580917471340328842103779922028754640077047587707" + .into(), + "9059147312071680695674575245237100802111605600478121517359780850134328696420" + .into(), + ], + ], + vec![ + vec![ + "8266021233794274332054729525918686051968756165685671155584565440479247355160" + .into(), + "7947823415909040438587565055355894256799314737783432792935458921778371169026" + .into(), + "16508811191852041977017821887204137955816331040385276110261643892701458724933" + .into(), + "1804800467126006102677564831888710635194614232739335985819349312754063580223" + .into(), + "11189892034806587650995829160516587240879881493093022855087765921356611070470" + .into(), + "20567450145123179140729389574352706949280207113956641415022972885523439610844" + .into(), + ], + vec![ + "4666756311257455192796774305229624459258864488677689058174087310651786875914" + .into(), + "11389253665835451896363091846189307652796786468610595637047377864063404843117" + .into(), + "18793736599347263150867965517898541872137378991464725717839931503944801692688" + .into(), + "4206344588923325482680116848820594823631536459347642329098796888497153867720" + .into(), + "1739462481670645248707834504605096139894257554120906850613041004917967456145" + .into(), + "18514227342636266640333254638454588508118462110178719555586534011641424431745" + .into(), + ], + vec![ + "17887039315911403193186866703775654467672391491657957999455462537283842145802" + .into(), + "2824959020572825365047639014537190268717891749361604043531643698340708119767" + .into(), + "12521547103713919592301476538318318223836047611311454785951907894055964264287" + .into(), + "8658146183671258251984364885894342376430874614261222570603159082682815800788" + .into(), + "154390145585284450772861151318029820117470958184878116158462181541183085587" + .into(), + "7593705166056392393963956710828665339496927193740869686529339432486182720653" + .into(), + ], + vec![ + "5529559239163081088908568555890212324771345012509269613465629182165427812002" + .into(), + "3729910453162885538930719732708124491456460687048972152311428493400220125686" + .into(), + "11942815243552870715777415109008273807076911177089425348095503288499102855779" + .into(), + "498938524453430895689241565973888863905147713935369405079343247530256066618" + .into(), + "3976257517234324421403708035200810671331954932478384823208414346189926720724" + .into(), + "723540703523219510043977323240437576248315561543814629392162302024056718473" + .into(), + ], + vec![ + "13306548824219676333032339487546407241767961556934015003605485324283250885682" + .into(), + "7970147269291664639740298762956131361316495463191268382513594527221399186752" + .into(), + "20633313939958767604804835838065337107615699351647541991788258289962727735454" + .into(), + "17162090859520817529294904484646695645841022315617926715432606252643123848792" + .into(), + "9181379842957190051440498041153333325098774266789773971685141362947015398641" + .into(), + "7051606617662816798224904133351061549832959857069896192072217769241273559278" + .into(), + ], + vec![ + "16619522548478824222688310091434959542211899852679631815023615875678448806029" + .into(), + "14965311177811968100298579672135357167599499478246106482433786066289128683961" + .into(), + "9792733250919070275775594069208673385381167169182805600474820364274865306108" + .into(), + "2069253833779081039049908513863485270550301879399727430830923273191877809560" + .into(), + "15847298987712771667136245955631872888473964330474501593909263901393348546986" + .into(), + "12244443532166430060291409356011430759892629145539185535677568234713942157668" + .into(), + ], + ], + vec![ + vec![ + "19332164824128329382868318451458022991369413618825711961282217322674570624669" + .into(), + "12346323761995603285640868741615937712088302657627126374070962894016296466118" + .into(), + "3913895681115272361294397190916803190924061797587910478563401817340941991811" + .into(), + "7048322889096718105055545382948709082135086733564574465991576956878202831861" + .into(), + "10375086910057323893637057154182902576957472442368661576421122036461645295833" + .into(), + "12765622911241487148932810040772504127756393086809438933166282251044289864727" + .into(), + "266900212758702307861826326591090138389415348463003233900705815890364224151" + .into(), + ], + vec![ + "14435131616556129905356866638030823183270286404767286105643513738132789033353" + .into(), + "5780976801287540146775934937953368730928109502001687434229528186520268917700" + .into(), + "1618320442446662026869390273942730786145909339107736579759397243640902802126" + .into(), + "3818399583522206096165108192531271582827953520684743806492664825009577810261" + .into(), + "11764506724346386316602508039052965575734225646587104133777798242528580374987" + .into(), + "2414215974836165993714858157462355581258152126063378817495129367240311967136" + .into(), + "17609437036230923129211608175600293197801044251801590649435913902851695334081" + .into(), + ], + vec![ + "363438080029711424794236047863047716381155074181485245036621530063262917196" + .into(), + "535766679023716739184211613469394818313893958493710642899297971974381051070" + .into(), + "5305068908469731303772738758164870877638068032868328180355958394150421214337" + .into(), + "10807632568240507366657354568432178961148417327580695024415275247652313539292" + .into(), + "15964415873358391713354948903242729080763777490509563223190335273158191600135" + .into(), + "20700362719972015883260687302741075186857660623182772413609788566925949033885" + .into(), + "10135127975676256977820296631533839366076919827597067890970660746228807376456" + .into(), + ], + vec![ + "4251490167543116819728642817282216847143714366441358372252125244838181656331" + .into(), + "7745587495915033527847242564710473705100826890903278244320948416581724663023" + .into(), + "11741113129223221800185946819924457344647035336264986754437921049066977440806" + .into(), + "11630296782890656599545188109639399768829653360050213193782325240600583381364" + .into(), + "16861140446185941149398487176581839232380972247302922484807333229513905651035" + .into(), + "365879246117123675211400356410703684399715291171114630107795112994207447819" + .into(), + "21725607857580053522363567649763546934441685061337033780528788383243719579033" + .into(), + ], + vec![ + "9222866548596464928765000608129177609426964853736257576074550520759533736918" + .into(), + "10261578281201197531384003420612639018011405529775212563256392340336951230146" + .into(), + "15644037447921591571869862919382888810859308861783088910843592577202362807673" + .into(), + "12752004188139535619565478547449108772137477456363099481095747591698702436636" + .into(), + "4205805109630387448825516813913983509046636797101589615147198457314360427718" + .into(), + "21047095155106717901091873146599497621258071512562421967648909471775919992713" + .into(), + "15624165295872926124160584750951090817255240214488120310950503163805737026315" + .into(), + ], + vec![ + "15064589937731741958666763896598138037875460434244947486199623542160035749721" + .into(), + "1801577872277160959016940766173040841160105238799805406938450020949902989173" + .into(), + "2896766420608048344829901127120623317655260981420052771341833288256800199953" + .into(), + "12828791469509204618898135640019714232831708508424682785876476343251730674999" + .into(), + "21363471986981372923191391880511344708743312828234098289107697080824665183315" + .into(), + "21372706354350795416381912271616633829725494570576895047490974943034914894898" + .into(), + "16006531510217730955981102005088687858079561573088629102219485906666961331083" + .into(), + ], + vec![ + "2389357602244845938251345005183369360523566673990464798041306722747500447645" + .into(), + "15275955107196234672088664710679934029171843237458844492987233368659104714648" + .into(), + "8038797517535218686870517662905230585331773059774130312418943649247287196930" + .into(), + "17923922393436914864421862212181654800719733137689602673604754147078808030201" + .into(), + "12890519745320143484176500044628647247549456778462652469313611980363507314914" + .into(), + "8058516556024397257577081553178859094042894928866720408652077334516681924252" + .into(), + "768425396034382182896247252731538808045254601036758108993106260984310129743" + .into(), + ], + ], + vec![ + vec![ + "12051363189633051999486642007657476767332174247874678146882148540363198906151" + .into(), + "6387692555402871022209406699166470377527846400909826148301704257996818597444" + .into(), + "5501161701967897191598344153113501150221327945211106479845703139297020305204" + .into(), + "11704372055359680530622226011526065512090721245437046184430227296826364812961" + .into(), + "1448611482943320179763394986273491989368427112997509352702795612841455555221" + .into(), + "11429145481524962708631235759094055797723504985787912972575745356597208940857" + .into(), + "18021858528471759023192195347788820214752298716891162685115069036283008604659" + .into(), + "19817577944622399780828745167469547332167999743980557486183403063955748437619" + .into(), + ], + vec![ + "16868980302925985719076889965831700407328155411673408077166038059874616424216" + .into(), + "14717432944340806781505761211058502775325970511884444497202848327581753493322" + .into(), + "6273484270523289845253546319956998489830555038697388950038256377785540828355" + .into(), + "7726043103954429233325852791166106732104332590864071922310309250010129731951" + .into(), + "21052353119157611359715869265647287129868507410601603360127523286602350622783" + .into(), + "14881796557136180514390287939887071460258251160875710427576954128871507002642" + .into(), + "16341327439981153879863707938117355436152690262312411284193970279829974799334" + .into(), + "10737675906107372302108775622264379258926415910493665638388971468924879578019" + .into(), + ], + vec![ + "17652699767629314433191915267767147860052614073432922215674211498672835339113" + .into(), + "7457854400138129895665591719907473144796504905294990100367501377050420942800" + .into(), + "2136850802972823585140870808569264373787409642804109426616292140046700710743" + .into(), + "14029467347298896610468190615212519453678316548442709087191045978401072380889" + .into(), + "17927699952921266007590534383984238136710494507499176330493504416180410161683" + .into(), + "1404719213830610030709583332543456268094679432456284386108188509031502237811" + .into(), + "15774757292079018355173698870903422490868220545526384876021336136892926326596" + .into(), + "13992040374687149195439840459922227749294794072303579532004750946306028893274" + .into(), + ], + vec![ + "19895094843870397064274579657905921299619388074084417486420154568847155746891" + .into(), + "943833985612967248618844364501030453998731991825395875139617731659343743483" + .into(), + "18334641092245356682448009823797080853859186519922476229272838591594967878678" + .into(), + "12440287044655505483131716236615633401781045711053210640202766768864619378050" + .into(), + "19130942564098572936370308509908873069169152245172660555660369853346605570826" + .into(), + "13687979327148217614616687417475244897906227789285703940171633508277844471062" + .into(), + "16887921327479880141959363366262254722342925451159884082370074726344024008329" + .into(), + "20378003125024698406589040864014894045124234695859352480989552885205935609512" + .into(), + ], + vec![ + "9961553412530901953022991497331082655746860319830309417179972582392489275965" + .into(), + "17755268665220780466271147660314410613992814315871705414495724015443459797439" + .into(), + "15394131279964876131165951719955566821453162041574233072088124095626652523043" + .into(), + "12668230348320365182085867728169435383987570924921845106243310905832768752125" + .into(), + "14046812111383844816383347755263287603387502282980410255379630204396960343368" + .into(), + "11590093969266595252327261214735156204516524792938909229175092594303424141199" + .into(), + "4623517074925959322927421514289132524032863498392441375476410779446526502799" + .into(), + "11550389531965919926150256242174358326491059727918559332939872696684299343135" + .into(), + ], + vec![ + "408487396317981846281976563618407581852133413686169882346565860317912856432" + .into(), + "10717757571561029382519744040791773994731123262749372629687813122941078154016" + .into(), + "21323787615496251932181222397986048515693661833099659753170924658480548866921" + .into(), + "20780799310067873093555276926357624414275975377319941015818682052081980020892" + .into(), + "9948385944800296129032348634683354181546876394979291412116493575442898426065" + .into(), + "4957033413111065858035065225611730571499258914257595411830870977545212164095" + .into(), + "5227254936689728148737265263965107718869714128941995977191096572191110991079" + .into(), + "3582814872786080867997255427740166393615552773099677831398251586195329933975" + .into(), + ], + vec![ + "2136737803483410555580163900871515004623198990079556379647848364282254542316" + .into(), + "2965752098571712086281180512370022839542603960309127077035724860894697782076" + .into(), + "1478525086510042909660572998242949118476342047444968703549274608283885678547" + .into(), + "3563375996604290844805064443647611841824012587505923250907062088840679700555" + .into(), + "15461452581843517997080348781604020486994675070532901120353124746087231692278" + .into(), + "20472517020063295821544268171575414161230806406668271887185150097779785573889" + .into(), + "21058001005918321995459971112208002381460494177332965873048074199074929946172" + .into(), + "15805746645980285645504697043988763170971539673993759868487715403982423015009" + .into(), + ], + vec![ + "7141240965656437676130015766799708612940092856280620325870466265817008351948" + .into(), + "21418010338098024788434337801477243267248314524079104488811186206038748626642" + .into(), + "20272108634229595317682817969506273496034097230124371921628691470754475805838" + .into(), + "16734095147399743907618148751687506877774623133599770145304816136018239803101" + .into(), + "8439324632051181834455499457268557602816180314723268640869118054114888151316" + .into(), + "4953900961796661020464968131122569974581908893169105485631905994366550928492" + .into(), + "18071625983692455679240094911529791119099077429122520426399552756115503123111" + .into(), + "19638917592063029281156873227053827678889868373299664608974791764751784473040" + .into(), + ], + ], + vec![ + vec![ + "708458300293891745856425423607721463509413916954480913172999113933455141974" + .into(), + "14271228280974236486906321420750465147409060481575418066139408902283524749997" + .into(), + "15852878306984329426654933335929774834335684656381336212668681628835945610740" + .into(), + "14650063533814858868677752931082459040894187001723054833238582599403791885108" + .into(), + "5582010871038992135003913294240928881356211983701117708338786934614118892655" + .into(), + "17817167707934144056061336113828482446323869140602919022203233163412357573520" + .into(), + "16618894908063983272770489218670262360190849213687934219652137459014587794085" + .into(), + "10883405878649359800090160909097238327402403049670067541357916315880123123342" + .into(), + "7439184039942350631846254109167666628442833987137988596039526179738154790587" + .into(), + ], + vec![ + "2727663760525187222746025175304386977552466570311228286110141668880678011929" + .into(), + "16992375884417886634716738306539629570444547136030480542879886913528563834233" + .into(), + "4178586893949624406750122665277033849762243490544460031634329370298105635905" + .into(), + "2517914797385699886738929430037355069462619900197972886482360691236776726214" + .into(), + "20164173810534657634631187494276970100735049909727379228976555863615716408280" + .into(), + "19970958827248077001061220127605534603528515080207197493660642269195127427214" + .into(), + "15606275977308968307194602612931727810866183872589808138812916593200446820753" + .into(), + "12261436001550634140750381230737452634746867040398895669545077774504957433511" + .into(), + "10405309809257831434323731445544896504541938387524726028487604098725193737428" + .into(), + ], + vec![ + "13408856444092113657034337770571899796129642125690066226794939383190876435468" + .into(), + "19768080898957882918527124226120459667739640387901357739011662191034806046251" + .into(), + "16749889646056241484852997428132695501278739424507088920371060969471495213919" + .into(), + "12331609790192161246735870679870317366088443875784324655482358218146673901073" + .into(), + "15769331739277556832196167201116801527901089923090632364403958141614820528626" + .into(), + "5227172275505968397128736045169568430462701766148126842874241545343535393924" + .into(), + "919073378344729780131814412541912290691661039815032069498359347682919854836" + .into(), + "17858725475505870077023114050620337312678855554361132257763133392017321111169" + .into(), + "21805188450184460363143840112266872832328782034569970452376470141743078343745" + .into(), + ], + vec![ + "15808413311863154368918155104905222670782553225279887458053980771135357021692" + .into(), + "12828907214414139667587331812274388831051429093098655261887619166452245292431" + .into(), + "19323880880917307340820066456419195877039970908109908221992925424585030574269" + .into(), + "17591732412986269470826282099678922890996647592922237928486497997144096433314" + .into(), + "5282593184575641056912422403901924986019740793240905758215569065763629999318" + .into(), + "16013130707598525718519250412251656096494468043256226360413191733653074896117" + .into(), + "928381583587170989315021718439506896903185927814675820160976165627097308915" + .into(), + "13354336789663524324458402003354905134416094005220899335023797754517805691310" + .into(), + "8780135673134081873589118311874067764073719549433574820315100541871522642766" + .into(), + ], + vec![ + "3334957744389892864165113989538814646945861179021194859030934481494560681812" + .into(), + "10553413566358881045095498839713459314577909144176577153981801574128014927353" + .into(), + "18894321506279909207228932263261226433242541255661384643559047811974513999438" + .into(), + "20211894014628303327332299342564779073614790317614402383971270594430055013904" + .into(), + "16723480621932556506775906903415088312771104391224076734252099577243237899106" + .into(), + "1131872547334579236404174618548801749854242069301712398106619948805304881636" + .into(), + "17386814048141719093058723520379257085987299288710382497237609774141718421404" + .into(), + "13729980537487612221640320393867198844745491357830417754869369043292518007370" + .into(), + "15860780436383591737179656321807464721751913977397035980422407138400867838633" + .into(), + ], + vec![ + "14708550460111247278740231297332510059116901767161326454481923990389610737973" + .into(), + "3132820559166321299152015048428879769905404947939291493327190426785911502819" + .into(), + "8658132367999084824971296219169212568783540935524918908332001856872807119287" + .into(), + "21064783047501777742084787259676320053480170916619513986794406566953069418035" + .into(), + "20731000104011695148048713576219525164619502119638555785381543866326561323".into(), + "17189725817866212967650950297463469529475851286172280116066228706121595462088" + .into(), + "3310440878606659516028312898499559492876015493892608849966645073367377278233" + .into(), + "18463918215326370595980949760897480127622730018343709491036454088497976892863" + .into(), + "10894192430593140913557164014343360386192963621862346779515699758352916852228" + .into(), + ], + vec![ + "5060610877870389107953459328006060153180283860738879092399406248484265273634" + .into(), + "9068988823145592214189961315730261367007076042069390630024839612151270430414" + .into(), + "13160707893890865447331361630522644819624543031829773191665491273833460019183" + .into(), + "13920568292534026180186486064598876780779571940988254327823480971820885713801" + .into(), + "3894011501178134026216736522445829906312115650019712122802932677318433032635" + .into(), + "17895318821130376385979570244603067634449453259842805202694945793852667231847" + .into(), + "9777993060458301797155055013115849176281006051494461044565335406558308324220" + .into(), + "16521293541516305251718414192107787058980727971856888501176820100904791554730" + .into(), + "7744063601405355255689420547832904761861257642931934580021876189691881462544" + .into(), + ], + vec![ + "5444730929053688962452159157646022068806222098484627080046464163159451208522" + .into(), + "1524118152994294864739915388438939180298324297960159419600850033701763764640" + .into(), + "1334622237342346242862023763160346671504959163544406543315614662442562816653" + .into(), + "16126317914306849967682996412350336172782726693375105190424151365140854833923" + .into(), + "6345975085253358297751050638846919833013142450462810543971050115910612860460" + .into(), + "2703875280053263252177031410407166981522153304496807669518295313468095058674" + .into(), + "20550626512184448884716175825490086259235894802178999642552696391947509065676" + .into(), + "15013718986700828670892638677446258841869291160144196138236407826511808592486" + .into(), + "4682264015512203762723381542642871160915706748420642731100634327658667608042" + .into(), + ], + vec![ + "12834108073603507925748862283503586970613250684810871463629807392488566121352" + .into(), + "8422606792378744850363509404165092879785007388646473871019846954536829739979" + .into(), + "9339209090550177650528715604504958143078492516052997365409534971861874881780" + .into(), + "9141831918422847136631159987994781722269889810731887947045878986971886716767" + .into(), + "18329180549061748373684938917948729366786279119056979983310618862430068636631" + .into(), + "2009551904565170718789964252583363785971078331314490170341991643087565227885" + .into(), + "3859729780601667888281187160881197567257456581829833310753128034179061564519" + .into(), + "8535335342372994336873304745903510543599314397287086554558824692658347277251" + .into(), + "14148514289641991520153975838000398174635263164584825009402034843810351225518" + .into(), + ], + ], + vec![ + vec![ + "5029285279710800539227619495938136407778783814400587102957398897867261120664" + .into(), + "21661833903534656620291231766157513264428291380933208423519374035927473262119" + .into(), + "21013170147855726227668315492699186959893088673047129690411646575996043835024" + .into(), + "15893628062504267735591398483514002406192781085288489283447316241330749546879" + .into(), + "9860639032243003377544947110034203265885715041305770375052648470285182020229" + .into(), + "10431760628292478929366440566994655480900443273305000842144090945543100651218" + .into(), + "4662341343242273661833461144031815716144681076466659112993661636426666579986" + .into(), + "6674279191498784183427663914511569570797862586816649467168170855788360268943" + .into(), + "16895097041920841073767278653214275321407577186751547609698446652984399225877" + .into(), + "8168606076413192332279322347673356872630772122089948509553934257426773045038" + .into(), + ], + vec![ + "12091567755121016869657080116466607855522522017768906776539212195551888602502" + .into(), + "4684576201081771194613696765517034834984066296253124029929753160055156611363" + .into(), + "16693488266039456124835102259365515976900969074532557489095946797080826193662" + .into(), + "7638443036775258881709317582832080783911189229963788890221615286494482929025" + .into(), + "10111436214822932149781668218956845833675824936886829015449750181332010388640" + .into(), + "896682691957564465177669890535917423987915406885797833670239687119295318467" + .into(), + "12612639059115228106858238115822505521432423470330120640591982767272085175034" + .into(), + "1851711744209473345586117150836616408053748535684022739058625441026889320297" + .into(), + "14132260688735080257390420980422269734275443926576061985351678038992087770902" + .into(), + "487493866037948515547037886552479973316400139387425953088274857424154262588" + .into(), + ], + vec![ + "7712516772901240105339429973116360243232161870164307482409826131312962380842" + .into(), + "20295556720945067049585659016570679551265845058805648954004989969704769135170" + .into(), + "378208946912325140295069471345064814132951473534378635003955801655986417900" + .into(), + "15111601008893945567629460471315838423301021468457758533702272669431620017222" + .into(), + "1503682435556321218669089857094247703956565058167121192612334331910088441071" + .into(), + "13084874799693933186811120569396911285611047490876409383659779579088985591229" + .into(), + "17464483161247836988344436558341194021876261750085348252730901647076441211862" + .into(), + "6628743087463083391707355927377412170189936607932592258517748766250528223430" + .into(), + "15153763588458144568353947674975114179172744555450771328418442212716084083525" + .into(), + "11217853102739260248713425002157925483291370125178251466195670948291389406199" + .into(), + ], + vec![ + "11275485266433075885440484136400353724892671196084163231314370685019444807048" + .into(), + "20167106354875398113371399754994549089359568833089630824992752829251678891797" + .into(), + "14151330869211746069130604993916224881047448810615413435448712767752320095045" + .into(), + "17260356243574396880210370581740651566334589568095587416844511054569255137183" + .into(), + "12436078462666286197074526218535647721230687376129721353230123441759960021666" + .into(), + "12001627458343654011606323250787666795709808266974343548842843520227918922255" + .into(), + "15944850302839498288636342399223012131590208876255723227505947857641523034493" + .into(), + "8444103924869263585176528654612076203716402818569041992813095331662367021655" + .into(), + "13015682914180762871967848617514355587762125694235380084430680565032083402270" + .into(), + "16200183380426364054409550129683752323493215428097334915015688753327665325485" + .into(), + ], + vec![ + "13717643109958965551675619584464549580820722892266661529182798599670194908199" + .into(), + "19801725181447377274232761944437523251067599053402428862557912155522673980500" + .into(), + "8260354277364856843022982286494019620277496829494935775254726797533957063267" + .into(), + "124621144162335766862972192337737579448571172779117809776129849377329817478" + .into(), + "16488884047551411705397223604196364132975353217876182634038895586664127388979" + .into(), + "17336432076451490238716890901095007360946878388179175784603587179384718443321" + .into(), + "1210338460555723584699132156502555539583432069430631008706741082485009017102" + .into(), + "5933432012048351362807861976737945204535374770355507745694008880123055490802" + .into(), + "5127952499969178010015035020598142881788437616516517827214405489972695632240" + .into(), + "21100924218139544842807404598627913291698574448527131003096325470925085906016" + .into(), + ], + vec![ + "7683521602764604419863026286445694988900727173175219514555132623764360793654" + .into(), + "20928394065137007852706990901925870323120588543710137320004640014111073449000" + .into(), + "21375535333469484792161302750563386607223088895810564711097025913956371171769" + .into(), + "8663517227154706072248636076587789834246541965140682871530851124960776424787" + .into(), + "9182938389356039217318590654716613493414550996824701664670650439783557720226" + .into(), + "8327338979442122743919832154397496089418582414082199116629974300650113777515" + .into(), + "2474727241701323049333019668054716886184808783449917153147248751503852312804" + .into(), + "8543922237501430855864877057711792269479294116675004771113148647309219620030" + .into(), + "7863611214303285947093025404346084345102544167615769255495752297507346719791" + .into(), + "1448902069752048144992778676670381235906144579949631101518897035253311063307" + .into(), + ], + vec![ + "19501657783346989621892787238946890715709847672294934508902622542828235185048" + .into(), + "17076525025777667838921778388186176564387475624769926249793144074465528465933" + .into(), + "2381176586418291387279201678056498732033435079507661703992537801751492053086" + .into(), + "20723508866659831749949206314442193102431573526415976696387848305764994281574" + .into(), + "17461795780729443663350296040956479984433953861306521086706732257263430387445" + .into(), + "14849025218838139413138931958408289986915143240245452275066866730847749323920" + .into(), + "21207204042106390965753782189145584243052148578812105334769740484186308017901" + .into(), + "3105302592226642624386332562899903659948819667537402316192380465808886843623" + .into(), + "8765266846991616382097124552983206033439769882065573909634090515268812396114" + .into(), + "9950016446092650730639179912416912603745831292536616469358668786853463197224" + .into(), + ], + vec![ + "11739731747351277092817771330729393674312591071236310446088293450266807414263" + .into(), + "5424991773995591044103668717299468589013142114099340604018933512575789323446" + .into(), + "14582885509715812510585748465607279869582209618804039923778041514988867577359" + .into(), + "12468934763690970929325823037406509081405444759649987929912706732364016057892" + .into(), + "2792793293657306144108993077959195845478902430027171873963281969527327256602" + .into(), + "8841327809851437433386666692145437950603022633472031964220924157605803799391" + .into(), + "7845859360796082275932181771457755704129556353505380746504571839006944723429" + .into(), + "10731793207832149137187382442869034250153492853628224932026933458041993639295" + .into(), + "5597792614864287090861003890414825257635680048696075527563498604714157576447" + .into(), + "2638669099010916296300870639816763122907432841565512299246441500223692345671" + .into(), + ], + vec![ + "7150832464835357604208338666096132398994318721877322228060899549998179405057" + .into(), + "5470477812928960639347760417261508685840724903499112719517942324191018679706" + .into(), + "1063854480993555660259858748055514950231824974684462401269695511649059715242" + .into(), + "14508243449586598349750829047481358081191713699373322296041764577478835760927" + .into(), + "14872220983064543437506211589956319796231014912750035729896461676577407407598" + .into(), + "9523202653584689553554068772241228948237208444616905879849472383190180438058" + .into(), + "10557133197819890801524243760013157188954914093770589635201319240903423455316" + .into(), + "4973822148190287060777561091733583032026446820262414806412485028147721872972" + .into(), + "12017319043066808147670914562193696608548297038020764496633388575589573229927" + .into(), + "20958507279974171556413354796214800332148109902768069171659933168603089927180" + .into(), + ], + vec![ + "16142225389165963605704721785850680620029805525816101628767304750729950332962" + .into(), + "21691255103889531967215183091383836488808797368461467004501598817850515277674" + .into(), + "13360009791215314413428942977255018953699328534302248245107197249816193370823" + .into(), + "5270206696221786165451075835596925139630328202641350960582852969440862939023" + .into(), + "2626561181956261201864606929566987806068271006198808163435823619705436605447" + .into(), + "5520368836328496672510351296660387187466158872913871354651108826881774455909" + .into(), + "21597143280250120305740582323272730661347349587666707484376745221123282421748" + .into(), + "5891209530846741397700015863630938364586207627850850447237189083999656313978" + .into(), + "1202436381171550812585103405636986166232789491390007497511342220946215395818" + .into(), + "9920320882147650877649039705433660083926352954797066179512349368247190410310" + .into(), + ], + ], + vec![ + vec![ + "1098498142837982582047608372723518751721607512716925277273595859756333857326" + .into(), + "498382712248562027578374863343601618793781182132084383060312181008958381971" + .into(), + "19040726265283429618662679510157690394832296024968480927415996691029230011306" + .into(), + "10367579130776133414495805974535693744211249758950881275217429221792836643614" + .into(), + "1229596364469449066712193908302977020022727834238778132871229393863406546866" + .into(), + "5594347757215876411130934611555467571639435097442631641074898978663329410864" + .into(), + "625275312666547608222628560378372315159605662141936411119837279426221363981" + .into(), + "15485529557721639677666143827295121022852505628489596851713462276650737776670" + .into(), + "12156576509577081554587930818670905775536581975823788207855134544267814269606" + .into(), + "1981640929928975005466842670997136169304057407742291166386016130552621471939" + .into(), + "9375079124430521740651903984797221620963928972304905809259607327125669559872" + .into(), + ], + vec![ + "268697279437287801043057266739136500465135819021738115532631740070584831216" + .into(), + "9310725094036396036773344350803037792624399505581573214229419814378683970851" + .into(), + "6144934044671205976376028664002834283864020049596457260475210339996948797436" + .into(), + "4985941506647510031967748765284991041503308370910665002557248958100799063851" + .into(), + "15851062719909725150709309168582658649310704358483047683106225599004779349418" + .into(), + "9869770840966008659377598457679699092337106962689936558150689057592239644963" + .into(), + "4964286354328869036674130011248598806906438908586967212984901377099285878228" + .into(), + "13408525694456518383125684465410538061086669117275911801498275369395798296201" + .into(), + "18263306792332242197764383101132914152275840410710698264525919817458731671889" + .into(), + "10401786441956087930118823951510684636068781082958380915651220354850381871543" + .into(), + "12496745101887166473879957440401384727148915595227764657145046356182346897947" + .into(), + ], + vec![ + "56825204182651219072479187681186238157981743937496557304633023935549648224".into(), + "7949519580094467639897040111470236633243836928348452962417270559805860514707" + .into(), + "3509286722306670968352119363633866055096352721394520084890481975258162907251" + .into(), + "21359945526252146173553061920944871506626324563977081669248710516265311530589" + .into(), + "14649491209868365229844087258057697734286269047837985905275053819765825128984" + .into(), + "12122186136173879572357400046587658543826161883897136171993927935307093999926" + .into(), + "2666476328185593105035429309804341325262753927547102747066987631391232293139" + .into(), + "21005241858197204874543384881533661499138265185107903730534607574687765896488" + .into(), + "4866331653274711303641000079325074227730641553230218424779550288347820225149" + .into(), + "938689939079340009195180604139206414955240264736983491692686499992823741696" + .into(), + "950493909161345219342597929783079468041198261349024441783356363638640688155" + .into(), + ], + vec![ + "8227093387774305505218050843028014038423742476679149203160700406235271548925" + .into(), + "298899716277443866412562171123535849674476895336539413683307522836440058745" + .into(), + "6985094123716229565713211140430519589886023406928617334981414752732877292051" + .into(), + "4561102873171162160916461632027561255705058072826965137552144392802414262261" + .into(), + "15422356128912397775473168682864290042256748428952418907369066530964035265216" + .into(), + "21534011877473706794700774934355764894917955655606512952257743854629820348396" + .into(), + "9461908500272520643111839486963426035162115487175673718316249722520977894185" + .into(), + "9042969964854694648113546554619141983055960736166619708191725199599555275062" + .into(), + "1441104948831954255692318866730011748129225465895791664253095290347818907280" + .into(), + "19417400621113450826458192671383621002793369580946623762558060167661227354799" + .into(), + "8244773274459817591888745631242804467035454174608673362960589130536385507190" + .into(), + ], + vec![ + "17088086767144106377842029064730946925009348520592888187451688601493882340857" + .into(), + "12886019902209719236096958359125451092745638766392722988311451127550961945660" + .into(), + "20280862819329644063010032903732505647194710429034928708829957501178343790858" + .into(), + "13239701144341900586601825324587185682073736334523805955933121583949546821724" + .into(), + "2994618864933374534869864629648211464657674590007913715843569952783382900518" + .into(), + "3072221011986428615228338853345294533299624086589539664037325300531050793357" + .into(), + "13594276105600327401961157952766116939399999497643063180657161489419638074478" + .into(), + "12904364780884039213184464580277965622079185353283126471569179129906875486852" + .into(), + "15088962493677593800057541234990587773412340265413268221386103386021880406010" + .into(), + "14138285403526705785804535000245522290348086552790608567368815987904186155718" + .into(), + "13481415964846572771441311017814910258609608797603836070350286657768815710822" + .into(), + ], + vec![ + "15459769479990273742477151452466966963353767555965255520456901549474045452607" + .into(), + "8586052864861352028352866296665876117392195296860481710367953704812400661703" + .into(), + "180502622991267551120688532508657597773982647209049478186474242637299204110" + .into(), + "4785745751361586866577727263713743688205421961646731269452058881240942369409" + .into(), + "4583871856798894230250707953295146343968130822948818555994825096960225600041" + .into(), + "12377924729639905725281972784629126900954187435957722012223715002490809152047" + .into(), + "21554415644278070156493674075483844873249829791940344144484983897474364915950" + .into(), + "8390225843490125870104241611355504124284851919520955291024552578484662824128" + .into(), + "2330476067094130593913781764168287559468546989640021387799865123741354870445" + .into(), + "15749497374252464770935521609391859230015300749964554524771184068776070217841" + .into(), + "16817654103281917947623051388088441309787140809596505043937473012669498321704" + .into(), + ], + vec![ + "9987656178378986905964646161927549614205785047077068310684205046327286932204" + .into(), + "21450061958292240283686535241652971764195183478875921481624114699420928365160" + .into(), + "3904617432242099936494425054740854886663050476318725032541401300619628714123" + .into(), + "21454964104289781104446533610149551385791852085041524046710270949744081353102" + .into(), + "10768409462143965702783360646769759623397882338491564999208626639994081655791" + .into(), + "19385613828688830964519526099114207553837496617978489639408163709100497624509" + .into(), + "9385292780799468553063371906778802189174789542685475364513544798199315486080" + .into(), + "19882577122462819381545089778080532575686772634821281258975533828284349988146" + .into(), + "1462201549484596350490921057903425036211202388283463006651220816599917679116" + .into(), + "16564642856725628254155356607086672564976261497486137590399143770170930986182" + .into(), + "11606470848655267736219046910932382494518380844147406842964119623341701511194" + .into(), + ], + vec![ + "546921055225672463086391798419385468083264065960104350335293012629066408625" + .into(), + "12676737821548820987278730174038033161886561534502963159950183188070064038340" + .into(), + "16429180804851559661054910451008618941371882312211198495282444364589225325606" + .into(), + "8318514508896823373027050528521007144041407638548138855564062559664141902892" + .into(), + "18546910687432012966956995548470714600618104024117576926439677823609854961263" + .into(), + "12006683905722730408249989907056432037202625403043550391187503858618155798348" + .into(), + "10816814135685807143320832554644398181525372167669730953193258726693903362148" + .into(), + "1969445073620598650457101028079888612893685228913473332116076918643068711808" + .into(), + "16873795316557869761040796336264749169213884122126281483001377666183529927793" + .into(), + "8441268321647668856014389726368355391497206989491787976537908376817970369132" + .into(), + "3378086906271763133245748026584767009750550242946195995254881868035794898559" + .into(), + ], + vec![ + "12721353531573613369892164015903035636498816100971168742462654106875931342664" + .into(), + "14969430369156214890953989610124286618925370029259450629468188666450865580556" + .into(), + "8545723361883060050915916338313252821252873299513393695440138873537985282439" + .into(), + "947668284380905375962163908708231363459059635485281084900173592739603282382" + .into(), + "4418352807772484492818068921024797225893951828921880350002134747344565378254" + .into(), + "18146914067008843660990756743559427698617136456156926109157771781314720068545" + .into(), + "2353279078725994188579023195684884389261433430819033940093641668202046052763" + .into(), + "18228226015329570627220992288018909552101992748538110505558715089403194764144" + .into(), + "2251557590571495628913478692960173580728135227602564510397207128937882297417" + .into(), + "20421664597091787362209209474226188711714308866665750343509458297343168321800" + .into(), + "8187951594294388715811532560312339537604737243977265499957088579012554679278" + .into(), + ], + vec![ + "15810834190411667509425096842396102750984990364193499272150958331088983323159" + .into(), + "16884308240478579935994044823717491481297317573500280152191710196639752382061" + .into(), + "234497484353824748419812158321111328486478789224631887096763967543932891899" + .into(), + "21452418791072076854500976656696245147472896609273403517249960331326136475572" + .into(), + "10860322289080285812992522532751459911253736747190334349942615321085283282595" + .into(), + "149826608572716492570322179195234088797160854886751475825283168005807771516" + .into(), + "11491761442863092383423796629001188933840969144934642247702733820824608517603" + .into(), + "12099180244453415217270377899736157198045626379801787493348249001794558732373" + .into(), + "13177983303979037999809722097100345612970493007300007493855625634642663397908" + .into(), + "6849052800275826145043024580348093078809773712986428314364827674907764829568" + .into(), + "21486255029472594818259653174918852363002807142725698741685253190938680807594" + .into(), + ], + vec![ + "11451503340703054732459437884000132607423536025797075877436151438425159994269" + .into(), + "8462539135531767509735697608276067216182907546891182278996691315801807234639" + .into(), + "19944711893825824667372913293784300313762563232409638194240029859435259601775" + .into(), + "10396631238556297232793544122243237485091433966091043100758266678889110827200" + .into(), + "20667999270580360504376758654763163152764187226267414436968564661080084475852" + .into(), + "10424436665500877000658892169756884171624649701456443210945810183301667922053" + .into(), + "13894422482417998868290238401966517700776990643618129177567797594771207188055" + .into(), + "9076475964444407787992938909179730031379198268423789105813333967195259669658" + .into(), + "20479003631920854685589262232015009286810147171298477411667705150903826855301" + .into(), + "9928015403359312830073752955992978705151208358029077246413002475277600546387" + .into(), + "13981618256931763962905358530247354996931923386029793318275706908114940457317" + .into(), + ], + ], + vec![ + vec![ + "20214838738486568883466588390719332066160511773018226407137866846447805607366" + .into(), + "7161524737853996242838650618412058002168848579199128467811556550737619970970" + .into(), + "2264369418377007316930430297757084139629356094085160360541578125176213258694" + .into(), + "18691044064909968568998201940845291098399339626807500263611343942450116503516" + .into(), + "15978743992268694554518277110515494413411623432213713029162001242329212269562" + .into(), + "6711615239704822975151699228936015251056551262955961924747531220602950448829" + .into(), + "14954997163751606686696628499315041796272082739441018134122451910369305642115" + .into(), + "21573550100361192110069886620445669562472881453105471211193858578537227040439" + .into(), + "21785281999660091964290541777959906196912107196794342243439922177000502203701" + .into(), + "2946923208312508080510106804563669422427642075683605437758174474435322095802" + .into(), + "14039283821812338763616072949057938719426671560747126284782727998420210694521" + .into(), + "2531474643515410792989587528850930504447014242967363822821359471367799986101" + .into(), + ], + vec![ + "14281461695965914110119049602449207565231627068856382054789426564141005041994" + .into(), + "2155595480001027852247471998853878746887483662385654030663226564169133356539" + .into(), + "6212474220474204735846033034823136351584003532895558668927059407038678087162" + .into(), + "21589299957493491709069669042662513245508573637668760884022386808061869005942" + .into(), + "5228547858762057503048110033821407961973668275986265942002757629551762149969" + .into(), + "1151995769496843179907951142523838829938796346663877830241077357918848539138" + .into(), + "16195901973518083237059346288792924649902586274815274684503783828189220931050" + .into(), + "6205461827971201267719191643863468322713562983419848159871959495317073732623" + .into(), + "21004710389082547785746156915318076260017385298749146368429985483091499557183" + .into(), + "10094301525352802553607719810440185681054064961117719137647202357989110756759" + .into(), + "1174362264673060234121108394303385502501621739298129145129042091221378391858" + .into(), + "14586772089804608057953886654898255839796797046217599185042293580394420546552" + .into(), + ], + vec![ + "704103301411330239947625288325002010320119746677418877341164806595452864925" + .into(), + "7447867166827402056774077383104558156866119014007569966692643297177923018546" + .into(), + "4252152864489296917539284690826221964698345550054947572793948075436067436040" + .into(), + "3675525234832046985215853449128143168168428943627479235047788418993254287405" + .into(), + "20125795627598431311475910664717716586147044241536953058242999762934679572886" + .into(), + "9159576094573932436478222856304524043339640337232471953289062354187369243885" + .into(), + "10410289328536677868407694844650868516861553712016012272941004725559785872650" + .into(), + "18813119519933909103102649065156934680537361290190751928265976568411443987994" + .into(), + "15043786404237278119878717250753259786450872051876817420168142382486008024593" + .into(), + "16614805203312302723146840789675006378900903626996105116400354962001922700157" + .into(), + "899949298359737140980259063526066233582477211127560605822280959405167872532" + .into(), + "8350589775626940122507262589996655703528509795097550101006133878991750882468" + .into(), + ], + vec![ + "10881253968160794744779175936360108103824976232977458894007732866457848744711" + .into(), + "19359742822671794584060954988237182553116341604406926658049749172292672638977" + .into(), + "10716853194721085390661796797973316855886234718612858006131046035921078793777" + .into(), + "1194676839570189281149587289656564753779383829131008000754135056646064455278" + .into(), + "11530412134598354110310733773537950950490005376234226554463355736782774653810" + .into(), + "7158806839647137330333220334046918613209783693378018773439140974716028082046" + .into(), + "18873459493111992992450800068055835432261777460679870727272006783676545919785" + .into(), + "4597339034364379110034269874329162788488647975988086437272199171979371177111" + .into(), + "16047595573111403874356093398802733070084530893238592035018321960924442437232" + .into(), + "192949463851654477795020911703008125546432931266166268873310745978202434603" + .into(), + "4826544617576366487123936439697751633333779280970103286526767080486441353413" + .into(), + "10372441609969764399977561535165700928227575842447057367716683958896898456242" + .into(), + ], + vec![ + "21479608666927871465054861416648367371602717876964759897062141685818604541372" + .into(), + "18757812710789932354215078701254559681588101606101822541277700443926569010598" + .into(), + "8502339138598356500092304059172334649791727023646195989902300809704249803746" + .into(), + "21240184871409684692673423121366677112492469214890212851758021155034260698420" + .into(), + "2702659403779176675766431784851669876796725738129029887042678538644093630255" + .into(), + "18760062461290937265331504644060340132840729161526164449611377215801441916965" + .into(), + "9598514148929007169331478849372274288455651725546984183500169574552892743616" + .into(), + "12460679873938368098608659480431260988399308425323633114529665233186673892475" + .into(), + "20582262751655750693560201069767758489467289978119794831247596435694971251287" + .into(), + "7495462389257720258504478831214292184152544822380786356126692935003910627822" + .into(), + "15847020891468169726540675640439992039404102490965287792626266482436024810091" + .into(), + "13444178956365729587956577087448840645730541657243126743158358416431709484781" + .into(), + ], + vec![ + "975733333906184480394673719901416555779305044861384485566696694336272649841" + .into(), + "3016935868211088289963870855929013645268121688015888423636516996750583017171" + .into(), + "20123197829824640950428347870445510232078708523077317828689832072338303017047" + .into(), + "18496031799198869774970797646230665906722932354114482887753612521775690376535" + .into(), + "448875332457320150287933426080386825611557032389972932765197125881964153702" + .into(), + "15686083476904717209874986881961195356503069952883501862704199048297926079733" + .into(), + "7399632407841430295111381086121470926608686430000074868388902950170939693998" + .into(), + "19157441199146430337309347165554892283908758853741856357912555742738097866135" + .into(), + "16120175937370916934366957179931217076202557540631878137626313655342796978134" + .into(), + "17363463873417672052573440102339969267068334412527908172228668014397269133762" + .into(), + "19764937897808275673467150361977575240242645746672288031015882089680753193420" + .into(), + "4264866715026149043371443488601547814355809386242957666273811883512215893986" + .into(), + ], + vec![ + "15361071774597522987390988933793735468585435977940286138223232105339041682390" + .into(), + "13444894080484049025660420839638753203298145906295848687612728375851966859563" + .into(), + "21344396291142953621865942956005813155481114773979414786364869990164493168988" + .into(), + "13658955537084761077271566555621122724333408573482369456630860179831273897019" + .into(), + "12249794154563702076745009616085271813195258124596555920372455161542247237218" + .into(), + "2325936171131642979629131064685171177284405924159468319138840745089808621723" + .into(), + "3077792516542862676300186898187316876000625806438082696997401720196346610884" + .into(), + "21531439209065692564653170259849715075994439889756241721092517464459744953429" + .into(), + "765723669836774164873260120197059605145439283015732643840276151768662398969" + .into(), + "18430565163341347334129211602477703661982280889871625277638214021594755964149" + .into(), + "15168574820004856312411802521805000105968244665018577358614174215627811033660" + .into(), + "15609931056593305381714243964783225295053513474263648739398875586829969929857" + .into(), + ], + vec![ + "13057901697952283349663465856361305032896972742145291496691475939407531431061" + .into(), + "12802339382735521870414423620343194986509343830854539673006232369799979885569" + .into(), + "7880620299082787885902391014823825400306816285911629201048471522567587002433" + .into(), + "11085221899164994413080236199596538381402309021910771788195135649489784323294" + .into(), + "5662061777175931509849062158785593075054461300898808576328927259801174692172" + .into(), + "19620207415640534190314969542389551464821476135583607027506853686406083753807" + .into(), + "1396012663571482634431038119696061726217340333644346862093678929991918911771" + .into(), + "2833239640477482582925766504780450890721725782645633022528100604619065406714" + .into(), + "9289278993548596713194730547769009982667061443580050906003394115646319823584" + .into(), + "255970566924787837673441110425992267446525707891905710167559324774004600788" + .into(), + "1607914894461957709182037732125046273691353312066921168498378132410220447224" + .into(), + "18182823650001333075321511247233769219797858542696762318647781137777390858484" + .into(), + ], + vec![ + "7933393968545943401801081658073805133658457161128306876557740191220424567009" + .into(), + "20347911076420909832061080138703827506796370399337016973599032573292195453934" + .into(), + "21705355682416154516146726727072637540254191883301906287097797468012136754530" + .into(), + "4393707213821090202627671673506613966066953344462172841905371093203947245835" + .into(), + "20015218870609611793683104277034599032802947064261475029925414549380954228847" + .into(), + "1857954279082383201486002148223947538500417662449637107611316684336169564836" + .into(), + "17075622394357639776259605879156105089449277115338468776425087829255085998708" + .into(), + "7849339039625631210191134106813689727565217371730065596482503614929894173038" + .into(), + "21512601485458872387622978217203423557092449252654722032828165846830386134980" + .into(), + "18829825907628826679915224363735067358668372607988097049308159580108100510295" + .into(), + "20145344934445170391525281095510437469996020214770888614087514859850402376676" + .into(), + "9313354461544201805378332544085825218707302313348145137439630918018863309279" + .into(), + ], + vec![ + "10073510764514576042491642785075889098754556687860419668420630560526859371108" + .into(), + "11174613823246619246542059297257164876488140873425479553593597508295299674750" + .into(), + "14486836623332191458290523271325176557072260063250466764030358280051240942286" + .into(), + "1932726606175618877183776650118686155013167275195994452359992776843317764341" + .into(), + "10586981584735794740885178709528523597770788130558769643251430679627096503451" + .into(), + "17058731514535449611097320348142652958393616886312564975789110366863616539420" + .into(), + "21546144187559470183347034044609056404441744756503592317087407561629774536920" + .into(), + "5681882870231768621749544290358493454695957383787481123871436386675876219635" + .into(), + "6603033703828934401094376159910456525337139277249114676008536852402499584614" + .into(), + "1075347119451441392402288921187671249679641364496638526228862984392689015760" + .into(), + "9887880282527621962449293235959776308591956208594163166185549404667941094205" + .into(), + "3081779595493746844428351914840666042619592907445560123915127502392400574614" + .into(), + ], + vec![ + "11109844704163389102553826717541117344605357734084342755776036340022417198082" + .into(), + "5060345909602600407449982784585458050225699107485058131116856520723613936306" + .into(), + "20049893406143885619592680397871697055896501875354968455865404394786911398458" + .into(), + "11740809795693360891733016778293494519471041728488086332325924371909574885493" + .into(), + "21414777615318644939200434569971788107661288737252832241371433802590137831626" + .into(), + "19926309906539946638451151936495517057597377615887396199326446649447508281702" + .into(), + "5833294070005894544680949322571753681474561462111500624110195335953784263127" + .into(), + "20316262126697618722223967532370347145297985363803056816800332573538115921683" + .into(), + "20118897454905330779316757365327082825225674670546613715170828215358297124461" + .into(), + "15893782176793316439240260419014348246083695310846638270933249304684265430800" + .into(), + "12847296795001788271556697499714377689095182754228824085698104180563585670787" + .into(), + "17258109440267943312537478894153608811927087776527641627893802618672319064807" + .into(), + ], + vec![ + "3727185744255496747036491258134142468721926815259510264718979349995349167789" + .into(), + "2377620008282598351802066487452475263179928244128123362464911386705146759528" + .into(), + "20330733534745333298462159658402131849518313653717741882717272744687077336453" + .into(), + "3063488930518144343621406800230347607891775381984489334408858649400823600099" + .into(), + "11822391183098027641060542512210687183510613996100060945754635806285989372827" + .into(), + "2697686870567304805976687716601580249659499813659634827192211658186812105269" + .into(), + "19466890284409856892962357589067669895394760875472697889494886746493744150398" + .into(), + "1006970646211395884475799222625896618366447925898943829426435645426534803620" + .into(), + "18668143903499292595688863135570950175417970684200058312498191992564173409237" + .into(), + "374118929819602952730503470915153310582862106749955863047933775501492632816" + .into(), + "8098759627317959799834443934069068232617039455327629644555780572940389866941" + .into(), + "5647931789489182000343586961287147762347200093731102535565999902997464444183" + .into(), + ], + ], + vec![ + vec![ + "5891205978627836991071144083270417159015157070199928807771268303875194037650" + .into(), + "19534191765629085451497649051014772157774065629075791332793195826681584551273" + .into(), + "5154833515272483128294702820663628026710043323095920240638701304804298499578" + .into(), + "7917593571945709638335150893778153193741477651398934233734658265336884279055" + .into(), + "5133163238095742835090645087711007173805146496653007212988348307349716673728" + .into(), + "19458003745533910239158707983408152209004063097952693956218574434126899070042" + .into(), + "18880819400751577287416293176849355951596193714265500681157024197361640709188" + .into(), + "11433257809059443065528679883569438998689217744131300496692049205047550090935" + .into(), + "12190874701550908088290603272755607342096152398135156662834905887211629834704" + .into(), + "3673886960353993252497154566539843726250940370616316569888448647738018022083" + .into(), + "15676037835112699420746702265028664494892600184195941373794207326709270851677" + .into(), + "5045635616511022726309482514512221209262777381751689684810362174166837266849" + .into(), + "2127981970274354891783037834911068612842150500572698772082540184222710046966" + .into(), + ], + vec![ + "20602209860969247631763456039704661822909928028055826436834118113792574371849" + .into(), + "4493454930923344041221912772221535937546211498548091351164191172571413962846" + .into(), + "9203273896007845628978022785284502260659411177801743914849998536940830966257" + .into(), + "8789969715987458351416076625905723956294447007026950650495844175328857015476" + .into(), + "11215930522605102963220331022508304708093835095809374254680583403572809751875" + .into(), + "15586225257380986275249577321891268850474446160605827963612996219805386502932" + .into(), + "10011841080310254678847831138830289501574615297642717372149423820287945856079" + .into(), + "18753960408421275836614693391945489354883623026056573684871883182548779915774" + .into(), + "19761678450406536764850099419131825403291221931719095841429266089353917202891" + .into(), + "2072721799733232450097521331054430993573774079728747665078766019206508544636" + .into(), + "10615297085623730419003352440151421233872764050371800287995783143303761635742" + .into(), + "11342862179098306415193268089882628716478112844171129584590093902612740081938" + .into(), + "19276661508999391381559540866976056457016683157876039262437370976462342153704" + .into(), + ], + vec![ + "3487982371390545412669202630006964510064537478038100559383689697998101381695" + .into(), + "18019611455528497754832260490613820837001396745087990928355304172772344827985" + .into(), + "18731969803319425707048526160946688629598037600738751037795347491343115736279" + .into(), + "18895516979789867152215547520753345684534505439453679476362827176682322481937" + .into(), + "8262582236770254192527817333585360308520228688394271238254948357217070957179" + .into(), + "20124670759706967221386035867404978156598790198778905768160002435038055915086" + .into(), + "10113512603622787997151768792636528607526786828914243169997168136386113705095" + .into(), + "915897670578586705347124681284501818659263249883815809455861196950322359631" + .into(), + "4168315355477923626825760085789663510629998017921421169386695143739645254818" + .into(), + "13671994112691093230470350971338683534216964112891455368255908216954091232088" + .into(), + "16886224211742114996348237388698196253930997227482938831037908300450123060344" + .into(), + "3345133367703042017339663005080189359441174937067366586009093723866269451347" + .into(), + "21528583089657067992968569213666076092311468898762774519530397406988724032331" + .into(), + ], + vec![ + "15876161034145690426475777675897218203065468785806228994483284137836054650127" + .into(), + "11419482087592638487692501143058453465931464811952523437138882421550619359191" + .into(), + "4688593371456663565609532492788107789533208004746508669973851893459273665535" + .into(), + "1201806670097794327812047975382630669548999745861216990648173033237996826404" + .into(), + "4317641195125807665177432835324854194367911827066332519796115671103402289320" + .into(), + "7278153623621857829571838333149240184056003767208572498343141321166882833584" + .into(), + "5645725384264681461759518050072125001915558414870126637288820197391715227313" + .into(), + "7876944044995178879031614460771670730631140542631541219377182212657483769883" + .into(), + "1817650660807237476840344988506407016951597837358142384730920692904089879519" + .into(), + "5477456541807551375261337022497006230056471139241891672239301675658950367705" + .into(), + "8048211508931499636316723219242009338536169470228918968914286373316199192147" + .into(), + "11051780522682663717015921863167554166331060525740053284975151807269395431450" + .into(), + "13621239165564266256293257623306520070257833884735472109300551735647149439281" + .into(), + ], + vec![ + "2212937635665982737914958126511628913962157442295931340442990688391698941226" + .into(), + "15995366165560744217392074544914614299200056620022955679432568011246760194348" + .into(), + "8186603384193770310414376291688089375415922904100458138117461272757184852177" + .into(), + "19591014640167727612037871145675698427320771791339346286884839214170680861630" + .into(), + "6878084246380728147562027775456286883121281123291292075367753371891198993189" + .into(), + "2177050211387664317673794964274713735596159455191994291603735988793477650579" + .into(), + "19810792753868883549077084303872022596455081266982011682803771833184330522738" + .into(), + "7185072414158632003497987061744951789947697798790757573674746012007540132625" + .into(), + "12527008463897431318214816404214269326472255194708737027196205809865368523993" + .into(), + "6934500447964393691613594947677420114877610140521721836488732706620555412923" + .into(), + "9978011727059765171039296158502240318826874847845043732001483806732259491882" + .into(), + "21367223873262404675887107131444254925505042466133465670356500592380419754092" + .into(), + "12370989539828127569760369184182336850673006315469364895069603190681475159813" + .into(), + ], + vec![ + "4771734208255151020750966033073146490824522462970752283771863328392876062708" + .into(), + "13551343845317029011162863399460125746613189002552375164523344552618567494698" + .into(), + "8714428409330855425634336943651573814895603648572558273360471650145732556280" + .into(), + "1770920281347553432035718101936298919487500537138994976517066719980578590089" + .into(), + "3110069281490803391353365800012007306367848815614936878141004366920256162421" + .into(), + "587101336788172489216190547515347729725635829809300681040400739386253763168" + .into(), + "2745547964008447408503376832407161412155228373377123760513770235899201269964" + .into(), + "15688219884606649780982718944113231917978875761031663915052600865004707442286" + .into(), + "18825709614401251798160680403375685428394659815656068061728877494732182115807" + .into(), + "7344398268236623675422623600003139537460576229211381042555723883054380022043" + .into(), + "14666515770263042245313469306170077834894759906373286169967918153150186862642" + .into(), + "6262353441640473491135912890626291592970997790093308164286742582769628052614" + .into(), + "16647307543328963728423591228360400112670150511841037950484862728187168155597" + .into(), + ], + vec![ + "11187123547390829437191210933751439038277808812390863028310714957203862953416" + .into(), + "13431586020033401007013925927716006954532655767977222332198563123215088393612" + .into(), + "4290575536028694423523505804878297212249395907285796384174966179335089734293" + .into(), + "9525030500997851642842588010076299538258273880797610368114449809143832950303" + .into(), + "8240494019366037169932737683997756281590058122972608854062263901069681117554" + .into(), + "20634410655079842888667296641045124414486057143740858179591482529433244800210" + .into(), + "2021226937398532158458585055746155459624344885692396128118875161667614679890" + .into(), + "13363628208058779432402710458326211021009444288989875416757721068391318188214" + .into(), + "14662421589311461388753832631349921077594459767269924751258576584313288868105" + .into(), + "18478701421001679788418312436274921897007545359172305786472370930338255515306" + .into(), + "779644354087716689348274274240595541489283221242495213448957276905050464536" + .into(), + "16071592196394048404777963063722475415819376772419538934537115615105548954438" + .into(), + "5095096770582161819227893847981354649325178848130636101047350986634230116037" + .into(), + ], + vec![ + "1243295118881144894548654933667320243992122811397983231810580344448403973343" + .into(), + "11884934205846782297010667633102865650294795721122133935339824653150509106639" + .into(), + "7458968983251027899406062962031140351528726088472453510482489928822496580100" + .into(), + "19572605475586099575374380909719328911692508931262625802140140705257944509766" + .into(), + "14705309817743162695613012501115465410697971407093611587529338557210155341093" + .into(), + "21814585268359946040619047839768523980706543116273413618895661291550785045639" + .into(), + "18720305501197276565912107809183977276090965285535057360911917408689742145019" + .into(), + "2134301697439195186325742384937390317718398738774895777564128344393744278579" + .into(), + "16999326242022117650983709520661797031983791094852258286603416430772587131676" + .into(), + "17897483181215416614794986081059087805317610826416633427262022077916365348849" + .into(), + "19707797946013555426424189263942163273279448488563211841018471715309464788783" + .into(), + "14555678829341308540860562709255991938855501651550888461483653488337939676588" + .into(), + "17257409408848021559108687223120061819076248102607600439065783833668882002860" + .into(), + ], + vec![ + "3083159817330696927114122348973911210253613266522114299928693807761894470034" + .into(), + "5736074496230638296274343498461296106748247754169694274901381380232637436330" + .into(), + "8207744709591183622611260068351833593643143431375276434360211505091128037806" + .into(), + "16073710603427960567922233549405442518423088068367439127980767364626490766482" + .into(), + "16125801016656798988611163501719363451449395969542389751490084517803061425074" + .into(), + "16681204974924630782971582682795720527615927905982945319130944791490607417696" + .into(), + "4072675318306311800303326010748274698511258916524447342766005819081244518392" + .into(), + "4639558473350853876171991553789446979158416238030006798108198266387155784407" + .into(), + "21611752344375994669307116989730257280581712049771305171467376136735830835317" + .into(), + "14260028812889714557229612460335412900866192266288236233734870104907234066106" + .into(), + "10936007367915129326030460455513265303372033050181411601450223671258879981020" + .into(), + "2935032369592212871409648743766195391225915910970902425024269583756879977136" + .into(), + "6634946569637959135045435256486295750841113686207268069245788404426148269439" + .into(), + ], + vec![ + "11165382706437522214793349607928919108508947068233467479625942161240196013032" + .into(), + "11449774151698349588558943383567398137746718964787475388291636245211033594857" + .into(), + "13380655477865684658511486065341626238240224687038830357479380314844874141318" + .into(), + "1556090800260299290436338214947407050034615159120446561828975680392439133850" + .into(), + "15775229412830292677008903643751483031582598935755329498528261914480871637362" + .into(), + "8325948986690458545596228454116700887740572176003019243020371356605705227449" + .into(), + "1530006957398320461897940887072398082651602436763497487522949690691142033613" + .into(), + "12096799031117418724262752691656478204625211872574986459351576236141568686903" + .into(), + "9795222686269696766812901618791046177266354705263547407732303299461050133927" + .into(), + "6370027108686216641431817942352503637286925173249339052075608610090399016749" + .into(), + "16881432515653361341795686702127944966732199129855247935726797972793170639701" + .into(), + "15351738821101585856536273249878864428731819041333447251288973888111661451683" + .into(), + "14781212701946742438784746658712056984412254191444809612525158953148214912100" + .into(), + ], + vec![ + "8091550554023025707193058566806958042583606199181127012808071174695106343115" + .into(), + "20490495862854672187041438553984493686275844004543178578410057111061213880755" + .into(), + "17273768908086623408127314492263145283983205996943328025810362733169697859553" + .into(), + "19294495315219029609698328900049261980545541811825479502505060031138576089112" + .into(), + "21659964890056395567978937545379633715401240283937936880886676845913261767053" + .into(), + "17982445074266408124204445317785538167802060185652418682509052997213396963809" + .into(), + "5844393214733022541634389892381973171593154485646342141850588851005973351855" + .into(), + "18594392739980641449638044568401634303115397702236811642652121212412266849233" + .into(), + "8182160648431978634742268417253967926524505311200391645921674911687111696608" + .into(), + "11615953679796573972512524871850400010935503185676276201708450621253942540441" + .into(), + "21529769273680682324960067458105490598252059794192747768640910191459525561125" + .into(), + "20132110404365493609540330952369583903023878161175287878881490874880122068662" + .into(), + "3315629685104800403477925330007368560121731964009894641262206418774875779654" + .into(), + ], + vec![ + "20760167657218552641600617661638902204729190730886821404831449731791027856388" + .into(), + "1071944235595105837840075721866496337725631499557288435858051131598364359180" + .into(), + "4555500238184379140256400675916267083899817767012366155141824954325999920862" + .into(), + "12550383095401941417929336389002623533497339330808819244458405679309028814897" + .into(), + "16242206659396631583090203067480642438705316999988728264056381863300603790054" + .into(), + "9739751068073084582742270086389269366580429568378316378361121134559060472609" + .into(), + "16116599372526867528428771037225046273420084760475362036998100512782645537424" + .into(), + "1491702476187736766465155485454380021314952117101776564498935819251125640550" + .into(), + "15487639992401023512588653485898591177260483949656489128772103961337143916568" + .into(), + "271607781293753174262565976623664759968566553298322241892863651425822643900" + .into(), + "19977950168772761551778240748644447848771422612925087742515420134526515662483" + .into(), + "1983058998587842721058540590280805932874010898105761473162166721490888500174" + .into(), + "16107724245058262116195892590046753877901405295502298275519986777803068771465" + .into(), + ], + vec![ + "15598969808860428995774387003121151871293432204528482957206560135604646381090" + .into(), + "10303850219152079194793643117738367623863138704730923818757626414538626924093" + .into(), + "18042134015692222011976424010811982750427479608456170876100049843530175591980" + .into(), + "11698490610713203406365860893495577403678312400711273827266244766764136895745" + .into(), + "15094763799279956189651458728729592947034549021140257103479085265792492443257" + .into(), + "430096731859098382312819496464652103427606481282347941948051379574546904741" + .into(), + "13960717870097098006695192936395126361902142536383841733120824678277505383661" + .into(), + "4016844697779547662080765040347308557842613782172342555085194018348439875647" + .into(), + "21390612758814913695838461279472506230937295081984131634720827665924667792338" + .into(), + "15210153052017283712229693210655704356560541803226219169450332030313730637768" + .into(), + "1405020343631287949667260064722407285384539868534544303707265307694982887517" + .into(), + "923081188208761071201163943024810005498690637139303388717205039798310044759" + .into(), + "11733793144318360060340673323677375331041661345320756410073354731608712531433" + .into(), + ], + ], + vec![ + vec![ + "6418344278839121997761558068555633277874924383297235060428040203550148460392" + .into(), + "8184778893153361724502825398680471446632259194563037088246379746010351701224" + .into(), + "16103007220917684483813510789286348056519805127857943417823167430289370248882" + .into(), + "1664056765289606259902279533842090994664529145577166075019619213360544081038" + .into(), + "8189267971733078428327714274800548471520787418839983750528172780925243373109" + .into(), + "20200362011107872066803394413626937903139046091127740529060907959370256880701" + .into(), + "20173744990845412211550008592188171997284132875646553363208340317991304601908" + .into(), + "6100976759548353184263451545777211359935324012594548692714862113681123862281" + .into(), + "18589557631793259794347972680714314322014920073994928130094285735070065431315" + .into(), + "774191617468212021433032703138192471679212014828288788368078839883023639562" + .into(), + "20846157077077136618808870082870348881758556999252332666235423866411633465885" + .into(), + "179369838162302125553370844232732082673907343764950159722856079645852949296" + .into(), + "11918621764929568738238867861947162353407822663185301208219638335882724852664" + .into(), + "11892816132266551039449220442540498829071526084314780371963132054098089857199" + .into(), + ], + vec![ + "8372083054789567618141127024727893763407629214826505908070525202510871464376" + .into(), + "16053969929668995455194287538126250235271805322133461918670713060143921845064" + .into(), + "2376335354846722834191665301267088635977856417140218787958824096999457654386" + .into(), + "8473768787638105406889032631028566536048259675966039382891971280359035809717" + .into(), + "21341339254986951084972542739281440827262568377712659704364142966685544694259" + .into(), + "17589661461505870684154172724042456165609752814624225501433267429299923279866" + .into(), + "15569410058783548405562051853887675489795673100717406458705097884992918832740" + .into(), + "17136771849960699223133648686528512780844440317239376836819594891344485260741" + .into(), + "6846911134505306255484642794298233221271816466086606204236441947984501580805" + .into(), + "6155197242495613968880604860842601809115711093712877692064968695514089066009" + .into(), + "19128637117872288039529749360298403408432890998345732113683578733270478526860" + .into(), + "13897081066927368111242039741190751095002227555149381235627883407347099966197" + .into(), + "15568696369607733677931070776042128239961624292553408918258251667007167792827" + .into(), + "4872469898347155985390501972210222637651779849978701189099596399070936407527" + .into(), + ], + vec![ + "15828528305683107589096901962734302130704783164486093165237194118210566499447" + .into(), + "10871050448146531061933214597828428556087596885695624079458134367495768692641" + .into(), + "12715634392841723603484337993449575047813504874083783048519693646075495401499" + .into(), + "12461797447292058643518857326956961860724022201671935652194772338021756072767" + .into(), + "7893478478385050482361413970069327892342442172936930424757486435631374257835" + .into(), + "13837601157198138379089270567935062044589875195895421018230835162664685103799" + .into(), + "2902437942374588332095798046833013227638771976745087670719351344275677524328" + .into(), + "15243444720000907684100993103252257759861356192301299607302082943722781818072" + .into(), + "13807772002950549127635782715429970069282430778664455286304012934232212216437" + .into(), + "3330690659470353458591000516836956263198779643900957759109026769401030561064" + .into(), + "9121369550752168091283493588432030055987339875340008013960004670271046125246" + .into(), + "6735940425069065421825600687003545396352913420833444906054707032006918504438" + .into(), + "10202597085447755780667010145999569476263425973948073114389503196598866401333" + .into(), + "5502428069812047253902185475715178213199056100606031376530298129227524479345" + .into(), + ], + vec![ + "16644412428296109976288812904703533310452124314681543539244699753829624611041" + .into(), + "1464363769917768267070780357984170193149530071521363359029977430003041058712" + .into(), + "6585015011013232733155666746473112211355795487658434466373981554623709413097" + .into(), + "4801399347914387190330170488585953929447785020623558112311440395749307391856" + .into(), + "4112082032915966312546686950757741516554419896344654985273514512341960565972" + .into(), + "21751105223203500709422523072334627517145925318798961657655356624798705469220" + .into(), + "5444707496026644829581718915396971639630623832272466243662913303978147462160" + .into(), + "5176202236175268355216491019205403607765794084697970681965844070084688885903" + .into(), + "9306814982626014542830328909308268815941037317672165275254890008767120833129" + .into(), + "18534005003473959617581160840952814838772020918476059581821124675494141739706" + .into(), + "6315286473605163880327953940950267064586093973100720152185112301408202766527" + .into(), + "1300066066523772310953083996541204931152213396378337235691979863222476540925" + .into(), + "5067928636570884230858740896001720688497565237268231912398775294420405805509" + .into(), + "2247332707031505717534336329466562637772796336116582429349574314234357562544" + .into(), + ], + vec![ + "20247738241445291688801934041857783193064556313940428743035963593744371184825" + .into(), + "18242310613256758222060583109097399506427347785291803988073944852458874238653" + .into(), + "5027570650503078047623512055050961603632044524914664539379503584245086748701" + .into(), + "5171024028215683374631525538977404535998938725569475537905229563957776539718" + .into(), + "14944506678316768982115249709678873125670683211913646815567280613875065136559" + .into(), + "12712774808537003317921729077604843728755135888798177519726799585299850232729" + .into(), + "9704917525949617705578174784620125192847872142914494686332094535178307346294" + .into(), + "17607293541936391773356775760016259535976314844718033784671775110177131099056" + .into(), + "2540563558441922228123722363766317044293335648393481767268774186670778379682" + .into(), + "675334497725789143197575210295522345522852999942555409554666160997486976627" + .into(), + "19406946394055111935833464724872576724279555593094398662505995447037826031689" + .into(), + "8556081865419484437161415079267380957278647215963771014478927152338629734722" + .into(), + "10894080579102201350420363592007910695129177124810422954037601558695648517625" + .into(), + "11291505870061211613677608925985023369330908318817731122393740145823745286941" + .into(), + ], + vec![ + "8111345701049008354923033690718534410144029586027127161268652239467844080405" + .into(), + "274549942856353845785392053622966579448945647367587770109876363273061435394" + .into(), + "4517253471300397604524436610993375842851058850598308811197142784491633632173" + .into(), + "752091920065645178354359484693693144698238715470242001046768604290011335506" + .into(), + "11638201273705718037686872429810169270152128290712392950719778292205581621583" + .into(), + "347106139963113855692776969661437659762102942024424980057845079543094637582" + .into(), + "1650860247124995235720423411540903470864782697117302631679816892337391491913" + .into(), + "8807792864573824348524944774842835780541376663413796103538010142332713129239" + .into(), + "10125706840777901469452374931444076850109537279493387272544675392762656076263" + .into(), + "5796395502360267028606772833560634747652475684290748049574810918512176530377" + .into(), + "14560087906294745333868694378307270211143393355422503000240374502300675908843" + .into(), + "5122547319055760404581514483238475236540700695938727023605315976547513018758" + .into(), + "18167310745991466802649320845690518166441060923615799361858509593334862918793" + .into(), + "10537655786889979411145772153623144374332103428122704649668615330715372491734" + .into(), + ], + vec![ + "11129217221221505592409814225825346415830118409870141057639470413354985898470" + .into(), + "18539905463366886572265160849053355497514707079972544561033138019460289616722" + .into(), + "2634262960559341073751666364573144239402802922297326052528609874036724778513" + .into(), + "1017221748705774701515487698826243372900579204850532621904642815268966836932" + .into(), + "17514607882958712820434977243901228312981191640605665595923587928949930664369" + .into(), + "11518730158165740016295488007249832672982237183652963149460237152135828893220" + .into(), + "5440554707898671641433725775650782970626686693839748603607869391440450924607" + .into(), + "9147974268228093219515993293158817299424235911620662153289209094112083583914" + .into(), + "2705991777416683673465834362340927783587369064825521543877658261521166181909" + .into(), + "17763220243034576321349123132976335282008457858814906290437636386824597776861" + .into(), + "8436811158855395670172615633911662578819734431001698203244903207934616540973" + .into(), + "4138616319619099661442960332848564292509386403496259477312793995345740346110" + .into(), + "9291305887217504987438822522544685995385674372729325355668719643387679293273" + .into(), + "14247130176192495492949419984506575479387281081069847984799112525576996955413" + .into(), + ], + vec![ + "12065917784915207956255864287163607339403293417584561373088222422519855200010" + .into(), + "19602746430676351974790620090862300757670420200072717172839368523905726952498" + .into(), + "5634613092261261536249683912838127927279222476821160426063089671641025921660" + .into(), + "5043354945289735676261322233679902891599697472766188245131942353534121350357" + .into(), + "10504830582720502950783794220870443419986496189960249704278002268186024527222" + .into(), + "14773755085011014730752864609252484899166588993023999143755304644745097411069" + .into(), + "15143011250372146369484566133567610168314155803929775363148643430052742055690" + .into(), + "9296771043817098875687880718048618354875037373605143322099708291917193542563" + .into(), + "6699308928121904151061270394393246387724926649022798867634982215175491150673" + .into(), + "8773562200655600334022608356584787571874596251190350038742467377669146368640" + .into(), + "14351113364159322541281738731216101468935785403577478179790191188832046493458" + .into(), + "4850132968055386067280912095292570467587181719320385350609813213372507065742" + .into(), + "4016458733475657342057293585429845015911706822034026494547861057484844319736" + .into(), + "894041043510502707790816962794342134661931379046385946357294445947889380857" + .into(), + ], + vec![ + "6102901509904208647404960172211548023168535043766794923435155807898706549347" + .into(), + "10993930772305308408754972248679389846078570700140268311987303413959565163567" + .into(), + "11304109937008720250639855591630423562437629896442798574433639679310105935745" + .into(), + "2101572929952921976477335632619843501489349436900225186251308254022656908969" + .into(), + "13379549674365217138865497711163371499213584223441782700932894243483907931587" + .into(), + "14594340674649653462364863346385318403203482098904810919462071947226757935441" + .into(), + "10647634642733631076053157841634424396589261258510712678605967658799137793311" + .into(), + "16930068860033006574251855928288208559689461032565007963103701897524112820278" + .into(), + "17170753006961827437085793102001841977757115057767702296590976802337127094191" + .into(), + "1342928489123424058754093123906133488378103161461686346784407392402405815911" + .into(), + "355084123756415397117817901422581736826549282147584490336790542339114994995" + .into(), + "1823092998982212793902589678970070284970565355035702454684653415443223765731" + .into(), + "8048898551230697881244098474076915607341905762324328681953519068200924158237" + .into(), + "20856146965109368880184165603695312280799455722407374791306423939515039038196" + .into(), + ], + vec![ + "3432336669019104452964292940253917813738689308738628946720655243686051453920" + .into(), + "7245438991832359030357502486353272155562833935372540938834365796871211337300" + .into(), + "3373527222057951116939099747150655397463396105930227294848620975346900604136" + .into(), + "12572707687046208161448564696692430877391470466184921096766838283998472321208" + .into(), + "12384937031493229201662431219560608524659573369707802134091983819133706078063" + .into(), + "20462122407152292556380602425948480298757076111535435284512383591498863731385" + .into(), + "6622785585380387259638611864923325325049433660844748518649420260935388464763" + .into(), + "15605049021670133989806310757216368923392900885847286376362426319164543334301" + .into(), + "14810486805240500676478878806350063220126440203863567013665308737654824897393" + .into(), + "15365995118119349988306703047684161019037838200991362610041019844939301169864" + .into(), + "6214064907470154787016516294417895267553707205697453843662274325114029144555" + .into(), + "21692877109550228305351038417910800050989918141485758185602644390604582268196" + .into(), + "7958034635501608074408556743462969750498597833797740641656952760348926890967" + .into(), + "3158783210920420629823137370399691077131051120551658723733852791768119909942" + .into(), + ], + vec![ + "18819671751132769006885776104610704012597692175792895108910904654781356258396" + .into(), + "4790325452934584098003515312147561388538271922609950099121379835100064976005" + .into(), + "20767398895337785696143628393192433940154143348446221959395714109953426025406" + .into(), + "21100955173515864394919601160078443835098475047602620649008862351655083694950" + .into(), + "8821416529246363042674973639302381036248538012124317741611227124444469290957" + .into(), + "11914021995756592924439683950035443074155751236836696847913625157420325157778" + .into(), + "12885168008148287764892654933214399102034605991806678959486007885552095995306" + .into(), + "4425275709248360981167156075250953689813562430283330510520431316578062369047" + .into(), + "18741351324856319300007832572892392651059227679283122747711533395263296376480" + .into(), + "684786537545646459534722549414114801405982388707719975074226812265976115381" + .into(), + "13474309605538568830758681457803561340228343878559353019653388997246494295147" + .into(), + "3099805728977762711509412402176547022850118685798164610570416531465753528610" + .into(), + "5600770441217208920894248156825173605916487562076226390167638135518086562704" + .into(), + "20319458329312269677818220957673767032133429098791178878849925747919552800984" + .into(), + ], + vec![ + "14900034607445803864669093098274289660238359682591825230729114910199159461945" + .into(), + "8290880045417869099036293157381173265982569186829040493570954991061128070937" + .into(), + "8462436298566072387124764625279911648753174589013128335782578822029407672023" + .into(), + "21859976115370951722195695482147975930683297060804395404861331672395447064815" + .into(), + "14955925753372802734448760267566451635799685206346879293698701403481312089613" + .into(), + "4065981601972608953318716423564125873552717375448854762316325928896715511302" + .into(), + "16523330234791629267329601436136683980570251446067965291433875700044665982992" + .into(), + "4829516041540259457263557932051130738530942234255982016822863700315113125159" + .into(), + "2798840870433591285859944008589519120500790120158397573735407833376475300691" + .into(), + "13606709616318037902590277227595176470474803011913892797662975188860939656033" + .into(), + "18594031070203820621139505381680984797252179318940256457018473319622484612714" + .into(), + "301595919185954926511829797241004376609778538475902874115890821742528188064" + .into(), + "13821866899865257052128875138353714301663063129600329885914206051645789155403" + .into(), + "17834817495579398016717972195069792832976969906967797534885124084571666971093" + .into(), + ], + vec![ + "4636638693344568063056245513561087534038777759740617868439601311740450428791" + .into(), + "19461094558355290408553329139619939302588770167904059927666632659470708174566" + .into(), + "21540578068459849172758651661944081878533817564350404794583097681041474789761" + .into(), + "14656354515483866868842463518575118611017107704362419507056354562832024225447" + .into(), + "343560765432454876700403976911389497782754407830340801930660700142029455668" + .into(), + "11849359483183996767521484728570765305101196328390648813094255236259946055317" + .into(), + "19924264286421201319107239943837407504286665549555105416180422159851955997381" + .into(), + "17166797941774367858319514708612731492719223682833034509589752159955652705269" + .into(), + "214236542688014950402766598120744795751883810511956979430464011049570919618" + .into(), + "19396313302865541199110558245182888004201133198902041449761385765703954241755" + .into(), + "12933296125492490585010683271492258153065303286717891025958189212827073719981" + .into(), + "17844270568289843619005627429285364222068221173113885235581641198349558307966" + .into(), + "10272636157097124940050637562161546985351212351240072791897429490752295849402" + .into(), + "5635761628763643716582314870927054669889487541831762994265390120663404976213" + .into(), + ], + vec![ + "1986614715532243876888195532508093896345436225715262830320266933005235142097" + .into(), + "1081989267417492031457620947123272024310181199896785177670601875483149154563" + .into(), + "4271010751492446271792663900865386872716262596127435260947966701647960153580" + .into(), + "9432817034096362918055925335622289410018247097406473925807967486887418646785" + .into(), + "8420506466048434939907866067225441403679080978454172178355845135440301602325" + .into(), + "7476612931957868870953074766426582066042735713430654491467754928300129405389" + .into(), + "7004218213610744415525118134908394594325377744218316765510260162880307132371" + .into(), + "7571768879245095309014348656876595924444469219593531275918644914891061799835" + .into(), + "4094897072683886403820666572344492999293005026807228977160362626188210714131" + .into(), + "16648450944544843693538650431603587114533063135176857105384828552675810607958" + .into(), + "12887042974366913908128212909540148628955501312789089034196573637089727885967" + .into(), + "7707639242815954375609957852547086529799646052590213478924819089112009057667" + .into(), + "8823950326593148986044014943552213420762296615584865468926316064433145020153" + .into(), + "3784973153813546220636380916429273484146041804997715013288159720673291711004" + .into(), + ], + ], + vec![ + vec![ + "1954546571818731885139861264947334230782822161673023234242993080695489129982" + .into(), + "11606713580838194823093847718802359011098299538034455148401855555744041817997" + .into(), + "21778217939341959600865514937973379081571132553754734185669152755967486158807" + .into(), + "11867012199835162777599593543744285374463489953452947402200134749407575327780" + .into(), + "20668200962959535110219664454556867828577715202494491079283962871771719016091" + .into(), + "6796938349934826085352626361055311106987991567096993611616805270698773290279" + .into(), + "15108030096316731537404525399718062561401239877230837132486990179727134215091" + .into(), + "18618583058942935584876943765894457772128324732451762769633954204661267055617" + .into(), + "7446958258820445329937058505234183740111199633995331064868799738609505041620" + .into(), + "11019126795578266009911151080068316016022306110283709712693862589215772062237" + .into(), + "17644961526468872013219663511656737898249108220341985100127433673616476030536" + .into(), + "4959721361611533340499147366149623398780635086479466110353420780375692399477" + .into(), + "18273613083607267201259595982191169247452947601227995394305633285537292365096" + .into(), + "17816466502776842735116945485728134149282831962573761460376746436502757322332" + .into(), + "7013781340485780306773324480395548266877763825891494701746512901494705653158" + .into(), + ], + vec![ + "7793291197073594006386853421213450159077336220644997691715731402410704643042" + .into(), + "3426005025972529257284910903433598760993095858232060658495826014698591260944" + .into(), + "18530832095369567225742997294004908129637537286260217494648426754251364141983" + .into(), + "16489909564793485793960504581924093693287639849995832883358263301067754354184" + .into(), + "15126602449686250365534081130480301443166072957206187316416707568927051456663" + .into(), + "13994979972845996867477162556668014834350043400046615820474382058282820089898" + .into(), + "17892071176071030024436108339592708638442586845389229765280031864359446154887" + .into(), + "7094246519433675669226318744483462997736245331529142696208838903133391196837" + .into(), + "19739600791550679646703071148379836779124671330492407813260108679076122705926" + .into(), + "16577895124793812992345966235533166228538388644746952252700985971901794098608" + .into(), + "2190990407832635064016354900528055762572032133913345251583721394536626731922" + .into(), + "10838969594099257399038118686024400001327577210604256394537002295046250661365" + .into(), + "13742554186879139633322968994905507641568437399912823098239782636831322642395" + .into(), + "10281113667801091149613944447670705624056560574926411753502305328318932013688" + .into(), + "7661208680673970050246952651218127022141152720979640414729369551173790735959" + .into(), + ], + vec![ + "14369836811580283035547554195559038793886958236571577920508487931208924192768" + .into(), + "20603628573396476191496332378884772502350107937108583985752646932901407759112" + .into(), + "19296916296835469264085474516279583782033370007674993417080564950885860980156" + .into(), + "10983867448590555143664432588641225682254935452824608025544914671100236945380" + .into(), + "5670198946055747149234813634846142209283829958947146164536023332201358566553" + .into(), + "4904816432035963931263837796941455228547544800276020247096183162764093041386" + .into(), + "928528370618860212551901809222389226336726628142306562102221490519648216649" + .into(), + "8727385187994811157471310113729025912812882704232858255495018737569420129281" + .into(), + "19909768217191699902186248006262494556099457367519802119733085801884256380544" + .into(), + "10635786582281955931244778086998962127059196955758207056871772748744817883737" + .into(), + "7140512340052162441606422433836236465795273624186668144911701254961330905493" + .into(), + "16598081311443832517669265039250197623929992506944409626575335140315057620768" + .into(), + "2339664320384903939910962081546057089170206846484766939921698239663651706239" + .into(), + "425509623704802982425483674266195224640999670982140442030650575449074971057" + .into(), + "7922384239142329258156226873732902413897900318612725000714450267548570680404" + .into(), + ], + vec![ + "8178140403014386057685967488315772252114289881535707170540858306748328725322" + .into(), + "12689293740944871195190670877158259851710828253354810002997981152414697198513" + .into(), + "13670630626216376948528966598720909229691593992164633421606526176324419533442" + .into(), + "20189490101967313329851160663874367593390331759675962821030507426149184002493" + .into(), + "18209972608416650990264895614325602746017028678399567737887116829945804399280" + .into(), + "20353660437114078502000672122042327871511027701339587880609263231648053792209" + .into(), + "367135858451744056025051491593060073950844607000402056456474235270560576836" + .into(), + "13355850760886700974133527239382497141869096511168824351814359808886023658462" + .into(), + "7206193356029734986150290058613471641978817208643432940709861990432648635433" + .into(), + "7885684183122679587266799938650213096329650494585142531776846669540995068168" + .into(), + "19085115218990181267812208821832153255121894513890241319104580206329327134131" + .into(), + "16305675470941528258170184941405206862153955794946952667798249341324791515500" + .into(), + "15443626257895746936982356461453477742473783071787883800166168668037169561924" + .into(), + "101832047855527584987088220264952346019960111874606050409415217845556024488" + .into(), + "10576438072746903138917852030571732352003417543540340689583487864994727144138" + .into(), + ], + vec![ + "5996861730264922256270512962050361669936822432104376503237345294243995854540" + .into(), + "14877973900502178557219336745361213333854789301797456671201806787562463919326" + .into(), + "14807962843542542498914061591358875654692300327506360640837865566998761281322" + .into(), + "3133673265931719924452668737189159279894652423873815799856403146721022028744" + .into(), + "2314426743898183021393131908284299082806555710249089245305873178073379019830" + .into(), + "15353836455896084897563929713128028858175788390437902641700134508986437653318" + .into(), + "5529981971838869469294842442312128910934708838384001405870007618574232226406" + .into(), + "21863108219378799978996648633069571801923287451100447450849597846874069699478" + .into(), + "6773528450923012634292634195479655092490402578779439394568805920957004744133" + .into(), + "12150245180431051120309675366247495517352377611113958096501103925281912163211" + .into(), + "11142442323884902255425165263749428309435092933107089893872191462936441527962" + .into(), + "7030165611221942623542847326918501014233687676615371955108018311000952911338" + .into(), + "14168907664945894221515023422776939138433274836712427741726190314020662482321" + .into(), + "17973846874050037633502661848899122581090847479984048542694367819419188584711" + .into(), + "6612448947387099268202244798863603173886774177350810153571814261011002084641" + .into(), + ], + vec![ + "20765273984039816245168454930434370234220284726385931011063596091927027011108" + .into(), + "12525833775624943075128880966259784896817535866921245846013552547690890352574" + .into(), + "18082141488353658073882233625595393585704703572017716887747923068948432979709" + .into(), + "12080205172928213829055364897249628749790838461826174687455161036760925324146" + .into(), + "10426041417079669712788047630796875947997831039494087898318197116078426849054" + .into(), + "18333367395418670733687742418586004501344157225223371831515062599898496336393" + .into(), + "10666844346085567030848134043176991777319226647942683934134661343455999941894" + .into(), + "20287202184725945519955164847740850432153598475156573942705745729215691030796" + .into(), + "17942851314410450183054332374663618442963349517519641485232231950262698445043" + .into(), + "17672277011389568686180232934337352157780343745417630591806280730146798908966" + .into(), + "17416106918062278234521335281965623696779795380548750331807903068461573518054" + .into(), + "15034390385078628923681181367678507353936042776187855843799428131823528700439" + .into(), + "15016371809918204032764565101078018512566551812562861502095795302834732872947" + .into(), + "8176229788878503959342848267153225222150151339952249831413185552792554528595" + .into(), + "14202549166569309182319866775579092322766621157492056208423752359103429675445" + .into(), + ], + vec![ + "6628758202046565882882491271332141326521031973243028104017889062740759748530" + .into(), + "20267845326067450413789379016153439637066264448919236391605812427944953078755" + .into(), + "2438946774028723892708023594952994993532105735189593503088246493623252811398" + .into(), + "5327774123437518227973303235331602588839413198088244869937007412210139714640" + .into(), + "16416517260868931624699960600760845305546648577328049221217547593071007584547" + .into(), + "21691457642736313179352706050711464825492028639914839210493298427277168769684" + .into(), + "17369736170805089474636304643282290719726533149056710536120639858255935606857" + .into(), + "19460761623902421883797374762298555710671254062576987287084819867262496770119" + .into(), + "19770570144034267396127078712986043464609355920552337517533085194608634179666" + .into(), + "20904722049832148410244764905538463264520496768863784169293376826852051688706" + .into(), + "6086931305514615639236334006857789100145606465861722355610937306339550862".into(), + "5885641636117295888159072068000551173102681944020015073964039109891861226751" + .into(), + "2197549059218467728366947205357858398035068309306368786367182781869500529089" + .into(), + "18571065033075196607590252486530861399537536653506142751073877421696969841444" + .into(), + "8754088881400442345643534933850221698544089985357770542959718766123813634810" + .into(), + ], + vec![ + "13673463077059539437815915980077307152850526782227553623693374041649724049605" + .into(), + "827897346385894242944663854685785871137033256170575635435612086616249561082" + .into(), + "3256718342276213157296321691616951565542440296040693556287210483669841487973" + .into(), + "18851058760089844863118102177423730353882359970674430675542303019981882750705" + .into(), + "21009037983427297652279654800570889577926790955118786173230430780907667982896" + .into(), + "19370673591737489444054265538393258291592098201577177240415593550679982651270" + .into(), + "9712794467513451079466753095103777002587307366322848872714822737566534970868" + .into(), + "2452976395290300719873209484043914405675637974162415011707015440506646332236" + .into(), + "13967023770779438454858978860214792451127045212657472381516830319420403024355" + .into(), + "18974770134907327058718913691556562240688992993972407935785848010954975834526" + .into(), + "18005343276101020422248769804338953747590444642920980587346957205121649916277" + .into(), + "5364199751574723768938730543610903684616886041855996090009230701935892264768" + .into(), + "16915141432748433783158989990154900013143930156431056052460984382677436665679" + .into(), + "9810457740455050658326943855759399108575402539560713791636000764640385927272" + .into(), + "15711844859260073612371012979328688796642677336582424352155989490394966892994" + .into(), + ], + vec![ + "2523486975208775388230636032695576725855997180931786065064150527465407276212" + .into(), + "12939257203114853364537111886847673104871159136302860798643783368852456402126" + .into(), + "18563343508729873190283517746040347988882591986176993103658794343898711153424" + .into(), + "9139767925154848725661711816797304370425590863714334419686930754659416933343" + .into(), + "15630326979358561783620795846763021145439701823262337294600523076394775855291" + .into(), + "16709031855693747432049197217266634836607267701623669179857743766059308291076" + .into(), + "11081746589259753124653594380015131650915622160245647873370327872758282529429" + .into(), + "10263829532434991046602117509549967441368717347217075372460446855507340910410" + .into(), + "923380097607272775621985864770232207712801803675470117823528453022294342573" + .into(), + "7060362012752050086965449046391069479205357779670943566418766734533864254186" + .into(), + "4238871118210220589598309748597547342336859622832314796736348778105851398663" + .into(), + "17520061366255155846404852213753339526277619564174678951991892080505590972066" + .into(), + "10513625869281904114245087023227471195681135249236672625281292889978751612829" + .into(), + "8435396899666453466602702234562279601174578748121165208762270334814881381944" + .into(), + "8088078454252433245088686773582075061475116946251984942060002979516553775360" + .into(), + ], + vec![ + "2719987334353537600366656327639544587227927648913835976421439609622334518069" + .into(), + "1024019320641379568207362641296952564452568447918936948059464814762366331275" + .into(), + "13889412498086825909291896540147715217051215696137185869382874120375855480535" + .into(), + "16901630530096437974516169843541514517956751790577876352535912471650304576719" + .into(), + "10977539500432168331426791033212854950231005312634634920276038346698892818211" + .into(), + "3226460659346175003896135924099734866664778523605375090881255390738403665617" + .into(), + "9695421696482260394078309163365413177132015345359377667904366174083251929056" + .into(), + "11269053203885423427900382169641426379373759430258387104391167619152696936070" + .into(), + "5178750298399029508026924620615685679610392082191932524033828050686124044833" + .into(), + "12845878982355860044505488053971997443594073436267126888909625977253125523142" + .into(), + "7530981388399357124357431126610695946065175975095472306777796169245607001944" + .into(), + "14058213441446348117716607452759269076404820355922112411973297063005877380478" + .into(), + "825603012201903073682942337154197674883919500964784037574585377691889312422" + .into(), + "10236993586198323123785803013039327931978354285685191819502917433935835639701" + .into(), + "9438970111160688934509448828104684236155844660381402912193342504181825365420" + .into(), + ], + vec![ + "11536612122721678319037657954738688943272460041908847457419482683492719528721" + .into(), + "4048128893355211133472225346691072554088570917135573376272009214966234274059" + .into(), + "17007960555125781716346334106074020327440615260084049810189285619997415816473" + .into(), + "2935744638594795881476272405224480626548498961519135317809162650064622710267" + .into(), + "6384493312721061401062408865799313573644091862395937725107886310975229942194" + .into(), + "10507848115923740198082149097677194763453026968238422206438345199258995348681" + .into(), + "18755782391252715265425541321566381935042481942506333926799033963914433188574" + .into(), + "1622030934879728521636669415221999170954870898240640125007128754133416241951" + .into(), + "19178897048453000979590659690957596324038669245300140765620928017217201486492" + .into(), + "7668471074291870526245483897884601792626426080083647233981123797699500787980" + .into(), + "17022938204221917796509718984925895198444138607270396412440297468084153383727" + .into(), + "10938747411001421463106680010228586254730710894241856448408311145676137003709" + .into(), + "1892143994611253681927160695719312882099525827316460372080933907151205825399" + .into(), + "2626413664304179483436880400231214693597358131317388676910101259191110264005" + .into(), + "10976814250018194880310517382191223839713741375476951941358522267556104616194" + .into(), + ], + vec![ + "3267603976137604608815546917515683877598008503122930381370588099122094818035" + .into(), + "18223585230504941070194267966378685287221743128395324427323638965512681791787" + .into(), + "4055897021092860484143383650117982675609656498724344612791022670810747280835" + .into(), + "18652001434191198724037217430343155151673545332667032591923572773249520166995" + .into(), + "1179210983342192637294098069949454912191992256395734070923896011947222260627" + .into(), + "4403412539347069757548448548289536146089860634393235869990028179479631393017" + .into(), + "18208249577016536190404023195559477353692681610041814639755282640930299265764" + .into(), + "5253459060178003600605009461295776576191151024266914967218417406063967602725" + .into(), + "2110599375707753504956307604156004992055199034205281989577749327575131764193" + .into(), + "16838175205667561737978735977781331049290662487428014461885404122880770700959" + .into(), + "6473428079010461623647807107937762759737150259352991014918746820779470984847" + .into(), + "8337370031139132243770630686523670334344396638842630818638144451253681713442" + .into(), + "5852133535345551978469538570221409138541345120679970583582105205614182914641" + .into(), + "7693908046708935218171096369565374697059710647347392990894755587360287791527" + .into(), + "6754690467826351700887172852005234976131445583530162984365436173814346372178" + .into(), + ], + vec![ + "4362899351088205531982963806583486557201252717995038448293398829823910923472" + .into(), + "11518397041006514564038599401506526387562942749501723393674197214904315107893" + .into(), + "20606341697536003623317613291213380804130123512962185582210369767659416485838" + .into(), + "16897394754877405156789353426985842311670174197348619627467370676352261158652" + .into(), + "11049995264887964858828368499123384474282091734658191426291499678845498016770" + .into(), + "18903841016151023909305743424460730902070062204415137089939274033985227379247" + .into(), + "14501632343069777665672565757138143573066425682965558756989443143462299059377" + .into(), + "11936194426294671569251421865691095594275214629276606276242590758676139955663" + .into(), + "8684782852463301178275527204056308121145836348455196441596832143888384190591" + .into(), + "13840275015334112173632265864573045139112521216777064496416258170300441524371" + .into(), + "8976112149735004651499648151657522459186187854485087924493254571044062238478" + .into(), + "2557541446593153253492913007582627823644717754910327615163106890559828872362" + .into(), + "17214289010670114093415697072867115184169717632618753776383803280726887828982" + .into(), + "9277044732799923560347274951803854995664839245943597020159605756847120319168" + .into(), + "8665104485244718969383349524127237156930430459852710098382428996861193438718" + .into(), + ], + vec![ + "2966017993337369327831105148290320997881600321998609267125699685969931023637" + .into(), + "20703140915572601301330743722461592884427012015286406537986295678495182028439" + .into(), + "12415056396133226456247587270673507158810318408454307171336801889305959276558" + .into(), + "14096884501745579659192341381525893498221351476730589189812251926483574089240" + .into(), + "11380799045102603249740262962085086862746282867943901718244205293076495402152" + .into(), + "11397463999860523006350413477163951990037313029979805067679629038011006323456" + .into(), + "3989254560279764593104713297200294887781503399600736805260233256187721580128" + .into(), + "12670526207690537332382598355517632246478654103765566238996179271593311461311" + .into(), + "3183711571356392622250181639411196710255078687860623143026450897732409025180" + .into(), + "5610846600257417510307213084599977483933519996901968341741797171439650039141" + .into(), + "3280606490416179974005759319341626407586508917823138878220428529454629673364" + .into(), + "1201982324417186063031536229293952219287079604094432487446300310977814955299" + .into(), + "5320694228353869326806779260357179675523748849691821041819016623807887766991" + .into(), + "1117900147109997141002482710095589298468009897854242933473562031850755107739" + .into(), + "3423874914270570048663861326353594445564054387733265522218157141041404209456" + .into(), + ], + vec![ + "15544724812507000325032356684034497915485954044805225704411638706645864153677" + .into(), + "5773122431233952373926318394008190376724448498619094117536291976195311877322" + .into(), + "6101823265492636176451963193766486622777300610881276372298697176417958756135" + .into(), + "15795396300010870823125638802470599845845464744307444307899702370966825169519" + .into(), + "1323789030194931509684647858838729962688902410898850967128356366290242773839" + .into(), + "5751046064881673173633677922158261597917572500845688735304279201507132509829" + .into(), + "1621252171583823353515150633750260236914561977816101468910968069661001399932" + .into(), + "12193773521417435700759146251386454694422997324768376620870510312596267301181" + .into(), + "6582582178277044206368630428791785430498389945338461249283089656681050213384" + .into(), + "14215781677876725356925186332463972498447213260700771466349787162195918816425" + .into(), + "5782842445406193701766362226063474843566378485612132749823957396025995938674" + .into(), + "5452153017648783662501027666999013472879951066118424395570738985848450672673" + .into(), + "54899108049022846277426184613878330780751769989719315816516065174629128493".into(), + "8847320923102214377720246218239804718366581789144394009065472718259977212062" + .into(), + "7818599458828105010909362503034203380908251186730393621503231459166558068065" + .into(), + ], + ], + vec![ + vec![ + "19647061463337916460942375553072101475191437675089764130648797272059706835097" + .into(), + "2987900412319695329324667493933426290750629320482434345012869808788189293747" + .into(), + "14313117549814523542459271158255968194819696107203500245376504355915249564569" + .into(), + "635066671179149779961724809079155342626591882143599249747638714005480456001" + .into(), + "14160366375280976850992425663667859199067402849136919009370279834492741756927" + .into(), + "6973916440684075662378599037972982797550158082488606172483341283171694141353" + .into(), + "407790128607292443078618781455551950270304278197678311107891073846005921099" + .into(), + "1875793830194257638983834574124736838833728874912304344706772047211830871895" + .into(), + "101555677977911034029979807139724697918613026657646487138174278033141465909" + .into(), + "13298961474358064737775518932222238976786587146906206646633234612439936576772" + .into(), + "6675018665213382228528485041578965344759847379196981998842754547093440230230" + .into(), + "5085649234634970209690321129917296688853246686378177913913323311616242468355" + .into(), + "10058141944442728296289308385948277117189357184119821310668675797744136293133" + .into(), + "20711981720256091912789603700019290285604375596717389895155646132584571552203" + .into(), + "20115432152302860531854002084546199214679745925822431241410388037137709465378" + .into(), + "19426738311039094155622173280735935805207149231732138766959497422037163547769" + .into(), + ], + vec![ + "7740589787985988848427674257205602851899971532434369842038308874897481875095" + .into(), + "11072265639503386933704945672016505140436978537584329931993329650203494086219" + .into(), + "6167282302581750408390138662907316184354012779517813053982109604767767995057" + .into(), + "14593714320140781629003483490890381863557111469157054599498274206519671343499" + .into(), + "17959188687624917851017921366866983692604241271917787434145985166811823698158" + .into(), + "10852786592684215415216400376119268936907433212885674472022333115957039052793" + .into(), + "15899441678259173360040901233792251513972059637300348276334545233380063193689" + .into(), + "3640175378514868793712597306483649195648235320181954901691448087453970656158" + .into(), + "19498930515578230344335483600141550927765501643188753803487668144320311818295" + .into(), + "4153883544158745158953668931089517690854504894896391299015592025101035411270" + .into(), + "19024468701496237603291237797335586206588375930028220273546773163298357041151" + .into(), + "7469727364011292433851252680653746774195189525727608179319902706399363717756" + .into(), + "2372143841469285674441303263292066347817168610069150223765733476276718069613" + .into(), + "2516526351266496289030890575774410993157441063594813081137075222758309555822" + .into(), + "20958751338961200084885567700868871946051162714262967700193597995642229058459" + .into(), + "9198209373895042225521605474867845062450002141670817279014351290187429107128" + .into(), + ], + vec![ + "543785608759854122795367682791595958842618445464321379849398930724000250504" + .into(), + "10214529630060513503750965897811894289300014475522844219670830726679857175601" + .into(), + "11576753654045835303746511804171201194442330501175712221979130082457712862265" + .into(), + "6214928611453392028562534794962748192402530967301618657847917468183855957477" + .into(), + "6248903930557664471829331572570457764958370320737816568669654972084840708363" + .into(), + "3521559114442643806761280511561190556015853803605505266866910604261521098953" + .into(), + "14207749404758918058098136067805881181486166837455095244160881284733449919110" + .into(), + "9959485107346230833915817969343930335833003289106263613217998567268111531500" + .into(), + "17002458248120505483758089120825692383088865286608827557586088545674133219848" + .into(), + "9310286746554253001882911152696415122865977191166769045081952245779941262056" + .into(), + "833245639626789987010046903814146615257437312131003591772116076699143834195" + .into(), + "8257332153195419962290907487481324519003765405123021230564312430389478396079" + .into(), + "15127724347963527967475442670935452967842333763417615675896327776913208692165" + .into(), + "15791631600664089304301903868070551535052107017766205491164731100213785544191" + .into(), + "3248589614829341629004884091016822219853816257771914825780122055933452087513" + .into(), + "17215199223989028745431952733663229031216291778213241728328297124270973463797" + .into(), + ], + vec![ + "3857684745108028860654397149812523817069881299315264066597992653650257401551" + .into(), + "4707785116452305555993924679316564589154347100943642537399862884483438576343" + .into(), + "19430682328356065477111453488344441289467658065205729792227680437122893422861" + .into(), + "8005988640968242998051528980068908390083328633663970547195021707967989536508" + .into(), + "1972474227742829959658839187518313253567182690341134307491795498427960575880" + .into(), + "6504813065413498635983080741406156525863657160083764580567056987831449046042" + .into(), + "5823311218891803691266204716746992257279538141703406410574718561307174926795" + .into(), + "9892303067707797586148875186586047934481214044907972144908705198351662761557" + .into(), + "20467423831764780786043971286447965746242601887189594828393353559483921550575" + .into(), + "5337137105639218811346004301122986797373254603744281473362301032791465429184" + .into(), + "2653918865001450389595199059314513619487087198676481143857196098234024054997" + .into(), + "20026090683375374670866007502511215153733777854247692013299401340222837331064" + .into(), + "16088029123818655662676092939046004587731443682967462740467056646463545748825" + .into(), + "9880178757459464201483861677712096813007025248923714154921858424834034903165" + .into(), + "1227858189983101698453184059397045112686910656353893224019532173573557918655" + .into(), + "6965709790321124552058584230424761849742693958580766537537673695015364525547" + .into(), + ], + vec![ + "5275724511243540616354496187333612866929959836267482390875038898914899476257" + .into(), + "697708336385781014957549769788950342363636191998726381071876409126144042559" + .into(), + "7274584324261857876506709208086520820725839679509101845928052585127373751594" + .into(), + "1101072498472320542658663987709974387416478403320298285132888772486638626384" + .into(), + "17063249509595154712877503960715103016753273139274556931196815282616091591377" + .into(), + "20468232842910222775240425801279694589286852891430236774476461428028768660386" + .into(), + "10839957331597622631657614186340514237771754591887181416690281526344756522470" + .into(), + "1833441125433983427564061829081424752522350755265858559398836992598910515884" + .into(), + "18955730579934733484387457001397648556717991843841809299503396866826874046919" + .into(), + "8193171082824386660318148864436464606096456472585333569988889002087311061541" + .into(), + "6800731829409783994258949782115883803874917294598056504156236185152975271613" + .into(), + "4551221506539437319374784319536342657448457365716669137274070321896962382201" + .into(), + "3888700085587860510427705376785182344099574784427861867496328978292244934753" + .into(), + "8086322087822351497126170321910559010882234382816099821864406027930561491554" + .into(), + "13275797274085199955841117698566970822958536692349164078040808025934114965830" + .into(), + "11798506987450083560046523556681776539473600393190500985018551824337777992733" + .into(), + ], + vec![ + "2379081429050928317988088394722736405728459402480510127050576787799908525809" + .into(), + "2158947553437093664557813698796314628878318098916390925037304154608297340081" + .into(), + "11904049624504424229914369023060185670359894203980447724969113153014864088654" + .into(), + "21129595246904679929428089867320350013514202309069019924095527072919847726344" + .into(), + "10893562472341509760161513998095439702562664638408764329166649578524495942254" + .into(), + "14633782125268548143403043594739012390811363821154748677494041549086652426818" + .into(), + "18155420130909256009162482779733306385315875131491307204196352931575522168643" + .into(), + "12073522950076264054413053294532869251854443128423131910399999522064467473027" + .into(), + "1433592116103756425832298952472313408701354429203600638317025112329710147915" + .into(), + "2210939565463298865317782595691956567659826882335372151952428383797077275627" + .into(), + "17035360868359161456401993589512915729326589319922635525934508061308509305732" + .into(), + "21403800287219776827894322644981677663016408317172756418765747341745060868637" + .into(), + "13463317002652268594305080031749651114168039804631789430404782211764311412845" + .into(), + "19738499492349409431828527491123847227085394983018723982858408988105307624104" + .into(), + "2012548380220619299131832783872761872147153098580334235039922730491934764706" + .into(), + "3325274441705326523449614352431988173829782789776117744919906973769657338996" + .into(), + ], + vec![ + "5261611144921901341966147913919865209616390993972727644394713260572315512744" + .into(), + "18987697050242894331980397947115962487019662790026980590641254086717180862945" + .into(), + "8658141027857622941054124779019043605220504649377920644749538450450805414621" + .into(), + "11298428708044619749095290390778425959792777464903586113463716315584533582828" + .into(), + "6730200291399992595132121834599191803078178940321882359439272645986988925939" + .into(), + "16058286461189478903573915480209402516073069688039571574175048313793344696582" + .into(), + "9740895146643188739739241045620497326490653096157416163918867637699590812365" + .into(), + "17328668678982472669285290349933801381460489699965770954259262923597437466085" + .into(), + "21089229510079204828717685354260991995629733636903215847138008238449607565274" + .into(), + "20640971546156771190021485453412235742638585574517108137718546522103899393969" + .into(), + "14758279983387100491873648446401986574422791750180622274744397880182747812100" + .into(), + "1331898546985028774480334813742156878861378216830516346949642945416964272379" + .into(), + "6432287430987511826080726363315893796139259314225964668680871966245781390173" + .into(), + "16771287021606049252082476128446106722127174299597407353702759915141825150750" + .into(), + "8558856604643032676967156921137773032066151674912302830855999926475047747086" + .into(), + "3441849687388033123111488396776112259878496892302987380166582753348946609870" + .into(), + ], + vec![ + "20817116194964519717309108464421257788806753886196720998666047916921548668924" + .into(), + "19363239836951813038374327912605477961457473367759250309818663552575087804364" + .into(), + "8719722538679135055399244869855972116946451760806505569767286592823561841553" + .into(), + "18664054074328463099250618543796241821469021451703648566147509976488389212302" + .into(), + "14668897608285076749626150823646322752663015099871458303607991619920343960884" + .into(), + "8824985320268620533295858061606775496359110158594681923758227994736311199135" + .into(), + "10765520116421824752776648993191019870707037690612646148788741126433863060128" + .into(), + "20754227554163810768271776561488490692278680037121708279136293739447289576147" + .into(), + "8507072847563043340105426835824153184629689984787563844408253684598778757305" + .into(), + "6766982373679017786884251724806484438649942596522690604198707242527640673411" + .into(), + "3038766798814116247860373387571799940341461487105503437312437210868806237693" + .into(), + "16132175023628563044043762398003871532172614031006064729051923614189729264142" + .into(), + "15583173149116838843387513514855791665649616393679968646432984027900294981739" + .into(), + "19200443718712964237956082975258333421930476944060656325774330146577168149713" + .into(), + "14490821043935432280588585568226041328772039440696419883978899443298638245193" + .into(), + "1261830229525183456874822855513761625054204680497477037321364189175040481068" + .into(), + ], + vec![ + "6528746667003363057717101918351735481714469206031070610241614606650021871543" + .into(), + "16147698956945808666133328464174436996026072559773234518262594815923002983587" + .into(), + "9020387669972688980419006674825908656426016085797207362353154226605692909040" + .into(), + "6727316761823910734900206867002954254557029243225097482815337322560175181198" + .into(), + "10066421681146255853671223544720366622786875122426340101570461526567311479729" + .into(), + "18114193263469715956238812322551819970497722041025850638963351240642707536449" + .into(), + "13327552382593937204593701292574430198134175441510741573417228229955049364251" + .into(), + "2372604211171385703747757710474646305749482500024237878826421281702483230858" + .into(), + "13257727745849193909326785093877285673934675536283265665870765530981203548766" + .into(), + "6028193081122651452411463574343231811776375151328081689399842891316362242212" + .into(), + "8508301356193721985012355411615100178521599009635936162890863637274261948848" + .into(), + "19464559199695905284994131173285166577427724356611906328878634139911049316349" + .into(), + "11574946347736941315258330071986157639717219704847732435648573723449097294965" + .into(), + "14316018291870434740761571976364226850140038868497601980741769481529398163257" + .into(), + "10918196690875147279977362872452345319770767457845834002916792583407449275430" + .into(), + "8977373069224380198540140180493576791843577554452269707469880849832228035023" + .into(), + ], + vec![ + "748498829648879147053737200607377785638767247375633990031472844537260809404" + .into(), + "7873158704115081877804196477528352958470140833786962209738121862287852609943" + .into(), + "8630532424574483719830065132415752445222218233997041715460638881404278125797" + .into(), + "8690582614704926771670051368117061261335922283383440650770249469863376973533" + .into(), + "11095347717221488007795836937657301037546485308926406743891578760518489637433" + .into(), + "14135401679286508502504277387212121656373093920904597158275723422439876100612" + .into(), + "14738090907871182095556666808390406734966899260337679732930591106508814238308" + .into(), + "69949271807030541733792162811562320986072778465870031251424993196153906266".into(), + "1261108319753649612663311207745706802298135850234573661502446278242936235395" + .into(), + "16817683438765699400477322528948826720336276287491287100775393652707943792575" + .into(), + "18841362612982270174762542916999427955157117780377439797570032391179795654286" + .into(), + "21870116979686159000730008975387147484906370787640570497473602061164852395071" + .into(), + "2765949947644452455039725847864010340741814376903283748968022076584286340602" + .into(), + "17243705140322781483942034937718263695017457618400778609034996357553437986248" + .into(), + "13677914966377093417490296499705767815775553283213762175449591670735007344873" + .into(), + "13297897273167025228957171745153893110275891317806768533464851402665750442708" + .into(), + ], + vec![ + "13343269561671098171091946421541340634645677702710756455899883309946421878045" + .into(), + "14317554923995329326292532110843156058636017277221221405605647959782965284991" + .into(), + "2449835610256525707119222686954432076774548565002604197859382557987062142872" + .into(), + "17311927259294224200654531686487034697399582221230204382526629700762752029323" + .into(), + "15886029754147081563564215095016637219622964863827251334461319377673888336370" + .into(), + "12975391569205596000382467418571211360327385366404855968892273321920864753986" + .into(), + "5192224731376769981697271181929966876988577937843948018413420047649317448463" + .into(), + "10676192139479409715075805869252336543157972214291179434959380291895052573000" + .into(), + "11177450837775344504988539319102121281143354970746599512770721409890402968920" + .into(), + "4593200667847399069176143966880767249193687931869738276411303724780636851859" + .into(), + "11038090380551563944847929106606106685586830480239388947878234434263502089528" + .into(), + "3953526418885419728011595573117200571065709475826662733812952860173033412620" + .into(), + "11423581837569206292763368836201420979900393158634684009052097987935130296343" + .into(), + "20821758092880168608657749212670937227806187953778513378055795779476865339010" + .into(), + "18497750301637542715216545677959957759969933594321504330433834545748130561538" + .into(), + "12908315310864070359072899712184126229744818024807969170422172983759986468742" + .into(), + ], + vec![ + "18124554128224712379655197019948407579501104121202515283344405665022477997811" + .into(), + "20982975342803604005070898815103511622812678185245827078739170834137855132820" + .into(), + "269825514811016046965635325890713556615518696022373524499024558861784638050" + .into(), + "3147172016143608266119085281262979524079358702373693860744797997889998689295" + .into(), + "14386832245166477008833710911810567249931220515383598373556096298357174022469" + .into(), + "4556487278328022691163443795787718624849832853076824895328263286768388362379" + .into(), + "12261472135716169178595791281788338424856082203277018628926152780653238868197" + .into(), + "3899423277681311798156637809536718065846612626667684730473026778811334914007" + .into(), + "19506309861341587023369919042973949592579256277585657370274971571040135953685" + .into(), + "1364959409282923580897524375843789492029158451437094417717346158650761726050" + .into(), + "16825446178437335546349323854223244861262257417842514939476542139147191650927" + .into(), + "8507209116997169365742612629060440573797814488088100151461758543065101868641" + .into(), + "15267004752470933248572062004321128218304784520473623806984809921883550707694" + .into(), + "676031704648473427598859615894926588607941948575683685792835248653139785855" + .into(), + "5619669402121492986528563034254744932241765329516105050276049374453441613893" + .into(), + "14704798323824402102639327448519792804756861685698966598005087155126928897024" + .into(), + ], + vec![ + "16320067378138810368504584396122999292945236808095532790918287639367557973453" + .into(), + "8146733655224190459272793912328710535983522769849572460217349885461291275505" + .into(), + "1038180418056776651442944028459510265058633383281360520702142043667403503844" + .into(), + "19104250152149288692194160087229108962380481983770051876357439473931889382526" + .into(), + "7003760916474780870091321276888809099928758016210575511830123521067523691017" + .into(), + "10460150809039904156668983747345347198316841366879181914914418086579576664491" + .into(), + "15677112907432790716289265075133862681087874169637399306212310599581157175963" + .into(), + "15326287388823547786897864243344800490244989953594543352512215080754525987008" + .into(), + "3009920542142872962638960374061083879344081833005888204328736035225746795718" + .into(), + "1804978488347291728619316877980070589260679228487014904460247370976082660690" + .into(), + "1328483773995482788116589592947585572244503408960547493086897090179230375909" + .into(), + "5730439196427856076422854580234519707227885379777920110477133774925338997125" + .into(), + "12981431367443547352573507131765244291012615436617972351790438163822109185806" + .into(), + "4832711978673748239567077367987729540684018769513731999388791063624971084279" + .into(), + "19167638139894327951096186708600927728591679782746822664161578344690189946483" + .into(), + "9333793061773227893961520586484148770892826436173136355616167263506645189532" + .into(), + ], + vec![ + "21458443518750111068075382716496819469049134888053123021475459386077573760694" + .into(), + "10205061553685164402371459751106832224694007401400200656551443744478399832956" + .into(), + "9830442925198991171494436686328858756494894392913894165312258843542937207416" + .into(), + "13609869649628867442619044498926584416410429910199812031508542862339177409898" + .into(), + "842857359216662427573900948838829890161571314532391785590044951074431433210" + .into(), + "6704851129269714864143856350805682503777715960622547687053167421313207852468" + .into(), + "20114446898395957281817578351485444375476540075666338352767091837280210668931" + .into(), + "6130491715603374999851365684496448519606340852139128448851532580625582546602" + .into(), + "15813600594451539718733724931622603275717510629305297903420212550967482486778" + .into(), + "21327142130781371825633810115678136219928778056926678460292750153897861437357" + .into(), + "4961568602907543961625596532526706517274095072299784820035412496941108876522" + .into(), + "9960714813540172203971946479714057278358565379915043327324100653488017320531" + .into(), + "19766028424299726292403979387148081559608033800073407130824876437622345769610" + .into(), + "19128679427621049663909949398415698465159247423858348746959133844645715231748" + .into(), + "5570166864868188450021144960131468276106515498742461735810689530781856406802" + .into(), + "11574972995586621052272541749980259251569951388173301707052886832340902170154" + .into(), + ], + vec![ + "20877646438494519923058752260065237612204466401313282232221152388173388627982" + .into(), + "5249150519585813956946898091205522450918428396100844955321690157312140444303" + .into(), + "2017741632554727420098342601911590665808744692822556685407780092354922864904" + .into(), + "1195760854074467363227832424961613965990883686618742557387108941759791735821" + .into(), + "11466582138640916980683611003811079018804741425452823176665968956853901549307" + .into(), + "10985903304141344987201754580174851046824447026961915755527591886735857840658" + .into(), + "2130569969210610976943124127703960718576010294778156297713757734434872381369" + .into(), + "10738808247531379378397673739325665568136689079862172683794674460448121540040" + .into(), + "20614646033198180892625991863201166456931543657809805208583071176938057085966" + .into(), + "565997125213498936861304726982380864240733960104507001725677609359585569840" + .into(), + "10632097546602816816944445466416073366486654512740953706603375905768461201631" + .into(), + "12929362833112356946255271584627031609627907912912902502403907291451582319157" + .into(), + "1034235212357952436868793031480652544314617407845212103021627643626485031876" + .into(), + "19390762319422155950976700977771604452581304443563816543281343170335005291057" + .into(), + "10630153844633439282958810722979033212891017579520387012386923832074337305798" + .into(), + "14190876500956732147461925775340768352695586121304109940717530850819588911999" + .into(), + ], + vec![ + "18722506055380266423054060346625995391170752586033471909467419883841861306716" + .into(), + "3056819986793075694786756176651004538794778835114033048299678024064951583754" + .into(), + "4672570536584218848208255703572454924953635491594309524306431682544106754221" + .into(), + "17351556719883029551473146382008643929874047263147825865359162665615894766393" + .into(), + "15361589507494181649833267508353254535596250253216356073840778948791727807159" + .into(), + "13693471199005207103868448237123737586403755421081623632730820742927025187060" + .into(), + "17755277847125531485682000612777686738631414648862078678723432159826928724703" + .into(), + "5078737090654746516628738054730387217943533822956354885634428155918832329055" + .into(), + "12147601749747781924069337935019031145159705806951967042421913575214356549816" + .into(), + "14365231440612787726412058658929032228572314258997026523542183583106877612565" + .into(), + "7278303658563994843919131396912585917500535615791945995381401546353032136647" + .into(), + "16203755920169126984249498560164803107868240707492521482933949021054510520315" + .into(), + "6791925607504018751125155518211487306271141824074365658905258365090537532910" + .into(), + "13823494237593720607868138054959291887740146822262268248432322209124930846096" + .into(), + "11009501160902109690977091445438703229756969339078969536565574715162502634351" + .into(), + "14720462490975063947234490477382491041961626472580003583159938559677559185952" + .into(), + ], + ], + vec![ + vec![ + "11497693837059016825308731789443585196852778517742143582474723527597064448312" + .into(), + "9160854578263429171202421862962594026987177464192712717562131193605088890171" + .into(), + "16140003334191084124451468943070902129052879491017160345910048022420147165440" + .into(), + "13954253824852759534031493316905992731351625718124698909948022659536770029356" + .into(), + "21692459647877833789326815072729212414846887919903018341690717828718320112005" + .into(), + "9941936267230985844518782624440910125063679135232844826673261884947459743883" + .into(), + "13764916706054812213341909699290503443927147550756936312875016380348026052252" + .into(), + "19102599208524798070012402067365820884265931087114568811319734727534891174260" + .into(), + "2317229314815846955024814528087757341110607641360330608288042339421595574836" + .into(), + "18416483069534816725178879766589878658686265350707575814092642317380006218736" + .into(), + "1008780931278741447191637805167409477999443010661365677836100477728938308997" + .into(), + "2545090804346934163783014162536858416885322260022963915511642447380970940906" + .into(), + "20063061892576784746234714844937263854658165123147516223299128773175198821424" + .into(), + "10515454963476061878165006305843100325941655508909556343534889232612007413255" + .into(), + "14666046876279128708964624720946075308028756066224010008571298456055047416803" + .into(), + "1019066804447509488848959767263827580870207313924599851872882869076383737080" + .into(), + "12069305948801710705684828347680772060569105557945593846885134778987150642368" + .into(), + ], + vec![ + "8621808318996908879998215444507382011199442894883948814246574848625262495021" + .into(), + "2880093917191730817222149621749122107421670637456732204857760380124742164752" + .into(), + "6542220672426887509675431131054437027921301676718161645952700116082427886835" + .into(), + "5244163576308284656156828799646671881031097312780189786429450879353224489935" + .into(), + "6473177356818363685990488940547034234726012773074061935352613261396549623686" + .into(), + "21820960109882302233412313690073759484098924860992104203363318963429862834269" + .into(), + "776485012941102583811996326817291151707768276005464663608504425462324969189" + .into(), + "16008040671461692654565857317129301282959438973184407738728929267807682783872" + .into(), + "11432621469217532329151110587073289606303873577842245393046608426744660231625" + .into(), + "16529428848681190297024382986484471912959470051547049854507168832330777582917" + .into(), + "21022141832384945339899318910964518260971220396886777286264903115370862218195" + .into(), + "14654032557013894559632377088090851151746560959506805696507500157500224672350" + .into(), + "21354688910115519251326230418165921377662159943573060390104844506964336391116" + .into(), + "7752619774088278019865202458496784783724532278543560344508935599686049023521" + .into(), + "9970654674664205783578937362441466301698784253920856829108173973533602978434" + .into(), + "8766781005151378907464491478911534398597711022218593484510275842885145890118" + .into(), + "427418044823104424459481289119034112585817787122063948281766019090147258009" + .into(), + ], + vec![ + "20178400825368362428846659280983736786989246199446877730902913555280765010239" + .into(), + "20090950791842994139452610863206653079874108855048809192426038290381481405709" + .into(), + "20312256534186716461922298801781816516042265527822903054862895650988285562148" + .into(), + "4909922693172591670749898596465752034935631291352031623980892388245203503556" + .into(), + "14711873682305388680005649678171045910409841883308457847530167875811246495050" + .into(), + "15958770417311022337941451962731041577578914593218560089308342731268531712920" + .into(), + "14344571866504726216497643454572836820480317972842515429281824926793932325980" + .into(), + "1769397319845168475613819781273574132774109508454697099778007579313922258588" + .into(), + "258587389395146525547707547116022862705614858555176588203787762094788370646" + .into(), + "7022463667655232121918607020344762877802128330181623732816227821885112788637" + .into(), + "19845060031908398041047500648731073426448577406797522741285587091948148114644" + .into(), + "8929070527835967284189505459276001504282186650781989399671939877306459603924" + .into(), + "3547034362934554702033015610070536051926221082690581599667998618549114761320" + .into(), + "4305372853651357563855521637099663044320680997112932657745973067738472275743" + .into(), + "12214176886158725776791687535468106829515395407992835497927312162638766770078" + .into(), + "11706938131011539306398383828922578377803816015869195385220014980091795489607" + .into(), + "11651169608106469169652973769680487604343355235752770879131657116772958352610" + .into(), + ], + vec![ + "13534129170664288430416028489502009030456779206590118389136628926201783734674" + .into(), + "17992046595622622161177230469344714863802535922717313714552838880957499215981" + .into(), + "10357743233228120740298650287569640748031608153491097653220067758242532130547" + .into(), + "14517928487614900276301089165901434951535050275402697067248151083596639118376" + .into(), + "8209182069608252268840455827520737704804441580266130568152978886513947166844" + .into(), + "7640073974915695432060688995568585354936052110685953897202450273254409304465" + .into(), + "6665784939677502535233010310165049782941185917893655988878440622136707103599" + .into(), + "5556533518127592657586282330044128807869230494205809390140992474026365014562" + .into(), + "408852126690043032125614061393097734033642541042497038832349288207856020928" + .into(), + "12457107183372565990532088400464843754189254530767241274690121422398153743230" + .into(), + "13376606774696045471509234199808902800235186345177397461276986609303176540711" + .into(), + "5984750790245793264714000637029537436625478804219132653560918517073545131213" + .into(), + "17088785934598552415567637681327922806463774512289686863420148678865116179843" + .into(), + "20571346584413672249720180719864302959756306192295084730012069713189235458689" + .into(), + "10563908856437897624492808163035753573292724651291788140893440219493498317425" + .into(), + "8196914932493081300314276778567572383087801740102758457948867897900124032622" + .into(), + "15351358433647454670571026954149706648025357095082471550411625465102412004635" + .into(), + ], + vec![ + "4691549575923073479244290910433477874429633596189924034857922132851997509067" + .into(), + "3824402350445187488499181297883462500055483166807595223590583390670577007868" + .into(), + "3079709149206454455742256725666572935951085219797765997598190006202700553625" + .into(), + "2933435916857570611285367519434342227981452027299670163614250683347221879812" + .into(), + "11563256620627039928684054332499213986056102448749960304814905960272568400553" + .into(), + "823982800920919472728462051408065816783743200056445901787723684868401502814" + .into(), + "1426387027752743868839120378047531362360888408160839303820390910661721602405" + .into(), + "5731997056751920185589748689260608921390513807500493745181552863591316000292" + .into(), + "7720993839832885597994588400736035173902383878486875722645777844101744411689" + .into(), + "9002464001048295091481045293483875664362475441902824699977741340020558338559" + .into(), + "14092406920820325227424145554147519809410886190773484884502907926085878880530" + .into(), + "17941121573263204671416572834368313711647830547916231506552438869447449377380" + .into(), + "14129082317750218835304685520412323779932243077309313102580200435673568399019" + .into(), + "12243264707350346605364305720479795822428336866265087370053925276897423448873" + .into(), + "7359586883838006703688461248312304702229090925413466255532354254039350309505" + .into(), + "3965462104895971860844762333128974051506845133345812196538391893357098668635" + .into(), + "20413404046776512481224629835753522701244606906272947012890416028042446470875" + .into(), + ], + vec![ + "14776939713848231582251698583594764736648030077584017202377553055729297756934" + .into(), + "11733430377692682072369036122380634083002891720790435745639760545087660184574" + .into(), + "11292503903510690808481905838273174776860675950299588217800551517281190922743" + .into(), + "11420684871520848382159461165975179232780830769432469479502922603701278643559" + .into(), + "508745459215920279034800716428719752357838311731203417677851933253804443780" + .into(), + "13406614833603475717176189537057020401687589054028958458013676908546295198427" + .into(), + "139498778461763532268768837474164379463641791532876816780917343085192436149" + .into(), + "21267978943610728192699568453293742013765519272282009809779840168468272374983" + .into(), + "13427075011225899058346282386775414644140701782157119231274761492276104178003" + .into(), + "19408556542747941554117161987423225644412569826994655839704848575188694207985" + .into(), + "4344748623903456736892311801593408666061687792073497811421388122550378864729" + .into(), + "5249164170024197498338412206444867159273343488553715674803882832943638673489" + .into(), + "2429321160057297680775462145791389342017255648014188548442207641044604477112" + .into(), + "17633034583356484958669009250049760097861376611937917426871239574846566954163" + .into(), + "8589358080626653532622087734866363152181033731195407703922685134228472217961" + .into(), + "169706674245923892154983046053808641575955705892301306744269839968148939837" + .into(), + "6930563539197568226045059034503291611681517204525149696500944995384070682620" + .into(), + ], + vec![ + "7715180204730071272045176332163669182843407084675929323110162515552729145466" + .into(), + "18820951479360410886797033460215573843065563410033088307349452904607006418510" + .into(), + "13004571734426490172877737218587476803131320809388224624418159739666018276230" + .into(), + "4221983194911935567520445796137770165826124860682996668925096525627918503987" + .into(), + "4179458328609322852016864400883248432169808675570811233121213062838243996150" + .into(), + "1974895668668582620251979849937803690939422905200315076249511583941627363923" + .into(), + "6163089641798041487723906004413611225955533949531028903999813030048150618255" + .into(), + "20798314848405704563578428763355242237484138146926032039199072579614133708498" + .into(), + "7390763848541838674951447729994978703104555741288179669331719960925737939679" + .into(), + "17595709281213749734228944927123024058411855966868873405155429088809356857614" + .into(), + "2441295378821425129112278178696474169691086102217838124236465522719015824771" + .into(), + "17849999656709233176331949642982487670074039937621948427273232063967495006615" + .into(), + "5474039350730649299741439140946492457291098791158740216537321189511814004320" + .into(), + "18047496680368319496003204418900793303422432417949101604087210129655489944730" + .into(), + "13429638037186326961998474643589136448656547552567706898682870898125573018456" + .into(), + "4946359485751570678621380009145072972898871622865400112232372288432448523287" + .into(), + "3957822358540559545601980592836183988567881320017369675759228588453032530432" + .into(), + ], + vec![ + "2400564715392273231728553914090327718988368277691346375047322875638060719294" + .into(), + "11894303179337833272080120828007835602120013500741388580434416759507002392538" + .into(), + "855198138664096487124576201178969627556714572486300747020353650684651888948" + .into(), + "16178438790960495615141009256088177242852822581784859562011828222911238142141" + .into(), + "10137977256085689928230181030370096331013301569857962720687404778057694535741" + .into(), + "20515298296047282701750471139118794292164841708209011112551973440387123890479" + .into(), + "7578927989884657210580284649728673292510875058413984297956940891614841867974" + .into(), + "15739538064110791981043195085211350004676897766248503311173839899207608217532" + .into(), + "8509314140684268376822128791119356380924264099968476717867240237437611156406" + .into(), + "17940958334079989608306082569922896755033794405963583994940471979845228834401" + .into(), + "6805144082112931458671099527607595264417843202533041916539891329055761729898" + .into(), + "17799985834198911964923035884156388240555414131807336706586014102549806733421" + .into(), + "1321093977038377443723007121319910985126631474786194145179087170534580192225" + .into(), + "7212420993868874204016591911998823569295893131341249414736874789036080080431" + .into(), + "16508732240056273863067547854850721821696261984346034975271352570374366672941" + .into(), + "19365952558655609049762470933563228578883041309045062714419724629518231309330" + .into(), + "7130642306151081144305696424018041594798737863203511582301667374235931034428" + .into(), + ], + vec![ + "15515575256858646879456258778282520667217724217234033369846512585375869363745" + .into(), + "9059306962111196078284859952597377729932287486280210246946890053176853282710" + .into(), + "4280792737479805409403664514498090636723785279466801897971750554236293020515" + .into(), + "597858655089478477234738420519139785711327216887739704481840585427214898574" + .into(), + "12083197860096972935386715826619665719725493525449003092099143765304129114681" + .into(), + "12386885105651539176174534724546204378869340659228252756723599802198672140462" + .into(), + "16104345036536728728283892631581380860380214418375736872156768647788333934514" + .into(), + "1354879242449295308208627398119082438254972230604954870747255418847391619159" + .into(), + "4337243085646703769896498772355566596731636687256987520433742534413482763248" + .into(), + "21848361732679483806572599977716282165817086425830260890883893707113846583427" + .into(), + "19219603360817893268138318807528436572901663409914252265449565639651815377666" + .into(), + "8493170221045553556330983879086435859112709446579471993956965243640551891229" + .into(), + "14527043796091130553415865581526659472541220106744153300268446542314624889828" + .into(), + "7937111786465239058321924657743649234315319708955946415168847089269341907969" + .into(), + "6983241618969891267833664988834562719925952220932975709556427480787427509845" + .into(), + "18465592203176453842524453315376851907098534348906659566313290009668304127477" + .into(), + "12577134888425026368283611608059363450080978991251364658919457233283032077618" + .into(), + ], + vec![ + "6518118228984271075704215109336988269949556606855830580887066004852197959585" + .into(), + "10874313757780612756454383662805810655295466490423990025119534270332368723291" + .into(), + "20050459982999023948350579488832955163413000705527333267976511300912322921717" + .into(), + "21437528840108163281775491180120464654045652656938712316426352947376535924261" + .into(), + "15993399358508475124653838111269121783855992741631266590611036066175654211556" + .into(), + "10666318855988978766724392469406816805194254878125837815249460550109965279109" + .into(), + "3728246313656508848829733684629154197051824118611903835393981369401120614690" + .into(), + "15672779156766891637063280303644284349935441934769848438404666835306555334745" + .into(), + "20385059098581842810391414884461531594818480948222590493458925664185735049630" + .into(), + "17350905999329080753256083745369335770368978719306867594625495391694963226620" + .into(), + "6853271030225637900654747228083647747830481256853322607732231926920221636638" + .into(), + "16945013335725553221697952514726788415224349981949301561114619373330552300437" + .into(), + "13958698773337419850196158271441769489630950623502500296722238460406723080346" + .into(), + "10720560039907520317176701532328409797933913846226344831945799921818292744504" + .into(), + "21881661898012375513399335499754096939362343783241122010305516740752509759219" + .into(), + "8000150875389840177411621828177856485533123076967778453164076865957788973133" + .into(), + "12006864256015504014085835403867962462497397391751885107034795661085483190829" + .into(), + ], + vec![ + "11134943591559521791276826061536197135026619606969540608969613264694780202214" + .into(), + "10460719249321273180939167776515335033037028258913526408809327052344611741158" + .into(), + "15622824325483665989612723680117847052463187830077568039968213833454442303285" + .into(), + "15410746446975895232352042328675463711076427815985485297730903996168105249910" + .into(), + "2891625203408623652388062275887825901052275992286085168525244670232353662516" + .into(), + "13945346776936592111645435552497641082181197055210791224507452108313960355724" + .into(), + "9948029837520576958543862437428843878940881432043095435233721163595331057646" + .into(), + "19462884070334019417166507501239463855294968830363882698967905077860898932836" + .into(), + "5520136928844106525731832126160315598282989291025512087174203871698688956372" + .into(), + "2261360495098633227748653797124151186175710067614537829940284371045336769476" + .into(), + "18416255543912854662465760974639117371049579168655995592136361451572699752296" + .into(), + "11463536517819692876416279640167390882172379596277754722588804736317693209031" + .into(), + "14766312573746115666714530391277822821010415216258152152785909105008723593246" + .into(), + "15974413313983607427146441647805917978765835122483385258268287590594890695726" + .into(), + "7395768998784322986017026062700445312131764953109410144512127957624588026520" + .into(), + "4984362060666297962621166548113407854009922708767986933509108968930963325593" + .into(), + "7856069925664789206382562869453172139307214363990979700110954142509543954720" + .into(), + ], + vec![ + "13392340056392075670742743235567931160245313927053798436913893965097578743589" + .into(), + "14474853581934896987119860016624045909083123926480713073931600741515836999440" + .into(), + "10281094117655562522718238098582121237113987849950975777935895552307296780258" + .into(), + "15651874975250045926713763993802349605521485743295256479167713866101959393837" + .into(), + "18837584337473843738351569365992574544592556787094567312567026738414350732486" + .into(), + "5422285873429437751528536004788420694267218180077508101272917323525729776286" + .into(), + "14483434861134394018133707852914799521209887335043004813883706597879855694501" + .into(), + "20781651897373919207655051244184479648667009194959049646213659882342077548670" + .into(), + "13257874746816536319517386553976680827294792028033921278407244416529098958298" + .into(), + "11687595443717328453023567060513955787814475781716013805208754435947875015986" + .into(), + "18166734702075655056906373297658555632769934998914813942424547912757194661408" + .into(), + "21844245024899402789239043296889438934444349492098488274411135961103932824086" + .into(), + "11576523423366505825808301659798885009429028155669986846400153307591026628871" + .into(), + "1148929907457849288972409801053032039492913919668345502756020163532587226569" + .into(), + "9833563661199700341560575887921549871699630430379479441644037429312353663854" + .into(), + "3530072023449326955819177530541286351102246058096342120743109127661619847487" + .into(), + "9578572618820421025088920868463234434017082077820664789170518853528041438058" + .into(), + ], + vec![ + "7903274455513569732248555355877155817202990193424390098928646605922794954894" + .into(), + "15657165496965632442135478212817895834512397576120080820666617370995124184076" + .into(), + "20874620177603797416855214546759257732117862468392283423272496519554421621175" + .into(), + "15225141515575386127960844403303707527145779098407107474807381929305493685913" + .into(), + "18969726739742369826201131743053235807095357084892793088520738053078283905792" + .into(), + "2273322388759602026766688787958421446004694547879317377854849512027808056744" + .into(), + "5432511453110863684214390264939773007164867954188379597097897639266721931777" + .into(), + "16573736113133010502307046980851924227669732687124639246608245168226674694938" + .into(), + "14442377229880210285366376088368281053151209045775399428755468734230536622166" + .into(), + "11507669956883827819118311484641373285663729813488745641363568933392081679659" + .into(), + "17839532429606263422276008800408286253970022114288361307186094837217135904945" + .into(), + "4014265539378515041529068347957642340321904838486974789543291691351911347680" + .into(), + "13003411109936526663340169024379119873659186776343250211166084846025445488231" + .into(), + "15306462549196010658398578543921194922332897913051564489815921371400376655492" + .into(), + "15374920278441335059913374044144421033417626851032526544458034333565246831914" + .into(), + "2323707387573936061377018731706151895551673463355017998838984100857163359959" + .into(), + "8763825614293136074070194100490720958859071057754803255698169463529260604989" + .into(), + ], + vec![ + "6460078619951263360498950096593765796985616066844041272594234887640757495947" + .into(), + "7787749571541900537576366919010287855513576825542236245962970793208463720908" + .into(), + "14155132064389382746286911268123763500605059883946776474093143918456417640061" + .into(), + "5273412399684398435707041325417233001212071630727434066521150499193222197699" + .into(), + "21782771343726562685905794523874393783448988772056027768682759741108535654588" + .into(), + "2051311409953010480673263657665054154914165583084681916263406447692393737285" + .into(), + "11875661765858469891704167021983258181748059479218144774825851054326067416765" + .into(), + "21068403881106541076710977164706790160208140346094887767675202443752560686192" + .into(), + "15758340092420689120259589661569467106735378390624556577895360824207644286190" + .into(), + "6348044865909997285104441438862139025250301814988835887095459638724790173542" + .into(), + "17932524652786058307278475190078972097828770914834668973535335630530452917847" + .into(), + "3228816608788245618072625224844244257443056161113290604232896419878090903926" + .into(), + "9551656383427589703749567396517141384933708835078892692797520811547619069163" + .into(), + "9020946637763713728445703977273148745690000656147983844173946393849341364502" + .into(), + "2943971707938849936044214925657955353752398302351355306608002652427585494465" + .into(), + "13685248050363412502458409958081650237171619758299111626717716567774759779438" + .into(), + "2868302441860272746035818268943730589208611466934920188905195434962368539498" + .into(), + ], + vec![ + "12942754961762450702258615422302669970585456811961698112554744196254280397924" + .into(), + "7774153016753223166594232439290374237307352882928845881491063153885238423849" + .into(), + "6049199094489456460150873785624904803705527267070980629871787636829670320696" + .into(), + "20845774618508072345571200270048422316984213457783733272821856328966613453129" + .into(), + "893781271752145245996199566953390937322128434854461459523685354415462146991" + .into(), + "18253521160421279022112163574719081086152283091051794425756555232523003389714" + .into(), + "5830356839546054218884630677325272050228211705054095093735137780865196640272" + .into(), + "465724850238100756077164119125411924005501292693566847128509233889037994810" + .into(), + "16128424014911176575708362980592727342883849823936273728991741993116151256846" + .into(), + "16294469866182032082852110477156803007474009685855833013024070339538767961172" + .into(), + "16065302076211391028679793764673039756452069543133653373534107626161947643947" + .into(), + "16884841668405833752583472630774457863811531316950980947099417366816649918715" + .into(), + "6491186733352076588617073683591626846805330965399318749556699217006305107353" + .into(), + "2905363039904017404089840194168764066155916074074586587258377722869451022895" + .into(), + "12278525316798802696433043227895855967349302508482435733539545800309845622462" + .into(), + "17314019937917464307399357201462393625411723762603710024519926241513301595993" + .into(), + "17575148837153403621130441476206668397295797814863081080474688530411862819003" + .into(), + ], + vec![ + "15471095648400918007606825575051718247465842587885842548618709676930106301461" + .into(), + "12384318072593682092503177281279533170809948746241154089695346932340260672911" + .into(), + "485123300957579906724434519828279717066415303115967428444773045074341666269" + .into(), + "20783388705359037084238123327815399486599342641041862648140815340928809400500" + .into(), + "782158089782171747510228803754257660300288627956397010472288943774913698434" + .into(), + "3478639244366860518133129066736643188173227661380443503605881541719237123785" + .into(), + "8117881660412001220282982503758841397343065193097821242261528946890929063115" + .into(), + "35058721234476129650326366428341402515138917944396335645144933468879076532".into(), + "16397938679668108498255642177027044558510343548921376375827648132906803583443" + .into(), + "2433453154751221667718635947799318194191827076977739122814782387249355591958" + .into(), + "10029257080729083671923349509739320383181145104464214096029001591053185820564" + .into(), + "10656149073844062963681322014553075060763965763326275398946747079560564035850" + .into(), + "16593959744982924702318121631183966060100893770061806448540495161947421822987" + .into(), + "2068414540408163070577553011598011878961687148891918803076043034635892035594" + .into(), + "7626934390068570344424013149524663454707020514194937543157326823804903603467" + .into(), + "4554638618925430597188146675680715151198309496396245054063619103455584547039" + .into(), + "13496396626587678250552586498039471308586222626132206600246125554024514007655" + .into(), + ], + vec![ + "9386575758165199373292193827891788760863572720909068403298778509513288095168" + .into(), + "7915179166050735066974287284338956896350001911949145779842510041370266325973" + .into(), + "8167168351770465939827858686491835211463722244546741051891842055610292036496" + .into(), + "2984855641991264563125921669666111398496570671860076360618117725463255935294" + .into(), + "13345515131307851631101763161069239153529744919878592460694942948582652858318" + .into(), + "1683899377816547722982571372970082226593844001913781056647398610840773630210" + .into(), + "18082738493041031740109434905429676829872619916356658605260447196047710914008" + .into(), + "3093627219579176143734483600677215286836843343902256951787659223143740127833" + .into(), + "13711569243726999863512778161983125470458400981069655361767578939766971032706" + .into(), + "2942999073656444791256166118886574877976523928599259988730412230442182398832" + .into(), + "13741805436257583212406513632778404280140012772934699700695904335718144229392" + .into(), + "2966496260001027437433299921197242880503083867421600405476235949236365134313" + .into(), + "12214449406116917832159291589682410934022154504967698746834536840379875421993" + .into(), + "14724028677081535225097028894192068729072210977241429998194582705094787450320" + .into(), + "18532472576071611894578331306492326674151870705350144561012248059523768664658" + .into(), + "12590949238667856614747230064159254583777806754508472794550424398198576585120" + .into(), + "13228220894074693515947418568115512670466893414535562052872530653586084906533" + .into(), + ], + ], + ]; + + (c_str, m_str) +} diff --git a/crates/aptos-crypto/src/poseidon_bn254/keyless.rs b/crates/aptos-crypto/src/poseidon_bn254/keyless.rs new file mode 100644 index 0000000..3ae52fb --- /dev/null +++ b/crates/aptos-crypto/src/poseidon_bn254/keyless.rs @@ -0,0 +1,393 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +//! Implements keyless-specific domain-separated hash functions from Poseidon scalar hashing. +use crate::{ + poseidon_bn254, + poseidon_bn254::{hash_scalars, MAX_NUM_INPUT_SCALARS}, +}; +use anyhow::bail; +use ark_bn254::Fr; +use ark_ff::{BigInteger, PrimeField}; + +/// A BN254 scalar is 254 bits which means it can only store up to 31 bytes of data. We could use a +/// more complicated packing to take advantage of the unused 6 bits, but we do not since it allows +/// us to keep our SNARK circuits simpler. +pub const BYTES_PACKED_PER_SCALAR: usize = 31; + +/// `BYTES_PACKED_PER_SCALAR` but for u64s. +pub const LIMBS_PACKED_PER_SCALAR: usize = 3; + +/// The maximum number of bytes that can be given as input to the byte-oriented variant of the +/// Poseidon-BN254 hash function exposed in `pad_and_hash_bytes`. +/// +/// Note: The first scalar is used to encode the length of the byte array. The max. # of bytes that +/// can be stored in 16 scalars is 16 * 31 = 496 bytes. So the size can be encoded into +/// `ceil(log_2(496)) = 9` bits of a scalar. That would leave 254 - 9 = 245 bits > 30 bytes for +/// storing data in that scalar. We do not plan on exploiting this extra free space (since our +/// SNARK circuits would have to implement this more complicated packing). +pub const MAX_NUM_INPUT_BYTES: usize = MAX_NUM_INPUT_SCALARS * BYTES_PACKED_PER_SCALAR; + +/// `MAX_NUM_INPUT_BYTES` but for u64s. +pub const MAX_NUM_INPUT_LIMBS: usize = MAX_NUM_INPUT_SCALARS * LIMBS_PACKED_PER_SCALAR; + +/// Given a string and `max_bytes`, it pads the byte array of the string with zeros up to size `max_bytes`, +/// packs it to scalars, and returns the hash of the scalars. +/// +/// This function calls `pad_and_pack_bytes_to_scalars_no_len` safely as strings will not contain the zero byte except to terminate. +pub fn pad_and_hash_string(str: &str, max_bytes: usize) -> anyhow::Result { + pad_and_hash_bytes_with_len(str.as_bytes(), max_bytes) +} + +/// Given $n$ bytes, this function returns $k$ field elements that pack those bytes as tightly as +/// possible. This will not store the length $n$. +/// +/// We pack the $i$th chunk of 31 bytes into $e_(i-1)$, assuming a little-endian (LE) encoding. +/// +/// If the last chunk is smaller than 31 bytes, so all remaining bytes in its associated field +/// element are padded to zero due to the LE encoding. +pub(crate) fn pack_bytes_to_scalars(bytes: &[u8]) -> anyhow::Result> { + if bytes.len() > MAX_NUM_INPUT_BYTES { + bail!( + "Cannot hash more than {} bytes. Was given {} bytes.", + MAX_NUM_INPUT_BYTES, + bytes.len() + ); + } + + let scalars = bytes + .chunks(BYTES_PACKED_PER_SCALAR) + .map(|chunk| pack_bytes_to_one_scalar(chunk).expect("chunk converts to scalar")) + .collect::>(); + + Ok(scalars) +} + +fn pack_limbs_to_scalars(limbs: &[u64]) -> anyhow::Result> { + if limbs.len() > MAX_NUM_INPUT_LIMBS { + bail!( + "Cannot hash more than {} limbs. Was given {} limbs.", + MAX_NUM_INPUT_LIMBS, + limbs.len() + ); + } + + let scalars = limbs + .chunks(LIMBS_PACKED_PER_SCALAR) + .map(|chunk| pack_limbs_to_one_scalar(chunk).expect("chunk converts to scalar")) + .collect::>(); + + Ok(scalars) +} + +/// Given $n$ bytes, this function left pads bytes with 'max_bytes'- $n$ zeros and returns $k+1$ field elements that pack those bytes as tightly as +/// possible where $e_(0)$ is $n$ and $k$ is the ceiling of `max_bytes`/`BYTES_PACKED_PER_SCALAR`. +pub fn pad_and_pack_bytes_to_scalars_with_len( + bytes: &[u8], + max_bytes: usize, +) -> anyhow::Result> { + let len = bytes.len(); + if max_bytes > MAX_NUM_INPUT_BYTES { + bail!( + "Cannot hash more than {} bytes. Was given {} bytes.", + MAX_NUM_INPUT_BYTES, + len + ); + } + if len > max_bytes { + bail!( + "Byte array length of {} is NOT <= max length of {} bytes.", + bytes.len(), + max_bytes + ); + } + + let len_scalar = pack_bytes_to_one_scalar(&len.to_le_bytes())?; + let scalars = pad_and_pack_bytes_to_scalars_no_len(bytes, max_bytes)? + .into_iter() + .chain([len_scalar]) + .collect::>(); + Ok(scalars) +} + +/// `pad_and_pack_bytes_to_scalars_with_len` but for u64s. +pub fn pad_and_pack_limbs_to_scalars_with_len( + limbs: &[u64], + max_limbs: usize, +) -> anyhow::Result> { + let len = limbs.len(); + if max_limbs > MAX_NUM_INPUT_LIMBS { + bail!( + "Cannot hash more than {} limbs. Was given {} limbs.", + MAX_NUM_INPUT_LIMBS, + len + ); + } + if len > max_limbs { + bail!( + "Limb array length of {} is NOT <= max length of {} limbs.", + limbs.len(), + max_limbs + ); + } + + let len_scalar = From::from(len as u64); + let scalars = pad_and_pack_limbs_to_scalars_no_len(limbs, max_limbs)? + .into_iter() + .chain([len_scalar]) + .collect::>(); + Ok(scalars) +} + +/// Given $n$ bytes, this function left pads bytes with 'max_bytes'- $n$ zeros and returns $k$ field elements that pack those bytes as tightly as +/// possible, where $k$ is the ceiling of `max_bytes`/`BYTES_PACKED_PER_SCALAR`. +pub(crate) fn pad_and_pack_bytes_to_scalars_no_len( + bytes: &[u8], + max_bytes: usize, +) -> anyhow::Result> { + let len = bytes.len(); + if max_bytes > MAX_NUM_INPUT_BYTES { + bail!( + "Cannot hash more than {} bytes. Was given {} bytes.", + MAX_NUM_INPUT_BYTES, + len + ); + } + if bytes.len() > max_bytes { + bail!( + "Byte array length of {} is NOT <= max length of {} bytes.", + bytes.len(), + max_bytes + ); + } + + let padded = zero_pad_bytes(bytes, max_bytes)?; + let scalars = pack_bytes_to_scalars(padded.as_slice())?; + Ok(scalars) +} + +fn pad_and_pack_limbs_to_scalars_no_len( + limbs: &[u64], + max_limbs: usize, +) -> anyhow::Result> { + let len = limbs.len(); + if max_limbs > MAX_NUM_INPUT_LIMBS { + bail!( + "Cannot hash more than {} limbs. Was given {} limbs.", + MAX_NUM_INPUT_LIMBS, + len + ); + } + if limbs.len() > max_limbs { + bail!( + "Limb array length of {} is NOT <= max length of {} limbs.", + limbs.len(), + max_limbs + ); + } + + let padded = zero_pad_limbs(limbs, max_limbs)?; + let scalars = pack_limbs_to_scalars(padded.as_slice())?; + Ok(scalars) +} + +/// Packs the bytes to a vector of scalars (see `pack_bytes_to_scalars`) and hashes the scalars via +/// `hash_scalars`. +/// +/// Note: The byte packing encodes the length of the bytes properly so as to avoid collisions when +/// hashing, say, 0x00 versus 0x0000. +/// +/// WARNING: We do not expose this function to avoid unnecessary bugs, since for SNARK circuits we +/// always have to hash padded byte arrays. If necessary, an expert developer can indirectly call +/// this via `pad_and_hash_bytes(bytes, bytes.len())`. +#[allow(unused)] +fn hash_bytes(bytes: &[u8]) -> anyhow::Result { + let scalars = pack_bytes_to_scalars(bytes)?; + poseidon_bn254::hash_scalars(scalars) +} + +/// Given `bytes`, if the length of `bytes` is less than `max_bytes`, pads `bytes` with zeros to length `max_bytes`. +/// Then it packs padded `bytes` and returns the hash of the scalars via `hash_scalars`. +/// +/// This is used when we know that `bytes` will not terminate in 0's and may skip encoding the length, for +/// example ASCII strings. Otherwise unexpected collisions can occur. +/// +/// Due to risk of collisions due to improper use by the caller, it is not exposed. +pub fn pad_and_hash_bytes_no_len(bytes: &[u8], max_bytes: usize) -> anyhow::Result { + let scalars = pad_and_pack_bytes_to_scalars_no_len(bytes, max_bytes)?; + hash_scalars(scalars) +} + +/// Given `bytes`, if the length of `bytes` is less than `max_bytes`, pads `bytes` with zeros to length `max_bytes`. +/// Then it packs these padded `bytes` and preserves the original length as the first scalar and returns the hash of the scalars via `hash_scalars`. +/// +/// This is used when we want to preserve the length of the `bytes` to prevent collisions where `bytes` could terminate in 0's. +pub fn pad_and_hash_bytes_with_len(bytes: &[u8], max_bytes: usize) -> anyhow::Result { + let scalars = pad_and_pack_bytes_to_scalars_with_len(bytes, max_bytes)?; + hash_scalars(scalars) +} + +/// `pad_and_hash_bytes_with_len` but for u64s. +pub fn pad_and_hash_limbs_with_len(limbs: &[u64], max_limbs: usize) -> anyhow::Result { + let scalars = pad_and_pack_limbs_to_scalars_with_len(limbs, max_limbs)?; + hash_scalars(scalars) +} + +/// We often have to pad byte arrays with 0s, up to a maximum length. +/// Given an array of bytes in `bytes`, if its length is less than `size`, appends zero bytes to +/// it until its length is equal to `size`. +fn zero_pad_bytes(bytes: &[u8], size: usize) -> anyhow::Result> { + if size > MAX_NUM_INPUT_BYTES { + bail!( + "Cannot pad to more than {} bytes. Requested size is {}.", + MAX_NUM_INPUT_BYTES, + size + ); + } + + if bytes.len() > size { + bail!("Cannot pad {} byte(s) to size {}", bytes.len(), size); + } + + let mut padded = bytes.to_vec(); + padded.resize(size, 0x00); + Ok(padded) +} + +fn zero_pad_limbs(limbs: &[u64], size: usize) -> anyhow::Result> { + if size > MAX_NUM_INPUT_LIMBS { + bail!( + "Cannot pad to more than {} limbs. Requested size is {}.", + MAX_NUM_INPUT_LIMBS, + size + ); + } + + if limbs.len() > size { + bail!("Cannot pad {} limb(s) to size {}", limbs.len(), size); + } + + let mut padded = limbs.to_vec(); + padded.resize(size, 0); + Ok(padded) +} + +/// Converts the chunk of bytes into a scalar, assuming it is of size less than or equal to `BYTES_PACKED_PER_SCALAR`. +pub fn pack_bytes_to_one_scalar(chunk: &[u8]) -> anyhow::Result { + if chunk.len() > BYTES_PACKED_PER_SCALAR { + bail!( + "Cannot convert chunk to scalar. Max chunk size is {} bytes. Was given {} bytes.", + BYTES_PACKED_PER_SCALAR, + chunk.len(), + ); + } + let fr = ark_bn254::Fr::from_le_bytes_mod_order(chunk); + Ok(fr) +} + +/// `pack_bytes_to_one_scalar` but for u64s. +pub fn pack_limbs_to_one_scalar(chunk: &[u64]) -> anyhow::Result { + if chunk.len() > LIMBS_PACKED_PER_SCALAR { + bail!( + "Cannot convert chunk to scalar. Max chunk size is {} limbs. Was given {} limbs.", + LIMBS_PACKED_PER_SCALAR, + chunk.len(), + ); + } + let bytes_chunk: Vec = chunk + .iter() + .flat_map(|&limb| limb.to_le_bytes().into_iter()) + .collect(); + let fr = ark_bn254::Fr::from_le_bytes_mod_order(bytes_chunk.as_slice()); + Ok(fr) +} + +/// Utility method to convert an Fr to a 32-byte slice. +pub fn fr_to_bytes_le(fr: &ark_bn254::Fr) -> [u8; 32] { + fr.into_bigint() + .to_bytes_le() + .try_into() + .expect("expected 32-byte public inputs hash") +} + +#[cfg(test)] +mod test { + use crate::poseidon_bn254::{ + keyless, + keyless::{pack_bytes_to_scalars, BYTES_PACKED_PER_SCALAR, MAX_NUM_INPUT_BYTES}, + MAX_NUM_INPUT_SCALARS, + }; + use ark_ff::{BigInteger, One, PrimeField, Zero}; + use num_bigint::BigUint; + + #[test] + fn test_poseidon_bn254_pad_and_hash_bytes() { + let aud = "google"; + const LEN: usize = 248; + let aud_val_hash = keyless::pad_and_hash_string(aud, LEN).unwrap(); + assert_eq!( + aud_val_hash.to_string(), + "4022319167392179362271493931675371567039199401695470709241660273812313544045" + ); + } + + #[test] + fn test_poseidon_bn254_pad_and_hash_bytes_no_collision() { + let s1: [u8; 3] = [0, 0, 1]; + let s2: [u8; 4] = [0, 0, 1, 0]; + const MAX_BYTES: usize = 248; + let h1 = keyless::pad_and_hash_bytes_with_len(&s1, MAX_BYTES).unwrap(); + let h2 = keyless::pad_and_hash_bytes_with_len(&s2, MAX_BYTES).unwrap(); + + assert_ne!(h1, h2); + } + + #[test] + fn test_poseidon_bn254_pack_bytes() { + // b"" -> vec![Fr(0)] + let scalars = pack_bytes_to_scalars(b"").unwrap(); + assert_eq!(scalars.len(), 0); + + // 0x01 -> vec![Fr(0)] + let scalars = pack_bytes_to_scalars(vec![0x01].as_slice()).unwrap(); + assert_eq!(scalars.len(), 1); + assert_eq!(scalars[0], ark_bn254::Fr::one()); + + // (2^247).to_le_bytes() -> vec![Fr(31), Fr(2^247)] + let pow_2_to_247 = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80"; + let scalars = pack_bytes_to_scalars(pow_2_to_247.as_slice()).unwrap(); + assert_eq!(scalars.len(), 1); + let pow_2_to_247_le_bytes = BigUint::from(2u8).pow(247).to_bytes_le(); + assert_eq!( + scalars[0], + ark_bn254::Fr::from_le_bytes_mod_order(pow_2_to_247_le_bytes.as_slice()) + ); + + // (2^248).to_le_bytes() -> vec![Fr(32), Fr(2^248)] + let pow_2_to_248 = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"; + let scalars = pack_bytes_to_scalars(pow_2_to_248.as_slice()).unwrap(); + assert_eq!(scalars.len(), 2); + assert_eq!(scalars[0], ark_bn254::Fr::zero()); + assert_eq!(scalars[1], ark_bn254::Fr::one()); + + // Trying to pack the max # of bytes should NOT fail + let full_bytes = (0..MAX_NUM_INPUT_BYTES).map(|_| 0xFF).collect::>(); + let scalars = pack_bytes_to_scalars(full_bytes.as_slice()).unwrap(); + assert_eq!(scalars.len(), MAX_NUM_INPUT_SCALARS); + + let mut expected_bytes = (0..BYTES_PACKED_PER_SCALAR) + .map(|_| 0xFF) + .collect::>(); + expected_bytes.push(0x00); // last 32nd byte is zero + for scalar in scalars.iter().take(MAX_NUM_INPUT_SCALARS).skip(1) { + assert_eq!(scalar.into_bigint().to_bytes_le(), expected_bytes) + } + + // Trying to pack 1 more byte than allowed should fail + let too_many_bytes = (0..MAX_NUM_INPUT_BYTES + 1) + .map(|_| 0xFF) + .collect::>(); + let result = pack_bytes_to_scalars(too_many_bytes.as_slice()); + assert!(result.is_err()) + } +} diff --git a/crates/aptos-crypto/src/poseidon_bn254/mod.rs b/crates/aptos-crypto/src/poseidon_bn254/mod.rs new file mode 100644 index 0000000..bb61bfb --- /dev/null +++ b/crates/aptos-crypto/src/poseidon_bn254/mod.rs @@ -0,0 +1,144 @@ +// Copyright (c) Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +//! Implements the Poseidon hash function for BN-254, which hashes $\le$ 16 field elements and +//! produces a single field element as output. +mod alt_fr; +mod constants; +pub mod keyless; + +use crate::poseidon_bn254::constants::*; +use anyhow::bail; +use keyless::{ + pack_bytes_to_scalars, pad_and_pack_bytes_to_scalars_no_len, + pad_and_pack_bytes_to_scalars_with_len, +}; +use neptune::poseidon::{HashMode::OptimizedStatic, Poseidon}; + +/// The maximum number of input scalars that can be hashed using the Poseidon-BN254 hash function +/// exposed in `hash_scalars`. +pub const MAX_NUM_INPUT_SCALARS: usize = 16; + +/// Macro for Poseidon-BN254-hashing a vector of scalars. +macro_rules! neptune_hash { + ($elems:expr, $constants:expr) => {{ + let mut hasher = Poseidon::new(&$constants); + hasher.reset(); + for elem in $elems.into_iter() { + hasher.input(elem.into()).expect("Too many inputs"); + } + hasher.hash_in_mode(OptimizedStatic); + hasher.elements[0] + }}; +} + +/// Given an array of up to `MAX_NUM_INPUT_SCALARS` field elements (in the BN254 scalar field), hashes +/// them using Poseidon-BN254 into a single field element. +pub fn hash_scalars(inputs: Vec) -> anyhow::Result { + if inputs.is_empty() || inputs.len() > MAX_NUM_INPUT_SCALARS { + bail!( + "Poseidon-BN254 needs > 0 and <= {} inputs, but was called with {} inputs", + MAX_NUM_INPUT_SCALARS, + inputs.len() + ); + } + + let result = match inputs.len() { + 1 => neptune_hash!(inputs, POSEIDON_1), + 2 => neptune_hash!(inputs, POSEIDON_2), + 3 => neptune_hash!(inputs, POSEIDON_3), + 4 => neptune_hash!(inputs, POSEIDON_4), + 5 => neptune_hash!(inputs, POSEIDON_5), + 6 => neptune_hash!(inputs, POSEIDON_6), + 7 => neptune_hash!(inputs, POSEIDON_7), + 8 => neptune_hash!(inputs, POSEIDON_8), + 9 => neptune_hash!(inputs, POSEIDON_9), + 10 => neptune_hash!(inputs, POSEIDON_10), + 11 => neptune_hash!(inputs, POSEIDON_11), + 12 => neptune_hash!(inputs, POSEIDON_12), + 13 => neptune_hash!(inputs, POSEIDON_13), + 14 => neptune_hash!(inputs, POSEIDON_14), + 15 => neptune_hash!(inputs, POSEIDON_15), + 16 => neptune_hash!(inputs, POSEIDON_16), + _ => bail!( + "Poseidon-BN254 was called with {} inputs, more than the maximum 16 allowed inputs.", + inputs.len() + ), + }; + + Ok(result.into()) +} + +/// Given a string and `max_bytes`, it pads the byte array of the string with zeros up to size `max_bytes`, +/// packs it to scalars, and returns the hash of the scalars. +pub fn pad_and_hash_string(str: &str, max_bytes: usize) -> anyhow::Result { + pad_and_hash_bytes_with_len(str.as_bytes(), max_bytes) +} + +/// Packs the bytes to a vector of scalars (see `pack_bytes_to_scalars`) and hashes the scalars via +/// `hash_scalars`. +/// +/// Note: The byte packing encodes the length of the bytes properly so as to avoid collisions when +/// hashing, say, 0x00 versus 0x0000. +/// +/// Note: We do not expose this function to avoid unnecessary bugs, since for SNARK circuits we +/// always have to hash padded byte arrays. If necessary, an expert developer can indirectly call +/// this via `pad_and_hash_bytes(bytes, bytes.len())`. +#[allow(unused)] +fn hash_bytes(bytes: &[u8]) -> anyhow::Result { + let scalars = pack_bytes_to_scalars(bytes)?; + hash_scalars(scalars) +} + +/// Given `bytes`, if the length of `bytes` is less than `max_bytes`, pads `bytes` with zeros to length `max_bytes`. +/// Then it packs padded `bytes` and returns the hash of the scalars via `hash_scalars`. +/// +/// This is used when we know that `bytes` will not terminate in 0's and may skip encoding the length, for +/// example ASCII strings. Otherwise, unexpected collisions can occur. +/// +/// We do not expose this to minimize the risk of collisions due to improper use by the caller. +pub fn pad_and_hash_bytes_no_len(bytes: &[u8], max_bytes: usize) -> anyhow::Result { + let scalars = pad_and_pack_bytes_to_scalars_no_len(bytes, max_bytes)?; + hash_scalars(scalars) +} + +/// Given `bytes`, if the length of `bytes` is less than `max_bytes`, pads `bytes` with zeros to length `max_bytes`. +/// Then it packs these padded `bytes` and preserves the original length as the first scalar and returns the hash of the scalars via `hash_scalars`. +/// +/// This is used when we want to preserve the length of the `bytes` to prevent collisions where `bytes` could terminate in 0's. +pub fn pad_and_hash_bytes_with_len( + bytes: &[u8], + max_bytes: usize, +) -> anyhow::Result { + let scalars = pad_and_pack_bytes_to_scalars_with_len(bytes, max_bytes)?; + hash_scalars(scalars) +} + +#[cfg(test)] +mod test { + use crate::poseidon_bn254::hash_scalars; + use std::str::FromStr; + + #[test] + fn test_poseidon_bn254_poseidon_ark_vectors() { + let mut inputs = vec!["1", "2"] + .into_iter() + .map(|hex| ark_bn254::Fr::from_str(hex).unwrap()) + .collect::>(); + + // From https://github.com/arnaucube/poseidon-ark/blob/6d2487aa1308d9d3860a2b724c485d73095c1c68/src/lib.rs#L170 + let h = hash_scalars(inputs.clone()).unwrap(); + assert_eq!( + h.to_string(), + "7853200120776062878684798364095072458815029376092732009249414926327459813530" + ); + + // From the same place. + inputs.pop(); + let h = hash_scalars(inputs).unwrap(); + assert_eq!( + h.to_string(), + "18586133768512220936620570745912940619677854269274689475585506675881198879027" + ); + } +} diff --git a/crates/aptos-crypto/src/secp256k1_ecdsa.rs b/crates/aptos-crypto/src/secp256k1_ecdsa.rs new file mode 100644 index 0000000..2c30069 --- /dev/null +++ b/crates/aptos-crypto/src/secp256k1_ecdsa.rs @@ -0,0 +1,293 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +//! This module provides APIs for private keys and public keys used in Secp256k1 ecdsa. + +use crate::{ + hash::{CryptoHash, HashValue}, + traits, + traits::{CryptoMaterialError, ValidCryptoMaterial, ValidCryptoMaterialStringExt}, +}; +use anyhow::{anyhow, Result}; +use aptos_crypto_derive::{key_name, DeserializeKey, SerializeKey, SilentDebug, SilentDisplay}; +use core::convert::TryFrom; +use serde::Serialize; + +/// libsecp256k1 expects pre-hashed messages of 32-bytes. +pub const MESSAGE_LENGTH: usize = 32; +/// Secp256k1 ecdsa private keys are 256-bit. +pub const PRIVATE_KEY_LENGTH: usize = 32; +/// Secp256k1 ecdsa public keys contain a prefix indicating compression and two 32-byte coordinates. +pub const PUBLIC_KEY_LENGTH: usize = 65; +/// Secp256k1 ecdsa signatures are 256-bit. +pub const SIGNATURE_LENGTH: usize = 64; + +/// Secp256k1 ecdsa private key +#[derive(DeserializeKey, Eq, PartialEq, SerializeKey, SilentDebug, SilentDisplay)] +#[key_name("Secp256k1EcdsaPrivateKey")] +pub struct PrivateKey(pub(crate) libsecp256k1::SecretKey); + +#[cfg(feature = "assert-private-keys-not-cloneable")] +static_assertions::assert_not_impl_any!(PrivateKey: Clone); + +#[cfg(any(test, feature = "cloneable-private-keys"))] +impl Clone for PrivateKey { + fn clone(&self) -> Self { + let serialized: &[u8] = &(self.to_bytes()); + PrivateKey::try_from(serialized).unwrap() + } +} + +impl PrivateKey { + /// Serialize the private key into a byte vector + pub fn to_bytes(&self) -> Vec { + self.0.serialize().to_vec() + } + + fn sign(&self, message: &libsecp256k1::Message) -> Signature { + let (signature, _recovery_id) = libsecp256k1::sign(message, &self.0); + Signature(signature) + } + + /// Private function aimed at minimizing code duplication between sign + /// methods of the SigningKey implementation. This should remain private. + #[cfg(any(test, feature = "fuzzing"))] + fn sign_arbitrary_message(&self, message: &[u8]) -> Signature { + let message = + bytes_to_message(message).expect("Consistently hashed to 32-bytes, should never fail."); + // libsecp256k1 ensures that the s in signature is normalized + self.sign(&message) + } +} + +impl TryFrom<&[u8]> for PrivateKey { + type Error = CryptoMaterialError; + + fn try_from(bytes: &[u8]) -> std::result::Result { + match libsecp256k1::SecretKey::parse_slice(bytes) { + Ok(private_key) => Ok(PrivateKey(private_key)), + Err(_) => Err(CryptoMaterialError::DeserializationError), + } + } +} + +impl traits::Length for PrivateKey { + fn length(&self) -> usize { + PRIVATE_KEY_LENGTH + } +} + +impl traits::PrivateKey for PrivateKey { + type PublicKeyMaterial = PublicKey; +} + +impl traits::SigningKey for PrivateKey { + type SignatureMaterial = Signature; + type VerifyingKeyMaterial = PublicKey; + + fn sign( + &self, + message: &T, + ) -> Result { + match bytes_to_message(&traits::signing_message(message)?) { + Ok(message) => Ok(self.sign(&message)), + Err(_) => Err(CryptoMaterialError::SerializationError), + } + } + + #[cfg(any(test, feature = "fuzzing"))] + fn sign_arbitrary_message(&self, message: &[u8]) -> Signature { + PrivateKey::sign_arbitrary_message(self, message) + } +} + +impl traits::Uniform for PrivateKey { + fn generate(rng: &mut R) -> Self + where + R: ::rand::RngCore + ::rand::CryptoRng + ::rand_core::CryptoRng + ::rand_core::RngCore, + { + loop { + let mut ret = [0u8; PRIVATE_KEY_LENGTH]; + rng.fill_bytes(&mut ret); + if let Ok(key) = libsecp256k1::SecretKey::parse(&ret) { + return Self(key); + } + } + } +} + +impl ValidCryptoMaterial for PrivateKey { + fn to_bytes(&self) -> Vec { + self.to_bytes().to_vec() + } +} + +/// Secp256k1 ecds public key +#[derive(DeserializeKey, Clone, Eq, PartialEq, SerializeKey)] +#[key_name("Secp256k1EcdsaPublicKey")] +pub struct PublicKey(pub(crate) libsecp256k1::PublicKey); + +impl PublicKey { + /// Serialize the public key into a byte vector (full length) + pub fn to_bytes(&self) -> Vec { + self.0.serialize().to_vec() + } +} + +impl std::fmt::Debug for PublicKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "secp256k1_ecdsa::PublicKey({})", self) + } +} + +impl std::fmt::Display for PublicKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", hex::encode(&self.to_bytes()[..])) + } +} + +impl std::hash::Hash for PublicKey { + fn hash(&self, state: &mut H) { + let encoded_public_key = self.to_bytes(); + state.write(&encoded_public_key); + } +} + +impl TryFrom<&[u8]> for PublicKey { + type Error = CryptoMaterialError; + + fn try_from(bytes: &[u8]) -> std::result::Result { + match libsecp256k1::PublicKey::parse_slice(bytes, None) { + Ok(public_key) => Ok(PublicKey(public_key)), + Err(_) => Err(CryptoMaterialError::DeserializationError), + } + } +} + +impl From<&PrivateKey> for PublicKey { + fn from(private_key: &PrivateKey) -> Self { + PublicKey(libsecp256k1::PublicKey::from_secret_key(&private_key.0)) + } +} + +impl traits::PublicKey for PublicKey { + type PrivateKeyMaterial = PrivateKey; +} + +impl traits::Length for PublicKey { + fn length(&self) -> usize { + PUBLIC_KEY_LENGTH + } +} + +impl ValidCryptoMaterial for PublicKey { + fn to_bytes(&self) -> Vec { + self.to_bytes().to_vec() + } +} + +impl traits::VerifyingKey for PublicKey { + type SignatureMaterial = Signature; + type SigningKeyMaterial = PrivateKey; +} + +/// Secp256k1 ecdsa signature +#[derive(DeserializeKey, Clone, SerializeKey)] +#[key_name("Secp256k1EcdsaSignature")] +pub struct Signature(pub(crate) libsecp256k1::Signature); + +impl Signature { + /// Serialize the signature into a byte vector + pub fn to_bytes(&self) -> Vec { + self.0.serialize().to_vec() + } + + fn verify( + &self, + message: &libsecp256k1::Message, + public_key: &libsecp256k1::PublicKey, + ) -> Result<()> { + // Prevent malleability attacks, low order only. The library only signs in low + // order, so this was done intentionally. + if self.0.s.is_high() { + Err(anyhow!(CryptoMaterialError::CanonicalRepresentationError)) + } else if libsecp256k1::verify(message, &self.0, public_key) { + Ok(()) + } else { + Err(anyhow!("Unable to verify signature.")) + } + } +} + +impl Eq for Signature {} + +impl PartialEq for Signature { + fn eq(&self, other: &Signature) -> bool { + self.to_bytes()[..] == other.to_bytes()[..] + } +} + +impl TryFrom<&[u8]> for Signature { + type Error = CryptoMaterialError; + + fn try_from(bytes: &[u8]) -> std::result::Result { + match libsecp256k1::Signature::parse_standard_slice(bytes) { + Ok(signature) => Ok(Signature(signature)), + Err(_) => Err(CryptoMaterialError::DeserializationError), + } + } +} + +impl std::fmt::Debug for Signature { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "secp256k1_ecdsa::Signature({})", self) + } +} + +impl std::fmt::Display for Signature { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", hex::encode(&self.to_bytes()[..])) + } +} + +impl std::hash::Hash for Signature { + fn hash(&self, state: &mut H) { + state.write(&self.to_bytes()); + } +} + +impl traits::Signature for Signature { + type SigningKeyMaterial = PrivateKey; + type VerifyingKeyMaterial = PublicKey; + + fn verify(&self, message: &T, public_key: &PublicKey) -> Result<()> { + let message = bytes_to_message(&traits::signing_message(message)?)?; + self.verify(&message, &public_key.0) + } + + fn verify_arbitrary_msg(&self, message: &[u8], public_key: &PublicKey) -> Result<()> { + let message = bytes_to_message(message)?; + self.verify(&message, &public_key.0) + } + + fn to_bytes(&self) -> Vec { + self.to_bytes() + } +} + +impl traits::Length for Signature { + fn length(&self) -> usize { + SIGNATURE_LENGTH + } +} + +impl ValidCryptoMaterial for Signature { + fn to_bytes(&self) -> Vec { + self.to_bytes() + } +} + +fn bytes_to_message(message: &[u8]) -> Result { + let message_digest = HashValue::sha3_256_of(message).to_vec(); + libsecp256k1::Message::parse_slice(&message_digest).map_err(|e| anyhow!("{}", e)) +} diff --git a/crates/aptos-crypto/src/secp256r1_ecdsa/mod.rs b/crates/aptos-crypto/src/secp256r1_ecdsa/mod.rs new file mode 100644 index 0000000..b00a97b --- /dev/null +++ b/crates/aptos-crypto/src/secp256r1_ecdsa/mod.rs @@ -0,0 +1,64 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 +//! This module provides an API for the ECDSA signature scheme over the NIST-P256 curve as defined in [NIST SP 800-186](https://csrc.nist.gov/publications/detail/sp/800-186/final). +//! NIST-P256 is also known as Secp256r1 or Prime256v1. +//! +//! Signature verification also checks and rejects non-canonical signatures. Signing is guaranteed +//! to output the canonical signature which passes this module's verification. +//! +//! # Examples +//! +//! ``` +//! use aptos_crypto_derive::{CryptoHasher, BCSCryptoHash}; +//! use aptos_crypto::{ +//! secp256r1_ecdsa::*, +//! traits::{Signature, SigningKey, Uniform}, +//! test_utils::KeyPair +//! }; +//! use rand::{rngs::StdRng, SeedableRng}; +//! use rand_core::OsRng; +//! use serde::{Serialize, Deserialize}; +//! +//! #[derive(Serialize, Deserialize, CryptoHasher, BCSCryptoHash)] +//! pub struct TestCryptoDocTest(String); +//! let message = TestCryptoDocTest("Test message".to_string()); +//! +//! let mut rng = OsRng; +//! let kp = KeyPair::::generate(&mut rng); +//! +//! let signature = kp.private_key.sign(&message).unwrap(); +//! assert!(signature.verify(&message, &kp.public_key).is_ok()); +//! ``` + +/// The length in bytes of the Secp256r1Ecdsa PrivateKey +pub const PRIVATE_KEY_LENGTH: usize = 32; +/// The length in bytes of the Secp256r1Ecdsa PublicKey +pub const PUBLIC_KEY_LENGTH: usize = 65; +/// The length in bytes of the Secp256r1Ecdsa Signature +pub const SIGNATURE_LENGTH: usize = 64; + +/// The order of Secp256r1Ecdsa as defined in [NIST SP 800-186](https://csrc.nist.gov/publications/detail/sp/800-186/final) +/// In big-endian form +const ORDER: [u8; 32] = [ + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84, 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51, +]; + +/// The value (q-1)/2 in big-endian form, where q is the order of Secp256r1Ecdsa as defined in [NIST SP 800-186](https://csrc.nist.gov/publications/detail/sp/800-186/final). +/// Computed with the following SageMath code: +/// +/// # Curve order +/// qq = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551 +/// q_half = (qq-1)/2 +pub const ORDER_HALF: [u8; 32] = [ + 0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xDE, 0x73, 0x7D, 0x56, 0xD3, 0x8B, 0xCF, 0x42, 0x79, 0xDC, 0xE5, 0x61, 0x7E, 0x31, 0x92, 0xA8, +]; + +pub mod secp256r1_ecdsa_keys; +pub mod secp256r1_ecdsa_sigs; + +#[cfg(any(test, feature = "fuzzing"))] +pub use secp256r1_ecdsa_keys::keypair_strategy; +pub use secp256r1_ecdsa_keys::{PrivateKey, PublicKey}; +pub use secp256r1_ecdsa_sigs::Signature; diff --git a/crates/aptos-crypto/src/secp256r1_ecdsa/secp256r1_ecdsa_keys.rs b/crates/aptos-crypto/src/secp256r1_ecdsa/secp256r1_ecdsa_keys.rs new file mode 100644 index 0000000..e2f3201 --- /dev/null +++ b/crates/aptos-crypto/src/secp256r1_ecdsa/secp256r1_ecdsa_keys.rs @@ -0,0 +1,275 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +//! This file implements traits for Secp256r1 ECDSA private keys and public keys. + +#[cfg(any(test, feature = "fuzzing"))] +use crate::test_utils::{self, KeyPair}; +use crate::{ + hash::CryptoHash, + secp256r1_ecdsa::{Signature, ORDER, PRIVATE_KEY_LENGTH, PUBLIC_KEY_LENGTH}, + traits::{PrivateKey as PrivateKeyTrait, PublicKey as PublicKeyTrait, *}, +}; +use aptos_crypto_derive::{key_name, DeserializeKey, SerializeKey, SilentDebug, SilentDisplay}; +use core::convert::TryFrom; +use num_bigint::BigUint; +use num_integer::Integer; +use p256::{self, ecdsa::signature::Signer}; +#[cfg(any(test, feature = "fuzzing"))] +use proptest::prelude::*; +use serde::Serialize; +use std::fmt; + +/// A secp256r1_ecdsa private key +#[derive(DeserializeKey, SerializeKey, SilentDebug, SilentDisplay)] +#[key_name("Secp256r1EcdsaPrivateKey")] +pub struct PrivateKey(pub(crate) p256::ecdsa::SigningKey); + +#[cfg(feature = "assert-private-keys-not-cloneable")] +static_assertions::assert_not_impl_any!(PrivateKey: Clone); + +#[cfg(any(test, feature = "cloneable-private-keys"))] +impl Clone for PrivateKey { + fn clone(&self) -> Self { + let serialized: &[u8] = &(self.to_bytes()); + PrivateKey::try_from(serialized).unwrap() + } +} + +/// A secp256r1_ecdsa public key +#[derive(DeserializeKey, Clone, SerializeKey)] +#[key_name("Secp256r1EcdsaPublicKey")] +pub struct PublicKey(pub(crate) p256::ecdsa::VerifyingKey); + +impl PrivateKey { + /// The length of the PrivateKey + pub const LENGTH: usize = PRIVATE_KEY_LENGTH; + + /// Serialize a PrivateKey. Uses the SEC1 serialization format. + pub fn to_bytes(&self) -> [u8; PRIVATE_KEY_LENGTH] { + self.0.to_bytes().into() + } + + /// Deserialize a PrivateKey without any validation checks apart from expected key size. + /// Uses the SEC1 serialization format. Bytes are expected to be in big-endian form. + pub(crate) fn from_bytes_unchecked( + bytes: &[u8], + ) -> std::result::Result { + match p256::ecdsa::SigningKey::from_slice(bytes) { + Ok(p256_secret_key) => Ok(PrivateKey(p256_secret_key)), + Err(_) => Err(CryptoMaterialError::DeserializationError), + } + } + + /// Private function aimed at minimizing code duplication between sign + /// methods of the SigningKey implementation. This should remain private. + /// This function uses the `RustCrypto` secp256r1_ecdsa signing library, which uses, + /// as of version 0.13.2, SHA2-256 as its hashing algorithm + fn sign_arbitrary_message(&self, message: &[u8]) -> Signature { + let secret_key = &self.0; + let sig = Signature(secret_key.sign(message.as_ref())); + Signature::make_canonical(&sig) + } +} + +impl PublicKey { + /// Serialize a PublicKey. Uses the SEC1 serialization format. + pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_LENGTH] { + // The RustCrypto P256 `to_sec1_bytes` call here should never return an array of the wrong length and cause a panic + (*self.0.to_sec1_bytes()).try_into().unwrap() + } + + /// Deserialize a P256PublicKey, checking expected key size + /// and that it is a valid curve point. + /// Uses the SEC1 serialization format. + pub(crate) fn from_bytes_unchecked( + bytes: &[u8], + ) -> std::result::Result { + match p256::ecdsa::VerifyingKey::from_sec1_bytes(bytes) { + Ok(p256_public_key) => Ok(PublicKey(p256_public_key)), + Err(_) => Err(CryptoMaterialError::DeserializationError), + } + } +} + +/////////////////////// +// PrivateKey Traits // +/////////////////////// + +impl PrivateKeyTrait for PrivateKey { + type PublicKeyMaterial = PublicKey; +} + +impl SigningKey for PrivateKey { + type SignatureMaterial = Signature; + type VerifyingKeyMaterial = PublicKey; + + fn sign( + &self, + message: &T, + ) -> Result { + Ok(PrivateKey::sign_arbitrary_message( + self, + signing_message(message)?.as_ref(), + )) + } + + #[cfg(any(test, feature = "fuzzing"))] + fn sign_arbitrary_message(&self, message: &[u8]) -> Signature { + PrivateKey::sign_arbitrary_message(self, message) + } +} + +impl Uniform for PrivateKey { + // Returns a random field element as a private key indistinguishable from uniformly random. + // Uses a hack to get around the incompatability of the `aptos-crypto` RngCore trait and the + // `RustCrypto` RngCore trait + fn generate(rng: &mut R) -> Self + where + R: ::rand::RngCore + ::rand::CryptoRng + ::rand_core::CryptoRng + ::rand_core::RngCore, + { + let mut bytes = [0u8; PRIVATE_KEY_LENGTH * 2]; + rng.fill_bytes(&mut bytes); + let bignum = BigUint::from_bytes_be(&bytes[..]); + let order = BigUint::from_bytes_be(&ORDER); + let remainder = bignum.mod_floor(&order); + PrivateKey::from_bytes_unchecked(&remainder.to_bytes_be()).unwrap() + } +} + +impl PartialEq for PrivateKey { + fn eq(&self, other: &Self) -> bool { + self.to_bytes() == other.to_bytes() + } +} + +impl Eq for PrivateKey {} + +impl TryFrom<&[u8]> for PrivateKey { + type Error = CryptoMaterialError; + + /// Deserialize a PrivateKey. This method will check for private key validity: i.e., + /// correct key length. + fn try_from(bytes: &[u8]) -> std::result::Result { + // Note that the only requirement is that the size of the key is 32 bytes, something that + // is already checked during deserialization of p256::ecdsa::SigningKey + PrivateKey::from_bytes_unchecked(bytes) + } +} + +impl Length for PrivateKey { + fn length(&self) -> usize { + Self::LENGTH + } +} + +impl ValidCryptoMaterial for PrivateKey { + fn to_bytes(&self) -> Vec { + self.to_bytes().to_vec() + } +} + +impl Genesis for PrivateKey { + fn genesis() -> Self { + let mut buf = [0u8; PRIVATE_KEY_LENGTH]; + buf[PRIVATE_KEY_LENGTH - 1] = 1; + Self::try_from(buf.as_ref()).unwrap() + } +} + +////////////////////// +// PublicKey Traits // +////////////////////// + +// Implementing From<&PrivateKey<...>> allows to derive a public key in a more elegant fashion +impl From<&PrivateKey> for PublicKey { + fn from(private_key: &PrivateKey) -> Self { + let secret = &private_key.0; + let public: p256::ecdsa::VerifyingKey = secret.into(); + PublicKey(public) + } +} + +// We deduce PublicKey from this +impl PublicKeyTrait for PublicKey { + type PrivateKeyMaterial = PrivateKey; +} + +impl std::hash::Hash for PublicKey { + fn hash(&self, state: &mut H) { + let encoded_pubkey = self.to_bytes(); + state.write(&encoded_pubkey); + } +} + +// Those are required by the implementation of hash above +impl PartialEq for PublicKey { + fn eq(&self, other: &PublicKey) -> bool { + self.to_bytes() == other.to_bytes() + } +} + +impl Eq for PublicKey {} + +// We deduce VerifyingKey from pointing to the signature material +// we get the ability to do `pubkey.validate(msg, signature)` +impl VerifyingKey for PublicKey { + type SignatureMaterial = Signature; + type SigningKeyMaterial = PrivateKey; +} + +impl fmt::Display for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", hex::encode(self.0.to_sec1_bytes())) + } +} + +impl fmt::Debug for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "secp256r1_ecdsa::PublicKey({})", self) + } +} + +impl TryFrom<&[u8]> for PublicKey { + type Error = CryptoMaterialError; + + /// Deserialize a PublicKey. + fn try_from(bytes: &[u8]) -> std::result::Result { + PublicKey::from_bytes_unchecked(bytes) + } +} + +impl Length for PublicKey { + fn length(&self) -> usize { + PUBLIC_KEY_LENGTH + } +} + +impl ValidCryptoMaterial for PublicKey { + fn to_bytes(&self) -> Vec { + self.to_bytes().to_vec() + } +} + +///////////// +// Fuzzing // +///////////// + +/// Produces a uniformly random secp256r1_ecdsa keypair from a seed +#[cfg(any(test, feature = "fuzzing"))] +pub fn keypair_strategy() -> impl Strategy> { + test_utils::uniform_keypair_strategy::() +} + +/// Produces a uniformly random secp256r1_ecdsa public key +#[cfg(any(test, feature = "fuzzing"))] +impl proptest::arbitrary::Arbitrary for PublicKey { + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + crate::test_utils::uniform_keypair_strategy::() + .prop_map(|v| v.public_key) + .boxed() + } +} diff --git a/crates/aptos-crypto/src/secp256r1_ecdsa/secp256r1_ecdsa_sigs.rs b/crates/aptos-crypto/src/secp256r1_ecdsa/secp256r1_ecdsa_sigs.rs new file mode 100644 index 0000000..1ba97ee --- /dev/null +++ b/crates/aptos-crypto/src/secp256r1_ecdsa/secp256r1_ecdsa_sigs.rs @@ -0,0 +1,195 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +//! This file implements traits for ECDSA signatures over Secp256r1. + +use super::SIGNATURE_LENGTH; +use crate::{ + hash::CryptoHash, + secp256r1_ecdsa::{PrivateKey, PublicKey, ORDER_HALF}, + traits::{Signature as SignatureTrait, *}, +}; +use anyhow::{anyhow, Result}; +use aptos_crypto_derive::{key_name, DeserializeKey, SerializeKey}; +use core::convert::TryFrom; +use p256::NonZeroScalar; +use serde::Serialize; +use signature::Verifier; +use std::{cmp::Ordering, fmt}; + +/// A secp256r1 ECDSA signature. +/// NOTE: The max size on this struct is enforced in its `TryFrom` trait implementation. +#[derive(DeserializeKey, Clone, SerializeKey)] +#[key_name("Secp256r1EcdsaSignature")] +pub struct Signature(pub(crate) p256::ecdsa::Signature); + +impl Signature { + /// The length of the Signature + pub const LENGTH: usize = SIGNATURE_LENGTH; + + /// Serialize an Signature. Uses the SEC1 serialization format. + pub fn to_bytes(&self) -> [u8; SIGNATURE_LENGTH] { + // The RustCrypto P256 `to_bytes` call here should never return a byte array of the wrong length + self.0.to_bytes().into() + } + + /// Deserialize an P256Signature, without checking for malleability + /// Uses the SEC1 serialization format. + pub(crate) fn from_bytes_unchecked( + bytes: &[u8], + ) -> std::result::Result { + match p256::ecdsa::Signature::try_from(bytes) { + Ok(p256_signature) => Ok(Signature(p256_signature)), + Err(_) => Err(CryptoMaterialError::DeserializationError), + } + } + + /// return an all-zero signature (for test only) + #[cfg(any(test, feature = "fuzzing"))] + pub fn dummy_signature() -> Self { + Self::from_bytes_unchecked(&[0u8; Self::LENGTH]).unwrap() + } + + /// Check for correct size and third-party based signature malleability issues. + /// This method is required to ensure that given a valid signature for some message under some + /// key, an attacker cannot produce another valid signature for the same message and key. + /// + /// We use the technique described in + /// [BIP146](https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki) to prevent + /// malleability of ECDSA signatures. Signatures comprise elements {R, S}, and S can be + /// enforced to be of canonical form by ensuring it is less than the order of the Secp256r1 curve + /// divided by 2. If this is not done, a value S > n/2 can be replaced by S' = n - S to form another distinct valid + /// signature, where n is the curve order. This check is not performed by the RustCrypto P256 library + /// we use + pub fn check_s_malleability(bytes: &[u8]) -> std::result::Result<(), CryptoMaterialError> { + if bytes.len() != SIGNATURE_LENGTH { + return Err(CryptoMaterialError::WrongLengthError); + } + if !Signature::check_s_lt_order_half(&bytes[32..]) { + return Err(CryptoMaterialError::CanonicalRepresentationError); + } + Ok(()) + } + + /// Check if S < ORDER_HALF to capture invalid signatures. + fn check_s_lt_order_half(s: &[u8]) -> bool { + for i in 0..32 { + match s[i].cmp(&ORDER_HALF[i]) { + Ordering::Less => return true, + Ordering::Greater => return false, + _ => {} + } + } + // At this stage S == ORDER_HALF which implies a non canonical S. + false + } + + /// If the signature {R,S} does not have S < n/2 where n is the Ristretto255 order, return + /// {R,n-S} as the canonical encoding of this signature to prevent malleability attacks. See + /// `check_s_malleability` for more detail + pub fn make_canonical(&self) -> Signature { + if Signature::check_s_malleability(&self.to_bytes()[..]).is_ok() { + return self.clone(); + }; + let s = self.0.s(); + let r = self.0.r(); + let new_s = -*s; + let new_s_nonzero = NonZeroScalar::new(new_s).unwrap(); + let new_sig = p256::ecdsa::Signature::from_scalars(r, new_s_nonzero).unwrap(); + Signature(new_sig) + } + + /// If signature bytes are serialized correctly, this function will return a canonical signature + /// that passes malleability checks. + #[cfg(feature = "testing")] + pub fn make_canonical_from_bytes_unchecked( + bytes: &[u8], + ) -> Result { + let signature = Signature::from_bytes_unchecked(bytes)?; + Ok(Signature::make_canonical(&signature)) + } +} + +////////////////////// +// Signature Traits // +////////////////////// + +impl SignatureTrait for Signature { + type SigningKeyMaterial = PrivateKey; + type VerifyingKeyMaterial = PublicKey; + + /// Verifies that the provided signature is valid for the provided message, going beyond the + /// [NIST SP 800-186](https://csrc.nist.gov/publications/detail/sp/800-186/final) specification, to prevent scalar malleability as done in [BIP146](https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki). + fn verify(&self, message: &T, public_key: &PublicKey) -> Result<()> { + Self::verify_arbitrary_msg(self, &signing_message(message)?, public_key) + } + + /// Checks that `self` is valid for an arbitrary &[u8] `message` using `public_key`. + /// Outside of this crate, this particular function should only be used for native signature + /// verification in Move. + /// + /// Checks for and rejects non-canonical signatures (r,s) where s > (n/2), where n is the group + /// order + fn verify_arbitrary_msg(&self, message: &[u8], public_key: &PublicKey) -> Result<()> { + Signature::check_s_malleability(&self.to_bytes())?; + + public_key + .0 + .verify(message, &self.0) + .map_err(|e| anyhow!("{}", e)) + .and(Ok(())) + } + + fn to_bytes(&self) -> Vec { + self.0.to_bytes().to_vec() + } +} + +impl Length for Signature { + fn length(&self) -> usize { + SIGNATURE_LENGTH + } +} + +impl ValidCryptoMaterial for Signature { + fn to_bytes(&self) -> Vec { + self.to_bytes().to_vec() + } +} + +impl std::hash::Hash for Signature { + fn hash(&self, state: &mut H) { + let encoded_signature = self.to_bytes(); + state.write(&encoded_signature); + } +} + +impl TryFrom<&[u8]> for Signature { + type Error = CryptoMaterialError; + + fn try_from(bytes: &[u8]) -> std::result::Result { + Signature::check_s_malleability(bytes)?; + Signature::from_bytes_unchecked(bytes) + } +} + +// Those are required by the implementation of hash above +impl PartialEq for Signature { + fn eq(&self, other: &Signature) -> bool { + self.to_bytes()[..] == other.to_bytes()[..] + } +} + +impl Eq for Signature {} + +impl fmt::Display for Signature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", hex::encode(&self.0.to_bytes()[..])) + } +} + +impl fmt::Debug for Signature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "secp256r1_ecdsa::Signature({})", self) + } +} diff --git a/crates/aptos-crypto/src/test_utils.rs b/crates/aptos-crypto/src/test_utils.rs new file mode 100644 index 0000000..d9976c1 --- /dev/null +++ b/crates/aptos-crypto/src/test_utils.rs @@ -0,0 +1,313 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//! Internal module containing convenience utility functions mainly for testing + +use crate::traits::Uniform; +use rand::distributions; +use serde::{Deserialize, Serialize}; + +/// A deterministic seed for PRNGs related to keys +pub const TEST_SEED: [u8; 32] = [0u8; 32]; + +/// A keypair consisting of a private and public key +#[cfg_attr(feature = "cloneable-private-keys", derive(Clone))] +#[derive(Serialize, Deserialize, PartialEq, Eq)] +pub struct KeyPair +where + for<'a> P: From<&'a S>, +{ + /// the private key component + pub private_key: S, + /// the public key component + pub public_key: P, +} + +impl From for KeyPair +where + for<'a> P: From<&'a S>, +{ + fn from(private_key: S) -> Self { + KeyPair { + public_key: (&private_key).into(), + private_key, + } + } +} + +impl Uniform for KeyPair +where + S: Uniform, + for<'a> P: From<&'a S>, +{ + fn generate(rng: &mut R) -> Self + where + R: ::rand::RngCore + ::rand::CryptoRng, + { + let private_key = S::generate(rng); + private_key.into() + } +} + +/// A pair consisting of a private and public key +impl Uniform for (S, P) +where + S: Uniform, + for<'a> P: From<&'a S>, +{ + fn generate(rng: &mut R) -> Self + where + R: ::rand::RngCore + ::rand::CryptoRng, + { + let private_key = S::generate(rng); + let public_key = (&private_key).into(); + (private_key, public_key) + } +} + +impl std::fmt::Debug for KeyPair +where + Priv: Serialize, + Pub: Serialize + for<'a> From<&'a Priv>, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut v = bcs::to_bytes(&self.private_key).unwrap(); + v.extend(&bcs::to_bytes(&self.public_key).unwrap()); + write!(f, "{}", hex::encode(&v[..])) + } +} + +#[cfg(any(test, feature = "fuzzing"))] +use crate::signing_message; +#[cfg(any(test, feature = "fuzzing"))] +use curve25519_dalek::constants::EIGHT_TORSION; +#[cfg(any(test, feature = "fuzzing"))] +use curve25519_dalek::edwards::EdwardsPoint; +#[cfg(any(test, feature = "fuzzing"))] +use curve25519_dalek::scalar::Scalar; +#[cfg(any(test, feature = "fuzzing"))] +use curve25519_dalek::traits::Identity; +#[cfg(any(test, feature = "fuzzing"))] +use digest::Digest; +#[cfg(any(test, feature = "fuzzing"))] +use proptest::prelude::*; +use rand::prelude::IteratorRandom; +#[cfg(any(test, feature = "fuzzing"))] +use rand::{rngs::StdRng, SeedableRng}; +#[cfg(any(test, feature = "fuzzing"))] +use sha2::Sha512; + +/// Produces a uniformly random keypair from a seed +#[cfg(any(test, feature = "fuzzing"))] +pub fn uniform_keypair_strategy() -> impl Strategy> +where + Pub: Serialize + for<'a> From<&'a Priv>, + Priv: Serialize + Uniform, +{ + // The no_shrink is because keypairs should be fixed -- shrinking would cause a different + // keypair to be generated, which appears to not be very useful. + any::<[u8; 32]>() + .prop_map(|seed| { + let mut rng = StdRng::from_seed(seed); + KeyPair::::generate(&mut rng) + }) + .no_shrink() +} + +/// Produces a small order group element +#[cfg(any(test, feature = "fuzzing"))] +pub fn small_order_strategy() -> impl Strategy { + (0..EIGHT_TORSION.len()) + .prop_map(|exp| { + let generator = EIGHT_TORSION[1]; // generator of size-8 subgroup is at index 1 + Scalar::from(exp as u64) * generator + }) + .no_shrink() +} + +/// Produces a small order R, public key A and a hash h = H(R, A, m) such that sB - hA = R when s is +/// zero. +#[allow(non_snake_case)] +#[cfg(any(test, feature = "fuzzing"))] +pub fn small_order_pk_with_adversarial_message( +) -> impl Strategy { + ( + small_order_strategy(), + small_order_strategy(), + random_serializable_struct(), + ) + .prop_filter( + "Filtering messages by hash * pk == R", + |(R, pk_point, msg)| { + let pk_bytes = pk_point.compress().to_bytes(); + + let msg_bytes = signing_message(msg).unwrap(); + + let mut h: Sha512 = Sha512::new(); + h.update(R.compress().as_bytes()); + h.update(pk_bytes); + h.update(msg_bytes); + + let k = Scalar::from_hash(h); + + k * pk_point + (*R) == EdwardsPoint::identity() + }, + ) +} + +/// Produces a uniformly random keypair from a seed and the user can alter this sleed slightly. +/// Useful for circumstances where you want two disjoint keypair generations that may interact with +/// each other. +#[cfg(any(test, feature = "fuzzing"))] +pub fn uniform_keypair_strategy_with_perturbation( + perturbation: u8, +) -> impl Strategy> +where + Pub: Serialize + for<'a> From<&'a Priv>, + Priv: Serialize + Uniform, +{ + // The no_shrink is because keypairs should be fixed -- shrinking would cause a different + // keypair to be generated, which appears to not be very useful. + any::<[u8; 32]>() + .prop_map(move |mut seed| { + for elem in seed.iter_mut() { + *elem = elem.saturating_add(perturbation); + } + let mut rng = StdRng::from_seed(seed); + KeyPair::::generate(&mut rng) + }) + .no_shrink() +} + +/// Returns `subset_size` numbers picked uniformly at random from 0 to `max_set_size - 1` (inclusive). +pub fn random_subset(mut rng: &mut R, max_set_size: usize, subset_size: usize) -> Vec +where + R: ::rand::Rng + ?Sized, +{ + let mut vec = (0..max_set_size) + .choose_multiple(&mut rng, subset_size) + .into_iter() + .collect::>(); + + vec.sort_unstable(); + + vec +} + +/// Returns n random bytes. +pub fn random_bytes(rng: &mut R, n: usize) -> Vec +where + R: ::rand::Rng + Copy, +{ + let range = distributions::Uniform::from(0u8..u8::MAX); + rng.sample_iter(&range).take(n).collect() +} + +/// Generates `num_signers` random key-pairs. +pub fn random_keypairs( + mut rng: &mut R, + num_signers: usize, +) -> Vec> +where + R: ::rand::RngCore + ::rand::CryptoRng, + PubKey: for<'a> std::convert::From<&'a PrivKey>, + PrivKey: Uniform, +{ + let mut key_pairs = vec![]; + for _ in 0..num_signers { + key_pairs.push(KeyPair::::generate(&mut rng)); + } + key_pairs +} + +/// This struct provides a means of testing signing and verification through +/// BCS serialization and domain separation +//#[cfg(any(test, feature = "fuzzing"))] +#[derive(Debug, Serialize, Deserialize)] +pub struct TestAptosCrypto(pub String); + +// the following block is macro expanded from derive(CryptoHasher, BCSCryptoHash) + +/// Cryptographic hasher for an BCS-serializable #item +// #[cfg(any(test, feature = "fuzzing"))] +pub struct TestAptosCryptoHasher(crate::hash::DefaultHasher); +// #[cfg(any(test, feature = "fuzzing"))] +impl ::core::clone::Clone for TestAptosCryptoHasher { + #[inline] + fn clone(&self) -> TestAptosCryptoHasher { + match *self { + TestAptosCryptoHasher(ref __self_0_0) => { + TestAptosCryptoHasher(::core::clone::Clone::clone(__self_0_0)) + } + } + } +} +// #[cfg(any(test, feature = "fuzzing"))] +static TEST_CRYPTO_SEED: crate::_once_cell::sync::OnceCell<[u8; 32]> = + crate::_once_cell::sync::OnceCell::new(); +// #[cfg(any(test, feature = "fuzzing"))] +impl TestAptosCryptoHasher { + fn new() -> Self { + let name = crate::_serde_name::trace_name::() + .expect("The `CryptoHasher` macro only applies to structs and enums"); + TestAptosCryptoHasher(crate::hash::DefaultHasher::new(name.as_bytes())) + } +} +// #[cfg(any(test, feature = "fuzzing"))] +static TEST_CRYPTO_HASHER: crate::_once_cell::sync::Lazy = + crate::_once_cell::sync::Lazy::new(TestAptosCryptoHasher::new); +// #[cfg(any(test, feature = "fuzzing"))] +impl std::default::Default for TestAptosCryptoHasher { + fn default() -> Self { + TEST_CRYPTO_HASHER.clone() + } +} +// #[cfg(any(test, feature = "fuzzing"))] +impl crate::hash::CryptoHasher for TestAptosCryptoHasher { + fn seed() -> &'static [u8; 32] { + TEST_CRYPTO_SEED.get_or_init(|| { + let name = crate::_serde_name::trace_name::() + .expect("The `CryptoHasher` macro only applies to structs and enums.") + .as_bytes(); + crate::hash::DefaultHasher::prefixed_hash(name) + }) + } + + fn update(&mut self, bytes: &[u8]) { + self.0.update(bytes); + } + + fn finish(self) -> crate::hash::HashValue { + self.0.finish() + } +} +// #[cfg(any(test, feature = "fuzzing"))] +impl std::io::Write for TestAptosCryptoHasher { + fn write(&mut self, bytes: &[u8]) -> std::io::Result { + self.0.update(bytes); + Ok(bytes.len()) + } + + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } +} +// #[cfg(any(test, feature = "fuzzing"))] +impl crate::hash::CryptoHash for TestAptosCrypto { + type Hasher = TestAptosCryptoHasher; + + fn hash(&self) -> crate::hash::HashValue { + use crate::hash::CryptoHasher; + let mut state = Self::Hasher::default(); + bcs::serialize_into(&mut state, &self) + .expect("BCS serialization of TestAptosCrypto should not fail"); + state.finish() + } +} + +/// Produces a random TestAptosCrypto signable / verifiable struct. +#[cfg(any(test, feature = "fuzzing"))] +pub fn random_serializable_struct() -> impl Strategy { + (String::arbitrary()).prop_map(TestAptosCrypto).no_shrink() +} diff --git a/crates/aptos-crypto/src/traits.rs b/crates/aptos-crypto/src/traits.rs new file mode 100644 index 0000000..e9ccfdc --- /dev/null +++ b/crates/aptos-crypto/src/traits.rs @@ -0,0 +1,328 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//! This module provides a generic set of traits for dealing with cryptographic primitives. +//! +//! For examples on how to use these traits, see the implementations of the [`crate::ed25519`] + +use crate::hash::{CryptoHash, CryptoHasher}; +use anyhow::Result; +use core::convert::{From, TryFrom}; +use rand::{rngs::StdRng, CryptoRng, RngCore, SeedableRng}; +use serde::{de::DeserializeOwned, Serialize}; +use std::{fmt::Debug, hash::Hash}; +use thiserror::Error; + +/// An error type for key and signature validation issues, see [`ValidCryptoMaterial`]. +/// +/// This enum reflects there are two interesting causes of validation +/// failure for the ingestion of key or signature material: deserialization errors +/// (often, due to mangled material or curve equation failure for ECC) and +/// validation errors (material recognizable but unacceptable for use, +/// e.g. unsafe). +#[derive(Clone, Debug, PartialEq, Eq, Error)] +#[error("{:?}", self)] +pub enum CryptoMaterialError { + /// Struct to be signed does not serialize correctly. + SerializationError, + /// Key or signature material does not deserialize correctly. + DeserializationError, + /// Key or signature material deserializes, but is otherwise not valid. + ValidationError, + /// Key, threshold or signature material does not have the expected size. + WrongLengthError, + /// Part of the signature or key is not canonical resulting to malleability issues. + CanonicalRepresentationError, + /// A curve point (i.e., a public key) lies on a small group. + SmallSubgroupError, + /// A curve point (i.e., a public key) does not satisfy the curve equation. + PointNotOnCurveError, + /// BitVec errors in accountable multi-sig schemes. + BitVecError(String), +} + +/// The serialized length of the data that enables macro derived serialization and deserialization. +pub trait Length { + /// The serialized length of the data + fn length(&self) -> usize; +} + +/// Key or more generally crypto material with a notion of byte validation. +/// +/// A type family for material that knows how to serialize and +/// deserialize, as well as validate byte-encoded material. The +/// validation must be implemented as a [`TryFrom`] which +/// classifies its failures against the above +/// [`CryptoMaterialError`]. +/// +/// This provides an implementation for a validation that relies on a +/// round-trip to bytes and corresponding [`TryFrom`]. +pub trait ValidCryptoMaterial: + // The for<'a> exactly matches the assumption "deserializable from any lifetime". + for<'a> TryFrom<&'a [u8], Error=CryptoMaterialError> + Serialize + DeserializeOwned +{ + /// Convert the valid crypto material to bytes. + fn to_bytes(&self) -> Vec; +} + +/// An extension to to/from Strings for [`ValidCryptoMaterial`]. +/// +/// Relies on [`hex`] for string encoding / decoding. +/// No required fields, provides a default implementation. +pub trait ValidCryptoMaterialStringExt: ValidCryptoMaterial { + /// When trying to convert from bytes, we simply decode the string into + /// bytes before checking if we can convert. + fn from_encoded_string(encoded_str: &str) -> std::result::Result { + // Strip 0x at beginning if there is one + let encoded_str = encoded_str.strip_prefix("0x").unwrap_or(encoded_str); + + let bytes_out = ::hex::decode(encoded_str); + // We defer to `try_from` to make sure we only produce valid crypto materials. + bytes_out + // We reinterpret a failure to serialize: key is mangled someway. + .or(Err(CryptoMaterialError::DeserializationError)) + .and_then(|ref bytes| Self::try_from(bytes)) + } + + /// A function to encode into hex-string after serializing. + fn to_encoded_string(&self) -> Result { + Ok(format!("0x{}", ::hex::encode(self.to_bytes()))) + } +} + +// There's nothing required in this extension, so let's just derive it +// for anybody that has a ValidCryptoMaterial. +impl ValidCryptoMaterialStringExt for T {} + +/// A type family for key material that should remain secret and has an +/// associated type of the [`PublicKey`] family. +pub trait PrivateKey: Sized { + /// We require public / private types to be coupled, i.e. their + /// associated type is each other. + type PublicKeyMaterial: PublicKey; + + /// Returns the associated public key + fn public_key(&self) -> Self::PublicKeyMaterial { + self.into() + } +} + +/// A type family of valid keys that know how to sign. +/// +/// This trait has a requirement on a `pub(crate)` marker trait meant to +/// specifically limit its implementations to the present crate. +/// +/// A trait for a [`ValidCryptoMaterial`] which knows how to sign a +/// message, and return an associated `Signature` type. +pub trait SigningKey: + PrivateKey::VerifyingKeyMaterial> + + ValidCryptoMaterial + + private::Sealed +{ + /// The associated verifying key type for this signing key. + type VerifyingKeyMaterial: VerifyingKey; + /// The associated signature type for this signing key. + type SignatureMaterial: Signature; + + /// Signs an object that has an distinct domain-separation hasher and + /// that we know how to serialize. There is no pre-hashing into a + /// `HashValue` to be done by the caller. + /// + /// Note: this assumes serialization is infallible. See crates::bcs::ser + /// for a discussion of this assumption. + fn sign( + &self, + message: &T, + ) -> Result; + + /// Signs a non-hash input message. For testing only. + #[cfg(any(test, feature = "fuzzing"))] + fn sign_arbitrary_message(&self, message: &[u8]) -> Self::SignatureMaterial; + + /// Returns the associated verifying key + fn verifying_key(&self) -> Self::VerifyingKeyMaterial { + self.public_key() + } +} + +/// Returns the signing message for the given message. +/// It is used by `SigningKey#sign` function. +pub fn signing_message( + message: &T, +) -> Result, CryptoMaterialError> { + let mut bytes = ::seed().to_vec(); + bcs::serialize_into(&mut bytes, &message) + .map_err(|_| CryptoMaterialError::SerializationError)?; + Ok(bytes) +} + +/// A type for key material that can be publicly shared, and in asymmetric +/// fashion, can be obtained from a [`PrivateKey`] +/// reference. +/// This convertibility requirement ensures the existence of a +/// deterministic, canonical public key construction from a private key. +pub trait PublicKey: Sized + Clone + Eq + Hash + ValidCryptoMaterial + + // This unsightly turbofish type parameter is the precise constraint + // needed to require that there exists an + // + // ``` + // impl From<&MyPrivateKeyMaterial> for MyPublicKeyMaterial + // ``` + // + // declaration, for any `MyPrivateKeyMaterial`, `MyPublicKeyMaterial` + // on which we register (respectively) `PublicKey` and `PrivateKey` + // implementations. + for<'a> From<&'a ::PrivateKeyMaterial> { + /// We require public / private types to be coupled, i.e. their + /// associated type is each other. + type PrivateKeyMaterial: PrivateKey; +} + +/// A type family of public keys that are used for signing. +/// +/// This trait has a requirement on a `pub(crate)` marker trait meant to +/// specifically limit its implementations to the present crate. +/// +/// It is linked to a type of the Signature family, which carries the +/// verification implementation. +pub trait VerifyingKey: + PublicKey::SigningKeyMaterial> + + ValidCryptoMaterial + + private::Sealed +{ + /// The associated signing key type for this verifying key. + type SigningKeyMaterial: SigningKey; + /// The associated signature type for this verifying key. + type SignatureMaterial: Signature; + + /// We provide the striaghtfoward implementation which dispatches to the signature. + fn verify_struct_signature( + &self, + message: &T, + signature: &Self::SignatureMaterial, + ) -> Result<()> { + signature.verify(message, self) + } + + /// We provide the implementation which dispatches to the signature. + fn batch_verify( + message: &T, + keys_and_signatures: Vec<(Self, Self::SignatureMaterial)>, + ) -> Result<()> { + Self::SignatureMaterial::batch_verify(message, keys_and_signatures) + } +} + +/// A type family for signature material that knows which public key type +/// is needed to verify it, and given such a public key, knows how to +/// verify. +/// +/// This trait simply requires an association to some type of the +/// [`PublicKey`] family of which we are the `SignatureMaterial`. +/// +/// This trait has a requirement on a `pub(crate)` marker trait meant to +/// specifically limit its implementations to the present crate. +/// +/// It should be possible to write a generic signature function that +/// checks signature material passed as `&[u8]` and only returns Ok when +/// that material de-serializes to a signature of the expected concrete +/// scheme. This would be done as an extension trait of +/// [`Signature`]. +pub trait Signature: + for<'a> TryFrom<&'a [u8], Error = CryptoMaterialError> + + Sized + + Debug + + Clone + + Eq + + Hash + + private::Sealed +{ + /// The associated verifying key type for this signature. + type VerifyingKeyMaterial: VerifyingKey; + /// The associated signing key type for this signature + type SigningKeyMaterial: SigningKey; + + /// Verification for a struct we unabmiguously know how to serialize and + /// that we have a domain separation prefix for. + fn verify( + &self, + message: &T, + public_key: &Self::VerifyingKeyMaterial, + ) -> Result<()>; + + /// Native verification function. + fn verify_arbitrary_msg( + &self, + message: &[u8], + public_key: &Self::VerifyingKeyMaterial, + ) -> Result<()>; + + /// Convert the signature into a byte representation. + fn to_bytes(&self) -> Vec; + + /// The implementer can override a batch verification implementation + /// that by default iterates over each signature. More efficient + /// implementations exist and should be implemented for many schemes. + fn batch_verify( + message: &T, + keys_and_signatures: Vec<(Self::VerifyingKeyMaterial, Self)>, + ) -> Result<()> { + for (key, signature) in keys_and_signatures { + signature.verify(message, &key)? + } + Ok(()) + } +} + +/// A type family for schemes which know how to generate key material from +/// a cryptographically-secure [`CryptoRng`]. +pub trait Uniform { + /// Generate key material from a cryptographically-secure RNG. + fn generate(rng: &mut R) -> Self + where + R: RngCore + CryptoRng; + + /// Generate a random key using the shared TEST_SEED + fn generate_for_testing() -> Self + where + Self: Sized, + { + let mut rng: StdRng = SeedableRng::from_seed(crate::test_utils::TEST_SEED); + Self::generate(&mut rng) + } +} + +/// A type family with a by-convention notion of genesis private key. +pub trait Genesis: PrivateKey { + /// Produces the genesis private key. + fn genesis() -> Self; +} + +/// A pub(crate) mod hiding a Sealed trait and its implementations, allowing +/// us to make sure implementations are constrained to the crypto crate. +// See https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed +pub(crate) mod private { + pub trait Sealed {} + + impl Sealed for crate::bls12381::PrivateKey {} + impl Sealed for crate::bls12381::PublicKey {} + impl Sealed for crate::bls12381::Signature {} + impl Sealed for crate::bls12381::ProofOfPossession {} + + impl Sealed for crate::ed25519::Ed25519PrivateKey {} + impl Sealed for crate::ed25519::Ed25519PublicKey {} + impl Sealed for crate::ed25519::Ed25519Signature {} + + impl Sealed for crate::multi_ed25519::MultiEd25519PrivateKey {} + impl Sealed for crate::multi_ed25519::MultiEd25519PublicKey {} + impl Sealed for crate::multi_ed25519::MultiEd25519Signature {} + + impl Sealed for crate::secp256r1_ecdsa::PrivateKey {} + impl Sealed for crate::secp256r1_ecdsa::PublicKey {} + impl Sealed for crate::secp256r1_ecdsa::Signature {} + + impl Sealed for crate::secp256k1_ecdsa::PrivateKey {} + impl Sealed for crate::secp256k1_ecdsa::PublicKey {} + impl Sealed for crate::secp256k1_ecdsa::Signature {} +} diff --git a/crates/aptos-crypto/src/unit_tests/bcs_test.rs b/crates/aptos-crypto/src/unit_tests/bcs_test.rs new file mode 100644 index 0000000..ae9086d --- /dev/null +++ b/crates/aptos-crypto/src/unit_tests/bcs_test.rs @@ -0,0 +1,110 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + ed25519::{ + Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature, ED25519_PRIVATE_KEY_LENGTH, + ED25519_PUBLIC_KEY_LENGTH, ED25519_SIGNATURE_LENGTH, + }, + multi_ed25519::{MultiEd25519PrivateKey, MultiEd25519PublicKey, MultiEd25519Signature}, + test_utils::{TestAptosCrypto, TEST_SEED}, + Signature, SigningKey, Uniform, +}; +use rand::{rngs::StdRng, SeedableRng}; +use std::convert::TryFrom; + +#[test] +fn ed25519_bcs_material() { + use std::borrow::Cow; + + let private_key = + Ed25519PrivateKey::try_from([1u8; ED25519_PRIVATE_KEY_LENGTH].as_ref()).unwrap(); + let public_key = Ed25519PublicKey::from(&private_key); + + let serialized_public_key = bcs::to_bytes(&Cow::Borrowed(&public_key)).unwrap(); + // Expected size should be 1 byte due to BCS length prefix + 32 bytes for the raw key bytes + assert_eq!(serialized_public_key.len(), 1 + ED25519_PUBLIC_KEY_LENGTH); + + // Ensure public key serialization - deserialization is stable and deterministic + let deserialized_public_key: Ed25519PublicKey = + bcs::from_bytes(&serialized_public_key).unwrap(); + assert_eq!(deserialized_public_key, public_key); + + let message = TestAptosCrypto("Hello, World".to_string()); + let signature: Ed25519Signature = private_key.sign(&message).unwrap(); + + let serialized_signature = bcs::to_bytes(&Cow::Borrowed(&signature)).unwrap(); + // Expected size should be 1 byte due to BCS length prefix + 64 bytes for the raw signature bytes + assert_eq!(serialized_signature.len(), 1 + ED25519_SIGNATURE_LENGTH); + + // Ensure signature serialization - deserialization is stable and deterministic + let deserialized_signature: Ed25519Signature = bcs::from_bytes(&serialized_signature).unwrap(); + assert_eq!(deserialized_signature, signature); + + // Verify signature + let verified_signature = signature.verify(&message, &public_key); + assert!(verified_signature.is_ok()) +} + +#[test] +fn multi_ed25519_bcs_material() { + use std::borrow::Cow; + + // Helper function to generate N ed25519 private keys. + fn generate_keys(n: usize) -> Vec { + let mut rng = StdRng::from_seed(TEST_SEED); + (0..n) + .map(|_| Ed25519PrivateKey::generate(&mut rng)) + .collect() + } + + let num_of_keys = 10; + let threshold = 7; + let private_keys_10 = generate_keys(num_of_keys); + let multi_private_key_7of10 = MultiEd25519PrivateKey::new(private_keys_10, threshold).unwrap(); + let multi_public_key_7of10 = MultiEd25519PublicKey::from(&multi_private_key_7of10); + + let serialized_multi_public_key = + bcs::to_bytes(&Cow::Borrowed(&multi_public_key_7of10)).unwrap(); + + // Expected size due to specialization is + // 2 bytes for BCS length prefix (due to ULEB128) + // + 10 * single_pub_key_size bytes (each key is the compressed Edwards Y coordinate) + // + 1 byte for the threshold + assert_eq!( + serialized_multi_public_key.len(), + 2 + num_of_keys * ED25519_PUBLIC_KEY_LENGTH + 1 + ); + + let deserialized_multi_public_key: MultiEd25519PublicKey = + bcs::from_bytes(&serialized_multi_public_key).unwrap(); + assert_eq!(deserialized_multi_public_key, multi_public_key_7of10); + + let message = TestAptosCrypto("Hello, World".to_string()); + + // Verifying a 7-of-10 signature against a public key with the same threshold should pass. + let multi_signature_7of10: MultiEd25519Signature = + multi_private_key_7of10.sign(&message).unwrap(); + + let serialized_multi_signature = bcs::to_bytes(&Cow::Borrowed(&multi_signature_7of10)).unwrap(); + // Expected size due to specialization is + // 2 bytes for BCS length prefix (due to ULEB128) + // + 7 * single_signature_size bytes (each sig is of the form (R,s), + // a 32B compressed Edwards Y coordinate concatenated with a 32B scalar) + // + 4 bytes for the bitmap (the bitmap can hold up to 32 bits) + assert_eq!( + serialized_multi_signature.len(), + 2 + threshold as usize * ED25519_SIGNATURE_LENGTH + 4 + ); + + // Verify bitmap + assert_eq!( + multi_signature_7of10.bitmap(), + &[0b1111_1110, 0u8, 0u8, 0u8] + ); + + // Verify signature + assert!(multi_signature_7of10 + .verify(&message, &multi_public_key_7of10) + .is_ok()); +} diff --git a/crates/aptos-crypto/src/unit_tests/bls12381_test.rs b/crates/aptos-crypto/src/unit_tests/bls12381_test.rs new file mode 100644 index 0000000..2977c39 --- /dev/null +++ b/crates/aptos-crypto/src/unit_tests/bls12381_test.rs @@ -0,0 +1,616 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + bls12381, + bls12381::{PrivateKey, ProofOfPossession, PublicKey}, + test_utils::{random_subset, KeyPair, TestAptosCrypto}, + validatable::{Validatable, Validate}, + Signature, SigningKey, Uniform, +}; +use rand::{distributions::Alphanumeric, Rng}; +use rand_core::OsRng; +use std::{convert::TryFrom, iter::zip}; + +/// Tests that an individual signature share computed correctly on a message m passes verification on m. +/// Tests that a signature share computed on a different message m' fails verification on m. +/// Tests that a signature share fails verification under the wrong public key. +#[test] +fn bls12381_sigshare_verify() { + let mut rng = OsRng; + + let message = b"Hello world"; + let message_wrong = b"Wello Horld"; + + let key_pair = KeyPair::::generate(&mut rng); + let key_pair_wrong = KeyPair::::generate(&mut rng); + + let signature = key_pair.private_key.sign_arbitrary_message(message); + let signature_wrong = key_pair_wrong.private_key.sign_arbitrary_message(message); + + // sig on message under key_pair should verify + assert!(signature + .verify_arbitrary_msg(message, &key_pair.public_key) + .is_ok()); + + // sig_wrong on message under key_pair_wrong should verify + assert!(signature_wrong + .verify_arbitrary_msg(message, &key_pair_wrong.public_key) + .is_ok()); + + // sig on message under keypair should NOT verify under keypair_wrong + assert!(signature + .verify_arbitrary_msg(message, &key_pair_wrong.public_key) + .is_err()); + + // sig on message under keypair should NOT verify on message_wrong under key_pair + assert!(signature + .verify_arbitrary_msg(message_wrong, &key_pair.public_key) + .is_err()); + + // sig on message under keypair_wrong should NOT verify under key_pair + assert!(signature_wrong + .verify_arbitrary_msg(message, &key_pair.public_key) + .is_err()); +} + +/// Tests that a PoP for PK 1 verifies under PK 1. +/// Tests that a PoP for PK 1 does NOT verify under PK 2. +#[test] +fn bls12381_pop_verify() { + let mut rng = OsRng; + + let keypair1 = KeyPair::::generate(&mut rng); + + let keypair2 = KeyPair::::generate(&mut rng); + + // Valid PoP for SK 1 + let pop1 = ProofOfPossession::create_with_pubkey(&keypair1.private_key, &keypair1.public_key); + // Valid PoP for SK 2 + let pop2 = ProofOfPossession::create(&keypair2.private_key); + // Invalid PoP for SK 2 + let pop_bad = + ProofOfPossession::create_with_pubkey(&keypair1.private_key, &keypair2.public_key); + + // PoP for SK i should verify for PK i + assert!(pop1.verify(&keypair1.public_key).is_ok()); + assert!(pop2.verify(&keypair2.public_key).is_ok()); + + // PoP for SK 1 should not verify for PK 2 + assert!(pop1.verify(&keypair2.public_key).is_err()); + // Pop for SK 2 should not verify for PK 1 + assert!(pop2.verify(&keypair1.public_key).is_err()); + // Invalid PoP for SK 2 should not verify + assert!(pop_bad.verify(&keypair2.public_key).is_err()); +} + +/// Generates `num_signers` BLS key-pairs. +fn bls12381_keygen(num_signers: usize, mut rng: &mut OsRng) -> Vec> { + let mut key_pairs = vec![]; + for _ in 0..num_signers { + key_pairs.push(KeyPair::::generate(&mut rng)); + } + key_pairs +} + +/// Returns a 256-character unique string that can be signed by the BLS API. +fn random_message_for_signing(rng: &mut OsRng) -> TestAptosCrypto { + TestAptosCrypto( + rng.sample_iter(&Alphanumeric) + .take(256) + .map(char::from) + .collect::(), + ) +} + +/// Returns several 256-character unique strings that can be aggregate-signed by the BLS API. +fn random_messages_for_signing(rng: &mut OsRng, n: usize) -> Vec { + (0..n) + .map(|_| random_message_for_signing(rng)) + .collect::>() +} + +/// Tests that a multisignature on a message m aggregated from n/2 out of n signers verifies +/// correctly on m but fails verification on a different m'. +#[test] +fn bls12381_multisig_should_verify() { + let mut rng = OsRng; + + let message = random_message_for_signing(&mut rng); + let message_wrong = random_message_for_signing(&mut rng); + + let num_signers = 1000; + let key_pairs = bls12381_keygen(num_signers, &mut rng); + + let mut signatures = vec![]; + let mut pubkeys: Vec<&PublicKey> = vec![]; + + let good_step = 2; + for keys in key_pairs.iter().step_by(good_step) { + let signature = keys.private_key.sign(&message).unwrap(); + signatures.push(signature); + pubkeys.push(&keys.public_key); + } + + let multisig = bls12381::Signature::aggregate(signatures).unwrap(); + let aggpk = PublicKey::aggregate(pubkeys).unwrap(); + + // multisig should verify on the correct message under the correct aggregate PK + assert!(multisig.verify(&message, &aggpk).is_ok()); + + // multisig should not verify on an incorrect message under the correct aggregate PK + assert!(multisig.verify(&message_wrong, &aggpk).is_err()); +} + +/// Tests signature (de)serialization +#[test] +fn bls12381_serialize_sig() { + let mut rng = OsRng; + let message = b"Hello world"; + let key_pair = KeyPair::::generate(&mut rng); + let signature = key_pair.private_key.sign_arbitrary_message(message); + + let sig_bytes = signature.to_bytes(); + + let signature_deserialized = bls12381::Signature::try_from(&sig_bytes[..]).unwrap(); + + assert_eq!(signature, signature_deserialized); +} + +/// Tests that an aggregate signature on `n` different messages verifies correctly. +#[test] +fn bls12381_aggsig_should_verify() { + let mut rng = OsRng; + let num_signers = 1000; + + let messages = random_messages_for_signing(&mut rng, num_signers); + let messages_wrong = random_messages_for_signing(&mut rng, num_signers); + + let key_pairs = bls12381_keygen(num_signers, &mut rng); + + let mut signatures = vec![]; + let mut pubkeys: Vec<&PublicKey> = vec![]; + + for i in 0..num_signers { + let msg = &messages[i]; + let key = &key_pairs[i]; + + signatures.push(key.private_key.sign(msg).unwrap()); + pubkeys.push(&key.public_key); + } + + let aggsig = bls12381::Signature::aggregate(signatures).unwrap(); + + // aggsig should verify on the correct messages under the correct PKs + let msgs_refs = messages.iter().collect::>(); + assert!(aggsig.verify_aggregate(&msgs_refs, &pubkeys).is_ok()); + + // aggsig should NOT verify on incorrect messages under the correct PKs + let msgs_wrong_refs = messages_wrong.iter().collect::>(); + assert!(aggsig.verify_aggregate(&msgs_wrong_refs, &pubkeys).is_err()); +} + +/// Tests that an aggregate signature on 0 messages or PKs does NOT verify. +#[test] +fn bls12381_aggsig_zero_messages_or_pks_does_not_verify() { + let mut rng = OsRng; + + let message = random_message_for_signing(&mut rng); + + let key_pair = KeyPair::::generate(&mut rng); + + let aggsig = + bls12381::Signature::aggregate(vec![key_pair.private_key.sign(&message).unwrap()]).unwrap(); + + // aggsig should NOT verify on zero messages and zero PKs + let pubkeys: Vec<&PublicKey> = vec![]; + let messages = vec![]; + let msgs_refs = messages.iter().collect::>(); + assert!(aggsig.verify_aggregate(&msgs_refs, &pubkeys).is_err()); + + // aggsig should NOT verify on zero PKs + let pubkeys: Vec<&PublicKey> = vec![]; + let messages = vec![message]; + let msgs_refs = messages.iter().collect::>(); + assert!(aggsig.verify_aggregate(&msgs_refs, &pubkeys).is_err()); + + // aggsig should NOT verify on zero messages + let pubkeys: Vec<&PublicKey> = vec![&key_pair.public_key]; + let messages = vec![]; + let msgs_refs = messages.iter().collect::>(); + assert!(aggsig.verify_aggregate(&msgs_refs, &pubkeys).is_err()); +} + +/// Tests that a multisignature incorrectly aggregated from signature shares on different messages does +/// NOT verify. +#[test] +fn bls12381_multisig_wrong_messages_aggregated() { + let mut rng = OsRng; + + let message = random_message_for_signing(&mut rng); + let message_wrong = random_message_for_signing(&mut rng); + + let num_signers = 500; + let key_pairs = bls12381_keygen(num_signers, &mut rng); + assert_eq!(key_pairs.len(), num_signers); + + let mut signatures = vec![]; + let mut pubkeys: Vec<&PublicKey> = vec![]; + + for (i, key_pair) in key_pairs.iter().enumerate() { + let signature = if i % 2 == 0 { + key_pair.private_key.sign(&message).unwrap() + } else { + key_pair.private_key.sign(&message_wrong).unwrap() + }; + signatures.push(signature); + pubkeys.push(&key_pair.public_key); + } + + let multisig = bls12381::Signature::aggregate(signatures).unwrap(); + let aggpk = PublicKey::aggregate(pubkeys).unwrap(); + + // multisig should NOT verify on any of the messages, because it is actually not a multisig: + // i.e., it is not an aggregate signature on a single message + assert!(multisig.verify(&message, &aggpk).is_err()); + assert!(multisig.verify(&message_wrong, &aggpk).is_err()); +} + +/// Returns two different sets of signer IDs (i.e., numbers in 0..num_signers) +pub fn random_different_signer_sets( + rng: &mut OsRng, + num_signers: usize, + subset_size: usize, +) -> (Vec, Vec) { + let signers1 = random_subset(rng, num_signers, subset_size); + let mut signers2 = random_subset(rng, num_signers, subset_size); + + while signers1 == signers2 { + signers2 = random_subset(rng, num_signers, subset_size); + } + + (signers1, signers2) +} + +/// Tests that a multisig aggregated from a set of signers A does not verify under a public key +/// aggregated from a different set B of signers. +#[test] +fn bls12381_multisig_wrong_pks_aggregated() { + let mut rng = OsRng; + + let message1 = random_message_for_signing(&mut rng); + let message2 = random_message_for_signing(&mut rng); + + let num_signers = 1000; + let key_pairs = bls12381_keygen(num_signers, &mut rng); + assert_eq!(key_pairs.len(), num_signers); + + let (signers1, signers2) = random_different_signer_sets(&mut rng, num_signers, num_signers / 2); + + let mut signatures1 = vec![]; + let mut signatures2 = vec![]; + let mut pubkeys1 = vec![]; + let mut pubkeys2 = vec![]; + + for (i1, i2) in zip(signers1, signers2) { + signatures1.push(key_pairs[i1].private_key.sign(&message1).unwrap()); + signatures2.push(key_pairs[i2].private_key.sign(&message2).unwrap()); + + pubkeys1.push(&key_pairs[i1].public_key); + pubkeys2.push(&key_pairs[i2].public_key); + } + assert_ne!(signatures1.len(), 0); + assert_ne!(signatures2.len(), 0); + + let multisig1 = bls12381::Signature::aggregate(signatures1).unwrap(); + let multisig2 = bls12381::Signature::aggregate(signatures2).unwrap(); + let aggpk1 = PublicKey::aggregate(pubkeys1).unwrap(); + let aggpk2 = PublicKey::aggregate(pubkeys2).unwrap(); + + // first, make sure multisig1 (and multisig2) verify on message1 (and on message2) under aggpk1 (and aggpk2, respectively) + assert!(multisig1.verify(&message1, &aggpk1).is_ok()); + assert!(multisig2.verify(&message2, &aggpk2).is_ok()); + + // second, make sure multisig1 doesn't verify against multisig2's signer set (and viceversa) + assert!(multisig1.verify(&message1, &aggpk2).is_err()); + assert!(multisig2.verify(&message2, &aggpk1).is_err()); + + // ...and try swapping the messages too + assert!(multisig1.verify(&message2, &aggpk2).is_err()); + assert!(multisig2.verify(&message1, &aggpk1).is_err()); +} + +/// Tests that a randomly generated multisig does not verify under a randomly generated PK. +#[test] +fn bls12381_random_multisig_dont_verify_with_random_pk() { + let mut rng = OsRng; + + let message = random_message_for_signing(&mut rng); + let keypair = KeyPair::::generate(&mut rng); + let keypair_junk = KeyPair::::generate(&mut rng); + + let signature = keypair.private_key.sign(&message).unwrap(); + + assert!(signature.verify(&message, &keypair.public_key).is_ok()); + + assert!(signature + .verify(&message, &keypair_junk.public_key) + .is_err()); +} + +#[test] +fn bls12381_validatable_pk() { + let mut rng = OsRng; + + // Test that prime-order points pass the validate() call + let keypair = KeyPair::::generate(&mut rng); + let pk_bytes = keypair.public_key.to_bytes(); + + let validatable = Validatable::from_validated(keypair.public_key); + + assert!(validatable.validate().is_ok()); + assert_eq!(validatable.validate().unwrap().to_bytes(), pk_bytes); + + // Test that low-order points don't pass the validate() call + // + // Low-order points were sampled from bls12_381 crate (https://github.com/zkcrypto/bls12_381/blob/main/src/g1.rs) + // - The first point was convereted from projective to affine coordinates and serialized via `point.to_affine().to_compressed()`. + // - The second point was in affine coordinates and serialized via `a.to_compressed()`. + let low_order_points = [ + "ae3cd9403b69c20a0d455fd860e977fe6ee7140a7f091f26c860f2caccd3e0a7a7365798ac10df776675b3a67db8faa0", + "928d4862a40439a67fd76a9c7560e2ff159e770dcf688ff7b2dd165792541c88ee76c82eb77dd6e9e72c89cbf1a56a68", + ]; + + for p in low_order_points { + let point = hex::decode(p).unwrap(); + assert_eq!(point.len(), PublicKey::LENGTH); + + let pk = PublicKey::try_from(point.as_slice()).unwrap(); + + // First, make sure group_check() identifies this point as a low-order point + assert!(pk.subgroup_check().is_err()); + + // Second, make sure our Validatable implementation agrees with group_check + let validatable = Validatable::::from_unvalidated(pk.to_unvalidated()); + assert!(validatable.validate().is_err()); + } +} + +#[test] +#[ignore] +/// Not an actual test: only used to generate test cases for testing the BLS Move module in +/// aptos-move/framework/move-stdlib/sources/signer.move +fn bls12381_sample_pop() { + let mut rng = OsRng; + + let num = 5; + + let mut kps = vec![]; + + for _i in 1..=num { + kps.push(KeyPair::::generate(&mut rng)); + } + + println!("let pks = vector["); + for kp in &kps { + println!(" x\"{}\",", kp.public_key); + } + println!("];\n"); + + println!("let pops = vector["); + for kp in &kps { + println!(" x\"{}\",", ProofOfPossession::create(&kp.private_key)); + } + println!("];\n"); +} + +#[test] +#[ignore] +/// Not an actual test: only used to generate test cases for testing the BLS Move module in +/// aptos-move/framework/move-stdlib/sources/signer.move +/// For simplicity, we use `sign_arbitrary_message` to generate a signature directly on a +/// message `m` rather than on its hash derived using the `CryptoHasher` trait. This makes it easier +/// to verify the signature in our Move code, which uses `verify_arbitrary_message`. +fn bls12381_sample_signature() { + let mut rng = OsRng; + + let keypair = KeyPair::::generate(&mut rng); + let sk = keypair.private_key; + let pk = keypair.public_key; + + let message = b"Hello Aptos!"; + let signature = sk.sign_arbitrary_message(message); + + println!("SK: {}", hex::encode(sk.to_bytes())); + println!("PK: {}", hex::encode(pk.to_bytes())); + println!("Message: {}", std::str::from_utf8(message).unwrap()); + println!("Signature: {}", hex::encode(signature.to_bytes())); +} + +#[test] +/// Tests deserialization and verification of a signature generated for the Move submodule via +/// `bls12381_sample_signature` above. +fn bls12381_sample_signature_verifies() { + let pk = PublicKey::try_from( + hex::decode( + "94209a296b739577cb076d3bfb1ca8ee936f29b69b7dae436118c4dd1cc26fd43dcd16249476a006b8b949bf022a7858" + ).unwrap().as_slice() + ).unwrap(); + + let sig = bls12381::Signature::try_from( + hex::decode( + "b01ce4632e94d8c611736e96aa2ad8e0528a02f927a81a92db8047b002a8c71dc2d6bfb94729d0973790c10b6ece446817e4b7543afd7ca9a17c75de301ae835d66231c26a003f11ae26802b98d90869a9e73788c38739f7ac9d52659e1f7cf7" + ).unwrap().as_slice() + ).unwrap(); + + assert!(sig.verify_arbitrary_msg(b"Hello Aptos!", &pk).is_ok()); +} + +#[test] +#[ignore] +fn bls12381_sample_doc_test_for_normal_sigs() { + let mut rng = OsRng; + + // A signer locally generated their own BLS key-pair via: + let kp = KeyPair::::generate(&mut rng); + + // The signer computes a normal signature on a message. + let message = TestAptosCrypto("test".to_owned()); + let sig = kp.private_key.sign(&message).unwrap(); + + // Any verifier who has the signer's public key can verify the `(message, sig)` pair as: + assert!(sig.verify(&message, &kp.public_key).is_ok()); + + println!( + "let sk_bytes = hex::decode(\"{}\").unwrap();", + hex::encode(kp.private_key.to_bytes()) + ); + println!( + "let pk_bytes = hex::decode(\"{}\").unwrap();", + hex::encode(kp.public_key.to_bytes()) + ); + println!("// signature on TestAptosCrypto(\"test\".to_owned())"); + println!( + "let sig_bytes = hex::decode(\"{}\").unwrap();", + hex::encode(sig.to_bytes()) + ); +} + +#[test] +#[ignore] +/// Not an actual test: only used to generate test cases for testing the BLS Move module in +/// aptos-move/framework/move-stdlib/sources/signer.move +fn bls12381_sample_aggregate_pk_and_aggsig() { + let mut rng = OsRng; + + let num = 5; + let mut messages = vec![]; + + let mut pks = vec![]; + let mut sigs = vec![]; + let mut aggsigs = vec![]; + + for i in 1..=num { + let msg = format!("Hello, Aptos {}!", i); + let keypair = KeyPair::::generate(&mut rng); + + messages.push(msg); + pks.push(keypair.public_key); + sigs.push( + keypair + .private_key + .sign_arbitrary_message(messages.last().unwrap().as_bytes()), + ); + + aggsigs.push(bls12381::Signature::aggregate(sigs.clone()).unwrap()); + } + + println!( + "// The signed messages are \"Hello, Aptos !\", where \\in {{1, ..., {}}}", + num + ); + println!("let msgs = vector["); + for m in messages { + println!(" x\"{}\",", hex::encode(m.as_bytes())); + } + println!("];\n"); + + println!("// Public key of signer i"); + println!("let pks = vector["); + for pk in pks { + println!(" x\"{}\",", pk); + } + println!("];\n"); + + println!("// aggsigs[i] = \\sum_{{j <= i}} sigs[j], where sigs[j] is a signature on msgs[j] under pks[j]"); + println!("let aggsigs = vector["); + for multisig in aggsigs { + println!(" x\"{}\",", multisig); + } + println!("];"); +} + +#[test] +#[ignore] +/// Not an actual test: only used to generate test cases for testing the BLS Move module in +/// aptos-move/framework/move-stdlib/sources/signer.move +fn bls12381_sample_aggregate_pk_and_multisig() { + let mut rng = OsRng; + + let num = 5; + let message = b"Hello, Aptoverse!"; + + let mut pks = vec![]; + let mut agg_pks = vec![]; + let mut sigs = vec![]; + let mut multisigs = vec![]; + + for _i in 1..=num { + let keypair = KeyPair::::generate(&mut rng); + + pks.push(keypair.public_key); + sigs.push(keypair.private_key.sign_arbitrary_message(message)); + + multisigs.push(bls12381::Signature::aggregate(sigs.clone()).unwrap()); + + let pk_refs = pks.iter().collect::>(); + agg_pks.push(PublicKey::aggregate(pk_refs).unwrap()); + } + + println!("// Public keys for each signer i"); + println!("let pks = vector["); + for pk in pks { + println!(" x\"{}\",", pk); + } + println!("];\n"); + + println!("// agg_pks[i] = \\sum_{{j <= i}} pk[j] (i.e., the aggregation of all public keys from 0 to i, inclusive)"); + println!("let agg_pks = vector["); + for aggpk in agg_pks { + println!(" x\"{}\",", aggpk); + } + println!("];\n"); + + println!("// multisigs[i] is a signature on \"Hello, Aptoverse!\" under agg_pks[i]"); + println!("let multisigs = vector["); + for multisig in multisigs { + println!(" x\"{}\",", multisig); + } + println!("];"); +} + +#[test] +#[ignore] +/// Not an actual test: only used to generate test cases for testing the BLS Move module in +/// aptos-move/framework/move-stdlib/sources/signer.move +fn bls12381_sample_aggregate_sigs() { + let mut rng = OsRng; + + let num = 5; + let message = b"Hello, Aptoverse!"; + + let mut sigs = vec![]; + let mut multisigs = vec![]; + + for _i in 1..=num { + let keypair = KeyPair::::generate(&mut rng); + + sigs.push(keypair.private_key.sign_arbitrary_message(message)); + + multisigs.push(bls12381::Signature::aggregate(sigs.clone()).unwrap()); + } + + println!("// Signatures of each signer i"); + println!("let sigs = vector["); + for sig in sigs { + println!(" signature_from_bytes(x\"{}\"),", sig); + } + println!("];\n"); + + println!("// multisigs[i] is a signature on \"Hello, Aptoverse!\" from signers 1 through i (inclusive)"); + println!("let multisigs = vector["); + for multisig in multisigs { + println!(" AggrOrMultiSignature {{ bytes: x\"{}\" }},", multisig); + } + println!("];"); +} diff --git a/crates/aptos-crypto/src/unit_tests/bulletproofs_test.rs b/crates/aptos-crypto/src/unit_tests/bulletproofs_test.rs new file mode 100644 index 0000000..0343fa5 --- /dev/null +++ b/crates/aptos-crypto/src/unit_tests/bulletproofs_test.rs @@ -0,0 +1,100 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::bulletproofs::MAX_RANGE_BITS; +use bulletproofs::{BulletproofGens, PedersenGens, RangeProof}; +use curve25519_dalek_ng::{ristretto::CompressedRistretto, scalar::Scalar}; +use merlin::Transcript; +use rand::{thread_rng, Rng}; +use std::convert::TryFrom; + +const TEST_DOMAIN_SEPARATION_TAG: &[u8; 21] = b"AptosTestBulletproofs"; + +#[test] +#[ignore] +pub fn test_generated_bulletproof_verifies() { + let mut rng = thread_rng(); + + let pg = PedersenGens::default(); + let bg = BulletproofGens::new(64, 1); + + // Pick a random value and a Pedersen commitment blinder for it + let value = rng.gen_range(0u64, (2u128.pow(MAX_RANGE_BITS as u32) - 1u128) as u64); + let value_scalar = Scalar::from(value); + let blinder = Scalar::hash_from_bytes::(b"some random blinder"); + + // Compute a range proof for 'value' committed with commitment key `pg` and `blinder` + let mut t_prv = Transcript::new(TEST_DOMAIN_SEPARATION_TAG); + let range_proof = + RangeProof::prove_single(&bg, &pg, &mut t_prv, value, &blinder, MAX_RANGE_BITS); + + assert!(range_proof.is_ok()); + + let (range_proof, comm_expected) = range_proof.unwrap(); + + // Make sure the proof passes verification + let comm = pg.commit(value_scalar, blinder); + assert!(comm.eq(&comm_expected.decompress().unwrap())); + + let mut t_ver = Transcript::new(TEST_DOMAIN_SEPARATION_TAG); + let success = range_proof.verify_single(&bg, &pg, &mut t_ver, &comm.compress(), MAX_RANGE_BITS); + + assert!(success.is_ok()); + + println!("Value: {value}"); + println!( + "Value (as Scalar): {}", + hex::encode(value_scalar.to_bytes()) + ); + println!("Blinder: {}", hex::encode(blinder.to_bytes())); + println!("Commitment: {}", hex::encode(comm.compress().to_bytes())); + println!("Range proof: {}", hex::encode(range_proof.to_bytes())); + println!( + "Domain: {}", + String::from_utf8(TEST_DOMAIN_SEPARATION_TAG.to_vec()).unwrap() + ); +} + +#[test] +#[ignore] +pub fn test_valid_bulletproof_verifies() { + // value is 5020644638028926087 + let range_proof_bytes = hex::decode("d8d422d3fb9511d1942b78e3ec1a8c82fe1c01a0a690c55a4761e7e825633a753cca816667d2cbb716fe04a9c199cad748c2d4e59de4ed04fedf5f04f4341a74ae75b63c1997fd65d5fb3a8c03ad8771abe2c0a4f65d19496c11d948d6809503eac4d996f2c6be4e64ebe2df31102c96f106695bdf489dc9290c93b4d4b5411fb6298d0c33afa57e2e1948c38ef567268a661e7b1c099272e29591e717930a06a2c6e0e2d56aedea3078fd59334634f1a4543069865409eba074278f191039083102a9a0621791a9be09212a847e22061e083d7a712b05bca7274b25e4cb1201c679c4957f0842d7661fa1d3f5456a651e89112628b456026f8ad3a7abeaba3fec8031ec8b0392c0aa6c96205f7b21b0c2d6b5d064bd5bd1a1d91c41625d910688fa0dca35ec0f0e31a45792f8d6a330be970a22e1e0773111a083de893c89419ee7de97295978de90bcdf873a2826746809e64f9143417dbed09fa1c124e673febfed65c137cc45fabda963c96b64645802d1440cba5e58717e539f55f3321ab0c0f60410fba70070c5db500fee874265a343a2a59773fd150bcae09321a5166062e176e2e76bef0e3dd1a9250bcb7f4c971c10f0b24eb2a94e009b72c1fc21ee4267881e27b4edba8bed627ddf37e0c53cd425bc279d0c50d154d136503e54882e9541820d6394bd52ca2b438fd8c517f186fec0649c4846c4e43ce845d80e503dee157ce55392188039a7efc78719107ab989db8d9363b9dfc1946f01a84dbca5e742ed5f30b07ac61cf17ce2cf2c6a49d799ed3968a63a3ccb90d9a0e50960d959f17f202dd5cf0f2c375a8a702e063d339e48c0227e7cf710157f63f13136d8c3076c672ea2c1028fc1825366a145a4311de6c2cc46d3144ae3d2bc5808819b9817be3fce1664ecb60f74733e75e97ca8e567d1b81bdd4c56c7a340ba00").unwrap(); + let range_proof = RangeProof::from_bytes(range_proof_bytes.as_slice()).unwrap(); + + let comm_bytes = + hex::decode("0a665260a4e42e575882c2cdcb3d0febd6cf168834f6de1e9e61e7b2e53dbf14").unwrap(); + let compressed = CompressedRistretto(<[u8; 32]>::try_from(comm_bytes).unwrap()); + let comm = compressed.decompress().unwrap(); + + let pg = PedersenGens::default(); + let bg = BulletproofGens::new(64, 1); + + // scalar is 870c2fa1b2e9ac45000000000000000000000000000000000000000000000000 + let value = 5020644638028926087u64; + let blinder = <[u8; 32]>::try_from( + hex::decode("e7c7b42b75503bfc7b1932783786d227ebf88f79da752b68f6b865a9c179640c").unwrap(), + ) + .unwrap(); + + assert!(pg + .commit( + Scalar::from(value), + Scalar::from_canonical_bytes(blinder).unwrap() + ) + .eq(&comm)); + + let mut t_ver = Transcript::new(TEST_DOMAIN_SEPARATION_TAG); + let success = range_proof.verify_single(&bg, &pg, &mut t_ver, &comm.compress(), MAX_RANGE_BITS); + + assert!(success.is_ok()); +} + +#[test] +#[ignore] +fn print_rand_base() { + println!( + "Default PedersenGens's blinding factor hex: {}", + hex::encode(PedersenGens::default().B_blinding.compress().to_bytes()) + ); +} diff --git a/crates/aptos-crypto/src/unit_tests/compat_test.rs b/crates/aptos-crypto/src/unit_tests/compat_test.rs new file mode 100644 index 0000000..4395234 --- /dev/null +++ b/crates/aptos-crypto/src/unit_tests/compat_test.rs @@ -0,0 +1,60 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::compat; +use digest::Digest; +use proptest::{collection::vec, prelude::*}; + +// sanity check our compatibility layer by testing some basic SHA3-256 test vectors. +#[test] +fn check_basic_sha3_256_test_vectors() { + let one_million_a = vec![b'a'; 1_000_000]; + + let tests: [(&[u8], &[u8]); 4] = [ + ( + b"", + b"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a", + ), + ( + b"abc", + b"3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", + ), + ( + b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + b"41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376", + ), + ( + &one_million_a, + b"5c8875ae474a3634ba4fd55ec85bffd661f32aca75c6d699d0cdcb6c115891c1", + ), + ]; + + for (input, expected_output) in &tests { + let expected_output = hex::decode(expected_output).unwrap(); + let output1 = compat::Sha3_256::digest(input); + let output2 = sha3::Sha3_256::digest(input); + assert_eq!(&expected_output, &output1.as_slice()); + assert_eq!(&expected_output, &output2.as_slice()); + } +} + +proptest! { + // check that RustCrypto SHA3-256 and our compatibility wrapper over tiny-keccak + // SHA3-256 have the exact same behaviour for random inputs. + #[test] + fn sha3_256_compatibility_test( + updates in vec(vec(any::(), 0..200), 0..10) + ) { + let mut hasher1 = compat::Sha3_256::default(); + let mut hasher2 = sha3::Sha3_256::default(); + + for update in updates { + hasher1.update(&update); + hasher2.update(&update); + + let out1 = hasher1.clone().finalize(); + let out2 = hasher2.clone().finalize(); + assert_eq!(&out1, &out2); + } + } +} diff --git a/crates/aptos-crypto/src/unit_tests/compilation/cross_test.rs b/crates/aptos-crypto/src/unit_tests/compilation/cross_test.rs new file mode 100644 index 0000000..35ff9e5 --- /dev/null +++ b/crates/aptos-crypto/src/unit_tests/compilation/cross_test.rs @@ -0,0 +1,29 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use aptos_crypto::{ + ed25519::{Ed25519PrivateKey, Ed25519PublicKey}, + multi_ed25519::{MultiEd25519PrivateKey, MultiEd25519PublicKey}, + test_utils::KeyPair, + traits::*, +}; +use aptos_crypto_derive::{BCSCryptoHash, CryptoHasher}; +use rand::{prelude::ThreadRng, thread_rng}; +use serde::{Deserialize, Serialize}; + +#[derive(CryptoHasher, BCSCryptoHash, Serialize, Deserialize)] +struct TestTypedSemantics(String); + +fn main() { + let mut csprng: ThreadRng = thread_rng(); + let ed25519_keypair: KeyPair = + KeyPair::generate(&mut csprng); + + let message = TestTypedSemantics(String::from("hello_world")); + let signature = ed25519_keypair.private_key.sign(&message).unwrap(); + + let multi_ed25519_keypair: KeyPair = + KeyPair::generate(&mut csprng); + + signature.verify(&message, &multi_ed25519_keypair.public_key); +} diff --git a/crates/aptos-crypto/src/unit_tests/compilation/cross_test_trait_obj.rs b/crates/aptos-crypto/src/unit_tests/compilation/cross_test_trait_obj.rs new file mode 100644 index 0000000..a7cfa00 --- /dev/null +++ b/crates/aptos-crypto/src/unit_tests/compilation/cross_test_trait_obj.rs @@ -0,0 +1,8 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use aptos_crypto::traits::*; + +fn main() { + let mut l: Vec> = vec![]; +} diff --git a/crates/aptos-crypto/src/unit_tests/compilation/cross_test_trait_obj_pub.rs b/crates/aptos-crypto/src/unit_tests/compilation/cross_test_trait_obj_pub.rs new file mode 100644 index 0000000..41e52a6 --- /dev/null +++ b/crates/aptos-crypto/src/unit_tests/compilation/cross_test_trait_obj_pub.rs @@ -0,0 +1,8 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use aptos_crypto::traits::*; + +fn main() { + let mut l: Vec> = vec![]; +} diff --git a/crates/aptos-crypto/src/unit_tests/compilation/cross_test_trait_obj_sig.rs b/crates/aptos-crypto/src/unit_tests/compilation/cross_test_trait_obj_sig.rs new file mode 100644 index 0000000..a7cfa00 --- /dev/null +++ b/crates/aptos-crypto/src/unit_tests/compilation/cross_test_trait_obj_sig.rs @@ -0,0 +1,8 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use aptos_crypto::traits::*; + +fn main() { + let mut l: Vec> = vec![]; +} diff --git a/crates/aptos-crypto/src/unit_tests/compilation/small_kdf.rs b/crates/aptos-crypto/src/unit_tests/compilation/small_kdf.rs new file mode 100644 index 0000000..2f20bc1 --- /dev/null +++ b/crates/aptos-crypto/src/unit_tests/compilation/small_kdf.rs @@ -0,0 +1,8 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +fn main() { + // Test for ripemd160, output_length < 256 + let ripemd = aptos_crypto::hkdf::Hkdf::::extract(None, &[]); + assert!(ripemd.is_ok()); +} diff --git a/crates/aptos-crypto/src/unit_tests/cross_test.rs b/crates/aptos-crypto/src/unit_tests/cross_test.rs new file mode 100644 index 0000000..f1424c3 --- /dev/null +++ b/crates/aptos-crypto/src/unit_tests/cross_test.rs @@ -0,0 +1,130 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +// This is necessary for the derive macros which rely on being used in a +// context where the crypto crate is external +use crate as aptos_crypto; +use crate::{ + ed25519::{Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature}, + multi_ed25519::{MultiEd25519PrivateKey, MultiEd25519PublicKey, MultiEd25519Signature}, + test_utils::{random_serializable_struct, uniform_keypair_strategy}, + traits::*, +}; +use aptos_crypto_derive::{ + PrivateKey, PublicKey, Signature, SigningKey, SilentDebug, ValidCryptoMaterial, VerifyingKey, +}; +use proptest::prelude::*; +use serde::{Deserialize, Serialize}; + +// Here we aim to make a point about how we can build an enum generically +// on top of a few choice signing scheme types. This enum implements the +// VerifyingKey, SigningKey for precisely the types selected for that enum +// (here, Ed25519(PublicKey|PrivateKey|Signature) and MultiEd25519(...)). +// +// Note that we do not break type safety (see towards the end), and that +// this enum can safely be put into the usual collection (Vec, HashMap). +// + +#[derive( + Serialize, + Deserialize, + Debug, + Clone, + PartialEq, + Eq, + Hash, + ValidCryptoMaterial, + PublicKey, + VerifyingKey, +)] +#[PrivateKeyType = "PrivateK"] +#[SignatureType = "Sig"] +enum PublicK { + Ed(Ed25519PublicKey), + MultiEd(MultiEd25519PublicKey), +} + +#[derive(Serialize, Deserialize, SilentDebug, ValidCryptoMaterial, PrivateKey, SigningKey)] +#[PublicKeyType = "PublicK"] +#[SignatureType = "Sig"] +enum PrivateK { + Ed(Ed25519PrivateKey), + MultiEd(MultiEd25519PrivateKey), +} + +#[allow(clippy::large_enum_variant)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Signature)] +#[PublicKeyType = "PublicK"] +#[PrivateKeyType = "PrivateK"] +enum Sig { + Ed(Ed25519Signature), + MultiEd(MultiEd25519Signature), +} + +/////////////////////////////////////////////////////// +// End of declarations — let's now prove type safety // +/////////////////////////////////////////////////////// +proptest! { + #![proptest_config(ProptestConfig::with_cases(20))] + + #[test] + #[allow(deprecated)] + fn test_keys_mix( + message in random_serializable_struct(), + ed_keypair1 in uniform_keypair_strategy::(), + ed_keypair2 in uniform_keypair_strategy::(), + med_keypair in uniform_keypair_strategy::() + ) { + // this is impossible to write statically, due to the trait not being + // object-safe (voluntarily), see unsupported_sigs below + // let mut l: Vec> = vec![]; + let mut l: Vec = vec![ed_keypair1.private_key]; + let ed_key = l.pop().unwrap(); + let signature = ed_key.sign(&message).unwrap(); + + // This is business as usual + prop_assert!(signature.verify(&message, &ed_keypair1.public_key).is_ok()); + + // signature-publickey mixups are statically impossible, see unsupported_sigs below + let mut l2: Vec = vec![ + PrivateK::MultiEd(med_keypair.private_key), + PrivateK::Ed(ed_keypair2.private_key), + ]; + + let ed_key = l2.pop().unwrap(); + let ed_signature = ed_key.sign(&message).unwrap(); + + // This is still business as usual + let ed_pubkey2 = PublicK::Ed(ed_keypair2.public_key); + let good_sigver = ed_signature.verify(&message, &ed_pubkey2); + prop_assert!(good_sigver.is_ok(), "{:?}", good_sigver); + + // but this still fails, as expected + let med_pubkey = PublicK::MultiEd(med_keypair.public_key); + let bad_sigver = ed_signature.verify(&message, &med_pubkey); + prop_assert!(bad_sigver.is_err(), "{:?}", bad_sigver); + + // And now just in case we're confused again, we pop in the + // reverse direction + let med_key = l2.pop().unwrap(); + let med_signature = med_key.sign(&message).unwrap(); + + // This is still business as usual + let good_sigver = med_signature.verify(&message, &med_pubkey); + prop_assert!(good_sigver.is_ok(), "{:?}", good_sigver); + + // but this still fails, as expected + let bad_sigver = med_signature.verify(&message, &ed_pubkey2); + prop_assert!(bad_sigver.is_err(), "{:?}", bad_sigver); + } +} + +#[test] +#[ignore] +fn unsupported_sigs() { + let t = trybuild::TestCases::new(); + t.compile_fail("src/unit_tests/compilation/cross_test.rs"); + t.compile_fail("src/unit_tests/compilation/cross_test_trait_obj.rs"); + t.compile_fail("src/unit_tests/compilation/cross_test_trait_obj_sig.rs"); + t.compile_fail("src/unit_tests/compilation/cross_test_trait_obj_pub.rs"); +} diff --git a/crates/aptos-crypto/src/unit_tests/cryptohasher.rs b/crates/aptos-crypto/src/unit_tests/cryptohasher.rs new file mode 100644 index 0000000..091f4a6 --- /dev/null +++ b/crates/aptos-crypto/src/unit_tests/cryptohasher.rs @@ -0,0 +1,128 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +//! Test file for the procedural macros CryptoHasher and BCSCryptoHash. + +use crate as aptos_crypto; +use crate::{ + hash::{CryptoHash, CryptoHasher, HASH_PREFIX}, + HashValue, +}; +use aptos_crypto_derive::{BCSCryptoHash, CryptoHasher}; +use serde::{Deserialize, Serialize}; +use tiny_keccak::{Hasher, Sha3}; + +// The expected use case. +#[derive(Serialize, Deserialize, CryptoHasher, BCSCryptoHash)] +pub struct Foo { + a: u64, + b: u32, +} + +// Used for testing the seed in FooHasher. +pub struct Bar {} + +// Complex example with generics and serde-rename. +#[derive(Serialize, Deserialize, CryptoHasher, BCSCryptoHash)] +#[serde(rename = "Foo")] +pub struct Baz { + a: T, + b: u32, +} + +#[derive(Serialize, Deserialize, CryptoHasher, BCSCryptoHash)] +pub struct Duplo { + a: A, + b: B, + c: u32, +} + +impl CryptoHash for Bar { + type Hasher = FooHasher; + + fn hash(&self) -> HashValue { + let state = Self::Hasher::default(); + state.finish() + } +} + +#[test] +fn test_cryptohasher_name() { + let mut salt = HASH_PREFIX.to_vec(); + salt.extend_from_slice(b"Foo"); + + let value = Bar {}; + let expected = { + let mut digest = Sha3::v256(); + digest.update(HashValue::sha3_256_of(&salt[..]).as_ref()); + let mut hasher_bytes = [0u8; 32]; + digest.finalize(&mut hasher_bytes); + hasher_bytes + }; + let actual = CryptoHash::hash(&value); + assert_eq!(&expected, actual.as_ref()); +} + +#[test] +fn test_bcs_cryptohash() { + let mut salt = HASH_PREFIX.to_vec(); + salt.extend_from_slice(b"Foo"); + + let value = Foo { a: 5, b: 1025 }; + let expected = { + let mut digest = Sha3::v256(); + digest.update(HashValue::sha3_256_of(&salt[..]).as_ref()); + digest.update(&bcs::to_bytes(&value).unwrap()); + let mut hasher_bytes = [0u8; 32]; + digest.finalize(&mut hasher_bytes); + hasher_bytes + }; + let actual = CryptoHash::hash(&value); + assert_eq!(&expected, actual.as_ref()); +} + +#[test] +fn test_bcs_cryptohash_with_generics() { + let value = Baz { a: 5u64, b: 1025 }; + let expected = CryptoHash::hash(&Foo { a: 5, b: 1025 }); + let actual = CryptoHash::hash(&value); + assert_eq!(expected, actual); +} + +fn prefixed_sha3(input: &[u8]) -> [u8; 32] { + let mut sha3 = ::tiny_keccak::Sha3::v256(); + let salt: Vec = [HASH_PREFIX, input].concat(); + sha3.update(&salt); + let mut output = [0u8; 32]; + sha3.finalize(&mut output); + output +} + +#[test] +fn test_cryptohasher_salt_access() { + // the salt for this simple struct is expected to be its name + assert_eq!(FooHasher::seed(), &prefixed_sha3(b"Foo")); + assert_eq!(::Hasher::seed(), &prefixed_sha3(b"Foo")); + assert_eq!( + as CryptoHash>::Hasher::seed(), + &prefixed_sha3(b"Foo") + ); + assert_eq!( + as CryptoHash>::Hasher::seed(), + &prefixed_sha3(b"Foo") + ); + assert_eq!(::Hasher::seed(), &prefixed_sha3(b"Foo")); + + assert_eq!( + as CryptoHash>::Hasher::seed(), + &prefixed_sha3(b"Duplo") + ); + + // WARNING: There is no domain separation between `Foo` and `Foo`. This might be on purpose, + // so as to avoid changing the hash when the type of A or B needs to be changed in the code, but + // it means we should exercise extreme caution when using the CryptoHasher derive. + assert_eq!( + as CryptoHash>::Hasher::seed(), + &prefixed_sha3(b"Duplo") + ); +} diff --git a/crates/aptos-crypto/src/unit_tests/ed25519_test.rs b/crates/aptos-crypto/src/unit_tests/ed25519_test.rs new file mode 100644 index 0000000..335f2de --- /dev/null +++ b/crates/aptos-crypto/src/unit_tests/ed25519_test.rs @@ -0,0 +1,658 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +#![allow(clippy::redundant_clone)] // Required to work around prop_assert_eq! limitations + +use crate as aptos_crypto; +use crate::{ + ed25519::{ + Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature, ED25519_PRIVATE_KEY_LENGTH, + ED25519_PUBLIC_KEY_LENGTH, ED25519_SIGNATURE_LENGTH, + }, + test_utils::{ + random_serializable_struct, small_order_pk_with_adversarial_message, + uniform_keypair_strategy, + }, + traits::*, + x25519, +}; +use aptos_crypto_derive::{BCSCryptoHash, CryptoHasher}; +use core::{ + convert::TryFrom, + ops::{Add, Index, IndexMut, Mul, Neg}, +}; +use curve25519_dalek::{ + constants::ED25519_BASEPOINT_POINT, + edwards::{CompressedEdwardsY, EdwardsPoint}, + scalar::Scalar, +}; +use digest::Digest; +use ed25519_dalek::ed25519::signature::Verifier as _; +use proptest::{collection::vec, prelude::*}; +use serde::{Deserialize, Serialize}; +use sha2::Sha512; + +#[derive(CryptoHasher, BCSCryptoHash, Serialize, Deserialize)] +struct CryptoHashable(pub usize); + +// Takes a point in eight_torsion and finds its order +fn eight_torsion_order(ep: EdwardsPoint) -> usize { + let mut pt = ep; + let mut ord = 1; + for _i in 0..8 { + if pt == EdwardsPoint::default() { + break; + } else { + pt = pt.add(ep); + ord += 1; + } + } + ord +} + +proptest! { + #[test] + fn verify_canonicity_torsion(scalar in any::<[u8;32]>(), idx in 0usize..8usize){ + let s = Scalar::from_bytes_mod_order(scalar); + let s_b = ED25519_BASEPOINT_POINT.mul(s); + let torsion_component = CompressedEdwardsY(EIGHT_TORSION[idx]).decompress().unwrap(); + let torsioned = s_b.add(torsion_component); + let torsioned_bytes = torsioned.compress().to_bytes(); + let deserialized = CompressedEdwardsY(torsioned_bytes).decompress().unwrap(); + prop_assert_eq!(deserialized, torsioned); + } + + + #[test] + fn verify_mul_torsion(idx in 0usize..8usize){ + let torsion_component = CompressedEdwardsY(EIGHT_TORSION[idx]).decompress().unwrap(); + let mut order_bytes = [0u8;32]; + order_bytes[..8].copy_from_slice(&(eight_torsion_order(torsion_component)).to_le_bytes()); + let torsion_order = Scalar::from_bits(order_bytes); + + prop_assert_eq!(torsion_component.mul(torsion_order), EdwardsPoint::default()); + } + + // In this test we demonstrate a signature that's not message-bound by only + // modifying the public key and the R component, under a pathological yet + // admissible s < l value for the signature + #[test] + fn verify_sig_malleable_torsion(keypair in uniform_keypair_strategy::(), idx in 0usize..8usize){ + let message = b"hello_world"; + /////////////////////////////////// + // baseline signature components // + /////////////////////////////////// + let pub_key_bytes = keypair.public_key.to_bytes(); + let priv_key_bytes = keypair.private_key.to_bytes(); + let pub_key = ed25519_dalek::PublicKey::from_bytes(&pub_key_bytes).unwrap(); + let secret_key = ed25519_dalek::SecretKey::from_bytes(&priv_key_bytes).unwrap(); + let priv_key = ed25519_dalek::ExpandedSecretKey::from(&secret_key); + let signature = priv_key.sign(&message[..], &pub_key); + prop_assert!(pub_key.verify(&message[..], &signature).is_ok()); + + let torsion_component = CompressedEdwardsY(EIGHT_TORSION[idx]).decompress().unwrap(); + + let mut r_bits = [0u8; 32]; + r_bits.copy_from_slice(&signature.to_bytes()[..32]); + + let r_point = CompressedEdwardsY(r_bits).decompress().unwrap(); + let mixed_r_point = r_point.add(torsion_component); + prop_assert_eq!(r_point.mul_by_cofactor(), mixed_r_point.mul_by_cofactor()); + + let pub_point = CompressedEdwardsY(pub_key_bytes).decompress().unwrap(); + let mixed_pub_point = pub_point.add(torsion_component); + prop_assert_eq!(pub_point.mul_by_cofactor(), mixed_pub_point.mul_by_cofactor()); + + ////////////////////////// + // Compute k = H(R∥A∥m) // + ////////////////////////// + let mut h: Sha512 = Sha512::default(); + h.update(mixed_r_point.compress().to_bytes()); + h.update(mixed_pub_point.compress().to_bytes()); + h.update(message); + // curve25519_dalek is stuck on an old digest version, so we can't do + // Scalar::from_hash + let mut output = [0u8; 64]; + output.copy_from_slice(h.finalize().as_slice()); + let k = Scalar::from_bytes_mod_order_wide(&output); + + ////////////////////////////////////////////////////////////// + // obtain the original r s.t. R = r B, to solve for s later // + ////////////////////////////////////////////////////////////// + let mut expanded_priv_key = [0u8; 64]; + let mut h: Sha512 = Sha512::default(); + h.update(priv_key_bytes); + expanded_priv_key.copy_from_slice(h.finalize().as_slice()); + + let nonce = &expanded_priv_key[32..]; + let mut h: Sha512 = Sha512::default(); + h.update(nonce); + h.update(message); + // curve25519_dalek is stuck on an old digest version, so we can't do + // Scalar::from_hash + let mut output = [0u8; 64]; + output.copy_from_slice(h.finalize().as_slice()); + let original_r = Scalar::from_bytes_mod_order_wide(&output); + + // check r_point = original_r * basepoint + prop_assert_eq!(ED25519_BASEPOINT_POINT.mul(original_r), r_point); + + ////////////////////////////////////////// + // obtain the original a s.t. a * B = A // + ////////////////////////////////////////// + let mut key_bytes = [0u8;32]; + key_bytes.copy_from_slice(&expanded_priv_key[..32]); + key_bytes[0] &= 248; + key_bytes[31] &= 127; + key_bytes[31] |= 64; + let priv_scalar = Scalar::from_bits(key_bytes); + // check pub_point = priv_scalar * basepoint + prop_assert_eq!(ED25519_BASEPOINT_POINT.mul(priv_scalar), pub_point); + + ////////////////////////// + // s = r + k a as usual // + ////////////////////////// + let s = k * priv_scalar + original_r; + prop_assert!(s.is_canonical()); + + ///////////////////////////////////////////////////////////////////////////////// + // Check the cofactored equation (modulo 8) before conversion to dalek formats // + ///////////////////////////////////////////////////////////////////////////////// + let mut eight_scalar_bytes = [0u8;32]; + eight_scalar_bytes[..8].copy_from_slice(&(8_usize).to_le_bytes()); + let eight_scalar = Scalar::from_bits(eight_scalar_bytes); + + let r_candidate_point = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(mixed_pub_point.neg().mul_by_cofactor()), &(s * eight_scalar)); + prop_assert_eq!(mixed_r_point.mul_by_cofactor(), r_candidate_point); + + /////////////////////////////////////////////////////////// + // convert byte strings in dalek terms and do API checks // + /////////////////////////////////////////////////////////// + let mixed_pub_key = ed25519_dalek::PublicKey::from_bytes(&mixed_pub_point.compress().to_bytes()).unwrap(); + // check we would not have caught this mixed order point on PublicKey deserialization + prop_assert!(Ed25519PublicKey::try_from(&mixed_pub_point.compress().to_bytes()[..]).is_ok()); + + let mixed_signature_bits : Vec = [mixed_r_point.compress().to_bytes(), s.to_bytes()].concat(); + // this will error if we don't have 0 ≤ s < l + let mixed_signature = ed25519_dalek::Signature::from_bytes(&mixed_signature_bits).unwrap(); + + // Check, however, that dalek is doing the raw equation check sB = R + kA + let permissive_passes = mixed_pub_key.verify(&message[..], &mixed_signature).is_ok(); + let strict_passes = mixed_pub_key.verify_strict(&message[..], &mixed_signature).is_ok(); + + let torsion_order = eight_torsion_order(torsion_component); + let torsion_components_cancel = torsion_component + k * torsion_component == EdwardsPoint::default(); + + prop_assert!(!permissive_passes || (torsion_order == 1) || torsion_components_cancel, + "bad verification_state permissive passes {} strict passes {} mixed_order {:?} torsion_components_cancel {:?}", + permissive_passes, + strict_passes, + torsion_order, + torsion_components_cancel + ); + } + + // In this test we demonstrate a signature that's transformable by only + // modifying the public key and the R component, under a pathological yet + // admissible s < l value for the signature. It shows the difference + // between `verify` and `verify_strict` in ed25519-dalek + #[test] + fn verify_sig_strict_torsion(idx in 0usize..8usize){ + let message = b"hello_world"; + + // Dalek only performs an order check, so this is allowed + let bad_scalar = Scalar::zero(); + + let bad_component_1 = curve25519_dalek::constants::EIGHT_TORSION[idx]; + let bad_component_2 = bad_component_1.neg(); + + // compute bad_pub_key, bad_signature + let bad_pub_key_point = bad_component_1; // we need this to cancel the hashed component of the verification equation + + // we pick an evil R component + let bad_sig_point = bad_component_2; + + let bad_key = ed25519_dalek::PublicKey::from_bytes(&bad_pub_key_point.compress().to_bytes()).unwrap(); + // This assertion passes because Ed25519PublicKey::TryFrom<&[u8]> no longer checks for small subgroup membership + prop_assert!(Ed25519PublicKey::try_from(&bad_pub_key_point.compress().to_bytes()[..]).is_ok()); + + let bad_signature = ed25519_dalek::Signature::from_bytes(&[ + &bad_sig_point.compress().to_bytes()[..], + &bad_scalar.to_bytes()[..] + ].concat()).unwrap(); + + // Seek k = H(R, A, M) ≡ 1 [8] so that sB - kA = R <=> -kA = -A <=> k mod order(A) = 0 + prop_assume!(bad_key.verify(&message[..], &bad_signature).is_ok()); + prop_assert!(bad_key.verify_strict(&message[..], &bad_signature).is_err()); + } + + + #[test] + fn convert_from_ed25519_publickey(keypair in uniform_keypair_strategy::()) { + let x25519_public_key = x25519::PublicKey::from_ed25519_public_bytes(&keypair.public_key.to_bytes()[..]).unwrap(); + + // Let's construct an x25519 private key from the ed25519 private key. + let x25519_privatekey = x25519::PrivateKey::from_ed25519_private_bytes(&keypair.private_key.to_bytes()[..]); + + // This is the important part! We abandon the entire test if an x25519 private + // key can't be built from this ed25519 private key, thus "grinding" + // the RNG. + if x25519_privatekey.is_ok() { + // Now derive the public key from x25519_privatekey and see if it matches the public key that + // was created from the Ed25519PublicKey. + let x25519_publickey_2 = x25519_privatekey.unwrap().public_key(); + assert_eq!(x25519_public_key, x25519_publickey_2); + } + } + + #[test] + fn ed25519_and_x25519_privkeys(keypair in uniform_keypair_strategy::()){ + let x25519_public_bytes = keypair.public_key.to_bytes(); + let x25519_private_bytes = keypair.private_key.to_bytes(); + //sanity-check + prop_assert_eq!(x25519_public_bytes.clone(), x25519::PublicKey::from(&keypair.private_key).to_bytes()); + + // always pass false if you hope to ever get back to the original public key + let ed25519_public = Ed25519PublicKey::from_x25519_public_bytes(&x25519_public_bytes, false).unwrap(); + let x25519_backconverted_public = x25519::PublicKey::from_ed25519_public_bytes(&ed25519_public.to_bytes()[..]).unwrap(); + + let ed25519_private = Ed25519PrivateKey::try_from(&x25519_private_bytes[..]).unwrap(); + let x25519_backconverted_private = x25519::PrivateKey::try_from(&ed25519_private.to_bytes()[..]).unwrap(); + + // Test that you can always retrieve a valid x25519 keypair after + // "serialization" as an ed25519 keypair + prop_assert_eq!(keypair.public_key, x25519_backconverted_public); + prop_assert_eq!(keypair.private_key, x25519_backconverted_private.clone()); + prop_assert_eq!(x25519_backconverted_public, x25519::PublicKey::from(&x25519_backconverted_private)); + // Note that the reverse is not true: converting to an x25519 private + // key mangles the ed25519 private key bits irreversibly ! + } + + #[test] + fn ed25519_to_x25519_roundtrip(keypair in uniform_keypair_strategy::()){ + let ed25519_bytes = keypair.public_key.to_bytes(); + let x25519 = x25519::PublicKey::from_ed25519_public_bytes(&ed25519_bytes).unwrap(); + let x25519_bytes = x25519.as_slice(); + let backconverted_ed_positive = Ed25519PublicKey::from_x25519_public_bytes(x25519_bytes, false).unwrap(); + let backconverted_ed_negative = Ed25519PublicKey::from_x25519_public_bytes(x25519_bytes, true).unwrap(); + prop_assert!(keypair.public_key == backconverted_ed_negative || keypair.public_key == backconverted_ed_positive); + } + + #[test] + fn test_pub_key_deserialization(bits in any::<[u8; 32]>()){ + let pt_deser = CompressedEdwardsY(bits).decompress(); + let pub_key = Ed25519PublicKey::try_from(&bits[..]); + let check = matches!((pt_deser, pub_key), + (Some(_), Ok(_)) // we agree with Dalek, + | (Some(_), Err(CryptoMaterialError::SmallSubgroupError)) // dalek does not detect pubkeys in a small subgroup, + | (None, Err(CryptoMaterialError::DeserializationError)) // we agree on point decompression failures, + ); + prop_assert!(check); + } + + #[test] + fn test_keys_encode(keypair in uniform_keypair_strategy::()) { + { + let encoded = keypair.private_key.to_encoded_string().unwrap(); + // Hex encoding of a 32-bytes key is 64 (2 x 32) characters. + prop_assert_eq!(2 + 2 * ED25519_PRIVATE_KEY_LENGTH, encoded.len()); + let decoded = Ed25519PrivateKey::from_encoded_string(&encoded); + prop_assert_eq!(Some(keypair.private_key), decoded.ok()); + } + { + let encoded = keypair.public_key.to_encoded_string().unwrap(); + // Hex encoding of a 32-bytes key is 64 (2 x 32) characters. + prop_assert_eq!(2 + 2 * ED25519_PUBLIC_KEY_LENGTH, encoded.len()); + let decoded = Ed25519PublicKey::from_encoded_string(&encoded); + prop_assert_eq!(Some(keypair.public_key), decoded.ok()); + } + } + + #[test] + fn test_batch_verify( + message in random_serializable_struct(), + keypairs in proptest::array::uniform10(uniform_keypair_strategy::()) + ) { + let mut signatures: Vec<(Ed25519PublicKey, Ed25519Signature)> = keypairs.iter().map(|keypair| { + (keypair.public_key.clone(), keypair.private_key.sign(&message).unwrap()) + }).collect(); + prop_assert!(Ed25519Signature::batch_verify(&message, signatures.clone()).is_ok()); + // We swap message and signature for the last element, + // resulting in an incorrect signature + let (key, _sig) = signatures.pop().unwrap(); + let other_sig = signatures.last().unwrap().clone().1; + signatures.push((key, other_sig)); + prop_assert!(Ed25519Signature::batch_verify(&message, signatures).is_err()); + } + + #[test] + fn test_keys_custom_serialisation( + keypair in uniform_keypair_strategy::() + ) { + { + let serialized: &[u8] = &(keypair.private_key.to_bytes()); + prop_assert_eq!(ED25519_PRIVATE_KEY_LENGTH, serialized.len()); + let deserialized = Ed25519PrivateKey::try_from(serialized); + prop_assert_eq!(Some(keypair.private_key), deserialized.ok()); + } + { + let serialized: &[u8] = &(keypair.public_key.to_bytes()); + prop_assert_eq!(ED25519_PUBLIC_KEY_LENGTH, serialized.len()); + let deserialized = Ed25519PublicKey::try_from(serialized); + prop_assert_eq!(Some(keypair.public_key), deserialized.ok()); + } + } + + #[test] + fn test_signature_verification_custom_serialisation( + message in random_serializable_struct(), + keypair in uniform_keypair_strategy::() + ) { + let signature = keypair.private_key.sign(&message).unwrap(); + let serialized: &[u8] = &(signature.to_bytes()); + prop_assert_eq!(ED25519_SIGNATURE_LENGTH, serialized.len()); + let deserialized = Ed25519Signature::try_from(serialized).unwrap(); + prop_assert!(deserialized.verify(&message, &keypair.public_key).is_ok()); + } + + #[test] + fn test_signature_verification_from_arbitrary( + // this should be > 64 bits to go over the length of a default hash + msg in vec(proptest::num::u8::ANY, 1..128), + keypair in uniform_keypair_strategy::() + ) { + let signature = keypair.private_key.sign_arbitrary_message(&msg); + let serialized: &[u8] = &(signature.to_bytes()); + prop_assert_eq!(ED25519_SIGNATURE_LENGTH, serialized.len()); + let deserialized = Ed25519Signature::try_from(serialized).unwrap(); + prop_assert!(deserialized.verify_arbitrary_msg(&msg, &keypair.public_key).is_ok()); + } + + #[test] + fn test_signature_verification_from_struct( + x in any::(), + keypair in uniform_keypair_strategy::() + ) { + let hashable = CryptoHashable(x); + let signature = keypair.private_key.sign(&hashable).unwrap(); + let serialized: &[u8] = &(signature.to_bytes()); + prop_assert_eq!(ED25519_SIGNATURE_LENGTH, serialized.len()); + let deserialized = Ed25519Signature::try_from(serialized).unwrap(); + prop_assert!(deserialized.verify(&hashable, &keypair.public_key).is_ok()); + } + + + // Check for canonical S. + #[test] + fn test_signature_malleability( + message in random_serializable_struct(), + keypair in uniform_keypair_strategy::() + ) { + let signature = keypair.private_key.sign(&message).unwrap(); + let mut serialized = signature.to_bytes(); + let serialized_old = serialized; // implements Copy trait + prop_assert_eq!(serialized_old, serialized); + + let mut r_bytes: [u8; 32] = [0u8; 32]; + r_bytes.copy_from_slice(&serialized[..32]); + + let mut s_bytes: [u8; 32] = [0u8; 32]; + s_bytes.copy_from_slice(&serialized[32..]); + + // ed25519-dalek signing ensures a canonical S value. + let s = Scalar52::from_bytes(&s_bytes); + + // adding L (order of the base point) so that S + L > L + let malleable_s = Scalar52::add(&s, &L); + let malleable_s_bytes = malleable_s.to_bytes(); + // Update the signature (the S part). + serialized[32..].copy_from_slice(&malleable_s_bytes); + + prop_assert_ne!(serialized_old, serialized); + + // Check that malleable signatures will pass verification and deserialization in dalek. + // Construct the corresponding dalek public key. + let _dalek_public_key = ed25519_dalek::PublicKey::from_bytes( + &keypair.public_key.to_bytes() + ).unwrap(); + + // Construct the corresponding dalek Signature. This signature is malleable. + let dalek_sig = ed25519_dalek::Signature::from_bytes(&serialized); + + // ed25519_dalek will (post 2.0) deserialize the malleable + // signature. It does not detect it. + prop_assert!(dalek_sig.is_ok()); + + let msg_bytes = bcs::to_bytes(&message); + prop_assert!(msg_bytes.is_ok()); + + // ed25519_dalek verify will NOT accept the mauled signature + prop_assert!(_dalek_public_key.verify(msg_bytes.as_ref().unwrap(), dalek_sig.as_ref().unwrap()).is_err()); + // ...and ed25519_dalek verify_strict will NOT accept it either + prop_assert!(_dalek_public_key.verify_strict(msg_bytes.as_ref().unwrap(), dalek_sig.as_ref().unwrap()).is_err()); + // ...therefore, neither will our own Ed25519Signature::verify_arbitrary_msg + let sig = Ed25519Signature::from_bytes_unchecked(&serialized).unwrap(); + prop_assert!(sig.verify(&message, &keypair.public_key).is_err()); + + let serialized_malleable: &[u8] = &serialized; + // try_from will fail on malleable signatures. We detect malleable signatures + // early during deserialization. + prop_assert_eq!( + Ed25519Signature::try_from(serialized_malleable), + Err(CryptoMaterialError::CanonicalRepresentationError) + ); + + // We expect from_bytes_unchecked deserialization to succeed, as dalek + // does not check for signature malleability. This method is pub(crate) + // and only used for test purposes. + let sig_unchecked = Ed25519Signature::from_bytes_unchecked(&serialized); + prop_assert!(sig_unchecked.is_ok()); + + // Update the signature by setting S = L to make it invalid. + serialized[32..].copy_from_slice(&L.to_bytes()); + let serialized_malleable_l: &[u8] = &serialized; + // try_from will fail with CanonicalRepresentationError. + prop_assert_eq!( + Ed25519Signature::try_from(serialized_malleable_l), + Err(CryptoMaterialError::CanonicalRepresentationError) + ); + } + + // Test against known small subgroup public keys. + #[allow(non_snake_case)] + #[test] + fn test_publickey_smallorder((R, A, m) in small_order_pk_with_adversarial_message()) { + let pk_bytes = A.compress().to_bytes(); + + // We expect from_bytes to pass in ed25519_dalek, as it does not validate the PK. + let pk_dalek = ed25519_dalek::PublicKey::from_bytes(&pk_bytes); + prop_assert!(pk_dalek.is_ok()); + let pk_dalek = pk_dalek.unwrap(); + + // We expect from_bytes_unchecked to pass, as it does not validate the PK. + let pk = Ed25519PublicKey::from_bytes_unchecked(&pk_bytes); + prop_assert!(pk.is_ok()); + let pk = pk.unwrap(); + + // Ensure the order of the PK is small + prop_assert!(EIGHT_TORSION.len() <= 8); + prop_assert!(eight_torsion_order(A) <= EIGHT_TORSION.len()); + + // Verification checks sB - hA = R. We set s = 0, and we get R + hA = Identity. We set R to + // be a small order element, and all we have to do is find a message with any hash h such + // that R + hA = Identity. + let s = Scalar::zero(); + + let sig_bytes : Vec = [R.compress().to_bytes(), s.to_bytes()].concat(); + let sig_dalek = ed25519_dalek::Signature::from_bytes(&sig_bytes).unwrap(); + + // We expect ed25519-dalek verify to succeed + prop_assert!(pk_dalek.verify(signing_message(&m).unwrap().as_ref(), &sig_dalek).is_ok()); + + // We expect ed25519-dalek verify_strict to fail + prop_assert!(pk_dalek.verify_strict(signing_message(&m).unwrap().as_ref(), &sig_dalek).is_err()); + + // We expect our own validation to fail in Ed25519Signature::verify_arbitrary_msg, since it + // calls ed25519-dalek's verify_strict + let sig = Ed25519Signature::from_bytes_unchecked(sig_bytes.as_ref()).unwrap(); + prop_assert!(pk.verify_struct_signature(&m, &sig).is_err()); + } +} + +// The 8-torsion subgroup E[8]. +// +// In the case of Curve25519, it is cyclic; the i-th element of +// the array is [i]P, where P is a point of order 8 +// generating E[8]. +// +// Thus E[8] is the points indexed by `0,2,4,6`, and +// E[2] is the points indexed by `0,4`. +// +// The following byte arrays have been ported from curve25519-dalek /backend/serial/u64/constants.rs +// and they represent the serialised version of the CompressedEdwardsY points. + +pub const EIGHT_TORSION: [[u8; 32]; 8] = [ + [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + ], + [ + 199, 23, 106, 112, 61, 77, 216, 79, 186, 60, 11, 118, 13, 16, 103, 15, 42, 32, 83, 250, 44, + 57, 204, 198, 78, 199, 253, 119, 146, 172, 3, 122, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 128, + ], + [ + 38, 232, 149, 143, 194, 178, 39, 176, 69, 195, 244, 137, 242, 239, 152, 240, 213, 223, 172, + 5, 211, 198, 51, 57, 177, 56, 2, 136, 109, 83, 252, 5, + ], + [ + 236, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127, + ], + [ + 38, 232, 149, 143, 194, 178, 39, 176, 69, 195, 244, 137, 242, 239, 152, 240, 213, 223, 172, + 5, 211, 198, 51, 57, 177, 56, 2, 136, 109, 83, 252, 133, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + ], + [ + 199, 23, 106, 112, 61, 77, 216, 79, 186, 60, 11, 118, 13, 16, 103, 15, 42, 32, 83, 250, 44, + 57, 204, 198, 78, 199, 253, 119, 146, 172, 3, 250, + ], +]; + +/// The `Scalar52` struct represents an element in +/// ℤ/ℓℤ as 5 52-bit limbs. +pub struct Scalar52(pub [u64; 5]); + +/// `L` is the order of base point, i.e. 2^252 + 27742317777372353535851937790883648493 +pub const L: Scalar52 = Scalar52([ + 0x0002_631A_5CF5_D3ED, + 0x000D_EA2F_79CD_6581, + 0x0000_0000_0014_DEF9, + 0x0000_0000_0000_0000, + 0x0000_1000_0000_0000, +]); + +impl Scalar52 { + /// Return the zero scalar + fn zero() -> Scalar52 { + Scalar52([0, 0, 0, 0, 0]) + } + + /// Unpack a 32 byte / 256 bit scalar into 5 52-bit limbs. + pub fn from_bytes(bytes: &[u8; 32]) -> Scalar52 { + let mut words = [0u64; 4]; + for i in 0..4 { + for j in 0..8 { + words[i] |= u64::from(bytes[(i * 8) + j]) << (j * 8) as u64; + } + } + + let mask = (1u64 << 52) - 1; + let top_mask = (1u64 << 48) - 1; + let mut s = Scalar52::zero(); + + s[0] = words[0] & mask; + s[1] = ((words[0] >> 52) | (words[1] << 12)) & mask; + s[2] = ((words[1] >> 40) | (words[2] << 24)) & mask; + s[3] = ((words[2] >> 28) | (words[3] << 36)) & mask; + s[4] = (words[3] >> 16) & top_mask; + + s + } + + /// Pack the limbs of this `Scalar52` into 32 bytes + pub fn to_bytes(&self) -> [u8; 32] { + let mut s = [0u8; 32]; + + s[0] = self.0[0] as u8; + s[1] = (self.0[0] >> 8) as u8; + s[2] = (self.0[0] >> 16) as u8; + s[3] = (self.0[0] >> 24) as u8; + s[4] = (self.0[0] >> 32) as u8; + s[5] = (self.0[0] >> 40) as u8; + s[6] = ((self.0[0] >> 48) | (self.0[1] << 4)) as u8; + s[7] = (self.0[1] >> 4) as u8; + s[8] = (self.0[1] >> 12) as u8; + s[9] = (self.0[1] >> 20) as u8; + s[10] = (self.0[1] >> 28) as u8; + s[11] = (self.0[1] >> 36) as u8; + s[12] = (self.0[1] >> 44) as u8; + s[13] = self.0[2] as u8; + s[14] = (self.0[2] >> 8) as u8; + s[15] = (self.0[2] >> 16) as u8; + s[16] = (self.0[2] >> 24) as u8; + s[17] = (self.0[2] >> 32) as u8; + s[18] = (self.0[2] >> 40) as u8; + s[19] = ((self.0[2] >> 48) | (self.0[3] << 4)) as u8; + s[20] = (self.0[3] >> 4) as u8; + s[21] = (self.0[3] >> 12) as u8; + s[22] = (self.0[3] >> 20) as u8; + s[23] = (self.0[3] >> 28) as u8; + s[24] = (self.0[3] >> 36) as u8; + s[25] = (self.0[3] >> 44) as u8; + s[26] = self.0[4] as u8; + s[27] = (self.0[4] >> 8) as u8; + s[28] = (self.0[4] >> 16) as u8; + s[29] = (self.0[4] >> 24) as u8; + s[30] = (self.0[4] >> 32) as u8; + s[31] = (self.0[4] >> 40) as u8; + + s + } + + /// Compute `a + b` (without mod ℓ) + pub fn add(a: &Scalar52, b: &Scalar52) -> Scalar52 { + let mut sum = Scalar52::zero(); + let mask = (1u64 << 52) - 1; + + // a + b + let mut carry: u64 = 0; + for i in 0..5 { + carry = a[i] + b[i] + (carry >> 52); + sum[i] = carry & mask; + } + + sum + } +} + +impl Index for Scalar52 { + type Output = u64; + + fn index(&self, _index: usize) -> &u64 { + &(self.0[_index]) + } +} + +impl IndexMut for Scalar52 { + fn index_mut(&mut self, _index: usize) -> &mut u64 { + &mut (self.0[_index]) + } +} diff --git a/crates/aptos-crypto/src/unit_tests/hash_test.rs b/crates/aptos-crypto/src/unit_tests/hash_test.rs new file mode 100644 index 0000000..7be5d92 --- /dev/null +++ b/crates/aptos-crypto/src/unit_tests/hash_test.rs @@ -0,0 +1,248 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::hash::*; +use bitvec::prelude::*; +use proptest::{collection::vec, prelude::*}; +use rand::{rngs::StdRng, Rng, SeedableRng}; +use serde::Serialize; +use std::str::FromStr; + +#[derive(Serialize)] +struct Foo(u32); + +#[test] +fn test_default_hasher() { + assert_eq!( + Foo(3).test_only_hash(), + HashValue::from_iter_sha3(vec![bcs::to_bytes(&Foo(3)).unwrap().as_slice()]), + ); + assert_eq!( + format!("{:x}", b"hello".test_only_hash()), + "3338be694f50c5f338814986cdf0686453a888b84f424d792af4b9202398f392", + ); + assert_eq!( + format!("{:x}", b"world".test_only_hash()), + "420baf620e3fcd9b3715b42b92506e9304d56e02d3a103499a3a292560cb66b2", + ); +} + +#[test] +fn test_primitive_type() { + let x = 0xF312_u16; + let mut wtr: Vec = vec![]; + wtr.extend_from_slice(&x.to_le_bytes()); + assert_eq!(x.test_only_hash(), HashValue::sha3_256_of(&wtr[..])); + + let x = 0xFF00_1234_u32; + let mut wtr: Vec = vec![]; + wtr.extend_from_slice(&x.to_le_bytes()); + assert_eq!(x.test_only_hash(), HashValue::sha3_256_of(&wtr[..])); + + let x = 0x89AB_CDEF_0123_4567_u64; + let mut wtr: Vec = vec![]; + wtr.extend_from_slice(&x.to_le_bytes()); + assert_eq!(x.test_only_hash(), HashValue::sha3_256_of(&wtr[..])); +} + +#[test] +fn test_from_slice() { + { + let zero_byte_vec = vec![0; 32]; + assert_eq!( + HashValue::from_slice(zero_byte_vec).unwrap(), + HashValue::zero() + ); + } + { + // The length is mismatched. + let zero_byte_vec = vec![0; 31]; + assert!(HashValue::from_slice(zero_byte_vec).is_err()); + } + { + let bytes = [1; 123]; + assert!(HashValue::from_slice(&bytes[..]).is_err()); + } +} + +#[test] +fn test_random_with_rng() { + let mut seed = [0u8; 32]; + seed[..4].copy_from_slice(&[1, 2, 3, 4]); + let hash1; + let hash2; + { + let mut rng: StdRng = SeedableRng::from_seed(seed); + hash1 = HashValue::random_with_rng(&mut rng); + } + { + let mut rng: StdRng = SeedableRng::from_seed(seed); + hash2 = HashValue::random_with_rng(&mut rng); + } + assert_eq!(hash1, hash2); +} + +#[test] +fn test_hash_value_iter_bits() { + let hash = b"hello".test_only_hash(); + let bits = hash.iter_bits().collect::>(); + + assert_eq!(bits.len(), HashValue::LENGTH_IN_BITS); + assert!(!bits[0]); + assert!(!bits[1]); + assert!(bits[2]); + assert!(bits[3]); + assert!(!bits[4]); + assert!(!bits[5]); + assert!(bits[6]); + assert!(bits[7]); + assert!(bits[248]); + assert!(!bits[249]); + assert!(!bits[250]); + assert!(bits[251]); + assert!(!bits[252]); + assert!(!bits[253]); + assert!(bits[254]); + assert!(!bits[255]); + + let mut bits_rev = hash.iter_bits().rev().collect::>(); + bits_rev.reverse(); + assert_eq!(bits, bits_rev); +} + +#[test] +fn test_hash_value_iterator_exact_size() { + let hash = b"hello".test_only_hash(); + + let mut iter = hash.iter_bits(); + assert_eq!(iter.len(), HashValue::LENGTH_IN_BITS); + iter.next(); + assert_eq!(iter.len(), HashValue::LENGTH_IN_BITS - 1); + iter.next_back(); + assert_eq!(iter.len(), HashValue::LENGTH_IN_BITS - 2); + + let iter_rev = hash.iter_bits().rev(); + assert_eq!(iter_rev.len(), HashValue::LENGTH_IN_BITS); + + let iter_skip = hash.iter_bits().skip(100); + assert_eq!(iter_skip.len(), HashValue::LENGTH_IN_BITS - 100); +} + +#[test] +fn test_fmt_binary() { + let hash = b"hello".test_only_hash(); + let hash_str = format!("{:b}", hash); + assert_eq!(hash_str.len(), HashValue::LENGTH_IN_BITS); + for (bit, chr) in hash.iter_bits().zip(hash_str.chars()) { + assert_eq!(chr, if bit { '1' } else { '0' }); + } +} + +#[test] +fn test_get_nibble() { + let mut bytes = [0u8; HashValue::LENGTH]; + let mut nibbles = vec![]; + for byte in bytes.iter_mut().take(HashValue::LENGTH) { + *byte = rand::thread_rng().gen(); + nibbles.push(*byte >> 4); + nibbles.push(*byte & 0x0F); + } + let hash = HashValue::new(bytes); + for (i, nibble) in nibbles.iter().enumerate().take(HashValue::LENGTH * 2) { + assert_eq!(hash.nibble(i), *nibble); + } +} + +#[test] +fn test_common_prefix_bits_len() { + { + let hash1 = b"hello".test_only_hash(); + let hash2 = b"HELLO".test_only_hash(); + assert_eq!(hash1[0], 0b0011_0011); + assert_eq!(hash2[0], 0b1011_1000); + assert_eq!(hash1.common_prefix_bits_len(hash2), 0); + } + { + let hash1 = b"hello".test_only_hash(); + let hash2 = b"world".test_only_hash(); + assert_eq!(hash1[0], 0b0011_0011); + assert_eq!(hash2[0], 0b0100_0010); + assert_eq!(hash1.common_prefix_bits_len(hash2), 1); + } + { + let hash1 = b"hello".test_only_hash(); + let hash2 = b"100011001000".test_only_hash(); + assert_eq!(hash1[0], 0b0011_0011); + assert_eq!(hash2[0], 0b0011_0011); + assert_eq!(hash1[1], 0b0011_1000); + assert_eq!(hash2[1], 0b0010_0010); + assert_eq!(hash1.common_prefix_bits_len(hash2), 11); + } + { + let hash1 = b"hello".test_only_hash(); + let hash2 = b"hello".test_only_hash(); + assert_eq!( + hash1.common_prefix_bits_len(hash2), + HashValue::LENGTH_IN_BITS + ); + } +} + +proptest! { + #[test] + fn test_hashvalue_to_bits_roundtrip(hash in any::()) { + let bitvec: BitVec = hash.iter_bits().collect(); + let bytes: Vec = bitvec.into(); + let hash2 = HashValue::from_slice(bytes).unwrap(); + prop_assert_eq!(hash, hash2); + } + + #[test] + fn test_hashvalue_to_bits_inverse_roundtrip(bits in vec(any::(), HashValue::LENGTH_IN_BITS)) { + let bitvec: BitVec = bits.iter().cloned().collect(); + let bytes: Vec = bitvec.into(); + let hash = HashValue::from_slice(bytes).unwrap(); + let bits2: Vec = hash.iter_bits().collect(); + prop_assert_eq!(bits, bits2); + } + + #[test] + fn test_hashvalue_iter_bits_rev(hash in any::()) { + let bits1: Vec = hash.iter_bits().collect(); + let mut bits2: Vec = hash.iter_bits().rev().collect(); + bits2.reverse(); + prop_assert_eq!(bits1, bits2); + } + + #[test] + fn test_hashvalue_to_rev_bits_roundtrip(hash in any::()) { + let bitvec: BitVec = hash.iter_bits().rev().collect(); + let mut bytes: Vec = bitvec.into(); + bytes.reverse(); + let hash2 = HashValue::from_slice(bytes).unwrap(); + prop_assert_eq!(hash, hash2); + } + + #[test] + fn test_hashvalue_to_str_roundtrip(hash in any::()) { + let hash2 = HashValue::from_str(&hash.to_hex()).unwrap(); + prop_assert_eq!(hash, hash2); + } + + #[test] + fn test_hashvalue_to_hex_literal(hash in any::()) { + prop_assert_eq!(format!("0x{}", hash.to_hex()), hash.to_hex_literal()); + } + + #[test] + fn test_hashvalue_from_bit_iter(hash in any::()) { + let hash2 = HashValue::from_bit_iter(hash.iter_bits()).unwrap(); + prop_assert_eq!(hash, hash2); + + let bits1 = vec![false; HashValue::LENGTH_IN_BITS - 10]; + prop_assert!(HashValue::from_bit_iter(bits1.into_iter()).is_err()); + + let bits2 = vec![false; HashValue::LENGTH_IN_BITS + 10]; + prop_assert!(HashValue::from_bit_iter(bits2.into_iter()).is_err()); + } +} diff --git a/crates/aptos-crypto/src/unit_tests/hkdf_test.rs b/crates/aptos-crypto/src/unit_tests/hkdf_test.rs new file mode 100644 index 0000000..336eac9 --- /dev/null +++ b/crates/aptos-crypto/src/unit_tests/hkdf_test.rs @@ -0,0 +1,237 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{compat::Sha3_256, hkdf::*}; +use sha2::{Sha256, Sha512}; + +// Testing against sha256 test vectors. Unfortunately the rfc does not provide test vectors for +// sha3 and sha512. +#[test] +fn test_sha256_test_vectors() { + let tests = test_vectors_sha256(); + for t in tests.iter() { + let ikm = hex::decode(t.ikm).unwrap(); + let salt = hex::decode(t.salt).unwrap(); + let info = hex::decode(t.info).unwrap(); + + let hkdf_extract = Hkdf::::extract(Option::from(&salt[..]), &ikm[..]).unwrap(); + let hkdf_expand = Hkdf::::expand(&hkdf_extract, Some(&info[..]), t.length); + + assert!(hkdf_expand.is_ok()); + assert_eq!(t.prk, hex::encode(hkdf_extract)); + assert_eq!(t.okm, hex::encode(hkdf_expand.unwrap())); + } +} + +// Testing against sha256 test vectors for the extract_then_expand function. +#[test] +fn test_extract_then_expand() { + let tests = test_vectors_sha256(); + for t in tests.iter() { + let ikm = hex::decode(t.ikm).unwrap(); + let salt = hex::decode(t.salt).unwrap(); + let info = hex::decode(t.info).unwrap(); + + let hkdf_full = Hkdf::::extract_then_expand( + Option::from(&salt[..]), + &ikm[..], + Option::from(&info[..]), + t.length, + ); + + assert!(hkdf_full.is_ok()); + assert_eq!(t.okm, hex::encode(hkdf_full.unwrap())); + } +} + +#[test] +fn test_sha256_output_length() { + // According to rfc, max_sha256_length <= 255 * HashLen bytes + let max_hash_length: usize = 255 * 32; // = 8160 + let ikm = [0u8; 32]; + // We extract once, then we reuse it. + let hkdf_extract = Hkdf::::extract(None, &ikm).unwrap(); + + // Test for max allowed (expected to pass) + let hkdf_expand = Hkdf::::expand(&hkdf_extract, None, max_hash_length); + assert!(hkdf_expand.is_ok()); + assert_eq!(hkdf_expand.unwrap().len(), max_hash_length); + + // Test for max + 1 (expected to fail) + let hkdf_expand = Hkdf::::expand(&hkdf_extract, None, max_hash_length + 1); + assert_eq!( + hkdf_expand.unwrap_err(), + HkdfError::InvalidOutputLengthError + ); + + // Test for 10_000 > max (expected to fail) + let hkdf_expand = Hkdf::::expand(&hkdf_extract, None, 10_000); + assert_eq!( + hkdf_expand.unwrap_err(), + HkdfError::InvalidOutputLengthError + ); + + // Test for zero size output (expected to fail) + let hkdf_expand = Hkdf::::expand(&hkdf_extract, None, 0); + assert_eq!( + hkdf_expand.unwrap_err(), + HkdfError::InvalidOutputLengthError + ); +} + +// FIPS 202 approves HMAC-SHA3 and specifies the block sizes (see top of page 22). +// SP 800-56C approves of HKDF-HMAC-hash as a randomness extractor with any approved hash function. +// But in contrast, I can't find any NIST statement that explicitly approves the use of KMAC +// as a randomness extractor. +// But, it's debatable if this is a pointless construct, as HMAC only exists to cover up weaknesses +// in Merkle-Damgard hashes, but sha3 (and Keccak) are sponge constructions, immune to length +// extension attacks. +#[test] +fn test_sha3_256_output_length() { + let max_hash_length: usize = 255 * 32; // = 8160 + let ikm = [0u8; 32]; + let hkdf_extract = Hkdf::::extract(None, &ikm).unwrap(); + + // Test for max allowed (expected to pass) + let hkdf_expand = Hkdf::::expand(&hkdf_extract, None, max_hash_length); + assert!(hkdf_expand.is_ok()); + assert_eq!(hkdf_expand.unwrap().len(), max_hash_length); + + // Test for max + 1 (expected to fail) + let hkdf_expand = Hkdf::::expand(&hkdf_extract, None, max_hash_length + 1); + assert_eq!( + hkdf_expand.unwrap_err(), + HkdfError::InvalidOutputLengthError + ); + + // Test for 10_000 > max (expected to fail) + let hkdf_expand = Hkdf::::expand(&hkdf_extract, None, 10_000); + assert_eq!( + hkdf_expand.unwrap_err(), + HkdfError::InvalidOutputLengthError + ); + + // Test for zero size output (expected to fail) + let hkdf_expand = Hkdf::::expand(&hkdf_extract, None, 0); + assert_eq!( + hkdf_expand.unwrap_err(), + HkdfError::InvalidOutputLengthError + ); +} + +#[test] +fn test_sha512_output_length() { + let max_hash_length: usize = 255 * 64; // = 16320 + let ikm = [0u8; 32]; + let hkdf_extract = Hkdf::::extract(None, &ikm).unwrap(); + + // Test for max allowed (expected to pass) + let hkdf_expand = Hkdf::::expand(&hkdf_extract, None, max_hash_length); + assert!(hkdf_expand.is_ok()); + assert_eq!(hkdf_expand.unwrap().len(), max_hash_length); + + // Test for max + 1 (expected to fail) + let hkdf_expand = Hkdf::::expand(&hkdf_extract, None, max_hash_length + 1); + assert_eq!( + hkdf_expand.unwrap_err(), + HkdfError::InvalidOutputLengthError + ); + + // Test for 10_000 > max (expected to fail) + let hkdf_expand = Hkdf::::expand(&hkdf_extract, None, 20_000); + assert_eq!( + hkdf_expand.unwrap_err(), + HkdfError::InvalidOutputLengthError + ); + + // Test for zero size output (expected to fail) + let hkdf_expand = Hkdf::::expand(&hkdf_extract, None, 0); + assert_eq!( + hkdf_expand.unwrap_err(), + HkdfError::InvalidOutputLengthError + ); +} + +#[test] +#[ignore] +fn unsupported_digest() { + let t = trybuild::TestCases::new(); + t.compile_fail("src/unit_tests/compilation/small_kdf.rs"); +} + +#[test] +fn test_ikm_size() { + // Test for 16 bytes seed. + let ikm16 = [0u8; 16]; + assert!(Hkdf::::extract(None, &ikm16).is_ok()); + + // Test for 32 bytes seed. + let ikm32 = [0u8; 32]; + assert!(Hkdf::::extract(None, &ikm32).is_ok()); + + // Test for 15 bytes seed. + let ikm15 = [0u8; 15]; + assert_eq!( + Hkdf::::extract(None, &ikm15), + Err(HkdfError::InvalidSeedLengthError) + ); + + // Test for empty seed. + let ikm0 = []; + assert_eq!( + Hkdf::::extract(None, &ikm0), + Err(HkdfError::InvalidSeedLengthError) + ); +} + +// Test Vectors for sha256 from https://tools.ietf.org/html/rfc5869. +struct Test<'a> { + ikm: &'a str, + salt: &'a str, + info: &'a str, + length: usize, + prk: &'a str, + okm: &'a str, +} + +fn test_vectors_sha256<'a>() -> Vec> { + vec![ + Test { + // Test Case 1 + ikm: "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + salt: "000102030405060708090a0b0c", + info: "f0f1f2f3f4f5f6f7f8f9", + length: 42, + prk: "077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5", + okm: "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b8\ + 87185865", + }, + Test { + // Test Case 2 + ikm: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425\ + 262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b\ + 4c4d4e4f", + salt: "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f80818283848\ + 5868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aa\ + abacadaeaf", + info: "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d\ + 5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fa\ + fbfcfdfeff", + length: 82, + prk: "06a6b88c5853361a06104c9ceb35b45cef760014904671014a193f40c15fc244", + okm: "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c59045a99cac7\ + 827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71cc30c58179ec3e87c14c01d5\ + c1f3434f1d87", + }, + Test { + // Test Case 3 + ikm: "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + salt: "", + info: "", + length: 42, + prk: "19ef24a32c717b167f33a91d6f648bdf96596776afdb6377ac434c1c293ccb04", + okm: "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4\ + b61a96c8", + }, + ] +} diff --git a/crates/aptos-crypto/src/unit_tests/mod.rs b/crates/aptos-crypto/src/unit_tests/mod.rs new file mode 100644 index 0000000..34cf334 --- /dev/null +++ b/crates/aptos-crypto/src/unit_tests/mod.rs @@ -0,0 +1,16 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +mod bcs_test; +mod bls12381_test; +mod bulletproofs_test; +mod compat_test; +mod cross_test; +mod cryptohasher; +mod ed25519_test; +mod hash_test; +mod hkdf_test; +mod multi_ed25519_test; +mod noise_test; +mod secp256k1_ecdsa_test; +mod secp256r1_ecdsa_test; diff --git a/crates/aptos-crypto/src/unit_tests/multi_ed25519_test.rs b/crates/aptos-crypto/src/unit_tests/multi_ed25519_test.rs new file mode 100644 index 0000000..eceb2a6 --- /dev/null +++ b/crates/aptos-crypto/src/unit_tests/multi_ed25519_test.rs @@ -0,0 +1,544 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + ed25519::{Ed25519PrivateKey, Ed25519PublicKey, ED25519_PUBLIC_KEY_LENGTH}, + multi_ed25519::{MultiEd25519PrivateKey, MultiEd25519PublicKey, MultiEd25519Signature}, + test_utils::{TestAptosCrypto, TEST_SEED}, + traits::*, + CryptoMaterialError::{ValidationError, WrongLengthError}, +}; +use core::convert::TryFrom; +use once_cell::sync::Lazy; +use rand::{rngs::StdRng, SeedableRng}; + +static MESSAGE: Lazy = Lazy::new(|| TestAptosCrypto("Test Message".to_string())); +fn message() -> &'static TestAptosCrypto { + &MESSAGE +} + +// Helper function to generate N ed25519 private keys. +fn generate_keys(n: usize) -> Vec { + let mut rng = StdRng::from_seed(TEST_SEED); + (0..n) + .map(|_| Ed25519PrivateKey::generate(&mut rng)) + .collect() +} + +// Reused assertions in our tests. +fn test_successful_public_key_serialization(original_keys: &[Ed25519PublicKey], threshold: u8) { + let num_pks = original_keys.len(); + let public_key: MultiEd25519PublicKey = + MultiEd25519PublicKey::new(original_keys.to_vec(), threshold).unwrap(); + assert_eq!(public_key.threshold(), &threshold); + assert_eq!(public_key.public_keys().len(), num_pks); + assert_eq!(public_key.public_keys(), &original_keys.to_vec()); + let serialized = public_key.to_bytes(); + assert_eq!(serialized.len(), num_pks * ED25519_PUBLIC_KEY_LENGTH + 1); + let reserialized = MultiEd25519PublicKey::try_from(&serialized[..]); + assert!(reserialized.is_ok()); + assert_eq!(public_key, reserialized.unwrap()); +} + +fn test_failed_public_key_serialization( + result: std::result::Result, + expected_error: CryptoMaterialError, +) { + assert!(result.is_err()); + assert_eq!(result.err().unwrap(), expected_error); +} + +fn test_successful_signature_serialization(private_keys: &[Ed25519PrivateKey], threshold: u8) { + let multi_private_key = MultiEd25519PrivateKey::new(private_keys.to_vec(), threshold).unwrap(); + let multi_public_key = MultiEd25519PublicKey::from(&multi_private_key); + let multi_signature = multi_private_key.sign(message()).unwrap(); + + // Serialize then Deserialize. + let multi_signature_serialized = + MultiEd25519Signature::try_from(&multi_signature.to_bytes()[..]); + assert!(multi_signature_serialized.is_ok()); + let multi_signature_serialized_unwrapped = multi_signature_serialized.unwrap(); + assert_eq!(multi_signature, multi_signature_serialized_unwrapped); + // Ensure that the signature verifies. + assert!(multi_signature.verify(message(), &multi_public_key).is_ok()); +} + +// Test multi-sig Ed25519 public key serialization. +#[test] +fn test_multi_ed25519_public_key_serialization() { + let pub_keys_1: Vec<_> = generate_keys(1).iter().map(|x| x.public_key()).collect(); + let pub_keys_10: Vec<_> = generate_keys(10).iter().map(|x| x.public_key()).collect(); + let pub_keys_32: Vec<_> = generate_keys(32).iter().map(|x| x.public_key()).collect(); + let pub_keys_33: Vec<_> = generate_keys(33).iter().map(|x| x.public_key()).collect(); + + // Test 1-of-1 + test_successful_public_key_serialization(&pub_keys_1, 1); + // Test 1-of-10 + test_successful_public_key_serialization(&pub_keys_10, 1); + // Test 7-of-10 + test_successful_public_key_serialization(&pub_keys_10, 7); + // Test 10-of-10 + test_successful_public_key_serialization(&pub_keys_10, 10); + // Test 2-of-32 + test_successful_public_key_serialization(&pub_keys_32, 2); + // Test 32-of-32 + test_successful_public_key_serialization(&pub_keys_32, 32); + + // Test 11-of-10 (should fail). + let multi_key_11of10 = MultiEd25519PublicKey::new(pub_keys_10.clone(), 11); + test_failed_public_key_serialization(multi_key_11of10, ValidationError); + + // Test 0-of-10 (should fail). + let multi_key_0of10 = MultiEd25519PublicKey::new(pub_keys_10, 0); + test_failed_public_key_serialization(multi_key_0of10, ValidationError); + + // Test 1-of-33 (should fail). + let multi_key_1of33 = MultiEd25519PublicKey::new(pub_keys_33, 1); + test_failed_public_key_serialization(multi_key_1of33, WrongLengthError); + + // Test try_from empty bytes (should fail). + let multi_key_empty_bytes = MultiEd25519PublicKey::try_from(&[] as &[u8]); + test_failed_public_key_serialization(multi_key_empty_bytes, WrongLengthError); + + // Test try_from 1 byte (should fail). + let multi_key_1_byte = MultiEd25519PublicKey::try_from(&[0u8][..]); + test_failed_public_key_serialization(multi_key_1_byte, WrongLengthError); + + // Test try_from 31 bytes (should fail). + let multi_key_31_bytes = + MultiEd25519PublicKey::try_from(&[0u8; ED25519_PUBLIC_KEY_LENGTH - 1][..]); + test_failed_public_key_serialization(multi_key_31_bytes, WrongLengthError); + + // Test try_from 32 bytes (should fail) because we always need ED25519_PUBLIC_KEY_LENGTH * N + 1 + // bytes (thus 32N + 1). + let multi_key_32_bytes = MultiEd25519PublicKey::try_from(&[0u8; ED25519_PUBLIC_KEY_LENGTH][..]); + test_failed_public_key_serialization(multi_key_32_bytes, WrongLengthError); + + // Test try_from 34 bytes (should fail). + let multi_key_34_bytes = + MultiEd25519PublicKey::try_from(&[0u8; ED25519_PUBLIC_KEY_LENGTH + 2][..]); + test_failed_public_key_serialization(multi_key_34_bytes, WrongLengthError); + + // Test try_from 33 all zero bytes (size is fine, but it should fail due to + // validation issues). + let multi_key_33_zero_bytes = + MultiEd25519PublicKey::try_from(&[0u8; ED25519_PUBLIC_KEY_LENGTH + 1][..]); + test_failed_public_key_serialization(multi_key_33_zero_bytes, ValidationError); + + let priv_keys_10 = generate_keys(10); + let pub_keys_10: Vec<_> = priv_keys_10.iter().map(|x| x.public_key()).collect(); + + let multi_private_key_7of10 = MultiEd25519PrivateKey::new(priv_keys_10, 7).unwrap(); + let multi_public_key_7of10 = MultiEd25519PublicKey::new(pub_keys_10, 7).unwrap(); + + // Check that MultiEd25519PublicKey::from MultiEd25519PrivateKey works as expected. + let multi_public_key_7of10_from_multi_private_key = + MultiEd25519PublicKey::from(&multi_private_key_7of10); + assert_eq!( + multi_public_key_7of10_from_multi_private_key, + multi_public_key_7of10 + ); + + // Check that MultiEd25519PublicKey::from Ed25519PublicKey works as expected. + let multi_public_key_from_ed25519 = MultiEd25519PublicKey::from( + multi_public_key_7of10_from_multi_private_key.public_keys()[0].clone(), + ); + assert_eq!(multi_public_key_from_ed25519.public_keys().len(), 1); + assert_eq!( + &multi_public_key_from_ed25519.public_keys()[0], + &multi_public_key_7of10_from_multi_private_key.public_keys()[0] + ); + assert_eq!(multi_public_key_from_ed25519.threshold(), &1u8); +} + +// Test against known small subgroup public key. +#[ignore] +#[test] +fn test_publickey_smallorder() { + // A small group point with threshold 1 (last byte). + // See EIGHT_TORSION in ed25519_test.rs for more about small group points. + let torsion_point_with_threshold_1: [u8; 33] = [ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, + ]; + + let torsion_key = MultiEd25519PublicKey::try_from(&torsion_point_with_threshold_1[..]); + + assert!(torsion_key.is_err()); + assert_eq!( + torsion_key.err().unwrap(), + CryptoMaterialError::SmallSubgroupError + ); +} + +// Test multi-sig Ed25519 signature serialization. +#[test] +fn test_multi_ed25519_signature_serialization() { + let priv_keys_3 = generate_keys(3); + + // Test 1 of 3 + test_successful_signature_serialization(&priv_keys_3, 1); + // Test 2 of 3 + test_successful_signature_serialization(&priv_keys_3, 2); + // Test 3 of 3 + test_successful_signature_serialization(&priv_keys_3, 3); + + let priv_keys_32 = generate_keys(32); + // Test 1 of 32 + test_successful_signature_serialization(&priv_keys_32, 1); + // Test 32 of 32 + test_successful_signature_serialization(&priv_keys_32, 32); + + // Construct from single Ed25519Signature. + let single_signature = priv_keys_3[0].sign(message()).unwrap(); + let multi_signature = MultiEd25519Signature::from(single_signature.clone()); + assert_eq!(1, multi_signature.signatures().len()); + assert_eq!(multi_signature.signatures()[0], single_signature); + assert_eq!(multi_signature.bitmap(), &[0b1000_0000u8, 0u8, 0u8, 0u8]); + let multi_priv_key_1of3 = MultiEd25519PrivateKey::new(priv_keys_3.to_vec(), 1).unwrap(); + let multi_pub_key_1of3 = MultiEd25519PublicKey::from(&multi_priv_key_1of3); + assert!(multi_signature + .verify(message(), &multi_pub_key_1of3) + .is_ok()); + + // We can construct signatures from 32 single signatures. + let sigs_32 = vec![single_signature.clone(); 32]; + let indices = 0..32; + let sig32_tuple = sigs_32.into_iter().zip(indices).collect(); + + let multi_sig32 = MultiEd25519Signature::new(sig32_tuple); + assert!(multi_sig32.is_ok()); + let multi_sig32_unwrapped = multi_sig32.unwrap(); + assert_eq!( + multi_sig32_unwrapped.bitmap(), + &[0b1111_1111, 0b1111_1111, 0b1111_1111, 0b1111_1111] + ); + let pub_key_32 = vec![priv_keys_3[0].public_key(); 32]; + let multi_pub_key_32 = MultiEd25519PublicKey::new(pub_key_32, 32).unwrap(); + assert!(multi_sig32_unwrapped + .verify(message(), &multi_pub_key_32) + .is_ok()); + + // Fail to construct a MultiEd25519Signature object from 33 or more single signatures. + let sigs_33 = vec![single_signature.clone(); 33]; + let indices = 0..33; + let sig33_tuple = sigs_33.into_iter().zip(indices).collect(); + + let multi_sig33 = MultiEd25519Signature::new(sig33_tuple); + assert!(multi_sig33.is_err()); + assert_eq!( + multi_sig33.err().unwrap(), + CryptoMaterialError::ValidationError + ); + + // Fail to construct a MultiEd25519Signature object if there are duplicated indexes. + let sigs_3 = vec![single_signature; 3]; + let indices_with_duplicate = vec![0u8, 1u8, 1u8]; + let sig3_tuple = sigs_3 + .clone() + .into_iter() + .zip(indices_with_duplicate) + .collect(); + + let multi_sig3 = MultiEd25519Signature::new(sig3_tuple); + assert!(multi_sig3.is_err()); + assert_eq!( + multi_sig3.err().unwrap(), + CryptoMaterialError::BitVecError("Duplicate signature index".to_string()) + ); + + // Fail to construct a MultiEd25519Signature object if an index is out of range. + let indices_with_out_of_range = vec![0u8, 33u8, 1u8]; + let sig3_tuple = sigs_3.into_iter().zip(indices_with_out_of_range).collect(); + + let multi_sig3 = MultiEd25519Signature::new(sig3_tuple); + assert!(multi_sig3.is_err()); + assert_eq!( + multi_sig3.err().unwrap(), + CryptoMaterialError::BitVecError("Signature index is out of range".to_string()) + ); +} + +// Test multi-sig Ed25519 signature verification. +#[test] +fn test_multi_ed25519_signature_verification() { + let priv_keys_10 = generate_keys(10); + let pub_keys_10: Vec<_> = priv_keys_10.iter().map(|x| x.public_key()).collect(); + + let multi_private_key_7of10 = MultiEd25519PrivateKey::new(priv_keys_10.clone(), 7).unwrap(); + let multi_public_key_7of10 = MultiEd25519PublicKey::from(&multi_private_key_7of10); + + // Verifying a 7-of-10 signature against a public key with the same threshold should pass. + let multi_signature_7of10 = multi_private_key_7of10.sign(message()).unwrap(); + assert_eq!( + multi_signature_7of10.bitmap(), + &[0b1111_1110, 0u8, 0u8, 0u8] + ); + assert!(multi_signature_7of10 + .verify(message(), &multi_public_key_7of10) + .is_ok()); + + // Verifying a 7-of-10 signature against a public key with bigger threshold (i.e., 8) should fail. + let multi_public_key_8of10 = MultiEd25519PublicKey::new(pub_keys_10.clone(), 8).unwrap(); + assert!(multi_signature_7of10 + .verify(message(), &multi_public_key_8of10) + .is_err()); + + // Verifying a 7-of-10 signature against a public key with smaller threshold (i.e., 6) should pass. + let multi_public_key_6of10 = MultiEd25519PublicKey::new(pub_keys_10.clone(), 6).unwrap(); + assert!(multi_signature_7of10 + .verify(message(), &multi_public_key_6of10) + .is_ok()); + + // Verifying a 7-of-10 signature against a reordered MultiEd25519PublicKey should fail. + // To deterministically simulate reshuffling, we use a reversed vector of 10 keys. + // Note that because 10 is an even number, all of they keys will change position. + let mut pub_keys_10_reversed = pub_keys_10; + pub_keys_10_reversed.reverse(); + let multi_public_key_7of10_reversed = + MultiEd25519PublicKey::new(pub_keys_10_reversed, 7).unwrap(); + assert!(multi_signature_7of10 + .verify(message(), &multi_public_key_7of10_reversed) + .is_err()); + + let priv_keys_3 = generate_keys(3); + + let multi_private_key_1of3 = MultiEd25519PrivateKey::new(priv_keys_3.clone(), 1).unwrap(); + let multi_public_key_1of3 = MultiEd25519PublicKey::from(&multi_private_key_1of3); + + // Signing with the 2nd key must succeed. + let sig_with_2nd_key = priv_keys_3[1].sign(message()).unwrap(); + let multi_sig_signed_by_2nd_key = MultiEd25519Signature::new(vec![(sig_with_2nd_key, 1)]); + assert!(multi_sig_signed_by_2nd_key.is_ok()); + let multi_sig_signed_by_2nd_key_unwrapped = multi_sig_signed_by_2nd_key.unwrap(); + assert_eq!( + multi_sig_signed_by_2nd_key_unwrapped.bitmap(), + &[0b0100_0000, 0u8, 0u8, 0u8] + ); + assert!(multi_sig_signed_by_2nd_key_unwrapped + .verify(message(), &multi_public_key_1of3) + .is_ok()); + + // Signing with the 2nd key but using wrong index will fail. + let sig_with_2nd_key = priv_keys_3[1].sign(message()).unwrap(); + let multi_sig_signed_by_2nd_key_wrong_index = + MultiEd25519Signature::new(vec![(sig_with_2nd_key.clone(), 2)]); + assert!(multi_sig_signed_by_2nd_key_wrong_index.is_ok()); + let failed_multi_sig_signed_by_2nd_key_wrong_index = multi_sig_signed_by_2nd_key_wrong_index + .unwrap() + .verify(message(), &multi_public_key_1of3); + assert!(failed_multi_sig_signed_by_2nd_key_wrong_index.is_err()); + + // Signing with the 2nd and 3rd keys must succeed, even if we surpass the threshold. + let sig_with_3rd_key = priv_keys_3[2].sign(message()).unwrap(); + let multi_sig_signed_by_2nd_and_3rd_key = MultiEd25519Signature::new(vec![ + (sig_with_2nd_key.clone(), 1), + (sig_with_3rd_key.clone(), 2), + ]); + assert!(multi_sig_signed_by_2nd_and_3rd_key.is_ok()); + let multi_sig_signed_by_2nd_and_3rd_key_unwrapped = + multi_sig_signed_by_2nd_and_3rd_key.unwrap(); + assert_eq!( + multi_sig_signed_by_2nd_and_3rd_key_unwrapped.bitmap(), + &[0b0110_0000, 0u8, 0u8, 0u8] + ); + assert!(multi_sig_signed_by_2nd_and_3rd_key_unwrapped + .verify(message(), &multi_public_key_1of3) + .is_ok()); + + // Signing with the 2nd and 3rd keys will fail if we swap indexes. + let multi_sig_signed_by_2nd_and_3rd_key_swapped = MultiEd25519Signature::new(vec![ + (sig_with_2nd_key.clone(), 2), + (sig_with_3rd_key.clone(), 1), + ]); + let failed_multi_sig_signed_by_2nd_and_3rd_key_swapped = + multi_sig_signed_by_2nd_and_3rd_key_swapped + .unwrap() + .verify(message(), &multi_public_key_1of3); + assert!(failed_multi_sig_signed_by_2nd_and_3rd_key_swapped.is_err()); + + // Signing with the 2nd and an unrelated key. Although threshold is met, it should fail as + // we don't accept invalid signatures. + let sig_with_unrelated_key = priv_keys_10[9].sign(message()).unwrap(); + let multi_sig_signed_by_2nd_and_unrelated_key = MultiEd25519Signature::new(vec![ + (sig_with_2nd_key.clone(), 1), + (sig_with_unrelated_key, 2), + ]); + assert!(multi_sig_signed_by_2nd_and_unrelated_key.is_ok()); + let failed_verified_sig = multi_sig_signed_by_2nd_and_unrelated_key + .unwrap() + .verify(message(), &multi_public_key_1of3); + assert!(failed_verified_sig.is_err()); + + // Testing all combinations for 2 of 3. + let multi_private_key_2of3 = MultiEd25519PrivateKey::new(priv_keys_3.clone(), 2).unwrap(); + let multi_public_key_2of3 = MultiEd25519PublicKey::from(&multi_private_key_2of3); + + let sig_with_1st_key = priv_keys_3[0].sign(message()).unwrap(); + + // Signing with the 1st and 2nd keys must succeed. + let signed_by_1st_and_2nd_key = MultiEd25519Signature::new(vec![ + (sig_with_1st_key.clone(), 0), + (sig_with_2nd_key.clone(), 1), + ]); + assert!(signed_by_1st_and_2nd_key.is_ok()); + let signed_by_1st_and_2nd_key_unwrapped = signed_by_1st_and_2nd_key.unwrap(); + assert_eq!( + signed_by_1st_and_2nd_key_unwrapped.bitmap(), + &[0b1100_0000, 0u8, 0u8, 0u8] + ); + assert!(signed_by_1st_and_2nd_key_unwrapped + .verify(message(), &multi_public_key_2of3) + .is_ok()); + + // Signing with the 1st and 3rd keys must succeed. + let signed_by_1st_and_3rd_key = MultiEd25519Signature::new(vec![ + (sig_with_1st_key.clone(), 0), + (sig_with_3rd_key.clone(), 2), + ]); + assert!(signed_by_1st_and_3rd_key.is_ok()); + let signed_by_1st_and_3rd_key_unwrapped = signed_by_1st_and_3rd_key.unwrap(); + assert_eq!( + signed_by_1st_and_3rd_key_unwrapped.bitmap(), + &[0b1010_0000, 0u8, 0u8, 0u8] + ); + assert!(signed_by_1st_and_3rd_key_unwrapped + .verify(message(), &multi_public_key_2of3) + .is_ok()); + + // Signing with the 2nd and 3rd keys must succeed. + let signed_by_2nd_and_3rd_key = MultiEd25519Signature::new(vec![ + (sig_with_2nd_key.clone(), 1), + (sig_with_3rd_key.clone(), 2), + ]); + assert!(signed_by_2nd_and_3rd_key.is_ok()); + let signed_by_2nd_and_3rd_key_unwrapped = signed_by_2nd_and_3rd_key.unwrap(); + assert_eq!( + signed_by_2nd_and_3rd_key_unwrapped.bitmap(), + &[0b0110_0000, 0u8, 0u8, 0u8] + ); + assert!(signed_by_2nd_and_3rd_key_unwrapped + .verify(message(), &multi_public_key_2of3) + .is_ok()); + + // Signing with the 2nd and 3rd keys must succeed. + let signed_by_all_3_keys = MultiEd25519Signature::new(vec![ + (sig_with_1st_key, 0), + (sig_with_2nd_key.clone(), 1), + (sig_with_3rd_key, 2), + ]); + assert!(signed_by_all_3_keys.is_ok()); + let signed_by_all_3_keys_unwrapped = signed_by_all_3_keys.unwrap(); + assert_eq!( + signed_by_all_3_keys_unwrapped.bitmap(), + &[0b1110_0000, 0u8, 0u8, 0u8] + ); + assert!(signed_by_all_3_keys_unwrapped + .verify(message(), &multi_public_key_2of3) + .is_ok()); + + // Signing with the 2nd only will fail. + let signed_by_2nd_key = MultiEd25519Signature::new(vec![(sig_with_2nd_key, 1)]); + assert!(signed_by_2nd_key.is_ok()); + let signed_by_2nd_key_unwrapped = signed_by_2nd_key.unwrap(); + assert_eq!( + signed_by_2nd_key_unwrapped.bitmap(), + &[0b0100_0000, 0u8, 0u8, 0u8] + ); + assert!(signed_by_2nd_key_unwrapped + .verify(message(), &multi_public_key_2of3) + .is_err()); +} + +#[test] +fn test_invalid_multi_ed25519_signature_bitmap() { + let priv_keys_3 = generate_keys(3); + + let multi_private_key_2of3 = MultiEd25519PrivateKey::new(priv_keys_3, 2).unwrap(); + let multi_public_key_2of3 = MultiEd25519PublicKey::from(&multi_private_key_2of3); + + let multi_signature_2of3 = multi_private_key_2of3.sign(message()).unwrap(); + + assert_eq!(multi_signature_2of3.bitmap(), &[0b1100_0000, 0u8, 0u8, 0u8]); + + assert!(multi_signature_2of3 + .verify(message(), &multi_public_key_2of3) + .is_ok()); + + let multi_signature_2of3_invalid_bitmap = MultiEd25519Signature::new_with_signatures_and_bitmap( + multi_signature_2of3.signatures().to_vec(), + [0b0000_1000, 0u8, 0u8, 0u8], + ); + + // Fails due to bitmap is set with a bit that's invalid + assert!(multi_signature_2of3_invalid_bitmap + .verify(message(), &multi_public_key_2of3) + .is_err()); + + let multi_signature_1of3 = MultiEd25519Signature::new_with_signatures_and_bitmap( + multi_signature_2of3.signatures().to_vec(), + [0b1000_0000, 0u8, 0u8, 0u8], + ); + + // Bitmap is valid, but fails due to threshold is not met + assert!(multi_signature_1of3 + .verify(message(), &multi_public_key_2of3) + .is_err()); +} + +/// Used for generating test cases for the MultiEd25519 Move module. +#[test] +#[ignore] +fn test_sample_multisig() { + let test_cases = [(1, 1), (1, 2), (2, 2), (2, 3), (3, 10), (15, 32)] + .iter() + .map(|(k, n)| (*k as usize, *n as usize)) + .collect::>(); + + let mut ks = vec![]; + let mut ns = vec![]; + let mut pks = vec![]; + let mut sigs = vec![]; + let msg = b"Hello Aptos!"; + + for &(k, n) in test_cases.iter() { + let private_keys = generate_keys(n); + + let multi_private_key = + MultiEd25519PrivateKey::new(private_keys.to_vec(), k as u8).unwrap(); + let multi_public_key = MultiEd25519PublicKey::from(&multi_private_key); + let multi_signature = multi_private_key.sign_arbitrary_message(msg); + + ks.push(k); + ns.push(n); + pks.push(multi_public_key); + sigs.push(multi_signature); + } + + println!("let msg = b\"Hello Aptos!\";"); + print!("//let ks = vector["); + for k in ks { + print!("{k}, ") + } + println!("]; // the thresholds, implicitly encoded in the public keys"); + + print!("let ns = vector["); + for n in ns { + print!("{n}, ") + } + println!("];"); + + println!("let pks = vector["); + for pk in pks { + println!("\tx\"{}\",", hex::encode(pk.to_bytes())); + } + println!("];"); + + println!("let sigs = vector["); + for sig in sigs { + println!("\tx\"{}\",", hex::encode(sig.to_bytes())); + } + println!("];"); + + println!(); +} diff --git a/crates/aptos-crypto/src/unit_tests/noise_test.rs b/crates/aptos-crypto/src/unit_tests/noise_test.rs new file mode 100644 index 0000000..c5245ee --- /dev/null +++ b/crates/aptos-crypto/src/unit_tests/noise_test.rs @@ -0,0 +1,532 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + noise::{handshake_init_msg_len, handshake_resp_msg_len, NoiseConfig, MAX_SIZE_NOISE_MSG}, + test_utils::TEST_SEED, + x25519, Uniform as _, +}; +use rand::SeedableRng; +use serde::*; +use std::{fs::File, io::BufReader, path::PathBuf}; + +#[test] +fn simple_handshake() { + // setup peers + let mut rng = ::rand::rngs::StdRng::from_seed(TEST_SEED); + let initiator_private = x25519::PrivateKey::generate(&mut rng); + let initiator_public = initiator_private.public_key(); + let responder_private = x25519::PrivateKey::generate(&mut rng); + let responder_public = responder_private.public_key(); + let initiator = NoiseConfig::new(initiator_private); + let responder = NoiseConfig::new(responder_private); + + // test the two APIs + for i in 0..2 { + // initiator sends first message + let prologue = b"prologue"; + let payload1 = b"payload1"; + let mut first_message = vec![0u8; handshake_init_msg_len(payload1.len())]; + let initiator_state = initiator + .initiate_connection( + &mut rng, + prologue, + responder_public, + Some(payload1), + &mut first_message, + ) + .unwrap(); + + let payload2 = b"payload2"; + let mut second_message = vec![0u8; handshake_resp_msg_len(payload2.len())]; + + // responder parses the first message and responds + let mut responder_session = if i == 0 { + let (received_payload, responder_session) = responder + .respond_to_client_and_finalize( + &mut rng, + prologue, + &first_message, + Some(payload2), + &mut second_message, + ) + .unwrap(); + let remote_static = responder_session.get_remote_static(); + assert_eq!(remote_static, initiator_public); + assert_eq!(received_payload, b"payload1"); + responder_session + } else { + let payload2 = b"payload2"; + let (remote_static, handshake_state, received_payload) = responder + .parse_client_init_message(prologue, &first_message) + .unwrap(); + assert_eq!(remote_static, initiator_public); + assert_eq!(received_payload, b"payload1"); + + responder + .respond_to_client( + &mut rng, + handshake_state, + Some(payload2), + &mut second_message, + ) + .unwrap() + }; + + // initiator parses the response + let (received_payload, mut initiator_session) = initiator + .finalize_connection(initiator_state, &second_message) + .unwrap(); + assert_eq!(received_payload, b"payload2"); + + // session usage + let mut message_sent = b"payload".to_vec(); + for i in 0..10 { + message_sent.push(i); + let mut message = message_sent.clone(); + let received_message = if i % 2 == 0 { + let auth_tag = initiator_session + .write_message_in_place(&mut message) + .expect("session should not be closed"); + message.extend_from_slice(&auth_tag); + responder_session + .read_message_in_place(&mut message) + .expect("session should not be closed") + } else { + let auth_tag = responder_session + .write_message_in_place(&mut message) + .expect("session should not be closed"); + message.extend_from_slice(&auth_tag); + initiator_session + .read_message_in_place(&mut message) + .expect("session should not be closed") + }; + assert_eq!(received_message, message_sent.as_slice()); + } + } +} + +#[test] +fn test_vectors() { + // structures needed to deserialize test vectors + #[derive(Serialize, Deserialize)] + struct TestVectors { + vectors: Vec, + } + #[derive(Serialize, Deserialize, Debug)] + struct TestVector { + protocol_name: String, + init_prologue: String, + init_static: Option, + init_ephemeral: String, + init_remote_static: Option, + resp_static: Option, + resp_ephemeral: Option, + handshake_hash: String, + messages: Vec, + } + #[derive(Serialize, Deserialize, Debug)] + struct TestMessage { + payload: String, + ciphertext: String, + } + + // EphemeralRng is used to get deterministic ephemeral keys based on test vectors + struct EphemeralRng { + ephemeral: Vec, + } + impl rand::RngCore for EphemeralRng { + fn next_u32(&mut self) -> u32 { + unreachable!() + } + + fn next_u64(&mut self) -> u64 { + unreachable!() + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + dest.copy_from_slice(&self.ephemeral); + } + + fn try_fill_bytes(&mut self, _dest: &mut [u8]) -> Result<(), rand::Error> { + unreachable!() + } + } + impl rand::CryptoRng for EphemeralRng {} + + // test vectors are taken from the cacophony library + let mut test_vectors_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + test_vectors_path.push("test_vectors"); + test_vectors_path.push("noise_cacophony.txt"); + let test_vectors_path = test_vectors_path.to_str().unwrap(); + let test_vectors_file = File::open(test_vectors_path).expect("missing noise test vectors"); + let test_vectors: TestVectors = + serde_json::from_reader(BufReader::new(test_vectors_file)).unwrap(); + + // only go through Noise_IK_25519_AESGCM_SHA256 test vectors (don't exist for SHA-3) + let test_vector = test_vectors + .vectors + .iter() + .find(|vector| vector.protocol_name == "Noise_IK_25519_AESGCM_SHA256") + .expect("test vector for Noise_IK_25519_AESGCM_SHA256 should be in cacophony test vectors"); + + // initiate peers with test vector + use crate::traits::ValidCryptoMaterialStringExt; + let initiator_private = + x25519::PrivateKey::from_encoded_string(test_vector.init_static.as_ref().unwrap()).unwrap(); + let initiator_public = initiator_private.public_key(); + let responder_private = + x25519::PrivateKey::from_encoded_string(test_vector.resp_static.as_ref().unwrap()).unwrap(); + let responder_public = responder_private.public_key(); + + let initiator = NoiseConfig::new(initiator_private); + let responder = NoiseConfig::new(responder_private); + + // test the two APIs + for i in 0..2 { + // assert public keys + let init_remote_static = + hex::decode(test_vector.init_remote_static.as_ref().unwrap()).unwrap(); + assert_eq!(responder_public.as_slice(), init_remote_static.as_slice()); + + // go through handshake test messages + let prologue = hex::decode(&test_vector.init_prologue).unwrap(); + let mut messages = test_vector.messages.iter(); + + // first handshake message + let message = messages.next().unwrap(); + let payload1 = hex::decode(&message.payload).unwrap(); + let expected_ciphertext = hex::decode(&message.ciphertext).unwrap(); + let init_ephemeral = hex::decode(&test_vector.init_ephemeral).unwrap(); + let mut rng = EphemeralRng { + ephemeral: init_ephemeral, + }; + let mut first_message = vec![0u8; handshake_init_msg_len(payload1.len())]; + let initiator_state = initiator + .initiate_connection( + &mut rng, + &prologue, + responder_public, + Some(&payload1), + &mut first_message, + ) + .unwrap(); + assert_eq!(first_message, expected_ciphertext); + + // second handshake message + let message = messages.next().unwrap(); + let payload2 = hex::decode(&message.payload).unwrap(); + let expected_ciphertext = hex::decode(&message.ciphertext).unwrap(); + + // responder part + let resp_ephemeral = hex::decode(test_vector.resp_ephemeral.as_ref().unwrap()).unwrap(); + let mut rng = EphemeralRng { + ephemeral: resp_ephemeral, + }; + let mut second_message = vec![0u8; handshake_resp_msg_len(payload2.len())]; + + let mut responder_session = if i == 0 { + let (received_payload, responder_session) = responder + .respond_to_client_and_finalize( + &mut rng, + &prologue, + &first_message, + Some(&payload2), + &mut second_message, + ) + .unwrap(); + assert_eq!(payload1, received_payload); + responder_session + } else { + let (remote_static, handshake_state, received_payload) = responder + .parse_client_init_message(&prologue, &first_message) + .unwrap(); + assert_eq!(remote_static, initiator_public); + assert_eq!(payload1, received_payload); + + responder + .respond_to_client( + &mut rng, + handshake_state, + Some(&payload2), + &mut second_message, + ) + .unwrap() + }; + + let remote_static = responder_session.get_remote_static(); + assert_eq!(second_message, expected_ciphertext); + assert_eq!(remote_static, initiator_public); + + // initiator part + let (received_payload, mut initiator_session) = initiator + .finalize_connection(initiator_state, &second_message) + .unwrap(); + assert_eq!(payload2, received_payload); + + // post-handshake messages + let mut client_turn = true; + for message in messages { + // decode + let payload = hex::decode(&message.payload).unwrap(); + let expected_ciphertext = hex::decode(&message.ciphertext).unwrap(); + + // initiator and responder takes turn to send messages + let mut message = payload.clone(); + if client_turn { + let auth_tag = initiator_session + .write_message_in_place(&mut message) + .expect("session should not be closed"); + message.extend_from_slice(&auth_tag); + assert_eq!(message, expected_ciphertext); + + let received_payload = responder_session + .read_message_in_place(&mut message) + .expect("session should not be closed"); + assert_eq!(payload, received_payload); + } else { + let auth_tag = responder_session + .write_message_in_place(&mut message) + .expect("session should not be closed"); + message.extend_from_slice(&auth_tag); + assert_eq!(message, expected_ciphertext); + + let received_payload = initiator_session + .read_message_in_place(&mut message) + .expect("session should not be closed"); + assert_eq!(payload, received_payload); + } + + // swap sender + client_turn = !client_turn; + } + } +} + +// Negative tests +// -------------- +// +// things that should fail during the handshake: +// - buffer to write is too small (should fail) +// - message received is too small (should fail) +// - message received is too big (should fail) +// - message received is larger than max noise size (should fail) +// - payload to write is larger than max noise size (should fail) +// +// things that should work during the handshake: +// - buffer to write is too big +// + +#[test] +fn wrong_buffer_sizes() { + // setup peers + let mut rng = ::rand::rngs::StdRng::from_seed(TEST_SEED); + let initiator_private = x25519::PrivateKey::generate(&mut rng); + let responder_private = x25519::PrivateKey::generate(&mut rng); + let responder_public = responder_private.public_key(); + let initiator = NoiseConfig::new(initiator_private); + let responder = NoiseConfig::new(responder_private); + + // test the two APIs + for i in 0..2 { + // initiator sends first message with buffer too small (should fail) + let payload = b"payload"; + let mut first_message_bad = vec![0u8; handshake_init_msg_len(payload.len()) - 1]; + let res = initiator.initiate_connection( + &mut rng, + b"", + responder_public, + Some(payload), + &mut first_message_bad, + ); + + assert!(res.is_err()); + + // try again with payload too large (should fail) + let mut large_buffer = vec![0u8; MAX_SIZE_NOISE_MSG + 3]; + let payload_too_large = vec![1u8; MAX_SIZE_NOISE_MSG - handshake_init_msg_len(0) + 1]; + let res = initiator.initiate_connection( + &mut rng, + b"", + responder_public, + Some(&payload_too_large), + &mut large_buffer, + ); + + assert!(res.is_err()); + + // try again with buffer too large (should work) + let mut first_message_good = vec![0u8; handshake_init_msg_len(payload.len()) + 1]; + let initiator_state = initiator + .initiate_connection( + &mut rng, + b"", + responder_public, + Some(payload), + &mut first_message_good, + ) + .unwrap(); + + // responder parses the first message and responds + let mut second_message_small = vec![0u8; handshake_resp_msg_len(payload.len()) - 1]; + let mut second_message_large = vec![0u8; handshake_resp_msg_len(payload.len()) + 1]; + + let (mut responder_session, second_message_large) = if i == 0 { + // with buffer too small (shouldn't work) + let res = responder.respond_to_client_and_finalize( + &mut rng, + b"", + &first_message_good[..first_message_good.len() - 1], + Some(payload), + &mut second_message_small, + ); + + assert!(res.is_err()); + + // with first message too large (shouldn't work) + let res = responder.respond_to_client_and_finalize( + &mut rng, + b"", + &first_message_good, + Some(payload), + &mut second_message_large, + ); + + assert!(res.is_err()); + + // with incorrect prologue (should fail) + let res = responder.respond_to_client_and_finalize( + &mut rng, + b"incorrect prologue", + &first_message_good[..first_message_good.len() - 1], + Some(payload), + &mut second_message_large, + ); + + assert!(res.is_err()); + + // with payload too large (should fail) + let mut large_buffer = vec![0u8; MAX_SIZE_NOISE_MSG + 3]; + let payload_too_large = vec![1u8; MAX_SIZE_NOISE_MSG - handshake_resp_msg_len(0) + 1]; + let res = responder.respond_to_client_and_finalize( + &mut rng, + b"", + &first_message_good[..first_message_good.len() - 1], + Some(&payload_too_large), + &mut large_buffer, + ); + + assert!(res.is_err()); + + // with correct first message and buffer too large (should work) + let (_, responder_session) = responder + .respond_to_client_and_finalize( + &mut rng, + b"", + &first_message_good[..first_message_good.len() - 1], + Some(payload), + &mut second_message_large, + ) + .unwrap(); + + (responder_session, second_message_large) + } else { + // with first message too large + let res = responder.parse_client_init_message(b"", &first_message_good); + + assert!(res.is_err()); + + // with first message too small + let res = responder.parse_client_init_message( + b"", + &first_message_good[..first_message_good.len() - 2], + ); + + assert!(res.is_err()); + + // with wrong prologue + let res = responder.parse_client_init_message( + b"incorrect prologue", + &first_message_good[..first_message_good.len() - 1], + ); + + assert!(res.is_err()); + + // with first message of correct length + let (_, handshake_state, _) = responder + .parse_client_init_message(b"", &first_message_good[..first_message_good.len() - 1]) + .unwrap(); + + // write to buffer to small (should fail) + let res = responder.respond_to_client( + &mut rng, + handshake_state.clone(), + Some(payload), + &mut second_message_small, + ); + + assert!(res.is_err()); + + // with payload too large (should fail) + let mut large_buffer = vec![0u8; MAX_SIZE_NOISE_MSG + 3]; + let payload_too_large = vec![1u8; MAX_SIZE_NOISE_MSG - handshake_resp_msg_len(0) + 1]; + let res = responder.respond_to_client( + &mut rng, + handshake_state.clone(), + Some(&payload_too_large), + &mut large_buffer, + ); + + assert!(res.is_err()); + + // write to buffer too big (should work) + let responder_session = responder + .respond_to_client( + &mut rng, + handshake_state, + Some(payload), + &mut second_message_large, + ) + .unwrap(); + + (responder_session, second_message_large) + }; + + // initiator parses the response too large (should fail) + let res = initiator.finalize_connection(initiator_state.clone(), &second_message_large); + + assert!(res.is_err()); + + // initiator parses the response too small (should fail) + let res = initiator.finalize_connection( + initiator_state.clone(), + &second_message_large[..second_message_large.len() - 2], + ); + + assert!(res.is_err()); + + // initiator parses response of correct size + let (_, mut initiator_session) = initiator + .finalize_connection( + initiator_state.clone(), + &second_message_large[..second_message_large.len() - 1], + ) + .unwrap(); + + // session usage + let mut message = b"".to_vec(); + + let auth_tag = initiator_session + .write_message_in_place(&mut message) + .expect("should work"); + + // message too short to have auth tag + let res = responder_session.read_message_in_place(&mut message); + assert!(res.is_err()); + + // session should be unusable now + message.extend_from_slice(&auth_tag); + let res = responder_session.read_message_in_place(&mut message); + assert!(res.is_err()); + } +} diff --git a/crates/aptos-crypto/src/unit_tests/secp256k1_ecdsa_test.rs b/crates/aptos-crypto/src/unit_tests/secp256k1_ecdsa_test.rs new file mode 100644 index 0000000..8b5963f --- /dev/null +++ b/crates/aptos-crypto/src/unit_tests/secp256k1_ecdsa_test.rs @@ -0,0 +1,121 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + secp256k1_ecdsa::{self, PrivateKey, PublicKey}, + test_utils::KeyPair, + Signature, SigningKey, Uniform, +}; +use rand_core::OsRng; + +/// Tests that an individual signature share computed correctly on a message m passes verification on m. +/// Tests that a signature share computed on a different message m' fails verification on m. +/// Tests that a signature share fails verification under the wrong public key. +#[test] +fn basic() { + let mut rng = OsRng; + + let message = b"Hello world"; + let message_wrong = b"Wello Horld"; + + let key_pair = KeyPair::::generate(&mut rng); + let key_pair_wrong = KeyPair::::generate(&mut rng); + + let signature = key_pair.private_key.sign_arbitrary_message(message); + let signature_wrong = key_pair_wrong.private_key.sign_arbitrary_message(message); + + // sig on message under key_pair should verify + assert!(signature + .verify_arbitrary_msg(message, &key_pair.public_key) + .is_ok()); + + // sig_wrong on message under key_pair_wrong should verify + assert!(signature_wrong + .verify_arbitrary_msg(message, &key_pair_wrong.public_key) + .is_ok()); + + // sig on message under keypair should NOT verify under keypair_wrong + assert!(signature + .verify_arbitrary_msg(message, &key_pair_wrong.public_key) + .is_err()); + + // sig on message under keypair should NOT verify on message_wrong under key_pair + assert!(signature + .verify_arbitrary_msg(message_wrong, &key_pair.public_key) + .is_err()); + + // sig on message under keypair_wrong should NOT verify under key_pair + assert!(signature_wrong + .verify_arbitrary_msg(message, &key_pair.public_key) + .is_err()); +} + +/// Tests signature (de)serialization +#[test] +fn serialization() { + let mut rng = OsRng; + let message = b"Hello world"; + let key_pair = KeyPair::::generate(&mut rng); + + let signature = key_pair.private_key.sign_arbitrary_message(message); + assert!(signature + .verify_arbitrary_msg(message, &key_pair.public_key) + .is_ok()); + + let signature_bytes = signature.to_bytes(); + let signature_deserialized = + secp256k1_ecdsa::Signature::try_from(&signature_bytes[..]).unwrap(); + assert_eq!(signature, signature_deserialized); + + let private_key_bytes = key_pair.private_key.to_bytes(); + let private_key_deserialized = + secp256k1_ecdsa::PrivateKey::try_from(&private_key_bytes[..]).unwrap(); + assert_eq!(key_pair.private_key, private_key_deserialized); + + let public_key_bytes = key_pair.public_key.to_bytes(); + let public_key_deserialized = + secp256k1_ecdsa::PublicKey::try_from(&public_key_bytes[..]).unwrap(); + assert_eq!(key_pair.public_key, public_key_deserialized); +} + +/// Tests malleability +#[test] +fn malleability() { + let mut rng = OsRng; + let message = b"Hello world"; + let key_pair = KeyPair::::generate(&mut rng); + + let signature = key_pair.private_key.sign_arbitrary_message(message); + assert!(signature + .verify_arbitrary_msg(message, &key_pair.public_key) + .is_ok()); + + let signature_bytes = signature.to_bytes(); + let signature_deserialized = + secp256k1_ecdsa::Signature::try_from(&signature_bytes[..]).unwrap(); + assert_eq!(signature, signature_deserialized); + + let mut high_signature = signature.clone(); + high_signature.0.s = -high_signature.0.s; + let high_signature_bytes = high_signature.to_bytes(); + + // We can load + secp256k1_ecdsa::Signature::try_from(&high_signature_bytes[..]).unwrap(); + + // Ensure this is now high. + assert!(!signature.0.s.is_high()); + assert!(high_signature.0.s.is_high()); + assert!(high_signature.0.s != signature.0.s); + high_signature + .verify_arbitrary_msg(message, &key_pair.public_key) + .unwrap_err(); +} + +/// Test deserialization_failures +#[test] +fn deserialization_failure() { + let fake = [0u8, 31]; + secp256k1_ecdsa::Signature::try_from(fake.as_slice()).unwrap_err(); + secp256k1_ecdsa::PrivateKey::try_from(fake.as_slice()).unwrap_err(); + secp256k1_ecdsa::PublicKey::try_from(fake.as_slice()).unwrap_err(); +} diff --git a/crates/aptos-crypto/src/unit_tests/secp256r1_ecdsa_test.rs b/crates/aptos-crypto/src/unit_tests/secp256r1_ecdsa_test.rs new file mode 100644 index 0000000..79d5d32 --- /dev/null +++ b/crates/aptos-crypto/src/unit_tests/secp256r1_ecdsa_test.rs @@ -0,0 +1,220 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +#![allow(clippy::redundant_clone)] // Required to work around prop_assert_eq! limitations + +use crate as aptos_crypto; +use crate::{ + secp256r1_ecdsa::{ + PrivateKey, PublicKey, Signature, ORDER_HALF, PRIVATE_KEY_LENGTH, PUBLIC_KEY_LENGTH, + SIGNATURE_LENGTH, + }, + test_utils::{random_serializable_struct, uniform_keypair_strategy}, + traits::{Signature as SignatureTrait, *}, +}; +use aptos_crypto_derive::{BCSCryptoHash, CryptoHasher}; +use core::convert::TryFrom; +use p256::{EncodedPoint, NonZeroScalar}; +use proptest::{collection::vec, prelude::*}; +use serde::{Deserialize, Serialize}; +use signature::Verifier; + +#[derive(CryptoHasher, BCSCryptoHash, Serialize, Deserialize)] +struct CryptoHashable(pub usize); + +#[test] +fn test_private_key_deserialization_endianness() { + let more_than_order_be: [u8; 32] = [ + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84, 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, + 0x25, 0xFF, + ]; + // If this assert passes, we know `from_bytes_unchecked` expects big-endian inputs + assert_eq!( + Signature::from_bytes_unchecked(&more_than_order_be), + Err(CryptoMaterialError::DeserializationError), + ); +} + +proptest! { + #[test] + fn test_pub_key_deserialization(bits in any::<[u8; 32]>()){ + let pt_deser = EncodedPoint::from_bytes(&bits[..]); + let pub_key = PublicKey::try_from(&bits[..]); + let check = matches!((pt_deser, pub_key), + (Ok(_), Ok(_)) // we agree with RustCrypto's sec1 implementation, + | (Err(_), Err(_)) // we agree on point decompression failures, + ); + prop_assert!(check); + } + + + #[test] + fn test_keys_encode(keypair in uniform_keypair_strategy::()) { + { + let encoded = keypair.private_key.to_encoded_string().unwrap(); + // Hex encoding of a 64-bytes key is 128 (2 x 64) characters + 2 for the prepended '0x' + prop_assert_eq!(2 + 2 * PRIVATE_KEY_LENGTH, encoded.len()); + let decoded = PrivateKey::from_encoded_string(&encoded); + prop_assert_eq!(Some(keypair.private_key), decoded.ok()); + } + { + let encoded = keypair.public_key.to_encoded_string().unwrap(); + // Hex encoding of a 65-bytes key is 130 (2 x 65) characters + 2 for the prepended '0x' + prop_assert_eq!(2 + 2 * PUBLIC_KEY_LENGTH, encoded.len()); + let decoded = PublicKey::from_encoded_string(&encoded); + prop_assert_eq!(Some(keypair.public_key), decoded.ok()); + } + } + + #[test] + fn test_batch_verify( + message in random_serializable_struct(), + keypairs in proptest::array::uniform10(uniform_keypair_strategy::()) + ) { + let mut pks_and_sigs: Vec<(PublicKey, Signature)> = keypairs.iter().map(|keypair| { + (keypair.public_key.clone(), keypair.private_key.sign(&message).unwrap()) + }).collect(); + prop_assert!(Signature::batch_verify(&message, pks_and_sigs.clone()).is_ok()); + // We swap message and signature for the last element, + // resulting in an incorrect signature + let (pk, _sig) = pks_and_sigs.pop().unwrap(); + let other_sig = pks_and_sigs.last().unwrap().clone().1; + pks_and_sigs.push((pk, other_sig)); + prop_assert!(Signature::batch_verify(&message, pks_and_sigs).is_err()); + } + + #[test] + fn test_keys_custom_serialisation( + keypair in uniform_keypair_strategy::() + ) { + { + let serialized: &[u8] = &(keypair.private_key.to_bytes()); + prop_assert_eq!(PRIVATE_KEY_LENGTH, serialized.len()); + let deserialized = PrivateKey::try_from(serialized); + prop_assert_eq!(Some(keypair.private_key), deserialized.ok()); + } + { + let serialized: &[u8] = &(keypair.public_key.to_bytes()); + prop_assert_eq!(PUBLIC_KEY_LENGTH, serialized.len()); + let deserialized = PublicKey::try_from(serialized); + prop_assert_eq!(Some(keypair.public_key), deserialized.ok()); + } + } + + #[test] + fn test_signature_verification_custom_serialisation( + message in random_serializable_struct(), + keypair in uniform_keypair_strategy::() + ) { + let signature = keypair.private_key.sign(&message).unwrap(); + let serialized: &[u8] = &(signature.to_bytes()); + prop_assert_eq!(SIGNATURE_LENGTH, serialized.len()); + let deserialized = Signature::try_from(serialized).unwrap(); + prop_assert!(deserialized.verify(&message, &keypair.public_key).is_ok()); + } + + #[test] + fn test_signature_verification_from_arbitrary( + // this should be > 64 bits to go over the length of a default hash + msg in vec(proptest::num::u8::ANY, 1..128), + keypair in uniform_keypair_strategy::() + ) { + let signature = keypair.private_key.sign_arbitrary_message(&msg); + let serialized: &[u8] = &(signature.to_bytes()); + prop_assert_eq!(SIGNATURE_LENGTH, serialized.len()); + let deserialized = Signature::try_from(serialized).unwrap(); + prop_assert!(deserialized.verify_arbitrary_msg(&msg, &keypair.public_key).is_ok()); + } + + #[test] + fn test_signature_verification_from_struct( + x in any::(), + keypair in uniform_keypair_strategy::() + ) { + let hashable = CryptoHashable(x); + let signature = keypair.private_key.sign(&hashable).unwrap(); + let serialized: &[u8] = &(signature.to_bytes()); + prop_assert_eq!(SIGNATURE_LENGTH, serialized.len()); + let deserialized = Signature::try_from(serialized).unwrap(); + prop_assert!(deserialized.verify(&hashable, &keypair.public_key).is_ok()); + } + + + // Check for canonical S. + #[test] + fn test_signature_malleability( + message in random_serializable_struct(), + keypair in uniform_keypair_strategy::() + ) { + let signature = keypair.private_key.sign(&message).unwrap(); + let mut serialized = signature.to_bytes(); + let serialized_old = serialized; // implements Copy trait + prop_assert_eq!(serialized_old, serialized); + + let mut r_bytes: [u8; 32] = [0u8; 32]; + r_bytes.copy_from_slice(&serialized[..32]); + + let mut s_bytes: [u8; 32] = [0u8; 32]; + s_bytes.copy_from_slice(&serialized[32..]); + + // NIST-P256 signing ensures a canonical S value. + let s = NonZeroScalar::try_from(&s_bytes[..]).unwrap(); + + // computing s' = n - s to obtain the non-canonical valid signature over `message` + let malleable_s = NonZeroScalar::new(-*s).unwrap(); + let malleable_s_bytes = malleable_s.to_bytes(); + // Update the signature (the S part). + serialized[32..].copy_from_slice(&malleable_s_bytes); + + prop_assert_ne!(serialized_old, serialized); + + // Check that valid non-canonical signatures will pass verification and deserialization in the RustCrypto + // p256 crate. + // Construct the corresponding RustCrypto p256 public key. + let rustcrypto_public_key = p256::ecdsa::VerifyingKey::from_sec1_bytes( + &keypair.public_key.to_bytes() + ).unwrap(); + + // Construct the corresponding RustCrypto p256 Signature. This signature is valid but + // non-canonical. + let rustcrypto_sig = p256::ecdsa::Signature::try_from(&serialized[..]); + + // RustCrypto p256 will deserialize the non-canonical + // signature. It does not detect it. + prop_assert!(rustcrypto_sig.is_ok()); + + let msg_bytes = signing_message(&message); + prop_assert!(msg_bytes.is_ok()); + + let rustcrypto_sig = rustcrypto_sig.unwrap(); + // RustCrypto p256 verify WILL accept the mauled signature + prop_assert!(rustcrypto_public_key.verify(msg_bytes.as_ref().unwrap(), &rustcrypto_sig).is_ok()); + // ...however, our own P256Signature::verify will not + let sig = Signature::from_bytes_unchecked(&serialized).unwrap(); + prop_assert!(sig.verify(&message, &keypair.public_key).is_err()); + + let serialized_malleable: &[u8] = &serialized; + // try_from will fail on non-canonical signatures. We detect non-canonical signatures + // early during deserialization. + prop_assert_eq!( + Signature::try_from(serialized_malleable), + Err(CryptoMaterialError::CanonicalRepresentationError) + ); + + // We expect from_bytes_unchecked deserialization to succeed, as RustCrypto p256 + // does not check for non-canonical signatures. This method is pub(crate) + // and only used for test purposes. + let sig_unchecked = Signature::from_bytes_unchecked(&serialized); + prop_assert!(sig_unchecked.is_ok()); + + // Update the signature by setting S = L to make it invalid. + serialized[32..].copy_from_slice(&ORDER_HALF); + let serialized_malleable_l: &[u8] = &serialized; + // try_from will fail with CanonicalRepresentationError. + prop_assert_eq!( + Signature::try_from(serialized_malleable_l), + Err(CryptoMaterialError::CanonicalRepresentationError) + ); + } +} diff --git a/crates/aptos-crypto/src/validatable.rs b/crates/aptos-crypto/src/validatable.rs new file mode 100644 index 0000000..2a9a6a4 --- /dev/null +++ b/crates/aptos-crypto/src/validatable.rs @@ -0,0 +1,143 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//! This module provides the `Validate` trait and `Validatable` type in order to aid in deferred +//! validation. + +use crate::ValidCryptoMaterial; +use anyhow::Result; +use once_cell::sync::OnceCell; +use serde::{Deserialize, Serialize}; +use std::hash::Hash; + +/// The `Validate` trait is used in tandem with the `Validatable` type in order to provide deferred +/// validation for types. +/// +/// ## Trait Contract +/// +/// Any type `V` which implement this trait must adhere to the following contract: +/// +/// * `V` and `V::Unvalidated` are byte-for-byte equivalent. +/// * `V` and `V::Unvalidated` have equivalent `Hash` implementations. +/// * `V` and `V::Unvalidated` must have equivalent `Serialize` and `Deserialize` implementation. +/// This means that `V` and `V:Unvalidated` have equivalent serialized formats and that you can +/// deserialize a `V::Unvalidated` from a `V` that was previously serialized. +pub trait Validate: Sized { + /// The unvalidated form of some type `V` + type Unvalidated: ValidCryptoMaterial; + + /// Attempt to validate a `V::Unvalidated` and returning a validated `V` on success + fn validate(unvalidated: &Self::Unvalidated) -> Result; + + /// Return the unvalidated form of type `V` + fn to_unvalidated(&self) -> Self::Unvalidated; +} + +/// Used in connection with the `Validate` trait to be able to represent types which can benefit +/// from deferred validation as a performance optimization. +#[derive(Clone, Debug)] +pub struct Validatable { + unvalidated: V::Unvalidated, + maybe_valid: OnceCell, +} + +impl Validatable { + /// Create a new `Validatable` from a validated type. This will assume the input has been validated + /// by the caller and as a result `Validatable::::validate().is_ok()` will always return true. + pub fn from_validated(valid: V) -> Self { + let unvalidated = valid.to_unvalidated(); + + let maybe_valid = OnceCell::new(); + maybe_valid.set(valid).unwrap_or_else(|_| unreachable!()); + + Self { + unvalidated, + maybe_valid, + } + } + + /// Create a new `Validatable` from an unvalidated type + pub fn from_unvalidated(unvalidated: V::Unvalidated) -> Self { + Self { + unvalidated, + maybe_valid: OnceCell::new(), + } + } + + /// Return a reference to the unvalidated form `V::Unvalidated` + pub fn unvalidated(&self) -> &V::Unvalidated { + &self.unvalidated + } + + /// Try to validate the unvalidated form, returning `Some(&V)` on success and `None` on failure. + pub fn valid(&self) -> Option<&V> { + self.validate().ok() + } + + // TODO maybe optimize to only try once and keep track when we fail. This would avoid multiple calls to validate() by valid() when validation fails + /// Attempt to validate `V::Unvalidated` and return a reference to a valid `V` + pub fn validate(&self) -> Result<&V> { + self.maybe_valid + .get_or_try_init(|| V::validate(&self.unvalidated)) + } +} + +/// Serializes a `Validatable` using the `serde::Serialize` implementation of `V::Unvalidated` +impl Serialize for Validatable +where + V: Validate + Serialize, + V::Unvalidated: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.unvalidated.serialize(serializer) + } +} + +/// Deserializes a `Validatable` using the `serde::Deserialize` implementation of `V::Unvalidated`. +/// Does *not* perform validation on the deserialized `V::Unvalidated` object. +impl<'de, V> Deserialize<'de> for Validatable +where + V: Validate, + V::Unvalidated: Deserialize<'de>, +{ + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let unvalidated = ::deserialize(deserializer)?; + Ok(Self::from_unvalidated(unvalidated)) + } +} + +/// Simply calls the equality operator of `V::Unvalidated` +impl PartialEq for Validatable +where + V: Validate, + V::Unvalidated: PartialEq, +{ + fn eq(&self, other: &Self) -> bool { + self.unvalidated == other.unvalidated + } +} + +impl Eq for Validatable +where + V: Validate, + V::Unvalidated: Eq, +{ +} + +/// Simply calls the `Hash` implementation of `V::Unvalidated` +impl Hash for Validatable +where + V: Validate, + V::Unvalidated: Hash, +{ + fn hash(&self, state: &mut H) { + self.unvalidated.hash(state); + } +} diff --git a/crates/aptos-crypto/src/x25519.rs b/crates/aptos-crypto/src/x25519.rs new file mode 100644 index 0000000..82cd96c --- /dev/null +++ b/crates/aptos-crypto/src/x25519.rs @@ -0,0 +1,269 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//! An abstraction of x25519 elliptic curve keys required for +//! [Diffie-Hellman key exchange](https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange) +//! +//! Ideally, only `x25519::PrivateKey` and `x25519::PublicKey` should be used throughout the +//! codebase, until the bytes are actually used in cryptographic operations. +//! +//! # Examples +//! +//! ``` +//! use aptos_crypto::{x25519, Uniform, test_utils::TEST_SEED}; +//! use rand::{rngs::StdRng, SeedableRng}; +//! +//! // Derive an X25519 private key for testing. +//! let mut rng: StdRng = SeedableRng::from_seed(TEST_SEED); +//! let private_key = x25519::PrivateKey::generate(&mut rng); +//! let public_key = private_key.public_key(); +//! +//! // Deserialize an hexadecimal private or public key +//! use aptos_crypto::traits::ValidCryptoMaterialStringExt; +//! # fn main() -> Result<(), aptos_crypto::traits::CryptoMaterialError> { +//! let private_key = "404acc8ec6a0f18df7359a6ee7823f19dd95616b10fed8bdb0de030e891b945a"; +//! let private_key = x25519::PrivateKey::from_encoded_string(&private_key)?; +//! let public_key = "080e287879c918794170e258bfaddd75acac5b3e350419044655e4983a487120"; +//! let public_key = x25519::PublicKey::from_encoded_string(&public_key)?; +//! # Ok(()) +//! # } +//! ``` +//! + +use crate::{ + traits::{self, CryptoMaterialError, ValidCryptoMaterial, ValidCryptoMaterialStringExt}, + x25519, +}; +use aptos_crypto_derive::{DeserializeKey, SerializeKey, SilentDebug, SilentDisplay}; +#[cfg(any(test, feature = "fuzzing"))] +use proptest_derive::Arbitrary; +use rand::{CryptoRng, RngCore}; +use std::convert::{TryFrom, TryInto}; +// +// Underlying Implementation +// ========================= +// +// We re-export the dalek-x25519 library, +// This makes it easier to uniformalize build dalek-x25519. +// +pub use x25519_dalek; + +// +// Main types and constants +// ======================== +// + +/// Size of a X25519 private key +pub const PRIVATE_KEY_SIZE: usize = 32; + +/// Size of a X25519 public key +pub const PUBLIC_KEY_SIZE: usize = 32; + +/// Size of a X25519 shared secret +pub const SHARED_SECRET_SIZE: usize = 32; + +/// This type should be used to deserialize a received private key +#[derive(DeserializeKey, SilentDisplay, SilentDebug, SerializeKey)] +#[cfg_attr(any(test, feature = "fuzzing"), derive(Clone))] +pub struct PrivateKey(x25519_dalek::StaticSecret); + +/// This type should be used to deserialize a received public key +#[derive( + Default, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, SerializeKey, DeserializeKey, +)] +#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))] +pub struct PublicKey([u8; PUBLIC_KEY_SIZE]); + +// +// Handy implementations +// ===================== +// + +impl PrivateKey { + /// Obtain the public key part of a private key + pub fn public_key(&self) -> PublicKey { + let public_key: x25519_dalek::PublicKey = (&self.0).into(); + PublicKey(public_key.as_bytes().to_owned()) + } + + /// To perform a key exchange with another public key + pub fn diffie_hellman(&self, remote_public_key: &PublicKey) -> [u8; SHARED_SECRET_SIZE] { + let remote_public_key = x25519_dalek::PublicKey::from(remote_public_key.0); + let shared_secret = self.0.diffie_hellman(&remote_public_key); + shared_secret.as_bytes().to_owned() + } + + /// Deserialize an X25519 PrivateKey given the sha512 pre-image of a hash + /// whose least significant half is a canonical X25519 scalar, following + /// the XEdDSA approach. + /// + /// This will FAIL if the passed-in byte representation converts to a + /// non-canonical scalar in the X25519 sense (and thus cannot correspond to + /// a X25519 valid key without bit-mangling). + /// + /// This is meant to compensate for the poor key storage capabilities of some + /// key management solutions, and NOT to promote double usage of keys under + /// several schemes, which would lead to BAD vulnerabilities. + pub fn from_ed25519_private_bytes(private_slice: &[u8]) -> Result { + let ed25519_secretkey = ed25519_dalek::SecretKey::from_bytes(private_slice) + .map_err(|_| CryptoMaterialError::DeserializationError)?; + let expanded_key = ed25519_dalek::ExpandedSecretKey::from(&ed25519_secretkey); + + let mut expanded_keypart = [0u8; 32]; + expanded_keypart.copy_from_slice(&expanded_key.to_bytes()[..32]); + let potential_x25519 = x25519::PrivateKey::from(expanded_keypart); + + // This checks for x25519 clamping & reduction, which is an RFC requirement + if potential_x25519.to_bytes()[..] != expanded_key.to_bytes()[..32] { + Err(CryptoMaterialError::DeserializationError) + } else { + Ok(potential_x25519) + } + } +} + +impl PublicKey { + /// Obtain a slice reference to the underlying bytearray + pub fn as_slice(&self) -> &[u8] { + &self.0 + } + + /// Deserialize an X25519 PublicKey from its representation as an + /// Ed25519PublicKey, following the XEdDSA approach. This is meant to + /// compensate for the poor key storage capabilities of key management + /// solutions, and NOT to promote double usage of keys under several + /// schemes, which would lead to BAD vulnerabilities. + pub fn from_ed25519_public_bytes(ed25519_bytes: &[u8]) -> Result { + if ed25519_bytes.len() != 32 { + return Err(CryptoMaterialError::DeserializationError); + } + let ed_point = curve25519_dalek::edwards::CompressedEdwardsY::from_slice(ed25519_bytes) + .decompress() + .ok_or(CryptoMaterialError::DeserializationError)?; + + Ok(x25519::PublicKey::from(ed_point.to_montgomery().to_bytes())) + } +} + +// +// Traits implementations +// ====================== +// + +// private key part + +impl std::convert::From<[u8; PRIVATE_KEY_SIZE]> for PrivateKey { + fn from(private_key_bytes: [u8; PRIVATE_KEY_SIZE]) -> Self { + Self(x25519_dalek::StaticSecret::from(private_key_bytes)) + } +} + +impl std::convert::TryFrom<&[u8]> for PrivateKey { + type Error = traits::CryptoMaterialError; + + fn try_from(private_key_bytes: &[u8]) -> Result { + let private_key_bytes: [u8; PRIVATE_KEY_SIZE] = private_key_bytes + .try_into() + .map_err(|_| traits::CryptoMaterialError::DeserializationError)?; + Ok(Self(x25519_dalek::StaticSecret::from(private_key_bytes))) + } +} + +impl traits::PrivateKey for PrivateKey { + type PublicKeyMaterial = PublicKey; +} + +impl traits::Uniform for PrivateKey { + fn generate(rng: &mut R) -> Self + where + R: RngCore + CryptoRng, + { + Self(x25519_dalek::StaticSecret::new(rng)) + } +} + +// TODO: should this be gated under test flag? (mimoo) +impl traits::ValidCryptoMaterial for PrivateKey { + fn to_bytes(&self) -> Vec { + self.0.to_bytes().to_vec() + } +} + +#[cfg(any(test, feature = "fuzzing"))] +impl PartialEq for PrivateKey { + fn eq(&self, other: &Self) -> bool { + self.to_bytes() == other.to_bytes() + } +} + +#[cfg(any(test, feature = "fuzzing"))] +impl proptest::arbitrary::Arbitrary for PrivateKey { + type Parameters = (); + type Strategy = proptest::strategy::BoxedStrategy; + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + use proptest::strategy::Strategy as _; + proptest::arbitrary::any::<[u8; 32]>() + .prop_map(PrivateKey::from) + .boxed() + } +} + +// public key part + +impl From<&PrivateKey> for PublicKey { + fn from(private_key: &PrivateKey) -> Self { + private_key.public_key() + } +} + +impl std::convert::From<[u8; PUBLIC_KEY_SIZE]> for PublicKey { + fn from(public_key_bytes: [u8; PUBLIC_KEY_SIZE]) -> Self { + Self(public_key_bytes) + } +} + +impl std::convert::TryFrom<&[u8]> for PublicKey { + type Error = traits::CryptoMaterialError; + + fn try_from(public_key_bytes: &[u8]) -> Result { + let public_key_bytes: [u8; PUBLIC_KEY_SIZE] = public_key_bytes + .try_into() + .map_err(|_| traits::CryptoMaterialError::WrongLengthError)?; + Ok(Self(public_key_bytes)) + } +} + +impl traits::PublicKey for PublicKey { + type PrivateKeyMaterial = PrivateKey; +} + +impl traits::ValidCryptoMaterial for PublicKey { + fn to_bytes(&self) -> Vec { + self.0.to_vec() + } +} + +impl std::fmt::Display for PublicKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", hex::encode(self.0)) + } +} + +impl std::fmt::Debug for PublicKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "x25519::PublicKey({})", self) + } +} + +#[cfg(any(test, feature = "fuzzing"))] +use crate::test_utils::{self, KeyPair}; +#[cfg(any(test, feature = "fuzzing"))] +use proptest::prelude::*; + +/// Produces a uniformly random ed25519 keypair from a seed +#[cfg(any(test, feature = "fuzzing"))] +pub fn keypair_strategy() -> impl Strategy> { + test_utils::uniform_keypair_strategy::() +} diff --git a/crates/aptos-crypto/test_vectors/noise_cacophony.txt b/crates/aptos-crypto/test_vectors/noise_cacophony.txt new file mode 100644 index 0000000..4efd720 --- /dev/null +++ b/crates/aptos-crypto/test_vectors/noise_cacophony.txt @@ -0,0 +1,41 @@ +{ +"vectors": [ +{ +"protocol_name": "Noise_IK_25519_AESGCM_SHA256", +"init_prologue": "4a6f686e2047616c74", +"init_static": "e61ef9919cde45dd5f82166404bd08e38bceb5dfdfded0a34c8df7ed542214d1", +"init_ephemeral": "893e28b9dc6ca8d611ab664754b8ceb7bac5117349a4439a6b0569da977c464a", +"init_remote_static": "31e0303fd6418d2f8c0e78b91f22e8caed0fbe48656dcf4767e4834f701b8f62", +"resp_prologue": "4a6f686e2047616c74", +"resp_static": "4a3acbfdb163dec651dfa3194dece676d437029c62a408b4c5ea9114246e4893", +"resp_ephemeral": "bbdb4cdbd309f1a1f2e1456967fe288cadd6f712d65dc7b7793d5e63da6b375b", +"handshake_hash": "669c8640d9e42a3cda2f232f78597ceefb01daa6e3df81181ccce6fc6b5026bf", +"messages": [ +{ +"payload": "4c756477696720766f6e204d69736573", +"ciphertext": "ca35def5ae56cec33dc2036731ab14896bc4c75dbb07a61f879f8e3afa4c79444e417bc55c7a8166c993356c1be41ef67818a292426f301556c7f26b21d25ddb097153891a9a956cff47b83e63ad8d701c1342c209cff1ca5ecd43402762ac249e3bd3a4c0a145fe07cb5dae28ea13a3" +}, +{ +"payload": "4d757272617920526f746862617264", +"ciphertext": "95ebc60d2b1fa672c1f46a8aa265ef51bfe38e7ccb39ec5be34069f144808843af2ccf9972e22afc67aeafcd25162f7f98c363b7762e3e4cb7d272e39f27a5" +}, +{ +"payload": "462e20412e20486179656b", +"ciphertext": "66acfc92e3197de166809e6d4d5d003dcc819a84bc3522ca53c9d9" +}, +{ +"payload": "4361726c204d656e676572", +"ciphertext": "71f89aa6533a6de70b0826864dd75f60806ee40170c16290189eb3" +}, +{ +"payload": "4a65616e2d426170746973746520536179", +"ciphertext": "4795a3423550c8bf00386bd496a3e2c76c10669d2a75ab8f79b5094c5412a25705" +}, +{ +"payload": "457567656e2042f6686d20766f6e2042617765726b", +"ciphertext": "aa0bb39097555c918e40be82abc2b909eb79d9eb87adb07e268fc37323a6cf904fd01fb391" +} +] +} +] +} diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index 0ad6c67..1f77433 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -7,20 +7,19 @@ publish = false edition = "2021" [dependencies] -serde = { version = "1.0.130" } -serde_bytes = "0.11.5" -hex = "0.4.3" -anyhow = "1.0" -diem-crypto = { package="diem-crypto", path = "../crates/diem-crypto", features = ["fuzzing"] } -diem-crypto-derive = { package="diem-crypto-derive", path = "../crates/diem-crypto-derive" } -bcs = "0.1.3" -crypto-macro = { package="starcoin-crypto-macro", path = "./crypto-macro"} -rand = "0.8.4" -rand_core = { version = "0.6.3", default-features = false } -once_cell = "1.8.0" -serde-name = "0.2" +serde = { workspace = true } +serde_bytes = { workspace = true } +hex = { workspace = true } +anyhow = { workspace = true } +aptos-crypto = { workspace = true, features = ["fuzzing"] } +aptos-crypto-derive = { workspace = true } +bcs = { workspace = true } +rand = { workspace = true } +rand_core = { workspace = true } +once_cell = { workspace = true } +serde-name = { workspace = true } +starcoin-crypto-macro = { workspace = true } [features] default = [] -fuzzing = ["diem-crypto/fuzzing"] -avx512f = ["diem-crypto/avx512f"] +fuzzing = ["aptos-crypto/fuzzing"] diff --git a/crypto/crypto-macro/Cargo.toml b/crypto/crypto-macro/Cargo.toml index a4b9123..a1fa4fb 100644 --- a/crypto/crypto-macro/Cargo.toml +++ b/crypto/crypto-macro/Cargo.toml @@ -10,8 +10,8 @@ edition = "2021" proc-macro = true [dependencies] -syn = { version = "1.0.30", features = ["derive"] } -quote = "1.0.6" -proc-macro2 = "1.0.18" +syn = { workspace = true } +quote = { workspace = true } +proc-macro2 = { workspace = true } [dev-dependencies] diff --git a/crypto/src/hash.rs b/crypto/src/hash.rs index dc8a2fc..3debbb8 100644 --- a/crypto/src/hash.rs +++ b/crypto/src/hash.rs @@ -1,9 +1,11 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 -pub use crypto_macro::{CryptoHash, CryptoHasher}; -pub use diem_crypto::hash::{CryptoHash, CryptoHasher, DefaultHasher, HashValue, TestOnlyHash}; +pub use aptos_crypto::hash::{ + CryptoHash, CryptoHasher, DefaultHasher, DummyHasher, HashValue, TestOnlyHash, +}; use once_cell::sync::Lazy; +pub use starcoin_crypto_macro::{CryptoHash, CryptoHasher}; /// A type that implements `PlainCryptoHash` can be hashed by a cryptographic hash function and produce /// a `HashValue`. diff --git a/crypto/src/keygen.rs b/crypto/src/keygen.rs index c16578c..abdec7d 100644 --- a/crypto/src/keygen.rs +++ b/crypto/src/keygen.rs @@ -1,7 +1,8 @@ // Copyright (c) The Diem Core Contributors // SPDX-License-Identifier: Apache-2.0 -use crate::{ +use aptos_crypto::{ + bls12381, ed25519::{Ed25519PrivateKey, Ed25519PublicKey}, PrivateKey, Uniform, }; @@ -11,6 +12,7 @@ use rand::{ }; /// Ed25519 key generator. +#[derive(Debug)] pub struct KeyGen(StdRng); impl KeyGen { @@ -27,9 +29,25 @@ impl KeyGen { Self::from_seed(seed) } + pub fn generate_ed25519_private_key(&mut self) -> Ed25519PrivateKey { + Ed25519PrivateKey::generate(&mut self.0) + } + /// Generate an Ed25519 key pair. pub fn generate_keypair(&mut self) -> (Ed25519PrivateKey, Ed25519PublicKey) { - let private_key = Ed25519PrivateKey::generate(&mut self.0); + let private_key = self.generate_ed25519_private_key(); + let public_key = private_key.public_key(); + (private_key, public_key) + } + + /// Generate a bls12381 private key. + pub fn generate_bls12381_private_key(&mut self) -> bls12381::PrivateKey { + bls12381::PrivateKey::generate(&mut self.0) + } + + /// Generate an Ed25519 key pair. + pub fn generate_ed25519_keypair(&mut self) -> (Ed25519PrivateKey, Ed25519PublicKey) { + let private_key = self.generate_ed25519_private_key(); let public_key = private_key.public_key(); (private_key, public_key) } diff --git a/crypto/src/lib.rs b/crypto/src/lib.rs index dd710d0..463d41c 100644 --- a/crypto/src/lib.rs +++ b/crypto/src/lib.rs @@ -4,12 +4,14 @@ #![forbid(unsafe_code)] //! A library supplying various cryptographic primitives -// just wrap diem-crypto. +// just wr +// ap aptos-crypto pub mod ed25519 { use crate::keygen::KeyGen; - use crate::{Genesis, PrivateKey}; - pub use diem_crypto::ed25519::*; + use crate::Genesis; + pub use aptos_crypto::ed25519::*; + pub use aptos_crypto::PrivateKey; pub fn random_public_key() -> Ed25519PublicKey { KeyGen::from_os_rng().generate_keypair().1 @@ -28,11 +30,19 @@ pub mod keygen; pub mod multi_ed25519; pub mod test_utils { - pub use diem_crypto::test_utils::*; + pub use aptos_crypto::test_utils::*; } pub mod traits { - pub use diem_crypto::traits::*; + pub use aptos_crypto::traits::*; +} + +pub mod bls12381 { + pub use aptos_crypto::bls12381::*; +} + +pub mod bulletproofs { + pub use aptos_crypto::bulletproofs::*; } pub use crate::hash::HashValue; @@ -45,5 +55,7 @@ pub use once_cell as _once_cell; pub use serde_name as _serde_name; pub mod derive { - pub use diem_crypto_derive::{DeserializeKey, SerializeKey, SilentDebug, SilentDisplay}; + pub use aptos_crypto_derive::{ + CryptoHasher, DeserializeKey, SerializeKey, SilentDebug, SilentDisplay, + }; } diff --git a/crypto/src/multi_ed25519/mod.rs b/crypto/src/multi_ed25519/mod.rs index 0e7e3af..67cdd92 100644 --- a/crypto/src/multi_ed25519/mod.rs +++ b/crypto/src/multi_ed25519/mod.rs @@ -3,7 +3,7 @@ use crate::multi_ed25519::multi_shard::MultiEd25519KeyShard; use crate::test_utils::TEST_SEED; -pub use diem_crypto::multi_ed25519::*; +pub use aptos_crypto::multi_ed25519::*; use rand::SeedableRng; pub mod multi_shard; diff --git a/crypto/src/multi_ed25519/multi_shard.rs b/crypto/src/multi_ed25519/multi_shard.rs index 26346aa..948ce17 100644 --- a/crypto/src/multi_ed25519/multi_shard.rs +++ b/crypto/src/multi_ed25519/multi_shard.rs @@ -1,15 +1,15 @@ // Copyright (c) The Starcoin Core Contributors // SPDX-License-Identifier: Apache-2.0 -use crate::ed25519::{ - Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature, ED25519_PRIVATE_KEY_LENGTH, - ED25519_PUBLIC_KEY_LENGTH, -}; use crate::hash::{CryptoHash, CryptoHasher}; use crate::{CryptoMaterialError, Length, PrivateKey, Signature, ValidCryptoMaterial}; use crate::{SigningKey, Uniform}; use anyhow::{anyhow, bail, ensure, Result}; -use diem_crypto::multi_ed25519::{MultiEd25519PublicKey, MultiEd25519Signature}; +use aptos_crypto::ed25519::{ + Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature, ED25519_PRIVATE_KEY_LENGTH, + ED25519_PUBLIC_KEY_LENGTH, +}; +use aptos_crypto::multi_ed25519::{MultiEd25519PublicKey, MultiEd25519Signature}; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; use std::convert::TryFrom; @@ -123,7 +123,7 @@ impl MultiEd25519KeyShard { let signatures: Vec<(Ed25519Signature, u8)> = self .private_keys .iter() - .map(|(i, item)| (item.sign(message), *i as u8)) + .map(|(i, item)| (item.sign(message).expect("sign message failed"), *i as u8)) .collect(); MultiEd25519SignatureShard::new( diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index f27a1d5..0000000 --- a/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -1.57.0 \ No newline at end of file diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..7897a24 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.75.0"